Added support for the new PKCS API, Auth API and other API
[silc.git] / lib / silcclient / command.c
1 /*
2
3   command.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 1997 - 2007 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; version 2 of the License.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18 */
19 /* $Id$ */
20
21 #include "silc.h"
22 #include "silcclient.h"
23 #include "client_internal.h"
24
25 /************************** Types and definitions ***************************/
26
27 /* Command operation that is called at the end of all commands.
28    Usage: COMMAND(status); */
29 #define COMMAND(status) cmd->conn->client->internal->ops->command(      \
30   cmd->conn->client, cmd->conn, TRUE, cmd->cmd, (status), cmd->argc, cmd->argv)
31
32 /* Error to application. Usage: COMMAND_ERROR(status); */
33 #define COMMAND_ERROR(status)                                   \
34   cmd->conn->client->internal->ops->command(cmd->conn->client,  \
35   cmd->conn, FALSE, cmd->cmd, (status), cmd->argc, cmd->argv)
36
37 /* Used to register new command */
38 #define SILC_CLIENT_CMD(func, cmd, name, args)                          \
39 silc_client_command_register(client, SILC_COMMAND_##cmd, name,          \
40                              silc_client_command_##func,                \
41                              silc_client_command_reply_##func, args)
42
43 /* Used to unregister command */
44 #define SILC_CLIENT_CMDU(func, cmd, name)                               \
45 silc_client_command_unregister(client, SILC_COMMAND_##cmd,              \
46                                silc_client_command_##func,              \
47                                silc_client_command_reply_##func)
48
49 #define SAY cmd->conn->client->internal->ops->say
50
51 /************************ Static utility functions **************************/
52
53 /* Return next available command identifier. */
54
55 static SilcUInt16 silc_client_cmd_ident(SilcClientConnection conn)
56 {
57   SilcUInt16 cmd_ident;
58
59   cmd_ident = silc_atomic_add_int16(&conn->internal->cmd_ident, 1);
60   if (!cmd_ident)
61     cmd_ident = silc_atomic_add_int16(&conn->internal->cmd_ident, 1);
62
63   return cmd_ident;
64 }
65
66 /* State to finish command thread after an error in resolving command */
67
68 SILC_FSM_STATE(silc_client_command_continue_error)
69 {
70   /* Destructor will free all resources */
71   return SILC_FSM_FINISH;
72 }
73
74 /* Command reply callback to continue with the execution of a command.
75    This will continue when first successful reply is received, and ignores
76    the rest.  On the other hand, if only errors are received it will
77    wait for all errors before continuing. */
78
79 static SilcBool silc_client_command_continue(SilcClient client,
80                                              SilcClientConnection conn,
81                                              SilcCommand command,
82                                              SilcStatus status,
83                                              SilcStatus error,
84                                              void *context,
85                                              va_list ap)
86 {
87   SilcClientCommandContext cmd = context;
88
89   /* Continue immediately when successful reply is received */
90   if (status == SILC_STATUS_OK || !SILC_STATUS_IS_ERROR(error)) {
91     SILC_FSM_CALL_CONTINUE(&cmd->thread);
92     return FALSE;
93   }
94
95   /* Error */
96   COMMAND_ERROR(error);
97
98   /* Continue after last error is received */
99   if (SILC_STATUS_IS_ERROR(status) ||
100       (status == SILC_STATUS_LIST_END && SILC_STATUS_IS_ERROR(error))) {
101     silc_fsm_next(&cmd->thread, silc_client_command_continue_error);
102     SILC_FSM_CALL_CONTINUE(&cmd->thread);
103     return FALSE;
104   }
105
106   return TRUE;
107 }
108
109 /* Continues after resolving completed. */
110
111 static void silc_client_command_resolve_continue(SilcClient client,
112                                                  SilcClientConnection conn,
113                                                  SilcStatus status,
114                                                  SilcDList clients,
115                                                  void *context)
116 {
117   SilcClientCommandContext cmd = context;
118
119   if (status != SILC_STATUS_OK)
120     silc_fsm_next(&cmd->thread, silc_client_command_continue_error);
121
122   /* Continue with the command */
123   SILC_FSM_CALL_CONTINUE(&cmd->thread);
124 }
125
126 /* Dummy command callback.  Nothing interesting to do here.  Use this when
127    you just send command but don't care about reply. */
128
129 SilcBool silc_client_command_called_dummy(SilcClient client,
130                                           SilcClientConnection conn,
131                                           SilcCommand command,
132                                           SilcStatus status,
133                                           SilcStatus error,
134                                           void *context,
135                                           va_list ap)
136 {
137   return FALSE;
138 }
139
140 /* Dummy resolving callback.  Nothing interesting to do here.  Use this
141    when you just resolve entires but don't care about reply. */
142
143 void silc_client_command_resolve_dummy(SilcClient client,
144                                        SilcClientConnection conn,
145                                        SilcStatus status,
146                                        SilcDList clients,
147                                        void *context)
148 {
149   /* Nothing */
150 }
151
152 /* Register command to client */
153
154 static SilcBool
155 silc_client_command_register(SilcClient client,
156                              SilcCommand command,
157                              const char *name,
158                              SilcFSMStateCallback command_func,
159                              SilcFSMStateCallback command_reply_func,
160                              SilcUInt8 max_args)
161 {
162   SilcClientCommand cmd;
163
164   cmd = silc_calloc(1, sizeof(*cmd));
165   if (!cmd)
166     return FALSE;
167   cmd->cmd = command;
168   cmd->command = command_func;
169   cmd->reply = command_reply_func;
170   cmd->max_args = max_args;
171   cmd->name = name ? strdup(name) : NULL;
172   if (!cmd->name) {
173     silc_free(cmd);
174     return FALSE;
175   }
176
177   silc_list_add(client->internal->commands, cmd);
178
179   return TRUE;
180 }
181
182 /* Unregister command from client */
183
184 static SilcBool
185 silc_client_command_unregister(SilcClient client,
186                                SilcCommand command,
187                                SilcFSMStateCallback command_func,
188                                SilcFSMStateCallback command_reply_func)
189 {
190   SilcClientCommand cmd;
191
192   silc_list_start(client->internal->commands);
193   while ((cmd = silc_list_get(client->internal->commands)) != SILC_LIST_END) {
194     if (cmd->cmd == command && cmd->command == command_func &&
195         cmd->reply == command_reply_func) {
196       silc_list_del(client->internal->commands, cmd);
197       silc_free(cmd->name);
198       silc_free(cmd);
199       return TRUE;
200     }
201   }
202
203   return FALSE;
204 }
205
206 /* Finds and returns a pointer to the command list. Return NULL if the
207    command is not found. */
208
209 static SilcClientCommand silc_client_command_find(SilcClient client,
210                                                   const char *name)
211 {
212   SilcClientCommand cmd;
213
214   silc_list_start(client->internal->commands);
215   while ((cmd = silc_list_get(client->internal->commands)) != SILC_LIST_END) {
216     if (cmd->name && !strcasecmp(cmd->name, name))
217       return cmd;
218   }
219
220   return NULL;
221 }
222
223 /* Command thread destructor */
224
225 static void silc_client_command_destructor(SilcFSMThread thread,
226                                            void *fsm_context,
227                                            void *destructor_context)
228 {
229   SilcClientCommandContext cmd = fsm_context;
230   SilcClientConnection conn = cmd->conn;
231
232   /* Removes commands that aren't waiting for reply but are waiting
233      for something.  They may not have been removed yet. */
234   silc_list_del(conn->internal->pending_commands, cmd);
235
236   silc_client_command_free(cmd);
237 }
238
239 /* Add a command pending a command reply.  Used internally by the library. */
240
241 static SilcBool
242 silc_client_command_add_pending(SilcClientConnection conn,
243                                 SilcClientCommandContext cmd,
244                                 SilcClientCommandReply reply,
245                                 void *context)
246 {
247   SilcClientCommandReplyCallback cb;
248
249   silc_mutex_lock(conn->internal->lock);
250
251   /* Add pending callback, if defined */
252   if (reply) {
253     cb = silc_calloc(1, sizeof(*cb));
254     if (!cb) {
255       silc_mutex_unlock(conn->internal->lock);
256       return FALSE;
257     }
258     cb->reply = reply;
259     cb->context = context;
260     silc_list_add(cmd->reply_callbacks, cb);
261   }
262
263   /* Add pending reply */
264   silc_list_add(conn->internal->pending_commands, cmd);
265
266   silc_mutex_unlock(conn->internal->lock);
267
268   return TRUE;
269 }
270
271 /* Generic function to send any command. The arguments must be sent already
272    encoded into correct format and in correct order.  Arguments come from
273    variable argument list pointer. */
274
275 static SilcUInt16 silc_client_command_send_vap(SilcClient client,
276                                                SilcClientConnection conn,
277                                                SilcClientCommandContext cmd,
278                                                SilcCommand command,
279                                                SilcClientCommandReply reply,
280                                                void *reply_context,
281                                                SilcUInt32 argc, va_list ap)
282 {
283   SilcBuffer packet;
284
285   SILC_LOG_DEBUG(("Send command %s", silc_get_command_name(command)));
286
287   if (conn->internal->disconnected)
288     return 0;
289
290   if (!cmd->cmd_ident)
291     cmd->cmd_ident = silc_client_cmd_ident(conn);
292
293   /* Encode command payload */
294   packet = silc_command_payload_encode_vap(command, cmd->cmd_ident, argc, ap);
295   if (!packet)
296     return 0;
297
298   /* Send the command */
299   if (!silc_packet_send(conn->stream, SILC_PACKET_COMMAND, 0,
300                         silc_buffer_datalen(packet))) {
301     silc_buffer_free(packet);
302     return 0;
303   }
304
305   /* Add the command pending command reply */
306   silc_client_command_add_pending(conn, cmd, reply, reply_context);
307
308   silc_buffer_free(packet);
309
310   return cmd->cmd_ident;
311 }
312
313 /* Generic function to send any command. The arguments must be sent already
314    encoded into correct format and in correct order.  Arguments come from
315    arrays. */
316
317 static SilcUInt16
318 silc_client_command_send_arg_array(SilcClient client,
319                                    SilcClientConnection conn,
320                                    SilcClientCommandContext cmd,
321                                    SilcCommand command,
322                                    SilcClientCommandReply reply,
323                                    void *reply_context,
324                                    SilcUInt32 argc,
325                                    unsigned char **argv,
326                                    SilcUInt32 *argv_lens,
327                                    SilcUInt32 *argv_types)
328 {
329   SilcBuffer packet;
330
331   SILC_LOG_DEBUG(("Send command %s", silc_get_command_name(command)));
332
333   if (conn->internal->disconnected)
334     return 0;
335
336   if (!cmd->cmd_ident)
337     cmd->cmd_ident = silc_client_cmd_ident(conn);
338
339   /* Encode command payload */
340   packet = silc_command_payload_encode(command, argc, argv, argv_lens,
341                                        argv_types, cmd->cmd_ident);
342   if (!packet)
343     return 0;
344
345   /* Send the command */
346   if (!silc_packet_send(conn->stream, SILC_PACKET_COMMAND, 0,
347                         silc_buffer_datalen(packet))) {
348     silc_buffer_free(packet);
349     return 0;
350   }
351
352   /* Add the command pending command reply */
353   silc_client_command_add_pending(conn, cmd, reply, reply_context);
354
355   silc_buffer_free(packet);
356
357   return cmd->cmd_ident;
358 }
359
360 /* Generic function to send any command. The arguments must be sent already
361    encoded into correct format and in correct order.  This is used internally
362    by the library.  */
363
364 static SilcUInt16 silc_client_command_send_va(SilcClientConnection conn,
365                                               SilcClientCommandContext cmd,
366                                               SilcCommand command,
367                                               SilcClientCommandReply reply,
368                                               void *reply_context,
369                                               SilcUInt32 argc, ...)
370 {
371   va_list ap;
372   SilcUInt16 cmd_ident;
373
374   va_start(ap, argc);
375   cmd_ident = silc_client_command_send_vap(conn->client, conn, cmd, command,
376                                            reply, reply_context, argc, ap);
377   va_end(ap);
378
379   return cmd_ident;
380 }
381
382 /****************************** Command API *********************************/
383
384 /* Free command context and its internals */
385
386 void silc_client_command_free(SilcClientCommandContext cmd)
387 {
388   SilcClientCommandReplyCallback cb;
389   int i;
390
391   for (i = 0; i < cmd->argc; i++)
392     silc_free(cmd->argv[i]);
393   silc_free(cmd->argv);
394   silc_free(cmd->argv_lens);
395   silc_free(cmd->argv_types);
396
397   silc_list_start(cmd->reply_callbacks);
398   while ((cb = silc_list_get(cmd->reply_callbacks)))
399     silc_free(cb);
400
401   silc_free(cmd);
402 }
403
404 /* Executes a command */
405
406 SilcUInt16 silc_client_command_call(SilcClient client,
407                                     SilcClientConnection conn,
408                                     const char *command_line, ...)
409 {
410   va_list va;
411   SilcUInt32 argc = 0;
412   unsigned char **argv = NULL;
413   SilcUInt32 *argv_lens = NULL, *argv_types = NULL;
414   SilcClientCommand command;
415   SilcClientCommandContext cmd;
416   char *arg;
417
418   if (!conn) {
419     client->internal->ops->say(client, NULL, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
420       "You are not connected to a server, please connect to server");
421     return 0;
422   }
423
424   /* Parse arguments */
425   va_start(va, command_line);
426   if (command_line) {
427     char *command_name;
428
429     /* Get command name */
430     command_name = silc_memdup(command_line, strcspn(command_line, " "));
431     if (!command_name)
432       return 0;
433
434     /* Find command by name */
435     command = silc_client_command_find(client, command_name);
436     if (!command) {
437       silc_free(command_name);
438       return 0;
439     }
440
441     /* Parse command line */
442     silc_parse_command_line((char *)command_line, &argv, &argv_lens,
443                             &argv_types, &argc, command->max_args);
444
445     silc_free(command_name);
446   } else {
447     arg = va_arg(va, char *);
448     if (!arg)
449       return 0;
450
451     /* Find command by name */
452     command = silc_client_command_find(client, arg);
453     if (!command)
454       return 0;
455
456     while (arg) {
457       argv = silc_realloc(argv, sizeof(*argv) * (argc + 1));
458       argv_lens = silc_realloc(argv_lens, sizeof(*argv_lens) * (argc + 1));
459       argv_types = silc_realloc(argv_types, sizeof(*argv_types) * (argc + 1));
460       if (!argv || !argv_lens || !argv_types)
461         return 0;
462       argv[argc] = silc_memdup(arg, strlen(arg));
463       if (!argv[argc])
464         return 0;
465       argv_lens[argc] = strlen(arg);
466       argv_types[argc] = argc;
467       argc++;
468       arg = va_arg(va, char *);
469     }
470   }
471   va_end(va);
472
473   /* Allocate command context */
474   cmd = silc_calloc(1, sizeof(*cmd));
475   if (!cmd)
476     return 0;
477   cmd->conn = conn;
478   cmd->cmd = command->cmd;
479   cmd->argc = argc;
480   cmd->argv = argv;
481   cmd->argv_lens = argv_lens;
482   cmd->argv_types = argv_types;
483   cmd->cmd_ident = silc_client_cmd_ident(conn);
484   cmd->called = TRUE;
485   cmd->verbose = TRUE;
486   silc_list_init(cmd->reply_callbacks,
487                  struct SilcClientCommandReplyCallbackStruct, next);
488
489   /*** Call command */
490   SILC_LOG_DEBUG(("Calling %s command", silc_get_command_name(cmd->cmd)));
491   silc_fsm_thread_init(&cmd->thread, &conn->internal->fsm, cmd,
492                        silc_client_command_destructor, NULL, FALSE);
493   silc_fsm_start_sync(&cmd->thread, command->command);
494
495   return cmd->cmd_ident;
496 }
497
498 /* Generic function to send any command. The arguments must be sent already
499    encoded into correct format and in correct order. */
500
501 SilcUInt16 silc_client_command_send(SilcClient client,
502                                     SilcClientConnection conn,
503                                     SilcCommand command,
504                                     SilcClientCommandReply reply,
505                                     void *reply_context,
506                                     SilcUInt32 argc, ...)
507 {
508   SilcClientCommandContext cmd;
509   va_list ap;
510
511   if (!conn || !reply)
512     return 0;
513
514   /* Allocate command context */
515   cmd = silc_calloc(1, sizeof(*cmd));
516   if (!cmd)
517     return 0;
518   cmd->conn = conn;
519   cmd->cmd = command;
520   silc_list_init(cmd->reply_callbacks,
521                  struct SilcClientCommandReplyCallbackStruct, next);
522
523   /* Send the command */
524   va_start(ap, argc);
525   cmd->cmd_ident =
526     silc_client_command_send_vap(client, conn, cmd, command, reply,
527                                  reply_context, argc, ap);
528   va_end(ap);
529
530   if (!cmd->cmd_ident) {
531     silc_client_command_free(cmd);
532     return 0;
533   }
534
535   /*** Wait for command reply */
536   silc_fsm_thread_init(&cmd->thread, &conn->internal->fsm, cmd,
537                        silc_client_command_destructor, NULL, FALSE);
538   silc_fsm_start_sync(&cmd->thread, silc_client_command_reply_wait);
539
540   return cmd->cmd_ident;
541 }
542
543 /* Generic function to send any command. The arguments must be sent already
544    encoded into correct format and in correct order.  Arguments come from
545    arrays. */
546
547 SilcUInt16 silc_client_command_send_argv(SilcClient client,
548                                          SilcClientConnection conn,
549                                          SilcCommand command,
550                                          SilcClientCommandReply reply,
551                                          void *reply_context,
552                                          SilcUInt32 argc,
553                                          unsigned char **argv,
554                                          SilcUInt32 *argv_lens,
555                                          SilcUInt32 *argv_types)
556 {
557   SilcClientCommandContext cmd;
558
559   if (!conn || !reply)
560     return 0;
561
562   /* Allocate command context */
563   cmd = silc_calloc(1, sizeof(*cmd));
564   if (!cmd)
565     return 0;
566   cmd->conn = conn;
567   cmd->cmd = command;
568
569   /* Send the command */
570   cmd->cmd_ident =
571     silc_client_command_send_arg_array(client, conn, cmd, command, reply,
572                                        reply_context, argc, argv, argv_lens,
573                                        argv_types);
574   if (!cmd->cmd_ident) {
575     silc_client_command_free(cmd);
576     return 0;
577   }
578
579   /*** Wait for command reply */
580   silc_fsm_thread_init(&cmd->thread, &conn->internal->fsm, cmd,
581                        silc_client_command_destructor, NULL, FALSE);
582   silc_fsm_start_sync(&cmd->thread, silc_client_command_reply_wait);
583
584   return cmd->cmd_ident;
585 }
586
587 /* Attach to a command and command identifier to receive command reply. */
588
589 SilcBool silc_client_command_pending(SilcClientConnection conn,
590                                      SilcCommand command,
591                                      SilcUInt16 ident,
592                                      SilcClientCommandReply reply,
593                                      void *context)
594 {
595   SilcClientCommandContext cmd;
596   SilcClientCommandReplyCallback cb;
597
598   if (!conn || !reply)
599     return FALSE;
600
601   SILC_LOG_DEBUG(("Add pending command reply for ident %d", ident));
602
603   silc_mutex_lock(conn->internal->lock);
604
605   /* Find the pending command */
606   silc_list_start(conn->internal->pending_commands);
607   while ((cmd = silc_list_get(conn->internal->pending_commands)))
608     if ((cmd->cmd == command || command == SILC_COMMAND_NONE)
609         && cmd->cmd_ident == ident) {
610
611       /* Add the callback */
612       cb = silc_calloc(1, sizeof(*cb));
613       if (!cb)
614         continue;
615       cb->reply = reply;
616       cb->context = context;
617       silc_list_add(cmd->reply_callbacks, cb);
618     }
619
620   silc_mutex_unlock(conn->internal->lock);
621
622   return TRUE;
623 }
624
625 /******************************** WHOIS *************************************/
626
627 /* Command WHOIS. This command is used to query information about
628    specific user. */
629
630 SILC_FSM_STATE(silc_client_command_whois)
631 {
632   SilcClientCommandContext cmd = fsm_context;
633   SilcClientConnection conn = cmd->conn;
634   SilcClient client = conn->client;
635   SilcBuffer attrs = NULL;
636   unsigned char count[4], *tmp = NULL;
637   SilcBool details = FALSE, nick = FALSE;
638   unsigned char *pubkey = NULL;
639   char *nickname = NULL;
640   int i;
641
642   /* Given without arguments fetches client's own information */
643   if (cmd->argc < 2) {
644     silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1, 4,
645                                 silc_buffer_data(conn->internal->local_idp),
646                                 silc_buffer_len(conn->internal->local_idp));
647
648     /* Notify application */
649     COMMAND(SILC_STATUS_OK);
650
651     /** Wait for command reply */
652     silc_fsm_next(fsm, silc_client_command_reply_wait);
653     return SILC_FSM_CONTINUE;
654   }
655
656   for (i = 1; i < cmd->argc; i++) {
657     if (!strcasecmp(cmd->argv[i], "-details")) {
658       details = TRUE;
659     } else if (!strcasecmp(cmd->argv[i], "-pubkey") && cmd->argc > i + 1) {
660       pubkey = cmd->argv[i + 1];
661       i++;
662     } else {
663       /* We assume that the first parameter is the nickname, if it isn't
664          -details or -pubkey. The last parameter should always be the count */
665       if (i == 1) {
666         nick = TRUE;
667       } else if (i == cmd->argc - 1) {
668         int c = atoi(cmd->argv[i]);
669         SILC_PUT32_MSB(c, count);
670         tmp = count;
671       }
672     }
673   }
674
675   if (details) {
676     /* If pubkey is set, add all attributes to the attrs buffer, except
677        public key */
678     if (pubkey) {
679       attrs = silc_client_attributes_request(SILC_ATTRIBUTE_USER_INFO,
680                                              SILC_ATTRIBUTE_SERVICE,
681                                              SILC_ATTRIBUTE_STATUS_MOOD,
682                                              SILC_ATTRIBUTE_STATUS_FREETEXT,
683                                              SILC_ATTRIBUTE_STATUS_MESSAGE,
684                                              SILC_ATTRIBUTE_PREFERRED_LANGUAGE,
685                                              SILC_ATTRIBUTE_PREFERRED_CONTACT,
686                                              SILC_ATTRIBUTE_TIMEZONE,
687                                              SILC_ATTRIBUTE_GEOLOCATION,
688                                              SILC_ATTRIBUTE_DEVICE_INFO,
689                                              SILC_ATTRIBUTE_USER_ICON, 0);
690     } else {
691       attrs = silc_client_attributes_request(0);
692     }
693   }
694
695   if (pubkey) {
696     SilcAttributeObjPk obj;
697     SilcPublicKey pk;
698
699     if (!silc_pkcs_load_public_key(pubkey, &pk)) {
700       SAY(client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
701           "Could not load public key %s, check the filename",
702           pubkey);
703       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
704       goto out;
705     }
706
707     switch (silc_pkcs_get_type(pk)) {
708     case SILC_PKCS_SILC:
709       obj.type = "silc-rsa";
710       break;
711     case SILC_PKCS_SSH2:
712       obj.type = "ssh-rsa";
713       break;
714     case SILC_PKCS_X509V3:
715       obj.type = "x509v3-sign-rsa";
716       break;
717     case SILC_PKCS_OPENPGP:
718       obj.type = "pgp-sign-rsa";
719       break;
720     default:
721       goto out;
722       break;
723     }
724     obj.data = silc_pkcs_public_key_encode(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, &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                                            conn->local_id,
1505                                            SILC_ID_CLIENT,
1506                                            silc_client_command_join_signed,
1507                                            cmd));
1508         /* NOT REACHED */
1509       }
1510     } else {
1511       /* Passphrases must be UTF-8 encoded, so encode if it is not */
1512       if (!silc_utf8_valid(cmd->argv[i], cmd->argv_lens[i])) {
1513         passphrase_len = silc_utf8_encoded_len(cmd->argv[i],
1514                                                cmd->argv_lens[i], 0);
1515         pu8 = silc_calloc(passphrase_len, sizeof(*pu8));
1516         passphrase_len = silc_utf8_encode(cmd->argv[i], cmd->argv_lens[i],
1517                                           0, pu8, passphrase_len);
1518         passphrase = pu8;
1519       } else {
1520         passphrase = strdup(cmd->argv[i]);
1521         passphrase_len = cmd->argv_lens[i];
1522       }
1523     }
1524   }
1525
1526   /* Send JOIN command to the server */
1527   silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 7,
1528                               1, name, strlen(name),
1529                               2, silc_buffer_datalen(conn->internal->
1530                                                      local_idp),
1531                               3, passphrase, passphrase_len,
1532                               4, cipher, cipher ? strlen(cipher) : 0,
1533                               5, hmac, hmac ? strlen(hmac) : 0,
1534                               6, silc_buffer_datalen(auth),
1535                               7, silc_buffer_datalen(cauth));
1536
1537   if (passphrase)
1538     memset(passphrase, 0, strlen(passphrase));
1539   silc_free(passphrase);
1540   silc_client_unref_channel(client, conn, channel);
1541   if (j) {
1542     silc_buffer_free(j->auth);
1543     silc_buffer_free(j->cauth);
1544     silc_free(j);
1545   }
1546
1547   /* Notify application */
1548   COMMAND(SILC_STATUS_OK);
1549
1550   /** Wait for command reply */
1551   silc_fsm_next(fsm, silc_client_command_reply_wait);
1552   return SILC_FSM_CONTINUE;
1553
1554  out:
1555   silc_client_unref_channel(client, conn, channel);
1556   return SILC_FSM_FINISH;
1557 }
1558
1559 /********************************** MOTD ************************************/
1560
1561 /* MOTD command. Requests motd from server. */
1562
1563 SILC_FSM_STATE(silc_client_command_motd)
1564 {
1565   SilcClientCommandContext cmd = fsm_context;
1566   SilcClientConnection conn = cmd->conn;
1567
1568   if (cmd->argc < 1 || cmd->argc > 2) {
1569     SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1570         "Usage: /MOTD [<server>]");
1571     COMMAND_ERROR((cmd->argc < 1 ? SILC_STATUS_ERR_NOT_ENOUGH_PARAMS :
1572                    SILC_STATUS_ERR_TOO_MANY_PARAMS));
1573     return SILC_FSM_FINISH;
1574   }
1575
1576   /* Send the command */
1577   if (cmd->argc == 1)
1578     silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1579                                 1, conn->remote_host,
1580                                 strlen(conn->remote_host));
1581   else
1582     silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1583                                 1, cmd->argv[1], cmd->argv_lens[1]);
1584
1585   /* Notify application */
1586   COMMAND(SILC_STATUS_OK);
1587
1588   /** Wait for command reply */
1589   silc_fsm_next(fsm, silc_client_command_reply_wait);
1590   return SILC_FSM_CONTINUE;
1591 }
1592
1593 /********************************** UMODE ***********************************/
1594
1595 /* UMODE. Set/unset user mode in SILC. This is used mainly to unset the
1596    modes as client cannot set itself server/router operator privileges. */
1597
1598 SILC_FSM_STATE(silc_client_command_umode)
1599 {
1600   SilcClientCommandContext cmd = fsm_context;
1601   SilcClientConnection conn = cmd->conn;
1602   unsigned char *cp, modebuf[4];
1603   SilcUInt32 mode, add, len;
1604   int i;
1605
1606   if (cmd->argc < 2) {
1607     SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1608         "Usage: /UMODE +|-<modes>");
1609     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1610     return SILC_FSM_FINISH;
1611   }
1612
1613   mode = conn->local_entry->mode;
1614
1615   /* Are we adding or removing mode */
1616   if (cmd->argv[1][0] == '-')
1617     add = FALSE;
1618   else
1619     add = TRUE;
1620
1621   /* Parse mode */
1622   cp = cmd->argv[1] + 1;
1623   len = strlen(cp);
1624   for (i = 0; i < len; i++) {
1625     switch(cp[i]) {
1626     case 'a':
1627       if (add) {
1628         mode = 0;
1629         mode |= SILC_UMODE_SERVER_OPERATOR;
1630         mode |= SILC_UMODE_ROUTER_OPERATOR;
1631         mode |= SILC_UMODE_GONE;
1632         mode |= SILC_UMODE_INDISPOSED;
1633         mode |= SILC_UMODE_BUSY;
1634         mode |= SILC_UMODE_PAGE;
1635         mode |= SILC_UMODE_HYPER;
1636         mode |= SILC_UMODE_ROBOT;
1637         mode |= SILC_UMODE_BLOCK_PRIVMSG;
1638         mode |= SILC_UMODE_REJECT_WATCHING;
1639       } else {
1640         mode = SILC_UMODE_NONE;
1641       }
1642       break;
1643     case 's':
1644       if (add)
1645         mode |= SILC_UMODE_SERVER_OPERATOR;
1646       else
1647         mode &= ~SILC_UMODE_SERVER_OPERATOR;
1648       break;
1649     case 'r':
1650       if (add)
1651         mode |= SILC_UMODE_ROUTER_OPERATOR;
1652       else
1653         mode &= ~SILC_UMODE_ROUTER_OPERATOR;
1654       break;
1655     case 'g':
1656       if (add)
1657         mode |= SILC_UMODE_GONE;
1658       else
1659         mode &= ~SILC_UMODE_GONE;
1660       break;
1661     case 'i':
1662       if (add)
1663         mode |= SILC_UMODE_INDISPOSED;
1664       else
1665         mode &= ~SILC_UMODE_INDISPOSED;
1666       break;
1667     case 'b':
1668       if (add)
1669         mode |= SILC_UMODE_BUSY;
1670       else
1671         mode &= ~SILC_UMODE_BUSY;
1672       break;
1673     case 'p':
1674       if (add)
1675         mode |= SILC_UMODE_PAGE;
1676       else
1677         mode &= ~SILC_UMODE_PAGE;
1678       break;
1679     case 'h':
1680       if (add)
1681         mode |= SILC_UMODE_HYPER;
1682       else
1683         mode &= ~SILC_UMODE_HYPER;
1684       break;
1685     case 't':
1686       if (add)
1687         mode |= SILC_UMODE_ROBOT;
1688       else
1689         mode &= ~SILC_UMODE_ROBOT;
1690       break;
1691     case 'P':
1692       if (add)
1693         mode |= SILC_UMODE_BLOCK_PRIVMSG;
1694       else
1695         mode &= ~SILC_UMODE_BLOCK_PRIVMSG;
1696       break;
1697     case 'w':
1698       if (add)
1699         mode |= SILC_UMODE_REJECT_WATCHING;
1700       else
1701         mode &= ~SILC_UMODE_REJECT_WATCHING;
1702       break;
1703     case 'I':
1704       if (add)
1705         mode |= SILC_UMODE_BLOCK_INVITE;
1706       else
1707         mode &= ~SILC_UMODE_BLOCK_INVITE;
1708       break;
1709     default:
1710       COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE);
1711       return SILC_FSM_FINISH;
1712       break;
1713     }
1714   }
1715
1716   SILC_PUT32_MSB(mode, modebuf);
1717
1718   /* Send the command */
1719   silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
1720                               1, silc_buffer_datalen(conn->internal->
1721                                                      local_idp),
1722                               2, modebuf, sizeof(modebuf));
1723
1724   /* Notify application */
1725   COMMAND(SILC_STATUS_OK);
1726
1727   /** Wait for command reply */
1728   silc_fsm_next(fsm, silc_client_command_reply_wait);
1729   return SILC_FSM_CONTINUE;
1730 }
1731
1732 /********************************** CMODE ***********************************/
1733
1734 /* Signature callback */
1735
1736 static void silc_client_command_cmode_signed(const SilcBuffer buffer,
1737                                              void *context)
1738 {
1739   SilcClientCommandContext cmd = context;
1740
1741   if (!buffer) {
1742     silc_fsm_finish(&cmd->thread);
1743     return;
1744   }
1745
1746   silc_fsm_set_state_context(&cmd->thread, buffer);
1747   SILC_FSM_CALL_CONTINUE_SYNC(&cmd->thread);
1748 }
1749
1750 /* CMODE command. Sets channel mode. Modes that does not require any arguments
1751    can be set several at once. Those modes that require argument must be set
1752    separately (unless set with modes that does not require arguments). */
1753
1754 SILC_FSM_STATE(silc_client_command_cmode)
1755 {
1756   SilcClientCommandContext cmd = fsm_context;
1757   SilcClientConnection conn = cmd->conn;
1758   SilcClient client = conn->client;
1759   SilcBuffer auth = state_context;
1760   SilcChannelEntry channel = NULL;
1761   SilcBuffer chidp, pk = NULL;
1762   unsigned char *name, *cp, modebuf[4], tmp[4], *arg = NULL;
1763   SilcUInt32 mode, add, type, len, arg_len = 0;
1764   int i;
1765
1766   if (cmd->argc < 3) {
1767     SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1768         "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1769     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1770     goto out;
1771   }
1772
1773   if (cmd->argv[1][0] == '*') {
1774     if (!conn->current_channel) {
1775       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1776       goto out;
1777     }
1778
1779     channel = conn->current_channel;
1780     silc_client_ref_channel(client, conn, channel);
1781   } else {
1782     name = cmd->argv[1];
1783
1784     channel = silc_client_get_channel(conn->client, conn, name);
1785     if (!channel) {
1786       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1787       goto out;
1788     }
1789   }
1790
1791   mode = channel->mode;
1792
1793   /* Are we adding or removing mode */
1794   if (cmd->argv[2][0] == '-')
1795     add = FALSE;
1796   else
1797     add = TRUE;
1798
1799   /* Argument type to be sent to server */
1800   type = 0;
1801
1802   /* Parse mode */
1803   cp = cmd->argv[2] + 1;
1804   len = strlen(cp);
1805   for (i = 0; i < len; i++) {
1806     switch(cp[i]) {
1807     case 'p':
1808       if (add)
1809         mode |= SILC_CHANNEL_MODE_PRIVATE;
1810       else
1811         mode &= ~SILC_CHANNEL_MODE_PRIVATE;
1812       break;
1813     case 's':
1814       if (add)
1815         mode |= SILC_CHANNEL_MODE_SECRET;
1816       else
1817         mode &= ~SILC_CHANNEL_MODE_SECRET;
1818       break;
1819     case 'k':
1820       if (add)
1821         mode |= SILC_CHANNEL_MODE_PRIVKEY;
1822       else
1823         mode &= ~SILC_CHANNEL_MODE_PRIVKEY;
1824       break;
1825     case 'i':
1826       if (add)
1827         mode |= SILC_CHANNEL_MODE_INVITE;
1828       else
1829         mode &= ~SILC_CHANNEL_MODE_INVITE;
1830       break;
1831     case 't':
1832       if (add)
1833         mode |= SILC_CHANNEL_MODE_TOPIC;
1834       else
1835         mode &= ~SILC_CHANNEL_MODE_TOPIC;
1836       break;
1837     case 'm':
1838       if (add)
1839         mode |= SILC_CHANNEL_MODE_SILENCE_USERS;
1840       else
1841         mode &= ~SILC_CHANNEL_MODE_SILENCE_USERS;
1842       break;
1843     case 'M':
1844       if (add)
1845         mode |= SILC_CHANNEL_MODE_SILENCE_OPERS;
1846       else
1847         mode &= ~SILC_CHANNEL_MODE_SILENCE_OPERS;
1848       break;
1849     case 'l':
1850       if (add) {
1851         int ll;
1852         mode |= SILC_CHANNEL_MODE_ULIMIT;
1853         type = 3;
1854         if (cmd->argc < 4) {
1855           SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1856               "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1857           COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1858           goto out;
1859         }
1860         ll = atoi(cmd->argv[3]);
1861         SILC_PUT32_MSB(ll, tmp);
1862         arg = tmp;
1863         arg_len = 4;
1864       } else {
1865         mode &= ~SILC_CHANNEL_MODE_ULIMIT;
1866       }
1867       break;
1868     case 'a':
1869       if (add) {
1870         mode |= SILC_CHANNEL_MODE_PASSPHRASE;
1871         type = 4;
1872         if (cmd->argc < 4) {
1873           SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1874               "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1875           COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1876           goto out;
1877         }
1878         arg = cmd->argv[3];
1879         arg_len = cmd->argv_lens[3];
1880       } else {
1881         mode &= ~SILC_CHANNEL_MODE_PASSPHRASE;
1882       }
1883       break;
1884     case 'c':
1885       if (add) {
1886         mode |= SILC_CHANNEL_MODE_CIPHER;
1887         type = 5;
1888         if (cmd->argc < 4) {
1889           SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1890               "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1891           COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1892           goto out;
1893         }
1894         arg = cmd->argv[3];
1895         arg_len = cmd->argv_lens[3];
1896       } else {
1897         mode &= ~SILC_CHANNEL_MODE_CIPHER;
1898       }
1899       break;
1900     case 'h':
1901       if (add) {
1902         mode |= SILC_CHANNEL_MODE_HMAC;
1903         type = 6;
1904         if (cmd->argc < 4) {
1905           SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1906               "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1907           COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1908           goto out;
1909         }
1910         arg = cmd->argv[3];
1911         arg_len = cmd->argv_lens[3];
1912       } else {
1913         mode &= ~SILC_CHANNEL_MODE_HMAC;
1914       }
1915       break;
1916     case 'f':
1917       if (add) {
1918         if (!auth) {
1919           SilcPublicKey pubkey = conn->public_key;
1920           SilcPrivateKey privkey = conn->private_key;
1921
1922           mode |= SILC_CHANNEL_MODE_FOUNDER_AUTH;
1923           type = 7;
1924
1925           if (cmd->argc >= 5) {
1926             char *pass = "";
1927             if (cmd->argc >= 6)
1928               pass = cmd->argv[5];
1929             if (!silc_load_key_pair(cmd->argv[3], cmd->argv[4], pass,
1930                                     &pubkey, &privkey)) {
1931               SAY(client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
1932                   "Could not load key pair, check your arguments");
1933               COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1934               goto out;
1935             }
1936           }
1937
1938           pk = silc_public_key_payload_encode(NULL, pubkey);
1939           silc_client_unref_channel(client, conn, channel);
1940           SILC_FSM_CALL(silc_auth_public_key_auth_generate(
1941                                              pubkey, privkey,
1942                                              conn->client->rng,
1943                                              conn->internal->sha1hash,
1944                                              conn->local_id,
1945                                              SILC_ID_CLIENT,
1946                                              silc_client_command_cmode_signed,
1947                                              cmd));
1948           /* NOT REACHED */
1949         }
1950         arg = silc_buffer_data(auth);
1951         arg_len = silc_buffer_len(auth);
1952       } else {
1953         mode &= ~SILC_CHANNEL_MODE_FOUNDER_AUTH;
1954       }
1955       break;
1956     case 'C':
1957       if (add) {
1958         int k;
1959         SilcBool chadd = FALSE;
1960         SilcPublicKey chpk = NULL;
1961
1962         mode |= SILC_CHANNEL_MODE_CHANNEL_AUTH;
1963         type = 9;
1964
1965         if (cmd->argc == 3) {
1966           /* Send empty command to receive the public key list. */
1967           chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
1968           silc_client_command_send_va(conn, cmd, SILC_COMMAND_CMODE,
1969                                       NULL, NULL, 1,
1970                                       1, silc_buffer_datalen(chidp));
1971           silc_buffer_free(chidp);
1972           silc_client_unref_channel(client, conn, channel);
1973
1974           /* Notify application */
1975           COMMAND(SILC_STATUS_OK);
1976
1977           /** Wait for command reply */
1978           silc_fsm_next(fsm, silc_client_command_reply_wait);
1979           return SILC_FSM_CONTINUE;
1980         }
1981
1982         if (cmd->argc >= 4) {
1983           auth = silc_buffer_alloc_size(2);
1984           silc_buffer_format(auth,
1985                              SILC_STR_UI_SHORT(cmd->argc - 3),
1986                              SILC_STR_END);
1987         }
1988
1989         for (k = 3; k < cmd->argc; k++) {
1990           if (cmd->argv[k][0] == '+')
1991             chadd = TRUE;
1992           if (!silc_pkcs_load_public_key(cmd->argv[k] + 1, &chpk)) {
1993             SAY(conn->client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
1994                 "Could not load public key %s, check the filename",
1995                 cmd->argv[k]);
1996             COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1997             silc_buffer_free(auth);
1998             goto out;
1999           }
2000
2001           if (chpk) {
2002             pk = silc_public_key_payload_encode(NULL, chpk);
2003             auth = silc_argument_payload_encode_one(auth,
2004                                                     silc_buffer_datalen(pk),
2005                                                     chadd ? 0x00 : 0x01);
2006             silc_pkcs_public_key_free(chpk);
2007             silc_buffer_free(pk);
2008             pk = NULL;
2009           }
2010         }
2011
2012         arg = silc_buffer_data(auth);
2013         arg_len = silc_buffer_len(auth);
2014       } else {
2015         mode &= ~SILC_CHANNEL_MODE_CHANNEL_AUTH;
2016       }
2017       break;
2018     default:
2019       COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE);
2020       goto out;
2021       break;
2022     }
2023   }
2024
2025   chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2026   SILC_PUT32_MSB(mode, modebuf);
2027
2028   /* Send the command. We support sending only one mode at once that
2029      requires an argument. */
2030   if (type && arg) {
2031     silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 4,
2032                                 1, silc_buffer_datalen(chidp),
2033                                 2, modebuf, sizeof(modebuf),
2034                                 type, arg, arg_len,
2035                                 8, silc_buffer_datalen(pk));
2036   } else {
2037     silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
2038                                 1, silc_buffer_datalen(chidp),
2039                                 2, modebuf, sizeof(modebuf));
2040   }
2041
2042   silc_buffer_free(chidp);
2043   silc_buffer_free(auth);
2044   silc_buffer_free(pk);
2045   silc_client_unref_channel(client, conn, channel);
2046
2047   /* Notify application */
2048   COMMAND(SILC_STATUS_OK);
2049
2050   /** Wait for command reply */
2051   silc_fsm_next(fsm, silc_client_command_reply_wait);
2052   return SILC_FSM_CONTINUE;
2053
2054  out:
2055   silc_client_unref_channel(client, conn, channel);
2056   return SILC_FSM_FINISH;
2057 }
2058
2059 /********************************* CUMODE ***********************************/
2060
2061 /* Signature callback */
2062
2063 static void silc_client_command_cumode_signed(const SilcBuffer buffer,
2064                                               void *context)
2065 {
2066   SilcClientCommandContext cmd = context;
2067
2068   if (!buffer) {
2069     silc_fsm_finish(&cmd->thread);
2070     return;
2071   }
2072
2073   silc_fsm_set_state_context(&cmd->thread, buffer);
2074   SILC_FSM_CALL_CONTINUE_SYNC(&cmd->thread);
2075 }
2076
2077 /* CUMODE command. Changes client's mode on a channel. */
2078
2079 SILC_FSM_STATE(silc_client_command_cumode)
2080 {
2081   SilcClientCommandContext cmd = fsm_context;
2082   SilcClientConnection conn = cmd->conn;
2083   SilcClient client = conn->client;
2084   SilcBuffer auth = state_context;
2085   SilcChannelEntry channel = NULL;
2086   SilcChannelUser chu;
2087   SilcClientEntry client_entry;
2088   SilcBuffer clidp, chidp;
2089   SilcDList clients = NULL;
2090   unsigned char *name, *cp, modebuf[4];
2091   SilcUInt32 mode = 0, add, len;
2092   char *nickname = NULL;
2093   int i;
2094
2095   if (cmd->argc < 4) {
2096     SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2097         "Usage: /CUMODE <channel> +|-<modes> <nickname>[@<server>]");
2098     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2099     goto out;
2100   }
2101
2102   if (cmd->argv[1][0] == '*') {
2103     if (!conn->current_channel) {
2104       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2105       goto out;
2106     }
2107
2108     channel = conn->current_channel;
2109     silc_client_ref_channel(client, conn, channel);
2110   } else {
2111     name = cmd->argv[1];
2112
2113     channel = silc_client_get_channel(conn->client, conn, name);
2114     if (!channel) {
2115       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2116       goto out;
2117     }
2118   }
2119
2120   /* Parse the typed nickname. */
2121   silc_client_nickname_parse(client, conn, cmd->argv[3], &nickname);
2122
2123   /* Find client entry */
2124   clients = silc_client_get_clients_local(client, conn, cmd->argv[3], FALSE);
2125   if (!clients)
2126     /* Resolve client information */
2127     SILC_FSM_CALL(silc_client_get_clients(client, conn, nickname, NULL,
2128                                           silc_client_command_resolve_continue,
2129                                           cmd));
2130
2131   client_entry = silc_dlist_get(clients);
2132
2133   /* Get the current mode */
2134   chu = silc_client_on_channel(channel, client_entry);
2135   if (chu)
2136     mode = chu->mode;
2137
2138   /* Are we adding or removing mode */
2139   if (cmd->argv[2][0] == '-')
2140     add = FALSE;
2141   else
2142     add = TRUE;
2143
2144   /* Parse mode */
2145   cp = cmd->argv[2] + 1;
2146   len = strlen(cp);
2147   for (i = 0; i < len; i++) {
2148     switch(cp[i]) {
2149     case 'a':
2150       if (add) {
2151         mode |= SILC_CHANNEL_UMODE_CHANFO;
2152         mode |= SILC_CHANNEL_UMODE_CHANOP;
2153         mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
2154         mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
2155         mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
2156       } else {
2157         mode = SILC_CHANNEL_UMODE_NONE;
2158       }
2159       break;
2160     case 'f':
2161       if (add) {
2162         if (!auth) {
2163           SilcPublicKey pubkey = conn->public_key;
2164           SilcPrivateKey privkey = conn->private_key;
2165
2166           if (cmd->argc >= 6) {
2167             char *pass = "";
2168             if (cmd->argc >= 7)
2169               pass = cmd->argv[6];
2170             if (!silc_load_key_pair(cmd->argv[4], cmd->argv[5], pass,
2171                                     &pubkey, &privkey)) {
2172               SAY(conn->client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
2173                   "Could not load key pair, check your arguments");
2174               COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2175               goto out;
2176             }
2177           }
2178
2179           silc_free(nickname);
2180           silc_client_list_free(client, conn, clients);
2181           silc_client_unref_channel(client, conn, channel);
2182
2183           SILC_FSM_CALL(silc_auth_public_key_auth_generate(
2184                                              pubkey, privkey,
2185                                              conn->client->rng,
2186                                              conn->internal->sha1hash,
2187                                              conn->local_id,
2188                                              SILC_ID_CLIENT,
2189                                              silc_client_command_cumode_signed,
2190                                              cmd));
2191           /* NOT REACHED */
2192         }
2193
2194         mode |= SILC_CHANNEL_UMODE_CHANFO;
2195       } else {
2196         mode &= ~SILC_CHANNEL_UMODE_CHANFO;
2197       }
2198       break;
2199     case 'o':
2200       if (add)
2201         mode |= SILC_CHANNEL_UMODE_CHANOP;
2202       else
2203         mode &= ~SILC_CHANNEL_UMODE_CHANOP;
2204       break;
2205     case 'b':
2206       if (add)
2207         mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
2208       else
2209         mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
2210       break;
2211     case 'u':
2212       if (add)
2213         mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
2214       else
2215         mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
2216       break;
2217     case 'r':
2218       if (add)
2219         mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
2220       else
2221         mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
2222       break;
2223     case 'q':
2224       if (add)
2225         mode |= SILC_CHANNEL_UMODE_QUIET;
2226       else
2227         mode &= ~SILC_CHANNEL_UMODE_QUIET;
2228       break;
2229     default:
2230       COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE);
2231       goto out;
2232       break;
2233     }
2234   }
2235
2236   chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2237   SILC_PUT32_MSB(mode, modebuf);
2238   clidp = silc_id_payload_encode(&client_entry->id, SILC_ID_CLIENT);
2239
2240   /* Send the command packet. We support sending only one mode at once
2241      that requires an argument. */
2242   silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, auth ? 4 : 3,
2243                               1, silc_buffer_datalen(chidp),
2244                               2, modebuf, 4,
2245                               3, silc_buffer_datalen(clidp),
2246                               4, silc_buffer_datalen(auth));
2247
2248   silc_buffer_free(chidp);
2249   silc_buffer_free(clidp);
2250   if (auth)
2251     silc_buffer_free(auth);
2252   silc_free(nickname);
2253   silc_client_list_free(client, conn, clients);
2254   silc_client_unref_channel(client, conn, channel);
2255
2256   /* Notify application */
2257   COMMAND(SILC_STATUS_OK);
2258
2259   /** Wait for command reply */
2260   silc_fsm_next(fsm, silc_client_command_reply_wait);
2261   return SILC_FSM_CONTINUE;
2262
2263  out:
2264   silc_client_unref_channel(client, conn, channel);
2265   silc_client_list_free(client, conn, clients);
2266   silc_free(nickname);
2267   return SILC_FSM_FINISH;
2268 }
2269
2270 /********************************** KICK ************************************/
2271
2272 /* KICK command. Kicks a client out of channel. */
2273
2274 SILC_FSM_STATE(silc_client_command_kick)
2275 {
2276   SilcClientCommandContext cmd = fsm_context;
2277   SilcClientConnection conn = cmd->conn;
2278   SilcClient client = conn->client;
2279   SilcChannelEntry channel = NULL;
2280   SilcBuffer idp, idp2;
2281   SilcClientEntry target;
2282   SilcDList clients = NULL;
2283   char *name, tmp[512];
2284
2285   if (cmd->argc < 3) {
2286     SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2287         "Usage: /KICK <channel> <nickname> [<comment>]");
2288     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2289     goto out;
2290   }
2291
2292   if (cmd->argv[1][0] == '*') {
2293     if (!conn->current_channel) {
2294       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2295       goto out;
2296     }
2297
2298     if (client->internal->params->full_channel_names)
2299       silc_snprintf(tmp, sizeof(tmp), conn->current_channel->channel_name);
2300     else
2301       silc_snprintf(tmp, sizeof(tmp), "%s%s%s",
2302                     conn->current_channel->channel_name,
2303                     conn->current_channel->server[0] ? "@" : "",
2304                     conn->current_channel->server);
2305     name = tmp;
2306   } else {
2307     name = cmd->argv[1];
2308   }
2309
2310   if (!conn->current_channel) {
2311     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2312     goto out;
2313   }
2314
2315   /* Get the Channel ID of the channel */
2316   channel = silc_client_get_channel(conn->client, conn, name);
2317   if (!channel) {
2318     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2319     goto out;
2320   }
2321
2322   /* Get the target client */
2323   clients = silc_client_get_clients_local(client, conn, cmd->argv[2], FALSE);
2324   if (!clients) {
2325     SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2326         "No such client: %s", cmd->argv[2]);
2327     COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_NICK);
2328     goto out;
2329   }
2330   target = silc_dlist_get(clients);
2331
2332   /* Send KICK command to the server */
2333   idp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2334   idp2 = silc_id_payload_encode(&target->id, SILC_ID_CLIENT);
2335   if (cmd->argc == 3)
2336     silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
2337                                 1, silc_buffer_datalen(idp),
2338                                 2, silc_buffer_datalen(idp2));
2339   else
2340     silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 3,
2341                                 1, silc_buffer_datalen(idp),
2342                                 2, silc_buffer_datalen(idp2),
2343                                 3, cmd->argv[3], strlen(cmd->argv[3]));
2344
2345   silc_buffer_free(idp);
2346   silc_buffer_free(idp2);
2347   silc_client_list_free(client, conn, clients);
2348   silc_client_unref_channel(client, conn, channel);
2349
2350   /* Notify application */
2351   COMMAND(SILC_STATUS_OK);
2352
2353   /** Wait for command reply */
2354   silc_fsm_next(fsm, silc_client_command_reply_wait);
2355   return SILC_FSM_CONTINUE;
2356
2357  out:
2358   silc_client_unref_channel(client, conn, channel);
2359   return SILC_FSM_FINISH;
2360 }
2361
2362 /***************************** OPER & SILCOPER ******************************/
2363
2364 typedef struct {
2365   unsigned char *passphrase;
2366   SilcUInt32 passphrase_len;
2367   SilcBuffer auth;
2368 } *SilcClientCommandOper;
2369
2370 /* Ask passphrase callback */
2371
2372 static void silc_client_command_oper_cb(const unsigned char *data,
2373                                         SilcUInt32 data_len, void *context)
2374 {
2375   SilcClientCommandContext cmd = context;
2376   SilcClientCommandOper oper = cmd->context;
2377
2378   if (data && data_len)
2379     oper->passphrase = silc_memdup(data, data_len);
2380   oper->passphrase_len = data_len;
2381
2382   /* Continue */
2383   SILC_FSM_CALL_CONTINUE(&cmd->thread);
2384 }
2385
2386 static void silc_client_command_oper_sign_cb(const SilcBuffer data,
2387                                              void *context)
2388 {
2389   SilcClientCommandContext cmd = context;
2390   SilcClientCommandOper oper = cmd->context;
2391
2392   if (data)
2393     oper->auth = silc_buffer_copy(data);
2394
2395   /* Continue */
2396   SILC_FSM_CALL_CONTINUE(&cmd->thread);
2397 }
2398
2399 /* Send OPER/SILCOPER command */
2400
2401 SILC_FSM_STATE(silc_client_command_oper_send)
2402 {
2403   SilcClientCommandContext cmd = fsm_context;
2404   SilcClientConnection conn = cmd->conn;
2405   SilcClientCommandOper oper = cmd->context;
2406   SilcBuffer auth = oper ? oper->auth : NULL;
2407
2408   silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
2409                               1, cmd->argv[1], strlen(cmd->argv[1]),
2410                               2, silc_buffer_datalen(auth));
2411
2412   silc_buffer_clear(auth);
2413   silc_buffer_free(auth);
2414   if (oper) {
2415     silc_free(oper->passphrase);
2416     silc_free(oper);
2417   }
2418
2419   /* Notify application */
2420   COMMAND(SILC_STATUS_OK);
2421
2422   /** Wait for command reply */
2423   silc_fsm_next(fsm, silc_client_command_reply_wait);
2424   return SILC_FSM_CONTINUE;
2425 }
2426
2427 /* Get authentication data */
2428
2429 SILC_FSM_STATE(silc_client_command_oper_get_auth)
2430 {
2431   SilcClientCommandContext cmd = fsm_context;
2432   SilcClientConnection conn = cmd->conn;
2433   SilcClientCommandOper oper = cmd->context;
2434
2435   silc_fsm_next(fsm, silc_client_command_oper_send);
2436
2437   if (!oper || !oper->passphrase) {
2438     /* Encode the public key authentication payload */
2439     SILC_FSM_CALL(silc_auth_public_key_auth_generate(
2440                                        conn->public_key,
2441                                        conn->private_key,
2442                                        conn->client->rng,
2443                                        conn->internal->hash,
2444                                        conn->local_id, SILC_ID_CLIENT,
2445                                        silc_client_command_oper_sign_cb,
2446                                        oper));
2447     /* NOT REACHED */
2448   }
2449
2450   /* Encode the password authentication payload */
2451   oper->auth = silc_auth_payload_encode(NULL, SILC_AUTH_PASSWORD, NULL, 0,
2452                                         oper->passphrase, oper->passphrase_len);
2453
2454   return SILC_FSM_CONTINUE;
2455 }
2456
2457 /* OPER command. Used to obtain server operator privileges. */
2458
2459 SILC_FSM_STATE(silc_client_command_oper)
2460 {
2461   SilcClientCommandContext cmd = fsm_context;
2462   SilcClientConnection conn = cmd->conn;
2463   SilcClientCommandOper oper;
2464
2465   if (cmd->argc < 2) {
2466     SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2467         "Usage: /OPER <username> [-pubkey]");
2468     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2469     return SILC_FSM_FINISH;
2470   }
2471
2472   silc_fsm_next(fsm, silc_client_command_oper_get_auth);
2473
2474   /* Get passphrase */
2475   if (cmd->argc < 3) {
2476     oper = silc_calloc(1, sizeof(*oper));
2477     if (!oper)
2478       return SILC_FSM_FINISH;
2479     cmd->context = oper;
2480     SILC_FSM_CALL(conn->client->internal->
2481                   ops->ask_passphrase(conn->client, conn,
2482                                       silc_client_command_oper_cb, cmd));
2483   }
2484
2485   return SILC_FSM_CONTINUE;
2486 }
2487
2488 /* SILCOPER command. Used to obtain router operator privileges. */
2489
2490 SILC_FSM_STATE(silc_client_command_silcoper)
2491 {
2492   SilcClientCommandContext cmd = fsm_context;
2493   SilcClientConnection conn = cmd->conn;
2494   SilcClientCommandOper oper;
2495
2496   if (cmd->argc < 2) {
2497     SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2498         "Usage: /SILCOPER <username> [-pubkey]");
2499     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2500     return SILC_FSM_FINISH;
2501   }
2502
2503   silc_fsm_next(fsm, silc_client_command_oper_send);
2504
2505   /* Get passphrase */
2506   if (cmd->argc < 3) {
2507     oper = silc_calloc(1, sizeof(*oper));
2508     if (!oper)
2509       return SILC_FSM_FINISH;
2510     cmd->context = oper;
2511     SILC_FSM_CALL(conn->client->internal->
2512                   ops->ask_passphrase(conn->client, conn,
2513                                       silc_client_command_oper_cb, cmd));
2514   }
2515
2516   return SILC_FSM_CONTINUE;
2517 }
2518
2519 /*********************************** BAN ************************************/
2520
2521 /* Command BAN. This is used to manage the ban list of the channel. */
2522
2523 SILC_FSM_STATE(silc_client_command_ban)
2524 {
2525   SilcClientCommandContext cmd = fsm_context;
2526   SilcClientConnection conn = cmd->conn;
2527   SilcClient client = conn->client;
2528   SilcChannelEntry channel;
2529   SilcBuffer chidp, args = NULL;
2530   char *name, *ban = NULL;
2531   unsigned char action[1];
2532   SilcPublicKey pubkey = NULL;
2533
2534   if (cmd->argc < 2) {
2535     SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2536         "Usage: /BAN <channel> "
2537         "[+|-[<nickname>[@<server>[!<username>[@hostname>]]]]]");
2538     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2539     goto out;
2540   }
2541
2542   if (cmd->argv[1][0] == '*') {
2543     if (!conn->current_channel) {
2544       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2545       goto out;
2546     }
2547
2548     channel = conn->current_channel;
2549     silc_client_ref_channel(client, conn, channel);
2550   } else {
2551     name = cmd->argv[1];
2552
2553     channel = silc_client_get_channel(conn->client, conn, name);
2554     if (!channel) {
2555       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2556       goto out;
2557     }
2558   }
2559
2560   if (cmd->argc == 3) {
2561     if (cmd->argv[2][0] == '+')
2562       action[0] = 0x00;
2563     else
2564       action[0] = 0x01;
2565
2566     /* Check if it is public key file to be added to invite list */
2567     silc_pkcs_load_public_key(cmd->argv[2] + 1, &pubkey);
2568     ban = cmd->argv[2];
2569     if (!pubkey)
2570       ban++;
2571   }
2572
2573   if (ban) {
2574     args = silc_buffer_alloc_size(2);
2575     silc_buffer_format(args,
2576                        SILC_STR_UI_SHORT(1),
2577                        SILC_STR_END);
2578     if (pubkey) {
2579       chidp = silc_public_key_payload_encode(NULL, pubkey);
2580       args = silc_argument_payload_encode_one(args,
2581                                               silc_buffer_datalen(chidp), 2);
2582       silc_buffer_free(chidp);
2583       silc_pkcs_public_key_free(pubkey);
2584     } else {
2585       args = silc_argument_payload_encode_one(args, ban, strlen(ban), 1);
2586     }
2587   }
2588
2589   chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2590
2591   /* Send the command */
2592   silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 3,
2593                               1, silc_buffer_datalen(chidp),
2594                               2, args ? action : NULL, args ? 1 : 0,
2595                               3, silc_buffer_datalen(args));
2596
2597   silc_buffer_free(chidp);
2598   silc_buffer_free(args);
2599   silc_client_unref_channel(client, conn, channel);
2600
2601   /* Notify application */
2602   COMMAND(SILC_STATUS_OK);
2603
2604   /** Wait for command reply */
2605   silc_fsm_next(fsm, silc_client_command_reply_wait);
2606   return SILC_FSM_CONTINUE;
2607
2608  out:
2609   return SILC_FSM_FINISH;
2610 }
2611
2612 /********************************* DETACH ***********************************/
2613
2614 /* Command DETACH. This is used to detach from the server */
2615
2616 SILC_FSM_STATE(silc_client_command_detach)
2617 {
2618   SilcClientCommandContext cmd = fsm_context;
2619   SilcClientConnection conn = cmd->conn;
2620
2621   silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 0);
2622
2623   /* Notify application */
2624   COMMAND(SILC_STATUS_OK);
2625
2626   /** Wait for command reply */
2627   silc_fsm_next(fsm, silc_client_command_reply_wait);
2628   return SILC_FSM_CONTINUE;
2629 }
2630
2631 /********************************** WATCH ***********************************/
2632
2633 /* Command WATCH. */
2634
2635 SILC_FSM_STATE(silc_client_command_watch)
2636 {
2637   SilcClientCommandContext cmd = fsm_context;
2638   SilcClientConnection conn = cmd->conn;
2639   SilcBuffer args = NULL;
2640   int type = 0;
2641   const char *pubkey = NULL;
2642   SilcBool pubkey_add = TRUE;
2643
2644   if (cmd->argc < 3) {
2645     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2646     goto out;
2647   }
2648
2649   if (!strcasecmp(cmd->argv[1], "-add")) {
2650     type = 2;
2651   } else if (!strcasecmp(cmd->argv[1], "-del")) {
2652     type = 3;
2653   } else if (!strcasecmp(cmd->argv[1], "-pubkey") && cmd->argc >= 3) {
2654     type = 4;
2655     pubkey = cmd->argv[2] + 1;
2656     if (cmd->argv[2][0] == '-')
2657       pubkey_add = FALSE;
2658   } else {
2659     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2660     goto out;
2661   }
2662
2663   if (pubkey) {
2664     SilcPublicKey pk;
2665     SilcBuffer buffer;
2666
2667     if (!silc_pkcs_load_public_key(pubkey, &pk)) {
2668       SAY(conn->client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
2669           "Could not load public key %s, check the filename", pubkey);
2670       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2671       goto out;
2672     }
2673
2674     args = silc_buffer_alloc_size(2);
2675     silc_buffer_format(args,
2676                        SILC_STR_UI_SHORT(1),
2677                        SILC_STR_END);
2678     buffer = silc_public_key_payload_encode(NULL, pk);
2679     args = silc_argument_payload_encode_one(args, silc_buffer_datalen(buffer),
2680                                             pubkey_add ? 0x00 : 0x01);
2681     silc_buffer_free(buffer);
2682     silc_pkcs_public_key_free(pk);
2683   }
2684
2685   /* If watching by nickname, resolve all users with that nickname so that
2686      we get their information immediately. */
2687   if (type == 2)
2688     silc_client_get_clients(conn->client, conn, cmd->argv[2], NULL,
2689                             silc_client_command_resolve_dummy, NULL);
2690
2691   /* Send the commmand */
2692   silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
2693                               1, silc_buffer_datalen(conn->internal->
2694                                                      local_idp),
2695                               type, pubkey ? args->data : cmd->argv[2],
2696                               pubkey ? silc_buffer_len(args) :
2697                               cmd->argv_lens[2]);
2698
2699   silc_buffer_free(args);
2700
2701   /* Notify application */
2702   COMMAND(SILC_STATUS_OK);
2703
2704   /** Wait for command reply */
2705   silc_fsm_next(fsm, silc_client_command_reply_wait);
2706   return SILC_FSM_CONTINUE;
2707
2708  out:
2709   return SILC_FSM_FINISH;
2710 }
2711
2712 /********************************** LEAVE ***********************************/
2713
2714 /* LEAVE command. Leaves a channel. Client removes itself from a channel. */
2715
2716 SILC_FSM_STATE(silc_client_command_leave)
2717 {
2718   SilcClientCommandContext cmd = fsm_context;
2719   SilcClientConnection conn = cmd->conn;
2720   SilcClient client = conn->client;
2721   SilcChannelEntry channel;
2722   SilcBuffer idp;
2723   char *name, tmp[512];
2724
2725   if (cmd->argc != 2) {
2726     SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2727         "Usage: /LEAVE <channel>");
2728     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2729     goto out;
2730   }
2731
2732   if (cmd->argv[1][0] == '*') {
2733     if (!conn->current_channel) {
2734       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2735       goto out;
2736     }
2737
2738     if (client->internal->params->full_channel_names)
2739       silc_snprintf(tmp, sizeof(tmp), conn->current_channel->channel_name);
2740     else
2741       silc_snprintf(tmp, sizeof(tmp), "%s%s%s",
2742                     conn->current_channel->channel_name,
2743                     conn->current_channel->server[0] ? "@" : "",
2744                     conn->current_channel->server);
2745     name = tmp;
2746   } else {
2747     name = cmd->argv[1];
2748   }
2749
2750   /* Get the channel entry */
2751   channel = silc_client_get_channel(conn->client, conn, name);
2752   if (!channel) {
2753     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2754     goto out;
2755   }
2756
2757   idp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2758
2759   /* Send LEAVE command to the server */
2760   silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
2761                               1, silc_buffer_datalen(idp));
2762
2763   silc_buffer_free(idp);
2764
2765   /* Notify application */
2766   COMMAND(SILC_STATUS_OK);
2767
2768   if (conn->current_channel == channel)
2769     conn->current_channel = NULL;
2770
2771   silc_client_unref_channel(client, conn, channel);
2772
2773   /** Wait for command reply */
2774   silc_fsm_next(fsm, silc_client_command_reply_wait);
2775   return SILC_FSM_CONTINUE;
2776
2777  out:
2778   return SILC_FSM_FINISH;
2779 }
2780
2781 /********************************** USERS ***********************************/
2782
2783 /* Command USERS. Requests the USERS of the clients joined on requested
2784    channel. */
2785
2786 SILC_FSM_STATE(silc_client_command_users)
2787 {
2788   SilcClientCommandContext cmd = fsm_context;
2789   SilcClientConnection conn = cmd->conn;
2790   char *name, tmp[512];
2791
2792   if (cmd->argc != 2) {
2793     SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2794         "Usage: /USERS <channel>");
2795     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2796     goto out;
2797   }
2798
2799   if (cmd->argv[1][0] == '*') {
2800     if (!conn->current_channel) {
2801       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2802       goto out;
2803     }
2804
2805     if (conn->client->internal->params->full_channel_names)
2806       silc_snprintf(tmp, sizeof(tmp), conn->current_channel->channel_name);
2807     else
2808       silc_snprintf(tmp, sizeof(tmp), "%s%s%s",
2809                     conn->current_channel->channel_name,
2810                     conn->current_channel->server[0] ? "@" : "",
2811                     conn->current_channel->server);
2812     name = tmp;
2813   } else {
2814     name = cmd->argv[1];
2815   }
2816
2817   /* Send USERS command to the server */
2818   silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
2819                               2, name, strlen(name));
2820
2821   /* Notify application */
2822   COMMAND(SILC_STATUS_OK);
2823
2824   /** Wait for command reply */
2825   silc_fsm_next(fsm, silc_client_command_reply_wait);
2826   return SILC_FSM_CONTINUE;
2827
2828  out:
2829   return SILC_FSM_FINISH;
2830 }
2831
2832 /********************************* GETKEY ***********************************/
2833
2834 /* Command GETKEY. Used to fetch remote client's public key. */
2835
2836 SILC_FSM_STATE(silc_client_command_getkey)
2837 {
2838   SilcClientCommandContext cmd = fsm_context;
2839   SilcClientConnection conn = cmd->conn;
2840   SilcClient client = conn->client;
2841   SilcClientEntry client_entry;
2842   SilcServerEntry server_entry;
2843   SilcDList clients;
2844   SilcBuffer idp;
2845
2846   if (cmd->argc < 2) {
2847     client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_INFO,
2848                      "Usage: /GETKEY <nickname or server name>");
2849     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2850     return SILC_FSM_FINISH;
2851   }
2852
2853   /* Find client entry */
2854   clients = silc_client_get_clients_local(client, conn, cmd->argv[1], FALSE);
2855   if (!clients) {
2856     /* Check whether user requested server */
2857     server_entry = silc_client_get_server(client, conn, cmd->argv[1]);
2858     if (!server_entry) {
2859       if (cmd->resolved) {
2860         /* Resolving didn't find anything.  We should never get here as
2861            errors are handled in the resolving callback. */
2862         COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_NICK);
2863         COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_SERVER);
2864         return SILC_FSM_FINISH;
2865       }
2866
2867       /* No client or server exist with this name, query for both. */
2868       cmd->resolved = TRUE;
2869       SILC_FSM_CALL(silc_client_command_send(client, conn,
2870                                              SILC_COMMAND_IDENTIFY,
2871                                              silc_client_command_continue,
2872                                              cmd, 2,
2873                                              1, cmd->argv[1],
2874                                              strlen(cmd->argv[1]),
2875                                              2, cmd->argv[1],
2876                                              strlen(cmd->argv[1])));
2877       /* NOT REACHED */
2878     }
2879     idp = silc_id_payload_encode(&server_entry->id, SILC_ID_SERVER);
2880     silc_client_unref_server(client, conn, server_entry);
2881   } else {
2882     client_entry = silc_dlist_get(clients);
2883     idp = silc_id_payload_encode(&client_entry->id, SILC_ID_CLIENT);
2884     silc_client_list_free(client, conn, clients);
2885   }
2886
2887   /* Send the commmand */
2888   silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
2889                               1, silc_buffer_datalen(idp));
2890
2891   silc_buffer_free(idp);
2892
2893   /* Notify application */
2894   COMMAND(SILC_STATUS_OK);
2895
2896   /** Wait for command reply */
2897   silc_fsm_next(fsm, silc_client_command_reply_wait);
2898   return SILC_FSM_CONTINUE;
2899 }
2900
2901 /********************************* SERVICE **********************************/
2902
2903 /* Command SERVICE.  Negotiates service agreement with server. */
2904 /* XXX incomplete */
2905
2906 SILC_FSM_STATE(silc_client_command_service)
2907 {
2908   SilcClientCommandContext cmd = fsm_context;
2909 #if 0
2910   SilcClientConnection conn = cmd->conn;
2911   SilcBuffer buffer;
2912   char *name;
2913
2914   if (cmd->argc < 2) {
2915     SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2916         "Usage: /SERVICE [<service name>] [-pubkey]");
2917     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2918     return SILC_FSM_FINISH;
2919   }
2920
2921   name = cmd->argv[1];
2922
2923   /* Send SERVICE command to the server */
2924   buffer = silc_command_payload_encode_va(SILC_COMMAND_SERVICE,
2925                                           ++conn->cmd_ident, 1,
2926                                           1, name, strlen(name));
2927   silc_client_packet_send(conn->client, conn->sock, SILC_PACKET_COMMAND,
2928                           NULL, 0, NULL, NULL, buffer->data,
2929                           buffer->len, TRUE);
2930   silc_buffer_free(buffer);
2931 #endif /* 0 */
2932
2933   /* Notify application */
2934   COMMAND(SILC_STATUS_OK);
2935
2936   /** Wait for command reply */
2937   silc_fsm_next(fsm, silc_client_command_reply_wait);
2938   return SILC_FSM_CONTINUE;
2939 }
2940
2941 /* Register all default commands provided by the client library for the
2942    application. */
2943
2944 void silc_client_commands_register(SilcClient client)
2945 {
2946   silc_list_init(client->internal->commands, struct SilcClientCommandStruct,
2947                  next);
2948
2949   SILC_CLIENT_CMD(whois, WHOIS, "WHOIS", 5);
2950   SILC_CLIENT_CMD(whowas, WHOWAS, "WHOWAS", 3);
2951   SILC_CLIENT_CMD(identify, IDENTIFY, "IDENTIFY", 3);
2952   SILC_CLIENT_CMD(nick, NICK, "NICK", 2);
2953   SILC_CLIENT_CMD(list, LIST, "LIST", 2);
2954   SILC_CLIENT_CMD(topic, TOPIC, "TOPIC", 3);
2955   SILC_CLIENT_CMD(invite, INVITE, "INVITE", 3);
2956   SILC_CLIENT_CMD(quit, QUIT, "QUIT", 2);
2957   SILC_CLIENT_CMD(kill, KILL, "KILL", 4);
2958   SILC_CLIENT_CMD(info, INFO, "INFO", 2);
2959   SILC_CLIENT_CMD(stats, STATS, "STATS", 0);
2960   SILC_CLIENT_CMD(ping, PING, "PING", 2);
2961   SILC_CLIENT_CMD(oper, OPER, "OPER", 3);
2962   SILC_CLIENT_CMD(join, JOIN, "JOIN", 9);
2963   SILC_CLIENT_CMD(motd, MOTD, "MOTD", 2);
2964   SILC_CLIENT_CMD(umode, UMODE, "UMODE", 2);
2965   SILC_CLIENT_CMD(cmode, CMODE, "CMODE", 6);
2966   SILC_CLIENT_CMD(cumode, CUMODE, "CUMODE", 9);
2967   SILC_CLIENT_CMD(kick, KICK, "KICK", 4);
2968   SILC_CLIENT_CMD(ban, BAN, "BAN", 3);
2969   SILC_CLIENT_CMD(detach, DETACH, "DETACH", 0);
2970   SILC_CLIENT_CMD(watch, WATCH, "WATCH", 3);
2971   SILC_CLIENT_CMD(silcoper, SILCOPER, "SILCOPER", 3);
2972   SILC_CLIENT_CMD(leave, LEAVE, "LEAVE", 2);
2973   SILC_CLIENT_CMD(users, USERS, "USERS", 2);
2974   SILC_CLIENT_CMD(getkey, GETKEY, "GETKEY", 2);
2975   SILC_CLIENT_CMD(service, SERVICE, "SERVICE", 10);
2976 }
2977
2978 /* Unregister all commands. */
2979
2980 void silc_client_commands_unregister(SilcClient client)
2981 {
2982   SILC_CLIENT_CMDU(whois, WHOIS, "WHOIS");
2983   SILC_CLIENT_CMDU(whowas, WHOWAS, "WHOWAS");
2984   SILC_CLIENT_CMDU(identify, IDENTIFY, "IDENTIFY");
2985   SILC_CLIENT_CMDU(nick, NICK, "NICK");
2986   SILC_CLIENT_CMDU(list, LIST, "LIST");
2987   SILC_CLIENT_CMDU(topic, TOPIC, "TOPIC");
2988   SILC_CLIENT_CMDU(invite, INVITE, "INVITE");
2989   SILC_CLIENT_CMDU(quit, QUIT, "QUIT");
2990   SILC_CLIENT_CMDU(kill, KILL, "KILL");
2991   SILC_CLIENT_CMDU(info, INFO, "INFO");
2992   SILC_CLIENT_CMDU(stats, STATS, "STATS");
2993   SILC_CLIENT_CMDU(ping, PING, "PING");
2994   SILC_CLIENT_CMDU(oper, OPER, "OPER");
2995   SILC_CLIENT_CMDU(join, JOIN, "JOIN");
2996   SILC_CLIENT_CMDU(motd, MOTD, "MOTD");
2997   SILC_CLIENT_CMDU(umode, UMODE, "UMODE");
2998   SILC_CLIENT_CMDU(cmode, CMODE, "CMODE");
2999   SILC_CLIENT_CMDU(cumode, CUMODE, "CUMODE");
3000   SILC_CLIENT_CMDU(kick, KICK, "KICK");
3001   SILC_CLIENT_CMDU(ban, BAN, "BAN");
3002   SILC_CLIENT_CMDU(detach, DETACH, "DETACH");
3003   SILC_CLIENT_CMDU(watch, WATCH, "WATCH");
3004   SILC_CLIENT_CMDU(silcoper, SILCOPER, "SILCOPER");
3005   SILC_CLIENT_CMDU(leave, LEAVE, "LEAVE");
3006   SILC_CLIENT_CMDU(users, USERS, "USERS");
3007   SILC_CLIENT_CMDU(getkey, GETKEY, "GETKEY");
3008   SILC_CLIENT_CMDU(service, SERVICE, "SERVICE");
3009 }
3010
3011 /****************** Client Side Incoming Command Handling *******************/
3012
3013 typedef struct {
3014   SilcClientConnection conn;
3015   SilcUInt16 cmd_ident;
3016 } *SilcClientProcessWhois;
3017
3018 /* Send reply to WHOIS from server */
3019
3020 static void
3021 silc_client_command_process_whois_send(SilcBool success,
3022                                        const unsigned char *data,
3023                                        SilcUInt32 data_len, void *context)
3024 {
3025   SilcClientProcessWhois w = context;
3026   SilcBufferStruct buffer;
3027   SilcBuffer packet;
3028
3029   if (!data) {
3030     silc_free(w);
3031     return;
3032   }
3033
3034   silc_buffer_set(&buffer, (unsigned char *)data, data_len);
3035
3036   /* Send the attributes back in COMMAND_REPLY packet */
3037   packet =
3038     silc_command_reply_payload_encode_va(SILC_COMMAND_WHOIS,
3039                                          SILC_STATUS_OK, 0, w->cmd_ident,
3040                                          1, 11, buffer.data,
3041                                          silc_buffer_len(&buffer));
3042   if (!packet) {
3043     silc_free(w);
3044     return;
3045   }
3046
3047   SILC_LOG_DEBUG(("Sending back requested WHOIS attributes"));
3048
3049   silc_packet_send(w->conn->stream, SILC_PACKET_COMMAND_REPLY, 0,
3050                    silc_buffer_datalen(packet));
3051
3052   silc_buffer_free(packet);
3053   silc_free(w);
3054 }
3055
3056 /* Process WHOIS command from server */
3057
3058 static void silc_client_command_process_whois(SilcClient client,
3059                                               SilcClientConnection conn,
3060                                               SilcCommandPayload payload,
3061                                               SilcArgumentPayload args)
3062 {
3063   SilcClientProcessWhois w;
3064   SilcDList attrs;
3065   unsigned char *tmp;
3066   SilcUInt32 tmp_len;
3067
3068   SILC_LOG_DEBUG(("Received WHOIS command"));
3069
3070   /* Try to take the Requested Attributes */
3071   tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
3072   if (!tmp)
3073     return;
3074
3075   attrs = silc_attribute_payload_parse(tmp, tmp_len);
3076   if (!attrs)
3077     return;
3078
3079   w = silc_calloc(1, sizeof(*w));
3080   if (!w) {
3081     silc_attribute_payload_list_free(attrs);
3082     return;
3083   }
3084   w->conn = conn;
3085   w->cmd_ident = silc_command_get_ident(payload);
3086
3087   /* Process requested attributes */
3088   silc_client_attributes_process(client, conn, attrs,
3089                                  silc_client_command_process_whois_send, w);
3090   silc_attribute_payload_list_free(attrs);
3091 }
3092
3093 /* Client is able to receive some command packets even though they are
3094    special case.  Server may send WHOIS command to the client to retrieve
3095    Requested Attributes information for WHOIS query the server is
3096    processing. This function currently handles only the WHOIS command,
3097    but if in the future more commands may arrive then this can be made
3098    to support other commands too. */
3099
3100 SILC_FSM_STATE(silc_client_command)
3101 {
3102   SilcClientConnection conn = fsm_context;
3103   SilcClient client = conn->client;
3104   SilcPacket packet = state_context;
3105   SilcCommandPayload payload;
3106   SilcCommand command;
3107   SilcArgumentPayload args;
3108
3109   /* Get command payload from packet */
3110   payload = silc_command_payload_parse(packet->buffer.data,
3111                                        silc_buffer_len(&packet->buffer));
3112   if (!payload) {
3113     SILC_LOG_DEBUG(("Bad command packet"));
3114     return SILC_FSM_FINISH;
3115   }
3116
3117   /* Get arguments */
3118   args = silc_command_get_args(payload);
3119
3120   /* Get the command */
3121   command = silc_command_get(payload);
3122   switch (command) {
3123
3124   case SILC_COMMAND_WHOIS:
3125     /* Ignore everything if requested by application */
3126     if (conn->internal->params.ignore_requested_attributes)
3127       break;
3128
3129     silc_client_command_process_whois(client, conn, payload, args);
3130     break;
3131
3132   default:
3133     break;
3134   }
3135
3136   silc_command_payload_free(payload);
3137   return SILC_FSM_FINISH;
3138 }