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