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