Initial revision
[silc.git] / apps / silc / 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 /*
21  * Command reply functions are "the otherside" of the command functions.
22  * Reply to a command sent by server is handled by these functions.
23  */
24 /*
25  * $Id$
26  * $Log$
27  * Revision 1.1  2000/06/27 11:36:56  priikone
28  * Initial revision
29  *
30  *
31  */
32
33 #include "clientincludes.h"
34
35 /* Client command reply list. */
36 SilcClientCommandReply silc_command_reply_list[] =
37 {
38   SILC_CLIENT_CMD_REPLY(whois, WHOIS),
39   SILC_CLIENT_CMD_REPLY(whowas, WHOWAS),
40   SILC_CLIENT_CMD_REPLY(identify, IDENTIFY),
41   SILC_CLIENT_CMD_REPLY(nick, NICK),
42   SILC_CLIENT_CMD_REPLY(list, LIST),
43   SILC_CLIENT_CMD_REPLY(topic, TOPIC),
44   SILC_CLIENT_CMD_REPLY(invite, INVITE),
45   SILC_CLIENT_CMD_REPLY(quit, QUIT),
46   SILC_CLIENT_CMD_REPLY(kill, KILL),
47   SILC_CLIENT_CMD_REPLY(info, INFO),
48   SILC_CLIENT_CMD_REPLY(away, AWAY),
49   SILC_CLIENT_CMD_REPLY(connect, CONNECT),
50   SILC_CLIENT_CMD_REPLY(ping, PING),
51   SILC_CLIENT_CMD_REPLY(oper, OPER),
52   SILC_CLIENT_CMD_REPLY(join, JOIN),
53   SILC_CLIENT_CMD_REPLY(motd, MOTD),
54   SILC_CLIENT_CMD_REPLY(umode, UMODE),
55   SILC_CLIENT_CMD_REPLY(cmode, CMODE),
56   SILC_CLIENT_CMD_REPLY(kick, KICK),
57   SILC_CLIENT_CMD_REPLY(restart, RESTART),
58   SILC_CLIENT_CMD_REPLY(close, CLOSE),
59   SILC_CLIENT_CMD_REPLY(die, DIE),
60   SILC_CLIENT_CMD_REPLY(silcoper, SILCOPER),
61   SILC_CLIENT_CMD_REPLY(leave, LEAVE),
62   SILC_CLIENT_CMD_REPLY(names, LEAVE),
63
64   { NULL, 0 },
65 };
66
67 /* Status message structure. Messages are defined below. */
68 typedef struct {
69   SilcCommandStatus status;
70   char *message;
71 } SilcCommandStatusMessage;
72
73 /* Status messages returned by the server */
74 #define STAT(x) SILC_STATUS_ERR_##x
75 const SilcCommandStatusMessage silc_command_status_messages[] = {
76
77   { STAT(NO_SUCH_NICK),      "No such nickname" },
78   { STAT(NO_SUCH_CHANNEL),   "No such channel" },
79   { STAT(NO_SUCH_SERVER),    "No such server" },
80   { STAT(TOO_MANY_TARGETS),  "Duplicate recipients. No message delivered" },
81   { STAT(NO_RECIPIENT),      "No recipient given" },
82   { STAT(UNKNOWN_COMMAND),   "Unknown command" },
83   { STAT(WILDCARDS),         "Unknown command" },
84   { STAT(NO_CLIENT_ID),      "No Client ID given" },
85   { STAT(NO_CHANNEL_ID),     "No Channel ID given" },
86   { STAT(BAD_CLIENT_ID),     "Bad Client ID" },
87   { STAT(BAD_CHANNEL_ID),    "Bad Channel ID" },
88   { STAT(NO_SUCH_CLIENT_ID), "No such Client ID" },
89   { STAT(NO_SUCH_CHANNEL_ID),"No such Channel ID" },
90   { STAT(NICKNAME_IN_USE),   "Nickname already exists" },
91   { STAT(NOT_ON_CHANNEL),    "You are not on that channel" },
92   { STAT(USER_ON_CHANNEL),   "User already on channel" },
93   { STAT(NOT_REGISTERED),    "You have not registered" },
94   { STAT(NOT_ENOUGH_PARAMS), "Not enough parameters" },
95   { STAT(TOO_MANY_PARAMS),   "Too many parameters" },
96   { STAT(PERM_DENIED),       "Your host is not among the privileged" },
97   { STAT(BANNED_FROM_SERVER),"You are banned from this server" },
98   { STAT(BAD_PASSWORD),      "Cannot join channel. Incorrect password" },
99   { STAT(CHANNEL_IS_FULL),   "Cannot join channel. Channel is full" },
100   { STAT(NOT_INVITED),     "Cannot join channel. You have not been invited" },
101   { STAT(BANNED_FROM_CHANNEL), "Cannot join channel. You have been banned" },
102   { STAT(UNKNOWN_MODE),    "Unknown mode" },
103   { STAT(NOT_YOU),         "Cannot change mode for other users" },
104   { STAT(NO_CHANNEL_PRIV), "Permission denied. You are not channel operator" },
105   { STAT(NO_SERVER_PRIV),  "Permission denied. You are not server operator" },
106   { STAT(NO_ROUTER_PRIV),  "Permission denied. You are not SILC operator" },
107   { STAT(BAD_NICKNAME),    "Bad nickname" },
108   { STAT(BAD_CHANNEL),     "Bad channel name" },
109   { STAT(AUTH_FAILED),     "Authentication failed" },
110
111   { 0, NULL }
112 };
113
114 /* Process received command reply. */
115
116 void silc_client_command_reply_process(SilcClient client,
117                                        SilcSocketConnection sock,
118                                        SilcBuffer buffer)
119 {
120   SilcClientCommandReplyContext ctx;
121   SilcCommandPayload payload;
122
123   /* Get command reply payload from packet */
124   payload = silc_command_parse_payload(buffer);
125   if (!payload) {
126     /* Silently ignore bad reply packet */
127     SILC_LOG_DEBUG(("Bad command reply packet"));
128     return;
129   }
130   
131   /* Allocate command reply context. This must be free'd by the
132      command reply routine receiving it. */
133   ctx = silc_calloc(1, sizeof(*ctx));
134   ctx->client = client;
135   ctx->sock = sock;
136   ctx->payload = payload;
137       
138   /* Check for pending commands and mark to be exeucted */
139   SILC_CLIENT_COMMAND_CHECK_PENDING(ctx);
140   
141   /* Execute command reply */
142   SILC_CLIENT_COMMAND_REPLY_EXEC(ctx);
143 }
144
145 /* Returns status message string */
146
147 static char *
148 silc_client_command_status_message(SilcCommandStatus status)
149 {
150   int i;
151
152   for (i = 0; silc_command_status_messages[i].message; i++) {
153     if (silc_command_status_messages[i].status == status)
154       break;
155   }
156
157   if (silc_command_status_messages[i].message == NULL)
158     return NULL;
159
160   return silc_command_status_messages[i].message;
161 }
162
163 /* Free command reply context and its internals. */
164
165 void silc_client_command_reply_free(SilcClientCommandReplyContext cmd)
166 {
167   if (cmd) {
168     silc_command_free_payload(cmd->payload);
169     silc_free(cmd);
170   }
171 }
172
173 /* Received reply for WHOIS command. This maybe called several times
174    for one WHOIS command as server may reply with list of results. */
175
176 SILC_CLIENT_CMD_REPLY_FUNC(whois)
177 {
178   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
179   SilcClient client = cmd->client;
180   SilcCommandStatus status;
181   unsigned char *tmp;
182
183   SILC_LOG_DEBUG(("Start"));
184
185   tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
186   SILC_GET16_MSB(status, tmp);
187   if (status != SILC_STATUS_OK) {
188     if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
189       tmp += 2;
190       silc_say(cmd->client, "%s: %s", tmp,
191                silc_client_command_status_message(status));
192       goto out;
193     } else {
194       silc_say(cmd->client, "%s", silc_client_command_status_message(status));
195       goto out;
196     }
197   }
198
199   /* Display one whois reply */
200   if (status == SILC_STATUS_OK) {
201     char buf[256];
202     int argc, len;
203     unsigned char *id_data;
204     char *nickname = NULL, *username = NULL;
205     char *realname = NULL;
206     void *id;
207
208     memset(buf, 0, sizeof(buf));
209
210     argc = silc_command_get_arg_num(cmd->payload);
211     id_data = silc_command_get_arg_type(cmd->payload, 2, NULL);
212
213     nickname = silc_command_get_arg_type(cmd->payload, 3, &len);
214     if (nickname) {
215       strncat(buf, nickname, len);
216       strncat(buf, " is ", 4);
217     }
218
219     username = silc_command_get_arg_type(cmd->payload, 4, &len);
220     if (username) {
221       strncat(buf, username, len);
222     }
223
224     realname = silc_command_get_arg_type(cmd->payload, 5, &len);
225     if (realname) {
226       strncat(buf, " (", 2);
227       strncat(buf, realname, len);
228       strncat(buf, ")", 1);
229     }
230
231 #if 0
232     /* Save received Client ID to ID cache */
233     /* XXX Maybe should not be saved as /MSG will get confused */
234     id = silc_id_str2id(id_data, SILC_ID_CLIENT);
235     client->current_win->client_id_cache_count[(int)nickname[0] - 32] =
236     silc_idcache_add(&client->current_win->
237                      client_id_cache[(int)nickname[0] - 32],
238                      client->current_win->
239                      client_id_cache_count[(int)nickname[0] - 32],
240                      strdup(nickname), SILC_ID_CLIENT, id, NULL);
241 #endif
242
243     silc_say(cmd->client, "%s", buf);
244    }
245
246   if (status == SILC_STATUS_LIST_START) {
247
248   }
249
250   if (status == SILC_STATUS_LIST_END) {
251
252   }
253
254   SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_WHOIS);
255
256  out:
257   silc_client_command_reply_free(cmd);
258 }
259
260 SILC_CLIENT_CMD_REPLY_FUNC(whowas)
261 {
262 }
263
264 /* Received reply for IDENTIFY command. This maybe called several times
265    for one IDENTIFY command as server may reply with list of results. 
266    This is totally silent and does not print anything on screen. */
267
268 SILC_CLIENT_CMD_REPLY_FUNC(identify)
269 {
270   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
271   SilcClientWindow win = (SilcClientWindow)cmd->sock->user_data;
272   SilcClientEntry client_entry;
273   SilcCommandStatus status;
274   unsigned char *tmp;
275
276   SILC_LOG_DEBUG(("Start"));
277
278 #define CIDC(x) win->client_id_cache[(x) - 32]
279 #define CIDCC(x) win->client_id_cache_count[(x) - 32]
280
281   tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
282   SILC_GET16_MSB(status, tmp);
283   if (status != SILC_STATUS_OK) {
284     if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
285       tmp += 2;
286       silc_say(cmd->client, "%s: %s", tmp,
287                silc_client_command_status_message(status));
288       goto out;
289     } else {
290       silc_say(cmd->client, "%s", silc_client_command_status_message(status));
291       goto out;
292     }
293   }
294
295   /* Display one whois reply */
296   if (status == SILC_STATUS_OK) {
297     unsigned char *id_data;
298     char *nickname;
299
300     id_data = silc_command_get_arg_type(cmd->payload, 2, NULL);
301     nickname = silc_command_get_arg_type(cmd->payload, 3, NULL);
302
303     /* Allocate client entry */
304     client_entry = silc_calloc(1, sizeof(*client_entry));
305     client_entry->id = silc_id_str2id(id_data, SILC_ID_CLIENT);
306     client_entry->nickname = strdup(nickname);
307
308     /* Save received Client ID to ID cache */
309     CIDCC(nickname[0]) =
310       silc_idcache_add(&CIDC(nickname[0]), CIDCC(nickname[0]),
311                        client_entry->nickname, SILC_ID_CLIENT, 
312                        client_entry->id, client_entry);
313   }
314
315   if (status == SILC_STATUS_LIST_START) {
316
317   }
318
319   if (status == SILC_STATUS_LIST_END) {
320
321   }
322
323   SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_IDENTIFY);
324
325  out:
326   silc_client_command_reply_free(cmd);
327 #undef CIDC
328 #undef CIDCC
329 }
330
331 /* Received reply for command NICK. If everything went without errors
332    we just received our new Client ID. */
333
334 SILC_CLIENT_CMD_REPLY_FUNC(nick)
335 {
336   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
337   SilcClientWindow win = (SilcClientWindow)cmd->sock->user_data;
338   SilcCommandStatus status;
339   unsigned char *tmp, *id_string;
340   int argc;
341
342   SILC_LOG_DEBUG(("Start"));
343
344   tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
345   SILC_GET16_MSB(status, tmp);
346   if (status != SILC_STATUS_OK) {
347     silc_say(cmd->client, "Cannot set nickname: %s", 
348              silc_client_command_status_message(status));
349     goto out;
350   }
351
352   argc = silc_command_get_arg_num(cmd->payload);
353   if (argc < 2 || argc > 2) {
354     silc_say(cmd->client, "Cannot set nickname: bad reply to command");
355     goto out;
356   }
357
358   /* Take received Client ID */
359   id_string = silc_command_get_arg_type(cmd->payload, 2, NULL);
360   silc_client_receive_new_id(cmd->client, cmd->sock, id_string);
361
362   /* Update nickname on screen */
363   cmd->client->screen->bottom_line->nickname = win->nickname;
364   silc_screen_print_bottom_line(cmd->client->screen, 0);
365
366  out:
367   silc_client_command_reply_free(cmd);
368 }
369
370 SILC_CLIENT_CMD_REPLY_FUNC(list)
371 {
372 }
373
374 SILC_CLIENT_CMD_REPLY_FUNC(topic)
375 {
376 }
377
378 SILC_CLIENT_CMD_REPLY_FUNC(invite)
379 {
380 }
381  
382 SILC_CLIENT_CMD_REPLY_FUNC(quit)
383 {
384 }
385
386 SILC_CLIENT_CMD_REPLY_FUNC(kill)
387 {
388 }
389
390 SILC_CLIENT_CMD_REPLY_FUNC(info)
391 {
392 }
393
394 SILC_CLIENT_CMD_REPLY_FUNC(away)
395 {
396 }
397
398 SILC_CLIENT_CMD_REPLY_FUNC(connect)
399 {
400 }
401
402 SILC_CLIENT_CMD_REPLY_FUNC(ping)
403 {
404 }
405
406 SILC_CLIENT_CMD_REPLY_FUNC(oper)
407 {
408 }
409
410 /* Received reply for JOIN command. */
411
412 SILC_CLIENT_CMD_REPLY_FUNC(join)
413 {
414   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
415   SilcClient client = cmd->client;
416   SilcCommandStatus status;
417   unsigned int argc;
418   unsigned char *id_string;
419   char *topic, *tmp, *channel_name;
420
421   SILC_LOG_DEBUG(("Start"));
422
423   tmp = silc_command_get_arg_type(cmd->payload, 1, NULL);
424   SILC_GET16_MSB(status, tmp);
425   if (status != SILC_STATUS_OK) {
426     silc_say(cmd->client, "%s", silc_client_command_status_message(status));
427     goto out;
428   }
429
430   argc = silc_command_get_arg_num(cmd->payload);
431   if (argc < 3 || argc > 4) {
432     silc_say(cmd->client, "Cannot join channel: Bad reply packet");
433     goto out;
434   }
435
436   /* Get channel name */
437   tmp = silc_command_get_arg_type(cmd->payload, 2, NULL);
438   channel_name = strdup(tmp);
439
440   /* Get channel ID */
441   id_string = silc_command_get_arg_type(cmd->payload, 3, NULL);
442
443   /* Get topic */
444   topic = silc_command_get_arg_type(cmd->payload, 4, NULL);
445
446   /* Save received Channel ID */
447   silc_client_new_channel_id(cmd->client, cmd->sock, channel_name, id_string);
448
449   /* Print channel name on screen */
450   client->screen->bottom_line->channel = channel_name;
451   silc_screen_print_bottom_line(client->screen, 0);
452
453   if (topic)
454     silc_say(client, "Topic for %s: %s", channel_name, topic);
455
456  out:
457   silc_client_command_reply_free(cmd);
458 }
459
460 SILC_CLIENT_CMD_REPLY_FUNC(motd)
461 {
462 }
463
464 SILC_CLIENT_CMD_REPLY_FUNC(umode)
465 {
466 }
467
468 SILC_CLIENT_CMD_REPLY_FUNC(cmode)
469 {
470 }
471
472 SILC_CLIENT_CMD_REPLY_FUNC(kick)
473 {
474 }
475
476 SILC_CLIENT_CMD_REPLY_FUNC(restart)
477 {
478 }
479  
480 SILC_CLIENT_CMD_REPLY_FUNC(close)
481 {
482 }
483  
484 SILC_CLIENT_CMD_REPLY_FUNC(die)
485 {
486 }
487  
488 SILC_CLIENT_CMD_REPLY_FUNC(silcoper)
489 {
490 }
491
492 SILC_CLIENT_CMD_REPLY_FUNC(leave)
493 {
494 }
495
496 SILC_CLIENT_CMD_REPLY_FUNC(names)
497 {
498 }
499
500 /* Private message received. This processes the private message and
501    finally displays it on the screen. */
502
503 SILC_CLIENT_CMD_REPLY_FUNC(msg)
504 {
505   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
506   SilcClient client = cmd->client;
507   SilcBuffer buffer = (SilcBuffer)cmd->context;
508   unsigned short nick_len;
509   unsigned char *nickname, *message;
510   SilcIDCache *id_cache;
511   unsigned char *id_string;
512   void *id;
513
514   /* Get nickname */
515   silc_buffer_unformat(buffer, 
516                        SILC_STR_UI16_NSTRING_ALLOC(&nickname, &nick_len),
517                        SILC_STR_END);
518   silc_buffer_pull(buffer, 2 + nick_len);
519
520 #if 0
521   /* Get ID of the sender */
522   id_string = silc_calloc(SILC_ID_CLIENT_LEN, sizeof(unsigned char *));
523   silc_buffer_push(buffer, SILC_ID_CLIENT_LEN + SILC_ID_CLIENT_LEN);
524   memcpy(id_string, buffer->data, SILC_ID_CLIENT_LEN);
525   silc_buffer_pull(buffer, SILC_ID_CLIENT_LEN + SILC_ID_CLIENT_LEN);
526   id = silc_id_str2id(id_string, SILC_ID_CLIENT);
527   silc_free(id_string);
528
529   /* Nickname should be verified if we don't have it in the cache */
530   if (silc_idcache_find_by_data(client->current_win->
531                                 client_id_cache[nickname[0] - 32],
532                                 client->current_win->
533                                 client_id_cache_count[nickname[0] - 32],
534                                 nickname, &id_cache) == FALSE) {
535
536     SilcClientCommandContext ctx;
537     char whois[255];
538
539     /* Private message from unknown source, try to resolve it. */
540
541
542     return;
543   }
544 #endif
545      
546   message = silc_calloc(buffer->len + 1, sizeof(char));
547   memcpy(message, buffer->data, buffer->len);
548   silc_print(client, "*%s* %s", nickname, message);
549   memset(message, 0, buffer->len);
550   silc_free(message);
551 }