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