Removed HTTP server libary, it's available in SRT now
[silc.git] / lib / silcclient / command.c
1 /*
2
3   command.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 1997 - 2007 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; version 2 of the License.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18 */
19 /* $Id$ */
20
21 #include "silc.h"
22 #include "silcclient.h"
23 #include "client_internal.h"
24
25 /************************** Types and definitions ***************************/
26
27 /* Command operation that is called at the end of all commands.
28    Usage: COMMAND(status); */
29 #define COMMAND(status) cmd->conn->client->internal->ops->command(      \
30   cmd->conn->client, cmd->conn, TRUE, cmd->cmd, (status), cmd->argc, cmd->argv)
31
32 /* Error to application. Usage: COMMAND_ERROR(status); */
33 #define COMMAND_ERROR(status)                                   \
34   cmd->conn->client->internal->ops->command(cmd->conn->client,  \
35   cmd->conn, FALSE, cmd->cmd, (status), cmd->argc, cmd->argv)
36
37 /* Used to register new command */
38 #define SILC_CLIENT_CMD(func, cmd, name, args)                          \
39 silc_client_command_register(client, SILC_COMMAND_##cmd, name,          \
40                              silc_client_command_##func,                \
41                              silc_client_command_reply_##func, args)
42
43 /* Used to unregister command */
44 #define SILC_CLIENT_CMDU(func, cmd, name)                               \
45 silc_client_command_unregister(client, SILC_COMMAND_##cmd,              \
46                                silc_client_command_##func,              \
47                                silc_client_command_reply_##func)
48
49 #define SAY cmd->conn->client->internal->ops->say
50
51 /************************ Static utility functions **************************/
52
53 /* Return next available command identifier. */
54
55 static SilcUInt16 silc_client_cmd_ident(SilcClientConnection conn)
56 {
57   SilcUInt16 cmd_ident;
58
59   cmd_ident = silc_atomic_add_int16(&conn->internal->cmd_ident, 1);
60   if (!cmd_ident)
61     cmd_ident = silc_atomic_add_int16(&conn->internal->cmd_ident, 1);
62
63   return cmd_ident;
64 }
65
66 /* State to finish command thread after an error in resolving command */
67
68 SILC_FSM_STATE(silc_client_command_continue_error)
69 {
70   /* Destructor will free all resources */
71   return SILC_FSM_FINISH;
72 }
73
74 /* Command reply callback to continue with the execution of a command.
75    This will continue when first successful reply is received, and ignores
76    the rest.  On the other hand, if only errors are received it will
77    wait for all errors before continuing. */
78
79 static SilcBool silc_client_command_continue(SilcClient client,
80                                              SilcClientConnection conn,
81                                              SilcCommand command,
82                                              SilcStatus status,
83                                              SilcStatus error,
84                                              void *context,
85                                              va_list ap)
86 {
87   SilcClientCommandContext cmd = context;
88
89   /* Continue immediately when successful reply is received */
90   if (status == SILC_STATUS_OK || !SILC_STATUS_IS_ERROR(error)) {
91     SILC_FSM_CALL_CONTINUE(&cmd->thread);
92     return FALSE;
93   }
94
95   /* Error */
96   COMMAND_ERROR(error);
97
98   /* Continue after last error is received */
99   if (SILC_STATUS_IS_ERROR(status) ||
100       (status == SILC_STATUS_LIST_END && SILC_STATUS_IS_ERROR(error))) {
101     silc_fsm_next(&cmd->thread, silc_client_command_continue_error);
102     SILC_FSM_CALL_CONTINUE(&cmd->thread);
103     return FALSE;
104   }
105
106   return TRUE;
107 }
108
109 /* Continues after resolving completed. */
110
111 static void silc_client_command_resolve_continue(SilcClient client,
112                                                  SilcClientConnection conn,
113                                                  SilcStatus status,
114                                                  SilcDList clients,
115                                                  void *context)
116 {
117   SilcClientCommandContext cmd = context;
118
119   if (status != SILC_STATUS_OK)
120     silc_fsm_next(&cmd->thread, silc_client_command_continue_error);
121
122   /* Continue with the command */
123   SILC_FSM_CALL_CONTINUE(&cmd->thread);
124 }
125
126 /* Dummy command callback.  Nothing interesting to do here.  Use this when
127    you just send command but don't care about reply. */
128
129 SilcBool silc_client_command_called_dummy(SilcClient client,
130                                           SilcClientConnection conn,
131                                           SilcCommand command,
132                                           SilcStatus status,
133                                           SilcStatus error,
134                                           void *context,
135                                           va_list ap)
136 {
137   return FALSE;
138 }
139
140 /* Dummy resolving callback.  Nothing interesting to do here.  Use this
141    when you just resolve entires but don't care about reply. */
142
143 void silc_client_command_resolve_dummy(SilcClient client,
144                                        SilcClientConnection conn,
145                                        SilcStatus status,
146                                        SilcDList clients,
147                                        void *context)
148 {
149   /* Nothing */
150 }
151
152 /* Register command to client */
153
154 static SilcBool
155 silc_client_command_register(SilcClient client,
156                              SilcCommand command,
157                              const char *name,
158                              SilcFSMStateCallback command_func,
159                              SilcFSMStateCallback command_reply_func,
160                              SilcUInt8 max_args)
161 {
162   SilcClientCommand cmd;
163
164   cmd = silc_calloc(1, sizeof(*cmd));
165   if (!cmd)
166     return FALSE;
167   cmd->cmd = command;
168   cmd->command = command_func;
169   cmd->reply = command_reply_func;
170   cmd->max_args = max_args;
171   cmd->name = name ? strdup(name) : NULL;
172   if (!cmd->name) {
173     silc_free(cmd);
174     return FALSE;
175   }
176
177   silc_list_add(client->internal->commands, cmd);
178
179   return TRUE;
180 }
181
182 /* Unregister command from client */
183
184 static SilcBool
185 silc_client_command_unregister(SilcClient client,
186                                SilcCommand command,
187                                SilcFSMStateCallback command_func,
188                                SilcFSMStateCallback command_reply_func)
189 {
190   SilcClientCommand cmd;
191
192   silc_list_start(client->internal->commands);
193   while ((cmd = silc_list_get(client->internal->commands)) != SILC_LIST_END) {
194     if (cmd->cmd == command && cmd->command == command_func &&
195         cmd->reply == command_reply_func) {
196       silc_list_del(client->internal->commands, cmd);
197       silc_free(cmd->name);
198       silc_free(cmd);
199       return TRUE;
200     }
201   }
202
203   return FALSE;
204 }
205
206 /* Finds and returns a pointer to the command list. Return NULL if the
207    command is not found. */
208
209 static SilcClientCommand silc_client_command_find(SilcClient client,
210                                                   const char *name)
211 {
212   SilcClientCommand cmd;
213
214   silc_list_start(client->internal->commands);
215   while ((cmd = silc_list_get(client->internal->commands)) != SILC_LIST_END) {
216     if (cmd->name && !strcasecmp(cmd->name, name))
217       return cmd;
218   }
219
220   return NULL;
221 }
222
223 /* Command thread destructor */
224
225 static void silc_client_command_destructor(SilcFSMThread thread,
226                                            void *fsm_context,
227                                            void *destructor_context)
228 {
229   SilcClientCommandContext cmd = fsm_context;
230   SilcClientConnection conn = cmd->conn;
231
232   /* Removes commands that aren't waiting for reply but are waiting
233      for something.  They may not have been removed yet. */
234   silc_list_del(conn->internal->pending_commands, cmd);
235
236   silc_client_command_free(cmd);
237 }
238
239 /* Add a command pending a command reply.  Used internally by the library. */
240
241 static SilcBool
242 silc_client_command_add_pending(SilcClientConnection conn,
243                                 SilcClientCommandContext cmd,
244                                 SilcClientCommandReply reply,
245                                 void *context)
246 {
247   SilcClientCommandReplyCallback cb;
248
249   silc_mutex_lock(conn->internal->lock);
250
251   /* Add pending callback, if defined */
252   if (reply) {
253     cb = silc_calloc(1, sizeof(*cb));
254     if (!cb) {
255       silc_mutex_unlock(conn->internal->lock);
256       return FALSE;
257     }
258     cb->reply = reply;
259     cb->context = context;
260     silc_list_add(cmd->reply_callbacks, cb);
261   }
262
263   /* Add pending reply */
264   silc_list_add(conn->internal->pending_commands, cmd);
265
266   silc_mutex_unlock(conn->internal->lock);
267
268   return TRUE;
269 }
270
271 /* Generic function to send any command. The arguments must be sent already
272    encoded into correct format and in correct order.  Arguments come from
273    variable argument list pointer. */
274
275 static SilcUInt16 silc_client_command_send_vap(SilcClient client,
276                                                SilcClientConnection conn,
277                                                SilcClientCommandContext cmd,
278                                                SilcCommand command,
279                                                SilcClientCommandReply reply,
280                                                void *reply_context,
281                                                SilcUInt32 argc, va_list ap)
282 {
283   SilcBuffer packet;
284
285   SILC_LOG_DEBUG(("Send command %s", silc_get_command_name(command)));
286
287   if (conn->internal->disconnected)
288     return 0;
289
290   if (!cmd->cmd_ident)
291     cmd->cmd_ident = silc_client_cmd_ident(conn);
292
293   /* Encode command payload */
294   packet = silc_command_payload_encode_vap(command, cmd->cmd_ident, argc, ap);
295   if (!packet)
296     return 0;
297
298   /* Send the command */
299   if (!silc_packet_send(conn->stream, SILC_PACKET_COMMAND, 0,
300                         silc_buffer_datalen(packet))) {
301     silc_buffer_free(packet);
302     return 0;
303   }
304
305   /* Add the command pending command reply */
306   silc_client_command_add_pending(conn, cmd, reply, reply_context);
307
308   silc_buffer_free(packet);
309
310   return cmd->cmd_ident;
311 }
312
313 /* Generic function to send any command. The arguments must be sent already
314    encoded into correct format and in correct order.  Arguments come from
315    arrays. */
316
317 static SilcUInt16
318 silc_client_command_send_arg_array(SilcClient client,
319                                    SilcClientConnection conn,
320                                    SilcClientCommandContext cmd,
321                                    SilcCommand command,
322                                    SilcClientCommandReply reply,
323                                    void *reply_context,
324                                    SilcUInt32 argc,
325                                    unsigned char **argv,
326                                    SilcUInt32 *argv_lens,
327                                    SilcUInt32 *argv_types)
328 {
329   SilcBuffer packet;
330
331   SILC_LOG_DEBUG(("Send command %s", silc_get_command_name(command)));
332
333   if (conn->internal->disconnected)
334     return 0;
335
336   if (!cmd->cmd_ident)
337     cmd->cmd_ident = silc_client_cmd_ident(conn);
338
339   /* Encode command payload */
340   packet = silc_command_payload_encode(command, argc, argv, argv_lens,
341                                        argv_types, cmd->cmd_ident);
342   if (!packet)
343     return 0;
344
345   /* Send the command */
346   if (!silc_packet_send(conn->stream, SILC_PACKET_COMMAND, 0,
347                         silc_buffer_datalen(packet))) {
348     silc_buffer_free(packet);
349     return 0;
350   }
351
352   /* Add the command pending command reply */
353   silc_client_command_add_pending(conn, cmd, reply, reply_context);
354
355   silc_buffer_free(packet);
356
357   return cmd->cmd_ident;
358 }
359
360 /* Generic function to send any command. The arguments must be sent already
361    encoded into correct format and in correct order.  This is used internally
362    by the library.  */
363
364 static SilcUInt16 silc_client_command_send_va(SilcClientConnection conn,
365                                               SilcClientCommandContext cmd,
366                                               SilcCommand command,
367                                               SilcClientCommandReply reply,
368                                               void *reply_context,
369                                               SilcUInt32 argc, ...)
370 {
371   va_list ap;
372   SilcUInt16 cmd_ident;
373
374   va_start(ap, argc);
375   cmd_ident = silc_client_command_send_vap(conn->client, conn, cmd, command,
376                                            reply, reply_context, argc, ap);
377   va_end(ap);
378
379   return cmd_ident;
380 }
381
382 /****************************** Command API *********************************/
383
384 /* Free command context and its internals */
385
386 void silc_client_command_free(SilcClientCommandContext cmd)
387 {
388   SilcClientCommandReplyCallback cb;
389   int i;
390
391   for (i = 0; i < cmd->argc; i++)
392     silc_free(cmd->argv[i]);
393   silc_free(cmd->argv);
394   silc_free(cmd->argv_lens);
395   silc_free(cmd->argv_types);
396
397   silc_list_start(cmd->reply_callbacks);
398   while ((cb = silc_list_get(cmd->reply_callbacks)))
399     silc_free(cb);
400
401   silc_free(cmd);
402 }
403
404 /* Executes a command */
405
406 SilcUInt16 silc_client_command_call(SilcClient client,
407                                     SilcClientConnection conn,
408                                     const char *command_line, ...)
409 {
410   va_list va;
411   SilcUInt32 argc = 0;
412   unsigned char **argv = NULL;
413   SilcUInt32 *argv_lens = NULL, *argv_types = NULL;
414   SilcClientCommand command;
415   SilcClientCommandContext cmd;
416   char *arg;
417
418   if (!conn) {
419     client->internal->ops->say(client, NULL, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
420       "You are not connected to a server, please connect to server");
421     return 0;
422   }
423
424   /* Parse arguments */
425   va_start(va, command_line);
426   if (command_line) {
427     char *command_name;
428
429     /* Get command name */
430     command_name = silc_memdup(command_line, strcspn(command_line, " "));
431     if (!command_name)
432       return 0;
433
434     /* Find command by name */
435     command = silc_client_command_find(client, command_name);
436     if (!command) {
437       silc_free(command_name);
438       return 0;
439     }
440
441     /* Parse command line */
442     silc_parse_command_line((char *)command_line, &argv, &argv_lens,
443                             &argv_types, &argc, command->max_args);
444
445     silc_free(command_name);
446   } else {
447     arg = va_arg(va, char *);
448     if (!arg)
449       return 0;
450
451     /* Find command by name */
452     command = silc_client_command_find(client, arg);
453     if (!command)
454       return 0;
455
456     while (arg) {
457       argv = silc_realloc(argv, sizeof(*argv) * (argc + 1));
458       argv_lens = silc_realloc(argv_lens, sizeof(*argv_lens) * (argc + 1));
459       argv_types = silc_realloc(argv_types, sizeof(*argv_types) * (argc + 1));
460       if (!argv || !argv_lens || !argv_types)
461         return 0;
462       argv[argc] = silc_memdup(arg, strlen(arg));
463       if (!argv[argc])
464         return 0;
465       argv_lens[argc] = strlen(arg);
466       argv_types[argc] = argc;
467       argc++;
468       arg = va_arg(va, char *);
469     }
470   }
471   va_end(va);
472
473   /* Allocate command context */
474   cmd = silc_calloc(1, sizeof(*cmd));
475   if (!cmd)
476     return 0;
477   cmd->conn = conn;
478   cmd->cmd = command->cmd;
479   cmd->argc = argc;
480   cmd->argv = argv;
481   cmd->argv_lens = argv_lens;
482   cmd->argv_types = argv_types;
483   cmd->cmd_ident = silc_client_cmd_ident(conn);
484   cmd->called = TRUE;
485   cmd->verbose = TRUE;
486   silc_list_init(cmd->reply_callbacks,
487                  struct SilcClientCommandReplyCallbackStruct, next);
488
489   /*** Call command */
490   SILC_LOG_DEBUG(("Calling %s command", silc_get_command_name(cmd->cmd)));
491   silc_fsm_thread_init(&cmd->thread, &conn->internal->fsm, cmd,
492                        silc_client_command_destructor, NULL, FALSE);
493   silc_fsm_start_sync(&cmd->thread, command->command);
494
495   return cmd->cmd_ident;
496 }
497
498 /* Generic function to send any command. The arguments must be sent already
499    encoded into correct format and in correct order. */
500
501 SilcUInt16 silc_client_command_send(SilcClient client,
502                                     SilcClientConnection conn,
503                                     SilcCommand command,
504                                     SilcClientCommandReply reply,
505                                     void *reply_context,
506                                     SilcUInt32 argc, ...)
507 {
508   SilcClientCommandContext cmd;
509   va_list ap;
510
511   if (!conn || !reply)
512     return 0;
513
514   /* Allocate command context */
515   cmd = silc_calloc(1, sizeof(*cmd));
516   if (!cmd)
517     return 0;
518   cmd->conn = conn;
519   cmd->cmd = command;
520   silc_list_init(cmd->reply_callbacks,
521                  struct SilcClientCommandReplyCallbackStruct, next);
522
523   /* Send the command */
524   va_start(ap, argc);
525   cmd->cmd_ident =
526     silc_client_command_send_vap(client, conn, cmd, command, reply,
527                                  reply_context, argc, ap);
528   va_end(ap);
529
530   if (!cmd->cmd_ident) {
531     silc_client_command_free(cmd);
532     return 0;
533   }
534
535   /*** Wait for command reply */
536   silc_fsm_thread_init(&cmd->thread, &conn->internal->fsm, cmd,
537                        silc_client_command_destructor, NULL, FALSE);
538   silc_fsm_start_sync(&cmd->thread, silc_client_command_reply_wait);
539
540   return cmd->cmd_ident;
541 }
542
543 /* Generic function to send any command. The arguments must be sent already
544    encoded into correct format and in correct order.  Arguments come from
545    arrays. */
546
547 SilcUInt16 silc_client_command_send_argv(SilcClient client,
548                                          SilcClientConnection conn,
549                                          SilcCommand command,
550                                          SilcClientCommandReply reply,
551                                          void *reply_context,
552                                          SilcUInt32 argc,
553                                          unsigned char **argv,
554                                          SilcUInt32 *argv_lens,
555                                          SilcUInt32 *argv_types)
556 {
557   SilcClientCommandContext cmd;
558
559   if (!conn || !reply)
560     return 0;
561
562   /* Allocate command context */
563   cmd = silc_calloc(1, sizeof(*cmd));
564   if (!cmd)
565     return 0;
566   cmd->conn = conn;
567   cmd->cmd = command;
568
569   /* Send the command */
570   cmd->cmd_ident =
571     silc_client_command_send_arg_array(client, conn, cmd, command, reply,
572                                        reply_context, argc, argv, argv_lens,
573                                        argv_types);
574   if (!cmd->cmd_ident) {
575     silc_client_command_free(cmd);
576     return 0;
577   }
578
579   /*** Wait for command reply */
580   silc_fsm_thread_init(&cmd->thread, &conn->internal->fsm, cmd,
581                        silc_client_command_destructor, NULL, FALSE);
582   silc_fsm_start_sync(&cmd->thread, silc_client_command_reply_wait);
583
584   return cmd->cmd_ident;
585 }
586
587 /* Attach to a command and command identifier to receive command reply. */
588
589 SilcBool silc_client_command_pending(SilcClientConnection conn,
590                                      SilcCommand command,
591                                      SilcUInt16 ident,
592                                      SilcClientCommandReply reply,
593                                      void *context)
594 {
595   SilcClientCommandContext cmd;
596   SilcClientCommandReplyCallback cb;
597
598   if (!conn || !reply)
599     return FALSE;
600
601   SILC_LOG_DEBUG(("Add pending command reply for ident %d", ident));
602
603   silc_mutex_lock(conn->internal->lock);
604
605   /* Find the pending command */
606   silc_list_start(conn->internal->pending_commands);
607   while ((cmd = silc_list_get(conn->internal->pending_commands)))
608     if ((cmd->cmd == command || command == SILC_COMMAND_NONE)
609         && cmd->cmd_ident == ident) {
610
611       /* Add the callback */
612       cb = silc_calloc(1, sizeof(*cb));
613       if (!cb)
614         continue;
615       cb->reply = reply;
616       cb->context = context;
617       silc_list_add(cmd->reply_callbacks, cb);
618     }
619
620   silc_mutex_unlock(conn->internal->lock);
621
622   return TRUE;
623 }
624
625 /******************************** WHOIS *************************************/
626
627 /* Command WHOIS. This command is used to query information about
628    specific user. */
629
630 SILC_FSM_STATE(silc_client_command_whois)
631 {
632   SilcClientCommandContext cmd = fsm_context;
633   SilcClientConnection conn = cmd->conn;
634   SilcClient client = conn->client;
635   SilcBuffer attrs = NULL;
636   unsigned char count[4], *tmp = NULL;
637   SilcBool details = FALSE, nick = FALSE;
638   unsigned char *pubkey = NULL;
639   char *nickname = NULL;
640   int i;
641
642   /* Given without arguments fetches client's own information */
643   if (cmd->argc < 2) {
644     silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1, 4,
645                                 silc_buffer_data(conn->internal->local_idp),
646                                 silc_buffer_len(conn->internal->local_idp));
647
648     /* Notify application */
649     COMMAND(SILC_STATUS_OK);
650
651     /** Wait for command reply */
652     silc_fsm_next(fsm, silc_client_command_reply_wait);
653     return SILC_FSM_CONTINUE;
654   }
655
656   for (i = 1; i < cmd->argc; i++) {
657     if (!strcasecmp(cmd->argv[i], "-details")) {
658       details = TRUE;
659     } else if (!strcasecmp(cmd->argv[i], "-pubkey") && cmd->argc > i + 1) {
660       pubkey = cmd->argv[i + 1];
661       i++;
662     } else {
663       /* We assume that the first parameter is the nickname, if it isn't
664          -details or -pubkey. The last parameter should always be the count */
665       if (i == 1) {
666         nick = TRUE;
667       } else if (i == cmd->argc - 1) {
668         int c = atoi(cmd->argv[i]);
669         SILC_PUT32_MSB(c, count);
670         tmp = count;
671       }
672     }
673   }
674
675   if (details) {
676     /* If pubkey is set, add all attributes to the attrs buffer, except
677        public key */
678     if (pubkey) {
679       attrs = silc_client_attributes_request(SILC_ATTRIBUTE_USER_INFO,
680                                              SILC_ATTRIBUTE_SERVICE,
681                                              SILC_ATTRIBUTE_STATUS_MOOD,
682                                              SILC_ATTRIBUTE_STATUS_FREETEXT,
683                                              SILC_ATTRIBUTE_STATUS_MESSAGE,
684                                              SILC_ATTRIBUTE_PREFERRED_LANGUAGE,
685                                              SILC_ATTRIBUTE_PREFERRED_CONTACT,
686                                              SILC_ATTRIBUTE_TIMEZONE,
687                                              SILC_ATTRIBUTE_GEOLOCATION,
688                                              SILC_ATTRIBUTE_DEVICE_INFO,
689                                              SILC_ATTRIBUTE_USER_ICON, 0);
690     } else {
691       attrs = silc_client_attributes_request(0);
692     }
693   }
694
695   if (pubkey) {
696     SilcAttributeObjPk obj;
697     SilcPublicKey pk;
698
699     if (!silc_pkcs_load_public_key(pubkey, SILC_PKCS_ANY, &pk)) {
700       SAY(client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
701           "Could not load public key %s, check the filename",
702           pubkey);
703       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
704       goto out;
705     }
706
707     switch (silc_pkcs_get_type(pk)) {
708     case SILC_PKCS_SILC:
709       obj.type = "silc-rsa";
710       break;
711     case SILC_PKCS_SSH2:
712       obj.type = "ssh-rsa";
713       break;
714     case SILC_PKCS_X509V3:
715       obj.type = "x509v3-sign-rsa";
716       break;
717     case SILC_PKCS_OPENPGP:
718       obj.type = "pgp-sign-rsa";
719       break;
720     default:
721       goto out;
722       break;
723     }
724     obj.data = silc_pkcs_public_key_encode(NULL, pk, &obj.data_len);
725
726     attrs = silc_attribute_payload_encode(attrs,
727                                           SILC_ATTRIBUTE_USER_PUBLIC_KEY,
728                                           SILC_ATTRIBUTE_FLAG_VALID,
729                                           &obj, sizeof(obj));
730     silc_free(obj.data);
731   }
732
733   if (nick) {
734     if (!silc_client_nickname_parse(client, conn, cmd->argv[1], &nickname))
735       goto out;
736   }
737
738   /* Send command */
739   silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
740                               3, 1, nick ? nickname : NULL,
741                               nick ? strlen(nickname) : 0,
742                               2, tmp ? tmp : NULL, tmp ? 4 : 0,
743                               3, silc_buffer_datalen(attrs));
744   silc_free(nickname);
745
746   /* Notify application */
747   COMMAND(SILC_STATUS_OK);
748
749   /** Wait for command reply */
750   silc_fsm_next(fsm, silc_client_command_reply_wait);
751   return SILC_FSM_CONTINUE;
752
753  out:
754   return SILC_FSM_FINISH;
755 }
756
757 /******************************** WHOWAS ************************************/
758
759 /* Command WHOWAS. This command is used to query history information about
760    specific user that used to exist in the network. */
761
762 SILC_FSM_STATE(silc_client_command_whowas)
763 {
764   SilcClientCommandContext cmd = fsm_context;
765   SilcClientConnection conn = cmd->conn;
766   unsigned char count[4];
767   int c;
768
769   if (cmd->argc < 2 || cmd->argc > 3) {
770     SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
771         "Usage: /WHOWAS <nickname>[@<server>] [<count>]");
772     COMMAND_ERROR((cmd->argc < 2 ? SILC_STATUS_ERR_NOT_ENOUGH_PARAMS :
773                    SILC_STATUS_ERR_TOO_MANY_PARAMS));
774     return SILC_FSM_FINISH;
775   }
776
777   if (cmd->argc == 2) {
778     silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
779                                 1, 1, cmd->argv[1], cmd->argv_lens[1]);
780   } else {
781     c = atoi(cmd->argv[2]);
782     SILC_PUT32_MSB(c, count);
783     silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
784                                 2, 1, cmd->argv[1], cmd->argv_lens[1],
785                                 2, count, sizeof(count));
786   }
787
788   /* Notify application */
789   COMMAND(SILC_STATUS_OK);
790
791   /** Wait for command reply */
792   silc_fsm_next(fsm, silc_client_command_reply_wait);
793   return SILC_FSM_CONTINUE;
794 }
795
796 /******************************** IDENTIFY **********************************/
797
798 /* Command IDENTIFY. This command is used to query information about
799    specific user, especially ID's. */
800
801 SILC_FSM_STATE(silc_client_command_identify)
802 {
803   SilcClientCommandContext cmd = fsm_context;
804   SilcClientConnection conn = cmd->conn;
805   unsigned char count[4];
806   int c;
807
808   if (cmd->argc < 2 || cmd->argc > 3)
809     return SILC_FSM_FINISH;
810
811   if (cmd->argc == 2) {
812     silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
813                                 1, 1, cmd->argv[1], cmd->argv_lens[1]);
814   } else {
815     c = atoi(cmd->argv[2]);
816     SILC_PUT32_MSB(c, count);
817     silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
818                                 2, 1, cmd->argv[1], cmd->argv_lens[1],
819                                 4, count, sizeof(count));
820   }
821
822   /** Wait for command reply */
823   silc_fsm_next(fsm, silc_client_command_reply_wait);
824   return SILC_FSM_CONTINUE;
825 }
826
827 /********************************** NICK ************************************/
828
829 /* Command NICK. Shows current nickname/sets new nickname on current
830    window. */
831
832 SILC_FSM_STATE(silc_client_command_nick)
833 {
834   SilcClientCommandContext cmd2, cmd = fsm_context;
835   SilcClientConnection conn = cmd->conn;
836
837   if (cmd->argc < 2) {
838     SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
839         "Usage: /NICK <nickname>");
840     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
841     goto out;
842   }
843
844   if (silc_utf8_strcasecmp(conn->local_entry->nickname, cmd->argv[1]))
845     goto out;
846
847   /* Show current nickname */
848   if (cmd->argc < 2) {
849     if (cmd->conn) {
850       SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
851           "Your nickname is %s on server %s",
852           conn->local_entry->nickname, conn->remote_host);
853     } else {
854       SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
855           "Your nickname is %s", conn->local_entry->nickname);
856     }
857
858     COMMAND(SILC_STATUS_OK);
859     goto out;
860   }
861
862   /* If JOIN command is active, wait for it to finish before sending NICK.
863      To avoid problems locally with changing IDs while joining, we do this. */
864   silc_mutex_lock(conn->internal->lock);
865   silc_list_start(conn->internal->pending_commands);
866   while ((cmd2 = silc_list_get(conn->internal->pending_commands))) {
867     if (cmd2->cmd == SILC_COMMAND_JOIN) {
868       silc_mutex_unlock(conn->internal->lock);
869       silc_fsm_next_later(fsm, silc_client_command_nick, 0, 300000);
870       return SILC_FSM_WAIT;
871     }
872   }
873   silc_mutex_unlock(conn->internal->lock);
874
875   if (cmd->argv_lens[1] > 128)
876     cmd->argv_lens[1] = 128;
877
878   /* Send the NICK command */
879   silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
880                               1, 1, cmd->argv[1], cmd->argv_lens[1]);
881
882   /* Notify application */
883   COMMAND(SILC_STATUS_OK);
884
885   /** Wait for command reply */
886   silc_fsm_next(fsm, silc_client_command_reply_wait);
887   return SILC_FSM_CONTINUE;
888
889  out:
890   return SILC_FSM_FINISH;
891 }
892
893 /********************************** LIST ************************************/
894
895 /* Command LIST. Lists channels on the current server. */
896
897 SILC_FSM_STATE(silc_client_command_list)
898 {
899   SilcClientCommandContext cmd = fsm_context;
900   SilcClientConnection conn = cmd->conn;
901   SilcClient client = conn->client;
902   SilcChannelEntry channel = NULL;
903   SilcBuffer idp = NULL;
904
905   if (cmd->argc == 2) {
906     /* Get the Channel ID of the channel */
907     channel = silc_client_get_channel(conn->client, cmd->conn, cmd->argv[1]);
908     if (channel)
909       idp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
910   }
911
912   if (!idp)
913     silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 0);
914   else
915     silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL,
916                                 1, 1, silc_buffer_datalen(idp));
917
918   silc_buffer_free(idp);
919   silc_client_unref_channel(client, conn, channel);
920
921   /* Notify application */
922   COMMAND(SILC_STATUS_OK);
923
924   /** Wait for command reply */
925   silc_fsm_next(fsm, silc_client_command_reply_wait);
926   return SILC_FSM_CONTINUE;
927 }
928
929 /********************************** TOPIC ***********************************/
930
931 /* Command TOPIC. Sets/shows topic on a channel. */
932
933 SILC_FSM_STATE(silc_client_command_topic)
934 {
935   SilcClientCommandContext cmd = fsm_context;
936   SilcClientConnection conn = cmd->conn;
937   SilcClient client = conn->client;
938   SilcChannelEntry channel;
939   SilcBuffer idp;
940   char *name, tmp[512];
941
942   if (cmd->argc < 2 || cmd->argc > 3) {
943     SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
944         "Usage: /TOPIC <channel> [<topic>]");
945     COMMAND_ERROR((cmd->argc < 2 ? SILC_STATUS_ERR_NOT_ENOUGH_PARAMS :
946                    SILC_STATUS_ERR_TOO_MANY_PARAMS));
947     goto out;
948   }
949
950   if (cmd->argv[1][0] == '*') {
951     if (!conn->current_channel) {
952       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
953       goto out;
954     }
955
956     if (client->internal->params->full_channel_names)
957       silc_snprintf(tmp, sizeof(tmp), conn->current_channel->channel_name);
958     else
959       silc_snprintf(tmp, sizeof(tmp), "%s%s%s",
960                     conn->current_channel->channel_name,
961                     conn->current_channel->server[0] ? "@" : "",
962                     conn->current_channel->server);
963     name = tmp;
964   } else {
965     name = cmd->argv[1];
966   }
967
968   if (!conn->current_channel) {
969     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
970     goto out;
971   }
972
973   /* Get the Channel ID of the channel */
974   channel = silc_client_get_channel(conn->client, conn, name);
975   if (!channel) {
976     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
977     goto out;
978   }
979
980   idp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
981
982   /* Send TOPIC command to the server */
983   if (cmd->argc > 2)
984     silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
985                                 1, silc_buffer_datalen(idp),
986                                 2, cmd->argv[2], strlen(cmd->argv[2]));
987   else
988     silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
989                                 1, silc_buffer_datalen(idp));
990
991   silc_buffer_free(idp);
992   silc_client_unref_channel(client, conn, channel);
993
994   /* Notify application */
995   COMMAND(SILC_STATUS_OK);
996
997   /** Wait for command reply */
998   silc_fsm_next(fsm, silc_client_command_reply_wait);
999   return SILC_FSM_CONTINUE;
1000
1001  out:
1002   return SILC_FSM_FINISH;
1003 }
1004
1005 /********************************* INVITE ***********************************/
1006
1007 /* Command INVITE. Invites specific client to join a channel. This is
1008    also used to mange the invite list of the channel. */
1009
1010 SILC_FSM_STATE(silc_client_command_invite)
1011 {
1012   SilcClientCommandContext cmd = fsm_context;
1013   SilcClientConnection conn = cmd->conn;
1014   SilcClient client = conn->client;
1015   SilcClientEntry client_entry = NULL;
1016   SilcChannelEntry channel = NULL;
1017   SilcBuffer clidp, chidp, args = NULL;
1018   SilcPublicKey pubkey = NULL;
1019   SilcDList clients = NULL;
1020   char *nickname = NULL, *name;
1021   char *invite = NULL;
1022   unsigned char action[1];
1023
1024   if (cmd->argc < 2) {
1025     SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1026         "Usage: /INVITE <channel> [<nickname>[@server>]"
1027         "[+|-[<nickname>[@<server>[!<username>[@hostname>]]]]]");
1028     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1029     goto out;
1030   }
1031
1032   if (cmd->argv[1][0] == '*') {
1033     if (!conn->current_channel) {
1034       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1035       goto out;
1036     }
1037
1038     channel = conn->current_channel;
1039     silc_client_ref_channel(client, conn, channel);
1040   } else {
1041     name = cmd->argv[1];
1042
1043     channel = silc_client_get_channel(conn->client, conn, name);
1044     if (!channel) {
1045       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1046       goto out;
1047     }
1048   }
1049
1050   /* Parse the typed nickname. */
1051   if (cmd->argc == 3) {
1052     if (cmd->argv[2][0] != '+' && cmd->argv[2][0] != '-') {
1053       if (!silc_client_nickname_parse(client, conn, cmd->argv[2], &nickname)) {
1054         silc_client_unref_channel(client, conn, channel);
1055         goto out;
1056       }
1057
1058       /* Find client entry */
1059       clients = silc_client_get_clients_local(client, conn, cmd->argv[2],
1060                                               FALSE);
1061       if (!clients)
1062         /* Resolve client information */
1063         SILC_FSM_CALL(silc_client_get_clients(
1064                                       client, conn, nickname, NULL,
1065                                       silc_client_command_resolve_continue,
1066                                       cmd));
1067
1068       client_entry = silc_dlist_get(clients);
1069     } else {
1070       if (cmd->argv[2][0] == '+')
1071         action[0] = 0x00;
1072       else
1073         action[0] = 0x01;
1074
1075       /* Check if it is public key file to be added to invite list */
1076       silc_pkcs_load_public_key(cmd->argv[2] + 1, SILC_PKCS_ANY, &pubkey);
1077       invite = cmd->argv[2];
1078       if (!pubkey)
1079         invite++;
1080     }
1081   }
1082
1083   if (invite) {
1084     args = silc_buffer_alloc_size(2);
1085     silc_buffer_format(args,
1086                        SILC_STR_UI_SHORT(1),
1087                        SILC_STR_END);
1088     if (pubkey) {
1089       chidp = silc_public_key_payload_encode(NULL, pubkey);
1090       args = silc_argument_payload_encode_one(args, silc_buffer_data(chidp),
1091                                               silc_buffer_len(chidp), 2);
1092       silc_buffer_free(chidp);
1093       silc_pkcs_public_key_free(pubkey);
1094     } else {
1095       args = silc_argument_payload_encode_one(args, invite, strlen(invite), 1);
1096     }
1097   }
1098
1099   /* Send the command */
1100   chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
1101   if (client_entry) {
1102     clidp = silc_id_payload_encode(&client_entry->id, SILC_ID_CLIENT);
1103     silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 4,
1104                                 1, silc_buffer_datalen(chidp),
1105                                 2, silc_buffer_datalen(clidp),
1106                                 3, args ? action : NULL, args ? 1 : 0,
1107                                 4, silc_buffer_datalen(args));
1108     silc_buffer_free(clidp);
1109   } else {
1110     silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 3,
1111                                 1, silc_buffer_datalen(chidp),
1112                                 3, args ? action : NULL, args ? 1 : 0,
1113                                 4, silc_buffer_datalen(args));
1114   }
1115
1116   silc_buffer_free(chidp);
1117   silc_buffer_free(args);
1118   silc_free(nickname);
1119   silc_client_list_free(client, conn, clients);
1120   silc_client_unref_channel(client, conn, channel);
1121
1122   /* Notify application */
1123   COMMAND(SILC_STATUS_OK);
1124
1125   /** Wait for command reply */
1126   silc_fsm_next(fsm, silc_client_command_reply_wait);
1127   return SILC_FSM_CONTINUE;
1128
1129  out:
1130   silc_free(nickname);
1131   return SILC_FSM_FINISH;
1132 }
1133
1134 /********************************** QUIT ************************************/
1135
1136 /* Close the connection */
1137
1138 SILC_FSM_STATE(silc_client_command_quit_final)
1139 {
1140   SilcClientCommandContext cmd = fsm_context;
1141   SilcClientConnection conn = cmd->conn;
1142
1143   SILC_LOG_DEBUG(("Quitting"));
1144
1145   /* Notify application */
1146   COMMAND(SILC_STATUS_OK);
1147
1148   /* Signal to close connection */
1149   conn->internal->status = SILC_CLIENT_CONN_DISCONNECTED;
1150   if (!conn->internal->disconnected) {
1151     conn->internal->disconnected = TRUE;
1152     SILC_FSM_EVENT_SIGNAL(&conn->internal->wait_event);
1153   }
1154
1155   return SILC_FSM_FINISH;
1156 }
1157
1158 /* Command QUIT. Closes connection with current server. */
1159
1160 SILC_FSM_STATE(silc_client_command_quit)
1161 {
1162   SilcClientCommandContext cmd = fsm_context;
1163   SilcClientConnection conn = cmd->conn;
1164
1165   if (cmd->argc > 1)
1166     silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1167                                 1, cmd->argv[1], cmd->argv_lens[1]);
1168   else
1169     silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 0);
1170
1171   /* Sleep for a while */
1172   sleep(1);
1173
1174   /* We close the connection with a little timeout */
1175   silc_fsm_next_later(fsm, silc_client_command_quit_final, 2, 0);
1176   return SILC_FSM_WAIT;
1177 }
1178
1179 /********************************** KILL ************************************/
1180
1181 /* Signature callback */
1182
1183 static void silc_client_command_kill_signed(const SilcBuffer buffer,
1184                                             void *context)
1185 {
1186   SilcClientCommandContext cmd = context;
1187
1188   if (!buffer) {
1189     silc_fsm_finish(&cmd->thread);
1190     return;
1191   }
1192
1193   silc_fsm_set_state_context(&cmd->thread, buffer);
1194   SILC_FSM_CALL_CONTINUE_SYNC(&cmd->thread);
1195 }
1196
1197 /* Send KILL command */
1198
1199 SILC_FSM_STATE(silc_client_command_kill_send)
1200 {
1201   SilcClientCommandContext cmd = fsm_context;
1202   SilcClientConnection conn = cmd->conn;
1203   SilcClient client = conn->client;
1204   SilcBuffer idp, auth = state_context;
1205   SilcClientEntry target = cmd->context;
1206   char *comment = NULL;
1207
1208   if (cmd->argc >= 3)
1209     if (strcasecmp(cmd->argv[2], "-pubkey"))
1210       comment = cmd->argv[2];
1211
1212   /* Send the KILL command to the server */
1213   idp = silc_id_payload_encode(&target->id, SILC_ID_CLIENT);
1214   silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 3,
1215                               1, silc_buffer_datalen(idp),
1216                               2, comment, comment ? strlen(comment) : 0,
1217                               3, silc_buffer_datalen(auth));
1218
1219   silc_buffer_free(idp);
1220   silc_client_unref_client(client, conn, target);
1221
1222   /* Notify application */
1223   COMMAND(SILC_STATUS_OK);
1224
1225   /** Wait for command reply */
1226   silc_fsm_next(fsm, silc_client_command_reply_wait);
1227   return SILC_FSM_CONTINUE;
1228 }
1229
1230 /* Command KILL. Router operator can use this command to remove an client
1231    fromthe SILC Network. */
1232
1233 SILC_FSM_STATE(silc_client_command_kill)
1234 {
1235   SilcClientCommandContext cmd = fsm_context;
1236   SilcClientConnection conn = cmd->conn;
1237   SilcClient client = conn->client;
1238   SilcClientEntry target;
1239   SilcDList clients;
1240   char *nickname = NULL;
1241
1242   if (cmd->argc < 2) {
1243     SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1244         "Usage: /KILL <nickname> [<comment>] [-pubkey]");
1245     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1246     return SILC_FSM_FINISH;
1247   }
1248
1249   /* Parse the typed nickname. */
1250   if (!silc_client_nickname_parse(client, conn, cmd->argv[1], &nickname))
1251     return SILC_FSM_FINISH;
1252
1253   /* Get the target client */
1254   clients = silc_client_get_clients_local(client, conn, cmd->argv[1], FALSE);
1255   if (!clients)
1256     /* Resolve client information */
1257     SILC_FSM_CALL(silc_client_get_clients(client, conn, nickname, NULL,
1258                                           silc_client_command_resolve_continue,
1259                                           cmd));
1260
1261   target = silc_dlist_get(clients);
1262   cmd->context = silc_client_ref_client(client, conn, target);
1263
1264   silc_free(nickname);
1265   silc_client_list_free(client, conn, clients);
1266
1267   /** Send KILL */
1268   silc_fsm_next(fsm, silc_client_command_kill_send);
1269
1270   if (cmd->argc >= 3) {
1271     if (!strcasecmp(cmd->argv[2], "-pubkey") ||
1272         (cmd->argc >= 4 && !strcasecmp(cmd->argv[3], "-pubkey"))) {
1273       /* Encode the public key authentication payload */
1274       SILC_FSM_CALL(silc_auth_public_key_auth_generate(
1275                                          conn->public_key,
1276                                          conn->private_key,
1277                                          conn->client->rng,
1278                                          conn->internal->sha1hash,
1279                                          &target->id, SILC_ID_CLIENT,
1280                                          silc_client_command_kill_signed,
1281                                          cmd));
1282       /* NOT REACHED */
1283     }
1284   }
1285
1286   return SILC_FSM_CONTINUE;
1287 }
1288
1289 /********************************** INFO ************************************/
1290
1291 /* Command INFO. Request information about specific server. If specific
1292    server is not provided the current server is used. */
1293
1294 SILC_FSM_STATE(silc_client_command_info)
1295 {
1296   SilcClientCommandContext cmd = fsm_context;
1297   SilcClientConnection conn = cmd->conn;
1298
1299   /* Send the command */
1300   if (cmd->argc == 2)
1301     silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1302                                 1, cmd->argv[1], cmd->argv_lens[1]);
1303   else
1304     silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 0);
1305
1306   /* Notify application */
1307   COMMAND(SILC_STATUS_OK);
1308
1309   /** Wait for command reply */
1310   silc_fsm_next(fsm, silc_client_command_reply_wait);
1311   return SILC_FSM_CONTINUE;
1312 }
1313
1314 /********************************** STATS ***********************************/
1315
1316 /* Command STATS. Shows server and network statistics. */
1317
1318 SILC_FSM_STATE(silc_client_command_stats)
1319 {
1320   SilcClientCommandContext cmd = fsm_context;
1321   SilcClientConnection conn = cmd->conn;
1322
1323   /* Send the command */
1324   silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1325                               1, silc_buffer_datalen(conn->internal->
1326                                                      remote_idp));
1327
1328   /* Notify application */
1329   COMMAND(SILC_STATUS_OK);
1330
1331   /** Wait for command reply */
1332   silc_fsm_next(fsm, silc_client_command_reply_wait);
1333   return SILC_FSM_CONTINUE;
1334 }
1335
1336 /********************************** PING ************************************/
1337
1338 /* Command PING. Sends ping to server. */
1339
1340 SILC_FSM_STATE(silc_client_command_ping)
1341 {
1342   SilcClientCommandContext cmd = fsm_context;
1343   SilcClientConnection conn = cmd->conn;
1344
1345   if (cmd->argc < 2) {
1346     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1347     return SILC_FSM_FINISH;
1348   }
1349
1350   /* Send the command */
1351   silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1352                               1, silc_buffer_datalen(conn->internal->
1353                                                      remote_idp));
1354
1355   /* Save ping time */
1356   cmd->context = SILC_64_TO_PTR(silc_time());
1357
1358   /* Notify application */
1359   COMMAND(SILC_STATUS_OK);
1360
1361   /** Wait for command reply */
1362   silc_fsm_next(fsm, silc_client_command_reply_wait);
1363   return SILC_FSM_CONTINUE;
1364 }
1365
1366 /********************************** JOIN ************************************/
1367
1368 typedef struct {
1369   int type;
1370   SilcBuffer auth;
1371   SilcBuffer cauth;
1372 } *SilcClientJoinContext;
1373
1374 /* Signature callback */
1375
1376 static void silc_client_command_join_signed(const SilcBuffer buffer,
1377                                             void *context)
1378 {
1379   SilcClientCommandContext cmd = context;
1380   SilcClientJoinContext j = cmd->context;
1381
1382   if (!buffer) {
1383     silc_fsm_finish(&cmd->thread);
1384     return;
1385   }
1386
1387   if (!j->type)
1388     j->auth = silc_buffer_copy(buffer);
1389   else
1390     j->cauth = silc_buffer_copy(buffer);
1391
1392   SILC_FSM_CALL_CONTINUE(&cmd->thread);
1393 }
1394
1395 /* Command JOIN. Joins to a channel. */
1396
1397 SILC_FSM_STATE(silc_client_command_join)
1398 {
1399   SilcClientCommandContext cmd2, cmd = fsm_context;
1400   SilcClientConnection conn = cmd->conn;
1401   SilcClient client = conn->client;
1402   SilcChannelEntry channel = NULL;
1403   SilcClientJoinContext j = cmd->context;
1404   SilcBuffer auth = NULL, cauth = NULL;
1405   char *name, *passphrase = NULL, *pu8, *cipher = NULL, *hmac = NULL;
1406   int i, passphrase_len = 0;
1407
1408   if (cmd->argc < 2) {
1409     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1410     goto out;
1411   }
1412
1413   /* See if we have joined to the requested channel already */
1414   channel = silc_client_get_channel(conn->client, conn, cmd->argv[1]);
1415   if (channel && silc_client_on_channel(channel, conn->local_entry))
1416     goto out;
1417
1418   /* If NICK command is active, wait for it to finish before sending JOIN.
1419      To avoid problems locally with changing IDs while joining, we do this. */
1420   silc_mutex_lock(conn->internal->lock);
1421   silc_list_start(conn->internal->pending_commands);
1422   while ((cmd2 = silc_list_get(conn->internal->pending_commands))) {
1423     if (cmd2->cmd == SILC_COMMAND_NICK) {
1424       silc_mutex_unlock(conn->internal->lock);
1425       silc_fsm_next_later(fsm, silc_client_command_join, 0, 300000);
1426       return SILC_FSM_WAIT;
1427     }
1428   }
1429   silc_mutex_unlock(conn->internal->lock);
1430
1431   if (cmd->argv_lens[1] > 256)
1432     cmd->argv_lens[1] = 256;
1433
1434   name = cmd->argv[1];
1435
1436   for (i = 2; i < cmd->argc; i++) {
1437     if (!strcasecmp(cmd->argv[i], "-cipher") && cmd->argc > i + 1) {
1438       cipher = cmd->argv[++i];
1439     } else if (!strcasecmp(cmd->argv[i], "-hmac") && cmd->argc > i + 1) {
1440       hmac = cmd->argv[++i];
1441     } else if (!strcasecmp(cmd->argv[i], "-founder")) {
1442       if (!j || !j->auth) {
1443         if (!j) {
1444           j = silc_calloc(1, sizeof(*j));
1445           if (!j)
1446             goto out;
1447           cmd->context = j;
1448         }
1449         j->type = 0;
1450         silc_free(passphrase);
1451         silc_client_unref_channel(client, conn, channel);
1452         SILC_FSM_CALL(silc_auth_public_key_auth_generate(
1453                                            conn->public_key,
1454                                            conn->private_key,
1455                                            conn->client->rng,
1456                                            conn->internal->sha1hash,
1457                                            conn->local_id,
1458                                            SILC_ID_CLIENT,
1459                                            silc_client_command_join_signed,
1460                                            cmd));
1461         /* NOT REACHED */
1462       }
1463     } else if (!strcasecmp(cmd->argv[i], "-auth")) {
1464       SilcPublicKey pubkey = conn->public_key;
1465       SilcPrivateKey privkey = conn->private_key;
1466       unsigned char *pk, pkhash[SILC_HASH_MAXLEN], pubdata[128];
1467       SilcUInt32 pk_len;
1468
1469       if (!j || !j->cauth) {
1470         if (!j) {
1471           j = silc_calloc(1, sizeof(*j));
1472           if (!j)
1473             goto out;
1474           cmd->context = j;
1475         }
1476
1477         if (cmd->argc >= i + 3) {
1478           char *pass = "";
1479           if (cmd->argc >= i + 4) {
1480             pass = cmd->argv[i + 3];
1481             i++;
1482           }
1483           if (!silc_load_key_pair(cmd->argv[i + 1], cmd->argv[i + 2], pass,
1484                                   &pubkey, &privkey)) {
1485             SAY(conn->client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
1486                 "Could not load key pair, check your arguments");
1487             COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1488             goto out;
1489           }
1490           i += 2;
1491         }
1492
1493         j->type = 1;
1494         pk = silc_pkcs_public_key_encode(NULL, pubkey, &pk_len);
1495         silc_hash_make(conn->internal->sha1hash, pk, pk_len, pkhash);
1496         silc_free(pk);
1497         silc_rng_get_rn_data(conn->client->rng, sizeof(pubdata), pubdata,
1498                              sizeof(pubdata));
1499         memcpy(pubdata, pkhash, 20);
1500         silc_free(passphrase);
1501         silc_client_unref_channel(client, conn, channel);
1502         SILC_FSM_CALL(silc_auth_public_key_auth_generate_wpub(
1503                                            pubkey, privkey,
1504                                            pubdata, sizeof(pubdata),
1505                                            conn->internal->sha1hash,
1506                                            client->rng,
1507                                            conn->local_id,
1508                                            SILC_ID_CLIENT,
1509                                            silc_client_command_join_signed,
1510                                            cmd));
1511         /* NOT REACHED */
1512       }
1513     } else {
1514       /* Passphrases must be UTF-8 encoded, so encode if it is not */
1515       if (!silc_utf8_valid(cmd->argv[i], cmd->argv_lens[i])) {
1516         passphrase_len = silc_utf8_encoded_len(cmd->argv[i],
1517                                                cmd->argv_lens[i], 0);
1518         pu8 = silc_calloc(passphrase_len, sizeof(*pu8));
1519         passphrase_len = silc_utf8_encode(cmd->argv[i], cmd->argv_lens[i],
1520                                           0, pu8, passphrase_len);
1521         passphrase = pu8;
1522       } else {
1523         passphrase = strdup(cmd->argv[i]);
1524         passphrase_len = cmd->argv_lens[i];
1525       }
1526     }
1527   }
1528
1529   /* Send JOIN command to the server */
1530   silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 7,
1531                               1, name, strlen(name),
1532                               2, silc_buffer_datalen(conn->internal->
1533                                                      local_idp),
1534                               3, passphrase, passphrase_len,
1535                               4, cipher, cipher ? strlen(cipher) : 0,
1536                               5, hmac, hmac ? strlen(hmac) : 0,
1537                               6, silc_buffer_datalen(auth),
1538                               7, silc_buffer_datalen(cauth));
1539
1540   if (passphrase)
1541     memset(passphrase, 0, strlen(passphrase));
1542   silc_free(passphrase);
1543   silc_client_unref_channel(client, conn, channel);
1544   if (j) {
1545     silc_buffer_free(j->auth);
1546     silc_buffer_free(j->cauth);
1547     silc_free(j);
1548   }
1549
1550   /* Notify application */
1551   COMMAND(SILC_STATUS_OK);
1552
1553   /** Wait for command reply */
1554   silc_fsm_next(fsm, silc_client_command_reply_wait);
1555   return SILC_FSM_CONTINUE;
1556
1557  out:
1558   silc_client_unref_channel(client, conn, channel);
1559   return SILC_FSM_FINISH;
1560 }
1561
1562 /********************************** MOTD ************************************/
1563
1564 /* MOTD command. Requests motd from server. */
1565
1566 SILC_FSM_STATE(silc_client_command_motd)
1567 {
1568   SilcClientCommandContext cmd = fsm_context;
1569   SilcClientConnection conn = cmd->conn;
1570
1571   if (cmd->argc < 1 || cmd->argc > 2) {
1572     SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1573         "Usage: /MOTD [<server>]");
1574     COMMAND_ERROR((cmd->argc < 1 ? SILC_STATUS_ERR_NOT_ENOUGH_PARAMS :
1575                    SILC_STATUS_ERR_TOO_MANY_PARAMS));
1576     return SILC_FSM_FINISH;
1577   }
1578
1579   /* Send the command */
1580   if (cmd->argc == 1)
1581     silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1582                                 1, conn->remote_host,
1583                                 strlen(conn->remote_host));
1584   else
1585     silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
1586                                 1, cmd->argv[1], cmd->argv_lens[1]);
1587
1588   /* Notify application */
1589   COMMAND(SILC_STATUS_OK);
1590
1591   /** Wait for command reply */
1592   silc_fsm_next(fsm, silc_client_command_reply_wait);
1593   return SILC_FSM_CONTINUE;
1594 }
1595
1596 /********************************** UMODE ***********************************/
1597
1598 /* UMODE. Set/unset user mode in SILC. This is used mainly to unset the
1599    modes as client cannot set itself server/router operator privileges. */
1600
1601 SILC_FSM_STATE(silc_client_command_umode)
1602 {
1603   SilcClientCommandContext cmd = fsm_context;
1604   SilcClientConnection conn = cmd->conn;
1605   unsigned char *cp, modebuf[4];
1606   SilcUInt32 mode, add, len;
1607   int i;
1608
1609   if (cmd->argc < 2) {
1610     SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1611         "Usage: /UMODE +|-<modes>");
1612     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1613     return SILC_FSM_FINISH;
1614   }
1615
1616   mode = conn->local_entry->mode;
1617
1618   /* Are we adding or removing mode */
1619   if (cmd->argv[1][0] == '-')
1620     add = FALSE;
1621   else
1622     add = TRUE;
1623
1624   /* Parse mode */
1625   cp = cmd->argv[1] + 1;
1626   len = strlen(cp);
1627   for (i = 0; i < len; i++) {
1628     switch(cp[i]) {
1629     case 'a':
1630       if (add) {
1631         mode = 0;
1632         mode |= SILC_UMODE_SERVER_OPERATOR;
1633         mode |= SILC_UMODE_ROUTER_OPERATOR;
1634         mode |= SILC_UMODE_GONE;
1635         mode |= SILC_UMODE_INDISPOSED;
1636         mode |= SILC_UMODE_BUSY;
1637         mode |= SILC_UMODE_PAGE;
1638         mode |= SILC_UMODE_HYPER;
1639         mode |= SILC_UMODE_ROBOT;
1640         mode |= SILC_UMODE_BLOCK_PRIVMSG;
1641         mode |= SILC_UMODE_REJECT_WATCHING;
1642       } else {
1643         mode = SILC_UMODE_NONE;
1644       }
1645       break;
1646     case 's':
1647       if (add)
1648         mode |= SILC_UMODE_SERVER_OPERATOR;
1649       else
1650         mode &= ~SILC_UMODE_SERVER_OPERATOR;
1651       break;
1652     case 'r':
1653       if (add)
1654         mode |= SILC_UMODE_ROUTER_OPERATOR;
1655       else
1656         mode &= ~SILC_UMODE_ROUTER_OPERATOR;
1657       break;
1658     case 'g':
1659       if (add)
1660         mode |= SILC_UMODE_GONE;
1661       else
1662         mode &= ~SILC_UMODE_GONE;
1663       break;
1664     case 'i':
1665       if (add)
1666         mode |= SILC_UMODE_INDISPOSED;
1667       else
1668         mode &= ~SILC_UMODE_INDISPOSED;
1669       break;
1670     case 'b':
1671       if (add)
1672         mode |= SILC_UMODE_BUSY;
1673       else
1674         mode &= ~SILC_UMODE_BUSY;
1675       break;
1676     case 'p':
1677       if (add)
1678         mode |= SILC_UMODE_PAGE;
1679       else
1680         mode &= ~SILC_UMODE_PAGE;
1681       break;
1682     case 'h':
1683       if (add)
1684         mode |= SILC_UMODE_HYPER;
1685       else
1686         mode &= ~SILC_UMODE_HYPER;
1687       break;
1688     case 't':
1689       if (add)
1690         mode |= SILC_UMODE_ROBOT;
1691       else
1692         mode &= ~SILC_UMODE_ROBOT;
1693       break;
1694     case 'P':
1695       if (add)
1696         mode |= SILC_UMODE_BLOCK_PRIVMSG;
1697       else
1698         mode &= ~SILC_UMODE_BLOCK_PRIVMSG;
1699       break;
1700     case 'w':
1701       if (add)
1702         mode |= SILC_UMODE_REJECT_WATCHING;
1703       else
1704         mode &= ~SILC_UMODE_REJECT_WATCHING;
1705       break;
1706     case 'I':
1707       if (add)
1708         mode |= SILC_UMODE_BLOCK_INVITE;
1709       else
1710         mode &= ~SILC_UMODE_BLOCK_INVITE;
1711       break;
1712     default:
1713       COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE);
1714       return SILC_FSM_FINISH;
1715       break;
1716     }
1717   }
1718
1719   SILC_PUT32_MSB(mode, modebuf);
1720
1721   /* Send the command */
1722   silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
1723                               1, silc_buffer_datalen(conn->internal->
1724                                                      local_idp),
1725                               2, modebuf, sizeof(modebuf));
1726
1727   /* Notify application */
1728   COMMAND(SILC_STATUS_OK);
1729
1730   /** Wait for command reply */
1731   silc_fsm_next(fsm, silc_client_command_reply_wait);
1732   return SILC_FSM_CONTINUE;
1733 }
1734
1735 /********************************** CMODE ***********************************/
1736
1737 /* Signature callback */
1738
1739 static void silc_client_command_cmode_signed(const SilcBuffer buffer,
1740                                              void *context)
1741 {
1742   SilcClientCommandContext cmd = context;
1743
1744   if (!buffer) {
1745     silc_fsm_finish(&cmd->thread);
1746     return;
1747   }
1748
1749   silc_fsm_set_state_context(&cmd->thread, buffer);
1750   SILC_FSM_CALL_CONTINUE_SYNC(&cmd->thread);
1751 }
1752
1753 /* CMODE command. Sets channel mode. Modes that does not require any arguments
1754    can be set several at once. Those modes that require argument must be set
1755    separately (unless set with modes that does not require arguments). */
1756
1757 SILC_FSM_STATE(silc_client_command_cmode)
1758 {
1759   SilcClientCommandContext cmd = fsm_context;
1760   SilcClientConnection conn = cmd->conn;
1761   SilcClient client = conn->client;
1762   SilcBuffer auth = state_context;
1763   SilcChannelEntry channel = NULL;
1764   SilcBuffer chidp, pk = NULL;
1765   unsigned char *name, *cp, modebuf[4], tmp[4], *arg = NULL;
1766   SilcUInt32 mode, add, type, len, arg_len = 0;
1767   int i;
1768
1769   if (cmd->argc < 3) {
1770     SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1771         "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1772     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1773     goto out;
1774   }
1775
1776   if (cmd->argv[1][0] == '*') {
1777     if (!conn->current_channel) {
1778       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1779       goto out;
1780     }
1781
1782     channel = conn->current_channel;
1783     silc_client_ref_channel(client, conn, channel);
1784   } else {
1785     name = cmd->argv[1];
1786
1787     channel = silc_client_get_channel(conn->client, conn, name);
1788     if (!channel) {
1789       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
1790       goto out;
1791     }
1792   }
1793
1794   mode = channel->mode;
1795
1796   /* Are we adding or removing mode */
1797   if (cmd->argv[2][0] == '-')
1798     add = FALSE;
1799   else
1800     add = TRUE;
1801
1802   /* Argument type to be sent to server */
1803   type = 0;
1804
1805   /* Parse mode */
1806   cp = cmd->argv[2] + 1;
1807   len = strlen(cp);
1808   for (i = 0; i < len; i++) {
1809     switch(cp[i]) {
1810     case 'p':
1811       if (add)
1812         mode |= SILC_CHANNEL_MODE_PRIVATE;
1813       else
1814         mode &= ~SILC_CHANNEL_MODE_PRIVATE;
1815       break;
1816     case 's':
1817       if (add)
1818         mode |= SILC_CHANNEL_MODE_SECRET;
1819       else
1820         mode &= ~SILC_CHANNEL_MODE_SECRET;
1821       break;
1822     case 'k':
1823       if (add)
1824         mode |= SILC_CHANNEL_MODE_PRIVKEY;
1825       else
1826         mode &= ~SILC_CHANNEL_MODE_PRIVKEY;
1827       break;
1828     case 'i':
1829       if (add)
1830         mode |= SILC_CHANNEL_MODE_INVITE;
1831       else
1832         mode &= ~SILC_CHANNEL_MODE_INVITE;
1833       break;
1834     case 't':
1835       if (add)
1836         mode |= SILC_CHANNEL_MODE_TOPIC;
1837       else
1838         mode &= ~SILC_CHANNEL_MODE_TOPIC;
1839       break;
1840     case 'm':
1841       if (add)
1842         mode |= SILC_CHANNEL_MODE_SILENCE_USERS;
1843       else
1844         mode &= ~SILC_CHANNEL_MODE_SILENCE_USERS;
1845       break;
1846     case 'M':
1847       if (add)
1848         mode |= SILC_CHANNEL_MODE_SILENCE_OPERS;
1849       else
1850         mode &= ~SILC_CHANNEL_MODE_SILENCE_OPERS;
1851       break;
1852     case 'l':
1853       if (add) {
1854         int ll;
1855         mode |= SILC_CHANNEL_MODE_ULIMIT;
1856         type = 3;
1857         if (cmd->argc < 4) {
1858           SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1859               "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1860           COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1861           goto out;
1862         }
1863         ll = atoi(cmd->argv[3]);
1864         SILC_PUT32_MSB(ll, tmp);
1865         arg = tmp;
1866         arg_len = 4;
1867       } else {
1868         mode &= ~SILC_CHANNEL_MODE_ULIMIT;
1869       }
1870       break;
1871     case 'a':
1872       if (add) {
1873         mode |= SILC_CHANNEL_MODE_PASSPHRASE;
1874         type = 4;
1875         if (cmd->argc < 4) {
1876           SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1877               "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1878           COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1879           goto out;
1880         }
1881         arg = cmd->argv[3];
1882         arg_len = cmd->argv_lens[3];
1883       } else {
1884         mode &= ~SILC_CHANNEL_MODE_PASSPHRASE;
1885       }
1886       break;
1887     case 'c':
1888       if (add) {
1889         mode |= SILC_CHANNEL_MODE_CIPHER;
1890         type = 5;
1891         if (cmd->argc < 4) {
1892           SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1893               "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1894           COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1895           goto out;
1896         }
1897         arg = cmd->argv[3];
1898         arg_len = cmd->argv_lens[3];
1899       } else {
1900         mode &= ~SILC_CHANNEL_MODE_CIPHER;
1901       }
1902       break;
1903     case 'h':
1904       if (add) {
1905         mode |= SILC_CHANNEL_MODE_HMAC;
1906         type = 6;
1907         if (cmd->argc < 4) {
1908           SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
1909               "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1910           COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1911           goto out;
1912         }
1913         arg = cmd->argv[3];
1914         arg_len = cmd->argv_lens[3];
1915       } else {
1916         mode &= ~SILC_CHANNEL_MODE_HMAC;
1917       }
1918       break;
1919     case 'f':
1920       if (add) {
1921         if (!auth) {
1922           SilcPublicKey pubkey = conn->public_key;
1923           SilcPrivateKey privkey = conn->private_key;
1924
1925           mode |= SILC_CHANNEL_MODE_FOUNDER_AUTH;
1926           type = 7;
1927
1928           if (cmd->argc >= 5) {
1929             char *pass = "";
1930             if (cmd->argc >= 6)
1931               pass = cmd->argv[5];
1932             if (!silc_load_key_pair(cmd->argv[3], cmd->argv[4], pass,
1933                                     &pubkey, &privkey)) {
1934               SAY(client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
1935                   "Could not load key pair, check your arguments");
1936               COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
1937               goto out;
1938             }
1939           }
1940
1941           pk = silc_public_key_payload_encode(NULL, pubkey);
1942           silc_client_unref_channel(client, conn, channel);
1943           SILC_FSM_CALL(silc_auth_public_key_auth_generate(
1944                                              pubkey, privkey,
1945                                              conn->client->rng,
1946                                              conn->internal->sha1hash,
1947                                              conn->local_id,
1948                                              SILC_ID_CLIENT,
1949                                              silc_client_command_cmode_signed,
1950                                              cmd));
1951           /* NOT REACHED */
1952         }
1953         arg = silc_buffer_data(auth);
1954         arg_len = silc_buffer_len(auth);
1955       } else {
1956         mode &= ~SILC_CHANNEL_MODE_FOUNDER_AUTH;
1957       }
1958       break;
1959     case 'C':
1960       if (add) {
1961         int k;
1962         SilcBool chadd = FALSE;
1963         SilcPublicKey chpk = NULL;
1964
1965         mode |= SILC_CHANNEL_MODE_CHANNEL_AUTH;
1966         type = 9;
1967
1968         if (cmd->argc == 3) {
1969           /* Send empty command to receive the public key list. */
1970           chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
1971           silc_client_command_send_va(conn, cmd, SILC_COMMAND_CMODE,
1972                                       NULL, NULL, 1,
1973                                       1, silc_buffer_datalen(chidp));
1974           silc_buffer_free(chidp);
1975           silc_client_unref_channel(client, conn, channel);
1976
1977           /* Notify application */
1978           COMMAND(SILC_STATUS_OK);
1979
1980           /** Wait for command reply */
1981           silc_fsm_next(fsm, silc_client_command_reply_wait);
1982           return SILC_FSM_CONTINUE;
1983         }
1984
1985         if (cmd->argc >= 4) {
1986           auth = silc_buffer_alloc_size(2);
1987           silc_buffer_format(auth,
1988                              SILC_STR_UI_SHORT(cmd->argc - 3),
1989                              SILC_STR_END);
1990         }
1991
1992         for (k = 3; k < cmd->argc; k++) {
1993           if (cmd->argv[k][0] == '+')
1994             chadd = TRUE;
1995           if (!silc_pkcs_load_public_key(cmd->argv[k] + 1, SILC_PKCS_ANY,
1996                                          &chpk)) {
1997             SAY(conn->client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
1998                 "Could not load public key %s, check the filename",
1999                 cmd->argv[k]);
2000             COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2001             silc_buffer_free(auth);
2002             goto out;
2003           }
2004
2005           if (chpk) {
2006             pk = silc_public_key_payload_encode(NULL, chpk);
2007             auth = silc_argument_payload_encode_one(auth,
2008                                                     silc_buffer_datalen(pk),
2009                                                     chadd ? 0x00 : 0x01);
2010             silc_pkcs_public_key_free(chpk);
2011             silc_buffer_free(pk);
2012             pk = NULL;
2013           }
2014         }
2015
2016         arg = silc_buffer_data(auth);
2017         arg_len = silc_buffer_len(auth);
2018       } else {
2019         mode &= ~SILC_CHANNEL_MODE_CHANNEL_AUTH;
2020       }
2021       break;
2022     default:
2023       COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE);
2024       goto out;
2025       break;
2026     }
2027   }
2028
2029   chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2030   SILC_PUT32_MSB(mode, modebuf);
2031
2032   /* Send the command. We support sending only one mode at once that
2033      requires an argument. */
2034   if (type && arg) {
2035     silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 4,
2036                                 1, silc_buffer_datalen(chidp),
2037                                 2, modebuf, sizeof(modebuf),
2038                                 type, arg, arg_len,
2039                                 8, silc_buffer_datalen(pk));
2040   } else {
2041     silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
2042                                 1, silc_buffer_datalen(chidp),
2043                                 2, modebuf, sizeof(modebuf));
2044   }
2045
2046   silc_buffer_free(chidp);
2047   silc_buffer_free(auth);
2048   silc_buffer_free(pk);
2049   silc_client_unref_channel(client, conn, channel);
2050
2051   /* Notify application */
2052   COMMAND(SILC_STATUS_OK);
2053
2054   /** Wait for command reply */
2055   silc_fsm_next(fsm, silc_client_command_reply_wait);
2056   return SILC_FSM_CONTINUE;
2057
2058  out:
2059   silc_client_unref_channel(client, conn, channel);
2060   return SILC_FSM_FINISH;
2061 }
2062
2063 /********************************* CUMODE ***********************************/
2064
2065 /* Signature callback */
2066
2067 static void silc_client_command_cumode_signed(const SilcBuffer buffer,
2068                                               void *context)
2069 {
2070   SilcClientCommandContext cmd = context;
2071
2072   if (!buffer) {
2073     silc_fsm_finish(&cmd->thread);
2074     return;
2075   }
2076
2077   silc_fsm_set_state_context(&cmd->thread, buffer);
2078   SILC_FSM_CALL_CONTINUE_SYNC(&cmd->thread);
2079 }
2080
2081 /* CUMODE command. Changes client's mode on a channel. */
2082
2083 SILC_FSM_STATE(silc_client_command_cumode)
2084 {
2085   SilcClientCommandContext cmd = fsm_context;
2086   SilcClientConnection conn = cmd->conn;
2087   SilcClient client = conn->client;
2088   SilcBuffer auth = state_context;
2089   SilcChannelEntry channel = NULL;
2090   SilcChannelUser chu;
2091   SilcClientEntry client_entry;
2092   SilcBuffer clidp, chidp;
2093   SilcDList clients = NULL;
2094   unsigned char *name, *cp, modebuf[4];
2095   SilcUInt32 mode = 0, add, len;
2096   char *nickname = NULL;
2097   int i;
2098
2099   if (cmd->argc < 4) {
2100     SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2101         "Usage: /CUMODE <channel> +|-<modes> <nickname>[@<server>]");
2102     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2103     goto out;
2104   }
2105
2106   if (cmd->argv[1][0] == '*') {
2107     if (!conn->current_channel) {
2108       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2109       goto out;
2110     }
2111
2112     channel = conn->current_channel;
2113     silc_client_ref_channel(client, conn, channel);
2114   } else {
2115     name = cmd->argv[1];
2116
2117     channel = silc_client_get_channel(conn->client, conn, name);
2118     if (!channel) {
2119       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2120       goto out;
2121     }
2122   }
2123
2124   /* Parse the typed nickname. */
2125   if (!silc_client_nickname_parse(client, conn, cmd->argv[3], &nickname))
2126     goto out;
2127
2128   /* Find client entry */
2129   clients = silc_client_get_clients_local(client, conn, cmd->argv[3], FALSE);
2130   if (!clients)
2131     /* Resolve client information */
2132     SILC_FSM_CALL(silc_client_get_clients(client, conn, nickname, NULL,
2133                                           silc_client_command_resolve_continue,
2134                                           cmd));
2135
2136   client_entry = silc_dlist_get(clients);
2137
2138   /* Get the current mode */
2139   chu = silc_client_on_channel(channel, client_entry);
2140   if (chu)
2141     mode = chu->mode;
2142
2143   /* Are we adding or removing mode */
2144   if (cmd->argv[2][0] == '-')
2145     add = FALSE;
2146   else
2147     add = TRUE;
2148
2149   /* Parse mode */
2150   cp = cmd->argv[2] + 1;
2151   len = strlen(cp);
2152   for (i = 0; i < len; i++) {
2153     switch(cp[i]) {
2154     case 'a':
2155       if (add) {
2156         mode |= SILC_CHANNEL_UMODE_CHANFO;
2157         mode |= SILC_CHANNEL_UMODE_CHANOP;
2158         mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
2159         mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
2160         mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
2161       } else {
2162         mode = SILC_CHANNEL_UMODE_NONE;
2163       }
2164       break;
2165     case 'f':
2166       if (add) {
2167         if (!auth) {
2168           SilcPublicKey pubkey = conn->public_key;
2169           SilcPrivateKey privkey = conn->private_key;
2170
2171           if (cmd->argc >= 6) {
2172             char *pass = "";
2173             if (cmd->argc >= 7)
2174               pass = cmd->argv[6];
2175             if (!silc_load_key_pair(cmd->argv[4], cmd->argv[5], pass,
2176                                     &pubkey, &privkey)) {
2177               SAY(conn->client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
2178                   "Could not load key pair, check your arguments");
2179               COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2180               goto out;
2181             }
2182           }
2183
2184           silc_free(nickname);
2185           silc_client_list_free(client, conn, clients);
2186           silc_client_unref_channel(client, conn, channel);
2187
2188           SILC_FSM_CALL(silc_auth_public_key_auth_generate(
2189                                              pubkey, privkey,
2190                                              conn->client->rng,
2191                                              conn->internal->sha1hash,
2192                                              conn->local_id,
2193                                              SILC_ID_CLIENT,
2194                                              silc_client_command_cumode_signed,
2195                                              cmd));
2196           /* NOT REACHED */
2197         }
2198
2199         mode |= SILC_CHANNEL_UMODE_CHANFO;
2200       } else {
2201         mode &= ~SILC_CHANNEL_UMODE_CHANFO;
2202       }
2203       break;
2204     case 'o':
2205       if (add)
2206         mode |= SILC_CHANNEL_UMODE_CHANOP;
2207       else
2208         mode &= ~SILC_CHANNEL_UMODE_CHANOP;
2209       break;
2210     case 'b':
2211       if (add)
2212         mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
2213       else
2214         mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
2215       break;
2216     case 'u':
2217       if (add)
2218         mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
2219       else
2220         mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS;
2221       break;
2222     case 'r':
2223       if (add)
2224         mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
2225       else
2226         mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS;
2227       break;
2228     case 'q':
2229       if (add)
2230         mode |= SILC_CHANNEL_UMODE_QUIET;
2231       else
2232         mode &= ~SILC_CHANNEL_UMODE_QUIET;
2233       break;
2234     default:
2235       COMMAND_ERROR(SILC_STATUS_ERR_UNKNOWN_MODE);
2236       goto out;
2237       break;
2238     }
2239   }
2240
2241   chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2242   SILC_PUT32_MSB(mode, modebuf);
2243   clidp = silc_id_payload_encode(&client_entry->id, SILC_ID_CLIENT);
2244
2245   /* Send the command packet. We support sending only one mode at once
2246      that requires an argument. */
2247   silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, auth ? 4 : 3,
2248                               1, silc_buffer_datalen(chidp),
2249                               2, modebuf, 4,
2250                               3, silc_buffer_datalen(clidp),
2251                               4, silc_buffer_datalen(auth));
2252
2253   silc_buffer_free(chidp);
2254   silc_buffer_free(clidp);
2255   if (auth)
2256     silc_buffer_free(auth);
2257   silc_free(nickname);
2258   silc_client_list_free(client, conn, clients);
2259   silc_client_unref_channel(client, conn, channel);
2260
2261   /* Notify application */
2262   COMMAND(SILC_STATUS_OK);
2263
2264   /** Wait for command reply */
2265   silc_fsm_next(fsm, silc_client_command_reply_wait);
2266   return SILC_FSM_CONTINUE;
2267
2268  out:
2269   silc_client_unref_channel(client, conn, channel);
2270   silc_client_list_free(client, conn, clients);
2271   silc_free(nickname);
2272   return SILC_FSM_FINISH;
2273 }
2274
2275 /********************************** KICK ************************************/
2276
2277 /* KICK command. Kicks a client out of channel. */
2278
2279 SILC_FSM_STATE(silc_client_command_kick)
2280 {
2281   SilcClientCommandContext cmd = fsm_context;
2282   SilcClientConnection conn = cmd->conn;
2283   SilcClient client = conn->client;
2284   SilcChannelEntry channel = NULL;
2285   SilcBuffer idp, idp2;
2286   SilcClientEntry target;
2287   SilcDList clients = NULL;
2288   char *name, tmp[512];
2289
2290   if (cmd->argc < 3) {
2291     SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2292         "Usage: /KICK <channel> <nickname> [<comment>]");
2293     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2294     goto out;
2295   }
2296
2297   if (cmd->argv[1][0] == '*') {
2298     if (!conn->current_channel) {
2299       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2300       goto out;
2301     }
2302
2303     if (client->internal->params->full_channel_names)
2304       silc_snprintf(tmp, sizeof(tmp), conn->current_channel->channel_name);
2305     else
2306       silc_snprintf(tmp, sizeof(tmp), "%s%s%s",
2307                     conn->current_channel->channel_name,
2308                     conn->current_channel->server[0] ? "@" : "",
2309                     conn->current_channel->server);
2310     name = tmp;
2311   } else {
2312     name = cmd->argv[1];
2313   }
2314
2315   if (!conn->current_channel) {
2316     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2317     goto out;
2318   }
2319
2320   /* Get the Channel ID of the channel */
2321   channel = silc_client_get_channel(conn->client, conn, name);
2322   if (!channel) {
2323     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2324     goto out;
2325   }
2326
2327   /* Get the target client */
2328   clients = silc_client_get_clients_local(client, conn, cmd->argv[2], FALSE);
2329   if (!clients) {
2330     SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2331         "No such client: %s", cmd->argv[2]);
2332     COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_NICK);
2333     goto out;
2334   }
2335   target = silc_dlist_get(clients);
2336
2337   /* Send KICK command to the server */
2338   idp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2339   idp2 = silc_id_payload_encode(&target->id, SILC_ID_CLIENT);
2340   if (cmd->argc == 3)
2341     silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
2342                                 1, silc_buffer_datalen(idp),
2343                                 2, silc_buffer_datalen(idp2));
2344   else
2345     silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 3,
2346                                 1, silc_buffer_datalen(idp),
2347                                 2, silc_buffer_datalen(idp2),
2348                                 3, cmd->argv[3], strlen(cmd->argv[3]));
2349
2350   silc_buffer_free(idp);
2351   silc_buffer_free(idp2);
2352   silc_client_list_free(client, conn, clients);
2353   silc_client_unref_channel(client, conn, channel);
2354
2355   /* Notify application */
2356   COMMAND(SILC_STATUS_OK);
2357
2358   /** Wait for command reply */
2359   silc_fsm_next(fsm, silc_client_command_reply_wait);
2360   return SILC_FSM_CONTINUE;
2361
2362  out:
2363   silc_client_unref_channel(client, conn, channel);
2364   return SILC_FSM_FINISH;
2365 }
2366
2367 /***************************** OPER & SILCOPER ******************************/
2368
2369 typedef struct {
2370   unsigned char *passphrase;
2371   SilcUInt32 passphrase_len;
2372   SilcBuffer auth;
2373 } *SilcClientCommandOper;
2374
2375 /* Ask passphrase callback */
2376
2377 static void silc_client_command_oper_cb(const unsigned char *data,
2378                                         SilcUInt32 data_len, void *context)
2379 {
2380   SilcClientCommandContext cmd = context;
2381   SilcClientCommandOper oper = cmd->context;
2382
2383   if (data && data_len)
2384     oper->passphrase = silc_memdup(data, data_len);
2385   oper->passphrase_len = data_len;
2386
2387   /* Continue */
2388   SILC_FSM_CALL_CONTINUE(&cmd->thread);
2389 }
2390
2391 static void silc_client_command_oper_sign_cb(const SilcBuffer data,
2392                                              void *context)
2393 {
2394   SilcClientCommandContext cmd = context;
2395   SilcClientCommandOper oper = cmd->context;
2396
2397   if (data)
2398     oper->auth = silc_buffer_copy(data);
2399
2400   /* Continue */
2401   SILC_FSM_CALL_CONTINUE(&cmd->thread);
2402 }
2403
2404 /* Send OPER/SILCOPER command */
2405
2406 SILC_FSM_STATE(silc_client_command_oper_send)
2407 {
2408   SilcClientCommandContext cmd = fsm_context;
2409   SilcClientConnection conn = cmd->conn;
2410   SilcClientCommandOper oper = cmd->context;
2411   SilcBuffer auth = oper ? oper->auth : NULL;
2412
2413   silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
2414                               1, cmd->argv[1], strlen(cmd->argv[1]),
2415                               2, silc_buffer_datalen(auth));
2416
2417   silc_buffer_clear(auth);
2418   silc_buffer_free(auth);
2419   if (oper) {
2420     silc_free(oper->passphrase);
2421     silc_free(oper);
2422   }
2423
2424   /* Notify application */
2425   COMMAND(SILC_STATUS_OK);
2426
2427   /** Wait for command reply */
2428   silc_fsm_next(fsm, silc_client_command_reply_wait);
2429   return SILC_FSM_CONTINUE;
2430 }
2431
2432 /* Get authentication data */
2433
2434 SILC_FSM_STATE(silc_client_command_oper_get_auth)
2435 {
2436   SilcClientCommandContext cmd = fsm_context;
2437   SilcClientConnection conn = cmd->conn;
2438   SilcClientCommandOper oper = cmd->context;
2439
2440   silc_fsm_next(fsm, silc_client_command_oper_send);
2441
2442   if (!oper || !oper->passphrase) {
2443     /* Encode the public key authentication payload */
2444     SILC_FSM_CALL(silc_auth_public_key_auth_generate(
2445                                        conn->public_key,
2446                                        conn->private_key,
2447                                        conn->client->rng,
2448                                        conn->internal->hash,
2449                                        conn->local_id, SILC_ID_CLIENT,
2450                                        silc_client_command_oper_sign_cb,
2451                                        oper));
2452     /* NOT REACHED */
2453   }
2454
2455   /* Encode the password authentication payload */
2456   oper->auth = silc_auth_payload_encode(NULL, SILC_AUTH_PASSWORD, NULL, 0,
2457                                         oper->passphrase, oper->passphrase_len);
2458
2459   return SILC_FSM_CONTINUE;
2460 }
2461
2462 /* OPER command. Used to obtain server operator privileges. */
2463
2464 SILC_FSM_STATE(silc_client_command_oper)
2465 {
2466   SilcClientCommandContext cmd = fsm_context;
2467   SilcClientConnection conn = cmd->conn;
2468   SilcClientCommandOper oper;
2469
2470   if (cmd->argc < 2) {
2471     SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2472         "Usage: /OPER <username> [-pubkey]");
2473     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2474     return SILC_FSM_FINISH;
2475   }
2476
2477   silc_fsm_next(fsm, silc_client_command_oper_get_auth);
2478
2479   /* Get passphrase */
2480   if (cmd->argc < 3) {
2481     oper = silc_calloc(1, sizeof(*oper));
2482     if (!oper)
2483       return SILC_FSM_FINISH;
2484     cmd->context = oper;
2485     SILC_FSM_CALL(conn->client->internal->
2486                   ops->ask_passphrase(conn->client, conn,
2487                                       silc_client_command_oper_cb, cmd));
2488   }
2489
2490   return SILC_FSM_CONTINUE;
2491 }
2492
2493 /* SILCOPER command. Used to obtain router operator privileges. */
2494
2495 SILC_FSM_STATE(silc_client_command_silcoper)
2496 {
2497   SilcClientCommandContext cmd = fsm_context;
2498   SilcClientConnection conn = cmd->conn;
2499   SilcClientCommandOper oper;
2500
2501   if (cmd->argc < 2) {
2502     SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2503         "Usage: /SILCOPER <username> [-pubkey]");
2504     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2505     return SILC_FSM_FINISH;
2506   }
2507
2508   silc_fsm_next(fsm, silc_client_command_oper_send);
2509
2510   /* Get passphrase */
2511   if (cmd->argc < 3) {
2512     oper = silc_calloc(1, sizeof(*oper));
2513     if (!oper)
2514       return SILC_FSM_FINISH;
2515     cmd->context = oper;
2516     SILC_FSM_CALL(conn->client->internal->
2517                   ops->ask_passphrase(conn->client, conn,
2518                                       silc_client_command_oper_cb, cmd));
2519   }
2520
2521   return SILC_FSM_CONTINUE;
2522 }
2523
2524 /*********************************** BAN ************************************/
2525
2526 /* Command BAN. This is used to manage the ban list of the channel. */
2527
2528 SILC_FSM_STATE(silc_client_command_ban)
2529 {
2530   SilcClientCommandContext cmd = fsm_context;
2531   SilcClientConnection conn = cmd->conn;
2532   SilcClient client = conn->client;
2533   SilcChannelEntry channel;
2534   SilcBuffer chidp, args = NULL;
2535   char *name, *ban = NULL;
2536   unsigned char action[1];
2537   SilcPublicKey pubkey = NULL;
2538
2539   if (cmd->argc < 2) {
2540     SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2541         "Usage: /BAN <channel> "
2542         "[+|-[<nickname>[@<server>[!<username>[@hostname>]]]]]");
2543     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2544     goto out;
2545   }
2546
2547   if (cmd->argv[1][0] == '*') {
2548     if (!conn->current_channel) {
2549       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2550       goto out;
2551     }
2552
2553     channel = conn->current_channel;
2554     silc_client_ref_channel(client, conn, channel);
2555   } else {
2556     name = cmd->argv[1];
2557
2558     channel = silc_client_get_channel(conn->client, conn, name);
2559     if (!channel) {
2560       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2561       goto out;
2562     }
2563   }
2564
2565   if (cmd->argc == 3) {
2566     if (cmd->argv[2][0] == '+')
2567       action[0] = 0x00;
2568     else
2569       action[0] = 0x01;
2570
2571     /* Check if it is public key file to be added to invite list */
2572     silc_pkcs_load_public_key(cmd->argv[2] + 1, SILC_PKCS_ANY, &pubkey);
2573     ban = cmd->argv[2];
2574     if (!pubkey)
2575       ban++;
2576   }
2577
2578   if (ban) {
2579     args = silc_buffer_alloc_size(2);
2580     silc_buffer_format(args,
2581                        SILC_STR_UI_SHORT(1),
2582                        SILC_STR_END);
2583     if (pubkey) {
2584       chidp = silc_public_key_payload_encode(NULL, pubkey);
2585       args = silc_argument_payload_encode_one(args,
2586                                               silc_buffer_datalen(chidp), 2);
2587       silc_buffer_free(chidp);
2588       silc_pkcs_public_key_free(pubkey);
2589     } else {
2590       args = silc_argument_payload_encode_one(args, ban, strlen(ban), 1);
2591     }
2592   }
2593
2594   chidp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2595
2596   /* Send the command */
2597   silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 3,
2598                               1, silc_buffer_datalen(chidp),
2599                               2, args ? action : NULL, args ? 1 : 0,
2600                               3, silc_buffer_datalen(args));
2601
2602   silc_buffer_free(chidp);
2603   silc_buffer_free(args);
2604   silc_client_unref_channel(client, conn, channel);
2605
2606   /* Notify application */
2607   COMMAND(SILC_STATUS_OK);
2608
2609   /** Wait for command reply */
2610   silc_fsm_next(fsm, silc_client_command_reply_wait);
2611   return SILC_FSM_CONTINUE;
2612
2613  out:
2614   return SILC_FSM_FINISH;
2615 }
2616
2617 /********************************* DETACH ***********************************/
2618
2619 /* Command DETACH. This is used to detach from the server */
2620
2621 SILC_FSM_STATE(silc_client_command_detach)
2622 {
2623   SilcClientCommandContext cmd = fsm_context;
2624   SilcClientConnection conn = cmd->conn;
2625
2626   silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 0);
2627
2628   /* Notify application */
2629   COMMAND(SILC_STATUS_OK);
2630
2631   /** Wait for command reply */
2632   silc_fsm_next(fsm, silc_client_command_reply_wait);
2633   return SILC_FSM_CONTINUE;
2634 }
2635
2636 /********************************** WATCH ***********************************/
2637
2638 /* Command WATCH. */
2639
2640 SILC_FSM_STATE(silc_client_command_watch)
2641 {
2642   SilcClientCommandContext cmd = fsm_context;
2643   SilcClientConnection conn = cmd->conn;
2644   SilcBuffer args = NULL;
2645   int type = 0;
2646   const char *pubkey = NULL;
2647   SilcBool pubkey_add = TRUE;
2648
2649   if (cmd->argc < 3) {
2650     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2651     goto out;
2652   }
2653
2654   if (!strcasecmp(cmd->argv[1], "-add")) {
2655     type = 2;
2656   } else if (!strcasecmp(cmd->argv[1], "-del")) {
2657     type = 3;
2658   } else if (!strcasecmp(cmd->argv[1], "-pubkey") && cmd->argc >= 3) {
2659     type = 4;
2660     pubkey = cmd->argv[2] + 1;
2661     if (cmd->argv[2][0] == '-')
2662       pubkey_add = FALSE;
2663   } else {
2664     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2665     goto out;
2666   }
2667
2668   if (pubkey) {
2669     SilcPublicKey pk;
2670     SilcBuffer buffer;
2671
2672     if (!silc_pkcs_load_public_key(pubkey, SILC_PKCS_ANY, &pk)) {
2673       SAY(conn->client, conn, SILC_CLIENT_MESSAGE_COMMAND_ERROR,
2674           "Could not load public key %s, check the filename", pubkey);
2675       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2676       goto out;
2677     }
2678
2679     args = silc_buffer_alloc_size(2);
2680     silc_buffer_format(args,
2681                        SILC_STR_UI_SHORT(1),
2682                        SILC_STR_END);
2683     buffer = silc_public_key_payload_encode(NULL, pk);
2684     args = silc_argument_payload_encode_one(args, silc_buffer_datalen(buffer),
2685                                             pubkey_add ? 0x00 : 0x01);
2686     silc_buffer_free(buffer);
2687     silc_pkcs_public_key_free(pk);
2688   }
2689
2690   /* If watching by nickname, resolve all users with that nickname so that
2691      we get their information immediately. */
2692   if (type == 2)
2693     silc_client_get_clients(conn->client, conn, cmd->argv[2], NULL,
2694                             silc_client_command_resolve_dummy, NULL);
2695
2696   /* Send the commmand */
2697   silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 2,
2698                               1, silc_buffer_datalen(conn->internal->
2699                                                      local_idp),
2700                               type, pubkey ? args->data : cmd->argv[2],
2701                               pubkey ? silc_buffer_len(args) :
2702                               cmd->argv_lens[2]);
2703
2704   silc_buffer_free(args);
2705
2706   /* Notify application */
2707   COMMAND(SILC_STATUS_OK);
2708
2709   /** Wait for command reply */
2710   silc_fsm_next(fsm, silc_client_command_reply_wait);
2711   return SILC_FSM_CONTINUE;
2712
2713  out:
2714   return SILC_FSM_FINISH;
2715 }
2716
2717 /********************************** LEAVE ***********************************/
2718
2719 /* LEAVE command. Leaves a channel. Client removes itself from a channel. */
2720
2721 SILC_FSM_STATE(silc_client_command_leave)
2722 {
2723   SilcClientCommandContext cmd = fsm_context;
2724   SilcClientConnection conn = cmd->conn;
2725   SilcClient client = conn->client;
2726   SilcChannelEntry channel;
2727   SilcBuffer idp;
2728   char *name, tmp[512];
2729
2730   if (cmd->argc != 2) {
2731     SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2732         "Usage: /LEAVE <channel>");
2733     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2734     goto out;
2735   }
2736
2737   if (cmd->argv[1][0] == '*') {
2738     if (!conn->current_channel) {
2739       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2740       goto out;
2741     }
2742
2743     if (client->internal->params->full_channel_names)
2744       silc_snprintf(tmp, sizeof(tmp), conn->current_channel->channel_name);
2745     else
2746       silc_snprintf(tmp, sizeof(tmp), "%s%s%s",
2747                     conn->current_channel->channel_name,
2748                     conn->current_channel->server[0] ? "@" : "",
2749                     conn->current_channel->server);
2750     name = tmp;
2751   } else {
2752     name = cmd->argv[1];
2753   }
2754
2755   /* Get the channel entry */
2756   channel = silc_client_get_channel(conn->client, conn, name);
2757   if (!channel) {
2758     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2759     goto out;
2760   }
2761
2762   idp = silc_id_payload_encode(&channel->id, SILC_ID_CHANNEL);
2763
2764   /* Send LEAVE command to the server */
2765   silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
2766                               1, silc_buffer_datalen(idp));
2767
2768   silc_buffer_free(idp);
2769
2770   /* Notify application */
2771   COMMAND(SILC_STATUS_OK);
2772
2773   if (conn->current_channel == channel)
2774     conn->current_channel = NULL;
2775
2776   silc_client_unref_channel(client, conn, channel);
2777
2778   /** Wait for command reply */
2779   silc_fsm_next(fsm, silc_client_command_reply_wait);
2780   return SILC_FSM_CONTINUE;
2781
2782  out:
2783   return SILC_FSM_FINISH;
2784 }
2785
2786 /********************************** USERS ***********************************/
2787
2788 /* Command USERS. Requests the USERS of the clients joined on requested
2789    channel. */
2790
2791 SILC_FSM_STATE(silc_client_command_users)
2792 {
2793   SilcClientCommandContext cmd = fsm_context;
2794   SilcClientConnection conn = cmd->conn;
2795   char *name, tmp[512];
2796
2797   if (cmd->argc != 2) {
2798     SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2799         "Usage: /USERS <channel>");
2800     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2801     goto out;
2802   }
2803
2804   if (cmd->argv[1][0] == '*') {
2805     if (!conn->current_channel) {
2806       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2807       goto out;
2808     }
2809
2810     if (conn->client->internal->params->full_channel_names)
2811       silc_snprintf(tmp, sizeof(tmp), conn->current_channel->channel_name);
2812     else
2813       silc_snprintf(tmp, sizeof(tmp), "%s%s%s",
2814                     conn->current_channel->channel_name,
2815                     conn->current_channel->server[0] ? "@" : "",
2816                     conn->current_channel->server);
2817     name = tmp;
2818   } else {
2819     name = cmd->argv[1];
2820   }
2821
2822   /* Send USERS command to the server */
2823   silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
2824                               2, name, strlen(name));
2825
2826   /* Notify application */
2827   COMMAND(SILC_STATUS_OK);
2828
2829   /** Wait for command reply */
2830   silc_fsm_next(fsm, silc_client_command_reply_wait);
2831   return SILC_FSM_CONTINUE;
2832
2833  out:
2834   return SILC_FSM_FINISH;
2835 }
2836
2837 /********************************* GETKEY ***********************************/
2838
2839 /* Command GETKEY. Used to fetch remote client's public key. */
2840
2841 SILC_FSM_STATE(silc_client_command_getkey)
2842 {
2843   SilcClientCommandContext cmd = fsm_context;
2844   SilcClientConnection conn = cmd->conn;
2845   SilcClient client = conn->client;
2846   SilcClientEntry client_entry;
2847   SilcServerEntry server_entry;
2848   SilcDList clients;
2849   SilcBuffer idp;
2850
2851   if (cmd->argc < 2) {
2852     client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_INFO,
2853                      "Usage: /GETKEY <nickname or server name>");
2854     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2855     return SILC_FSM_FINISH;
2856   }
2857
2858   /* Find client entry */
2859   clients = silc_client_get_clients_local(client, conn, cmd->argv[1], FALSE);
2860   if (!clients) {
2861     /* Check whether user requested server */
2862     server_entry = silc_client_get_server(client, conn, cmd->argv[1]);
2863     if (!server_entry) {
2864       if (cmd->resolved) {
2865         /* Resolving didn't find anything.  We should never get here as
2866            errors are handled in the resolving callback. */
2867         COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_NICK);
2868         COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_SERVER);
2869         return SILC_FSM_FINISH;
2870       }
2871
2872       /* No client or server exist with this name, query for both. */
2873       cmd->resolved = TRUE;
2874       SILC_FSM_CALL(silc_client_command_send(client, conn,
2875                                              SILC_COMMAND_IDENTIFY,
2876                                              silc_client_command_continue,
2877                                              cmd, 2,
2878                                              1, cmd->argv[1],
2879                                              strlen(cmd->argv[1]),
2880                                              2, cmd->argv[1],
2881                                              strlen(cmd->argv[1])));
2882       /* NOT REACHED */
2883     }
2884     idp = silc_id_payload_encode(&server_entry->id, SILC_ID_SERVER);
2885     silc_client_unref_server(client, conn, server_entry);
2886   } else {
2887     client_entry = silc_dlist_get(clients);
2888     idp = silc_id_payload_encode(&client_entry->id, SILC_ID_CLIENT);
2889     silc_client_list_free(client, conn, clients);
2890   }
2891
2892   /* Send the commmand */
2893   silc_client_command_send_va(conn, cmd, cmd->cmd, NULL, NULL, 1,
2894                               1, silc_buffer_datalen(idp));
2895
2896   silc_buffer_free(idp);
2897
2898   /* Notify application */
2899   COMMAND(SILC_STATUS_OK);
2900
2901   /** Wait for command reply */
2902   silc_fsm_next(fsm, silc_client_command_reply_wait);
2903   return SILC_FSM_CONTINUE;
2904 }
2905
2906 /********************************* SERVICE **********************************/
2907
2908 /* Command SERVICE.  Negotiates service agreement with server. */
2909 /* XXX incomplete */
2910
2911 SILC_FSM_STATE(silc_client_command_service)
2912 {
2913   SilcClientCommandContext cmd = fsm_context;
2914 #if 0
2915   SilcClientConnection conn = cmd->conn;
2916   SilcBuffer buffer;
2917   char *name;
2918
2919   if (cmd->argc < 2) {
2920     SAY(conn->client, conn, SILC_CLIENT_MESSAGE_INFO,
2921         "Usage: /SERVICE [<service name>] [-pubkey]");
2922     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2923     return SILC_FSM_FINISH;
2924   }
2925
2926   name = cmd->argv[1];
2927
2928   /* Send SERVICE command to the server */
2929   buffer = silc_command_payload_encode_va(SILC_COMMAND_SERVICE,
2930                                           ++conn->cmd_ident, 1,
2931                                           1, name, strlen(name));
2932   silc_client_packet_send(conn->client, conn->sock, SILC_PACKET_COMMAND,
2933                           NULL, 0, NULL, NULL, buffer->data,
2934                           buffer->len, TRUE);
2935   silc_buffer_free(buffer);
2936 #endif /* 0 */
2937
2938   /* Notify application */
2939   COMMAND(SILC_STATUS_OK);
2940
2941   /** Wait for command reply */
2942   silc_fsm_next(fsm, silc_client_command_reply_wait);
2943   return SILC_FSM_CONTINUE;
2944 }
2945
2946 /* Register all default commands provided by the client library for the
2947    application. */
2948
2949 void silc_client_commands_register(SilcClient client)
2950 {
2951   silc_list_init(client->internal->commands, struct SilcClientCommandStruct,
2952                  next);
2953
2954   SILC_CLIENT_CMD(whois, WHOIS, "WHOIS", 5);
2955   SILC_CLIENT_CMD(whowas, WHOWAS, "WHOWAS", 3);
2956   SILC_CLIENT_CMD(identify, IDENTIFY, "IDENTIFY", 3);
2957   SILC_CLIENT_CMD(nick, NICK, "NICK", 2);
2958   SILC_CLIENT_CMD(list, LIST, "LIST", 2);
2959   SILC_CLIENT_CMD(topic, TOPIC, "TOPIC", 3);
2960   SILC_CLIENT_CMD(invite, INVITE, "INVITE", 3);
2961   SILC_CLIENT_CMD(quit, QUIT, "QUIT", 2);
2962   SILC_CLIENT_CMD(kill, KILL, "KILL", 4);
2963   SILC_CLIENT_CMD(info, INFO, "INFO", 2);
2964   SILC_CLIENT_CMD(stats, STATS, "STATS", 0);
2965   SILC_CLIENT_CMD(ping, PING, "PING", 2);
2966   SILC_CLIENT_CMD(oper, OPER, "OPER", 3);
2967   SILC_CLIENT_CMD(join, JOIN, "JOIN", 9);
2968   SILC_CLIENT_CMD(motd, MOTD, "MOTD", 2);
2969   SILC_CLIENT_CMD(umode, UMODE, "UMODE", 2);
2970   SILC_CLIENT_CMD(cmode, CMODE, "CMODE", 6);
2971   SILC_CLIENT_CMD(cumode, CUMODE, "CUMODE", 9);
2972   SILC_CLIENT_CMD(kick, KICK, "KICK", 4);
2973   SILC_CLIENT_CMD(ban, BAN, "BAN", 3);
2974   SILC_CLIENT_CMD(detach, DETACH, "DETACH", 0);
2975   SILC_CLIENT_CMD(watch, WATCH, "WATCH", 3);
2976   SILC_CLIENT_CMD(silcoper, SILCOPER, "SILCOPER", 3);
2977   SILC_CLIENT_CMD(leave, LEAVE, "LEAVE", 2);
2978   SILC_CLIENT_CMD(users, USERS, "USERS", 2);
2979   SILC_CLIENT_CMD(getkey, GETKEY, "GETKEY", 2);
2980   SILC_CLIENT_CMD(service, SERVICE, "SERVICE", 10);
2981 }
2982
2983 /* Unregister all commands. */
2984
2985 void silc_client_commands_unregister(SilcClient client)
2986 {
2987   SILC_CLIENT_CMDU(whois, WHOIS, "WHOIS");
2988   SILC_CLIENT_CMDU(whowas, WHOWAS, "WHOWAS");
2989   SILC_CLIENT_CMDU(identify, IDENTIFY, "IDENTIFY");
2990   SILC_CLIENT_CMDU(nick, NICK, "NICK");
2991   SILC_CLIENT_CMDU(list, LIST, "LIST");
2992   SILC_CLIENT_CMDU(topic, TOPIC, "TOPIC");
2993   SILC_CLIENT_CMDU(invite, INVITE, "INVITE");
2994   SILC_CLIENT_CMDU(quit, QUIT, "QUIT");
2995   SILC_CLIENT_CMDU(kill, KILL, "KILL");
2996   SILC_CLIENT_CMDU(info, INFO, "INFO");
2997   SILC_CLIENT_CMDU(stats, STATS, "STATS");
2998   SILC_CLIENT_CMDU(ping, PING, "PING");
2999   SILC_CLIENT_CMDU(oper, OPER, "OPER");
3000   SILC_CLIENT_CMDU(join, JOIN, "JOIN");
3001   SILC_CLIENT_CMDU(motd, MOTD, "MOTD");
3002   SILC_CLIENT_CMDU(umode, UMODE, "UMODE");
3003   SILC_CLIENT_CMDU(cmode, CMODE, "CMODE");
3004   SILC_CLIENT_CMDU(cumode, CUMODE, "CUMODE");
3005   SILC_CLIENT_CMDU(kick, KICK, "KICK");
3006   SILC_CLIENT_CMDU(ban, BAN, "BAN");
3007   SILC_CLIENT_CMDU(detach, DETACH, "DETACH");
3008   SILC_CLIENT_CMDU(watch, WATCH, "WATCH");
3009   SILC_CLIENT_CMDU(silcoper, SILCOPER, "SILCOPER");
3010   SILC_CLIENT_CMDU(leave, LEAVE, "LEAVE");
3011   SILC_CLIENT_CMDU(users, USERS, "USERS");
3012   SILC_CLIENT_CMDU(getkey, GETKEY, "GETKEY");
3013   SILC_CLIENT_CMDU(service, SERVICE, "SERVICE");
3014 }
3015
3016 /****************** Client Side Incoming Command Handling *******************/
3017
3018 typedef struct {
3019   SilcClientConnection conn;
3020   SilcUInt16 cmd_ident;
3021 } *SilcClientProcessWhois;
3022
3023 /* Send reply to WHOIS from server */
3024
3025 static void
3026 silc_client_command_process_whois_send(SilcBool success,
3027                                        const unsigned char *data,
3028                                        SilcUInt32 data_len, void *context)
3029 {
3030   SilcClientProcessWhois w = context;
3031   SilcBufferStruct buffer;
3032   SilcBuffer packet;
3033
3034   if (!data) {
3035     silc_free(w);
3036     return;
3037   }
3038
3039   silc_buffer_set(&buffer, (unsigned char *)data, data_len);
3040
3041   /* Send the attributes back in COMMAND_REPLY packet */
3042   packet =
3043     silc_command_reply_payload_encode_va(SILC_COMMAND_WHOIS,
3044                                          SILC_STATUS_OK, 0, w->cmd_ident,
3045                                          1, 11, buffer.data,
3046                                          silc_buffer_len(&buffer));
3047   if (!packet) {
3048     silc_free(w);
3049     return;
3050   }
3051
3052   SILC_LOG_DEBUG(("Sending back requested WHOIS attributes"));
3053
3054   silc_packet_send(w->conn->stream, SILC_PACKET_COMMAND_REPLY, 0,
3055                    silc_buffer_datalen(packet));
3056
3057   silc_buffer_free(packet);
3058   silc_free(w);
3059 }
3060
3061 /* Process WHOIS command from server */
3062
3063 static void silc_client_command_process_whois(SilcClient client,
3064                                               SilcClientConnection conn,
3065                                               SilcCommandPayload payload,
3066                                               SilcArgumentPayload args)
3067 {
3068   SilcClientProcessWhois w;
3069   SilcDList attrs;
3070   unsigned char *tmp;
3071   SilcUInt32 tmp_len;
3072
3073   SILC_LOG_DEBUG(("Received WHOIS command"));
3074
3075   /* Try to take the Requested Attributes */
3076   tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
3077   if (!tmp)
3078     return;
3079
3080   attrs = silc_attribute_payload_parse(tmp, tmp_len);
3081   if (!attrs)
3082     return;
3083
3084   w = silc_calloc(1, sizeof(*w));
3085   if (!w) {
3086     silc_attribute_payload_list_free(attrs);
3087     return;
3088   }
3089   w->conn = conn;
3090   w->cmd_ident = silc_command_get_ident(payload);
3091
3092   /* Process requested attributes */
3093   silc_client_attributes_process(client, conn, attrs,
3094                                  silc_client_command_process_whois_send, w);
3095   silc_attribute_payload_list_free(attrs);
3096 }
3097
3098 /* Client is able to receive some command packets even though they are
3099    special case.  Server may send WHOIS command to the client to retrieve
3100    Requested Attributes information for WHOIS query the server is
3101    processing. This function currently handles only the WHOIS command,
3102    but if in the future more commands may arrive then this can be made
3103    to support other commands too. */
3104
3105 SILC_FSM_STATE(silc_client_command)
3106 {
3107   SilcClientConnection conn = fsm_context;
3108   SilcClient client = conn->client;
3109   SilcPacket packet = state_context;
3110   SilcCommandPayload payload;
3111   SilcCommand command;
3112   SilcArgumentPayload args;
3113
3114   /* Get command payload from packet */
3115   payload = silc_command_payload_parse(packet->buffer.data,
3116                                        silc_buffer_len(&packet->buffer));
3117   if (!payload) {
3118     SILC_LOG_DEBUG(("Bad command packet"));
3119     return SILC_FSM_FINISH;
3120   }
3121
3122   /* Get arguments */
3123   args = silc_command_get_args(payload);
3124
3125   /* Get the command */
3126   command = silc_command_get(payload);
3127   switch (command) {
3128
3129   case SILC_COMMAND_WHOIS:
3130     /* Ignore everything if requested by application */
3131     if (conn->internal->params.ignore_requested_attributes)
3132       break;
3133
3134     silc_client_command_process_whois(client, conn, payload, args);
3135     break;
3136
3137   default:
3138     break;
3139   }
3140
3141   silc_command_payload_free(payload);
3142   return SILC_FSM_FINISH;
3143 }