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   /* Send the command */
1205   silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1206                               1, silc_buffer_datalen(conn->internal->
1207                                                      remote_idp));
1208
1209   /* Notify application */
1210   COMMAND(SILC_STATUS_OK);
1211
1212   /** Wait for command reply */
1213   silc_fsm_next(fsm, silc_client_command_reply_wait);
1214   return SILC_FSM_CONTINUE;
1215 }
1216
1217 /********************************** PING ************************************/
1218
1219 /* Command PING. Sends ping to server. */
1220
1221 SILC_FSM_STATE(silc_client_command_ping)
1222 {
1223   SilcClientCommandContext cmd = fsm_context;
1224   SilcClientConnection conn = cmd->conn;
1225
1226   if (cmd->argc < 2) {
1227     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1228     return SILC_FSM_FINISH;
1229   }
1230
1231   /* Send the command */
1232   silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1233                               1, silc_buffer_datalen(conn->internal->
1234                                                      remote_idp));
1235
1236   /* Save ping time */
1237   cmd->context = SILC_64_TO_PTR(silc_time());
1238
1239   /* Notify application */
1240   COMMAND(SILC_STATUS_OK);
1241
1242   /** Wait for command reply */
1243   silc_fsm_next(fsm, silc_client_command_reply_wait);
1244   return SILC_FSM_CONTINUE;
1245 }
1246
1247 /********************************** JOIN ************************************/
1248
1249 /* Command JOIN. Joins to a channel. */
1250
1251 SILC_FSM_STATE(silc_client_command_join)
1252 {
1253   SilcClientCommandContext cmd = fsm_context;
1254   SilcClientConnection conn = cmd->conn;
1255   SilcChannelEntry channel;
1256   SilcBuffer auth = NULL, cauth = NULL;
1257   char *name, *passphrase = NULL, *pu8, *cipher = NULL, *hmac = NULL;
1258   int i, passphrase_len = 0;
1259
1260   if (cmd->argc < 2) {
1261     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1262     goto out;
1263   }
1264
1265   /* See if we have joined to the requested channel already */
1266   channel = silc_client_get_channel(conn->client, conn, cmd->argv[1]);
1267   if (channel && silc_client_on_channel(channel, conn->local_entry))
1268     goto out;
1269
1270   if (cmd->argv_lens[1] > 256)
1271     cmd->argv_lens[1] = 256;
1272
1273   name = cmd->argv[1];
1274
1275   for (i = 2; i < cmd->argc; i++) {
1276     if (!strcasecmp(cmd->argv[i], "-cipher") && cmd->argc > i + 1) {
1277       cipher = cmd->argv[++i];
1278     } else if (!strcasecmp(cmd->argv[i], "-hmac") && cmd->argc > i + 1) {
1279       hmac = cmd->argv[++i];
1280     } else if (!strcasecmp(cmd->argv[i], "-founder")) {
1281       auth = silc_auth_public_key_auth_generate(conn->public_key,
1282                                                 conn->private_key,
1283                                                 conn->client->rng,
1284                                                 conn->internal->sha1hash,
1285                                                 conn->local_id,
1286                                                 SILC_ID_CLIENT);
1287     } else if (!strcasecmp(cmd->argv[i], "-auth")) {
1288       SilcPublicKey pubkey = conn->public_key;
1289       SilcPrivateKey privkey = conn->private_key;
1290       unsigned char *pk, pkhash[SILC_HASH_MAXLEN], *pubdata;
1291       SilcUInt32 pk_len;
1292
1293       if (cmd->argc >= i + 3) {
1294         char *pass = "";
1295         if (cmd->argc >= i + 4) {
1296           pass = cmd->argv[i + 3];
1297           i++;
1298         }
1299         if (!silc_load_key_pair(cmd->argv[i + 1], cmd->argv[i + 2], pass,
1300                                 &pubkey, &privkey)) {
1301           SAY(conn->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1302               "Could not load key pair, check your arguments");
1303           COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1304           goto out;
1305         }
1306         i += 2;
1307       }
1308
1309       pk = silc_pkcs_public_key_encode(pubkey, &pk_len);
1310       silc_hash_make(conn->internal->sha1hash, pk, pk_len, pkhash);
1311       silc_free(pk);
1312       pubdata = silc_rng_get_rn_data(conn->client->rng, 128);
1313       memcpy(pubdata, pkhash, 20);
1314       cauth = silc_auth_public_key_auth_generate_wpub(pubkey, privkey,
1315                                                       pubdata, 128,
1316                                                       conn->internal->sha1hash,
1317                                                       conn->local_id,
1318                                                       SILC_ID_CLIENT);
1319       memset(pubdata, 0, 128);
1320       silc_free(pubdata);
1321     } else {
1322       /* Passphrases must be UTF-8 encoded, so encode if it is not */
1323       if (!silc_utf8_valid(cmd->argv[i], cmd->argv_lens[i])) {
1324         passphrase_len = silc_utf8_encoded_len(cmd->argv[i],
1325                                                cmd->argv_lens[i], 0);
1326         pu8 = silc_calloc(passphrase_len, sizeof(*pu8));
1327         passphrase_len = silc_utf8_encode(cmd->argv[i], cmd->argv_lens[i],
1328                                           0, pu8, passphrase_len);
1329         passphrase = pu8;
1330       } else {
1331         passphrase = strdup(cmd->argv[i]);
1332         passphrase_len = cmd->argv_lens[i];
1333       }
1334     }
1335   }
1336
1337   /* Send JOIN command to the server */
1338   silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 7,
1339                               1, name, strlen(name),
1340                               2, silc_buffer_datalen(conn->internal->
1341                                                      local_idp),
1342                               3, passphrase, passphrase_len,
1343                               4, cipher, cipher ? strlen(cipher) : 0,
1344                               5, hmac, hmac ? strlen(hmac) : 0,
1345                               6, silc_buffer_datalen(auth),
1346                               7, silc_buffer_datalen(cauth));
1347
1348   silc_buffer_free(auth);
1349   silc_buffer_free(cauth);
1350   if (passphrase)
1351     memset(passphrase, 0, strlen(passphrase));
1352   silc_free(passphrase);
1353
1354   /* Notify application */
1355   COMMAND(SILC_STATUS_OK);
1356
1357   /** Wait for command reply */
1358   silc_fsm_next(fsm, silc_client_command_reply_wait);
1359   return SILC_FSM_CONTINUE;
1360
1361  out:
1362   return SILC_FSM_FINISH;
1363 }
1364
1365 /********************************** MOTD ************************************/
1366
1367 /* MOTD command. Requests motd from server. */
1368
1369 SILC_FSM_STATE(silc_client_command_motd)
1370 {
1371   SilcClientCommandContext cmd = fsm_context;
1372   SilcClientConnection conn = cmd->conn;
1373
1374   if (cmd->argc < 1 || cmd->argc > 2) {
1375     SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1376         "Usage: /MOTD [<server>]");
1377     COMMAND_ERROR((cmd->argc < 1 ? SILC_STATUS_ERR_NOT_ENOUGH_PARAMS :
1378                    SILC_STATUS_ERR_TOO_MANY_PARAMS));
1379     return SILC_FSM_FINISH;
1380   }
1381
1382   /* Send the command */
1383   if (cmd->argc == 1)
1384     silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1385                                 1, conn->remote_host,
1386                                 strlen(conn->remote_host));
1387   else
1388     silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1389                                 1, cmd->argv[1], cmd->argv_lens[1]);
1390
1391   /* Notify application */
1392   COMMAND(SILC_STATUS_OK);
1393
1394   /** Wait for command reply */
1395   silc_fsm_next(fsm, silc_client_command_reply_wait);
1396   return SILC_FSM_CONTINUE;
1397 }
1398
1399 /********************************** UMODE ***********************************/
1400
1401 /* UMODE. Set/unset user mode in SILC. This is used mainly to unset the
1402    modes as client cannot set itself server/router operator privileges. */
1403
1404 SILC_FSM_STATE(silc_client_command_umode)
1405 {
1406   SilcClientCommandContext cmd = fsm_context;
1407   SilcClientConnection conn = cmd->conn;
1408   unsigned char *cp, modebuf[4];
1409   SilcUInt32 mode, add, len;
1410   int i;
1411
1412   if (cmd->argc < 2) {
1413     SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1414         "Usage: /UMODE +|-<modes>");
1415     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1416     return SILC_FSM_FINISH;
1417   }
1418
1419   mode = conn->local_entry->mode;
1420
1421   /* Are we adding or removing mode */
1422   if (cmd->argv[1][0] == '-')
1423     add = FALSE;
1424   else
1425     add = TRUE;
1426
1427   /* Parse mode */
1428   cp = cmd->argv[1] + 1;
1429   len = strlen(cp);
1430   for (i = 0; i < len; i++) {
1431     switch(cp[i]) {
1432     case 'a':
1433       if (add) {
1434         mode = 0;
1435         mode |= SILC_UMODE_SERVER_OPERATOR;
1436         mode |= SILC_UMODE_ROUTER_OPERATOR;
1437         mode |= SILC_UMODE_GONE;
1438         mode |= SILC_UMODE_INDISPOSED;
1439         mode |= SILC_UMODE_BUSY;
1440         mode |= SILC_UMODE_PAGE;
1441         mode |= SILC_UMODE_HYPER;
1442         mode |= SILC_UMODE_ROBOT;
1443         mode |= SILC_UMODE_BLOCK_PRIVMSG;
1444         mode |= SILC_UMODE_REJECT_WATCHING;
1445       } else {
1446         mode = SILC_UMODE_NONE;
1447       }
1448       break;
1449     case 's':
1450       if (add)
1451         mode |= SILC_UMODE_SERVER_OPERATOR;
1452       else
1453         mode &= ~SILC_UMODE_SERVER_OPERATOR;
1454       break;
1455     case 'r':
1456       if (add)
1457         mode |= SILC_UMODE_ROUTER_OPERATOR;
1458       else
1459         mode &= ~SILC_UMODE_ROUTER_OPERATOR;
1460       break;
1461     case 'g':
1462       if (add)
1463         mode |= SILC_UMODE_GONE;
1464       else
1465         mode &= ~SILC_UMODE_GONE;
1466       break;
1467     case 'i':
1468       if (add)
1469         mode |= SILC_UMODE_INDISPOSED;
1470       else
1471         mode &= ~SILC_UMODE_INDISPOSED;
1472       break;
1473     case 'b':
1474       if (add)
1475         mode |= SILC_UMODE_BUSY;
1476       else
1477         mode &= ~SILC_UMODE_BUSY;
1478       break;
1479     case 'p':
1480       if (add)
1481         mode |= SILC_UMODE_PAGE;
1482       else
1483         mode &= ~SILC_UMODE_PAGE;
1484       break;
1485     case 'h':
1486       if (add)
1487         mode |= SILC_UMODE_HYPER;
1488       else
1489         mode &= ~SILC_UMODE_HYPER;
1490       break;
1491     case 't':
1492       if (add)
1493         mode |= SILC_UMODE_ROBOT;
1494       else
1495         mode &= ~SILC_UMODE_ROBOT;
1496       break;
1497     case 'P':
1498       if (add)
1499         mode |= SILC_UMODE_BLOCK_PRIVMSG;
1500       else
1501         mode &= ~SILC_UMODE_BLOCK_PRIVMSG;
1502       break;
1503     case 'w':
1504       if (add)
1505         mode |= SILC_UMODE_REJECT_WATCHING;
1506       else
1507         mode &= ~SILC_UMODE_REJECT_WATCHING;
1508       break;
1509     case 'I':
1510       if (add)
1511         mode |= SILC_UMODE_BLOCK_INVITE;
1512       else
1513         mode &= ~SILC_UMODE_BLOCK_INVITE;
1514       break;
1515     default:
1516       COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE);
1517       return SILC_FSM_FINISH;
1518       break;
1519     }
1520   }
1521
1522   SILC_PUT32_MSB(mode, modebuf);
1523
1524   /* Send the command */
1525   silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
1526                               1, silc_buffer_datalen(conn->internal->
1527                                                      local_idp),
1528                               2, modebuf, sizeof(modebuf));
1529
1530   /* Notify application */
1531   COMMAND(SILC_STATUS_OK);
1532
1533   /** Wait for command reply */
1534   silc_fsm_next(fsm, silc_client_command_reply_wait);
1535   return SILC_FSM_CONTINUE;
1536 }
1537
1538 /********************************** CMODE ***********************************/
1539
1540 /* CMODE command. Sets channel mode. Modes that does not require any arguments
1541    can be set several at once. Those modes that require argument must be set
1542    separately (unless set with modes that does not require arguments). */
1543
1544 SILC_FSM_STATE(silc_client_command_cmode)
1545 {
1546   SilcClientCommandContext cmd = fsm_context;
1547   SilcClientConnection conn = cmd->conn;
1548   SilcClient client = conn->client;
1549   SilcChannelEntry channel;
1550   SilcBuffer chidp, auth = NULL, pk = NULL;
1551   unsigned char *name, *cp, modebuf[4], tmp[4], *arg = NULL;
1552   SilcUInt32 mode, add, type, len, arg_len = 0;
1553   int i;
1554
1555   if (cmd->argc < 3) {
1556     SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1557         "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1558     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1559     goto out;
1560   }
1561
1562   if (cmd->argv[1][0] == '*') {
1563     if (!conn->current_channel) {
1564       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1565       goto out;
1566     }
1567
1568     channel = conn->current_channel;
1569   } else {
1570     name = cmd->argv[1];
1571
1572     channel = silc_client_get_channel(conn->client, conn, name);
1573     if (!channel) {
1574       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1575       goto out;
1576     }
1577   }
1578
1579   mode = channel->mode;
1580
1581   /* Are we adding or removing mode */
1582   if (cmd->argv[2][0] == '-')
1583     add = FALSE;
1584   else
1585     add = TRUE;
1586
1587   /* Argument type to be sent to server */
1588   type = 0;
1589
1590   /* Parse mode */
1591   cp = cmd->argv[2] + 1;
1592   len = strlen(cp);
1593   for (i = 0; i < len; i++) {
1594     switch(cp[i]) {
1595     case 'p':
1596       if (add)
1597         mode |= SILC_CHANNEL_MODE_PRIVATE;
1598       else
1599         mode &= ~SILC_CHANNEL_MODE_PRIVATE;
1600       break;
1601     case 's':
1602       if (add)
1603         mode |= SILC_CHANNEL_MODE_SECRET;
1604       else
1605         mode &= ~SILC_CHANNEL_MODE_SECRET;
1606       break;
1607     case 'k':
1608       if (add)
1609         mode |= SILC_CHANNEL_MODE_PRIVKEY;
1610       else
1611         mode &= ~SILC_CHANNEL_MODE_PRIVKEY;
1612       break;
1613     case 'i':
1614       if (add)
1615         mode |= SILC_CHANNEL_MODE_INVITE;
1616       else
1617         mode &= ~SILC_CHANNEL_MODE_INVITE;
1618       break;
1619     case 't':
1620       if (add)
1621         mode |= SILC_CHANNEL_MODE_TOPIC;
1622       else
1623         mode &= ~SILC_CHANNEL_MODE_TOPIC;
1624       break;
1625     case 'm':
1626       if (add)
1627         mode |= SILC_CHANNEL_MODE_SILENCE_USERS;
1628       else
1629         mode &= ~SILC_CHANNEL_MODE_SILENCE_USERS;
1630       break;
1631     case 'M':
1632       if (add)
1633         mode |= SILC_CHANNEL_MODE_SILENCE_OPERS;
1634       else
1635         mode &= ~SILC_CHANNEL_MODE_SILENCE_OPERS;
1636       break;
1637     case 'l':
1638       if (add) {
1639         int ll;
1640         mode |= SILC_CHANNEL_MODE_ULIMIT;
1641         type = 3;
1642         if (cmd->argc < 4) {
1643           SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1644               "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1645           COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1646           goto out;
1647         }
1648         ll = atoi(cmd->argv[3]);
1649         SILC_PUT32_MSB(ll, tmp);
1650         arg = tmp;
1651         arg_len = 4;
1652       } else {
1653         mode &= ~SILC_CHANNEL_MODE_ULIMIT;
1654       }
1655       break;
1656     case 'a':
1657       if (add) {
1658         mode |= SILC_CHANNEL_MODE_PASSPHRASE;
1659         type = 4;
1660         if (cmd->argc < 4) {
1661           SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1662               "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1663           COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1664           goto out;
1665         }
1666         arg = cmd->argv[3];
1667         arg_len = cmd->argv_lens[3];
1668       } else {
1669         mode &= ~SILC_CHANNEL_MODE_PASSPHRASE;
1670       }
1671       break;
1672     case 'c':
1673       if (add) {
1674         mode |= SILC_CHANNEL_MODE_CIPHER;
1675         type = 5;
1676         if (cmd->argc < 4) {
1677           SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1678               "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1679           COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1680           goto out;
1681         }
1682         arg = cmd->argv[3];
1683         arg_len = cmd->argv_lens[3];
1684       } else {
1685         mode &= ~SILC_CHANNEL_MODE_CIPHER;
1686       }
1687       break;
1688     case 'h':
1689       if (add) {
1690         mode |= SILC_CHANNEL_MODE_HMAC;
1691         type = 6;
1692         if (cmd->argc < 4) {
1693           SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1694               "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1695           COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1696           goto out;
1697         }
1698         arg = cmd->argv[3];
1699         arg_len = cmd->argv_lens[3];
1700       } else {
1701         mode &= ~SILC_CHANNEL_MODE_HMAC;
1702       }
1703       break;
1704     case 'f':
1705       if (add) {
1706         SilcPublicKey pubkey = conn->public_key;
1707         SilcPrivateKey privkey = conn->private_key;
1708
1709         mode |= SILC_CHANNEL_MODE_FOUNDER_AUTH;
1710         type = 7;
1711
1712         if (cmd->argc >= 5) {
1713           char *pass = "";
1714           if (cmd->argc >= 6)
1715             pass = cmd->argv[5];
1716           if (!silc_load_key_pair(cmd->argv[3], cmd->argv[4], pass,
1717                                   &pubkey, &privkey)) {
1718             SAY(client, conn, SILC_CLIENT_MESSAGE_ERROR,
1719                 "Could not load key pair, check your arguments");
1720             COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1721             goto out;
1722           }
1723         }
1724
1725         pk = silc_public_key_payload_encode(pubkey);
1726         auth = silc_auth_public_key_auth_generate(pubkey, privkey,
1727                                                   conn->client->rng,
1728                                                   conn->internal->sha1hash,
1729                                                   conn->local_id,
1730                                                   SILC_ID_CLIENT);
1731         arg = silc_buffer_data(auth);
1732         arg_len = silc_buffer_len(auth);
1733       } else {
1734         mode &= ~SILC_CHANNEL_MODE_FOUNDER_AUTH;
1735       }
1736       break;
1737     case 'C':
1738       if (add) {
1739         int k;
1740         SilcBool chadd = FALSE;
1741         SilcPublicKey chpk = NULL;
1742
1743         mode |= SILC_CHANNEL_MODE_CHANNEL_AUTH;
1744         type = 9;
1745
1746         if (cmd->argc == 3) {
1747           /* Send empty command to receive the public key list. */
1748           chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
1749           silc_client_command_send_va(conn, cmd, SILC_COMMAND_CMODE,
1750                                       NULL, NULL, 1,
1751                                       1, silc_buffer_datalen(chidp));
1752           silc_buffer_free(chidp);
1753
1754           /* Notify application */
1755           COMMAND(SILC_STATUS_OK);
1756           goto out;
1757         }
1758
1759         if (cmd->argc >= 4) {
1760           auth = silc_buffer_alloc_size(2);
1761           silc_buffer_format(auth,
1762                              SILC_STR_UI_SHORT(cmd->argc - 3),
1763                              SILC_STR_END);
1764         }
1765
1766         for (k = 3; k < cmd->argc; k++) {
1767           if (cmd->argv[k][0] == '+')
1768             chadd = TRUE;
1769           if (!silc_pkcs_load_public_key(cmd->argv[k] + 1, &chpk)) {
1770             SAY(conn->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1771                 "Could not load public key %s, check the filename",
1772                 cmd->argv[k]);
1773             COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1774             silc_buffer_free(auth);
1775             goto out;
1776           }
1777
1778           if (chpk) {
1779             pk = silc_public_key_payload_encode(chpk);
1780             auth = silc_argument_payload_encode_one(auth,
1781                                                     silc_buffer_datalen(pk),
1782                                                     chadd ? 0x00 : 0x01);
1783             silc_pkcs_public_key_free(chpk);
1784             silc_buffer_free(pk);
1785             pk = NULL;
1786           }
1787         }
1788
1789         arg = silc_buffer_data(auth);
1790         arg_len = silc_buffer_len(auth);
1791       } else {
1792         mode &= ~SILC_CHANNEL_MODE_CHANNEL_AUTH;
1793       }
1794       break;
1795     default:
1796       COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE);
1797       goto out;
1798       break;
1799     }
1800   }
1801
1802   chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
1803   SILC_PUT32_MSB(mode, modebuf);
1804
1805   /* Send the command. We support sending only one mode at once that
1806      requires an argument. */
1807   if (type && arg) {
1808     silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 4,
1809                                 1, silc_buffer_datalen(chidp),
1810                                 2, modebuf, sizeof(modebuf),
1811                                 type, arg, arg_len,
1812                                 8, silc_buffer_datalen(pk));
1813   } else {
1814     silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
1815                                 1, silc_buffer_datalen(chidp),
1816                                 2, modebuf, sizeof(modebuf));
1817   }
1818
1819   silc_buffer_free(chidp);
1820   silc_buffer_free(auth);
1821   silc_buffer_free(pk);
1822
1823   /* Notify application */
1824   COMMAND(SILC_STATUS_OK);
1825
1826   /** Wait for command reply */
1827   silc_fsm_next(fsm, silc_client_command_reply_wait);
1828   return SILC_FSM_CONTINUE;
1829
1830  out:
1831   return SILC_FSM_FINISH;
1832 }
1833
1834 /********************************* CUMODE ***********************************/
1835
1836 /* CUMODE command. Changes client's mode on a channel. */
1837
1838 SILC_FSM_STATE(silc_client_command_cumode)
1839 {
1840   SilcClientCommandContext cmd = fsm_context;
1841   SilcClientConnection conn = cmd->conn;
1842   SilcClient client = conn->client;
1843   SilcChannelEntry channel;
1844   SilcChannelUser chu;
1845   SilcClientEntry client_entry;
1846   SilcBuffer clidp, chidp, auth = NULL;
1847   SilcDList clients = NULL;
1848   unsigned char *name, *cp, modebuf[4];
1849   SilcUInt32 mode = 0, add, len;
1850   char *nickname = NULL;
1851   int i;
1852
1853   if (cmd->argc < 4) {
1854     SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1855         "Usage: /CUMODE <channel> +|-<modes> <nickname>[@<server>]");
1856     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1857     goto out;
1858   }
1859
1860   if (cmd->argv[1][0] == '*') {
1861     if (!conn->current_channel) {
1862       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1863       goto out;
1864     }
1865
1866     channel = conn->current_channel;
1867   } else {
1868     name = cmd->argv[1];
1869
1870     channel = silc_client_get_channel(conn->client, conn, name);
1871     if (!channel) {
1872       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1873       goto out;
1874     }
1875   }
1876
1877   /* Parse the typed nickname. */
1878   if (client->internal->params->nickname_parse)
1879     client->internal->params->nickname_parse(cmd->argv[3], &nickname);
1880   else
1881     nickname = strdup(cmd->argv[3]);
1882
1883   /* Find client entry */
1884   clients = silc_client_get_clients_local(client, conn, nickname,
1885                                           cmd->argv[3]);
1886   if (!clients)
1887     /* Resolve client information */
1888     SILC_FSM_CALL(silc_client_get_clients(client, conn, nickname, cmd->argv[3],
1889                                           silc_client_command_resolve_continue,
1890                                           cmd));
1891
1892   client_entry = silc_dlist_get(clients);
1893
1894   /* Get the current mode */
1895   chu = silc_client_on_channel(channel, client_entry);
1896   if (chu)
1897     mode = chu->mode;
1898
1899   /* Are we adding or removing mode */
1900   if (cmd->argv[2][0] == '-')
1901     add = FALSE;
1902   else
1903     add = TRUE;
1904
1905   /* Parse mode */
1906   cp = cmd->argv[2] + 1;
1907   len = strlen(cp);
1908   for (i = 0; i < len; i++) {
1909     switch(cp[i]) {
1910     case 'a':
1911       if (add) {
1912         mode |= SILC_CHANNEL_UMODE_CHANFO;
1913         mode |= SILC_CHANNEL_UMODE_CHANOP;
1914         mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
1915         mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
1916         mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
1917       } else {
1918         mode = SILC_CHANNEL_UMODE_NONE;
1919       }
1920       break;
1921     case 'f':
1922       if (add) {
1923         SilcPublicKey pubkey = conn->public_key;
1924         SilcPrivateKey privkey = conn->private_key;
1925
1926         if (cmd->argc >= 6) {
1927           char *pass = "";
1928           if (cmd->argc >= 7)
1929             pass = cmd->argv[6];
1930           if (!silc_load_key_pair(cmd->argv[4], cmd->argv[5], pass,
1931                                   &pubkey, &privkey)) {
1932             SAY(conn->client, conn, SILC_CLIENT_MESSAGE_ERROR,
1933                 "Could not load key pair, check your arguments");
1934             COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1935             goto out;
1936           }
1937         }
1938
1939         auth = silc_auth_public_key_auth_generate(pubkey, privkey,
1940                                                   conn->client->rng,
1941                                                   conn->internal->sha1hash,
1942                                                   conn->local_id,
1943                                                   SILC_ID_CLIENT);
1944         mode |= SILC_CHANNEL_UMODE_CHANFO;
1945       } else {
1946         mode &= ~SILC_CHANNEL_UMODE_CHANFO;
1947       }
1948       break;
1949     case 'o':
1950       if (add)
1951         mode |= SILC_CHANNEL_UMODE_CHANOP;
1952       else
1953         mode &= ~SILC_CHANNEL_UMODE_CHANOP;
1954       break;
1955     case 'b':
1956       if (add)
1957         mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
1958       else
1959         mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
1960       break;
1961     case 'u':
1962       if (add)
1963         mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
1964       else
1965         mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
1966       break;
1967     case 'r':
1968       if (add)
1969         mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
1970       else
1971         mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
1972       break;
1973     case 'q':
1974       if (add)
1975         mode |= SILC_CHANNEL_UMODE_QUIET;
1976       else
1977         mode &= ~SILC_CHANNEL_UMODE_QUIET;
1978       break;
1979     default:
1980       COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE);
1981       goto out;
1982       break;
1983     }
1984   }
1985
1986   chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
1987   SILC_PUT32_MSB(mode, modebuf);
1988   clidp = silc_id_payload_encode(&client_entry->id, SILC_ID_CLIENT);
1989
1990   /* Send the command packet. We support sending only one mode at once
1991      that requires an argument. */
1992   silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, auth ? 4 : 3,
1993                               1, silc_buffer_datalen(chidp),
1994                               2, modebuf, 4,
1995                               3, silc_buffer_datalen(clidp),
1996                               4, silc_buffer_datalen(auth));
1997
1998   silc_buffer_free(chidp);
1999   silc_buffer_free(clidp);
2000   if (auth)
2001     silc_buffer_free(auth);
2002   silc_free(nickname);
2003   silc_client_list_free(client, conn, clients);
2004
2005   /* Notify application */
2006   COMMAND(SILC_STATUS_OK);
2007
2008   /** Wait for command reply */
2009   silc_fsm_next(fsm, silc_client_command_reply_wait);
2010   return SILC_FSM_CONTINUE;
2011
2012  out:
2013   silc_client_list_free(client, conn, clients);
2014   silc_free(nickname);
2015   return SILC_FSM_FINISH;
2016 }
2017
2018 /********************************** KICK ************************************/
2019
2020 /* KICK command. Kicks a client out of channel. */
2021
2022 SILC_FSM_STATE(silc_client_command_kick)
2023 {
2024   SilcClientCommandContext cmd = fsm_context;
2025   SilcClientConnection conn = cmd->conn;
2026   SilcClient client = conn->client;
2027   SilcChannelEntry channel;
2028   SilcBuffer idp, idp2;
2029   SilcClientEntry target;
2030   SilcDList clients = NULL;
2031   char *name;
2032   char *nickname = NULL;
2033
2034   if (cmd->argc < 3) {
2035     SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2036         "Usage: /KICK <channel> <nickname> [<comment>]");
2037     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2038     goto out;
2039   }
2040
2041   if (cmd->argv[1][0] == '*') {
2042     if (!conn->current_channel) {
2043       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2044       goto out;
2045     }
2046     name = conn->current_channel->channel_name;
2047   } else {
2048     name = cmd->argv[1];
2049   }
2050
2051   if (!conn->current_channel) {
2052     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2053     goto out;
2054   }
2055
2056   /* Get the Channel ID of the channel */
2057   channel = silc_client_get_channel(conn->client, conn, name);
2058   if (!channel) {
2059     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2060     goto out;
2061   }
2062
2063   /* Parse the typed nickname. */
2064   if (client->internal->params->nickname_parse)
2065     client->internal->params->nickname_parse(cmd->argv[2], &nickname);
2066   else
2067     nickname = strdup(cmd->argv[2]);
2068
2069   /* Get the target client */
2070   clients = silc_client_get_clients_local(client, conn, nickname,
2071                                           cmd->argv[2]);
2072   if (!clients) {
2073     SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2074         "No such client: %s", cmd->argv[2]);
2075     COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_NICK);
2076     goto out;
2077   }
2078   target = silc_dlist_get(clients);
2079
2080   /* Send KICK command to the server */
2081   idp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2082   idp2 = silc_id_payload_encode(&target->id, SILC_ID_CLIENT);
2083   if (cmd->argc == 3)
2084     silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
2085                                 1, silc_buffer_datalen(idp),
2086                                 2, silc_buffer_datalen(idp2));
2087   else
2088     silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 3,
2089                                 1, silc_buffer_datalen(idp),
2090                                 2, silc_buffer_datalen(idp2),
2091                                 3, cmd->argv[3], strlen(cmd->argv[3]));
2092
2093   silc_buffer_free(idp);
2094   silc_buffer_free(idp2);
2095   silc_free(nickname);
2096   silc_client_list_free(client, conn, clients);
2097
2098   /* Notify application */
2099   COMMAND(SILC_STATUS_OK);
2100
2101   /** Wait for command reply */
2102   silc_fsm_next(fsm, silc_client_command_reply_wait);
2103   return SILC_FSM_CONTINUE;
2104
2105  out:
2106   silc_free(nickname);
2107   return SILC_FSM_FINISH;
2108 }
2109
2110 /***************************** OPER & SILCOPER ******************************/
2111
2112 typedef struct {
2113   unsigned char *passphrase;
2114   SilcUInt32 passphrase_len;
2115 } *SilcClientCommandOper;
2116
2117 /* Ask passphrase callback */
2118
2119 static void silc_client_command_oper_cb(unsigned char *data,
2120                                         SilcUInt32 data_len, void *context)
2121 {
2122   SilcClientCommandContext cmd = context;
2123   SilcClientCommandOper oper = cmd->context;
2124
2125   if (data && data_len)
2126     oper->passphrase = silc_memdup(data, data_len);
2127   oper->passphrase_len = data_len;
2128
2129   /* Continue */
2130   SILC_FSM_CALL_CONTINUE(&cmd->thread);
2131 }
2132
2133 /* Send OPER/SILCOPER command */
2134
2135 SILC_FSM_STATE(silc_client_command_oper_send)
2136 {
2137   SilcClientCommandContext cmd = fsm_context;
2138   SilcClientConnection conn = cmd->conn;
2139   SilcClientCommandOper oper = cmd->context;
2140   SilcBuffer auth;
2141
2142   if (!oper || !oper->passphrase) {
2143     /* Encode the public key authentication payload */
2144     auth = silc_auth_public_key_auth_generate(conn->public_key,
2145                                               conn->private_key,
2146                                               conn->client->rng,
2147                                               conn->internal->hash,
2148                                               conn->local_id,
2149                                               SILC_ID_CLIENT);
2150   } else {
2151     /* Encode the password authentication payload */
2152     auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
2153                                     oper->passphrase, oper->passphrase_len);
2154   }
2155
2156   silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
2157                               1, cmd->argv[1], strlen(cmd->argv[1]),
2158                               2, silc_buffer_datalen(auth));
2159
2160   silc_buffer_clear(auth);
2161   silc_buffer_free(auth);
2162   if (oper) {
2163     silc_free(oper->passphrase);
2164     silc_free(oper);
2165   }
2166
2167   /* Notify application */
2168   COMMAND(SILC_STATUS_OK);
2169
2170   /** Wait for command reply */
2171   silc_fsm_next(fsm, silc_client_command_reply_wait);
2172   return SILC_FSM_CONTINUE;
2173 }
2174
2175 /* OPER command. Used to obtain server operator privileges. */
2176
2177 SILC_FSM_STATE(silc_client_command_oper)
2178 {
2179   SilcClientCommandContext cmd = fsm_context;
2180   SilcClientConnection conn = cmd->conn;
2181   SilcClientCommandOper oper;
2182
2183   if (cmd->argc < 2) {
2184     SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2185         "Usage: /OPER <username> [-pubkey]");
2186     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2187     return SILC_FSM_FINISH;
2188   }
2189
2190   /* Get passphrase */
2191   if (cmd->argc < 3) {
2192     oper = silc_calloc(1, sizeof(*oper));
2193     if (!oper)
2194       return SILC_FSM_FINISH;
2195     cmd->context = oper;
2196     SILC_FSM_CALL(conn->client->internal->
2197                   ops->ask_passphrase(conn->client, conn,
2198                                       silc_client_command_oper_cb, cmd));
2199   }
2200
2201   silc_fsm_next(fsm, silc_client_command_oper_send);
2202   return SILC_FSM_CONTINUE;
2203 }
2204
2205 /* SILCOPER command. Used to obtain router operator privileges. */
2206
2207 SILC_FSM_STATE(silc_client_command_silcoper)
2208 {
2209   SilcClientCommandContext cmd = fsm_context;
2210   SilcClientConnection conn = cmd->conn;
2211   SilcClientCommandOper oper;
2212
2213   if (cmd->argc < 2) {
2214     SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2215         "Usage: /SILCOPER <username> [-pubkey]");
2216     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2217     return SILC_FSM_FINISH;
2218   }
2219
2220   /* Get passphrase */
2221   if (cmd->argc < 3) {
2222     oper = silc_calloc(1, sizeof(*oper));
2223     if (!oper)
2224       return SILC_FSM_FINISH;
2225     cmd->context = oper;
2226     SILC_FSM_CALL(conn->client->internal->
2227                   ops->ask_passphrase(conn->client, conn,
2228                                       silc_client_command_oper_cb, cmd));
2229   }
2230
2231   silc_fsm_next(fsm, silc_client_command_oper_send);
2232   return SILC_FSM_CONTINUE;
2233 }
2234
2235 /*********************************** BAN ************************************/
2236
2237 /* Command BAN. This is used to manage the ban list of the channel. */
2238
2239 SILC_FSM_STATE(silc_client_command_ban)
2240 {
2241   SilcClientCommandContext cmd = fsm_context;
2242   SilcClientConnection conn = cmd->conn;
2243   SilcChannelEntry channel;
2244   SilcBuffer chidp, args = NULL;
2245   char *name, *ban = NULL;
2246   unsigned char action[1];
2247   SilcPublicKey pubkey = NULL;
2248
2249   if (cmd->argc < 2) {
2250     SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2251         "Usage: /BAN <channel> "
2252         "[+|-[<nickname>[@<server>[!<username>[@hostname>]]]]]");
2253     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2254     goto out;
2255   }
2256
2257   if (cmd->argv[1][0] == '*') {
2258     if (!conn->current_channel) {
2259       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2260       goto out;
2261     }
2262
2263     channel = conn->current_channel;
2264   } else {
2265     name = cmd->argv[1];
2266
2267     channel = silc_client_get_channel(conn->client, conn, name);
2268     if (!channel) {
2269       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2270       goto out;
2271     }
2272   }
2273
2274   if (cmd->argc == 3) {
2275     if (cmd->argv[2][0] == '+')
2276       action[0] = 0x00;
2277     else
2278       action[0] = 0x01;
2279
2280     /* Check if it is public key file to be added to invite list */
2281     silc_pkcs_load_public_key(cmd->argv[2] + 1, &pubkey);
2282     ban = cmd->argv[2];
2283     if (!pubkey)
2284       ban++;
2285   }
2286
2287   if (ban) {
2288     args = silc_buffer_alloc_size(2);
2289     silc_buffer_format(args,
2290                        SILC_STR_UI_SHORT(1),
2291                        SILC_STR_END);
2292     if (pubkey) {
2293       chidp = silc_public_key_payload_encode(pubkey);
2294       args = silc_argument_payload_encode_one(args,
2295                                               silc_buffer_datalen(chidp), 2);
2296       silc_buffer_free(chidp);
2297       silc_pkcs_public_key_free(pubkey);
2298     } else {
2299       args = silc_argument_payload_encode_one(args, ban, strlen(ban), 1);
2300     }
2301   }
2302
2303   chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2304
2305   /* Send the command */
2306   silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 3,
2307                               1, silc_buffer_datalen(chidp),
2308                               2, args ? action : NULL, args ? 1 : 0,
2309                               3, silc_buffer_datalen(args));
2310
2311   silc_buffer_free(chidp);
2312   silc_buffer_free(args);
2313
2314   /* Notify application */
2315   COMMAND(SILC_STATUS_OK);
2316
2317   /** Wait for command reply */
2318   silc_fsm_next(fsm, silc_client_command_reply_wait);
2319   return SILC_FSM_CONTINUE;
2320
2321  out:
2322   return SILC_FSM_FINISH;
2323 }
2324
2325 /********************************* DETACH ***********************************/
2326
2327 /* Command DETACH. This is used to detach from the server */
2328
2329 SILC_FSM_STATE(silc_client_command_detach)
2330 {
2331   SilcClientCommandContext cmd = fsm_context;
2332   SilcClientConnection conn = cmd->conn;
2333
2334   silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 0);
2335
2336   /* Notify application */
2337   COMMAND(SILC_STATUS_OK);
2338
2339   /** Wait for command reply */
2340   silc_fsm_next(fsm, silc_client_command_reply_wait);
2341   return SILC_FSM_CONTINUE;
2342 }
2343
2344 /********************************** WATCH ***********************************/
2345
2346 /* Command WATCH. */
2347
2348 SILC_FSM_STATE(silc_client_command_watch)
2349 {
2350   SilcClientCommandContext cmd = fsm_context;
2351   SilcClientConnection conn = cmd->conn;
2352   SilcBuffer args = NULL;
2353   int type = 0;
2354   const char *pubkey = NULL;
2355   SilcBool pubkey_add = TRUE;
2356
2357   if (cmd->argc < 3) {
2358     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2359     goto out;
2360   }
2361
2362   if (!strcasecmp(cmd->argv[1], "-add")) {
2363     type = 2;
2364   } else if (!strcasecmp(cmd->argv[1], "-del")) {
2365     type = 3;
2366   } else if (!strcasecmp(cmd->argv[1], "-pubkey") && cmd->argc >= 3) {
2367     type = 4;
2368     pubkey = cmd->argv[2] + 1;
2369     if (cmd->argv[2][0] == '-')
2370       pubkey_add = FALSE;
2371   } else {
2372     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2373     goto out;
2374   }
2375
2376   if (pubkey) {
2377     SilcPublicKey pk;
2378     SilcBuffer buffer;
2379
2380     if (!silc_pkcs_load_public_key(pubkey, &pk)) {
2381       SAY(conn->client, conn, SILC_CLIENT_MESSAGE_ERROR,
2382           "Could not load public key %s, check the filename", pubkey);
2383       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2384       goto out;
2385     }
2386
2387     args = silc_buffer_alloc_size(2);
2388     silc_buffer_format(args,
2389                        SILC_STR_UI_SHORT(1),
2390                        SILC_STR_END);
2391     buffer = silc_public_key_payload_encode(pk);
2392     args = silc_argument_payload_encode_one(args, silc_buffer_datalen(buffer),
2393                                             pubkey_add ? 0x00 : 0x01);
2394     silc_buffer_free(buffer);
2395     silc_pkcs_public_key_free(pk);
2396   }
2397
2398   /* Send the commmand */
2399   silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
2400                               1, silc_buffer_datalen(conn->internal->
2401                                                      local_idp),
2402                               type, pubkey ? args->data : cmd->argv[2],
2403                               pubkey ? silc_buffer_len(args) :
2404                               cmd->argv_lens[2]);
2405
2406   silc_buffer_free(args);
2407
2408   /* Notify application */
2409   COMMAND(SILC_STATUS_OK);
2410
2411   /** Wait for command reply */
2412   silc_fsm_next(fsm, silc_client_command_reply_wait);
2413   return SILC_FSM_CONTINUE;
2414
2415  out:
2416   return SILC_FSM_FINISH;
2417 }
2418
2419 /********************************** LEAVE ***********************************/
2420
2421 /* LEAVE command. Leaves a channel. Client removes itself from a channel. */
2422
2423 SILC_FSM_STATE(silc_client_command_leave)
2424 {
2425   SilcClientCommandContext cmd = fsm_context;
2426   SilcClientConnection conn = cmd->conn;
2427   SilcChannelEntry channel;
2428   SilcBuffer idp;
2429   char *name;
2430
2431   if (cmd->argc != 2) {
2432     SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2433         "Usage: /LEAVE <channel>");
2434     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2435     goto out;
2436   }
2437
2438   if (cmd->argv[1][0] == '*') {
2439     if (!conn->current_channel) {
2440       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2441       goto out;
2442     }
2443     name = conn->current_channel->channel_name;
2444   } else {
2445     name = cmd->argv[1];
2446   }
2447
2448   /* Get the channel entry */
2449   channel = silc_client_get_channel(conn->client, conn, name);
2450   if (!channel) {
2451     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2452     goto out;
2453   }
2454
2455   idp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2456
2457   /* Send LEAVE command to the server */
2458   silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
2459                               1, silc_buffer_datalen(idp));
2460
2461   silc_buffer_free(idp);
2462
2463   /* Notify application */
2464   COMMAND(SILC_STATUS_OK);
2465
2466   if (conn->current_channel == channel)
2467     conn->current_channel = NULL;
2468
2469   /** Wait for command reply */
2470   silc_fsm_next(fsm, silc_client_command_reply_wait);
2471   return SILC_FSM_CONTINUE;
2472
2473  out:
2474   return SILC_FSM_FINISH;
2475 }
2476
2477 /********************************** USERS ***********************************/
2478
2479 /* Command USERS. Requests the USERS of the clients joined on requested
2480    channel. */
2481
2482 SILC_FSM_STATE(silc_client_command_users)
2483 {
2484   SilcClientCommandContext cmd = fsm_context;
2485   SilcClientConnection conn = cmd->conn;
2486   char *name;
2487
2488   if (cmd->argc != 2) {
2489     SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2490         "Usage: /USERS <channel>");
2491     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2492     goto out;
2493   }
2494
2495   if (cmd->argv[1][0] == '*') {
2496     if (!conn->current_channel) {
2497       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2498       goto out;
2499     }
2500     name = conn->current_channel->channel_name;
2501   } else {
2502     name = cmd->argv[1];
2503   }
2504
2505   /* Send USERS command to the server */
2506   silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
2507                               2, name, strlen(name));
2508
2509   /* Notify application */
2510   COMMAND(SILC_STATUS_OK);
2511
2512   /** Wait for command reply */
2513   silc_fsm_next(fsm, silc_client_command_reply_wait);
2514   return SILC_FSM_CONTINUE;
2515
2516  out:
2517   return SILC_FSM_FINISH;
2518 }
2519
2520 /********************************* GETKEY ***********************************/
2521
2522 /* Command GETKEY. Used to fetch remote client's public key. */
2523
2524 SILC_FSM_STATE(silc_client_command_getkey)
2525 {
2526   SilcClientCommandContext cmd = fsm_context;
2527   SilcClientConnection conn = cmd->conn;
2528   SilcClient client = conn->client;
2529   SilcClientEntry client_entry;
2530   SilcServerEntry server_entry;
2531   SilcDList clients;
2532   char *nickname = NULL;
2533   SilcBuffer idp;
2534
2535   if (cmd->argc < 2) {
2536     client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_INFO,
2537                      "Usage: /GETKEY <nickname or server name>");
2538     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2539     return SILC_FSM_FINISH;
2540   }
2541
2542   /* Parse the typed nickname. */
2543   if (client->internal->params->nickname_parse)
2544     client->internal->params->nickname_parse(cmd->argv[1], &nickname);
2545   else
2546     nickname = strdup(cmd->argv[1]);
2547   if (!nickname) {
2548     COMMAND_ERROR(SILC_STATUS_ERR_RESOURCE_LIMIT);
2549     return SILC_FSM_FINISH;
2550   }
2551
2552   /* Find client entry */
2553   clients = silc_client_get_clients_local(client, conn, nickname,
2554                                           cmd->argv[1]);
2555   if (!clients) {
2556     /* Check whether user requested server */
2557     server_entry = silc_client_get_server(client, conn, cmd->argv[1]);
2558     if (!server_entry) {
2559       if (cmd->resolved) {
2560         /* Resolving didn't find anything.  We should never get here as
2561            errors are handled in the resolving callback. */
2562         COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_NICK);
2563         COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_SERVER);
2564         return SILC_FSM_FINISH;
2565       }
2566
2567       /* No client or server exist with this name, query for both. */
2568       cmd->resolved = TRUE;
2569       SILC_FSM_CALL(silc_client_command_send(client, conn,
2570                                              SILC_COMMAND_IDENTIFY,
2571                                              silc_client_command_continue,
2572                                              cmd, 2,
2573                                              1, cmd->argv[1],
2574                                              strlen(cmd->argv[1]),
2575                                              2, cmd->argv[1],
2576                                              strlen(cmd->argv[1])));
2577       /* NOT REACHED */
2578     }
2579     idp = silc_id_payload_encode(&server_entry->id, SILC_ID_SERVER);
2580   } else {
2581     client_entry = silc_dlist_get(clients);
2582     idp = silc_id_payload_encode(&client_entry->id, SILC_ID_CLIENT);
2583     silc_client_list_free(client, conn, clients);
2584   }
2585
2586   /* Send the commmand */
2587   silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
2588                               1, silc_buffer_datalen(idp));
2589
2590   silc_buffer_free(idp);
2591   silc_free(nickname);
2592
2593   /* Notify application */
2594   COMMAND(SILC_STATUS_OK);
2595
2596   /** Wait for command reply */
2597   silc_fsm_next(fsm, silc_client_command_reply_wait);
2598   return SILC_FSM_CONTINUE;
2599 }
2600
2601 /********************************* SERVICE **********************************/
2602
2603 /* Command SERVICE.  Negotiates service agreement with server. */
2604 /* XXX incomplete */
2605
2606 SILC_FSM_STATE(silc_client_command_service)
2607 {
2608   SilcClientCommandContext cmd = fsm_context;
2609 #if 0
2610   SilcClientConnection conn = cmd->conn;
2611   SilcBuffer buffer;
2612   char *name;
2613
2614   if (cmd->argc < 2) {
2615     SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2616         "Usage: /SERVICE [<service name>] [-pubkey]");
2617     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2618     return SILC_FSM_FINISH;
2619   }
2620
2621   name = cmd->argv[1];
2622
2623   /* Send SERVICE command to the server */
2624   buffer = silc_command_payload_encode_va(SILC_COMMAND_SERVICE,
2625                                           ++conn->cmd_ident, 1,
2626                                           1, name, strlen(name));
2627   silc_client_packet_send(conn->client, conn->sock, SILC_PACKET_COMMAND,
2628                           NULL, 0, NULL, NULL, buffer->data,
2629                           buffer->len, TRUE);
2630   silc_buffer_free(buffer);
2631 #endif /* 0 */
2632
2633   /* Notify application */
2634   COMMAND(SILC_STATUS_OK);
2635
2636   /** Wait for command reply */
2637   silc_fsm_next(fsm, silc_client_command_reply_wait);
2638   return SILC_FSM_CONTINUE;
2639 }
2640
2641 /* Register all default commands provided by the client library for the
2642    application. */
2643
2644 void silc_client_commands_register(SilcClient client)
2645 {
2646   silc_list_init(client->internal->commands, struct SilcClientCommandStruct,
2647                  next);
2648
2649   SILC_CLIENT_CMD(whois, WHOIS, "WHOIS", 5);
2650   SILC_CLIENT_CMD(whowas, WHOWAS, "WHOWAS", 3);
2651   SILC_CLIENT_CMD(identify, IDENTIFY, "IDENTIFY", 3);
2652   SILC_CLIENT_CMD(nick, NICK, "NICK", 2);
2653   SILC_CLIENT_CMD(list, LIST, "LIST", 2);
2654   SILC_CLIENT_CMD(topic, TOPIC, "TOPIC", 3);
2655   SILC_CLIENT_CMD(invite, INVITE, "INVITE", 3);
2656   SILC_CLIENT_CMD(quit, QUIT, "QUIT", 2);
2657   SILC_CLIENT_CMD(kill, KILL, "KILL", 4);
2658   SILC_CLIENT_CMD(info, INFO, "INFO", 2);
2659   SILC_CLIENT_CMD(stats, STATS, "STATS", 0);
2660   SILC_CLIENT_CMD(ping, PING, "PING", 2);
2661   SILC_CLIENT_CMD(oper, OPER, "OPER", 3);
2662   SILC_CLIENT_CMD(join, JOIN, "JOIN", 9);
2663   SILC_CLIENT_CMD(motd, MOTD, "MOTD", 2);
2664   SILC_CLIENT_CMD(umode, UMODE, "UMODE", 2);
2665   SILC_CLIENT_CMD(cmode, CMODE, "CMODE", 6);
2666   SILC_CLIENT_CMD(cumode, CUMODE, "CUMODE", 9);
2667   SILC_CLIENT_CMD(kick, KICK, "KICK", 4);
2668   SILC_CLIENT_CMD(ban, BAN, "BAN", 3);
2669   SILC_CLIENT_CMD(detach, DETACH, "DETACH", 0);
2670   SILC_CLIENT_CMD(watch, WATCH, "WATCH", 3);
2671   SILC_CLIENT_CMD(silcoper, SILCOPER, "SILCOPER", 3);
2672   SILC_CLIENT_CMD(leave, LEAVE, "LEAVE", 2);
2673   SILC_CLIENT_CMD(users, USERS, "USERS", 2);
2674   SILC_CLIENT_CMD(getkey, GETKEY, "GETKEY", 2);
2675   SILC_CLIENT_CMD(service, SERVICE, "SERVICE", 10);
2676 }
2677
2678 /* Unregister all commands. */
2679
2680 void silc_client_commands_unregister(SilcClient client)
2681 {
2682   SILC_CLIENT_CMDU(whois, WHOIS, "WHOIS");
2683   SILC_CLIENT_CMDU(whowas, WHOWAS, "WHOWAS");
2684   SILC_CLIENT_CMDU(identify, IDENTIFY, "IDENTIFY");
2685   SILC_CLIENT_CMDU(nick, NICK, "NICK");
2686   SILC_CLIENT_CMDU(list, LIST, "LIST");
2687   SILC_CLIENT_CMDU(topic, TOPIC, "TOPIC");
2688   SILC_CLIENT_CMDU(invite, INVITE, "INVITE");
2689   SILC_CLIENT_CMDU(quit, QUIT, "QUIT");
2690   SILC_CLIENT_CMDU(kill, KILL, "KILL");
2691   SILC_CLIENT_CMDU(info, INFO, "INFO");
2692   SILC_CLIENT_CMDU(stats, STATS, "STATS");
2693   SILC_CLIENT_CMDU(ping, PING, "PING");
2694   SILC_CLIENT_CMDU(oper, OPER, "OPER");
2695   SILC_CLIENT_CMDU(join, JOIN, "JOIN");
2696   SILC_CLIENT_CMDU(motd, MOTD, "MOTD");
2697   SILC_CLIENT_CMDU(umode, UMODE, "UMODE");
2698   SILC_CLIENT_CMDU(cmode, CMODE, "CMODE");
2699   SILC_CLIENT_CMDU(cumode, CUMODE, "CUMODE");
2700   SILC_CLIENT_CMDU(kick, KICK, "KICK");
2701   SILC_CLIENT_CMDU(ban, BAN, "BAN");
2702   SILC_CLIENT_CMDU(detach, DETACH, "DETACH");
2703   SILC_CLIENT_CMDU(watch, WATCH, "WATCH");
2704   SILC_CLIENT_CMDU(silcoper, SILCOPER, "SILCOPER");
2705   SILC_CLIENT_CMDU(leave, LEAVE, "LEAVE");
2706   SILC_CLIENT_CMDU(users, USERS, "USERS");
2707   SILC_CLIENT_CMDU(getkey, GETKEY, "GETKEY");
2708   SILC_CLIENT_CMDU(service, SERVICE, "SERVICE");
2709 }
2710
2711 /****************** Client Side Incoming Command Handling *******************/
2712
2713 /* Reply to WHOIS command from server */
2714
2715 static void silc_client_command_process_whois(SilcClient client,
2716                                               SilcClientConnection conn,
2717                                               SilcCommandPayload payload,
2718                                               SilcArgumentPayload args)
2719 {
2720 #if 0
2721   SilcDList attrs;
2722   unsigned char *tmp;
2723   SilcUInt32 tmp_len;
2724   SilcBuffer buffer, packet;
2725
2726   SILC_LOG_DEBUG(("Received WHOIS command"));
2727
2728   /* Try to take the Requested Attributes */
2729   tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
2730   if (!tmp)
2731     return;
2732
2733   attrs = silc_attribute_payload_parse(tmp, tmp_len);
2734   if (!attrs)
2735     return;
2736
2737   /* Process requested attributes */
2738   buffer = silc_client_attributes_process(client, conn, attrs);
2739   if (!buffer) {
2740     silc_attribute_payload_list_free(attrs);
2741     return;
2742   }
2743
2744   /* Send the attributes back */
2745   packet =
2746     silc_command_reply_payload_encode_va(SILC_COMMAND_WHOIS,
2747                                          SILC_STATUS_OK, 0,
2748                                          silc_command_get_ident(payload),
2749                                          1, 11, buffer->data, buffer->len);
2750   silc_client_packet_send(client, sock, SILC_PACKET_COMMAND_REPLY,
2751                           NULL, 0, NULL, NULL, packet->data,
2752                           packet->len, TRUE);
2753   silc_buffer_free(packet);
2754   silc_buffer_free(buffer);
2755 #endif /* 0 */
2756 }
2757
2758 /* Client is able to receive some command packets even though they are
2759    special case.  Server may send WHOIS command to the client to retrieve
2760    Requested Attributes information for WHOIS query the server is
2761    processing. This function currently handles only the WHOIS command,
2762    but if in the future more commands may arrive then this can be made
2763    to support other commands too. */
2764
2765 SILC_FSM_STATE(silc_client_command)
2766 {
2767   SilcClientConnection conn = fsm_context;
2768   SilcClient client = conn->client;
2769   SilcPacket packet = state_context;
2770   SilcCommandPayload payload;
2771   SilcCommand command;
2772   SilcArgumentPayload args;
2773
2774   /* Get command payload from packet */
2775   payload = silc_command_payload_parse(packet->buffer.data,
2776                                        silc_buffer_len(&packet->buffer));
2777   if (!payload) {
2778     SILC_LOG_DEBUG(("Bad command packet"));
2779     return SILC_FSM_FINISH;
2780   }
2781
2782   /* Get arguments */
2783   args = silc_command_get_args(payload);
2784
2785   /* Get the command */
2786   command = silc_command_get(payload);
2787   switch (command) {
2788
2789   case SILC_COMMAND_WHOIS:
2790     /* Ignore everything if requested by application */
2791     if (client->internal->params->ignore_requested_attributes)
2792       break;
2793
2794     silc_client_command_process_whois(client, conn, payload, args);
2795     break;
2796
2797   default:
2798     break;
2799   }
2800
2801   silc_command_payload_free(payload);
2802   return SILC_FSM_FINISH;
2803 }