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