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.
@@ -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
+
@@ -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
+
@@ -0,0 +1,6 @@
1
+ require 'mkmf'
2
+ $CFLAGS = "#{ENV['CFLAGS']} -Wall -O3 -g"
3
+ pkg_config 'purple'
4
+ pkg_config 'glib-2.0'
5
+ pkg_config 'gthread-2.0'
6
+ create_makefile('purple_ruby_ext')
@@ -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(&notify_ops);
430
+ set_callback(&notify_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(&notify_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
+ }