dcu-purple_ruby 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +10 -0
- data/History.txt +2 -0
- data/LICENSE +341 -0
- data/Manifest.txt +9 -0
- data/README.rdoc +62 -0
- data/Rakefile +51 -0
- data/examples/purplegw_example.rb +92 -0
- data/ext/account.c +162 -0
- data/ext/extconf.rb +6 -0
- data/ext/purple_ruby.c +934 -0
- data/ext/reconnect.c +166 -0
- data/lib/purple_ruby.rb +70 -0
- metadata +78 -0
@@ -0,0 +1,92 @@
|
|
1
|
+
#
|
2
|
+
#Example Usage:
|
3
|
+
#
|
4
|
+
#Start the daemon and receive IM:
|
5
|
+
#$ruby examples/purplegw_example.rb prpl-msn user@hotmail.com password prpl-jabber user@gmail.com password
|
6
|
+
#
|
7
|
+
#Send im:
|
8
|
+
#$ irb
|
9
|
+
#irb(main):001:0> require 'examples/purplegw_example'
|
10
|
+
#irb(main):007:0> PurpleGWExample.deliver 'prpl-jabber', 'friend@gmail.com', 'hello worlds!'
|
11
|
+
#
|
12
|
+
|
13
|
+
require 'socket'
|
14
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '../lib/purple_ruby'))
|
15
|
+
|
16
|
+
class PurpleGWExample
|
17
|
+
SERVER_IP = "127.0.0.1"
|
18
|
+
SERVER_PORT = 9877
|
19
|
+
|
20
|
+
def start configs
|
21
|
+
Purple.init :debug => false, :user_dir => File.dirname(__FILE__) #use 'true' if you want to see the debug messages
|
22
|
+
|
23
|
+
puts "Available protocols:", Purple.list_protocols
|
24
|
+
|
25
|
+
accounts = {}
|
26
|
+
configs.each {|config|
|
27
|
+
puts "logging in #{config[:username]} (#{config[:protocol]})..."
|
28
|
+
account = Purple.login(config[:protocol], config[:username], config[:password])
|
29
|
+
accounts[config[:protocol]] = account
|
30
|
+
}
|
31
|
+
|
32
|
+
#handle incoming im messages
|
33
|
+
Purple.watch_incoming_im do |acc, sender, message|
|
34
|
+
sender = sender[0...sender.index('/')] if sender.index('/') #discard anything after '/'
|
35
|
+
puts "recv: #{acc.username}, #{sender}, #{message}"
|
36
|
+
end
|
37
|
+
|
38
|
+
Purple.watch_signed_on_event do |acc|
|
39
|
+
puts "signed on: #{acc.username}"
|
40
|
+
end
|
41
|
+
|
42
|
+
Purple.watch_connection_error do |acc, type, description|
|
43
|
+
puts "connection_error: #{acc.username} #{type} #{description}"
|
44
|
+
true #'true': auto-reconnect; 'false': do nothing
|
45
|
+
end
|
46
|
+
|
47
|
+
#request can be: 'SSL Certificate Verification' etc
|
48
|
+
Purple.watch_request do |title, primary, secondary, who|
|
49
|
+
puts "request: #{title}, #{primary}, #{secondary}, #{who}"
|
50
|
+
true #'true': accept a request; 'false': ignore a request
|
51
|
+
end
|
52
|
+
|
53
|
+
#request for authorization when someone adds this account to their buddy list
|
54
|
+
Purple.watch_new_buddy do |acc, remote_user, message|
|
55
|
+
puts "new buddy: #{acc.username} #{remote_user} #{message}"
|
56
|
+
true #'true': accept; 'false': deny
|
57
|
+
end
|
58
|
+
|
59
|
+
Purple.watch_notify_message do |type, title, primary, secondary|
|
60
|
+
puts "notification: #{type}, #{title}, #{primary}, #{secondary}"
|
61
|
+
end
|
62
|
+
|
63
|
+
#listen a tcp port, parse incoming data and send it out.
|
64
|
+
#We assume the incoming data is in the following format (separated by comma):
|
65
|
+
#<protocol>,<user>,<message>
|
66
|
+
Purple.watch_incoming_ipc(SERVER_IP, SERVER_PORT) do |data|
|
67
|
+
protocol, user, message = data.split(",").collect{|x| x.chomp.strip}
|
68
|
+
puts "send: #{protocol},#{user},#{message}"
|
69
|
+
puts accounts[protocol].send_im(user, message)
|
70
|
+
end
|
71
|
+
|
72
|
+
Purple.main_loop_run
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.deliver(protocol, to_users, message)
|
76
|
+
to_users = [to_users] unless to_users.is_a?(Array)
|
77
|
+
to_users.each do |user|
|
78
|
+
t = TCPSocket.new(SERVER_IP, SERVER_PORT)
|
79
|
+
t.print "#{protocol},#{user},#{message}"
|
80
|
+
t.close
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
if ARGV.length >= 3
|
86
|
+
configs = []
|
87
|
+
configs << {:protocol => ARGV[0], :username => ARGV[1], :password => ARGV[2]}
|
88
|
+
configs << {:protocol => ARGV[3], :username => ARGV[4], :password => ARGV[5]} if ARGV.length >= 6
|
89
|
+
#add more accounts here if you like
|
90
|
+
PurpleGWExample.new.start configs
|
91
|
+
end
|
92
|
+
|
data/ext/account.c
ADDED
@@ -0,0 +1,162 @@
|
|
1
|
+
/*
|
2
|
+
* adopted from finch's gntaccount.c
|
3
|
+
*/
|
4
|
+
|
5
|
+
/* finch
|
6
|
+
*
|
7
|
+
* Finch is the legal property of its developers, whose names are too numerous
|
8
|
+
* to list here. Please refer to the COPYRIGHT file distributed with this
|
9
|
+
* source distribution.
|
10
|
+
*
|
11
|
+
* This program is free software; you can redistribute it and/or modify
|
12
|
+
* it under the terms of the GNU General Public License as published by
|
13
|
+
* the Free Software Foundation; either version 2 of the License, or
|
14
|
+
* (at your option) any later version.
|
15
|
+
*
|
16
|
+
* This program is distributed in the hope that it will be useful,
|
17
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
18
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
19
|
+
* GNU General Public License for more details.
|
20
|
+
*
|
21
|
+
* You should have received a copy of the GNU General Public License
|
22
|
+
* along with this program; if not, write to the Free Software
|
23
|
+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
|
24
|
+
*/
|
25
|
+
|
26
|
+
#include <libpurple/account.h>
|
27
|
+
#include <libpurple/conversation.h>
|
28
|
+
#include <libpurple/core.h>
|
29
|
+
#include <libpurple/debug.h>
|
30
|
+
#include <libpurple/cipher.h>
|
31
|
+
#include <libpurple/eventloop.h>
|
32
|
+
#include <libpurple/ft.h>
|
33
|
+
#include <libpurple/log.h>
|
34
|
+
#include <libpurple/notify.h>
|
35
|
+
#include <libpurple/prefs.h>
|
36
|
+
#include <libpurple/prpl.h>
|
37
|
+
#include <libpurple/pounce.h>
|
38
|
+
#include <libpurple/request.h>
|
39
|
+
#include <libpurple/savedstatuses.h>
|
40
|
+
#include <libpurple/sound.h>
|
41
|
+
#include <libpurple/status.h>
|
42
|
+
#include <libpurple/util.h>
|
43
|
+
#include <libpurple/whiteboard.h>
|
44
|
+
#include <libpurple/network.h>
|
45
|
+
|
46
|
+
#include <ruby.h>
|
47
|
+
|
48
|
+
extern ID CALL;
|
49
|
+
extern VALUE cAccount;
|
50
|
+
extern VALUE new_buddy_handler;
|
51
|
+
|
52
|
+
extern VALUE check_callback(VALUE, const char*);
|
53
|
+
|
54
|
+
static char *
|
55
|
+
make_info(PurpleAccount *account, PurpleConnection *gc, const char *remote_user,
|
56
|
+
const char *id, const char *alias, const char *msg)
|
57
|
+
{
|
58
|
+
if (msg != NULL && *msg == '\0')
|
59
|
+
msg = NULL;
|
60
|
+
|
61
|
+
return g_strdup_printf(_("%s%s%s%s has made %s his or her buddy%s%s"),
|
62
|
+
remote_user,
|
63
|
+
(alias != NULL ? " (" : ""),
|
64
|
+
(alias != NULL ? alias : ""),
|
65
|
+
(alias != NULL ? ")" : ""),
|
66
|
+
(id != NULL
|
67
|
+
? id
|
68
|
+
: (purple_connection_get_display_name(gc) != NULL
|
69
|
+
? purple_connection_get_display_name(gc)
|
70
|
+
: purple_account_get_username(account))),
|
71
|
+
(msg != NULL ? ": " : "."),
|
72
|
+
(msg != NULL ? msg : ""));
|
73
|
+
}
|
74
|
+
|
75
|
+
static void
|
76
|
+
notify_added(PurpleAccount *account, const char *remote_user,
|
77
|
+
const char *id, const char *alias,
|
78
|
+
const char *msg)
|
79
|
+
{
|
80
|
+
char *buffer;
|
81
|
+
PurpleConnection *gc;
|
82
|
+
|
83
|
+
gc = purple_account_get_connection(account);
|
84
|
+
|
85
|
+
buffer = make_info(account, gc, remote_user, id, alias, msg);
|
86
|
+
|
87
|
+
purple_notify_info(NULL, NULL, buffer, NULL);
|
88
|
+
|
89
|
+
g_free(buffer);
|
90
|
+
}
|
91
|
+
|
92
|
+
static void
|
93
|
+
request_add(PurpleAccount *account, const char *remote_user,
|
94
|
+
const char *id, const char *alias,
|
95
|
+
const char *message)
|
96
|
+
{
|
97
|
+
if (new_buddy_handler != Qnil) {
|
98
|
+
VALUE args[3];
|
99
|
+
args[0] = Data_Wrap_Struct(cAccount, NULL, NULL, account);
|
100
|
+
args[1] = rb_str_new2(NULL == remote_user ? "" : remote_user);
|
101
|
+
args[2] = rb_str_new2(NULL == message ? "" : message);
|
102
|
+
check_callback(new_buddy_handler, "new_buddy_handler");
|
103
|
+
VALUE v = rb_funcall2(new_buddy_handler, CALL, 3, args);
|
104
|
+
|
105
|
+
if (v != Qnil && v != Qfalse) {
|
106
|
+
PurpleConnection *gc = purple_account_get_connection(account);
|
107
|
+
if (g_list_find(purple_connections_get_all(), gc))
|
108
|
+
{
|
109
|
+
purple_blist_request_add_buddy(account, remote_user,
|
110
|
+
NULL, alias);
|
111
|
+
}
|
112
|
+
}
|
113
|
+
}
|
114
|
+
}
|
115
|
+
|
116
|
+
static void *request_authorize(PurpleAccount *account,
|
117
|
+
const char *remote_user,
|
118
|
+
const char *id,
|
119
|
+
const char *alias,
|
120
|
+
const char *message,
|
121
|
+
gboolean on_list,
|
122
|
+
PurpleAccountRequestAuthorizationCb auth_cb,
|
123
|
+
PurpleAccountRequestAuthorizationCb deny_cb,
|
124
|
+
void *user_data)
|
125
|
+
{
|
126
|
+
if (new_buddy_handler != Qnil) {
|
127
|
+
VALUE args[3];
|
128
|
+
args[0] = Data_Wrap_Struct(cAccount, NULL, NULL, account);
|
129
|
+
args[1] = rb_str_new2(NULL == remote_user ? "" : remote_user);
|
130
|
+
args[2] = rb_str_new2(NULL == message ? "" : message);
|
131
|
+
VALUE v = rb_funcall2((VALUE)new_buddy_handler, CALL, 3, args);
|
132
|
+
|
133
|
+
if (v != Qnil && v != Qfalse) {
|
134
|
+
auth_cb(user_data);
|
135
|
+
purple_blist_request_add_buddy(account, remote_user, NULL, alias);
|
136
|
+
} else {
|
137
|
+
deny_cb(user_data);
|
138
|
+
}
|
139
|
+
}
|
140
|
+
|
141
|
+
return NULL;
|
142
|
+
}
|
143
|
+
|
144
|
+
static void
|
145
|
+
request_close(void *uihandle)
|
146
|
+
{
|
147
|
+
purple_request_close(PURPLE_REQUEST_ACTION, uihandle);
|
148
|
+
}
|
149
|
+
|
150
|
+
PurpleAccountUiOps account_ops =
|
151
|
+
{
|
152
|
+
notify_added,
|
153
|
+
NULL,
|
154
|
+
request_add,
|
155
|
+
request_authorize,
|
156
|
+
request_close,
|
157
|
+
NULL,
|
158
|
+
NULL,
|
159
|
+
NULL,
|
160
|
+
NULL
|
161
|
+
};
|
162
|
+
|
data/ext/extconf.rb
ADDED
data/ext/purple_ruby.c
ADDED
@@ -0,0 +1,934 @@
|
|
1
|
+
/*
|
2
|
+
* Author: yong@intridea.com
|
3
|
+
*
|
4
|
+
* This program is free software; you can redistribute it and/or modify
|
5
|
+
* it under the terms of the GNU General Public License as published by
|
6
|
+
* the Free Software Foundation; either version 2 of the License, or
|
7
|
+
* (at your option) any later version.
|
8
|
+
*
|
9
|
+
* This program is distributed in the hope that it will be useful,
|
10
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
* GNU General Public License for more details.
|
13
|
+
*
|
14
|
+
* You should have received a copy of the GNU General Public License
|
15
|
+
* along with this program; if not, write to the Free Software
|
16
|
+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
|
17
|
+
*
|
18
|
+
*/
|
19
|
+
|
20
|
+
#include <libpurple/account.h>
|
21
|
+
#include <libpurple/accountopt.h>
|
22
|
+
#include <libpurple/conversation.h>
|
23
|
+
#include <libpurple/core.h>
|
24
|
+
#include <libpurple/debug.h>
|
25
|
+
#include <libpurple/cipher.h>
|
26
|
+
#include <libpurple/eventloop.h>
|
27
|
+
#include <libpurple/ft.h>
|
28
|
+
#include <libpurple/log.h>
|
29
|
+
#include <libpurple/notify.h>
|
30
|
+
#include <libpurple/prefs.h>
|
31
|
+
#include <libpurple/prpl.h>
|
32
|
+
#include <libpurple/pounce.h>
|
33
|
+
#include <libpurple/request.h>
|
34
|
+
#include <libpurple/savedstatuses.h>
|
35
|
+
#include <libpurple/sound.h>
|
36
|
+
#include <libpurple/status.h>
|
37
|
+
#include <libpurple/util.h>
|
38
|
+
#include <libpurple/whiteboard.h>
|
39
|
+
#include <libpurple/network.h>
|
40
|
+
|
41
|
+
#include <ruby.h>
|
42
|
+
#include <errno.h>
|
43
|
+
#include <signal.h>
|
44
|
+
#include <stdarg.h>
|
45
|
+
#include <unistd.h>
|
46
|
+
#include <netinet/in.h>
|
47
|
+
#include <sys/socket.h>
|
48
|
+
#include <arpa/inet.h>
|
49
|
+
#include <fcntl.h>
|
50
|
+
|
51
|
+
#define PURPLE_GLIB_READ_COND (G_IO_IN | G_IO_HUP | G_IO_ERR)
|
52
|
+
#define PURPLE_GLIB_WRITE_COND (G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL)
|
53
|
+
|
54
|
+
typedef struct _PurpleGLibIOClosure {
|
55
|
+
PurpleInputFunction function;
|
56
|
+
guint result;
|
57
|
+
gpointer data;
|
58
|
+
} PurpleGLibIOClosure;
|
59
|
+
|
60
|
+
static void purple_glib_io_destroy(gpointer data)
|
61
|
+
{
|
62
|
+
g_free(data);
|
63
|
+
}
|
64
|
+
|
65
|
+
static gboolean purple_glib_io_invoke(GIOChannel *source, GIOCondition condition, gpointer data)
|
66
|
+
{
|
67
|
+
PurpleGLibIOClosure *closure = data;
|
68
|
+
PurpleInputCondition purple_cond = 0;
|
69
|
+
|
70
|
+
if (condition & PURPLE_GLIB_READ_COND)
|
71
|
+
purple_cond |= PURPLE_INPUT_READ;
|
72
|
+
if (condition & PURPLE_GLIB_WRITE_COND)
|
73
|
+
purple_cond |= PURPLE_INPUT_WRITE;
|
74
|
+
|
75
|
+
closure->function(closure->data, g_io_channel_unix_get_fd(source),
|
76
|
+
purple_cond);
|
77
|
+
|
78
|
+
return TRUE;
|
79
|
+
}
|
80
|
+
|
81
|
+
static guint glib_input_add(gint fd, PurpleInputCondition condition, PurpleInputFunction function,
|
82
|
+
gpointer data)
|
83
|
+
{
|
84
|
+
PurpleGLibIOClosure *closure = g_new0(PurpleGLibIOClosure, 1);
|
85
|
+
GIOChannel *channel;
|
86
|
+
GIOCondition cond = 0;
|
87
|
+
|
88
|
+
closure->function = function;
|
89
|
+
closure->data = data;
|
90
|
+
|
91
|
+
if (condition & PURPLE_INPUT_READ)
|
92
|
+
cond |= PURPLE_GLIB_READ_COND;
|
93
|
+
if (condition & PURPLE_INPUT_WRITE)
|
94
|
+
cond |= PURPLE_GLIB_WRITE_COND;
|
95
|
+
|
96
|
+
channel = g_io_channel_unix_new(fd);
|
97
|
+
closure->result = g_io_add_watch_full(channel, G_PRIORITY_DEFAULT, cond,
|
98
|
+
purple_glib_io_invoke, closure, purple_glib_io_destroy);
|
99
|
+
|
100
|
+
g_io_channel_unref(channel);
|
101
|
+
return closure->result;
|
102
|
+
}
|
103
|
+
|
104
|
+
static PurpleEventLoopUiOps glib_eventloops =
|
105
|
+
{
|
106
|
+
g_timeout_add,
|
107
|
+
g_source_remove,
|
108
|
+
glib_input_add,
|
109
|
+
g_source_remove,
|
110
|
+
NULL,
|
111
|
+
#if GLIB_CHECK_VERSION(2,14,0)
|
112
|
+
g_timeout_add_seconds,
|
113
|
+
#else
|
114
|
+
NULL,
|
115
|
+
#endif
|
116
|
+
|
117
|
+
/* padding */
|
118
|
+
NULL,
|
119
|
+
NULL,
|
120
|
+
NULL
|
121
|
+
};
|
122
|
+
|
123
|
+
static VALUE cPurpleRuby, cProtocol;
|
124
|
+
VALUE cAccount;
|
125
|
+
const char* UI_ID = "purplegw";
|
126
|
+
static GMainLoop *main_loop = NULL;
|
127
|
+
static GHashTable* data_hash_table = NULL;
|
128
|
+
static GHashTable* fd_hash_table = NULL;
|
129
|
+
ID CALL, USER_DIR, DEBUG;
|
130
|
+
extern PurpleAccountUiOps account_ops;
|
131
|
+
|
132
|
+
static VALUE im_handler = Qnil;
|
133
|
+
static VALUE signed_on_handler = Qnil;
|
134
|
+
static VALUE connection_error_handler = Qnil;
|
135
|
+
static VALUE notify_message_handler = Qnil;
|
136
|
+
static VALUE request_handler = Qnil;
|
137
|
+
static VALUE ipc_handler = Qnil;
|
138
|
+
static VALUE timer_handler = Qnil;
|
139
|
+
guint timer_timeout = 0;
|
140
|
+
VALUE new_buddy_handler = Qnil;
|
141
|
+
|
142
|
+
extern void
|
143
|
+
finch_connection_report_disconnect(PurpleConnection *gc, PurpleConnectionError reason,
|
144
|
+
const char *text);
|
145
|
+
|
146
|
+
extern void finch_connections_init();
|
147
|
+
|
148
|
+
VALUE inspect_rb_obj(VALUE obj)
|
149
|
+
{
|
150
|
+
return rb_funcall(obj, rb_intern("inspect"), 0, 0);
|
151
|
+
}
|
152
|
+
|
153
|
+
void set_callback(VALUE* handler, const char* handler_name)
|
154
|
+
{
|
155
|
+
if (!rb_block_given_p()) {
|
156
|
+
rb_raise(rb_eArgError, "%s: no block", handler_name);
|
157
|
+
}
|
158
|
+
|
159
|
+
if (Qnil != *handler) {
|
160
|
+
rb_raise(rb_eArgError, "%s should only be assigned once", handler_name);
|
161
|
+
}
|
162
|
+
|
163
|
+
*handler = rb_block_proc();
|
164
|
+
/*
|
165
|
+
* If you create a Ruby object from C and store it in a C global variable without
|
166
|
+
* exporting it to Ruby, you must at least tell the garbage collector about it,
|
167
|
+
* lest ye be reaped inadvertently:
|
168
|
+
*/
|
169
|
+
rb_global_variable(handler);
|
170
|
+
|
171
|
+
if (rb_obj_class(*handler) != rb_cProc) {
|
172
|
+
rb_raise(rb_eTypeError, "%s got unexpected value: %s", handler_name,
|
173
|
+
RSTRING(inspect_rb_obj(*handler))->ptr);
|
174
|
+
}
|
175
|
+
}
|
176
|
+
|
177
|
+
void check_callback(VALUE handler, const char* handler_name){
|
178
|
+
if (rb_obj_class(handler) != rb_cProc) {
|
179
|
+
rb_raise(rb_eTypeError, "%s has unexpected value: %s",
|
180
|
+
handler_name,
|
181
|
+
RSTRING(inspect_rb_obj(handler))->ptr);
|
182
|
+
}
|
183
|
+
}
|
184
|
+
|
185
|
+
void report_disconnect(PurpleConnection *gc, PurpleConnectionError reason, const char *text)
|
186
|
+
{
|
187
|
+
if (Qnil != connection_error_handler) {
|
188
|
+
VALUE args[3];
|
189
|
+
args[0] = Data_Wrap_Struct(cAccount, NULL, NULL, purple_connection_get_account(gc));
|
190
|
+
args[1] = INT2FIX(reason);
|
191
|
+
args[2] = rb_str_new2(text);
|
192
|
+
check_callback(connection_error_handler, "connection_error_handler");
|
193
|
+
VALUE v = rb_funcall2(connection_error_handler, CALL, 3, args);
|
194
|
+
|
195
|
+
if (v != Qnil && v != Qfalse) {
|
196
|
+
finch_connection_report_disconnect(gc, reason, text);
|
197
|
+
}
|
198
|
+
}
|
199
|
+
}
|
200
|
+
|
201
|
+
static void* notify_message(PurpleNotifyMsgType type,
|
202
|
+
const char *title,
|
203
|
+
const char *primary,
|
204
|
+
const char *secondary)
|
205
|
+
{
|
206
|
+
if (notify_message_handler != Qnil) {
|
207
|
+
VALUE args[4];
|
208
|
+
args[0] = INT2FIX(type);
|
209
|
+
args[1] = rb_str_new2(NULL == title ? "" : title);
|
210
|
+
args[2] = rb_str_new2(NULL == primary ? "" : primary);
|
211
|
+
args[3] = rb_str_new2(NULL == secondary ? "" : secondary);
|
212
|
+
check_callback(notify_message_handler, "notify_message_handler");
|
213
|
+
rb_funcall2(notify_message_handler, CALL, 4, args);
|
214
|
+
}
|
215
|
+
|
216
|
+
return NULL;
|
217
|
+
}
|
218
|
+
|
219
|
+
static void write_conv(PurpleConversation *conv, const char *who, const char *alias,
|
220
|
+
const char *message, PurpleMessageFlags flags, time_t mtime)
|
221
|
+
{
|
222
|
+
if (im_handler != Qnil) {
|
223
|
+
PurpleAccount* account = purple_conversation_get_account(conv);
|
224
|
+
if (strcmp(purple_account_get_protocol_id(account), "prpl-msn") == 0 &&
|
225
|
+
(strstr(message, "Message could not be sent") != NULL ||
|
226
|
+
strstr(message, "Message was not sent") != NULL ||
|
227
|
+
strstr(message, "Message may have not been sent") != NULL
|
228
|
+
)
|
229
|
+
) {
|
230
|
+
/* I have seen error like 'msn: Connection error from Switchboard server'.
|
231
|
+
* In that case, libpurple will notify user with two regular im message.
|
232
|
+
* The first message is an error message, the second one is the original message that failed to send.
|
233
|
+
*/
|
234
|
+
notify_message(PURPLE_CONNECTION_ERROR_NETWORK_ERROR, message, purple_account_get_protocol_id(account), who);
|
235
|
+
} else {
|
236
|
+
VALUE args[3];
|
237
|
+
args[0] = Data_Wrap_Struct(cAccount, NULL, NULL, account);
|
238
|
+
args[1] = rb_str_new2(who);
|
239
|
+
args[2] = rb_str_new2(message);
|
240
|
+
check_callback(im_handler, "im_handler");
|
241
|
+
rb_funcall2(im_handler, CALL, 3, args);
|
242
|
+
}
|
243
|
+
}
|
244
|
+
}
|
245
|
+
|
246
|
+
static PurpleConversationUiOps conv_uiops =
|
247
|
+
{
|
248
|
+
NULL, /* create_conversation */
|
249
|
+
NULL, /* destroy_conversation */
|
250
|
+
NULL, /* write_chat */
|
251
|
+
NULL, /* write_im */
|
252
|
+
write_conv, /* write_conv */
|
253
|
+
NULL, /* chat_add_users */
|
254
|
+
NULL, /* chat_rename_user */
|
255
|
+
NULL, /* chat_remove_users */
|
256
|
+
NULL, /* chat_update_user */
|
257
|
+
NULL, /* present */
|
258
|
+
NULL, /* has_focus */
|
259
|
+
NULL, /* custom_smiley_add */
|
260
|
+
NULL, /* custom_smiley_write */
|
261
|
+
NULL, /* custom_smiley_close */
|
262
|
+
NULL, /* send_confirm */
|
263
|
+
NULL,
|
264
|
+
NULL,
|
265
|
+
NULL,
|
266
|
+
NULL
|
267
|
+
};
|
268
|
+
|
269
|
+
static PurpleConnectionUiOps connection_ops =
|
270
|
+
{
|
271
|
+
NULL, /* connect_progress */
|
272
|
+
NULL, /* connected */
|
273
|
+
NULL, /* disconnected */
|
274
|
+
NULL, /* notice */
|
275
|
+
NULL,
|
276
|
+
NULL, /* network_connected */
|
277
|
+
NULL, /* network_disconnected */
|
278
|
+
report_disconnect,
|
279
|
+
NULL,
|
280
|
+
NULL,
|
281
|
+
NULL
|
282
|
+
};
|
283
|
+
|
284
|
+
static void* request_action(const char *title, const char *primary, const char *secondary,
|
285
|
+
int default_action,
|
286
|
+
PurpleAccount *account,
|
287
|
+
const char *who,
|
288
|
+
PurpleConversation *conv,
|
289
|
+
void *user_data,
|
290
|
+
size_t action_count,
|
291
|
+
va_list actions)
|
292
|
+
{
|
293
|
+
if (request_handler != Qnil) {
|
294
|
+
VALUE args[4];
|
295
|
+
args[0] = rb_str_new2(NULL == title ? "" : title);
|
296
|
+
args[1] = rb_str_new2(NULL == primary ? "" : primary);
|
297
|
+
args[2] = rb_str_new2(NULL == secondary ? "" : secondary);
|
298
|
+
args[3] = rb_str_new2(NULL == who ? "" : who);
|
299
|
+
check_callback(request_handler, "request_handler");
|
300
|
+
VALUE v = rb_funcall2(request_handler, CALL, 4, args);
|
301
|
+
|
302
|
+
if (v != Qnil && v != Qfalse) {
|
303
|
+
/*const char *text =*/ va_arg(actions, const char *);
|
304
|
+
GCallback ok_cb = va_arg(actions, GCallback);
|
305
|
+
((PurpleRequestActionCb)ok_cb)(user_data, default_action);
|
306
|
+
}
|
307
|
+
}
|
308
|
+
|
309
|
+
return NULL;
|
310
|
+
}
|
311
|
+
|
312
|
+
static PurpleRequestUiOps request_ops =
|
313
|
+
{
|
314
|
+
NULL, /*request_input*/
|
315
|
+
NULL, /*request_choice*/
|
316
|
+
request_action, /*request_action*/
|
317
|
+
NULL, /*request_fields*/
|
318
|
+
NULL, /*request_file*/
|
319
|
+
NULL, /*close_request*/
|
320
|
+
NULL, /*request_folder*/
|
321
|
+
NULL,
|
322
|
+
NULL,
|
323
|
+
NULL,
|
324
|
+
NULL
|
325
|
+
};
|
326
|
+
|
327
|
+
static PurpleNotifyUiOps notify_ops =
|
328
|
+
{
|
329
|
+
notify_message, /*notify_message*/
|
330
|
+
NULL, /*notify_email*/
|
331
|
+
NULL, /*notify_emails*/
|
332
|
+
NULL, /*notify_formatted*/
|
333
|
+
NULL, /*notify_searchresults*/
|
334
|
+
NULL, /*notify_searchresults_new_rows*/
|
335
|
+
NULL, /*notify_userinfo*/
|
336
|
+
NULL, /*notify_uri*/
|
337
|
+
NULL, /*close_notify*/
|
338
|
+
NULL,
|
339
|
+
NULL,
|
340
|
+
NULL,
|
341
|
+
NULL,
|
342
|
+
};
|
343
|
+
|
344
|
+
static PurpleCoreUiOps core_uiops =
|
345
|
+
{
|
346
|
+
NULL,
|
347
|
+
NULL,
|
348
|
+
NULL,
|
349
|
+
NULL,
|
350
|
+
|
351
|
+
/* padding */
|
352
|
+
NULL,
|
353
|
+
NULL,
|
354
|
+
NULL,
|
355
|
+
NULL
|
356
|
+
};
|
357
|
+
|
358
|
+
//I have tried to detect Ctrl-C using ruby's trap method,
|
359
|
+
//but it does not work as expected: it can not detect Ctrl-C
|
360
|
+
//until a network event occurs
|
361
|
+
static void sighandler(int sig)
|
362
|
+
{
|
363
|
+
switch (sig) {
|
364
|
+
case SIGINT:
|
365
|
+
g_main_loop_quit(main_loop);
|
366
|
+
break;
|
367
|
+
}
|
368
|
+
}
|
369
|
+
|
370
|
+
static VALUE init(int argc, VALUE *argv, VALUE self)
|
371
|
+
{
|
372
|
+
VALUE debug, path, settings;
|
373
|
+
|
374
|
+
rb_scan_args(argc, argv, "01", &settings);
|
375
|
+
|
376
|
+
if (argc == 0) {
|
377
|
+
debug = Qnil;
|
378
|
+
path = Qnil;
|
379
|
+
}
|
380
|
+
else {
|
381
|
+
settings = rb_convert_type(settings, T_HASH, "Hash", "to_hash");
|
382
|
+
|
383
|
+
debug = rb_hash_aref(settings, ID2SYM(DEBUG));
|
384
|
+
path = rb_hash_aref(settings, ID2SYM(USER_DIR));
|
385
|
+
}
|
386
|
+
|
387
|
+
signal(SIGCHLD, SIG_IGN);
|
388
|
+
signal(SIGPIPE, SIG_IGN);
|
389
|
+
signal(SIGINT, sighandler);
|
390
|
+
|
391
|
+
data_hash_table = g_hash_table_new(NULL, NULL);
|
392
|
+
fd_hash_table = g_hash_table_new(NULL, NULL);
|
393
|
+
|
394
|
+
purple_debug_set_enabled(RTEST(debug) ? TRUE : FALSE);
|
395
|
+
|
396
|
+
if (path != Qnil) {
|
397
|
+
purple_util_set_user_dir(StringValueCStr(path));
|
398
|
+
}
|
399
|
+
|
400
|
+
purple_core_set_ui_ops(&core_uiops);
|
401
|
+
purple_eventloop_set_ui_ops(&glib_eventloops);
|
402
|
+
|
403
|
+
if (!purple_core_init(UI_ID)) {
|
404
|
+
rb_raise(rb_eRuntimeError, "libpurple initialization failed");
|
405
|
+
}
|
406
|
+
|
407
|
+
/* Create and load the buddylist. */
|
408
|
+
purple_set_blist(purple_blist_new());
|
409
|
+
purple_blist_load();
|
410
|
+
|
411
|
+
/* Load the preferences. */
|
412
|
+
purple_prefs_load();
|
413
|
+
|
414
|
+
/* Load the pounces. */
|
415
|
+
purple_pounces_load();
|
416
|
+
|
417
|
+
return Qnil;
|
418
|
+
}
|
419
|
+
|
420
|
+
static VALUE watch_incoming_im(VALUE self)
|
421
|
+
{
|
422
|
+
purple_conversations_set_ui_ops(&conv_uiops);
|
423
|
+
set_callback(&im_handler, "im_handler");
|
424
|
+
return im_handler;
|
425
|
+
}
|
426
|
+
|
427
|
+
static VALUE watch_notify_message(VALUE self)
|
428
|
+
{
|
429
|
+
purple_notify_set_ui_ops(¬ify_ops);
|
430
|
+
set_callback(¬ify_message_handler, "notify_message_handler");
|
431
|
+
return notify_message_handler;
|
432
|
+
}
|
433
|
+
|
434
|
+
static VALUE watch_request(VALUE self)
|
435
|
+
{
|
436
|
+
purple_request_set_ui_ops(&request_ops);
|
437
|
+
set_callback(&request_handler, "request_handler");
|
438
|
+
return request_handler;
|
439
|
+
}
|
440
|
+
|
441
|
+
static VALUE watch_new_buddy(VALUE self)
|
442
|
+
{
|
443
|
+
purple_accounts_set_ui_ops(&account_ops);
|
444
|
+
set_callback(&new_buddy_handler, "new_buddy_handler");
|
445
|
+
return new_buddy_handler;
|
446
|
+
}
|
447
|
+
|
448
|
+
static void signed_on(PurpleConnection* connection)
|
449
|
+
{
|
450
|
+
VALUE args[1];
|
451
|
+
args[0] = Data_Wrap_Struct(cAccount, NULL, NULL, purple_connection_get_account(connection));
|
452
|
+
check_callback(signed_on_handler, "signed_on_handler");
|
453
|
+
rb_funcall2(signed_on_handler, CALL, 1, args);
|
454
|
+
}
|
455
|
+
|
456
|
+
static VALUE watch_signed_on_event(VALUE self)
|
457
|
+
{
|
458
|
+
set_callback(&signed_on_handler, "signed_on_handler");
|
459
|
+
int handle;
|
460
|
+
purple_signal_connect(purple_connections_get_handle(), "signed-on", &handle,
|
461
|
+
PURPLE_CALLBACK(signed_on), NULL);
|
462
|
+
return signed_on_handler;
|
463
|
+
}
|
464
|
+
|
465
|
+
static VALUE watch_connection_error(VALUE self)
|
466
|
+
{
|
467
|
+
finch_connections_init();
|
468
|
+
purple_connections_set_ui_ops(&connection_ops);
|
469
|
+
|
470
|
+
set_callback(&connection_error_handler, "connection_error_handler");
|
471
|
+
|
472
|
+
/*int handle;
|
473
|
+
purple_signal_connect(purple_connections_get_handle(), "connection-error", &handle,
|
474
|
+
PURPLE_CALLBACK(connection_error), NULL);*/
|
475
|
+
return connection_error_handler;
|
476
|
+
}
|
477
|
+
|
478
|
+
static void _read_socket_handler(gpointer notused, int socket, PurpleInputCondition condition)
|
479
|
+
{
|
480
|
+
char message[4096] = {0};
|
481
|
+
int i = recv(socket, message, sizeof(message) - 1, 0);
|
482
|
+
if (i > 0) {
|
483
|
+
purple_debug_info("purple_ruby", "recv %d: %d\n", socket, i);
|
484
|
+
|
485
|
+
gpointer str = g_hash_table_lookup(data_hash_table, (gpointer)socket);
|
486
|
+
if (NULL == str) rb_raise(rb_eRuntimeError, "can not find socket: %d", socket);
|
487
|
+
rb_str_append((VALUE)str, rb_str_new2(message));
|
488
|
+
} else {
|
489
|
+
purple_debug_info("purple_ruby", "close connection %d: %d %d\n", socket, i, errno);
|
490
|
+
|
491
|
+
gpointer str = g_hash_table_lookup(data_hash_table, (gpointer)socket);
|
492
|
+
if (NULL == str) {
|
493
|
+
purple_debug_warning("purple_ruby", "can not find socket in data_hash_table %d\n", socket);
|
494
|
+
return;
|
495
|
+
}
|
496
|
+
|
497
|
+
gpointer purple_fd = g_hash_table_lookup(fd_hash_table, (gpointer)socket);
|
498
|
+
if (NULL == purple_fd) {
|
499
|
+
purple_debug_warning("purple_ruby", "can not find socket in fd_hash_table %d\n", socket);
|
500
|
+
return;
|
501
|
+
}
|
502
|
+
|
503
|
+
g_hash_table_remove(fd_hash_table, (gpointer)socket);
|
504
|
+
g_hash_table_remove(data_hash_table, (gpointer)socket);
|
505
|
+
purple_input_remove((guint)purple_fd);
|
506
|
+
close(socket);
|
507
|
+
|
508
|
+
VALUE args[1];
|
509
|
+
args[0] = (VALUE)str;
|
510
|
+
check_callback(ipc_handler, "ipc_handler");
|
511
|
+
rb_funcall2(ipc_handler, CALL, 1, args);
|
512
|
+
}
|
513
|
+
}
|
514
|
+
|
515
|
+
static void _accept_socket_handler(gpointer notused, int server_socket, PurpleInputCondition condition)
|
516
|
+
{
|
517
|
+
/* Check that it is a read condition */
|
518
|
+
if (condition != PURPLE_INPUT_READ)
|
519
|
+
return;
|
520
|
+
|
521
|
+
struct sockaddr_in their_addr; /* connector's address information */
|
522
|
+
socklen_t sin_size = sizeof(struct sockaddr);
|
523
|
+
int client_socket;
|
524
|
+
if ((client_socket = accept(server_socket, (struct sockaddr *)&their_addr, &sin_size)) == -1) {
|
525
|
+
purple_debug_warning("purple_ruby", "failed to accept %d: %d\n", client_socket, errno);
|
526
|
+
return;
|
527
|
+
}
|
528
|
+
|
529
|
+
int flags = fcntl(client_socket, F_GETFL);
|
530
|
+
fcntl(client_socket, F_SETFL, flags | O_NONBLOCK);
|
531
|
+
#ifndef _WIN32
|
532
|
+
fcntl(client_socket, F_SETFD, FD_CLOEXEC);
|
533
|
+
#endif
|
534
|
+
|
535
|
+
purple_debug_info("purple_ruby", "new connection: %d\n", client_socket);
|
536
|
+
|
537
|
+
guint purple_fd = purple_input_add(client_socket, PURPLE_INPUT_READ, _read_socket_handler, NULL);
|
538
|
+
|
539
|
+
g_hash_table_insert(data_hash_table, (gpointer)client_socket, (gpointer)rb_str_new2(""));
|
540
|
+
g_hash_table_insert(fd_hash_table, (gpointer)client_socket, (gpointer)purple_fd);
|
541
|
+
}
|
542
|
+
|
543
|
+
static VALUE watch_incoming_ipc(VALUE self, VALUE serverip, VALUE port)
|
544
|
+
{
|
545
|
+
struct sockaddr_in my_addr;
|
546
|
+
int soc;
|
547
|
+
int on = 1;
|
548
|
+
|
549
|
+
/* Open a listening socket for incoming conversations */
|
550
|
+
if ((soc = socket(PF_INET, SOCK_STREAM, 0)) < 0)
|
551
|
+
{
|
552
|
+
rb_raise(rb_eRuntimeError, "Cannot open socket: %s\n", g_strerror(errno));
|
553
|
+
return Qnil;
|
554
|
+
}
|
555
|
+
|
556
|
+
if (setsockopt(soc, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
|
557
|
+
{
|
558
|
+
rb_raise(rb_eRuntimeError, "SO_REUSEADDR failed: %s\n", g_strerror(errno));
|
559
|
+
return Qnil;
|
560
|
+
}
|
561
|
+
|
562
|
+
memset(&my_addr, 0, sizeof(struct sockaddr_in));
|
563
|
+
my_addr.sin_family = AF_INET;
|
564
|
+
my_addr.sin_addr.s_addr = inet_addr(StringValueCStr(serverip));
|
565
|
+
my_addr.sin_port = htons(FIX2INT(port));
|
566
|
+
if (bind(soc, (struct sockaddr*)&my_addr, sizeof(struct sockaddr)) != 0)
|
567
|
+
{
|
568
|
+
rb_raise(rb_eRuntimeError, "Unable to bind to port %d: %s\n", (int)FIX2INT(port), g_strerror(errno));
|
569
|
+
return Qnil;
|
570
|
+
}
|
571
|
+
|
572
|
+
/* Attempt to listen on the bound socket */
|
573
|
+
if (listen(soc, 10) != 0)
|
574
|
+
{
|
575
|
+
rb_raise(rb_eRuntimeError, "Cannot listen on socket: %s\n", g_strerror(errno));
|
576
|
+
return Qnil;
|
577
|
+
}
|
578
|
+
|
579
|
+
set_callback(&ipc_handler, "ipc_handler");
|
580
|
+
|
581
|
+
/* Open a watcher in the socket we have just opened */
|
582
|
+
purple_input_add(soc, PURPLE_INPUT_READ, _accept_socket_handler, NULL);
|
583
|
+
|
584
|
+
return port;
|
585
|
+
}
|
586
|
+
|
587
|
+
static gboolean
|
588
|
+
do_timeout(gpointer data)
|
589
|
+
{
|
590
|
+
VALUE handler = data;
|
591
|
+
check_callback(handler, "timer_handler");
|
592
|
+
VALUE v = rb_funcall(handler, CALL, 0, 0);
|
593
|
+
return (v == Qtrue);
|
594
|
+
}
|
595
|
+
|
596
|
+
static VALUE watch_timer(VALUE self, VALUE delay)
|
597
|
+
{
|
598
|
+
set_callback(&timer_handler, "timer_handler");
|
599
|
+
if (timer_timeout != 0)
|
600
|
+
g_source_remove(timer_timeout);
|
601
|
+
timer_timeout = g_timeout_add(delay, do_timeout, timer_handler);
|
602
|
+
return delay;
|
603
|
+
}
|
604
|
+
|
605
|
+
static VALUE login(VALUE self, VALUE protocol, VALUE username, VALUE password)
|
606
|
+
{
|
607
|
+
PurpleAccount* account = purple_account_new(StringValueCStr(username), StringValueCStr(protocol));
|
608
|
+
if (NULL == account || NULL == account->presence) {
|
609
|
+
rb_raise(rb_eRuntimeError, "No able to create account: %s", StringValueCStr(protocol));
|
610
|
+
}
|
611
|
+
|
612
|
+
purple_account_set_password(account, StringValueCStr(password));
|
613
|
+
purple_account_set_remember_password(account, TRUE);
|
614
|
+
purple_account_set_enabled(account, UI_ID, TRUE);
|
615
|
+
PurpleSavedStatus *status = purple_savedstatus_new(NULL, PURPLE_STATUS_AVAILABLE);
|
616
|
+
purple_savedstatus_activate(status);
|
617
|
+
|
618
|
+
return Data_Wrap_Struct(cAccount, NULL, NULL, account);
|
619
|
+
}
|
620
|
+
|
621
|
+
static VALUE main_loop_run(VALUE self)
|
622
|
+
{
|
623
|
+
main_loop = g_main_loop_new(NULL, FALSE);
|
624
|
+
g_main_loop_run(main_loop);
|
625
|
+
|
626
|
+
#ifdef DEBUG_MEM_LEAK
|
627
|
+
printf("QUIT!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
|
628
|
+
purple_core_quit();
|
629
|
+
if (im_handler == Qnil) rb_gc_unregister_address(&im_handler);
|
630
|
+
if (signed_on_handler == Qnil) rb_gc_unregister_address(&signed_on_handler);
|
631
|
+
if (connection_error_handler == Qnil) rb_gc_unregister_address(&connection_error_handler);
|
632
|
+
if (notify_message_handler == Qnil) rb_gc_unregister_address(¬ify_message_handler);
|
633
|
+
if (request_handler == Qnil) rb_gc_unregister_address(&request_handler);
|
634
|
+
if (ipc_handler == Qnil) rb_gc_unregister_address(&ipc_handler);
|
635
|
+
if (timer_timeout != 0) g_source_remove(timer_timeout);
|
636
|
+
if (timer_handler == Qnil) rb_gc_unregister_address(&timer_handler);
|
637
|
+
if (new_buddy_handler == Qnil) rb_gc_unregister_address(&new_buddy_handler);
|
638
|
+
rb_gc_start();
|
639
|
+
#endif
|
640
|
+
|
641
|
+
return Qnil;
|
642
|
+
}
|
643
|
+
|
644
|
+
static VALUE main_loop_stop(VALUE self)
|
645
|
+
{
|
646
|
+
g_main_loop_quit(main_loop);
|
647
|
+
return Qnil;
|
648
|
+
}
|
649
|
+
|
650
|
+
static VALUE send_im(VALUE self, VALUE name, VALUE message)
|
651
|
+
{
|
652
|
+
PurpleAccount *account;
|
653
|
+
Data_Get_Struct(self, PurpleAccount, account);
|
654
|
+
|
655
|
+
if (purple_account_is_connected(account)) {
|
656
|
+
int i = serv_send_im(purple_account_get_connection(account), StringValueCStr(name), StringValueCStr(message), 0);
|
657
|
+
return INT2FIX(i);
|
658
|
+
} else {
|
659
|
+
return Qnil;
|
660
|
+
}
|
661
|
+
}
|
662
|
+
|
663
|
+
static VALUE username(VALUE self)
|
664
|
+
{
|
665
|
+
PurpleAccount *account;
|
666
|
+
Data_Get_Struct(self, PurpleAccount, account);
|
667
|
+
return rb_str_new2(purple_account_get_username(account));
|
668
|
+
}
|
669
|
+
|
670
|
+
static VALUE protocol_id(VALUE self)
|
671
|
+
{
|
672
|
+
PurpleAccount *account;
|
673
|
+
Data_Get_Struct(self, PurpleAccount, account);
|
674
|
+
return rb_str_new2(purple_account_get_protocol_id(account));
|
675
|
+
}
|
676
|
+
|
677
|
+
static VALUE protocol_name(VALUE self)
|
678
|
+
{
|
679
|
+
PurpleAccount *account;
|
680
|
+
Data_Get_Struct(self, PurpleAccount, account);
|
681
|
+
return rb_str_new2(purple_account_get_protocol_name(account));
|
682
|
+
}
|
683
|
+
|
684
|
+
static VALUE get_bool_setting(VALUE self, VALUE name, VALUE default_value)
|
685
|
+
{
|
686
|
+
PurpleAccount *account;
|
687
|
+
Data_Get_Struct(self, PurpleAccount, account);
|
688
|
+
gboolean value = purple_account_get_bool(account, StringValueCStr(name), RTEST(default_value) ? TRUE : FALSE);
|
689
|
+
return (TRUE == value) ? Qtrue : Qfalse;
|
690
|
+
}
|
691
|
+
|
692
|
+
static VALUE set_bool_setting(VALUE self, VALUE name, VALUE value)
|
693
|
+
{
|
694
|
+
PurpleAccount *account;
|
695
|
+
Data_Get_Struct(self, PurpleAccount, account);
|
696
|
+
purple_account_set_bool(account, StringValueCStr(name), RTEST(value) ? TRUE : FALSE);
|
697
|
+
return value;
|
698
|
+
}
|
699
|
+
|
700
|
+
static VALUE get_int_setting(VALUE self, VALUE name, VALUE default_value)
|
701
|
+
{
|
702
|
+
PurpleAccount *account;
|
703
|
+
Data_Get_Struct(self, PurpleAccount, account);
|
704
|
+
return INT2FIX(purple_account_get_int(account, StringValueCStr(name), FIX2INT(default_value)));
|
705
|
+
}
|
706
|
+
|
707
|
+
static VALUE set_int_setting(VALUE self, VALUE name, VALUE value)
|
708
|
+
{
|
709
|
+
PurpleAccount *account;
|
710
|
+
Data_Get_Struct(self, PurpleAccount, account);
|
711
|
+
purple_account_set_int(account, StringValueCStr(name), FIX2INT(value));
|
712
|
+
return value;
|
713
|
+
}
|
714
|
+
|
715
|
+
static VALUE get_string_setting(VALUE self, VALUE name, VALUE default_value)
|
716
|
+
{
|
717
|
+
PurpleAccount *account;
|
718
|
+
Data_Get_Struct(self, PurpleAccount, account);
|
719
|
+
const char* value = purple_account_get_string(account, StringValueCStr(name), StringValueCStr(default_value));
|
720
|
+
return (NULL == value) ? Qnil : rb_str_new2(value);
|
721
|
+
}
|
722
|
+
|
723
|
+
static VALUE set_string_setting(VALUE self, VALUE name, VALUE value)
|
724
|
+
{
|
725
|
+
PurpleAccount *account;
|
726
|
+
Data_Get_Struct(self, PurpleAccount, account);
|
727
|
+
purple_account_set_string(account, StringValueCStr(name), StringValueCStr(value));
|
728
|
+
return value;
|
729
|
+
}
|
730
|
+
|
731
|
+
static VALUE list_protocols(VALUE self)
|
732
|
+
{
|
733
|
+
VALUE array = rb_ary_new();
|
734
|
+
|
735
|
+
GList *iter = purple_plugins_get_protocols();
|
736
|
+
int i;
|
737
|
+
for (i = 0; iter; iter = iter->next) {
|
738
|
+
VALUE protocol = Data_Wrap_Struct(cProtocol, NULL, NULL, iter->data);
|
739
|
+
rb_ary_push(array, protocol);
|
740
|
+
}
|
741
|
+
|
742
|
+
return array;
|
743
|
+
}
|
744
|
+
|
745
|
+
static VALUE add_buddy(VALUE self, VALUE buddy)
|
746
|
+
{
|
747
|
+
PurpleAccount *account;
|
748
|
+
Data_Get_Struct(self, PurpleAccount, account);
|
749
|
+
|
750
|
+
PurpleBuddy* pb = purple_buddy_new(account, StringValueCStr(buddy), NULL);
|
751
|
+
|
752
|
+
char* group = _("Buddies");
|
753
|
+
PurpleGroup* grp = purple_find_group(group);
|
754
|
+
if (!grp)
|
755
|
+
{
|
756
|
+
grp = purple_group_new(group);
|
757
|
+
purple_blist_add_group(grp, NULL);
|
758
|
+
}
|
759
|
+
|
760
|
+
purple_blist_add_buddy(pb, NULL, grp, NULL);
|
761
|
+
purple_account_add_buddy(account, pb);
|
762
|
+
return Qtrue;
|
763
|
+
}
|
764
|
+
|
765
|
+
static VALUE remove_buddy(VALUE self, VALUE buddy)
|
766
|
+
{
|
767
|
+
PurpleAccount *account;
|
768
|
+
Data_Get_Struct(self, PurpleAccount, account);
|
769
|
+
|
770
|
+
PurpleBuddy* pb = purple_find_buddy(account, StringValueCStr(buddy));
|
771
|
+
if (NULL == pb) {
|
772
|
+
rb_raise(rb_eRuntimeError, "Failed to remove buddy for %s : %s does not exist", purple_account_get_username(account), StringValueCStr(buddy));
|
773
|
+
}
|
774
|
+
|
775
|
+
char* group = _("Buddies");
|
776
|
+
PurpleGroup* grp = purple_find_group(group);
|
777
|
+
if (!grp)
|
778
|
+
{
|
779
|
+
grp = purple_group_new(group);
|
780
|
+
purple_blist_add_group(grp, NULL);
|
781
|
+
}
|
782
|
+
|
783
|
+
purple_blist_remove_buddy(pb);
|
784
|
+
purple_account_remove_buddy(account, pb, grp);
|
785
|
+
|
786
|
+
return Qtrue;
|
787
|
+
}
|
788
|
+
|
789
|
+
static VALUE has_buddy(VALUE self, VALUE buddy)
|
790
|
+
{
|
791
|
+
PurpleAccount *account;
|
792
|
+
Data_Get_Struct(self, PurpleAccount, account);
|
793
|
+
if (purple_find_buddy(account, StringValueCStr(buddy)) != NULL) {
|
794
|
+
return Qtrue;
|
795
|
+
} else {
|
796
|
+
return Qfalse;
|
797
|
+
}
|
798
|
+
}
|
799
|
+
|
800
|
+
static VALUE acc_delete(VALUE self)
|
801
|
+
{
|
802
|
+
PurpleAccount *account;
|
803
|
+
Data_Get_Struct(self, PurpleAccount, account);
|
804
|
+
purple_accounts_delete(account);
|
805
|
+
return Qnil;
|
806
|
+
}
|
807
|
+
|
808
|
+
static VALUE protocol_to_s(VALUE self)
|
809
|
+
{
|
810
|
+
PurplePlugin *protocol;
|
811
|
+
PurplePluginInfo *info;
|
812
|
+
|
813
|
+
Data_Get_Struct(self, PurplePlugin, protocol);
|
814
|
+
info = protocol->info;
|
815
|
+
if (info && info->name) {
|
816
|
+
char s[256];
|
817
|
+
snprintf(s, sizeof(s) -1, "%s %s", info->id, info->name);
|
818
|
+
return rb_str_new2(s);
|
819
|
+
}
|
820
|
+
|
821
|
+
return Qnil;
|
822
|
+
}
|
823
|
+
|
824
|
+
static VALUE protocol_get_id(VALUE self)
|
825
|
+
{
|
826
|
+
PurplePlugin *protocol;
|
827
|
+
PurplePluginInfo *info;
|
828
|
+
|
829
|
+
Data_Get_Struct(self, PurplePlugin, protocol);
|
830
|
+
info = protocol->info;
|
831
|
+
return info && info->id ? rb_str_new2(info->id) : Qnil;
|
832
|
+
}
|
833
|
+
|
834
|
+
static VALUE protocol_get_name(VALUE self)
|
835
|
+
{
|
836
|
+
PurplePlugin *protocol;
|
837
|
+
PurplePluginInfo *info;
|
838
|
+
|
839
|
+
Data_Get_Struct(self, PurplePlugin, protocol);
|
840
|
+
info = protocol->info;
|
841
|
+
return info && info->name ? rb_str_new2(info->name) : Qnil;
|
842
|
+
}
|
843
|
+
|
844
|
+
static VALUE protocol_get_default_options(VALUE self)
|
845
|
+
{
|
846
|
+
PurplePlugin *protocol;
|
847
|
+
PurplePluginProtocolInfo *prpl_info;
|
848
|
+
PurpleAccountOption *opt;
|
849
|
+
GList *opts;
|
850
|
+
const char *str_val;
|
851
|
+
VALUE h = rb_hash_new(), key, val;
|
852
|
+
|
853
|
+
Data_Get_Struct(self, PurplePlugin, protocol);
|
854
|
+
prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(protocol);
|
855
|
+
opts = prpl_info->protocol_options;
|
856
|
+
for (; opts; opts = opts->next) {
|
857
|
+
opt = (PurpleAccountOption *)opts->data;
|
858
|
+
key = rb_str_new2(opt->pref_name);
|
859
|
+
switch (opt->type)
|
860
|
+
{
|
861
|
+
case PURPLE_PREF_BOOLEAN:
|
862
|
+
val = purple_account_option_get_default_bool(opt) ? Qtrue : Qfalse;
|
863
|
+
break;
|
864
|
+
case PURPLE_PREF_INT:
|
865
|
+
val = INT2FIX(purple_account_option_get_default_int(opt));
|
866
|
+
break;
|
867
|
+
case PURPLE_PREF_STRING:
|
868
|
+
str_val = purple_account_option_get_default_string(opt);
|
869
|
+
val = str_val == NULL ? rb_str_new2("") : rb_str_new2(str_val);
|
870
|
+
break;
|
871
|
+
case PURPLE_PREF_STRING_LIST:
|
872
|
+
str_val = rb_str_new2(purple_account_option_get_default_list_value(opt));
|
873
|
+
val = str_val == NULL ? rb_str_new2("") : rb_str_new2(str_val);
|
874
|
+
break;
|
875
|
+
default:
|
876
|
+
key = Qnil;
|
877
|
+
}
|
878
|
+
|
879
|
+
if (key != Qnil)
|
880
|
+
rb_hash_aset(h, key, val);
|
881
|
+
}
|
882
|
+
|
883
|
+
return h;
|
884
|
+
}
|
885
|
+
|
886
|
+
void Init_purple_ruby_ext()
|
887
|
+
{
|
888
|
+
CALL = rb_intern("call");
|
889
|
+
DEBUG = rb_intern("debug");
|
890
|
+
USER_DIR = rb_intern("user_dir");
|
891
|
+
|
892
|
+
cPurpleRuby = rb_define_class("PurpleRuby", rb_cObject);
|
893
|
+
rb_define_singleton_method(cPurpleRuby, "init", init, -1);
|
894
|
+
rb_define_singleton_method(cPurpleRuby, "list_protocols", list_protocols, 0);
|
895
|
+
rb_define_singleton_method(cPurpleRuby, "watch_signed_on_event", watch_signed_on_event, 0);
|
896
|
+
rb_define_singleton_method(cPurpleRuby, "watch_connection_error", watch_connection_error, 0);
|
897
|
+
rb_define_singleton_method(cPurpleRuby, "watch_incoming_im", watch_incoming_im, 0);
|
898
|
+
rb_define_singleton_method(cPurpleRuby, "watch_notify_message", watch_notify_message, 0);
|
899
|
+
rb_define_singleton_method(cPurpleRuby, "watch_request", watch_request, 0);
|
900
|
+
rb_define_singleton_method(cPurpleRuby, "watch_new_buddy", watch_new_buddy, 0);
|
901
|
+
rb_define_singleton_method(cPurpleRuby, "watch_incoming_ipc", watch_incoming_ipc, 2);
|
902
|
+
rb_define_singleton_method(cPurpleRuby, "watch_timer", watch_timer, 1);
|
903
|
+
rb_define_singleton_method(cPurpleRuby, "login", login, 3);
|
904
|
+
rb_define_singleton_method(cPurpleRuby, "main_loop_run", main_loop_run, 0);
|
905
|
+
rb_define_singleton_method(cPurpleRuby, "main_loop_stop", main_loop_stop, 0);
|
906
|
+
|
907
|
+
rb_define_const(cPurpleRuby, "NOTIFY_MSG_ERROR", INT2NUM(PURPLE_NOTIFY_MSG_ERROR));
|
908
|
+
rb_define_const(cPurpleRuby, "NOTIFY_MSG_WARNING", INT2NUM(PURPLE_NOTIFY_MSG_WARNING));
|
909
|
+
rb_define_const(cPurpleRuby, "NOTIFY_MSG_INFO", INT2NUM(PURPLE_NOTIFY_MSG_INFO));
|
910
|
+
|
911
|
+
cAccount = rb_define_class_under(cPurpleRuby, "Account", rb_cObject);
|
912
|
+
rb_define_method(cAccount, "send_im", send_im, 2);
|
913
|
+
rb_define_method(cAccount, "username", username, 0);
|
914
|
+
rb_define_method(cAccount, "protocol_id", protocol_id, 0);
|
915
|
+
rb_define_method(cAccount, "protocol_name", protocol_name, 0);
|
916
|
+
rb_define_method(cAccount, "get_bool_setting", get_bool_setting, 2);
|
917
|
+
rb_define_method(cAccount, "set_bool_setting", set_bool_setting, 2);
|
918
|
+
rb_define_method(cAccount, "get_int_setting", get_int_setting, 2);
|
919
|
+
rb_define_method(cAccount, "set_int_setting", set_int_setting, 2);
|
920
|
+
rb_define_method(cAccount, "get_string_setting", get_string_setting, 2);
|
921
|
+
rb_define_method(cAccount, "set_string_setting", set_string_setting, 2);
|
922
|
+
rb_define_method(cAccount, "add_buddy", add_buddy, 1);
|
923
|
+
rb_define_method(cAccount, "remove_buddy", remove_buddy, 1);
|
924
|
+
rb_define_method(cAccount, "has_buddy?", has_buddy, 1);
|
925
|
+
rb_define_method(cAccount, "delete", acc_delete, 0);
|
926
|
+
|
927
|
+
cProtocol = rb_define_class_under(cPurpleRuby, "Protocol", rb_cObject);
|
928
|
+
rb_define_method(cProtocol, "default_options", protocol_get_default_options, 0);
|
929
|
+
rb_define_method(cProtocol, "id", protocol_get_id, 0);
|
930
|
+
rb_define_method(cProtocol, "name", protocol_get_name, 0);
|
931
|
+
rb_define_method(cProtocol, "to_str", protocol_to_s, 0);
|
932
|
+
rb_define_method(cProtocol, "to_s", protocol_to_s, 0);
|
933
|
+
|
934
|
+
}
|