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;
216 unsigned char count[4];
219 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
224 /* Given without arguments fetches client's own information */
226 buffer = silc_id_payload_encode(cmd->conn->local_id, SILC_ID_CLIENT);
227 silc_client_command_send(cmd->client, cmd->conn, SILC_COMMAND_WHOIS,
229 1, 3, buffer->data, buffer->len);
230 silc_buffer_free(buffer);
234 if (cmd->argc == 2) {
235 buffer = silc_command_payload_encode_va(SILC_COMMAND_WHOIS,
236 ++conn->cmd_ident, 1,
240 int c = atoi(cmd->argv[2]);
241 memset(count, 0, sizeof(count));
242 SILC_PUT32_MSB(c, count);
243 buffer = silc_command_payload_encode_va(SILC_COMMAND_WHOIS,
244 ++conn->cmd_ident, 2,
245 1, cmd->argv[1], cmd->argv_lens[1],
246 2, count, sizeof(count));
248 silc_client_packet_send(cmd->client, cmd->conn->sock,
249 SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
250 buffer->data, buffer->len, TRUE);
251 silc_buffer_free(buffer);
253 /* Notify application */
257 silc_client_command_free(cmd);
260 /* Command WHOWAS. This command is used to query history information about
261 specific user that used to exist in the network. */
263 SILC_CLIENT_CMD_FUNC(whowas)
265 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
266 SilcClientConnection conn = cmd->conn;
268 unsigned char count[4];
271 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
276 if (cmd->argc < 2 || cmd->argc > 3) {
277 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
278 "Usage: /WHOWAS <nickname>[@<server>] [<count>]");
283 if (cmd->argc == 2) {
284 buffer = silc_command_payload_encode_va(SILC_COMMAND_WHOWAS,
285 ++conn->cmd_ident, 1,
289 int c = atoi(cmd->argv[2]);
290 memset(count, 0, sizeof(count));
291 SILC_PUT32_MSB(c, count);
292 buffer = silc_command_payload_encode_va(SILC_COMMAND_WHOWAS,
293 ++conn->cmd_ident, 2,
294 1, cmd->argv[1], cmd->argv_lens[1],
295 2, count, sizeof(count));
297 silc_client_packet_send(cmd->client, cmd->conn->sock,
298 SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
299 buffer->data, buffer->len, TRUE);
300 silc_buffer_free(buffer);
302 /* Notify application */
306 silc_client_command_free(cmd);
309 /* Command IDENTIFY. This command is used to query information about
310 specific user, especially ID's.
312 NOTE: This command is used only internally by the client library
313 and application MUST NOT call this command directly. */
315 SILC_CLIENT_CMD_FUNC(identify)
317 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
318 SilcClientConnection conn = cmd->conn;
320 unsigned char count[4];
323 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
327 if (cmd->argc < 2 || cmd->argc > 3)
330 if (cmd->argc == 2) {
331 buffer = silc_command_payload_encode_va(SILC_COMMAND_IDENTIFY,
332 ++conn->cmd_ident, 1,
336 int c = atoi(cmd->argv[2]);
337 memset(count, 0, sizeof(count));
338 SILC_PUT32_MSB(c, count);
339 buffer = silc_command_payload_encode_va(SILC_COMMAND_IDENTIFY,
340 ++conn->cmd_ident, 2,
343 4, count, sizeof(count));
346 silc_client_packet_send(cmd->client, cmd->conn->sock,
347 SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
348 buffer->data, buffer->len, TRUE);
349 silc_buffer_free(buffer);
352 silc_client_command_free(cmd);
355 /* Pending callbcak that will be called after the NICK command was
356 replied by the server. This sets the nickname if there were no
359 SILC_CLIENT_CMD_FUNC(nick_change)
361 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
362 SilcClientConnection conn = cmd->conn;
363 SilcClientCommandReplyContext reply =
364 (SilcClientCommandReplyContext)context2;
365 SilcCommandStatus status;
367 silc_command_get_status(reply->payload, &status, NULL);
368 if (status == SILC_STATUS_OK) {
369 /* Set the nickname */
370 silc_idcache_del_by_context(conn->client_cache, conn->local_entry);
372 silc_free(conn->nickname);
373 conn->nickname = strdup(cmd->argv[1]);
374 conn->local_entry->nickname = conn->nickname;
375 silc_client_nickname_format(cmd->client, conn, conn->local_entry);
376 silc_idcache_add(conn->client_cache, strdup(cmd->argv[1]),
377 conn->local_entry->id, conn->local_entry, 0, NULL);
383 silc_client_command_free(cmd);
386 /* Command NICK. Shows current nickname/sets new nickname on current
389 SILC_CLIENT_CMD_FUNC(nick)
391 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
392 SilcClientConnection conn = cmd->conn;
396 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
402 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
403 "Usage: /NICK <nickname>");
408 if (!strcmp(conn->nickname, cmd->argv[1]))
411 /* Show current nickname */
414 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
415 "Your nickname is %s on server %s",
416 conn->nickname, conn->remote_host);
418 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
419 "Your nickname is %s", conn->nickname);
426 if (cmd->argv_lens[1] > 128)
427 cmd->argv_lens[1] = 128;
429 /* Send the NICK command */
430 buffer = silc_command_payload_encode(SILC_COMMAND_NICK, 1,
434 ++cmd->conn->cmd_ident);
435 silc_client_packet_send(cmd->client, cmd->conn->sock,
436 SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
437 buffer->data, buffer->len, TRUE);
438 silc_buffer_free(buffer);
440 /* Register pending callback that will actually set the new nickname
441 if there were no errors returned by the server. */
442 silc_client_command_pending(conn, SILC_COMMAND_NICK,
443 cmd->conn->cmd_ident,
444 silc_client_command_nick_change,
445 silc_client_command_dup(cmd));
449 silc_client_command_free(cmd);
452 /* Command LIST. Lists channels on the current server. */
454 SILC_CLIENT_CMD_FUNC(list)
456 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
457 SilcClientConnection conn = cmd->conn;
458 SilcIDCacheEntry id_cache = NULL;
459 SilcChannelEntry channel;
460 SilcBuffer buffer, idp = NULL;
464 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
469 if (cmd->argc == 2) {
472 /* Get the Channel ID of the channel */
473 if (silc_idcache_find_by_name_one(conn->channel_cache, name, &id_cache)) {
474 channel = (SilcChannelEntry)id_cache->context;
475 idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
480 buffer = silc_command_payload_encode_va(SILC_COMMAND_LIST,
481 ++conn->cmd_ident, 0);
483 buffer = silc_command_payload_encode_va(SILC_COMMAND_LIST,
484 ++conn->cmd_ident, 1,
485 1, idp->data, idp->len);
487 silc_client_packet_send(cmd->client, cmd->conn->sock,
488 SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
489 buffer->data, buffer->len, TRUE);
490 silc_buffer_free(buffer);
492 silc_buffer_free(idp);
494 /* Notify application */
498 silc_client_command_free(cmd);
501 /* Command TOPIC. Sets/shows topic on a channel. */
503 SILC_CLIENT_CMD_FUNC(topic)
505 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
506 SilcClientConnection conn = cmd->conn;
507 SilcIDCacheEntry id_cache = NULL;
508 SilcChannelEntry channel;
509 SilcBuffer buffer, idp;
513 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
518 if (cmd->argc < 2 || cmd->argc > 3) {
519 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
520 "Usage: /TOPIC <channel> [<topic>]");
525 if (cmd->argv[1][0] == '*') {
526 if (!conn->current_channel) {
527 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
528 "You are not on any channel");
532 name = conn->current_channel->channel_name;
537 if (!conn->current_channel) {
538 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
539 "You are not on that channel");
544 /* Get the Channel ID of the channel */
545 if (!silc_idcache_find_by_name_one(conn->channel_cache, name, &id_cache)) {
546 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
547 "You are not on that channel");
552 channel = (SilcChannelEntry)id_cache->context;
554 /* Send TOPIC command to the server */
555 idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
557 buffer = silc_command_payload_encode_va(SILC_COMMAND_TOPIC,
558 ++conn->cmd_ident, 2,
559 1, idp->data, idp->len,
561 strlen(cmd->argv[2]));
563 buffer = silc_command_payload_encode_va(SILC_COMMAND_TOPIC,
564 ++conn->cmd_ident, 1,
565 1, idp->data, idp->len);
566 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
567 0, NULL, NULL, buffer->data, buffer->len, TRUE);
568 silc_buffer_free(buffer);
569 silc_buffer_free(idp);
571 /* Notify application */
575 silc_client_command_free(cmd);
578 /* Command INVITE. Invites specific client to join a channel. This is
579 also used to mange the invite list of the channel. */
581 SILC_CLIENT_CMD_FUNC(invite)
583 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
584 SilcClient client = cmd->client;
585 SilcClientConnection conn = cmd->conn;
586 SilcClientEntry client_entry = NULL;
587 SilcChannelEntry channel;
588 SilcBuffer buffer, clidp, chidp;
590 char *nickname = NULL, *name;
594 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
600 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
601 "Usage: /INVITE <channel> [<nickname>[@server>]"
602 "[+|-[<nickname>[@<server>[!<username>[@hostname>]]]]]");
607 if (cmd->argv[1][0] == '*') {
608 if (!conn->current_channel) {
609 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
610 "You are not on any channel");
615 channel = conn->current_channel;
619 channel = silc_client_get_channel(cmd->client, conn, name);
621 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
622 "You are on that channel");
628 /* Parse the typed nickname. */
629 if (cmd->argc == 3) {
630 if (cmd->argv[2][0] != '+' && cmd->argv[2][0] != '-') {
631 if (client->internal->params->nickname_parse)
632 client->internal->params->nickname_parse(cmd->argv[2], &nickname);
634 nickname = strdup(cmd->argv[2]);
636 /* Find client entry */
637 client_entry = silc_idlist_get_client(client, conn, nickname,
645 /* Client entry not found, it was requested thus mark this to be
647 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY,
649 silc_client_command_invite,
650 silc_client_command_dup(cmd));
655 invite = cmd->argv[2];
657 if (cmd->argv[2][0] == '+')
664 /* Send the command */
665 chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
667 clidp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
668 buffer = silc_command_payload_encode_va(SILC_COMMAND_INVITE,
669 ++conn->cmd_ident, 3,
670 1, chidp->data, chidp->len,
671 2, clidp->data, clidp->len,
672 type, invite, invite ?
674 silc_buffer_free(clidp);
676 buffer = silc_command_payload_encode_va(SILC_COMMAND_INVITE,
677 ++conn->cmd_ident, 2,
678 1, chidp->data, chidp->len,
679 type, invite, invite ?
683 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
684 0, NULL, NULL, buffer->data, buffer->len, TRUE);
685 silc_buffer_free(buffer);
686 silc_buffer_free(chidp);
688 /* Notify application */
693 silc_client_command_free(cmd);
698 SilcClientConnection conn;
701 SILC_TASK_CALLBACK(silc_client_command_quit_cb)
703 QuitInternal q = (QuitInternal)context;
705 /* Close connection */
706 q->client->internal->ops->disconnect(q->client, q->conn);
707 silc_client_close_connection(q->client, q->conn->sock->user_data);
712 /* Command QUIT. Closes connection with current server. */
714 SILC_CLIENT_CMD_FUNC(quit)
716 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
721 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
727 buffer = silc_command_payload_encode(SILC_COMMAND_QUIT, cmd->argc - 1,
728 &cmd->argv[1], &cmd->argv_lens[1],
729 &cmd->argv_types[1], 0);
731 buffer = silc_command_payload_encode(SILC_COMMAND_QUIT, 0,
732 NULL, NULL, NULL, 0);
733 silc_client_packet_send(cmd->client, cmd->conn->sock, SILC_PACKET_COMMAND,
735 buffer->data, buffer->len, TRUE);
736 silc_buffer_free(buffer);
738 q = silc_calloc(1, sizeof(*q));
739 q->client = cmd->client;
742 /* Sleep for a while */
745 /* We quit the connection with little timeout */
746 silc_schedule_task_add(cmd->client->schedule, cmd->conn->sock->sock,
747 silc_client_command_quit_cb, (void *)q,
748 1, 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
750 /* Notify application */
754 silc_client_command_free(cmd);
757 /* Timeout callback to remove the killed client from cache */
759 SILC_TASK_CALLBACK(silc_client_command_kill_remove_later)
761 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
762 SilcClient client = cmd->client;
763 SilcClientConnection conn = cmd->conn;
764 SilcClientEntry target;
765 char *nickname = NULL;
767 /* Parse the typed nickname. */
768 if (client->internal->params->nickname_parse)
769 client->internal->params->nickname_parse(cmd->argv[1], &nickname);
771 nickname = strdup(cmd->argv[1]);
773 /* Get the target client */
774 target = silc_idlist_get_client(cmd->client, conn, nickname,
775 cmd->argv[1], FALSE);
777 /* Remove the client from all channels and free it */
778 silc_client_del_client(client, conn, target);
781 silc_client_command_free(cmd);
784 /* Kill command's pending command callback to actually remove the killed
785 client from our local cache. */
787 SILC_CLIENT_CMD_FUNC(kill_remove)
789 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
790 SilcClientCommandReplyContext reply =
791 (SilcClientCommandReplyContext)context2;
792 SilcCommandStatus status;
794 silc_command_get_status(reply->payload, &status, NULL);
795 if (status == SILC_STATUS_OK) {
796 /* Remove with timeout */
797 silc_schedule_task_add(cmd->client->schedule, cmd->conn->sock->sock,
798 silc_client_command_kill_remove_later, context,
799 1, 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
803 silc_client_command_free(cmd);
806 /* Command KILL. Router operator can use this command to remove an client
807 fromthe SILC Network. */
809 SILC_CLIENT_CMD_FUNC(kill)
811 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
812 SilcClient client = cmd->client;
813 SilcClientConnection conn = cmd->conn;
814 SilcBuffer buffer, idp;
815 SilcClientEntry target;
816 char *nickname = NULL;
819 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
825 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
826 "Usage: /KILL <nickname> [<comment>]");
831 /* Parse the typed nickname. */
832 if (client->internal->params->nickname_parse)
833 client->internal->params->nickname_parse(cmd->argv[1], &nickname);
835 nickname = strdup(cmd->argv[1]);
837 /* Get the target client */
838 target = silc_idlist_get_client(cmd->client, conn, nickname,
846 /* Client entry not found, it was requested thus mark this to be
848 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY,
850 silc_client_command_kill,
851 silc_client_command_dup(cmd));
856 /* Send the KILL command to the server */
857 idp = silc_id_payload_encode(target->id, SILC_ID_CLIENT);
859 buffer = silc_command_payload_encode_va(SILC_COMMAND_KILL,
860 ++conn->cmd_ident, 1,
861 1, idp->data, idp->len);
863 buffer = silc_command_payload_encode_va(SILC_COMMAND_KILL,
864 ++conn->cmd_ident, 2,
865 1, idp->data, idp->len,
867 strlen(cmd->argv[2]));
868 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
869 0, NULL, NULL, buffer->data, buffer->len, TRUE);
870 silc_buffer_free(buffer);
871 silc_buffer_free(idp);
873 /* Notify application */
876 /* Register a pending callback that will actually remove the killed
877 client from our cache. */
878 silc_client_command_pending(conn, SILC_COMMAND_KILL, conn->cmd_ident,
879 silc_client_command_kill_remove,
880 silc_client_command_dup(cmd));
884 silc_client_command_free(cmd);
887 /* Command INFO. Request information about specific server. If specific
888 server is not provided the current server is used. */
890 SILC_CLIENT_CMD_FUNC(info)
892 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
893 SilcClientConnection conn = cmd->conn;
898 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
904 name = strdup(cmd->argv[1]);
906 /* Send the command */
908 buffer = silc_command_payload_encode_va(SILC_COMMAND_INFO, 0, 1,
909 1, name, strlen(name));
911 buffer = silc_command_payload_encode(SILC_COMMAND_INFO, 0,
912 NULL, NULL, NULL, 0);
913 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
914 0, NULL, NULL, buffer->data, buffer->len, TRUE);
915 silc_buffer_free(buffer);
919 /* Notify application */
923 silc_client_command_free(cmd);
926 /* Command PING. Sends ping to server. This is used to test the
927 communication channel. */
929 SILC_CLIENT_CMD_FUNC(ping)
931 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
932 SilcClientConnection conn = cmd->conn;
938 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
943 /* Send the command */
944 buffer = silc_command_payload_encode_va(SILC_COMMAND_PING, 0, 1,
945 1, conn->remote_id_data,
946 silc_id_get_len(conn->remote_id,
948 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
949 0, NULL, NULL, buffer->data, buffer->len, TRUE);
950 silc_buffer_free(buffer);
952 id = silc_id_str2id(conn->remote_id_data, conn->remote_id_data_len,
955 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
960 /* Start counting time */
961 for (i = 0; i < conn->ping_count; i++) {
962 if (conn->ping[i].dest_id == NULL) {
963 conn->ping[i].start_time = time(NULL);
964 conn->ping[i].dest_id = id;
965 conn->ping[i].dest_name = strdup(conn->remote_host);
969 if (i >= conn->ping_count) {
970 i = conn->ping_count;
971 conn->ping = silc_realloc(conn->ping, sizeof(*conn->ping) * (i + 1));
972 conn->ping[i].start_time = time(NULL);
973 conn->ping[i].dest_id = id;
974 conn->ping[i].dest_name = strdup(conn->remote_host);
978 /* Notify application */
982 silc_client_command_free(cmd);
985 /* Command JOIN. Joins to a channel. */
987 SILC_CLIENT_CMD_FUNC(join)
989 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
990 SilcClientConnection conn = cmd->conn;
991 SilcChannelEntry channel;
992 SilcBuffer buffer, idp, auth = NULL;
993 char *name, *passphrase = NULL, *pu8, *cipher = NULL, *hmac = NULL;
994 int i, passphrase_len = 0;
997 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1002 if (cmd->argc < 2) {
1007 /* See if we have joined to the requested channel already */
1008 channel = silc_client_get_channel(cmd->client, conn, cmd->argv[1]);
1009 if (channel && silc_client_on_channel(channel, conn->local_entry))
1012 idp = silc_id_payload_encode(conn->local_id, SILC_ID_CLIENT);
1014 if (cmd->argv_lens[1] > 256)
1015 cmd->argv_lens[1] = 256;
1017 name = cmd->argv[1];
1019 for (i = 2; i < cmd->argc; i++) {
1020 if (!strcasecmp(cmd->argv[i], "-cipher") && cmd->argc > i + 1) {
1021 cipher = cmd->argv[i + 1];
1023 } else if (!strcasecmp(cmd->argv[i], "-hmac") && cmd->argc > i + 1) {
1024 hmac = cmd->argv[i + 1];
1026 } else if (!strcasecmp(cmd->argv[i], "-founder") && cmd->argc > i + 1) {
1027 if (!strcasecmp(cmd->argv[i + 1], "-pubkey")) {
1028 auth = silc_auth_public_key_auth_generate(cmd->client->public_key,
1029 cmd->client->private_key,
1030 cmd->client->rng, conn->hash,
1034 auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
1036 cmd->argv_lens[i + 1]);
1040 /* Passphrases must be UTF-8 encoded, so encode if it is not */
1041 if (!silc_utf8_valid(cmd->argv[i], cmd->argv_lens[i])) {
1042 passphrase_len = silc_utf8_encoded_len(cmd->argv[i],
1043 cmd->argv_lens[i], 0);
1044 pu8 = silc_calloc(passphrase_len, sizeof(*pu8));
1045 passphrase_len = silc_utf8_encode(cmd->argv[i], cmd->argv_lens[i],
1046 0, pu8, passphrase_len);
1049 passphrase = strdup(cmd->argv[i]);
1050 passphrase_len = cmd->argv_lens[i];
1055 /* Send JOIN command to the server */
1057 silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 6,
1058 1, name, strlen(name),
1059 2, idp->data, idp->len,
1060 3, passphrase, passphrase_len,
1061 4, cipher, cipher ? strlen(cipher) : 0,
1062 5, hmac, hmac ? strlen(hmac) : 0,
1063 6, auth ? auth->data : NULL,
1064 auth ? auth->len : 0);
1065 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1066 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1067 silc_buffer_free(buffer);
1068 silc_buffer_free(idp);
1070 silc_buffer_free(auth);
1071 silc_free(passphrase);
1073 /* Notify application */
1077 silc_client_command_free(cmd);
1080 /* MOTD command. Requests motd from server. */
1082 SILC_CLIENT_CMD_FUNC(motd)
1084 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1085 SilcClientConnection conn = cmd->conn;
1089 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1094 if (cmd->argc < 1 || cmd->argc > 2) {
1095 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1096 "Usage: /MOTD [<server>]");
1101 /* Send TOPIC command to the server */
1103 buffer = silc_command_payload_encode_va(SILC_COMMAND_MOTD, 0, 1,
1104 1, conn->remote_host,
1105 strlen(conn->remote_host));
1107 buffer = silc_command_payload_encode_va(SILC_COMMAND_MOTD, 0, 1,
1110 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1111 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1112 silc_buffer_free(buffer);
1114 /* Notify application */
1118 silc_client_command_free(cmd);
1121 /* UMODE. Set/unset user mode in SILC. This is used mainly to unset the
1122 modes as client cannot set itself server/router operator privileges. */
1124 SILC_CLIENT_CMD_FUNC(umode)
1126 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1127 SilcClientConnection conn = cmd->conn;
1128 SilcBuffer buffer, idp;
1129 unsigned char *cp, modebuf[4];
1130 SilcUInt32 mode, add, len;
1134 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1139 if (cmd->argc < 2) {
1140 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1141 "Usage: /UMODE +|-<modes>");
1146 mode = conn->local_entry->mode;
1148 /* Are we adding or removing mode */
1149 if (cmd->argv[1][0] == '-')
1155 cp = cmd->argv[1] + 1;
1157 for (i = 0; i < len; i++) {
1162 mode |= SILC_UMODE_SERVER_OPERATOR;
1163 mode |= SILC_UMODE_ROUTER_OPERATOR;
1165 mode = SILC_UMODE_NONE;
1170 mode |= SILC_UMODE_SERVER_OPERATOR;
1172 mode &= ~SILC_UMODE_SERVER_OPERATOR;
1176 mode |= SILC_UMODE_ROUTER_OPERATOR;
1178 mode &= ~SILC_UMODE_ROUTER_OPERATOR;
1182 mode |= SILC_UMODE_GONE;
1184 mode &= ~SILC_UMODE_GONE;
1188 mode |= SILC_UMODE_INDISPOSED;
1190 mode &= ~SILC_UMODE_INDISPOSED;
1194 mode |= SILC_UMODE_BUSY;
1196 mode &= ~SILC_UMODE_BUSY;
1200 mode |= SILC_UMODE_PAGE;
1202 mode &= ~SILC_UMODE_PAGE;
1206 mode |= SILC_UMODE_HYPER;
1208 mode &= ~SILC_UMODE_HYPER;
1212 mode |= SILC_UMODE_ROBOT;
1214 mode &= ~SILC_UMODE_ROBOT;
1218 mode |= SILC_UMODE_BLOCK_PRIVMSG;
1220 mode &= ~SILC_UMODE_BLOCK_PRIVMSG;
1229 idp = silc_id_payload_encode(conn->local_id, SILC_ID_CLIENT);
1230 SILC_PUT32_MSB(mode, modebuf);
1232 /* Send the command packet. We support sending only one mode at once
1233 that requires an argument. */
1235 silc_command_payload_encode_va(SILC_COMMAND_UMODE, ++conn->cmd_ident, 2,
1236 1, idp->data, idp->len,
1237 2, modebuf, sizeof(modebuf));
1238 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1239 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1240 silc_buffer_free(buffer);
1241 silc_buffer_free(idp);
1243 /* Notify application */
1247 silc_client_command_free(cmd);
1250 /* CMODE command. Sets channel mode. Modes that does not require any arguments
1251 can be set several at once. Those modes that require argument must be set
1252 separately (unless set with modes that does not require arguments). */
1254 SILC_CLIENT_CMD_FUNC(cmode)
1256 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1257 SilcClientConnection conn = cmd->conn;
1258 SilcChannelEntry channel;
1259 SilcBuffer buffer, chidp, auth = NULL;
1260 unsigned char *name, *cp, modebuf[4], tmp[4], *arg = NULL;
1261 SilcUInt32 mode, add, type, len, arg_len = 0;
1265 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1270 if (cmd->argc < 3) {
1271 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1272 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1277 if (cmd->argv[1][0] == '*') {
1278 if (!conn->current_channel) {
1279 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1280 "You are not on any channel");
1285 channel = conn->current_channel;
1287 name = cmd->argv[1];
1289 channel = silc_client_get_channel(cmd->client, conn, name);
1291 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1292 "You are on that channel");
1298 mode = channel->mode;
1300 /* Are we adding or removing mode */
1301 if (cmd->argv[2][0] == '-')
1306 /* Argument type to be sent to server */
1310 cp = cmd->argv[2] + 1;
1312 for (i = 0; i < len; i++) {
1316 mode |= SILC_CHANNEL_MODE_PRIVATE;
1318 mode &= ~SILC_CHANNEL_MODE_PRIVATE;
1322 mode |= SILC_CHANNEL_MODE_SECRET;
1324 mode &= ~SILC_CHANNEL_MODE_SECRET;
1328 mode |= SILC_CHANNEL_MODE_PRIVKEY;
1330 mode &= ~SILC_CHANNEL_MODE_PRIVKEY;
1334 mode |= SILC_CHANNEL_MODE_INVITE;
1336 mode &= ~SILC_CHANNEL_MODE_INVITE;
1340 mode |= SILC_CHANNEL_MODE_TOPIC;
1342 mode &= ~SILC_CHANNEL_MODE_TOPIC;
1346 mode |= SILC_CHANNEL_MODE_SILENCE_USERS;
1348 mode &= ~SILC_CHANNEL_MODE_SILENCE_USERS;
1352 mode |= SILC_CHANNEL_MODE_SILENCE_OPERS;
1354 mode &= ~SILC_CHANNEL_MODE_SILENCE_OPERS;
1359 mode |= SILC_CHANNEL_MODE_ULIMIT;
1361 if (cmd->argc < 4) {
1362 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1363 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1367 ll = atoi(cmd->argv[3]);
1368 SILC_PUT32_MSB(ll, tmp);
1372 mode &= ~SILC_CHANNEL_MODE_ULIMIT;
1377 mode |= SILC_CHANNEL_MODE_PASSPHRASE;
1379 if (cmd->argc < 4) {
1380 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1381 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1386 arg_len = cmd->argv_lens[3];
1388 mode &= ~SILC_CHANNEL_MODE_PASSPHRASE;
1393 mode |= SILC_CHANNEL_MODE_CIPHER;
1395 if (cmd->argc < 4) {
1396 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1397 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1402 arg_len = cmd->argv_lens[3];
1404 mode &= ~SILC_CHANNEL_MODE_CIPHER;
1409 mode |= SILC_CHANNEL_MODE_HMAC;
1411 if (cmd->argc < 4) {
1412 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1413 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1418 arg_len = cmd->argv_lens[3];
1420 mode &= ~SILC_CHANNEL_MODE_HMAC;
1425 mode |= SILC_CHANNEL_MODE_FOUNDER_AUTH;
1428 if (cmd->argc < 4) {
1429 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1430 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1435 if (!strcasecmp(cmd->argv[3], "-pubkey")) {
1436 auth = silc_auth_public_key_auth_generate(cmd->client->public_key,
1437 cmd->client->private_key,
1443 auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
1444 cmd->argv[3], cmd->argv_lens[3]);
1448 arg_len = auth->len;
1450 mode &= ~SILC_CHANNEL_MODE_FOUNDER_AUTH;
1460 chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1461 SILC_PUT32_MSB(mode, modebuf);
1463 /* Send the command packet. We support sending only one mode at once
1464 that requires an argument. */
1467 silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 3,
1468 1, chidp->data, chidp->len,
1469 2, modebuf, sizeof(modebuf),
1470 type, arg, arg_len);
1473 silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 2,
1474 1, chidp->data, chidp->len,
1475 2, modebuf, sizeof(modebuf));
1478 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1479 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1480 silc_buffer_free(buffer);
1481 silc_buffer_free(chidp);
1483 silc_buffer_free(auth);
1485 /* Notify application */
1489 silc_client_command_free(cmd);
1492 /* CUMODE command. Changes client's mode on a channel. */
1494 SILC_CLIENT_CMD_FUNC(cumode)
1496 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1497 SilcClient client = cmd->client;
1498 SilcClientConnection conn = cmd->conn;
1499 SilcChannelEntry channel;
1500 SilcChannelUser chu;
1501 SilcClientEntry client_entry;
1502 SilcBuffer buffer, clidp, chidp, auth = NULL;
1503 unsigned char *name, *cp, modebuf[4];
1504 SilcUInt32 mode = 0, add, len;
1505 char *nickname = NULL;
1509 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1514 if (cmd->argc < 4) {
1515 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1516 "Usage: /CUMODE <channel> +|-<modes> <nickname>[@<server>]");
1521 if (cmd->argv[1][0] == '*') {
1522 if (!conn->current_channel) {
1523 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1524 "You are not on any channel");
1529 channel = conn->current_channel;
1531 name = cmd->argv[1];
1533 channel = silc_client_get_channel(cmd->client, conn, name);
1535 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1536 "You are on that channel");
1542 /* Parse the typed nickname. */
1543 if (client->internal->params->nickname_parse)
1544 client->internal->params->nickname_parse(cmd->argv[3], &nickname);
1546 nickname = strdup(cmd->argv[3]);
1548 /* Find client entry */
1549 client_entry = silc_idlist_get_client(cmd->client, conn, nickname,
1550 cmd->argv[3], TRUE);
1551 if (!client_entry) {
1557 /* Client entry not found, it was requested thus mark this to be
1559 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY,
1561 silc_client_command_cumode,
1562 silc_client_command_dup(cmd));
1567 /* Get the current mode */
1568 chu = silc_client_on_channel(channel, client_entry);
1572 /* Are we adding or removing mode */
1573 if (cmd->argv[2][0] == '-')
1579 cp = cmd->argv[2] + 1;
1581 for (i = 0; i < len; i++) {
1585 mode |= SILC_CHANNEL_UMODE_CHANFO;
1586 mode |= SILC_CHANNEL_UMODE_CHANOP;
1587 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
1589 mode = SILC_CHANNEL_UMODE_NONE;
1594 if (cmd->argc == 5) {
1595 if (!strcasecmp(cmd->argv[4], "-pubkey")) {
1596 auth = silc_auth_public_key_auth_generate(cmd->client->public_key,
1597 cmd->client->private_key,
1603 auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
1604 cmd->argv[4], cmd->argv_lens[4]);
1607 mode |= SILC_CHANNEL_UMODE_CHANFO;
1609 mode &= ~SILC_CHANNEL_UMODE_CHANFO;
1614 mode |= SILC_CHANNEL_UMODE_CHANOP;
1616 mode &= ~SILC_CHANNEL_UMODE_CHANOP;
1620 mode |= SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
1622 mode &= ~SILC_CHANNEL_UMODE_BLOCK_MESSAGES;
1631 chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1632 SILC_PUT32_MSB(mode, modebuf);
1633 clidp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
1635 /* Send the command packet. We support sending only one mode at once
1636 that requires an argument. */
1637 buffer = silc_command_payload_encode_va(SILC_COMMAND_CUMODE, 0,
1639 1, chidp->data, chidp->len,
1641 3, clidp->data, clidp->len,
1642 4, auth ? auth->data : NULL,
1643 auth ? auth->len : 0);
1645 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1646 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1647 silc_buffer_free(buffer);
1648 silc_buffer_free(chidp);
1649 silc_buffer_free(clidp);
1651 silc_buffer_free(auth);
1653 /* Notify application */
1657 silc_free(nickname);
1658 silc_client_command_free(cmd);
1661 /* KICK command. Kicks a client out of channel. */
1663 SILC_CLIENT_CMD_FUNC(kick)
1665 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1666 SilcClient client = cmd->client;
1667 SilcClientConnection conn = cmd->conn;
1668 SilcIDCacheEntry id_cache = NULL;
1669 SilcChannelEntry channel;
1670 SilcBuffer buffer, idp, idp2;
1671 SilcClientEntry target;
1673 char *nickname = NULL;
1676 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1681 if (cmd->argc < 3) {
1682 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1683 "Usage: /KICK <channel> <nickname> [<comment>]");
1688 if (cmd->argv[1][0] == '*') {
1689 if (!conn->current_channel) {
1690 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1691 "You are not on any channel");
1695 name = conn->current_channel->channel_name;
1697 name = cmd->argv[1];
1700 if (!conn->current_channel) {
1701 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1702 "You are not on that channel");
1707 /* Get the Channel ID of the channel */
1708 if (!silc_idcache_find_by_name_one(conn->channel_cache, name, &id_cache)) {
1709 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1710 "You are not on that channel");
1715 channel = (SilcChannelEntry)id_cache->context;
1717 /* Parse the typed nickname. */
1718 if (client->internal->params->nickname_parse)
1719 client->internal->params->nickname_parse(cmd->argv[2], &nickname);
1721 nickname = strdup(cmd->argv[2]);
1723 /* Get the target client */
1724 target = silc_idlist_get_client(cmd->client, conn, nickname,
1725 cmd->argv[2], FALSE);
1727 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1728 "No such client: %s", cmd->argv[2]);
1733 /* Send KICK command to the server */
1734 idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1735 idp2 = silc_id_payload_encode(target->id, SILC_ID_CLIENT);
1737 buffer = silc_command_payload_encode_va(SILC_COMMAND_KICK, 0, 2,
1738 1, idp->data, idp->len,
1739 2, idp2->data, idp2->len);
1741 buffer = silc_command_payload_encode_va(SILC_COMMAND_KICK, 0, 3,
1742 1, idp->data, idp->len,
1743 2, idp2->data, idp2->len,
1745 strlen(cmd->argv[3]));
1746 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1747 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1748 silc_buffer_free(buffer);
1749 silc_buffer_free(idp);
1750 silc_buffer_free(idp2);
1752 /* Notify application */
1756 silc_free(nickname);
1757 silc_client_command_free(cmd);
1760 static void silc_client_command_oper_send(unsigned char *data,
1761 SilcUInt32 data_len, void *context)
1763 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1764 SilcClientConnection conn = cmd->conn;
1765 SilcBuffer buffer, auth;
1767 if (cmd->argc >= 3) {
1768 /* Encode the public key authentication payload */
1769 auth = silc_auth_public_key_auth_generate(cmd->client->public_key,
1770 cmd->client->private_key,
1771 cmd->client->rng, conn->hash,
1775 /* Encode the password authentication payload */
1776 auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
1780 buffer = silc_command_payload_encode_va(SILC_COMMAND_OPER, 0, 2,
1782 strlen(cmd->argv[1]),
1783 2, auth->data, auth->len);
1784 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1785 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1787 silc_buffer_free(buffer);
1788 silc_buffer_free(auth);
1790 /* Notify application */
1794 /* OPER command. Used to obtain server operator privileges. */
1796 SILC_CLIENT_CMD_FUNC(oper)
1798 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1799 SilcClientConnection conn = cmd->conn;
1802 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1807 if (cmd->argc < 2) {
1808 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1809 "Usage: /OPER <username> [-pubkey]");
1814 if (cmd->argc < 3) {
1815 /* Get passphrase */
1816 cmd->client->internal->ops->ask_passphrase(cmd->client, conn,
1817 silc_client_command_oper_send,
1822 silc_client_command_oper_send(NULL, 0, context);
1825 silc_client_command_free(cmd);
1828 static void silc_client_command_silcoper_send(unsigned char *data,
1829 SilcUInt32 data_len,
1832 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1833 SilcClientConnection conn = cmd->conn;
1834 SilcBuffer buffer, auth;
1836 if (cmd->argc >= 3) {
1837 /* Encode the public key authentication payload */
1838 auth = silc_auth_public_key_auth_generate(cmd->client->public_key,
1839 cmd->client->private_key,
1840 cmd->client->rng, conn->hash,
1844 /* Encode the password authentication payload */
1845 auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
1849 buffer = silc_command_payload_encode_va(SILC_COMMAND_SILCOPER, 0, 2,
1851 strlen(cmd->argv[1]),
1852 2, auth->data, auth->len);
1853 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1854 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1856 silc_buffer_free(buffer);
1857 silc_buffer_free(auth);
1859 /* Notify application */
1863 /* SILCOPER command. Used to obtain router operator privileges. */
1865 SILC_CLIENT_CMD_FUNC(silcoper)
1867 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1868 SilcClientConnection conn = cmd->conn;
1871 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1876 if (cmd->argc < 2) {
1877 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1878 "Usage: /SILCOPER <username> [-pubkey]");
1883 if (cmd->argc < 3) {
1884 /* Get passphrase */
1885 cmd->client->internal->ops->ask_passphrase(cmd->client, conn,
1886 silc_client_command_silcoper_send,
1891 silc_client_command_silcoper_send(NULL, 0, context);
1894 silc_client_command_free(cmd);
1897 /* Command BAN. This is used to manage the ban list of the channel. */
1899 SILC_CLIENT_CMD_FUNC(ban)
1901 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1902 SilcClientConnection conn = cmd->conn;
1903 SilcChannelEntry channel;
1904 SilcBuffer buffer, chidp;
1906 char *name, *ban = NULL;
1909 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1914 if (cmd->argc < 2) {
1915 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1916 "Usage: /BAN <channel> "
1917 "[+|-[<nickname>[@<server>[!<username>[@hostname>]]]]]");
1922 if (cmd->argv[1][0] == '*') {
1923 if (!conn->current_channel) {
1924 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1925 "You are not on any channel");
1930 channel = conn->current_channel;
1932 name = cmd->argv[1];
1934 channel = silc_client_get_channel(cmd->client, conn, name);
1936 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1937 "You are on that channel");
1943 if (cmd->argc == 3) {
1944 if (cmd->argv[2][0] == '+')
1953 chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1955 /* Send the command */
1956 buffer = silc_command_payload_encode_va(SILC_COMMAND_BAN,
1957 ++conn->cmd_ident, 2,
1958 1, chidp->data, chidp->len,
1959 type, ban, ban ? strlen(ban) : 0);
1960 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1961 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1962 silc_buffer_free(buffer);
1963 silc_buffer_free(chidp);
1965 /* Notify application */
1969 silc_client_command_free(cmd);
1972 /* Command DETACH. This is used to detach from the server */
1974 SILC_CLIENT_CMD_FUNC(detach)
1976 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1977 SilcClientConnection conn = cmd->conn;
1981 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1986 buffer = silc_command_payload_encode_va(SILC_COMMAND_DETACH,
1987 ++conn->cmd_ident, 0);
1988 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1989 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1990 silc_buffer_free(buffer);
1992 /* Notify application */
1996 silc_client_command_free(cmd);
1999 /* LEAVE command. Leaves a channel. Client removes itself from a channel. */
2001 SILC_CLIENT_CMD_FUNC(leave)
2003 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
2004 SilcClientConnection conn = cmd->conn;
2005 SilcChannelEntry channel;
2006 SilcChannelUser chu;
2007 SilcBuffer buffer, idp;
2011 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
2016 if (cmd->argc != 2) {
2017 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
2018 "Usage: /LEAVE <channel>");
2023 if (cmd->argv[1][0] == '*') {
2024 if (!conn->current_channel) {
2025 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
2026 "You are not on any channel");
2030 name = conn->current_channel->channel_name;
2032 name = cmd->argv[1];
2035 /* Get the channel entry */
2036 channel = silc_client_get_channel(cmd->client, conn, name);
2038 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
2039 "You are not on that channel");
2044 /* Remove us from channel */
2045 chu = silc_client_on_channel(channel, conn->local_entry);
2047 silc_hash_table_del(chu->client->channels, chu->channel);
2048 silc_hash_table_del(chu->channel->user_list, chu->client);
2052 /* Send LEAVE command to the server */
2053 idp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
2054 buffer = silc_command_payload_encode_va(SILC_COMMAND_LEAVE, 0, 1,
2055 1, idp->data, idp->len);
2056 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
2057 0, NULL, NULL, buffer->data, buffer->len, TRUE);
2058 silc_buffer_free(buffer);
2059 silc_buffer_free(idp);
2061 /* Notify application */
2064 if (conn->current_channel == channel)
2065 conn->current_channel = NULL;
2067 silc_client_del_channel(cmd->client, cmd->conn, channel);
2070 silc_client_command_free(cmd);
2073 /* Command USERS. Requests the USERS of the clients joined on requested
2076 SILC_CLIENT_CMD_FUNC(users)
2078 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
2079 SilcClientConnection conn = cmd->conn;
2084 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
2089 if (cmd->argc != 2) {
2090 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
2091 "Usage: /USERS <channel>");
2096 if (cmd->argv[1][0] == '*') {
2097 if (!conn->current_channel) {
2098 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
2099 "You are not on any channel");
2103 name = conn->current_channel->channel_name;
2105 name = cmd->argv[1];
2108 /* Send USERS command to the server */
2109 buffer = silc_command_payload_encode_va(SILC_COMMAND_USERS,
2110 ++conn->cmd_ident, 1,
2111 2, name, strlen(name));
2112 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND,
2113 NULL, 0, NULL, NULL, buffer->data,
2115 silc_buffer_free(buffer);
2117 /* Notify application */
2121 silc_client_command_free(cmd);
2124 /* Command GETKEY. Used to fetch remote client's public key. */
2126 SILC_CLIENT_CMD_FUNC(getkey)
2128 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
2129 SilcClientConnection conn = cmd->conn;
2130 SilcClient client = cmd->client;
2131 SilcClientEntry client_entry = NULL;
2132 SilcServerEntry server_entry = NULL;
2133 char *nickname = NULL;
2134 SilcBuffer idp, buffer;
2136 SILC_LOG_DEBUG(("Start"));
2139 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
2144 if (cmd->argc < 2) {
2145 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_INFO,
2146 "Usage: /GETKEY <nickname or server name>");
2151 /* Parse the typed nickname. */
2152 if (client->internal->params->nickname_parse)
2153 client->internal->params->nickname_parse(cmd->argv[1], &nickname);
2155 nickname = strdup(cmd->argv[1]);
2157 /* Find client entry */
2158 client_entry = silc_idlist_get_client(client, conn, nickname, cmd->argv[1],
2160 if (!client_entry) {
2161 /* Check whether user requested server actually */
2162 server_entry = silc_client_get_server(client, conn, cmd->argv[1]);
2164 if (!server_entry) {
2165 /* No. what ever user wants we don't have it, so resolve it. We
2166 will first try to resolve the client, and if that fails then
2167 we'll try to resolve the server. */
2169 if (!cmd->pending) {
2170 /* This will send the IDENTIFY command for nickname */
2171 silc_idlist_get_client(client, conn, nickname, cmd->argv[1], TRUE);
2172 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY,
2174 silc_client_command_getkey,
2175 silc_client_command_dup(cmd));
2179 SilcClientCommandReplyContext reply =
2180 (SilcClientCommandReplyContext)context2;
2181 SilcCommandStatus error;
2183 /* If nickname was not found, then resolve the server. */
2184 silc_command_get_status(reply->payload, NULL, &error);
2185 if (error == SILC_STATUS_ERR_NO_SUCH_NICK) {
2186 /* This sends the IDENTIFY command to resolve the server. */
2187 silc_client_command_register(client, SILC_COMMAND_IDENTIFY,
2189 silc_client_command_reply_identify_i, 0,
2191 silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
2193 2, cmd->argv[1], cmd->argv_lens[1]);
2194 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY,
2196 silc_client_command_getkey,
2197 silc_client_command_dup(cmd));
2201 /* If server was not found, then we've resolved both nickname and
2202 server and did not find anybody. */
2203 if (error == SILC_STATUS_ERR_NO_SUCH_SERVER) {
2204 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, "%s",
2205 silc_client_command_status_message(SILC_STATUS_ERR_NO_SUCH_NICK));
2206 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, "%s",
2207 silc_client_command_status_message(error));
2217 idp = silc_id_payload_encode(server_entry->server_id, SILC_ID_SERVER);
2219 idp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
2222 buffer = silc_command_payload_encode_va(SILC_COMMAND_GETKEY, 0, 1,
2223 1, idp->data, idp->len);
2224 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
2225 0, NULL, NULL, buffer->data, buffer->len, TRUE);
2226 silc_buffer_free(buffer);
2227 silc_buffer_free(idp);
2229 /* Notify application */
2233 silc_free(nickname);
2234 silc_client_command_free(cmd);
2237 /* Register a new command indicated by the `command' to the SILC client.
2238 The `name' is optional command name. If provided the command may be
2239 searched using the silc_client_command_find by that name. The
2240 `command_function' is the function to be called when the command is
2241 executed, and the `command_reply_function' is the function to be
2242 called after the server has sent reply back to the command.
2244 The `ident' is optional identifier for the command. If non-zero
2245 the `command_reply_function' for the command type `command' will be
2246 called only if the command reply sent by server includes the
2247 command identifier `ident'. Application usually does not need it
2248 and set it to zero value. */
2250 bool silc_client_command_register(SilcClient client,
2251 SilcCommand command,
2253 SilcCommandCb command_function,
2254 SilcCommandCb command_reply_function,
2258 SilcClientCommand cmd;
2260 cmd = silc_calloc(1, sizeof(*cmd));
2262 cmd->command = command_function;
2263 cmd->reply = command_reply_function;
2264 cmd->name = name ? strdup(name) : NULL;
2265 cmd->max_args = max_args;
2268 silc_list_add(client->internal->commands, cmd);
2273 /* Unregister a command indicated by the `command' with command function
2274 `command_function' and command reply function `command_reply_function'.
2275 Returns TRUE if the command was found and unregistered. */
2277 bool silc_client_command_unregister(SilcClient client,
2278 SilcCommand command,
2279 SilcCommandCb command_function,
2280 SilcCommandCb command_reply_function,
2283 SilcClientCommand cmd;
2285 silc_list_start(client->internal->commands);
2286 while ((cmd = silc_list_get(client->internal->commands)) != SILC_LIST_END) {
2287 if (cmd->cmd == command && cmd->command == command_function &&
2288 cmd->reply == command_reply_function && cmd->ident == ident) {
2289 silc_list_del(client->internal->commands, cmd);
2290 silc_free(cmd->name);
2299 /* Private range commands, specific to this implementation (and compatible
2300 with SILC Server). */
2302 /* CONNECT command. Connects the server to another server. */
2304 SILC_CLIENT_CMD_FUNC(connect)
2306 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
2307 SilcClientConnection conn = cmd->conn;
2309 unsigned char port[4];
2313 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
2318 if (cmd->argc < 2) {
2319 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
2320 "Usage: /CONNECT <server> [<port>]");
2325 if (cmd->argc == 3) {
2326 tmp = atoi(cmd->argv[2]);
2327 SILC_PUT32_MSB(tmp, port);
2331 buffer = silc_command_payload_encode_va(SILC_COMMAND_PRIV_CONNECT, 0, 2,
2333 strlen(cmd->argv[1]),
2336 buffer = silc_command_payload_encode_va(SILC_COMMAND_PRIV_CONNECT, 0, 1,
2338 strlen(cmd->argv[1]));
2339 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
2340 0, NULL, NULL, buffer->data, buffer->len, TRUE);
2341 silc_buffer_free(buffer);
2343 /* Notify application */
2347 silc_client_command_free(cmd);
2351 /* CLOSE command. Close server connection to the remote server */
2353 SILC_CLIENT_CMD_FUNC(close)
2355 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
2356 SilcClientConnection conn = cmd->conn;
2358 unsigned char port[4];
2362 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
2367 if (cmd->argc < 2) {
2368 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
2369 "Usage: /CLOSE <server> [<port>]");
2374 if (cmd->argc == 3) {
2375 tmp = atoi(cmd->argv[2]);
2376 SILC_PUT32_MSB(tmp, port);
2380 buffer = silc_command_payload_encode_va(SILC_COMMAND_PRIV_CLOSE, 0, 2,
2382 strlen(cmd->argv[1]),
2385 buffer = silc_command_payload_encode_va(SILC_COMMAND_PRIV_CLOSE, 0, 1,
2387 strlen(cmd->argv[1]));
2388 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
2389 0, NULL, NULL, buffer->data, buffer->len, TRUE);
2390 silc_buffer_free(buffer);
2392 /* Notify application */
2396 silc_client_command_free(cmd);
2399 /* SHUTDOWN command. Shutdowns the server. */
2401 SILC_CLIENT_CMD_FUNC(shutdown)
2403 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
2406 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
2411 /* Send the command */
2412 silc_client_command_send(cmd->client, cmd->conn,
2413 SILC_COMMAND_PRIV_SHUTDOWN, 0, 0);
2415 /* Notify application */
2419 silc_client_command_free(cmd);
2422 /* Register all default commands provided by the client library for the
2425 void silc_client_commands_register(SilcClient client)
2427 silc_list_init(client->internal->commands, struct SilcClientCommandStruct,
2430 SILC_CLIENT_CMD(whois, WHOIS, "WHOIS", 3);
2431 SILC_CLIENT_CMD(whowas, WHOWAS, "WHOWAS", 3);
2432 SILC_CLIENT_CMD(identify, IDENTIFY, "IDENTIFY", 3);
2433 SILC_CLIENT_CMD(nick, NICK, "NICK", 2);
2434 SILC_CLIENT_CMD(list, LIST, "LIST", 2);
2435 SILC_CLIENT_CMD(topic, TOPIC, "TOPIC", 3);
2436 SILC_CLIENT_CMD(invite, INVITE, "INVITE", 3);
2437 SILC_CLIENT_CMD(quit, QUIT, "QUIT", 2);
2438 SILC_CLIENT_CMD(kill, KILL, "KILL", 3);
2439 SILC_CLIENT_CMD(info, INFO, "INFO", 2);
2440 SILC_CLIENT_CMD(ping, PING, "PING", 2);
2441 SILC_CLIENT_CMD(oper, OPER, "OPER", 3);
2442 SILC_CLIENT_CMD(join, JOIN, "JOIN", 9);
2443 SILC_CLIENT_CMD(motd, MOTD, "MOTD", 2);
2444 SILC_CLIENT_CMD(umode, UMODE, "UMODE", 2);
2445 SILC_CLIENT_CMD(cmode, CMODE, "CMODE", 4);
2446 SILC_CLIENT_CMD(cumode, CUMODE, "CUMODE", 5);
2447 SILC_CLIENT_CMD(kick, KICK, "KICK", 4);
2448 SILC_CLIENT_CMD(ban, BAN, "BAN", 3);
2449 SILC_CLIENT_CMD(detach, DETACH, "DETACH", 0);
2450 SILC_CLIENT_CMD(silcoper, SILCOPER, "SILCOPER", 3);
2451 SILC_CLIENT_CMD(leave, LEAVE, "LEAVE", 2);
2452 SILC_CLIENT_CMD(users, USERS, "USERS", 2);
2453 SILC_CLIENT_CMD(getkey, GETKEY, "GETKEY", 2);
2455 SILC_CLIENT_CMD(connect, PRIV_CONNECT, "CONNECT", 3);
2456 SILC_CLIENT_CMD(close, PRIV_CLOSE, "CLOSE", 3);
2457 SILC_CLIENT_CMD(shutdown, PRIV_SHUTDOWN, "SHUTDOWN", 1);
2460 /* Unregister all commands. */
2462 void silc_client_commands_unregister(SilcClient client)
2464 SILC_CLIENT_CMDU(whois, WHOIS, "WHOIS");
2465 SILC_CLIENT_CMDU(whowas, WHOWAS, "WHOWAS");
2466 SILC_CLIENT_CMDU(identify, IDENTIFY, "IDENTIFY");
2467 SILC_CLIENT_CMDU(nick, NICK, "NICK");
2468 SILC_CLIENT_CMDU(list, LIST, "LIST");
2469 SILC_CLIENT_CMDU(topic, TOPIC, "TOPIC");
2470 SILC_CLIENT_CMDU(invite, INVITE, "INVITE");
2471 SILC_CLIENT_CMDU(quit, QUIT, "QUIT");
2472 SILC_CLIENT_CMDU(kill, KILL, "KILL");
2473 SILC_CLIENT_CMDU(info, INFO, "INFO");
2474 SILC_CLIENT_CMDU(ping, PING, "PING");
2475 SILC_CLIENT_CMDU(oper, OPER, "OPER");
2476 SILC_CLIENT_CMDU(join, JOIN, "JOIN");
2477 SILC_CLIENT_CMDU(motd, MOTD, "MOTD");
2478 SILC_CLIENT_CMDU(umode, UMODE, "UMODE");
2479 SILC_CLIENT_CMDU(cmode, CMODE, "CMODE");
2480 SILC_CLIENT_CMDU(cumode, CUMODE, "CUMODE");
2481 SILC_CLIENT_CMDU(kick, KICK, "KICK");
2482 SILC_CLIENT_CMDU(ban, BAN, "BAN");
2483 SILC_CLIENT_CMDU(detach, DETACH, "DETACH");
2484 SILC_CLIENT_CMDU(silcoper, SILCOPER, "SILCOPER");
2485 SILC_CLIENT_CMDU(leave, LEAVE, "LEAVE");
2486 SILC_CLIENT_CMDU(users, USERS, "USERS");
2487 SILC_CLIENT_CMDU(getkey, GETKEY, "GETKEY");
2489 SILC_CLIENT_CMDU(connect, PRIV_CONNECT, "CONNECT");
2490 SILC_CLIENT_CMDU(close, PRIV_CLOSE, "CLOSE");
2491 SILC_CLIENT_CMDU(shutdown, PRIV_SHUTDOWN, "SHUTDOWN");