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