5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 1997 - 2002 Pekka Riikonen
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; version 2 of the License.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
21 #include "silcincludes.h"
22 #include "silcclient.h"
23 #include "client_internal.h"
25 #define SILC_NOT_CONNECTED(x, c) \
26 x->internal->ops->say((x), (c), SILC_CLIENT_MESSAGE_ERROR, \
27 "You are not connected to a server, use /SERVER to connect");
29 /* Command operation that is called at the end of all commands.
31 #define COMMAND cmd->client->internal->ops->command(cmd->client, cmd->conn, \
32 cmd, TRUE, cmd->command->cmd)
34 /* Error to application. Usage: COMMAND_ERROR; */
35 #define COMMAND_ERROR cmd->client->internal->ops->command(cmd->client, \
36 cmd->conn, cmd, FALSE, cmd->command->cmd)
38 #define SAY cmd->client->internal->ops->say
40 /* Generic function to send any command. The arguments must be sent already
41 encoded into correct form and in correct order. */
43 void silc_client_command_send(SilcClient client, SilcClientConnection conn,
44 SilcCommand command, SilcUInt16 ident,
52 packet = silc_command_payload_encode_vap(command, ident, argc, ap);
53 silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND,
54 NULL, 0, NULL, NULL, packet->data,
56 silc_buffer_free(packet);
59 /* Finds and returns a pointer to the command list. Return NULL if the
60 command is not found. */
62 SilcClientCommand silc_client_command_find(SilcClient client,
65 SilcClientCommand cmd;
67 silc_list_start(client->internal->commands);
68 while ((cmd = silc_list_get(client->internal->commands)) != SILC_LIST_END) {
69 if (cmd->name && !strcmp(cmd->name, name))
76 /* Calls the command (executes it). Application can call this after
77 it has allocated the SilcClientCommandContext with the function
78 silc_client_command_alloc and found the command from the client
79 library by calling silc_client_command_find. This will execute
82 void silc_client_command_call(SilcClientCommand command,
83 SilcClientCommandContext cmd)
85 (*command->command)((void *)cmd, NULL);
88 /* Add new pending command to be executed when reply to a command has been
89 received. The `reply_cmd' is the command that will call the `callback'
90 with `context' when reply has been received. If `ident is non-zero
91 the `callback' will be executed when received reply with command
92 identifier `ident'. */
94 void silc_client_command_pending(SilcClientConnection conn,
95 SilcCommand reply_cmd,
97 SilcCommandCb callback,
100 SilcClientCommandPending *reply;
102 reply = silc_calloc(1, sizeof(*reply));
103 reply->reply_cmd = reply_cmd;
104 reply->ident = ident;
105 reply->context = context;
106 reply->callback = callback;
107 silc_dlist_add(conn->pending_commands, reply);
110 /* Deletes pending command by reply command type. */
112 void silc_client_command_pending_del(SilcClientConnection conn,
113 SilcCommand reply_cmd,
116 SilcClientCommandPending *r;
118 silc_dlist_start(conn->pending_commands);
119 while ((r = silc_dlist_get(conn->pending_commands)) != SILC_LIST_END) {
120 if (r->reply_cmd == reply_cmd && r->ident == ident) {
121 silc_dlist_del(conn->pending_commands, r);
127 /* Checks for pending commands and marks callbacks to be called from
128 the command reply function. Returns TRUE if there were pending command. */
130 int silc_client_command_pending_check(SilcClientConnection conn,
131 SilcClientCommandReplyContext ctx,
135 SilcClientCommandPending *r;
137 silc_dlist_start(conn->pending_commands);
138 while ((r = silc_dlist_get(conn->pending_commands)) != SILC_LIST_END) {
139 if (r->reply_cmd == command && r->ident == ident) {
140 ctx->context = r->context;
141 ctx->callback = r->callback;
150 /* Allocate Command Context */
152 SilcClientCommandContext silc_client_command_alloc(void)
154 SilcClientCommandContext ctx = silc_calloc(1, sizeof(*ctx));
159 /* Free command context and its internals */
161 void silc_client_command_free(SilcClientCommandContext ctx)
164 SILC_LOG_DEBUG(("Command context %p refcnt %d->%d", ctx, ctx->users + 1,
166 if (ctx->users < 1) {
169 for (i = 0; i < ctx->argc; i++)
170 silc_free(ctx->argv[i]);
171 silc_free(ctx->argv_lens);
172 silc_free(ctx->argv_types);
177 /* Duplicate Command Context by adding reference counter. The context won't
178 be free'd untill it hits zero. */
180 SilcClientCommandContext silc_client_command_dup(SilcClientCommandContext ctx)
183 SILC_LOG_DEBUG(("Command context %p refcnt %d->%d", ctx, ctx->users - 1,
188 /* Command WHOIS. This command is used to query information about
191 SILC_CLIENT_CMD_FUNC(whois)
193 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
194 SilcClientConnection conn = cmd->conn;
198 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
203 /* Given without arguments fetches client's own information */
205 buffer = silc_id_payload_encode(cmd->conn->local_id, SILC_ID_CLIENT);
206 silc_client_command_send(cmd->client, cmd->conn, SILC_COMMAND_WHOIS,
208 1, 3, buffer->data, buffer->len);
209 silc_buffer_free(buffer);
213 buffer = silc_command_payload_encode(SILC_COMMAND_WHOIS,
214 cmd->argc - 1, ++cmd->argv,
215 ++cmd->argv_lens, ++cmd->argv_types,
217 silc_client_packet_send(cmd->client, cmd->conn->sock,
218 SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
219 buffer->data, buffer->len, TRUE);
220 silc_buffer_free(buffer);
225 /* Notify application */
229 silc_client_command_free(cmd);
232 /* Command WHOWAS. This command is used to query history information about
233 specific user that used to exist in the network. */
235 SILC_CLIENT_CMD_FUNC(whowas)
237 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
238 SilcClientConnection conn = cmd->conn;
242 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
247 if (cmd->argc < 2 || cmd->argc > 3) {
248 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
249 "Usage: /WHOWAS <nickname>[@<server>] [<count>]");
254 buffer = silc_command_payload_encode(SILC_COMMAND_WHOWAS,
255 cmd->argc - 1, ++cmd->argv,
256 ++cmd->argv_lens, ++cmd->argv_types,
258 silc_client_packet_send(cmd->client, cmd->conn->sock,
259 SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
260 buffer->data, buffer->len, TRUE);
261 silc_buffer_free(buffer);
266 /* Notify application */
270 silc_client_command_free(cmd);
273 /* Command IDENTIFY. This command is used to query information about
274 specific user, especially ID's.
276 NOTE: This command is used only internally by the client library
277 and application MUST NOT call this command directly. */
279 SILC_CLIENT_CMD_FUNC(identify)
281 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
282 SilcClientConnection conn = cmd->conn;
286 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
290 if (cmd->argc < 2 || cmd->argc > 3)
294 buffer = silc_command_payload_encode_va(SILC_COMMAND_IDENTIFY,
295 ++conn->cmd_ident, 1,
299 buffer = silc_command_payload_encode_va(SILC_COMMAND_IDENTIFY,
300 ++conn->cmd_ident, 2,
306 silc_client_packet_send(cmd->client, cmd->conn->sock,
307 SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
308 buffer->data, buffer->len, TRUE);
309 silc_buffer_free(buffer);
312 silc_client_command_free(cmd);
315 /* Pending callbcak that will be called after the NICK command was
316 replied by the server. This sets the nickname if there were no
319 SILC_CLIENT_CMD_FUNC(nick_change)
321 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
322 SilcClientConnection conn = cmd->conn;
323 SilcClientCommandReplyContext reply =
324 (SilcClientCommandReplyContext)context2;
325 SilcCommandStatus status = silc_command_get_status(reply->payload);
327 if (status == SILC_STATUS_OK) {
328 /* Set the nickname */
329 silc_idcache_del_by_context(conn->client_cache, conn->local_entry);
331 silc_free(conn->nickname);
332 conn->nickname = strdup(cmd->argv[1]);
333 conn->local_entry->nickname = conn->nickname;
334 silc_client_nickname_format(cmd->client, conn, conn->local_entry);
335 silc_idcache_add(conn->client_cache, strdup(cmd->argv[1]),
336 conn->local_entry->id, conn->local_entry, 0, NULL);
342 silc_client_command_free(cmd);
345 /* Command NICK. Shows current nickname/sets new nickname on current
348 SILC_CLIENT_CMD_FUNC(nick)
350 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
351 SilcClientConnection conn = cmd->conn;
355 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
361 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
362 "Usage: /NICK <nickname>");
367 if (!strcmp(conn->nickname, cmd->argv[1]))
370 /* Show current nickname */
373 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
374 "Your nickname is %s on server %s",
375 conn->nickname, conn->remote_host);
377 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
378 "Your nickname is %s", conn->nickname);
385 if (cmd->argv_lens[1] > 128)
386 cmd->argv_lens[1] = 128;
388 /* Send the NICK command */
389 buffer = silc_command_payload_encode(SILC_COMMAND_NICK, 1,
393 ++cmd->conn->cmd_ident);
394 silc_client_packet_send(cmd->client, cmd->conn->sock,
395 SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
396 buffer->data, buffer->len, TRUE);
397 silc_buffer_free(buffer);
399 /* Register pending callback that will actually set the new nickname
400 if there were no errors returned by the server. */
401 silc_client_command_pending(conn, SILC_COMMAND_NICK,
402 cmd->conn->cmd_ident,
403 silc_client_command_nick_change,
404 silc_client_command_dup(cmd));
408 silc_client_command_free(cmd);
411 /* Command LIST. Lists channels on the current server. */
413 SILC_CLIENT_CMD_FUNC(list)
415 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
416 SilcClientConnection conn = cmd->conn;
417 SilcIDCacheEntry id_cache = NULL;
418 SilcChannelEntry channel;
419 SilcBuffer buffer, idp = NULL;
423 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
428 if (cmd->argc == 2) {
431 /* Get the Channel ID of the channel */
432 if (silc_idcache_find_by_name_one(conn->channel_cache, name, &id_cache)) {
433 channel = (SilcChannelEntry)id_cache->context;
434 idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
439 buffer = silc_command_payload_encode_va(SILC_COMMAND_LIST,
440 ++conn->cmd_ident, 0);
442 buffer = silc_command_payload_encode_va(SILC_COMMAND_LIST,
443 ++conn->cmd_ident, 1,
444 1, idp->data, idp->len);
446 silc_client_packet_send(cmd->client, cmd->conn->sock,
447 SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
448 buffer->data, buffer->len, TRUE);
449 silc_buffer_free(buffer);
451 silc_buffer_free(idp);
453 /* Notify application */
457 silc_client_command_free(cmd);
460 /* Command TOPIC. Sets/shows topic on a channel. */
462 SILC_CLIENT_CMD_FUNC(topic)
464 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
465 SilcClientConnection conn = cmd->conn;
466 SilcIDCacheEntry id_cache = NULL;
467 SilcChannelEntry channel;
468 SilcBuffer buffer, idp;
472 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
477 if (cmd->argc < 2 || cmd->argc > 3) {
478 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
479 "Usage: /TOPIC <channel> [<topic>]");
484 if (cmd->argv[1][0] == '*') {
485 if (!conn->current_channel) {
486 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
487 "You are not on any channel");
491 name = conn->current_channel->channel_name;
496 if (!conn->current_channel) {
497 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
498 "You are not on that channel");
503 /* Get the Channel ID of the channel */
504 if (!silc_idcache_find_by_name_one(conn->channel_cache, name, &id_cache)) {
505 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
506 "You are not on that channel");
511 channel = (SilcChannelEntry)id_cache->context;
513 /* Send TOPIC command to the server */
514 idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
516 buffer = silc_command_payload_encode_va(SILC_COMMAND_TOPIC,
517 ++conn->cmd_ident, 2,
518 1, idp->data, idp->len,
520 strlen(cmd->argv[2]));
522 buffer = silc_command_payload_encode_va(SILC_COMMAND_TOPIC,
523 ++conn->cmd_ident, 1,
524 1, idp->data, idp->len);
525 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
526 0, NULL, NULL, buffer->data, buffer->len, TRUE);
527 silc_buffer_free(buffer);
528 silc_buffer_free(idp);
530 /* Notify application */
534 silc_client_command_free(cmd);
537 /* Command INVITE. Invites specific client to join a channel. This is
538 also used to mange the invite list of the channel. */
540 SILC_CLIENT_CMD_FUNC(invite)
542 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
543 SilcClient client = cmd->client;
544 SilcClientConnection conn = cmd->conn;
545 SilcClientEntry client_entry = NULL;
546 SilcChannelEntry channel;
547 SilcBuffer buffer, clidp, chidp;
549 char *nickname = NULL, *name;
553 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
559 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
560 "Usage: /INVITE <channel> [<nickname>[@server>]"
561 "[+|-[<nickname>[@<server>[!<username>[@hostname>]]]]]");
566 if (cmd->argv[1][0] == '*') {
567 if (!conn->current_channel) {
568 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
569 "You are not on any channel");
574 channel = conn->current_channel;
578 channel = silc_client_get_channel(cmd->client, conn, name);
580 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
581 "You are on that channel");
587 /* Parse the typed nickname. */
588 if (cmd->argc == 3) {
589 if (cmd->argv[2][0] != '+' && cmd->argv[2][0] != '-') {
590 if (client->internal->params->nickname_parse)
591 client->internal->params->nickname_parse(cmd->argv[2], &nickname);
593 nickname = strdup(cmd->argv[2]);
595 /* Find client entry */
596 client_entry = silc_idlist_get_client(client, conn, nickname,
604 /* Client entry not found, it was requested thus mark this to be
606 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY,
608 silc_client_command_invite,
609 silc_client_command_dup(cmd));
614 invite = cmd->argv[2];
616 if (cmd->argv[2][0] == '+')
623 /* Send the command */
624 chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
626 clidp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
627 buffer = silc_command_payload_encode_va(SILC_COMMAND_INVITE,
628 ++conn->cmd_ident, 3,
629 1, chidp->data, chidp->len,
630 2, clidp->data, clidp->len,
631 type, invite, invite ?
633 silc_buffer_free(clidp);
635 buffer = silc_command_payload_encode_va(SILC_COMMAND_INVITE,
636 ++conn->cmd_ident, 2,
637 1, chidp->data, chidp->len,
638 type, invite, invite ?
642 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
643 0, NULL, NULL, buffer->data, buffer->len, TRUE);
644 silc_buffer_free(buffer);
645 silc_buffer_free(chidp);
647 /* Notify application */
652 silc_client_command_free(cmd);
657 SilcClientConnection conn;
660 SILC_TASK_CALLBACK(silc_client_command_quit_cb)
662 QuitInternal q = (QuitInternal)context;
664 /* Close connection */
665 q->client->internal->ops->disconnect(q->client, q->conn);
666 silc_client_close_connection(q->client, q->conn->sock->user_data);
671 /* Command QUIT. Closes connection with current server. */
673 SILC_CLIENT_CMD_FUNC(quit)
675 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
680 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
686 buffer = silc_command_payload_encode(SILC_COMMAND_QUIT, cmd->argc - 1,
687 &cmd->argv[1], &cmd->argv_lens[1],
688 &cmd->argv_types[1], 0);
690 buffer = silc_command_payload_encode(SILC_COMMAND_QUIT, 0,
691 NULL, NULL, NULL, 0);
692 silc_client_packet_send(cmd->client, cmd->conn->sock, SILC_PACKET_COMMAND,
694 buffer->data, buffer->len, TRUE);
695 silc_buffer_free(buffer);
697 q = silc_calloc(1, sizeof(*q));
698 q->client = cmd->client;
701 /* Sleep for a while */
704 /* We quit the connection with little timeout */
705 silc_schedule_task_add(cmd->client->schedule, cmd->conn->sock->sock,
706 silc_client_command_quit_cb, (void *)q,
707 1, 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
709 /* Notify application */
713 silc_client_command_free(cmd);
716 /* Timeout callback to remove the killed client from cache */
718 SILC_TASK_CALLBACK(silc_client_command_kill_remove_later)
720 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
721 SilcClient client = cmd->client;
722 SilcClientConnection conn = cmd->conn;
723 SilcClientEntry target;
724 char *nickname = NULL;
726 /* Parse the typed nickname. */
727 if (client->internal->params->nickname_parse)
728 client->internal->params->nickname_parse(cmd->argv[1], &nickname);
730 nickname = strdup(cmd->argv[1]);
732 /* Get the target client */
733 target = silc_idlist_get_client(cmd->client, conn, nickname,
734 cmd->argv[1], FALSE);
736 /* Remove the client from all channels and free it */
737 silc_client_del_client(client, conn, target);
740 silc_client_command_free(cmd);
743 /* Kill command's pending command callback to actually remove the killed
744 client from our local cache. */
746 SILC_CLIENT_CMD_FUNC(kill_remove)
748 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
749 SilcClientCommandReplyContext reply =
750 (SilcClientCommandReplyContext)context2;
751 SilcCommandStatus status = silc_command_get_status(reply->payload);
753 if (status == SILC_STATUS_OK) {
754 /* Remove with timeout */
755 silc_schedule_task_add(cmd->client->schedule, cmd->conn->sock->sock,
756 silc_client_command_kill_remove_later, context,
757 1, 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
761 silc_client_command_free(cmd);
764 /* Command KILL. Router operator can use this command to remove an client
765 fromthe SILC Network. */
767 SILC_CLIENT_CMD_FUNC(kill)
769 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
770 SilcClient client = cmd->client;
771 SilcClientConnection conn = cmd->conn;
772 SilcBuffer buffer, idp;
773 SilcClientEntry target;
774 char *nickname = NULL;
777 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
783 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
784 "Usage: /KILL <nickname> [<comment>]");
789 /* Parse the typed nickname. */
790 if (client->internal->params->nickname_parse)
791 client->internal->params->nickname_parse(cmd->argv[1], &nickname);
793 nickname = strdup(cmd->argv[1]);
795 /* Get the target client */
796 target = silc_idlist_get_client(cmd->client, conn, nickname,
804 /* Client entry not found, it was requested thus mark this to be
806 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY,
808 silc_client_command_kill,
809 silc_client_command_dup(cmd));
814 /* Send the KILL command to the server */
815 idp = silc_id_payload_encode(target->id, SILC_ID_CLIENT);
817 buffer = silc_command_payload_encode_va(SILC_COMMAND_KILL,
818 ++conn->cmd_ident, 1,
819 1, idp->data, idp->len);
821 buffer = silc_command_payload_encode_va(SILC_COMMAND_KILL,
822 ++conn->cmd_ident, 2,
823 1, idp->data, idp->len,
825 strlen(cmd->argv[2]));
826 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
827 0, NULL, NULL, buffer->data, buffer->len, TRUE);
828 silc_buffer_free(buffer);
829 silc_buffer_free(idp);
831 /* Notify application */
834 /* Register a pending callback that will actually remove the killed
835 client from our cache. */
836 silc_client_command_pending(conn, SILC_COMMAND_KILL, conn->cmd_ident,
837 silc_client_command_kill_remove,
838 silc_client_command_dup(cmd));
842 silc_client_command_free(cmd);
845 /* Command INFO. Request information about specific server. If specific
846 server is not provided the current server is used. */
848 SILC_CLIENT_CMD_FUNC(info)
850 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
851 SilcClientConnection conn = cmd->conn;
856 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
862 name = strdup(cmd->argv[1]);
864 /* Send the command */
866 buffer = silc_command_payload_encode_va(SILC_COMMAND_INFO, 0, 1,
867 1, name, strlen(name));
869 buffer = silc_command_payload_encode(SILC_COMMAND_INFO, 0,
870 NULL, NULL, NULL, 0);
871 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
872 0, NULL, NULL, buffer->data, buffer->len, TRUE);
873 silc_buffer_free(buffer);
877 /* Notify application */
881 silc_client_command_free(cmd);
884 /* Command PING. Sends ping to server. This is used to test the
885 communication channel. */
887 SILC_CLIENT_CMD_FUNC(ping)
889 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
890 SilcClientConnection conn = cmd->conn;
896 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
901 /* Send the command */
902 buffer = silc_command_payload_encode_va(SILC_COMMAND_PING, 0, 1,
903 1, conn->remote_id_data,
904 silc_id_get_len(conn->remote_id,
906 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
907 0, NULL, NULL, buffer->data, buffer->len, TRUE);
908 silc_buffer_free(buffer);
910 id = silc_id_str2id(conn->remote_id_data, conn->remote_id_data_len,
913 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
918 /* Start counting time */
919 for (i = 0; i < conn->ping_count; i++) {
920 if (conn->ping[i].dest_id == NULL) {
921 conn->ping[i].start_time = time(NULL);
922 conn->ping[i].dest_id = id;
923 conn->ping[i].dest_name = strdup(conn->remote_host);
927 if (i >= conn->ping_count) {
928 i = conn->ping_count;
929 conn->ping = silc_realloc(conn->ping, sizeof(*conn->ping) * (i + 1));
930 conn->ping[i].start_time = time(NULL);
931 conn->ping[i].dest_id = id;
932 conn->ping[i].dest_name = strdup(conn->remote_host);
936 /* Notify application */
940 silc_client_command_free(cmd);
943 /* Command JOIN. Joins to a channel. */
945 SILC_CLIENT_CMD_FUNC(join)
947 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
948 SilcClientConnection conn = cmd->conn;
949 SilcChannelEntry channel;
950 SilcBuffer buffer, idp, auth = NULL;
951 char *name, *passphrase = NULL, *pu8, *cipher = NULL, *hmac = NULL;
952 int i, passphrase_len = 0;
955 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
965 /* See if we have joined to the requested channel already */
966 channel = silc_client_get_channel(cmd->client, conn, cmd->argv[1]);
967 if (channel && silc_client_on_channel(channel, conn->local_entry))
970 idp = silc_id_payload_encode(conn->local_id, SILC_ID_CLIENT);
972 if (cmd->argv_lens[1] > 256)
973 cmd->argv_lens[1] = 256;
977 for (i = 2; i < cmd->argc; i++) {
978 if (!strcasecmp(cmd->argv[i], "-cipher") && cmd->argc > i + 1) {
979 cipher = cmd->argv[i + 1];
981 } else if (!strcasecmp(cmd->argv[i], "-hmac") && cmd->argc > i + 1) {
982 hmac = cmd->argv[i + 1];
984 } else if (!strcasecmp(cmd->argv[i], "-founder") && cmd->argc > i + 1) {
985 if (!strcasecmp(cmd->argv[i + 1], "-pubkey")) {
986 auth = silc_auth_public_key_auth_generate(cmd->client->public_key,
987 cmd->client->private_key,
988 cmd->client->rng, conn->hash,
992 auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
994 cmd->argv_lens[i + 1]);
998 /* Passphrases must be UTF-8 encoded, so encode if it is not */
999 if (!silc_utf8_valid(cmd->argv[i], cmd->argv_lens[i])) {
1000 passphrase_len = silc_utf8_encoded_len(cmd->argv[i],
1001 cmd->argv_lens[i], 0);
1002 pu8 = silc_calloc(passphrase_len, sizeof(*pu8));
1003 passphrase_len = silc_utf8_encode(cmd->argv[i], cmd->argv_lens[i],
1004 0, pu8, passphrase_len);
1007 passphrase = strdup(cmd->argv[i]);
1008 passphrase_len = cmd->argv_lens[i];
1013 /* Send JOIN command to the server */
1015 silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 6,
1016 1, name, strlen(name),
1017 2, idp->data, idp->len,
1018 3, passphrase, passphrase_len,
1019 4, cipher, cipher ? strlen(cipher) : 0,
1020 5, hmac, hmac ? strlen(hmac) : 0,
1021 6, auth ? auth->data : NULL,
1022 auth ? auth->len : 0);
1023 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1024 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1025 silc_buffer_free(buffer);
1026 silc_buffer_free(idp);
1028 silc_buffer_free(auth);
1029 silc_free(passphrase);
1031 /* Notify application */
1035 silc_client_command_free(cmd);
1038 /* MOTD command. Requests motd from server. */
1040 SILC_CLIENT_CMD_FUNC(motd)
1042 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1043 SilcClientConnection conn = cmd->conn;
1047 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1052 if (cmd->argc < 1 || cmd->argc > 2) {
1053 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1054 "Usage: /MOTD [<server>]");
1059 /* Send TOPIC command to the server */
1061 buffer = silc_command_payload_encode_va(SILC_COMMAND_MOTD, 0, 1,
1062 1, conn->remote_host,
1063 strlen(conn->remote_host));
1065 buffer = silc_command_payload_encode_va(SILC_COMMAND_MOTD, 0, 1,
1068 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1069 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1070 silc_buffer_free(buffer);
1072 /* Notify application */
1076 silc_client_command_free(cmd);
1079 /* UMODE. Set/unset user mode in SILC. This is used mainly to unset the
1080 modes as client cannot set itself server/router operator privileges. */
1082 SILC_CLIENT_CMD_FUNC(umode)
1084 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1085 SilcClientConnection conn = cmd->conn;
1086 SilcBuffer buffer, idp;
1087 unsigned char *cp, modebuf[4];
1088 SilcUInt32 mode, add, len;
1092 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1097 if (cmd->argc < 2) {
1098 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1099 "Usage: /UMODE +|-<modes>");
1104 mode = conn->local_entry->mode;
1106 /* Are we adding or removing mode */
1107 if (cmd->argv[1][0] == '-')
1113 cp = cmd->argv[1] + 1;
1115 for (i = 0; i < len; i++) {
1120 mode |= SILC_UMODE_SERVER_OPERATOR;
1121 mode |= SILC_UMODE_ROUTER_OPERATOR;
1123 mode = SILC_UMODE_NONE;
1128 mode |= SILC_UMODE_SERVER_OPERATOR;
1130 mode &= ~SILC_UMODE_SERVER_OPERATOR;
1134 mode |= SILC_UMODE_ROUTER_OPERATOR;
1136 mode &= ~SILC_UMODE_ROUTER_OPERATOR;
1140 mode |= SILC_UMODE_GONE;
1142 mode &= ~SILC_UMODE_GONE;
1151 idp = silc_id_payload_encode(conn->local_id, SILC_ID_CLIENT);
1152 SILC_PUT32_MSB(mode, modebuf);
1154 /* Send the command packet. We support sending only one mode at once
1155 that requires an argument. */
1157 silc_command_payload_encode_va(SILC_COMMAND_UMODE, ++conn->cmd_ident, 2,
1158 1, idp->data, idp->len,
1159 2, modebuf, sizeof(modebuf));
1160 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1161 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1162 silc_buffer_free(buffer);
1163 silc_buffer_free(idp);
1165 /* Notify application */
1169 silc_client_command_free(cmd);
1172 /* CMODE command. Sets channel mode. Modes that does not require any arguments
1173 can be set several at once. Those modes that require argument must be set
1174 separately (unless set with modes that does not require arguments). */
1176 SILC_CLIENT_CMD_FUNC(cmode)
1178 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1179 SilcClientConnection conn = cmd->conn;
1180 SilcChannelEntry channel;
1181 SilcBuffer buffer, chidp, auth = NULL;
1182 unsigned char *name, *cp, modebuf[4], tmp[4], *arg = NULL;
1183 SilcUInt32 mode, add, type, len, arg_len = 0;
1187 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1192 if (cmd->argc < 3) {
1193 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1194 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1199 if (cmd->argv[1][0] == '*') {
1200 if (!conn->current_channel) {
1201 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1202 "You are not on any channel");
1207 channel = conn->current_channel;
1209 name = cmd->argv[1];
1211 channel = silc_client_get_channel(cmd->client, conn, name);
1213 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1214 "You are on that channel");
1220 mode = channel->mode;
1222 /* Are we adding or removing mode */
1223 if (cmd->argv[2][0] == '-')
1228 /* Argument type to be sent to server */
1232 cp = cmd->argv[2] + 1;
1234 for (i = 0; i < len; i++) {
1238 mode |= SILC_CHANNEL_MODE_PRIVATE;
1240 mode &= ~SILC_CHANNEL_MODE_PRIVATE;
1244 mode |= SILC_CHANNEL_MODE_SECRET;
1246 mode &= ~SILC_CHANNEL_MODE_SECRET;
1250 mode |= SILC_CHANNEL_MODE_PRIVKEY;
1252 mode &= ~SILC_CHANNEL_MODE_PRIVKEY;
1256 mode |= SILC_CHANNEL_MODE_INVITE;
1258 mode &= ~SILC_CHANNEL_MODE_INVITE;
1262 mode |= SILC_CHANNEL_MODE_TOPIC;
1264 mode &= ~SILC_CHANNEL_MODE_TOPIC;
1268 mode |= SILC_CHANNEL_MODE_SILENCE_USERS;
1270 mode &= ~SILC_CHANNEL_MODE_SILENCE_USERS;
1274 mode |= SILC_CHANNEL_MODE_SILENCE_OPERS;
1276 mode &= ~SILC_CHANNEL_MODE_SILENCE_OPERS;
1281 mode |= SILC_CHANNEL_MODE_ULIMIT;
1283 if (cmd->argc < 4) {
1284 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1285 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1289 ll = atoi(cmd->argv[3]);
1290 SILC_PUT32_MSB(ll, tmp);
1294 mode &= ~SILC_CHANNEL_MODE_ULIMIT;
1299 mode |= SILC_CHANNEL_MODE_PASSPHRASE;
1301 if (cmd->argc < 4) {
1302 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1303 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1308 arg_len = cmd->argv_lens[3];
1310 mode &= ~SILC_CHANNEL_MODE_PASSPHRASE;
1315 mode |= SILC_CHANNEL_MODE_CIPHER;
1317 if (cmd->argc < 4) {
1318 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1319 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1324 arg_len = cmd->argv_lens[3];
1326 mode &= ~SILC_CHANNEL_MODE_CIPHER;
1331 mode |= SILC_CHANNEL_MODE_HMAC;
1333 if (cmd->argc < 4) {
1334 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1335 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1340 arg_len = cmd->argv_lens[3];
1342 mode &= ~SILC_CHANNEL_MODE_HMAC;
1347 mode |= SILC_CHANNEL_MODE_FOUNDER_AUTH;
1350 if (cmd->argc < 4) {
1351 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1352 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
1357 if (!strcasecmp(cmd->argv[3], "-pubkey")) {
1358 auth = silc_auth_public_key_auth_generate(cmd->client->public_key,
1359 cmd->client->private_key,
1365 auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
1366 cmd->argv[3], cmd->argv_lens[3]);
1370 arg_len = auth->len;
1372 mode &= ~SILC_CHANNEL_MODE_FOUNDER_AUTH;
1382 chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1383 SILC_PUT32_MSB(mode, modebuf);
1385 /* Send the command packet. We support sending only one mode at once
1386 that requires an argument. */
1389 silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 3,
1390 1, chidp->data, chidp->len,
1391 2, modebuf, sizeof(modebuf),
1392 type, arg, arg_len);
1395 silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 2,
1396 1, chidp->data, chidp->len,
1397 2, modebuf, sizeof(modebuf));
1400 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1401 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1402 silc_buffer_free(buffer);
1403 silc_buffer_free(chidp);
1405 silc_buffer_free(auth);
1407 /* Notify application */
1411 silc_client_command_free(cmd);
1414 /* CUMODE command. Changes client's mode on a channel. */
1416 SILC_CLIENT_CMD_FUNC(cumode)
1418 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1419 SilcClient client = cmd->client;
1420 SilcClientConnection conn = cmd->conn;
1421 SilcChannelEntry channel;
1422 SilcChannelUser chu;
1423 SilcClientEntry client_entry;
1424 SilcBuffer buffer, clidp, chidp, auth = NULL;
1425 unsigned char *name, *cp, modebuf[4];
1426 SilcUInt32 mode = 0, add, len;
1427 char *nickname = NULL;
1431 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1436 if (cmd->argc < 4) {
1437 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1438 "Usage: /CUMODE <channel> +|-<modes> <nickname>[@<server>]");
1443 if (cmd->argv[1][0] == '*') {
1444 if (!conn->current_channel) {
1445 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1446 "You are not on any channel");
1451 channel = conn->current_channel;
1453 name = cmd->argv[1];
1455 channel = silc_client_get_channel(cmd->client, conn, name);
1457 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1458 "You are on that channel");
1464 /* Parse the typed nickname. */
1465 if (client->internal->params->nickname_parse)
1466 client->internal->params->nickname_parse(cmd->argv[3], &nickname);
1468 nickname = strdup(cmd->argv[3]);
1470 /* Find client entry */
1471 client_entry = silc_idlist_get_client(cmd->client, conn, nickname,
1472 cmd->argv[3], TRUE);
1473 if (!client_entry) {
1479 /* Client entry not found, it was requested thus mark this to be
1481 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY,
1483 silc_client_command_cumode,
1484 silc_client_command_dup(cmd));
1489 /* Get the current mode */
1490 chu = silc_client_on_channel(channel, client_entry);
1494 /* Are we adding or removing mode */
1495 if (cmd->argv[2][0] == '-')
1501 cp = cmd->argv[2] + 1;
1503 for (i = 0; i < len; i++) {
1507 mode |= SILC_CHANNEL_UMODE_CHANFO;
1508 mode |= SILC_CHANNEL_UMODE_CHANOP;
1510 mode = SILC_CHANNEL_UMODE_NONE;
1515 if (cmd->argc == 5) {
1516 if (!strcasecmp(cmd->argv[4], "-pubkey")) {
1517 auth = silc_auth_public_key_auth_generate(cmd->client->public_key,
1518 cmd->client->private_key,
1524 auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
1525 cmd->argv[4], cmd->argv_lens[4]);
1528 mode |= SILC_CHANNEL_UMODE_CHANFO;
1530 mode &= ~SILC_CHANNEL_UMODE_CHANFO;
1535 mode |= SILC_CHANNEL_UMODE_CHANOP;
1537 mode &= ~SILC_CHANNEL_UMODE_CHANOP;
1546 chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1547 SILC_PUT32_MSB(mode, modebuf);
1548 clidp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
1550 /* Send the command packet. We support sending only one mode at once
1551 that requires an argument. */
1552 buffer = silc_command_payload_encode_va(SILC_COMMAND_CUMODE, 0,
1554 1, chidp->data, chidp->len,
1556 3, clidp->data, clidp->len,
1557 4, auth ? auth->data : NULL,
1558 auth ? auth->len : 0);
1560 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1561 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1562 silc_buffer_free(buffer);
1563 silc_buffer_free(chidp);
1564 silc_buffer_free(clidp);
1566 silc_buffer_free(auth);
1568 /* Notify application */
1572 silc_free(nickname);
1573 silc_client_command_free(cmd);
1576 /* KICK command. Kicks a client out of channel. */
1578 SILC_CLIENT_CMD_FUNC(kick)
1580 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1581 SilcClient client = cmd->client;
1582 SilcClientConnection conn = cmd->conn;
1583 SilcIDCacheEntry id_cache = NULL;
1584 SilcChannelEntry channel;
1585 SilcBuffer buffer, idp, idp2;
1586 SilcClientEntry target;
1588 char *nickname = NULL;
1591 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1596 if (cmd->argc < 3) {
1597 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1598 "Usage: /KICK <channel> <nickname> [<comment>]");
1603 if (cmd->argv[1][0] == '*') {
1604 if (!conn->current_channel) {
1605 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1606 "You are not on any channel");
1610 name = conn->current_channel->channel_name;
1612 name = cmd->argv[1];
1615 if (!conn->current_channel) {
1616 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1617 "You are not on that channel");
1622 /* Get the Channel ID of the channel */
1623 if (!silc_idcache_find_by_name_one(conn->channel_cache, name, &id_cache)) {
1624 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1625 "You are not on that channel");
1630 channel = (SilcChannelEntry)id_cache->context;
1632 /* Parse the typed nickname. */
1633 if (client->internal->params->nickname_parse)
1634 client->internal->params->nickname_parse(cmd->argv[2], &nickname);
1636 nickname = strdup(cmd->argv[2]);
1638 /* Get the target client */
1639 target = silc_idlist_get_client(cmd->client, conn, nickname,
1640 cmd->argv[2], FALSE);
1642 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1643 "No such client: %s", cmd->argv[2]);
1648 /* Send KICK command to the server */
1649 idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1650 idp2 = silc_id_payload_encode(target->id, SILC_ID_CLIENT);
1652 buffer = silc_command_payload_encode_va(SILC_COMMAND_KICK, 0, 2,
1653 1, idp->data, idp->len,
1654 2, idp2->data, idp2->len);
1656 buffer = silc_command_payload_encode_va(SILC_COMMAND_KICK, 0, 3,
1657 1, idp->data, idp->len,
1658 2, idp2->data, idp2->len,
1660 strlen(cmd->argv[3]));
1661 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1662 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1663 silc_buffer_free(buffer);
1664 silc_buffer_free(idp);
1665 silc_buffer_free(idp2);
1667 /* Notify application */
1671 silc_free(nickname);
1672 silc_client_command_free(cmd);
1675 static void silc_client_command_oper_send(unsigned char *data,
1676 SilcUInt32 data_len, void *context)
1678 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1679 SilcClientConnection conn = cmd->conn;
1680 SilcBuffer buffer, auth;
1682 if (cmd->argc >= 3) {
1683 /* Encode the public key authentication payload */
1684 auth = silc_auth_public_key_auth_generate(cmd->client->public_key,
1685 cmd->client->private_key,
1686 cmd->client->rng, conn->hash,
1690 /* Encode the password authentication payload */
1691 auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
1695 buffer = silc_command_payload_encode_va(SILC_COMMAND_OPER, 0, 2,
1697 strlen(cmd->argv[1]),
1698 2, auth->data, auth->len);
1699 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1700 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1702 silc_buffer_free(buffer);
1703 silc_buffer_free(auth);
1705 /* Notify application */
1709 /* OPER command. Used to obtain server operator privileges. */
1711 SILC_CLIENT_CMD_FUNC(oper)
1713 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1714 SilcClientConnection conn = cmd->conn;
1717 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1722 if (cmd->argc < 2) {
1723 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1724 "Usage: /OPER <username> [-pubkey]");
1729 if (cmd->argc < 3) {
1730 /* Get passphrase */
1731 cmd->client->internal->ops->ask_passphrase(cmd->client, conn,
1732 silc_client_command_oper_send,
1737 silc_client_command_oper_send(NULL, 0, context);
1740 silc_client_command_free(cmd);
1743 static void silc_client_command_silcoper_send(unsigned char *data,
1744 SilcUInt32 data_len,
1747 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1748 SilcClientConnection conn = cmd->conn;
1749 SilcBuffer buffer, auth;
1751 if (cmd->argc >= 3) {
1752 /* Encode the public key authentication payload */
1753 auth = silc_auth_public_key_auth_generate(cmd->client->public_key,
1754 cmd->client->private_key,
1755 cmd->client->rng, conn->hash,
1759 /* Encode the password authentication payload */
1760 auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
1764 buffer = silc_command_payload_encode_va(SILC_COMMAND_SILCOPER, 0, 2,
1766 strlen(cmd->argv[1]),
1767 2, auth->data, auth->len);
1768 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1769 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1771 silc_buffer_free(buffer);
1772 silc_buffer_free(auth);
1774 /* Notify application */
1778 /* SILCOPER command. Used to obtain router operator privileges. */
1780 SILC_CLIENT_CMD_FUNC(silcoper)
1782 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1783 SilcClientConnection conn = cmd->conn;
1786 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1791 if (cmd->argc < 2) {
1792 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1793 "Usage: /SILCOPER <username> [-pubkey]");
1798 if (cmd->argc < 3) {
1799 /* Get passphrase */
1800 cmd->client->internal->ops->ask_passphrase(cmd->client, conn,
1801 silc_client_command_silcoper_send,
1806 silc_client_command_silcoper_send(NULL, 0, context);
1809 silc_client_command_free(cmd);
1812 /* Command BAN. This is used to manage the ban list of the channel. */
1814 SILC_CLIENT_CMD_FUNC(ban)
1816 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1817 SilcClientConnection conn = cmd->conn;
1818 SilcChannelEntry channel;
1819 SilcBuffer buffer, chidp;
1821 char *name, *ban = NULL;
1824 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1829 if (cmd->argc < 2) {
1830 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1831 "Usage: /BAN <channel> "
1832 "[+|-[<nickname>[@<server>[!<username>[@hostname>]]]]]");
1837 if (cmd->argv[1][0] == '*') {
1838 if (!conn->current_channel) {
1839 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1840 "You are not on any channel");
1845 channel = conn->current_channel;
1847 name = cmd->argv[1];
1849 channel = silc_client_get_channel(cmd->client, conn, name);
1851 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1852 "You are on that channel");
1858 if (cmd->argc == 3) {
1859 if (cmd->argv[2][0] == '+')
1868 chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1870 /* Send the command */
1872 buffer = silc_command_payload_encode_va(SILC_COMMAND_BAN, 0, 2,
1873 1, chidp->data, chidp->len,
1874 type, ban, strlen(ban));
1876 buffer = silc_command_payload_encode_va(SILC_COMMAND_BAN, 0, 1,
1877 1, chidp->data, chidp->len);
1879 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1880 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1881 silc_buffer_free(buffer);
1882 silc_buffer_free(chidp);
1884 /* Notify application */
1888 silc_client_command_free(cmd);
1891 /* LEAVE command. Leaves a channel. Client removes itself from a channel. */
1893 SILC_CLIENT_CMD_FUNC(leave)
1895 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1896 SilcClientConnection conn = cmd->conn;
1897 SilcChannelEntry channel;
1898 SilcChannelUser chu;
1899 SilcBuffer buffer, idp;
1903 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1908 if (cmd->argc != 2) {
1909 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1910 "Usage: /LEAVE <channel>");
1915 if (cmd->argv[1][0] == '*') {
1916 if (!conn->current_channel) {
1917 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1918 "You are not on any channel");
1922 name = conn->current_channel->channel_name;
1924 name = cmd->argv[1];
1927 /* Get the channel entry */
1928 channel = silc_client_get_channel(cmd->client, conn, name);
1930 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1931 "You are not on that channel");
1936 /* Remove us from channel */
1937 chu = silc_client_on_channel(channel, conn->local_entry);
1939 silc_hash_table_del(chu->client->channels, chu->channel);
1940 silc_hash_table_del(chu->channel->user_list, chu->client);
1944 /* Send LEAVE command to the server */
1945 idp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1946 buffer = silc_command_payload_encode_va(SILC_COMMAND_LEAVE, 0, 1,
1947 1, idp->data, idp->len);
1948 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1949 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1950 silc_buffer_free(buffer);
1951 silc_buffer_free(idp);
1953 /* Notify application */
1956 if (conn->current_channel == channel)
1957 conn->current_channel = NULL;
1959 silc_client_del_channel(cmd->client, cmd->conn, channel);
1962 silc_client_command_free(cmd);
1965 /* Command USERS. Requests the USERS of the clients joined on requested
1968 SILC_CLIENT_CMD_FUNC(users)
1970 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1971 SilcClientConnection conn = cmd->conn;
1976 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1981 if (cmd->argc != 2) {
1982 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1983 "Usage: /USERS <channel>");
1988 if (cmd->argv[1][0] == '*') {
1989 if (!conn->current_channel) {
1990 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
1991 "You are not on any channel");
1995 name = conn->current_channel->channel_name;
1997 name = cmd->argv[1];
2000 /* Send USERS command to the server */
2001 buffer = silc_command_payload_encode_va(SILC_COMMAND_USERS,
2002 ++conn->cmd_ident, 1,
2003 2, name, strlen(name));
2004 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND,
2005 NULL, 0, NULL, NULL, buffer->data,
2007 silc_buffer_free(buffer);
2009 /* Notify application */
2013 silc_client_command_free(cmd);
2016 /* Command GETKEY. Used to fetch remote client's public key. */
2018 SILC_CLIENT_CMD_FUNC(getkey)
2020 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
2021 SilcClientConnection conn = cmd->conn;
2022 SilcClient client = cmd->client;
2023 SilcClientEntry client_entry = NULL;
2024 SilcServerEntry server_entry = NULL;
2025 char *nickname = NULL;
2026 SilcBuffer idp, buffer;
2028 SILC_LOG_DEBUG(("Start"));
2031 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
2036 if (cmd->argc < 2) {
2037 client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_INFO,
2038 "Usage: /GETKEY <nickname or server name>");
2043 /* Parse the typed nickname. */
2044 if (client->internal->params->nickname_parse)
2045 client->internal->params->nickname_parse(cmd->argv[1], &nickname);
2047 nickname = strdup(cmd->argv[1]);
2049 /* Find client entry */
2050 client_entry = silc_idlist_get_client(client, conn, nickname, cmd->argv[1],
2052 if (!client_entry) {
2053 /* Check whether user requested server actually */
2054 server_entry = silc_client_get_server(client, conn, cmd->argv[1]);
2056 if (!server_entry) {
2057 /* No. what ever user wants we don't have it, so resolve it. We
2058 will first try to resolve the client, and if that fails then
2059 we'll try to resolve the server. */
2061 if (!cmd->pending) {
2062 /* This will send the IDENTIFY command for nickname */
2063 silc_idlist_get_client(client, conn, nickname, cmd->argv[1], TRUE);
2064 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY,
2066 silc_client_command_getkey,
2067 silc_client_command_dup(cmd));
2071 SilcClientCommandReplyContext reply =
2072 (SilcClientCommandReplyContext)context2;
2073 SilcCommandStatus status = silc_command_get_status(reply->payload);
2075 /* If nickname was not found, then resolve the server. */
2076 if (status == SILC_STATUS_ERR_NO_SUCH_NICK) {
2077 /* This sends the IDENTIFY command to resolve the server. */
2078 silc_client_command_register(client, SILC_COMMAND_IDENTIFY,
2080 silc_client_command_reply_identify_i, 0,
2082 silc_client_command_send(client, conn, SILC_COMMAND_IDENTIFY,
2084 2, cmd->argv[1], cmd->argv_lens[1]);
2085 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY,
2087 silc_client_command_getkey,
2088 silc_client_command_dup(cmd));
2092 /* If server was not found, then we've resolved both nickname and
2093 server and did not find anybody. */
2094 if (status == SILC_STATUS_ERR_NO_SUCH_SERVER) {
2095 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, "%s",
2096 silc_client_command_status_message(SILC_STATUS_ERR_NO_SUCH_NICK));
2097 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_ERROR, "%s",
2098 silc_client_command_status_message(status));
2108 idp = silc_id_payload_encode(server_entry->server_id, SILC_ID_SERVER);
2110 idp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
2113 buffer = silc_command_payload_encode_va(SILC_COMMAND_GETKEY, 0, 1,
2114 1, idp->data, idp->len);
2115 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
2116 0, NULL, NULL, buffer->data, buffer->len, TRUE);
2117 silc_buffer_free(buffer);
2118 silc_buffer_free(idp);
2120 /* Notify application */
2124 silc_free(nickname);
2125 silc_client_command_free(cmd);
2128 /* Register a new command indicated by the `command' to the SILC client.
2129 The `name' is optional command name. If provided the command may be
2130 searched using the silc_client_command_find by that name. The
2131 `command_function' is the function to be called when the command is
2132 executed, and the `command_reply_function' is the function to be
2133 called after the server has sent reply back to the command.
2135 The `ident' is optional identifier for the command. If non-zero
2136 the `command_reply_function' for the command type `command' will be
2137 called only if the command reply sent by server includes the
2138 command identifier `ident'. Application usually does not need it
2139 and set it to zero value. */
2141 bool silc_client_command_register(SilcClient client,
2142 SilcCommand command,
2144 SilcCommandCb command_function,
2145 SilcCommandCb command_reply_function,
2149 SilcClientCommand cmd;
2151 cmd = silc_calloc(1, sizeof(*cmd));
2153 cmd->command = command_function;
2154 cmd->reply = command_reply_function;
2155 cmd->name = name ? strdup(name) : NULL;
2156 cmd->max_args = max_args;
2159 silc_list_add(client->internal->commands, cmd);
2164 /* Unregister a command indicated by the `command' with command function
2165 `command_function' and command reply function `command_reply_function'.
2166 Returns TRUE if the command was found and unregistered. */
2168 bool silc_client_command_unregister(SilcClient client,
2169 SilcCommand command,
2170 SilcCommandCb command_function,
2171 SilcCommandCb command_reply_function,
2174 SilcClientCommand cmd;
2176 silc_list_start(client->internal->commands);
2177 while ((cmd = silc_list_get(client->internal->commands)) != SILC_LIST_END) {
2178 if (cmd->cmd == command && cmd->command == command_function &&
2179 cmd->reply == command_reply_function && cmd->ident == ident) {
2180 silc_list_del(client->internal->commands, cmd);
2181 silc_free(cmd->name);
2190 /* Private range commands, specific to this implementation (and compatible
2191 with SILC Server). */
2193 /* CONNECT command. Connects the server to another server. */
2195 SILC_CLIENT_CMD_FUNC(connect)
2197 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
2198 SilcClientConnection conn = cmd->conn;
2200 unsigned char port[4];
2204 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
2209 if (cmd->argc < 2) {
2210 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
2211 "Usage: /CONNECT <server> [<port>]");
2216 if (cmd->argc == 3) {
2217 tmp = atoi(cmd->argv[2]);
2218 SILC_PUT32_MSB(tmp, port);
2222 buffer = silc_command_payload_encode_va(SILC_COMMAND_PRIV_CONNECT, 0, 2,
2224 strlen(cmd->argv[1]),
2227 buffer = silc_command_payload_encode_va(SILC_COMMAND_PRIV_CONNECT, 0, 1,
2229 strlen(cmd->argv[1]));
2230 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
2231 0, NULL, NULL, buffer->data, buffer->len, TRUE);
2232 silc_buffer_free(buffer);
2234 /* Notify application */
2238 silc_client_command_free(cmd);
2242 /* CLOSE command. Close server connection to the remote server */
2244 SILC_CLIENT_CMD_FUNC(close)
2246 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
2247 SilcClientConnection conn = cmd->conn;
2249 unsigned char port[4];
2253 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
2258 if (cmd->argc < 2) {
2259 SAY(cmd->client, conn, SILC_CLIENT_MESSAGE_INFO,
2260 "Usage: /CLOSE <server> [<port>]");
2265 if (cmd->argc == 3) {
2266 tmp = atoi(cmd->argv[2]);
2267 SILC_PUT32_MSB(tmp, port);
2271 buffer = silc_command_payload_encode_va(SILC_COMMAND_PRIV_CLOSE, 0, 2,
2273 strlen(cmd->argv[1]),
2276 buffer = silc_command_payload_encode_va(SILC_COMMAND_PRIV_CLOSE, 0, 1,
2278 strlen(cmd->argv[1]));
2279 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
2280 0, NULL, NULL, buffer->data, buffer->len, TRUE);
2281 silc_buffer_free(buffer);
2283 /* Notify application */
2287 silc_client_command_free(cmd);
2290 /* SHUTDOWN command. Shutdowns the server. */
2292 SILC_CLIENT_CMD_FUNC(shutdown)
2294 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
2297 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
2302 /* Send the command */
2303 silc_client_command_send(cmd->client, cmd->conn,
2304 SILC_COMMAND_PRIV_SHUTDOWN, 0, 0);
2306 /* Notify application */
2310 silc_client_command_free(cmd);
2313 /* Register all default commands provided by the client library for the
2316 void silc_client_commands_register(SilcClient client)
2318 silc_list_init(client->internal->commands, struct SilcClientCommandStruct,
2321 SILC_CLIENT_CMD(whois, WHOIS, "WHOIS", 3);
2322 SILC_CLIENT_CMD(whowas, WHOWAS, "WHOWAS", 3);
2323 SILC_CLIENT_CMD(identify, IDENTIFY, "IDENTIFY", 3);
2324 SILC_CLIENT_CMD(nick, NICK, "NICK", 2);
2325 SILC_CLIENT_CMD(list, LIST, "LIST", 2);
2326 SILC_CLIENT_CMD(topic, TOPIC, "TOPIC", 3);
2327 SILC_CLIENT_CMD(invite, INVITE, "INVITE", 3);
2328 SILC_CLIENT_CMD(quit, QUIT, "QUIT", 2);
2329 SILC_CLIENT_CMD(kill, KILL, "KILL", 3);
2330 SILC_CLIENT_CMD(info, INFO, "INFO", 2);
2331 SILC_CLIENT_CMD(ping, PING, "PING", 2);
2332 SILC_CLIENT_CMD(oper, OPER, "OPER", 3);
2333 SILC_CLIENT_CMD(join, JOIN, "JOIN", 9);
2334 SILC_CLIENT_CMD(motd, MOTD, "MOTD", 2);
2335 SILC_CLIENT_CMD(umode, UMODE, "UMODE", 2);
2336 SILC_CLIENT_CMD(cmode, CMODE, "CMODE", 4);
2337 SILC_CLIENT_CMD(cumode, CUMODE, "CUMODE", 5);
2338 SILC_CLIENT_CMD(kick, KICK, "KICK", 4);
2339 SILC_CLIENT_CMD(ban, BAN, "BAN", 3);
2340 SILC_CLIENT_CMD(silcoper, SILCOPER, "SILCOPER", 3);
2341 SILC_CLIENT_CMD(leave, LEAVE, "LEAVE", 2);
2342 SILC_CLIENT_CMD(users, USERS, "USERS", 2);
2343 SILC_CLIENT_CMD(getkey, GETKEY, "GETKEY", 2);
2345 SILC_CLIENT_CMD(connect, PRIV_CONNECT, "CONNECT", 3);
2346 SILC_CLIENT_CMD(close, PRIV_CLOSE, "CLOSE", 3);
2347 SILC_CLIENT_CMD(shutdown, PRIV_SHUTDOWN, "SHUTDOWN", 1);
2350 /* Unregister all commands. */
2352 void silc_client_commands_unregister(SilcClient client)
2354 SILC_CLIENT_CMDU(whois, WHOIS, "WHOIS");
2355 SILC_CLIENT_CMDU(whowas, WHOWAS, "WHOWAS");
2356 SILC_CLIENT_CMDU(identify, IDENTIFY, "IDENTIFY");
2357 SILC_CLIENT_CMDU(nick, NICK, "NICK");
2358 SILC_CLIENT_CMDU(list, LIST, "LIST");
2359 SILC_CLIENT_CMDU(topic, TOPIC, "TOPIC");
2360 SILC_CLIENT_CMDU(invite, INVITE, "INVITE");
2361 SILC_CLIENT_CMDU(quit, QUIT, "QUIT");
2362 SILC_CLIENT_CMDU(kill, KILL, "KILL");
2363 SILC_CLIENT_CMDU(info, INFO, "INFO");
2364 SILC_CLIENT_CMDU(ping, PING, "PING");
2365 SILC_CLIENT_CMDU(oper, OPER, "OPER");
2366 SILC_CLIENT_CMDU(join, JOIN, "JOIN");
2367 SILC_CLIENT_CMDU(motd, MOTD, "MOTD");
2368 SILC_CLIENT_CMDU(umode, UMODE, "UMODE");
2369 SILC_CLIENT_CMDU(cmode, CMODE, "CMODE");
2370 SILC_CLIENT_CMDU(cumode, CUMODE, "CUMODE");
2371 SILC_CLIENT_CMDU(kick, KICK, "KICK");
2372 SILC_CLIENT_CMDU(ban, BAN, "BAN");
2373 SILC_CLIENT_CMDU(silcoper, SILCOPER, "SILCOPER");
2374 SILC_CLIENT_CMDU(leave, LEAVE, "LEAVE");
2375 SILC_CLIENT_CMDU(users, USERS, "USERS");
2376 SILC_CLIENT_CMDU(getkey, GETKEY, "GETKEY");
2378 SILC_CLIENT_CMDU(connect, PRIV_CONNECT, "CONNECT");
2379 SILC_CLIENT_CMDU(close, PRIV_CLOSE, "CLOSE");
2380 SILC_CLIENT_CMDU(shutdown, PRIV_SHUTDOWN, "SHUTDOWN");