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