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