pidgin/purple-plugin-pack
Clone
Summary
Browse
Changes
Graph
Add the turtles target to keep the foot clan at bay
default
tip
13 months ago, Gary Kramlich
63ad7e4f10b4
Add the turtles target to keep the foot clan at bay
Testing Done:
Ran `ninja turtles` and verified it worked correctly.
Reviewed at https://reviews.imfreedom.org/r/2409/
/*
* IRC Helper Plugin for libpurple
*
* Copyright (C) 2005-2009, Richard Laager <rlaager@pidgin.im>
* Copyright (C) 2004-2005, Mathias Hasselmann <mathias@taschenorakel.de>
* Copyright (C) 2005, Daniel Beardsmore <uilleann@users.sf.net>
* Copyright (C) 2005, Björn Nilsson <BNI on irc.freenode.net>
* Copyright (C) 2005, Anthony Sofocleous <itchysoft_ant@users.sf.net>
*
* 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.
*/
/* If you can't figure out what this line is for, DON'T TOUCH IT. */
#include
"../common/pp_internal.h"
#include
<string.h>
#include
<account.h>
#include
<accountopt.h>
#include
<cmds.h>
#include
<connection.h>
#include
<conversation.h>
#include
<debug.h>
#include
<notify.h>
#include
<plugin.h>
#include
<pluginpref.h>
#include
<prefs.h>
#include
<util.h>
#define PLUGIN_STATIC_NAME "irchelper"
#define PLUGIN_ID "core-rlaager-" PLUGIN_STATIC_NAME
#define PLUGIN_AUTHOR "Richard Laager <rlaager@guifications.org>"
/*****************************************************************************
* Constants *
*****************************************************************************/
#define IRC_PLUGIN_ID "prpl-irc"
/* Copied from SECS_BEFORE_RESENDING_AUTORESPONSE in src/server.c */
#define AUTO_RESPONSE_INTERVAL 600
#define DOMAIN_SUFFIX_DALNET ".dal.net"
#define DOMAIN_SUFFIX_FREENODE ".freenode.net"
#define DOMAIN_SUFFIX_FUNCOM ".funcom.com"
#define DOMAIN_SUFFIX_GAMESURGE ".gamesurge.net"
#define DOMAIN_SUFFIX_INDIEZEN ".indiezen.org"
#define DOMAIN_SUFFIX_JEUX ".jeux.fr"
#define DOMAIN_SUFFIX_QUAKENET ".quakenet.org"
#define DOMAIN_SUFFIX_SPIDERNET ".spidernet.org"
#define DOMAIN_SUFFIX_THUNDERCITY ".thundercity.org"
#define DOMAIN_SUFFIX_UNDERNET ".undernet.org"
#define MESSAGE_CHANSERV_ACCESS_LIST_ADD \
"You have been added to the access list for "
/* Omit the first character of the portion after the channel name. */
#define MESSAGE_CHANSERV_ACCESS_LIST_ADD_WITH_LEVEL "with level ["
#define MESSAGE_CHANSERV_ACCESS_LIST_DEL \
"You have been deleted from the access list for ["
#define MESSAGE_CHANSERV_NO_OP_ACCESS \
"You do not have channel operator access to"
#define MESSAGE_FREENODE_INFO "[freenode-info] "
#define MESSAGE_FRENCH_ADVERTISEMENT_START \
"<B>Avertissement</B> : Le pseudo <B>"
#define MESSAGE_FRENCH_ADVERTISEMENT_END "<votre pass>"
#define MESSAGE_FRENCH_LOGIN "Login <B>r?ussi</B>"
#define MESSAGE_FRENCH_MAXIMUM_CONNECTION_COUNT "Maximum de connexion"
#define MESSAGE_FRENCH_MOTD "Message du Jour :"
#define MESSAGE_PURPLE_NOTICE_PREFIX "(notice) "
#define MESSAGE_GAMESURGE_AUTHSERV_IDENTIFIED "I recognize you."
#define MESSAGE_GAMESURGE_AUTHSERV_ID_FAILURE \
"Incorrect password; please try again."
#define MESSAGE_GHOST_KILLED " has been killed"
#define MESSAGE_INVITED " invited "
#define MESSAGE_LOGIN_CONNECTION_COUNT "Highest connection count"
#define MESSAGE_MEMOSERV_NO_NEW_MEMOS "You have no new memos"
#define MESSAGE_MODE_NOTICE_PREFIX "mode (+"
#define MESSAGE_MODE_NOTICE_SUFFIX " ) by "
#define MESSAGE_NICKSERV_CLOAKED " set your hostname to"
#define MESSAGE_NICKSERV_ID_FAILURE "Password Incorrect"
#define MESSAGE_NICKSERV_IDENTIFIED \
"Password accepted - you are now recognized"
#define MESSAGE_NICKSERV_IDENTIFIED_FREENODE \
"You are now identified for"
#define MESSAGE_NICKSERV_IDENTIFIED_INDIEZEN \
"Password accepted -- you are now recognized."
#define MESSAGE_SPOOFING_YOUR_IP "*** Spoofing your IP. congrats."
#define MESSAGE_SET_HOSTNAME "idoru set your hostname to"
#define MESSAGE_QUAKENET_Q_CRUFT \
"Remember: NO-ONE from QuakeNet will ever ask for your password. " \
"NEVER send your password to ANYONE except Q@CServe.quakenet.org."
#define MESSAGE_QUAKENET_Q_ID_FAILURE \
"Lastly, When you do recover your password, please choose a " \
"NEW PASSWORD, not your old one! " \
"See the above URL for details."
#define MESSAGE_QUAKENET_Q_IDENTIFIED "AUTH'd successfully."
#define MESSAGE_UNREAL_IRCD_HOSTNAME_FOUND "*** Found your hostname"
#define MESSAGE_UNREAL_IRCD_HOSTNAME_LOOKUP "*** Looking up your hostname..."
#define MESSAGE_UNREAL_IRCD_IDENT_LOOKUP "*** Checking ident..."
#define MESSAGE_UNREAL_IRCD_IDENT_NO_RESPONSE \
"*** No ident response; username prefixed with ~"
#define MESSAGE_UNREAL_IRCD_PONG_CRUFT \
"*** If you are having problems connecting due to ping timeouts, " \
"please type /quote pong"
#define MESSAGE_VOICE_ADD "mode (+v"
#define MESSAGE_VOICE_REMOVE "mode(-v"
/* Generic AuthServ, not currently used for any networks. */
#define NICK_AUTHSERV "AuthServ"
#define NICK_CHANSERV "ChanServ"
#define NICK_FREENODE_CONNECT "frigg"
#define NICK_FUNCOM_Q_SERVICE NICK_QUAKENET_Q "@cserve.funcom.com"
#define NICK_GAMESURGE_AUTHSERV "AuthServ"
#define NICK_GAMESURGE_AUTHSERV_SERVICE \
NICK_GAMESURGE_AUTHSERV "@Services.GameSurge.net"
#define NICK_GAMESURGE_GLOBAL "Global"
#define NICK_JEUX_Z "Z"
#define NICK_JEUX_WELCOME "[Welcome]"
#define NICK_MEMOSERV "MemoServ"
#define NICK_NICKSERV "NickServ"
#define NICK_DALNET_AUTHSERV_SERVICE NICK_NICKSERV "@services.dal.net"
#define NICK_QUAKENET_L "L"
#define NICK_QUAKENET_Q "Q"
#define NICK_QUAKENET_Q_SERVICE NICK_QUAKENET_Q "@CServe.quakenet.org"
#define NICK_UNDERNET_X "x@channels.undernet.org"
/* The %c exists so we can tell if a match occurred. */
#define PATTERN_WEIRD_LOGIN_CRUFT "o%c %*u ca %*u(%*u) ft %*u(%*u)"
#define TIMEOUT_IDENTIFY 8000
#define TIMEOUT_KILLING_GHOST 8000
typedef
enum
{
IRC_NONE
=
0x0000
,
IRC_KILLING_GHOST
=
0x0001
,
IRC_WILL_ID
=
0x0002
,
IRC_DID_ID
=
0x0004
,
IRC_ID_FAILED
=
0x0008
,
IRC_NETWORK_TYPE_UNKNOWN
=
0x0010
,
IRC_NETWORK_TYPE_GAMESURGE
=
0x0020
,
IRC_NETWORK_TYPE_NICKSERV
=
0x0040
,
IRC_NETWORK_TYPE_QUAKENET
=
0x0080
,
IRC_NETWORK_TYPE_JEUX
=
0x0100
,
IRC_NETWORK_TYPE_UNDERNET
=
0x0200
,
IRC_NETWORK_TYPE_THUNDERCITY
=
0x0400
,
IRC_NETWORK_TYPE_DALNET
=
0x0800
,
IRC_NETWORK_TYPE_FUNCOM
=
0x1000
,
IRC_NETWORK_TYPE_INDIEZEN
=
0x2000
,
IRC_NETWORK_TYPE_SPIDERNET
=
0x4000
,
IRC_NETWORK_TYPE_FREENODE
=
0x8000
,
}
IRCHelperStateFlags
;
struct
proto_stuff
{
gpointer
*
proto_data
;
PurpleAccount
*
account
;
};
GHashTable
*
states
;
/*****************************************************************************
* Prototypes *
*****************************************************************************/
static
gboolean
plugin_load
(
PurplePlugin
*
plugin
);
static
gboolean
plugin_unload
(
PurplePlugin
*
plugin
);
/*****************************************************************************
* Plugin Info *
*****************************************************************************/
static
PurplePluginInfo
info
=
{
PURPLE_PLUGIN_MAGIC
,
PURPLE_MAJOR_VERSION
,
0
,
PURPLE_PLUGIN_STANDARD
,
/**< type */
NULL
,
/**< ui_requirement */
0
,
/**< flags */
NULL
,
/**< dependencies */
PURPLE_PRIORITY_DEFAULT
,
/**< priority */
PLUGIN_ID
,
/**< id */
NULL
,
/**< name */
PP_VERSION
,
/**< version */
NULL
,
/**< summary */
NULL
,
/**< description */
PLUGIN_AUTHOR
,
/**< author */
PP_WEBSITE
,
/**< homepage */
plugin_load
,
/**< load */
plugin_unload
,
/**< unload */
NULL
,
/**< destroy */
NULL
,
/**< ui_info */
NULL
,
/**< extra_info */
NULL
,
/**< prefs_info */
NULL
,
/**< actions */
NULL
,
/**< reserved 1 */
NULL
,
/**< reserved 2 */
NULL
,
/**< reserved 3 */
NULL
/**< reserved 4 */
};
/* XXX: This is a dirty hack. It's better than what I was doing before, though. */
static
PurpleConversation
*
get_conversation
(
PurpleAccount
*
account
)
{
PurpleConversation
*
conv
;
#if PURPLE_VERSION_CHECK(3,0,0)
conv
=
purple_conversation_new
(
PURPLE_CONV_TYPE_IM
,
account
,
"None"
);
#else
conv
=
g_new0
(
PurpleConversation
,
1
);
conv
->
type
=
PURPLE_CONV_TYPE_IM
;
/* If we use this then the conversation updated signal is fired and
* other plugins might start doing things to our conversation, such as
* setting data on it which we would then need to free etc. It's easier
* just to be more hacky by setting account directly. */
/* purple_conversation_set_account(conv, account); */
conv
->
account
=
account
;
#endif
return
conv
;
}
static
IRCHelperStateFlags
get_connection_type
(
PurpleConnection
*
connection
)
{
PurpleAccount
*
account
;
const
gchar
*
protocol
;
gchar
*
username
;
IRCHelperStateFlags
type
=
IRC_NETWORK_TYPE_UNKNOWN
;
g_return_val_if_fail
(
NULL
!=
connection
,
IRC_NETWORK_TYPE_UNKNOWN
);
account
=
purple_connection_get_account
(
connection
);
protocol
=
purple_account_get_protocol_id
(
account
);
g_return_val_if_fail
(
g_str_equal
(
protocol
,
IRC_PLUGIN_ID
),
IRC_NETWORK_TYPE_UNKNOWN
);
username
=
g_utf8_strdown
(
purple_account_get_username
(
account
),
-1
);
if
(
g_str_has_suffix
(
username
,
DOMAIN_SUFFIX_GAMESURGE
))
type
=
IRC_NETWORK_TYPE_GAMESURGE
;
else
if
(
g_str_has_suffix
(
username
,
DOMAIN_SUFFIX_THUNDERCITY
))
type
=
IRC_NETWORK_TYPE_THUNDERCITY
;
else
if
(
g_str_has_suffix
(
username
,
DOMAIN_SUFFIX_DALNET
))
type
=
IRC_NETWORK_TYPE_DALNET
;
else
if
(
g_str_has_suffix
(
username
,
DOMAIN_SUFFIX_QUAKENET
))
type
=
IRC_NETWORK_TYPE_QUAKENET
;
else
if
(
g_str_has_suffix
(
username
,
DOMAIN_SUFFIX_FUNCOM
))
type
=
IRC_NETWORK_TYPE_FUNCOM
;
else
if
(
g_str_has_suffix
(
username
,
DOMAIN_SUFFIX_JEUX
))
type
=
IRC_NETWORK_TYPE_JEUX
;
else
if
(
g_str_has_suffix
(
username
,
DOMAIN_SUFFIX_UNDERNET
))
type
=
IRC_NETWORK_TYPE_UNDERNET
;
else
if
(
g_str_has_suffix
(
username
,
DOMAIN_SUFFIX_INDIEZEN
))
type
=
IRC_NETWORK_TYPE_INDIEZEN
;
else
if
(
g_str_has_suffix
(
username
,
DOMAIN_SUFFIX_SPIDERNET
))
type
=
IRC_NETWORK_TYPE_SPIDERNET
;
else
if
(
g_str_has_suffix
(
username
,
DOMAIN_SUFFIX_FREENODE
))
type
=
IRC_NETWORK_TYPE_FREENODE
;
g_free
(
username
);
return
type
;
}
#if PURPLE_VERSION_CHECK(2,7,0)
static
gboolean
autojoin_cb
(
PurpleConnection
*
connection
,
gpointer
data
)
{
IRCHelperStateFlags
state
;
g_return_val_if_fail
(
NULL
!=
connection
,
FALSE
);
state
=
GPOINTER_TO_INT
(
g_hash_table_lookup
(
states
,
connection
->
proto_data
));
if
(
state
&
IRC_WILL_ID
||
state
&
IRC_KILLING_GHOST
)
{
/* Block autojoining; we'll re-fire the signal once authed. */
purple_debug_misc
(
PLUGIN_STATIC_NAME
,
"Blocking the autojoin signal.
\n
"
);
return
TRUE
;
}
return
FALSE
;
}
#endif
static
void
identify_finished
(
PurpleConnection
*
connection
,
IRCHelperStateFlags
new_state
)
{
IRCHelperStateFlags
state
;
#if PURPLE_VERSION_CHECK(2,7,0)
gboolean
emit
;
#endif
g_return_if_fail
(
NULL
!=
connection
);
state
=
GPOINTER_TO_INT
(
g_hash_table_lookup
(
states
,
connection
->
proto_data
));
#if PURPLE_VERSION_CHECK(2,7,0)
emit
=
(
state
&
IRC_WILL_ID
||
state
&
IRC_KILLING_GHOST
);
#endif
g_hash_table_insert
(
states
,
connection
->
proto_data
,
GINT_TO_POINTER
((
state
&
~
IRC_KILLING_GHOST
&
~
IRC_WILL_ID
)
|
new_state
));
#if PURPLE_VERSION_CHECK(2,7,0)
if
(
emit
)
{
/* This is done after updating the state so autojoin_cb
* won't re-block the signal. */
purple_debug_misc
(
PLUGIN_STATIC_NAME
,
"Re-emitting the autojoin signal.
\n
"
);
purple_signal_emit
(
purple_connections_get_handle
(),
"autojoin"
,
connection
);
}
#endif
}
static
gboolean
auth_timeout
(
gpointer
data
)
{
PurpleConnection
*
connection
=
data
;
gpointer
proto_data
=
connection
->
proto_data
;
IRCHelperStateFlags
state
;
state
=
GPOINTER_TO_INT
(
g_hash_table_lookup
(
states
,
proto_data
));
if
(
state
&
IRC_WILL_ID
)
{
purple_debug_info
(
PLUGIN_STATIC_NAME
,
"Authentication failed: timeout expired
\n
"
);
identify_finished
(
connection
,
IRC_ID_FAILED
);
}
return
FALSE
;
}
/*****************************************************************************
* AuthServ Helper Functions *
*****************************************************************************/
static
void
authserv_identify
(
const
char
*
command
,
PurpleConnection
*
connection
,
IRCHelperStateFlags
state
)
{
PurpleAccount
*
account
;
gchar
**
userparts
=
NULL
;
const
gchar
*
username
;
const
gchar
*
password
;
g_return_if_fail
(
NULL
!=
connection
);
account
=
purple_connection_get_account
(
connection
);
username
=
purple_account_get_string
(
account
,
PLUGIN_ID
"_authname"
,
""
);
if
(
NULL
==
username
||
'\0'
==
*
username
)
{
userparts
=
g_strsplit
(
purple_account_get_username
(
account
),
"@"
,
2
);
username
=
userparts
[
0
];
}
password
=
purple_account_get_string
(
account
,
PLUGIN_ID
"_nickpassword"
,
""
);
if
(
NULL
!=
username
&&
'\0'
!=
*
username
&&
NULL
!=
password
&&
'\0'
!=
*
password
)
{
const
gchar
*
authserv
=
NICK_AUTHSERV
;
gchar
*
authentication
=
g_strconcat
(
command
,
" "
,
username
,
" "
,
password
,
NULL
);
purple_debug_misc
(
PLUGIN_STATIC_NAME
,
"Sending authentication: %s %s <PASSWORD>
\n
"
,
command
,
username
);
g_hash_table_insert
(
states
,
connection
->
proto_data
,
GINT_TO_POINTER
(
state
|
IRC_WILL_ID
));
if
(
state
&
IRC_NETWORK_TYPE_GAMESURGE
)
authserv
=
NICK_GAMESURGE_AUTHSERV_SERVICE
;
else
if
(
state
&
IRC_NETWORK_TYPE_DALNET
)
authserv
=
NICK_DALNET_AUTHSERV_SERVICE
;
else
if
(
state
&
IRC_NETWORK_TYPE_QUAKENET
)
authserv
=
NICK_QUAKENET_Q_SERVICE
;
else
if
(
state
&
IRC_NETWORK_TYPE_FUNCOM
)
authserv
=
NICK_FUNCOM_Q_SERVICE
;
else
if
(
state
&
IRC_NETWORK_TYPE_UNDERNET
)
authserv
=
NICK_UNDERNET_X
;
serv_send_im
(
connection
,
authserv
,
authentication
,
0
);
g_free
(
authentication
);
/* Register a timeout... If we don't get the expected response from AuthServ,
* we need to stop suppressing messages from it at some point or the user
* could be very confused.
*/
purple_timeout_add
(
TIMEOUT_IDENTIFY
,
(
GSourceFunc
)
auth_timeout
,
(
gpointer
)
connection
);
}
g_strfreev
(
userparts
);
}
/*****************************************************************************
* Operator Helper Functions *
*****************************************************************************/
static
void
jeux_identify
(
PurpleConnection
*
connection
,
IRCHelperStateFlags
state
)
{
PurpleAccount
*
account
;
gchar
**
userparts
;
const
gchar
*
username
;
const
gchar
*
password
;
g_return_if_fail
(
NULL
!=
connection
);
account
=
purple_connection_get_account
(
connection
);
userparts
=
g_strsplit
(
purple_account_get_username
(
account
),
"@"
,
2
);
username
=
userparts
[
0
];
password
=
purple_account_get_string
(
account
,
PLUGIN_ID
"_nickpassword"
,
""
);
if
(
NULL
!=
username
&&
'\0'
!=
*
username
&&
NULL
!=
password
&&
'\0'
!=
*
password
)
{
gchar
*
authentication
=
g_strdup_printf
(
"quote %s login %s %s"
,
NICK_JEUX_Z
,
username
,
password
);
PurpleConversation
*
conv
=
get_conversation
(
account
);
gchar
*
error
;
purple_debug_misc
(
PLUGIN_STATIC_NAME
,
"Sending authentication: quote %s login %s <PASSWORD>
\n
"
,
NICK_JEUX_Z
,
username
);
g_hash_table_insert
(
states
,
connection
->
proto_data
,
GINT_TO_POINTER
(
state
|
IRC_WILL_ID
));
if
(
purple_cmd_do_command
(
conv
,
authentication
,
authentication
,
&
error
)
!=
PURPLE_CMD_STATUS_OK
)
{
/* TODO: PRINT ERROR MESSAGE */
g_free
(
error
);
}
g_free
(
conv
);
g_free
(
authentication
);
/* Register a timeout... If we don't get the expected response from AuthServ,
* we need to stop suppressing messages from it at some point or the user
* could be very confused.
*/
purple_timeout_add
(
TIMEOUT_IDENTIFY
,
(
GSourceFunc
)
auth_timeout
,
(
gpointer
)
connection
);
}
g_strfreev
(
userparts
);
}
/*****************************************************************************
* Operator Helper Functions *
*****************************************************************************/
static
void
oper_identify
(
PurpleAccount
*
account
)
{
const
char
*
operpassword
;
operpassword
=
purple_account_get_string
(
account
,
PLUGIN_ID
"_operpassword"
,
""
);
if
(
'\0'
!=
*
operpassword
)
{
PurpleConversation
*
conv
=
get_conversation
(
account
);
PurpleConnection
*
connection
=
purple_account_get_connection
(
account
);
const
gchar
*
name
=
purple_connection_get_display_name
(
connection
);
char
*
command
=
g_strdup_printf
(
"quote OPER %s %s"
,
name
,
operpassword
);
gchar
*
error
;
if
(
purple_cmd_do_command
(
conv
,
command
,
command
,
&
error
)
!=
PURPLE_CMD_STATUS_OK
)
{
/* TODO: PRINT ERROR MESSAGE */
g_free
(
error
);
}
g_free
(
command
);
g_free
(
conv
);
}
}
/*****************************************************************************
* NickServ Helper Functions *
*****************************************************************************/
static
void
nickserv_do_identify
(
char
*
authentication
,
gpointer
proto_data
,
PurpleConnection
*
gc
,
const
char
*
nickpassword
)
{
PurpleConversation
*
conv
=
get_conversation
(
purple_connection_get_account
(
gc
));
gchar
*
error
;
gchar
*
tmp
;
purple_debug_misc
(
PLUGIN_STATIC_NAME
,
"Sending authentication: %s <PASSWORD>
\n
"
,
authentication
);
tmp
=
g_strconcat
(
authentication
,
" "
,
nickpassword
,
NULL
);
g_free
(
authentication
);
authentication
=
tmp
;
if
(
purple_cmd_do_command
(
conv
,
authentication
,
authentication
,
&
error
)
!=
PURPLE_CMD_STATUS_OK
)
{
/* TODO: PRINT ERROR MESSAGE */
g_free
(
error
);
}
g_free
(
authentication
);
g_free
(
conv
);
/* Register a timeout... If we don't get the expected response from NickServ,
* we need to stop suppressing messages from it at some point or the user
* could be very confused.
*/
purple_timeout_add
(
TIMEOUT_IDENTIFY
,
(
GSourceFunc
)
auth_timeout
,
(
gpointer
)
gc
);
}
static
void
nickserv_identify
(
gpointer
proto_data
,
PurpleConnection
*
gc
,
const
char
*
nickpassword
)
{
char
*
authentication
=
g_strdup_printf
(
"quote %s IDENTIFY"
,
NICK_NICKSERV
);
nickserv_do_identify
(
authentication
,
proto_data
,
gc
,
nickpassword
);
}
static
void
nickserv_msg_identify
(
const
char
*
command
,
gpointer
proto_data
,
PurpleConnection
*
gc
,
const
char
*
nickpassword
)
{
char
*
authentication
=
g_strdup_printf
(
"quote PRIVMSG %s : %s"
,
NICK_NICKSERV
,
command
);
nickserv_do_identify
(
authentication
,
proto_data
,
gc
,
nickpassword
);
}
static
gboolean
ghosted_nickname_killed_cb
(
struct
proto_stuff
*
stuff
)
{
PurpleConnection
*
gc
;
char
**
userparts
;
IRCHelperStateFlags
state
;
PurpleConversation
*
conv
;
char
*
command
;
gchar
*
error
;
state
=
GPOINTER_TO_INT
(
g_hash_table_lookup
(
states
,
stuff
->
proto_data
));
/* We only want this function to act once. Under normal circumstances,
* it's going to get called twice: Once when NickServ tells us the ghost
* has been killed and once when the timeout expires. Under abnormal
* conditions, it will only be called when the timeout expires.
*/
if
(
!
(
state
&
IRC_KILLING_GHOST
))
{
g_free
(
stuff
);
return
FALSE
;
}
g_hash_table_insert
(
states
,
stuff
->
proto_data
,
GINT_TO_POINTER
((
state
&
~
IRC_KILLING_GHOST
)
|
IRC_WILL_ID
));
gc
=
purple_account_get_connection
(
stuff
->
account
);
if
(
NULL
==
gc
)
{
g_free
(
stuff
);
return
FALSE
;
}
/* Switch back to the normal nickname. */
userparts
=
g_strsplit
(
purple_account_get_username
(
stuff
->
account
),
\
"@"
,
2
);
conv
=
get_conversation
(
stuff
->
account
);
command
=
g_strdup_printf
(
"nick %s"
,
userparts
[
0
]);
if
(
purple_cmd_do_command
(
conv
,
command
,
command
,
&
error
)
!=
PURPLE_CMD_STATUS_OK
)
{
/* TODO: PRINT ERROR MESSAGE */
g_free
(
error
);
}
g_free
(
command
);
g_free
(
conv
);
nickserv_identify
(
stuff
->
proto_data
,
gc
,
purple_account_get_string
(
stuff
->
account
,
PLUGIN_ID
"_nickpassword"
,
""
));
g_strfreev
(
userparts
);
g_free
(
stuff
);
oper_identify
(
stuff
->
account
);
return
FALSE
;
}
/*****************************************************************************
* Callbacks *
*****************************************************************************/
static
void
signed_on_cb
(
PurpleConnection
*
connection
)
{
PurpleAccount
*
account
;
IRCHelperStateFlags
state
;
const
char
*
nickpassword
;
g_return_if_fail
(
NULL
!=
connection
);
g_return_if_fail
(
NULL
!=
connection
->
proto_data
);
account
=
purple_connection_get_account
(
connection
);
g_return_if_fail
(
NULL
!=
account
);
if
(
!
g_str_equal
(
purple_account_get_protocol_id
(
account
),
IRC_PLUGIN_ID
))
return
;
state
=
get_connection_type
(
connection
);
if
(
state
&
IRC_NETWORK_TYPE_GAMESURGE
)
{
purple_debug_info
(
PLUGIN_STATIC_NAME
,
"Connected with GameSurge: %s
\n
"
,
purple_connection_get_display_name
(
connection
));
authserv_identify
(
"AUTH"
,
connection
,
state
);
}
if
(
state
&
IRC_NETWORK_TYPE_DALNET
)
{
purple_debug_info
(
PLUGIN_STATIC_NAME
,
"Connected with DalNet: %s
\n
"
,
purple_connection_get_display_name
(
connection
));
authserv_identify
(
"IDENTIFY"
,
connection
,
state
);
}
else
if
(
state
&
IRC_NETWORK_TYPE_JEUX
)
{
purple_debug_info
(
PLUGIN_STATIC_NAME
,
"Connected with Jeux.fr: %s
\n
"
,
purple_connection_get_display_name
(
connection
));
jeux_identify
(
connection
,
state
);
}
else
if
(
state
&
IRC_NETWORK_TYPE_QUAKENET
)
{
purple_debug_info
(
PLUGIN_STATIC_NAME
,
"Connected with QuakeNet: %s
\n
"
,
purple_connection_get_display_name
(
connection
));
authserv_identify
(
"AUTH"
,
connection
,
state
);
}
else
if
(
state
&
IRC_NETWORK_TYPE_UNDERNET
)
{
purple_debug_info
(
PLUGIN_STATIC_NAME
,
"Connected with UnderNet: %s
\n
"
,
purple_connection_get_display_name
(
connection
));
authserv_identify
(
"login "
,
connection
,
state
);
}
else
if
(
state
&
IRC_NETWORK_TYPE_FUNCOM
)
{
purple_debug_info
(
PLUGIN_STATIC_NAME
,
"Connected with Funcom: %s
\n
"
,
purple_connection_get_display_name
(
connection
));
authserv_identify
(
"AUTH"
,
connection
,
state
);
}
else
{
nickpassword
=
purple_account_get_string
(
account
,
PLUGIN_ID
"_nickpassword"
,
""
);
if
(
'\0'
!=
*
nickpassword
)
{
char
**
userparts
;
g_hash_table_insert
(
states
,
connection
->
proto_data
,
GINT_TO_POINTER
(
IRC_NETWORK_TYPE_NICKSERV
|
IRC_WILL_ID
));
userparts
=
g_strsplit
(
purple_account_get_username
(
account
),
"@"
,
2
);
if
(
purple_account_get_bool
(
account
,
PLUGIN_ID
"_disconnectghosts"
,
0
)
&&
strcmp
(
userparts
[
0
],
purple_connection_get_display_name
(
connection
)))
{
struct
proto_stuff
*
stuff
=
g_new0
(
struct
proto_stuff
,
1
);
char
*
command
;
PurpleConversation
*
conv
;
gchar
*
error
;
stuff
->
proto_data
=
connection
->
proto_data
;
stuff
->
account
=
account
;
/* Disconnect the ghosted connection. */
command
=
g_strdup_printf
(
"quote %s GHOST %s %s"
,
NICK_NICKSERV
,
userparts
[
0
],
nickpassword
);
conv
=
get_conversation
(
account
);
purple_debug_misc
(
PLUGIN_STATIC_NAME
,
"Sending command: quote %s GHOST %s <PASSWORD>
\n
"
,
NICK_NICKSERV
,
userparts
[
0
]);
if
(
purple_cmd_do_command
(
conv
,
command
,
command
,
&
error
)
!=
PURPLE_CMD_STATUS_OK
)
{
/* TODO: PRINT ERROR MESSAGE */
g_free
(
error
);
}
g_free
(
command
);
g_free
(
conv
);
g_hash_table_insert
(
states
,
connection
->
proto_data
,
GINT_TO_POINTER
(
IRC_NETWORK_TYPE_NICKSERV
|
IRC_KILLING_GHOST
));
/* We have to wait for the server to disconnect the
* other username before we can reclaim it. This
* timeout sets an upper bound on the length of time
* we'll wait for the ghost to be killed.
*/
purple_timeout_add
(
TIMEOUT_KILLING_GHOST
,
(
GSourceFunc
)
ghosted_nickname_killed_cb
,
(
gpointer
)
stuff
);
g_strfreev
(
userparts
);
return
;
}
if
(
state
&
IRC_NETWORK_TYPE_THUNDERCITY
)
nickserv_msg_identify
(
"AUTH"
,
connection
->
proto_data
,
connection
,
nickpassword
);
else
if
(
state
&
(
IRC_NETWORK_TYPE_INDIEZEN
|
IRC_NETWORK_TYPE_SPIDERNET
))
nickserv_msg_identify
(
"identify"
,
connection
->
proto_data
,
connection
,
nickpassword
);
else
if
(
state
&
IRC_NETWORK_TYPE_FREENODE
)
{
char
*
authentication
=
g_strdup_printf
(
"quote %s IDENTIFY %s"
,
NICK_NICKSERV
,
userparts
[
0
]);
nickserv_do_identify
(
authentication
,
connection
->
proto_data
,
connection
,
nickpassword
);
}
else
nickserv_identify
(
connection
->
proto_data
,
connection
,
nickpassword
);
g_strfreev
(
userparts
);
}
}
oper_identify
(
account
);
}
static
void
conversation_created_cb
(
PurpleConversation
*
conv
)
{
purple_conversation_set_data
(
conv
,
PLUGIN_ID
"_start_time"
,
GINT_TO_POINTER
((
int
)
time
(
NULL
)));
}
static
gboolean
writing_chat_msg_cb
(
PurpleAccount
*
account
,
const
char
*
who
,
char
**
message
,
PurpleConversation
*
conv
,
PurpleMessageFlags
flags
)
{
const
gchar
*
name
;
const
gchar
*
topic
;
if
(
!
g_str_equal
(
purple_account_get_protocol_id
(
account
),
IRC_PLUGIN_ID
))
return
FALSE
;
if
(
NULL
==
*
message
)
return
FALSE
;
g_return_val_if_fail
(
purple_conversation_get_type
(
conv
)
==
PURPLE_CONV_TYPE_CHAT
,
FALSE
);
if
(
flags
&
PURPLE_MESSAGE_SYSTEM
&&
(
g_str_has_prefix
(
*
message
,
MESSAGE_MODE_NOTICE_PREFIX
"v "
)
||
g_str_has_prefix
(
*
message
,
MESSAGE_MODE_NOTICE_PREFIX
"o "
)))
{
const
char
*
tmp
=
*
message
+
sizeof
(
MESSAGE_MODE_NOTICE_PREFIX
"X "
)
-
1
;
const
char
*
name
=
purple_connection_get_display_name
(
purple_account_get_connection
(
account
));
if
(
g_str_has_prefix
(
tmp
,
name
)
&&
g_str_has_prefix
(
tmp
+
strlen
(
name
),
MESSAGE_MODE_NOTICE_SUFFIX
NICK_CHANSERV
))
{
if
(
time
(
NULL
)
<
(
time_t
)
GPOINTER_TO_INT
(
purple_conversation_get_data
(
conv
,
PLUGIN_ID
"_start_time"
))
+
10
)
return
TRUE
;
}
}
if
(
flags
&
PURPLE_MESSAGE_SYSTEM
&&
(
topic
=
purple_conv_chat_get_topic
(
PURPLE_CONV_CHAT
(
conv
)))
!=
NULL
&&
(
name
=
purple_conversation_get_name
(
conv
))
!=
NULL
)
{
char
*
name_escaped
=
g_markup_escape_text
(
name
,
-1
);
char
*
topic_escaped
=
g_markup_escape_text
(
topic
,
-1
);
char
*
topic_linkified
=
purple_markup_linkify
(
topic_escaped
);
if
(
strstr
(
*
message
,
name_escaped
)
!=
NULL
&&
strstr
(
*
message
,
topic_linkified
)
!=
NULL
)
{
/* We've got a topic notice. */
PurpleBlistNode
*
node
;
if
((
node
=
(
PurpleBlistNode
*
)
purple_blist_find_chat
(
account
,
name
))
!=
NULL
)
{
const
char
*
last_topic
=
purple_blist_node_get_string
(
node
,
PLUGIN_ID
"_topic"
);
/* If we saw this the last time we joined, suppress it. */
if
(
last_topic
!=
NULL
&&
strcmp
(
topic
,
last_topic
)
==
0
)
{
g_free
(
name_escaped
);
g_free
(
topic_escaped
);
g_free
(
topic_linkified
);
return
TRUE
;
}
else
purple_blist_node_set_string
(
node
,
PLUGIN_ID
"_topic"
,
topic
);
}
}
g_free
(
name_escaped
);
g_free
(
topic_escaped
);
g_free
(
topic_linkified
);
}
return
FALSE
;
}
static
GSList
*
auto_responses
=
NULL
;
struct
auto_response
{
PurpleConnection
*
gc
;
char
*
name
;
time_t
received
;
char
*
message
;
};
static
gboolean
expire_auto_responses
(
gpointer
data
)
{
GSList
*
tmp
,
*
cur
;
struct
auto_response
*
ar
;
tmp
=
auto_responses
;
while
(
tmp
)
{
cur
=
tmp
;
tmp
=
tmp
->
next
;
ar
=
(
struct
auto_response
*
)
cur
->
data
;
if
((
time
(
NULL
)
-
ar
->
received
)
>
AUTO_RESPONSE_INTERVAL
)
{
auto_responses
=
g_slist_remove
(
auto_responses
,
ar
);
g_free
(
ar
->
message
);
g_free
(
ar
);
}
}
return
FALSE
;
/* do not run again */
}
static
struct
auto_response
*
get_auto_response
(
PurpleConnection
*
gc
,
const
char
*
name
)
{
GSList
*
tmp
;
struct
auto_response
*
ar
;
purple_timeout_add
((
AUTO_RESPONSE_INTERVAL
+
1
)
*
1000
,
expire_auto_responses
,
NULL
);
tmp
=
auto_responses
;
while
(
tmp
)
{
ar
=
(
struct
auto_response
*
)
tmp
->
data
;
if
(
gc
==
ar
->
gc
&&
purple_strequal
(
name
,
ar
->
name
))
return
ar
;
tmp
=
tmp
->
next
;
}
ar
=
(
struct
auto_response
*
)
g_new0
(
struct
auto_response
,
1
);
ar
->
name
=
g_strdup
(
name
);
ar
->
gc
=
gc
;
ar
->
received
=
0
;
auto_responses
=
g_slist_prepend
(
auto_responses
,
ar
);
return
ar
;
}
static
gboolean
receiving_im_msg_cb
(
PurpleAccount
*
account
,
gchar
**
sender
,
gchar
**
buffer
,
PurpleConversation
*
conv
,
gint
*
flags
,
gpointer
data
)
{
gchar
*
msg
;
gchar
*
nick
;
PurpleConversation
*
chat
;
PurpleConnection
*
connection
;
IRCHelperStateFlags
state
;
gchar
*
invite_prefix
;
if
(
!
g_str_equal
(
purple_account_get_protocol_id
(
account
),
IRC_PLUGIN_ID
))
return
FALSE
;
msg
=
*
buffer
;
nick
=
*
sender
;
connection
=
purple_account_get_connection
(
account
);
g_return_val_if_fail
(
NULL
!=
connection
,
FALSE
);
state
=
GPOINTER_TO_INT
(
g_hash_table_lookup
(
states
,
connection
->
proto_data
));
/* SUPPRESS EXTRA AUTO-RESPONSES */
if
(
*
flags
&
PURPLE_MESSAGE_AUTO_RESP
)
{
/* The same idea as the code in src/server.c. */
struct
auto_response
*
ar
=
get_auto_response
(
connection
,
nick
);
time_t
now
=
time
(
NULL
);
if
((
now
-
ar
->
received
)
<=
AUTO_RESPONSE_INTERVAL
&&
!
strcmp
(
msg
,
ar
->
message
))
{
/* We've recently received an auto-response
* and it's the same as the one we've just received.
* Drop it!
*/
ar
->
received
=
now
;
return
TRUE
;
}
ar
->
received
=
now
;
g_free
(
ar
->
message
);
ar
->
message
=
g_strdup
(
msg
);
/* None of the other rules are for auto-responses. */
return
FALSE
;
}
/* SIMPLE SUPPRESSION RULES */
/* Suppress the FreeNode stats collection bot. */
if
(
g_str_equal
(
nick
,
NICK_FREENODE_CONNECT
)
&&
g_str_has_prefix
(
msg
,
"Received CTCP 'VERSION'"
))
{
return
TRUE
;
}
/* Suppress useless ChanServ notification. */
if
(
g_str_equal
(
nick
,
NICK_CHANSERV
)
&&
g_str_has_prefix
(
msg
,
MESSAGE_PURPLE_NOTICE_PREFIX
MESSAGE_CHANSERV_NO_OP_ACCESS
))
{
return
TRUE
;
}
/* Suppress GameSurge message(s) of the day. */
if
(
state
&
IRC_NETWORK_TYPE_GAMESURGE
&&
g_str_equal
(
nick
,
NICK_GAMESURGE_GLOBAL
))
{
return
TRUE
;
}
/* Suppress useless Jeux welcome. */
if
(
g_str_equal
(
nick
,
NICK_JEUX_WELCOME
))
{
return
TRUE
;
}
/* Suppress FreeNode welcome messages. */
if
(
g_str_has_prefix
(
msg
,
MESSAGE_PURPLE_NOTICE_PREFIX
MESSAGE_FREENODE_INFO
))
{
return
TRUE
;
}
/* Suppress useless MemoServ notification. */
if
(
g_str_equal
(
nick
,
NICK_MEMOSERV
)
&&
g_str_equal
(
msg
,
MESSAGE_PURPLE_NOTICE_PREFIX
MESSAGE_MEMOSERV_NO_NEW_MEMOS
))
{
return
TRUE
;
}
/* Suppress QuakeNet Q password warning. */
if
(
g_str_equal
(
nick
,
NICK_QUAKENET_Q
)
&&
g_str_equal
(
msg
,
MESSAGE_PURPLE_NOTICE_PREFIX
MESSAGE_QUAKENET_Q_CRUFT
))
{
return
TRUE
;
}
/* Suppress Z registration notice. */
if
(
g_str_equal
(
nick
,
"Z"
)
&&
g_str_has_prefix
(
msg
,
MESSAGE_PURPLE_NOTICE_PREFIX
MESSAGE_FRENCH_ADVERTISEMENT_START
)
&&
g_str_has_suffix
(
msg
,
MESSAGE_FRENCH_ADVERTISEMENT_END
))
{
return
TRUE
;
}
/* Suppress Z's successful login notice. */
if
(
g_str_equal
(
nick
,
"Z"
)
&&
(
g_str_has_prefix
(
msg
,
MESSAGE_PURPLE_NOTICE_PREFIX
MESSAGE_FRENCH_LOGIN
)
||
g_str_has_prefix
(
msg
,
MESSAGE_PURPLE_NOTICE_PREFIX
MESSAGE_FRENCH_MOTD
)))
{
return
TRUE
;
}
/* Suppress login message for the highest connection count. */
if
(
g_str_has_prefix
(
msg
,
MESSAGE_PURPLE_NOTICE_PREFIX
MESSAGE_LOGIN_CONNECTION_COUNT
)
||
g_str_has_prefix
(
msg
,
MESSAGE_PURPLE_NOTICE_PREFIX
MESSAGE_FRENCH_MAXIMUM_CONNECTION_COUNT
))
{
return
TRUE
;
}
/* Suppress UnrealIRCd login cruft. */
if
(
g_str_has_prefix
(
msg
,
MESSAGE_PURPLE_NOTICE_PREFIX
MESSAGE_UNREAL_IRCD_HOSTNAME_FOUND
)
||
g_str_equal
(
msg
,
MESSAGE_PURPLE_NOTICE_PREFIX
MESSAGE_UNREAL_IRCD_HOSTNAME_LOOKUP
)
||
g_str_equal
(
msg
,
MESSAGE_PURPLE_NOTICE_PREFIX
MESSAGE_UNREAL_IRCD_IDENT_LOOKUP
)
||
g_str_equal
(
msg
,
MESSAGE_PURPLE_NOTICE_PREFIX
MESSAGE_UNREAL_IRCD_IDENT_NO_RESPONSE
)
||
g_str_has_prefix
(
msg
,
MESSAGE_PURPLE_NOTICE_PREFIX
MESSAGE_UNREAL_IRCD_PONG_CRUFT
))
{
return
TRUE
;
}
/* Suppress hostname cloak notification on Freenode. */
if
(
g_str_has_suffix
(
nick
,
DOMAIN_SUFFIX_FREENODE
)
&&
g_str_has_prefix
(
msg
,
MESSAGE_PURPLE_NOTICE_PREFIX
NICK_NICKSERV
MESSAGE_NICKSERV_CLOAKED
))
{
return
TRUE
;
}
/* Suppress "IP spoofing" notice on worldforge.org:
* http://plugins.guifications.org/trac/ticket/524 */
if
(
g_str_has_prefix
(
msg
,
MESSAGE_PURPLE_NOTICE_PREFIX
MESSAGE_SPOOFING_YOUR_IP
))
{
return
TRUE
;
}
if
(
g_str_has_prefix
(
msg
,
MESSAGE_PURPLE_NOTICE_PREFIX
MESSAGE_SET_HOSTNAME
))
{
return
TRUE
;
}
/* Suppress voice mode change messages. */
if
(
g_str_has_prefix
(
msg
,
MESSAGE_VOICE_ADD
)
||
g_str_has_prefix
(
msg
,
MESSAGE_VOICE_REMOVE
))
{
return
TRUE
;
}
/* SLIGHTLY COMPLICATED SUPPRESSION RULES */
/* Supress QuakeNet and UnderNet Weird Login Cruft */
{
char
temp
;
if
(
sscanf
(
msg
,
MESSAGE_PURPLE_NOTICE_PREFIX
PATTERN_WEIRD_LOGIN_CRUFT
,
&
temp
)
==
1
)
{
return
TRUE
;
}
}
/* Suppress silly notices of my own invites. */
invite_prefix
=
g_strconcat
(
MESSAGE_PURPLE_NOTICE_PREFIX
,
purple_connection_get_display_name
(
connection
),
MESSAGE_INVITED
,
NULL
);
if
(
g_str_has_prefix
(
msg
,
invite_prefix
))
{
g_free
(
invite_prefix
);
return
TRUE
;
}
g_free
(
invite_prefix
);
/* SERVICE MAGIC */
/* Display ChanServ Access List Add Notifications in the chat window. */
if
(
g_str_has_prefix
(
msg
,
MESSAGE_PURPLE_NOTICE_PREFIX
MESSAGE_CHANSERV_ACCESS_LIST_ADD
)
&&
g_str_equal
(
nick
,
NICK_CHANSERV
))
{
gchar
*
tmp
;
gchar
*
channel
;
gchar
*
level
=
NULL
;
gchar
*
msg2
;
channel
=
purple_markup_strip_html
(
msg
+
sizeof
(
MESSAGE_PURPLE_NOTICE_PREFIX
MESSAGE_CHANSERV_ACCESS_LIST_ADD
)
-
1
);
if
((
tmp
=
strchr
(
channel
,
' '
))
!=
NULL
)
{
*
tmp
=
'\0'
;
if
(
g_str_has_prefix
(
tmp
+
1
,
MESSAGE_CHANSERV_ACCESS_LIST_ADD_WITH_LEVEL
))
{
/* The + 1 and - 1 are both kept to make it clear how that relates with other code. */
level
=
tmp
+
1
+
sizeof
(
MESSAGE_CHANSERV_ACCESS_LIST_ADD_WITH_LEVEL
)
-
1
;
if
((
tmp
=
strchr
(
level
,
']'
))
!=
NULL
)
*
tmp
=
'\0'
;
}
}
if
(
NULL
==
level
)
msg2
=
g_strdup
(
_
(
"You have been added to the access list."
));
else
msg2
=
g_strdup_printf
(
_
(
"You have been added to the access list with an access level of %s."
),
level
);
chat
=
purple_find_conversation_with_account
(
PURPLE_CONV_TYPE_CHAT
,
channel
,
account
);
if
(
chat
)
{
purple_conv_chat_write
(
PURPLE_CONV_CHAT
(
chat
),
nick
,
msg2
,
PURPLE_MESSAGE_SYSTEM
,
time
(
NULL
));
g_free
(
channel
);
g_free
(
msg2
);
return
TRUE
;
}
g_free
(
channel
);
g_free
(
msg2
);
return
FALSE
;
}
/* Display ChanServ Access List Delete Notifications in the chat window. */
if
(
g_str_has_prefix
(
msg
,
MESSAGE_PURPLE_NOTICE_PREFIX
MESSAGE_CHANSERV_ACCESS_LIST_DEL
)
&&
g_str_equal
(
nick
,
NICK_CHANSERV
))
{
gchar
*
tmp
;
gchar
*
channel
;
channel
=
purple_markup_strip_html
(
msg
+
sizeof
(
MESSAGE_PURPLE_NOTICE_PREFIX
MESSAGE_CHANSERV_ACCESS_LIST_DEL
)
-
1
);
if
((
tmp
=
strchr
(
channel
,
']'
))
!=
NULL
)
*
tmp
=
'\0'
;
chat
=
purple_find_conversation_with_account
(
PURPLE_CONV_TYPE_CHAT
,
channel
,
account
);
if
(
chat
)
{
purple_conv_chat_write
(
PURPLE_CONV_CHAT
(
chat
),
nick
,
_
(
"You have been removed from the access list."
),
PURPLE_MESSAGE_SYSTEM
,
time
(
NULL
));
g_free
(
channel
);
return
TRUE
;
}
g_free
(
channel
);
return
FALSE
;
}
/* Display ChanServ channel join messages in the chat window. */
if
(
g_str_has_prefix
(
msg
,
MESSAGE_PURPLE_NOTICE_PREFIX
"[#"
)
&&
(
g_str_equal
(
nick
,
NICK_CHANSERV
)
||
g_str_equal
(
nick
,
NICK_QUAKENET_L
)))
{
gchar
*
msg2
;
gchar
*
msg3
;
gchar
*
channel
;
/* Duplicate the message so we can modify the string in place. */
msg2
=
g_strdup
(
msg
);
/* We need to keep a pointer to the beginning of the string so we can free it later. */
msg3
=
msg2
;
/* channel needs to start with the # */
channel
=
msg3
;
channel
+=
sizeof
(
MESSAGE_PURPLE_NOTICE_PREFIX
);
/* Find the "]", set it to the null character.
* This makes chan contain just the channel.
* Then, increment msg3 two spots so it contains
* just the message.
*/
if
((
msg3
=
g_strstr_len
(
msg3
,
strlen
(
msg3
),
"]"
)))
{
PurpleBlistNode
*
node
;
*
msg3
=
'\0'
;
/* Make sure it's safe to increment the pointer */
if
(
'\0'
==
msg3
[
1
]
||
'\0'
==
msg3
[
2
])
{
g_free
(
msg2
);
return
FALSE
;
}
msg3
+=
2
;
if
((
node
=
(
PurpleBlistNode
*
)
purple_blist_find_chat
(
account
,
channel
))
!=
NULL
)
{
const
char
*
last_msg
=
purple_blist_node_get_string
(
node
,
PLUGIN_ID
"_chanserv_join_msg"
);
/* If we saw this the last time we joined, suppress it. */
if
(
last_msg
!=
NULL
&&
strcmp
(
msg3
,
last_msg
)
==
0
)
{
g_free
(
msg2
);
return
TRUE
;
}
else
purple_blist_node_set_string
(
node
,
PLUGIN_ID
"_chanserv_join_msg"
,
msg3
);
}
/* Write the message to the chat as a system message. */
chat
=
purple_find_conversation_with_account
(
PURPLE_CONV_TYPE_CHAT
,
channel
,
account
);
if
(
chat
)
{
purple_conv_chat_write
(
PURPLE_CONV_CHAT
(
chat
),
nick
,
msg3
,
PURPLE_MESSAGE_SYSTEM
,
time
(
NULL
));
g_free
(
msg2
);
return
TRUE
;
}
}
g_free
(
msg2
);
return
FALSE
;
}
/* Suppress useless NickServ notifications if we're identifying automatically. */
if
(
state
&
IRC_NETWORK_TYPE_NICKSERV
&&
(
state
&
IRC_WILL_ID
||
state
&
IRC_KILLING_GHOST
)
&&
g_str_equal
(
nick
,
NICK_NICKSERV
))
{
/* Track that the identification is finished. */
if
(
g_str_equal
(
msg
,
MESSAGE_PURPLE_NOTICE_PREFIX
MESSAGE_NICKSERV_IDENTIFIED
)
||
g_str_has_prefix
(
msg
,
MESSAGE_PURPLE_NOTICE_PREFIX
MESSAGE_NICKSERV_IDENTIFIED_FREENODE
)
||
g_str_equal
(
msg
,
MESSAGE_PURPLE_NOTICE_PREFIX
MESSAGE_NICKSERV_IDENTIFIED_INDIEZEN
))
identify_finished
(
connection
,
IRC_DID_ID
);
/* The ghost has been killed, continue the signon. */
if
(
state
&
IRC_KILLING_GHOST
&&
strstr
(
msg
,
MESSAGE_GHOST_KILLED
))
{
struct
proto_stuff
*
stuff
=
g_new0
(
struct
proto_stuff
,
1
);
stuff
->
proto_data
=
connection
->
proto_data
;
stuff
->
account
=
account
;
ghosted_nickname_killed_cb
(
stuff
);
}
/* The identification has failed. */
if
(
g_str_equal
(
msg
,
MESSAGE_PURPLE_NOTICE_PREFIX
MESSAGE_NICKSERV_ID_FAILURE
))
{
identify_finished
(
connection
,
IRC_ID_FAILED
);
purple_notify_error
(
NULL
,
_
(
"NickServ Authentication Error"
),
_
(
"Error authenticating with NickServ"
),
_
(
"Check your password."
));
}
return
TRUE
;
}
/* Suppress useless AuthServ notifications if we're identifying automatically. */
if
(
state
&
IRC_NETWORK_TYPE_GAMESURGE
&&
state
&
IRC_WILL_ID
&&
g_str_equal
(
nick
,
NICK_GAMESURGE_AUTHSERV
))
{
/* Track that the identification is finished. */
if
(
g_str_equal
(
msg
,
MESSAGE_PURPLE_NOTICE_PREFIX
MESSAGE_GAMESURGE_AUTHSERV_IDENTIFIED
))
identify_finished
(
connection
,
IRC_DID_ID
);
/* The identification has failed. */
if
(
g_str_equal
(
msg
,
MESSAGE_PURPLE_NOTICE_PREFIX
MESSAGE_GAMESURGE_AUTHSERV_ID_FAILURE
))
{
identify_finished
(
connection
,
IRC_ID_FAILED
);
purple_notify_error
(
NULL
,
_
(
"GameSurge Authentication Error"
),
_
(
"Error authenticating with AuthServ"
),
_
(
"Check your password."
));
}
return
TRUE
;
}
/* Suppress useless Q notifications if we're identifying automatically. */
if
((
state
&
IRC_NETWORK_TYPE_QUAKENET
||
state
&
IRC_NETWORK_TYPE_FUNCOM
)
&&
state
&
IRC_WILL_ID
&&
g_str_equal
(
nick
,
NICK_QUAKENET_Q
))
{
/* Track that the identification is finished. */
if
(
g_str_equal
(
msg
,
MESSAGE_PURPLE_NOTICE_PREFIX
MESSAGE_QUAKENET_Q_IDENTIFIED
))
identify_finished
(
connection
,
IRC_DID_ID
);
/* The identification has failed. */
if
(
g_str_equal
(
msg
,
MESSAGE_PURPLE_NOTICE_PREFIX
MESSAGE_QUAKENET_Q_ID_FAILURE
))
{
identify_finished
(
connection
,
IRC_ID_FAILED
);
purple_notify_error
(
NULL
,
_
(
"QuakeNet Authentication Error"
),
_
(
"Error authenticating with Q"
),
_
(
"Check your password."
));
}
return
TRUE
;
}
return
FALSE
;
}
/*****************************************************************************
* Plugin Code *
*****************************************************************************/
static
gboolean
plugin_load
(
PurplePlugin
*
plugin
)
{
PurplePlugin
*
irc_prpl
;
PurplePluginProtocolInfo
*
prpl_info
;
PurpleAccountOption
*
option
;
void
*
conn_handle
;
void
*
conv_handle
;
irc_prpl
=
purple_plugins_find_with_id
(
IRC_PLUGIN_ID
);
if
(
NULL
==
irc_prpl
)
return
FALSE
;
prpl_info
=
PURPLE_PLUGIN_PROTOCOL_INFO
(
irc_prpl
);
if
(
NULL
==
prpl_info
)
return
FALSE
;
/* Create hash table. */
states
=
g_hash_table_new
(
g_direct_hash
,
g_direct_equal
);
/* Register protocol preferences. */
option
=
purple_account_option_string_new
(
_
(
"Auth name"
),
PLUGIN_ID
"_authname"
,
""
);
prpl_info
->
protocol_options
=
g_list_append
(
prpl_info
->
protocol_options
,
option
);
option
=
purple_account_option_string_new
(
_
(
"Nick password"
),
PLUGIN_ID
"_nickpassword"
,
""
);
#if PURPLE_VERSION_CHECK(3,0,0)
purple_account_option_string_set_masked
(
option
,
TRUE
);
#else
purple_account_option_set_masked
(
option
,
TRUE
);
#endif
prpl_info
->
protocol_options
=
g_list_append
(
prpl_info
->
protocol_options
,
option
);
option
=
purple_account_option_bool_new
(
_
(
"Disconnect ghosts (Duplicate nicknames)"
),
PLUGIN_ID
"_disconnectghosts"
,
0
);
prpl_info
->
protocol_options
=
g_list_append
(
prpl_info
->
protocol_options
,
option
);
option
=
purple_account_option_string_new
(
_
(
"Operator password"
),
PLUGIN_ID
"_operpassword"
,
""
);
#if PURPLE_VERSION_CHECK(3,0,0)
purple_account_option_string_set_masked
(
option
,
TRUE
);
#else
purple_account_option_set_masked
(
option
,
TRUE
);
#endif
prpl_info
->
protocol_options
=
g_list_append
(
prpl_info
->
protocol_options
,
option
);
/* Register callbacks. */
conn_handle
=
purple_connections_get_handle
();
conv_handle
=
purple_conversations_get_handle
();
purple_signal_connect
(
conn_handle
,
"signed-on"
,
plugin
,
PURPLE_CALLBACK
(
signed_on_cb
),
NULL
);
#if PURPLE_VERSION_CHECK(2,7,0)
purple_signal_connect
(
conn_handle
,
"autojoin"
,
plugin
,
PURPLE_CALLBACK
(
autojoin_cb
),
NULL
);
#endif
purple_signal_connect
(
conv_handle
,
"conversation-created"
,
plugin
,
PURPLE_CALLBACK
(
conversation_created_cb
),
NULL
);
purple_signal_connect
(
conv_handle
,
"receiving-im-msg"
,
plugin
,
PURPLE_CALLBACK
(
receiving_im_msg_cb
),
NULL
);
purple_signal_connect
(
conv_handle
,
"writing-chat-msg"
,
plugin
,
PURPLE_CALLBACK
(
writing_chat_msg_cb
),
NULL
);
return
TRUE
;
}
static
gboolean
plugin_unload
(
PurplePlugin
*
plugin
)
{
PurplePlugin
*
irc_prpl
;
PurplePluginProtocolInfo
*
prpl_info
;
GList
*
list
;
irc_prpl
=
purple_plugins_find_with_id
(
IRC_PLUGIN_ID
);
if
(
NULL
==
irc_prpl
)
return
FALSE
;
prpl_info
=
PURPLE_PLUGIN_PROTOCOL_INFO
(
irc_prpl
);
if
(
NULL
==
prpl_info
)
return
FALSE
;
list
=
prpl_info
->
protocol_options
;
/* Remove protocol preferences. */
while
(
NULL
!=
list
)
{
PurpleAccountOption
*
option
=
(
PurpleAccountOption
*
)
list
->
data
;
if
(
g_str_has_prefix
(
purple_account_option_get_setting
(
option
),
PLUGIN_ID
"_"
))
{
GList
*
llist
=
list
;
/* Remove this element from the list. */
if
(
llist
->
prev
)
llist
->
prev
->
next
=
llist
->
next
;
if
(
llist
->
next
)
llist
->
next
->
prev
=
llist
->
prev
;
purple_account_option_destroy
(
option
);
list
=
g_list_next
(
list
);
g_list_free_1
(
llist
);
}
else
list
=
g_list_next
(
list
);
}
return
TRUE
;
}
static
void
plugin_init
(
PurplePlugin
*
plugin
)
{
info
.
dependencies
=
g_list_append
(
info
.
dependencies
,
IRC_PLUGIN_ID
);
#ifdef ENABLE_NLS
bindtextdomain
(
GETTEXT_PACKAGE
,
PP_LOCALEDIR
);
bind_textdomain_codeset
(
GETTEXT_PACKAGE
,
"UTF-8"
);
#endif
/* ENABLE_NLS */
info
.
name
=
_
(
"IRC Helper"
);
info
.
summary
=
_
(
"Handles the rough edges of the IRC protocol."
);
info
.
description
=
_
(
"- Transparent authentication with a variety of "
"services.
\n
- Suppression of various useless messages"
);
}
PURPLE_INIT_PLUGIN
(
PLUGIN_STATIC_NAME
,
plugin_init
,
info
)