5 Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
7 Copyright (C) 1997 - 2001 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; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
22 #include "clientlibincludes.h"
23 #include "client_internal.h"
25 /* Client command list. */
26 SilcClientCommand silc_command_list[] =
28 SILC_CLIENT_CMD(whois, WHOIS, "WHOIS", SILC_CF_LAG | SILC_CF_REG, 3),
29 SILC_CLIENT_CMD(whowas, WHOWAS, "WHOWAS", SILC_CF_LAG | SILC_CF_REG, 3),
30 SILC_CLIENT_CMD(identify, IDENTIFY, "IDENTIFY",
31 SILC_CF_LAG | SILC_CF_REG, 3),
32 SILC_CLIENT_CMD(nick, NICK, "NICK", SILC_CF_LAG | SILC_CF_REG, 2),
33 SILC_CLIENT_CMD(list, LIST, "LIST", SILC_CF_LAG | SILC_CF_REG, 2),
34 SILC_CLIENT_CMD(topic, TOPIC, "TOPIC", SILC_CF_LAG | SILC_CF_REG, 3),
35 SILC_CLIENT_CMD(invite, INVITE, "INVITE", SILC_CF_LAG | SILC_CF_REG, 3),
36 SILC_CLIENT_CMD(quit, QUIT, "QUIT", SILC_CF_LAG | SILC_CF_REG, 2),
37 SILC_CLIENT_CMD(kill, KILL, "KILL",
38 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 3),
39 SILC_CLIENT_CMD(info, INFO, "INFO", SILC_CF_LAG | SILC_CF_REG, 2),
40 SILC_CLIENT_CMD(connect, CONNECT, "CONNECT",
41 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 3),
42 SILC_CLIENT_CMD(ping, PING, "PING", SILC_CF_LAG | SILC_CF_REG, 2),
43 SILC_CLIENT_CMD(oper, OPER, "OPER",
44 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
45 SILC_CLIENT_CMD(join, JOIN, "JOIN", SILC_CF_LAG | SILC_CF_REG, 5),
46 SILC_CLIENT_CMD(motd, MOTD, "MOTD", SILC_CF_LAG | SILC_CF_REG, 2),
47 SILC_CLIENT_CMD(umode, UMODE, "UMODE", SILC_CF_LAG | SILC_CF_REG, 2),
48 SILC_CLIENT_CMD(cmode, CMODE, "CMODE", SILC_CF_LAG | SILC_CF_REG, 4),
49 SILC_CLIENT_CMD(cumode, CUMODE, "CUMODE", SILC_CF_LAG | SILC_CF_REG, 5),
50 SILC_CLIENT_CMD(kick, KICK, "KICK", SILC_CF_LAG | SILC_CF_REG, 4),
51 SILC_CLIENT_CMD(restart, RESTART, "RESTART",
52 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 2),
53 SILC_CLIENT_CMD(close, CLOSE, "CLOSE",
54 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 3),
55 SILC_CLIENT_CMD(shutdown, SHUTDOWN, "SHUTDOWN",
56 SILC_CF_LAG | SILC_CF_REG | SILC_CF_OPER, 1),
57 SILC_CLIENT_CMD(silcoper, SILCOPER, "SILCOPER",
58 SILC_CF_LAG | SILC_CF_REG | SILC_CF_SILC_OPER, 3),
59 SILC_CLIENT_CMD(leave, LEAVE, "LEAVE", SILC_CF_LAG | SILC_CF_REG, 2),
60 SILC_CLIENT_CMD(users, USERS, "USERS", SILC_CF_LAG | SILC_CF_REG, 2),
62 { NULL, 0, NULL, 0, 0 },
65 #define SILC_NOT_CONNECTED(x, c) \
66 x->ops->say((x), (c), \
67 "You are not connected to a server, use /SERVER to connect");
69 /* Command operation that is called at the end of all commands.
71 #define COMMAND cmd->client->ops->command(cmd->client, cmd->conn, \
72 cmd, TRUE, cmd->command->cmd)
74 /* Error to application. Usage: COMMAND_ERROR; */
75 #define COMMAND_ERROR cmd->client->ops->command(cmd->client, cmd->conn, \
76 cmd, FALSE, cmd->command->cmd)
78 /* Generic function to send any command. The arguments must be sent already
79 encoded into correct form and in correct order. */
81 void silc_client_send_command(SilcClient client, SilcClientConnection conn,
82 SilcCommand command, unsigned short ident,
83 unsigned int argc, ...)
90 packet = silc_command_payload_encode_vap(command, ident, argc, ap);
91 silc_client_packet_send(client, conn->sock, SILC_PACKET_COMMAND,
92 NULL, 0, NULL, NULL, packet->data,
94 silc_buffer_free(packet);
97 /* Finds and returns a pointer to the command list. Return NULL if the
98 command is not found. */
100 SilcClientCommand *silc_client_command_find(const char *name)
102 SilcClientCommand *cmd;
104 for (cmd = silc_command_list; cmd->name; cmd++) {
105 if (!strcmp(cmd->name, name))
112 /* Add new pending command to be executed when reply to a command has been
113 received. The `reply_cmd' is the command that will call the `callback'
114 with `context' when reply has been received. If `ident is non-zero
115 the `callback' will be executed when received reply with command
116 identifier `ident'. */
118 void silc_client_command_pending(SilcClientConnection conn,
119 SilcCommand reply_cmd,
120 unsigned short ident,
121 SilcClientPendingDestructor destructor,
122 SilcCommandCb callback,
125 SilcClientCommandPending *reply;
127 reply = silc_calloc(1, sizeof(*reply));
128 reply->reply_cmd = reply_cmd;
129 reply->ident = ident;
130 reply->context = context;
131 reply->callback = callback;
132 reply->destructor = destructor;
133 silc_dlist_add(conn->pending_commands, reply);
136 /* Deletes pending command by reply command type. */
138 void silc_client_command_pending_del(SilcClientConnection conn,
139 SilcCommand reply_cmd,
140 unsigned short ident)
142 SilcClientCommandPending *r;
144 silc_dlist_start(conn->pending_commands);
145 while ((r = silc_dlist_get(conn->pending_commands)) != SILC_LIST_END) {
146 if (r->reply_cmd == reply_cmd && r->ident == ident) {
147 silc_dlist_del(conn->pending_commands, r);
153 /* Checks for pending commands and marks callbacks to be called from
154 the command reply function. Returns TRUE if there were pending command. */
156 int silc_client_command_pending_check(SilcClientConnection conn,
157 SilcClientCommandReplyContext ctx,
159 unsigned short ident)
161 SilcClientCommandPending *r;
163 silc_dlist_start(conn->pending_commands);
164 while ((r = silc_dlist_get(conn->pending_commands)) != SILC_LIST_END) {
165 if (r->reply_cmd == command && r->ident == ident) {
166 ctx->context = r->context;
167 ctx->callback = r->callback;
168 ctx->destructor = r->destructor;
177 /* Allocate Command Context */
179 SilcClientCommandContext silc_client_command_alloc()
181 SilcClientCommandContext ctx = silc_calloc(1, sizeof(*ctx));
186 /* Free command context and its internals */
188 void silc_client_command_free(SilcClientCommandContext ctx)
191 SILC_LOG_DEBUG(("Command context %p refcnt %d->%d", ctx, ctx->users + 1,
193 if (ctx->users < 1) {
196 for (i = 0; i < ctx->argc; i++)
197 silc_free(ctx->argv[i]);
202 /* Duplicate Command Context by adding reference counter. The context won't
203 be free'd untill it hits zero. */
205 SilcClientCommandContext silc_client_command_dup(SilcClientCommandContext ctx)
208 SILC_LOG_DEBUG(("Command context %p refcnt %d->%d", ctx, ctx->users - 1,
213 /* Pending command destructor. */
215 static void silc_client_command_destructor(void *context)
217 silc_client_command_free((SilcClientCommandContext)context);
220 /* silc_client_get_client completion callback */
221 void silc_client_command_completion(SilcClient client,
222 SilcClientConnection conn,
223 SilcClientEntry clients,
224 unsigned int clients_count,
230 /* Command WHOIS. This command is used to query information about
233 SILC_CLIENT_CMD_FUNC(whois)
235 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
236 SilcClientConnection conn = cmd->conn;
240 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
245 if (cmd->argc < 2 || cmd->argc > 3) {
246 cmd->client->ops->say(cmd->client, conn,
247 "Usage: /WHOIS <nickname>[@<server>] [<count>]");
252 buffer = silc_command_payload_encode(SILC_COMMAND_WHOIS,
253 cmd->argc - 1, ++cmd->argv,
254 ++cmd->argv_lens, ++cmd->argv_types,
256 silc_client_packet_send(cmd->client, cmd->conn->sock,
257 SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
258 buffer->data, buffer->len, TRUE);
259 silc_buffer_free(buffer);
264 /* Notify application */
268 silc_client_command_free(cmd);
271 /* Command WHOWAS. This command is used to query history information about
272 specific user that used to exist in the network. */
274 SILC_CLIENT_CMD_FUNC(whowas)
276 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
277 SilcClientConnection conn = cmd->conn;
281 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
286 if (cmd->argc < 2 || cmd->argc > 3) {
287 cmd->client->ops->say(cmd->client, conn,
288 "Usage: /WHOWAS <nickname>[@<server>] [<count>]");
293 buffer = silc_command_payload_encode(SILC_COMMAND_WHOWAS,
294 cmd->argc - 1, ++cmd->argv,
295 ++cmd->argv_lens, ++cmd->argv_types,
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);
305 /* Notify application */
309 silc_client_command_free(cmd);
312 /* Command IDENTIFY. This command is used to query information about
313 specific user, especially ID's. */
315 SILC_CLIENT_CMD_FUNC(identify)
317 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
318 SilcClientConnection conn = cmd->conn;
322 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
327 if (cmd->argc < 2 || cmd->argc > 3) {
328 cmd->client->ops->say(cmd->client, conn,
329 "Usage: /IDENTIFY <nickname>[@<server>] [<count>]");
334 buffer = silc_command_payload_encode(SILC_COMMAND_IDENTIFY,
335 cmd->argc - 1, ++cmd->argv,
336 ++cmd->argv_lens, ++cmd->argv_types,
338 silc_client_packet_send(cmd->client, cmd->conn->sock,
339 SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
340 buffer->data, buffer->len, TRUE);
341 silc_buffer_free(buffer);
346 /* Notify application */
350 silc_client_command_free(cmd);
353 /* Command NICK. Shows current nickname/sets new nickname on current
356 SILC_CLIENT_CMD_FUNC(nick)
358 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
359 SilcClientConnection conn = cmd->conn;
363 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
368 if (!strcmp(conn->nickname, cmd->argv[1]))
371 /* Show current nickname */
374 cmd->client->ops->say(cmd->client, conn,
375 "Your nickname is %s on server %s",
376 conn->nickname, conn->remote_host);
378 cmd->client->ops->say(cmd->client, conn,
379 "Your nickname is %s", conn->nickname);
382 /* XXX Notify application */
387 /* Set new nickname */
388 buffer = silc_command_payload_encode(SILC_COMMAND_NICK,
389 cmd->argc - 1, ++cmd->argv,
390 ++cmd->argv_lens, ++cmd->argv_types,
391 ++cmd->conn->cmd_ident);
392 silc_client_packet_send(cmd->client, cmd->conn->sock,
393 SILC_PACKET_COMMAND, NULL, 0, NULL, NULL,
394 buffer->data, buffer->len, TRUE);
395 silc_buffer_free(buffer);
400 silc_free(conn->nickname);
401 conn->nickname = strdup(cmd->argv[1]);
403 /* Notify application */
407 silc_client_command_free(cmd);
410 SILC_CLIENT_CMD_FUNC(list)
414 /* Command TOPIC. Sets/shows topic on a channel. */
416 SILC_CLIENT_CMD_FUNC(topic)
418 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
419 SilcClientConnection conn = cmd->conn;
420 SilcIDCacheEntry id_cache = NULL;
421 SilcChannelEntry channel;
422 SilcBuffer buffer, idp;
426 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
431 if (cmd->argc < 2 || cmd->argc > 3) {
432 cmd->client->ops->say(cmd->client, conn,
433 "Usage: /TOPIC <channel> [<topic>]");
438 if (cmd->argv[1][0] == '*') {
439 if (!conn->current_channel) {
440 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
444 name = conn->current_channel->channel_name;
449 if (!conn->current_channel) {
450 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
455 /* Get the Channel ID of the channel */
456 if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
457 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
462 channel = (SilcChannelEntry)id_cache->context;
464 /* Send TOPIC command to the server */
465 idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
467 buffer = silc_command_payload_encode_va(SILC_COMMAND_TOPIC, 0, 2,
468 1, idp->data, idp->len,
470 strlen(cmd->argv[2]));
472 buffer = silc_command_payload_encode_va(SILC_COMMAND_TOPIC, 1,
473 1, idp->data, idp->len,
475 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
476 0, NULL, NULL, buffer->data, buffer->len, TRUE);
477 silc_buffer_free(buffer);
478 silc_buffer_free(idp);
480 /* Notify application */
484 silc_client_command_free(cmd);
487 /* Command INVITE. Invites specific client to join a channel. */
489 SILC_CLIENT_CMD_FUNC(invite)
491 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
492 SilcClient client = cmd->client;
493 SilcClientConnection conn = cmd->conn;
494 SilcClientEntry client_entry;
495 SilcChannelEntry channel_entry;
496 SilcBuffer buffer, clidp, chidp;
497 unsigned int num = 0;
498 char *nickname = NULL, *server = NULL;
501 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
506 if (cmd->argc != 3) {
507 cmd->client->ops->say(cmd->client, conn,
508 "Usage: /INVITE <nickname>[@<server>] <channel>");
513 /* Parse the typed nickname. */
514 if (!silc_parse_nickname(cmd->argv[1], &nickname, &server, &num)) {
515 cmd->client->ops->say(cmd->client, conn, "Bad nickname");
520 /* Find client entry */
521 client_entry = silc_idlist_get_client(client, conn, nickname, server, num,
529 /* Client entry not found, it was requested thus mark this to be
531 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY, conn->cmd_ident,
532 silc_client_command_destructor,
533 silc_client_command_invite,
534 silc_client_command_dup(cmd));
539 /* Find channel entry */
540 channel_entry = silc_client_get_channel(client, conn, cmd->argv[2]);
541 if (!channel_entry) {
542 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
548 clidp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
549 chidp = silc_id_payload_encode(channel_entry->id, SILC_ID_CHANNEL);
550 buffer = silc_command_payload_encode_va(SILC_COMMAND_INVITE, 0, 2,
551 1, clidp->data, clidp->len,
552 2, chidp->data, chidp->len);
553 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
554 0, NULL, NULL, buffer->data, buffer->len, TRUE);
555 silc_buffer_free(buffer);
556 silc_buffer_free(clidp);
557 silc_buffer_free(chidp);
559 cmd->client->ops->say(cmd->client, conn,
560 "Inviting %s to channel %s", cmd->argv[1],
563 /* Notify application */
571 silc_client_command_free(cmd);
576 SilcClientConnection conn;
579 SILC_TASK_CALLBACK(silc_client_command_quit_cb)
581 QuitInternal q = (QuitInternal)context;
583 /* Close connection */
584 q->client->ops->disconnect(q->client, q->conn);
585 silc_client_close_connection(q->client, q->conn->sock->user_data);
590 /* Command QUIT. Closes connection with current server. */
592 SILC_CLIENT_CMD_FUNC(quit)
594 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
599 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
605 buffer = silc_command_payload_encode(SILC_COMMAND_QUIT, cmd->argc - 1,
606 &cmd->argv[1], &cmd->argv_lens[1],
607 &cmd->argv_types[1], 0);
609 buffer = silc_command_payload_encode(SILC_COMMAND_QUIT, 0,
610 NULL, NULL, NULL, 0);
611 silc_client_packet_send(cmd->client, cmd->conn->sock, SILC_PACKET_COMMAND,
613 buffer->data, buffer->len, TRUE);
614 silc_buffer_free(buffer);
616 q = silc_calloc(1, sizeof(*q));
617 q->client = cmd->client;
620 /* We quit the connection with little timeout */
621 silc_task_register(cmd->client->timeout_queue, cmd->conn->sock->sock,
622 silc_client_command_quit_cb, (void *)q,
623 1, 0, SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
625 /* Notify application */
629 silc_client_command_free(cmd);
632 /* Command KILL. Router operator can use this command to remove an client
633 fromthe SILC Network. */
635 SILC_CLIENT_CMD_FUNC(kill)
637 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
638 SilcClientConnection conn = cmd->conn;
639 SilcBuffer buffer, idp;
640 SilcClientEntry target;
641 unsigned int num = 0;
642 char *nickname = NULL, *server = NULL;
645 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
651 cmd->client->ops->say(cmd->client, conn,
652 "Usage: /KILL <nickname> [<comment>]");
657 /* Parse the typed nickname. */
658 if (!silc_parse_nickname(cmd->argv[1], &nickname, &server, &num)) {
659 cmd->client->ops->say(cmd->client, conn, "Bad nickname");
664 /* Get the target client */
665 target = silc_idlist_get_client(cmd->client, conn, nickname,
672 /* Client entry not found, it was requested thus mark this to be
674 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY,
676 silc_client_command_destructor,
677 silc_client_command_kill,
678 silc_client_command_dup(cmd));
683 /* Send the KILL command to the server */
684 idp = silc_id_payload_encode(target->id, SILC_ID_CLIENT);
686 buffer = silc_command_payload_encode_va(SILC_COMMAND_KILL, 0, 1,
687 1, idp->data, idp->len);
689 buffer = silc_command_payload_encode_va(SILC_COMMAND_KILL, 0, 2,
690 1, idp->data, idp->len,
692 strlen(cmd->argv[2]));
693 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
694 0, NULL, NULL, buffer->data, buffer->len, TRUE);
695 silc_buffer_free(buffer);
696 silc_buffer_free(idp);
698 /* Notify application */
701 /* Remove the client entry to be killed */
702 silc_idcache_del_by_id(conn->client_cache, SILC_ID_CLIENT,
704 if (target->nickname)
705 silc_free(target->nickname);
707 silc_free(target->server);
709 silc_free(target->id);
710 if (target->send_key)
711 silc_cipher_free(target->send_key);
712 if (target->receive_key)
713 silc_cipher_free(target->receive_key);
721 silc_client_command_free(cmd);
724 /* Command INFO. Request information about specific server. If specific
725 server is not provided the current server is used. */
727 SILC_CLIENT_CMD_FUNC(info)
729 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
730 SilcClientConnection conn = cmd->conn;
735 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
741 name = strdup(conn->remote_host);
743 name = strdup(cmd->argv[1]);
745 /* Send the command */
746 buffer = silc_command_payload_encode_va(SILC_COMMAND_INFO, 0, 1,
747 1, name, strlen(name));
748 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
749 0, NULL, NULL, buffer->data, buffer->len, TRUE);
750 silc_buffer_free(buffer);
752 /* Notify application */
756 silc_client_command_free(cmd);
759 /* Command PING. Sends ping to server. This is used to test the
760 communication channel. */
762 SILC_CLIENT_CMD_FUNC(ping)
764 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
765 SilcClientConnection conn = cmd->conn;
772 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
777 if (cmd->argc == 1 || !strcmp(cmd->argv[1], conn->remote_host))
778 name = strdup(conn->remote_host);
780 /* Send the command */
781 buffer = silc_command_payload_encode_va(SILC_COMMAND_PING, 0, 1,
782 1, conn->remote_id_data,
784 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
785 0, NULL, NULL, buffer->data, buffer->len, TRUE);
786 silc_buffer_free(buffer);
788 id = silc_id_str2id(conn->remote_id_data, conn->remote_id_data_len,
791 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
796 /* Start counting time */
797 for (i = 0; i < conn->ping_count; i++) {
798 if (conn->ping[i].dest_id == NULL) {
799 conn->ping[i].start_time = time(NULL);
800 conn->ping[i].dest_id = id;
801 conn->ping[i].dest_name = name;
806 if (i >= conn->ping_count) {
807 i = conn->ping_count;
808 conn->ping = silc_realloc(conn->ping, sizeof(*conn->ping) * (i + 1));
809 conn->ping[i].start_time = time(NULL);
810 conn->ping[i].dest_id = id;
811 conn->ping[i].dest_name = name;
815 /* Notify application */
819 silc_client_command_free(cmd);
822 SILC_CLIENT_CMD_FUNC(notice)
826 /* Command JOIN. Joins to a channel. */
828 SILC_CLIENT_CMD_FUNC(join)
830 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
831 SilcClientConnection conn = cmd->conn;
832 SilcIDCacheEntry id_cache = NULL;
833 SilcBuffer buffer, idp;
836 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
842 /* Show channels currently joined to */
847 /* See if we have joined to the requested channel already */
848 if (silc_idcache_find_by_data_one(conn->channel_cache, cmd->argv[1],
850 cmd->client->ops->say(cmd->client, conn,
851 "You are talking to channel %s", cmd->argv[1]);
852 conn->current_channel = (SilcChannelEntry)id_cache->context;
854 cmd->client->screen->bottom_line->channel = cmd->argv[1];
855 silc_screen_print_bottom_line(cmd->client->screen, 0);
860 idp = silc_id_payload_encode(conn->local_id, SILC_ID_CLIENT);
862 /* Send JOIN command to the server */
865 silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 2,
866 1, cmd->argv[1], cmd->argv_lens[1],
867 2, idp->data, idp->len);
868 else if (cmd->argc == 3)
871 silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 3,
872 1, cmd->argv[1], cmd->argv_lens[1],
873 2, idp->data, idp->len,
874 3, cmd->argv[2], cmd->argv_lens[2]);
877 silc_command_payload_encode_va(SILC_COMMAND_JOIN, 0, 4,
878 1, cmd->argv[1], cmd->argv_lens[1],
879 2, idp->data, idp->len,
880 3, cmd->argv[2], cmd->argv_lens[2],
881 4, cmd->argv[3], cmd->argv_lens[3]);
883 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
884 0, NULL, NULL, buffer->data, buffer->len, TRUE);
885 silc_buffer_free(buffer);
886 silc_buffer_free(idp);
888 /* Notify application */
892 silc_client_command_free(cmd);
895 /* MOTD command. Requests motd from server. */
897 SILC_CLIENT_CMD_FUNC(motd)
899 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
900 SilcClientConnection conn = cmd->conn;
904 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
909 if (cmd->argc < 1 || cmd->argc > 1) {
910 cmd->client->ops->say(cmd->client, conn,
916 /* Send TOPIC command to the server */
917 buffer = silc_command_payload_encode_va(SILC_COMMAND_MOTD, 0, 1,
918 2, conn->remote_host,
919 strlen(conn->remote_host));
920 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
921 0, NULL, NULL, buffer->data, buffer->len, TRUE);
922 silc_buffer_free(buffer);
924 /* Notify application */
928 silc_client_command_free(cmd);
931 /* UMODE. Set user mode in SILC. */
933 SILC_CLIENT_CMD_FUNC(umode)
938 /* CMODE command. Sets channel mode. Modes that does not require any arguments
939 can be set several at once. Those modes that require argument must be set
940 separately (unless set with modes that does not require arguments). */
942 SILC_CLIENT_CMD_FUNC(cmode)
944 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
945 SilcClientConnection conn = cmd->conn;
946 SilcChannelEntry channel;
947 SilcBuffer buffer, chidp;
948 unsigned char *name, *cp, modebuf[4], tmp[4], *arg = NULL;
949 unsigned int mode, add, type, len, arg_len = 0;
953 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
959 cmd->client->ops->say(cmd->client, conn,
960 "Usage: /CMODE <channel> +|-<modes> [{ <arguments>}]");
965 if (cmd->argv[1][0] == '*') {
966 if (!conn->current_channel) {
967 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
972 channel = conn->current_channel;
976 channel = silc_client_get_channel(cmd->client, conn, name);
978 cmd->client->ops->say(cmd->client, conn, "You are on that channel");
984 mode = channel->mode;
986 /* Are we adding or removing mode */
987 if (cmd->argv[2][0] == '-')
992 /* Argument type to be sent to server */
996 cp = cmd->argv[2] + 1;
998 for (i = 0; i < len; i++) {
1002 mode |= SILC_CHANNEL_MODE_PRIVATE;
1004 mode &= ~SILC_CHANNEL_MODE_PRIVATE;
1008 mode |= SILC_CHANNEL_MODE_SECRET;
1010 mode &= ~SILC_CHANNEL_MODE_SECRET;
1014 mode |= SILC_CHANNEL_MODE_PRIVKEY;
1016 mode &= ~SILC_CHANNEL_MODE_PRIVKEY;
1020 mode |= SILC_CHANNEL_MODE_INVITE;
1022 mode &= ~SILC_CHANNEL_MODE_INVITE;
1026 mode |= SILC_CHANNEL_MODE_TOPIC;
1028 mode &= ~SILC_CHANNEL_MODE_TOPIC;
1033 mode |= SILC_CHANNEL_MODE_ULIMIT;
1035 ll = atoi(cmd->argv[3]);
1036 SILC_PUT32_MSB(ll, tmp);
1040 mode &= ~SILC_CHANNEL_MODE_ULIMIT;
1045 mode |= SILC_CHANNEL_MODE_PASSPHRASE;
1048 arg_len = cmd->argv_lens[3];
1050 mode &= ~SILC_CHANNEL_MODE_PASSPHRASE;
1055 mode |= SILC_CHANNEL_MODE_BAN;
1058 arg_len = cmd->argv_lens[3];
1060 mode &= ~SILC_CHANNEL_MODE_BAN;
1065 mode |= SILC_CHANNEL_MODE_INVITE_LIST;
1068 arg_len = cmd->argv_lens[3];
1070 mode &= ~SILC_CHANNEL_MODE_INVITE_LIST;
1075 mode |= SILC_CHANNEL_MODE_CIPHER;
1078 arg_len = cmd->argv_lens[3];
1080 mode &= ~SILC_CHANNEL_MODE_CIPHER;
1090 if (type && cmd->argc < 3) {
1095 chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1096 SILC_PUT32_MSB(mode, modebuf);
1098 /* Send the command packet. We support sending only one mode at once
1099 that requires an argument. */
1102 silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 3,
1103 1, chidp->data, chidp->len,
1104 2, modebuf, sizeof(modebuf),
1105 type, arg, arg_len);
1108 silc_command_payload_encode_va(SILC_COMMAND_CMODE, 0, 2,
1109 1, chidp->data, chidp->len,
1110 2, modebuf, sizeof(modebuf));
1113 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1114 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1115 silc_buffer_free(buffer);
1116 silc_buffer_free(chidp);
1118 /* Notify application */
1122 silc_client_command_free(cmd);
1125 /* CUMODE command. Changes client's mode on a channel. */
1127 SILC_CLIENT_CMD_FUNC(cumode)
1129 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1130 SilcClientConnection conn = cmd->conn;
1131 SilcChannelEntry channel;
1132 SilcChannelUser chu;
1133 SilcClientEntry client_entry;
1134 SilcBuffer buffer, clidp, chidp;
1135 unsigned char *name, *cp, modebuf[4];
1136 unsigned int mode = 0, add, len;
1137 char *nickname = NULL, *server = NULL;
1138 unsigned int num = 0;
1142 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1147 if (cmd->argc < 4) {
1148 cmd->client->ops->say(cmd->client, conn,
1149 "Usage: /CUMODE <channel> +|-<modes> <nickname>[@<server>]");
1154 if (cmd->argv[1][0] == '*') {
1155 if (!conn->current_channel) {
1156 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1161 channel = conn->current_channel;
1163 name = cmd->argv[1];
1165 channel = silc_client_get_channel(cmd->client, conn, name);
1167 cmd->client->ops->say(cmd->client, conn, "You are on that channel");
1173 /* Parse the typed nickname. */
1174 if (!silc_parse_nickname(cmd->argv[3], &nickname, &server, &num)) {
1175 cmd->client->ops->say(cmd->client, conn, "Bad nickname");
1180 /* Find client entry */
1181 client_entry = silc_idlist_get_client(cmd->client, conn,
1182 nickname, server, num, TRUE);
1183 if (!client_entry) {
1184 /* Client entry not found, it was requested thus mark this to be
1186 silc_client_command_pending(conn, SILC_COMMAND_IDENTIFY,
1188 silc_client_command_destructor,
1189 silc_client_command_cumode,
1190 silc_client_command_dup(cmd));
1195 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1196 if (chu->client == client_entry) {
1202 /* Are we adding or removing mode */
1203 if (cmd->argv[2][0] == '-')
1209 cp = cmd->argv[2] + 1;
1211 for (i = 0; i < len; i++) {
1215 mode |= SILC_CHANNEL_UMODE_CHANFO;
1216 mode |= SILC_CHANNEL_UMODE_CHANOP;
1218 mode = SILC_CHANNEL_UMODE_NONE;
1223 mode |= SILC_CHANNEL_UMODE_CHANFO;
1225 mode &= ~SILC_CHANNEL_UMODE_CHANFO;
1229 mode |= SILC_CHANNEL_UMODE_CHANOP;
1231 mode &= ~SILC_CHANNEL_UMODE_CHANOP;
1240 chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
1241 SILC_PUT32_MSB(mode, modebuf);
1242 clidp = silc_id_payload_encode(client_entry->id, SILC_ID_CLIENT);
1244 /* Send the command packet. We support sending only one mode at once
1245 that requires an argument. */
1246 buffer = silc_command_payload_encode_va(SILC_COMMAND_CUMODE, 0, 3,
1247 1, chidp->data, chidp->len,
1249 3, clidp->data, clidp->len);
1251 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1252 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1253 silc_buffer_free(buffer);
1254 silc_buffer_free(chidp);
1255 silc_buffer_free(clidp);
1257 /* Notify application */
1262 silc_free(nickname);
1265 silc_client_command_free(cmd);
1268 /* KICK command. Kicks a client out of channel. */
1270 SILC_CLIENT_CMD_FUNC(kick)
1272 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1273 SilcClientConnection conn = cmd->conn;
1274 SilcIDCacheEntry id_cache = NULL;
1275 SilcChannelEntry channel;
1276 SilcBuffer buffer, idp, idp2;
1277 SilcClientEntry target;
1279 unsigned int num = 0;
1280 char *nickname = NULL, *server = NULL;
1283 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1288 if (cmd->argc < 3) {
1289 cmd->client->ops->say(cmd->client, conn,
1290 "Usage: /KICK <channel> <nickname> [<comment>]");
1295 if (cmd->argv[1][0] == '*') {
1296 if (!conn->current_channel) {
1297 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1301 name = conn->current_channel->channel_name;
1303 name = cmd->argv[1];
1306 if (!conn->current_channel) {
1307 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1312 /* Get the Channel ID of the channel */
1313 if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
1314 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1319 channel = (SilcChannelEntry)id_cache->context;
1321 /* Parse the typed nickname. */
1322 if (!silc_parse_nickname(cmd->argv[2], &nickname, &server, &num)) {
1323 cmd->client->ops->say(cmd->client, conn, "Bad nickname");
1328 /* Get the target client */
1329 target = silc_idlist_get_client(cmd->client, conn, nickname,
1330 server, num, FALSE);
1332 cmd->client->ops->say(cmd->client, conn, "No such client: %s",
1338 /* Send KICK command to the server */
1339 idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1340 idp2 = silc_id_payload_encode(target->id, SILC_ID_CLIENT);
1342 buffer = silc_command_payload_encode_va(SILC_COMMAND_KICK, 0, 2,
1343 1, idp->data, idp->len,
1344 2, idp2->data, idp2->len);
1346 buffer = silc_command_payload_encode_va(SILC_COMMAND_KICK, 0, 3,
1347 1, idp->data, idp->len,
1348 2, idp2->data, idp2->len,
1350 strlen(cmd->argv[3]));
1351 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1352 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1353 silc_buffer_free(buffer);
1354 silc_buffer_free(idp);
1355 silc_buffer_free(idp2);
1357 /* Notify application */
1362 silc_free(nickname);
1365 silc_client_command_free(cmd);
1368 /* OPER command. Used to obtain server operator privileges. */
1370 SILC_CLIENT_CMD_FUNC(oper)
1372 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1373 SilcClientConnection conn = cmd->conn;
1375 unsigned char *auth_data;
1379 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1384 if (cmd->argc < 2) {
1385 cmd->client->ops->say(cmd->client, conn,
1386 "Usage: /OPER <username> [<public key>]");
1391 if (cmd->argc == 3) {
1392 /* XXX Get public key */
1397 /* Get passphrase */
1399 auth_data = cmd->client->ops->ask_passphrase(cmd->client, conn);
1405 /* Encode the authentication payload */
1406 auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
1407 auth_data, strlen(auth_data));
1410 buffer = silc_command_payload_encode_va(SILC_COMMAND_OPER, 0, 2,
1412 strlen(cmd->argv[1]),
1413 2, auth->data, auth->len);
1414 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1415 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1417 silc_buffer_free(buffer);
1418 silc_buffer_free(auth);
1419 memset(auth_data, 0, strlen(auth_data));
1420 silc_free(auth_data);
1422 /* Notify application */
1426 silc_client_command_free(cmd);
1429 /* SILCOPER command. Used to obtain router operator privileges. */
1431 SILC_CLIENT_CMD_FUNC(silcoper)
1433 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1434 SilcClientConnection conn = cmd->conn;
1436 unsigned char *auth_data;
1440 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1445 if (cmd->argc < 2) {
1446 cmd->client->ops->say(cmd->client, conn,
1447 "Usage: /SILCOPER <username> [<public key>]");
1452 if (cmd->argc == 3) {
1453 /* XXX Get public key */
1458 /* Get passphrase */
1460 auth_data = cmd->client->ops->ask_passphrase(cmd->client, conn);
1466 /* Encode the authentication payload */
1467 auth = silc_auth_payload_encode(SILC_AUTH_PASSWORD, NULL, 0,
1468 auth_data, strlen(auth_data));
1471 buffer = silc_command_payload_encode_va(SILC_COMMAND_SILCOPER, 0, 2,
1473 strlen(cmd->argv[1]),
1474 2, auth->data, auth->len);
1475 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1476 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1478 silc_buffer_free(buffer);
1479 silc_buffer_free(auth);
1480 memset(auth_data, 0, strlen(auth_data));
1481 silc_free(auth_data);
1483 /* Notify application */
1487 silc_client_command_free(cmd);
1490 /* CONNECT command. Connects the server to another server. */
1492 SILC_CLIENT_CMD_FUNC(connect)
1494 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1495 SilcClientConnection conn = cmd->conn;
1497 unsigned char port[4];
1501 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1506 if (cmd->argc < 2) {
1507 cmd->client->ops->say(cmd->client, conn,
1508 "Usage: /CONNECT <server> [<port>]");
1513 if (cmd->argc == 3) {
1514 tmp = atoi(cmd->argv[2]);
1515 SILC_PUT32_MSB(tmp, port);
1519 buffer = silc_command_payload_encode_va(SILC_COMMAND_CONNECT, 0, 2,
1521 strlen(cmd->argv[1]),
1524 buffer = silc_command_payload_encode_va(SILC_COMMAND_CONNECT, 0, 1,
1526 strlen(cmd->argv[1]));
1527 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1528 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1529 silc_buffer_free(buffer);
1531 /* Notify application */
1535 silc_client_command_free(cmd);
1538 SILC_CLIENT_CMD_FUNC(restart)
1542 /* CLOSE command. Close server connection to the remote server */
1544 SILC_CLIENT_CMD_FUNC(close)
1546 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1547 SilcClientConnection conn = cmd->conn;
1549 unsigned char port[4];
1553 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1558 if (cmd->argc < 2) {
1559 cmd->client->ops->say(cmd->client, conn,
1560 "Usage: /CLOSE <server> [<port>]");
1565 if (cmd->argc == 3) {
1566 tmp = atoi(cmd->argv[2]);
1567 SILC_PUT32_MSB(tmp, port);
1571 buffer = silc_command_payload_encode_va(SILC_COMMAND_CLOSE, 0, 2,
1573 strlen(cmd->argv[1]),
1576 buffer = silc_command_payload_encode_va(SILC_COMMAND_CLOSE, 0, 1,
1578 strlen(cmd->argv[1]));
1579 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1580 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1581 silc_buffer_free(buffer);
1583 /* Notify application */
1587 silc_client_command_free(cmd);
1590 /* SHUTDOWN command. Shutdowns the server. */
1592 SILC_CLIENT_CMD_FUNC(shutdown)
1594 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1597 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1602 /* Send the command */
1603 silc_client_send_command(cmd->client, cmd->conn,
1604 SILC_COMMAND_SHUTDOWN, 0, 0);
1606 /* Notify application */
1610 silc_client_command_free(cmd);
1613 /* LEAVE command. Leaves a channel. Client removes itself from a channel. */
1615 SILC_CLIENT_CMD_FUNC(leave)
1617 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1618 SilcClientConnection conn = cmd->conn;
1619 SilcIDCacheEntry id_cache = NULL;
1620 SilcChannelEntry channel;
1621 SilcBuffer buffer, idp;
1625 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1630 if (cmd->argc != 2) {
1631 cmd->client->ops->say(cmd->client, conn, "Usage: /LEAVE <channel>");
1636 if (cmd->argv[1][0] == '*') {
1637 if (!conn->current_channel) {
1638 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1642 name = conn->current_channel->channel_name;
1644 name = cmd->argv[1];
1647 if (!conn->current_channel) {
1648 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1653 /* Get the Channel ID of the channel */
1654 if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
1655 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1660 channel = (SilcChannelEntry)id_cache->context;
1662 /* Send LEAVE command to the server */
1663 idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1664 buffer = silc_command_payload_encode_va(SILC_COMMAND_LEAVE, 0, 1,
1665 1, idp->data, idp->len);
1666 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND, NULL,
1667 0, NULL, NULL, buffer->data, buffer->len, TRUE);
1668 silc_buffer_free(buffer);
1669 silc_buffer_free(idp);
1671 /* We won't talk anymore on this channel */
1672 cmd->client->ops->say(cmd->client, conn, "You have left channel %s", name);
1674 conn->current_channel = NULL;
1676 silc_idcache_del_by_id(conn->channel_cache, SILC_ID_CHANNEL, channel->id);
1677 silc_free(channel->channel_name);
1678 silc_free(channel->id);
1679 silc_free(channel->key);
1680 silc_cipher_free(channel->channel_key);
1683 /* Notify application */
1687 silc_client_command_free(cmd);
1690 /* Command USERS. Requests the USERS of the clients joined on requested
1693 SILC_CLIENT_CMD_FUNC(users)
1695 SilcClientCommandContext cmd = (SilcClientCommandContext)context;
1696 SilcClientConnection conn = cmd->conn;
1697 SilcIDCacheEntry id_cache = NULL;
1698 SilcChannelEntry channel;
1699 SilcBuffer buffer, idp;
1700 char *name, *line = NULL;
1701 unsigned int line_len = 0;
1704 SILC_NOT_CONNECTED(cmd->client, cmd->conn);
1709 if (cmd->argc != 2) {
1710 cmd->client->ops->say(cmd->client, conn, "Usage: /USERS <channel>");
1715 if (cmd->argv[1][0] == '*') {
1716 if (!conn->current_channel) {
1717 cmd->client->ops->say(cmd->client, conn, "You are not on any channel");
1721 name = conn->current_channel->channel_name;
1723 name = cmd->argv[1];
1726 if (!conn->current_channel) {
1727 cmd->client->ops->say(cmd->client, conn, "You are not on that channel");
1732 /* Get the Channel ID of the channel */
1733 if (!silc_idcache_find_by_data_one(conn->channel_cache, name, &id_cache)) {
1734 /* XXX should resolve the channel ID; LIST command */
1735 cmd->client->ops->say(cmd->client, conn,
1736 "You are not on that channel", name);
1741 channel = (SilcChannelEntry)id_cache->context;
1743 if (!cmd->pending) {
1744 /* Send USERS command to the server */
1745 idp = silc_id_payload_encode(id_cache->id, SILC_ID_CHANNEL);
1746 buffer = silc_command_payload_encode_va(SILC_COMMAND_USERS,
1747 ++conn->cmd_ident, 1,
1748 1, idp->data, idp->len);
1749 silc_client_packet_send(cmd->client, conn->sock, SILC_PACKET_COMMAND,
1750 NULL, 0, NULL, NULL, buffer->data,
1752 silc_buffer_free(buffer);
1753 silc_buffer_free(idp);
1755 /* Register pending callback which will recall this command callback with
1756 same context and reprocesses the command. When reprocessing we actually
1757 display the information on the screen. */
1758 silc_client_command_pending(conn, SILC_COMMAND_USERS, conn->cmd_ident,
1759 silc_client_command_destructor,
1760 silc_client_command_users,
1761 silc_client_command_dup(cmd));
1762 cmd->pending = TRUE;
1767 /* Pending command. Now we've resolved the information from server and
1768 we are ready to display the information on screen. */
1770 SilcChannelUser chu;
1772 cmd->client->ops->say(cmd->client, conn, "Users on %s",
1773 channel->channel_name);
1775 line = silc_calloc(4096, sizeof(*line));
1777 silc_list_start(channel->clients);
1778 while ((chu = silc_list_get(channel->clients)) != SILC_LIST_END) {
1779 SilcClientEntry e = chu->client;
1780 char *m, tmp[80], len1;
1782 memset(line, 0, sizeof(line_len));
1784 if (strlen(e->nickname) + strlen(e->server) + 100 > line_len) {
1786 line_len += strlen(e->nickname) + strlen(e->server) + 100;
1787 line = silc_calloc(line_len, sizeof(*line));
1790 memset(tmp, 0, sizeof(tmp));
1791 m = silc_client_chumode_char(chu->mode);
1793 strncat(line, " ", 1);
1794 strncat(line, e->nickname, strlen(e->nickname));
1795 strncat(line, e->server ? "@" : "", 1);
1799 len1 = strlen(e->server);
1800 strncat(line, e->server ? e->server : "", len1 > 30 ? 30 : len1);
1802 len1 = strlen(line);
1804 memset(&line[29], 0, len1 - 29);
1806 for (i = 0; i < 30 - len1 - 1; i++)
1810 strncat(line, " H", 3);
1811 strcat(tmp, m ? m : "");
1812 strncat(line, tmp, strlen(tmp));
1814 if (strlen(tmp) < 5)
1815 for (i = 0; i < 5 - strlen(tmp); i++)
1818 strcat(line, e->username ? e->username : "");
1820 cmd->client->ops->say(cmd->client, conn, "%s", line);
1830 /* Notify application */
1834 silc_client_command_free(cmd);