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   client_id = silc_id_payload_parse_id(id_data, id_len);
157   if (!client_id)
158     return FALSE;
159
160   /* Check if we have this client cached already. */
161
162   client = silc_idlist_find_client_by_id(server->local_list, client_id,
163                                          &cache);
164   if (!client) {
165     client = silc_idlist_find_client_by_id(server->global_list, 
166                                            client_id, &cache);
167     global = TRUE;
168   }
169
170   if (!client) {
171     /* If router did not find such Client ID in its lists then this must
172        be bogus client or some router in the net is buggy. */
173     if (server->server_type == SILC_ROUTER)
174       return FALSE;
175
176     /* Take hostname out of nick string if it includes it. */
177     if (strchr(nickname, '@')) {
178       int len = strcspn(nickname, "@");
179       nick = silc_calloc(len + 1, sizeof(char));
180       memcpy(nick, 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->mode = mode;
196   } else {
197     /* We have the client already, update the data */
198
199     SILC_LOG_DEBUG(("Updating client data"));
200
201     /* Take hostname out of nick string if it includes it. */
202     if (strchr(nickname, '@')) {
203       int len = strcspn(nickname, "@");
204       nick = silc_calloc(len + 1, sizeof(char));
205       memcpy(nick, nickname, len);
206     } else {
207       nick = strdup(nickname);
208     }
209
210     if (client->nickname)
211       silc_free(client->nickname);
212     if (client->username)
213       silc_free(client->username);
214     if (client->userinfo)
215       silc_free(client->userinfo);
216     
217     client->nickname = nick;
218     client->username = strdup(username);
219     client->userinfo = strdup(realname);
220     client->mode = mode;
221
222     if (cache) {
223       cache->data = nick;
224       silc_idcache_sort_by_data(global ? server->global_list->clients : 
225                                 server->local_list->clients);
226     }
227
228     silc_free(client_id);
229   }
230
231   return TRUE;
232 }
233
234 /* Reiceved reply for WHOIS command. We sent the whois request to our
235    primary router, if we are normal server, and thus has now received reply
236    to the command. We will figure out what client originally sent us the
237    command and will send the reply to it.  If we are router we will figure
238    out who server sent us the command and send reply to that one. */
239
240 SILC_SERVER_CMD_REPLY_FUNC(whois)
241 {
242   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
243   SilcCommandStatus status;
244
245   COMMAND_CHECK_STATUS_LIST;
246
247   if (!silc_server_command_reply_whois_save(cmd))
248     goto out;
249
250   /* Execute any pending commands */
251   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_WHOIS);
252
253  out:
254   SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_WHOIS);
255   silc_server_command_reply_free(cmd);
256 }
257
258 /* Caches the received IDENTIFY information. */
259
260 static char
261 silc_server_command_reply_identify_save(SilcServerCommandReplyContext cmd)
262 {
263   SilcServer server = cmd->server;
264   int len, id_len;
265   unsigned char *id_data;
266   char *nickname, *username;
267   SilcClientID *client_id;
268   SilcClientEntry client;
269   SilcIDCacheEntry cache = NULL;
270   char global = FALSE;
271   char *nick = NULL;
272
273   id_data = silc_argument_get_arg_type(cmd->args, 2, &id_len);
274   nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
275   username = silc_argument_get_arg_type(cmd->args, 4, &len);
276   if (!id_data)
277     return FALSE;
278
279   client_id = silc_id_payload_parse_id(id_data, id_len);
280   if (!client_id)
281     return FALSE;
282
283   /* Check if we have this client cached already. */
284
285   client = silc_idlist_find_client_by_id(server->local_list, client_id,
286                                          &cache);
287   if (!client) {
288     client = silc_idlist_find_client_by_id(server->global_list, 
289                                            client_id, &cache);
290     global = TRUE;
291   }
292
293   if (!client) {
294     /* If router did not find such Client ID in its lists then this must
295        be bogus client or some router in the net is buggy. */
296     if (server->server_type == SILC_ROUTER)
297       return FALSE;
298
299     /* Take hostname out of nick string if it includes it. */
300     if (nickname) {
301       if (strchr(nickname, '@')) {
302         int len = strcspn(nickname, "@");
303         nick = silc_calloc(len + 1, sizeof(char));
304         memcpy(nick, nickname, len);
305       } else {
306         nick = strdup(nickname);
307       }
308     }
309
310     /* We don't have that client anywhere, add it. The client is added
311        to global list since server didn't have it in the lists so it must be 
312        global. */
313     silc_idlist_add_client(server->global_list, nick,
314                            username ? strdup(username) : NULL, NULL,
315                            client_id, cmd->sock->user_data, NULL);
316   } else {
317     /* We have the client already, update the data */
318
319     SILC_LOG_DEBUG(("Updating client data"));
320
321     /* Take hostname out of nick string if it includes it. */
322     if (nickname) {
323       if (strchr(nickname, '@')) {
324         int len = strcspn(nickname, "@");
325         nick = silc_calloc(len + 1, sizeof(char));
326         memcpy(nick, nickname, len);
327       } else {
328         nick = strdup(nickname);
329       }
330     }
331
332     if (nickname && client->nickname)
333       silc_free(client->nickname);
334
335     if (nickname)
336       client->nickname = nick;
337
338     if (username && client->username) {
339       silc_free(client->username);
340       client->username = strdup(username);
341     }
342
343     if (nickname && cache) {
344       cache->data = nick;
345       silc_idcache_sort_by_data(global ? server->global_list->clients : 
346                                 server->local_list->clients);
347     }
348
349     silc_free(client_id);
350   }
351
352   return TRUE;
353 }
354
355 /* Received reply for forwarded IDENTIFY command. We have received the
356    requested identify information now and we will cache it. After this we
357    will call the pending command so that the requestee gets the information
358    after all. */
359
360 SILC_SERVER_CMD_REPLY_FUNC(identify)
361 {
362   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
363   SilcCommandStatus status;
364
365   COMMAND_CHECK_STATUS_LIST;
366
367   if (!silc_server_command_reply_identify_save(cmd))
368     goto out;
369
370   /* Execute any pending commands */
371   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_IDENTIFY);
372
373  out:
374   SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_IDENTIFY);
375   silc_server_command_reply_free(cmd);
376 }
377
378 /* Received reply for forwarded JOIN command. Router has created or joined
379    the client to the channel. We save some channel information locally
380    for future use. */
381
382 SILC_SERVER_CMD_REPLY_FUNC(join)
383 {
384   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
385   SilcServer server = cmd->server;
386   SilcCommandStatus status;
387   SilcChannelID *id;
388   SilcClientID *client_id = NULL;
389   SilcChannelEntry entry;
390   SilcHmac hmac = NULL;
391   unsigned int id_len, len, list_count;
392   unsigned char *id_string;
393   char *channel_name, *tmp;
394   unsigned int mode, created;
395   SilcBuffer keyp, client_id_list, client_mode_list;
396
397   COMMAND_CHECK_STATUS;
398
399   /* Get channel name */
400   channel_name = silc_argument_get_arg_type(cmd->args, 2, NULL);
401   if (!channel_name)
402     goto out;
403
404   /* Get channel ID */
405   id_string = silc_argument_get_arg_type(cmd->args, 3, &id_len);
406   if (!id_string)
407     goto out;
408
409   /* Get client ID */
410   tmp = silc_argument_get_arg_type(cmd->args, 4, &len);
411   if (!tmp)
412     goto out;
413   client_id = silc_id_payload_parse_id(tmp, len);
414   if (!client_id)
415     goto out;
416
417   /* Get mode mask */
418   tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
419   if (!tmp)
420     goto out;
421   SILC_GET32_MSB(mode, tmp);
422
423   /* Get created boolean value */
424   tmp = silc_argument_get_arg_type(cmd->args, 6, NULL);
425   if (!tmp)
426     goto out;
427   SILC_GET32_MSB(created, tmp);
428   if (created != 0 && created != 1)
429     goto out;
430
431   /* Get channel key */
432   tmp = silc_argument_get_arg_type(cmd->args, 7, &len);
433   if (!tmp)
434     goto out;
435   keyp = silc_buffer_alloc(len);
436   silc_buffer_pull_tail(keyp, SILC_BUFFER_END(keyp));
437   silc_buffer_put(keyp, tmp, len);
438
439   id = silc_id_payload_parse_id(id_string, id_len);
440   if (!id)
441     goto out;
442
443   /* Get hmac */
444   tmp = silc_argument_get_arg_type(cmd->args, 11, NULL);
445   if (tmp) {
446     if (!silc_hmac_alloc(tmp, NULL, &hmac))
447       goto out;
448   }
449
450   /* Get the list count */
451   tmp = silc_argument_get_arg_type(cmd->args, 12, &len);
452   if (!tmp)
453     goto out;
454   SILC_GET32_MSB(list_count, tmp);
455
456   /* Get Client ID list */
457   tmp = silc_argument_get_arg_type(cmd->args, 13, &len);
458   if (!tmp)
459     goto out;
460
461   client_id_list = silc_buffer_alloc(len);
462   silc_buffer_pull_tail(client_id_list, len);
463   silc_buffer_put(client_id_list, tmp, len);
464
465   /* Get client mode list */
466   tmp = silc_argument_get_arg_type(cmd->args, 14, &len);
467   if (!tmp)
468     goto out;
469
470   client_mode_list = silc_buffer_alloc(len);
471   silc_buffer_pull_tail(client_mode_list, len);
472   silc_buffer_put(client_mode_list, tmp, len);
473
474   /* See whether we already have the channel. */
475   entry = silc_idlist_find_channel_by_id(server->local_list, id, NULL);
476   if (!entry) {
477     /* Add new channel */
478
479     SILC_LOG_DEBUG(("Adding new [%s] channel %s id(%s)", 
480                     (created == 0 ? "existing" : "created"), channel_name,
481                     silc_id_render(id, SILC_ID_CHANNEL)));
482
483     /* Add the channel to our local list. */
484     entry = silc_idlist_add_channel(server->local_list, strdup(channel_name), 
485                                     SILC_CHANNEL_MODE_NONE, id, 
486                                     server->router, NULL, hmac);
487     if (!entry) {
488       silc_free(id);
489       goto out;
490     }
491   } else {
492     silc_free(id);
493   }
494
495   /* If channel was not created we know there is global users on the 
496      channel. */
497   entry->global_users = (created == 0 ? TRUE : FALSE);
498
499   /* If channel was just created the mask must be zero */
500   if (!entry->global_users && mode) {
501     SILC_LOG_DEBUG(("Buggy router `%s' sent non-zero mode mask for "
502                     "new channel, forcing it to zero", cmd->sock->hostname));
503     mode = 0;
504   }
505
506   /* Save channel mode */
507   entry->mode = mode;
508
509   /* Save channel key */
510   silc_server_save_channel_key(server, keyp, entry);
511   silc_buffer_free(keyp);
512
513   /* Save the users to the channel */
514   silc_server_save_users_on_channel(server, cmd->sock, entry, 
515                                     client_id, client_id_list,
516                                     client_mode_list, list_count);
517
518   silc_buffer_free(client_id_list);
519   silc_buffer_free(client_mode_list);
520
521   /* Execute any pending commands */
522   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_JOIN);
523
524  out:
525   SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_JOIN);
526   if (client_id)
527     silc_free(client_id);
528   silc_server_command_reply_free(cmd);
529 }
530
531 SILC_SERVER_CMD_REPLY_FUNC(users)
532 {
533   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
534   SilcServer server = cmd->server;
535   SilcCommandStatus status;
536   SilcChannelEntry channel;
537   SilcChannelID *channel_id = NULL;
538   SilcBuffer client_id_list;
539   SilcBuffer client_mode_list;
540   unsigned char *tmp;
541   unsigned int tmp_len;
542   unsigned int list_count;
543
544   COMMAND_CHECK_STATUS;
545
546   /* Get channel ID */
547   tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
548   if (!tmp)
549     goto out;
550   channel_id = silc_id_payload_parse_id(tmp, tmp_len);
551   if (!channel_id)
552     goto out;
553
554   /* Get channel entry */
555   channel = silc_idlist_find_channel_by_id(server->local_list, 
556                                            channel_id, NULL);
557   if (!channel) {
558     channel = silc_idlist_find_channel_by_id(server->global_list, 
559                                              channel_id, NULL);
560     if (!channel)
561       goto out;
562   }
563
564   /* Get the list count */
565   tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
566   if (!tmp)
567     goto out;
568   SILC_GET32_MSB(list_count, tmp);
569
570   /* Get Client ID list */
571   tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
572   if (!tmp)
573     goto out;
574
575   client_id_list = silc_buffer_alloc(tmp_len);
576   silc_buffer_pull_tail(client_id_list, tmp_len);
577   silc_buffer_put(client_id_list, tmp, tmp_len);
578
579   /* Get client mode list */
580   tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
581   if (!tmp)
582     goto out;
583
584   client_mode_list = silc_buffer_alloc(tmp_len);
585   silc_buffer_pull_tail(client_mode_list, tmp_len);
586   silc_buffer_put(client_mode_list, tmp, tmp_len);
587
588   /* Save the users to the channel */
589   silc_server_save_users_on_channel(server, cmd->sock, channel, NULL,
590                                     client_id_list, client_mode_list, 
591                                     list_count);
592
593   silc_buffer_free(client_id_list);
594   silc_buffer_free(client_mode_list);
595
596   /* Execute any pending commands */
597   SILC_SERVER_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
598
599  out:
600   SILC_SERVER_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_USERS);
601   if (channel_id)
602     silc_free(channel_id);
603   silc_server_command_reply_free(cmd);
604 }