revolution 0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. data/extconf.rb +7 -0
  2. data/revolution.c +399 -0
  3. metadata +39 -0
data/extconf.rb ADDED
@@ -0,0 +1,7 @@
1
+ require 'mkmf'
2
+
3
+ $CPPFLAGS += " -Wall " + `pkg-config --cflags libebook-1.0 libecal-1.0 glib-2.0 evolution-data-server-1.0`
4
+ $LOCAL_LIBS += `pkg-config --libs libebook-1.0 libecal-1.0`
5
+
6
+ create_makefile("revolution")
7
+
data/revolution.c ADDED
@@ -0,0 +1,399 @@
1
+ #include <stdio.h>
2
+
3
+ #include "ruby.h"
4
+
5
+ #include "glib.h"
6
+ #include <glib-object.h>
7
+
8
+ #include <libebook/e-book.h>
9
+ #include <libecal/e-cal.h>
10
+
11
+ #include <libical/pvl.h>
12
+ #include <libical/ical.h>
13
+
14
+ #include <libical/icalcomponent.h>
15
+ #include <libical/icalproperty.h>
16
+ #include <libical/icalvalue.h>
17
+ #include <libical/icaltime.h>
18
+ #include <libical/icalenums.h>
19
+
20
+ VALUE crRevolution;
21
+ VALUE module;
22
+
23
+ /*
24
+ * call-seq:
25
+ * new() -> Revolution
26
+ *
27
+ * Creates a new Revolution object
28
+ */
29
+ static VALUE revolution_init(VALUE self) {
30
+ return Qnil;
31
+ }
32
+
33
+ static gint cal_opened_status = -1;
34
+
35
+ static void revolution_calendar_opened_callback(ECal* calendar, gint arg, gpointer user_data) {
36
+ cal_opened_status = arg;
37
+ }
38
+
39
+ void copy_summary(const VALUE ruby_obj, ECalComponent* ev_obj) {
40
+ ECalComponentText summary;
41
+ e_cal_component_get_summary(ev_obj, &summary);
42
+ rb_iv_set(ruby_obj, "@summary", rb_str_new2(summary.value ? summary.value : ""));
43
+ }
44
+
45
+ void copy_start(const VALUE ruby_obj, ECalComponent* ev_obj) {
46
+ ECalComponentDateTime start;
47
+ e_cal_component_get_dtstart(ev_obj, &start);
48
+ if (start.value) {
49
+ rb_iv_set(ruby_obj, "@start", rb_funcall( rb_cTime, rb_intern( "at" ), 1, INT2NUM(icaltime_as_timet(*start.value))));
50
+ e_cal_component_free_datetime(&start);
51
+ }
52
+ }
53
+
54
+ void copy_last_modification(const VALUE ruby_obj, ECalComponent* ev_obj) {
55
+ struct icaltimetype* last_mod;
56
+ e_cal_component_get_last_modified(ev_obj, &last_mod);
57
+ if (last_mod) {
58
+ rb_iv_set(ruby_obj, "@last_modification", rb_funcall( rb_cTime, rb_intern( "at" ), 1, INT2NUM(icaltime_as_timet(*last_mod))));
59
+ }
60
+ }
61
+
62
+ void copy_uid(const VALUE ruby_obj, ECalComponent* ev_obj) {
63
+ const char* uid;
64
+ e_cal_component_get_uid(ev_obj, &uid);
65
+ if (uid) {
66
+ rb_iv_set(ruby_obj, "@uid", rb_str_new2(uid));
67
+ }
68
+ }
69
+
70
+
71
+ ///////////////////////////////////////////////////////
72
+ // Tasks
73
+ VALUE crEvTask;
74
+
75
+ /*
76
+ * call-seq:
77
+ * new() -> EvTask
78
+ *
79
+ * Creates a new EvTask object
80
+ */
81
+ static VALUE evTask_init(VALUE self) {
82
+ rb_define_attr(crEvTask, "uid", 1, 0);
83
+ rb_define_attr(crEvTask, "summary", 1, 0);
84
+ rb_define_attr(crEvTask, "description", 1, 0);
85
+ rb_define_attr(crEvTask, "start", 1, 0);
86
+ rb_define_attr(crEvTask, "due", 1, 0);
87
+ rb_define_attr(crEvTask, "last_modification", 1, 0);
88
+ return Qtrue;
89
+ }
90
+
91
+ void copy_into_task(const VALUE ruby_task, ECalComponent* ev_task) {
92
+ copy_uid(ruby_task, ev_task);
93
+ copy_summary(ruby_task, ev_task);
94
+ copy_last_modification(ruby_task, ev_task);
95
+ copy_start(ruby_task, ev_task);
96
+
97
+ // Note that task have only one description, so this singly-linked list
98
+ // has no more than one element. This is as opposed to a journal object, which
99
+ // has multiple description entries.
100
+ GSList* descriptions = NULL;
101
+ e_cal_component_get_description_list(ev_task, &descriptions);
102
+ if (descriptions) {
103
+ ECalComponentText* desc = (ECalComponentText*)descriptions->data;
104
+ rb_iv_set(ruby_task, "@description", rb_str_new2(desc->value ? desc->value: ""));
105
+ }
106
+ e_cal_component_free_text_list(descriptions);
107
+
108
+ ECalComponentDateTime due_date;
109
+ e_cal_component_get_due(ev_task, &due_date);
110
+ if (due_date.value) {
111
+ rb_iv_set(ruby_task, "@due", rb_funcall( rb_cTime, rb_intern( "at" ), 1, INT2NUM(icaltime_as_timet(*due_date.value))));
112
+ e_cal_component_free_datetime(&due_date);
113
+ }
114
+ }
115
+
116
+ /*
117
+ * call-seq:
118
+ * get_all_tasks() -> Array
119
+ *
120
+ * Fetches all tasks
121
+ */
122
+ static VALUE revolution_get_all_tasks(VALUE self) {
123
+ VALUE result = rb_ary_new();
124
+
125
+ ECal* cal = e_cal_new_system_tasks();
126
+ g_signal_connect(G_OBJECT(cal), "cal-opened", G_CALLBACK(revolution_calendar_opened_callback), NULL);
127
+ GError* error = 0;
128
+ e_cal_open(cal, FALSE, &error);
129
+ if (error) {
130
+ g_warning("Unable to open calendar: %s", error->message);
131
+ g_clear_error(&error);
132
+ return result;
133
+ }
134
+
135
+ while (cal_opened_status == -1) {
136
+ g_usleep(G_USEC_PER_SEC*0.01);
137
+ }
138
+ if (cal_opened_status != 0) {
139
+ g_warning("Unable to open calendar; error code %d", cal_opened_status);
140
+ return result;
141
+ }
142
+
143
+ GList* objects, *l;
144
+ // #t == true, i.e. return everything
145
+ e_cal_get_object_list_as_comp(cal, "#t", &objects, &error);
146
+ if (error) {
147
+ g_warning("Unable to query calendar: %s", error->message);
148
+ g_clear_error(&error);
149
+ return result;
150
+ }
151
+
152
+ for (l = objects; l;l = l->next) {
153
+ ECalComponent *ev_task = E_CAL_COMPONENT (l->data);
154
+ VALUE ruby_task = rb_class_new_instance(0, 0, crEvTask);
155
+ copy_into_task(ruby_task, ev_task);
156
+ rb_ary_push(result, ruby_task);
157
+ g_object_unref (ev_task);
158
+ }
159
+
160
+ return result;
161
+ }
162
+
163
+ ///////////////////////////////////////////////////////
164
+ // Calendar
165
+ VALUE crEvAppointment;
166
+
167
+ /*
168
+ * call-seq:
169
+ * new() -> EvAppointment
170
+ *
171
+ * Creates a new EvAppointment object
172
+ */
173
+ static VALUE evAppointment_init(VALUE self) {
174
+ rb_define_attr(crEvAppointment, "uid", 1, 0);
175
+ rb_define_attr(crEvAppointment, "summary", 1, 0);
176
+ rb_define_attr(crEvAppointment, "location", 1, 0);
177
+ rb_define_attr(crEvAppointment, "organizer", 1, 0);
178
+ rb_define_attr(crEvAppointment, "start", 1, 0);
179
+ rb_define_attr(crEvAppointment, "end", 1, 0);
180
+ rb_define_attr(crEvAppointment, "last_modification", 1, 0);
181
+ rb_define_attr(crEvAppointment, "alarm_set", 1, 0);
182
+ rb_define_attr(crEvAppointment, "busy_status", 1, 0);
183
+ rb_define_attr(crEvAppointment, "recurring", 1, 0);
184
+ // INLINE RUBY CODE define an "all_day" method
185
+ return Qtrue;
186
+ }
187
+
188
+ void copy_into_appt(const VALUE ruby_appt, ECalComponent* ev_appt) {
189
+ copy_uid(ruby_appt, ev_appt);
190
+ copy_summary(ruby_appt, ev_appt);
191
+ copy_start(ruby_appt, ev_appt);
192
+ copy_last_modification(ruby_appt, ev_appt);
193
+
194
+ const char* location;
195
+ e_cal_component_get_location(ev_appt, &location);
196
+ if (location) {
197
+ rb_iv_set(ruby_appt, "@location", rb_str_new2(location));
198
+ }
199
+
200
+ // there's probably a good way to combine this with due_date
201
+ ECalComponentDateTime end;
202
+ e_cal_component_get_dtend(ev_appt, &end);
203
+ if (end.value) {
204
+ rb_iv_set(ruby_appt, "@end", rb_funcall( rb_cTime, rb_intern( "at" ), 1, INT2NUM(icaltime_as_timet(*end.value))));
205
+ e_cal_component_free_datetime(&end);
206
+ }
207
+
208
+ ECalComponentOrganizer organizer;
209
+ e_cal_component_get_organizer(ev_appt, &organizer);
210
+ if (organizer.value) {
211
+ if (!g_strncasecmp(organizer.value, "mailto:", 7)) {
212
+ organizer.value += 7;
213
+ }
214
+ rb_iv_set(ruby_appt, "@organizer", rb_str_new2(organizer.value?organizer.value: ""));
215
+ }
216
+
217
+ ECalComponentTransparency transp;
218
+ e_cal_component_get_transparency(ev_appt, &transp);
219
+ // This should probably be an int or enum or some such rather than just a true/false
220
+ rb_iv_set(ruby_appt, "@busy_status", transp == E_CAL_COMPONENT_TRANSP_OPAQUE ? Qtrue : Qfalse);
221
+
222
+ rb_iv_set(ruby_appt, "@alarm_set", e_cal_component_has_alarms(ev_appt) ? Qtrue : Qfalse);
223
+ rb_iv_set(ruby_appt, "@recurring", e_cal_component_has_recurrences(ev_appt) ? Qtrue : Qfalse);
224
+ }
225
+
226
+ /*
227
+ * call-seq:
228
+ * get_all_appointments(start, end) -> Array
229
+ *
230
+ * Fetches all appointments between the given Time values.
231
+ */
232
+ static VALUE revolution_get_all_appointments(VALUE self, VALUE start, VALUE end) {
233
+ VALUE result = rb_ary_new();
234
+
235
+ // This works, but what about getting an ESourceList and all that?
236
+ ECal* cal = e_cal_new_system_calendar();
237
+ g_signal_connect(G_OBJECT(cal), "cal-opened", G_CALLBACK(revolution_calendar_opened_callback), NULL);
238
+ GError* error = 0;
239
+ e_cal_open(cal, FALSE, &error);
240
+ if (error) {
241
+ g_warning("Unable to open calendar: %s", error->message);
242
+ g_clear_error(&error);
243
+ return result;
244
+ }
245
+
246
+ while (cal_opened_status == -1) {
247
+ g_usleep(G_USEC_PER_SEC*0.01);
248
+ }
249
+ if (cal_opened_status != 0) {
250
+ g_warning("Unable to open calendar; error code %d", cal_opened_status);
251
+ return result;
252
+ }
253
+
254
+ GList* results, *l;
255
+ e_cal_get_object_list_as_comp(cal, "#t", &results, &error);
256
+ if (error) {
257
+ g_warning("Unable to query calendar: %s", error->message);
258
+ g_clear_error(&error);
259
+ return result;
260
+ }
261
+
262
+ for (l = results; l;l = l->next) {
263
+ ECalComponent *ev_appt = E_CAL_COMPONENT(l->data);
264
+ VALUE ruby_appt = rb_class_new_instance(0, 0, crEvAppointment);
265
+ copy_into_appt(ruby_appt, ev_appt);
266
+ rb_ary_push(result, ruby_appt);
267
+ g_object_unref (ev_appt);
268
+ }
269
+
270
+ g_list_free(results);
271
+
272
+ return result;
273
+ }
274
+
275
+
276
+ ///////////////////////////////////////////////////////
277
+ // Address Book
278
+ VALUE crEvContact;
279
+
280
+ /*
281
+ * call-seq:
282
+ * new() -> EvContact
283
+ *
284
+ * Creates a new EvContact object
285
+ */
286
+ static VALUE evContact_init(VALUE self) {
287
+ rb_define_attr(crEvContact, "uid", 1, 0);
288
+ rb_define_attr(crEvContact, "first_name", 1, 0);
289
+ rb_define_attr(crEvContact, "last_name", 1, 0);
290
+ rb_define_attr(crEvContact, "home_email", 1, 0);
291
+ rb_define_attr(crEvContact, "work_email", 1, 0);
292
+ return Qtrue;
293
+ }
294
+
295
+ char* fetch_field_or_empty_str(EContact* ev_contact, const EContactField field) {
296
+ char* value = e_contact_get(ev_contact, field);
297
+ return (value) ? value : "";
298
+ }
299
+
300
+ void copy_into_contact(const VALUE ruby_contact, EContact* ev_contact) {
301
+ // last mod time will work in v2.16+ : e_contact_get (ev_contact, E_CONTACT_REV));
302
+ rb_iv_set(ruby_contact, "@uid", rb_str_new2(fetch_field_or_empty_str(ev_contact, E_CONTACT_UID)));
303
+ rb_iv_set(ruby_contact, "@first_name", rb_str_new2(fetch_field_or_empty_str(ev_contact, E_CONTACT_GIVEN_NAME)));
304
+ rb_iv_set(ruby_contact, "@last_name", rb_str_new2(fetch_field_or_empty_str(ev_contact, E_CONTACT_FAMILY_NAME)));
305
+
306
+ gpointer email1 = e_contact_get(ev_contact, E_CONTACT_EMAIL);
307
+ if (email1) {
308
+ GList* attrs, *attr;
309
+ attrs = e_contact_get_attributes(ev_contact, E_CONTACT_EMAIL);
310
+ for (attr = attrs; attr ; attr = attr->next) {
311
+ EVCardAttribute* a = attr->data; // g_message("attr name = %s\n", e_vcard_attribute_get_name(a));
312
+ char* value = e_vcard_attribute_get_value(a);
313
+ GList* params, *l;
314
+ params = e_vcard_attribute_get_params(a);
315
+ for (l = params; l; l = l->next) {
316
+ EVCardAttributeParam* p = l->data;
317
+ if (!g_ascii_strcasecmp((char*)e_vcard_attribute_param_get_name(p), "TYPE")) {
318
+ char* type = e_vcard_attribute_param_get_values(p)->data;
319
+ if (!g_ascii_strcasecmp(type, "WORK")) {
320
+ rb_iv_set(ruby_contact, "@work_email", rb_str_new2(value));
321
+ } else if (!g_ascii_strcasecmp(type, "HOME")) {
322
+ rb_iv_set(ruby_contact, "@home_email", rb_str_new2(value));
323
+ }
324
+ }
325
+ }
326
+ }
327
+ }
328
+ }
329
+
330
+ /*
331
+ * call-seq:
332
+ * get_all_contacts() -> Array
333
+ *
334
+ * Fetches all contacts
335
+ */
336
+ static VALUE revolution_get_all_contacts(VALUE self) {
337
+ VALUE result = rb_ary_new();
338
+ GError* error = 0;
339
+ EBook* book = e_book_new_default_addressbook(&error);
340
+ if (error != NULL) {
341
+ g_warning("Unable to locate the default Evolution address book: %s", error->message);
342
+ g_clear_error(&error);
343
+ return result;
344
+ }
345
+
346
+ e_book_open(book, TRUE, &error);
347
+ if (error) {
348
+ g_warning("Unable to open the Evolution address book: %s", error->message);
349
+ g_clear_error(&error);
350
+ return result;
351
+ }
352
+
353
+ EBookQuery* query = e_book_query_any_field_contains("");
354
+
355
+ GList* results, *l = NULL;
356
+ e_book_get_contacts(book, query, &results, &error);
357
+ if (error) {
358
+ g_warning("Unable to query the Evolution address book: %s", error->message);
359
+ g_clear_error(&error);
360
+ return result;
361
+ }
362
+ for (l = results; l; l = l->next) {
363
+ EContact* ev_contact = E_CONTACT(l->data);
364
+ VALUE ruby_contact = rb_class_new_instance(0, 0, crEvContact);
365
+ copy_into_contact(ruby_contact, ev_contact);
366
+ rb_ary_push(result, ruby_contact);
367
+ g_object_unref(ev_contact);
368
+ }
369
+ e_book_query_unref(query);
370
+ g_list_free(results);
371
+
372
+ return result;
373
+ }
374
+
375
+ /*
376
+ * An interface to the Evolution[http://www.gnome.org/projects/evolution/]
377
+ * calendar, contact, and task information.
378
+ */
379
+ void Init_revolution() {
380
+ module = rb_define_module("Revolution");
381
+
382
+ crEvContact = rb_define_class_under(module, "EvContact", rb_cObject);
383
+ rb_define_method(crEvContact, "initialize", evContact_init, 0);
384
+
385
+ crEvAppointment = rb_define_class_under(module, "EvAppointment", rb_cObject);
386
+ rb_define_method(crEvAppointment, "initialize", evAppointment_init, 0);
387
+
388
+ crEvTask = rb_define_class_under(module, "EvTask", rb_cObject);
389
+ rb_define_method(crEvTask, "initialize", evTask_init, 0);
390
+
391
+ crRevolution = rb_define_class_under(module, "Revolution", rb_cObject);
392
+ rb_define_method(crRevolution, "initialize", revolution_init, 0);
393
+ rb_define_method(crRevolution, "get_all_contacts", revolution_get_all_contacts, 0);
394
+ rb_define_method(crRevolution, "get_all_appointments", revolution_get_all_appointments, 2);
395
+ rb_define_method(crRevolution, "get_all_tasks", revolution_get_all_tasks, 0);
396
+
397
+ g_type_init();
398
+ }
399
+
metadata ADDED
@@ -0,0 +1,39 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.8.8
3
+ specification_version: 1
4
+ name: revolution
5
+ version: !ruby/object:Gem::Version
6
+ version: "0.1"
7
+ date: 2005-03-21
8
+ summary: Revolution is a binding for the Evolution email client
9
+ require_paths:
10
+ - ''
11
+ email: tom@infoether.com
12
+ homepage: http://revolution.rubyforge.org/
13
+ rubyforge_project: revolution
14
+ description:
15
+ autorequire: revolution
16
+ default_executable:
17
+ bindir: bin
18
+ has_rdoc: false
19
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
20
+ requirements:
21
+ -
22
+ - ">="
23
+ - !ruby/object:Gem::Version
24
+ version: 1.8.0
25
+ version:
26
+ platform: ruby
27
+ authors:
28
+ - Tom Copeland
29
+ files:
30
+ - revolution.c
31
+ - extconf.rb
32
+ test_files: []
33
+ rdoc_options: []
34
+ extra_rdoc_files: []
35
+ executables: []
36
+ extensions:
37
+ - extconf.rb
38
+ requirements: []
39
+ dependencies: []