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