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