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