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