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