dcu-purple_ruby 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
}
|