5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 1997 - 2002 Pekka Riikonen
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.
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.
21 #include "silcincludes.h"
22 #include "silcclient.h"
23 #include "client_internal.h"
25 #define SILC_NOT_CONNECTED(x, c) \
26 x->internal->ops->say((x), (c), SILC_CLIENT_MESSAGE_ERROR, \
27 "You are not connected to a server, use /SERVER to connect");
29 /* Command operation that is called at the end of all commands.
31 #define COMMAND cmd->client->internal->ops->command(cmd->client, cmd->conn, \
32 cmd, TRUE, cmd->command->cmd)
34 /* Error to application. Usage: COMMAND_ERROR; */
35 #define COMMAND_ERROR cmd->client->internal->ops->command(cmd->client, \
36 cmd->conn, cmd, FALSE, cmd->command->cmd)
38 #define SAY cmd->client->internal->ops->say
40 /* Generic function to send any command. The arguments must be sent already
41 encoded into correct form and in correct order. */
43 void silc_client_command_send(SilcClient client, SilcClientConnection conn,
44 SilcCommand command, SilcUInt16 ident,
52 packet = silc_command_payload_encode_vap(command, ident, argc, ap);
53 silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND,
54 NULL, 0, NULL, NULL, packet->data,
56 silc_buffer_free(packet);
59 /* Finds and returns a pointer to the command list. Return NULL if the
60 command is not found. */
62 SilcClientCommand silc_client_command_find(SilcClient client,
65 SilcClientCommand cmd;
67 silc_list_start(client->internal->commands);
68 while ((cmd = silc_list_get(client->internal->commands)) != SILC_LIST_END) {
69 if (cmd->name && !strcmp(cmd->name, name))
76 /* Calls the command (executes it). Application can call this after
77 it has allocated the SilcClientCommandContext with the function
78 silc_client_command_alloc and found the command from the client
79 library by calling silc_client_command_find. This will execute
82 void silc_client_command_call(SilcClientCommand command,
83 SilcClientCommandContext cmd)
85 (*command->command)((void *)cmd, NULL);
88 /* Add new pending command to be executed when reply to a command has been
89 received. The `reply_cmd' is the command that will call the `callback'
90 with `context' when reply has been received. If `ident is non-zero
91 the `callback' will be executed when received reply with command
92 identifier `ident'. */
94 void silc_client_command_pending(SilcClientConnection conn,
95 SilcCommand reply_cmd,
97 SilcCommandCb callback,
100 SilcClientCommandPending *reply;
102 reply = silc_calloc(1, sizeof(*reply));
103 reply->reply_cmd = reply_cmd;
104 reply->ident = ident;
105 reply->context = context;
106 reply->callback = callback;
107 silc_dlist_add(conn->pending_commands, reply);
110 /* Deletes pending command by reply command type. */
112 void silc_client_command_pending_del(SilcClientConnection conn,
113 SilcCommand reply_cmd,
116 SilcClientCommandPending *r;
118 silc_dlist_start(conn->pending_commands);
119 while ((r = silc_dlist_get(conn->pending_commands)) != SILC_LIST_END) {
120 if (r->reply_cmd == reply_cmd && r->ident == ident) {
121 silc_dlist_del(conn->pending_commands, r);
127 /* Checks for pending commands and marks callbacks to be called from
128 the command reply function. Returns TRUE if there were pending command. */
130 int silc_client_command_pending_check(SilcClientConnection conn,
131 SilcClientCommandReplyContext ctx,
135 SilcClientCommandPending *r;
137 silc_dlist_start(conn->pending_commands);
138 while ((r = silc_dlist_get(conn->pending_commands)) != SILC_LIST_END) {
139 if (r->reply_cmd == command && r->ident == ident) {
140 ctx->context = r->context;
141 ctx->callback = r->callback;
150 /* Allocate Command Context */
152 SilcClientCommandContext silc_client_command_alloc(void)
154 SilcClientCommandContext ctx = silc_calloc(1, sizeof(*ctx));
159 /* Free command context and its internals */
161 void silc_client_command_free(SilcClientCommandContext ctx)
164 SILC_LOG_DEBUG(("Command context %p refcnt %d->%d", ctx, ctx->users + 1,
166 if (ctx->users < 1) {
169 for (i = 0; i < ctx->argc; i++)
170 silc_free(ctx->argv[i]);
171 silc_free(ctx->argv_lens);
172 silc_free(ctx->argv_types);
177 /* Duplicate Command Context by adding reference counter. The context won't
178 be free'd untill it hits zero. */
180 SilcClientCommandContext silc_client_command_dup(SilcClientCommandContext ctx)
183 SILC_LOG_DEBUG(("Command context %p refcnt %d->%d", ctx, ctx->users - 1,
188 /* Command WHOIS. This command is used to query information about
191 SILC_CLIENT_CMD_FUNC(whois)
193 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
194 SilcClientConnection conn = cmd->conn;
198 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
203 /* Given without arguments fetches client's own information */
205 buffer = silc_id_payload_encode(cmd->conn->local_id, SILC_ID_CLIENT);
206 silc_client_command_send(cmd->client, cmd->conn, SILC_COMMAND_WHOIS,
208 1, 3, buffer->data, buffer->len);
209 silc_buffer_free(buffer);
213 buffer = silc_command_payload_encode(SILC_COMMAND_WHOIS,
214 cmd->argc - 1, ++cmd->argv,
215 ++cmd->argv_lens, ++cmd->argv_types,
217 silc_client_packet_send(cmd->client, cmd->conn->sock,
218 SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
219 buffer->data, buffer->len, TRUE);
220 silc_buffer_free(buffer);
225 /* Notify application */
229 silc_client_command_free(cmd);
232 /* Command WHOWAS. This command is used to query history information about
233 specific user that used to exist in the network. */
235 SILC_CLIENT_CMD_FUNC(whowas)
237 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
238 SilcClientConnection conn = cmd->conn;
242 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
247 if (cmd->argc < 2 || cmd->argc > 3) {
248 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
249 "Usage: /WHOWAS <nickname>[@<server>] [<count>]");
254 buffer = silc_command_payload_encode(SILC_COMMAND_WHOWAS,
255 cmd->argc - 1, ++cmd->argv,
256 ++cmd->argv_lens, ++cmd->argv_types,
258 silc_client_packet_send(cmd->client, cmd->conn->sock,
259 SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
260 buffer->data, buffer->len, TRUE);
261 silc_buffer_free(buffer);
266 /* Notify application */
270 silc_client_command_free(cmd);
273 /* Command IDENTIFY. This command is used to query information about
274 specific user, especially ID's.
276 NOTE: This command is used only internally by the client library
277 and application MUST NOT call this command directly. */
279 SILC_CLIENT_CMD_FUNC(identify)
281 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
282 SilcClientConnection conn = cmd->conn;
286 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
290 if (cmd->argc < 2 || cmd->argc > 3)
294 buffer = silc_command_payload_encode_va(SILC_COMMAND_IDENTIFY,
295 ++conn->cmd_ident, 1,
299 buffer = silc_command_payload_encode_va(SILC_COMMAND_IDENTIFY,
300 ++conn->cmd_ident, 2,
306 silc_client_packet_send(cmd->client, cmd->conn->sock,
307 SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
308 buffer->data, buffer->len, TRUE);
309 silc_buffer_free(buffer);
312 silc_client_command_free(cmd);
315 /* Pending callbcak that will be called after the NICK command was
316 replied by the server. This sets the nickname if there were no
319 SILC_CLIENT_CMD_FUNC(nick_change)
321 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
322 SilcClientConnection conn = cmd->conn;
323 SilcClientCommandReplyContext reply =
324 (SilcClientCommandReplyContext)context2;
325 SilcCommandStatus status;
327 silc_command_get_status(reply->payload, &status, NULL);
328 if (status == SILC_STATUS_OK) {
329 /* Set the nickname */
330 silc_idcache_del_by_context(conn->client_cache, conn->local_entry);
332 silc_free(conn->nickname);
333 conn->nickname = strdup(cmd->argv[1]);
334 conn->local_entry->nickname = conn->nickname;
335 silc_client_nickname_format(cmd->client, conn, conn->local_entry);
336 silc_idcache_add(conn->client_cache, strdup(cmd->argv[1]),
337 conn->local_entry->id, conn->local_entry, 0, NULL);
343 silc_client_command_free(cmd);
346 /* Command NICK. Shows current nickname/sets new nickname on current
349 SILC_CLIENT_CMD_FUNC(nick)
351 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
352 SilcClientConnection conn = cmd->conn;
356 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
362 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
363 "Usage: /NICK <nickname>");
368 if (!strcmp(conn->nickname, cmd->argv[1]))
371 /* Show current nickname */
374 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
375 "Your nickname is %s on server %s",
376 conn->nickname, conn->remote_host);
378 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
379 "Your nickname is %s", conn->nickname);
386 if (cmd->argv_lens[1] > 128)
387 cmd->argv_lens[1] = 128;
389 /* Send the NICK command */
390 buffer = silc_command_payload_encode(SILC_COMMAND_NICK, 1,
394 ++cmd->conn->cmd_ident);
395 silc_client_packet_send(cmd->client, cmd->conn->sock,
396 SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
397 buffer->data, buffer->len, TRUE);
398 silc_buffer_free(buffer);
400 /* Register pending callback that will actually set the new nickname
401 if there were no errors returned by the server. */
402 silc_client_command_pending(conn, SILC_COMMAND_NICK,
403 cmd->conn->cmd_ident,
404 silc_client_command_nick_change,
405 silc_client_command_dup(cmd));
409 silc_client_command_free(cmd);
412 /* Command LIST. Lists channels on the current server. */
414 SILC_CLIENT_CMD_FUNC(list)
416 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
417 SilcClientConnection conn = cmd->conn;
418 SilcIDCacheEntry id_cache = NULL;
419 SilcChannelEntry channel;
420 SilcBuffer buffer, idp = NULL;
424 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
429 if (cmd->argc == 2) {
432 /* Get the Channel ID of the channel */
433 if (silc_idcache_find_by_name_one(conn->channel_cache, name, &id_cache)) {
434 channel = (SilcChannelEntry)id_cache->context;
435 idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
440 buffer = silc_command_payload_encode_va(SILC_COMMAND_LIST,
441 ++conn->cmd_ident, 0);
443 buffer = silc_command_payload_encode_va(SILC_COMMAND_LIST,
444 ++conn->cmd_ident, 1,
445 1, idp->data, idp->len);
447 silc_client_packet_send(cmd->client, cmd->conn->sock,
448 SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
449 buffer->data, buffer->len, TRUE);
450 silc_buffer_free(buffer);
452 silc_buffer_free(idp);
454 /* Notify application */
458 silc_client_command_free(cmd);
461 /* Command TOPIC. Sets/shows topic on a channel. */
463 SILC_CLIENT_CMD_FUNC(topic)
465 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
466 SilcClientConnection conn = cmd->conn;
467 SilcIDCacheEntry id_cache = NULL;
468 SilcChannelEntry channel;
469 SilcBuffer buffer, idp;
473 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
478 if (cmd->argc < 2 || cmd->argc > 3) {
479 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
480 "Usage: /TOPIC <channel> [<topic>]");
485 if (cmd->argv[1][0] == '*') {
486 if (!conn->current_channel) {
487 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
488 "You are not on any channel");
492 name = conn->current_channel->channel_name;
497 if (!conn->current_channel) {
498 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
499 "You are not on that channel");
504 /* Get the Channel ID of the channel */
505 if (!silc_idcache_find_by_name_one(conn->channel_cache, name, &id_cache)) {
506 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
507 "You are not on that channel");
512 channel = (SilcChannelEntry)id_cache->context;
514 /* Send TOPIC command to the server */
515 idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
517 buffer = silc_command_payload_encode_va(SILC_COMMAND_TOPIC,
518 ++conn->cmd_ident, 2,
519 1, idp->data, idp->len,
521 strlen(cmd->argv[2]));
523 buffer = silc_command_payload_encode_va(SILC_COMMAND_TOPIC,
524 ++conn->cmd_ident, 1,
525 1, idp->data, idp->len);
526 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
527 0, NULL, NULL, buffer->data, buffer->len, TRUE);
528 silc_buffer_free(buffer);
529 silc_buffer_free(idp);
531 /* Notify application */
535 silc_client_command_free(cmd);
538 /* Command INVITE. Invites specific client to join a channel. This is
539 also used to mange the invite list of the channel. */
541 SILC_CLIENT_CMD_FUNC(invite)
543 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
544 SilcClient client = cmd->client;
545 SilcClientConnection conn = cmd->conn;
546 SilcClientEntry client_entry = NULL;
547 SilcChannelEntry channel;
548 SilcBuffer buffer, clidp, chidp;
550 char *nickname = NULL, *name;
554 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
560 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
561 "Usage: /INVITE <channel> [<nickname>[@server>]"
562 "[+|-[<nickname>[@<server>[!<username>[@hostname>]]]]]");
567 if (cmd->argv[1][0] == '*') {
568 if (!conn->current_channel) {
569 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
570 "You are not on any channel");
575 channel = conn->current_channel;
579 channel = silc_client_get_channel(cmd->client, conn, name);
581 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
582 "You are on that channel");
588 /* Parse the typed nickname. */
589 if (cmd->argc == 3) {
590 if (cmd->argv[2][0] != '+' && cmd->argv[2][0] != '-') {
591 if (client->internal->params->nickname_parse)
592 client->internal->params->nickname_parse(cmd->argv[2], &nickname);
594 nickname = strdup(cmd->argv[2]);
596 /* Find client entry */
597 client_entry = silc_idlist_get_client(client, conn, nickname,
605 /* Client entry not found, it was requested thus mark this to be
607 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY,
609 silc_client_command_invite,
610 silc_client_command_dup(cmd));
615 invite = cmd->argv[2];
617 if (cmd->argv[2][0] == '+')
624 /* Send the command */
625 chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
627 clidp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
628 buffer = silc_command_payload_encode_va(SILC_COMMAND_INVITE,
629 ++conn->cmd_ident, 3,
630 1, chidp->data, chidp->len,
631 2, clidp->data, clidp->len,
632 type, invite, invite ?
634 silc_buffer_free(clidp);
636 buffer = silc_command_payload_encode_va(SILC_COMMAND_INVITE,
637 ++conn->cmd_ident, 2,
638 1, chidp->data, chidp->len,
639 type, invite, invite ?
643 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
644 0, NULL, NULL, buffer->data, buffer->len, TRUE);
645 silc_buffer_free(buffer);
646 silc_buffer_free(chidp);
648 /* Notify application */
653 silc_client_command_free(cmd);
658 SilcClientConnection conn;
661 SILC_TASK_CALLBACK(silc_client_command_quit_cb)
663 QuitInternal q = (QuitInternal)context;
665 /* Close connection */
666 q->client->internal->ops->disconnect(q->client, q->conn);
667 silc_client_close_connection(q->client, q->conn->sock->user_data);
672 /* Command QUIT. Closes connection with current server. */
674 SILC_CLIENT_CMD_FUNC(quit)
676 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
681 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
687 buffer = silc_command_payload_encode(SILC_COMMAND_QUIT, cmd->argc - 1,
688 &cmd->argv[1], &cmd->argv_lens[1],
689 &cmd->argv_types[1], 0);
691 buffer = silc_command_payload_encode(SILC_COMMAND_QUIT, 0,
692 NULL, NULL, NULL, 0);
693 silc_client_packet_send(cmd->client, cmd->conn->sock, SILC_PACKET_COMMAND,
695 buffer->data, buffer->len, TRUE);
696 silc_buffer_free(buffer);
698 q = silc_calloc(1, sizeof(*q));
699 q->client = cmd->client;
702 /* Sleep for a while */
705 /* We quit the connection with little timeout */
706 silc_schedule_task_add(cmd->client->schedule, cmd->conn->sock->sock,
707 silc_client_command_quit_cb, (void *)q,
708 1, 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
710 /* Notify application */
714 silc_client_command_free(cmd);
717 /* Timeout callback to remove the killed client from cache */
719 SILC_TASK_CALLBACK(silc_client_command_kill_remove_later)
721 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
722 SilcClient client = cmd->client;
723 SilcClientConnection conn = cmd->conn;
724 SilcClientEntry target;
725 char *nickname = NULL;
727 /* Parse the typed nickname. */
728 if (client->internal->params->nickname_parse)
729 client->internal->params->nickname_parse(cmd->argv[1], &nickname);
731 nickname = strdup(cmd->argv[1]);
733 /* Get the target client */
734 target = silc_idlist_get_client(cmd->client, conn, nickname,
735 cmd->argv[1], FALSE);
737 /* Remove the client from all channels and free it */
738 silc_client_del_client(client, conn, target);
741 silc_client_command_free(cmd);
744 /* Kill command's pending command callback to actually remove the killed
745 client from our local cache. */
747 SILC_CLIENT_CMD_FUNC(kill_remove)
749 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
750 SilcClientCommandReplyContext reply =
751 (SilcClientCommandReplyContext)context2;
752 SilcCommandStatus status;
754 silc_command_get_status(reply->payload, &status, NULL);
755 if (status == SILC_STATUS_OK) {
756 /* Remove with timeout */
757 silc_schedule_task_add(cmd->client->schedule, cmd->conn->sock->sock,
758 silc_client_command_kill_remove_later, context,
759 1, 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
763 silc_client_command_free(cmd);
766 /* Command KILL. Router operator can use this command to remove an client
767 fromthe SILC Network. */
769 SILC_CLIENT_CMD_FUNC(kill)
771 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
772 SilcClient client = cmd->client;
773 SilcClientConnection conn = cmd->conn;
774 SilcBuffer buffer, idp;
775 SilcClientEntry target;
776 char *nickname = NULL;
779 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
785 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
786 "Usage: /KILL <nickname> [<comment>]");
791 /* Parse the typed nickname. */
792 if (client->internal->params->nickname_parse)
793 client->internal->params->nickname_parse(cmd->argv[1], &nickname);
795 nickname = strdup(cmd->argv[1]);
797 /* Get the target client */
798 target = silc_idlist_get_client(cmd->client, conn, nickname,
806 /* Client entry not found, it was requested thus mark this to be
808 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY,
810 silc_client_command_kill,
811 silc_client_command_dup(cmd));
816 /* Send the KILL command to the server */
817 idp = silc_id_payload_encode(target->id, SILC_ID_CLIENT);
819 buffer = silc_command_payload_encode_va(SILC_COMMAND_KILL,
820 ++conn->cmd_ident, 1,
821 1, idp->data, idp->len);
823 buffer = silc_command_payload_encode_va(SILC_COMMAND_KILL,
824 ++conn->cmd_ident, 2,
825 1, idp->data, idp->len,
827 strlen(cmd->argv[2]));
828 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
829 0, NULL, NULL, buffer->data, buffer->len, TRUE);
830 silc_buffer_free(buffer);
831 silc_buffer_free(idp);
833 /* Notify application */
836 /* Register a pending callback that will actually remove the killed
837 client from our cache. */
838 silc_client_command_pending(conn, SILC_COMMAND_KILL, conn->cmd_ident,
839 silc_client_command_kill_remove,
840 silc_client_command_dup(cmd));
844 silc_client_command_free(cmd);
847 /* Command INFO. Request information about specific server. If specific
848 server is not provided the current server is used. */
850 SILC_CLIENT_CMD_FUNC(info)
852 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
853 SilcClientConnection conn = cmd->conn;
858 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
864 name = strdup(cmd->argv[1]);
866 /* Send the command */
868 buffer = silc_command_payload_encode_va(SILC_COMMAND_INFO, 0, 1,
869 1, name, strlen(name));
871 buffer = silc_command_payload_encode(SILC_COMMAND_INFO, 0,
872 NULL, NULL, NULL, 0);
873 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
874 0, NULL, NULL, buffer->data, buffer->len, TRUE);
875 silc_buffer_free(buffer);
879 /* Notify application */
883 silc_client_command_free(cmd);
886 /* Command PING. Sends ping to server. This is used to test the
887 communication channel. */
889 SILC_CLIENT_CMD_FUNC(ping)
891 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
892 SilcClientConnection conn = cmd->conn;
898 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
903 /* Send the command */
904 buffer = silc_command_payload_encode_va(SILC_COMMAND_PING, 0, 1,
905 1, conn->remote_id_data,
906 silc_id_get_len(conn->remote_id,
908 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
909 0, NULL, NULL, buffer->data, buffer->len, TRUE);
910 silc_buffer_free(buffer);
912 id = silc_id_str2id(conn->remote_id_data, conn->remote_id_data_len,
915 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
920 /* Start counting time */
921 for (i = 0; i < conn->ping_count; i++) {
922 if (conn->ping[i].dest_id == NULL) {
923 conn->ping[i].start_time = time(NULL);
924 conn->ping[i].dest_id = id;
925 conn->ping[i].dest_name = strdup(conn->remote_host);
929 if (i >= conn->ping_count) {
930 i = conn->ping_count;
931 conn->ping = silc_realloc(conn->ping, sizeof(*conn->ping) * (i + 1));
932 conn->ping[i].start_time = time(NULL);
933 conn->ping[i].dest_id = id;
934 conn->ping[i].dest_name = strdup(conn->remote_host);
938 /* Notify application */
942 silc_client_command_free(cmd);
945 /* Command JOIN. Joins to a channel. */
947 SILC_CLIENT_CMD_FUNC(join)
949 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
950 SilcClientConnection conn = cmd->conn;
951 SilcChannelEntry channel;
952 SilcBuffer buffer, idp, auth = NULL;
953 char *name, *passphrase = NULL, *pu8, *cipher = NULL, *hmac = NULL;
954 int i, passphrase_len = 0;
957 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
967 /* See if we have joined to the requested channel already */
968 channel = silc_client_get_channel(cmd->client, conn, cmd->argv[1]);
969 if (channel && silc_client_on_channel(channel, conn->local_entry))
972 idp = silc_id_payload_encode(conn->local_id, SILC_ID_CLIENT);
974 if (cmd->argv_lens[1] > 256)
975 cmd->argv_lens[1] = 256;
979 for (i = 2; i < cmd->argc; i++) {
980 if (!strcasecmp(cmd->argv[i], "-cipher") && cmd->argc > i + 1) {
981 cipher = cmd->argv[i + 1];
983 } else if (!strcasecmp(cmd->argv[i], "-hmac") && cmd->argc > i + 1) {
984 hmac = cmd->argv[i + 1];
986 } else if (!strcasecmp(cmd->argv[i], "-founder") && cmd->argc > i + 1) {
987 if (!strcasecmp(cmd->argv[i + 1], "-pubkey")) {
988 auth = silc_auth_public_key_auth_generate(cmd->client->public_key,
989 cmd->client->private_key,
990 cmd->client->rng, conn->hash,
994 auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
996 cmd->argv_lens[i + 1]);
1000 /* Passphrases must be UTF-8 encoded, so encode if it is not */
1001 if (!silc_utf8_valid(cmd->argv[i], cmd->argv_lens[i])) {
1002 passphrase_len = silc_utf8_encoded_len(cmd->argv[i],
1003 cmd->argv_lens[i], 0);
1004 pu8 = silc_calloc(passphrase_len, sizeof(*pu8));
1005 passphrase_len = silc_utf8_encode(cmd->argv[i], cmd->argv_lens[i],
1006 0, pu8, passphrase_len);
1009 passphrase = strdup(cmd->argv[i]);
1010 passphrase_len = cmd->argv_lens[i];
1015 /* Send JOIN command to the server */
1017 silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 6,
1018 1, name, strlen(name),
1019 2, idp->data, idp->len,
1020 3, passphrase, passphrase_len,
1021 4, cipher, cipher ? strlen(cipher) : 0,
1022 5, hmac, hmac ? strlen(hmac) : 0,
1023 6, auth ? auth->data : NULL,
1024 auth ? auth->len : 0);
1025 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1026 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1027 silc_buffer_free(buffer);
1028 silc_buffer_free(idp);
1030 silc_buffer_free(auth);
1031 silc_free(passphrase);
1033 /* Notify application */
1037 silc_client_command_free(cmd);
1040 /* MOTD command. Requests motd from server. */
1042 SILC_CLIENT_CMD_FUNC(motd)
1044 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1045 SilcClientConnection conn = cmd->conn;
1049 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1054 if (cmd->argc < 1 || cmd->argc > 2) {
1055 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1056 "Usage: /MOTD [<server>]");
1061 /* Send TOPIC command to the server */
1063 buffer = silc_command_payload_encode_va(SILC_COMMAND_MOTD, 0, 1,
1064 1, conn->remote_host,
1065 strlen(conn->remote_host));
1067 buffer = silc_command_payload_encode_va(SILC_COMMAND_MOTD, 0, 1,
1070 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1071 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1072 silc_buffer_free(buffer);
1074 /* Notify application */
1078 silc_client_command_free(cmd);
1081 /* UMODE. Set/unset user mode in SILC. This is used mainly to unset the
1082 modes as client cannot set itself server/router operator privileges. */
1084 SILC_CLIENT_CMD_FUNC(umode)
1086 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1087 SilcClientConnection conn = cmd->conn;
1088 SilcBuffer buffer, idp;
1089 unsigned char *cp, modebuf[4];
1090 SilcUInt32 mode, add, len;
1094 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1099 if (cmd->argc < 2) {
1100 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1101 "Usage: /UMODE +|-<modes>");
1106 mode = conn->local_entry->mode;
1108 /* Are we adding or removing mode */
1109 if (cmd->argv[1][0] == '-')
1115 cp = cmd->argv[1] + 1;
1117 for (i = 0; i < len; i++) {
1122 mode |= SILC_UMODE_SERVER_OPERATOR;
1123 mode |= SILC_UMODE_ROUTER_OPERATOR;
1125 mode = SILC_UMODE_NONE;
1130 mode |= SILC_UMODE_SERVER_OPERATOR;
1132 mode &= ~SILC_UMODE_SERVER_OPERATOR;
1136 mode |= SILC_UMODE_ROUTER_OPERATOR;
1138 mode &= ~SILC_UMODE_ROUTER_OPERATOR;
1142 mode |= SILC_UMODE_GONE;
1144 mode &= ~SILC_UMODE_GONE;
1148 mode |= SILC_UMODE_INDISPOSED;
1150 mode &= ~SILC_UMODE_INDISPOSED;
1154 mode |= SILC_UMODE_BUSY;
1156 mode &= ~SILC_UMODE_BUSY;
1160 mode |= SILC_UMODE_PAGE;
1162 mode &= ~SILC_UMODE_PAGE;
1166 mode |= SILC_UMODE_HYPER;
1168 mode &= ~SILC_UMODE_HYPER;
1172 mode |= SILC_UMODE_ROBOT;
1174 mode &= ~SILC_UMODE_ROBOT;
1178 mode |= SILC_UMODE_BLOCK_PRIVMSG;
1180 mode &= ~SILC_UMODE_BLOCK_PRIVMSG;
1189 idp = silc_id_payload_encode(conn->local_id, SILC_ID_CLIENT);
1190 SILC_PUT32_MSB(mode, modebuf);
1192 /* Send the command packet. We support sending only one mode at once
1193 that requires an argument. */
1195 silc_command_payload_encode_va(SILC_COMMAND_UMODE, ++conn->cmd_ident, 2,
1196 1, idp->data, idp->len,
1197 2, modebuf, sizeof(modebuf));
1198 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1199 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1200 silc_buffer_free(buffer);
1201 silc_buffer_free(idp);
1203 /* Notify application */
1207 silc_client_command_free(cmd);
1210 /* CMODE command. Sets channel mode. Modes that does not require any arguments
1211 can be set several at once. Those modes that require argument must be set
1212 separately (unless set with modes that does not require arguments). */
1214 SILC_CLIENT_CMD_FUNC(cmode)
1216 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1217 SilcClientConnection conn = cmd->conn;
1218 SilcChannelEntry channel;
1219 SilcBuffer buffer, chidp, auth = NULL;
1220 unsigned char *name, *cp, modebuf[4], tmp[4], *arg = NULL;
1221 SilcUInt32 mode, add, type, len, arg_len = 0;
1225 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1230 if (cmd->argc < 3) {
1231 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1232 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1237 if (cmd->argv[1][0] == '*') {
1238 if (!conn->current_channel) {
1239 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1240 "You are not on any channel");
1245 channel = conn->current_channel;
1247 name = cmd->argv[1];
1249 channel = silc_client_get_channel(cmd->client, conn, name);
1251 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1252 "You are on that channel");
1258 mode = channel->mode;
1260 /* Are we adding or removing mode */
1261 if (cmd->argv[2][0] == '-')
1266 /* Argument type to be sent to server */
1270 cp = cmd->argv[2] + 1;
1272 for (i = 0; i < len; i++) {
1276 mode |= SILC_CHANNEL_MODE_PRIVATE;
1278 mode &= ~SILC_CHANNEL_MODE_PRIVATE;
1282 mode |= SILC_CHANNEL_MODE_SECRET;
1284 mode &= ~SILC_CHANNEL_MODE_SECRET;
1288 mode |= SILC_CHANNEL_MODE_PRIVKEY;
1290 mode &= ~SILC_CHANNEL_MODE_PRIVKEY;
1294 mode |= SILC_CHANNEL_MODE_INVITE;
1296 mode &= ~SILC_CHANNEL_MODE_INVITE;
1300 mode |= SILC_CHANNEL_MODE_TOPIC;
1302 mode &= ~SILC_CHANNEL_MODE_TOPIC;
1306 mode |= SILC_CHANNEL_MODE_SILENCE_USERS;
1308 mode &= ~SILC_CHANNEL_MODE_SILENCE_USERS;
1312 mode |= SILC_CHANNEL_MODE_SILENCE_OPERS;
1314 mode &= ~SILC_CHANNEL_MODE_SILENCE_OPERS;
1319 mode |= SILC_CHANNEL_MODE_ULIMIT;
1321 if (cmd->argc < 4) {
1322 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1323 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1327 ll = atoi(cmd->argv[3]);
1328 SILC_PUT32_MSB(ll, tmp);
1332 mode &= ~SILC_CHANNEL_MODE_ULIMIT;
1337 mode |= SILC_CHANNEL_MODE_PASSPHRASE;
1339 if (cmd->argc < 4) {
1340 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1341 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1346 arg_len = cmd->argv_lens[3];
1348 mode &= ~SILC_CHANNEL_MODE_PASSPHRASE;
1353 mode |= SILC_CHANNEL_MODE_CIPHER;
1355 if (cmd->argc < 4) {
1356 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1357 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1362 arg_len = cmd->argv_lens[3];
1364 mode &= ~SILC_CHANNEL_MODE_CIPHER;
1369 mode |= SILC_CHANNEL_MODE_HMAC;
1371 if (cmd->argc < 4) {
1372 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1373 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1378 arg_len = cmd->argv_lens[3];
1380 mode &= ~SILC_CHANNEL_MODE_HMAC;
1385 mode |= SILC_CHANNEL_MODE_FOUNDER_AUTH;
1388 if (cmd->argc < 4) {
1389 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1390 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1395 if (!strcasecmp(cmd->argv[3], "-pubkey")) {
1396 auth = silc_auth_public_key_auth_generate(cmd->client->public_key,
1397 cmd->client->private_key,
1403 auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
1404 cmd->argv[3], cmd->argv_lens[3]);
1408 arg_len = auth->len;
1410 mode &= ~SILC_CHANNEL_MODE_FOUNDER_AUTH;
1420 chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1421 SILC_PUT32_MSB(mode, modebuf);
1423 /* Send the command packet. We support sending only one mode at once
1424 that requires an argument. */
1427 silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 3,
1428 1, chidp->data, chidp->len,
1429 2, modebuf, sizeof(modebuf),
1430 type, arg, arg_len);
1433 silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 2,
1434 1, chidp->data, chidp->len,
1435 2, modebuf, sizeof(modebuf));
1438 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1439 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1440 silc_buffer_free(buffer);
1441 silc_buffer_free(chidp);
1443 silc_buffer_free(auth);
1445 /* Notify application */
1449 silc_client_command_free(cmd);
1452 /* CUMODE command. Changes client's mode on a channel. */
1454 SILC_CLIENT_CMD_FUNC(cumode)
1456 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1457 SilcClient client = cmd->client;
1458 SilcClientConnection conn = cmd->conn;
1459 SilcChannelEntry channel;
1460 SilcChannelUser chu;
1461 SilcClientEntry client_entry;
1462 SilcBuffer buffer, clidp, chidp, auth = NULL;
1463 unsigned char *name, *cp, modebuf[4];
1464 SilcUInt32 mode = 0, add, len;
1465 char *nickname = NULL;
1469 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1474 if (cmd->argc < 4) {
1475 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1476 "Usage: /CUMODE <channel> +|-<modes> <nickname>[@<server>]");
1481 if (cmd->argv[1][0] == '*') {
1482 if (!conn->current_channel) {
1483 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1484 "You are not on any channel");
1489 channel = conn->current_channel;
1491 name = cmd->argv[1];
1493 channel = silc_client_get_channel(cmd->client, conn, name);
1495 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1496 "You are on that channel");
1502 /* Parse the typed nickname. */
1503 if (client->internal->params->nickname_parse)
1504 client->internal->params->nickname_parse(cmd->argv[3], &nickname);
1506 nickname = strdup(cmd->argv[3]);
1508 /* Find client entry */
1509 client_entry = silc_idlist_get_client(cmd->client, conn, nickname,
1510 cmd->argv[3], TRUE);
1511 if (!client_entry) {
1517 /* Client entry not found, it was requested thus mark this to be
1519 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY,
1521 silc_client_command_cumode,
1522 silc_client_command_dup(cmd));
1527 /* Get the current mode */
1528 chu = silc_client_on_channel(channel, client_entry);
1532 /* Are we adding or removing mode */
1533 if (cmd->argv[2][0] == '-')
1539 cp = cmd->argv[2] + 1;
1541 for (i = 0; i < len; i++) {
1545 mode |= SILC_CHANNEL_UMODE_CHANFO;
1546 mode |= SILC_CHANNEL_UMODE_CHANOP;
1547 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
1549 mode = SILC_CHANNEL_UMODE_NONE;
1554 if (cmd->argc == 5) {
1555 if (!strcasecmp(cmd->argv[4], "-pubkey")) {
1556 auth = silc_auth_public_key_auth_generate(cmd->client->public_key,
1557 cmd->client->private_key,
1563 auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
1564 cmd->argv[4], cmd->argv_lens[4]);
1567 mode |= SILC_CHANNEL_UMODE_CHANFO;
1569 mode &= ~SILC_CHANNEL_UMODE_CHANFO;
1574 mode |= SILC_CHANNEL_UMODE_CHANOP;
1576 mode &= ~SILC_CHANNEL_UMODE_CHANOP;
1580 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
1582 mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
1591 chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1592 SILC_PUT32_MSB(mode, modebuf);
1593 clidp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
1595 /* Send the command packet. We support sending only one mode at once
1596 that requires an argument. */
1597 buffer = silc_command_payload_encode_va(SILC_COMMAND_CUMODE, 0,
1599 1, chidp->data, chidp->len,
1601 3, clidp->data, clidp->len,
1602 4, auth ? auth->data : NULL,
1603 auth ? auth->len : 0);
1605 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1606 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1607 silc_buffer_free(buffer);
1608 silc_buffer_free(chidp);
1609 silc_buffer_free(clidp);
1611 silc_buffer_free(auth);
1613 /* Notify application */
1617 silc_free(nickname);
1618 silc_client_command_free(cmd);
1621 /* KICK command. Kicks a client out of channel. */
1623 SILC_CLIENT_CMD_FUNC(kick)
1625 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1626 SilcClient client = cmd->client;
1627 SilcClientConnection conn = cmd->conn;
1628 SilcIDCacheEntry id_cache = NULL;
1629 SilcChannelEntry channel;
1630 SilcBuffer buffer, idp, idp2;
1631 SilcClientEntry target;
1633 char *nickname = NULL;
1636 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1641 if (cmd->argc < 3) {
1642 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1643 "Usage: /KICK <channel> <nickname> [<comment>]");
1648 if (cmd->argv[1][0] == '*') {
1649 if (!conn->current_channel) {
1650 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1651 "You are not on any channel");
1655 name = conn->current_channel->channel_name;
1657 name = cmd->argv[1];
1660 if (!conn->current_channel) {
1661 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1662 "You are not on that channel");
1667 /* Get the Channel ID of the channel */
1668 if (!silc_idcache_find_by_name_one(conn->channel_cache, name, &id_cache)) {
1669 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1670 "You are not on that channel");
1675 channel = (SilcChannelEntry)id_cache->context;
1677 /* Parse the typed nickname. */
1678 if (client->internal->params->nickname_parse)
1679 client->internal->params->nickname_parse(cmd->argv[2], &nickname);
1681 nickname = strdup(cmd->argv[2]);
1683 /* Get the target client */
1684 target = silc_idlist_get_client(cmd->client, conn, nickname,
1685 cmd->argv[2], FALSE);
1687 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1688 "No such client: %s", cmd->argv[2]);
1693 /* Send KICK command to the server */
1694 idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1695 idp2 = silc_id_payload_encode(target->id, SILC_ID_CLIENT);
1697 buffer = silc_command_payload_encode_va(SILC_COMMAND_KICK, 0, 2,
1698 1, idp->data, idp->len,
1699 2, idp2->data, idp2->len);
1701 buffer = silc_command_payload_encode_va(SILC_COMMAND_KICK, 0, 3,
1702 1, idp->data, idp->len,
1703 2, idp2->data, idp2->len,
1705 strlen(cmd->argv[3]));
1706 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1707 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1708 silc_buffer_free(buffer);
1709 silc_buffer_free(idp);
1710 silc_buffer_free(idp2);
1712 /* Notify application */
1716 silc_free(nickname);
1717 silc_client_command_free(cmd);
1720 static void silc_client_command_oper_send(unsigned char *data,
1721 SilcUInt32 data_len, void *context)
1723 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1724 SilcClientConnection conn = cmd->conn;
1725 SilcBuffer buffer, auth;
1727 if (cmd->argc >= 3) {
1728 /* Encode the public key authentication payload */
1729 auth = silc_auth_public_key_auth_generate(cmd->client->public_key,
1730 cmd->client->private_key,
1731 cmd->client->rng, conn->hash,
1735 /* Encode the password authentication payload */
1736 auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
1740 buffer = silc_command_payload_encode_va(SILC_COMMAND_OPER, 0, 2,
1742 strlen(cmd->argv[1]),
1743 2, auth->data, auth->len);
1744 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1745 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1747 silc_buffer_free(buffer);
1748 silc_buffer_free(auth);
1750 /* Notify application */
1754 /* OPER command. Used to obtain server operator privileges. */
1756 SILC_CLIENT_CMD_FUNC(oper)
1758 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1759 SilcClientConnection conn = cmd->conn;
1762 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1767 if (cmd->argc < 2) {
1768 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1769 "Usage: /OPER <username> [-pubkey]");
1774 if (cmd->argc < 3) {
1775 /* Get passphrase */
1776 cmd->client->internal->ops->ask_passphrase(cmd->client, conn,
1777 silc_client_command_oper_send,
1782 silc_client_command_oper_send(NULL, 0, context);
1785 silc_client_command_free(cmd);
1788 static void silc_client_command_silcoper_send(unsigned char *data,
1789 SilcUInt32 data_len,
1792 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1793 SilcClientConnection conn = cmd->conn;
1794 SilcBuffer buffer, auth;
1796 if (cmd->argc >= 3) {
1797 /* Encode the public key authentication payload */
1798 auth = silc_auth_public_key_auth_generate(cmd->client->public_key,
1799 cmd->client->private_key,
1800 cmd->client->rng, conn->hash,
1804 /* Encode the password authentication payload */
1805 auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
1809 buffer = silc_command_payload_encode_va(SILC_COMMAND_SILCOPER, 0, 2,
1811 strlen(cmd->argv[1]),
1812 2, auth->data, auth->len);
1813 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1814 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1816 silc_buffer_free(buffer);
1817 silc_buffer_free(auth);
1819 /* Notify application */
1823 /* SILCOPER command. Used to obtain router operator privileges. */
1825 SILC_CLIENT_CMD_FUNC(silcoper)
1827 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1828 SilcClientConnection conn = cmd->conn;
1831 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1836 if (cmd->argc < 2) {
1837 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1838 "Usage: /SILCOPER <username> [-pubkey]");
1843 if (cmd->argc < 3) {
1844 /* Get passphrase */
1845 cmd->client->internal->ops->ask_passphrase(cmd->client, conn,
1846 silc_client_command_silcoper_send,
1851 silc_client_command_silcoper_send(NULL, 0, context);
1854 silc_client_command_free(cmd);
1857 /* Command BAN. This is used to manage the ban list of the channel. */
1859 SILC_CLIENT_CMD_FUNC(ban)
1861 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1862 SilcClientConnection conn = cmd->conn;
1863 SilcChannelEntry channel;
1864 SilcBuffer buffer, chidp;
1866 char *name, *ban = NULL;
1869 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1874 if (cmd->argc < 2) {
1875 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1876 "Usage: /BAN <channel> "
1877 "[+|-[<nickname>[@<server>[!<username>[@hostname>]]]]]");
1882 if (cmd->argv[1][0] == '*') {
1883 if (!conn->current_channel) {
1884 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1885 "You are not on any channel");
1890 channel = conn->current_channel;
1892 name = cmd->argv[1];
1894 channel = silc_client_get_channel(cmd->client, conn, name);
1896 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1897 "You are on that channel");
1903 if (cmd->argc == 3) {
1904 if (cmd->argv[2][0] == '+')
1913 chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1915 /* Send the command */
1916 buffer = silc_command_payload_encode_va(SILC_COMMAND_BAN,
1917 ++conn->cmd_ident, 2,
1918 1, chidp->data, chidp->len,
1919 type, ban, ban ? strlen(ban) : 0);
1920 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1921 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1922 silc_buffer_free(buffer);
1923 silc_buffer_free(chidp);
1925 /* Notify application */
1929 silc_client_command_free(cmd);
1932 /* Command DETACH. This is used to detach from the server */
1934 SILC_CLIENT_CMD_FUNC(detach)
1936 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1937 SilcClientConnection conn = cmd->conn;
1941 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1946 buffer = silc_command_payload_encode_va(SILC_COMMAND_DETACH,
1947 ++conn->cmd_ident, 0);
1948 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1949 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1950 silc_buffer_free(buffer);
1952 /* Notify application */
1956 silc_client_command_free(cmd);
1959 /* LEAVE command. Leaves a channel. Client removes itself from a channel. */
1961 SILC_CLIENT_CMD_FUNC(leave)
1963 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1964 SilcClientConnection conn = cmd->conn;
1965 SilcChannelEntry channel;
1966 SilcChannelUser chu;
1967 SilcBuffer buffer, idp;
1971 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1976 if (cmd->argc != 2) {
1977 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1978 "Usage: /LEAVE <channel>");
1983 if (cmd->argv[1][0] == '*') {
1984 if (!conn->current_channel) {
1985 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1986 "You are not on any channel");
1990 name = conn->current_channel->channel_name;
1992 name = cmd->argv[1];
1995 /* Get the channel entry */
1996 channel = silc_client_get_channel(cmd->client, conn, name);
1998 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1999 "You are not on that channel");
2004 /* Remove us from channel */
2005 chu = silc_client_on_channel(channel, conn->local_entry);
2007 silc_hash_table_del(chu->client->channels, chu->channel);
2008 silc_hash_table_del(chu->channel->user_list, chu->client);
2012 /* Send LEAVE command to the server */
2013 idp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
2014 buffer = silc_command_payload_encode_va(SILC_COMMAND_LEAVE, 0, 1,
2015 1, idp->data, idp->len);
2016 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
2017 0, NULL, NULL, buffer->data, buffer->len, TRUE);
2018 silc_buffer_free(buffer);
2019 silc_buffer_free(idp);
2021 /* Notify application */
2024 if (conn->current_channel == channel)
2025 conn->current_channel = NULL;
2027 silc_client_del_channel(cmd->client, cmd->conn, channel);
2030 silc_client_command_free(cmd);
2033 /* Command USERS. Requests the USERS of the clients joined on requested
2036 SILC_CLIENT_CMD_FUNC(users)
2038 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
2039 SilcClientConnection conn = cmd->conn;
2044 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
2049 if (cmd->argc != 2) {
2050 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
2051 "Usage: /USERS <channel>");
2056 if (cmd->argv[1][0] == '*') {
2057 if (!conn->current_channel) {
2058 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
2059 "You are not on any channel");
2063 name = conn->current_channel->channel_name;
2065 name = cmd->argv[1];
2068 /* Send USERS command to the server */
2069 buffer = silc_command_payload_encode_va(SILC_COMMAND_USERS,
2070 ++conn->cmd_ident, 1,
2071 2, name, strlen(name));
2072 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND,
2073 NULL, 0, NULL, NULL, buffer->data,
2075 silc_buffer_free(buffer);
2077 /* Notify application */
2081 silc_client_command_free(cmd);
2084 /* Command GETKEY. Used to fetch remote client's public key. */
2086 SILC_CLIENT_CMD_FUNC(getkey)
2088 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
2089 SilcClientConnection conn = cmd->conn;
2090 SilcClient client = cmd->client;
2091 SilcClientEntry client_entry = NULL;
2092 SilcServerEntry server_entry = NULL;
2093 char *nickname = NULL;
2094 SilcBuffer idp, buffer;
2096 SILC_LOG_DEBUG(("Start"));
2099 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
2104 if (cmd->argc < 2) {
2105 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_INFO,
2106 "Usage: /GETKEY <nickname or server name>");
2111 /* Parse the typed nickname. */
2112 if (client->internal->params->nickname_parse)
2113 client->internal->params->nickname_parse(cmd->argv[1], &nickname);
2115 nickname = strdup(cmd->argv[1]);
2117 /* Find client entry */
2118 client_entry = silc_idlist_get_client(client, conn, nickname, cmd->argv[1],
2120 if (!client_entry) {
2121 /* Check whether user requested server actually */
2122 server_entry = silc_client_get_server(client, conn, cmd->argv[1]);
2124 if (!server_entry) {
2125 /* No. what ever user wants we don't have it, so resolve it. We
2126 will first try to resolve the client, and if that fails then
2127 we'll try to resolve the server. */
2129 if (!cmd->pending) {
2130 /* This will send the IDENTIFY command for nickname */
2131 silc_idlist_get_client(client, conn, nickname, cmd->argv[1], TRUE);
2132 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY,
2134 silc_client_command_getkey,
2135 silc_client_command_dup(cmd));
2139 SilcClientCommandReplyContext reply =
2140 (SilcClientCommandReplyContext)context2;
2141 SilcCommandStatus error;
2143 /* If nickname was not found, then resolve the server. */
2144 silc_command_get_status(reply->payload, NULL, &error);
2145 if (error == SILC_STATUS_ERR_NO_SUCH_NICK) {
2146 /* This sends the IDENTIFY command to resolve the server. */
2147 silc_client_command_register(client, SILC_COMMAND_IDENTIFY,
2149 silc_client_command_reply_identify_i, 0,
2151 silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
2153 2, cmd->argv[1], cmd->argv_lens[1]);
2154 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY,
2156 silc_client_command_getkey,
2157 silc_client_command_dup(cmd));
2161 /* If server was not found, then we've resolved both nickname and
2162 server and did not find anybody. */
2163 if (error == SILC_STATUS_ERR_NO_SUCH_SERVER) {
2164 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, "%s",
2165 silc_client_command_status_message(SILC_STATUS_ERR_NO_SUCH_NICK));
2166 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, "%s",
2167 silc_client_command_status_message(error));
2177 idp = silc_id_payload_encode(server_entry->server_id, SILC_ID_SERVER);
2179 idp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
2182 buffer = silc_command_payload_encode_va(SILC_COMMAND_GETKEY, 0, 1,
2183 1, idp->data, idp->len);
2184 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
2185 0, NULL, NULL, buffer->data, buffer->len, TRUE);
2186 silc_buffer_free(buffer);
2187 silc_buffer_free(idp);
2189 /* Notify application */
2193 silc_free(nickname);
2194 silc_client_command_free(cmd);
2197 /* Register a new command indicated by the `command' to the SILC client.
2198 The `name' is optional command name. If provided the command may be
2199 searched using the silc_client_command_find by that name. The
2200 `command_function' is the function to be called when the command is
2201 executed, and the `command_reply_function' is the function to be
2202 called after the server has sent reply back to the command.
2204 The `ident' is optional identifier for the command. If non-zero
2205 the `command_reply_function' for the command type `command' will be
2206 called only if the command reply sent by server includes the
2207 command identifier `ident'. Application usually does not need it
2208 and set it to zero value. */
2210 bool silc_client_command_register(SilcClient client,
2211 SilcCommand command,
2213 SilcCommandCb command_function,
2214 SilcCommandCb command_reply_function,
2218 SilcClientCommand cmd;
2220 cmd = silc_calloc(1, sizeof(*cmd));
2222 cmd->command = command_function;
2223 cmd->reply = command_reply_function;
2224 cmd->name = name ? strdup(name) : NULL;
2225 cmd->max_args = max_args;
2228 silc_list_add(client->internal->commands, cmd);
2233 /* Unregister a command indicated by the `command' with command function
2234 `command_function' and command reply function `command_reply_function'.
2235 Returns TRUE if the command was found and unregistered. */
2237 bool silc_client_command_unregister(SilcClient client,
2238 SilcCommand command,
2239 SilcCommandCb command_function,
2240 SilcCommandCb command_reply_function,
2243 SilcClientCommand cmd;
2245 silc_list_start(client->internal->commands);
2246 while ((cmd = silc_list_get(client->internal->commands)) != SILC_LIST_END) {
2247 if (cmd->cmd == command && cmd->command == command_function &&
2248 cmd->reply == command_reply_function && cmd->ident == ident) {
2249 silc_list_del(client->internal->commands, cmd);
2250 silc_free(cmd->name);
2259 /* Private range commands, specific to this implementation (and compatible
2260 with SILC Server). */
2262 /* CONNECT command. Connects the server to another server. */
2264 SILC_CLIENT_CMD_FUNC(connect)
2266 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
2267 SilcClientConnection conn = cmd->conn;
2269 unsigned char port[4];
2273 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
2278 if (cmd->argc < 2) {
2279 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
2280 "Usage: /CONNECT <server> [<port>]");
2285 if (cmd->argc == 3) {
2286 tmp = atoi(cmd->argv[2]);
2287 SILC_PUT32_MSB(tmp, port);
2291 buffer = silc_command_payload_encode_va(SILC_COMMAND_PRIV_CONNECT, 0, 2,
2293 strlen(cmd->argv[1]),
2296 buffer = silc_command_payload_encode_va(SILC_COMMAND_PRIV_CONNECT, 0, 1,
2298 strlen(cmd->argv[1]));
2299 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
2300 0, NULL, NULL, buffer->data, buffer->len, TRUE);
2301 silc_buffer_free(buffer);
2303 /* Notify application */
2307 silc_client_command_free(cmd);
2311 /* CLOSE command. Close server connection to the remote server */
2313 SILC_CLIENT_CMD_FUNC(close)
2315 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
2316 SilcClientConnection conn = cmd->conn;
2318 unsigned char port[4];
2322 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
2327 if (cmd->argc < 2) {
2328 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
2329 "Usage: /CLOSE <server> [<port>]");
2334 if (cmd->argc == 3) {
2335 tmp = atoi(cmd->argv[2]);
2336 SILC_PUT32_MSB(tmp, port);
2340 buffer = silc_command_payload_encode_va(SILC_COMMAND_PRIV_CLOSE, 0, 2,
2342 strlen(cmd->argv[1]),
2345 buffer = silc_command_payload_encode_va(SILC_COMMAND_PRIV_CLOSE, 0, 1,
2347 strlen(cmd->argv[1]));
2348 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
2349 0, NULL, NULL, buffer->data, buffer->len, TRUE);
2350 silc_buffer_free(buffer);
2352 /* Notify application */
2356 silc_client_command_free(cmd);
2359 /* SHUTDOWN command. Shutdowns the server. */
2361 SILC_CLIENT_CMD_FUNC(shutdown)
2363 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
2366 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
2371 /* Send the command */
2372 silc_client_command_send(cmd->client, cmd->conn,
2373 SILC_COMMAND_PRIV_SHUTDOWN, 0, 0);
2375 /* Notify application */
2379 silc_client_command_free(cmd);
2382 /* Register all default commands provided by the client library for the
2385 void silc_client_commands_register(SilcClient client)
2387 silc_list_init(client->internal->commands, struct SilcClientCommandStruct,
2390 SILC_CLIENT_CMD(whois, WHOIS, "WHOIS", 3);
2391 SILC_CLIENT_CMD(whowas, WHOWAS, "WHOWAS", 3);
2392 SILC_CLIENT_CMD(identify, IDENTIFY, "IDENTIFY", 3);
2393 SILC_CLIENT_CMD(nick, NICK, "NICK", 2);
2394 SILC_CLIENT_CMD(list, LIST, "LIST", 2);
2395 SILC_CLIENT_CMD(topic, TOPIC, "TOPIC", 3);
2396 SILC_CLIENT_CMD(invite, INVITE, "INVITE", 3);
2397 SILC_CLIENT_CMD(quit, QUIT, "QUIT", 2);
2398 SILC_CLIENT_CMD(kill, KILL, "KILL", 3);
2399 SILC_CLIENT_CMD(info, INFO, "INFO", 2);
2400 SILC_CLIENT_CMD(ping, PING, "PING", 2);
2401 SILC_CLIENT_CMD(oper, OPER, "OPER", 3);
2402 SILC_CLIENT_CMD(join, JOIN, "JOIN", 9);
2403 SILC_CLIENT_CMD(motd, MOTD, "MOTD", 2);
2404 SILC_CLIENT_CMD(umode, UMODE, "UMODE", 2);
2405 SILC_CLIENT_CMD(cmode, CMODE, "CMODE", 4);
2406 SILC_CLIENT_CMD(cumode, CUMODE, "CUMODE", 5);
2407 SILC_CLIENT_CMD(kick, KICK, "KICK", 4);
2408 SILC_CLIENT_CMD(ban, BAN, "BAN", 3);
2409 SILC_CLIENT_CMD(detach, DETACH, "DETACH", 0);
2410 SILC_CLIENT_CMD(silcoper, SILCOPER, "SILCOPER", 3);
2411 SILC_CLIENT_CMD(leave, LEAVE, "LEAVE", 2);
2412 SILC_CLIENT_CMD(users, USERS, "USERS", 2);
2413 SILC_CLIENT_CMD(getkey, GETKEY, "GETKEY", 2);
2415 SILC_CLIENT_CMD(connect, PRIV_CONNECT, "CONNECT", 3);
2416 SILC_CLIENT_CMD(close, PRIV_CLOSE, "CLOSE", 3);
2417 SILC_CLIENT_CMD(shutdown, PRIV_SHUTDOWN, "SHUTDOWN", 1);
2420 /* Unregister all commands. */
2422 void silc_client_commands_unregister(SilcClient client)
2424 SILC_CLIENT_CMDU(whois, WHOIS, "WHOIS");
2425 SILC_CLIENT_CMDU(whowas, WHOWAS, "WHOWAS");
2426 SILC_CLIENT_CMDU(identify, IDENTIFY, "IDENTIFY");
2427 SILC_CLIENT_CMDU(nick, NICK, "NICK");
2428 SILC_CLIENT_CMDU(list, LIST, "LIST");
2429 SILC_CLIENT_CMDU(topic, TOPIC, "TOPIC");
2430 SILC_CLIENT_CMDU(invite, INVITE, "INVITE");
2431 SILC_CLIENT_CMDU(quit, QUIT, "QUIT");
2432 SILC_CLIENT_CMDU(kill, KILL, "KILL");
2433 SILC_CLIENT_CMDU(info, INFO, "INFO");
2434 SILC_CLIENT_CMDU(ping, PING, "PING");
2435 SILC_CLIENT_CMDU(oper, OPER, "OPER");
2436 SILC_CLIENT_CMDU(join, JOIN, "JOIN");
2437 SILC_CLIENT_CMDU(motd, MOTD, "MOTD");
2438 SILC_CLIENT_CMDU(umode, UMODE, "UMODE");
2439 SILC_CLIENT_CMDU(cmode, CMODE, "CMODE");
2440 SILC_CLIENT_CMDU(cumode, CUMODE, "CUMODE");
2441 SILC_CLIENT_CMDU(kick, KICK, "KICK");
2442 SILC_CLIENT_CMDU(ban, BAN, "BAN");
2443 SILC_CLIENT_CMDU(detach, DETACH, "DETACH");
2444 SILC_CLIENT_CMDU(silcoper, SILCOPER, "SILCOPER");
2445 SILC_CLIENT_CMDU(leave, LEAVE, "LEAVE");
2446 SILC_CLIENT_CMDU(users, USERS, "USERS");
2447 SILC_CLIENT_CMDU(getkey, GETKEY, "GETKEY");
2449 SILC_CLIENT_CMDU(connect, PRIV_CONNECT, "CONNECT");
2450 SILC_CLIENT_CMDU(close, PRIV_CLOSE, "CLOSE");
2451 SILC_CLIENT_CMDU(shutdown, PRIV_SHUTDOWN, "SHUTDOWN");