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