Sun Mar 11 15:22:42 CET 2007 Jochen Eisinger <coffee@silcnet.org>
[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);
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   SilcDList channel_pubkeys = NULL;
1424   SilcID id;
1425
1426   /* Sanity checks */
1427   CHECK_STATUS("Cannot change mode: ");
1428   CHECK_ARGS(3, 6);
1429
1430   /* Take Channel ID */
1431   if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL)) {
1432     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1433     goto out;
1434   }
1435
1436   /* Get the channel entry */
1437   channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
1438   if (!channel) {
1439     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1440     goto out;
1441   }
1442
1443   /* Get founder public key */
1444   tmp = silc_argument_get_arg_type(args, 4, &len);
1445   if (tmp)
1446     silc_public_key_payload_decode(tmp, len, &public_key);
1447
1448   /* Get channel mode */
1449   tmp = silc_argument_get_arg_type(args, 3, &len);
1450   if (!tmp || len != 4) {
1451     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1452     goto out;
1453   }
1454
1455   silc_rwlock_wrlock(channel->internal.lock);
1456
1457   /* Save the mode */
1458   SILC_GET32_MSB(mode, tmp);
1459   channel->mode = mode;
1460
1461   /* Get user limit */
1462   tmp = silc_argument_get_arg_type(args, 6, &len);
1463   if (tmp && len == 4)
1464     SILC_GET32_MSB(channel->user_limit, tmp);
1465   if (!(channel->mode & SILC_CHANNEL_MODE_ULIMIT))
1466     channel->user_limit = 0;
1467
1468   /* Get channel public key(s) */
1469   tmp = silc_argument_get_arg_type(args, 5, &len);
1470   if (tmp)
1471     silc_client_channel_save_public_keys(channel, tmp, len);
1472
1473   silc_rwlock_unlock(channel->internal.lock);
1474
1475   /* Notify application */
1476   silc_client_command_callback(cmd, channel, mode, public_key,
1477                                channel_pubkeys, channel->user_limit);
1478
1479   silc_argument_list_free(channel_pubkeys, SILC_ARGUMENT_PUBLIC_KEY);
1480
1481  out:
1482   if (public_key)
1483     silc_pkcs_public_key_free(public_key);
1484   silc_fsm_next(fsm, silc_client_command_reply_processed);
1485   return SILC_FSM_CONTINUE;
1486 }
1487
1488 /********************************** CUMODE **********************************/
1489
1490 /* Received reply for CUMODE command */
1491
1492 SILC_FSM_STATE(silc_client_command_reply_cumode)
1493 {
1494   SilcClientCommandContext cmd = fsm_context;
1495   SilcClientConnection conn = cmd->conn;
1496   SilcClient client = conn->client;
1497   SilcCommandPayload payload = state_context;
1498   SilcArgumentPayload args = silc_command_get_args(payload);
1499   SilcClientEntry client_entry;
1500   SilcChannelEntry channel;
1501   SilcChannelUser chu;
1502   unsigned char *modev;
1503   SilcUInt32 len, mode;
1504   SilcID id;
1505
1506   /* Sanity checks */
1507   CHECK_STATUS("Cannot change mode: ");
1508   CHECK_ARGS(4, 4);
1509
1510   /* Get channel mode */
1511   modev = silc_argument_get_arg_type(args, 2, &len);
1512   if (!modev || len != 4) {
1513     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1514     goto out;
1515   }
1516   SILC_GET32_MSB(mode, modev);
1517
1518   /* Take Channel ID */
1519   if (!silc_argument_get_decoded(args, 3, SILC_ARGUMENT_ID, &id, NULL)) {
1520     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1521     goto out;
1522   }
1523
1524   /* Get the channel entry */
1525   channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
1526   if (!channel) {
1527     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1528     goto out;
1529   }
1530
1531   /* Get Client ID */
1532   if (!silc_argument_get_decoded(args, 4, SILC_ARGUMENT_ID, &id, NULL)) {
1533     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1534     goto out;
1535   }
1536
1537   /* Get client entry */
1538   client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
1539   if (!client_entry) {
1540     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1541     goto out;
1542   }
1543
1544   /* Save the mode */
1545   silc_rwlock_wrlock(channel->internal.lock);
1546   chu = silc_client_on_channel(channel, client_entry);
1547   if (chu)
1548     chu->mode = mode;
1549   silc_rwlock_unlock(channel->internal.lock);
1550
1551   /* Notify application */
1552   silc_client_command_callback(cmd, mode, channel, client_entry);
1553
1554   silc_client_unref_client(client, conn, client_entry);
1555
1556  out:
1557   silc_fsm_next(fsm, silc_client_command_reply_processed);
1558   return SILC_FSM_CONTINUE;
1559 }
1560
1561 /********************************** KICK ************************************/
1562
1563 SILC_FSM_STATE(silc_client_command_reply_kick)
1564 {
1565   SilcClientCommandContext cmd = fsm_context;
1566   SilcClientConnection conn = cmd->conn;
1567   SilcClient client = conn->client;
1568   SilcCommandPayload payload = state_context;
1569   SilcArgumentPayload args = silc_command_get_args(payload);
1570   SilcClientEntry client_entry;
1571   SilcChannelEntry channel;
1572   SilcID id;
1573
1574   /* Sanity checks */
1575   CHECK_STATUS("Cannot kick: ");
1576   CHECK_ARGS(3, 3);
1577
1578   /* Take Channel ID */
1579   if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL)) {
1580     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1581     goto out;
1582   }
1583
1584   /* Get the channel entry */
1585   channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
1586   if (!channel) {
1587     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1588     goto out;
1589   }
1590
1591   /* Get Client ID */
1592   if (!silc_argument_get_decoded(args, 3, SILC_ARGUMENT_ID, &id, NULL)) {
1593     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1594     goto out;
1595   }
1596
1597   /* Get client entry */
1598   client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
1599   if (!client_entry) {
1600     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1601     goto out;
1602   }
1603
1604   /* Notify application */
1605   silc_client_command_callback(cmd, channel, client_entry);
1606
1607   silc_client_unref_client(client, conn, client_entry);
1608
1609  out:
1610   silc_fsm_next(fsm, silc_client_command_reply_processed);
1611   return SILC_FSM_CONTINUE;
1612 }
1613
1614 /******************************** SILCOPER **********************************/
1615
1616 SILC_FSM_STATE(silc_client_command_reply_silcoper)
1617 {
1618   SilcClientCommandContext cmd = fsm_context;
1619   SilcCommandPayload payload = state_context;
1620   SilcArgumentPayload args = silc_command_get_args(payload);
1621
1622   /* Sanity checks */
1623   CHECK_STATUS("Cannot change mode: ");
1624   CHECK_ARGS(1, 1);
1625
1626   /* Notify application */
1627   silc_client_command_callback(cmd);
1628
1629   silc_fsm_next(fsm, silc_client_command_reply_processed);
1630   return SILC_FSM_CONTINUE;
1631 }
1632
1633 /********************************** OPER ************************************/
1634
1635 SILC_FSM_STATE(silc_client_command_reply_oper)
1636 {
1637   SilcClientCommandContext cmd = fsm_context;
1638   SilcCommandPayload payload = state_context;
1639   SilcArgumentPayload args = silc_command_get_args(payload);
1640
1641   /* Sanity checks */
1642   CHECK_STATUS("Cannot change mode: ");
1643   CHECK_ARGS(1, 1);
1644
1645   /* Notify application */
1646   silc_client_command_callback(cmd);
1647
1648   silc_fsm_next(fsm, silc_client_command_reply_processed);
1649   return SILC_FSM_CONTINUE;
1650 }
1651
1652 /********************************* DETACH ***********************************/
1653
1654 SILC_FSM_STATE(silc_client_command_reply_detach)
1655 {
1656   SilcClientCommandContext cmd = fsm_context;
1657   SilcClientConnection conn = cmd->conn;
1658   SilcClient client = conn->client;
1659   SilcCommandPayload payload = state_context;
1660   SilcArgumentPayload args = silc_command_get_args(payload);
1661   SilcBuffer detach;
1662
1663   /* Sanity checks */
1664   CHECK_STATUS("Cannot detach: ");
1665   CHECK_ARGS(1, 1);
1666
1667   /* Get detachment data */
1668   detach = silc_client_get_detach_data(client, conn);
1669   if (!detach) {
1670     ERROR_CALLBACK(SILC_STATUS_ERR_RESOURCE_LIMIT);
1671     goto out;
1672   }
1673
1674   /* Notify application */
1675   silc_client_command_callback(cmd, detach);
1676   silc_buffer_free(detach);
1677
1678  out:
1679   silc_fsm_next(fsm, silc_client_command_reply_processed);
1680   return SILC_FSM_CONTINUE;
1681 }
1682
1683 /********************************** WATCH ***********************************/
1684
1685 SILC_FSM_STATE(silc_client_command_reply_watch)
1686 {
1687   SilcClientCommandContext cmd = fsm_context;
1688   SilcCommandPayload payload = state_context;
1689   SilcArgumentPayload args = silc_command_get_args(payload);
1690
1691   /* Sanity checks */
1692   CHECK_STATUS("Cannot set watch: ");
1693   CHECK_ARGS(1, 1);
1694
1695   /* Notify application */
1696   silc_client_command_callback(cmd);
1697
1698   silc_fsm_next(fsm, silc_client_command_reply_processed);
1699   return SILC_FSM_CONTINUE;
1700 }
1701
1702 /*********************************** BAN ************************************/
1703
1704 SILC_FSM_STATE(silc_client_command_reply_ban)
1705 {
1706   SilcClientCommandContext cmd = fsm_context;
1707   SilcClientConnection conn = cmd->conn;
1708   SilcClient client = conn->client;
1709   SilcCommandPayload payload = state_context;
1710   SilcArgumentPayload args = silc_command_get_args(payload);
1711   SilcChannelEntry channel;
1712   unsigned char *tmp;
1713   SilcUInt32 len;
1714   SilcArgumentPayload invite_args = NULL;
1715   SilcID id;
1716
1717   /* Sanity checks */
1718   CHECK_STATUS("Cannot set ban: ");
1719   CHECK_ARGS(2, 3);
1720
1721   /* Take Channel ID */
1722   if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL)) {
1723     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1724     goto out;
1725   }
1726
1727   /* Get the channel entry */
1728   channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
1729   if (!channel) {
1730     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1731     goto out;
1732   }
1733
1734   /* Get the invite list */
1735   tmp = silc_argument_get_arg_type(args, 3, &len);
1736   if (tmp)
1737     invite_args = silc_argument_list_parse(tmp, len);
1738
1739   /* Notify application */
1740   silc_client_command_callback(cmd, channel, invite_args);
1741
1742   if (invite_args)
1743     silc_argument_payload_free(invite_args);
1744
1745  out:
1746   silc_fsm_next(fsm, silc_client_command_reply_processed);
1747   return SILC_FSM_CONTINUE;
1748 }
1749
1750 /********************************** LEAVE ***********************************/
1751
1752 /* Reply to LEAVE command. */
1753
1754 SILC_FSM_STATE(silc_client_command_reply_leave)
1755 {
1756   SilcClientCommandContext cmd = fsm_context;
1757   SilcClientConnection conn = cmd->conn;
1758   SilcClient client = conn->client;
1759   SilcCommandPayload payload = state_context;
1760   SilcArgumentPayload args = silc_command_get_args(payload);
1761   SilcChannelEntry channel;
1762   SilcID id;
1763
1764   /* Sanity checks */
1765   CHECK_STATUS("Cannot set leave: ");
1766   CHECK_ARGS(2, 2);
1767
1768   /* Get Channel ID */
1769   if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL)) {
1770     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1771     goto out;
1772   }
1773
1774   /* Get the channel entry */
1775   channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
1776   if (!channel) {
1777     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1778     goto out;
1779   }
1780
1781   /* Remove us from this channel. */
1782   silc_client_remove_from_channel(client, conn, channel, conn->local_entry);
1783
1784   /* Notify application */
1785   silc_client_command_callback(cmd, channel);
1786
1787   /* Now delete the channel. */
1788   silc_client_empty_channel(client, conn, channel);
1789   silc_client_del_channel(client, conn, channel);
1790
1791  out:
1792   silc_fsm_next(fsm, silc_client_command_reply_processed);
1793   return SILC_FSM_CONTINUE;
1794 }
1795
1796 /********************************* USERS ************************************/
1797
1798 /* Continue USERS command reply processing after resolving unknown users */
1799
1800 static void
1801 silc_client_command_reply_users_resolved(SilcClient client,
1802                                          SilcClientConnection conn,
1803                                          SilcStatus status,
1804                                          SilcDList clients,
1805                                          void *context)
1806 {
1807   SilcClientCommandContext cmd = context;
1808   SILC_FSM_CALL_CONTINUE(&cmd->thread);
1809 }
1810
1811
1812 /* Continue USERS command after resolving unknown channel */
1813
1814 static void
1815 silc_client_command_reply_users_continue(SilcClient client,
1816                                          SilcClientConnection conn,
1817                                          SilcStatus status,
1818                                          SilcDList channels,
1819                                          void *context)
1820 {
1821   SilcClientCommandContext cmd = context;
1822
1823   if (!channels) {
1824     SilcCommandPayload payload = silc_fsm_get_state_context(&cmd->thread);
1825     SilcArgumentPayload args = silc_command_get_args(payload);
1826
1827     cmd->status = SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID;
1828     ERROR_CALLBACK(cmd->status);
1829     silc_fsm_next(&cmd->thread, silc_client_command_reply_processed);
1830   }
1831
1832   SILC_FSM_CALL_CONTINUE(&cmd->thread);
1833 }
1834
1835 /* Reply to USERS command. Received list of client ID's and theirs modes
1836    on the channel we requested. */
1837
1838 SILC_FSM_STATE(silc_client_command_reply_users)
1839 {
1840   SilcClientCommandContext cmd = fsm_context;
1841   SilcClientConnection conn = cmd->conn;
1842   SilcClient client = conn->client;
1843   SilcCommandPayload payload = state_context;
1844   SilcArgumentPayload args = silc_command_get_args(payload);
1845   unsigned char *tmp;
1846   SilcUInt32 tmp_len, list_count;
1847   SilcUInt16 idp_len, mode;
1848   SilcHashTableList htl;
1849   SilcBufferStruct client_id_list, client_mode_list;
1850   SilcChannelEntry channel = NULL;
1851   SilcClientEntry client_entry;
1852   SilcID id;
1853   int i;
1854
1855   /* Sanity checks */
1856   CHECK_STATUS("Cannot get users: ");
1857   CHECK_ARGS(5, 5);
1858
1859   /* Get channel ID */
1860   if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL)) {
1861     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1862     goto out;
1863   }
1864
1865   /* Get channel entry */
1866   channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
1867   if (!channel) {
1868     /* Resolve the channel from server */
1869     SILC_FSM_CALL(silc_client_get_channel_by_id_resolve(
1870                         client, conn, &id.u.channel_id,
1871                         silc_client_command_reply_users_continue, cmd));
1872     /* NOT REACHED */
1873   }
1874
1875   /* Get the list count */
1876   tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
1877   if (!tmp || tmp_len != 4) {
1878     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1879     goto out;
1880   }
1881   SILC_GET32_MSB(list_count, tmp);
1882
1883   /* Get Client ID list */
1884   tmp = silc_argument_get_arg_type(args, 4, &tmp_len);
1885   if (!tmp) {
1886     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1887     goto out;
1888   }
1889   silc_buffer_set(&client_id_list, tmp, tmp_len);
1890
1891   /* Resolve users we do not know about */
1892   if (!cmd->resolved) {
1893     cmd->resolved = TRUE;
1894     silc_client_unref_channel(client, conn, channel);
1895     SILC_FSM_CALL(silc_client_get_clients_by_list(
1896                           client, conn, list_count, &client_id_list,
1897                           silc_client_command_reply_users_resolved, cmd));
1898     /* NOT REACHED */
1899   }
1900
1901   /* Get client mode list */
1902   tmp = silc_argument_get_arg_type(args, 5, &tmp_len);
1903   if (!tmp) {
1904     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1905     goto out;
1906   }
1907   silc_buffer_set(&client_mode_list, tmp, tmp_len);
1908
1909   SILC_LOG_DEBUG(("channel %s, %d users", channel->channel_name, list_count));
1910
1911   silc_rwlock_wrlock(channel->internal.lock);
1912
1913   /* Cache the received Client ID's and modes. */
1914   for (i = 0; i < list_count; i++) {
1915     SILC_GET16_MSB(idp_len, client_id_list.data + 2);
1916     idp_len += 4;
1917     if (!silc_id_payload_parse_id(client_id_list.data, idp_len, &id))
1918       goto out;
1919
1920     /* Mode */
1921     SILC_GET32_MSB(mode, client_mode_list.data);
1922
1923     /* Save the client on this channel.  Unknown clients are ignored as they
1924        clearly do not exist since the resolving didn't find them. */
1925     client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
1926     if (client_entry && client_entry->internal.valid) {
1927       silc_rwlock_wrlock(client_entry->internal.lock);
1928       silc_client_add_to_channel(client, conn, channel, client_entry, mode);
1929       silc_rwlock_unlock(client_entry->internal.lock);
1930     }
1931     silc_client_unref_client(client, conn, client_entry);
1932
1933     if (!silc_buffer_pull(&client_id_list, idp_len)) {
1934       silc_rwlock_unlock(channel->internal.lock);
1935       goto out;
1936     }
1937     if (!silc_buffer_pull(&client_mode_list, 4)) {
1938       silc_rwlock_unlock(channel->internal.lock);
1939       goto out;
1940     }
1941   }
1942
1943   /* Notify application */
1944   silc_hash_table_list(channel->user_list, &htl);
1945   silc_client_command_callback(cmd, channel, &htl);
1946   silc_hash_table_list_reset(&htl);
1947
1948  out:
1949   silc_client_unref_channel(client, conn, channel);
1950   silc_fsm_next(fsm, silc_client_command_reply_processed);
1951   return SILC_FSM_CONTINUE;
1952 }
1953
1954 /********************************** GETKEY **********************************/
1955
1956 /* Received command reply to GETKEY command. WE've received the remote
1957    client's public key. */
1958
1959 SILC_FSM_STATE(silc_client_command_reply_getkey)
1960 {
1961   SilcClientCommandContext cmd = fsm_context;
1962   SilcClientConnection conn = cmd->conn;
1963   SilcClient client = conn->client;
1964   SilcCommandPayload payload = state_context;
1965   SilcArgumentPayload args = silc_command_get_args(payload);
1966   SilcClientEntry client_entry;
1967   SilcServerEntry server_entry;
1968   unsigned char *tmp;
1969   SilcUInt32 len;
1970   SilcPublicKey public_key;
1971   SilcID id;
1972
1973   /* Sanity checks */
1974   CHECK_STATUS("Cannot get key: ");
1975   CHECK_ARGS(2, 3);
1976
1977   /* Get the ID */
1978   if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL)) {
1979     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1980     goto out;
1981   }
1982
1983   /* Get the public key */
1984   tmp = silc_argument_get_arg_type(args, 3, &len);
1985   if (!tmp) {
1986     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1987     goto out;
1988   }
1989   if (!silc_public_key_payload_decode(tmp, len, &public_key)) {
1990     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1991     goto out;
1992   }
1993
1994   if (id.type == SILC_ID_CLIENT) {
1995     /* Received client's public key */
1996     client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
1997     if (!client_entry) {
1998       ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1999       goto out;
2000     }
2001
2002     silc_rwlock_wrlock(client_entry->internal.lock);
2003
2004     /* Save fingerprint */
2005     if (!client_entry->fingerprint)
2006       silc_hash_make(conn->internal->sha1hash, tmp + 4, len - 4,
2007                      client_entry->fingerprint);
2008     if (!client_entry->public_key) {
2009       client_entry->public_key = public_key;
2010       public_key = NULL;
2011     }
2012
2013     silc_rwlock_unlock(client_entry->internal.lock);
2014
2015     /* Notify application */
2016     silc_client_command_callback(cmd, SILC_ID_CLIENT, client_entry,
2017                                  client_entry->public_key);
2018     silc_client_unref_client(client, conn, client_entry);
2019   } else if (id.type == SILC_ID_SERVER) {
2020     /* Received server's public key */
2021     server_entry = silc_client_get_server_by_id(client, conn, &id.u.server_id);
2022     if (!server_entry) {
2023       ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2024       goto out;
2025     }
2026
2027     silc_rwlock_wrlock(server_entry->internal.lock);
2028
2029     if (!server_entry->public_key) {
2030       server_entry->public_key = public_key;
2031       public_key = NULL;
2032     }
2033
2034     silc_rwlock_unlock(server_entry->internal.lock);
2035
2036     /* Notify application */
2037     silc_client_command_callback(cmd, SILC_ID_SERVER, server_entry,
2038                                  server_entry->public_key);
2039     silc_client_unref_server(client, conn, server_entry);
2040   }
2041
2042  out:
2043   if (public_key)
2044     silc_pkcs_public_key_free(public_key);
2045   silc_fsm_next(fsm, silc_client_command_reply_processed);
2046   return SILC_FSM_CONTINUE;
2047 }
2048
2049 /********************************** SERVICE *********************************/
2050
2051 /* Reply to SERVICE command. */
2052 /* XXX incomplete */
2053
2054 SILC_FSM_STATE(silc_client_command_reply_service)
2055 {
2056   SilcClientCommandContext cmd = fsm_context;
2057   SilcCommandPayload payload = state_context;
2058   SilcArgumentPayload args = silc_command_get_args(payload);
2059   SilcUInt32 tmp_len;
2060   unsigned char *service_list, *name;
2061
2062   /* Sanity checks */
2063   CHECK_STATUS("Cannot get service: ");
2064
2065   /* Get service list */
2066   service_list = silc_argument_get_arg_type(args, 2, &tmp_len);
2067
2068   /* Get requested service name */
2069   name = silc_argument_get_arg_type(args, 3, &tmp_len);
2070
2071   /* Notify application */
2072   silc_client_command_callback(cmd, service_list, name);
2073
2074   silc_fsm_next(fsm, silc_client_command_reply_processed);
2075   return SILC_FSM_CONTINUE;
2076 }
2077
2078 /*********************************** QUIT ***********************************/
2079
2080 /* QUIT command reply stub */
2081
2082 SILC_FSM_STATE(silc_client_command_reply_quit)
2083 {
2084   silc_fsm_next(fsm, silc_client_command_reply_processed);
2085   return SILC_FSM_CONTINUE;
2086 }