Fix updating entry XML with category element
[cascardo/atompub.git] / atom / entry.c
1 /*
2  *  Copyright (C) 2008  Thadeu Lima de Souza Cascardo <cascardo@holoscopio.com>
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License along
15  *  with this program; if not, write to the Free Software Foundation, Inc.,
16  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17  */
18
19
20 #include <atompub/atom.h>
21
22 #include <glib.h>
23 #include <libxml/tree.h>
24 #include <libxml/parser.h>
25 #include <time.h>
26
27 struct _atom_entry
28 {
29   xmlDocPtr doc;
30   AtomID *id;
31   char *title;
32   time_t updated;
33   GPtrArray *authors;
34   GPtrArray *categories;
35   char *summary;
36 };
37
38 static void atom_entry_updated_set_from_iso8601 (AtomEntry *, char *);
39
40 void atom_entry_author_add (AtomEntry *, AtomPerson *);
41 static void atom_entry_authors_delete (AtomEntry *);
42
43 void atom_entry_category_add (AtomEntry *, AtomCategory *);
44 static void atom_entry_categories_delete (AtomEntry *);
45
46 AtomEntry *
47 atom_entry_new (char *id, char *title, AtomPerson *author)
48 {
49   AtomEntry *entry;
50   entry = g_slice_new (AtomEntry);
51   entry->doc = NULL;
52   entry->id = atom_id_new (id);
53   entry->title = g_strdup (title);
54   entry->updated = time (0);
55   entry->authors = NULL;
56   entry->categories = NULL;
57   atom_entry_author_add (entry, author);
58   entry->summary = NULL;
59   return entry;
60 }
61
62 AtomEntry *
63 atom_entry_new_data_len (char *data, size_t len)
64 {
65   AtomEntry *entry;
66   xmlNodePtr root;
67   xmlNodePtr child;
68   entry = g_slice_new0 (AtomEntry);
69   entry->doc = xmlReadMemory (data, len, NULL, NULL,
70                               XML_PARSE_RECOVER | XML_PARSE_NOERROR);
71   if (entry->doc == NULL ||
72       (root = xmlDocGetRootElement (entry->doc)) == NULL)
73     {
74       g_slice_free (AtomEntry, entry);
75       return NULL;
76     }
77   if (xmlStrcmp (root->name, "entry"))
78     {
79       xmlFreeDoc (entry->doc);
80       g_slice_free (AtomEntry, entry);
81       return NULL;
82     }
83   for (child = root->xmlChildrenNode; child != NULL; child = child->next)
84     {
85       char * content;
86       content = xmlNodeGetContent (child->xmlChildrenNode);
87       if (!xmlStrcmp (child->name, "id"))
88         entry->id = atom_id_new (content);
89       else if (!xmlStrcmp (child->name, "title"))
90         entry->title = g_strdup (content);
91       else if (!xmlStrcmp (child->name, "updated"))
92         atom_entry_updated_set_from_iso8601 (entry, content);
93       else if (!xmlStrcmp (child->name, "summary"))
94         entry->summary = g_strdup (content);
95       else if (!xmlStrcmp (child->name, "author"))
96         atom_entry_author_add (entry, atom_person_new_from_xmlnode (child));
97       else if (!xmlStrcmp (child->name, "category"))
98         atom_entry_category_add (entry, atom_category_new_from_xmlnode (child));
99       else
100         xmlFree (content);
101     }
102   if (entry->id == NULL || entry->title == NULL ||
103       entry->updated == 0 || entry->authors == NULL)
104     {
105       atom_entry_delete (entry);
106       return NULL;
107     }
108   return entry;
109 }
110
111 void
112 atom_entry_delete (AtomEntry *entry)
113 {
114   if (entry->doc)
115     xmlFreeDoc (entry->doc);
116   if (entry->id)
117     atom_id_delete (entry->id);
118   if (entry->title)
119     g_free (entry->title);
120   if (entry->authors)
121     atom_entry_authors_delete (entry);
122   if (entry->categories)
123     atom_entry_categories_delete (entry);
124   if (entry->summary)
125     g_free (entry->summary);
126   g_slice_free (AtomEntry, entry);
127 }
128
129 AtomID *
130 atom_entry_id (AtomEntry *entry)
131 {
132   return entry->id;
133 }
134
135 void
136 atom_entry_id_set (AtomEntry *entry, AtomID *id)
137 {
138   if (id == NULL)
139     return;
140   if (entry->id)
141     atom_id_delete (entry->id);
142   entry->id = id;
143 }
144
145 char *
146 atom_entry_title (AtomEntry *entry)
147 {
148   return entry->title;
149 }
150
151 void
152 atom_entry_title_set (AtomEntry *entry, char *title)
153 {
154   if (title == NULL)
155     return;
156   if (entry->title)
157     g_free (title);
158   entry->title = g_strdup (title);
159 }
160
161 time_t
162 atom_entry_updated (AtomEntry *entry)
163 {
164   return entry->updated;
165 }
166
167 void
168 atom_entry_updated_set (AtomEntry *entry, time_t updated)
169 {
170   entry->updated = updated;
171 }
172
173 static void
174 atom_entry_updated_set_from_iso8601 (AtomEntry *entry, char *updated)
175 {
176   GTimeVal gtv;
177   g_time_val_from_iso8601 (updated, &gtv);
178   entry->updated = gtv.tv_sec;
179 }
180
181 static char *
182 atom_entry_updated_to_iso8601 (AtomEntry *entry)
183 {
184   GTimeVal gtv;
185   gtv.tv_sec = entry->updated;
186   gtv.tv_usec = 0;
187   return g_time_val_to_iso8601 (&gtv);
188 }
189
190 void
191 atom_entry_authors (AtomEntry *entry, AtomPerson *** authors, size_t *len)
192 {
193   if (len)
194     *len = entry->authors->len;
195   if (authors)
196     *authors = entry->authors->pdata;
197 }
198
199 void
200 atom_entry_author_add (AtomEntry *entry, AtomPerson *author)
201 {
202   if (entry->authors == NULL)
203     {
204       entry->authors = g_ptr_array_new ();
205     }
206   g_ptr_array_add (entry->authors, author);
207 }
208
209 static void
210 atom_entry_authors_delete (AtomEntry *entry)
211 {
212   size_t len = entry->authors->len;
213   int i;
214   for (i = 0; i < len; i++)
215     atom_person_delete (g_ptr_array_index (entry->authors, i));
216   g_ptr_array_free (entry->authors, TRUE);
217 }
218
219 void
220 atom_entry_categories (AtomEntry *entry, AtomCategory *** categories,
221                        size_t *len)
222 {
223   if (len)
224     *len = entry->categories->len;
225   if (categories)
226     *categories = entry->categories->pdata;
227 }
228
229 void
230 atom_entry_category_add (AtomEntry *entry, AtomCategory *category)
231 {
232   g_ptr_array_add (entry->categories, category);
233   if (entry->categories == NULL)
234     {
235       entry->categories = g_ptr_array_new ();
236     }
237   g_ptr_array_add (entry->categories, category);
238 }
239
240 static void
241 atom_entry_categories_delete (AtomEntry *entry)
242 {
243   size_t len = entry->categories->len;
244   int i;
245   for (i = 0; i < len; i++)
246     atom_category_delete (g_ptr_array_index (entry->categories, i));
247   g_ptr_array_free (entry->categories, TRUE);
248 }
249
250 char *
251 atom_entry_summary (AtomEntry *entry)
252 {
253   return entry->summary;
254 }
255
256 void
257 atom_entry_summary_set (AtomEntry *entry, char *summary)
258 {
259   if (entry->summary)
260     g_free (entry->summary);
261   entry->summary = g_strdup (summary);
262 }
263
264 static void
265 atom_entry_update_xmlnode (AtomEntry *entry)
266 {
267   xmlNodePtr root;
268   xmlNodePtr id;
269   xmlNodePtr title;
270   xmlNodePtr updated;
271   xmlNodePtr summary;
272   xmlNodePtr author;
273   xmlNodePtr cat;
274   char *updatedstr;
275   int i;
276   if (entry->doc == NULL)
277     {
278       entry->doc = xmlNewDoc ("1.0");
279       root = xmlNewNode (NULL, "entry");
280       xmlNewNs (root, ATOM_NAMESPACE, NULL);
281       xmlDocSetRootElement (entry->doc, root);
282     }
283   else
284     {
285       xmlNodePtr child;
286       xmlNodePtr next;
287       root = xmlDocGetRootElement (entry->doc);
288       child = root->xmlChildrenNode;
289       while (child != NULL)
290         {
291           next = child->next;
292           if (!xmlStrcmp (child->name, "id") ||
293               !xmlStrcmp (child->name, "title") ||
294               !xmlStrcmp (child->name, "updated") ||
295               !xmlStrcmp (child->name, "summary") ||
296               !xmlStrcmp (child->name, "author") ||
297               !xmlStrcmp (child->name, "category"))
298             {
299               xmlUnlinkNode (child);
300               xmlFreeNode (child);
301             }
302           child = next;
303         }
304     }
305   id = xmlNewTextChild (root, NULL, "id", atom_id_string (entry->id));
306   title = xmlNewTextChild (root, NULL, "title", entry->title);
307   updatedstr = atom_entry_updated_to_iso8601 (entry);
308   updated = xmlNewTextChild (root, NULL, "updated", updatedstr);
309   g_free (updatedstr);
310   if (entry->summary)
311     summary = xmlNewTextChild (root, NULL, "summary", entry->summary);
312   for (i = 0; i < entry->authors->len; i++)
313     {
314       AtomPerson *person;
315       person = g_ptr_array_index (entry->authors, i);
316       author = atom_person_to_xmlnode (person, "author");
317       xmlAddChild (root, author);
318     }
319   for (i = 0; entry->categories && i < entry->categories->len; i++)
320     {
321       AtomCategory *category;
322       category = g_ptr_array_index (entry->categories, i);
323       cat = atom_category_to_xmlnode (category, "category");
324       xmlAddChild (root, cat);
325     }
326 }
327
328 void
329 atom_entry_string (AtomEntry *entry, char **buffer, size_t *len)
330 {
331   atom_entry_update_xmlnode (entry);
332   xmlDocDumpMemory (entry->doc, buffer, len);
333 }
334
335 xmlNodePtr
336 atom_entry_to_xmlnode (AtomEntry *entry)
337 {
338   atom_entry_update_xmlnode (entry);
339   return xmlCopyNodeList (xmlDocGetRootElement (entry->doc));
340 }