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