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