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