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