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 (keyp) {
947     if (!(entry->mode & SILC_CHANNEL_MODE_PRIVKEY))
948       silc_server_save_channel_key(server, keyp, entry);
949     silc_buffer_free(keyp);
950   }
951
952   /* Save the users to the channel */
953   silc_server_save_users_on_channel(server, cmd->sock, entry, 
954                                     client_id, client_id_list,
955                                     client_mode_list, list_count);
956
957  out:
958   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_JOIN);
959   silc_free(client_id);
960   silc_server_command_reply_free(cmd);
961
962   if (client_id_list)
963     silc_buffer_free(client_id_list);
964   if (client_mode_list)
965     silc_buffer_free(client_mode_list);
966 }
967
968 SILC_SERVER_CMD_REPLY_FUNC(users)
969 {
970   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
971   SilcServer server = cmd->server;
972   SilcCommandStatus status;
973   SilcChannelEntry channel;
974   SilcChannelID *channel_id = NULL;
975   SilcBuffer client_id_list;
976   SilcBuffer client_mode_list;
977   unsigned char *tmp;
978   uint32 tmp_len;
979   uint32 list_count;
980
981   COMMAND_CHECK_STATUS;
982
983   /* Get channel ID */
984   tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
985   if (!tmp)
986     goto out;
987   channel_id = silc_id_payload_parse_id(tmp, tmp_len);
988   if (!channel_id)
989     goto out;
990
991   /* Get channel entry */
992   channel = silc_idlist_find_channel_by_id(server->local_list, 
993                                            channel_id, NULL);
994   if (!channel) {
995     channel = silc_idlist_find_channel_by_id(server->global_list, 
996                                              channel_id, NULL);
997     if (!channel) {
998       SilcBuffer idp;
999
1000       if (server->server_type != SILC_SERVER)
1001         goto out;
1002
1003       idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
1004       silc_server_send_command(server, server->router->connection,
1005                                SILC_COMMAND_IDENTIFY, ++server->cmd_ident,
1006                                1, 5, idp->data, idp->len);
1007       silc_buffer_free(idp);
1008
1009       /* Register pending command callback. After we've received the channel
1010          information we will reprocess this command reply by re-calling this
1011          USERS command reply callback. */
1012       silc_server_command_pending(server, SILC_COMMAND_IDENTIFY, 
1013                                   server->cmd_ident,
1014                                   silc_server_command_reply_users, cmd);
1015       return;
1016     }
1017   }
1018
1019   /* Get the list count */
1020   tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
1021   if (!tmp)
1022     goto out;
1023   SILC_GET32_MSB(list_count, tmp);
1024
1025   /* Get Client ID list */
1026   tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
1027   if (!tmp)
1028     goto out;
1029
1030   client_id_list = silc_buffer_alloc(tmp_len);
1031   silc_buffer_pull_tail(client_id_list, tmp_len);
1032   silc_buffer_put(client_id_list, tmp, tmp_len);
1033
1034   /* Get client mode list */
1035   tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
1036   if (!tmp)
1037     goto out;
1038
1039   client_mode_list = silc_buffer_alloc(tmp_len);
1040   silc_buffer_pull_tail(client_mode_list, tmp_len);
1041   silc_buffer_put(client_mode_list, tmp, tmp_len);
1042
1043   /* Save the users to the channel */
1044   silc_server_save_users_on_channel(server, cmd->sock, channel, NULL,
1045                                     client_id_list, client_mode_list, 
1046                                     list_count);
1047
1048   silc_buffer_free(client_id_list);
1049   silc_buffer_free(client_mode_list);
1050
1051  out:
1052   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
1053   silc_free(channel_id);
1054   silc_server_command_reply_free(cmd);
1055 }
1056
1057 SILC_SERVER_CMD_REPLY_FUNC(getkey)
1058 {
1059   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
1060   SilcServer server = cmd->server;
1061   SilcCommandStatus status;
1062   SilcClientEntry client = NULL;
1063   SilcServerEntry server_entry = NULL;
1064   SilcClientID *client_id = NULL;
1065   SilcServerID *server_id = NULL;
1066   SilcSKEPKType type;
1067   unsigned char *tmp, *pk;
1068   uint32 len;
1069   uint16 pk_len;
1070   SilcIDPayload idp = NULL;
1071   SilcIdType id_type;
1072   SilcPublicKey public_key = NULL;
1073
1074   COMMAND_CHECK_STATUS;
1075
1076   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1077   if (!tmp)
1078     goto out;
1079   idp = silc_id_payload_parse(tmp, len);
1080   if (!idp)
1081     goto out;
1082
1083   /* Get the public key payload */
1084   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
1085   if (!tmp)
1086     goto out;
1087
1088   /* Decode the public key */
1089
1090   SILC_GET16_MSB(pk_len, tmp);
1091   SILC_GET16_MSB(type, tmp + 2);
1092   pk = tmp + 4;
1093
1094   if (type != SILC_SKE_PK_TYPE_SILC)
1095     goto out;
1096
1097   if (!silc_pkcs_public_key_decode(pk, pk_len, &public_key))
1098     goto out;
1099
1100   id_type = silc_id_payload_get_type(idp);
1101   if (id_type == SILC_ID_CLIENT) {
1102     client_id = silc_id_payload_get_id(idp);
1103
1104     client = silc_idlist_find_client_by_id(server->local_list, client_id,
1105                                            TRUE, NULL);
1106     if (!client) {
1107       client = silc_idlist_find_client_by_id(server->global_list, 
1108                                              client_id, TRUE, NULL);
1109       if (!client)
1110         goto out;
1111     }
1112
1113     client->data.public_key = public_key;
1114   } else if (id_type == SILC_ID_SERVER) {
1115     server_id = silc_id_payload_get_id(idp);
1116
1117     server_entry = silc_idlist_find_server_by_id(server->local_list, server_id,
1118                                                  TRUE, NULL);
1119     if (!server_entry) {
1120       server_entry = silc_idlist_find_server_by_id(server->global_list, 
1121                                                    server_id, TRUE, NULL);
1122       if (!server_entry)
1123         goto out;
1124     }
1125
1126     server_entry->data.public_key = public_key;
1127   } else {
1128     goto out;
1129   }
1130
1131  out:
1132   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_GETKEY);
1133   if (idp)
1134     silc_id_payload_free(idp);
1135   silc_free(client_id);
1136   silc_free(server_id);
1137   if (public_key)
1138     silc_pkcs_public_key_free(public_key);
1139   silc_server_command_reply_free(cmd);
1140 }
1141
1142 SILC_SERVER_CMD_REPLY_FUNC(list)
1143 {
1144   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
1145   SilcServer server = cmd->server;
1146   SilcCommandStatus status;
1147   SilcChannelID *channel_id = NULL;
1148   SilcChannelEntry channel;
1149   SilcIDCacheEntry cache;
1150   uint32 len;
1151   unsigned char *tmp, *name, *topic;
1152   uint32 usercount = 0;
1153   bool global_list = FALSE;
1154
1155   COMMAND_CHECK_STATUS_LIST;
1156
1157   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1158   channel_id = silc_id_payload_parse_id(tmp, len);
1159   if (!channel_id)
1160     goto out;
1161
1162   name = silc_argument_get_arg_type(cmd->args, 3, NULL);
1163   topic = silc_argument_get_arg_type(cmd->args, 4, NULL);
1164   tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
1165   if (tmp)
1166     SILC_GET32_MSB(usercount, tmp);
1167
1168   /* Add the channel entry if we do not have it already */
1169   channel = silc_idlist_find_channel_by_name(server->local_list, 
1170                                              name, &cache);
1171   if (!channel) {
1172     channel = silc_idlist_find_channel_by_name(server->global_list, 
1173                                                name, &cache);
1174     global_list = TRUE;
1175   }
1176   if (!channel) {
1177     /* If router did not find such channel in its lists then this must
1178        be bogus channel or some router in the net is buggy. */
1179     if (server->server_type != SILC_SERVER)
1180       goto out;
1181     
1182     channel = silc_idlist_add_channel(server->global_list, strdup(name),
1183                                       SILC_CHANNEL_MODE_NONE, channel_id, 
1184                                       server->router, NULL, NULL, 
1185                                       time(NULL) + 60);
1186     if (!channel)
1187       goto out;
1188     channel_id = NULL;
1189   } else {
1190     /* Found, update expiry */
1191     if (global_list && server->server_type == SILC_SERVER)
1192       cache->expire = time(NULL) + 60;
1193   }
1194
1195   if (topic) {
1196     silc_free(channel->topic);
1197     channel->topic = strdup(topic);
1198   }
1199
1200   /* Pending callbacks are not executed if this was an list entry */
1201   if (status != SILC_STATUS_OK &&
1202       status != SILC_STATUS_LIST_END) {
1203     silc_server_command_reply_free(cmd);
1204     return;
1205   }
1206
1207   /* Now purge all old entries from the global list, otherwise we'll might
1208      have non-existent entries for long periods of time in the cache. */
1209   silc_idcache_purge(server->global_list->channels);
1210
1211  out:
1212   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_LIST);
1213   silc_free(channel_id);
1214   silc_server_command_reply_free(cmd);
1215 }