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