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