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 - 2001 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 #include "client_internal.h"
38
39 /* Client command reply list. */
40 SilcClientCommandReply silc_command_reply_list[] =
41 {
42   SILC_CLIENT_CMD_REPLY(whois, WHOIS),
43   SILC_CLIENT_CMD_REPLY(whowas, WHOWAS),
44   SILC_CLIENT_CMD_REPLY(identify, IDENTIFY),
45   SILC_CLIENT_CMD_REPLY(nick, NICK),
46   SILC_CLIENT_CMD_REPLY(list, LIST),
47   SILC_CLIENT_CMD_REPLY(topic, TOPIC),
48   SILC_CLIENT_CMD_REPLY(invite, INVITE),
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(shutdown, SHUTDOWN),
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_CHANNEL_FOPRIV),"Permission denied. You are not channel founder" },
111   { STAT(NO_SERVER_PRIV),  "Permission denied. You are not server operator" },
112   { STAT(NO_ROUTER_PRIV),  "Permission denied. You are not SILC operator" },
113   { STAT(BAD_NICKNAME),    "Bad nickname" },
114   { STAT(BAD_CHANNEL),     "Bad channel name" },
115   { STAT(AUTH_FAILED),     "Authentication failed" },
116   { STAT(UNKNOWN_ALGORITHM), "Unsupported algorithm" },
117
118   { 0, NULL }
119 };
120
121 /* Command reply operation that is called at the end of all command replys. 
122    Usage: COMMAND_REPLY((ARGS, argument1, argument2, etc...)), */
123 #define COMMAND_REPLY(args) cmd->client->ops->command_reply args
124 #define ARGS cmd->client, cmd->sock->user_data, \
125              cmd->payload, TRUE, silc_command_get(cmd->payload), status
126
127 /* Error reply to application. Usage: COMMAND_REPLY_ERROR; */
128 #define COMMAND_REPLY_ERROR cmd->client->ops->command_reply(cmd->client, \
129   cmd->sock->user_data, cmd->payload, FALSE, \
130   silc_command_get(cmd->payload), status)
131
132 /* Process received command reply. */
133
134 void silc_client_command_reply_process(SilcClient client,
135                                        SilcSocketConnection sock,
136                                        SilcPacketContext *packet)
137 {
138   SilcBuffer buffer = packet->buffer;
139   SilcClientCommandReply *cmd;
140   SilcClientCommandReplyContext ctx;
141   SilcCommandPayload payload;
142   SilcCommand command;
143   unsigned short ident;
144
145   /* Get command reply payload from packet */
146   payload = silc_command_payload_parse(buffer);
147   if (!payload) {
148     /* Silently ignore bad reply packet */
149     SILC_LOG_DEBUG(("Bad command reply packet"));
150     return;
151   }
152   
153   /* Allocate command reply context. This must be free'd by the
154      command reply routine receiving it. */
155   ctx = silc_calloc(1, sizeof(*ctx));
156   ctx->client = client;
157   ctx->sock = sock;
158   ctx->payload = payload;
159   ctx->args = silc_command_get_args(ctx->payload);
160   ctx->packet = packet;
161   ident = silc_command_get_ident(ctx->payload);
162       
163   /* Check for pending commands and mark to be exeucted */
164   silc_client_command_pending_check(sock->user_data, ctx, 
165                                     silc_command_get(ctx->payload), ident);
166
167   /* Execute command reply */
168   command = silc_command_get(ctx->payload);
169   for (cmd = silc_command_reply_list; cmd->cb; cmd++)
170     if (cmd->cmd == command)
171       break;
172
173   if (cmd == NULL || !cmd->cb) {
174     silc_free(ctx);
175     return;
176   }
177
178   cmd->cb(ctx);
179 }
180
181 /* Returns status message string */
182
183 static char *
184 silc_client_command_status_message(SilcCommandStatus status)
185 {
186   int i;
187
188   for (i = 0; silc_command_status_messages[i].message; i++) {
189     if (silc_command_status_messages[i].status == status)
190       break;
191   }
192
193   if (silc_command_status_messages[i].message == NULL)
194     return NULL;
195
196   return silc_command_status_messages[i].message;
197 }
198
199 /* Free command reply context and its internals. */
200
201 void silc_client_command_reply_free(SilcClientCommandReplyContext cmd)
202 {
203   if (cmd) {
204     silc_command_free_payload(cmd->payload);
205     silc_free(cmd);
206   }
207 }
208
209 static void 
210 silc_client_command_reply_whois_save(SilcClientCommandReplyContext cmd,
211                                      SilcCommandStatus status)
212 {
213   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
214   SilcClientID *client_id;
215   SilcIDCacheEntry id_cache = NULL;
216   SilcClientEntry client_entry = NULL;
217   int argc, len;
218   unsigned char *id_data, *tmp;
219   char *nickname = NULL, *username = NULL;
220   char *realname = NULL;
221   unsigned int idle = 0;
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   username = silc_argument_get_arg_type(cmd->args, 4, &len);
239   realname = silc_argument_get_arg_type(cmd->args, 5, &len);
240   if (!nickname || !username || !realname) {
241     COMMAND_REPLY_ERROR;
242     return;
243   }
244
245   tmp = silc_argument_get_arg_type(cmd->args, 7, &len);
246   if (tmp)
247     SILC_GET32_MSB(idle, tmp);
248
249   /* Check if we have this client cached already. */
250   if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
251                                    SILC_ID_CLIENT, &id_cache)) {
252     SILC_LOG_DEBUG(("Adding new client entry"));
253
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_LOG_DEBUG(("Updating client entry"));
277
278     silc_parse_nickname(nickname, &client_entry->nickname, 
279                         &client_entry->server, &client_entry->num);
280     client_entry->username = strdup(username);
281     if (realname)
282       client_entry->realname = strdup(realname);
283
284     id_cache->data = client_entry->nickname;
285     silc_idcache_sort_by_data(conn->client_cache);
286
287     silc_free(client_id);
288   }
289
290   /* Notify application */
291   COMMAND_REPLY((ARGS, client_entry, nickname, username, realname, 
292                  NULL, idle));
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_save(cmd, status);
335
336   /* List */
337   if (status == SILC_STATUS_LIST_START ||
338       status == SILC_STATUS_LIST_ITEM ||
339       status == SILC_STATUS_LIST_END)
340     silc_client_command_reply_whois_save(cmd, status);
341
342   /* Pending callbacks are not executed if this was an list entry */
343   if (status != SILC_STATUS_OK &&
344       status != SILC_STATUS_LIST_END) {
345     silc_client_command_reply_free(cmd);
346     return;
347   }
348
349   /* Execute any pending command callbacks */
350   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_WHOIS);
351
352  out:
353   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_WHOIS);
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 static void 
365 silc_client_command_reply_identify_save(SilcClientCommandReplyContext cmd,
366                                         SilcCommandStatus status)
367 {
368   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
369   SilcClientID *client_id;
370   SilcIDCacheEntry id_cache = NULL;
371   SilcClientEntry client_entry = NULL;
372   int argc, len;
373   unsigned char *id_data;
374   char *nickname = NULL, *username = NULL;
375   
376   argc = silc_argument_get_arg_num(cmd->args);
377
378   id_data = silc_argument_get_arg_type(cmd->args, 2, &len);
379   if (!id_data) {
380     COMMAND_REPLY_ERROR;
381     return;
382   }
383   
384   client_id = silc_id_payload_parse_id(id_data, len);
385   if (!client_id) {
386     COMMAND_REPLY_ERROR;
387     return;
388   }
389   
390   nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
391   username = silc_argument_get_arg_type(cmd->args, 4, &len);
392
393   /* Check if we have this client cached already. */
394   if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
395                                    SILC_ID_CLIENT, &id_cache)) {
396     SILC_LOG_DEBUG(("Adding new client entry"));
397
398     client_entry = silc_calloc(1, sizeof(*client_entry));
399     client_entry->id = client_id;
400     silc_parse_nickname(nickname, &client_entry->nickname, 
401                         &client_entry->server, &client_entry->num);
402     if (username)
403       client_entry->username = strdup(username);
404     
405     /* Add client to cache */
406     silc_idcache_add(conn->client_cache, client_entry->nickname,
407                      SILC_ID_CLIENT, client_id, (void *)client_entry, TRUE);
408   } else {
409     client_entry = (SilcClientEntry)id_cache->context;
410     if (client_entry->nickname)
411       silc_free(client_entry->nickname);
412     if (client_entry->server)
413       silc_free(client_entry->server);
414     if (username && client_entry->username)
415       silc_free(client_entry->username);
416     
417     SILC_LOG_DEBUG(("Updating client entry"));
418
419     silc_parse_nickname(nickname, &client_entry->nickname, 
420                         &client_entry->server, &client_entry->num);
421     
422     if (username)
423       client_entry->username = strdup(username);
424     
425     id_cache->data = client_entry->nickname;
426     silc_idcache_sort_by_data(conn->client_cache);
427     
428     silc_free(client_id);
429   }
430
431   /* Notify application */
432   COMMAND_REPLY((ARGS, client_entry, nickname, username));
433 }
434
435 /* Received reply for IDENTIFY command. This maybe called several times
436    for one IDENTIFY command as server may reply with list of results. 
437    This is totally silent and does not print anything on screen. */
438
439 SILC_CLIENT_CMD_REPLY_FUNC(identify)
440 {
441   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
442   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
443   SilcCommandStatus status;
444   unsigned char *tmp;
445
446   SILC_LOG_DEBUG(("Start"));
447
448   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
449   SILC_GET16_MSB(status, tmp);
450   if (status != SILC_STATUS_OK && 
451       status != SILC_STATUS_LIST_START &&
452       status != SILC_STATUS_LIST_ITEM &&
453       status != SILC_STATUS_LIST_END) {
454     if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
455       /* Take nickname which may be provided */
456       tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
457       if (tmp)
458         cmd->client->ops->say(cmd->client, conn, "%s: %s", tmp,
459                  silc_client_command_status_message(status));
460       else
461         cmd->client->ops->say(cmd->client, conn, "%s",
462                  silc_client_command_status_message(status));
463       COMMAND_REPLY_ERROR;
464       goto out;
465     } else {
466       cmd->client->ops->say(cmd->client, conn,
467                "%s", silc_client_command_status_message(status));
468       COMMAND_REPLY_ERROR;
469       goto out;
470     }
471   }
472
473   /* Save one IDENTIFY entry */
474   if (status == SILC_STATUS_OK)
475     silc_client_command_reply_identify_save(cmd, status);
476
477   /* List */
478   if (status == SILC_STATUS_LIST_START ||
479       status == SILC_STATUS_LIST_ITEM ||
480       status == SILC_STATUS_LIST_END)
481     silc_client_command_reply_identify_save(cmd, status);
482
483   /* Pending callbacks are not executed if this was an list entry */
484   if (status != SILC_STATUS_OK &&
485       status != SILC_STATUS_LIST_END) {
486     silc_client_command_reply_free(cmd);
487     return;
488   }
489
490   /* Execute any pending command callbacks */
491   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_IDENTIFY);
492
493  out:
494   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_IDENTIFY);
495   silc_client_command_reply_free(cmd);
496 }
497
498 /* Received reply for command NICK. If everything went without errors
499    we just received our new Client ID. */
500
501 SILC_CLIENT_CMD_REPLY_FUNC(nick)
502 {
503   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
504   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
505   SilcCommandStatus status;
506   SilcIDPayload idp;
507   unsigned char *tmp;
508   unsigned int argc, len;
509
510   SILC_LOG_DEBUG(("Start"));
511
512   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
513   if (status != SILC_STATUS_OK) {
514     cmd->client->ops->say(cmd->client, conn, "Cannot set nickname: %s", 
515              silc_client_command_status_message(status));
516     COMMAND_REPLY_ERROR;
517     goto out;
518   }
519
520   argc = silc_argument_get_arg_num(cmd->args);
521   if (argc < 2 || argc > 2) {
522     cmd->client->ops->say(cmd->client, conn, 
523                           "Cannot set nickname: bad reply to command");
524     COMMAND_REPLY_ERROR;
525     goto out;
526   }
527
528   /* Take received Client ID */
529   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
530   idp = silc_id_payload_parse_data(tmp, len);
531   if (!idp) {
532     COMMAND_REPLY_ERROR;
533     goto out;
534   }
535   silc_client_receive_new_id(cmd->client, cmd->sock, idp);
536     
537   /* Notify application */
538   COMMAND_REPLY((ARGS, conn->local_entry));
539
540   /* Execute any pending command callbacks */
541   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_NICK);
542
543  out:
544   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_NICK);
545   silc_client_command_reply_free(cmd);
546 }
547
548 SILC_CLIENT_CMD_REPLY_FUNC(list)
549 {
550 }
551
552 /* Received reply to topic command. */
553
554 SILC_CLIENT_CMD_REPLY_FUNC(topic)
555 {
556   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
557   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
558   SilcCommandStatus status;
559   SilcChannelEntry channel;
560   SilcChannelID *channel_id = NULL;
561   SilcIDCacheEntry id_cache = NULL;
562   unsigned char *tmp;
563   char *topic;
564   unsigned int argc, len;
565
566   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
567   if (status != SILC_STATUS_OK) {
568     cmd->client->ops->say(cmd->client, conn,
569              "%s", silc_client_command_status_message(status));
570     COMMAND_REPLY_ERROR;
571     SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_TOPIC);
572     silc_client_command_reply_free(cmd);
573     return;
574   }
575
576   argc = silc_argument_get_arg_num(cmd->args);
577   if (argc < 1 || argc > 3) {
578     COMMAND_REPLY_ERROR;
579     goto out;
580   }
581
582   /* Take Channel ID */
583   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
584   if (!tmp)
585     goto out;
586
587   /* Take topic */
588   topic = silc_argument_get_arg_type(cmd->args, 3, NULL);
589   if (!topic)
590     goto out;
591
592   channel_id = silc_id_payload_parse_id(tmp, len);
593   if (!channel_id)
594     goto out;
595
596   /* Get the channel name */
597   if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
598                                    SILC_ID_CHANNEL, &id_cache)) {
599     silc_free(channel_id);
600     COMMAND_REPLY_ERROR;
601     goto out;
602   }
603   
604   channel = (SilcChannelEntry)id_cache->context;
605
606   cmd->client->ops->say(cmd->client, conn, 
607                         "Topic on channel %s: %s", channel->channel_name,
608                         topic);
609
610   /* Notify application */
611   COMMAND_REPLY((ARGS, channel, topic));
612
613   /* Execute any pending command callbacks */
614   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_TOPIC);
615
616  out:
617   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_TOPIC);
618   silc_client_command_reply_free(cmd);
619 }
620
621 /* Received reply to invite command. */
622
623 SILC_CLIENT_CMD_REPLY_FUNC(invite)
624 {
625   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
626   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
627   SilcCommandStatus status;
628   unsigned char *tmp;
629
630   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
631   SILC_GET16_MSB(status, tmp);
632   if (status != SILC_STATUS_OK) {
633     cmd->client->ops->say(cmd->client, conn,
634              "%s", silc_client_command_status_message(status));
635     COMMAND_REPLY_ERROR;
636     SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INVITE);
637     silc_client_command_reply_free(cmd);
638     return;
639   }
640
641   /* Notify application */
642   COMMAND_REPLY((ARGS));
643
644   /* Execute any pending command callbacks */
645   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INVITE);
646
647   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INVITE);
648   silc_client_command_reply_free(cmd);
649 }
650  
651 SILC_CLIENT_CMD_REPLY_FUNC(kill)
652 {
653 }
654
655 /* Received reply to INFO command. We receive the server ID and some
656    information about the server user requested. */
657
658 SILC_CLIENT_CMD_REPLY_FUNC(info)
659 {
660   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
661   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
662   SilcClient client = cmd->client;
663   SilcCommandStatus status;
664   unsigned char *tmp;
665
666   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
667   SILC_GET16_MSB(status, tmp);
668   if (status != SILC_STATUS_OK) {
669     cmd->client->ops->say(cmd->client, conn,
670              "%s", silc_client_command_status_message(status));
671     COMMAND_REPLY_ERROR;
672     SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INFO);
673     silc_client_command_reply_free(cmd);
674     return;
675   }
676
677   /* Get server ID */
678   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
679   if (!tmp)
680     goto out;
681
682   /* XXX save server id */
683
684   /* Get server info */
685   tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
686   if (!tmp)
687     goto out;
688
689   client->ops->say(cmd->client, conn, "Info: %s", tmp);
690
691   /* Notify application */
692   COMMAND_REPLY((ARGS, NULL, (char *)tmp));
693
694   /* Execute any pending command callbacks */
695   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INFO);
696
697  out:
698   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INFO);
699   silc_client_command_reply_free(cmd);
700 }
701
702 /* Received reply to PING command. The reply time is shown to user. */
703
704 SILC_CLIENT_CMD_REPLY_FUNC(ping)
705 {
706   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
707   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
708   SilcCommandStatus status;
709   void *id;
710   int i;
711   time_t diff, curtime;
712
713   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
714   if (status != SILC_STATUS_OK) {
715     cmd->client->ops->say(cmd->client, conn,
716              "%s", silc_client_command_status_message(status));
717     COMMAND_REPLY_ERROR;
718     goto out;
719   }
720
721   curtime = time(NULL);
722   id = silc_id_str2id(cmd->packet->src_id, cmd->packet->src_id_len,
723                       cmd->packet->src_id_type);
724   if (!id) {
725     COMMAND_REPLY_ERROR;
726     goto out;
727   }
728
729   for (i = 0; i < conn->ping_count; i++) {
730     if (!SILC_ID_SERVER_COMPARE(conn->ping[i].dest_id, id)) {
731       diff = curtime - conn->ping[i].start_time;
732       cmd->client->ops->say(cmd->client, conn, 
733                             "Ping reply from %s: %d second%s", 
734                             conn->ping[i].dest_name, diff, 
735                             diff == 1 ? "" : "s");
736       
737       conn->ping[i].start_time = 0;
738       silc_free(conn->ping[i].dest_id);
739       conn->ping[i].dest_id = NULL;
740       silc_free(conn->ping[i].dest_name);
741       conn->ping[i].dest_name = NULL;
742       break;
743     }
744   }
745
746   silc_free(id);
747
748   /* Notify application */
749   COMMAND_REPLY((ARGS));
750
751   /* Execute any pending command callbacks */
752   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PING);
753
754  out:
755   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_PING);
756   silc_client_command_reply_free(cmd);
757 }
758
759 /* Received reply for JOIN command. */
760
761 SILC_CLIENT_CMD_REPLY_FUNC(join)
762 {
763   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
764   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
765   SilcCommandStatus status;
766   SilcIDPayload idp = NULL;
767   SilcChannelEntry channel;
768   SilcIDCacheEntry id_cache = NULL;
769   SilcChannelUser chu;
770   unsigned int argc, mode, len, list_count;
771   char *topic, *tmp, *channel_name = NULL, *hmac;
772   SilcBuffer keyp, client_id_list, client_mode_list;
773   int i;
774
775   SILC_LOG_DEBUG(("Start"));
776
777   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
778   if (status != SILC_STATUS_OK) {
779     cmd->client->ops->say(cmd->client, conn,
780              "%s", silc_client_command_status_message(status));
781     COMMAND_REPLY_ERROR;
782     goto out;
783   }
784
785   argc = silc_argument_get_arg_num(cmd->args);
786   if (argc < 7 || argc > 14) {
787     cmd->client->ops->say(cmd->client, conn,
788              "Cannot join channel: Bad reply packet");
789     COMMAND_REPLY_ERROR;
790     goto out;
791   }
792
793   /* Get channel name */
794   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
795   if (!tmp) {
796     cmd->client->ops->say(cmd->client, conn, 
797                           "Cannot join channel: Bad reply packet");
798     COMMAND_REPLY_ERROR;
799     goto out;
800   }
801   channel_name = strdup(tmp);
802
803   /* Get Channel ID */
804   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
805   if (!tmp) {
806     cmd->client->ops->say(cmd->client, conn, 
807                           "Cannot join channel: Bad reply packet");
808     COMMAND_REPLY_ERROR;
809     silc_free(channel_name);
810     goto out;
811   }
812   idp = silc_id_payload_parse_data(tmp, len);
813   if (!idp) {
814     COMMAND_REPLY_ERROR;
815     silc_free(channel_name);
816     goto out;
817   }
818
819   /* Get channel mode */
820   tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
821   if (tmp)
822     SILC_GET32_MSB(mode, tmp);
823   else
824     mode = 0;
825
826   /* Get channel key */
827   tmp = silc_argument_get_arg_type(cmd->args, 7, &len);
828   if (!tmp) {
829     silc_id_payload_free(idp);
830     silc_free(channel_name);
831     goto out;
832   }
833   keyp = silc_buffer_alloc(len);
834   silc_buffer_pull_tail(keyp, SILC_BUFFER_END(keyp));
835   silc_buffer_put(keyp, tmp, len);
836
837   /* Get topic */
838   topic = silc_argument_get_arg_type(cmd->args, 10, NULL);
839
840   /* Save received Channel ID. This actually creates the channel */
841   channel = silc_client_new_channel_id(cmd->client, cmd->sock, channel_name, 
842                                        mode, idp);
843   silc_id_payload_free(idp);
844
845   /* Get hmac */
846   hmac = silc_argument_get_arg_type(cmd->args, 11, NULL);
847   if (hmac) {
848     if (!silc_hmac_alloc(hmac, NULL, &channel->hmac)) {
849       cmd->client->ops->say(cmd->client, conn, 
850                             "Cannot join channel: Unsupported HMAC `%s'",
851                             hmac);
852       COMMAND_REPLY_ERROR;
853       silc_free(channel_name);
854       goto out;
855     }
856   }
857
858   /* Get the list count */
859   tmp = silc_argument_get_arg_type(cmd->args, 12, &len);
860   if (!tmp)
861     goto out;
862   SILC_GET32_MSB(list_count, tmp);
863
864   /* Get Client ID list */
865   tmp = silc_argument_get_arg_type(cmd->args, 13, &len);
866   if (!tmp)
867     goto out;
868
869   client_id_list = silc_buffer_alloc(len);
870   silc_buffer_pull_tail(client_id_list, len);
871   silc_buffer_put(client_id_list, tmp, len);
872
873   /* Get client mode list */
874   tmp = silc_argument_get_arg_type(cmd->args, 14, &len);
875   if (!tmp)
876     goto out;
877
878   client_mode_list = silc_buffer_alloc(len);
879   silc_buffer_pull_tail(client_mode_list, len);
880   silc_buffer_put(client_mode_list, tmp, len);
881
882   /* Add clients we received in the reply to the channel */
883   for (i = 0; i < list_count; i++) {
884     unsigned short idp_len;
885     unsigned int mode;
886     SilcClientID *client_id;
887     SilcClientEntry client_entry;
888
889     /* Client ID */
890     SILC_GET16_MSB(idp_len, client_id_list->data + 2);
891     idp_len += 4;
892     client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
893     if (!client_id)
894       continue;
895
896     /* Mode */
897     SILC_GET32_MSB(mode, client_mode_list->data);
898
899     /* Check if we have this client cached already. */
900     if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
901                                      SILC_ID_CLIENT, &id_cache)) {
902       /* No, we don't have it, add entry for it. */
903       client_entry = silc_calloc(1, sizeof(*client_entry));
904       client_entry->id = silc_id_dup(client_id, SILC_ID_CLIENT);
905       silc_idcache_add(conn->client_cache, NULL, SILC_ID_CLIENT, 
906                        client_entry->id, (void *)client_entry, FALSE);
907     } else {
908       /* Yes, we have it already */
909       client_entry = (SilcClientEntry)id_cache->context;
910     }
911
912     /* Join the client to the channel */
913     chu = silc_calloc(1, sizeof(*chu));
914     chu->client = client_entry;
915     chu->mode = mode;
916     silc_list_add(channel->clients, chu);
917     silc_free(client_id);
918
919     silc_buffer_pull(client_id_list, idp_len);
920     silc_buffer_pull(client_mode_list, 4);
921   }
922   silc_buffer_push(client_id_list, client_id_list->data - 
923                    client_id_list->head);
924   silc_buffer_push(client_mode_list, client_mode_list->data - 
925                    client_mode_list->head);
926
927   /* Save channel key */
928   silc_client_save_channel_key(conn, keyp, channel);
929
930   /* Notify application */
931   COMMAND_REPLY((ARGS, channel_name, channel, mode, 0, keyp->head, NULL,
932                  NULL, topic, hmac, list_count, client_id_list, 
933                  client_mode_list));
934
935   /* Execute any pending command callbacks */
936   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_JOIN);
937
938   silc_buffer_free(keyp);
939   silc_buffer_free(client_id_list);
940   silc_buffer_free(client_mode_list);
941
942  out:
943   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_JOIN);
944   silc_client_command_reply_free(cmd);
945 }
946
947 /* Received reply for MOTD command */
948
949 SILC_CLIENT_CMD_REPLY_FUNC(motd)
950 {
951   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
952   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
953   SilcCommandStatus status;
954   unsigned int argc, i;
955   unsigned char *tmp;
956   char *motd = NULL, *cp, line[256];
957
958   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
959   SILC_GET16_MSB(status, tmp);
960   if (status != SILC_STATUS_OK) {
961     cmd->client->ops->say(cmd->client, conn,
962              "%s", silc_client_command_status_message(status));
963     COMMAND_REPLY_ERROR;
964     return;
965   }
966
967   argc = silc_argument_get_arg_num(cmd->args);
968   if (argc > 2) {
969     COMMAND_REPLY_ERROR;
970     goto out;
971   }
972
973   if (argc == 2) {
974     motd = silc_argument_get_arg_type(cmd->args, 2, NULL);
975     if (!motd) {
976       COMMAND_REPLY_ERROR;
977       goto out;
978     }
979
980     i = 0;
981     cp = motd;
982     while(cp[i] != 0) {
983       if (cp[i++] == '\n') {
984         memset(line, 0, sizeof(line));
985         strncat(line, cp, i - 1);
986         cp += i;
987         
988         if (i == 2)
989           line[0] = ' ';
990         
991         cmd->client->ops->say(cmd->client, conn, "%s", line);
992         
993         if (!strlen(cp))
994           break;
995         i = 0;
996       }
997     }
998   }
999
1000   /* Notify application */
1001   COMMAND_REPLY((ARGS, motd));
1002
1003   /* Execute any pending command callbacks */
1004   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_MOTD);
1005
1006  out:
1007   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_MOTD);
1008   silc_client_command_reply_free(cmd);
1009 }
1010
1011 SILC_CLIENT_CMD_REPLY_FUNC(umode)
1012 {
1013 }
1014
1015 /* Received reply for CMODE command. */
1016
1017 SILC_CLIENT_CMD_REPLY_FUNC(cmode)
1018 {
1019   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1020   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1021   SilcCommandStatus status;
1022   unsigned char *tmp;
1023
1024   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
1025   if (status != SILC_STATUS_OK) {
1026     cmd->client->ops->say(cmd->client, conn,
1027              "%s", silc_client_command_status_message(status));
1028     COMMAND_REPLY_ERROR;
1029     goto out;
1030   }
1031
1032   /* Get channel mode */
1033   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
1034   if (!tmp) {
1035     COMMAND_REPLY_ERROR;
1036     goto out;
1037   }
1038
1039   /* Notify application */
1040   COMMAND_REPLY((ARGS, tmp));
1041
1042   /* Execute any pending command callbacks */
1043   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CMODE);
1044
1045  out:
1046   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CMODE);
1047   silc_client_command_reply_free(cmd);
1048 }
1049
1050 /* Received reply for CUMODE command */
1051
1052 SILC_CLIENT_CMD_REPLY_FUNC(cumode)
1053 {
1054   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1055   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1056   SilcCommandStatus status;
1057   SilcIDCacheEntry id_cache = NULL;
1058   SilcClientID *client_id;
1059   unsigned char *tmp, *id;
1060   unsigned int len;
1061   
1062   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
1063   if (status != SILC_STATUS_OK) {
1064     cmd->client->ops->say(cmd->client, conn,
1065              "%s", silc_client_command_status_message(status));
1066     COMMAND_REPLY_ERROR;
1067     goto out;
1068   }
1069   
1070   /* Get channel mode */
1071   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
1072   if (!tmp) {
1073     COMMAND_REPLY_ERROR;
1074     goto out;
1075   }
1076
1077   /* Get Client ID */
1078   id = silc_argument_get_arg_type(cmd->args, 3, &len);
1079   if (!id) {
1080     COMMAND_REPLY_ERROR;
1081     goto out;
1082   }
1083   client_id = silc_id_payload_parse_id(id, len);
1084   if (!client_id) {
1085     COMMAND_REPLY_ERROR;
1086     goto out;
1087   }
1088   
1089   /* Get client entry */
1090   if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
1091                                    SILC_ID_CLIENT, &id_cache)) {
1092     COMMAND_REPLY_ERROR;
1093     goto out;
1094   }
1095
1096   /* Notify application */
1097   COMMAND_REPLY((ARGS, tmp, (SilcClientEntry)id_cache->context));
1098   silc_free(client_id);
1099   
1100   /* Execute any pending command callbacks */
1101   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CUMODE);
1102
1103  out:
1104   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CUMODE);
1105   silc_client_command_reply_free(cmd);
1106 }
1107
1108 SILC_CLIENT_CMD_REPLY_FUNC(kick)
1109 {
1110   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1111   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1112   SilcCommandStatus status;
1113   unsigned char *tmp;
1114
1115   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1116   SILC_GET16_MSB(status, tmp);
1117   if (status != SILC_STATUS_OK) {
1118     cmd->client->ops->say(cmd->client, conn,
1119              "%s", silc_client_command_status_message(status));
1120     COMMAND_REPLY_ERROR;
1121     goto out;
1122   }
1123
1124   /* Notify application */
1125   COMMAND_REPLY((ARGS));
1126
1127   /* Execute any pending command callbacks */
1128   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_KICK);
1129
1130  out:
1131   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_KICK);
1132   silc_client_command_reply_free(cmd);
1133 }
1134
1135 SILC_CLIENT_CMD_REPLY_FUNC(silcoper)
1136 {
1137 }
1138
1139 SILC_CLIENT_CMD_REPLY_FUNC(oper)
1140 {
1141 }
1142
1143 SILC_CLIENT_CMD_REPLY_FUNC(connect)
1144 {
1145   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1146   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1147   SilcCommandStatus status;
1148   unsigned char *tmp;
1149
1150   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1151   SILC_GET16_MSB(status, tmp);
1152   if (status != SILC_STATUS_OK) {
1153     cmd->client->ops->say(cmd->client, conn,
1154              "%s", silc_client_command_status_message(status));
1155     COMMAND_REPLY_ERROR;
1156     goto out;
1157   }
1158
1159   /* Notify application */
1160   COMMAND_REPLY((ARGS));
1161
1162   /* Execute any pending command callbacks */
1163   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CONNECT);
1164
1165  out:
1166   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CONNECT);
1167   silc_client_command_reply_free(cmd);
1168 }
1169
1170 SILC_CLIENT_CMD_REPLY_FUNC(restart)
1171 {
1172   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1173   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1174   SilcCommandStatus status;
1175   unsigned char *tmp;
1176
1177   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1178   SILC_GET16_MSB(status, tmp);
1179   if (status != SILC_STATUS_OK) {
1180     cmd->client->ops->say(cmd->client, conn,
1181              "%s", silc_client_command_status_message(status));
1182     COMMAND_REPLY_ERROR;
1183     goto out;
1184   }
1185
1186   /* Notify application */
1187   COMMAND_REPLY((ARGS));
1188
1189   /* Execute any pending command callbacks */
1190   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_RESTART);
1191
1192  out:
1193   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_RESTART);
1194   silc_client_command_reply_free(cmd);
1195 }
1196  
1197 SILC_CLIENT_CMD_REPLY_FUNC(close)
1198 {
1199   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1200   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1201   SilcCommandStatus status;
1202   unsigned char *tmp;
1203
1204   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1205   SILC_GET16_MSB(status, tmp);
1206   if (status != SILC_STATUS_OK) {
1207     cmd->client->ops->say(cmd->client, conn,
1208              "%s", silc_client_command_status_message(status));
1209     COMMAND_REPLY_ERROR;
1210     goto out;
1211   }
1212
1213   /* Notify application */
1214   COMMAND_REPLY((ARGS));
1215
1216   /* Execute any pending command callbacks */
1217   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CLOSE);
1218
1219  out:
1220   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CLOSE);
1221   silc_client_command_reply_free(cmd);
1222 }
1223  
1224 SILC_CLIENT_CMD_REPLY_FUNC(shutdown)
1225 {
1226   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1227   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1228   SilcCommandStatus status;
1229   unsigned char *tmp;
1230
1231   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1232   SILC_GET16_MSB(status, tmp);
1233   if (status != SILC_STATUS_OK) {
1234     cmd->client->ops->say(cmd->client, conn,
1235              "%s", silc_client_command_status_message(status));
1236     COMMAND_REPLY_ERROR;
1237     goto out;
1238   }
1239
1240   /* Notify application */
1241   COMMAND_REPLY((ARGS));
1242
1243   /* Execute any pending command callbacks */
1244   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_SHUTDOWN);
1245
1246  out:
1247   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_SHUTDOWN);
1248   silc_client_command_reply_free(cmd);
1249 }
1250  
1251 /* Reply to LEAVE command. */
1252
1253 SILC_CLIENT_CMD_REPLY_FUNC(leave)
1254 {
1255   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1256   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1257   SilcCommandStatus status;
1258   unsigned char *tmp;
1259
1260   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1261   SILC_GET16_MSB(status, tmp);
1262   if (status != SILC_STATUS_OK) {
1263     cmd->client->ops->say(cmd->client, conn,
1264              "%s", silc_client_command_status_message(status));
1265     COMMAND_REPLY_ERROR;
1266     goto out;
1267   }
1268
1269   /* Notify application */
1270   COMMAND_REPLY((ARGS));
1271
1272   /* Execute any pending command callbacks */
1273   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_LEAVE);
1274
1275  out:
1276   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_LEAVE);
1277   silc_client_command_reply_free(cmd);
1278 }
1279
1280 /* Reply to USERS command. Received list of client ID's and theirs modes
1281    on the channel we requested. */
1282
1283 SILC_CLIENT_CMD_REPLY_FUNC(users)
1284 {
1285   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1286   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1287   SilcCommandStatus status;
1288   SilcIDCacheEntry id_cache = NULL;
1289   SilcChannelEntry channel;
1290   SilcChannelUser chu;
1291   SilcChannelID *channel_id = NULL;
1292   SilcBuffer client_id_list;
1293   SilcBuffer client_mode_list;
1294   unsigned char *tmp;
1295   unsigned int tmp_len, list_count;
1296   int i;
1297   unsigned char **res_argv = NULL;
1298   unsigned int *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
1299
1300   SILC_LOG_DEBUG(("Start"));
1301
1302   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1303   SILC_GET16_MSB(status, tmp);
1304   if (status != SILC_STATUS_OK) {
1305     cmd->client->ops->say(cmd->client, conn,
1306              "%s", silc_client_command_status_message(status));
1307     COMMAND_REPLY_ERROR;
1308     goto out;
1309   }
1310
1311   /* Get channel ID */
1312   tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
1313   if (!tmp)
1314     goto out;
1315   channel_id = silc_id_payload_parse_id(tmp, tmp_len);
1316   if (!channel_id)
1317     goto out;
1318
1319   /* Get the list count */
1320   tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
1321   if (!tmp)
1322     goto out;
1323   SILC_GET32_MSB(list_count, tmp);
1324
1325   /* Get Client ID list */
1326   tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
1327   if (!tmp)
1328     goto out;
1329
1330   client_id_list = silc_buffer_alloc(tmp_len);
1331   silc_buffer_pull_tail(client_id_list, tmp_len);
1332   silc_buffer_put(client_id_list, tmp, tmp_len);
1333
1334   /* Get client mode list */
1335   tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
1336   if (!tmp)
1337     goto out;
1338
1339   client_mode_list = silc_buffer_alloc(tmp_len);
1340   silc_buffer_pull_tail(client_mode_list, tmp_len);
1341   silc_buffer_put(client_mode_list, tmp, tmp_len);
1342
1343   /* Get channel entry */
1344   if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
1345                                    SILC_ID_CHANNEL, &id_cache)) {
1346     COMMAND_REPLY_ERROR;
1347     goto out;
1348   }
1349   channel = (SilcChannelEntry)id_cache->context;
1350
1351   /* Remove old client list from channel. */
1352   silc_list_start(channel->clients);
1353   while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1354     silc_list_del(channel->clients, chu);
1355     silc_free(chu);
1356   }
1357
1358   /* Cache the received Client ID's and modes. This cache expires
1359      whenever server sends notify message to channel. It means two things;
1360      some user has joined or leaved the channel. XXX! */
1361   for (i = 0; i < list_count; i++) {
1362     unsigned short idp_len;
1363     unsigned int mode;
1364     SilcClientID *client_id;
1365     SilcClientEntry client;
1366
1367     /* Client ID */
1368     SILC_GET16_MSB(idp_len, client_id_list->data + 2);
1369     idp_len += 4;
1370     client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
1371     if (!client_id)
1372       continue;
1373
1374     /* Mode */
1375     SILC_GET32_MSB(mode, client_mode_list->data);
1376
1377     /* Check if we have this client cached already. */
1378     if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
1379                                      SILC_ID_CLIENT, &id_cache)) {
1380       /* No we don't have it, query it from the server. Assemble argument
1381          table that will be sent fr the IDENTIFY command later. */
1382       res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
1383                               (res_argc + 1));
1384       res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
1385                                    (res_argc + 1));
1386       res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
1387                                     (res_argc + 1));
1388       res_argv[res_argc] = client_id_list->data;
1389       res_argv_lens[res_argc] = idp_len;
1390       res_argv_types[res_argc] = res_argc + 3;
1391       res_argc++;
1392     } else {
1393       /* Found the client, join it to the channel */
1394       client = (SilcClientEntry)id_cache->context;
1395       chu = silc_calloc(1, sizeof(*chu));
1396       chu->client = client;
1397       chu->mode = mode;
1398       silc_list_add(channel->clients, chu);
1399
1400       silc_free(client_id);
1401       id_cache = NULL;
1402     }
1403
1404     silc_buffer_pull(client_id_list, idp_len);
1405     silc_buffer_pull(client_mode_list, 4);
1406   }
1407
1408   /* Query the client information from server if the list included clients
1409      that we don't know about. */
1410   if (res_argc) {
1411     SilcBuffer res_cmd;
1412
1413     /* Send the IDENTIFY command to server */
1414     res_cmd = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
1415                                           res_argc, res_argv, res_argv_lens,
1416                                           res_argv_types, ++conn->cmd_ident);
1417     silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, 
1418                             NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
1419                             TRUE);
1420
1421     /* Register pending command callback. After we've received the IDENTIFY
1422        command reply we will reprocess this command reply by re-calling this
1423        USERS command reply callback. */
1424     silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
1425                                 NULL, silc_client_command_reply_users, cmd);
1426
1427     silc_buffer_free(res_cmd);
1428     if (channel_id)
1429       silc_free(channel_id);
1430
1431     silc_free(res_argv);
1432     silc_free(res_argv_lens);
1433     silc_free(res_argv_types);
1434     return;
1435   }
1436
1437   /* Notify application */
1438   COMMAND_REPLY((ARGS, channel, list_count, client_id_list, client_mode_list));
1439
1440   /* Execute any pending command callbacks */
1441   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
1442
1443   silc_buffer_free(client_id_list);
1444   silc_buffer_free(client_mode_list);
1445
1446  out:
1447   if (channel_id)
1448     silc_free(channel_id);
1449   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_USERS);
1450   silc_client_command_reply_free(cmd);
1451 }