yong-purplegw_ruby 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,2 @@
1
+ == 0.1.0
2
+
@@ -0,0 +1,7 @@
1
+ ext/extconf.rb
2
+ ext/purple_ruby.c
3
+ examples/purplegw_example.rb
4
+ Manifest.txt
5
+ History.txt
6
+ README.txt
7
+ Rakefile
@@ -0,0 +1,38 @@
1
+ == OVERVIEW
2
+
3
+ purple_ruby is a ruby gem to write servers that send and recive IM messages. It uses libpurple (http://developer.pidgin.im/wiki/WhatIsLibpurple) and therforce supports all protocols that Pidgin/Adium supports (MSN/Gtalk/Yahoo/AIM/ICQ etc).
4
+
5
+ Please check examples/purplegw_example.rb for details. Bascially you just tell it what to do when an IM was received, and there is an embedded tcp 'proxy' which allows you send IM messages.
6
+
7
+ Why not "ruburple"? I have used ruburple (http://rubyforge.org/projects/ruburple), but found it blocks a lot. libpurple needs to run its own event loop which interferes with ruby's green thread model. Ruburple's author has done lots of hard work to workaround the problem (http://rubyforge.org/pipermail/ruburple-development/2007-June/000005.html), but it does not work well.
8
+
9
+ == INSTALLATION
10
+
11
+ Linux (Ubuntu):
12
+ ---------------
13
+ apt-get install libpurple0 libpurple-dev
14
+ gem install yong-purple_ruby
15
+
16
+ OSX:
17
+ ----
18
+ sudo port -d selfupdate
19
+ sudo port install gnutls
20
+ (wait forever....)
21
+ sudo port install nss
22
+ (wait forever....)
23
+ wget http://downloads.sourceforge.net/pidgin/pidgin-2.5.5.tar.bz2
24
+ tar xvjf pidgin-2.5.5.tar.bz2
25
+ cd pidgin-2.5.5
26
+ ./configure --disable-gtkui --disable-screensaver --disable-consoleui --disable-sm --disable-perl --disable-tk --disable-tcl --disable-gstreamer --disable-schemas-install --disable-gestures --disable-cap --disable-gevolution --disable-gtkspell --disable-startup-notification --disable-avahi --disable-nm --disable-dbus --disable-meanwhile
27
+ make
28
+ (wait forever...)
29
+ sudo make install
30
+
31
+ edit your ~/.bash_profile and add this line
32
+ export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/usr/local/lib/pkgconfig
33
+
34
+ sudo gem install yong-purple_ruby
35
+
36
+
37
+
38
+
@@ -0,0 +1,24 @@
1
+ require 'rubygems'
2
+ require 'hoe'
3
+
4
+ EXT = "ext/ruburple_ext.#{Hoe::DLEXT}"
5
+
6
+ Hoe.new('purplegw_ruby', '0.1.0') do |p|
7
+ p.author = 'yong'
8
+ p.email = 'yong@intridea.com'
9
+ p.url = 'http://www.intridea.com'
10
+ p.summary = 'A ruby gem to write server that sends and recives IM messages'
11
+ p.description = 'A ruby gem to write server that sends and recives IM messages'
12
+
13
+ p.spec_extras[:extensions] = "ext/extconf.rb"
14
+ p.clean_globs << EXT << "ext/*.o" << "ext/Makefile"
15
+ end
16
+
17
+ task :test => EXT
18
+
19
+ file EXT => ["ext/extconf.rb", "ext/purple_ruby.c"] do
20
+ Dir.chdir "ext" do
21
+ ruby "extconf.rb"
22
+ sh "make"
23
+ end
24
+ end
@@ -0,0 +1,83 @@
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 'lib/purplegw_ruby'
10
+ #irb(main):007:0> PurpleGW.deliver 'prpl-jabber', 'friend@gmail.com', 'hello worlds!'
11
+ #
12
+
13
+ require 'hpricot'
14
+ require 'socket'
15
+ require File.expand_path(File.join(File.dirname(__FILE__), '../ext/purple_ruby'))
16
+
17
+ class PurpleGWExample
18
+ SERVER_IP = "127.0.0.1"
19
+ SERVER_PORT = 9876
20
+
21
+ def start configs
22
+ PurpleRuby.init false #use 'true' if you want to see the debug messages
23
+
24
+ puts "Available protocols:", PurpleRuby.list_protocols
25
+
26
+ accounts = {}
27
+ configs.each {|config|
28
+ account = PurpleRuby.login(config[:protocol], config[:username], config[:password])
29
+ accounts[config[:protocol]] = account
30
+ }
31
+
32
+ #handle incoming im messages
33
+ PurpleRuby.watch_incoming_im do |receiver, sender, message|
34
+ sender = sender[0...sender.index('/')] if sender.index('/') #discard anything after '/'
35
+ text = (Hpricot(message)).to_plain_text
36
+ puts "recv: #{receiver}, #{sender}, #{text}"
37
+ end
38
+
39
+ #TODO detect login failure
40
+ PurpleRuby.watch_signed_on_event do |acc|
41
+ puts "signed on: #{acc.username}"
42
+ end
43
+
44
+ #listen a tcp port, parse incoming data and send it out.
45
+ #We assume the incoming data is in the following format:
46
+ #<protocol> <user> <message>
47
+ PurpleRuby.watch_incoming_ipc(SERVER_IP, SERVER_PORT) do |data|
48
+ first_space = data.index(' ')
49
+ second_space = data.index(' ', first_space + 1)
50
+ protocol = data[0...first_space]
51
+ user = data[(first_space+1)...second_space]
52
+ message = data[(second_space+1)...-1]
53
+ puts "send: #{protocol}, #{user}, #{message}"
54
+ accounts[protocol].send_im(user, message)
55
+ end
56
+
57
+ trap("INT") {
58
+ #TODO ctrl-c can not be detected until a message is coming
59
+ puts 'Ctrl-C, quit...'
60
+ PurpleRuby.main_loop_stop
61
+ }
62
+
63
+ PurpleRuby.main_loop_run
64
+ end
65
+
66
+ def self.deliver(protocol, to_users, message)
67
+ to_users = [to_users] unless to_users.is_a?(Array)
68
+ to_users.each do |user|
69
+ t = TCPSocket.new(SERVER_IP, SERVER_PORT)
70
+ t.print "#{protocol} #{user} #{message}\n"
71
+ t.close
72
+ end
73
+ end
74
+ end
75
+
76
+ if ARGV.length >= 3
77
+ configs = []
78
+ configs << {:protocol => ARGV[0], :username => ARGV[1], :password => ARGV[2]}
79
+ configs << {:protocol => ARGV[3], :username => ARGV[4], :password => ARGV[5]} if ARGV.length >= 6
80
+ #add more accounts here if you like
81
+ PurpleGWExample.new.start configs
82
+ end
83
+
@@ -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')
@@ -0,0 +1,411 @@
1
+ #include <libpurple/account.h>
2
+ #include <libpurple/conversation.h>
3
+ #include <libpurple/core.h>
4
+ #include <libpurple/debug.h>
5
+ #include <libpurple/cipher.h>
6
+ #include <libpurple/eventloop.h>
7
+ #include <libpurple/ft.h>
8
+ #include <libpurple/log.h>
9
+ #include <libpurple/notify.h>
10
+ #include <libpurple/prefs.h>
11
+ #include <libpurple/prpl.h>
12
+ #include <libpurple/pounce.h>
13
+ #include <libpurple/request.h>
14
+ #include <libpurple/savedstatuses.h>
15
+ #include <libpurple/sound.h>
16
+ #include <libpurple/status.h>
17
+ #include <libpurple/util.h>
18
+ #include <libpurple/whiteboard.h>
19
+ #include <libpurple/network.h>
20
+
21
+ #include <ruby.h>
22
+ #include <errno.h>
23
+ #include <stdarg.h>
24
+ #include <netinet/in.h>
25
+ #include <sys/socket.h>
26
+ #include <arpa/inet.h>
27
+ #include <fcntl.h>
28
+
29
+ #define PURPLE_GLIB_READ_COND (G_IO_IN | G_IO_HUP | G_IO_ERR)
30
+ #define PURPLE_GLIB_WRITE_COND (G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL)
31
+
32
+ typedef struct _PurpleGLibIOClosure {
33
+ PurpleInputFunction function;
34
+ guint result;
35
+ gpointer data;
36
+ } PurpleGLibIOClosure;
37
+
38
+ static void purple_glib_io_destroy(gpointer data)
39
+ {
40
+ g_free(data);
41
+ }
42
+
43
+ static gboolean purple_glib_io_invoke(GIOChannel *source, GIOCondition condition, gpointer data)
44
+ {
45
+ PurpleGLibIOClosure *closure = data;
46
+ PurpleInputCondition purple_cond = 0;
47
+
48
+ if (condition & PURPLE_GLIB_READ_COND)
49
+ purple_cond |= PURPLE_INPUT_READ;
50
+ if (condition & PURPLE_GLIB_WRITE_COND)
51
+ purple_cond |= PURPLE_INPUT_WRITE;
52
+
53
+ closure->function(closure->data, g_io_channel_unix_get_fd(source),
54
+ purple_cond);
55
+
56
+ return TRUE;
57
+ }
58
+
59
+ static guint glib_input_add(gint fd, PurpleInputCondition condition, PurpleInputFunction function,
60
+ gpointer data)
61
+ {
62
+ PurpleGLibIOClosure *closure = g_new0(PurpleGLibIOClosure, 1);
63
+ GIOChannel *channel;
64
+ GIOCondition cond = 0;
65
+
66
+ closure->function = function;
67
+ closure->data = data;
68
+
69
+ if (condition & PURPLE_INPUT_READ)
70
+ cond |= PURPLE_GLIB_READ_COND;
71
+ if (condition & PURPLE_INPUT_WRITE)
72
+ cond |= PURPLE_GLIB_WRITE_COND;
73
+
74
+ channel = g_io_channel_unix_new(fd);
75
+ closure->result = g_io_add_watch_full(channel, G_PRIORITY_DEFAULT, cond,
76
+ purple_glib_io_invoke, closure, purple_glib_io_destroy);
77
+
78
+ g_io_channel_unref(channel);
79
+ return closure->result;
80
+ }
81
+
82
+ static PurpleEventLoopUiOps glib_eventloops =
83
+ {
84
+ g_timeout_add,
85
+ g_source_remove,
86
+ glib_input_add,
87
+ g_source_remove,
88
+ NULL,
89
+ #if GLIB_CHECK_VERSION(2,14,0)
90
+ g_timeout_add_seconds,
91
+ #else
92
+ NULL,
93
+ #endif
94
+
95
+ /* padding */
96
+ NULL,
97
+ NULL,
98
+ NULL
99
+ };
100
+
101
+ static VALUE cPurpleRuby;
102
+ static VALUE cAccount;
103
+ static char* UI_ID = "purplegw";
104
+ static GMainLoop *main_loop;
105
+ static VALUE im_hanlder;
106
+ static VALUE signed_on_hanlder;
107
+ static GHashTable* hash_table;
108
+
109
+ static void write_conv(PurpleConversation *conv, const char *who, const char *alias,
110
+ const char *message, PurpleMessageFlags flags, time_t mtime)
111
+ {
112
+ VALUE *args = g_new(VALUE, 3);
113
+ args[0] = rb_str_new2(purple_account_get_username(purple_conversation_get_account(conv)));
114
+ args[1] = rb_str_new2(who);
115
+ args[2] = rb_str_new2(message);
116
+ rb_funcall2(im_hanlder, rb_intern("call"), 3, args);
117
+ }
118
+
119
+ static PurpleConversationUiOps conv_uiops =
120
+ {
121
+ NULL, /* create_conversation */
122
+ NULL, /* destroy_conversation */
123
+ NULL, /* write_chat */
124
+ NULL, /* write_im */
125
+ write_conv, /* write_conv */
126
+ NULL, /* chat_add_users */
127
+ NULL, /* chat_rename_user */
128
+ NULL, /* chat_remove_users */
129
+ NULL, /* chat_update_user */
130
+ NULL, /* present */
131
+ NULL, /* has_focus */
132
+ NULL, /* custom_smiley_add */
133
+ NULL, /* custom_smiley_write */
134
+ NULL, /* custom_smiley_close */
135
+ NULL, /* send_confirm */
136
+ NULL,
137
+ NULL,
138
+ NULL,
139
+ NULL
140
+ };
141
+
142
+ static void ui_init(void)
143
+ {
144
+ /**
145
+ * This should initialize the UI components for all the modules. Here we
146
+ * just initialize the UI for conversations.
147
+ */
148
+ purple_conversations_set_ui_ops(&conv_uiops);
149
+ }
150
+
151
+ static PurpleCoreUiOps core_uiops =
152
+ {
153
+ NULL,
154
+ NULL,
155
+ ui_init,
156
+ NULL,
157
+
158
+ /* padding */
159
+ NULL,
160
+ NULL,
161
+ NULL,
162
+ NULL
163
+ };
164
+
165
+ static VALUE init(VALUE self, VALUE debug)
166
+ {
167
+ hash_table = g_hash_table_new(NULL, NULL);
168
+
169
+ purple_debug_set_enabled((debug == Qnil || debug == Qfalse) ? FALSE : TRUE);
170
+ purple_core_set_ui_ops(&core_uiops);
171
+ purple_eventloop_set_ui_ops(&glib_eventloops);
172
+
173
+ if (!purple_core_init(UI_ID)) {
174
+ rb_raise(rb_eRuntimeError, "libpurple initialization failed");
175
+ }
176
+
177
+ /* Create and load the buddylist. */
178
+ purple_set_blist(purple_blist_new());
179
+ purple_blist_load();
180
+
181
+ /* Load the preferences. */
182
+ purple_prefs_load();
183
+
184
+ /* Load the pounces. */
185
+ purple_pounces_load();
186
+
187
+ return Qnil;
188
+ }
189
+
190
+ static VALUE watch_incoming_im(VALUE self)
191
+ {
192
+ im_hanlder = rb_block_proc();
193
+ return im_hanlder;
194
+ }
195
+
196
+ static void signed_on(PurpleConnection* connection)
197
+ {
198
+ VALUE *args = g_new(VALUE, 1);
199
+ args[0] = Data_Wrap_Struct(cAccount, NULL, NULL, purple_connection_get_account(connection));
200
+ rb_funcall2((VALUE)signed_on_hanlder, rb_intern("call"), 1, args);
201
+ }
202
+
203
+ static VALUE watch_signed_on_event(VALUE self)
204
+ {
205
+ signed_on_hanlder = rb_block_proc();
206
+ int handle;
207
+ purple_signal_connect(purple_connections_get_handle(), "signed-on", &handle,
208
+ PURPLE_CALLBACK(signed_on), NULL);
209
+ return signed_on_hanlder;
210
+ }
211
+
212
+ static void _read_socket_handler(gpointer data, int socket, PurpleInputCondition condition)
213
+ {
214
+ char message[4096] = {0};
215
+ int i = recv(socket, message, sizeof(message) - 1, 0);
216
+ if (i > 0) {
217
+ //printf("recv %d %d\n", socket, i);
218
+
219
+ VALUE str = (VALUE)g_hash_table_lookup(hash_table, (gpointer)socket);
220
+ if (NULL == str) rb_raise(rb_eRuntimeError, "can not find socket: %d", socket);
221
+ rb_str_append(str, rb_str_new2(message));
222
+ } else {
223
+ //printf("closed %d %d %s\n", socket, i, g_strerror(errno));
224
+
225
+ VALUE str = (VALUE)g_hash_table_lookup(hash_table, (gpointer)socket);
226
+ if (NULL == str) return;
227
+
228
+ close(socket);
229
+ purple_input_remove(socket);
230
+ g_hash_table_remove(hash_table, (gpointer)socket);
231
+
232
+ VALUE *args = g_new(VALUE, 1);
233
+ args[0] = str;
234
+ rb_funcall2((VALUE)data, rb_intern("call"), 1, args);
235
+ }
236
+ }
237
+
238
+ static void _accept_socket_handler(gpointer data, int server_socket, PurpleInputCondition condition)
239
+ {
240
+ /* Check that it is a read condition */
241
+ if (condition != PURPLE_INPUT_READ)
242
+ return;
243
+
244
+ struct sockaddr_in their_addr; /* connector's address information */
245
+ socklen_t sin_size = sizeof(struct sockaddr);
246
+ int client_socket;
247
+ if ((client_socket = accept(server_socket, (struct sockaddr *)&their_addr, &sin_size)) == -1) {
248
+ return;
249
+ }
250
+
251
+ int flags = fcntl(client_socket, F_GETFL);
252
+ fcntl(client_socket, F_SETFL, flags | O_NONBLOCK);
253
+ #ifndef _WIN32
254
+ fcntl(client_socket, F_SETFD, FD_CLOEXEC);
255
+ #endif
256
+
257
+ //printf("new connection: %d\n", client_socket);
258
+
259
+ g_hash_table_insert(hash_table, (gpointer)client_socket, (gpointer)rb_str_new2(""));
260
+
261
+ purple_input_add(client_socket, PURPLE_INPUT_READ, _read_socket_handler, data);
262
+ }
263
+
264
+ static VALUE watch_incoming_ipc(VALUE self, VALUE serverip, VALUE port)
265
+ {
266
+ struct sockaddr_in my_addr;
267
+ int soc;
268
+
269
+ /* Open a listening socket for incoming conversations */
270
+ if ((soc = socket(PF_INET, SOCK_STREAM, 0)) < 0)
271
+ {
272
+ rb_raise(rb_eRuntimeError, "Cannot open socket: %s\n", g_strerror(errno));
273
+ return Qnil;
274
+ }
275
+
276
+ memset(&my_addr, 0, sizeof(struct sockaddr_in));
277
+ my_addr.sin_family = AF_INET;
278
+ my_addr.sin_addr.s_addr = inet_addr(RSTRING(serverip)->ptr);
279
+ my_addr.sin_port = htons(FIX2INT(port));
280
+ if (bind(soc, (struct sockaddr*)&my_addr, sizeof(struct sockaddr)) != 0)
281
+ {
282
+ rb_raise(rb_eRuntimeError, "Unable to bind to port %d: %s\n", (int)FIX2INT(port), g_strerror(errno));
283
+ return Qnil;
284
+ }
285
+
286
+ /* Attempt to listen on the bound socket */
287
+ if (listen(soc, 10) != 0)
288
+ {
289
+ rb_raise(rb_eRuntimeError, "Cannot listen on socket: %s\n", g_strerror(errno));
290
+ return Qnil;
291
+ }
292
+
293
+ VALUE proc = rb_block_proc();
294
+
295
+ /* Open a watcher in the socket we have just opened */
296
+ purple_input_add(soc, PURPLE_INPUT_READ, _accept_socket_handler, (gpointer)proc);
297
+
298
+ return port;
299
+ }
300
+
301
+ static VALUE login(VALUE self, VALUE protocol, VALUE username, VALUE password)
302
+ {
303
+ PurpleAccount* account = purple_account_new(RSTRING(username)->ptr, RSTRING(protocol)->ptr);
304
+ purple_account_set_password(account, RSTRING(password)->ptr);
305
+ purple_account_set_enabled(account, UI_ID, TRUE);
306
+ PurpleSavedStatus *status = purple_savedstatus_new(NULL, PURPLE_STATUS_AVAILABLE);
307
+ purple_savedstatus_activate(status);
308
+
309
+ return Data_Wrap_Struct(cAccount, NULL, NULL, account);
310
+ }
311
+
312
+ static VALUE main_loop_run(VALUE self)
313
+ {
314
+ main_loop = g_main_loop_new(NULL, FALSE);
315
+ g_main_loop_run(main_loop);
316
+ return Qnil;
317
+ }
318
+
319
+ static VALUE main_loop_stop(VALUE self)
320
+ {
321
+ g_main_loop_quit(main_loop);
322
+ return Qnil;
323
+ }
324
+
325
+ static VALUE send_im(VALUE self, VALUE name, VALUE message)
326
+ {
327
+ PurpleAccount *account;
328
+ Data_Get_Struct(self, PurpleAccount, account);
329
+
330
+ if (purple_account_is_connected(account)) {
331
+ int i = serv_send_im(purple_account_get_connection(account), RSTRING(name)->ptr, RSTRING(message)->ptr, 0);
332
+ return INT2FIX(i);
333
+ } else {
334
+ return Qnil;
335
+ }
336
+ }
337
+
338
+ static VALUE username(VALUE self)
339
+ {
340
+ PurpleAccount *account;
341
+ Data_Get_Struct(self, PurpleAccount, account);
342
+ return rb_str_new2(purple_account_get_username(account));
343
+ }
344
+
345
+ static VALUE list_protocols(VALUE self)
346
+ {
347
+ VALUE array = rb_ary_new();
348
+
349
+ GList *iter = purple_plugins_get_protocols();
350
+ int i;
351
+ for (i = 0; iter; iter = iter->next) {
352
+ PurplePlugin *plugin = iter->data;
353
+ PurplePluginInfo *info = plugin->info;
354
+ if (info && info->name) {
355
+ char s[256];
356
+ snprintf(s, sizeof(s) -1, "%s %s", info->id, info->name);
357
+ rb_ary_push(array, rb_str_new2(s));
358
+ }
359
+ }
360
+
361
+ return array;
362
+ }
363
+
364
+ static VALUE add_buddy(VALUE self, VALUE buddy)
365
+ {
366
+ PurpleAccount *account;
367
+ Data_Get_Struct(self, PurpleAccount, account);
368
+
369
+ char* group = _("Buddies");
370
+ PurpleGroup* grp = purple_find_group(group);
371
+ if (!grp)
372
+ {
373
+ grp = purple_group_new(group);
374
+ purple_blist_add_group(grp, NULL);
375
+ }
376
+
377
+ PurpleBuddy* pb = purple_buddy_new(account, RSTRING(buddy)->ptr, NULL);
378
+ purple_blist_add_buddy(pb, NULL, grp, NULL);
379
+ purple_account_add_buddy(account, pb);
380
+ return Qtrue;
381
+ }
382
+
383
+ static VALUE has_buddy(VALUE self, VALUE buddy)
384
+ {
385
+ PurpleAccount *account;
386
+ Data_Get_Struct(self, PurpleAccount, account);
387
+ if (purple_find_buddy(account, RSTRING(buddy)->ptr) != NULL) {
388
+ return Qtrue;
389
+ } else {
390
+ return Qfalse;
391
+ }
392
+ }
393
+
394
+ void Init_purple_ruby()
395
+ {
396
+ cPurpleRuby = rb_define_class("PurpleRuby", rb_cObject);
397
+ rb_define_singleton_method(cPurpleRuby, "init", init, 1);
398
+ rb_define_singleton_method(cPurpleRuby, "list_protocols", list_protocols, 0);
399
+ rb_define_singleton_method(cPurpleRuby, "watch_signed_on_event", watch_signed_on_event, 0);
400
+ rb_define_singleton_method(cPurpleRuby, "watch_incoming_im", watch_incoming_im, 0);
401
+ rb_define_singleton_method(cPurpleRuby, "login", login, 3);
402
+ rb_define_singleton_method(cPurpleRuby, "watch_incoming_ipc", watch_incoming_ipc, 2);
403
+ rb_define_singleton_method(cPurpleRuby, "main_loop_run", main_loop_run, 0);
404
+ rb_define_singleton_method(cPurpleRuby, "main_loop_stop", main_loop_stop, 0);
405
+
406
+ cAccount = rb_define_class_under(cPurpleRuby, "Account", rb_cObject);
407
+ rb_define_method(cAccount, "send_im", send_im, 2);
408
+ rb_define_method(cAccount, "username", username, 0);
409
+ rb_define_method(cAccount, "add_buddy", add_buddy, 1);
410
+ rb_define_method(cAccount, "has_buddy?", has_buddy, 1);
411
+ }
metadata ADDED
@@ -0,0 +1,71 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: yong-purplegw_ruby
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - yong
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-04-01 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: hoe
17
+ type: :development
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 1.8.3
24
+ version:
25
+ description: A ruby gem to write server that sends and recives IM messages
26
+ email: yong@intridea.com
27
+ executables: []
28
+
29
+ extensions:
30
+ - ext/extconf.rb
31
+ extra_rdoc_files:
32
+ - Manifest.txt
33
+ - History.txt
34
+ - README.txt
35
+ files:
36
+ - ext/extconf.rb
37
+ - ext/purple_ruby.c
38
+ - examples/purplegw_example.rb
39
+ - Manifest.txt
40
+ - History.txt
41
+ - README.txt
42
+ - Rakefile
43
+ has_rdoc: true
44
+ homepage: http://www.intridea.com
45
+ post_install_message:
46
+ rdoc_options:
47
+ - --main
48
+ - README.txt
49
+ require_paths:
50
+ - ext
51
+ required_ruby_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: "0"
56
+ version:
57
+ required_rubygems_version: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: "0"
62
+ version:
63
+ requirements: []
64
+
65
+ rubyforge_project: purplegw_ruby
66
+ rubygems_version: 1.2.0
67
+ signing_key:
68
+ specification_version: 2
69
+ summary: A ruby gem to write server that sends and recives IM messages
70
+ test_files: []
71
+