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/
/*
* gaim-snpp Protocol Plugin
*
* Copyright (C) 2004-2008 Don Seiler <don@seiler.us>
*
* 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"
#define _GNU_SOURCE
#include
<stdio.h>
#include
<string.h>
#ifndef _WIN32
#
include
<sys/socket.h>
#else
#
include
<libc_interface.h>
#endif
#include
<sys/types.h>
#include
<unistd.h>
#include
<plugin.h>
#include
<accountopt.h>
#include
<prpl.h>
#include
<conversation.h>
#include
<notify.h>
#include
<debug.h>
#include
<blist.h>
#include
<util.h>
#define SNPP_DEFAULT_SERVER "localhost"
#define SNPP_DEFAULT_PORT 444
#define SNPP_INITIAL_BUFSIZE 1024
enum
state
{
CONN
,
PAGE
,
MESS
,
SEND
,
QUIT
,
LOGI
};
struct
snpp_data
{
PurpleAccount
*
account
;
int
fd
;
struct
snpp_page
*
current_page
;
};
struct
snpp_page
{
char
*
pager
;
char
*
message
;
int
state
;
};
static
PurplePlugin
*
_snpp_plugin
=
NULL
;
static
struct
snpp_page
*
snpp_page_new
()
{
struct
snpp_page
*
sp
;
purple_debug_info
(
"snpp"
,
"snpp_page_new
\n
"
);
sp
=
g_new0
(
struct
snpp_page
,
1
);
sp
->
state
=
CONN
;
return
sp
;
};
static
void
snpp_page_destroy
(
struct
snpp_page
*
sp
)
{
purple_debug_info
(
"snpp"
,
"snpp_page_destroy
\n
"
);
if
(
sp
->
pager
!=
NULL
)
g_free
(
sp
->
pager
);
if
(
sp
->
message
!=
NULL
)
g_free
(
sp
->
message
);
g_free
(
sp
);
sp
=
NULL
;
};
static
void
snpp_send
(
gint
fd
,
const
char
*
buf
)
{
purple_debug_info
(
"snpp"
,
"snpp_send
\n
"
);
purple_debug_info
(
"snpp"
,
"snpp_send: sending %s
\n
"
,
buf
);
if
(
!
write
(
fd
,
buf
,
strlen
(
buf
)))
{
purple_debug_warning
(
"snpp"
,
"snpp_send: Error sending message
\n
"
);
}
};
static
void
snpp_reset
(
PurpleConnection
*
gc
,
struct
snpp_data
*
sd
)
{
purple_debug_info
(
"snpp"
,
"snpp_reset
\n
"
);
if
(
gc
&&
gc
->
inpa
)
purple_input_remove
(
gc
->
inpa
);
if
(
sd
&&
sd
->
fd
)
close
(
sd
->
fd
);
if
(
sd
->
current_page
!=
NULL
)
snpp_page_destroy
(
sd
->
current_page
);
}
static
int
snpp_cmd_logi
(
struct
snpp_data
*
sd
)
{
char
command
[
SNPP_INITIAL_BUFSIZE
];
const
char
*
password
;
purple_debug_info
(
"snpp"
,
"snpp_cmd_logi
\n
"
);
if
((
password
=
purple_account_get_password
(
sd
->
account
))
==
NULL
)
password
=
""
;
/* If LOGI is unsupported, this should return 500 */
sd
->
current_page
->
state
=
LOGI
;
g_snprintf
(
command
,
sizeof
(
command
),
"LOGI %s %s
\n
"
,
purple_account_get_username
(
sd
->
account
),
password
);
snpp_send
(
sd
->
fd
,
command
);
return
0
;
}
static
int
snpp_cmd_page
(
struct
snpp_data
*
sd
)
{
char
command
[
SNPP_INITIAL_BUFSIZE
];
purple_debug_info
(
"snpp"
,
"snpp_cmd_page
\n
"
);
sd
->
current_page
->
state
=
PAGE
;
g_snprintf
(
command
,
sizeof
(
command
),
"PAGE %s
\n
"
,
sd
->
current_page
->
pager
);
snpp_send
(
sd
->
fd
,
command
);
return
0
;
};
static
int
snpp_cmd_mess
(
struct
snpp_data
*
sd
)
{
char
command
[
SNPP_INITIAL_BUFSIZE
];
purple_debug_info
(
"snpp"
,
"snpp_cmd_mess
\n
"
);
sd
->
current_page
->
state
=
MESS
;
g_snprintf
(
command
,
sizeof
(
command
),
"MESS %s
\n
"
,
sd
->
current_page
->
message
);
snpp_send
(
sd
->
fd
,
command
);
return
0
;
};
static
int
snpp_cmd_send
(
struct
snpp_data
*
sd
)
{
char
command
[
SNPP_INITIAL_BUFSIZE
];
purple_debug_info
(
"snpp"
,
"snpp_cmd_send
\n
"
);
sd
->
current_page
->
state
=
SEND
;
g_snprintf
(
command
,
sizeof
(
command
),
"SEND
\n
"
);
snpp_send
(
sd
->
fd
,
command
);
return
0
;
};
static
int
snpp_cmd_quit
(
struct
snpp_data
*
sd
)
{
char
command
[
SNPP_INITIAL_BUFSIZE
];
purple_debug_info
(
"snpp"
,
"snpp_cmd_quit
\n
"
);
sd
->
current_page
->
state
=
QUIT
;
g_snprintf
(
command
,
sizeof
(
command
),
"QUIT
\n
"
);
snpp_send
(
sd
->
fd
,
command
);
return
0
;
};
static
void
snpp_callback
(
gpointer
data
,
gint
source
,
PurpleInputCondition
cond
)
{
PurpleConnection
*
gc
;
struct
snpp_data
*
sd
;
int
len
;
char
buf
[
SNPP_INITIAL_BUFSIZE
];
char
*
retcode
=
NULL
;
PurpleConversation
*
conv
;
purple_debug_info
(
"snpp"
,
"snpp_callback
\n
"
);
gc
=
data
;
sd
=
gc
->
proto_data
;
if
((
len
=
read
(
sd
->
fd
,
buf
,
SNPP_INITIAL_BUFSIZE
-
1
))
<
0
)
{
purple_debug_warning
(
"snpp"
,
"snpp_callback: Read error
\n
"
);
/* purple_connection_error(gc, _("Read error")); */
return
;
}
else
if
(
len
==
0
)
{
/* Remote closed the connection, probably */
return
;
}
buf
[
len
]
=
'\0'
;
purple_debug_info
(
"snpp"
,
"snpp_callback: Recv: %s
\n
"
,
buf
);
retcode
=
g_strndup
(
buf
,
3
);
if
(
sd
->
current_page
!=
NULL
)
{
purple_debug_info
(
"snpp"
,
"snpp_callback: Current page found.
\n
"
);
/*
* Evaluate state and return code and call appropriate function
* to faciliate processing of pages.
*/
switch
(
sd
->
current_page
->
state
)
{
case
CONN
:
purple_debug_info
(
"snpp"
,
"snpp_callback: State is CONN, return code was %s
\n
"
,
retcode
);
if
(
!
g_ascii_strcasecmp
(
retcode
,
"220"
))
snpp_cmd_logi
(
sd
);
else
{
purple_notify_error
(
gc
,
NULL
,
buf
,
NULL
);
snpp_reset
(
gc
,
sd
);
}
break
;
case
LOGI
:
purple_debug_info
(
"snpp"
,
"snpp_callback: State is LOGI, return code was %s
\n
"
,
retcode
);
/* If LOGI is unsupported, server should return 500
XXX 230 is crutch for HylaFAX breaking the protocol */
if
(
!
g_ascii_strcasecmp
(
retcode
,
"250"
)
||
!
g_ascii_strcasecmp
(
retcode
,
"500"
)
||
!
g_ascii_strcasecmp
(
retcode
,
"230"
))
snpp_cmd_page
(
sd
);
else
{
purple_notify_error
(
gc
,
NULL
,
buf
,
NULL
);
snpp_reset
(
gc
,
sd
);
}
break
;
case
PAGE
:
purple_debug_info
(
"snpp"
,
"snpp_callback: State is PAGE, return code was %s
\n
"
,
retcode
);
if
(
!
g_ascii_strcasecmp
(
retcode
,
"250"
))
snpp_cmd_mess
(
sd
);
else
{
purple_notify_error
(
gc
,
NULL
,
buf
,
NULL
);
snpp_reset
(
gc
,
sd
);
}
break
;
case
MESS
:
purple_debug_info
(
"snpp"
,
"snpp_callback: State is MESS, return code was %s
\n
"
,
retcode
);
if
(
!
g_ascii_strcasecmp
(
retcode
,
"250"
))
snpp_cmd_send
(
sd
);
else
{
purple_notify_error
(
gc
,
NULL
,
buf
,
NULL
);
snpp_reset
(
gc
,
sd
);
}
break
;
case
SEND
:
purple_debug_info
(
"snpp"
,
"snpp_callback: State is SEND, return code was %s
\n
"
,
retcode
);
if
(
!
g_ascii_strcasecmp
(
retcode
,
"250"
)
||
!
g_ascii_strcasecmp
(
retcode
,
"860"
)
||
!
g_ascii_strcasecmp
(
retcode
,
"960"
))
{
/* Print status message (buf) to window */
if
((
conv
=
purple_find_conversation_with_account
(
PURPLE_CONV_TYPE_IM
,
sd
->
current_page
->
pager
,
sd
->
account
)))
{
purple_conversation_write
(
conv
,
NULL
,
buf
,
PURPLE_MESSAGE_SYSTEM
,
time
(
NULL
));
}
snpp_cmd_quit
(
sd
);
}
else
{
purple_notify_error
(
gc
,
NULL
,
buf
,
NULL
);
snpp_reset
(
gc
,
sd
);
}
break
;
case
QUIT
:
/* Not sure if we should ever get here */
purple_debug_info
(
"snpp"
,
"snpp_callback: State is QUIT, return code was %s
\n
"
,
retcode
);
if
(
g_ascii_strcasecmp
(
retcode
,
"221"
))
purple_debug_info
(
"snpp"
,
"snpp_callback: Return code of 221 expected, not received
\n
"
);
snpp_reset
(
gc
,
sd
);
break
;
default
:
purple_debug_info
(
"snpp"
,
"snpp_callback: current_page in unknown state
\n
"
);
purple_notify_error
(
gc
,
NULL
,
buf
,
NULL
);
}
}
else
{
purple_debug_info
(
"snpp"
,
"snpp_callback: No current page to process
\n
"
);
}
g_free
(
retcode
);
};
static
void
snpp_connect_cb
(
gpointer
data
,
gint
source
,
const
gchar
*
error_message
)
{
PurpleConnection
*
gc
;
struct
snpp_data
*
sd
;
GList
*
connections
;
purple_debug_info
(
"snpp"
,
"snpp_connect_cb
\n
"
);
gc
=
data
;
sd
=
gc
->
proto_data
;
connections
=
purple_connections_get_all
();
if
(
source
<
0
)
return
;
if
(
!
g_list_find
(
connections
,
gc
))
{
close
(
source
);
return
;
}
sd
->
fd
=
source
;
gc
->
inpa
=
purple_input_add
(
sd
->
fd
,
PURPLE_INPUT_READ
,
snpp_callback
,
gc
);
}
static
void
snpp_connect
(
PurpleConnection
*
gc
)
{
PurpleProxyConnectData
*
ppcd
=
NULL
;
purple_debug_info
(
"snpp"
,
"snpp_connect
\n
"
);
ppcd
=
purple_proxy_connect
(
gc
->
account
,
gc
->
account
,
purple_account_get_string
(
gc
->
account
,
"server"
,
SNPP_DEFAULT_SERVER
),
purple_account_get_int
(
gc
->
account
,
"port"
,
SNPP_DEFAULT_PORT
),
snpp_connect_cb
,
gc
);
if
(
!
ppcd
||
!
gc
->
account
->
gc
)
{
purple_connection_error
(
gc
,
_
(
"Couldn't connect to SNPP server"
));
return
;
}
};
static
int
snpp_process
(
PurpleConnection
*
gc
,
struct
snpp_data
*
sd
)
{
purple_debug_info
(
"snpp"
,
"snpp_process
\n
"
);
if
(
sd
->
current_page
->
message
&&
(
strlen
(
sd
->
current_page
->
message
)
>
0
))
{
/* Just completed proxy_connect, ready to send data to server */
purple_debug_info
(
"snpp"
,
"snpp_page: Sending SNPP Request:
\n\n
%s
\n\n
"
,
sd
->
current_page
->
message
);
/* Get ball rolling, snpp_callback will take over */
snpp_connect
(
gc
);
}
return
1
;
};
static
int
snpp_send_im
(
PurpleConnection
*
gc
,
const
char
*
who
,
const
char
*
what
,
PurpleMessageFlags
flags
)
{
struct
snpp_data
*
sd
;
struct
snpp_page
*
sp
;
purple_debug_info
(
"snpp"
,
"snpp_send_im
\n
"
);
sd
=
gc
->
proto_data
;
sp
=
snpp_page_new
();
sp
->
pager
=
g_strdup
(
who
);
sp
->
message
=
g_strdup
(
what
);
sd
->
current_page
=
sp
;
snpp_process
(
gc
,
sd
);
return
1
;
};
static
const
char
*
snpp_icon
(
PurpleAccount
*
a
,
PurpleBuddy
*
b
)
{
/* purple_debug_info("snpp", "snpp_icon\n"); */
return
"snpp"
;
}
static
void
fake_buddy_signons
(
PurpleAccount
*
account
)
{
PurpleBlistNode
*
node
=
purple_get_blist
()
->
root
;
while
(
node
!=
NULL
)
{
if
(
PURPLE_BLIST_NODE_IS_BUDDY
(
node
))
{
PurpleBuddy
*
buddy
=
(
PurpleBuddy
*
)
node
;
if
(
buddy
->
account
==
account
)
purple_prpl_got_user_status
(
account
,
buddy
->
name
,
"available"
,
NULL
);
}
node
=
purple_blist_node_next
(
node
,
FALSE
);
}
}
static
void
snpp_login
(
PurpleAccount
*
account
)
{
PurpleConnection
*
gc
;
struct
snpp_data
*
sd
;
purple_debug_info
(
"snpp"
,
"snpp_login
\n
"
);
gc
=
purple_account_get_connection
(
account
);
gc
->
proto_data
=
sd
=
g_new0
(
struct
snpp_data
,
1
);
sd
->
account
=
account
;
purple_connection_set_state
(
gc
,
PURPLE_CONNECTED
);
fake_buddy_signons
(
account
);
};
static
void
snpp_close
(
PurpleConnection
*
gc
)
{
struct
snpp_data
*
sd
;
purple_debug_info
(
"snpp"
,
"snpp_close
\n
"
);
sd
=
gc
->
proto_data
;
if
(
sd
==
NULL
)
return
;
snpp_reset
(
gc
,
sd
);
g_free
(
sd
);
};
static
void
snpp_add_buddy
(
PurpleConnection
*
gc
,
PurpleBuddy
*
b
,
PurpleGroup
*
group
)
{
purple_debug_info
(
"snpp"
,
"snpp_add_buddy
\n
"
);
purple_prpl_got_user_status
(
purple_connection_get_account
(
gc
),
b
->
name
,
"available"
,
NULL
);
};
static
void
snpp_remove_buddy
(
PurpleConnection
*
gc
,
PurpleBuddy
*
b
,
PurpleGroup
*
group
)
{
purple_debug_info
(
"snpp"
,
"snpp_remove_buddy
\n
"
);
};
static
GList
*
snpp_status_types
(
PurpleAccount
*
account
)
{
PurpleStatusType
*
status
;
GList
*
types
=
NULL
;
status
=
purple_status_type_new_full
(
PURPLE_STATUS_OFFLINE
,
NULL
,
NULL
,
FALSE
,
TRUE
,
FALSE
);
types
=
g_list_append
(
types
,
status
);
status
=
purple_status_type_new_full
(
PURPLE_STATUS_AVAILABLE
,
NULL
,
NULL
,
FALSE
,
TRUE
,
FALSE
);
types
=
g_list_append
(
types
,
status
);
return
types
;
}
static
PurplePluginProtocolInfo
prpl_info
=
{
OPT_PROTO_PASSWORD_OPTIONAL
,
/* options */
NULL
,
/* user_splits */
NULL
,
/* protocol_options */
NO_BUDDY_ICONS
,
/* icon_spec */
snpp_icon
,
/* list_icon */
NULL
,
/* list_emblems */
NULL
,
/* status_text */
NULL
,
/* tooltip_text */
snpp_status_types
,
/* status_types */
NULL
,
/* blist_node_menu */
NULL
,
/* chat_info */
NULL
,
/* chat_info_defaults */
snpp_login
,
/* login */
snpp_close
,
/* close */
snpp_send_im
,
/* send_im */
NULL
,
/* set_info */
NULL
,
/* send_typing */
NULL
,
/* get_info */
NULL
,
/* set_away */
NULL
,
/* set_idle */
NULL
,
/* change_password */
snpp_add_buddy
,
/* add_buddy */
NULL
,
/* add_buddies */
snpp_remove_buddy
,
/* remove_buddy */
NULL
,
/* remove_buddies */
NULL
,
/* add_permit */
NULL
,
/* add_deny */
NULL
,
/* rem_permit */
NULL
,
/* rem_deny */
NULL
,
/* set_permit_deny */
NULL
,
/* warn */
NULL
,
/* join_chat */
NULL
,
/* reject_chat */
NULL
,
/* chat_invite */
NULL
,
/* chat_leave */
NULL
,
/* chat_whisper */
NULL
,
/* chat_send */
NULL
,
/* keepalive */
NULL
,
/* register_user */
NULL
,
/* get_cb_info */
NULL
,
/* get_cb_away */
NULL
,
/* alias_buddy */
NULL
,
/* group_buddy */
NULL
,
/* rename_group */
NULL
,
/* buddy_free */
NULL
,
/* convo_closed */
NULL
,
/* normalize */
NULL
,
/* set_buddy_icon */
NULL
,
/* remove_group */
NULL
,
/* get_cb_real_name */
NULL
,
/* set_chat_topic */
NULL
,
/* find_blist_chat */
NULL
,
/* roomlist_get_list */
NULL
,
/* roomlist_cancel */
NULL
,
/* roomlist_expand_catagory */
NULL
,
/* can_receive_file */
NULL
,
/* send_file */
NULL
,
/* new_xfer */
NULL
,
/* offline_message */
NULL
,
/* whiteboard_prpl_ops */
NULL
,
/* send_raw */
NULL
,
/* roomlist_room_serialize */
NULL
,
/* unregister_user */
NULL
,
/* send_attention */
NULL
,
/* get_attention_types */
sizeof
(
PurplePluginProtocolInfo
),
/* struct_size */
NULL
,
/* get_account_text_table */
NULL
,
/* initiate_media */
NULL
,
/* get_media_caps */
NULL
,
/* get_moods */
NULL
,
/* set_public_alias */
NULL
/* get_public_alias */
};
static
PurplePluginInfo
info
=
{
PURPLE_PLUGIN_MAGIC
,
/* magic */
PURPLE_MAJOR_VERSION
,
/* purple major */
PURPLE_MINOR_VERSION
,
/* purple minor */
PURPLE_PLUGIN_PROTOCOL
,
/* type */
NULL
,
/* ui_requirement */
0
,
/* flags */
NULL
,
/* dependencies */
PURPLE_PRIORITY_DEFAULT
,
/* priority */
"prpl-snpp"
,
/* id */
NULL
,
/* name */
PP_VERSION
,
/* version */
NULL
,
/* summary */
NULL
,
/* description */
"Don Seiler <don@seiler.us>"
,
/* author */
PP_WEBSITE
,
/* homepage */
NULL
,
/* load */
NULL
,
/* unload */
NULL
,
/* destroy */
NULL
,
/* ui_info */
&
prpl_info
,
/* extra_info */
NULL
,
/* prefs_info */
NULL
,
/* actions */
NULL
,
/* reserved */
NULL
,
/* reserved */
NULL
,
/* reserved */
NULL
/* reserved */
};
static
void
_init_plugin
(
PurplePlugin
*
plugin
)
{
PurpleAccountOption
*
option
;
option
=
purple_account_option_string_new
(
_
(
"Server"
),
"server"
,
SNPP_DEFAULT_SERVER
);
prpl_info
.
protocol_options
=
g_list_append
(
prpl_info
.
protocol_options
,
option
);
option
=
purple_account_option_int_new
(
_
(
"Port"
),
"port"
,
SNPP_DEFAULT_PORT
);
prpl_info
.
protocol_options
=
g_list_append
(
prpl_info
.
protocol_options
,
option
);
_snpp_plugin
=
plugin
;
info
.
name
=
_
(
"SNPP"
);
info
.
summary
=
_
(
"SNPP Plugin"
);
info
.
description
=
_
(
"Allows libpurple to send messages over the Simple Network Paging Protocol (SNPP)."
);
};
PURPLE_INIT_PLUGIN
(
snpp
,
_init_plugin
,
info
);