Integer type name change.
[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   SilcUInt16 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   SilcUInt32 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     SilcUInt32 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   SilcUInt32 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   SilcUInt32 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     SilcUInt32 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   SilcUInt32 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   SilcUInt32 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   SilcUInt32 id_len, len, list_count;
780   unsigned char *id_string;
781   char *channel_name, *tmp;
782   SilcUInt32 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(entry->id, id))
899       silc_idlist_replace_channel_id(server->local_list, entry->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     silc_free(entry->ban_list);
921     entry->ban_list = silc_memdup(tmp, len);
922   }
923
924   /* Get the invite list */
925   tmp = silc_argument_get_arg_type(cmd->args, 9, &len);
926   if (tmp) {
927     silc_free(entry->invite_list);
928     entry->invite_list = silc_memdup(tmp, len);
929   }
930
931   /* Get the topic */
932   tmp = silc_argument_get_arg_type(cmd->args, 10, &len);
933   if (tmp) {
934     silc_free(entry->topic);
935     entry->topic = strdup(tmp);
936   }
937
938   /* If channel was not created we know there is global users on the 
939      channel. */
940   entry->global_users = (created == 0 ? TRUE : FALSE);
941
942   /* If channel was just created the mask must be zero */
943   if (!entry->global_users && mode) {
944     SILC_LOG_DEBUG(("Buggy router `%s' sent non-zero mode mask for "
945                     "new channel, forcing it to zero", cmd->sock->hostname));
946     mode = 0;
947   }
948
949   /* Save channel mode */
950   entry->mode = mode;
951
952   /* Save channel key */
953   if (keyp) {
954     if (!(entry->mode & SILC_CHANNEL_MODE_PRIVKEY))
955       silc_server_save_channel_key(server, keyp, entry);
956     silc_buffer_free(keyp);
957   }
958
959   /* Save the users to the channel */
960   silc_server_save_users_on_channel(server, cmd->sock, entry, 
961                                     client_id, client_id_list,
962                                     client_mode_list, list_count);
963
964  out:
965   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_JOIN);
966   silc_free(client_id);
967   silc_server_command_reply_free(cmd);
968
969   if (client_id_list)
970     silc_buffer_free(client_id_list);
971   if (client_mode_list)
972     silc_buffer_free(client_mode_list);
973 }
974
975 SILC_SERVER_CMD_REPLY_FUNC(users)
976 {
977   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
978   SilcServer server = cmd->server;
979   SilcCommandStatus status;
980   SilcChannelEntry channel;
981   SilcChannelID *channel_id = NULL;
982   SilcBuffer client_id_list;
983   SilcBuffer client_mode_list;
984   unsigned char *tmp;
985   SilcUInt32 tmp_len;
986   SilcUInt32 list_count;
987
988   COMMAND_CHECK_STATUS;
989
990   /* Get channel ID */
991   tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
992   if (!tmp)
993     goto out;
994   channel_id = silc_id_payload_parse_id(tmp, tmp_len);
995   if (!channel_id)
996     goto out;
997
998   /* Get channel entry */
999   channel = silc_idlist_find_channel_by_id(server->local_list, 
1000                                            channel_id, NULL);
1001   if (!channel) {
1002     channel = silc_idlist_find_channel_by_id(server->global_list, 
1003                                              channel_id, NULL);
1004     if (!channel) {
1005       SilcBuffer idp;
1006
1007       if (server->server_type != SILC_SERVER)
1008         goto out;
1009
1010       idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
1011       silc_server_send_command(server, server->router->connection,
1012                                SILC_COMMAND_IDENTIFY, ++server->cmd_ident,
1013                                1, 5, idp->data, idp->len);
1014       silc_buffer_free(idp);
1015
1016       /* Register pending command callback. After we've received the channel
1017          information we will reprocess this command reply by re-calling this
1018          USERS command reply callback. */
1019       silc_server_command_pending(server, SILC_COMMAND_IDENTIFY, 
1020                                   server->cmd_ident,
1021                                   silc_server_command_reply_users, cmd);
1022       return;
1023     }
1024   }
1025
1026   /* Get the list count */
1027   tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
1028   if (!tmp)
1029     goto out;
1030   SILC_GET32_MSB(list_count, tmp);
1031
1032   /* Get Client ID list */
1033   tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
1034   if (!tmp)
1035     goto out;
1036
1037   client_id_list = silc_buffer_alloc(tmp_len);
1038   silc_buffer_pull_tail(client_id_list, tmp_len);
1039   silc_buffer_put(client_id_list, tmp, tmp_len);
1040
1041   /* Get client mode list */
1042   tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
1043   if (!tmp)
1044     goto out;
1045
1046   client_mode_list = silc_buffer_alloc(tmp_len);
1047   silc_buffer_pull_tail(client_mode_list, tmp_len);
1048   silc_buffer_put(client_mode_list, tmp, tmp_len);
1049
1050   /* Save the users to the channel */
1051   silc_server_save_users_on_channel(server, cmd->sock, channel, NULL,
1052                                     client_id_list, client_mode_list, 
1053                                     list_count);
1054
1055   silc_buffer_free(client_id_list);
1056   silc_buffer_free(client_mode_list);
1057
1058  out:
1059   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
1060   silc_free(channel_id);
1061   silc_server_command_reply_free(cmd);
1062 }
1063
1064 SILC_SERVER_CMD_REPLY_FUNC(getkey)
1065 {
1066   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
1067   SilcServer server = cmd->server;
1068   SilcCommandStatus status;
1069   SilcClientEntry client = NULL;
1070   SilcServerEntry server_entry = NULL;
1071   SilcClientID *client_id = NULL;
1072   SilcServerID *server_id = NULL;
1073   SilcSKEPKType type;
1074   unsigned char *tmp, *pk;
1075   SilcUInt32 len;
1076   SilcUInt16 pk_len;
1077   SilcIDPayload idp = NULL;
1078   SilcIdType id_type;
1079   SilcPublicKey public_key = NULL;
1080
1081   COMMAND_CHECK_STATUS;
1082
1083   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1084   if (!tmp)
1085     goto out;
1086   idp = silc_id_payload_parse(tmp, len);
1087   if (!idp)
1088     goto out;
1089
1090   /* Get the public key payload */
1091   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
1092   if (!tmp)
1093     goto out;
1094
1095   /* Decode the public key */
1096
1097   SILC_GET16_MSB(pk_len, tmp);
1098   SILC_GET16_MSB(type, tmp + 2);
1099   pk = tmp + 4;
1100
1101   if (type != SILC_SKE_PK_TYPE_SILC)
1102     goto out;
1103
1104   if (!silc_pkcs_public_key_decode(pk, pk_len, &public_key))
1105     goto out;
1106
1107   id_type = silc_id_payload_get_type(idp);
1108   if (id_type == SILC_ID_CLIENT) {
1109     client_id = silc_id_payload_get_id(idp);
1110
1111     client = silc_idlist_find_client_by_id(server->local_list, client_id,
1112                                            TRUE, NULL);
1113     if (!client) {
1114       client = silc_idlist_find_client_by_id(server->global_list, 
1115                                              client_id, TRUE, NULL);
1116       if (!client)
1117         goto out;
1118     }
1119
1120     client->data.public_key = public_key;
1121   } else if (id_type == SILC_ID_SERVER) {
1122     server_id = silc_id_payload_get_id(idp);
1123
1124     server_entry = silc_idlist_find_server_by_id(server->local_list, server_id,
1125                                                  TRUE, NULL);
1126     if (!server_entry) {
1127       server_entry = silc_idlist_find_server_by_id(server->global_list, 
1128                                                    server_id, TRUE, NULL);
1129       if (!server_entry)
1130         goto out;
1131     }
1132
1133     server_entry->data.public_key = public_key;
1134   } else {
1135     goto out;
1136   }
1137
1138  out:
1139   SILC_SERVER_PENDING_EXEC(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   SilcUInt32 len;
1158   unsigned char *tmp, *name, *topic;
1159   SilcUInt32 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   channel->user_count = usercount;
1203
1204   if (topic) {
1205     silc_free(channel->topic);
1206     channel->topic = strdup(topic);
1207   }
1208
1209   /* Pending callbacks are not executed if this was an list entry */
1210   if (status != SILC_STATUS_OK &&
1211       status != SILC_STATUS_LIST_END) {
1212     silc_server_command_reply_free(cmd);
1213     return;
1214   }
1215
1216   /* Now purge all old entries from the global list, otherwise we'll might
1217      have non-existent entries for long periods of time in the cache. */
1218   silc_idcache_purge(server->global_list->channels);
1219
1220  out:
1221   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_LIST);
1222   silc_free(channel_id);
1223   silc_server_command_reply_free(cmd);
1224 }