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