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