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