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