Fix updating entry XML with category element
[cascardo/atompub.git] / atom / entry.c
index d366500..ff414ee 100644 (file)
 #include <glib.h>
 #include <libxml/tree.h>
 #include <libxml/parser.h>
-
-#define ATOM_NAMESPACE "http://www.w3.org/2005/Atom"
+#include <time.h>
 
 struct _atom_entry
 {
   xmlDocPtr doc;
-  char *id;
+  AtomID *id;
   char *title;
-  AtomPerson *author;
+  time_t updated;
+  GPtrArray *authors;
+  GPtrArray *categories;
   char *summary;
 };
 
+static void atom_entry_updated_set_from_iso8601 (AtomEntry *, char *);
+
+void atom_entry_author_add (AtomEntry *, AtomPerson *);
+static void atom_entry_authors_delete (AtomEntry *);
+
+void atom_entry_category_add (AtomEntry *, AtomCategory *);
+static void atom_entry_categories_delete (AtomEntry *);
+
 AtomEntry *
-atom_entry_new (char *title, AtomPerson *author)
+atom_entry_new (char *id, char *title, AtomPerson *author)
 {
   AtomEntry *entry;
   entry = g_slice_new (AtomEntry);
   entry->doc = NULL;
-  entry->id = NULL;
+  entry->id = atom_id_new (id);
   entry->title = g_strdup (title);
-  entry->author = author;
+  entry->updated = time (0);
+  entry->authors = NULL;
+  entry->categories = NULL;
+  atom_entry_author_add (entry, author);
   entry->summary = NULL;
   return entry;
 }
@@ -54,7 +66,8 @@ atom_entry_new_data_len (char *data, size_t len)
   xmlNodePtr root;
   xmlNodePtr child;
   entry = g_slice_new0 (AtomEntry);
-  entry->doc = xmlReadMemory (data, len, NULL, NULL, XML_PARSE_RECOVER);
+  entry->doc = xmlReadMemory (data, len, NULL, NULL,
+                              XML_PARSE_RECOVER | XML_PARSE_NOERROR);
   if (entry->doc == NULL ||
       (root = xmlDocGetRootElement (entry->doc)) == NULL)
     {
@@ -72,15 +85,25 @@ atom_entry_new_data_len (char *data, size_t len)
       char * content;
       content = xmlNodeGetContent (child->xmlChildrenNode);
       if (!xmlStrcmp (child->name, "id"))
-       entry->id = content;
+        entry->id = atom_id_new (content);
       else if (!xmlStrcmp (child->name, "title"))
-       entry->title = content;
+        entry->title = g_strdup (content);
+      else if (!xmlStrcmp (child->name, "updated"))
+        atom_entry_updated_set_from_iso8601 (entry, content);
       else if (!xmlStrcmp (child->name, "summary"))
-       entry->summary = content;
+        entry->summary = g_strdup (content);
       else if (!xmlStrcmp (child->name, "author"))
-       entry->author = atom_person_new_from_xmlnode (child);
+        atom_entry_author_add (entry, atom_person_new_from_xmlnode (child));
+      else if (!xmlStrcmp (child->name, "category"))
+        atom_entry_category_add (entry, atom_category_new_from_xmlnode (child));
       else
-       xmlFree (content);
+        xmlFree (content);
+    }
+  if (entry->id == NULL || entry->title == NULL ||
+      entry->updated == 0 || entry->authors == NULL)
+    {
+      atom_entry_delete (entry);
+      return NULL;
     }
   return entry;
 }
@@ -91,28 +114,32 @@ atom_entry_delete (AtomEntry *entry)
   if (entry->doc)
     xmlFreeDoc (entry->doc);
   if (entry->id)
-    g_free (entry->id);
+    atom_id_delete (entry->id);
   if (entry->title)
     g_free (entry->title);
-  if (entry->author)
-    atom_person_delete (entry->author);
+  if (entry->authors)
+    atom_entry_authors_delete (entry);
+  if (entry->categories)
+    atom_entry_categories_delete (entry);
   if (entry->summary)
     g_free (entry->summary);
   g_slice_free (AtomEntry, entry);
 }
 
-char *
+AtomID *
 atom_entry_id (AtomEntry *entry)
 {
   return entry->id;
 }
 
 void
-atom_entry_id_set (AtomEntry *entry, char *id)
+atom_entry_id_set (AtomEntry *entry, AtomID *id)
 {
+  if (id == NULL)
+    return;
   if (entry->id)
-    g_free (entry->id);
-  entry->id = g_strdup (id);
+    atom_id_delete (entry->id);
+  entry->id = id;
 }
 
 char *
