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, NULL);
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_payload_free(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, 
162                                          FALSE, NULL);
163   if (!client) {
164     client = silc_idlist_find_client_by_id(server->global_list, client_id, 
165                                            FALSE, NULL);
166     global = TRUE;
167   }
168
169   if (!client) {
170     /* If router did not find such Client ID in its lists then this must
171        be bogus client or some router in the net is buggy. */
172     if (server->server_type == SILC_ROUTER)
173       return FALSE;
174
175     /* Take hostname out of nick string if it includes it. */
176     if (strchr(nickname, '@')) {
177       int len = strcspn(nickname, "@");
178       nick = silc_calloc(len + 1, sizeof(char));
179       servername = silc_calloc((strlen(nickname) - len) + 1, sizeof(char));
180       memcpy(nick, nickname, len);
181       memcpy(servername, nickname + len + 1, strlen(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, 
190                                     strdup(username), 
191                                     strdup(realname), client_id, 
192                                     cmd->sock->user_data, NULL);
193     if (!client) {
194       SILC_LOG_ERROR(("Could not add new client to the ID Cache"));
195       return FALSE;
196     }
197
198     client->data.registered = TRUE;
199     client->mode = mode;
200     client->servername = servername;
201   } else {
202     /* We have the client already, update the data */
203
204     SILC_LOG_DEBUG(("Updating client data"));
205
206     /* Take hostname out of nick string if it includes it. */
207     if (strchr(nickname, '@')) {
208       int len = strcspn(nickname, "@");
209       nick = silc_calloc(len + 1, sizeof(char));
210       servername = silc_calloc((strlen(nickname) - len) + 1, sizeof(char));
211       memcpy(nick, nickname, len);
212       memcpy(servername, nickname + len + 1, strlen(nickname) - len);
213     } else {
214       nick = strdup(nickname);
215     }
216
217     if (client->nickname)
218       silc_free(client->nickname);
219     if (client->username)
220       silc_free(client->username);
221     if (client->userinfo)
222       silc_free(client->userinfo);
223     
224     client->nickname = nick;
225     client->username = strdup(username);
226     client->userinfo = strdup(realname);
227     client->mode = mode;
228     client->servername = servername;
229
230     /* Remove the old cache entry and create a new one */
231     silc_idcache_del_by_context(global ? server->global_list->clients :
232                                 server->local_list->clients, client);
233     silc_idcache_add(global ? server->global_list->clients :
234                      server->local_list->clients, nick, client->id, 
235                      client, FALSE);
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                                          FALSE, &cache);
302   if (!client) {
303     client = silc_idlist_find_client_by_id(server->global_list, 
304                                            client_id, FALSE, &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,
329                                     strdup(username), strdup(realname), 
330                                     silc_id_dup(client_id, SILC_ID_CLIENT), 
331                                     cmd->sock->user_data, NULL);
332     if (!client) {
333       SILC_LOG_ERROR(("Could not add new client to the ID Cache"));
334       return FALSE;
335     }
336
337     client->data.registered = FALSE;
338     client = silc_idlist_find_client_by_id(server->global_list, 
339                                            client_id, TRUE, &cache);
340     cache->expire = SILC_ID_CACHE_EXPIRE_DEF;
341     client->servername = servername;
342   } else {
343     /* We have the client already, update the data */
344
345     /* Take hostname out of nick string if it includes it. */
346     if (strchr(nickname, '@')) {
347       int len = strcspn(nickname, "@");
348       nick = silc_calloc(len + 1, sizeof(char));
349       servername = silc_calloc((strlen(nickname) - len) + 1, sizeof(char));
350       memcpy(nick, nickname, len);
351       memcpy(servername, nickname + len + 1, strlen(nickname) - len);
352     } else {
353       nick = strdup(nickname);
354     }
355
356     if (client->nickname)
357       silc_free(client->nickname);
358     if (client->username)
359       silc_free(client->username);
360     
361     client->nickname = nick;
362     client->username = strdup(username);
363     client->servername = servername;
364
365     /* Remove the old cache entry and create a new one */
366     silc_idcache_del_by_context(global ? server->global_list->clients :
367                                 server->local_list->clients, client);
368     silc_idcache_add(global ? server->global_list->clients :
369                      server->local_list->clients, nick, client->id, 
370                      client, FALSE);
371   }
372
373   silc_free(client_id);
374
375   return TRUE;
376 }
377
378 /* Received reply for WHOWAS command. Cache the client information only for
379    a short period of time. */
380
381 SILC_SERVER_CMD_REPLY_FUNC(whowas)
382 {
383   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
384   SilcCommandStatus status;
385
386   COMMAND_CHECK_STATUS_LIST;
387
388   if (!silc_server_command_reply_whowas_save(cmd))
389     goto out;
390
391   /* Pending callbacks are not executed if this was an list entry */
392   if (status != SILC_STATUS_OK &&
393       status != SILC_STATUS_LIST_END) {
394     silc_server_command_reply_free(cmd);
395     return;
396   }
397
398  out:
399   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_WHOWAS);
400   SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_WHOWAS);
401   silc_server_command_reply_free(cmd);
402 }
403
404 /* Caches the received IDENTIFY information. */
405
406 static char
407 silc_server_command_reply_identify_save(SilcServerCommandReplyContext cmd)
408 {
409   SilcServer server = cmd->server;
410   uint32 len, id_len;
411   unsigned char *id_data;
412   char *name, *info;
413   SilcClientID *client_id = NULL;
414   SilcServerID *server_id = NULL;
415   SilcChannelID *channel_id = NULL;
416   SilcClientEntry client;
417   SilcServerEntry server_entry;
418   SilcChannelEntry channel;
419   char global = FALSE;
420   char *nick = NULL;
421   SilcIDPayload idp = NULL;
422   SilcIdType id_type;
423
424   id_data = silc_argument_get_arg_type(cmd->args, 2, &id_len);
425   if (!id_data)
426     return FALSE;
427   idp = silc_id_payload_parse_data(id_data, id_len);
428   if (!idp)
429     return FALSE;
430
431   name = silc_argument_get_arg_type(cmd->args, 3, &len);
432   info = silc_argument_get_arg_type(cmd->args, 4, &len);
433
434   id_type = silc_id_payload_get_type(idp);
435
436   switch (id_type) {
437   case SILC_ID_CLIENT:
438     client_id = silc_id_payload_get_id(idp);
439     if (!client_id)
440       goto error;
441
442     SILC_LOG_DEBUG(("Received client information"));
443
444     client = silc_idlist_find_client_by_id(server->local_list, 
445                                            client_id, FALSE, NULL);
446     if (!client) {
447       client = silc_idlist_find_client_by_id(server->global_list, client_id,
448                                              FALSE, NULL);
449       global = TRUE;
450     }
451     if (!client) {
452       /* If router did not find such Client ID in its lists then this must
453          be bogus client or some router in the net is buggy. */
454       if (server->server_type == SILC_ROUTER)
455         goto error;
456       
457       /* Take hostname out of nick string if it includes it. */
458       if (name) {
459         if (strchr(name, '@')) {
460           int len = strcspn(name, "@");
461           nick = silc_calloc(len + 1, sizeof(char));
462           memcpy(nick, name, len);
463         } else {
464           nick = strdup(name);
465         }
466       }
467
468       /* We don't have that client anywhere, add it. The client is added
469          to global list since server didn't have it in the lists so it must be 
470          global. */
471       client = silc_idlist_add_client(server->global_list, nick, 
472                                       info ? strdup(info) : NULL, NULL,
473                                       client_id, cmd->sock->user_data, NULL);
474       if (!client) {
475         SILC_LOG_ERROR(("Could not add new client to the ID Cache"));
476         goto error;
477       }
478       client->data.registered = TRUE;
479     } else {
480       /* We have the client already, update the data */
481       
482       SILC_LOG_DEBUG(("Updating client data"));
483       
484       /* Take hostname out of nick string if it includes it. */
485       if (name) {
486         if (strchr(name, '@')) {
487           int len = strcspn(name, "@");
488           nick = silc_calloc(len + 1, sizeof(char));
489           memcpy(nick, name, len);
490         } else {
491           nick = strdup(name);
492         }
493       }
494       
495       if (name && client->nickname)
496         silc_free(client->nickname);
497       
498       if (nick)
499         client->nickname = nick;
500       
501       if (info && client->username) {
502         silc_free(client->username);
503         client->username = strdup(info);
504       }
505       
506       /* Remove the old cache entry and create a new one */
507       if (name) {
508         silc_idcache_del_by_context(global ? server->global_list->clients :
509                                     server->local_list->clients, client);
510         silc_idcache_add(global ? server->global_list->clients :
511                          server->local_list->clients, nick, client->id, 
512                          client, FALSE);
513       }
514
515       silc_free(client_id);
516     }
517
518     break;
519
520   case SILC_ID_SERVER:
521     server_id = silc_id_payload_get_id(idp);
522     if (!server_id)
523       goto error;
524
525     SILC_LOG_DEBUG(("Received server information"));
526
527     server_entry = silc_idlist_find_server_by_id(server->local_list, 
528                                                  server_id, FALSE, NULL);
529     if (!server_entry)
530       server_entry = silc_idlist_find_server_by_id(server->global_list, 
531                                                    server_id, FALSE, NULL);
532     if (!server_entry) {
533       /* If router did not find such Server ID in its lists then this must
534          be bogus client or some router in the net is buggy. */
535       if (server->server_type == SILC_ROUTER)
536         goto error;
537       
538       /* We don't have that server anywhere, add it. */
539       server_entry = silc_idlist_add_server(server->global_list, 
540                                             strdup(name), 0,
541                                             server_id, NULL, NULL);
542       if (!server_entry) {
543         silc_free(server_id);
544         goto error;
545       }
546       server_entry->data.registered = TRUE;
547       server_id = NULL;
548     }
549
550     silc_free(server_id);
551     break;
552
553   case SILC_ID_CHANNEL:
554     channel_id = silc_id_payload_get_id(idp);
555     if (!channel_id)
556       goto error;
557
558     SILC_LOG_DEBUG(("Received channel information"));
559
560     channel = silc_idlist_find_channel_by_id(server->local_list, 
561                                              channel_id, NULL);
562     if (!channel)
563       channel = silc_idlist_find_channel_by_id(server->global_list, channel_id,
564                                                NULL);
565     if (!channel) {
566       /* If router did not find such Server ID in its lists then this must
567          be bogus client or some router in the net is buggy. */
568       if (server->server_type == SILC_ROUTER)
569         goto error;
570       
571       /* We don't have that server anywhere, add it. */
572       channel = silc_idlist_add_channel(server->global_list, strdup(name),
573                                         SILC_CHANNEL_MODE_NONE, channel_id, 
574                                         server->router->connection, 
575                                         NULL, NULL);
576       if (!channel) {
577         silc_free(channel_id);
578         goto error;
579       }
580       channel_id = NULL;
581     }
582
583     silc_free(channel_id);
584     break;
585   }
586
587   silc_id_payload_free(idp);
588   return TRUE;
589
590  error:
591   silc_id_payload_free(idp);
592   return FALSE;
593 }
594
595 /* Received reply for forwarded IDENTIFY command. We have received the
596    requested identify information now and we will cache it. After this we
597    will call the pending command so that the requestee gets the information
598    after all. */
599
600 SILC_SERVER_CMD_REPLY_FUNC(identify)
601 {
602   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
603   SilcCommandStatus status;
604
605   COMMAND_CHECK_STATUS_LIST;
606
607   if (!silc_server_command_reply_identify_save(cmd))
608     goto out;
609
610   /* Pending callbacks are not executed if this was an list entry */
611   if (status != SILC_STATUS_OK &&
612       status != SILC_STATUS_LIST_END) {
613     silc_server_command_reply_free(cmd);
614     return;
615   }
616
617  out:
618   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_IDENTIFY);
619   SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_IDENTIFY);
620   silc_server_command_reply_free(cmd);
621 }
622
623 /* Received reply fro INFO command. Cache the server and its information */
624
625 SILC_SERVER_CMD_REPLY_FUNC(info)
626 {
627   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
628   SilcServer server = cmd->server;
629   SilcCommandStatus status;
630   SilcServerEntry entry;
631   SilcServerID *server_id;
632   uint32 tmp_len;
633   unsigned char *tmp, *name;
634
635   COMMAND_CHECK_STATUS;
636
637   /* Get Server ID */
638   tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
639   if (!tmp)
640     goto out;
641   server_id = silc_id_payload_parse_id(tmp, tmp_len);
642   if (!server_id)
643     goto out;
644
645   /* Get the name */
646   name = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
647   if (tmp_len > 256)
648     goto out;
649
650   entry = silc_idlist_find_server_by_id(server->local_list, server_id, 
651                                         FALSE, NULL);
652   if (!entry) {
653     entry = silc_idlist_find_server_by_id(server->global_list, server_id, 
654                                           FALSE, NULL);
655     if (!entry) {
656       /* Add the server to global list */
657       server_id = silc_id_dup(server_id, SILC_ID_SERVER);
658       entry = silc_idlist_add_server(server->global_list, name, 0,
659                                      server_id, NULL, NULL);
660       if (!entry) {
661         silc_free(server_id);
662         goto out;
663       }
664       entry->data.registered = TRUE;
665     }
666   }
667
668   /* Get the info string */
669   tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
670   if (tmp_len > 256)
671     tmp = NULL;
672
673   entry->server_info = tmp ? strdup(tmp) : NULL;
674
675  out:
676   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_INFO);
677   SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INFO);
678   silc_server_command_reply_free(cmd);
679 }
680
681 /* Received reply fro MOTD command. */
682
683 SILC_SERVER_CMD_REPLY_FUNC(motd)
684 {
685   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
686   SilcServer server = cmd->server;
687   SilcCommandStatus status;
688   SilcServerEntry entry = NULL;
689   SilcServerID *server_id;
690   uint32 tmp_len;
691   unsigned char *tmp;
692
693   COMMAND_CHECK_STATUS;
694
695   /* Get Server ID */
696   tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
697   if (!tmp)
698     goto out;
699   server_id = silc_id_payload_parse_id(tmp, tmp_len);
700   if (!server_id)
701     goto out;
702
703   entry = silc_idlist_find_server_by_id(server->local_list, server_id, 
704                                         TRUE, NULL);
705   if (!entry) {
706     entry = silc_idlist_find_server_by_id(server->global_list, server_id, 
707                                           TRUE, NULL);
708     if (!entry)
709       goto out;
710   }
711
712   /* Get the motd */
713   tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
714   if (tmp_len > 256)
715     tmp = NULL;
716
717   entry->motd = tmp;
718
719  out:
720   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_MOTD);
721   SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_MOTD);
722   silc_server_command_reply_free(cmd);
723
724   if (entry)
725     entry->motd = NULL;
726 }
727
728 /* Received reply for forwarded JOIN command. Router has created or joined
729    the client to the channel. We save some channel information locally
730    for future use. */
731
732 SILC_SERVER_CMD_REPLY_FUNC(join)
733 {
734   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
735   SilcServer server = cmd->server;
736   SilcIDCacheEntry cache = NULL;
737   SilcCommandStatus status;
738   SilcChannelID *id;
739   SilcClientID *client_id = NULL;
740   SilcChannelEntry entry;
741   SilcHmac hmac = NULL;
742   uint32 id_len, len, list_count;
743   unsigned char *id_string;
744   char *channel_name, *tmp;
745   uint32 mode, created;
746   SilcBuffer keyp = NULL, client_id_list = NULL, client_mode_list = NULL;
747
748   COMMAND_CHECK_STATUS;
749
750   /* Get channel name */
751   channel_name = silc_argument_get_arg_type(cmd->args, 2, NULL);
752   if (!channel_name)
753     goto out;
754
755   /* Get channel ID */
756   id_string = silc_argument_get_arg_type(cmd->args, 3, &id_len);
757   if (!id_string)
758     goto out;
759
760   /* Get client ID */
761   tmp = silc_argument_get_arg_type(cmd->args, 4, &len);
762   if (!tmp)
763     goto out;
764   client_id = silc_id_payload_parse_id(tmp, len);
765   if (!client_id)
766     goto out;
767
768   /* Get mode mask */
769   tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
770   if (!tmp)
771     goto out;
772   SILC_GET32_MSB(mode, tmp);
773
774   /* Get created boolean value */
775   tmp = silc_argument_get_arg_type(cmd->args, 6, NULL);
776   if (!tmp)
777     goto out;
778   SILC_GET32_MSB(created, tmp);
779   if (created != 0 && created != 1)
780     goto out;
781
782   /* Get channel key */
783   tmp = silc_argument_get_arg_type(cmd->args, 7, &len);
784   if (tmp) {
785     keyp = silc_buffer_alloc(len);
786     silc_buffer_pull_tail(keyp, SILC_BUFFER_END(keyp));
787     silc_buffer_put(keyp, tmp, len);
788   }
789
790   id = silc_id_payload_parse_id(id_string, id_len);
791   if (!id)
792     goto out;
793
794   /* Get hmac */
795   tmp = silc_argument_get_arg_type(cmd->args, 11, NULL);
796   if (tmp) {
797     if (!silc_hmac_alloc(tmp, NULL, &hmac))
798       goto out;
799   }
800
801   /* Get the list count */
802   tmp = silc_argument_get_arg_type(cmd->args, 12, &len);
803   if (!tmp)
804     goto out;
805   SILC_GET32_MSB(list_count, tmp);
806
807   /* Get Client ID list */
808   tmp = silc_argument_get_arg_type(cmd->args, 13, &len);
809   if (!tmp)
810     goto out;
811
812   client_id_list = silc_buffer_alloc(len);
813   silc_buffer_pull_tail(client_id_list, len);
814   silc_buffer_put(client_id_list, tmp, len);
815
816   /* Get client mode list */
817   tmp = silc_argument_get_arg_type(cmd->args, 14, &len);
818   if (!tmp)
819     goto out;
820
821   client_mode_list = silc_buffer_alloc(len);
822   silc_buffer_pull_tail(client_mode_list, len);
823   silc_buffer_put(client_mode_list, tmp, len);
824
825   /* See whether we already have the channel. */
826   entry = silc_idlist_find_channel_by_name(server->local_list, 
827                                            channel_name, &cache);
828   if (!entry) {
829     /* Add new channel */
830
831     SILC_LOG_DEBUG(("Adding new [%s] channel %s id(%s)", 
832                     (created == 0 ? "existing" : "created"), channel_name,
833                     silc_id_render(id, SILC_ID_CHANNEL)));
834
835     /* Add the channel to our local list. */
836     entry = silc_idlist_add_channel(server->local_list, strdup(channel_name), 
837                                     SILC_CHANNEL_MODE_NONE, id, 
838                                     server->router, NULL, hmac);
839     if (!entry) {
840       silc_free(id);
841       goto out;
842     }
843   } else {
844     /* The entry exists. */
845     if (cache->id)
846       silc_free(cache->id);
847     entry->id = id;
848     cache->id = entry->id;
849
850     /* Remove the founder auth data if the mode is not set but we have
851        them in the entry */
852     if (!(mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) && entry->founder_key) {
853       silc_pkcs_public_key_free(entry->founder_key);
854       if (entry->founder_passwd) {
855         silc_free(entry->founder_passwd);
856         entry->founder_passwd = NULL;
857       }
858     }
859   }
860
861   if (entry->hmac_name && hmac) {
862     silc_free(entry->hmac_name);
863     entry->hmac_name = strdup(hmac->hmac->name);
864   }
865
866   /* Get the ban list */
867   tmp = silc_argument_get_arg_type(cmd->args, 8, &len);
868   if (tmp) {
869     if (entry->ban_list)
870       silc_free(entry->ban_list);
871     entry->ban_list = silc_calloc(len, sizeof(*entry->ban_list));
872     memcpy(entry->ban_list, tmp, len);
873   }
874
875   /* Get the invite list */
876   tmp = silc_argument_get_arg_type(cmd->args, 9, &len);
877   if (tmp) {
878     if (entry->invite_list)
879       silc_free(entry->invite_list);
880     entry->invite_list = silc_calloc(len, sizeof(*entry->invite_list));
881     memcpy(entry->invite_list, tmp, len);
882   }
883
884   /* Get the topic */
885   tmp = silc_argument_get_arg_type(cmd->args, 10, &len);
886   if (tmp) {
887     if (entry->topic)
888       silc_free(entry->topic);
889     entry->topic = strdup(tmp);
890   }
891
892   /* If channel was not created we know there is global users on the 
893      channel. */
894   entry->global_users = (created == 0 ? TRUE : FALSE);
895
896   /* If channel was just created the mask must be zero */
897   if (!entry->global_users && mode) {
898     SILC_LOG_DEBUG(("Buggy router `%s' sent non-zero mode mask for "
899                     "new channel, forcing it to zero", cmd->sock->hostname));
900     mode = 0;
901   }
902
903   /* Save channel mode */
904   entry->mode = mode;
905
906   /* Save channel key */
907   if (!(entry->mode & SILC_CHANNEL_MODE_PRIVKEY))
908     silc_server_save_channel_key(server, keyp, entry);
909   if (keyp)
910     silc_buffer_free(keyp);
911
912   /* Save the users to the channel */
913   silc_server_save_users_on_channel(server, cmd->sock, entry, 
914                                     client_id, client_id_list,
915                                     client_mode_list, list_count);
916
917  out:
918   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_JOIN);
919   SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_JOIN);
920   if (client_id)
921     silc_free(client_id);
922   silc_server_command_reply_free(cmd);
923
924   if (client_id_list)
925     silc_buffer_free(client_id_list);
926   if (client_mode_list)
927     silc_buffer_free(client_mode_list);
928 }
929
930 SILC_SERVER_CMD_REPLY_FUNC(users)
931 {
932   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
933   SilcServer server = cmd->server;
934   SilcCommandStatus status;
935   SilcChannelEntry channel;
936   SilcChannelID *channel_id = NULL;
937   SilcBuffer client_id_list;
938   SilcBuffer client_mode_list;
939   unsigned char *tmp;
940   uint32 tmp_len;
941   uint32 list_count;
942
943   COMMAND_CHECK_STATUS;
944
945   /* Get channel ID */
946   tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
947   if (!tmp)
948     goto out;
949   channel_id = silc_id_payload_parse_id(tmp, tmp_len);
950   if (!channel_id)
951     goto out;
952
953   /* Get channel entry */
954   channel = silc_idlist_find_channel_by_id(server->local_list, 
955                                            channel_id, NULL);
956   if (!channel) {
957     channel = silc_idlist_find_channel_by_id(server->global_list, 
958                                              channel_id, NULL);
959     if (!channel) {
960       SilcBuffer idp;
961
962       if (server->server_type == SILC_ROUTER)
963         goto out;
964
965       idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
966       silc_server_send_command(server, server->router->connection,
967                                SILC_COMMAND_IDENTIFY, ++server->cmd_ident,
968                                1, 5, idp->data, idp->len);
969       silc_buffer_free(idp);
970
971       /* Register pending command callback. After we've received the channel
972          information we will reprocess this command reply by re-calling this
973          USERS command reply callback. */
974       silc_server_command_pending(server, SILC_COMMAND_IDENTIFY, 
975                                   server->cmd_ident,
976                                   NULL, silc_server_command_reply_users, cmd);
977       return;
978     }
979   }
980
981   /* Get the list count */
982   tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
983   if (!tmp)
984     goto out;
985   SILC_GET32_MSB(list_count, tmp);
986
987   /* Get Client ID list */
988   tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
989   if (!tmp)
990     goto out;
991
992   client_id_list = silc_buffer_alloc(tmp_len);
993   silc_buffer_pull_tail(client_id_list, tmp_len);
994   silc_buffer_put(client_id_list, tmp, tmp_len);
995
996   /* Get client mode list */
997   tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
998   if (!tmp)
999     goto out;
1000
1001   client_mode_list = silc_buffer_alloc(tmp_len);
1002   silc_buffer_pull_tail(client_mode_list, tmp_len);
1003   silc_buffer_put(client_mode_list, tmp, tmp_len);
1004
1005   /* Save the users to the channel */
1006   silc_server_save_users_on_channel(server, cmd->sock, channel, NULL,
1007                                     client_id_list, client_mode_list, 
1008                                     list_count);
1009
1010   silc_buffer_free(client_id_list);
1011   silc_buffer_free(client_mode_list);
1012
1013  out:
1014   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
1015   SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_USERS);
1016   silc_free(channel_id);
1017   silc_server_command_reply_free(cmd);
1018 }
1019
1020 SILC_SERVER_CMD_REPLY_FUNC(getkey)
1021 {
1022   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
1023   SilcServer server = cmd->server;
1024   SilcCommandStatus status;
1025   SilcClientEntry client = NULL;
1026   SilcServerEntry server_entry = NULL;
1027   SilcClientID *client_id = NULL;
1028   SilcServerID *server_id = NULL;
1029   SilcSKEPKType type;
1030   unsigned char *tmp, *pk;
1031   uint32 len;
1032   uint16 pk_len;
1033   SilcIDPayload idp = NULL;
1034   SilcIdType id_type;
1035   SilcPublicKey public_key = NULL;
1036
1037   COMMAND_CHECK_STATUS;
1038
1039   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1040   if (!tmp)
1041     goto out;
1042   idp = silc_id_payload_parse_data(tmp, len);
1043   if (!idp)
1044     goto out;
1045
1046   /* Get the public key payload */
1047   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
1048   if (!tmp)
1049     goto out;
1050
1051   /* Decode the public key */
1052
1053   SILC_GET16_MSB(pk_len, tmp);
1054   SILC_GET16_MSB(type, tmp + 2);
1055   pk = tmp + 4;
1056
1057   if (type != SILC_SKE_PK_TYPE_SILC)
1058     goto out;
1059
1060   if (!silc_pkcs_public_key_decode(pk, pk_len, &public_key))
1061     goto out;
1062
1063   id_type = silc_id_payload_get_type(idp);
1064   if (id_type == SILC_ID_CLIENT) {
1065     client_id = silc_id_payload_get_id(idp);
1066
1067     client = silc_idlist_find_client_by_id(server->local_list, client_id,
1068                                            TRUE, NULL);
1069     if (!client) {
1070       client = silc_idlist_find_client_by_id(server->global_list, 
1071                                              client_id, TRUE, NULL);
1072       if (!client)
1073         goto out;
1074     }
1075
1076     client->data.public_key = public_key;
1077   } else if (id_type == SILC_ID_SERVER) {
1078     server_id = silc_id_payload_get_id(idp);
1079
1080     server_entry = silc_idlist_find_server_by_id(server->local_list, server_id,
1081                                                  TRUE, NULL);
1082     if (!server_entry) {
1083       server_entry = silc_idlist_find_server_by_id(server->global_list, 
1084                                                    server_id, TRUE, NULL);
1085       if (!server_entry)
1086         goto out;
1087     }
1088
1089     server_entry->data.public_key = public_key;
1090   } else {
1091     goto out;
1092   }
1093
1094  out:
1095   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
1096   SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_USERS);
1097   if (idp)
1098     silc_id_payload_free(idp);
1099   silc_free(client_id);
1100   silc_free(server_id);
1101   if (public_key)
1102     silc_pkcs_public_key_free(public_key);
1103   silc_server_command_reply_free(cmd);
1104 }