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