updates. New data types.
[silc.git] / apps / silcd / command_reply.c
1 /*
2
3   command_reply.c
4
5   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
6
7   Copyright (C) 1997 - 2001 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 /* $Id$ */
21
22 #include "serverincludes.h"
23 #include "server_internal.h"
24 #include "command_reply.h"
25
26 /* All functions that call the COMMAND_CHECK_STATUS or the
27    COMMAND_CHECK_STATUS_LIST macros must have out: goto label. */
28
29 #define COMMAND_CHECK_STATUS                                              \
30 do {                                                                      \
31   SILC_LOG_DEBUG(("Start"));                                              \
32   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL)); \
33   if (status != SILC_STATUS_OK)                                           \
34     goto out;                                                             \
35 } while(0)
36
37 #define COMMAND_CHECK_STATUS_LIST                                         \
38 do {                                                                      \
39   SILC_LOG_DEBUG(("Start"));                                              \
40   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL)); \
41   if (status != SILC_STATUS_OK &&                                         \
42       status != SILC_STATUS_LIST_START &&                                 \
43       status != SILC_STATUS_LIST_ITEM &&                                  \
44       status != SILC_STATUS_LIST_END)                                     \
45     goto out;                                                             \
46 } while(0)
47
48 /* Server command reply list. Not all commands have reply function as
49    they are never sent by server. More maybe added later if need appears. */
50 SilcServerCommandReply silc_command_reply_list[] =
51 {
52   SILC_SERVER_CMD_REPLY(whois, WHOIS),
53   SILC_SERVER_CMD_REPLY(whowas, WHOWAS),
54   SILC_SERVER_CMD_REPLY(identify, IDENTIFY),
55   SILC_SERVER_CMD_REPLY(info, INFO),
56   SILC_SERVER_CMD_REPLY(motd, MOTD),
57   SILC_SERVER_CMD_REPLY(join, JOIN),
58   SILC_SERVER_CMD_REPLY(users, USERS),
59
60   { NULL, 0 },
61 };
62
63 /* Process received command reply. */
64
65 void silc_server_command_reply_process(SilcServer server,
66                                        SilcSocketConnection sock,
67                                        SilcBuffer buffer)
68 {
69   SilcServerCommandReply *cmd;
70   SilcServerCommandReplyContext ctx;
71   SilcCommandPayload payload;
72   SilcCommand command;
73   uint16 ident;
74
75   SILC_LOG_DEBUG(("Start"));
76
77   /* Get command reply payload from packet */
78   payload = silc_command_payload_parse(buffer);
79   if (!payload) {
80     /* Silently ignore bad reply packet */
81     SILC_LOG_DEBUG(("Bad command reply packet"));
82     return;
83   }
84   
85   /* Allocate command reply context. This must be free'd by the
86      command reply routine receiving it. */
87   ctx = silc_calloc(1, sizeof(*ctx));
88   ctx->server = server;
89   ctx->sock = silc_socket_dup(sock);
90   ctx->payload = payload;
91   ctx->args = silc_command_get_args(ctx->payload);
92   ident = silc_command_get_ident(ctx->payload);
93       
94   /* Check for pending commands and mark to be exeucted */
95   silc_server_command_pending_check(server, ctx, 
96                                     silc_command_get(ctx->payload), ident);
97
98   /* Execute command reply */
99   command = silc_command_get(ctx->payload);
100   for (cmd = silc_command_reply_list; cmd->cb; cmd++)
101     if (cmd->cmd == command)
102       break;
103
104   if (cmd == NULL || !cmd->cb) {
105     silc_server_command_reply_free(ctx);
106     return;
107   }
108
109   cmd->cb(ctx);
110 }
111
112 /* Free command reply context and its internals. */
113
114 void silc_server_command_reply_free(SilcServerCommandReplyContext cmd)
115 {
116   if (cmd) {
117     silc_command_free_payload(cmd->payload);
118     if (cmd->sock)
119       silc_socket_free(cmd->sock); /* Decrease the reference counter */
120     silc_free(cmd);
121   }
122 }
123
124 /* Caches the received WHOIS information. */
125
126 static char
127 silc_server_command_reply_whois_save(SilcServerCommandReplyContext cmd)
128 {
129   SilcServer server = cmd->server;
130   unsigned char *tmp, *id_data;
131   char *nickname, *username, *realname;
132   SilcClientID *client_id;
133   SilcClientEntry client;
134   SilcIDCacheEntry cache = NULL;
135   char global = FALSE;
136   char *nick;
137   uint32 mode = 0, len, id_len;
138
139   id_data = silc_argument_get_arg_type(cmd->args, 2, &id_len);
140   nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
141   username = silc_argument_get_arg_type(cmd->args, 4, &len);
142   realname = silc_argument_get_arg_type(cmd->args, 5, &len);
143   if (!id_data || !nickname || !username || !realname) {
144     SILC_LOG_ERROR(("Incomplete WHOIS info: %s %s %s",
145                     nickname ? nickname : "",
146                     username ? username : "",
147                     realname ? realname : ""));
148     return FALSE;
149   }
150
151   tmp = silc_argument_get_arg_type(cmd->args, 7, &len);
152   if (tmp)
153     SILC_GET32_MSB(mode, tmp);
154
155   client_id = silc_id_payload_parse_id(id_data, id_len);
156   if (!client_id)
157     return FALSE;
158
159   /* Check if we have this client cached already. */
160
161   client = silc_idlist_find_client_by_id(server->local_list, client_id,
162                                          &cache);
163   if (!client) {
164     client = silc_idlist_find_client_by_id(server->global_list, 
165                                            client_id, &cache);
166     global = TRUE;
167   }
168
169   if (!client) {
170     /* If router did not find such Client ID in its lists then this must
171        be bogus client or some router in the net is buggy. */
172     if (server->server_type == SILC_ROUTER)
173       return FALSE;
174
175     /* Take hostname out of nick string if it includes it. */
176     if (strchr(nickname, '@')) {
177       int len = strcspn(nickname, "@");
178       nick = silc_calloc(len + 1, sizeof(char));
179       memcpy(nick, nickname, len);
180     } else {
181       nick = strdup(nickname);
182     }
183
184     /* We don't have that client anywhere, add it. The client is added
185        to global list since server didn't have it in the lists so it must be 
186        global. */
187     client = silc_idlist_add_client(server->global_list, nick, strlen(nick),
188                                     strdup(username), 
189                                     strdup(realname), client_id, 
190                                     cmd->sock->user_data, NULL);
191     if (!client)
192       return FALSE;
193
194     client->data.registered = TRUE;
195     client->mode = mode;
196   } else {
197     /* We have the client already, update the data */
198
199     SILC_LOG_DEBUG(("Updating client data"));
200
201     /* Take hostname out of nick string if it includes it. */
202     if (strchr(nickname, '@')) {
203       int len = strcspn(nickname, "@");
204       nick = silc_calloc(len + 1, sizeof(char));
205       memcpy(nick, nickname, len);
206     } else {
207       nick = strdup(nickname);
208     }
209
210     if (client->nickname)
211       silc_free(client->nickname);
212     if (client->username)
213       silc_free(client->username);
214     if (client->userinfo)
215       silc_free(client->userinfo);
216     
217     client->nickname = nick;
218     client->username = strdup(username);
219     client->userinfo = strdup(realname);
220     client->mode = mode;
221
222     if (cache) {
223       cache->data = nick;
224       cache->data_len = strlen(nick);
225       silc_idcache_sort_by_data(global ? server->global_list->clients : 
226                                 server->local_list->clients);
227     }
228
229     silc_free(client_id);
230   }
231
232   return TRUE;
233 }
234
235 /* Reiceved reply for WHOIS command. We sent the whois request to our
236    primary router, if we are normal server, and thus has now received reply
237    to the command. We will figure out what client originally sent us the
238    command and will send the reply to it.  If we are router we will figure
239    out who server sent us the command and send reply to that one. */
240
241 SILC_SERVER_CMD_REPLY_FUNC(whois)
242 {
243   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
244   SilcCommandStatus status;
245
246   COMMAND_CHECK_STATUS_LIST;
247
248   if (!silc_server_command_reply_whois_save(cmd))
249     goto out;
250
251   /* Pending callbacks are not executed if this was an list entry */
252   if (status != SILC_STATUS_OK &&
253       status != SILC_STATUS_LIST_END) {
254     silc_server_command_reply_free(cmd);
255     return;
256   }
257
258  out:
259   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_WHOIS);
260   SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_WHOIS);
261   silc_server_command_reply_free(cmd);
262 }
263
264 /* Caches the received WHOWAS information for a short period of time. */
265
266 static char
267 silc_server_command_reply_whowas_save(SilcServerCommandReplyContext cmd)
268 {
269   SilcServer server = cmd->server;
270   uint32 len, id_len;
271   unsigned char *id_data;
272   char *nickname, *username, *realname;
273   SilcClientID *client_id;
274   SilcClientEntry client;
275   SilcIDCacheEntry cache = NULL;
276   char *nick;
277   int global = FALSE;
278
279   id_data = silc_argument_get_arg_type(cmd->args, 2, &id_len);
280   nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
281   username = silc_argument_get_arg_type(cmd->args, 4, &len);
282   if (!id_data || !nickname || !username)
283     return FALSE;
284
285   realname = silc_argument_get_arg_type(cmd->args, 5, &len);
286
287   client_id = silc_id_payload_parse_id(id_data, id_len);
288   if (!client_id)
289     return FALSE;
290
291   /* Check if we have this client cached already. */
292
293   client = silc_idlist_find_client_by_id(server->local_list, client_id,
294                                          &cache);
295   if (!client) {
296     client = silc_idlist_find_client_by_id(server->global_list, 
297                                            client_id, &cache);
298     global = TRUE;
299   }
300
301   if (!client) {
302     /* If router did not find such Client ID in its lists then this must
303        be bogus client or some router in the net is buggy. */
304     if (server->server_type == SILC_ROUTER)
305       return FALSE;
306
307     /* Take hostname out of nick string if it includes it. */
308     if (strchr(nickname, '@')) {
309       int len = strcspn(nickname, "@");
310       nick = silc_calloc(len + 1, sizeof(char));
311       memcpy(nick, nickname, len);
312     } else {
313       nick = strdup(nickname);
314     }
315
316     /* We don't have that client anywhere, add it. The client is added
317        to global list since server didn't have it in the lists so it must be 
318        global. */
319     client = silc_idlist_add_client(server->global_list, nick, strlen(nick),
320                                     strdup(username), strdup(realname), 
321                                     silc_id_dup(client_id, SILC_ID_CLIENT), 
322                                     cmd->sock->user_data, NULL);
323     if (!client)
324       return FALSE;
325
326     client->data.registered = FALSE;
327     client = silc_idlist_find_client_by_id(server->global_list, 
328                                            client_id, &cache);
329     cache->expire = SILC_ID_CACHE_EXPIRE_DEF;
330   } else {
331     /* We have the client already, update the data */
332
333     /* Take hostname out of nick string if it includes it. */
334     if (strchr(nickname, '@')) {
335       int len = strcspn(nickname, "@");
336       nick = silc_calloc(len + 1, sizeof(char));
337       memcpy(nick, nickname, len);
338     } else {
339       nick = strdup(nickname);
340     }
341
342     if (client->nickname)
343       silc_free(client->nickname);
344     if (client->username)
345       silc_free(client->username);
346     
347     client->nickname = nick;
348     client->username = strdup(username);
349
350     if (cache) {
351       cache->data = nick;
352       cache->data_len = strlen(nick);
353       silc_idcache_sort_by_data(global ? server->global_list->clients : 
354                                 server->local_list->clients);
355     }
356   }
357
358   silc_free(client_id);
359
360   return TRUE;
361 }
362
363 /* Received reply for WHOWAS command. Cache the client information only for
364    a short period of time. */
365
366 SILC_SERVER_CMD_REPLY_FUNC(whowas)
367 {
368   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
369   SilcCommandStatus status;
370
371   COMMAND_CHECK_STATUS_LIST;
372
373   if (!silc_server_command_reply_whowas_save(cmd))
374     goto out;
375
376   /* Pending callbacks are not executed if this was an list entry */
377   if (status != SILC_STATUS_OK &&
378       status != SILC_STATUS_LIST_END) {
379     silc_server_command_reply_free(cmd);
380     return;
381   }
382
383  out:
384   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_WHOWAS);
385   SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_WHOWAS);
386   silc_server_command_reply_free(cmd);
387 }
388
389 /* Caches the received IDENTIFY information. */
390
391 static char
392 silc_server_command_reply_identify_save(SilcServerCommandReplyContext cmd)
393 {
394   SilcServer server = cmd->server;
395   uint32 len, id_len;
396   unsigned char *id_data;
397   char *nickname, *username;
398   SilcClientID *client_id;
399   SilcClientEntry client;
400   SilcIDCacheEntry cache = NULL;
401   char global = FALSE;
402   char *nick = NULL;
403
404   id_data = silc_argument_get_arg_type(cmd->args, 2, &id_len);
405   nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
406   username = silc_argument_get_arg_type(cmd->args, 4, &len);
407   if (!id_data)
408     return FALSE;
409
410   client_id = silc_id_payload_parse_id(id_data, id_len);
411   if (!client_id)
412     return FALSE;
413
414   /* Check if we have this client cached already. */
415
416   client = silc_idlist_find_client_by_id(server->local_list, client_id,
417                                          &cache);
418   if (!client) {
419     client = silc_idlist_find_client_by_id(server->global_list, 
420                                            client_id, &cache);
421     global = TRUE;
422   }
423
424   if (!client) {
425     /* If router did not find such Client ID in its lists then this must
426        be bogus client or some router in the net is buggy. */
427     if (server->server_type == SILC_ROUTER)
428       return FALSE;
429
430     /* Take hostname out of nick string if it includes it. */
431     if (nickname) {
432       if (strchr(nickname, '@')) {
433         int len = strcspn(nickname, "@");
434         nick = silc_calloc(len + 1, sizeof(char));
435         memcpy(nick, nickname, len);
436       } else {
437         nick = strdup(nickname);
438       }
439     }
440
441     /* We don't have that client anywhere, add it. The client is added
442        to global list since server didn't have it in the lists so it must be 
443        global. */
444     client = silc_idlist_add_client(server->global_list, nick, strlen(nick),
445                                     username ? strdup(username) : NULL, NULL,
446                                     client_id, cmd->sock->user_data, NULL);
447     client->data.registered = TRUE;
448   } else {
449     /* We have the client already, update the data */
450
451     SILC_LOG_DEBUG(("Updating client data"));
452
453     /* Take hostname out of nick string if it includes it. */
454     if (nickname) {
455       if (strchr(nickname, '@')) {
456         int len = strcspn(nickname, "@");
457         nick = silc_calloc(len + 1, sizeof(char));
458         memcpy(nick, nickname, len);
459       } else {
460         nick = strdup(nickname);
461       }
462     }
463
464     if (nickname && client->nickname)
465       silc_free(client->nickname);
466
467     if (nickname)
468       client->nickname = nick;
469
470     if (username && client->username) {
471       silc_free(client->username);
472       client->username = strdup(username);
473     }
474
475     if (nickname && cache) {
476       cache->data = nick;
477       cache->data_len = strlen(nick);
478       silc_idcache_sort_by_data(global ? server->global_list->clients : 
479                                 server->local_list->clients);
480     }
481
482     silc_free(client_id);
483   }
484
485   return TRUE;
486 }
487
488 /* Received reply for forwarded IDENTIFY command. We have received the
489    requested identify information now and we will cache it. After this we
490    will call the pending command so that the requestee gets the information
491    after all. */
492
493 SILC_SERVER_CMD_REPLY_FUNC(identify)
494 {
495   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
496   SilcCommandStatus status;
497
498   COMMAND_CHECK_STATUS_LIST;
499
500   if (!silc_server_command_reply_identify_save(cmd))
501     goto out;
502
503   /* Pending callbacks are not executed if this was an list entry */
504   if (status != SILC_STATUS_OK &&
505       status != SILC_STATUS_LIST_END) {
506     silc_server_command_reply_free(cmd);
507     return;
508   }
509
510  out:
511   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_IDENTIFY);
512   SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_IDENTIFY);
513   silc_server_command_reply_free(cmd);
514 }
515
516 /* Received reply fro INFO command. Cache the server and its information */
517
518 SILC_SERVER_CMD_REPLY_FUNC(info)
519 {
520   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
521   SilcServer server = cmd->server;
522   SilcCommandStatus status;
523   SilcServerEntry entry;
524   SilcServerID *server_id;
525   uint32 tmp_len;
526   unsigned char *tmp, *name;
527
528   COMMAND_CHECK_STATUS;
529
530   /* Get Server ID */
531   tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
532   if (!tmp)
533     goto out;
534   server_id = silc_id_payload_parse_id(tmp, tmp_len);
535   if (!server_id)
536     goto out;
537
538   /* Get the name */
539   name = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
540   if (tmp_len > 256)
541     goto out;
542
543   entry = silc_idlist_find_server_by_id(server->local_list, server_id, NULL);
544   if (!entry) {
545     entry = silc_idlist_find_server_by_id(server->global_list, server_id, 
546                                           NULL);
547     if (!entry) {
548       /* Add the server to global list */
549       server_id = silc_id_dup(server_id, SILC_ID_SERVER);
550       entry = silc_idlist_add_server(server->global_list, name, 0,
551                                      server_id, NULL, NULL);
552       if (!entry) {
553         silc_free(server_id);
554         goto out;
555       }
556     }
557   }
558
559   /* Get the info string */
560   tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
561   if (tmp_len > 256)
562     tmp = NULL;
563
564   entry->server_info = tmp ? strdup(tmp) : NULL;
565
566  out:
567   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_INFO);
568   SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INFO);
569   silc_server_command_reply_free(cmd);
570 }
571
572 /* Received reply fro MOTD command. */
573
574 SILC_SERVER_CMD_REPLY_FUNC(motd)
575 {
576   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
577   SilcServer server = cmd->server;
578   SilcCommandStatus status;
579   SilcServerEntry entry = NULL;
580   SilcServerID *server_id;
581   uint32 tmp_len;
582   unsigned char *tmp;
583
584   COMMAND_CHECK_STATUS;
585
586   /* Get Server ID */
587   tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
588   if (!tmp)
589     goto out;
590   server_id = silc_id_payload_parse_id(tmp, tmp_len);
591   if (!server_id)
592     goto out;
593
594   entry = silc_idlist_find_server_by_id(server->local_list, server_id, NULL);
595   if (!entry) {
596     entry = silc_idlist_find_server_by_id(server->global_list, server_id, 
597                                           NULL);
598     if (!entry)
599       goto out;
600   }
601
602   /* Get the motd */
603   tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
604   if (tmp_len > 256)
605     tmp = NULL;
606
607   entry->motd = tmp;
608
609  out:
610   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_MOTD);
611   SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_MOTD);
612   silc_server_command_reply_free(cmd);
613
614   if (entry)
615     entry->motd = NULL;
616 }
617
618 /* Received reply for forwarded JOIN command. Router has created or joined
619    the client to the channel. We save some channel information locally
620    for future use. */
621
622 SILC_SERVER_CMD_REPLY_FUNC(join)
623 {
624   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
625   SilcServer server = cmd->server;
626   SilcIDCacheEntry cache = NULL;
627   SilcCommandStatus status;
628   SilcChannelID *id;
629   SilcClientID *client_id = NULL;
630   SilcChannelEntry entry;
631   SilcHmac hmac = NULL;
632   uint32 id_len, len, list_count;
633   unsigned char *id_string;
634   char *channel_name, *tmp;
635   uint32 mode, created;
636   SilcBuffer keyp = NULL, client_id_list = NULL, client_mode_list = NULL;
637
638   COMMAND_CHECK_STATUS;
639
640   /* Get channel name */
641   channel_name = silc_argument_get_arg_type(cmd->args, 2, NULL);
642   if (!channel_name)
643     goto out;
644
645   /* Get channel ID */
646   id_string = silc_argument_get_arg_type(cmd->args, 3, &id_len);
647   if (!id_string)
648     goto out;
649
650   /* Get client ID */
651   tmp = silc_argument_get_arg_type(cmd->args, 4, &len);
652   if (!tmp)
653     goto out;
654   client_id = silc_id_payload_parse_id(tmp, len);
655   if (!client_id)
656     goto out;
657
658   /* Get mode mask */
659   tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
660   if (!tmp)
661     goto out;
662   SILC_GET32_MSB(mode, tmp);
663
664   /* Get created boolean value */
665   tmp = silc_argument_get_arg_type(cmd->args, 6, NULL);
666   if (!tmp)
667     goto out;
668   SILC_GET32_MSB(created, tmp);
669   if (created != 0 && created != 1)
670     goto out;
671
672   /* Get channel key */
673   tmp = silc_argument_get_arg_type(cmd->args, 7, &len);
674   if (tmp) {
675     keyp = silc_buffer_alloc(len);
676     silc_buffer_pull_tail(keyp, SILC_BUFFER_END(keyp));
677     silc_buffer_put(keyp, tmp, len);
678   }
679
680   id = silc_id_payload_parse_id(id_string, id_len);
681   if (!id)
682     goto out;
683
684   /* Get hmac */
685   tmp = silc_argument_get_arg_type(cmd->args, 11, NULL);
686   if (tmp) {
687     if (!silc_hmac_alloc(tmp, NULL, &hmac))
688       goto out;
689   }
690
691   /* Get the list count */
692   tmp = silc_argument_get_arg_type(cmd->args, 12, &len);
693   if (!tmp)
694     goto out;
695   SILC_GET32_MSB(list_count, tmp);
696
697   /* Get Client ID list */
698   tmp = silc_argument_get_arg_type(cmd->args, 13, &len);
699   if (!tmp)
700     goto out;
701
702   client_id_list = silc_buffer_alloc(len);
703   silc_buffer_pull_tail(client_id_list, len);
704   silc_buffer_put(client_id_list, tmp, len);
705
706   /* Get client mode list */
707   tmp = silc_argument_get_arg_type(cmd->args, 14, &len);
708   if (!tmp)
709     goto out;
710
711   client_mode_list = silc_buffer_alloc(len);
712   silc_buffer_pull_tail(client_mode_list, len);
713   silc_buffer_put(client_mode_list, tmp, len);
714
715   /* See whether we already have the channel. */
716   entry = silc_idlist_find_channel_by_name(server->local_list, 
717                                            channel_name, &cache);
718   if (!entry) {
719     /* Add new channel */
720
721     SILC_LOG_DEBUG(("Adding new [%s] channel %s id(%s)", 
722                     (created == 0 ? "existing" : "created"), channel_name,
723                     silc_id_render(id, SILC_ID_CHANNEL)));
724
725     /* Add the channel to our local list. */
726     entry = silc_idlist_add_channel(server->local_list, strdup(channel_name), 
727                                     SILC_CHANNEL_MODE_NONE, id, 
728                                     server->router, NULL, hmac);
729     if (!entry) {
730       silc_free(id);
731       goto out;
732     }
733   } else {
734     /* The entry exists. */
735     if (cache->id)
736       silc_free(cache->id);
737     entry->id = id;
738     cache->id = entry->id;
739
740     /* Remove the founder auth data if the mode is not set but we have
741        them in the entry */
742     if (!(mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) && entry->founder_key) {
743       silc_pkcs_public_key_free(entry->founder_key);
744       if (entry->founder_passwd) {
745         silc_free(entry->founder_passwd);
746         entry->founder_passwd = NULL;
747       }
748     }
749   }
750
751   if (entry->hmac_name && hmac) {
752     silc_free(entry->hmac_name);
753     entry->hmac_name = strdup(hmac->hmac->name);
754   }
755
756   /* Get the ban list */
757   tmp = silc_argument_get_arg_type(cmd->args, 8, &len);
758   if (tmp) {
759     if (entry->ban_list)
760       silc_free(entry->ban_list);
761     entry->ban_list = silc_calloc(len, sizeof(*entry->ban_list));
762     memcpy(entry->ban_list, tmp, len);
763   }
764
765   /* Get the invite list */
766   tmp = silc_argument_get_arg_type(cmd->args, 9, &len);
767   if (tmp) {
768     if (entry->invite_list)
769       silc_free(entry->invite_list);
770     entry->invite_list = silc_calloc(len, sizeof(*entry->invite_list));
771     memcpy(entry->invite_list, tmp, len);
772   }
773
774   /* Get the topic */
775   tmp = silc_argument_get_arg_type(cmd->args, 10, &len);
776   if (tmp) {
777     if (entry->topic)
778       silc_free(entry->topic);
779     entry->topic = strdup(tmp);
780   }
781
782   /* If channel was not created we know there is global users on the 
783      channel. */
784   entry->global_users = (created == 0 ? TRUE : FALSE);
785
786   /* If channel was just created the mask must be zero */
787   if (!entry->global_users && mode) {
788     SILC_LOG_DEBUG(("Buggy router `%s' sent non-zero mode mask for "
789                     "new channel, forcing it to zero", cmd->sock->hostname));
790     mode = 0;
791   }
792
793   /* Save channel mode */
794   entry->mode = mode;
795
796   /* Save channel key */
797   if (!(entry->mode & SILC_CHANNEL_MODE_PRIVKEY))
798     silc_server_save_channel_key(server, keyp, entry);
799   if (keyp)
800     silc_buffer_free(keyp);
801
802   /* Save the users to the channel */
803   silc_server_save_users_on_channel(server, cmd->sock, entry, 
804                                     client_id, client_id_list,
805                                     client_mode_list, list_count);
806
807  out:
808   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_JOIN);
809   SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_JOIN);
810   if (client_id)
811     silc_free(client_id);
812   silc_server_command_reply_free(cmd);
813
814   if (client_id_list)
815     silc_buffer_free(client_id_list);
816   if (client_mode_list)
817     silc_buffer_free(client_mode_list);
818 }
819
820 SILC_SERVER_CMD_REPLY_FUNC(users)
821 {
822   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
823   SilcServer server = cmd->server;
824   SilcCommandStatus status;
825   SilcChannelEntry channel;
826   SilcChannelID *channel_id = NULL;
827   SilcBuffer client_id_list;
828   SilcBuffer client_mode_list;
829   unsigned char *tmp;
830   uint32 tmp_len;
831   uint32 list_count;
832
833   COMMAND_CHECK_STATUS;
834
835   /* Get channel ID */
836   tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
837   if (!tmp)
838     goto out;
839   channel_id = silc_id_payload_parse_id(tmp, tmp_len);
840   if (!channel_id)
841     goto out;
842
843   /* Get channel entry */
844   channel = silc_idlist_find_channel_by_id(server->local_list, 
845                                            channel_id, NULL);
846   if (!channel) {
847     channel = silc_idlist_find_channel_by_id(server->global_list, 
848                                              channel_id, NULL);
849     if (!channel)
850       goto out;
851   }
852
853   /* Get the list count */
854   tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
855   if (!tmp)
856     goto out;
857   SILC_GET32_MSB(list_count, tmp);
858
859   /* Get Client ID list */
860   tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
861   if (!tmp)
862     goto out;
863
864   client_id_list = silc_buffer_alloc(tmp_len);
865   silc_buffer_pull_tail(client_id_list, tmp_len);
866   silc_buffer_put(client_id_list, tmp, tmp_len);
867
868   /* Get client mode list */
869   tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
870   if (!tmp)
871     goto out;
872
873   client_mode_list = silc_buffer_alloc(tmp_len);
874   silc_buffer_pull_tail(client_mode_list, tmp_len);
875   silc_buffer_put(client_mode_list, tmp, tmp_len);
876
877   /* Save the users to the channel */
878   silc_server_save_users_on_channel(server, cmd->sock, channel, NULL,
879                                     client_id_list, client_mode_list, 
880                                     list_count);
881
882   silc_buffer_free(client_id_list);
883   silc_buffer_free(client_mode_list);
884
885  out:
886   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
887   SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_USERS);
888   if (channel_id)
889     silc_free(channel_id);
890   silc_server_command_reply_free(cmd);
891 }