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_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   SILC_SERVER_CMD_REPLY(names, NAMES),
55
56   { NULL, 0 },
57 };
58
59 /* Process received command reply. */
60
61 void silc_server_command_reply_process(SilcServer server,
62                                        SilcSocketConnection sock,
63                                        SilcBuffer buffer)
64 {
65   SilcServerCommandReply *cmd;
66   SilcServerCommandReplyContext ctx;
67   SilcCommandPayload payload;
68   SilcCommand command;
69   unsigned short ident;
70
71   SILC_LOG_DEBUG(("Start"));
72
73   /* Get command reply payload from packet */
74   payload = silc_command_payload_parse(buffer);
75   if (!payload) {
76     /* Silently ignore bad reply packet */
77     SILC_LOG_DEBUG(("Bad command reply packet"));
78     return;
79   }
80   
81   /* Allocate command reply context. This must be free'd by the
82      command reply routine receiving it. */
83   ctx = silc_calloc(1, sizeof(*ctx));
84   ctx->server = server;
85   ctx->sock = sock;
86   ctx->payload = payload;
87   ctx->args = silc_command_get_args(ctx->payload);
88   ident = silc_command_get_ident(ctx->payload);
89       
90   /* Check for pending commands and mark to be exeucted */
91   silc_server_command_pending_check(server, ctx, 
92                                     silc_command_get(ctx->payload), ident);
93
94   /* Execute command reply */
95   command = silc_command_get(ctx->payload);
96   for (cmd = silc_command_reply_list; cmd->cb; cmd++)
97     if (cmd->cmd == command)
98       break;
99
100   if (cmd == NULL || !cmd->cb) {
101     silc_free(ctx);
102     return;
103   }
104
105   cmd->cb(ctx);
106 }
107
108 /* Free command reply context and its internals. */
109
110 void silc_server_command_reply_free(SilcServerCommandReplyContext cmd)
111 {
112   if (cmd) {
113     silc_command_free_payload(cmd->payload);
114     silc_free(cmd);
115   }
116 }
117
118 /* Caches the received WHOIS information. If we are normal server currently
119    we cache global information only for short period of time.  */
120 /* XXX cache expirying not implemented yet! */
121
122 static char
123 silc_server_command_reply_whois_save(SilcServerCommandReplyContext cmd)
124 {
125   SilcServer server = cmd->server;
126   int len, id_len;
127   unsigned char *id_data;
128   char *nickname, *username, *realname;
129   SilcClientID *client_id;
130   SilcClientEntry client;
131   SilcIDCacheEntry cache = NULL;
132   char global = FALSE;
133   char *nick;
134
135   id_data = silc_argument_get_arg_type(cmd->args, 2, &id_len);
136   nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
137   username = silc_argument_get_arg_type(cmd->args, 4, &len);
138   realname = silc_argument_get_arg_type(cmd->args, 5, &len);
139   if (!id_data || !nickname || !username || !realname) 
140     return FALSE;
141
142   client_id = silc_id_payload_parse_id(id_data, id_len);
143
144   /* Check if we have this client cached already. */
145
146   client = silc_idlist_find_client_by_id(server->local_list, client_id,
147                                          &cache);
148   if (!client) {
149     client = silc_idlist_find_client_by_id(server->global_list, 
150                                            client_id, &cache);
151     global = TRUE;
152   }
153
154   if (!client) {
155     /* If router did not find such Client ID in its lists then this must
156        be bogus client or some router in the net is buggy. */
157     if (server->server_type == SILC_ROUTER)
158       return FALSE;
159
160     /* Take hostname out of nick string if it includes it. */
161     if (strchr(nickname, '@')) {
162       int len = strcspn(nickname, "@");
163       nick = silc_calloc(len + 1, sizeof(char));
164       memcpy(nick, nickname, len);
165     } else {
166       nick = strdup(nickname);
167     }
168
169     /* We don't have that client anywhere, add it. The client is added
170        to global list since server didn't have it in the lists so it must be 
171        global. */
172     silc_idlist_add_client(server->global_list, nick,
173                            strdup(username), 
174                            strdup(realname), client_id, NULL, NULL);
175   } else {
176     /* We have the client already, update the data */
177
178     SILC_LOG_DEBUG(("Updating client data"));
179
180     /* Take hostname out of nick string if it includes it. */
181     if (strchr(nickname, '@')) {
182       int len = strcspn(nickname, "@");
183       nick = silc_calloc(len + 1, sizeof(char));
184       memcpy(nick, nickname, len);
185     } else {
186       nick = strdup(nickname);
187     }
188
189     if (client->nickname)
190       silc_free(client->nickname);
191     if (client->username)
192       silc_free(client->username);
193     if (client->userinfo)
194       silc_free(client->userinfo);
195     
196     client->nickname = nick;
197     client->username = strdup(username);
198     client->userinfo = strdup(realname);
199
200     if (cache) {
201       cache->data = nick;
202       silc_idcache_sort_by_data(global ? server->global_list->clients : 
203                                 server->local_list->clients);
204     }
205
206     silc_free(client_id);
207   }
208
209   return TRUE;
210 }
211
212 /* Reiceved reply for WHOIS command. We sent the whois request to our
213    primary router, if we are normal server, and thus has now received reply
214    to the command. We will figure out what client originally sent us the
215    command and will send the reply to it.  If we are router we will figure
216    out who server sent us the command and send reply to that one. */
217
218 SILC_SERVER_CMD_REPLY_FUNC(whois)
219 {
220   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
221   SilcCommandStatus status;
222
223   SILC_LOG_DEBUG(("Start"));
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   SILC_LOG_DEBUG(("Start"));
361
362   COMMAND_CHECK_STATUS_LIST;
363
364   if (!silc_server_command_reply_identify_save(cmd))
365     goto out;
366
367   /* XXX */
368
369   if (status == SILC_STATUS_OK) {
370
371   }
372
373   if (status == SILC_STATUS_LIST_START) {
374
375   }
376
377   if (status == SILC_STATUS_LIST_ITEM) {
378
379   }
380
381   if (status == SILC_STATUS_LIST_END) {
382
383   }
384
385   /* Execute any pending commands */
386   SILC_SERVER_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_IDENTIFY);
387
388  out:
389   silc_server_command_reply_free(cmd);
390 }
391
392 /* Received reply for forwarded JOIN command. Router has created or joined
393    the client to the channel. We save some channel information locally
394    for future use. */
395
396 SILC_SERVER_CMD_REPLY_FUNC(join)
397 {
398   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
399   SilcServer server = cmd->server;
400   SilcCommandStatus status;
401   SilcChannelID *id;
402   SilcChannelEntry entry;
403   unsigned int len;
404   unsigned char *id_string;
405   char *channel_name, *tmp;
406   unsigned int mode, created;
407   SilcBuffer keyp;
408
409   SILC_LOG_DEBUG(("Start"));
410
411   COMMAND_CHECK_STATUS;
412
413   /* Get channel name */
414   channel_name = silc_argument_get_arg_type(cmd->args, 2, NULL);
415   if (!channel_name)
416     goto out;
417
418   /* Get channel ID */
419   id_string = silc_argument_get_arg_type(cmd->args, 3, &len);
420   if (!id_string)
421     goto out;
422
423   /* Get mode mask */
424   tmp = silc_argument_get_arg_type(cmd->args, 4, NULL);
425   if (!tmp)
426     goto out;
427   SILC_GET32_MSB(mode, tmp);
428
429   /* Get created boolean value */
430   tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
431   if (!tmp)
432     goto out;
433   SILC_GET32_MSB(created, tmp);
434
435   /* Get channel key */
436   tmp = silc_argument_get_arg_type(cmd->args, 6, &len);
437   if (!tmp)
438     goto out;
439   keyp = silc_buffer_alloc(len);
440   silc_buffer_pull_tail(keyp, SILC_BUFFER_END(keyp));
441   silc_buffer_put(keyp, tmp, len);
442
443   id = silc_id_payload_parse_id(id_string, len);
444
445   /* See whether we already have the channel. */
446   entry = silc_idlist_find_channel_by_id(server->local_list, id, NULL);
447   if (!entry) {
448     /* Add new channel */
449
450     SILC_LOG_DEBUG(("Adding new [%s] channel %s id(%s)", 
451                     (created == 0 ? "existing" : "created"), channel_name,
452                     silc_id_render(id, SILC_ID_CHANNEL)));
453
454     /* Add the channel to our local list. */
455     entry = silc_idlist_add_channel(server->local_list, strdup(channel_name), 
456                                     SILC_CHANNEL_MODE_NONE, id, 
457                                     server->router, NULL);
458     if (!entry) {
459       silc_free(id);
460       goto out;
461     }
462   } else {
463     silc_free(id);
464   }
465
466   /* If channel was not created we know there is global users on the 
467      channel. */
468   entry->global_users = (created == 0 ? TRUE : FALSE);
469
470   /* If channel was just created the mask must be zero */
471   if (!entry->global_users && mode) {
472     SILC_LOG_DEBUG(("Buggy router `%s' sent non-zero mode mask for "
473                     "new channel, forcing it to zero", cmd->sock->hostname));
474     mode = 0;
475   }
476
477   /* Save channel mode */
478   entry->mode = mode;
479
480   /* Save channel key */
481   silc_server_save_channel_key(server, keyp, entry);
482   silc_buffer_free(keyp);
483
484   /* Execute any pending commands */
485   SILC_SERVER_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_JOIN);
486
487  out:
488   silc_server_command_reply_free(cmd);
489 }
490
491 SILC_SERVER_CMD_REPLY_FUNC(names)
492 {
493   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
494   SilcServer server = cmd->server;
495   SilcCommandStatus status;
496
497   SILC_LOG_DEBUG(("Start"));
498
499   COMMAND_CHECK_STATUS;
500
501 }