updates.
[silc.git] / lib / silcclient / 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  * The arguments received from server are also passed to the calling
25  * application through command_reply client operation.  The arguments are
26  * exactly same and in same order as the server sent it.  However, ID's are
27  * not sent to the application.  Instead, corresponding ID entry is sent
28  * to the application.  For example, instead of sending Client ID the 
29  * corresponding SilcClientEntry is sent to the application.  The case is
30  * same with for example Channel ID's.  This way application has all the
31  * necessary data already in hand without redundant searching.  If ID is
32  * received but ID entry does not exist, NULL is sent.
33  */
34 /* $Id$ */
35
36 #include "clientlibincludes.h"
37
38 /* Client command reply list. */
39 SilcClientCommandReply silc_command_reply_list[] =
40 {
41   SILC_CLIENT_CMD_REPLY(whois, WHOIS),
42   SILC_CLIENT_CMD_REPLY(whowas, WHOWAS),
43   SILC_CLIENT_CMD_REPLY(identify, IDENTIFY),
44   SILC_CLIENT_CMD_REPLY(nick, NICK),
45   SILC_CLIENT_CMD_REPLY(list, LIST),
46   SILC_CLIENT_CMD_REPLY(topic, TOPIC),
47   SILC_CLIENT_CMD_REPLY(invite, INVITE),
48   SILC_CLIENT_CMD_REPLY(quit, QUIT),
49   SILC_CLIENT_CMD_REPLY(kill, KILL),
50   SILC_CLIENT_CMD_REPLY(info, INFO),
51   SILC_CLIENT_CMD_REPLY(connect, CONNECT),
52   SILC_CLIENT_CMD_REPLY(ping, PING),
53   SILC_CLIENT_CMD_REPLY(oper, OPER),
54   SILC_CLIENT_CMD_REPLY(join, JOIN),
55   SILC_CLIENT_CMD_REPLY(motd, MOTD),
56   SILC_CLIENT_CMD_REPLY(umode, UMODE),
57   SILC_CLIENT_CMD_REPLY(cmode, CMODE),
58   SILC_CLIENT_CMD_REPLY(cumode, CUMODE),
59   SILC_CLIENT_CMD_REPLY(kick, KICK),
60   SILC_CLIENT_CMD_REPLY(restart, RESTART),
61   SILC_CLIENT_CMD_REPLY(close, CLOSE),
62   SILC_CLIENT_CMD_REPLY(die, DIE),
63   SILC_CLIENT_CMD_REPLY(silcoper, SILCOPER),
64   SILC_CLIENT_CMD_REPLY(leave, LEAVE),
65   SILC_CLIENT_CMD_REPLY(users, USERS),
66
67   { NULL, 0 },
68 };
69
70 /* Status message structure. Messages are defined below. */
71 typedef struct {
72   SilcCommandStatus status;
73   char *message;
74 } SilcCommandStatusMessage;
75
76 /* Status messages returned by the server */
77 #define STAT(x) SILC_STATUS_ERR_##x
78 const SilcCommandStatusMessage silc_command_status_messages[] = {
79
80   { STAT(NO_SUCH_NICK),      "No such nickname" },
81   { STAT(NO_SUCH_CHANNEL),   "No such channel" },
82   { STAT(NO_SUCH_SERVER),    "No such server" },
83   { STAT(TOO_MANY_TARGETS),  "Duplicate recipients. No message delivered" },
84   { STAT(NO_RECIPIENT),      "No recipient given" },
85   { STAT(UNKNOWN_COMMAND),   "Unknown command" },
86   { STAT(WILDCARDS),         "Unknown command" },
87   { STAT(NO_CLIENT_ID),      "No Client ID given" },
88   { STAT(NO_CHANNEL_ID),     "No Channel ID given" },
89   { STAT(NO_SERVER_ID),      "No Server ID given" },
90   { STAT(BAD_CLIENT_ID),     "Bad Client ID" },
91   { STAT(BAD_CHANNEL_ID),    "Bad Channel ID" },
92   { STAT(NO_SUCH_CLIENT_ID), "No such Client ID" },
93   { STAT(NO_SUCH_CHANNEL_ID),"No such Channel ID" },
94   { STAT(NICKNAME_IN_USE),   "Nickname already exists" },
95   { STAT(NOT_ON_CHANNEL),    "You are not on that channel" },
96   { STAT(USER_NOT_ON_CHANNEL),"They are not on the channel" },
97   { STAT(USER_ON_CHANNEL),   "User already on the channel" },
98   { STAT(NOT_REGISTERED),    "You have not registered" },
99   { STAT(NOT_ENOUGH_PARAMS), "Not enough parameters" },
100   { STAT(TOO_MANY_PARAMS),   "Too many parameters" },
101   { STAT(PERM_DENIED),       "Your host is not among the privileged" },
102   { STAT(BANNED_FROM_SERVER),"You are banned from this server" },
103   { STAT(BAD_PASSWORD),      "Cannot join channel. Incorrect password" },
104   { STAT(CHANNEL_IS_FULL),   "Cannot join channel. Channel is full" },
105   { STAT(NOT_INVITED),     "Cannot join channel. You have not been invited" },
106   { STAT(BANNED_FROM_CHANNEL), "Cannot join channel. You have been banned" },
107   { STAT(UNKNOWN_MODE),    "Unknown mode" },
108   { STAT(NOT_YOU),         "Cannot change mode for other users" },
109   { STAT(NO_CHANNEL_PRIV), "Permission denied. You are not channel operator" },
110   { STAT(NO_SERVER_PRIV),  "Permission denied. You are not server operator" },
111   { STAT(NO_ROUTER_PRIV),  "Permission denied. You are not SILC operator" },
112   { STAT(BAD_NICKNAME),    "Bad nickname" },
113   { STAT(BAD_CHANNEL),     "Bad channel name" },
114   { STAT(AUTH_FAILED),     "Authentication failed" },
115
116   { 0, NULL }
117 };
118
119 /* Command reply operation that is called at the end of all command replys. 
120    Usage: COMMAND_REPLY((ARGS, argument1, argument2, etc...)), */
121 #define COMMAND_REPLY(args) cmd->client->ops->command_reply args
122 #define ARGS cmd->client, cmd->sock->user_data, \
123              cmd->payload, TRUE, silc_command_get(cmd->payload), status
124
125 /* Error reply to application. Usage: COMMAND_REPLY_ERROR; */
126 #define COMMAND_REPLY_ERROR cmd->client->ops->command_reply(cmd->client, \
127   cmd->sock->user_data, cmd->payload, FALSE, \
128   silc_command_get(cmd->payload), status)
129
130 /* Process received command reply. */
131
132 void silc_client_command_reply_process(SilcClient client,
133                                        SilcSocketConnection sock,
134                                        SilcPacketContext *packet)
135 {
136   SilcBuffer buffer = packet->buffer;
137   SilcClientCommandReply *cmd;
138   SilcClientCommandReplyContext ctx;
139   SilcCommandPayload payload;
140   SilcCommand command;
141   unsigned short ident;
142
143   /* Get command reply payload from packet */
144   payload = silc_command_payload_parse(buffer);
145   if (!payload) {
146     /* Silently ignore bad reply packet */
147     SILC_LOG_DEBUG(("Bad command reply packet"));
148     return;
149   }
150   
151   /* Allocate command reply context. This must be free'd by the
152      command reply routine receiving it. */
153   ctx = silc_calloc(1, sizeof(*ctx));
154   ctx->client = client;
155   ctx->sock = sock;
156   ctx->payload = payload;
157   ctx->args = silc_command_get_args(ctx->payload);
158   ctx->packet = packet;
159   ident = silc_command_get_ident(ctx->payload);
160       
161   /* Check for pending commands and mark to be exeucted */
162   silc_client_command_pending_check(sock->user_data, ctx, 
163                                     silc_command_get(ctx->payload), ident);
164
165   /* Execute command reply */
166   command = silc_command_get(ctx->payload);
167   for (cmd = silc_command_reply_list; cmd->cb; cmd++)
168     if (cmd->cmd == command)
169       break;
170
171   if (cmd == NULL || !cmd->cb) {
172     silc_free(ctx);
173     return;
174   }
175
176   cmd->cb(ctx);
177 }
178
179 /* Returns status message string */
180
181 static char *
182 silc_client_command_status_message(SilcCommandStatus status)
183 {
184   int i;
185
186   for (i = 0; silc_command_status_messages[i].message; i++) {
187     if (silc_command_status_messages[i].status == status)
188       break;
189   }
190
191   if (silc_command_status_messages[i].message == NULL)
192     return NULL;
193
194   return silc_command_status_messages[i].message;
195 }
196
197 /* Free command reply context and its internals. */
198
199 void silc_client_command_reply_free(SilcClientCommandReplyContext cmd)
200 {
201   if (cmd) {
202     silc_command_free_payload(cmd->payload);
203     silc_free(cmd);
204   }
205 }
206
207 static void 
208 silc_client_command_reply_whois_print(SilcClientCommandReplyContext cmd,
209                                       SilcCommandStatus status)
210 {
211   char buf[256];
212   int argc, len;
213   unsigned char *id_data;
214   char *nickname = NULL, *username = NULL;
215   char *realname = NULL;
216   SilcClientID *client_id;
217   SilcIDCacheEntry id_cache = NULL;
218   SilcClientEntry client_entry = NULL;
219   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
220   
221   memset(buf, 0, sizeof(buf));
222   
223   argc = silc_argument_get_arg_num(cmd->args);
224
225   id_data = silc_argument_get_arg_type(cmd->args, 2, &len);
226   if (!id_data) {
227     COMMAND_REPLY_ERROR;
228     return;
229   }
230   
231   client_id = silc_id_payload_parse_id(id_data, len);
232   if (!client_id) {
233     COMMAND_REPLY_ERROR;
234     return;
235   }
236   
237   nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
238   if (nickname) {
239     strncat(buf, nickname, len);
240     strncat(buf, " is ", 4);
241   }
242   
243   username = silc_argument_get_arg_type(cmd->args, 4, &len);
244   if (username) {
245     strncat(buf, username, len);
246   }
247   
248   realname = silc_argument_get_arg_type(cmd->args, 5, &len);
249   if (realname) {
250     strncat(buf, " (", 2);
251     strncat(buf, realname, len);
252     strncat(buf, ")", 1);
253   }
254   
255   /* Check if we have this client cached already. */
256   if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
257                                    SILC_ID_CLIENT, &id_cache)) {
258     client_entry = silc_calloc(1, sizeof(*client_entry));
259     client_entry->id = client_id;
260     silc_parse_nickname(nickname, &client_entry->nickname, 
261                         &client_entry->server, &client_entry->num);
262     client_entry->username = strdup(username);
263     if (realname)
264       client_entry->realname = strdup(realname);
265     
266     /* Add client to cache */
267     silc_idcache_add(conn->client_cache, client_entry->nickname,
268                      SILC_ID_CLIENT, client_id, (void *)client_entry, TRUE);
269   } else {
270     client_entry = (SilcClientEntry)id_cache->context;
271     if (client_entry->nickname)
272       silc_free(client_entry->nickname);
273     if (client_entry->server)
274       silc_free(client_entry->server);
275     if (client_entry->username)
276       silc_free(client_entry->username);
277     if (client_entry->realname)
278       silc_free(client_entry->realname);
279
280     silc_parse_nickname(nickname, &client_entry->nickname, 
281                         &client_entry->server, &client_entry->num);
282     client_entry->username = strdup(username);
283     if (realname)
284       client_entry->realname = strdup(realname);
285
286     id_cache->data = client_entry->nickname;
287     silc_idcache_sort_by_data(conn->client_cache);
288
289     silc_free(client_id);
290   }
291
292   if (!cmd->callback)
293     cmd->client->ops->say(cmd->client, conn, "%s", buf);
294
295   /* Notify application */
296   COMMAND_REPLY((ARGS, client_entry, nickname, username, realname, 
297                  NULL, NULL));
298 }
299
300 /* Received reply for WHOIS command. This maybe called several times
301    for one WHOIS command as server may reply with list of results. */
302
303 SILC_CLIENT_CMD_REPLY_FUNC(whois)
304 {
305   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
306   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
307   SilcCommandStatus status;
308   unsigned char *tmp;
309
310   SILC_LOG_DEBUG(("Start"));
311
312   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
313   SILC_GET16_MSB(status, tmp);
314   if (status != SILC_STATUS_OK && 
315       status != SILC_STATUS_LIST_START &&
316       status != SILC_STATUS_LIST_ITEM &&
317       status != SILC_STATUS_LIST_END) {
318     if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
319       /* Take nickname which may be provided */
320       tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
321       if (tmp)
322         cmd->client->ops->say(cmd->client, conn, "%s: %s", tmp,
323                  silc_client_command_status_message(status));
324       else
325         cmd->client->ops->say(cmd->client, conn, "%s",
326                  silc_client_command_status_message(status));
327       COMMAND_REPLY_ERROR;
328       goto out;
329     } else {
330       cmd->client->ops->say(cmd->client, conn,
331                "%s", silc_client_command_status_message(status));
332       COMMAND_REPLY_ERROR;
333       goto out;
334     }
335   }
336
337   /* Display one whois reply */
338   if (status == SILC_STATUS_OK) {
339     silc_client_command_reply_whois_print(cmd, status);
340   }
341
342   /* XXX list should not be displayed untill all items has been received. */
343   if (status == SILC_STATUS_LIST_START) {
344     silc_client_command_reply_whois_print(cmd, status);
345   }
346
347   if (status == SILC_STATUS_LIST_ITEM) {
348     silc_client_command_reply_whois_print(cmd, status);
349   }
350
351   if (status == SILC_STATUS_LIST_END) {
352     silc_client_command_reply_whois_print(cmd, status);
353   }
354
355   /* Execute any pending command callbacks */
356   SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_WHOIS);
357
358  out:
359   silc_client_command_reply_free(cmd);
360 }
361
362 /* Received reply for WHOWAS command. */
363
364 SILC_CLIENT_CMD_REPLY_FUNC(whowas)
365 {
366
367 }
368
369 /* Received reply for IDENTIFY command. This maybe called several times
370    for one IDENTIFY command as server may reply with list of results. 
371    This is totally silent and does not print anything on screen. */
372
373 SILC_CLIENT_CMD_REPLY_FUNC(identify)
374 {
375   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
376   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
377   SilcClientEntry client_entry;
378   SilcIDCacheEntry id_cache = NULL;
379   SilcCommandStatus status;
380   unsigned char *tmp;
381
382   SILC_LOG_DEBUG(("Start"));
383
384   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
385   SILC_GET16_MSB(status, tmp);
386   if (status != SILC_STATUS_OK) {
387     if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
388       /* Take nickname which may be provided */
389       tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
390       if (tmp)
391         cmd->client->ops->say(cmd->client, conn, "%s: %s", tmp,
392                  silc_client_command_status_message(status));
393       else
394         cmd->client->ops->say(cmd->client, conn, "%s",
395                  silc_client_command_status_message(status));
396       COMMAND_REPLY_ERROR;
397       goto out;
398     } else {
399       cmd->client->ops->say(cmd->client, conn,
400                "%s", silc_client_command_status_message(status));
401       COMMAND_REPLY_ERROR;
402       goto out;
403     }
404   }
405
406   /* Display one whois reply */
407   if (status == SILC_STATUS_OK) {
408     unsigned int len;
409     unsigned char *id_data;
410     char *nickname;
411     char *username;
412     SilcClientID *client_id;
413
414     id_data = silc_argument_get_arg_type(cmd->args, 2, &len);
415     if (!id_data)
416       goto out;
417     client_id = silc_id_payload_parse_id(id_data, len);
418     if (!client_id)
419       goto out;
420
421     nickname = silc_argument_get_arg_type(cmd->args, 3, NULL);
422     username = silc_argument_get_arg_type(cmd->args, 4, NULL);
423
424     if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
425                                      SILC_ID_CLIENT, &id_cache)) {
426       client_entry = silc_calloc(1, sizeof(*client_entry));
427       client_entry->id = client_id;
428       silc_parse_nickname(nickname, &client_entry->nickname, 
429                           &client_entry->server, &client_entry->num);
430       if (username)
431         client_entry->username = strdup(username);
432     
433       /* Add client to cache */
434       silc_idcache_add(conn->client_cache, client_entry->nickname,
435                        SILC_ID_CLIENT, client_id, (void *)client_entry, TRUE);
436     } else {
437       client_entry = (SilcClientEntry)id_cache->context;
438       if (client_entry->nickname)
439         silc_free(client_entry->nickname);
440       if (client_entry->server)
441         silc_free(client_entry->server);
442       if (username && client_entry->username)
443         silc_free(client_entry->username);
444
445       silc_parse_nickname(nickname, &client_entry->nickname, 
446                           &client_entry->server, &client_entry->num);
447
448       if (username)
449         client_entry->username = strdup(username);
450
451       id_cache->data = client_entry->nickname;
452       silc_idcache_sort_by_data(conn->client_cache);
453     
454       silc_free(client_id);
455     }
456   }
457
458   if (status == SILC_STATUS_LIST_START) {
459
460   }
461
462   if (status == SILC_STATUS_LIST_END) {
463
464   }
465
466   /* Execute any pending command callbacks */
467   SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_IDENTIFY);
468
469  out:
470   silc_client_command_reply_free(cmd);
471 }
472
473 /* Received reply for command NICK. If everything went without errors
474    we just received our new Client ID. */
475
476 SILC_CLIENT_CMD_REPLY_FUNC(nick)
477 {
478   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
479   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
480   SilcCommandStatus status;
481   SilcIDPayload idp;
482   unsigned char *tmp;
483   unsigned int argc, len;
484
485   SILC_LOG_DEBUG(("Start"));
486
487   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
488   if (status != SILC_STATUS_OK) {
489     cmd->client->ops->say(cmd->client, conn, "Cannot set nickname: %s", 
490              silc_client_command_status_message(status));
491     COMMAND_REPLY_ERROR;
492     goto out;
493   }
494
495   argc = silc_argument_get_arg_num(cmd->args);
496   if (argc < 2 || argc > 2) {
497     cmd->client->ops->say(cmd->client, conn, 
498                           "Cannot set nickname: bad reply to command");
499     COMMAND_REPLY_ERROR;
500     goto out;
501   }
502
503   /* Take received Client ID */
504   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
505   idp = silc_id_payload_parse_data(tmp, len);
506   if (!idp) {
507     COMMAND_REPLY_ERROR;
508     goto out;
509   }
510   silc_client_receive_new_id(cmd->client, cmd->sock, idp);
511     
512   /* Notify application */
513   COMMAND_REPLY((ARGS, conn->local_entry));
514
515   /* Execute any pending command callbacks */
516   SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_NICK);
517
518  out:
519   silc_client_command_reply_free(cmd);
520 }
521
522 SILC_CLIENT_CMD_REPLY_FUNC(list)
523 {
524 }
525
526 /* Received reply to topic command. */
527
528 SILC_CLIENT_CMD_REPLY_FUNC(topic)
529 {
530   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
531   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
532   SilcCommandStatus status;
533   SilcChannelEntry channel;
534   SilcChannelID *channel_id = NULL;
535   SilcIDCacheEntry id_cache = NULL;
536   unsigned char *tmp;
537   char *topic;
538   unsigned int argc, len;
539
540   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
541   if (status != SILC_STATUS_OK) {
542     cmd->client->ops->say(cmd->client, conn,
543              "%s", silc_client_command_status_message(status));
544     COMMAND_REPLY_ERROR;
545     silc_client_command_reply_free(cmd);
546     return;
547   }
548
549   argc = silc_argument_get_arg_num(cmd->args);
550   if (argc < 1 || argc > 3) {
551     COMMAND_REPLY_ERROR;
552     goto out;
553   }
554
555   /* Take Channel ID */
556   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
557   if (!tmp)
558     goto out;
559
560   /* Take topic */
561   topic = silc_argument_get_arg_type(cmd->args, 3, NULL);
562   if (!topic)
563     goto out;
564
565   channel_id = silc_id_payload_parse_id(tmp, len);
566   if (!channel_id)
567     goto out;
568
569   /* Get the channel name */
570   if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
571                                    SILC_ID_CHANNEL, &id_cache)) {
572     silc_free(channel_id);
573     COMMAND_REPLY_ERROR;
574     goto out;
575   }
576   
577   channel = (SilcChannelEntry)id_cache->context;
578
579   cmd->client->ops->say(cmd->client, conn, 
580                         "Topic on channel %s: %s", channel->channel_name,
581                         topic);
582
583   /* Notify application */
584   COMMAND_REPLY((ARGS, channel, topic));
585
586   /* Execute any pending command callbacks */
587   SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_TOPIC);
588
589  out:
590   silc_client_command_reply_free(cmd);
591 }
592
593 /* Received reply to invite command. */
594
595 SILC_CLIENT_CMD_REPLY_FUNC(invite)
596 {
597   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
598   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
599   SilcCommandStatus status;
600   unsigned char *tmp;
601
602   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
603   SILC_GET16_MSB(status, tmp);
604   if (status != SILC_STATUS_OK) {
605     cmd->client->ops->say(cmd->client, conn,
606              "%s", silc_client_command_status_message(status));
607     COMMAND_REPLY_ERROR;
608     silc_client_command_reply_free(cmd);
609     return;
610   }
611
612   /* Notify application */
613   COMMAND_REPLY((ARGS));
614
615   /* Execute any pending command callbacks */
616   SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_INVITE);
617
618   silc_client_command_reply_free(cmd);
619 }
620  
621 SILC_CLIENT_CMD_REPLY_FUNC(quit)
622 {
623 }
624
625 SILC_CLIENT_CMD_REPLY_FUNC(kill)
626 {
627 }
628
629 /* Received reply to INFO command. We receive the server ID and some
630    information about the server user requested. */
631
632 SILC_CLIENT_CMD_REPLY_FUNC(info)
633 {
634   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
635   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
636   SilcClient client = cmd->client;
637   SilcCommandStatus status;
638   unsigned char *tmp;
639
640   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
641   SILC_GET16_MSB(status, tmp);
642   if (status != SILC_STATUS_OK) {
643     cmd->client->ops->say(cmd->client, conn,
644              "%s", silc_client_command_status_message(status));
645     COMMAND_REPLY_ERROR;
646     silc_client_command_reply_free(cmd);
647     return;
648   }
649
650   /* Get server ID */
651   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
652   if (!tmp)
653     goto out;
654
655   /* XXX save server id */
656
657   /* Get server info */
658   tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
659   if (!tmp)
660     goto out;
661
662   client->ops->say(cmd->client, conn, "Info: %s", tmp);
663
664   /* Notify application */
665   COMMAND_REPLY((ARGS, NULL, (char *)tmp));
666
667   /* Execute any pending command callbacks */
668   SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_INFO);
669
670  out:
671   silc_client_command_reply_free(cmd);
672 }
673
674 SILC_CLIENT_CMD_REPLY_FUNC(connect)
675 {
676 }
677
678 /* Received reply to PING command. The reply time is shown to user. */
679
680 SILC_CLIENT_CMD_REPLY_FUNC(ping)
681 {
682   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
683   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
684   SilcCommandStatus status;
685   void *id;
686   int i;
687   time_t diff, curtime;
688
689   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
690   if (status != SILC_STATUS_OK) {
691     cmd->client->ops->say(cmd->client, conn,
692              "%s", silc_client_command_status_message(status));
693     COMMAND_REPLY_ERROR;
694     goto out;
695   }
696
697   curtime = time(NULL);
698   id = silc_id_str2id(cmd->packet->src_id, cmd->packet->src_id_len,
699                       cmd->packet->src_id_type);
700   if (!id) {
701     COMMAND_REPLY_ERROR;
702     goto out;
703   }
704
705   for (i = 0; i < conn->ping_count; i++) {
706     if (!SILC_ID_SERVER_COMPARE(conn->ping[i].dest_id, id)) {
707       diff = curtime - conn->ping[i].start_time;
708       cmd->client->ops->say(cmd->client, conn, 
709                             "Ping reply from %s: %d second%s", 
710                             conn->ping[i].dest_name, diff, 
711                             diff == 1 ? "" : "s");
712       
713       conn->ping[i].start_time = 0;
714       silc_free(conn->ping[i].dest_id);
715       conn->ping[i].dest_id = NULL;
716       silc_free(conn->ping[i].dest_name);
717       conn->ping[i].dest_name = NULL;
718       break;
719     }
720   }
721
722   silc_free(id);
723
724   /* Notify application */
725   COMMAND_REPLY((ARGS));
726
727   /* Execute any pending command callbacks */
728   SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_PING);
729
730  out:
731   silc_client_command_reply_free(cmd);
732 }
733
734 SILC_CLIENT_CMD_REPLY_FUNC(oper)
735 {
736 }
737
738 /* Received reply for JOIN command. */
739
740 SILC_CLIENT_CMD_REPLY_FUNC(join)
741 {
742   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
743   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
744   SilcClient client = cmd->client;
745   SilcCommandStatus status;
746   SilcIDPayload idp = NULL;
747   unsigned int argc, mode, len;
748   char *topic, *tmp, *channel_name = NULL;
749   SilcBuffer keyp;
750
751   SILC_LOG_DEBUG(("Start"));
752
753   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
754   if (status != SILC_STATUS_OK) {
755     cmd->client->ops->say(cmd->client, conn,
756              "%s", silc_client_command_status_message(status));
757     COMMAND_REPLY_ERROR;
758     goto out;
759   }
760
761   argc = silc_argument_get_arg_num(cmd->args);
762   if (argc < 3 || argc > 9) {
763     cmd->client->ops->say(cmd->client, conn,
764              "Cannot join channel: Bad reply packet");
765     COMMAND_REPLY_ERROR;
766     goto out;
767   }
768
769   /* Get channel name */
770   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
771   if (!tmp) {
772     cmd->client->ops->say(cmd->client, conn, 
773                           "Cannot join channel: Bad reply packet");
774     COMMAND_REPLY_ERROR;
775     goto out;
776   }
777   channel_name = strdup(tmp);
778
779   /* Get Channel ID */
780   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
781   if (!tmp) {
782     cmd->client->ops->say(cmd->client, conn, 
783                           "Cannot join channel: Bad reply packet");
784     COMMAND_REPLY_ERROR;
785     silc_free(channel_name);
786     goto out;
787   }
788   idp = silc_id_payload_parse_data(tmp, len);
789   if (!idp) {
790     COMMAND_REPLY_ERROR;
791     silc_free(channel_name);
792     goto out;
793   }
794
795   /* Get channel mode */
796   tmp = silc_argument_get_arg_type(cmd->args, 4, NULL);
797   if (tmp)
798     SILC_GET32_MSB(mode, tmp);
799   else
800     mode = 0;
801
802   /* Get channel key */
803   tmp = silc_argument_get_arg_type(cmd->args, 6, &len);
804   if (!tmp) {
805     silc_id_payload_free(idp);
806     silc_free(channel_name);
807     goto out;
808   }
809   keyp = silc_buffer_alloc(len);
810   silc_buffer_pull_tail(keyp, SILC_BUFFER_END(keyp));
811   silc_buffer_put(keyp, tmp, len);
812
813   /* Get topic */
814   topic = silc_argument_get_arg_type(cmd->args, 8, NULL);
815
816   /* Save received Channel ID */
817   silc_client_new_channel_id(cmd->client, cmd->sock, channel_name, 
818                              mode, idp);
819   silc_id_payload_free(idp);
820
821   /* Save channel key */
822   silc_client_save_channel_key(conn, keyp, conn->current_channel);
823   silc_buffer_free(keyp);
824
825   if (topic)
826     client->ops->say(cmd->client, conn, 
827                      "Topic for %s: %s", channel_name, topic);
828
829   /* Notify application */
830   COMMAND_REPLY((ARGS, channel_name, conn->current_channel, mode,
831                  NULL, NULL, topic));
832
833   /* Execute any pending command callbacks */
834   SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_JOIN);
835
836  out:
837   silc_client_command_reply_free(cmd);
838 }
839
840 /* Received reply for MOTD command */
841
842 SILC_CLIENT_CMD_REPLY_FUNC(motd)
843 {
844   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
845   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
846   SilcCommandStatus status;
847   unsigned int argc, i;
848   unsigned char *tmp;
849   char *motd = NULL, *cp, line[256];
850
851   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
852   SILC_GET16_MSB(status, tmp);
853   if (status != SILC_STATUS_OK) {
854     cmd->client->ops->say(cmd->client, conn,
855              "%s", silc_client_command_status_message(status));
856     COMMAND_REPLY_ERROR;
857     return;
858   }
859
860   argc = silc_argument_get_arg_num(cmd->args);
861   if (argc > 2) {
862     COMMAND_REPLY_ERROR;
863     goto out;
864   }
865
866   if (argc == 2) {
867     motd = silc_argument_get_arg_type(cmd->args, 2, NULL);
868     if (!motd) {
869       COMMAND_REPLY_ERROR;
870       goto out;
871     }
872
873     i = 0;
874     cp = motd;
875     while(cp[i] != 0) {
876       if (cp[i++] == '\n') {
877         memset(line, 0, sizeof(line));
878         strncat(line, cp, i - 1);
879         cp += i;
880         
881         if (i == 2)
882           line[0] = ' ';
883         
884         cmd->client->ops->say(cmd->client, conn, "%s", line);
885         
886         if (!strlen(cp))
887           break;
888         i = 0;
889       }
890     }
891   }
892
893   /* Notify application */
894   COMMAND_REPLY((ARGS, motd));
895
896   /* Execute any pending command callbacks */
897   SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_MOTD);
898
899  out:
900   silc_client_command_reply_free(cmd);
901 }
902
903 SILC_CLIENT_CMD_REPLY_FUNC(umode)
904 {
905 }
906
907 /* Received reply for CMODE command. */
908
909 SILC_CLIENT_CMD_REPLY_FUNC(cmode)
910 {
911   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
912   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
913   SilcCommandStatus status;
914   unsigned char *tmp;
915
916   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
917   if (status != SILC_STATUS_OK) {
918     cmd->client->ops->say(cmd->client, conn,
919              "%s", silc_client_command_status_message(status));
920     COMMAND_REPLY_ERROR;
921     goto out;
922   }
923
924   /* Get channel mode */
925   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
926   if (!tmp) {
927     COMMAND_REPLY_ERROR;
928     goto out;
929   }
930
931   /* Notify application */
932   COMMAND_REPLY((ARGS, tmp));
933
934   /* Execute any pending command callbacks */
935   SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_CMODE);
936
937  out:
938   silc_client_command_reply_free(cmd);
939 }
940
941 /* Received reply for CUMODE command */
942
943 SILC_CLIENT_CMD_REPLY_FUNC(cumode)
944 {
945   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
946   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
947   SilcCommandStatus status;
948   SilcIDCacheEntry id_cache = NULL;
949   SilcClientID *client_id;
950   unsigned char *tmp, *id;
951   unsigned int len;
952   
953   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
954   if (status != SILC_STATUS_OK) {
955     cmd->client->ops->say(cmd->client, conn,
956              "%s", silc_client_command_status_message(status));
957     COMMAND_REPLY_ERROR;
958     goto out;
959   }
960   
961   /* Get channel mode */
962   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
963   if (!tmp) {
964     COMMAND_REPLY_ERROR;
965     goto out;
966   }
967
968   /* Get Client ID */
969   id = silc_argument_get_arg_type(cmd->args, 3, &len);
970   if (!id) {
971     COMMAND_REPLY_ERROR;
972     goto out;
973   }
974   client_id = silc_id_payload_parse_id(id, len);
975   if (!client_id) {
976     COMMAND_REPLY_ERROR;
977     goto out;
978   }
979   
980   /* Get client entry */
981   if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
982                                    SILC_ID_CLIENT, &id_cache)) {
983     COMMAND_REPLY_ERROR;
984     goto out;
985   }
986
987   /* Notify application */
988   COMMAND_REPLY((ARGS, tmp, (SilcClientEntry)id_cache->context));
989   silc_free(client_id);
990   
991   /* Execute any pending command callbacks */
992   SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_CUMODE);
993
994  out:
995   silc_client_command_reply_free(cmd);
996 }
997
998 SILC_CLIENT_CMD_REPLY_FUNC(kick)
999 {
1000 }
1001
1002 SILC_CLIENT_CMD_REPLY_FUNC(restart)
1003 {
1004 }
1005  
1006 SILC_CLIENT_CMD_REPLY_FUNC(close)
1007 {
1008 }
1009  
1010 SILC_CLIENT_CMD_REPLY_FUNC(die)
1011 {
1012 }
1013  
1014 SILC_CLIENT_CMD_REPLY_FUNC(silcoper)
1015 {
1016 }
1017
1018 /* Reply to LEAVE command. */
1019
1020 SILC_CLIENT_CMD_REPLY_FUNC(leave)
1021 {
1022   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1023   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1024   SilcCommandStatus status;
1025   unsigned char *tmp;
1026
1027   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1028   SILC_GET16_MSB(status, tmp);
1029   if (status != SILC_STATUS_OK) {
1030     cmd->client->ops->say(cmd->client, conn,
1031              "%s", silc_client_command_status_message(status));
1032     COMMAND_REPLY_ERROR;
1033     return;
1034   }
1035
1036   /* Notify application */
1037   COMMAND_REPLY((ARGS));
1038
1039   /* Execute any pending command callbacks */
1040   SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_LEAVE);
1041
1042   silc_client_command_reply_free(cmd);
1043 }
1044
1045 /* Reply to USERS command. Received list of client ID's and theirs modes
1046    on the channel we requested. */
1047
1048 SILC_CLIENT_CMD_REPLY_FUNC(users)
1049 {
1050   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1051   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1052   SilcCommandStatus status;
1053   SilcIDCacheEntry id_cache = NULL;
1054   SilcChannelEntry channel;
1055   SilcChannelUser chu;
1056   SilcChannelID *channel_id = NULL;
1057   SilcBuffer client_id_list;
1058   SilcBuffer client_mode_list;
1059   unsigned char *tmp;
1060   unsigned int tmp_len, list_count;
1061   int i;
1062   unsigned char **res_argv = NULL;
1063   unsigned int *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
1064
1065   SILC_LOG_DEBUG(("Start"));
1066
1067   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1068   SILC_GET16_MSB(status, tmp);
1069   if (status != SILC_STATUS_OK) {
1070     cmd->client->ops->say(cmd->client, conn,
1071              "%s", silc_client_command_status_message(status));
1072     COMMAND_REPLY_ERROR;
1073     goto out;
1074   }
1075
1076   /* Get channel ID */
1077   tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
1078   if (!tmp)
1079     goto out;
1080   channel_id = silc_id_payload_parse_id(tmp, tmp_len);
1081   if (!channel_id)
1082     goto out;
1083
1084   /* Get the list count */
1085   tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
1086   if (!tmp)
1087     goto out;
1088   SILC_GET32_MSB(list_count, tmp);
1089
1090   /* Get Client ID list */
1091   tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
1092   if (!tmp)
1093     goto out;
1094
1095   client_id_list = silc_buffer_alloc(tmp_len);
1096   silc_buffer_pull_tail(client_id_list, tmp_len);
1097   silc_buffer_put(client_id_list, tmp, tmp_len);
1098
1099   /* Get client mode list */
1100   tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
1101   if (!tmp)
1102     goto out;
1103
1104   client_mode_list = silc_buffer_alloc(tmp_len);
1105   silc_buffer_pull_tail(client_mode_list, tmp_len);
1106   silc_buffer_put(client_mode_list, tmp, tmp_len);
1107
1108   /* Get channel entry */
1109   if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
1110                                    SILC_ID_CHANNEL, &id_cache)) {
1111     COMMAND_REPLY_ERROR;
1112     goto out;
1113   }
1114   channel = (SilcChannelEntry)id_cache->context;
1115
1116   /* Remove old client list from channel. */
1117   silc_list_start(channel->clients);
1118   while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1119     silc_list_del(channel->clients, chu);
1120     silc_free(chu);
1121   }
1122
1123   /* Cache the received Client ID's and modes. This cache expires
1124      whenever server sends notify message to channel. It means two things;
1125      some user has joined or leaved the channel. XXX! */
1126   for (i = 0; i < list_count; i++) {
1127     unsigned short idp_len;
1128     unsigned int mode;
1129     SilcClientID *client_id;
1130     SilcClientEntry client;
1131
1132     /* Client ID */
1133     SILC_GET16_MSB(idp_len, client_id_list->data + 2);
1134     idp_len += 4;
1135     client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
1136     if (!client_id)
1137       continue;
1138
1139     /* Mode */
1140     SILC_GET32_MSB(mode, client_mode_list->data);
1141
1142     /* Check if we have this client cached already. */
1143     if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
1144                                      SILC_ID_CLIENT, &id_cache)) {
1145       /* No we don't have it, query it from the server. Assemble argument
1146          table that will be sent fr the IDENTIFY command later. */
1147       res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
1148                               (res_argc + 1));
1149       res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
1150                                    (res_argc + 1));
1151       res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
1152                                     (res_argc + 1));
1153       res_argv[res_argc] = client_id_list->data;
1154       res_argv_lens[res_argc] = idp_len;
1155       res_argv_types[res_argc] = res_argc + 3;
1156       res_argc++;
1157     } else {
1158       /* Found the client, join it to the channel */
1159       client = (SilcClientEntry)id_cache->context;
1160       chu = silc_calloc(1, sizeof(*chu));
1161       chu->client = client;
1162       chu->mode = mode;
1163       silc_list_add(channel->clients, chu);
1164
1165       silc_free(client_id);
1166       id_cache = NULL;
1167     }
1168
1169     silc_buffer_pull(client_id_list, idp_len);
1170     silc_buffer_pull(client_mode_list, 4);
1171   }
1172
1173   /* Query the client information from server if the list included clients
1174      that we don't know about. */
1175   if (res_argc) {
1176     SilcBuffer res_cmd;
1177
1178     /* Send the IDENTIFY command to server */
1179     res_cmd = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
1180                                           res_argc, res_argv, res_argv_lens,
1181                                           res_argv_types, ++conn->cmd_ident);
1182     silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, 
1183                             NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
1184                             TRUE);
1185
1186     /* Register pending command callback. After we've received the IDENTIFY
1187        command reply we will reprocess this command reply by re-calling this
1188        USERS command reply callback. */
1189     silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
1190                                 silc_client_command_reply_users, cmd);
1191
1192     silc_buffer_free(res_cmd);
1193     if (channel_id)
1194       silc_free(channel_id);
1195
1196     silc_free(res_argv);
1197     silc_free(res_argv_lens);
1198     silc_free(res_argv_types);
1199     return;
1200   }
1201
1202   /* We have all the clients on the channel cached now. Create a nice
1203      output for user interface and notify application. */
1204
1205   if (!cmd->callback) {
1206     /* Server has sent us USERS reply even when we haven't actually sent
1207        USERS command. This is normal behaviour when joining to a channel.
1208        Display some nice information on the user interface. */
1209     int k = 0, len1 = 0, len2 = 0;
1210     char *name_list = NULL;
1211
1212     silc_list_start(channel->clients);
1213     while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1214       char *m, *n = chu->client->nickname;
1215       len2 = strlen(n);
1216       len1 += len2;
1217
1218       name_list = silc_realloc(name_list, sizeof(*name_list) * (len1 + 3));
1219
1220       m = silc_client_chumode_char(chu->mode);
1221       if (m) {
1222         memcpy(name_list + (len1 - len2), m, strlen(m));
1223         len1 += strlen(m);
1224         silc_free(m);
1225       }
1226
1227       memcpy(name_list + (len1 - len2), n, len2);
1228       name_list[len1] = 0;
1229       
1230       if (k == silc_list_count(channel->clients) - 1)
1231         break;
1232       memcpy(name_list + len1, " ", 1);
1233       len1++;
1234       k++;
1235     }
1236
1237     cmd->client->ops->say(cmd->client, conn, "Users on %s: %s",
1238                           channel->channel_name, name_list);
1239     silc_free(name_list);
1240   }
1241
1242   /* Notify application */
1243   COMMAND_REPLY((ARGS, channel, client_id_list->head,
1244                  client_mode_list->head));
1245
1246   /* Execute any pending command callbacks */
1247   SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_USERS);
1248
1249   silc_buffer_free(client_id_list);
1250   silc_buffer_free(client_mode_list);
1251
1252  out:
1253   if (channel_id)
1254     silc_free(channel_id);
1255   silc_client_command_reply_free(cmd);
1256 }