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