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