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   SILC_SERVER_CMD_REPLY(list, LIST),
61
62   { NULL, 0 },
63 };
64
65 /* Process received command reply. */
66
67 void silc_server_command_reply_process(SilcServer server,
68                                        SilcSocketConnection sock,
69                                        SilcBuffer buffer)
70 {
71   SilcServerCommandReply *cmd;
72   SilcServerCommandReplyContext ctx;
73   SilcCommandPayload payload;
74   SilcCommand command;
75   uint16 ident;
76
77   SILC_LOG_DEBUG(("Start"));
78
79   /* Get command reply payload from packet */
80   payload = silc_command_payload_parse(buffer->data, buffer->len);
81   if (!payload) {
82     /* Silently ignore bad reply packet */
83     SILC_LOG_DEBUG(("Bad command reply packet"));
84     return;
85   }
86   
87   /* Allocate command reply context. This must be free'd by the
88      command reply routine receiving it. */
89   ctx = silc_calloc(1, sizeof(*ctx));
90   ctx->server = server;
91   ctx->sock = silc_socket_dup(sock);
92   ctx->payload = payload;
93   ctx->args = silc_command_get_args(ctx->payload);
94   ident = silc_command_get_ident(ctx->payload);
95       
96   /* Check for pending commands and mark to be exeucted */
97   ctx->callbacks = 
98     silc_server_command_pending_check(server, ctx, 
99                                       silc_command_get(ctx->payload), 
100                                       ident, &ctx->callbacks_count);
101
102   /* Execute command reply */
103   command = silc_command_get(ctx->payload);
104   for (cmd = silc_command_reply_list; cmd->cb; cmd++)
105     if (cmd->cmd == command)
106       break;
107
108   if (cmd == NULL || !cmd->cb) {
109     silc_server_command_reply_free(ctx);
110     return;
111   }
112
113   cmd->cb(ctx, NULL);
114 }
115
116 /* Free command reply context and its internals. */
117
118 void silc_server_command_reply_free(SilcServerCommandReplyContext cmd)
119 {
120   if (cmd) {
121     silc_command_payload_free(cmd->payload);
122     if (cmd->sock)
123       silc_socket_free(cmd->sock); /* Decrease the reference counter */
124     silc_free(cmd->callbacks);
125     silc_free(cmd);
126   }
127 }
128
129 /* Caches the received WHOIS information. */
130
131 static char
132 silc_server_command_reply_whois_save(SilcServerCommandReplyContext cmd)
133 {
134   SilcServer server = cmd->server;
135   unsigned char *tmp, *id_data;
136   char *nickname, *username, *realname, *servername = NULL;
137   unsigned char *fingerprint;
138   SilcClientID *client_id;
139   SilcClientEntry client;
140   char global = FALSE;
141   char *nick;
142   uint32 mode = 0, len, id_len, flen;
143   int expire = 0;
144
145   id_data = silc_argument_get_arg_type(cmd->args, 2, &id_len);
146   nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
147   username = silc_argument_get_arg_type(cmd->args, 4, &len);
148   realname = silc_argument_get_arg_type(cmd->args, 5, &len);
149   if (!id_data || !nickname || !username || !realname) {
150     SILC_LOG_ERROR(("Incomplete WHOIS info: %s %s %s",
151                     nickname ? nickname : "",
152                     username ? username : "",
153                     realname ? realname : ""));
154     return FALSE;
155   }
156
157   tmp = silc_argument_get_arg_type(cmd->args, 7, &len);
158   if (tmp)
159     SILC_GET32_MSB(mode, tmp);
160
161   client_id = silc_id_payload_parse_id(id_data, id_len);
162   if (!client_id)
163     return FALSE;
164
165   fingerprint = silc_argument_get_arg_type(cmd->args, 9, &flen);
166
167   /* Check if we have this client cached already. */
168
169   client = silc_idlist_find_client_by_id(server->local_list, client_id, 
170                                          FALSE, NULL);
171   if (!client) {
172     client = silc_idlist_find_client_by_id(server->global_list, client_id, 
173                                            FALSE, NULL);
174     global = TRUE;
175   }
176
177   if (!client) {
178     /* If router did not find such Client ID in its lists then this must
179        be bogus client or some router in the net is buggy. */
180     if (server->server_type != SILC_SERVER)
181       return FALSE;
182
183     /* Take hostname out of nick string if it includes it. */
184     silc_parse_userfqdn(nickname, &nick, &servername);
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                                     time(NULL) + 300);
194     if (!client) {
195       SILC_LOG_ERROR(("Could not add new client to the ID Cache"));
196       return FALSE;
197     }
198
199     client->data.status |= 
200       (SILC_IDLIST_STATUS_REGISTERED | SILC_IDLIST_STATUS_RESOLVED);
201     client->data.status &= ~SILC_IDLIST_STATUS_RESOLVING;
202     client->mode = mode;
203     client->servername = servername;
204   } else {
205     /* We have the client already, update the data */
206
207     SILC_LOG_DEBUG(("Updating client data"));
208
209     /* Take hostname out of nick string if it includes it. */
210     silc_parse_userfqdn(nickname, &nick, &servername);
211
212     /* Remove the old cache entry  */
213     silc_idcache_del_by_context(global ? server->global_list->clients :
214                                 server->local_list->clients, client);
215
216     silc_free(client->nickname);
217     silc_free(client->username);
218     silc_free(client->userinfo);
219     silc_free(client->servername);
220     
221     client->nickname = nick;
222     client->username = strdup(username);
223     client->userinfo = strdup(realname);
224     client->servername = servername;
225     client->mode = mode;
226     client->data.status |= SILC_IDLIST_STATUS_RESOLVED;
227     client->data.status &= ~SILC_IDLIST_STATUS_RESOLVING;
228
229     /* If client is global and is not on any channel then add that we'll
230        expire the entry after a while. */
231     if (global && !silc_hash_table_count(client->channels) &&
232         server->server_type == SILC_SERVER)
233       expire = time(NULL) + 300;
234
235     /* Create new cache entry */
236     silc_idcache_add(global ? server->global_list->clients :
237                      server->local_list->clients, nick, client->id, 
238                      client, expire, NULL); 
239     silc_free(client_id);
240   }
241
242   if (fingerprint && flen == sizeof(client->data.fingerprint))
243     memcpy(client->data.fingerprint, fingerprint, flen);
244
245   return TRUE;
246 }
247
248 /* Reiceved reply for WHOIS command. We sent the whois request to our
249    primary router, if we are normal server, and thus has now received reply
250    to the command. We will figure out what client originally sent us the
251    command and will send the reply to it.  If we are router we will figure
252    out who server sent us the command and send reply to that one. */
253
254 SILC_SERVER_CMD_REPLY_FUNC(whois)
255 {
256   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
257   SilcServer server = cmd->server;
258   SilcCommandStatus status;
259
260   COMMAND_CHECK_STATUS_LIST;
261
262   if (!silc_server_command_reply_whois_save(cmd))
263     goto out;
264
265   /* Pending callbacks are not executed if this was an list entry */
266   if (status != SILC_STATUS_OK &&
267       status != SILC_STATUS_LIST_END) {
268     silc_server_command_reply_free(cmd);
269     return;
270   }
271
272  out:
273   /* If we received notify for invalid ID we'll remove the ID if we
274      have it cached. */
275   if (status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID &&
276       cmd->sock->type == SILC_SOCKET_TYPE_ROUTER) {
277     SilcClientEntry client;
278     uint32 tmp_len;
279     unsigned char *tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
280     if (tmp) {
281       SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len);
282       if (client_id) {
283         SILC_LOG_DEBUG(("Received invalid client ID notification, deleting "
284                         "the entry from cache"));
285         client = silc_idlist_find_client_by_id(server->global_list, 
286                                                client_id, FALSE, NULL);
287         if (client)
288           silc_idlist_del_client(server->global_list, client);
289         silc_free(client_id);
290       }
291     }
292   }
293
294   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_WHOIS);
295   SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_WHOIS);
296   silc_server_command_reply_free(cmd);
297 }
298
299 /* Caches the received WHOWAS information for a short period of time. */
300
301 static char
302 silc_server_command_reply_whowas_save(SilcServerCommandReplyContext cmd)
303 {
304   SilcServer server = cmd->server;
305   uint32 len, id_len;
306   unsigned char *id_data;
307   char *nickname, *username, *realname, *servername = NULL;
308   SilcClientID *client_id;
309   SilcClientEntry client;
310   SilcIDCacheEntry cache = NULL;
311   char *nick;
312   int global = FALSE;
313
314   id_data = silc_argument_get_arg_type(cmd->args, 2, &id_len);
315   nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
316   username = silc_argument_get_arg_type(cmd->args, 4, &len);
317   if (!id_data || !nickname || !username)
318     return FALSE;
319
320   realname = silc_argument_get_arg_type(cmd->args, 5, &len);
321
322   client_id = silc_id_payload_parse_id(id_data, id_len);
323   if (!client_id)
324     return FALSE;
325
326   /* Check if we have this client cached already. */
327
328   client = silc_idlist_find_client_by_id(server->local_list, client_id,
329                                          FALSE, &cache);
330   if (!client) {
331     client = silc_idlist_find_client_by_id(server->global_list, 
332                                            client_id, FALSE, &cache);
333     global = TRUE;
334   }
335
336   if (!client) {
337     /* If router did not find such Client ID in its lists then this must
338        be bogus client or some router in the net is buggy. */
339     if (server->server_type != SILC_SERVER)
340       return FALSE;
341
342     /* Take hostname out of nick string if it includes it. */
343     silc_parse_userfqdn(nickname, &nick, &servername);
344
345     /* We don't have that client anywhere, add it. The client is added
346        to global list since server didn't have it in the lists so it must be 
347        global. */
348     client = silc_idlist_add_client(server->global_list, nick,
349                                     strdup(username), strdup(realname), 
350                                     silc_id_dup(client_id, SILC_ID_CLIENT), 
351                                     cmd->sock->user_data, NULL,
352                                     SILC_ID_CACHE_EXPIRE_DEF);
353     if (!client) {
354       SILC_LOG_ERROR(("Could not add new client to the ID Cache"));
355       return FALSE;
356     }
357
358     client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED; 
359     client->servername = servername;
360   } else {
361     /* We have the client already, update the data */
362
363     /* Take hostname out of nick string if it includes it. */
364     silc_parse_userfqdn(nickname, &nick, &servername);
365
366     silc_free(client->nickname);
367     silc_free(client->username);
368     
369     client->nickname = nick;
370     client->username = strdup(username);
371     client->servername = servername;
372
373     /* Remove the old cache entry and create a new one */
374     silc_idcache_del_by_context(global ? server->global_list->clients :
375                                 server->local_list->clients, client);
376     silc_idcache_add(global ? server->global_list->clients :
377                      server->local_list->clients, nick, client->id, 
378                      client, 0, NULL);
379   }
380
381   silc_free(client_id);
382
383   return TRUE;
384 }
385
386 /* Received reply for WHOWAS command. Cache the client information only for
387    a short period of time. */
388
389 SILC_SERVER_CMD_REPLY_FUNC(whowas)
390 {
391   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
392   SilcCommandStatus status;
393
394   COMMAND_CHECK_STATUS_LIST;
395
396   if (!silc_server_command_reply_whowas_save(cmd))
397     goto out;
398
399   /* Pending callbacks are not executed if this was an list entry */
400   if (status != SILC_STATUS_OK &&
401       status != SILC_STATUS_LIST_END) {
402     silc_server_command_reply_free(cmd);
403     return;
404   }
405
406  out:
407   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_WHOWAS);
408   SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_WHOWAS);
409   silc_server_command_reply_free(cmd);
410 }
411
412 /* Caches the received IDENTIFY information. */
413
414 static char
415 silc_server_command_reply_identify_save(SilcServerCommandReplyContext cmd)
416 {
417   SilcServer server = cmd->server;
418   uint32 len, id_len;
419   unsigned char *id_data;
420   char *name, *info;
421   SilcClientID *client_id = NULL;
422   SilcServerID *server_id = NULL;
423   SilcChannelID *channel_id = NULL;
424   SilcClientEntry client;
425   SilcServerEntry server_entry;
426   SilcChannelEntry channel;
427   char global = FALSE;
428   char *nick = NULL;
429   SilcIDPayload idp = NULL;
430   SilcIdType id_type;
431   int expire = 0;
432
433   id_data = silc_argument_get_arg_type(cmd->args, 2, &id_len);
434   if (!id_data)
435     return FALSE;
436   idp = silc_id_payload_parse(id_data, id_len);
437   if (!idp)
438     return FALSE;
439
440   name = silc_argument_get_arg_type(cmd->args, 3, &len);
441   info = silc_argument_get_arg_type(cmd->args, 4, &len);
442
443   id_type = silc_id_payload_get_type(idp);
444
445   switch (id_type) {
446   case SILC_ID_CLIENT:
447     client_id = silc_id_payload_get_id(idp);
448     if (!client_id)
449       goto error;
450
451     SILC_LOG_DEBUG(("Received client information"));
452
453     client = silc_idlist_find_client_by_id(server->local_list, 
454                                            client_id, FALSE, NULL);
455     if (!client) {
456       client = silc_idlist_find_client_by_id(server->global_list, client_id,
457                                              FALSE, NULL);
458       global = TRUE;
459     }
460     if (!client) {
461       /* If router did not find such Client ID in its lists then this must
462          be bogus client or some router in the net is buggy. */
463       if (server->server_type != SILC_SERVER)
464         goto error;
465
466       /* Take nickname */
467       if (name)
468         silc_parse_userfqdn(name, &nick, NULL);
469
470       /* We don't have that client anywhere, add it. The client is added
471          to global list since server didn't have it in the lists so it must be 
472          global. */
473       client = silc_idlist_add_client(server->global_list, nick, 
474                                       info ? strdup(info) : NULL, NULL,
475                                       client_id, cmd->sock->user_data, NULL,
476                                       time(NULL) + 300);
477       if (!client) {
478         SILC_LOG_ERROR(("Could not add new client to the ID Cache"));
479         goto error;
480       }
481       client->data.status |= SILC_IDLIST_STATUS_REGISTERED;
482       client->data.status |= SILC_IDLIST_STATUS_RESOLVED;
483       client->data.status &= ~SILC_IDLIST_STATUS_RESOLVING;
484     } else {
485       /* We have the client already, update the data */
486       
487       SILC_LOG_DEBUG(("Updating client data"));
488       
489       /* Take nickname */
490       if (name) {
491         silc_parse_userfqdn(name, &nick, NULL);
492
493         /* Remove the old cache entry */
494         silc_idcache_del_by_context(global ? server->global_list->clients :
495                                     server->local_list->clients, client);
496
497         silc_free(client->nickname);
498         client->nickname = nick;
499       }
500       
501       if (info) {
502         silc_free(client->username);
503         client->username = strdup(info);
504       }
505
506       client->data.status |= SILC_IDLIST_STATUS_RESOLVED;
507       client->data.status &= ~SILC_IDLIST_STATUS_RESOLVING;
508       
509       if (name) {
510         /* If client is global and is not on any channel then add that we'll
511            expire the entry after a while. */
512         if (global && !silc_hash_table_count(client->channels) &&
513             server->server_type == SILC_SERVER)
514           expire = time(NULL) + 300;
515
516         /* Add new cache entry */
517         silc_idcache_add(global ? server->global_list->clients :
518                          server->local_list->clients, nick, client->id, 
519                          client, expire, NULL);
520       }
521
522       silc_free(client_id);
523     }
524
525     break;
526
527   case SILC_ID_SERVER:
528     if (!name)
529       goto error;
530
531     server_id = silc_id_payload_get_id(idp);
532     if (!server_id)
533       goto error;
534
535     SILC_LOG_DEBUG(("Received server information"));
536
537     server_entry = silc_idlist_find_server_by_id(server->local_list, 
538                                                  server_id, FALSE, NULL);
539     if (!server_entry)
540       server_entry = silc_idlist_find_server_by_id(server->global_list, 
541                                                    server_id, FALSE, NULL);
542     if (!server_entry) {
543       /* If router did not find such Server ID in its lists then this must
544          be bogus server or some router in the net is buggy. */
545       if (server->server_type != SILC_SERVER)
546         goto error;
547       
548       /* We don't have that server anywhere, add it. */
549       server_entry = silc_idlist_add_server(server->global_list, 
550                                             strdup(name), 0,
551                                             server_id, NULL, NULL);
552       if (!server_entry) {
553         silc_free(server_id);
554         goto error;
555       }
556       server_entry->data.status |= SILC_IDLIST_STATUS_REGISTERED;
557       server_entry->data.status |= SILC_IDLIST_STATUS_RESOLVED;
558       server_entry->data.status &= ~SILC_IDLIST_STATUS_RESOLVING;
559       server_id = NULL;
560     }
561
562     silc_free(server_id);
563     break;
564
565   case SILC_ID_CHANNEL:
566     if (!name)
567       goto error;
568
569     channel_id = silc_id_payload_get_id(idp);
570     if (!channel_id)
571       goto error;
572
573     SILC_LOG_DEBUG(("Received channel information"));
574
575     channel = silc_idlist_find_channel_by_name(server->local_list, 
576                                                name, NULL);
577     if (!channel)
578       channel = silc_idlist_find_channel_by_name(server->global_list, 
579                                                  name, NULL);
580     if (!channel) {
581       /* If router did not find such Channel ID in its lists then this must
582          be bogus channel or some router in the net is buggy. */
583       if (server->server_type != SILC_SERVER)
584         goto error;
585       
586       /* We don't have that server anywhere, add it. */
587       channel = silc_idlist_add_channel(server->global_list, strdup(name),
588                                         SILC_CHANNEL_MODE_NONE, channel_id, 
589                                         server->router, NULL, NULL, 0);
590       if (!channel) {
591         silc_free(channel_id);
592         goto error;
593       }
594       channel_id = NULL;
595     }
596
597     silc_free(channel_id);
598     break;
599   }
600
601   silc_id_payload_free(idp);
602   return TRUE;
603
604  error:
605   silc_id_payload_free(idp);
606   return FALSE;
607 }
608
609 /* Received reply for forwarded IDENTIFY command. We have received the
610    requested identify information now and we will cache it. After this we
611    will call the pending command so that the requestee gets the information
612    after all. */
613
614 SILC_SERVER_CMD_REPLY_FUNC(identify)
615 {
616   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
617   SilcServer server = cmd->server;
618   SilcCommandStatus status;
619
620   COMMAND_CHECK_STATUS_LIST;
621
622   if (!silc_server_command_reply_identify_save(cmd))
623     goto out;
624
625   /* Pending callbacks are not executed if this was an list entry */
626   if (status != SILC_STATUS_OK &&
627       status != SILC_STATUS_LIST_END) {
628     silc_server_command_reply_free(cmd);
629     return;
630   }
631
632  out:
633   /* If we received notify for invalid ID we'll remove the ID if we
634      have it cached. */
635   if (status == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID &&
636       cmd->sock->type == SILC_SOCKET_TYPE_ROUTER) {
637     SilcClientEntry client;
638     uint32 tmp_len;
639     unsigned char *tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
640     if (tmp) {
641       SilcClientID *client_id = silc_id_payload_parse_id(tmp, tmp_len);
642       if (client_id) {
643         SILC_LOG_DEBUG(("Received invalid client ID notification, deleting "
644                         "the entry from cache"));
645         client = silc_idlist_find_client_by_id(server->global_list, 
646                                                client_id, FALSE, NULL);
647         if (client)
648           silc_idlist_del_client(server->global_list, client);
649         silc_free(client_id);
650       }
651     }
652   }
653
654   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_IDENTIFY);
655   SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_IDENTIFY);
656   silc_server_command_reply_free(cmd);
657 }
658
659 /* Received reply fro INFO command. Cache the server and its information */
660
661 SILC_SERVER_CMD_REPLY_FUNC(info)
662 {
663   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
664   SilcServer server = cmd->server;
665   SilcCommandStatus status;
666   SilcServerEntry entry;
667   SilcServerID *server_id;
668   uint32 tmp_len;
669   unsigned char *tmp, *name;
670
671   COMMAND_CHECK_STATUS;
672
673   /* Get Server ID */
674   tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
675   if (!tmp)
676     goto out;
677   server_id = silc_id_payload_parse_id(tmp, tmp_len);
678   if (!server_id)
679     goto out;
680
681   /* Get the name */
682   name = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
683   if (tmp_len > 256)
684     goto out;
685
686   entry = silc_idlist_find_server_by_id(server->local_list, server_id, 
687                                         FALSE, NULL);
688   if (!entry) {
689     entry = silc_idlist_find_server_by_id(server->global_list, server_id, 
690                                           FALSE, NULL);
691     if (!entry) {
692       /* Add the server to global list */
693       server_id = silc_id_dup(server_id, SILC_ID_SERVER);
694       entry = silc_idlist_add_server(server->global_list, name, 0,
695                                      server_id, NULL, NULL);
696       if (!entry) {
697         silc_free(server_id);
698         goto out;
699       }
700       entry->data.status |= SILC_IDLIST_STATUS_REGISTERED;
701     }
702   }
703
704   /* Get the info string */
705   tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
706   if (tmp_len > 256)
707     tmp = NULL;
708
709   entry->server_info = tmp ? strdup(tmp) : NULL;
710
711  out:
712   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_INFO);
713   SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INFO);
714   silc_server_command_reply_free(cmd);
715 }
716
717 /* Received reply fro MOTD command. */
718
719 SILC_SERVER_CMD_REPLY_FUNC(motd)
720 {
721   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
722   SilcServer server = cmd->server;
723   SilcCommandStatus status;
724   SilcServerEntry entry = NULL;
725   SilcServerID *server_id;
726   uint32 tmp_len;
727   unsigned char *tmp;
728
729   COMMAND_CHECK_STATUS;
730
731   /* Get Server ID */
732   tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
733   if (!tmp)
734     goto out;
735   server_id = silc_id_payload_parse_id(tmp, tmp_len);
736   if (!server_id)
737     goto out;
738
739   entry = silc_idlist_find_server_by_id(server->local_list, server_id, 
740                                         TRUE, NULL);
741   if (!entry) {
742     entry = silc_idlist_find_server_by_id(server->global_list, server_id, 
743                                           TRUE, NULL);
744     if (!entry)
745       goto out;
746   }
747
748   /* Get the motd */
749   tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
750   if (tmp_len > 256)
751     tmp = NULL;
752
753   entry->motd = tmp;
754
755  out:
756   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_MOTD);
757   SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_MOTD);
758   silc_server_command_reply_free(cmd);
759
760   if (entry)
761     entry->motd = NULL;
762 }
763
764 /* Received reply for forwarded JOIN command. Router has created or joined
765    the client to the channel. We save some channel information locally
766    for future use. */
767
768 SILC_SERVER_CMD_REPLY_FUNC(join)
769 {
770   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
771   SilcServer server = cmd->server;
772   SilcIDCacheEntry cache = NULL;
773   SilcCommandStatus status;
774   SilcChannelID *id;
775   SilcClientID *client_id = NULL;
776   SilcChannelEntry entry;
777   SilcHmac hmac = NULL;
778   uint32 id_len, len, list_count;
779   unsigned char *id_string;
780   char *channel_name, *tmp;
781   uint32 mode, created;
782   SilcBuffer keyp = NULL, client_id_list = NULL, client_mode_list = NULL;
783
784   COMMAND_CHECK_STATUS;
785
786   /* Get channel name */
787   channel_name = silc_argument_get_arg_type(cmd->args, 2, NULL);
788   if (!channel_name)
789     goto out;
790
791   /* Get channel ID */
792   id_string = silc_argument_get_arg_type(cmd->args, 3, &id_len);
793   if (!id_string)
794     goto out;
795
796   /* Get client ID */
797   tmp = silc_argument_get_arg_type(cmd->args, 4, &len);
798   if (!tmp)
799     goto out;
800   client_id = silc_id_payload_parse_id(tmp, len);
801   if (!client_id)
802     goto out;
803
804   /* Get mode mask */
805   tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
806   if (!tmp)
807     goto out;
808   SILC_GET32_MSB(mode, tmp);
809
810   /* Get created boolean value */
811   tmp = silc_argument_get_arg_type(cmd->args, 6, NULL);
812   if (!tmp)
813     goto out;
814   SILC_GET32_MSB(created, tmp);
815   if (created != 0 && created != 1)
816     goto out;
817
818   /* Get channel key */
819   tmp = silc_argument_get_arg_type(cmd->args, 7, &len);
820   if (tmp) {
821     keyp = silc_buffer_alloc(len);
822     silc_buffer_pull_tail(keyp, SILC_BUFFER_END(keyp));
823     silc_buffer_put(keyp, tmp, len);
824   }
825
826   id = silc_id_payload_parse_id(id_string, id_len);
827   if (!id)
828     goto out;
829
830   /* Get hmac */
831   tmp = silc_argument_get_arg_type(cmd->args, 11, NULL);
832   if (tmp) {
833     if (!silc_hmac_alloc(tmp, NULL, &hmac))
834       goto out;
835   }
836
837   /* Get the list count */
838   tmp = silc_argument_get_arg_type(cmd->args, 12, &len);
839   if (!tmp)
840     goto out;
841   SILC_GET32_MSB(list_count, tmp);
842
843   /* Get Client ID list */
844   tmp = silc_argument_get_arg_type(cmd->args, 13, &len);
845   if (!tmp)
846     goto out;
847
848   client_id_list = silc_buffer_alloc(len);
849   silc_buffer_pull_tail(client_id_list, len);
850   silc_buffer_put(client_id_list, tmp, len);
851
852   /* Get client mode list */
853   tmp = silc_argument_get_arg_type(cmd->args, 14, &len);
854   if (!tmp)
855     goto out;
856
857   client_mode_list = silc_buffer_alloc(len);
858   silc_buffer_pull_tail(client_mode_list, len);
859   silc_buffer_put(client_mode_list, tmp, len);
860
861   /* See whether we already have the channel. */
862   entry = silc_idlist_find_channel_by_name(server->local_list, 
863                                            channel_name, &cache);
864   if (!entry) {
865     /* Add new channel */
866
867     SILC_LOG_DEBUG(("Adding new [%s] channel %s id(%s)", 
868                     (created == 0 ? "existing" : "created"), channel_name,
869                     silc_id_render(id, SILC_ID_CHANNEL)));
870
871     /* If the channel is found from global list we must move it to the
872        local list. */
873     entry = silc_idlist_find_channel_by_name(server->global_list, 
874                                              channel_name, &cache);
875     if (entry)
876       silc_idlist_del_channel(server->global_list, entry);
877
878     /* Add the channel to our local list. */
879     entry = silc_idlist_add_channel(server->local_list, strdup(channel_name), 
880                                     SILC_CHANNEL_MODE_NONE, id, 
881                                     server->router, NULL, hmac, 0);
882     if (!entry) {
883       silc_free(id);
884       goto out;
885     }
886     server->stat.my_channels++;
887   } else {
888     /* The entry exists. */
889     silc_free(cache->id);
890     entry->id = id;
891     cache->id = entry->id;
892     entry->disabled = FALSE;
893
894     /* Remove the founder auth data if the mode is not set but we have
895        them in the entry */
896     if (!(mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) && entry->founder_key) {
897       silc_pkcs_public_key_free(entry->founder_key);
898       if (entry->founder_passwd) {
899         silc_free(entry->founder_passwd);
900         entry->founder_passwd = NULL;
901       }
902     }
903   }
904
905   if (entry->hmac_name && hmac) {
906     silc_free(entry->hmac_name);
907     entry->hmac_name = strdup(silc_hmac_get_name(hmac));
908   }
909
910   /* Get the ban list */
911   tmp = silc_argument_get_arg_type(cmd->args, 8, &len);
912   if (tmp) {
913     if (entry->ban_list)
914       silc_free(entry->ban_list);
915     entry->ban_list = silc_calloc(len, sizeof(*entry->ban_list));
916     memcpy(entry->ban_list, tmp, len);
917   }
918
919   /* Get the invite list */
920   tmp = silc_argument_get_arg_type(cmd->args, 9, &len);
921   if (tmp) {
922     if (entry->invite_list)
923       silc_free(entry->invite_list);
924     entry->invite_list = silc_calloc(len, sizeof(*entry->invite_list));
925     memcpy(entry->invite_list, tmp, len);
926   }
927
928   /* Get the topic */
929   tmp = silc_argument_get_arg_type(cmd->args, 10, &len);
930   if (tmp) {
931     if (entry->topic)
932       silc_free(entry->topic);
933     entry->topic = strdup(tmp);
934   }
935
936   /* If channel was not created we know there is global users on the 
937      channel. */
938   entry->global_users = (created == 0 ? TRUE : FALSE);
939
940   /* If channel was just created the mask must be zero */
941   if (!entry->global_users && mode) {
942     SILC_LOG_DEBUG(("Buggy router `%s' sent non-zero mode mask for "
943                     "new channel, forcing it to zero", cmd->sock->hostname));
944     mode = 0;
945   }
946
947   /* Save channel mode */
948   entry->mode = mode;
949
950   /* Save channel key */
951   if (!(entry->mode & SILC_CHANNEL_MODE_PRIVKEY))
952     silc_server_save_channel_key(server, keyp, entry);
953   if (keyp)
954     silc_buffer_free(keyp);
955
956   /* Save the users to the channel */
957   silc_server_save_users_on_channel(server, cmd->sock, entry, 
958                                     client_id, client_id_list,
959                                     client_mode_list, list_count);
960
961  out:
962   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_JOIN);
963   SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_JOIN);
964   silc_free(client_id);
965   silc_server_command_reply_free(cmd);
966
967   if (client_id_list)
968     silc_buffer_free(client_id_list);
969   if (client_mode_list)
970     silc_buffer_free(client_mode_list);
971 }
972
973 SILC_SERVER_CMD_REPLY_FUNC(users)
974 {
975   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
976   SilcServer server = cmd->server;
977   SilcCommandStatus status;
978   SilcChannelEntry channel;
979   SilcChannelID *channel_id = NULL;
980   SilcBuffer client_id_list;
981   SilcBuffer client_mode_list;
982   unsigned char *tmp;
983   uint32 tmp_len;
984   uint32 list_count;
985
986   COMMAND_CHECK_STATUS;
987
988   /* Get channel ID */
989   tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
990   if (!tmp)
991     goto out;
992   channel_id = silc_id_payload_parse_id(tmp, tmp_len);
993   if (!channel_id)
994     goto out;
995
996   /* Get channel entry */
997   channel = silc_idlist_find_channel_by_id(server->local_list, 
998                                            channel_id, NULL);
999   if (!channel) {
1000     channel = silc_idlist_find_channel_by_id(server->global_list, 
1001                                              channel_id, NULL);
1002     if (!channel) {
1003       SilcBuffer idp;
1004
1005       if (server->server_type != SILC_SERVER)
1006         goto out;
1007
1008       idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
1009       silc_server_send_command(server, server->router->connection,
1010                                SILC_COMMAND_IDENTIFY, ++server->cmd_ident,
1011                                1, 5, idp->data, idp->len);
1012       silc_buffer_free(idp);
1013
1014       /* Register pending command callback. After we've received the channel
1015          information we will reprocess this command reply by re-calling this
1016          USERS command reply callback. */
1017       silc_server_command_pending(server, SILC_COMMAND_IDENTIFY, 
1018                                   server->cmd_ident,
1019                                   NULL, silc_server_command_reply_users, cmd);
1020       return;
1021     }
1022   }
1023
1024   /* Get the list count */
1025   tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
1026   if (!tmp)
1027     goto out;
1028   SILC_GET32_MSB(list_count, tmp);
1029
1030   /* Get Client ID list */
1031   tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
1032   if (!tmp)
1033     goto out;
1034
1035   client_id_list = silc_buffer_alloc(tmp_len);
1036   silc_buffer_pull_tail(client_id_list, tmp_len);
1037   silc_buffer_put(client_id_list, tmp, tmp_len);
1038
1039   /* Get client mode list */
1040   tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
1041   if (!tmp)
1042     goto out;
1043
1044   client_mode_list = silc_buffer_alloc(tmp_len);
1045   silc_buffer_pull_tail(client_mode_list, tmp_len);
1046   silc_buffer_put(client_mode_list, tmp, tmp_len);
1047
1048   /* Save the users to the channel */
1049   silc_server_save_users_on_channel(server, cmd->sock, channel, NULL,
1050                                     client_id_list, client_mode_list, 
1051                                     list_count);
1052
1053   silc_buffer_free(client_id_list);
1054   silc_buffer_free(client_mode_list);
1055
1056  out:
1057   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
1058   SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_USERS);
1059   silc_free(channel_id);
1060   silc_server_command_reply_free(cmd);
1061 }
1062
1063 SILC_SERVER_CMD_REPLY_FUNC(getkey)
1064 {
1065   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
1066   SilcServer server = cmd->server;
1067   SilcCommandStatus status;
1068   SilcClientEntry client = NULL;
1069   SilcServerEntry server_entry = NULL;
1070   SilcClientID *client_id = NULL;
1071   SilcServerID *server_id = NULL;
1072   SilcSKEPKType type;
1073   unsigned char *tmp, *pk;
1074   uint32 len;
1075   uint16 pk_len;
1076   SilcIDPayload idp = NULL;
1077   SilcIdType id_type;
1078   SilcPublicKey public_key = NULL;
1079
1080   COMMAND_CHECK_STATUS;
1081
1082   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1083   if (!tmp)
1084     goto out;
1085   idp = silc_id_payload_parse(tmp, len);
1086   if (!idp)
1087     goto out;
1088
1089   /* Get the public key payload */
1090   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
1091   if (!tmp)
1092     goto out;
1093
1094   /* Decode the public key */
1095
1096   SILC_GET16_MSB(pk_len, tmp);
1097   SILC_GET16_MSB(type, tmp + 2);
1098   pk = tmp + 4;
1099
1100   if (type != SILC_SKE_PK_TYPE_SILC)
1101     goto out;
1102
1103   if (!silc_pkcs_public_key_decode(pk, pk_len, &public_key))
1104     goto out;
1105
1106   id_type = silc_id_payload_get_type(idp);
1107   if (id_type == SILC_ID_CLIENT) {
1108     client_id = silc_id_payload_get_id(idp);
1109
1110     client = silc_idlist_find_client_by_id(server->local_list, client_id,
1111                                            TRUE, NULL);
1112     if (!client) {
1113       client = silc_idlist_find_client_by_id(server->global_list, 
1114                                              client_id, TRUE, NULL);
1115       if (!client)
1116         goto out;
1117     }
1118
1119     client->data.public_key = public_key;
1120   } else if (id_type == SILC_ID_SERVER) {
1121     server_id = silc_id_payload_get_id(idp);
1122
1123     server_entry = silc_idlist_find_server_by_id(server->local_list, server_id,
1124                                                  TRUE, NULL);
1125     if (!server_entry) {
1126       server_entry = silc_idlist_find_server_by_id(server->global_list, 
1127                                                    server_id, TRUE, NULL);
1128       if (!server_entry)
1129         goto out;
1130     }
1131
1132     server_entry->data.public_key = public_key;
1133   } else {
1134     goto out;
1135   }
1136
1137  out:
1138   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_GETKEY);
1139   SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_GETKEY);
1140   if (idp)
1141     silc_id_payload_free(idp);
1142   silc_free(client_id);
1143   silc_free(server_id);
1144   if (public_key)
1145     silc_pkcs_public_key_free(public_key);
1146   silc_server_command_reply_free(cmd);
1147 }
1148
1149 SILC_SERVER_CMD_REPLY_FUNC(list)
1150 {
1151   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
1152   SilcServer server = cmd->server;
1153   SilcCommandStatus status;
1154   SilcChannelID *channel_id = NULL;
1155   SilcChannelEntry channel;
1156   SilcIDCacheEntry cache;
1157   uint32 len;
1158   unsigned char *tmp, *name, *topic;
1159   uint32 usercount = 0;
1160   bool global_list = FALSE;
1161
1162   COMMAND_CHECK_STATUS_LIST;
1163
1164   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1165   channel_id = silc_id_payload_parse_id(tmp, len);
1166   if (!channel_id)
1167     goto out;
1168
1169   name = silc_argument_get_arg_type(cmd->args, 3, NULL);
1170   topic = silc_argument_get_arg_type(cmd->args, 4, NULL);
1171   tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
1172   if (tmp)
1173     SILC_GET32_MSB(usercount, tmp);
1174
1175   /* Add the channel entry if we do not have it already */
1176   channel = silc_idlist_find_channel_by_name(server->local_list, 
1177                                              name, &cache);
1178   if (!channel) {
1179     channel = silc_idlist_find_channel_by_name(server->global_list, 
1180                                                name, &cache);
1181     global_list = TRUE;
1182   }
1183   if (!channel) {
1184     /* If router did not find such channel in its lists then this must
1185        be bogus channel or some router in the net is buggy. */
1186     if (server->server_type != SILC_SERVER)
1187       goto out;
1188     
1189     channel = silc_idlist_add_channel(server->global_list, strdup(name),
1190                                       SILC_CHANNEL_MODE_NONE, channel_id, 
1191                                       server->router, NULL, NULL, 
1192                                       time(NULL) + 60);
1193     if (!channel)
1194       goto out;
1195     channel_id = NULL;
1196   } else {
1197     /* Found, update expiry */
1198     if (global_list && server->server_type == SILC_SERVER)
1199       cache->expire = time(NULL) + 60;
1200   }
1201
1202   if (topic) {
1203     silc_free(channel->topic);
1204     channel->topic = strdup(topic);
1205   }
1206
1207   /* Pending callbacks are not executed if this was an list entry */
1208   if (status != SILC_STATUS_OK &&
1209       status != SILC_STATUS_LIST_END) {
1210     silc_server_command_reply_free(cmd);
1211     return;
1212   }
1213
1214   /* Now purge all old entries from the global list, otherwise we'll might
1215      have non-existent entries for long periods of time in the cache. */
1216   silc_idcache_purge(server->global_list->channels);
1217
1218  out:
1219   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_LIST);
1220   SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_LIST);
1221   silc_free(channel_id);
1222   silc_server_command_reply_free(cmd);
1223 }