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