Mark client entry invalid when killed and when notified non-existing
[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 the list count */
1176   tmp = silc_argument_get_arg_type(args, 12, &len);
1177   if (!tmp) {
1178     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1179     goto out;
1180   }
1181   SILC_GET32_MSB(list_count, tmp);
1182
1183   /* Get Client ID list */
1184   tmp = silc_argument_get_arg_type(args, 13, &len);
1185   if (!tmp) {
1186     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1187     goto out;
1188   }
1189   silc_buffer_set(&client_id_list, tmp, len);
1190
1191   /* Resolve users we do not know about */
1192   if (!cmd->resolved) {
1193     cmd->resolved = TRUE;
1194     cmd->context = channel;
1195     SILC_FSM_CALL(channel->internal.resolve_cmd_ident =
1196                   silc_client_get_clients_by_list(
1197                           client, conn, list_count, &client_id_list,
1198                           silc_client_command_reply_join_resolved, cmd));
1199     /* NOT REACHED */
1200   }
1201
1202   /* Get client mode list */
1203   tmp = silc_argument_get_arg_type(args, 14, &len);
1204   if (!tmp) {
1205     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1206     goto out;
1207   }
1208   silc_buffer_set(&client_mode_list, tmp, len);
1209
1210   silc_rwlock_wrlock(channel->internal.lock);
1211
1212   /* Add clients we received in the reply to the channel */
1213   for (i = 0; i < list_count; i++) {
1214     SilcUInt16 idp_len;
1215     SilcUInt32 mode;
1216     SilcID id;
1217     SilcClientEntry client_entry;
1218
1219     /* Client ID */
1220     SILC_GET16_MSB(idp_len, client_id_list.data + 2);
1221     idp_len += 4;
1222     if (!silc_id_payload_parse_id(client_id_list.data, idp_len, &id))
1223       continue;
1224
1225     /* Mode */
1226     SILC_GET32_MSB(mode, client_mode_list.data);
1227
1228     /* Get client entry */
1229     client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
1230     if (client_entry && client_entry->internal.valid) {
1231       /* Join client to the channel */
1232       silc_rwlock_wrlock(client_entry->internal.lock);
1233       silc_client_add_to_channel(client, conn, channel, client_entry, mode);
1234       silc_rwlock_unlock(client_entry->internal.lock);
1235     }
1236     silc_client_unref_client(client, conn, client_entry);
1237
1238     if (!silc_buffer_pull(&client_id_list, idp_len)) {
1239       silc_rwlock_unlock(channel->internal.lock);
1240       goto out;
1241     }
1242     if (!silc_buffer_pull(&client_mode_list, 4)) {
1243       silc_rwlock_unlock(channel->internal.lock);
1244       goto out;
1245     }
1246   }
1247
1248   /* Get hmac */
1249   hmac = silc_argument_get_arg_type(args, 11, NULL);
1250   if (hmac) {
1251     if (!silc_hmac_alloc(hmac, NULL, &channel->internal.hmac)) {
1252       if (cmd->verbose)
1253         SAY(client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
1254             "Cannot join channel: Unsupported HMAC `%s'", hmac);
1255       ERROR_CALLBACK(SILC_STATUS_ERR_UNKNOWN_ALGORITHM);
1256       silc_rwlock_unlock(channel->internal.lock);
1257       goto out;
1258     }
1259   }
1260
1261   /* Get channel mode */
1262   tmp = silc_argument_get_arg_type(args, 5, &len);
1263   if (tmp && len == 4)
1264     SILC_GET32_MSB(mode, tmp);
1265   channel->mode = mode;
1266
1267   /* Get channel key and save it */
1268   tmp = silc_argument_get_arg_type(args, 7, &len);
1269   if (tmp) {
1270     /* If channel key already exists on the channel then while resolving
1271        the user list we have already received new key from server.  Don't
1272        replace it with this old key. */
1273     if (!channel->internal.send_key) {
1274       silc_buffer_set(&keyp, tmp, len);
1275       silc_client_save_channel_key(client, conn, &keyp, channel);
1276     }
1277   }
1278
1279   /* Get topic */
1280   topic = silc_argument_get_arg_type(args, 10, NULL);
1281   if (topic) {
1282     silc_free(channel->topic);
1283     channel->topic = silc_memdup(topic, strlen(topic));
1284   }
1285
1286   /* Get founder key */
1287   tmp = silc_argument_get_arg_type(args, 15, &len);
1288   if (tmp) {
1289     if (channel->founder_key)
1290       silc_pkcs_public_key_free(channel->founder_key);
1291     channel->founder_key = NULL;
1292     silc_public_key_payload_decode(tmp, len, &channel->founder_key);
1293   }
1294
1295   /* Get user limit */
1296   tmp = silc_argument_get_arg_type(args, 17, &len);
1297   if (tmp && len == 4)
1298     SILC_GET32_MSB(channel->user_limit, tmp);
1299   if (!(channel->mode & SILC_CHANNEL_MODE_ULIMIT))
1300     channel->user_limit = 0;
1301
1302   /* Get channel public key list */
1303   tmp = silc_argument_get_arg_type(args, 16, &len);
1304   if (tmp)
1305     silc_client_channel_save_public_keys(channel, tmp, len, FALSE);
1306
1307   /* Set current channel */
1308   conn->current_channel = channel;
1309
1310   silc_rwlock_unlock(channel->internal.lock);
1311
1312   cipher = (channel->internal.send_key ?
1313             silc_cipher_get_name(channel->internal.send_key) : NULL);
1314   silc_hash_table_list(channel->user_list, &htl);
1315
1316   /* Notify application */
1317   silc_client_command_callback(cmd, channel->channel_name, channel, mode, &htl,
1318                                topic, cipher, hmac, channel->founder_key,
1319                                channel->channel_pubkeys, channel->user_limit);
1320
1321   silc_hash_table_list_reset(&htl);
1322   silc_client_unref_channel(client, conn, channel);
1323
1324  out:
1325   silc_fsm_next(fsm, silc_client_command_reply_processed);
1326   return SILC_FSM_CONTINUE;
1327 }
1328
1329 /********************************** MOTD ************************************/
1330
1331 /* Received reply for MOTD command */
1332
1333 SILC_FSM_STATE(silc_client_command_reply_motd)
1334 {
1335   SilcClientCommandContext cmd = fsm_context;
1336   SilcClientConnection conn = cmd->conn;
1337   SilcClient client = conn->client;
1338   SilcCommandPayload payload = state_context;
1339   SilcArgumentPayload args = silc_command_get_args(payload);
1340   SilcUInt32 i;
1341   char *motd = NULL, *cp, line[256];
1342
1343   /* Sanity checks */
1344   CHECK_STATUS("Cannot get motd: ");
1345   CHECK_ARGS(2, 3);
1346
1347   if (silc_argument_get_arg_num(args) == 3) {
1348     motd = silc_argument_get_arg_type(args, 3, NULL);
1349     if (!motd) {
1350       ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1351       goto out;
1352     }
1353
1354     i = 0;
1355     cp = motd;
1356     while(cp[i] != 0) {
1357       if (cp[i++] == '\n') {
1358         memset(line, 0, sizeof(line));
1359         silc_strncat(line, sizeof(line), cp, i - 1);
1360         cp += i;
1361
1362         if (i == 2)
1363           line[0] = ' ';
1364
1365         if (cmd->verbose)
1366           SAY(client, conn, SILC_CLIENT_MESSAGE_INFO, "%s", line);
1367
1368         if (!strlen(cp))
1369           break;
1370         i = 0;
1371       }
1372     }
1373   }
1374
1375   /* Notify application */
1376   silc_client_command_callback(cmd, motd);
1377
1378  out:
1379   silc_fsm_next(fsm, silc_client_command_reply_processed);
1380   return SILC_FSM_CONTINUE;
1381 }
1382
1383 /********************************** UMODE ***********************************/
1384
1385 /* Received reply to the UMODE command. Save the current user mode */
1386
1387 SILC_FSM_STATE(silc_client_command_reply_umode)
1388 {
1389   SilcClientCommandContext cmd = fsm_context;
1390   SilcClientConnection conn = cmd->conn;
1391   SilcCommandPayload payload = state_context;
1392   SilcArgumentPayload args = silc_command_get_args(payload);
1393   unsigned char *tmp;
1394   SilcUInt32 mode, len;
1395
1396   /* Sanity checks */
1397   CHECK_STATUS("Cannot change mode: ");
1398   CHECK_ARGS(2, 2);
1399
1400   tmp = silc_argument_get_arg_type(args, 2, &len);
1401   if (!tmp || len != 4) {
1402     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1403     goto out;
1404   }
1405
1406   SILC_GET32_MSB(mode, tmp);
1407   silc_rwlock_wrlock(conn->local_entry->internal.lock);
1408   conn->local_entry->mode = mode;
1409   silc_rwlock_unlock(conn->local_entry->internal.lock);
1410
1411   /* Notify application */
1412   silc_client_command_callback(cmd, mode);
1413
1414  out:
1415   silc_fsm_next(fsm, silc_client_command_reply_processed);
1416   return SILC_FSM_CONTINUE;
1417 }
1418
1419 /********************************** CMODE ***********************************/
1420
1421 /* Received reply for CMODE command. */
1422
1423 SILC_FSM_STATE(silc_client_command_reply_cmode)
1424 {
1425   SilcClientCommandContext cmd = fsm_context;
1426   SilcClientConnection conn = cmd->conn;
1427   SilcClient client = conn->client;
1428   SilcCommandPayload payload = state_context;
1429   SilcArgumentPayload args = silc_command_get_args(payload);
1430   unsigned char *tmp;
1431   SilcUInt32 mode;
1432   SilcChannelEntry channel = NULL;
1433   SilcUInt32 len;
1434   SilcPublicKey public_key = NULL;
1435   SilcID id;
1436
1437   /* Sanity checks */
1438   CHECK_STATUS("Cannot change mode: ");
1439   CHECK_ARGS(3, 6);
1440
1441   /* Take Channel ID */
1442   if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL)) {
1443     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1444     goto out;
1445   }
1446
1447   /* Get the channel entry */
1448   channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
1449   if (!channel) {
1450     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1451     goto out;
1452   }
1453
1454   /* Get founder public key */
1455   tmp = silc_argument_get_arg_type(args, 4, &len);
1456   if (tmp)
1457     silc_public_key_payload_decode(tmp, len, &public_key);
1458
1459   /* Get channel mode */
1460   tmp = silc_argument_get_arg_type(args, 3, &len);
1461   if (!tmp || len != 4) {
1462     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1463     goto out;
1464   }
1465   SILC_GET32_MSB(mode, tmp);
1466
1467   silc_rwlock_wrlock(channel->internal.lock);
1468
1469   /* Get user limit */
1470   tmp = silc_argument_get_arg_type(args, 6, &len);
1471   if (tmp && len == 4)
1472     SILC_GET32_MSB(channel->user_limit, tmp);
1473   if (!(channel->mode & SILC_CHANNEL_MODE_ULIMIT))
1474     channel->user_limit = 0;
1475
1476   /* Get channel public key(s) */
1477   tmp = silc_argument_get_arg_type(args, 5, &len);
1478   if (tmp)
1479     silc_client_channel_save_public_keys(channel, tmp, len, FALSE);
1480   else if (channel->mode & SILC_CHANNEL_MODE_CHANNEL_AUTH)
1481     silc_client_channel_save_public_keys(channel, NULL, 0, TRUE);
1482
1483   /* Save the mode */
1484   channel->mode = mode;
1485
1486   silc_rwlock_unlock(channel->internal.lock);
1487
1488   /* Notify application */
1489   silc_client_command_callback(cmd, channel, mode, public_key,
1490                                channel->channel_pubkeys, channel->user_limit);
1491
1492  out:
1493   silc_client_unref_channel(client, conn, channel);
1494   if (public_key)
1495     silc_pkcs_public_key_free(public_key);
1496   silc_fsm_next(fsm, silc_client_command_reply_processed);
1497   return SILC_FSM_CONTINUE;
1498 }
1499
1500 /********************************** CUMODE **********************************/
1501
1502 /* Received reply for CUMODE command */
1503
1504 SILC_FSM_STATE(silc_client_command_reply_cumode)
1505 {
1506   SilcClientCommandContext cmd = fsm_context;
1507   SilcClientConnection conn = cmd->conn;
1508   SilcClient client = conn->client;
1509   SilcCommandPayload payload = state_context;
1510   SilcArgumentPayload args = silc_command_get_args(payload);
1511   SilcClientEntry client_entry;
1512   SilcChannelEntry channel = NULL;
1513   SilcChannelUser chu;
1514   unsigned char *modev;
1515   SilcUInt32 len, mode;
1516   SilcID id;
1517
1518   /* Sanity checks */
1519   CHECK_STATUS("Cannot change mode: ");
1520   CHECK_ARGS(4, 4);
1521
1522   /* Get channel mode */
1523   modev = silc_argument_get_arg_type(args, 2, &len);
1524   if (!modev || len != 4) {
1525     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1526     goto out;
1527   }
1528   SILC_GET32_MSB(mode, modev);
1529
1530   /* Take Channel ID */
1531   if (!silc_argument_get_decoded(args, 3, SILC_ARGUMENT_ID, &id, NULL)) {
1532     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1533     goto out;
1534   }
1535
1536   /* Get the channel entry */
1537   channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
1538   if (!channel) {
1539     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1540     goto out;
1541   }
1542
1543   /* Get Client ID */
1544   if (!silc_argument_get_decoded(args, 4, SILC_ARGUMENT_ID, &id, NULL)) {
1545     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1546     goto out;
1547   }
1548
1549   /* Get client entry */
1550   client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
1551   if (!client_entry) {
1552     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1553     goto out;
1554   }
1555
1556   /* Save the mode */
1557   silc_rwlock_wrlock(channel->internal.lock);
1558   chu = silc_client_on_channel(channel, client_entry);
1559   if (chu)
1560     chu->mode = mode;
1561   silc_rwlock_unlock(channel->internal.lock);
1562
1563   /* Notify application */
1564   silc_client_command_callback(cmd, mode, channel, client_entry);
1565
1566   silc_client_unref_client(client, conn, client_entry);
1567
1568  out:
1569   silc_client_unref_channel(client, conn, channel);
1570   silc_fsm_next(fsm, silc_client_command_reply_processed);
1571   return SILC_FSM_CONTINUE;
1572 }
1573
1574 /********************************** KICK ************************************/
1575
1576 SILC_FSM_STATE(silc_client_command_reply_kick)
1577 {
1578   SilcClientCommandContext cmd = fsm_context;
1579   SilcClientConnection conn = cmd->conn;
1580   SilcClient client = conn->client;
1581   SilcCommandPayload payload = state_context;
1582   SilcArgumentPayload args = silc_command_get_args(payload);
1583   SilcClientEntry client_entry;
1584   SilcChannelEntry channel = NULL;
1585   SilcID id;
1586
1587   /* Sanity checks */
1588   CHECK_STATUS("Cannot kick: ");
1589   CHECK_ARGS(3, 3);
1590
1591   /* Take Channel ID */
1592   if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL)) {
1593     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1594     goto out;
1595   }
1596
1597   /* Get the channel entry */
1598   channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
1599   if (!channel) {
1600     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1601     goto out;
1602   }
1603
1604   /* Get Client ID */
1605   if (!silc_argument_get_decoded(args, 3, SILC_ARGUMENT_ID, &id, NULL)) {
1606     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1607     goto out;
1608   }
1609
1610   /* Get client entry */
1611   client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
1612   if (!client_entry) {
1613     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1614     goto out;
1615   }
1616
1617   /* Notify application */
1618   silc_client_command_callback(cmd, channel, client_entry);
1619
1620   silc_client_unref_client(client, conn, client_entry);
1621
1622  out:
1623   silc_client_unref_channel(client, conn, channel);
1624   silc_fsm_next(fsm, silc_client_command_reply_processed);
1625   return SILC_FSM_CONTINUE;
1626 }
1627
1628 /******************************** SILCOPER **********************************/
1629
1630 SILC_FSM_STATE(silc_client_command_reply_silcoper)
1631 {
1632   SilcClientCommandContext cmd = fsm_context;
1633   SilcCommandPayload payload = state_context;
1634   SilcArgumentPayload args = silc_command_get_args(payload);
1635
1636   /* Sanity checks */
1637   CHECK_STATUS("Cannot change mode: ");
1638   CHECK_ARGS(1, 1);
1639
1640   /* Set user mode */
1641   cmd->conn->local_entry->mode |= SILC_UMODE_ROUTER_OPERATOR;
1642
1643   /* Notify application */
1644   silc_client_command_callback(cmd);
1645
1646   silc_fsm_next(fsm, silc_client_command_reply_processed);
1647   return SILC_FSM_CONTINUE;
1648 }
1649
1650 /********************************** OPER ************************************/
1651
1652 SILC_FSM_STATE(silc_client_command_reply_oper)
1653 {
1654   SilcClientCommandContext cmd = fsm_context;
1655   SilcCommandPayload payload = state_context;
1656   SilcArgumentPayload args = silc_command_get_args(payload);
1657
1658   /* Sanity checks */
1659   CHECK_STATUS("Cannot change mode: ");
1660   CHECK_ARGS(1, 1);
1661
1662   /* Set user mode */
1663   cmd->conn->local_entry->mode |= SILC_UMODE_SERVER_OPERATOR;
1664
1665   /* Notify application */
1666   silc_client_command_callback(cmd);
1667
1668   silc_fsm_next(fsm, silc_client_command_reply_processed);
1669   return SILC_FSM_CONTINUE;
1670 }
1671
1672 /********************************* DETACH ***********************************/
1673
1674 SILC_FSM_STATE(silc_client_command_reply_detach)
1675 {
1676   SilcClientCommandContext cmd = fsm_context;
1677   SilcClientConnection conn = cmd->conn;
1678   SilcClient client = conn->client;
1679   SilcCommandPayload payload = state_context;
1680   SilcArgumentPayload args = silc_command_get_args(payload);
1681   SilcBuffer detach;
1682
1683   /* Sanity checks */
1684   CHECK_STATUS("Cannot detach: ");
1685   CHECK_ARGS(1, 1);
1686
1687   /* Get detachment data */
1688   detach = silc_client_get_detach_data(client, conn);
1689   if (!detach) {
1690     ERROR_CALLBACK(SILC_STATUS_ERR_RESOURCE_LIMIT);
1691     goto out;
1692   }
1693
1694   /* Notify application */
1695   silc_client_command_callback(cmd, detach);
1696   silc_buffer_free(detach);
1697
1698  out:
1699   silc_fsm_next(fsm, silc_client_command_reply_processed);
1700   return SILC_FSM_CONTINUE;
1701 }
1702
1703 /********************************** WATCH ***********************************/
1704
1705 SILC_FSM_STATE(silc_client_command_reply_watch)
1706 {
1707   SilcClientCommandContext cmd = fsm_context;
1708   SilcCommandPayload payload = state_context;
1709   SilcArgumentPayload args = silc_command_get_args(payload);
1710
1711   /* Sanity checks */
1712   CHECK_STATUS("Cannot set watch: ");
1713   CHECK_ARGS(1, 1);
1714
1715   /* Notify application */
1716   silc_client_command_callback(cmd);
1717
1718   silc_fsm_next(fsm, silc_client_command_reply_processed);
1719   return SILC_FSM_CONTINUE;
1720 }
1721
1722 /*********************************** BAN ************************************/
1723
1724 SILC_FSM_STATE(silc_client_command_reply_ban)
1725 {
1726   SilcClientCommandContext cmd = fsm_context;
1727   SilcClientConnection conn = cmd->conn;
1728   SilcClient client = conn->client;
1729   SilcCommandPayload payload = state_context;
1730   SilcArgumentPayload args = silc_command_get_args(payload);
1731   SilcChannelEntry channel = NULL;
1732   unsigned char *tmp;
1733   SilcUInt32 len;
1734   SilcArgumentPayload invite_args = NULL;
1735   SilcID id;
1736
1737   /* Sanity checks */
1738   CHECK_STATUS("Cannot set ban: ");
1739   CHECK_ARGS(2, 3);
1740
1741   /* Take Channel ID */
1742   if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL)) {
1743     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1744     goto out;
1745   }
1746
1747   /* Get the channel entry */
1748   channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
1749   if (!channel) {
1750     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1751     goto out;
1752   }
1753
1754   /* Get the invite list */
1755   tmp = silc_argument_get_arg_type(args, 3, &len);
1756   if (tmp)
1757     invite_args = silc_argument_list_parse(tmp, len);
1758
1759   /* Notify application */
1760   silc_client_command_callback(cmd, channel, invite_args);
1761
1762   if (invite_args)
1763     silc_argument_payload_free(invite_args);
1764
1765  out:
1766   silc_client_unref_channel(client, conn, channel);
1767   silc_fsm_next(fsm, silc_client_command_reply_processed);
1768   return SILC_FSM_CONTINUE;
1769 }
1770
1771 /********************************** LEAVE ***********************************/
1772
1773 /* Reply to LEAVE command. */
1774
1775 SILC_FSM_STATE(silc_client_command_reply_leave)
1776 {
1777   SilcClientCommandContext cmd = fsm_context;
1778   SilcClientConnection conn = cmd->conn;
1779   SilcClient client = conn->client;
1780   SilcCommandPayload payload = state_context;
1781   SilcArgumentPayload args = silc_command_get_args(payload);
1782   SilcChannelEntry channel;
1783   SilcCipher key;
1784   SilcHmac hmac;
1785   SilcID id;
1786
1787   /* Sanity checks */
1788   CHECK_STATUS("Cannot set leave: ");
1789   CHECK_ARGS(2, 2);
1790
1791   /* Get Channel ID */
1792   if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL)) {
1793     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1794     goto out;
1795   }
1796
1797   /* Get the channel entry */
1798   channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
1799   if (!channel) {
1800     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1801     goto out;
1802   }
1803
1804   /* Remove us from this channel. */
1805   silc_client_remove_from_channel(client, conn, channel, conn->local_entry);
1806
1807   /* Notify application */
1808   silc_client_command_callback(cmd, channel);
1809
1810   /* Remove old keys and stuff.  The channel may remain even after leaving
1811      but we want to remove these always. */
1812   if (channel->internal.send_key)
1813     silc_cipher_free(channel->internal.send_key);
1814   channel->internal.send_key = NULL;
1815   if (channel->internal.receive_key)
1816     silc_cipher_free(channel->internal.receive_key);
1817   channel->internal.receive_key = NULL;
1818   if (channel->internal.hmac)
1819     silc_hmac_free(channel->internal.hmac);
1820   channel->internal.hmac = NULL;
1821   if (channel->internal.old_channel_keys) {
1822     silc_dlist_start(channel->internal.old_channel_keys);
1823     while ((key = silc_dlist_get(channel->internal.old_channel_keys)))
1824       silc_cipher_free(key);
1825     silc_dlist_uninit(channel->internal.old_channel_keys);
1826   }
1827   channel->internal.old_channel_keys = NULL;
1828   if (channel->internal.old_hmacs) {
1829     silc_dlist_start(channel->internal.old_hmacs);
1830     while ((hmac = silc_dlist_get(channel->internal.old_hmacs)))
1831       silc_hmac_free(hmac);
1832     silc_dlist_uninit(channel->internal.old_hmacs);
1833   }
1834   channel->internal.old_hmacs = NULL;
1835
1836   /* Now delete the channel. */
1837   silc_client_empty_channel(client, conn, channel);
1838   silc_client_del_channel(client, conn, channel);
1839
1840  out:
1841   silc_fsm_next(fsm, silc_client_command_reply_processed);
1842   return SILC_FSM_CONTINUE;
1843 }
1844
1845 /********************************* USERS ************************************/
1846
1847 /* Continue USERS command reply processing after resolving unknown users */
1848
1849 static void
1850 silc_client_command_reply_users_resolved(SilcClient client,
1851                                          SilcClientConnection conn,
1852                                          SilcStatus status,
1853                                          SilcDList clients,
1854                                          void *context)
1855 {
1856   SilcClientCommandContext cmd = context;
1857   SILC_FSM_CALL_CONTINUE(&cmd->thread);
1858 }
1859
1860
1861 /* Continue USERS command after resolving unknown channel */
1862
1863 static void
1864 silc_client_command_reply_users_continue(SilcClient client,
1865                                          SilcClientConnection conn,
1866                                          SilcStatus status,
1867                                          SilcDList channels,
1868                                          void *context)
1869 {
1870   SilcClientCommandContext cmd = context;
1871
1872   if (!channels) {
1873     SilcCommandPayload payload = silc_fsm_get_state_context(&cmd->thread);
1874     SilcArgumentPayload args = silc_command_get_args(payload);
1875
1876     cmd->status = SILC_STATUS_ERR_NO_SUCH_CHANNEL_ID;
1877     ERROR_CALLBACK(cmd->status);
1878     silc_fsm_next(&cmd->thread, silc_client_command_reply_processed);
1879   }
1880
1881   SILC_FSM_CALL_CONTINUE(&cmd->thread);
1882 }
1883
1884 /* Reply to USERS command. Received list of client ID's and theirs modes
1885    on the channel we requested. */
1886
1887 SILC_FSM_STATE(silc_client_command_reply_users)
1888 {
1889   SilcClientCommandContext cmd = fsm_context;
1890   SilcClientConnection conn = cmd->conn;
1891   SilcClient client = conn->client;
1892   SilcCommandPayload payload = state_context;
1893   SilcArgumentPayload args = silc_command_get_args(payload);
1894   unsigned char *tmp;
1895   SilcUInt32 tmp_len, list_count, mode;
1896   SilcUInt16 idp_len;
1897   SilcHashTableList htl;
1898   SilcBufferStruct client_id_list, client_mode_list;
1899   SilcChannelEntry channel = NULL;
1900   SilcClientEntry client_entry;
1901   SilcID id;
1902   int i;
1903
1904   /* Sanity checks */
1905   CHECK_STATUS("Cannot get users: ");
1906   CHECK_ARGS(5, 5);
1907
1908   /* Get channel ID */
1909   if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL)) {
1910     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1911     goto out;
1912   }
1913
1914   /* Get channel entry */
1915   channel = silc_client_get_channel_by_id(client, conn, &id.u.channel_id);
1916   if (!channel) {
1917     /* Resolve the channel from server */
1918     SILC_FSM_CALL(silc_client_get_channel_by_id_resolve(
1919                         client, conn, &id.u.channel_id,
1920                         silc_client_command_reply_users_continue, cmd));
1921     /* NOT REACHED */
1922   }
1923
1924   /* Get the list count */
1925   tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
1926   if (!tmp || tmp_len != 4) {
1927     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1928     goto out;
1929   }
1930   SILC_GET32_MSB(list_count, tmp);
1931
1932   /* Get Client ID list */
1933   tmp = silc_argument_get_arg_type(args, 4, &tmp_len);
1934   if (!tmp) {
1935     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1936     goto out;
1937   }
1938   silc_buffer_set(&client_id_list, tmp, tmp_len);
1939
1940   /* Resolve users we do not know about */
1941   if (!cmd->resolved) {
1942     cmd->resolved = TRUE;
1943     silc_client_unref_channel(client, conn, channel);
1944     SILC_FSM_CALL(silc_client_get_clients_by_list(
1945                           client, conn, list_count, &client_id_list,
1946                           silc_client_command_reply_users_resolved, cmd));
1947     /* NOT REACHED */
1948   }
1949
1950   /* Get client mode list */
1951   tmp = silc_argument_get_arg_type(args, 5, &tmp_len);
1952   if (!tmp) {
1953     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1954     goto out;
1955   }
1956   silc_buffer_set(&client_mode_list, tmp, tmp_len);
1957
1958   SILC_LOG_DEBUG(("channel %s, %d users", channel->channel_name, list_count));
1959
1960   silc_rwlock_wrlock(channel->internal.lock);
1961
1962   /* Cache the received Client ID's and modes. */
1963   for (i = 0; i < list_count; i++) {
1964     SILC_GET16_MSB(idp_len, client_id_list.data + 2);
1965     idp_len += 4;
1966     if (!silc_id_payload_parse_id(client_id_list.data, idp_len, &id))
1967       goto out;
1968
1969     /* Mode */
1970     SILC_GET32_MSB(mode, client_mode_list.data);
1971
1972     /* Save the client on this channel.  Unknown clients are ignored as they
1973        clearly do not exist since the resolving didn't find them. */
1974     client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
1975     if (client_entry && client_entry->internal.valid) {
1976       silc_rwlock_wrlock(client_entry->internal.lock);
1977       silc_client_add_to_channel(client, conn, channel, client_entry, mode);
1978       silc_rwlock_unlock(client_entry->internal.lock);
1979     }
1980     silc_client_unref_client(client, conn, client_entry);
1981
1982     if (!silc_buffer_pull(&client_id_list, idp_len)) {
1983       silc_rwlock_unlock(channel->internal.lock);
1984       goto out;
1985     }
1986     if (!silc_buffer_pull(&client_mode_list, 4)) {
1987       silc_rwlock_unlock(channel->internal.lock);
1988       goto out;
1989     }
1990   }
1991
1992   silc_rwlock_unlock(channel->internal.lock);
1993
1994   /* Notify application */
1995   silc_hash_table_list(channel->user_list, &htl);
1996   silc_client_command_callback(cmd, channel, &htl);
1997   silc_hash_table_list_reset(&htl);
1998
1999  out:
2000   silc_client_unref_channel(client, conn, channel);
2001   silc_fsm_next(fsm, silc_client_command_reply_processed);
2002   return SILC_FSM_CONTINUE;
2003 }
2004
2005 /********************************** GETKEY **********************************/
2006
2007 /* Received command reply to GETKEY command. WE've received the remote
2008    client's public key. */
2009
2010 SILC_FSM_STATE(silc_client_command_reply_getkey)
2011 {
2012   SilcClientCommandContext cmd = fsm_context;
2013   SilcClientConnection conn = cmd->conn;
2014   SilcClient client = conn->client;
2015   SilcCommandPayload payload = state_context;
2016   SilcArgumentPayload args = silc_command_get_args(payload);
2017   SilcClientEntry client_entry;
2018   SilcServerEntry server_entry;
2019   unsigned char *tmp;
2020   SilcUInt32 len;
2021   SilcPublicKey public_key;
2022   SilcID id;
2023
2024   /* Sanity checks */
2025   CHECK_STATUS("Cannot get key: ");
2026   CHECK_ARGS(2, 3);
2027
2028   /* Get the ID */
2029   if (!silc_argument_get_decoded(args, 2, SILC_ARGUMENT_ID, &id, NULL)) {
2030     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2031     goto out;
2032   }
2033
2034   /* Get the public key */
2035   tmp = silc_argument_get_arg_type(args, 3, &len);
2036   if (!tmp) {
2037     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2038     goto out;
2039   }
2040   if (!silc_public_key_payload_decode(tmp, len, &public_key)) {
2041     SAY(client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
2042         "Cannot decode public key: malformed/unsupported public key");
2043     ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2044     goto out;
2045   }
2046
2047   if (id.type == SILC_ID_CLIENT) {
2048     /* Received client's public key */
2049     client_entry = silc_client_get_client_by_id(client, conn, &id.u.client_id);
2050     if (!client_entry) {
2051       ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2052       goto out;
2053     }
2054
2055     silc_rwlock_wrlock(client_entry->internal.lock);
2056
2057     /* Save fingerprint */
2058     if (!client_entry->fingerprint)
2059       silc_hash_make(conn->internal->sha1hash, tmp + 4, len - 4,
2060                      client_entry->fingerprint);
2061     if (!client_entry->public_key) {
2062       client_entry->public_key = public_key;
2063       public_key = NULL;
2064     }
2065
2066     silc_rwlock_unlock(client_entry->internal.lock);
2067
2068     /* Notify application */
2069     silc_client_command_callback(cmd, SILC_ID_CLIENT, client_entry,
2070                                  client_entry->public_key);
2071     silc_client_unref_client(client, conn, client_entry);
2072   } else if (id.type == SILC_ID_SERVER) {
2073     /* Received server's public key */
2074     server_entry = silc_client_get_server_by_id(client, conn, &id.u.server_id);
2075     if (!server_entry) {
2076       ERROR_CALLBACK(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2077       goto out;
2078     }
2079
2080     silc_rwlock_wrlock(server_entry->internal.lock);
2081
2082     if (!server_entry->public_key) {
2083       server_entry->public_key = public_key;
2084       public_key = NULL;
2085     }
2086
2087     silc_rwlock_unlock(server_entry->internal.lock);
2088
2089     /* Notify application */
2090     silc_client_command_callback(cmd, SILC_ID_SERVER, server_entry,
2091                                  server_entry->public_key);
2092     silc_client_unref_server(client, conn, server_entry);
2093   }
2094
2095  out:
2096   if (public_key)
2097     silc_pkcs_public_key_free(public_key);
2098   silc_fsm_next(fsm, silc_client_command_reply_processed);
2099   return SILC_FSM_CONTINUE;
2100 }
2101
2102 /********************************** SERVICE *********************************/
2103
2104 /* Reply to SERVICE command. */
2105 /* XXX incomplete */
2106
2107 SILC_FSM_STATE(silc_client_command_reply_service)
2108 {
2109   SilcClientCommandContext cmd = fsm_context;
2110   SilcCommandPayload payload = state_context;
2111   SilcArgumentPayload args = silc_command_get_args(payload);
2112   SilcUInt32 tmp_len;
2113   unsigned char *service_list, *name;
2114
2115   /* Sanity checks */
2116   CHECK_STATUS("Cannot get service: ");
2117
2118   /* Get service list */
2119   service_list = silc_argument_get_arg_type(args, 2, &tmp_len);
2120
2121   /* Get requested service name */
2122   name = silc_argument_get_arg_type(args, 3, &tmp_len);
2123
2124   /* Notify application */
2125   silc_client_command_callback(cmd, service_list, name);
2126
2127   silc_fsm_next(fsm, silc_client_command_reply_processed);
2128   return SILC_FSM_CONTINUE;
2129 }
2130
2131 /*********************************** QUIT ***********************************/
2132
2133 /* QUIT command reply stub */
2134
2135 SILC_FSM_STATE(silc_client_command_reply_quit)
2136 {
2137   silc_fsm_next(fsm, silc_client_command_reply_processed);
2138   return SILC_FSM_CONTINUE;
2139 }