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