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