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