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