Merged silc_1_0_branch to trunk.
[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   SilcBuffer buffer, idp;
2331   char *name;
2332
2333   if (!cmd->conn) {
2334     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
2335     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
2336     goto out;
2337   }
2338
2339   if (cmd->argc != 2) {
2340     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
2341         "Usage: /LEAVE <channel>");
2342     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2343     goto out;
2344   }
2345
2346   if (cmd->argv[1][0] == '*') {
2347     if (!conn->current_channel) {
2348       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2349       goto out;
2350     }
2351     name = conn->current_channel->channel_name;
2352   } else {
2353     name = cmd->argv[1];
2354   }
2355
2356   /* Get the channel entry */
2357   channel = silc_client_get_channel(cmd->client, conn, name);
2358   if (!channel) {
2359     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2360     goto out;
2361   }
2362
2363   /* Send LEAVE command to the server */
2364   idp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
2365   buffer = silc_command_payload_encode_va(SILC_COMMAND_LEAVE, 0, 1,
2366                                           1, idp->data, idp->len);
2367   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
2368                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
2369   silc_buffer_free(buffer);
2370   silc_buffer_free(idp);
2371
2372   /* Notify application */
2373   COMMAND(SILC_STATUS_OK);
2374
2375   if (conn->current_channel == channel)
2376     conn->current_channel = NULL;
2377
2378  out:
2379   silc_client_command_free(cmd);
2380 }
2381
2382 /* Command USERS. Requests the USERS of the clients joined on requested
2383    channel. */
2384
2385 SILC_CLIENT_CMD_FUNC(users)
2386 {
2387   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
2388   SilcClientConnection conn = cmd->conn;
2389   SilcBuffer buffer;
2390   char *name;
2391
2392   if (!cmd->conn) {
2393     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
2394     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
2395     goto out;
2396   }
2397
2398   if (cmd->argc != 2) {
2399     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
2400         "Usage: /USERS <channel>");
2401     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2402     goto out;
2403   }
2404
2405   if (cmd->argv[1][0] == '*') {
2406     if (!conn->current_channel) {
2407       COMMAND_ERROR(SILC_STATUS_ERR_NOT_ON_CHANNEL);
2408       goto out;
2409     }
2410     name = conn->current_channel->channel_name;
2411   } else {
2412     name = cmd->argv[1];
2413   }
2414
2415   /* Send USERS command to the server */
2416   buffer = silc_command_payload_encode_va(SILC_COMMAND_USERS,
2417                                           ++conn->cmd_ident, 1,
2418                                           2, name, strlen(name));
2419   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND,
2420                           NULL, 0, NULL, NULL, buffer->data,
2421                           buffer->len, TRUE);
2422   silc_buffer_free(buffer);
2423
2424   /* Notify application */
2425   COMMAND(SILC_STATUS_OK);
2426
2427  out:
2428   silc_client_command_free(cmd);
2429 }
2430
2431 /* Command GETKEY. Used to fetch remote client's public key. */
2432
2433 SILC_CLIENT_CMD_FUNC(getkey)
2434 {
2435   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
2436   SilcClientConnection conn = cmd->conn;
2437   SilcClient client = cmd->client;
2438   SilcClientEntry client_entry = NULL;
2439   SilcServerEntry server_entry = NULL;
2440   char *nickname = NULL;
2441   SilcBuffer idp, buffer;
2442
2443   SILC_LOG_DEBUG(("Start"));
2444
2445   if (!cmd->conn) {
2446     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
2447     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
2448     goto out;
2449   }
2450
2451   if (cmd->argc < 2) {
2452     client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_INFO,
2453                      "Usage: /GETKEY <nickname or server name>");
2454     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2455     goto out;
2456   }
2457
2458   /* Parse the typed nickname. */
2459   if (client->internal->params->nickname_parse)
2460     client->internal->params->nickname_parse(cmd->argv[1], &nickname);
2461   else
2462     nickname = strdup(cmd->argv[1]);
2463
2464   /* Find client entry */
2465   client_entry = silc_idlist_get_client(client, conn, nickname, cmd->argv[1],
2466                                         FALSE);
2467   if (!client_entry) {
2468     /* Check whether user requested server actually */
2469     server_entry = silc_client_get_server(client, conn, cmd->argv[1]);
2470
2471     if (!server_entry) {
2472       /* No. what ever user wants we don't have it, so resolve it. We
2473          will first try to resolve the client, and if that fails then
2474          we'll try to resolve the server. */
2475
2476       if (!cmd->pending) {
2477         /* This will send the IDENTIFY command for nickname */
2478         silc_idlist_get_client(client, conn, nickname, cmd->argv[1], TRUE);
2479         silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY,
2480                                     conn->cmd_ident,
2481                                     silc_client_command_getkey,
2482                                     silc_client_command_dup(cmd));
2483         cmd->pending = 1;
2484         goto out;
2485       } else {
2486         SilcClientCommandReplyContext reply =
2487           (SilcClientCommandReplyContext)context2;
2488         SilcStatus error;
2489
2490         /* If nickname was not found, then resolve the server. */
2491         silc_command_get_status(reply->payload, NULL, &error);
2492         if (error == SILC_STATUS_ERR_NO_SUCH_NICK) {
2493           /* This sends the IDENTIFY command to resolve the server. */
2494           silc_client_command_register(client, SILC_COMMAND_IDENTIFY,
2495                                        NULL, NULL,
2496                                        silc_client_command_reply_identify_i, 0,
2497                                        ++conn->cmd_ident);
2498           silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
2499                                    conn->cmd_ident, 1,
2500                                    2, cmd->argv[1], cmd->argv_lens[1]);
2501           silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY,
2502                                       conn->cmd_ident,
2503                                       silc_client_command_getkey,
2504                                       silc_client_command_dup(cmd));
2505           goto out;
2506         }
2507
2508         /* If server was not found, then we've resolved both nickname and
2509            server and did not find anybody. */
2510         if (error == SILC_STATUS_ERR_NO_SUCH_SERVER) {
2511           SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, "%s",
2512              silc_get_status_message(SILC_STATUS_ERR_NO_SUCH_NICK));
2513           SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, "%s",
2514            silc_get_status_message(error));
2515           COMMAND_ERROR(SILC_STATUS_ERR_NO_SUCH_NICK);
2516           goto out;
2517         }
2518
2519         COMMAND_ERROR(error);
2520         goto out;
2521       }
2522     }
2523
2524     idp = silc_id_payload_encode(server_entry->server_id, SILC_ID_SERVER);
2525   } else {
2526     idp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
2527   }
2528
2529   buffer = silc_command_payload_encode_va(SILC_COMMAND_GETKEY, 0, 1,
2530                                           1, idp->data, idp->len);
2531   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
2532                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
2533   silc_buffer_free(buffer);
2534   silc_buffer_free(idp);
2535
2536   /* Notify application */
2537   COMMAND(SILC_STATUS_OK);
2538
2539  out:
2540   silc_free(nickname);
2541   silc_client_command_free(cmd);
2542 }
2543
2544 /* Register a new command indicated by the `command' to the SILC client.
2545    The `name' is optional command name.  If provided the command may be
2546    searched using the silc_client_command_find by that name.  The
2547    `command_function' is the function to be called when the command is
2548    executed, and the `command_reply_function' is the function to be
2549    called after the server has sent reply back to the command.
2550
2551    The `ident' is optional identifier for the command.  If non-zero
2552    the `command_reply_function' for the command type `command' will be
2553    called only if the command reply sent by server includes the
2554    command identifier `ident'. Application usually does not need it
2555    and set it to zero value. */
2556
2557 bool silc_client_command_register(SilcClient client,
2558                                   SilcCommand command,
2559                                   const char *name,
2560                                   SilcCommandCb command_function,
2561                                   SilcCommandCb command_reply_function,
2562                                   SilcUInt8 max_args,
2563                                   SilcUInt16 ident)
2564 {
2565   SilcClientCommand cmd;
2566
2567   cmd = silc_calloc(1, sizeof(*cmd));
2568   cmd->cmd = command;
2569   cmd->command = command_function;
2570   cmd->reply = command_reply_function;
2571   cmd->name = name ? strdup(name) : NULL;
2572   cmd->max_args = max_args;
2573   cmd->ident = ident;
2574
2575   silc_list_add(client->internal->commands, cmd);
2576
2577   return TRUE;
2578 }
2579
2580 /* Unregister a command indicated by the `command' with command function
2581    `command_function' and command reply function `command_reply_function'.
2582    Returns TRUE if the command was found and unregistered. */
2583
2584 bool silc_client_command_unregister(SilcClient client,
2585                                     SilcCommand command,
2586                                     SilcCommandCb command_function,
2587                                     SilcCommandCb command_reply_function,
2588                                     SilcUInt16 ident)
2589 {
2590   SilcClientCommand cmd;
2591
2592   silc_list_start(client->internal->commands);
2593   while ((cmd = silc_list_get(client->internal->commands)) != SILC_LIST_END) {
2594     if (cmd->cmd == command && cmd->command == command_function &&
2595         cmd->reply == command_reply_function && cmd->ident == ident) {
2596       silc_list_del(client->internal->commands, cmd);
2597       silc_free(cmd->name);
2598       silc_free(cmd);
2599       return TRUE;
2600     }
2601   }
2602
2603   return FALSE;
2604 }
2605
2606 /* Private range commands, specific to this implementation (and compatible
2607    with SILC Server). */
2608
2609 /* CONNECT command. Connects the server to another server. */
2610
2611 SILC_CLIENT_CMD_FUNC(connect)
2612 {
2613   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
2614   SilcClientConnection conn = cmd->conn;
2615   SilcBuffer buffer;
2616   unsigned char port[4];
2617   SilcUInt32 tmp;
2618
2619   if (!cmd->conn) {
2620     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
2621     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
2622     goto out;
2623   }
2624
2625   if (cmd->argc < 2) {
2626     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
2627         "Usage: /CONNECT <server> [<port>]");
2628     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2629     goto out;
2630   }
2631
2632   if (cmd->argc == 3) {
2633     tmp = atoi(cmd->argv[2]);
2634     SILC_PUT32_MSB(tmp, port);
2635   }
2636
2637   if (cmd->argc == 3)
2638     buffer = silc_command_payload_encode_va(SILC_COMMAND_PRIV_CONNECT, 0, 2,
2639                                             1, cmd->argv[1],
2640                                             strlen(cmd->argv[1]),
2641                                             2, port, 4);
2642   else
2643     buffer = silc_command_payload_encode_va(SILC_COMMAND_PRIV_CONNECT, 0, 1,
2644                                             1, cmd->argv[1],
2645                                             strlen(cmd->argv[1]));
2646   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
2647                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
2648   silc_buffer_free(buffer);
2649
2650   /* Notify application */
2651   COMMAND(SILC_STATUS_OK);
2652
2653  out:
2654   silc_client_command_free(cmd);
2655 }
2656
2657
2658 /* CLOSE command. Close server connection to the remote server */
2659
2660 SILC_CLIENT_CMD_FUNC(close)
2661 {
2662   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
2663   SilcClientConnection conn = cmd->conn;
2664   SilcBuffer buffer;
2665   unsigned char port[4];
2666   SilcUInt32 tmp;
2667
2668   if (!cmd->conn) {
2669     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
2670     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
2671     goto out;
2672   }
2673
2674   if (cmd->argc < 2) {
2675     SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
2676         "Usage: /CLOSE <server> [<port>]");
2677     COMMAND_ERROR(SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
2678     goto out;
2679   }
2680
2681   if (cmd->argc == 3) {
2682     tmp = atoi(cmd->argv[2]);
2683     SILC_PUT32_MSB(tmp, port);
2684   }
2685
2686   if (cmd->argc == 3)
2687     buffer = silc_command_payload_encode_va(SILC_COMMAND_PRIV_CLOSE, 0, 2,
2688                                             1, cmd->argv[1],
2689                                             strlen(cmd->argv[1]),
2690                                             2, port, 4);
2691   else
2692     buffer = silc_command_payload_encode_va(SILC_COMMAND_PRIV_CLOSE, 0, 1,
2693                                             1, cmd->argv[1],
2694                                             strlen(cmd->argv[1]));
2695   silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
2696                           0, NULL, NULL, buffer->data, buffer->len, TRUE);
2697   silc_buffer_free(buffer);
2698
2699   /* Notify application */
2700   COMMAND(SILC_STATUS_OK);
2701
2702  out:
2703   silc_client_command_free(cmd);
2704 }
2705
2706 /* SHUTDOWN command. Shutdowns the server. */
2707
2708 SILC_CLIENT_CMD_FUNC(shutdown)
2709 {
2710   SilcClientCommandContext cmd = (SilcClientCommandContext)context;
2711
2712   if (!cmd->conn) {
2713     SILC_NOT_CONNECTED(cmd->client, cmd->conn);
2714     COMMAND_ERROR(SILC_STATUS_ERR_NOT_REGISTERED);
2715     goto out;
2716   }
2717
2718   /* Send the command */
2719   silc_client_command_send(cmd->client, cmd->conn,
2720                            SILC_COMMAND_PRIV_SHUTDOWN, 0, 0);
2721
2722   /* Notify application */
2723   COMMAND(SILC_STATUS_OK);
2724
2725  out:
2726   silc_client_command_free(cmd);
2727 }
2728
2729 /* Register all default commands provided by the client library for the
2730    application. */
2731
2732 void silc_client_commands_register(SilcClient client)
2733 {
2734   silc_list_init(client->internal->commands, struct SilcClientCommandStruct,
2735                  next);
2736
2737   SILC_CLIENT_CMD(whois, WHOIS, "WHOIS", 3);
2738   SILC_CLIENT_CMD(whowas, WHOWAS, "WHOWAS", 3);
2739   SILC_CLIENT_CMD(identify, IDENTIFY, "IDENTIFY", 3);
2740   SILC_CLIENT_CMD(nick, NICK, "NICK", 2);
2741   SILC_CLIENT_CMD(list, LIST, "LIST", 2);
2742   SILC_CLIENT_CMD(topic, TOPIC, "TOPIC", 3);
2743   SILC_CLIENT_CMD(invite, INVITE, "INVITE", 3);
2744   SILC_CLIENT_CMD(quit, QUIT, "QUIT", 2);
2745   SILC_CLIENT_CMD(kill, KILL, "KILL", 4);
2746   SILC_CLIENT_CMD(info, INFO, "INFO", 2);
2747   SILC_CLIENT_CMD(stats, STATS, "STATS", 0);
2748   SILC_CLIENT_CMD(ping, PING, "PING", 2);
2749   SILC_CLIENT_CMD(oper, OPER, "OPER", 3);
2750   SILC_CLIENT_CMD(join, JOIN, "JOIN", 9);
2751   SILC_CLIENT_CMD(motd, MOTD, "MOTD", 2);
2752   SILC_CLIENT_CMD(umode, UMODE, "UMODE", 2);
2753   SILC_CLIENT_CMD(cmode, CMODE, "CMODE", 6);
2754   SILC_CLIENT_CMD(cumode, CUMODE, "CUMODE", 9);
2755   SILC_CLIENT_CMD(kick, KICK, "KICK", 4);
2756   SILC_CLIENT_CMD(ban, BAN, "BAN", 3);
2757   SILC_CLIENT_CMD(detach, DETACH, "DETACH", 0);
2758   SILC_CLIENT_CMD(watch, WATCH, "WATCH", 3);
2759   SILC_CLIENT_CMD(silcoper, SILCOPER, "SILCOPER", 3);
2760   SILC_CLIENT_CMD(leave, LEAVE, "LEAVE", 2);
2761   SILC_CLIENT_CMD(users, USERS, "USERS", 2);
2762   SILC_CLIENT_CMD(getkey, GETKEY, "GETKEY", 2);
2763
2764   SILC_CLIENT_CMD(connect, PRIV_CONNECT, "CONNECT", 3);
2765   SILC_CLIENT_CMD(close, PRIV_CLOSE, "CLOSE", 3);
2766   SILC_CLIENT_CMD(shutdown, PRIV_SHUTDOWN, "SHUTDOWN", 1);
2767 }
2768
2769 /* Unregister all commands. */
2770
2771 void silc_client_commands_unregister(SilcClient client)
2772 {
2773   SILC_CLIENT_CMDU(whois, WHOIS, "WHOIS");
2774   SILC_CLIENT_CMDU(whowas, WHOWAS, "WHOWAS");
2775   SILC_CLIENT_CMDU(identify, IDENTIFY, "IDENTIFY");
2776   SILC_CLIENT_CMDU(nick, NICK, "NICK");
2777   SILC_CLIENT_CMDU(list, LIST, "LIST");
2778   SILC_CLIENT_CMDU(topic, TOPIC, "TOPIC");
2779   SILC_CLIENT_CMDU(invite, INVITE, "INVITE");
2780   SILC_CLIENT_CMDU(quit, QUIT, "QUIT");
2781   SILC_CLIENT_CMDU(kill, KILL, "KILL");
2782   SILC_CLIENT_CMDU(info, INFO, "INFO");
2783   SILC_CLIENT_CMDU(stats, STATS, "STATS");
2784   SILC_CLIENT_CMDU(ping, PING, "PING");
2785   SILC_CLIENT_CMDU(oper, OPER, "OPER");
2786   SILC_CLIENT_CMDU(join, JOIN, "JOIN");
2787   SILC_CLIENT_CMDU(motd, MOTD, "MOTD");
2788   SILC_CLIENT_CMDU(umode, UMODE, "UMODE");
2789   SILC_CLIENT_CMDU(cmode, CMODE, "CMODE");
2790   SILC_CLIENT_CMDU(cumode, CUMODE, "CUMODE");
2791   SILC_CLIENT_CMDU(kick, KICK, "KICK");
2792   SILC_CLIENT_CMDU(ban, BAN, "BAN");
2793   SILC_CLIENT_CMDU(detach, DETACH, "DETACH");
2794   SILC_CLIENT_CMDU(watch, WATCH, "WATCH");
2795   SILC_CLIENT_CMDU(silcoper, SILCOPER, "SILCOPER");
2796   SILC_CLIENT_CMDU(leave, LEAVE, "LEAVE");
2797   SILC_CLIENT_CMDU(users, USERS, "USERS");
2798   SILC_CLIENT_CMDU(getkey, GETKEY, "GETKEY");
2799
2800   SILC_CLIENT_CMDU(connect, PRIV_CONNECT, "CONNECT");
2801   SILC_CLIENT_CMDU(close, PRIV_CLOSE, "CLOSE");
2802   SILC_CLIENT_CMDU(shutdown, PRIV_SHUTDOWN, "SHUTDOWN");
2803 }
2804
2805 /**** Client side incoming command handling **********************************/
2806
2807 void silc_client_command_process_whois(SilcClient client,
2808                                        SilcSocketConnection sock,
2809                                        SilcCommandPayload payload,
2810                                        SilcArgumentPayload args);
2811
2812 /* Client is able to receive some command packets even though they are
2813    special case.  Server may send WHOIS command to the client to retrieve
2814    Requested Attributes information for WHOIS query the server is
2815    processing. This function currently handles only the WHOIS command,
2816    but if in the future for commands may arrive then this can be made
2817    to support other commands too. */
2818
2819 void silc_client_command_process(SilcClient client,
2820                                  SilcSocketConnection sock,
2821                                  SilcPacketContext *packet)
2822 {
2823   SilcCommandPayload payload;
2824   SilcCommand command;
2825   SilcArgumentPayload args;
2826
2827   /* Get command payload from packet */
2828   payload = silc_command_payload_parse(packet->buffer->data,
2829                                        packet->buffer->len);
2830   if (!payload) {
2831     /* Silently ignore bad reply packet */
2832     SILC_LOG_DEBUG(("Bad command packet"));
2833     return;
2834   }
2835
2836   /* Get arguments */
2837   args = silc_command_get_args(payload);
2838
2839   /* Get the command */
2840   command = silc_command_get(payload);
2841   switch (command) {
2842
2843   case SILC_COMMAND_WHOIS:
2844     /* Ignore everything if requested by application */
2845     if (client->internal->params->ignore_requested_attributes)
2846       break;
2847
2848     silc_client_command_process_whois(client, sock, payload, args);
2849     break;
2850
2851   default:
2852     break;
2853   }
2854
2855   silc_command_payload_free(payload);
2856 }
2857
2858 void silc_client_command_process_whois(SilcClient client,
2859                                        SilcSocketConnection sock,
2860                                        SilcCommandPayload payload,
2861                                        SilcArgumentPayload args)
2862 {
2863   SilcDList attrs;
2864   unsigned char *tmp;
2865   SilcUInt32 tmp_len;
2866   SilcBuffer buffer, packet;
2867
2868   SILC_LOG_DEBUG(("Received WHOIS command"));
2869
2870   /* Try to take the Requested Attributes */
2871   tmp = silc_argument_get_arg_type(args, 3, &tmp_len);
2872   if (!tmp)
2873     return;
2874
2875   attrs = silc_attribute_payload_parse(tmp, tmp_len);
2876   if (!attrs)
2877     return;
2878
2879   /* Process requested attributes */
2880   buffer = silc_client_attributes_process(client, sock, attrs);
2881   if (!buffer) {
2882     silc_attribute_payload_list_free(attrs);
2883     return;
2884   }
2885
2886   /* Send the attributes back */
2887   packet =
2888     silc_command_reply_payload_encode_va(SILC_COMMAND_WHOIS,
2889                                          SILC_STATUS_OK, 0,
2890                                          silc_command_get_ident(payload),
2891                                          1, 11, buffer->data, buffer->len);
2892   silc_client_packet_send(client, sock, SILC_PACKET_COMMAND_REPLY,
2893                           NULL, 0, NULL, NULL, packet->data,
2894                           packet->len, TRUE);
2895   silc_buffer_free(packet);
2896   silc_buffer_free(buffer);
2897 }