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;
1183 idp = silc_id_payload_encode(conn->local_id, SILC_ID_CLIENT);
1184 SILC_PUT32_MSB(mode, modebuf);
1186 /* Send the command packet. We support sending only one mode at once
1187 that requires an argument. */
1189 silc_command_payload_encode_va(SILC_COMMAND_UMODE, ++conn->cmd_ident, 2,
1190 1, idp->data, idp->len,
1191 2, modebuf, sizeof(modebuf));
1192 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1193 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1194 silc_buffer_free(buffer);
1195 silc_buffer_free(idp);
1197 /* Notify application */
1201 silc_client_command_free(cmd);
1204 /* CMODE command. Sets channel mode. Modes that does not require any arguments
1205 can be set several at once. Those modes that require argument must be set
1206 separately (unless set with modes that does not require arguments). */
1208 SILC_CLIENT_CMD_FUNC(cmode)
1210 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1211 SilcClientConnection conn = cmd->conn;
1212 SilcChannelEntry channel;
1213 SilcBuffer buffer, chidp, auth = NULL;
1214 unsigned char *name, *cp, modebuf[4], tmp[4], *arg = NULL;
1215 SilcUInt32 mode, add, type, len, arg_len = 0;
1219 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1224 if (cmd->argc < 3) {
1225 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1226 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1231 if (cmd->argv[1][0] == '*') {
1232 if (!conn->current_channel) {
1233 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1234 "You are not on any channel");
1239 channel = conn->current_channel;
1241 name = cmd->argv[1];
1243 channel = silc_client_get_channel(cmd->client, conn, name);
1245 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1246 "You are on that channel");
1252 mode = channel->mode;
1254 /* Are we adding or removing mode */
1255 if (cmd->argv[2][0] == '-')
1260 /* Argument type to be sent to server */
1264 cp = cmd->argv[2] + 1;
1266 for (i = 0; i < len; i++) {
1270 mode |= SILC_CHANNEL_MODE_PRIVATE;
1272 mode &= ~SILC_CHANNEL_MODE_PRIVATE;
1276 mode |= SILC_CHANNEL_MODE_SECRET;
1278 mode &= ~SILC_CHANNEL_MODE_SECRET;
1282 mode |= SILC_CHANNEL_MODE_PRIVKEY;
1284 mode &= ~SILC_CHANNEL_MODE_PRIVKEY;
1288 mode |= SILC_CHANNEL_MODE_INVITE;
1290 mode &= ~SILC_CHANNEL_MODE_INVITE;
1294 mode |= SILC_CHANNEL_MODE_TOPIC;
1296 mode &= ~SILC_CHANNEL_MODE_TOPIC;
1300 mode |= SILC_CHANNEL_MODE_SILENCE_USERS;
1302 mode &= ~SILC_CHANNEL_MODE_SILENCE_USERS;
1306 mode |= SILC_CHANNEL_MODE_SILENCE_OPERS;
1308 mode &= ~SILC_CHANNEL_MODE_SILENCE_OPERS;
1313 mode |= SILC_CHANNEL_MODE_ULIMIT;
1315 if (cmd->argc < 4) {
1316 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1317 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1321 ll = atoi(cmd->argv[3]);
1322 SILC_PUT32_MSB(ll, tmp);
1326 mode &= ~SILC_CHANNEL_MODE_ULIMIT;
1331 mode |= SILC_CHANNEL_MODE_PASSPHRASE;
1333 if (cmd->argc < 4) {
1334 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1335 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1340 arg_len = cmd->argv_lens[3];
1342 mode &= ~SILC_CHANNEL_MODE_PASSPHRASE;
1347 mode |= SILC_CHANNEL_MODE_CIPHER;
1349 if (cmd->argc < 4) {
1350 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1351 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1356 arg_len = cmd->argv_lens[3];
1358 mode &= ~SILC_CHANNEL_MODE_CIPHER;
1363 mode |= SILC_CHANNEL_MODE_HMAC;
1365 if (cmd->argc < 4) {
1366 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1367 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1372 arg_len = cmd->argv_lens[3];
1374 mode &= ~SILC_CHANNEL_MODE_HMAC;
1379 mode |= SILC_CHANNEL_MODE_FOUNDER_AUTH;
1382 if (cmd->argc < 4) {
1383 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1384 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1389 if (!strcasecmp(cmd->argv[3], "-pubkey")) {
1390 auth = silc_auth_public_key_auth_generate(cmd->client->public_key,
1391 cmd->client->private_key,
1397 auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
1398 cmd->argv[3], cmd->argv_lens[3]);
1402 arg_len = auth->len;
1404 mode &= ~SILC_CHANNEL_MODE_FOUNDER_AUTH;
1414 chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1415 SILC_PUT32_MSB(mode, modebuf);
1417 /* Send the command packet. We support sending only one mode at once
1418 that requires an argument. */
1421 silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 3,
1422 1, chidp->data, chidp->len,
1423 2, modebuf, sizeof(modebuf),
1424 type, arg, arg_len);
1427 silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 2,
1428 1, chidp->data, chidp->len,
1429 2, modebuf, sizeof(modebuf));
1432 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1433 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1434 silc_buffer_free(buffer);
1435 silc_buffer_free(chidp);
1437 silc_buffer_free(auth);
1439 /* Notify application */
1443 silc_client_command_free(cmd);
1446 /* CUMODE command. Changes client's mode on a channel. */
1448 SILC_CLIENT_CMD_FUNC(cumode)
1450 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1451 SilcClient client = cmd->client;
1452 SilcClientConnection conn = cmd->conn;
1453 SilcChannelEntry channel;
1454 SilcChannelUser chu;
1455 SilcClientEntry client_entry;
1456 SilcBuffer buffer, clidp, chidp, auth = NULL;
1457 unsigned char *name, *cp, modebuf[4];
1458 SilcUInt32 mode = 0, add, len;
1459 char *nickname = NULL;
1463 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1468 if (cmd->argc < 4) {
1469 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1470 "Usage: /CUMODE <channel> +|-<modes> <nickname>[@<server>]");
1475 if (cmd->argv[1][0] == '*') {
1476 if (!conn->current_channel) {
1477 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1478 "You are not on any channel");
1483 channel = conn->current_channel;
1485 name = cmd->argv[1];
1487 channel = silc_client_get_channel(cmd->client, conn, name);
1489 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1490 "You are on that channel");
1496 /* Parse the typed nickname. */
1497 if (client->internal->params->nickname_parse)
1498 client->internal->params->nickname_parse(cmd->argv[3], &nickname);
1500 nickname = strdup(cmd->argv[3]);
1502 /* Find client entry */
1503 client_entry = silc_idlist_get_client(cmd->client, conn, nickname,
1504 cmd->argv[3], TRUE);
1505 if (!client_entry) {
1511 /* Client entry not found, it was requested thus mark this to be
1513 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY,
1515 silc_client_command_cumode,
1516 silc_client_command_dup(cmd));
1521 /* Get the current mode */
1522 chu = silc_client_on_channel(channel, client_entry);
1526 /* Are we adding or removing mode */
1527 if (cmd->argv[2][0] == '-')
1533 cp = cmd->argv[2] + 1;
1535 for (i = 0; i < len; i++) {
1539 mode |= SILC_CHANNEL_UMODE_CHANFO;
1540 mode |= SILC_CHANNEL_UMODE_CHANOP;
1542 mode = SILC_CHANNEL_UMODE_NONE;
1547 if (cmd->argc == 5) {
1548 if (!strcasecmp(cmd->argv[4], "-pubkey")) {
1549 auth = silc_auth_public_key_auth_generate(cmd->client->public_key,
1550 cmd->client->private_key,
1556 auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
1557 cmd->argv[4], cmd->argv_lens[4]);
1560 mode |= SILC_CHANNEL_UMODE_CHANFO;
1562 mode &= ~SILC_CHANNEL_UMODE_CHANFO;
1567 mode |= SILC_CHANNEL_UMODE_CHANOP;
1569 mode &= ~SILC_CHANNEL_UMODE_CHANOP;
1578 chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1579 SILC_PUT32_MSB(mode, modebuf);
1580 clidp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
1582 /* Send the command packet. We support sending only one mode at once
1583 that requires an argument. */
1584 buffer = silc_command_payload_encode_va(SILC_COMMAND_CUMODE, 0,
1586 1, chidp->data, chidp->len,
1588 3, clidp->data, clidp->len,
1589 4, auth ? auth->data : NULL,
1590 auth ? auth->len : 0);
1592 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1593 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1594 silc_buffer_free(buffer);
1595 silc_buffer_free(chidp);
1596 silc_buffer_free(clidp);
1598 silc_buffer_free(auth);
1600 /* Notify application */
1604 silc_free(nickname);
1605 silc_client_command_free(cmd);
1608 /* KICK command. Kicks a client out of channel. */
1610 SILC_CLIENT_CMD_FUNC(kick)
1612 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1613 SilcClient client = cmd->client;
1614 SilcClientConnection conn = cmd->conn;
1615 SilcIDCacheEntry id_cache = NULL;
1616 SilcChannelEntry channel;
1617 SilcBuffer buffer, idp, idp2;
1618 SilcClientEntry target;
1620 char *nickname = NULL;
1623 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1628 if (cmd->argc < 3) {
1629 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1630 "Usage: /KICK <channel> <nickname> [<comment>]");
1635 if (cmd->argv[1][0] == '*') {
1636 if (!conn->current_channel) {
1637 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1638 "You are not on any channel");
1642 name = conn->current_channel->channel_name;
1644 name = cmd->argv[1];
1647 if (!conn->current_channel) {
1648 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1649 "You are not on that channel");
1654 /* Get the Channel ID of the channel */
1655 if (!silc_idcache_find_by_name_one(conn->channel_cache, name, &id_cache)) {
1656 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1657 "You are not on that channel");
1662 channel = (SilcChannelEntry)id_cache->context;
1664 /* Parse the typed nickname. */
1665 if (client->internal->params->nickname_parse)
1666 client->internal->params->nickname_parse(cmd->argv[2], &nickname);
1668 nickname = strdup(cmd->argv[2]);
1670 /* Get the target client */
1671 target = silc_idlist_get_client(cmd->client, conn, nickname,
1672 cmd->argv[2], FALSE);
1674 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1675 "No such client: %s", cmd->argv[2]);
1680 /* Send KICK command to the server */
1681 idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1682 idp2 = silc_id_payload_encode(target->id, SILC_ID_CLIENT);
1684 buffer = silc_command_payload_encode_va(SILC_COMMAND_KICK, 0, 2,
1685 1, idp->data, idp->len,
1686 2, idp2->data, idp2->len);
1688 buffer = silc_command_payload_encode_va(SILC_COMMAND_KICK, 0, 3,
1689 1, idp->data, idp->len,
1690 2, idp2->data, idp2->len,
1692 strlen(cmd->argv[3]));
1693 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1694 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1695 silc_buffer_free(buffer);
1696 silc_buffer_free(idp);
1697 silc_buffer_free(idp2);
1699 /* Notify application */
1703 silc_free(nickname);
1704 silc_client_command_free(cmd);
1707 static void silc_client_command_oper_send(unsigned char *data,
1708 SilcUInt32 data_len, void *context)
1710 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1711 SilcClientConnection conn = cmd->conn;
1712 SilcBuffer buffer, auth;
1714 if (cmd->argc >= 3) {
1715 /* Encode the public key authentication payload */
1716 auth = silc_auth_public_key_auth_generate(cmd->client->public_key,
1717 cmd->client->private_key,
1718 cmd->client->rng, conn->hash,
1722 /* Encode the password authentication payload */
1723 auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
1727 buffer = silc_command_payload_encode_va(SILC_COMMAND_OPER, 0, 2,
1729 strlen(cmd->argv[1]),
1730 2, auth->data, auth->len);
1731 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1732 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1734 silc_buffer_free(buffer);
1735 silc_buffer_free(auth);
1737 /* Notify application */
1741 /* OPER command. Used to obtain server operator privileges. */
1743 SILC_CLIENT_CMD_FUNC(oper)
1745 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1746 SilcClientConnection conn = cmd->conn;
1749 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1754 if (cmd->argc < 2) {
1755 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1756 "Usage: /OPER <username> [-pubkey]");
1761 if (cmd->argc < 3) {
1762 /* Get passphrase */
1763 cmd->client->internal->ops->ask_passphrase(cmd->client, conn,
1764 silc_client_command_oper_send,
1769 silc_client_command_oper_send(NULL, 0, context);
1772 silc_client_command_free(cmd);
1775 static void silc_client_command_silcoper_send(unsigned char *data,
1776 SilcUInt32 data_len,
1779 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1780 SilcClientConnection conn = cmd->conn;
1781 SilcBuffer buffer, auth;
1783 if (cmd->argc >= 3) {
1784 /* Encode the public key authentication payload */
1785 auth = silc_auth_public_key_auth_generate(cmd->client->public_key,
1786 cmd->client->private_key,
1787 cmd->client->rng, conn->hash,
1791 /* Encode the password authentication payload */
1792 auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
1796 buffer = silc_command_payload_encode_va(SILC_COMMAND_SILCOPER, 0, 2,
1798 strlen(cmd->argv[1]),
1799 2, auth->data, auth->len);
1800 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1801 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1803 silc_buffer_free(buffer);
1804 silc_buffer_free(auth);
1806 /* Notify application */
1810 /* SILCOPER command. Used to obtain router operator privileges. */
1812 SILC_CLIENT_CMD_FUNC(silcoper)
1814 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1815 SilcClientConnection conn = cmd->conn;
1818 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1823 if (cmd->argc < 2) {
1824 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1825 "Usage: /SILCOPER <username> [-pubkey]");
1830 if (cmd->argc < 3) {
1831 /* Get passphrase */
1832 cmd->client->internal->ops->ask_passphrase(cmd->client, conn,
1833 silc_client_command_silcoper_send,
1838 silc_client_command_silcoper_send(NULL, 0, context);
1841 silc_client_command_free(cmd);
1844 /* Command BAN. This is used to manage the ban list of the channel. */
1846 SILC_CLIENT_CMD_FUNC(ban)
1848 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1849 SilcClientConnection conn = cmd->conn;
1850 SilcChannelEntry channel;
1851 SilcBuffer buffer, chidp;
1853 char *name, *ban = NULL;
1856 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1861 if (cmd->argc < 2) {
1862 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1863 "Usage: /BAN <channel> "
1864 "[+|-[<nickname>[@<server>[!<username>[@hostname>]]]]]");
1869 if (cmd->argv[1][0] == '*') {
1870 if (!conn->current_channel) {
1871 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1872 "You are not on any channel");
1877 channel = conn->current_channel;
1879 name = cmd->argv[1];
1881 channel = silc_client_get_channel(cmd->client, conn, name);
1883 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1884 "You are on that channel");
1890 if (cmd->argc == 3) {
1891 if (cmd->argv[2][0] == '+')
1900 chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1902 /* Send the command */
1904 buffer = silc_command_payload_encode_va(SILC_COMMAND_BAN, 0, 2,
1905 1, chidp->data, chidp->len,
1906 type, ban, strlen(ban));
1908 buffer = silc_command_payload_encode_va(SILC_COMMAND_BAN, 0, 1,
1909 1, chidp->data, chidp->len);
1911 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1912 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1913 silc_buffer_free(buffer);
1914 silc_buffer_free(chidp);
1916 /* Notify application */
1920 silc_client_command_free(cmd);
1923 /* LEAVE command. Leaves a channel. Client removes itself from a channel. */
1925 SILC_CLIENT_CMD_FUNC(leave)
1927 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1928 SilcClientConnection conn = cmd->conn;
1929 SilcChannelEntry channel;
1930 SilcChannelUser chu;
1931 SilcBuffer buffer, idp;
1935 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1940 if (cmd->argc != 2) {
1941 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1942 "Usage: /LEAVE <channel>");
1947 if (cmd->argv[1][0] == '*') {
1948 if (!conn->current_channel) {
1949 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1950 "You are not on any channel");
1954 name = conn->current_channel->channel_name;
1956 name = cmd->argv[1];
1959 /* Get the channel entry */
1960 channel = silc_client_get_channel(cmd->client, conn, name);
1962 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1963 "You are not on that channel");
1968 /* Remove us from channel */
1969 chu = silc_client_on_channel(channel, conn->local_entry);
1971 silc_hash_table_del(chu->client->channels, chu->channel);
1972 silc_hash_table_del(chu->channel->user_list, chu->client);
1976 /* Send LEAVE command to the server */
1977 idp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1978 buffer = silc_command_payload_encode_va(SILC_COMMAND_LEAVE, 0, 1,
1979 1, idp->data, idp->len);
1980 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1981 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1982 silc_buffer_free(buffer);
1983 silc_buffer_free(idp);
1985 /* Notify application */
1988 if (conn->current_channel == channel)
1989 conn->current_channel = NULL;
1991 silc_client_del_channel(cmd->client, cmd->conn, channel);
1994 silc_client_command_free(cmd);
1997 /* Command USERS. Requests the USERS of the clients joined on requested
2000 SILC_CLIENT_CMD_FUNC(users)
2002 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
2003 SilcClientConnection conn = cmd->conn;
2008 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
2013 if (cmd->argc != 2) {
2014 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
2015 "Usage: /USERS <channel>");
2020 if (cmd->argv[1][0] == '*') {
2021 if (!conn->current_channel) {
2022 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
2023 "You are not on any channel");
2027 name = conn->current_channel->channel_name;
2029 name = cmd->argv[1];
2032 /* Send USERS command to the server */
2033 buffer = silc_command_payload_encode_va(SILC_COMMAND_USERS,
2034 ++conn->cmd_ident, 1,
2035 2, name, strlen(name));
2036 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND,
2037 NULL, 0, NULL, NULL, buffer->data,
2039 silc_buffer_free(buffer);
2041 /* Notify application */
2045 silc_client_command_free(cmd);
2048 /* Command GETKEY. Used to fetch remote client's public key. */
2050 SILC_CLIENT_CMD_FUNC(getkey)
2052 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
2053 SilcClientConnection conn = cmd->conn;
2054 SilcClient client = cmd->client;
2055 SilcClientEntry client_entry = NULL;
2056 SilcServerEntry server_entry = NULL;
2057 char *nickname = NULL;
2058 SilcBuffer idp, buffer;
2060 SILC_LOG_DEBUG(("Start"));
2063 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
2068 if (cmd->argc < 2) {
2069 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_INFO,
2070 "Usage: /GETKEY <nickname or server name>");
2075 /* Parse the typed nickname. */
2076 if (client->internal->params->nickname_parse)
2077 client->internal->params->nickname_parse(cmd->argv[1], &nickname);
2079 nickname = strdup(cmd->argv[1]);
2081 /* Find client entry */
2082 client_entry = silc_idlist_get_client(client, conn, nickname, cmd->argv[1],
2084 if (!client_entry) {
2085 /* Check whether user requested server actually */
2086 server_entry = silc_client_get_server(client, conn, cmd->argv[1]);
2088 if (!server_entry) {
2089 /* No. what ever user wants we don't have it, so resolve it. We
2090 will first try to resolve the client, and if that fails then
2091 we'll try to resolve the server. */
2093 if (!cmd->pending) {
2094 /* This will send the IDENTIFY command for nickname */
2095 silc_idlist_get_client(client, conn, nickname, cmd->argv[1], TRUE);
2096 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY,
2098 silc_client_command_getkey,
2099 silc_client_command_dup(cmd));
2103 SilcClientCommandReplyContext reply =
2104 (SilcClientCommandReplyContext)context2;
2105 SilcCommandStatus error;
2107 /* If nickname was not found, then resolve the server. */
2108 silc_command_get_status(reply->payload, NULL, &error);
2109 if (error == SILC_STATUS_ERR_NO_SUCH_NICK) {
2110 /* This sends the IDENTIFY command to resolve the server. */
2111 silc_client_command_register(client, SILC_COMMAND_IDENTIFY,
2113 silc_client_command_reply_identify_i, 0,
2115 silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
2117 2, cmd->argv[1], cmd->argv_lens[1]);
2118 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY,
2120 silc_client_command_getkey,
2121 silc_client_command_dup(cmd));
2125 /* If server was not found, then we've resolved both nickname and
2126 server and did not find anybody. */
2127 if (error == SILC_STATUS_ERR_NO_SUCH_SERVER) {
2128 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, "%s",
2129 silc_client_command_status_message(SILC_STATUS_ERR_NO_SUCH_NICK));
2130 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, "%s",
2131 silc_client_command_status_message(error));
2141 idp = silc_id_payload_encode(server_entry->server_id, SILC_ID_SERVER);
2143 idp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
2146 buffer = silc_command_payload_encode_va(SILC_COMMAND_GETKEY, 0, 1,
2147 1, idp->data, idp->len);
2148 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
2149 0, NULL, NULL, buffer->data, buffer->len, TRUE);
2150 silc_buffer_free(buffer);
2151 silc_buffer_free(idp);
2153 /* Notify application */
2157 silc_free(nickname);
2158 silc_client_command_free(cmd);
2161 /* Register a new command indicated by the `command' to the SILC client.
2162 The `name' is optional command name. If provided the command may be
2163 searched using the silc_client_command_find by that name. The
2164 `command_function' is the function to be called when the command is
2165 executed, and the `command_reply_function' is the function to be
2166 called after the server has sent reply back to the command.
2168 The `ident' is optional identifier for the command. If non-zero
2169 the `command_reply_function' for the command type `command' will be
2170 called only if the command reply sent by server includes the
2171 command identifier `ident'. Application usually does not need it
2172 and set it to zero value. */
2174 bool silc_client_command_register(SilcClient client,
2175 SilcCommand command,
2177 SilcCommandCb command_function,
2178 SilcCommandCb command_reply_function,
2182 SilcClientCommand cmd;
2184 cmd = silc_calloc(1, sizeof(*cmd));
2186 cmd->command = command_function;
2187 cmd->reply = command_reply_function;
2188 cmd->name = name ? strdup(name) : NULL;
2189 cmd->max_args = max_args;
2192 silc_list_add(client->internal->commands, cmd);
2197 /* Unregister a command indicated by the `command' with command function
2198 `command_function' and command reply function `command_reply_function'.
2199 Returns TRUE if the command was found and unregistered. */
2201 bool silc_client_command_unregister(SilcClient client,
2202 SilcCommand command,
2203 SilcCommandCb command_function,
2204 SilcCommandCb command_reply_function,
2207 SilcClientCommand cmd;
2209 silc_list_start(client->internal->commands);
2210 while ((cmd = silc_list_get(client->internal->commands)) != SILC_LIST_END) {
2211 if (cmd->cmd == command && cmd->command == command_function &&
2212 cmd->reply == command_reply_function && cmd->ident == ident) {
2213 silc_list_del(client->internal->commands, cmd);
2214 silc_free(cmd->name);
2223 /* Private range commands, specific to this implementation (and compatible
2224 with SILC Server). */
2226 /* CONNECT command. Connects the server to another server. */
2228 SILC_CLIENT_CMD_FUNC(connect)
2230 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
2231 SilcClientConnection conn = cmd->conn;
2233 unsigned char port[4];
2237 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
2242 if (cmd->argc < 2) {
2243 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
2244 "Usage: /CONNECT <server> [<port>]");
2249 if (cmd->argc == 3) {
2250 tmp = atoi(cmd->argv[2]);
2251 SILC_PUT32_MSB(tmp, port);
2255 buffer = silc_command_payload_encode_va(SILC_COMMAND_PRIV_CONNECT, 0, 2,
2257 strlen(cmd->argv[1]),
2260 buffer = silc_command_payload_encode_va(SILC_COMMAND_PRIV_CONNECT, 0, 1,
2262 strlen(cmd->argv[1]));
2263 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
2264 0, NULL, NULL, buffer->data, buffer->len, TRUE);
2265 silc_buffer_free(buffer);
2267 /* Notify application */
2271 silc_client_command_free(cmd);
2275 /* CLOSE command. Close server connection to the remote server */
2277 SILC_CLIENT_CMD_FUNC(close)
2279 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
2280 SilcClientConnection conn = cmd->conn;
2282 unsigned char port[4];
2286 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
2291 if (cmd->argc < 2) {
2292 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
2293 "Usage: /CLOSE <server> [<port>]");
2298 if (cmd->argc == 3) {
2299 tmp = atoi(cmd->argv[2]);
2300 SILC_PUT32_MSB(tmp, port);
2304 buffer = silc_command_payload_encode_va(SILC_COMMAND_PRIV_CLOSE, 0, 2,
2306 strlen(cmd->argv[1]),
2309 buffer = silc_command_payload_encode_va(SILC_COMMAND_PRIV_CLOSE, 0, 1,
2311 strlen(cmd->argv[1]));
2312 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
2313 0, NULL, NULL, buffer->data, buffer->len, TRUE);
2314 silc_buffer_free(buffer);
2316 /* Notify application */
2320 silc_client_command_free(cmd);
2323 /* SHUTDOWN command. Shutdowns the server. */
2325 SILC_CLIENT_CMD_FUNC(shutdown)
2327 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
2330 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
2335 /* Send the command */
2336 silc_client_command_send(cmd->client, cmd->conn,
2337 SILC_COMMAND_PRIV_SHUTDOWN, 0, 0);
2339 /* Notify application */
2343 silc_client_command_free(cmd);
2346 /* Register all default commands provided by the client library for the
2349 void silc_client_commands_register(SilcClient client)
2351 silc_list_init(client->internal->commands, struct SilcClientCommandStruct,
2354 SILC_CLIENT_CMD(whois, WHOIS, "WHOIS", 3);
2355 SILC_CLIENT_CMD(whowas, WHOWAS, "WHOWAS", 3);
2356 SILC_CLIENT_CMD(identify, IDENTIFY, "IDENTIFY", 3);
2357 SILC_CLIENT_CMD(nick, NICK, "NICK", 2);
2358 SILC_CLIENT_CMD(list, LIST, "LIST", 2);
2359 SILC_CLIENT_CMD(topic, TOPIC, "TOPIC", 3);
2360 SILC_CLIENT_CMD(invite, INVITE, "INVITE", 3);
2361 SILC_CLIENT_CMD(quit, QUIT, "QUIT", 2);
2362 SILC_CLIENT_CMD(kill, KILL, "KILL", 3);
2363 SILC_CLIENT_CMD(info, INFO, "INFO", 2);
2364 SILC_CLIENT_CMD(ping, PING, "PING", 2);
2365 SILC_CLIENT_CMD(oper, OPER, "OPER", 3);
2366 SILC_CLIENT_CMD(join, JOIN, "JOIN", 9);
2367 SILC_CLIENT_CMD(motd, MOTD, "MOTD", 2);
2368 SILC_CLIENT_CMD(umode, UMODE, "UMODE", 2);
2369 SILC_CLIENT_CMD(cmode, CMODE, "CMODE", 4);
2370 SILC_CLIENT_CMD(cumode, CUMODE, "CUMODE", 5);
2371 SILC_CLIENT_CMD(kick, KICK, "KICK", 4);
2372 SILC_CLIENT_CMD(ban, BAN, "BAN", 3);
2373 SILC_CLIENT_CMD(silcoper, SILCOPER, "SILCOPER", 3);
2374 SILC_CLIENT_CMD(leave, LEAVE, "LEAVE", 2);
2375 SILC_CLIENT_CMD(users, USERS, "USERS", 2);
2376 SILC_CLIENT_CMD(getkey, GETKEY, "GETKEY", 2);
2378 SILC_CLIENT_CMD(connect, PRIV_CONNECT, "CONNECT", 3);
2379 SILC_CLIENT_CMD(close, PRIV_CLOSE, "CLOSE", 3);
2380 SILC_CLIENT_CMD(shutdown, PRIV_SHUTDOWN, "SHUTDOWN", 1);
2383 /* Unregister all commands. */
2385 void silc_client_commands_unregister(SilcClient client)
2387 SILC_CLIENT_CMDU(whois, WHOIS, "WHOIS");
2388 SILC_CLIENT_CMDU(whowas, WHOWAS, "WHOWAS");
2389 SILC_CLIENT_CMDU(identify, IDENTIFY, "IDENTIFY");
2390 SILC_CLIENT_CMDU(nick, NICK, "NICK");
2391 SILC_CLIENT_CMDU(list, LIST, "LIST");
2392 SILC_CLIENT_CMDU(topic, TOPIC, "TOPIC");
2393 SILC_CLIENT_CMDU(invite, INVITE, "INVITE");
2394 SILC_CLIENT_CMDU(quit, QUIT, "QUIT");
2395 SILC_CLIENT_CMDU(kill, KILL, "KILL");
2396 SILC_CLIENT_CMDU(info, INFO, "INFO");
2397 SILC_CLIENT_CMDU(ping, PING, "PING");
2398 SILC_CLIENT_CMDU(oper, OPER, "OPER");
2399 SILC_CLIENT_CMDU(join, JOIN, "JOIN");
2400 SILC_CLIENT_CMDU(motd, MOTD, "MOTD");
2401 SILC_CLIENT_CMDU(umode, UMODE, "UMODE");
2402 SILC_CLIENT_CMDU(cmode, CMODE, "CMODE");
2403 SILC_CLIENT_CMDU(cumode, CUMODE, "CUMODE");
2404 SILC_CLIENT_CMDU(kick, KICK, "KICK");
2405 SILC_CLIENT_CMDU(ban, BAN, "BAN");
2406 SILC_CLIENT_CMDU(silcoper, SILCOPER, "SILCOPER");
2407 SILC_CLIENT_CMDU(leave, LEAVE, "LEAVE");
2408 SILC_CLIENT_CMDU(users, USERS, "USERS");
2409 SILC_CLIENT_CMDU(getkey, GETKEY, "GETKEY");
2411 SILC_CLIENT_CMDU(connect, PRIV_CONNECT, "CONNECT");
2412 SILC_CLIENT_CMDU(close, PRIV_CLOSE, "CLOSE");
2413 SILC_CLIENT_CMDU(shutdown, PRIV_SHUTDOWN, "SHUTDOWN");