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