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(close, CLOSE),
61   SILC_CLIENT_CMD_REPLY(shutdown, SHUTDOWN),
62   SILC_CLIENT_CMD_REPLY(silcoper, SILCOPER),
63   SILC_CLIENT_CMD_REPLY(leave, LEAVE),
64   SILC_CLIENT_CMD_REPLY(users, USERS),
65   SILC_CLIENT_CMD_REPLY(ban, BAN),
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),       "Permission denied" },
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, mode = 0;
212   SilcBuffer channels = NULL;
213   
214   argc = silc_argument_get_arg_num(cmd->args);
215
216   id_data = silc_argument_get_arg_type(cmd->args, 2, &len);
217   if (!id_data) {
218     COMMAND_REPLY_ERROR;
219     return;
220   }
221   
222   client_id = silc_id_payload_parse_id(id_data, len);
223   if (!client_id) {
224     COMMAND_REPLY_ERROR;
225     return;
226   }
227   
228   nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
229   username = silc_argument_get_arg_type(cmd->args, 4, &len);
230   realname = silc_argument_get_arg_type(cmd->args, 5, &len);
231   if (!nickname || !username || !realname) {
232     COMMAND_REPLY_ERROR;
233     return;
234   }
235
236   tmp = silc_argument_get_arg_type(cmd->args, 6, &len);
237   if (tmp) {
238     channels = silc_buffer_alloc(len);
239     silc_buffer_pull_tail(channels, SILC_BUFFER_END(channels));
240     silc_buffer_put(channels, tmp, len);
241   }
242
243   tmp = silc_argument_get_arg_type(cmd->args, 7, &len);
244   if (tmp)
245     SILC_GET32_MSB(mode, tmp);
246
247   tmp = silc_argument_get_arg_type(cmd->args, 8, &len);
248   if (tmp)
249     SILC_GET32_MSB(idle, tmp);
250
251   /* Check if we have this client cached already. */
252   if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
253                                    SILC_ID_CLIENT, &id_cache)) {
254     SILC_LOG_DEBUG(("Adding new client entry"));
255
256     client_entry = silc_calloc(1, sizeof(*client_entry));
257     client_entry->id = client_id;
258     silc_parse_nickname(nickname, &client_entry->nickname, 
259                         &client_entry->server, &client_entry->num);
260     client_entry->username = strdup(username);
261     if (realname)
262       client_entry->realname = strdup(realname);
263     client_entry->mode = mode;
264     
265     /* Add client to cache */
266     silc_idcache_add(conn->client_cache, client_entry->nickname,
267                      strlen(client_entry->nickname),
268                      SILC_ID_CLIENT, client_id, (void *)client_entry, 
269                      TRUE, FALSE);
270   } else {
271     client_entry = (SilcClientEntry)id_cache->context;
272     if (client_entry->nickname)
273       silc_free(client_entry->nickname);
274     if (client_entry->server)
275       silc_free(client_entry->server);
276     if (client_entry->username)
277       silc_free(client_entry->username);
278     if (client_entry->realname)
279       silc_free(client_entry->realname);
280     client_entry->mode = mode;
281
282     SILC_LOG_DEBUG(("Updating client entry"));
283
284     silc_parse_nickname(nickname, &client_entry->nickname, 
285                         &client_entry->server, &client_entry->num);
286     client_entry->username = strdup(username);
287     if (realname)
288       client_entry->realname = strdup(realname);
289
290     id_cache->data = client_entry->nickname;
291     id_cache->data_len = strlen(client_entry->nickname);
292     silc_idcache_sort_by_data(conn->client_cache);
293
294     silc_free(client_id);
295   }
296
297   /* Notify application */
298   if (!cmd->callback)
299     COMMAND_REPLY((ARGS, client_entry, nickname, username, realname, 
300                    channels, mode, idle));
301
302   if (channels)
303     silc_buffer_free(channels);
304 }
305
306 /* Received reply for WHOIS command. This maybe called several times
307    for one WHOIS command as server may reply with list of results. */
308
309 SILC_CLIENT_CMD_REPLY_FUNC(whois)
310 {
311   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
312   SilcCommandStatus status;
313   unsigned char *tmp;
314
315   SILC_LOG_DEBUG(("Start"));
316
317   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
318   SILC_GET16_MSB(status, tmp);
319   if (status != SILC_STATUS_OK && 
320       status != SILC_STATUS_LIST_START &&
321       status != SILC_STATUS_LIST_ITEM &&
322       status != SILC_STATUS_LIST_END) {
323     COMMAND_REPLY_ERROR;
324     goto out;
325   }
326
327   /* Display one whois reply */
328   if (status == SILC_STATUS_OK)
329     silc_client_command_reply_whois_save(cmd, status);
330
331   /* List */
332   if (status == SILC_STATUS_LIST_START ||
333       status == SILC_STATUS_LIST_ITEM ||
334       status == SILC_STATUS_LIST_END)
335     silc_client_command_reply_whois_save(cmd, status);
336
337   /* Pending callbacks are not executed if this was an list entry */
338   if (status != SILC_STATUS_OK &&
339       status != SILC_STATUS_LIST_END) {
340     silc_client_command_reply_free(cmd);
341     return;
342   }
343
344   /* Execute any pending command callbacks */
345   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_WHOIS);
346
347  out:
348   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_WHOIS);
349   silc_client_command_reply_free(cmd);
350 }
351
352 /* Received reply for WHOWAS command. */
353
354 SILC_CLIENT_CMD_REPLY_FUNC(whowas)
355 {
356   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
357   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
358   SilcCommandStatus status;
359   SilcClientID *client_id;
360   SilcIDCacheEntry id_cache = NULL;
361   SilcClientEntry client_entry = NULL;
362   unsigned int len;
363   unsigned char *id_data, *tmp;
364   char *nickname, *username;
365   char *realname = NULL;
366
367   SILC_LOG_DEBUG(("Start"));
368
369   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
370   SILC_GET16_MSB(status, tmp);
371   if (status != SILC_STATUS_OK && 
372       status != SILC_STATUS_LIST_START &&
373       status != SILC_STATUS_LIST_ITEM &&
374       status != SILC_STATUS_LIST_END) {
375     COMMAND_REPLY_ERROR;
376     goto out;
377   }
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   /* Get the client entry, if exists */
392   if (silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
393                                   SILC_ID_CLIENT, &id_cache))
394     client_entry = (SilcClientEntry)id_cache->context;
395   silc_free(client_id);
396
397   nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
398   username = silc_argument_get_arg_type(cmd->args, 4, &len);
399   realname = silc_argument_get_arg_type(cmd->args, 5, &len);
400   if (!nickname || !username) {
401     COMMAND_REPLY_ERROR;
402     return;
403   }
404   /* Notify application. We don't save any history information to any
405      cache. Just pass the data to the application for displaying on 
406      the screen. */
407   COMMAND_REPLY((ARGS, client_entry, nickname, username, realname));
408
409   /* Pending callbacks are not executed if this was an list entry */
410   if (status != SILC_STATUS_OK &&
411       status != SILC_STATUS_LIST_END) {
412     silc_client_command_reply_free(cmd);
413     return;
414   }
415
416   /* Execute any pending command callbacks */
417   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_WHOIS);
418
419  out:
420   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_WHOIS);
421   silc_client_command_reply_free(cmd);
422 }
423
424 static void 
425 silc_client_command_reply_identify_save(SilcClientCommandReplyContext cmd,
426                                         SilcCommandStatus status)
427 {
428   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
429   SilcClientID *client_id;
430   SilcIDCacheEntry id_cache = NULL;
431   SilcClientEntry client_entry = NULL;
432   int argc, len;
433   unsigned char *id_data;
434   char *nickname = NULL, *username = NULL;
435   
436   argc = silc_argument_get_arg_num(cmd->args);
437
438   id_data = silc_argument_get_arg_type(cmd->args, 2, &len);
439   if (!id_data) {
440     COMMAND_REPLY_ERROR;
441     return;
442   }
443   
444   client_id = silc_id_payload_parse_id(id_data, len);
445   if (!client_id) {
446     COMMAND_REPLY_ERROR;
447     return;
448   }
449   
450   nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
451   username = silc_argument_get_arg_type(cmd->args, 4, &len);
452
453   /* Check if we have this client cached already. */
454   if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
455                                    SILC_ID_CLIENT, &id_cache)) {
456     SILC_LOG_DEBUG(("Adding new client entry"));
457
458     client_entry = silc_calloc(1, sizeof(*client_entry));
459     client_entry->id = client_id;
460     silc_parse_nickname(nickname, &client_entry->nickname, 
461                         &client_entry->server, &client_entry->num);
462     if (username)
463       client_entry->username = strdup(username);
464     
465     /* Add client to cache */
466     silc_idcache_add(conn->client_cache, client_entry->nickname,
467                      strlen(client_entry->nickname),
468                      SILC_ID_CLIENT, client_id, (void *)client_entry, 
469                      TRUE, FALSE);
470   } else {
471     client_entry = (SilcClientEntry)id_cache->context;
472     if (client_entry->nickname)
473       silc_free(client_entry->nickname);
474     if (client_entry->server)
475       silc_free(client_entry->server);
476     if (username && client_entry->username)
477       silc_free(client_entry->username);
478     
479     SILC_LOG_DEBUG(("Updating client entry"));
480
481     silc_parse_nickname(nickname, &client_entry->nickname, 
482                         &client_entry->server, &client_entry->num);
483     
484     if (username)
485       client_entry->username = strdup(username);
486     
487     id_cache->data = client_entry->nickname;
488     id_cache->data_len = strlen(client_entry->nickname);
489     silc_idcache_sort_by_data(conn->client_cache);
490     
491     silc_free(client_id);
492   }
493
494   /* Notify application */
495   COMMAND_REPLY((ARGS, client_entry, nickname, username));
496 }
497
498 /* Received reply for IDENTIFY command. This maybe called several times
499    for one IDENTIFY command as server may reply with list of results. 
500    This is totally silent and does not print anything on screen. */
501
502 SILC_CLIENT_CMD_REPLY_FUNC(identify)
503 {
504   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
505   SilcCommandStatus status;
506   unsigned char *tmp;
507
508   SILC_LOG_DEBUG(("Start"));
509
510   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
511   SILC_GET16_MSB(status, tmp);
512   if (status != SILC_STATUS_OK && 
513       status != SILC_STATUS_LIST_START &&
514       status != SILC_STATUS_LIST_ITEM &&
515       status != SILC_STATUS_LIST_END) {
516     COMMAND_REPLY_ERROR;
517     goto out;
518   }
519
520   /* Save one IDENTIFY entry */
521   if (status == SILC_STATUS_OK)
522     silc_client_command_reply_identify_save(cmd, status);
523
524   /* List */
525   if (status == SILC_STATUS_LIST_START ||
526       status == SILC_STATUS_LIST_ITEM ||
527       status == SILC_STATUS_LIST_END)
528     silc_client_command_reply_identify_save(cmd, status);
529
530   /* Pending callbacks are not executed if this was an list entry */
531   if (status != SILC_STATUS_OK &&
532       status != SILC_STATUS_LIST_END) {
533     silc_client_command_reply_free(cmd);
534     return;
535   }
536
537   /* Execute any pending command callbacks */
538   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_IDENTIFY);
539
540  out:
541   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_IDENTIFY);
542   silc_client_command_reply_free(cmd);
543 }
544
545 /* Received reply for command NICK. If everything went without errors
546    we just received our new Client ID. */
547
548 SILC_CLIENT_CMD_REPLY_FUNC(nick)
549 {
550   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
551   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
552   SilcCommandStatus status;
553   SilcIDPayload idp;
554   unsigned char *tmp;
555   unsigned int argc, len;
556
557   SILC_LOG_DEBUG(("Start"));
558
559   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
560   if (status != SILC_STATUS_OK) {
561     cmd->client->ops->say(cmd->client, conn, "Cannot set nickname: %s", 
562              silc_client_command_status_message(status));
563     COMMAND_REPLY_ERROR;
564     goto out;
565   }
566
567   argc = silc_argument_get_arg_num(cmd->args);
568   if (argc < 2 || argc > 2) {
569     cmd->client->ops->say(cmd->client, conn, 
570                           "Cannot set nickname: bad reply to command");
571     COMMAND_REPLY_ERROR;
572     goto out;
573   }
574
575   /* Take received Client ID */
576   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
577   idp = silc_id_payload_parse_data(tmp, len);
578   if (!idp) {
579     COMMAND_REPLY_ERROR;
580     goto out;
581   }
582   silc_client_receive_new_id(cmd->client, cmd->sock, idp);
583     
584   /* Notify application */
585   COMMAND_REPLY((ARGS, conn->local_entry));
586
587   /* Execute any pending command callbacks */
588   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_NICK);
589
590  out:
591   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_NICK);
592   silc_client_command_reply_free(cmd);
593 }
594
595 /* Received reply to the LIST command. */
596
597 SILC_CLIENT_CMD_REPLY_FUNC(list)
598 {
599   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
600   SilcCommandStatus status;
601   unsigned char *tmp, *name, *topic;
602   unsigned int usercount = 0;
603
604   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
605   SILC_GET16_MSB(status, tmp);
606   if (status != SILC_STATUS_OK && 
607       status != SILC_STATUS_LIST_START &&
608       status != SILC_STATUS_LIST_ITEM &&
609       status != SILC_STATUS_LIST_END) {
610     COMMAND_REPLY_ERROR;
611     goto out;
612   }
613
614   name = silc_argument_get_arg_type(cmd->args, 3, NULL);
615   topic = silc_argument_get_arg_type(cmd->args, 4, NULL);
616   tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
617   if (tmp)
618     SILC_GET32_MSB(usercount, tmp);
619
620   /* Notify application */
621   COMMAND_REPLY((ARGS, NULL, name, topic, usercount));
622
623   /* Pending callbacks are not executed if this was an list entry */
624   if (status != SILC_STATUS_OK &&
625       status != SILC_STATUS_LIST_END) {
626     silc_client_command_reply_free(cmd);
627     return;
628   }
629
630   /* Execute any pending command callbacks */
631   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_LIST);
632
633  out:
634   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_LIST);
635   silc_client_command_reply_free(cmd);
636 }
637
638 /* Received reply to topic command. */
639
640 SILC_CLIENT_CMD_REPLY_FUNC(topic)
641 {
642   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
643   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
644   SilcCommandStatus status;
645   SilcChannelEntry channel;
646   SilcChannelID *channel_id = NULL;
647   SilcIDCacheEntry id_cache = NULL;
648   unsigned char *tmp;
649   char *topic;
650   unsigned int argc, len;
651
652   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
653   if (status != SILC_STATUS_OK) {
654     cmd->client->ops->say(cmd->client, conn,
655              "%s", silc_client_command_status_message(status));
656     COMMAND_REPLY_ERROR;
657     SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_TOPIC);
658     silc_client_command_reply_free(cmd);
659     return;
660   }
661
662   argc = silc_argument_get_arg_num(cmd->args);
663   if (argc < 1 || argc > 3) {
664     COMMAND_REPLY_ERROR;
665     goto out;
666   }
667
668   /* Take Channel ID */
669   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
670   if (!tmp)
671     goto out;
672
673   /* Take topic */
674   topic = silc_argument_get_arg_type(cmd->args, 3, NULL);
675   if (!topic)
676     goto out;
677
678   channel_id = silc_id_payload_parse_id(tmp, len);
679   if (!channel_id)
680     goto out;
681
682   /* Get the channel entry */
683   if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
684                                    SILC_ID_CHANNEL, &id_cache)) {
685     silc_free(channel_id);
686     COMMAND_REPLY_ERROR;
687     goto out;
688   }
689   
690   channel = (SilcChannelEntry)id_cache->context;
691
692   cmd->client->ops->say(cmd->client, conn, 
693                         "Topic on channel %s: %s", channel->channel_name,
694                         topic);
695
696   /* Notify application */
697   COMMAND_REPLY((ARGS, channel, topic));
698
699   /* Execute any pending command callbacks */
700   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_TOPIC);
701
702  out:
703   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_TOPIC);
704   silc_client_command_reply_free(cmd);
705 }
706
707 /* Received reply to invite command. */
708
709 SILC_CLIENT_CMD_REPLY_FUNC(invite)
710 {
711   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
712   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
713   SilcCommandStatus status;
714   SilcChannelEntry channel;
715   SilcChannelID *channel_id;
716   SilcIDCacheEntry id_cache;
717   unsigned char *tmp;
718   unsigned int len;
719
720   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
721   SILC_GET16_MSB(status, tmp);
722   if (status != SILC_STATUS_OK) {
723     cmd->client->ops->say(cmd->client, conn,
724              "%s", silc_client_command_status_message(status));
725     COMMAND_REPLY_ERROR;
726     SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INVITE);
727     silc_client_command_reply_free(cmd);
728     return;
729   }
730
731   /* Take Channel ID */
732   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
733   if (!tmp)
734     goto out;
735
736   channel_id = silc_id_payload_parse_id(tmp, len);
737   if (!channel_id)
738     goto out;
739
740   /* Get the channel entry */
741   if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
742                                    SILC_ID_CHANNEL, &id_cache)) {
743     silc_free(channel_id);
744     COMMAND_REPLY_ERROR;
745     goto out;
746   }
747   
748   channel = (SilcChannelEntry)id_cache->context;
749
750   /* Get the invite list */
751   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
752
753   /* Notify application */
754   COMMAND_REPLY((ARGS, channel, tmp));
755
756   /* Execute any pending command callbacks */
757   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INVITE);
758
759  out:
760   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INVITE);
761   silc_client_command_reply_free(cmd);
762 }
763
764 /* Received reply to the KILL command. */
765  
766 SILC_CLIENT_CMD_REPLY_FUNC(kill)
767 {
768   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
769   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
770   SilcCommandStatus status;
771   unsigned char *tmp;
772
773   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
774   SILC_GET16_MSB(status, tmp);
775   if (status != SILC_STATUS_OK) {
776     cmd->client->ops->say(cmd->client, conn,
777              "%s", silc_client_command_status_message(status));
778     COMMAND_REPLY_ERROR;
779     goto out;
780   }
781
782   /* Notify application */
783   COMMAND_REPLY((ARGS));
784
785   /* Execute any pending command callbacks */
786   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_KILL);
787
788  out:
789   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_KILL);
790   silc_client_command_reply_free(cmd);
791 }
792
793 /* Received reply to INFO command. We receive the server ID and some
794    information about the server user requested. */
795
796 SILC_CLIENT_CMD_REPLY_FUNC(info)
797 {
798   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
799   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
800   SilcClient client = cmd->client;
801   SilcCommandStatus status;
802   unsigned char *tmp;
803
804   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
805   SILC_GET16_MSB(status, tmp);
806   if (status != SILC_STATUS_OK) {
807     cmd->client->ops->say(cmd->client, conn,
808              "%s", silc_client_command_status_message(status));
809     COMMAND_REPLY_ERROR;
810     SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INFO);
811     silc_client_command_reply_free(cmd);
812     return;
813   }
814
815   /* Get server ID */
816   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
817   if (!tmp)
818     goto out;
819
820   /* XXX save server id */
821
822   /* Get server name */
823   tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
824   if (!tmp)
825     goto out;
826
827   /* Get server info */
828   tmp = silc_argument_get_arg_type(cmd->args, 4, NULL);
829   if (!tmp)
830     goto out;
831
832   client->ops->say(cmd->client, conn, "Info: %s", tmp);
833
834   /* Notify application */
835   COMMAND_REPLY((ARGS, NULL, (char *)tmp));
836
837   /* Execute any pending command callbacks */
838   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INFO);
839
840  out:
841   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INFO);
842   silc_client_command_reply_free(cmd);
843 }
844
845 /* Received reply to PING command. The reply time is shown to user. */
846
847 SILC_CLIENT_CMD_REPLY_FUNC(ping)
848 {
849   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
850   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
851   SilcCommandStatus status;
852   void *id;
853   int i;
854   time_t diff, curtime;
855
856   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
857   if (status != SILC_STATUS_OK) {
858     cmd->client->ops->say(cmd->client, conn,
859              "%s", silc_client_command_status_message(status));
860     COMMAND_REPLY_ERROR;
861     goto out;
862   }
863
864   curtime = time(NULL);
865   id = silc_id_str2id(cmd->packet->src_id, cmd->packet->src_id_len,
866                       cmd->packet->src_id_type);
867   if (!id) {
868     COMMAND_REPLY_ERROR;
869     goto out;
870   }
871
872   for (i = 0; i < conn->ping_count; i++) {
873     if (!SILC_ID_SERVER_COMPARE(conn->ping[i].dest_id, id)) {
874       diff = curtime - conn->ping[i].start_time;
875       cmd->client->ops->say(cmd->client, conn, 
876                             "Ping reply from %s: %d second%s", 
877                             conn->ping[i].dest_name, diff, 
878                             diff == 1 ? "" : "s");
879       
880       conn->ping[i].start_time = 0;
881       silc_free(conn->ping[i].dest_id);
882       conn->ping[i].dest_id = NULL;
883       silc_free(conn->ping[i].dest_name);
884       conn->ping[i].dest_name = NULL;
885       break;
886     }
887   }
888
889   silc_free(id);
890
891   /* Notify application */
892   COMMAND_REPLY((ARGS));
893
894   /* Execute any pending command callbacks */
895   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PING);
896
897  out:
898   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_PING);
899   silc_client_command_reply_free(cmd);
900 }
901
902 /* Received reply for JOIN command. */
903
904 SILC_CLIENT_CMD_REPLY_FUNC(join)
905 {
906   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
907   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
908   SilcCommandStatus status;
909   SilcIDPayload idp = NULL;
910   SilcChannelEntry channel;
911   SilcIDCacheEntry id_cache = NULL;
912   SilcChannelUser chu;
913   unsigned int argc, mode, len, list_count;
914   char *topic, *tmp, *channel_name = NULL, *hmac;
915   SilcBuffer keyp = NULL, client_id_list, client_mode_list;
916   int i;
917
918   SILC_LOG_DEBUG(("Start"));
919
920   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
921   if (status != SILC_STATUS_OK) {
922     cmd->client->ops->say(cmd->client, conn,
923              "%s", silc_client_command_status_message(status));
924     COMMAND_REPLY_ERROR;
925     goto out;
926   }
927
928   argc = silc_argument_get_arg_num(cmd->args);
929   if (argc < 7 || argc > 14) {
930     cmd->client->ops->say(cmd->client, conn,
931              "Cannot join channel: Bad reply packet");
932     COMMAND_REPLY_ERROR;
933     goto out;
934   }
935
936   /* Get channel name */
937   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
938   if (!tmp) {
939     cmd->client->ops->say(cmd->client, conn, 
940                           "Cannot join channel: Bad reply packet");
941     COMMAND_REPLY_ERROR;
942     goto out;
943   }
944   channel_name = strdup(tmp);
945
946   /* Get Channel ID */
947   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
948   if (!tmp) {
949     cmd->client->ops->say(cmd->client, conn, 
950                           "Cannot join channel: Bad reply packet");
951     COMMAND_REPLY_ERROR;
952     silc_free(channel_name);
953     goto out;
954   }
955   idp = silc_id_payload_parse_data(tmp, len);
956   if (!idp) {
957     COMMAND_REPLY_ERROR;
958     silc_free(channel_name);
959     goto out;
960   }
961
962   /* Get channel mode */
963   tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
964   if (tmp)
965     SILC_GET32_MSB(mode, tmp);
966   else
967     mode = 0;
968
969   /* Get channel key */
970   tmp = silc_argument_get_arg_type(cmd->args, 7, &len);
971   if (tmp) {
972     keyp = silc_buffer_alloc(len);
973     silc_buffer_pull_tail(keyp, SILC_BUFFER_END(keyp));
974     silc_buffer_put(keyp, tmp, len);
975   }
976
977   /* Get topic */
978   topic = silc_argument_get_arg_type(cmd->args, 10, NULL);
979
980   /* Save received Channel ID. This actually creates the channel */
981   channel = silc_client_new_channel_id(cmd->client, cmd->sock, channel_name, 
982                                        mode, idp);
983   silc_id_payload_free(idp);
984
985   /* Get hmac */
986   hmac = silc_argument_get_arg_type(cmd->args, 11, NULL);
987   if (hmac) {
988     if (!silc_hmac_alloc(hmac, NULL, &channel->hmac)) {
989       cmd->client->ops->say(cmd->client, conn, 
990                             "Cannot join channel: Unsupported HMAC `%s'",
991                             hmac);
992       COMMAND_REPLY_ERROR;
993       silc_free(channel_name);
994       goto out;
995     }
996   }
997
998   /* Get the list count */
999   tmp = silc_argument_get_arg_type(cmd->args, 12, &len);
1000   if (!tmp)
1001     goto out;
1002   SILC_GET32_MSB(list_count, tmp);
1003
1004   /* Get Client ID list */
1005   tmp = silc_argument_get_arg_type(cmd->args, 13, &len);
1006   if (!tmp)
1007     goto out;
1008
1009   client_id_list = silc_buffer_alloc(len);
1010   silc_buffer_pull_tail(client_id_list, len);
1011   silc_buffer_put(client_id_list, tmp, len);
1012
1013   /* Get client mode list */
1014   tmp = silc_argument_get_arg_type(cmd->args, 14, &len);
1015   if (!tmp)
1016     goto out;
1017
1018   client_mode_list = silc_buffer_alloc(len);
1019   silc_buffer_pull_tail(client_mode_list, len);
1020   silc_buffer_put(client_mode_list, tmp, len);
1021
1022   /* Add clients we received in the reply to the channel */
1023   for (i = 0; i < list_count; i++) {
1024     unsigned short idp_len;
1025     unsigned int mode;
1026     SilcClientID *client_id;
1027     SilcClientEntry client_entry;
1028
1029     /* Client ID */
1030     SILC_GET16_MSB(idp_len, client_id_list->data + 2);
1031     idp_len += 4;
1032     client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
1033     if (!client_id)
1034       continue;
1035
1036     /* Mode */
1037     SILC_GET32_MSB(mode, client_mode_list->data);
1038
1039     /* Check if we have this client cached already. */
1040     if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
1041                                      SILC_ID_CLIENT, &id_cache)) {
1042       /* No, we don't have it, add entry for it. */
1043       client_entry = silc_calloc(1, sizeof(*client_entry));
1044       client_entry->id = silc_id_dup(client_id, SILC_ID_CLIENT);
1045       silc_idcache_add(conn->client_cache, NULL, 0, SILC_ID_CLIENT, 
1046                        client_entry->id, (void *)client_entry, FALSE, FALSE);
1047     } else {
1048       /* Yes, we have it already */
1049       client_entry = (SilcClientEntry)id_cache->context;
1050     }
1051
1052     /* Join the client to the channel */
1053     chu = silc_calloc(1, sizeof(*chu));
1054     chu->client = client_entry;
1055     chu->mode = mode;
1056     silc_list_add(channel->clients, chu);
1057     silc_free(client_id);
1058
1059     silc_buffer_pull(client_id_list, idp_len);
1060     silc_buffer_pull(client_mode_list, 4);
1061   }
1062   silc_buffer_push(client_id_list, client_id_list->data - 
1063                    client_id_list->head);
1064   silc_buffer_push(client_mode_list, client_mode_list->data - 
1065                    client_mode_list->head);
1066
1067   /* Save channel key */
1068   if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY))
1069     silc_client_save_channel_key(conn, keyp, channel);
1070
1071   /* Client is now joined to the channel */
1072   channel->on_channel = TRUE;
1073
1074   /* Notify application */
1075   COMMAND_REPLY((ARGS, channel_name, channel, mode, 0, 
1076                  keyp ? keyp->head : NULL, NULL,
1077                  NULL, topic, hmac, list_count, client_id_list, 
1078                  client_mode_list));
1079
1080   /* Execute any pending command callbacks */
1081   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_JOIN);
1082
1083   if (keyp)
1084     silc_buffer_free(keyp);
1085   silc_buffer_free(client_id_list);
1086   silc_buffer_free(client_mode_list);
1087
1088  out:
1089   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_JOIN);
1090   silc_client_command_reply_free(cmd);
1091 }
1092
1093 /* Received reply for MOTD command */
1094
1095 SILC_CLIENT_CMD_REPLY_FUNC(motd)
1096 {
1097   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1098   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1099   SilcCommandStatus status;
1100   unsigned int argc, i;
1101   unsigned char *tmp;
1102   char *motd = NULL, *cp, line[256];
1103
1104   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1105   SILC_GET16_MSB(status, tmp);
1106   if (status != SILC_STATUS_OK) {
1107     cmd->client->ops->say(cmd->client, conn,
1108              "%s", silc_client_command_status_message(status));
1109     COMMAND_REPLY_ERROR;
1110     return;
1111   }
1112
1113   argc = silc_argument_get_arg_num(cmd->args);
1114   if (argc > 3) {
1115     COMMAND_REPLY_ERROR;
1116     goto out;
1117   }
1118
1119   if (argc == 3) {
1120     motd = silc_argument_get_arg_type(cmd->args, 3, NULL);
1121     if (!motd) {
1122       COMMAND_REPLY_ERROR;
1123       goto out;
1124     }
1125
1126     i = 0;
1127     cp = motd;
1128     while(cp[i] != 0) {
1129       if (cp[i++] == '\n') {
1130         memset(line, 0, sizeof(line));
1131         strncat(line, cp, i - 1);
1132         cp += i;
1133         
1134         if (i == 2)
1135           line[0] = ' ';
1136         
1137         cmd->client->ops->say(cmd->client, conn, "%s", line);
1138         
1139         if (!strlen(cp))
1140           break;
1141         i = 0;
1142       }
1143     }
1144   }
1145
1146   /* Notify application */
1147   COMMAND_REPLY((ARGS, motd));
1148
1149   /* Execute any pending command callbacks */
1150   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_MOTD);
1151
1152  out:
1153   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_MOTD);
1154   silc_client_command_reply_free(cmd);
1155 }
1156
1157 /* Received reply tot he UMODE command. Save the current user mode */
1158
1159 SILC_CLIENT_CMD_REPLY_FUNC(umode)
1160 {
1161   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1162   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1163   SilcCommandStatus status;
1164   unsigned char *tmp;
1165   unsigned int mode;
1166
1167   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1168   SILC_GET16_MSB(status, tmp);
1169   if (status != SILC_STATUS_OK) {
1170     cmd->client->ops->say(cmd->client, conn,
1171              "%s", silc_client_command_status_message(status));
1172     COMMAND_REPLY_ERROR;
1173     goto out;
1174   }
1175
1176   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
1177   if (!tmp) {
1178     COMMAND_REPLY_ERROR;
1179     goto out;
1180   }
1181
1182   SILC_GET32_MSB(mode, tmp);
1183   conn->local_entry->mode = mode;
1184
1185   /* Notify application */
1186   COMMAND_REPLY((ARGS, mode));
1187
1188   /* Execute any pending command callbacks */
1189   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_UMODE);
1190
1191  out:
1192   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_UMODE);
1193   silc_client_command_reply_free(cmd);
1194 }
1195
1196 /* Received reply for CMODE command. */
1197
1198 SILC_CLIENT_CMD_REPLY_FUNC(cmode)
1199 {
1200   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1201   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1202   SilcCommandStatus status;
1203   unsigned char *tmp;
1204
1205   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
1206   if (status != SILC_STATUS_OK) {
1207     cmd->client->ops->say(cmd->client, conn,
1208              "%s", silc_client_command_status_message(status));
1209     COMMAND_REPLY_ERROR;
1210     goto out;
1211   }
1212
1213   /* Get channel mode */
1214   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
1215   if (!tmp) {
1216     COMMAND_REPLY_ERROR;
1217     goto out;
1218   }
1219
1220   /* Notify application */
1221   COMMAND_REPLY((ARGS, tmp));
1222
1223   /* Execute any pending command callbacks */
1224   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CMODE);
1225
1226  out:
1227   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CMODE);
1228   silc_client_command_reply_free(cmd);
1229 }
1230
1231 /* Received reply for CUMODE command */
1232
1233 SILC_CLIENT_CMD_REPLY_FUNC(cumode)
1234 {
1235   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1236   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1237   SilcCommandStatus status;
1238   SilcIDCacheEntry id_cache = NULL;
1239   SilcClientID *client_id;
1240   unsigned char *tmp, *id;
1241   unsigned int len;
1242   
1243   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
1244   if (status != SILC_STATUS_OK) {
1245     cmd->client->ops->say(cmd->client, conn,
1246              "%s", silc_client_command_status_message(status));
1247     COMMAND_REPLY_ERROR;
1248     goto out;
1249   }
1250   
1251   /* Get channel mode */
1252   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
1253   if (!tmp) {
1254     COMMAND_REPLY_ERROR;
1255     goto out;
1256   }
1257
1258   /* Get Client ID */
1259   id = silc_argument_get_arg_type(cmd->args, 3, &len);
1260   if (!id) {
1261     COMMAND_REPLY_ERROR;
1262     goto out;
1263   }
1264   client_id = silc_id_payload_parse_id(id, len);
1265   if (!client_id) {
1266     COMMAND_REPLY_ERROR;
1267     goto out;
1268   }
1269   
1270   /* Get client entry */
1271   if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
1272                                    SILC_ID_CLIENT, &id_cache)) {
1273     COMMAND_REPLY_ERROR;
1274     goto out;
1275   }
1276
1277   /* Notify application */
1278   COMMAND_REPLY((ARGS, tmp, (SilcClientEntry)id_cache->context));
1279   silc_free(client_id);
1280   
1281   /* Execute any pending command callbacks */
1282   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CUMODE);
1283
1284  out:
1285   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CUMODE);
1286   silc_client_command_reply_free(cmd);
1287 }
1288
1289 SILC_CLIENT_CMD_REPLY_FUNC(kick)
1290 {
1291   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1292   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1293   SilcCommandStatus status;
1294   unsigned char *tmp;
1295
1296   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1297   SILC_GET16_MSB(status, tmp);
1298   if (status != SILC_STATUS_OK) {
1299     cmd->client->ops->say(cmd->client, conn,
1300              "%s", silc_client_command_status_message(status));
1301     COMMAND_REPLY_ERROR;
1302     goto out;
1303   }
1304
1305   /* Notify application */
1306   COMMAND_REPLY((ARGS));
1307
1308   /* Execute any pending command callbacks */
1309   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_KICK);
1310
1311  out:
1312   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_KICK);
1313   silc_client_command_reply_free(cmd);
1314 }
1315
1316 SILC_CLIENT_CMD_REPLY_FUNC(silcoper)
1317 {
1318   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1319   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1320   SilcCommandStatus status;
1321   unsigned char *tmp;
1322
1323   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1324   SILC_GET16_MSB(status, tmp);
1325   if (status != SILC_STATUS_OK) {
1326     cmd->client->ops->say(cmd->client, conn,
1327              "%s", silc_client_command_status_message(status));
1328     COMMAND_REPLY_ERROR;
1329     goto out;
1330   }
1331
1332   /* Notify application */
1333   COMMAND_REPLY((ARGS));
1334
1335   /* Execute any pending command callbacks */
1336   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_SILCOPER);
1337
1338  out:
1339   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_SILCOPER);
1340   silc_client_command_reply_free(cmd);
1341 }
1342
1343 SILC_CLIENT_CMD_REPLY_FUNC(oper)
1344 {
1345   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1346   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1347   SilcCommandStatus status;
1348   unsigned char *tmp;
1349
1350   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1351   SILC_GET16_MSB(status, tmp);
1352   if (status != SILC_STATUS_OK) {
1353     cmd->client->ops->say(cmd->client, conn,
1354              "%s", silc_client_command_status_message(status));
1355     COMMAND_REPLY_ERROR;
1356     goto out;
1357   }
1358
1359   /* Notify application */
1360   COMMAND_REPLY((ARGS));
1361
1362   /* Execute any pending command callbacks */
1363   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_OPER);
1364
1365  out:
1366   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_OPER);
1367   silc_client_command_reply_free(cmd);
1368 }
1369
1370 SILC_CLIENT_CMD_REPLY_FUNC(connect)
1371 {
1372   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1373   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1374   SilcCommandStatus status;
1375   unsigned char *tmp;
1376
1377   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1378   SILC_GET16_MSB(status, tmp);
1379   if (status != SILC_STATUS_OK) {
1380     cmd->client->ops->say(cmd->client, conn,
1381              "%s", silc_client_command_status_message(status));
1382     COMMAND_REPLY_ERROR;
1383     goto out;
1384   }
1385
1386   /* Notify application */
1387   COMMAND_REPLY((ARGS));
1388
1389   /* Execute any pending command callbacks */
1390   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CONNECT);
1391
1392  out:
1393   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CONNECT);
1394   silc_client_command_reply_free(cmd);
1395 }
1396
1397 SILC_CLIENT_CMD_REPLY_FUNC(close)
1398 {
1399   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1400   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1401   SilcCommandStatus status;
1402   unsigned char *tmp;
1403
1404   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1405   SILC_GET16_MSB(status, tmp);
1406   if (status != SILC_STATUS_OK) {
1407     cmd->client->ops->say(cmd->client, conn,
1408              "%s", silc_client_command_status_message(status));
1409     COMMAND_REPLY_ERROR;
1410     goto out;
1411   }
1412
1413   /* Notify application */
1414   COMMAND_REPLY((ARGS));
1415
1416   /* Execute any pending command callbacks */
1417   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CLOSE);
1418
1419  out:
1420   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CLOSE);
1421   silc_client_command_reply_free(cmd);
1422 }
1423  
1424 SILC_CLIENT_CMD_REPLY_FUNC(shutdown)
1425 {
1426   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1427   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1428   SilcCommandStatus status;
1429   unsigned char *tmp;
1430
1431   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1432   SILC_GET16_MSB(status, tmp);
1433   if (status != SILC_STATUS_OK) {
1434     cmd->client->ops->say(cmd->client, conn,
1435              "%s", silc_client_command_status_message(status));
1436     COMMAND_REPLY_ERROR;
1437     goto out;
1438   }
1439
1440   /* Notify application */
1441   COMMAND_REPLY((ARGS));
1442
1443   /* Execute any pending command callbacks */
1444   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_SHUTDOWN);
1445
1446  out:
1447   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_SHUTDOWN);
1448   silc_client_command_reply_free(cmd);
1449 }
1450  
1451 /* Reply to LEAVE command. */
1452
1453 SILC_CLIENT_CMD_REPLY_FUNC(leave)
1454 {
1455   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1456   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1457   SilcCommandStatus status;
1458   unsigned char *tmp;
1459
1460   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1461   SILC_GET16_MSB(status, tmp);
1462   if (status != SILC_STATUS_OK) {
1463     cmd->client->ops->say(cmd->client, conn,
1464              "%s", silc_client_command_status_message(status));
1465     COMMAND_REPLY_ERROR;
1466     goto out;
1467   }
1468
1469   /* Notify application */
1470   COMMAND_REPLY((ARGS));
1471
1472   /* Execute any pending command callbacks */
1473   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_LEAVE);
1474
1475  out:
1476   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_LEAVE);
1477   silc_client_command_reply_free(cmd);
1478 }
1479
1480 /* Reply to USERS command. Received list of client ID's and theirs modes
1481    on the channel we requested. */
1482
1483 SILC_CLIENT_CMD_REPLY_FUNC(users)
1484 {
1485   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1486   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1487   SilcCommandStatus status;
1488   SilcIDCacheEntry id_cache = NULL;
1489   SilcChannelEntry channel;
1490   SilcChannelUser chu;
1491   SilcChannelID *channel_id = NULL;
1492   SilcBuffer client_id_list;
1493   SilcBuffer client_mode_list;
1494   unsigned char *tmp;
1495   unsigned int tmp_len, list_count;
1496   int i;
1497   unsigned char **res_argv = NULL;
1498   unsigned int *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
1499
1500   SILC_LOG_DEBUG(("Start"));
1501
1502   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1503   SILC_GET16_MSB(status, tmp);
1504   if (status != SILC_STATUS_OK) {
1505     cmd->client->ops->say(cmd->client, conn,
1506              "%s", silc_client_command_status_message(status));
1507     COMMAND_REPLY_ERROR;
1508     goto out;
1509   }
1510
1511   /* Get channel ID */
1512   tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
1513   if (!tmp)
1514     goto out;
1515   channel_id = silc_id_payload_parse_id(tmp, tmp_len);
1516   if (!channel_id)
1517     goto out;
1518
1519   /* Get the list count */
1520   tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
1521   if (!tmp)
1522     goto out;
1523   SILC_GET32_MSB(list_count, tmp);
1524
1525   /* Get Client ID list */
1526   tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
1527   if (!tmp)
1528     goto out;
1529
1530   client_id_list = silc_buffer_alloc(tmp_len);
1531   silc_buffer_pull_tail(client_id_list, tmp_len);
1532   silc_buffer_put(client_id_list, tmp, tmp_len);
1533
1534   /* Get client mode list */
1535   tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
1536   if (!tmp)
1537     goto out;
1538
1539   client_mode_list = silc_buffer_alloc(tmp_len);
1540   silc_buffer_pull_tail(client_mode_list, tmp_len);
1541   silc_buffer_put(client_mode_list, tmp, tmp_len);
1542
1543   /* Get channel entry */
1544   if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
1545                                    SILC_ID_CHANNEL, &id_cache)) {
1546     COMMAND_REPLY_ERROR;
1547     goto out;
1548   }
1549   channel = (SilcChannelEntry)id_cache->context;
1550
1551   /* Remove old client list from channel. */
1552   silc_list_start(channel->clients);
1553   while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1554     silc_list_del(channel->clients, chu);
1555     silc_free(chu);
1556   }
1557
1558   /* Cache the received Client ID's and modes. This cache expires
1559      whenever server sends notify message to channel. It means two things;
1560      some user has joined or leaved the channel. XXX! */
1561   for (i = 0; i < list_count; i++) {
1562     unsigned short idp_len;
1563     unsigned int mode;
1564     SilcClientID *client_id;
1565     SilcClientEntry client;
1566
1567     /* Client ID */
1568     SILC_GET16_MSB(idp_len, client_id_list->data + 2);
1569     idp_len += 4;
1570     client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
1571     if (!client_id)
1572       continue;
1573
1574     /* Mode */
1575     SILC_GET32_MSB(mode, client_mode_list->data);
1576
1577     /* Check if we have this client cached already. */
1578     if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
1579                                      SILC_ID_CLIENT, &id_cache)) {
1580       /* No we don't have it, query it from the server. Assemble argument
1581          table that will be sent fr the IDENTIFY command later. */
1582       res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
1583                               (res_argc + 1));
1584       res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
1585                                    (res_argc + 1));
1586       res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
1587                                     (res_argc + 1));
1588       res_argv[res_argc] = client_id_list->data;
1589       res_argv_lens[res_argc] = idp_len;
1590       res_argv_types[res_argc] = res_argc + 3;
1591       res_argc++;
1592     } else {
1593       /* Found the client, join it to the channel */
1594       client = (SilcClientEntry)id_cache->context;
1595       chu = silc_calloc(1, sizeof(*chu));
1596       chu->client = client;
1597       chu->mode = mode;
1598       silc_list_add(channel->clients, chu);
1599
1600       silc_free(client_id);
1601       id_cache = NULL;
1602     }
1603
1604     silc_buffer_pull(client_id_list, idp_len);
1605     silc_buffer_pull(client_mode_list, 4);
1606   }
1607
1608   /* Query the client information from server if the list included clients
1609      that we don't know about. */
1610   if (res_argc) {
1611     SilcBuffer res_cmd;
1612
1613     /* Send the IDENTIFY command to server */
1614     res_cmd = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
1615                                           res_argc, res_argv, res_argv_lens,
1616                                           res_argv_types, ++conn->cmd_ident);
1617     silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, 
1618                             NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
1619                             TRUE);
1620
1621     /* Register pending command callback. After we've received the IDENTIFY
1622        command reply we will reprocess this command reply by re-calling this
1623        USERS command reply callback. */
1624     silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
1625                                 NULL, silc_client_command_reply_users, cmd);
1626
1627     silc_buffer_free(res_cmd);
1628     if (channel_id)
1629       silc_free(channel_id);
1630
1631     silc_free(res_argv);
1632     silc_free(res_argv_lens);
1633     silc_free(res_argv_types);
1634     return;
1635   }
1636
1637   /* Notify application */
1638   COMMAND_REPLY((ARGS, channel, list_count, client_id_list, client_mode_list));
1639
1640   /* Execute any pending command callbacks */
1641   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
1642
1643   silc_buffer_free(client_id_list);
1644   silc_buffer_free(client_mode_list);
1645
1646  out:
1647   if (channel_id)
1648     silc_free(channel_id);
1649   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_USERS);
1650   silc_client_command_reply_free(cmd);
1651 }
1652
1653 SILC_CLIENT_CMD_REPLY_FUNC(ban)
1654 {
1655   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1656   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1657   SilcCommandStatus status;
1658   SilcIDCacheEntry id_cache = NULL;
1659   SilcChannelEntry channel;
1660   SilcChannelID *channel_id;
1661   unsigned char *tmp;
1662   unsigned int len;
1663
1664   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1665   SILC_GET16_MSB(status, tmp);
1666   if (status != SILC_STATUS_OK) {
1667     cmd->client->ops->say(cmd->client, conn,
1668              "%s", silc_client_command_status_message(status));
1669     COMMAND_REPLY_ERROR;
1670     goto out;
1671   }
1672
1673   /* Take Channel ID */
1674   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1675   if (!tmp)
1676     goto out;
1677
1678   channel_id = silc_id_payload_parse_id(tmp, len);
1679   if (!channel_id)
1680     goto out;
1681
1682   /* Get the channel entry */
1683   if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
1684                                    SILC_ID_CHANNEL, &id_cache)) {
1685     silc_free(channel_id);
1686     COMMAND_REPLY_ERROR;
1687     goto out;
1688   }
1689   
1690   channel = (SilcChannelEntry)id_cache->context;
1691
1692   /* Get the ban list */
1693   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
1694
1695   /* Notify application */
1696   COMMAND_REPLY((ARGS, channel, tmp));
1697
1698   /* Execute any pending command callbacks */
1699   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_BAN);
1700
1701  out:
1702   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_BAN);
1703   silc_client_command_reply_free(cmd);
1704 }