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