Added support for channel@server channel name strings (SILC
[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 ? 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     goto out;
737   }
738
739   silc_rwlock_unlock(conn->local_entry->internal.lock);
740
741   /* Notify application */
742   silc_client_command_callback(cmd, conn->local_entry,
743                                conn->local_entry->nickname, &old_client_id);
744
745  out:
746   silc_fsm_next(fsm, silc_client_command_reply_processed);
747   return SILC_FSM_CONTINUE;
748 }
749
750 /********************************** LIST ************************************/
751
752 /* Received reply to the LIST command. */
753
754 SILC_FSM_STATE(silc_client_command_reply_list)
755 {
756   SilcClientCommandContext cmd = fsm_context;
757   SilcClientConnection conn = cmd->conn;
758   SilcClient client = conn->client;
759   SilcCommandPayload payload = state_context;
760   SilcArgumentPayload args = silc_command_get_args(payload);
761   unsigned char *tmp, *name, *topic;
762   SilcUInt32 usercount = 0;
763   SilcChannelEntry channel_entry = NULL;
764   SilcID id;
765
766   /* Sanity checks */
767   CHECK_STATUS("Cannot list channels: ");
768
769   if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL)) {
770     /* There were no channels in the network. */
771     silc_client_command_callback(cmd, NULL, NULL, NULL, 0);
772     silc_fsm_next(fsm, silc_client_command_reply_processed);
773     return SILC_FSM_CONTINUE;
774   }
775
776   CHECK_ARGS(3, 5);
777
778   name = silc_argument_get_arg_type(args, 3, NULL);
779   if (!name) {
780     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
781     goto out;
782   }
783
784   topic = silc_argument_get_arg_type(args, 4, NULL);
785   tmp = silc_argument_get_arg_type(args, 5, NULL);
786   if (tmp)
787     SILC_GET32_MSB(usercount, tmp);
788
789   /* Check whether the channel exists, and add it to cache if it doesn't. */
790   channel_entry = silc_client_get_channel_by_id(client, conn,
791                                                 &id.u.channel_id);
792   if (!channel_entry) {
793     /* Add new channel entry */
794     channel_entry = silc_client_add_channel(client, conn, name, 0,
795                                             &id.u.channel_id);
796     if (!channel_entry) {
797       ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
798       goto out;
799     }
800     silc_client_ref_channel(client, conn, channel_entry);
801   }
802
803   /* Notify application */
804   silc_client_command_callback(cmd, channel_entry, channel_entry->channel_name,
805                                topic, usercount);
806
807  out:
808   silc_client_unref_channel(client, conn, channel_entry);
809   silc_fsm_next(fsm, silc_client_command_reply_processed);
810   return SILC_FSM_CONTINUE;
811 }
812
813 /********************************* TOPIC ************************************/
814
815 /* Received reply to topic command. */
816
817 SILC_FSM_STATE(silc_client_command_reply_topic)
818 {
819   SilcClientCommandContext cmd = fsm_context;
820   SilcClientConnection conn = cmd->conn;
821   SilcClient client = conn->client;
822   SilcCommandPayload payload = state_context;
823   SilcArgumentPayload args = silc_command_get_args(payload);
824   SilcChannelEntry channel;
825   char *topic;
826   SilcUInt32 len;
827   SilcID id;
828
829   /* Sanity checks */
830   CHECK_STATUS("Cannot set topic: ");
831   CHECK_ARGS(2, 3);
832
833   /* Take Channel ID */
834   if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL)) {
835     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
836     goto out;
837   }
838
839   /* Get the channel entry */
840   channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
841   if (!channel) {
842     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
843     goto out;
844   }
845
846   silc_rwlock_wrlock(channel->internal.lock);
847
848   /* Take topic */
849   topic = silc_argument_get_arg_type(args, 3, &len);
850   if (topic) {
851     silc_free(channel->topic);
852     channel->topic = silc_memdup(topic, len);
853   }
854
855   silc_rwlock_unlock(channel->internal.lock);
856
857   /* Notify application */
858   silc_client_command_callback(cmd, channel, channel->topic);
859
860  out:
861   silc_fsm_next(fsm, silc_client_command_reply_processed);
862   return SILC_FSM_CONTINUE;
863 }
864
865 /********************************* INVITE ***********************************/
866
867 /* Received reply to invite command. */
868
869 SILC_FSM_STATE(silc_client_command_reply_invite)
870 {
871   SilcClientCommandContext cmd = fsm_context;
872   SilcClientConnection conn = cmd->conn;
873   SilcClient client = conn->client;
874   SilcCommandPayload payload = state_context;
875   SilcArgumentPayload args = silc_command_get_args(payload);
876   SilcChannelEntry channel;
877   unsigned char *tmp;
878   SilcUInt32 len;
879   SilcArgumentPayload invite_args = NULL;
880   SilcID id;
881
882   /* Sanity checks */
883   CHECK_STATUS("Cannot invite: ");
884   CHECK_ARGS(2, 3);
885
886   /* Take Channel ID */
887   if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL)) {
888     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
889     goto out;
890   }
891
892   /* Get the channel entry */
893   channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
894   if (!channel) {
895     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
896     goto out;
897   }
898
899   /* Get the invite list */
900   tmp = silc_argument_get_arg_type(args, 3, &len);
901   if (tmp)
902     invite_args = silc_argument_list_parse(tmp, len);
903
904   /* Notify application */
905   silc_client_command_callback(cmd, channel, invite_args);
906
907   if (invite_args)
908     silc_argument_payload_free(invite_args);
909
910  out:
911   silc_fsm_next(fsm, silc_client_command_reply_processed);
912   return SILC_FSM_CONTINUE;
913 }
914
915 /********************************** KILL ************************************/
916
917 /* Received reply to the KILL command. */
918
919 SILC_FSM_STATE(silc_client_command_reply_kill)
920 {
921   SilcClientCommandContext cmd = fsm_context;
922   SilcClientConnection conn = cmd->conn;
923   SilcClient client = conn->client;
924   SilcCommandPayload payload = state_context;
925   SilcArgumentPayload args = silc_command_get_args(payload);
926   SilcClientEntry client_entry;
927   SilcID id;
928
929   /* Sanity checks */
930   CHECK_STATUS("Cannot kill: ");
931   CHECK_ARGS(2, 2);
932
933   if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL)) {
934     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
935     goto out;
936   }
937
938   /* Get the client entry, if exists */
939   client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
940
941   /* Notify application */
942   silc_client_command_callback(cmd, client_entry);
943
944   /* Remove the client */
945   if (client_entry) {
946     silc_client_remove_from_channels(client, conn, client_entry);
947     silc_client_del_client(client, conn, client_entry);
948     silc_client_unref_client(client, conn, client_entry);
949   }
950
951  out:
952   silc_fsm_next(fsm, silc_client_command_reply_processed);
953   return SILC_FSM_CONTINUE;
954 }
955
956 /********************************** INFO ************************************/
957
958 /* Received reply to INFO command. We receive the server ID and some
959    information about the server user requested. */
960
961 SILC_FSM_STATE(silc_client_command_reply_info)
962 {
963   SilcClientCommandContext cmd = fsm_context;
964   SilcClientConnection conn = cmd->conn;
965   SilcClient client = conn->client;
966   SilcCommandPayload payload = state_context;
967   SilcArgumentPayload args = silc_command_get_args(payload);
968   SilcServerEntry server;
969   char *server_name, *server_info;
970   SilcID id;
971
972   /* Sanity checks */
973   CHECK_STATUS("Cannot get info: ");
974   CHECK_ARGS(4, 4);
975
976   /* Get server ID */
977   if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL)) {
978     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
979     goto out;
980   }
981
982   /* Get server name */
983   server_name = silc_argument_get_arg_type(args, 3, NULL);
984   if (!server_name) {
985     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
986     goto out;
987   }
988
989   /* Get server info */
990   server_info = silc_argument_get_arg_type(args, 4, NULL);
991   if (!server_info) {
992     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
993     goto out;
994   }
995
996   /* See whether we have this server cached. If not create it. */
997   server = silc_client_get_server_by_id(client, conn, &id.u.server_id);
998   if (!server) {
999     SILC_LOG_DEBUG(("Add new server entry (INFO)"));
1000     server = silc_client_add_server(client, conn, server_name,
1001                                     server_info, &id.u.server_id);
1002     if (!server)
1003       goto out;
1004     silc_client_ref_server(client, conn, server);
1005   }
1006
1007   /* Notify application */
1008   silc_client_command_callback(cmd, server, server->server_name,
1009                                server->server_info);
1010   silc_client_unref_server(client, conn, server);
1011
1012  out:
1013   silc_fsm_next(fsm, silc_client_command_reply_processed);
1014   return SILC_FSM_CONTINUE;
1015 }
1016
1017 /********************************** STATS ***********************************/
1018
1019 /* Received reply to STATS command. */
1020
1021 SILC_FSM_STATE(silc_client_command_reply_stats)
1022 {
1023   SilcClientCommandContext cmd = fsm_context;
1024   SilcCommandPayload payload = state_context;
1025   SilcArgumentPayload args = silc_command_get_args(payload);
1026   SilcClientStats stats;
1027   unsigned char *buf = NULL;
1028   SilcUInt32 buf_len = 0;
1029   SilcBufferStruct b;
1030   SilcID id;
1031
1032   /* Sanity checks */
1033   CHECK_STATUS("Cannot get stats: ");
1034   CHECK_ARGS(2, 3);
1035
1036   /* Get server ID */
1037   if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL)) {
1038     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1039     goto out;
1040   }
1041
1042   /* Get statistics structure */
1043   memset(&stats, 0, sizeof(stats));
1044   buf = silc_argument_get_arg_type(args, 3, &buf_len);
1045   if (buf) {
1046     silc_buffer_set(&b, buf, buf_len);
1047     silc_buffer_unformat(&b,
1048                          SILC_STR_UI_INT(&stats.starttime),
1049                          SILC_STR_UI_INT(&stats.uptime),
1050                          SILC_STR_UI_INT(&stats.my_clients),
1051                          SILC_STR_UI_INT(&stats.my_channels),
1052                          SILC_STR_UI_INT(&stats.my_server_ops),
1053                          SILC_STR_UI_INT(&stats.my_router_ops),
1054                          SILC_STR_UI_INT(&stats.cell_clients),
1055                          SILC_STR_UI_INT(&stats.cell_channels),
1056                          SILC_STR_UI_INT(&stats.cell_servers),
1057                          SILC_STR_UI_INT(&stats.clients),
1058                          SILC_STR_UI_INT(&stats.channels),
1059                          SILC_STR_UI_INT(&stats.servers),
1060                          SILC_STR_UI_INT(&stats.routers),
1061                          SILC_STR_UI_INT(&stats.server_ops),
1062                          SILC_STR_UI_INT(&stats.router_ops),
1063                          SILC_STR_END);
1064   }
1065
1066   /* Notify application */
1067   silc_client_command_callback(cmd, &stats);
1068
1069  out:
1070   silc_fsm_next(fsm, silc_client_command_reply_processed);
1071   return SILC_FSM_CONTINUE;
1072 }
1073
1074 /********************************** PING ************************************/
1075
1076 /* Received reply to PING command. */
1077
1078 SILC_FSM_STATE(silc_client_command_reply_ping)
1079 {
1080   SilcClientCommandContext cmd = fsm_context;
1081   SilcClientConnection conn = cmd->conn;
1082   SilcClient client = conn->client;
1083   SilcInt64 diff;
1084
1085   diff = silc_time() - SILC_PTR_TO_64(cmd->context);
1086   if (cmd->verbose)
1087     SAY(client, conn, SILC_CLIENT_MESSAGE_INFO,
1088         "Ping reply from %s: %d second%s", conn->remote_host,
1089         (int)diff, diff == 1 ? "" : "s");
1090
1091   /* Notify application */
1092   silc_client_command_callback(cmd);
1093
1094   silc_fsm_next(fsm, silc_client_command_reply_processed);
1095   return SILC_FSM_CONTINUE;
1096 }
1097
1098 /********************************** JOIN ************************************/
1099
1100 /* Continue JOIN command reply processing after resolving unknown users */
1101
1102 static void
1103 silc_client_command_reply_join_resolved(SilcClient client,
1104                                         SilcClientConnection conn,
1105                                         SilcStatus status,
1106                                         SilcDList clients,
1107                                         void *context)
1108 {
1109   SilcClientCommandContext cmd = context;
1110   SilcChannelEntry channel = cmd->context;
1111
1112   channel->internal.resolve_cmd_ident = 0;
1113   silc_client_unref_channel(client, conn, channel);
1114
1115   SILC_FSM_CALL_CONTINUE(&cmd->thread);
1116 }
1117
1118
1119 /* Received reply for JOIN command. */
1120
1121 SILC_FSM_STATE(silc_client_command_reply_join)
1122 {
1123   SilcClientCommandContext cmd = fsm_context;
1124   SilcClientConnection conn = cmd->conn;
1125   SilcClient client = conn->client;
1126   SilcCommandPayload payload = state_context;
1127   SilcArgumentPayload args = silc_command_get_args(payload);
1128   SilcChannelEntry channel;
1129   SilcUInt32 mode = 0, len, list_count;
1130   char *topic, *tmp, *channel_name = NULL, *hmac;
1131   const char *cipher;
1132   SilcBufferStruct client_id_list, client_mode_list, keyp;
1133   SilcHashTableList htl;
1134   SilcID id;
1135   int i;
1136
1137   /* Sanity checks */
1138   CHECK_STATUS("Cannot join channel: ");
1139   CHECK_ARGS(9, 17);
1140
1141   /* Get channel name */
1142   channel_name = silc_argument_get_arg_type(args, 2, NULL);
1143   if (!channel_name) {
1144     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1145     goto out;
1146   }
1147
1148   /* Get Channel ID */
1149   if (!silc_argument_get_decoded(args, 3, SILC_ARGUMENT_ID, &id, NULL)) {
1150     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1151     goto out;
1152   }
1153
1154   /* Check whether we have this channel entry already. */
1155   channel = silc_client_get_channel(client, conn, channel_name);
1156   if (channel) {
1157     if (!SILC_ID_CHANNEL_COMPARE(&channel->id, &id.u.channel_id))
1158       silc_client_replace_channel_id(client, conn, channel, &id.u.channel_id);
1159   } else {
1160     /* Create new channel entry */
1161     channel = silc_client_add_channel(client, conn, channel_name,
1162                                       mode, &id.u.channel_id);
1163     if (!channel) {
1164       ERROR_CALLBACK(SILC_STATUS_ERR_BAD_CHANNEL);
1165       goto out;
1166     }
1167     silc_client_ref_channel(client, conn, channel);
1168   }
1169
1170   /* Get the list count */
1171   tmp = silc_argument_get_arg_type(args, 12, &len);
1172   if (!tmp) {
1173     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1174     goto out;
1175   }
1176   SILC_GET32_MSB(list_count, tmp);
1177
1178   /* Get Client ID list */
1179   tmp = silc_argument_get_arg_type(args, 13, &len);
1180   if (!tmp) {
1181     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1182     goto out;
1183   }
1184   silc_buffer_set(&client_id_list, tmp, len);
1185
1186   /* Resolve users we do not know about */
1187   if (!cmd->resolved) {
1188     cmd->resolved = TRUE;
1189     cmd->context = channel;
1190     SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
1191                   silc_client_get_clients_by_list(
1192                           client, conn, list_count, &client_id_list,
1193                           silc_client_command_reply_join_resolved, cmd));
1194     /* NOT REACHED */
1195   }
1196
1197   /* Get client mode list */
1198   tmp = silc_argument_get_arg_type(args, 14, &len);
1199   if (!tmp) {
1200     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1201     goto out;
1202   }
1203   silc_buffer_set(&client_mode_list, tmp, len);
1204
1205   silc_rwlock_wrlock(channel->internal.lock);
1206
1207   /* Add clients we received in the reply to the channel */
1208   for (i = 0; i < list_count; i++) {
1209     SilcUInt16 idp_len;
1210     SilcUInt32 mode;
1211     SilcID id;
1212     SilcClientEntry client_entry;
1213
1214     /* Client ID */
1215     SILC_GET16_MSB(idp_len, client_id_list.data + 2);
1216     idp_len += 4;
1217     if (!silc_id_payload_parse_id(client_id_list.data, idp_len, &id))
1218       continue;
1219
1220     /* Mode */
1221     SILC_GET32_MSB(mode, client_mode_list.data);
1222
1223     /* Get client entry */
1224     client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
1225     if (client_entry && client_entry->internal.valid) {
1226       /* Join client to the channel */
1227       silc_rwlock_wrlock(client_entry->internal.lock);
1228       silc_client_add_to_channel(client, conn, channel, client_entry, mode);
1229       silc_rwlock_unlock(client_entry->internal.lock);
1230     }
1231     silc_client_unref_client(client, conn, client_entry);
1232
1233     if (!silc_buffer_pull(&client_id_list, idp_len)) {
1234       silc_rwlock_unlock(channel->internal.lock);
1235       goto out;
1236     }
1237     if (!silc_buffer_pull(&client_mode_list, 4)) {
1238       silc_rwlock_unlock(channel->internal.lock);
1239       goto out;
1240     }
1241   }
1242
1243   /* Get hmac */
1244   hmac = silc_argument_get_arg_type(args, 11, NULL);
1245   if (hmac) {
1246     if (!silc_hmac_alloc(hmac, NULL, &channel->internal.hmac)) {
1247       if (cmd->verbose)
1248         SAY(client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
1249             "Cannot join channel: Unsupported HMAC `%s'", hmac);
1250       ERROR_CALLBACK(SILC_STATUS_ERR_UNKNOWN_ALGORITHM);
1251       silc_rwlock_unlock(channel->internal.lock);
1252       goto out;
1253     }
1254   }
1255
1256   /* Get channel mode */
1257   tmp = silc_argument_get_arg_type(args, 5, &len);
1258   if (tmp && len == 4)
1259     SILC_GET32_MSB(mode, tmp);
1260   channel->mode = mode;
1261
1262   /* Get channel key and save it */
1263   tmp = silc_argument_get_arg_type(args, 7, &len);
1264   if (tmp) {
1265     silc_buffer_set(&keyp, tmp, len);
1266     silc_client_save_channel_key(client, conn, &keyp, channel);
1267   }
1268
1269   /* Get topic */
1270   topic = silc_argument_get_arg_type(args, 10, NULL);
1271   if (topic) {
1272     silc_free(channel->topic);
1273     channel->topic = silc_memdup(topic, strlen(topic));
1274   }
1275
1276   /* Get founder key */
1277   tmp = silc_argument_get_arg_type(args, 15, &len);
1278   if (tmp) {
1279     if (channel->founder_key)
1280       silc_pkcs_public_key_free(channel->founder_key);
1281     channel->founder_key = NULL;
1282     silc_public_key_payload_decode(tmp, len, &channel->founder_key);
1283   }
1284
1285   /* Get user limit */
1286   tmp = silc_argument_get_arg_type(args, 17, &len);
1287   if (tmp && len == 4)
1288     SILC_GET32_MSB(channel->user_limit, tmp);
1289   if (!(channel->mode & SILC_CHANNEL_MODE_ULIMIT))
1290     channel->user_limit = 0;
1291
1292   /* Get channel public key list */
1293   tmp = silc_argument_get_arg_type(args, 16, &len);
1294   if (tmp)
1295     silc_client_channel_save_public_keys(channel, tmp, len, FALSE);
1296
1297   /* Set current channel */
1298   conn->current_channel = channel;
1299
1300   silc_rwlock_unlock(channel->internal.lock);
1301
1302   cipher = (channel->internal.send_key ?
1303             silc_cipher_get_name(channel->internal.send_key) : NULL);
1304   silc_hash_table_list(channel->user_list, &htl);
1305
1306   /* Notify application */
1307   silc_client_command_callback(cmd, channel->channel_name, channel, mode, &htl,
1308                                topic, cipher, hmac, channel->founder_key,
1309                                channel->channel_pubkeys, channel->user_limit);
1310
1311   silc_hash_table_list_reset(&htl);
1312   silc_client_unref_channel(client, conn, channel);
1313
1314  out:
1315   silc_fsm_next(fsm, silc_client_command_reply_processed);
1316   return SILC_FSM_CONTINUE;
1317 }
1318
1319 /********************************** MOTD ************************************/
1320
1321 /* Received reply for MOTD command */
1322
1323 SILC_FSM_STATE(silc_client_command_reply_motd)
1324 {
1325   SilcClientCommandContext cmd = fsm_context;
1326   SilcClientConnection conn = cmd->conn;
1327   SilcClient client = conn->client;
1328   SilcCommandPayload payload = state_context;
1329   SilcArgumentPayload args = silc_command_get_args(payload);
1330   SilcUInt32 i;
1331   char *motd = NULL, *cp, line[256];
1332
1333   /* Sanity checks */
1334   CHECK_STATUS("Cannot get motd: ");
1335   CHECK_ARGS(2, 3);
1336
1337   if (silc_argument_get_arg_num(args) == 3) {
1338     motd = silc_argument_get_arg_type(args, 3, NULL);
1339     if (!motd) {
1340       ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1341       goto out;
1342     }
1343
1344     i = 0;
1345     cp = motd;
1346     while(cp[i] != 0) {
1347       if (cp[i++] == '\n') {
1348         memset(line, 0, sizeof(line));
1349         silc_strncat(line, sizeof(line), cp, i - 1);
1350         cp += i;
1351
1352         if (i == 2)
1353           line[0] = ' ';
1354
1355         if (cmd->verbose)
1356           SAY(client, conn, SILC_CLIENT_MESSAGE_INFO, "%s", line);
1357
1358         if (!strlen(cp))
1359           break;
1360         i = 0;
1361       }
1362     }
1363   }
1364
1365   /* Notify application */
1366   silc_client_command_callback(cmd, motd);
1367
1368  out:
1369   silc_fsm_next(fsm, silc_client_command_reply_processed);
1370   return SILC_FSM_CONTINUE;
1371 }
1372
1373 /********************************** UMODE ***********************************/
1374
1375 /* Received reply to the UMODE command. Save the current user mode */
1376
1377 SILC_FSM_STATE(silc_client_command_reply_umode)
1378 {
1379   SilcClientCommandContext cmd = fsm_context;
1380   SilcClientConnection conn = cmd->conn;
1381   SilcCommandPayload payload = state_context;
1382   SilcArgumentPayload args = silc_command_get_args(payload);
1383   unsigned char *tmp;
1384   SilcUInt32 mode, len;
1385
1386   /* Sanity checks */
1387   CHECK_STATUS("Cannot change mode: ");
1388   CHECK_ARGS(2, 2);
1389
1390   tmp = silc_argument_get_arg_type(args, 2, &len);
1391   if (!tmp || len != 4) {
1392     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1393     goto out;
1394   }
1395
1396   SILC_GET32_MSB(mode, tmp);
1397   silc_rwlock_wrlock(conn->local_entry->internal.lock);
1398   conn->local_entry->mode = mode;
1399   silc_rwlock_unlock(conn->local_entry->internal.lock);
1400
1401   /* Notify application */
1402   silc_client_command_callback(cmd, mode);
1403
1404  out:
1405   silc_fsm_next(fsm, silc_client_command_reply_processed);
1406   return SILC_FSM_CONTINUE;
1407 }
1408
1409 /********************************** CMODE ***********************************/
1410
1411 /* Received reply for CMODE command. */
1412
1413 SILC_FSM_STATE(silc_client_command_reply_cmode)
1414 {
1415   SilcClientCommandContext cmd = fsm_context;
1416   SilcClientConnection conn = cmd->conn;
1417   SilcClient client = conn->client;
1418   SilcCommandPayload payload = state_context;
1419   SilcArgumentPayload args = silc_command_get_args(payload);
1420   unsigned char *tmp;
1421   SilcUInt32 mode;
1422   SilcChannelEntry channel;
1423   SilcUInt32 len;
1424   SilcPublicKey public_key = NULL;
1425   SilcID id;
1426
1427   /* Sanity checks */
1428   CHECK_STATUS("Cannot change mode: ");
1429   CHECK_ARGS(3, 6);
1430
1431   /* Take Channel ID */
1432   if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL)) {
1433     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1434     goto out;
1435   }
1436
1437   /* Get the channel entry */
1438   channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
1439   if (!channel) {
1440     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1441     goto out;
1442   }
1443
1444   /* Get founder public key */
1445   tmp = silc_argument_get_arg_type(args, 4, &len);
1446   if (tmp)
1447     silc_public_key_payload_decode(tmp, len, &public_key);
1448
1449   /* Get channel mode */
1450   tmp = silc_argument_get_arg_type(args, 3, &len);
1451   if (!tmp || len != 4) {
1452     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1453     goto out;
1454   }
1455   SILC_GET32_MSB(mode, tmp);
1456
1457   silc_rwlock_wrlock(channel->internal.lock);
1458
1459   /* Get user limit */
1460   tmp = silc_argument_get_arg_type(args, 6, &len);
1461   if (tmp && len == 4)
1462     SILC_GET32_MSB(channel->user_limit, tmp);
1463   if (!(channel->mode & SILC_CHANNEL_MODE_ULIMIT))
1464     channel->user_limit = 0;
1465
1466   /* Get channel public key(s) */
1467   tmp = silc_argument_get_arg_type(args, 5, &len);
1468   if (tmp)
1469     silc_client_channel_save_public_keys(channel, tmp, len, FALSE);
1470   else if (channel->mode & SILC_CHANNEL_MODE_CHANNEL_AUTH)
1471     silc_client_channel_save_public_keys(channel, NULL, 0, TRUE);
1472
1473   /* Save the mode */
1474   channel->mode = mode;
1475
1476   silc_rwlock_unlock(channel->internal.lock);
1477
1478   /* Notify application */
1479   silc_client_command_callback(cmd, channel, mode, public_key,
1480                                channel->channel_pubkeys, channel->user_limit);
1481
1482  out:
1483   if (public_key)
1484     silc_pkcs_public_key_free(public_key);
1485   silc_fsm_next(fsm, silc_client_command_reply_processed);
1486   return SILC_FSM_CONTINUE;
1487 }
1488
1489 /********************************** CUMODE **********************************/
1490
1491 /* Received reply for CUMODE command */
1492
1493 SILC_FSM_STATE(silc_client_command_reply_cumode)
1494 {
1495   SilcClientCommandContext cmd = fsm_context;
1496   SilcClientConnection conn = cmd->conn;
1497   SilcClient client = conn->client;
1498   SilcCommandPayload payload = state_context;
1499   SilcArgumentPayload args = silc_command_get_args(payload);
1500   SilcClientEntry client_entry;
1501   SilcChannelEntry channel;
1502   SilcChannelUser chu;
1503   unsigned char *modev;
1504   SilcUInt32 len, mode;
1505   SilcID id;
1506
1507   /* Sanity checks */
1508   CHECK_STATUS("Cannot change mode: ");
1509   CHECK_ARGS(4, 4);
1510
1511   /* Get channel mode */
1512   modev = silc_argument_get_arg_type(args, 2, &len);
1513   if (!modev || len != 4) {
1514     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1515     goto out;
1516   }
1517   SILC_GET32_MSB(mode, modev);
1518
1519   /* Take Channel ID */
1520   if (!silc_argument_get_decoded(args, 3, SILC_ARGUMENT_ID, &id, NULL)) {
1521     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1522     goto out;
1523   }
1524
1525   /* Get the channel entry */
1526   channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
1527   if (!channel) {
1528     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1529     goto out;
1530   }
1531
1532   /* Get Client ID */
1533   if (!silc_argument_get_decoded(args, 4, SILC_ARGUMENT_ID, &id, NULL)) {
1534     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1535     goto out;
1536   }
1537
1538   /* Get client entry */
1539   client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
1540   if (!client_entry) {
1541     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1542     goto out;
1543   }
1544
1545   /* Save the mode */
1546   silc_rwlock_wrlock(channel->internal.lock);
1547   chu = silc_client_on_channel(channel, client_entry);
1548   if (chu)
1549     chu->mode = mode;
1550   silc_rwlock_unlock(channel->internal.lock);
1551
1552   /* Notify application */
1553   silc_client_command_callback(cmd, mode, channel, client_entry);
1554
1555   silc_client_unref_client(client, conn, client_entry);
1556
1557  out:
1558   silc_fsm_next(fsm, silc_client_command_reply_processed);
1559   return SILC_FSM_CONTINUE;
1560 }
1561
1562 /********************************** KICK ************************************/
1563
1564 SILC_FSM_STATE(silc_client_command_reply_kick)
1565 {
1566   SilcClientCommandContext cmd = fsm_context;
1567   SilcClientConnection conn = cmd->conn;
1568   SilcClient client = conn->client;
1569   SilcCommandPayload payload = state_context;
1570   SilcArgumentPayload args = silc_command_get_args(payload);
1571   SilcClientEntry client_entry;
1572   SilcChannelEntry channel;
1573   SilcID id;
1574
1575   /* Sanity checks */
1576   CHECK_STATUS("Cannot kick: ");
1577   CHECK_ARGS(3, 3);
1578
1579   /* Take Channel ID */
1580   if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL)) {
1581     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1582     goto out;
1583   }
1584
1585   /* Get the channel entry */
1586   channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
1587   if (!channel) {
1588     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1589     goto out;
1590   }
1591
1592   /* Get Client ID */
1593   if (!silc_argument_get_decoded(args, 3, SILC_ARGUMENT_ID, &id, NULL)) {
1594     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1595     goto out;
1596   }
1597
1598   /* Get client entry */
1599   client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
1600   if (!client_entry) {
1601     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1602     goto out;
1603   }
1604
1605   /* Notify application */
1606   silc_client_command_callback(cmd, channel, client_entry);
1607
1608   silc_client_unref_client(client, conn, client_entry);
1609
1610  out:
1611   silc_fsm_next(fsm, silc_client_command_reply_processed);
1612   return SILC_FSM_CONTINUE;
1613 }
1614
1615 /******************************** SILCOPER **********************************/
1616
1617 SILC_FSM_STATE(silc_client_command_reply_silcoper)
1618 {
1619   SilcClientCommandContext cmd = fsm_context;
1620   SilcCommandPayload payload = state_context;
1621   SilcArgumentPayload args = silc_command_get_args(payload);
1622
1623   /* Sanity checks */
1624   CHECK_STATUS("Cannot change mode: ");
1625   CHECK_ARGS(1, 1);
1626
1627   /* Set user mode */
1628   cmd->conn->local_entry->mode |= SILC_UMODE_ROUTER_OPERATOR;
1629
1630   /* Notify application */
1631   silc_client_command_callback(cmd);
1632
1633   silc_fsm_next(fsm, silc_client_command_reply_processed);
1634   return SILC_FSM_CONTINUE;
1635 }
1636
1637 /********************************** OPER ************************************/
1638
1639 SILC_FSM_STATE(silc_client_command_reply_oper)
1640 {
1641   SilcClientCommandContext cmd = fsm_context;
1642   SilcCommandPayload payload = state_context;
1643   SilcArgumentPayload args = silc_command_get_args(payload);
1644
1645   /* Sanity checks */
1646   CHECK_STATUS("Cannot change mode: ");
1647   CHECK_ARGS(1, 1);
1648
1649   /* Set user mode */
1650   cmd->conn->local_entry->mode |= SILC_UMODE_SERVER_OPERATOR;
1651
1652   /* Notify application */
1653   silc_client_command_callback(cmd);
1654
1655   silc_fsm_next(fsm, silc_client_command_reply_processed);
1656   return SILC_FSM_CONTINUE;
1657 }
1658
1659 /********************************* DETACH ***********************************/
1660
1661 SILC_FSM_STATE(silc_client_command_reply_detach)
1662 {
1663   SilcClientCommandContext cmd = fsm_context;
1664   SilcClientConnection conn = cmd->conn;
1665   SilcClient client = conn->client;
1666   SilcCommandPayload payload = state_context;
1667   SilcArgumentPayload args = silc_command_get_args(payload);
1668   SilcBuffer detach;
1669
1670   /* Sanity checks */
1671   CHECK_STATUS("Cannot detach: ");
1672   CHECK_ARGS(1, 1);
1673
1674   /* Get detachment data */
1675   detach = silc_client_get_detach_data(client, conn);
1676   if (!detach) {
1677     ERROR_CALLBACK(SILC_STATUS_ERR_RESOURCE_LIMIT);
1678     goto out;
1679   }
1680
1681   /* Notify application */
1682   silc_client_command_callback(cmd, detach);
1683   silc_buffer_free(detach);
1684
1685  out:
1686   silc_fsm_next(fsm, silc_client_command_reply_processed);
1687   return SILC_FSM_CONTINUE;
1688 }
1689
1690 /********************************** WATCH ***********************************/
1691
1692 SILC_FSM_STATE(silc_client_command_reply_watch)
1693 {
1694   SilcClientCommandContext cmd = fsm_context;
1695   SilcCommandPayload payload = state_context;
1696   SilcArgumentPayload args = silc_command_get_args(payload);
1697
1698   /* Sanity checks */
1699   CHECK_STATUS("Cannot set watch: ");
1700   CHECK_ARGS(1, 1);
1701
1702   /* Notify application */
1703   silc_client_command_callback(cmd);
1704
1705   silc_fsm_next(fsm, silc_client_command_reply_processed);
1706   return SILC_FSM_CONTINUE;
1707 }
1708
1709 /*********************************** BAN ************************************/
1710
1711 SILC_FSM_STATE(silc_client_command_reply_ban)
1712 {
1713   SilcClientCommandContext cmd = fsm_context;
1714   SilcClientConnection conn = cmd->conn;
1715   SilcClient client = conn->client;
1716   SilcCommandPayload payload = state_context;
1717   SilcArgumentPayload args = silc_command_get_args(payload);
1718   SilcChannelEntry channel;
1719   unsigned char *tmp;
1720   SilcUInt32 len;
1721   SilcArgumentPayload invite_args = NULL;
1722   SilcID id;
1723
1724   /* Sanity checks */
1725   CHECK_STATUS("Cannot set ban: ");
1726   CHECK_ARGS(2, 3);
1727
1728   /* Take Channel ID */
1729   if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL)) {
1730     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1731     goto out;
1732   }
1733
1734   /* Get the channel entry */
1735   channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
1736   if (!channel) {
1737     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1738     goto out;
1739   }
1740
1741   /* Get the invite list */
1742   tmp = silc_argument_get_arg_type(args, 3, &len);
1743   if (tmp)
1744     invite_args = silc_argument_list_parse(tmp, len);
1745
1746   /* Notify application */
1747   silc_client_command_callback(cmd, channel, invite_args);
1748
1749   if (invite_args)
1750     silc_argument_payload_free(invite_args);
1751
1752  out:
1753   silc_fsm_next(fsm, silc_client_command_reply_processed);
1754   return SILC_FSM_CONTINUE;
1755 }
1756
1757 /********************************** LEAVE ***********************************/
1758
1759 /* Reply to LEAVE command. */
1760
1761 SILC_FSM_STATE(silc_client_command_reply_leave)
1762 {
1763   SilcClientCommandContext cmd = fsm_context;
1764   SilcClientConnection conn = cmd->conn;
1765   SilcClient client = conn->client;
1766   SilcCommandPayload payload = state_context;
1767   SilcArgumentPayload args = silc_command_get_args(payload);
1768   SilcChannelEntry channel;
1769   SilcID id;
1770
1771   /* Sanity checks */
1772   CHECK_STATUS("Cannot set leave: ");
1773   CHECK_ARGS(2, 2);
1774
1775   /* Get Channel ID */
1776   if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL)) {
1777     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1778     goto out;
1779   }
1780
1781   /* Get the channel entry */
1782   channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
1783   if (!channel) {
1784     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1785     goto out;
1786   }
1787
1788   /* Remove us from this channel. */
1789   silc_client_remove_from_channel(client, conn, channel, conn->local_entry);
1790
1791   /* Notify application */
1792   silc_client_command_callback(cmd, channel);
1793
1794   /* Now delete the channel. */
1795   silc_client_empty_channel(client, conn, channel);
1796   silc_client_del_channel(client, conn, channel);
1797
1798  out:
1799   silc_fsm_next(fsm, silc_client_command_reply_processed);
1800   return SILC_FSM_CONTINUE;
1801 }
1802
1803 /********************************* USERS ************************************/
1804
1805 /* Continue USERS command reply processing after resolving unknown users */
1806
1807 static void
1808 silc_client_command_reply_users_resolved(SilcClient client,
1809                                          SilcClientConnection conn,
1810                                          SilcStatus status,
1811                                          SilcDList clients,
1812                                          void *context)
1813 {
1814   SilcClientCommandContext cmd = context;
1815   SILC_FSM_CALL_CONTINUE(&cmd->thread);
1816 }
1817
1818
1819 /* Continue USERS command after resolving unknown channel */
1820
1821 static void
1822 silc_client_command_reply_users_continue(SilcClient client,
1823                                          SilcClientConnection conn,
1824                                          SilcStatus status,
1825                                          SilcDList channels,
1826                                          void *context)
1827 {
1828   SilcClientCommandContext cmd = context;
1829
1830   if (!channels) {
1831     SilcCommandPayload payload = silc_fsm_get_state_context(&cmd->thread);
1832     SilcArgumentPayload args = silc_command_get_args(payload);
1833
1834     cmd->status = SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID;
1835     ERROR_CALLBACK(cmd->status);
1836     silc_fsm_next(&cmd->thread, silc_client_command_reply_processed);
1837   }
1838
1839   SILC_FSM_CALL_CONTINUE(&cmd->thread);
1840 }
1841
1842 /* Reply to USERS command. Received list of client ID's and theirs modes
1843    on the channel we requested. */
1844
1845 SILC_FSM_STATE(silc_client_command_reply_users)
1846 {
1847   SilcClientCommandContext cmd = fsm_context;
1848   SilcClientConnection conn = cmd->conn;
1849   SilcClient client = conn->client;
1850   SilcCommandPayload payload = state_context;
1851   SilcArgumentPayload args = silc_command_get_args(payload);
1852   unsigned char *tmp;
1853   SilcUInt32 tmp_len, list_count;
1854   SilcUInt16 idp_len, mode;
1855   SilcHashTableList htl;
1856   SilcBufferStruct client_id_list, client_mode_list;
1857   SilcChannelEntry channel = NULL;
1858   SilcClientEntry client_entry;
1859   SilcID id;
1860   int i;
1861
1862   /* Sanity checks */
1863   CHECK_STATUS("Cannot get users: ");
1864   CHECK_ARGS(5, 5);
1865
1866   /* Get channel ID */
1867   if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL)) {
1868     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1869     goto out;
1870   }
1871
1872   /* Get channel entry */
1873   channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
1874   if (!channel) {
1875     /* Resolve the channel from server */
1876     SILC_FSM_CALL(silc_client_get_channel_by_id_resolve(
1877                         client, conn, &id.u.channel_id,
1878                         silc_client_command_reply_users_continue, cmd));
1879     /* NOT REACHED */
1880   }
1881
1882   /* Get the list count */
1883   tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
1884   if (!tmp || tmp_len != 4) {
1885     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1886     goto out;
1887   }
1888   SILC_GET32_MSB(list_count, tmp);
1889
1890   /* Get Client ID list */
1891   tmp = silc_argument_get_arg_type(args, 4, &tmp_len);
1892   if (!tmp) {
1893     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1894     goto out;
1895   }
1896   silc_buffer_set(&client_id_list, tmp, tmp_len);
1897
1898   /* Resolve users we do not know about */
1899   if (!cmd->resolved) {
1900     cmd->resolved = TRUE;
1901     silc_client_unref_channel(client, conn, channel);
1902     SILC_FSM_CALL(silc_client_get_clients_by_list(
1903                           client, conn, list_count, &client_id_list,
1904                           silc_client_command_reply_users_resolved, cmd));
1905     /* NOT REACHED */
1906   }
1907
1908   /* Get client mode list */
1909   tmp = silc_argument_get_arg_type(args, 5, &tmp_len);
1910   if (!tmp) {
1911     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1912     goto out;
1913   }
1914   silc_buffer_set(&client_mode_list, tmp, tmp_len);
1915
1916   SILC_LOG_DEBUG(("channel %s, %d users", channel->channel_name, list_count));
1917
1918   silc_rwlock_wrlock(channel->internal.lock);
1919
1920   /* Cache the received Client ID's and modes. */
1921   for (i = 0; i < list_count; i++) {
1922     SILC_GET16_MSB(idp_len, client_id_list.data + 2);
1923     idp_len += 4;
1924     if (!silc_id_payload_parse_id(client_id_list.data, idp_len, &id))
1925       goto out;
1926
1927     /* Mode */
1928     SILC_GET32_MSB(mode, client_mode_list.data);
1929
1930     /* Save the client on this channel.  Unknown clients are ignored as they
1931        clearly do not exist since the resolving didn't find them. */
1932     client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
1933     if (client_entry && client_entry->internal.valid) {
1934       silc_rwlock_wrlock(client_entry->internal.lock);
1935       silc_client_add_to_channel(client, conn, channel, client_entry, mode);
1936       silc_rwlock_unlock(client_entry->internal.lock);
1937     }
1938     silc_client_unref_client(client, conn, client_entry);
1939
1940     if (!silc_buffer_pull(&client_id_list, idp_len)) {
1941       silc_rwlock_unlock(channel->internal.lock);
1942       goto out;
1943     }
1944     if (!silc_buffer_pull(&client_mode_list, 4)) {
1945       silc_rwlock_unlock(channel->internal.lock);
1946       goto out;
1947     }
1948   }
1949
1950   /* Notify application */
1951   silc_hash_table_list(channel->user_list, &htl);
1952   silc_client_command_callback(cmd, channel, &htl);
1953   silc_hash_table_list_reset(&htl);
1954
1955  out:
1956   silc_client_unref_channel(client, conn, channel);
1957   silc_fsm_next(fsm, silc_client_command_reply_processed);
1958   return SILC_FSM_CONTINUE;
1959 }
1960
1961 /********************************** GETKEY **********************************/
1962
1963 /* Received command reply to GETKEY command. WE've received the remote
1964    client's public key. */
1965
1966 SILC_FSM_STATE(silc_client_command_reply_getkey)
1967 {
1968   SilcClientCommandContext cmd = fsm_context;
1969   SilcClientConnection conn = cmd->conn;
1970   SilcClient client = conn->client;
1971   SilcCommandPayload payload = state_context;
1972   SilcArgumentPayload args = silc_command_get_args(payload);
1973   SilcClientEntry client_entry;
1974   SilcServerEntry server_entry;
1975   unsigned char *tmp;
1976   SilcUInt32 len;
1977   SilcPublicKey public_key;
1978   SilcID id;
1979
1980   /* Sanity checks */
1981   CHECK_STATUS("Cannot get key: ");
1982   CHECK_ARGS(2, 3);
1983
1984   /* Get the ID */
1985   if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL)) {
1986     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1987     goto out;
1988   }
1989
1990   /* Get the public key */
1991   tmp = silc_argument_get_arg_type(args, 3, &len);
1992   if (!tmp) {
1993     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1994     goto out;
1995   }
1996   if (!silc_public_key_payload_decode(tmp, len, &public_key)) {
1997     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1998     goto out;
1999   }
2000
2001   if (id.type == SILC_ID_CLIENT) {
2002     /* Received client's public key */
2003     client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
2004     if (!client_entry) {
2005       ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2006       goto out;
2007     }
2008
2009     silc_rwlock_wrlock(client_entry->internal.lock);
2010
2011     /* Save fingerprint */
2012     if (!client_entry->fingerprint)
2013       silc_hash_make(conn->internal->sha1hash, tmp + 4, len - 4,
2014                      client_entry->fingerprint);
2015     if (!client_entry->public_key) {
2016       client_entry->public_key = public_key;
2017       public_key = NULL;
2018     }
2019
2020     silc_rwlock_unlock(client_entry->internal.lock);
2021
2022     /* Notify application */
2023     silc_client_command_callback(cmd, SILC_ID_CLIENT, client_entry,
2024                                  client_entry->public_key);
2025     silc_client_unref_client(client, conn, client_entry);
2026   } else if (id.type == SILC_ID_SERVER) {
2027     /* Received server's public key */
2028     server_entry = silc_client_get_server_by_id(client, conn, &id.u.server_id);
2029     if (!server_entry) {
2030       ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2031       goto out;
2032     }
2033
2034     silc_rwlock_wrlock(server_entry->internal.lock);
2035
2036     if (!server_entry->public_key) {
2037       server_entry->public_key = public_key;
2038       public_key = NULL;
2039     }
2040
2041     silc_rwlock_unlock(server_entry->internal.lock);
2042
2043     /* Notify application */
2044     silc_client_command_callback(cmd, SILC_ID_SERVER, server_entry,
2045                                  server_entry->public_key);
2046     silc_client_unref_server(client, conn, server_entry);
2047   }
2048
2049  out:
2050   if (public_key)
2051     silc_pkcs_public_key_free(public_key);
2052   silc_fsm_next(fsm, silc_client_command_reply_processed);
2053   return SILC_FSM_CONTINUE;
2054 }
2055
2056 /********************************** SERVICE *********************************/
2057
2058 /* Reply to SERVICE command. */
2059 /* XXX incomplete */
2060
2061 SILC_FSM_STATE(silc_client_command_reply_service)
2062 {
2063   SilcClientCommandContext cmd = fsm_context;
2064   SilcCommandPayload payload = state_context;
2065   SilcArgumentPayload args = silc_command_get_args(payload);
2066   SilcUInt32 tmp_len;
2067   unsigned char *service_list, *name;
2068
2069   /* Sanity checks */
2070   CHECK_STATUS("Cannot get service: ");
2071
2072   /* Get service list */
2073   service_list = silc_argument_get_arg_type(args, 2, &tmp_len);
2074
2075   /* Get requested service name */
2076   name = silc_argument_get_arg_type(args, 3, &tmp_len);
2077
2078   /* Notify application */
2079   silc_client_command_callback(cmd, service_list, name);
2080
2081   silc_fsm_next(fsm, silc_client_command_reply_processed);
2082   return SILC_FSM_CONTINUE;
2083 }
2084
2085 /*********************************** QUIT ***********************************/
2086
2087 /* QUIT command reply stub */
2088
2089 SILC_FSM_STATE(silc_client_command_reply_quit)
2090 {
2091   silc_fsm_next(fsm, silc_client_command_reply_processed);
2092   return SILC_FSM_CONTINUE;
2093 }