Added silc_show_public_key_file.
[silc.git] / lib / silcapputil / silcapputil.c
1 /*
2
3   silcapputil.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2002 - 2006 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; version 2 of the License.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18 */
19 /* $Id$ */
20
21 #include "silc.h"
22
23 static char *silc_create_pk_identifier(void)
24 {
25   char *username = NULL, *realname = NULL;
26   char *hostname, email[256];
27   char *ident;
28
29   /* Get realname */
30   realname = silc_get_real_name();
31
32   /* Get hostname */
33   hostname = silc_net_localhost();
34   if (!hostname)
35     return NULL;
36
37   /* Get username (mandatory) */
38   username = silc_get_username();
39   if (!username)
40     return NULL;
41
42   /* Create default email address, whether it is right or not */
43   snprintf(email, sizeof(email), "%s@%s", username, hostname);
44
45   ident = silc_pkcs_silc_encode_identifier(username, hostname, realname,
46                                            email, NULL, NULL);
47   if (realname)
48     silc_free(realname);
49   silc_free(hostname);
50   silc_free(username);
51
52   return ident;
53 }
54
55 /* Generate key pair */
56
57 SilcBool silc_create_key_pair(const char *pkcs_name,
58                               SilcUInt32 key_len_bits,
59                               const char *pub_filename,
60                               const char *prv_filename,
61                               const char *pub_identifier,
62                               const char *passphrase,
63                               SilcPublicKey *return_public_key,
64                               SilcPrivateKey *return_private_key,
65                               SilcBool interactive)
66 {
67   SilcRng rng;
68   char line[256];
69   char *pkfile = pub_filename ? strdup(pub_filename) : NULL;
70   char *prvfile = prv_filename ? strdup(prv_filename) : NULL;
71   char *alg = pkcs_name ? strdup(pkcs_name) : NULL;
72   char *identifier = pub_identifier ? strdup(pub_identifier) : NULL;
73   char *pass = passphrase ? strdup(passphrase) : NULL;
74
75   if (interactive && (!alg || !pub_filename || !prv_filename))
76     printf("\
77 New pair of keys will be created.  Please, answer to following questions.\n\
78 ");
79
80   if (!alg) {
81     if (interactive) {
82       while (!alg) {
83         alg = silc_get_input("PKCS name (l to list names) [rsa]: ", FALSE);
84         if (!alg)
85           alg = strdup("rsa");
86
87         if (*alg == 'l' || *alg == 'L') {
88           char *list = silc_pkcs_get_supported();
89           printf("%s\n", list);
90           silc_free(list);
91           silc_free(alg);
92           alg = NULL;
93         }
94       }
95     } else {
96       alg = strdup("rsa");
97     }
98   }
99
100   if (!silc_pkcs_find_algorithm(alg, NULL)) {
101     fprintf(stderr, "Unknown PKCS algorithm `%s' or crypto library"
102             "is not initialized", alg);
103     return FALSE;
104   }
105
106   if (!key_len_bits) {
107     if (interactive) {
108       char *length = NULL;
109       length = silc_get_input("Key length in key_len_bits [2048]: ", FALSE);
110       if (length)
111         key_len_bits = atoi(length);
112       silc_free(length);
113     }
114     if (!key_len_bits)
115       key_len_bits = 2048;
116   }
117
118   if (!identifier) {
119     char *def = silc_create_pk_identifier();
120
121     if (interactive) {
122       memset(line, 0, sizeof(line));
123       if (def)
124         snprintf(line, sizeof(line), "Identifier [%s]: ", def);
125       else
126         snprintf(line, sizeof(line),
127                "Identifier (eg. UN=jon, HN=jon.dummy.com, "
128                "RN=Jon Johnson, E=jon@dummy.com): ");
129
130       while (!identifier) {
131         identifier = silc_get_input(line, FALSE);
132         if (!identifier && def)
133           identifier = strdup(def);
134       }
135     } else {
136       if (!def) {
137         fprintf(stderr, "Could not create public key identifier: %s\n",
138                 strerror(errno));
139         return FALSE;
140       }
141       identifier = strdup(def);
142     }
143
144     silc_free(def);
145   }
146
147   rng = silc_rng_alloc();
148   silc_rng_init(rng);
149   silc_rng_global_init(rng);
150
151   if (!pkfile) {
152     if (interactive) {
153       memset(line, 0, sizeof(line));
154       snprintf(line, sizeof(line), "Public key filename [public_key.pub]: ");
155       pkfile = silc_get_input(line, FALSE);
156     }
157     if (!pkfile)
158       pkfile = strdup("public_key.pub");
159   }
160
161   if (!prvfile) {
162     if (interactive) {
163       memset(line, 0, sizeof(line));
164       snprintf(line, sizeof(line), "Private key filename [private_key.prv]: ");
165       prvfile = silc_get_input(line, FALSE);
166     }
167     if (!prvfile)
168       prvfile = strdup("private_key.prv");
169   }
170
171   if (!pass) {
172     while (TRUE) {
173       char *pass2 = NULL;
174       pass = silc_get_input("Private key passphrase: ", TRUE);
175       if (!pass) {
176         pass = strdup("");
177         break;
178       } else {
179         SilcBool match;
180         printf("\n");
181         pass2 = silc_get_input("Retype private key passphrase: ", TRUE);
182         if (!pass2)
183           pass2 = strdup("");
184         match = !strcmp(pass, pass2);
185         silc_free(pass2);
186         if (match)
187           break;
188         fprintf(stderr, "\nPassphrases do not match\n\n");
189       }
190     }
191   }
192
193   /* Generate keys */
194   if (!silc_pkcs_silc_generate_key(alg, "pkcs1-no-oid", key_len_bits,
195                                    identifier, rng, return_public_key,
196                                    return_private_key))
197     return FALSE;
198
199   /* Save public key into file */
200   silc_pkcs_save_public_key(pkfile, *return_public_key, SILC_PKCS_FILE_BASE64);
201
202   /* Save private key into file */
203   silc_pkcs_save_private_key(prvfile, *return_private_key,
204                              (const unsigned char *)pass, strlen(pass),
205                              SILC_PKCS_FILE_BIN, rng);
206
207   printf("Public key has been saved into `%s'.\n", pkfile);
208   printf("Private key has been saved into `%s'.\n", prvfile);
209   if (interactive) {
210     printf("Press <Enter> to continue...\n");
211     getchar();
212   }
213
214   silc_rng_free(rng);
215   silc_free(alg);
216   silc_free(pkfile);
217   silc_free(prvfile);
218   silc_free(identifier);
219   memset(pass, 0, strlen(pass));
220   silc_free(pass);
221
222   return TRUE;
223 }
224
225 /* Load key pair */
226
227 SilcBool silc_load_key_pair(const char *pub_filename,
228                             const char *prv_filename,
229                             const char *passphrase,
230                             SilcPublicKey *return_public_key,
231                             SilcPrivateKey *return_private_key)
232 {
233   char *pass = passphrase ? strdup(passphrase) : NULL;
234
235   SILC_LOG_DEBUG(("Loading public and private keys"));
236
237   if (!silc_pkcs_load_public_key(pub_filename, return_public_key)) {
238     if (pass)
239       memset(pass, 0, strlen(pass));
240     silc_free(pass);
241     return FALSE;
242   }
243
244   if (!pass) {
245     pass = silc_get_input("Private key passphrase: ", TRUE);
246     if (!pass)
247       pass = strdup("");
248   }
249
250   if (!silc_pkcs_load_private_key(prv_filename,
251                                   (const unsigned char *)pass, strlen(pass),
252                                   return_private_key)) {
253     memset(pass, 0, strlen(pass));
254     silc_free(pass);
255     return FALSE;
256   }
257
258   memset(pass, 0, strlen(pass));
259   silc_free(pass);
260   return TRUE;
261 }
262
263 /* Dump public key into stdout */
264
265 SilcBool silc_show_public_key(SilcPublicKey public_key)
266 {
267   SilcSILCPublicKey silc_pubkey;
268   SilcPublicKeyIdentifier ident;
269   char *fingerprint, *babbleprint;
270   unsigned char *pk;
271   SilcUInt32 pk_len;
272   SilcUInt32 key_len = 0;
273
274   silc_pubkey = silc_pkcs_get_context(SILC_PKCS_SILC, public_key);
275   if (!silc_pubkey) {
276     silc_pkcs_public_key_free(public_key);
277     return FALSE;
278   }
279
280   ident = &silc_pubkey->identifier;
281   key_len = silc_pkcs_public_key_get_len(public_key);
282   pk = silc_pkcs_public_key_encode(public_key, &pk_len);
283   if (!pk) {
284     silc_pkcs_public_key_free(public_key);
285     return FALSE;
286   }
287   fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
288   babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
289
290   printf("Algorithm          : %s\n", silc_pkcs_get_name(public_key));
291   if (key_len)
292     printf("Key length (bits)  : %d\n", (unsigned int)key_len);
293   if (ident->realname)
294     printf("Real name          : %s\n", ident->realname);
295   if (ident->username)
296     printf("Username           : %s\n", ident->username);
297   if (ident->host)
298     printf("Hostname           : %s\n", ident->host);
299   if (ident->email)
300     printf("Email              : %s\n", ident->email);
301   if (ident->org)
302     printf("Organization       : %s\n", ident->org);
303   if (ident->country)
304     printf("Country            : %s\n", ident->country);
305   printf("Fingerprint (SHA1) : %s\n", fingerprint);
306   printf("Babbleprint (SHA1) : %s\n", babbleprint);
307
308   fflush(stdout);
309
310   silc_free(fingerprint);
311   silc_free(babbleprint);
312   silc_free(pk);
313
314   return TRUE;
315 }
316
317 /* Dump public key into stdout */
318
319 SilcBool silc_show_public_key_file(const char *pub_filename)
320 {
321   SilcPublicKey public_key;
322   SilcBool ret;
323
324   if (!silc_pkcs_load_public_key((char *)pub_filename, &public_key)) {
325     fprintf(stderr, "Could not load public key file `%s'\n", pub_filename);
326     return FALSE;
327   }
328
329   printf("Public key file    : %s\n", pub_filename);
330   ret = silc_show_public_key(public_key);
331   silc_pkcs_public_key_free(public_key);
332
333   return ret;
334 }
335
336 /* Change private key passphrase */
337
338 SilcBool silc_change_private_key_passphrase(const char *prv_filename,
339                                             const char *old_passphrase,
340                                             const char *new_passphrase)
341 {
342   SilcPrivateKey private_key;
343   char *pass;
344   SilcRng rng;
345
346   pass = old_passphrase ? strdup(old_passphrase) : NULL;
347   if (!pass) {
348     pass = silc_get_input("Old passphrase: ", TRUE);
349     if (!pass)
350       pass = strdup("");
351   }
352
353   if (!silc_pkcs_load_private_key(prv_filename,
354                                   (const unsigned char *)pass, strlen(pass),
355                                   &private_key)) {
356     memset(pass, 0, strlen(pass));
357     silc_free(pass);
358     fprintf(stderr, "Could not load private key `%s' file\n", prv_filename);
359     return FALSE;
360   }
361
362   memset(pass, 0, strlen(pass));
363   silc_free(pass);
364
365   pass = new_passphrase ? strdup(new_passphrase) : NULL;
366   if (!pass) {
367     char *pass2 = NULL;
368     fprintf(stdout, "\n");
369     pass = silc_get_input("New passphrase: ", TRUE);
370     if (!pass) {
371       pass = strdup("");
372     } else {
373       while (TRUE) {
374         printf("\n");
375         pass2 = silc_get_input("Retype new passphrase: ", TRUE);
376         if (!pass2)
377           pass2 = strdup("");
378         if (!strcmp(pass, pass2))
379           break;
380         fprintf(stderr, "\nPassphrases do not match");
381       }
382       silc_free(pass2);
383     }
384   }
385
386   rng = silc_rng_alloc();
387   silc_rng_init(rng);
388
389   silc_pkcs_save_private_key((char *)prv_filename, private_key,
390                              (unsigned char *)pass, strlen(pass),
391                              SILC_PKCS_FILE_BIN, rng);
392
393   fprintf(stdout, "\nPassphrase changed\n");
394
395   memset(pass, 0, strlen(pass));
396   silc_free(pass);
397
398   silc_pkcs_private_key_free(private_key);
399   silc_rng_free(rng);
400
401   return TRUE;
402 }
403
404 /* Checks that the 'identifier' string is valid identifier string
405    and does not contain any unassigned or prohibited character.  This
406    function is used to check for valid nicknames, channel names,
407    server names, usernames, hostnames, service names, algorithm names,
408    other security property names, and SILC Public Key name. */
409
410 unsigned char *silc_identifier_check(const unsigned char *identifier,
411                                      SilcUInt32 identifier_len,
412                                      SilcStringEncoding identifier_encoding,
413                                      SilcUInt32 max_allowed_length,
414                                      SilcUInt32 *out_len)
415 {
416   unsigned char *utf8s;
417   SilcUInt32 utf8s_len;
418   SilcStringprepStatus status;
419
420   if (!identifier || !identifier_len)
421     return NULL;
422
423   if (max_allowed_length && identifier_len > max_allowed_length)
424     return NULL;
425
426   status = silc_stringprep(identifier, identifier_len,
427                            identifier_encoding, SILC_IDENTIFIER_PREP, 0,
428                            &utf8s, &utf8s_len, SILC_STRING_UTF8);
429   if (status != SILC_STRINGPREP_OK) {
430     SILC_LOG_DEBUG(("silc_stringprep() status error %d", status));
431     return NULL;
432   }
433
434   if (out_len)
435     *out_len = utf8s_len;
436
437   return utf8s;
438 }
439
440 /* Same as above but does not allocate memory, just checks the
441    validity of the string. */
442
443 SilcBool silc_identifier_verify(const unsigned char *identifier,
444                                 SilcUInt32 identifier_len,
445                                 SilcStringEncoding identifier_encoding,
446                                 SilcUInt32 max_allowed_length)
447 {
448   SilcStringprepStatus status;
449
450   if (!identifier || !identifier_len)
451     return FALSE;
452
453   if (max_allowed_length && identifier_len > max_allowed_length)
454     return FALSE;
455
456   status = silc_stringprep(identifier, identifier_len,
457                            identifier_encoding, SILC_IDENTIFIER_PREP, 0,
458                            NULL, NULL, SILC_STRING_UTF8);
459   if (status != SILC_STRINGPREP_OK) {
460     SILC_LOG_DEBUG(("silc_stringprep() status error %d", status));
461     return FALSE;
462   }
463
464   return TRUE;
465 }
466
467 unsigned char *silc_channel_name_check(const unsigned char *identifier,
468                                        SilcUInt32 identifier_len,
469                                        SilcStringEncoding identifier_encoding,
470                                        SilcUInt32 max_allowed_length,
471                                        SilcUInt32 *out_len)
472 {
473   unsigned char *utf8s;
474   SilcUInt32 utf8s_len;
475   SilcStringprepStatus status;
476
477   if (!identifier || !identifier_len)
478     return NULL;
479
480   if (max_allowed_length && identifier_len > max_allowed_length)
481     return NULL;
482
483   status = silc_stringprep(identifier, identifier_len,
484                            identifier_encoding, SILC_IDENTIFIER_CH_PREP, 0,
485                            &utf8s, &utf8s_len, SILC_STRING_UTF8);
486   if (status != SILC_STRINGPREP_OK) {
487     SILC_LOG_DEBUG(("silc_stringprep() status error %d", status));
488     return NULL;
489   }
490
491   if (out_len)
492     *out_len = utf8s_len;
493
494   return utf8s;
495 }
496
497 /* Same as above but does not allocate memory, just checks the
498    validity of the string. */
499
500 SilcBool silc_channel_name_verify(const unsigned char *identifier,
501                                   SilcUInt32 identifier_len,
502                                   SilcStringEncoding identifier_encoding,
503                                   SilcUInt32 max_allowed_length)
504 {
505   SilcStringprepStatus status;
506
507   if (!identifier || !identifier_len)
508     return FALSE;
509
510   if (max_allowed_length && identifier_len > max_allowed_length)
511     return FALSE;
512
513   status = silc_stringprep(identifier, identifier_len,
514                            identifier_encoding, SILC_IDENTIFIER_CH_PREP, 0,
515                            NULL, NULL, SILC_STRING_UTF8);
516   if (status != SILC_STRINGPREP_OK) {
517     SILC_LOG_DEBUG(("silc_stringprep() status error %d", status));
518     return FALSE;
519   }
520
521   return TRUE;
522 }
523
524 /* Return mode list */
525
526 SilcBool silc_get_mode_list(SilcBuffer mode_list, SilcUInt32 mode_list_count,
527                             SilcUInt32 **list)
528 {
529   int i;
530
531   if (silc_buffer_len(mode_list) / 4 != mode_list_count)
532     return FALSE;
533
534   *list = silc_calloc(mode_list_count, sizeof(**list));
535
536   for (i = 0; i < mode_list_count; i++) {
537     SILC_GET32_MSB((*list)[i], mode_list->data);
538     silc_buffer_pull(mode_list, 4);
539   }
540
541   silc_buffer_push(mode_list, mode_list->data - mode_list->head);
542
543   return TRUE;
544 }
545
546 /* Status message structure. Messages are defined below. */
547 typedef struct {
548   SilcStatus status;
549   const char *message;
550 } SilcStatusMessage;
551
552 #define STAT(x) SILC_STATUS_ERR_##x
553 static const SilcStatusMessage silc_status_messages[] = {
554
555   { STAT(NO_SUCH_NICK),      "There was no such nickname" },
556   { STAT(NO_SUCH_CHANNEL),   "There was no such channel" },
557   { STAT(NO_SUCH_SERVER),    "There was no such server" },
558   { STAT(INCOMPLETE_INFORMATION),  "Incomplete registration information" },
559   { STAT(NO_RECIPIENT),      "No recipient given" },
560   { STAT(UNKNOWN_COMMAND),   "Unknown command" },
561   { STAT(WILDCARDS),         "Wilcrads not allowed" },
562   { STAT(NO_CLIENT_ID),      "No Client ID given" },
563   { STAT(NO_CHANNEL_ID),     "No Channel ID given" },
564   { STAT(NO_SERVER_ID),      "No Server ID given" },
565   { STAT(BAD_CLIENT_ID),     "Bad Client ID" },
566   { STAT(BAD_CHANNEL_ID),    "Bad Channel ID" },
567   { STAT(NO_SUCH_CLIENT_ID), "There is no such client" },
568   { STAT(NO_SUCH_CHANNEL_ID),"There is no such channel" },
569   { STAT(NICKNAME_IN_USE),   "Nickname already exists" },
570   { STAT(NOT_ON_CHANNEL),    "You are not on that channel" },
571   { STAT(USER_NOT_ON_CHANNEL),"They are not on the channel" },
572   { STAT(USER_ON_CHANNEL),   "User already on the channel" },
573   { STAT(NOT_REGISTERED),    "You have not registered" },
574   { STAT(NOT_ENOUGH_PARAMS), "Not enough parameters" },
575   { STAT(TOO_MANY_PARAMS),   "Too many parameters" },
576   { STAT(PERM_DENIED),       "Permission denied" },
577   { STAT(BANNED_FROM_SERVER),"You are not allowed to connect" },
578   { STAT(BAD_PASSWORD),      "Cannot join channel. Incorrect password" },
579   { STAT(CHANNEL_IS_FULL),   "Cannot join channel. Channel is full" },
580   { STAT(NOT_INVITED),     "Cannot join channel. You have not been invited" },
581   { STAT(BANNED_FROM_CHANNEL), "Cannot join channel. You have been banned" },
582   { STAT(UNKNOWN_MODE),    "Unknown mode" },
583   { STAT(NOT_YOU),         "Cannot change mode for other users" },
584   { STAT(NO_CHANNEL_PRIV), "Permission denied. You are not channel operator" },
585   { STAT(NO_CHANNEL_FOPRIV),"Permission denied. You are not channel founder" },
586   { STAT(NO_SERVER_PRIV),  "Permission denied. You are not server operator" },
587   { STAT(NO_ROUTER_PRIV),  "Permission denied. You are not SILC operator" },
588   { STAT(BAD_NICKNAME),    "Bad nickname" },
589   { STAT(BAD_CHANNEL),     "Bad channel name" },
590   { STAT(AUTH_FAILED),     "Authentication failed" },
591   { STAT(UNKNOWN_ALGORITHM), "Unsupported algorithm" },
592   { STAT(NO_SUCH_SERVER_ID), "No such Server ID" },
593   { STAT(RESOURCE_LIMIT), "No more free resources" },
594   { STAT(NO_SUCH_SERVICE), "Service doesn't exist" },
595   { STAT(NOT_AUTHENTICATED), "You have not been authenticated" },
596   { STAT(BAD_SERVER_ID), "Server ID is not valid" },
597   { STAT(KEY_EXCHANGE_FAILED), "Key exchange failed" },
598   { STAT(BAD_VERSION), "Bad version" },
599   { STAT(TIMEDOUT), "Service timed out" },
600   { STAT(UNSUPPORTED_PUBLIC_KEY), "Unsupported public key type" },
601   { STAT(OPERATION_ALLOWED), "Operation is not allowed" },
602   { STAT(BAD_SERVER), "Bad server name" },
603   { STAT(BAD_USERNAME), "Bad user name" },
604
605   { 0, NULL }
606 };
607
608 /* Returns status message string */
609
610 const char *silc_get_status_message(unsigned char status)
611 {
612   int i;
613
614   for (i = 0; silc_status_messages[i].message; i++) {
615     if (silc_status_messages[i].status == status)
616       break;
617   }
618
619   if (silc_status_messages[i].message == NULL)
620     return "";
621
622   return silc_status_messages[i].message;
623 }
624
625 static const char *packet_name[] = {
626   "NONE",
627   "DISCONNECT",
628   "SUCCESS",
629   "FAILURE",
630   "REJECT",
631   "NOTIFY",
632   "ERROR",
633   "CHANNEL MESSAGE",
634   "CHANNEL KEY",
635   "PRIVATE MESSAGE",
636   "PRIVATE MESSAGE KEY",
637   "COMMAND",
638   "COMMAND REPLY",
639   "KEY EXCHANGE",
640   "KEY EXCHANGE 1",
641   "KEY EXCHANGE 2",
642   "CONNECTION AUTH REQUEST",
643   "CONNECTION AUTH",
644   "NEW ID",
645   "NEW CLIENT",
646   "NEW SERVER",
647   "NEW CHANNEL",
648   "REKEY",
649   "REKEY_DONE",
650   "HEARTBEAT",
651   "KEY AGREEMENT",
652   "RESUME ROUTER",
653   "FTP",
654   "RESUME CLIENT",
655 };
656
657 /* Returns packet type name */
658
659 const char *silc_get_packet_name(unsigned char type)
660 {
661   if (type >= SILC_PACKET_MAX)
662     return "RESERVED";
663   if (type >= SILC_PACKET_PRIVATE)
664     return "PRIVATE RANGE";
665   if (type > (sizeof(packet_name) / sizeof(*packet_name)))
666     return "UNKNOWN";
667   return packet_name[type];
668 }
669
670 static const char *command_name[] = {
671   "NONE",
672   "WHOIS",
673   "WHOWAS",
674   "IDENTIFY",
675   "NICK",
676   "LIST",
677   "TOPIC",
678   "INVITE",
679   "QUIT",
680   "KILL",
681   "INFO",
682   "STATS",
683   "PING",
684   "OPER",
685   "JOIN",
686   "MOTD",
687   "UMODE",
688   "CMODE",
689   "CUMODE",
690   "KICK",
691   "BAN",
692   "DETACH",
693   "WATCH",
694   "SILCOPER",
695   "LEAVE",
696   "USERS",
697   "GETKEY",
698   "SERVICE",
699 };
700
701 /* Returns command name */
702
703 const char *silc_get_command_name(unsigned char command)
704 {
705   if (command >= SILC_COMMAND_RESERVED)
706     return "RESERVED";
707   if (command >= SILC_COMMAND_PRIVATE)
708     return "PRIVATE RANGE";
709   if (command > (sizeof(command_name) / sizeof(*command_name)))
710     return "UNKNOWN";
711   return command_name[command];
712 }
713
714 /* Parses SILC protocol style version string. */
715
716 SilcBool silc_parse_version_string(const char *version,
717                                    SilcUInt32 *protocol_version,
718                                    char **protocol_version_string,
719                                    SilcUInt32 *software_version,
720                                    char **software_version_string,
721                                    char **vendor_version)
722 {
723   char *cp, buf[32];
724   int maj = 0, min = 0;
725
726   if (!strstr(version, "SILC-"))
727     return FALSE;
728
729   cp = (char *)version + 5;
730   if (!cp)
731     return FALSE;
732
733   /* Take protocol version */
734
735   maj = atoi(cp);
736   if (!strchr(cp, '.'))
737     return FALSE;
738   cp = strchr(cp, '.') + 1;
739   if (!cp || !(*cp))
740     return FALSE;
741   min = atoi(cp);
742
743   memset(buf, 0, sizeof(buf));
744   snprintf(buf, sizeof(buf) - 1, "%d%d", maj, min);
745   if (protocol_version)
746     *protocol_version = atoi(buf);
747   memset(buf, 0, sizeof(buf));
748   snprintf(buf, sizeof(buf) - 1, "%d.%d", maj, min);
749   if (protocol_version_string)
750     *protocol_version_string = strdup(buf);
751
752   /* Take software version */
753
754   maj = 0;
755   min = 0;
756   if (!strchr(cp, '-'))
757     return FALSE;
758   cp = strchr(cp, '-') + 1;
759   if (!cp || !(*cp))
760     return FALSE;
761
762   maj = atoi(cp);
763   if (strchr(cp, '.')) {
764     cp = strchr(cp, '.') + 1;
765     if (cp && *cp)
766       min = atoi(cp);
767   }
768
769   memset(buf, 0, sizeof(buf));
770   snprintf(buf, sizeof(buf) - 1, "%d%d", maj, min);
771   if (software_version)
772     *software_version = atoi(buf);
773   memset(buf, 0, sizeof(buf));
774   snprintf(buf, sizeof(buf) - 1, "%d.%d", maj, min);
775   if (software_version_string)
776     *software_version_string = strdup(buf);
777
778   /* Take vendor string */
779
780   if (strchr(cp, '.')) {
781     cp = strchr(cp, '.') + 1;
782     if (cp && *cp && vendor_version)
783       *vendor_version = strdup(cp);
784   }
785
786   return TRUE;
787 }
788
789 /* Converts version string x.x into number representation. */
790
791 SilcUInt32 silc_version_to_num(const char *version)
792 {
793   int maj = 0, min = 0;
794   char *cp, buf[32];
795
796   if (!version)
797     return 0;
798
799   cp = (char *)version;
800   maj = atoi(cp);
801   cp = strchr(cp, '.');
802   if (cp)
803     min = atoi(cp + 1);
804
805   memset(buf, 0, sizeof(buf));
806   snprintf(buf, sizeof(buf) - 1, "%d%d", maj, min);
807   return (SilcUInt32)atoi(buf);
808 }
809
810 /* Parses mode mask and returns the mode as string. */
811
812 char *silc_client_chmode(SilcUInt32 mode, const char *cipher, const char *hmac)
813 {
814   char string[100];
815
816   if (!mode)
817     return NULL;
818
819   memset(string, 0, sizeof(string));
820
821   if (mode & SILC_CHANNEL_MODE_PRIVATE)
822     strncat(string, "p", 1);
823
824   if (mode & SILC_CHANNEL_MODE_SECRET)
825     strncat(string, "s", 1);
826
827   if (mode & SILC_CHANNEL_MODE_PRIVKEY)
828     strncat(string, "k", 1);
829
830   if (mode & SILC_CHANNEL_MODE_INVITE)
831     strncat(string, "i", 1);
832
833   if (mode & SILC_CHANNEL_MODE_TOPIC)
834     strncat(string, "t", 1);
835
836   if (mode & SILC_CHANNEL_MODE_ULIMIT)
837     strncat(string, "l", 1);
838
839   if (mode & SILC_CHANNEL_MODE_PASSPHRASE)
840     strncat(string, "a", 1);
841
842   if (mode & SILC_CHANNEL_MODE_FOUNDER_AUTH)
843     strncat(string, "f", 1);
844
845   if (mode & SILC_CHANNEL_MODE_CHANNEL_AUTH)
846     strncat(string, "C", 1);
847
848   if (mode & SILC_CHANNEL_MODE_SILENCE_USERS)
849     strncat(string, "m", 1);
850
851   if (mode & SILC_CHANNEL_MODE_SILENCE_OPERS)
852     strncat(string, "M", 1);
853
854   if (mode & SILC_CHANNEL_MODE_CIPHER)
855     strncat(string, "c", 1);
856
857   if (mode & SILC_CHANNEL_MODE_HMAC)
858     strncat(string, "h", 1);
859
860   if (mode & SILC_CHANNEL_MODE_CIPHER) {
861     if (strlen(cipher) + strlen(string) + 1< sizeof(string)) {
862       strncat(string, " ", 1);
863       strncat(string, cipher, strlen(cipher));
864     }
865   }
866
867   if (mode & SILC_CHANNEL_MODE_HMAC) {
868     if (strlen(hmac) + strlen(string) + 1< sizeof(string)) {
869       strncat(string, " ", 1);
870       strncat(string, hmac, strlen(hmac));
871     }
872   }
873
874   /* Rest of mode is ignored */
875
876   return strdup(string);
877 }
878
879 /* Parses channel user mode mask and returns te mode as string */
880
881 char *silc_client_chumode(SilcUInt32 mode)
882 {
883   char string[64];
884
885   if (!mode)
886     return NULL;
887
888   memset(string, 0, sizeof(string));
889
890   if (mode & SILC_CHANNEL_UMODE_CHANFO)
891     strncat(string, "f", 1);
892
893   if (mode & SILC_CHANNEL_UMODE_CHANOP)
894     strncat(string, "o", 1);
895
896   if (mode & SILC_CHANNEL_UMODE_BLOCK_MESSAGES)
897     strncat(string, "b", 1);
898
899   if (mode & SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS)
900     strncat(string, "u", 1);
901
902   if (mode & SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS)
903     strncat(string, "r", 1);
904
905   if (mode & SILC_CHANNEL_UMODE_QUIET)
906     strncat(string, "q", 1);
907
908   return strdup(string);
909 }
910
911 /* Parses channel user mode and returns it as special mode character. */
912
913 char *silc_client_chumode_char(SilcUInt32 mode)
914 {
915   char string[64];
916
917   if (!mode)
918     return NULL;
919
920   memset(string, 0, sizeof(string));
921
922   if (mode & SILC_CHANNEL_UMODE_CHANFO)
923     strncat(string, "*", 1);
924
925   if (mode & SILC_CHANNEL_UMODE_CHANOP)
926     strncat(string, "@", 1);
927
928   if (mode & SILC_CHANNEL_UMODE_QUIET)
929     strncat(string, "&", 1);
930
931   return strdup(string);
932 }
933
934 /* Renders ID to suitable to print for example to log file. */
935
936 static char rid[256];
937 #define _PUT_STRING(__d__, __s__)                                       \
938 do {                                                                    \
939   int __sp = sizeof(__d__) - 1 - strlen(__d__);                         \
940   if (__sp < strlen(__s__)) {                                           \
941     if (__sp)                                                           \
942       strncat(__d__, __s__, (sizeof(__d__) - 1) - strlen(__d__));       \
943   } else {                                                              \
944     strncat(__d__, __s__, strlen(__s__));                               \
945   }                                                                     \
946 } while(0)
947
948 char *silc_id_render(void *id, SilcIdType id_type)
949 {
950   char tmp[100];
951   unsigned char tmps[2];
952   char *cp;
953
954   memset(rid, 0, sizeof(rid));
955   switch(id_type) {
956   case SILC_ID_SERVER:
957     {
958       SilcServerID *server_id = (SilcServerID *)id;
959       if (server_id->ip.data_len > 4) {
960 #ifdef HAVE_IPV6
961         struct sockaddr_in6 ipv6;
962         memset(&ipv6, 0, sizeof(ipv6));
963         ipv6.sin6_family = AF_INET6;
964         memmove(&ipv6.sin6_addr, server_id->ip.data, sizeof(ipv6.sin6_addr));
965         if (!getnameinfo((struct sockaddr *)&ipv6, sizeof(ipv6),
966                          tmp, sizeof(tmp) - 1, NULL, 0, NI_NUMERICHOST))
967           _PUT_STRING(rid, tmp);
968 #endif
969       } else {
970         struct in_addr ipv4;
971         memmove(&ipv4.s_addr, server_id->ip.data, 4);
972         cp = inet_ntoa(ipv4);
973         if (cp)
974           _PUT_STRING(rid, cp);
975       }
976
977       memset(tmp, 0, sizeof(tmp));
978       snprintf(tmp, sizeof(tmp) - 1, ",%d,", ntohs(server_id->port));
979       _PUT_STRING(rid, tmp);
980       SILC_PUT16_MSB(server_id->rnd, tmps);
981       memset(tmp, 0, sizeof(tmp));
982       snprintf(tmp, sizeof(tmp) - 1, "[%02x %02x]", tmps[0], tmps[1]);
983       _PUT_STRING(rid, tmp);
984     }
985     break;
986   case SILC_ID_CLIENT:
987     {
988       SilcClientID *client_id = (SilcClientID *)id;
989       if (client_id->ip.data_len > 4) {
990 #ifdef HAVE_IPV6
991         struct sockaddr_in6 ipv6;
992         memset(&ipv6, 0, sizeof(ipv6));
993         ipv6.sin6_family = AF_INET6;
994         memmove(&ipv6.sin6_addr, client_id->ip.data, sizeof(ipv6.sin6_addr));
995         if (!getnameinfo((struct sockaddr *)&ipv6, sizeof(ipv6),
996                          tmp, sizeof(tmp) - 1, NULL, 0, NI_NUMERICHOST))
997           _PUT_STRING(rid, tmp);
998 #endif
999       } else {
1000         struct in_addr ipv4;
1001         memmove(&ipv4.s_addr, client_id->ip.data, 4);
1002         cp = inet_ntoa(ipv4);
1003         if (cp)
1004           _PUT_STRING(rid, cp);
1005       }
1006
1007       memset(tmp, 0, sizeof(tmp));
1008       snprintf(tmp, sizeof(tmp) - 1, ",%02x,", client_id->rnd);
1009       _PUT_STRING(rid, tmp);
1010       memset(tmp, 0, sizeof(tmp));
1011       snprintf(tmp, sizeof(tmp) - 1, "[%02x %02x %02x %02x...]",
1012                client_id->hash[0], client_id->hash[1],
1013                client_id->hash[2], client_id->hash[3]);
1014       _PUT_STRING(rid, tmp);
1015     }
1016     break;
1017   case SILC_ID_CHANNEL:
1018     {
1019       SilcChannelID *channel_id = (SilcChannelID *)id;
1020       if (channel_id->ip.data_len > 4) {
1021 #ifdef HAVE_IPV6
1022         struct sockaddr_in6 ipv6;
1023         memset(&ipv6, 0, sizeof(ipv6));
1024         ipv6.sin6_family = AF_INET6;
1025         memmove(&ipv6.sin6_addr, channel_id->ip.data, sizeof(ipv6.sin6_addr));
1026         if (!getnameinfo((struct sockaddr *)&ipv6, sizeof(ipv6),
1027                          tmp, sizeof(tmp) - 1, NULL, 0, NI_NUMERICHOST))
1028           _PUT_STRING(rid, tmp);
1029 #endif
1030       } else {
1031         struct in_addr ipv4;
1032         memmove(&ipv4.s_addr, channel_id->ip.data, 4);
1033         cp = inet_ntoa(ipv4);
1034         if (cp)
1035           _PUT_STRING(rid, cp);
1036       }
1037
1038       memset(tmp, 0, sizeof(tmp));
1039       snprintf(tmp, sizeof(tmp) - 1, ",%d,", ntohs(channel_id->port));
1040       _PUT_STRING(rid, tmp);
1041       SILC_PUT16_MSB(channel_id->rnd, tmps);
1042       memset(tmp, 0, sizeof(tmp));
1043       snprintf(tmp, sizeof(tmp) - 1, "[%02x %02x]", tmps[0], tmps[1]);
1044       _PUT_STRING(rid, tmp);
1045     }
1046     break;
1047   }
1048
1049   return rid;
1050 }
1051 #undef _PUT_STRING