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