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