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