bug fix.
[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   
233   nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
234   if (nickname) {
235     strncat(buf, nickname, len);
236     strncat(buf, " is ", 4);
237   }
238   
239   username = silc_argument_get_arg_type(cmd->args, 4, &len);
240   if (username) {
241     strncat(buf, username, len);
242   }
243   
244   realname = silc_argument_get_arg_type(cmd->args, 5, &len);
245   if (realname) {
246     strncat(buf, " (", 2);
247     strncat(buf, realname, len);
248     strncat(buf, ")", 1);
249   }
250   
251   /* Check if we have this client cached already. */
252   if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
253                                    SILC_ID_CLIENT, &id_cache)) {
254     client_entry = silc_calloc(1, sizeof(*client_entry));
255     client_entry->id = client_id;
256     silc_parse_nickname(nickname, &client_entry->nickname, 
257                         &client_entry->server, &client_entry->num);
258     client_entry->username = strdup(username);
259     if (realname)
260       client_entry->realname = strdup(realname);
261     
262     /* Add client to cache */
263     silc_idcache_add(conn->client_cache, client_entry->nickname,
264                      SILC_ID_CLIENT, client_id, (void *)client_entry, TRUE);
265   } else {
266     client_entry = (SilcClientEntry)id_cache->context;
267     if (client_entry->nickname)
268       silc_free(client_entry->nickname);
269     if (client_entry->server)
270       silc_free(client_entry->server);
271     if (client_entry->username)
272       silc_free(client_entry->username);
273     if (client_entry->realname)
274       silc_free(client_entry->realname);
275
276     silc_parse_nickname(nickname, &client_entry->nickname, 
277                         &client_entry->server, &client_entry->num);
278     client_entry->username = strdup(username);
279     if (realname)
280       client_entry->realname = strdup(realname);
281
282     id_cache->data = client_entry->nickname;
283
284     silc_free(client_id);
285   }
286
287   if (!cmd->callback)
288     cmd->client->ops->say(cmd->client, conn, "%s", buf);
289
290   /* Notify application */
291   COMMAND_REPLY((ARGS, client_entry, nickname, username, realname, 
292                  NULL, NULL));
293 }
294
295 /* Received reply for WHOIS command. This maybe called several times
296    for one WHOIS command as server may reply with list of results. */
297
298 SILC_CLIENT_CMD_REPLY_FUNC(whois)
299 {
300   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
301   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
302   SilcCommandStatus status;
303   unsigned char *tmp;
304
305   SILC_LOG_DEBUG(("Start"));
306
307   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
308   SILC_GET16_MSB(status, tmp);
309   if (status != SILC_STATUS_OK && 
310       status != SILC_STATUS_LIST_START &&
311       status != SILC_STATUS_LIST_ITEM &&
312       status != SILC_STATUS_LIST_END) {
313     if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
314       /* Take nickname which may be provided */
315       tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
316       if (tmp)
317         cmd->client->ops->say(cmd->client, conn, "%s: %s", tmp,
318                  silc_client_command_status_message(status));
319       else
320         cmd->client->ops->say(cmd->client, conn, "%s",
321                  silc_client_command_status_message(status));
322       COMMAND_REPLY_ERROR;
323       goto out;
324     } else {
325       cmd->client->ops->say(cmd->client, conn,
326                "%s", silc_client_command_status_message(status));
327       COMMAND_REPLY_ERROR;
328       goto out;
329     }
330   }
331
332   /* Display one whois reply */
333   if (status == SILC_STATUS_OK) {
334     silc_client_command_reply_whois_print(cmd, status);
335   }
336
337   /* XXX list should not be displayed untill all items has been received. */
338   if (status == SILC_STATUS_LIST_START) {
339     silc_client_command_reply_whois_print(cmd, status);
340   }
341
342   if (status == SILC_STATUS_LIST_ITEM) {
343     silc_client_command_reply_whois_print(cmd, status);
344   }
345
346   if (status == SILC_STATUS_LIST_END) {
347     silc_client_command_reply_whois_print(cmd, status);
348   }
349
350   /* Execute any pending command callbacks */
351   SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_WHOIS);
352
353  out:
354   silc_client_command_reply_free(cmd);
355 }
356
357 /* Received reply for WHOWAS command. */
358
359 SILC_CLIENT_CMD_REPLY_FUNC(whowas)
360 {
361
362 }
363
364 /* Received reply for IDENTIFY command. This maybe called several times
365    for one IDENTIFY command as server may reply with list of results. 
366    This is totally silent and does not print anything on screen. */
367
368 SILC_CLIENT_CMD_REPLY_FUNC(identify)
369 {
370   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
371   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
372   SilcClientEntry client_entry;
373   SilcIDCacheEntry id_cache = NULL;
374   SilcCommandStatus status;
375   unsigned char *tmp;
376
377   SILC_LOG_DEBUG(("Start"));
378
379   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
380   SILC_GET16_MSB(status, tmp);
381   if (status != SILC_STATUS_OK) {
382     if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
383       /* Take nickname which may be provided */
384       tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
385       if (tmp)
386         cmd->client->ops->say(cmd->client, conn, "%s: %s", tmp,
387                  silc_client_command_status_message(status));
388       else
389         cmd->client->ops->say(cmd->client, conn, "%s",
390                  silc_client_command_status_message(status));
391       COMMAND_REPLY_ERROR;
392       goto out;
393     } else {
394       cmd->client->ops->say(cmd->client, conn,
395                "%s", silc_client_command_status_message(status));
396       COMMAND_REPLY_ERROR;
397       goto out;
398     }
399   }
400
401   /* Display one whois reply */
402   if (status == SILC_STATUS_OK) {
403     unsigned int len;
404     unsigned char *id_data;
405     char *nickname;
406     char *username;
407     SilcClientID *client_id;
408
409     id_data = silc_argument_get_arg_type(cmd->args, 2, &len);
410     if (!id_data)
411       goto out;
412     client_id = silc_id_payload_parse_id(id_data, len);
413
414     nickname = silc_argument_get_arg_type(cmd->args, 3, NULL);
415     username = silc_argument_get_arg_type(cmd->args, 4, NULL);
416
417     if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
418                                      SILC_ID_CLIENT, &id_cache)) {
419       client_entry = silc_calloc(1, sizeof(*client_entry));
420       client_entry->id = client_id;
421       silc_parse_nickname(nickname, &client_entry->nickname, 
422                           &client_entry->server, &client_entry->num);
423       if (username)
424         client_entry->username = strdup(username);
425     
426       /* Add client to cache */
427       silc_idcache_add(conn->client_cache, client_entry->nickname,
428                        SILC_ID_CLIENT, client_id, (void *)client_entry, TRUE);
429     } else {
430       client_entry = (SilcClientEntry)id_cache->context;
431       if (client_entry->nickname)
432         silc_free(client_entry->nickname);
433       if (client_entry->server)
434         silc_free(client_entry->server);
435       if (username && client_entry->username)
436         silc_free(client_entry->username);
437
438       silc_parse_nickname(nickname, &client_entry->nickname, 
439                           &client_entry->server, &client_entry->num);
440
441       if (username)
442         client_entry->username = strdup(username);
443
444       id_cache->data = client_entry->nickname;
445
446       silc_free(client_id);
447     }
448   }
449
450   if (status == SILC_STATUS_LIST_START) {
451
452   }
453
454   if (status == SILC_STATUS_LIST_END) {
455
456   }
457
458   /* Execute any pending command callbacks */
459   SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_IDENTIFY);
460
461  out:
462   silc_client_command_reply_free(cmd);
463 }
464
465 /* Received reply for command NICK. If everything went without errors
466    we just received our new Client ID. */
467
468 SILC_CLIENT_CMD_REPLY_FUNC(nick)
469 {
470   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
471   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
472   SilcCommandStatus status;
473   SilcIDPayload idp;
474   unsigned char *tmp;
475   unsigned int argc, len;
476
477   SILC_LOG_DEBUG(("Start"));
478
479   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
480   if (status != SILC_STATUS_OK) {
481     cmd->client->ops->say(cmd->client, conn, "Cannot set nickname: %s", 
482              silc_client_command_status_message(status));
483     COMMAND_REPLY_ERROR;
484     goto out;
485   }
486
487   argc = silc_argument_get_arg_num(cmd->args);
488   if (argc < 2 || argc > 2) {
489     cmd->client->ops->say(cmd->client, conn, 
490                           "Cannot set nickname: bad reply to command");
491     COMMAND_REPLY_ERROR;
492     goto out;
493   }
494
495   /* Take received Client ID */
496   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
497   idp = silc_id_payload_parse_data(tmp, len);
498   silc_client_receive_new_id(cmd->client, cmd->sock, idp);
499     
500   /* Notify application */
501   COMMAND_REPLY((ARGS, conn->local_entry));
502
503   /* Execute any pending command callbacks */
504   SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_NICK);
505
506  out:
507   silc_client_command_reply_free(cmd);
508 }
509
510 SILC_CLIENT_CMD_REPLY_FUNC(list)
511 {
512 }
513
514 /* Received reply to topic command. */
515
516 SILC_CLIENT_CMD_REPLY_FUNC(topic)
517 {
518   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
519   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
520   SilcCommandStatus status;
521   SilcChannelEntry channel;
522   SilcChannelID *channel_id = NULL;
523   SilcIDCacheEntry id_cache = NULL;
524   unsigned char *tmp;
525   char *topic;
526   unsigned int argc, len;
527
528   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
529   if (status != SILC_STATUS_OK) {
530     cmd->client->ops->say(cmd->client, conn,
531              "%s", silc_client_command_status_message(status));
532     COMMAND_REPLY_ERROR;
533     silc_client_command_reply_free(cmd);
534     return;
535   }
536
537   argc = silc_argument_get_arg_num(cmd->args);
538   if (argc < 1 || argc > 3) {
539     COMMAND_REPLY_ERROR;
540     goto out;
541   }
542
543   /* Take Channel ID */
544   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
545   if (!tmp)
546     goto out;
547
548   /* Take topic */
549   topic = silc_argument_get_arg_type(cmd->args, 3, NULL);
550   if (!topic)
551     goto out;
552
553   channel_id = silc_id_payload_parse_id(tmp, len);
554
555   /* Get the channel name */
556   if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
557                                    SILC_ID_CHANNEL, &id_cache)) {
558     silc_free(channel_id);
559     COMMAND_REPLY_ERROR;
560     goto out;
561   }
562   
563   channel = (SilcChannelEntry)id_cache->context;
564
565   cmd->client->ops->say(cmd->client, conn, 
566                         "Topic on channel %s: %s", channel->channel_name,
567                         topic);
568
569   /* Notify application */
570   COMMAND_REPLY((ARGS, channel, topic));
571
572   /* Execute any pending command callbacks */
573   SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_TOPIC);
574
575  out:
576   silc_client_command_reply_free(cmd);
577 }
578
579 /* Received reply to invite command. */
580
581 SILC_CLIENT_CMD_REPLY_FUNC(invite)
582 {
583   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
584   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
585   SilcCommandStatus status;
586   unsigned char *tmp;
587
588   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
589   SILC_GET16_MSB(status, tmp);
590   if (status != SILC_STATUS_OK) {
591     cmd->client->ops->say(cmd->client, conn,
592              "%s", silc_client_command_status_message(status));
593     COMMAND_REPLY_ERROR;
594     silc_client_command_reply_free(cmd);
595     return;
596   }
597
598   /* Notify application */
599   COMMAND_REPLY((ARGS));
600
601   /* Execute any pending command callbacks */
602   SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_INVITE);
603
604   silc_client_command_reply_free(cmd);
605 }
606  
607 SILC_CLIENT_CMD_REPLY_FUNC(quit)
608 {
609 }
610
611 SILC_CLIENT_CMD_REPLY_FUNC(kill)
612 {
613 }
614
615 /* Received reply to INFO command. We receive the server ID and some
616    information about the server user requested. */
617
618 SILC_CLIENT_CMD_REPLY_FUNC(info)
619 {
620   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
621   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
622   SilcClient client = cmd->client;
623   SilcCommandStatus status;
624   unsigned char *tmp;
625
626   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
627   SILC_GET16_MSB(status, tmp);
628   if (status != SILC_STATUS_OK) {
629     cmd->client->ops->say(cmd->client, conn,
630              "%s", silc_client_command_status_message(status));
631     COMMAND_REPLY_ERROR;
632     silc_client_command_reply_free(cmd);
633     return;
634   }
635
636   /* Get server ID */
637   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
638   if (!tmp)
639     goto out;
640
641   /* XXX save server id */
642
643   /* Get server info */
644   tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
645   if (!tmp)
646     goto out;
647
648   client->ops->say(cmd->client, conn, "Info: %s", tmp);
649
650   /* Notify application */
651   COMMAND_REPLY((ARGS, NULL, (char *)tmp));
652
653   /* Execute any pending command callbacks */
654   SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_INFO);
655
656  out:
657   silc_client_command_reply_free(cmd);
658 }
659
660 SILC_CLIENT_CMD_REPLY_FUNC(connect)
661 {
662 }
663
664 /* Received reply to PING command. The reply time is shown to user. */
665
666 SILC_CLIENT_CMD_REPLY_FUNC(ping)
667 {
668   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
669   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
670   SilcCommandStatus status;
671   void *id;
672   int i;
673   time_t diff, curtime;
674
675   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
676   if (status != SILC_STATUS_OK) {
677     cmd->client->ops->say(cmd->client, conn,
678              "%s", silc_client_command_status_message(status));
679     COMMAND_REPLY_ERROR;
680     goto out;
681   }
682
683   curtime = time(NULL);
684   id = silc_id_str2id(cmd->packet->src_id, cmd->packet->src_id_type);
685
686   for (i = 0; i < conn->ping_count; i++) {
687     if (!SILC_ID_SERVER_COMPARE(conn->ping[i].dest_id, id)) {
688       diff = curtime - conn->ping[i].start_time;
689       cmd->client->ops->say(cmd->client, conn, 
690                             "Ping reply from %s: %d second%s", 
691                             conn->ping[i].dest_name, diff, 
692                             diff == 1 ? "" : "s");
693
694       conn->ping[i].start_time = 0;
695       silc_free(conn->ping[i].dest_id);
696       conn->ping[i].dest_id = NULL;
697       silc_free(conn->ping[i].dest_name);
698       conn->ping[i].dest_name = NULL;
699
700       /* Notify application */
701       COMMAND_REPLY((ARGS));
702       break;
703     }
704   }
705
706   silc_free(id);
707
708   /* Execute any pending command callbacks */
709   SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_PING);
710
711  out:
712   silc_client_command_reply_free(cmd);
713 }
714
715 SILC_CLIENT_CMD_REPLY_FUNC(oper)
716 {
717 }
718
719 /* Received reply for JOIN command. */
720
721 SILC_CLIENT_CMD_REPLY_FUNC(join)
722 {
723   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
724   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
725   SilcClient client = cmd->client;
726   SilcCommandStatus status;
727   SilcIDPayload idp = NULL;
728   unsigned int argc, mode, len;
729   char *topic, *tmp, *channel_name = NULL;
730   SilcBuffer keyp;
731
732   SILC_LOG_DEBUG(("Start"));
733
734   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
735   if (status != SILC_STATUS_OK) {
736     cmd->client->ops->say(cmd->client, conn,
737              "%s", silc_client_command_status_message(status));
738     COMMAND_REPLY_ERROR;
739     goto out;
740   }
741
742   argc = silc_argument_get_arg_num(cmd->args);
743   if (argc < 3 || argc > 9) {
744     cmd->client->ops->say(cmd->client, conn,
745              "Cannot join channel: Bad reply packet");
746     COMMAND_REPLY_ERROR;
747     goto out;
748   }
749
750   /* Get channel name */
751   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
752   if (!tmp) {
753     cmd->client->ops->say(cmd->client, conn, 
754                           "Cannot join channel: Bad reply packet");
755     COMMAND_REPLY_ERROR;
756     goto out;
757   }
758   channel_name = strdup(tmp);
759
760   /* Get Channel ID */
761   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
762   if (!tmp) {
763     cmd->client->ops->say(cmd->client, conn, 
764                           "Cannot join channel: Bad reply packet");
765     COMMAND_REPLY_ERROR;
766     silc_free(channel_name);
767     goto out;
768   }
769   idp = silc_id_payload_parse_data(tmp, len);
770
771   /* Get channel mode */
772   tmp = silc_argument_get_arg_type(cmd->args, 4, NULL);
773   if (tmp)
774     SILC_GET32_MSB(mode, tmp);
775   else
776     mode = 0;
777
778   /* Get channel key */
779   tmp = silc_argument_get_arg_type(cmd->args, 6, &len);
780   if (!tmp) {
781     silc_id_payload_free(idp);
782     silc_free(channel_name);
783     goto out;
784   }
785   keyp = silc_buffer_alloc(len);
786   silc_buffer_pull_tail(keyp, SILC_BUFFER_END(keyp));
787   silc_buffer_put(keyp, tmp, len);
788
789   /* Get topic */
790   topic = silc_argument_get_arg_type(cmd->args, 8, NULL);
791
792   /* Save received Channel ID */
793   silc_client_new_channel_id(cmd->client, cmd->sock, channel_name, 
794                              mode, idp);
795   silc_id_payload_free(idp);
796
797   /* Save channel key */
798   silc_client_save_channel_key(conn, keyp, conn->current_channel);
799   silc_buffer_free(keyp);
800
801   if (topic)
802     client->ops->say(cmd->client, conn, 
803                      "Topic for %s: %s", channel_name, topic);
804
805   /* Notify application */
806   COMMAND_REPLY((ARGS, channel_name, conn->current_channel, mode,
807                  NULL, NULL, topic));
808
809   /* Execute any pending command callbacks */
810   SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_JOIN);
811
812  out:
813   silc_client_command_reply_free(cmd);
814 }
815
816 /* Received reply for MOTD command */
817
818 SILC_CLIENT_CMD_REPLY_FUNC(motd)
819 {
820   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
821   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
822   SilcCommandStatus status;
823   unsigned int argc, i;
824   unsigned char *tmp;
825   char *motd = NULL, *cp, line[256];
826
827   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
828   SILC_GET16_MSB(status, tmp);
829   if (status != SILC_STATUS_OK) {
830     cmd->client->ops->say(cmd->client, conn,
831              "%s", silc_client_command_status_message(status));
832     COMMAND_REPLY_ERROR;
833     return;
834   }
835
836   argc = silc_argument_get_arg_num(cmd->args);
837   if (argc > 2) {
838     COMMAND_REPLY_ERROR;
839     goto out;
840   }
841
842   if (argc == 2) {
843     motd = silc_argument_get_arg_type(cmd->args, 2, NULL);
844     if (!motd) {
845       COMMAND_REPLY_ERROR;
846       goto out;
847     }
848
849     i = 0;
850     cp = motd;
851     while(cp[i] != 0) {
852       if (cp[i++] == '\n') {
853         memset(line, 0, sizeof(line));
854         strncat(line, cp, i - 1);
855         cp += i;
856         
857         if (i == 2)
858           line[0] = ' ';
859         
860         cmd->client->ops->say(cmd->client, conn, "%s", line);
861         
862         if (!strlen(cp))
863           break;
864         i = 0;
865       }
866     }
867   }
868
869   /* Notify application */
870   COMMAND_REPLY((ARGS, motd));
871
872   /* Execute any pending command callbacks */
873   SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_MOTD);
874
875  out:
876   silc_client_command_reply_free(cmd);
877 }
878
879 SILC_CLIENT_CMD_REPLY_FUNC(umode)
880 {
881 }
882
883 /* Received reply for CMODE command. */
884
885 SILC_CLIENT_CMD_REPLY_FUNC(cmode)
886 {
887   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
888   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
889   SilcCommandStatus status;
890   unsigned char *tmp;
891
892   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
893   if (status != SILC_STATUS_OK) {
894     cmd->client->ops->say(cmd->client, conn,
895              "%s", silc_client_command_status_message(status));
896     COMMAND_REPLY_ERROR;
897     goto out;
898   }
899
900   /* Get channel mode */
901   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
902   if (!tmp) {
903     COMMAND_REPLY_ERROR;
904     goto out;
905   }
906
907   /* Notify application */
908   COMMAND_REPLY((ARGS, tmp));
909
910   /* Execute any pending command callbacks */
911   SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_CMODE);
912
913  out:
914   silc_client_command_reply_free(cmd);
915 }
916
917 /* Received reply for CUMODE command */
918
919 SILC_CLIENT_CMD_REPLY_FUNC(cumode)
920 {
921   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
922   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
923   SilcCommandStatus status;
924   SilcIDCacheEntry id_cache = NULL;
925   SilcClientID *client_id;
926   unsigned char *tmp, *id;
927   unsigned int len;
928   
929   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
930   if (status != SILC_STATUS_OK) {
931     cmd->client->ops->say(cmd->client, conn,
932              "%s", silc_client_command_status_message(status));
933     COMMAND_REPLY_ERROR;
934     goto out;
935   }
936   
937   /* Get channel mode */
938   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
939   if (!tmp) {
940     COMMAND_REPLY_ERROR;
941     goto out;
942   }
943
944   /* Get Client ID */
945   id = silc_argument_get_arg_type(cmd->args, 3, &len);
946   if (!id) {
947     COMMAND_REPLY_ERROR;
948     goto out;
949   }
950   client_id = silc_id_payload_parse_id(id, len);
951   
952   /* Get client entry */
953   if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
954                                    SILC_ID_CLIENT, &id_cache)) {
955     COMMAND_REPLY_ERROR;
956     goto out;
957   }
958
959   /* Notify application */
960   COMMAND_REPLY((ARGS, tmp, (SilcClientEntry)id_cache->context));
961   silc_free(client_id);
962   
963   /* Execute any pending command callbacks */
964   SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_CUMODE);
965
966  out:
967   silc_client_command_reply_free(cmd);
968 }
969
970 SILC_CLIENT_CMD_REPLY_FUNC(kick)
971 {
972 }
973
974 SILC_CLIENT_CMD_REPLY_FUNC(restart)
975 {
976 }
977  
978 SILC_CLIENT_CMD_REPLY_FUNC(close)
979 {
980 }
981  
982 SILC_CLIENT_CMD_REPLY_FUNC(die)
983 {
984 }
985  
986 SILC_CLIENT_CMD_REPLY_FUNC(silcoper)
987 {
988 }
989
990 /* Reply to LEAVE command. */
991
992 SILC_CLIENT_CMD_REPLY_FUNC(leave)
993 {
994   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
995   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
996   SilcCommandStatus status;
997   unsigned char *tmp;
998
999   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1000   SILC_GET16_MSB(status, tmp);
1001   if (status != SILC_STATUS_OK) {
1002     cmd->client->ops->say(cmd->client, conn,
1003              "%s", silc_client_command_status_message(status));
1004     COMMAND_REPLY_ERROR;
1005     return;
1006   }
1007
1008   /* Notify application */
1009   COMMAND_REPLY((ARGS));
1010
1011   /* Execute any pending command callbacks */
1012   SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_LEAVE);
1013
1014   silc_client_command_reply_free(cmd);
1015 }
1016
1017 /* Reply to USERS command. Received list of client ID's and theirs modes
1018    on the channel we requested. */
1019
1020 SILC_CLIENT_CMD_REPLY_FUNC(users)
1021 {
1022   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1023   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1024   SilcCommandStatus status;
1025   SilcIDCacheEntry id_cache = NULL;
1026   SilcChannelEntry channel;
1027   SilcChannelUser chu;
1028   SilcChannelID *channel_id = NULL;
1029   SilcBuffer client_id_list;
1030   SilcBuffer client_mode_list;
1031   unsigned char *tmp;
1032   unsigned int tmp_len, list_count;
1033   int i;
1034   unsigned char **res_argv = NULL;
1035   unsigned int *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
1036
1037   SILC_LOG_DEBUG(("Start"));
1038
1039   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1040   SILC_GET16_MSB(status, tmp);
1041   if (status != SILC_STATUS_OK) {
1042     cmd->client->ops->say(cmd->client, conn,
1043              "%s", silc_client_command_status_message(status));
1044     COMMAND_REPLY_ERROR;
1045     goto out;
1046   }
1047
1048   /* Get channel ID */
1049   tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
1050   if (!tmp)
1051     goto out;
1052   channel_id = silc_id_payload_parse_id(tmp, tmp_len);
1053
1054   /* Get the list count */
1055   tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
1056   if (!tmp)
1057     goto out;
1058   SILC_GET32_MSB(list_count, tmp);
1059
1060   /* Get Client ID list */
1061   tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
1062   if (!tmp)
1063     goto out;
1064
1065   client_id_list = silc_buffer_alloc(tmp_len);
1066   silc_buffer_pull_tail(client_id_list, tmp_len);
1067   silc_buffer_put(client_id_list, tmp, tmp_len);
1068
1069   /* Get client mode list */
1070   tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
1071   if (!tmp)
1072     goto out;
1073
1074   client_mode_list = silc_buffer_alloc(tmp_len);
1075   silc_buffer_pull_tail(client_mode_list, tmp_len);
1076   silc_buffer_put(client_mode_list, tmp, tmp_len);
1077
1078   /* Get channel entry */
1079   if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
1080                                    SILC_ID_CHANNEL, &id_cache)) {
1081     COMMAND_REPLY_ERROR;
1082     goto out;
1083   }
1084   channel = (SilcChannelEntry)id_cache->context;
1085
1086   /* Remove old client list from channel. */
1087   silc_list_start(channel->clients);
1088   while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1089     silc_list_del(channel->clients, chu);
1090     silc_free(chu);
1091   }
1092
1093   /* Cache the received Client ID's and modes. This cache expires
1094      whenever server sends notify message to channel. It means two things;
1095      some user has joined or leaved the channel. XXX! */
1096   for (i = 0; i < list_count; i++) {
1097     unsigned short idp_len;
1098     unsigned int mode;
1099     SilcClientID *client_id;
1100     SilcClientEntry client;
1101
1102     /* Client ID */
1103     SILC_GET16_MSB(idp_len, client_id_list->data + 2);
1104     idp_len += 4;
1105     client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
1106
1107     /* Mode */
1108     SILC_GET32_MSB(mode, client_mode_list->data);
1109
1110     /* Check if we have this client cached already. */
1111     if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
1112                                      SILC_ID_CLIENT, &id_cache)) {
1113       /* No we don't have it, query it from the server. Assemble argument
1114          table that will be sent fr the IDENTIFY command later. */
1115       res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
1116                               (res_argc + 1));
1117       res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
1118                                    (res_argc + 1));
1119       res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
1120                                     (res_argc + 1));
1121       res_argv[res_argc] = client_id_list->data;
1122       res_argv_lens[res_argc] = idp_len;
1123       res_argv_types[res_argc] = res_argc + 3;
1124       res_argc++;
1125     } else {
1126       /* Found the client, join it to the channel */
1127       client = (SilcClientEntry)id_cache->context;
1128       chu = silc_calloc(1, sizeof(*chu));
1129       chu->client = client;
1130       chu->mode = mode;
1131       silc_list_add(channel->clients, chu);
1132
1133       silc_free(client_id);
1134       id_cache = NULL;
1135     }
1136
1137     silc_buffer_pull(client_id_list, idp_len);
1138     silc_buffer_pull(client_mode_list, 4);
1139   }
1140
1141   /* Query the client information from server if the list included clients
1142      that we don't know about. */
1143   if (res_argc) {
1144     SilcBuffer res_cmd;
1145
1146     /* Send the IDENTIFY command to server */
1147     res_cmd = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
1148                                           res_argc, res_argv, res_argv_lens,
1149                                           res_argv_types, ++conn->cmd_ident);
1150     silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, 
1151                             NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
1152                             TRUE);
1153
1154     /* Register pending command callback. After we've received the IDENTIFY
1155        command reply we will reprocess this command reply by re-calling this
1156        USERS command reply callback. */
1157     silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
1158                                 silc_client_command_reply_users, cmd);
1159
1160     silc_buffer_free(res_cmd);
1161     if (channel_id)
1162       silc_free(channel_id);
1163
1164     silc_free(res_argv);
1165     silc_free(res_argv_lens);
1166     silc_free(res_argv_types);
1167     return;
1168   }
1169
1170   /* We have all the clients on the channel cached now. Create a nice
1171      output for user interface and notify application. */
1172
1173   if (!cmd->callback) {
1174     /* Server has sent us USERS reply even when we haven't actually sent
1175        USERS command. This is normal behaviour when joining to a channel.
1176        Display some nice information on the user interface. */
1177     int k = 0, len1 = 0, len2 = 0;
1178     char *name_list = NULL;
1179
1180     silc_list_start(channel->clients);
1181     while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1182       char *m, *n = chu->client->nickname;
1183       len2 = strlen(n);
1184       len1 += len2;
1185
1186       name_list = silc_realloc(name_list, sizeof(*name_list) * (len1 + 3));
1187
1188       m = silc_client_chumode_char(chu->mode);
1189       if (m) {
1190         memcpy(name_list + (len1 - len2), m, strlen(m));
1191         len1 += strlen(m);
1192         silc_free(m);
1193       }
1194
1195       memcpy(name_list + (len1 - len2), n, len2);
1196       name_list[len1] = 0;
1197       
1198       if (k == silc_list_count(channel->clients) - 1)
1199         break;
1200       memcpy(name_list + len1, " ", 1);
1201       len1++;
1202       k++;
1203     }
1204
1205     cmd->client->ops->say(cmd->client, conn, "Users on %s: %s",
1206                           channel->channel_name, name_list);
1207     silc_free(name_list);
1208   }
1209
1210   /* Notify application */
1211   COMMAND_REPLY((ARGS, channel, client_id_list->head,
1212                  client_mode_list->head));
1213
1214   /* Execute any pending command callbacks */
1215   SILC_CLIENT_COMMAND_EXEC_PENDING(cmd, SILC_COMMAND_USERS);
1216
1217   silc_buffer_free(client_id_list);
1218   silc_buffer_free(client_mode_list);
1219
1220  out:
1221   if (channel_id)
1222     silc_free(channel_id);
1223   silc_client_command_reply_free(cmd);
1224 }