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