00001
00002 #include <microfeed-provider/microfeedfeed.h>
00003 #include <microfeed-common/microfeedmisc.h>
00004 #include <microfeed-common/microfeeditem.h>
00005 #include <microfeed-common/microfeedthread.h>
00006 #include <microfeed-provider/microfeederror.h>
00007 #include <microfeed-common/microfeedprotocol.h>
00008
00009 #include <stdlib.h>
00010 #include <string.h>
00011 #include <stdio.h>
00012 #include <stdint.h>
00013
00014 typedef struct _SendItemDataData SendItemDataData;
00015
00016 struct _MicrofeedFeed {
00017 unsigned int reference_count;
00018 MicrofeedWeakReference* weak_reference;
00019
00020 char* uri;
00021 char* name;
00022 MicrofeedFeedPermission feed_permission;
00023 MicrofeedItem* metadata_item;
00024 MicrofeedPublisher* publisher;
00025 MicrofeedFeedCallbacks callbacks;
00026 void* user_data;
00027 MicrofeedFeedIterator* iterators;
00028 int subscriber_count;
00029 MicrofeedDatabase* database;
00030 MicrofeedDatabaseIndex* database_index;
00031 MicrofeedDatabase* statuses_database;
00032 MicrofeedDatabase* item_data_database;
00033 MicrofeedMutex* mutex;
00034 int updating;
00035 MicrofeedStore* send_item_data_datas;
00036 };
00037
00038 struct _MicrofeedFeedIterator {
00039 MicrofeedFeedIterator* previous;
00040 MicrofeedFeedIterator* next;
00041
00042 MicrofeedFeed* feed;
00043 MicrofeedDatabaseIterator* database_iterator;
00044 int backwards : 1;
00045 int timeline : 1;
00046 };
00047
00048 typedef struct {
00049 MicrofeedFeed* feed;
00050 const char* start_uid;
00051 const char* end_uid;
00052 unsigned int max_count;
00053 const char* bus_name;
00054 } RepublishingData;
00055
00056 typedef struct {
00057 MicrofeedFeed* feed;
00058 MicrofeedItem* existing_item;
00059 MicrofeedItem* new_item;
00060 } ModifyItemData;
00061
00062 struct _SendItemDataData {
00063 MicrofeedFeed* feed;
00064 char* uid;
00065 MicrofeedStore* bus_names;
00066 };
00067
00068 static void index_function(void* key, const size_t key_size, void* data, const size_t data_size, void** index_key, size_t* index_key_size);
00069 static int compare_function(const void* key1, const size_t key1_size, const void* key2, const size_t key2_size);
00070 static void* update_thread_periodical(void* data);
00071 static void* update_thread_user_initiated(void* data);
00072 static void* republish_thread(void* user_data);
00073 static void* send_item_data_thread(void* data);
00074 static void* modify_item_thread(void* user_data);
00075 static void remove_old_items(MicrofeedFeed* feed);
00076 static const char* send_item_data_data_get_uid(SendItemDataData* send_item_data_data);
00077
00078 MicrofeedFeed* microfeed_feed_new(MicrofeedPublisher* publisher, const char* uri, const char* name, MicrofeedFeedPermission feed_permission, MicrofeedFeedCallbacks* callbacks, void* user_data) {
00079 MicrofeedFeed* feed;
00080 char* database_name;
00081 char* s;
00082
00083 feed = (MicrofeedFeed*)microfeed_memory_allocate(MicrofeedFeed);
00084
00085 feed->reference_count = 1;
00086 feed->weak_reference = NULL;
00087
00088 feed->uri = strdup(uri);
00089 feed->name = strdup(name);
00090 feed->feed_permission = feed_permission;
00091 feed->metadata_item = microfeed_item_new(MICROFEED_ITEM_UID_FEED_METADATA, time(NULL));
00092 microfeed_item_set_property(feed->metadata_item, MICROFEED_ITEM_PROPERTY_NAME_FEED_NAME, name);
00093 s = microfeed_feed_permission_to_string(feed_permission);
00094 if (s && *s) {
00095 microfeed_item_set_property(feed->metadata_item, MICROFEED_ITEM_PROPERTY_NAME_FEED_PERMISSION, s);
00096 }
00097 free(s);
00098 feed->publisher = publisher;
00099 feed->iterators = NULL;
00100 if (callbacks) {
00101 feed->callbacks = *callbacks;
00102 }
00103 feed->user_data = user_data;
00104
00105 feed->database = microfeed_database_environment_get_database(microfeed_publisher_get_database_environment(publisher), uri, compare_function);
00106 feed->database_index = microfeed_database_get_index(feed->database, "uid", index_function);
00107 database_name = microfeed_util_string_concatenate(uri, ":microfeed.statuses", NULL);
00108 feed->statuses_database = microfeed_database_environment_get_database(microfeed_publisher_get_database_environment(publisher), database_name, NULL);
00109 free(database_name);
00110 if (feed->callbacks.download_item_data) {
00111 database_name = microfeed_util_string_concatenate(uri, ":microfeed.item_data", NULL);
00112 feed->item_data_database = microfeed_database_environment_get_database(microfeed_publisher_get_database_environment(publisher), database_name, NULL);
00113 free(database_name);
00114 }
00115 feed->mutex = microfeed_mutex_new();
00116 feed->send_item_data_datas = microfeed_store_new_sorted((MicrofeedStoreCompareKeysFunction)strcmp, (MicrofeedStoreGetKeyFunction)send_item_data_data_get_uid);
00117
00118 return feed;
00119 }
00120
00121
00122 void microfeed_feed_free(MicrofeedFeed* feed) {
00123 MicrofeedFeedIterator* iterator;
00124
00125 if (feed->callbacks.destroy) {
00126 feed->callbacks.destroy(feed, feed->user_data);
00127 }
00128
00129 free(feed->uri);
00130 for (iterator = feed->iterators; iterator; iterator = iterator->next) {
00131 iterator->feed = NULL;
00132 }
00133
00134 microfeed_database_unref(feed->database);
00135 microfeed_database_unref(feed->statuses_database);
00136 if (feed->item_data_database) {
00137 microfeed_database_unref(feed->item_data_database);
00138 }
00139 microfeed_mutex_free(feed->mutex);
00140
00141
00142
00143 feed->uri = NULL;
00144 feed->publisher = NULL;
00145 feed->callbacks.destroy = NULL;
00146 feed->callbacks.update = NULL;
00147 feed->subscriber_count = 0;
00148 feed->iterators = NULL;
00149 microfeed_weak_reference_invalidate(feed->weak_reference);
00150 feed->weak_reference = NULL;
00151
00152 free(feed);
00153 }
00154
00155 MicrofeedFeed* microfeed_feed_ref(MicrofeedFeed* feed) {
00156 microfeed_mutex_lock(feed->mutex);
00157
00158 feed->reference_count++;
00159
00160 microfeed_mutex_unlock(feed->mutex);
00161
00162 return feed;
00163 }
00164
00165 void microfeed_feed_unref(MicrofeedFeed* feed) {
00166 microfeed_mutex_lock(feed->mutex);
00167
00168 feed->reference_count--;
00169
00170 if (feed->reference_count == 0) {
00171 microfeed_feed_free(feed);
00172 } else {
00173
00174 microfeed_mutex_unlock(feed->mutex);
00175 }
00176 }
00177
00178 MicrofeedWeakReference* microfeed_feed_get_weak_reference(MicrofeedFeed* feed) {
00179 MicrofeedWeakReference* weak_reference;
00180
00181 microfeed_mutex_lock(feed->mutex);
00182
00183 weak_reference = feed->weak_reference = microfeed_weak_reference_new(feed, feed->weak_reference);
00184
00185 microfeed_mutex_unlock(feed->mutex);
00186
00187 return weak_reference;
00188 }
00189
00190 MicrofeedPublisher* microfeed_feed_get_publisher(MicrofeedFeed* feed) {
00191
00192 return feed->publisher;
00193 }
00194
00195 void microfeed_feed_stop_update(MicrofeedFeed* feed) {
00196 microfeed_mutex_lock(feed->mutex);
00197
00198 if (feed->updating && microfeed_publisher_get_singleton_update_thread(feed->publisher)) {
00199 microfeed_publisher_send_feed_signal(feed->publisher, NULL, MICROFEED_SIGNAL_NAME_FEED_UPDATE_ENDED, feed->uri);
00200 feed->updating = 0;
00201 }
00202
00203 microfeed_mutex_unlock(feed->mutex);
00204 }
00205
00206 void microfeed_feed_replace_item(MicrofeedFeed* feed, MicrofeedItem* item) {
00207 const char* uid;
00208 size_t uid_size;
00209 uint64_t timestamp;
00210 void* key;
00211 size_t key_size;
00212 const void* data;
00213 size_t data_size;
00214 void* existing_key;
00215 size_t existing_key_size;
00216 void* existing_data;
00217 size_t existing_size;
00218 uint64_t existing_timestamp;
00219 void* status_data;
00220 size_t status_data_size;
00221 char status;
00222 MicrofeedItem* existing_item;
00223
00224 microfeed_mutex_lock(feed->mutex);
00225
00226 uid = microfeed_item_get_uid(item);
00227 uid_size = strlen(uid) + 1;
00228 timestamp = (uint64_t)microfeed_item_get_timestamp(item);
00229 key_size = sizeof(uint64_t) + uid_size;
00230 key = (char*)malloc(key_size);
00231 *((uint64_t*)key) = timestamp;
00232 memcpy(key + sizeof(uint64_t), uid, uid_size);
00233 microfeed_item_marshal_properties(item, &data, &data_size);
00234 if (microfeed_database_index_get_data(feed->database_index, uid, uid_size, &existing_key, &existing_key_size, &existing_data, &existing_size)) {
00235 existing_timestamp = *((uint64_t*)existing_key);
00236 if (timestamp != existing_timestamp || data_size != existing_size || memcmp(data, existing_data, existing_size)) {
00237 if (microfeed_database_get_data(feed->statuses_database, uid, uid_size, &status_data, &status_data_size)) {
00238 status = *((char*)status_data);
00239 free(status_data);
00240 if (!(status & (char)MICROFEED_ITEM_STATUS_ACTIVE)) {
00241 status |= (char)(MICROFEED_ITEM_STATUS_ACTIVE);
00242 microfeed_database_replace_data(feed->statuses_database, uid, uid_size, &status, sizeof(char));
00243 }
00244 } else {
00245 status = (char)MICROFEED_ITEM_STATUS_ACTIVE;
00246 microfeed_database_replace_data(feed->statuses_database, uid, uid_size, &status, sizeof(char));
00247 }
00248
00249 existing_item = microfeed_item_new_with_status(uid, existing_timestamp, status);
00250 microfeed_item_demarshal_properties(existing_item, existing_data, existing_size);
00251 microfeed_publisher_handle_item_property_change(feed->publisher, existing_item, item);
00252 microfeed_item_free(existing_item);
00253
00254 microfeed_publisher_send_item_signal(feed->publisher, NULL, MICROFEED_SIGNAL_NAME_ITEM_CHANGED, feed->uri, item);
00255
00256 microfeed_database_index_remove_data(feed->database_index, uid, uid_size);
00257
00258 microfeed_database_replace_data(feed->database, key, key_size, data, data_size);
00259 }
00260 free(existing_key);
00261 free(existing_data);
00262 } else {
00263 status = (char)MICROFEED_ITEM_STATUS_NEW;
00264 microfeed_publisher_handle_item_property_change(feed->publisher, NULL, item);
00265 microfeed_publisher_send_item_signal(feed->publisher, NULL, MICROFEED_SIGNAL_NAME_ITEM_ADDED, feed->uri, item);
00266 microfeed_database_replace_data(feed->database, key, key_size, data, data_size);
00267 microfeed_database_replace_data(feed->statuses_database, uid, uid_size, &status, sizeof(char));
00268 }
00269 free(key);
00270
00271 microfeed_mutex_unlock(feed->mutex);
00272 }
00273
00274 void microfeed_feed_remove_item(MicrofeedFeed* feed, const char* uid) {
00275 size_t uid_size;
00276 void* key;
00277 size_t key_size;
00278 void* data;
00279 size_t data_size;
00280 uint64_t timestamp;
00281 MicrofeedItem* item;
00282
00283 microfeed_mutex_lock(feed->mutex);
00284
00285 uid_size = strlen(uid) + 1;
00286 if (microfeed_database_index_get_data(feed->database_index, uid, uid_size, &key, &key_size, &data, &data_size)) {
00287 timestamp = *((uint64_t*)key);
00288 item = microfeed_item_new(uid, timestamp);
00289 microfeed_item_demarshal_properties(item, data, data_size);
00290 microfeed_publisher_handle_item_property_change(feed->publisher, item, NULL);
00291 microfeed_item_free(item);
00292 free(key);
00293 free(data);
00294
00295 microfeed_database_index_remove_data(feed->database_index, uid, uid_size);
00296 microfeed_database_remove_data(feed->statuses_database, uid, uid_size);
00297 if (feed->item_data_database) {
00298 microfeed_database_remove_data(feed->item_data_database, uid, uid_size);
00299 }
00300 microfeed_publisher_send_item_uid_signal(feed->publisher, NULL, MICROFEED_SIGNAL_NAME_ITEM_REMOVED, feed->uri, uid);
00301 }
00302
00303 microfeed_mutex_unlock(feed->mutex);
00304 }
00305
00306 void microfeed_feed_remove_items(MicrofeedFeed* feed, const char* start_uid, const char* end_uid) {
00307 size_t uid_size;
00308 MicrofeedDatabaseIterator* iterator;
00309 const void* key;
00310 size_t key_size;
00311 const void* data;
00312 size_t data_size;
00313 uint64_t timestamp;
00314 MicrofeedItem* item;
00315
00316 microfeed_mutex_lock(feed->mutex);
00317
00318 uid_size = (start_uid ? strlen(start_uid) + 1 : 0);
00319 for (iterator = microfeed_database_index_iterate(feed->database_index, start_uid, uid_size, 0);
00320 (microfeed_database_iterator_get(iterator, &key, &key_size, &data, &data_size)) && (!end_uid || strcmp ((const char*)key, end_uid) <= 0);
00321 microfeed_database_iterator_next(iterator)) {
00322 timestamp = *((uint64_t*)key);
00323 item = microfeed_item_new(key + sizeof(uint64_t), timestamp);
00324 microfeed_item_demarshal_properties(item, data, data_size);
00325 microfeed_publisher_handle_item_property_change(feed->publisher, item, NULL);
00326 microfeed_item_free(item);
00327
00328 microfeed_database_index_remove_data(feed->database_index, key + sizeof(uint64_t), key_size - sizeof(uint64_t));
00329 microfeed_database_remove_data(feed->statuses_database, key + sizeof(uint64_t), key_size - sizeof(uint64_t));
00330 if (feed->item_data_database) {
00331 microfeed_database_remove_data(feed->item_data_database, key + sizeof(uint64_t), key_size - sizeof(uint64_t));
00332 }
00333 microfeed_publisher_send_item_uid_signal(feed->publisher, NULL, MICROFEED_SIGNAL_NAME_ITEM_REMOVED, feed->uri, key + sizeof(uint64_t));
00334 }
00335 microfeed_database_iterator_free(iterator);
00336
00337 microfeed_mutex_unlock(feed->mutex);
00338 }
00339
00340 void microfeed_feed_add_subscriber(MicrofeedFeed* feed, const char* bus_name) {
00341 microfeed_mutex_lock(feed->mutex);
00342
00343 feed->subscriber_count++;
00344 feed->reference_count++;
00345
00346 microfeed_mutex_unlock(feed->mutex);
00347 }
00348
00349 int microfeed_feed_get_subscriber_count(MicrofeedFeed* feed) {
00350 int subscriber_count;
00351
00352 microfeed_mutex_lock(feed->mutex);
00353
00354 subscriber_count = feed->subscriber_count;
00355
00356 microfeed_mutex_unlock(feed->mutex);
00357
00358 return subscriber_count;
00359 }
00360
00361 MicrofeedItem* microfeed_feed_get_item(MicrofeedFeed* feed, const char* uid) {
00362 void* key;
00363 size_t key_size;
00364 void* data;
00365 size_t data_size;
00366 uint64_t timestamp;
00367 MicrofeedItem* item;
00368
00369 microfeed_mutex_lock(feed->mutex);
00370
00371 if (!microfeed_database_index_get_data(feed->database_index, uid, strlen(uid) + 1, &key, &key_size, &data, &data_size)) {
00372 item = NULL;
00373 } else {
00374 timestamp = *((uint64_t*)key);
00375 item = microfeed_item_new((char*)(key + sizeof(uint64_t)), (time_t)timestamp);
00376 microfeed_item_demarshal_properties(item, data, data_size);
00377 free(data);
00378 }
00379
00380 microfeed_mutex_unlock(feed->mutex);
00381
00382 return item;
00383 }
00384
00385 const char* microfeed_feed_get_uri(MicrofeedFeed* feed) {
00386
00387 return feed->uri;
00388 }
00389
00390 const char* microfeed_feed_get_name(MicrofeedFeed* feed) {
00391
00392 return feed->name;
00393 }
00394
00395 void microfeed_feed_set_name(MicrofeedFeed* feed, const char* name) {
00396 if (strcmp(name, feed->name)) {
00397 free(feed->name);
00398 feed->name = strdup(name);
00399
00400 }
00401 }
00402
00403 MicrofeedItem* microfeed_feed_get_metadata_item(MicrofeedFeed* feed) {
00404
00405 return feed->metadata_item;
00406 }
00407
00408 MicrofeedFeedIterator* microfeed_feed_iterate(MicrofeedFeed* feed, const char* start_uid, int backwards) {
00409 MicrofeedFeedIterator* iterator;
00410
00411 microfeed_mutex_lock(feed->mutex);
00412
00413 iterator = (MicrofeedFeedIterator*)microfeed_memory_allocate(MicrofeedFeedIterator);
00414 iterator->feed = feed;
00415 iterator->backwards = backwards;
00416 iterator->timeline = 0;
00417 iterator->database_iterator = microfeed_database_index_iterate(feed->database_index, start_uid, (start_uid ? strlen(start_uid) + 1 : 0), backwards);
00418
00419 if (feed->iterators) {
00420 feed->iterators->previous = iterator;
00421 iterator->next = feed->iterators;
00422 } else {
00423 iterator->next = NULL;
00424 }
00425 iterator->previous = NULL;
00426 feed->iterators = iterator;
00427
00428 microfeed_mutex_unlock(feed->mutex);
00429
00430 return iterator;
00431 }
00432
00433 MicrofeedFeedIterator* microfeed_feed_iterate_timeline(MicrofeedFeed* feed, const time_t start_timestamp, int backwards) {
00434 MicrofeedFeedIterator* iterator;
00435 char* key;
00436
00437 microfeed_mutex_lock(feed->mutex);
00438
00439 iterator = (MicrofeedFeedIterator*)microfeed_memory_allocate(MicrofeedFeedIterator);
00440 iterator->feed = feed;
00441 iterator->backwards = backwards;
00442 iterator->timeline = 1;
00443 if (start_timestamp) {
00444 key = (char*)malloc(sizeof(uint64_t) + 1);
00445 *((uint64_t*)key) = (uint64_t)start_timestamp;
00446 *(key + sizeof(uint64_t)) = 0;
00447 iterator->database_iterator = microfeed_database_iterate(feed->database, key, sizeof(uint64_t) + 1, backwards);
00448 free(key);
00449 } else {
00450 iterator->database_iterator = microfeed_database_iterate(feed->database, NULL, 0, backwards);
00451 }
00452
00453 if (feed->iterators) {
00454 feed->iterators->previous = iterator;
00455 iterator->next = feed->iterators;
00456 } else {
00457 iterator->next = NULL;
00458 }
00459 iterator->previous = NULL;
00460 feed->iterators = iterator;
00461
00462 microfeed_mutex_unlock(feed->mutex);
00463
00464 return iterator;
00465 }
00466
00467 void microfeed_feed_remove_subscriber(MicrofeedFeed* feed, const char* bus_name) {
00468 microfeed_mutex_lock(feed->mutex);
00469
00470 feed->subscriber_count--;
00471 feed->reference_count--;
00472 if (feed->reference_count == 0) {
00473 microfeed_feed_free(feed);
00474 } else {
00475
00476 microfeed_mutex_unlock(feed->mutex);
00477 }
00478 }
00479
00480 void microfeed_feed_update(MicrofeedFeed* feed, const char* bus_name) {
00481 MicrofeedError* error;
00482
00483 microfeed_mutex_lock(feed->mutex);
00484
00485 if (feed->updating) {
00486 if (bus_name) {
00487 microfeed_publisher_send_feed_signal(feed->publisher, bus_name, MICROFEED_SIGNAL_NAME_FEED_UPDATE_STARTED, feed->uri);
00488 }
00489 } else {
00490 feed->updating = 1;
00491 microfeed_publisher_send_feed_signal(feed->publisher, NULL, MICROFEED_SIGNAL_NAME_FEED_UPDATE_STARTED, feed->uri);
00492
00493 if (feed->callbacks.update) {
00494 if (microfeed_publisher_get_singleton_update_thread(feed->publisher)) {
00495 microfeed_mutex_unlock(feed->mutex);
00496
00497 error = feed->callbacks.update(feed, (bus_name ? 1 : 0), feed->user_data);
00498
00499 microfeed_mutex_lock(feed->mutex);
00500
00501 if (error) {
00502 microfeed_publisher_send_error_signal(feed->publisher, NULL, microfeed_error_get_name(error), feed->uri, NULL, microfeed_error_get_message(error));
00503 microfeed_error_free(error);
00504 }
00505 } else {
00506 feed->reference_count++;
00507 if (bus_name) {
00508 microfeed_publisher_queue_thread(feed->publisher, update_thread_user_initiated, feed);
00509 } else {
00510 microfeed_publisher_queue_thread(feed->publisher, update_thread_periodical, feed);
00511 }
00512 }
00513 }
00514 }
00515
00516 microfeed_mutex_unlock(feed->mutex);
00517 }
00518
00519 void microfeed_feed_republish(MicrofeedFeed* feed, const char* start_uid, const char* end_uid, unsigned int max_count, const char* bus_name) {
00520 RepublishingData* data;
00521
00522 data = microfeed_memory_allocate(RepublishingData);
00523 data->feed = feed;
00524 data->start_uid = start_uid;
00525 data->end_uid = end_uid;
00526 data->max_count = max_count;
00527 data->bus_name = bus_name;
00528
00529 microfeed_publisher_queue_thread(feed->publisher, republish_thread, data);
00530 }
00531
00532 int microfeed_feed_set_item_status(MicrofeedFeed* feed, const char* uid, MicrofeedItemStatus status_to_set) {
00533 int retval = 0;
00534 size_t uid_size;
00535 void* key;
00536 size_t key_size;
00537 void* data;
00538 size_t data_size;
00539 char status;
00540
00541 microfeed_mutex_lock(feed->mutex);
00542
00543 uid_size = strlen(uid) + 1;
00544 if (microfeed_database_get_data(feed->statuses_database, uid, uid_size, &data, &data_size)) {
00545 retval = 1;
00546 status = *((char*)data);
00547 if (!(status & (char)status_to_set)) {
00548 if (status_to_set == MICROFEED_ITEM_STATUS_MARKED && feed->callbacks.mark_item) {
00549 feed->callbacks.mark_item(feed, uid, 1, feed->user_data);
00550 }
00551 status |= (char)(status_to_set);
00552 microfeed_publisher_send_status_changed_signal(feed->publisher, NULL, feed->uri, uid, status);
00553 if (status) {
00554 microfeed_database_replace_data(feed->statuses_database, uid, uid_size, &status, sizeof(char));
00555 } else {
00556 microfeed_database_remove_data(feed->statuses_database, uid, uid_size);
00557 }
00558 }
00559 } else if (microfeed_database_index_get_data(feed->database_index, uid, uid_size, &key, &key_size, &data, &data_size)) {
00560 if (status_to_set == MICROFEED_ITEM_STATUS_MARKED && feed->callbacks.mark_item) {
00561 feed->callbacks.mark_item(feed, uid, 1, feed->user_data);
00562 }
00563 status = (char)status_to_set;
00564 microfeed_publisher_send_status_changed_signal(feed->publisher, NULL, feed->uri, uid, status);
00565 microfeed_database_replace_data(feed->statuses_database, uid, uid_size, &status, sizeof(char));
00566 }
00567
00568 microfeed_mutex_unlock(feed->mutex);
00569
00570 return retval;
00571 }
00572
00573 int microfeed_feed_unset_item_status(MicrofeedFeed* feed, const char* uid, MicrofeedItemStatus status_to_unset) {
00574 int retval = 0;
00575 size_t uid_size;
00576 void* data;
00577 size_t data_size;
00578 char status;
00579
00580 microfeed_mutex_lock(feed->mutex);
00581
00582 uid_size = strlen(uid) + 1;
00583 if (microfeed_database_get_data(feed->statuses_database, uid, uid_size, &data, &data_size)) {
00584 retval = 1;
00585 status = *((char*)data);
00586 if (status & (char)status_to_unset) {
00587 if (status_to_unset == MICROFEED_ITEM_STATUS_MARKED && feed->callbacks.mark_item) {
00588 feed->callbacks.mark_item(feed, uid, 0, feed->user_data);
00589 }
00590 status &= (char)(~status_to_unset);
00591 microfeed_publisher_send_status_changed_signal(feed->publisher, NULL, feed->uri, uid, status);
00592 if (status) {
00593 microfeed_database_replace_data(feed->statuses_database, uid, uid_size, &status, sizeof(char));
00594 } else {
00595 microfeed_database_remove_data(feed->statuses_database, uid, uid_size);
00596 }
00597 }
00598 }
00599
00600 microfeed_mutex_unlock(feed->mutex);
00601
00602 return retval;
00603 }
00604
00605 void microfeed_feed_unset_item_statuses(MicrofeedFeed* feed, const char* start_uid, const char* end_uid, MicrofeedItemStatus status_to_unset) {
00606 size_t uid_size;
00607 MicrofeedDatabaseIterator* iterator;
00608 const void* key;
00609 size_t key_size;
00610 const void* data;
00611 size_t data_size;
00612 char status;
00613
00614 microfeed_mutex_lock(feed->mutex);
00615
00616 uid_size = (start_uid ? strlen(start_uid) + 1 : 0);
00617 for (iterator = microfeed_database_iterate(feed->statuses_database, start_uid, uid_size, 0);
00618 (microfeed_database_iterator_get(iterator, &key, &key_size, &data, &data_size)) && (!end_uid || strcmp ((const char*)key, end_uid) <= 0);
00619 microfeed_database_iterator_next(iterator)) {
00620 status = *((char*)data);
00621 if (status & (char)status_to_unset) {
00622 if (status_to_unset == MICROFEED_ITEM_STATUS_MARKED && feed->callbacks.mark_item) {
00623 feed->callbacks.mark_item(feed, key, 0, feed->user_data);
00624 }
00625 status &= (char)(~status_to_unset);
00626 microfeed_publisher_send_status_changed_signal(feed->publisher, NULL, feed->uri, (const char*)key, status);
00627 if (status) {
00628 microfeed_database_replace_data(feed->statuses_database, key, key_size, &status, sizeof(char));
00629 } else {
00630 microfeed_database_remove_data(feed->statuses_database, key, key_size);
00631 }
00632 }
00633 }
00634 microfeed_database_iterator_free(iterator);
00635
00636 microfeed_mutex_unlock(feed->mutex);
00637 }
00638
00639 void microfeed_feed_send_item_data(MicrofeedFeed* feed, const char* uid, const char* bus_name) {
00640 void* key;
00641 size_t key_size;
00642 void* data;
00643 size_t data_size;
00644 SendItemDataData* sid_data;
00645 const char* existing_bus_name;
00646
00647 microfeed_mutex_lock(feed->mutex);
00648
00649 if (!feed->item_data_database) {
00650 microfeed_publisher_send_error_signal(feed->publisher, bus_name, MICROFEED_ERROR_NO_ITEM_DATA, feed->uri, uid, "Feed does not support item data");
00651 } else if (!microfeed_database_index_get_data(feed->database_index, uid, strlen(uid) + 1, &key, &key_size, &data, &data_size)) {
00652 microfeed_publisher_send_error_signal(feed->publisher, bus_name, MICROFEED_ERROR_NO_SUCH_ITEM, feed->uri, uid, "Item does not exist");
00653 } else {
00654 if (microfeed_database_get_data(feed->item_data_database, uid, strlen(uid) + 1, &data, &data_size) && data_size > sizeof(uint64_t)) {
00655 microfeed_publisher_send_item_data_signal(feed->publisher, bus_name, feed->uri, uid, data + sizeof(uint64_t), data_size - sizeof(uint64_t));
00656 free(data);
00657 } else {
00658 if (!(sid_data = microfeed_store_get(feed->send_item_data_datas, uid, SendItemDataData))) {
00659 sid_data = microfeed_memory_allocate(SendItemDataData);
00660 sid_data->uid = strdup(uid);
00661 sid_data->feed = feed;
00662 sid_data->bus_names = microfeed_store_new_sorted(microfeed_store_compare_keys_direct, microfeed_store_get_key_direct);
00663 microfeed_store_insert(feed->send_item_data_datas, sid_data);
00664
00665 microfeed_publisher_queue_thread(feed->publisher, send_item_data_thread, sid_data);
00666 }
00667 if (!(existing_bus_name = microfeed_store_get(sid_data->bus_names, bus_name, const char))) {
00668 microfeed_store_insert(sid_data->bus_names, strdup(bus_name));
00669 }
00670 }
00671 }
00672
00673 microfeed_mutex_unlock(feed->mutex);
00674 }
00675
00676 void microfeed_feed_ref_item_data(MicrofeedFeed* feed, const char* uid) {
00677 size_t uid_size;
00678 uint64_t reference_count;
00679 size_t size;
00680 MicrofeedItem* item;
00681 uint64_t timestamp;
00682 void* key;
00683 size_t key_size;
00684 const void* data;
00685 size_t data_size;
00686 char status;
00687
00688 microfeed_mutex_lock(feed->mutex);
00689
00690 if (feed->item_data_database) {
00691 uid_size = strlen(uid) + 1;
00692 size = sizeof(uint64_t);
00693 if (microfeed_database_get_data_partial(feed->item_data_database, uid, uid_size, &reference_count, &size, 0) && size == sizeof(uint64_t)) {
00694 reference_count++;
00695 microfeed_database_replace_data_partial(feed->item_data_database, uid, uid_size, &reference_count, size, 0);
00696 } else {
00697 reference_count = 1;
00698 microfeed_database_replace_data(feed->item_data_database, uid, uid_size, &reference_count, size);
00699 item = microfeed_item_new(uid, time(NULL));
00700 timestamp = (uint64_t)microfeed_item_get_timestamp(item);
00701 key_size = sizeof(uint64_t) + uid_size;
00702 key = (char*)malloc(key_size);
00703 *((uint64_t*)key) = timestamp;
00704 memcpy(key + sizeof(uint64_t), uid, uid_size);
00705 microfeed_item_marshal_properties(item, &data, &data_size);
00706 microfeed_publisher_send_item_signal(feed->publisher, NULL, MICROFEED_SIGNAL_NAME_ITEM_ADDED, feed->uri, item);
00707 microfeed_database_replace_data(feed->database, key, key_size, data, data_size);
00708 microfeed_database_replace_data(feed->statuses_database, uid, uid_size, &status, sizeof(char));
00709 }
00710 }
00711
00712 microfeed_mutex_unlock(feed->mutex);
00713 }
00714
00715 void microfeed_feed_unref_item_data(MicrofeedFeed* feed, const char* uid) {
00716 size_t uid_size;
00717 uint64_t reference_count;
00718 size_t size;
00719
00720 microfeed_mutex_lock(feed->mutex);
00721
00722 if (feed->item_data_database) {
00723 uid_size = strlen(uid) + 1;
00724 size = sizeof(uint64_t);
00725 if (microfeed_database_get_data_partial(feed->item_data_database, uid, uid_size, &reference_count, &size, 0) && size == sizeof(uint64_t)) {
00726 reference_count--;
00727 if (reference_count == 0) {
00728 microfeed_database_index_remove_data(feed->database_index, uid, uid_size);
00729 microfeed_database_remove_data(feed->statuses_database, uid, uid_size);
00730 microfeed_database_remove_data(feed->item_data_database, uid, uid_size);
00731 microfeed_publisher_send_item_uid_signal(feed->publisher, NULL, MICROFEED_SIGNAL_NAME_ITEM_REMOVED, feed->uri, uid);
00732 } else {
00733 microfeed_database_replace_data_partial(feed->item_data_database, uid, uid_size, &reference_count, size, 0);
00734 }
00735 }
00736 }
00737
00738 microfeed_mutex_unlock(feed->mutex);
00739 }
00740
00741
00742 MicrofeedFeedPermission microfeedfeed_permission_from_string(const char* string) {
00743 MicrofeedFeedPermission feed_permission = MICROFEED_FEED_PERMISSION_NONE;
00744
00745 while (*string == ':') {
00746 string++;
00747 if (!strncmp(string, "add", 3) && (string[3] == 0 || string[3] == ':')) {
00748 feed_permission |= MICROFEED_FEED_PERMISSION_ADD;
00749 string += 3;
00750 }
00751 }
00752
00753 return feed_permission;
00754 }
00755
00756
00757 char* microfeed_feed_permission_to_string(MicrofeedFeedPermission feed_permission) {
00758 char buffer[1024];
00759 char* string = buffer;
00760
00761 if (feed_permission & MICROFEED_FEED_PERMISSION_ADD) {
00762 memcpy(string, ":add", 4);
00763 string += 4;
00764 }
00765 *string = 0;
00766
00767 return strdup(buffer);
00768 }
00769
00770
00771 void microfeed_feed_iterator_free(MicrofeedFeedIterator* iterator) {
00772 if (iterator->next) {
00773 iterator->next->previous = iterator->previous;
00774 }
00775 if (iterator->previous) {
00776 iterator->previous->next = iterator->next;
00777 } else if (iterator->feed) {
00778 iterator->feed->iterators = iterator->next;
00779 }
00780
00781 iterator->feed = NULL;
00782 microfeed_database_iterator_free(iterator->database_iterator);
00783 microfeed_memory_free(iterator);
00784 }
00785
00786 MicrofeedFeed* microfeed_feed_iterator_get_feed(MicrofeedFeedIterator* iterator) {
00787
00788 return iterator->feed;
00789 }
00790
00791 MicrofeedItem* microfeed_feed_iterator_get_item(MicrofeedFeedIterator* iterator) {
00792 const void* key;
00793 size_t key_size;
00794 const void* data;
00795 size_t data_size;
00796 uint64_t timestamp;
00797 MicrofeedItem* item;
00798
00799 microfeed_mutex_lock(iterator->feed->mutex);
00800
00801 if (!microfeed_database_iterator_get(iterator->database_iterator, &key, &key_size, &data, &data_size)) {
00802 item = NULL;
00803 } else {
00804 timestamp = *((uint64_t*)key);
00805 item = microfeed_item_new(key + sizeof(uint64_t), (time_t)timestamp);
00806 microfeed_item_demarshal_properties(item, data, data_size);
00807 }
00808
00809 microfeed_mutex_unlock(iterator->feed->mutex);
00810
00811 return item;
00812 }
00813
00814 void microfeed_feed_iterator_next(MicrofeedFeedIterator* iterator) {
00815 microfeed_database_iterator_next(iterator->database_iterator);
00816 }
00817
00818 int microfeed_feed_iterator_jump_and_remove_previous_items(MicrofeedFeedIterator* iterator, const char* uid) {
00819 int retval = 0;
00820 size_t uid_size;
00821 void* start_key;
00822 size_t start_key_size;
00823 void* end_key;
00824 size_t end_key_size;
00825 void* jump_key;
00826 size_t jump_key_size;
00827 const void* key;
00828 size_t key_size;
00829 void* data;
00830 const void* const_data;
00831 size_t data_size;
00832
00833 microfeed_mutex_lock(iterator->feed->mutex);
00834
00835 uid_size = strlen(uid) + 1;
00836 if (microfeed_database_index_get_data(iterator->feed->database_index, uid, uid_size, &jump_key, &jump_key_size, &data, &data_size)) {
00837 retval = 1;
00838 free(data);
00839 if (microfeed_database_iterator_get(iterator->database_iterator, &key, &key_size, &const_data, &data_size) && (key_size - sizeof(uint64_t) != uid_size || memcmp(key + sizeof(uint64_t), uid, uid_size))) {
00840 start_key = malloc(key_size);
00841 memcpy(start_key, key, key_size);
00842 start_key_size = key_size;
00843 end_key = malloc(key_size);
00844 memcpy(end_key, key, key_size);
00845 end_key_size = key_size;
00846 while (microfeed_database_iterator_get(iterator->database_iterator, &key, &key_size, &const_data, &data_size) && (key_size != jump_key_size || memcmp(key, jump_key, jump_key_size))) {
00847 end_key = realloc(end_key, key_size);
00848 memcpy(end_key, key, key_size);
00849 end_key_size = key_size;
00850 microfeed_publisher_send_item_uid_signal(iterator->feed->publisher, NULL, MICROFEED_SIGNAL_NAME_ITEM_REMOVED, iterator->feed->uri, key + sizeof(uint64_t));
00851 microfeed_database_iterator_next(iterator->database_iterator);
00852 }
00853 free(jump_key);
00854 if (iterator->backwards) {
00855 if (iterator->timeline) {
00856 microfeed_database_remove_data_range(iterator->feed->database, end_key, end_key_size, start_key, start_key_size);
00857 } else {
00858 microfeed_database_index_remove_data_range(iterator->feed->database_index, end_key + sizeof(uint64_t), end_key_size - sizeof(uint64_t), start_key + sizeof(uint64_t), start_key_size - sizeof(uint64_t));
00859 }
00860 microfeed_database_remove_data_range(iterator->feed->statuses_database, end_key + sizeof(uint64_t), end_key_size - sizeof(uint64_t), start_key + sizeof(uint64_t), start_key_size - sizeof(uint64_t));
00861 } else {
00862 if (iterator->timeline) {
00863 microfeed_database_remove_data_range(iterator->feed->database, start_key, start_key_size, end_key, end_key_size);
00864 } else {
00865 microfeed_database_index_remove_data_range(iterator->feed->database_index, start_key + sizeof(uint64_t), start_key_size - sizeof(uint64_t), end_key + sizeof(uint64_t), end_key_size - sizeof(uint64_t));
00866 }
00867 microfeed_database_remove_data_range(iterator->feed->statuses_database, start_key + sizeof(uint64_t), start_key_size - sizeof(uint64_t), end_key + sizeof(uint64_t), end_key_size - sizeof(uint64_t));
00868 }
00869 free(start_key);
00870 free(end_key);
00871 }
00872 }
00873
00874 microfeed_mutex_unlock(iterator->feed->mutex);
00875
00876 return retval;
00877 }
00878
00879 void microfeed_feed_call_modify_item_callback(MicrofeedFeed* feed, MicrofeedItem* existing_item, MicrofeedItem* new_item) {
00880 ModifyItemData* data;
00881
00882 data = microfeed_memory_allocate(ModifyItemData);
00883 data->feed = feed;
00884 if (existing_item) {
00885 data->existing_item = microfeed_item_duplicate(existing_item);
00886 }
00887 if (new_item) {
00888 data->new_item = microfeed_item_duplicate(new_item);
00889 }
00890
00891 microfeed_mutex_lock(feed->mutex);
00892
00893
00894
00895 if (feed->callbacks.modify_item) {
00896 feed->reference_count++;
00897 microfeed_publisher_queue_thread(feed->publisher, modify_item_thread, data);
00898 } else {
00899
00900 }
00901
00902 microfeed_mutex_unlock(feed->mutex);
00903 }
00904
00905 static void index_function(void* key, const size_t key_size, void* data, const size_t data_size, void** index_key, size_t* index_key_size) {
00906 *index_key = key + sizeof(uint64_t);
00907 *index_key_size = key_size - sizeof(uint64_t);
00908 }
00909
00910 static int compare_function(const void* key1, const size_t key1_size, const void* key2, const size_t key2_size) {
00911 int retvalue = 0;
00912
00913 if (key1_size > sizeof(uint64_t) && key2_size > sizeof(int64_t)) {
00914 if (!(retvalue = *((uint64_t*)key1) - *((uint64_t*)key2))) {
00915 retvalue = strcmp(key1 + sizeof(uint64_t), key2 + sizeof(uint64_t));
00916 }
00917 }
00918
00919 return retvalue;
00920 }
00921
00922 static void* update_thread_periodical(void* data) {
00923 MicrofeedFeed* feed;
00924 MicrofeedError* error;
00925
00926 feed = (MicrofeedFeed*)data;
00927 error = feed->callbacks.update(feed, 0, feed->user_data);
00928
00929 microfeed_mutex_lock(feed->mutex);
00930
00931 if (error) {
00932 microfeed_publisher_send_error_signal(feed->publisher, NULL, microfeed_error_get_name(error), feed->uri, NULL, microfeed_error_get_message(error));
00933 microfeed_error_free(error);
00934 } else {
00935 remove_old_items(feed);
00936 }
00937 microfeed_publisher_send_feed_signal(feed->publisher, NULL, MICROFEED_SIGNAL_NAME_FEED_UPDATE_ENDED, feed->uri);
00938 feed->updating = 0;
00939 feed->reference_count--;
00940 if (feed->reference_count == 0) {
00941 microfeed_feed_free(feed);
00942 } else {
00943
00944 microfeed_mutex_unlock(feed->mutex);
00945 }
00946
00947 return NULL;
00948 }
00949
00950 static void* update_thread_user_initiated(void* data) {
00951 MicrofeedFeed* feed;
00952 MicrofeedError* error;
00953
00954 feed = (MicrofeedFeed*)data;
00955 error = feed->callbacks.update(feed, 1, feed->user_data);
00956
00957 microfeed_mutex_lock(feed->mutex);
00958
00959 if (error) {
00960 microfeed_publisher_send_error_signal(feed->publisher, NULL, microfeed_error_get_name(error), feed->uri, NULL, microfeed_error_get_message(error));
00961 microfeed_error_free(error);
00962 } else {
00963 remove_old_items(feed);
00964 }
00965 microfeed_publisher_send_feed_signal(feed->publisher, NULL, MICROFEED_SIGNAL_NAME_FEED_UPDATE_ENDED, feed->uri);
00966 feed->updating = 0;
00967 feed->reference_count--;
00968 if (feed->reference_count == 0) {
00969 microfeed_feed_free(feed);
00970 } else {
00971
00972 microfeed_mutex_unlock(feed->mutex);
00973 }
00974
00975 return NULL;
00976 }
00977
00978 static void* republish_thread(void* user_data) {
00979 RepublishingData* data;
00980 unsigned int count;
00981 MicrofeedDatabaseIterator* iterator;
00982 const void* key;
00983 size_t key_size;
00984 const void* item_data;
00985 size_t item_data_size;
00986 uint64_t timestamp;
00987 MicrofeedItem* item;
00988 void* status_data;
00989 size_t status_data_size;
00990 char status;
00991
00992
00993 data = (RepublishingData*)user_data;
00994
00995 microfeed_mutex_lock(data->feed->mutex);
00996
00997 microfeed_publisher_send_feed_signal(data->feed->publisher, data->bus_name, MICROFEED_SIGNAL_NAME_FEED_REPUBLISHING_STARTED, data->feed->uri);
00998
00999 count = 0;
01000 for (iterator = microfeed_database_iterate(data->feed->database, data->end_uid, (data->end_uid ? strlen(data->end_uid) + 1 : 0), 1);
01001 microfeed_database_iterator_get(iterator, &key, &key_size, &item_data, &item_data_size);
01002 microfeed_database_iterator_next(iterator)) {
01003 timestamp = *((uint64_t*)key);
01004 if (microfeed_database_get_data(data->feed->statuses_database, key + sizeof(uint64_t), key_size - sizeof(uint64_t), &status_data, &status_data_size)) {
01005 status = *((char*)status_data);
01006 } else {
01007 status = (char)MICROFEED_ITEM_STATUS_NONE;
01008 }
01009 item = microfeed_item_new_with_status(key + sizeof(uint64_t), (time_t)timestamp, (MicrofeedItemStatus)status);
01010 microfeed_item_demarshal_properties(item, item_data, item_data_size);
01011 microfeed_publisher_send_item_signal(data->feed->publisher, data->bus_name, MICROFEED_SIGNAL_NAME_ITEM_REPUBLISHED, data->feed->uri, item);
01012 microfeed_item_free(item);
01013 if (data->start_uid && strcmp(data->start_uid, key + sizeof(uint64_t)) <= 0) {
01014 break;
01015 }
01016 if (++count == data->max_count) {
01017 break;
01018 }
01019 }
01020 microfeed_database_iterator_free(iterator);
01021
01022 microfeed_publisher_send_feed_signal(data->feed->publisher, data->bus_name, MICROFEED_SIGNAL_NAME_FEED_REPUBLISHING_ENDED, data->feed->uri);
01023
01024 microfeed_mutex_unlock(data->feed->mutex);
01025
01026 return NULL;
01027 }
01028
01029 static void* send_item_data_thread(void* data) {
01030 SendItemDataData* sid_data;
01031 MicrofeedError* error;
01032 MicrofeedStoreIterator* iterator;
01033 void* item_data;
01034 size_t item_data_size;
01035 char* bus_name;
01036
01037 sid_data = (SendItemDataData*)data;
01038 error = sid_data->feed->callbacks.download_item_data(sid_data->feed, sid_data->uid, &item_data, &item_data_size, sid_data->feed->user_data);
01039
01040 microfeed_mutex_lock(sid_data->feed->mutex);
01041
01042 if (error) {
01043 for (iterator = microfeed_store_iterate(sid_data->bus_names, NULL);
01044 (bus_name = microfeed_store_iterator_get(iterator, char));
01045 microfeed_store_iterator_next(iterator)) {
01046 microfeed_publisher_send_error_signal(sid_data->feed->publisher, bus_name, microfeed_error_get_name(error), sid_data->feed->uri, NULL, microfeed_error_get_message(error));
01047 free(bus_name);
01048 }
01049 microfeed_store_iterator_free(iterator);
01050 microfeed_error_free(error);
01051 } else {
01052 for (iterator = microfeed_store_iterate(sid_data->bus_names, NULL);
01053 (bus_name = microfeed_store_iterator_get(iterator, char));
01054 microfeed_store_iterator_next(iterator)) {
01055 microfeed_publisher_send_item_data_signal(sid_data->feed->publisher, bus_name, sid_data->feed->uri, sid_data->uid, item_data, item_data_size);
01056 free(bus_name);
01057 }
01058 microfeed_store_iterator_free(iterator);
01059
01060 item_data = realloc(item_data, item_data_size + sizeof(uint64_t));
01061 memmove(item_data + sizeof(uint64_t), item_data, item_data_size);
01062 item_data_size += sizeof(uint64_t);
01063 *((uint64_t*)item_data) = 1;
01064 microfeed_database_replace_data(sid_data->feed->item_data_database, sid_data->uid, strlen(sid_data->uid) + 1, item_data, item_data_size);
01065 free(item_data);
01066 }
01067 microfeed_store_remove(sid_data->feed->send_item_data_datas, sid_data);
01068
01069 microfeed_mutex_unlock(sid_data->feed->mutex);
01070
01071 microfeed_store_free(sid_data->bus_names);
01072 free(sid_data->uid);
01073 microfeed_memory_free(sid_data);
01074
01075 return NULL;
01076 }
01077
01078 static void* modify_item_thread(void* user_data) {
01079 ModifyItemData* data;
01080 MicrofeedError* error;
01081
01082 data = (ModifyItemData*)user_data;
01083 error = data->feed->callbacks.modify_item(data->feed, data->existing_item, data->new_item, data->feed->user_data);
01084
01085 microfeed_mutex_lock(data->feed->mutex);
01086
01087 if (error) {
01088 microfeed_publisher_send_error_signal(data->feed->publisher, NULL, microfeed_error_get_name(error), data->feed->uri, microfeed_item_get_uid((data->existing_item ? data->existing_item : data->new_item)), microfeed_error_get_message(error));
01089 microfeed_error_free(error);
01090 }
01091
01092 microfeed_item_free(data->existing_item);
01093 microfeed_item_free(data->new_item);
01094
01095 microfeed_mutex_unlock(data->feed->mutex);
01096
01097 return NULL;
01098 }
01099
01100 static void remove_old_items(MicrofeedFeed* feed) {
01101 time_t timestamp;
01102 int keep_old_items;
01103
01104 keep_old_items = atoi(microfeed_publisher_get_setting_value(feed->publisher, "keep_old_items", "0"));
01105 timestamp = time(NULL) + 60 * 60 * keep_old_items;
01106
01107
01108
01109 }
01110
01111 static const char* send_item_data_data_get_uid(SendItemDataData* send_item_data_data) {
01112
01113 return send_item_data_data->uid;
01114 }
01115
01116