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