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), "No such Client ID" },
86   { STAT(NO_SUCH_CHANNEL_ID),"No such Channel ID" },
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, "Cannot set nickname: %s", 
568              silc_client_command_status_message(status));
569     COMMAND_REPLY_ERROR;
570     goto out;
571   }
572
573   argc = silc_argument_get_arg_num(cmd->args);
574   if (argc < 2 || argc > 2) {
575     cmd->client->ops->say(cmd->client, conn, 
576                           "Cannot set nickname: bad reply to command");
577     COMMAND_REPLY_ERROR;
578     goto out;
579   }
580
581   /* Take received Client ID */
582   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
583   idp = silc_id_payload_parse_data(tmp, len);
584   if (!idp) {
585     COMMAND_REPLY_ERROR;
586     goto out;
587   }
588   silc_client_receive_new_id(cmd->client, cmd->sock, idp);
589     
590   /* Notify application */
591   COMMAND_REPLY((ARGS, conn->local_entry));
592
593   /* Execute any pending command callbacks */
594   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_NICK);
595
596  out:
597   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_NICK);
598   silc_client_command_reply_free(cmd);
599 }
600
601 /* Received reply to the LIST command. */
602
603 SILC_CLIENT_CMD_REPLY_FUNC(list)
604 {
605   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
606   SilcCommandStatus status;
607   unsigned char *tmp, *name, *topic;
608   uint32 usercount = 0;
609
610   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
611   SILC_GET16_MSB(status, tmp);
612   if (status != SILC_STATUS_OK && 
613       status != SILC_STATUS_LIST_START &&
614       status != SILC_STATUS_LIST_ITEM &&
615       status != SILC_STATUS_LIST_END) {
616     COMMAND_REPLY_ERROR;
617     goto out;
618   }
619
620   name = silc_argument_get_arg_type(cmd->args, 3, NULL);
621   topic = silc_argument_get_arg_type(cmd->args, 4, NULL);
622   tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
623   if (tmp)
624     SILC_GET32_MSB(usercount, tmp);
625
626   /* Notify application */
627   COMMAND_REPLY((ARGS, NULL, name, topic, usercount));
628
629   /* Pending callbacks are not executed if this was an list entry */
630   if (status != SILC_STATUS_OK &&
631       status != SILC_STATUS_LIST_END) {
632     silc_client_command_reply_free(cmd);
633     return;
634   }
635
636   /* Execute any pending command callbacks */
637   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_LIST);
638
639  out:
640   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_LIST);
641   silc_client_command_reply_free(cmd);
642 }
643
644 /* Received reply to topic command. */
645
646 SILC_CLIENT_CMD_REPLY_FUNC(topic)
647 {
648   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
649   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
650   SilcCommandStatus status;
651   SilcChannelEntry channel;
652   SilcChannelID *channel_id = NULL;
653   SilcIDCacheEntry id_cache = NULL;
654   unsigned char *tmp;
655   char *topic;
656   uint32 argc, len;
657
658   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
659   if (status != SILC_STATUS_OK) {
660     cmd->client->ops->say(cmd->client, conn,
661              "%s", silc_client_command_status_message(status));
662     COMMAND_REPLY_ERROR;
663     SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_TOPIC);
664     silc_client_command_reply_free(cmd);
665     return;
666   }
667
668   argc = silc_argument_get_arg_num(cmd->args);
669   if (argc < 1 || argc > 3) {
670     COMMAND_REPLY_ERROR;
671     goto out;
672   }
673
674   /* Take Channel ID */
675   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
676   if (!tmp)
677     goto out;
678
679   /* Take topic */
680   topic = silc_argument_get_arg_type(cmd->args, 3, NULL);
681   if (!topic)
682     goto out;
683
684   channel_id = silc_id_payload_parse_id(tmp, len);
685   if (!channel_id)
686     goto out;
687
688   /* Get the channel entry */
689   if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
690                                    &id_cache)) {
691     silc_free(channel_id);
692     COMMAND_REPLY_ERROR;
693     goto out;
694   }
695   
696   channel = (SilcChannelEntry)id_cache->context;
697
698   /* Notify application */
699   COMMAND_REPLY((ARGS, channel, topic));
700
701   /* Execute any pending command callbacks */
702   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_TOPIC);
703
704  out:
705   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_TOPIC);
706   silc_client_command_reply_free(cmd);
707 }
708
709 /* Received reply to invite command. */
710
711 SILC_CLIENT_CMD_REPLY_FUNC(invite)
712 {
713   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
714   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
715   SilcCommandStatus status;
716   SilcChannelEntry channel;
717   SilcChannelID *channel_id;
718   SilcIDCacheEntry id_cache;
719   unsigned char *tmp;
720   uint32 len;
721
722   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
723   SILC_GET16_MSB(status, tmp);
724   if (status != SILC_STATUS_OK) {
725     cmd->client->ops->say(cmd->client, conn,
726              "%s", silc_client_command_status_message(status));
727     COMMAND_REPLY_ERROR;
728     SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INVITE);
729     silc_client_command_reply_free(cmd);
730     return;
731   }
732
733   /* Take Channel ID */
734   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
735   if (!tmp)
736     goto out;
737
738   channel_id = silc_id_payload_parse_id(tmp, len);
739   if (!channel_id)
740     goto out;
741
742   /* Get the channel entry */
743   if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
744                                    &id_cache)) {
745     silc_free(channel_id);
746     COMMAND_REPLY_ERROR;
747     goto out;
748   }
749   
750   channel = (SilcChannelEntry)id_cache->context;
751
752   /* Get the invite list */
753   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
754
755   /* Notify application */
756   COMMAND_REPLY((ARGS, channel, tmp));
757
758   /* Execute any pending command callbacks */
759   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INVITE);
760
761  out:
762   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INVITE);
763   silc_client_command_reply_free(cmd);
764 }
765
766 /* Received reply to the KILL command. */
767  
768 SILC_CLIENT_CMD_REPLY_FUNC(kill)
769 {
770   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
771   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
772   SilcCommandStatus status;
773   unsigned char *tmp;
774
775   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
776   SILC_GET16_MSB(status, tmp);
777   if (status != SILC_STATUS_OK) {
778     cmd->client->ops->say(cmd->client, conn,
779              "%s", silc_client_command_status_message(status));
780     COMMAND_REPLY_ERROR;
781     goto out;
782   }
783
784   /* Notify application */
785   COMMAND_REPLY((ARGS));
786
787   /* Execute any pending command callbacks */
788   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_KILL);
789
790  out:
791   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_KILL);
792   silc_client_command_reply_free(cmd);
793 }
794
795 /* Received reply to INFO command. We receive the server ID and some
796    information about the server user requested. */
797
798 SILC_CLIENT_CMD_REPLY_FUNC(info)
799 {
800   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
801   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
802   SilcClient client = cmd->client;
803   SilcCommandStatus status;
804   unsigned char *tmp;
805
806   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
807   SILC_GET16_MSB(status, tmp);
808   if (status != SILC_STATUS_OK) {
809     cmd->client->ops->say(cmd->client, conn,
810              "%s", silc_client_command_status_message(status));
811     COMMAND_REPLY_ERROR;
812     SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INFO);
813     silc_client_command_reply_free(cmd);
814     return;
815   }
816
817   /* Get server ID */
818   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
819   if (!tmp)
820     goto out;
821
822   /* XXX save server id */
823
824   /* Get server name */
825   tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
826   if (!tmp)
827     goto out;
828
829   /* Get server info */
830   tmp = silc_argument_get_arg_type(cmd->args, 4, NULL);
831   if (!tmp)
832     goto out;
833
834   client->ops->say(cmd->client, conn, "Info: %s", tmp);
835
836   /* Notify application */
837   COMMAND_REPLY((ARGS, NULL, (char *)tmp));
838
839   /* Execute any pending command callbacks */
840   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INFO);
841
842  out:
843   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INFO);
844   silc_client_command_reply_free(cmd);
845 }
846
847 /* Received reply to PING command. The reply time is shown to user. */
848
849 SILC_CLIENT_CMD_REPLY_FUNC(ping)
850 {
851   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
852   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
853   SilcCommandStatus status;
854   void *id;
855   int i;
856   time_t diff, curtime;
857
858   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
859   if (status != SILC_STATUS_OK) {
860     cmd->client->ops->say(cmd->client, conn,
861              "%s", silc_client_command_status_message(status));
862     COMMAND_REPLY_ERROR;
863     goto out;
864   }
865
866   curtime = time(NULL);
867   id = silc_id_str2id(cmd->packet->src_id, cmd->packet->src_id_len,
868                       cmd->packet->src_id_type);
869   if (!id) {
870     COMMAND_REPLY_ERROR;
871     goto out;
872   }
873
874   for (i = 0; i < conn->ping_count; i++) {
875     if (SILC_ID_SERVER_COMPARE(conn->ping[i].dest_id, id)) {
876       diff = curtime - conn->ping[i].start_time;
877       cmd->client->ops->say(cmd->client, conn, 
878                             "Ping reply from %s: %d second%s", 
879                             conn->ping[i].dest_name, diff, 
880                             diff == 1 ? "" : "s");
881       
882       conn->ping[i].start_time = 0;
883       silc_free(conn->ping[i].dest_id);
884       conn->ping[i].dest_id = NULL;
885       silc_free(conn->ping[i].dest_name);
886       conn->ping[i].dest_name = NULL;
887       break;
888     }
889   }
890
891   silc_free(id);
892
893   /* Notify application */
894   COMMAND_REPLY((ARGS));
895
896   /* Execute any pending command callbacks */
897   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PING);
898
899  out:
900   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_PING);
901   silc_client_command_reply_free(cmd);
902 }
903
904 /* Received reply for JOIN command. */
905
906 SILC_CLIENT_CMD_REPLY_FUNC(join)
907 {
908   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
909   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
910   SilcCommandStatus status;
911   SilcIDPayload idp = NULL;
912   SilcChannelEntry channel;
913   SilcIDCacheEntry id_cache = NULL;
914   SilcChannelUser chu;
915   uint32 argc, mode, len, list_count;
916   char *topic, *tmp, *channel_name = NULL, *hmac;
917   SilcBuffer keyp = NULL, client_id_list, client_mode_list;
918   int i;
919
920   SILC_LOG_DEBUG(("Start"));
921
922   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
923   if (status != SILC_STATUS_OK) {
924     cmd->client->ops->say(cmd->client, conn,
925              "%s", silc_client_command_status_message(status));
926     COMMAND_REPLY_ERROR;
927     goto out;
928   }
929
930   argc = silc_argument_get_arg_num(cmd->args);
931   if (argc < 7 || argc > 14) {
932     cmd->client->ops->say(cmd->client, conn,
933              "Cannot join channel: Bad reply packet");
934     COMMAND_REPLY_ERROR;
935     goto out;
936   }
937
938   /* Get channel name */
939   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
940   if (!tmp) {
941     cmd->client->ops->say(cmd->client, conn, 
942                           "Cannot join channel: Bad reply packet");
943     COMMAND_REPLY_ERROR;
944     goto out;
945   }
946   channel_name = strdup(tmp);
947
948   /* Get Channel ID */
949   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
950   if (!tmp) {
951     cmd->client->ops->say(cmd->client, conn, 
952                           "Cannot join channel: Bad reply packet");
953     COMMAND_REPLY_ERROR;
954     silc_free(channel_name);
955     goto out;
956   }
957   idp = silc_id_payload_parse_data(tmp, len);
958   if (!idp) {
959     COMMAND_REPLY_ERROR;
960     silc_free(channel_name);
961     goto out;
962   }
963
964   /* Get channel mode */
965   tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
966   if (tmp)
967     SILC_GET32_MSB(mode, tmp);
968   else
969     mode = 0;
970
971   /* Get channel key */
972   tmp = silc_argument_get_arg_type(cmd->args, 7, &len);
973   if (tmp) {
974     keyp = silc_buffer_alloc(len);
975     silc_buffer_pull_tail(keyp, SILC_BUFFER_END(keyp));
976     silc_buffer_put(keyp, tmp, len);
977   }
978
979   /* Get topic */
980   topic = silc_argument_get_arg_type(cmd->args, 10, NULL);
981
982   /* Save received Channel ID. This actually creates the channel */
983   channel = silc_client_new_channel_id(cmd->client, cmd->sock, channel_name, 
984                                        mode, idp);
985   silc_id_payload_free(idp);
986
987   /* Get hmac */
988   hmac = silc_argument_get_arg_type(cmd->args, 11, NULL);
989   if (hmac) {
990     if (!silc_hmac_alloc(hmac, NULL, &channel->hmac)) {
991       cmd->client->ops->say(cmd->client, conn, 
992                             "Cannot join channel: Unsupported HMAC `%s'",
993                             hmac);
994       COMMAND_REPLY_ERROR;
995       silc_free(channel_name);
996       goto out;
997     }
998   }
999
1000   /* Get the list count */
1001   tmp = silc_argument_get_arg_type(cmd->args, 12, &len);
1002   if (!tmp)
1003     goto out;
1004   SILC_GET32_MSB(list_count, tmp);
1005
1006   /* Get Client ID list */
1007   tmp = silc_argument_get_arg_type(cmd->args, 13, &len);
1008   if (!tmp)
1009     goto out;
1010
1011   client_id_list = silc_buffer_alloc(len);
1012   silc_buffer_pull_tail(client_id_list, len);
1013   silc_buffer_put(client_id_list, tmp, len);
1014
1015   /* Get client mode list */
1016   tmp = silc_argument_get_arg_type(cmd->args, 14, &len);
1017   if (!tmp)
1018     goto out;
1019
1020   client_mode_list = silc_buffer_alloc(len);
1021   silc_buffer_pull_tail(client_mode_list, len);
1022   silc_buffer_put(client_mode_list, tmp, len);
1023
1024   /* Add clients we received in the reply to the channel */
1025   for (i = 0; i < list_count; i++) {
1026     uint16 idp_len;
1027     uint32 mode;
1028     SilcClientID *client_id;
1029     SilcClientEntry client_entry;
1030
1031     /* Client ID */
1032     SILC_GET16_MSB(idp_len, client_id_list->data + 2);
1033     idp_len += 4;
1034     client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
1035     if (!client_id)
1036       continue;
1037
1038     /* Mode */
1039     SILC_GET32_MSB(mode, client_mode_list->data);
1040
1041     /* Check if we have this client cached already. */
1042     if (!silc_idcache_find_by_id_one_ext(conn->client_cache, 
1043                                          (void *)client_id, 
1044                                          NULL, NULL, 
1045                                          silc_hash_client_id_compare, NULL,
1046                                          &id_cache)) {
1047       /* No, we don't have it, add entry for it. */
1048       client_entry = silc_calloc(1, sizeof(*client_entry));
1049       client_entry->id = silc_id_dup(client_id, SILC_ID_CLIENT);
1050       silc_idcache_add(conn->client_cache, NULL, client_entry->id, 
1051                        (void *)client_entry, FALSE);
1052     } else {
1053       /* Yes, we have it already */
1054       client_entry = (SilcClientEntry)id_cache->context;
1055     }
1056
1057     /* Join the client to the channel */
1058     chu = silc_calloc(1, sizeof(*chu));
1059     chu->client = client_entry;
1060     chu->mode = mode;
1061     silc_list_add(channel->clients, chu);
1062     silc_free(client_id);
1063
1064     silc_buffer_pull(client_id_list, idp_len);
1065     silc_buffer_pull(client_mode_list, 4);
1066   }
1067   silc_buffer_push(client_id_list, client_id_list->data - 
1068                    client_id_list->head);
1069   silc_buffer_push(client_mode_list, client_mode_list->data - 
1070                    client_mode_list->head);
1071
1072   /* Save channel key */
1073   if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY))
1074     silc_client_save_channel_key(conn, keyp, channel);
1075
1076   /* Client is now joined to the channel */
1077   channel->on_channel = TRUE;
1078
1079   /* Notify application */
1080   COMMAND_REPLY((ARGS, channel_name, channel, mode, 0, 
1081                  keyp ? keyp->head : NULL, NULL,
1082                  NULL, topic, hmac, list_count, client_id_list, 
1083                  client_mode_list));
1084
1085   /* Execute any pending command callbacks */
1086   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_JOIN);
1087
1088   if (keyp)
1089     silc_buffer_free(keyp);
1090   silc_buffer_free(client_id_list);
1091   silc_buffer_free(client_mode_list);
1092
1093  out:
1094   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_JOIN);
1095   silc_client_command_reply_free(cmd);
1096 }
1097
1098 /* Received reply for MOTD command */
1099
1100 SILC_CLIENT_CMD_REPLY_FUNC(motd)
1101 {
1102   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1103   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1104   SilcCommandStatus status;
1105   uint32 argc, i;
1106   unsigned char *tmp;
1107   char *motd = NULL, *cp, line[256];
1108
1109   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1110   SILC_GET16_MSB(status, tmp);
1111   if (status != SILC_STATUS_OK) {
1112     cmd->client->ops->say(cmd->client, conn,
1113              "%s", silc_client_command_status_message(status));
1114     COMMAND_REPLY_ERROR;
1115     return;
1116   }
1117
1118   argc = silc_argument_get_arg_num(cmd->args);
1119   if (argc > 3) {
1120     COMMAND_REPLY_ERROR;
1121     goto out;
1122   }
1123
1124   if (argc == 3) {
1125     motd = silc_argument_get_arg_type(cmd->args, 3, NULL);
1126     if (!motd) {
1127       COMMAND_REPLY_ERROR;
1128       goto out;
1129     }
1130
1131     i = 0;
1132     cp = motd;
1133     while(cp[i] != 0) {
1134       if (cp[i++] == '\n') {
1135         memset(line, 0, sizeof(line));
1136         strncat(line, cp, i - 1);
1137         cp += i;
1138         
1139         if (i == 2)
1140           line[0] = ' ';
1141         
1142         cmd->client->ops->say(cmd->client, conn, "%s", line);
1143         
1144         if (!strlen(cp))
1145           break;
1146         i = 0;
1147       }
1148     }
1149   }
1150
1151   /* Notify application */
1152   COMMAND_REPLY((ARGS, motd));
1153
1154   /* Execute any pending command callbacks */
1155   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_MOTD);
1156
1157  out:
1158   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_MOTD);
1159   silc_client_command_reply_free(cmd);
1160 }
1161
1162 /* Received reply tot he UMODE command. Save the current user mode */
1163
1164 SILC_CLIENT_CMD_REPLY_FUNC(umode)
1165 {
1166   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1167   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1168   SilcCommandStatus status;
1169   unsigned char *tmp;
1170   uint32 mode;
1171
1172   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1173   SILC_GET16_MSB(status, tmp);
1174   if (status != SILC_STATUS_OK) {
1175     cmd->client->ops->say(cmd->client, conn,
1176              "%s", silc_client_command_status_message(status));
1177     COMMAND_REPLY_ERROR;
1178     goto out;
1179   }
1180
1181   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
1182   if (!tmp) {
1183     COMMAND_REPLY_ERROR;
1184     goto out;
1185   }
1186
1187   SILC_GET32_MSB(mode, tmp);
1188   conn->local_entry->mode = mode;
1189
1190   /* Notify application */
1191   COMMAND_REPLY((ARGS, mode));
1192
1193   /* Execute any pending command callbacks */
1194   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_UMODE);
1195
1196  out:
1197   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_UMODE);
1198   silc_client_command_reply_free(cmd);
1199 }
1200
1201 /* Received reply for CMODE command. */
1202
1203 SILC_CLIENT_CMD_REPLY_FUNC(cmode)
1204 {
1205   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1206   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1207   SilcCommandStatus status;
1208   unsigned char *tmp;
1209
1210   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
1211   if (status != SILC_STATUS_OK) {
1212     cmd->client->ops->say(cmd->client, conn,
1213              "%s", silc_client_command_status_message(status));
1214     COMMAND_REPLY_ERROR;
1215     goto out;
1216   }
1217
1218   /* Get channel mode */
1219   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
1220   if (!tmp) {
1221     COMMAND_REPLY_ERROR;
1222     goto out;
1223   }
1224
1225   /* Notify application */
1226   COMMAND_REPLY((ARGS, tmp));
1227
1228   /* Execute any pending command callbacks */
1229   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CMODE);
1230
1231  out:
1232   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CMODE);
1233   silc_client_command_reply_free(cmd);
1234 }
1235
1236 /* Received reply for CUMODE command */
1237
1238 SILC_CLIENT_CMD_REPLY_FUNC(cumode)
1239 {
1240   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1241   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1242   SilcCommandStatus status;
1243   SilcIDCacheEntry id_cache = NULL;
1244   SilcClientID *client_id;
1245   unsigned char *tmp, *id;
1246   uint32 len;
1247   
1248   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
1249   if (status != SILC_STATUS_OK) {
1250     cmd->client->ops->say(cmd->client, conn,
1251              "%s", silc_client_command_status_message(status));
1252     COMMAND_REPLY_ERROR;
1253     goto out;
1254   }
1255   
1256   /* Get channel mode */
1257   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
1258   if (!tmp) {
1259     COMMAND_REPLY_ERROR;
1260     goto out;
1261   }
1262
1263   /* Get Client ID */
1264   id = silc_argument_get_arg_type(cmd->args, 3, &len);
1265   if (!id) {
1266     COMMAND_REPLY_ERROR;
1267     goto out;
1268   }
1269   client_id = silc_id_payload_parse_id(id, len);
1270   if (!client_id) {
1271     COMMAND_REPLY_ERROR;
1272     goto out;
1273   }
1274   
1275   /* Get client entry */
1276   if (!silc_idcache_find_by_id_one_ext(conn->client_cache, (void *)client_id, 
1277                                        NULL, NULL, 
1278                                        silc_hash_client_id_compare, NULL,
1279                                        &id_cache)) {
1280     COMMAND_REPLY_ERROR;
1281     goto out;
1282   }
1283
1284   /* Notify application */
1285   COMMAND_REPLY((ARGS, tmp, (SilcClientEntry)id_cache->context));
1286   silc_free(client_id);
1287   
1288   /* Execute any pending command callbacks */
1289   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CUMODE);
1290
1291  out:
1292   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CUMODE);
1293   silc_client_command_reply_free(cmd);
1294 }
1295
1296 SILC_CLIENT_CMD_REPLY_FUNC(kick)
1297 {
1298   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1299   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1300   SilcCommandStatus status;
1301   unsigned char *tmp;
1302
1303   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1304   SILC_GET16_MSB(status, tmp);
1305   if (status != SILC_STATUS_OK) {
1306     cmd->client->ops->say(cmd->client, conn,
1307              "%s", silc_client_command_status_message(status));
1308     COMMAND_REPLY_ERROR;
1309     goto out;
1310   }
1311
1312   /* Notify application */
1313   COMMAND_REPLY((ARGS));
1314
1315   /* Execute any pending command callbacks */
1316   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_KICK);
1317
1318  out:
1319   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_KICK);
1320   silc_client_command_reply_free(cmd);
1321 }
1322
1323 SILC_CLIENT_CMD_REPLY_FUNC(silcoper)
1324 {
1325   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1326   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1327   SilcCommandStatus status;
1328   unsigned char *tmp;
1329
1330   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1331   SILC_GET16_MSB(status, tmp);
1332   if (status != SILC_STATUS_OK) {
1333     cmd->client->ops->say(cmd->client, conn,
1334              "%s", silc_client_command_status_message(status));
1335     COMMAND_REPLY_ERROR;
1336     goto out;
1337   }
1338
1339   /* Notify application */
1340   COMMAND_REPLY((ARGS));
1341
1342   /* Execute any pending command callbacks */
1343   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_SILCOPER);
1344
1345  out:
1346   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_SILCOPER);
1347   silc_client_command_reply_free(cmd);
1348 }
1349
1350 SILC_CLIENT_CMD_REPLY_FUNC(oper)
1351 {
1352   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1353   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1354   SilcCommandStatus status;
1355   unsigned char *tmp;
1356
1357   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1358   SILC_GET16_MSB(status, tmp);
1359   if (status != SILC_STATUS_OK) {
1360     cmd->client->ops->say(cmd->client, conn,
1361              "%s", silc_client_command_status_message(status));
1362     COMMAND_REPLY_ERROR;
1363     goto out;
1364   }
1365
1366   /* Notify application */
1367   COMMAND_REPLY((ARGS));
1368
1369   /* Execute any pending command callbacks */
1370   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_OPER);
1371
1372  out:
1373   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_OPER);
1374   silc_client_command_reply_free(cmd);
1375 }
1376
1377 SILC_CLIENT_CMD_REPLY_FUNC(connect)
1378 {
1379   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1380   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1381   SilcCommandStatus status;
1382   unsigned char *tmp;
1383
1384   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1385   SILC_GET16_MSB(status, tmp);
1386   if (status != SILC_STATUS_OK) {
1387     cmd->client->ops->say(cmd->client, conn,
1388              "%s", silc_client_command_status_message(status));
1389     COMMAND_REPLY_ERROR;
1390     goto out;
1391   }
1392
1393   /* Notify application */
1394   COMMAND_REPLY((ARGS));
1395
1396   /* Execute any pending command callbacks */
1397   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CONNECT);
1398
1399  out:
1400   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CONNECT);
1401   silc_client_command_reply_free(cmd);
1402 }
1403
1404 SILC_CLIENT_CMD_REPLY_FUNC(ban)
1405 {
1406   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1407   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1408   SilcCommandStatus status;
1409   SilcIDCacheEntry id_cache = NULL;
1410   SilcChannelEntry channel;
1411   SilcChannelID *channel_id;
1412   unsigned char *tmp;
1413   uint32 len;
1414
1415   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1416   SILC_GET16_MSB(status, tmp);
1417   if (status != SILC_STATUS_OK) {
1418     cmd->client->ops->say(cmd->client, conn,
1419              "%s", silc_client_command_status_message(status));
1420     COMMAND_REPLY_ERROR;
1421     goto out;
1422   }
1423
1424   /* Take Channel ID */
1425   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1426   if (!tmp)
1427     goto out;
1428
1429   channel_id = silc_id_payload_parse_id(tmp, len);
1430   if (!channel_id)
1431     goto out;
1432
1433   /* Get the channel entry */
1434   if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
1435                                    &id_cache)) {
1436     silc_free(channel_id);
1437     COMMAND_REPLY_ERROR;
1438     goto out;
1439   }
1440   
1441   channel = (SilcChannelEntry)id_cache->context;
1442
1443   /* Get the ban list */
1444   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
1445
1446   /* Notify application */
1447   COMMAND_REPLY((ARGS, channel, tmp));
1448
1449   /* Execute any pending command callbacks */
1450   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_BAN);
1451
1452  out:
1453   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_BAN);
1454   silc_client_command_reply_free(cmd);
1455 }
1456
1457 SILC_CLIENT_CMD_REPLY_FUNC(close)
1458 {
1459   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1460   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1461   SilcCommandStatus status;
1462   unsigned char *tmp;
1463
1464   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1465   SILC_GET16_MSB(status, tmp);
1466   if (status != SILC_STATUS_OK) {
1467     cmd->client->ops->say(cmd->client, conn,
1468              "%s", silc_client_command_status_message(status));
1469     COMMAND_REPLY_ERROR;
1470     goto out;
1471   }
1472
1473   /* Notify application */
1474   COMMAND_REPLY((ARGS));
1475
1476   /* Execute any pending command callbacks */
1477   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CLOSE);
1478
1479  out:
1480   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CLOSE);
1481   silc_client_command_reply_free(cmd);
1482 }
1483  
1484 SILC_CLIENT_CMD_REPLY_FUNC(shutdown)
1485 {
1486   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1487   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1488   SilcCommandStatus status;
1489   unsigned char *tmp;
1490
1491   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1492   SILC_GET16_MSB(status, tmp);
1493   if (status != SILC_STATUS_OK) {
1494     cmd->client->ops->say(cmd->client, conn,
1495              "%s", silc_client_command_status_message(status));
1496     COMMAND_REPLY_ERROR;
1497     goto out;
1498   }
1499
1500   /* Notify application */
1501   COMMAND_REPLY((ARGS));
1502
1503   /* Execute any pending command callbacks */
1504   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_SHUTDOWN);
1505
1506  out:
1507   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_SHUTDOWN);
1508   silc_client_command_reply_free(cmd);
1509 }
1510  
1511 /* Reply to LEAVE command. */
1512
1513 SILC_CLIENT_CMD_REPLY_FUNC(leave)
1514 {
1515   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1516   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1517   SilcCommandStatus status;
1518   unsigned char *tmp;
1519
1520   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1521   SILC_GET16_MSB(status, tmp);
1522   if (status != SILC_STATUS_OK) {
1523     cmd->client->ops->say(cmd->client, conn,
1524              "%s", silc_client_command_status_message(status));
1525     COMMAND_REPLY_ERROR;
1526     goto out;
1527   }
1528
1529   /* Notify application */
1530   COMMAND_REPLY((ARGS));
1531
1532   /* Execute any pending command callbacks */
1533   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_LEAVE);
1534
1535  out:
1536   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_LEAVE);
1537   silc_client_command_reply_free(cmd);
1538 }
1539
1540 /* Reply to USERS command. Received list of client ID's and theirs modes
1541    on the channel we requested. */
1542
1543 SILC_CLIENT_CMD_REPLY_FUNC(users)
1544 {
1545   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1546   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1547   SilcCommandStatus status;
1548   SilcIDCacheEntry id_cache = NULL;
1549   SilcChannelEntry channel;
1550   SilcChannelUser chu;
1551   SilcChannelID *channel_id = NULL;
1552   SilcBuffer client_id_list;
1553   SilcBuffer client_mode_list;
1554   unsigned char *tmp;
1555   uint32 tmp_len, list_count;
1556   int i;
1557   unsigned char **res_argv = NULL;
1558   uint32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
1559
1560   SILC_LOG_DEBUG(("Start"));
1561
1562   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1563   SILC_GET16_MSB(status, tmp);
1564   if (status != SILC_STATUS_OK) {
1565     cmd->client->ops->say(cmd->client, conn,
1566              "%s", silc_client_command_status_message(status));
1567     COMMAND_REPLY_ERROR;
1568     goto out;
1569   }
1570
1571   /* Get channel ID */
1572   tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
1573   if (!tmp)
1574     goto out;
1575   channel_id = silc_id_payload_parse_id(tmp, tmp_len);
1576   if (!channel_id)
1577     goto out;
1578
1579   /* Get the list count */
1580   tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
1581   if (!tmp)
1582     goto out;
1583   SILC_GET32_MSB(list_count, tmp);
1584
1585   /* Get Client ID list */
1586   tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
1587   if (!tmp)
1588     goto out;
1589
1590   client_id_list = silc_buffer_alloc(tmp_len);
1591   silc_buffer_pull_tail(client_id_list, tmp_len);
1592   silc_buffer_put(client_id_list, tmp, tmp_len);
1593
1594   /* Get client mode list */
1595   tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
1596   if (!tmp)
1597     goto out;
1598
1599   client_mode_list = silc_buffer_alloc(tmp_len);
1600   silc_buffer_pull_tail(client_mode_list, tmp_len);
1601   silc_buffer_put(client_mode_list, tmp, tmp_len);
1602
1603   /* Get channel entry */
1604   if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
1605                                    &id_cache)) {
1606     COMMAND_REPLY_ERROR;
1607     goto out;
1608   }
1609   channel = (SilcChannelEntry)id_cache->context;
1610
1611   /* Remove old client list from channel. */
1612   silc_list_start(channel->clients);
1613   while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1614     silc_list_del(channel->clients, chu);
1615     silc_free(chu);
1616   }
1617
1618   /* Cache the received Client ID's and modes. This cache expires
1619      whenever server sends notify message to channel. It means two things;
1620      some user has joined or leaved the channel. XXX! */
1621   for (i = 0; i < list_count; i++) {
1622     uint16 idp_len;
1623     uint32 mode;
1624     SilcClientID *client_id;
1625     SilcClientEntry client;
1626
1627     /* Client ID */
1628     SILC_GET16_MSB(idp_len, client_id_list->data + 2);
1629     idp_len += 4;
1630     client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
1631     if (!client_id)
1632       continue;
1633
1634     /* Mode */
1635     SILC_GET32_MSB(mode, client_mode_list->data);
1636
1637     /* Check if we have this client cached already. */
1638     id_cache = NULL;
1639     silc_idcache_find_by_id_one_ext(conn->client_cache, 
1640                                     (void *)client_id, 
1641                                     NULL, NULL, 
1642                                     silc_hash_client_id_compare, NULL,
1643                                     &id_cache);
1644
1645     if (!id_cache || !((SilcClientEntry)id_cache->context)->username) {
1646       /* No we don't have it (or it is incomplete in information), query
1647          it from the server. Assemble argument table that will be sent
1648          for the WHOIS command later. */
1649       res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
1650                               (res_argc + 1));
1651       res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
1652                                    (res_argc + 1));
1653       res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
1654                                     (res_argc + 1));
1655       res_argv[res_argc] = client_id_list->data;
1656       res_argv_lens[res_argc] = idp_len;
1657       res_argv_types[res_argc] = res_argc + 3;
1658       res_argc++;
1659     } else {
1660       /* Found the client, join it to the channel */
1661       client = (SilcClientEntry)id_cache->context;
1662       chu = silc_calloc(1, sizeof(*chu));
1663       chu->client = client;
1664       chu->mode = mode;
1665       silc_list_add(channel->clients, chu);
1666
1667       silc_free(client_id);
1668       id_cache = NULL;
1669     }
1670
1671     silc_buffer_pull(client_id_list, idp_len);
1672     silc_buffer_pull(client_mode_list, 4);
1673   }
1674
1675   /* Query the client information from server if the list included clients
1676      that we don't know about. */
1677   if (res_argc) {
1678     SilcBuffer res_cmd;
1679
1680     /* Send the WHOIS command to server */
1681     res_cmd = silc_command_payload_encode(SILC_COMMAND_WHOIS,
1682                                           res_argc, res_argv, res_argv_lens,
1683                                           res_argv_types, ++conn->cmd_ident);
1684     silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, 
1685                             NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
1686                             TRUE);
1687
1688     /* Register pending command callback. After we've received the WHOIS
1689        command reply we will reprocess this command reply by re-calling this
1690        USERS command reply callback. */
1691     silc_client_command_pending(conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
1692                                 NULL, silc_client_command_reply_users, cmd);
1693
1694     silc_buffer_free(res_cmd);
1695     if (channel_id)
1696       silc_free(channel_id);
1697
1698     silc_free(res_argv);
1699     silc_free(res_argv_lens);
1700     silc_free(res_argv_types);
1701     return;
1702   }
1703
1704   /* Notify application */
1705   COMMAND_REPLY((ARGS, channel, list_count, client_id_list, client_mode_list));
1706
1707   /* Execute any pending command callbacks */
1708   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
1709
1710   silc_buffer_free(client_id_list);
1711   silc_buffer_free(client_mode_list);
1712
1713  out:
1714   if (channel_id)
1715     silc_free(channel_id);
1716   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_USERS);
1717   silc_client_command_reply_free(cmd);
1718 }
1719
1720 /* Received command reply to GETKEY command. WE've received the remote
1721    client's public key. */
1722
1723 SILC_CLIENT_CMD_REPLY_FUNC(getkey)
1724 {
1725   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1726   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1727   SilcCommandStatus status;
1728   SilcIDCacheEntry id_cache;
1729   SilcIDPayload idp = NULL;
1730   SilcClientID *client_id = NULL;
1731   SilcClientEntry client_entry;
1732   SilcSKEPKType type;
1733   unsigned char *tmp, *pk;
1734   uint32 len;
1735   uint16 pk_len;
1736   SilcIdType id_type;
1737   SilcPublicKey public_key = NULL;
1738
1739   SILC_LOG_DEBUG(("Start"));
1740
1741   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1742   SILC_GET16_MSB(status, tmp);
1743   if (status != SILC_STATUS_OK) {
1744     cmd->client->ops->say(cmd->client, conn,
1745              "%s", silc_client_command_status_message(status));
1746     COMMAND_REPLY_ERROR;
1747     goto out;
1748   }
1749
1750   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1751   if (!tmp)
1752     goto out;
1753   idp = silc_id_payload_parse_data(tmp, len);
1754   if (!idp)
1755     goto out;
1756
1757   /* Get the public key payload */
1758   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
1759   if (!tmp)
1760     goto out;
1761
1762   /* Decode the public key */
1763
1764   SILC_GET16_MSB(pk_len, tmp);
1765   SILC_GET16_MSB(type, tmp + 2);
1766   pk = tmp + 4;
1767
1768   if (type != SILC_SKE_PK_TYPE_SILC)
1769     goto out;
1770
1771   if (!silc_pkcs_public_key_decode(pk, pk_len, &public_key))
1772     goto out;
1773
1774   id_type = silc_id_payload_get_type(idp);
1775   if (id_type == SILC_ID_CLIENT) {
1776     client_id = silc_id_payload_get_id(idp);
1777
1778     if (!silc_idcache_find_by_id_one_ext(conn->client_cache, 
1779                                          (void *)client_id, 
1780                                          NULL, NULL, 
1781                                          silc_hash_client_id_compare, NULL,
1782                                          &id_cache))
1783       goto out;
1784
1785     client_entry = (SilcClientEntry)id_cache->context;
1786
1787     /* Notify application */
1788     COMMAND_REPLY((ARGS, id_type, client_entry, public_key));
1789   } else if (id_type == SILC_ID_SERVER) {
1790     /* XXX we don't have server entries at all */
1791     goto out;
1792   } else {
1793     goto out;
1794   }
1795
1796  out:
1797   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_GETKEY);
1798   if (idp)
1799     silc_id_payload_free(idp);
1800   if (public_key)
1801     silc_pkcs_public_key_free(public_key);
1802   silc_free(client_id);
1803   silc_client_command_reply_free(cmd);
1804 }