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