Add support for content in the entry.
[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   AtomContent *content;
37 };
38
39 static void atom_entry_updated_set_from_iso8601 (AtomEntry *, char *);
40
41 void atom_entry_author_add (AtomEntry *, AtomPerson *);
42 static void atom_entry_authors_delete (AtomEntry *);
43
44 void atom_entry_category_add (AtomEntry *, AtomCategory *);
45 static void atom_entry_categories_delete (AtomEntry *);
46
47 AtomEntry *
48 atom_entry_new (char *id, char *title, AtomPerson *author)
49 {
50   AtomEntry *entry;
51   entry = g_slice_new (AtomEntry);
52   entry->doc = NULL;
53   entry->id = atom_id_new (id);
54   entry->title = g_strdup (title);
55   entry->updated = time (0);
56   entry->authors = NULL;
57   entry->categories = NULL;
58   atom_entry_author_add (entry, author);
59   entry->summary = NULL;
60   entry->content = NULL;
61   return entry;
62 }
63
64 AtomEntry *
65 atom_entry_new_data_len (char *data, size_t len)
66 {
67   AtomEntry *entry;
68   xmlNodePtr root;
69   xmlNodePtr child;
70   entry = g_slice_new0 (AtomEntry);
71   entry->doc = xmlReadMemory (data, len, NULL, NULL,
72                               XML_PARSE_RECOVER | XML_PARSE_NOERROR);
73   if (entry->doc == NULL ||
74       (root = xmlDocGetRootElement (entry->doc)) == NULL)
75     {
76       g_slice_free (AtomEntry, entry);
77       return NULL;
78     }
79   if (xmlStrcmp (root->name, "entry"))
80     {
81       xmlFreeDoc (entry->doc);
82       g_slice_free (AtomEntry, entry);
83       return NULL;
84     }
85   for (child = root->xmlChildrenNode; child != NULL; child = child->next)
86     {
87       char * content;
88       content = xmlNodeGetContent (child->xmlChildrenNode);
89       if (!xmlStrcmp (child->name, "id"))
90         entry->id = atom_id_new (content);
91       else if (!xmlStrcmp (child->name, "title"))
92         entry->title = g_strdup (content);
93       else if (!xmlStrcmp (child->name, "updated"))
94         atom_entry_updated_set_from_iso8601 (entry, content);
95       else if (!xmlStrcmp (child->name, "summary"))
96         entry->summary = g_strdup (content);
97       else if (!xmlStrcmp (child->name, "author"))
98         atom_entry_author_add (entry, atom_person_new_from_xmlnode (child));
99       else if (!xmlStrcmp (child->name, "category"))
100         atom_entry_category_add (entry, atom_category_new_from_xmlnode (child));
101       else if (!xmlStrcmp (child->name, "content"))
102         entry->content = atom_content_new_from_xmlnode (child);
103       else
104         xmlFree (content);
105     }
106   if (entry->id == NULL || entry->title == NULL ||
107       entry->updated == 0 || entry->authors == NULL)
108     {
109       atom_entry_delete (entry);
110       return NULL;
111     }
112   return entry;
113 }
114
115 void
116 atom_entry_delete (AtomEntry *entry)
117 {
118   if (entry->doc)
119     xmlFreeDoc (entry->doc);
120   if (entry->id)
121     atom_id_delete (entry->id);
122   if (entry->title)
123     g_free (entry->title);
124   if (entry->authors)
125     atom_entry_authors_delete (entry);
126   if (entry->categories)
127     atom_entry_categories_delete (entry);
128   if (entry->summary)
129     g_free (entry->summary);
130   g_slice_free (AtomEntry, entry);
131 }
132
133 AtomID *
134 atom_entry_id (AtomEntry *entry)
135 {
136   return entry->id;
137 }
138
139 void
140 atom_entry_id_set (AtomEntry *entry, AtomID *id)
141 {
142   if (id == NULL)
143     return;
144   if (entry->id)
145     atom_id_delete (entry->id);
146   entry->id = id;
147 }
148
149 char *
150 atom_entry_title (AtomEntry *entry)
151 {
152   return entry->title;
153 }
154
155 void
156 atom_entry_title_set (AtomEntry *entry, char *title)
157 {
158   if (title == NULL)
159     return;
160   if (entry->title)
161     g_free (title);
162   entry->title = g_strdup (title);
163 }
164
165 time_t
166 atom_entry_updated (AtomEntry *entry)
167 {
168   return entry->updated;
169 }
170
171 void
172 atom_entry_updated_set (AtomEntry *entry, time_t updated)
173 {
174   entry->updated = updated;
175 }
176
177 static void
178 atom_entry_updated_set_from_iso8601 (AtomEntry *entry, char *updated)
179 {
180   GTimeVal gtv;
181   g_time_val_from_iso8601 (updated, &gtv);
182   entry->updated = gtv.tv_sec;
183 }
184
185 static char *
186 atom_entry_updated_to_iso8601 (AtomEntry *entry)
187 {
188   GTimeVal gtv;
189   gtv.tv_sec = entry->updated;
190   gtv.tv_usec = 0;
191   return g_time_val_to_iso8601 (&gtv);
192 }
193
194 void
195 atom_entry_authors (AtomEntry *entry, AtomPerson *** authors, size_t *len)
196 {
197   if (len)
198     *len = entry->authors->len;
199   if (authors)
200     *authors = entry->authors->pdata;
201 }
202
203 void
204 atom_entry_author_add (AtomEntry *entry, AtomPerson *author)
205 {
206   if (entry->authors == NULL)
207     {
208       entry->authors = g_ptr_array_new ();
209     }
210   g_ptr_array_add (entry->authors, author);
211 }
212
213 static void
214 atom_entry_authors_delete (AtomEntry *entry)
215 {
216   size_t len = entry->authors->len;
217   int i;
218   for (i = 0; i < len; i++)
219     atom_person_delete (g_ptr_array_index (entry->authors, i));
220   g_ptr_array_free (entry->authors, TRUE);
221 }
222
223 void
224 atom_entry_categories (AtomEntry *entry, AtomCategory *** categories,
225                        size_t *len)
226 {
227   if (len)
228     *len = entry->categories->len;
229   if (categories)
230     *categories = entry->categories->pdata;
231 }
232
233 void
234 atom_entry_category_add (AtomEntry *entry, AtomCategory *category)
235 {
236   g_ptr_array_add (entry->categories, category);
237   if (entry->categories == NULL)
238     {
239       entry->categories = g_ptr_array_new ();
240     }
241   g_ptr_array_add (entry->categories, category);
242 }
243
244 static void
245 atom_entry_categories_delete (AtomEntry *entry)
246 {
247   size_t len = entry->categories->len;
248   int i;
249   for (i = 0; i < len; i++)
250     atom_category_delete (g_ptr_array_index (entry->categories, i));
251   g_ptr_array_free (entry->categories, TRUE);
252 }
253
254 char *
255 atom_entry_summary (AtomEntry *entry)
256 {
257   return entry->summary;
258 }
259
260 void
261 atom_entry_summary_set (AtomEntry *entry, char *summary)
262 {
263   if (entry->summary)
264     g_free (entry->summary);
265   entry->summary = g_strdup (summary);
266 }
267
268 AtomContent *
269 atom_entry_content (AtomEntry *entry)
270 {
271   return entry->content;
272 }
273
274 void
275 atom_entry_content_set (AtomEntry *entry, AtomContent *content)
276 {
277   if (entry->content)
278     atom_content_delete (entry->content);
279   entry->content = content;
280 }
281
282 static void
283 atom_entry_update_xmlnode (AtomEntry *entry)
284 {
285   xmlNodePtr root;
286   xmlNodePtr id;
287   xmlNodePtr title;
288   xmlNodePtr updated;
289   xmlNodePtr summary;
290   xmlNodePtr author;
291   xmlNodePtr cat;
292   char *updatedstr;
293   int i;
294   if (entry->doc == NULL)
295     {
296       entry->doc = xmlNewDoc ("1.0");
297       root = xmlNewNode (NULL, "entry");
298       xmlNewNs (root, ATOM_NAMESPACE, NULL);
299       xmlDocSetRootElement (entry->doc, root);
300     }
301   else
302     {
303       xmlNodePtr child;
304       xmlNodePtr next;
305       root = xmlDocGetRootElement (entry->doc);
306       child = root->xmlChildrenNode;
307       while (child != NULL)
308         {
309           next = child->next;
310           if (!xmlStrcmp (child->name, "id") ||
311               !xmlStrcmp (child->name, "title") ||
312               !xmlStrcmp (child->name, "updated") ||
313               !xmlStrcmp (child->name, "summary") ||
314               !xmlStrcmp (child->name, "author") ||
315               !xmlStrcmp (child->name, "category") ||
316               !xmlStrcmp (child->name, "content"))
317             {
318               xmlUnlinkNode (child);
319               xmlFreeNode (child);
320             }
321           child = next;
322         }
323     }
324   id = xmlNewTextChild (root, NULL, "id", atom_id_string (entry->id));
325   title = xmlNewTextChild (root, NULL, "title", entry->title);
326   updatedstr = atom_entry_updated_to_iso8601 (entry);
327   updated = xmlNewTextChild (root, NULL, "updated", updatedstr);
328   g_free (updatedstr);
329   if (entry->summary)
330     summary = xmlNewTextChild (root, NULL, "summary", entry->summary);
331   for (i = 0; i < entry->authors->len; i++)
332     {
333       AtomPerson *person;
334       person = g_ptr_array_index (entry->authors, i);
335       author = atom_person_to_xmlnode (person, "author");
336       xmlAddChild (root, author);
337     }
338   for (i = 0; entry->categories && i < entry->categories->len; i++)
339     {
340       AtomCategory *category;
341       category = g_ptr_array_index (entry->categories, i);
342       cat = atom_category_to_xmlnode (category, "category");
343       xmlAddChild (root, cat);
344     }
345   if (entry->content)
346     xmlAddChild (root, atom_content_to_xmlnode (entry->content));
347 }
348
349 void
350 atom_entry_string (AtomEntry *entry, char **buffer, size_t *len)
351 {
352   atom_entry_update_xmlnode (entry);
353   xmlDocDumpMemory (entry->doc, buffer, len);
354 }
355
356 xmlNodePtr
357 atom_entry_to_xmlnode (AtomEntry *entry)
358 {
359   atom_entry_update_xmlnode (entry);
360   return xmlCopyNodeList (xmlDocGetRootElement (entry->doc));
361 }