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