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