From 420fdcdacd7079f897c9c3176352acbf3f00f47d Mon Sep 17 00:00:00 2001 From: Aldo Gunsing Date: Tue, 13 Nov 2018 16:12:54 +0100 Subject: [PATCH] Add DecSync plugin --- .gitmodules | 3 + meson_options.txt | 4 +- plugins/backend/decsync/Rfc822.vala | 144 ++ plugins/backend/decsync/decsync.gresource.xml | 7 + plugins/backend/decsync/decsync.plugin | 9 + plugins/backend/decsync/decsyncInterface.vala | 738 +++++++ plugins/backend/decsync/decsyncListeners.vala | 285 +++ plugins/backend/decsync/decsyncUtils.vala | 120 ++ .../places/feed-service-decsync-symbolic.svg | 132 ++ .../64x64/places/feed-service-decsync.svg | 145 ++ plugins/backend/decsync/libdecsync | 1 + plugins/backend/decsync/libmrss/meson.build | 16 + plugins/backend/decsync/libmrss/mrss.h | 897 +++++++++ .../backend/decsync/libmrss/mrss_download.c | 51 + plugins/backend/decsync/libmrss/mrss_edit.c | 1739 +++++++++++++++++ plugins/backend/decsync/libmrss/mrss_free.c | 464 +++++ .../backend/decsync/libmrss/mrss_generic.c | 146 ++ .../backend/decsync/libmrss/mrss_internal.h | 35 + .../backend/decsync/libmrss/mrss_options.c | 76 + plugins/backend/decsync/libmrss/mrss_parser.c | 1376 +++++++++++++ plugins/backend/decsync/libmrss/mrss_search.c | 122 ++ plugins/backend/decsync/libmrss/mrss_write.c | 1228 ++++++++++++ plugins/backend/decsync/libnxml/meson.build | 19 + plugins/backend/decsync/libnxml/nxml.h | 947 +++++++++ .../backend/decsync/libnxml/nxml_download.c | 139 ++ plugins/backend/decsync/libnxml/nxml_easy.c | 392 ++++ plugins/backend/decsync/libnxml/nxml_edit.c | 184 ++ plugins/backend/decsync/libnxml/nxml_error.c | 63 + plugins/backend/decsync/libnxml/nxml_free.c | 233 +++ plugins/backend/decsync/libnxml/nxml_init.c | 400 ++++ .../backend/decsync/libnxml/nxml_internal.h | 84 + .../backend/decsync/libnxml/nxml_namespace.c | 354 ++++ plugins/backend/decsync/libnxml/nxml_parser.c | 1482 ++++++++++++++ plugins/backend/decsync/libnxml/nxml_string.c | 116 ++ plugins/backend/decsync/libnxml/nxml_tools.c | 122 ++ plugins/backend/decsync/libnxml/nxml_utf.c | 515 +++++ plugins/backend/decsync/libnxml/nxml_write.c | 410 ++++ plugins/backend/decsync/meson.build | 68 + .../org.gnome.feedreader.decsync.gschema.xml | 11 + plugins/backend/decsync/rss-glib/meson.build | 12 + .../decsync/rss-glib/rss-document-private.h | 58 + .../backend/decsync/rss-glib/rss-document.c | 1085 ++++++++++ .../backend/decsync/rss-glib/rss-document.h | 105 + .../decsync/rss-glib/rss-glib-1.0.vapi | 126 ++ plugins/backend/decsync/rss-glib/rss-glib.h | 9 + .../decsync/rss-glib/rss-item-private.h | 48 + plugins/backend/decsync/rss-glib/rss-item.c | 819 ++++++++ plugins/backend/decsync/rss-glib/rss-item.h | 97 + .../backend/decsync/rss-glib/rss-marshal.c | 54 + .../backend/decsync/rss-glib/rss-marshal.h | 15 + .../decsync/rss-glib/rss-parser-private.h | 45 + plugins/backend/decsync/rss-glib/rss-parser.c | 338 ++++ plugins/backend/decsync/rss-glib/rss-parser.h | 102 + .../backend/decsync/rss-glib/rss-version.h | 96 + plugins/backend/meson.build | 1 + 55 files changed, 16285 insertions(+), 2 deletions(-) create mode 100644 plugins/backend/decsync/Rfc822.vala create mode 100644 plugins/backend/decsync/decsync.gresource.xml create mode 100644 plugins/backend/decsync/decsync.plugin create mode 100644 plugins/backend/decsync/decsyncInterface.vala create mode 100644 plugins/backend/decsync/decsyncListeners.vala create mode 100644 plugins/backend/decsync/decsyncUtils.vala create mode 100644 plugins/backend/decsync/icons/64x64/places/feed-service-decsync-symbolic.svg create mode 100644 plugins/backend/decsync/icons/64x64/places/feed-service-decsync.svg create mode 160000 plugins/backend/decsync/libdecsync create mode 100644 plugins/backend/decsync/libmrss/meson.build create mode 100644 plugins/backend/decsync/libmrss/mrss.h create mode 100644 plugins/backend/decsync/libmrss/mrss_download.c create mode 100644 plugins/backend/decsync/libmrss/mrss_edit.c create mode 100644 plugins/backend/decsync/libmrss/mrss_free.c create mode 100644 plugins/backend/decsync/libmrss/mrss_generic.c create mode 100644 plugins/backend/decsync/libmrss/mrss_internal.h create mode 100644 plugins/backend/decsync/libmrss/mrss_options.c create mode 100644 plugins/backend/decsync/libmrss/mrss_parser.c create mode 100644 plugins/backend/decsync/libmrss/mrss_search.c create mode 100644 plugins/backend/decsync/libmrss/mrss_write.c create mode 100644 plugins/backend/decsync/libnxml/meson.build create mode 100644 plugins/backend/decsync/libnxml/nxml.h create mode 100644 plugins/backend/decsync/libnxml/nxml_download.c create mode 100644 plugins/backend/decsync/libnxml/nxml_easy.c create mode 100644 plugins/backend/decsync/libnxml/nxml_edit.c create mode 100644 plugins/backend/decsync/libnxml/nxml_error.c create mode 100644 plugins/backend/decsync/libnxml/nxml_free.c create mode 100644 plugins/backend/decsync/libnxml/nxml_init.c create mode 100644 plugins/backend/decsync/libnxml/nxml_internal.h create mode 100644 plugins/backend/decsync/libnxml/nxml_namespace.c create mode 100644 plugins/backend/decsync/libnxml/nxml_parser.c create mode 100644 plugins/backend/decsync/libnxml/nxml_string.c create mode 100644 plugins/backend/decsync/libnxml/nxml_tools.c create mode 100644 plugins/backend/decsync/libnxml/nxml_utf.c create mode 100644 plugins/backend/decsync/libnxml/nxml_write.c create mode 100644 plugins/backend/decsync/meson.build create mode 100644 plugins/backend/decsync/org.gnome.feedreader.decsync.gschema.xml create mode 100644 plugins/backend/decsync/rss-glib/meson.build create mode 100644 plugins/backend/decsync/rss-glib/rss-document-private.h create mode 100644 plugins/backend/decsync/rss-glib/rss-document.c create mode 100644 plugins/backend/decsync/rss-glib/rss-document.h create mode 100644 plugins/backend/decsync/rss-glib/rss-glib-1.0.vapi create mode 100644 plugins/backend/decsync/rss-glib/rss-glib.h create mode 100644 plugins/backend/decsync/rss-glib/rss-item-private.h create mode 100644 plugins/backend/decsync/rss-glib/rss-item.c create mode 100644 plugins/backend/decsync/rss-glib/rss-item.h create mode 100644 plugins/backend/decsync/rss-glib/rss-marshal.c create mode 100644 plugins/backend/decsync/rss-glib/rss-marshal.h create mode 100644 plugins/backend/decsync/rss-glib/rss-parser-private.h create mode 100644 plugins/backend/decsync/rss-glib/rss-parser.c create mode 100644 plugins/backend/decsync/rss-glib/rss-parser.h create mode 100644 plugins/backend/decsync/rss-glib/rss-version.h diff --git a/.gitmodules b/.gitmodules index f6797cdd9..c4366c27e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -2,3 +2,6 @@ path = data/GrabberConfig url = https://github.com/fivefilters/ftr-site-config branch = master +[submodule "libdecsync"] + path = plugins/backend/decsync/libdecsync + url = https://github.com/39aldo39/libdecsync-vala diff --git a/meson_options.txt b/meson_options.txt index d8d893204..773e9f66a 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -6,7 +6,7 @@ option('share-plugins', ) option('backend-plugins', type: 'array', - choices: ['bazqux', 'feedbin', 'feedhq', 'feedly', 'fresh', 'inoreader', 'local', 'oldreader', 'owncloud', 'ttrss'], - value : ['bazqux', 'feedbin', 'feedhq', 'feedly', 'fresh', 'inoreader', 'local', 'oldreader', 'owncloud', 'ttrss'], + choices: ['bazqux', 'decsync', 'feedbin', 'feedhq', 'feedly', 'fresh', 'inoreader', 'local', 'oldreader', 'owncloud', 'ttrss'], + value : ['bazqux', 'decsync', 'feedbin', 'feedhq', 'feedly', 'fresh', 'inoreader', 'local', 'oldreader', 'owncloud', 'ttrss'], description: 'List of backend plugins to install' ) diff --git a/plugins/backend/decsync/Rfc822.vala b/plugins/backend/decsync/Rfc822.vala new file mode 100644 index 000000000..368c69872 --- /dev/null +++ b/plugins/backend/decsync/Rfc822.vala @@ -0,0 +1,144 @@ +namespace FeedReader.Rfc822 { + + /** + * Parse a date string in RFC 822 format + * Note that we don't use Time.strptime because it uses the current locale + * to parse month names, but RFC 822 specifically requires months to be + * written in English. + * See: https://www.w3.org/Protocols/rfc822/#z28 + * And: https://groups.yahoo.com/neo/groups/rss-public/conversations/topics/536 + * */ + public static DateTime? parseDate(string? str) + { + if (str == null) + return null; + + Regex re; + try { + re = new Regex(""" + # We don't care about the day of the week + \s*(?:(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun),\s*)? + (?\d{1,2})\s+ + (?Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s+ + # The standard specifies 2-digit years but 4 digit years are + # recommended now. + # This pattern will also accept 3-digit years, so we'll have to + # check for that separately + (?\d{2,4})\s+ + (?\d{2}) + :(?\d{2}) + (?::(?\d{2}))?\s+ + (?UT|GMT|EST|EDT|MST|MDT|PST|PDT|[A-Z]|(?:[+-]\d{4})) + """, + RegexCompileFlags.CASELESS | RegexCompileFlags.EXTENDED, + RegexMatchFlags.ANCHORED); + } catch (RegexError e) { + stderr.printf("RFC822 regex failed to parse: %s\n", e.message); + assert(false); + return null; + } + + MatchInfo info; + if (!re.match(str, 0, out info)) + return null; + + var dayStr = info.fetch_named("day"); + var day = int.parse(dayStr); + + var monthStr = info.fetch_named("month").ascii_down(); + int month; + switch(monthStr) { + case "jan": + month = 1; + break; + case "feb": + month = 2; + break; + case "mar": + month = 3; + break; + case "apr": + month = 4; + break; + case "may": + month = 5; + break; + case "jun": + month = 6; + break; + case "jul": + month = 7; + break; + case "aug": + month = 8; + break; + case "sep": + month = 9; + break; + case "oct": + month = 10; + break; + case "nov": + month = 11; + break; + case "dec": + month = 12; + break; + default: + // The regex should make this impossible + assert(false); + return null; + } + + var yearStr = info.fetch_named("year"); + var year = int.parse(yearStr); + // Two-digit years from 00 to 49 should be interpreted as 2000 to 2049 + if (year >= 0 && year <= 49) + year += 2000; + // Two-digit years from 50 to 99 should be interpreted as 1950 to 1999 + else if (year >= 50 && year < 100) + year += 1900; + var hourStr = info.fetch_named("hour"); + var hour = int.parse(hourStr); + var minuteStr = info.fetch_named("minute"); + var minute = int.parse(minuteStr); + var secondStr = info.fetch_named("second"); + var second = secondStr == null || secondStr == "" ? 0 : int.parse(secondStr); + + var zoneStr = info.fetch_named("zone"); + TimeZone zone; + switch(zoneStr.ascii_up()) { + // Note sure if new TimeZone(zoneStr) would always work for these, + // so specifically handle the cases the spec requires + case "EDT": + zone = new TimeZone("-04"); + break; + case "CDT": + case "EST": + zone = new TimeZone("-05"); + break; + case "CST": + case "MDT": + zone = new TimeZone("-06"); + break; + case "MST": + case "PDT": + zone = new TimeZone("-07"); + break; + case "PST": + zone = new TimeZone("-08"); + break; + + case "GMT": + case "UT": + case "Z": + zone = new TimeZone.utc(); + break; + default: + zone = new TimeZone(zoneStr); + break; + } + + return new DateTime(zone, year, month, day, hour, minute, second); + } +} diff --git a/plugins/backend/decsync/decsync.gresource.xml b/plugins/backend/decsync/decsync.gresource.xml new file mode 100644 index 000000000..a9f96b29e --- /dev/null +++ b/plugins/backend/decsync/decsync.gresource.xml @@ -0,0 +1,7 @@ + + + + icons/64x64/places/feed-service-decsync.svg + icons/64x64/places/feed-service-decsync-symbolic.svg + + diff --git a/plugins/backend/decsync/decsync.plugin b/plugins/backend/decsync/decsync.plugin new file mode 100644 index 000000000..d6aeda44c --- /dev/null +++ b/plugins/backend/decsync/decsync.plugin @@ -0,0 +1,9 @@ +[Plugin] +Module=decsync +Loader=C +Name=DecSync Backend +Version=0.1 +Description=Adds a DecSync back-end to FeedReader. No RSS-service required. +Authors=Jan Lukas Gernert , Aldo Gunsing +Copyright=Copyright © 2015-16 Jan Lukas Gernert, Copyright © 2018 Aldo Gunsing +Website=https://github.com/39aldo39/DecSync diff --git a/plugins/backend/decsync/decsyncInterface.vala b/plugins/backend/decsync/decsyncInterface.vala new file mode 100644 index 000000000..84599d168 --- /dev/null +++ b/plugins/backend/decsync/decsyncInterface.vala @@ -0,0 +1,738 @@ +// This file is part of FeedReader. +// +// FeedReader is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// FeedReader is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with FeedReader. If not, see . + +public class FeedReader.decsyncInterface : Peas.ExtensionBase, FeedServerInterface { + + internal DecsyncUtils m_utils; + private Soup.Session m_session; + internal Decsync m_sync; + private string m_loginDir; + private Gtk.Button loginButton; + private Gtk.Spinner waitingSpinner; + private Gtk.Stack loginStack; + internal DataBaseReadOnly m_db; + internal DataBase m_db_write; + + public void init(GLib.SettingsBackend? settings_backend, Secret.Collection secrets, DataBaseReadOnly db, DataBase db_write) + { + m_db = db; + m_db_write = db_write; + m_utils = new DecsyncUtils(settings_backend); + m_session = new Soup.Session(); + m_session.user_agent = Constants.USER_AGENT; + m_session.timeout = 5; + } + + private bool initDecsync() + { + var decsyncDir = m_utils.getDecsyncDir(); + if (decsyncDir == "") + { + return false; + } + var dir = getDecsyncSubdir(decsyncDir, "rss"); + var ownAppId = getAppId("FeedReader"); + var listeners = new Gee.ArrayList(); + listeners.add(new DecsyncListeners.ReadMarkListener(true, this)); + listeners.add(new DecsyncListeners.ReadMarkListener(false, this)); + listeners.add(new DecsyncListeners.SubscriptionsListener(this)); + listeners.add(new DecsyncListeners.FeedNamesListener(this)); + listeners.add(new DecsyncListeners.CategoriesListener(this)); + listeners.add(new DecsyncListeners.CategoryNamesListener(this)); + listeners.add(new DecsyncListeners.CategoryParentsListener(this)); + m_sync = new Decsync(dir, ownAppId, listeners); + m_sync.syncComplete.connect((extra) => { + FeedReaderBackend.get_default().updateBadge(); + refreshFeedListCounter(); + newFeedList(); + updateArticleList(); + }); + m_sync.initMonitor(new Unit()); + return true; + } + + public string getWebsite() + { + return "https://github.com/39aldo39/DecSync"; + } + + public BackendFlags getFlags() + { + return (BackendFlags.LOCAL | BackendFlags.FREE_SOFTWARE | BackendFlags.FREE); + } + + public string getID() + { + return "decsync"; + } + + public string iconName() + { + return "feed-service-decsync"; + } + + public string serviceName() + { + return "DecSync"; + } + + public bool needWebLogin() + { + return false; + } + + public Gtk.Box? getWidget() + { + var doneLabel = new Gtk.Label(_("Done")); + var waitingLabel = new Gtk.Label(_("Adding Feeds")); + waitingSpinner = new Gtk.Spinner(); + var waitingBox = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 5); + waitingBox.pack_start(waitingSpinner, false, false, 0); + waitingBox.pack_start(waitingLabel, true, false, 0); + loginStack = new Gtk.Stack(); + loginStack.add_named(doneLabel, "label"); + loginStack.add_named(waitingBox, "waiting"); + var dirLabel = new Gtk.Label(_("DecSync directory:")); + dirLabel.set_alignment(1.0f, 0.5f); + dirLabel.set_hexpand(true); + m_loginDir = m_utils.getDecsyncDir(); + var buttonLabel = m_loginDir; + if (buttonLabel == "") + { + buttonLabel = _("Select..."); + } + var dirButton = new Gtk.Button.with_label(buttonLabel); + dirButton.clicked.connect(() => { + var chooser = new Gtk.FileChooserDialog("Select Directory", + null, + Gtk.FileChooserAction.SELECT_FOLDER, + _("_Cancel"), + Gtk.ResponseType.CANCEL, + _("_Select"), + Gtk.ResponseType.ACCEPT); + chooser.set_show_hidden(true); + chooser.set_current_folder(m_utils.getDecsyncDir()); + if (chooser.run() == Gtk.ResponseType.ACCEPT) + { + m_loginDir = chooser.get_filename(); + dirButton.set_label(m_loginDir); + } + chooser.close(); + }); + + var grid = new Gtk.Grid(); + grid.set_column_spacing(10); + grid.set_row_spacing(10); + grid.set_valign(Gtk.Align.CENTER); + grid.set_halign(Gtk.Align.CENTER); + + grid.attach(dirLabel, 0, 0, 1, 1); + grid.attach(dirButton, 1, 0, 1, 1); + + //--------------------------------------------------------------------- + + var logo = new Gtk.Image.from_icon_name("feed-service-decsync", Gtk.IconSize.MENU); + + var loginLabel = new Gtk.Label(_("Please select your DecSync directory and enjoy using FeedReader")); + loginLabel.get_style_context().add_class("h2"); + loginLabel.set_justify(Gtk.Justification.CENTER); + loginLabel.set_lines(3); + + loginButton = new Gtk.Button(); + loginButton.add(loginStack); + loginButton.halign = Gtk.Align.END; + loginButton.set_size_request(80, 30); + loginButton.get_style_context().add_class(Gtk.STYLE_CLASS_SUGGESTED_ACTION); + loginButton.clicked.connect(() => { tryLogin(); }); + + var box = new Gtk.Box(Gtk.Orientation.VERTICAL, 10); + box.valign = Gtk.Align.CENTER; + box.halign = Gtk.Align.CENTER; + box.pack_start(loginLabel, false, false, 10); + box.pack_start(logo, false, false, 10); + box.pack_start(grid, true, true, 10); + box.pack_end(loginButton, false, false, 20); + + return box; + } + + public void showHtAccess() + { + return; + } + + public void writeData() + { + m_utils.setDecsyncDir(m_loginDir); + } + + public async void postLoginAction() + { + loginButton.set_sensitive(false); + waitingSpinner.start(); + loginButton.get_style_context().remove_class(Gtk.STYLE_CLASS_SUGGESTED_ACTION); + loginStack.set_visible_child_name("waiting"); + SourceFunc callback = postLoginAction.callback; + new GLib.Thread(null, () => { + m_sync.initStoredEntries(); + m_sync.executeStoredEntries({"feeds", "subscriptions"}, new Unit()); + Idle.add((owned) callback); + return null; + }); + yield; + } + + public string buildLoginURL() + { + return ""; + } + + public bool extractCode(string redirectURL) + { + return false; + } + + public bool supportTags() + { + return false; + } + + public bool doInitSync() + { + return false; + } + + public string symbolicIcon() + { + return "feed-service-decsync-symbolic"; + } + + public string accountName() + { + return "DecSync"; + } + + public string getServerURL() + { + return "http://localhost/"; + } + + public string uncategorizedID() + { + return "0"; + } + + public bool hideCategoryWhenEmpty(string catID) + { + return false; + } + + public bool supportCategories() + { + return true; + } + + public bool supportFeedManipulation() + { + return true; + } + + public bool supportMultiLevelCategories() + { + return true; + } + + public bool supportMultiCategoriesPerFeed() + { + return false; + } + + public bool syncFeedsAndCategories() + { + return false; + } + + public bool tagIDaffectedByNameChange() + { + return false; + } + + public void resetAccount() + { + return; + } + + public bool useMaxArticles() + { + return true; + } + + public LoginResponse login() + { + if (initDecsync()) + { + return LoginResponse.SUCCESS; + } + else + { + return LoginResponse.ALL_EMPTY; + } + } + + public bool logout() + { + return true; + } + + public bool serverAvailable() + { + return Utils.ping("https://duckduckgo.com/"); + } + + public void setArticleIsRead(string articleIDs, ArticleStatus readStatus) + { + var read = readStatus == ArticleStatus.READ; + Logger.debug("Mark " + articleIDs + " as " + (read ? "read" : "unread")); + var entries = new Gee.ArrayList(); + foreach (var articleID in articleIDs.split(",")) + { + Article? article = m_db.read_article(articleID); + if (article != null) + { + var path = articleToPath(article, "read"); + var key = stringToNode(article.getArticleID()); + entries.add(new Decsync.EntryWithPath.now(path, key, boolToNode(read))); + } + } + m_sync.setEntries(entries); + } + + public void setArticleIsMarked(string articleID, ArticleStatus markedStatus) + { + var marked = markedStatus == ArticleStatus.MARKED; + Logger.debug("Mark " + articleID + " as " + (marked ? "marked" : "unmarked")); + Article? article = m_db.read_article(articleID); + if (article != null) + { + var path = articleToPath(article, "marked"); + var key = stringToNode(article.getArticleID()); + m_sync.setEntry(path, key, boolToNode(marked)); + } + } + + public bool alwaysSetReadByID() + { + return true; + } + + public void setFeedRead(string feedID) + { + return; + } + + public void setCategoryRead(string catID) + { + return; + } + + public void markAllItemsRead() + { + return; + } + + public void tagArticle(string articleID, string tagID) + { + return; + } + + public void removeArticleTag(string articleID, string tagID) + { + return; + } + + public string createTag(string caption) + { + string tagID = "1"; + + if(!m_db.isTableEmpty("tags")) + tagID = (int.parse(m_db.getMaxID("tags", "tagID")) + 1).to_string(); + + Logger.info("createTag: ID = " + tagID); + return tagID; + } + + public void deleteTag(string tagID) + { + return; + } + + public void renameTag(string tagID, string title) + { + return; + } + + public bool addFeed(string feedURL, string? catID, string? newCatName, out string feedID, out string errmsg) + { + return addFeedWithDecsync(feedURL, catID, newCatName, out feedID, out errmsg); + } + + public bool addFeedWithDecsync(string feedURL, string? catID, string? newCatName, out string feedID, out string errmsg, bool updateDecsync = true) + { + var catIDs = new Gee.ArrayList(); + if(catID == null && newCatName != null) + { + string cID = createCategory(newCatName, null); + var cat = new Category(cID, newCatName, 0, 99, CategoryID.MASTER.to_string(), 1); + var list = new Gee.ArrayList(); + list.add(cat); + m_db_write.write_categories(list); + catIDs.add(cID); + } + else if(catID != null && newCatName == null) + { + catIDs.add(catID); + } + else + { + catIDs.add(uncategorizedID()); + } + + feedID = feedURL; + + Logger.info(@"addFeed: ID = $feedID"); + Feed? Feed = m_utils.downloadFeed(m_session, feedURL, feedID, catIDs, out errmsg); + + if(Feed != null) + { + if(!m_db.feed_exists(Feed.getURL())) { + var list = new Gee.ArrayList(); + list.add(Feed); + m_db_write.write_feeds(list); + + if (updateDecsync) + { + m_sync.setEntry({"feeds", "subscriptions"}, stringToNode(feedID), boolToNode(true)); + renameFeed(feedID, Feed.getTitle()); + moveFeed(feedID, Feed.getCatString(), null); + } + + m_sync.executeStoredEntries({"feeds", "names"}, new Unit(), + stringEquals(feedID) + ); + m_sync.executeStoredEntries({"feeds", "categories"}, new Unit(), + stringEquals(feedID) + ); + return true; + } + } + + return false; + } + + public void addFeeds(Gee.List feeds) + { + string feedID, errmsg; + foreach(Feed feed in feeds) + { + var catString = feed.getCatString(); + addFeed(feed.getXmlUrl(), catString != "" ? catString : null, null, out feedID, out errmsg); + } + } + + public void removeFeed(string feedID) + { + m_sync.setEntry({"feeds", "subscriptions"}, stringToNode(feedID), boolToNode(false)); + } + + public void renameFeed(string feedID, string title) + { + m_sync.setEntry({"feeds", "names"}, stringToNode(feedID), stringToNode(title)); + } + + public void moveFeed(string feedID, string newCatID, string? currentCatID) + { + string? value = newCatID == uncategorizedID() ? null : newCatID; + m_sync.setEntry({"feeds", "categories"}, stringToNode(feedID), stringToNode(value)); + } + + public string createCategory(string title, string? parentID) + { + string? catID = m_db.getCategoryID(title); + while (catID == null || m_db.category_exists(catID)) + { + catID = "catID%05d".printf(Random.int_range(0, 100000)); + } + renameCategory(catID, title); + moveCategory(catID, parentID ?? CategoryID.MASTER.to_string()); + Logger.info("createCategory: ID = " + catID); + return catID; + } + + public void renameCategory(string catID, string title) + { + m_sync.setEntry({"categories", "names"}, stringToNode(catID), stringToNode(title)); + } + + public void moveCategory(string catID, string newParentID) + { + string? value = newParentID == CategoryID.MASTER.to_string() ? null : newParentID; + m_sync.setEntry({"categories", "parents"}, stringToNode(catID), stringToNode(value)); + } + + public void deleteCategory(string catID) + { + Logger.info("Delete category " + catID); + var feedIDs = m_db.getFeedIDofCategorie(catID); + foreach (var feedID in feedIDs) + { + moveFeed(feedID, uncategorizedID(), catID); + } + } + + public void removeCatFromFeed(string feedID, string catID) + { + moveFeed(feedID, uncategorizedID(), catID); + } + + public void importOPML(string opml) + { + var parser = new OPMLparser(opml); + parser.parse(); + } + + public bool getFeedsAndCats(Gee.List feeds, Gee.List categories, Gee.List tags, GLib.Cancellable? cancellable = null) + { + return true; + } + + public int getUnreadCount() + { + return 0; + } + + public void getArticles(int count, ArticleStatus whatToGet, DateTime? since, string? feedID, bool isTagID, GLib.Cancellable? cancellable = null) + { + var feeds = m_db.read_feeds(); + var articles = new Gee.ArrayList
(); + GLib.Mutex mutex = GLib.Mutex(); + var now = new GLib.DateTime.now_local(); + int? weeks = ((DropArticles)Settings.general().get_enum("drop-articles-after")).to_weeks(); + var dropDate = weeks == null ? null : now.add_weeks(-(int)weeks); + + try + { + var threads = new ThreadPool.with_owned_data((feed) => { + if(cancellable != null && cancellable.is_cancelled()) + return; + + Logger.debug("getArticles for feed: " + feed.getTitle()); + string url = feed.getXmlUrl().escape(""); + + if(url == null || url == "" || GLib.Uri.parse_scheme(url) == null) + { + Logger.error("no valid URL"); + return; + } + + var msg = new Soup.Message("GET", url); + var session = new Soup.Session(); + session.user_agent = Constants.USER_AGENT; + session.timeout = 5; + session.send_message(msg); + string xml = (string)msg.response_body.flatten().data; + + // parse + Rss.Parser parser = new Rss.Parser(); + try + { + parser.load_from_data(xml, xml.length); + } + catch(GLib.Error e) + { + Logger.error("decsyncInterface.getArticles: %s".printf(e.message)); + return; + } + var doc = parser.get_document(); + + string? locale = null; + if(doc.encoding != null + && doc.encoding != "") + { + locale = doc.encoding; + } + + Logger.debug("Got %u articles".printf(doc.get_items().length())); + var newArticles = new Gee.ArrayList
(); + foreach(Rss.Item item in doc.get_items()) + { + string? articleID = item.guid; + + if(articleID == null) + { + if(item.link == null) + { + Logger.warning("no valid id and no valid URL as well? what the hell man? I'm giving up"); + continue; + } + + articleID = item.link; + } + + if (m_db.read_article(articleID) != null) + { + continue; + } + + var date = Rfc822.parseDate(item.pub_date); + if (date != null) + { + Logger.info(@"Parsed $(item.pub_date) as $(date.to_string())"); + } + else + { + if (item.pub_date != null) + Logger.warning(@"RFC 822 date parser failed to parse $(item.pub_date). Falling back to DateTime.now()"); + date = new DateTime.now_local(); + } + + if (dropDate != null && date.compare(dropDate) == -1) + { + continue; + } + + //Logger.info("Got content: " + item.description); + string? content = m_utils.convert(item.description, locale); + //Logger.info("Converted to: " + item.description); + if(content == null) + content = _("Nothing to read here."); + + var enclosures = new Gee.ArrayList(); + + if(item.enclosure_url != null) + { + // FIXME: check what type of media we actually got + enclosures.add(new Enclosure(articleID, item.enclosure_url, EnclosureType.FILE)); + } + + string articleURL = item.link; + if(articleURL.has_prefix("/")) + articleURL = feed.getURL() + articleURL.substring(1); + + var article = new Article( + articleID, + (item.title != null) ? m_utils.convert(item.title, locale) : null, + articleURL, + feed.getFeedID(), + ArticleStatus.UNREAD, + ArticleStatus.UNMARKED, + content, + content, + m_utils.convert(item.author, locale), + date, + 0, + null, + enclosures + ); + + Logger.debug("Got new article: " + article.getTitle()); + + newArticles.add(article); + } + mutex.lock(); + articles.add_all(newArticles); + mutex.unlock(); + }, (int)GLib.get_num_processors(), true); + + foreach(Feed feed in feeds) + { + try + { + threads.add(feed); + } + catch(GLib.Error e) + { + Logger.error("Error creating thread to download Feed %s: %s".printf(feed.getTitle(), e.message)); + } + } + + bool immediate = false; // allow to queue up additional tasks + bool wait = true; // function will block until all tasks are done + ThreadPool.free((owned)threads, immediate, wait); + } + catch(Error e) + { + Logger.error("Error creating threads to download Feeds: " + e.message); + } + + articles.sort((a, b) => { + return strcmp(a.getArticleID(), b.getArticleID()); + }); + + if(articles.size > 0) + { + m_db_write.write_articles(articles); + Logger.debug("decsyncInterface: %i articles written".printf(articles.size)); + + var multiMap = groupBy, Article>( + articles, + article => { return articleToBasePath(article); } + ); + multiMap.get_keys().@foreach(basePath => { + var articleIDs = multiMap.@get(basePath).map(article => { + return stringToNode(article.getArticleID()); + }); + foreach (var type in toList({"read","marked"})) + { + m_sync.executeStoredEntries(basePathToPath(basePath, type), new Unit(), + key => { return articleIDs.any_match(articleID => { return articleID.equal(key); }); } + ); + } + return true; + }); + } + + m_sync.executeAllNewEntries(new Unit()); + } + + public string[] articleToPath(Article article, string type) + { + return basePathToPath(articleToBasePath(article), type); + } + + public string[] basePathToPath(Gee.List basePath, string type) + { + var path = new Gee.ArrayList(); + path.add("articles"); + path.add(type); + path.add_all(basePath); + return path.to_array(); + } + + public Gee.List articleToBasePath(Article article) + { + var datetime = article.getDate().to_utc(); + var year = datetime.format("%Y"); + var month = datetime.format("%m"); + var day = datetime.format("%d"); + return toList({year, month, day}); + } +} + +[ModuleInit] +public void peas_register_types(GLib.TypeModule module) +{ + var objmodule = module as Peas.ObjectModule; + objmodule.register_extension_type(typeof(FeedReader.FeedServerInterface), typeof(FeedReader.decsyncInterface)); +} diff --git a/plugins/backend/decsync/decsyncListeners.vala b/plugins/backend/decsync/decsyncListeners.vala new file mode 100644 index 000000000..386ae3610 --- /dev/null +++ b/plugins/backend/decsync/decsyncListeners.vala @@ -0,0 +1,285 @@ +// This file is part of FeedReader. +// +// FeedReader is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// FeedReader is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with FeedReader. If not, see . + +public class FeedReader.DecsyncListeners : GLib.Object { + + public class ReadMarkListener : OnSubdirEntryUpdateListener { + + private Gee.List m_subdir; + private bool m_is_read_entry; + private decsyncInterface m_plugin; + + public ReadMarkListener(bool is_read_entry, decsyncInterface plugin) + { + this.m_subdir = toList({"articles", is_read_entry ? "read" : "marked"}); + this.m_is_read_entry = is_read_entry; + this.m_plugin = plugin; + } + + public override Gee.List subdir() + { + return m_subdir; + } + + public override void onSubdirEntryUpdate(Gee.List path, Decsync.Entry entry, Unit extra) + { + var articleID = entry.key.get_string(); + if (articleID == null) + { + Logger.warning("Invalid articleID " + Json.to_string(entry.key, false)); + return; + } + var added = entry.value.get_boolean(); + if (m_is_read_entry) + { + Logger.debug((added ? "read " : "unread ") + articleID); + } + else + { + Logger.debug((added ? "mark " : "unmark ") + articleID); + } + Article? article = m_plugin.m_db.read_article(articleID); + if (article == null) + { + Logger.info("Unkown article " + articleID); + return; + } + if (m_is_read_entry) + { + article.setUnread(added ? ArticleStatus.READ : ArticleStatus.UNREAD); + } + else + { + article.setMarked(added ? ArticleStatus.MARKED : ArticleStatus.UNMARKED); + } + m_plugin.m_db_write.update_article(article); + } + } + + public class SubscriptionsListener : OnSubfileEntryUpdateListener { + + private Gee.List m_subfile; + private decsyncInterface m_plugin; + + public SubscriptionsListener(decsyncInterface plugin) + { + this.m_subfile = toList({"feeds", "subscriptions"}); + this.m_plugin = plugin; + } + + public override Gee.List subfile() + { + return m_subfile; + } + + public override void onSubfileEntryUpdate(Decsync.Entry entry, Unit extra) + { + var feedID = entry.key.get_string(); + if (feedID == null) + { + Logger.warning("Invalid feedID " + Json.to_string(entry.key, false)); + return; + } + var subscribed = entry.value.get_boolean(); + if (subscribed) + { + string outFeedID, errmsg; + m_plugin.addFeedWithDecsync(feedID, null, null, out outFeedID, out errmsg, false); + } + else + { + m_plugin.m_db_write.delete_feed(feedID); + } + } + } + + public class FeedNamesListener : OnSubfileEntryUpdateListener { + + private Gee.List m_subfile; + private decsyncInterface m_plugin; + + public FeedNamesListener(decsyncInterface plugin) + { + this.m_subfile = toList({"feeds", "names"}); + this.m_plugin = plugin; + } + + public override Gee.List subfile() + { + return m_subfile; + } + + public override void onSubfileEntryUpdate(Decsync.Entry entry, Unit extra) + { + var feedID = entry.key.get_string(); + if (feedID == null) + { + Logger.warning("Invalid feedID " + Json.to_string(entry.key, false)); + return; + } + var name = entry.value.get_string(); + if (name == null) + { + Logger.warning("Invalid name " + Json.to_string(entry.value, false)); + return; + } + m_plugin.m_db_write.rename_feed(feedID, name); + } + } + + public class CategoriesListener : OnSubfileEntryUpdateListener { + + private Gee.List m_subfile; + private decsyncInterface m_plugin; + + public CategoriesListener(decsyncInterface plugin) + { + this.m_subfile = toList({"feeds", "categories"}); + this.m_plugin = plugin; + } + + public override Gee.List subfile() + { + return m_subfile; + } + + public override void onSubfileEntryUpdate(Decsync.Entry entry, Unit extra) + { + var feedID = entry.key.get_string(); + if (feedID == null) + { + Logger.warning("Invalid feedID " + Json.to_string(entry.key, false)); + return; + } + var feed = m_plugin.m_db.read_feed(feedID); + if (feed == null) return; + var currentCatID = feed.getCatString(); + string newCatID; + if (entry.value.is_null()) + { + newCatID = m_plugin.uncategorizedID(); + } + else + { + newCatID = entry.value.get_string(); + } + if (newCatID == null) + { + Logger.warning("Invalid catID " + Json.to_string(entry.value, false)); + return; + } + addCategory(m_plugin, newCatID); + m_plugin.m_db_write.move_feed(feedID, currentCatID, newCatID); + } + } + + public class CategoryNamesListener : OnSubfileEntryUpdateListener { + + private Gee.List m_subfile; + private decsyncInterface m_plugin; + + public CategoryNamesListener(decsyncInterface plugin) + { + this.m_subfile = toList({"categories", "names"}); + this.m_plugin = plugin; + } + + public override Gee.List subfile() + { + return m_subfile; + } + + public override void onSubfileEntryUpdate(Decsync.Entry entry, Unit extra) + { + var catID = entry.key.get_string(); + if (catID == null) + { + Logger.warning("Invalid catID " + Json.to_string(entry.key, false)); + return; + } + var name = entry.value.get_string(); + if (name == null) + { + Logger.warning("Invalid name " + Json.to_string(entry.value, false)); + return; + } + m_plugin.m_db_write.rename_category(catID, name); + Logger.debug("Renamed category " + catID + " to " + name); + } + } + + public class CategoryParentsListener : OnSubfileEntryUpdateListener { + + private Gee.List m_subfile; + private decsyncInterface m_plugin; + + public CategoryParentsListener(decsyncInterface plugin) + { + this.m_subfile = toList({"categories", "parents"}); + this.m_plugin = plugin; + } + + public override Gee.List subfile() + { + return m_subfile; + } + + public override void onSubfileEntryUpdate(Decsync.Entry entry, Unit extra) + { + var catID = entry.key.get_string(); + if (catID == null) + { + Logger.warning("Invalid catID " + Json.to_string(entry.key, false)); + return; + } + string parentID; + if (entry.value.is_null()) + { + parentID = CategoryID.MASTER.to_string(); + } + else + { + parentID = entry.value.get_string(); + } + if (parentID == null) + { + Logger.warning("Invalid parentID " + Json.to_string(entry.value, false)); + return; + } + addCategory(m_plugin, parentID); + m_plugin.m_db_write.move_category(catID, parentID); + Logger.debug("Moved category " + catID + " to " + parentID); + } + } + + private static void addCategory(decsyncInterface plugin, string catID) + { + if (catID == plugin.uncategorizedID() || catID == CategoryID.MASTER.to_string() || plugin.m_db.category_exists(catID)) + { + return; + } + var cat = new Category(catID, catID, 0, 99, CategoryID.MASTER.to_string(), 1); + var list = new Gee.LinkedList(); + list.add(cat); + plugin.m_db_write.write_categories(list); + plugin.m_sync.executeStoredEntries({"categories", "names"}, new Unit(), + stringEquals(catID) + ); + plugin.m_sync.executeStoredEntries({"categories", "parents"}, new Unit(), + stringEquals(catID) + ); + Logger.debug("Added category " + catID); + } +} diff --git a/plugins/backend/decsync/decsyncUtils.vala b/plugins/backend/decsync/decsyncUtils.vala new file mode 100644 index 000000000..f71c32417 --- /dev/null +++ b/plugins/backend/decsync/decsyncUtils.vala @@ -0,0 +1,120 @@ +// This file is part of FeedReader. +// +// FeedReader is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// FeedReader is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with FeedReader. If not, see . + +public class FeedReader.DecsyncUtils : GLib.Object { + + GLib.Settings m_settings; + + public DecsyncUtils(GLib.SettingsBackend? settings_backend) + { + if(settings_backend != null) + m_settings = new GLib.Settings.with_backend("org.gnome.feedreader.decsync", settings_backend); + else + m_settings = new GLib.Settings("org.gnome.feedreader.decsync"); + } + + public string getDecsyncDir() + { + var dir = Utils.gsettingReadString(m_settings, "decsync-dir"); + if (dir == "") + { + return getDefaultDecsyncBaseDir(); + } + else + { + return dir; + } + } + + public void setDecsyncDir(string decsyncDir) + { + Utils.gsettingWriteString(m_settings, "decsync-dir", decsyncDir); + } + + public Feed? downloadFeed(Soup.Session session, string xmlURL, string feedID, Gee.List catIDs, out string errmsg) + { + errmsg = ""; + + // download + //Logger.debug(@"Requesting: $xmlURL"); + var msg = new Soup.Message("GET", xmlURL); + if (msg == null) + { + errmsg = @"Couldn't parse feed URL: $xmlURL"; + Logger.warning(errmsg); + return null; + } + uint status = session.send_message(msg); + if(status != 200) + { + errmsg = "Could not download feed"; + Logger.warning(errmsg); + return null; + } + string xml = (string)msg.response_body.flatten().data; + string url = "https://google.com"; + + // parse + Rss.Parser parser = new Rss.Parser(); + + try + { + parser.load_from_data(xml, xml.length); + } + catch(Error e) + { + errmsg = "Could not parse feed"; + Logger.warning(errmsg); + return null; + } + + var doc = parser.get_document(); + + if(doc.link != null + && doc.link != "") + url = doc.link; + + var Feed = new Feed( + feedID, + doc.title, + url, + 0, + catIDs, + doc.image_url, + xmlURL); + + return Feed; + } + + public string? convert(string? text, string? locale) + { + if(text == null) + return null; + + if(locale == null) + return text; + + try + { + return GLib.convert(text, -1, "utf-8", locale); + } + catch(ConvertError e) + { + Logger.error(e.message); + } + + return ""; + } +} diff --git a/plugins/backend/decsync/icons/64x64/places/feed-service-decsync-symbolic.svg b/plugins/backend/decsync/icons/64x64/places/feed-service-decsync-symbolic.svg new file mode 100644 index 000000000..5172ae9f5 --- /dev/null +++ b/plugins/backend/decsync/icons/64x64/places/feed-service-decsync-symbolic.svg @@ -0,0 +1,132 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/backend/decsync/icons/64x64/places/feed-service-decsync.svg b/plugins/backend/decsync/icons/64x64/places/feed-service-decsync.svg new file mode 100644 index 000000000..55d8af496 --- /dev/null +++ b/plugins/backend/decsync/icons/64x64/places/feed-service-decsync.svg @@ -0,0 +1,145 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/backend/decsync/libdecsync b/plugins/backend/decsync/libdecsync new file mode 160000 index 000000000..30681106c --- /dev/null +++ b/plugins/backend/decsync/libdecsync @@ -0,0 +1 @@ +Subproject commit 30681106cf38c4017e428630ee365e1a15eed318 diff --git a/plugins/backend/decsync/libmrss/meson.build b/plugins/backend/decsync/libmrss/meson.build new file mode 100644 index 000000000..595dab82c --- /dev/null +++ b/plugins/backend/decsync/libmrss/meson.build @@ -0,0 +1,16 @@ +mrss_inc = include_directories(['.', '../libnxml']) +mrss_lib = static_library( + 'mrss', + [ + 'mrss_download.c', + 'mrss_edit.c', + 'mrss_free.c', + 'mrss_generic.c', + 'mrss_options.c', + 'mrss_parser.c', + 'mrss_search.c', + 'mrss_write.c' + ], + c_args: ['-Wno-comment', '-Wno-pointer-to-int-cast'], + include_directories: mrss_inc +) diff --git a/plugins/backend/decsync/libmrss/mrss.h b/plugins/backend/decsync/libmrss/mrss.h new file mode 100644 index 000000000..d1055b5cd --- /dev/null +++ b/plugins/backend/decsync/libmrss/mrss.h @@ -0,0 +1,897 @@ +/* mRss - Copyright (C) 2005-2007 bakunin - Andrea Marchesini + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __M_RSS_H__ +#define __M_RSS_H__ + +#include +#include + +#define LIBMRSS_VERSION_STRING "0.19.2" + +#define LIBMRSS_MAJOR_VERSION 0 +#define LIBMRSS_MINOR_VERSION 19 +#define LIBMRSS_MICRO_VERSION 2 + +typedef struct mrss_t mrss_t; +typedef struct mrss_options_t mrss_options_t; +typedef struct mrss_item_t mrss_item_t; +typedef struct mrss_category_t mrss_category_t; +typedef struct mrss_hour_t mrss_hour_t; +typedef struct mrss_day_t mrss_day_t; +typedef struct mrss_tag_t mrss_tag_t; +typedef struct mrss_attribute_t mrss_attribute_t; +typedef void * mrss_generic_t; + +/** This enum describes the error type of libmrss */ +typedef enum { + MRSS_OK = 0, /**< No error */ + MRSS_ERR_POSIX, /**< For the correct error, use errno */ + MRSS_ERR_PARSER, /**< Parser error */ + MRSS_ERR_DOWNLOAD, /**< Download error */ + MRSS_ERR_VERSION, /**< The RSS has a no compatible VERSION */ + MRSS_ERR_DATA /**< The parameters are incorrect */ +} mrss_error_t; + +typedef enum { + MRSS_VERSION_0_91, /**< 0.91 RSS version */ + MRSS_VERSION_0_92, /**< 0.92 RSS version */ + MRSS_VERSION_1_0, /**< 1.0 RSS version */ + MRSS_VERSION_2_0, /**< 2.0 RSS version */ + MRSS_VERSION_ATOM_0_3, /**< 0.3 Atom version */ + MRSS_VERSION_ATOM_1_0 /**< 1.0 Atom version */ +} mrss_version_t; + +/** Flag list for mrss_set and mrss_get functions */ +typedef enum { + /* Generic */ + + /** Set the ersion to a mrss_t element - the value is a mrss_version_t enum */ + MRSS_FLAG_VERSION = 1, + + /** Set the title to a mrss_t element - the value is a string */ + MRSS_FLAG_TITLE, + /** Set the title type to a mrss_t element - the value is a string (ex: text, html, ...)*/ + MRSS_FLAG_TITLE_TYPE, + /** Set the description to a mrss_t element - the value is a string */ + MRSS_FLAG_DESCRIPTION, + /** Set the description type to a mrss_t element - the value is a string */ + MRSS_FLAG_DESCRIPTION_TYPE, + /** Set the link to a mrss_t element - the value is a string */ + MRSS_FLAG_LINK, + /** Set the id to a mrss_t element - the value is a string */ + MRSS_FLAG_ID, + /** Set the language to a mrss_t element - the value is a string */ + MRSS_FLAG_LANGUAGE, + /** Set the rating to a mrss_t element - the value is a string */ + MRSS_FLAG_RATING, + /** Set the copyright to a mrss_t element - the value is a string */ + MRSS_FLAG_COPYRIGHT, + /** Set the copyright type to a mrss_t element - the value is a string */ + MRSS_FLAG_COPYRIGHT_TYPE, + /** Set the pubDate to a mrss_t element - the value is a string */ + MRSS_FLAG_PUBDATE, + /** Set the lastBuildDate to a mrss_t element - the value is a string */ + MRSS_FLAG_LASTBUILDDATE, + /** Set the docs to a mrss_t element - the value is a string */ + MRSS_FLAG_DOCS, + /** Set the managingeditor to a mrss_t element - the value is a string */ + MRSS_FLAG_MANAGINGEDITOR, + /** Set the managingeditor's email to a mrss_t element - the value is a string */ + MRSS_FLAG_MANAGINGEDITOR_EMAIL, + /** Set the managingeditor's uri to a mrss_t element - the value is a string */ + MRSS_FLAG_MANAGINGEDITOR_URI, + /** Set the webMaster to a mrss_t element - the value is a string */ + MRSS_FLAG_WEBMASTER, + /** Set the generator to a mrss_t element - the value is a string */ + MRSS_FLAG_TTL, + /** Set the about to a mrss_t element - the value is a string */ + MRSS_FLAG_ABOUT, + + /* Contributor */ + + /** Set the contributor to a mrss_t element - the value is a string */ + MRSS_FLAG_CONTRIBUTOR, + /** Set the contributor's email to a mrss_t element - the value is a string */ + MRSS_FLAG_CONTRIBUTOR_EMAIL, + /** Set the contributor's uri to a mrss_t element - the value is a string */ + MRSS_FLAG_CONTRIBUTOR_URI, + + /* Generator */ + + /** Set the generator to a mrss_t element - the value is a string */ + MRSS_FLAG_GENERATOR, + /** Set the generator's email to a mrss_t element - the value is a string */ + MRSS_FLAG_GENERATOR_URI, + /** Set the generator's uri to a mrss_t element - the value is a string */ + MRSS_FLAG_GENERATOR_VERSION, + + /* Image */ + + /** Set the image_title to a mrss_t element - the value is a string */ + MRSS_FLAG_IMAGE_TITLE, + /** Set the image_url to a mrss_t element - the value is a string */ + MRSS_FLAG_IMAGE_URL, + /** Set the image_logo to a mrss_t element - the value is a string */ + MRSS_FLAG_IMAGE_LOGO, + /** Set the image_link to a mrss_t element - the value is a string */ + MRSS_FLAG_IMAGE_LINK, + /** Set the image_width to a mrss_t element - the value is a integer */ + MRSS_FLAG_IMAGE_WIDTH, + /** Set the image_height to a mrss_t element - the value is a integer */ + MRSS_FLAG_IMAGE_HEIGHT, + /** Set the image_description to a mrss_t element - the value is a string */ + MRSS_FLAG_IMAGE_DESCRIPTION, + + /* TextInput */ + + /** Set the textinput_title to a mrss_t element - the value is a string */ + MRSS_FLAG_TEXTINPUT_TITLE, + /** Set the textinput_description to a mrss_t element - the value is a string */ + MRSS_FLAG_TEXTINPUT_DESCRIPTION, + /** Set the textinput_name to a mrss_t element - the value is a string */ + MRSS_FLAG_TEXTINPUT_NAME, + /** Set the textinput_link to a mrss_t element - the value is a string */ + MRSS_FLAG_TEXTINPUT_LINK, + + /* Cloud */ + + /** Set the cloud to a mrss_t element - the value is a string */ + MRSS_FLAG_CLOUD, + /** Set the cloud_domain to a mrss_t element - the value is a string */ + MRSS_FLAG_CLOUD_DOMAIN, + /** Set the cloud_port to a mrss_t element - the value is a string */ + MRSS_FLAG_CLOUD_PORT, + /** Set the cloud_path to a mrss_t element - the value is a integer */ + MRSS_FLAG_CLOUD_PATH, + /** Set the cloud_registerProcedure to a mrss_t element - + * the value is a string */ + MRSS_FLAG_CLOUD_REGISTERPROCEDURE, + /** Set the cloud_protocol to a mrss_t element - the value is a string */ + MRSS_FLAG_CLOUD_PROTOCOL, + + /* SkipHours */ + + /** Set the hour to a mrss_hour_t element - the value is a string */ + MRSS_FLAG_HOUR, + + /* SkipDays */ + + /** Set the day to a mrss_day_t element - the value is a string */ + MRSS_FLAG_DAY, + + /* Category or Item/Category */ + + /** Set the category to a mrss_category_t element - the value is a string */ + MRSS_FLAG_CATEGORY, + /** Set the domain to a mrss_category_t element - the value is a string */ + MRSS_FLAG_CATEGORY_DOMAIN, + /** Set the label to a mrss_category_t element - the value is a string */ + MRSS_FLAG_CATEGORY_LABEL, + + /* Item */ + + /** Set the title to a mrss_item_t element - the value is a string */ + MRSS_FLAG_ITEM_TITLE, + /** Set the title type to a mrss_item_t element - the value is a string */ + MRSS_FLAG_ITEM_TITLE_TYPE, + /** Set the link to a mrss_item_t element - the value is a string */ + MRSS_FLAG_ITEM_LINK, + /** Set the description to a mrss_item_t element - the value is a string */ + MRSS_FLAG_ITEM_DESCRIPTION, + /** Set the description type to a mrss_item_t element - the value is a string */ + MRSS_FLAG_ITEM_DESCRIPTION_TYPE, + /** Set the copyright to a mrss_item_t element - the value is a string */ + MRSS_FLAG_ITEM_COPYRIGHT, + /** Set the copyright type to a mrss_item_t element - the value is a string */ + MRSS_FLAG_ITEM_COPYRIGHT_TYPE, + + /** Set the author to a mrss_item_t element - the value is a string */ + MRSS_FLAG_ITEM_AUTHOR, + /** Set the author's uri to a mrss_item_t element - the value is a string */ + MRSS_FLAG_ITEM_AUTHOR_URI, + /** Set the author's email to a mrss_item_t element - the value is a string */ + MRSS_FLAG_ITEM_AUTHOR_EMAIL, + + /** Set the contributor to a mrss_item_t element - the value is a string */ + MRSS_FLAG_ITEM_CONTRIBUTOR, + /** Set the contributor's uri to a mrss_item_t element - the value is a string */ + MRSS_FLAG_ITEM_CONTRIBUTOR_URI, + /** Set the contributor's email to a mrss_item_t element - the value is a string */ + MRSS_FLAG_ITEM_CONTRIBUTOR_EMAIL, + + /** Set the comments to a mrss_item_t element - the value is a string */ + MRSS_FLAG_ITEM_COMMENTS, + /** Set the pubDate to a mrss_item_t element - the value is a string */ + MRSS_FLAG_ITEM_PUBDATE, + /** Set the guid to a mrss_item_t element - the value is a string */ + MRSS_FLAG_ITEM_GUID, + /** Set the guid_isPermaLink to a mrss_item_t element - + * the value is a integer */ + MRSS_FLAG_ITEM_GUID_ISPERMALINK, + /** Set the source to a mrss_item_t element - the value is a string */ + MRSS_FLAG_ITEM_SOURCE, + /** Set the source_url to a mrss_item_t element - the value is a string */ + MRSS_FLAG_ITEM_SOURCE_URL, + /** Set the enclosure to a mrss_item_t element - the value is a string */ + MRSS_FLAG_ITEM_ENCLOSURE, + /** Set the enclosure_url to a mrss_item_t element - the value is a string */ + MRSS_FLAG_ITEM_ENCLOSURE_URL, + /** Set the enclosure_length to a mrss_item_t element - + * the value is a integer */ + MRSS_FLAG_ITEM_ENCLOSURE_LENGTH, + /** Set the enclosure_type to a mrss_item_t element - the value is a string */ + MRSS_FLAG_ITEM_ENCLOSURE_TYPE, + + /* Item */ + + /** Set the name to a mrss_tag_t element - the value is a string */ + MRSS_FLAG_TAG_NAME, + + /** Set the value to a mrss_tag_t element - the value is a string */ + MRSS_FLAG_TAG_VALUE, + + /** Set the namespace to a mrss_tag_t element - the value is a string */ + MRSS_FLAG_TAG_NS, + + /** Set the name to a mrss_attribute_t element - the value is a string */ + MRSS_FLAG_ATTRIBUTE_NAME, + + /** Set the value to a mrss_attribute_t element - the value is a string */ + MRSS_FLAG_ATTRIBUTE_VALUE, + + /** Set the namespace to a mrss_attribute_t element - the value is a string */ + MRSS_FLAG_ATTRIBUTE_NS, + + /** Set the terminetor flag */ + MRSS_FLAG_END = 0 + +} mrss_flag_t; + +/** Enum for the casting of the libmrss data struct */ +typedef enum { + /** The data struct is a mrss_t */ + MRSS_ELEMENT_CHANNEL, + /** The data struct is a mrss_item_t */ + MRSS_ELEMENT_ITEM, + /** The data struct is a mrss_hour_t */ + MRSS_ELEMENT_SKIPHOURS, + /** The data struct is a mrss_day_t */ + MRSS_ELEMENT_SKIPDAYS, + /** The data struct is a mrss_category_t */ + MRSS_ELEMENT_CATEGORY, + /** The data struct is a mrss_tag_t */ + MRSS_ELEMENT_TAG, + /** The data struct is a mrss_attribute_t */ + MRSS_ELEMENT_ATTRIBUTE +} mrss_element_t; + +/** Data struct for any items of RSS. It contains a pointer to the list + * of categories. + * + * \brief + * Struct data for item elements */ +struct mrss_item_t { + + /** For internal use only: */ + mrss_element_t element; + int allocated; + + /* Data: */ + + /* 0.91 0.92 1.0 2.0 ATOM */ + char *title; /* R O O O R */ + char *title_type; /* - - - - O */ + char *link; /* R O O O O */ + char *description; /* R O - O O */ + char *description_type; /* - - - - 0 */ + char *copyright; /* - - - - O */ + char *copyright_type; /* - - - - O */ + + char *author; /* - - - O O */ + char *author_uri; /* - - - - O */ + char *author_email; /* - - - - O */ + + char *contributor; /* - - - - O */ + char *contributor_uri; /* - - - - O */ + char *contributor_email; /* - - - - O */ + + char *comments; /* - - - O - */ + char *pubDate; /* - - - O O */ + char *guid; /* - - - O O */ + int guid_isPermaLink; /* - - - O - */ + + char *source; /* - O - O - */ + char *source_url; /* - R - R - */ + + char *enclosure; /* - O - O - */ + char *enclosure_url; /* - R - R - */ + int enclosure_length; /* - R - R - */ + char *enclosure_type; /* - R - R - */ + + mrss_category_t *category; /* - O - O O */ + + mrss_tag_t *other_tags; + + mrss_item_t *next; +}; + +/** Data struct for skipHours elements. + * + * \brief + * Struct data for skipHours elements */ +struct mrss_hour_t { + /** For internal use only: */ + mrss_element_t element; + int allocated; + + /* Data: */ + /* 0.91 0.92 1.0 2.0 ATOM */ + char *hour; /* R R - R - */ + mrss_hour_t *next; +}; + +/** Data struct for skipDays elements. + * + * \brief + * Struct data for skipDays elements */ +struct mrss_day_t { + /** For internal use only: */ + mrss_element_t element; + int allocated; + + /* Data: */ + /* 0.91 0.92 1.0 2.0 ATOM */ + char *day; /* R R - R - */ + mrss_day_t *next; +}; + +/** Data struct for category elements + * + * \brief + * Struct data for category elements */ +struct mrss_category_t { + /** For internal use only: */ + mrss_element_t element; + int allocated; + + /* Data: */ + /* 0.91 0.92 1.0 2.0 ATOM */ + char *category; /* - R - R R */ + char *domain; /* - O - O O */ + char *label; /* - - - - O */ + mrss_category_t *next; +}; + +/** Principal data struct. It contains pointers to any other structures. + * + * \brief + * Principal data struct. It contains pointers to any other structures */ +struct mrss_t { + /** For internal use only: */ + mrss_element_t element; + int allocated; + int curl_error; + + /* Data: */ + + char *file; + size_t size; + char *encoding; + + mrss_version_t version; /* 0.91 0.92 1.0 2.0 ATOM */ + + char *title; /* R R R R R */ + char *title_type; /* - - - - O */ + char *description; /* R R R R R */ + char *description_type; /* - - - - O */ + char *link; /* R R R R O */ + char *id; /* - - - - O */ + char *language; /* R O - O O */ + char *rating; /* O O - O - */ + char *copyright; /* O O - O O */ + char *copyright_type; /* - - - - O */ + char *pubDate; /* O O - O - */ + char *lastBuildDate; /* O O - O O */ + char *docs; /* O O - O - */ + char *managingeditor; /* O O - O O */ + char *managingeditor_email; /* O O - O O */ + char *managingeditor_uri; /* O O - O O */ + char *webMaster; /* O O - O - */ + int ttl; /* - - - O - */ + char *about; /* - - R - - */ + + /* Contributor */ /* - - - - O */ + char *contributor; /* - - - - R */ + char *contributor_email; /* - - - - O */ + char *contributor_uri; /* - - - - O */ + + /* Generator */ + char *generator; /* - - - O O */ + char *generator_uri; /* - - - - O */ + char *generator_version; /* - - - - O */ + + /* Tag Image: */ /* O O O O - */ + char *image_title; /* R R R R - */ + char *image_url; /* R R R R O */ + char *image_logo; /* - - - - O */ + char *image_link; /* R R R R - */ + unsigned int image_width; /* O O - O - */ + unsigned int image_height; /* O O - O - */ + char *image_description; /* O O - O - */ + + /* TextInput: */ /* O O O O - */ + char *textinput_title; /* R R R R - */ + char *textinput_description; /* R R R R - */ + char *textinput_name; /* R R R R - */ + char *textinput_link; /* R R R R - */ + + /* Cloud */ + char *cloud; /* - O - O - */ + char *cloud_domain; /* - R - R - */ + int cloud_port; /* - R - R - */ + char *cloud_path; /* - R - R - */ + char *cloud_registerProcedure;/* - R - R - */ + char *cloud_protocol; /* - R - R - */ + + mrss_hour_t *skipHours; /* O O - O - */ + mrss_day_t *skipDays; /* O O - O - */ + + mrss_category_t *category; /* - O - O O */ + + mrss_item_t *item; /* R R R R R */ + + mrss_tag_t *other_tags; + + void *c_locale; + +}; + +/** Data struct for any other tag out of the RSS namespace. + * + * \brief + * Struct data for external tags */ +struct mrss_tag_t { + /** For internal use only: */ + mrss_element_t element; + int allocated; + + /*name of the tag */ + char *name; + + /* value */ + char *value; + + /* namespace */ + char *ns; + + /* list of attributes: */ + mrss_attribute_t *attributes; + + /* Sub tags: */ + mrss_tag_t *children; + + /* the next tag: */ + mrss_tag_t *next; +}; + +/** Data struct for the attributes of the tag + * + * \brief + * Struct data for external attribute */ +struct mrss_attribute_t { + /** For internal use only: */ + mrss_element_t element; + int allocated; + + /* name of the tag */ + char *name; + + /* value */ + char *value; + + /* namespace */ + char *ns; + + /* The next attribute: */ + mrss_attribute_t *next; +}; + +/** Options data struct. It contains some user preferences. + * + * \brief + * Options data struct. It contains some user preferences. */ +struct mrss_options_t { + int timeout; + char *proxy; + char *proxy_authentication; + char *certfile; + char *cacert; + char *password; + int verifypeer; + char *authentication; + char *user_agent; +}; + +/** PARSE FUNCTIONS *********************************************************/ + +/** + * Parses a url and creates the data struct of the feed RSS url. + * This function downloads your request if this is http or ftp. + * \param url The url to be parsed + * \param mrss the pointer to your data struct + * \return the error code + */ +mrss_error_t mrss_parse_url (char * url, + mrss_t ** mrss); + +/** + * Like the previous function but with a options struct. + * \param url The url to be parsed + * \param mrss the pointer to your data struct + * \param options a pointer to a options data struct + * \return the error code + */ +mrss_error_t mrss_parse_url_with_options + (char * url, + mrss_t ** mrss, + mrss_options_t * options); + +/** + * Like the previous function but with CURLcode error + * \param url The url to be parsed + * \param mrss the pointer to your data struct + * \param options a pointer to a options data struct. It can be NULL + * \param curlcode the error code from libcurl + * \return the error code + */ +mrss_error_t mrss_parse_url_with_options_and_error + (char * url, + mrss_t ** mrss, + mrss_options_t * options, + CURLcode * curlcode); + +/** + * Like the previous function but you take ownership of the downloaded buffer + * in case of success + * \param url The url to be parsed + * \param mrss the pointer to your data struct + * \param options a pointer to a options data struct + * \param curlcode the error code from libcurl + * \param feed_content a pointer to the buffer with the document. This is not NULL terminated + * \param feed_size the size of the buffer above + * \return the error code + */ +mrss_error_t mrss_parse_url_with_options_error_and_transfer_buffer + (char * url, + mrss_t ** mrss, + mrss_options_t * options, + CURLcode * curlcode, + char ** feed_content, + int * feed_size); + +/** + * Parses a file and creates the data struct of the feed RSS url + * \param file The file to be parsed + * \param mrss the pointer to your data struct + * \return the error code + */ +mrss_error_t mrss_parse_file (char * file, + mrss_t ** mrss); + +/** + * Parses a buffer and creates the data struct of the feed RSS url + * \param buffer Pointer to the xml memory stream to be parsed + * \param size_buffer The size of the array of char + * \param mrss the pointer to your data struct + * \return the error code + */ +mrss_error_t mrss_parse_buffer (char * buffer, + size_t size_buffer, + mrss_t ** mrss); + +/** WRITE FUNCTIONS *********************************************************/ + +/** + * Writes a RSS struct data in a local file + * \param mrss the rss struct data + * \param file the local file + * \return the error code + */ +mrss_error_t mrss_write_file (mrss_t * mrss, + char * file); + +/** + * Write a RSS struct data in a buffer. + * + * \code + * char *buffer; + * buffer=NULL; //<--- This is important!! + * mrss_write_buffer (mrss, &buffer); + * \endcode + * + * The buffer must be NULL. + * \param mrss the rss struct data + * \param buffer the buffer + * \return the error code + */ +mrss_error_t mrss_write_buffer (mrss_t * mrss, + char ** buffer); + +/** FREE FUNCTION ***********************************************************/ + +/** + * This function frees any type of data struct of libmrss. If the element + * is alloced by libmrss, it will be freed, else this function frees + * only the internal data. + * + * \code + * mrss_t *t=....; + * mrss_item_t *item=...; + * + * mrss_free(t); + * mrss_free(item); + * \endcode + * + * \param element the data struct + * \return the error code + */ +mrss_error_t mrss_free (mrss_generic_t element); + +/** GENERIC FUNCTION ********************************************************/ + +/** + * This function returns a static string with the description of error code + * \param err the error code that you need as string + * \return a string. Don't free this string! + */ +char * mrss_strerror (mrss_error_t err); + +/** + * This function returns a static string with the description of curl code + * \param err the error code that you need as string + * \return a string. Don't free this string! + */ +char * mrss_curl_strerror (CURLcode err); + +/** + * This function returns the mrss_element_t of a mrss data struct. + * \param element it is the element that you want check + * \param ret it is a pointer to a mrss_element_t. It will be sets. + * \return the error code + */ +mrss_error_t mrss_element (mrss_generic_t element, + mrss_element_t *ret); + +/** + * This function returns the number of seconds sinze Jennuary 1st 1970 in the + * UTC time zone, for the url that the urlstring parameter specifies. + * + * \param urlstring the url + * \param lastmodified is a pointer to a time_t struct. The return value can + * be 0 if the HEAD request does not return a Last-Modified value. + * \return the error code + */ +mrss_error_t mrss_get_last_modified (char * urlstring, + time_t * lastmodified); + +/** + * Like the previous function but with a options struct. + * + * \param urlstring the url + * \param lastmodified is a pointer to a time_t struct. The return value can + * be 0 if the HEAD request does not return a Last-Modified value. + * \param options a pointer to a options struct + * \return the error code + */ +mrss_error_t mrss_get_last_modified_with_options + (char * urlstring, + time_t * lastmodified, + mrss_options_t * options); +/** + * Like the previous function but with a CURLcode pointer. + * + * \param urlstring the url + * \param lastmodified is a pointer to a time_t struct. The return value can + * be 0 if the HEAD request does not return a Last-Modified value. + * \param options a pointer to a options struct + * \param curl_code it will contain the error code of libcurl + * \return the error code + */ +mrss_error_t mrss_get_last_modified_with_options_and_error + (char * urlstring, + time_t * lastmodified, + mrss_options_t * options, + CURLcode * curl_code); + +/** EDIT FUNCTIONS **********************************************************/ + +/** If you want create a new feed RSS from scratch, you need use + * this function as the first. + * + * \code + * mrss_t *d; + * mrss_error_t err; + * char *string; + * int integer; + * + * d=NULL; // ->this is important! If d!=NULL, mrss_new doesn't alloc memory. + * mrss_new(&d); + * + * err=mrss_set (d, + * MRSS_FLAG_VERSION, MRSS_VERSION_0_92, + * MRSS_FLAG_TITLE, "the title!", + * MRSS_FLAG_TTL, 12, + * MRSS_FLAG_END); + * + * if(err!=MRSS_OK) printf("%s\n",mrss_strerror(err)); + * + * err=mrss_get (d, + * MRSS_FLAG_TITLE, &string, + * MRSS_FLAG_TTL, &integer, + * MRSS_FLAG_END); + * + * if(err!=MRSS_OK) printf("%s\n",mrss_strerror(err)); + * printf("The title is: '%s'\n", string); + * printf("The ttl is: '%d'\n", integer); + * free(string); + * \endcode + * + * \param mrss is the pointer to the new data struct + * \return the error code + */ +mrss_error_t mrss_new (mrss_t ** mrss); + +/** + * For insert/replace/remove a flags use this function as this example: + * \code + * mrss_set(mrss, MRSS_FLAG_TITLE, "hello world", MRSS_FLAG_END); + * mrss_set(item, MRSS_FLAG_DESCRIPTION, NULL, MRSS_FLAG_END); + * \endcode + * + * \param element it is the mrss data that you want changes the the next + * list of elements. The list is composted by KEY - VALUES and as last + * element MRSS_FLAG_END. The variable of value depends from key. + * \see mrss_flag_t + * \return the error code + */ +mrss_error_t mrss_set (mrss_generic_t element, + ...); + +/** + * This function returns the request arguments. The syntax is the same of + * mrss_set but the values of the list are pointer to data element (int *, + * char **). If the key needs a char **, the value will be allocated. + * \code + * mrss_get(category, MRSS_FLAG_CATEGORY_DOMAIN, &string, MRSS_FLAG_END); + * if(string) free(string); + * \endcode + * \param element it is any type of mrss data struct. + * \return the error code + */ +mrss_error_t mrss_get (mrss_generic_t element, + ...); + +/** + * This function adds an element to another element. For example you can + * add a item to a channel, or a category to a item, and so on. Look this + * example: + * \code + * mrss_item_t *item = NULL; + * mrss_hour_t *hour = NULL; + * mrss_day_t day; // If the element is no null, the function + * mrss_category_t category, // does not alloc it + * + * mrss_new_subdata(mrss, MRSS_ELEMENT_ITEM, &item); + * mrss_new_subdata(mrss, MRSS_ELEMENT_SKIPHOURS, &hour); + * mrss_new_subdata(mrss, MRSS_ELEMENT_SKIPDAYS, &day); + * mrss_new_subdata(item, MRSS_ELEMENT_ITEM_CATEGORY, &category); + * \endcode + * \param element it is the parent element + * \param subelement it is the type of the child (MRSS_ELEMENT_ITEM, + * MRSS_ELEMENT_CATEGORY, ...) + * \param subdata it is the pointer to the new struct. If the pointer + * of *subdata exists, it will no alloced, else yes. + * \return the error code + * \see mrss_element_t + */ +mrss_error_t mrss_new_subdata (mrss_generic_t element, + mrss_element_t subelement, + mrss_generic_t subdata); + +/** + * This function removes a subdata element. As first argoment you must specify + * the parent, and second argoment the child. + * \code + * mrss_remove_subdata(mrss, item); + * \endcode + * \param element it is the parent + * \param subdata the child that you want remove. Remember: + * mrss_remove_subdata does not free the memory. So you can remove a item + * and reinsert it after. + * \return the error code + */ +mrss_error_t mrss_remove_subdata (mrss_generic_t element, + mrss_generic_t subdata); + +/* TAGS FUNCTIONS **********************************************************/ + +/** + * This function search a tag in a mrss_t, a mrss_item_t or a mrss_tag_t from + * name and a namespace. + * \param element it is the parent node (mrss_t or mrss_item_t) + * \param name the name of the element + * \param ns the namespace. It can be null if the tag has a null namespace + * \param tag the return pointer + * \return the error code + */ +mrss_error_t mrss_search_tag (mrss_generic_t element, + char * name, + char * ns, + mrss_tag_t ** tag); + +/** + * This function search an attribute from a mrss_tag_t, a name and a namespace + * \param element it is the mrss_tag_t + * \param name the name of the element + * \param ns the namespace. It can be null if the tag has a null namespace + * \param attribute the return pointer + * \return the error code + */ +mrss_error_t mrss_search_attribute (mrss_generic_t element, + char * name, + char * ns, + mrss_attribute_t ** attribute); + +/* OPTIONS FUNCTIONS *******************************************************/ + +/** + * This function creates a options struct. + * + * \param timeout timeout for the download procedure + * \param proxy a proxy server. can be NULL + * \param proxy_authentication a proxy authentication (user:pwd). can be NULL + * \param certfile a certificate for ssl autentication connection + * \param password the password of certfile + * \param cacert CA certificate to verify peer against. can be NULL + * \param verifypeer active/deactive the peer check + * \param authentication an authentication login (user:pwd). can be NULL + * \param user_agent a user_agent. can be NULL + * \return a pointer to a new allocated mrss_options_t struct + */ +mrss_options_t * + mrss_options_new (int timeout, + char *proxy, + char *proxy_authentication, + char *certfile, + char *password, + char *cacert, + int verifypeer, + char *authentication, + char *user_agent); + +/** + * This function destroys a options struct. + * \param options a pointer to a options struct + */ +void mrss_options_free (mrss_options_t *options); + +#endif + +/* EOF */ + diff --git a/plugins/backend/decsync/libmrss/mrss_download.c b/plugins/backend/decsync/libmrss/mrss_download.c new file mode 100644 index 000000000..c28980aa2 --- /dev/null +++ b/plugins/backend/decsync/libmrss/mrss_download.c @@ -0,0 +1,51 @@ +/* mRss - Copyright (C) 2005-2007 bakunin - Andrea Marchesini + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#include "mrss.h" +#include "mrss_internal.h" + +char * +__mrss_download_file (nxml_t * nxml, char *fl, size_t * size, + mrss_error_t * error, CURLcode * code) +{ + char *buffer; + + if (code) + *code = CURLE_OK; + + switch (nxml_download_file (nxml, fl, &buffer, size)) + { + case NXML_OK: + return buffer; + + case NXML_ERR_DOWNLOAD: + + if (code) + *code = nxml_curl_error (nxml, NXML_ERR_DOWNLOAD); + + *error = MRSS_ERR_DOWNLOAD; + return NULL; + + default: + *error = MRSS_ERR_POSIX; + return NULL; + } +} + +/* EOF */ diff --git a/plugins/backend/decsync/libmrss/mrss_edit.c b/plugins/backend/decsync/libmrss/mrss_edit.c new file mode 100644 index 000000000..a21a559d6 --- /dev/null +++ b/plugins/backend/decsync/libmrss/mrss_edit.c @@ -0,0 +1,1739 @@ +/* mRss - Copyright (C) 2005-2007 bakunin - Andrea Marchesini + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "mrss.h" +#include "mrss_internal.h" + +static mrss_error_t __mrss_set_channel (mrss_t *, va_list); +static mrss_error_t __mrss_set_item (mrss_item_t *, va_list); +static mrss_error_t __mrss_set_hour (mrss_hour_t *, va_list); +static mrss_error_t __mrss_set_day (mrss_day_t *, va_list); +static mrss_error_t __mrss_set_category (mrss_category_t *, va_list); +static mrss_error_t __mrss_set_tag (mrss_tag_t *, va_list); +static mrss_error_t __mrss_set_attribute (mrss_attribute_t *, va_list); + +static mrss_error_t __mrss_get_channel (mrss_t *, va_list); +static mrss_error_t __mrss_get_item (mrss_item_t *, va_list); +static mrss_error_t __mrss_get_hour (mrss_hour_t *, va_list); +static mrss_error_t __mrss_get_day (mrss_day_t *, va_list); +static mrss_error_t __mrss_get_category (mrss_category_t *, va_list); +static mrss_error_t __mrss_get_tag (mrss_tag_t *, va_list); +static mrss_error_t __mrss_get_attribute (mrss_attribute_t *, va_list); + +static mrss_error_t __mrss_new_subdata_channel (mrss_t *, mrss_element_t, + mrss_generic_t); +static mrss_error_t __mrss_new_subdata_item (mrss_item_t *, mrss_element_t, + mrss_generic_t); +static mrss_error_t __mrss_new_subdata_tag (mrss_tag_t *, mrss_element_t, + mrss_generic_t); + +static mrss_error_t __mrss_remove_subdata_channel (mrss_t *, mrss_generic_t); +static mrss_error_t __mrss_remove_subdata_item (mrss_item_t *, + mrss_generic_t); +static mrss_error_t __mrss_remove_subdata_tag (mrss_tag_t *, mrss_generic_t); + +#define __MRSS_SET_STRING( x ) \ + if(x) free(x); \ + if(value && !(x=strdup(value))) return MRSS_ERR_POSIX; \ + else if(!value) x=NULL; + +#define __MRSS_SET_INTEGER( x ) \ + x=(int)value; + +#define __MRSS_GET_STRING( x ) \ + string = (char **)value; \ + if (!x) *string = NULL; \ + else if(!(*string = strdup(x))) return MRSS_ERR_POSIX; + +#define __MRSS_GET_INTEGER( x ) \ + integer=(int *)value; \ + *integer=x; + +mrss_error_t +mrss_new (mrss_t ** data) +{ + int allocated; + + if (!data) + return MRSS_ERR_DATA; + + if (!*data) + { + if (!(*data = (mrss_t *) malloc (sizeof (mrss_t)))) + return MRSS_ERR_POSIX; + + allocated = 1; + } + else + allocated = 0; + + memset (*data, 0, sizeof (mrss_t)); + (*data)->element = MRSS_ELEMENT_CHANNEL; + (*data)->allocated = allocated; + + return MRSS_OK; +} + +mrss_error_t +mrss_set (mrss_generic_t data, ...) +{ + va_list va; + mrss_error_t err; + mrss_t *tmp; + + if (!data) + return MRSS_ERR_DATA; + + va_start (va, data); + + tmp = (mrss_t *) data; + switch (tmp->element) + { + case MRSS_ELEMENT_CHANNEL: + err = __mrss_set_channel ((mrss_t *) data, va); + break; + + case MRSS_ELEMENT_ITEM: + err = __mrss_set_item ((mrss_item_t *) data, va); + break; + + case MRSS_ELEMENT_SKIPHOURS: + err = __mrss_set_hour ((mrss_hour_t *) data, va); + break; + + case MRSS_ELEMENT_SKIPDAYS: + err = __mrss_set_day ((mrss_day_t *) data, va); + break; + + case MRSS_ELEMENT_CATEGORY: + err = __mrss_set_category ((mrss_category_t *) data, va); + break; + + case MRSS_ELEMENT_TAG: + err = __mrss_set_tag ((mrss_tag_t *) data, va); + break; + + case MRSS_ELEMENT_ATTRIBUTE: + err = __mrss_set_attribute ((mrss_attribute_t *) data, va); + break; + + default: + err = MRSS_ERR_DATA; + break; + } + + va_end (va); + return err; +} + +static mrss_error_t +__mrss_set_channel (mrss_t * data, va_list va) +{ + mrss_flag_t flag; + void *value; + + while ((flag = va_arg (va, mrss_flag_t))) + { + value = va_arg (va, void *); + + switch (flag) + { + case MRSS_FLAG_VERSION: + if ((mrss_version_t) value != MRSS_VERSION_0_91 && + (mrss_version_t) value != MRSS_VERSION_0_92 && + (mrss_version_t) value != MRSS_VERSION_2_0) + return MRSS_ERR_DATA; + + data->version = (mrss_version_t) value; + break; + + case MRSS_FLAG_TITLE: + __MRSS_SET_STRING (data->title); + break; + + case MRSS_FLAG_TITLE_TYPE: + __MRSS_SET_STRING (data->title_type); + break; + + case MRSS_FLAG_DESCRIPTION: + __MRSS_SET_STRING (data->description); + break; + + case MRSS_FLAG_DESCRIPTION_TYPE: + __MRSS_SET_STRING (data->description_type); + break; + + case MRSS_FLAG_LINK: + __MRSS_SET_STRING (data->link); + break; + + case MRSS_FLAG_ID: + __MRSS_SET_STRING (data->id); + break; + + case MRSS_FLAG_LANGUAGE: + __MRSS_SET_STRING (data->language); + break; + + case MRSS_FLAG_RATING: + __MRSS_SET_STRING (data->rating); + break; + + case MRSS_FLAG_COPYRIGHT: + __MRSS_SET_STRING (data->copyright); + break; + + case MRSS_FLAG_COPYRIGHT_TYPE: + __MRSS_SET_STRING (data->copyright_type); + break; + + case MRSS_FLAG_PUBDATE: + __MRSS_SET_STRING (data->pubDate); + break; + + case MRSS_FLAG_LASTBUILDDATE: + __MRSS_SET_STRING (data->lastBuildDate); + break; + + case MRSS_FLAG_DOCS: + __MRSS_SET_STRING (data->docs); + break; + + case MRSS_FLAG_MANAGINGEDITOR: + __MRSS_SET_STRING (data->managingeditor); + break; + + case MRSS_FLAG_MANAGINGEDITOR_EMAIL: + __MRSS_SET_STRING (data->managingeditor_email); + break; + + case MRSS_FLAG_MANAGINGEDITOR_URI: + __MRSS_SET_STRING (data->managingeditor_uri); + break; + + case MRSS_FLAG_WEBMASTER: + __MRSS_SET_STRING (data->webMaster); + break; + + case MRSS_FLAG_TTL: + __MRSS_SET_INTEGER (data->ttl); + break; + + case MRSS_FLAG_ABOUT: + __MRSS_SET_STRING (data->about); + break; + + case MRSS_FLAG_CONTRIBUTOR: + __MRSS_SET_STRING (data->contributor); + break; + + case MRSS_FLAG_CONTRIBUTOR_EMAIL: + __MRSS_SET_STRING (data->contributor_email); + break; + + case MRSS_FLAG_CONTRIBUTOR_URI: + __MRSS_SET_STRING (data->contributor_uri); + break; + + case MRSS_FLAG_GENERATOR: + __MRSS_SET_STRING (data->generator); + break; + + case MRSS_FLAG_GENERATOR_URI: + __MRSS_SET_STRING (data->generator_uri); + break; + + case MRSS_FLAG_GENERATOR_VERSION: + __MRSS_SET_STRING (data->generator_version); + break; + + case MRSS_FLAG_IMAGE_TITLE: + __MRSS_SET_STRING (data->image_title); + break; + + case MRSS_FLAG_IMAGE_URL: + __MRSS_SET_STRING (data->image_url); + break; + + case MRSS_FLAG_IMAGE_LOGO: + __MRSS_SET_STRING (data->image_logo); + break; + + case MRSS_FLAG_IMAGE_LINK: + __MRSS_SET_STRING (data->image_link); + break; + + case MRSS_FLAG_IMAGE_WIDTH: + __MRSS_SET_INTEGER (data->image_width); + break; + + case MRSS_FLAG_IMAGE_HEIGHT: + __MRSS_SET_INTEGER (data->image_height); + break; + + case MRSS_FLAG_IMAGE_DESCRIPTION: + __MRSS_SET_STRING (data->image_description); + break; + + case MRSS_FLAG_TEXTINPUT_TITLE: + __MRSS_SET_STRING (data->textinput_title); + break; + + case MRSS_FLAG_TEXTINPUT_DESCRIPTION: + __MRSS_SET_STRING (data->textinput_description); + break; + + case MRSS_FLAG_TEXTINPUT_NAME: + __MRSS_SET_STRING (data->textinput_name); + break; + + case MRSS_FLAG_TEXTINPUT_LINK: + __MRSS_SET_STRING (data->textinput_link); + break; + + case MRSS_FLAG_CLOUD: + __MRSS_SET_STRING (data->cloud); + break; + + case MRSS_FLAG_CLOUD_DOMAIN: + __MRSS_SET_STRING (data->cloud_domain); + break; + + case MRSS_FLAG_CLOUD_PORT: + __MRSS_SET_INTEGER (data->cloud_port); + break; + + case MRSS_FLAG_CLOUD_PATH: + __MRSS_SET_STRING (data->cloud_path); + break; + + case MRSS_FLAG_CLOUD_REGISTERPROCEDURE: + __MRSS_SET_STRING (data->cloud_registerProcedure); + break; + + case MRSS_FLAG_CLOUD_PROTOCOL: + __MRSS_SET_STRING (data->cloud_protocol); + break; + + default: + return MRSS_ERR_DATA; + } + } + + return MRSS_OK; +} + +static mrss_error_t +__mrss_set_hour (mrss_hour_t * data, va_list va) +{ + mrss_flag_t flag; + void *value; + + while ((flag = va_arg (va, mrss_flag_t))) + { + value = va_arg (va, void *); + + switch (flag) + { + case MRSS_FLAG_HOUR: + __MRSS_SET_STRING (data->hour); + break; + + default: + return MRSS_ERR_DATA; + } + } + + return MRSS_OK; +} + +static mrss_error_t +__mrss_set_day (mrss_day_t * data, va_list va) +{ + mrss_flag_t flag; + void *value; + + while ((flag = va_arg (va, mrss_flag_t))) + { + value = va_arg (va, void *); + + switch (flag) + { + case MRSS_FLAG_DAY: + __MRSS_SET_STRING (data->day); + break; + + default: + return MRSS_ERR_DATA; + } + } + + return MRSS_OK; +} + +static mrss_error_t +__mrss_set_category (mrss_category_t * data, va_list va) +{ + mrss_flag_t flag; + void *value; + + while ((flag = va_arg (va, mrss_flag_t))) + { + value = va_arg (va, void *); + + switch (flag) + { + case MRSS_FLAG_CATEGORY: + __MRSS_SET_STRING (data->category); + break; + + case MRSS_FLAG_CATEGORY_DOMAIN: + __MRSS_SET_STRING (data->domain); + break; + + case MRSS_FLAG_CATEGORY_LABEL: + __MRSS_SET_STRING (data->label); + break; + + default: + return MRSS_ERR_DATA; + } + } + + return MRSS_OK; +} + +static mrss_error_t +__mrss_set_tag (mrss_tag_t * data, va_list va) +{ + mrss_flag_t flag; + void *value; + + while ((flag = va_arg (va, mrss_flag_t))) + { + value = va_arg (va, void *); + + switch (flag) + { + case MRSS_FLAG_TAG_NAME: + __MRSS_SET_STRING (data->name); + break; + + case MRSS_FLAG_TAG_VALUE: + __MRSS_SET_STRING (data->value); + break; + + case MRSS_FLAG_TAG_NS: + __MRSS_SET_STRING (data->ns); + break; + + default: + return MRSS_ERR_DATA; + } + } + + return MRSS_OK; +} + +static mrss_error_t +__mrss_set_attribute (mrss_attribute_t * data, va_list va) +{ + mrss_flag_t flag; + void *value; + + while ((flag = va_arg (va, mrss_flag_t))) + { + value = va_arg (va, void *); + + switch (flag) + { + case MRSS_FLAG_ATTRIBUTE_NAME: + __MRSS_SET_STRING (data->name); + break; + + case MRSS_FLAG_ATTRIBUTE_VALUE: + __MRSS_SET_STRING (data->value); + break; + + case MRSS_FLAG_ATTRIBUTE_NS: + __MRSS_SET_STRING (data->ns); + break; + + default: + return MRSS_ERR_DATA; + } + } + + return MRSS_OK; +} + +static mrss_error_t +__mrss_set_item (mrss_item_t * data, va_list va) +{ + mrss_flag_t flag; + void *value; + + while ((flag = va_arg (va, mrss_flag_t))) + { + value = va_arg (va, void *); + + switch (flag) + { + case MRSS_FLAG_ITEM_TITLE: + __MRSS_SET_STRING (data->title); + break; + + case MRSS_FLAG_ITEM_TITLE_TYPE: + __MRSS_SET_STRING (data->title_type); + break; + + case MRSS_FLAG_ITEM_LINK: + __MRSS_SET_STRING (data->link); + break; + + case MRSS_FLAG_ITEM_DESCRIPTION: + __MRSS_SET_STRING (data->description); + break; + + case MRSS_FLAG_ITEM_DESCRIPTION_TYPE: + __MRSS_SET_STRING (data->description_type); + break; + + case MRSS_FLAG_ITEM_COPYRIGHT: + __MRSS_SET_STRING (data->copyright); + break; + + case MRSS_FLAG_ITEM_COPYRIGHT_TYPE: + __MRSS_SET_STRING (data->copyright_type); + break; + + case MRSS_FLAG_ITEM_AUTHOR: + __MRSS_SET_STRING (data->author); + break; + + case MRSS_FLAG_ITEM_AUTHOR_EMAIL: + __MRSS_SET_STRING (data->author_email); + break; + + case MRSS_FLAG_ITEM_AUTHOR_URI: + __MRSS_SET_STRING (data->author_uri); + break; + + case MRSS_FLAG_ITEM_CONTRIBUTOR: + __MRSS_SET_STRING (data->contributor); + break; + + case MRSS_FLAG_ITEM_CONTRIBUTOR_EMAIL: + __MRSS_SET_STRING (data->contributor_email); + break; + + case MRSS_FLAG_ITEM_CONTRIBUTOR_URI: + __MRSS_SET_STRING (data->contributor_uri); + break; + + case MRSS_FLAG_ITEM_COMMENTS: + __MRSS_SET_STRING (data->comments); + break; + + case MRSS_FLAG_ITEM_PUBDATE: + __MRSS_SET_STRING (data->pubDate); + break; + + case MRSS_FLAG_ITEM_GUID: + __MRSS_SET_STRING (data->guid); + break; + + case MRSS_FLAG_ITEM_GUID_ISPERMALINK: + __MRSS_SET_INTEGER (data->guid_isPermaLink); + break; + + case MRSS_FLAG_ITEM_SOURCE: + __MRSS_SET_STRING (data->source); + break; + + case MRSS_FLAG_ITEM_SOURCE_URL: + __MRSS_SET_STRING (data->source_url); + break; + + case MRSS_FLAG_ITEM_ENCLOSURE: + __MRSS_SET_STRING (data->enclosure); + break; + + case MRSS_FLAG_ITEM_ENCLOSURE_URL: + __MRSS_SET_STRING (data->enclosure_url); + break; + + case MRSS_FLAG_ITEM_ENCLOSURE_LENGTH: + __MRSS_SET_INTEGER (data->enclosure_length); + break; + + case MRSS_FLAG_ITEM_ENCLOSURE_TYPE: + __MRSS_SET_STRING (data->enclosure_type); + break; + + default: + return MRSS_ERR_DATA; + } + } + + return MRSS_OK; +} + +mrss_error_t +mrss_get (mrss_generic_t data, ...) +{ + va_list va; + mrss_error_t err; + mrss_t *tmp; + + if (!data) + return MRSS_ERR_DATA; + + va_start (va, data); + + tmp = (mrss_t *) data; + switch (tmp->element) + { + case MRSS_ELEMENT_CHANNEL: + err = __mrss_get_channel ((mrss_t *) data, va); + break; + + case MRSS_ELEMENT_ITEM: + err = __mrss_get_item ((mrss_item_t *) data, va); + break; + + case MRSS_ELEMENT_SKIPHOURS: + err = __mrss_get_hour ((mrss_hour_t *) data, va); + break; + + case MRSS_ELEMENT_SKIPDAYS: + err = __mrss_get_day ((mrss_day_t *) data, va); + break; + + case MRSS_ELEMENT_CATEGORY: + err = __mrss_get_category ((mrss_category_t *) data, va); + break; + + case MRSS_ELEMENT_TAG: + err = __mrss_get_tag ((mrss_tag_t *) data, va); + break; + + case MRSS_ELEMENT_ATTRIBUTE: + err = __mrss_get_attribute ((mrss_attribute_t *) data, va); + break; + + default: + err = MRSS_ERR_DATA; + break; + } + + va_end (va); + return err; +} + +static mrss_error_t +__mrss_get_channel (mrss_t * data, va_list va) +{ + mrss_flag_t flag; + void *value; + int *integer; + mrss_version_t *version; + char **string; + + while ((flag = va_arg (va, mrss_flag_t))) + { + value = va_arg (va, void *); + + switch (flag) + { + case MRSS_FLAG_VERSION: + version = value; + *version = data->version; + break; + + case MRSS_FLAG_TITLE: + __MRSS_GET_STRING (data->title); + break; + + case MRSS_FLAG_TITLE_TYPE: + __MRSS_GET_STRING (data->title_type); + break; + + case MRSS_FLAG_DESCRIPTION: + __MRSS_GET_STRING (data->description); + break; + + case MRSS_FLAG_DESCRIPTION_TYPE: + __MRSS_GET_STRING (data->description_type); + break; + + case MRSS_FLAG_LINK: + __MRSS_GET_STRING (data->link); + break; + + case MRSS_FLAG_ID: + __MRSS_GET_STRING (data->id); + break; + + case MRSS_FLAG_LANGUAGE: + __MRSS_GET_STRING (data->language); + break; + + case MRSS_FLAG_RATING: + __MRSS_GET_STRING (data->rating); + break; + + case MRSS_FLAG_COPYRIGHT: + __MRSS_GET_STRING (data->copyright); + break; + + case MRSS_FLAG_COPYRIGHT_TYPE: + __MRSS_GET_STRING (data->copyright_type); + break; + + case MRSS_FLAG_PUBDATE: + __MRSS_GET_STRING (data->pubDate); + break; + + case MRSS_FLAG_LASTBUILDDATE: + __MRSS_GET_STRING (data->lastBuildDate); + break; + + case MRSS_FLAG_DOCS: + __MRSS_GET_STRING (data->docs); + break; + + case MRSS_FLAG_MANAGINGEDITOR: + __MRSS_GET_STRING (data->managingeditor); + break; + + case MRSS_FLAG_MANAGINGEDITOR_EMAIL: + __MRSS_GET_STRING (data->managingeditor_email); + break; + + case MRSS_FLAG_MANAGINGEDITOR_URI: + __MRSS_GET_STRING (data->managingeditor_uri); + break; + + case MRSS_FLAG_WEBMASTER: + __MRSS_GET_STRING (data->webMaster); + break; + + case MRSS_FLAG_TTL: + __MRSS_GET_INTEGER (data->ttl); + break; + + case MRSS_FLAG_ABOUT: + __MRSS_GET_STRING (data->about); + break; + + case MRSS_FLAG_CONTRIBUTOR: + __MRSS_GET_STRING (data->contributor); + break; + + case MRSS_FLAG_CONTRIBUTOR_EMAIL: + __MRSS_GET_STRING (data->contributor_email); + break; + + case MRSS_FLAG_CONTRIBUTOR_URI: + __MRSS_GET_STRING (data->contributor_uri); + break; + + case MRSS_FLAG_GENERATOR: + __MRSS_GET_STRING (data->generator); + break; + + case MRSS_FLAG_GENERATOR_URI: + __MRSS_GET_STRING (data->generator_uri); + break; + + case MRSS_FLAG_GENERATOR_VERSION: + __MRSS_GET_STRING (data->generator_version); + break; + + case MRSS_FLAG_IMAGE_TITLE: + __MRSS_GET_STRING (data->image_title); + break; + + case MRSS_FLAG_IMAGE_URL: + __MRSS_GET_STRING (data->image_url); + break; + + case MRSS_FLAG_IMAGE_LOGO: + __MRSS_GET_STRING (data->image_logo); + break; + + case MRSS_FLAG_IMAGE_LINK: + __MRSS_GET_STRING (data->image_link); + break; + + case MRSS_FLAG_IMAGE_WIDTH: + __MRSS_GET_INTEGER (data->image_width); + break; + + case MRSS_FLAG_IMAGE_HEIGHT: + __MRSS_GET_INTEGER (data->image_height); + break; + + case MRSS_FLAG_IMAGE_DESCRIPTION: + __MRSS_GET_STRING (data->image_description); + break; + + case MRSS_FLAG_TEXTINPUT_TITLE: + __MRSS_GET_STRING (data->textinput_title); + break; + + case MRSS_FLAG_TEXTINPUT_DESCRIPTION: + __MRSS_GET_STRING (data->textinput_description); + break; + + case MRSS_FLAG_TEXTINPUT_NAME: + __MRSS_GET_STRING (data->textinput_name); + break; + + case MRSS_FLAG_TEXTINPUT_LINK: + __MRSS_GET_STRING (data->textinput_link); + break; + + case MRSS_FLAG_CLOUD: + __MRSS_GET_STRING (data->cloud); + break; + + case MRSS_FLAG_CLOUD_DOMAIN: + __MRSS_GET_STRING (data->cloud_domain); + break; + + case MRSS_FLAG_CLOUD_PORT: + __MRSS_GET_INTEGER (data->cloud_port); + break; + + case MRSS_FLAG_CLOUD_PATH: + __MRSS_GET_STRING (data->cloud_path); + break; + + case MRSS_FLAG_CLOUD_REGISTERPROCEDURE: + __MRSS_GET_STRING (data->cloud_registerProcedure); + break; + + case MRSS_FLAG_CLOUD_PROTOCOL: + __MRSS_GET_STRING (data->cloud_protocol); + break; + + default: + return MRSS_ERR_DATA; + } + } + + return MRSS_OK; +} + +static mrss_error_t +__mrss_get_hour (mrss_hour_t * data, va_list va) +{ + mrss_flag_t flag; + void *value; + char **string; + + while ((flag = va_arg (va, mrss_flag_t))) + { + value = va_arg (va, void *); + + switch (flag) + { + case MRSS_FLAG_HOUR: + __MRSS_GET_STRING (data->hour); + break; + + default: + return MRSS_ERR_DATA; + } + } + + return MRSS_OK; +} + +static mrss_error_t +__mrss_get_day (mrss_day_t * data, va_list va) +{ + mrss_flag_t flag; + void *value; + char **string; + + while ((flag = va_arg (va, mrss_flag_t))) + { + value = va_arg (va, void *); + + switch (flag) + { + case MRSS_FLAG_DAY: + __MRSS_GET_STRING (data->day); + break; + + default: + return MRSS_ERR_DATA; + } + } + + return MRSS_OK; +} + +static mrss_error_t +__mrss_get_category (mrss_category_t * data, va_list va) +{ + mrss_flag_t flag; + void *value; + char **string; + + while ((flag = va_arg (va, mrss_flag_t))) + { + value = va_arg (va, void *); + + switch (flag) + { + case MRSS_FLAG_CATEGORY: + __MRSS_GET_STRING (data->category); + break; + + case MRSS_FLAG_CATEGORY_DOMAIN: + __MRSS_GET_STRING (data->domain); + break; + + case MRSS_FLAG_CATEGORY_LABEL: + __MRSS_GET_STRING (data->label); + break; + + default: + return MRSS_ERR_DATA; + } + } + + return MRSS_OK; +} + +static mrss_error_t +__mrss_get_tag (mrss_tag_t * data, va_list va) +{ + mrss_flag_t flag; + void *value; + char **string; + + while ((flag = va_arg (va, mrss_flag_t))) + { + value = va_arg (va, void *); + + switch (flag) + { + case MRSS_FLAG_TAG_NAME: + __MRSS_GET_STRING (data->name); + break; + + case MRSS_FLAG_TAG_VALUE: + __MRSS_GET_STRING (data->value); + break; + + case MRSS_FLAG_TAG_NS: + __MRSS_GET_STRING (data->ns); + break; + + default: + return MRSS_ERR_DATA; + } + } + + return MRSS_OK; +} + +static mrss_error_t +__mrss_get_attribute (mrss_attribute_t * data, va_list va) +{ + mrss_flag_t flag; + void *value; + char **string; + + while ((flag = va_arg (va, mrss_flag_t))) + { + value = va_arg (va, void *); + + switch (flag) + { + case MRSS_FLAG_ATTRIBUTE_NAME: + __MRSS_GET_STRING (data->name); + break; + + case MRSS_FLAG_ATTRIBUTE_VALUE: + __MRSS_GET_STRING (data->value); + break; + + case MRSS_FLAG_ATTRIBUTE_NS: + __MRSS_GET_STRING (data->ns); + break; + + default: + return MRSS_ERR_DATA; + } + } + + return MRSS_OK; +} + +static mrss_error_t +__mrss_get_item (mrss_item_t * data, va_list va) +{ + mrss_flag_t flag; + void *value; + char **string; + int *integer; + + while ((flag = va_arg (va, mrss_flag_t))) + { + value = va_arg (va, void *); + + switch (flag) + { + case MRSS_FLAG_ITEM_TITLE: + __MRSS_GET_STRING (data->title); + break; + + case MRSS_FLAG_ITEM_TITLE_TYPE: + __MRSS_GET_STRING (data->title_type); + break; + + case MRSS_FLAG_ITEM_LINK: + __MRSS_GET_STRING (data->link); + break; + + case MRSS_FLAG_ITEM_DESCRIPTION: + __MRSS_GET_STRING (data->description); + break; + + case MRSS_FLAG_ITEM_DESCRIPTION_TYPE: + __MRSS_GET_STRING (data->description_type); + break; + + case MRSS_FLAG_ITEM_COPYRIGHT: + __MRSS_GET_STRING (data->copyright); + break; + + case MRSS_FLAG_ITEM_COPYRIGHT_TYPE: + __MRSS_GET_STRING (data->copyright_type); + break; + + case MRSS_FLAG_ITEM_AUTHOR: + __MRSS_GET_STRING (data->author); + break; + + case MRSS_FLAG_ITEM_AUTHOR_EMAIL: + __MRSS_GET_STRING (data->author_email); + break; + + case MRSS_FLAG_ITEM_AUTHOR_URI: + __MRSS_GET_STRING (data->author_uri); + break; + + case MRSS_FLAG_ITEM_CONTRIBUTOR: + __MRSS_GET_STRING (data->contributor); + break; + + case MRSS_FLAG_ITEM_CONTRIBUTOR_EMAIL: + __MRSS_GET_STRING (data->contributor_email); + break; + + case MRSS_FLAG_ITEM_CONTRIBUTOR_URI: + __MRSS_GET_STRING (data->contributor_uri); + break; + + case MRSS_FLAG_ITEM_COMMENTS: + __MRSS_GET_STRING (data->comments); + break; + + case MRSS_FLAG_ITEM_PUBDATE: + __MRSS_GET_STRING (data->pubDate); + break; + + case MRSS_FLAG_ITEM_GUID: + __MRSS_GET_STRING (data->guid); + break; + + case MRSS_FLAG_ITEM_GUID_ISPERMALINK: + __MRSS_GET_INTEGER (data->guid_isPermaLink); + break; + + case MRSS_FLAG_ITEM_SOURCE: + __MRSS_GET_STRING (data->source); + break; + + case MRSS_FLAG_ITEM_SOURCE_URL: + __MRSS_GET_STRING (data->source_url); + break; + + case MRSS_FLAG_ITEM_ENCLOSURE: + __MRSS_GET_STRING (data->enclosure); + break; + + case MRSS_FLAG_ITEM_ENCLOSURE_URL: + __MRSS_GET_STRING (data->enclosure_url); + break; + + case MRSS_FLAG_ITEM_ENCLOSURE_LENGTH: + __MRSS_GET_INTEGER (data->enclosure_length); + break; + + case MRSS_FLAG_ITEM_ENCLOSURE_TYPE: + __MRSS_GET_STRING (data->enclosure_type); + break; + + default: + return MRSS_ERR_DATA; + } + } + + return MRSS_OK; +} + +mrss_error_t +mrss_new_subdata (mrss_generic_t data, mrss_element_t element, + mrss_generic_t new) +{ + mrss_t *tmp; + + if (!data || !new) + return MRSS_ERR_DATA; + + tmp = (mrss_t *) data; + + switch (tmp->element) + { + case MRSS_ELEMENT_CHANNEL: + return __mrss_new_subdata_channel ((mrss_t *) data, element, new); + + case MRSS_ELEMENT_ITEM: + return __mrss_new_subdata_item ((mrss_item_t *) data, element, new); + + case MRSS_ELEMENT_TAG: + return __mrss_new_subdata_tag ((mrss_tag_t *) data, element, new); + + default: + return MRSS_ERR_DATA; + } +} + +static mrss_error_t +__mrss_new_subdata_channel (mrss_t * mrss, mrss_element_t element, + mrss_generic_t data) +{ + mrss_item_t **item; + mrss_hour_t **hour; + mrss_day_t **day; + mrss_category_t **category; + mrss_tag_t **tag; + int allocated; + + switch (element) + { + case MRSS_ELEMENT_ITEM: + item = (mrss_item_t **) data; + + if (!*item) + { + if (!(*item = (mrss_item_t *) malloc (sizeof (mrss_item_t)))) + return MRSS_ERR_POSIX; + + allocated = 1; + } + else + allocated = 0; + + memset (*item, 0, sizeof (mrss_item_t)); + + (*item)->element = MRSS_ELEMENT_ITEM; + (*item)->allocated = allocated; + (*item)->next = mrss->item; + mrss->item = (*item); + + break; + + case MRSS_ELEMENT_SKIPHOURS: + hour = (mrss_hour_t **) data; + + if (!*hour) + { + if (!(*hour = (mrss_hour_t *) malloc (sizeof (mrss_hour_t)))) + return MRSS_ERR_POSIX; + + allocated = 1; + } + else + allocated = 0; + + memset (*hour, 0, sizeof (mrss_hour_t)); + + (*hour)->element = MRSS_ELEMENT_SKIPHOURS; + (*hour)->allocated = allocated; + (*hour)->next = mrss->skipHours; + mrss->skipHours = (*hour); + + break; + + case MRSS_ELEMENT_SKIPDAYS: + day = (mrss_day_t **) data; + + if (!*day) + { + if (!(*day = (mrss_day_t *) malloc (sizeof (mrss_day_t)))) + return MRSS_ERR_POSIX; + + allocated = 1; + } + else + allocated = 0; + + memset (*day, 0, sizeof (mrss_day_t)); + + (*day)->element = MRSS_ELEMENT_SKIPDAYS; + (*day)->allocated = allocated; + (*day)->next = mrss->skipDays; + mrss->skipDays = (*day); + + break; + + case MRSS_ELEMENT_CATEGORY: + category = (mrss_category_t **) data; + + if (!*category) + { + if (! + (*category = + (mrss_category_t *) malloc (sizeof (mrss_category_t)))) + return MRSS_ERR_POSIX; + allocated = 1; + } + else + allocated = 0; + + memset (*category, 0, sizeof (mrss_category_t)); + + (*category)->element = MRSS_ELEMENT_CATEGORY; + (*category)->allocated = allocated; + (*category)->next = mrss->category; + mrss->category = (*category); + + break; + + case MRSS_ELEMENT_TAG: + tag = (mrss_tag_t **) data; + + if (!*tag) + { + if (!(*tag = (mrss_tag_t *) malloc (sizeof (mrss_tag_t)))) + return MRSS_ERR_POSIX; + allocated = 1; + } + else + allocated = 0; + + memset (*tag, 0, sizeof (mrss_tag_t)); + + (*tag)->element = MRSS_ELEMENT_TAG; + (*tag)->allocated = allocated; + (*tag)->next = mrss->other_tags; + mrss->other_tags = (*tag); + + + break; + + default: + return MRSS_ERR_DATA; + } + + return MRSS_OK; +} + +static mrss_error_t +__mrss_new_subdata_item (mrss_item_t * item, mrss_element_t element, + mrss_generic_t data) +{ + mrss_category_t **category; + mrss_tag_t **tag; + int allocated; + + switch (element) + { + case MRSS_ELEMENT_CATEGORY: + category = (mrss_category_t **) data; + + if (!*category) + { + if (! + (*category = + (mrss_category_t *) malloc (sizeof (mrss_category_t)))) + return MRSS_ERR_POSIX; + + allocated = 1; + } + else + allocated = 0; + + memset (*category, 0, sizeof (mrss_category_t)); + + (*category)->element = MRSS_ELEMENT_CATEGORY; + (*category)->allocated = allocated; + (*category)->next = item->category; + item->category = (*category); + + break; + + case MRSS_ELEMENT_TAG: + tag = (mrss_tag_t **) data; + + if (!*tag) + { + if (!(*tag = (mrss_tag_t *) malloc (sizeof (mrss_tag_t)))) + return MRSS_ERR_POSIX; + + allocated = 1; + } + else + allocated = 0; + + memset (*tag, 0, sizeof (mrss_tag_t)); + + (*tag)->element = MRSS_ELEMENT_TAG; + (*tag)->allocated = allocated; + (*tag)->next = item->other_tags; + item->other_tags = (*tag); + + break; + + default: + return MRSS_ERR_DATA; + } + + return MRSS_OK; +} + +static mrss_error_t +__mrss_new_subdata_tag (mrss_tag_t * tag, mrss_element_t element, + mrss_generic_t data) +{ + mrss_tag_t **new; + mrss_attribute_t **attribute; + int allocated; + + switch (element) + { + case MRSS_ELEMENT_TAG: + new = (mrss_tag_t **) data; + + if (!*new) + { + if (!(*new = (mrss_tag_t *) malloc (sizeof (mrss_tag_t)))) + return MRSS_ERR_POSIX; + + allocated = 1; + } + else + allocated = 0; + + memset (*new, 0, sizeof (mrss_tag_t)); + + (*new)->element = MRSS_ELEMENT_TAG; + (*new)->allocated = allocated; + (*new)->next = tag->children; + tag->children = (*new); + + break; + + case MRSS_ELEMENT_ATTRIBUTE: + attribute = (mrss_attribute_t **) data; + + if (!*attribute) + { + if (! + (*attribute = + (mrss_attribute_t *) malloc (sizeof (mrss_attribute_t)))) + return MRSS_ERR_POSIX; + + allocated = 1; + } + else + allocated = 0; + + memset (*attribute, 0, sizeof (mrss_attribute_t)); + + (*attribute)->element = MRSS_ELEMENT_ATTRIBUTE; + (*attribute)->allocated = allocated; + (*attribute)->next = tag->attributes; + tag->attributes = (*attribute); + + break; + + default: + return MRSS_ERR_DATA; + } + + return MRSS_OK; +} + +mrss_error_t +mrss_remove_subdata (mrss_generic_t data, mrss_generic_t subdata) +{ + mrss_t *tmp; + + if (!data || !subdata) + return MRSS_ERR_DATA; + + tmp = (mrss_t *) data; + + switch (tmp->element) + { + case MRSS_ELEMENT_CHANNEL: + return __mrss_remove_subdata_channel ((mrss_t *) data, subdata); + + case MRSS_ELEMENT_ITEM: + return __mrss_remove_subdata_item ((mrss_item_t *) data, subdata); + + case MRSS_ELEMENT_TAG: + return __mrss_remove_subdata_tag ((mrss_tag_t *) data, subdata); + + default: + return MRSS_ERR_DATA; + } +} + +static mrss_error_t +__mrss_remove_subdata_channel (mrss_t * data, mrss_generic_t subdata) +{ + mrss_hour_t *hour, *hour_tmp, *hour_old; + mrss_day_t *day, *day_tmp, *day_old; + mrss_category_t *category, *category_tmp, *category_old; + mrss_item_t *item, *item_tmp, *item_old; + mrss_tag_t *tag, *tag_tmp, *tag_old; + + int found = 0; + + mrss_t *tmp = (mrss_t *) subdata; + + switch (tmp->element) + { + case MRSS_ELEMENT_ITEM: + item = (mrss_item_t *) subdata; + + item_tmp = data->item; + item_old = NULL; + + while (item_tmp) + { + if (item_tmp == item) + { + found++; + + if (item_old) + item_old->next = item_tmp->next; + else + data->item = item_tmp->next; + + break; + } + + item_old = item_tmp; + item_tmp = item_tmp->next; + } + + if (!found) + return MRSS_ERR_DATA; + + break; + + case MRSS_ELEMENT_SKIPHOURS: + hour = (mrss_hour_t *) subdata; + + hour_tmp = data->skipHours; + hour_old = NULL; + + while (hour_tmp) + { + if (hour_tmp == hour) + { + found++; + + if (hour_old) + hour_old->next = hour_tmp->next; + else + data->skipHours = hour_tmp->next; + + break; + } + + hour_old = hour_tmp; + hour_tmp = hour_tmp->next; + } + + if (!found) + return MRSS_ERR_DATA; + + break; + + case MRSS_ELEMENT_SKIPDAYS: + day = (mrss_day_t *) subdata; + + day_tmp = data->skipDays; + day_old = NULL; + + while (day_tmp) + { + if (day_tmp == day) + { + found++; + + if (day_old) + day_old->next = day_tmp->next; + else + data->skipDays = day_tmp->next; + + break; + } + + day_old = day_tmp; + day_tmp = day_tmp->next; + } + + if (!found) + return MRSS_ERR_DATA; + + break; + + case MRSS_ELEMENT_CATEGORY: + category = (mrss_category_t *) subdata; + + category_tmp = data->category; + category_old = NULL; + + while (category_tmp) + { + if (category_tmp == category) + { + found++; + + if (category_old) + category_old->next = category_tmp->next; + else + data->category = category_tmp->next; + + break; + } + + category_old = category_tmp; + category_tmp = category_tmp->next; + } + + if (!found) + return MRSS_ERR_DATA; + + break; + + case MRSS_ELEMENT_TAG: + tag = (mrss_tag_t *) subdata; + + tag_tmp = data->other_tags; + tag_old = NULL; + + while (tag_tmp) + { + if (tag_tmp == tag) + { + found++; + + if (tag_old) + tag_old->next = tag_tmp->next; + else + data->other_tags = tag_tmp->next; + + break; + } + + tag_old = tag_tmp; + tag_tmp = tag_tmp->next; + } + + if (!found) + return MRSS_ERR_DATA; + + break; + + default: + return MRSS_ERR_DATA; + } + + return MRSS_OK; +} + +static mrss_error_t +__mrss_remove_subdata_item (mrss_item_t * data, mrss_generic_t subdata) +{ + mrss_category_t *category, *category_tmp, *category_old; + mrss_tag_t *tag, *tag_tmp, *tag_old; + + int found = 0; + + mrss_t *tmp = (mrss_t *) subdata; + + switch (tmp->element) + { + case MRSS_ELEMENT_CATEGORY: + category = (mrss_category_t *) subdata; + + category_tmp = data->category; + category_old = NULL; + + while (category_tmp) + { + if (category_tmp == category) + { + found++; + + if (category_old) + category_old->next = category_tmp->next; + else + data->category = category_tmp->next; + + break; + } + + category_old = category_tmp; + category_tmp = category_tmp->next; + } + + if (!found) + return MRSS_ERR_DATA; + + break; + + case MRSS_ELEMENT_TAG: + tag = (mrss_tag_t *) subdata; + + tag_tmp = data->other_tags; + tag_old = NULL; + + while (tag_tmp) + { + if (tag_tmp == tag) + { + found++; + + if (tag_old) + tag_old->next = tag_tmp->next; + else + data->other_tags = tag_tmp->next; + + break; + } + + tag_old = tag_tmp; + tag_tmp = tag_tmp->next; + } + + if (!found) + return MRSS_ERR_DATA; + + break; + + default: + return MRSS_ERR_DATA; + } + + return MRSS_OK; +} + +static mrss_error_t +__mrss_remove_subdata_tag (mrss_tag_t * data, mrss_generic_t subdata) +{ + mrss_attribute_t *attribute, *attribute_tmp, *attribute_old; + mrss_tag_t *tag, *tag_tmp, *tag_old; + + int found = 0; + + mrss_t *tmp = (mrss_t *) subdata; + + switch (tmp->element) + { + case MRSS_ELEMENT_TAG: + tag = (mrss_tag_t *) subdata; + + tag_tmp = data->children; + tag_old = NULL; + + while (tag_tmp) + { + if (tag_tmp == tag) + { + found++; + + if (tag_old) + tag_old->next = tag_tmp->next; + else + data->children = tag_tmp->next; + + break; + } + + tag_old = tag_tmp; + tag_tmp = tag_tmp->next; + } + + if (!found) + return MRSS_ERR_DATA; + + break; + + case MRSS_ELEMENT_ATTRIBUTE: + attribute = (mrss_attribute_t *) subdata; + + attribute_tmp = data->attributes; + attribute_old = NULL; + + while (attribute_tmp) + { + if (attribute_tmp == attribute) + { + found++; + + if (attribute_old) + attribute_old->next = attribute_tmp->next; + else + data->attributes = attribute_tmp->next; + + break; + } + + attribute_old = attribute_tmp; + attribute_tmp = attribute_tmp->next; + } + + if (!found) + return MRSS_ERR_DATA; + + break; + + default: + return MRSS_ERR_DATA; + } + + return MRSS_OK; +} + +/* EOF */ diff --git a/plugins/backend/decsync/libmrss/mrss_free.c b/plugins/backend/decsync/libmrss/mrss_free.c new file mode 100644 index 000000000..e11c4c9bc --- /dev/null +++ b/plugins/backend/decsync/libmrss/mrss_free.c @@ -0,0 +1,464 @@ +/* mRss - Copyright (C) 2005-2007 bakunin - Andrea Marchesini + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "mrss.h" +#include "mrss_internal.h" + +static void __mrss_free_channel (mrss_t * mrss); +static void __mrss_free_category (mrss_category_t * category); +static void __mrss_free_hour (mrss_hour_t * hour); +static void __mrss_free_day (mrss_day_t * day); +static void __mrss_free_item (mrss_item_t * item); +static void __mrss_free_tag (mrss_tag_t * tag); +static void __mrss_free_attribute (mrss_attribute_t * attribute); + +static void +__mrss_free_channel (mrss_t * mrss) +{ + mrss_hour_t *hour; + mrss_day_t *day; + mrss_category_t *category; + mrss_item_t *item; + mrss_tag_t *tag; + void *old; + + if (!mrss) + return; + + if (mrss->file) + free (mrss->file); + + if (mrss->encoding) + free (mrss->encoding); + + if (mrss->title) + free (mrss->title); + + if (mrss->title_type) + free (mrss->title_type); + + if (mrss->description) + free (mrss->description); + + if (mrss->description_type) + free (mrss->description_type); + + if (mrss->link) + free (mrss->link); + + if (mrss->id) + free (mrss->id); + + if (mrss->language) + free (mrss->language); + + if (mrss->rating) + free (mrss->rating); + + if (mrss->copyright) + free (mrss->copyright); + + if (mrss->copyright_type) + free (mrss->copyright_type); + + if (mrss->pubDate) + free (mrss->pubDate); + + if (mrss->lastBuildDate) + free (mrss->lastBuildDate); + + if (mrss->docs) + free (mrss->docs); + + if (mrss->managingeditor) + free (mrss->managingeditor); + + if (mrss->managingeditor_email) + free (mrss->managingeditor_email); + + if (mrss->managingeditor_uri) + free (mrss->managingeditor_uri); + + if (mrss->webMaster) + free (mrss->webMaster); + + if (mrss->about) + free (mrss->about); + + if (mrss->contributor) + free (mrss->contributor); + + if (mrss->contributor_email) + free (mrss->contributor_email); + + if (mrss->contributor_uri) + free (mrss->contributor_uri); + + if (mrss->generator) + free (mrss->generator); + + if (mrss->generator_uri) + free (mrss->generator_uri); + + if (mrss->generator_version) + free (mrss->generator_version); + + if (mrss->image_title) + free (mrss->image_title); + + if (mrss->image_url) + free (mrss->image_url); + + if (mrss->image_logo) + free (mrss->image_logo); + + if (mrss->image_link) + free (mrss->image_link); + + if (mrss->image_description) + free (mrss->image_description); + + if (mrss->textinput_title) + free (mrss->textinput_title); + + if (mrss->textinput_description) + free (mrss->textinput_description); + + if (mrss->textinput_name) + free (mrss->textinput_name); + + if (mrss->textinput_link) + free (mrss->textinput_link); + + if (mrss->cloud) + free (mrss->cloud); + + if (mrss->cloud_domain) + free (mrss->cloud_domain); + + if (mrss->cloud_path) + free (mrss->cloud_path); + + if (mrss->cloud_registerProcedure) + free (mrss->cloud_registerProcedure); + + if (mrss->cloud_protocol) + free (mrss->cloud_protocol); + + category = mrss->category; + while (category) + { + old = category; + category = category->next; + + __mrss_free_category ((mrss_category_t *) old); + } + + tag = mrss->other_tags; + while (tag) + { + old = tag; + tag = tag->next; + + __mrss_free_tag ((mrss_tag_t *) old); + } + + hour = mrss->skipHours; + while (hour) + { + old = hour; + hour = hour->next; + + __mrss_free_hour ((mrss_hour_t *) old); + } + + day = mrss->skipDays; + while (day) + { + old = day; + day = day->next; + + __mrss_free_day ((mrss_day_t *) old); + } + + item = mrss->item; + while (item) + { + old = item; + item = item->next; + + __mrss_free_item ((mrss_item_t *) old); + } + + if (mrss->c_locale) + freelocale (mrss->c_locale); + + if (mrss->allocated) + free (mrss); +} + +static void +__mrss_free_tag (mrss_tag_t * tag) +{ + mrss_attribute_t *attribute; + mrss_tag_t *child; + void *old; + + if (!tag) + return; + + if (tag->name) + free (tag->name); + + if (tag->value) + free (tag->value); + + if (tag->ns) + free (tag->ns); + + attribute = tag->attributes; + while (attribute) + { + old = attribute; + attribute = attribute->next; + + __mrss_free_attribute ((mrss_attribute_t *) old); + } + + child = tag->children; + while (child) + { + old = child; + child = child->next; + + __mrss_free_tag ((mrss_tag_t *) old); + } + + if (tag->allocated) + free (tag); +} + +static void +__mrss_free_attribute (mrss_attribute_t * attribute) +{ + if (!attribute) + return; + + if (attribute->name) + free (attribute->name); + + if (attribute->value) + free (attribute->value); + + if (attribute->ns) + free (attribute->ns); + + if (attribute->allocated) + free (attribute); +} + +static void +__mrss_free_category (mrss_category_t * category) +{ + if (!category) + return; + + if (category->category) + free (category->category); + + if (category->domain) + free (category->domain); + + if (category->label) + free (category->label); + + if (category->allocated) + free (category); +} + +static void +__mrss_free_hour (mrss_hour_t * hour) +{ + if (!hour) + return; + + if (hour->hour) + free (hour->hour); + + if (hour->allocated) + free (hour); +} + +static void +__mrss_free_day (mrss_day_t * day) +{ + if (!day) + return; + + if (day->day) + free (day->day); + + if (day->allocated) + free (day); +} + +static void +__mrss_free_item (mrss_item_t * item) +{ + mrss_category_t *category; + mrss_tag_t *tag; + void *old; + + if (!item) + return; + + if (item->title) + free (item->title); + + if (item->title_type) + free (item->title_type); + + if (item->link) + free (item->link); + + if (item->description) + free (item->description); + + if (item->description_type) + free (item->description_type); + + if (item->copyright) + free (item->copyright); + + if (item->copyright_type) + free (item->copyright_type); + + if (item->author) + free (item->author); + + if (item->author_email) + free (item->author_email); + + if (item->author_uri) + free (item->author_uri); + + if (item->contributor) + free (item->contributor); + + if (item->contributor_email) + free (item->contributor_email); + + if (item->contributor_uri) + free (item->contributor_uri); + + if (item->comments) + free (item->comments); + + if (item->pubDate) + free (item->pubDate); + + if (item->guid) + free (item->guid); + + if (item->source) + free (item->source); + + if (item->source_url) + free (item->source_url); + + if (item->enclosure) + free (item->enclosure); + + if (item->enclosure_url) + free (item->enclosure_url); + + if (item->enclosure_type) + free (item->enclosure_type); + + category = item->category; + while (category) + { + old = category; + category = category->next; + + __mrss_free_category ((mrss_category_t *) old); + } + + tag = item->other_tags; + while (tag) + { + old = tag; + tag = tag->next; + + __mrss_free_tag ((mrss_tag_t *) old); + } + + if (item->allocated) + free (item); +} + +/*************************** EXTERNAL FUNCTION ******************************/ + +mrss_error_t +mrss_free (mrss_generic_t element) +{ + mrss_t *tmp; + + tmp = (mrss_t *) element; + + if (!tmp) + return MRSS_OK; + + switch (tmp->element) + { + case MRSS_ELEMENT_CHANNEL: + __mrss_free_channel ((mrss_t *) element); + break; + + case MRSS_ELEMENT_ITEM: + __mrss_free_item ((mrss_item_t *) element); + break; + + case MRSS_ELEMENT_SKIPHOURS: + __mrss_free_hour ((mrss_hour_t *) element); + break; + + case MRSS_ELEMENT_SKIPDAYS: + __mrss_free_day ((mrss_day_t *) element); + break; + + case MRSS_ELEMENT_CATEGORY: + __mrss_free_category ((mrss_category_t *) element); + break; + + case MRSS_ELEMENT_TAG: + __mrss_free_tag ((mrss_tag_t *) element); + break; + + case MRSS_ELEMENT_ATTRIBUTE: + __mrss_free_attribute ((mrss_attribute_t *) element); + break; + + default: + return MRSS_ERR_DATA; + } + + return MRSS_OK; +} + +/* EOF */ diff --git a/plugins/backend/decsync/libmrss/mrss_generic.c b/plugins/backend/decsync/libmrss/mrss_generic.c new file mode 100644 index 000000000..ec4e458d2 --- /dev/null +++ b/plugins/backend/decsync/libmrss/mrss_generic.c @@ -0,0 +1,146 @@ +/* mRss - Copyright (C) 2005-2007 bakunin - Andrea Marchesini + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "mrss.h" +#include "mrss_internal.h" + +char * +mrss_strerror (mrss_error_t err) +{ + switch (err) + { + case MRSS_OK: + return "Success"; + + case MRSS_ERR_PARSER: + return "Parser error"; + + case MRSS_ERR_DOWNLOAD: + return "Download error"; + + case MRSS_ERR_VERSION: + return "Version error"; + + case MRSS_ERR_DATA: + return "No correct paramenter in the function"; + + default: + return strerror (errno); + } +} + +char * +mrss_curl_strerror (CURLcode err) +{ + return (char *) curl_easy_strerror (err); +} + +mrss_error_t +mrss_element (mrss_generic_t element, mrss_element_t * ret) +{ + mrss_t *tmp; + + if (!element || !ret) + return MRSS_ERR_DATA; + + tmp = (mrss_t *) element; + *ret = tmp->element; + return MRSS_OK; +} + +static size_t +__mrss_get_last_modified_header (void *ptr, size_t size, size_t nmemb, + time_t * timing) +{ + char *header = (char *) ptr; + + if (!strncmp ("Last-Modified:", header, 14)) + *timing = curl_getdate (header + 14, NULL); + + return size * nmemb; +} + +mrss_error_t +mrss_get_last_modified (char *urlstring, time_t * lastmodified) +{ + return mrss_get_last_modified_with_options (urlstring, lastmodified, NULL); +} + +mrss_error_t +mrss_get_last_modified_with_options (char *urlstring, time_t * lastmodified, + mrss_options_t * options) +{ + CURL *curl; + + if (!urlstring || !lastmodified) + return MRSS_ERR_DATA; + + *lastmodified = 0; + + curl_global_init (CURL_GLOBAL_DEFAULT); + if (!(curl = curl_easy_init ())) + return MRSS_ERR_POSIX; + + curl_easy_setopt (curl, CURLOPT_URL, urlstring); + curl_easy_setopt (curl, CURLOPT_HEADERFUNCTION, + __mrss_get_last_modified_header); + curl_easy_setopt (curl, CURLOPT_HEADERDATA, lastmodified); + curl_easy_setopt (curl, CURLOPT_NOBODY, 1); + curl_easy_setopt (curl, CURLOPT_FOLLOWLOCATION, 1); + curl_easy_setopt (curl, CURLOPT_NOSIGNAL, 1); + + if (options) + { + if (options->timeout > 0) + curl_easy_setopt (curl, CURLOPT_TIMEOUT, options->timeout); + else if (options->timeout < 0) + curl_easy_setopt (curl, CURLOPT_TIMEOUT, 10); + + if (options->certfile) + curl_easy_setopt (curl, CURLOPT_SSLCERT, options->certfile); + + if (options->password) + curl_easy_setopt (curl, CURLOPT_SSLCERTPASSWD, options->password); + + if (options->cacert) + curl_easy_setopt (curl, CURLOPT_CAINFO, options->cacert); + + if (options->proxy) + { + curl_easy_setopt (curl, CURLOPT_PROXY, options->proxy); + + if (options->proxy_authentication) + curl_easy_setopt (curl, CURLOPT_PROXYUSERPWD, + options->proxy_authentication); + } + + curl_easy_setopt (curl, CURLOPT_SSL_VERIFYPEER, options->verifypeer); + } + + if (curl_easy_perform (curl)) + { + curl_easy_cleanup (curl); + return MRSS_ERR_POSIX; + } + + curl_easy_cleanup (curl); + + return MRSS_OK; +} + +/* EOF */ diff --git a/plugins/backend/decsync/libmrss/mrss_internal.h b/plugins/backend/decsync/libmrss/mrss_internal.h new file mode 100644 index 000000000..0b496fdf8 --- /dev/null +++ b/plugins/backend/decsync/libmrss/mrss_internal.h @@ -0,0 +1,35 @@ +/* mRss - Copyright (C) 2005-2007 bakunin - Andrea Marchesini + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __M_RSS_INTERNAL_H__ +#define __M_RSS_INTERNAL_H__ + +#include +#include +#include +#include +#include +#include +#include + +char * __mrss_download_file (nxml_t *, char *, size_t *, mrss_error_t *, CURLcode *code); + +#endif + +/* EOF */ + diff --git a/plugins/backend/decsync/libmrss/mrss_options.c b/plugins/backend/decsync/libmrss/mrss_options.c new file mode 100644 index 000000000..bd02c343e --- /dev/null +++ b/plugins/backend/decsync/libmrss/mrss_options.c @@ -0,0 +1,76 @@ +/* mRss - Copyright (C) 2005-2007 bakunin - Andrea Marchesini + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "mrss.h" +#include "mrss_internal.h" + +mrss_options_t * +mrss_options_new (int timeout, char *proxy, char *proxy_authentication, + char *certfile, char *password, char *cacert, + int verifypeer, char *authentication, char *user_agent) +{ + mrss_options_t *options; + + if (!(options = (mrss_options_t *) malloc (sizeof (mrss_options_t)))) + return NULL; + + options->timeout = timeout; + options->proxy = proxy ? strdup (proxy) : NULL; + options->proxy_authentication = + proxy_authentication ? strdup (proxy_authentication) : NULL; + options->certfile = certfile ? strdup (certfile) : NULL; + options->password = password ? strdup (password) : NULL; + options->cacert = cacert ? strdup (cacert) : NULL; + options->authentication = authentication ? strdup (authentication) : NULL; + options->user_agent = user_agent ? strdup (user_agent) : NULL; + options->verifypeer = verifypeer; + + return options; +} + +void +mrss_options_free (mrss_options_t * options) +{ + if (!options) + return; + + if (options->proxy) + free (options->proxy); + + if (options->proxy_authentication) + free (options->proxy_authentication); + + if (options->certfile) + free (options->certfile); + + if (options->password) + free (options->password); + + if (options->cacert) + free (options->cacert); + + if (options->authentication) + free (options->authentication); + + if (options->user_agent) + free (options->user_agent); + + free (options); +} + +/* EOF */ diff --git a/plugins/backend/decsync/libmrss/mrss_parser.c b/plugins/backend/decsync/libmrss/mrss_parser.c new file mode 100644 index 000000000..200cd8ddf --- /dev/null +++ b/plugins/backend/decsync/libmrss/mrss_parser.c @@ -0,0 +1,1376 @@ +/* mRss - Copyright (C) 2005-2007 bakunin - Andrea Marchesini + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "mrss.h" +#include "mrss_internal.h" + +static void +__mrss_parse_tag_insert (mrss_tag_t ** where, mrss_tag_t * what) +{ + if (!*where) + *where = what; + else + { + mrss_tag_t *tag = *where; + + while (tag->next) + tag = tag->next; + + tag->next = what; + } +} + +static mrss_tag_t * +__mrss_parse_tag (nxml_t * doc, nxml_data_t * cur) +{ + mrss_tag_t *tag; + mrss_attribute_t *attribute; + nxml_attr_t *nxml_attr; + + if (!(tag = (mrss_tag_t *) calloc (1, sizeof (mrss_tag_t)))) + return NULL; + + tag->element = MRSS_ELEMENT_TAG; + tag->allocated = 1; + + if (!(tag->name = strdup (cur->value))) + { + mrss_free (tag); + return NULL; + } + + if (cur->ns && cur->ns->ns && !(tag->ns = strdup (cur->ns->ns))) + { + mrss_free (tag); + return NULL; + } + + for (nxml_attr = cur->attributes; nxml_attr; nxml_attr = nxml_attr->next) + { + + if (! + (attribute = + (mrss_attribute_t *) calloc (1, sizeof (mrss_attribute_t)))) + return NULL; + + attribute->element = MRSS_ELEMENT_ATTRIBUTE; + attribute->allocated = 1; + + if (!(attribute->name = strdup (nxml_attr->name))) + { + mrss_free (tag); + return NULL; + } + + if (!(attribute->value = strdup (nxml_attr->value))) + { + mrss_free (tag); + return NULL; + } + + if (nxml_attr->ns && nxml_attr->ns->ns + && !(attribute->ns = strdup (nxml_attr->ns->ns))) + { + mrss_free (tag); + return NULL; + } + + if (!tag->attributes) + tag->attributes = attribute; + else + { + mrss_attribute_t *tmp = tag->attributes; + + while (tmp->next) + tmp = tmp->next; + + tmp->next = attribute; + } + + } + + for (cur = cur->children; cur; cur = cur->next) + { + if (cur->type == NXML_TYPE_TEXT) + { + if (!tag->value && !(tag->value = strdup (cur->value))) + { + mrss_free (tag); + return NULL; + } + } + else if (cur->type == NXML_TYPE_ELEMENT) + { + mrss_tag_t *child = __mrss_parse_tag (doc, cur); + + if (child) + __mrss_parse_tag_insert (&tag->children, child); + } + } + + return tag; +} + +static void +__mrss_parser_atom_string (nxml_t * doc, nxml_data_t * cur, char **what, + char **type) +{ + char *c; + + if (!(c = nxmle_find_attribute (cur, "type", NULL)) || !strcmp (c, "text")) + { + *what = nxmle_get_string (cur, NULL); + *type = c; + return; + } + + if (!strcmp (c, "html") || !strcmp (c, "xhtml")) + { + nxml_data_t *ncur; + char *total, *c1; + nxml_t *new; + int size; + + total = NULL; + size = 0; + + c1 = nxmle_get_string (cur, NULL); + + if (c1 && *c1) + { + total = strdup (c1); + size = strlen (total); + } + + else + { + while ((ncur = cur->children)) + { + char *buffer = NULL, *p; + char *tmp; + int len; + + if (nxml_remove (doc, cur, ncur) != NXML_OK) + continue; + + if (nxml_new (&new) != NXML_OK) + { + nxml_free_data (ncur); + continue; + } + + if (nxml_add (new, NULL, &ncur) != NXML_OK) + { + nxml_free_data (ncur); + nxml_free (new); + continue; + } + + if (!(buffer = nxmle_write_buffer (new, NULL))) + { + nxml_free (new); + continue; + } + + nxml_free (new); + + if (strncmp (buffer, "') + p++; + + if (!*p) + { + free (buffer); + continue; + } + + p++; + while (*p && (*p == ' ' || *p == '\t' || *p == '\n')) + p++; + + len = strlen (p); + + if (!(tmp = realloc (total, size + len + 1))) + { + free (buffer); + + if (total) + { + free (total); + total = NULL; + } + + break; + } + + total = tmp; + strcpy (total + size, p); + size += len; + + free (buffer); + } + } + + *what = total; + *type = c; + free(c1); + return; + } + + free (c); + *what = nxmle_get_string (cur, NULL); +} + +static char * +__mrss_atom_prepare_date (mrss_t * data, char *datestr) +{ + struct tm stm; + + if (!datestr) + return NULL; + + memset (&stm, 0, sizeof (stm)); + + /* format: 2007-01-17T07:45:50Z */ + if (sscanf + (datestr, "%04d-%02d-%02dT%02d:%02d:%02dZ", &stm.tm_year, + &stm.tm_mon, &stm.tm_mday, &stm.tm_hour, &stm.tm_min, + &stm.tm_sec) == 6) + { + char datebuf[256]; + stm.tm_year -= 1900; + stm.tm_mon -= 1; + + if (!data->c_locale + && !(data->c_locale = newlocale (LC_ALL_MASK, "C", NULL))) + return NULL; + + strftime_l (datebuf, sizeof (datebuf), "%a, %d %b %Y %H:%M:%S %z", &stm, + data->c_locale); + + return strdup (datebuf); + } + + return NULL; +} + +static void +__mrss_parser_atom_category (nxml_data_t * cur, mrss_category_t ** category) +{ + char *c; + mrss_category_t *cat; + + if (!(cat = calloc (1, sizeof (mrss_category_t)))) + return; + + if (!(c = nxmle_find_attribute (cur, "term", NULL))) + { + free (cat); + return; + } + + cat->element = MRSS_ELEMENT_CATEGORY; + cat->allocated = 1; + cat->category = c; + + if ((c = nxmle_find_attribute (cur, "scheme", NULL))) + cat->domain = c; + + if ((c = nxmle_find_attribute (cur, "label", NULL))) + cat->label = c; + + if (!*category) + *category = cat; + + else + { + mrss_category_t *tmp; + tmp = *category; + + while (tmp->next) + tmp = tmp->next; + + tmp->next = cat; + } +} + +static void +__mrss_parser_atom_author (nxml_data_t * cur, char **name, char **email, + char **uri) +{ + for (cur = cur->children; cur; cur = cur->next) + { + if (!*name && !strcmp (cur->value, "name")) + *name = nxmle_get_string (cur, NULL); + + else if (!*email && !strcmp (cur->value, "email")) + *email = nxmle_get_string (cur, NULL); + + else if (!*uri && !strcmp (cur->value, "uri")) + *uri = nxmle_get_string (cur, NULL); + } +} + +static void +__mrss_parser_atom_entry (nxml_t * doc, nxml_data_t * cur, mrss_t * data) +{ + char *c; + mrss_item_t *item; + + if (!(item = malloc (sizeof (mrss_item_t)))) + return; + + memset (item, 0, sizeof (mrss_item_t)); + item->element = MRSS_ELEMENT_ITEM; + item->allocated = 1; + + for (cur = cur->children; cur; cur = cur->next) + { + if (cur->type == NXML_TYPE_ELEMENT) + { + /* title -> title */ + if (!item->title && !strcmp (cur->value, "title")) + __mrss_parser_atom_string (doc, cur, &item->title, + &item->title_type); + + /* link href -> link */ + else if (!item->link && !strcmp (cur->value, "link") + && (c = nxmle_find_attribute (cur, "href", NULL))) + item->link = c; + + /* content -> description */ + /* Note: We intentionally override summary with content */ + else if (!strcmp (cur->value, "content")) + { + if (item->description) + { + free(item->description); + item->description = NULL; + } + __mrss_parser_atom_string (doc, cur, &item->description, + &item->description_type); + } + + /* summary -> description */ + else if (!item->description && !strcmp (cur->value, "summary")) + __mrss_parser_atom_string (doc, cur, &item->description, + &item->description_type); + + /* right -> copyright */ + else if (!item->copyright && !strcmp (cur->value, "rights")) + __mrss_parser_atom_string (doc, cur, &item->description, + &item->description_type); + + /* author structure -> author elements */ + else if (!strcmp (cur->value, "author")) + __mrss_parser_atom_author (cur, &item->author, + &item->author_email, + &item->author_uri); + + /* contributor structure -> contributor elements */ + else if (!strcmp (cur->value, "contributor")) + __mrss_parser_atom_author (cur, &item->contributor, + &item->contributor_email, + &item->contributor_uri); + + /* published -> pubDate */ + else if (!item->pubDate && !strcmp (cur->value, "published") + && data->version == MRSS_VERSION_ATOM_1_0 + && (c = nxmle_get_string (cur, NULL))) + { + item->pubDate = __mrss_atom_prepare_date (data, c); + free (c); + } + + else if (!item->pubDate && !strcmp (cur->value, "updated") + && data->version == MRSS_VERSION_ATOM_1_0 + && (c = nxmle_get_string (cur, NULL))) + { + item->pubDate = __mrss_atom_prepare_date (data, c); + free (c); + } + + /* issued -> pubDate (Atom 0.3) */ + else if (!item->pubDate && !strcmp (cur->value, "issued") + && (c = nxmle_get_string (cur, NULL))) + { + item->pubDate = __mrss_atom_prepare_date (data, c); + free (c); + } + + /* id -> guid */ + else if (!item->guid && !strcmp (cur->value, "id") + && (c = nxmle_get_string (cur, NULL))) + item->guid = c; + + /* categories */ + else if (!strcmp (cur->value, "category")) + __mrss_parser_atom_category (cur, &item->category); + + else + { + mrss_tag_t *tag; + if ((tag = __mrss_parse_tag (doc, cur))) + __mrss_parse_tag_insert (&item->other_tags, tag); + } + } + } + + if (!data->item) + data->item = item; + + else + { + mrss_item_t *tmp = data->item; + + while (tmp->next) + tmp = tmp->next; + + tmp->next = item; + } +} + +static void +__mrss_parser_rss_image (nxml_t * doc, nxml_data_t * cur, mrss_t * data) +{ + char *c; + + for (cur = cur->children; cur; cur = cur->next) + { + if (cur->type == NXML_TYPE_ELEMENT) + { + /* title */ + if (!strcmp (cur->value, "title") && !data->image_title + && (c = nxmle_get_string (cur, NULL))) + data->image_title = c; + + /* url */ + else if (!strcmp (cur->value, "url") && !data->image_url + && (c = nxmle_get_string (cur, NULL))) + data->image_url = c; + + /* link */ + else if (!strcmp (cur->value, "link") && !data->image_link + && (c = nxmle_get_string (cur, NULL))) + data->image_link = c; + + /* width */ + else if (!strcmp (cur->value, "width") && !data->image_width + && (c = nxmle_get_string (cur, NULL))) + { + data->image_width = atoi (c); + free (c); + } + + /* height */ + else if (!strcmp (cur->value, "height") && !data->image_height + && (c = nxmle_get_string (cur, NULL))) + { + data->image_height = atoi (c); + free (c); + } + + /* description */ + else if (!strcmp (cur->value, "description") + && !data->image_description + && (c = nxmle_get_string (cur, NULL))) + data->image_description = c; + } + } +} + +static void +__mrss_parser_rss_textinput (nxml_t * doc, nxml_data_t * cur, mrss_t * data) +{ + char *c; + + for (cur = cur->children; cur; cur = cur->next) + { + if (cur->type == NXML_TYPE_ELEMENT) + { + /* title */ + if (!strcmp (cur->value, "title") && !data->textinput_title + && (c = nxmle_get_string (cur, NULL))) + data->textinput_title = c; + + /* description */ + else if (!strcmp (cur->value, "description") + && !data->textinput_description + && (c = nxmle_get_string (cur, NULL))) + data->textinput_description = c; + + /* name */ + else if (!strcmp (cur->value, "name") && !data->textinput_name + && (c = nxmle_get_string (cur, NULL))) + data->textinput_name = c; + + /* link */ + else if (!strcmp (cur->value, "link") && !data->textinput_link + && (c = nxmle_get_string (cur, NULL))) + data->textinput_link = c; + } + } +} + +static void +__mrss_parser_rss_skipHours (nxml_t * doc, nxml_data_t * cur, mrss_t * data) +{ + char *c; + + for (cur = cur->children; cur; cur = cur->next) + { + if (cur->type == NXML_TYPE_ELEMENT) + { + if (!strcmp (cur->value, "hour") + && (c = nxmle_get_string (cur, NULL))) + { + mrss_hour_t *hour; + + if (!(hour = (mrss_hour_t *) calloc (1, sizeof (mrss_hour_t)))) + { + free (c); + return; + } + + hour->element = MRSS_ELEMENT_SKIPHOURS; + hour->allocated = 1; + hour->hour = c; + + if (!data->skipHours) + data->skipHours = hour; + else + { + mrss_hour_t *tmp; + + tmp = data->skipHours; + + while (tmp->next) + tmp = tmp->next; + tmp->next = hour; + } + } + } + } +} + +static void +__mrss_parser_rss_skipDays (nxml_t * doc, nxml_data_t * cur, mrss_t * data) +{ + char *c; + + for (cur = cur->children; cur; cur = cur->next) + { + if (cur->type == NXML_TYPE_ELEMENT) + { + if (!strcmp (cur->value, "day") + && (c = nxmle_get_string (cur, NULL))) + { + mrss_day_t *day; + + if (!(day = (mrss_day_t *) calloc (1, sizeof (mrss_day_t)))) + { + free (c); + return; + } + + day->element = MRSS_ELEMENT_SKIPDAYS; + day->allocated = 1; + day->day = c; + + if (!data->skipDays) + data->skipDays = day; + else + { + mrss_day_t *tmp; + + tmp = data->skipDays; + + while (tmp->next) + tmp = tmp->next; + tmp->next = day; + } + } + } + } +} + +static void +__mrss_parser_rss_item (nxml_t * doc, nxml_data_t * cur, mrss_t * data) +{ + char *c; + char *attr; + mrss_item_t *item; + + if (!(item = (mrss_item_t *) calloc (1, sizeof (mrss_item_t)))) + return; + + item->element = MRSS_ELEMENT_ITEM; + item->allocated = 1; + + for (cur = cur->children; cur; cur = cur->next) + { + if (cur->type == NXML_TYPE_ELEMENT) + { + /* title */ + if (!strcmp (cur->value, "title") && !item->title + && (c = nxmle_get_string (cur, NULL))) + item->title = c; + + /* link */ + else if (!strcmp (cur->value, "link") && !item->link + && (c = nxmle_get_string (cur, NULL))) + item->link = c; + + /* content:encoded + * FIXME: We are ignoring the namespace. + /* Note: We intentionally override description with content:encoded */ + else if (!strcmp (cur->value, "encoded") + && (c = nxmle_get_string (cur, NULL))) + { + if (item->description) + free(item->description); + item->description = c; + } + + /* description */ + else if (!strcmp (cur->value, "description") && !item->description + && (c = nxmle_get_string (cur, NULL))) + item->description = c; + + /* source */ + else if (!strcmp (cur->value, "source") && !item->source) + { + item->source = nxmle_get_string (cur, NULL); + + if ((attr = nxmle_find_attribute (cur, "url", NULL))) + item->source_url = attr; + } + + /* enclosure */ + else if (!strcmp (cur->value, "enclosure") && !item->enclosure) + { + item->enclosure = nxmle_get_string (cur, NULL); + + if ((attr = nxmle_find_attribute (cur, "url", NULL))) + item->enclosure_url = attr; + + if ((attr = nxmle_find_attribute (cur, "length", NULL))) + { + item->enclosure_length = atoi (attr); + free (attr); + } + + if ((attr = nxmle_find_attribute (cur, "type", NULL))) + item->enclosure_type = attr; + } + + /* category */ + else if (!strcmp (cur->value, "category") + && (c = nxmle_get_string (cur, NULL))) + { + mrss_category_t *category; + + if (! + (category = + (mrss_category_t *) calloc (1, sizeof (mrss_category_t)))) + { + free (c); + return; + } + + category->element = MRSS_ELEMENT_CATEGORY; + category->allocated = 1; + category->category = c; + + if ((attr = nxmle_find_attribute (cur, "domain", NULL))) + category->domain = attr; + + if (!item->category) + item->category = category; + else + { + mrss_category_t *tmp; + + tmp = item->category; + while (tmp->next) + tmp = tmp->next; + tmp->next = category; + } + } + + /* author */ + else if (!strcmp (cur->value, "author") && !item->author + && (c = nxmle_get_string (cur, NULL))) + item->author = c; + + /* comments */ + else if (!strcmp (cur->value, "comments") && !item->comments + && (c = nxmle_get_string (cur, NULL))) + item->comments = c; + + /* guid */ + else if (!strcmp (cur->value, "guid") && !item->guid + && (c = nxmle_get_string (cur, NULL))) + { + item->guid = c; + + if ((attr = nxmle_find_attribute (cur, "isPermaLink", NULL))) + { + if (!strcmp (attr, "false")) + item->guid_isPermaLink = 0; + else + item->guid_isPermaLink = 1; + + free (attr); + } + } + + /* pubDate */ + else if (!strcmp (cur->value, "pubDate") && !item->pubDate + && (c = nxmle_get_string (cur, NULL))) + item->pubDate = c; + + /* Other tags: */ + else + { + mrss_tag_t *tag; + + if ((tag = __mrss_parse_tag (doc, cur))) + __mrss_parse_tag_insert (&item->other_tags, tag); + } + + } + } + + + if (!data->item) + data->item = item; + else + { + mrss_item_t *tmp; + + tmp = data->item; + + while (tmp->next) + tmp = tmp->next; + tmp->next = item; + } +} + +static mrss_error_t +__mrss_parser_atom (nxml_t * doc, nxml_data_t * cur, mrss_t ** ret) +{ + mrss_t *data; + char *c = NULL; + + if (!(data = malloc (sizeof (mrss_t)))) + return MRSS_ERR_POSIX; + + memset (data, 0, sizeof (mrss_t)); + data->element = MRSS_ELEMENT_CHANNEL; + data->allocated = 1; + data->version = MRSS_VERSION_ATOM_1_0; + + if (doc->encoding && !(data->encoding = strdup (doc->encoding))) + { + mrss_free (data); + return MRSS_ERR_POSIX; + } + + if (!data->language && (c = nxmle_find_attribute (cur, "xml:lang", NULL))) + data->language = c; + + if ((c = nxmle_find_attribute (cur, "version", NULL))) + { + if (!strcmp (c, "0.3")) + data->version = MRSS_VERSION_ATOM_0_3; + + free (c); + } + + for (cur = cur->children; cur; cur = cur->next) + { + if (cur->type == NXML_TYPE_ELEMENT) + { + /* title -> title */ + if (!data->title && !strcmp (cur->value, "title")) + __mrss_parser_atom_string (doc, cur, &data->title, + &data->title_type); + + /* subtitle -> description */ + else if (!data->description + && data->version == MRSS_VERSION_ATOM_1_0 + && !strcmp (cur->value, "subtitle")) + __mrss_parser_atom_string (doc, cur, &data->description, + &data->description_type); + + /* tagline -> description (Atom 0.3) */ + else if (data->version == MRSS_VERSION_ATOM_0_3 + && !data->description && !strcmp (cur->value, "tagline")) + __mrss_parser_atom_string (doc, cur, &data->description, + &data->description_type); + + /* link href -> link */ + else if (!strcmp (cur->value, "link") && !data->link + && (c = nxmle_find_attribute (cur, "href", NULL))) + data->link = c; + + /* id -> id */ + else if (!strcmp (cur->value, "id") && !data->id + && (c = nxmle_get_string (cur, NULL))) + data->id = c; + + /* rights -> copyright */ + else if (!data->copyright && !strcmp (cur->value, "rights")) + __mrss_parser_atom_string (doc, cur, &data->copyright, + &data->copyright_type); + + /* updated -> lastBuildDate */ + else if (!strcmp (cur->value, "updated") + && (c = nxmle_get_string (cur, NULL))) + { + data->lastBuildDate = __mrss_atom_prepare_date (data, c); + free (c); + } + + /* author -> managingeditor */ + else if (!strcmp (cur->value, "author")) + __mrss_parser_atom_author (cur, &data->managingeditor, + &data->managingeditor_email, + &data->managingeditor_uri); + + /* contributor */ + else if (!strcmp (cur->value, "contributor")) + __mrss_parser_atom_author (cur, &data->contributor, + &data->contributor_email, + &data->contributor_uri); + + /* generator -> generator */ + else if (!strcmp (cur->value, "generator") && !data->generator + && (c = nxmle_get_string (cur, NULL))) + { + char *attr; + + data->generator = c; + + if ((attr = nxmle_find_attribute (cur, "uri", NULL))) + data->generator_uri = attr; + + if ((attr = nxmle_find_attribute (cur, "version", NULL))) + data->generator_version = attr; + } + + /* icon -> image_url */ + else if (!strcmp (cur->value, "icon") && !data->image_url + && (c = nxmle_get_string (cur, NULL))) + data->image_url = c; + + /* logo -> image_logo */ + else if (!strcmp (cur->value, "logo") && !data->image_logo + && (c = nxmle_get_string (cur, NULL))) + data->image_logo = c; + + /* category */ + else if (!strcmp (cur->value, "category")) + __mrss_parser_atom_category (cur, &data->category); + + /* entry -> item */ + else if (!strcmp (cur->value, "entry")) + __mrss_parser_atom_entry (doc, cur, data); + + else + { + mrss_tag_t *tag; + if ((tag = __mrss_parse_tag (doc, cur))) + __mrss_parse_tag_insert (&data->other_tags, tag); + } + + } + } + + *ret = data; + + return MRSS_OK; +} + +static mrss_error_t +__mrss_parser_rss (mrss_version_t v, nxml_t * doc, nxml_data_t * cur, + mrss_t ** ret) +{ + mrss_t *data; + char *c, *attr; + + if (!(data = (mrss_t *) calloc (1, sizeof (mrss_t)))) + return MRSS_ERR_POSIX; + + data->element = MRSS_ELEMENT_CHANNEL; + data->allocated = 1; + data->version = v; + + if (doc->encoding && !(data->encoding = strdup (doc->encoding))) + { + mrss_free (data); + return MRSS_ERR_POSIX; + } + + if (data->version == MRSS_VERSION_1_0) + { + nxml_data_t *cur_channel = NULL; + + while (cur) + { + + if (!strcmp (cur->value, "channel")) + cur_channel = cur; + + else if (!strcmp (cur->value, "image")) + __mrss_parser_rss_image (doc, cur, data); + + else if (!strcmp (cur->value, "textinput")) + __mrss_parser_rss_textinput (doc, cur, data); + + else if (!strcmp (cur->value, "item")) + __mrss_parser_rss_item (doc, cur, data); + + cur = cur->next; + } + + cur = cur_channel; + } + else + { + while (cur && strcmp (cur->value, "channel")) + cur = cur->next; + } + + if (!cur) + { + mrss_free (data); + return MRSS_ERR_PARSER; + } + + if (data->version == MRSS_VERSION_1_0) + { + if ((attr = nxmle_find_attribute (cur, "about", NULL))) + data->about = attr; + } + + for (cur = cur->children; cur; cur = cur->next) + { + if (cur->type == NXML_TYPE_ELEMENT) + { + /* title */ + if (!strcmp (cur->value, "title") && !data->title && + (c = nxmle_get_string (cur, NULL))) + data->title = c; + + /* description */ + else if (!strcmp (cur->value, "description") && !data->description + && (c = nxmle_get_string (cur, NULL))) + data->description = c; + + /* link */ + else if (!strcmp (cur->value, "link") && !data->link + && (c = nxmle_get_string (cur, NULL))) + data->link = c; + + /* language */ + else if (!strcmp (cur->value, "language") && !data->language + && (c = nxmle_get_string (cur, NULL))) + data->language = c; + + /* rating */ + else if (!strcmp (cur->value, "rating") && !data->rating + && (c = nxmle_get_string (cur, NULL))) + data->rating = c; + + /* copyright */ + else if (!strcmp (cur->value, "copyright") && !data->copyright + && (c = nxmle_get_string (cur, NULL))) + data->copyright = c; + + /* pubDate */ + else if (!strcmp (cur->value, "pubDate") && !data->pubDate + && (c = nxmle_get_string (cur, NULL))) + data->pubDate = c; + + /* lastBuildDate */ + else if (!strcmp (cur->value, "lastBuildDate") + && !data->lastBuildDate + && (c = nxmle_get_string (cur, NULL))) + data->lastBuildDate = c; + + /* docs */ + else if (!strcmp (cur->value, "docs") && !data->docs + && (c = nxmle_get_string (cur, NULL))) + data->docs = c; + + /* managingeditor */ + else if (!strcmp (cur->value, "managingeditor") + && !data->managingeditor + && (c = nxmle_get_string (cur, NULL))) + data->managingeditor = c; + + /* webMaster */ + else if (!strcmp (cur->value, "webMaster") && !data->webMaster + && (c = nxmle_get_string (cur, NULL))) + data->webMaster = c; + + /* image */ + else if (!strcmp (cur->value, "image")) + __mrss_parser_rss_image (doc, cur, data); + + /* textinput */ + else if (!strcmp (cur->value, "textinput")) + __mrss_parser_rss_textinput (doc, cur, data); + + /* skipHours */ + else if (!strcmp (cur->value, "skipHours")) + __mrss_parser_rss_skipHours (doc, cur, data); + + /* skipDays */ + else if (!strcmp (cur->value, "skipDays")) + __mrss_parser_rss_skipDays (doc, cur, data); + + /* item */ + else if (!strcmp (cur->value, "item")) + __mrss_parser_rss_item (doc, cur, data); + + /* category */ + else if (!strcmp (cur->value, "category") + && (c = nxmle_get_string (cur, NULL))) + { + mrss_category_t *category; + + if (! + (category = + (mrss_category_t *) calloc (1, sizeof (mrss_category_t)))) + { + mrss_free ((mrss_generic_t *) data); + free (c); + return MRSS_ERR_POSIX; + } + + category->element = MRSS_ELEMENT_CATEGORY; + category->allocated = 1; + category->category = c; + + if ((attr = nxmle_find_attribute (cur, "domain", NULL))) + category->domain = attr; + + if (!data->category) + data->category = category; + else + { + mrss_category_t *tmp; + + tmp = data->category; + while (tmp->next) + tmp = tmp->next; + tmp->next = category; + } + } + + /* enclosure */ + else if (!strcmp (cur->value, "cloud") && !data->cloud) + { + data->cloud = nxmle_get_string (cur, NULL); + + if (!data->cloud_domain + && (attr = nxmle_find_attribute (cur, "domain", NULL))) + data->cloud_domain = attr; + + if (!data->cloud_port + && (attr = nxmle_find_attribute (cur, "port", NULL))) + data->cloud_port = atoi (attr); + + if (!data->cloud_registerProcedure + && (attr = + nxmle_find_attribute (cur, "registerProcedure", NULL))) + data->cloud_registerProcedure = attr; + + if (!data->cloud_protocol + && (attr = nxmle_find_attribute (cur, "protocol", NULL))) + data->cloud_protocol = attr; + } + + /* generator */ + else if (!strcmp (cur->value, "generator") && !data->generator + && (c = nxmle_get_string (cur, NULL))) + data->generator = c; + + /* ttl */ + else if (!strcmp (cur->value, "ttl") && !data->ttl + && (c = nxmle_get_string (cur, NULL))) + { + data->ttl = atoi (c); + free (c); + } + + /* Other tags: */ + else if (data->version != MRSS_VERSION_1_0 + || strcmp (cur->value, "items")) + { + mrss_tag_t *tag; + + if ((tag = __mrss_parse_tag (doc, cur))) + __mrss_parse_tag_insert (&data->other_tags, tag); + } + } + } + + *ret = data; + + return MRSS_OK; +} + +static mrss_error_t +__mrss_parser (nxml_t * doc, mrss_t ** ret) +{ + mrss_error_t r = MRSS_ERR_VERSION; + nxml_data_t *cur; + char *c; + + if (!(cur = nxmle_root_element (doc, NULL))) + return MRSS_ERR_PARSER; + + if (!strcmp (cur->value, "rss")) + { + if ((c = nxmle_find_attribute (cur, "version", NULL))) + { + /* 0.91 VERSION */ + if (!strcmp (c, "0.91")) + r = + __mrss_parser_rss (MRSS_VERSION_0_91, doc, cur->children, ret); + + /* 0.92 VERSION */ + else if (!strcmp (c, "0.92")) + r = + __mrss_parser_rss (MRSS_VERSION_0_92, doc, cur->children, ret); + + /* 2.0 VERSION */ + else if (!strcmp (c, "2.0")) + r = __mrss_parser_rss (MRSS_VERSION_2_0, doc, cur->children, ret); + + else + r = MRSS_ERR_VERSION; + + free (c); + } + + else + r = MRSS_ERR_VERSION; + } + + else if (!strcmp (cur->value, "RDF")) + r = __mrss_parser_rss (MRSS_VERSION_1_0, doc, cur->children, ret); + + else if (!strcmp (cur->value, "feed")) + r = __mrss_parser_atom (doc, cur, ret); + + else + r = MRSS_ERR_PARSER; + + return r; +} + +/*************************** EXTERNAL FUNCTION ******************************/ + +mrss_error_t +mrss_parse_url (char *url, mrss_t ** ret) +{ + return mrss_parse_url_with_options_error_and_transfer_buffer (url, ret, + NULL, NULL, + NULL, NULL); +} + +mrss_error_t +mrss_parse_url_with_options (char *url, mrss_t ** ret, + mrss_options_t * options) +{ + return mrss_parse_url_with_options_error_and_transfer_buffer (url, ret, + options, NULL, + NULL, NULL); +} + +mrss_error_t +mrss_parse_url_with_options_and_error (char *url, mrss_t ** ret, + mrss_options_t * options, + CURLcode * code) +{ + return mrss_parse_url_with_options_error_and_transfer_buffer (url, ret, + options, code, + NULL, NULL); +} + +mrss_error_t +mrss_parse_url_with_options_error_and_transfer_buffer (char *url, + mrss_t ** ret, + mrss_options_t * + options, + CURLcode * code, + char **feed_content, + int *feed_size) +{ + nxml_t *doc; + mrss_error_t err; + char *buffer; + size_t size; + + if (feed_content) + *feed_content = NULL; + + if (feed_size) + *feed_size = 0; + + if (!url || !ret) + return MRSS_ERR_DATA; + + if (nxml_new (&doc) != NXML_OK) + return MRSS_ERR_POSIX; + + if (options) + { + if (options->timeout >= 0) + nxml_set_timeout (doc, options->timeout); + + if (options->proxy) + nxml_set_proxy (doc, options->proxy, options->proxy_authentication); + + if (options->authentication) + nxml_set_authentication (doc, options->authentication); + + if (options->user_agent) + nxml_set_user_agent (doc, options->user_agent); + + nxml_set_certificate (doc, options->certfile, options->password, + options->cacert, options->verifypeer); + } + + if (!(buffer = __mrss_download_file (doc, url, &size, &err, code))) + return err; + + if (nxml_parse_buffer (doc, buffer, size) != NXML_OK) + { + free (buffer); + nxml_free (doc); + + return MRSS_ERR_PARSER; + } + + if (!(err = __mrss_parser (doc, ret))) + { + if (!((*ret)->file = strdup (url))) + { + mrss_free (*ret); + nxml_free (doc); + free (buffer); + + return MRSS_ERR_POSIX; + } + + (*ret)->size = size; + } + + nxml_free (doc); + + /* transfer ownership: */ + if (!feed_content) + free (buffer); + else + *feed_content = buffer; + + if (feed_size) + *feed_size = size; + + return err; +} + +mrss_error_t +mrss_parse_file (char *file, mrss_t ** ret) +{ + nxml_t *doc; + mrss_error_t err; + struct stat st; + + if (!file || !ret) + return MRSS_ERR_DATA; + + if (lstat (file, &st)) + return MRSS_ERR_POSIX; + + if (nxml_new (&doc) != NXML_OK) + return MRSS_ERR_POSIX; + + if (nxml_parse_file (doc, file) != NXML_OK) + { + nxml_free (doc); + return MRSS_ERR_PARSER; + } + + if (!(err = __mrss_parser (doc, ret))) + { + if (!((*ret)->file = strdup (file))) + { + nxml_free (doc); + mrss_free (*ret); + + return MRSS_ERR_POSIX; + } + + (*ret)->size = st.st_size; + } + + nxml_free (doc); + + return err; +} + +mrss_error_t +mrss_parse_buffer (char *buffer, size_t size, mrss_t ** ret) +{ + nxml_t *doc; + mrss_error_t err; + + if (!buffer || !size || !ret) + return MRSS_ERR_DATA; + + if (nxml_new (&doc) != NXML_OK) + return MRSS_ERR_POSIX; + + if (nxml_parse_buffer (doc, buffer, size)) + { + nxml_free (doc); + return MRSS_ERR_PARSER; + } + + if (!(err = __mrss_parser (doc, ret))) + (*ret)->size = size; + + nxml_free (doc); + return err; +} + +/* EOF */ diff --git a/plugins/backend/decsync/libmrss/mrss_search.c b/plugins/backend/decsync/libmrss/mrss_search.c new file mode 100644 index 000000000..8d82c2c30 --- /dev/null +++ b/plugins/backend/decsync/libmrss/mrss_search.c @@ -0,0 +1,122 @@ +/* mRss - Copyright (C) 2005-2007 bakunin - Andrea Marchesini + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "mrss.h" +#include "mrss_internal.h" + +static mrss_error_t __mrss_search_tag_real (mrss_tag_t * tag, char *name, + char *ns, mrss_tag_t ** ret); + +mrss_error_t +mrss_search_tag (mrss_generic_t data, char *name, char *ns, mrss_tag_t ** tag) +{ + mrss_t *tmp; + mrss_error_t err; + + if (!data || !name) + return MRSS_ERR_DATA; + + + tmp = (mrss_t *) data; + switch (tmp->element) + { + case MRSS_ELEMENT_CHANNEL: + err = + __mrss_search_tag_real (((mrss_t *) data)->other_tags, name, ns, tag); + break; + + case MRSS_ELEMENT_ITEM: + err = + __mrss_search_tag_real (((mrss_item_t *) data)->other_tags, name, ns, + tag); + break; + + case MRSS_ELEMENT_TAG: + err = + __mrss_search_tag_real (((mrss_tag_t *) data)->children, name, ns, + tag); + break; + + default: + err = MRSS_ERR_DATA; + break; + } + + return err; +} + +static mrss_error_t +__mrss_search_tag_real (mrss_tag_t * tag, char *name, char *ns, + mrss_tag_t ** ret) +{ + int i; + + for (*ret = NULL; tag; tag = tag->next) + { + i = 0; + if (tag->ns) + i++; + if (ns) + i++; + + if ((!i || (i == 2 && !strcmp (tag->ns, ns))) + && !strcmp (name, tag->name)) + { + *ret = tag; + return MRSS_OK; + } + } + + return MRSS_OK; +} + +mrss_error_t +mrss_search_attribute (mrss_generic_t data, char *name, char *ns, + mrss_attribute_t ** attribute) +{ + mrss_tag_t *tag; + mrss_attribute_t *attr; + int i; + + if (!data || !name) + return MRSS_ERR_DATA; + + tag = (mrss_tag_t *) data; + if (tag->element != MRSS_ELEMENT_TAG) + return MRSS_ERR_DATA; + + for (*attribute = NULL, attr = tag->attributes; attr; attr = attr->next) + { + i = 0; + if (attr->ns) + i++; + if (ns) + i++; + + if ((!i || (i == 2 && !strcmp (attr->ns, ns))) + && !strcmp (name, attr->name)) + { + *attribute = attr; + return MRSS_OK; + } + } + + return MRSS_OK; +} + +/* EOF */ diff --git a/plugins/backend/decsync/libmrss/mrss_write.c b/plugins/backend/decsync/libmrss/mrss_write.c new file mode 100644 index 000000000..09d648d3c --- /dev/null +++ b/plugins/backend/decsync/libmrss/mrss_write.c @@ -0,0 +1,1228 @@ +/* mRss - Copyright (C) 2005-2007 bakunin - Andrea Marchesini + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "mrss.h" +#include "mrss_internal.h" + +static void +__mrss_write_string (void (*func) (void *, char *, ...), void *obj, char *str) +{ + int i; + int len; + char buf[1024]; + int j; + +#define __NXML_CHECK_BUF \ + if(j==sizeof(buf)-1) { buf[j]=0; func(obj, "%s",buf); j=0; } + + if (!str) + return; + + len = strlen (str); + + for (j = i = 0; i < len; i++) + { + if (str[i] == '\r') + continue; + + else if (str[i] == '<') + { + buf[j++] = '&'; + __NXML_CHECK_BUF; + buf[j++] = 'l'; + __NXML_CHECK_BUF; + buf[j++] = 't'; + __NXML_CHECK_BUF; + buf[j++] = ';'; + __NXML_CHECK_BUF; + } + + else if (str[i] == '>') + { + buf[j++] = '&'; + __NXML_CHECK_BUF; + buf[j++] = 'g'; + __NXML_CHECK_BUF; + buf[j++] = 't'; + __NXML_CHECK_BUF; + buf[j++] = ';'; + __NXML_CHECK_BUF; + } + + else if (str[i] == '&') + { + buf[j++] = '&'; + __NXML_CHECK_BUF; + buf[j++] = 'a'; + __NXML_CHECK_BUF; + buf[j++] = 'm'; + __NXML_CHECK_BUF; + buf[j++] = 'p'; + __NXML_CHECK_BUF; + buf[j++] = ';'; + __NXML_CHECK_BUF; + } + + else if (str[i] == '\'') + { + buf[j++] = '&'; + __NXML_CHECK_BUF; + buf[j++] = 'a'; + __NXML_CHECK_BUF; + buf[j++] = 'p'; + __NXML_CHECK_BUF; + buf[j++] = 'o'; + __NXML_CHECK_BUF; + buf[j++] = 's'; + __NXML_CHECK_BUF; + buf[j++] = ';'; + __NXML_CHECK_BUF; + } + + else if (str[i] == '\"') + { + buf[j++] = '&'; + __NXML_CHECK_BUF; + buf[j++] = 'q'; + __NXML_CHECK_BUF; + buf[j++] = 'u'; + __NXML_CHECK_BUF; + buf[j++] = 'o'; + __NXML_CHECK_BUF; + buf[j++] = 't'; + __NXML_CHECK_BUF; + buf[j++] = ';'; + __NXML_CHECK_BUF; + } + + else + { + buf[j++] = str[i]; + __NXML_CHECK_BUF; + } + } + + if (j) + { + buf[j] = 0; + func (obj, "%s", buf); + j = 0; + } +} + +static void +__mrss_write_real_image (mrss_t * mrss, void (*func) (void *, char *, ...), + void *obj) +{ + if (mrss->image_title || mrss->image_url || mrss->image_link + || mrss->image_width || mrss->image_height || mrss->description) + { + + func (obj, " %s\n", + mrss->version == MRSS_VERSION_1_0 ? "" : " "); + + if (mrss->image_title) + { + func (obj, " %s", + mrss->version == MRSS_VERSION_1_0 ? "" : " "); + __mrss_write_string (func, obj, mrss->image_title); + func (obj, "\n"); + } + + if (mrss->image_url) + { + func (obj, " %s", + mrss->version == MRSS_VERSION_1_0 ? "" : " "); + __mrss_write_string (func, obj, mrss->image_url); + func (obj, "\n"); + } + + if (mrss->image_link) + { + func (obj, " %s", + mrss->version == MRSS_VERSION_1_0 ? "" : " "); + __mrss_write_string (func, obj, mrss->image_link); + func (obj, "\n"); + } + + if (mrss->version != MRSS_VERSION_1_0) + { + if (mrss->image_width) + func (obj, " %d\n", mrss->image_width); + + if (mrss->image_height) + func (obj, " %d\n", mrss->image_height); + + if (mrss->image_description) + { + func (obj, " "); + __mrss_write_string (func, obj, mrss->image_description); + func (obj, "\n"); + } + } + + func (obj, " %s\n", + mrss->version == MRSS_VERSION_1_0 ? "" : " "); + } +} + +static void +__mrss_write_real_textinput (mrss_t * mrss, + void (*func) (void *, char *, ...), void *obj) +{ + if (mrss->textinput_title || mrss->textinput_description + || mrss->textinput_name || mrss->textinput_link) + { + + func (obj, " %s\n", + mrss->version == MRSS_VERSION_1_0 ? "" : " "); + + if (mrss->textinput_title) + { + func (obj, " %s", + mrss->version == MRSS_VERSION_1_0 ? "" : " "); + __mrss_write_string (func, obj, mrss->textinput_title); + func (obj, "\n"); + } + + if (mrss->textinput_description) + { + func (obj, " %s", + mrss->version == MRSS_VERSION_1_0 ? "" : " "); + __mrss_write_string (func, obj, mrss->textinput_description); + func (obj, "\n"); + } + + if (mrss->textinput_name) + { + func (obj, " %s", + mrss->version == MRSS_VERSION_1_0 ? "" : " "); + __mrss_write_string (func, obj, mrss->textinput_name); + func (obj, "\n"); + } + + if (mrss->textinput_link) + { + func (obj, " %s", + mrss->version == MRSS_VERSION_1_0 ? "" : " "); + __mrss_write_string (func, obj, mrss->textinput_link); + func (obj, "\n"); + } + + func (obj, " \n"); + } +} + +static void +__mrss_write_real_cloud (mrss_t * mrss, void (*func) (void *, char *, ...), + void *obj) +{ + if ((mrss->version == MRSS_VERSION_0_92 + || mrss->version == MRSS_VERSION_2_0) && (mrss->cloud + || mrss->cloud_domain + || mrss->cloud_port + || mrss->cloud_path + || mrss-> + cloud_registerProcedure + || mrss->cloud_protocol)) + { + func (obj, " cloud_domain) + { + func (obj, " domain=\""); + __mrss_write_string (func, obj, mrss->cloud_domain); + func (obj, "\""); + } + + if (mrss->cloud_port) + func (obj, " port=\"%d\"", mrss->cloud_port); + + if (mrss->cloud_path) + { + func (obj, " path=\""); + __mrss_write_string (func, obj, mrss->cloud_path); + func (obj, "\""); + } + + if (mrss->cloud_registerProcedure) + { + func (obj, " registerProcedure=\""); + __mrss_write_string (func, obj, mrss->cloud_registerProcedure); + func (obj, "\""); + } + + if (mrss->cloud_protocol) + { + func (obj, " protocol=\""); + __mrss_write_string (func, obj, mrss->cloud_protocol); + func (obj, "\""); + } + + if (mrss->cloud) + func (obj, ">%s\n", mrss->cloud); + else + func (obj, " />\n", mrss->cloud); + } +} + +static void +__mrss_write_real_skipHours (mrss_t * mrss, + void (*func) (void *, char *, ...), void *obj) +{ + if (mrss->skipHours && mrss->version != MRSS_VERSION_1_0) + { + mrss_hour_t *hour; + + func (obj, " \n"); + + hour = mrss->skipHours; + while (hour) + { + func (obj, " "); + __mrss_write_string (func, obj, hour->hour); + func (obj, "\n"); + + hour = hour->next; + } + + func (obj, " \n"); + } +} + +static void +__mrss_write_real_skipDays (mrss_t * mrss, void (*func) (void *, char *, ...), + void *obj) +{ + if (mrss->skipDays && mrss->version != MRSS_VERSION_1_0) + { + mrss_day_t *day; + + func (obj, " \n"); + + day = mrss->skipDays; + while (day) + { + func (obj, " "); + __mrss_write_string (func, obj, day->day); + func (obj, "\n"); + day = day->next; + } + + func (obj, " \n"); + } +} + +static void +__mrss_write_real_category (mrss_t * mrss, mrss_category_t * category, + void (*func) (void *, char *, ...), void *obj) +{ + if ((mrss->version == MRSS_VERSION_0_92 + || mrss->version == MRSS_VERSION_2_0) && mrss->category) + { + while (category) + { + func (obj, " domain) + { + func (obj, " domain=\""); + __mrss_write_string (func, obj, category->domain); + func (obj, "\""); + } + + func (obj, ">"); + __mrss_write_string (func, obj, category->category); + func (obj, "\n"); + + category = category->next; + } + } +} + +static void +__mrss_write_real_tag (mrss_tag_t * tag, void (*func) (void *, char *, ...), + void *obj, int index) +{ + mrss_attribute_t *attribute; + + struct mrss_ns_t + { + char *ns; + struct mrss_ns_t *next; + } *namespaces = NULL, *nstmp; + + int i; + + while (tag) + { + if (tag->ns) + { + if (!(namespaces = calloc (1, sizeof (struct mrss_ns_t)))) + return; + + namespaces->ns = tag->ns; + } + + for (attribute = tag->attributes; attribute; + attribute = attribute->next) + { + if (attribute->ns) + { + for (nstmp = namespaces; nstmp; nstmp = nstmp->next) + if (!strcmp (nstmp->ns, attribute->ns)) + break; + + if (nstmp) + continue; + + if (!(nstmp = calloc (1, sizeof (struct mrss_ns_t)))) + return; + + nstmp->ns = attribute->ns; + nstmp->next = namespaces->next; + namespaces->next = nstmp; + } + } + + + for (i = 0; i < index; i++) + func (obj, " "); + + if (tag->ns) + func (obj, " name); + else + func (obj, " <%s", tag->name); + + for (i = 0, nstmp = namespaces; nstmp; nstmp = nstmp->next) + func (obj, " xmlns:ns%d=\"%s\"", i++, nstmp->ns); + + for (attribute = tag->attributes; attribute; + attribute = attribute->next) + { + if (attribute->ns) + { + for (i = 0, nstmp = namespaces; nstmp; i++, nstmp = nstmp->next) + { + if (!strcmp (nstmp->ns, attribute->ns)) + { + func (obj, " ns%d:%s=\"%s\"", i, attribute->name, + attribute->value); + break; + } + } + } + else + func (obj, " %s=\"%s\"", attribute->name, attribute->value); + } + + if (tag->value) + { + func (obj, ">"); + __mrss_write_string (func, obj, tag->value); + } + + if (tag->children) + { + if (!tag->value) + func (obj, ">\n"); + + func (obj, "\n"); + __mrss_write_real_tag (tag->children, func, obj, index + 1); + } + + if (tag->children || tag->value) + { + if (tag->children) + for (i = 0; i < index; i++) + func (obj, " "); + + if (tag->ns) + func (obj, "\n", tag->name); + else + func (obj, "\n", tag->name); + + } + else + func (obj, "/>\n"); + + while (namespaces) + { + nstmp = namespaces; + namespaces = namespaces->next; + free (nstmp); + } + + tag = tag->next; + } +} + +static void +__mrss_write_real_item (mrss_t * mrss, void (*func) (void *, char *, ...), + void *obj) +{ + mrss_item_t *item = mrss->item; + + while (item) + { + func (obj, " %s\n", + mrss->version == MRSS_VERSION_1_0 ? "" : " "); + + if (item->title) + { + func (obj, " %s", + mrss->version == MRSS_VERSION_1_0 ? "" : " "); + __mrss_write_string (func, obj, item->title); + func (obj, "\n"); + } + + if (item->link) + { + func (obj, " %s", + mrss->version == MRSS_VERSION_1_0 ? "" : " "); + __mrss_write_string (func, obj, item->link); + func (obj, "\n"); + } + + if (item->description) + { + func (obj, " %s", + mrss->version == MRSS_VERSION_1_0 ? "" : " "); + __mrss_write_string (func, obj, item->description); + func (obj, "\n"); + } + + if (mrss->version == MRSS_VERSION_2_0) + { + if (item->author) + { + func (obj, " "); + __mrss_write_string (func, obj, item->author); + func (obj, "\n"); + } + + if (item->comments) + { + func (obj, " "); + __mrss_write_string (func, obj, item->comments); + func (obj, "\n"); + } + + if (item->pubDate) + { + func (obj, " "); + __mrss_write_string (func, obj, item->pubDate); + func (obj, "\n"); + } + + if (item->guid) + { + func (obj, " ", + item->guid_isPermaLink ? "true" : "false"); + __mrss_write_string (func, obj, item->guid); + func (obj, "\n"); + } + + } + + if (mrss->version == MRSS_VERSION_2_0 + || mrss->version == MRSS_VERSION_0_92) + { + if (item->source || item->source_url) + { + func (obj, " source_url) + { + func (obj, " url=\""); + __mrss_write_string (func, obj, item->source_url); + func (obj, "\""); + } + + if (item->source) + { + func (obj, ">"); + __mrss_write_string (func, obj, item->source); + func (obj, "\n"); + } + else + func (obj, " />\n"); + } + } + + if (item->enclosure || item->enclosure_length || item->enclosure_type) + { + func (obj, " enclosure_url) + { + func (obj, " url=\""); + __mrss_write_string (func, obj, item->enclosure_url); + func (obj, "\""); + } + + if (item->enclosure_length) + func (obj, " length=\"%d\"", item->enclosure_length); + + if (item->enclosure_type) + { + func (obj, " type=\""); + __mrss_write_string (func, obj, item->enclosure_type); + func (obj, "\""); + } + + if (item->enclosure) + { + func (obj, ">"); + __mrss_write_string (func, obj, item->enclosure); + func (obj, "\n"); + } + else + func (obj, " />\n"); + + } + + __mrss_write_real_category (mrss, item->category, func, obj); + + if (item->other_tags) + __mrss_write_real_tag (item->other_tags, func, obj, 1); + + func (obj, " %s\n", + mrss->version == MRSS_VERSION_1_0 ? "" : " "); + + item = item->next; + } + +} + +static void +__mrss_write_real_atom_category (mrss_category_t * category, + void (*func) (void *, char *, ...), + void *obj) +{ + while (category) + { + func (obj, " domain) + { + func (obj, " scheme=\""); + __mrss_write_string (func, obj, category->domain); + func (obj, "\""); + } + + if (category->category) + { + func (obj, " term=\""); + __mrss_write_string (func, obj, category->category); + func (obj, "\""); + } + + if (category->label) + { + func (obj, " label=\""); + __mrss_write_string (func, obj, category->label); + func (obj, "\""); + } + + func (obj, "/>\n"); + + category = category->next; + } +} + +static void +__mrss_write_real_atom_entry (mrss_t * mrss, + void (*func) (void *, char *, ...), void *obj) +{ + mrss_item_t *item = mrss->item; + + while (item) + { + func (obj, " \n"); + + if (item->title) + { + func (obj, " title_type) + { + func (obj, " type=\""); + __mrss_write_string (func, obj, item->title_type); + func (obj, "\""); + } + + func (obj, ">"); + __mrss_write_string (func, obj, item->title); + func (obj, "\n"); + } + + if (item->link) + { + func (obj, " "); + __mrss_write_string (func, obj, item->link); + func (obj, "\n"); + } + + if (item->description) + { + func (obj, " description_type) + { + func (obj, " type=\""); + __mrss_write_string (func, obj, item->description_type); + func (obj, "\""); + } + + func (obj, ">"); + __mrss_write_string (func, obj, item->description); + func (obj, "\n"); + } + + if (item->copyright) + { + func (obj, " "); + __mrss_write_string (func, obj, item->copyright); + func (obj, "\n"); + } + + if (item->author) + { + func (obj, " \n"); + func (obj, " "); + __mrss_write_string (func, obj, item->author); + func (obj, "\n"); + + if (item->author_email) + { + func (obj, " "); + __mrss_write_string (func, obj, item->author_email); + func (obj, "\n"); + } + + if (item->author_uri) + { + func (obj, " "); + __mrss_write_string (func, obj, item->author_uri); + func (obj, "\n"); + } + + func (obj, " \n"); + } + + if (item->contributor) + { + func (obj, " \n"); + func (obj, " "); + __mrss_write_string (func, obj, item->contributor); + func (obj, "\n"); + + if (item->contributor_email) + { + func (obj, " "); + __mrss_write_string (func, obj, item->contributor_email); + func (obj, "\n"); + } + + if (item->contributor_uri) + { + func (obj, " "); + __mrss_write_string (func, obj, item->contributor_uri); + func (obj, "\n"); + } + + func (obj, " \n"); + } + + /* TODO */ + if (mrss->pubDate) + { + if (mrss->version == MRSS_VERSION_ATOM_1_0) + { + func (obj, " "); + __mrss_write_string (func, obj, item->pubDate); + func (obj, "\n"); + } + else + { + func (obj, " "); + __mrss_write_string (func, obj, item->pubDate); + func (obj, "\n"); + } + } + + if (item->guid) + { + func (obj, " "); + __mrss_write_string (func, obj, item->guid); + func (obj, "\n"); + } + + __mrss_write_real_atom_category (item->category, func, obj); + + if (item->other_tags) + __mrss_write_real_tag (item->other_tags, func, obj, 1); + + func (obj, " \n"); + + item = item->next; + } +} + +static mrss_error_t +__mrss_write_atom (mrss_t * mrss, void (*func) (void *, char *, ...), + void *obj) +{ + func (obj, "language) + func (obj, " xml:lang=\"%s\"", mrss->language); + + if (mrss->version == MRSS_VERSION_ATOM_0_3) + func (obj, " version=\"0.3\""); + + func (obj, ">\n"); + + func (obj, " title_type) + { + func (obj, " type=\""); + __mrss_write_string (func, obj, mrss->title_type); + func (obj, "\""); + } + + func (obj, ">"); + __mrss_write_string (func, obj, mrss->title); + func (obj, "\n"); + + if (mrss->description) + { + if (mrss->version == MRSS_VERSION_ATOM_1_0) + { + func (obj, " description_type) + { + func (obj, " type=\""); + __mrss_write_string (func, obj, mrss->description_type); + func (obj, "\""); + } + + func (obj, ">"); + __mrss_write_string (func, obj, mrss->description); + func (obj, "\n"); + } + else + { + func (obj, " description_type) + { + func (obj, " type=\""); + __mrss_write_string (func, obj, mrss->description_type); + func (obj, "\""); + } + + func (obj, ">"); + __mrss_write_string (func, obj, mrss->description); + func (obj, "\n"); + } + } + + if (mrss->link) + { + func (obj, " link); + func (obj, "\"/>\n"); + } + + if (mrss->id) + { + func (obj, " "); + __mrss_write_string (func, obj, mrss->id); + func (obj, "\n"); + } + + if (mrss->copyright) + { + func (obj, " copyright_type) + { + func (obj, " type=\""); + __mrss_write_string (func, obj, mrss->copyright_type); + func (obj, "\""); + } + + func (obj, ">"); + __mrss_write_string (func, obj, mrss->copyright); + func (obj, "\n"); + } + + /* TODO */ + if (mrss->lastBuildDate) + { + func (obj, " "); + __mrss_write_string (func, obj, mrss->lastBuildDate); + func (obj, "\n"); + } + + if (mrss->managingeditor) + { + func (obj, " \n"); + func (obj, " "); + __mrss_write_string (func, obj, mrss->managingeditor); + func (obj, "\n"); + + if (mrss->managingeditor_email) + { + func (obj, " "); + __mrss_write_string (func, obj, mrss->managingeditor_email); + func (obj, "\n"); + } + + if (mrss->managingeditor_uri) + { + func (obj, " "); + __mrss_write_string (func, obj, mrss->managingeditor_uri); + func (obj, "\n"); + } + + func (obj, " \n"); + } + + if (mrss->generator) + { + func (obj, " generator_uri) + { + func (obj, " uri=\""); + __mrss_write_string (func, obj, mrss->generator_uri); + func (obj, "\""); + } + + if (mrss->generator_version) + { + func (obj, " version=\""); + __mrss_write_string (func, obj, mrss->generator_version); + func (obj, "\""); + } + + func (obj, ">"); + __mrss_write_string (func, obj, mrss->generator); + func (obj, "\n"); + } + + if (mrss->image_url) + { + func (obj, " "); + __mrss_write_string (func, obj, mrss->image_url); + func (obj, "\n"); + } + + if (mrss->image_logo) + { + func (obj, " "); + __mrss_write_string (func, obj, mrss->image_logo); + func (obj, "\n"); + } + + __mrss_write_real_atom_category (mrss->category, func, obj); + + __mrss_write_real_atom_entry (mrss, func, obj); + + if (mrss->other_tags) + __mrss_write_real_tag (mrss->other_tags, func, obj, 0); + + func (obj, "\n"); + + return MRSS_OK; +} + +static mrss_error_t +__mrss_write_real (mrss_t * mrss, void (*func) (void *, char *, ...), + void *obj) +{ + func (obj, "\n"); + + if (mrss->version == MRSS_VERSION_1_0) + func (obj, + "\n"); + + else if (mrss->version == MRSS_VERSION_ATOM_1_0 + || mrss->version == MRSS_VERSION_ATOM_0_3) + return __mrss_write_atom (mrss, func, obj); + + else + { + func (obj, "version) + { + case MRSS_VERSION_0_91: + func (obj, "0.91"); + break; + + case MRSS_VERSION_0_92: + func (obj, "0.92"); + break; + + case MRSS_VERSION_2_0: + func (obj, "2.0"); + break; + + default: + break; + } + + func (obj, "\">\n"); + } + + if (mrss->version == MRSS_VERSION_1_0 && mrss->about) + { + func (obj, " about); + func (obj, "\">\n"); + } + + else + func (obj, " \n"); + + if (mrss->title) + { + func (obj, " "); + __mrss_write_string (func, obj, mrss->title); + func (obj, "\n"); + } + + if (mrss->description) + { + func (obj, " "); + __mrss_write_string (func, obj, mrss->description); + func (obj, "\n"); + } + + if (mrss->link) + { + func (obj, " "); + __mrss_write_string (func, obj, mrss->link); + func (obj, "\n"); + } + + if (mrss->language && mrss->version != MRSS_VERSION_1_0) + { + func (obj, " "); + __mrss_write_string (func, obj, mrss->language); + func (obj, "\n"); + } + + if (mrss->rating && mrss->version != MRSS_VERSION_1_0) + { + func (obj, " "); + __mrss_write_string (func, obj, mrss->rating); + func (obj, "\n"); + } + + if (mrss->copyright && mrss->version != MRSS_VERSION_1_0) + { + func (obj, " "); + __mrss_write_string (func, obj, mrss->copyright); + func (obj, "\n"); + } + + if (mrss->pubDate && mrss->version != MRSS_VERSION_1_0) + { + func (obj, " "); + __mrss_write_string (func, obj, mrss->pubDate); + func (obj, "\n"); + } + + if (mrss->lastBuildDate && mrss->version != MRSS_VERSION_1_0) + { + func (obj, " "); + __mrss_write_string (func, obj, mrss->lastBuildDate); + func (obj, "\n"); + } + + if (mrss->docs && mrss->version != MRSS_VERSION_1_0) + { + func (obj, " "); + __mrss_write_string (func, obj, mrss->docs); + func (obj, "\n"); + } + + if (mrss->managingeditor && mrss->version != MRSS_VERSION_1_0) + { + func (obj, " "); + __mrss_write_string (func, obj, mrss->managingeditor); + func (obj, "\n"); + } + + if (mrss->webMaster && mrss->version != MRSS_VERSION_1_0) + { + func (obj, " "); + __mrss_write_string (func, obj, mrss->webMaster); + func (obj, "\n"); + } + + if (mrss->version == MRSS_VERSION_2_0) + { + if (mrss->generator) + { + func (obj, " "); + __mrss_write_string (func, obj, mrss->generator); + func (obj, "\n"); + } + + if (mrss->ttl) + func (obj, " %d\n", mrss->ttl); + } + + if (mrss->version == MRSS_VERSION_1_0) + { + if (mrss->image_url) + { + func (obj, " image_url); + func (obj, "\" />\n"); + } + + if (mrss->textinput_link) + { + func (obj, " textinput_link); + func (obj, "\" />\n"); + } + + if (mrss->item) + { + mrss_item_t *item = mrss->item; + + func (obj, " \n" " \n"); + while (item) + { + func (obj, " link); + func (obj, "\" />\n"); + item = item->next; + } + func (obj, " \n" " \n"); + } + + func (obj, " \n"); + } + + __mrss_write_real_image (mrss, func, obj); + + __mrss_write_real_textinput (mrss, func, obj); + + __mrss_write_real_cloud (mrss, func, obj); + + __mrss_write_real_skipHours (mrss, func, obj); + + __mrss_write_real_skipDays (mrss, func, obj); + + __mrss_write_real_category (mrss, mrss->category, func, obj); + + if (mrss->other_tags) + __mrss_write_real_tag (mrss->other_tags, func, obj, 1); + + __mrss_write_real_item (mrss, func, obj); + + if (mrss->version != MRSS_VERSION_1_0) + func (obj, " \n" "\n"); + else + func (obj, "\n"); + + return MRSS_OK; +} + +static void +__mrss_file_write (void *obj, char *str, ...) +{ + va_list va; + + va_start (va, str); + vfprintf ((FILE *) obj, str, va); + va_end (va); +} + +static void +__mrss_buffer_write (void *obj, char *str, ...) +{ + va_list va; + char s[4096]; + int len; + char **buffer = (char **) obj; + + va_start (va, str); + len = vsnprintf (s, sizeof (s), str, va); + va_end (va); + + if (!*buffer) + { + if (!(*buffer = (char *) malloc (sizeof (char) * (len + 1)))) + return; + + strncpy (*buffer, s, len + 1); + } + else + { + if (!(*buffer = (char *) realloc (*buffer, + sizeof (char) * (strlen (*buffer) + + len + 1)))) + return; + strncat (*buffer, s, len + 1); + } +} + +/*************************** EXTERNAL FUNCTION ******************************/ + +mrss_error_t +mrss_write_file (mrss_t * mrss, char *file) +{ + FILE *fl; + mrss_error_t ret; + + if (!mrss || !file) + return MRSS_ERR_DATA; + + if (!(fl = fopen (file, "wb"))) + return MRSS_ERR_POSIX; + + ret = __mrss_write_real (mrss, __mrss_file_write, fl); + fclose (fl); + + return ret; +} + +mrss_error_t +mrss_write_buffer (mrss_t * mrss, char **buffer) +{ + if (!mrss || !buffer) + return MRSS_ERR_DATA; + + return __mrss_write_real (mrss, __mrss_buffer_write, buffer); +} + +/* EOF */ diff --git a/plugins/backend/decsync/libnxml/meson.build b/plugins/backend/decsync/libnxml/meson.build new file mode 100644 index 000000000..1eaa8ed82 --- /dev/null +++ b/plugins/backend/decsync/libnxml/meson.build @@ -0,0 +1,19 @@ +nxml_inc = include_directories('.') +nxml_lib = static_library( + 'nxml', + [ + 'nxml_download.c', + 'nxml_easy.c', + 'nxml_edit.c', + 'nxml_error.c', + 'nxml_free.c', + 'nxml_init.c', + 'nxml_namespace.c', + 'nxml_parser.c', + 'nxml_string.c', + 'nxml_tools.c', + 'nxml_utf.c', + 'nxml_write.c' + ], + include_directories: nxml_inc +) diff --git a/plugins/backend/decsync/libnxml/nxml.h b/plugins/backend/decsync/libnxml/nxml.h new file mode 100644 index 000000000..0406038bb --- /dev/null +++ b/plugins/backend/decsync/libnxml/nxml.h @@ -0,0 +1,947 @@ +/* nXml - Copyright (C) 2005-2007 bakunin - Andrea Marchesini + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __N_XML_H__ +#define __N_XML_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LIBNXML_VERSION_STRING "0.18.3" + +#define LIBNXML_MAJOR_VERSION 0 +#define LIBNXML_MINOR_VERSION 18 +#define LIBNXML_MICRO_VERSION 3 + +typedef struct nxml_t nxml_t; +typedef struct nxml_data_t nxml_data_t; +typedef struct nxml_attr_t nxml_attr_t; +typedef struct nxml_doctype_t nxml_doctype_t; +typedef struct nxml_namespace_t nxml_namespace_t; + +typedef struct __nxml_private_t __nxml_private_t; +typedef struct __nxml_entity_t __nxml_entity_t; + +/** This enum describes the error type of libnxml */ +typedef enum { + NXML_OK = 0, /**< No error */ + NXML_ERR_POSIX, /**< For the correct error, use errno */ + NXML_ERR_PARSER, /**< Parser error */ + NXML_ERR_DOWNLOAD, /**< Download error */ + NXML_ERR_DATA /**< The parameters are incorrect */ +} nxml_error_t; + +/** This enum describes the type of data element of libnxml */ +typedef enum { + NXML_TYPE_TEXT, /**< Text element */ + NXML_TYPE_COMMENT, /**< Comment element */ + NXML_TYPE_ELEMENT, /**< Data element */ + NXML_TYPE_PI, /**< PI element */ + NXML_TYPE_ELEMENT_CLOSE /**< Data element - For internal use only */ +} nxml_type_t; + +/** This enum describes the supported XML version */ +typedef enum { + NXML_VERSION_1_1, /**< XML 1.1 */ + NXML_VERSION_1_0 /**< XML 1.0 */ +} nxml_version_t; + +/** This enum describes the CharSet of XML document */ +typedef enum { + NXML_CHARSET_UTF8, /**< UTF8 chatset detected */ + NXML_CHARSET_UTF16LE, /**< UTF 16 Little Endian detected */ + NXML_CHARSET_UTF16BE, /**< UTF 16 Big Endian detected */ + NXML_CHARSET_UCS4_1234, /**< UCS 4byte order 1234 detected */ + NXML_CHARSET_UCS4_4321, /**< UCS 3byte order 4321 detected */ + NXML_CHARSET_UCS4_2143, /**< UCS 3byte order 2143 detected */ + NXML_CHARSET_UCS4_3412, /**< UCS 3byte order 3412 detected */ + NXML_CHARSET_UNKNOWN /**< Unknown format */ +} nxml_charset_t; + +/** + * Data struct for any element of XML stream/files + * + * \brief + * Data struct for any element of XML streams/files + */ +struct nxml_data_t +{ + nxml_type_t type; /**< type of this nxml_data_t struct */ + + char *value; /**< The value of this data struct */ + + nxml_attr_t *attributes; /**< List of attributes of this struct. + This list exists only if + type == NXML_TYPE_ELEMENT */ + + nxml_namespace_t *ns; /**< Pointer to the correct namespace */ + nxml_namespace_t *ns_list; /**< The namespaces in this element */ + + nxml_data_t *children; /**< The children of this data struct */ + nxml_data_t *next; /**< The next element */ + + nxml_data_t *parent; /**< The parent */ + nxml_t *doc; /**< The nxml_t */ +}; + +/** + * Data struct for any element of attribute of xml element + * + * \brief + * Data struct for any element of attribute of xml element + */ +struct nxml_attr_t +{ + char *name; + char *value; + + nxml_namespace_t *ns; + + nxml_attr_t *next; +}; + +/** + * Data struct for doctype elements + * + * \brief + * Data struct for doctype elements + */ +struct nxml_doctype_t +{ + char *value; /**< The string no parsers */ + char *name; /**< The name of current doctype */ + + nxml_t *doc; /**< The nxml_t */ + nxml_doctype_t *next; +}; + +/** + * Data struct for namespace + * + * \brief + * Data struct for namespace + */ +struct nxml_namespace_t +{ + char *prefix; + char *ns; + nxml_namespace_t *next; +}; + +/** Data struct private about entities for internal use only + * + * \brief + * Data struct private about entities for internal use only + */ +struct __nxml_entity_t +{ + char *name; + char *entity; + + __nxml_entity_t *next; +}; + +/** Data struct private for internal use only + * + * \brief + * Data struct private for internal use only + */ +struct __nxml_private_t +{ + void (*func)(char *, ...); + int line; + int timeout; + char *proxy; + char *proxy_authentication; + char *cacert; + char *certfile; + char *password; + int verifypeer; + char *authentication; + char *user_agent; + char textindent; + + CURLcode curl_error; + + __nxml_entity_t *entities; +}; + +/** + * Principal data struct. It describes a XML document and it contains pointers + * to any other structures. + * + * \brief + * Principal data struct. It describes a XML document and it contains pointers + * to any other structures */ +struct nxml_t +{ + + char *file; /**< XML document filename or url */ + size_t size; /**< Size of XML document in byte */ + + nxml_version_t version; /**< XML document version */ + int standalone; /**< This document is standalone ? */ + char *encoding; /**< Encoding type */ + + nxml_charset_t charset_detected; /**< charset detected when the a + XML document is parsed. The document + will be convert to UTF-8 */ + + nxml_data_t *data; /**< The data of XML document */ + nxml_doctype_t *doctype; /**< The doctype of XML document */ + + __nxml_private_t priv; /**< For internal use only */ +}; + +/* INIT FUNCTIONS ************************************************************/ + +/** + * This function creates a new nxml_t data struct. + * + * \param nxml Pointer to a nxml_t data struct. It will be allocated. + * \return the error code + */ +nxml_error_t nxml_new(nxml_t **nxml); + +/** + * This function creates a new nxml_data_t child of a parent in the data + * struct. If parent is NULL the child will be created in the root level + * of XML document. + * + * \param nxml Pointer to a nxml_t data struct. + * \param parent The parent of new data struct child. If it is NULL, the + * child is in the root level. + * \param child It is the pointer to the new data struct. If *child is NULL, + * it will be allocated, else it will be insert as it is. + * \return the error code + * + * \code + * nxml_data_t *data1, *data2; + * data1=NULL; + * nxml_add(nxml, NULL, &data1); + * + * data2=(nxml_data_t *)malloc(sizeof(nxml_data_t)); + * nxml_add(nxml, NULL, &data2); + * \endcode + */ +nxml_error_t nxml_add(nxml_t *nxml, + nxml_data_t *parent, + nxml_data_t **child); + +/** + * This function removes a nxml_data_t child from a parent in the data + * struct. If parent is NULL the child will be removed in the root level of + * XML document. This function doesn't free the child. If you want you can + * reinsert the child in another parent tree or use the nxml_free_data + * function. + * + * \param nxml Pointer to a nxml_t data struct. + * \param parent The parent of data struct child. If it is NULL, the + * child will be searched in the root level. + * \param child It is the pointer to the child that you want remove + * \return the error code + */ +nxml_error_t nxml_remove(nxml_t *nxml, + nxml_data_t *parent, + nxml_data_t *child); + +/** + * This function creates a new nxml_attr_t data of a element in the data + * struct. + * + * \param nxml Pointer to a nxml_t data struct. + * \param element The element of the new data struct attribute. + * \param attribute The pointer to the your data struct. If it is NULL it will + * be allocated, else no. + * \return the error code + */ +nxml_error_t nxml_add_attribute(nxml_t *nxml, + nxml_data_t *element, + nxml_attr_t **attribute); + +/** + * This function removes a nxml_attr_t data of a element. It does not free it + * so you can reinsert o free it with nxml_free_attribute. + * + * \param nxml Pointer to a nxml_t data struct. + * \param element The element that contains the attribute + * \param attribute The attribute that you want remove. + * \return the error code + */ +nxml_error_t nxml_remove_attribute(nxml_t *nxml, + nxml_data_t *element, + nxml_attr_t *attribute); + +/** + * This function adds a nxml_namespace_t data in a nxml document. + * + * \param nxml Pointer to a nxml_t data struct. + * \param element The element of the new data struct namespace. + * \param ns The namespace that you want add + * \return the error code + */ +nxml_error_t nxml_add_namespace(nxml_t *nxml, + nxml_data_t *element, + nxml_namespace_t **ns); + +/** + * This function removes a nxml_namespace_t data from a nxml document. + * + * \param nxml Pointer to a nxml_t data struct. + * \param element The element of the new data struct namespace. + * \param ns The namespace that you want remove + * \return the error code + */ +nxml_error_t nxml_remove_namespace(nxml_t *nxml, + nxml_data_t *element, + nxml_namespace_t *ns); + +/** + * This function sets the output function. If you set your function, the + * parser'll write the error by this function. As default there is not a + * function. If you want tou can set 'nxml_print_general' function that + * print to stderr. + * + * \param nxml The struct create with nxml_new. + * \param func Your function. If you don't want the function, set it to NULL. + * As default a nxml_t element has not a output function. + * \return the error code + */ +nxml_error_t nxml_set_func(nxml_t *nxml, + void (*func)(char *, ...)); + +void nxml_print_generic(char *, ...); + +/** + * This function sets the timeout in seconds for the download of a remote + * XML document. Default is 0 and 0 is no timeout. + * + * \param nxml The struct create with nxml_new. + * \param seconds the timeout in seconds + * \return the error code + */ +nxml_error_t nxml_set_timeout(nxml_t *nxml, + int seconds); + +/** + * This functions sets a proxy server for the downloading procedure. + * + * \param nxml The struct create with nxml_new. + * \param proxy the proxy as a string + * \param userpwd the user and password in this format user:password + * \return the error code + */ +nxml_error_t nxml_set_proxy(nxml_t *nxml, + char *proxy, + char *userpwd); + +/** + * This functions sets a user/password for a for the download procedure. + * + * \param nxml The struct create with nxml_new. + * \param userpwd the user and password in this format user:password + * \return the error code + */ +nxml_error_t nxml_set_authentication(nxml_t *nxml, + char *userpwd); + +/** + * This functions sets an user agent for a for the download procedure. + * + * \param nxml The struct create with nxml_new. + * \param user_agent The agent + * \return the error code + */ +nxml_error_t nxml_set_user_agent(nxml_t *nxml, + char *user_agent); + +/** + * This functions sets a certificate in the http request. You can set a + * certificate file and a password. + * + * \param nxml The struct create with nxml_new. + * \param certfile the certfile for the ssl connection (can be NULL) + * \param password the password of your certifcate (can be NULL) + * \param cacert the CA certificate to verify peer against (can be NULL) + * \param verifypeer active/deactive the peer validation + * \return the error code + */ +nxml_error_t nxml_set_certificate(nxml_t *nxml, + char *certfile, + char *password, + char *cacert, + int verifypeer); + +/** + * This function (de)actives the indent of the TEXT elements. Default it is + * activated. + * + * \param nxml The struct create with nxml_new + * \param textindent If it is != 0, the indent will be activated + * \return the error code + */ +nxml_error_t nxml_set_textindent(nxml_t *nxml, + char textindent); + +/* DOWNLOAD *****************************************************************/ + +/** + * This function downloads a stream from a http/https/ftp server. + * + * \param nxml The struct create with nxml_new. + * \param url the http file + * \param buffer a string for the buffer + * \param size The function sets here the length of the file if it's not NULL. + * \return a buffer or NULL + */ +nxml_error_t nxml_download_file(nxml_t *nxml, + char *url, + char **buffer, + size_t *size); + +/* PARSER FUNCTIONS *********************************************************/ + +/** + * This function parses a url. It downloads a url with curl library and + * parses it. + * + * \param nxml the struct create with nxml_new. + * \param url the url that you want parse. + * \return the error code + */ +nxml_error_t nxml_parse_url(nxml_t *nxml, + char *url); + +/** + * This function parses a file. + * + * \param nxml the struct create with nxml_new. + * \param file the file that you want parse. + * \return the error code + */ +nxml_error_t nxml_parse_file(nxml_t *nxml, + char *file); + +/** + * This function parses a buffer in memory. + * + * \param nxml the struct create with nxml_new. + * \param buffer the buffer that you want parse. + * \param size the size of buffer. If size is 0, the function checks the + * length of your buffer searching a '\\0'. + * \return the error code + */ +nxml_error_t nxml_parse_buffer(nxml_t *nxml, + char *buffer, + size_t size); + +/* WRITE FUNCTIONS **********************************************************/ + +/** + * This function writes the data struct in a local file. + * + * \param nxml the nxml data strut + * \param file the local file + * \return the error code + */ +nxml_error_t nxml_write_file(nxml_t *nxml, + char *file); + +/** + * This function writes the data struct in a buffer. + * + * \code + * char *buffer; + * buffer=NULL; // This is important! + * nxml_write_buffer(nxml, &buffer); + * \endcode + * + * The buffer must be NULL. + * + * \param nxml + * \param buffer the memory buffer + * \return the error code + */ +nxml_error_t nxml_write_buffer(nxml_t *nxml, + char **buffer); + +/* FREE FUNCTIONS ************************************************************/ + +/** + * This function removes the data in a structure nxml_t and makes it clean for + * another usage. + * + * \param nxml the pointer to you data struct. + * \return the error code. + */ +nxml_error_t nxml_empty(nxml_t *nxml); + +/** + * This function frees the memory of a nxml_t *element. After the free, + * your data struct is not useful. If you want erase the internal data, use + * nxml_empty function + * + * \param nxml the pointer to your data struct. + * \return the error code. + */ +nxml_error_t nxml_free(nxml_t *nxml); + +/** + * This function frees the memory of a nxml_data_t *element and any its + * children and its attributes. + * + * \param data the pointer to you data struct. + * \return the error code + */ +nxml_error_t nxml_free_data(nxml_data_t *data); + +/** + * This function frees the memory of a nxml_attr_t *element. + * + * \param data the pointer to you data struct. + * \return the error code + */ +nxml_error_t nxml_free_attribute(nxml_attr_t *data); + +/** + * This function frees the memory of a nxml_namespace_t *element. + * + * \param data the pointer to you data struct. + * \return the error code + */ +nxml_error_t nxml_free_namespace(nxml_namespace_t *data); + +/* EDIT FUNCTIONS ***********************************************************/ + +/** + * This function returns the root element of xml data struct. + * + * \code + * nxml_t *nxml; + * nxml_data_t *root; + * + * nxml_new(&nxml); + * nxml_parser_file(nxml, "file.xml"); + * nxml_root_element(nxml, &root); + * printf("%p\n",root); + * nxml_free(nxml); + * \endcode + * + * \param nxml the data struct + * \param element the pointer to your nxml_data_t struct + * \return the error code + */ +nxml_error_t nxml_root_element(nxml_t *nxml, + nxml_data_t **element); + +/** + * This function searchs the request element in the children of the data struct. + * + * \code + * nxml_t *nxml; + * nxml_data_t *root; + * + * nxml_new(&nxml); + * nxml_parser_file(nxml, "file.xml"); + * nxml_find_element(nxml, NULL, "hello_world", &root); + * printf("%p\n",root); + * nxml_free(nxml); + * \endcode + * + * \param nxml the data struct + * \param parent the data struct nxml_data_t of parent. If it is NULL, this + * function searchs in the root element level. + * \param name the name of the node that you want. + * \param element the pointer to your nxml_data_t struct. If element will be + * NULL, the item that you want does not exist. + * \return the error code + */ +nxml_error_t nxml_find_element(nxml_t *nxml, + nxml_data_t *parent, + char *name, + nxml_data_t **element); + +/** + * This function searchs the first doctype element in the nxml_t document. + * + * \param nxml the data struct + * \param doctype the pointer to your nxml_doctype_t struct. If element will be + * NULL, the item that you want does not exist. + * \return the error code + */ +nxml_error_t nxml_doctype_element(nxml_t *nxml, + nxml_doctype_t **doctype); + +/** + * This function searchs the request attribute and returns its values. + * + * \code + * nxml_t *nxml; + * nxml_data_t *root; + * + * nxml_new(&nxml); + * nxml_parser_file(nxml, "file.xml"); + * nxml_find_element(nxml, NULL, "hello_world", &root); + * if(root) { + * nxml_attr_t *attribute=NULL; + * nxml_find_attribute(root, "attribute", &attribute); + * + * if(attribute) + * printf("%s\n",attribute->value); + * } + * nxml_free(nxml); + * \endcode + * + * \param data the data struct + * \param name the attribute that you want search + * \param attribute the pointer to your nxml_attr_t struct. If attribute will + * be NULL, the attribute that you want does not exist. + * does not exist. + * \return the error code + */ +nxml_error_t nxml_find_attribute(nxml_data_t *data, + char *name, + nxml_attr_t **attribute); + +/** + * This function searchs the request namespaceibute and returns its values. + * + * \param data the data struct + * \param name the namespace that you want search + * \param ns the pointer to your nxml_attr_t struct. If namespace will + * be NULL, the namespace that you want does not exist. + * does not exist. + * \return the error code + */ +nxml_error_t nxml_find_namespace(nxml_data_t *data, + char *name, + nxml_namespace_t **ns); + +/** + * This function returns the string of a XML element. + * \code + * nxml_t *nxml; + * nxml_data_t *root; + * char *str; + * + * nxml_new(&nxml); + * nxml_parser_file(nxml, "file.xml"); + * nxml_find_element(nxml, NULL, "hello_world", &root); + * if(root) { + * nxml_get_string(root, &str); + * if(str) { + * printf("Hello_world item contains: %s\n",str); + * free(str); + * } + * } + * nxml_free(nxml); + * \endcode + * + * \param element the xnml_data_t pointer + * \param string the pointer to you char *. You must free it after usage. + * \return the error code + */ +nxml_error_t nxml_get_string(nxml_data_t *element, + char **string); + +/* ERROR FUNCTIONS **********************************************************/ + +/** + * This function returns a static string with the description of error code + * + * \param nxml the pointer to data struct + * \param err the error code that you need as string + * \return a string. Don't free this string! + */ +char *nxml_strerror(nxml_t *nxml, + nxml_error_t err); + +/** + * This function returns the CURLcode error if there was a problem about the + * downloading procedure: + * + * \param nxml the pointer to data struct + * \param err the error code that you need as string + * \return the CURLcode + */ +CURLcode nxml_curl_error(nxml_t *nxml, + nxml_error_t err); + +/** + * This function return the line of a error of parse. + * + * \param nxml the pointer to data struct + * \param line pointer to your integer. In this pointer will be set the line. + * \return the error code + */ +nxml_error_t nxml_line_error(nxml_t *nxml, + int *line); + +/* EASY FUNCTIONS ***********************************************************/ + +/** + * This function returns a new nxml_t data. + * + * \param err If err is not NULL, err will be set to the error flag. + * \return the pointer to a new nxml_t data. If it returns NULL, read the err + * code. This function use nxml_set_func with nxml_print_generic so the + * error will be write in the standard output. + */ +nxml_t *nxmle_new_data(nxml_error_t *err); + +/** + * This function returns a new nxml_t data and parses a remote url document + * from http or ftp protocol. This function use nxml_set_func with + * nxml_print_generic so the error will be write in the standard output. + * + * \param url the url that you want parse. + * \param err If err is not NULL, err will be set to the error flag. + * \return the pointer to a new nxml_t data. + */ +nxml_t *nxmle_new_data_from_url(char *url, + nxml_error_t *err); + +/** + * This function returns a new nxml_t data and parses a local file. This + * function use nxml_set_func with nxml_print_generic so the error will be + * write in the standard output. + * + * \param file the file that you want parse. + * \param err If err is not NULL, err will be set to the error flag. + * \return the pointer to a new nxml_t data. + */ +nxml_t *nxmle_new_data_from_file(char *file, + nxml_error_t *err); + +/** + * This function returns a new nxml_t data and parses a buffer. This + * function use nxml_set_func with nxml_print_generic so the error will be + * write in the standard output. + * + * \param buffer the buffer that you want parse. + * \param size the size of buffer. If size is 0, the function checks the + * \param err If err is not NULL, err will be set to the error flag. + * \return the pointer to a new nxml_t data. + */ +nxml_t *nxmle_new_data_from_buffer(char *buffer, + size_t size, + nxml_error_t *err); + +/** + * This function creates and adds a child nxml_data_t to a parent in your + * nxml data struct. + * + * \param nxml Pointer to your nxml data. + * \param parent The parent of new data struct child. If it is NULL, the + * child is in the root level. + * \param err If err is not NULL, err will be set to the error flag. + * \return the pointer to a new nxml_data_t data child. + */ +nxml_data_t *nxmle_add_new(nxml_t *nxml, + nxml_data_t *parent, + nxml_error_t *err); + +/** + * This function adds a your nxml_data_t to a parent in your nxml + * data struct. + * + * \param nxml Pointer to your nxml data. + * \param parent The parent of new data struct child. If it is NULL, the + * child is in the root level. + * \param child The you child nxml_data_t struct that you want insert. + * \param err If err is not NULL, err will be set to the error flag. + * \return the pointer to a new nxml_data_t data child. + */ +nxml_data_t *nxmle_add_data(nxml_t *nxml, + nxml_data_t *parent, + nxml_data_t *child, + nxml_error_t *err); + +/** + * This function creates and adds an attribute nxml_attr_t data to a + * nxml_data_t struct in your nxml data struct. + * + * \param nxml Pointer to your nxml data. + * \param element The parent of new nxml_attr_t struct. + * \param err If err is not NULL, err will be set to the error flag. + * \return the pointer to a new nxml_data_t data child. + */ +nxml_attr_t *nxmle_add_attribute_new(nxml_t *nxml, + nxml_data_t *element, + nxml_error_t *err); + +/** + * This function adds an attribute nxml_attr_t data to a + * nxml_data_t struct in your nxml data struct. + * + * \param nxml Pointer to your nxml data. + * \param element The parent of your nxml_attr_t struct. + * \param attribute Your attribute element. + * \param err If err is not NULL, err will be set to the error flag. + * \return the pointer to a new nxml_data_t data child. + */ +nxml_attr_t *nxmle_add_attribute_data(nxml_t *nxml, + nxml_data_t *element, + nxml_attr_t *attribute, + nxml_error_t *err); + +/** + * This function creates and adds a namespace nxml_namespace_t data to a + * nxml data struct. + * + * \param nxml Pointer to your nxml data. + * \param element The element of in witch you want add the namespace. + * \param err If err is not NULL, err will be set to the error flag. + * \return the pointer to a new nxml_data_t data child. + */ +nxml_namespace_t *nxmle_add_namespace_new(nxml_t *nxml, + nxml_data_t *element, + nxml_error_t *err); + +/** + * This function adds an namespace nxml_namespace-t data to a nxml data struct. + * + * \param nxml Pointer to your nxml data. + * \param element The element of in witch you want add the namespace. + * \param ns Your namespace element. + * \param err If err is not NULL, err will be set to the error flag. + * \return the pointer to a new nxml_data_t data child. + */ +nxml_namespace_t *nxmle_add_namespace_data(nxml_t *nxml, + nxml_data_t *element, + nxml_namespace_t *ns, + nxml_error_t *err); + +/** + * This function returns the root element of a nxml_t. + * + * \param nxml Pointer to your nxml data. + * \param err If err is not NULL, err will be set to the error flag. + * \return the pointer to the root element. If NULL the element does not + * exist. + */ +nxml_data_t *nxmle_root_element(nxml_t *nxml, + nxml_error_t *err); + +/** + * This function returns the first doctype element of a nxml_t. + * + * \param nxml Pointer to your nxml data. + * \param err If err is not NULL, err will be set to the error flag. + * \return the pointer to the doctype element. If NULL the element does not + * exist. + */ +nxml_doctype_t *nxmle_doctype_element(nxml_t *nxml, + nxml_error_t *err); + +/** + * This function returns the nxml_data_t pointer to a element by + * a name. + * + * \param nxml Pointer to your nxml data. + * \param parent Pointer to your nxml_data_t parent. If it is NULL, this + * function searchs in a root element level. + * \param name The name of element that you want. + * \param err If err is not NULL, err will be set to the error flag. + * \return the pointer to the root element. If NULL the element does not + * exist. + */ +nxml_data_t *nxmle_find_element(nxml_t *nxml, + nxml_data_t *parent, + char *name, + nxml_error_t *err); + +/** + * This function returns the value of a attribute by a name. + * + * \param element Pointer to your nxml_data_t. + * \param name The name of attribute that you want. + * \param err If err is not NULL, err will be set to the error flag. + * \return a pointer to a char allocated so you must free it after usage. If + * it is NULL, the attribute does not exist. + */ +char *nxmle_find_attribute(nxml_data_t *element, + char *name, + nxml_error_t *err); + +/** + * This function returns the value of a namespace by a name. + * + * \param element Pointer to your nxml_data_t. + * \param name The name of namespace that you want. + * \param err If err is not NULL, err will be set to the error flag. + * \return a pointer to a char allocated so you must free it after usage. If + * it is NULL, the namespace does not exist. + */ +char *nxmle_find_namespace(nxml_data_t *element, + char *name, + nxml_error_t *err); + +/** + * This function returns the contain of a element. + * + * \param element Pointer to your nxml_data_t. + * \param err If err is not NULL, err will be set to the error flag. + * \return a pointer to a char allocated so you must free it after usage. If + * it is NULL, the attribute does not exist. + */ +char *nxmle_get_string(nxml_data_t *element, + nxml_error_t *err); + +/** + * This function writes the data struct in a buffer. + * + * \param nxml + * \param err If err is not NULL, err will be set to the error flag. + * \return a pointer to a char allocated so you must free it after usage. + */ +char *nxmle_write_buffer(nxml_t *nxml, nxml_error_t *err); + +/** + * This function return the line of a error of parse. + * \param nxml the pointer to data struct + * \param err If err is not NULL, err will be set to the error flag. + * \return the line with the error. + */ +int nxmle_line_error(nxml_t *nxml, nxml_error_t *err); + +/* Easy functions defined: */ +#define nxmle_remove nxml_remove +#define nxmle_remove_attribute nxml_remove_attribute +#define nxmle_remove_namespace nxml_remove_namespace +#define nxmle_write_file nxml_write_file + +#define nxmle_empty nxml_empty +#define nxmle_free nxml_free +#define nxmle_free_data nxml_free_data +#define nxmle_free_attribute nxml_free_attribute + +#define nxmle_strerror nxml_strerror + +#include "nxml_internal.h" + +#endif + +/* EOF */ diff --git a/plugins/backend/decsync/libnxml/nxml_download.c b/plugins/backend/decsync/libnxml/nxml_download.c new file mode 100644 index 000000000..9f079bd37 --- /dev/null +++ b/plugins/backend/decsync/libnxml/nxml_download.c @@ -0,0 +1,139 @@ +/* nXml - Copyright (C) 2005-2007 bakunin - Andrea Marchesini + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "nxml.h" + +typedef struct __nxml_download_t__ __nxml_download_t; + +struct __nxml_download_t__ +{ + char *mm; + size_t size; +}; + +static size_t +__nxml_memorize_file(void *ptr, size_t size, size_t nmemb, void *data) +{ + register int realsize = size * nmemb; + __nxml_download_t *mem = (__nxml_download_t *)data; + + if (!mem->mm) + { + if (!(mem->mm = (char *)malloc(realsize + 1))) + return -1; + } + else + { + if (!(mem->mm = (char *)realloc(mem->mm, mem->size + realsize + 1))) + return -1; + } + + memcpy(&(mem->mm[mem->size]), ptr, realsize); + mem->size += realsize; + mem->mm[mem->size] = 0; + + return realsize; +} + +nxml_error_t +nxml_download_file(nxml_t *nxml, char *fl, char **buffer, size_t *size) +{ + __nxml_download_t *chunk; + CURL *curl; + CURLcode ret; + + if (!fl || !buffer || !nxml) + return NXML_ERR_DATA; + + if (!(chunk = (__nxml_download_t *)malloc(sizeof(__nxml_download_t)))) + return NXML_ERR_POSIX; + + chunk->mm = NULL; + chunk->size = 0; + + curl_global_init(CURL_GLOBAL_DEFAULT); + if (!(curl = curl_easy_init())) + { + free(chunk); + return NXML_ERR_POSIX; + } + + curl_easy_setopt(curl, CURLOPT_URL, fl); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, __nxml_memorize_file); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); + curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1); + curl_easy_setopt(curl, CURLOPT_FILE, (void *)chunk); + curl_easy_setopt(curl, CURLOPT_ENCODING, "gzip, deflate"); + + if (nxml->priv.timeout) + curl_easy_setopt(curl, CURLOPT_TIMEOUT, nxml->priv.timeout); + + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, !nxml->priv.verifypeer); + + if (nxml->priv.certfile) + { + curl_easy_setopt(curl, CURLOPT_SSLCERT, nxml->priv.certfile); + + if (nxml->priv.password) + curl_easy_setopt(curl, CURLOPT_SSLCERTPASSWD, nxml->priv.password); + + if (nxml->priv.cacert) + curl_easy_setopt(curl, CURLOPT_CAINFO, nxml->priv.cacert); + } + + if (nxml->priv.authentication) + curl_easy_setopt(curl, CURLOPT_USERPWD, nxml->priv.authentication); + + if (nxml->priv.proxy) + { + curl_easy_setopt(curl, CURLOPT_PROXY, nxml->priv.proxy); + + if (nxml->priv.proxy_authentication) + curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, + nxml->priv.proxy_authentication); + } + + if (nxml->priv.user_agent) + curl_easy_setopt(curl, CURLOPT_USERAGENT, nxml->priv.user_agent); + + if ((ret = curl_easy_perform(curl))) + { + if (chunk->mm) + free(chunk->mm); + + free(chunk); + + nxml->priv.curl_error = ret; + + curl_easy_cleanup(curl); + return NXML_ERR_DOWNLOAD; + } + + curl_easy_cleanup(curl); + + *buffer = chunk->mm; + + if (size) + *size = chunk->size; + + free(chunk); + + return NXML_OK; +} + +/* EOF */ diff --git a/plugins/backend/decsync/libnxml/nxml_easy.c b/plugins/backend/decsync/libnxml/nxml_easy.c new file mode 100644 index 000000000..006905533 --- /dev/null +++ b/plugins/backend/decsync/libnxml/nxml_easy.c @@ -0,0 +1,392 @@ +/* nXml - Copyright (C) 2005-2007 bakunin - Andrea Marchesini + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "nxml.h" + +nxml_t * +nxmle_new_data(nxml_error_t *err) +{ + nxml_t *data = NULL; + nxml_error_t ret; + + ret = nxml_new(&data); + if (err) + *err = ret; + + if (ret != NXML_OK) + return NULL; + + return data; +} + +nxml_t * +nxmle_new_data_from_url(char *url, nxml_error_t *err) +{ + nxml_t *data = NULL; + nxml_error_t ret; + + ret = nxml_new(&data); + if (err) + *err = ret; + + if (ret != NXML_OK) + return NULL; + + nxml_set_func(data, nxml_print_generic); + + ret = nxml_parse_url(data, url); + if (err) + *err = ret; + + if (ret != NXML_OK) + { + nxml_free(data); + return NULL; + } + + return data; +} + +nxml_t * +nxmle_new_data_from_file(char *file, nxml_error_t *err) +{ + nxml_t *data = NULL; + nxml_error_t ret; + + ret = nxml_new(&data); + if (err) + *err = ret; + + if (ret != NXML_OK) + return NULL; + + nxml_set_func(data, nxml_print_generic); + + ret = nxml_parse_file(data, file); + if (err) + *err = ret; + + if (ret != NXML_OK) + { + nxml_free(data); + return NULL; + } + + return data; +} + +nxml_t * +nxmle_new_data_from_buffer(char *buffer, size_t size, nxml_error_t *err) +{ + nxml_t *data = NULL; + nxml_error_t ret; + + ret = nxml_new(&data); + if (err) + *err = ret; + + if (ret != NXML_OK) + return NULL; + + nxml_set_func(data, nxml_print_generic); + + ret = nxml_parse_buffer(data, buffer, size); + if (err) + *err = ret; + + if (ret != NXML_OK) + { + nxml_free(data); + return NULL; + } + + return data; +} + +nxml_data_t * +nxmle_add_new(nxml_t *nxml, nxml_data_t *parent, nxml_error_t *err) +{ + nxml_error_t ret; + nxml_data_t *child = NULL; + + ret = nxml_add(nxml, parent, &child); + if (err) + *err = ret; + + if (ret != NXML_OK) + return NULL; + + return child; +} + +nxml_data_t * +nxmle_add_data(nxml_t *nxml, nxml_data_t *parent, nxml_data_t *child, + nxml_error_t *err) +{ + nxml_error_t ret; + + if (!child) + { + if (err) + *err = NXML_ERR_DATA; + return NULL; + } + + ret = nxml_add(nxml, parent, &child); + if (err) + *err = ret; + + if (ret != NXML_OK) + return NULL; + + return child; +} + +nxml_attr_t * +nxmle_add_attribute_new(nxml_t *nxml, nxml_data_t *element, + nxml_error_t *err) +{ + nxml_error_t ret; + nxml_attr_t *attribute = NULL; + + ret = nxml_add_attribute(nxml, element, &attribute); + if (err) + *err = ret; + + if (ret != NXML_OK) + return NULL; + + return attribute; +} + +nxml_attr_t * +nxmle_add_attribute_data(nxml_t *nxml, nxml_data_t *element, + nxml_attr_t *attribute, nxml_error_t *err) +{ + nxml_error_t ret; + + if (!attribute) + { + if (err) + *err = NXML_ERR_DATA; + return NULL; + } + + ret = nxml_add_attribute(nxml, element, &attribute); + if (err) + *err = ret; + + if (ret != NXML_OK) + return NULL; + + return attribute; +} + +nxml_namespace_t * +nxmle_add_namespace_new(nxml_t *nxml, nxml_data_t *element, + nxml_error_t *err) +{ + nxml_error_t ret; + nxml_namespace_t *namespace = NULL; + + ret = nxml_add_namespace(nxml, element, &namespace); + if (err) + *err = ret; + + if (ret != NXML_OK) + return NULL; + + return namespace; +} + +nxml_namespace_t * +nxmle_add_namespace_data(nxml_t *nxml, nxml_data_t *element, + nxml_namespace_t *namespace, nxml_error_t *err) +{ + nxml_error_t ret; + + if (!namespace) + { + if (err) + *err = NXML_ERR_DATA; + return NULL; + } + + ret = nxml_add_namespace(nxml, element, &namespace); + if (err) + *err = ret; + + if (ret != NXML_OK) + return NULL; + + return namespace; +} + +nxml_data_t * +nxmle_root_element(nxml_t *nxml, nxml_error_t *err) +{ + nxml_data_t *root; + nxml_error_t ret; + + ret = nxml_root_element(nxml, &root); + if (err) + *err = ret; + + if (ret != NXML_OK) + return NULL; + + return root; +} + +nxml_doctype_t * +nxmle_doctype_element(nxml_t *nxml, nxml_error_t *err) +{ + nxml_doctype_t *doctype; + nxml_error_t ret; + + ret = nxml_doctype_element(nxml, &doctype); + if (err) + *err = ret; + + if (ret != NXML_OK) + return NULL; + + return doctype; +} + +nxml_data_t * +nxmle_find_element(nxml_t *nxml, nxml_data_t *data, char *name, + nxml_error_t *err) +{ + nxml_data_t *element; + nxml_error_t ret; + + ret = nxml_find_element(nxml, data, name, &element); + if (err) + *err = ret; + + if (ret != NXML_OK) + return NULL; + + return element; +} + +char * +nxmle_find_attribute(nxml_data_t *data, char *name, nxml_error_t *err) +{ + nxml_attr_t *attribute; + nxml_error_t ret; + char *str; + + ret = nxml_find_attribute(data, name, &attribute); + if (err) + *err = ret; + + if (ret != NXML_OK) + return NULL; + + if (!attribute) + return NULL; + + str = strdup(attribute->value); + if (!str) + { + if (err) + *err = NXML_ERR_POSIX; + return NULL; + } + + return str; +} + +char * +nxmle_find_namespace(nxml_data_t *data, char *name, nxml_error_t *err) +{ + nxml_namespace_t *namespace; + nxml_error_t ret; + char *str; + + ret = nxml_find_namespace(data, name, &namespace); + if (err) + *err = ret; + + if (ret != NXML_OK) + return NULL; + + if (!namespace) + return NULL; + + str = strdup(namespace->ns); + if (!str) + { + if (err) + *err = NXML_ERR_POSIX; + return NULL; + } + + return str; +} + +char * +nxmle_get_string(nxml_data_t *data, nxml_error_t *err) +{ + nxml_error_t ret; + char *str = NULL; + + ret = nxml_get_string(data, &str); + if (err) + *err = ret; + + if (ret != NXML_OK) + return NULL; + + return str; +} + +char * +nxmle_write_buffer(nxml_t *nxml, nxml_error_t *err) +{ + char *buffer; + nxml_error_t ret; + + buffer = NULL; + ret = nxml_write_buffer(nxml, &buffer); + + if (err) + *err = ret; + + if (ret != NXML_OK) + return NULL; + + return buffer; +} + +int nxmle_line_error(nxml_t *nxml, nxml_error_t *err) +{ + int line; + nxml_error_t ret; + + ret = nxml_line_error(nxml, &line); + + if (err) + *err = ret; + + return line; +} + +/* EOF */ diff --git a/plugins/backend/decsync/libnxml/nxml_edit.c b/plugins/backend/decsync/libnxml/nxml_edit.c new file mode 100644 index 000000000..597dcaa4b --- /dev/null +++ b/plugins/backend/decsync/libnxml/nxml_edit.c @@ -0,0 +1,184 @@ +/* nXml - Copyright (C) 2005-2007 bakunin - Andrea Marchesini + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "nxml.h" + +nxml_error_t +nxml_root_element(nxml_t *nxml, nxml_data_t **data) +{ + nxml_data_t *tmp; + + if (!data || !nxml) + return NXML_ERR_DATA; + + tmp = nxml->data; + while (tmp) + { + + if (tmp->type == NXML_TYPE_ELEMENT) + break; + + tmp = tmp->next; + } + + *data = tmp; + + return NXML_OK; +} + +nxml_error_t +nxml_doctype_element(nxml_t *nxml, nxml_doctype_t **data) +{ + if (!data || !nxml) + return NXML_ERR_DATA; + + *data = nxml->doctype; + return NXML_OK; +} + +nxml_error_t +nxml_get_string(nxml_data_t *data, char **string) +{ + if (!data || !string) + return NXML_ERR_DATA; + + if (data->type == NXML_TYPE_TEXT) + *string = strdup(data->value); + + else if (data->type == NXML_TYPE_ELEMENT) + { + nxml_data_t *tmp; + + tmp = data->children; + *string = NULL; + + while (tmp) + { + if (tmp->type == NXML_TYPE_TEXT) + { + *string = strdup(tmp->value); + break; + } + tmp = tmp->next; + } + } + else + *string = NULL; + + return NXML_OK; +} + +nxml_error_t +nxml_find_element(nxml_t *nxml, nxml_data_t *data, char *name, + nxml_data_t **element) +{ + nxml_data_t *tmp; + + if (!nxml || !name || !element) + return NXML_ERR_DATA; + + if (data && data->type != NXML_TYPE_ELEMENT) + { + *element = NULL; + return NXML_OK; + } + + if (data) + tmp = data->children; + else + tmp = nxml->data; + + while (tmp) + { + if (tmp->type == NXML_TYPE_ELEMENT && !strcmp(tmp->value, name)) + { + *element = tmp; + return NXML_OK; + } + + tmp = tmp->next; + } + + *element = NULL; + return NXML_OK; +} + +nxml_error_t +nxml_find_attribute(nxml_data_t *data, char *name, nxml_attr_t **attribute) +{ + nxml_attr_t *tmp; + + if (!data || !name || !attribute) + return NXML_ERR_DATA; + + if (data->type != NXML_TYPE_ELEMENT) + { + *attribute = NULL; + return NXML_OK; + } + + tmp = data->attributes; + + while (tmp) + { + if (!strcmp(tmp->name, name)) + { + *attribute = tmp; + return NXML_OK; + } + + tmp = tmp->next; + } + + *attribute = NULL; + return NXML_OK; +} + +nxml_error_t +nxml_find_namespace(nxml_data_t *data, char *name, + nxml_namespace_t **namespace) +{ + nxml_namespace_t *tmp; + + if (!data || !name || !namespace) + return NXML_ERR_DATA; + + if (data->type != NXML_TYPE_ELEMENT) + { + *namespace = NULL; + return NXML_OK; + } + + tmp = data->ns_list; + + while (tmp) + { + if (!strcmp(tmp->ns, name)) + { + *namespace = tmp; + return NXML_OK; + } + + tmp = tmp->next; + } + + *namespace = NULL; + return NXML_OK; +} + +/* EOF */ diff --git a/plugins/backend/decsync/libnxml/nxml_error.c b/plugins/backend/decsync/libnxml/nxml_error.c new file mode 100644 index 000000000..604fd6401 --- /dev/null +++ b/plugins/backend/decsync/libnxml/nxml_error.c @@ -0,0 +1,63 @@ +/* nXml - Copyright (C) 2005-2007 bakunin - Andrea Marchesini + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "nxml.h" + +char * +nxml_strerror(nxml_t *nxml, nxml_error_t err) +{ + switch (err) + { + case NXML_OK: + return "Success"; + + case NXML_ERR_PARSER: + return "Parser error"; + + case NXML_ERR_DOWNLOAD: + return nxml && nxml->priv.curl_error ? (char *)curl_easy_strerror(nxml->priv.curl_error) : "Download error"; + + case NXML_ERR_DATA: + return "No correct paramenter in the function"; + + default: + return strerror(errno); + } +} + +CURLcode +nxml_curl_error(nxml_t *nxml, nxml_error_t err) +{ + if (!nxml || err != NXML_ERR_DOWNLOAD) + return CURLE_OK; + + return nxml->priv.curl_error; +} + +nxml_error_t +nxml_line_error(nxml_t *nxml, int *line) +{ + if (!nxml || !line) + return NXML_ERR_DATA; + + *line = nxml->priv.line; + + return NXML_OK; +} + +/* EOF */ diff --git a/plugins/backend/decsync/libnxml/nxml_free.c b/plugins/backend/decsync/libnxml/nxml_free.c new file mode 100644 index 000000000..ae4ee0805 --- /dev/null +++ b/plugins/backend/decsync/libnxml/nxml_free.c @@ -0,0 +1,233 @@ +/* nXml - Copyright (C) 2005-2007 bakunin - Andrea Marchesini + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "nxml.h" + +static void __nxml_private_free(__nxml_private_t *priv); +static void __nxml_entity_free(__nxml_private_t *priv); + +nxml_error_t +nxml_free_data(nxml_data_t *data) +{ + nxml_namespace_t *namespace; + nxml_attr_t *attr; + nxml_data_t *tmp; + void *old; + + if (!data) + return NXML_ERR_DATA; + + if (data->value) + free(data->value); + + namespace = data->ns_list; + while (namespace) + { + old = namespace; + namespace = namespace->next; + + nxml_free_namespace(old); + } + + attr = data->attributes; + while (attr) + { + old = attr; + attr = attr->next; + + nxml_free_attribute(old); + } + + tmp = data->children; + while (tmp) + { + old = tmp; + tmp = tmp->next; + + nxml_free_data(old); + } + + free(data); + + return NXML_OK; +} + +nxml_error_t +nxml_free_attribute(nxml_attr_t *t) +{ + if (!t) + return NXML_ERR_DATA; + + if (t->name) + free(t->name); + + if (t->value) + free(t->value); + + free(t); + + return NXML_OK; +} + +nxml_error_t +nxml_free_namespace(nxml_namespace_t *t) +{ + if (!t) + return NXML_ERR_DATA; + + if (t->prefix) + free(t->prefix); + + if (t->ns) + free(t->ns); + + free(t); + + return NXML_OK; +} + +nxml_error_t +nxml_free_doctype(nxml_doctype_t *doctype) +{ + nxml_doctype_t *tmp; + + if (!doctype) + return NXML_ERR_DATA; + + while (doctype) + { + if (doctype->value) + free(doctype->value); + + if (doctype->name) + free(doctype->name); + + tmp = doctype; + doctype = doctype->next; + + free(tmp); + } + + return NXML_OK; +} + +nxml_error_t +nxml_free(nxml_t *data) +{ + if (!data) + return NXML_ERR_DATA; + + nxml_empty(data); + + __nxml_private_free(&data->priv); + + free(data); + + return NXML_OK; +} + +nxml_error_t +nxml_empty(nxml_t *data) +{ + nxml_data_t *t, *old; + __nxml_private_t priv; + + if (!data) + return NXML_ERR_DATA; + + if (data->file) + free(data->file); + + if (data->encoding) + free(data->encoding); + + /* I must free the doctype, I must not empty only! */ + if (data->doctype) + nxml_free_doctype(data->doctype); + + t = data->data; + while (t) + { + old = t; + t = t->next; + nxml_free_data(old); + } + + __nxml_entity_free(&data->priv); + + memcpy(&priv, &data->priv, sizeof(__nxml_private_t)); + memset(data, 0, sizeof(nxml_t)); + memcpy(&data->priv, &priv, sizeof(__nxml_private_t)); + + return NXML_OK; +} + +static void +__nxml_entity_free(__nxml_private_t *priv) +{ + __nxml_entity_t *entity; + + if (!priv) + return; + + while (priv->entities) + { + entity = priv->entities; + priv->entities = priv->entities->next; + + if (entity->entity) + free(entity->entity); + + if (entity->name) + free(entity->name); + + free(entity); + } +} + +static void +__nxml_private_free(__nxml_private_t *priv) +{ + if (!priv) + return; + + if (priv->proxy) + free(priv->proxy); + + if (priv->proxy_authentication) + free(priv->proxy_authentication); + + if (priv->certfile) + free(priv->certfile); + + if (priv->password) + free(priv->password); + + if (priv->cacert) + free(priv->cacert); + + if (priv->authentication) + free(priv->authentication); + + if (priv->user_agent) + free(priv->user_agent); + + __nxml_entity_free(priv); +} + +/* EOF */ diff --git a/plugins/backend/decsync/libnxml/nxml_init.c b/plugins/backend/decsync/libnxml/nxml_init.c new file mode 100644 index 000000000..e7789fa50 --- /dev/null +++ b/plugins/backend/decsync/libnxml/nxml_init.c @@ -0,0 +1,400 @@ +/* nXml - Copyright (C) 2005-2007 bakunin - Andrea Marchesini + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "nxml.h" + +nxml_error_t +nxml_new(nxml_t **nxml) +{ + if (!nxml) + return NXML_ERR_DATA; + + if (!(*nxml = (nxml_t *)calloc(1, sizeof(nxml_t)))) + return NXML_ERR_POSIX; + + return NXML_OK; +} + +static void +nxml_add_rec(nxml_t *nxml, nxml_data_t *data) +{ + while (data) + { + data->doc = nxml; + if (data->children) + nxml_add_rec(nxml, data->children); + + data = data->next; + } +} + +nxml_error_t +nxml_add(nxml_t *nxml, nxml_data_t *parent, nxml_data_t **child) +{ + nxml_data_t *tmp; + + if (!nxml || !child) + return NXML_ERR_DATA; + + if (!*child && !(*child = (nxml_data_t *)calloc(1, sizeof(nxml_data_t)))) + return NXML_ERR_POSIX; + + (*child)->doc = nxml; + (*child)->parent = parent; + (*child)->next = NULL; + + if (parent) + { + if (!parent->children) + parent->children = *child; + + else + { + tmp = parent->children; + + while (tmp->next) + tmp = tmp->next; + + tmp->next = *child; + } + } + else + { + if (!nxml->data) + nxml->data = *child; + + else + { + tmp = nxml->data; + + while (tmp->next) + tmp = tmp->next; + + tmp->next = *child; + } + } + + nxml_add_rec(nxml, (*child)->children); + + return NXML_OK; +} + +nxml_error_t +nxml_remove(nxml_t *nxml, nxml_data_t *parent, nxml_data_t *child) +{ + nxml_data_t *tmp, *old; + + if (!nxml || !child) + return NXML_ERR_DATA; + + if (parent) + tmp = parent->children; + else + tmp = nxml->data; + + old = NULL; + + while (tmp) + { + if (tmp == child) + { + if (old) + old->next = child->next; + else if (parent) + parent->children = child->next; + else + nxml->data = child->next; + + break; + } + + old = tmp; + tmp = tmp->next; + } + + child->next = NULL; + + return NXML_OK; +} + +nxml_error_t +nxml_add_attribute(nxml_t *nxml, nxml_data_t *element, nxml_attr_t **attr) +{ + nxml_attr_t *tmp; + + if (!nxml || !element || !attr) + return NXML_ERR_DATA; + + if (!*attr && !(*attr = (nxml_attr_t *)calloc(1, sizeof(nxml_attr_t)))) + return NXML_ERR_POSIX; + + (*attr)->next = NULL; + + if (!element->attributes) + element->attributes = *attr; + + else + { + tmp = element->attributes; + + while (tmp->next) + tmp = tmp->next; + + tmp->next = *attr; + } + + return NXML_OK; +} + +nxml_error_t +nxml_remove_attribute(nxml_t *nxml, nxml_data_t *element, + nxml_attr_t *attr) +{ + nxml_attr_t *tmp, *old; + + if (!nxml || !element || !attr) + return NXML_ERR_DATA; + + tmp = element->attributes; + + old = NULL; + + while (tmp) + { + if (tmp == attr) + { + if (old) + old->next = attr->next; + else + element->attributes = attr->next; + + break; + } + + old = tmp; + tmp = tmp->next; + } + + attr->next = NULL; + + return NXML_OK; +} + +nxml_error_t +nxml_add_namespace(nxml_t *nxml, nxml_data_t *element, + nxml_namespace_t **namespace) +{ + nxml_namespace_t *tmp; + + if (!nxml || !element || !namespace) + return NXML_ERR_DATA; + + if (!*namespace && !(*namespace = + (nxml_namespace_t *)calloc(1, sizeof(nxml_namespace_t)))) + return NXML_ERR_POSIX; + + (*namespace)->next = NULL; + + if (!element->ns_list) + element->ns_list = *namespace; + + else + { + tmp = element->ns_list; + + while (tmp->next) + tmp = tmp->next; + + tmp->next = *namespace; + } + + return NXML_OK; +} + +nxml_error_t +nxml_remove_namespace(nxml_t *nxml, nxml_data_t *element, + nxml_namespace_t *namespace) +{ + nxml_namespace_t *tmp, *old; + + if (!nxml || !element || !namespace) + return NXML_ERR_DATA; + + tmp = element->ns_list; + + old = NULL; + + while (tmp) + { + if (tmp == namespace) + { + if (old) + old->next = namespace->next; + else + element->ns_list = namespace->next; + + break; + } + + old = tmp; + tmp = tmp->next; + } + + namespace->next = NULL; + + return NXML_OK; +} + +nxml_error_t +nxml_set_func(nxml_t *nxml, void (*func)(char *, ...)) +{ + if (!nxml) + return NXML_ERR_DATA; + + nxml->priv.func = func; + + return NXML_OK; +} + +nxml_error_t +nxml_set_timeout(nxml_t *nxml, int timeout) +{ + if (!nxml) + return NXML_ERR_DATA; + + nxml->priv.timeout = timeout; + + return NXML_OK; +} + +nxml_error_t +nxml_set_proxy(nxml_t *nxml, char *proxy, char *userpwd) +{ + if (!nxml) + return NXML_ERR_DATA; + + if (nxml->priv.proxy) + free(nxml->priv.proxy); + + if (proxy) + nxml->priv.proxy = strdup(proxy); + else + nxml->priv.proxy = NULL; + + if (nxml->priv.proxy_authentication) + free(nxml->priv.proxy_authentication); + + if (userpwd) + nxml->priv.proxy_authentication = strdup(userpwd); + else + nxml->priv.proxy_authentication = NULL; + + return NXML_OK; +} + +nxml_error_t +nxml_set_authentication(nxml_t *nxml, char *userpwd) +{ + if (!nxml) + return NXML_ERR_DATA; + + if (nxml->priv.authentication) + free(nxml->priv.authentication); + + if (userpwd) + nxml->priv.authentication = strdup(userpwd); + else + nxml->priv.authentication = NULL; + + return NXML_OK; +} + +nxml_error_t +nxml_set_textindent(nxml_t *nxml, char textindent) +{ + if (!nxml) + return NXML_ERR_DATA; + + if (textindent) + nxml->priv.textindent = 1; + else + nxml->priv.textindent = 0; + + return NXML_OK; +} + +nxml_error_t +nxml_set_user_agent(nxml_t *nxml, char *user_agent) +{ + if (!nxml) + return NXML_ERR_DATA; + + if (nxml->priv.user_agent) + free(nxml->priv.user_agent); + + if (user_agent) + nxml->priv.user_agent = strdup(user_agent); + else + nxml->priv.user_agent = NULL; + + return NXML_OK; +} + +nxml_error_t +nxml_set_certificate(nxml_t *nxml, char *certificate, char *password, + char *cacert, int verifypeer) +{ + if (!nxml) + return NXML_ERR_DATA; + + if (nxml->priv.certfile) + free(nxml->priv.certfile); + + if (certificate) + nxml->priv.certfile = strdup(certificate); + else + nxml->priv.certfile = NULL; + + if (nxml->priv.password) + free(nxml->priv.password); + + if (password) + nxml->priv.password = strdup(password); + else + nxml->priv.password = NULL; + + if (cacert) + nxml->priv.cacert = strdup(cacert); + else + nxml->priv.cacert = NULL; + + nxml->priv.verifypeer = !verifypeer; + + return NXML_OK; +} + +void nxml_print_generic(char *str, ...) +{ + va_list va; + + va_start(va, str); + vfprintf(stderr, str, va); + va_end(va); +} + +/* EOF */ diff --git a/plugins/backend/decsync/libnxml/nxml_internal.h b/plugins/backend/decsync/libnxml/nxml_internal.h new file mode 100644 index 000000000..675b66542 --- /dev/null +++ b/plugins/backend/decsync/libnxml/nxml_internal.h @@ -0,0 +1,84 @@ +/* nXml - Copyright (C) 2005-2007 bakunin - Andrea Marchesini + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __N_XML_INTERNAL_H__ +#define __N_XML_INTERNAL_H__ + +/* Rule [4] */ +#define __NXML_NAMESTARTCHARS \ + ((ch = __NXML_U8()) == ':' || (ch >= 'a' && ch <= 'z') || ch == '_' || (ch >= 'A' && ch <= 'Z') || (ch >= 0xc0 && ch <= 0xd6) || (ch >= 0xd8 && ch <= 0xf6) || (ch >= 0xf8 && ch <= 0x2ff) || (ch >= 0x370 && ch <= 0x37d) || (ch >= 0x37f && ch <= 0x1fff) || (ch >= 0x200c && ch <= 0x200d) || (ch >= 0x2070 && ch <= 0x218f) || (ch >= 0x2c00 && ch <= 0x2fef) || (ch >= 0x3001 && ch <= 0xd7ff) || (ch >= 0xf900 && ch <= 0xfdcf) || (ch >= 0xfdf0 && ch <= 0xfffd) || (ch >= 0x10000 && ch <= 0xeffff)) + +/* Rule [4a] */ +#define __NXML_NAMECHARS \ + (__NXML_NAMESTARTCHARS || ch == '-' || ch == '.' || (ch >= '0' && ch <= '9') || ch == 0xb7 || (ch >= 0x0300 && ch <= 0x036f) || (ch >= 0x203f && ch <= 0x2040)) + +#define __NXML_U8() __nxml_utf8((unsigned char **)buffer, size, &byte) + +typedef struct __nxml_string_t__ __nxml_string_t; + +int64_t __nxml_utf8(unsigned char **buffer, + size_t *size, + int *byte); + +int64_t __nxml_int_charset(int i, + unsigned char *buffer, + char *charset); + +int __nxml_utf_detection(char *r_buffer, + size_t r_size, + char **buffer, + size_t *size, + nxml_charset_t *); + +int __nxml_escape_spaces(nxml_t *doc, + char **buffer, + size_t *size); + +char *__nxml_get_value(nxml_t *doc, + char **buffer, + size_t *size); + +char *__nxml_trim(char *tmp); + +/* nxml_string.c */ + +/** + * \brief + * For internal use only + */ +struct __nxml_string_t__ +{ + char *string; + size_t size; +}; + +__nxml_string_t *__nxml_string_new(void); + +int __nxml_string_add(__nxml_string_t *st, + char *what, + size_t size); + +char *__nxml_string_free(__nxml_string_t *st); + +void __nxml_namespace_parse(nxml_t *nxml); + +int __nxml_atoi(char *str); + +#endif + +/* EOF */ diff --git a/plugins/backend/decsync/libnxml/nxml_namespace.c b/plugins/backend/decsync/libnxml/nxml_namespace.c new file mode 100644 index 000000000..2f54670a9 --- /dev/null +++ b/plugins/backend/decsync/libnxml/nxml_namespace.c @@ -0,0 +1,354 @@ +/* nXml - Copyright (C) 2005-2007 bakunin - Andrea Marchesini + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "nxml.h" + +struct __nxml_data_ns_t +{ + nxml_namespace_t *ns; + struct __nxml_data_ns_t *next; +}; + +static void +__nxml_namespace_free_item(nxml_data_t *e) +{ + nxml_namespace_t *ns; + nxml_data_t *child; + + while (e->ns_list) + { + ns = e->ns_list->next; + + if (e->ns_list->prefix) + free(e->ns_list->prefix); + + if (e->ns_list->ns) + free(e->ns_list->ns); + + free(e->ns_list); + + e->ns_list = ns; + } + + e->ns = NULL; + + child = e->children; + while (child) + { + __nxml_namespace_free_item(child); + child = child->next; + } +} + +static void +__nxml_namespace_free(nxml_t *nxml) +{ + nxml_data_t *e; + + e = nxml->data; + while (e) + { + __nxml_namespace_free_item(e); + + e = e->next; + } +} + +int __nxml_namespace_parse_add(nxml_data_t *data, char *prefix, char *namespace) +{ + nxml_namespace_t *ns; + + if (!(ns = (nxml_namespace_t *)calloc(1, sizeof(nxml_namespace_t)))) + return 1; + + if (prefix && !(ns->prefix = strdup(prefix))) + { + free(ns); + return 1; + } + + if (!(ns->ns = strdup(namespace))) + { + if (ns->prefix) + free(ns->prefix); + free(ns); + return 1; + } + + ns->next = data->ns_list; + data->ns_list = ns; + + return 0; +} + +static int +__nxml_namespace_find_item(nxml_t *nxml, nxml_data_t *e) +{ + nxml_data_t *child; + nxml_attr_t *att; + + att = e->attributes; + while (att) + { + if (!strcmp(att->name, "xmlns")) + { + if (__nxml_namespace_parse_add(e, NULL, att->value)) + { + __nxml_namespace_free(nxml); + return 1; + } + } + else if (!strncmp(att->name, "xmlns:", 6)) + { + if (__nxml_namespace_parse_add(e, att->name + 6, att->value)) + { + __nxml_namespace_free(nxml); + return 1; + } + } + + att = att->next; + } + + child = e->children; + while (child) + { + if (child->type == NXML_TYPE_ELEMENT) + __nxml_namespace_find_item(nxml, child); + child = child->next; + } + + return 0; +} + +static int +__nxml_namespace_find(nxml_t *nxml) +{ + nxml_data_t *e; + + e = nxml->data; + while (e) + { + if (e->type == NXML_TYPE_ELEMENT) + __nxml_namespace_find_item(nxml, e); + e = e->next; + } + + return 0; +} + +static void +__nxml_namespace_associate_attribute(struct __nxml_data_ns_t *list, + nxml_attr_t *e) +{ + int i; + int len = strlen(e->name); + int k; + + for (i = k = 0; i < len; i++) + if (e->name[i] == ':') + { + k = i; + break; + } + + if (!k) + { + while (list) + { + if (!list->ns->prefix) + { + e->ns = list->ns; + return; + } + list = list->next; + } + + return; + } + + else + { + while (list) + { + if (list->ns->prefix && !strncmp(list->ns->prefix, e->name, k)) + { + char *a = strdup(e->name + strlen(list->ns->prefix) + 1); + + if (!a) + return; + + free(e->name); + e->name = a; + + e->ns = list->ns; + return; + } + list = list->next; + } + } +} + +static void +__nxml_namespace_associate_item(struct __nxml_data_ns_t *list, + nxml_data_t *e) +{ + int i; + int len; + int k; + nxml_attr_t *attr; + + attr = e->attributes; + while (attr) + { + __nxml_namespace_associate_attribute(list, attr); + attr = attr->next; + } + + len = strlen(e->value); + + for (i = k = 0; i < len; i++) + if (e->value[i] == ':') + { + k = i; + break; + } + + if (!k) + { + while (list) + { + if (!list->ns->prefix) + { + e->ns = list->ns; + return; + } + list = list->next; + } + + return; + } + + else + { + while (list) + { + if (list->ns->prefix && !strncmp(list->ns->prefix, e->value, k)) + { + char *a = strdup(e->value + strlen(list->ns->prefix) + 1); + + if (!a) + return; + + free(e->value); + e->value = a; + + e->ns = list->ns; + return; + } + list = list->next; + } + } +} + +static void +__nxml_namespace_associate(struct __nxml_data_ns_t **list, + nxml_data_t *root) +{ + nxml_data_t *e; + nxml_namespace_t *ns; + struct __nxml_data_ns_t *tmp, *old; + + ns = root->ns_list; + while (ns) + { + if (!(tmp = calloc(1, sizeof(struct __nxml_data_ns_t)))) + return; + + tmp->ns = ns; + tmp->next = (*list); + (*list) = tmp; + + ns = ns->next; + } + + __nxml_namespace_associate_item(*list, root); + + e = root->children; + while (e) + { + if (e->type == NXML_TYPE_ELEMENT) + __nxml_namespace_associate(list, e); + + e = e->next; + } + + ns = root->ns_list; + while (ns) + { + tmp = *list; + old = NULL; + + while (tmp) + { + if (tmp->ns == ns) + { + if (old) + old->next = tmp->next; + else + *list = tmp->next; + + free(tmp); + break; + } + + old = tmp; + tmp = tmp->next; + } + + ns = ns->next; + } +} + +static void +__nxml_namespace_connect(nxml_t *nxml) +{ + nxml_data_t *e; + struct __nxml_data_ns_t *list = NULL; + + e = nxml->data; + while (e) + { + if (e->type == NXML_TYPE_ELEMENT) + __nxml_namespace_associate(&list, e); + + e = e->next; + } +} + +void __nxml_namespace_parse(nxml_t *nxml) +{ + __nxml_namespace_free(nxml); + + if (__nxml_namespace_find(nxml)) + return; + + __nxml_namespace_connect(nxml); +} + +/* EOF */ diff --git a/plugins/backend/decsync/libnxml/nxml_parser.c b/plugins/backend/decsync/libnxml/nxml_parser.c new file mode 100644 index 000000000..8379aa018 --- /dev/null +++ b/plugins/backend/decsync/libnxml/nxml_parser.c @@ -0,0 +1,1482 @@ +/* nXml - Copyright (C) 2005-2007 bakunin - Andrea Marchesini + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "nxml.h" + +static int +__nxml_parse_unique_attribute(nxml_attr_t *attr, char *name) +{ + /* + * Rule [40] - Well-formedness contraint: Unique Att Spec + */ + + while (attr) + { + if (!strcmp(attr->name, name)) + return 1; + + attr = attr->next; + } + + return 0; +} + +static char * +__nxml_parse_string(nxml_t *doc, char *buffer, int size) +{ + char *real; + int i; + int q; + __nxml_string_t *ret; + + ret = __nxml_string_new(); + + for (q = i = 0; i < size; i++) + { + if (*(buffer + i) == 0xd) + continue; + + if (*(buffer + i) == 0xa || *(buffer + i) == 0x9 || *(buffer + i) == 0x20) + { + if (!q) + { + if (!doc->priv.textindent) + q = 1; + + __nxml_string_add(ret, buffer + i, 1); + } + } + + else if (*(buffer + i) == '&') + { + if (!strncmp(buffer + i, "<", 4)) + { + __nxml_string_add(ret, "<", 1); + i += 3; + q = 0; + } + + else if (!strncmp(buffer + i, ">", 4)) + { + __nxml_string_add(ret, ">", 1); + i += 3; + q = 0; + } + + else if (!strncmp(buffer + i, "&", 5)) + { + __nxml_string_add(ret, "&", 1); + i += 4; + q = 0; + } + + else if (!strncmp(buffer + i, "'", 6)) + { + __nxml_string_add(ret, "'", 1); + i += 5; + q = 0; + } + + else if (!strncmp(buffer + i, """, 6)) + { + __nxml_string_add(ret, "\"", 1); + i += 5; + q = 0; + } + + else if (*(buffer + i + 1) == '#') + { + char buf[2048]; + int k = i; + int last; + + while (*(buffer + k) != ';' && k < size) + k++; + + last = k - (i + 2) > sizeof(buf) ? sizeof(buf) : k - (i + 2); + strncpy(buf, buffer + i + 2, last); + buf[last] = 0; + + int value; + if (buf[0] != 'x') + value = atoi(buf); + else + value = __nxml_atoi(&buf[1]); + + int num_bytes; + if ((num_bytes = __nxml_int_charset(value, (unsigned char *)buf, doc->encoding)) > 0) + { + __nxml_string_add(ret, buf, num_bytes); + } + else + __nxml_string_add(ret, buffer + i, 1); + i += k - i; + q = 0; + } + + else + { + __nxml_entity_t *entity; + char buf[1024]; + int k = i; + int last; + + while (*(buffer + k) != ';' && k < size) + k++; + + last = k - (i + 1) > sizeof(buf) ? sizeof(buf) : k - (i + 1); + strncpy(buf, buffer + i + 1, last); + buf[last] = 0; + + for (entity = doc->priv.entities; entity; entity = entity->next) + { + if (!strcmp(entity->name, buf)) + { + __nxml_string_add(ret, entity->entity, + strlen(entity->entity)); + break; + } + } + + if (!entity) + __nxml_string_add(ret, buffer + i, 1); + else + i += strlen(entity->name) + 1; + + q = 0; + } + } + + else + { + q = 0; + __nxml_string_add(ret, buffer + i, 1); + } + } + + if (!(real = __nxml_string_free(ret))) + real = strdup(""); + + return real; +} + +static char * +__nxml_parse_get_attr(nxml_t *doc, char **buffer, size_t *size) +{ + char attr[1024]; + int i; + int byte; + int64_t ch; + + if (!*size) + return NULL; + + if (!__NXML_NAMESTARTCHARS) + return NULL; + + memcpy(&attr[0], *buffer, byte); + + i = byte; + (*buffer) += byte; + (*size) -= byte; + + while (__NXML_NAMECHARS && *size && i < sizeof(attr) - 1) + { + memcpy(&attr[i], *buffer, byte); + + i += byte; + (*buffer) += byte; + (*size) -= byte; + } + + if (**buffer != 0x20 && **buffer != 0x9 && **buffer != 0xa && **buffer != 0xd && **buffer != '=') + { + (*buffer) -= i; + (*size) += i; + return NULL; + } + + i += __nxml_escape_spaces(doc, buffer, size); + + if (**buffer != '=') + { + (*buffer) -= i; + (*size) += i; + return NULL; + } + + (*buffer)++; + (*size)--; + + __nxml_escape_spaces(doc, buffer, size); + + attr[i] = 0; + return strdup(attr); +} + +static nxml_error_t +__nxml_parse_get_attribute(nxml_t *doc, char **buffer, size_t *size, + nxml_attr_t **attr) +{ + /* + * Rule [41] - Attribute ::= Name Eq AttValue + * Rule [25] - Eq ::= S? '=' S? + * Rule [5] - Name ::= NameStartChar (NameChar)* + * Rule [4] - NameStartChar ::= ":" | [A-Z] | ["_"] | [a-z] | Unicode... + * Rule [4a] - NameChar ::= NameStarChar | "-" | "." | [0-9] | Unicode... + * Rule [10] - AttValue ::= '"' ([^<&"] | Reference)* '"' | + * "'" ([^<&'] | Reference)* "'" + */ + char *tag, *value, *v; + + if (!*size) + return NXML_OK; + + *attr = NULL; + + __nxml_escape_spaces(doc, buffer, size); + + if (!(tag = __nxml_parse_get_attr(doc, buffer, size))) + return NXML_OK; + + if (!(value = __nxml_get_value(doc, buffer, size))) + { + free(tag); + + if (doc->priv.func) + doc->priv.func("%s: expected value of attribute (line %d)\n", + doc->file ? doc->file : "", doc->priv.line); + return NXML_ERR_PARSER; + } + + if (!(v = __nxml_parse_string(doc, value, strlen(value)))) + { + free(tag); + return NXML_ERR_POSIX; + } + + free(value); + value = v; + + __nxml_escape_spaces(doc, buffer, size); + + if (!(*attr = (nxml_attr_t *)calloc(1, sizeof(nxml_attr_t)))) + { + free(tag); + free(value); + + return NXML_ERR_POSIX; + } + + (*attr)->name = tag; + (*attr)->value = value; + + return NXML_OK; +} + +static nxml_error_t +__nxml_parse_cdata(nxml_t *doc, char **buffer, size_t *size, + nxml_data_t **data) +{ + /* + * Rule [18] - CDSect ::= CDStart CData CDEnd + * Rule [19] - CDStart ::= '' Char *)) + * Rule [21] - CDEnd ::= ']]>' + */ + + int i = 0; + nxml_data_t *t; + char *value; + + while (i < *size) + { + if (!strncmp(*buffer + i, "]]>", 3)) + break; + + if (*(*buffer + i) == 0xa && doc->priv.func) + doc->priv.line++; + + i++; + } + + if (strncmp(*buffer + i, "]]>", 3)) + { + if (doc->priv.func) + doc->priv.func("%s: expected ']]>' as close of a CDATA (line %d)\n", + doc->file ? doc->file : "", doc->priv.line); + + return NXML_ERR_PARSER; + } + + if (!(t = (nxml_data_t *)calloc(1, sizeof(nxml_data_t)))) + return NXML_ERR_POSIX; + + t->doc = doc; + + if (!(value = (char *)malloc(sizeof(char) * (i + 1)))) + { + free(t); + return NXML_ERR_POSIX; + } + + strncpy(value, *buffer, i); + value[i] = 0; + + t->value = value; + t->type = NXML_TYPE_TEXT; + + (*buffer) += i + 3; + (*size) -= i + 3; + + *data = t; + + return NXML_OK; +} + +static void +__nxml_parse_entity(nxml_t *doc, char **buffer, size_t *size) +{ + /* + * [70] EntityDecl ::= GEDecl | PEDecl + * [71] GEDecl ::= '' + * [72] PEDecl ::= '' + * [73] EntityDef ::= EntityValue | (ExternalID NDataDecl?) + * [74] PEDef ::= EntityValue | ExternalID + */ + + int i; + char name[1024]; + char *entity; + int byte; + int64_t ch; + + __nxml_escape_spaces(doc, buffer, size); + + if (strncmp(*buffer, "' || q) && i < *size) + { + if (*(*buffer + i) == '<') + q++; + + else if (*(*buffer + i) == '>') + q--; + + i++; + } + + if (*(*buffer) == '>') + i++; + + (*buffer) += i; + (*size) -= i; + return; + } + + *buffer += 8; + *size -= 8; + + __nxml_escape_spaces(doc, buffer, size); + + /* Name */ + if (!__NXML_NAMESTARTCHARS) + { + int q = i = 0; + + while ((*(*buffer + i) != '>' || q) && i < *size) + { + if (*(*buffer + i) == '<') + q++; + + else if (*(*buffer + i) == '>') + q--; + + i++; + } + + if (*(*buffer) == '>') + i++; + + (*buffer) += i; + (*size) -= i; + return; + } + + memcpy(&name[0], *buffer, byte); + + i = byte; + (*buffer) += byte; + (*size) -= byte; + + while (__NXML_NAMECHARS && *size && i < sizeof(name) - 1) + { + memcpy(&name[i], *buffer, byte); + + i += byte; + (*buffer) += byte; + (*size) -= byte; + } + + name[i] = 0; + + if (!i || !strcmp(name, "%")) + { + int q = i = 0; + + while ((*(*buffer + i) != '>' || q) && i < *size) + { + if (*(*buffer + i) == '<') + q++; + + else if (*(*buffer + i) == '>') + q--; + + i++; + } + + if (*(*buffer) == '>') + i++; + + (*buffer) += i; + (*size) -= i; + return; + } + + __nxml_escape_spaces(doc, buffer, size); + + entity = __nxml_get_value(doc, buffer, size); + + __nxml_escape_spaces(doc, buffer, size); + + if (**buffer == '>') + { + (*buffer)++; + (*size)--; + } + + if (entity) + { + __nxml_entity_t *e; + + if (!(e = calloc(1, sizeof(__nxml_entity_t)))) + { + free(entity); + return; + } + + if (!(e->name = strdup(name))) + { + free(e); + free(entity); + return; + } + + e->entity = entity; + + if (!doc->priv.entities) + doc->priv.entities = e; + else + { + __nxml_entity_t *tmp = doc->priv.entities; + + while (tmp->next) + tmp = tmp->next; + + tmp->next = e; + } + } +} + +static nxml_error_t +__nxml_parse_doctype(nxml_t *doc, char **buffer, size_t *size, + int *doctype) +{ + /* + * Rule [28] - doctypedecl ::= '' + */ + + nxml_doctype_t *t; + nxml_doctype_t *tmp; + char str[1024]; + char *value; + int i; + int byte; + int64_t ch; + int q = 0; + + __nxml_escape_spaces(doc, buffer, size); + + if (!__NXML_NAMESTARTCHARS) + { + if (doc->priv.func) + doc->priv.func("%s: abnormal char '%c' (line %d)\n", + doc->file ? doc->file : "", **buffer, doc->priv.line); + return NXML_ERR_PARSER; + } + + memcpy(&str[0], *buffer, byte); + + i = byte; + (*buffer) += byte; + (*size) -= byte; + + while (__NXML_NAMECHARS && *size && i < sizeof(str) - 1) + { + memcpy(&str[i], *buffer, byte); + + i += byte; + (*buffer) += byte; + (*size) -= byte; + } + + str[i] = 0; + + if (!(t = (nxml_doctype_t *)calloc(1, sizeof(nxml_doctype_t)))) + return NXML_ERR_POSIX; + + t->doc = doc; + + if (!(t->name = strdup(str))) + { + free(t); + return NXML_ERR_POSIX; + } + + __nxml_escape_spaces(doc, buffer, size); + + i = 0; + while ((*(*buffer + i) != '>' || q) && i < *size) + { + if (*(*buffer + i) == '<') + q++; + + else if (*(*buffer + i) == '>') + q--; + + if (*(*buffer + i) == 0xa && doc->priv.func) + doc->priv.line++; + + i++; + } + + if (*(*buffer + i) != '>') + { + if (doc->priv.func) + doc->priv.func("%s: expected '>' as close of a doctype (line %d)\n", + doc->file ? doc->file : "", doc->priv.line); + + free(t->value); + free(t); + return NXML_ERR_PARSER; + } + + if (!(value = __nxml_parse_string(doc, *buffer, i))) + { + free(t->value); + free(t); + return NXML_ERR_POSIX; + } + + t->value = value; + + (*buffer) += i + 1; + (*size) -= i + 1; + + tmp = doc->doctype; + if (!tmp) + doc->doctype = t; + + else + { + while (tmp->next) + tmp = tmp->next; + + tmp->next = t; + } + + *doctype = 1; + + while (value && *value && *value != '[') + value++; + + if (value && *value == '[') + { + unsigned int size; + + value++; + size = strlen(value); + + while (size > 0 && value && *value) + __nxml_parse_entity(doc, &value, (size_t *)&size); + } + + return NXML_OK; +} + +static nxml_error_t +__nxml_parse_comment(nxml_t *doc, char **buffer, size_t *size, + nxml_data_t **data) +{ + /* + * Rule [15] - Comment ::= '' + */ + + int i = 0; + nxml_data_t *t; + char *value; + + while (strncmp(*buffer + i, "-->", 3) && i < *size) + { + if (*(*buffer + i) == 0xa && doc->priv.func) + doc->priv.line++; + i++; + } + + if (strncmp(*buffer + i, "-->", 3)) + { + if (doc->priv.func) + doc->priv.func("%s: expected '--' as close comment (line %d)\n", + doc->file ? doc->file : "", doc->priv.line); + + return NXML_ERR_PARSER; + } + + if (!(t = (nxml_data_t *)calloc(1, sizeof(nxml_data_t)))) + return NXML_ERR_POSIX; + + t->doc = doc; + + if (!(value = __nxml_parse_string(doc, *buffer, i))) + { + free(t); + return NXML_ERR_POSIX; + } + + t->value = value; + + (*buffer) += i + 3; + (*size) -= i + 3; + + t->type = NXML_TYPE_COMMENT; + + *data = t; + + return NXML_OK; +} + +static nxml_error_t +__nxml_parse_pi(nxml_t *doc, char **buffer, size_t *size, + nxml_data_t **data) +{ + /* + * Rule [16] - PI ::= '' Char *)))? + * '?>' + * Rule [17] - PITarget ::= Name - (('X' | 'x') ('M' | 'm') ('L' | 'l')) + */ + + int i = 0; + nxml_data_t *t; + char *value; + + if (!*size) + return NXML_OK; + + *data = NULL; + + (*buffer) += 1; + (*size) -= 1; + + while (strncmp(*buffer + i, "?>", 2) && i < *size) + { + if (*(*buffer + i) == 0xa && doc->priv.func) + doc->priv.line++; + i++; + } + + if (strncmp(*buffer + i, "?>", 2)) + { + if (doc->priv.func) + doc->priv.func("%s: expected '?' as close pi tag (line %d)\n", + doc->file ? doc->file : "", doc->priv.line); + + return NXML_ERR_PARSER; + } + + if (!strncasecmp(*buffer, "?xml", 4)) + { + if (doc->priv.func) + doc->priv.func("%s: pi xml is reserved! (line %d)\n", + doc->file ? doc->file : "", doc->priv.line); + + return NXML_ERR_PARSER; + } + + if (!(t = (nxml_data_t *)calloc(1, sizeof(nxml_data_t)))) + return NXML_ERR_POSIX; + + t->doc = doc; + + if (!(value = __nxml_parse_string(doc, *buffer, i))) + { + free(t); + return NXML_ERR_POSIX; + } + + t->value = value; + + (*buffer) += i + 2; + (*size) -= i + 2; + + t->type = NXML_TYPE_PI; + + *data = t; + + return NXML_OK; +} + +static nxml_error_t +__nxml_parse_other(nxml_t *doc, char **buffer, size_t *size, + nxml_data_t **data, int *doctype) +{ + /* Tags 'priv.func) + doc->priv.func("%s: abnormal tag (line %d)\n", + doc->file ? doc->file : "", doc->priv.line); + + return NXML_ERR_PARSER; + } +} + +static nxml_error_t +__nxml_parse_text(nxml_t *doc, char **buffer, size_t *size, + nxml_data_t **data) +{ + int i = 0; + nxml_data_t *t; + char *value; + char *value_new; + + *data = NULL; + + if (!*size) + return NXML_OK; + + while (*(*buffer + i) != '<' && i < *size) + { + if (*(*buffer + i) == 0xa && doc->priv.func) + doc->priv.line++; + i++; + } + + if (!(t = (nxml_data_t *)calloc(1, sizeof(nxml_data_t)))) + return NXML_ERR_POSIX; + + t->doc = doc; + + if (!(value = __nxml_parse_string(doc, *buffer, i))) + { + free(t); + return NXML_ERR_POSIX; + } + + (*buffer) += i; + (*size) -= i; + + value_new = __nxml_trim(value); + free(value); + + if (!value_new) + { + free(t); + return NXML_ERR_POSIX; + } + + t->value = value_new; + t->type = NXML_TYPE_TEXT; + + *data = t; + + return NXML_OK; +} + +static nxml_error_t +__nxml_parse_close(nxml_t *doc, char **buffer, size_t *size, + nxml_data_t **data) +{ + /* + * Rule [42] - ETag ::= '' + */ + + nxml_data_t *tag; + char str[1024]; + int i; + int byte; + int64_t ch; + + *data = NULL; + + if (!*size) + return NXML_OK; + + if (!__NXML_NAMESTARTCHARS) + { + if (doc->priv.func) + doc->priv.func("%s: abnormal char '%c' (line %d)\n", + doc->file ? doc->file : "", **buffer, doc->priv.line); + return NXML_ERR_PARSER; + } + + memcpy(&str[0], *buffer, byte); + + i = byte; + (*buffer) += byte; + (*size) -= byte; + + while (__NXML_NAMECHARS && *size && i < sizeof(str) - 1) + { + memcpy(&str[i], *buffer, byte); + + i += byte; + (*buffer) += byte; + (*size) -= byte; + } + + str[i] = 0; + + __nxml_escape_spaces(doc, buffer, size); + + if (**buffer != '>') + { + if (doc->priv.func) + doc->priv.func("%s: expected char '>' after tag %s (line %d)\n", + doc->file ? doc->file : "", str, doc->priv.line); + return NXML_ERR_PARSER; + } + + (*buffer) += 1; + (*size) -= 1; + + if (!(tag = (nxml_data_t *)calloc(1, sizeof(nxml_data_t)))) + return NXML_ERR_POSIX; + + tag->doc = doc; + + if (!(tag->value = strdup(str))) + { + free(tag); + return NXML_ERR_POSIX; + } + + tag->type = NXML_TYPE_ELEMENT_CLOSE; + + *data = tag; + + return NXML_OK; +} + +static nxml_error_t +__nxml_parse_get_tag(nxml_t *doc, char **buffer, size_t *size, + nxml_data_t **data, int *doctype) +{ + /* Parse the element... */ + nxml_attr_t *attr, *last; + nxml_data_t *tag, *child, *child_last; + nxml_error_t err; + + char str[1024]; + int i; + int closed = 0; + int byte; + int64_t ch; + + *data = NULL; + *doctype = 0; + + if (!*size) + return NXML_OK; + + __nxml_escape_spaces(doc, buffer, size); + + /* Text */ + if (**buffer != '<') + return __nxml_parse_text(doc, buffer, size, data); + + (*buffer) += 1; + (*size) -= 1; + + /* Comment, CData, DocType or other elements */ + if (**buffer == '!') + return __nxml_parse_other(doc, buffer, size, data, doctype); + + /* PI */ + else if (**buffer == '?') + return __nxml_parse_pi(doc, buffer, size, data); + + /* Close tag */ + else if (**buffer == '/') + { + (*buffer) += 1; + (*size) -= 1; + return __nxml_parse_close(doc, buffer, size, data); + } + + __nxml_escape_spaces(doc, buffer, size); + + if (!__NXML_NAMESTARTCHARS) + { + if (doc->priv.func) + doc->priv.func("%s: abnormal char '%c' (line %d)\n", + doc->file ? doc->file : "", **buffer, doc->priv.line); + return NXML_ERR_PARSER; + } + + memcpy(&str[0], *buffer, byte); + + i = byte; + (*buffer) += byte; + (*size) -= byte; + + while (__NXML_NAMECHARS && *size && i < sizeof(str) - 1) + { + memcpy(&str[i], *buffer, byte); + + i += byte; + (*buffer) += byte; + (*size) -= byte; + } + + str[i] = 0; + + if (**buffer != 0x20 && **buffer != 0x9 && **buffer != 0xa && **buffer != 0xd && **buffer != '>' && **buffer != '/') + { + (*buffer) -= i; + (*size) += i; + + if (doc->priv.func) + doc->priv.func("%s: abnormal char '%c' after tag %s (line %d)\n", + doc->file ? doc->file : "", *(*buffer + i), str, + doc->priv.line); + + return NXML_ERR_PARSER; + } + + if (!(tag = (nxml_data_t *)calloc(1, sizeof(nxml_data_t)))) + return NXML_ERR_POSIX; + + tag->doc = doc; + + if (!(tag->value = strdup(str))) + { + free(tag); + return NXML_ERR_POSIX; + } + + last = NULL; + + /* Get attribute: */ + while (!(err = __nxml_parse_get_attribute(doc, buffer, size, &attr)) && attr) + { + if (!*size) + return NXML_ERR_PARSER; + + if (__nxml_parse_unique_attribute(tag->attributes, attr->name)) + { + if (doc->priv.func) + doc->priv.func("%s: Duplicate attribute '%s' in tag '%s' (line %d)\n", + doc->file ? doc->file : "", attr->name, tag->value, + doc->priv.line); + + nxml_free_attribute(attr); + nxml_free_data(tag); + + return NXML_ERR_PARSER; + } + + if (last) + last->next = attr; + else + tag->attributes = attr; + + last = attr; + } + + if (err) + { + nxml_free_data(tag); + return err; + } + + /* Closed element */ + if (**buffer == '/') + { + closed++; + (*buffer) += 1; + (*size) -= 1; + + __nxml_escape_spaces(doc, buffer, size); + } + + if (**buffer != '>') + { + if (doc->priv.func) + doc->priv.func("%s: expected char '>' after tag %s (line %d)\n", + doc->file ? doc->file : "", tag->value, + doc->priv.line); + + nxml_free_data(tag); + return NXML_ERR_PARSER; + } + + (*buffer) += 1; + (*size) -= 1; + + if (!closed) + { + child_last = NULL; + + /* Search children: */ + while (!(err = __nxml_parse_get_tag(doc, buffer, size, &child, doctype)) && (*doctype || child)) + { + if (*doctype) + continue; + + /* If the current child, break: */ + if (child->type == NXML_TYPE_ELEMENT_CLOSE) + { + if (!strcmp(child->value, tag->value)) + { + closed = 1; + nxml_free_data(child); + break; + } + + else + { + if (doc->priv.func) + doc->priv.func("%s: expected tag '/%s' and not '%s' (line %d)\n", + doc->file ? doc->file : "", tag->value, + child->value, doc->priv.line); + + nxml_free_data(child); + nxml_free_data(tag); + + return NXML_ERR_PARSER; + } + } + + /* Set the parent */ + child->parent = tag; + + if (child_last) + child_last->next = child; + else + tag->children = child; + + child_last = child; + } + + if (err) + { + nxml_free_data(tag); + return err; + } + } + + tag->type = NXML_TYPE_ELEMENT; + + if (!closed) + { + if (doc->priv.func) + doc->priv.func("%s: expected tag '/%s' (line %d)\n", + doc->file ? doc->file : "", tag->value, + doc->priv.line); + + nxml_free_data(tag); + return NXML_ERR_PARSER; + } + + *data = tag; + return NXML_OK; +} + +static nxml_error_t +__nxml_parse_buffer(nxml_t *nxml, char *r_buffer, size_t r_size) +{ + /* + * Rule [1] - Document ::= prolog element Misc* - Char* RestrictedChar Char* + */ + + nxml_attr_t *attr; + nxml_error_t err; + nxml_charset_t charset; + nxml_data_t *tag, *last, *root; + int doctype; + + int freed; + + char *buffer = NULL; + size_t size; + + if (!r_buffer || !nxml) + return NXML_ERR_DATA; + + if (!r_size) + r_size = strlen(r_buffer); + + switch ((freed = + __nxml_utf_detection(r_buffer, r_size, &buffer, &size, &charset))) + { + case 0: + buffer = r_buffer; + size = r_size; + break; + + case -1: + return NXML_ERR_POSIX; + } + + nxml->priv.line = 1; + nxml->version = NXML_VERSION_1_0; + nxml->standalone = 1; + + /* + * Rule [22] - prolog ::= XMLDecl Misc* (doctypedecl Misc*)? + * Rule [23] - XMLDecl ::= '' + */ + if (!strncmp(buffer, "priv.func) + nxml->priv.func("%s: expected 'version' attribute (line %d)\n", + nxml->file ? nxml->file : "", nxml->priv.line); + + if (freed) + free(buffer); + + return NXML_ERR_PARSER; + } + + if (!strcmp(attr->value, "1.0")) + nxml->version = NXML_VERSION_1_0; + + else if (!strcmp(attr->value, "1.1")) + nxml->version = NXML_VERSION_1_1; + + else + { + if (nxml->priv.func) + nxml->priv.func("libnxml 0.18.3 suports only xml 1.1 or 1.0 (line %d)\n", + nxml->priv.line); + + if (freed) + free(buffer); + + return NXML_ERR_PARSER; + } + + nxml_free_attribute(attr); + + while (!(err = __nxml_parse_get_attribute(nxml, &buffer, &size, &attr)) && attr) + { + if (!strcmp(attr->name, "standalone")) + { + if (!strcmp(attr->value, "yes")) + nxml->standalone = 1; + + else + nxml->standalone = 0; + } + + else if (!strcmp(attr->name, "encoding")) + { + nxml->encoding = strdup(attr->value); + + if (!nxml->encoding) + { + nxml_empty(nxml); + nxml_free_attribute(attr); + + if (freed) + free(buffer); + + return NXML_ERR_POSIX; + } + } + + else + { + + if (nxml->priv.func) + nxml->priv.func("%s: unexpected attribute '%s' (line %d)\n", + nxml->file ? nxml->file : "", attr->name, + nxml->priv.line); + + nxml_empty(nxml); + nxml_free_attribute(attr); + + if (freed) + free(buffer); + + return NXML_ERR_PARSER; + } + + nxml_free_attribute(attr); + } + + if (err || strncmp(buffer, "?>", 2)) + { + if (nxml->priv.func) + nxml->priv.func("%s expected '?>' (line %d)\n", + nxml->file ? nxml->file : "", nxml->priv.line); + + nxml_empty(nxml); + + if (freed) + free(buffer); + + return NXML_ERR_PARSER; + } + + buffer += 2; + size -= 2; + } + + root = last = NULL; + while (!(err = __nxml_parse_get_tag(nxml, &buffer, &size, &tag, &doctype)) && (doctype || tag)) + { + if (doctype) + continue; + + if (tag->type == NXML_TYPE_ELEMENT && !root) + root = tag; + + if (last) + last->next = tag; + else + nxml->data = tag; + + last = tag; + } + + if (err) + { + nxml_empty(nxml); + + if (freed) + free(buffer); + + return NXML_ERR_PARSER; + } + + if (!root) + { + if (nxml->priv.func) + nxml->priv.func("%s: No root element founded!\n", + nxml->file ? nxml->file : ""); + + nxml_empty(nxml); + + if (freed) + free(buffer); + + return NXML_ERR_PARSER; + } + + if (freed) + free(buffer); + + nxml->charset_detected = charset; + + __nxml_namespace_parse(nxml); + + return NXML_OK; +} + +/* EXTERNAL FUNCTIONS *******************************************************/ + +nxml_error_t +nxml_parse_url(nxml_t *nxml, char *url) +{ + nxml_error_t err; + char *buffer; + size_t size; + + if (!url || !nxml) + return NXML_ERR_DATA; + + if ((err = nxml_download_file(nxml, url, &buffer, &size)) != NXML_OK) + return err; + + if (nxml->file) + free(nxml->file); + + if (!(nxml->file = strdup(url))) + { + nxml_empty(nxml); + return NXML_ERR_POSIX; + } + + nxml->size = size; + + nxml_empty(nxml); + + err = __nxml_parse_buffer(nxml, buffer, size); + + free(buffer); + + return err; +} + +nxml_error_t +nxml_parse_file(nxml_t *nxml, char *file) +{ + nxml_error_t err; + char *buffer; + struct stat st; + int fd, len, ret; + + if (!file || !nxml) + return NXML_ERR_DATA; + + if (stat(file, &st)) + return NXML_ERR_POSIX; + + if ((fd = open(file, O_RDONLY)) < 0) + return NXML_ERR_POSIX; + + if (!(buffer = (char *)malloc(sizeof(char) * (st.st_size + 1)))) + return NXML_ERR_POSIX; + + len = 0; + + while (len < st.st_size) + { + if ((ret = read(fd, buffer + len, st.st_size - len)) <= 0) + { + free(buffer); + close(fd); + return NXML_ERR_POSIX; + } + + len += ret; + } + + buffer[len] = 0; + close(fd); + + nxml_empty(nxml); + + if (nxml->file) + free(nxml->file); + + if (!(nxml->file = strdup(file))) + { + nxml_empty(nxml); + free(buffer); + return NXML_ERR_POSIX; + } + + nxml->size = st.st_size; + + err = __nxml_parse_buffer(nxml, buffer, st.st_size); + + free(buffer); + return err; +} + +nxml_error_t +nxml_parse_buffer(nxml_t *nxml, char *buffer, size_t size) +{ + if (!buffer || !nxml) + return NXML_ERR_DATA; + + nxml_empty(nxml); + + if (nxml->file) + free(nxml->file); + + if (!(nxml->file = strdup("buffer"))) + { + nxml_empty(nxml); + return NXML_ERR_POSIX; + } + + nxml->size = size; + + return __nxml_parse_buffer(nxml, buffer, size); +} + +/* EOF */ diff --git a/plugins/backend/decsync/libnxml/nxml_string.c b/plugins/backend/decsync/libnxml/nxml_string.c new file mode 100644 index 000000000..f5d4fd492 --- /dev/null +++ b/plugins/backend/decsync/libnxml/nxml_string.c @@ -0,0 +1,116 @@ +/* nXml - Copyright (C) 2005-2007 bakunin - Andrea Marchesini + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "nxml.h" + +char * +__nxml_string_no_space(char *str) +{ + char *ret; + int i, j; + int q; + int size; + + if (!str) + return NULL; + + size = strlen(str); + + if (!(ret = (char *)malloc(sizeof(char) * (size + 1)))) + return NULL; + + for (q = i = j = 0; i < size; i++) + { + if (*(str + i) == 0xd) + continue; + + if (*(str + i) == 0xa || *(str + i) == 0x9 || *(str + i) == 0x20) + { + if (!q) + { + q = 1; + ret[j++] = *(str + i); + } + } + + else + { + q = 0; + ret[j++] = *(str + i); + } + } + + ret[j] = 0; + + return ret; +} + +__nxml_string_t * +__nxml_string_new(void) +{ + __nxml_string_t *st; + + if (!(st = (__nxml_string_t *)calloc(1, sizeof(__nxml_string_t)))) + return NULL; + + return st; +} + +char * +__nxml_string_free(__nxml_string_t *st) +{ + char *ret; + + if (!st) + return NULL; + + ret = st->string; + free(st); + + return ret; +} + +int __nxml_string_add(__nxml_string_t *st, char *what, size_t size) +{ + if (!st || !*what) + return 1; + + if (size <= 0) + size = strlen(what); + + if (!st->size) + { + if (!(st->string = (char *)malloc(sizeof(char) * (size + 1)))) + return 1; + } + else + { + if (!(st->string = + (char *)realloc(st->string, + sizeof(char) * (size + st->size + 1)))) + return 1; + } + + memcpy(st->string + st->size, what, size); + st->size += size; + st->string[st->size] = 0; + + return 0; +} + +/* EOF */ diff --git a/plugins/backend/decsync/libnxml/nxml_tools.c b/plugins/backend/decsync/libnxml/nxml_tools.c new file mode 100644 index 000000000..9f075acfb --- /dev/null +++ b/plugins/backend/decsync/libnxml/nxml_tools.c @@ -0,0 +1,122 @@ +/* nXml - Copyright (C) 2005-2007 bakunin - Andrea Marchesini + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "nxml.h" + +int __nxml_atoi(char *str) +{ + int ret; + sscanf(str, "%x", (unsigned int *)&ret); + return ret; +} + +int __nxml_escape_spaces(nxml_t *doc, char **buffer, size_t *size) +{ + /* + * Rule [3] - S ::= (#x20 | #x9 | #xD | #xA)+ + */ + + int k = 0; + + if (!*size) + return 0; + + while ((**buffer == 0x20 || **buffer == 0x9 || **buffer == 0xd || **buffer == 0xa) && *size) + { + if (**buffer == 0xa && doc->priv.func) + doc->priv.line++; + + (*buffer)++; + (*size)--; + k++; + } + + return k; +} + +char * +__nxml_get_value(nxml_t *doc, char **buffer, size_t *size) +{ + char *attr; + int i; + int quot; + + if (!*size) + return NULL; + + if (**buffer == '"') + quot = 1; + + else if (**buffer == '\'') + quot = 0; + + else + return NULL; + + (*buffer)++; + (*size)--; + + i = 0; + while (((quot && *(*buffer + i) != '"') || (!quot && *(*buffer + i) != '\''))) + { + if (*(*buffer + i) == '\n' && doc->priv.func) + doc->priv.line++; + + i++; + } + + if (quot && *(*buffer + i) != '"') + return NULL; + + else if (!quot && *(*buffer + i) != '\'') + return NULL; + + if (!(attr = (char *)malloc(sizeof(char) * (i + 1)))) + return NULL; + + memcpy(attr, *buffer, i); + + attr[i] = 0; + + i++; + (*buffer) += i; + (*size) -= i; + + return attr; +} + +char * +__nxml_trim(char *tmp) +{ + /* Trim function: */ + int i = 0; + while (tmp[i] == 0x20 || tmp[i] == 0x9 || tmp[i] == 0xd || tmp[i] == 0xa) + tmp++; + + i = strlen(tmp); + i--; + + while (tmp[i] == 0x20 || tmp[i] == 0x9 || tmp[i] == 0xd || tmp[i] == 0xa) + i--; + + tmp[i + 1] = 0; + + return strdup(tmp); +} + +/* EOF */ diff --git a/plugins/backend/decsync/libnxml/nxml_utf.c b/plugins/backend/decsync/libnxml/nxml_utf.c new file mode 100644 index 000000000..9d8f9fbda --- /dev/null +++ b/plugins/backend/decsync/libnxml/nxml_utf.c @@ -0,0 +1,515 @@ +/* nXml - Copyright (C) 2005-2007 bakunin - Andrea Marchesini + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "nxml.h" + +/* + * UTF-8 + * 7bits: 0xxxxxxx + * 11bits: 110xxxxx 10xxxxxx + * 16bits: 1110xxxx 10xxxxxx 10xxxxxx + * 21bits: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + * 26bits: 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx + * 31bits: 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx + */ + +int64_t +__nxml_utf8(unsigned char **buffer, size_t *size, int *byte) +{ + int64_t ret; + unsigned char c0 = **buffer, c1, c2, c3, c4; + + if (c0 < 0x80 || *size < 2) + { + *byte = 1; + ret = (int64_t)c0; + return ret; + } + + c1 = *(*buffer + 1); + + if ((c0 & 0xe0) == 0xc0 || *size < 3) + { + *byte = 2; + ret = (((c0 & 0x1f) << 6) | (c1 & 0x3f)); + return ret; + } + + c2 = *(*buffer + 2); + + if ((c0 & 0xf0) == 0xe0 || *size < 4) + { + *byte = 3; + ret = (((c0 & 0x0f) << 12) | ((c1 & 0x3f) << 6) | (c2 & 0x3f)); + return ret; + } + + c3 = *(*buffer + 3); + + if ((c0 & 0xf8) == 0xf0 || *size < 5) + { + *byte = 4; + ret = + (((c0 & 0x7) << 18) | ((c1 & 0x3f) << 12) | ((c2 & 0x3f) << 6) | + (c3 & 0x3f)); + return ret; + } + + c4 = *(*buffer + 4); + + if ((c0 & 0xfc) == 0xf8) + { + *byte = 5; + ret = + (((c0 & 0x3) << 24) | ((c1 & 0x3f) << 18) | ((c2 & 0x3f) << 12) | + ((c3 & 0x3f) << 6) | (c4 & 0x3f)); + return ret; + } + + *byte = 1; + ret = (int64_t)c0; + return ret; +} + +#define __NXML_XTO8(x, b) \ + if (byte >= 1023 - b) \ + { \ + if (!(ret = realloc(ret, (j + b) * sizeof(char)))) \ + return -1; \ + byte = 0; \ + } \ + memcpy(&ret[j], x, b); \ + j += b; \ + byte += b; + +static size_t +__nxml_utf16to8(int le, unsigned char *buffer, size_t size, + unsigned char **ret_buffer) +{ + int64_t ch; + int j = 0; + int byte = 0; + unsigned char *ret; + + if (!(ret = (unsigned char *)malloc(sizeof(unsigned char) * 1024))) + return -1; + + while (size > 0) + { + if (le) + { + if ((*buffer & 0xfc) == 0xd8 && (*(buffer + 2) & 0xfc) == 0xdc) + { + ch = ((*buffer & 0x03) << 18) + (*(buffer + 1) << 10) + + ((*(buffer + 2) & 0x03) << 8) + *(buffer + 3); + + buffer += 4; + size -= 4; + } + + else + { + + ch = (*buffer << 8) + *(buffer + 1); + buffer += 2; + size -= 2; + } + } + + else if ((*(buffer + 1) & 0xfc) == 0xd8 && (*(buffer + 3) & 0xfc) == 0xdc) + { + ch = ((*(buffer + 1) & 0x03) << 18) + (*buffer << 10) + + ((*(buffer + 3) & 0x03) << 8) + *(buffer + 2); + + buffer += 4; + size -= 4; + } + + else + { + + ch = (*(buffer + 1) << 8) + *buffer; + buffer += 2; + size -= 2; + } + + /* 8bit: 1000000 */ + if (ch < 0x80) + { + __NXML_XTO8((void *)&ch, 1); + } + + /* 11bit: xx100000 xx000000 + * 1000 0000 0000 + * 0x 8 0 0 + */ + else if (ch < 0x800) + { + /* 11bits: 110xxxxx 10xxxxxx */ + char a[2]; + a[0] = (ch >> 6) | 0xc0; + a[1] = (ch & 0x3f) | 0x80; + __NXML_XTO8((void *)a, 2); + } + + /* 16bit: xxx10000 xx000000 xx000000 + * 1 0000 0000 0000 0000 + * 0x 1 0 0 0 0 + */ + else if (ch < 0x10000) + { + /* 16bits: 1110xxxx 10xxxxxx 10xxxxxx */ + char a[3]; + a[0] = (ch >> 12) | 0xe0; + a[1] = ((ch >> 6) & 0x3f) | 0x80; + a[2] = (ch & 0x3f) | 0x80; + __NXML_XTO8((void *)a, 3); + } + + /* 21bit: xxxx1000 xx000000 xx000000 xx000000 + * 10 0000 0000 0000 0000 0000 + * 0x 2 0 0 0 0 0 + */ + else if (ch < 0x200000) + { + /* 21bits: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ + char a[4]; + a[0] = (ch >> 18) | 0xf0; + a[1] = ((ch >> 12) & 0x3f); + a[2] = ((ch >> 6) & 0x3f); + a[3] = (ch & 0x3f); + __NXML_XTO8((void *)a, 4); + } + + /* 26bit: xxxxx100 xx000000 xx000000 xx000000 xx000000 + * 100 0000 0000 0000 0000 0000 0000 + * 0x 4 0 0 0 0 0 0 + */ + else if (ch < 0x4000000) + { + /* 21bits: 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx */ + char a[5]; + a[0] = (ch >> 24) | 0xf8; + a[1] = ((ch >> 18) & 0x3f); + a[2] = ((ch >> 12) & 0x3f); + a[3] = ((ch >> 6) & 0x3f); + a[4] = (ch & 0x3f); + __NXML_XTO8((void *)a, 5); + } + } + + ret[j] = 0; + (*ret_buffer) = ret; + + return (size_t)j; +} + +static size_t +__nxml_ucs4to8(int type, unsigned char *buffer, size_t size, + unsigned char **ret_buffer) +{ + int64_t ch; + int j = 0; + int byte = 0; + unsigned char *ret; + + if (!(ret = (unsigned char *)malloc(sizeof(unsigned char) * 1024))) + return -1; + + while (size > 0) + { + switch (type) + { + case 0: /* 1234 */ + ch = + (*buffer << 18) + (*(buffer + 1) << 12) + (*(buffer + 2) << 6) + + (*(buffer + 3)); + break; + + case 1: /* 4321 */ + ch = + (*buffer) + (*(buffer + 1) << 6) + (*(buffer + 2) << 12) + + (*(buffer + 3) << 18); + break; + + case 2: /* 2143 */ + ch = + ((*buffer) << 12) + (*(buffer + 1) << 18) + (*(buffer + 2)) + + (*(buffer + 3) << 6); + break; + + case 3: /* 3412 */ + ch = + ((*buffer) << 6) + (*(buffer + 1)) + (*(buffer + 2) << 18) + + (*(buffer + 3) << 12); + break; + } + + buffer += 4; + size -= 4; + + /* 8bit: 1000000 */ + if (ch < 0x80) + { + __NXML_XTO8((void *)&ch, 1); + } + + /* 11bit: xx100000 xx000000 + * 1000 0000 0000 + * 0x 8 0 0 + */ + else if (ch < 0x800) + { + /* 11bits: 110xxxxx 10xxxxxx */ + char a[2]; + a[0] = (ch >> 6) | 0xc0; + a[1] = (ch & 0x3f) | 0x80; + __NXML_XTO8((void *)a, 2); + } + + /* 16bit: xxx10000 xx000000 xx000000 + * 1 0000 0000 0000 0000 + * 0x 1 0 0 0 0 + */ + else if (ch < 0x10000) + { + /* 16bits: 1110xxxx 10xxxxxx 10xxxxxx */ + char a[3]; + a[0] = (ch >> 12) | 0xe0; + a[1] = ((ch >> 6) & 0x3f) | 0x80; + a[2] = (ch & 0x3f) | 0x80; + __NXML_XTO8((void *)a, 3); + } + + /* 21bit: xxxx1000 xx000000 xx000000 xx000000 + * 10 0000 0000 0000 0000 0000 + * 0x 2 0 0 0 0 0 + */ + else if (ch < 0x200000) + { + /* 21bits: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ + char a[4]; + a[0] = (ch >> 18) | 0xf0; + a[1] = ((ch >> 12) & 0x3f); + a[2] = ((ch >> 6) & 0x3f); + a[3] = (ch & 0x3f); + __NXML_XTO8((void *)a, 4); + } + + /* 26bit: xxxxx100 xx000000 xx000000 xx000000 xx000000 + * 100 0000 0000 0000 0000 0000 0000 + * 0x 4 0 0 0 0 0 0 + */ + else if (ch < 0x4000000) + { + /* 21bits: 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx */ + char a[5]; + a[0] = (ch >> 24) | 0xf8; + a[1] = ((ch >> 18) & 0x3f); + a[2] = ((ch >> 12) & 0x3f); + a[3] = ((ch >> 6) & 0x3f); + a[4] = (ch & 0x3f); + __NXML_XTO8((void *)a, 5); + } + } + + ret[j] = 0; + (*ret_buffer) = ret; + + return (size_t)j; +} + +int __nxml_utf_detection(char *r_buffer, size_t r_size, char **buffer, + size_t *size, nxml_charset_t *charset) +{ + /* Utf-8: 0x3c 0x3f 0x78 0x6d */ + if (strncmp(r_buffer, "> 6) | 0xc0; + str[1] = (ch & 0x3f) | 0x80; + return 2; + } + + /* 16bit: xxx10000 xx000000 xx000000 + * 1 0000 0000 0000 0000 + * 0x 1 0 0 0 0 + */ + else if (ch < 0x10000) + { + /* 16bits: 1110xxxx 10xxxxxx 10xxxxxx */ + str[0] = (ch >> 12) | 0xe0; + str[1] = ((ch >> 6) & 0x3f) | 0x80; + str[2] = (ch & 0x3f) | 0x80; + return 3; + } + + /* 21bit: xxxx1000 xx000000 xx000000 xx000000 + * 10 0000 0000 0000 0000 0000 + * 0x 2 0 0 0 0 0 + */ + else if (ch < 0x200000) + { + /* 21bits: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ + str[0] = (ch >> 18) | 0xf0; + str[1] = ((ch >> 12) & 0x3f); + str[2] = ((ch >> 6) & 0x3f); + str[3] = (ch & 0x3f); + return 4; + } + + /* 26bit: xxxxx100 xx000000 xx000000 xx000000 xx000000 + * 100 0000 0000 0000 0000 0000 0000 + * 0x 4 0 0 0 0 0 0 + */ + else if (ch < 0x4000000) + { + /* 21bits: 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx */ + str[0] = (ch >> 24) | 0xf8; + str[1] = ((ch >> 18) & 0x3f); + str[2] = ((ch >> 12) & 0x3f); + str[3] = ((ch >> 6) & 0x3f); + str[4] = (ch & 0x3f); + return 5; + } + + return 0; +} + +/* EOF */ diff --git a/plugins/backend/decsync/libnxml/nxml_write.c b/plugins/backend/decsync/libnxml/nxml_write.c new file mode 100644 index 000000000..f1c658c7c --- /dev/null +++ b/plugins/backend/decsync/libnxml/nxml_write.c @@ -0,0 +1,410 @@ +/* nXml - Copyright (C) 2005-2007 bakunin - Andrea Marchesini + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "nxml.h" + +static void +__nxml_write_escape_string(void (*func)(void *, char *, ...), void *obj, + char *str) +{ + int i; + int len; + char buf[1024]; + int j; + +#define __NXML_CHECK_BUF \ + if (j == sizeof(buf) - 1) \ + { \ + buf[j] = 0; \ + func(obj, "%s", buf); \ + j = 0; \ + } + + if (!str) + return; + + len = strlen(str); + + for (j = i = 0; i < len; i++) + { + if (str[i] == '\r') + continue; + + else if (str[i] == '<') + { + buf[j++] = '&'; + __NXML_CHECK_BUF; + buf[j++] = 'l'; + __NXML_CHECK_BUF; + buf[j++] = 't'; + __NXML_CHECK_BUF; + buf[j++] = ';'; + __NXML_CHECK_BUF; + } + + else if (str[i] == '>') + { + buf[j++] = '&'; + __NXML_CHECK_BUF; + buf[j++] = 'g'; + __NXML_CHECK_BUF; + buf[j++] = 't'; + __NXML_CHECK_BUF; + buf[j++] = ';'; + __NXML_CHECK_BUF; + } + + else if (str[i] == '&') + { + buf[j++] = '&'; + __NXML_CHECK_BUF; + buf[j++] = 'a'; + __NXML_CHECK_BUF; + buf[j++] = 'm'; + __NXML_CHECK_BUF; + buf[j++] = 'p'; + __NXML_CHECK_BUF; + buf[j++] = ';'; + __NXML_CHECK_BUF; + } + + else if (str[i] == '\'') + { + buf[j++] = '&'; + __NXML_CHECK_BUF; + buf[j++] = 'a'; + __NXML_CHECK_BUF; + buf[j++] = 'p'; + __NXML_CHECK_BUF; + buf[j++] = 'o'; + __NXML_CHECK_BUF; + buf[j++] = 's'; + __NXML_CHECK_BUF; + buf[j++] = ';'; + __NXML_CHECK_BUF; + } + + else if (str[i] == '\"') + { + buf[j++] = '&'; + __NXML_CHECK_BUF; + buf[j++] = 'q'; + __NXML_CHECK_BUF; + buf[j++] = 'u'; + __NXML_CHECK_BUF; + buf[j++] = 'o'; + __NXML_CHECK_BUF; + buf[j++] = 't'; + __NXML_CHECK_BUF; + buf[j++] = ';'; + __NXML_CHECK_BUF; + } + + else + { + buf[j++] = str[i]; + __NXML_CHECK_BUF; + } + } + + if (j) + { + buf[j] = 0; + func(obj, "%s", buf); + j = 0; + } +} + +static int +__nxml_write_haslines(char *what) +{ + while (what && *what) + { + if (*what == '\n') + return 1; + what++; + } + + return 0; +} + +static void +__nxml_write_indent(void (*func)(void *, char *, ...), void *obj, + int indent) +{ + int i; + for (i = 0; i < indent; i++) + func(obj, " "); +} + +static void +__nxml_write_newline(void (*func)(void *, char *, ...), void *obj) +{ + func(obj, "\n"); +} + +static void +__nxml_write_data_text(nxml_data_t *data, + void (*func)(void *, char *, ...), void *obj) +{ + __nxml_write_escape_string(func, obj, data->value); +} + +static void +__nxml_write_data_comment(nxml_data_t *data, + void (*func)(void *, char *, ...), void *obj) +{ + func(obj, "", data->value); +} + +static void +__nxml_write_data_pi(nxml_data_t *data, void (*func)(void *, char *, ...), + void *obj) +{ + func(obj, "", data->value); +} + +static void +__nxml_write_data_doctype(nxml_doctype_t *data, + void (*func)(void *, char *, ...), void *obj) +{ + func(obj, "", data->name, data->value); +} + +static void +__nxml_write_data_element(nxml_data_t *data, + void (*func)(void *, char *, ...), void *obj) +{ + nxml_attr_t *attr; + + func(obj, "<"); + if (data->ns && data->ns->prefix) + func(obj, "%s:", data->ns->prefix); + func(obj, "%s", data->value); + + attr = data->attributes; + while (attr) + { + func(obj, " "); + + if (attr->ns && attr->ns->prefix) + func(obj, "%s:", attr->ns->prefix); + func(obj, "%s=\"", attr->name); + + __nxml_write_escape_string(func, obj, attr->value); + func(obj, "\""); + attr = attr->next; + } + + if (!data->children) + func(obj, " /"); + + func(obj, ">"); +} + +static void +__nxml_write_data(nxml_t *nxml, nxml_data_t *data, + void (*func)(void *, char *, ...), void *obj, int indent) +{ + nxml_data_t *tmp; + int i; + + switch (data->type) + { + case NXML_TYPE_TEXT: + if (data->children || data->next || __nxml_write_haslines(data->value) || (data->parent && data->parent->children != data)) + { + i = 1; + __nxml_write_indent(func, obj, indent); + } + else + i = 0; + + __nxml_write_data_text(data, func, obj); + + if (i) + __nxml_write_newline(func, obj); + break; + + case NXML_TYPE_COMMENT: + __nxml_write_indent(func, obj, indent); + __nxml_write_data_comment(data, func, obj); + __nxml_write_newline(func, obj); + break; + + case NXML_TYPE_PI: + __nxml_write_indent(func, obj, indent); + __nxml_write_data_pi(data, func, obj); + __nxml_write_newline(func, obj); + break; + + default: + __nxml_write_indent(func, obj, indent); + __nxml_write_data_element(data, func, obj); + + if (!data->children || data->children->type != NXML_TYPE_TEXT || data->children->next || __nxml_write_haslines(data->children->value)) + __nxml_write_newline(func, obj); + + break; + } + + if (data->children) + { + tmp = data->children; + + while (tmp) + { + __nxml_write_data(nxml, tmp, func, obj, indent + 1); + tmp = tmp->next; + } + + if (data->type == NXML_TYPE_ELEMENT) + { + if (!data->children || data->children->type != NXML_TYPE_TEXT || data->children->next || data->children->children || __nxml_write_haslines(data->children->value)) + __nxml_write_indent(func, obj, indent); + + func(obj, "ns && data->ns->prefix) + func(obj, "%s:", data->ns->prefix); + func(obj, "%s>", data->value); + + __nxml_write_newline(func, obj); + } + } +} + +static nxml_error_t +__nxml_write_real(nxml_t *nxml, void (*func)(void *, char *, ...), + void *obj) +{ + nxml_data_t *data; + nxml_doctype_t *doctype; + + func(obj, "version) + { + case NXML_VERSION_1_0: + func(obj, "1.0"); + break; + default: + func(obj, "1.1"); + } + + func(obj, "\""); + + if (nxml->encoding) + func(obj, " encoding=\"%s\"", nxml->encoding); + + func(obj, " standalone=\"%s\"?>\n\n", nxml->standalone ? "yes" : "no"); + + doctype = nxml->doctype; + + while (doctype) + { + __nxml_write_indent(func, obj, 0); + __nxml_write_data_doctype(doctype, func, obj); + __nxml_write_newline(func, obj); + + doctype = doctype->next; + } + + data = nxml->data; + + while (data) + { + __nxml_write_data(nxml, data, func, obj, 0); + + data = data->next; + } + + return NXML_OK; +} + +static void +__nxml_file_write(void *obj, char *str, ...) +{ + va_list va; + + va_start(va, str); + vfprintf((FILE *)obj, str, va); + va_end(va); +} + +static void +__nxml_buffer_write(void *obj, char *str, ...) +{ + va_list va; + char s[4096]; + int len; + char **buffer = (char **)obj; + + va_start(va, str); + len = vsnprintf(s, sizeof(s), str, va); + va_end(va); + + if (!*buffer) + { + if (!(*buffer = (char *)malloc(sizeof(char) * (len + 1)))) + return; + + strcpy(*buffer, s); + } + else + { + if (!(*buffer = (char *)realloc(*buffer, + sizeof(char) * (strlen(*buffer) + + len + 1)))) + return; + + strcat(*buffer, s); + } +} + +/*************************** EXTERNAL FUNCTION ******************************/ + +nxml_error_t +nxml_write_file(nxml_t *nxml, char *file) +{ + FILE *fl; + nxml_error_t ret; + + if (!nxml || !file) + return NXML_ERR_DATA; + + if (!(fl = fopen(file, "wb"))) + return NXML_ERR_POSIX; + + ret = __nxml_write_real(nxml, __nxml_file_write, fl); + fclose(fl); + + return ret; +} + +nxml_error_t +nxml_write_buffer(nxml_t *nxml, char **buffer) +{ + if (!nxml || !buffer) + return NXML_ERR_DATA; + + return __nxml_write_real(nxml, __nxml_buffer_write, buffer); +} + +/* EOF */ diff --git a/plugins/backend/decsync/meson.build b/plugins/backend/decsync/meson.build new file mode 100644 index 000000000..aba485dc1 --- /dev/null +++ b/plugins/backend/decsync/meson.build @@ -0,0 +1,68 @@ +decsync_resources = gnome.compile_resources( + 'decsync_res', + 'decsync.gresource.xml' +) + +subdir('libnxml') +subdir('libmrss') +subdir('rss-glib') + +rss_glib_dir = join_paths(meson.current_source_dir(), 'rss-glib/') +rss_glib_vapi = vala_compiler.find_library('rss-glib-1.0', dirs: rss_glib_dir) +lib_rss_glib = declare_dependency( + link_with: rss_glib_lib, + include_directories: rss_glib_inc, + dependencies: rss_glib_vapi +) + +subdir('libdecsync/src') + +shared_library( + 'decsync', + [ + 'decsyncInterface.vala', + 'decsyncListeners.vala', + 'decsyncUtils.vala', + 'Rfc822.vala' + ], + decsync_resources, + c_args: c_args, + vala_args: vala_args, + dependencies: [ + libpeas, + libgd, + gtk, + gee, + libsecret, + sqlite3, + libsoup, + libxml, + json_glib, + libcurl, + lib_rss_glib, + webkit2gtk, + libdecsync + ], + link_with: [ + feedreader_lib, + mrss_lib, + nxml_lib + ], + install: true, + install_dir: BACKEND_PLUGINS_DIR, + include_directories: include_directories('../../..') +) + +install_data( + [ + 'org.gnome.feedreader.decsync.gschema.xml' + ], + install_dir: join_paths(SHARE_DIR, 'glib-2.0/schemas') +) + +install_data( + [ + 'decsync.plugin' + ], + install_dir: BACKEND_PLUGINS_DIR +) diff --git a/plugins/backend/decsync/org.gnome.feedreader.decsync.gschema.xml b/plugins/backend/decsync/org.gnome.feedreader.decsync.gschema.xml new file mode 100644 index 000000000..ee135cd56 --- /dev/null +++ b/plugins/backend/decsync/org.gnome.feedreader.decsync.gschema.xml @@ -0,0 +1,11 @@ + + + + "" + DecSync directory + + DecSync directory + + + + diff --git a/plugins/backend/decsync/rss-glib/meson.build b/plugins/backend/decsync/rss-glib/meson.build new file mode 100644 index 000000000..05c5be84d --- /dev/null +++ b/plugins/backend/decsync/rss-glib/meson.build @@ -0,0 +1,12 @@ +rss_glib_inc = include_directories(['.', '../libmrss']) +rss_glib_lib = static_library( + 'rss-glib', + [ + 'rss-document.c', + 'rss-marshal.c', + 'rss-item.c', + 'rss-parser.c' + ], + include_directories: rss_glib_inc, + dependencies: glib +) diff --git a/plugins/backend/decsync/rss-glib/rss-document-private.h b/plugins/backend/decsync/rss-glib/rss-document-private.h new file mode 100644 index 000000000..84889e93f --- /dev/null +++ b/plugins/backend/decsync/rss-glib/rss-document-private.h @@ -0,0 +1,58 @@ +/* rss-document-private.h + * + * This file is part of RSS-GLib. + * Copyright (C) 2008 Christian Hergert + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + * + * Author: + * Christian Hergert + */ + +#ifndef __RSS_DOCUMENT_PRIVATE_H__ +#define __RSS_DOCUMENT_PRIVATE_H__ + +#include + +struct _RssDocumentPrivate +{ + gchar *encoding; + gchar *guid; + gchar *title; + gchar *description; + gchar *link; + gchar *language; + gchar *rating; + gchar *copyright; + gchar *pub_date; + gchar *editor; + gchar *editor_email; + gchar *editor_uri; + gint ttl; + gchar *about; + gchar *contributor; + gchar *contributor_email; + gchar *contributor_uri; + gchar *generator; + gchar *generator_uri; + gchar *generator_version; + gchar *image_title; + gchar *image_url; + gchar *image_link; + + GList *items; + GList *categories; +}; + +#endif /* __RSS_DOCUMENT_PRIVATE_H__ */ diff --git a/plugins/backend/decsync/rss-glib/rss-document.c b/plugins/backend/decsync/rss-glib/rss-document.c new file mode 100644 index 000000000..c98a2f139 --- /dev/null +++ b/plugins/backend/decsync/rss-glib/rss-document.c @@ -0,0 +1,1085 @@ +/* rss-document.c + * + * This file is part of RSS-GLib. + * Copyright (C) 2008 Christian Hergert + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + * + * Author: + * Christian Hergert + */ + +/** + * SECTION:rss-document + * @short_description: a RSS document representation + * @see_also: rss_parser_get_document() + * + * #RssDocument is the representation of the resource that was parsed. It + * contains a list of #RssItems which in turn contain article information. + */ + +#include "rss-document.h" +#include "rss-document-private.h" + +G_DEFINE_TYPE (RssDocument, rss_document, G_TYPE_OBJECT); + +enum { + PROP_0, + + PROP_ENCODING, + PROP_GUID, + PROP_TITLE, + PROP_DESCRIPTION, + PROP_LINK, + PROP_LANGUAGE, + PROP_RATING, + PROP_COPYRIGHT, + PROP_PUB_DATE, + PROP_PUB_DATE_PARSED, + PROP_EDITOR, + PROP_EDITOR_EMAIL, + PROP_EDITOR_URI, + PROP_TTL, + PROP_ABOUT, + PROP_CONTRIBUTOR, + PROP_CONTRIBUTOR_EMAIL, + PROP_CONTRIBUTOR_URI, + PROP_GENERATOR, + PROP_GENERATOR_URI, + PROP_GENERATOR_VERSION, + PROP_IMAGE_TITLE, + PROP_IMAGE_URL, + PROP_IMAGE_LINK +}; + +static void +rss_document_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + RssDocumentPrivate *priv = RSS_DOCUMENT (object)->priv; + + switch (property_id) { + case PROP_ENCODING: + g_value_set_string (value, priv->encoding); + break; + case PROP_GUID: + g_value_set_string (value, priv->guid); + break; + case PROP_TITLE: + g_value_set_string (value, priv->title); + break; + case PROP_DESCRIPTION: + g_value_set_string (value, priv->description); + break; + case PROP_LINK: + g_value_set_string (value, priv->link); + break; + case PROP_LANGUAGE: + g_value_set_string (value, priv->language); + break; + case PROP_RATING: + g_value_set_string (value, priv->rating); + break; + case PROP_COPYRIGHT: + g_value_set_string (value, priv->copyright); + break; + case PROP_PUB_DATE: + g_value_set_string (value, priv->pub_date); + break; + case PROP_EDITOR: + g_value_set_string (value, priv->editor); + break; + case PROP_EDITOR_EMAIL: + g_value_set_string (value, priv->editor_email); + break; + case PROP_EDITOR_URI: + g_value_set_string (value, priv->editor_uri); + break; + case PROP_ABOUT: + g_value_set_string (value, priv->about); + break; + case PROP_CONTRIBUTOR: + g_value_set_string (value, priv->contributor); + break; + case PROP_CONTRIBUTOR_EMAIL: + g_value_set_string (value, priv->contributor_email); + break; + case PROP_CONTRIBUTOR_URI: + g_value_set_string (value, priv->contributor_uri); + break; + case PROP_GENERATOR: + g_value_set_string (value, priv->generator); + break; + case PROP_GENERATOR_URI: + g_value_set_string (value, priv->generator_uri); + break; + case PROP_GENERATOR_VERSION: + g_value_set_string (value, priv->generator_version); + break; + case PROP_IMAGE_TITLE: + g_value_set_string (value, priv->image_title); + break; + case PROP_IMAGE_URL: + g_value_set_string (value, priv->image_url); + break; + case PROP_IMAGE_LINK: + g_value_set_string (value, priv->image_link); + break; + case PROP_TTL: + g_value_set_int (value, priv->ttl); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + +static void +rss_document_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + RssDocumentPrivate *priv = RSS_DOCUMENT (object)->priv; + + switch (property_id) { + case PROP_ENCODING: + g_free (priv->encoding); + priv->encoding = g_value_dup_string (value); + break; + case PROP_GUID: + g_free (priv->guid); + priv->guid = g_value_dup_string (value); + break; + case PROP_TITLE: + g_free (priv->title); + priv->title = g_value_dup_string (value); + break; + case PROP_DESCRIPTION: + g_free (priv->description); + priv->description = g_value_dup_string (value); + break; + case PROP_LINK: + g_free (priv->link); + priv->link = g_value_dup_string (value); + break; + case PROP_LANGUAGE: + g_free (priv->language); + priv->language = g_value_dup_string (value); + break; + case PROP_RATING: + g_free (priv->rating); + priv->rating = g_value_dup_string (value); + break; + case PROP_COPYRIGHT: + g_free (priv->copyright); + priv->copyright = g_value_dup_string (value); + break; + case PROP_PUB_DATE: + g_free (priv->pub_date); + priv->pub_date = g_value_dup_string (value); + break; + case PROP_EDITOR: + g_free (priv->editor); + priv->editor = g_value_dup_string (value); + break; + case PROP_EDITOR_EMAIL: + g_free (priv->editor_email); + priv->editor_email = g_value_dup_string (value); + break; + case PROP_EDITOR_URI: + g_free (priv->editor_uri); + priv->editor_uri = g_value_dup_string (value); + break; + case PROP_ABOUT: + g_free (priv->about); + priv->about = g_value_dup_string (value); + break; + case PROP_CONTRIBUTOR: + g_free (priv->contributor); + priv->contributor = g_value_dup_string (value); + break; + case PROP_CONTRIBUTOR_EMAIL: + g_free (priv->contributor_email); + priv->contributor_email = g_value_dup_string (value); + break; + case PROP_CONTRIBUTOR_URI: + g_free (priv->contributor_uri); + priv->contributor_uri = g_value_dup_string (value); + break; + case PROP_GENERATOR: + g_free (priv->generator); + priv->generator = g_value_dup_string (value); + break; + case PROP_GENERATOR_URI: + g_free (priv->generator_uri); + priv->generator_uri = g_value_dup_string (value); + break; + case PROP_GENERATOR_VERSION: + g_free (priv->generator_version); + priv->generator_version = g_value_dup_string (value); + break; + case PROP_IMAGE_TITLE: + g_free (priv->image_title); + priv->image_title = g_value_dup_string (value); + break; + case PROP_IMAGE_URL: + g_free (priv->image_url); + priv->image_url = g_value_dup_string (value); + break; + case PROP_IMAGE_LINK: + g_free (priv->image_link); + priv->image_link = g_value_dup_string (value); + break; + case PROP_TTL: + priv->ttl = g_value_get_int (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + +static void +rss_document_dispose (GObject *object) +{ + RssDocumentPrivate *priv = RSS_DOCUMENT (object)->priv; + + g_free (priv->encoding); + g_free (priv->guid); + g_free (priv->title); + g_free (priv->description); + g_free (priv->link); + g_free (priv->language); + g_free (priv->rating); + g_free (priv->copyright); + g_free (priv->pub_date); + g_free (priv->editor); + g_free (priv->editor_email); + g_free (priv->editor_uri); + g_free (priv->about); + g_free (priv->contributor); + g_free (priv->contributor_email); + g_free (priv->contributor_uri); + g_free (priv->generator); + g_free (priv->generator_uri); + g_free (priv->generator_version); + g_free (priv->image_title); + g_free (priv->image_url); + g_free (priv->image_link); + + /* free the items */ + g_list_foreach (priv->items, (GFunc) g_object_unref, NULL); + g_list_free (priv->items); + + /* free the category strings */ + g_list_foreach (priv->categories, (GFunc) g_free, NULL); + g_list_free (priv->categories); + + G_OBJECT_CLASS (rss_document_parent_class)->dispose (object); +} + +static void +rss_document_class_init (RssDocumentClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GParamSpec *pspec; + + g_type_class_add_private (klass, sizeof (RssDocumentPrivate)); + + gobject_class->get_property = rss_document_get_property; + gobject_class->set_property = rss_document_set_property; + gobject_class->dispose = rss_document_dispose; + + /** + * RssDocument:encoding: + * + * The encoding of the #RssDocument. + */ + pspec = g_param_spec_string ("encoding", + "Encoding", + "Encoding of the document", + NULL, + G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, + PROP_ENCODING, + pspec); + + /** + * RssDocument:guid: + * + * The guid associated with the feed. This is often the url of the + * feed. + */ + pspec = g_param_spec_string ("guid", + "GUID", + "The GUID of the document", + NULL, + G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, + PROP_GUID, + pspec); + + /** + * RssDocument:title: + * + * The title of the #RssDocument. + */ + pspec = g_param_spec_string ("title", + "Title", + "Title of the document", + NULL, + G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, + PROP_TITLE, + pspec); + + /** + * RssDocument:description: + * + * The description of the #RssDocument. + */ + pspec = g_param_spec_string ("description", + "Description", + "Description of the document", + NULL, + G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, + PROP_DESCRIPTION, + pspec); + + /** + * RssDocument:link: + * + * The link to the source document. This is parsed from the actual + * document and can point to whatever the source publisher choses. + */ + pspec = g_param_spec_string ("link", + "Link", + "The link to the source document", + NULL, + G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, + PROP_LINK, + pspec); + + /** + * RssDocument:language: + * + * The language the #RssDocument was published in. + */ + pspec = g_param_spec_string ("language", + "Language", + "Language of the document", + NULL, + G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, + PROP_LANGUAGE, + pspec); + + /** + * RssDocument:rating: + * + * The rating associated with the #RssDocument. + */ + pspec = g_param_spec_string ("rating", + "Rating", + "Rating of the document", + NULL, + G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, + PROP_RATING, + pspec); + + /** + * RssDocument:copyright: + * + * The copyright of the #RssDocument. + */ + pspec = g_param_spec_string ("copyright", + "Copyright", + "Copyright of the document", + NULL, + G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, + PROP_COPYRIGHT, + pspec); + + /** + * RssDocument:pub-date: + * + * The string representation of the date the document was published. + */ + pspec = g_param_spec_string ("pub-date", + "Publication Date", + "Publication date of the document", + NULL, + G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, + PROP_PUB_DATE, + pspec); + + /** + * RssDocument:editor: + * + * The name of the editor. + */ + pspec = g_param_spec_string ("editor", + "Editor", + "Editor of the document", + NULL, + G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, + PROP_EDITOR, + pspec); + + /** + * RssDocument:editor-email: + * + * The email address of the editor. + */ + pspec = g_param_spec_string ("editor-email", + "Editor Email", + "Email of the editor", + NULL, + G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, + PROP_EDITOR_EMAIL, + pspec); + + /** + * RssDocument:editor-uri: + * + * The uri for more information about the editor. + */ + pspec = g_param_spec_string ("editor-uri", + "Editor URI", + "The URI of the editor", + NULL, + G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, + PROP_EDITOR_URI, + pspec); + + /** + * RssDocument:about: + * + * Information about the #RssDocument. + */ + pspec = g_param_spec_string ("about", + "About", + "Information about the document", + NULL, + G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, + PROP_ABOUT, + pspec); + + /** + * RssDocument:contributor: + * + * The name of the particular contributor. + */ + pspec = g_param_spec_string ("contributor", + "Contributor", + "Name of the contributor", + NULL, + G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, + PROP_CONTRIBUTOR, + pspec); + + /** + * RssDocument:contributor-email: + * + * The email of the particular contributor. + */ + pspec = g_param_spec_string ("contributor-email", + "Contributor Email", + "Email of the contributor", + NULL, + G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, + PROP_CONTRIBUTOR_EMAIL, + pspec); + + /** + * RssDocument:contributor-uri: + * + * The uri to more information on the particular contributer. + */ + pspec = g_param_spec_string ("contributor-uri", + "Contributor URI", + "URI of the contributor", + NULL, + G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, + PROP_CONTRIBUTOR_URI, + pspec); + + /** + * RssDocument:generator: + * + * The name of the generator on the server side. + */ + pspec = g_param_spec_string ("generator", + "Generator", + "Name of the document generator", + NULL, + G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, + PROP_GENERATOR, + pspec); + + /** + * RssDocument:generator-uri: + * + * Url to more information about the generator on the server side. + */ + pspec = g_param_spec_string ("generator-uri", + "Generator URI", + "URI of the document generator", + NULL, + G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, + PROP_GENERATOR_URI, + pspec); + + /** + * RssDocument:generator-version: + * + * The version of the server side generator. + */ + pspec = g_param_spec_string ("generator-version", + "Generator Version", + "Version of the document generator", + NULL, + G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, + PROP_GENERATOR_VERSION, + pspec); + + /** + * RssDocument:image-title: + * + * The title for the image. This is often the alt="" tag in HTML. + */ + pspec = g_param_spec_string ("image-title", + "Image Title", + "Title of the image for the document", + NULL, + G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, + PROP_IMAGE_TITLE, + pspec); + + /** + * RssDocument:image-url: + * + * A url to the image for the RssDocument. Use this before checking + * for a favicon.ico. + */ + pspec = g_param_spec_string ("image-url", + "Image URL", + "URL of the image for the document", + NULL, + G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, + PROP_IMAGE_URL, + pspec); + + /** + * RssDocument:image-link: + * + * The url a user should be redirected to when clicking on the image + * for the #RssDocument. Of course, its up to UI designers if they + * wish to implement this in any sort of way. + */ + pspec = g_param_spec_string ("image-link", + "Image Link", + "URL for document image link", + NULL, + G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, + PROP_IMAGE_LINK, + pspec); + + /** + * RssDocument:ttl: + * + * The publisher determined TTL for the source. Readers should try + * to respect this value and not update feeds any more often than + * necessary. + */ + pspec = g_param_spec_int ("ttl", + "TTL", + "Time to live for the document", + 0, G_MAXINT32, 0, + G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, + PROP_TTL, + pspec); +} + +static void +rss_document_init (RssDocument *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, RSS_TYPE_DOCUMENT, + RssDocumentPrivate); +} + +/** + * rss_document_new: + * + * Creates a new instance of #RssDocument. This isn't incredibly useful + * currently, but is here none-the-less. The desire is to create an + * RSS generator that will allow for building RSS streams out of the + * document hierarchy. + * + * Returns: a new #RssDocument. Use g_object_unref() when you are done. + */ +RssDocument* +rss_document_new (void) +{ + return g_object_new (RSS_TYPE_DOCUMENT, NULL); +} + +/** + * rss_document_get_items: + * @self: a #RssDocument + * + * Creates a #GList of #RssItems that were found in the syndication. The objects + * in the list are weak references. Consumers of those objects should ref + * them with g_object_ref. + * + * Returns: a new #GList owned by the caller. + */ +GList* +rss_document_get_items (RssDocument *self) +{ + g_return_val_if_fail (RSS_IS_DOCUMENT (self), NULL); + + return g_list_copy (self->priv->items); +} + +/** + * rss_document_get_categories: + * @self: a #RssDocument + * + * Creates a #GList of categories found in the syndication. The strings + * in the list are weak references. Consumers should duplicate them + * with g_strdup(). + * + * Returns: a new #GList owned by the caller + */ +GList* +rss_document_get_categories (RssDocument *self) +{ + g_return_val_if_fail (RSS_IS_DOCUMENT (self), NULL); + + return g_list_copy (self->priv->categories); +} + +/** + * rss_document_get_guid: + * @self: a #RssDocument + * + * Retrieves the #RssDocument:guid field. + * + * Return value: the contents of the field. The returned string is + * owned by the #RssDocument and should never be modified of freed + */ +const gchar * +rss_document_get_guid (RssDocument *self) +{ + g_return_val_if_fail (RSS_IS_DOCUMENT (self), NULL); + + return self->priv->guid; +} + +/** + * rss_document_get_about: + * @self: a #RssDocument + * + * Retrieves the #RssDocument:about field. + * + * Return value: the contents of the field. The returned string is + * owned by the #RssDocument and should never be modified of freed + */ +const gchar * +rss_document_get_about (RssDocument *self) +{ + g_return_val_if_fail (RSS_IS_DOCUMENT (self), NULL); + + return self->priv->about; +} + +/** + * rss_document_get_title: + * @self: a #RssDocument + * + * Retrieves the #RssDocument:title field. + * + * Return value: the contents of the field. The returned string is + * owned by the #RssDocument and should never be modified of freed + */ +const gchar * +rss_document_get_title (RssDocument *self) +{ + g_return_val_if_fail (RSS_IS_DOCUMENT (self), NULL); + + return self->priv->title; +} + +/** + * rss_document_get_description: + * @self: a #RssDocument + * + * Retrieves the #RssDocument:description field. + * + * Return value: the contents of the field. The returned string is + * owned by the #RssDocument and should never be modified of freed + */ +const gchar * +rss_document_get_description (RssDocument *self) +{ + g_return_val_if_fail (RSS_IS_DOCUMENT (self), NULL); + + return self->priv->description; +} + +/** + * rss_document_get_link: + * @self: a #RssDocument + * + * Retrieves the #RssDocument:link field. + * + * Return value: the contents of the field. The returned string is + * owned by the #RssDocument and should never be modified of freed + */ +const gchar * +rss_document_get_link (RssDocument *self) +{ + g_return_val_if_fail (RSS_IS_DOCUMENT (self), NULL); + + return self->priv->link; +} + +/** + * rss_document_get_encoding: + * @self: a #RssDocument + * + * Retrieves the #RssDocument:encoding field. + * + * Return value: the contents of the field. The returned string is + * owned by the #RssDocument and should never be modified of freed + */ +const gchar * +rss_document_get_encoding (RssDocument *self) +{ + g_return_val_if_fail (RSS_IS_DOCUMENT (self), NULL); + + return self->priv->encoding; +} + +/** + * rss_document_get_language: + * @self: a #RssDocument + * + * Retrieves the #RssDocument:language field. + * + * Return value: the contents of the field. The returned string is + * owned by the #RssDocument and should never be modified of freed + */ +const gchar * +rss_document_get_language (RssDocument *self) +{ + g_return_val_if_fail (RSS_IS_DOCUMENT (self), NULL); + + return self->priv->language; +} + +/** + * rss_document_get_rating: + * @self: a #RssDocument + * + * Retrieves the #RssDocument:rating field. + * + * Return value: the contents of the field. The returned string is + * owned by the #RssDocument and should never be modified of freed + */ +const gchar * +rss_document_get_rating (RssDocument *self) +{ + g_return_val_if_fail (RSS_IS_DOCUMENT (self), NULL); + + return self->priv->rating; +} + +/** + * rss_document_get_copyright: + * @self: a #RssDocument + * + * Retrieves the #RssDocument:copyright field. + * + * Return value: the contents of the field. The returned string is + * owned by the #RssDocument and should never be modified of freed + */ +const gchar * +rss_document_get_copyright (RssDocument *self) +{ + g_return_val_if_fail (RSS_IS_DOCUMENT (self), NULL); + + return self->priv->copyright; +} + +/** + * rss_document_get_pub_date: + * @self: a #RssDocument + * + * Retrieves the #RssDocument:pub-date field. + * + * Return value: the contents of the field. The returned string is + * owned by the #RssDocument and should never be modified of freed + */ +const gchar * +rss_document_get_pub_date (RssDocument *self) +{ + g_return_val_if_fail (RSS_IS_DOCUMENT (self), NULL); + + return self->priv->pub_date; +} + +/** + * rss_document_get_editor_name: + * @self: a #RssDocument + * + * Retrieves the #RssDocument:editor field. + * + * Return value: the contents of the field. The returned string is + * owned by the #RssDocument and should never be modified of freed + */ +const gchar * +rss_document_get_editor_name (RssDocument *self) +{ + g_return_val_if_fail (RSS_IS_DOCUMENT (self), NULL); + + return self->priv->editor; +} + +/** + * rss_document_get_editor_email: + * @self: a #RssDocument + * + * Retrieves the #RssDocument:editor-email field. + * + * Return value: the contents of the field. The returned string is + * owned by the #RssDocument and should never be modified of freed + */ +const gchar * +rss_document_get_editor_email (RssDocument *self) +{ + g_return_val_if_fail (RSS_IS_DOCUMENT (self), NULL); + + return self->priv->editor_email; +} + +/** + * rss_document_get_editor_uri: + * @self: a #RssDocument + * + * Retrieves the #RssDocument:editor-uri field. + * + * Return value: the contents of the field. The returned string is + * owned by the #RssDocument and should never be modified of freed + */ +const gchar * +rss_document_get_editor_uri (RssDocument *self) +{ + g_return_val_if_fail (RSS_IS_DOCUMENT (self), NULL); + + return self->priv->editor_uri; +} + +/** + * rss_document_get_contributor: + * @self: a #RssDocument + * + * Retrieves the #RssDocument:contributor field. + * + * Return value: the contents of the field. The returned string is + * owned by the #RssDocument and should never be modified of freed + */ +const gchar * +rss_document_get_contributor_name (RssDocument *self) +{ + g_return_val_if_fail (RSS_IS_DOCUMENT (self), NULL); + + return self->priv->contributor; +} + +/** + * rss_document_get_contributor_email: + * @self: a #RssDocument + * + * Retrieves the #RssDocument:contributor-email field. + * + * Return value: the contents of the field. The returned string is + * owned by the #RssDocument and should never be modified of freed + */ +const gchar * +rss_document_get_contributor_email (RssDocument *self) +{ + g_return_val_if_fail (RSS_IS_DOCUMENT (self), NULL); + + return self->priv->contributor_email; +} + +/** + * rss_document_get_contributor_uri: + * @self: a #RssDocument + * + * Retrieves the #RssDocument:contributor-uri field. + * + * Return value: the contents of the field. The returned string is + * owned by the #RssDocument and should never be modified of freed + */ +const gchar * +rss_document_get_contributor_uri (RssDocument *self) +{ + g_return_val_if_fail (RSS_IS_DOCUMENT (self), NULL); + + return self->priv->contributor_uri; +} + +/** + * rss_document_get_generator_name: + * @self: a #RssDocument + * + * Retrieves the #RssDocument:generator-name field. + * + * Return value: the contents of the field. The returned string is + * owned by the #RssDocument and should never be modified of freed + */ +const gchar * +rss_document_get_generator_name (RssDocument *self) +{ + g_return_val_if_fail (RSS_IS_DOCUMENT (self), NULL); + + return self->priv->generator; +} + +/** + * rss_document_get_generator_uri: + * @self: a #RssDocument + * + * Retrieves the #RssDocument:generator-uri field. + * + * Return value: the contents of the field. The returned string is + * owned by the #RssDocument and should never be modified of freed + */ +const gchar * +rss_document_get_generator_uri (RssDocument *self) +{ + g_return_val_if_fail (RSS_IS_DOCUMENT (self), NULL); + + return self->priv->generator_uri; +} + +/** + * rss_document_get_generator_version: + * @self: a #RssDocument + * + * Retrieves the #RssDocument:generator-version field. + * + * Return value: the contents of the field. The returned string is + * owned by the #RssDocument and should never be modified of freed + */ +const gchar * +rss_document_get_generator_version (RssDocument *self) +{ + g_return_val_if_fail (RSS_IS_DOCUMENT (self), NULL); + + return self->priv->generator_version; +} + +/** + * rss_document_get_image_title: + * @self: a #RssDocument + * + * Retrieves the #RssDocument:image-title field. + * + * Return value: the contents of the field. The returned string is + * owned by the #RssDocument and should never be modified of freed + */ +const gchar * +rss_document_get_image_title (RssDocument *self) +{ + g_return_val_if_fail (RSS_IS_DOCUMENT (self), NULL); + + return self->priv->image_title; +} + +/** + * rss_document_get_image_url: + * @self: a #RssDocument + * + * Retrieves the #RssDocument:image-url field. + * + * Return value: the contents of the field. The returned string is + * owned by the #RssDocument and should never be modified of freed + */ +const gchar * +rss_document_get_image_url (RssDocument *self) +{ + g_return_val_if_fail (RSS_IS_DOCUMENT (self), NULL); + + return self->priv->image_url; +} + +/** + * rss_document_get_image_link: + * @self: a #RssDocument + * + * Retrieves the #RssDocument:image-link field. + * + * Return value: the contents of the field. The returned string is + * owned by the #RssDocument and should never be modified of freed + */ +const gchar * +rss_document_get_image_link (RssDocument *self) +{ + g_return_val_if_fail (RSS_IS_DOCUMENT (self), NULL); + + return self->priv->image_link; +} + +/** + * rss_document_get_ttl: + * @self: a #RssDocument + * + * Retrieves the #RssDocument:ttl field. + * + * Return value: the contents of the field. The returned string is + * owned by the #RssDocument and should never be modified of freed + */ +gint +rss_document_get_ttl (RssDocument *self) +{ + g_return_val_if_fail (RSS_IS_DOCUMENT (self), 0); + + return self->priv->ttl; +} diff --git a/plugins/backend/decsync/rss-glib/rss-document.h b/plugins/backend/decsync/rss-glib/rss-document.h new file mode 100644 index 000000000..58c995abc --- /dev/null +++ b/plugins/backend/decsync/rss-glib/rss-document.h @@ -0,0 +1,105 @@ +/* rss-document.h + * + * This file is part of RSS-GLib. + * Copyright (C) 2008 Christian Hergert + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + * + * Author: + * Christian Hergert + */ + +#ifndef __RSS_DOCUMENT_H__ +#define __RSS_DOCUMENT_H__ + +#include + +G_BEGIN_DECLS + +#define RSS_TYPE_DOCUMENT rss_document_get_type() + +#define RSS_DOCUMENT(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + RSS_TYPE_DOCUMENT, \ + RssDocument)) + +#define RSS_DOCUMENT_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), \ + RSS_TYPE_DOCUMENT, \ + RssDocumentClass)) + +#define RSS_IS_DOCUMENT(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + RSS_TYPE_DOCUMENT)) + +#define RSS_IS_DOCUMENT_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + RSS_TYPE_DOCUMENT)) + +#define RSS_DOCUMENT_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + RSS_TYPE_DOCUMENT, \ + RssDocumentClass)) + +typedef struct _RssDocument RssDocument; +typedef struct _RssDocumentPrivate RssDocumentPrivate; +typedef struct _RssDocumentClass RssDocumentClass; + +struct _RssDocument +{ + /*< private >*/ + GObject parent_instance; + + RssDocumentPrivate *priv; +}; + +struct _RssDocumentClass +{ + /*< private >*/ + GObjectClass parent_class; +}; + +GType rss_document_get_type (void); +RssDocument* rss_document_new (void); + +const gchar *rss_document_get_guid (RssDocument *self); +const gchar *rss_document_get_about (RssDocument *self); +const gchar *rss_document_get_title (RssDocument *self); +const gchar *rss_document_get_description (RssDocument *self); +const gchar *rss_document_get_link (RssDocument *self); +const gchar *rss_document_get_encoding (RssDocument *self); +const gchar *rss_document_get_language (RssDocument *self); +const gchar *rss_document_get_rating (RssDocument *self); +const gchar *rss_document_get_copyright (RssDocument *self); +const gchar *rss_document_get_pub_date (RssDocument *self); +const gchar *rss_document_get_editor_name (RssDocument *self); +const gchar *rss_document_get_editor_email (RssDocument *self); +const gchar *rss_document_get_editor_uri (RssDocument *self); +const gchar *rss_document_get_contributor_name (RssDocument *self); +const gchar *rss_document_get_contributor_email (RssDocument *self); +const gchar *rss_document_get_contributor_uri (RssDocument *self); +const gchar *rss_document_get_generator_name (RssDocument *self); +const gchar *rss_document_get_generator_uri (RssDocument *self); +const gchar *rss_document_get_generator_version (RssDocument *self); +const gchar *rss_document_get_image_title (RssDocument *self); +const gchar *rss_document_get_image_url (RssDocument *self); +const gchar *rss_document_get_image_link (RssDocument *self); +gint rss_document_get_ttl (RssDocument *self); + +GList * rss_document_get_items (RssDocument *self); +GList * rss_document_get_categories (RssDocument *self); + +G_END_DECLS + +#endif /* __RSS_DOCUMENT_H__ */ diff --git a/plugins/backend/decsync/rss-glib/rss-glib-1.0.vapi b/plugins/backend/decsync/rss-glib/rss-glib-1.0.vapi new file mode 100644 index 000000000..2a571cb4a --- /dev/null +++ b/plugins/backend/decsync/rss-glib/rss-glib-1.0.vapi @@ -0,0 +1,126 @@ +/* rss-glib-1.0.vapi + * + * This file is part of RSS-GLib. + * Copyright (C) 2008 Christian Hergert + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + * + * Author: + * Christian Hergert + */ + +[CCode (cprefix = "Rss", lower_case_cprefix = "rss_", cheader_filename = "rss-glib.h")] +namespace Rss { + public errordomain ParserError { + INVALID_DATA + } + + public class Parser : GLib.Object { + public Parser (); + public bool load_from_data (string data, ulong length) throws Rss.ParserError; + public bool load_from_file (string filename) throws Rss.ParserError; + public Rss.Document get_document (); + } + + public class Document : GLib.Object { + [NoAccessorMethod] + public string encoding { owned get; set; } + [NoAccessorMethod] + public string guid { owned get; set; } + [NoAccessorMethod] + public string title { owned get; set; } + [NoAccessorMethod] + public string description { owned get; set; } + [NoAccessorMethod] + public string link { owned get; set; } + [NoAccessorMethod] + public string language { owned get; set; } + [NoAccessorMethod] + public string rating { owned get; set; } + [NoAccessorMethod] + public string copyright { owned get; set; } + [NoAccessorMethod] + public string pub_date { owned get; set; } + [NoAccessorMethod] + public string editor { owned get; set; } + [NoAccessorMethod] + public string editor_email { owned get; set; } + [NoAccessorMethod] + public string editor_uri { owned get; set; } + [NoAccessorMethod] + public int ttl { owned get; set; } + [NoAccessorMethod] + public string about { owned get; set; } + [NoAccessorMethod] + public string contributor { owned get; set; } + [NoAccessorMethod] + public string contributor_email { owned get; set; } + [NoAccessorMethod] + public string contributor_uri { owned get; set; } + [NoAccessorMethod] + public string generator { owned get; set; } + [NoAccessorMethod] + public string generator_uri { owned get; set; } + [NoAccessorMethod] + public string generator_version { owned get; set; } + [NoAccessorMethod] + public string image_title { owned get; set; } + [NoAccessorMethod] + public string image_url { owned get; set; } + [NoAccessorMethod] + public string image_link { owned get; set; } + public Document (); + public GLib.List get_items (); + public GLib.List get_categories (); + } + + public class Item : GLib.Object { + [NoAccessorMethod] + public string guid { owned get; set; } + [NoAccessorMethod] + public string title { owned get; set; } + [NoAccessorMethod] + public string link { owned get; set; } + [NoAccessorMethod] + public string description { owned get; set; } + [NoAccessorMethod] + public string copyright{ owned get; set; } + [NoAccessorMethod] + public string author { owned get; set; } + [NoAccessorMethod] + public string author_uri { owned get; set; } + [NoAccessorMethod] + public string author_email { owned get; set; } + [NoAccessorMethod] + public string contributor { owned get; set; } + [NoAccessorMethod] + public string contributor_uri { owned get; set; } + [NoAccessorMethod] + public string contributor_email { owned get; set; } + [NoAccessorMethod] + public string comments { owned get; set; } + [NoAccessorMethod] + public string pub_date { owned get; set; } + [NoAccessorMethod] + public string source { owned get; set; } + [NoAccessorMethod] + public string source_url { owned get; set; } + [NoAccessorMethod] + public string enclosure { owned get; set; } + [NoAccessorMethod] + public string enclosure_url { owned get; set; } + public Item (); + public GLib.List get_categories (); + } +} diff --git a/plugins/backend/decsync/rss-glib/rss-glib.h b/plugins/backend/decsync/rss-glib/rss-glib.h new file mode 100644 index 000000000..1595d64c4 --- /dev/null +++ b/plugins/backend/decsync/rss-glib/rss-glib.h @@ -0,0 +1,9 @@ +#ifndef __RSS_GLIB_H__ +#define __RSS_GLIB_H__ + +#include +#include +#include +#include + +#endif /* __RSS_GLIB_H__ */ diff --git a/plugins/backend/decsync/rss-glib/rss-item-private.h b/plugins/backend/decsync/rss-glib/rss-item-private.h new file mode 100644 index 000000000..d4951a382 --- /dev/null +++ b/plugins/backend/decsync/rss-glib/rss-item-private.h @@ -0,0 +1,48 @@ +/* rss-item-private.h + * + * This file is part of RSS-GLib. + * Copyright (C) 2008 Christian Hergert + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + * + * Author: + * Christian Hergert + */ + +#ifndef __RSS_ITEM_PRIVATE_H__ +#define __RSS_ITEM_PRIVATE_H__ + +struct _RssItemPrivate { + gchar *guid; + gchar *title; + gchar *link; + gchar *description; + gchar *copyright; + gchar *author; + gchar *author_uri; + gchar *author_email; + gchar *contributor; + gchar *contributor_uri; + gchar *contributor_email; + gchar *comments; + gchar *pub_date; + gchar *source; + gchar *source_url; + gchar *enclosure; + gchar *enclosure_url; + + GList *categories; +}; + +#endif /* __RSS_ITEM_PRIVATE_H__ */ diff --git a/plugins/backend/decsync/rss-glib/rss-item.c b/plugins/backend/decsync/rss-glib/rss-item.c new file mode 100644 index 000000000..fb39cbb97 --- /dev/null +++ b/plugins/backend/decsync/rss-glib/rss-item.c @@ -0,0 +1,819 @@ +/* rss-item.c + * + * This file is part of RSS-GLib. + * Copyright (C) 2008 Christian Hergert + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + * + * Author: + * Christian Hergert + */ + +/** + * SECTION:rss-item + * @short_description: a RSS entry representation + * + * #RssItem is the representation of an individual item parsed from a + * #RssDocument. This would mean an individual article, or note within + * the source feed. + */ + +#include "rss-item.h" +#include "rss-item-private.h" + +G_DEFINE_TYPE (RssItem, rss_item, G_TYPE_OBJECT); + +enum { + PROP_0, + + PROP_TITLE, + PROP_LINK, + PROP_DESCRIPTION, + PROP_COPYRIGHT, + PROP_AUTHOR, + PROP_AUTHOR_URI, + PROP_AUTHOR_EMAIL, + PROP_CONTRIBUTOR, + PROP_CONTRIBUTOR_URI, + PROP_CONTRIBUTOR_EMAIL, + PROP_COMMENTS, + PROP_PUB_DATE, + PROP_GUID, + PROP_SOURCE, + PROP_SOURCE_URL, + PROP_ENCLOSURE, + PROP_ENCLOSURE_URL +}; + +static void +rss_item_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + RssItemPrivate *priv = RSS_ITEM (object)->priv; + + switch (property_id) { + case PROP_ENCLOSURE: + g_value_set_string (value, priv->enclosure); + break; + case PROP_ENCLOSURE_URL: + g_value_set_string (value, priv->enclosure_url); + break; + case PROP_TITLE: + g_value_set_string (value, priv->title); + break; + case PROP_LINK: + g_value_set_string (value, priv->link); + break; + case PROP_DESCRIPTION: + g_value_set_string (value, priv->description); + break; + case PROP_COPYRIGHT: + g_value_set_string (value, priv->copyright); + break; + case PROP_AUTHOR: + g_value_set_string (value, priv->author); + break; + case PROP_AUTHOR_URI: + g_value_set_string (value, priv->author_uri); + break; + case PROP_AUTHOR_EMAIL: + g_value_set_string (value, priv->author_email); + break; + case PROP_CONTRIBUTOR: + g_value_set_string (value, priv->contributor); + break; + case PROP_CONTRIBUTOR_URI: + g_value_set_string (value, priv->contributor_uri); + break; + case PROP_CONTRIBUTOR_EMAIL: + g_value_set_string (value, priv->contributor_email); + break; + case PROP_COMMENTS: + g_value_set_string (value, priv->comments); + break; + case PROP_PUB_DATE: + g_value_set_string (value, priv->pub_date); + break; + case PROP_GUID: + g_value_set_string (value, priv->guid); + break; + case PROP_SOURCE: + g_value_set_string (value, priv->source); + break; + case PROP_SOURCE_URL: + g_value_set_string (value, priv->source_url); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + +static void +rss_item_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + RssItemPrivate *priv = RSS_ITEM (object)->priv; + + switch (property_id) { + case PROP_ENCLOSURE: + g_free (priv->enclosure); + priv->enclosure = g_value_dup_string (value); + break; + case PROP_ENCLOSURE_URL: + g_free (priv->enclosure_url); + priv->enclosure_url = g_value_dup_string (value); + break; + case PROP_TITLE: + g_free (priv->title); + priv->title = g_value_dup_string (value); + break; + case PROP_LINK: + g_free (priv->link); + priv->link = g_value_dup_string (value); + break; + case PROP_DESCRIPTION: + g_free (priv->description); + priv->description = g_value_dup_string (value); + break; + case PROP_COPYRIGHT: + g_free (priv->copyright); + priv->copyright = g_value_dup_string (value); + break; + case PROP_AUTHOR: + g_free (priv->author); + priv->author = g_value_dup_string (value); + break; + case PROP_AUTHOR_URI: + g_free (priv->author_uri); + priv->author_uri = g_value_dup_string (value); + break; + case PROP_AUTHOR_EMAIL: + g_free (priv->author_email); + priv->author_email = g_value_dup_string (value); + break; + case PROP_CONTRIBUTOR: + g_free (priv->contributor); + priv->contributor = g_value_dup_string (value); + break; + case PROP_CONTRIBUTOR_URI: + g_free (priv->contributor_uri); + priv->contributor_uri = g_value_dup_string (value); + break; + case PROP_CONTRIBUTOR_EMAIL: + g_free (priv->contributor_email); + priv->contributor_email = g_value_dup_string (value); + break; + case PROP_COMMENTS: + g_free (priv->comments); + priv->comments = g_value_dup_string (value); + break; + case PROP_PUB_DATE: + g_free (priv->pub_date); + priv->pub_date = g_value_dup_string (value); + break; + case PROP_GUID: + g_free (priv->guid); + priv->guid = g_value_dup_string (value); + break; + case PROP_SOURCE: + g_free (priv->source); + priv->source = g_value_dup_string (value); + break; + case PROP_SOURCE_URL: + g_free (priv->source_url); + priv->source_url = g_value_dup_string (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + +static void +rss_item_finalize (GObject *object) +{ + RssItemPrivate *priv = RSS_ITEM (object)->priv; + + g_free (priv->guid); + g_free (priv->title); + g_free (priv->link); + g_free (priv->description); + g_free (priv->copyright); + g_free (priv->author); + g_free (priv->author_uri); + g_free (priv->author_email); + g_free (priv->contributor); + g_free (priv->contributor_uri); + g_free (priv->contributor_email); + g_free (priv->comments); + g_free (priv->pub_date); + g_free (priv->source); + g_free (priv->source_url); + g_free (priv->enclosure); + g_free (priv->enclosure_url); + + g_list_foreach (priv->categories, (GFunc) g_free, NULL); + g_list_free (priv->categories); + + G_OBJECT_CLASS (rss_item_parent_class)->finalize (object); +} + +static void +rss_item_class_init (RssItemClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GParamSpec *pspec; + + g_type_class_add_private (klass, sizeof (RssItemPrivate)); + + gobject_class->get_property = rss_item_get_property; + gobject_class->set_property = rss_item_set_property; + gobject_class->finalize = rss_item_finalize; + + /** + * RssItem:enclosure: + * + * The enclosure of the item. + */ + pspec = g_param_spec_string ("enclosure", + "Enclosure", + "Enclosure of the item", + NULL, + G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, + PROP_ENCLOSURE, + pspec); + + + /** + * RssItem:enclosure_url: + * + * The enclosure_url of the item. + */ + pspec = g_param_spec_string ("enclosure-url", + "Enclosure URL", + "Enclosure URL of the item", + NULL, + G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, + PROP_ENCLOSURE_URL, + pspec); + + /** + * RssItem:title: + * + * The title of the item. + */ + pspec = g_param_spec_string ("title", + "Title", + "Title of the item", + NULL, + G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, + PROP_TITLE, + pspec); + + /** + * RssItem:link: + * + * The link to the upstream source of the item. + */ + pspec = g_param_spec_string ("link", + "Link", + "Link to the upstream source", + NULL, + G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, + PROP_LINK, + pspec); + + /** + * RssItem:description: + * + * The description of the item. This is often where the actual + * content for the item is stored. + */ + pspec = g_param_spec_string ("description", + "Description", + "Description of the item", + NULL, + G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, + PROP_DESCRIPTION, + pspec); + + /** + * RssItem:copyright: + * + * Any associated copyright that may exist for the content. + */ + pspec = g_param_spec_string ("copyright", + "Copyright", + "Any associated copyright for " + "the content", + NULL, + G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, + PROP_COPYRIGHT, + pspec); + + /** + * RssItem:author: + * + * The author's name. + */ + pspec = g_param_spec_string ("author", + "Author", + "The name of the author", + NULL, + G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, + PROP_AUTHOR, + pspec); + + /** + * RssItem:author-uri: + * + * The authors uri, often a website. + */ + pspec = g_param_spec_string ("author-uri", + "Author URI", + "The URI of the author", + NULL, + G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, + PROP_AUTHOR_URI, + pspec); + + /** + * RssItem:author-email: + * + * The authors email. + */ + pspec = g_param_spec_string ("author-email", + "Author Email", + "The email address of the author", + NULL, + G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, + PROP_AUTHOR_EMAIL, + pspec); + + /** + * RssItem:contributor: + * + * The contributors name. + */ + pspec = g_param_spec_string ("contributor", + "Contributor", + "The name of the contributor", + NULL, + G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, + PROP_CONTRIBUTOR, + pspec); + + /** + * RssItem:contributor-uri: + * + * The contributors uri, often a website. + */ + pspec = g_param_spec_string ("contributor-uri", + "Contributor URI", + "The URI of the contributor", + NULL, + G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, + PROP_CONTRIBUTOR_URI, + pspec); + + /** + * RssItem:contributor-email: + * + * The contributors email. + */ + pspec = g_param_spec_string ("contributor-email", + "Contributor Email", + "The email of the contributor", + NULL, + G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, + PROP_CONTRIBUTOR_EMAIL, + pspec); + + /** + * RssItem:comments: + * + * Any comments that may have been associated with the item. + */ + pspec = g_param_spec_string ("comments", + "Comments", + "Any comment associated to the item", + NULL, + G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, + PROP_COMMENTS, + pspec); + + /** + * RssItem:pub-date: + * + * The string representation of the publish date. + */ + pspec = g_param_spec_string ("pub-date", + "Publication Date", + "The date of publication", + NULL, + G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, + PROP_PUB_DATE, + pspec); + + /** + * RssItem:guid: + * + * The guid of the item. Many feed engines will use the url here + * plus some tag metadata. + */ + pspec = g_param_spec_string ("guid", + "GUID", + "The guid of the item", + NULL, + G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, + PROP_GUID, + pspec); + + /** + * RssItem:source: + * + * The name of the source of the item. + */ + pspec = g_param_spec_string ("source", + "Source", + "Source of the item", + NULL, + G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, + PROP_SOURCE, + pspec); + + /** + * RssItem:source-url: + * + * The url of the source of the item. + */ + pspec = g_param_spec_string ("source-url", + "Source URL", + "URL of the source", + NULL, + G_PARAM_READWRITE); + g_object_class_install_property (gobject_class, + PROP_SOURCE_URL, + pspec); +} + +static void +rss_item_init (RssItem *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, RSS_TYPE_ITEM, RssItemPrivate); +} + +/** + * rss_item_new: + * + * Creates a new #RssItem. This isn't incredibly useful yet as we do not + * have an RssGenerator written. However, that will come eventuall and + * make creating feeds much simpler. + * + * Returns: a new #RssItem which should be unref'd with g_object_unref(). + */ +RssItem* +rss_item_new (void) +{ + return g_object_new (RSS_TYPE_ITEM, NULL); +} + +/** + * rss_item_get_guid: + * @self: a #RssItem + * + * Retrieves the #RssItem:guid field. + * + * Return value: the value of the field. The returned string is + * owned by the #RssItem and should never be modified or freed. + */ +const gchar * +rss_item_get_guid (RssItem *self) +{ + g_return_val_if_fail (RSS_IS_ITEM (self), NULL); + + return self->priv->guid; +} + +/** + * rss_item_get_enclosure: + * @self: a #RssItem + * + * Retrieves the #RssItem:enclosure field. + * + * Return value: the value of the field. The returned string is + * owned by the #RssItem and should never be modified or freed. + */ +const gchar * +rss_item_get_enclosure (RssItem *self) +{ + g_return_val_if_fail (RSS_IS_ITEM (self), NULL); + + return self->priv->enclosure; +} + +/** + * rss_item_get_enclosure_url: + * @self: a #RssItem + * + * Retrieves the #RssItem:enclosure_url field. + * + * Return value: the value of the field. The returned string is + * owned by the #RssItem and should never be modified or freed. + */ +const gchar * +rss_item_get_enclosure_url (RssItem *self) +{ + g_return_val_if_fail (RSS_IS_ITEM (self), NULL); + + return self->priv->enclosure_url; +} + +/** + * rss_item_get_title: + * @self: a #RssItem + * + * Retrieves the #RssItem:title field. + * + * Return value: the value of the field. The returned string is + * owned by the #RssItem and should never be modified or freed. + */ +const gchar * +rss_item_get_title (RssItem *self) +{ + g_return_val_if_fail (RSS_IS_ITEM (self), NULL); + + return self->priv->title; +} + +/** + * rss_item_get_link: + * @self: a #RssItem + * + * Retrieves the #RssItem:link field. + * + * Return value: the value of the field. The returned string is + * owned by the #RssItem and should never be modified or freed. + */ +const gchar * +rss_item_get_link (RssItem *self) +{ + g_return_val_if_fail (RSS_IS_ITEM (self), NULL); + + return self->priv->link; +} + +/** + * rss_item_get_description: + * @self: a #RssItem + * + * Retrieves the #RssItem:description field. + * + * Return value: the value of the field. The returned string is + * owned by the #RssItem and should never be modified or freed. + */ +const gchar * +rss_item_get_description (RssItem *self) +{ + g_return_val_if_fail (RSS_IS_ITEM (self), NULL); + + return self->priv->description; +} + +/** + * rss_item_get_copyright: + * @self: a #RssItem + * + * Retrieves the #RssItem:copyright field. + * + * Return value: the value of the field. The returned string is + * owned by the #RssItem and should never be modified or freed. + */ +const gchar * +rss_item_get_copyright (RssItem *self) +{ + g_return_val_if_fail (RSS_IS_ITEM (self), NULL); + + return self->priv->copyright; +} + +/** + * rss_item_get_author_name: + * @self: a #RssItem + * + * Retrieves the #RssItem:author field. + * + * Return value: the value of the field. The returned string is + * owned by the #RssItem and should never be modified or freed. + */ +const gchar * +rss_item_get_author_name (RssItem *self) +{ + g_return_val_if_fail (RSS_IS_ITEM (self), NULL); + + return self->priv->author; +} + +/** + * rss_item_get_author_uri: + * @self: a #RssItem + * + * Retrieves the #RssItem:author-uri field. + * + * Return value: the value of the field. The returned string is + * owned by the #RssItem and should never be modified or freed. + */ +const gchar * +rss_item_get_author_uri (RssItem *self) +{ + g_return_val_if_fail (RSS_IS_ITEM (self), NULL); + + return self->priv->author_uri; +} + +/** + * rss_item_get_author_email: + * @self: a #RssItem + * + * Retrieves the #RssItem:author-email field. + * + * Return value: the value of the field. The returned string is + * owned by the #RssItem and should never be modified or freed. + */ +const gchar * +rss_item_get_author_email (RssItem *self) +{ + g_return_val_if_fail (RSS_IS_ITEM (self), NULL); + + return self->priv->author_email; +} + +/** + * rss_item_get_contributor_name: + * @self: a #RssItem + * + * Retrieves the #RssItem:contributor field. + * + * Return value: the value of the field. The returned string is + * owned by the #RssItem and should never be modified or freed. + */ +const gchar * +rss_item_get_contributor_name (RssItem *self) +{ + g_return_val_if_fail (RSS_IS_ITEM (self), NULL); + + return self->priv->contributor; +} + +/** + * rss_item_get_contributor_uri: + * @self: a #RssItem + * + * Retrieves the #RssItem:contributor-uri field. + * + * Return value: the value of the field. The returned string is + * owned by the #RssItem and should never be modified or freed. + */ +const gchar * +rss_item_get_contributor_uri (RssItem *self) +{ + g_return_val_if_fail (RSS_IS_ITEM (self), NULL); + + return self->priv->contributor_uri; +} + +/** + * rss_item_get_contributor_email: + * @self: a #RssItem + * + * Retrieves the #RssItem:contributor-email field. + * + * Return value: the value of the field. The returned string is + * owned by the #RssItem and should never be modified or freed. + */ +const gchar * +rss_item_get_contributor_email (RssItem *self) +{ + g_return_val_if_fail (RSS_IS_ITEM (self), NULL); + + return self->priv->contributor_email; +} + +/** + * rss_item_get_comments: + * @self: a #RssItem + * + * Retrieves the #RssItem:comments field. + * + * Return value: the value of the field. The returned string is + * owned by the #RssItem and should never be modified or freed. + */ +const gchar * +rss_item_get_comments (RssItem *self) +{ + g_return_val_if_fail (RSS_IS_ITEM (self), NULL); + + return self->priv->comments; +} + +/** + * rss_item_get_pub_date: + * @self: a #RssItem + * + * Retrieves the #RssItem:pub-date field. + * + * Return value: the value of the field. The returned string is + * owned by the #RssItem and should never be modified or freed. + */ +const gchar * +rss_item_get_pub_date (RssItem *self) +{ + g_return_val_if_fail (RSS_IS_ITEM (self), NULL); + + return self->priv->pub_date; +} + +/** + * rss_item_get_source: + * @self: a #RssItem + * + * Retrieves the #RssItem:source field. + * + * Return value: the value of the field. The returned string is + * owned by the #RssItem and should never be modified or freed. + */ +const gchar * +rss_item_get_source (RssItem *self) +{ + g_return_val_if_fail (RSS_IS_ITEM (self), NULL); + + return self->priv->source; +} + +/** + * rss_item_get_source_url: + * @self: a #RssItem + * + * Retrieves the #RssItem:source-url field. + * + * Return value: the value of the field. The returned string is + * owned by the #RssItem and should never be modified or freed. + */ +const gchar * +rss_item_get_source_url (RssItem *self) +{ + g_return_val_if_fail (RSS_IS_ITEM (self), NULL); + + return self->priv->source_url; +} + +/** + * rss_item_get_categories: + * @self: a #RssItem + * + * Returns a copy of the list of categories for the #RssItem. The data + * in the linked list are pointers to strings (char*). They are owned + * by the #RssItem and should not be modified. Use g_strdup() to copy + * the individual strings. + * + * Returns: a new #GList which should be freed with g_list_free(). + */ +GList* +rss_item_get_categories (RssItem *self) +{ + g_return_val_if_fail (RSS_IS_ITEM (self), NULL); + + return g_list_copy (self->priv->categories); +} diff --git a/plugins/backend/decsync/rss-glib/rss-item.h b/plugins/backend/decsync/rss-glib/rss-item.h new file mode 100644 index 000000000..1bbe041c0 --- /dev/null +++ b/plugins/backend/decsync/rss-glib/rss-item.h @@ -0,0 +1,97 @@ +/* rss-item.h + * + * This file is part of RSS-GLib. + * Copyright (C) 2008 Christian Hergert + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + * + * Author: + * Christian Hergert + */ + +#ifndef __RSS_ITEM_H__ +#define __RSS_ITEM_H__ + +#include + +G_BEGIN_DECLS + +#define RSS_TYPE_ITEM rss_item_get_type() + +#define RSS_ITEM(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + RSS_TYPE_ITEM, \ + RssItem)) + +#define RSS_ITEM_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), \ + RSS_TYPE_ITEM, \ + RssItemClass)) + +#define RSS_IS_ITEM(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + RSS_TYPE_ITEM)) + +#define RSS_IS_ITEM_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + RSS_TYPE_ITEM)) + +#define RSS_ITEM_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + RSS_TYPE_ITEM, \ + RssItemClass)) + +typedef struct _RssItem RssItem; +typedef struct _RssItemPrivate RssItemPrivate; +typedef struct _RssItemClass RssItemClass; + +struct _RssItem +{ + /*< private >*/ + GObject parent_instance; + + RssItemPrivate *priv; +}; + +struct _RssItemClass +{ + /*< private >*/ + GObjectClass parent_class; +}; + +GType rss_item_get_type (void); +RssItem* rss_item_new (void); + +const gchar *rss_item_get_guid (RssItem *self); +const gchar *rss_item_get_title (RssItem *self); +const gchar *rss_item_get_link (RssItem *self); +const gchar *rss_item_get_description (RssItem *self); +const gchar *rss_item_get_copyright (RssItem *self); +const gchar *rss_item_get_author_name (RssItem *self); +const gchar *rss_item_get_author_uri (RssItem *self); +const gchar *rss_item_get_author_email (RssItem *self); +const gchar *rss_item_get_contributor_name (RssItem *self); +const gchar *rss_item_get_contributor_uri (RssItem *self); +const gchar *rss_item_get_contributor_email (RssItem *self); +const gchar *rss_item_get_comments (RssItem *self); +const gchar *rss_item_get_pub_date (RssItem *self); +const gchar *rss_item_get_source (RssItem *self); +const gchar *rss_item_get_source_url (RssItem *self); +const gchar *rss_item_get_enclosure (RssItem *self); +const gchar *rss_item_get_enclosure_url (RssItem *self); +GList* rss_item_get_categories (RssItem *self); + +G_END_DECLS + +#endif /* __RSS_ITEM_H__ */ diff --git a/plugins/backend/decsync/rss-glib/rss-marshal.c b/plugins/backend/decsync/rss-glib/rss-marshal.c new file mode 100644 index 000000000..84e4a2dc7 --- /dev/null +++ b/plugins/backend/decsync/rss-glib/rss-marshal.c @@ -0,0 +1,54 @@ +#include "rss-marshal.h" + +#include + + +#ifdef G_ENABLE_DEBUG +#define g_marshal_value_peek_boolean(v) g_value_get_boolean (v) +#define g_marshal_value_peek_char(v) g_value_get_schar (v) +#define g_marshal_value_peek_uchar(v) g_value_get_uchar (v) +#define g_marshal_value_peek_int(v) g_value_get_int (v) +#define g_marshal_value_peek_uint(v) g_value_get_uint (v) +#define g_marshal_value_peek_long(v) g_value_get_long (v) +#define g_marshal_value_peek_ulong(v) g_value_get_ulong (v) +#define g_marshal_value_peek_int64(v) g_value_get_int64 (v) +#define g_marshal_value_peek_uint64(v) g_value_get_uint64 (v) +#define g_marshal_value_peek_enum(v) g_value_get_enum (v) +#define g_marshal_value_peek_flags(v) g_value_get_flags (v) +#define g_marshal_value_peek_float(v) g_value_get_float (v) +#define g_marshal_value_peek_double(v) g_value_get_double (v) +#define g_marshal_value_peek_string(v) (char*) g_value_get_string (v) +#define g_marshal_value_peek_param(v) g_value_get_param (v) +#define g_marshal_value_peek_boxed(v) g_value_get_boxed (v) +#define g_marshal_value_peek_pointer(v) g_value_get_pointer (v) +#define g_marshal_value_peek_object(v) g_value_get_object (v) +#define g_marshal_value_peek_variant(v) g_value_get_variant (v) +#else /* !G_ENABLE_DEBUG */ +/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API. + * Do not access GValues directly in your code. Instead, use the + * g_value_get_*() functions + */ +#define g_marshal_value_peek_boolean(v) (v)->data[0].v_int +#define g_marshal_value_peek_char(v) (v)->data[0].v_int +#define g_marshal_value_peek_uchar(v) (v)->data[0].v_uint +#define g_marshal_value_peek_int(v) (v)->data[0].v_int +#define g_marshal_value_peek_uint(v) (v)->data[0].v_uint +#define g_marshal_value_peek_long(v) (v)->data[0].v_long +#define g_marshal_value_peek_ulong(v) (v)->data[0].v_ulong +#define g_marshal_value_peek_int64(v) (v)->data[0].v_int64 +#define g_marshal_value_peek_uint64(v) (v)->data[0].v_uint64 +#define g_marshal_value_peek_enum(v) (v)->data[0].v_long +#define g_marshal_value_peek_flags(v) (v)->data[0].v_ulong +#define g_marshal_value_peek_float(v) (v)->data[0].v_float +#define g_marshal_value_peek_double(v) (v)->data[0].v_double +#define g_marshal_value_peek_string(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_param(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_boxed(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_pointer(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_object(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_variant(v) (v)->data[0].v_pointer +#endif /* !G_ENABLE_DEBUG */ + + +/* VOID:VOID (./rss-marshal.list:1) */ + diff --git a/plugins/backend/decsync/rss-glib/rss-marshal.h b/plugins/backend/decsync/rss-glib/rss-marshal.h new file mode 100644 index 000000000..159a0ac68 --- /dev/null +++ b/plugins/backend/decsync/rss-glib/rss-marshal.h @@ -0,0 +1,15 @@ + +#ifndef ___rss_marshal_MARSHAL_H__ +#define ___rss_marshal_MARSHAL_H__ + +#include + +G_BEGIN_DECLS + +/* VOID:VOID (./rss-marshal.list:1) */ +#define _rss_marshal_VOID__VOID g_cclosure_marshal_VOID__VOID + +G_END_DECLS + +#endif /* ___rss_marshal_MARSHAL_H__ */ + diff --git a/plugins/backend/decsync/rss-glib/rss-parser-private.h b/plugins/backend/decsync/rss-glib/rss-parser-private.h new file mode 100644 index 000000000..eaddb9e41 --- /dev/null +++ b/plugins/backend/decsync/rss-glib/rss-parser-private.h @@ -0,0 +1,45 @@ +/* rss-parser-private.h + * + * This file is part of RSS-GLib. + * Copyright (C) 2008 Christian Hergert + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + * + * Author: + * Christian Hergert + */ + +#ifndef __RSS_PARSER_PRIVATE_H__ +#define __RSS_PARSER_PRIVATE_H__ + +#include + +G_BEGIN_DECLS + +struct _RssParserPrivate +{ + RssDocument *document; +}; + +typedef enum +{ + RSS_PARSER_ERROR_INVALID_DATA, +} RssParserError; + +#define RSS_PARSER_ERROR rss_parser_error_quark() +GQuark rss_parser_error_quark (void); + +G_END_DECLS + +#endif /* __RSS_PARSER_PRIVATE_H__ */ diff --git a/plugins/backend/decsync/rss-glib/rss-parser.c b/plugins/backend/decsync/rss-glib/rss-parser.c new file mode 100644 index 000000000..451a7f9f6 --- /dev/null +++ b/plugins/backend/decsync/rss-glib/rss-parser.c @@ -0,0 +1,338 @@ +/* rss-generator.c + * + * This file is part of RSS-GLib. + * Copyright (C) 2008 Christian Hergert + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + * + * Author: + * Christian Hergert + */ + +/** + * SECTION:rss-parser + * @short_description: Parse RSS data streams + * + * #RssParser provides an object for parsing a RSS data stream, either + * inside a file or inside a static buffer. + */ + +#include "rss-parser.h" +#include "rss-parser-private.h" + +#include "rss-item.h" +#include "rss-item-private.h" + +#include "rss-document.h" +#include "rss-document-private.h" + +#include "rss-marshal.h" + +GQuark +rss_parser_error_quark (void) +{ + return g_quark_from_static_string ("rss_parser_error"); +} + +enum +{ + PARSE_START, + PARSE_END, + + LAST_SIGNAL +}; + +static guint parser_signals[LAST_SIGNAL] = { 0, }; + +G_DEFINE_TYPE (RssParser, rss_parser, G_TYPE_OBJECT); + +static void +rss_parser_dispose (GObject *object) +{ + RssParserPrivate *priv = RSS_PARSER (object)->priv; + + if (priv->document) { + g_object_unref (priv->document); + priv->document = NULL; + } + + G_OBJECT_CLASS (rss_parser_parent_class)->dispose (object); +} + +static void +rss_parser_class_init (RssParserClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (klass, sizeof (RssParserPrivate)); + object_class->dispose = rss_parser_dispose; + + /** + * RssParser::parse-start: + * @parser: the #RssParser that received the signal + * + * The ::parse-signal is emitted when the parser began parsing + * a RSS data stream. + */ + parser_signals[PARSE_START] = + g_signal_new ("parse-start", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (RssParserClass, parse_start), + NULL, NULL, + _rss_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + /** + * RssParser::parse-end: + * @parser: the #RssParser that received the signal + * + * The ::parse-end signal is emitted when the parser successfully + * finished parsing a RSS data stream. + */ + parser_signals[PARSE_END] = + g_signal_new ("parse-end", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (RssParserClass, parse_end), + NULL, NULL, + _rss_marshal_VOID__VOID, + G_TYPE_NONE, 0); +} + +static void +rss_parser_init (RssParser *self) +{ + self->priv = + G_TYPE_INSTANCE_GET_PRIVATE (self, RSS_TYPE_PARSER, + RssParserPrivate); +} + +/** + * rss_parser_new: + * + * Creates a new #RssParser instance. You can use the #RssParser to + * load a RSS stream from either a file or a buffer and then walk the + * items discovered through the resulting RssDocument. + * + * Return value: the new created #RssParser. Use g_object_unref() to + * release all the memory it allocates. + */ +RssParser* +rss_parser_new (void) +{ + return g_object_new (RSS_TYPE_PARSER, NULL); +} + +static RssDocument* +rss_parser_parse (RssParser *self, mrss_t *mrss) +{ + RssDocument *document; + RssItem *rss_item; + GList *list, *list2; + mrss_category_t *cat; + mrss_item_t *item; + + g_return_val_if_fail (RSS_IS_PARSER (self), NULL); + g_return_val_if_fail (mrss != NULL, NULL); + + /* create our document object */ + document = rss_document_new (); + + /* set our document level properties */ + g_object_set (document, + "encoding", mrss->encoding, + "guid", mrss->id, + "title", mrss->title, + "description", mrss->description, + "link", mrss->link, + "language", mrss->language, + "rating", mrss->rating, + "copyright", mrss->copyright, + "pub-date", mrss->pubDate, + "ttl", mrss->ttl, + "about", mrss->about, + "contributor", mrss->contributor, + "contributor-email", mrss->contributor_email, + "contributor-uri", mrss->contributor_uri, + "generator", mrss->generator, + "generator-uri", mrss->generator_uri, + "generator-version", mrss->generator_version, + "image-title", mrss->image_title, + "image-url", mrss->image_url, + "image-link", mrss->image_link, + NULL); + + /* build the list of categories */ + if (NULL != (cat = mrss->category)) { + list = NULL; + do { + list = g_list_prepend (list, g_strdup (cat->category)); + } while (NULL != (cat = cat->next)); + document->priv->categories = list; + } + + /* build the list of items */ + if (NULL != (item = mrss->item)) { + list = NULL; + do { + rss_item = rss_item_new (); + + /* set the rss item properties */ + g_object_set (rss_item, + "guid", item->guid, + "title", item->title, + "link", item->link, + "description", item->description, + "copyright", item->copyright, + "author", item->author, + "author-uri", item->author_uri, + "author-email", item->author_email, + "contributor", item->contributor, + "contributor-uri", item->contributor_uri, + "contributor-email", item->contributor_email, + "comments", item->comments, + "pub-date", item->pubDate, + "source", item->source, + "source-url", item->source_url, + "enclosure", item->enclosure, + "enclosure-url", item->enclosure_url, + NULL); + + /* parse the items categories */ + if (NULL != (cat = item->category)) { + list2 = NULL; + do { + list2 = g_list_prepend (list2, g_strdup (cat->category)); + } while (NULL != (cat = cat->next)); + rss_item->priv->categories = list2; + } + + list = g_list_prepend (list, rss_item); + } while (NULL != (item = item->next)); + document->priv->items = list; + } + + return document; +} + +/** + * rss_parser_load_from_data: + * @self: a #RssParser + * @data: a buffer containing the syndication data + * @length: the length of the buffer + * @error: a location to place a newly created GError in case of error + * + * Parses the contents found at @data as an rss file. You can retrieve + * the parsed document with rss_parser_get_document(). + * + * Returns: TRUE on success. + */ +gboolean +rss_parser_load_from_data (RssParser *self, + const gchar *data, + gsize length, + GError **error) +{ + mrss_t *mrss; + mrss_error_t res; + + g_signal_emit (self, parser_signals[PARSE_START], 0); + + /* parse the buffer */ + res = mrss_parse_buffer ((char*)data, length, &mrss); + + /* if there was an error parsing, set the error and return false */ + if (MRSS_OK != res) { + if (error) { + g_set_error (error, RSS_PARSER_ERROR, + RSS_PARSER_ERROR_INVALID_DATA, + "Could not parse data contents"); + } + return FALSE; + } + + /* keep a copy of our parsed document */ + self->priv->document = rss_parser_parse (self, mrss); + + /* free our mrss data */ + mrss_free (mrss); + + g_signal_emit (self, parser_signals[PARSE_END], 0); + + return TRUE; +} + +/** + * rss_parser_load_from_file: + * @self: a #RssParser + * @filename: the path to the file to parse + * @error: a location for a newly created #GError + * + * Parses the file found at @filename as an rss file. You can retrieve + * the parsed document with rss_parser_get_document(). + * + * Returns: TRUE if the parse was successful + */ +gboolean +rss_parser_load_from_file (RssParser *self, + gchar *filename, + GError **error) +{ + mrss_t *mrss; + mrss_error_t res; + + g_signal_emit (self, parser_signals[PARSE_START], 0); + + /* parse the buffer */ + res = mrss_parse_file (filename, &mrss); + + /* if there was an error parsing, set the error and return false */ + if (MRSS_OK != res) { + if (error) { + g_set_error (error, RSS_PARSER_ERROR, + RSS_PARSER_ERROR_INVALID_DATA, + "Could not parse file contents"); + } + return FALSE; + } + + /* keep a copy of our parsed document */ + self->priv->document = rss_parser_parse (self, mrss); + + /* free our mrss data */ + mrss_free (mrss); + + g_signal_emit (self, parser_signals[PARSE_END], 0); + + return TRUE; +} + +/** + * rss_parser_get_document: + * @self: a #RssParser + * + * Retreives the document result from parsing rss data from either + * a buffer or a file. The document's ref-count is increased, so + * call g_object_unref when you are done. + * + * Returns: a #RssDocument + */ +RssDocument* +rss_parser_get_document (RssParser *self) +{ + g_return_val_if_fail (RSS_IS_PARSER (self), NULL); + + return g_object_ref (self->priv->document); +} diff --git a/plugins/backend/decsync/rss-glib/rss-parser.h b/plugins/backend/decsync/rss-glib/rss-parser.h new file mode 100644 index 000000000..6ab95a3d6 --- /dev/null +++ b/plugins/backend/decsync/rss-glib/rss-parser.h @@ -0,0 +1,102 @@ +/* rss-parser.h + * + * This file is part of RSS-GLib. + * Copyright (C) 2008 Christian Hergert + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + * + * Author: + * Christian Hergert + */ + +#ifndef __RSS_PARSER_H__ +#define __RSS_PARSER_H__ + +#include +#include "rss-document.h" + +G_BEGIN_DECLS + +#define RSS_TYPE_PARSER rss_parser_get_type() + +#define RSS_PARSER(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + RSS_TYPE_PARSER, \ + RssParser)) + +#define RSS_PARSER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), \ + RSS_TYPE_PARSER, \ + RssParserClass)) + +#define RSS_IS_PARSER(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + RSS_TYPE_PARSER)) + +#define RSS_IS_PARSER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + RSS_TYPE_PARSER)) + +#define RSS_PARSER_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + RSS_TYPE_PARSER, \ + RssParserClass)) + +typedef struct _RssParser RssParser; +typedef struct _RssParserPrivate RssParserPrivate; +typedef struct _RssParserClass RssParserClass; + +struct _RssParser +{ + /*< private >*/ + GObject parent_instance; + + RssParserPrivate *priv; +}; + +struct _RssParserClass +{ + /*< private >*/ + GObjectClass parent_class; + + /*< public >*/ + void (* parse_start) (RssParser *parser); + void (* parse_end) (RssParser *parser); + + /*< private >*/ + /* padding for future expansion */ + void (* _rss_reserved1) (void); + void (* _rss_reserved2) (void); + void (* _rss_reserved3) (void); + void (* _rss_reserved4) (void); + void (* _rss_reserved5) (void); + void (* _rss_reserved6) (void); + void (* _rss_reserved7) (void); + void (* _rss_reserved8) (void); +}; + +GType rss_parser_get_type (void); +RssParser* rss_parser_new (void); +gboolean rss_parser_load_from_data (RssParser * self, + const gchar *data, + gsize length, + GError **error); +gboolean rss_parser_load_from_file (RssParser *self, + gchar *filename, + GError **error); +RssDocument* rss_parser_get_document (RssParser *self); + +G_END_DECLS + +#endif /* __RSS_PARSER_H__ */ diff --git a/plugins/backend/decsync/rss-glib/rss-version.h b/plugins/backend/decsync/rss-glib/rss-version.h new file mode 100644 index 000000000..6a618529b --- /dev/null +++ b/plugins/backend/decsync/rss-glib/rss-version.h @@ -0,0 +1,96 @@ +/* rss-version.h - RSS-GLib versioning information + * + * This file is part of RSS-GLib. + * Copyright (C) 2008 Christian Hergert + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + * + * Author: + * Christian Hergert + */ + +#ifndef __RSS_VERSION_H__ +#define __RSS_VERSION_H__ + +/** + * SECTION:rss-version + * @title: Versioning + * @short_description: RSS-GLib version checking + * + * RSS-GLib provides macros to check the version of the library + * at compile-time + */ + +/** + * RSS_MAJOR_VERSION: + * + * Rss major version component (e.g. 1 if %RSS_VERSION is 1.2.3) + */ +#define RSS_MAJOR_VERSION (0) + +/** + * RSS_MINOR_VERSION: + * + * Rss minor version component (e.g. 2 if %RSS_VERSION is 1.2.3) + */ +#define RSS_MINOR_VERSION (2) + +/** + * RSS_MICRO_VERSION: + * + * Rss micro version component (e.g. 3 if %RSS_VERSION is 1.2.3) + */ +#define RSS_MICRO_VERSION (3) + +/** + * RSS_VERSION + * + * Rss version. + */ +#define RSS_VERSION (0.2.3) + +/** + * RSS_VERSION_S: + * + * Rss version, encoded as a string, useful for printing and + * concatenation. + */ +#define RSS_VERSION_S "0.2.3" + +/** + * RSS_VERSION_HEX: + * + * Rss version, encoded as an hexadecimal number, useful for + * integer comparisons. + */ +#define RSS_VERSION_HEX (RSS_MAJOR_VERSION << 24 | \ + RSS_MINOR_VERSION << 16 | \ + RSS_MICRO_VERSION << 8) + +/** + * RSS_CHECK_VERSION: + * @major: required major version + * @minor: required minor version + * @micro: required micro version + * + * Compile-time version checking. Evaluates to %TRUE if the version + * of Rss is greater than the required one. + */ +#define RSS_CHECK_VERSION(major,minor,micro) \ + (RSS_MAJOR_VERSION > (major) || \ + (RSS_MAJOR_VERSION == (major) && RSS_MINOR_VERSION > (minor)) || \ + (RSS_MAJOR_VERSION == (major) && RSS_MINOR_VERSION == (minor) && \ + RSS_MICRO_VERSION >= (micro))) + +#endif /* __RSS_VERSION_H__ */ diff --git a/plugins/backend/meson.build b/plugins/backend/meson.build index ff8f0f04a..03385a775 100644 --- a/plugins/backend/meson.build +++ b/plugins/backend/meson.build @@ -1,5 +1,6 @@ supported_backend_plugins = [ 'bazqux', + 'decsync', 'feedbin', 'feedhq', 'feedly',