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