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