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