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