pidgin/pidgin

closing merged branch
port-changes-from-branch-2.x.y-to-default
2020-02-03, Gary Kramlich
2f836435c33c
closing merged branch
/**
* purple
*
* Copyright (C) 2003, Ethan Blanton <eblanton@cs.purdue.edu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
#include "internal.h"
#include <purple.h>
#include "irc.h"
static void irc_do_mode(struct irc_conn *irc, const char *target, const char *sign, char **ops);
int irc_cmd_default(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
{
PurpleConversation *convo = purple_conversations_find_with_account(target, irc->account);
char *buf;
if (!convo)
return 1;
buf = g_strdup_printf(_("Unknown command: %s"), cmd);
purple_conversation_write_system_message(convo, buf, PURPLE_MESSAGE_NO_LOG);
g_free(buf);
return 1;
}
int irc_cmd_away(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
{
char *buf, *message;
if (args[0] && !purple_strequal(cmd, "back")) {
message = purple_markup_strip_html(args[0]);
purple_util_chrreplace(message, '\n', ' ');
buf = irc_format(irc, "v:", "AWAY", message);
g_free(message);
} else {
buf = irc_format(irc, "v", "AWAY");
}
irc_send(irc, buf);
g_free(buf);
return 0;
}
int irc_cmd_ctcp(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
{
/* we have defined args as args[0] is target and args[1] is ctcp command */
char *buf;
GString *string;
/* check if we have args */
if (!args || !args[0] || !args[1])
return 0;
/* TODO:strip newlines or send each line as separate ctcp or something
* actually, this shouldn't be done here but somewhere else since irc should support escaping newlines */
string = g_string_new(args[1]);
g_string_prepend_c (string,'\001');
g_string_append_c (string,'\001');
buf = irc_format(irc, "vn:", "PRIVMSG", args[0], string->str);
g_string_free(string,TRUE);
irc_send(irc, buf);
g_free(buf);
return 1;
}
int irc_cmd_ctcp_action(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
{
PurpleConnection *gc = purple_account_get_connection(irc->account);
char *action, *escaped, *dst, **newargs;
const char *src;
char *msg;
PurpleConversation *convo;
PurpleMessage *pmsg;
if (!args || !args[0] || !gc)
return 0;
convo = purple_conversations_find_with_account(target, irc->account);
msg = g_strdup_printf("/me %s", args[0]);
/* XXX: we'd prefer to keep this in conversation.c */
if (PURPLE_IS_IM_CONVERSATION(convo)) {
pmsg = purple_message_new_outgoing(
purple_conversation_get_name(convo), msg, 0);
purple_signal_emit(purple_conversations_get_handle(),
"sending-im-msg", irc->account, pmsg);
} else {
pmsg = purple_message_new_outgoing(NULL, msg, 0);
purple_signal_emit(purple_conversations_get_handle(),
"sending-chat-msg", irc->account, pmsg,
purple_chat_conversation_get_id(PURPLE_CHAT_CONVERSATION(convo)));
}
g_free(msg);
if (purple_message_is_empty(pmsg))
return 0;
msg = g_strdup(purple_message_get_contents(pmsg)); /* XXX: is it really necessary? */
if (strncmp(msg, "/me ", 4) != 0) {
newargs = g_new0(char *, 2);
newargs[0] = g_strdup(target);
newargs[1] = msg;
irc_cmd_privmsg(irc, cmd, target, (const char **)newargs);
g_free(newargs[0]);
g_free(newargs);
} else {
action = g_malloc(strlen(&msg[4]) + 10);
sprintf(action, "\001ACTION ");
src = &msg[4];
dst = action + 8;
while (*src) {
if (*src == '\n') {
if (*(src + 1) == '\0') {
break;
} else {
*dst++ = ' ';
src++;
continue;
}
}
*dst++ = *src++;
}
*dst++ = '\001';
*dst = '\0';
newargs = g_new0(char *, 2);
newargs[0] = g_strdup(target);
newargs[1] = action;
irc_cmd_privmsg(irc, cmd, target, (const char **)newargs);
g_free(newargs[0]);
g_free(newargs);
g_free(action);
}
/* XXX: we'd prefer to keep this in conversation.c */
if (PURPLE_IS_IM_CONVERSATION(convo)) {
purple_signal_emit(purple_conversations_get_handle(),
"sent-im-msg", irc->account, pmsg);
} else {
purple_signal_emit(purple_conversations_get_handle(),
"sent-chat-msg", irc->account, pmsg,
purple_chat_conversation_get_id(PURPLE_CHAT_CONVERSATION(convo)));
}
g_free(msg);
if (convo) {
escaped = g_markup_escape_text(args[0], -1);
action = g_strdup_printf("/me %s", escaped);
g_free(escaped);
if (action[strlen(action) - 1] == '\n')
action[strlen(action) - 1] = '\0';
if (PURPLE_IS_CHAT_CONVERSATION(convo))
purple_serv_got_chat_in(gc, purple_chat_conversation_get_id(PURPLE_CHAT_CONVERSATION(convo)),
purple_connection_get_display_name(gc),
PURPLE_MESSAGE_SEND, action, time(NULL));
else
purple_conversation_write_message(convo, purple_message_new_outgoing(
purple_connection_get_display_name(gc), action, 0));
g_free(action);
}
return 1;
}
int irc_cmd_ctcp_version(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
{
char *buf;
if (!args || !args[0])
return 0;
buf = irc_format(irc, "vn:", "PRIVMSG", args[0], "\001VERSION\001");
irc_send(irc, buf);
g_free(buf);
return 0;
}
int irc_cmd_invite(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
{
char *buf;
if (!args || !args[0] || !(args[1] || target))
return 0;
buf = irc_format(irc, "vnc", "INVITE", args[0], args[1] ? args[1] : target);
irc_send(irc, buf);
g_free(buf);
return 0;
}
int irc_cmd_join(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
{
char *buf;
if (!args || !args[0])
return 0;
if (args[1])
buf = irc_format(irc, "vcv", "JOIN", args[0], args[1]);
else
buf = irc_format(irc, "vc", "JOIN", args[0]);
irc_send(irc, buf);
g_free(buf);
return 0;
}
int irc_cmd_kick(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
{
char *buf;
if (!args || !args[0])
return 0;
if (!purple_conversations_find_chat_with_account(target, irc->account))
return 0;
if (args[1])
buf = irc_format(irc, "vcn:", "KICK", target, args[0], args[1]);
else
buf = irc_format(irc, "vcn", "KICK", target, args[0]);
irc_send(irc, buf);
g_free(buf);
return 0;
}
int irc_cmd_list(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
{
purple_roomlist_show_with_account(irc->account);
return 0;
}
int irc_cmd_mode(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
{
PurpleConnection *gc;
char *buf;
if (!args)
return 0;
if (purple_strequal(cmd, "mode")) {
if (!args[0] && irc_ischannel(target))
buf = irc_format(irc, "vc", "MODE", target);
else if (args[0] && (*args[0] == '+' || *args[0] == '-'))
buf = irc_format(irc, "vcn", "MODE", target, args[0]);
else if (args[0])
buf = irc_format(irc, "vn", "MODE", args[0]);
else
return 0;
} else if (purple_strequal(cmd, "umode")) {
if (!args[0])
return 0;
gc = purple_account_get_connection(irc->account);
buf = irc_format(irc, "vnc", "MODE", purple_connection_get_display_name(gc), args[0]);
} else {
return 0;
}
irc_send(irc, buf);
g_free(buf);
return 0;
}
int irc_cmd_names(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
{
char *buf;
if (!args || (!args[0] && !irc_ischannel(target)))
return 0;
buf = irc_format(irc, "vc", "NAMES", args[0] ? args[0] : target);
irc_send(irc, buf);
g_free(buf);
return 0;
}
int irc_cmd_nick(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
{
char *buf;
if (!args || !args[0])
return 0;
buf = irc_format(irc, "v:", "NICK", args[0]);
g_free(irc->reqnick);
irc->reqnick = g_strdup(args[0]);
irc->nickused = FALSE;
irc_send(irc, buf);
g_free(buf);
return 0;
}
int irc_cmd_op(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
{
char **nicks, **ops, *sign, *mode;
int i = 0, used = 0;
if (!args || !args[0] || !*args[0])
return 0;
if (purple_strequal(cmd, "op")) {
sign = "+";
mode = "o";
} else if (purple_strequal(cmd, "deop")) {
sign = "-";
mode = "o";
} else if (purple_strequal(cmd, "voice")) {
sign = "+";
mode = "v";
} else if (purple_strequal(cmd, "devoice")) {
sign = "-";
mode = "v";
} else {
purple_debug(PURPLE_DEBUG_ERROR, "irc", "invalid 'op' command '%s'\n", cmd);
return 0;
}
nicks = g_strsplit(args[0], " ", -1);
for (i = 0; nicks[i]; i++)
/* nothing */;
ops = g_new0(char *, i * 2 + 1);
for (i = 0; nicks[i]; i++) {
if (*nicks[i]) {
ops[used++] = mode;
ops[used++] = nicks[i];
}
}
irc_do_mode(irc, target, sign, ops);
g_free(ops);
g_strfreev(nicks);
return 0;
}
int irc_cmd_part(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
{
char *buf;
if (!args)
return 0;
if (args[1])
buf = irc_format(irc, "vc:", "PART", args[0] ? args[0] : target, args[1]);
else
buf = irc_format(irc, "vc", "PART", args[0] ? args[0] : target);
irc_send(irc, buf);
g_free(buf);
return 0;
}
int irc_cmd_ping(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
{
char *stamp;
char *buf;
if (args && args[0]) {
if (irc_ischannel(args[0]))
return 0;
stamp = g_strdup_printf("\001PING %" G_GINT64_FORMAT "\001",
g_get_monotonic_time());
buf = irc_format(irc, "vn:", "PRIVMSG", args[0], stamp);
g_free(stamp);
} else if (target) {
stamp = g_strdup_printf("%s %" G_GINT64_FORMAT, target,
g_get_monotonic_time());
buf = irc_format(irc, "v:", "PING", stamp);
g_free(stamp);
} else {
stamp = g_strdup_printf("%lu", time(NULL));
buf = irc_format(irc, "vv", "PING", stamp);
g_free(stamp);
}
irc_send(irc, buf);
g_free(buf);
return 0;
}
int irc_cmd_privmsg(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
{
int max_privmsg_arg_len;
const char *cur, *end;
gchar *salvaged;
char *msg, *buf;
if (!args || !args[0] || !args[1])
return 0;
max_privmsg_arg_len = IRC_MAX_MSG_SIZE - strlen(args[0]) - 64;
salvaged = purple_utf8_salvage(args[1]);
cur = salvaged;
end = salvaged;
while (*end && *cur) {
end = strchr(cur, '\n');
if (!end)
end = cur + strlen(cur);
if (end - cur > max_privmsg_arg_len) {
/* this call is used to find the last valid character position in the first
* max_privmsg_arg_len bytes of the utf-8 message
*/
g_utf8_validate(cur, max_privmsg_arg_len, &end);
}
msg = g_strndup(cur, end - cur);
if(purple_strequal(cmd, "notice"))
buf = irc_format(irc, "vt:", "NOTICE", args[0], msg);
else
buf = irc_format(irc, "vt:", "PRIVMSG", args[0], msg);
irc_send(irc, buf);
g_free(msg);
g_free(buf);
cur = end;
if(*cur == '\n') {
cur++;
}
}
g_free(salvaged);
return 0;
}
int irc_cmd_quit(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
{
char *buf;
if (!irc->quitting) {
/*
* Use purple_account_get_string(irc->account, "quitmsg", IRC_DEFAULT_QUIT)
* and uncomment the appropriate account preference in irc.c if we
* decide we want custom quit messages.
*/
buf = irc_format(irc, "v:", "QUIT", (args && args[0]) ? args[0] : IRC_DEFAULT_QUIT);
irc_send(irc, buf);
g_free(buf);
irc->quitting = TRUE;
if (!purple_account_is_disconnecting(irc->account))
purple_account_set_status(irc->account, "offline", TRUE, NULL);
}
return 0;
}
int irc_cmd_quote(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
{
char *buf;
if (!args || !args[0])
return 0;
buf = irc_format(irc, "n", args[0]);
irc_send(irc, buf);
g_free(buf);
return 0;
}
int irc_cmd_query(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
{
PurpleIMConversation *im;
PurpleConnection *gc;
if (!args || !args[0])
return 0;
im = purple_im_conversation_new(irc->account, args[0]);
purple_conversation_present(PURPLE_CONVERSATION(im));
if (args[1]) {
gc = purple_account_get_connection(irc->account);
irc_cmd_privmsg(irc, cmd, target, args);
purple_conversation_write_message(PURPLE_CONVERSATION(im),
purple_message_new_outgoing(
purple_connection_get_display_name(gc), args[1], 0));
}
return 0;
}
int irc_cmd_remove(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
{
char *buf;
if (!args || !args[0])
return 0;
if (!irc_ischannel(target)) /* not a channel, punt */
return 0;
if (args[1])
buf = irc_format(irc, "vcn:", "REMOVE", target, args[0], args[1]);
else
buf = irc_format(irc, "vcn", "REMOVE", target, args[0]);
irc_send(irc, buf);
g_free(buf);
return 0;
}
int irc_cmd_service(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
{
char *capital_cmd, *buf;
if (!args || !args[0])
return 0;
/* cmd will be one of nickserv, chanserv, memoserv or operserv */
capital_cmd = g_ascii_strup(cmd, -1);
buf = irc_format(irc, "v:", capital_cmd, args[0]);
irc_send(irc, buf);
g_free(capital_cmd);
g_free(buf);
return 0;
}
int irc_cmd_time(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
{
char *buf;
buf = irc_format(irc, "v", "TIME");
irc_send(irc, buf);
g_free(buf);
return 0;
}
int irc_cmd_topic(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
{
char *buf;
const char *topic;
PurpleChatConversation *chat;
if (!args)
return 0;
chat = purple_conversations_find_chat_with_account(target, irc->account);
if (!chat)
return 0;
if (!args[0]) {
topic = purple_chat_conversation_get_topic (chat);
if (topic) {
char *tmp, *tmp2;
tmp = g_markup_escape_text(topic, -1);
tmp2 = purple_markup_linkify(tmp);
buf = g_strdup_printf(_("current topic is: %s"), tmp2);
g_free(tmp);
g_free(tmp2);
} else
buf = g_strdup(_("No topic is set"));
purple_conversation_write_system_message(
PURPLE_CONVERSATION(chat), buf, PURPLE_MESSAGE_NO_LOG);
g_free(buf);
return 0;
}
buf = irc_format(irc, "vt:", "TOPIC", target, args[0]);
irc_send(irc, buf);
g_free(buf);
return 0;
}
int irc_cmd_wallops(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
{
char *buf;
if (!args || !args[0])
return 0;
if (purple_strequal(cmd, "wallops"))
buf = irc_format(irc, "v:", "WALLOPS", args[0]);
else if (purple_strequal(cmd, "operwall"))
buf = irc_format(irc, "v:", "OPERWALL", args[0]);
else
return 0;
irc_send(irc, buf);
g_free(buf);
return 0;
}
int irc_cmd_whois(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
{
char *buf;
if (!args || !args[0])
return 0;
if (args[1]) {
buf = irc_format(irc, "vvn", "WHOIS", args[0], args[1]);
irc->whois.nick = g_strdup(args[1]);
} else {
buf = irc_format(irc, "vn", "WHOIS", args[0]);
irc->whois.nick = g_strdup(args[0]);
}
irc_send(irc, buf);
g_free(buf);
return 0;
}
int irc_cmd_whowas(struct irc_conn *irc, const char *cmd, const char *target, const char **args)
{
char *buf;
if (!args || !args[0])
return 0;
buf = irc_format(irc, "vn", "WHOWAS", args[0]);
irc->whois.nick = g_strdup(args[0]);
irc_send(irc, buf);
g_free(buf);
return 0;
}
static void irc_do_mode(struct irc_conn *irc, const char *target, const char *sign, char **ops)
{
char *buf, mode[5];
int i = 0;
if (!sign)
return;
while (ops[i]) {
if (ops[i + 2] && ops[i + 4]) {
g_snprintf(mode, sizeof(mode), "%s%s%s%s", sign,
ops[i], ops[i + 2], ops[i + 4]);
buf = irc_format(irc, "vcvnnn", "MODE", target, mode,
ops[i + 1], ops[i + 3], ops[i + 5]);
i += 6;
} else if (ops[i + 2]) {
g_snprintf(mode, sizeof(mode), "%s%s%s",
sign, ops[i], ops[i + 2]);
buf = irc_format(irc, "vcvnn", "MODE", target, mode,
ops[i + 1], ops[i + 3]);
i += 4;
} else {
g_snprintf(mode, sizeof(mode), "%s%s", sign, ops[i]);
buf = irc_format(irc, "vcvn", "MODE", target, mode, ops[i + 1]);
i += 2;
}
irc_send(irc, buf);
g_free(buf);
}
}