910aab545be02edd180c36a8dbc8e3cca64077fb
[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 - 2007 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 #include "silc.h"
22 #include "silcclient.h"
23 #include "client_internal.h"
24
25 /************************** Types and definitions ***************************/
26
27 /* Calls error command reply callback back to command sender. */
28 #define ERROR_CALLBACK(err)                                     \
29 do {                                                            \
30   void *arg1 = NULL, *arg2 = NULL;                              \
31   if (cmd->status != SILC_STATUS_OK)                            \
32     silc_status_get_args(cmd->status, args, &arg1, &arg2);      \
33   else                                                          \
34     cmd->status = cmd->error = err;                             \
35   SILC_LOG_DEBUG(("Error in command reply: %s",                 \
36                  silc_get_status_message(cmd->status)));        \
37   silc_client_command_callback(cmd, arg1, arg2);                \
38 } while(0)
39
40 /* Check for error */
41 #define CHECK_STATUS(msg)                                               \
42   SILC_LOG_DEBUG(("%s", silc_get_command_name(cmd->cmd)));              \
43   if (cmd->error != SILC_STATUS_OK) {                                   \
44     if (cmd->verbose)                                                   \
45       SAY(cmd->conn->client, cmd->conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR, \
46           msg "%s", silc_get_status_message(cmd->error));               \
47     ERROR_CALLBACK(cmd->error);                                         \
48     silc_client_command_process_error(cmd, state_context, cmd->error);  \
49     silc_fsm_next(fsm, silc_client_command_reply_processed);            \
50     return SILC_FSM_CONTINUE;                                           \
51   }
52
53 /* Check for correct arguments */
54 #define CHECK_ARGS(min, max)                                    \
55   if (silc_argument_get_arg_num(args) < min ||                  \
56       silc_argument_get_arg_num(args) > max) {                  \
57     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);          \
58     silc_fsm_next(fsm, silc_client_command_reply_processed);    \
59     return SILC_FSM_CONTINUE;                                   \
60   }
61
62 #define SAY cmd->conn->client->internal->ops->say
63
64 /************************ Static utility functions **************************/
65
66 /* Delivers the command reply back to application */
67
68 static inline void
69 silc_client_command_callback(SilcClientCommandContext cmd, ...)
70 {
71   SilcClientCommandReplyCallback cb;
72   SilcList list;
73   va_list ap, cp;
74
75   va_start(ap, cmd);
76
77   /* Default reply callback */
78   if (cmd->called) {
79     silc_va_copy(cp, ap);
80     cmd->conn->client->internal->ops->command_reply(
81                        cmd->conn->client, cmd->conn, cmd->cmd, cmd->status,
82                        cmd->error, cp);
83     va_end(cp);
84   }
85
86   /* Reply callback */
87   list = cmd->reply_callbacks;
88   silc_list_start(list);
89   while ((cb = silc_list_get(list)))
90     if (!cb->do_not_call) {
91       silc_va_copy(cp, ap);
92       cb->do_not_call = !cb->reply(cmd->conn->client, cmd->conn, cmd->cmd,
93                                    cmd->status, cmd->error, cb->context, cp);
94       va_end(cp);
95     }
96
97   va_end(ap);
98 }
99
100 /* Handles common error status types. */
101
102 static void silc_client_command_process_error(SilcClientCommandContext cmd,
103                                               SilcCommandPayload payload,
104                                               SilcStatus error)
105 {
106   SilcClient client = cmd->conn->client;
107   SilcClientConnection conn = cmd->conn;
108   SilcArgumentPayload args = silc_command_get_args(payload);
109   SilcID id;
110
111   if (cmd->error == SILC_STATUS_ERR_NO_SUCH_CLIENT_ID) {
112     SilcClientEntry client_entry;
113
114     /* Remove unknown client entry from cache */
115     if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL))
116       return;
117
118     client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
119     if (client_entry) {
120       silc_client_remove_from_channels(client, conn, client_entry);
121       silc_client_del_client(client, conn, client_entry);
122       silc_client_unref_client(client, conn, client_entry);
123     }
124     return;
125   }
126
127   if (cmd->error == SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID) {
128     SilcChannelEntry channel;
129
130     /* Remove unknown channel entry from cache */
131     if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL))
132       return;
133
134     channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
135     if (channel) {
136       silc_client_empty_channel(client, conn, channel);
137       silc_client_del_channel(client, conn, channel);
138       silc_client_unref_channel(client, conn, channel);
139     }
140     return;
141   }
142
143   if (cmd->error == SILC_STATUS_ERR_NO_SUCH_SERVER_ID) {
144     SilcServerEntry server_entry;
145
146     /* Remove unknown server entry from cache */
147     if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL))
148       return;
149
150     server_entry = silc_client_get_server_by_id(client, conn, &id.u.server_id);
151     if (server_entry) {
152       silc_client_del_server(client, conn, server_entry);
153       silc_client_unref_server(client, conn, server_entry);
154     }
155     return;
156   }
157 }
158
159 /***************************** Command Reply ********************************/
160
161 /* Process received command reply packet */
162
163 SILC_FSM_STATE(silc_client_command_reply)
164 {
165   SilcClientConnection conn = fsm_context;
166   SilcPacket packet = state_context;
167   SilcClientCommandContext cmd;
168   SilcCommandPayload payload;
169   SilcCommand command;
170   SilcUInt16 cmd_ident;
171
172   /* Get command reply payload from packet */
173   payload = silc_command_payload_parse(silc_buffer_datalen(&packet->buffer));
174   silc_packet_free(packet);
175   if (!payload) {
176     SILC_LOG_DEBUG(("Bad command reply packet"));
177     return SILC_FSM_FINISH;
178   }
179
180   cmd_ident = silc_command_get_ident(payload);
181   command = silc_command_get(payload);
182
183   /* Find the command pending reply */
184   silc_mutex_lock(conn->internal->lock);
185   silc_list_start(conn->internal->pending_commands);
186   while ((cmd = silc_list_get(conn->internal->pending_commands))) {
187     if ((cmd->cmd == command || cmd->cmd == SILC_COMMAND_NONE)
188         && cmd->cmd_ident == cmd_ident) {
189       silc_list_del(conn->internal->pending_commands, cmd);
190       break;
191     }
192   }
193   silc_mutex_unlock(conn->internal->lock);
194
195   if (!cmd) {
196     SILC_LOG_DEBUG(("Unknown command reply %s, ident %d",
197                     silc_get_command_name(command), cmd_ident));
198     silc_command_payload_free(payload);
199     return SILC_FSM_FINISH;
200   }
201
202   /* Signal command thread that command reply has arrived.  We continue
203      command reply processing synchronously because we save the command
204      payload into state context.  No other reply may arrive to this command
205      while we're processing this reply. */
206   silc_fsm_set_state_context(&cmd->thread, payload);
207   silc_fsm_next(&cmd->thread, silc_client_command_reply_process);
208   silc_fsm_continue_sync(&cmd->thread);
209
210   return SILC_FSM_FINISH;
211 }
212
213 /* Wait here for command reply to arrive from remote host */
214
215 SILC_FSM_STATE(silc_client_command_reply_wait)
216 {
217   SilcClientCommandContext cmd = fsm_context;
218
219   SILC_LOG_DEBUG(("Wait for command reply"));
220
221   /** Wait for command reply */
222   silc_fsm_set_state_context(fsm, NULL);
223   silc_fsm_next_later(fsm, silc_client_command_reply_timeout,
224                       cmd->cmd != SILC_COMMAND_PING ? 40 : 60, 0);
225   return SILC_FSM_WAIT;
226 }
227
228 /* Timeout occurred while waiting command reply */
229
230 SILC_FSM_STATE(silc_client_command_reply_timeout)
231 {
232   SilcClientCommandContext cmd = fsm_context;
233   SilcClientConnection conn = cmd->conn;
234   SilcArgumentPayload args = NULL;
235
236   if (conn->internal->disconnected) {
237     SILC_LOG_DEBUG(("Command %s canceled", silc_get_command_name(cmd->cmd)));
238     silc_list_del(conn->internal->pending_commands, cmd);
239     if (!cmd->called)
240       ERROR_CALLBACK(SILC_STATUS_ERR_TIMEDOUT);
241     return SILC_FSM_FINISH;
242   }
243
244   SILC_LOG_DEBUG(("Command %s timeout", silc_get_command_name(cmd->cmd)));
245
246   /* Timeout, reply not received in timely fashion */
247   silc_list_del(conn->internal->pending_commands, cmd);
248   ERROR_CALLBACK(SILC_STATUS_ERR_TIMEDOUT);
249   return SILC_FSM_FINISH;
250 }
251
252 /* Process received command reply payload */
253
254 SILC_FSM_STATE(silc_client_command_reply_process)
255 {
256   SilcClientCommandContext cmd = fsm_context;
257   SilcCommandPayload payload = state_context;
258
259   silc_command_get_status(payload, &cmd->status, &cmd->error);
260
261   switch (cmd->cmd) {
262   case SILC_COMMAND_WHOIS:
263     /** WHOIS */
264     silc_fsm_next(fsm, silc_client_command_reply_whois);
265     break;
266   case SILC_COMMAND_WHOWAS:
267     /** WHOWAS */
268     silc_fsm_next(fsm, silc_client_command_reply_whowas);
269     break;
270   case SILC_COMMAND_IDENTIFY:
271     /** IDENTIFY */
272     silc_fsm_next(fsm, silc_client_command_reply_identify);
273     break;
274   case SILC_COMMAND_NICK:
275     /** NICK */
276     silc_fsm_next(fsm, silc_client_command_reply_nick);
277     break;
278   case SILC_COMMAND_LIST:
279     /** LIST */
280     silc_fsm_next(fsm, silc_client_command_reply_list);
281     break;
282   case SILC_COMMAND_TOPIC:
283     /** TOPIC */
284     silc_fsm_next(fsm, silc_client_command_reply_topic);
285     break;
286   case SILC_COMMAND_INVITE:
287     /** INVITE */
288     silc_fsm_next(fsm, silc_client_command_reply_invite);
289     break;
290   case SILC_COMMAND_QUIT:
291     /** QUIT */
292     silc_fsm_next(fsm, silc_client_command_reply_quit);
293     break;
294   case SILC_COMMAND_KILL:
295     /** KILL */
296     silc_fsm_next(fsm, silc_client_command_reply_kill);
297     break;
298   case SILC_COMMAND_INFO:
299     /** INFO */
300     silc_fsm_next(fsm, silc_client_command_reply_info);
301     break;
302   case SILC_COMMAND_STATS:
303     /** STATS */
304     silc_fsm_next(fsm, silc_client_command_reply_stats);
305     break;
306   case SILC_COMMAND_PING:
307     /** PING */
308     silc_fsm_next(fsm, silc_client_command_reply_ping);
309     break;
310   case SILC_COMMAND_OPER:
311     /** OPER */
312     silc_fsm_next(fsm, silc_client_command_reply_oper);
313     break;
314   case SILC_COMMAND_JOIN:
315     /** JOIN */
316     silc_fsm_next(fsm, silc_client_command_reply_join);
317     break;
318   case SILC_COMMAND_MOTD:
319     /** MOTD */
320     silc_fsm_next(fsm, silc_client_command_reply_motd);
321     break;
322   case SILC_COMMAND_UMODE:
323     /** UMODE */
324     silc_fsm_next(fsm, silc_client_command_reply_umode);
325     break;
326   case SILC_COMMAND_CMODE:
327     /** CMODE */
328     silc_fsm_next(fsm, silc_client_command_reply_cmode);
329     break;
330   case SILC_COMMAND_CUMODE:
331     /** CUMODE */
332     silc_fsm_next(fsm, silc_client_command_reply_cumode);
333     break;
334   case SILC_COMMAND_KICK:
335     /** KICK */
336     silc_fsm_next(fsm, silc_client_command_reply_kick);
337     break;
338   case SILC_COMMAND_BAN:
339     /** BAN */
340     silc_fsm_next(fsm, silc_client_command_reply_ban);
341     break;
342   case SILC_COMMAND_DETACH:
343     /** DETACH */
344     silc_fsm_next(fsm, silc_client_command_reply_detach);
345     break;
346   case SILC_COMMAND_WATCH:
347     /** WATCH */
348     silc_fsm_next(fsm, silc_client_command_reply_watch);
349     break;
350   case SILC_COMMAND_SILCOPER:
351     /** SILCOPER */
352     silc_fsm_next(fsm, silc_client_command_reply_silcoper);
353     break;
354   case SILC_COMMAND_LEAVE:
355     /** LEAVE */
356     silc_fsm_next(fsm, silc_client_command_reply_leave);
357     break;
358   case SILC_COMMAND_USERS:
359     /** USERS */
360     silc_fsm_next(fsm, silc_client_command_reply_users);
361     break;
362   case SILC_COMMAND_GETKEY:
363     /** GETKEY */
364     silc_fsm_next(fsm, silc_client_command_reply_getkey);
365     break;
366   case SILC_COMMAND_SERVICE:
367     /** SERVICE */
368     silc_fsm_next(fsm, silc_client_command_reply_service);
369     break;
370   default:
371     return SILC_FSM_FINISH;
372   }
373
374   return SILC_FSM_CONTINUE;
375 }
376
377 /* Completes command reply processing */
378
379 SILC_FSM_STATE(silc_client_command_reply_processed)
380 {
381   SilcClientCommandContext cmd = fsm_context;
382   SilcClientConnection conn = cmd->conn;
383   SilcCommandPayload payload = state_context;
384
385   silc_command_payload_free(payload);
386
387   if (cmd->status == SILC_STATUS_OK || cmd->status == SILC_STATUS_LIST_END ||
388       SILC_STATUS_IS_ERROR(cmd->status))
389     return SILC_FSM_FINISH;
390
391   /* Add back to pending command reply list */
392   silc_mutex_lock(conn->internal->lock);
393   cmd->resolved = FALSE;
394   silc_list_add(conn->internal->pending_commands, cmd);
395   silc_mutex_unlock(conn->internal->lock);
396
397   /** Wait more command payloads */
398   silc_fsm_next(fsm, silc_client_command_reply_wait);
399   return SILC_FSM_CONTINUE;
400 }
401
402 /******************************** WHOIS *************************************/
403
404 /* Received reply for WHOIS command. */
405
406 SILC_FSM_STATE(silc_client_command_reply_whois)
407 {
408   SilcClientCommandContext cmd = fsm_context;
409   SilcClientConnection conn = cmd->conn;
410   SilcClient client = conn->client;
411   SilcCommandPayload payload = state_context;
412   SilcArgumentPayload args = silc_command_get_args(payload);
413   SilcClientEntry client_entry = NULL;
414   SilcUInt32 idle = 0, mode = 0, fingerprint_len, len, *umodes = NULL;
415   SilcBufferStruct channels, ch_user_modes;
416   SilcBool has_channels = FALSE;
417   SilcDList channel_list = NULL;
418   SilcID id;
419   char *nickname = NULL, *username = NULL, *realname = NULL;
420   unsigned char *fingerprint, *tmp;
421
422   CHECK_STATUS("WHOIS: ");
423   CHECK_ARGS(5, 11);
424
425   /* Get Client ID */
426   if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL)) {
427     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
428     goto out;
429   }
430
431   /* Get names */
432   nickname = silc_argument_get_arg_type(args, 3, NULL);
433   username = silc_argument_get_arg_type(args, 4, NULL);
434   realname = silc_argument_get_arg_type(args, 5, NULL);
435   if (!nickname || !username || !realname) {
436     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
437     goto out;
438   }
439
440   /* Get joined channel list */
441   memset(&channels, 0, sizeof(channels));
442   tmp = silc_argument_get_arg_type(args, 6, &len);
443   if (tmp) {
444     has_channels = TRUE;
445     silc_buffer_set(&channels, tmp, len);
446
447     /* Get channel user mode list */
448     tmp = silc_argument_get_arg_type(args, 10, &len);
449     if (!tmp) {
450       ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
451       goto out;
452     }
453     silc_buffer_set(&ch_user_modes, tmp, len);
454   }
455
456   /* Get user mode */
457   tmp = silc_argument_get_arg_type(args, 7, &len);
458   if (tmp)
459     SILC_GET32_MSB(mode, tmp);
460
461   /* Get idle time */
462   tmp = silc_argument_get_arg_type(args, 8, &len);
463   if (tmp)
464     SILC_GET32_MSB(idle, tmp);
465
466   /* Get fingerprint */
467   fingerprint = silc_argument_get_arg_type(args, 9, &fingerprint_len);
468
469   /* Check if we have this client cached already. */
470   client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
471   if (!client_entry) {
472     SILC_LOG_DEBUG(("Adding new client entry (WHOIS)"));
473     client_entry =
474       silc_client_add_client(client, conn, nickname, username, realname,
475                              &id.u.client_id, mode);
476     if (!client_entry) {
477       ERROR_CALLBACK(SILC_STATUS_ERR_RESOURCE_LIMIT);
478       goto out;
479     }
480     silc_client_ref_client(client, conn, client_entry);
481   } else {
482     silc_client_update_client(client, conn, client_entry,
483                               nickname, username, realname, mode);
484   }
485
486   silc_rwlock_wrlock(client_entry->internal.lock);
487
488   if (fingerprint && fingerprint_len == sizeof(client_entry->fingerprint))
489     memcpy(client_entry->fingerprint, fingerprint, fingerprint_len);
490
491   /* Get user attributes */
492   tmp = silc_argument_get_arg_type(args, 11, &len);
493   if (tmp) {
494     if (client_entry->attrs)
495       silc_attribute_payload_list_free(client_entry->attrs);
496     client_entry->attrs = silc_attribute_payload_parse(tmp, len);
497   }
498
499   silc_rwlock_unlock(client_entry->internal.lock);
500
501   /* Parse channel and channel user mode list */
502   if (has_channels) {
503     channel_list = silc_channel_payload_parse_list(silc_buffer_data(&channels),
504                                                    silc_buffer_len(&channels));
505     if (channel_list)
506       silc_get_mode_list(&ch_user_modes, silc_dlist_count(channel_list),
507                          &umodes);
508   }
509
510   /* Notify application */
511   silc_client_command_callback(cmd, client_entry, nickname, username,
512                                realname, channel_list, mode, idle, fingerprint,
513                                umodes, client_entry->attrs);
514
515   silc_client_unref_client(client, conn, client_entry);
516   if (has_channels) {
517     silc_channel_payload_list_free(channel_list);
518     silc_free(umodes);
519   }
520
521  out:
522   silc_fsm_next(fsm, silc_client_command_reply_processed);
523   return SILC_FSM_CONTINUE;
524 }
525
526 /******************************** WHOWAS ************************************/
527
528 /* Received reply for WHOWAS command. */
529
530 SILC_FSM_STATE(silc_client_command_reply_whowas)
531 {
532   SilcClientCommandContext cmd = fsm_context;
533   SilcClientConnection conn = cmd->conn;
534   SilcClient client = conn->client;
535   SilcCommandPayload payload = state_context;
536   SilcArgumentPayload args = silc_command_get_args(payload);
537   SilcClientEntry client_entry = NULL;
538   SilcID id;
539   char *nickname, *username;
540   char *realname = NULL;
541
542   CHECK_STATUS("WHOWAS: ");
543   CHECK_ARGS(4, 5);
544
545   /* Get Client ID */
546   if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL)) {
547     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
548     goto out;
549   }
550
551   /* Get the client entry */
552   client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
553
554   /* Get names */
555   nickname = silc_argument_get_arg_type(args, 3, NULL);
556   username = silc_argument_get_arg_type(args, 4, NULL);
557   realname = silc_argument_get_arg_type(args, 5, NULL);
558   if (!nickname || !username) {
559     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
560     goto out;
561   }
562
563   /* Notify application. We don't save any history information to any
564      cache. Just pass the data to the application. */
565   silc_client_command_callback(cmd, client_entry, nickname, username,
566                                realname);
567
568  out:
569   silc_client_unref_client(client, conn, client_entry);
570   silc_fsm_next(fsm, silc_client_command_reply_processed);
571   return SILC_FSM_CONTINUE;
572 }
573
574 /******************************** IDENTIFY **********************************/
575
576 /* Received reply for IDENTIFY command. */
577
578 SILC_FSM_STATE(silc_client_command_reply_identify)
579 {
580   SilcClientCommandContext cmd = fsm_context;
581   SilcClientConnection conn = cmd->conn;
582   SilcClient client = conn->client;
583   SilcCommandPayload payload = state_context;
584   SilcArgumentPayload args = silc_command_get_args(payload);
585   SilcClientEntry client_entry;
586   SilcServerEntry server_entry;
587   SilcChannelEntry channel_entry;
588   SilcUInt32 len;
589   SilcID id;
590   char *name = NULL, *info = NULL;
591
592   CHECK_STATUS("IDENTIFY: ");
593   CHECK_ARGS(2, 4);
594
595   /* Get the ID */
596   if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL)) {
597     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
598     goto out;
599   }
600
601   /* Get names */
602   name = silc_argument_get_arg_type(args, 3, &len);
603   info = silc_argument_get_arg_type(args, 4, &len);
604
605   switch (id.type) {
606   case SILC_ID_CLIENT:
607     SILC_LOG_DEBUG(("Received client information"));
608
609     /* Check if we have this client cached already. */
610     client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
611     if (!client_entry) {
612       SILC_LOG_DEBUG(("Adding new client entry (IDENTIFY)"));
613       client_entry =
614         silc_client_add_client(client, conn, name, info, NULL,
615                                &id.u.client_id, 0);
616       if (!client_entry) {
617         ERROR_CALLBACK(SILC_STATUS_ERR_RESOURCE_LIMIT);
618         goto out;
619       }
620       silc_client_ref_client(client, conn, client_entry);
621     } else {
622       silc_client_update_client(client, conn, client_entry,
623                                 name, info, NULL, 0);
624     }
625
626     /* Notify application */
627     silc_client_command_callback(cmd, client_entry, name, info);
628     silc_client_unref_client(client, conn, client_entry);
629     break;
630
631   case SILC_ID_SERVER:
632     SILC_LOG_DEBUG(("Received server information"));
633
634     /* Check if we have this server cached already. */
635     server_entry = silc_client_get_server_by_id(client, conn, &id.u.server_id);
636     if (!server_entry) {
637       SILC_LOG_DEBUG(("Adding new server entry (IDENTIFY)"));
638       server_entry = silc_client_add_server(client, conn, name, info,
639                                             &id.u.server_id);
640       if (!server_entry) {
641         ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
642         goto out;
643       }
644       silc_client_ref_server(client, conn, server_entry);
645     } else {
646       silc_client_update_server(client, conn, server_entry, name, info);
647     }
648     server_entry->internal.resolve_cmd_ident = 0;
649
650     /* Notify application */
651     silc_client_command_callback(cmd, server_entry, name, info);
652     silc_client_unref_server(client, conn, server_entry);
653     break;
654
655   case SILC_ID_CHANNEL:
656     SILC_LOG_DEBUG(("Received channel information"));
657
658     /* Check if we have this channel cached already. */
659     channel_entry = silc_client_get_channel_by_id(client, conn,
660                                                   &id.u.channel_id);
661     if (!channel_entry) {
662       SILC_LOG_DEBUG(("Adding new channel entry (IDENTIFY)"));
663
664       if (!name) {
665         ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
666         goto out;
667       }
668
669       /* Add new channel entry */
670       channel_entry = silc_client_add_channel(client, conn, name, 0,
671                                               &id.u.channel_id);
672       if (!channel_entry) {
673         ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
674         goto out;
675       }
676       silc_client_ref_channel(client, conn, channel_entry);
677     }
678
679     /* Notify application */
680     silc_client_command_callback(cmd, channel_entry,
681                                  channel_entry->channel_name, info);
682     silc_client_unref_channel(client, conn, channel_entry);
683     break;
684   }
685
686  out:
687   silc_fsm_next(fsm, silc_client_command_reply_processed);
688   return SILC_FSM_CONTINUE;
689 }
690
691 /********************************** NICK ************************************/
692
693 /* Received reply for command NICK. */
694
695 SILC_FSM_STATE(silc_client_command_reply_nick)
696 {
697   SilcClientCommandContext cmd = fsm_context;
698   SilcClientConnection conn = cmd->conn;
699   SilcClient client = conn->client;
700   SilcCommandPayload payload = state_context;
701   SilcArgumentPayload args = silc_command_get_args(payload);
702   unsigned char *nick, *idp;
703   SilcUInt32 len, idp_len;
704   SilcClientID old_client_id;
705   SilcID id;
706
707   /* Sanity checks */
708   CHECK_STATUS("Cannot set nickname: ");
709   CHECK_ARGS(2, 3);
710
711   /* Take received Client ID */
712   idp = silc_argument_get_arg_type(args, 2, &idp_len);
713   if (!idp) {
714     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
715     goto out;
716   }
717   if (!silc_id_payload_parse_id(idp, idp_len, &id)) {
718     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
719     goto out;
720   }
721
722   /* Take the new nickname */
723   nick = silc_argument_get_arg_type(args, 3, &len);
724   if (!nick) {
725     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
726     goto out;
727   }
728
729   silc_rwlock_wrlock(conn->local_entry->internal.lock);
730
731   /* Change the nickname */
732   old_client_id = *conn->local_id;
733   if (!silc_client_change_nickname(client, conn, conn->local_entry,
734                                    nick, &id.u.client_id, idp, idp_len)) {
735     ERROR_CALLBACK(SILC_STATUS_ERR_BAD_NICKNAME);
736     silc_rwlock_unlock(conn->local_entry->internal.lock);
737     goto out;
738   }
739
740   silc_rwlock_unlock(conn->local_entry->internal.lock);
741
742   /* Notify application */
743   silc_client_command_callback(cmd, conn->local_entry,
744                                conn->local_entry->nickname, &old_client_id);
745
746  out:
747   silc_fsm_next(fsm, silc_client_command_reply_processed);
748   return SILC_FSM_CONTINUE;
749 }
750
751 /********************************** LIST ************************************/
752
753 /* Received reply to the LIST command. */
754
755 SILC_FSM_STATE(silc_client_command_reply_list)
756 {
757   SilcClientCommandContext cmd = fsm_context;
758   SilcClientConnection conn = cmd->conn;
759   SilcClient client = conn->client;
760   SilcCommandPayload payload = state_context;
761   SilcArgumentPayload args = silc_command_get_args(payload);
762   unsigned char *tmp, *name, *topic;
763   SilcUInt32 usercount = 0;
764   SilcChannelEntry channel_entry = NULL;
765   SilcID id;
766
767   /* Sanity checks */
768   CHECK_STATUS("Cannot list channels: ");
769
770   if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL)) {
771     /* There were no channels in the network. */
772     silc_client_command_callback(cmd, NULL, NULL, NULL, 0);
773     silc_fsm_next(fsm, silc_client_command_reply_processed);
774     return SILC_FSM_CONTINUE;
775   }
776
777   CHECK_ARGS(3, 5);
778
779   name = silc_argument_get_arg_type(args, 3, NULL);
780   if (!name) {
781     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
782     goto out;
783   }
784
785   topic = silc_argument_get_arg_type(args, 4, NULL);
786   tmp = silc_argument_get_arg_type(args, 5, NULL);
787   if (tmp)
788     SILC_GET32_MSB(usercount, tmp);
789
790   /* Check whether the channel exists, and add it to cache if it doesn't. */
791   channel_entry = silc_client_get_channel_by_id(client, conn,
792                                                 &id.u.channel_id);
793   if (!channel_entry) {
794     /* Add new channel entry */
795     channel_entry = silc_client_add_channel(client, conn, name, 0,
796                                             &id.u.channel_id);
797     if (!channel_entry) {
798       ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
799       goto out;
800     }
801     silc_client_ref_channel(client, conn, channel_entry);
802   }
803
804   /* Notify application */
805   silc_client_command_callback(cmd, channel_entry, channel_entry->channel_name,
806                                topic, usercount);
807
808  out:
809   silc_client_unref_channel(client, conn, channel_entry);
810   silc_fsm_next(fsm, silc_client_command_reply_processed);
811   return SILC_FSM_CONTINUE;
812 }
813
814 /********************************* TOPIC ************************************/
815
816 /* Received reply to topic command. */
817
818 SILC_FSM_STATE(silc_client_command_reply_topic)
819 {
820   SilcClientCommandContext cmd = fsm_context;
821   SilcClientConnection conn = cmd->conn;
822   SilcClient client = conn->client;
823   SilcCommandPayload payload = state_context;
824   SilcArgumentPayload args = silc_command_get_args(payload);
825   SilcChannelEntry channel = NULL;
826   char *topic;
827   SilcUInt32 len;
828   SilcID id;
829
830   /* Sanity checks */
831   CHECK_STATUS("Cannot set topic: ");
832   CHECK_ARGS(2, 3);
833
834   /* Take Channel ID */
835   if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL)) {
836     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
837     goto out;
838   }
839
840   /* Get the channel entry */
841   channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
842   if (!channel) {
843     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
844     goto out;
845   }
846
847   silc_rwlock_wrlock(channel->internal.lock);
848
849   /* Take topic */
850   topic = silc_argument_get_arg_type(args, 3, &len);
851   if (topic) {
852     silc_free(channel->topic);
853     channel->topic = silc_memdup(topic, len);
854   }
855
856   silc_rwlock_unlock(channel->internal.lock);
857
858   /* Notify application */
859   silc_client_command_callback(cmd, channel, channel->topic);
860
861  out:
862   silc_client_unref_channel(client, conn, channel);
863   silc_fsm_next(fsm, silc_client_command_reply_processed);
864   return SILC_FSM_CONTINUE;
865 }
866
867 /********************************* INVITE ***********************************/
868
869 /* Received reply to invite command. */
870
871 SILC_FSM_STATE(silc_client_command_reply_invite)
872 {
873   SilcClientCommandContext cmd = fsm_context;
874   SilcClientConnection conn = cmd->conn;
875   SilcClient client = conn->client;
876   SilcCommandPayload payload = state_context;
877   SilcArgumentPayload args = silc_command_get_args(payload);
878   SilcChannelEntry channel = NULL;
879   unsigned char *tmp;
880   SilcUInt32 len;
881   SilcArgumentPayload invite_args = NULL;
882   SilcID id;
883
884   /* Sanity checks */
885   CHECK_STATUS("Cannot invite: ");
886   CHECK_ARGS(2, 3);
887
888   /* Take Channel ID */
889   if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL)) {
890     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
891     goto out;
892   }
893
894   /* Get the channel entry */
895   channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
896   if (!channel) {
897     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
898     goto out;
899   }
900
901   /* Get the invite list */
902   tmp = silc_argument_get_arg_type(args, 3, &len);
903   if (tmp)
904     invite_args = silc_argument_list_parse(tmp, len);
905
906   /* Notify application */
907   silc_client_command_callback(cmd, channel, invite_args);
908
909   if (invite_args)
910     silc_argument_payload_free(invite_args);
911
912  out:
913   silc_client_unref_channel(client, conn, channel);
914   silc_fsm_next(fsm, silc_client_command_reply_processed);
915   return SILC_FSM_CONTINUE;
916 }
917
918 /********************************** KILL ************************************/
919
920 /* Received reply to the KILL command. */
921
922 SILC_FSM_STATE(silc_client_command_reply_kill)
923 {
924   SilcClientCommandContext cmd = fsm_context;
925   SilcClientConnection conn = cmd->conn;
926   SilcClient client = conn->client;
927   SilcCommandPayload payload = state_context;
928   SilcArgumentPayload args = silc_command_get_args(payload);
929   SilcClientEntry client_entry;
930   SilcID id;
931
932   /* Sanity checks */
933   CHECK_STATUS("Cannot kill: ");
934   CHECK_ARGS(2, 2);
935
936   if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL)) {
937     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
938     goto out;
939   }
940
941   /* Get the client entry, if exists */
942   client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
943
944   /* Notify application */
945   silc_client_command_callback(cmd, client_entry);
946
947   /* Remove the client */
948   if (client_entry) {
949     silc_client_remove_from_channels(client, conn, client_entry);
950     silc_client_del_client(client, conn, client_entry);
951     silc_client_unref_client(client, conn, client_entry);
952   }
953
954  out:
955   silc_fsm_next(fsm, silc_client_command_reply_processed);
956   return SILC_FSM_CONTINUE;
957 }
958
959 /********************************** INFO ************************************/
960
961 /* Received reply to INFO command. We receive the server ID and some
962    information about the server user requested. */
963
964 SILC_FSM_STATE(silc_client_command_reply_info)
965 {
966   SilcClientCommandContext cmd = fsm_context;
967   SilcClientConnection conn = cmd->conn;
968   SilcClient client = conn->client;
969   SilcCommandPayload payload = state_context;
970   SilcArgumentPayload args = silc_command_get_args(payload);
971   SilcServerEntry server;
972   char *server_name, *server_info;
973   SilcID id;
974
975   /* Sanity checks */
976   CHECK_STATUS("Cannot get info: ");
977   CHECK_ARGS(4, 4);
978
979   /* Get server ID */
980   if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL)) {
981     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
982     goto out;
983   }
984
985   /* Get server name */
986   server_name = silc_argument_get_arg_type(args, 3, NULL);
987   if (!server_name) {
988     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
989     goto out;
990   }
991
992   /* Get server info */
993   server_info = silc_argument_get_arg_type(args, 4, NULL);
994   if (!server_info) {
995     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
996     goto out;
997   }
998
999   /* See whether we have this server cached. If not create it. */
1000   server = silc_client_get_server_by_id(client, conn, &id.u.server_id);
1001   if (!server) {
1002     SILC_LOG_DEBUG(("Add new server entry (INFO)"));
1003     server = silc_client_add_server(client, conn, server_name,
1004                                     server_info, &id.u.server_id);
1005     if (!server)
1006       goto out;
1007     silc_client_ref_server(client, conn, server);
1008   }
1009
1010   /* Notify application */
1011   silc_client_command_callback(cmd, server, server->server_name,
1012                                server->server_info);
1013   silc_client_unref_server(client, conn, server);
1014
1015  out:
1016   silc_fsm_next(fsm, silc_client_command_reply_processed);
1017   return SILC_FSM_CONTINUE;
1018 }
1019
1020 /********************************** STATS ***********************************/
1021
1022 /* Received reply to STATS command. */
1023
1024 SILC_FSM_STATE(silc_client_command_reply_stats)
1025 {
1026   SilcClientCommandContext cmd = fsm_context;
1027   SilcCommandPayload payload = state_context;
1028   SilcArgumentPayload args = silc_command_get_args(payload);
1029   SilcClientStats stats;
1030   unsigned char *buf = NULL;
1031   SilcUInt32 buf_len = 0;
1032   SilcBufferStruct b;
1033   SilcID id;
1034
1035   /* Sanity checks */
1036   CHECK_STATUS("Cannot get stats: ");
1037   CHECK_ARGS(2, 3);
1038
1039   /* Get server ID */
1040   if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL)) {
1041     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1042     goto out;
1043   }
1044
1045   /* Get statistics structure */
1046   memset(&stats, 0, sizeof(stats));
1047   buf = silc_argument_get_arg_type(args, 3, &buf_len);
1048   if (buf) {
1049     silc_buffer_set(&b, buf, buf_len);
1050     silc_buffer_unformat(&b,
1051                          SILC_STR_UI_INT(&stats.starttime),
1052                          SILC_STR_UI_INT(&stats.uptime),
1053                          SILC_STR_UI_INT(&stats.my_clients),
1054                          SILC_STR_UI_INT(&stats.my_channels),
1055                          SILC_STR_UI_INT(&stats.my_server_ops),
1056                          SILC_STR_UI_INT(&stats.my_router_ops),
1057                          SILC_STR_UI_INT(&stats.cell_clients),
1058                          SILC_STR_UI_INT(&stats.cell_channels),
1059                          SILC_STR_UI_INT(&stats.cell_servers),
1060                          SILC_STR_UI_INT(&stats.clients),
1061                          SILC_STR_UI_INT(&stats.channels),
1062                          SILC_STR_UI_INT(&stats.servers),
1063                          SILC_STR_UI_INT(&stats.routers),
1064                          SILC_STR_UI_INT(&stats.server_ops),
1065                          SILC_STR_UI_INT(&stats.router_ops),
1066                          SILC_STR_END);
1067   }
1068
1069   /* Notify application */
1070   silc_client_command_callback(cmd, &stats);
1071
1072  out:
1073   silc_fsm_next(fsm, silc_client_command_reply_processed);
1074   return SILC_FSM_CONTINUE;
1075 }
1076
1077 /********************************** PING ************************************/
1078
1079 /* Received reply to PING command. */
1080
1081 SILC_FSM_STATE(silc_client_command_reply_ping)
1082 {
1083   SilcClientCommandContext cmd = fsm_context;
1084   SilcClientConnection conn = cmd->conn;
1085   SilcClient client = conn->client;
1086   SilcInt64 diff;
1087
1088   diff = silc_time() - SILC_PTR_TO_64(cmd->context);
1089   if (cmd->verbose)
1090     SAY(client, conn, SILC_CLIENT_MESSAGE_INFO,
1091         "Ping reply from %s: %d second%s", conn->remote_host,
1092         (int)diff, diff == 1 ? "" : "s");
1093
1094   /* Notify application */
1095   silc_client_command_callback(cmd);
1096
1097   silc_fsm_next(fsm, silc_client_command_reply_processed);
1098   return SILC_FSM_CONTINUE;
1099 }
1100
1101 /********************************** JOIN ************************************/
1102
1103 /* Continue JOIN command reply processing after resolving unknown users */
1104
1105 static void
1106 silc_client_command_reply_join_resolved(SilcClient client,
1107                                         SilcClientConnection conn,
1108                                         SilcStatus status,
1109                                         SilcDList clients,
1110                                         void *context)
1111 {
1112   SilcClientCommandContext cmd = context;
1113   SilcChannelEntry channel = cmd->context;
1114
1115   channel->internal.resolve_cmd_ident = 0;
1116   silc_client_unref_channel(client, conn, channel);
1117
1118   SILC_FSM_CALL_CONTINUE_SYNC(&cmd->thread);
1119 }
1120
1121
1122 /* Received reply for JOIN command. */
1123
1124 SILC_FSM_STATE(silc_client_command_reply_join)
1125 {
1126   SilcClientCommandContext cmd = fsm_context;
1127   SilcClientConnection conn = cmd->conn;
1128   SilcClient client = conn->client;
1129   SilcCommandPayload payload = state_context;
1130   SilcArgumentPayload args = silc_command_get_args(payload);
1131   SilcChannelEntry channel;
1132   SilcUInt32 mode = 0, len, list_count;
1133   char *topic, *tmp, *channel_name = NULL, *hmac;
1134   const char *cipher;
1135   SilcBufferStruct client_id_list, client_mode_list, keyp;
1136   SilcHashTableList htl;
1137   SilcID id;
1138   int i;
1139
1140   /* Sanity checks */
1141   CHECK_STATUS("Cannot join channel: ");
1142   CHECK_ARGS(9, 17);
1143
1144   /* Get channel name */
1145   channel_name = silc_argument_get_arg_type(args, 2, NULL);
1146   if (!channel_name) {
1147     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1148     goto out;
1149   }
1150
1151   /* Get Channel ID */
1152   if (!silc_argument_get_decoded(args, 3, SILC_ARGUMENT_ID, &id, NULL)) {
1153     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1154     goto out;
1155   }
1156
1157   /* Check whether we have this channel entry already. */
1158   channel = silc_client_get_channel(client, conn, channel_name);
1159   if (channel) {
1160     if (!SILC_ID_CHANNEL_COMPARE(&channel->id, &id.u.channel_id))
1161       silc_client_replace_channel_id(client, conn, channel, &id.u.channel_id);
1162   } else {
1163     /* Create new channel entry */
1164     channel = silc_client_add_channel(client, conn, channel_name,
1165                                       mode, &id.u.channel_id);
1166     if (!channel) {
1167       ERROR_CALLBACK(SILC_STATUS_ERR_BAD_CHANNEL);
1168       goto out;
1169     }
1170     silc_client_ref_channel(client, conn, channel);
1171   }
1172
1173   /* Get the list count */
1174   tmp = silc_argument_get_arg_type(args, 12, &len);
1175   if (!tmp) {
1176     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1177     goto out;
1178   }
1179   SILC_GET32_MSB(list_count, tmp);
1180
1181   /* Get Client ID list */
1182   tmp = silc_argument_get_arg_type(args, 13, &len);
1183   if (!tmp) {
1184     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1185     goto out;
1186   }
1187   silc_buffer_set(&client_id_list, tmp, len);
1188
1189   /* Resolve users we do not know about */
1190   if (!cmd->resolved) {
1191     cmd->resolved = TRUE;
1192     cmd->context = channel;
1193     SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
1194                   silc_client_get_clients_by_list(
1195                           client, conn, list_count, &client_id_list,
1196                           silc_client_command_reply_join_resolved, cmd));
1197     /* NOT REACHED */
1198   }
1199
1200   /* Get client mode list */
1201   tmp = silc_argument_get_arg_type(args, 14, &len);
1202   if (!tmp) {
1203     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1204     goto out;
1205   }
1206   silc_buffer_set(&client_mode_list, tmp, len);
1207
1208   silc_rwlock_wrlock(channel->internal.lock);
1209
1210   /* Add clients we received in the reply to the channel */
1211   for (i = 0; i < list_count; i++) {
1212     SilcUInt16 idp_len;
1213     SilcUInt32 mode;
1214     SilcID id;
1215     SilcClientEntry client_entry;
1216
1217     /* Client ID */
1218     SILC_GET16_MSB(idp_len, client_id_list.data + 2);
1219     idp_len += 4;
1220     if (!silc_id_payload_parse_id(client_id_list.data, idp_len, &id))
1221       continue;
1222
1223     /* Mode */
1224     SILC_GET32_MSB(mode, client_mode_list.data);
1225
1226     /* Get client entry */
1227     client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
1228     if (client_entry && client_entry->internal.valid) {
1229       /* Join client to the channel */
1230       silc_rwlock_wrlock(client_entry->internal.lock);
1231       silc_client_add_to_channel(client, conn, channel, client_entry, mode);
1232       silc_rwlock_unlock(client_entry->internal.lock);
1233     }
1234     silc_client_unref_client(client, conn, client_entry);
1235
1236     if (!silc_buffer_pull(&client_id_list, idp_len)) {
1237       silc_rwlock_unlock(channel->internal.lock);
1238       goto out;
1239     }
1240     if (!silc_buffer_pull(&client_mode_list, 4)) {
1241       silc_rwlock_unlock(channel->internal.lock);
1242       goto out;
1243     }
1244   }
1245
1246   /* Get hmac */
1247   hmac = silc_argument_get_arg_type(args, 11, NULL);
1248   if (hmac) {
1249     if (!silc_hmac_alloc(hmac, NULL, &channel->internal.hmac)) {
1250       if (cmd->verbose)
1251         SAY(client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
1252             "Cannot join channel: Unsupported HMAC `%s'", hmac);
1253       ERROR_CALLBACK(SILC_STATUS_ERR_UNKNOWN_ALGORITHM);
1254       silc_rwlock_unlock(channel->internal.lock);
1255       goto out;
1256     }
1257   }
1258
1259   /* Get channel mode */
1260   tmp = silc_argument_get_arg_type(args, 5, &len);
1261   if (tmp && len == 4)
1262     SILC_GET32_MSB(mode, tmp);
1263   channel->mode = mode;
1264
1265   /* Get channel key and save it */
1266   tmp = silc_argument_get_arg_type(args, 7, &len);
1267   if (tmp) {
1268     /* If channel key already exists on the channel then while resolving
1269        the user list we have already received new key from server.  Don't
1270        replace it with this old key. */
1271     if (!channel->internal.send_key) {
1272       silc_buffer_set(&keyp, tmp, len);
1273       silc_client_save_channel_key(client, conn, &keyp, channel);
1274     }
1275   }
1276
1277   /* Get topic */
1278   topic = silc_argument_get_arg_type(args, 10, NULL);
1279   if (topic) {
1280     silc_free(channel->topic);
1281     channel->topic = silc_memdup(topic, strlen(topic));
1282   }
1283
1284   /* Get founder key */
1285   tmp = silc_argument_get_arg_type(args, 15, &len);
1286   if (tmp) {
1287     if (channel->founder_key)
1288       silc_pkcs_public_key_free(channel->founder_key);
1289     channel->founder_key = NULL;
1290     silc_public_key_payload_decode(tmp, len, &channel->founder_key);
1291   }
1292
1293   /* Get user limit */
1294   tmp = silc_argument_get_arg_type(args, 17, &len);
1295   if (tmp && len == 4)
1296     SILC_GET32_MSB(channel->user_limit, tmp);
1297   if (!(channel->mode & SILC_CHANNEL_MODE_ULIMIT))
1298     channel->user_limit = 0;
1299
1300   /* Get channel public key list */
1301   tmp = silc_argument_get_arg_type(args, 16, &len);
1302   if (tmp)
1303     silc_client_channel_save_public_keys(channel, tmp, len, FALSE);
1304
1305   /* Set current channel */
1306   conn->current_channel = channel;
1307
1308   silc_rwlock_unlock(channel->internal.lock);
1309
1310   cipher = (channel->internal.send_key ?
1311             silc_cipher_get_name(channel->internal.send_key) : NULL);
1312   silc_hash_table_list(channel->user_list, &htl);
1313
1314   /* Notify application */
1315   silc_client_command_callback(cmd, channel->channel_name, channel, mode, &htl,
1316                                topic, cipher, hmac, channel->founder_key,
1317                                channel->channel_pubkeys, channel->user_limit);
1318
1319   silc_hash_table_list_reset(&htl);
1320   silc_client_unref_channel(client, conn, channel);
1321
1322  out:
1323   silc_fsm_next(fsm, silc_client_command_reply_processed);
1324   return SILC_FSM_CONTINUE;
1325 }
1326
1327 /********************************** MOTD ************************************/
1328
1329 /* Received reply for MOTD command */
1330
1331 SILC_FSM_STATE(silc_client_command_reply_motd)
1332 {
1333   SilcClientCommandContext cmd = fsm_context;
1334   SilcClientConnection conn = cmd->conn;
1335   SilcClient client = conn->client;
1336   SilcCommandPayload payload = state_context;
1337   SilcArgumentPayload args = silc_command_get_args(payload);
1338   SilcUInt32 i;
1339   char *motd = NULL, *cp, line[256];
1340
1341   /* Sanity checks */
1342   CHECK_STATUS("Cannot get motd: ");
1343   CHECK_ARGS(2, 3);
1344
1345   if (silc_argument_get_arg_num(args) == 3) {
1346     motd = silc_argument_get_arg_type(args, 3, NULL);
1347     if (!motd) {
1348       ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1349       goto out;
1350     }
1351
1352     i = 0;
1353     cp = motd;
1354     while(cp[i] != 0) {
1355       if (cp[i++] == '\n') {
1356         memset(line, 0, sizeof(line));
1357         silc_strncat(line, sizeof(line), cp, i - 1);
1358         cp += i;
1359
1360         if (i == 2)
1361           line[0] = ' ';
1362
1363         if (cmd->verbose)
1364           SAY(client, conn, SILC_CLIENT_MESSAGE_INFO, "%s", line);
1365
1366         if (!strlen(cp))
1367           break;
1368         i = 0;
1369       }
1370     }
1371   }
1372
1373   /* Notify application */
1374   silc_client_command_callback(cmd, motd);
1375
1376  out:
1377   silc_fsm_next(fsm, silc_client_command_reply_processed);
1378   return SILC_FSM_CONTINUE;
1379 }
1380
1381 /********************************** UMODE ***********************************/
1382
1383 /* Received reply to the UMODE command. Save the current user mode */
1384
1385 SILC_FSM_STATE(silc_client_command_reply_umode)
1386 {
1387   SilcClientCommandContext cmd = fsm_context;
1388   SilcClientConnection conn = cmd->conn;
1389   SilcCommandPayload payload = state_context;
1390   SilcArgumentPayload args = silc_command_get_args(payload);
1391   unsigned char *tmp;
1392   SilcUInt32 mode, len;
1393
1394   /* Sanity checks */
1395   CHECK_STATUS("Cannot change mode: ");
1396   CHECK_ARGS(2, 2);
1397
1398   tmp = silc_argument_get_arg_type(args, 2, &len);
1399   if (!tmp || len != 4) {
1400     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1401     goto out;
1402   }
1403
1404   SILC_GET32_MSB(mode, tmp);
1405   silc_rwlock_wrlock(conn->local_entry->internal.lock);
1406   conn->local_entry->mode = mode;
1407   silc_rwlock_unlock(conn->local_entry->internal.lock);
1408
1409   /* Notify application */
1410   silc_client_command_callback(cmd, mode);
1411
1412  out:
1413   silc_fsm_next(fsm, silc_client_command_reply_processed);
1414   return SILC_FSM_CONTINUE;
1415 }
1416
1417 /********************************** CMODE ***********************************/
1418
1419 /* Received reply for CMODE command. */
1420
1421 SILC_FSM_STATE(silc_client_command_reply_cmode)
1422 {
1423   SilcClientCommandContext cmd = fsm_context;
1424   SilcClientConnection conn = cmd->conn;
1425   SilcClient client = conn->client;
1426   SilcCommandPayload payload = state_context;
1427   SilcArgumentPayload args = silc_command_get_args(payload);
1428   unsigned char *tmp;
1429   SilcUInt32 mode;
1430   SilcChannelEntry channel = NULL;
1431   SilcUInt32 len;
1432   SilcPublicKey public_key = NULL;
1433   SilcID id;
1434
1435   /* Sanity checks */
1436   CHECK_STATUS("Cannot change mode: ");
1437   CHECK_ARGS(3, 6);
1438
1439   /* Take Channel ID */
1440   if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL)) {
1441     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1442     goto out;
1443   }
1444
1445   /* Get the channel entry */
1446   channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
1447   if (!channel) {
1448     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1449     goto out;
1450   }
1451
1452   /* Get founder public key */
1453   tmp = silc_argument_get_arg_type(args, 4, &len);
1454   if (tmp)
1455     silc_public_key_payload_decode(tmp, len, &public_key);
1456
1457   /* Get channel mode */
1458   tmp = silc_argument_get_arg_type(args, 3, &len);
1459   if (!tmp || len != 4) {
1460     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1461     goto out;
1462   }
1463   SILC_GET32_MSB(mode, tmp);
1464
1465   silc_rwlock_wrlock(channel->internal.lock);
1466
1467   /* Get user limit */
1468   tmp = silc_argument_get_arg_type(args, 6, &len);
1469   if (tmp && len == 4)
1470     SILC_GET32_MSB(channel->user_limit, tmp);
1471   if (!(channel->mode & SILC_CHANNEL_MODE_ULIMIT))
1472     channel->user_limit = 0;
1473
1474   /* Get channel public key(s) */
1475   tmp = silc_argument_get_arg_type(args, 5, &len);
1476   if (tmp)
1477     silc_client_channel_save_public_keys(channel, tmp, len, FALSE);
1478   else if (channel->mode & SILC_CHANNEL_MODE_CHANNEL_AUTH)
1479     silc_client_channel_save_public_keys(channel, NULL, 0, TRUE);
1480
1481   /* Save the mode */
1482   channel->mode = mode;
1483
1484   silc_rwlock_unlock(channel->internal.lock);
1485
1486   /* Notify application */
1487   silc_client_command_callback(cmd, channel, mode, public_key,
1488                                channel->channel_pubkeys, channel->user_limit);
1489
1490  out:
1491   silc_client_unref_channel(client, conn, channel);
1492   if (public_key)
1493     silc_pkcs_public_key_free(public_key);
1494   silc_fsm_next(fsm, silc_client_command_reply_processed);
1495   return SILC_FSM_CONTINUE;
1496 }
1497
1498 /********************************** CUMODE **********************************/
1499
1500 /* Received reply for CUMODE command */
1501
1502 SILC_FSM_STATE(silc_client_command_reply_cumode)
1503 {
1504   SilcClientCommandContext cmd = fsm_context;
1505   SilcClientConnection conn = cmd->conn;
1506   SilcClient client = conn->client;
1507   SilcCommandPayload payload = state_context;
1508   SilcArgumentPayload args = silc_command_get_args(payload);
1509   SilcClientEntry client_entry;
1510   SilcChannelEntry channel = NULL;
1511   SilcChannelUser chu;
1512   unsigned char *modev;
1513   SilcUInt32 len, mode;
1514   SilcID id;
1515
1516   /* Sanity checks */
1517   CHECK_STATUS("Cannot change mode: ");
1518   CHECK_ARGS(4, 4);
1519
1520   /* Get channel mode */
1521   modev = silc_argument_get_arg_type(args, 2, &len);
1522   if (!modev || len != 4) {
1523     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1524     goto out;
1525   }
1526   SILC_GET32_MSB(mode, modev);
1527
1528   /* Take Channel ID */
1529   if (!silc_argument_get_decoded(args, 3, SILC_ARGUMENT_ID, &id, NULL)) {
1530     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1531     goto out;
1532   }
1533
1534   /* Get the channel entry */
1535   channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
1536   if (!channel) {
1537     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1538     goto out;
1539   }
1540
1541   /* Get Client ID */
1542   if (!silc_argument_get_decoded(args, 4, SILC_ARGUMENT_ID, &id, NULL)) {
1543     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1544     goto out;
1545   }
1546
1547   /* Get client entry */
1548   client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
1549   if (!client_entry) {
1550     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1551     goto out;
1552   }
1553
1554   /* Save the mode */
1555   silc_rwlock_wrlock(channel->internal.lock);
1556   chu = silc_client_on_channel(channel, client_entry);
1557   if (chu)
1558     chu->mode = mode;
1559   silc_rwlock_unlock(channel->internal.lock);
1560
1561   /* Notify application */
1562   silc_client_command_callback(cmd, mode, channel, client_entry);
1563
1564   silc_client_unref_client(client, conn, client_entry);
1565
1566  out:
1567   silc_client_unref_channel(client, conn, channel);
1568   silc_fsm_next(fsm, silc_client_command_reply_processed);
1569   return SILC_FSM_CONTINUE;
1570 }
1571
1572 /********************************** KICK ************************************/
1573
1574 SILC_FSM_STATE(silc_client_command_reply_kick)
1575 {
1576   SilcClientCommandContext cmd = fsm_context;
1577   SilcClientConnection conn = cmd->conn;
1578   SilcClient client = conn->client;
1579   SilcCommandPayload payload = state_context;
1580   SilcArgumentPayload args = silc_command_get_args(payload);
1581   SilcClientEntry client_entry;
1582   SilcChannelEntry channel = NULL;
1583   SilcID id;
1584
1585   /* Sanity checks */
1586   CHECK_STATUS("Cannot kick: ");
1587   CHECK_ARGS(3, 3);
1588
1589   /* Take Channel ID */
1590   if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL)) {
1591     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1592     goto out;
1593   }
1594
1595   /* Get the channel entry */
1596   channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
1597   if (!channel) {
1598     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1599     goto out;
1600   }
1601
1602   /* Get Client ID */
1603   if (!silc_argument_get_decoded(args, 3, SILC_ARGUMENT_ID, &id, NULL)) {
1604     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1605     goto out;
1606   }
1607
1608   /* Get client entry */
1609   client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
1610   if (!client_entry) {
1611     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1612     goto out;
1613   }
1614
1615   /* Notify application */
1616   silc_client_command_callback(cmd, channel, client_entry);
1617
1618   silc_client_unref_client(client, conn, client_entry);
1619
1620  out:
1621   silc_client_unref_channel(client, conn, channel);
1622   silc_fsm_next(fsm, silc_client_command_reply_processed);
1623   return SILC_FSM_CONTINUE;
1624 }
1625
1626 /******************************** SILCOPER **********************************/
1627
1628 SILC_FSM_STATE(silc_client_command_reply_silcoper)
1629 {
1630   SilcClientCommandContext cmd = fsm_context;
1631   SilcCommandPayload payload = state_context;
1632   SilcArgumentPayload args = silc_command_get_args(payload);
1633
1634   /* Sanity checks */
1635   CHECK_STATUS("Cannot change mode: ");
1636   CHECK_ARGS(1, 1);
1637
1638   /* Set user mode */
1639   cmd->conn->local_entry->mode |= SILC_UMODE_ROUTER_OPERATOR;
1640
1641   /* Notify application */
1642   silc_client_command_callback(cmd);
1643
1644   silc_fsm_next(fsm, silc_client_command_reply_processed);
1645   return SILC_FSM_CONTINUE;
1646 }
1647
1648 /********************************** OPER ************************************/
1649
1650 SILC_FSM_STATE(silc_client_command_reply_oper)
1651 {
1652   SilcClientCommandContext cmd = fsm_context;
1653   SilcCommandPayload payload = state_context;
1654   SilcArgumentPayload args = silc_command_get_args(payload);
1655
1656   /* Sanity checks */
1657   CHECK_STATUS("Cannot change mode: ");
1658   CHECK_ARGS(1, 1);
1659
1660   /* Set user mode */
1661   cmd->conn->local_entry->mode |= SILC_UMODE_SERVER_OPERATOR;
1662
1663   /* Notify application */
1664   silc_client_command_callback(cmd);
1665
1666   silc_fsm_next(fsm, silc_client_command_reply_processed);
1667   return SILC_FSM_CONTINUE;
1668 }
1669
1670 /********************************* DETACH ***********************************/
1671
1672 SILC_FSM_STATE(silc_client_command_reply_detach)
1673 {
1674   SilcClientCommandContext cmd = fsm_context;
1675   SilcClientConnection conn = cmd->conn;
1676   SilcClient client = conn->client;
1677   SilcCommandPayload payload = state_context;
1678   SilcArgumentPayload args = silc_command_get_args(payload);
1679   SilcBuffer detach;
1680
1681   /* Sanity checks */
1682   CHECK_STATUS("Cannot detach: ");
1683   CHECK_ARGS(1, 1);
1684
1685   /* Get detachment data */
1686   detach = silc_client_get_detach_data(client, conn);
1687   if (!detach) {
1688     ERROR_CALLBACK(SILC_STATUS_ERR_RESOURCE_LIMIT);
1689     goto out;
1690   }
1691
1692   /* Notify application */
1693   silc_client_command_callback(cmd, detach);
1694   silc_buffer_free(detach);
1695
1696  out:
1697   silc_fsm_next(fsm, silc_client_command_reply_processed);
1698   return SILC_FSM_CONTINUE;
1699 }
1700
1701 /********************************** WATCH ***********************************/
1702
1703 SILC_FSM_STATE(silc_client_command_reply_watch)
1704 {
1705   SilcClientCommandContext cmd = fsm_context;
1706   SilcCommandPayload payload = state_context;
1707   SilcArgumentPayload args = silc_command_get_args(payload);
1708
1709   /* Sanity checks */
1710   CHECK_STATUS("Cannot set watch: ");
1711   CHECK_ARGS(1, 1);
1712
1713   /* Notify application */
1714   silc_client_command_callback(cmd);
1715
1716   silc_fsm_next(fsm, silc_client_command_reply_processed);
1717   return SILC_FSM_CONTINUE;
1718 }
1719
1720 /*********************************** BAN ************************************/
1721
1722 SILC_FSM_STATE(silc_client_command_reply_ban)
1723 {
1724   SilcClientCommandContext cmd = fsm_context;
1725   SilcClientConnection conn = cmd->conn;
1726   SilcClient client = conn->client;
1727   SilcCommandPayload payload = state_context;
1728   SilcArgumentPayload args = silc_command_get_args(payload);
1729   SilcChannelEntry channel = NULL;
1730   unsigned char *tmp;
1731   SilcUInt32 len;
1732   SilcArgumentPayload invite_args = NULL;
1733   SilcID id;
1734
1735   /* Sanity checks */
1736   CHECK_STATUS("Cannot set ban: ");
1737   CHECK_ARGS(2, 3);
1738
1739   /* Take Channel ID */
1740   if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL)) {
1741     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1742     goto out;
1743   }
1744
1745   /* Get the channel entry */
1746   channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
1747   if (!channel) {
1748     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1749     goto out;
1750   }
1751
1752   /* Get the invite list */
1753   tmp = silc_argument_get_arg_type(args, 3, &len);
1754   if (tmp)
1755     invite_args = silc_argument_list_parse(tmp, len);
1756
1757   /* Notify application */
1758   silc_client_command_callback(cmd, channel, invite_args);
1759
1760   if (invite_args)
1761     silc_argument_payload_free(invite_args);
1762
1763  out:
1764   silc_client_unref_channel(client, conn, channel);
1765   silc_fsm_next(fsm, silc_client_command_reply_processed);
1766   return SILC_FSM_CONTINUE;
1767 }
1768
1769 /********************************** LEAVE ***********************************/
1770
1771 /* Reply to LEAVE command. */
1772
1773 SILC_FSM_STATE(silc_client_command_reply_leave)
1774 {
1775   SilcClientCommandContext cmd = fsm_context;
1776   SilcClientConnection conn = cmd->conn;
1777   SilcClient client = conn->client;
1778   SilcCommandPayload payload = state_context;
1779   SilcArgumentPayload args = silc_command_get_args(payload);
1780   SilcChannelEntry channel;
1781   SilcCipher key;
1782   SilcHmac hmac;
1783   SilcID id;
1784
1785   /* Sanity checks */
1786   CHECK_STATUS("Cannot set leave: ");
1787   CHECK_ARGS(2, 2);
1788
1789   /* Get Channel ID */
1790   if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL)) {
1791     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1792     goto out;
1793   }
1794
1795   /* Get the channel entry */
1796   channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
1797   if (!channel) {
1798     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1799     goto out;
1800   }
1801
1802   /* Remove us from this channel. */
1803   silc_client_remove_from_channel(client, conn, channel, conn->local_entry);
1804
1805   /* Notify application */
1806   silc_client_command_callback(cmd, channel);
1807
1808   /* Remove old keys and stuff.  The channel may remain even after leaving
1809      but we want to remove these always. */
1810   if (channel->internal.send_key)
1811     silc_cipher_free(channel->internal.send_key);
1812   channel->internal.send_key = NULL;
1813   if (channel->internal.receive_key)
1814     silc_cipher_free(channel->internal.receive_key);
1815   channel->internal.receive_key = NULL;
1816   if (channel->internal.hmac)
1817     silc_hmac_free(channel->internal.hmac);
1818   channel->internal.hmac = NULL;
1819   if (channel->internal.old_channel_keys) {
1820     silc_dlist_start(channel->internal.old_channel_keys);
1821     while ((key = silc_dlist_get(channel->internal.old_channel_keys)))
1822       silc_cipher_free(key);
1823     silc_dlist_uninit(channel->internal.old_channel_keys);
1824   }
1825   channel->internal.old_channel_keys = NULL;
1826   if (channel->internal.old_hmacs) {
1827     silc_dlist_start(channel->internal.old_hmacs);
1828     while ((hmac = silc_dlist_get(channel->internal.old_hmacs)))
1829       silc_hmac_free(hmac);
1830     silc_dlist_uninit(channel->internal.old_hmacs);
1831   }
1832   channel->internal.old_hmacs = NULL;
1833
1834   /* Now delete the channel. */
1835   silc_client_empty_channel(client, conn, channel);
1836   silc_client_del_channel(client, conn, channel);
1837
1838  out:
1839   silc_fsm_next(fsm, silc_client_command_reply_processed);
1840   return SILC_FSM_CONTINUE;
1841 }
1842
1843 /********************************* USERS ************************************/
1844
1845 /* Continue USERS command reply processing after resolving unknown users */
1846
1847 static void
1848 silc_client_command_reply_users_resolved(SilcClient client,
1849                                          SilcClientConnection conn,
1850                                          SilcStatus status,
1851                                          SilcDList clients,
1852                                          void *context)
1853 {
1854   SilcClientCommandContext cmd = context;
1855   SILC_FSM_CALL_CONTINUE(&cmd->thread);
1856 }
1857
1858
1859 /* Continue USERS command after resolving unknown channel */
1860
1861 static void
1862 silc_client_command_reply_users_continue(SilcClient client,
1863                                          SilcClientConnection conn,
1864                                          SilcStatus status,
1865                                          SilcDList channels,
1866                                          void *context)
1867 {
1868   SilcClientCommandContext cmd = context;
1869
1870   if (!channels) {
1871     SilcCommandPayload payload = silc_fsm_get_state_context(&cmd->thread);
1872     SilcArgumentPayload args = silc_command_get_args(payload);
1873
1874     cmd->status = SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID;
1875     ERROR_CALLBACK(cmd->status);
1876     silc_fsm_next(&cmd->thread, silc_client_command_reply_processed);
1877   }
1878
1879   SILC_FSM_CALL_CONTINUE(&cmd->thread);
1880 }
1881
1882 /* Reply to USERS command. Received list of client ID's and theirs modes
1883    on the channel we requested. */
1884
1885 SILC_FSM_STATE(silc_client_command_reply_users)
1886 {
1887   SilcClientCommandContext cmd = fsm_context;
1888   SilcClientConnection conn = cmd->conn;
1889   SilcClient client = conn->client;
1890   SilcCommandPayload payload = state_context;
1891   SilcArgumentPayload args = silc_command_get_args(payload);
1892   unsigned char *tmp;
1893   SilcUInt32 tmp_len, list_count, mode;
1894   SilcUInt16 idp_len;
1895   SilcHashTableList htl;
1896   SilcBufferStruct client_id_list, client_mode_list;
1897   SilcChannelEntry channel = NULL;
1898   SilcClientEntry client_entry;
1899   SilcID id;
1900   int i;
1901
1902   /* Sanity checks */
1903   CHECK_STATUS("Cannot get users: ");
1904   CHECK_ARGS(5, 5);
1905
1906   /* Get channel ID */
1907   if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL)) {
1908     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1909     goto out;
1910   }
1911
1912   /* Get channel entry */
1913   channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
1914   if (!channel) {
1915     /* Resolve the channel from server */
1916     SILC_FSM_CALL(silc_client_get_channel_by_id_resolve(
1917                         client, conn, &id.u.channel_id,
1918                         silc_client_command_reply_users_continue, cmd));
1919     /* NOT REACHED */
1920   }
1921
1922   /* Get the list count */
1923   tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
1924   if (!tmp || tmp_len != 4) {
1925     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1926     goto out;
1927   }
1928   SILC_GET32_MSB(list_count, tmp);
1929
1930   /* Get Client ID list */
1931   tmp = silc_argument_get_arg_type(args, 4, &tmp_len);
1932   if (!tmp) {
1933     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1934     goto out;
1935   }
1936   silc_buffer_set(&client_id_list, tmp, tmp_len);
1937
1938   /* Resolve users we do not know about */
1939   if (!cmd->resolved) {
1940     cmd->resolved = TRUE;
1941     silc_client_unref_channel(client, conn, channel);
1942     SILC_FSM_CALL(silc_client_get_clients_by_list(
1943                           client, conn, list_count, &client_id_list,
1944                           silc_client_command_reply_users_resolved, cmd));
1945     /* NOT REACHED */
1946   }
1947
1948   /* Get client mode list */
1949   tmp = silc_argument_get_arg_type(args, 5, &tmp_len);
1950   if (!tmp) {
1951     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1952     goto out;
1953   }
1954   silc_buffer_set(&client_mode_list, tmp, tmp_len);
1955
1956   SILC_LOG_DEBUG(("channel %s, %d users", channel->channel_name, list_count));
1957
1958   silc_rwlock_wrlock(channel->internal.lock);
1959
1960   /* Cache the received Client ID's and modes. */
1961   for (i = 0; i < list_count; i++) {
1962     SILC_GET16_MSB(idp_len, client_id_list.data + 2);
1963     idp_len += 4;
1964     if (!silc_id_payload_parse_id(client_id_list.data, idp_len, &id))
1965       goto out;
1966
1967     /* Mode */
1968     SILC_GET32_MSB(mode, client_mode_list.data);
1969
1970     /* Save the client on this channel.  Unknown clients are ignored as they
1971        clearly do not exist since the resolving didn't find them. */
1972     client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
1973     if (client_entry && client_entry->internal.valid) {
1974       silc_rwlock_wrlock(client_entry->internal.lock);
1975       silc_client_add_to_channel(client, conn, channel, client_entry, mode);
1976       silc_rwlock_unlock(client_entry->internal.lock);
1977     }
1978     silc_client_unref_client(client, conn, client_entry);
1979
1980     if (!silc_buffer_pull(&client_id_list, idp_len)) {
1981       silc_rwlock_unlock(channel->internal.lock);
1982       goto out;
1983     }
1984     if (!silc_buffer_pull(&client_mode_list, 4)) {
1985       silc_rwlock_unlock(channel->internal.lock);
1986       goto out;
1987     }
1988   }
1989
1990   silc_rwlock_unlock(channel->internal.lock);
1991
1992   /* Notify application */
1993   silc_hash_table_list(channel->user_list, &htl);
1994   silc_client_command_callback(cmd, channel, &htl);
1995   silc_hash_table_list_reset(&htl);
1996
1997  out:
1998   silc_client_unref_channel(client, conn, channel);
1999   silc_fsm_next(fsm, silc_client_command_reply_processed);
2000   return SILC_FSM_CONTINUE;
2001 }
2002
2003 /********************************** GETKEY **********************************/
2004
2005 /* Received command reply to GETKEY command. WE've received the remote
2006    client's public key. */
2007
2008 SILC_FSM_STATE(silc_client_command_reply_getkey)
2009 {
2010   SilcClientCommandContext cmd = fsm_context;
2011   SilcClientConnection conn = cmd->conn;
2012   SilcClient client = conn->client;
2013   SilcCommandPayload payload = state_context;
2014   SilcArgumentPayload args = silc_command_get_args(payload);
2015   SilcClientEntry client_entry;
2016   SilcServerEntry server_entry;
2017   unsigned char *tmp;
2018   SilcUInt32 len;
2019   SilcPublicKey public_key;
2020   SilcID id;
2021
2022   /* Sanity checks */
2023   CHECK_STATUS("Cannot get key: ");
2024   CHECK_ARGS(2, 3);
2025
2026   /* Get the ID */
2027   if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL)) {
2028     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2029     goto out;
2030   }
2031
2032   /* Get the public key */
2033   tmp = silc_argument_get_arg_type(args, 3, &len);
2034   if (!tmp) {
2035     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2036     goto out;
2037   }
2038   if (!silc_public_key_payload_decode(tmp, len, &public_key)) {
2039     SAY(client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
2040         "Cannot decode public key: malformed/unsupported public key");
2041     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2042     goto out;
2043   }
2044
2045   if (id.type == SILC_ID_CLIENT) {
2046     /* Received client's public key */
2047     client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
2048     if (!client_entry) {
2049       ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2050       goto out;
2051     }
2052
2053     silc_rwlock_wrlock(client_entry->internal.lock);
2054
2055     /* Save fingerprint */
2056     if (!client_entry->fingerprint)
2057       silc_hash_make(conn->internal->sha1hash, tmp + 4, len - 4,
2058                      client_entry->fingerprint);
2059     if (!client_entry->public_key) {
2060       client_entry->public_key = public_key;
2061       public_key = NULL;
2062     }
2063
2064     silc_rwlock_unlock(client_entry->internal.lock);
2065
2066     /* Notify application */
2067     silc_client_command_callback(cmd, SILC_ID_CLIENT, client_entry,
2068                                  client_entry->public_key);
2069     silc_client_unref_client(client, conn, client_entry);
2070   } else if (id.type == SILC_ID_SERVER) {
2071     /* Received server's public key */
2072     server_entry = silc_client_get_server_by_id(client, conn, &id.u.server_id);
2073     if (!server_entry) {
2074       ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2075       goto out;
2076     }
2077
2078     silc_rwlock_wrlock(server_entry->internal.lock);
2079
2080     if (!server_entry->public_key) {
2081       server_entry->public_key = public_key;
2082       public_key = NULL;
2083     }
2084
2085     silc_rwlock_unlock(server_entry->internal.lock);
2086
2087     /* Notify application */
2088     silc_client_command_callback(cmd, SILC_ID_SERVER, server_entry,
2089                                  server_entry->public_key);
2090     silc_client_unref_server(client, conn, server_entry);
2091   }
2092
2093  out:
2094   if (public_key)
2095     silc_pkcs_public_key_free(public_key);
2096   silc_fsm_next(fsm, silc_client_command_reply_processed);
2097   return SILC_FSM_CONTINUE;
2098 }
2099
2100 /********************************** SERVICE *********************************/
2101
2102 /* Reply to SERVICE command. */
2103 /* XXX incomplete */
2104
2105 SILC_FSM_STATE(silc_client_command_reply_service)
2106 {
2107   SilcClientCommandContext cmd = fsm_context;
2108   SilcCommandPayload payload = state_context;
2109   SilcArgumentPayload args = silc_command_get_args(payload);
2110   SilcUInt32 tmp_len;
2111   unsigned char *service_list, *name;
2112
2113   /* Sanity checks */
2114   CHECK_STATUS("Cannot get service: ");
2115
2116   /* Get service list */
2117   service_list = silc_argument_get_arg_type(args, 2, &tmp_len);
2118
2119   /* Get requested service name */
2120   name = silc_argument_get_arg_type(args, 3, &tmp_len);
2121
2122   /* Notify application */
2123   silc_client_command_callback(cmd, service_list, name);
2124
2125   silc_fsm_next(fsm, silc_client_command_reply_processed);
2126   return SILC_FSM_CONTINUE;
2127 }
2128
2129 /*********************************** QUIT ***********************************/
2130
2131 /* QUIT command reply stub */
2132
2133 SILC_FSM_STATE(silc_client_command_reply_quit)
2134 {
2135   silc_fsm_next(fsm, silc_client_command_reply_processed);
2136   return SILC_FSM_CONTINUE;
2137 }