Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/jangernert/FeedReader.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAldo Gunsing <dev@aldogunsing.nl>2018-11-13 18:12:54 +0300
committerAldo Gunsing <dev@aldogunsing.nl>2018-11-29 20:33:57 +0300
commit420fdcdacd7079f897c9c3176352acbf3f00f47d (patch)
tree6a3eb9e93b2418abeeb395a9ce39868a2112a0fa
parent4eb29727025e40f7afeecda7ab1c24f8e1d5884d (diff)
Add DecSync plugin
-rw-r--r--.gitmodules3
-rw-r--r--meson_options.txt4
-rw-r--r--plugins/backend/decsync/Rfc822.vala144
-rw-r--r--plugins/backend/decsync/decsync.gresource.xml7
-rw-r--r--plugins/backend/decsync/decsync.plugin9
-rw-r--r--plugins/backend/decsync/decsyncInterface.vala738
-rw-r--r--plugins/backend/decsync/decsyncListeners.vala285
-rw-r--r--plugins/backend/decsync/decsyncUtils.vala120
-rw-r--r--plugins/backend/decsync/icons/64x64/places/feed-service-decsync-symbolic.svg132
-rw-r--r--plugins/backend/decsync/icons/64x64/places/feed-service-decsync.svg145
m---------plugins/backend/decsync/libdecsync0
-rw-r--r--plugins/backend/decsync/libmrss/meson.build16
-rw-r--r--plugins/backend/decsync/libmrss/mrss.h897
-rw-r--r--plugins/backend/decsync/libmrss/mrss_download.c51
-rw-r--r--plugins/backend/decsync/libmrss/mrss_edit.c1739
-rw-r--r--plugins/backend/decsync/libmrss/mrss_free.c464
-rw-r--r--plugins/backend/decsync/libmrss/mrss_generic.c146
-rw-r--r--plugins/backend/decsync/libmrss/mrss_internal.h35
-rw-r--r--plugins/backend/decsync/libmrss/mrss_options.c76
-rw-r--r--plugins/backend/decsync/libmrss/mrss_parser.c1376
-rw-r--r--plugins/backend/decsync/libmrss/mrss_search.c122
-rw-r--r--plugins/backend/decsync/libmrss/mrss_write.c1228
-rw-r--r--plugins/backend/decsync/libnxml/meson.build19
-rw-r--r--plugins/backend/decsync/libnxml/nxml.h947
-rw-r--r--plugins/backend/decsync/libnxml/nxml_download.c139
-rw-r--r--plugins/backend/decsync/libnxml/nxml_easy.c392
-rw-r--r--plugins/backend/decsync/libnxml/nxml_edit.c184
-rw-r--r--plugins/backend/decsync/libnxml/nxml_error.c63
-rw-r--r--plugins/backend/decsync/libnxml/nxml_free.c233
-rw-r--r--plugins/backend/decsync/libnxml/nxml_init.c400
-rw-r--r--plugins/backend/decsync/libnxml/nxml_internal.h84
-rw-r--r--plugins/backend/decsync/libnxml/nxml_namespace.c354
-rw-r--r--plugins/backend/decsync/libnxml/nxml_parser.c1482
-rw-r--r--plugins/backend/decsync/libnxml/nxml_string.c116
-rw-r--r--plugins/backend/decsync/libnxml/nxml_tools.c122
-rw-r--r--plugins/backend/decsync/libnxml/nxml_utf.c515
-rw-r--r--plugins/backend/decsync/libnxml/nxml_write.c410
-rw-r--r--plugins/backend/decsync/meson.build68
-rw-r--r--plugins/backend/decsync/org.gnome.feedreader.decsync.gschema.xml11
-rw-r--r--plugins/backend/decsync/rss-glib/meson.build12
-rw-r--r--plugins/backend/decsync/rss-glib/rss-document-private.h58
-rw-r--r--plugins/backend/decsync/rss-glib/rss-document.c1085
-rw-r--r--plugins/backend/decsync/rss-glib/rss-document.h105
-rw-r--r--plugins/backend/decsync/rss-glib/rss-glib-1.0.vapi126
-rw-r--r--plugins/backend/decsync/rss-glib/rss-glib.h9
-rw-r--r--plugins/backend/decsync/rss-glib/rss-item-private.h48
-rw-r--r--plugins/backend/decsync/rss-glib/rss-item.c819
-rw-r--r--plugins/backend/decsync/rss-glib/rss-item.h97
-rw-r--r--plugins/backend/decsync/rss-glib/rss-marshal.c54
-rw-r--r--plugins/backend/decsync/rss-glib/rss-marshal.h15
-rw-r--r--plugins/backend/decsync/rss-glib/rss-parser-private.h45
-rw-r--r--plugins/backend/decsync/rss-glib/rss-parser.c338
-rw-r--r--plugins/backend/decsync/rss-glib/rss-parser.h102
-rw-r--r--plugins/backend/decsync/rss-glib/rss-version.h96
-rw-r--r--plugins/backend/meson.build1
55 files changed, 16284 insertions, 2 deletions
diff --git a/.gitmodules b/.gitmodules
index f6797cdd..c4366c27 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 d8d89320..773e9f66 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 00000000..368c6987
--- /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*)?
+ (?<day>\d{1,2})\s+
+ (?<month>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
+ (?<year>\d{2,4})\s+
+ (?<hour>\d{2})
+ :(?<minute>\d{2})
+ (?::(?<second>\d{2}))?\s+
+ (?<zone>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 00000000..a9f96b29
--- /dev/null
+++ b/plugins/backend/decsync/decsync.gresource.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+ <gresource prefix="/org/gnome/FeedReader">
+ <file preprocess="xml-stripblanks">icons/64x64/places/feed-service-decsync.svg</file>
+ <file preprocess="xml-stripblanks">icons/64x64/places/feed-service-decsync-symbolic.svg</file>
+ </gresource>
+</gresources>
diff --git a/plugins/backend/decsync/decsync.plugin b/plugins/backend/decsync/decsync.plugin
new file mode 100644
index 00000000..d6aeda44
--- /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 <jangernert@gmail.com>, Aldo Gunsing <dev@aldogunsing.nl>
+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 00000000..84599d16
--- /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 <http://www.gnu.org/licenses/>.
+
+public class FeedReader.decsyncInterface : Peas.ExtensionBase, FeedServerInterface {
+
+ internal DecsyncUtils m_utils;
+ private Soup.Session m_session;
+ internal Decsync<Unit> 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<OnEntryUpdateListener>();
+ 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<Unit>(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<void*>(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<Decsync.EntryWithPath>();
+ 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<string>();
+ 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<Category>();
+ 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<Feed>();
+ 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<Feed> 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<Feed> feeds, Gee.List<Category> categories, Gee.List<Tag> 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<Article>();
+ 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<Feed>.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<Article>();
+ 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<Enclosure>();
+
+ 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, Gee.List<string>, Article>(
+ articles,
+ article => { return articleToBasePath(article); }
+ );
+ multiMap.get_keys().@foreach(basePath => {
+ var articleIDs = multiMap.@get(basePath).map<Json.Node>(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<string> basePath, string type)
+ {
+ var path = new Gee.ArrayList<string>();
+ path.add("articles");
+ path.add(type);
+ path.add_all(basePath);
+ return path.to_array();
+ }
+
+ public Gee.List<string> 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 00000000..386ae361
--- /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 <http://www.gnu.org/licenses/>.
+
+public class FeedReader.DecsyncListeners : GLib.Object {
+
+ public class ReadMarkListener : OnSubdirEntryUpdateListener<Unit> {
+
+ private Gee.List<string> 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<string> subdir()
+ {
+ return m_subdir;
+ }
+
+ public override void onSubdirEntryUpdate(Gee.List<string> 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<Unit> {
+
+ private Gee.List<string> m_subfile;
+ private decsyncInterface m_plugin;
+
+ public SubscriptionsListener(decsyncInterface plugin)
+ {
+ this.m_subfile = toList({"feeds", "subscriptions"});
+ this.m_plugin = plugin;
+ }
+
+ public override Gee.List<string> 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<Unit> {
+
+ private Gee.List<string> m_subfile;
+ private decsyncInterface m_plugin;
+
+ public FeedNamesListener(decsyncInterface plugin)
+ {
+ this.m_subfile = toList({"feeds", "names"});
+ this.m_plugin = plugin;
+ }
+
+ public override Gee.List<string> 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<Unit> {
+
+ private Gee.List<string> m_subfile;
+ private decsyncInterface m_plugin;
+
+ public CategoriesListener(decsyncInterface plugin)
+ {
+ this.m_subfile = toList({"feeds", "categories"});
+ this.m_plugin = plugin;
+ }
+
+ public override Gee.List<string> 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<Unit> {
+
+ private Gee.List<string> m_subfile;
+ private decsyncInterface m_plugin;
+
+ public CategoryNamesListener(decsyncInterface plugin)
+ {
+ this.m_subfile = toList({"categories", "names"});
+ this.m_plugin = plugin;
+ }
+
+ public override Gee.List<string> 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<Unit> {
+
+ private Gee.List<string> m_subfile;
+ private decsyncInterface m_plugin;
+
+ public CategoryParentsListener(decsyncInterface plugin)
+ {
+ this.m_subfile = toList({"categories", "parents"});
+ this.m_plugin = plugin;
+ }
+
+ public override Gee.List<string> 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<Category>();
+ 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 00000000..f71c3241
--- /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 <http://www.gnu.org/licenses/>.
+
+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<string> 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 00000000..5172ae9f
--- /dev/null
+++ b/plugins/backend/decsync/icons/64x64/places/feed-service-decsync-symbolic.svg
@@ -0,0 +1,132 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="360"
+ height="360"
+ id="svg3812"
+ version="1.1"
+ inkscape:version="0.92.3 (2405546, 2018-03-11)"
+ sodipodi:docname="launcher.svg"
+ inkscape:export-filename="/home/moa/FeedEx/res/drawable-xhdpi/ic_statusbar_rss.png"
+ inkscape:export-xdpi="24.854368"
+ inkscape:export-ydpi="24.854368"
+ viewBox="0 0 360 360">
+ <defs
+ id="defs3814" />
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.4"
+ inkscape:cx="-6.0089286"
+ inkscape:cy="145.78032"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0"
+ inkscape:window-width="1920"
+ inkscape:window-height="1053"
+ inkscape:window-x="0"
+ inkscape:window-y="27"
+ inkscape:window-maximized="1"
+ inkscape:pagecheckerboard="true"
+ scale-x="1" />
+ <metadata
+ id="metadata3817">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-435.96875,-162.125)">
+ <g
+ id="g585073"
+ transform="matrix(1.2542117,0,0,1.301646,-24.260219,-233.53273)">
+ <path
+ inkscape:export-ydpi="137.33"
+ inkscape:export-xdpi="137.33"
+ inkscape:connector-curvature="0"
+ id="path4099"
+ d="m 460.70793,371.64153 v 25.83801 c 55.87559,0 100.89194,45.52497 100.89194,102.03186 h 25.54934 c 0,-70.46928 -56.75927,-127.86987 -126.44128,-127.86987 z"
+ style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:Sans;-inkscape-font-specification:Sans;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;display:inline;overflow:visible;visibility:visible;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2.5;marker:none;enable-background:accumulate" />
+ <path
+ inkscape:export-ydpi="137.33"
+ inkscape:export-xdpi="137.33"
+ inkscape:connector-curvature="0"
+ id="path4099-0"
+ d="m 460.70793,411.56096 v 25.62481 c 34.75712,0 62.66468,27.74517 62.66468,62.32563 h 25.75572 c 0,-48.42932 -39.74367,-87.95044 -88.4204,-87.95044 z"
+ style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:Sans;-inkscape-font-specification:Sans;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;display:inline;overflow:visible;visibility:visible;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2.5;marker:none;enable-background:accumulate" />
+ <ellipse
+ inkscape:export-ydpi="137.33"
+ inkscape:export-xdpi="137.33"
+ transform="matrix(0.97220719,0,0,0.99957802,168.35134,-121.16022)"
+ id="path4901-1-7"
+ style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none"
+ cx="322.5"
+ cy="599.50507"
+ rx="21.785715"
+ ry="21.428572" />
+ </g>
+ <g
+ style="display:inline"
+ id="g92885"
+ transform="matrix(0.98106563,0,0,0.98106563,637.52009,265.59075)">
+ <circle
+ r="153.92543"
+ cy="79.285713"
+ cx="-22.300594"
+ id="path9423"
+ style="opacity:0;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2.83999991;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill" />
+ <g
+ id="g9467">
+ <path
+ inkscape:connector-curvature="0"
+ id="circle9427"
+ d="m 130.20508,77.865234 v 1.419922 c 0,69.031684 -46.35567,129.441884 -113.035158,147.308594 C -49.509566,244.46046 -119.85916,215.32226 -154.375,155.53906 l -0.70898,-1.23047 -2.45899,1.41993 0.70899,1.23046 c 35.15168,60.8845 106.832396,90.57476 174.74023,72.37891 67.907834,-18.19585 115.13867,-79.74937 115.13867,-150.052734 v -1.419922 z"
+ style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:20.38599586;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path9471"
+ d="m 131.62484,66.176274 11.36,19.652799 h -22.72 z"
+ style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:20.38599586;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ <g
+ transform="rotate(-180,-22.115669,79.492158)"
+ id="g9477">
+ <path
+ style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:20.38599586;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+ d="m 130.20508,77.865234 v 1.419922 c 0,69.031684 -46.35567,129.441884 -113.035158,147.308594 C -49.509566,244.46046 -119.85916,215.32226 -154.375,155.53906 l -0.70898,-1.23047 -2.45899,1.41993 0.70899,1.23046 c 35.15168,60.8845 106.832396,90.57476 174.74023,72.37891 67.907834,-18.19585 115.13867,-79.74937 115.13867,-150.052734 v -1.419922 z"
+ id="path9473"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:20.38599586;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 131.62484,66.176274 11.36,19.652799 h -22.72 z"
+ id="path9475"
+ inkscape:connector-curvature="0" />
+ </g>
+ </g>
+ </g>
+</svg>
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 00000000..55d8af49
--- /dev/null
+++ b/plugins/backend/decsync/icons/64x64/places/feed-service-decsync.svg
@@ -0,0 +1,145 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="360"
+ height="360"
+ id="svg3812"
+ version="1.1"
+ inkscape:version="0.92.3 (2405546, 2018-03-11)"
+ sodipodi:docname="launcher-with-background.svg"
+ inkscape:export-filename="/home/moa/FeedEx/res/drawable-xhdpi/ic_statusbar_rss.png"
+ inkscape:export-xdpi="24.854368"
+ inkscape:export-ydpi="24.854368"
+ viewBox="0 0 360 360">
+ <defs
+ id="defs3814" />
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.4"
+ inkscape:cx="37.142858"
+ inkscape:cy="140.06604"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0"
+ inkscape:window-width="1920"
+ inkscape:window-height="1053"
+ inkscape:window-x="0"
+ inkscape:window-y="27"
+ inkscape:window-maximized="1"
+ inkscape:pagecheckerboard="true"
+ scale-x="1"
+ inkscape:object-nodes="true" />
+ <metadata
+ id="metadata3817">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-435.96875,-162.125)">
+ <a
+ id="a83619">
+ <rect
+ ry="45.000008"
+ rx="45"
+ y="162.125"
+ x="435.96875"
+ height="360"
+ width="360"
+ id="rect84423"
+ style="opacity:1;fill:#5c6bc0;fill-opacity:1;fill-rule:nonzero;stroke:#ff0000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:normal" />
+ </a>
+ <g
+ id="g585073"
+ transform="matrix(1.2542117,0,0,1.301646,-24.260219,-233.53273)">
+ <path
+ inkscape:export-ydpi="137.33"
+ inkscape:export-xdpi="137.33"
+ inkscape:connector-curvature="0"
+ id="path4099"
+ d="m 460.70793,371.64153 v 25.83801 c 55.87559,0 100.89194,45.52497 100.89194,102.03186 h 25.54934 c 0,-70.46928 -56.75927,-127.86987 -126.44128,-127.86987 z"
+ style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:Sans;-inkscape-font-specification:Sans;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;display:inline;overflow:visible;visibility:visible;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2.5;marker:none;enable-background:accumulate" />
+ <path
+ inkscape:export-ydpi="137.33"
+ inkscape:export-xdpi="137.33"
+ inkscape:connector-curvature="0"
+ id="path4099-0"
+ d="m 460.70793,411.56096 v 25.62481 c 34.75712,0 62.66468,27.74517 62.66468,62.32563 h 25.75572 c 0,-48.42932 -39.74367,-87.95044 -88.4204,-87.95044 z"
+ style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:Sans;-inkscape-font-specification:Sans;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;baseline-shift:baseline;text-anchor:start;display:inline;overflow:visible;visibility:visible;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2.5;marker:none;enable-background:accumulate" />
+ <ellipse
+ inkscape:export-ydpi="137.33"
+ inkscape:export-xdpi="137.33"
+ transform="matrix(0.97220719,0,0,0.99957802,168.35134,-121.16022)"
+ id="path4901-1-7"
+ style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none"
+ cx="322.5"
+ cy="599.50507"
+ rx="21.785715"
+ ry="21.428572" />
+ </g>
+ <g
+ style="display:inline"
+ id="g92885"
+ transform="matrix(0.98106563,0,0,0.98106563,637.52009,265.59075)">
+ <circle
+ r="153.92543"
+ cy="79.285713"
+ cx="-22.300594"
+ id="path9423"
+ style="opacity:0;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2.83999991;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill" />
+ <g
+ id="g9467">
+ <path
+ inkscape:connector-curvature="0"
+ id="circle9427"
+ d="m 130.20508,77.865234 v 1.419922 c 0,69.031684 -46.35567,129.441884 -113.035158,147.308594 C -49.509566,244.46046 -119.85916,215.32226 -154.375,155.53906 l -0.70898,-1.23047 -2.45899,1.41993 0.70899,1.23046 c 35.15168,60.8845 106.832396,90.57476 174.74023,72.37891 67.907834,-18.19585 115.13867,-79.74937 115.13867,-150.052734 v -1.419922 z"
+ style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:20.38599586;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path9471"
+ d="m 131.62484,66.176274 11.36,19.652799 h -22.72 z"
+ style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:20.38599586;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ </g>
+ <g
+ transform="rotate(-180,-22.115669,79.492158)"
+ id="g9477">
+ <path
+ style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:20.38599586;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers stroke fill;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+ d="m 130.20508,77.865234 v 1.419922 c 0,69.031684 -46.35567,129.441884 -113.035158,147.308594 C -49.509566,244.46046 -119.85916,215.32226 -154.375,155.53906 l -0.70898,-1.23047 -2.45899,1.41993 0.70899,1.23046 c 35.15168,60.8845 106.832396,90.57476 174.74023,72.37891 67.907834,-18.19585 115.13867,-79.74937 115.13867,-150.052734 v -1.419922 z"
+ id="path9473"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:20.38599586;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 131.62484,66.176274 11.36,19.652799 h -22.72 z"
+ id="path9475"
+ inkscape:connector-curvature="0" />
+ </g>
+ </g>
+ </g>
+</svg>
diff --git a/plugins/backend/decsync/libdecsync b/plugins/backend/decsync/libdecsync
new file mode 160000
+Subproject 30681106cf38c4017e428630ee365e1a15eed31
diff --git a/plugins/backend/decsync/libmrss/meson.build b/plugins/backend/decsync/libmrss/meson.build
new file mode 100644
index 00000000..595dab82
--- /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 00000000..d1055b5c
--- /dev/null
+++ b/plugins/backend/decsync/libmrss/mrss.h
@@ -0,0 +1,897 @@
+/* mRss - Copyright (C) 2005-2007 bakunin - Andrea Marchesini
+ * <bakunin@autistici.org>
+ *
+ * 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 <sys/types.h>
+#include <curl/curl.h>
+
+#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 00000000..c28980aa
--- /dev/null
+++ b/plugins/backend/decsync/libmrss/mrss_download.c
@@ -0,0 +1,51 @@
+/* mRss - Copyright (C) 2005-2007 bakunin - Andrea Marchesini
+ * <bakunin@autistici.org>
+ *
+ * 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 00000000..a21a559d
--- /dev/null
+++ b/plugins/backend/decsync/libmrss/mrss_edit.c
@@ -0,0 +1,1739 @@
+/* mRss - Copyright (C) 2005-2007 bakunin - Andrea Marchesini
+ * <bakunin@autistici.org>
+ *
+ * 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 00000000..e11c4c9b
--- /dev/null
+++ b/plugins/backend/decsync/libmrss/mrss_free.c
@@ -0,0 +1,464 @@
+/* mRss - Copyright (C) 2005-2007 bakunin - Andrea Marchesini
+ * <bakunin@autistici.org>
+ *
+ * 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 00000000..ec4e458d
--- /dev/null
+++ b/plugins/backend/decsync/libmrss/mrss_generic.c
@@ -0,0 +1,146 @@
+/* mRss - Copyright (C) 2005-2007 bakunin - Andrea Marchesini
+ * <bakunin@autistici.org>
+ *
+ * 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 00000000..0b496fdf
--- /dev/null
+++ b/plugins/backend/decsync/libmrss/mrss_internal.h
@@ -0,0 +1,35 @@
+/* mRss - Copyright (C) 2005-2007 bakunin - Andrea Marchesini
+ * <bakunin@autistici.org>
+ *
+ * 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 <curl/curl.h>
+#include <nxml.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <errno.h>
+#include <locale.h>
+
+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 00000000..bd02c343
--- /dev/null
+++ b/plugins/backend/decsync/libmrss/mrss_options.c
@@ -0,0 +1,76 @@
+/* mRss - Copyright (C) 2005-2007 bakunin - Andrea Marchesini
+ * <bakunin@autistici.org>
+ *
+ * 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 00000000..200cd8dd
--- /dev/null
+++ b/plugins/backend/decsync/libmrss/mrss_parser.c
@@ -0,0 +1,1376 @@
+/* mRss - Copyright (C) 2005-2007 bakunin - Andrea Marchesini
+ * <bakunin@autistici.org>
+ *
+ * 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, "<?xml ", 6))
+ {
+ free (buffer);
+ continue;
+ }
+
+ p = buffer;
+
+ while (*p && *p != '>')
+ 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 00000000..8d82c2c3
--- /dev/null
+++ b/plugins/backend/decsync/libmrss/mrss_search.c
@@ -0,0 +1,122 @@
+/* mRss - Copyright (C) 2005-2007 bakunin - Andrea Marchesini
+ * <bakunin@autistici.org>
+ *
+ * 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 00000000..09d648d3
--- /dev/null
+++ b/plugins/backend/decsync/libmrss/mrss_write.c
@@ -0,0 +1,1228 @@
+/* mRss - Copyright (C) 2005-2007 bakunin - Andrea Marchesini
+ * <bakunin@autistici.org>
+ *
+ * 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<image>\n",
+ mrss->version == MRSS_VERSION_1_0 ? "" : " ");
+
+ if (mrss->image_title)
+ {
+ func (obj, " %s<title>",
+ mrss->version == MRSS_VERSION_1_0 ? "" : " ");
+ __mrss_write_string (func, obj, mrss->image_title);
+ func (obj, "</title>\n");
+ }
+
+ if (mrss->image_url)
+ {
+ func (obj, " %s<url>",
+ mrss->version == MRSS_VERSION_1_0 ? "" : " ");
+ __mrss_write_string (func, obj, mrss->image_url);
+ func (obj, "</url>\n");
+ }
+
+ if (mrss->image_link)
+ {
+ func (obj, " %s<link>",
+ mrss->version == MRSS_VERSION_1_0 ? "" : " ");
+ __mrss_write_string (func, obj, mrss->image_link);
+ func (obj, "</link>\n");
+ }
+
+ if (mrss->version != MRSS_VERSION_1_0)
+ {
+ if (mrss->image_width)
+ func (obj, " <width>%d</width>\n", mrss->image_width);
+
+ if (mrss->image_height)
+ func (obj, " <height>%d</height>\n", mrss->image_height);
+
+ if (mrss->image_description)
+ {
+ func (obj, " <description>");
+ __mrss_write_string (func, obj, mrss->image_description);
+ func (obj, "</description>\n");
+ }
+ }
+
+ func (obj, " %s</image>\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<textinput>\n",
+ mrss->version == MRSS_VERSION_1_0 ? "" : " ");
+
+ if (mrss->textinput_title)
+ {
+ func (obj, " %s<title>",
+ mrss->version == MRSS_VERSION_1_0 ? "" : " ");
+ __mrss_write_string (func, obj, mrss->textinput_title);
+ func (obj, "</title>\n");
+ }
+
+ if (mrss->textinput_description)
+ {
+ func (obj, " %s<description>",
+ mrss->version == MRSS_VERSION_1_0 ? "" : " ");
+ __mrss_write_string (func, obj, mrss->textinput_description);
+ func (obj, "</description>\n");
+ }
+
+ if (mrss->textinput_name)
+ {
+ func (obj, " %s<name>",
+ mrss->version == MRSS_VERSION_1_0 ? "" : " ");
+ __mrss_write_string (func, obj, mrss->textinput_name);
+ func (obj, "</name>\n");
+ }
+
+ if (mrss->textinput_link)
+ {
+ func (obj, " %s<link>",
+ mrss->version == MRSS_VERSION_1_0 ? "" : " ");
+ __mrss_write_string (func, obj, mrss->textinput_link);
+ func (obj, "</link>\n");
+ }
+
+ func (obj, " </textinput>\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");
+
+ if (mrss->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</cloud>\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, " <skipHours>\n");
+
+ hour = mrss->skipHours;
+ while (hour)
+ {
+ func (obj, " <hour>");
+ __mrss_write_string (func, obj, hour->hour);
+ func (obj, "</hour>\n");
+
+ hour = hour->next;
+ }
+
+ func (obj, " </skipHours>\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, " <skipDays>\n");
+
+ day = mrss->skipDays;
+ while (day)
+ {
+ func (obj, " <day>");
+ __mrss_write_string (func, obj, day->day);
+ func (obj, "</day>\n");
+ day = day->next;
+ }
+
+ func (obj, " </skipDays>\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, " <category");
+
+ if (category->domain)
+ {
+ func (obj, " domain=\"");
+ __mrss_write_string (func, obj, category->domain);
+ func (obj, "\"");
+ }
+
+ func (obj, ">");
+ __mrss_write_string (func, obj, category->category);
+ func (obj, "</category>\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, " <ns0:%s", tag->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, "</ns0:%s>\n", tag->name);
+ else
+ func (obj, "</%s>\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<item>\n",
+ mrss->version == MRSS_VERSION_1_0 ? "" : " ");
+
+ if (item->title)
+ {
+ func (obj, " %s<title>",
+ mrss->version == MRSS_VERSION_1_0 ? "" : " ");
+ __mrss_write_string (func, obj, item->title);
+ func (obj, "</title>\n");
+ }
+
+ if (item->link)
+ {
+ func (obj, " %s<link>",
+ mrss->version == MRSS_VERSION_1_0 ? "" : " ");
+ __mrss_write_string (func, obj, item->link);
+ func (obj, "</link>\n");
+ }
+
+ if (item->description)
+ {
+ func (obj, " %s<description>",
+ mrss->version == MRSS_VERSION_1_0 ? "" : " ");
+ __mrss_write_string (func, obj, item->description);
+ func (obj, "</description>\n");
+ }
+
+ if (mrss->version == MRSS_VERSION_2_0)
+ {
+ if (item->author)
+ {
+ func (obj, " <author>");
+ __mrss_write_string (func, obj, item->author);
+ func (obj, "</author>\n");
+ }
+
+ if (item->comments)
+ {
+ func (obj, " <comments>");
+ __mrss_write_string (func, obj, item->comments);
+ func (obj, "</comments>\n");
+ }
+
+ if (item->pubDate)
+ {
+ func (obj, " <pubDate>");
+ __mrss_write_string (func, obj, item->pubDate);
+ func (obj, "</pubDate>\n");
+ }
+
+ if (item->guid)
+ {
+ func (obj, " <guid isPermaLink=\"%s\">",
+ item->guid_isPermaLink ? "true" : "false");
+ __mrss_write_string (func, obj, item->guid);
+ func (obj, "</guid>\n");
+ }
+
+ }
+
+ if (mrss->version == MRSS_VERSION_2_0
+ || mrss->version == MRSS_VERSION_0_92)
+ {
+ if (item->source || item->source_url)
+ {
+ func (obj, " <source");
+
+ if (item->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, "</source>\n");
+ }
+ else
+ func (obj, " />\n");
+ }
+ }
+
+ if (item->enclosure || item->enclosure_length || item->enclosure_type)
+ {
+ func (obj, " <enclosure");
+
+ if (item->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, "</enclosure>\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</item>\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, " <category");
+
+ if (category->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, " <entry>\n");
+
+ if (item->title)
+ {
+ func (obj, " <title");
+
+ if (item->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, "</title>\n");
+ }
+
+ if (item->link)
+ {
+ func (obj, " <link>");
+ __mrss_write_string (func, obj, item->link);
+ func (obj, "</link>\n");
+ }
+
+ if (item->description)
+ {
+ func (obj, " <summary");
+
+ if (item->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, "</summary>\n");
+ }
+
+ if (item->copyright)
+ {
+ func (obj, " <rights>");
+ __mrss_write_string (func, obj, item->copyright);
+ func (obj, "</rights>\n");
+ }
+
+ if (item->author)
+ {
+ func (obj, " <author>\n");
+ func (obj, " <name>");
+ __mrss_write_string (func, obj, item->author);
+ func (obj, "</name>\n");
+
+ if (item->author_email)
+ {
+ func (obj, " <email>");
+ __mrss_write_string (func, obj, item->author_email);
+ func (obj, "</email>\n");
+ }
+
+ if (item->author_uri)
+ {
+ func (obj, " <uri>");
+ __mrss_write_string (func, obj, item->author_uri);
+ func (obj, "</uri>\n");
+ }
+
+ func (obj, " </author>\n");
+ }
+
+ if (item->contributor)
+ {
+ func (obj, " <contributor>\n");
+ func (obj, " <name>");
+ __mrss_write_string (func, obj, item->contributor);
+ func (obj, "</name>\n");
+
+ if (item->contributor_email)
+ {
+ func (obj, " <email>");
+ __mrss_write_string (func, obj, item->contributor_email);
+ func (obj, "</email>\n");
+ }
+
+ if (item->contributor_uri)
+ {
+ func (obj, " <uri>");
+ __mrss_write_string (func, obj, item->contributor_uri);
+ func (obj, "</uri>\n");
+ }
+
+ func (obj, " </contributor>\n");
+ }
+
+ /* TODO */
+ if (mrss->pubDate)
+ {
+ if (mrss->version == MRSS_VERSION_ATOM_1_0)
+ {
+ func (obj, " <published>");
+ __mrss_write_string (func, obj, item->pubDate);
+ func (obj, "</published>\n");
+ }
+ else
+ {
+ func (obj, " <issued>");
+ __mrss_write_string (func, obj, item->pubDate);
+ func (obj, "</issued>\n");
+ }
+ }
+
+ if (item->guid)
+ {
+ func (obj, " <id>");
+ __mrss_write_string (func, obj, item->guid);
+ func (obj, "</id>\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, " </entry>\n");
+
+ item = item->next;
+ }
+}
+
+static mrss_error_t
+__mrss_write_atom (mrss_t * mrss, void (*func) (void *, char *, ...),
+ void *obj)
+{
+ func (obj, "<feed xmlns=\"http://www.w3.org/2005/Atom\"");
+
+ if (mrss->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");
+
+ if (mrss->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, "</title>\n");
+
+ if (mrss->description)
+ {
+ if (mrss->version == MRSS_VERSION_ATOM_1_0)
+ {
+ func (obj, " <subtitle");
+
+ if (mrss->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, "</subtitle>\n");
+ }
+ else
+ {
+ func (obj, " <tagline");
+
+ if (mrss->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, "</tagline>\n");
+ }
+ }
+
+ if (mrss->link)
+ {
+ func (obj, " <link href=\"");
+ __mrss_write_string (func, obj, mrss->link);
+ func (obj, "\"/>\n");
+ }
+
+ if (mrss->id)
+ {
+ func (obj, " <id>");
+ __mrss_write_string (func, obj, mrss->id);
+ func (obj, "</id>\n");
+ }
+
+ if (mrss->copyright)
+ {
+ func (obj, " <rights");
+
+ if (mrss->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, "</rights>\n");
+ }
+
+ /* TODO */
+ if (mrss->lastBuildDate)
+ {
+ func (obj, " <updated>");
+ __mrss_write_string (func, obj, mrss->lastBuildDate);
+ func (obj, "</updated>\n");
+ }
+
+ if (mrss->managingeditor)
+ {
+ func (obj, " <author>\n");
+ func (obj, " <name>");
+ __mrss_write_string (func, obj, mrss->managingeditor);
+ func (obj, "</name>\n");
+
+ if (mrss->managingeditor_email)
+ {
+ func (obj, " <email>");
+ __mrss_write_string (func, obj, mrss->managingeditor_email);
+ func (obj, "</email>\n");
+ }
+
+ if (mrss->managingeditor_uri)
+ {
+ func (obj, " <uri>");
+ __mrss_write_string (func, obj, mrss->managingeditor_uri);
+ func (obj, "</uri>\n");
+ }
+
+ func (obj, " </author>\n");
+ }
+
+ if (mrss->generator)
+ {
+ func (obj, " <generator");
+
+ if (mrss->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, "</generator>\n");
+ }
+
+ if (mrss->image_url)
+ {
+ func (obj, " <icon>");
+ __mrss_write_string (func, obj, mrss->image_url);
+ func (obj, "</icon>\n");
+ }
+
+ if (mrss->image_logo)
+ {
+ func (obj, " <logo>");
+ __mrss_write_string (func, obj, mrss->image_logo);
+ func (obj, "</logo>\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, "</feed>\n");
+
+ return MRSS_OK;
+}
+
+static mrss_error_t
+__mrss_write_real (mrss_t * mrss, void (*func) (void *, char *, ...),
+ void *obj)
+{
+ func (obj, "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
+
+ if (mrss->version == MRSS_VERSION_1_0)
+ func (obj,
+ "<rdf:RDF\n"
+ " xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\"\n"
+ " xmlns=\"http://purl.org/rss/1.0/\">\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, "<rss version=\"");
+
+ switch (mrss->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, " <channel rdf:about=\"");
+ __mrss_write_string (func, obj, mrss->about);
+ func (obj, "\">\n");
+ }
+
+ else
+ func (obj, " <channel>\n");
+
+ if (mrss->title)
+ {
+ func (obj, " <title>");
+ __mrss_write_string (func, obj, mrss->title);
+ func (obj, "</title>\n");
+ }
+
+ if (mrss->description)
+ {
+ func (obj, " <description>");
+ __mrss_write_string (func, obj, mrss->description);
+ func (obj, "</description>\n");
+ }
+
+ if (mrss->link)
+ {
+ func (obj, " <link>");
+ __mrss_write_string (func, obj, mrss->link);
+ func (obj, "</link>\n");
+ }
+
+ if (mrss->language && mrss->version != MRSS_VERSION_1_0)
+ {
+ func (obj, " <language>");
+ __mrss_write_string (func, obj, mrss->language);
+ func (obj, "</language>\n");
+ }
+
+ if (mrss->rating && mrss->version != MRSS_VERSION_1_0)
+ {
+ func (obj, " <rating>");
+ __mrss_write_string (func, obj, mrss->rating);
+ func (obj, "</rating>\n");
+ }
+
+ if (mrss->copyright && mrss->version != MRSS_VERSION_1_0)
+ {
+ func (obj, " <copyright>");
+ __mrss_write_string (func, obj, mrss->copyright);
+ func (obj, "</copyright>\n");
+ }
+
+ if (mrss->pubDate && mrss->version != MRSS_VERSION_1_0)
+ {
+ func (obj, " <pubDate>");
+ __mrss_write_string (func, obj, mrss->pubDate);
+ func (obj, "</pubDate>\n");
+ }
+
+ if (mrss->lastBuildDate && mrss->version != MRSS_VERSION_1_0)
+ {
+ func (obj, " <lastBuildDate>");
+ __mrss_write_string (func, obj, mrss->lastBuildDate);
+ func (obj, "</lastBuildDate>\n");
+ }
+
+ if (mrss->docs && mrss->version != MRSS_VERSION_1_0)
+ {
+ func (obj, " <docs>");
+ __mrss_write_string (func, obj, mrss->docs);
+ func (obj, "</docs>\n");
+ }
+
+ if (mrss->managingeditor && mrss->version != MRSS_VERSION_1_0)
+ {
+ func (obj, " <managingeditor>");
+ __mrss_write_string (func, obj, mrss->managingeditor);
+ func (obj, "</managingeditor>\n");
+ }
+
+ if (mrss->webMaster && mrss->version != MRSS_VERSION_1_0)
+ {
+ func (obj, " <webMaster>");
+ __mrss_write_string (func, obj, mrss->webMaster);
+ func (obj, "</webMaster>\n");
+ }
+
+ if (mrss->version == MRSS_VERSION_2_0)
+ {
+ if (mrss->generator)
+ {
+ func (obj, " <generator>");
+ __mrss_write_string (func, obj, mrss->generator);
+ func (obj, "</generator>\n");
+ }
+
+ if (mrss->ttl)
+ func (obj, " <ttl>%d</ttl>\n", mrss->ttl);
+ }
+
+ if (mrss->version == MRSS_VERSION_1_0)
+ {
+ if (mrss->image_url)
+ {
+ func (obj, " <image rdf:resource=\"");
+ __mrss_write_string (func, obj, mrss->image_url);
+ func (obj, "\" />\n");
+ }
+
+ if (mrss->textinput_link)
+ {
+ func (obj, " <textinput rdf:resource=\"");
+ __mrss_write_string (func, obj, mrss->textinput_link);
+ func (obj, "\" />\n");
+ }
+
+ if (mrss->item)
+ {
+ mrss_item_t *item = mrss->item;
+
+ func (obj, " <items>\n" " <rdf:Seq>\n");
+ while (item)
+ {
+ func (obj, " <rdf:li rdf:resource=\"");
+ __mrss_write_string (func, obj, item->link);
+ func (obj, "\" />\n");
+ item = item->next;
+ }
+ func (obj, " </rdf:Seq>\n" " </items>\n");
+ }
+
+ func (obj, " </channel>\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, " </channel>\n" "</rss>\n");
+ else
+ func (obj, "</rdf:RDF>\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 00000000..1eaa8ed8
--- /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 00000000..0406038b
--- /dev/null
+++ b/plugins/backend/decsync/libnxml/nxml.h
@@ -0,0 +1,947 @@
+/* nXml - Copyright (C) 2005-2007 bakunin - Andrea Marchesini
+ * <bakunin@autistici.org>
+ *
+ * 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 <curl/curl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#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 00000000..9f079bd3
--- /dev/null
+++ b/plugins/backend/decsync/libnxml/nxml_download.c
@@ -0,0 +1,139 @@
+/* nXml - Copyright (C) 2005-2007 bakunin - Andrea Marchesini
+ * <bakunin@autistici.org>
+ *
+ * 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 00000000..00690553
--- /dev/null
+++ b/plugins/backend/decsync/libnxml/nxml_easy.c
@@ -0,0 +1,392 @@
+/* nXml - Copyright (C) 2005-2007 bakunin - Andrea Marchesini
+ * <bakunin@autistici.org>
+ *
+ * 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 00000000..597dcaa4
--- /dev/null
+++ b/plugins/backend/decsync/libnxml/nxml_edit.c
@@ -0,0 +1,184 @@
+/* nXml - Copyright (C) 2005-2007 bakunin - Andrea Marchesini
+ * <bakunin@autistici.org>
+ *
+ * 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 00000000..604fd640
--- /dev/null
+++ b/plugins/backend/decsync/libnxml/nxml_error.c
@@ -0,0 +1,63 @@
+/* nXml - Copyright (C) 2005-2007 bakunin - Andrea Marchesini
+ * <bakunin@autistici.org>
+ *
+ * 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 00000000..ae4ee080
--- /dev/null
+++ b/plugins/backend/decsync/libnxml/nxml_free.c
@@ -0,0 +1,233 @@
+/* nXml - Copyright (C) 2005-2007 bakunin - Andrea Marchesini
+ * <bakunin@autistici.org>
+ *
+ * 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 00000000..e7789fa5
--- /dev/null
+++ b/plugins/backend/decsync/libnxml/nxml_init.c
@@ -0,0 +1,400 @@
+/* nXml - Copyright (C) 2005-2007 bakunin - Andrea Marchesini
+ * <bakunin@autistici.org>
+ *
+ * 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 00000000..675b6654
--- /dev/null
+++ b/plugins/backend/decsync/libnxml/nxml_internal.h
@@ -0,0 +1,84 @@
+/* nXml - Copyright (C) 2005-2007 bakunin - Andrea Marchesini
+ * <bakunin@autistici.org>
+ *
+ * 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 00000000..2f54670a
--- /dev/null
+++ b/plugins/backend/decsync/libnxml/nxml_namespace.c
@@ -0,0 +1,354 @@
+/* nXml - Copyright (C) 2005-2007 bakunin - Andrea Marchesini
+ * <bakunin@autistici.org>
+ *
+ * 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 00000000..8379aa01
--- /dev/null
+++ b/plugins/backend/decsync/libnxml/nxml_parser.c
@@ -0,0 +1,1482 @@
+/* nXml - Copyright (C) 2005-2007 bakunin - Andrea Marchesini
+ * <bakunin@autistici.org>
+ *
+ * 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, "&lt;", 4))
+ {
+ __nxml_string_add(ret, "<", 1);
+ i += 3;
+ q = 0;
+ }
+
+ else if (!strncmp(buffer + i, "&gt;", 4))
+ {
+ __nxml_string_add(ret, ">", 1);
+ i += 3;
+ q = 0;
+ }
+
+ else if (!strncmp(buffer + i, "&amp;", 5))
+ {
+ __nxml_string_add(ret, "&", 1);
+ i += 4;
+ q = 0;
+ }
+
+ else if (!strncmp(buffer + i, "&apos;", 6))
+ {
+ __nxml_string_add(ret, "'", 1);
+ i += 5;
+ q = 0;
+ }
+
+ else if (!strncmp(buffer + i, "&quot;", 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 ::= '<![CDATA['
+ * Rule [20] - CData ::= (Char * - (Char * ']]>' 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 ::= '<!ENTITY' S Name S EntityDef S? '>'
+ * [72] PEDecl ::= '<!ENTITY' S '%' S Name S PEDef S? '>'
+ * [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, "<!ENTITY", 8))
+ {
+ 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;
+ }
+
+ *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 ::= '<!DOCTYPE' S Name (S ExternalID)? S?
+ * ('[' intSubset '] S?)? '>'
+ */
+
+ 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 ::= '<!--' ((Char - '-') | ('-' (Char - '-')))* '-->'
+ */
+
+ 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 ::= '<?' PITarget (S (Char * - (Char * '?>' 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 '<!'... */
+
+ *data = NULL;
+ *doctype = 0;
+
+ if (!*size)
+ return NXML_OK;
+
+ (*buffer) += 1;
+ (*size) -= 1;
+
+ __nxml_escape_spaces(doc, buffer, size);
+
+ if (!strncmp(*buffer, "[CDATA[", 7))
+ {
+ (*buffer) += 7;
+ (*size) -= 7;
+
+ return __nxml_parse_cdata(doc, buffer, size, data);
+ }
+
+ else if (!strncmp(*buffer, "--", 2))
+ {
+ (*buffer) += 2;
+ (*size) -= 2;
+
+ return __nxml_parse_comment(doc, buffer, size, data);
+ }
+
+ else if (!strncmp(*buffer, "DOCTYPE", 7))
+ {
+ (*buffer) += 7;
+ (*size) -= 7;
+
+ return __nxml_parse_doctype(doc, buffer, size, doctype);
+ }
+
+ else
+ {
+ if (doc->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 ::= '</' Name S? '>'
+ */
+
+ 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 ::= '<?xml' VersionInfo EncodingDecl? SDDecl? S? '?>'
+ */
+ if (!strncmp(buffer, "<?xml ", 6))
+ {
+ buffer += 6;
+ size -= 6;
+
+ if ((err =
+ __nxml_parse_get_attribute(nxml, &buffer, &size,
+ &attr)) != NXML_OK)
+ {
+ nxml_empty(nxml);
+
+ if (freed)
+ free(buffer);
+
+ return err;
+ }
+
+ if (!attr)
+ {
+ if (nxml->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 00000000..f5d4fd49
--- /dev/null
+++ b/plugins/backend/decsync/libnxml/nxml_string.c
@@ -0,0 +1,116 @@
+/* nXml - Copyright (C) 2005-2007 bakunin - Andrea Marchesini
+ * <bakunin@autistici.org>
+ *
+ * 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 00000000..9f075acf
--- /dev/null
+++ b/plugins/backend/decsync/libnxml/nxml_tools.c
@@ -0,0 +1,122 @@
+/* nXml - Copyright (C) 2005-2007 bakunin - Andrea Marchesini
+ * <bakunin@autistici.org>
+ *
+ * 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 00000000..9d8f9fbd
--- /dev/null
+++ b/plugins/backend/decsync/libnxml/nxml_utf.c
@@ -0,0 +1,515 @@
+/* nXml - Copyright (C) 2005-2007 bakunin - Andrea Marchesini
+ * <bakunin@autistici.org>
+ *
+ * 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, "<?xml", 5))
+ {
+ *charset = NXML_CHARSET_UTF8;
+ return 0;
+ }
+
+ /* Utf-16LE: 0x00 0x3c 0x00 0x3f */
+ if (*r_buffer == 0x00 && *(r_buffer + 1) == 0x3c && *(r_buffer + 2) == 0x00 && *(r_buffer + 3) == 0x3f)
+ {
+ (*size) =
+ __nxml_utf16to8(1, (unsigned char *)r_buffer, r_size,
+ (unsigned char **)buffer);
+
+ if (*size < 0)
+ return -1;
+
+ *charset = NXML_CHARSET_UTF16LE;
+
+ return 1;
+ }
+
+ /* Utf-16BE: 0x3c 0x00 0x3f 0x00 */
+ if (*r_buffer == 0x3c && *(r_buffer + 1) == 0x00 && *(r_buffer + 2) == 0x3f && *(r_buffer + 3) == 0x00)
+ {
+ (*size) =
+ __nxml_utf16to8(0, (unsigned char *)r_buffer, r_size,
+ (unsigned char **)buffer);
+
+ if (*size < 0)
+ return -1;
+
+ *charset = NXML_CHARSET_UTF16BE;
+
+ return 1;
+ }
+
+ /* UCS-4 (1234): 0x00 0x00 0x00 0x3c */
+ if (*r_buffer == 0x00 && *(r_buffer + 1) == 0x00 && *(r_buffer + 2) == 0x00 && *(r_buffer + 3) == 0x3c)
+ {
+ (*size) =
+ __nxml_ucs4to8(0, (unsigned char *)r_buffer, r_size,
+ (unsigned char **)buffer);
+
+ if (*size < 0)
+ return -1;
+
+ *charset = NXML_CHARSET_UCS4_1234;
+
+ return 1;
+ }
+
+ /* UCS-4 (4321): 0x3c 0x00 0x00 0x00 */
+ if (*r_buffer == 0x3c && *(r_buffer + 1) == 0x00 && *(r_buffer + 2) == 0x00 && *(r_buffer + 3) == 0x00)
+ {
+ (*size) =
+ __nxml_ucs4to8(1, (unsigned char *)r_buffer, r_size,
+ (unsigned char **)buffer);
+
+ if (*size < 0)
+ return -1;
+
+ *charset = NXML_CHARSET_UCS4_4321;
+
+ return 1;
+ }
+
+ /* UCS-4 (2143): 0x00 0x00 0x3c 0x00 */
+ if (*r_buffer == 0x00 && *(r_buffer + 1) == 0x00 && *(r_buffer + 2) == 0x3c && *(r_buffer + 3) == 0x00)
+ {
+ (*size) =
+ __nxml_ucs4to8(2, (unsigned char *)r_buffer, r_size,
+ (unsigned char **)buffer);
+
+ if (*size < 0)
+ return -1;
+
+ *charset = NXML_CHARSET_UCS4_2143;
+
+ return 1;
+ }
+
+ /* UCS-4 (3412): 0x00 0x3c 0x00 0x00 */
+ if (*r_buffer == 0x00 && *(r_buffer + 1) == 0x3c && *(r_buffer + 2) == 0x00 && *(r_buffer + 3) == 0x00)
+ {
+ (*size) =
+ __nxml_ucs4to8(3, (unsigned char *)r_buffer, r_size,
+ (unsigned char **)buffer);
+
+ if (*size < 0)
+ return -1;
+
+ *charset = NXML_CHARSET_UCS4_3412;
+
+ return 1;
+ }
+
+ *charset = NXML_CHARSET_UNKNOWN;
+
+ return 0;
+}
+
+int64_t
+__nxml_int_charset(int ch, unsigned char *str, char *charset)
+{
+ if (!charset || strcasecmp(charset, "utf-8"))
+ {
+ str[0] = ch;
+ return 1;
+ }
+
+ /* 8bit: 1000000 */
+ if (ch < 0x80)
+ {
+ str[0] = ch;
+ return 1;
+ }
+
+ /* 11bit: xx100000 xx000000
+ * 1000 0000 0000
+ * 0x 8 0 0
+ */
+ else if (ch < 0x800)
+ {
+ /* 11bits: 110xxxxx 10xxxxxx */
+ str[0] = (ch >> 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 00000000..f1c658c7
--- /dev/null
+++ b/plugins/backend/decsync/libnxml/nxml_write.c
@@ -0,0 +1,410 @@
+/* nXml - Copyright (C) 2005-2007 bakunin - Andrea Marchesini
+ * <bakunin@autistici.org>
+ *
+ * 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, "<!--%s-->", data->value);
+}
+
+static void
+__nxml_write_data_pi(nxml_data_t *data, void (*func)(void *, char *, ...),
+ void *obj)
+{
+ func(obj, "<?%s?>", data->value);
+}
+
+static void
+__nxml_write_data_doctype(nxml_doctype_t *data,
+ void (*func)(void *, char *, ...), void *obj)
+{
+ func(obj, "<!DOCTYPE %s %s>", 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, "</");
+ if (data->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, "<?xml");
+
+ func(obj, " version=\"");
+
+ switch (nxml->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 00000000..aba485dc
--- /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 00000000..ee135cd5
--- /dev/null
+++ b/plugins/backend/decsync/org.gnome.feedreader.decsync.gschema.xml
@@ -0,0 +1,11 @@
+<schemalist>
+ <schema id="org.gnome.feedreader.decsync" path="/org/gnome/feedreader/decsync/" gettext-domain="feedreader">
+ <key name="decsync-dir" type="s">
+ <default>""</default>
+ <summary>DecSync directory</summary>
+ <description>
+ DecSync directory
+ </description>
+ </key>
+ </schema>
+</schemalist>
diff --git a/plugins/backend/decsync/rss-glib/meson.build b/plugins/backend/decsync/rss-glib/meson.build
new file mode 100644
index 00000000..05c5be84
--- /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 00000000..84889e93
--- /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 <chris@dronelabs.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ * Christian Hergert <chris@dronelabs.com>
+ */
+
+#ifndef __RSS_DOCUMENT_PRIVATE_H__
+#define __RSS_DOCUMENT_PRIVATE_H__
+
+#include <mrss.h>
+
+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 00000000..c98a2f13
--- /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 <chris@dronelabs.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ * Christian Hergert <chris@dronelabs.com>
+ */
+
+/**
+ * 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 #RssItem<!-- -->s 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 #RssItem<!-- -->s 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 00000000..58c995ab
--- /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 <chris@dronelabs.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ * Christian Hergert <chris@dronelabs.com>
+ */
+
+#ifndef __RSS_DOCUMENT_H__
+#define __RSS_DOCUMENT_H__
+
+#include <glib-object.h>
+
+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 00000000..2a571cb4
--- /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 <chris@dronelabs.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ * Christian Hergert <chris@dronelabs.com>
+ */
+
+[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<weak Rss.Item> get_items ();
+ public GLib.List<string> 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<string> 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 00000000..1595d64c
--- /dev/null
+++ b/plugins/backend/decsync/rss-glib/rss-glib.h
@@ -0,0 +1,9 @@
+#ifndef __RSS_GLIB_H__
+#define __RSS_GLIB_H__
+
+#include <rss-parser.h>
+#include <rss-document.h>
+#include <rss-item.h>
+#include <rss-version.h>
+
+#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 00000000..d4951a38
--- /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 <chris@dronelabs.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ * Christian Hergert <chris@dronelabs.com>
+ */
+
+#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 00000000..fb39cbb9
--- /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 <chris@dronelabs.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ * Christian Hergert <chris@dronelabs.com>
+ */
+
+/**
+ * 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 00000000..1bbe041c
--- /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 <chris@dronelabs.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ * Christian Hergert <chris@dronelabs.com>
+ */
+
+#ifndef __RSS_ITEM_H__
+#define __RSS_ITEM_H__
+
+#include <glib-object.h>
+
+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 00000000..84e4a2dc
--- /dev/null
+++ b/plugins/backend/decsync/rss-glib/rss-marshal.c
@@ -0,0 +1,54 @@
+#include "rss-marshal.h"
+
+#include <glib-object.h>
+
+
+#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 00000000..159a0ac6
--- /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 <glib-object.h>
+
+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 00000000..eaddb9e4
--- /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 <chris@dronelabs.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ * Christian Hergert <chris@dronelabs.com>
+ */
+
+#ifndef __RSS_PARSER_PRIVATE_H__
+#define __RSS_PARSER_PRIVATE_H__
+
+#include <glib-object.h>
+
+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 00000000..451a7f9f
--- /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 <chris@dronelabs.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ * Christian Hergert <chris@dronelabs.com>
+ */
+
+/**
+ * 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 00000000..6ab95a3d
--- /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 <chris@dronelabs.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ * Christian Hergert <chris@dronelabs.com>
+ */
+
+#ifndef __RSS_PARSER_H__
+#define __RSS_PARSER_H__
+
+#include <glib-object.h>
+#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 00000000..6a618529
--- /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 <chris@dronelabs.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ * Christian Hergert <chris@dronelabs.com>
+ */
+
+#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 ff8f0f04..03385a77 100644
--- a/plugins/backend/meson.build
+++ b/plugins/backend/meson.build
@@ -1,5 +1,6 @@
supported_backend_plugins = [
'bazqux',
+ 'decsync',
'feedbin',
'feedhq',
'feedly',