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