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(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   unsigned short 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. If we are normal server currently
125    we cache global information only for short period of time.  */
126 /* XXX cache expirying not implemented yet! */
127
128 static char
129 silc_server_command_reply_whois_save(SilcServerCommandReplyContext cmd)
130 {
131   SilcServer server = cmd->server;
132   int len, id_len;
133   unsigned char *tmp, *id_data;
134   char *nickname, *username, *realname;
135   SilcClientID *client_id;
136   SilcClientEntry client;
137   SilcIDCacheEntry cache = NULL;
138   char global = FALSE;
139   char *nick;
140   unsigned int mode = 0;
141
142   id_data = silc_argument_get_arg_type(cmd->args, 2, &id_len);
143   nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
144   username = silc_argument_get_arg_type(cmd->args, 4, &len);
145   realname = silc_argument_get_arg_type(cmd->args, 5, &len);
146   if (!id_data || !nickname || !username || !realname) {
147     SILC_LOG_ERROR(("Incomplete WHOIS info: %s %s %s",
148                     nickname ? nickname : "",
149                     username ? username : "",
150                     realname ? realname : ""));
151     return FALSE;
152   }
153
154   tmp = silc_argument_get_arg_type(cmd->args, 7, &len);
155   if (tmp)
156     SILC_GET32_MSB(mode, tmp);
157
158   client_id = silc_id_payload_parse_id(id_data, id_len);
159   if (!client_id)
160     return FALSE;
161
162   /* Check if we have this client cached already. */
163
164   client = silc_idlist_find_client_by_id(server->local_list, client_id,
165                                          &cache);
166   if (!client) {
167     client = silc_idlist_find_client_by_id(server->global_list, 
168                                            client_id, &cache);
169     global = TRUE;
170   }
171
172   if (!client) {
173     /* If router did not find such Client ID in its lists then this must
174        be bogus client or some router in the net is buggy. */
175     if (server->server_type == SILC_ROUTER)
176       return FALSE;
177
178     /* Take hostname out of nick string if it includes it. */
179     if (strchr(nickname, '@')) {
180       int len = strcspn(nickname, "@");
181       nick = silc_calloc(len + 1, sizeof(char));
182       memcpy(nick, nickname, len);
183     } else {
184       nick = strdup(nickname);
185     }
186
187     /* We don't have that client anywhere, add it. The client is added
188        to global list since server didn't have it in the lists so it must be 
189        global. */
190     client = silc_idlist_add_client(server->global_list, nick,
191                                     strdup(username), 
192                                     strdup(realname), client_id, 
193                                     cmd->sock->user_data, NULL);
194     if (!client)
195       return FALSE;
196
197     client->data.registered = TRUE;
198     client->mode = mode;
199   } else {
200     /* We have the client already, update the data */
201
202     SILC_LOG_DEBUG(("Updating client data"));
203
204     /* Take hostname out of nick string if it includes it. */
205     if (strchr(nickname, '@')) {
206       int len = strcspn(nickname, "@");
207       nick = silc_calloc(len + 1, sizeof(char));
208       memcpy(nick, nickname, len);
209     } else {
210       nick = strdup(nickname);
211     }
212
213     if (client->nickname)
214       silc_free(client->nickname);
215     if (client->username)
216       silc_free(client->username);
217     if (client->userinfo)
218       silc_free(client->userinfo);
219     
220     client->nickname = nick;
221     client->username = strdup(username);
222     client->userinfo = strdup(realname);
223     client->mode = mode;
224
225     if (cache) {
226       cache->data = nick;
227       cache->data_len = strlen(nick);
228       silc_idcache_sort_by_data(global ? server->global_list->clients : 
229                                 server->local_list->clients);
230     }
231
232     silc_free(client_id);
233   }
234
235   return TRUE;
236 }
237
238 /* Reiceved reply for WHOIS command. We sent the whois request to our
239    primary router, if we are normal server, and thus has now received reply
240    to the command. We will figure out what client originally sent us the
241    command and will send the reply to it.  If we are router we will figure
242    out who server sent us the command and send reply to that one. */
243
244 SILC_SERVER_CMD_REPLY_FUNC(whois)
245 {
246   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
247   SilcCommandStatus status;
248
249   COMMAND_CHECK_STATUS_LIST;
250
251   if (!silc_server_command_reply_whois_save(cmd))
252     goto out;
253
254   /* Execute any pending commands */
255   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_WHOIS);
256
257  out:
258   SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_WHOIS);
259   silc_server_command_reply_free(cmd);
260 }
261
262 /* Caches the received WHOWAS information for a short period of time. */
263
264 static char
265 silc_server_command_reply_whowas_save(SilcServerCommandReplyContext cmd)
266 {
267   SilcServer server = cmd->server;
268   int len, id_len;
269   unsigned char *id_data;
270   char *nickname, *username, *realname;
271   SilcClientID *client_id;
272   SilcClientEntry client;
273   SilcIDCacheEntry cache = NULL;
274   char *nick;
275   int global = FALSE;
276
277   id_data = silc_argument_get_arg_type(cmd->args, 2, &id_len);
278   nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
279   username = silc_argument_get_arg_type(cmd->args, 4, &len);
280   if (!id_data || !nickname || !username)
281     return FALSE;
282
283   realname = silc_argument_get_arg_type(cmd->args, 5, &len);
284
285   client_id = silc_id_payload_parse_id(id_data, id_len);
286   if (!client_id)
287     return FALSE;
288
289   /* Check if we have this client cached already. */
290
291   client = silc_idlist_find_client_by_id(server->local_list, client_id,
292                                          &cache);
293   if (!client) {
294     client = silc_idlist_find_client_by_id(server->global_list, 
295                                            client_id, &cache);
296     global = TRUE;
297   }
298
299   if (!client) {
300     /* If router did not find such Client ID in its lists then this must
301        be bogus client or some router in the net is buggy. */
302     if (server->server_type == SILC_ROUTER)
303       return FALSE;
304
305     /* Take hostname out of nick string if it includes it. */
306     if (strchr(nickname, '@')) {
307       int len = strcspn(nickname, "@");
308       nick = silc_calloc(len + 1, sizeof(char));
309       memcpy(nick, nickname, len);
310     } else {
311       nick = strdup(nickname);
312     }
313
314     /* We don't have that client anywhere, add it. The client is added
315        to global list since server didn't have it in the lists so it must be 
316        global. */
317     client = silc_idlist_add_client(server->global_list, nick,
318                                     strdup(username), 
319                                     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,
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 info string */
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 for forwarded JOIN command. Router has created or joined
564    the client to the channel. We save some channel information locally
565    for future use. */
566
567 SILC_SERVER_CMD_REPLY_FUNC(join)
568 {
569   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
570   SilcServer server = cmd->server;
571   SilcCommandStatus status;
572   SilcChannelID *id;
573   SilcClientID *client_id = NULL;
574   SilcChannelEntry entry;
575   SilcHmac hmac = NULL;
576   unsigned int id_len, len, list_count;
577   unsigned char *id_string;
578   char *channel_name, *tmp;
579   unsigned int mode, created;
580   SilcBuffer keyp, client_id_list, client_mode_list;
581
582   COMMAND_CHECK_STATUS;
583
584   /* Get channel name */
585   channel_name = silc_argument_get_arg_type(cmd->args, 2, NULL);
586   if (!channel_name)
587     goto out;
588
589   /* Get channel ID */
590   id_string = silc_argument_get_arg_type(cmd->args, 3, &id_len);
591   if (!id_string)
592     goto out;
593
594   /* Get client ID */
595   tmp = silc_argument_get_arg_type(cmd->args, 4, &len);
596   if (!tmp)
597     goto out;
598   client_id = silc_id_payload_parse_id(tmp, len);
599   if (!client_id)
600     goto out;
601
602   /* Get mode mask */
603   tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
604   if (!tmp)
605     goto out;
606   SILC_GET32_MSB(mode, tmp);
607
608   /* Get created boolean value */
609   tmp = silc_argument_get_arg_type(cmd->args, 6, NULL);
610   if (!tmp)
611     goto out;
612   SILC_GET32_MSB(created, tmp);
613   if (created != 0 && created != 1)
614     goto out;
615
616   /* Get channel key */
617   tmp = silc_argument_get_arg_type(cmd->args, 7, &len);
618   if (!tmp)
619     goto out;
620   keyp = silc_buffer_alloc(len);
621   silc_buffer_pull_tail(keyp, SILC_BUFFER_END(keyp));
622   silc_buffer_put(keyp, tmp, len);
623
624   id = silc_id_payload_parse_id(id_string, id_len);
625   if (!id)
626     goto out;
627
628   /* Get hmac */
629   tmp = silc_argument_get_arg_type(cmd->args, 11, NULL);
630   if (tmp) {
631     if (!silc_hmac_alloc(tmp, NULL, &hmac))
632       goto out;
633   }
634
635   /* Get the list count */
636   tmp = silc_argument_get_arg_type(cmd->args, 12, &len);
637   if (!tmp)
638     goto out;
639   SILC_GET32_MSB(list_count, tmp);
640
641   /* Get Client ID list */
642   tmp = silc_argument_get_arg_type(cmd->args, 13, &len);
643   if (!tmp)
644     goto out;
645
646   client_id_list = silc_buffer_alloc(len);
647   silc_buffer_pull_tail(client_id_list, len);
648   silc_buffer_put(client_id_list, tmp, len);
649
650   /* Get client mode list */
651   tmp = silc_argument_get_arg_type(cmd->args, 14, &len);
652   if (!tmp)
653     goto out;
654
655   client_mode_list = silc_buffer_alloc(len);
656   silc_buffer_pull_tail(client_mode_list, len);
657   silc_buffer_put(client_mode_list, tmp, len);
658
659   /* See whether we already have the channel. */
660   entry = silc_idlist_find_channel_by_id(server->local_list, id, NULL);
661   if (!entry) {
662     /* Add new channel */
663
664     SILC_LOG_DEBUG(("Adding new [%s] channel %s id(%s)", 
665                     (created == 0 ? "existing" : "created"), channel_name,
666                     silc_id_render(id, SILC_ID_CHANNEL)));
667
668     /* Add the channel to our local list. */
669     entry = silc_idlist_add_channel(server->local_list, strdup(channel_name), 
670                                     SILC_CHANNEL_MODE_NONE, id, 
671                                     server->router, NULL, hmac);
672     if (!entry) {
673       silc_free(id);
674       goto out;
675     }
676   } else {
677     silc_free(id);
678   }
679
680   /* If channel was not created we know there is global users on the 
681      channel. */
682   entry->global_users = (created == 0 ? TRUE : FALSE);
683
684   /* If channel was just created the mask must be zero */
685   if (!entry->global_users && mode) {
686     SILC_LOG_DEBUG(("Buggy router `%s' sent non-zero mode mask for "
687                     "new channel, forcing it to zero", cmd->sock->hostname));
688     mode = 0;
689   }
690
691   /* Save channel mode */
692   entry->mode = mode;
693
694   /* Save channel key */
695   silc_server_save_channel_key(server, keyp, entry);
696   silc_buffer_free(keyp);
697
698   /* Save the users to the channel */
699   silc_server_save_users_on_channel(server, cmd->sock, entry, 
700                                     client_id, client_id_list,
701                                     client_mode_list, list_count);
702
703   silc_buffer_free(client_id_list);
704   silc_buffer_free(client_mode_list);
705
706   /* Execute any pending commands */
707   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_JOIN);
708
709  out:
710   SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_JOIN);
711   if (client_id)
712     silc_free(client_id);
713   silc_server_command_reply_free(cmd);
714 }
715
716 SILC_SERVER_CMD_REPLY_FUNC(users)
717 {
718   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
719   SilcServer server = cmd->server;
720   SilcCommandStatus status;
721   SilcChannelEntry channel;
722   SilcChannelID *channel_id = NULL;
723   SilcBuffer client_id_list;
724   SilcBuffer client_mode_list;
725   unsigned char *tmp;
726   unsigned int tmp_len;
727   unsigned int list_count;
728
729   COMMAND_CHECK_STATUS;
730
731   /* Get channel ID */
732   tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
733   if (!tmp)
734     goto out;
735   channel_id = silc_id_payload_parse_id(tmp, tmp_len);
736   if (!channel_id)
737     goto out;
738
739   /* Get channel entry */
740   channel = silc_idlist_find_channel_by_id(server->local_list, 
741                                            channel_id, NULL);
742   if (!channel) {
743     channel = silc_idlist_find_channel_by_id(server->global_list, 
744                                              channel_id, NULL);
745     if (!channel)
746       goto out;
747   }
748
749   /* Get the list count */
750   tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
751   if (!tmp)
752     goto out;
753   SILC_GET32_MSB(list_count, tmp);
754
755   /* Get Client ID list */
756   tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
757   if (!tmp)
758     goto out;
759
760   client_id_list = silc_buffer_alloc(tmp_len);
761   silc_buffer_pull_tail(client_id_list, tmp_len);
762   silc_buffer_put(client_id_list, tmp, tmp_len);
763
764   /* Get client mode list */
765   tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
766   if (!tmp)
767     goto out;
768
769   client_mode_list = silc_buffer_alloc(tmp_len);
770   silc_buffer_pull_tail(client_mode_list, tmp_len);
771   silc_buffer_put(client_mode_list, tmp, tmp_len);
772
773   /* Save the users to the channel */
774   silc_server_save_users_on_channel(server, cmd->sock, channel, NULL,
775                                     client_id_list, client_mode_list, 
776                                     list_count);
777
778   silc_buffer_free(client_id_list);
779   silc_buffer_free(client_mode_list);
780
781   /* Execute any pending commands */
782   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
783
784  out:
785   SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_USERS);
786   if (channel_id)
787     silc_free(channel_id);
788   silc_server_command_reply_free(cmd);
789 }