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