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