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   SilcIDCacheEntry cache = NULL;
617   SilcCommandStatus status;
618   SilcChannelID *id;
619   SilcClientID *client_id = NULL;
620   SilcChannelEntry entry;
621   SilcHmac hmac = NULL;
622   unsigned int id_len, len, list_count;
623   unsigned char *id_string;
624   char *channel_name, *tmp;
625   unsigned int mode, created;
626   SilcBuffer keyp = NULL, client_id_list, client_mode_list;
627
628   COMMAND_CHECK_STATUS;
629
630   /* Get channel name */
631   channel_name = silc_argument_get_arg_type(cmd->args, 2, NULL);
632   if (!channel_name)
633     goto out;
634
635   /* Get channel ID */
636   id_string = silc_argument_get_arg_type(cmd->args, 3, &id_len);
637   if (!id_string)
638     goto out;
639
640   /* Get client ID */
641   tmp = silc_argument_get_arg_type(cmd->args, 4, &len);
642   if (!tmp)
643     goto out;
644   client_id = silc_id_payload_parse_id(tmp, len);
645   if (!client_id)
646     goto out;
647
648   /* Get mode mask */
649   tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
650   if (!tmp)
651     goto out;
652   SILC_GET32_MSB(mode, tmp);
653
654   /* Get created boolean value */
655   tmp = silc_argument_get_arg_type(cmd->args, 6, NULL);
656   if (!tmp)
657     goto out;
658   SILC_GET32_MSB(created, tmp);
659   if (created != 0 && created != 1)
660     goto out;
661
662   /* Get channel key */
663   tmp = silc_argument_get_arg_type(cmd->args, 7, &len);
664   if (tmp) {
665     keyp = silc_buffer_alloc(len);
666     silc_buffer_pull_tail(keyp, SILC_BUFFER_END(keyp));
667     silc_buffer_put(keyp, tmp, len);
668   }
669
670   id = silc_id_payload_parse_id(id_string, id_len);
671   if (!id)
672     goto out;
673
674   /* Get hmac */
675   tmp = silc_argument_get_arg_type(cmd->args, 11, NULL);
676   if (tmp) {
677     if (!silc_hmac_alloc(tmp, NULL, &hmac))
678       goto out;
679   }
680
681   /* Get the list count */
682   tmp = silc_argument_get_arg_type(cmd->args, 12, &len);
683   if (!tmp)
684     goto out;
685   SILC_GET32_MSB(list_count, tmp);
686
687   /* Get Client ID list */
688   tmp = silc_argument_get_arg_type(cmd->args, 13, &len);
689   if (!tmp)
690     goto out;
691
692   client_id_list = silc_buffer_alloc(len);
693   silc_buffer_pull_tail(client_id_list, len);
694   silc_buffer_put(client_id_list, tmp, len);
695
696   /* Get client mode list */
697   tmp = silc_argument_get_arg_type(cmd->args, 14, &len);
698   if (!tmp)
699     goto out;
700
701   client_mode_list = silc_buffer_alloc(len);
702   silc_buffer_pull_tail(client_mode_list, len);
703   silc_buffer_put(client_mode_list, tmp, len);
704
705   /* See whether we already have the channel. */
706   entry = silc_idlist_find_channel_by_name(server->local_list, 
707                                            channel_name, &cache);
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     /* The entry exists. */
725     if (entry->id)
726       silc_free(entry->id);
727     entry->id = id;
728     cache->id = entry->id;
729
730     /* Remove the founder auth data if the mode is not set but we have
731        them in the entry */
732     if (!(mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) && entry->founder_key) {
733       silc_pkcs_public_key_free(entry->founder_key);
734       if (entry->founder_passwd) {
735         silc_free(entry->founder_passwd);
736         entry->founder_passwd = NULL;
737       }
738     }
739   }
740
741   if (entry->hmac_name && hmac) {
742     silc_free(entry->hmac_name);
743     entry->hmac_name = strdup(hmac->hmac->name);
744   }
745
746   /* Get the ban list */
747   tmp = silc_argument_get_arg_type(cmd->args, 8, &len);
748   if (tmp) {
749     if (entry->ban_list)
750       silc_free(entry->ban_list);
751     entry->ban_list = silc_calloc(len, sizeof(*entry->ban_list));
752     memcpy(entry->ban_list, tmp, len);
753   }
754
755   /* Get the invite list */
756   tmp = silc_argument_get_arg_type(cmd->args, 9, &len);
757   if (tmp) {
758     if (entry->invite_list)
759       silc_free(entry->invite_list);
760     entry->invite_list = silc_calloc(len, sizeof(*entry->invite_list));
761     memcpy(entry->invite_list, tmp, len);
762   }
763
764   /* Get the topic */
765   tmp = silc_argument_get_arg_type(cmd->args, 10, &len);
766   if (tmp) {
767     if (entry->topic)
768       silc_free(entry->topic);
769     entry->topic = strdup(tmp);
770   }
771
772   /* If channel was not created we know there is global users on the 
773      channel. */
774   entry->global_users = (created == 0 ? TRUE : FALSE);
775
776   /* If channel was just created the mask must be zero */
777   if (!entry->global_users && mode) {
778     SILC_LOG_DEBUG(("Buggy router `%s' sent non-zero mode mask for "
779                     "new channel, forcing it to zero", cmd->sock->hostname));
780     mode = 0;
781   }
782
783   /* Save channel mode */
784   entry->mode = mode;
785
786   /* Save channel key */
787   if (!(entry->mode & SILC_CHANNEL_MODE_PRIVKEY)) {
788     silc_server_save_channel_key(server, keyp, entry);
789   }
790   if (keyp)
791     silc_buffer_free(keyp);
792
793   /* Save the users to the channel */
794   silc_server_save_users_on_channel(server, cmd->sock, entry, 
795                                     client_id, client_id_list,
796                                     client_mode_list, list_count);
797
798   silc_buffer_free(client_id_list);
799   silc_buffer_free(client_mode_list);
800
801   /* Execute any pending commands */
802   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_JOIN);
803
804  out:
805   SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_JOIN);
806   if (client_id)
807     silc_free(client_id);
808   silc_server_command_reply_free(cmd);
809 }
810
811 SILC_SERVER_CMD_REPLY_FUNC(users)
812 {
813   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
814   SilcServer server = cmd->server;
815   SilcCommandStatus status;
816   SilcChannelEntry channel;
817   SilcChannelID *channel_id = NULL;
818   SilcBuffer client_id_list;
819   SilcBuffer client_mode_list;
820   unsigned char *tmp;
821   unsigned int tmp_len;
822   unsigned int list_count;
823
824   COMMAND_CHECK_STATUS;
825
826   /* Get channel ID */
827   tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
828   if (!tmp)
829     goto out;
830   channel_id = silc_id_payload_parse_id(tmp, tmp_len);
831   if (!channel_id)
832     goto out;
833
834   /* Get channel entry */
835   channel = silc_idlist_find_channel_by_id(server->local_list, 
836                                            channel_id, NULL);
837   if (!channel) {
838     channel = silc_idlist_find_channel_by_id(server->global_list, 
839                                              channel_id, NULL);
840     if (!channel)
841       goto out;
842   }
843
844   /* Get the list count */
845   tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
846   if (!tmp)
847     goto out;
848   SILC_GET32_MSB(list_count, tmp);
849
850   /* Get Client ID list */
851   tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
852   if (!tmp)
853     goto out;
854
855   client_id_list = silc_buffer_alloc(tmp_len);
856   silc_buffer_pull_tail(client_id_list, tmp_len);
857   silc_buffer_put(client_id_list, tmp, tmp_len);
858
859   /* Get client mode list */
860   tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
861   if (!tmp)
862     goto out;
863
864   client_mode_list = silc_buffer_alloc(tmp_len);
865   silc_buffer_pull_tail(client_mode_list, tmp_len);
866   silc_buffer_put(client_mode_list, tmp, tmp_len);
867
868   /* Save the users to the channel */
869   silc_server_save_users_on_channel(server, cmd->sock, channel, NULL,
870                                     client_id_list, client_mode_list, 
871                                     list_count);
872
873   silc_buffer_free(client_id_list);
874   silc_buffer_free(client_mode_list);
875
876   /* Execute any pending commands */
877   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
878
879  out:
880   SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_USERS);
881   if (channel_id)
882     silc_free(channel_id);
883   silc_server_command_reply_free(cmd);
884 }