00001
00002 #include <microfeed/microfeedprovider.h>
00003 #include <microfeed/microfeedpublisher.h>
00004 #include <microfeed/microfeedmisc.h>
00005 #include <microfeed/microfeedthread.h>
00006 #include <microfeed/microfeederror.h>
00007 #include <microfeed/microfeedprotocol.h>
00008
00009 #include <stdio.h>
00010 #include <stdlib.h>
00011 #include <string.h>
00012 #include <time.h>
00013
00014 typedef struct _Thread Thread;
00015
00016 struct _MicrofeedPublisher {
00017 char* identifier;
00018 char* object_path;
00019 DBusConnection* connection;
00020 MicrofeedPublisherCallbacks callbacks;
00021 void* user_data;
00022 MicrofeedDatabaseEnvironment* database_environment;
00023 char** image_properties;
00024 MicrofeedProvider* provider;
00025
00026 MicrofeedStore* subscribers;
00027 MicrofeedStore* feeds;
00028 MicrofeedFeed* settings_feed;
00029 MicrofeedFeed* images_feed;
00030 MicrofeedThread* singleton_update_thread;
00031 MicrofeedThreadPool* thread_pool;
00032 };
00033
00034 typedef struct {
00035 MicrofeedPublisher* publisher;
00036 char* unique_connection_name;
00037 time_t last_activity;
00038 MicrofeedStore* feeds;
00039 } Subscriber;
00040
00041 typedef struct {
00042 const char* name;
00043 MicrofeedError* (*callback)(MicrofeedPublisher* publisher, Subscriber* subscriber, DBusMessage* message);
00044 } MethodCallback;
00045
00046 static void object_unregister(DBusConnection* connection, void* user_data);
00047 static DBusHandlerResult object_message(DBusConnection* connection, DBusMessage* message, void* user_data);
00048 static Subscriber* subscriber_new(MicrofeedPublisher* publisher, const char* unique_connection_name);
00049 static void subscriber_free(Subscriber* subscriber);
00050 static const char* subscriber_get_unique_connection_name(Subscriber* subscriber);
00051
00052 static MicrofeedError* method_add_item(MicrofeedPublisher* publisher, Subscriber* subscriber, DBusMessage* message);
00053 static MicrofeedError* method_modify_item(MicrofeedPublisher* publisher, Subscriber* subscriber, DBusMessage* message);
00054 static MicrofeedError* method_read_items(MicrofeedPublisher* publisher, Subscriber* subscriber, DBusMessage* message);
00055 static MicrofeedError* method_republish_items(MicrofeedPublisher* publisher, Subscriber* subscriber, DBusMessage* message);
00056 static MicrofeedError* method_send_item_data(MicrofeedPublisher* publisher, Subscriber* subscriber, DBusMessage* message);
00057 static MicrofeedError* method_ping(MicrofeedPublisher* publisher, Subscriber* subscriber, DBusMessage* message);
00058 static MicrofeedError* method_subscribe_feed(MicrofeedPublisher* publisher, Subscriber* subscriber, DBusMessage* message);
00059 static MicrofeedError* method_unsubscribe_feed(MicrofeedPublisher* publisher, Subscriber* subscriber, DBusMessage* message);
00060 static MicrofeedError* method_update_feed(MicrofeedPublisher* publisher, Subscriber* subscriber, DBusMessage* message);
00061 static MicrofeedError* method_create_publisher(MicrofeedPublisher* publisher, Subscriber* subscriber, DBusMessage* message);
00062 static MicrofeedError* method_destroy_publisher(MicrofeedPublisher* publisher, Subscriber* subscriber, DBusMessage* message);
00063
00064 static MicrofeedError* settings_feed_modify_item(MicrofeedFeed* feed, MicrofeedItem* existing_item, MicrofeedItem* new_item, void* user_data);
00065 static MicrofeedError* images_feed_download_item_data(MicrofeedFeed* feed, const char* uid, void** data, size_t* length, void* user_data);
00066 static void thread_exited(MicrofeedThread* thread, void* user_data);
00067
00068 static MethodCallback method_callbacks[] = {
00069 { "AddItem", method_add_item },
00070 { "ChangeItem", method_modify_item },
00071 { "ReadItems", method_read_items },
00072 { "RepublishItems", method_republish_items },
00073 { "SendItemData", method_send_item_data },
00074 { "Ping", method_ping },
00075 { "SubscribeFeed", method_subscribe_feed },
00076 { "UnsubscribeFeed", method_unsubscribe_feed },
00077 { "UpdateFeed", method_update_feed },
00078 { "CreatePublisher", method_create_publisher },
00079 { "DestroyPublisher", method_destroy_publisher },
00080 { NULL, NULL }
00081 };
00082
00083 static MicrofeedFeedCallbacks settings_feed_callbacks = {
00084 NULL,
00085 NULL,
00086 settings_feed_modify_item
00087 };
00088
00089 static MicrofeedFeedCallbacks images_feed_callbacks = {
00090 NULL,
00091 NULL,
00092 NULL,
00093 images_feed_download_item_data
00094 };
00095
00096 static DBusObjectPathVTable object_vtable = {
00097 object_unregister,
00098 object_message
00099 };
00100
00114 MicrofeedPublisher* microfeed_publisher_new(const char* publisher_identifier, const char* directory, DBusConnection* connection, MicrofeedPublisherCallbacks* callbacks, void* user_data) {
00115 MicrofeedPublisher* publisher;
00116 char* colon;
00117
00118 publisher = microfeed_memory_allocate(MicrofeedPublisher);
00119 publisher->identifier = strdup(publisher_identifier);
00120 if (!(colon = strchr(publisher->identifier, ':')) || colon[1] == 0) {
00121 free(publisher->identifier);
00122 microfeed_memory_free(publisher);
00123 publisher = NULL;
00124 } else {
00125 *colon = 0;
00126 publisher->object_path = microfeed_util_string_concatenate(MICROFEED_DBUS_OBJECT_PATH_PUBLISHER, publisher->identifier, NULL);
00127 *colon = ':';
00128 publisher->connection = connection;
00129 publisher->callbacks = *callbacks;
00130 publisher->user_data = user_data;
00131 publisher->image_properties = (char**)malloc(3 * sizeof(char*));
00132 publisher->image_properties[0] = strdup("content.image");
00133 publisher->image_properties[1] = strdup("sender.image");
00134 publisher->image_properties[2] = 0;
00135
00136 if (!dbus_connection_register_object_path(connection, publisher->object_path, &object_vtable, publisher)) {
00137
00138 return NULL;
00139 }
00140
00141 if (!(publisher->database_environment = microfeed_database_environment_new(publisher_identifier, directory))) {
00142
00143 return NULL;
00144 }
00145 publisher->subscribers = microfeed_store_new_sorted((MicrofeedStoreCompareKeysFunction)strcmp,
00146 (MicrofeedStoreGetKeyFunction)subscriber_get_unique_connection_name);
00147 publisher->feeds = microfeed_store_new_sorted((MicrofeedStoreCompareKeysFunction)strcmp,
00148 (MicrofeedStoreGetKeyFunction)microfeed_feed_get_uri);
00149
00150 publisher->settings_feed = microfeed_feed_new(publisher, MICROFEED_FEED_URI_SETTINGS, &settings_feed_callbacks, NULL);
00151 microfeed_store_insert(publisher->feeds, publisher->settings_feed);
00152 microfeed_feed_add_subscriber(publisher->settings_feed, NULL);
00153
00154 publisher->images_feed = microfeed_feed_new(publisher, MICROFEED_FEED_URI_IMAGES, &images_feed_callbacks, NULL);
00155 microfeed_store_insert(publisher->feeds, publisher->images_feed);
00156 microfeed_feed_add_subscriber(publisher->images_feed, NULL);
00157
00158 publisher->thread_pool = microfeed_thread_pool_new_with_exit_callback(microfeed_publisher_get_setting_value_integer(publisher, "threads.max", 10), thread_exited, publisher);
00159 }
00160
00161 return publisher;
00162 }
00163
00171 void microfeed_publisher_free(MicrofeedPublisher* publisher) {
00172
00173 }
00174
00182 void microfeed_publisher_add_setting(MicrofeedPublisher* publisher, MicrofeedItem* item) {
00183 MicrofeedItem* existing_item;
00184
00185 if ((existing_item = microfeed_feed_get_item(publisher->settings_feed, microfeed_item_get_uid(item)))) {
00186 microfeed_item_set_property(item, "value", microfeed_item_get_property(existing_item, "value"));
00187 }
00188 microfeed_feed_replace_item(publisher->settings_feed, item);
00189 }
00190
00197 void microfeed_publisher_remove_setting(MicrofeedPublisher* publisher, const char* uid) {
00198 microfeed_feed_remove_item(publisher->settings_feed, uid);
00199 }
00200
00209 const char* microfeed_publisher_get_setting_value(MicrofeedPublisher* publisher, const char* uid, const char* default_value) {
00210 const char* value;
00211 MicrofeedItem* item;
00212
00213 if ((item = microfeed_feed_get_item(publisher->settings_feed, uid))) {
00214 if (!(value = microfeed_item_get_property(item, "value"))) {
00215 value = default_value;
00216 }
00217 microfeed_item_free(item);
00218 } else {
00219 value = default_value;
00220 }
00221
00222 return value;
00223 }
00224
00225 long int microfeed_publisher_get_setting_value_integer(MicrofeedPublisher* publisher, const char* uid, long int default_value) {
00226 long int value;
00227 MicrofeedItem* item;
00228 const char* string;
00229 char* end;
00230
00231 if ((item = microfeed_feed_get_item(publisher->settings_feed, uid))) {
00232 if (!(string = microfeed_item_get_property(item, "value")) && *string != 0) {
00233 value = strtol(string, &end, 10);
00234 if (*end == 0) {
00235 value = default_value;
00236 }
00237 } else {
00238 value = default_value;
00239 }
00240 microfeed_item_free(item);
00241 } else {
00242 value = default_value;
00243 }
00244
00245 return value;
00246
00247 }
00248
00257 int microfeed_publisher_set_setting_value(MicrofeedPublisher* publisher, const char* uid, const char* value) {
00258 int retvalue = 0;
00259 MicrofeedItem* item;
00260
00261 if ((item = microfeed_feed_get_item(publisher->settings_feed, uid))) {
00262 microfeed_item_set_property(item, "value", value);
00263 microfeed_feed_replace_item(publisher->settings_feed, item);
00264 retvalue = 1;
00265 microfeed_item_free(item);
00266 }
00267
00268 return retvalue;
00269 }
00270
00271 MicrofeedThread* microfeed_publisher_queue_thread(MicrofeedPublisher* publisher, MicrofeedThreadFunction function, void* data) {
00272
00273 return microfeed_thread_pool_queue_thread(publisher->thread_pool, function, data);
00274 }
00275
00276 MicrofeedThread* microfeed_publisher_start_singleton_update_thread(MicrofeedPublisher* publisher, MicrofeedThreadFunction function, void* user_data) {
00277 if (!publisher->singleton_update_thread) {
00278 publisher->singleton_update_thread = microfeed_thread_new(function, user_data);
00279 }
00280
00281 return publisher->singleton_update_thread;
00282 }
00283
00284 MicrofeedThread* microfeed_publisher_get_singleton_update_thread(MicrofeedPublisher* publisher) {
00285
00286 return publisher->singleton_update_thread;
00287 }
00288
00289 MicrofeedDatabaseEnvironment* microfeed_publisher_get_database_environment(MicrofeedPublisher* publisher) {
00290
00291 return publisher->database_environment;
00292 }
00293
00294 void microfeed_publisher_handle_item_property_change(MicrofeedPublisher* publisher, MicrofeedItem* old_item, MicrofeedItem* new_item) {
00295 char** key;
00296 const char* old_value;
00297 const char* new_value;
00298
00299 for (key = publisher->image_properties; *key; key++) {
00300 if (old_item && (old_value = microfeed_item_get_property(old_item, *key))) {
00301 if (new_item && (new_value = microfeed_item_get_property(new_item, *key))) {
00302 if (strcmp(old_value, new_value)) {
00303 microfeed_feed_unref_item_data(publisher->images_feed, old_value);
00304 microfeed_feed_ref_item_data(publisher->images_feed, new_value);
00305 }
00306 } else {
00307 microfeed_feed_unref_item_data(publisher->images_feed, old_value);
00308 }
00309 } else if (new_item && (new_value = microfeed_item_get_property(new_item, *key))) {
00310 microfeed_feed_ref_item_data(publisher->images_feed, new_value);
00311 }
00312 }
00313 }
00314
00315 void microfeed_publisher_send_item_signal(MicrofeedPublisher* publisher, const char* destination, const char* signal_name, const char* uri, MicrofeedItem* item) {
00316 DBusMessage* message;
00317 DBusMessageIter iter;
00318 MicrofeedItemIterator* item_iterator;
00319 const char* uid;
00320 dbus_uint64_t timestamp;
00321 char status;
00322 const char* key;
00323 const char* value;
00324
00325 message = dbus_message_new_signal(publisher->object_path, (destination ? MICROFEED_DBUS_INTERFACE_PUBLISHER_TO_DESTINATION : MICROFEED_DBUS_INTERFACE_PUBLISHER), signal_name);
00326 if (destination) {
00327 dbus_message_set_destination(message,destination);
00328 }
00329 dbus_message_iter_init_append(message, &iter);
00330 dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &uri);
00331 uid = microfeed_item_get_uid(item);
00332 dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &uid);
00333 timestamp = (dbus_uint64_t)microfeed_item_get_timestamp(item);
00334 dbus_message_iter_append_basic(&iter, DBUS_TYPE_UINT64, ×tamp);
00335 status = (char)microfeed_item_get_status(item);
00336 dbus_message_iter_append_basic(&iter, DBUS_TYPE_BYTE, &status);
00337 for (item_iterator = microfeed_item_iterate_properties(item, NULL);
00338 microfeed_item_iterator_get(item_iterator, &key, &value);
00339 microfeed_item_iterator_next(item_iterator)) {
00340 dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &key);
00341 dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &value);
00342 }
00343 microfeed_item_iterator_free(item_iterator);
00344 dbus_connection_send(publisher->connection, message, NULL);
00345 dbus_message_unref(message);
00346 }
00347
00348 void microfeed_publisher_send_item_uid_signal(MicrofeedPublisher* publisher, const char* destination, const char* signal_name, const char* uri, const char* uid) {
00349 DBusMessage* message;
00350
00351 message = dbus_message_new_signal(publisher->object_path, (destination ? MICROFEED_DBUS_INTERFACE_PUBLISHER_TO_DESTINATION : MICROFEED_DBUS_INTERFACE_PUBLISHER), signal_name);
00352 if (destination) {
00353 dbus_message_set_destination(message, destination);
00354 }
00355 dbus_message_append_args(message, DBUS_TYPE_STRING, &uri, DBUS_TYPE_STRING, &uid, DBUS_TYPE_INVALID);
00356 dbus_connection_send(publisher->connection, message, NULL);
00357 dbus_message_unref(message);
00358 }
00359
00360 void microfeed_publisher_send_status_changed_signal(MicrofeedPublisher* publisher, const char* destination, const char* uri, const char* uid, const char status) {
00361 DBusMessage* message;
00362
00363 message = dbus_message_new_signal(publisher->object_path, (destination ? MICROFEED_DBUS_INTERFACE_PUBLISHER_TO_DESTINATION : MICROFEED_DBUS_INTERFACE_PUBLISHER), "ItemStatusChanged");
00364 if (destination) {
00365 dbus_message_set_destination(message, destination);
00366 }
00367 dbus_message_append_args(message, DBUS_TYPE_STRING, &uri, DBUS_TYPE_STRING, &uid, DBUS_TYPE_BYTE, &status, DBUS_TYPE_INVALID);
00368 dbus_connection_send(publisher->connection, message, NULL);
00369 dbus_message_unref(message);
00370 }
00371
00372 void microfeed_publisher_send_feed_signal(MicrofeedPublisher* publisher, const char* destination, const char* signal_name, const char* uri) {
00373 DBusMessage* message;
00374
00375 message = dbus_message_new_signal(publisher->object_path, (destination ? MICROFEED_DBUS_INTERFACE_PUBLISHER_TO_DESTINATION : MICROFEED_DBUS_INTERFACE_PUBLISHER), signal_name);
00376 if (destination) {
00377 dbus_message_set_destination(message, destination);
00378 }
00379 dbus_message_append_args(message, DBUS_TYPE_STRING, &uri, DBUS_TYPE_INVALID);
00380 dbus_connection_send(publisher->connection, message, NULL);
00381 dbus_message_unref(message);
00382 }
00383
00384 void microfeed_publisher_send_error_signal(MicrofeedPublisher* publisher, const char* destination, const char* error_name, const char* uri, const char* uid, const char* error_message) {
00385 DBusMessage* message;
00386 DBusMessageIter iter;
00387 const char* string;
00388
00389 message = dbus_message_new_signal(publisher->object_path, (destination ? MICROFEED_DBUS_INTERFACE_ERROR_TO_DESTINATION : MICROFEED_DBUS_INTERFACE_ERROR), error_name);
00390 if (destination) {
00391 dbus_message_set_destination(message, destination);
00392 }
00393 dbus_message_iter_init_append(message, &iter);
00394 string = (uri ? uri : "");
00395 dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &string);
00396 string = (uid ? uid : "");
00397 dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &string);
00398 string = (error_message ? error_message : "");
00399 dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &string);
00400 dbus_connection_send(publisher->connection, message, NULL);
00401 dbus_message_unref(message);
00402 }
00403
00404 void microfeed_publisher_send_item_data_signal(MicrofeedPublisher* publisher, const char* destination, const char* uri, const char* uid, const void* data, size_t data_size) {
00405 DBusMessage* message;
00406 DBusMessageIter iter;
00407 DBusMessageIter subiter;
00408
00409 message = dbus_message_new_signal(publisher->object_path, (destination ? MICROFEED_DBUS_INTERFACE_PUBLISHER_TO_DESTINATION : MICROFEED_DBUS_INTERFACE_PUBLISHER), MICROFEED_SIGNAL_NAME_ITEM_DATA);
00410 if (destination) {
00411 dbus_message_set_destination(message,destination);
00412 }
00413 dbus_message_iter_init_append(message, &iter);
00414 dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &uri);
00415 dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &uid);
00416 dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE_AS_STRING, &subiter);
00417 dbus_message_iter_append_fixed_array(&subiter, DBUS_TYPE_BYTE, &data, data_size);
00418 dbus_message_iter_close_container(&iter, &subiter);
00419 dbus_connection_send(publisher->connection, message, NULL);
00420 dbus_message_unref(message);
00421 }
00422
00423 const char* microfeed_publisher_get_object_path(MicrofeedPublisher* publisher) {
00424 return publisher->object_path;
00425 }
00426
00427 const char* microfeed_publisher_get_identifier(MicrofeedPublisher* publisher) {
00428 return publisher->identifier;
00429 }
00430
00431 void microfeed_publisher_remove_subscriber(MicrofeedPublisher* publisher, const char* unique_connection_name) {
00432 Subscriber* subscriber;
00433 MicrofeedStoreIterator* iterator;
00434 MicrofeedFeed* feed;
00435 char buffer[1024];
00436
00437 if ((subscriber = microfeed_store_get(publisher->subscribers, unique_connection_name, Subscriber))) {
00438 for (iterator = microfeed_store_iterate(subscriber->feeds, NULL);
00439 (feed = (MicrofeedFeed*)microfeed_store_iterator_get(iterator, MicrofeedFeed));
00440 microfeed_store_iterator_next(iterator)) {
00441 microfeed_feed_remove_subscriber(feed, unique_connection_name);
00442 microfeed_store_remove(subscriber->feeds, feed);
00443
00444 if (microfeed_feed_get_subscriber_count(feed) == 0) {
00445 microfeed_store_remove(publisher->feeds, feed);
00446 microfeed_feed_unref(feed);
00447 }
00448 }
00449
00450 snprintf(buffer, 1024, "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',method='NameOwnerChanged',arg0='%s'", unique_connection_name);
00451 dbus_bus_remove_match(subscriber->publisher->connection, buffer, NULL);
00452
00453 microfeed_store_remove(subscriber->publisher->subscribers, subscriber);
00454 subscriber_free(subscriber);
00455
00456 if (publisher->callbacks.no_more_subscribers &&
00457 microfeed_store_get_size(publisher->subscribers) == 0 &&
00458 microfeed_thread_pool_get_started_thread_count(publisher->thread_pool) == 0) {
00459 publisher->callbacks.no_more_subscribers(publisher, publisher->user_data);
00460 }
00461 }
00462 }
00463
00464 MicrofeedProvider* microfeed_publisher_get_provider(MicrofeedPublisher* publisher) {
00465
00466 return publisher->provider;
00467 }
00468
00469 void microfeed_publisher_set_provider(MicrofeedPublisher* publisher, MicrofeedProvider* provider) {
00470 publisher->provider = provider;
00471 }
00472
00473 static Subscriber* subscriber_new(MicrofeedPublisher* publisher, const char* unique_connection_name) {
00474 Subscriber* subscriber;
00475
00476 subscriber = (Subscriber*)microfeed_memory_allocate(Subscriber);
00477 subscriber->publisher = publisher;
00478 subscriber->unique_connection_name = strdup(unique_connection_name);
00479 subscriber->last_activity = time(NULL);
00480 subscriber->feeds = microfeed_store_new_sorted((MicrofeedStoreCompareKeysFunction)strcmp,
00481 (MicrofeedStoreGetKeyFunction)microfeed_feed_get_uri);
00482
00483 return subscriber;
00484 }
00485
00486 static void subscriber_free(Subscriber* subscriber) {
00487 free(subscriber->unique_connection_name);
00488 microfeed_store_free(subscriber->feeds);
00489
00490 microfeed_memory_free(subscriber);
00491 }
00492
00493 static const char* subscriber_get_unique_connection_name(Subscriber* subscriber) {
00494
00495 return subscriber->unique_connection_name;
00496 }
00497
00498 static MicrofeedError* method_add_item(MicrofeedPublisher* publisher, Subscriber* subscriber, DBusMessage* message) {
00499 DBusMessageIter iter;
00500 const char* uri;
00501 const char* uid;
00502 MicrofeedFeed* feed;
00503 MicrofeedItem* item;
00504 const char* key;
00505 const char* value;
00506
00507 if (!dbus_message_iter_init(message, &iter) || dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) {
00508
00509 return microfeed_error_new(MICROFEED_ERROR_INVALID_ARGUMENTS, "Expected feed uri argument in AddItem method.");
00510 }
00511 dbus_message_iter_get_basic(&iter, &uri);
00512 if (!dbus_message_iter_next(&iter) || dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) {
00513
00514 return microfeed_error_new(MICROFEED_ERROR_INVALID_ARGUMENTS, "Expected temporary item uid argument in AddItem method.");
00515 }
00516 dbus_message_iter_get_basic(&iter, &uid);
00517 if (!(feed = microfeed_store_get(subscriber->feeds, uri, MicrofeedFeed))) {
00518
00519 return microfeed_error_new(MICROFEED_ERROR_NO_SUCH_FEED, "Trying to add an item into a feed that does not exist.");
00520 }
00521
00522 item = microfeed_item_new_empty();
00523 while (dbus_message_iter_next(&iter)) {
00524 if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) {
00525 microfeed_item_free(item);
00526
00527 return microfeed_error_new(MICROFEED_ERROR_INVALID_ARGUMENTS, "Expected string key in AddItem method.");
00528 }
00529 dbus_message_iter_get_basic(&iter, &key);
00530
00531 if (!dbus_message_iter_next(&iter) || dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) {
00532 microfeed_item_free(item);
00533
00534 return microfeed_error_new(MICROFEED_ERROR_INVALID_ARGUMENTS, "Expected string value in AddItem method.");
00535 }
00536 dbus_message_iter_get_basic(&iter, &value);
00537
00538 microfeed_item_set_property(item, key, value);
00539 }
00540 microfeed_feed_call_modify_item_callback(feed, NULL, item);
00541 microfeed_item_free(item);
00542
00543 return NULL;
00544 }
00545
00546 static MicrofeedError* method_modify_item(MicrofeedPublisher* publisher, Subscriber* subscriber, DBusMessage* message) {
00547 DBusMessageIter iter;
00548 const char* uri;
00549 const char* uid;
00550 MicrofeedFeed* feed;
00551 MicrofeedItem* existing_item;
00552 MicrofeedItem* new_item;
00553 const char* key;
00554 const char* value;
00555
00556 if (!dbus_message_iter_init(message, &iter) || dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) {
00557
00558 return microfeed_error_new(MICROFEED_ERROR_INVALID_ARGUMENTS, "Expected feed uri argument in ModifyItem method.");
00559 }
00560 dbus_message_iter_get_basic(&iter, &uri);
00561 if (!dbus_message_iter_next(&iter) || dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) {
00562
00563 return microfeed_error_new(MICROFEED_ERROR_INVALID_ARGUMENTS, "Expected item uid argument in ModifyItem method.");
00564 }
00565 dbus_message_iter_get_basic(&iter, &uid);
00566 if (!(feed = microfeed_store_get(subscriber->feeds, uri, MicrofeedFeed))) {
00567
00568 return microfeed_error_new(MICROFEED_ERROR_NO_SUCH_FEED, "Trying to modify an item of a feed that does not exist.");
00569 }
00570
00571 if (!(existing_item = microfeed_feed_get_item(feed, uid))) {
00572
00573 return microfeed_error_new(MICROFEED_ERROR_NO_SUCH_ITEM, "Trying to modify an item that does not exist.");
00574 }
00575
00576 new_item = microfeed_item_new(uid, 0);
00577 while (dbus_message_iter_next(&iter)) {
00578 if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) {
00579 microfeed_item_free(new_item);
00580
00581 return microfeed_error_new(MICROFEED_ERROR_INVALID_ARGUMENTS, "Expected string key in ModifyItem method.");
00582 }
00583 dbus_message_iter_get_basic(&iter, &key);
00584
00585 if (!dbus_message_iter_next(&iter) || dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) {
00586 microfeed_item_free(new_item);
00587
00588 return microfeed_error_new(MICROFEED_ERROR_INVALID_ARGUMENTS, "Expected string value in ModifyItem method.");
00589 }
00590 dbus_message_iter_get_basic(&iter, &value);
00591
00592 microfeed_item_set_property(new_item, key, value);
00593 }
00594 microfeed_feed_call_modify_item_callback(feed, new_item, existing_item);
00595 microfeed_item_free(new_item);
00596
00597 return NULL;
00598 }
00599
00600 static MicrofeedError* method_read_items(MicrofeedPublisher* publisher, Subscriber* subscriber, DBusMessage* message) {
00601 DBusMessageIter iter;
00602 const char* uri;
00603 const char* start_uid;
00604 const char* end_uid;
00605 MicrofeedFeed* feed;
00606
00607 if (!dbus_message_iter_init(message, &iter) || dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) {
00608
00609 return microfeed_error_new(MICROFEED_ERROR_INVALID_ARGUMENTS, "Expected feed uri argument in ReadItem method.");
00610 }
00611 dbus_message_iter_get_basic(&iter, &uri);
00612 if (!dbus_message_iter_next(&iter) || dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) {
00613
00614 return microfeed_error_new(MICROFEED_ERROR_INVALID_ARGUMENTS, "Expected start item uid argument in ReadItem method.");
00615 }
00616 dbus_message_iter_get_basic(&iter, &start_uid);
00617 if (!dbus_message_iter_next(&iter) || dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) {
00618
00619 return microfeed_error_new(MICROFEED_ERROR_INVALID_ARGUMENTS, "Expected end item uid argument in ReadItem method.");
00620 }
00621 dbus_message_iter_get_basic(&iter, &end_uid);
00622 if (!(feed = microfeed_store_get(subscriber->feeds, uri, MicrofeedFeed))) {
00623
00624 return microfeed_error_new(MICROFEED_ERROR_NO_SUCH_FEED, "Trying to read items of a feed that does not exist.");
00625 }
00626
00627 microfeed_feed_unset_item_statuses(feed, start_uid, end_uid, MICROFEED_ITEM_STATUS_UNREAD);
00628
00629 return NULL;
00630 }
00631
00632 static MicrofeedError* method_republish_items(MicrofeedPublisher* publisher, Subscriber* subscriber, DBusMessage* message) {
00633 DBusMessageIter iter;
00634 const char* uri;
00635 const char* start_uid;
00636 const char* end_uid;
00637 dbus_uint16_t max_count;
00638 MicrofeedFeed* feed;
00639
00640 if (!dbus_message_iter_init(message, &iter) || dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) {
00641
00642 return microfeed_error_new(MICROFEED_ERROR_INVALID_ARGUMENTS, "Expected a feed uri argument in PublishItems method");
00643 }
00644 dbus_message_iter_get_basic(&iter, &uri);
00645 if (!dbus_message_iter_next(&iter) || dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) {
00646
00647 return microfeed_error_new(MICROFEED_ERROR_INVALID_ARGUMENTS, "Expected a start item uid argument in PublishItems method");
00648 }
00649 dbus_message_iter_get_basic(&iter, &start_uid);
00650 if (!dbus_message_iter_next(&iter) || dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) {
00651
00652 return microfeed_error_new(MICROFEED_ERROR_INVALID_ARGUMENTS, "Expected an end item uid argument in PublishItems method");
00653 }
00654 dbus_message_iter_get_basic(&iter, &end_uid);
00655 if (!dbus_message_iter_next(&iter) || dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT16) {
00656
00657 return microfeed_error_new(MICROFEED_ERROR_INVALID_ARGUMENTS, "Expected an maximum count argument in PublishItems method");
00658 }
00659 dbus_message_iter_get_basic(&iter, &max_count);
00660
00661 if (!(feed = microfeed_store_get(subscriber->feeds, uri, MicrofeedFeed))) {
00662
00663 return microfeed_error_new(MICROFEED_ERROR_NO_SUCH_FEED, "Trying to republish items of a feed that does not exist.");
00664 }
00665
00666 microfeed_feed_republish(feed, (*start_uid ? start_uid : NULL), (*end_uid ? end_uid : NULL), max_count, subscriber->unique_connection_name);
00667
00668 return NULL;
00669 }
00670
00671 static MicrofeedError* method_send_item_data(MicrofeedPublisher* publisher, Subscriber* subscriber, DBusMessage* message) {
00672 DBusMessageIter iter;
00673 const char* uri;
00674 const char* uid;
00675 MicrofeedFeed* feed;
00676
00677 if (!publisher->callbacks.download_image) {
00678
00679 return microfeed_error_new(MICROFEED_ERROR_NO_ITEM_DATA, "Publisher does not support image downloading");
00680 }
00681 if (!dbus_message_iter_init(message, &iter) || dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) {
00682
00683 return microfeed_error_new(MICROFEED_ERROR_INVALID_ARGUMENTS, "Expected a feed uri argument in SendItemData method");
00684 }
00685 dbus_message_iter_get_basic(&iter, &uri);
00686 if (!dbus_message_iter_next(&iter) || dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) {
00687
00688 return microfeed_error_new(MICROFEED_ERROR_INVALID_ARGUMENTS, "Expected an item uid argument in SendItemData method");
00689 }
00690 dbus_message_iter_get_basic(&iter, &uid);
00691
00692 if (!(feed = microfeed_store_get(subscriber->feeds, uri, MicrofeedFeed))) {
00693
00694 return microfeed_error_new(MICROFEED_ERROR_NO_SUCH_FEED, "Trying to republish items of a feed that does not exist.");
00695 }
00696
00697 microfeed_feed_send_item_data(feed, uid, subscriber->unique_connection_name);
00698
00699 return NULL;
00700 }
00701
00702
00703 static MicrofeedError* method_ping(MicrofeedPublisher* publisher, Subscriber* subscriber, DBusMessage* message) {
00704
00705 return NULL;
00706 }
00707
00708 static MicrofeedError* method_subscribe_feed(MicrofeedPublisher* publisher, Subscriber* subscriber, DBusMessage* message) {
00709 MicrofeedFeed* feed;
00710 DBusError error;
00711 char* uri;
00712
00713 dbus_error_init(&error);
00714 if (!dbus_message_get_args(message, &error, DBUS_TYPE_STRING, &uri, DBUS_TYPE_INVALID)) {
00715
00716 return microfeed_error_new(MICROFEED_ERROR_INVALID_ARGUMENTS, "Expected feed uri argument in SubscribeFeed method");
00717 }
00718
00719 if (!(feed = microfeed_store_get(publisher->feeds, uri, MicrofeedFeed))) {
00720 if ((feed = publisher->callbacks.instantiate_feed(publisher, uri, publisher->user_data))) {
00721 microfeed_store_insert(publisher->feeds, feed);
00722 microfeed_store_insert(subscriber->feeds, feed);
00723
00724 microfeed_feed_add_subscriber(feed, subscriber->unique_connection_name);
00725 } else {
00726
00727 return microfeed_error_new(MICROFEED_ERROR_NO_SUCH_FEED, "Failed to subscribe a new feed");
00728 }
00729 } else {
00730 microfeed_store_insert(subscriber->feeds, feed);
00731
00732 microfeed_feed_add_subscriber(feed, subscriber->unique_connection_name);
00733 }
00734
00735 return NULL;
00736 }
00737
00738 static MicrofeedError* method_unsubscribe_feed(MicrofeedPublisher* publisher, Subscriber* subscriber, DBusMessage* message) {
00739 MicrofeedFeed* feed;
00740 DBusError error;
00741 char* uri;
00742
00743 dbus_error_init(&error);
00744 if (!dbus_message_get_args(message, &error, DBUS_TYPE_STRING, &uri, DBUS_TYPE_INVALID)) {
00745
00746 return microfeed_error_new(MICROFEED_ERROR_INVALID_ARGUMENTS, "Expected feed uri argument in UnsubscribeFeed method");
00747 }
00748 if (!microfeed_store_remove(subscriber->feeds, feed)) {
00749
00750 return microfeed_error_new(MICROFEED_ERROR_FEED_NOT_SUBSCRIBED, "Trying to unsubscribe a feed that is not subscribed");
00751 }
00752
00753 microfeed_feed_remove_subscriber(feed, subscriber->unique_connection_name);
00754 if (microfeed_feed_get_subscriber_count(feed) == 0) {
00755 microfeed_store_remove(publisher->feeds, feed);
00756 microfeed_feed_unref(feed);
00757 }
00758
00759 return NULL;
00760 }
00761
00762 static MicrofeedError* method_update_feed(MicrofeedPublisher* publisher, Subscriber* subscriber, DBusMessage* message) {
00763 MicrofeedFeed* feed;
00764 DBusError error;
00765 char* uri;
00766
00767 dbus_error_init(&error);
00768 if (!dbus_message_get_args(message, &error, DBUS_TYPE_STRING, &uri, DBUS_TYPE_INVALID)) {
00769
00770 return microfeed_error_new(MICROFEED_ERROR_INVALID_ARGUMENTS, "Expected feed uri argument in UpdateFeed method");
00771 }
00772
00773 if (!(feed = microfeed_store_get(subscriber->feeds, uri, MicrofeedFeed))) {
00774
00775 return microfeed_error_new(MICROFEED_ERROR_FEED_NOT_SUBSCRIBED, "Trying to update a feed that is not subscribed");
00776 }
00777
00778 microfeed_feed_update(feed, subscriber->unique_connection_name);
00779
00780 return NULL;
00781 }
00782
00783 static MicrofeedError* method_create_publisher(MicrofeedPublisher* publisher, Subscriber* subscriber, DBusMessage* message) {
00784
00785
00786 microfeed_feed_add_new_setting(publisher->settings_feed, "archive_old_items", 30000, "Archive old items", "hours", "int", "24");
00787
00788 publisher->callbacks.initialize_settings(publisher, publisher->user_data);
00789
00790 return NULL;
00791 }
00792
00793 static MicrofeedError* method_destroy_publisher(MicrofeedPublisher* publisher, Subscriber* subscriber, DBusMessage* message) {
00794 MicrofeedStoreIterator* subscriber_iterator;
00795 Subscriber* feed_subscriber;
00796 MicrofeedStoreIterator* feed_iterator;
00797 MicrofeedFeed* feed;
00798
00799 dbus_connection_unregister_object_path(publisher->connection, publisher->object_path);
00800
00801 for (subscriber_iterator = microfeed_store_iterate(publisher->subscribers, NULL);
00802 (feed_subscriber = microfeed_store_iterator_get(subscriber_iterator, Subscriber));
00803 microfeed_store_iterator_next(subscriber_iterator)) {
00804 for (feed_iterator = microfeed_store_iterate(feed_subscriber->feeds, NULL);
00805 (feed = microfeed_store_iterator_get(feed_iterator, MicrofeedFeed));
00806 microfeed_store_iterator_next(feed_iterator)) {
00807 microfeed_feed_remove_subscriber(feed, subscriber->unique_connection_name);
00808 microfeed_publisher_send_error_signal(publisher, subscriber->unique_connection_name, MICROFEED_ERROR_NO_SUCH_PUBLISHER, NULL, NULL, "Publisher destroyed.");
00809 }
00810 }
00811 for (feed_iterator = microfeed_store_iterate(publisher->feeds, NULL);
00812 (feed = microfeed_store_iterator_get(feed_iterator, MicrofeedFeed));
00813 microfeed_store_iterator_next(feed_iterator)) {
00814 microfeed_feed_unref(feed);
00815 }
00816
00817 return NULL;
00818 }
00819
00820 static void object_unregister(DBusConnection* connection, void* user_data) {
00821 }
00822
00823 static DBusHandlerResult object_message(DBusConnection* connection, DBusMessage* message, void* user_data) {
00824 DBusHandlerResult result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
00825 MicrofeedPublisher* publisher;
00826 const char* unique_connection_name;
00827 int i;
00828 Subscriber* subscriber;
00829 char buffer[1024];
00830 MicrofeedError* error;
00831 DBusMessage* reply;
00832
00833 publisher = (MicrofeedPublisher*)user_data;
00834 unique_connection_name = dbus_message_get_sender(message);
00835 if (!(subscriber = (Subscriber*)microfeed_store_get(publisher->subscribers, unique_connection_name, Subscriber))) {
00836 subscriber = subscriber_new(publisher, unique_connection_name);
00837 microfeed_store_insert(publisher->subscribers, subscriber);
00838
00839 snprintf(buffer, 1024, "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged',arg0='%s'", unique_connection_name);
00840 dbus_bus_add_match(connection, buffer, NULL);
00841 }
00842
00843 subscriber->last_activity = time(NULL);
00844 for (i = 0; method_callbacks[i].name; i++) {
00845 if (dbus_message_is_method_call(message, MICROFEED_DBUS_INTERFACE_PUBLISHER, method_callbacks[i].name)) {
00846 printf("%s (%s)\n", method_callbacks[i].name, unique_connection_name);
00847 if ((error = method_callbacks[i].callback(publisher, subscriber, message))) {
00848 snprintf(buffer, 1024, "%s.%s", MICROFEED_DBUS_INTERFACE_ERROR, microfeed_error_get_name(error));
00849 reply = dbus_message_new_error(message, buffer, microfeed_error_get_message(error));
00850 microfeed_error_free(error);
00851 } else {
00852 reply = dbus_message_new_method_return(message);
00853 }
00854 dbus_connection_send(connection, reply, NULL);
00855 dbus_message_unref(reply);
00856 result = DBUS_HANDLER_RESULT_HANDLED;
00857 break;
00858 }
00859 }
00860
00861 if (microfeed_store_get_size(subscriber->feeds) == 0) {
00862 snprintf(buffer, 1024, "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',method='NameOwnerChanged',arg0='%s'", unique_connection_name);
00863 dbus_bus_remove_match(connection, buffer, NULL);
00864
00865 microfeed_store_remove(publisher->subscribers, subscriber);
00866 subscriber_free(subscriber);
00867 }
00868
00869 if (publisher->callbacks.no_more_subscribers &&
00870 microfeed_store_get_size(publisher->subscribers) == 0 &&
00871 microfeed_thread_pool_get_started_thread_count(publisher->thread_pool) == 0) {
00872 publisher->callbacks.no_more_subscribers(publisher, publisher->user_data);
00873 }
00874
00875 return result;
00876 }
00877
00878 static MicrofeedError* settings_feed_modify_item(MicrofeedFeed* feed, MicrofeedItem* existing_item, MicrofeedItem* new_item, void* user_data) {
00879 MicrofeedError* error = NULL;;
00880 MicrofeedPublisher* publisher;
00881 const char* value;
00882
00883 publisher = microfeed_feed_get_publisher(feed);
00884 if (!existing_item) {
00885 error = microfeed_error_new(MICROFEED_ERROR_NO_SUCH_ITEM, "No such setting.");
00886 } else {
00887 value = microfeed_item_get_property(new_item, "value");
00888 if (publisher->callbacks.update_setting(publisher, microfeed_item_get_uid(new_item), value, publisher->user_data)) {
00889 microfeed_item_set_property(existing_item, "value", value);
00890 microfeed_feed_replace_item(feed, existing_item);
00891 }
00892 }
00893
00894 return error;
00895
00896 }
00897
00898 static MicrofeedError* images_feed_download_item_data(MicrofeedFeed* feed, const char* uid, void** data, size_t* length, void* user_data) {
00899 MicrofeedPublisher* publisher;
00900 MicrofeedError* error;
00901
00902 publisher = microfeed_feed_get_publisher(feed);
00903 error = publisher->callbacks.download_image(publisher, uid, data, length, publisher->user_data);
00904 if (!error) {
00905 microfeed_feed_unset_item_status(feed, uid, MICROFEED_ITEM_STATUS_UNREAD);
00906 }
00907
00908 return error;
00909 }
00910
00911 static void thread_exited(MicrofeedThread* thread, void* user_data) {
00912 MicrofeedPublisher* publisher;
00913
00914 publisher = (MicrofeedPublisher*)user_data;
00915 if (publisher->callbacks.no_more_subscribers &&
00916 microfeed_store_get_size(publisher->subscribers) == 0 &&
00917 microfeed_thread_pool_get_started_thread_count(publisher->thread_pool) == 0) {
00918 publisher->callbacks.no_more_subscribers(publisher, publisher->user_data);
00919 }
00920 }