updates.
[silc.git] / lib / silcclient / command_reply.c
1 /*
2
3   command_reply.c
4
5   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
6
7   Copyright (C) 1997 - 2001 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; either version 2 of the License, or
12   (at your option) any later version.
13   
14   This program is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   GNU General Public License for more details.
18
19 */
20 /*
21  * Command reply functions are "the otherside" of the command functions.
22  * Reply to a command sent by server is handled by these functions.
23  *
24  * The arguments received from server are also passed to the calling
25  * application through command_reply client operation.  The arguments are
26  * exactly same and in same order as the server sent it.  However, ID's are
27  * not sent to the application.  Instead, corresponding ID entry is sent
28  * to the application.  For example, instead of sending Client ID the 
29  * corresponding SilcClientEntry is sent to the application.  The case is
30  * same with for example Channel ID's.  This way application has all the
31  * necessary data already in hand without redundant searching.  If ID is
32  * received but ID entry does not exist, NULL is sent.
33  */
34 /* $Id$ */
35
36 #include "clientlibincludes.h"
37 #include "client_internal.h"
38
39 /* Client command reply list. */
40 SilcClientCommandReply silc_command_reply_list[] =
41 {
42   SILC_CLIENT_CMD_REPLY(whois, WHOIS),
43   SILC_CLIENT_CMD_REPLY(whowas, WHOWAS),
44   SILC_CLIENT_CMD_REPLY(identify, IDENTIFY),
45   SILC_CLIENT_CMD_REPLY(nick, NICK),
46   SILC_CLIENT_CMD_REPLY(list, LIST),
47   SILC_CLIENT_CMD_REPLY(topic, TOPIC),
48   SILC_CLIENT_CMD_REPLY(invite, INVITE),
49   SILC_CLIENT_CMD_REPLY(kill, KILL),
50   SILC_CLIENT_CMD_REPLY(info, INFO),
51   SILC_CLIENT_CMD_REPLY(connect, CONNECT),
52   SILC_CLIENT_CMD_REPLY(ping, PING),
53   SILC_CLIENT_CMD_REPLY(oper, OPER),
54   SILC_CLIENT_CMD_REPLY(join, JOIN),
55   SILC_CLIENT_CMD_REPLY(motd, MOTD),
56   SILC_CLIENT_CMD_REPLY(umode, UMODE),
57   SILC_CLIENT_CMD_REPLY(cmode, CMODE),
58   SILC_CLIENT_CMD_REPLY(cumode, CUMODE),
59   SILC_CLIENT_CMD_REPLY(kick, KICK),
60   SILC_CLIENT_CMD_REPLY(restart, RESTART),
61   SILC_CLIENT_CMD_REPLY(close, CLOSE),
62   SILC_CLIENT_CMD_REPLY(shutdown, SHUTDOWN),
63   SILC_CLIENT_CMD_REPLY(silcoper, SILCOPER),
64   SILC_CLIENT_CMD_REPLY(leave, LEAVE),
65   SILC_CLIENT_CMD_REPLY(users, USERS),
66
67   { NULL, 0 },
68 };
69
70 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 name */
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   unsigned char *tmp;
715
716   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
717   SILC_GET16_MSB(status, tmp);
718   if (status != SILC_STATUS_OK) {
719     cmd->client->ops->say(cmd->client, conn,
720              "%s", silc_client_command_status_message(status));
721     COMMAND_REPLY_ERROR;
722     SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INVITE);
723     silc_client_command_reply_free(cmd);
724     return;
725   }
726
727   /* Notify application */
728   COMMAND_REPLY((ARGS));
729
730   /* Execute any pending command callbacks */
731   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INVITE);
732
733   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INVITE);
734   silc_client_command_reply_free(cmd);
735 }
736
737 /* Received reply to the KILL command. */
738  
739 SILC_CLIENT_CMD_REPLY_FUNC(kill)
740 {
741   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
742   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
743   SilcCommandStatus status;
744   unsigned char *tmp;
745
746   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
747   SILC_GET16_MSB(status, tmp);
748   if (status != SILC_STATUS_OK) {
749     cmd->client->ops->say(cmd->client, conn,
750              "%s", silc_client_command_status_message(status));
751     COMMAND_REPLY_ERROR;
752     goto out;
753   }
754
755   /* Notify application */
756   COMMAND_REPLY((ARGS));
757
758   /* Execute any pending command callbacks */
759   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_KILL);
760
761  out:
762   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_KILL);
763   silc_client_command_reply_free(cmd);
764 }
765
766 /* Received reply to INFO command. We receive the server ID and some
767    information about the server user requested. */
768
769 SILC_CLIENT_CMD_REPLY_FUNC(info)
770 {
771   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
772   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
773   SilcClient client = cmd->client;
774   SilcCommandStatus status;
775   unsigned char *tmp;
776
777   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
778   SILC_GET16_MSB(status, tmp);
779   if (status != SILC_STATUS_OK) {
780     cmd->client->ops->say(cmd->client, conn,
781              "%s", silc_client_command_status_message(status));
782     COMMAND_REPLY_ERROR;
783     SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INFO);
784     silc_client_command_reply_free(cmd);
785     return;
786   }
787
788   /* Get server ID */
789   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
790   if (!tmp)
791     goto out;
792
793   /* XXX save server id */
794
795   /* Get server name */
796   tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
797   if (!tmp)
798     goto out;
799
800   /* Get server info */
801   tmp = silc_argument_get_arg_type(cmd->args, 4, NULL);
802   if (!tmp)
803     goto out;
804
805   client->ops->say(cmd->client, conn, "Info: %s", tmp);
806
807   /* Notify application */
808   COMMAND_REPLY((ARGS, NULL, (char *)tmp));
809
810   /* Execute any pending command callbacks */
811   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INFO);
812
813  out:
814   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INFO);
815   silc_client_command_reply_free(cmd);
816 }
817
818 /* Received reply to PING command. The reply time is shown to user. */
819
820 SILC_CLIENT_CMD_REPLY_FUNC(ping)
821 {
822   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
823   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
824   SilcCommandStatus status;
825   void *id;
826   int i;
827   time_t diff, curtime;
828
829   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
830   if (status != SILC_STATUS_OK) {
831     cmd->client->ops->say(cmd->client, conn,
832              "%s", silc_client_command_status_message(status));
833     COMMAND_REPLY_ERROR;
834     goto out;
835   }
836
837   curtime = time(NULL);
838   id = silc_id_str2id(cmd->packet->src_id, cmd->packet->src_id_len,
839                       cmd->packet->src_id_type);
840   if (!id) {
841     COMMAND_REPLY_ERROR;
842     goto out;
843   }
844
845   for (i = 0; i < conn->ping_count; i++) {
846     if (!SILC_ID_SERVER_COMPARE(conn->ping[i].dest_id, id)) {
847       diff = curtime - conn->ping[i].start_time;
848       cmd->client->ops->say(cmd->client, conn, 
849                             "Ping reply from %s: %d second%s", 
850                             conn->ping[i].dest_name, diff, 
851                             diff == 1 ? "" : "s");
852       
853       conn->ping[i].start_time = 0;
854       silc_free(conn->ping[i].dest_id);
855       conn->ping[i].dest_id = NULL;
856       silc_free(conn->ping[i].dest_name);
857       conn->ping[i].dest_name = NULL;
858       break;
859     }
860   }
861
862   silc_free(id);
863
864   /* Notify application */
865   COMMAND_REPLY((ARGS));
866
867   /* Execute any pending command callbacks */
868   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PING);
869
870  out:
871   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_PING);
872   silc_client_command_reply_free(cmd);
873 }
874
875 /* Received reply for JOIN command. */
876
877 SILC_CLIENT_CMD_REPLY_FUNC(join)
878 {
879   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
880   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
881   SilcCommandStatus status;
882   SilcIDPayload idp = NULL;
883   SilcChannelEntry channel;
884   SilcIDCacheEntry id_cache = NULL;
885   SilcChannelUser chu;
886   unsigned int argc, mode, len, list_count;
887   char *topic, *tmp, *channel_name = NULL, *hmac;
888   SilcBuffer keyp, client_id_list, client_mode_list;
889   int i;
890
891   SILC_LOG_DEBUG(("Start"));
892
893   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
894   if (status != SILC_STATUS_OK) {
895     cmd->client->ops->say(cmd->client, conn,
896              "%s", silc_client_command_status_message(status));
897     COMMAND_REPLY_ERROR;
898     goto out;
899   }
900
901   argc = silc_argument_get_arg_num(cmd->args);
902   if (argc < 7 || argc > 14) {
903     cmd->client->ops->say(cmd->client, conn,
904              "Cannot join channel: Bad reply packet");
905     COMMAND_REPLY_ERROR;
906     goto out;
907   }
908
909   /* Get channel name */
910   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
911   if (!tmp) {
912     cmd->client->ops->say(cmd->client, conn, 
913                           "Cannot join channel: Bad reply packet");
914     COMMAND_REPLY_ERROR;
915     goto out;
916   }
917   channel_name = strdup(tmp);
918
919   /* Get Channel ID */
920   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
921   if (!tmp) {
922     cmd->client->ops->say(cmd->client, conn, 
923                           "Cannot join channel: Bad reply packet");
924     COMMAND_REPLY_ERROR;
925     silc_free(channel_name);
926     goto out;
927   }
928   idp = silc_id_payload_parse_data(tmp, len);
929   if (!idp) {
930     COMMAND_REPLY_ERROR;
931     silc_free(channel_name);
932     goto out;
933   }
934
935   /* Get channel mode */
936   tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
937   if (tmp)
938     SILC_GET32_MSB(mode, tmp);
939   else
940     mode = 0;
941
942   /* Get channel key */
943   tmp = silc_argument_get_arg_type(cmd->args, 7, &len);
944   if (!tmp) {
945     silc_id_payload_free(idp);
946     silc_free(channel_name);
947     goto out;
948   }
949   keyp = silc_buffer_alloc(len);
950   silc_buffer_pull_tail(keyp, SILC_BUFFER_END(keyp));
951   silc_buffer_put(keyp, tmp, len);
952
953   /* Get topic */
954   topic = silc_argument_get_arg_type(cmd->args, 10, NULL);
955
956   /* Save received Channel ID. This actually creates the channel */
957   channel = silc_client_new_channel_id(cmd->client, cmd->sock, channel_name, 
958                                        mode, idp);
959   silc_id_payload_free(idp);
960
961   /* Get hmac */
962   hmac = silc_argument_get_arg_type(cmd->args, 11, NULL);
963   if (hmac) {
964     if (!silc_hmac_alloc(hmac, NULL, &channel->hmac)) {
965       cmd->client->ops->say(cmd->client, conn, 
966                             "Cannot join channel: Unsupported HMAC `%s'",
967                             hmac);
968       COMMAND_REPLY_ERROR;
969       silc_free(channel_name);
970       goto out;
971     }
972   }
973
974   /* Get the list count */
975   tmp = silc_argument_get_arg_type(cmd->args, 12, &len);
976   if (!tmp)
977     goto out;
978   SILC_GET32_MSB(list_count, tmp);
979
980   /* Get Client ID list */
981   tmp = silc_argument_get_arg_type(cmd->args, 13, &len);
982   if (!tmp)
983     goto out;
984
985   client_id_list = silc_buffer_alloc(len);
986   silc_buffer_pull_tail(client_id_list, len);
987   silc_buffer_put(client_id_list, tmp, len);
988
989   /* Get client mode list */
990   tmp = silc_argument_get_arg_type(cmd->args, 14, &len);
991   if (!tmp)
992     goto out;
993
994   client_mode_list = silc_buffer_alloc(len);
995   silc_buffer_pull_tail(client_mode_list, len);
996   silc_buffer_put(client_mode_list, tmp, len);
997
998   /* Add clients we received in the reply to the channel */
999   for (i = 0; i < list_count; i++) {
1000     unsigned short idp_len;
1001     unsigned int mode;
1002     SilcClientID *client_id;
1003     SilcClientEntry client_entry;
1004
1005     /* Client ID */
1006     SILC_GET16_MSB(idp_len, client_id_list->data + 2);
1007     idp_len += 4;
1008     client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
1009     if (!client_id)
1010       continue;
1011
1012     /* Mode */
1013     SILC_GET32_MSB(mode, client_mode_list->data);
1014
1015     /* Check if we have this client cached already. */
1016     if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
1017                                      SILC_ID_CLIENT, &id_cache)) {
1018       /* No, we don't have it, add entry for it. */
1019       client_entry = silc_calloc(1, sizeof(*client_entry));
1020       client_entry->id = silc_id_dup(client_id, SILC_ID_CLIENT);
1021       silc_idcache_add(conn->client_cache, NULL, 0, SILC_ID_CLIENT, 
1022                        client_entry->id, (void *)client_entry, FALSE, FALSE);
1023     } else {
1024       /* Yes, we have it already */
1025       client_entry = (SilcClientEntry)id_cache->context;
1026     }
1027
1028     /* Join the client to the channel */
1029     chu = silc_calloc(1, sizeof(*chu));
1030     chu->client = client_entry;
1031     chu->mode = mode;
1032     silc_list_add(channel->clients, chu);
1033     silc_free(client_id);
1034
1035     silc_buffer_pull(client_id_list, idp_len);
1036     silc_buffer_pull(client_mode_list, 4);
1037   }
1038   silc_buffer_push(client_id_list, client_id_list->data - 
1039                    client_id_list->head);
1040   silc_buffer_push(client_mode_list, client_mode_list->data - 
1041                    client_mode_list->head);
1042
1043   /* Save channel key */
1044   silc_client_save_channel_key(conn, keyp, channel);
1045
1046   /* Notify application */
1047   COMMAND_REPLY((ARGS, channel_name, channel, mode, 0, keyp->head, NULL,
1048                  NULL, topic, hmac, list_count, client_id_list, 
1049                  client_mode_list));
1050
1051   /* Execute any pending command callbacks */
1052   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_JOIN);
1053
1054   silc_buffer_free(keyp);
1055   silc_buffer_free(client_id_list);
1056   silc_buffer_free(client_mode_list);
1057
1058  out:
1059   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_JOIN);
1060   silc_client_command_reply_free(cmd);
1061 }
1062
1063 /* Received reply for MOTD command */
1064
1065 SILC_CLIENT_CMD_REPLY_FUNC(motd)
1066 {
1067   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1068   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1069   SilcCommandStatus status;
1070   unsigned int argc, i;
1071   unsigned char *tmp;
1072   char *motd = NULL, *cp, line[256];
1073
1074   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1075   SILC_GET16_MSB(status, tmp);
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     return;
1081   }
1082
1083   argc = silc_argument_get_arg_num(cmd->args);
1084   if (argc > 3) {
1085     COMMAND_REPLY_ERROR;
1086     goto out;
1087   }
1088
1089   if (argc == 3) {
1090     motd = silc_argument_get_arg_type(cmd->args, 3, NULL);
1091     if (!motd) {
1092       COMMAND_REPLY_ERROR;
1093       goto out;
1094     }
1095
1096     i = 0;
1097     cp = motd;
1098     while(cp[i] != 0) {
1099       if (cp[i++] == '\n') {
1100         memset(line, 0, sizeof(line));
1101         strncat(line, cp, i - 1);
1102         cp += i;
1103         
1104         if (i == 2)
1105           line[0] = ' ';
1106         
1107         cmd->client->ops->say(cmd->client, conn, "%s", line);
1108         
1109         if (!strlen(cp))
1110           break;
1111         i = 0;
1112       }
1113     }
1114   }
1115
1116   /* Notify application */
1117   COMMAND_REPLY((ARGS, motd));
1118
1119   /* Execute any pending command callbacks */
1120   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_MOTD);
1121
1122  out:
1123   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_MOTD);
1124   silc_client_command_reply_free(cmd);
1125 }
1126
1127 /* Received reply tot he UMODE command. Save the current user mode */
1128
1129 SILC_CLIENT_CMD_REPLY_FUNC(umode)
1130 {
1131   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1132   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1133   SilcCommandStatus status;
1134   unsigned char *tmp;
1135   unsigned int mode;
1136
1137   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1138   SILC_GET16_MSB(status, tmp);
1139   if (status != SILC_STATUS_OK) {
1140     cmd->client->ops->say(cmd->client, conn,
1141              "%s", silc_client_command_status_message(status));
1142     COMMAND_REPLY_ERROR;
1143     goto out;
1144   }
1145
1146   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
1147   if (!tmp) {
1148     COMMAND_REPLY_ERROR;
1149     goto out;
1150   }
1151
1152   SILC_GET32_MSB(mode, tmp);
1153   conn->local_entry->mode = mode;
1154
1155   /* Notify application */
1156   COMMAND_REPLY((ARGS, mode));
1157
1158   /* Execute any pending command callbacks */
1159   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_UMODE);
1160
1161  out:
1162   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_UMODE);
1163   silc_client_command_reply_free(cmd);
1164 }
1165
1166 /* Received reply for CMODE command. */
1167
1168 SILC_CLIENT_CMD_REPLY_FUNC(cmode)
1169 {
1170   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1171   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1172   SilcCommandStatus status;
1173   unsigned char *tmp;
1174
1175   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
1176   if (status != SILC_STATUS_OK) {
1177     cmd->client->ops->say(cmd->client, conn,
1178              "%s", silc_client_command_status_message(status));
1179     COMMAND_REPLY_ERROR;
1180     goto out;
1181   }
1182
1183   /* Get channel mode */
1184   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
1185   if (!tmp) {
1186     COMMAND_REPLY_ERROR;
1187     goto out;
1188   }
1189
1190   /* Notify application */
1191   COMMAND_REPLY((ARGS, tmp));
1192
1193   /* Execute any pending command callbacks */
1194   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CMODE);
1195
1196  out:
1197   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CMODE);
1198   silc_client_command_reply_free(cmd);
1199 }
1200
1201 /* Received reply for CUMODE command */
1202
1203 SILC_CLIENT_CMD_REPLY_FUNC(cumode)
1204 {
1205   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1206   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1207   SilcCommandStatus status;
1208   SilcIDCacheEntry id_cache = NULL;
1209   SilcClientID *client_id;
1210   unsigned char *tmp, *id;
1211   unsigned int len;
1212   
1213   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
1214   if (status != SILC_STATUS_OK) {
1215     cmd->client->ops->say(cmd->client, conn,
1216              "%s", silc_client_command_status_message(status));
1217     COMMAND_REPLY_ERROR;
1218     goto out;
1219   }
1220   
1221   /* Get channel mode */
1222   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
1223   if (!tmp) {
1224     COMMAND_REPLY_ERROR;
1225     goto out;
1226   }
1227
1228   /* Get Client ID */
1229   id = silc_argument_get_arg_type(cmd->args, 3, &len);
1230   if (!id) {
1231     COMMAND_REPLY_ERROR;
1232     goto out;
1233   }
1234   client_id = silc_id_payload_parse_id(id, len);
1235   if (!client_id) {
1236     COMMAND_REPLY_ERROR;
1237     goto out;
1238   }
1239   
1240   /* Get client entry */
1241   if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
1242                                    SILC_ID_CLIENT, &id_cache)) {
1243     COMMAND_REPLY_ERROR;
1244     goto out;
1245   }
1246
1247   /* Notify application */
1248   COMMAND_REPLY((ARGS, tmp, (SilcClientEntry)id_cache->context));
1249   silc_free(client_id);
1250   
1251   /* Execute any pending command callbacks */
1252   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CUMODE);
1253
1254  out:
1255   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CUMODE);
1256   silc_client_command_reply_free(cmd);
1257 }
1258
1259 SILC_CLIENT_CMD_REPLY_FUNC(kick)
1260 {
1261   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1262   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1263   SilcCommandStatus status;
1264   unsigned char *tmp;
1265
1266   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1267   SILC_GET16_MSB(status, tmp);
1268   if (status != SILC_STATUS_OK) {
1269     cmd->client->ops->say(cmd->client, conn,
1270              "%s", silc_client_command_status_message(status));
1271     COMMAND_REPLY_ERROR;
1272     goto out;
1273   }
1274
1275   /* Notify application */
1276   COMMAND_REPLY((ARGS));
1277
1278   /* Execute any pending command callbacks */
1279   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_KICK);
1280
1281  out:
1282   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_KICK);
1283   silc_client_command_reply_free(cmd);
1284 }
1285
1286 SILC_CLIENT_CMD_REPLY_FUNC(silcoper)
1287 {
1288   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1289   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1290   SilcCommandStatus status;
1291   unsigned char *tmp;
1292
1293   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1294   SILC_GET16_MSB(status, tmp);
1295   if (status != SILC_STATUS_OK) {
1296     cmd->client->ops->say(cmd->client, conn,
1297              "%s", silc_client_command_status_message(status));
1298     COMMAND_REPLY_ERROR;
1299     goto out;
1300   }
1301
1302   /* Notify application */
1303   COMMAND_REPLY((ARGS));
1304
1305   /* Execute any pending command callbacks */
1306   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_SILCOPER);
1307
1308  out:
1309   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_SILCOPER);
1310   silc_client_command_reply_free(cmd);
1311 }
1312
1313 SILC_CLIENT_CMD_REPLY_FUNC(oper)
1314 {
1315   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1316   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1317   SilcCommandStatus status;
1318   unsigned char *tmp;
1319
1320   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1321   SILC_GET16_MSB(status, tmp);
1322   if (status != SILC_STATUS_OK) {
1323     cmd->client->ops->say(cmd->client, conn,
1324              "%s", silc_client_command_status_message(status));
1325     COMMAND_REPLY_ERROR;
1326     goto out;
1327   }
1328
1329   /* Notify application */
1330   COMMAND_REPLY((ARGS));
1331
1332   /* Execute any pending command callbacks */
1333   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_OPER);
1334
1335  out:
1336   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_OPER);
1337   silc_client_command_reply_free(cmd);
1338 }
1339
1340 SILC_CLIENT_CMD_REPLY_FUNC(connect)
1341 {
1342   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1343   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1344   SilcCommandStatus status;
1345   unsigned char *tmp;
1346
1347   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1348   SILC_GET16_MSB(status, tmp);
1349   if (status != SILC_STATUS_OK) {
1350     cmd->client->ops->say(cmd->client, conn,
1351              "%s", silc_client_command_status_message(status));
1352     COMMAND_REPLY_ERROR;
1353     goto out;
1354   }
1355
1356   /* Notify application */
1357   COMMAND_REPLY((ARGS));
1358
1359   /* Execute any pending command callbacks */
1360   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CONNECT);
1361
1362  out:
1363   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CONNECT);
1364   silc_client_command_reply_free(cmd);
1365 }
1366
1367 SILC_CLIENT_CMD_REPLY_FUNC(restart)
1368 {
1369   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1370   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1371   SilcCommandStatus status;
1372   unsigned char *tmp;
1373
1374   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1375   SILC_GET16_MSB(status, tmp);
1376   if (status != SILC_STATUS_OK) {
1377     cmd->client->ops->say(cmd->client, conn,
1378              "%s", silc_client_command_status_message(status));
1379     COMMAND_REPLY_ERROR;
1380     goto out;
1381   }
1382
1383   /* Notify application */
1384   COMMAND_REPLY((ARGS));
1385
1386   /* Execute any pending command callbacks */
1387   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_RESTART);
1388
1389  out:
1390   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_RESTART);
1391   silc_client_command_reply_free(cmd);
1392 }
1393  
1394 SILC_CLIENT_CMD_REPLY_FUNC(close)
1395 {
1396   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1397   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1398   SilcCommandStatus status;
1399   unsigned char *tmp;
1400
1401   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1402   SILC_GET16_MSB(status, tmp);
1403   if (status != SILC_STATUS_OK) {
1404     cmd->client->ops->say(cmd->client, conn,
1405              "%s", silc_client_command_status_message(status));
1406     COMMAND_REPLY_ERROR;
1407     goto out;
1408   }
1409
1410   /* Notify application */
1411   COMMAND_REPLY((ARGS));
1412
1413   /* Execute any pending command callbacks */
1414   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CLOSE);
1415
1416  out:
1417   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CLOSE);
1418   silc_client_command_reply_free(cmd);
1419 }
1420  
1421 SILC_CLIENT_CMD_REPLY_FUNC(shutdown)
1422 {
1423   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1424   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1425   SilcCommandStatus status;
1426   unsigned char *tmp;
1427
1428   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1429   SILC_GET16_MSB(status, tmp);
1430   if (status != SILC_STATUS_OK) {
1431     cmd->client->ops->say(cmd->client, conn,
1432              "%s", silc_client_command_status_message(status));
1433     COMMAND_REPLY_ERROR;
1434     goto out;
1435   }
1436
1437   /* Notify application */
1438   COMMAND_REPLY((ARGS));
1439
1440   /* Execute any pending command callbacks */
1441   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_SHUTDOWN);
1442
1443  out:
1444   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_SHUTDOWN);
1445   silc_client_command_reply_free(cmd);
1446 }
1447  
1448 /* Reply to LEAVE command. */
1449
1450 SILC_CLIENT_CMD_REPLY_FUNC(leave)
1451 {
1452   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1453   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1454   SilcCommandStatus status;
1455   unsigned char *tmp;
1456
1457   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1458   SILC_GET16_MSB(status, tmp);
1459   if (status != SILC_STATUS_OK) {
1460     cmd->client->ops->say(cmd->client, conn,
1461              "%s", silc_client_command_status_message(status));
1462     COMMAND_REPLY_ERROR;
1463     goto out;
1464   }
1465
1466   /* Notify application */
1467   COMMAND_REPLY((ARGS));
1468
1469   /* Execute any pending command callbacks */
1470   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_LEAVE);
1471
1472  out:
1473   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_LEAVE);
1474   silc_client_command_reply_free(cmd);
1475 }
1476
1477 /* Reply to USERS command. Received list of client ID's and theirs modes
1478    on the channel we requested. */
1479
1480 SILC_CLIENT_CMD_REPLY_FUNC(users)
1481 {
1482   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1483   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1484   SilcCommandStatus status;
1485   SilcIDCacheEntry id_cache = NULL;
1486   SilcChannelEntry channel;
1487   SilcChannelUser chu;
1488   SilcChannelID *channel_id = NULL;
1489   SilcBuffer client_id_list;
1490   SilcBuffer client_mode_list;
1491   unsigned char *tmp;
1492   unsigned int tmp_len, list_count;
1493   int i;
1494   unsigned char **res_argv = NULL;
1495   unsigned int *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
1496
1497   SILC_LOG_DEBUG(("Start"));
1498
1499   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1500   SILC_GET16_MSB(status, tmp);
1501   if (status != SILC_STATUS_OK) {
1502     cmd->client->ops->say(cmd->client, conn,
1503              "%s", silc_client_command_status_message(status));
1504     COMMAND_REPLY_ERROR;
1505     goto out;
1506   }
1507
1508   /* Get channel ID */
1509   tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
1510   if (!tmp)
1511     goto out;
1512   channel_id = silc_id_payload_parse_id(tmp, tmp_len);
1513   if (!channel_id)
1514     goto out;
1515
1516   /* Get the list count */
1517   tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
1518   if (!tmp)
1519     goto out;
1520   SILC_GET32_MSB(list_count, tmp);
1521
1522   /* Get Client ID list */
1523   tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
1524   if (!tmp)
1525     goto out;
1526
1527   client_id_list = silc_buffer_alloc(tmp_len);
1528   silc_buffer_pull_tail(client_id_list, tmp_len);
1529   silc_buffer_put(client_id_list, tmp, tmp_len);
1530
1531   /* Get client mode list */
1532   tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
1533   if (!tmp)
1534     goto out;
1535
1536   client_mode_list = silc_buffer_alloc(tmp_len);
1537   silc_buffer_pull_tail(client_mode_list, tmp_len);
1538   silc_buffer_put(client_mode_list, tmp, tmp_len);
1539
1540   /* Get channel entry */
1541   if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
1542                                    SILC_ID_CHANNEL, &id_cache)) {
1543     COMMAND_REPLY_ERROR;
1544     goto out;
1545   }
1546   channel = (SilcChannelEntry)id_cache->context;
1547
1548   /* Remove old client list from channel. */
1549   silc_list_start(channel->clients);
1550   while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1551     silc_list_del(channel->clients, chu);
1552     silc_free(chu);
1553   }
1554
1555   /* Cache the received Client ID's and modes. This cache expires
1556      whenever server sends notify message to channel. It means two things;
1557      some user has joined or leaved the channel. XXX! */
1558   for (i = 0; i < list_count; i++) {
1559     unsigned short idp_len;
1560     unsigned int mode;
1561     SilcClientID *client_id;
1562     SilcClientEntry client;
1563
1564     /* Client ID */
1565     SILC_GET16_MSB(idp_len, client_id_list->data + 2);
1566     idp_len += 4;
1567     client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
1568     if (!client_id)
1569       continue;
1570
1571     /* Mode */
1572     SILC_GET32_MSB(mode, client_mode_list->data);
1573
1574     /* Check if we have this client cached already. */
1575     if (!silc_idcache_find_by_id_one(conn->client_cache, (void *)client_id,
1576                                      SILC_ID_CLIENT, &id_cache)) {
1577       /* No we don't have it, query it from the server. Assemble argument
1578          table that will be sent fr the IDENTIFY command later. */
1579       res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
1580                               (res_argc + 1));
1581       res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
1582                                    (res_argc + 1));
1583       res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
1584                                     (res_argc + 1));
1585       res_argv[res_argc] = client_id_list->data;
1586       res_argv_lens[res_argc] = idp_len;
1587       res_argv_types[res_argc] = res_argc + 3;
1588       res_argc++;
1589     } else {
1590       /* Found the client, join it to the channel */
1591       client = (SilcClientEntry)id_cache->context;
1592       chu = silc_calloc(1, sizeof(*chu));
1593       chu->client = client;
1594       chu->mode = mode;
1595       silc_list_add(channel->clients, chu);
1596
1597       silc_free(client_id);
1598       id_cache = NULL;
1599     }
1600
1601     silc_buffer_pull(client_id_list, idp_len);
1602     silc_buffer_pull(client_mode_list, 4);
1603   }
1604
1605   /* Query the client information from server if the list included clients
1606      that we don't know about. */
1607   if (res_argc) {
1608     SilcBuffer res_cmd;
1609
1610     /* Send the IDENTIFY command to server */
1611     res_cmd = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
1612                                           res_argc, res_argv, res_argv_lens,
1613                                           res_argv_types, ++conn->cmd_ident);
1614     silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, 
1615                             NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
1616                             TRUE);
1617
1618     /* Register pending command callback. After we've received the IDENTIFY
1619        command reply we will reprocess this command reply by re-calling this
1620        USERS command reply callback. */
1621     silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
1622                                 NULL, silc_client_command_reply_users, cmd);
1623
1624     silc_buffer_free(res_cmd);
1625     if (channel_id)
1626       silc_free(channel_id);
1627
1628     silc_free(res_argv);
1629     silc_free(res_argv_lens);
1630     silc_free(res_argv_types);
1631     return;
1632   }
1633
1634   /* Notify application */
1635   COMMAND_REPLY((ARGS, channel, list_count, client_id_list, client_mode_list));
1636
1637   /* Execute any pending command callbacks */
1638   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
1639
1640   silc_buffer_free(client_id_list);
1641   silc_buffer_free(client_mode_list);
1642
1643  out:
1644   if (channel_id)
1645     silc_free(channel_id);
1646   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_USERS);
1647   silc_client_command_reply_free(cmd);
1648 }