updates.
[silc.git] / lib / silcclient / command_reply.c
1 /*
2
3   command_reply.c
4
5   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
6
7   Copyright (C) 1997 - 2001 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; either version 2 of the License, or
12   (at your option) any later version.
13   
14   This program is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   GNU General Public License for more details.
18
19 */
20 /*
21  * Command reply functions are "the otherside" of the command functions.
22  * Reply to a command sent by server is handled by these functions.
23  *
24  * The arguments received from server are also passed to the calling
25  * application through command_reply client operation.  The arguments are
26  * exactly same and in same order as the server sent it.  However, ID's are
27  * not sent to the application.  Instead, corresponding ID entry is sent
28  * to the application.  For example, instead of sending Client ID the 
29  * corresponding SilcClientEntry is sent to the application.  The case is
30  * same with for example Channel ID's.  This way application has all the
31  * necessary data already in hand without redundant searching.  If ID is
32  * received but ID entry does not exist, NULL is sent.
33  */
34 /* $Id$ */
35
36 #include "clientlibincludes.h"
37 #include "client_internal.h"
38
39 /* Client command reply list. */
40 SilcClientCommandReply silc_command_reply_list[] =
41 {
42   SILC_CLIENT_CMD_REPLY(whois, WHOIS),
43   SILC_CLIENT_CMD_REPLY(whowas, WHOWAS),
44   SILC_CLIENT_CMD_REPLY(identify, IDENTIFY),
45   SILC_CLIENT_CMD_REPLY(nick, NICK),
46   SILC_CLIENT_CMD_REPLY(list, LIST),
47   SILC_CLIENT_CMD_REPLY(topic, TOPIC),
48   SILC_CLIENT_CMD_REPLY(invite, INVITE),
49   SILC_CLIENT_CMD_REPLY(kill, KILL),
50   SILC_CLIENT_CMD_REPLY(info, INFO),
51   SILC_CLIENT_CMD_REPLY(connect, CONNECT),
52   SILC_CLIENT_CMD_REPLY(ping, PING),
53   SILC_CLIENT_CMD_REPLY(oper, OPER),
54   SILC_CLIENT_CMD_REPLY(join, JOIN),
55   SILC_CLIENT_CMD_REPLY(motd, MOTD),
56   SILC_CLIENT_CMD_REPLY(umode, UMODE),
57   SILC_CLIENT_CMD_REPLY(cmode, CMODE),
58   SILC_CLIENT_CMD_REPLY(cumode, CUMODE),
59   SILC_CLIENT_CMD_REPLY(kick, KICK),
60   SILC_CLIENT_CMD_REPLY(ban, BAN),
61   SILC_CLIENT_CMD_REPLY(close, CLOSE),
62   SILC_CLIENT_CMD_REPLY(shutdown, SHUTDOWN),
63   SILC_CLIENT_CMD_REPLY(silcoper, SILCOPER),
64   SILC_CLIENT_CMD_REPLY(leave, LEAVE),
65   SILC_CLIENT_CMD_REPLY(users, USERS),
66   SILC_CLIENT_CMD_REPLY(getkey, GETKEY),
67
68   { NULL, 0 },
69 };
70
71 const SilcCommandStatusMessage silc_command_status_messages[] = {
72
73   { STAT(NO_SUCH_NICK),      "There was no such nickname" },
74   { STAT(NO_SUCH_CHANNEL),   "There was no such channel" },
75   { STAT(NO_SUCH_SERVER),    "No such server" },
76   { STAT(TOO_MANY_TARGETS),  "Duplicate recipients. No message delivered" },
77   { STAT(NO_RECIPIENT),      "No recipient given" },
78   { STAT(UNKNOWN_COMMAND),   "Unknown command" },
79   { STAT(WILDCARDS),         "Unknown command" },
80   { STAT(NO_CLIENT_ID),      "No Client ID given" },
81   { STAT(NO_CHANNEL_ID),     "No Channel ID given" },
82   { STAT(NO_SERVER_ID),      "No Server ID given" },
83   { STAT(BAD_CLIENT_ID),     "Bad Client ID" },
84   { STAT(BAD_CHANNEL_ID),    "Bad Channel ID" },
85   { STAT(NO_SUCH_CLIENT_ID), "There was no such client" },
86   { STAT(NO_SUCH_CHANNEL_ID),"There was no such channel" },
87   { STAT(NICKNAME_IN_USE),   "Nickname already exists" },
88   { STAT(NOT_ON_CHANNEL),    "You are not on that channel" },
89   { STAT(USER_NOT_ON_CHANNEL),"They are not on the channel" },
90   { STAT(USER_ON_CHANNEL),   "User already on the channel" },
91   { STAT(NOT_REGISTERED),    "You have not registered" },
92   { STAT(NOT_ENOUGH_PARAMS), "Not enough parameters" },
93   { STAT(TOO_MANY_PARAMS),   "Too many parameters" },
94   { STAT(PERM_DENIED),       "Permission denied" },
95   { STAT(BANNED_FROM_SERVER),"You are banned from this server" },
96   { STAT(BAD_PASSWORD),      "Cannot join channel. Incorrect password" },
97   { STAT(CHANNEL_IS_FULL),   "Cannot join channel. Channel is full" },
98   { STAT(NOT_INVITED),     "Cannot join channel. You have not been invited" },
99   { STAT(BANNED_FROM_CHANNEL), "Cannot join channel. You have been banned" },
100   { STAT(UNKNOWN_MODE),    "Unknown mode" },
101   { STAT(NOT_YOU),         "Cannot change mode for other users" },
102   { STAT(NO_CHANNEL_PRIV), "Permission denied. You are not channel operator" },
103   { STAT(NO_CHANNEL_FOPRIV),"Permission denied. You are not channel founder" },
104   { STAT(NO_SERVER_PRIV),  "Permission denied. You are not server operator" },
105   { STAT(NO_ROUTER_PRIV),  "Permission denied. You are not SILC operator" },
106   { STAT(BAD_NICKNAME),    "Bad nickname" },
107   { STAT(BAD_CHANNEL),     "Bad channel name" },
108   { STAT(AUTH_FAILED),     "Authentication failed" },
109   { STAT(UNKNOWN_ALGORITHM), "Unsupported algorithm" },
110   { STAT(NO_SUCH_SERVER_ID), "No such Server ID" },
111
112   { 0, NULL }
113 };
114 /* Command reply operation that is called at the end of all command replys. 
115    Usage: COMMAND_REPLY((ARGS, argument1, argument2, etc...)), */
116 #define COMMAND_REPLY(args) cmd->client->ops->command_reply args
117 #define ARGS cmd->client, cmd->sock->user_data, \
118              cmd->payload, TRUE, silc_command_get(cmd->payload), status
119
120 /* Error reply to application. Usage: COMMAND_REPLY_ERROR; */
121 #define COMMAND_REPLY_ERROR cmd->client->ops->command_reply(cmd->client, \
122   cmd->sock->user_data, cmd->payload, FALSE, \
123   silc_command_get(cmd->payload), status)
124
125 /* Process received command reply. */
126
127 void silc_client_command_reply_process(SilcClient client,
128                                        SilcSocketConnection sock,
129                                        SilcPacketContext *packet)
130 {
131   SilcBuffer buffer = packet->buffer;
132   SilcClientCommandReply *cmd;
133   SilcClientCommandReplyContext ctx;
134   SilcCommandPayload payload;
135   SilcCommand command;
136   uint16 ident;
137
138   /* Get command reply payload from packet */
139   payload = silc_command_payload_parse(buffer);
140   if (!payload) {
141     /* Silently ignore bad reply packet */
142     SILC_LOG_DEBUG(("Bad command reply packet"));
143     return;
144   }
145   
146   /* Allocate command reply context. This must be free'd by the
147      command reply routine receiving it. */
148   ctx = silc_calloc(1, sizeof(*ctx));
149   ctx->client = client;
150   ctx->sock = sock;
151   ctx->payload = payload;
152   ctx->args = silc_command_get_args(ctx->payload);
153   ctx->packet = packet;
154   ident = silc_command_get_ident(ctx->payload);
155       
156   /* Check for pending commands and mark to be exeucted */
157   silc_client_command_pending_check(sock->user_data, ctx, 
158                                     silc_command_get(ctx->payload), ident);
159
160   /* Execute command reply */
161   command = silc_command_get(ctx->payload);
162   for (cmd = silc_command_reply_list; cmd->cb; cmd++)
163     if (cmd->cmd == command)
164       break;
165
166   if (cmd == NULL || !cmd->cb) {
167     silc_free(ctx);
168     return;
169   }
170
171   cmd->cb(ctx, 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   SilcClientID *client_id;
435   SilcIDCacheEntry id_cache = NULL;
436   SilcClientEntry client_entry = NULL;
437   int argc;
438   uint32 len;
439   unsigned char *id_data;
440   char *nickname = NULL, *username = NULL;
441   
442   argc = silc_argument_get_arg_num(cmd->args);
443
444   id_data = silc_argument_get_arg_type(cmd->args, 2, &len);
445   if (!id_data) {
446     COMMAND_REPLY_ERROR;
447     return;
448   }
449   
450   client_id = silc_id_payload_parse_id(id_data, len);
451   if (!client_id) {
452     COMMAND_REPLY_ERROR;
453     return;
454   }
455   
456   nickname = silc_argument_get_arg_type(cmd->args, 3, &len);
457   username = silc_argument_get_arg_type(cmd->args, 4, &len);
458
459   /* Check if we have this client cached already. */
460   if (!silc_idcache_find_by_id_one_ext(conn->client_cache, (void *)client_id, 
461                                        NULL, NULL, 
462                                        silc_hash_client_id_compare, NULL,
463                                        &id_cache)) {
464     SILC_LOG_DEBUG(("Adding new client entry"));
465
466     client_entry = silc_calloc(1, sizeof(*client_entry));
467     client_entry->id = client_id;
468     silc_parse_nickname(nickname, &client_entry->nickname, 
469                         &client_entry->server, &client_entry->num);
470     if (username)
471       client_entry->username = strdup(username);
472     
473     /* Add client to cache */
474     silc_idcache_add(conn->client_cache, client_entry->nickname,
475                      client_id, (void *)client_entry, FALSE);
476   } else {
477     client_entry = (SilcClientEntry)id_cache->context;
478     if (client_entry->nickname)
479       silc_free(client_entry->nickname);
480     if (client_entry->server)
481       silc_free(client_entry->server);
482     if (username && client_entry->username)
483       silc_free(client_entry->username);
484     
485     SILC_LOG_DEBUG(("Updating client entry"));
486
487     silc_parse_nickname(nickname, &client_entry->nickname, 
488                         &client_entry->server, &client_entry->num);
489     
490     if (username)
491       client_entry->username = strdup(username);
492     
493     /* Remove the old cache entry and create a new one */
494     silc_idcache_del_by_context(conn->client_cache, client_entry);
495     silc_idcache_add(conn->client_cache, client_entry->nickname, 
496                      client_entry->id, client_entry, FALSE);
497     silc_free(client_id);
498   }
499
500   /* Notify application */
501   COMMAND_REPLY((ARGS, client_entry, nickname, username));
502 }
503
504 /* Received reply for IDENTIFY command. This maybe called several times
505    for one IDENTIFY command as server may reply with list of results. 
506    This is totally silent and does not print anything on screen. */
507
508 SILC_CLIENT_CMD_REPLY_FUNC(identify)
509 {
510   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
511   SilcCommandStatus status;
512   unsigned char *tmp;
513
514   SILC_LOG_DEBUG(("Start"));
515
516   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
517   SILC_GET16_MSB(status, tmp);
518   if (status != SILC_STATUS_OK && 
519       status != SILC_STATUS_LIST_START &&
520       status != SILC_STATUS_LIST_ITEM &&
521       status != SILC_STATUS_LIST_END) {
522     COMMAND_REPLY_ERROR;
523     goto out;
524   }
525
526   /* Save one IDENTIFY entry */
527   if (status == SILC_STATUS_OK)
528     silc_client_command_reply_identify_save(cmd, status);
529
530   /* List */
531   if (status == SILC_STATUS_LIST_START ||
532       status == SILC_STATUS_LIST_ITEM ||
533       status == SILC_STATUS_LIST_END)
534     silc_client_command_reply_identify_save(cmd, status);
535
536   /* Pending callbacks are not executed if this was an list entry */
537   if (status != SILC_STATUS_OK &&
538       status != SILC_STATUS_LIST_END) {
539     silc_client_command_reply_free(cmd);
540     return;
541   }
542
543   /* Execute any pending command callbacks */
544   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_IDENTIFY);
545
546  out:
547   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_IDENTIFY);
548   silc_client_command_reply_free(cmd);
549 }
550
551 /* Received reply for command NICK. If everything went without errors
552    we just received our new Client ID. */
553
554 SILC_CLIENT_CMD_REPLY_FUNC(nick)
555 {
556   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
557   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
558   SilcCommandStatus status;
559   SilcIDPayload idp;
560   unsigned char *tmp;
561   uint32 argc, len;
562
563   SILC_LOG_DEBUG(("Start"));
564
565   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
566   if (status != SILC_STATUS_OK) {
567     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
568                           "Cannot set nickname: %s", 
569              silc_client_command_status_message(status));
570     COMMAND_REPLY_ERROR;
571     goto out;
572   }
573
574   argc = silc_argument_get_arg_num(cmd->args);
575   if (argc < 2 || argc > 2) {
576     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
577                           "Cannot set nickname: bad reply to command");
578     COMMAND_REPLY_ERROR;
579     goto out;
580   }
581
582   /* Take received Client ID */
583   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
584   idp = silc_id_payload_parse_data(tmp, len);
585   if (!idp) {
586     COMMAND_REPLY_ERROR;
587     goto out;
588   }
589   silc_client_receive_new_id(cmd->client, cmd->sock, idp);
590     
591   /* Notify application */
592   COMMAND_REPLY((ARGS, conn->local_entry));
593
594   /* Execute any pending command callbacks */
595   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_NICK);
596
597  out:
598   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_NICK);
599   silc_client_command_reply_free(cmd);
600 }
601
602 /* Received reply to the LIST command. */
603
604 SILC_CLIENT_CMD_REPLY_FUNC(list)
605 {
606   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
607   SilcCommandStatus status;
608   unsigned char *tmp, *name, *topic;
609   uint32 usercount = 0;
610
611   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
612   SILC_GET16_MSB(status, tmp);
613   if (status != SILC_STATUS_OK && 
614       status != SILC_STATUS_LIST_START &&
615       status != SILC_STATUS_LIST_ITEM &&
616       status != SILC_STATUS_LIST_END) {
617     COMMAND_REPLY_ERROR;
618     goto out;
619   }
620
621   name = silc_argument_get_arg_type(cmd->args, 3, NULL);
622   topic = silc_argument_get_arg_type(cmd->args, 4, NULL);
623   tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
624   if (tmp)
625     SILC_GET32_MSB(usercount, tmp);
626
627   /* Notify application */
628   COMMAND_REPLY((ARGS, NULL, name, topic, usercount));
629
630   /* Pending callbacks are not executed if this was an list entry */
631   if (status != SILC_STATUS_OK &&
632       status != SILC_STATUS_LIST_END) {
633     silc_client_command_reply_free(cmd);
634     return;
635   }
636
637   /* Execute any pending command callbacks */
638   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_LIST);
639
640  out:
641   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_LIST);
642   silc_client_command_reply_free(cmd);
643 }
644
645 /* Received reply to topic command. */
646
647 SILC_CLIENT_CMD_REPLY_FUNC(topic)
648 {
649   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
650   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
651   SilcCommandStatus status;
652   SilcChannelEntry channel;
653   SilcChannelID *channel_id = NULL;
654   SilcIDCacheEntry id_cache = NULL;
655   unsigned char *tmp;
656   char *topic;
657   uint32 argc, len;
658
659   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
660   if (status != SILC_STATUS_OK) {
661     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
662              "%s", silc_client_command_status_message(status));
663     COMMAND_REPLY_ERROR;
664     SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_TOPIC);
665     silc_client_command_reply_free(cmd);
666     return;
667   }
668
669   argc = silc_argument_get_arg_num(cmd->args);
670   if (argc < 1 || argc > 3) {
671     COMMAND_REPLY_ERROR;
672     goto out;
673   }
674
675   /* Take Channel ID */
676   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
677   if (!tmp)
678     goto out;
679
680   /* Take topic */
681   topic = silc_argument_get_arg_type(cmd->args, 3, NULL);
682   if (!topic)
683     goto out;
684
685   channel_id = silc_id_payload_parse_id(tmp, len);
686   if (!channel_id)
687     goto out;
688
689   /* Get the channel entry */
690   if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
691                                    &id_cache)) {
692     silc_free(channel_id);
693     COMMAND_REPLY_ERROR;
694     goto out;
695   }
696   
697   channel = (SilcChannelEntry)id_cache->context;
698
699   /* Notify application */
700   COMMAND_REPLY((ARGS, channel, topic));
701
702   /* Execute any pending command callbacks */
703   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_TOPIC);
704
705  out:
706   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_TOPIC);
707   silc_client_command_reply_free(cmd);
708 }
709
710 /* Received reply to invite command. */
711
712 SILC_CLIENT_CMD_REPLY_FUNC(invite)
713 {
714   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
715   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
716   SilcCommandStatus status;
717   SilcChannelEntry channel;
718   SilcChannelID *channel_id;
719   SilcIDCacheEntry id_cache;
720   unsigned char *tmp;
721   uint32 len;
722
723   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
724   SILC_GET16_MSB(status, tmp);
725   if (status != SILC_STATUS_OK) {
726     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
727              "%s", silc_client_command_status_message(status));
728     COMMAND_REPLY_ERROR;
729     SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INVITE);
730     silc_client_command_reply_free(cmd);
731     return;
732   }
733
734   /* Take Channel ID */
735   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
736   if (!tmp)
737     goto out;
738
739   channel_id = silc_id_payload_parse_id(tmp, len);
740   if (!channel_id)
741     goto out;
742
743   /* Get the channel entry */
744   if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
745                                    &id_cache)) {
746     silc_free(channel_id);
747     COMMAND_REPLY_ERROR;
748     goto out;
749   }
750   
751   channel = (SilcChannelEntry)id_cache->context;
752
753   /* Get the invite list */
754   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
755
756   /* Notify application */
757   COMMAND_REPLY((ARGS, channel, tmp));
758
759   /* Execute any pending command callbacks */
760   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INVITE);
761
762  out:
763   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INVITE);
764   silc_client_command_reply_free(cmd);
765 }
766
767 /* Received reply to the KILL command. */
768  
769 SILC_CLIENT_CMD_REPLY_FUNC(kill)
770 {
771   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
772   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
773   SilcCommandStatus status;
774   unsigned char *tmp;
775
776   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
777   SILC_GET16_MSB(status, tmp);
778   if (status != SILC_STATUS_OK) {
779     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
780              "%s", silc_client_command_status_message(status));
781     COMMAND_REPLY_ERROR;
782     goto out;
783   }
784
785   /* Notify application */
786   COMMAND_REPLY((ARGS));
787
788   /* Execute any pending command callbacks */
789   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_KILL);
790
791  out:
792   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_KILL);
793   silc_client_command_reply_free(cmd);
794 }
795
796 /* Received reply to INFO command. We receive the server ID and some
797    information about the server user requested. */
798
799 SILC_CLIENT_CMD_REPLY_FUNC(info)
800 {
801   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
802   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
803   SilcCommandStatus status;
804   unsigned char *tmp;
805   SilcIDCacheEntry id_cache;
806   SilcServerEntry server;
807   SilcServerID *server_id = NULL;
808   char *server_name, *server_info;
809   uint32 len;
810
811   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
812   SILC_GET16_MSB(status, tmp);
813   if (status != SILC_STATUS_OK) {
814     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
815              "%s", silc_client_command_status_message(status));
816     COMMAND_REPLY_ERROR;
817     SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INFO);
818     silc_client_command_reply_free(cmd);
819     return;
820   }
821
822   /* Get server ID */
823   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
824   if (!tmp)
825     goto out;
826
827   server_id = silc_id_payload_parse_id(tmp, len);
828   if (!server_id)
829     goto out;
830
831   /* Get server name */
832   server_name = silc_argument_get_arg_type(cmd->args, 3, NULL);
833   if (!server_name)
834     goto out;
835
836   /* Get server info */
837   server_info = silc_argument_get_arg_type(cmd->args, 4, NULL);
838   if (!server_info)
839     goto out;
840
841   /* See whether we have this server cached. If not create it. */
842   if (!silc_idcache_find_by_id_one(conn->server_cache, (void *)server_id,
843                                    &id_cache)) {
844     SILC_LOG_DEBUG(("New server entry"));
845
846     server = silc_calloc(1, sizeof(*server));
847     server->server_name = strdup(server_name);
848     server->server_info = strdup(server_info);
849     server->server_id = silc_id_dup(server_id, SILC_ID_SERVER);
850
851     /* Add it to the cache */
852     silc_idcache_add(conn->server_cache, server->server_name,
853                      server->server_id, (void *)server, FALSE);
854   } else {
855     server = (SilcServerEntry)id_cache->context;
856   }
857   
858   /* Notify application */
859   COMMAND_REPLY((ARGS, server, server->server_name, server->server_info));
860
861   /* Execute any pending command callbacks */
862   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_INFO);
863
864  out:
865   if (server_id)
866     silc_free(server_id);
867   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_INFO);
868   silc_client_command_reply_free(cmd);
869 }
870
871 /* Received reply to PING command. The reply time is shown to user. */
872
873 SILC_CLIENT_CMD_REPLY_FUNC(ping)
874 {
875   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
876   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
877   SilcCommandStatus status;
878   void *id;
879   int i;
880   time_t diff, curtime;
881
882   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
883   if (status != SILC_STATUS_OK) {
884     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
885              "%s", silc_client_command_status_message(status));
886     COMMAND_REPLY_ERROR;
887     goto out;
888   }
889
890   curtime = time(NULL);
891   id = silc_id_str2id(cmd->packet->src_id, cmd->packet->src_id_len,
892                       cmd->packet->src_id_type);
893   if (!id) {
894     COMMAND_REPLY_ERROR;
895     goto out;
896   }
897
898   for (i = 0; i < conn->ping_count; i++) {
899     if (SILC_ID_SERVER_COMPARE(conn->ping[i].dest_id, id)) {
900       diff = curtime - conn->ping[i].start_time;
901       cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO, 
902                             "Ping reply from %s: %d second%s", 
903                             conn->ping[i].dest_name, diff, 
904                             diff == 1 ? "" : "s");
905       
906       conn->ping[i].start_time = 0;
907       silc_free(conn->ping[i].dest_id);
908       conn->ping[i].dest_id = NULL;
909       silc_free(conn->ping[i].dest_name);
910       conn->ping[i].dest_name = NULL;
911       break;
912     }
913   }
914
915   silc_free(id);
916
917   /* Notify application */
918   COMMAND_REPLY((ARGS));
919
920   /* Execute any pending command callbacks */
921   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_PING);
922
923  out:
924   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_PING);
925   silc_client_command_reply_free(cmd);
926 }
927
928 /* Received reply for JOIN command. */
929
930 SILC_CLIENT_CMD_REPLY_FUNC(join)
931 {
932   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
933   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
934   SilcCommandStatus status;
935   SilcIDPayload idp = NULL;
936   SilcChannelEntry channel;
937   SilcIDCacheEntry id_cache = NULL;
938   SilcChannelUser chu;
939   uint32 argc, mode, len, list_count;
940   char *topic, *tmp, *channel_name = NULL, *hmac;
941   SilcBuffer keyp = NULL, client_id_list, client_mode_list;
942   int i;
943
944   SILC_LOG_DEBUG(("Start"));
945
946   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
947   if (status != SILC_STATUS_OK) {
948     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
949              "%s", silc_client_command_status_message(status));
950     COMMAND_REPLY_ERROR;
951     goto out;
952   }
953
954   argc = silc_argument_get_arg_num(cmd->args);
955   if (argc < 7 || argc > 14) {
956     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
957              "Cannot join channel: Bad reply packet");
958     COMMAND_REPLY_ERROR;
959     goto out;
960   }
961
962   /* Get channel name */
963   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
964   if (!tmp) {
965     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
966                           "Cannot join channel: Bad reply packet");
967     COMMAND_REPLY_ERROR;
968     goto out;
969   }
970   channel_name = strdup(tmp);
971
972   /* Get Channel ID */
973   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
974   if (!tmp) {
975     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
976                           "Cannot join channel: Bad reply packet");
977     COMMAND_REPLY_ERROR;
978     silc_free(channel_name);
979     goto out;
980   }
981   idp = silc_id_payload_parse_data(tmp, len);
982   if (!idp) {
983     COMMAND_REPLY_ERROR;
984     silc_free(channel_name);
985     goto out;
986   }
987
988   /* Get channel mode */
989   tmp = silc_argument_get_arg_type(cmd->args, 5, NULL);
990   if (tmp)
991     SILC_GET32_MSB(mode, tmp);
992   else
993     mode = 0;
994
995   /* Get channel key */
996   tmp = silc_argument_get_arg_type(cmd->args, 7, &len);
997   if (tmp) {
998     keyp = silc_buffer_alloc(len);
999     silc_buffer_pull_tail(keyp, SILC_BUFFER_END(keyp));
1000     silc_buffer_put(keyp, tmp, len);
1001   }
1002
1003   /* Get topic */
1004   topic = silc_argument_get_arg_type(cmd->args, 10, NULL);
1005
1006   /* Save received Channel ID. This actually creates the channel */
1007   channel = silc_client_new_channel_id(cmd->client, cmd->sock, channel_name, 
1008                                        mode, idp);
1009   silc_id_payload_free(idp);
1010
1011   /* Get hmac */
1012   hmac = silc_argument_get_arg_type(cmd->args, 11, NULL);
1013   if (hmac) {
1014     if (!silc_hmac_alloc(hmac, NULL, &channel->hmac)) {
1015       cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, 
1016                             "Cannot join channel: Unsupported HMAC `%s'",
1017                             hmac);
1018       COMMAND_REPLY_ERROR;
1019       silc_free(channel_name);
1020       goto out;
1021     }
1022   }
1023
1024   /* Get the list count */
1025   tmp = silc_argument_get_arg_type(cmd->args, 12, &len);
1026   if (!tmp)
1027     goto out;
1028   SILC_GET32_MSB(list_count, tmp);
1029
1030   /* Get Client ID list */
1031   tmp = silc_argument_get_arg_type(cmd->args, 13, &len);
1032   if (!tmp)
1033     goto out;
1034
1035   client_id_list = silc_buffer_alloc(len);
1036   silc_buffer_pull_tail(client_id_list, len);
1037   silc_buffer_put(client_id_list, tmp, len);
1038
1039   /* Get client mode list */
1040   tmp = silc_argument_get_arg_type(cmd->args, 14, &len);
1041   if (!tmp)
1042     goto out;
1043
1044   client_mode_list = silc_buffer_alloc(len);
1045   silc_buffer_pull_tail(client_mode_list, len);
1046   silc_buffer_put(client_mode_list, tmp, len);
1047
1048   /* Add clients we received in the reply to the channel */
1049   for (i = 0; i < list_count; i++) {
1050     uint16 idp_len;
1051     uint32 mode;
1052     SilcClientID *client_id;
1053     SilcClientEntry client_entry;
1054
1055     /* Client ID */
1056     SILC_GET16_MSB(idp_len, client_id_list->data + 2);
1057     idp_len += 4;
1058     client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
1059     if (!client_id)
1060       continue;
1061
1062     /* Mode */
1063     SILC_GET32_MSB(mode, client_mode_list->data);
1064
1065     /* Check if we have this client cached already. */
1066     if (!silc_idcache_find_by_id_one_ext(conn->client_cache, 
1067                                          (void *)client_id, 
1068                                          NULL, NULL, 
1069                                          silc_hash_client_id_compare, NULL,
1070                                          &id_cache)) {
1071       /* No, we don't have it, add entry for it. */
1072       client_entry = silc_calloc(1, sizeof(*client_entry));
1073       client_entry->id = silc_id_dup(client_id, SILC_ID_CLIENT);
1074       silc_idcache_add(conn->client_cache, NULL, client_entry->id, 
1075                        (void *)client_entry, FALSE);
1076     } else {
1077       /* Yes, we have it already */
1078       client_entry = (SilcClientEntry)id_cache->context;
1079     }
1080
1081     /* Join the client to the channel */
1082     chu = silc_calloc(1, sizeof(*chu));
1083     chu->client = client_entry;
1084     chu->mode = mode;
1085     silc_list_add(channel->clients, chu);
1086     silc_free(client_id);
1087
1088     silc_buffer_pull(client_id_list, idp_len);
1089     silc_buffer_pull(client_mode_list, 4);
1090   }
1091   silc_buffer_push(client_id_list, client_id_list->data - 
1092                    client_id_list->head);
1093   silc_buffer_push(client_mode_list, client_mode_list->data - 
1094                    client_mode_list->head);
1095
1096   /* Save channel key */
1097   if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY))
1098     silc_client_save_channel_key(conn, keyp, channel);
1099
1100   /* Client is now joined to the channel */
1101   channel->on_channel = TRUE;
1102
1103   /* Notify application */
1104   COMMAND_REPLY((ARGS, channel_name, channel, mode, 0, 
1105                  keyp ? keyp->head : NULL, NULL,
1106                  NULL, topic, hmac, list_count, client_id_list, 
1107                  client_mode_list));
1108
1109   /* Execute any pending command callbacks */
1110   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_JOIN);
1111
1112   if (keyp)
1113     silc_buffer_free(keyp);
1114   silc_buffer_free(client_id_list);
1115   silc_buffer_free(client_mode_list);
1116
1117  out:
1118   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_JOIN);
1119   silc_client_command_reply_free(cmd);
1120 }
1121
1122 /* Received reply for MOTD command */
1123
1124 SILC_CLIENT_CMD_REPLY_FUNC(motd)
1125 {
1126   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1127   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1128   SilcCommandStatus status;
1129   uint32 argc, i;
1130   unsigned char *tmp;
1131   char *motd = NULL, *cp, line[256];
1132
1133   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1134   SILC_GET16_MSB(status, tmp);
1135   if (status != SILC_STATUS_OK) {
1136     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1137              "%s", silc_client_command_status_message(status));
1138     COMMAND_REPLY_ERROR;
1139     return;
1140   }
1141
1142   argc = silc_argument_get_arg_num(cmd->args);
1143   if (argc > 3) {
1144     COMMAND_REPLY_ERROR;
1145     goto out;
1146   }
1147
1148   if (argc == 3) {
1149     motd = silc_argument_get_arg_type(cmd->args, 3, NULL);
1150     if (!motd) {
1151       COMMAND_REPLY_ERROR;
1152       goto out;
1153     }
1154
1155     i = 0;
1156     cp = motd;
1157     while(cp[i] != 0) {
1158       if (cp[i++] == '\n') {
1159         memset(line, 0, sizeof(line));
1160         strncat(line, cp, i - 1);
1161         cp += i;
1162         
1163         if (i == 2)
1164           line[0] = ' ';
1165         
1166         cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1167                               "%s", line);
1168         
1169         if (!strlen(cp))
1170           break;
1171         i = 0;
1172       }
1173     }
1174   }
1175
1176   /* Notify application */
1177   COMMAND_REPLY((ARGS, motd));
1178
1179   /* Execute any pending command callbacks */
1180   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_MOTD);
1181
1182  out:
1183   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_MOTD);
1184   silc_client_command_reply_free(cmd);
1185 }
1186
1187 /* Received reply tot he UMODE command. Save the current user mode */
1188
1189 SILC_CLIENT_CMD_REPLY_FUNC(umode)
1190 {
1191   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1192   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1193   SilcCommandStatus status;
1194   unsigned char *tmp;
1195   uint32 mode;
1196
1197   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1198   SILC_GET16_MSB(status, tmp);
1199   if (status != SILC_STATUS_OK) {
1200     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1201              "%s", silc_client_command_status_message(status));
1202     COMMAND_REPLY_ERROR;
1203     goto out;
1204   }
1205
1206   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
1207   if (!tmp) {
1208     COMMAND_REPLY_ERROR;
1209     goto out;
1210   }
1211
1212   SILC_GET32_MSB(mode, tmp);
1213   conn->local_entry->mode = mode;
1214
1215   /* Notify application */
1216   COMMAND_REPLY((ARGS, mode));
1217
1218   /* Execute any pending command callbacks */
1219   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_UMODE);
1220
1221  out:
1222   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_UMODE);
1223   silc_client_command_reply_free(cmd);
1224 }
1225
1226 /* Received reply for CMODE command. */
1227
1228 SILC_CLIENT_CMD_REPLY_FUNC(cmode)
1229 {
1230   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1231   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1232   SilcCommandStatus status;
1233   unsigned char *tmp;
1234   uint32 mode;
1235   SilcIDCacheEntry id_cache;
1236   SilcChannelID *channel_id;
1237   SilcChannelEntry channel;
1238   uint32 len;
1239
1240   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
1241   if (status != SILC_STATUS_OK) {
1242     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1243              "%s", silc_client_command_status_message(status));
1244     COMMAND_REPLY_ERROR;
1245     goto out;
1246   }
1247
1248   /* Take Channel ID */
1249   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1250   if (!tmp)
1251     goto out;
1252   channel_id = silc_id_payload_parse_id(tmp, len);
1253   if (!channel_id)
1254     goto out;
1255
1256   /* Get the channel entry */
1257   if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
1258                                    &id_cache)) {
1259     silc_free(channel_id);
1260     COMMAND_REPLY_ERROR;
1261     goto out;
1262   }
1263   
1264   channel = (SilcChannelEntry)id_cache->context;
1265
1266   /* Get channel mode */
1267   tmp = silc_argument_get_arg_type(cmd->args, 3, NULL);
1268   if (!tmp) {
1269     silc_free(channel_id);
1270     COMMAND_REPLY_ERROR;
1271     goto out;
1272   }
1273
1274   /* Save the mode */
1275   SILC_GET32_MSB(mode, tmp);
1276   channel->mode = mode;
1277
1278   /* Notify application */
1279   COMMAND_REPLY((ARGS, channel, mode));
1280   silc_free(channel_id);
1281
1282   /* Execute any pending command callbacks */
1283   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CMODE);
1284
1285  out:
1286   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CMODE);
1287   silc_client_command_reply_free(cmd);
1288 }
1289
1290 /* Received reply for CUMODE command */
1291
1292 SILC_CLIENT_CMD_REPLY_FUNC(cumode)
1293 {
1294   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1295   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1296   SilcCommandStatus status;
1297   SilcIDCacheEntry id_cache = NULL;
1298   SilcClientID *client_id;
1299   SilcChannelID *channel_id;
1300   SilcClientEntry client_entry;
1301   SilcChannelEntry channel;
1302   SilcChannelUser chu;
1303   unsigned char *tmp, *id;
1304   uint32 len, mode;
1305   
1306   SILC_GET16_MSB(status, silc_argument_get_arg_type(cmd->args, 1, NULL));
1307   if (status != SILC_STATUS_OK) {
1308     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1309              "%s", silc_client_command_status_message(status));
1310     COMMAND_REPLY_ERROR;
1311     goto out;
1312   }
1313   
1314   /* Get channel mode */
1315   tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
1316   if (!tmp) {
1317     COMMAND_REPLY_ERROR;
1318     goto out;
1319   }
1320
1321   /* Take Channel ID */
1322   tmp = silc_argument_get_arg_type(cmd->args, 3, &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 Client ID */
1340   id = silc_argument_get_arg_type(cmd->args, 4, &len);
1341   if (!id) {
1342     silc_free(channel_id);
1343     COMMAND_REPLY_ERROR;
1344     goto out;
1345   }
1346   client_id = silc_id_payload_parse_id(id, len);
1347   if (!client_id) {
1348     silc_free(channel_id);
1349     COMMAND_REPLY_ERROR;
1350     goto out;
1351   }
1352   
1353   /* Get client entry */
1354   if (!silc_idcache_find_by_id_one_ext(conn->client_cache, (void *)client_id, 
1355                                        NULL, NULL, 
1356                                        silc_hash_client_id_compare, NULL,
1357                                        &id_cache)) {
1358     silc_free(channel_id);
1359     silc_free(client_id);
1360     COMMAND_REPLY_ERROR;
1361     goto out;
1362   }
1363
1364   client_entry = (SilcClientEntry)id_cache->context;
1365
1366   /* Save the mode */
1367   SILC_GET32_MSB(mode, tmp);
1368   while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1369     if (chu->client == client_entry) {
1370       chu->mode = mode;
1371       break;
1372     }
1373   }
1374
1375   /* Notify application */
1376   COMMAND_REPLY((ARGS, mode, channel, client_entry));
1377   silc_free(client_id);
1378   silc_free(channel_id);
1379   
1380   /* Execute any pending command callbacks */
1381   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CUMODE);
1382
1383  out:
1384   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CUMODE);
1385   silc_client_command_reply_free(cmd);
1386 }
1387
1388 SILC_CLIENT_CMD_REPLY_FUNC(kick)
1389 {
1390   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1391   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1392   SilcCommandStatus status;
1393   unsigned char *tmp;
1394
1395   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1396   SILC_GET16_MSB(status, tmp);
1397   if (status != SILC_STATUS_OK) {
1398     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1399              "%s", silc_client_command_status_message(status));
1400     COMMAND_REPLY_ERROR;
1401     goto out;
1402   }
1403
1404   /* Notify application */
1405   COMMAND_REPLY((ARGS));
1406
1407   /* Execute any pending command callbacks */
1408   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_KICK);
1409
1410  out:
1411   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_KICK);
1412   silc_client_command_reply_free(cmd);
1413 }
1414
1415 SILC_CLIENT_CMD_REPLY_FUNC(silcoper)
1416 {
1417   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1418   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1419   SilcCommandStatus status;
1420   unsigned char *tmp;
1421
1422   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1423   SILC_GET16_MSB(status, tmp);
1424   if (status != SILC_STATUS_OK) {
1425     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1426              "%s", silc_client_command_status_message(status));
1427     COMMAND_REPLY_ERROR;
1428     goto out;
1429   }
1430
1431   /* Notify application */
1432   COMMAND_REPLY((ARGS));
1433
1434   /* Execute any pending command callbacks */
1435   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_SILCOPER);
1436
1437  out:
1438   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_SILCOPER);
1439   silc_client_command_reply_free(cmd);
1440 }
1441
1442 SILC_CLIENT_CMD_REPLY_FUNC(oper)
1443 {
1444   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1445   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1446   SilcCommandStatus status;
1447   unsigned char *tmp;
1448
1449   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1450   SILC_GET16_MSB(status, tmp);
1451   if (status != SILC_STATUS_OK) {
1452     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1453              "%s", silc_client_command_status_message(status));
1454     COMMAND_REPLY_ERROR;
1455     goto out;
1456   }
1457
1458   /* Notify application */
1459   COMMAND_REPLY((ARGS));
1460
1461   /* Execute any pending command callbacks */
1462   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_OPER);
1463
1464  out:
1465   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_OPER);
1466   silc_client_command_reply_free(cmd);
1467 }
1468
1469 SILC_CLIENT_CMD_REPLY_FUNC(connect)
1470 {
1471   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1472   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1473   SilcCommandStatus status;
1474   unsigned char *tmp;
1475
1476   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1477   SILC_GET16_MSB(status, tmp);
1478   if (status != SILC_STATUS_OK) {
1479     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1480              "%s", silc_client_command_status_message(status));
1481     COMMAND_REPLY_ERROR;
1482     goto out;
1483   }
1484
1485   /* Notify application */
1486   COMMAND_REPLY((ARGS));
1487
1488   /* Execute any pending command callbacks */
1489   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CONNECT);
1490
1491  out:
1492   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CONNECT);
1493   silc_client_command_reply_free(cmd);
1494 }
1495
1496 SILC_CLIENT_CMD_REPLY_FUNC(ban)
1497 {
1498   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1499   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1500   SilcCommandStatus status;
1501   SilcIDCacheEntry id_cache = NULL;
1502   SilcChannelEntry channel;
1503   SilcChannelID *channel_id;
1504   unsigned char *tmp;
1505   uint32 len;
1506
1507   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1508   SILC_GET16_MSB(status, tmp);
1509   if (status != SILC_STATUS_OK) {
1510     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1511              "%s", silc_client_command_status_message(status));
1512     COMMAND_REPLY_ERROR;
1513     goto out;
1514   }
1515
1516   /* Take Channel ID */
1517   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1518   if (!tmp)
1519     goto out;
1520
1521   channel_id = silc_id_payload_parse_id(tmp, len);
1522   if (!channel_id)
1523     goto out;
1524
1525   /* Get the channel entry */
1526   if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
1527                                    &id_cache)) {
1528     silc_free(channel_id);
1529     COMMAND_REPLY_ERROR;
1530     goto out;
1531   }
1532   
1533   channel = (SilcChannelEntry)id_cache->context;
1534
1535   /* Get the ban list */
1536   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
1537
1538   /* Notify application */
1539   COMMAND_REPLY((ARGS, channel, tmp));
1540
1541   /* Execute any pending command callbacks */
1542   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_BAN);
1543
1544  out:
1545   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_BAN);
1546   silc_client_command_reply_free(cmd);
1547 }
1548
1549 SILC_CLIENT_CMD_REPLY_FUNC(close)
1550 {
1551   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1552   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1553   SilcCommandStatus status;
1554   unsigned char *tmp;
1555
1556   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1557   SILC_GET16_MSB(status, tmp);
1558   if (status != SILC_STATUS_OK) {
1559     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1560              "%s", silc_client_command_status_message(status));
1561     COMMAND_REPLY_ERROR;
1562     goto out;
1563   }
1564
1565   /* Notify application */
1566   COMMAND_REPLY((ARGS));
1567
1568   /* Execute any pending command callbacks */
1569   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_CLOSE);
1570
1571  out:
1572   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_CLOSE);
1573   silc_client_command_reply_free(cmd);
1574 }
1575  
1576 SILC_CLIENT_CMD_REPLY_FUNC(shutdown)
1577 {
1578   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1579   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1580   SilcCommandStatus status;
1581   unsigned char *tmp;
1582
1583   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1584   SILC_GET16_MSB(status, tmp);
1585   if (status != SILC_STATUS_OK) {
1586     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1587              "%s", silc_client_command_status_message(status));
1588     COMMAND_REPLY_ERROR;
1589     goto out;
1590   }
1591
1592   /* Notify application */
1593   COMMAND_REPLY((ARGS));
1594
1595   /* Execute any pending command callbacks */
1596   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_SHUTDOWN);
1597
1598  out:
1599   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_SHUTDOWN);
1600   silc_client_command_reply_free(cmd);
1601 }
1602  
1603 /* Reply to LEAVE command. */
1604
1605 SILC_CLIENT_CMD_REPLY_FUNC(leave)
1606 {
1607   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1608   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1609   SilcCommandStatus status;
1610   unsigned char *tmp;
1611
1612   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1613   SILC_GET16_MSB(status, tmp);
1614   if (status != SILC_STATUS_OK) {
1615     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1616              "%s", silc_client_command_status_message(status));
1617     COMMAND_REPLY_ERROR;
1618     goto out;
1619   }
1620
1621   /* Notify application */
1622   COMMAND_REPLY((ARGS));
1623
1624   /* Execute any pending command callbacks */
1625   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_LEAVE);
1626
1627  out:
1628   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_LEAVE);
1629   silc_client_command_reply_free(cmd);
1630 }
1631
1632 /* Reply to USERS command. Received list of client ID's and theirs modes
1633    on the channel we requested. */
1634
1635 SILC_CLIENT_CMD_REPLY_FUNC(users)
1636 {
1637   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1638   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1639   SilcCommandStatus status;
1640   SilcIDCacheEntry id_cache = NULL;
1641   SilcChannelEntry channel;
1642   SilcChannelUser chu;
1643   SilcChannelID *channel_id = NULL;
1644   SilcBuffer client_id_list;
1645   SilcBuffer client_mode_list;
1646   unsigned char *tmp;
1647   uint32 tmp_len, list_count;
1648   int i;
1649   unsigned char **res_argv = NULL;
1650   uint32 *res_argv_lens = NULL, *res_argv_types = NULL, res_argc = 0;
1651
1652   SILC_LOG_DEBUG(("Start"));
1653
1654   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1655   SILC_GET16_MSB(status, tmp);
1656   if (status != SILC_STATUS_OK) {
1657     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1658              "%s", silc_client_command_status_message(status));
1659     COMMAND_REPLY_ERROR;
1660     goto out;
1661   }
1662
1663   /* Get channel ID */
1664   tmp = silc_argument_get_arg_type(cmd->args, 2, &tmp_len);
1665   if (!tmp)
1666     goto out;
1667   channel_id = silc_id_payload_parse_id(tmp, tmp_len);
1668   if (!channel_id)
1669     goto out;
1670
1671   /* Get the list count */
1672   tmp = silc_argument_get_arg_type(cmd->args, 3, &tmp_len);
1673   if (!tmp)
1674     goto out;
1675   SILC_GET32_MSB(list_count, tmp);
1676
1677   /* Get Client ID list */
1678   tmp = silc_argument_get_arg_type(cmd->args, 4, &tmp_len);
1679   if (!tmp)
1680     goto out;
1681
1682   client_id_list = silc_buffer_alloc(tmp_len);
1683   silc_buffer_pull_tail(client_id_list, tmp_len);
1684   silc_buffer_put(client_id_list, tmp, tmp_len);
1685
1686   /* Get client mode list */
1687   tmp = silc_argument_get_arg_type(cmd->args, 5, &tmp_len);
1688   if (!tmp)
1689     goto out;
1690
1691   client_mode_list = silc_buffer_alloc(tmp_len);
1692   silc_buffer_pull_tail(client_mode_list, tmp_len);
1693   silc_buffer_put(client_mode_list, tmp, tmp_len);
1694
1695   /* Get channel entry */
1696   if (!silc_idcache_find_by_id_one(conn->channel_cache, (void *)channel_id,
1697                                    &id_cache)) {
1698     COMMAND_REPLY_ERROR;
1699     goto out;
1700   }
1701   channel = (SilcChannelEntry)id_cache->context;
1702
1703   /* Remove old client list from channel. */
1704   silc_list_start(channel->clients);
1705   while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1706     silc_list_del(channel->clients, chu);
1707     silc_free(chu);
1708   }
1709
1710   /* Cache the received Client ID's and modes. This cache expires
1711      whenever server sends notify message to channel. It means two things;
1712      some user has joined or leaved the channel. XXX! */
1713   for (i = 0; i < list_count; i++) {
1714     uint16 idp_len;
1715     uint32 mode;
1716     SilcClientID *client_id;
1717     SilcClientEntry client;
1718
1719     /* Client ID */
1720     SILC_GET16_MSB(idp_len, client_id_list->data + 2);
1721     idp_len += 4;
1722     client_id = silc_id_payload_parse_id(client_id_list->data, idp_len);
1723     if (!client_id)
1724       continue;
1725
1726     /* Mode */
1727     SILC_GET32_MSB(mode, client_mode_list->data);
1728
1729     /* Check if we have this client cached already. */
1730     id_cache = NULL;
1731     silc_idcache_find_by_id_one_ext(conn->client_cache, 
1732                                     (void *)client_id, 
1733                                     NULL, NULL, 
1734                                     silc_hash_client_id_compare, NULL,
1735                                     &id_cache);
1736
1737     if (!id_cache || !((SilcClientEntry)id_cache->context)->username) {
1738       /* No we don't have it (or it is incomplete in information), query
1739          it from the server. Assemble argument table that will be sent
1740          for the WHOIS command later. */
1741       res_argv = silc_realloc(res_argv, sizeof(*res_argv) *
1742                               (res_argc + 1));
1743       res_argv_lens = silc_realloc(res_argv_lens, sizeof(*res_argv_lens) *
1744                                    (res_argc + 1));
1745       res_argv_types = silc_realloc(res_argv_types, sizeof(*res_argv_types) *
1746                                     (res_argc + 1));
1747       res_argv[res_argc] = client_id_list->data;
1748       res_argv_lens[res_argc] = idp_len;
1749       res_argv_types[res_argc] = res_argc + 3;
1750       res_argc++;
1751     } else {
1752       /* Found the client, join it to the channel */
1753       client = (SilcClientEntry)id_cache->context;
1754       chu = silc_calloc(1, sizeof(*chu));
1755       chu->client = client;
1756       chu->mode = mode;
1757       silc_list_add(channel->clients, chu);
1758
1759       silc_free(client_id);
1760       id_cache = NULL;
1761     }
1762
1763     silc_buffer_pull(client_id_list, idp_len);
1764     silc_buffer_pull(client_mode_list, 4);
1765   }
1766
1767   /* Query the client information from server if the list included clients
1768      that we don't know about. */
1769   if (res_argc) {
1770     SilcBuffer res_cmd;
1771
1772     /* Send the WHOIS command to server */
1773     res_cmd = silc_command_payload_encode(SILC_COMMAND_WHOIS,
1774                                           res_argc, res_argv, res_argv_lens,
1775                                           res_argv_types, ++conn->cmd_ident);
1776     silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, 
1777                             NULL, 0, NULL, NULL, res_cmd->data, res_cmd->len,
1778                             TRUE);
1779
1780     /* Register pending command callback. After we've received the WHOIS
1781        command reply we will reprocess this command reply by re-calling this
1782        USERS command reply callback. */
1783     silc_client_command_pending(conn, SILC_COMMAND_WHOIS, conn->cmd_ident,
1784                                 NULL, silc_client_command_reply_users, cmd);
1785
1786     silc_buffer_free(res_cmd);
1787     if (channel_id)
1788       silc_free(channel_id);
1789
1790     silc_free(res_argv);
1791     silc_free(res_argv_lens);
1792     silc_free(res_argv_types);
1793     return;
1794   }
1795
1796   /* Notify application */
1797   COMMAND_REPLY((ARGS, channel, list_count, client_id_list, client_mode_list));
1798
1799   /* Execute any pending command callbacks */
1800   SILC_CLIENT_PENDING_EXEC(cmd, SILC_COMMAND_USERS);
1801
1802   silc_buffer_free(client_id_list);
1803   silc_buffer_free(client_mode_list);
1804
1805  out:
1806   if (channel_id)
1807     silc_free(channel_id);
1808   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_USERS);
1809   silc_client_command_reply_free(cmd);
1810 }
1811
1812 /* Received command reply to GETKEY command. WE've received the remote
1813    client's public key. */
1814
1815 SILC_CLIENT_CMD_REPLY_FUNC(getkey)
1816 {
1817   SilcClientCommandReplyContext cmd = (SilcClientCommandReplyContext)context;
1818   SilcClientConnection conn = (SilcClientConnection)cmd->sock->user_data;
1819   SilcCommandStatus status;
1820   SilcIDCacheEntry id_cache;
1821   SilcIDPayload idp = NULL;
1822   SilcClientID *client_id = NULL;
1823   SilcClientEntry client_entry;
1824   SilcSKEPKType type;
1825   unsigned char *tmp, *pk;
1826   uint32 len;
1827   uint16 pk_len;
1828   SilcIdType id_type;
1829   SilcPublicKey public_key = NULL;
1830
1831   SILC_LOG_DEBUG(("Start"));
1832
1833   tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
1834   SILC_GET16_MSB(status, tmp);
1835   if (status != SILC_STATUS_OK) {
1836     cmd->client->ops->say(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1837              "%s", silc_client_command_status_message(status));
1838     COMMAND_REPLY_ERROR;
1839     goto out;
1840   }
1841
1842   tmp = silc_argument_get_arg_type(cmd->args, 2, &len);
1843   if (!tmp)
1844     goto out;
1845   idp = silc_id_payload_parse_data(tmp, len);
1846   if (!idp)
1847     goto out;
1848
1849   /* Get the public key payload */
1850   tmp = silc_argument_get_arg_type(cmd->args, 3, &len);
1851   if (!tmp)
1852     goto out;
1853
1854   /* Decode the public key */
1855
1856   SILC_GET16_MSB(pk_len, tmp);
1857   SILC_GET16_MSB(type, tmp + 2);
1858   pk = tmp + 4;
1859
1860   if (type != SILC_SKE_PK_TYPE_SILC)
1861     goto out;
1862
1863   if (!silc_pkcs_public_key_decode(pk, pk_len, &public_key))
1864     goto out;
1865
1866   id_type = silc_id_payload_get_type(idp);
1867   if (id_type == SILC_ID_CLIENT) {
1868     client_id = silc_id_payload_get_id(idp);
1869
1870     if (!silc_idcache_find_by_id_one_ext(conn->client_cache, 
1871                                          (void *)client_id, 
1872                                          NULL, NULL, 
1873                                          silc_hash_client_id_compare, NULL,
1874                                          &id_cache))
1875       goto out;
1876
1877     client_entry = (SilcClientEntry)id_cache->context;
1878
1879     /* Notify application */
1880     COMMAND_REPLY((ARGS, id_type, client_entry, public_key));
1881   } else if (id_type == SILC_ID_SERVER) {
1882     /* XXX we don't have server entries at all */
1883     goto out;
1884   } else {
1885     goto out;
1886   }
1887
1888  out:
1889   SILC_CLIENT_PENDING_DESTRUCTOR(cmd, SILC_COMMAND_GETKEY);
1890   if (idp)
1891     silc_id_payload_free(idp);
1892   if (public_key)
1893     silc_pkcs_public_key_free(public_key);
1894   silc_free(client_id);
1895   silc_client_command_reply_free(cmd);
1896 }