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