0ee0dd6816e15f64e4e22cdd1b9ce236b4329dc7
[silc.git] / lib / silcutil / silcutil.c
1 /*
2
3   silcutil.c
4
5   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
6
7   Copyright (C) 1997 - 2000 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; either version 2 of the License, or
12   (at your option) any later version.
13   
14   This program is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   GNU General Public License for more details.
18
19 */
20 /*
21  * These are general utility functions that doesn't belong to any specific
22  * group of routines.
23  */
24 /* $Id$ */
25
26 #include "silcincludes.h"
27
28 /* Opens a file indicated by the filename `filename' with flags indicated
29    by the `flags'. */
30
31 int silc_file_open(const char *filename, int flags)
32 {
33   int fd;
34
35   fd = open(filename, flags, 0600);
36
37   return fd;
38 }
39
40 /* Reads data from file descriptor `fd' to `buf'. */
41
42 int silc_file_read(int fd, unsigned char *buf, uint32 buf_len)
43 {
44   return read(fd, (void *)buf, buf_len);
45 }
46
47 /* Writes `buffer' of length of `len' to file descriptor `fd. */
48
49 int silc_file_write(int fd, const char *buffer, uint32 len)
50 {
51   return write(fd, (const void *)buffer, len);
52 }
53
54 /* Closes file descriptor */
55
56 int silc_file_close(int fd)
57 {
58   return close(fd);
59 }
60
61 /* Writes a buffer to the file. */
62
63 int silc_file_writefile(const char *filename, const char *buffer, uint32 len)
64 {
65   int fd;
66         
67   if ((fd = creat(filename, 0644)) == -1) {
68     SILC_LOG_ERROR(("Cannot open file %s for writing: %s", filename,
69                     strerror(errno)));
70     return -1;
71   }
72   
73   if ((write(fd, buffer, len)) == -1) {
74     SILC_LOG_ERROR(("Cannot write to file %s: %s", filename, strerror(errno)));
75     close(fd);
76     return -1;
77   }
78
79   close(fd);
80   
81   return 0;
82 }
83
84 /* Writes a buffer to the file.  If the file is created specific mode is
85    set to the file. */
86
87 int silc_file_writefile_mode(const char *filename, const char *buffer, 
88                              uint32 len, int mode)
89 {
90   int fd;
91         
92   if ((fd = creat(filename, mode)) == -1) {
93     SILC_LOG_ERROR(("Cannot open file %s for writing: %s", filename,
94                     strerror(errno)));
95     return -1;
96   }
97   
98   if ((write(fd, buffer, len)) == -1) {
99     SILC_LOG_ERROR(("Cannot write to file %s: %s", filename, strerror(errno)));
100     close(fd);
101     return -1;
102   }
103
104   close(fd);
105   
106   return 0;
107 }
108
109 /* Reads a file to a buffer. The allocated buffer is returned. Length of
110    the file read is returned to the return_len argument. */
111
112 char *silc_file_readfile(const char *filename, uint32 *return_len)
113 {
114   int fd;
115   char *buffer;
116   int filelen;
117
118   fd = silc_file_open(filename, O_RDONLY);
119   if (fd < 0) {
120     if (errno == ENOENT)
121       return NULL;
122     SILC_LOG_ERROR(("Cannot open file %s: %s", filename, strerror(errno)));
123     return NULL;
124   }
125
126   filelen = lseek(fd, (off_t)0L, SEEK_END);
127   if (filelen < 0) {
128     close(fd);
129     return NULL;
130   }
131   if (lseek(fd, (off_t)0L, SEEK_SET) < 0) {
132     close(fd);
133     return NULL;
134   }
135
136   if (filelen < 0) {
137     SILC_LOG_ERROR(("Cannot open file %s: %s", filename, strerror(errno)));
138     close(fd);
139     return NULL;
140   }
141   
142   buffer = silc_calloc(filelen + 1, sizeof(char));
143   
144   if ((read(fd, buffer, filelen)) == -1) {
145     memset(buffer, 0, sizeof(buffer));
146     close(fd);
147     SILC_LOG_ERROR(("Cannot read from file %s: %s", filename,
148                     strerror(errno)));
149     return NULL;
150   }
151
152   close(fd);
153   buffer[filelen] = EOF;
154
155   if (return_len)
156     *return_len = filelen;
157
158   return buffer;
159 }
160
161 /* Returns files size. Returns 0 on error. */
162
163 uint64 silc_file_size(const char *filename)
164 {
165   int ret;
166   struct stat stats;
167
168   ret = lstat(filename, &stats);
169   if (ret < 0)
170     return 0;
171
172   return (uint64)stats.st_size;
173 }
174
175 /* Gets line from a buffer. Stops reading when a newline or EOF occurs.
176    This doesn't remove the newline sign from the destination buffer. The
177    argument begin is returned and should be passed again for the function. */
178
179 int silc_gets(char *dest, int destlen, const char *src, int srclen, int begin)
180 {
181   static int start = 0;
182   int i;
183   
184   memset(dest, 0, destlen);
185   
186   if (begin != start)
187     start = 0;
188   
189   i = 0;
190   for ( ; start <= srclen; i++, start++) {
191     if (i > destlen)
192       return -1;
193     
194     dest[i] = src[start];
195     
196     if (dest[i] == EOF) 
197       return EOF;
198     
199     if (dest[i] == '\n') 
200       break;
201   }
202   start++;
203   
204   return start;
205 }
206
207 /* Checks line for illegal characters. Return -1 when illegal character
208    were found. This is used to check for bad lines when reading data from
209    for example a configuration file. */
210
211 int silc_check_line(char *buf) 
212 {
213   /* Illegal characters in line */
214   if (strchr(buf, '#')) return -1;
215   if (strchr(buf, '\'')) return -1;
216   if (strchr(buf, '\\')) return -1;
217   if (strchr(buf, '\r')) return -1;
218   if (strchr(buf, '\a')) return -1;
219   if (strchr(buf, '\b')) return -1;
220   if (strchr(buf, '\f')) return -1;
221   
222   /* Empty line */
223   if (buf[0] == '\n')
224     return -1;
225   
226   return 0;
227 }
228
229 /* Returns current time as string. */
230
231 char *silc_get_time()
232 {
233   time_t curtime;
234   char *return_time;
235
236   curtime = time(NULL);
237   return_time = ctime(&curtime);
238   return_time[strlen(return_time) - 1] = '\0';
239
240   return return_time;
241 }
242
243 /* Converts string to capital characters */
244
245 char *silc_to_upper(char *string)
246 {
247   int i;
248   char *ret = silc_calloc(strlen(string) + 1, sizeof(char));
249
250   for (i = 0; i < strlen(string); i++)
251     ret[i] = toupper(string[i]);
252
253   return ret;
254 }
255
256 static unsigned char pem_enc[64] =
257 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
258
259 /* Encodes data into PEM encoding. Returns NULL terminated PEM encoded
260    data string. Note: This is originally public domain code and is 
261    still PD. */
262
263 char *silc_encode_pem(unsigned char *data, uint32 len)
264 {
265   int i, j;
266   uint32 bits, c, char_count;
267   char *pem;
268
269   char_count = 0;
270   bits = 0;
271   j = 0;
272
273   pem = silc_calloc(((len * 8 + 5) / 6) + 5, sizeof(*pem));
274
275   for (i = 0; i < len; i++) {
276     c = data[i];
277     bits += c;
278     char_count++;
279
280     if (char_count == 3) {
281       pem[j++] = pem_enc[bits  >> 18];
282       pem[j++] = pem_enc[(bits >> 12) & 0x3f];
283       pem[j++] = pem_enc[(bits >> 6)  & 0x3f];
284       pem[j++] = pem_enc[bits & 0x3f];
285       bits = 0;
286       char_count = 0;
287     } else {
288       bits <<= 8;
289     }
290   }
291
292   if (char_count != 0) {
293     bits <<= 16 - (8 * char_count);
294     pem[j++] = pem_enc[bits >> 18];
295     pem[j++] = pem_enc[(bits >> 12) & 0x3f];
296
297     if (char_count == 1) {
298       pem[j++] = '=';
299       pem[j] = '=';
300     } else {
301       pem[j++] = pem_enc[(bits >> 6) & 0x3f];
302       pem[j] = '=';
303     }
304   }
305
306   return pem;
307 }
308
309 /* Same as above but puts newline ('\n') every 72 characters. */
310
311 char *silc_encode_pem_file(unsigned char *data, uint32 data_len)
312 {
313   int i, j;
314   uint32 len, cols;
315   char *pem, *pem2;
316
317   pem = silc_encode_pem(data, data_len);
318   len = strlen(pem);
319
320   pem2 = silc_calloc(len + (len / 72) + 1, sizeof(*pem2));
321
322   for (i = 0, j = 0, cols = 1; i < len; i++, cols++) {
323     if (cols == 72) {
324       pem2[i] = '\n';
325       cols = 0;
326       len++;
327       continue;
328     }
329
330     pem2[i] = pem[j++];
331   }
332
333   silc_free(pem);
334   return pem2;
335 }
336
337 /* Decodes PEM into data. Returns the decoded data. Note: This is
338    originally public domain code and is still PD. */
339
340 unsigned char *silc_decode_pem(unsigned char *pem, uint32 pem_len,
341                                uint32 *ret_len)
342 {
343   int i, j;
344   uint32 len, c, char_count, bits;
345   unsigned char *data;
346   static char ialpha[256], decoder[256];
347
348   for (i = 64 - 1; i >= 0; i--) {
349     ialpha[pem_enc[i]] = 1;
350     decoder[pem_enc[i]] = i;
351   }
352
353   char_count = 0;
354   bits = 0;
355   j = 0;
356
357   if (!pem_len)
358     len = strlen(pem);
359   else
360     len = pem_len;
361
362   data = silc_calloc(((len * 6) / 8), sizeof(*data));
363
364   for (i = 0; i < len; i++) {
365     c = pem[i];
366
367     if (c == '=')
368       break;
369
370     if (c > 127 || !ialpha[c])
371       continue;
372
373     bits += decoder[c];
374     char_count++;
375
376     if (char_count == 4) {
377       data[j++] = bits >> 16;
378       data[j++] = (bits >> 8) & 0xff;
379       data[j++] = bits & 0xff;
380       bits = 0;
381       char_count = 0;
382     } else {
383       bits <<= 6;
384     }
385   }
386
387   switch(char_count) {
388   case 1:
389     silc_free(data);
390     return NULL;
391     break;
392   case 2:
393     data[j++] = bits >> 10;
394     break;
395   case 3:
396     data[j++] = bits >> 16;
397     data[j++] = (bits >> 8) & 0xff;
398     break;
399   }
400
401   if (ret_len)
402     *ret_len = j;
403
404   return data;
405 }
406
407 /* Parse userfqdn string which is in user@fqdn format */
408
409 bool silc_parse_userfqdn(const char *string, char **left, char **right)
410 {
411   uint32 tlen;
412
413   if (!string)
414     return FALSE;
415
416   if (strchr(string, '@')) {
417     tlen = strcspn(string, "@");
418     
419     if (left) {
420       *left = silc_calloc(tlen + 1, sizeof(char));
421       memcpy(*left, string, tlen);
422     }
423     
424     if (right) {
425       *right = silc_calloc((strlen(string) - tlen) + 1, sizeof(char));
426       memcpy(*right, string + tlen + 1, strlen(string) - tlen - 1);
427     }
428   } else {
429     if (left)
430       *left = strdup(string);
431   }
432
433   return TRUE;
434 }
435
436 /* Parses command line. At most `max_args' is taken. Rest of the line
437    will be allocated as the last argument if there are more than `max_args'
438    arguments in the line. Note that the command name is counted as one
439    argument and is saved. */
440
441 void silc_parse_command_line(unsigned char *buffer, 
442                              unsigned char ***parsed,
443                              uint32 **parsed_lens,
444                              uint32 **parsed_types,
445                              uint32 *parsed_num,
446                              uint32 max_args)
447 {
448   int i, len = 0;
449   int argc = 0;
450   const char *cp = buffer;
451   char *tmp;
452
453   *parsed = silc_calloc(1, sizeof(**parsed));
454   *parsed_lens = silc_calloc(1, sizeof(**parsed_lens));
455
456   /* Get the command first */
457   len = strcspn(cp, " ");
458   tmp = silc_to_upper((char *)cp);
459   (*parsed)[0] = silc_calloc(len + 1, sizeof(char));
460   memcpy((*parsed)[0], tmp, len);
461   silc_free(tmp);
462   (*parsed_lens)[0] = len;
463   cp += len;
464   while (*cp == ' ')
465     cp++;
466   argc++;
467
468   /* Parse arguments */
469   if (strchr(cp, ' ') || strlen(cp) != 0) {
470     for (i = 1; i < max_args; i++) {
471
472       if (i != max_args - 1)
473         len = strcspn(cp, " ");
474       else
475         len = strlen(cp);
476       while (len && cp[len - 1] == ' ')
477         len--;
478       if (!len)
479         break;
480       
481       *parsed = silc_realloc(*parsed, sizeof(**parsed) * (argc + 1));
482       *parsed_lens = silc_realloc(*parsed_lens, 
483                                   sizeof(**parsed_lens) * (argc + 1));
484       (*parsed)[argc] = silc_calloc(len + 1, sizeof(char));
485       memcpy((*parsed)[argc], cp, len);
486       (*parsed_lens)[argc] = len;
487       argc++;
488
489       cp += len;
490       if (strlen(cp) == 0)
491         break;
492       else
493         while (*cp == ' ')
494           cp++;
495     }
496   }
497
498   /* Save argument types. Protocol defines all argument types but
499      this implementation makes sure that they are always in correct
500      order hence this simple code. */
501   *parsed_types = silc_calloc(argc, sizeof(**parsed_types));
502   for (i = 0; i < argc; i++)
503     (*parsed_types)[i] = i;
504
505   *parsed_num = argc;
506 }
507
508 /* Formats arguments to a string and returns it after allocating memory
509    for it. It must be remembered to free it later. */
510
511 char *silc_format(char *fmt, ...)
512 {
513   va_list args;
514   static char buf[8192];
515
516   memset(buf, 0, sizeof(buf));
517   va_start(args, fmt);
518   vsnprintf(buf, sizeof(buf) - 1, fmt, args);
519   va_end(args);
520
521   return strdup(buf);
522 }
523
524 /* Renders ID to suitable to print for example to log file. */
525
526 static char rid[256];
527
528 char *silc_id_render(void *id, uint16 type)
529 {
530   char tmp[100];
531   unsigned char tmps[2];
532
533   memset(rid, 0, sizeof(rid));
534   switch(type) {
535   case SILC_ID_SERVER:
536     {
537       SilcServerID *server_id = (SilcServerID *)id;
538       struct in_addr ipv4;
539
540       if (server_id->ip.data_len > 4) {
541
542       } else {
543         SILC_GET32_MSB(ipv4.s_addr, server_id->ip.data);
544         strcat(rid, inet_ntoa(ipv4));
545       }
546
547       memset(tmp, 0, sizeof(tmp));
548       snprintf(tmp, sizeof(tmp), ",%d,", ntohs(server_id->port));
549       strcat(rid, tmp);
550       SILC_PUT16_MSB(server_id->rnd, tmps);
551       memset(tmp, 0, sizeof(tmp));
552       snprintf(tmp, sizeof(tmp), "[%02x %02x]", tmps[0], tmps[1]);
553       strcat(rid, tmp);
554     }
555     break;
556   case SILC_ID_CLIENT:
557     {
558       SilcClientID *client_id = (SilcClientID *)id;
559       struct in_addr ipv4;
560
561       if (client_id->ip.data_len > 4) {
562
563       } else {
564         SILC_GET32_MSB(ipv4.s_addr, client_id->ip.data);
565         strcat(rid, inet_ntoa(ipv4));
566       }
567
568       memset(tmp, 0, sizeof(tmp));
569       snprintf(tmp, sizeof(tmp), ",%02x,", client_id->rnd);
570       strcat(rid, tmp);
571       memset(tmp, 0, sizeof(tmp));
572       snprintf(tmp, sizeof(tmp), "[%02x %02x %02x %02x...]", 
573                client_id->hash[0], client_id->hash[1],
574                client_id->hash[2], client_id->hash[3]);
575       strcat(rid, tmp);
576     }
577     break;
578   case SILC_ID_CHANNEL:
579     {
580       SilcChannelID *channel_id = (SilcChannelID *)id;
581       struct in_addr ipv4;
582
583       if (channel_id->ip.data_len > 4) {
584
585       } else {
586         SILC_GET32_MSB(ipv4.s_addr, channel_id->ip.data);
587         strcat(rid, inet_ntoa(ipv4));
588       }
589
590       memset(tmp, 0, sizeof(tmp));
591       snprintf(tmp, sizeof(tmp), ",%d,", ntohs(channel_id->port));
592       strcat(rid, tmp);
593       SILC_PUT16_MSB(channel_id->rnd, tmps);
594       memset(tmp, 0, sizeof(tmp));
595       snprintf(tmp, sizeof(tmp), "[%02x %02x]", tmps[0], tmps[1]);
596       strcat(rid, tmp);
597     }
598     break;
599   }
600
601   return rid;
602 }
603
604 /* Compares two strings. Strings may include wildcards * and ?.
605    Returns TRUE if strings match. */
606
607 int silc_string_compare(char *string1, char *string2)
608 {
609   int i;
610   int slen1 = strlen(string1);
611   int slen2 = strlen(string2);
612   char *tmpstr1, *tmpstr2;
613
614   if (!string1 || !string2)
615     return FALSE;
616
617   /* See if they are same already */
618   if (!strncmp(string1, string2, strlen(string2)))
619     return TRUE;
620
621   if (slen2 < slen1)
622     if (!strchr(string1, '*'))
623       return FALSE;
624   
625   /* Take copies of the original strings as we will change them */
626   tmpstr1 = silc_calloc(slen1 + 1, sizeof(char));
627   memcpy(tmpstr1, string1, slen1);
628   tmpstr2 = silc_calloc(slen2 + 1, sizeof(char));
629   memcpy(tmpstr2, string2, slen2);
630   
631   for (i = 0; i < slen1; i++) {
632     
633     /* * wildcard. Only one * wildcard is possible. */
634     if (tmpstr1[i] == '*')
635       if (!strncmp(tmpstr1, tmpstr2, i)) {
636         memset(tmpstr2, 0, slen2);
637         strncpy(tmpstr2, tmpstr1, i);
638         break;
639       }
640     
641     /* ? wildcard */
642     if (tmpstr1[i] == '?') {
643       if (!strncmp(tmpstr1, tmpstr2, i)) {
644         if (!(slen1 < i + 1))
645           if (tmpstr1[i + 1] != '?' &&
646               tmpstr1[i + 1] != tmpstr2[i + 1])
647             continue;
648         
649         if (!(slen1 < slen2))
650           tmpstr2[i] = '?';
651       }
652     }
653   }
654   
655   /* if using *, remove it */
656   if (strchr(tmpstr1, '*'))
657     *strchr(tmpstr1, '*') = 0;
658   
659   if (!strcmp(tmpstr1, tmpstr2)) {
660     memset(tmpstr1, 0, slen1);
661     memset(tmpstr2, 0, slen2);
662     silc_free(tmpstr1);
663     silc_free(tmpstr2);
664     return TRUE;
665   }
666   
667   memset(tmpstr1, 0, slen1);
668   memset(tmpstr2, 0, slen2);
669   silc_free(tmpstr1);
670   silc_free(tmpstr2);
671   return FALSE;
672 }
673
674 /* Basic has function to hash strings. May be used with the SilcHashTable. 
675    Note that this lowers the characters of the string (with tolower()) so
676    this is used usually with nicknames, channel and server names to provide
677    case insensitive keys. */
678
679 uint32 silc_hash_string(void *key, void *user_context)
680 {
681   char *s = (char *)key;
682   uint32 h = 0, g;
683   
684   while (*s != '\0') {
685     h = (h << 4) + tolower(*s);
686     if ((g = h & 0xf0000000)) {
687       h = h ^ (g >> 24);
688       h = h ^ g;
689     }
690     s++;
691   }
692   
693   return h;
694 }
695
696 /* Basic hash function to hash integers. May be used with the SilcHashTable. */
697
698 uint32 silc_hash_uint(void *key, void *user_context)
699 {
700   return *(uint32 *)key;
701 }
702
703 /* Basic hash funtion to hash pointers. May be used with the SilcHashTable. */
704
705 uint32 silc_hash_ptr(void *key, void *user_context)
706 {
707   return (uint32)key;
708 }
709
710 /* Hash a ID. The `user_context' is the ID type. */
711
712 uint32 silc_hash_id(void *key, void *user_context)
713 {
714   SilcIdType id_type = (SilcIdType)(uint32)user_context;
715   uint32 h = 0;
716   int i;
717
718   switch (id_type) {
719   case SILC_ID_CLIENT:
720     {
721       SilcClientID *id = (SilcClientID *)key;
722       uint32 g;
723   
724       /* The client ID is hashed by hashing the hash of the ID 
725          (which is a truncated MD5 hash of the nickname) so that we
726          can access the entry from the cache with both Client ID but
727          with just a hash from the ID as well. */
728
729       for (i = 0; i < sizeof(id->hash); i++) {
730         h = (h << 4) + id->hash[i];
731         if ((g = h & 0xf0000000)) {
732           h = h ^ (g >> 24);
733           h = h ^ g;
734         }
735       }
736
737       return h;
738     }
739     break;
740   case SILC_ID_SERVER:
741     {
742       SilcServerID *id = (SilcServerID *)key;
743       
744       h = id->port * id->rnd;
745       for (i = 0; i < id->ip.data_len; i++)
746         h ^= id->ip.data[i];
747       
748       return h;
749     }
750     break;
751   case SILC_ID_CHANNEL:
752     {
753       SilcChannelID *id = (SilcChannelID *)key;
754       
755       h = id->port * id->rnd;
756       for (i = 0; i < id->ip.data_len; i++)
757         h ^= id->ip.data[i];
758       
759       return h;
760     }
761     break;
762   default:
763     break;
764   }
765
766   return h;
767 }
768
769 /* Hash binary data. The `user_context' is the data length. */
770
771 uint32 silc_hash_data(void *key, void *user_context)
772 {
773   uint32 len = (uint32)user_context, h = 0;
774   unsigned char *data = (unsigned char *)key;
775   int i;
776
777   h = (data[0] * data[len - 1] + 1) * len;
778   for (i = 0; i < len; i++)
779     h ^= data[i];
780
781   return h;
782 }
783
784 /* Compares two strings. May be used as SilcHashTable comparison function. */
785
786 bool silc_hash_string_compare(void *key1, void *key2, void *user_context)
787 {
788   return !strcasecmp((char *)key1, (char *)key2);
789 }
790
791 /* Compares two ID's. May be used as SilcHashTable comparison function. 
792    The Client ID's compares only the hash of the Client ID not any other
793    part of the Client ID. Other ID's are fully compared. */
794
795 bool silc_hash_id_compare(void *key1, void *key2, void *user_context)
796 {
797   SilcIdType id_type = (SilcIdType)(uint32)user_context;
798   return (id_type == SILC_ID_CLIENT ? 
799           SILC_ID_COMPARE_HASH((SilcClientID *)key1, (SilcClientID *)key2) :
800           SILC_ID_COMPARE_TYPE(key1, key2, id_type));
801 }
802
803 /* Compare two Client ID's entirely and not just the hash from the ID. */
804
805 bool silc_hash_client_id_compare(void *key1, void *key2, void *user_context)
806 {
807   return SILC_ID_COMPARE_TYPE(key1, key2, SILC_ID_CLIENT);
808 }
809
810 /* Compares binary data. May be used as SilcHashTable comparison function. */
811
812 bool silc_hash_data_compare(void *key1, void *key2, void *user_context)
813 {
814   uint32 len = (uint32)user_context;
815   return !memcmp(key1, key2, len);
816 }
817
818 /* Parses mode mask and returns the mode as string. */
819
820 char *silc_client_chmode(uint32 mode, const char *cipher, const char *hmac)
821 {
822   char string[100];
823
824   if (!mode)
825     return NULL;
826
827   memset(string, 0, sizeof(string));
828
829   if (mode & SILC_CHANNEL_MODE_PRIVATE)
830     strncat(string, "p", 1);
831
832   if (mode & SILC_CHANNEL_MODE_SECRET)
833     strncat(string, "s", 1);
834
835   if (mode & SILC_CHANNEL_MODE_PRIVKEY)
836     strncat(string, "k", 1);
837
838   if (mode & SILC_CHANNEL_MODE_INVITE)
839     strncat(string, "i", 1);
840
841   if (mode & SILC_CHANNEL_MODE_TOPIC)
842     strncat(string, "t", 1);
843
844   if (mode & SILC_CHANNEL_MODE_ULIMIT)
845     strncat(string, "l", 1);
846
847   if (mode & SILC_CHANNEL_MODE_PASSPHRASE)
848     strncat(string, "a", 1);
849
850   if (mode & SILC_CHANNEL_MODE_FOUNDER_AUTH)
851     strncat(string, "f", 1);
852
853   if (mode & SILC_CHANNEL_MODE_CIPHER)
854     strncat(string, cipher, strlen(cipher));
855
856   if (mode & SILC_CHANNEL_MODE_HMAC)
857     strncat(string, hmac, strlen(hmac));
858
859   /* Rest of mode is ignored */
860
861   return strdup(string);
862 }
863
864 /* Parses channel user mode mask and returns te mode as string */
865
866 char *silc_client_chumode(uint32 mode)
867 {
868   char string[4];
869
870   if (!mode)
871     return NULL;
872
873   memset(string, 0, sizeof(string));
874
875   if (mode & SILC_CHANNEL_UMODE_CHANFO)
876     strncat(string, "f", 1);
877
878   if (mode & SILC_CHANNEL_UMODE_CHANOP)
879     strncat(string, "o", 1);
880
881   return strdup(string);
882 }
883
884 /* Parses channel user mode and returns it as special mode character. */
885
886 char *silc_client_chumode_char(uint32 mode)
887 {
888   char string[4];
889
890   if (!mode)
891     return NULL;
892
893   memset(string, 0, sizeof(string));
894
895   if (mode & SILC_CHANNEL_UMODE_CHANFO)
896     strncat(string, "*", 1);
897
898   if (mode & SILC_CHANNEL_UMODE_CHANOP)
899     strncat(string, "@", 1);
900
901   return strdup(string);
902 }