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