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