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       silc_free(entry->founder_passwd);
903       entry->founder_passwd = NULL;
904     }
905   }
906
907   if (entry->hmac_name && hmac) {
908     silc_free(entry->hmac_name);
909     entry->hmac_name = strdup(silc_hmac_get_name(hmac));
910   }
911
912   /* Get the ban list */
913   tmp = silc_argument_get_arg_type(cmd->args, 8, &len);
914   if (tmp) {
915     silc_free(entry->ban_list);
916     entry->ban_list = silc_memdup(tmp, len);
917   }
918
919   /* Get the invite list */
920   tmp = silc_argument_get_arg_type(cmd->args, 9, &len);
921   if (tmp) {
922     silc_free(entry->invite_list);
923     entry->invite_list = silc_memdup(tmp, len);
924   }
925
926   /* Get the topic */
927   tmp = silc_argument_get_arg_type(cmd->args, 10, &len);
928   if (tmp) {
929     silc_free(entry->topic);
930     entry->topic = strdup(tmp);
931   }
932
933   /* If channel was not created we know there is global users on the 
934      channel. */
935   entry->global_users = (created == 0 ? TRUE : FALSE);
936
937   /* If channel was just created the mask must be zero */
938   if (!entry->global_users && mode) {
939     SILC_LOG_DEBUG(("Buggy router `%s' sent non-zero mode mask for "
940                     "new channel, forcing it to zero", cmd->sock->hostname));
941     mode = 0;
942   }
943
944   /* Save channel mode */
945   entry->mode = mode;
946
947   /* Save channel key */
948   if (keyp) {
949     if (!(entry->mode & SILC_CHANNEL_MODE_PRIVKEY))
950       silc_server_save_channel_key(server, keyp, entry);
951     silc_buffer_free(keyp);
952   }
953
954   /* Save the users to the channel */
955   silc_server_save_users_on_channel(server, cmd->sock, entry, 
956                                     client_id, client_id_list,
957                                     client_mode_list, list_count);
958   entry->users_resolved = TRUE;
959
960  out:
961   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_JOIN);
962  err:
963   silc_free(client_id);
964   silc_server_command_reply_free(cmd);
965
966   if (client_id_list)
967     silc_buffer_free(client_id_list);
968   if (client_mode_list)
969     silc_buffer_free(client_mode_list);
970 }
971
972 /* Received reply to STATS command.  */
973
974 SILC_SERVER_CMD_REPLY_FUNC(stats)
975 {
976   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
977   SilcServer server = cmd->server;
978   SilcStatus status, error;
979   unsigned char *tmp;
980   SilcUInt32 tmp_len;
981   SilcBufferStruct buf;
982
983   COMMAND_CHECK_STATUS;
984
985   /* Get statistics structure */
986   tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
987   if (server->server_type == SILC_SERVER && tmp) {
988     silc_buffer_set(&buf, tmp, tmp_len);
989     silc_buffer_unformat(&buf,
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(NULL),
996                          SILC_STR_UI_INT(&server->stat.cell_clients),
997                          SILC_STR_UI_INT(&server->stat.cell_channels),
998                          SILC_STR_UI_INT(&server->stat.cell_servers),
999                          SILC_STR_UI_INT(&server->stat.clients),
1000                          SILC_STR_UI_INT(&server->stat.channels),
1001                          SILC_STR_UI_INT(&server->stat.servers),
1002                          SILC_STR_UI_INT(&server->stat.routers),
1003                          SILC_STR_UI_INT(&server->stat.server_ops),
1004                          SILC_STR_UI_INT(&server->stat.router_ops),
1005                          SILC_STR_END);
1006   }
1007
1008  out:
1009   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_STATS);
1010  err:
1011   silc_server_command_reply_free(cmd);
1012 }
1013
1014 SILC_SERVER_CMD_REPLY_FUNC(users)
1015 {
1016   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
1017   SilcServer server = cmd->server;
1018   SilcStatus status, error;
1019   SilcChannelEntry channel;
1020   SilcChannelID *channel_id = NULL;
1021   SilcBuffer client_id_list;
1022   SilcBuffer client_mode_list;
1023   unsigned char *tmp;
1024   SilcUInt32 tmp_len;
1025   SilcUInt32 list_count;
1026
1027   COMMAND_CHECK_STATUS;
1028
1029   /* Get channel ID */
1030   tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
1031   if (!tmp)
1032     goto out;
1033   channel_id = silc_id_payload_parse_id(tmp, tmp_len, NULL);
1034   if (!channel_id)
1035     goto out;
1036
1037   /* Get channel entry */
1038   channel = silc_idlist_find_channel_by_id(server->local_list, 
1039                                            channel_id, NULL);
1040   if (!channel) {
1041     channel = silc_idlist_find_channel_by_id(server->global_list, 
1042                                              channel_id, NULL);
1043     if (!channel) {
1044       SilcBuffer idp;
1045
1046       if (server->server_type != SILC_SERVER)
1047         goto out;
1048
1049       idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
1050       silc_server_send_command(server, server->router->connection,
1051                                SILC_COMMAND_IDENTIFY, ++server->cmd_ident,
1052                                1, 5, idp->data, idp->len);
1053       silc_buffer_free(idp);
1054
1055       /* Register pending command callback. After we've received the channel
1056          information we will reprocess this command reply by re-calling this
1057          USERS command reply callback. */
1058       silc_server_command_pending(server, SILC_COMMAND_IDENTIFY, 
1059                                   server->cmd_ident,
1060                                   silc_server_command_reply_users, cmd);
1061       return;
1062     }
1063   }
1064
1065   /* Get the list count */
1066   tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
1067   if (!tmp)
1068     goto out;
1069   SILC_GET32_MSB(list_count, tmp);
1070
1071   /* Get Client ID list */
1072   tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
1073   if (!tmp)
1074     goto out;
1075
1076   client_id_list = silc_buffer_alloc(tmp_len);
1077   silc_buffer_pull_tail(client_id_list, tmp_len);
1078   silc_buffer_put(client_id_list, tmp, tmp_len);
1079
1080   /* Get client mode list */
1081   tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
1082   if (!tmp)
1083     goto out;
1084
1085   client_mode_list = silc_buffer_alloc(tmp_len);
1086   silc_buffer_pull_tail(client_mode_list, tmp_len);
1087   silc_buffer_put(client_mode_list, tmp, tmp_len);
1088
1089   /* Save the users to the channel */
1090   silc_server_save_users_on_channel(server, cmd->sock, channel, NULL,
1091                                     client_id_list, client_mode_list, 
1092                                     list_count);
1093
1094   channel->global_users = silc_server_channel_has_global(channel);
1095   channel->users_resolved = TRUE;
1096
1097   silc_buffer_free(client_id_list);
1098   silc_buffer_free(client_mode_list);
1099
1100  out:
1101   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
1102   silc_free(channel_id);
1103  err:
1104   silc_server_command_reply_free(cmd);
1105 }
1106
1107 SILC_SERVER_CMD_REPLY_FUNC(getkey)
1108 {
1109   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
1110   SilcServer server = cmd->server;
1111   SilcStatus status, error;
1112   SilcClientEntry client = NULL;
1113   SilcServerEntry server_entry = NULL;
1114   SilcClientID *client_id = NULL;
1115   SilcServerID *server_id = NULL;
1116   SilcSKEPKType type;
1117   unsigned char *tmp, *pk;
1118   SilcUInt32 len;
1119   SilcUInt16 pk_len;
1120   SilcIDPayload idp = NULL;
1121   SilcIdType id_type;
1122   SilcPublicKey public_key = NULL;
1123
1124   COMMAND_CHECK_STATUS;
1125
1126   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1127   if (!tmp)
1128     goto out;
1129   idp = silc_id_payload_parse(tmp, len);
1130   if (!idp)
1131     goto out;
1132
1133   /* Get the public key payload */
1134   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
1135   if (!tmp)
1136     goto out;
1137
1138   /* Decode the public key */
1139
1140   SILC_GET16_MSB(pk_len, tmp);
1141   SILC_GET16_MSB(type, tmp + 2);
1142   pk = tmp + 4;
1143
1144   if (type != SILC_SKE_PK_TYPE_SILC)
1145     goto out;
1146
1147   if (!silc_pkcs_public_key_decode(pk, pk_len, &public_key))
1148     goto out;
1149
1150   id_type = silc_id_payload_get_type(idp);
1151   if (id_type == SILC_ID_CLIENT) {
1152     client_id = silc_id_payload_get_id(idp);
1153
1154     client = silc_idlist_find_client_by_id(server->local_list, client_id,
1155                                            TRUE, NULL);
1156     if (!client) {
1157       client = silc_idlist_find_client_by_id(server->global_list, 
1158                                              client_id, TRUE, NULL);
1159       if (!client)
1160         goto out;
1161     }
1162
1163     client->data.public_key = public_key;
1164     public_key = NULL;
1165   } else if (id_type == SILC_ID_SERVER) {
1166     server_id = silc_id_payload_get_id(idp);
1167
1168     server_entry = silc_idlist_find_server_by_id(server->local_list, server_id,
1169                                                  TRUE, NULL);
1170     if (!server_entry) {
1171       server_entry = silc_idlist_find_server_by_id(server->global_list, 
1172                                                    server_id, TRUE, NULL);
1173       if (!server_entry)
1174         goto out;
1175     }
1176
1177     server_entry->data.public_key = public_key;
1178     public_key = NULL;
1179   } else {
1180     goto out;
1181   }
1182
1183  out:
1184   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_GETKEY);
1185   if (idp)
1186     silc_id_payload_free(idp);
1187   silc_free(client_id);
1188   silc_free(server_id);
1189   if (public_key)
1190     silc_pkcs_public_key_free(public_key);
1191  err:
1192   silc_server_command_reply_free(cmd);
1193 }
1194
1195 SILC_SERVER_CMD_REPLY_FUNC(list)
1196 {
1197   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
1198   SilcServer server = cmd->server;
1199   SilcStatus status, error;
1200   SilcChannelID *channel_id = NULL;
1201   SilcChannelEntry channel;
1202   SilcIDCacheEntry cache;
1203   SilcUInt32 len;
1204   unsigned char *tmp, *name, *topic;
1205   SilcUInt32 usercount = 0;
1206   bool global_list = FALSE;
1207
1208   COMMAND_CHECK_STATUS;
1209
1210   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1211   channel_id = silc_id_payload_parse_id(tmp, len, NULL);
1212   if (!channel_id)
1213     goto out;
1214
1215   name = silc_argument_get_arg_type(cmd->args, 3, NULL);
1216   topic = silc_argument_get_arg_type(cmd->args, 4, NULL);
1217   tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
1218   if (tmp)
1219     SILC_GET32_MSB(usercount, tmp);
1220
1221   /* Add the channel entry if we do not have it already */
1222   channel = silc_idlist_find_channel_by_name(server->local_list, 
1223                                              name, &cache);
1224   if (!channel) {
1225     channel = silc_idlist_find_channel_by_name(server->global_list, 
1226                                                name, &cache);
1227     global_list = TRUE;
1228   }
1229   if (!channel) {
1230     /* If router did not find such channel in its lists then this must
1231        be bogus channel or some router in the net is buggy. */
1232     if (server->server_type != SILC_SERVER)
1233       goto out;
1234     
1235     channel = silc_idlist_add_channel(server->global_list, strdup(name),
1236                                       SILC_CHANNEL_MODE_NONE, channel_id, 
1237                                       server->router, NULL, NULL, 
1238                                       time(NULL) + 60);
1239     if (!channel)
1240       goto out;
1241     channel_id = NULL;
1242   } else {
1243     /* Found, update expiry */
1244     if (global_list && server->server_type == SILC_SERVER)
1245       cache->expire = time(NULL) + 60;
1246   }
1247
1248   channel->user_count = usercount;
1249
1250   if (topic) {
1251     silc_free(channel->topic);
1252     channel->topic = strdup(topic);
1253   }
1254
1255   /* Pending callbacks are not executed if this was an list entry */
1256   if (status != SILC_STATUS_OK &&
1257       status != SILC_STATUS_LIST_END) {
1258     silc_server_command_reply_free(cmd);
1259     return;
1260   }
1261
1262   /* Now purge all old entries from the global list, otherwise we'll might
1263      have non-existent entries for long periods of time in the cache. */
1264   silc_idcache_purge(server->global_list->channels);
1265
1266  out:
1267   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_LIST);
1268   silc_free(channel_id);
1269  err:
1270   silc_server_command_reply_free(cmd);
1271 }
1272
1273 SILC_SERVER_CMD_REPLY_FUNC(watch)
1274 {
1275   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
1276   SilcStatus status, error;
1277
1278   COMMAND_CHECK_STATUS;
1279
1280  out:
1281   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_WATCH);
1282  err:
1283   silc_server_command_reply_free(cmd);
1284 }