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