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