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