WHOIS command works on router environment.
[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_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL)); \
29   if (status != SILC_STATUS_OK) {                                         \
30     silc_server_command_reply_free(cmd);                                  \
31     return;                                                               \
32   }                                                                       \
33 } while(0)
34
35 #define COMMAND_CHECK_STATUS_LIST                                         \
36 do {                                                                      \
37   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL)); \
38   if (status != SILC_STATUS_OK &&                                         \
39       status != SILC_STATUS_LIST_START &&                                 \
40       status != SILC_STATUS_LIST_ITEM &&                                  \
41       status != SILC_STATUS_LIST_END) {                                   \
42     silc_server_command_reply_free(cmd);                                  \
43     return;                                                               \
44   }                                                                       \
45 } while(0)
46
47 /* Server command reply list. Not all commands have reply function as
48    they are never sent by server. More maybe added later if need appears. */
49 SilcServerCommandReply silc_command_reply_list[] =
50 {
51   SILC_SERVER_CMD_REPLY(join, JOIN),
52   SILC_SERVER_CMD_REPLY(whois, WHOIS),
53   SILC_SERVER_CMD_REPLY(identify, IDENTIFY),
54
55   { NULL, 0 },
56 };
57
58 /* Process received command reply. */
59
60 void silc_server_command_reply_process(SilcServer server,
61                                        SilcSocketConnection sock,
62                                        SilcBuffer buffer)
63 {
64   SilcServerCommandReply *cmd;
65   SilcServerCommandReplyContext ctx;
66   SilcCommandPayload payload;
67   SilcCommand command;
68   unsigned short ident;
69
70   SILC_LOG_DEBUG(("Start"));
71
72   /* Get command reply payload from packet */
73   payload = silc_command_payload_parse(buffer);
74   if (!payload) {
75     /* Silently ignore bad reply packet */
76     SILC_LOG_DEBUG(("Bad command reply packet"));
77     return;
78   }
79   
80   /* Allocate command reply context. This must be free'd by the
81      command reply routine receiving it. */
82   ctx = silc_calloc(1, sizeof(*ctx));
83   ctx->server = server;
84   ctx->sock = sock;
85   ctx->payload = payload;
86   ctx->args = silc_command_get_args(ctx->payload);
87   ident = silc_command_get_ident(ctx->payload);
88       
89   /* Check for pending commands and mark to be exeucted */
90   silc_server_command_pending_check(server, ctx, 
91                                     silc_command_get(ctx->payload), ident);
92
93   /* Execute command reply */
94   command = silc_command_get(ctx->payload);
95   for (cmd = silc_command_reply_list; cmd->cb; cmd++)
96     if (cmd->cmd == command)
97       break;
98
99   if (cmd == NULL) {
100     silc_free(ctx);
101     return;
102   }
103
104   cmd->cb(ctx);
105 }
106
107 /* Free command reply context and its internals. */
108
109 void silc_server_command_reply_free(SilcServerCommandReplyContext cmd)
110 {
111   if (cmd) {
112     silc_command_free_payload(cmd->payload);
113     silc_free(cmd);
114   }
115 }
116
117 /* Caches the received WHOIS information. If we are normal server currently
118    we cache global information only for short period of time.  */
119 /* XXX cache expirying not implemented yet! */
120
121 static char
122 silc_server_command_reply_whois_save(SilcServerCommandReplyContext cmd)
123 {
124   SilcServer server = cmd->server;
125   int len, id_len;
126   unsigned char *id_data;
127   char *nickname, *username, *realname;
128   SilcClientID *client_id;
129   SilcClientEntry client;
130   SilcIDCacheEntry cache = NULL;
131   char global = FALSE;
132   char *nick;
133
134   id_data = silc_argument_get_arg_type(cmd->args, 2, &id_len);
135   nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
136   username = silc_argument_get_arg_type(cmd->args, 4, &len);
137   realname = silc_argument_get_arg_type(cmd->args, 5, &len);
138   if (!id_data || !nickname || !username || !realname) 
139     return FALSE;
140
141   client_id = silc_id_payload_parse_id(id_data, id_len);
142
143   /* Check if we have this client cached already. */
144
145   client = silc_idlist_find_client_by_id(server->local_list, client_id,
146                                          &cache);
147   if (!client) {
148     client = silc_idlist_find_client_by_id(server->global_list, 
149                                            client_id, &cache);
150     global = TRUE;
151   }
152
153   if (!client) {
154     /* If router did not find such Client ID in its lists then this must
155        be bogus client or some router in the net is buggy. */
156     if (server->server_type == SILC_ROUTER)
157       return FALSE;
158
159     /* Take hostname out of nick string if it includes it. */
160     if (strchr(nickname, '@')) {
161       int len = strcspn(nickname, "@");
162       nick = silc_calloc(len + 1, sizeof(char));
163       memcpy(nick, nickname, len);
164     } else {
165       nick = strdup(nickname);
166     }
167
168     /* We don't have that client anywhere, add it. The client is added
169        to global list since server didn't have it in the lists so it must be 
170        global. */
171     silc_idlist_add_client(server->global_list, nick,
172                            strdup(username), 
173                            strdup(realname), client_id, NULL, NULL);
174   } else {
175     /* We have the client already, update the data */
176
177     SILC_LOG_DEBUG(("Updating client data"));
178
179     /* Take hostname out of nick string if it includes it. */
180     if (strchr(nickname, '@')) {
181       int len = strcspn(nickname, "@");
182       nick = silc_calloc(len + 1, sizeof(char));
183       memcpy(nick, nickname, len);
184     } else {
185       nick = strdup(nickname);
186     }
187
188     if (client->nickname)
189       silc_free(client->nickname);
190     if (client->username)
191       silc_free(client->username);
192     if (client->userinfo)
193       silc_free(client->userinfo);
194     
195     client->nickname = nick;
196     client->username = strdup(username);
197     client->userinfo = strdup(realname);
198
199     if (cache) {
200       cache->data = nick;
201       silc_idcache_sort_by_data(global ? server->global_list->clients : 
202                                 server->local_list->clients);
203     }
204
205     silc_free(client_id);
206   }
207
208   return TRUE;
209 }
210
211 /* Reiceved reply for WHOIS command. We sent the whois request to our
212    primary router, if we are normal server, and thus has now received reply
213    to the command. We will figure out what client originally sent us the
214    command and will send the reply to it.  If we are router we will figure
215    out who server sent us the command and send reply to that one. */
216
217 SILC_SERVER_CMD_REPLY_FUNC(whois)
218 {
219   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
220   SilcCommandStatus status;
221
222   SILC_LOG_DEBUG(("Start"));
223
224   COMMAND_CHECK_STATUS_LIST;
225
226   if (!silc_server_command_reply_whois_save(cmd))
227     goto out;
228
229   /* XXX */
230
231   /* Process one identify reply */
232   if (status == SILC_STATUS_OK) {
233
234   }
235
236   if (status == SILC_STATUS_LIST_START) {
237
238   }
239
240   if (status == SILC_STATUS_LIST_ITEM) {
241
242   }
243
244   if (status == SILC_STATUS_LIST_END) {
245
246   }
247
248   /* Execute any pending commands */
249   SILC_SERVER_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_WHOIS);
250
251  out:
252   silc_server_command_reply_free(cmd);
253 }
254
255 /* Caches the received IDENTIFY information. */
256
257 static char
258 silc_server_command_reply_identify_save(SilcServerCommandReplyContext cmd)
259 {
260   SilcServer server = cmd->server;
261   int len, id_len;
262   unsigned char *id_data;
263   char *nickname, *username;
264   SilcClientID *client_id;
265   SilcClientEntry client;
266   SilcIDCacheEntry cache = NULL;
267   char global = FALSE;
268   char *nick = NULL;
269
270   id_data = silc_argument_get_arg_type(cmd->args, 2, &id_len);
271   nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
272   username = silc_argument_get_arg_type(cmd->args, 4, &len);
273   if (!id_data)
274     return FALSE;
275
276   client_id = silc_id_payload_parse_id(id_data, id_len);
277
278   /* Check if we have this client cached already. */
279
280   client = silc_idlist_find_client_by_id(server->local_list, client_id,
281                                          &cache);
282   if (!client) {
283     client = silc_idlist_find_client_by_id(server->global_list, 
284                                            client_id, &cache);
285     global = TRUE;
286   }
287
288   if (!client) {
289     /* If router did not find such Client ID in its lists then this must
290        be bogus client or some router in the net is buggy. */
291     if (server->server_type == SILC_ROUTER)
292       return FALSE;
293
294     /* Take hostname out of nick string if it includes it. */
295     if (nickname) {
296       if (strchr(nickname, '@')) {
297         int len = strcspn(nickname, "@");
298         nick = silc_calloc(len + 1, sizeof(char));
299         memcpy(nick, nickname, len);
300       } else {
301         nick = strdup(nickname);
302       }
303     }
304
305     /* We don't have that client anywhere, add it. The client is added
306        to global list since server didn't have it in the lists so it must be 
307        global. */
308     silc_idlist_add_client(server->global_list, nick,
309                            username ? strdup(username) : NULL, NULL,
310                            client_id, NULL, NULL);
311   } else {
312     /* We have the client already, update the data */
313
314     SILC_LOG_DEBUG(("Updating client data"));
315
316     /* Take hostname out of nick string if it includes it. */
317     if (nickname) {
318       if (strchr(nickname, '@')) {
319         int len = strcspn(nickname, "@");
320         nick = silc_calloc(len + 1, sizeof(char));
321         memcpy(nick, nickname, len);
322       } else {
323         nick = strdup(nickname);
324       }
325     }
326
327     if (nickname && client->nickname) {
328       silc_free(client->nickname);
329       client->nickname = nick;
330     }
331
332     if (username && client->username) {
333       silc_free(client->username);
334       client->username = strdup(username);
335     }
336
337     if (nickname && cache) {
338       cache->data = nick;
339       silc_idcache_sort_by_data(global ? server->global_list->clients : 
340                                 server->local_list->clients);
341     }
342
343     silc_free(client_id);
344   }
345
346   return TRUE;
347 }
348
349 /* Received reply for forwarded IDENTIFY command. We have received the
350    requested identify information now and we will cache it. After this we
351    will call the pending command so that the requestee gets the information
352    after all. */
353
354 SILC_SERVER_CMD_REPLY_FUNC(identify)
355 {
356   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
357   SilcCommandStatus status;
358
359   SILC_LOG_DEBUG(("Start"));
360
361   COMMAND_CHECK_STATUS_LIST;
362
363   if (!silc_server_command_reply_identify_save(cmd))
364     goto out;
365
366   /* XXX */
367
368   if (status == SILC_STATUS_OK) {
369
370   }
371
372   if (status == SILC_STATUS_LIST_START) {
373
374   }
375
376   if (status == SILC_STATUS_LIST_ITEM) {
377
378   }
379
380   if (status == SILC_STATUS_LIST_END) {
381
382   }
383
384   /* Execute any pending commands */
385   SILC_SERVER_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_IDENTIFY);
386
387  out:
388   silc_server_command_reply_free(cmd);
389 }
390
391 /* Received reply for forwarded JOIN command. Router has created or joined
392    the client to the channel. We save some channel information locally
393    for future use. */
394
395 SILC_SERVER_CMD_REPLY_FUNC(join)
396 {
397   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
398   SilcServer server = cmd->server;
399   SilcCommandStatus status;
400   SilcChannelID *id;
401   SilcChannelEntry entry;
402   unsigned int len;
403   unsigned char *id_string;
404   char *channel_name, *tmp;
405
406   SILC_LOG_DEBUG(("Start"));
407
408   COMMAND_CHECK_STATUS;
409
410   /* Get channel name */
411   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
412   if (!tmp)
413     goto out;
414
415   /* Get channel ID */
416   id_string = silc_argument_get_arg_type(cmd->args, 3, &len);
417   if (!id_string)
418     goto out;
419
420   channel_name = strdup(tmp);
421   id = silc_id_payload_parse_id(id_string, len);
422
423   /* XXX We should check that we have sent JOIN command to the router
424      in the first place. Also should check that we don't have the channel
425      already in the cache. These checks must be made because of possible
426      buggy routers. */
427
428   SILC_LOG_DEBUG(("Adding new channel %s id(%s)", channel_name,
429                   silc_id_render(id, SILC_ID_CHANNEL)));
430
431   /* Add the channel to our local list. */
432   entry = silc_idlist_add_channel(server->local_list, channel_name, 
433                                   SILC_CHANNEL_MODE_NONE, id, 
434                                   server->router, NULL);
435   if (!entry) {
436     silc_free(channel_name);
437     silc_free(id);
438     goto out;
439   }
440
441   //entry->global_users = TRUE;
442
443   /* Execute any pending commands */
444   SILC_SERVER_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_JOIN);
445
446  out:
447   silc_server_command_reply_free(cmd);
448 }