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. It can be SILC_COMMAND_NONE
91 to match any command with the `ident'. If `ident' is non-zero
92 the `callback' will be executed when received reply with command
93 identifier `ident'. If there already exists pending command for the
94 specified command, ident, callback and context this function has no
97 void silc_client_command_pending(SilcClientConnection conn,
98 SilcCommand reply_cmd,
100 SilcCommandCb callback,
103 SilcClientCommandPending *reply;
105 /* Check whether identical pending already exists for same command,
106 ident, callback and callback context. If it does then it would be
107 error to register it again. */
108 silc_dlist_start(conn->pending_commands);
109 while ((reply = silc_dlist_get(conn->pending_commands)) != SILC_LIST_END) {
110 if (reply->reply_cmd == reply_cmd && reply->ident == ident &&
111 reply->callback == callback && reply->context == context)
115 reply = silc_calloc(1, sizeof(*reply));
116 reply->reply_cmd = reply_cmd;
117 reply->ident = ident;
118 reply->context = context;
119 reply->callback = callback;
120 silc_dlist_add(conn->pending_commands, reply);
123 /* Deletes pending command by reply command type. */
125 void silc_client_command_pending_del(SilcClientConnection conn,
126 SilcCommand reply_cmd,
129 SilcClientCommandPending *r;
131 silc_dlist_start(conn->pending_commands);
132 while ((r = silc_dlist_get(conn->pending_commands)) != SILC_LIST_END) {
133 if (r->reply_cmd == reply_cmd && r->ident == ident) {
134 silc_dlist_del(conn->pending_commands, r);
140 /* Checks for pending commands and marks callbacks to be called from
141 the command reply function. */
143 SilcClientCommandPendingCallbacks
144 silc_client_command_pending_check(SilcClientConnection conn,
145 SilcClientCommandReplyContext ctx,
148 SilcUInt32 *callbacks_count)
150 SilcClientCommandPending *r;
151 SilcClientCommandPendingCallbacks callbacks = NULL;
154 silc_dlist_start(conn->pending_commands);
155 while ((r = silc_dlist_get(conn->pending_commands)) != SILC_LIST_END) {
156 if ((r->reply_cmd == command || r->reply_cmd == SILC_COMMAND_NONE)
157 && r->ident == ident) {
158 callbacks = silc_realloc(callbacks, sizeof(*callbacks) * (i + 1));
159 callbacks[i].context = r->context;
160 callbacks[i].callback = r->callback;
166 *callbacks_count = i;
170 /* Allocate Command Context */
172 SilcClientCommandContext silc_client_command_alloc(void)
174 SilcClientCommandContext ctx = silc_calloc(1, sizeof(*ctx));
179 /* Free command context and its internals */
181 void silc_client_command_free(SilcClientCommandContext ctx)
184 SILC_LOG_DEBUG(("Command context %p refcnt %d->%d", ctx, ctx->users + 1,
186 if (ctx->users < 1) {
189 for (i = 0; i < ctx->argc; i++)
190 silc_free(ctx->argv[i]);
191 silc_free(ctx->argv_lens);
192 silc_free(ctx->argv_types);
197 /* Duplicate Command Context by adding reference counter. The context won't
198 be free'd untill it hits zero. */
200 SilcClientCommandContext silc_client_command_dup(SilcClientCommandContext ctx)
203 SILC_LOG_DEBUG(("Command context %p refcnt %d->%d", ctx, ctx->users - 1,
208 /* Command WHOIS. This command is used to query information about
211 SILC_CLIENT_CMD_FUNC(whois)
213 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
214 SilcClientConnection conn = cmd->conn;
218 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
223 /* Given without arguments fetches client's own information */
225 buffer = silc_id_payload_encode(cmd->conn->local_id, SILC_ID_CLIENT);
226 silc_client_command_send(cmd->client, cmd->conn, SILC_COMMAND_WHOIS,
228 1, 3, buffer->data, buffer->len);
229 silc_buffer_free(buffer);
233 buffer = silc_command_payload_encode(SILC_COMMAND_WHOIS,
234 cmd->argc - 1, ++cmd->argv,
235 ++cmd->argv_lens, ++cmd->argv_types,
237 silc_client_packet_send(cmd->client, cmd->conn->sock,
238 SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
239 buffer->data, buffer->len, TRUE);
240 silc_buffer_free(buffer);
245 /* Notify application */
249 silc_client_command_free(cmd);
252 /* Command WHOWAS. This command is used to query history information about
253 specific user that used to exist in the network. */
255 SILC_CLIENT_CMD_FUNC(whowas)
257 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
258 SilcClientConnection conn = cmd->conn;
262 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
267 if (cmd->argc < 2 || cmd->argc > 3) {
268 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
269 "Usage: /WHOWAS <nickname>[@<server>] [<count>]");
274 buffer = silc_command_payload_encode(SILC_COMMAND_WHOWAS,
275 cmd->argc - 1, ++cmd->argv,
276 ++cmd->argv_lens, ++cmd->argv_types,
278 silc_client_packet_send(cmd->client, cmd->conn->sock,
279 SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
280 buffer->data, buffer->len, TRUE);
281 silc_buffer_free(buffer);
286 /* Notify application */
290 silc_client_command_free(cmd);
293 /* Command IDENTIFY. This command is used to query information about
294 specific user, especially ID's.
296 NOTE: This command is used only internally by the client library
297 and application MUST NOT call this command directly. */
299 SILC_CLIENT_CMD_FUNC(identify)
301 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
302 SilcClientConnection conn = cmd->conn;
306 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
310 if (cmd->argc < 2 || cmd->argc > 3)
314 buffer = silc_command_payload_encode_va(SILC_COMMAND_IDENTIFY,
315 ++conn->cmd_ident, 1,
319 buffer = silc_command_payload_encode_va(SILC_COMMAND_IDENTIFY,
320 ++conn->cmd_ident, 2,
326 silc_client_packet_send(cmd->client, cmd->conn->sock,
327 SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
328 buffer->data, buffer->len, TRUE);
329 silc_buffer_free(buffer);
332 silc_client_command_free(cmd);
335 /* Pending callbcak that will be called after the NICK command was
336 replied by the server. This sets the nickname if there were no
339 SILC_CLIENT_CMD_FUNC(nick_change)
341 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
342 SilcClientConnection conn = cmd->conn;
343 SilcClientCommandReplyContext reply =
344 (SilcClientCommandReplyContext)context2;
345 SilcCommandStatus status;
347 silc_command_get_status(reply->payload, &status, NULL);
348 if (status == SILC_STATUS_OK) {
349 /* Set the nickname */
350 silc_idcache_del_by_context(conn->client_cache, conn->local_entry);
352 silc_free(conn->nickname);
353 conn->nickname = strdup(cmd->argv[1]);
354 conn->local_entry->nickname = conn->nickname;
355 silc_client_nickname_format(cmd->client, conn, conn->local_entry);
356 silc_idcache_add(conn->client_cache, strdup(cmd->argv[1]),
357 conn->local_entry->id, conn->local_entry, 0, NULL);
363 silc_client_command_free(cmd);
366 /* Command NICK. Shows current nickname/sets new nickname on current
369 SILC_CLIENT_CMD_FUNC(nick)
371 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
372 SilcClientConnection conn = cmd->conn;
376 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
382 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
383 "Usage: /NICK <nickname>");
388 if (!strcmp(conn->nickname, cmd->argv[1]))
391 /* Show current nickname */
394 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
395 "Your nickname is %s on server %s",
396 conn->nickname, conn->remote_host);
398 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
399 "Your nickname is %s", conn->nickname);
406 if (cmd->argv_lens[1] > 128)
407 cmd->argv_lens[1] = 128;
409 /* Send the NICK command */
410 buffer = silc_command_payload_encode(SILC_COMMAND_NICK, 1,
414 ++cmd->conn->cmd_ident);
415 silc_client_packet_send(cmd->client, cmd->conn->sock,
416 SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
417 buffer->data, buffer->len, TRUE);
418 silc_buffer_free(buffer);
420 /* Register pending callback that will actually set the new nickname
421 if there were no errors returned by the server. */
422 silc_client_command_pending(conn, SILC_COMMAND_NICK,
423 cmd->conn->cmd_ident,
424 silc_client_command_nick_change,
425 silc_client_command_dup(cmd));
429 silc_client_command_free(cmd);
432 /* Command LIST. Lists channels on the current server. */
434 SILC_CLIENT_CMD_FUNC(list)
436 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
437 SilcClientConnection conn = cmd->conn;
438 SilcIDCacheEntry id_cache = NULL;
439 SilcChannelEntry channel;
440 SilcBuffer buffer, idp = NULL;
444 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
449 if (cmd->argc == 2) {
452 /* Get the Channel ID of the channel */
453 if (silc_idcache_find_by_name_one(conn->channel_cache, name, &id_cache)) {
454 channel = (SilcChannelEntry)id_cache->context;
455 idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
460 buffer = silc_command_payload_encode_va(SILC_COMMAND_LIST,
461 ++conn->cmd_ident, 0);
463 buffer = silc_command_payload_encode_va(SILC_COMMAND_LIST,
464 ++conn->cmd_ident, 1,
465 1, idp->data, idp->len);
467 silc_client_packet_send(cmd->client, cmd->conn->sock,
468 SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
469 buffer->data, buffer->len, TRUE);
470 silc_buffer_free(buffer);
472 silc_buffer_free(idp);
474 /* Notify application */
478 silc_client_command_free(cmd);
481 /* Command TOPIC. Sets/shows topic on a channel. */
483 SILC_CLIENT_CMD_FUNC(topic)
485 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
486 SilcClientConnection conn = cmd->conn;
487 SilcIDCacheEntry id_cache = NULL;
488 SilcChannelEntry channel;
489 SilcBuffer buffer, idp;
493 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
498 if (cmd->argc < 2 || cmd->argc > 3) {
499 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
500 "Usage: /TOPIC <channel> [<topic>]");
505 if (cmd->argv[1][0] == '*') {
506 if (!conn->current_channel) {
507 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
508 "You are not on any channel");
512 name = conn->current_channel->channel_name;
517 if (!conn->current_channel) {
518 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
519 "You are not on that channel");
524 /* Get the Channel ID of the channel */
525 if (!silc_idcache_find_by_name_one(conn->channel_cache, name, &id_cache)) {
526 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
527 "You are not on that channel");
532 channel = (SilcChannelEntry)id_cache->context;
534 /* Send TOPIC command to the server */
535 idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
537 buffer = silc_command_payload_encode_va(SILC_COMMAND_TOPIC,
538 ++conn->cmd_ident, 2,
539 1, idp->data, idp->len,
541 strlen(cmd->argv[2]));
543 buffer = silc_command_payload_encode_va(SILC_COMMAND_TOPIC,
544 ++conn->cmd_ident, 1,
545 1, idp->data, idp->len);
546 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
547 0, NULL, NULL, buffer->data, buffer->len, TRUE);
548 silc_buffer_free(buffer);
549 silc_buffer_free(idp);
551 /* Notify application */
555 silc_client_command_free(cmd);
558 /* Command INVITE. Invites specific client to join a channel. This is
559 also used to mange the invite list of the channel. */
561 SILC_CLIENT_CMD_FUNC(invite)
563 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
564 SilcClient client = cmd->client;
565 SilcClientConnection conn = cmd->conn;
566 SilcClientEntry client_entry = NULL;
567 SilcChannelEntry channel;
568 SilcBuffer buffer, clidp, chidp;
570 char *nickname = NULL, *name;
574 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
580 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
581 "Usage: /INVITE <channel> [<nickname>[@server>]"
582 "[+|-[<nickname>[@<server>[!<username>[@hostname>]]]]]");
587 if (cmd->argv[1][0] == '*') {
588 if (!conn->current_channel) {
589 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
590 "You are not on any channel");
595 channel = conn->current_channel;
599 channel = silc_client_get_channel(cmd->client, conn, name);
601 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
602 "You are on that channel");
608 /* Parse the typed nickname. */
609 if (cmd->argc == 3) {
610 if (cmd->argv[2][0] != '+' && cmd->argv[2][0] != '-') {
611 if (client->internal->params->nickname_parse)
612 client->internal->params->nickname_parse(cmd->argv[2], &nickname);
614 nickname = strdup(cmd->argv[2]);
616 /* Find client entry */
617 client_entry = silc_idlist_get_client(client, conn, nickname,
625 /* Client entry not found, it was requested thus mark this to be
627 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY,
629 silc_client_command_invite,
630 silc_client_command_dup(cmd));
635 invite = cmd->argv[2];
637 if (cmd->argv[2][0] == '+')
644 /* Send the command */
645 chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
647 clidp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
648 buffer = silc_command_payload_encode_va(SILC_COMMAND_INVITE,
649 ++conn->cmd_ident, 3,
650 1, chidp->data, chidp->len,
651 2, clidp->data, clidp->len,
652 type, invite, invite ?
654 silc_buffer_free(clidp);
656 buffer = silc_command_payload_encode_va(SILC_COMMAND_INVITE,
657 ++conn->cmd_ident, 2,
658 1, chidp->data, chidp->len,
659 type, invite, invite ?
663 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
664 0, NULL, NULL, buffer->data, buffer->len, TRUE);
665 silc_buffer_free(buffer);
666 silc_buffer_free(chidp);
668 /* Notify application */
673 silc_client_command_free(cmd);
678 SilcClientConnection conn;
681 SILC_TASK_CALLBACK(silc_client_command_quit_cb)
683 QuitInternal q = (QuitInternal)context;
685 /* Close connection */
686 q->client->internal->ops->disconnect(q->client, q->conn);
687 silc_client_close_connection(q->client, q->conn->sock->user_data);
692 /* Command QUIT. Closes connection with current server. */
694 SILC_CLIENT_CMD_FUNC(quit)
696 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
701 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
707 buffer = silc_command_payload_encode(SILC_COMMAND_QUIT, cmd->argc - 1,
708 &cmd->argv[1], &cmd->argv_lens[1],
709 &cmd->argv_types[1], 0);
711 buffer = silc_command_payload_encode(SILC_COMMAND_QUIT, 0,
712 NULL, NULL, NULL, 0);
713 silc_client_packet_send(cmd->client, cmd->conn->sock, SILC_PACKET_COMMAND,
715 buffer->data, buffer->len, TRUE);
716 silc_buffer_free(buffer);
718 q = silc_calloc(1, sizeof(*q));
719 q->client = cmd->client;
722 /* Sleep for a while */
725 /* We quit the connection with little timeout */
726 silc_schedule_task_add(cmd->client->schedule, cmd->conn->sock->sock,
727 silc_client_command_quit_cb, (void *)q,
728 1, 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
730 /* Notify application */
734 silc_client_command_free(cmd);
737 /* Timeout callback to remove the killed client from cache */
739 SILC_TASK_CALLBACK(silc_client_command_kill_remove_later)
741 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
742 SilcClient client = cmd->client;
743 SilcClientConnection conn = cmd->conn;
744 SilcClientEntry target;
745 char *nickname = NULL;
747 /* Parse the typed nickname. */
748 if (client->internal->params->nickname_parse)
749 client->internal->params->nickname_parse(cmd->argv[1], &nickname);
751 nickname = strdup(cmd->argv[1]);
753 /* Get the target client */
754 target = silc_idlist_get_client(cmd->client, conn, nickname,
755 cmd->argv[1], FALSE);
757 /* Remove the client from all channels and free it */
758 silc_client_del_client(client, conn, target);
761 silc_client_command_free(cmd);
764 /* Kill command's pending command callback to actually remove the killed
765 client from our local cache. */
767 SILC_CLIENT_CMD_FUNC(kill_remove)
769 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
770 SilcClientCommandReplyContext reply =
771 (SilcClientCommandReplyContext)context2;
772 SilcCommandStatus status;
774 silc_command_get_status(reply->payload, &status, NULL);
775 if (status == SILC_STATUS_OK) {
776 /* Remove with timeout */
777 silc_schedule_task_add(cmd->client->schedule, cmd->conn->sock->sock,
778 silc_client_command_kill_remove_later, context,
779 1, 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
783 silc_client_command_free(cmd);
786 /* Command KILL. Router operator can use this command to remove an client
787 fromthe SILC Network. */
789 SILC_CLIENT_CMD_FUNC(kill)
791 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
792 SilcClient client = cmd->client;
793 SilcClientConnection conn = cmd->conn;
794 SilcBuffer buffer, idp;
795 SilcClientEntry target;
796 char *nickname = NULL;
799 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
805 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
806 "Usage: /KILL <nickname> [<comment>]");
811 /* Parse the typed nickname. */
812 if (client->internal->params->nickname_parse)
813 client->internal->params->nickname_parse(cmd->argv[1], &nickname);
815 nickname = strdup(cmd->argv[1]);
817 /* Get the target client */
818 target = silc_idlist_get_client(cmd->client, conn, nickname,
826 /* Client entry not found, it was requested thus mark this to be
828 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY,
830 silc_client_command_kill,
831 silc_client_command_dup(cmd));
836 /* Send the KILL command to the server */
837 idp = silc_id_payload_encode(target->id, SILC_ID_CLIENT);
839 buffer = silc_command_payload_encode_va(SILC_COMMAND_KILL,
840 ++conn->cmd_ident, 1,
841 1, idp->data, idp->len);
843 buffer = silc_command_payload_encode_va(SILC_COMMAND_KILL,
844 ++conn->cmd_ident, 2,
845 1, idp->data, idp->len,
847 strlen(cmd->argv[2]));
848 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
849 0, NULL, NULL, buffer->data, buffer->len, TRUE);
850 silc_buffer_free(buffer);
851 silc_buffer_free(idp);
853 /* Notify application */
856 /* Register a pending callback that will actually remove the killed
857 client from our cache. */
858 silc_client_command_pending(conn, SILC_COMMAND_KILL, conn->cmd_ident,
859 silc_client_command_kill_remove,
860 silc_client_command_dup(cmd));
864 silc_client_command_free(cmd);
867 /* Command INFO. Request information about specific server. If specific
868 server is not provided the current server is used. */
870 SILC_CLIENT_CMD_FUNC(info)
872 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
873 SilcClientConnection conn = cmd->conn;
878 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
884 name = strdup(cmd->argv[1]);
886 /* Send the command */
888 buffer = silc_command_payload_encode_va(SILC_COMMAND_INFO, 0, 1,
889 1, name, strlen(name));
891 buffer = silc_command_payload_encode(SILC_COMMAND_INFO, 0,
892 NULL, NULL, NULL, 0);
893 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
894 0, NULL, NULL, buffer->data, buffer->len, TRUE);
895 silc_buffer_free(buffer);
899 /* Notify application */
903 silc_client_command_free(cmd);
906 /* Command PING. Sends ping to server. This is used to test the
907 communication channel. */
909 SILC_CLIENT_CMD_FUNC(ping)
911 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
912 SilcClientConnection conn = cmd->conn;
918 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
923 /* Send the command */
924 buffer = silc_command_payload_encode_va(SILC_COMMAND_PING, 0, 1,
925 1, conn->remote_id_data,
926 silc_id_get_len(conn->remote_id,
928 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
929 0, NULL, NULL, buffer->data, buffer->len, TRUE);
930 silc_buffer_free(buffer);
932 id = silc_id_str2id(conn->remote_id_data, conn->remote_id_data_len,
935 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
940 /* Start counting time */
941 for (i = 0; i < conn->ping_count; i++) {
942 if (conn->ping[i].dest_id == NULL) {
943 conn->ping[i].start_time = time(NULL);
944 conn->ping[i].dest_id = id;
945 conn->ping[i].dest_name = strdup(conn->remote_host);
949 if (i >= conn->ping_count) {
950 i = conn->ping_count;
951 conn->ping = silc_realloc(conn->ping, sizeof(*conn->ping) * (i + 1));
952 conn->ping[i].start_time = time(NULL);
953 conn->ping[i].dest_id = id;
954 conn->ping[i].dest_name = strdup(conn->remote_host);
958 /* Notify application */
962 silc_client_command_free(cmd);
965 /* Command JOIN. Joins to a channel. */
967 SILC_CLIENT_CMD_FUNC(join)
969 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
970 SilcClientConnection conn = cmd->conn;
971 SilcChannelEntry channel;
972 SilcBuffer buffer, idp, auth = NULL;
973 char *name, *passphrase = NULL, *pu8, *cipher = NULL, *hmac = NULL;
974 int i, passphrase_len = 0;
977 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
987 /* See if we have joined to the requested channel already */
988 channel = silc_client_get_channel(cmd->client, conn, cmd->argv[1]);
989 if (channel && silc_client_on_channel(channel, conn->local_entry))
992 idp = silc_id_payload_encode(conn->local_id, SILC_ID_CLIENT);
994 if (cmd->argv_lens[1] > 256)
995 cmd->argv_lens[1] = 256;
999 for (i = 2; i < cmd->argc; i++) {
1000 if (!strcasecmp(cmd->argv[i], "-cipher") && cmd->argc > i + 1) {
1001 cipher = cmd->argv[i + 1];
1003 } else if (!strcasecmp(cmd->argv[i], "-hmac") && cmd->argc > i + 1) {
1004 hmac = cmd->argv[i + 1];
1006 } else if (!strcasecmp(cmd->argv[i], "-founder") && cmd->argc > i + 1) {
1007 if (!strcasecmp(cmd->argv[i + 1], "-pubkey")) {
1008 auth = silc_auth_public_key_auth_generate(cmd->client->public_key,
1009 cmd->client->private_key,
1010 cmd->client->rng, conn->hash,
1014 auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
1016 cmd->argv_lens[i + 1]);
1020 /* Passphrases must be UTF-8 encoded, so encode if it is not */
1021 if (!silc_utf8_valid(cmd->argv[i], cmd->argv_lens[i])) {
1022 passphrase_len = silc_utf8_encoded_len(cmd->argv[i],
1023 cmd->argv_lens[i], 0);
1024 pu8 = silc_calloc(passphrase_len, sizeof(*pu8));
1025 passphrase_len = silc_utf8_encode(cmd->argv[i], cmd->argv_lens[i],
1026 0, pu8, passphrase_len);
1029 passphrase = strdup(cmd->argv[i]);
1030 passphrase_len = cmd->argv_lens[i];
1035 /* Send JOIN command to the server */
1037 silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 6,
1038 1, name, strlen(name),
1039 2, idp->data, idp->len,
1040 3, passphrase, passphrase_len,
1041 4, cipher, cipher ? strlen(cipher) : 0,
1042 5, hmac, hmac ? strlen(hmac) : 0,
1043 6, auth ? auth->data : NULL,
1044 auth ? auth->len : 0);
1045 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1046 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1047 silc_buffer_free(buffer);
1048 silc_buffer_free(idp);
1050 silc_buffer_free(auth);
1051 silc_free(passphrase);
1053 /* Notify application */
1057 silc_client_command_free(cmd);
1060 /* MOTD command. Requests motd from server. */
1062 SILC_CLIENT_CMD_FUNC(motd)
1064 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1065 SilcClientConnection conn = cmd->conn;
1069 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1074 if (cmd->argc < 1 || cmd->argc > 2) {
1075 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1076 "Usage: /MOTD [<server>]");
1081 /* Send TOPIC command to the server */
1083 buffer = silc_command_payload_encode_va(SILC_COMMAND_MOTD, 0, 1,
1084 1, conn->remote_host,
1085 strlen(conn->remote_host));
1087 buffer = silc_command_payload_encode_va(SILC_COMMAND_MOTD, 0, 1,
1090 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1091 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1092 silc_buffer_free(buffer);
1094 /* Notify application */
1098 silc_client_command_free(cmd);
1101 /* UMODE. Set/unset user mode in SILC. This is used mainly to unset the
1102 modes as client cannot set itself server/router operator privileges. */
1104 SILC_CLIENT_CMD_FUNC(umode)
1106 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1107 SilcClientConnection conn = cmd->conn;
1108 SilcBuffer buffer, idp;
1109 unsigned char *cp, modebuf[4];
1110 SilcUInt32 mode, add, len;
1114 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1119 if (cmd->argc < 2) {
1120 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1121 "Usage: /UMODE +|-<modes>");
1126 mode = conn->local_entry->mode;
1128 /* Are we adding or removing mode */
1129 if (cmd->argv[1][0] == '-')
1135 cp = cmd->argv[1] + 1;
1137 for (i = 0; i < len; i++) {
1142 mode |= SILC_UMODE_SERVER_OPERATOR;
1143 mode |= SILC_UMODE_ROUTER_OPERATOR;
1145 mode = SILC_UMODE_NONE;
1150 mode |= SILC_UMODE_SERVER_OPERATOR;
1152 mode &= ~SILC_UMODE_SERVER_OPERATOR;
1156 mode |= SILC_UMODE_ROUTER_OPERATOR;
1158 mode &= ~SILC_UMODE_ROUTER_OPERATOR;
1162 mode |= SILC_UMODE_GONE;
1164 mode &= ~SILC_UMODE_GONE;
1168 mode |= SILC_UMODE_INDISPOSED;
1170 mode &= ~SILC_UMODE_INDISPOSED;
1174 mode |= SILC_UMODE_BUSY;
1176 mode &= ~SILC_UMODE_BUSY;
1180 mode |= SILC_UMODE_PAGE;
1182 mode &= ~SILC_UMODE_PAGE;
1186 mode |= SILC_UMODE_HYPER;
1188 mode &= ~SILC_UMODE_HYPER;
1192 mode |= SILC_UMODE_ROBOT;
1194 mode &= ~SILC_UMODE_ROBOT;
1198 mode |= SILC_UMODE_BLOCK_PRIVMSG;
1200 mode &= ~SILC_UMODE_BLOCK_PRIVMSG;
1209 idp = silc_id_payload_encode(conn->local_id, SILC_ID_CLIENT);
1210 SILC_PUT32_MSB(mode, modebuf);
1212 /* Send the command packet. We support sending only one mode at once
1213 that requires an argument. */
1215 silc_command_payload_encode_va(SILC_COMMAND_UMODE, ++conn->cmd_ident, 2,
1216 1, idp->data, idp->len,
1217 2, modebuf, sizeof(modebuf));
1218 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1219 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1220 silc_buffer_free(buffer);
1221 silc_buffer_free(idp);
1223 /* Notify application */
1227 silc_client_command_free(cmd);
1230 /* CMODE command. Sets channel mode. Modes that does not require any arguments
1231 can be set several at once. Those modes that require argument must be set
1232 separately (unless set with modes that does not require arguments). */
1234 SILC_CLIENT_CMD_FUNC(cmode)
1236 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1237 SilcClientConnection conn = cmd->conn;
1238 SilcChannelEntry channel;
1239 SilcBuffer buffer, chidp, auth = NULL;
1240 unsigned char *name, *cp, modebuf[4], tmp[4], *arg = NULL;
1241 SilcUInt32 mode, add, type, len, arg_len = 0;
1245 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1250 if (cmd->argc < 3) {
1251 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1252 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1257 if (cmd->argv[1][0] == '*') {
1258 if (!conn->current_channel) {
1259 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1260 "You are not on any channel");
1265 channel = conn->current_channel;
1267 name = cmd->argv[1];
1269 channel = silc_client_get_channel(cmd->client, conn, name);
1271 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1272 "You are on that channel");
1278 mode = channel->mode;
1280 /* Are we adding or removing mode */
1281 if (cmd->argv[2][0] == '-')
1286 /* Argument type to be sent to server */
1290 cp = cmd->argv[2] + 1;
1292 for (i = 0; i < len; i++) {
1296 mode |= SILC_CHANNEL_MODE_PRIVATE;
1298 mode &= ~SILC_CHANNEL_MODE_PRIVATE;
1302 mode |= SILC_CHANNEL_MODE_SECRET;
1304 mode &= ~SILC_CHANNEL_MODE_SECRET;
1308 mode |= SILC_CHANNEL_MODE_PRIVKEY;
1310 mode &= ~SILC_CHANNEL_MODE_PRIVKEY;
1314 mode |= SILC_CHANNEL_MODE_INVITE;
1316 mode &= ~SILC_CHANNEL_MODE_INVITE;
1320 mode |= SILC_CHANNEL_MODE_TOPIC;
1322 mode &= ~SILC_CHANNEL_MODE_TOPIC;
1326 mode |= SILC_CHANNEL_MODE_SILENCE_USERS;
1328 mode &= ~SILC_CHANNEL_MODE_SILENCE_USERS;
1332 mode |= SILC_CHANNEL_MODE_SILENCE_OPERS;
1334 mode &= ~SILC_CHANNEL_MODE_SILENCE_OPERS;
1339 mode |= SILC_CHANNEL_MODE_ULIMIT;
1341 if (cmd->argc < 4) {
1342 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1343 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1347 ll = atoi(cmd->argv[3]);
1348 SILC_PUT32_MSB(ll, tmp);
1352 mode &= ~SILC_CHANNEL_MODE_ULIMIT;
1357 mode |= SILC_CHANNEL_MODE_PASSPHRASE;
1359 if (cmd->argc < 4) {
1360 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1361 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1366 arg_len = cmd->argv_lens[3];
1368 mode &= ~SILC_CHANNEL_MODE_PASSPHRASE;
1373 mode |= SILC_CHANNEL_MODE_CIPHER;
1375 if (cmd->argc < 4) {
1376 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1377 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1382 arg_len = cmd->argv_lens[3];
1384 mode &= ~SILC_CHANNEL_MODE_CIPHER;
1389 mode |= SILC_CHANNEL_MODE_HMAC;
1391 if (cmd->argc < 4) {
1392 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1393 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1398 arg_len = cmd->argv_lens[3];
1400 mode &= ~SILC_CHANNEL_MODE_HMAC;
1405 mode |= SILC_CHANNEL_MODE_FOUNDER_AUTH;
1408 if (cmd->argc < 4) {
1409 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1410 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1415 if (!strcasecmp(cmd->argv[3], "-pubkey")) {
1416 auth = silc_auth_public_key_auth_generate(cmd->client->public_key,
1417 cmd->client->private_key,
1423 auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
1424 cmd->argv[3], cmd->argv_lens[3]);
1428 arg_len = auth->len;
1430 mode &= ~SILC_CHANNEL_MODE_FOUNDER_AUTH;
1440 chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1441 SILC_PUT32_MSB(mode, modebuf);
1443 /* Send the command packet. We support sending only one mode at once
1444 that requires an argument. */
1447 silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 3,
1448 1, chidp->data, chidp->len,
1449 2, modebuf, sizeof(modebuf),
1450 type, arg, arg_len);
1453 silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 2,
1454 1, chidp->data, chidp->len,
1455 2, modebuf, sizeof(modebuf));
1458 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1459 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1460 silc_buffer_free(buffer);
1461 silc_buffer_free(chidp);
1463 silc_buffer_free(auth);
1465 /* Notify application */
1469 silc_client_command_free(cmd);
1472 /* CUMODE command. Changes client's mode on a channel. */
1474 SILC_CLIENT_CMD_FUNC(cumode)
1476 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1477 SilcClient client = cmd->client;
1478 SilcClientConnection conn = cmd->conn;
1479 SilcChannelEntry channel;
1480 SilcChannelUser chu;
1481 SilcClientEntry client_entry;
1482 SilcBuffer buffer, clidp, chidp, auth = NULL;
1483 unsigned char *name, *cp, modebuf[4];
1484 SilcUInt32 mode = 0, add, len;
1485 char *nickname = NULL;
1489 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1494 if (cmd->argc < 4) {
1495 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1496 "Usage: /CUMODE <channel> +|-<modes> <nickname>[@<server>]");
1501 if (cmd->argv[1][0] == '*') {
1502 if (!conn->current_channel) {
1503 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1504 "You are not on any channel");
1509 channel = conn->current_channel;
1511 name = cmd->argv[1];
1513 channel = silc_client_get_channel(cmd->client, conn, name);
1515 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1516 "You are on that channel");
1522 /* Parse the typed nickname. */
1523 if (client->internal->params->nickname_parse)
1524 client->internal->params->nickname_parse(cmd->argv[3], &nickname);
1526 nickname = strdup(cmd->argv[3]);
1528 /* Find client entry */
1529 client_entry = silc_idlist_get_client(cmd->client, conn, nickname,
1530 cmd->argv[3], TRUE);
1531 if (!client_entry) {
1537 /* Client entry not found, it was requested thus mark this to be
1539 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY,
1541 silc_client_command_cumode,
1542 silc_client_command_dup(cmd));
1547 /* Get the current mode */
1548 chu = silc_client_on_channel(channel, client_entry);
1552 /* Are we adding or removing mode */
1553 if (cmd->argv[2][0] == '-')
1559 cp = cmd->argv[2] + 1;
1561 for (i = 0; i < len; i++) {
1565 mode |= SILC_CHANNEL_UMODE_CHANFO;
1566 mode |= SILC_CHANNEL_UMODE_CHANOP;
1567 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
1569 mode = SILC_CHANNEL_UMODE_NONE;
1574 if (cmd->argc == 5) {
1575 if (!strcasecmp(cmd->argv[4], "-pubkey")) {
1576 auth = silc_auth_public_key_auth_generate(cmd->client->public_key,
1577 cmd->client->private_key,
1583 auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
1584 cmd->argv[4], cmd->argv_lens[4]);
1587 mode |= SILC_CHANNEL_UMODE_CHANFO;
1589 mode &= ~SILC_CHANNEL_UMODE_CHANFO;
1594 mode |= SILC_CHANNEL_UMODE_CHANOP;
1596 mode &= ~SILC_CHANNEL_UMODE_CHANOP;
1600 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
1602 mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
1611 chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1612 SILC_PUT32_MSB(mode, modebuf);
1613 clidp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
1615 /* Send the command packet. We support sending only one mode at once
1616 that requires an argument. */
1617 buffer = silc_command_payload_encode_va(SILC_COMMAND_CUMODE, 0,
1619 1, chidp->data, chidp->len,
1621 3, clidp->data, clidp->len,
1622 4, auth ? auth->data : NULL,
1623 auth ? auth->len : 0);
1625 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1626 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1627 silc_buffer_free(buffer);
1628 silc_buffer_free(chidp);
1629 silc_buffer_free(clidp);
1631 silc_buffer_free(auth);
1633 /* Notify application */
1637 silc_free(nickname);
1638 silc_client_command_free(cmd);
1641 /* KICK command. Kicks a client out of channel. */
1643 SILC_CLIENT_CMD_FUNC(kick)
1645 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1646 SilcClient client = cmd->client;
1647 SilcClientConnection conn = cmd->conn;
1648 SilcIDCacheEntry id_cache = NULL;
1649 SilcChannelEntry channel;
1650 SilcBuffer buffer, idp, idp2;
1651 SilcClientEntry target;
1653 char *nickname = NULL;
1656 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1661 if (cmd->argc < 3) {
1662 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1663 "Usage: /KICK <channel> <nickname> [<comment>]");
1668 if (cmd->argv[1][0] == '*') {
1669 if (!conn->current_channel) {
1670 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1671 "You are not on any channel");
1675 name = conn->current_channel->channel_name;
1677 name = cmd->argv[1];
1680 if (!conn->current_channel) {
1681 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1682 "You are not on that channel");
1687 /* Get the Channel ID of the channel */
1688 if (!silc_idcache_find_by_name_one(conn->channel_cache, name, &id_cache)) {
1689 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1690 "You are not on that channel");
1695 channel = (SilcChannelEntry)id_cache->context;
1697 /* Parse the typed nickname. */
1698 if (client->internal->params->nickname_parse)
1699 client->internal->params->nickname_parse(cmd->argv[2], &nickname);
1701 nickname = strdup(cmd->argv[2]);
1703 /* Get the target client */
1704 target = silc_idlist_get_client(cmd->client, conn, nickname,
1705 cmd->argv[2], FALSE);
1707 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1708 "No such client: %s", cmd->argv[2]);
1713 /* Send KICK command to the server */
1714 idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1715 idp2 = silc_id_payload_encode(target->id, SILC_ID_CLIENT);
1717 buffer = silc_command_payload_encode_va(SILC_COMMAND_KICK, 0, 2,
1718 1, idp->data, idp->len,
1719 2, idp2->data, idp2->len);
1721 buffer = silc_command_payload_encode_va(SILC_COMMAND_KICK, 0, 3,
1722 1, idp->data, idp->len,
1723 2, idp2->data, idp2->len,
1725 strlen(cmd->argv[3]));
1726 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1727 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1728 silc_buffer_free(buffer);
1729 silc_buffer_free(idp);
1730 silc_buffer_free(idp2);
1732 /* Notify application */
1736 silc_free(nickname);
1737 silc_client_command_free(cmd);
1740 static void silc_client_command_oper_send(unsigned char *data,
1741 SilcUInt32 data_len, void *context)
1743 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1744 SilcClientConnection conn = cmd->conn;
1745 SilcBuffer buffer, auth;
1747 if (cmd->argc >= 3) {
1748 /* Encode the public key authentication payload */
1749 auth = silc_auth_public_key_auth_generate(cmd->client->public_key,
1750 cmd->client->private_key,
1751 cmd->client->rng, conn->hash,
1755 /* Encode the password authentication payload */
1756 auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
1760 buffer = silc_command_payload_encode_va(SILC_COMMAND_OPER, 0, 2,
1762 strlen(cmd->argv[1]),
1763 2, auth->data, auth->len);
1764 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1765 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1767 silc_buffer_free(buffer);
1768 silc_buffer_free(auth);
1770 /* Notify application */
1774 /* OPER command. Used to obtain server operator privileges. */
1776 SILC_CLIENT_CMD_FUNC(oper)
1778 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1779 SilcClientConnection conn = cmd->conn;
1782 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1787 if (cmd->argc < 2) {
1788 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1789 "Usage: /OPER <username> [-pubkey]");
1794 if (cmd->argc < 3) {
1795 /* Get passphrase */
1796 cmd->client->internal->ops->ask_passphrase(cmd->client, conn,
1797 silc_client_command_oper_send,
1802 silc_client_command_oper_send(NULL, 0, context);
1805 silc_client_command_free(cmd);
1808 static void silc_client_command_silcoper_send(unsigned char *data,
1809 SilcUInt32 data_len,
1812 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1813 SilcClientConnection conn = cmd->conn;
1814 SilcBuffer buffer, auth;
1816 if (cmd->argc >= 3) {
1817 /* Encode the public key authentication payload */
1818 auth = silc_auth_public_key_auth_generate(cmd->client->public_key,
1819 cmd->client->private_key,
1820 cmd->client->rng, conn->hash,
1824 /* Encode the password authentication payload */
1825 auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
1829 buffer = silc_command_payload_encode_va(SILC_COMMAND_SILCOPER, 0, 2,
1831 strlen(cmd->argv[1]),
1832 2, auth->data, auth->len);
1833 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1834 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1836 silc_buffer_free(buffer);
1837 silc_buffer_free(auth);
1839 /* Notify application */
1843 /* SILCOPER command. Used to obtain router operator privileges. */
1845 SILC_CLIENT_CMD_FUNC(silcoper)
1847 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1848 SilcClientConnection conn = cmd->conn;
1851 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1856 if (cmd->argc < 2) {
1857 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1858 "Usage: /SILCOPER <username> [-pubkey]");
1863 if (cmd->argc < 3) {
1864 /* Get passphrase */
1865 cmd->client->internal->ops->ask_passphrase(cmd->client, conn,
1866 silc_client_command_silcoper_send,
1871 silc_client_command_silcoper_send(NULL, 0, context);
1874 silc_client_command_free(cmd);
1877 /* Command BAN. This is used to manage the ban list of the channel. */
1879 SILC_CLIENT_CMD_FUNC(ban)
1881 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1882 SilcClientConnection conn = cmd->conn;
1883 SilcChannelEntry channel;
1884 SilcBuffer buffer, chidp;
1886 char *name, *ban = NULL;
1889 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1894 if (cmd->argc < 2) {
1895 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1896 "Usage: /BAN <channel> "
1897 "[+|-[<nickname>[@<server>[!<username>[@hostname>]]]]]");
1902 if (cmd->argv[1][0] == '*') {
1903 if (!conn->current_channel) {
1904 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1905 "You are not on any channel");
1910 channel = conn->current_channel;
1912 name = cmd->argv[1];
1914 channel = silc_client_get_channel(cmd->client, conn, name);
1916 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1917 "You are on that channel");
1923 if (cmd->argc == 3) {
1924 if (cmd->argv[2][0] == '+')
1933 chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1935 /* Send the command */
1936 buffer = silc_command_payload_encode_va(SILC_COMMAND_BAN,
1937 ++conn->cmd_ident, 2,
1938 1, chidp->data, chidp->len,
1939 type, ban, ban ? strlen(ban) : 0);
1940 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1941 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1942 silc_buffer_free(buffer);
1943 silc_buffer_free(chidp);
1945 /* Notify application */
1949 silc_client_command_free(cmd);
1952 /* Command DETACH. This is used to detach from the server */
1954 SILC_CLIENT_CMD_FUNC(detach)
1956 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1957 SilcClientConnection conn = cmd->conn;
1961 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1966 buffer = silc_command_payload_encode_va(SILC_COMMAND_DETACH,
1967 ++conn->cmd_ident, 0);
1968 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1969 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1970 silc_buffer_free(buffer);
1972 /* Notify application */
1976 silc_client_command_free(cmd);
1979 /* LEAVE command. Leaves a channel. Client removes itself from a channel. */
1981 SILC_CLIENT_CMD_FUNC(leave)
1983 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1984 SilcClientConnection conn = cmd->conn;
1985 SilcChannelEntry channel;
1986 SilcChannelUser chu;
1987 SilcBuffer buffer, idp;
1991 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1996 if (cmd->argc != 2) {
1997 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1998 "Usage: /LEAVE <channel>");
2003 if (cmd->argv[1][0] == '*') {
2004 if (!conn->current_channel) {
2005 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
2006 "You are not on any channel");
2010 name = conn->current_channel->channel_name;
2012 name = cmd->argv[1];
2015 /* Get the channel entry */
2016 channel = silc_client_get_channel(cmd->client, conn, name);
2018 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
2019 "You are not on that channel");
2024 /* Remove us from channel */
2025 chu = silc_client_on_channel(channel, conn->local_entry);
2027 silc_hash_table_del(chu->client->channels, chu->channel);
2028 silc_hash_table_del(chu->channel->user_list, chu->client);
2032 /* Send LEAVE command to the server */
2033 idp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
2034 buffer = silc_command_payload_encode_va(SILC_COMMAND_LEAVE, 0, 1,
2035 1, idp->data, idp->len);
2036 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
2037 0, NULL, NULL, buffer->data, buffer->len, TRUE);
2038 silc_buffer_free(buffer);
2039 silc_buffer_free(idp);
2041 /* Notify application */
2044 if (conn->current_channel == channel)
2045 conn->current_channel = NULL;
2047 silc_client_del_channel(cmd->client, cmd->conn, channel);
2050 silc_client_command_free(cmd);
2053 /* Command USERS. Requests the USERS of the clients joined on requested
2056 SILC_CLIENT_CMD_FUNC(users)
2058 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
2059 SilcClientConnection conn = cmd->conn;
2064 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
2069 if (cmd->argc != 2) {
2070 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
2071 "Usage: /USERS <channel>");
2076 if (cmd->argv[1][0] == '*') {
2077 if (!conn->current_channel) {
2078 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
2079 "You are not on any channel");
2083 name = conn->current_channel->channel_name;
2085 name = cmd->argv[1];
2088 /* Send USERS command to the server */
2089 buffer = silc_command_payload_encode_va(SILC_COMMAND_USERS,
2090 ++conn->cmd_ident, 1,
2091 2, name, strlen(name));
2092 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND,
2093 NULL, 0, NULL, NULL, buffer->data,
2095 silc_buffer_free(buffer);
2097 /* Notify application */
2101 silc_client_command_free(cmd);
2104 /* Command GETKEY. Used to fetch remote client's public key. */
2106 SILC_CLIENT_CMD_FUNC(getkey)
2108 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
2109 SilcClientConnection conn = cmd->conn;
2110 SilcClient client = cmd->client;
2111 SilcClientEntry client_entry = NULL;
2112 SilcServerEntry server_entry = NULL;
2113 char *nickname = NULL;
2114 SilcBuffer idp, buffer;
2116 SILC_LOG_DEBUG(("Start"));
2119 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
2124 if (cmd->argc < 2) {
2125 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_INFO,
2126 "Usage: /GETKEY <nickname or server name>");
2131 /* Parse the typed nickname. */
2132 if (client->internal->params->nickname_parse)
2133 client->internal->params->nickname_parse(cmd->argv[1], &nickname);
2135 nickname = strdup(cmd->argv[1]);
2137 /* Find client entry */
2138 client_entry = silc_idlist_get_client(client, conn, nickname, cmd->argv[1],
2140 if (!client_entry) {
2141 /* Check whether user requested server actually */
2142 server_entry = silc_client_get_server(client, conn, cmd->argv[1]);
2144 if (!server_entry) {
2145 /* No. what ever user wants we don't have it, so resolve it. We
2146 will first try to resolve the client, and if that fails then
2147 we'll try to resolve the server. */
2149 if (!cmd->pending) {
2150 /* This will send the IDENTIFY command for nickname */
2151 silc_idlist_get_client(client, conn, nickname, cmd->argv[1], TRUE);
2152 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY,
2154 silc_client_command_getkey,
2155 silc_client_command_dup(cmd));
2159 SilcClientCommandReplyContext reply =
2160 (SilcClientCommandReplyContext)context2;
2161 SilcCommandStatus error;
2163 /* If nickname was not found, then resolve the server. */
2164 silc_command_get_status(reply->payload, NULL, &error);
2165 if (error == SILC_STATUS_ERR_NO_SUCH_NICK) {
2166 /* This sends the IDENTIFY command to resolve the server. */
2167 silc_client_command_register(client, SILC_COMMAND_IDENTIFY,
2169 silc_client_command_reply_identify_i, 0,
2171 silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
2173 2, cmd->argv[1], cmd->argv_lens[1]);
2174 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY,
2176 silc_client_command_getkey,
2177 silc_client_command_dup(cmd));
2181 /* If server was not found, then we've resolved both nickname and
2182 server and did not find anybody. */
2183 if (error == SILC_STATUS_ERR_NO_SUCH_SERVER) {
2184 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, "%s",
2185 silc_client_command_status_message(SILC_STATUS_ERR_NO_SUCH_NICK));
2186 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, "%s",
2187 silc_client_command_status_message(error));
2197 idp = silc_id_payload_encode(server_entry->server_id, SILC_ID_SERVER);
2199 idp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
2202 buffer = silc_command_payload_encode_va(SILC_COMMAND_GETKEY, 0, 1,
2203 1, idp->data, idp->len);
2204 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
2205 0, NULL, NULL, buffer->data, buffer->len, TRUE);
2206 silc_buffer_free(buffer);
2207 silc_buffer_free(idp);
2209 /* Notify application */
2213 silc_free(nickname);
2214 silc_client_command_free(cmd);
2217 /* Register a new command indicated by the `command' to the SILC client.
2218 The `name' is optional command name. If provided the command may be
2219 searched using the silc_client_command_find by that name. The
2220 `command_function' is the function to be called when the command is
2221 executed, and the `command_reply_function' is the function to be
2222 called after the server has sent reply back to the command.
2224 The `ident' is optional identifier for the command. If non-zero
2225 the `command_reply_function' for the command type `command' will be
2226 called only if the command reply sent by server includes the
2227 command identifier `ident'. Application usually does not need it
2228 and set it to zero value. */
2230 bool silc_client_command_register(SilcClient client,
2231 SilcCommand command,
2233 SilcCommandCb command_function,
2234 SilcCommandCb command_reply_function,
2238 SilcClientCommand cmd;
2240 cmd = silc_calloc(1, sizeof(*cmd));
2242 cmd->command = command_function;
2243 cmd->reply = command_reply_function;
2244 cmd->name = name ? strdup(name) : NULL;
2245 cmd->max_args = max_args;
2248 silc_list_add(client->internal->commands, cmd);
2253 /* Unregister a command indicated by the `command' with command function
2254 `command_function' and command reply function `command_reply_function'.
2255 Returns TRUE if the command was found and unregistered. */
2257 bool silc_client_command_unregister(SilcClient client,
2258 SilcCommand command,
2259 SilcCommandCb command_function,
2260 SilcCommandCb command_reply_function,
2263 SilcClientCommand cmd;
2265 silc_list_start(client->internal->commands);
2266 while ((cmd = silc_list_get(client->internal->commands)) != SILC_LIST_END) {
2267 if (cmd->cmd == command && cmd->command == command_function &&
2268 cmd->reply == command_reply_function && cmd->ident == ident) {
2269 silc_list_del(client->internal->commands, cmd);
2270 silc_free(cmd->name);
2279 /* Private range commands, specific to this implementation (and compatible
2280 with SILC Server). */
2282 /* CONNECT command. Connects the server to another server. */
2284 SILC_CLIENT_CMD_FUNC(connect)
2286 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
2287 SilcClientConnection conn = cmd->conn;
2289 unsigned char port[4];
2293 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
2298 if (cmd->argc < 2) {
2299 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
2300 "Usage: /CONNECT <server> [<port>]");
2305 if (cmd->argc == 3) {
2306 tmp = atoi(cmd->argv[2]);
2307 SILC_PUT32_MSB(tmp, port);
2311 buffer = silc_command_payload_encode_va(SILC_COMMAND_PRIV_CONNECT, 0, 2,
2313 strlen(cmd->argv[1]),
2316 buffer = silc_command_payload_encode_va(SILC_COMMAND_PRIV_CONNECT, 0, 1,
2318 strlen(cmd->argv[1]));
2319 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
2320 0, NULL, NULL, buffer->data, buffer->len, TRUE);
2321 silc_buffer_free(buffer);
2323 /* Notify application */
2327 silc_client_command_free(cmd);
2331 /* CLOSE command. Close server connection to the remote server */
2333 SILC_CLIENT_CMD_FUNC(close)
2335 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
2336 SilcClientConnection conn = cmd->conn;
2338 unsigned char port[4];
2342 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
2347 if (cmd->argc < 2) {
2348 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
2349 "Usage: /CLOSE <server> [<port>]");
2354 if (cmd->argc == 3) {
2355 tmp = atoi(cmd->argv[2]);
2356 SILC_PUT32_MSB(tmp, port);
2360 buffer = silc_command_payload_encode_va(SILC_COMMAND_PRIV_CLOSE, 0, 2,
2362 strlen(cmd->argv[1]),
2365 buffer = silc_command_payload_encode_va(SILC_COMMAND_PRIV_CLOSE, 0, 1,
2367 strlen(cmd->argv[1]));
2368 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
2369 0, NULL, NULL, buffer->data, buffer->len, TRUE);
2370 silc_buffer_free(buffer);
2372 /* Notify application */
2376 silc_client_command_free(cmd);
2379 /* SHUTDOWN command. Shutdowns the server. */
2381 SILC_CLIENT_CMD_FUNC(shutdown)
2383 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
2386 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
2391 /* Send the command */
2392 silc_client_command_send(cmd->client, cmd->conn,
2393 SILC_COMMAND_PRIV_SHUTDOWN, 0, 0);
2395 /* Notify application */
2399 silc_client_command_free(cmd);
2402 /* Register all default commands provided by the client library for the
2405 void silc_client_commands_register(SilcClient client)
2407 silc_list_init(client->internal->commands, struct SilcClientCommandStruct,
2410 SILC_CLIENT_CMD(whois, WHOIS, "WHOIS", 3);
2411 SILC_CLIENT_CMD(whowas, WHOWAS, "WHOWAS", 3);
2412 SILC_CLIENT_CMD(identify, IDENTIFY, "IDENTIFY", 3);
2413 SILC_CLIENT_CMD(nick, NICK, "NICK", 2);
2414 SILC_CLIENT_CMD(list, LIST, "LIST", 2);
2415 SILC_CLIENT_CMD(topic, TOPIC, "TOPIC", 3);
2416 SILC_CLIENT_CMD(invite, INVITE, "INVITE", 3);
2417 SILC_CLIENT_CMD(quit, QUIT, "QUIT", 2);
2418 SILC_CLIENT_CMD(kill, KILL, "KILL", 3);
2419 SILC_CLIENT_CMD(info, INFO, "INFO", 2);
2420 SILC_CLIENT_CMD(ping, PING, "PING", 2);
2421 SILC_CLIENT_CMD(oper, OPER, "OPER", 3);
2422 SILC_CLIENT_CMD(join, JOIN, "JOIN", 9);
2423 SILC_CLIENT_CMD(motd, MOTD, "MOTD", 2);
2424 SILC_CLIENT_CMD(umode, UMODE, "UMODE", 2);
2425 SILC_CLIENT_CMD(cmode, CMODE, "CMODE", 4);
2426 SILC_CLIENT_CMD(cumode, CUMODE, "CUMODE", 5);
2427 SILC_CLIENT_CMD(kick, KICK, "KICK", 4);
2428 SILC_CLIENT_CMD(ban, BAN, "BAN", 3);
2429 SILC_CLIENT_CMD(detach, DETACH, "DETACH", 0);
2430 SILC_CLIENT_CMD(silcoper, SILCOPER, "SILCOPER", 3);
2431 SILC_CLIENT_CMD(leave, LEAVE, "LEAVE", 2);
2432 SILC_CLIENT_CMD(users, USERS, "USERS", 2);
2433 SILC_CLIENT_CMD(getkey, GETKEY, "GETKEY", 2);
2435 SILC_CLIENT_CMD(connect, PRIV_CONNECT, "CONNECT", 3);
2436 SILC_CLIENT_CMD(close, PRIV_CLOSE, "CLOSE", 3);
2437 SILC_CLIENT_CMD(shutdown, PRIV_SHUTDOWN, "SHUTDOWN", 1);
2440 /* Unregister all commands. */
2442 void silc_client_commands_unregister(SilcClient client)
2444 SILC_CLIENT_CMDU(whois, WHOIS, "WHOIS");
2445 SILC_CLIENT_CMDU(whowas, WHOWAS, "WHOWAS");
2446 SILC_CLIENT_CMDU(identify, IDENTIFY, "IDENTIFY");
2447 SILC_CLIENT_CMDU(nick, NICK, "NICK");
2448 SILC_CLIENT_CMDU(list, LIST, "LIST");
2449 SILC_CLIENT_CMDU(topic, TOPIC, "TOPIC");
2450 SILC_CLIENT_CMDU(invite, INVITE, "INVITE");
2451 SILC_CLIENT_CMDU(quit, QUIT, "QUIT");
2452 SILC_CLIENT_CMDU(kill, KILL, "KILL");
2453 SILC_CLIENT_CMDU(info, INFO, "INFO");
2454 SILC_CLIENT_CMDU(ping, PING, "PING");
2455 SILC_CLIENT_CMDU(oper, OPER, "OPER");
2456 SILC_CLIENT_CMDU(join, JOIN, "JOIN");
2457 SILC_CLIENT_CMDU(motd, MOTD, "MOTD");
2458 SILC_CLIENT_CMDU(umode, UMODE, "UMODE");
2459 SILC_CLIENT_CMDU(cmode, CMODE, "CMODE");
2460 SILC_CLIENT_CMDU(cumode, CUMODE, "CUMODE");
2461 SILC_CLIENT_CMDU(kick, KICK, "KICK");
2462 SILC_CLIENT_CMDU(ban, BAN, "BAN");
2463 SILC_CLIENT_CMDU(detach, DETACH, "DETACH");
2464 SILC_CLIENT_CMDU(silcoper, SILCOPER, "SILCOPER");
2465 SILC_CLIENT_CMDU(leave, LEAVE, "LEAVE");
2466 SILC_CLIENT_CMDU(users, USERS, "USERS");
2467 SILC_CLIENT_CMDU(getkey, GETKEY, "GETKEY");
2469 SILC_CLIENT_CMDU(connect, PRIV_CONNECT, "CONNECT");
2470 SILC_CLIENT_CMDU(close, PRIV_CLOSE, "CLOSE");
2471 SILC_CLIENT_CMDU(shutdown, PRIV_SHUTDOWN, "SHUTDOWN");