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