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