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