void subscription_free (subscriptionPtr subscription) { if (!subscription) return; g_free (subscription->updateError); g_free (subscription->filterError); g_free (subscription->httpError); g_free (subscription->source); g_free (subscription->origSource); g_free (subscription->filtercmd); update_job_cancel_by_owner (subscription); update_options_free (subscription->updateOptions); update_state_free (subscription->updateState); metadata_list_free (subscription->metadata); g_free (subscription); }
/** * itemset_generic_merge_check: (skip) * @items: existing items * @newItem: new item to merge * @maxChecks: maximum number of item checks * @allowUpdates: TRUE if item content update is to be * allowed for existing items * @allowStateChanges: TRUE if item state shall be * overwritten by source * * Generic merge logic suitable for feeds * * Returns: TRUE if merging instead of updating is necessary) */ static gboolean itemset_generic_merge_check (GList *items, itemPtr newItem, gint maxChecks, gboolean allowUpdates, gboolean allowStateChanges) { GList *oldItemIdIter = items; itemPtr oldItem = NULL; gboolean found, equal = FALSE; guint reason = 0; /* determine if we should add it... */ debug3 (DEBUG_CACHE, "check new item for merging: \"%s\", %i, %i", item_get_title (newItem), allowUpdates, allowStateChanges); /* compare to every existing item in this feed */ found = FALSE; while (oldItemIdIter) { oldItem = (itemPtr)(oldItemIdIter->data); /* try to compare the two items */ /* trivial case: one item has id the other doesn't -> they can't be equal */ if (((item_get_id (oldItem) == NULL) && (item_get_id (newItem) != NULL)) || ((item_get_id (oldItem) != NULL) && (item_get_id (newItem) == NULL))) { /* cannot be equal (different ids) so compare to next old item */ oldItemIdIter = g_list_next (oldItemIdIter); continue; } /* just for the case there are no ids: compare titles and HTML descriptions */ equal = TRUE; if (((item_get_title (oldItem) != NULL) && (item_get_title (newItem) != NULL)) && (0 != strcmp (item_get_title (oldItem), item_get_title (newItem)))) { equal = FALSE; reason |= 1; } if (((item_get_description (oldItem) != NULL) && (item_get_description (newItem) != NULL)) && (0 != strcmp (item_get_description(oldItem), item_get_description (newItem)))) { equal = FALSE; reason |= 2; } /* best case: they both have ids (position important: id check is useless without knowing if the items are different!) */ if (item_get_id (oldItem)) { if (0 == strcmp (item_get_id (oldItem), item_get_id (newItem))) { found = TRUE; if (allowStateChanges) { /* found corresponding item, check if they are REALLY equal (eg, read status may have changed) */ if(oldItem->readStatus != newItem->readStatus) { equal = FALSE; reason |= 4; } if(oldItem->flagStatus != newItem->flagStatus) { equal = FALSE; reason |= 8; } } break; } else { /* different ids, but the content might be still equal (e.g. empty) so we need to explicitly unset the equal flag !!! */ equal = FALSE; reason |= 16; } } if (equal) { found = TRUE; break; } oldItemIdIter = g_list_next (oldItemIdIter); } if (!found) { debug0 (DEBUG_CACHE, "-> item is to be added"); } else { /* if the item was found but has other contents -> update contents */ if (!equal) { if (allowUpdates) { /* no item_set_new_status() - we don't treat changed items as new items! */ item_set_title (oldItem, item_get_title (newItem)); /* don't use item_set_description as it does some unwanted length handling and we want to enforce the new description */ g_free (oldItem->description); oldItem->description = newItem->description; newItem->description = NULL; oldItem->time = newItem->time; oldItem->updateStatus = TRUE; // FIXME: this does not remove metadata from DB metadata_list_free (oldItem->metadata); oldItem->metadata = newItem->metadata; newItem->metadata = NULL; /* Only update item state for feed sources where it is necessary which means online accounts we sync against, but not normal online feeds where items have no read status. */ if (allowStateChanges) { /* To avoid notification spam from external sources: never set read items to unread again! */ if ((!oldItem->readStatus) && (newItem->readStatus)) oldItem->readStatus = newItem->readStatus; oldItem->flagStatus = newItem->flagStatus; } db_item_update (oldItem); debug1 (DEBUG_CACHE, "-> item already existing and was updated, reason %x", reason); } else { debug0 (DEBUG_CACHE, "-> item updates not merged because of parser errors"); } } else { debug0 (DEBUG_CACHE, "-> item already exists"); } } return !found; }
/** * General feed source parsing function. Parses the passed feed source * and tries to determine the source type. * * @param ctxt feed parsing context * * @returns FALSE if auto discovery is indicated, * TRUE if feed type was recognized and parsing was successful */ gboolean feed_parse (feedParserCtxtPtr ctxt) { xmlNodePtr cur; gboolean success = FALSE; debug_enter("feed_parse"); g_assert(NULL == ctxt->items); ctxt->failed = TRUE; /* reset on success ... */ if(ctxt->feed->parseErrors) g_string_truncate(ctxt->feed->parseErrors, 0); else ctxt->feed->parseErrors = g_string_new(NULL); /* try to parse buffer with XML and to create a DOM tree */ do { if(NULL == xml_parse_feed (ctxt)) { g_string_append_printf (ctxt->feed->parseErrors, _("XML error while reading feed! Feed \"%s\" could not be loaded!"), subscription_get_source (ctxt->subscription)); break; } if(NULL == (cur = xmlDocGetRootElement(ctxt->doc))) { g_string_append(ctxt->feed->parseErrors, _("Empty document!")); break; } while(cur && xmlIsBlankNode(cur)) { cur = cur->next; } if(!cur) break; if(!cur->name) { g_string_append(ctxt->feed->parseErrors, _("Invalid XML!")); break; } /* determine the syndication format and start parser */ GSList *handlerIter = feed_parsers_get_list (); while(handlerIter) { feedHandlerPtr handler = (feedHandlerPtr)(handlerIter->data); if(handler && handler->checkFormat && (*(handler->checkFormat))(ctxt->doc, cur)) { /* free old temp. parsing data, don't free right after parsing because it can be used until the last feed request is finished, move me to the place where the last request in list otherRequests is finished :-) */ g_hash_table_destroy(ctxt->tmpdata); ctxt->tmpdata = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free); /* we always drop old metadata */ metadata_list_free(ctxt->subscription->metadata); ctxt->subscription->metadata = NULL; ctxt->failed = FALSE; ctxt->feed->fhp = handler; (*(handler->feedParser))(ctxt, cur); /* parse it */ break; } handlerIter = handlerIter->next; } } while(0); /* if the given URI isn't valid we need to start auto discovery */ if(ctxt->failed) feed_parser_auto_discover (ctxt); if(ctxt->failed) { /* Autodiscovery failed */ /* test if we have a HTML page */ if((strstr(ctxt->data, "<html>") || strstr(ctxt->data, "<HTML>") || strstr(ctxt->data, "<html ") || strstr(ctxt->data, "<HTML "))) { debug0(DEBUG_UPDATE, "HTML document detected!"); g_string_append(ctxt->feed->parseErrors, _("Source points to HTML document.")); } else { debug0(DEBUG_UPDATE, "neither a known feed type nor a HTML document!"); g_string_append(ctxt->feed->parseErrors, _("Could not determine the feed type.")); } } else { debug1(DEBUG_UPDATE, "discovered feed format: %s", feed_type_fhp_to_str(ctxt->feed->fhp)); success = TRUE; } if(ctxt->doc) { xmlFreeDoc(ctxt->doc); ctxt->doc = NULL; } debug_exit("feed_parse"); return success; }