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