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