@@ -124,23 +151,100 @@ atom_entry_title (AtomEntry *entry)
 void
 atom_entry_title_set (AtomEntry *entry, char *title)
 {
+  if (title == NULL)
+    return;
   if (entry->title)
     g_free (title);
-  entry->title = g_strdup (entry);
+  entry->title = g_strdup (title);
+}
+
+time_t
+atom_entry_updated (AtomEntry *entry)
+{
+  return entry->updated;
+}
+
+void
+atom_entry_updated_set (AtomEntry *entry, time_t updated)
+{
+  entry->updated = updated;
+}
+
+static void
+atom_entry_updated_set_from_iso8601 (AtomEntry *entry, char *updated)
+{
+  GTimeVal gtv;
+  g_time_val_from_iso8601 (updated, &gtv);
+  entry->updated = gtv.tv_sec;
+}
+
+static char *
+atom_entry_updated_to_iso8601 (AtomEntry *entry)
+{
+  GTimeVal gtv;
+  gtv.tv_sec = entry->updated;
+  gtv.tv_usec = 0;
+  return g_time_val_to_iso8601 (&gtv);
+}
+
+void
+atom_entry_authors (AtomEntry *entry, AtomPerson *** authors, size_t *len)
+{
+  if (len)
+    *len = entry->authors->len;
+  if (authors)
+    *authors = entry->authors->pdata;
+}
+
+void
+atom_entry_author_add (AtomEntry *entry, AtomPerson *author)
+{
+  if (entry->authors == NULL)
+    {
+      entry->authors = g_ptr_array_new ();
+    }
+  g_ptr_array_add (entry->authors, author);
+}
+
+static void
+atom_entry_authors_delete (AtomEntry *entry)
+{
+  size_t len = entry->authors->len;
+  int i;
+  for (i = 0; i < len; i++)
+    atom_person_delete (g_ptr_array_index (entry->authors, i));
+  g_ptr_array_free (entry->authors, TRUE);
 }
 
-AtomPerson *
-atom_entry_author (AtomEntry *entry)
+void
+atom_entry_categories (AtomEntry *entry, AtomCategory *** categories,
+                       size_t *len)
 {
-  return entry->author;
+  if (len)
+    *len = entry->categories->len;
+  if (categories)
+    *categories = entry->categories->pdata;
 }
 
 void
-atom_entry_author_set (AtomEntry *entry, AtomPerson *author)
+atom_entry_category_add (AtomEntry *entry, AtomCategory *category)
 {
-  if (entry->author)
-    atom_person_delete (entry->author);
-  entry->author = author;
+  g_ptr_array_add (entry->categories, category);
+  if (entry->categories == NULL)
+    {
+      entry->categories = g_ptr_array_new ();
+    }
+  g_ptr_array_add (entry->categories, category);
+}
+
+static void
+atom_entry_categories_delete (AtomEntry *entry)
+{
+  size_t len = entry->categories->len;
+  int i;
+  for (i = 0; i < len; i++)
+    atom_category_delete (g_ptr_array_index (entry->categories, i));
+  g_ptr_array_free (entry->categories, TRUE);
 }
 
 char *
@@ -157,21 +261,80 @@ atom_entry_summary_set (AtomEntry *entry, char *summary)
   entry->summary = g_strdup (summary);
 }
 
-char *
-atom_entry_string (AtomEntry *entry)
+static void
+atom_entry_update_xmlnode (AtomEntry *entry)
+{
+  xmlNodePtr root;
+  xmlNodePtr id;
+  xmlNodePtr title;
+  xmlNodePtr updated;
+  xmlNodePtr summary;
+  xmlNodePtr author;
+  xmlNodePtr cat;
+  char *updatedstr;
+  int i;
+  if (entry->doc == NULL)
+    {
+      entry->doc = xmlNewDoc ("1.0");
+      root = xmlNewNode (NULL, "entry");
+      xmlNewNs (root, ATOM_NAMESPACE, NULL);
+      xmlDocSetRootElement (entry->doc, root);
+    }
+  else
+    {
+      xmlNodePtr child;
+      xmlNodePtr next;
+      root = xmlDocGetRootElement (entry->doc);
+      child = root->xmlChildrenNode;
+      while (child != NULL)
+        {
+          next = child->next;
+          if (!xmlStrcmp (child->name, "id") ||
+              !xmlStrcmp (child->name, "title") ||
+              !xmlStrcmp (child->name, "updated") ||
+              !xmlStrcmp (child->name, "summary") ||
+              !xmlStrcmp (child->name, "author") ||
+              !xmlStrcmp (child->name, "category"))
+            {
+              xmlUnlinkNode (child);
+              xmlFreeNode (child);
+            }
+          child = next;
+        }
+    }
+  id = xmlNewTextChild (root, NULL, "id", atom_id_string (entry->id));
+  title = xmlNewTextChild (root, NULL, "title", entry->title);
+  updatedstr = atom_entry_updated_to_iso8601 (entry);
+  updated = xmlNewTextChild (root, NULL, "updated", updatedstr);
+  g_free (updatedstr);
+  if (entry->summary)
+    summary = xmlNewTextChild (root, NULL, "summary", entry->summary);
+  for (i = 0; i < entry->authors->len; i++)
+    {
+      AtomPerson *person;
+      person = g_ptr_array_index (entry->authors, i);
+      author = atom_person_to_xmlnode (person, "author");
+      xmlAddChild (root, author);
+    }
+  for (i = 0; entry->categories && i < entry->categories->len; i++)
+    {
+      AtomCategory *category;
+      category = g_ptr_array_index (entry->categories, i);
+      cat = atom_category_to_xmlnode (category, "category");
+      xmlAddChild (root, cat);
+    }
+}
+
+void
+atom_entry_string (AtomEntry *entry, char **buffer, size_t *len)
 {
-  char *buffer;
-  int size;
-  xmlDocDumpMemory (entry->doc, &buffer, &size);
-  return buffer;
+  atom_entry_update_xmlnode (entry);
+  xmlDocDumpMemory (entry->doc, buffer, len);
 }
 
-size_t
-atom_entry_len (AtomEntry *entry)
+xmlNodePtr
+atom_entry_to_xmlnode (AtomEntry *entry)
 {
-  char *buffer;
-  int size;
-  xmlDocDumpMemory (entry->doc, &buffer, &size);
-  xmlFree (buffer);
-  return size;
+  atom_entry_update_xmlnode (entry);
+  return xmlCopyNodeList (xmlDocGetRootElement (entry->doc));
 }