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