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