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
785   COMMAND_CHECK_STATUS;
786
787   /* Get channel name */
788   channel_name = silc_argument_get_arg_type(cmd->args, 2, NULL);
789   if (!channel_name)
790     goto out;
791
792   /* Get channel ID */
793   id_string = silc_argument_get_arg_type(cmd->args, 3, &id_len);
794   if (!id_string)
795     goto out;
796
797   /* Get client ID */
798   tmp = silc_argument_get_arg_type(cmd->args, 4, &len);
799   if (!tmp)
800     goto out;
801   client_id = silc_id_payload_parse_id(tmp, len, NULL);
802   if (!client_id)
803     goto out;
804
805   /* Get mode mask */
806   tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
807   if (!tmp)
808     goto out;
809   SILC_GET32_MSB(mode, tmp);
810
811   /* Get created boolean value */
812   tmp = silc_argument_get_arg_type(cmd->args, 6, NULL);
813   if (!tmp)
814     goto out;
815   SILC_GET32_MSB(created, tmp);
816   if (created != 0 && created != 1)
817     goto out;
818
819   /* Get channel key */
820   tmp = silc_argument_get_arg_type(cmd->args, 7, &len);
821   if (tmp) {
822     keyp = silc_buffer_alloc(len);
823     silc_buffer_pull_tail(keyp, SILC_BUFFER_END(keyp));
824     silc_buffer_put(keyp, tmp, len);
825   }
826
827   /* Parse the Channel ID */
828   id = silc_id_payload_parse_id(id_string, id_len, NULL);
829   if (!id)
830     goto out;
831
832   /* Get hmac */
833   tmp = silc_argument_get_arg_type(cmd->args, 11, NULL);
834   if (tmp) {
835     if (!silc_hmac_alloc(tmp, NULL, &hmac))
836       goto out;
837   }
838
839   /* Get the list count */
840   tmp = silc_argument_get_arg_type(cmd->args, 12, &len);
841   if (!tmp)
842     goto out;
843   SILC_GET32_MSB(list_count, tmp);
844
845   /* Get Client ID list */
846   tmp = silc_argument_get_arg_type(cmd->args, 13, &len);
847   if (!tmp)
848     goto out;
849
850   client_id_list = silc_buffer_alloc(len);
851   silc_buffer_pull_tail(client_id_list, len);
852   silc_buffer_put(client_id_list, tmp, len);
853
854   /* Get client mode list */
855   tmp = silc_argument_get_arg_type(cmd->args, 14, &len);
856   if (!tmp)
857     goto out;
858
859   client_mode_list = silc_buffer_alloc(len);
860   silc_buffer_pull_tail(client_mode_list, len);
861   silc_buffer_put(client_mode_list, tmp, len);
862
863   /* See whether we already have the channel. */
864   entry = silc_idlist_find_channel_by_name(server->local_list, 
865                                            channel_name, &cache);
866   if (!entry) {
867     /* Add new channel */
868
869     SILC_LOG_DEBUG(("Adding new [%s] channel %s id(%s)", 
870                     (created == 0 ? "existing" : "created"), channel_name,
871                     silc_id_render(id, SILC_ID_CHANNEL)));
872
873     /* If the channel is found from global list we must move it to the
874        local list. */
875     entry = silc_idlist_find_channel_by_name(server->global_list,
876                                              channel_name, &cache);
877     if (entry)
878       silc_idlist_del_channel(server->global_list, entry);
879
880     /* Add the channel to our local list. */
881     entry = silc_idlist_add_channel(server->local_list, strdup(channel_name),
882                                     SILC_CHANNEL_MODE_NONE, id,
883                                     server->router, NULL, hmac, 0);
884     if (!entry) {
885       silc_free(id);
886       goto out;
887     }
888     server->stat.my_channels++;
889   } else {
890     /* The entry exists. */
891
892     /* If ID has changed, then update it to the cache too. */
893     if (!SILC_ID_CHANNEL_COMPARE(entry->id, id))
894       silc_idlist_replace_channel_id(server->local_list, entry->id, id);
895
896     entry->disabled = FALSE;
897
898     /* Remove the founder auth data if the mode is not set but we have
899        them in the entry */
900     if (!(mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) && entry->founder_key) {
901       silc_pkcs_public_key_free(entry->founder_key);
902       entry->founder_key = NULL;
903     }
904   }
905
906   if (entry->hmac_name && hmac) {
907     silc_free(entry->hmac_name);
908     entry->hmac_name = strdup(silc_hmac_get_name(hmac));
909   }
910
911   /* Get the ban list */
912   tmp = silc_argument_get_arg_type(cmd->args, 8, &len);
913   if (tmp) {
914     silc_free(entry->ban_list);
915     entry->ban_list = silc_memdup(tmp, len);
916   }
917
918   /* Get the invite list */
919   tmp = silc_argument_get_arg_type(cmd->args, 9, &len);
920   if (tmp) {
921     silc_free(entry->invite_list);
922     entry->invite_list = silc_memdup(tmp, len);
923   }
924
925   /* Get the topic */
926   tmp = silc_argument_get_arg_type(cmd->args, 10, &len);
927   if (tmp) {
928     silc_free(entry->topic);
929     entry->topic = strdup(tmp);
930   }
931
932   /* If channel was not created we know there is global users on the 
933      channel. */
934   entry->global_users = (created == 0 ? TRUE : FALSE);
935
936   /* If channel was just created the mask must be zero */
937   if (!entry->global_users && mode) {
938     SILC_LOG_DEBUG(("Buggy router `%s' sent non-zero mode mask for "
939                     "new channel, forcing it to zero", cmd->sock->hostname));
940     mode = 0;
941   }
942
943   /* Save channel mode */
944   entry->mode = mode;
945
946   /* Save channel key */
947   if (keyp) {
948     if (!(entry->mode & SILC_CHANNEL_MODE_PRIVKEY))
949       silc_server_save_channel_key(server, keyp, entry);
950     silc_buffer_free(keyp);
951   }
952
953   /* Save the users to the channel */
954   silc_server_save_users_on_channel(server, cmd->sock, entry, 
955                                     client_id, client_id_list,
956                                     client_mode_list, list_count);
957   entry->users_resolved = TRUE;
958
959  out:
960   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_JOIN);
961  err:
962   silc_free(client_id);
963   silc_server_command_reply_free(cmd);
964
965   if (client_id_list)
966     silc_buffer_free(client_id_list);
967   if (client_mode_list)
968     silc_buffer_free(client_mode_list);
969 }
970
971 /* Received reply to STATS command.  */
972
973 SILC_SERVER_CMD_REPLY_FUNC(stats)
974 {
975   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
976   SilcServer server = cmd->server;
977   SilcStatus status, error;
978   unsigned char *tmp;
979   SilcUInt32 tmp_len;
980   SilcBufferStruct buf;
981
982   COMMAND_CHECK_STATUS;
983
984   /* Get statistics structure */
985   tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
986   if (server->server_type == SILC_SERVER && tmp) {
987     silc_buffer_set(&buf, tmp, tmp_len);
988     silc_buffer_unformat(&buf,
989                          SILC_STR_UI_INT(NULL),
990                          SILC_STR_UI_INT(NULL),
991                          SILC_STR_UI_INT(NULL),
992                          SILC_STR_UI_INT(NULL),
993                          SILC_STR_UI_INT(NULL),
994                          SILC_STR_UI_INT(NULL),
995                          SILC_STR_UI_INT(&server->stat.cell_clients),
996                          SILC_STR_UI_INT(&server->stat.cell_channels),
997                          SILC_STR_UI_INT(&server->stat.cell_servers),
998                          SILC_STR_UI_INT(&server->stat.clients),
999                          SILC_STR_UI_INT(&server->stat.channels),
1000                          SILC_STR_UI_INT(&server->stat.servers),
1001                          SILC_STR_UI_INT(&server->stat.routers),
1002                          SILC_STR_UI_INT(&server->stat.server_ops),
1003                          SILC_STR_UI_INT(&server->stat.router_ops),
1004                          SILC_STR_END);
1005   }
1006
1007  out:
1008   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_STATS);
1009  err:
1010   silc_server_command_reply_free(cmd);
1011 }
1012
1013 SILC_SERVER_CMD_REPLY_FUNC(users)
1014 {
1015   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
1016   SilcServer server = cmd->server;
1017   SilcStatus status, error;
1018   SilcChannelEntry channel;
1019   SilcChannelID *channel_id = NULL;
1020   SilcBuffer client_id_list;
1021   SilcBuffer client_mode_list;
1022   unsigned char *tmp;
1023   SilcUInt32 tmp_len;
1024   SilcUInt32 list_count;
1025
1026   COMMAND_CHECK_STATUS;
1027
1028   /* Get channel ID */
1029   tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
1030   if (!tmp)
1031     goto out;
1032   channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
1033   if (!channel_id)
1034     goto out;
1035
1036   /* Get channel entry */
1037   channel = silc_idlist_find_channel_by_id(server->local_list, 
1038                                            channel_id, NULL);
1039   if (!channel) {
1040     channel = silc_idlist_find_channel_by_id(server->global_list, 
1041                                              channel_id, NULL);
1042     if (!channel) {
1043       SilcBuffer idp;
1044
1045       if (server->server_type != SILC_SERVER)
1046         goto out;
1047
1048       idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
1049       silc_server_send_command(server, server->router->connection,
1050                                SILC_COMMAND_IDENTIFY, ++server->cmd_ident,
1051                                1, 5, idp->data, idp->len);
1052       silc_buffer_free(idp);
1053
1054       /* Register pending command callback. After we've received the channel
1055          information we will reprocess this command reply by re-calling this
1056          USERS command reply callback. */
1057       silc_server_command_pending(server, SILC_COMMAND_IDENTIFY, 
1058                                   server->cmd_ident,
1059                                   silc_server_command_reply_users, cmd);
1060       return;
1061     }
1062   }
1063
1064   /* Get the list count */
1065   tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
1066   if (!tmp)
1067     goto out;
1068   SILC_GET32_MSB(list_count, tmp);
1069
1070   /* Get Client ID list */
1071   tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
1072   if (!tmp)
1073     goto out;
1074
1075   client_id_list = silc_buffer_alloc(tmp_len);
1076   silc_buffer_pull_tail(client_id_list, tmp_len);
1077   silc_buffer_put(client_id_list, tmp, tmp_len);
1078
1079   /* Get client mode list */
1080   tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
1081   if (!tmp)
1082     goto out;
1083
1084   client_mode_list = silc_buffer_alloc(tmp_len);
1085   silc_buffer_pull_tail(client_mode_list, tmp_len);
1086   silc_buffer_put(client_mode_list, tmp, tmp_len);
1087
1088   /* Save the users to the channel */
1089   silc_server_save_users_on_channel(server, cmd->sock, channel, NULL,
1090                                     client_id_list, client_mode_list, 
1091                                     list_count);
1092
1093   channel->global_users = silc_server_channel_has_global(channel);
1094   channel->users_resolved = TRUE;
1095
1096   silc_buffer_free(client_id_list);
1097   silc_buffer_free(client_mode_list);
1098
1099  out:
1100   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
1101   silc_free(channel_id);
1102  err:
1103   silc_server_command_reply_free(cmd);
1104 }
1105
1106 SILC_SERVER_CMD_REPLY_FUNC(getkey)
1107 {
1108   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
1109   SilcServer server = cmd->server;
1110   SilcStatus status, error;
1111   SilcClientEntry client = NULL;
1112   SilcServerEntry server_entry = NULL;
1113   SilcClientID *client_id = NULL;
1114   SilcServerID *server_id = NULL;
1115   SilcSKEPKType type;
1116   unsigned char *tmp, *pk;
1117   SilcUInt32 len;
1118   SilcUInt16 pk_len;
1119   SilcIDPayload idp = NULL;
1120   SilcIdType id_type;
1121   SilcPublicKey public_key = NULL;
1122
1123   COMMAND_CHECK_STATUS;
1124
1125   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1126   if (!tmp)
1127     goto out;
1128   idp = silc_id_payload_parse(tmp, len);
1129   if (!idp)
1130     goto out;
1131
1132   /* Get the public key payload */
1133   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
1134   if (!tmp)
1135     goto out;
1136
1137   /* Decode the public key */
1138
1139   SILC_GET16_MSB(pk_len, tmp);
1140   SILC_GET16_MSB(type, tmp + 2);
1141   pk = tmp + 4;
1142
1143   if (type != SILC_SKE_PK_TYPE_SILC)
1144     goto out;
1145
1146   if (!silc_pkcs_public_key_decode(pk, pk_len, &public_key))
1147     goto out;
1148
1149   id_type = silc_id_payload_get_type(idp);
1150   if (id_type == SILC_ID_CLIENT) {
1151     client_id = silc_id_payload_get_id(idp);
1152
1153     client = silc_idlist_find_client_by_id(server->local_list, client_id,
1154                                            TRUE, NULL);
1155     if (!client) {
1156       client = silc_idlist_find_client_by_id(server->global_list, 
1157                                              client_id, TRUE, NULL);
1158       if (!client)
1159         goto out;
1160     }
1161
1162     client->data.public_key = public_key;
1163     public_key = NULL;
1164   } else if (id_type == SILC_ID_SERVER) {
1165     server_id = silc_id_payload_get_id(idp);
1166
1167     server_entry = silc_idlist_find_server_by_id(server->local_list, server_id,
1168                                                  TRUE, NULL);
1169     if (!server_entry) {
1170       server_entry = silc_idlist_find_server_by_id(server->global_list, 
1171                                                    server_id, TRUE, NULL);
1172       if (!server_entry)
1173         goto out;
1174     }
1175
1176     server_entry->data.public_key = public_key;
1177     public_key = NULL;
1178   } else {
1179     goto out;
1180   }
1181
1182  out:
1183   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_GETKEY);
1184   if (idp)
1185     silc_id_payload_free(idp);
1186   silc_free(client_id);
1187   silc_free(server_id);
1188   if (public_key)
1189     silc_pkcs_public_key_free(public_key);
1190  err:
1191   silc_server_command_reply_free(cmd);
1192 }
1193
1194 SILC_SERVER_CMD_REPLY_FUNC(list)
1195 {
1196   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
1197   SilcServer server = cmd->server;
1198   SilcStatus status, error;
1199   SilcChannelID *channel_id = NULL;
1200   SilcChannelEntry channel;
1201   SilcIDCacheEntry cache;
1202   SilcUInt32 len;
1203   unsigned char *tmp, *name, *topic;
1204   SilcUInt32 usercount = 0;
1205   bool global_list = FALSE;
1206
1207   COMMAND_CHECK_STATUS;
1208
1209   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1210   channel_id = silc_id_payload_parse_id(tmp, len, NULL);
1211   if (!channel_id)
1212     goto out;
1213
1214   name = silc_argument_get_arg_type(cmd->args, 3, NULL);
1215   topic = silc_argument_get_arg_type(cmd->args, 4, NULL);
1216   tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
1217   if (tmp)
1218     SILC_GET32_MSB(usercount, tmp);
1219
1220   /* Add the channel entry if we do not have it already */
1221   channel = silc_idlist_find_channel_by_name(server->local_list, 
1222                                              name, &cache);
1223   if (!channel) {
1224     channel = silc_idlist_find_channel_by_name(server->global_list, 
1225                                                name, &cache);
1226     global_list = TRUE;
1227   }
1228   if (!channel) {
1229     /* If router did not find such channel in its lists then this must
1230        be bogus channel or some router in the net is buggy. */
1231     if (server->server_type != SILC_SERVER)
1232       goto out;
1233     
1234     channel = silc_idlist_add_channel(server->global_list, strdup(name),
1235                                       SILC_CHANNEL_MODE_NONE, channel_id, 
1236                                       server->router, NULL, NULL, 
1237                                       time(NULL) + 60);
1238     if (!channel)
1239       goto out;
1240     channel_id = NULL;
1241   } else {
1242     /* Found, update expiry */
1243     if (global_list && server->server_type == SILC_SERVER)
1244       cache->expire = time(NULL) + 60;
1245   }
1246
1247   channel->user_count = usercount;
1248
1249   if (topic) {
1250     silc_free(channel->topic);
1251     channel->topic = strdup(topic);
1252   }
1253
1254   /* Pending callbacks are not executed if this was an list entry */
1255   if (status != SILC_STATUS_OK &&
1256       status != SILC_STATUS_LIST_END) {
1257     silc_server_command_reply_free(cmd);
1258     return;
1259   }
1260
1261   /* Now purge all old entries from the global list, otherwise we'll might
1262      have non-existent entries for long periods of time in the cache. */
1263   silc_idcache_purge(server->global_list->channels);
1264
1265  out:
1266   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_LIST);
1267   silc_free(channel_id);
1268  err:
1269   silc_server_command_reply_free(cmd);
1270 }
1271
1272 SILC_SERVER_CMD_REPLY_FUNC(watch)
1273 {
1274   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
1275   SilcStatus status, error;
1276
1277   COMMAND_CHECK_STATUS;
1278
1279  out:
1280   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_WATCH);
1281  err:
1282   silc_server_command_reply_free(cmd);
1283 }