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