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 #define COMMAND_CHECK_STATUS                                              \
27 do {                                                                      \
28   SILC_LOG_DEBUG(("Start"));                                              \
29   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL)); \
30   if (status != SILC_STATUS_OK) {                                         \
31     silc_server_command_reply_free(cmd);                                  \
32     return;                                                               \
33   }                                                                       \
34 } while(0)
35
36 #define COMMAND_CHECK_STATUS_LIST                                         \
37 do {                                                                      \
38   SILC_LOG_DEBUG(("Start"));                                              \
39   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL)); \
40   if (status != SILC_STATUS_OK &&                                         \
41       status != SILC_STATUS_LIST_START &&                                 \
42       status != SILC_STATUS_LIST_ITEM &&                                  \
43       status != SILC_STATUS_LIST_END) {                                   \
44     silc_server_command_reply_free(cmd);                                  \
45     return;                                                               \
46   }                                                                       \
47 } while(0)
48
49 /* Server command reply list. Not all commands have reply function as
50    they are never sent by server. More maybe added later if need appears. */
51 SilcServerCommandReply silc_command_reply_list[] =
52 {
53   SILC_SERVER_CMD_REPLY(join, JOIN),
54   SILC_SERVER_CMD_REPLY(whois, WHOIS),
55   SILC_SERVER_CMD_REPLY(identify, IDENTIFY),
56   SILC_SERVER_CMD_REPLY(users, USERS),
57
58   { NULL, 0 },
59 };
60
61 /* Process received command reply. */
62
63 void silc_server_command_reply_process(SilcServer server,
64                                        SilcSocketConnection sock,
65                                        SilcBuffer buffer)
66 {
67   SilcServerCommandReply *cmd;
68   SilcServerCommandReplyContext ctx;
69   SilcCommandPayload payload;
70   SilcCommand command;
71   unsigned short ident;
72
73   SILC_LOG_DEBUG(("Start"));
74
75   /* Get command reply payload from packet */
76   payload = silc_command_payload_parse(buffer);
77   if (!payload) {
78     /* Silently ignore bad reply packet */
79     SILC_LOG_DEBUG(("Bad command reply packet"));
80     return;
81   }
82   
83   /* Allocate command reply context. This must be free'd by the
84      command reply routine receiving it. */
85   ctx = silc_calloc(1, sizeof(*ctx));
86   ctx->server = server;
87   ctx->sock = silc_socket_dup(sock);
88   ctx->payload = payload;
89   ctx->args = silc_command_get_args(ctx->payload);
90   ident = silc_command_get_ident(ctx->payload);
91       
92   /* Check for pending commands and mark to be exeucted */
93   silc_server_command_pending_check(server, ctx, 
94                                     silc_command_get(ctx->payload), ident);
95
96   /* Execute command reply */
97   command = silc_command_get(ctx->payload);
98   for (cmd = silc_command_reply_list; cmd->cb; cmd++)
99     if (cmd->cmd == command)
100       break;
101
102   if (cmd == NULL || !cmd->cb) {
103     silc_server_command_reply_free(ctx);
104     return;
105   }
106
107   cmd->cb(ctx);
108 }
109
110 /* Free command reply context and its internals. */
111
112 void silc_server_command_reply_free(SilcServerCommandReplyContext cmd)
113 {
114   if (cmd) {
115     silc_command_free_payload(cmd->payload);
116     if (cmd->sock)
117       silc_socket_free(cmd->sock); /* Decrease the reference counter */
118     silc_free(cmd);
119   }
120 }
121
122 /* Caches the received WHOIS information. If we are normal server currently
123    we cache global information only for short period of time.  */
124 /* XXX cache expirying not implemented yet! */
125
126 static char
127 silc_server_command_reply_whois_save(SilcServerCommandReplyContext cmd)
128 {
129   SilcServer server = cmd->server;
130   int len, id_len;
131   unsigned char *tmp, *id_data;
132   char *nickname, *username, *realname;
133   SilcClientID *client_id;
134   SilcClientEntry client;
135   SilcIDCacheEntry cache = NULL;
136   char global = FALSE;
137   char *nick;
138   unsigned int mode = 0;
139
140   id_data = silc_argument_get_arg_type(cmd->args, 2, &id_len);
141   nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
142   username = silc_argument_get_arg_type(cmd->args, 4, &len);
143   realname = silc_argument_get_arg_type(cmd->args, 5, &len);
144   if (!id_data || !nickname || !username || !realname) {
145     SILC_LOG_ERROR(("Incomplete WHOIS info: %s %s %s",
146                     nickname ? nickname : "",
147                     username ? username : "",
148                     realname ? realname : ""));
149     return FALSE;
150   }
151
152   tmp = silc_argument_get_arg_type(cmd->args, 7, &len);
153   if (tmp) {
154     SILC_GET32_MSB(mode, tmp);
155   }
156
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                                          &cache);
166   if (!client) {
167     client = silc_idlist_find_client_by_id(server->global_list, 
168                                            client_id, &cache);
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     if (strchr(nickname, '@')) {
180       int len = strcspn(nickname, "@");
181       nick = silc_calloc(len + 1, sizeof(char));
182       memcpy(nick, nickname, len);
183     } else {
184       nick = strdup(nickname);
185     }
186
187     /* We don't have that client anywhere, add it. The client is added
188        to global list since server didn't have it in the lists so it must be 
189        global. */
190     client = silc_idlist_add_client(server->global_list, nick,
191                                     strdup(username), 
192                                     strdup(realname), client_id, 
193                                     cmd->sock->user_data, NULL);
194     if (!client)
195       return FALSE;
196
197     client->mode = mode;
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       memcpy(nick, nickname, len);
208     } else {
209       nick = strdup(nickname);
210     }
211
212     if (client->nickname)
213       silc_free(client->nickname);
214     if (client->username)
215       silc_free(client->username);
216     if (client->userinfo)
217       silc_free(client->userinfo);
218     
219     client->nickname = nick;
220     client->username = strdup(username);
221     client->userinfo = strdup(realname);
222     client->mode = mode;
223
224     if (cache) {
225       cache->data = nick;
226       silc_idcache_sort_by_data(global ? server->global_list->clients : 
227                                 server->local_list->clients);
228     }
229
230     silc_free(client_id);
231   }
232
233   return TRUE;
234 }
235
236 /* Reiceved reply for WHOIS command. We sent the whois request to our
237    primary router, if we are normal server, and thus has now received reply
238    to the command. We will figure out what client originally sent us the
239    command and will send the reply to it.  If we are router we will figure
240    out who server sent us the command and send reply to that one. */
241
242 SILC_SERVER_CMD_REPLY_FUNC(whois)
243 {
244   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
245   SilcCommandStatus status;
246
247   COMMAND_CHECK_STATUS_LIST;
248
249   if (!silc_server_command_reply_whois_save(cmd))
250     goto out;
251
252   /* Execute any pending commands */
253   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_WHOIS);
254
255  out:
256   SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_WHOIS);
257   silc_server_command_reply_free(cmd);
258 }
259
260 /* Caches the received IDENTIFY information. */
261
262 static char
263 silc_server_command_reply_identify_save(SilcServerCommandReplyContext cmd)
264 {
265   SilcServer server = cmd->server;
266   int len, id_len;
267   unsigned char *id_data;
268   char *nickname, *username;
269   SilcClientID *client_id;
270   SilcClientEntry client;
271   SilcIDCacheEntry cache = NULL;
272   char global = FALSE;
273   char *nick = NULL;
274
275   id_data = silc_argument_get_arg_type(cmd->args, 2, &id_len);
276   nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
277   username = silc_argument_get_arg_type(cmd->args, 4, &len);
278   if (!id_data)
279     return FALSE;
280
281   client_id = silc_id_payload_parse_id(id_data, id_len);
282   if (!client_id)
283     return FALSE;
284
285   /* Check if we have this client cached already. */
286
287   client = silc_idlist_find_client_by_id(server->local_list, client_id,
288                                          &cache);
289   if (!client) {
290     client = silc_idlist_find_client_by_id(server->global_list, 
291                                            client_id, &cache);
292     global = TRUE;
293   }
294
295   if (!client) {
296     /* If router did not find such Client ID in its lists then this must
297        be bogus client or some router in the net is buggy. */
298     if (server->server_type == SILC_ROUTER)
299       return FALSE;
300
301     /* Take hostname out of nick string if it includes it. */
302     if (nickname) {
303       if (strchr(nickname, '@')) {
304         int len = strcspn(nickname, "@");
305         nick = silc_calloc(len + 1, sizeof(char));
306         memcpy(nick, nickname, len);
307       } else {
308         nick = strdup(nickname);
309       }
310     }
311
312     /* We don't have that client anywhere, add it. The client is added
313        to global list since server didn't have it in the lists so it must be 
314        global. */
315     silc_idlist_add_client(server->global_list, nick,
316                            username ? strdup(username) : NULL, NULL,
317                            client_id, cmd->sock->user_data, NULL);
318   } else {
319     /* We have the client already, update the data */
320
321     SILC_LOG_DEBUG(("Updating client data"));
322
323     /* Take hostname out of nick string if it includes it. */
324     if (nickname) {
325       if (strchr(nickname, '@')) {
326         int len = strcspn(nickname, "@");
327         nick = silc_calloc(len + 1, sizeof(char));
328         memcpy(nick, nickname, len);
329       } else {
330         nick = strdup(nickname);
331       }
332     }
333
334     if (nickname && client->nickname)
335       silc_free(client->nickname);
336
337     if (nickname)
338       client->nickname = nick;
339
340     if (username && client->username) {
341       silc_free(client->username);
342       client->username = strdup(username);
343     }
344
345     if (nickname && cache) {
346       cache->data = nick;
347       silc_idcache_sort_by_data(global ? server->global_list->clients : 
348                                 server->local_list->clients);
349     }
350
351     silc_free(client_id);
352   }
353
354   return TRUE;
355 }
356
357 /* Received reply for forwarded IDENTIFY command. We have received the
358    requested identify information now and we will cache it. After this we
359    will call the pending command so that the requestee gets the information
360    after all. */
361
362 SILC_SERVER_CMD_REPLY_FUNC(identify)
363 {
364   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
365   SilcCommandStatus status;
366
367   COMMAND_CHECK_STATUS_LIST;
368
369   if (!silc_server_command_reply_identify_save(cmd))
370     goto out;
371
372   /* Execute any pending commands */
373   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_IDENTIFY);
374
375  out:
376   SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_IDENTIFY);
377   silc_server_command_reply_free(cmd);
378 }
379
380 /* Received reply for forwarded JOIN command. Router has created or joined
381    the client to the channel. We save some channel information locally
382    for future use. */
383
384 SILC_SERVER_CMD_REPLY_FUNC(join)
385 {
386   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
387   SilcServer server = cmd->server;
388   SilcCommandStatus status;
389   SilcChannelID *id;
390   SilcClientID *client_id = NULL;
391   SilcChannelEntry entry;
392   SilcHmac hmac = NULL;
393   unsigned int id_len, len, list_count;
394   unsigned char *id_string;
395   char *channel_name, *tmp;
396   unsigned int mode, created;
397   SilcBuffer keyp, client_id_list, client_mode_list;
398
399   COMMAND_CHECK_STATUS;
400
401   /* Get channel name */
402   channel_name = silc_argument_get_arg_type(cmd->args, 2, NULL);
403   if (!channel_name)
404     goto out;
405
406   /* Get channel ID */
407   id_string = silc_argument_get_arg_type(cmd->args, 3, &id_len);
408   if (!id_string)
409     goto out;
410
411   /* Get client ID */
412   tmp = silc_argument_get_arg_type(cmd->args, 4, &len);
413   if (!tmp)
414     goto out;
415   client_id = silc_id_payload_parse_id(tmp, len);
416   if (!client_id)
417     goto out;
418
419   /* Get mode mask */
420   tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
421   if (!tmp)
422     goto out;
423   SILC_GET32_MSB(mode, tmp);
424
425   /* Get created boolean value */
426   tmp = silc_argument_get_arg_type(cmd->args, 6, NULL);
427   if (!tmp)
428     goto out;
429   SILC_GET32_MSB(created, tmp);
430   if (created != 0 && created != 1)
431     goto out;
432
433   /* Get channel key */
434   tmp = silc_argument_get_arg_type(cmd->args, 7, &len);
435   if (!tmp)
436     goto out;
437   keyp = silc_buffer_alloc(len);
438   silc_buffer_pull_tail(keyp, SILC_BUFFER_END(keyp));
439   silc_buffer_put(keyp, tmp, len);
440
441   id = silc_id_payload_parse_id(id_string, id_len);
442   if (!id)
443     goto out;
444
445   /* Get hmac */
446   tmp = silc_argument_get_arg_type(cmd->args, 11, NULL);
447   if (tmp) {
448     if (!silc_hmac_alloc(tmp, NULL, &hmac))
449       goto out;
450   }
451
452   /* Get the list count */
453   tmp = silc_argument_get_arg_type(cmd->args, 12, &len);
454   if (!tmp)
455     goto out;
456   SILC_GET32_MSB(list_count, tmp);
457
458   /* Get Client ID list */
459   tmp = silc_argument_get_arg_type(cmd->args, 13, &len);
460   if (!tmp)
461     goto out;
462
463   client_id_list = silc_buffer_alloc(len);
464   silc_buffer_pull_tail(client_id_list, len);
465   silc_buffer_put(client_id_list, tmp, len);
466
467   /* Get client mode list */
468   tmp = silc_argument_get_arg_type(cmd->args, 14, &len);
469   if (!tmp)
470     goto out;
471
472   client_mode_list = silc_buffer_alloc(len);
473   silc_buffer_pull_tail(client_mode_list, len);
474   silc_buffer_put(client_mode_list, tmp, len);
475
476   /* See whether we already have the channel. */
477   entry = silc_idlist_find_channel_by_id(server->local_list, id, NULL);
478   if (!entry) {
479     /* Add new channel */
480
481     SILC_LOG_DEBUG(("Adding new [%s] channel %s id(%s)", 
482                     (created == 0 ? "existing" : "created"), channel_name,
483                     silc_id_render(id, SILC_ID_CHANNEL)));
484
485     /* Add the channel to our local list. */
486     entry = silc_idlist_add_channel(server->local_list, strdup(channel_name), 
487                                     SILC_CHANNEL_MODE_NONE, id, 
488                                     server->router, NULL, hmac);
489     if (!entry) {
490       silc_free(id);
491       goto out;
492     }
493   } else {
494     silc_free(id);
495   }
496
497   /* If channel was not created we know there is global users on the 
498      channel. */
499   entry->global_users = (created == 0 ? TRUE : FALSE);
500
501   /* If channel was just created the mask must be zero */
502   if (!entry->global_users && mode) {
503     SILC_LOG_DEBUG(("Buggy router `%s' sent non-zero mode mask for "
504                     "new channel, forcing it to zero", cmd->sock->hostname));
505     mode = 0;
506   }
507
508   /* Save channel mode */
509   entry->mode = mode;
510
511   /* Save channel key */
512   silc_server_save_channel_key(server, keyp, entry);
513   silc_buffer_free(keyp);
514
515   /* Save the users to the channel */
516   silc_server_save_users_on_channel(server, cmd->sock, entry, 
517                                     client_id, client_id_list,
518                                     client_mode_list, list_count);
519
520   silc_buffer_free(client_id_list);
521   silc_buffer_free(client_mode_list);
522
523   /* Execute any pending commands */
524   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_JOIN);
525
526  out:
527   SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_JOIN);
528   if (client_id)
529     silc_free(client_id);
530   silc_server_command_reply_free(cmd);
531 }
532
533 SILC_SERVER_CMD_REPLY_FUNC(users)
534 {
535   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
536   SilcServer server = cmd->server;
537   SilcCommandStatus status;
538   SilcChannelEntry channel;
539   SilcChannelID *channel_id = NULL;
540   SilcBuffer client_id_list;
541   SilcBuffer client_mode_list;
542   unsigned char *tmp;
543   unsigned int tmp_len;
544   unsigned int list_count;
545
546   COMMAND_CHECK_STATUS;
547
548   /* Get channel ID */
549   tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
550   if (!tmp)
551     goto out;
552   channel_id = silc_id_payload_parse_id(tmp, tmp_len);
553   if (!channel_id)
554     goto out;
555
556   /* Get channel entry */
557   channel = silc_idlist_find_channel_by_id(server->local_list, 
558                                            channel_id, NULL);
559   if (!channel) {
560     channel = silc_idlist_find_channel_by_id(server->global_list, 
561                                              channel_id, NULL);
562     if (!channel)
563       goto out;
564   }
565
566   /* Get the list count */
567   tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
568   if (!tmp)
569     goto out;
570   SILC_GET32_MSB(list_count, tmp);
571
572   /* Get Client ID list */
573   tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
574   if (!tmp)
575     goto out;
576
577   client_id_list = silc_buffer_alloc(tmp_len);
578   silc_buffer_pull_tail(client_id_list, tmp_len);
579   silc_buffer_put(client_id_list, tmp, tmp_len);
580
581   /* Get client mode list */
582   tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
583   if (!tmp)
584     goto out;
585
586   client_mode_list = silc_buffer_alloc(tmp_len);
587   silc_buffer_pull_tail(client_mode_list, tmp_len);
588   silc_buffer_put(client_mode_list, tmp, tmp_len);
589
590   /* Save the users to the channel */
591   silc_server_save_users_on_channel(server, cmd->sock, channel, NULL,
592                                     client_id_list, client_mode_list, 
593                                     list_count);
594
595   silc_buffer_free(client_id_list);
596   silc_buffer_free(client_mode_list);
597
598   /* Execute any pending commands */
599   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
600
601  out:
602   SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_USERS);
603   if (channel_id)
604     silc_free(channel_id);
605   silc_server_command_reply_free(cmd);
606 }