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