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