pidgin/pidgin
Clone
Summary
Browse
Changes
Graph
closing merged branch
Ball--Gy-rgy/update-screenshot-url-in-appdata-file-1591293245974
2020-06-07, Gary Kramlich
708c6622acc6
closing merged branch
/**
* purple
*
* Purple is the legal property of its developers, whose names are too numerous
* to list here. Please refer to the COPYRIGHT file distributed with this
* source distribution.
*
* Nullprpl is a mock protocol plugin for Pidgin and libpurple. You can create
* accounts with it, sign on and off, add buddies, and send and receive IMs,
* all without connecting to a server!
*
* Beyond that basic functionality, nullprpl supports presence and
* away/available messages, offline messages, user info, typing notification,
* privacy allow/block lists, chat rooms, room lists, and protocol
* icons and emblems. Notable missing features are file transfer and account
* registration and authentication.
*
* Nullprpl is intended as an example of how to write a libpurple protocol
* plugin. It doesn't contain networking code or an event loop, but it does
* demonstrate how to use the libpurple API to do pretty much everything a
* protocol might need to do.
*
* Nullprpl is also a useful tool for hacking on Pidgin, Finch, and other
* libpurple clients. It's a full-featured protocol plugin, but doesn't depend
* on an external server, so it's a quick and easy way to exercise test new
* code. It also allows you to work while you're disconnected.
*
* 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
<stdarg.h>
#include
<string.h>
#include
<time.h>
#include
<glib.h>
/* If you're using this as the basis of a protocol that will be distributed
* separately from libpurple, remove the internal.h include below and replace
* it with code to include your own config.h or similar. If you're going to
* provide for translation, you'll also need to setup the gettext macros. */
#include
"internal.h"
#include
<purple.h>
#include
"nullprpl.h"
/*
* reference to the protocol instance, used for registering signals, prefs,
* etc. it is set when the protocol is added in plugin_load and is required
* for removing the protocol in plugin_unload.
*/
static
PurpleProtocol
*
my_protocol
=
NULL
;
#define NULL_STATUS_ONLINE "online"
#define NULL_STATUS_AWAY "away"
#define NULL_STATUS_OFFLINE "offline"
typedef
void
(
*
GcFunc
)(
PurpleConnection
*
from
,
PurpleConnection
*
to
,
gpointer
userdata
);
typedef
struct
{
GcFunc
fn
;
PurpleConnection
*
from
;
gpointer
userdata
;
}
GcFuncData
;
/*
* stores offline messages that haven't been delivered yet. maps username
* (char *) to GList * of GOfflineMessages. initialized in plugin_load.
*/
GHashTable
*
goffline_messages
=
NULL
;
typedef
struct
{
char
*
from
;
char
*
message
;
time_t
mtime
;
PurpleMessageFlags
flags
;
}
GOfflineMessage
;
/*
* helpers
*/
static
PurpleConnection
*
get_null_gc
(
const
char
*
username
)
{
PurpleAccount
*
acct
=
purple_accounts_find
(
username
,
"null"
);
if
(
acct
&&
purple_account_is_connected
(
acct
))
return
purple_account_get_connection
(
acct
);
else
return
NULL
;
}
static
void
call_if_nullprotocol
(
gpointer
data
,
gpointer
userdata
)
{
PurpleConnection
*
gc
=
(
PurpleConnection
*
)(
data
);
GcFuncData
*
gcfdata
=
(
GcFuncData
*
)
userdata
;
if
(
purple_strequal
(
purple_account_get_protocol_id
(
purple_connection_get_account
(
gc
)),
"null"
))
gcfdata
->
fn
(
gcfdata
->
from
,
gc
,
gcfdata
->
userdata
);
}
static
void
foreach_null_gc
(
GcFunc
fn
,
PurpleConnection
*
from
,
gpointer
userdata
)
{
GcFuncData
gcfdata
=
{
fn
,
from
,
userdata
};
g_list_foreach
(
purple_connections_get_all
(),
call_if_nullprotocol
,
&
gcfdata
);
}
typedef
void
(
*
ChatFunc
)(
PurpleChatConversation
*
from
,
PurpleChatConversation
*
to
,
int
id
,
const
char
*
room
,
gpointer
userdata
);
typedef
struct
{
ChatFunc
fn
;
PurpleChatConversation
*
from_chat
;
gpointer
userdata
;
}
ChatFuncData
;
static
void
call_chat_func
(
gpointer
data
,
gpointer
userdata
)
{
PurpleConnection
*
to
=
(
PurpleConnection
*
)
data
;
ChatFuncData
*
cfdata
=
(
ChatFuncData
*
)
userdata
;
int
id
=
purple_chat_conversation_get_id
(
cfdata
->
from_chat
);
PurpleChatConversation
*
chat
=
purple_conversations_find_chat
(
to
,
id
);
if
(
chat
)
cfdata
->
fn
(
cfdata
->
from_chat
,
chat
,
id
,
purple_conversation_get_name
(
PURPLE_CONVERSATION
(
chat
)),
cfdata
->
userdata
);
}
static
void
foreach_gc_in_chat
(
ChatFunc
fn
,
PurpleConnection
*
from
,
int
id
,
gpointer
userdata
)
{
PurpleChatConversation
*
chat
=
purple_conversations_find_chat
(
from
,
id
);
ChatFuncData
cfdata
=
{
fn
,
chat
,
userdata
};
g_list_foreach
(
purple_connections_get_all
(),
call_chat_func
,
&
cfdata
);
}
static
void
discover_status
(
PurpleConnection
*
from
,
PurpleConnection
*
to
,
gpointer
userdata
)
{
const
char
*
from_username
=
purple_account_get_username
(
purple_connection_get_account
(
from
));
const
char
*
to_username
=
purple_account_get_username
(
purple_connection_get_account
(
to
));
if
(
purple_blist_find_buddy
(
purple_connection_get_account
(
from
),
to_username
))
{
PurpleStatus
*
status
=
purple_account_get_active_status
(
purple_connection_get_account
(
to
));
const
char
*
status_id
=
purple_status_get_id
(
status
);
const
char
*
message
=
purple_status_get_attr_string
(
status
,
"message"
);
if
(
purple_strequal
(
status_id
,
NULL_STATUS_ONLINE
)
||
purple_strequal
(
status_id
,
NULL_STATUS_AWAY
)
||
purple_strequal
(
status_id
,
NULL_STATUS_OFFLINE
))
{
purple_debug_info
(
"nullprpl"
,
"%s sees that %s is %s: %s
\n
"
,
from_username
,
to_username
,
status_id
,
message
);
purple_protocol_got_user_status
(
purple_connection_get_account
(
from
),
to_username
,
status_id
,
(
message
)
?
"message"
:
NULL
,
message
,
NULL
);
}
else
{
purple_debug_error
(
"nullprpl"
,
"%s's buddy %s has an unknown status: %s, %s"
,
from_username
,
to_username
,
status_id
,
message
);
}
}
}
static
void
report_status_change
(
PurpleConnection
*
from
,
PurpleConnection
*
to
,
gpointer
userdata
)
{
purple_debug_info
(
"nullprpl"
,
"notifying %s that %s changed status
\n
"
,
purple_account_get_username
(
purple_connection_get_account
(
to
)),
purple_account_get_username
(
purple_connection_get_account
(
from
)));
discover_status
(
to
,
from
,
NULL
);
}
/*
* UI callbacks
*/
static
void
null_input_user_info
(
PurpleProtocolAction
*
action
)
{
PurpleConnection
*
gc
=
action
->
connection
;
PurpleAccount
*
acct
=
purple_connection_get_account
(
gc
);
purple_debug_info
(
"nullprpl"
,
"showing 'Set User Info' dialog for %s
\n
"
,
purple_account_get_username
(
acct
));
purple_account_request_change_user_info
(
acct
);
}
/*
* Protocol functions
*/
static
GList
*
null_get_actions
(
PurpleConnection
*
gc
)
{
PurpleProtocolAction
*
action
=
purple_protocol_action_new
(
_
(
"Set User Info..."
),
null_input_user_info
);
return
g_list_append
(
NULL
,
action
);
}
static
const
char
*
null_list_icon
(
PurpleAccount
*
acct
,
PurpleBuddy
*
buddy
)
{
return
"null"
;
}
static
char
*
null_status_text
(
PurpleBuddy
*
buddy
)
{
purple_debug_info
(
"nullprpl"
,
"getting %s's status text for %s
\n
"
,
purple_buddy_get_name
(
buddy
),
purple_account_get_username
(
purple_buddy_get_account
(
buddy
)));
if
(
purple_blist_find_buddy
(
purple_buddy_get_account
(
buddy
),
purple_buddy_get_name
(
buddy
)))
{
PurplePresence
*
presence
=
purple_buddy_get_presence
(
buddy
);
PurpleStatus
*
status
=
purple_presence_get_active_status
(
presence
);
const
char
*
name
=
purple_status_get_name
(
status
);
const
char
*
message
=
purple_status_get_attr_string
(
status
,
"message"
);
char
*
text
;
if
(
message
&&
*
message
)
text
=
g_strdup_printf
(
"%s: %s"
,
name
,
message
);
else
text
=
g_strdup
(
name
);
purple_debug_info
(
"nullprpl"
,
"%s's status text is %s
\n
"
,
purple_buddy_get_name
(
buddy
),
text
);
return
text
;
}
else
{
purple_debug_info
(
"nullprpl"
,
"...but %s is not logged in
\n
"
,
purple_buddy_get_name
(
buddy
));
return
g_strdup
(
"Not logged in"
);
}
}
static
void
null_tooltip_text
(
PurpleBuddy
*
buddy
,
PurpleNotifyUserInfo
*
info
,
gboolean
full
)
{
PurpleConnection
*
gc
=
get_null_gc
(
purple_buddy_get_name
(
buddy
));
if
(
gc
)
{
/* they're logged in */
PurplePresence
*
presence
=
purple_buddy_get_presence
(
buddy
);
PurpleStatus
*
status
=
purple_presence_get_active_status
(
presence
);
char
*
msg
=
null_status_text
(
buddy
);
/* TODO: Check whether it's correct to call add_pair_html,
or if we should be using add_pair_plaintext */
purple_notify_user_info_add_pair_html
(
info
,
purple_status_get_name
(
status
),
msg
);
g_free
(
msg
);
if
(
full
)
{
const
char
*
user_info
=
purple_account_get_user_info
(
purple_connection_get_account
(
gc
));
if
(
user_info
)
/* TODO: Check whether it's correct to call add_pair_html,
or if we should be using add_pair_plaintext */
purple_notify_user_info_add_pair_html
(
info
,
_
(
"User info"
),
user_info
);
}
}
else
{
/* they're not logged in */
purple_notify_user_info_add_pair_plaintext
(
info
,
_
(
"User info"
),
_
(
"not logged in"
));
}
purple_debug_info
(
"nullprpl"
,
"showing %s tooltip for %s
\n
"
,
(
full
)
?
"full"
:
"short"
,
purple_buddy_get_name
(
buddy
));
}
static
GList
*
null_status_types
(
PurpleAccount
*
acct
)
{
GList
*
types
=
NULL
;
PurpleStatusType
*
type
;
purple_debug_info
(
"nullprpl"
,
"returning status types for %s: %s, %s, %s
\n
"
,
purple_account_get_username
(
acct
),
NULL_STATUS_ONLINE
,
NULL_STATUS_AWAY
,
NULL_STATUS_OFFLINE
);
type
=
purple_status_type_new_with_attrs
(
PURPLE_STATUS_AVAILABLE
,
NULL_STATUS_ONLINE
,
NULL
,
TRUE
,
TRUE
,
FALSE
,
"message"
,
_
(
"Message"
),
purple_value_new
(
G_TYPE_STRING
),
NULL
);
types
=
g_list_prepend
(
types
,
type
);
type
=
purple_status_type_new_with_attrs
(
PURPLE_STATUS_AWAY
,
NULL_STATUS_AWAY
,
NULL
,
TRUE
,
TRUE
,
FALSE
,
"message"
,
_
(
"Message"
),
purple_value_new
(
G_TYPE_STRING
),
NULL
);
types
=
g_list_prepend
(
types
,
type
);
type
=
purple_status_type_new_with_attrs
(
PURPLE_STATUS_OFFLINE
,
NULL_STATUS_OFFLINE
,
NULL
,
TRUE
,
TRUE
,
FALSE
,
"message"
,
_
(
"Message"
),
purple_value_new
(
G_TYPE_STRING
),
NULL
);
types
=
g_list_prepend
(
types
,
type
);
return
g_list_reverse
(
types
);
}
static
void
blist_example_menu_item
(
PurpleBlistNode
*
node
,
gpointer
userdata
)
{
purple_debug_info
(
"nullprpl"
,
"example menu item clicked on user %s
\n
"
,
purple_buddy_get_name
(
PURPLE_BUDDY
(
node
)));
purple_notify_info
(
NULL
,
/* plugin handle or PurpleConnection */
_
(
"Primary title"
),
_
(
"Secondary title"
),
_
(
"This is the callback for the NullProtocol menu item."
),
NULL
);
}
static
GList
*
null_blist_node_menu
(
PurpleBlistNode
*
node
)
{
purple_debug_info
(
"nullprpl"
,
"providing buddy list context menu item
\n
"
);
if
(
PURPLE_IS_BUDDY
(
node
))
{
PurpleActionMenu
*
action
=
purple_action_menu_new
(
_
(
"NullProtocol example menu item"
),
PURPLE_CALLBACK
(
blist_example_menu_item
),
NULL
,
/* userdata passed to the callback */
NULL
);
/* child menu items */
return
g_list_append
(
NULL
,
action
);
}
else
{
return
NULL
;
}
}
static
GList
*
null_chat_info
(
PurpleConnection
*
gc
)
{
PurpleProtocolChatEntry
*
pce
;
/* defined in protocols.h */
purple_debug_info
(
"nullprpl"
,
"returning chat setting 'room'
\n
"
);
pce
=
g_new0
(
PurpleProtocolChatEntry
,
1
);
pce
->
label
=
_
(
"Chat room"
);
pce
->
identifier
=
"room"
;
pce
->
required
=
TRUE
;
return
g_list_append
(
NULL
,
pce
);
}
static
GHashTable
*
null_chat_info_defaults
(
PurpleConnection
*
gc
,
const
char
*
room
)
{
GHashTable
*
defaults
;
purple_debug_info
(
"nullprpl"
,
"returning chat default setting "
"'room' = 'default'
\n
"
);
defaults
=
g_hash_table_new_full
(
g_str_hash
,
g_str_equal
,
NULL
,
g_free
);
g_hash_table_insert
(
defaults
,
"room"
,
g_strdup
(
"default"
));
return
defaults
;
}
static
void
null_login
(
PurpleAccount
*
acct
)
{
PurpleConnection
*
gc
=
purple_account_get_connection
(
acct
);
GList
*
offline_messages
;
GList
*
all_offline_messages
;
purple_debug_info
(
"nullprpl"
,
"logging in %s
\n
"
,
purple_account_get_username
(
acct
));
purple_connection_set_flags
(
gc
,
PURPLE_CONNECTION_FLAG_NO_IMAGES
);
purple_connection_update_progress
(
gc
,
_
(
"Connecting"
),
0
,
/* which connection step this is */
2
);
/* total number of steps */
purple_connection_update_progress
(
gc
,
_
(
"Connected"
),
1
,
/* which connection step this is */
2
);
/* total number of steps */
purple_connection_set_state
(
gc
,
PURPLE_CONNECTION_CONNECTED
);
/* tell purple about everyone on our buddy list who's connected */
foreach_null_gc
(
discover_status
,
gc
,
NULL
);
/* notify other nullprotocol accounts */
foreach_null_gc
(
report_status_change
,
gc
,
NULL
);
/* fetch stored offline messages */
purple_debug_info
(
"nullprpl"
,
"checking for offline messages for %s
\n
"
,
purple_account_get_username
(
acct
));
all_offline_messages
=
offline_messages
=
g_hash_table_lookup
(
goffline_messages
,
purple_account_get_username
(
acct
));
while
(
offline_messages
)
{
GOfflineMessage
*
message
=
(
GOfflineMessage
*
)
offline_messages
->
data
;
purple_debug_info
(
"nullprpl"
,
"delivering offline message to %s: %s
\n
"
,
purple_account_get_username
(
acct
),
message
->
message
);
purple_serv_got_im
(
gc
,
message
->
from
,
message
->
message
,
message
->
flags
,
message
->
mtime
);
offline_messages
=
g_list_next
(
offline_messages
);
g_free
(
message
->
from
);
g_free
(
message
->
message
);
g_free
(
message
);
}
g_hash_table_remove
(
goffline_messages
,
purple_account_get_username
(
acct
));
g_list_free
(
all_offline_messages
);
}
static
void
null_close
(
PurpleConnection
*
gc
)
{
/* notify other nullprotocol accounts */
foreach_null_gc
(
report_status_change
,
gc
,
NULL
);
}
static
int
null_send_im
(
PurpleConnection
*
gc
,
PurpleMessage
*
msg
)
{
const
char
*
from_username
=
purple_account_get_username
(
purple_connection_get_account
(
gc
));
const
gchar
*
who
=
purple_message_get_recipient
(
msg
);
PurpleMessageFlags
receive_flags
;
PurpleAccount
*
to_acct
=
purple_accounts_find
(
who
,
"null"
);
PurpleConnection
*
to
;
const
gchar
*
message
=
purple_message_get_contents
(
msg
);
receive_flags
=
((
purple_message_get_flags
(
msg
)
&
~
PURPLE_MESSAGE_SEND
)
|
PURPLE_MESSAGE_RECV
);
purple_debug_info
(
"nullprpl"
,
"sending message from %s to %s: %s
\n
"
,
from_username
,
who
,
message
);
/* is the sender blocked by the recipient's privacy settings? */
if
(
to_acct
&&
!
purple_account_privacy_check
(
to_acct
,
purple_account_get_username
(
purple_connection_get_account
(
gc
))))
{
char
*
msg
=
g_strdup_printf
(
_
(
"Your message was blocked by %s's privacy settings."
),
who
);
purple_debug_info
(
"nullprpl"
,
"discarding; %s is blocked by %s's privacy settings
\n
"
,
from_username
,
who
);
purple_conversation_present_error
(
who
,
purple_connection_get_account
(
gc
),
msg
);
g_free
(
msg
);
return
0
;
}
/* is the recipient online? */
to
=
get_null_gc
(
who
);
if
(
to
)
{
/* yes, send */
purple_serv_got_im
(
to
,
from_username
,
message
,
receive_flags
,
time
(
NULL
));
}
else
{
/* nope, store as an offline message */
GOfflineMessage
*
offline_message
;
GList
*
messages
;
purple_debug_info
(
"nullprpl"
,
"%s is offline, sending as offline message
\n
"
,
who
);
offline_message
=
g_new0
(
GOfflineMessage
,
1
);
offline_message
->
from
=
g_strdup
(
from_username
);
offline_message
->
message
=
g_strdup
(
message
);
offline_message
->
mtime
=
time
(
NULL
);
offline_message
->
flags
=
receive_flags
;
messages
=
g_hash_table_lookup
(
goffline_messages
,
who
);
messages
=
g_list_append
(
messages
,
offline_message
);
g_hash_table_insert
(
goffline_messages
,
g_strdup
(
who
),
messages
);
}
return
1
;
}
static
void
null_set_info
(
PurpleConnection
*
gc
,
const
char
*
info
)
{
purple_debug_info
(
"nullprpl"
,
"setting %s's user info to %s
\n
"
,
purple_account_get_username
(
purple_connection_get_account
(
gc
)),
info
);
}
static
const
char
*
typing_state_to_string
(
PurpleIMTypingState
typing
)
{
switch
(
typing
)
{
case
PURPLE_IM_NOT_TYPING
:
return
"is not typing"
;
case
PURPLE_IM_TYPING
:
return
"is typing"
;
case
PURPLE_IM_TYPED
:
return
"stopped typing momentarily"
;
default
:
return
"unknown typing state"
;
}
}
static
void
notify_typing
(
PurpleConnection
*
from
,
PurpleConnection
*
to
,
gpointer
typing
)
{
const
char
*
from_username
=
purple_account_get_username
(
purple_connection_get_account
(
from
));
const
char
*
action
=
typing_state_to_string
((
PurpleIMTypingState
)
typing
);
purple_debug_info
(
"nullprpl"
,
"notifying %s that %s %s
\n
"
,
purple_account_get_username
(
purple_connection_get_account
(
to
)),
from_username
,
action
);
purple_serv_got_typing
(
to
,
from_username
,
0
,
/* if non-zero, a timeout in seconds after which to
* reset the typing status to PURPLE_IM_NOT_TYPING */
(
PurpleIMTypingState
)
typing
);
}
static
unsigned
int
null_send_typing
(
PurpleConnection
*
gc
,
const
char
*
name
,
PurpleIMTypingState
typing
)
{
purple_debug_info
(
"nullprpl"
,
"%s %s
\n
"
,
purple_account_get_username
(
purple_connection_get_account
(
gc
)),
typing_state_to_string
(
typing
));
foreach_null_gc
(
notify_typing
,
gc
,
(
gpointer
)
typing
);
return
0
;
}
static
void
null_get_info
(
PurpleConnection
*
gc
,
const
char
*
username
)
{
const
char
*
body
;
PurpleNotifyUserInfo
*
info
=
purple_notify_user_info_new
();
PurpleAccount
*
acct
;
purple_debug_info
(
"nullprpl"
,
"Fetching %s's user info for %s
\n
"
,
username
,
purple_account_get_username
(
purple_connection_get_account
(
gc
)));
acct
=
purple_accounts_find
(
username
,
"null"
);
if
(
!
get_null_gc
(
username
))
{
char
*
msg
=
g_strdup_printf
(
_
(
"%s is not logged in."
),
username
);
purple_notify_error
(
gc
,
_
(
"User Info"
),
_
(
"User info not available. "
),
msg
,
purple_request_cpar_from_account
(
acct
));
g_free
(
msg
);
}
if
(
acct
)
body
=
purple_account_get_user_info
(
acct
);
else
body
=
_
(
"No user info."
);
/* TODO: Check whether it's correct to call add_pair_html,
or if we should be using add_pair_plaintext */
purple_notify_user_info_add_pair_html
(
info
,
"Info"
,
body
);
/* show a buddy's user info in a nice dialog box */
purple_notify_userinfo
(
gc
,
/* connection the buddy info came through */
username
,
/* buddy's username */
info
,
/* body */
NULL
,
/* callback called when dialog closed */
NULL
);
/* userdata for callback */
}
static
void
null_set_status
(
PurpleAccount
*
acct
,
PurpleStatus
*
status
)
{
const
char
*
msg
=
purple_status_get_attr_string
(
status
,
"message"
);
purple_debug_info
(
"nullprpl"
,
"setting %s's status to %s: %s
\n
"
,
purple_account_get_username
(
acct
),
purple_status_get_name
(
status
),
msg
);
foreach_null_gc
(
report_status_change
,
get_null_gc
(
purple_account_get_username
(
acct
)),
NULL
);
}
static
void
null_set_idle
(
PurpleConnection
*
gc
,
int
idletime
)
{
purple_debug_info
(
"nullprpl"
,
"purple reports that %s has been idle for %d seconds
\n
"
,
purple_account_get_username
(
purple_connection_get_account
(
gc
)),
idletime
);
}
static
void
null_change_passwd
(
PurpleConnection
*
gc
,
const
char
*
old_pass
,
const
char
*
new_pass
)
{
purple_debug_info
(
"nullprpl"
,
"%s wants to change their password
\n
"
,
purple_account_get_username
(
purple_connection_get_account
(
gc
)));
}
static
void
null_add_buddy
(
PurpleConnection
*
gc
,
PurpleBuddy
*
buddy
,
PurpleGroup
*
group
,
const
char
*
message
)
{
const
char
*
username
=
purple_account_get_username
(
purple_connection_get_account
(
gc
));
PurpleConnection
*
buddy_gc
=
get_null_gc
(
purple_buddy_get_name
(
buddy
));
purple_debug_info
(
"nullprpl"
,
"adding %s to %s's buddy list
\n
"
,
purple_buddy_get_name
(
buddy
),
username
);
if
(
buddy_gc
)
{
PurpleAccount
*
buddy_acct
=
purple_connection_get_account
(
buddy_gc
);
discover_status
(
gc
,
buddy_gc
,
NULL
);
if
(
purple_blist_find_buddy
(
buddy_acct
,
username
))
{
purple_debug_info
(
"nullprpl"
,
"%s is already on %s's buddy list
\n
"
,
username
,
purple_buddy_get_name
(
buddy
));
}
else
{
purple_debug_info
(
"nullprpl"
,
"asking %s if they want to add %s
\n
"
,
purple_buddy_get_name
(
buddy
),
username
);
purple_account_request_add
(
buddy_acct
,
username
,
NULL
,
/* local account id (rarely used) */
NULL
,
/* alias */
message
);
/* message */
}
}
}
static
void
null_add_buddies
(
PurpleConnection
*
gc
,
GList
*
buddies
,
GList
*
groups
,
const
char
*
message
)
{
GList
*
buddy
=
buddies
;
GList
*
group
=
groups
;
purple_debug_info
(
"nullprpl"
,
"adding multiple buddies
\n
"
);
while
(
buddy
&&
group
)
{
null_add_buddy
(
gc
,
(
PurpleBuddy
*
)
buddy
->
data
,
(
PurpleGroup
*
)
group
->
data
,
message
);
buddy
=
g_list_next
(
buddy
);
group
=
g_list_next
(
group
);
}
}
static
void
null_remove_buddy
(
PurpleConnection
*
gc
,
PurpleBuddy
*
buddy
,
PurpleGroup
*
group
)
{
purple_debug_info
(
"nullprpl"
,
"removing %s from %s's buddy list
\n
"
,
purple_buddy_get_name
(
buddy
),
purple_account_get_username
(
purple_connection_get_account
(
gc
)));
}
static
void
null_remove_buddies
(
PurpleConnection
*
gc
,
GList
*
buddies
,
GList
*
groups
)
{
GList
*
buddy
=
buddies
;
GList
*
group
=
groups
;
purple_debug_info
(
"nullprpl"
,
"removing multiple buddies
\n
"
);
while
(
buddy
&&
group
)
{
null_remove_buddy
(
gc
,
(
PurpleBuddy
*
)
buddy
->
data
,
(
PurpleGroup
*
)
group
->
data
);
buddy
=
g_list_next
(
buddy
);
group
=
g_list_next
(
group
);
}
}
/*
* nullprotocol uses purple's local whitelist and blacklist, stored in blist.xml, as
* its authoritative privacy settings, and uses purple's logic (specifically
* purple_privacy_check(), from privacy.h), to determine whether messages are
* allowed or blocked.
*/
static
void
null_add_permit
(
PurpleConnection
*
gc
,
const
char
*
name
)
{
purple_debug_info
(
"nullprpl"
,
"%s adds %s to their allowed list
\n
"
,
purple_account_get_username
(
purple_connection_get_account
(
gc
)),
name
);
}
static
void
null_add_deny
(
PurpleConnection
*
gc
,
const
char
*
name
)
{
purple_debug_info
(
"nullprpl"
,
"%s adds %s to their blocked list
\n
"
,
purple_account_get_username
(
purple_connection_get_account
(
gc
)),
name
);
}
static
void
null_rem_permit
(
PurpleConnection
*
gc
,
const
char
*
name
)
{
purple_debug_info
(
"nullprpl"
,
"%s removes %s from their allowed list
\n
"
,
purple_account_get_username
(
purple_connection_get_account
(
gc
)),
name
);
}
static
void
null_rem_deny
(
PurpleConnection
*
gc
,
const
char
*
name
)
{
purple_debug_info
(
"nullprpl"
,
"%s removes %s from their blocked list
\n
"
,
purple_account_get_username
(
purple_connection_get_account
(
gc
)),
name
);
}
static
void
null_set_permit_deny
(
PurpleConnection
*
gc
)
{
/* this is for synchronizing the local black/whitelist with the server.
* for nullprotocol, it's a noop.
*/
}
static
void
joined_chat
(
PurpleChatConversation
*
from
,
PurpleChatConversation
*
to
,
int
id
,
const
char
*
room
,
gpointer
userdata
)
{
/* tell their chat window that we joined */
purple_debug_info
(
"nullprpl"
,
"%s sees that %s joined chat room %s
\n
"
,
purple_chat_conversation_get_nick
(
to
),
purple_chat_conversation_get_nick
(
from
),
room
);
purple_chat_conversation_add_user
(
to
,
purple_chat_conversation_get_nick
(
from
),
NULL
,
/* user-provided join message, IRC style */
PURPLE_CHAT_USER_NONE
,
TRUE
);
/* show a join message */
if
(
from
!=
to
)
{
/* add them to our chat window */
purple_debug_info
(
"nullprpl"
,
"%s sees that %s is in chat room %s
\n
"
,
purple_chat_conversation_get_nick
(
from
),
purple_chat_conversation_get_nick
(
to
),
room
);
purple_chat_conversation_add_user
(
from
,
purple_chat_conversation_get_nick
(
to
),
NULL
,
/* user-provided join message, IRC style */
PURPLE_CHAT_USER_NONE
,
FALSE
);
/* show a join message */
}
}
static
void
null_join_chat
(
PurpleConnection
*
gc
,
GHashTable
*
components
)
{
const
char
*
username
=
purple_account_get_username
(
purple_connection_get_account
(
gc
));
const
char
*
room
=
g_hash_table_lookup
(
components
,
"room"
);
int
chat_id
=
g_str_hash
(
room
);
purple_debug_info
(
"nullprpl"
,
"%s is joining chat room %s
\n
"
,
username
,
room
);
if
(
!
purple_conversations_find_chat
(
gc
,
chat_id
))
{
purple_serv_got_joined_chat
(
gc
,
chat_id
,
room
);
/* tell everyone that we joined, and add them if they're already there */
foreach_gc_in_chat
(
joined_chat
,
gc
,
chat_id
,
NULL
);
}
else
{
char
*
tmp
=
g_strdup_printf
(
_
(
"%s is already in chat room %s."
),
username
,
room
);
purple_debug_info
(
"nullprpl"
,
"%s is already in chat room %s
\n
"
,
username
,
room
);
purple_notify_info
(
gc
,
_
(
"Join chat"
),
_
(
"Join chat"
),
tmp
,
purple_request_cpar_from_connection
(
gc
));
g_free
(
tmp
);
}
}
static
void
null_reject_chat
(
PurpleConnection
*
gc
,
GHashTable
*
components
)
{
const
char
*
invited_by
=
g_hash_table_lookup
(
components
,
"invited_by"
);
const
char
*
room
=
g_hash_table_lookup
(
components
,
"room"
);
const
char
*
username
=
purple_account_get_username
(
purple_connection_get_account
(
gc
));
PurpleConnection
*
invited_by_gc
=
get_null_gc
(
invited_by
);
char
*
message
=
g_strdup_printf
(
"%s %s %s."
,
username
,
_
(
"has rejected your invitation to join the chat room"
),
room
);
purple_debug_info
(
"nullprpl"
,
"%s has rejected %s's invitation to join chat room %s
\n
"
,
username
,
invited_by
,
room
);
purple_notify_info
(
invited_by_gc
,
_
(
"Chat invitation rejected"
),
_
(
"Chat invitation rejected"
),
message
,
purple_request_cpar_from_connection
(
gc
));
g_free
(
message
);
}
static
char
*
null_get_chat_name
(
GHashTable
*
components
)
{
const
char
*
room
=
g_hash_table_lookup
(
components
,
"room"
);
purple_debug_info
(
"nullprpl"
,
"reporting chat room name '%s'
\n
"
,
room
);
return
g_strdup
(
room
);
}
static
void
null_chat_invite
(
PurpleConnection
*
gc
,
int
id
,
const
char
*
message
,
const
char
*
who
)
{
const
char
*
username
=
purple_account_get_username
(
purple_connection_get_account
(
gc
));
PurpleChatConversation
*
chat
=
purple_conversations_find_chat
(
gc
,
id
);
const
char
*
room
=
purple_conversation_get_name
(
PURPLE_CONVERSATION
(
chat
));
PurpleAccount
*
to_acct
=
purple_accounts_find
(
who
,
"null"
);
purple_debug_info
(
"nullprpl"
,
"%s is inviting %s to join chat room %s
\n
"
,
username
,
who
,
room
);
if
(
to_acct
)
{
PurpleChatConversation
*
to_conv
=
purple_conversations_find_chat
(
purple_account_get_connection
(
to_acct
),
id
);
if
(
to_conv
)
{
char
*
tmp
=
g_strdup_printf
(
"%s is already in chat room %s."
,
who
,
room
);
purple_debug_info
(
"nullprpl"
,
"%s is already in chat room %s; "
"ignoring invitation from %s
\n
"
,
who
,
room
,
username
);
purple_notify_info
(
gc
,
_
(
"Chat invitation"
),
_
(
"Chat invitation"
),
tmp
,
purple_request_cpar_from_conversation
(
PURPLE_CONVERSATION
(
to_conv
)));
g_free
(
tmp
);
}
else
{
GHashTable
*
components
;
components
=
g_hash_table_new_full
(
g_str_hash
,
g_str_equal
,
NULL
,
g_free
);
g_hash_table_replace
(
components
,
"room"
,
g_strdup
(
room
));
g_hash_table_replace
(
components
,
"invited_by"
,
g_strdup
(
username
));
purple_serv_got_chat_invite
(
purple_account_get_connection
(
to_acct
),
room
,
username
,
message
,
components
);
}
}
}
static
void
left_chat_room
(
PurpleChatConversation
*
from
,
PurpleChatConversation
*
to
,
int
id
,
const
char
*
room
,
gpointer
userdata
)
{
if
(
from
!=
to
)
{
/* tell their chat window that we left */
purple_debug_info
(
"nullprpl"
,
"%s sees that %s left chat room %s
\n
"
,
purple_chat_conversation_get_nick
(
to
),
purple_chat_conversation_get_nick
(
from
),
room
);
purple_chat_conversation_remove_user
(
to
,
purple_chat_conversation_get_nick
(
from
),
NULL
);
/* user-provided message, IRC style */
}
}
static
void
null_chat_leave
(
PurpleConnection
*
gc
,
int
id
)
{
PurpleChatConversation
*
chat
=
purple_conversations_find_chat
(
gc
,
id
);
purple_debug_info
(
"nullprpl"
,
"%s is leaving chat room %s
\n
"
,
purple_account_get_username
(
purple_connection_get_account
(
gc
)),
purple_conversation_get_name
(
PURPLE_CONVERSATION
(
chat
)));
/* tell everyone that we left */
foreach_gc_in_chat
(
left_chat_room
,
gc
,
id
,
NULL
);
}
static
void
receive_chat_message
(
PurpleChatConversation
*
from
,
PurpleChatConversation
*
to
,
int
id
,
const
char
*
room
,
gpointer
userdata
)
{
const
char
*
message
=
(
const
char
*
)
userdata
;
PurpleConnection
*
to_gc
=
get_null_gc
(
purple_chat_conversation_get_nick
(
to
));
purple_debug_info
(
"nullprpl"
,
"%s receives message from %s in chat room %s: %s
\n
"
,
purple_chat_conversation_get_nick
(
to
),
purple_chat_conversation_get_nick
(
from
),
room
,
message
);
purple_serv_got_chat_in
(
to_gc
,
id
,
purple_chat_conversation_get_nick
(
from
),
PURPLE_MESSAGE_RECV
,
message
,
time
(
NULL
));
}
static
int
null_chat_send
(
PurpleConnection
*
gc
,
int
id
,
PurpleMessage
*
msg
)
{
const
char
*
username
=
purple_account_get_username
(
purple_connection_get_account
(
gc
));
PurpleChatConversation
*
chat
=
purple_conversations_find_chat
(
gc
,
id
);
const
gchar
*
message
=
purple_message_get_contents
(
msg
);
if
(
chat
)
{
purple_debug_info
(
"nullprpl"
,
"%s is sending message to chat room %s: %s
\n
"
,
username
,
purple_conversation_get_name
(
PURPLE_CONVERSATION
(
chat
)),
message
);
/* send message to everyone in the chat room */
foreach_gc_in_chat
(
receive_chat_message
,
gc
,
id
,
(
gpointer
)
message
);
return
0
;
}
else
{
purple_debug_info
(
"nullprpl"
,
"tried to send message from %s to chat room #%d: %s
\n
"
"but couldn't find chat room"
,
username
,
id
,
message
);
return
-1
;
}
}
static
void
null_register_user
(
PurpleAccount
*
acct
)
{
purple_debug_info
(
"nullprpl"
,
"registering account for %s
\n
"
,
purple_account_get_username
(
acct
));
}
static
void
null_alias_buddy
(
PurpleConnection
*
gc
,
const
char
*
who
,
const
char
*
alias
)
{
purple_debug_info
(
"nullprpl"
,
"%s sets %s's alias to %s
\n
"
,
purple_account_get_username
(
purple_connection_get_account
(
gc
)),
who
,
alias
);
}
static
void
null_group_buddy
(
PurpleConnection
*
gc
,
const
char
*
who
,
const
char
*
old_group
,
const
char
*
new_group
)
{
purple_debug_info
(
"nullprpl"
,
"%s has moved %s from group %s to group %s
\n
"
,
purple_account_get_username
(
purple_connection_get_account
(
gc
)),
who
,
old_group
,
new_group
);
}
static
void
null_rename_group
(
PurpleConnection
*
gc
,
const
char
*
old_name
,
PurpleGroup
*
group
,
GList
*
moved_buddies
)
{
purple_debug_info
(
"nullprpl"
,
"%s has renamed group %s to %s
\n
"
,
purple_account_get_username
(
purple_connection_get_account
(
gc
)),
old_name
,
purple_group_get_name
(
group
));
}
static
void
null_convo_closed
(
PurpleConnection
*
gc
,
const
char
*
who
)
{
purple_debug_info
(
"nullprpl"
,
"%s's conversation with %s was closed
\n
"
,
purple_account_get_username
(
purple_connection_get_account
(
gc
)),
who
);
}
/* normalize a username (e.g. remove whitespace, add default domain, etc.)
* for nullprotocol, this is a noop.
*/
static
const
char
*
null_normalize
(
const
PurpleAccount
*
acct
,
const
char
*
input
)
{
return
NULL
;
}
static
void
null_set_buddy_icon
(
PurpleConnection
*
gc
,
PurpleImage
*
img
)
{
purple_debug_info
(
"nullprpl"
,
"setting %s's buddy icon to %s
\n
"
,
purple_account_get_username
(
purple_connection_get_account
(
gc
)),
img
?
purple_image_get_path
(
img
)
:
"(null)"
);
}
static
void
null_remove_group
(
PurpleConnection
*
gc
,
PurpleGroup
*
group
)
{
purple_debug_info
(
"nullprpl"
,
"%s has removed group %s
\n
"
,
purple_account_get_username
(
purple_connection_get_account
(
gc
)),
purple_group_get_name
(
group
));
}
static
void
set_chat_topic_fn
(
PurpleChatConversation
*
from
,
PurpleChatConversation
*
to
,
int
id
,
const
char
*
room
,
gpointer
userdata
)
{
const
char
*
topic
=
(
const
char
*
)
userdata
;
const
char
*
username
=
purple_account_get_username
(
purple_conversation_get_account
(
PURPLE_CONVERSATION
(
from
)));
char
*
msg
;
purple_chat_conversation_set_topic
(
to
,
username
,
topic
);
if
(
topic
&&
*
topic
)
msg
=
g_strdup_printf
(
_
(
"%s sets topic to: %s"
),
username
,
topic
);
else
msg
=
g_strdup_printf
(
_
(
"%s clears topic"
),
username
);
purple_conversation_write_system_message
(
PURPLE_CONVERSATION
(
to
),
msg
,
PURPLE_MESSAGE_NO_LOG
);
g_free
(
msg
);
}
static
void
null_set_chat_topic
(
PurpleConnection
*
gc
,
int
id
,
const
char
*
topic
)
{
PurpleChatConversation
*
chat
=
purple_conversations_find_chat
(
gc
,
id
);
const
char
*
last_topic
;
if
(
!
chat
)
return
;
purple_debug_info
(
"nullprpl"
,
"%s sets topic of chat room '%s' to '%s'
\n
"
,
purple_account_get_username
(
purple_connection_get_account
(
gc
)),
purple_conversation_get_name
(
PURPLE_CONVERSATION
(
chat
)),
topic
);
last_topic
=
purple_chat_conversation_get_topic
(
chat
);
if
(
purple_strequal
(
topic
,
last_topic
))
return
;
/* topic is unchanged, this is a noop */
foreach_gc_in_chat
(
set_chat_topic_fn
,
gc
,
id
,
(
gpointer
)
topic
);
}
static
gboolean
null_finish_get_roomlist
(
gpointer
roomlist
)
{
purple_roomlist_set_in_progress
(
PURPLE_ROOMLIST
(
roomlist
),
FALSE
);
g_object_unref
(
roomlist
);
return
FALSE
;
}
static
PurpleRoomlist
*
null_roomlist_get_list
(
PurpleConnection
*
gc
)
{
const
char
*
username
=
purple_account_get_username
(
purple_connection_get_account
(
gc
));
PurpleRoomlist
*
roomlist
=
purple_roomlist_new
(
purple_connection_get_account
(
gc
));
GList
*
fields
=
NULL
;
PurpleRoomlistField
*
field
;
GList
*
chats
;
GList
*
seen_ids
=
NULL
;
purple_debug_info
(
"nullprpl"
,
"%s asks for room list; returning:
\n
"
,
username
);
/* set up the room list */
field
=
purple_roomlist_field_new
(
PURPLE_ROOMLIST_FIELD_STRING
,
"room"
,
"room"
,
TRUE
/* hidden */
);
fields
=
g_list_append
(
fields
,
field
);
field
=
purple_roomlist_field_new
(
PURPLE_ROOMLIST_FIELD_INT
,
"Id"
,
"Id"
,
FALSE
);
fields
=
g_list_append
(
fields
,
field
);
purple_roomlist_set_fields
(
roomlist
,
fields
);
/* add each chat room. the chat ids are cached in seen_ids so that each room
* is only returned once, even if multiple users are in it. */
for
(
chats
=
purple_conversations_get_chats
();
chats
;
chats
=
g_list_next
(
chats
))
{
PurpleChatConversation
*
chat
=
PURPLE_CHAT_CONVERSATION
(
chats
->
data
);
PurpleRoomlistRoom
*
room
;
const
char
*
name
=
purple_conversation_get_name
(
PURPLE_CONVERSATION
(
chat
));
int
id
=
purple_chat_conversation_get_id
(
chat
);
/* have we already added this room? */
if
(
g_list_find_custom
(
seen_ids
,
name
,
(
GCompareFunc
)
strcmp
))
continue
;
/* yes! try the next one. */
/* This cast is OK because this list is only staying around for the life
* of this function and none of the conversations are being deleted
* in that timespan. */
seen_ids
=
g_list_prepend
(
seen_ids
,
(
char
*
)
name
);
/* no, it's new. */
purple_debug_info
(
"nullprpl"
,
"%s (%d), "
,
name
,
id
);
room
=
purple_roomlist_room_new
(
PURPLE_ROOMLIST_ROOMTYPE_ROOM
,
name
,
NULL
);
purple_roomlist_room_add_field
(
roomlist
,
room
,
name
);
purple_roomlist_room_add_field
(
roomlist
,
room
,
&
id
);
purple_roomlist_room_add
(
roomlist
,
room
);
}
g_list_free
(
seen_ids
);
g_timeout_add
(
1
/* ms */
,
null_finish_get_roomlist
,
g_object_ref
(
roomlist
));
return
roomlist
;
}
static
void
null_roomlist_cancel
(
PurpleRoomlist
*
list
)
{
PurpleAccount
*
account
=
purple_roomlist_get_account
(
list
);
purple_debug_info
(
"nullprpl"
,
"%s asked to cancel room list request
\n
"
,
purple_account_get_username
(
account
));
}
static
void
null_roomlist_expand_category
(
PurpleRoomlist
*
list
,
PurpleRoomlistRoom
*
category
)
{
PurpleAccount
*
account
=
purple_roomlist_get_account
(
list
);
purple_debug_info
(
"nullprpl"
,
"%s asked to expand room list category %s
\n
"
,
purple_account_get_username
(
account
),
purple_roomlist_room_get_name
(
category
));
}
/* PurpleClientIface->offline_message takes a const PurpleBuddy *, this needs
* to change, but didn't want to do that in this pull request.
*/
static
gboolean
null_offline_message
(
const
PurpleBuddy
*
buddy
)
{
purple_debug_info
(
"nullprpl"
,
"reporting that offline messages are supported for %s
\n
"
,
purple_buddy_get_name
((
PurpleBuddy
*
)
buddy
));
return
TRUE
;
}
/*
* Initialize the protocol instance. see protocol.h for more information.
*/
static
void
null_protocol_init
(
NullProtocol
*
self
)
{
PurpleProtocol
*
protocol
=
PURPLE_PROTOCOL
(
self
);
PurpleAccountUserSplit
*
split
;
PurpleAccountOption
*
option
;
protocol
->
id
=
"prpl-null"
;
protocol
->
name
=
"Null - Testing Protocol"
;
protocol
->
options
=
OPT_PROTO_NO_PASSWORD
|
OPT_PROTO_CHAT_TOPIC
;
protocol
->
icon_spec
=
purple_buddy_icon_spec_new
(
"png,jpg,gif"
,
/* format */
0
,
/* min_width */
0
,
/* min_height */
128
,
/* max_width */
128
,
/* max_height */
10000
,
/* max_filesize */
PURPLE_ICON_SCALE_DISPLAY
/* scale_rules */
);
/* see accountopt.h for information about user splits and protocol
* options */
split
=
purple_account_user_split_new
(
_
(
"Example user split"
),
/* text shown to user */
"default"
,
/* default value */
'@'
);
/* field separator */
option
=
purple_account_option_string_new
(
_
(
"Example option"
),
/* text shown to user */
"example"
,
/* pref name */
"default"
);
/* default value */
protocol
->
user_splits
=
g_list_append
(
NULL
,
split
);
protocol
->
account_options
=
g_list_append
(
NULL
,
option
);
}
/*
* Initialize the protocol class and interfaces.
* see protocol.h for more information.
*/
static
void
null_protocol_class_init
(
NullProtocolClass
*
klass
)
{
PurpleProtocolClass
*
protocol_class
=
PURPLE_PROTOCOL_CLASS
(
klass
);
protocol_class
->
login
=
null_login
;
protocol_class
->
close
=
null_close
;
protocol_class
->
status_types
=
null_status_types
;
protocol_class
->
list_icon
=
null_list_icon
;
}
static
void
null_protocol_class_finalize
(
G_GNUC_UNUSED
NullProtocolClass
*
klass
)
{
}
static
void
null_protocol_client_iface_init
(
PurpleProtocolClientInterface
*
client_iface
)
{
client_iface
->
get_actions
=
null_get_actions
;
client_iface
->
status_text
=
null_status_text
;
client_iface
->
tooltip_text
=
null_tooltip_text
;
client_iface
->
blist_node_menu
=
null_blist_node_menu
;
client_iface
->
convo_closed
=
null_convo_closed
;
client_iface
->
normalize
=
null_normalize
;
client_iface
->
offline_message
=
null_offline_message
;
}
static
void
null_protocol_server_iface_init
(
PurpleProtocolServerInterface
*
server_iface
)
{
server_iface
->
register_user
=
null_register_user
;
server_iface
->
set_info
=
null_set_info
;
server_iface
->
get_info
=
null_get_info
;
server_iface
->
set_status
=
null_set_status
;
server_iface
->
set_idle
=
null_set_idle
;
server_iface
->
change_passwd
=
null_change_passwd
;
server_iface
->
add_buddy
=
null_add_buddy
;
server_iface
->
add_buddies
=
null_add_buddies
;
server_iface
->
remove_buddy
=
null_remove_buddy
;
server_iface
->
remove_buddies
=
null_remove_buddies
;
server_iface
->
alias_buddy
=
null_alias_buddy
;
server_iface
->
group_buddy
=
null_group_buddy
;
server_iface
->
rename_group
=
null_rename_group
;
server_iface
->
set_buddy_icon
=
null_set_buddy_icon
;
server_iface
->
remove_group
=
null_remove_group
;
}
static
void
null_protocol_im_iface_init
(
PurpleProtocolIMInterface
*
im_iface
)
{
im_iface
->
send
=
null_send_im
;
im_iface
->
send_typing
=
null_send_typing
;
}
static
void
null_protocol_chat_iface_init
(
PurpleProtocolChatInterface
*
chat_iface
)
{
chat_iface
->
info
=
null_chat_info
;
chat_iface
->
info_defaults
=
null_chat_info_defaults
;
chat_iface
->
join
=
null_join_chat
;
chat_iface
->
reject
=
null_reject_chat
;
chat_iface
->
get_name
=
null_get_chat_name
;
chat_iface
->
invite
=
null_chat_invite
;
chat_iface
->
leave
=
null_chat_leave
;
chat_iface
->
send
=
null_chat_send
;
chat_iface
->
set_topic
=
null_set_chat_topic
;
}
static
void
null_protocol_privacy_iface_init
(
PurpleProtocolPrivacyInterface
*
privacy_iface
)
{
privacy_iface
->
add_permit
=
null_add_permit
;
privacy_iface
->
add_deny
=
null_add_deny
;
privacy_iface
->
rem_permit
=
null_rem_permit
;
privacy_iface
->
rem_deny
=
null_rem_deny
;
privacy_iface
->
set_permit_deny
=
null_set_permit_deny
;
}
static
void
null_protocol_roomlist_iface_init
(
PurpleProtocolRoomlistInterface
*
roomlist_iface
)
{
roomlist_iface
->
get_list
=
null_roomlist_get_list
;
roomlist_iface
->
cancel
=
null_roomlist_cancel
;
roomlist_iface
->
expand_category
=
null_roomlist_expand_category
;
}
/*
* define the null protocol type. this macro defines
* null_protocol_register_type(PurplePlugin *) which is called in plugin_load()
* to register this type with the type system, and null_protocol_get_type()
* which returns the registered GType.
*/
G_DEFINE_DYNAMIC_TYPE_EXTENDED
(
NullProtocol
,
null_protocol
,
PURPLE_TYPE_PROTOCOL
,
0
,
G_IMPLEMENT_INTERFACE_DYNAMIC
(
PURPLE_TYPE_PROTOCOL_CLIENT
,
null_protocol_client_iface_init
)
G_IMPLEMENT_INTERFACE_DYNAMIC
(
PURPLE_TYPE_PROTOCOL_SERVER
,
null_protocol_server_iface_init
)
G_IMPLEMENT_INTERFACE_DYNAMIC
(
PURPLE_TYPE_PROTOCOL_IM
,
null_protocol_im_iface_init
)
G_IMPLEMENT_INTERFACE_DYNAMIC
(
PURPLE_TYPE_PROTOCOL_CHAT
,
null_protocol_chat_iface_init
)
G_IMPLEMENT_INTERFACE_DYNAMIC
(
PURPLE_TYPE_PROTOCOL_PRIVACY
,
null_protocol_privacy_iface_init
)
G_IMPLEMENT_INTERFACE_DYNAMIC
(
PURPLE_TYPE_PROTOCOL_ROOMLIST
,
null_protocol_roomlist_iface_init
));
static
PurplePluginInfo
*
plugin_query
(
GError
**
error
)
{
return
purple_plugin_info_new
(
"id"
,
"prpl-null"
,
"name"
,
"Null Protocol"
,
"version"
,
DISPLAY_VERSION
,
"category"
,
N_
(
"Protocol"
),
"summary"
,
N_
(
"Null Protocol Plugin"
),
"description"
,
N_
(
"Null Protocol Plugin"
),
"website"
,
PURPLE_WEBSITE
,
"abi-version"
,
PURPLE_ABI_VERSION
,
/* If you're using this protocol plugin as the basis of a plugin that will
* be distributed separately from libpurple, do not include these flags. */
"flags"
,
PURPLE_PLUGIN_INFO_FLAGS_INTERNAL
|
PURPLE_PLUGIN_INFO_FLAGS_AUTO_LOAD
,
NULL
);
}
static
gboolean
plugin_load
(
PurplePlugin
*
plugin
,
GError
**
error
)
{
/* register the NULL_TYPE_PROTOCOL type in the type system. this function
* is defined by G_DEFINE_DYNAMIC_TYPE_EXTENDED. */
null_protocol_register_type
(
G_TYPE_MODULE
(
plugin
));
/* add the protocol to the core */
my_protocol
=
purple_protocols_add
(
NULL_TYPE_PROTOCOL
,
error
);
if
(
!
my_protocol
)
return
FALSE
;
purple_debug_info
(
"nullprpl"
,
"starting up
\n
"
);
/* get ready to store offline messages */
goffline_messages
=
g_hash_table_new_full
(
g_str_hash
,
/* hash fn */
g_str_equal
,
/* key comparison fn */
g_free
,
/* key free fn */
NULL
);
/* value free fn */
return
TRUE
;
}
static
gboolean
plugin_unload
(
PurplePlugin
*
plugin
,
GError
**
error
)
{
purple_debug_info
(
"nullprpl"
,
"shutting down
\n
"
);
/* remove the protocol from the core */
if
(
!
purple_protocols_remove
(
my_protocol
,
error
))
return
FALSE
;
return
TRUE
;
}
/* initialize the plugin */
PURPLE_PLUGIN_INIT
(
null
,
plugin_query
,
plugin_load
,
plugin_unload
);