Merge commit 'origin/silc.1.1.branch'
[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   SilcUInt16 cmd_ident;
415   SilcClientCommand command;
416   SilcClientCommandContext cmd;
417   char *arg;
418
419   if (!conn) {
420     client->internal->ops->say(client, NULL, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
421       "You are not connected to a server, please connect to server");
422     return 0;
423   }
424
425   /* Parse arguments */
426   va_start(va, command_line);
427   if (command_line) {
428     char *command_name;
429
430     /* Get command name */
431     command_name = silc_memdup(command_line, strcspn(command_line, " "));
432     if (!command_name)
433       return 0;
434
435     /* Find command by name */
436     command = silc_client_command_find(client, command_name);
437     if (!command) {
438       silc_free(command_name);
439       return 0;
440     }
441
442     /* Parse command line */
443     silc_parse_command_line((char *)command_line, &argv, &argv_lens,
444                             &argv_types, &argc, command->max_args);
445
446     silc_free(command_name);
447   } else {
448     arg = va_arg(va, char *);
449     if (!arg)
450       return 0;
451
452     /* Find command by name */
453     command = silc_client_command_find(client, arg);
454     if (!command)
455       return 0;
456
457     while (arg) {
458       argv = silc_realloc(argv, sizeof(*argv) * (argc + 1));
459       argv_lens = silc_realloc(argv_lens, sizeof(*argv_lens) * (argc + 1));
460       argv_types = silc_realloc(argv_types, sizeof(*argv_types) * (argc + 1));
461       if (!argv || !argv_lens || !argv_types)
462         return 0;
463       argv[argc] = silc_memdup(arg, strlen(arg));
464       if (!argv[argc])
465         return 0;
466       argv_lens[argc] = strlen(arg);
467       argv_types[argc] = argc;
468       argc++;
469       arg = va_arg(va, char *);
470     }
471   }
472   va_end(va);
473
474   /* Allocate command context */
475   cmd = silc_calloc(1, sizeof(*cmd));
476   if (!cmd)
477     return 0;
478   cmd->conn = conn;
479   cmd->cmd = command->cmd;
480   cmd->argc = argc;
481   cmd->argv = argv;
482   cmd->argv_lens = argv_lens;
483   cmd->argv_types = argv_types;
484   cmd_ident = cmd->cmd_ident = silc_client_cmd_ident(conn);
485   cmd->called = TRUE;
486   cmd->verbose = TRUE;
487   silc_list_init(cmd->reply_callbacks,
488                  struct SilcClientCommandReplyCallbackStruct, next);
489
490   /*** Call command */
491   SILC_LOG_DEBUG(("Calling %s command", silc_get_command_name(cmd->cmd)));
492   silc_fsm_thread_init(&cmd->thread, &conn->internal->fsm, cmd,
493                        silc_client_command_destructor, NULL, FALSE);
494   silc_fsm_start_sync(&cmd->thread, command->command);
495
496   return cmd_ident;
497 }
498
499 /* Generic function to send any command. The arguments must be sent already
500    encoded into correct format and in correct order. */
501
502 SilcUInt16 silc_client_command_send(SilcClient client,
503                                     SilcClientConnection conn,
504                                     SilcCommand command,
505                                     SilcClientCommandReply reply,
506                                     void *reply_context,
507                                     SilcUInt32 argc, ...)
508 {
509   SilcClientCommandContext cmd;
510   va_list ap;
511
512   if (!conn || !reply)
513     return 0;
514
515   /* Allocate command context */
516   cmd = silc_calloc(1, sizeof(*cmd));
517   if (!cmd)
518     return 0;
519   cmd->conn = conn;
520   cmd->cmd = command;
521   silc_list_init(cmd->reply_callbacks,
522                  struct SilcClientCommandReplyCallbackStruct, next);
523
524   /* Send the command */
525   va_start(ap, argc);
526   cmd->cmd_ident =
527     silc_client_command_send_vap(client, conn, cmd, command, reply,
528                                  reply_context, argc, ap);
529   va_end(ap);
530
531   if (!cmd->cmd_ident) {
532     silc_client_command_free(cmd);
533     return 0;
534   }
535
536   /*** Wait for command reply */
537   silc_fsm_thread_init(&cmd->thread, &conn->internal->fsm, cmd,
538                        silc_client_command_destructor, NULL, FALSE);
539   silc_fsm_start_sync(&cmd->thread, silc_client_command_reply_wait);
540
541   return cmd->cmd_ident;
542 }
543
544 /* Generic function to send any command. The arguments must be sent already
545    encoded into correct format and in correct order.  Arguments come from
546    arrays. */
547
548 SilcUInt16 silc_client_command_send_argv(SilcClient client,
549                                          SilcClientConnection conn,
550                                          SilcCommand command,
551                                          SilcClientCommandReply reply,
552                                          void *reply_context,
553                                          SilcUInt32 argc,
554                                          unsigned char **argv,
555                                          SilcUInt32 *argv_lens,
556                                          SilcUInt32 *argv_types)
557 {
558   SilcClientCommandContext cmd;
559
560   if (!conn || !reply)
561     return 0;
562
563   /* Allocate command context */
564   cmd = silc_calloc(1, sizeof(*cmd));
565   if (!cmd)
566     return 0;
567   cmd->conn = conn;
568   cmd->cmd = command;
569
570   /* Send the command */
571   cmd->cmd_ident =
572     silc_client_command_send_arg_array(client, conn, cmd, command, reply,
573                                        reply_context, argc, argv, argv_lens,
574                                        argv_types);
575   if (!cmd->cmd_ident) {
576     silc_client_command_free(cmd);
577     return 0;
578   }
579
580   /*** Wait for command reply */
581   silc_fsm_thread_init(&cmd->thread, &conn->internal->fsm, cmd,
582                        silc_client_command_destructor, NULL, FALSE);
583   silc_fsm_start_sync(&cmd->thread, silc_client_command_reply_wait);
584
585   return cmd->cmd_ident;
586 }
587
588 /* Attach to a command and command identifier to receive command reply. */
589
590 SilcBool silc_client_command_pending(SilcClientConnection conn,
591                                      SilcCommand command,
592                                      SilcUInt16 ident,
593                                      SilcClientCommandReply reply,
594                                      void *context)
595 {
596   SilcClientCommandContext cmd;
597   SilcClientCommandReplyCallback cb;
598
599   if (!conn || !reply)
600     return FALSE;
601
602   SILC_LOG_DEBUG(("Add pending command reply for ident %d", ident));
603
604   silc_mutex_lock(conn->internal->lock);
605
606   /* Find the pending command */
607   silc_list_start(conn->internal->pending_commands);
608   while ((cmd = silc_list_get(conn->internal->pending_commands)))
609     if ((cmd->cmd == command || command == SILC_COMMAND_NONE)
610         && cmd->cmd_ident == ident) {
611
612       /* Add the callback */
613       cb = silc_calloc(1, sizeof(*cb));
614       if (!cb)
615         continue;
616       cb->reply = reply;
617       cb->context = context;
618       silc_list_add(cmd->reply_callbacks, cb);
619     }
620
621   silc_mutex_unlock(conn->internal->lock);
622
623   return TRUE;
624 }
625
626 /******************************** WHOIS *************************************/
627
628 /* Command WHOIS. This command is used to query information about
629    specific user. */
630
631 SILC_FSM_STATE(silc_client_command_whois)
632 {
633   SilcClientCommandContext cmd = fsm_context;
634   SilcClientConnection conn = cmd->conn;
635   SilcClient client = conn->client;
636   SilcBuffer attrs = NULL;
637   unsigned char count[4], *tmp = NULL;
638   SilcBool details = FALSE, nick = FALSE;
639   unsigned char *pubkey = NULL;
640   char *nickname = NULL;
641   int i;
642
643   /* Given without arguments fetches client's own information */
644   if (cmd->argc < 2) {
645     silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1, 4,
646                                 silc_buffer_data(conn->internal->local_idp),
647                                 silc_buffer_len(conn->internal->local_idp));
648
649     /* Notify application */
650     COMMAND(SILC_STATUS_OK);
651
652     /** Wait for command reply */
653     silc_fsm_next(fsm, silc_client_command_reply_wait);
654     return SILC_FSM_CONTINUE;
655   }
656
657   for (i = 1; i < cmd->argc; i++) {
658     if (!strcasecmp(cmd->argv[i], "-details")) {
659       details = TRUE;
660     } else if (!strcasecmp(cmd->argv[i], "-pubkey") && cmd->argc > i + 1) {
661       pubkey = cmd->argv[i + 1];
662       i++;
663     } else {
664       /* We assume that the first parameter is the nickname, if it isn't
665          -details or -pubkey. The last parameter should always be the count */
666       if (i == 1) {
667         nick = TRUE;
668       } else if (i == cmd->argc - 1) {
669         int c = atoi(cmd->argv[i]);
670         SILC_PUT32_MSB(c, count);
671         tmp = count;
672       }
673     }
674   }
675
676   if (details) {
677     /* If pubkey is set, add all attributes to the attrs buffer, except
678        public key */
679     if (pubkey) {
680       attrs = silc_client_attributes_request(SILC_ATTRIBUTE_USER_INFO,
681                                              SILC_ATTRIBUTE_SERVICE,
682                                              SILC_ATTRIBUTE_STATUS_MOOD,
683                                              SILC_ATTRIBUTE_STATUS_FREETEXT,
684                                              SILC_ATTRIBUTE_STATUS_MESSAGE,
685                                              SILC_ATTRIBUTE_PREFERRED_LANGUAGE,
686                                              SILC_ATTRIBUTE_PREFERRED_CONTACT,
687                                              SILC_ATTRIBUTE_TIMEZONE,
688                                              SILC_ATTRIBUTE_GEOLOCATION,
689                                              SILC_ATTRIBUTE_DEVICE_INFO,
690                                              SILC_ATTRIBUTE_USER_ICON, 0);
691     } else {
692       attrs = silc_client_attributes_request(0);
693     }
694   }
695
696   if (pubkey) {
697     SilcAttributeObjPk obj;
698     SilcPublicKey pk;
699
700     if (!silc_pkcs_load_public_key(pubkey, SILC_PKCS_ANY, &pk)) {
701       SAY(client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
702           "Could not load public key %s, check the filename",
703           pubkey);
704       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
705       goto out;
706     }
707
708     switch (silc_pkcs_get_type(pk)) {
709     case SILC_PKCS_SILC:
710       obj.type = "silc-rsa";
711       break;
712     case SILC_PKCS_SSH2:
713       obj.type = "ssh-rsa";
714       break;
715     case SILC_PKCS_X509V3:
716       obj.type = "x509v3-sign-rsa";
717       break;
718     case SILC_PKCS_OPENPGP:
719       obj.type = "pgp-sign-rsa";
720       break;
721     default:
722       goto out;
723       break;
724     }
725     obj.data = silc_pkcs_public_key_encode(NULL, pk, &obj.data_len);
726
727     attrs = silc_attribute_payload_encode(attrs,
728                                           SILC_ATTRIBUTE_USER_PUBLIC_KEY,
729                                           SILC_ATTRIBUTE_FLAG_VALID,
730                                           &obj, sizeof(obj));
731     silc_free(obj.data);
732   }
733
734   if (nick) {
735     if (!silc_client_nickname_parse(client, conn, cmd->argv[1], &nickname))
736       goto out;
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       if (!silc_client_nickname_parse(client, conn, cmd->argv[2], &nickname)) {
1055         silc_client_unref_channel(client, conn, channel);
1056         goto out;
1057       }
1058
1059       /* Find client entry */
1060       clients = silc_client_get_clients_local(client, conn, cmd->argv[2],
1061                                               FALSE);
1062       if (!clients)
1063         /* Resolve client information */
1064         SILC_FSM_CALL(silc_client_get_clients(
1065                                       client, conn, nickname, NULL,
1066                                       silc_client_command_resolve_continue,
1067                                       cmd));
1068
1069       client_entry = silc_dlist_get(clients);
1070     } else {
1071       if (cmd->argv[2][0] == '+')
1072         action[0] = 0x00;
1073       else
1074         action[0] = 0x01;
1075
1076       /* Check if it is public key file to be added to invite list */
1077       silc_pkcs_load_public_key(cmd->argv[2] + 1, SILC_PKCS_ANY, &pubkey);
1078       invite = cmd->argv[2];
1079       if (!pubkey)
1080         invite++;
1081     }
1082   }
1083
1084   if (invite) {
1085     args = silc_buffer_alloc_size(2);
1086     silc_buffer_format(args,
1087                        SILC_STR_UI_SHORT(1),
1088                        SILC_STR_END);
1089     if (pubkey) {
1090       chidp = silc_public_key_payload_encode(NULL, pubkey);
1091       args = silc_argument_payload_encode_one(args, silc_buffer_data(chidp),
1092                                               silc_buffer_len(chidp), 2);
1093       silc_buffer_free(chidp);
1094       silc_pkcs_public_key_free(pubkey);
1095     } else {
1096       args = silc_argument_payload_encode_one(args, invite, strlen(invite), 1);
1097     }
1098   }
1099
1100   /* Send the command */
1101   chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
1102   if (client_entry) {
1103     clidp = silc_id_payload_encode(&client_entry->id, SILC_ID_CLIENT);
1104     silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 4,
1105                                 1, silc_buffer_datalen(chidp),
1106                                 2, silc_buffer_datalen(clidp),
1107                                 3, args ? action : NULL, args ? 1 : 0,
1108                                 4, silc_buffer_datalen(args));
1109     silc_buffer_free(clidp);
1110   } else {
1111     silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 3,
1112                                 1, silc_buffer_datalen(chidp),
1113                                 3, args ? action : NULL, args ? 1 : 0,
1114                                 4, silc_buffer_datalen(args));
1115   }
1116
1117   silc_buffer_free(chidp);
1118   silc_buffer_free(args);
1119   silc_free(nickname);
1120   silc_client_list_free(client, conn, clients);
1121   silc_client_unref_channel(client, conn, channel);
1122
1123   /* Notify application */
1124   COMMAND(SILC_STATUS_OK);
1125
1126   /** Wait for command reply */
1127   silc_fsm_next(fsm, silc_client_command_reply_wait);
1128   return SILC_FSM_CONTINUE;
1129
1130  out:
1131   silc_free(nickname);
1132   return SILC_FSM_FINISH;
1133 }
1134
1135 /********************************** QUIT ************************************/
1136
1137 /* Close the connection */
1138
1139 SILC_FSM_STATE(silc_client_command_quit_final)
1140 {
1141   SilcClientCommandContext cmd = fsm_context;
1142   SilcClientConnection conn = cmd->conn;
1143
1144   SILC_LOG_DEBUG(("Quitting"));
1145
1146   /* Notify application */
1147   COMMAND(SILC_STATUS_OK);
1148
1149   /* Signal to close connection */
1150   conn->internal->status = SILC_CLIENT_CONN_DISCONNECTED;
1151   if (!conn->internal->disconnected) {
1152     conn->internal->disconnected = TRUE;
1153     SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
1154   }
1155
1156   return SILC_FSM_FINISH;
1157 }
1158
1159 /* Command QUIT. Closes connection with current server. */
1160
1161 SILC_FSM_STATE(silc_client_command_quit)
1162 {
1163   SilcClientCommandContext cmd = fsm_context;
1164   SilcClientConnection conn = cmd->conn;
1165
1166   if (cmd->argc > 1)
1167     silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1168                                 1, cmd->argv[1], cmd->argv_lens[1]);
1169   else
1170     silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 0);
1171
1172   /* Sleep for a while */
1173   sleep(1);
1174
1175   /* We close the connection with a little timeout */
1176   silc_fsm_next_later(fsm, silc_client_command_quit_final, 2, 0);
1177   return SILC_FSM_WAIT;
1178 }
1179
1180 /********************************** KILL ************************************/
1181
1182 /* Signature callback */
1183
1184 static void silc_client_command_kill_signed(const SilcBuffer buffer,
1185                                             void *context)
1186 {
1187   SilcClientCommandContext cmd = context;
1188
1189   if (!buffer) {
1190     silc_fsm_finish(&cmd->thread);
1191     return;
1192   }
1193
1194   silc_fsm_set_state_context(&cmd->thread, buffer);
1195   SILC_FSM_CALL_CONTINUE_SYNC(&cmd->thread);
1196 }
1197
1198 /* Send KILL command */
1199
1200 SILC_FSM_STATE(silc_client_command_kill_send)
1201 {
1202   SilcClientCommandContext cmd = fsm_context;
1203   SilcClientConnection conn = cmd->conn;
1204   SilcClient client = conn->client;
1205   SilcBuffer idp, auth = state_context;
1206   SilcClientEntry target = cmd->context;
1207   char *comment = NULL;
1208
1209   if (cmd->argc >= 3)
1210     if (strcasecmp(cmd->argv[2], "-pubkey"))
1211       comment = cmd->argv[2];
1212
1213   /* Send the KILL command to the server */
1214   idp = silc_id_payload_encode(&target->id, SILC_ID_CLIENT);
1215   silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 3,
1216                               1, silc_buffer_datalen(idp),
1217                               2, comment, comment ? strlen(comment) : 0,
1218                               3, silc_buffer_datalen(auth));
1219
1220   silc_buffer_free(idp);
1221   silc_client_unref_client(client, conn, target);
1222
1223   /* Notify application */
1224   COMMAND(SILC_STATUS_OK);
1225
1226   /** Wait for command reply */
1227   silc_fsm_next(fsm, silc_client_command_reply_wait);
1228   return SILC_FSM_CONTINUE;
1229 }
1230
1231 /* Command KILL. Router operator can use this command to remove an client
1232    fromthe SILC Network. */
1233
1234 SILC_FSM_STATE(silc_client_command_kill)
1235 {
1236   SilcClientCommandContext cmd = fsm_context;
1237   SilcClientConnection conn = cmd->conn;
1238   SilcClient client = conn->client;
1239   SilcClientEntry target;
1240   SilcDList clients;
1241   char *nickname = NULL;
1242
1243   if (cmd->argc < 2) {
1244     SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1245         "Usage: /KILL <nickname> [<comment>] [-pubkey]");
1246     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1247     return SILC_FSM_FINISH;
1248   }
1249
1250   /* Parse the typed nickname. */
1251   if (!silc_client_nickname_parse(client, conn, cmd->argv[1], &nickname))
1252     return SILC_FSM_FINISH;
1253
1254   /* Get the target client */
1255   clients = silc_client_get_clients_local(client, conn, cmd->argv[1], FALSE);
1256   if (!clients)
1257     /* Resolve client information */
1258     SILC_FSM_CALL(silc_client_get_clients(client, conn, nickname, NULL,
1259                                           silc_client_command_resolve_continue,
1260                                           cmd));
1261
1262   target = silc_dlist_get(clients);
1263   cmd->context = silc_client_ref_client(client, conn, target);
1264
1265   silc_free(nickname);
1266   silc_client_list_free(client, conn, clients);
1267
1268   /** Send KILL */
1269   silc_fsm_next(fsm, silc_client_command_kill_send);
1270
1271   if (cmd->argc >= 3) {
1272     if (!strcasecmp(cmd->argv[2], "-pubkey") ||
1273         (cmd->argc >= 4 && !strcasecmp(cmd->argv[3], "-pubkey"))) {
1274       /* Encode the public key authentication payload */
1275       SILC_FSM_CALL(silc_auth_public_key_auth_generate(
1276                                          conn->public_key,
1277                                          conn->private_key,
1278                                          conn->client->rng,
1279                                          conn->internal->sha1hash,
1280                                          &target->id, SILC_ID_CLIENT,
1281                                          silc_client_command_kill_signed,
1282                                          cmd));
1283       /* NOT REACHED */
1284     }
1285   }
1286
1287   return SILC_FSM_CONTINUE;
1288 }
1289
1290 /********************************** INFO ************************************/
1291
1292 /* Command INFO. Request information about specific server. If specific
1293    server is not provided the current server is used. */
1294
1295 SILC_FSM_STATE(silc_client_command_info)
1296 {
1297   SilcClientCommandContext cmd = fsm_context;
1298   SilcClientConnection conn = cmd->conn;
1299
1300   /* Send the command */
1301   if (cmd->argc == 2)
1302     silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1303                                 1, cmd->argv[1], cmd->argv_lens[1]);
1304   else
1305     silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 0);
1306
1307   /* Notify application */
1308   COMMAND(SILC_STATUS_OK);
1309
1310   /** Wait for command reply */
1311   silc_fsm_next(fsm, silc_client_command_reply_wait);
1312   return SILC_FSM_CONTINUE;
1313 }
1314
1315 /********************************** STATS ***********************************/
1316
1317 /* Command STATS. Shows server and network statistics. */
1318
1319 SILC_FSM_STATE(silc_client_command_stats)
1320 {
1321   SilcClientCommandContext cmd = fsm_context;
1322   SilcClientConnection conn = cmd->conn;
1323
1324   /* Send the command */
1325   silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1326                               1, silc_buffer_datalen(conn->internal->
1327                                                      remote_idp));
1328
1329   /* Notify application */
1330   COMMAND(SILC_STATUS_OK);
1331
1332   /** Wait for command reply */
1333   silc_fsm_next(fsm, silc_client_command_reply_wait);
1334   return SILC_FSM_CONTINUE;
1335 }
1336
1337 /********************************** PING ************************************/
1338
1339 /* Command PING. Sends ping to server. */
1340
1341 SILC_FSM_STATE(silc_client_command_ping)
1342 {
1343   SilcClientCommandContext cmd = fsm_context;
1344   SilcClientConnection conn = cmd->conn;
1345
1346   if (cmd->argc < 2) {
1347     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1348     return SILC_FSM_FINISH;
1349   }
1350
1351   /* Send the command */
1352   silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1353                               1, silc_buffer_datalen(conn->internal->
1354                                                      remote_idp));
1355
1356   /* Save ping time */
1357   cmd->context = SILC_64_TO_PTR(silc_time());
1358
1359   /* Notify application */
1360   COMMAND(SILC_STATUS_OK);
1361
1362   /** Wait for command reply */
1363   silc_fsm_next(fsm, silc_client_command_reply_wait);
1364   return SILC_FSM_CONTINUE;
1365 }
1366
1367 /********************************** JOIN ************************************/
1368
1369 typedef struct {
1370   int type;
1371   SilcBuffer auth;
1372   SilcBuffer cauth;
1373 } *SilcClientJoinContext;
1374
1375 /* Signature callback */
1376
1377 static void silc_client_command_join_signed(const SilcBuffer buffer,
1378                                             void *context)
1379 {
1380   SilcClientCommandContext cmd = context;
1381   SilcClientJoinContext j = cmd->context;
1382
1383   if (!buffer) {
1384     silc_fsm_finish(&cmd->thread);
1385     return;
1386   }
1387
1388   if (!j->type)
1389     j->auth = silc_buffer_copy(buffer);
1390   else
1391     j->cauth = silc_buffer_copy(buffer);
1392
1393   SILC_FSM_CALL_CONTINUE(&cmd->thread);
1394 }
1395
1396 /* Command JOIN. Joins to a channel. */
1397
1398 SILC_FSM_STATE(silc_client_command_join)
1399 {
1400   SilcClientCommandContext cmd2, cmd = fsm_context;
1401   SilcClientConnection conn = cmd->conn;
1402   SilcClient client = conn->client;
1403   SilcChannelEntry channel = NULL;
1404   SilcClientJoinContext j = cmd->context;
1405   SilcBuffer auth = NULL, cauth = NULL;
1406   char *name, *passphrase = NULL, *pu8, *cipher = NULL, *hmac = NULL;
1407   int i, passphrase_len = 0;
1408
1409   if (cmd->argc < 2) {
1410     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1411     goto out;
1412   }
1413
1414   /* See if we have joined to the requested channel already */
1415   channel = silc_client_get_channel(conn->client, conn, cmd->argv[1]);
1416   if (channel && silc_client_on_channel(channel, conn->local_entry))
1417     goto out;
1418
1419   /* If NICK command is active, wait for it to finish before sending JOIN.
1420      To avoid problems locally with changing IDs while joining, we do this. */
1421   silc_mutex_lock(conn->internal->lock);
1422   silc_list_start(conn->internal->pending_commands);
1423   while ((cmd2 = silc_list_get(conn->internal->pending_commands))) {
1424     if (cmd2->cmd == SILC_COMMAND_NICK) {
1425       silc_mutex_unlock(conn->internal->lock);
1426       silc_fsm_next_later(fsm, silc_client_command_join, 0, 300000);
1427       return SILC_FSM_WAIT;
1428     }
1429   }
1430   silc_mutex_unlock(conn->internal->lock);
1431
1432   if (cmd->argv_lens[1] > 256)
1433     cmd->argv_lens[1] = 256;
1434
1435   name = cmd->argv[1];
1436
1437   for (i = 2; i < cmd->argc; i++) {
1438     if (!strcasecmp(cmd->argv[i], "-cipher") && cmd->argc > i + 1) {
1439       cipher = cmd->argv[++i];
1440     } else if (!strcasecmp(cmd->argv[i], "-hmac") && cmd->argc > i + 1) {
1441       hmac = cmd->argv[++i];
1442     } else if (!strcasecmp(cmd->argv[i], "-founder")) {
1443       if (!j || !j->auth) {
1444         if (!j) {
1445           j = silc_calloc(1, sizeof(*j));
1446           if (!j)
1447             goto out;
1448           cmd->context = j;
1449         }
1450         j->type = 0;
1451         silc_free(passphrase);
1452         silc_client_unref_channel(client, conn, channel);
1453         SILC_FSM_CALL(silc_auth_public_key_auth_generate(
1454                                            conn->public_key,
1455                                            conn->private_key,
1456                                            conn->client->rng,
1457                                            conn->internal->sha1hash,
1458                                            conn->local_id,
1459                                            SILC_ID_CLIENT,
1460                                            silc_client_command_join_signed,
1461                                            cmd));
1462         /* NOT REACHED */
1463       }
1464     } else if (!strcasecmp(cmd->argv[i], "-auth")) {
1465       SilcPublicKey pubkey = conn->public_key;
1466       SilcPrivateKey privkey = conn->private_key;
1467       unsigned char *pk, pkhash[SILC_HASH_MAXLEN], pubdata[128];
1468       SilcUInt32 pk_len;
1469
1470       if (!j || !j->cauth) {
1471         if (!j) {
1472           j = silc_calloc(1, sizeof(*j));
1473           if (!j)
1474             goto out;
1475           cmd->context = j;
1476         }
1477
1478         if (cmd->argc >= i + 3) {
1479           char *pass = "";
1480           if (cmd->argc >= i + 4) {
1481             pass = cmd->argv[i + 3];
1482             i++;
1483           }
1484           if (!silc_load_key_pair(cmd->argv[i + 1], cmd->argv[i + 2], pass,
1485                                   &pubkey, &privkey)) {
1486             SAY(conn->client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
1487                 "Could not load key pair, check your arguments");
1488             COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1489             goto out;
1490           }
1491           i += 2;
1492         }
1493
1494         j->type = 1;
1495         pk = silc_pkcs_public_key_encode(NULL, pubkey, &pk_len);
1496         silc_hash_make(conn->internal->sha1hash, pk, pk_len, pkhash);
1497         silc_free(pk);
1498         silc_rng_get_rn_data(conn->client->rng, sizeof(pubdata), pubdata,
1499                              sizeof(pubdata));
1500         memcpy(pubdata, pkhash, 20);
1501         silc_free(passphrase);
1502         silc_client_unref_channel(client, conn, channel);
1503         SILC_FSM_CALL(silc_auth_public_key_auth_generate_wpub(
1504                                            pubkey, privkey,
1505                                            pubdata, sizeof(pubdata),
1506                                            conn->internal->sha1hash,
1507                                            client->rng,
1508                                            conn->local_id,
1509                                            SILC_ID_CLIENT,
1510                                            silc_client_command_join_signed,
1511                                            cmd));
1512         /* NOT REACHED */
1513       }
1514     } else {
1515       /* Passphrases must be UTF-8 encoded, so encode if it is not */
1516       if (!silc_utf8_valid(cmd->argv[i], cmd->argv_lens[i])) {
1517         passphrase_len = silc_utf8_encoded_len(cmd->argv[i],
1518                                                cmd->argv_lens[i], 0);
1519         pu8 = silc_calloc(passphrase_len, sizeof(*pu8));
1520         passphrase_len = silc_utf8_encode(cmd->argv[i], cmd->argv_lens[i],
1521                                           0, pu8, passphrase_len);
1522         passphrase = pu8;
1523       } else {
1524         passphrase = strdup(cmd->argv[i]);
1525         passphrase_len = cmd->argv_lens[i];
1526       }
1527     }
1528   }
1529
1530   /* Send JOIN command to the server */
1531   silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 7,
1532                               1, name, strlen(name),
1533                               2, silc_buffer_datalen(conn->internal->
1534                                                      local_idp),
1535                               3, passphrase, passphrase_len,
1536                               4, cipher, cipher ? strlen(cipher) : 0,
1537                               5, hmac, hmac ? strlen(hmac) : 0,
1538                               6, silc_buffer_datalen(auth),
1539                               7, silc_buffer_datalen(cauth));
1540
1541   if (passphrase)
1542     memset(passphrase, 0, strlen(passphrase));
1543   silc_free(passphrase);
1544   silc_client_unref_channel(client, conn, channel);
1545   if (j) {
1546     silc_buffer_free(j->auth);
1547     silc_buffer_free(j->cauth);
1548     silc_free(j);
1549   }
1550
1551   /* Notify application */
1552   COMMAND(SILC_STATUS_OK);
1553
1554   /** Wait for command reply */
1555   silc_fsm_next(fsm, silc_client_command_reply_wait);
1556   return SILC_FSM_CONTINUE;
1557
1558  out:
1559   silc_client_unref_channel(client, conn, channel);
1560   return SILC_FSM_FINISH;
1561 }
1562
1563 /********************************** MOTD ************************************/
1564
1565 /* MOTD command. Requests motd from server. */
1566
1567 SILC_FSM_STATE(silc_client_command_motd)
1568 {
1569   SilcClientCommandContext cmd = fsm_context;
1570   SilcClientConnection conn = cmd->conn;
1571
1572   if (cmd->argc < 1 || cmd->argc > 2) {
1573     SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1574         "Usage: /MOTD [<server>]");
1575     COMMAND_ERROR((cmd->argc < 1 ? SILC_STATUS_ERR_NOT_ENOUGH_PARAMS :
1576                    SILC_STATUS_ERR_TOO_MANY_PARAMS));
1577     return SILC_FSM_FINISH;
1578   }
1579
1580   /* Send the command */
1581   if (cmd->argc == 1)
1582     silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1583                                 1, conn->remote_host,
1584                                 strlen(conn->remote_host));
1585   else
1586     silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1587                                 1, cmd->argv[1], cmd->argv_lens[1]);
1588
1589   /* Notify application */
1590   COMMAND(SILC_STATUS_OK);
1591
1592   /** Wait for command reply */
1593   silc_fsm_next(fsm, silc_client_command_reply_wait);
1594   return SILC_FSM_CONTINUE;
1595 }
1596
1597 /********************************** UMODE ***********************************/
1598
1599 /* UMODE. Set/unset user mode in SILC. This is used mainly to unset the
1600    modes as client cannot set itself server/router operator privileges. */
1601
1602 SILC_FSM_STATE(silc_client_command_umode)
1603 {
1604   SilcClientCommandContext cmd = fsm_context;
1605   SilcClientConnection conn = cmd->conn;
1606   unsigned char *cp, modebuf[4];
1607   SilcUInt32 mode, add, len;
1608   int i;
1609
1610   if (cmd->argc < 2) {
1611     SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1612         "Usage: /UMODE +|-<modes>");
1613     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1614     return SILC_FSM_FINISH;
1615   }
1616
1617   mode = conn->local_entry->mode;
1618
1619   /* Are we adding or removing mode */
1620   if (cmd->argv[1][0] == '-')
1621     add = FALSE;
1622   else
1623     add = TRUE;
1624
1625   /* Parse mode */
1626   cp = cmd->argv[1] + 1;
1627   len = strlen(cp);
1628   for (i = 0; i < len; i++) {
1629     switch(cp[i]) {
1630     case 'a':
1631       if (add) {
1632         mode = 0;
1633         mode |= SILC_UMODE_SERVER_OPERATOR;
1634         mode |= SILC_UMODE_ROUTER_OPERATOR;
1635         mode |= SILC_UMODE_GONE;
1636         mode |= SILC_UMODE_INDISPOSED;
1637         mode |= SILC_UMODE_BUSY;
1638         mode |= SILC_UMODE_PAGE;
1639         mode |= SILC_UMODE_HYPER;
1640         mode |= SILC_UMODE_ROBOT;
1641         mode |= SILC_UMODE_BLOCK_PRIVMSG;
1642         mode |= SILC_UMODE_REJECT_WATCHING;
1643       } else {
1644         mode = SILC_UMODE_NONE;
1645       }
1646       break;
1647     case 's':
1648       if (add)
1649         mode |= SILC_UMODE_SERVER_OPERATOR;
1650       else
1651         mode &= ~SILC_UMODE_SERVER_OPERATOR;
1652       break;
1653     case 'r':
1654       if (add)
1655         mode |= SILC_UMODE_ROUTER_OPERATOR;
1656       else
1657         mode &= ~SILC_UMODE_ROUTER_OPERATOR;
1658       break;
1659     case 'g':
1660       if (add)
1661         mode |= SILC_UMODE_GONE;
1662       else
1663         mode &= ~SILC_UMODE_GONE;
1664       break;
1665     case 'i':
1666       if (add)
1667         mode |= SILC_UMODE_INDISPOSED;
1668       else
1669         mode &= ~SILC_UMODE_INDISPOSED;
1670       break;
1671     case 'b':
1672       if (add)
1673         mode |= SILC_UMODE_BUSY;
1674       else
1675         mode &= ~SILC_UMODE_BUSY;
1676       break;
1677     case 'p':
1678       if (add)
1679         mode |= SILC_UMODE_PAGE;
1680       else
1681         mode &= ~SILC_UMODE_PAGE;
1682       break;
1683     case 'h':
1684       if (add)
1685         mode |= SILC_UMODE_HYPER;
1686       else
1687         mode &= ~SILC_UMODE_HYPER;
1688       break;
1689     case 't':
1690       if (add)
1691         mode |= SILC_UMODE_ROBOT;
1692       else
1693         mode &= ~SILC_UMODE_ROBOT;
1694       break;
1695     case 'P':
1696       if (add)
1697         mode |= SILC_UMODE_BLOCK_PRIVMSG;
1698       else
1699         mode &= ~SILC_UMODE_BLOCK_PRIVMSG;
1700       break;
1701     case 'w':
1702       if (add)
1703         mode |= SILC_UMODE_REJECT_WATCHING;
1704       else
1705         mode &= ~SILC_UMODE_REJECT_WATCHING;
1706       break;
1707     case 'I':
1708       if (add)
1709         mode |= SILC_UMODE_BLOCK_INVITE;
1710       else
1711         mode &= ~SILC_UMODE_BLOCK_INVITE;
1712       break;
1713     default:
1714       COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE);
1715       return SILC_FSM_FINISH;
1716       break;
1717     }
1718   }
1719
1720   SILC_PUT32_MSB(mode, modebuf);
1721
1722   /* Send the command */
1723   silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
1724                               1, silc_buffer_datalen(conn->internal->
1725                                                      local_idp),
1726                               2, modebuf, sizeof(modebuf));
1727
1728   /* Notify application */
1729   COMMAND(SILC_STATUS_OK);
1730
1731   /** Wait for command reply */
1732   silc_fsm_next(fsm, silc_client_command_reply_wait);
1733   return SILC_FSM_CONTINUE;
1734 }
1735
1736 /********************************** CMODE ***********************************/
1737
1738 /* Signature callback */
1739
1740 static void silc_client_command_cmode_signed(const SilcBuffer buffer,
1741                                              void *context)
1742 {
1743   SilcClientCommandContext cmd = context;
1744
1745   if (!buffer) {
1746     silc_fsm_finish(&cmd->thread);
1747     return;
1748   }
1749
1750   silc_fsm_set_state_context(&cmd->thread, buffer);
1751   SILC_FSM_CALL_CONTINUE_SYNC(&cmd->thread);
1752 }
1753
1754 /* CMODE command. Sets channel mode. Modes that does not require any arguments
1755    can be set several at once. Those modes that require argument must be set
1756    separately (unless set with modes that does not require arguments). */
1757
1758 SILC_FSM_STATE(silc_client_command_cmode)
1759 {
1760   SilcClientCommandContext cmd = fsm_context;
1761   SilcClientConnection conn = cmd->conn;
1762   SilcClient client = conn->client;
1763   SilcBuffer auth = state_context;
1764   SilcChannelEntry channel = NULL;
1765   SilcBuffer chidp, pk = NULL;
1766   unsigned char *name, *cp, modebuf[4], tmp[4], *arg = NULL;
1767   SilcUInt32 mode, add, type, len, arg_len = 0;
1768   int i;
1769
1770   if (cmd->argc < 3) {
1771     SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1772         "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1773     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1774     goto out;
1775   }
1776
1777   if (cmd->argv[1][0] == '*') {
1778     if (!conn->current_channel) {
1779       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1780       goto out;
1781     }
1782
1783     channel = conn->current_channel;
1784     silc_client_ref_channel(client, conn, channel);
1785   } else {
1786     name = cmd->argv[1];
1787
1788     channel = silc_client_get_channel(conn->client, conn, name);
1789     if (!channel) {
1790       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1791       goto out;
1792     }
1793   }
1794
1795   mode = channel->mode;
1796
1797   /* Are we adding or removing mode */
1798   if (cmd->argv[2][0] == '-')
1799     add = FALSE;
1800   else
1801     add = TRUE;
1802
1803   /* Argument type to be sent to server */
1804   type = 0;
1805
1806   /* Parse mode */
1807   cp = cmd->argv[2] + 1;
1808   len = strlen(cp);
1809   for (i = 0; i < len; i++) {
1810     switch(cp[i]) {
1811     case 'p':
1812       if (add)
1813         mode |= SILC_CHANNEL_MODE_PRIVATE;
1814       else
1815         mode &= ~SILC_CHANNEL_MODE_PRIVATE;
1816       break;
1817     case 's':
1818       if (add)
1819         mode |= SILC_CHANNEL_MODE_SECRET;
1820       else
1821         mode &= ~SILC_CHANNEL_MODE_SECRET;
1822       break;
1823     case 'k':
1824       if (add)
1825         mode |= SILC_CHANNEL_MODE_PRIVKEY;
1826       else
1827         mode &= ~SILC_CHANNEL_MODE_PRIVKEY;
1828       break;
1829     case 'i':
1830       if (add)
1831         mode |= SILC_CHANNEL_MODE_INVITE;
1832       else
1833         mode &= ~SILC_CHANNEL_MODE_INVITE;
1834       break;
1835     case 't':
1836       if (add)
1837         mode |= SILC_CHANNEL_MODE_TOPIC;
1838       else
1839         mode &= ~SILC_CHANNEL_MODE_TOPIC;
1840       break;
1841     case 'm':
1842       if (add)
1843         mode |= SILC_CHANNEL_MODE_SILENCE_USERS;
1844       else
1845         mode &= ~SILC_CHANNEL_MODE_SILENCE_USERS;
1846       break;
1847     case 'M':
1848       if (add)
1849         mode |= SILC_CHANNEL_MODE_SILENCE_OPERS;
1850       else
1851         mode &= ~SILC_CHANNEL_MODE_SILENCE_OPERS;
1852       break;
1853     case 'l':
1854       if (add) {
1855         int ll;
1856         mode |= SILC_CHANNEL_MODE_ULIMIT;
1857         type = 3;
1858         if (cmd->argc < 4) {
1859           SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1860               "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1861           COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1862           goto out;
1863         }
1864         ll = atoi(cmd->argv[3]);
1865         SILC_PUT32_MSB(ll, tmp);
1866         arg = tmp;
1867         arg_len = 4;
1868       } else {
1869         mode &= ~SILC_CHANNEL_MODE_ULIMIT;
1870       }
1871       break;
1872     case 'a':
1873       if (add) {
1874         mode |= SILC_CHANNEL_MODE_PASSPHRASE;
1875         type = 4;
1876         if (cmd->argc < 4) {
1877           SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1878               "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1879           COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1880           goto out;
1881         }
1882         arg = cmd->argv[3];
1883         arg_len = cmd->argv_lens[3];
1884       } else {
1885         mode &= ~SILC_CHANNEL_MODE_PASSPHRASE;
1886       }
1887       break;
1888     case 'c':
1889       if (add) {
1890         mode |= SILC_CHANNEL_MODE_CIPHER;
1891         type = 5;
1892         if (cmd->argc < 4) {
1893           SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1894               "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1895           COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1896           goto out;
1897         }
1898         arg = cmd->argv[3];
1899         arg_len = cmd->argv_lens[3];
1900       } else {
1901         mode &= ~SILC_CHANNEL_MODE_CIPHER;
1902       }
1903       break;
1904     case 'h':
1905       if (add) {
1906         mode |= SILC_CHANNEL_MODE_HMAC;
1907         type = 6;
1908         if (cmd->argc < 4) {
1909           SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1910               "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1911           COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1912           goto out;
1913         }
1914         arg = cmd->argv[3];
1915         arg_len = cmd->argv_lens[3];
1916       } else {
1917         mode &= ~SILC_CHANNEL_MODE_HMAC;
1918       }
1919       break;
1920     case 'f':
1921       if (add) {
1922         if (!auth) {
1923           SilcPublicKey pubkey = conn->public_key;
1924           SilcPrivateKey privkey = conn->private_key;
1925
1926           mode |= SILC_CHANNEL_MODE_FOUNDER_AUTH;
1927           type = 7;
1928
1929           if (cmd->argc >= 5) {
1930             char *pass = "";
1931             if (cmd->argc >= 6)
1932               pass = cmd->argv[5];
1933             if (!silc_load_key_pair(cmd->argv[3], cmd->argv[4], pass,
1934                                     &pubkey, &privkey)) {
1935               SAY(client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
1936                   "Could not load key pair, check your arguments");
1937               COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1938               goto out;
1939             }
1940           }
1941
1942           pk = silc_public_key_payload_encode(NULL, pubkey);
1943           silc_client_unref_channel(client, conn, channel);
1944           SILC_FSM_CALL(silc_auth_public_key_auth_generate(
1945                                              pubkey, privkey,
1946                                              conn->client->rng,
1947                                              conn->internal->sha1hash,
1948                                              conn->local_id,
1949                                              SILC_ID_CLIENT,
1950                                              silc_client_command_cmode_signed,
1951                                              cmd));
1952           /* NOT REACHED */
1953         }
1954         arg = silc_buffer_data(auth);
1955         arg_len = silc_buffer_len(auth);
1956       } else {
1957         mode &= ~SILC_CHANNEL_MODE_FOUNDER_AUTH;
1958       }
1959       break;
1960     case 'C':
1961       if (add) {
1962         int k;
1963         SilcBool chadd = FALSE;
1964         SilcPublicKey chpk = NULL;
1965
1966         mode |= SILC_CHANNEL_MODE_CHANNEL_AUTH;
1967         type = 9;
1968
1969         if (cmd->argc == 3) {
1970           /* Send empty command to receive the public key list. */
1971           chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
1972           silc_client_command_send_va(conn, cmd, SILC_COMMAND_CMODE,
1973                                       NULL, NULL, 1,
1974                                       1, silc_buffer_datalen(chidp));
1975           silc_buffer_free(chidp);
1976           silc_client_unref_channel(client, conn, channel);
1977
1978           /* Notify application */
1979           COMMAND(SILC_STATUS_OK);
1980
1981           /** Wait for command reply */
1982           silc_fsm_next(fsm, silc_client_command_reply_wait);
1983           return SILC_FSM_CONTINUE;
1984         }
1985
1986         if (cmd->argc >= 4) {
1987           auth = silc_buffer_alloc_size(2);
1988           silc_buffer_format(auth,
1989                              SILC_STR_UI_SHORT(cmd->argc - 3),
1990                              SILC_STR_END);
1991         }
1992
1993         for (k = 3; k < cmd->argc; k++) {
1994           if (cmd->argv[k][0] == '+')
1995             chadd = TRUE;
1996           if (!silc_pkcs_load_public_key(cmd->argv[k] + 1, SILC_PKCS_ANY,
1997                                          &chpk)) {
1998             SAY(conn->client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
1999                 "Could not load public key %s, check the filename",
2000                 cmd->argv[k]);
2001             COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2002             silc_buffer_free(auth);
2003             goto out;
2004           }
2005
2006           if (chpk) {
2007             pk = silc_public_key_payload_encode(NULL, chpk);
2008             auth = silc_argument_payload_encode_one(auth,
2009                                                     silc_buffer_datalen(pk),
2010                                                     chadd ? 0x00 : 0x01);
2011             silc_pkcs_public_key_free(chpk);
2012             silc_buffer_free(pk);
2013             pk = NULL;
2014           }
2015         }
2016
2017         arg = silc_buffer_data(auth);
2018         arg_len = silc_buffer_len(auth);
2019       } else {
2020         mode &= ~SILC_CHANNEL_MODE_CHANNEL_AUTH;
2021       }
2022       break;
2023     default:
2024       COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE);
2025       goto out;
2026       break;
2027     }
2028   }
2029
2030   chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2031   SILC_PUT32_MSB(mode, modebuf);
2032
2033   /* Send the command. We support sending only one mode at once that
2034      requires an argument. */
2035   if (type && arg) {
2036     silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 4,
2037                                 1, silc_buffer_datalen(chidp),
2038                                 2, modebuf, sizeof(modebuf),
2039                                 type, arg, arg_len,
2040                                 8, silc_buffer_datalen(pk));
2041   } else {
2042     silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
2043                                 1, silc_buffer_datalen(chidp),
2044                                 2, modebuf, sizeof(modebuf));
2045   }
2046
2047   silc_buffer_free(chidp);
2048   silc_buffer_free(auth);
2049   silc_buffer_free(pk);
2050   silc_client_unref_channel(client, conn, channel);
2051
2052   /* Notify application */
2053   COMMAND(SILC_STATUS_OK);
2054
2055   /** Wait for command reply */
2056   silc_fsm_next(fsm, silc_client_command_reply_wait);
2057   return SILC_FSM_CONTINUE;
2058
2059  out:
2060   silc_client_unref_channel(client, conn, channel);
2061   return SILC_FSM_FINISH;
2062 }
2063
2064 /********************************* CUMODE ***********************************/
2065
2066 /* Signature callback */
2067
2068 static void silc_client_command_cumode_signed(const SilcBuffer buffer,
2069                                               void *context)
2070 {
2071   SilcClientCommandContext cmd = context;
2072
2073   if (!buffer) {
2074     silc_fsm_finish(&cmd->thread);
2075     return;
2076   }
2077
2078   silc_fsm_set_state_context(&cmd->thread, buffer);
2079   SILC_FSM_CALL_CONTINUE_SYNC(&cmd->thread);
2080 }
2081
2082 /* CUMODE command. Changes client's mode on a channel. */
2083
2084 SILC_FSM_STATE(silc_client_command_cumode)
2085 {
2086   SilcClientCommandContext cmd = fsm_context;
2087   SilcClientConnection conn = cmd->conn;
2088   SilcClient client = conn->client;
2089   SilcBuffer auth = state_context;
2090   SilcChannelEntry channel = NULL;
2091   SilcChannelUser chu;
2092   SilcClientEntry client_entry;
2093   SilcBuffer clidp, chidp;
2094   SilcDList clients = NULL;
2095   unsigned char *name, *cp, modebuf[4];
2096   SilcUInt32 mode = 0, add, len;
2097   char *nickname = NULL;
2098   int i;
2099
2100   if (cmd->argc < 4) {
2101     SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2102         "Usage: /CUMODE <channel> +|-<modes> <nickname>[@<server>]");
2103     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2104     goto out;
2105   }
2106
2107   if (cmd->argv[1][0] == '*') {
2108     if (!conn->current_channel) {
2109       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2110       goto out;
2111     }
2112
2113     channel = conn->current_channel;
2114     silc_client_ref_channel(client, conn, channel);
2115   } else {
2116     name = cmd->argv[1];
2117
2118     channel = silc_client_get_channel(conn->client, conn, name);
2119     if (!channel) {
2120       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2121       goto out;
2122     }
2123   }
2124
2125   /* Parse the typed nickname. */
2126   if (!silc_client_nickname_parse(client, conn, cmd->argv[3], &nickname))
2127     goto out;
2128
2129   /* Find client entry */
2130   clients = silc_client_get_clients_local(client, conn, cmd->argv[3], FALSE);
2131   if (!clients)
2132     /* Resolve client information */
2133     SILC_FSM_CALL(silc_client_get_clients(client, conn, nickname, NULL,
2134                                           silc_client_command_resolve_continue,
2135                                           cmd));
2136
2137   client_entry = silc_dlist_get(clients);
2138
2139   /* Get the current mode */
2140   chu = silc_client_on_channel(channel, client_entry);
2141   if (chu)
2142     mode = chu->mode;
2143
2144   /* Are we adding or removing mode */
2145   if (cmd->argv[2][0] == '-')
2146     add = FALSE;
2147   else
2148     add = TRUE;
2149
2150   /* Parse mode */
2151   cp = cmd->argv[2] + 1;
2152   len = strlen(cp);
2153   for (i = 0; i < len; i++) {
2154     switch(cp[i]) {
2155     case 'a':
2156       if (add) {
2157         mode |= SILC_CHANNEL_UMODE_CHANFO;
2158         mode |= SILC_CHANNEL_UMODE_CHANOP;
2159         mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
2160         mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
2161         mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
2162       } else {
2163         mode = SILC_CHANNEL_UMODE_NONE;
2164       }
2165       break;
2166     case 'f':
2167       if (add) {
2168         if (!auth) {
2169           SilcPublicKey pubkey = conn->public_key;
2170           SilcPrivateKey privkey = conn->private_key;
2171
2172           if (cmd->argc >= 6) {
2173             char *pass = "";
2174             if (cmd->argc >= 7)
2175               pass = cmd->argv[6];
2176             if (!silc_load_key_pair(cmd->argv[4], cmd->argv[5], pass,
2177                                     &pubkey, &privkey)) {
2178               SAY(conn->client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
2179                   "Could not load key pair, check your arguments");
2180               COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2181               goto out;
2182             }
2183           }
2184
2185           silc_free(nickname);
2186           silc_client_list_free(client, conn, clients);
2187           silc_client_unref_channel(client, conn, channel);
2188
2189           SILC_FSM_CALL(silc_auth_public_key_auth_generate(
2190                                              pubkey, privkey,
2191                                              conn->client->rng,
2192                                              conn->internal->sha1hash,
2193                                              conn->local_id,
2194                                              SILC_ID_CLIENT,
2195                                              silc_client_command_cumode_signed,
2196                                              cmd));
2197           /* NOT REACHED */
2198         }
2199
2200         mode |= SILC_CHANNEL_UMODE_CHANFO;
2201       } else {
2202         mode &= ~SILC_CHANNEL_UMODE_CHANFO;
2203       }
2204       break;
2205     case 'o':
2206       if (add)
2207         mode |= SILC_CHANNEL_UMODE_CHANOP;
2208       else
2209         mode &= ~SILC_CHANNEL_UMODE_CHANOP;
2210       break;
2211     case 'b':
2212       if (add)
2213         mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
2214       else
2215         mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
2216       break;
2217     case 'u':
2218       if (add)
2219         mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
2220       else
2221         mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
2222       break;
2223     case 'r':
2224       if (add)
2225         mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
2226       else
2227         mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
2228       break;
2229     case 'q':
2230       if (add)
2231         mode |= SILC_CHANNEL_UMODE_QUIET;
2232       else
2233         mode &= ~SILC_CHANNEL_UMODE_QUIET;
2234       break;
2235     default:
2236       COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE);
2237       goto out;
2238       break;
2239     }
2240   }
2241
2242   chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2243   SILC_PUT32_MSB(mode, modebuf);
2244   clidp = silc_id_payload_encode(&client_entry->id, SILC_ID_CLIENT);
2245
2246   /* Send the command packet. We support sending only one mode at once
2247      that requires an argument. */
2248   silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, auth ? 4 : 3,
2249                               1, silc_buffer_datalen(chidp),
2250                               2, modebuf, 4,
2251                               3, silc_buffer_datalen(clidp),
2252                               4, silc_buffer_datalen(auth));
2253
2254   silc_buffer_free(chidp);
2255   silc_buffer_free(clidp);
2256   if (auth)
2257     silc_buffer_free(auth);
2258   silc_free(nickname);
2259   silc_client_list_free(client, conn, clients);
2260   silc_client_unref_channel(client, conn, channel);
2261
2262   /* Notify application */
2263   COMMAND(SILC_STATUS_OK);
2264
2265   /** Wait for command reply */
2266   silc_fsm_next(fsm, silc_client_command_reply_wait);
2267   return SILC_FSM_CONTINUE;
2268
2269  out:
2270   silc_client_unref_channel(client, conn, channel);
2271   silc_client_list_free(client, conn, clients);
2272   silc_free(nickname);
2273   return SILC_FSM_FINISH;
2274 }
2275
2276 /********************************** KICK ************************************/
2277
2278 /* KICK command. Kicks a client out of channel. */
2279
2280 SILC_FSM_STATE(silc_client_command_kick)
2281 {
2282   SilcClientCommandContext cmd = fsm_context;
2283   SilcClientConnection conn = cmd->conn;
2284   SilcClient client = conn->client;
2285   SilcChannelEntry channel = NULL;
2286   SilcBuffer idp, idp2;
2287   SilcClientEntry target;
2288   SilcDList clients = NULL;
2289   char *name, tmp[512];
2290
2291   if (cmd->argc < 3) {
2292     SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2293         "Usage: /KICK <channel> <nickname> [<comment>]");
2294     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2295     goto out;
2296   }
2297
2298   if (cmd->argv[1][0] == '*') {
2299     if (!conn->current_channel) {
2300       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2301       goto out;
2302     }
2303
2304     if (client->internal->params->full_channel_names)
2305       silc_snprintf(tmp, sizeof(tmp), conn->current_channel->channel_name);
2306     else
2307       silc_snprintf(tmp, sizeof(tmp), "%s%s%s",
2308                     conn->current_channel->channel_name,
2309                     conn->current_channel->server[0] ? "@" : "",
2310                     conn->current_channel->server);
2311     name = tmp;
2312   } else {
2313     name = cmd->argv[1];
2314   }
2315
2316   if (!conn->current_channel) {
2317     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2318     goto out;
2319   }
2320
2321   /* Get the Channel ID of the channel */
2322   channel = silc_client_get_channel(conn->client, conn, name);
2323   if (!channel) {
2324     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2325     goto out;
2326   }
2327
2328   /* Get the target client */
2329   clients = silc_client_get_clients_local(client, conn, cmd->argv[2], FALSE);
2330   if (!clients) {
2331     SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2332         "No such client: %s", cmd->argv[2]);
2333     COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_NICK);
2334     goto out;
2335   }
2336   target = silc_dlist_get(clients);
2337
2338   /* Send KICK command to the server */
2339   idp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2340   idp2 = silc_id_payload_encode(&target->id, SILC_ID_CLIENT);
2341   if (cmd->argc == 3)
2342     silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
2343                                 1, silc_buffer_datalen(idp),
2344                                 2, silc_buffer_datalen(idp2));
2345   else
2346     silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 3,
2347                                 1, silc_buffer_datalen(idp),
2348                                 2, silc_buffer_datalen(idp2),
2349                                 3, cmd->argv[3], strlen(cmd->argv[3]));
2350
2351   silc_buffer_free(idp);
2352   silc_buffer_free(idp2);
2353   silc_client_list_free(client, conn, clients);
2354   silc_client_unref_channel(client, conn, channel);
2355
2356   /* Notify application */
2357   COMMAND(SILC_STATUS_OK);
2358
2359   /** Wait for command reply */
2360   silc_fsm_next(fsm, silc_client_command_reply_wait);
2361   return SILC_FSM_CONTINUE;
2362
2363  out:
2364   silc_client_unref_channel(client, conn, channel);
2365   return SILC_FSM_FINISH;
2366 }
2367
2368 /***************************** OPER & SILCOPER ******************************/
2369
2370 typedef struct {
2371   unsigned char *passphrase;
2372   SilcUInt32 passphrase_len;
2373   SilcBuffer auth;
2374 } *SilcClientCommandOper;
2375
2376 /* Ask passphrase callback */
2377
2378 static void silc_client_command_oper_cb(const unsigned char *data,
2379                                         SilcUInt32 data_len, void *context)
2380 {
2381   SilcClientCommandContext cmd = context;
2382   SilcClientCommandOper oper = cmd->context;
2383
2384   if (data && data_len)
2385     oper->passphrase = silc_memdup(data, data_len);
2386   oper->passphrase_len = data_len;
2387
2388   /* Continue */
2389   SILC_FSM_CALL_CONTINUE(&cmd->thread);
2390 }
2391
2392 static void silc_client_command_oper_sign_cb(const SilcBuffer data,
2393                                              void *context)
2394 {
2395   SilcClientCommandContext cmd = context;
2396   SilcClientCommandOper oper = cmd->context;
2397
2398   if (data)
2399     oper->auth = silc_buffer_copy(data);
2400
2401   /* Continue */
2402   SILC_FSM_CALL_CONTINUE(&cmd->thread);
2403 }
2404
2405 /* Send OPER/SILCOPER command */
2406
2407 SILC_FSM_STATE(silc_client_command_oper_send)
2408 {
2409   SilcClientCommandContext cmd = fsm_context;
2410   SilcClientConnection conn = cmd->conn;
2411   SilcClientCommandOper oper = cmd->context;
2412   SilcBuffer auth = oper ? oper->auth : NULL;
2413
2414   silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
2415                               1, cmd->argv[1], strlen(cmd->argv[1]),
2416                               2, silc_buffer_datalen(auth));
2417
2418   silc_buffer_clear(auth);
2419   silc_buffer_free(auth);
2420   if (oper) {
2421     silc_free(oper->passphrase);
2422     silc_free(oper);
2423   }
2424
2425   /* Notify application */
2426   COMMAND(SILC_STATUS_OK);
2427
2428   /** Wait for command reply */
2429   silc_fsm_next(fsm, silc_client_command_reply_wait);
2430   return SILC_FSM_CONTINUE;
2431 }
2432
2433 /* Get authentication data */
2434
2435 SILC_FSM_STATE(silc_client_command_oper_get_auth)
2436 {
2437   SilcClientCommandContext cmd = fsm_context;
2438   SilcClientConnection conn = cmd->conn;
2439   SilcClientCommandOper oper = cmd->context;
2440
2441   silc_fsm_next(fsm, silc_client_command_oper_send);
2442
2443   if (!oper || !oper->passphrase) {
2444     /* Encode the public key authentication payload */
2445     SILC_FSM_CALL(silc_auth_public_key_auth_generate(
2446                                        conn->public_key,
2447                                        conn->private_key,
2448                                        conn->client->rng,
2449                                        conn->internal->hash,
2450                                        conn->local_id, SILC_ID_CLIENT,
2451                                        silc_client_command_oper_sign_cb,
2452                                        oper));
2453     /* NOT REACHED */
2454   }
2455
2456   /* Encode the password authentication payload */
2457   oper->auth = silc_auth_payload_encode(NULL, SILC_AUTH_PASSWORD, NULL, 0,
2458                                         oper->passphrase, oper->passphrase_len);
2459
2460   return SILC_FSM_CONTINUE;
2461 }
2462
2463 /* OPER command. Used to obtain server operator privileges. */
2464
2465 SILC_FSM_STATE(silc_client_command_oper)
2466 {
2467   SilcClientCommandContext cmd = fsm_context;
2468   SilcClientConnection conn = cmd->conn;
2469   SilcClientCommandOper oper;
2470
2471   if (cmd->argc < 2) {
2472     SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2473         "Usage: /OPER <username> [-pubkey]");
2474     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2475     return SILC_FSM_FINISH;
2476   }
2477
2478   silc_fsm_next(fsm, silc_client_command_oper_get_auth);
2479
2480   /* Get passphrase */
2481   if (cmd->argc < 3) {
2482     oper = silc_calloc(1, sizeof(*oper));
2483     if (!oper)
2484       return SILC_FSM_FINISH;
2485     cmd->context = oper;
2486     SILC_FSM_CALL(conn->client->internal->
2487                   ops->ask_passphrase(conn->client, conn,
2488                                       silc_client_command_oper_cb, cmd));
2489   }
2490
2491   return SILC_FSM_CONTINUE;
2492 }
2493
2494 /* SILCOPER command. Used to obtain router operator privileges. */
2495
2496 SILC_FSM_STATE(silc_client_command_silcoper)
2497 {
2498   SilcClientCommandContext cmd = fsm_context;
2499   SilcClientConnection conn = cmd->conn;
2500   SilcClientCommandOper oper;
2501
2502   if (cmd->argc < 2) {
2503     SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2504         "Usage: /SILCOPER <username> [-pubkey]");
2505     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2506     return SILC_FSM_FINISH;
2507   }
2508
2509   silc_fsm_next(fsm, silc_client_command_oper_send);
2510
2511   /* Get passphrase */
2512   if (cmd->argc < 3) {
2513     oper = silc_calloc(1, sizeof(*oper));
2514     if (!oper)
2515       return SILC_FSM_FINISH;
2516     cmd->context = oper;
2517     SILC_FSM_CALL(conn->client->internal->
2518                   ops->ask_passphrase(conn->client, conn,
2519                                       silc_client_command_oper_cb, cmd));
2520   }
2521
2522   return SILC_FSM_CONTINUE;
2523 }
2524
2525 /*********************************** BAN ************************************/
2526
2527 /* Command BAN. This is used to manage the ban list of the channel. */
2528
2529 SILC_FSM_STATE(silc_client_command_ban)
2530 {
2531   SilcClientCommandContext cmd = fsm_context;
2532   SilcClientConnection conn = cmd->conn;
2533   SilcClient client = conn->client;
2534   SilcChannelEntry channel;
2535   SilcBuffer chidp, args = NULL;
2536   char *name, *ban = NULL;
2537   unsigned char action[1];
2538   SilcPublicKey pubkey = NULL;
2539
2540   if (cmd->argc < 2) {
2541     SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2542         "Usage: /BAN <channel> "
2543         "[+|-[<nickname>[@<server>[!<username>[@hostname>]]]]]");
2544     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2545     goto out;
2546   }
2547
2548   if (cmd->argv[1][0] == '*') {
2549     if (!conn->current_channel) {
2550       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2551       goto out;
2552     }
2553
2554     channel = conn->current_channel;
2555     silc_client_ref_channel(client, conn, channel);
2556   } else {
2557     name = cmd->argv[1];
2558
2559     channel = silc_client_get_channel(conn->client, conn, name);
2560     if (!channel) {
2561       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2562       goto out;
2563     }
2564   }
2565
2566   if (cmd->argc == 3) {
2567     if (cmd->argv[2][0] == '+')
2568       action[0] = 0x00;
2569     else
2570       action[0] = 0x01;
2571
2572     /* Check if it is public key file to be added to invite list */
2573     silc_pkcs_load_public_key(cmd->argv[2] + 1, SILC_PKCS_ANY, &pubkey);
2574     ban = cmd->argv[2];
2575     if (!pubkey)
2576       ban++;
2577   }
2578
2579   if (ban) {
2580     args = silc_buffer_alloc_size(2);
2581     silc_buffer_format(args,
2582                        SILC_STR_UI_SHORT(1),
2583                        SILC_STR_END);
2584     if (pubkey) {
2585       chidp = silc_public_key_payload_encode(NULL, pubkey);
2586       args = silc_argument_payload_encode_one(args,
2587                                               silc_buffer_datalen(chidp), 2);
2588       silc_buffer_free(chidp);
2589       silc_pkcs_public_key_free(pubkey);
2590     } else {
2591       args = silc_argument_payload_encode_one(args, ban, strlen(ban), 1);
2592     }
2593   }
2594
2595   chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2596
2597   /* Send the command */
2598   silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 3,
2599                               1, silc_buffer_datalen(chidp),
2600                               2, args ? action : NULL, args ? 1 : 0,
2601                               3, silc_buffer_datalen(args));
2602
2603   silc_buffer_free(chidp);
2604   silc_buffer_free(args);
2605   silc_client_unref_channel(client, conn, channel);
2606
2607   /* Notify application */
2608   COMMAND(SILC_STATUS_OK);
2609
2610   /** Wait for command reply */
2611   silc_fsm_next(fsm, silc_client_command_reply_wait);
2612   return SILC_FSM_CONTINUE;
2613
2614  out:
2615   return SILC_FSM_FINISH;
2616 }
2617
2618 /********************************* DETACH ***********************************/
2619
2620 /* Command DETACH. This is used to detach from the server */
2621
2622 SILC_FSM_STATE(silc_client_command_detach)
2623 {
2624   SilcClientCommandContext cmd = fsm_context;
2625   SilcClientConnection conn = cmd->conn;
2626
2627   silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 0);
2628
2629   /* Notify application */
2630   COMMAND(SILC_STATUS_OK);
2631
2632   /** Wait for command reply */
2633   silc_fsm_next(fsm, silc_client_command_reply_wait);
2634   return SILC_FSM_CONTINUE;
2635 }
2636
2637 /********************************** WATCH ***********************************/
2638
2639 /* Command WATCH. */
2640
2641 SILC_FSM_STATE(silc_client_command_watch)
2642 {
2643   SilcClientCommandContext cmd = fsm_context;
2644   SilcClientConnection conn = cmd->conn;
2645   SilcBuffer args = NULL;
2646   int type = 0;
2647   const char *pubkey = NULL;
2648   SilcBool pubkey_add = TRUE;
2649
2650   if (cmd->argc < 3) {
2651     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2652     goto out;
2653   }
2654
2655   if (!strcasecmp(cmd->argv[1], "-add")) {
2656     type = 2;
2657   } else if (!strcasecmp(cmd->argv[1], "-del")) {
2658     type = 3;
2659   } else if (!strcasecmp(cmd->argv[1], "-pubkey") && cmd->argc >= 3) {
2660     type = 4;
2661     pubkey = cmd->argv[2] + 1;
2662     if (cmd->argv[2][0] == '-')
2663       pubkey_add = FALSE;
2664   } else {
2665     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2666     goto out;
2667   }
2668
2669   if (pubkey) {
2670     SilcPublicKey pk;
2671     SilcBuffer buffer;
2672
2673     if (!silc_pkcs_load_public_key(pubkey, SILC_PKCS_ANY, &pk)) {
2674       SAY(conn->client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
2675           "Could not load public key %s, check the filename", pubkey);
2676       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2677       goto out;
2678     }
2679
2680     args = silc_buffer_alloc_size(2);
2681     silc_buffer_format(args,
2682                        SILC_STR_UI_SHORT(1),
2683                        SILC_STR_END);
2684     buffer = silc_public_key_payload_encode(NULL, pk);
2685     args = silc_argument_payload_encode_one(args, silc_buffer_datalen(buffer),
2686                                             pubkey_add ? 0x00 : 0x01);
2687     silc_buffer_free(buffer);
2688     silc_pkcs_public_key_free(pk);
2689   }
2690
2691   /* If watching by nickname, resolve all users with that nickname so that
2692      we get their information immediately. */
2693   if (type == 2)
2694     silc_client_get_clients(conn->client, conn, cmd->argv[2], NULL,
2695                             silc_client_command_resolve_dummy, NULL);
2696
2697   /* Send the commmand */
2698   silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
2699                               1, silc_buffer_datalen(conn->internal->
2700                                                      local_idp),
2701                               type, pubkey ? args->data : cmd->argv[2],
2702                               pubkey ? silc_buffer_len(args) :
2703                               cmd->argv_lens[2]);
2704
2705   silc_buffer_free(args);
2706
2707   /* Notify application */
2708   COMMAND(SILC_STATUS_OK);
2709
2710   /** Wait for command reply */
2711   silc_fsm_next(fsm, silc_client_command_reply_wait);
2712   return SILC_FSM_CONTINUE;
2713
2714  out:
2715   return SILC_FSM_FINISH;
2716 }
2717
2718 /********************************** LEAVE ***********************************/
2719
2720 /* LEAVE command. Leaves a channel. Client removes itself from a channel. */
2721
2722 SILC_FSM_STATE(silc_client_command_leave)
2723 {
2724   SilcClientCommandContext cmd = fsm_context;
2725   SilcClientConnection conn = cmd->conn;
2726   SilcClient client = conn->client;
2727   SilcChannelEntry channel;
2728   SilcBuffer idp;
2729   char *name, tmp[512];
2730
2731   if (cmd->argc != 2) {
2732     SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2733         "Usage: /LEAVE <channel>");
2734     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2735     goto out;
2736   }
2737
2738   if (cmd->argv[1][0] == '*') {
2739     if (!conn->current_channel) {
2740       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2741       goto out;
2742     }
2743
2744     if (client->internal->params->full_channel_names)
2745       silc_snprintf(tmp, sizeof(tmp), conn->current_channel->channel_name);
2746     else
2747       silc_snprintf(tmp, sizeof(tmp), "%s%s%s",
2748                     conn->current_channel->channel_name,
2749                     conn->current_channel->server[0] ? "@" : "",
2750                     conn->current_channel->server);
2751     name = tmp;
2752   } else {
2753     name = cmd->argv[1];
2754   }
2755
2756   /* Get the channel entry */
2757   channel = silc_client_get_channel(conn->client, conn, name);
2758   if (!channel) {
2759     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2760     goto out;
2761   }
2762
2763   idp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2764
2765   /* Send LEAVE command to the server */
2766   silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
2767                               1, silc_buffer_datalen(idp));
2768
2769   silc_buffer_free(idp);
2770
2771   /* Notify application */
2772   COMMAND(SILC_STATUS_OK);
2773
2774   if (conn->current_channel == channel)
2775     conn->current_channel = NULL;
2776
2777   silc_client_unref_channel(client, conn, channel);
2778
2779   /** Wait for command reply */
2780   silc_fsm_next(fsm, silc_client_command_reply_wait);
2781   return SILC_FSM_CONTINUE;
2782
2783  out:
2784   return SILC_FSM_FINISH;
2785 }
2786
2787 /********************************** USERS ***********************************/
2788
2789 /* Command USERS. Requests the USERS of the clients joined on requested
2790    channel. */
2791
2792 SILC_FSM_STATE(silc_client_command_users)
2793 {
2794   SilcClientCommandContext cmd = fsm_context;
2795   SilcClientConnection conn = cmd->conn;
2796   char *name, tmp[512];
2797
2798   if (cmd->argc != 2) {
2799     SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2800         "Usage: /USERS <channel>");
2801     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2802     goto out;
2803   }
2804
2805   if (cmd->argv[1][0] == '*') {
2806     if (!conn->current_channel) {
2807       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2808       goto out;
2809     }
2810
2811     if (conn->client->internal->params->full_channel_names)
2812       silc_snprintf(tmp, sizeof(tmp), conn->current_channel->channel_name);
2813     else
2814       silc_snprintf(tmp, sizeof(tmp), "%s%s%s",
2815                     conn->current_channel->channel_name,
2816                     conn->current_channel->server[0] ? "@" : "",
2817                     conn->current_channel->server);
2818     name = tmp;
2819   } else {
2820     name = cmd->argv[1];
2821   }
2822
2823   /* Send USERS command to the server */
2824   silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
2825                               2, name, strlen(name));
2826
2827   /* Notify application */
2828   COMMAND(SILC_STATUS_OK);
2829
2830   /** Wait for command reply */
2831   silc_fsm_next(fsm, silc_client_command_reply_wait);
2832   return SILC_FSM_CONTINUE;
2833
2834  out:
2835   return SILC_FSM_FINISH;
2836 }
2837
2838 /********************************* GETKEY ***********************************/
2839
2840 /* Command GETKEY. Used to fetch remote client's public key. */
2841
2842 SILC_FSM_STATE(silc_client_command_getkey)
2843 {
2844   SilcClientCommandContext cmd = fsm_context;
2845   SilcClientConnection conn = cmd->conn;
2846   SilcClient client = conn->client;
2847   SilcClientEntry client_entry;
2848   SilcServerEntry server_entry;
2849   SilcDList clients;
2850   SilcBuffer idp;
2851
2852   if (cmd->argc < 2) {
2853     client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_INFO,
2854                      "Usage: /GETKEY <nickname or server name>");
2855     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2856     return SILC_FSM_FINISH;
2857   }
2858
2859   /* Find client entry */
2860   clients = silc_client_get_clients_local(client, conn, cmd->argv[1], FALSE);
2861   if (!clients) {
2862     /* Check whether user requested server */
2863     server_entry = silc_client_get_server(client, conn, cmd->argv[1]);
2864     if (!server_entry) {
2865       if (cmd->resolved) {
2866         /* Resolving didn't find anything.  We should never get here as
2867            errors are handled in the resolving callback. */
2868         COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_NICK);
2869         COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_SERVER);
2870         return SILC_FSM_FINISH;
2871       }
2872
2873       /* No client or server exist with this name, query for both. */
2874       cmd->resolved = TRUE;
2875       SILC_FSM_CALL(silc_client_command_send(client, conn,
2876                                              SILC_COMMAND_IDENTIFY,
2877                                              silc_client_command_continue,
2878                                              cmd, 2,
2879                                              1, cmd->argv[1],
2880                                              strlen(cmd->argv[1]),
2881                                              2, cmd->argv[1],
2882                                              strlen(cmd->argv[1])));
2883       /* NOT REACHED */
2884     }
2885     idp = silc_id_payload_encode(&server_entry->id, SILC_ID_SERVER);
2886     silc_client_unref_server(client, conn, server_entry);
2887   } else {
2888     client_entry = silc_dlist_get(clients);
2889     idp = silc_id_payload_encode(&client_entry->id, SILC_ID_CLIENT);
2890     silc_client_list_free(client, conn, clients);
2891   }
2892
2893   /* Send the commmand */
2894   silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
2895                               1, silc_buffer_datalen(idp));
2896
2897   silc_buffer_free(idp);
2898
2899   /* Notify application */
2900   COMMAND(SILC_STATUS_OK);
2901
2902   /** Wait for command reply */
2903   silc_fsm_next(fsm, silc_client_command_reply_wait);
2904   return SILC_FSM_CONTINUE;
2905 }
2906
2907 /********************************* SERVICE **********************************/
2908
2909 /* Command SERVICE.  Negotiates service agreement with server. */
2910 /* XXX incomplete */
2911
2912 SILC_FSM_STATE(silc_client_command_service)
2913 {
2914   SilcClientCommandContext cmd = fsm_context;
2915 #if 0
2916   SilcClientConnection conn = cmd->conn;
2917   SilcBuffer buffer;
2918   char *name;
2919
2920   if (cmd->argc < 2) {
2921     SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2922         "Usage: /SERVICE [<service name>] [-pubkey]");
2923     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2924     return SILC_FSM_FINISH;
2925   }
2926
2927   name = cmd->argv[1];
2928
2929   /* Send SERVICE command to the server */
2930   buffer = silc_command_payload_encode_va(SILC_COMMAND_SERVICE,
2931                                           ++conn->cmd_ident, 1,
2932                                           1, name, strlen(name));
2933   silc_client_packet_send(conn->client, conn->sock, SILC_PACKET_COMMAND,
2934                           NULL, 0, NULL, NULL, buffer->data,
2935                           buffer->len, TRUE);
2936   silc_buffer_free(buffer);
2937 #endif /* 0 */
2938
2939   /* Notify application */
2940   COMMAND(SILC_STATUS_OK);
2941
2942   /** Wait for command reply */
2943   silc_fsm_next(fsm, silc_client_command_reply_wait);
2944   return SILC_FSM_CONTINUE;
2945 }
2946
2947 /* Register all default commands provided by the client library for the
2948    application. */
2949
2950 void silc_client_commands_register(SilcClient client)
2951 {
2952   silc_list_init(client->internal->commands, struct SilcClientCommandStruct,
2953                  next);
2954
2955   SILC_CLIENT_CMD(whois, WHOIS, "WHOIS", 5);
2956   SILC_CLIENT_CMD(whowas, WHOWAS, "WHOWAS", 3);
2957   SILC_CLIENT_CMD(identify, IDENTIFY, "IDENTIFY", 3);
2958   SILC_CLIENT_CMD(nick, NICK, "NICK", 2);
2959   SILC_CLIENT_CMD(list, LIST, "LIST", 2);
2960   SILC_CLIENT_CMD(topic, TOPIC, "TOPIC", 3);
2961   SILC_CLIENT_CMD(invite, INVITE, "INVITE", 3);
2962   SILC_CLIENT_CMD(quit, QUIT, "QUIT", 2);
2963   SILC_CLIENT_CMD(kill, KILL, "KILL", 4);
2964   SILC_CLIENT_CMD(info, INFO, "INFO", 2);
2965   SILC_CLIENT_CMD(stats, STATS, "STATS", 0);
2966   SILC_CLIENT_CMD(ping, PING, "PING", 2);
2967   SILC_CLIENT_CMD(oper, OPER, "OPER", 3);
2968   SILC_CLIENT_CMD(join, JOIN, "JOIN", 9);
2969   SILC_CLIENT_CMD(motd, MOTD, "MOTD", 2);
2970   SILC_CLIENT_CMD(umode, UMODE, "UMODE", 2);
2971   SILC_CLIENT_CMD(cmode, CMODE, "CMODE", 6);
2972   SILC_CLIENT_CMD(cumode, CUMODE, "CUMODE", 9);
2973   SILC_CLIENT_CMD(kick, KICK, "KICK", 4);
2974   SILC_CLIENT_CMD(ban, BAN, "BAN", 3);
2975   SILC_CLIENT_CMD(detach, DETACH, "DETACH", 0);
2976   SILC_CLIENT_CMD(watch, WATCH, "WATCH", 3);
2977   SILC_CLIENT_CMD(silcoper, SILCOPER, "SILCOPER", 3);
2978   SILC_CLIENT_CMD(leave, LEAVE, "LEAVE", 2);
2979   SILC_CLIENT_CMD(users, USERS, "USERS", 2);
2980   SILC_CLIENT_CMD(getkey, GETKEY, "GETKEY", 2);
2981   SILC_CLIENT_CMD(service, SERVICE, "SERVICE", 10);
2982 }
2983
2984 /* Unregister all commands. */
2985
2986 void silc_client_commands_unregister(SilcClient client)
2987 {
2988   SILC_CLIENT_CMDU(whois, WHOIS, "WHOIS");
2989   SILC_CLIENT_CMDU(whowas, WHOWAS, "WHOWAS");
2990   SILC_CLIENT_CMDU(identify, IDENTIFY, "IDENTIFY");
2991   SILC_CLIENT_CMDU(nick, NICK, "NICK");
2992   SILC_CLIENT_CMDU(list, LIST, "LIST");
2993   SILC_CLIENT_CMDU(topic, TOPIC, "TOPIC");
2994   SILC_CLIENT_CMDU(invite, INVITE, "INVITE");
2995   SILC_CLIENT_CMDU(quit, QUIT, "QUIT");
2996   SILC_CLIENT_CMDU(kill, KILL, "KILL");
2997   SILC_CLIENT_CMDU(info, INFO, "INFO");
2998   SILC_CLIENT_CMDU(stats, STATS, "STATS");
2999   SILC_CLIENT_CMDU(ping, PING, "PING");
3000   SILC_CLIENT_CMDU(oper, OPER, "OPER");
3001   SILC_CLIENT_CMDU(join, JOIN, "JOIN");
3002   SILC_CLIENT_CMDU(motd, MOTD, "MOTD");
3003   SILC_CLIENT_CMDU(umode, UMODE, "UMODE");
3004   SILC_CLIENT_CMDU(cmode, CMODE, "CMODE");
3005   SILC_CLIENT_CMDU(cumode, CUMODE, "CUMODE");
3006   SILC_CLIENT_CMDU(kick, KICK, "KICK");
3007   SILC_CLIENT_CMDU(ban, BAN, "BAN");
3008   SILC_CLIENT_CMDU(detach, DETACH, "DETACH");
3009   SILC_CLIENT_CMDU(watch, WATCH, "WATCH");
3010   SILC_CLIENT_CMDU(silcoper, SILCOPER, "SILCOPER");
3011   SILC_CLIENT_CMDU(leave, LEAVE, "LEAVE");
3012   SILC_CLIENT_CMDU(users, USERS, "USERS");
3013   SILC_CLIENT_CMDU(getkey, GETKEY, "GETKEY");
3014   SILC_CLIENT_CMDU(service, SERVICE, "SERVICE");
3015 }
3016
3017 /****************** Client Side Incoming Command Handling *******************/
3018
3019 typedef struct {
3020   SilcClientConnection conn;
3021   SilcUInt16 cmd_ident;
3022 } *SilcClientProcessWhois;
3023
3024 /* Send reply to WHOIS from server */
3025
3026 static void
3027 silc_client_command_process_whois_send(SilcBool success,
3028                                        const unsigned char *data,
3029                                        SilcUInt32 data_len, void *context)
3030 {
3031   SilcClientProcessWhois w = context;
3032   SilcBufferStruct buffer;
3033   SilcBuffer packet;
3034
3035   if (!data) {
3036     silc_free(w);
3037     return;
3038   }
3039
3040   silc_buffer_set(&buffer, (unsigned char *)data, data_len);
3041
3042   /* Send the attributes back in COMMAND_REPLY packet */
3043   packet =
3044     silc_command_reply_payload_encode_va(SILC_COMMAND_WHOIS,
3045                                          SILC_STATUS_OK, 0, w->cmd_ident,
3046                                          1, 11, buffer.data,
3047                                          silc_buffer_len(&buffer));
3048   if (!packet) {
3049     silc_free(w);
3050     return;
3051   }
3052
3053   SILC_LOG_DEBUG(("Sending back requested WHOIS attributes"));
3054
3055   silc_packet_send(w->conn->stream, SILC_PACKET_COMMAND_REPLY, 0,
3056                    silc_buffer_datalen(packet));
3057
3058   silc_buffer_free(packet);
3059   silc_free(w);
3060 }
3061
3062 /* Process WHOIS command from server */
3063
3064 static void silc_client_command_process_whois(SilcClient client,
3065                                               SilcClientConnection conn,
3066                                               SilcCommandPayload payload,
3067                                               SilcArgumentPayload args)
3068 {
3069   SilcClientProcessWhois w;
3070   SilcDList attrs;
3071   unsigned char *tmp;
3072   SilcUInt32 tmp_len;
3073
3074   SILC_LOG_DEBUG(("Received WHOIS command"));
3075
3076   /* Try to take the Requested Attributes */
3077   tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
3078   if (!tmp)
3079     return;
3080
3081   attrs = silc_attribute_payload_parse(tmp, tmp_len);
3082   if (!attrs)
3083     return;
3084
3085   w = silc_calloc(1, sizeof(*w));
3086   if (!w) {
3087     silc_attribute_payload_list_free(attrs);
3088     return;
3089   }
3090   w->conn = conn;
3091   w->cmd_ident = silc_command_get_ident(payload);
3092
3093   /* Process requested attributes */
3094   silc_client_attributes_process(client, conn, attrs,
3095                                  silc_client_command_process_whois_send, w);
3096   silc_attribute_payload_list_free(attrs);
3097 }
3098
3099 /* Client is able to receive some command packets even though they are
3100    special case.  Server may send WHOIS command to the client to retrieve
3101    Requested Attributes information for WHOIS query the server is
3102    processing. This function currently handles only the WHOIS command,
3103    but if in the future more commands may arrive then this can be made
3104    to support other commands too. */
3105
3106 SILC_FSM_STATE(silc_client_command)
3107 {
3108   SilcClientConnection conn = fsm_context;
3109   SilcClient client = conn->client;
3110   SilcPacket packet = state_context;
3111   SilcCommandPayload payload;
3112   SilcCommand command;
3113   SilcArgumentPayload args;
3114
3115   /* Get command payload from packet */
3116   payload = silc_command_payload_parse(packet->buffer.data,
3117                                        silc_buffer_len(&packet->buffer));
3118   if (!payload) {
3119     SILC_LOG_DEBUG(("Bad command packet"));
3120     return SILC_FSM_FINISH;
3121   }
3122
3123   /* Get arguments */
3124   args = silc_command_get_args(payload);
3125
3126   /* Get the command */
3127   command = silc_command_get(payload);
3128   switch (command) {
3129
3130   case SILC_COMMAND_WHOIS:
3131     /* Ignore everything if requested by application */
3132     if (conn->internal->params.ignore_requested_attributes)
3133       break;
3134
3135     silc_client_command_process_whois(client, conn, payload, args);
3136     break;
3137
3138   default:
3139     break;
3140   }
3141
3142   silc_command_payload_free(payload);
3143   return SILC_FSM_FINISH;
3144 }