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