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