gaim/gaim
Clone
Summary
Browse
Changes
Graph
I feel dumb that it took me so long to get this right.
oldstatus
v1_2_1
2005-04-03, Mark Doliner
b59671364e74
I feel dumb that it took me so long to get this right.
I hope it's right. I'm going to do a bit o' testing.
/**
* Remote control plugin for Gaim
*
* Copyright (C) 2003 Christian Hammond.
* Copyright (C) 1998-1999, Mark Spencer <markster@marko.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., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* @todo Make this a core plugin!
*/
#include
"internal.h"
#include
"gtkgaim.h"
#ifndef _WIN32
#
include
<sys/un.h>
#endif
#include
<signal.h>
#include
<getopt.h>
#include
"conversation.h"
#include
"core.h"
#include
"debug.h"
#include
"prpl.h"
#include
"notify.h"
#include
"version.h"
/* XXX */
#include
"away.h"
#include
"gtkconv.h"
#include
"gtkplugin.h"
#include
"gaim.h"
#include
"prefs.h"
#include
<gaim-remote/remote.h>
#define REMOTE_PLUGIN_ID "gtk-remote"
struct
UI
{
GIOChannel
*
channel
;
guint
inpa
;
};
#ifndef _WIN32
static
gint
UI_fd
=
-1
;
static
guint
watcher
=
0
;
#endif
static
int
gaim_session
=
0
;
static
GSList
*
uis
=
NULL
;
/* AIM URI's ARE FUN :-D */
static
const
char
*
gaim_remote_handle_uri
(
const
char
*
uri
)
{
const
char
*
username
;
GString
*
str
;
GList
*
conn
;
GaimConnection
*
gc
=
NULL
;
GaimAccount
*
account
;
gaim_debug
(
GAIM_DEBUG_INFO
,
"gaim_remote_handle_uri"
,
"Handling URI: %s
\n
"
,
uri
);
/* Well, we'd better check to make sure we have at least one
AIM account connected. */
for
(
conn
=
gaim_connections_get_all
();
conn
!=
NULL
;
conn
=
conn
->
next
)
{
gc
=
conn
->
data
;
account
=
gaim_connection_get_account
(
gc
);
username
=
gaim_account_get_username
(
account
);
if
(
strcmp
(
gaim_account_get_protocol_id
(
account
),
"prpl-oscar"
)
==
0
&&
username
!=
NULL
&&
isalpha
(
*
username
))
{
break
;
}
}
if
(
gc
==
NULL
)
return
_
(
"Not connected to AIM"
);
/* aim:goim?screenname=screenname&message=message */
if
(
!
g_ascii_strncasecmp
(
uri
,
"aim:goim?"
,
strlen
(
"aim:goim?"
)))
{
char
*
who
,
*
what
;
GaimConversation
*
c
;
uri
=
uri
+
strlen
(
"aim:goim?"
);
if
(
!
(
who
=
strstr
(
uri
,
"screenname="
)))
{
return
_
(
"No screenname given."
);
}
/* spaces are encoded as +'s */
who
=
who
+
strlen
(
"screenname="
);
str
=
g_string_new
(
NULL
);
while
(
*
who
&&
(
*
who
!=
'&'
))
{
g_string_append_c
(
str
,
*
who
==
'+'
?
' '
:
*
who
);
who
++
;
}
who
=
g_strdup
(
str
->
str
);
g_string_free
(
str
,
TRUE
);
what
=
strstr
(
uri
,
"message="
);
if
(
what
)
{
what
=
what
+
strlen
(
"message="
);
str
=
g_string_new
(
NULL
);
while
(
*
what
&&
(
*
what
!=
'&'
||
!
g_ascii_strncasecmp
(
what
,
"&"
,
5
)))
{
g_string_append_c
(
str
,
*
what
==
'+'
?
' '
:
*
what
);
what
++
;
}
what
=
g_strdup
(
str
->
str
);
g_string_free
(
str
,
TRUE
);
}
c
=
gaim_conversation_new
(
GAIM_CONV_IM
,
gc
->
account
,
who
);
g_free
(
who
);
if
(
what
)
{
GaimGtkConversation
*
gtkconv
=
GAIM_GTK_CONVERSATION
(
c
);
gtk_text_buffer_insert_at_cursor
(
gtkconv
->
entry_buffer
,
what
,
-1
);
g_free
(
what
);
}
}
else
if
(
!
g_ascii_strncasecmp
(
uri
,
"aim:addbuddy?"
,
strlen
(
"aim:addbuddy?"
)))
{
char
*
who
,
*
group
;
uri
=
uri
+
strlen
(
"aim:addbuddy?"
);
/* spaces are encoded as +'s */
if
(
!
(
who
=
strstr
(
uri
,
"screenname="
)))
{
return
_
(
"No screenname given."
);
}
who
=
who
+
strlen
(
"screenname="
);
str
=
g_string_new
(
NULL
);
while
(
*
who
&&
(
*
who
!=
'&'
))
{
g_string_append_c
(
str
,
*
who
==
'+'
?
' '
:
*
who
);
who
++
;
}
who
=
g_strdup
(
str
->
str
);
g_string_free
(
str
,
TRUE
);
group
=
strstr
(
uri
,
"group="
);
if
(
group
)
{
group
=
group
+
strlen
(
"group="
);
str
=
g_string_new
(
NULL
);
while
(
*
group
&&
(
*
group
!=
'&'
||
!
g_ascii_strncasecmp
(
group
,
"&"
,
5
)))
{
g_string_append_c
(
str
,
*
group
==
'+'
?
' '
:
*
group
);
group
++
;
}
group
=
g_strdup
(
str
->
str
);
g_string_free
(
str
,
TRUE
);
}
gaim_debug
(
GAIM_DEBUG_MISC
,
"gaim_remote_handle_uri"
,
"who: %s
\n
"
,
who
);
gaim_blist_request_add_buddy
(
gc
->
account
,
who
,
group
,
NULL
);
g_free
(
who
);
if
(
group
)
g_free
(
group
);
}
else
if
(
!
g_ascii_strncasecmp
(
uri
,
"aim:gochat?"
,
strlen
(
"aim:gochat?"
)))
{
char
*
room
;
GHashTable
*
components
;
int
exch
=
5
;
uri
=
uri
+
strlen
(
"aim:gochat?"
);
/* spaces are encoded as +'s */
if
(
!
(
room
=
strstr
(
uri
,
"roomname="
)))
{
return
_
(
"No roomname given."
);
}
room
=
room
+
strlen
(
"roomname="
);
str
=
g_string_new
(
NULL
);
while
(
*
room
&&
(
*
room
!=
'&'
))
{
g_string_append_c
(
str
,
*
room
==
'+'
?
' '
:
*
room
);
room
++
;
}
room
=
g_strdup
(
str
->
str
);
g_string_free
(
str
,
TRUE
);
components
=
g_hash_table_new_full
(
g_str_hash
,
g_str_equal
,
g_free
,
g_free
);
g_hash_table_replace
(
components
,
g_strdup
(
"room"
),
room
);
g_hash_table_replace
(
components
,
g_strdup
(
"exchange"
),
g_strdup_printf
(
"%d"
,
exch
));
serv_join_chat
(
gc
,
components
);
g_hash_table_destroy
(
components
);
}
else
{
return
_
(
"Invalid AIM URI"
);
}
return
NULL
;
}
#if 0
static guchar *
UI_build(guint32 *len, guchar type, guchar subtype, va_list args)
{
guchar *buffer;
guint32 pos;
int size;
void *data;
*len = sizeof(guchar) * 2 + 4;
buffer = g_malloc(*len);
pos = 0;
memcpy(buffer + pos, &type, sizeof(type)); pos += sizeof(type);
memcpy(buffer + pos, &subtype, sizeof(subtype)); pos += sizeof(subtype);
/* we come back and do size last */
pos += 4;
size = va_arg(args, int);
while (size != -1) {
*len += size;
buffer = g_realloc(buffer, *len);
data = va_arg(args, void *);
memcpy(buffer + pos, data, size);
pos += size;
size = va_arg(args, int);
}
pos -= sizeof(guchar) * 2 + 4;
/* now we do size */
memcpy(buffer + sizeof(guchar) * 2, &pos, 4);
return buffer;
}
static gint
UI_write(struct UI *ui, guchar *data, gint len)
{
GError *error = NULL;
gint sent;
/* we'll let the write silently fail because the read will pick it up as dead */
g_io_channel_write_chars(ui->channel, data, len, &sent, &error);
if (error)
g_error_free(error);
return sent;
}
static void
UI_build_write(struct UI *ui, guchar type, guchar subtype, ...)
{
va_list ap;
gchar *data;
guint32 len;
va_start(ap, subtype);
data = UI_build(&len, type, subtype, ap);
va_end(ap);
UI_write(ui, data, len);
g_free(data);
}
static void
UI_broadcast(guchar *data, gint len)
{
GSList *u = uis;
while (u) {
struct UI *ui = u->data;
UI_write(ui, data, len);
u = u->next;
}
}
static void
UI_build_broadcast(guchar type, guchar subtype, ...)
{
va_list ap;
gchar *data;
guint32 len;
if (!uis)
return;
va_start(ap, subtype);
data = UI_build(&len, type, subtype, ap);
va_end(ap);
UI_broadcast(data, len);
g_free(data);
}
#endif
#ifndef _WIN32
static
void
meta_handler
(
struct
UI
*
ui
,
guchar
subtype
,
gchar
*
data
)
{
GaimRemotePacket
*
p
;
GError
*
error
=
NULL
;
switch
(
subtype
)
{
case
CUI_META_LIST
:
break
;
case
CUI_META_QUIT
:
while
(
uis
)
{
ui
=
uis
->
data
;
uis
=
g_slist_remove
(
uis
,
ui
);
g_io_channel_shutdown
(
ui
->
channel
,
TRUE
,
&
error
);
g_source_remove
(
ui
->
inpa
);
g_free
(
ui
);
}
g_timeout_add
(
0
,
gaim_core_quit_cb
,
NULL
);
break
;
case
CUI_META_DETACH
:
uis
=
g_slist_remove
(
uis
,
ui
);
g_io_channel_shutdown
(
ui
->
channel
,
TRUE
,
&
error
);
g_source_remove
(
ui
->
inpa
);
g_free
(
ui
);
break
;
case
CUI_META_PING
:
p
=
gaim_remote_packet_new
(
CUI_TYPE_META
,
CUI_META_ACK
);
gaim_remote_session_send_packet
(
g_io_channel_unix_get_fd
(
ui
->
channel
),
p
);
gaim_remote_packet_free
(
p
);
break
;
default
:
gaim_debug
(
GAIM_DEBUG_WARNING
,
"cui"
,
"Unhandled meta subtype %d
\n
"
,
subtype
);
break
;
}
if
(
error
)
g_error_free
(
error
);
}
static
void
plugin_handler
(
struct
UI
*
ui
,
guchar
subtype
,
gpointer
data
)
{
#ifdef GAIM_PLUGINS
guint
id
;
GaimPlugin
*
p
;
switch
(
subtype
)
{
/*
case CUI_PLUGIN_LIST:
break;
*/
case
CUI_PLUGIN_LOAD
:
gaim_plugin_load
(
gaim_plugin_probe
(
data
));
break
;
case
CUI_PLUGIN_UNLOAD
:
memcpy
(
&
id
,
data
,
sizeof
(
id
));
p
=
g_list_nth_data
(
gaim_plugins_get_loaded
(),
id
);
if
(
p
)
{
gaim_plugin_unload
(
p
);
}
break
;
default
:
gaim_debug
(
GAIM_DEBUG_WARNING
,
"cui"
,
"Unhandled plugin subtype %d
\n
"
,
subtype
);
break
;
}
#endif
}
static
void
user_handler
(
struct
UI
*
ui
,
guchar
subtype
,
gchar
*
data
)
{
guint
id
;
GaimAccount
*
account
;
switch
(
subtype
)
{
/*
case CUI_USER_LIST:
break;
case CUI_USER_ADD:
break;
case CUI_USER_REMOVE:
break;
case CUI_USER_MODIFY:
break;
*/
case
CUI_USER_SIGNON
:
if
(
!
data
)
return
;
memcpy
(
&
id
,
data
,
sizeof
(
id
));
account
=
g_list_nth_data
(
gaim_accounts_get_all
(),
id
);
if
(
account
)
gaim_account_connect
(
account
);
/* don't need to do anything here because the UI will get updates from other handlers */
break
;
case
CUI_USER_AWAY
:
{
GSList
*
l
;
const
char
*
default_away_name
=
gaim_prefs_get_string
(
"/core/away/default_message"
);
for
(
l
=
away_messages
;
l
;
l
=
l
->
next
)
{
if
(
!
strcmp
(
default_away_name
,
((
struct
away_message
*
)
l
->
data
)
->
name
))
{
do_away_message
(
NULL
,
l
->
data
);
break
;
}
}
}
break
;
case
CUI_USER_BACK
:
do_im_back
(
NULL
,
NULL
);
break
;
default
:
gaim_debug
(
GAIM_DEBUG_WARNING
,
"cui"
,
"Unhandled user subtype %d
\n
"
,
subtype
);
break
;
}
}
static
void
message_handler
(
struct
UI
*
ui
,
guchar
subtype
,
gchar
*
data
)
{
switch
(
subtype
)
{
case
CUI_MESSAGE_LIST
:
break
;
case
CUI_MESSAGE_SEND
:
if
(
!
data
)
return
;
{
guint
id
;
GaimConnection
*
gc
;
guint
len
;
char
*
who
,
*
msg
;
gint
flags
;
int
pos
=
0
;
memcpy
(
&
id
,
data
+
pos
,
sizeof
(
id
));
pos
+=
sizeof
(
id
);
gc
=
g_list_nth_data
(
gaim_connections_get_all
(),
id
);
if
(
!
gc
)
return
;
memcpy
(
&
len
,
data
+
pos
,
sizeof
(
len
));
pos
+=
sizeof
(
len
);
who
=
g_strndup
(
data
+
pos
,
len
+
1
);
pos
+=
len
;
memcpy
(
&
len
,
data
+
pos
,
sizeof
(
len
));
pos
+=
sizeof
(
len
);
msg
=
g_strndup
(
data
+
pos
,
len
+
1
);
pos
+=
len
;
memcpy
(
&
flags
,
data
+
pos
,
sizeof
(
flags
));
serv_send_im
(
gc
,
who
,
msg
,
flags
);
g_free
(
who
);
g_free
(
msg
);
}
break
;
case
CUI_MESSAGE_RECV
:
break
;
default
:
gaim_debug
(
GAIM_DEBUG_WARNING
,
"cui"
,
"Unhandled message subtype %d
\n
"
,
subtype
);
break
;
}
}
static
gint
gaim_recv
(
GIOChannel
*
source
,
gchar
*
buf
,
gint
len
)
{
gint
total
=
0
;
guint
cur
;
GError
*
error
=
NULL
;
while
(
total
<
len
)
{
if
(
g_io_channel_read_chars
(
source
,
buf
+
total
,
len
-
total
,
(
gsize
*
)
&
cur
,
&
error
)
!=
G_IO_STATUS_NORMAL
)
{
if
(
error
)
g_error_free
(
error
);
return
-1
;
}
if
(
cur
==
0
)
return
total
;
total
+=
cur
;
}
return
total
;
}
static
void
remote_handler
(
struct
UI
*
ui
,
guchar
subtype
,
gchar
*
data
,
int
len
)
{
const
char
*
resp
;
char
*
send
;
switch
(
subtype
)
{
case
CUI_REMOTE_CONNECTIONS
:
break
;
case
CUI_REMOTE_URI
:
send
=
g_malloc
(
len
+
1
);
memcpy
(
send
,
data
,
len
);
send
[
len
]
=
0
;
resp
=
gaim_remote_handle_uri
(
send
);
g_free
(
send
);
/* report error */
break
;
default
:
gaim_debug
(
GAIM_DEBUG_WARNING
,
"cui"
,
"Unhandled remote subtype %d
\n
"
,
subtype
);
break
;
}
}
static
gboolean
UI_readable
(
GIOChannel
*
source
,
GIOCondition
cond
,
gpointer
data
)
{
struct
UI
*
ui
=
data
;
gchar
type
;
gchar
subtype
;
gint
len
;
GError
*
error
=
NULL
;
gchar
*
in
;
/* no byte order worries! this'll change if we go to TCP */
if
(
gaim_recv
(
source
,
&
type
,
sizeof
(
type
))
!=
sizeof
(
type
))
{
gaim_debug
(
GAIM_DEBUG_ERROR
,
"cui"
,
"UI has abandoned us!
\n
"
);
uis
=
g_slist_remove
(
uis
,
ui
);
g_io_channel_shutdown
(
ui
->
channel
,
TRUE
,
&
error
);
if
(
error
)
{
g_error_free
(
error
);
error
=
NULL
;
}
g_source_remove
(
ui
->
inpa
);
g_free
(
ui
);
return
FALSE
;
}
if
(
gaim_recv
(
source
,
&
subtype
,
sizeof
(
subtype
))
!=
sizeof
(
subtype
))
{
gaim_debug
(
GAIM_DEBUG_ERROR
,
"cui"
,
"UI has abandoned us!
\n
"
);
uis
=
g_slist_remove
(
uis
,
ui
);
g_io_channel_shutdown
(
ui
->
channel
,
TRUE
,
&
error
);
if
(
error
)
{
g_error_free
(
error
);
error
=
NULL
;
}
g_source_remove
(
ui
->
inpa
);
g_free
(
ui
);
return
FALSE
;
}
if
(
gaim_recv
(
source
,
(
gchar
*
)
&
len
,
sizeof
(
len
))
!=
sizeof
(
len
))
{
gaim_debug
(
GAIM_DEBUG_ERROR
,
"cui"
,
"UI has abandoned us!
\n
"
);
uis
=
g_slist_remove
(
uis
,
ui
);
g_io_channel_shutdown
(
ui
->
channel
,
TRUE
,
&
error
);
if
(
error
)
{
g_error_free
(
error
);
error
=
NULL
;
}
g_source_remove
(
ui
->
inpa
);
g_free
(
ui
);
return
FALSE
;
}
if
(
len
)
{
in
=
g_new0
(
gchar
,
len
);
if
(
gaim_recv
(
source
,
in
,
len
)
!=
len
)
{
gaim_debug
(
GAIM_DEBUG_ERROR
,
"cui"
,
"UI has abandoned us!
\n
"
);
uis
=
g_slist_remove
(
uis
,
ui
);
g_io_channel_shutdown
(
ui
->
channel
,
TRUE
,
&
error
);
if
(
error
)
{
g_error_free
(
error
);
error
=
NULL
;
}
g_source_remove
(
ui
->
inpa
);
g_free
(
ui
);
return
FALSE
;
}
}
else
in
=
NULL
;
switch
(
type
)
{
case
CUI_TYPE_META
:
meta_handler
(
ui
,
subtype
,
in
);
break
;
case
CUI_TYPE_PLUGIN
:
plugin_handler
(
ui
,
subtype
,
in
);
break
;
case
CUI_TYPE_USER
:
user_handler
(
ui
,
subtype
,
in
);
break
;
/*
case CUI_TYPE_CONN:
conn_handler(ui, subtype, in);
break;
case CUI_TYPE_BUDDY:
buddy_handler(ui, subtype, in);
break;
*/
case
CUI_TYPE_MESSAGE
:
message_handler
(
ui
,
subtype
,
in
);
break
;
/*
case CUI_TYPE_CHAT:
chat_handler(ui, subtype, in);
break;
*/
case
CUI_TYPE_REMOTE
:
remote_handler
(
ui
,
subtype
,
in
,
len
);
break
;
default
:
gaim_debug
(
GAIM_DEBUG_WARNING
,
"cui"
,
"Unhandled type %d
\n
"
,
type
);
break
;
}
if
(
in
)
g_free
(
in
);
return
TRUE
;
}
static
gboolean
socket_readable
(
GIOChannel
*
source
,
GIOCondition
cond
,
gpointer
data
)
{
struct
sockaddr_un
saddr
;
guint
len
=
sizeof
(
saddr
);
gint
fd
;
struct
UI
*
ui
;
if
((
fd
=
accept
(
UI_fd
,
(
struct
sockaddr
*
)
&
saddr
,
&
len
))
==
-1
)
return
FALSE
;
ui
=
g_new0
(
struct
UI
,
1
);
uis
=
g_slist_append
(
uis
,
ui
);
ui
->
channel
=
g_io_channel_unix_new
(
fd
);
ui
->
inpa
=
g_io_add_watch
(
ui
->
channel
,
G_IO_IN
|
G_IO_HUP
|
G_IO_ERR
,
UI_readable
,
ui
);
g_io_channel_unref
(
ui
->
channel
);
gaim_debug
(
GAIM_DEBUG_MISC
,
"cui"
,
"Got one
\n
"
);
return
TRUE
;
}
static
gint
open_socket
(
char
**
error
)
{
struct
sockaddr_un
saddr
;
gint
fd
;
while
(
gaim_remote_session_exists
(
gaim_session
))
gaim_session
++
;
gaim_debug
(
GAIM_DEBUG_MISC
,
"cui"
,
"Session: %d
\n
"
,
gaim_session
);
if
((
fd
=
socket
(
AF_UNIX
,
SOCK_STREAM
,
0
))
!=
-1
)
{
mode_t
m
=
umask
(
0177
);
saddr
.
sun_family
=
AF_UNIX
;
g_snprintf
(
saddr
.
sun_path
,
sizeof
(
saddr
.
sun_path
),
"%s"
G_DIR_SEPARATOR_S
"gaim_%s.%d"
,
g_get_tmp_dir
(),
g_get_user_name
(),
gaim_session
);
/* Unlink any existing socket for this session */
unlink
(
saddr
.
sun_path
);
if
(
bind
(
fd
,
(
struct
sockaddr
*
)
&
saddr
,
sizeof
(
saddr
))
!=
-1
)
listen
(
fd
,
100
);
else
{
char
*
tmp
=
g_locale_to_utf8
(
strerror
(
errno
),
-1
,
NULL
,
NULL
,
NULL
);
*
error
=
g_strdup_printf
(
_
(
"Failed to assign %s to a socket:
\n
%s"
),
saddr
.
sun_path
,
tmp
);
g_log
(
NULL
,
G_LOG_LEVEL_CRITICAL
,
"Failed to assign %s to a socket (Error: %s)"
,
saddr
.
sun_path
,
tmp
);
g_free
(
tmp
);
umask
(
m
);
return
-1
;
}
umask
(
m
);
}
else
g_log
(
NULL
,
G_LOG_LEVEL_CRITICAL
,
"Unable to open socket: %s"
,
strerror
(
errno
));
return
fd
;
}
#endif
/*! _WIN32*/
static
gboolean
plugin_load
(
GaimPlugin
*
plugin
)
{
#ifndef _WIN32
GIOChannel
*
channel
;
char
*
buf
;
if
((
UI_fd
=
open_socket
(
&
buf
))
<
0
)
{
gaim_notify_error
(
NULL
,
NULL
,
_
(
"Unable to open socket"
),
buf
);
g_free
(
buf
);
return
FALSE
;
}
channel
=
g_io_channel_unix_new
(
UI_fd
);
watcher
=
g_io_add_watch
(
channel
,
G_IO_IN
,
socket_readable
,
NULL
);
g_io_channel_unref
(
channel
);
return
TRUE
;
#else
return
FALSE
;
#endif
}
static
gboolean
plugin_unload
(
GaimPlugin
*
plugin
)
{
/* don't save prefs after plugins are gone... */
#ifndef _WIN32
char
buf
[
1024
];
g_source_remove
(
watcher
);
close
(
UI_fd
);
g_snprintf
(
buf
,
sizeof
(
buf
),
"%s"
G_DIR_SEPARATOR_S
"gaim_%s.%d"
,
g_get_tmp_dir
(),
g_get_user_name
(),
gaim_session
);
unlink
(
buf
);
gaim_debug
(
GAIM_DEBUG_MISC
,
"core"
,
"Removed core
\n
"
);
return
TRUE
;
#else
return
FALSE
;
#endif
}
static
GaimPluginInfo
info
=
{
GAIM_PLUGIN_MAGIC
,
GAIM_MAJOR_VERSION
,
GAIM_MINOR_VERSION
,
GAIM_PLUGIN_STANDARD
,
/**< type */
GAIM_GTK_PLUGIN_TYPE
,
/**< ui_requirement */
0
,
/**< flags */
NULL
,
/**< dependencies */
GAIM_PRIORITY_DEFAULT
,
/**< priority */
REMOTE_PLUGIN_ID
,
/**< id */
N_
(
"Remote Control"
),
/**< name */
VERSION
,
/**< version */
/** summary */
N_
(
"Provides remote control for gaim applications."
),
/** description */
N_
(
"Gives Gaim the ability to be remote-controlled through third-party "
"applications or through the gaim-remote tool."
),
"Sean Egan <sean.egan@binghamton.edu>"
,
GAIM_WEBSITE
,
/**< homepage */
plugin_load
,
/**< load */
plugin_unload
,
/**< unload */
NULL
,
/**< destroy */
NULL
,
/**< ui_info */
NULL
,
/**< extra_info */
NULL
,
NULL
};
static
void
__init_plugin
(
GaimPlugin
*
plugin
)
{
}
/* This may be horribly wrong. Oh the mayhem! */
GAIM_INIT_PLUGIN
(
remote
,
__init_plugin
,
info
)