00001
00002 #include <microfeed/microfeedsubscriber.h>
00003 #include <microfeed/microfeedmisc.h>
00004 #include <microfeed/microfeedconfiguration.h>
00005 #include <microfeed/microfeedprotocol.h>
00006
00007 #include <stdio.h>
00008 #include <stdlib.h>
00009 #include <string.h>
00010 #include <time.h>
00011 #include <sys/types.h>
00012 #include <dirent.h>
00013
00014 struct _MicrofeedSubscriber {
00015 DBusConnection* connection;
00016 char* object_path;
00017 MicrofeedConfiguration* configuration;
00018
00019 MicrofeedStore* publishers_by_identifier;
00020 MicrofeedStore* publishers_by_unique_connection_name;
00021 };
00022
00023 typedef struct {
00024 MicrofeedSubscriber* subscriber;
00025 char* identifier;
00026 char* object_path;
00027 char* bus_name;
00028 char* unique_connection_name;
00029 time_t last_activity;
00030 MicrofeedStore* feeds;
00031 MicrofeedItem* configuration;
00032 } Publisher;
00033
00034 typedef struct {
00035 unsigned int reference_count;
00036 Publisher* publisher;
00037 char* uri;
00038 MicrofeedSubscriberCallbacks callbacks;
00039 void* user_data;
00040 } Feed;
00041
00042 typedef struct {
00043 const char* name;
00044 void (*callback)(MicrofeedSubscriber* subscriber, Publisher* publisher, DBusMessage* message);
00045 } SignalCallback;
00046
00047 typedef struct {
00048 Publisher* publisher;
00049 char* uri;
00050 char* uid;
00051 MicrofeedSubscriberErrorCallback callback;
00052 void* user_data;
00053 } MethodReturnData;
00054
00055 typedef enum {
00056 ITEM_SIGNAL_TYPE_ADDED,
00057 ITEM_SIGNAL_TYPE_CHANGED,
00058 ITEM_SIGNAL_TYPE_REPUBLISHED
00059 } ItemSignalType;
00060
00061 typedef enum {
00062 FEED_SIGNAL_TYPE_UPDATE_STARTED,
00063 FEED_SIGNAL_TYPE_UPDATE_ENDED,
00064 FEED_SIGNAL_TYPE_REPUBLISHING_STARTED,
00065 FEED_SIGNAL_TYPE_REPUBLISHING_ENDED,
00066 } FeedSignalType;
00067
00068 static Publisher* publisher_new(MicrofeedSubscriber* subscriber, const char* identifier);
00069 static void publisher_free(Publisher* publisher);
00070 static void publisher_try_to_destroy(Publisher* publisher);
00071 static Feed* feed_new(Publisher* publisher, const char* uri, MicrofeedSubscriberCallbacks* callbacks, void* user_data);
00072 static void feed_free(Feed* feed);
00073
00074 static void call_publisher_method(Publisher* publisher, const char* uri, const char* uid, DBusMessage* message, MicrofeedSubscriberErrorCallback callback, void* user_data);
00075 static MicrofeedItem* parse_item_from_message(DBusMessageIter* iter);
00076 static void do_item_signal(MicrofeedSubscriber* subscriber, Publisher* publisher, DBusMessage* message, ItemSignalType item_signal_type);
00077 static void do_feed_signal(MicrofeedSubscriber* subscriber, Publisher* publisher, DBusMessage* message, FeedSignalType feed_signal_type);
00078 static Publisher* get_publisher(MicrofeedSubscriber* subscriber, const char* publisher_identifier, const char* uri, const char* uid, MicrofeedSubscriberErrorCallback callback, void* user_data);
00079 static Feed* get_feed(MicrofeedSubscriber* subscriber, const char* publisher_identifier, const char* uri, const char* uid, MicrofeedSubscriberErrorCallback callback, void* user_data);
00080 static const char* feed_get_uri(Feed* feed);
00081 static const char* publisher_get_identifier(Publisher* publisher);
00082 static const char* publisher_get_unique_connection_name(Publisher* publisher);
00083 static void object_unregister(DBusConnection* connection, void* user_data);
00084 static DBusHandlerResult object_message(DBusConnection* connection, DBusMessage* message, void* user_data);
00085
00086 static void signal_feed_update_started(MicrofeedSubscriber* subscriber, Publisher* publisher, DBusMessage* message);
00087 static void signal_feed_update_ended(MicrofeedSubscriber* subscriber, Publisher* publisher, DBusMessage* message);
00088 static void signal_feed_republishing_started(MicrofeedSubscriber* subscriber, Publisher* publisher, DBusMessage* message);
00089 static void signal_feed_republishing_ended(MicrofeedSubscriber* subscriber, Publisher* publisher, DBusMessage* message);
00090 static void signal_item_data(MicrofeedSubscriber* subscriber, Publisher* publisher, DBusMessage* message);
00091 static void signal_item_added(MicrofeedSubscriber* subscriber, Publisher* publisher, DBusMessage* message);
00092 static void signal_item_changed(MicrofeedSubscriber* subscriber, Publisher* publisher, DBusMessage* message);
00093 static void signal_item_republished(MicrofeedSubscriber* subscriber, Publisher* publisher, DBusMessage* message);
00094 static void signal_item_removed(MicrofeedSubscriber* subscriber, Publisher* publisher, DBusMessage* message);
00095 static void signal_item_status_changed(MicrofeedSubscriber* subscriber, Publisher* publisher, DBusMessage* message);
00096
00097 static SignalCallback signal_callbacks[] = {
00098 { "FeedUpdateStarted", signal_feed_update_started },
00099 { "FeedUpdateEnded", signal_feed_update_ended },
00100 { "FeedRepublishingStarted", signal_feed_republishing_started },
00101 { "FeedRepublishingEnded", signal_feed_republishing_ended },
00102 { "ItemData", signal_item_data },
00103 { "ItemAdded", signal_item_added },
00104 { "ItemChanged", signal_item_changed },
00105 { "ItemRepublished", signal_item_republished },
00106 { "ItemRemoved", signal_item_removed },
00107 { "ItemStatusChanged", signal_item_status_changed },
00108 { NULL, NULL }
00109 };
00110
00111 static DBusObjectPathVTable object_vtable = {
00112 object_unregister,
00113 object_message
00114 };
00115
00116 MicrofeedSubscriber* microfeed_subscriber_new(const char* object_path, MicrofeedConfiguration* configuration, DBusConnection* connection) {
00117 MicrofeedSubscriber* subscriber;
00118
00119 subscriber = microfeed_memory_allocate(MicrofeedSubscriber);
00120 subscriber->connection = connection;
00121 subscriber->object_path = strdup(object_path);
00122 subscriber->configuration = configuration;
00123
00124 subscriber->publishers_by_identifier = microfeed_store_new_sorted((MicrofeedStoreCompareKeysFunction)strcmp,
00125 (MicrofeedStoreGetKeyFunction)publisher_get_identifier);
00126 subscriber->publishers_by_unique_connection_name = microfeed_store_new_sorted((MicrofeedStoreCompareKeysFunction)strcmp,
00127 (MicrofeedStoreGetKeyFunction)publisher_get_unique_connection_name);
00128
00129 if (!dbus_connection_register_object_path(subscriber->connection, subscriber->object_path, &object_vtable, subscriber)) {
00130
00131 return NULL;
00132 }
00133 if (!dbus_connection_add_filter(subscriber->connection, object_message, subscriber, NULL)) {
00134
00135 return NULL;
00136 }
00137
00138 return subscriber;
00139 }
00140
00141 void microfeed_subscriber_add_item(MicrofeedSubscriber* subscriber, const char* publisher_identifier, const char* uri, MicrofeedItem* item, MicrofeedSubscriberErrorCallback callback, void* user_data) {
00142 Publisher* publisher;
00143 DBusMessage* message;
00144 DBusMessageIter iter;
00145 MicrofeedItemIterator* iterator;
00146 const char* key;
00147 const char* value;
00148
00149 if ((publisher = get_publisher(subscriber, publisher_identifier, uri, microfeed_item_get_uid(item), callback, user_data))) {
00150 message = dbus_message_new_method_call(publisher->bus_name, publisher->object_path, MICROFEED_DBUS_INTERFACE_PUBLISHER, "AddItem");
00151 dbus_message_iter_init_append(message, &iter);
00152 for (iterator = microfeed_item_iterate_properties(item, NULL);
00153 microfeed_item_iterator_get(iterator, &key, &value);
00154 microfeed_item_iterator_next(iterator)) {
00155 dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &key);
00156 dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &value);
00157 }
00158 microfeed_item_iterator_free(iterator);
00159 call_publisher_method(publisher, uri, microfeed_item_get_uid(item), message, callback, user_data);
00160 dbus_message_unref(message);
00161
00162 publisher_try_to_destroy(publisher);
00163 }
00164 }
00165
00166 void microfeed_subscriber_subscribe_feed(MicrofeedSubscriber* subscriber, const char* publisher_identifier, const char* uri, MicrofeedSubscriberCallbacks* callbacks, MicrofeedSubscriberErrorCallback callback, void* user_data) {
00167 Publisher* publisher;
00168 Feed* feed;
00169 DBusMessage* message;
00170
00171 if ((publisher = get_publisher(subscriber, publisher_identifier, uri, NULL, callback, user_data))) {
00172 if ((feed = microfeed_store_get(publisher->feeds, uri, Feed))) {
00173 if (callback) {
00174 callback(subscriber, publisher_identifier, uri, NULL, MICROFEED_ERROR_FEED_ALREADY_SUBSCRIBED, "Subscribed a feed that is already subscribed.", user_data);
00175 }
00176 } else {
00177 feed = feed_new(publisher, uri, callbacks, user_data);
00178 microfeed_store_insert(publisher->feeds, feed);
00179
00180 message = dbus_message_new_method_call(feed->publisher->bus_name, feed->publisher->object_path, MICROFEED_DBUS_INTERFACE_PUBLISHER, "SubscribeFeed");
00181 dbus_message_append_args(message, DBUS_TYPE_STRING, &feed->uri, DBUS_TYPE_INVALID);
00182 call_publisher_method(publisher, uri, NULL, message, callback, user_data);
00183 dbus_message_unref(message);
00184 }
00185 }
00186 }
00187
00188 void microfeed_subscriber_unsubscribe_feed(MicrofeedSubscriber* subscriber, const char* publisher_identifier, const char* uri, MicrofeedSubscriberErrorCallback callback, void* user_data) {
00189 Feed* feed;
00190 DBusMessage* message;
00191 Publisher* publisher;
00192
00193 if ((feed = get_feed(subscriber, publisher_identifier, uri, NULL, callback, user_data))) {
00194 message = dbus_message_new_method_call(feed->publisher->bus_name, feed->publisher->object_path, MICROFEED_DBUS_INTERFACE_PUBLISHER, "UnsubscribeFeed");
00195 dbus_message_append_args(message, DBUS_TYPE_STRING, &feed->uri, DBUS_TYPE_INVALID);
00196 call_publisher_method(feed->publisher, feed->uri, NULL, message, callback, user_data);
00197 dbus_message_unref(message);
00198
00199 publisher = feed->publisher;
00200 microfeed_store_remove(feed->publisher->feeds, feed);
00201 feed_free(feed);
00202
00203 publisher_try_to_destroy(publisher);
00204 }
00205 }
00206
00207 void microfeed_subscriber_update_feed(MicrofeedSubscriber* subscriber, const char* publisher_identifier, const char* uri, MicrofeedSubscriberErrorCallback callback, void* user_data) {
00208 Feed* feed;
00209 DBusMessage* message;
00210
00211 if ((feed = get_feed(subscriber, publisher_identifier, uri, NULL, callback, user_data))) {
00212 message = dbus_message_new_method_call(feed->publisher->bus_name, feed->publisher->object_path, MICROFEED_DBUS_INTERFACE_PUBLISHER, "UpdateFeed");
00213 dbus_message_append_args(message, DBUS_TYPE_STRING, &uri, DBUS_TYPE_INVALID);
00214 call_publisher_method(feed->publisher, feed->uri, NULL, message, callback, user_data);
00215 dbus_message_unref(message);
00216 }
00217 }
00218
00219 void microfeed_subscriber_republish_items(MicrofeedSubscriber* subscriber, const char* publisher_identifier, const char* uri, const char* start_uid, const char* end_uid, unsigned int max_count, MicrofeedSubscriberErrorCallback callback, void* user_data) {
00220 Feed* feed;
00221 DBusMessage* message;
00222 const char* start_uid_dbus;
00223 const char* end_uid_dbus;
00224 dbus_uint16_t max_count_dbus;
00225
00226 if ((feed = get_feed(subscriber, publisher_identifier, uri, start_uid, callback, user_data))) {
00227 message = dbus_message_new_method_call(feed->publisher->bus_name, feed->publisher->object_path, MICROFEED_DBUS_INTERFACE_PUBLISHER, "RepublishItems");
00228 start_uid_dbus = (start_uid ? start_uid : "");
00229 end_uid_dbus = (end_uid ? end_uid : "");
00230 max_count_dbus = max_count;
00231 dbus_message_append_args(message, DBUS_TYPE_STRING, &uri, DBUS_TYPE_STRING, &start_uid_dbus, DBUS_TYPE_STRING, &end_uid_dbus, DBUS_TYPE_UINT16, &max_count_dbus, DBUS_TYPE_INVALID);
00232 call_publisher_method(feed->publisher, feed->uri, start_uid, message, callback, user_data);
00233 dbus_message_unref(message);
00234 }
00235 }
00236
00237 void microfeed_subscriber_send_item_data(MicrofeedSubscriber* subscriber, const char* publisher_identifier, const char* uri, const char* uid, MicrofeedSubscriberErrorCallback callback, void* user_data) {
00238 Publisher* publisher;
00239 DBusMessage* message;
00240
00241 if ((publisher = get_publisher(subscriber, publisher_identifier, NULL, uid, callback, user_data))) {
00242 message = dbus_message_new_method_call(publisher->bus_name, publisher->object_path, MICROFEED_DBUS_INTERFACE_PUBLISHER, "SendItemData");
00243 dbus_message_append_args(message, DBUS_TYPE_STRING, &uri, DBUS_TYPE_STRING, &uid, DBUS_TYPE_INVALID);
00244 call_publisher_method(publisher, NULL, uid, message, callback, user_data);
00245 dbus_message_unref(message);
00246 }
00247 }
00248
00249 void microfeed_subscriber_create_publisher(MicrofeedSubscriber* subscriber, const char* publisher_identifier, MicrofeedSubscriberErrorCallback callback, void* user_data) {
00250 Publisher* publisher;
00251 DBusMessage* message;
00252
00253 if ((publisher = microfeed_store_get(subscriber->publishers_by_identifier, publisher_identifier, Publisher))) {
00254 if (callback) {
00255 callback(subscriber, publisher_identifier, NULL, NULL, MICROFEED_ERROR_PUBLISHER_ALREADY_EXISTS, "Trying to create a publisher that already exists.", user_data);
00256 }
00257 } else if (!(publisher = publisher_new(subscriber, publisher_identifier))) {
00258 if (callback) {
00259 callback(subscriber, publisher_identifier, NULL, NULL, MICROFEED_ERROR_INVALID_PUBLISHER_IDENTIFIER, "Publisher identifier was invalid.", user_data);
00260 }
00261 } else if (microfeed_configuration_get_publisher_directory(subscriber->configuration, publisher_identifier)) {
00262 if (callback) {
00263 callback(subscriber, publisher_identifier, NULL, NULL, MICROFEED_ERROR_PUBLISHER_ALREADY_EXISTS, "Trying to create a publisher that already exists.", user_data);
00264 }
00265 } else {
00266 microfeed_store_insert(subscriber->publishers_by_identifier, publisher);
00267
00268 message = dbus_message_new_method_call(publisher->bus_name, publisher->object_path, MICROFEED_DBUS_INTERFACE_PUBLISHER, "CreatePublisher");
00269 call_publisher_method(publisher, NULL, NULL, message, callback, user_data);
00270 dbus_message_unref(message);
00271 }
00272 }
00273
00274 static Publisher* publisher_new(MicrofeedSubscriber* subscriber, const char* identifier) {
00275 Publisher* publisher;
00276 char* colon;
00277
00278 publisher = microfeed_memory_allocate(Publisher);
00279 publisher->subscriber = subscriber;
00280 publisher->identifier = strdup(identifier);
00281 if (!(colon = strchr(publisher->identifier, ':')) || colon[1] == 0) {
00282 free(publisher->identifier);
00283 microfeed_memory_free(publisher);
00284 publisher = NULL;
00285 } else {
00286 *colon = 0;
00287 publisher->object_path = microfeed_util_string_concatenate(MICROFEED_DBUS_OBJECT_PATH_PUBLISHER, publisher->identifier, NULL);
00288 publisher->bus_name = strdup(colon + 1);
00289 *colon = ':';
00290 publisher->unique_connection_name = NULL;
00291 publisher->last_activity = 0;
00292 publisher->feeds = microfeed_store_new_sorted((MicrofeedStoreCompareKeysFunction)strcmp,
00293 (MicrofeedStoreGetKeyFunction)feed_get_uri);
00294 }
00295
00296 return publisher;
00297 }
00298
00299 static void publisher_free(Publisher* publisher) {
00300 char buffer[1024];
00301
00302 if (publisher->unique_connection_name) {
00303 snprintf(buffer, 1024, "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged',arg0='%s'", publisher->unique_connection_name);
00304 dbus_bus_remove_match(publisher->subscriber->connection, buffer, NULL);
00305 snprintf(buffer, 1024, "type='signal',sender='%s',interface='%s',path='%s'", publisher->unique_connection_name, MICROFEED_DBUS_INTERFACE_PUBLISHER, publisher->object_path);
00306 dbus_bus_remove_match(publisher->subscriber->connection, buffer, NULL);
00307 snprintf(buffer, 1024, "type='signal',sender='%s',interface='%s',path='%s',destination='%s'", publisher->unique_connection_name, MICROFEED_DBUS_INTERFACE_PUBLISHER_TO_DESTINATION, publisher->object_path, dbus_bus_get_unique_name(publisher->subscriber->connection));
00308 dbus_bus_remove_match(publisher->subscriber->connection, buffer, NULL);
00309 snprintf(buffer, 1024, "type='signal',sender='%s',interface='%s',path='%s'", publisher->unique_connection_name, MICROFEED_DBUS_INTERFACE_ERROR, publisher->object_path);
00310 dbus_bus_remove_match(publisher->subscriber->connection, buffer, NULL);
00311 snprintf(buffer, 1024, "type='signal',sender='%s',interface='%s',path='%s',destination ='%s'", publisher->unique_connection_name, MICROFEED_DBUS_INTERFACE_ERROR_TO_DESTINATION, publisher->object_path, dbus_bus_get_unique_name(publisher->subscriber->connection));
00312 dbus_bus_remove_match(publisher->subscriber->connection, buffer, NULL);
00313 }
00314
00315 free(publisher->identifier);
00316 free(publisher->object_path);
00317 free(publisher->bus_name);
00318 free(publisher->unique_connection_name);
00319 publisher->subscriber = NULL;
00320 publisher->identifier = NULL;
00321 publisher->object_path = NULL;
00322 publisher->bus_name = NULL;
00323 publisher->unique_connection_name = NULL;
00324 microfeed_store_free(publisher->feeds);
00325 microfeed_memory_free(publisher);
00326 }
00327
00328 static void publisher_try_to_destroy(Publisher* publisher) {
00329 if (microfeed_store_get_size(publisher->feeds) == 0) {
00330 microfeed_store_remove(publisher->subscriber->publishers_by_identifier, publisher);
00331 microfeed_store_remove(publisher->subscriber->publishers_by_unique_connection_name, publisher);
00332 publisher_free(publisher);
00333 }
00334 }
00335
00336 static Feed* feed_new(Publisher* publisher, const char* uri, MicrofeedSubscriberCallbacks* callbacks, void* user_data) {
00337 Feed* feed;
00338
00339 feed = microfeed_memory_allocate(Feed);
00340 feed->reference_count = 1;
00341 feed->publisher = publisher;
00342 feed->uri = strdup(uri);
00343 feed->callbacks = *callbacks;
00344 feed->user_data = user_data;
00345
00346 return feed;
00347 }
00348
00349 static void feed_free(Feed* feed) {
00350 free(feed->uri);
00351 feed->publisher = NULL;
00352 feed->uri = NULL;
00353 feed->user_data = NULL;
00354 microfeed_memory_free(feed);
00355 }
00356
00357
00358
00359
00360
00361
00362
00363
00364
00365
00366
00367
00368
00369
00370 static void signal_feed_update_started(MicrofeedSubscriber* subscriber, Publisher* publisher, DBusMessage* message) {
00371 do_feed_signal(subscriber, publisher, message, FEED_SIGNAL_TYPE_UPDATE_STARTED);
00372 }
00373
00374 static void signal_feed_update_ended(MicrofeedSubscriber* subscriber, Publisher* publisher, DBusMessage* message) {
00375 do_feed_signal(subscriber, publisher, message, FEED_SIGNAL_TYPE_UPDATE_ENDED);
00376 }
00377
00378 static void signal_feed_republishing_started(MicrofeedSubscriber* subscriber, Publisher* publisher, DBusMessage* message) {
00379 do_feed_signal(subscriber, publisher, message, FEED_SIGNAL_TYPE_REPUBLISHING_STARTED);
00380 }
00381
00382 static void signal_feed_republishing_ended(MicrofeedSubscriber* subscriber, Publisher* publisher, DBusMessage* message) {
00383 do_feed_signal(subscriber, publisher, message, FEED_SIGNAL_TYPE_REPUBLISHING_ENDED);
00384 }
00385
00386 static void signal_item_data(MicrofeedSubscriber* subscriber, Publisher* publisher, DBusMessage* message) {
00387 DBusMessageIter iter;
00388 char* uri;
00389 Feed* feed;
00390 char* uid;
00391 DBusMessageIter subiter;
00392 char* data;
00393 int length;
00394
00395 if (dbus_message_iter_init(message, &iter) && dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_STRING) {
00396 dbus_message_iter_get_basic(&iter, &uri);
00397 if ((feed = microfeed_store_get(publisher->feeds, uri, Feed)) && feed->callbacks.item_data_received &&
00398 dbus_message_iter_next(&iter) && dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_STRING) {
00399 dbus_message_iter_get_basic(&iter, &uid);
00400 if (dbus_message_iter_next(&iter) && dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_ARRAY &&
00401 dbus_message_iter_get_element_type(&iter) == DBUS_TYPE_BYTE) {
00402 dbus_message_iter_recurse(&iter, &subiter);
00403 dbus_message_iter_get_fixed_array(&subiter, &data, &length);
00404 feed->callbacks.item_data_received(subscriber, publisher->identifier, uri, uid, data, length, feed->user_data);
00405 }
00406 }
00407 }
00408 }
00409
00410 static void signal_item_added(MicrofeedSubscriber* subscriber, Publisher* publisher, DBusMessage* message) {
00411 do_item_signal(subscriber, publisher, message, ITEM_SIGNAL_TYPE_ADDED);
00412 }
00413
00414 static void signal_item_changed(MicrofeedSubscriber* subscriber, Publisher* publisher, DBusMessage* message) {
00415 do_item_signal(subscriber, publisher, message, ITEM_SIGNAL_TYPE_CHANGED);
00416 }
00417
00418 static void signal_item_republished(MicrofeedSubscriber* subscriber, Publisher* publisher, DBusMessage* message) {
00419 do_item_signal(subscriber, publisher, message, ITEM_SIGNAL_TYPE_REPUBLISHED);
00420 }
00421
00422 static void signal_item_removed(MicrofeedSubscriber* subscriber, Publisher* publisher, DBusMessage* message) {
00423 DBusError error;
00424 char* uri;
00425 char* uid;
00426 Feed* feed;
00427
00428 dbus_error_init(&error);
00429 if (dbus_message_get_args(message, &error, DBUS_TYPE_STRING, &uri, DBUS_TYPE_STRING, &uid, DBUS_TYPE_INVALID) &&
00430 (feed = microfeed_store_get(publisher->feeds, uri, Feed)) && feed->callbacks.item_removed) {
00431 feed->callbacks.item_removed(subscriber, publisher->identifier, uri, uid, feed->user_data);
00432 }
00433 }
00434
00435 static void signal_item_status_changed(MicrofeedSubscriber* subscriber, Publisher* publisher, DBusMessage* message) {
00436 DBusError error;
00437 char* uri;
00438 char* uid;
00439 char status;
00440 Feed* feed;
00441
00442 dbus_error_init(&error);
00443 if (dbus_message_get_args(message, &error, DBUS_TYPE_STRING, &uri, DBUS_TYPE_STRING, &uid, DBUS_TYPE_BYTE, &status, DBUS_TYPE_INVALID) &&
00444 (feed = microfeed_store_get(publisher->feeds, uri, Feed)) && feed->callbacks.item_status_changed) {
00445 feed->callbacks.item_status_changed(subscriber, publisher->identifier, uri, uid, (MicrofeedItemStatus)status, feed->user_data);
00446 }
00447 }
00448
00449 static const char* feed_get_uri(Feed* feed) {
00450
00451 return feed->uri;
00452 }
00453
00454 static const char* publisher_get_unique_connection_name(Publisher* publisher) {
00455
00456 return publisher->unique_connection_name;
00457 }
00458
00459 static const char* publisher_get_identifier(Publisher* publisher) {
00460
00461 return publisher->identifier;
00462 }
00463
00464 static Publisher* get_publisher(MicrofeedSubscriber* subscriber, const char* publisher_identifier, const char* uri, const char* uid, MicrofeedSubscriberErrorCallback callback, void* user_data) {
00465 Publisher* publisher = NULL;
00466
00467 if (!(publisher = microfeed_store_get(subscriber->publishers_by_identifier, publisher_identifier, Publisher))) {
00468 if (!(publisher = publisher_new(subscriber, publisher_identifier))) {
00469 if (callback) {
00470 callback(subscriber, publisher_identifier, uri, uid, MICROFEED_ERROR_INVALID_PUBLISHER_IDENTIFIER, "Publisher identifier was invalid.", user_data);
00471 }
00472 } else if (!microfeed_configuration_get_provider_name(subscriber->configuration, publisher->bus_name)) {
00473 publisher_free(publisher);
00474 publisher = NULL;
00475 if (callback) {
00476 callback(subscriber, publisher_identifier, uri, uid, MICROFEED_ERROR_NO_SUCH_PROVIDER, "Provider for the publisher does not exist.", user_data);
00477 }
00478 } else {
00479 microfeed_store_insert(subscriber->publishers_by_identifier, publisher);
00480 }
00481 }
00482
00483 return publisher;
00484 }
00485 static Feed* get_feed(MicrofeedSubscriber* subscriber, const char* publisher_identifier, const char* uri, const char* uid, MicrofeedSubscriberErrorCallback callback, void* user_data) {
00486 Publisher* publisher;
00487 Feed* feed = NULL;
00488
00489 if (!(publisher = microfeed_store_get(subscriber->publishers_by_identifier, publisher_identifier, Publisher))) {
00490 if (callback) {
00491 callback(subscriber, publisher_identifier, uri, uid, MICROFEED_ERROR_NO_SUCH_PUBLISHER, "Publisher does not exist.", user_data);
00492 }
00493 } else if (!(feed = microfeed_store_get(publisher->feeds, uri, Feed))) {
00494 if (callback) {
00495 callback(subscriber, publisher_identifier, uri, uid, MICROFEED_ERROR_FEED_NOT_SUBSCRIBED, "Trying to access a feed that is not subscribed.", user_data);
00496 }
00497 }
00498
00499 return feed;
00500 }
00501 static MicrofeedItem* parse_item_from_message(DBusMessageIter* iter) {
00502 MicrofeedItem* item = NULL;
00503 char* uid;
00504 dbus_uint64_t timestamp;
00505 unsigned char status;
00506 char* key;
00507 char* value;
00508
00509 if (dbus_message_iter_next(iter) && dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_STRING) {
00510 dbus_message_iter_get_basic(iter, &uid);
00511 if (dbus_message_iter_next(iter) && dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_UINT64) {
00512 dbus_message_iter_get_basic(iter, ×tamp);
00513 if (dbus_message_iter_next(iter) && dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_BYTE) {
00514 dbus_message_iter_get_basic(iter, &status);
00515 item = microfeed_item_new_with_status(uid, (time_t)timestamp, (MicrofeedItemStatus)status);
00516 while (dbus_message_iter_next(iter) && dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_STRING) {
00517 dbus_message_iter_get_basic(iter, &key);
00518 if (dbus_message_iter_next(iter) && dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_STRING) {
00519 dbus_message_iter_get_basic(iter, &value);
00520 microfeed_item_set_property(item, key, value);
00521 }
00522 }
00523 }
00524 } else {
00525 item = microfeed_item_new(uid, 0);
00526 }
00527 }
00528
00529 return item;
00530 }
00531
00532 static void do_item_signal(MicrofeedSubscriber* subscriber, Publisher* publisher, DBusMessage* message, ItemSignalType item_signal_type) {
00533 DBusMessageIter iter;
00534 char* uri;
00535 Feed* feed;
00536 MicrofeedItem* item;
00537
00538 if (dbus_message_iter_init(message, &iter) && dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_STRING) {
00539 dbus_message_iter_get_basic(&iter, &uri);
00540 if ((feed = microfeed_store_get(publisher->feeds, uri, Feed)) &&
00541 (item = parse_item_from_message(&iter))) {
00542 switch (item_signal_type) {
00543 case ITEM_SIGNAL_TYPE_CHANGED:
00544 if (feed->callbacks.item_changed) {
00545 feed->callbacks.item_changed(subscriber, publisher->identifier, uri, item, feed->user_data);
00546 }
00547 break;
00548 case ITEM_SIGNAL_TYPE_ADDED:
00549 if (feed->callbacks.item_added) {
00550 feed->callbacks.item_added(subscriber, publisher->identifier, uri, item, feed->user_data);
00551 }
00552 break;
00553 case ITEM_SIGNAL_TYPE_REPUBLISHED:
00554 if (feed->callbacks.item_republished) {
00555 feed->callbacks.item_republished(subscriber, publisher->identifier, uri, item, feed->user_data);
00556 }
00557 break;
00558 }
00559 microfeed_item_free(item);
00560 }
00561 }
00562 }
00563
00564 static void do_feed_signal(MicrofeedSubscriber* subscriber, Publisher* publisher, DBusMessage* message, FeedSignalType feed_signal_type) {
00565 DBusError error;
00566 const char* uri;
00567 Feed* feed;
00568
00569 dbus_error_init(&error);
00570 if (dbus_message_get_args(message, &error, DBUS_TYPE_STRING, &uri, DBUS_TYPE_INVALID)) {
00571 if ((feed = microfeed_store_get(publisher->feeds, uri, Feed))) {
00572 switch (feed_signal_type) {
00573 case FEED_SIGNAL_TYPE_UPDATE_STARTED:
00574 if (feed->callbacks.feed_update_started) {
00575 feed->callbacks.feed_update_started(subscriber, publisher->identifier, uri, feed->user_data);
00576 }
00577 break;
00578 case FEED_SIGNAL_TYPE_UPDATE_ENDED:
00579 if (feed->callbacks.feed_update_ended) {
00580 feed->callbacks.feed_update_ended(subscriber, publisher->identifier, uri, feed->user_data);
00581 }
00582 break;
00583 case FEED_SIGNAL_TYPE_REPUBLISHING_STARTED:
00584 if (feed->callbacks.feed_republishing_started) {
00585 feed->callbacks.feed_republishing_started(subscriber, publisher->identifier, uri, feed->user_data);
00586 }
00587 break;
00588 case FEED_SIGNAL_TYPE_REPUBLISHING_ENDED:
00589 if (feed->callbacks.feed_republishing_ended) {
00590 feed->callbacks.feed_republishing_ended(subscriber, publisher->identifier, uri, feed->user_data);
00591 }
00592 break;
00593 }
00594 }
00595 } else {
00596 dbus_error_free(&error);
00597 }
00598 }
00599
00600 static void handle_publisher_method_return(DBusPendingCall* pending, void* user_data) {
00601 MethodReturnData* data;
00602 DBusMessage* reply;
00603 DBusError error;
00604 const char* error_name;
00605 const char* error_message;
00606 char buffer[1024];
00607
00608 data = (MethodReturnData*)user_data;
00609 reply = dbus_pending_call_steal_reply(pending);
00610 if (!reply) {
00611 if (data->callback) {
00612 data->callback(data->publisher->subscriber, data->publisher->identifier, data->uri, data->uid, MICROFEED_ERROR_DBUS_MESSAGE_FAILED, "No reply from the publisher.", data->user_data);
00613 }
00614 } else if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
00615 if (data->callback) {
00616 error_name = dbus_message_get_error_name(reply);
00617 dbus_error_init(&error);
00618 if (!dbus_message_get_args(reply, &error, DBUS_TYPE_STRING, &error_message, DBUS_TYPE_INVALID)) {
00619 error_message = NULL;
00620 }
00621 data->callback(data->publisher->subscriber, data->publisher->identifier, data->uri, data->uid, error_name, error_message, data->user_data);
00622 }
00623 } else {
00624 if (!data->publisher->unique_connection_name) {
00625 data->publisher->unique_connection_name = strdup(dbus_message_get_sender(reply));
00626 microfeed_store_insert(data->publisher->subscriber->publishers_by_unique_connection_name, data->publisher);
00627
00628 snprintf(buffer, 1024, "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged',arg0='%s'", data->publisher->unique_connection_name);
00629 dbus_bus_add_match(data->publisher->subscriber->connection, buffer, NULL);
00630 snprintf(buffer, 1024, "type='signal',sender='%s',interface='%s',path='%s'", data->publisher->unique_connection_name, MICROFEED_DBUS_INTERFACE_PUBLISHER, data->publisher->object_path);
00631 dbus_bus_add_match(data->publisher->subscriber->connection, buffer, NULL);
00632 snprintf(buffer, 1024, "type='signal',sender='%s',interface='%s',path='%s',destination='%s'", data->publisher->unique_connection_name, MICROFEED_DBUS_INTERFACE_PUBLISHER_TO_DESTINATION, data->publisher->object_path, dbus_bus_get_unique_name(data->publisher->subscriber->connection));
00633 dbus_bus_add_match(data->publisher->subscriber->connection, buffer, NULL);
00634 snprintf(buffer, 1024, "type='signal',sender='%s',interface='%s',path='%s'", data->publisher->unique_connection_name, MICROFEED_DBUS_INTERFACE_ERROR, data->publisher->object_path);
00635 dbus_bus_add_match(data->publisher->subscriber->connection, buffer, NULL);
00636 snprintf(buffer, 1024, "type='signal',sender='%s',interface='%s',path='%s',destination ='%s'", data->publisher->unique_connection_name, MICROFEED_DBUS_INTERFACE_ERROR_TO_DESTINATION, data->publisher->object_path, dbus_bus_get_unique_name(data->publisher->subscriber->connection));
00637 dbus_bus_add_match(data->publisher->subscriber->connection, buffer, NULL);
00638 }
00639 if (data->callback) {
00640 data->callback(data->publisher->subscriber, data->publisher->identifier, data->uri, data->uid, NULL, NULL, data->user_data);
00641 }
00642 }
00643 if (reply) {
00644 dbus_message_unref(reply);
00645 }
00646 free(data->uri);
00647 free(data->uid);
00648 microfeed_memory_free(data);
00649 }
00650
00651 static void call_publisher_method(Publisher* publisher, const char* uri, const char* uid, DBusMessage* message, MicrofeedSubscriberErrorCallback callback, void* user_data) {
00652 DBusPendingCall* pending_call;
00653 MethodReturnData* data;
00654
00655 dbus_connection_send_with_reply(publisher->subscriber->connection, message, &pending_call, -1);
00656 if (pending_call) {
00657 data = microfeed_memory_allocate(MethodReturnData);
00658 data->publisher = publisher;
00659 if (uri) {
00660 data->uri = strdup(uri);
00661 } else {
00662 data->uri = NULL;
00663 }
00664 if (uid) {
00665 data->uid = strdup(uid);
00666 } else {
00667 data->uid = NULL;
00668 }
00669 data->callback = callback;
00670 data->user_data = user_data;
00671 dbus_pending_call_set_notify(pending_call, handle_publisher_method_return, data, NULL);
00672 }
00673 }
00674
00675 static void object_unregister(DBusConnection* connection, void* user_data) {
00676 }
00677
00678 static DBusHandlerResult object_message(DBusConnection* connection, DBusMessage* message, void* user_data) {
00679 DBusHandlerResult result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
00680 MicrofeedSubscriber* subscriber;
00681 const char* name;
00682 const char* old_owner;
00683 const char* new_owner;
00684 MicrofeedStoreIterator* iterator;
00685 Feed* feed;
00686 int i;
00687 Publisher* publisher;
00688 DBusMessage* reply;
00689 DBusError error;
00690 const char* error_name;
00691 char* error_message;
00692 char* uri;
00693 char* uid;
00694
00695 subscriber = (MicrofeedSubscriber*)user_data;
00696 if (dbus_message_is_signal(message, "org.freedesktop.DBus", "NameOwnerChanged") &&
00697 dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &name, DBUS_TYPE_STRING, &old_owner, DBUS_TYPE_STRING, &new_owner, DBUS_TYPE_INVALID) &&
00698 name[0] == ':' && new_owner[0] == 0 && !strcmp(name, old_owner) && (publisher = microfeed_store_get(subscriber->publishers_by_unique_connection_name, name, Publisher))) {
00699 for (iterator = microfeed_store_iterate(publisher->feeds, NULL); (feed = microfeed_store_iterator_get(iterator, Feed)); microfeed_store_iterator_next(iterator)) {
00700 feed->callbacks.error_occured(subscriber, publisher->identifier, feed->uri, NULL, MICROFEED_ERROR_PROVIDER_CLOSED_CONNECTION, "Provider closed connection", feed->user_data);
00701 feed_free(feed);
00702 }
00703 microfeed_store_iterator_free(iterator);
00704 microfeed_store_remove(subscriber->publishers_by_unique_connection_name, publisher);
00705 microfeed_store_remove(subscriber->publishers_by_identifier, publisher);
00706 publisher_free(publisher);
00707 }
00708
00709 if ((publisher = microfeed_store_get(subscriber->publishers_by_unique_connection_name, dbus_message_get_sender(message), Publisher))) {
00710 publisher->last_activity = time(NULL);
00711 if (dbus_message_is_method_call(message, MICROFEED_DBUS_INTERFACE_SUBSCRIBER, "Ping")) {
00712 reply = dbus_message_new_method_return(message);
00713 dbus_connection_send(connection, reply, NULL);
00714 dbus_message_unref(reply);
00715 result = DBUS_HANDLER_RESULT_HANDLED;
00716 } else if (dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_SIGNAL && (dbus_message_has_interface(message, MICROFEED_DBUS_INTERFACE_ERROR) || dbus_message_has_interface(message, MICROFEED_DBUS_INTERFACE_ERROR_TO_DESTINATION))) {
00717 dbus_error_init(&error);
00718 if ((error_name = dbus_message_get_member(message)) && dbus_message_get_args(message, &error, DBUS_TYPE_STRING, &uri, DBUS_TYPE_STRING, &uid, DBUS_TYPE_STRING, &error_message, DBUS_TYPE_INVALID)) {
00719 if ((feed = microfeed_store_get(publisher->feeds, uri, Feed)) && feed->callbacks.error_occured) {
00720 feed->callbacks.error_occured(subscriber, publisher->identifier, uri, uid, error_name, error_message, feed->user_data);
00721 }
00722 }
00723 } else {
00724 for (i = 0; signal_callbacks[i].name; i++) {
00725 if (dbus_message_is_signal(message, MICROFEED_DBUS_INTERFACE_PUBLISHER, signal_callbacks[i].name) ||
00726 dbus_message_is_signal(message, MICROFEED_DBUS_INTERFACE_PUBLISHER_TO_DESTINATION, signal_callbacks[i].name)) {
00727 signal_callbacks[i].callback(subscriber, publisher, message);
00728 result = DBUS_HANDLER_RESULT_HANDLED;
00729 break;
00730 }
00731 }
00732 }
00733 }
00734
00735 return result;
00736 }
00737