More client library rewrites.
[silc.git] / lib / silcclient / command.c
1 /*
2
3   command.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 /* Command operation that is called at the end of all commands.
28    Usage: COMMAND(status); */
29 #define COMMAND(status) cmd->conn->client->internal->ops->command(      \
30   cmd->conn->client, cmd->conn, TRUE, cmd->cmd, (status), cmd->argc, cmd->argv)
31
32 /* Error to application. Usage: COMMAND_ERROR(status); */
33 #define COMMAND_ERROR(status)                                   \
34   cmd->conn->client->internal->ops->command(cmd->conn->client,  \
35   cmd->conn, FALSE, cmd->cmd, (status), cmd->argc, cmd->argv)
36
37 /* Used to register new command */
38 #define SILC_CLIENT_CMD(func, cmd, name, args)                          \
39 silc_client_command_register(client, SILC_COMMAND_##cmd, name,          \
40                              silc_client_command_##func,                \
41                              silc_client_command_reply_##func, args)
42
43 /* Used to unregister command */
44 #define SILC_CLIENT_CMDU(func, cmd, name)                               \
45 silc_client_command_unregister(client, SILC_COMMAND_##cmd,              \
46                                silc_client_command_##func,              \
47                                silc_client_command_reply_##func)
48
49 #define SAY cmd->conn->client->internal->ops->say
50
51 /************************ Static utility functions **************************/
52
53 /* Return next available command identifier. */
54
55 static SilcUInt16 silc_client_cmd_ident(SilcClientConnection conn)
56 {
57   SilcUInt16 cmd_ident;
58
59   cmd_ident = silc_atomic_add_int16(&conn->internal->cmd_ident, 1);
60   if (!cmd_ident)
61     cmd_ident = silc_atomic_add_int16(&conn->internal->cmd_ident, 1);
62
63   return cmd_ident;
64 }
65
66 /* State to finish command thread after an error in resolving command */
67
68 SILC_FSM_STATE(silc_client_command_continue_error)
69 {
70   /* Destructor will free all resources */
71   return SILC_FSM_FINISH;
72 }
73
74 /* Command reply callback to continue with the execution of a command.
75    This will continue when first successful reply is received, and ignores
76    the rest.  On the other hand, if only errors are received it will
77    wait for all errors before continuing. */
78
79 static SilcBool silc_client_command_continue(SilcClient client,
80                                              SilcClientConnection conn,
81                                              SilcCommand command,
82                                              SilcStatus status,
83                                              SilcStatus error,
84                                              void *context,
85                                              va_list ap)
86 {
87   SilcClientCommandContext cmd = context;
88
89   /* Continue immediately when successful reply is received */
90   if (status == SILC_STATUS_OK || !SILC_STATUS_IS_ERROR(error)) {
91     SILC_FSM_CALL_CONTINUE(&cmd->thread);
92     return FALSE;
93   }
94
95   /* Error */
96   COMMAND_ERROR(error);
97
98   /* Continue after last error is received */
99   if (SILC_STATUS_IS_ERROR(status) ||
100       (status == SILC_STATUS_LIST_END && SILC_STATUS_IS_ERROR(error))) {
101     silc_fsm_next(&cmd->thread, silc_client_command_continue_error);
102     SILC_FSM_CALL_CONTINUE(&cmd->thread);
103     return FALSE;
104   }
105
106   return TRUE;
107 }
108
109 /* Continues after resolving completed. */
110
111 static void silc_client_command_resolve_continue(SilcClient client,
112                                                  SilcClientConnection conn,
113                                                  SilcStatus status,
114                                                  SilcDList clients,
115                                                  void *context)
116 {
117   SilcClientCommandContext cmd = context;
118
119   if (status != SILC_STATUS_OK)
120     silc_fsm_next(&cmd->thread, silc_client_command_continue_error);
121
122   if (clients)
123     silc_dlist_uninit(clients);
124
125   /* Continue with the command */
126   SILC_FSM_CALL_CONTINUE(&cmd->thread);
127 }
128
129 /* Register command to client */
130
131 static SilcBool
132 silc_client_command_register(SilcClient client,
133                              SilcCommand command,
134                              const char *name,
135                              SilcFSMStateCallback command_func,
136                              SilcFSMStateCallback command_reply_func,
137                              SilcUInt8 max_args)
138 {
139   SilcClientCommand cmd;
140
141   cmd = silc_calloc(1, sizeof(*cmd));
142   cmd->cmd = command;
143   cmd->command = command_func;
144   cmd->reply = command_reply_func;
145   cmd->name = name ? strdup(name) : NULL;
146   cmd->max_args = max_args;
147
148   silc_list_add(client->internal->commands, cmd);
149
150   return TRUE;
151 }
152
153 /* Unregister command from client */
154
155 static SilcBool
156 silc_client_command_unregister(SilcClient client,
157                                SilcCommand command,
158                                SilcFSMStateCallback command_func,
159                                SilcFSMStateCallback command_reply_func)
160 {
161   SilcClientCommand cmd;
162
163   silc_list_start(client->internal->commands);
164   while ((cmd = silc_list_get(client->internal->commands)) != SILC_LIST_END) {
165     if (cmd->cmd == command && cmd->command == command_func &&
166         cmd->reply == command_reply_func) {
167       silc_list_del(client->internal->commands, cmd);
168       silc_free(cmd->name);
169       silc_free(cmd);
170       return TRUE;
171     }
172   }
173
174   return FALSE;
175 }
176
177 /* Finds and returns a pointer to the command list. Return NULL if the
178    command is not found. */
179
180 static SilcClientCommand silc_client_command_find(SilcClient client,
181                                                   const char *name)
182 {
183   SilcClientCommand cmd;
184
185   silc_list_start(client->internal->commands);
186   while ((cmd = silc_list_get(client->internal->commands)) != SILC_LIST_END) {
187     if (cmd->name && !strcasecmp(cmd->name, name))
188       return cmd;
189   }
190
191   return NULL;
192 }
193
194 /* Free command context and its internals */
195
196 static void silc_client_command_free(SilcClientCommandContext cmd)
197 {
198   int i;
199
200   for (i = 0; i < cmd->argc; i++)
201     silc_free(cmd->argv[i]);
202   silc_free(cmd->argv);
203   silc_free(cmd->argv_lens);
204   silc_free(cmd->argv_types);
205   silc_free(cmd);
206 }
207
208 /* Command thread destructor */
209
210 static void silc_client_command_destructor(SilcFSMThread thread,
211                                            void *fsm_context,
212                                            void *destructor_context)
213 {
214   silc_client_command_free(fsm_context);
215 }
216
217 /* Add a command pending a command reply.  Used internally by the library. */
218
219 static SilcBool
220 silc_client_command_add_pending(SilcClientConnection conn,
221                                 SilcClientCommandContext cmd,
222                                 SilcClientCommandReply reply,
223                                 void *context)
224 {
225   SilcClientCommandReplyCallback cb;
226
227   silc_mutex_lock(conn->internal->lock);
228
229   /* Add pending callback, if defined */
230   if (reply) {
231     cb = silc_calloc(1, sizeof(*cb));
232     if (!cb) {
233       silc_mutex_unlock(conn->internal->lock);
234       return FALSE;
235     }
236     cb->reply = reply;
237     cb->context = context;
238     silc_list_add(cmd->reply_callbacks, cb);
239   }
240
241   /* Add pending reply */
242   silc_list_add(conn->internal->pending_commands, cmd);
243
244   silc_mutex_unlock(conn->internal->lock);
245
246   return TRUE;
247 }
248
249 /* Generic function to send any command. The arguments must be sent already
250    encoded into correct format and in correct order.  Arguments come from
251    variable argument list pointer. */
252
253 static SilcUInt16 silc_client_command_send_vap(SilcClient client,
254                                                SilcClientConnection conn,
255                                                SilcClientCommandContext cmd,
256                                                SilcCommand command,
257                                                SilcClientCommandReply reply,
258                                                void *reply_context,
259                                                SilcUInt32 argc, va_list ap)
260 {
261   SilcBuffer packet;
262
263   SILC_LOG_DEBUG(("Send command %s", silc_get_command_name(command)));
264
265   if (!cmd->cmd_ident)
266     cmd->cmd_ident = silc_client_cmd_ident(conn);
267
268   /* Encode command payload */
269   packet = silc_command_payload_encode_vap(command, cmd->cmd_ident, argc, ap);
270   if (!packet)
271     return 0;
272
273   /* Send the command */
274   if (!silc_packet_send(conn->stream, SILC_PACKET_COMMAND, 0,
275                         silc_buffer_datalen(packet))) {
276     silc_buffer_free(packet);
277     return 0;
278   }
279
280   /* Add the command pending command reply */
281   silc_client_command_add_pending(conn, cmd, reply, reply_context);
282
283   silc_buffer_free(packet);
284
285   return cmd->cmd_ident;
286 }
287
288 /* Generic function to send any command. The arguments must be sent already
289    encoded into correct format and in correct order.  Arguments come from
290    arrays. */
291
292 static SilcUInt16
293 silc_client_command_send_arg_array(SilcClient client,
294                                    SilcClientConnection conn,
295                                    SilcClientCommandContext cmd,
296                                    SilcCommand command,
297                                    SilcClientCommandReply reply,
298                                    void *reply_context,
299                                    SilcUInt32 argc,
300                                    unsigned char **argv,
301                                    SilcUInt32 *argv_lens,
302                                    SilcUInt32 *argv_types)
303 {
304   SilcBuffer packet;
305
306   SILC_LOG_DEBUG(("Send command %s", silc_get_command_name(command)));
307
308   if (!cmd->cmd_ident)
309     cmd->cmd_ident = silc_client_cmd_ident(conn);
310
311   /* Encode command payload */
312   packet = silc_command_payload_encode(command, argc, argv, argv_lens,
313                                        argv_types, cmd->cmd_ident);
314   if (!packet)
315     return 0;
316
317   /* Send the command */
318   if (!silc_packet_send(conn->stream, SILC_PACKET_COMMAND, 0,
319                         silc_buffer_datalen(packet))) {
320     silc_buffer_free(packet);
321     return 0;
322   }
323
324   /* Add the command pending command reply */
325   silc_client_command_add_pending(conn, cmd, reply, reply_context);
326
327   silc_buffer_free(packet);
328
329   return cmd->cmd_ident;
330 }
331
332 /* Generic function to send any command. The arguments must be sent already
333    encoded into correct format and in correct order.  This is used internally
334    by the library.  */
335
336 static SilcUInt16 silc_client_command_send_va(SilcClientConnection conn,
337                                               SilcClientCommandContext cmd,
338                                               SilcCommand command,
339                                               SilcClientCommandReply reply,
340                                               void *reply_context,
341                                               SilcUInt32 argc, ...)
342 {
343   va_list ap;
344   SilcUInt16 cmd_ident;
345
346   va_start(ap, argc);
347   cmd_ident = silc_client_command_send_vap(conn->client, conn, cmd, command,
348                                            reply, reply_context, argc, ap);
349   va_end(ap);
350
351   return cmd_ident;
352 }
353
354 /****************************** Command API *********************************/
355
356 /* Executes a command */
357
358 SilcUInt16 silc_client_command_call(SilcClient client,
359                                     SilcClientConnection conn,
360                                     const char *command_line, ...)
361 {
362   va_list va;
363   SilcUInt32 argc = 0;
364   unsigned char **argv = NULL;
365   SilcUInt32 *argv_lens = NULL, *argv_types = NULL;
366   SilcClientCommand command;
367   SilcClientCommandContext cmd;
368   char *arg;
369
370   if (!conn) {
371     client->internal->ops->say(client, NULL, SILC_CLIENT_MESSAGE_ERROR,
372       "You are not connected to a server, please connect to server");
373     return 0;
374   }
375
376   /* Parse arguments */
377   va_start(va, command_line);
378   if (command_line) {
379     char *command_name;
380
381     /* Get command name */
382     command_name = silc_memdup(command_line, strcspn(command_line, " "));
383     if (!command_name)
384       return 0;
385
386     /* Find command by name */
387     command = silc_client_command_find(client, command_name);
388     if (!command) {
389       silc_free(command_name);
390       return 0;
391     }
392
393     /* Parse command line */
394     silc_parse_command_line((char *)command_line, &argv, &argv_lens,
395                             &argv_types, &argc, command->max_args);
396
397     silc_free(command_name);
398   } else {
399     arg = va_arg(va, char *);
400     if (!arg)
401       return 0;
402
403     /* Find command by name */
404     command = silc_client_command_find(client, arg);
405     if (!command)
406       return 0;
407
408     while (arg) {
409       argv = silc_realloc(argv, sizeof(*argv) * (argc + 1));
410       argv_lens = silc_realloc(argv_lens, sizeof(*argv_lens) * (argc + 1));
411       argv_types = silc_realloc(argv_types, sizeof(*argv_types) * (argc + 1));
412       if (!argv || !argv_lens || !argv_types)
413         return 0;
414       argv[argc] = silc_memdup(arg, strlen(arg));
415       if (!argv[argc])
416         return 0;
417       argv_lens[argc] = strlen(arg);
418       argv_types[argc] = argc;
419       argc++;
420       arg = va_arg(va, char *);
421     }
422   }
423   va_end(va);
424
425   /* Allocate command context */
426   cmd = silc_calloc(1, sizeof(*cmd));
427   if (!cmd)
428     return 0;
429   cmd->conn = conn;
430   cmd->cmd = command->cmd;
431   cmd->argc = argc;
432   cmd->argv = argv;
433   cmd->argv_lens = argv_lens;
434   cmd->argv_types = argv_types;
435   cmd->cmd_ident = silc_client_cmd_ident(conn);
436   cmd->called = TRUE;
437   cmd->verbose = TRUE;
438
439   /*** Call command */
440   SILC_LOG_DEBUG(("Calling %s command", silc_get_command_name(cmd->cmd)));
441   silc_fsm_thread_init(&cmd->thread, &conn->internal->fsm, cmd,
442                        silc_client_command_destructor, NULL, FALSE);
443   silc_fsm_start_sync(&cmd->thread, command->command);
444
445   return cmd->cmd_ident;
446 }
447
448 /* Generic function to send any command. The arguments must be sent already
449    encoded into correct format and in correct order. */
450
451 SilcUInt16 silc_client_command_send(SilcClient client,
452                                     SilcClientConnection conn,
453                                     SilcCommand command,
454                                     SilcClientCommandReply reply,
455                                     void *reply_context,
456                                     SilcUInt32 argc, ...)
457 {
458   SilcClientCommandContext cmd;
459   va_list ap;
460
461   if (!conn || !reply)
462     return 0;
463
464   /* Allocate command context */
465   cmd = silc_calloc(1, sizeof(*cmd));
466   if (!cmd)
467     return 0;
468   cmd->conn = conn;
469   cmd->cmd = command;
470
471   /* Send the command */
472   va_start(ap, argc);
473   cmd->cmd_ident =
474     silc_client_command_send_vap(client, conn, cmd, command, reply,
475                                  reply_context, argc, ap);
476   va_end(ap);
477
478   if (!cmd->cmd_ident) {
479     silc_client_command_free(cmd);
480     return 0;
481   }
482
483   /*** Wait for command reply */
484   silc_fsm_thread_init(&cmd->thread, &conn->internal->fsm, cmd,
485                        silc_client_command_destructor, NULL, FALSE);
486   silc_fsm_start_sync(&cmd->thread, silc_client_command_reply_wait);
487
488   return cmd->cmd_ident;
489 }
490
491 /* Generic function to send any command. The arguments must be sent already
492    encoded into correct format and in correct order.  Arguments come from
493    arrays. */
494
495 SilcUInt16 silc_client_command_send_argv(SilcClient client,
496                                          SilcClientConnection conn,
497                                          SilcCommand command,
498                                          SilcClientCommandReply reply,
499                                          void *reply_context,
500                                          SilcUInt32 argc,
501                                          unsigned char **argv,
502                                          SilcUInt32 *argv_lens,
503                                          SilcUInt32 *argv_types)
504 {
505   SilcClientCommandContext cmd;
506
507   if (!conn || !reply)
508     return 0;
509
510   /* Allocate command context */
511   cmd = silc_calloc(1, sizeof(*cmd));
512   if (!cmd)
513     return 0;
514   cmd->conn = conn;
515   cmd->cmd = command;
516
517   /* Send the command */
518   cmd->cmd_ident =
519     silc_client_command_send_arg_array(client, conn, cmd, command, reply,
520                                        reply_context, argc, argv, argv_lens,
521                                        argv_types);
522   if (!cmd->cmd_ident) {
523     silc_client_command_free(cmd);
524     return 0;
525   }
526
527   /*** Wait for command reply */
528   silc_fsm_thread_init(&cmd->thread, &conn->internal->fsm, cmd,
529                        silc_client_command_destructor, NULL, FALSE);
530   silc_fsm_start_sync(&cmd->thread, silc_client_command_reply_wait);
531
532   return cmd->cmd_ident;
533 }
534
535 /* Attach to a command and command identifier to receive command reply. */
536
537 SilcBool silc_client_command_pending(SilcClientConnection conn,
538                                      SilcCommand command,
539                                      SilcUInt16 ident,
540                                      SilcClientCommandReply reply,
541                                      void *context)
542 {
543   SilcClientCommandContext cmd;
544   SilcClientCommandReplyCallback cb;
545
546   if (!conn || !reply)
547     return FALSE;
548
549   SILC_LOG_DEBUG(("Add pending command reply for ident %d", ident));
550
551   silc_mutex_lock(conn->internal->lock);
552
553   /* Find the pending command */
554   silc_list_start(conn->internal->pending_commands);
555   while ((cmd = silc_list_get(conn->internal->pending_commands)))
556     if ((cmd->cmd == command || command == SILC_COMMAND_NONE)
557         && cmd->cmd_ident == ident) {
558
559       /* Add the callback */
560       cb = silc_calloc(1, sizeof(*cb));
561       if (!cb)
562         continue;
563       cb->reply = reply;
564       cb->context = context;
565       silc_list_add(cmd->reply_callbacks, cb);
566     }
567
568   silc_mutex_unlock(conn->internal->lock);
569
570   return TRUE;
571 }
572
573 /******************************** WHOIS *************************************/
574
575 /* Command WHOIS. This command is used to query information about
576    specific user. */
577
578 SILC_FSM_STATE(silc_client_command_whois)
579 {
580   SilcClientCommandContext cmd = fsm_context;
581   SilcClientConnection conn = cmd->conn;
582   SilcClient client = conn->client;
583   SilcBuffer attrs = NULL;
584   unsigned char count[4], *tmp = NULL;
585   SilcBool details = FALSE, nick = FALSE;
586   unsigned char *pubkey = NULL;
587   int i;
588
589   /* Given without arguments fetches client's own information */
590   if (cmd->argc < 2) {
591     silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1, 4,
592                                 silc_buffer_data(conn->internal->local_idp),
593                                 silc_buffer_len(conn->internal->local_idp));
594
595     /* Notify application */
596     COMMAND(SILC_STATUS_OK);
597
598     /** Wait for command reply */
599     silc_fsm_next(fsm, silc_client_command_reply_wait);
600     return SILC_FSM_CONTINUE;
601   }
602
603   for (i = 1; i < cmd->argc; i++) {
604     if (!strcasecmp(cmd->argv[i], "-details")) {
605       details = TRUE;
606     } else if (!strcasecmp(cmd->argv[i], "-pubkey") && cmd->argc > i + 1) {
607       pubkey = cmd->argv[i + 1];
608       i++;
609     } else {
610       /* We assume that the first parameter is the nickname, if it isn't
611          -details or -pubkey. The last parameter should always be the count */
612       if (i == 1) {
613         nick = TRUE;
614       } else if (i == cmd->argc - 1) {
615         int c = atoi(cmd->argv[i]);
616         SILC_PUT32_MSB(c, count);
617         tmp = count;
618       }
619     }
620   }
621
622   if (details) {
623     /* If pubkey is set, add all attributes to the attrs buffer, except
624        public key */
625     if (pubkey) {
626       attrs = silc_client_attributes_request(SILC_ATTRIBUTE_USER_INFO,
627                                              SILC_ATTRIBUTE_SERVICE,
628                                              SILC_ATTRIBUTE_STATUS_MOOD,
629                                              SILC_ATTRIBUTE_STATUS_FREETEXT,
630                                              SILC_ATTRIBUTE_STATUS_MESSAGE,
631                                              SILC_ATTRIBUTE_PREFERRED_LANGUAGE,
632                                              SILC_ATTRIBUTE_PREFERRED_CONTACT,
633                                              SILC_ATTRIBUTE_TIMEZONE,
634                                              SILC_ATTRIBUTE_GEOLOCATION,
635                                              SILC_ATTRIBUTE_DEVICE_INFO,
636                                              SILC_ATTRIBUTE_USER_ICON, 0);
637     } else {
638       attrs = silc_client_attributes_request(0);
639     }
640   }
641
642   if (pubkey) {
643     SilcAttributeObjPk obj;
644     SilcPublicKey pk;
645
646     if (!silc_pkcs_load_public_key(pubkey, &pk)) {
647       SAY(client, conn, SILC_CLIENT_MESSAGE_ERROR,
648           "Could not load public key %s, check the filename",
649           pubkey);
650       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
651       goto out;
652     }
653
654     switch (silc_pkcs_get_type(pk)) {
655     case SILC_PKCS_SILC:
656       obj.type = "silc-rsa";
657       break;
658     case SILC_PKCS_SSH2:
659       obj.type = "ssh-rsa";
660       break;
661     case SILC_PKCS_X509V3:
662       obj.type = "x509v3-sign-rsa";
663       break;
664     case SILC_PKCS_OPENPGP:
665       obj.type = "pgp-sign-rsa";
666       break;
667     default:
668       goto out;
669       break;
670     }
671     obj.data = silc_pkcs_public_key_encode(pk, &obj.data_len);
672
673     attrs = silc_attribute_payload_encode(attrs,
674                                           SILC_ATTRIBUTE_USER_PUBLIC_KEY,
675                                           SILC_ATTRIBUTE_FLAG_VALID,
676                                           &obj, sizeof(obj));
677   }
678
679   /* Send command */
680   silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
681                               3, 1, nick ? cmd->argv[1] : NULL,
682                               nick ? cmd->argv_lens[1] : 0,
683                               2, tmp ? tmp : NULL, tmp ? 4 : 0,
684                               3, silc_buffer_datalen(attrs));
685
686   /* Notify application */
687   COMMAND(SILC_STATUS_OK);
688
689   /** Wait for command reply */
690   silc_fsm_next(fsm, silc_client_command_reply_wait);
691   return SILC_FSM_CONTINUE;
692
693  out:
694   return SILC_FSM_FINISH;
695 }
696
697 /******************************** WHOWAS ************************************/
698
699 /* Command WHOWAS. This command is used to query history information about
700    specific user that used to exist in the network. */
701
702 SILC_FSM_STATE(silc_client_command_whowas)
703 {
704   SilcClientCommandContext cmd = fsm_context;
705   SilcClientConnection conn = cmd->conn;
706   unsigned char count[4];
707   int c;
708
709   if (cmd->argc < 2 || cmd->argc > 3) {
710     SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
711         "Usage: /WHOWAS <nickname>[@<server>] [<count>]");
712     COMMAND_ERROR((cmd->argc < 2 ? SILC_STATUS_ERR_NOT_ENOUGH_PARAMS :
713                    SILC_STATUS_ERR_TOO_MANY_PARAMS));
714     return SILC_FSM_FINISH;
715   }
716
717   if (cmd->argc == 2) {
718     silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
719                                 1, 1, cmd->argv[1], cmd->argv_lens[1]);
720   } else {
721     c = atoi(cmd->argv[2]);
722     SILC_PUT32_MSB(c, count);
723     silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
724                                 2, 1, cmd->argv[1], cmd->argv_lens[1],
725                                 2, count, sizeof(count));
726   }
727
728   /* Notify application */
729   COMMAND(SILC_STATUS_OK);
730
731   /** Wait for command reply */
732   silc_fsm_next(fsm, silc_client_command_reply_wait);
733   return SILC_FSM_CONTINUE;
734 }
735
736 /******************************** IDENTIFY **********************************/
737
738 /* Command IDENTIFY. This command is used to query information about
739    specific user, especially ID's. */
740
741 SILC_FSM_STATE(silc_client_command_identify)
742 {
743   SilcClientCommandContext cmd = fsm_context;
744   SilcClientConnection conn = cmd->conn;
745   unsigned char count[4];
746   int c;
747
748   if (cmd->argc < 2 || cmd->argc > 3)
749     return SILC_FSM_FINISH;
750
751   if (cmd->argc == 2) {
752     silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
753                                 1, 1, cmd->argv[1], cmd->argv_lens[1]);
754   } else {
755     c = atoi(cmd->argv[2]);
756     SILC_PUT32_MSB(c, count);
757     silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
758                                 2, 1, cmd->argv[1], cmd->argv_lens[1],
759                                 4, count, sizeof(count));
760   }
761
762   /** Wait for command reply */
763   silc_fsm_next(fsm, silc_client_command_reply_wait);
764   return SILC_FSM_CONTINUE;
765 }
766
767 /********************************** NICK ************************************/
768
769 /* Command NICK. Shows current nickname/sets new nickname on current
770    window. */
771
772 SILC_FSM_STATE(silc_client_command_nick)
773 {
774   SilcClientCommandContext cmd = fsm_context;
775   SilcClientConnection conn = cmd->conn;
776
777   if (cmd->argc < 2) {
778     SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
779         "Usage: /NICK <nickname>");
780     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
781     goto out;
782   }
783
784   if (silc_utf8_strcasecmp(conn->local_entry->nickname, cmd->argv[1]))
785     goto out;
786
787   /* Show current nickname */
788   if (cmd->argc < 2) {
789     if (cmd->conn) {
790       SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
791           "Your nickname is %s on server %s",
792           conn->local_entry->nickname, conn->remote_host);
793     } else {
794       SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
795           "Your nickname is %s", conn->local_entry->nickname);
796     }
797
798     COMMAND(SILC_STATUS_OK);
799     goto out;
800   }
801
802   if (cmd->argv_lens[1] > 128)
803     cmd->argv_lens[1] = 128;
804
805   /* Send the NICK command */
806   silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
807                               1, 1, cmd->argv[1], cmd->argv_lens[1]);
808
809   /* Notify application */
810   COMMAND(SILC_STATUS_OK);
811
812   /** Wait for command reply */
813   silc_fsm_next(fsm, silc_client_command_reply_wait);
814   return SILC_FSM_CONTINUE;
815
816  out:
817   return SILC_FSM_FINISH;
818 }
819
820 /********************************** LIST ************************************/
821
822 /* Command LIST. Lists channels on the current server. */
823
824 SILC_FSM_STATE(silc_client_command_list)
825 {
826   SilcClientCommandContext cmd = fsm_context;
827   SilcClientConnection conn = cmd->conn;
828   SilcChannelEntry channel;
829   SilcBuffer idp = NULL;
830
831   if (cmd->argc == 2) {
832     /* Get the Channel ID of the channel */
833     channel = silc_client_get_channel(conn->client, cmd->conn, cmd->argv[1]);
834     if (channel)
835       idp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
836   }
837
838   if (!idp)
839     silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 0);
840   else
841     silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
842                                 1, 1, silc_buffer_datalen(idp));
843
844   silc_buffer_free(idp);
845
846   /* Notify application */
847   COMMAND(SILC_STATUS_OK);
848
849   /** Wait for command reply */
850   silc_fsm_next(fsm, silc_client_command_reply_wait);
851   return SILC_FSM_CONTINUE;
852 }
853
854 /********************************** TOPIC ***********************************/
855
856 /* Command TOPIC. Sets/shows topic on a channel. */
857
858 SILC_FSM_STATE(silc_client_command_topic)
859 {
860   SilcClientCommandContext cmd = fsm_context;
861   SilcClientConnection conn = cmd->conn;
862   SilcChannelEntry channel;
863   SilcBuffer idp;
864   char *name;
865
866   if (cmd->argc < 2 || cmd->argc > 3) {
867     SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
868         "Usage: /TOPIC <channel> [<topic>]");
869     COMMAND_ERROR((cmd->argc < 2 ? SILC_STATUS_ERR_NOT_ENOUGH_PARAMS :
870                    SILC_STATUS_ERR_TOO_MANY_PARAMS));
871     goto out;
872   }
873
874   if (cmd->argv[1][0] == '*') {
875     if (!conn->current_channel) {
876       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
877       goto out;
878     }
879     name = conn->current_channel->channel_name;
880   } else {
881     name = cmd->argv[1];
882   }
883
884   if (!conn->current_channel) {
885     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
886     goto out;
887   }
888
889   /* Get the Channel ID of the channel */
890   channel = silc_client_get_channel(conn->client, conn, name);
891   if (!channel) {
892     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
893     goto out;
894   }
895
896   idp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
897
898   /* Send TOPIC command to the server */
899   if (cmd->argc > 2)
900     silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
901                                 1, silc_buffer_datalen(idp),
902                                 2, cmd->argv[2], strlen(cmd->argv[2]));
903   else
904     silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
905                                 1, silc_buffer_datalen(idp));
906
907   silc_buffer_free(idp);
908
909   /* Notify application */
910   COMMAND(SILC_STATUS_OK);
911
912   /** Wait for command reply */
913   silc_fsm_next(fsm, silc_client_command_reply_wait);
914   return SILC_FSM_CONTINUE;
915
916  out:
917   return SILC_FSM_FINISH;
918 }
919
920 /********************************* INVITE ***********************************/
921
922 /* Command INVITE. Invites specific client to join a channel. This is
923    also used to mange the invite list of the channel. */
924
925 SILC_FSM_STATE(silc_client_command_invite)
926 {
927   SilcClientCommandContext cmd = fsm_context;
928   SilcClientConnection conn = cmd->conn;
929   SilcClient client = conn->client;
930   SilcClientEntry client_entry = NULL;
931   SilcChannelEntry channel;
932   SilcBuffer clidp, chidp, args = NULL;
933   SilcPublicKey pubkey = NULL;
934   SilcDList clients = NULL;
935   char *nickname = NULL, *name;
936   char *invite = NULL;
937   unsigned char action[1];
938
939   if (cmd->argc < 2) {
940     SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
941         "Usage: /INVITE <channel> [<nickname>[@server>]"
942         "[+|-[<nickname>[@<server>[!<username>[@hostname>]]]]]");
943     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
944     goto out;
945   }
946
947   if (cmd->argv[1][0] == '*') {
948     if (!conn->current_channel) {
949       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
950       goto out;
951     }
952
953     channel = conn->current_channel;
954   } else {
955     name = cmd->argv[1];
956
957     channel = silc_client_get_channel(conn->client, conn, name);
958     if (!channel) {
959       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
960       goto out;
961     }
962   }
963
964   /* Parse the typed nickname. */
965   if (cmd->argc == 3) {
966     if (cmd->argv[2][0] != '+' && cmd->argv[2][0] != '-') {
967       if (client->internal->params->nickname_parse)
968         client->internal->params->nickname_parse(cmd->argv[2], &nickname);
969       else
970         nickname = strdup(cmd->argv[2]);
971
972       /* Find client entry */
973       clients = silc_client_get_clients_local(client, conn, nickname,
974                                               cmd->argv[2]);
975       if (!clients)
976         /* Resolve client information */
977         SILC_FSM_CALL(silc_client_get_clients(
978                                       client, conn, nickname,
979                                       cmd->argv[2],
980                                       silc_client_command_resolve_continue,
981                                       cmd));
982
983       client_entry = silc_dlist_get(clients);
984     } else {
985       if (cmd->argv[2][0] == '+')
986         action[0] = 0x00;
987       else
988         action[0] = 0x01;
989
990       /* Check if it is public key file to be added to invite list */
991       silc_pkcs_load_public_key(cmd->argv[2] + 1, &pubkey);
992       invite = cmd->argv[2];
993       if (!pubkey)
994         invite++;
995     }
996   }
997
998   if (invite) {
999     args = silc_buffer_alloc_size(2);
1000     silc_buffer_format(args,
1001                        SILC_STR_UI_SHORT(1),
1002                        SILC_STR_END);
1003     if (pubkey) {
1004       chidp = silc_public_key_payload_encode(pubkey);
1005       args = silc_argument_payload_encode_one(args, silc_buffer_data(chidp),
1006                                               silc_buffer_len(chidp), 2);
1007       silc_buffer_free(chidp);
1008       silc_pkcs_public_key_free(pubkey);
1009     } else {
1010       args = silc_argument_payload_encode_one(args, invite, strlen(invite), 1);
1011     }
1012   }
1013
1014   /* Send the command */
1015   chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
1016   if (client_entry) {
1017     clidp = silc_id_payload_encode(&client_entry->id, SILC_ID_CLIENT);
1018     silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 4,
1019                                 1, silc_buffer_datalen(chidp),
1020                                 2, silc_buffer_datalen(clidp),
1021                                 3, args ? action : NULL, args ? 1 : 0,
1022                                 4, silc_buffer_datalen(args));
1023     silc_buffer_free(clidp);
1024   } else {
1025     silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 3,
1026                                 1, silc_buffer_datalen(chidp),
1027                                 3, args ? action : NULL, args ? 1 : 0,
1028                                 4, silc_buffer_datalen(args));
1029   }
1030
1031   silc_buffer_free(chidp);
1032   silc_buffer_free(args);
1033   silc_free(nickname);
1034   silc_client_list_free(client, conn, clients);
1035
1036   /* Notify application */
1037   COMMAND(SILC_STATUS_OK);
1038
1039   /** Wait for command reply */
1040   silc_fsm_next(fsm, silc_client_command_reply_wait);
1041   return SILC_FSM_CONTINUE;
1042
1043  out:
1044   silc_free(nickname);
1045   return SILC_FSM_FINISH;
1046 }
1047
1048 /********************************** QUIT ************************************/
1049
1050 /* Close the connection */
1051
1052 SILC_FSM_STATE(silc_client_command_quit_final)
1053 {
1054   SilcClientCommandContext cmd = fsm_context;
1055   SilcClientConnection conn = cmd->conn;
1056   SilcClient client = conn->client;
1057
1058   /* Notify application */
1059   COMMAND(SILC_STATUS_OK);
1060
1061   /* Call connection callback */
1062   conn->callback(client, conn, SILC_CLIENT_CONN_DISCONNECTED,
1063                  0, NULL, conn->callback_context);
1064
1065   /* Signal to close connection */
1066   conn->internal->disconnected = TRUE;
1067   SILC_FSM_SEMA_POST(&conn->internal->wait_event);
1068
1069   return SILC_FSM_FINISH;
1070 }
1071
1072 /* Command QUIT. Closes connection with current server. */
1073
1074 SILC_FSM_STATE(silc_client_command_quit)
1075 {
1076   SilcClientCommandContext cmd = fsm_context;
1077   SilcClientConnection conn = cmd->conn;
1078
1079   if (cmd->argc > 1)
1080     silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1081                                 1, cmd->argv[1], cmd->argv_lens[1]);
1082   else
1083     silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 0);
1084
1085   /* Sleep for a while */
1086   sleep(1);
1087
1088   /* We close the connection with a little timeout */
1089   silc_fsm_next_later(fsm, silc_client_command_quit_final, 2, 0);
1090   return SILC_FSM_WAIT;
1091 }
1092
1093 /********************************** KILL ************************************/
1094
1095
1096 /* Command KILL. Router operator can use this command to remove an client
1097    fromthe SILC Network. */
1098
1099 SILC_FSM_STATE(silc_client_command_kill)
1100 {
1101   SilcClientCommandContext cmd = fsm_context;
1102   SilcClientConnection conn = cmd->conn;
1103   SilcClient client = conn->client;
1104   SilcBuffer idp, auth = NULL;
1105   SilcClientEntry target;
1106   SilcDList clients;
1107   char *nickname = NULL, *comment = NULL;
1108
1109   if (cmd->argc < 2) {
1110     SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1111         "Usage: /KILL <nickname> [<comment>] [-pubkey]");
1112     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1113     return SILC_FSM_FINISH;
1114   }
1115
1116   /* Parse the typed nickname. */
1117   if (client->internal->params->nickname_parse)
1118     client->internal->params->nickname_parse(cmd->argv[1], &nickname);
1119   else
1120     nickname = strdup(cmd->argv[1]);
1121   if (!nickname)
1122     return SILC_FSM_FINISH;
1123
1124   /* Get the target client */
1125   clients = silc_client_get_clients_local(client, conn, nickname,
1126                                           cmd->argv[1]);
1127   if (!clients)
1128     /* Resolve client information */
1129     SILC_FSM_CALL(silc_client_get_clients(client, conn, nickname,
1130                                           cmd->argv[1],
1131                                           silc_client_command_resolve_continue,
1132                                           cmd));
1133
1134   target = silc_dlist_get(clients);
1135
1136   if (cmd->argc >= 3) {
1137     if (strcasecmp(cmd->argv[2], "-pubkey"))
1138       comment = cmd->argv[2];
1139
1140     if (!strcasecmp(cmd->argv[2], "-pubkey") ||
1141         (cmd->argc >= 4 && !strcasecmp(cmd->argv[3], "-pubkey"))) {
1142       /* Encode the public key authentication payload */
1143       auth = silc_auth_public_key_auth_generate(conn->public_key,
1144                                                 conn->private_key,
1145                                                 conn->client->rng,
1146                                                 conn->internal->sha1hash,
1147                                                 &target->id, SILC_ID_CLIENT);
1148     }
1149   }
1150
1151   /* Send the KILL command to the server */
1152   idp = silc_id_payload_encode(&target->id, SILC_ID_CLIENT);
1153   silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 3,
1154                               1, silc_buffer_datalen(idp),
1155                               2, comment, comment ? strlen(comment) : 0,
1156                               3, silc_buffer_datalen(auth));
1157   silc_buffer_free(idp);
1158   silc_buffer_free(auth);
1159   silc_free(nickname);
1160   silc_client_list_free(client, conn, clients);
1161
1162   /* Notify application */
1163   COMMAND(SILC_STATUS_OK);
1164
1165   /** Wait for command reply */
1166   silc_fsm_next(fsm, silc_client_command_reply_wait);
1167   return SILC_FSM_CONTINUE;
1168 }
1169
1170 /********************************** INFO ************************************/
1171
1172 /* Command INFO. Request information about specific server. If specific
1173    server is not provided the current server is used. */
1174
1175 SILC_FSM_STATE(silc_client_command_info)
1176 {
1177   SilcClientCommandContext cmd = fsm_context;
1178   SilcClientConnection conn = cmd->conn;
1179
1180   /* Send the command */
1181   if (cmd->argc == 2)
1182     silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1183                                 1, cmd->argv[1], cmd->argv_lens[1]);
1184   else
1185     silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 0);
1186
1187   /* Notify application */
1188   COMMAND(SILC_STATUS_OK);
1189
1190   /** Wait for command reply */
1191   silc_fsm_next(fsm, silc_client_command_reply_wait);
1192   return SILC_FSM_CONTINUE;
1193 }
1194
1195 /********************************** STATS ***********************************/
1196
1197 /* Command STATS. Shows server and network statistics. */
1198
1199 SILC_FSM_STATE(silc_client_command_stats)
1200 {
1201   SilcClientCommandContext cmd = fsm_context;
1202   SilcClientConnection conn = cmd->conn;
1203
1204   if (cmd->argc < 2) {
1205     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1206     return SILC_FSM_FINISH;
1207   }
1208
1209   /* Send the command */
1210   silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1211                               1, silc_buffer_datalen(conn->internal->
1212                                                      remote_idp));
1213
1214   /* Notify application */
1215   COMMAND(SILC_STATUS_OK);
1216
1217   /** Wait for command reply */
1218   silc_fsm_next(fsm, silc_client_command_reply_wait);
1219   return SILC_FSM_CONTINUE;
1220 }
1221
1222 /********************************** PING ************************************/
1223
1224 /* Command PING. Sends ping to server. */
1225
1226 SILC_FSM_STATE(silc_client_command_ping)
1227 {
1228   SilcClientCommandContext cmd = fsm_context;
1229   SilcClientConnection conn = cmd->conn;
1230
1231   if (cmd->argc < 2) {
1232     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1233     return SILC_FSM_FINISH;
1234   }
1235
1236   /* Send the command */
1237   silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1238                               1, silc_buffer_datalen(conn->internal->
1239                                                      remote_idp));
1240
1241   /* Save ping time */
1242   cmd->context = SILC_64_TO_PTR(silc_time());
1243
1244   /* Notify application */
1245   COMMAND(SILC_STATUS_OK);
1246
1247   /** Wait for command reply */
1248   silc_fsm_next(fsm, silc_client_command_reply_wait);
1249   return SILC_FSM_CONTINUE;
1250 }
1251
1252 /********************************** JOIN ************************************/
1253
1254 /* Command JOIN. Joins to a channel. */
1255
1256 SILC_FSM_STATE(silc_client_command_join)
1257 {
1258   SilcClientCommandContext cmd = fsm_context;
1259   SilcClientConnection conn = cmd->conn;
1260   SilcChannelEntry channel;
1261   SilcBuffer auth = NULL, cauth = NULL;
1262   char *name, *passphrase = NULL, *pu8, *cipher = NULL, *hmac = NULL;
1263   int i, passphrase_len = 0;
1264
1265   if (cmd->argc < 2) {
1266     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1267     goto out;
1268   }
1269
1270   /* See if we have joined to the requested channel already */
1271   channel = silc_client_get_channel(conn->client, conn, cmd->argv[1]);
1272   if (channel && silc_client_on_channel(channel, conn->local_entry))
1273     goto out;
1274
1275   if (cmd->argv_lens[1] > 256)
1276     cmd->argv_lens[1] = 256;
1277
1278   name = cmd->argv[1];
1279
1280   for (i = 2; i < cmd->argc; i++) {
1281     if (!strcasecmp(cmd->argv[i], "-cipher") && cmd->argc > i + 1) {
1282       cipher = cmd->argv[++i];
1283     } else if (!strcasecmp(cmd->argv[i], "-hmac") && cmd->argc > i + 1) {
1284       hmac = cmd->argv[++i];
1285     } else if (!strcasecmp(cmd->argv[i], "-founder")) {
1286       auth = silc_auth_public_key_auth_generate(conn->public_key,
1287                                                 conn->private_key,
1288                                                 conn->client->rng,
1289                                                 conn->internal->sha1hash,
1290                                                 conn->local_id,
1291                                                 SILC_ID_CLIENT);
1292     } else if (!strcasecmp(cmd->argv[i], "-auth")) {
1293       SilcPublicKey pubkey = conn->public_key;
1294       SilcPrivateKey privkey = conn->private_key;
1295       unsigned char *pk, pkhash[SILC_HASH_MAXLEN], *pubdata;
1296       SilcUInt32 pk_len;
1297
1298       if (cmd->argc >= i + 3) {
1299         char *pass = "";
1300         if (cmd->argc >= i + 4) {
1301           pass = cmd->argv[i + 3];
1302           i++;
1303         }
1304         if (!silc_load_key_pair(cmd->argv[i + 1], cmd->argv[i + 2], pass,
1305                                 &pubkey, &privkey)) {
1306           SAY(conn->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1307               "Could not load key pair, check your arguments");
1308           COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1309           goto out;
1310         }
1311         i += 2;
1312       }
1313
1314       pk = silc_pkcs_public_key_encode(pubkey, &pk_len);
1315       silc_hash_make(conn->internal->sha1hash, pk, pk_len, pkhash);
1316       silc_free(pk);
1317       pubdata = silc_rng_get_rn_data(conn->client->rng, 128);
1318       memcpy(pubdata, pkhash, 20);
1319       cauth = silc_auth_public_key_auth_generate_wpub(pubkey, privkey,
1320                                                       pubdata, 128,
1321                                                       conn->internal->sha1hash,
1322                                                       conn->local_id,
1323                                                       SILC_ID_CLIENT);
1324       memset(pubdata, 0, 128);
1325       silc_free(pubdata);
1326     } else {
1327       /* Passphrases must be UTF-8 encoded, so encode if it is not */
1328       if (!silc_utf8_valid(cmd->argv[i], cmd->argv_lens[i])) {
1329         passphrase_len = silc_utf8_encoded_len(cmd->argv[i],
1330                                                cmd->argv_lens[i], 0);
1331         pu8 = silc_calloc(passphrase_len, sizeof(*pu8));
1332         passphrase_len = silc_utf8_encode(cmd->argv[i], cmd->argv_lens[i],
1333                                           0, pu8, passphrase_len);
1334         passphrase = pu8;
1335       } else {
1336         passphrase = strdup(cmd->argv[i]);
1337         passphrase_len = cmd->argv_lens[i];
1338       }
1339     }
1340   }
1341
1342   /* Send JOIN command to the server */
1343   silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 7,
1344                               1, name, strlen(name),
1345                               2, silc_buffer_datalen(conn->internal->
1346                                                      local_idp),
1347                               3, passphrase, passphrase_len,
1348                               4, cipher, cipher ? strlen(cipher) : 0,
1349                               5, hmac, hmac ? strlen(hmac) : 0,
1350                               6, silc_buffer_datalen(auth),
1351                               7, silc_buffer_datalen(cauth));
1352
1353   silc_buffer_free(auth);
1354   silc_buffer_free(cauth);
1355   if (passphrase)
1356     memset(passphrase, 0, strlen(passphrase));
1357   silc_free(passphrase);
1358
1359   /* Notify application */
1360   COMMAND(SILC_STATUS_OK);
1361
1362   /** Wait for command reply */
1363   silc_fsm_next(fsm, silc_client_command_reply_wait);
1364   return SILC_FSM_CONTINUE;
1365
1366  out:
1367   return SILC_FSM_FINISH;
1368 }
1369
1370 /********************************** MOTD ************************************/
1371
1372 /* MOTD command. Requests motd from server. */
1373
1374 SILC_FSM_STATE(silc_client_command_motd)
1375 {
1376   SilcClientCommandContext cmd = fsm_context;
1377   SilcClientConnection conn = cmd->conn;
1378
1379   if (cmd->argc < 1 || cmd->argc > 2) {
1380     SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1381         "Usage: /MOTD [<server>]");
1382     COMMAND_ERROR((cmd->argc < 1 ? SILC_STATUS_ERR_NOT_ENOUGH_PARAMS :
1383                    SILC_STATUS_ERR_TOO_MANY_PARAMS));
1384     return SILC_FSM_FINISH;
1385   }
1386
1387   /* Send the command */
1388   if (cmd->argc == 1)
1389     silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1390                                 1, conn->remote_host,
1391                                 strlen(conn->remote_host));
1392   else
1393     silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1394                                 1, cmd->argv[1], cmd->argv_lens[1]);
1395
1396   /* Notify application */
1397   COMMAND(SILC_STATUS_OK);
1398
1399   /** Wait for command reply */
1400   silc_fsm_next(fsm, silc_client_command_reply_wait);
1401   return SILC_FSM_CONTINUE;
1402 }
1403
1404 /********************************** UMODE ***********************************/
1405
1406 /* UMODE. Set/unset user mode in SILC. This is used mainly to unset the
1407    modes as client cannot set itself server/router operator privileges. */
1408
1409 SILC_FSM_STATE(silc_client_command_umode)
1410 {
1411   SilcClientCommandContext cmd = fsm_context;
1412   SilcClientConnection conn = cmd->conn;
1413   unsigned char *cp, modebuf[4];
1414   SilcUInt32 mode, add, len;
1415   int i;
1416
1417   if (cmd->argc < 2) {
1418     SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1419         "Usage: /UMODE +|-<modes>");
1420     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1421     return SILC_FSM_FINISH;
1422   }
1423
1424   mode = conn->local_entry->mode;
1425
1426   /* Are we adding or removing mode */
1427   if (cmd->argv[1][0] == '-')
1428     add = FALSE;
1429   else
1430     add = TRUE;
1431
1432   /* Parse mode */
1433   cp = cmd->argv[1] + 1;
1434   len = strlen(cp);
1435   for (i = 0; i < len; i++) {
1436     switch(cp[i]) {
1437     case 'a':
1438       if (add) {
1439         mode = 0;
1440         mode |= SILC_UMODE_SERVER_OPERATOR;
1441         mode |= SILC_UMODE_ROUTER_OPERATOR;
1442         mode |= SILC_UMODE_GONE;
1443         mode |= SILC_UMODE_INDISPOSED;
1444         mode |= SILC_UMODE_BUSY;
1445         mode |= SILC_UMODE_PAGE;
1446         mode |= SILC_UMODE_HYPER;
1447         mode |= SILC_UMODE_ROBOT;
1448         mode |= SILC_UMODE_BLOCK_PRIVMSG;
1449         mode |= SILC_UMODE_REJECT_WATCHING;
1450       } else {
1451         mode = SILC_UMODE_NONE;
1452       }
1453       break;
1454     case 's':
1455       if (add)
1456         mode |= SILC_UMODE_SERVER_OPERATOR;
1457       else
1458         mode &= ~SILC_UMODE_SERVER_OPERATOR;
1459       break;
1460     case 'r':
1461       if (add)
1462         mode |= SILC_UMODE_ROUTER_OPERATOR;
1463       else
1464         mode &= ~SILC_UMODE_ROUTER_OPERATOR;
1465       break;
1466     case 'g':
1467       if (add)
1468         mode |= SILC_UMODE_GONE;
1469       else
1470         mode &= ~SILC_UMODE_GONE;
1471       break;
1472     case 'i':
1473       if (add)
1474         mode |= SILC_UMODE_INDISPOSED;
1475       else
1476         mode &= ~SILC_UMODE_INDISPOSED;
1477       break;
1478     case 'b':
1479       if (add)
1480         mode |= SILC_UMODE_BUSY;
1481       else
1482         mode &= ~SILC_UMODE_BUSY;
1483       break;
1484     case 'p':
1485       if (add)
1486         mode |= SILC_UMODE_PAGE;
1487       else
1488         mode &= ~SILC_UMODE_PAGE;
1489       break;
1490     case 'h':
1491       if (add)
1492         mode |= SILC_UMODE_HYPER;
1493       else
1494         mode &= ~SILC_UMODE_HYPER;
1495       break;
1496     case 't':
1497       if (add)
1498         mode |= SILC_UMODE_ROBOT;
1499       else
1500         mode &= ~SILC_UMODE_ROBOT;
1501       break;
1502     case 'P':
1503       if (add)
1504         mode |= SILC_UMODE_BLOCK_PRIVMSG;
1505       else
1506         mode &= ~SILC_UMODE_BLOCK_PRIVMSG;
1507       break;
1508     case 'w':
1509       if (add)
1510         mode |= SILC_UMODE_REJECT_WATCHING;
1511       else
1512         mode &= ~SILC_UMODE_REJECT_WATCHING;
1513       break;
1514     case 'I':
1515       if (add)
1516         mode |= SILC_UMODE_BLOCK_INVITE;
1517       else
1518         mode &= ~SILC_UMODE_BLOCK_INVITE;
1519       break;
1520     default:
1521       COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE);
1522       return SILC_FSM_FINISH;
1523       break;
1524     }
1525   }
1526
1527   SILC_PUT32_MSB(mode, modebuf);
1528
1529   /* Send the command */
1530   silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
1531                               1, silc_buffer_datalen(conn->internal->
1532                                                      local_idp),
1533                               2, modebuf, sizeof(modebuf));
1534
1535   /* Notify application */
1536   COMMAND(SILC_STATUS_OK);
1537
1538   /** Wait for command reply */
1539   silc_fsm_next(fsm, silc_client_command_reply_wait);
1540   return SILC_FSM_CONTINUE;
1541 }
1542
1543 /********************************** CMODE ***********************************/
1544
1545 /* CMODE command. Sets channel mode. Modes that does not require any arguments
1546    can be set several at once. Those modes that require argument must be set
1547    separately (unless set with modes that does not require arguments). */
1548
1549 SILC_FSM_STATE(silc_client_command_cmode)
1550 {
1551   SilcClientCommandContext cmd = fsm_context;
1552   SilcClientConnection conn = cmd->conn;
1553   SilcClient client = conn->client;
1554   SilcChannelEntry channel;
1555   SilcBuffer chidp, auth = NULL, pk = NULL;
1556   unsigned char *name, *cp, modebuf[4], tmp[4], *arg = NULL;
1557   SilcUInt32 mode, add, type, len, arg_len = 0;
1558   int i;
1559
1560   if (cmd->argc < 3) {
1561     SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1562         "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1563     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1564     goto out;
1565   }
1566
1567   if (cmd->argv[1][0] == '*') {
1568     if (!conn->current_channel) {
1569       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1570       goto out;
1571     }
1572
1573     channel = conn->current_channel;
1574   } else {
1575     name = cmd->argv[1];
1576
1577     channel = silc_client_get_channel(conn->client, conn, name);
1578     if (!channel) {
1579       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1580       goto out;
1581     }
1582   }
1583
1584   mode = channel->mode;
1585
1586   /* Are we adding or removing mode */
1587   if (cmd->argv[2][0] == '-')
1588     add = FALSE;
1589   else
1590     add = TRUE;
1591
1592   /* Argument type to be sent to server */
1593   type = 0;
1594
1595   /* Parse mode */
1596   cp = cmd->argv[2] + 1;
1597   len = strlen(cp);
1598   for (i = 0; i < len; i++) {
1599     switch(cp[i]) {
1600     case 'p':
1601       if (add)
1602         mode |= SILC_CHANNEL_MODE_PRIVATE;
1603       else
1604         mode &= ~SILC_CHANNEL_MODE_PRIVATE;
1605       break;
1606     case 's':
1607       if (add)
1608         mode |= SILC_CHANNEL_MODE_SECRET;
1609       else
1610         mode &= ~SILC_CHANNEL_MODE_SECRET;
1611       break;
1612     case 'k':
1613       if (add)
1614         mode |= SILC_CHANNEL_MODE_PRIVKEY;
1615       else
1616         mode &= ~SILC_CHANNEL_MODE_PRIVKEY;
1617       break;
1618     case 'i':
1619       if (add)
1620         mode |= SILC_CHANNEL_MODE_INVITE;
1621       else
1622         mode &= ~SILC_CHANNEL_MODE_INVITE;
1623       break;
1624     case 't':
1625       if (add)
1626         mode |= SILC_CHANNEL_MODE_TOPIC;
1627       else
1628         mode &= ~SILC_CHANNEL_MODE_TOPIC;
1629       break;
1630     case 'm':
1631       if (add)
1632         mode |= SILC_CHANNEL_MODE_SILENCE_USERS;
1633       else
1634         mode &= ~SILC_CHANNEL_MODE_SILENCE_USERS;
1635       break;
1636     case 'M':
1637       if (add)
1638         mode |= SILC_CHANNEL_MODE_SILENCE_OPERS;
1639       else
1640         mode &= ~SILC_CHANNEL_MODE_SILENCE_OPERS;
1641       break;
1642     case 'l':
1643       if (add) {
1644         int ll;
1645         mode |= SILC_CHANNEL_MODE_ULIMIT;
1646         type = 3;
1647         if (cmd->argc < 4) {
1648           SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1649               "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1650           COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1651           goto out;
1652         }
1653         ll = atoi(cmd->argv[3]);
1654         SILC_PUT32_MSB(ll, tmp);
1655         arg = tmp;
1656         arg_len = 4;
1657       } else {
1658         mode &= ~SILC_CHANNEL_MODE_ULIMIT;
1659       }
1660       break;
1661     case 'a':
1662       if (add) {
1663         mode |= SILC_CHANNEL_MODE_PASSPHRASE;
1664         type = 4;
1665         if (cmd->argc < 4) {
1666           SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1667               "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1668           COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1669           goto out;
1670         }
1671         arg = cmd->argv[3];
1672         arg_len = cmd->argv_lens[3];
1673       } else {
1674         mode &= ~SILC_CHANNEL_MODE_PASSPHRASE;
1675       }
1676       break;
1677     case 'c':
1678       if (add) {
1679         mode |= SILC_CHANNEL_MODE_CIPHER;
1680         type = 5;
1681         if (cmd->argc < 4) {
1682           SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1683               "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1684           COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1685           goto out;
1686         }
1687         arg = cmd->argv[3];
1688         arg_len = cmd->argv_lens[3];
1689       } else {
1690         mode &= ~SILC_CHANNEL_MODE_CIPHER;
1691       }
1692       break;
1693     case 'h':
1694       if (add) {
1695         mode |= SILC_CHANNEL_MODE_HMAC;
1696         type = 6;
1697         if (cmd->argc < 4) {
1698           SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1699               "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1700           COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1701           goto out;
1702         }
1703         arg = cmd->argv[3];
1704         arg_len = cmd->argv_lens[3];
1705       } else {
1706         mode &= ~SILC_CHANNEL_MODE_HMAC;
1707       }
1708       break;
1709     case 'f':
1710       if (add) {
1711         SilcPublicKey pubkey = conn->public_key;
1712         SilcPrivateKey privkey = conn->private_key;
1713
1714         mode |= SILC_CHANNEL_MODE_FOUNDER_AUTH;
1715         type = 7;
1716
1717         if (cmd->argc >= 5) {
1718           char *pass = "";
1719           if (cmd->argc >= 6)
1720             pass = cmd->argv[5];
1721           if (!silc_load_key_pair(cmd->argv[3], cmd->argv[4], pass,
1722                                   &pubkey, &privkey)) {
1723             SAY(client, conn, SILC_CLIENT_MESSAGE_ERROR,
1724                 "Could not load key pair, check your arguments");
1725             COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1726             goto out;
1727           }
1728         }
1729
1730         pk = silc_public_key_payload_encode(pubkey);
1731         auth = silc_auth_public_key_auth_generate(pubkey, privkey,
1732                                                   conn->client->rng,
1733                                                   conn->internal->sha1hash,
1734                                                   conn->local_id,
1735                                                   SILC_ID_CLIENT);
1736         arg = silc_buffer_data(auth);
1737         arg_len = silc_buffer_len(auth);
1738       } else {
1739         mode &= ~SILC_CHANNEL_MODE_FOUNDER_AUTH;
1740       }
1741       break;
1742     case 'C':
1743       if (add) {
1744         int k;
1745         SilcBool chadd = FALSE;
1746         SilcPublicKey chpk = NULL;
1747
1748         mode |= SILC_CHANNEL_MODE_CHANNEL_AUTH;
1749         type = 9;
1750
1751         if (cmd->argc == 3) {
1752           /* Send empty command to receive the public key list. */
1753           chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
1754           silc_client_command_send_va(conn, cmd, SILC_COMMAND_CMODE,
1755                                       NULL, NULL, 1,
1756                                       1, silc_buffer_datalen(chidp));
1757           silc_buffer_free(chidp);
1758
1759           /* Notify application */
1760           COMMAND(SILC_STATUS_OK);
1761           goto out;
1762         }
1763
1764         if (cmd->argc >= 4) {
1765           auth = silc_buffer_alloc_size(2);
1766           silc_buffer_format(auth,
1767                              SILC_STR_UI_SHORT(cmd->argc - 3),
1768                              SILC_STR_END);
1769         }
1770
1771         for (k = 3; k < cmd->argc; k++) {
1772           if (cmd->argv[k][0] == '+')
1773             chadd = TRUE;
1774           if (!silc_pkcs_load_public_key(cmd->argv[k] + 1, &chpk)) {
1775             SAY(conn->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1776                 "Could not load public key %s, check the filename",
1777                 cmd->argv[k]);
1778             COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1779             silc_buffer_free(auth);
1780             goto out;
1781           }
1782
1783           if (chpk) {
1784             pk = silc_public_key_payload_encode(chpk);
1785             auth = silc_argument_payload_encode_one(auth,
1786                                                     silc_buffer_datalen(pk),
1787                                                     chadd ? 0x00 : 0x01);
1788             silc_pkcs_public_key_free(chpk);
1789             silc_buffer_free(pk);
1790             pk = NULL;
1791           }
1792         }
1793
1794         arg = silc_buffer_data(auth);
1795         arg_len = silc_buffer_len(auth);
1796       } else {
1797         mode &= ~SILC_CHANNEL_MODE_CHANNEL_AUTH;
1798       }
1799       break;
1800     default:
1801       COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE);
1802       goto out;
1803       break;
1804     }
1805   }
1806
1807   chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
1808   SILC_PUT32_MSB(mode, modebuf);
1809
1810   /* Send the command. We support sending only one mode at once that
1811      requires an argument. */
1812   if (type && arg) {
1813     silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 4,
1814                                 1, silc_buffer_datalen(chidp),
1815                                 2, modebuf, sizeof(modebuf),
1816                                 type, arg, arg_len,
1817                                 8, silc_buffer_datalen(pk));
1818   } else {
1819     silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
1820                                 1, silc_buffer_datalen(chidp),
1821                                 2, modebuf, sizeof(modebuf));
1822   }
1823
1824   silc_buffer_free(chidp);
1825   silc_buffer_free(auth);
1826   silc_buffer_free(pk);
1827
1828   /* Notify application */
1829   COMMAND(SILC_STATUS_OK);
1830
1831   /** Wait for command reply */
1832   silc_fsm_next(fsm, silc_client_command_reply_wait);
1833   return SILC_FSM_CONTINUE;
1834
1835  out:
1836   return SILC_FSM_FINISH;
1837 }
1838
1839 /********************************* CUMODE ***********************************/
1840
1841 /* CUMODE command. Changes client's mode on a channel. */
1842
1843 SILC_FSM_STATE(silc_client_command_cumode)
1844 {
1845   SilcClientCommandContext cmd = fsm_context;
1846   SilcClientConnection conn = cmd->conn;
1847   SilcClient client = conn->client;
1848   SilcChannelEntry channel;
1849   SilcChannelUser chu;
1850   SilcClientEntry client_entry;
1851   SilcBuffer clidp, chidp, auth = NULL;
1852   SilcDList clients = NULL;
1853   unsigned char *name, *cp, modebuf[4];
1854   SilcUInt32 mode = 0, add, len;
1855   char *nickname = NULL;
1856   int i;
1857
1858   if (cmd->argc < 4) {
1859     SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1860         "Usage: /CUMODE <channel> +|-<modes> <nickname>[@<server>]");
1861     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1862     goto out;
1863   }
1864
1865   if (cmd->argv[1][0] == '*') {
1866     if (!conn->current_channel) {
1867       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1868       goto out;
1869     }
1870
1871     channel = conn->current_channel;
1872   } else {
1873     name = cmd->argv[1];
1874
1875     channel = silc_client_get_channel(conn->client, conn, name);
1876     if (!channel) {
1877       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1878       goto out;
1879     }
1880   }
1881
1882   /* Parse the typed nickname. */
1883   if (client->internal->params->nickname_parse)
1884     client->internal->params->nickname_parse(cmd->argv[3], &nickname);
1885   else
1886     nickname = strdup(cmd->argv[3]);
1887
1888   /* Find client entry */
1889   clients = silc_client_get_clients_local(client, conn, nickname,
1890                                           cmd->argv[3]);
1891   if (!clients)
1892     /* Resolve client information */
1893     SILC_FSM_CALL(silc_client_get_clients(client, conn, nickname, cmd->argv[3],
1894                                           silc_client_command_resolve_continue,
1895                                           cmd));
1896
1897   client_entry = silc_dlist_get(clients);
1898
1899   /* Get the current mode */
1900   chu = silc_client_on_channel(channel, client_entry);
1901   if (chu)
1902     mode = chu->mode;
1903
1904   /* Are we adding or removing mode */
1905   if (cmd->argv[2][0] == '-')
1906     add = FALSE;
1907   else
1908     add = TRUE;
1909
1910   /* Parse mode */
1911   cp = cmd->argv[2] + 1;
1912   len = strlen(cp);
1913   for (i = 0; i < len; i++) {
1914     switch(cp[i]) {
1915     case 'a':
1916       if (add) {
1917         mode |= SILC_CHANNEL_UMODE_CHANFO;
1918         mode |= SILC_CHANNEL_UMODE_CHANOP;
1919         mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
1920         mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
1921         mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
1922       } else {
1923         mode = SILC_CHANNEL_UMODE_NONE;
1924       }
1925       break;
1926     case 'f':
1927       if (add) {
1928         SilcPublicKey pubkey = conn->public_key;
1929         SilcPrivateKey privkey = conn->private_key;
1930
1931         if (cmd->argc >= 6) {
1932           char *pass = "";
1933           if (cmd->argc >= 7)
1934             pass = cmd->argv[6];
1935           if (!silc_load_key_pair(cmd->argv[4], cmd->argv[5], pass,
1936                                   &pubkey, &privkey)) {
1937             SAY(conn->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1938                 "Could not load key pair, check your arguments");
1939             COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1940             goto out;
1941           }
1942         }
1943
1944         auth = silc_auth_public_key_auth_generate(pubkey, privkey,
1945                                                   conn->client->rng,
1946                                                   conn->internal->sha1hash,
1947                                                   conn->local_id,
1948                                                   SILC_ID_CLIENT);
1949         mode |= SILC_CHANNEL_UMODE_CHANFO;
1950       } else {
1951         mode &= ~SILC_CHANNEL_UMODE_CHANFO;
1952       }
1953       break;
1954     case 'o':
1955       if (add)
1956         mode |= SILC_CHANNEL_UMODE_CHANOP;
1957       else
1958         mode &= ~SILC_CHANNEL_UMODE_CHANOP;
1959       break;
1960     case 'b':
1961       if (add)
1962         mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
1963       else
1964         mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
1965       break;
1966     case 'u':
1967       if (add)
1968         mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
1969       else
1970         mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
1971       break;
1972     case 'r':
1973       if (add)
1974         mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
1975       else
1976         mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
1977       break;
1978     case 'q':
1979       if (add)
1980         mode |= SILC_CHANNEL_UMODE_QUIET;
1981       else
1982         mode &= ~SILC_CHANNEL_UMODE_QUIET;
1983       break;
1984     default:
1985       COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE);
1986       goto out;
1987       break;
1988     }
1989   }
1990
1991   chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
1992   SILC_PUT32_MSB(mode, modebuf);
1993   clidp = silc_id_payload_encode(&client_entry->id, SILC_ID_CLIENT);
1994
1995   /* Send the command packet. We support sending only one mode at once
1996      that requires an argument. */
1997   silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, auth ? 4 : 3,
1998                               1, silc_buffer_datalen(chidp),
1999                               2, modebuf, 4,
2000                               3, silc_buffer_datalen(clidp),
2001                               4, silc_buffer_datalen(auth));
2002
2003   silc_buffer_free(chidp);
2004   silc_buffer_free(clidp);
2005   if (auth)
2006     silc_buffer_free(auth);
2007   silc_free(nickname);
2008   silc_client_list_free(client, conn, clients);
2009
2010   /* Notify application */
2011   COMMAND(SILC_STATUS_OK);
2012
2013   /** Wait for command reply */
2014   silc_fsm_next(fsm, silc_client_command_reply_wait);
2015   return SILC_FSM_CONTINUE;
2016
2017  out:
2018   silc_client_list_free(client, conn, clients);
2019   silc_free(nickname);
2020   return SILC_FSM_FINISH;
2021 }
2022
2023 /********************************** KICK ************************************/
2024
2025 /* KICK command. Kicks a client out of channel. */
2026
2027 SILC_FSM_STATE(silc_client_command_kick)
2028 {
2029   SilcClientCommandContext cmd = fsm_context;
2030   SilcClientConnection conn = cmd->conn;
2031   SilcClient client = conn->client;
2032   SilcChannelEntry channel;
2033   SilcBuffer idp, idp2;
2034   SilcClientEntry target;
2035   SilcDList clients = NULL;
2036   char *name;
2037   char *nickname = NULL;
2038
2039   if (cmd->argc < 3) {
2040     SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2041         "Usage: /KICK <channel> <nickname> [<comment>]");
2042     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2043     goto out;
2044   }
2045
2046   if (cmd->argv[1][0] == '*') {
2047     if (!conn->current_channel) {
2048       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2049       goto out;
2050     }
2051     name = conn->current_channel->channel_name;
2052   } else {
2053     name = cmd->argv[1];
2054   }
2055
2056   if (!conn->current_channel) {
2057     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2058     goto out;
2059   }
2060
2061   /* Get the Channel ID of the channel */
2062   channel = silc_client_get_channel(conn->client, conn, name);
2063   if (!channel) {
2064     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2065     goto out;
2066   }
2067
2068   /* Parse the typed nickname. */
2069   if (client->internal->params->nickname_parse)
2070     client->internal->params->nickname_parse(cmd->argv[2], &nickname);
2071   else
2072     nickname = strdup(cmd->argv[2]);
2073
2074   /* Get the target client */
2075   clients = silc_client_get_clients_local(client, conn, nickname,
2076                                           cmd->argv[2]);
2077   if (!clients) {
2078     SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2079         "No such client: %s", cmd->argv[2]);
2080     COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_NICK);
2081     goto out;
2082   }
2083   target = silc_dlist_get(clients);
2084
2085   /* Send KICK command to the server */
2086   idp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2087   idp2 = silc_id_payload_encode(&target->id, SILC_ID_CLIENT);
2088   if (cmd->argc == 3)
2089     silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
2090                                 1, silc_buffer_datalen(idp),
2091                                 2, silc_buffer_datalen(idp2));
2092   else
2093     silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 3,
2094                                 1, silc_buffer_datalen(idp),
2095                                 2, silc_buffer_datalen(idp2),
2096                                 3, cmd->argv[3], strlen(cmd->argv[3]));
2097
2098   silc_buffer_free(idp);
2099   silc_buffer_free(idp2);
2100   silc_free(nickname);
2101   silc_client_list_free(client, conn, clients);
2102
2103   /* Notify application */
2104   COMMAND(SILC_STATUS_OK);
2105
2106   /** Wait for command reply */
2107   silc_fsm_next(fsm, silc_client_command_reply_wait);
2108   return SILC_FSM_CONTINUE;
2109
2110  out:
2111   silc_free(nickname);
2112   return SILC_FSM_FINISH;
2113 }
2114
2115 /***************************** OPER & SILCOPER ******************************/
2116
2117 typedef struct {
2118   unsigned char *passphrase;
2119   SilcUInt32 passphrase_len;
2120 } *SilcClientCommandOper;
2121
2122 /* Ask passphrase callback */
2123
2124 static void silc_client_command_oper_cb(unsigned char *data,
2125                                         SilcUInt32 data_len, void *context)
2126 {
2127   SilcClientCommandContext cmd = context;
2128   SilcClientCommandOper oper = cmd->context;
2129
2130   if (data && data_len)
2131     oper->passphrase = silc_memdup(data, data_len);
2132   oper->passphrase_len = data_len;
2133
2134   /* Continue */
2135   SILC_FSM_CALL_CONTINUE(&cmd->thread);
2136 }
2137
2138 /* Send OPER/SILCOPER command */
2139
2140 SILC_FSM_STATE(silc_client_command_oper_send)
2141 {
2142   SilcClientCommandContext cmd = fsm_context;
2143   SilcClientConnection conn = cmd->conn;
2144   SilcClientCommandOper oper = cmd->context;
2145   SilcBuffer auth;
2146
2147   if (!oper || !oper->passphrase) {
2148     /* Encode the public key authentication payload */
2149     auth = silc_auth_public_key_auth_generate(conn->public_key,
2150                                               conn->private_key,
2151                                               conn->client->rng,
2152                                               conn->internal->hash,
2153                                               conn->local_id,
2154                                               SILC_ID_CLIENT);
2155   } else {
2156     /* Encode the password authentication payload */
2157     auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
2158                                     oper->passphrase, oper->passphrase_len);
2159   }
2160
2161   silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
2162                               1, cmd->argv[1], strlen(cmd->argv[1]),
2163                               2, silc_buffer_datalen(auth));
2164
2165   silc_buffer_clear(auth);
2166   silc_buffer_free(auth);
2167   if (oper) {
2168     silc_free(oper->passphrase);
2169     silc_free(oper);
2170   }
2171
2172   /* Notify application */
2173   COMMAND(SILC_STATUS_OK);
2174
2175   /** Wait for command reply */
2176   silc_fsm_next(fsm, silc_client_command_reply_wait);
2177   return SILC_FSM_CONTINUE;
2178 }
2179
2180 /* OPER command. Used to obtain server operator privileges. */
2181
2182 SILC_FSM_STATE(silc_client_command_oper)
2183 {
2184   SilcClientCommandContext cmd = fsm_context;
2185   SilcClientConnection conn = cmd->conn;
2186   SilcClientCommandOper oper;
2187
2188   if (cmd->argc < 2) {
2189     SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2190         "Usage: /OPER <username> [-pubkey]");
2191     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2192     return SILC_FSM_FINISH;
2193   }
2194
2195   /* Get passphrase */
2196   if (cmd->argc < 3) {
2197     oper = silc_calloc(1, sizeof(*oper));
2198     if (!oper)
2199       return SILC_FSM_FINISH;
2200     cmd->context = oper;
2201     SILC_FSM_CALL(conn->client->internal->
2202                   ops->ask_passphrase(conn->client, conn,
2203                                       silc_client_command_oper_cb, cmd));
2204   }
2205
2206   silc_fsm_next(fsm, silc_client_command_oper_send);
2207   return SILC_FSM_CONTINUE;
2208 }
2209
2210 /* SILCOPER command. Used to obtain router operator privileges. */
2211
2212 SILC_FSM_STATE(silc_client_command_silcoper)
2213 {
2214   SilcClientCommandContext cmd = fsm_context;
2215   SilcClientConnection conn = cmd->conn;
2216   SilcClientCommandOper oper;
2217
2218   if (cmd->argc < 2) {
2219     SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2220         "Usage: /SILCOPER <username> [-pubkey]");
2221     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2222     return SILC_FSM_FINISH;
2223   }
2224
2225   /* Get passphrase */
2226   if (cmd->argc < 3) {
2227     oper = silc_calloc(1, sizeof(*oper));
2228     if (!oper)
2229       return SILC_FSM_FINISH;
2230     cmd->context = oper;
2231     SILC_FSM_CALL(conn->client->internal->
2232                   ops->ask_passphrase(conn->client, conn,
2233                                       silc_client_command_oper_cb, cmd));
2234   }
2235
2236   silc_fsm_next(fsm, silc_client_command_oper_send);
2237   return SILC_FSM_CONTINUE;
2238 }
2239
2240 /*********************************** BAN ************************************/
2241
2242 /* Command BAN. This is used to manage the ban list of the channel. */
2243
2244 SILC_FSM_STATE(silc_client_command_ban)
2245 {
2246   SilcClientCommandContext cmd = fsm_context;
2247   SilcClientConnection conn = cmd->conn;
2248   SilcChannelEntry channel;
2249   SilcBuffer chidp, args = NULL;
2250   char *name, *ban = NULL;
2251   unsigned char action[1];
2252   SilcPublicKey pubkey = NULL;
2253
2254   if (cmd->argc < 2) {
2255     SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2256         "Usage: /BAN <channel> "
2257         "[+|-[<nickname>[@<server>[!<username>[@hostname>]]]]]");
2258     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2259     goto out;
2260   }
2261
2262   if (cmd->argv[1][0] == '*') {
2263     if (!conn->current_channel) {
2264       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2265       goto out;
2266     }
2267
2268     channel = conn->current_channel;
2269   } else {
2270     name = cmd->argv[1];
2271
2272     channel = silc_client_get_channel(conn->client, conn, name);
2273     if (!channel) {
2274       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2275       goto out;
2276     }
2277   }
2278
2279   if (cmd->argc == 3) {
2280     if (cmd->argv[2][0] == '+')
2281       action[0] = 0x00;
2282     else
2283       action[0] = 0x01;
2284
2285     /* Check if it is public key file to be added to invite list */
2286     silc_pkcs_load_public_key(cmd->argv[2] + 1, &pubkey);
2287     ban = cmd->argv[2];
2288     if (!pubkey)
2289       ban++;
2290   }
2291
2292   if (ban) {
2293     args = silc_buffer_alloc_size(2);
2294     silc_buffer_format(args,
2295                        SILC_STR_UI_SHORT(1),
2296                        SILC_STR_END);
2297     if (pubkey) {
2298       chidp = silc_public_key_payload_encode(pubkey);
2299       args = silc_argument_payload_encode_one(args,
2300                                               silc_buffer_datalen(chidp), 2);
2301       silc_buffer_free(chidp);
2302       silc_pkcs_public_key_free(pubkey);
2303     } else {
2304       args = silc_argument_payload_encode_one(args, ban, strlen(ban), 1);
2305     }
2306   }
2307
2308   chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2309
2310   /* Send the command */
2311   silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 3,
2312                               1, silc_buffer_datalen(chidp),
2313                               2, args ? action : NULL, args ? 1 : 0,
2314                               3, silc_buffer_datalen(args));
2315
2316   silc_buffer_free(chidp);
2317   silc_buffer_free(args);
2318
2319   /* Notify application */
2320   COMMAND(SILC_STATUS_OK);
2321
2322   /** Wait for command reply */
2323   silc_fsm_next(fsm, silc_client_command_reply_wait);
2324   return SILC_FSM_CONTINUE;
2325
2326  out:
2327   return SILC_FSM_FINISH;
2328 }
2329
2330 /********************************* DETACH ***********************************/
2331
2332 /* Command DETACH. This is used to detach from the server */
2333
2334 SILC_FSM_STATE(silc_client_command_detach)
2335 {
2336   SilcClientCommandContext cmd = fsm_context;
2337   SilcClientConnection conn = cmd->conn;
2338
2339   silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 0);
2340
2341   /* Notify application */
2342   COMMAND(SILC_STATUS_OK);
2343
2344   /** Wait for command reply */
2345   silc_fsm_next(fsm, silc_client_command_reply_wait);
2346   return SILC_FSM_CONTINUE;
2347 }
2348
2349 /********************************** WATCH ***********************************/
2350
2351 /* Command WATCH. */
2352
2353 SILC_FSM_STATE(silc_client_command_watch)
2354 {
2355   SilcClientCommandContext cmd = fsm_context;
2356   SilcClientConnection conn = cmd->conn;
2357   SilcBuffer args = NULL;
2358   int type = 0;
2359   const char *pubkey = NULL;
2360   SilcBool pubkey_add = TRUE;
2361
2362   if (cmd->argc < 3) {
2363     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2364     goto out;
2365   }
2366
2367   if (!strcasecmp(cmd->argv[1], "-add")) {
2368     type = 2;
2369   } else if (!strcasecmp(cmd->argv[1], "-del")) {
2370     type = 3;
2371   } else if (!strcasecmp(cmd->argv[1], "-pubkey") && cmd->argc >= 3) {
2372     type = 4;
2373     pubkey = cmd->argv[2] + 1;
2374     if (cmd->argv[2][0] == '-')
2375       pubkey_add = FALSE;
2376   } else {
2377     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2378     goto out;
2379   }
2380
2381   if (pubkey) {
2382     SilcPublicKey pk;
2383     SilcBuffer buffer;
2384
2385     if (!silc_pkcs_load_public_key(pubkey, &pk)) {
2386       SAY(conn->client, conn, SILC_CLIENT_MESSAGE_ERROR,
2387           "Could not load public key %s, check the filename", pubkey);
2388       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2389       goto out;
2390     }
2391
2392     args = silc_buffer_alloc_size(2);
2393     silc_buffer_format(args,
2394                        SILC_STR_UI_SHORT(1),
2395                        SILC_STR_END);
2396     buffer = silc_public_key_payload_encode(pk);
2397     args = silc_argument_payload_encode_one(args, silc_buffer_datalen(buffer),
2398                                             pubkey_add ? 0x00 : 0x01);
2399     silc_buffer_free(buffer);
2400     silc_pkcs_public_key_free(pk);
2401   }
2402
2403   /* Send the commmand */
2404   silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
2405                               1, silc_buffer_datalen(conn->internal->
2406                                                      local_idp),
2407                               type, pubkey ? args->data : cmd->argv[2],
2408                               pubkey ? silc_buffer_len(args) :
2409                               cmd->argv_lens[2]);
2410
2411   silc_buffer_free(args);
2412
2413   /* Notify application */
2414   COMMAND(SILC_STATUS_OK);
2415
2416   /** Wait for command reply */
2417   silc_fsm_next(fsm, silc_client_command_reply_wait);
2418   return SILC_FSM_CONTINUE;
2419
2420  out:
2421   return SILC_FSM_FINISH;
2422 }
2423
2424 /********************************** LEAVE ***********************************/
2425
2426 /* LEAVE command. Leaves a channel. Client removes itself from a channel. */
2427
2428 SILC_FSM_STATE(silc_client_command_leave)
2429 {
2430   SilcClientCommandContext cmd = fsm_context;
2431   SilcClientConnection conn = cmd->conn;
2432   SilcChannelEntry channel;
2433   SilcBuffer idp;
2434   char *name;
2435
2436   if (cmd->argc != 2) {
2437     SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2438         "Usage: /LEAVE <channel>");
2439     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2440     goto out;
2441   }
2442
2443   if (cmd->argv[1][0] == '*') {
2444     if (!conn->current_channel) {
2445       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2446       goto out;
2447     }
2448     name = conn->current_channel->channel_name;
2449   } else {
2450     name = cmd->argv[1];
2451   }
2452
2453   /* Get the channel entry */
2454   channel = silc_client_get_channel(conn->client, conn, name);
2455   if (!channel) {
2456     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2457     goto out;
2458   }
2459
2460   idp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2461
2462   /* Send LEAVE command to the server */
2463   silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
2464                               1, silc_buffer_datalen(idp));
2465
2466   silc_buffer_free(idp);
2467
2468   /* Notify application */
2469   COMMAND(SILC_STATUS_OK);
2470
2471   if (conn->current_channel == channel)
2472     conn->current_channel = NULL;
2473
2474   /** Wait for command reply */
2475   silc_fsm_next(fsm, silc_client_command_reply_wait);
2476   return SILC_FSM_CONTINUE;
2477
2478  out:
2479   return SILC_FSM_FINISH;
2480 }
2481
2482 /********************************** USERS ***********************************/
2483
2484 /* Command USERS. Requests the USERS of the clients joined on requested
2485    channel. */
2486
2487 SILC_FSM_STATE(silc_client_command_users)
2488 {
2489   SilcClientCommandContext cmd = fsm_context;
2490   SilcClientConnection conn = cmd->conn;
2491   char *name;
2492
2493   if (cmd->argc != 2) {
2494     SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2495         "Usage: /USERS <channel>");
2496     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2497     goto out;
2498   }
2499
2500   if (cmd->argv[1][0] == '*') {
2501     if (!conn->current_channel) {
2502       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2503       goto out;
2504     }
2505     name = conn->current_channel->channel_name;
2506   } else {
2507     name = cmd->argv[1];
2508   }
2509
2510   /* Send USERS command to the server */
2511   silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
2512                               2, name, strlen(name));
2513
2514   /* Notify application */
2515   COMMAND(SILC_STATUS_OK);
2516
2517   /** Wait for command reply */
2518   silc_fsm_next(fsm, silc_client_command_reply_wait);
2519   return SILC_FSM_CONTINUE;
2520
2521  out:
2522   return SILC_FSM_FINISH;
2523 }
2524
2525 /********************************* GETKEY ***********************************/
2526
2527 /* Command GETKEY. Used to fetch remote client's public key. */
2528
2529 SILC_FSM_STATE(silc_client_command_getkey)
2530 {
2531   SilcClientCommandContext cmd = fsm_context;
2532   SilcClientConnection conn = cmd->conn;
2533   SilcClient client = conn->client;
2534   SilcClientEntry client_entry;
2535   SilcServerEntry server_entry;
2536   SilcDList clients;
2537   char *nickname = NULL;
2538   SilcBuffer idp;
2539
2540   if (cmd->argc < 2) {
2541     client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_INFO,
2542                      "Usage: /GETKEY <nickname or server name>");
2543     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2544     return SILC_FSM_FINISH;
2545   }
2546
2547   /* Parse the typed nickname. */
2548   if (client->internal->params->nickname_parse)
2549     client->internal->params->nickname_parse(cmd->argv[1], &nickname);
2550   else
2551     nickname = strdup(cmd->argv[1]);
2552   if (!nickname) {
2553     COMMAND_ERROR(SILC_STATUS_ERR_RESOURCE_LIMIT);
2554     return SILC_FSM_FINISH;
2555   }
2556
2557   /* Find client entry */
2558   clients = silc_client_get_clients_local(client, conn, nickname,
2559                                           cmd->argv[1]);
2560   if (!clients) {
2561     /* Check whether user requested server */
2562     server_entry = silc_client_get_server(client, conn, cmd->argv[1]);
2563     if (!server_entry) {
2564       /* No client or server exist with this name, query for both. */
2565       SILC_FSM_CALL(silc_client_command_send(client, conn,
2566                                              SILC_COMMAND_IDENTIFY,
2567                                              silc_client_command_continue,
2568                                              cmd, 2,
2569                                              1, cmd->argv[1],
2570                                              strlen(cmd->argv[1]),
2571                                              2, cmd->argv[1],
2572                                              strlen(cmd->argv[1])));
2573     }
2574     idp = silc_id_payload_encode(&server_entry->id, SILC_ID_SERVER);
2575   } else {
2576     client_entry = silc_dlist_get(clients);
2577     idp = silc_id_payload_encode(&client_entry->id, SILC_ID_CLIENT);
2578     silc_client_list_free(client, conn, clients);
2579   }
2580
2581   /* Send the commmand */
2582   silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
2583                               1, silc_buffer_datalen(idp));
2584
2585   silc_buffer_free(idp);
2586   silc_free(nickname);
2587
2588   /* Notify application */
2589   COMMAND(SILC_STATUS_OK);
2590
2591   /** Wait for command reply */
2592   silc_fsm_next(fsm, silc_client_command_reply_wait);
2593   return SILC_FSM_CONTINUE;
2594 }
2595
2596 /********************************* SERVICE **********************************/
2597
2598 /* Command SERVICE.  Negotiates service agreement with server. */
2599 /* XXX incomplete */
2600
2601 SILC_FSM_STATE(silc_client_command_service)
2602 {
2603   SilcClientCommandContext cmd = fsm_context;
2604 #if 0
2605   SilcClientConnection conn = cmd->conn;
2606   SilcBuffer buffer;
2607   char *name;
2608
2609   if (cmd->argc < 2) {
2610     SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2611         "Usage: /SERVICE [<service name>] [-pubkey]");
2612     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2613     return SILC_FSM_FINISH;
2614   }
2615
2616   name = cmd->argv[1];
2617
2618   /* Send SERVICE command to the server */
2619   buffer = silc_command_payload_encode_va(SILC_COMMAND_SERVICE,
2620                                           ++conn->cmd_ident, 1,
2621                                           1, name, strlen(name));
2622   silc_client_packet_send(conn->client, conn->sock, SILC_PACKET_COMMAND,
2623                           NULL, 0, NULL, NULL, buffer->data,
2624                           buffer->len, TRUE);
2625   silc_buffer_free(buffer);
2626 #endif /* 0 */
2627
2628   /* Notify application */
2629   COMMAND(SILC_STATUS_OK);
2630
2631   /** Wait for command reply */
2632   silc_fsm_next(fsm, silc_client_command_reply_wait);
2633   return SILC_FSM_CONTINUE;
2634 }
2635
2636 /* Register all default commands provided by the client library for the
2637    application. */
2638
2639 void silc_client_commands_register(SilcClient client)
2640 {
2641   silc_list_init(client->internal->commands, struct SilcClientCommandStruct,
2642                  next);
2643
2644   SILC_CLIENT_CMD(whois, WHOIS, "WHOIS", 5);
2645   SILC_CLIENT_CMD(whowas, WHOWAS, "WHOWAS", 3);
2646   SILC_CLIENT_CMD(identify, IDENTIFY, "IDENTIFY", 3);
2647   SILC_CLIENT_CMD(nick, NICK, "NICK", 2);
2648   SILC_CLIENT_CMD(list, LIST, "LIST", 2);
2649   SILC_CLIENT_CMD(topic, TOPIC, "TOPIC", 3);
2650   SILC_CLIENT_CMD(invite, INVITE, "INVITE", 3);
2651   SILC_CLIENT_CMD(quit, QUIT, "QUIT", 2);
2652   SILC_CLIENT_CMD(kill, KILL, "KILL", 4);
2653   SILC_CLIENT_CMD(info, INFO, "INFO", 2);
2654   SILC_CLIENT_CMD(stats, STATS, "STATS", 0);
2655   SILC_CLIENT_CMD(ping, PING, "PING", 2);
2656   SILC_CLIENT_CMD(oper, OPER, "OPER", 3);
2657   SILC_CLIENT_CMD(join, JOIN, "JOIN", 9);
2658   SILC_CLIENT_CMD(motd, MOTD, "MOTD", 2);
2659   SILC_CLIENT_CMD(umode, UMODE, "UMODE", 2);
2660   SILC_CLIENT_CMD(cmode, CMODE, "CMODE", 6);
2661   SILC_CLIENT_CMD(cumode, CUMODE, "CUMODE", 9);
2662   SILC_CLIENT_CMD(kick, KICK, "KICK", 4);
2663   SILC_CLIENT_CMD(ban, BAN, "BAN", 3);
2664   SILC_CLIENT_CMD(detach, DETACH, "DETACH", 0);
2665   SILC_CLIENT_CMD(watch, WATCH, "WATCH", 3);
2666   SILC_CLIENT_CMD(silcoper, SILCOPER, "SILCOPER", 3);
2667   SILC_CLIENT_CMD(leave, LEAVE, "LEAVE", 2);
2668   SILC_CLIENT_CMD(users, USERS, "USERS", 2);
2669   SILC_CLIENT_CMD(getkey, GETKEY, "GETKEY", 2);
2670   SILC_CLIENT_CMD(service, SERVICE, "SERVICE", 10);
2671 }
2672
2673 /* Unregister all commands. */
2674
2675 void silc_client_commands_unregister(SilcClient client)
2676 {
2677   SILC_CLIENT_CMDU(whois, WHOIS, "WHOIS");
2678   SILC_CLIENT_CMDU(whowas, WHOWAS, "WHOWAS");
2679   SILC_CLIENT_CMDU(identify, IDENTIFY, "IDENTIFY");
2680   SILC_CLIENT_CMDU(nick, NICK, "NICK");
2681   SILC_CLIENT_CMDU(list, LIST, "LIST");
2682   SILC_CLIENT_CMDU(topic, TOPIC, "TOPIC");
2683   SILC_CLIENT_CMDU(invite, INVITE, "INVITE");
2684   SILC_CLIENT_CMDU(quit, QUIT, "QUIT");
2685   SILC_CLIENT_CMDU(kill, KILL, "KILL");
2686   SILC_CLIENT_CMDU(info, INFO, "INFO");
2687   SILC_CLIENT_CMDU(stats, STATS, "STATS");
2688   SILC_CLIENT_CMDU(ping, PING, "PING");
2689   SILC_CLIENT_CMDU(oper, OPER, "OPER");
2690   SILC_CLIENT_CMDU(join, JOIN, "JOIN");
2691   SILC_CLIENT_CMDU(motd, MOTD, "MOTD");
2692   SILC_CLIENT_CMDU(umode, UMODE, "UMODE");
2693   SILC_CLIENT_CMDU(cmode, CMODE, "CMODE");
2694   SILC_CLIENT_CMDU(cumode, CUMODE, "CUMODE");
2695   SILC_CLIENT_CMDU(kick, KICK, "KICK");
2696   SILC_CLIENT_CMDU(ban, BAN, "BAN");
2697   SILC_CLIENT_CMDU(detach, DETACH, "DETACH");
2698   SILC_CLIENT_CMDU(watch, WATCH, "WATCH");
2699   SILC_CLIENT_CMDU(silcoper, SILCOPER, "SILCOPER");
2700   SILC_CLIENT_CMDU(leave, LEAVE, "LEAVE");
2701   SILC_CLIENT_CMDU(users, USERS, "USERS");
2702   SILC_CLIENT_CMDU(getkey, GETKEY, "GETKEY");
2703   SILC_CLIENT_CMDU(service, SERVICE, "SERVICE");
2704 }
2705
2706 /****************** Client Side Incoming Command Handling *******************/
2707
2708 /* Reply to WHOIS command from server */
2709
2710 static void silc_client_command_process_whois(SilcClient client,
2711                                               SilcClientConnection conn,
2712                                               SilcCommandPayload payload,
2713                                               SilcArgumentPayload args)
2714 {
2715 #if 0
2716   SilcDList attrs;
2717   unsigned char *tmp;
2718   SilcUInt32 tmp_len;
2719   SilcBuffer buffer, packet;
2720
2721   SILC_LOG_DEBUG(("Received WHOIS command"));
2722
2723   /* Try to take the Requested Attributes */
2724   tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
2725   if (!tmp)
2726     return;
2727
2728   attrs = silc_attribute_payload_parse(tmp, tmp_len);
2729   if (!attrs)
2730     return;
2731
2732   /* Process requested attributes */
2733   buffer = silc_client_attributes_process(client, conn, attrs);
2734   if (!buffer) {
2735     silc_attribute_payload_list_free(attrs);
2736     return;
2737   }
2738
2739   /* Send the attributes back */
2740   packet =
2741     silc_command_reply_payload_encode_va(SILC_COMMAND_WHOIS,
2742                                          SILC_STATUS_OK, 0,
2743                                          silc_command_get_ident(payload),
2744                                          1, 11, buffer->data, buffer->len);
2745   silc_client_packet_send(client, sock, SILC_PACKET_COMMAND_REPLY,
2746                           NULL, 0, NULL, NULL, packet->data,
2747                           packet->len, TRUE);
2748   silc_buffer_free(packet);
2749   silc_buffer_free(buffer);
2750 #endif /* 0 */
2751 }
2752
2753 /* Client is able to receive some command packets even though they are
2754    special case.  Server may send WHOIS command to the client to retrieve
2755    Requested Attributes information for WHOIS query the server is
2756    processing. This function currently handles only the WHOIS command,
2757    but if in the future more commands may arrive then this can be made
2758    to support other commands too. */
2759
2760 SILC_FSM_STATE(silc_client_command)
2761 {
2762   SilcClientConnection conn = fsm_context;
2763   SilcClient client = conn->client;
2764   SilcPacket packet = state_context;
2765   SilcCommandPayload payload;
2766   SilcCommand command;
2767   SilcArgumentPayload args;
2768
2769   /* Get command payload from packet */
2770   payload = silc_command_payload_parse(packet->buffer.data,
2771                                        silc_buffer_len(&packet->buffer));
2772   if (!payload) {
2773     SILC_LOG_DEBUG(("Bad command packet"));
2774     return SILC_FSM_FINISH;
2775   }
2776
2777   /* Get arguments */
2778   args = silc_command_get_args(payload);
2779
2780   /* Get the command */
2781   command = silc_command_get(payload);
2782   switch (command) {
2783
2784   case SILC_COMMAND_WHOIS:
2785     /* Ignore everything if requested by application */
2786     if (client->internal->params->ignore_requested_attributes)
2787       break;
2788
2789     silc_client_command_process_whois(client, conn, payload, args);
2790     break;
2791
2792   default:
2793     break;
2794   }
2795
2796   silc_command_payload_free(payload);
2797   return SILC_FSM_FINISH;
2798 }