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