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
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(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     if (name) {
534       channel = silc_idlist_find_channel_by_name(server->local_list, 
535                                                  name, NULL);
536       if (!channel)
537         channel = silc_idlist_find_channel_by_name(server->global_list, 
538                                                    name, NULL);
539     } else {
540       channel = silc_idlist_find_channel_by_id(server->local_list, 
541                                                channel_id, NULL);
542       if (!channel)
543         channel = silc_idlist_find_channel_by_id(server->global_list, 
544                                                  channel_id, NULL);
545     }
546     if (!channel) {
547       /* If router did not find such Channel ID in its lists then this must
548          be bogus channel or some router in the net is buggy. */
549       if (server->server_type != SILC_SERVER)
550         goto error;
551       
552       /* We don't have that server anywhere, add it. */
553       channel = silc_idlist_add_channel(server->global_list, strdup(name),
554                                         SILC_CHANNEL_MODE_NONE, channel_id, 
555                                         server->router, 
556                                         NULL, NULL);
557       if (!channel) {
558         silc_free(channel_id);
559         goto error;
560       }
561       channel_id = NULL;
562     }
563
564     silc_free(channel_id);
565     break;
566   }
567
568   silc_id_payload_free(idp);
569   return TRUE;
570
571  error:
572   silc_id_payload_free(idp);
573   return FALSE;
574 }
575
576 /* Received reply for forwarded IDENTIFY command. We have received the
577    requested identify information now and we will cache it. After this we
578    will call the pending command so that the requestee gets the information
579    after all. */
580
581 SILC_SERVER_CMD_REPLY_FUNC(identify)
582 {
583   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
584   SilcCommandStatus status;
585
586   COMMAND_CHECK_STATUS_LIST;
587
588   if (!silc_server_command_reply_identify_save(cmd))
589     goto out;
590
591   /* Pending callbacks are not executed if this was an list entry */
592   if (status != SILC_STATUS_OK &&
593       status != SILC_STATUS_LIST_END) {
594     silc_server_command_reply_free(cmd);
595     return;
596   }
597
598  out:
599   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_IDENTIFY);
600   SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_IDENTIFY);
601   silc_server_command_reply_free(cmd);
602 }
603
604 /* Received reply fro INFO command. Cache the server and its information */
605
606 SILC_SERVER_CMD_REPLY_FUNC(info)
607 {
608   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
609   SilcServer server = cmd->server;
610   SilcCommandStatus status;
611   SilcServerEntry entry;
612   SilcServerID *server_id;
613   uint32 tmp_len;
614   unsigned char *tmp, *name;
615
616   COMMAND_CHECK_STATUS;
617
618   /* Get Server ID */
619   tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
620   if (!tmp)
621     goto out;
622   server_id = silc_id_payload_parse_id(tmp, tmp_len);
623   if (!server_id)
624     goto out;
625
626   /* Get the name */
627   name = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
628   if (tmp_len > 256)
629     goto out;
630
631   entry = silc_idlist_find_server_by_id(server->local_list, server_id, 
632                                         FALSE, NULL);
633   if (!entry) {
634     entry = silc_idlist_find_server_by_id(server->global_list, server_id, 
635                                           FALSE, NULL);
636     if (!entry) {
637       /* Add the server to global list */
638       server_id = silc_id_dup(server_id, SILC_ID_SERVER);
639       entry = silc_idlist_add_server(server->global_list, name, 0,
640                                      server_id, NULL, NULL);
641       if (!entry) {
642         silc_free(server_id);
643         goto out;
644       }
645       entry->data.status |= SILC_IDLIST_STATUS_REGISTERED;
646     }
647   }
648
649   /* Get the info string */
650   tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
651   if (tmp_len > 256)
652     tmp = NULL;
653
654   entry->server_info = tmp ? strdup(tmp) : NULL;
655
656  out:
657   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_INFO);
658   SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INFO);
659   silc_server_command_reply_free(cmd);
660 }
661
662 /* Received reply fro MOTD command. */
663
664 SILC_SERVER_CMD_REPLY_FUNC(motd)
665 {
666   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
667   SilcServer server = cmd->server;
668   SilcCommandStatus status;
669   SilcServerEntry entry = NULL;
670   SilcServerID *server_id;
671   uint32 tmp_len;
672   unsigned char *tmp;
673
674   COMMAND_CHECK_STATUS;
675
676   /* Get Server ID */
677   tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
678   if (!tmp)
679     goto out;
680   server_id = silc_id_payload_parse_id(tmp, tmp_len);
681   if (!server_id)
682     goto out;
683
684   entry = silc_idlist_find_server_by_id(server->local_list, server_id, 
685                                         TRUE, NULL);
686   if (!entry) {
687     entry = silc_idlist_find_server_by_id(server->global_list, server_id, 
688                                           TRUE, NULL);
689     if (!entry)
690       goto out;
691   }
692
693   /* Get the motd */
694   tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
695   if (tmp_len > 256)
696     tmp = NULL;
697
698   entry->motd = tmp;
699
700  out:
701   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_MOTD);
702   SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_MOTD);
703   silc_server_command_reply_free(cmd);
704
705   if (entry)
706     entry->motd = NULL;
707 }
708
709 /* Received reply for forwarded JOIN command. Router has created or joined
710    the client to the channel. We save some channel information locally
711    for future use. */
712
713 SILC_SERVER_CMD_REPLY_FUNC(join)
714 {
715   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
716   SilcServer server = cmd->server;
717   SilcIDCacheEntry cache = NULL;
718   SilcCommandStatus status;
719   SilcChannelID *id;
720   SilcClientID *client_id = NULL;
721   SilcChannelEntry entry;
722   SilcHmac hmac = NULL;
723   uint32 id_len, len, list_count;
724   unsigned char *id_string;
725   char *channel_name, *tmp;
726   uint32 mode, created;
727   SilcBuffer keyp = NULL, client_id_list = NULL, client_mode_list = NULL;
728
729   COMMAND_CHECK_STATUS;
730
731   /* Get channel name */
732   channel_name = silc_argument_get_arg_type(cmd->args, 2, NULL);
733   if (!channel_name)
734     goto out;
735
736   /* Get channel ID */
737   id_string = silc_argument_get_arg_type(cmd->args, 3, &id_len);
738   if (!id_string)
739     goto out;
740
741   /* Get client ID */
742   tmp = silc_argument_get_arg_type(cmd->args, 4, &len);
743   if (!tmp)
744     goto out;
745   client_id = silc_id_payload_parse_id(tmp, len);
746   if (!client_id)
747     goto out;
748
749   /* Get mode mask */
750   tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
751   if (!tmp)
752     goto out;
753   SILC_GET32_MSB(mode, tmp);
754
755   /* Get created boolean value */
756   tmp = silc_argument_get_arg_type(cmd->args, 6, NULL);
757   if (!tmp)
758     goto out;
759   SILC_GET32_MSB(created, tmp);
760   if (created != 0 && created != 1)
761     goto out;
762
763   /* Get channel key */
764   tmp = silc_argument_get_arg_type(cmd->args, 7, &len);
765   if (tmp) {
766     keyp = silc_buffer_alloc(len);
767     silc_buffer_pull_tail(keyp, SILC_BUFFER_END(keyp));
768     silc_buffer_put(keyp, tmp, len);
769   }
770
771   id = silc_id_payload_parse_id(id_string, id_len);
772   if (!id)
773     goto out;
774
775   /* Get hmac */
776   tmp = silc_argument_get_arg_type(cmd->args, 11, NULL);
777   if (tmp) {
778     if (!silc_hmac_alloc(tmp, NULL, &hmac))
779       goto out;
780   }
781
782   /* Get the list count */
783   tmp = silc_argument_get_arg_type(cmd->args, 12, &len);
784   if (!tmp)
785     goto out;
786   SILC_GET32_MSB(list_count, tmp);
787
788   /* Get Client ID list */
789   tmp = silc_argument_get_arg_type(cmd->args, 13, &len);
790   if (!tmp)
791     goto out;
792
793   client_id_list = silc_buffer_alloc(len);
794   silc_buffer_pull_tail(client_id_list, len);
795   silc_buffer_put(client_id_list, tmp, len);
796
797   /* Get client mode list */
798   tmp = silc_argument_get_arg_type(cmd->args, 14, &len);
799   if (!tmp)
800     goto out;
801
802   client_mode_list = silc_buffer_alloc(len);
803   silc_buffer_pull_tail(client_mode_list, len);
804   silc_buffer_put(client_mode_list, tmp, len);
805
806   /* See whether we already have the channel. */
807   entry = silc_idlist_find_channel_by_name(server->local_list, 
808                                            channel_name, &cache);
809   if (!entry) {
810     /* Add new channel */
811
812     SILC_LOG_DEBUG(("Adding new [%s] channel %s id(%s)", 
813                     (created == 0 ? "existing" : "created"), channel_name,
814                     silc_id_render(id, SILC_ID_CHANNEL)));
815
816     /* If the channel is found from global list we must move it to the
817        local list. */
818     entry = silc_idlist_find_channel_by_name(server->global_list, 
819                                              channel_name, &cache);
820     if (entry)
821       silc_idlist_del_channel(server->global_list, entry);
822
823     /* Add the channel to our local list. */
824     entry = silc_idlist_add_channel(server->local_list, strdup(channel_name), 
825                                     SILC_CHANNEL_MODE_NONE, id, 
826                                     server->router, NULL, hmac);
827     if (!entry) {
828       silc_free(id);
829       goto out;
830     }
831     server->stat.my_channels++;
832   } else {
833     /* The entry exists. */
834     silc_free(cache->id);
835     entry->id = id;
836     cache->id = entry->id;
837     entry->disabled = FALSE;
838
839     /* Remove the founder auth data if the mode is not set but we have
840        them in the entry */
841     if (!(mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) && entry->founder_key) {
842       silc_pkcs_public_key_free(entry->founder_key);
843       if (entry->founder_passwd) {
844         silc_free(entry->founder_passwd);
845         entry->founder_passwd = NULL;
846       }
847     }
848   }
849
850   if (entry->hmac_name && hmac) {
851     silc_free(entry->hmac_name);
852     entry->hmac_name = strdup(silc_hmac_get_name(hmac));
853   }
854
855   /* Get the ban list */
856   tmp = silc_argument_get_arg_type(cmd->args, 8, &len);
857   if (tmp) {
858     if (entry->ban_list)
859       silc_free(entry->ban_list);
860     entry->ban_list = silc_calloc(len, sizeof(*entry->ban_list));
861     memcpy(entry->ban_list, tmp, len);
862   }
863
864   /* Get the invite list */
865   tmp = silc_argument_get_arg_type(cmd->args, 9, &len);
866   if (tmp) {
867     if (entry->invite_list)
868       silc_free(entry->invite_list);
869     entry->invite_list = silc_calloc(len, sizeof(*entry->invite_list));
870     memcpy(entry->invite_list, tmp, len);
871   }
872
873   /* Get the topic */
874   tmp = silc_argument_get_arg_type(cmd->args, 10, &len);
875   if (tmp) {
876     if (entry->topic)
877       silc_free(entry->topic);
878     entry->topic = strdup(tmp);
879   }
880
881   /* If channel was not created we know there is global users on the 
882      channel. */
883   entry->global_users = (created == 0 ? TRUE : FALSE);
884
885   /* If channel was just created the mask must be zero */
886   if (!entry->global_users && mode) {
887     SILC_LOG_DEBUG(("Buggy router `%s' sent non-zero mode mask for "
888                     "new channel, forcing it to zero", cmd->sock->hostname));
889     mode = 0;
890   }
891
892   /* Save channel mode */
893   entry->mode = mode;
894
895   /* Save channel key */
896   if (!(entry->mode & SILC_CHANNEL_MODE_PRIVKEY))
897     silc_server_save_channel_key(server, keyp, entry);
898   if (keyp)
899     silc_buffer_free(keyp);
900
901   /* Save the users to the channel */
902   silc_server_save_users_on_channel(server, cmd->sock, entry, 
903                                     client_id, client_id_list,
904                                     client_mode_list, list_count);
905
906  out:
907   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_JOIN);
908   SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_JOIN);
909   silc_free(client_id);
910   silc_server_command_reply_free(cmd);
911
912   if (client_id_list)
913     silc_buffer_free(client_id_list);
914   if (client_mode_list)
915     silc_buffer_free(client_mode_list);
916 }
917
918 SILC_SERVER_CMD_REPLY_FUNC(users)
919 {
920   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
921   SilcServer server = cmd->server;
922   SilcCommandStatus status;
923   SilcChannelEntry channel;
924   SilcChannelID *channel_id = NULL;
925   SilcBuffer client_id_list;
926   SilcBuffer client_mode_list;
927   unsigned char *tmp;
928   uint32 tmp_len;
929   uint32 list_count;
930
931   COMMAND_CHECK_STATUS;
932
933   /* Get channel ID */
934   tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
935   if (!tmp)
936     goto out;
937   channel_id = silc_id_payload_parse_id(tmp, tmp_len);
938   if (!channel_id)
939     goto out;
940
941   /* Get channel entry */
942   channel = silc_idlist_find_channel_by_id(server->local_list, 
943                                            channel_id, NULL);
944   if (!channel) {
945     channel = silc_idlist_find_channel_by_id(server->global_list, 
946                                              channel_id, NULL);
947     if (!channel) {
948       SilcBuffer idp;
949
950       if (server->server_type != SILC_SERVER)
951         goto out;
952
953       idp = silc_id_payload_encode(channel_id, SILC_ID_CHANNEL);
954       silc_server_send_command(server, server->router->connection,
955                                SILC_COMMAND_IDENTIFY, ++server->cmd_ident,
956                                1, 5, idp->data, idp->len);
957       silc_buffer_free(idp);
958
959       /* Register pending command callback. After we've received the channel
960          information we will reprocess this command reply by re-calling this
961          USERS command reply callback. */
962       silc_server_command_pending(server, SILC_COMMAND_IDENTIFY, 
963                                   server->cmd_ident,
964                                   NULL, silc_server_command_reply_users, cmd);
965       return;
966     }
967   }
968
969   /* Get the list count */
970   tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
971   if (!tmp)
972     goto out;
973   SILC_GET32_MSB(list_count, tmp);
974
975   /* Get Client ID list */
976   tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
977   if (!tmp)
978     goto out;
979
980   client_id_list = silc_buffer_alloc(tmp_len);
981   silc_buffer_pull_tail(client_id_list, tmp_len);
982   silc_buffer_put(client_id_list, tmp, tmp_len);
983
984   /* Get client mode list */
985   tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
986   if (!tmp)
987     goto out;
988
989   client_mode_list = silc_buffer_alloc(tmp_len);
990   silc_buffer_pull_tail(client_mode_list, tmp_len);
991   silc_buffer_put(client_mode_list, tmp, tmp_len);
992
993   /* Save the users to the channel */
994   silc_server_save_users_on_channel(server, cmd->sock, channel, NULL,
995                                     client_id_list, client_mode_list, 
996                                     list_count);
997
998   silc_buffer_free(client_id_list);
999   silc_buffer_free(client_mode_list);
1000
1001  out:
1002   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
1003   SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_USERS);
1004   silc_free(channel_id);
1005   silc_server_command_reply_free(cmd);
1006 }
1007
1008 SILC_SERVER_CMD_REPLY_FUNC(getkey)
1009 {
1010   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
1011   SilcServer server = cmd->server;
1012   SilcCommandStatus status;
1013   SilcClientEntry client = NULL;
1014   SilcServerEntry server_entry = NULL;
1015   SilcClientID *client_id = NULL;
1016   SilcServerID *server_id = NULL;
1017   SilcSKEPKType type;
1018   unsigned char *tmp, *pk;
1019   uint32 len;
1020   uint16 pk_len;
1021   SilcIDPayload idp = NULL;
1022   SilcIdType id_type;
1023   SilcPublicKey public_key = NULL;
1024
1025   COMMAND_CHECK_STATUS;
1026
1027   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1028   if (!tmp)
1029     goto out;
1030   idp = silc_id_payload_parse(tmp, len);
1031   if (!idp)
1032     goto out;
1033
1034   /* Get the public key payload */
1035   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
1036   if (!tmp)
1037     goto out;
1038
1039   /* Decode the public key */
1040
1041   SILC_GET16_MSB(pk_len, tmp);
1042   SILC_GET16_MSB(type, tmp + 2);
1043   pk = tmp + 4;
1044
1045   if (type != SILC_SKE_PK_TYPE_SILC)
1046     goto out;
1047
1048   if (!silc_pkcs_public_key_decode(pk, pk_len, &public_key))
1049     goto out;
1050
1051   id_type = silc_id_payload_get_type(idp);
1052   if (id_type == SILC_ID_CLIENT) {
1053     client_id = silc_id_payload_get_id(idp);
1054
1055     client = silc_idlist_find_client_by_id(server->local_list, client_id,
1056                                            TRUE, NULL);
1057     if (!client) {
1058       client = silc_idlist_find_client_by_id(server->global_list, 
1059                                              client_id, TRUE, NULL);
1060       if (!client)
1061         goto out;
1062     }
1063
1064     client->data.public_key = public_key;
1065   } else if (id_type == SILC_ID_SERVER) {
1066     server_id = silc_id_payload_get_id(idp);
1067
1068     server_entry = silc_idlist_find_server_by_id(server->local_list, server_id,
1069                                                  TRUE, NULL);
1070     if (!server_entry) {
1071       server_entry = silc_idlist_find_server_by_id(server->global_list, 
1072                                                    server_id, TRUE, NULL);
1073       if (!server_entry)
1074         goto out;
1075     }
1076
1077     server_entry->data.public_key = public_key;
1078   } else {
1079     goto out;
1080   }
1081
1082  out:
1083   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_GETKEY);
1084   SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_GETKEY);
1085   if (idp)
1086     silc_id_payload_free(idp);
1087   silc_free(client_id);
1088   silc_free(server_id);
1089   if (public_key)
1090     silc_pkcs_public_key_free(public_key);
1091   silc_server_command_reply_free(cmd);
1092 }
1093
1094 SILC_SERVER_CMD_REPLY_FUNC(list)
1095 {
1096   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
1097   SilcServer server = cmd->server;
1098   SilcCommandStatus status;
1099   SilcChannelID *channel_id = NULL;
1100   SilcChannelEntry channel;
1101   uint32 len;
1102   unsigned char *tmp, *name, *topic;
1103   uint32 usercount = 0;
1104
1105   COMMAND_CHECK_STATUS_LIST;
1106
1107   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1108   channel_id = silc_id_payload_parse_id(tmp, len);
1109   if (!channel_id)
1110     goto out;
1111
1112   name = silc_argument_get_arg_type(cmd->args, 3, NULL);
1113   topic = silc_argument_get_arg_type(cmd->args, 4, NULL);
1114   tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
1115   if (tmp)
1116     SILC_GET32_MSB(usercount, tmp);
1117
1118   /* Add the channel entry if we do not have it already */
1119   channel = silc_idlist_find_channel_by_name(server->local_list, 
1120                                              name, NULL);
1121   if (!channel)
1122     channel = silc_idlist_find_channel_by_name(server->global_list, 
1123                                                name, NULL);
1124   if (!channel) {
1125     /* If router did not find such Channel ID in its lists then this must
1126        be bogus channel or some router in the net is buggy. */
1127     if (server->server_type != SILC_SERVER)
1128       goto out;
1129     
1130     channel = silc_idlist_add_channel(server->global_list, strdup(name),
1131                                       SILC_CHANNEL_MODE_NONE, channel_id, 
1132                                       server->router, NULL, NULL);
1133     if (!channel)
1134       goto out;
1135     channel_id = NULL;
1136   }
1137
1138   if (topic) {
1139     silc_free(channel->topic);
1140     channel->topic = strdup(topic);
1141   }
1142
1143   /* Pending callbacks are not executed if this was an list entry */
1144   if (status != SILC_STATUS_OK &&
1145       status != SILC_STATUS_LIST_END) {
1146     silc_server_command_reply_free(cmd);
1147     return;
1148   }
1149
1150  out:
1151   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_LIST);
1152   SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_LIST);
1153   silc_free(channel_id);
1154   silc_server_command_reply_free(cmd);
1155 }