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