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