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