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