Cleaned up WHOIS command.
[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(identify, 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.  If we are
119    router we want to cache them a bit longer since we can receive information
120    if any of the information becomes invalid. Normal server cannot receive
121    that information. Returns FALSE if something was wrong with the reply. */
122
123 static char
124 silc_server_command_reply_whois_save(SilcServerCommandReplyContext cmd)
125 {
126   SilcServer server = cmd->server;
127   int len, id_len;
128   unsigned char *id_data;
129   char *nickname, *username, *realname;
130   SilcClientID *client_id;
131   SilcClientEntry client;
132
133   id_data = silc_argument_get_arg_type(cmd->args, 2, &id_len);
134   nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
135   username = silc_argument_get_arg_type(cmd->args, 4, &len);
136   realname = silc_argument_get_arg_type(cmd->args, 5, &len);
137   if (!id_data || !nickname || !username || !realname) 
138     return FALSE;
139
140   client_id = silc_id_payload_parse_id(id_data, id_len);
141
142   /* Check if we have this client cached already. */
143
144   client = silc_idlist_find_client_by_id(server->local_list, client_id);
145   if (!client && server->server_type == SILC_ROUTER)
146     client = silc_idlist_find_client_by_id(server->global_list, client_id);
147
148   if (!client) {
149     /* We don't have that client anywhere, add it. The client is added
150        to global list since server or router didn't have it in the lists
151        so it must be global. */
152     silc_idlist_add_client(server->global_list, strdup(nickname),
153                            username, realname, client_id, NULL, NULL);
154   } else {
155     /* We have the client already, update the data */
156
157     if (client->username)
158       silc_free(client->username);
159     if (client->userinfo)
160       silc_free(client->userinfo);
161     
162     client->username = strdup(username);
163     client->userinfo = strdup(realname);
164   }
165
166   silc_free(client_id);
167
168   return TRUE;
169 }
170
171 /* Reiceved reply for WHOIS command. We sent the whois request to our
172    primary router, if we are normal server, and thus has now received reply
173    to the command. We will figure out what client originally sent us the
174    command and will send the reply to it.  If we are router we will figure
175    out who server sent us the command and send reply to that one. */
176
177 SILC_SERVER_CMD_REPLY_FUNC(whois)
178 {
179   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
180   SilcCommandStatus status;
181
182   SILC_LOG_DEBUG(("Start"));
183
184   COMMAND_CHECK_STATUS_LIST;
185
186   if (!silc_server_command_reply_whois_save(cmd))
187     goto out;
188
189   /* XXX */
190
191   /* Process one identify reply */
192   if (status == SILC_STATUS_OK) {
193
194   }
195
196   if (status == SILC_STATUS_LIST_START) {
197
198   }
199
200   if (status == SILC_STATUS_LIST_ITEM) {
201
202   }
203
204   if (status == SILC_STATUS_LIST_END) {
205
206   }
207
208   /* Execute any pending commands */
209   SILC_SERVER_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_WHOIS);
210
211  out:
212   silc_server_command_reply_free(cmd);
213 }
214
215 /* Received reply for forwarded IDENTIFY command. We have received the
216    requested identify information now and we will cache it. After this we
217    will call the pending command so that the requestee gets the information
218    after all. */
219
220 SILC_SERVER_CMD_REPLY_FUNC(identify)
221 {
222   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
223   SilcServer server = cmd->server;
224   SilcCommandStatus status;
225
226   SILC_LOG_DEBUG(("Start"));
227
228   COMMAND_CHECK_STATUS_LIST;
229
230   /* Process one identify reply */
231   if (status == SILC_STATUS_OK) {
232     SilcClientID *client_id;
233     unsigned int len;
234     unsigned char *id_data;
235     char *nickname, *username;
236
237     id_data = silc_argument_get_arg_type(cmd->args, 2, &len);
238     nickname = silc_argument_get_arg_type(cmd->args, 3, NULL);
239     if (!id_data || !nickname)
240       goto out;
241
242     username = silc_argument_get_arg_type(cmd->args, 4, NULL);
243     client_id = silc_id_payload_parse_id(id_data, len);
244
245     /* Add the client always to our global list. If normal or router server
246        ever gets here it means they don't have this client's information
247        in their cache. */
248     silc_idlist_add_client(server->global_list, strdup(nickname),
249                            username, NULL, client_id, NULL, NULL);
250   }
251
252   if (status == SILC_STATUS_LIST_START) {
253
254   }
255
256   if (status == SILC_STATUS_LIST_ITEM) {
257
258   }
259
260   if (status == SILC_STATUS_LIST_END) {
261
262   }
263
264   /* Execute any pending commands */
265   SILC_SERVER_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_IDENTIFY);
266
267  out:
268   silc_server_command_reply_free(cmd);
269 }
270
271 /* Received reply for forwarded JOIN command. Router has created or joined
272    the client to the channel. We save some channel information locally
273    for future use. */
274
275 SILC_SERVER_CMD_REPLY_FUNC(join)
276 {
277   SilcServerCommandReplyContext cmd = (SilcServerCommandReplyContext)context;
278   SilcServer server = cmd->server;
279   SilcCommandStatus status;
280   SilcChannelID *id;
281   SilcChannelEntry entry;
282   unsigned int len;
283   unsigned char *id_string;
284   char *channel_name, *tmp;
285
286   SILC_LOG_DEBUG(("Start"));
287
288   COMMAND_CHECK_STATUS;
289
290   /* Get channel name */
291   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
292   if (!tmp)
293     goto out;
294
295   /* Get channel ID */
296   id_string = silc_argument_get_arg_type(cmd->args, 3, &len);
297   if (!id_string)
298     goto out;
299
300   channel_name = strdup(tmp);
301   id = silc_id_payload_parse_id(id_string, len);
302
303   /* XXX We should check that we have sent JOIN command to the router
304      in the first place. Also should check that we don't have the channel
305      already in the cache. These checks must be made because of possible
306      buggy routers. */
307
308   SILC_LOG_DEBUG(("Adding new channel %s id(%s)", channel_name,
309                   silc_id_render(id, SILC_ID_CHANNEL)));
310
311   /* Add the channel to our local list. */
312   entry = silc_idlist_add_channel(server->local_list, channel_name, 
313                                   SILC_CHANNEL_MODE_NONE, id, 
314                                   server->router, NULL);
315   if (!entry) {
316     silc_free(channel_name);
317     silc_free(id);
318     goto out;
319   }
320
321   //entry->global_users = TRUE;
322
323   /* Execute any pending commands */
324   SILC_SERVER_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_JOIN);
325
326  out:
327   silc_server_command_reply_free(cmd);
328 }