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