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