summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordiscomfitor <michael.blumenkrantz@gmail.com>2013-03-23 00:05:50 +0000
committerdiscomfitor <michael.blumenkrantz@gmail.com>2013-03-23 00:05:50 +0000
commit214f614274ca1ca737966494eea046947dce6609 (patch)
tree7f51ba7839408140743c0e2d5b4a4dea4246c986
parent1c0029b3d484076c6832035c3f129805f74fdc75 (diff)
more rss work
-rw-r--r--src/include/azy/Azy.h88
-rw-r--r--src/include/azy/azy_private.h57
-rw-r--r--src/lib/azy/azy_content_xml.cpp67
-rw-r--r--src/lib/azy/azy_net_cookie.c202
-rw-r--r--src/lib/azy/azy_rss.c76
-rw-r--r--src/lib/azy/azy_rss_item.c67
-rw-r--r--src/lib/azy/azy_utils.c201
7 files changed, 460 insertions, 298 deletions
diff --git a/src/include/azy/Azy.h b/src/include/azy/Azy.h
index 86af469..dff09b6 100644
--- a/src/include/azy/Azy.h
+++ b/src/include/azy/Azy.h
@@ -124,13 +124,19 @@ typedef struct Azy_Rss_Item Azy_Rss_Item;
124 */ 124 */
125typedef struct Azy_Rss_Link 125typedef struct Azy_Rss_Link
126{ 126{
127 const char *title; /**< The title of the link */ 127 Eina_Stringshare *title; /**< The title of the link */
128 const char *href; /**< The link URI */ 128 Eina_Stringshare *href; /**< The link URI */
129 const char *rel; /**< The relation type */ 129 Eina_Stringshare *rel; /**< The relation type */
130 const char *type; /**< The content-type */ 130 Eina_Stringshare *type; /**< The content-type */
131 const char *hreflang; /**< The language of the URI */ 131 Eina_Stringshare *hreflang; /**< The language of the URI */
132 size_t length; /**< The size of the URI */ 132 size_t length; /**< The size of the URI */
133} Azy_Rss_Link; 133} Azy_Rss_Link;
134
135typedef struct Azy_Rss_Category
136{
137 Eina_Stringshare *category;
138 Eina_Stringshare *domain;
139} Azy_Rss_Category;
134/** 140/**
135 * @typedef Azy_Rss_Contact 141 * @typedef Azy_Rss_Contact
136 * An object representing a contact from an RSS feed 142 * An object representing a contact from an RSS feed
@@ -362,6 +368,7 @@ EAPI const char *azy_util_uuid_new(void);
362EAPI Azy_Net_Transport azy_util_transport_get(const char *content_type); 368EAPI Azy_Net_Transport azy_util_transport_get(const char *content_type);
363EAPI Eina_Bool azy_util_ip_is_valid(const char *ip); 369EAPI Eina_Bool azy_util_ip_is_valid(const char *ip);
364EAPI Eina_Bool azy_util_domain_match(const char *domain, const char *match); 370EAPI Eina_Bool azy_util_domain_match(const char *domain, const char *match);
371EAPI time_t azy_util_date_parse(char *start, char **end);
365EAPI char *azy_util_strdup(const char *str); 372EAPI char *azy_util_strdup(const char *str);
366EAPI Eina_Bool azy_util_streq(const char *a, const char *b); 373EAPI Eina_Bool azy_util_streq(const char *a, const char *b);
367 374
@@ -546,41 +553,44 @@ EAPI void azy_rss_free(Azy_Rss *rss);
546EAPI void azy_rss_link_free(Azy_Rss_Link *li); 553EAPI void azy_rss_link_free(Azy_Rss_Link *li);
547EAPI void azy_rss_contact_free(Azy_Rss_Contact *c); 554EAPI void azy_rss_contact_free(Azy_Rss_Contact *c);
548EAPI void azy_rss_item_free(Azy_Rss_Item *item); 555EAPI void azy_rss_item_free(Azy_Rss_Item *item);
549EAPI const Eina_List *azy_rss_items_get(Azy_Rss *rss); 556EAPI const Eina_List *azy_rss_items_get(const Azy_Rss *rss);
550EAPI Eina_List *azy_rss_items_steal(Azy_Rss *rss); 557EAPI Eina_List *azy_rss_items_steal(Azy_Rss *rss);
551EAPI Eina_List *azy_rss_authors_get(Azy_Rss *rss); 558EAPI unsigned int azy_rss_skipdays_get(const Azy_Rss *rss);
552EAPI Eina_List *azy_rss_contributors_get(Azy_Rss *rss); 559EAPI unsigned long long azy_rss_skiphours_get(const Azy_Rss *rss);
553EAPI Eina_List *azy_rss_categories_get(Azy_Rss *rss); 560EAPI const Eina_List *azy_rss_authors_get(const Azy_Rss *rss);
554EAPI Eina_List *azy_rss_links_get(Azy_Rss *rss); 561EAPI const Eina_List *azy_rss_contributors_get(const Azy_Rss *rss);
555EAPI const char *azy_rss_title_get(Azy_Rss *rss); 562EAPI const Eina_List *azy_rss_categories_get(const Azy_Rss *rss);
556EAPI const char *azy_rss_link_get(Azy_Rss *rss); 563EAPI const Eina_List *azy_rss_links_get(const Azy_Rss *rss);
557EAPI const char *azy_rss_img_url_get(Azy_Rss *rss); 564EAPI Eina_Stringshare *azy_rss_title_get(const Azy_Rss *rss);
558EAPI const char *azy_rss_desc_get(Azy_Rss *rss); 565EAPI Eina_Stringshare *azy_rss_link_get(const Azy_Rss *rss);
559EAPI const char *azy_rss_rights_get(Azy_Rss *rss); 566EAPI Eina_Stringshare *azy_rss_img_url_get(const Azy_Rss *rss);
560EAPI const char *azy_rss_id_get(Azy_Rss *rss); 567EAPI Eina_Stringshare *azy_rss_desc_get(const Azy_Rss *rss);
561EAPI const char *azy_rss_logo_get(Azy_Rss *rss); 568EAPI Eina_Stringshare *azy_rss_rights_get(const Azy_Rss *rss);
562EAPI const char *azy_rss_generator_get(Azy_Rss *rss); 569EAPI Eina_Stringshare *azy_rss_id_get(const Azy_Rss *rss);
563EAPI const char *azy_rss_subtitle_get(Azy_Rss *rss); 570EAPI Eina_Stringshare *azy_rss_logo_get(const Azy_Rss *rss);
564EAPI Eina_List *azy_rss_item_authors_get(Azy_Rss_Item *item); 571EAPI Eina_Stringshare *azy_rss_generator_get(const Azy_Rss *rss);
565EAPI Eina_List *azy_rss_item_contributors_get(Azy_Rss_Item *item); 572EAPI Eina_Stringshare *azy_rss_subtitle_get(const Azy_Rss *rss);
566EAPI Eina_List *azy_rss_item_categories_get(Azy_Rss_Item *item); 573EAPI const Eina_List *azy_rss_item_authors_get(const Azy_Rss_Item *item);
567EAPI Eina_List *azy_rss_item_links_get(Azy_Rss_Item *item); 574EAPI const Eina_List *azy_rss_item_contributors_get(const Azy_Rss_Item *item);
568EAPI const char *azy_rss_item_title_get(Azy_Rss_Item *item); 575EAPI const Eina_List *azy_rss_item_categories_get(const Azy_Rss_Item *item);
569EAPI const char *azy_rss_item_content_get(Azy_Rss_Item *item); 576EAPI const Eina_List *azy_rss_item_links_get(const Azy_Rss_Item *item);
570EAPI const char *azy_rss_item_content_encoded_get(Azy_Rss_Item *item); 577EAPI Eina_Stringshare *azy_rss_item_title_get(const Azy_Rss_Item *item);
571EAPI const char *azy_rss_item_link_get(Azy_Rss_Item *item); 578EAPI Eina_Stringshare *azy_rss_item_content_get(const Azy_Rss_Item *item);
572EAPI const char *azy_rss_item_desc_get(Azy_Rss_Item *item); 579EAPI Eina_Stringshare *azy_rss_item_content_encoded_get(const Azy_Rss_Item *item);
573EAPI const char *azy_rss_item_date_get(Azy_Rss_Item *item); 580EAPI Eina_Stringshare *azy_rss_item_link_get(const Azy_Rss_Item *item);
574EAPI const char *azy_rss_item_guid_get(Azy_Rss_Item *item); 581EAPI Eina_Stringshare *azy_rss_item_desc_get(const Azy_Rss_Item *item);
575EAPI const char *azy_rss_item_comment_url_get(Azy_Rss_Item *item); 582EAPI time_t azy_rss_item_date_get(const Azy_Rss_Item *item);
576EAPI const char *azy_rss_item_author_get(Azy_Rss_Item *item); 583EAPI Eina_Stringshare *azy_rss_item_guid_get(const Azy_Rss_Item *item);
577EAPI const char *azy_rss_item_rights_get(Azy_Rss_Item *rss); 584EAPI Eina_Stringshare *azy_rss_item_comment_url_get(const Azy_Rss_Item *item);
578EAPI const char *azy_rss_item_summary_get(Azy_Rss_Item *rss); 585EAPI Eina_Stringshare *azy_rss_item_author_get(const Azy_Rss_Item *item);
579EAPI const char *azy_rss_item_id_get(Azy_Rss_Item *rss); 586EAPI Eina_Stringshare *azy_rss_item_rights_get(const Azy_Rss_Item *rss);
580EAPI void azy_rss_print(const char *pre, int indent, Azy_Rss *rss); 587EAPI Eina_Stringshare *azy_rss_item_summary_get(const Azy_Rss_Item *rss);
581EAPI void azy_rss_link_print(const char *pre, int indent, Azy_Rss_Link *li); 588EAPI Eina_Stringshare *azy_rss_item_id_get(const Azy_Rss_Item *rss);
582EAPI void azy_rss_contact_print(const char *pre, int indent, Azy_Rss_Contact *c); 589EAPI void azy_rss_item_enclosure_get(const Azy_Rss_Item *item, Eina_Stringshare **url, Eina_Stringshare **content_type, size_t *length);
583EAPI void azy_rss_item_print(const char *pre, int indent, Azy_Rss_Item *item); 590EAPI void azy_rss_print(const char *pre, int indent, const Azy_Rss *rss);
591EAPI void azy_rss_link_print(const char *pre, int indent, const Azy_Rss_Link *li);
592EAPI void azy_rss_contact_print(const char *pre, int indent, const Azy_Rss_Contact *c);
593EAPI void azy_rss_item_print(const char *pre, int indent, const Azy_Rss_Item *item);
584#ifdef __cplusplus 594#ifdef __cplusplus
585} 595}
586#endif 596#endif
diff --git a/src/include/azy/azy_private.h b/src/include/azy/azy_private.h
index 96bfdac..76e0c68 100644
--- a/src/include/azy/azy_private.h
+++ b/src/include/azy/azy_private.h
@@ -151,24 +151,27 @@ struct Azy_Rss
151{ 151{
152 AZY_MAGIC; 152 AZY_MAGIC;
153 Eina_Bool atom : 1; /* true if item is Azy_Rss_Atom */ 153 Eina_Bool atom : 1; /* true if item is Azy_Rss_Atom */
154 const char *title; 154 Eina_Stringshare *title;
155 const char *img_url; 155 Eina_Stringshare *img_url;
156 156
157 /* rss format only */ 157 /* rss format only */
158 const char *link; 158 Eina_Stringshare *link;
159 const char *desc; 159 Eina_Stringshare *desc;
160 160
161 /* atom format only */ 161 /* atom format only */
162 const char *id; 162 Eina_Stringshare *id;
163 const char *subtitle; 163 Eina_Stringshare *subtitle;
164 const char *rights; 164 Eina_Stringshare *rights;
165 const char *logo; 165 Eina_Stringshare *logo;
166 const char *generator; 166 Eina_Stringshare *generator;
167 struct tm updated; 167 struct tm updated;
168 Eina_List *categories; 168 Eina_List *categories;
169 Eina_List *contributors; 169 Eina_List *contributors;
170 Eina_List *authors; 170 Eina_List *authors;
171 Eina_List *atom_links; 171 Eina_List *atom_links;
172 unsigned int skipdays; // bitshift per day
173 unsigned long long skiphours; // bitshift per hour
174 unsigned int ttl; // in minutes
172 175
173 Eina_List *items; 176 Eina_List *items;
174}; 177};
@@ -177,29 +180,37 @@ struct Azy_Rss_Item
177{ 180{
178 AZY_MAGIC; 181 AZY_MAGIC;
179 Eina_Bool atom : 1; /* true if item is Azy_Rss_Atom */ 182 Eina_Bool atom : 1; /* true if item is Azy_Rss_Atom */
180 const char *title; 183 Eina_Stringshare *title;
181 184
182 /* rss format only */ 185 /* rss format only */
183 const char *link; 186 Eina_Stringshare *link;
184 const char *desc; 187 Eina_Stringshare *desc;
185 const char *date; 188 time_t date;
186 const char *guid; 189 Eina_Stringshare *guid;
187 const char *comment_url; 190 Eina_Stringshare *comment_url;
188 const char *author; 191 Eina_Stringshare *author;
189 const char *content; 192 Eina_Stringshare *content;
190 const char *content_encoded; 193 Eina_Stringshare *content_encoded;
191 194
192 /* atom format only */ 195 /* atom format only */
193 const char *rights; 196 Eina_Stringshare *rights;
194 const char *summary; 197 Eina_Stringshare *summary;
195 const char *id; 198 Eina_Stringshare *id;
196 const char *icon; 199 Eina_Stringshare *icon;
197 struct tm updated; 200 struct tm updated;
198 struct tm published; 201 struct tm published;
202 struct
203 {
204 Eina_Stringshare *url;
205 size_t length;
206 Eina_Stringshare *type;
207 } enclosure;
199 Eina_List *categories; 208 Eina_List *categories;
200 Eina_List *contributors; 209 Eina_List *contributors;
201 Eina_List *authors; 210 Eina_List *authors;
202 Eina_List *atom_links; 211 Eina_List *atom_links;
212
213 Eina_Bool permalink : 1; //guid is permalink (guid is a link)
203}; 214};
204 215
205struct Azy_Net 216struct Azy_Net
@@ -434,6 +445,8 @@ Eina_Bool
434 445
435Azy_Rss *azy_rss_new(void); 446Azy_Rss *azy_rss_new(void);
436Azy_Rss_Item *azy_rss_item_new(void); 447Azy_Rss_Item *azy_rss_item_new(void);
448Azy_Rss_Category *azy_rss_category_new(void);
449void azy_rss_category_free(Azy_Rss_Category *cat);
437 450
438Eina_Bool azy_content_serialize_request_xml(Azy_Content *content); 451Eina_Bool azy_content_serialize_request_xml(Azy_Content *content);
439Eina_Bool azy_content_serialize_response_xml(Azy_Content *content); 452Eina_Bool azy_content_serialize_response_xml(Azy_Content *content);
diff --git a/src/lib/azy/azy_content_xml.cpp b/src/lib/azy/azy_content_xml.cpp
index dbb78d7..1c1d418 100644
--- a/src/lib/azy/azy_content_xml.cpp
+++ b/src/lib/azy/azy_content_xml.cpp
@@ -730,6 +730,17 @@ azy_content_deserialize_response_xml(Azy_Content *content,
730 return EINA_TRUE; 730 return EINA_TRUE;
731} 731}
732 732
733static Azy_Rss_Category *
734azy_content_deserialize_rss_category(xml_node &node)
735{
736 Azy_Rss_Category *cat;
737
738 cat = azy_rss_category_new();
739 cat->domain = eina_stringshare_add(node.attribute("domain").value());
740 cat->category = eina_stringshare_add(node.child_value());
741 return cat;
742}
743
733static Eina_Bool 744static Eina_Bool
734azy_content_deserialize_rss_xml(Azy_Content *content, 745azy_content_deserialize_rss_xml(Azy_Content *content,
735 xml_document &doc) 746 xml_document &doc)
@@ -769,6 +780,14 @@ azy_content_deserialize_rss_xml(Azy_Content *content,
769 rss->title = eina_stringshare_add(n.child_value()); 780 rss->title = eina_stringshare_add(n.child_value());
770 else if ((!rss->link) && (!strcmp(name, "link"))) 781 else if ((!rss->link) && (!strcmp(name, "link")))
771 rss->link = eina_stringshare_add(n.child_value()); 782 rss->link = eina_stringshare_add(n.child_value());
783 else if ((!rss->desc) && (!strcmp(name, "category")))
784 {
785 Azy_Rss_Category *cat;
786
787 cat = azy_content_deserialize_rss_category(n);
788 if (cat)
789 rss->categories = eina_list_append(rss->categories, cat);
790 }
772 else if ((!rss->desc) && (!strcmp(name, "description"))) 791 else if ((!rss->desc) && (!strcmp(name, "description")))
773 rss->desc = eina_stringshare_add(n.child_value()); 792 rss->desc = eina_stringshare_add(n.child_value());
774 else if (!strcmp(name, "item") && (!n.empty())) 793 else if (!strcmp(name, "item") && (!n.empty()))
@@ -793,12 +812,24 @@ azy_content_deserialize_rss_xml(Azy_Content *content,
793 i->desc = eina_stringshare_add(nn.child_value()); 812 i->desc = eina_stringshare_add(nn.child_value());
794 else if ((!i->author) && (!strcmp(name, "author"))) 813 else if ((!i->author) && (!strcmp(name, "author")))
795 eina_stringshare_replace(&i->author, nn.child_value()); 814 eina_stringshare_replace(&i->author, nn.child_value());
815 else if (!strcmp(name, "enclosure"))
816 {
817 i->enclosure.length = nn.attribute("length").as_uint();
818 i->enclosure.url = eina_stringshare_add(nn.attribute("url").value());
819 i->enclosure.type = eina_stringshare_add(nn.attribute("type").value());
820 }
796 else if ((!i->author) && (!strcmp(name, "dc:creator"))) 821 else if ((!i->author) && (!strcmp(name, "dc:creator")))
797 eina_stringshare_replace(&i->author, nn.child_value()); 822 eina_stringshare_replace(&i->author, nn.child_value());
798 else if ((!i->date) && (!strcmp(name, "pubDate"))) 823 else if ((!i->date) && (!strcmp(name, "pubDate")))
799 i->date = eina_stringshare_add(nn.child_value()); 824 i->date = azy_util_date_parse(strdupa(nn.child_value()), NULL);
800 else if ((!i->guid) && (!strcmp(name, "guid"))) 825 else if ((!i->guid) && (!strcmp(name, "guid")))
801 i->guid = eina_stringshare_add(nn.child_value()); 826 {
827 const char *permalink;
828
829 i->guid = eina_stringshare_add(nn.child_value());
830 permalink = nn.attribute("isPermaLink").value();
831 if (permalink && permalink[0]) i->permalink = !strcmp(permalink, "true");
832 }
802 else if ((!i->comment_url) && (!strcmp(name, "comments"))) 833 else if ((!i->comment_url) && (!strcmp(name, "comments")))
803 i->comment_url = eina_stringshare_add(nn.child_value()); 834 i->comment_url = eina_stringshare_add(nn.child_value());
804 else if ((!i->content) && (!strcmp(name, "content"))) 835 else if ((!i->content) && (!strcmp(name, "content")))
@@ -808,6 +839,28 @@ azy_content_deserialize_rss_xml(Azy_Content *content,
808 } 839 }
809 rss->items = eina_list_append(rss->items, i); 840 rss->items = eina_list_append(rss->items, i);
810 } 841 }
842 else if (!strcmp(name, "ttl"))
843 {
844 const char *str;
845
846 str = n.child_value();
847 rss->ttl = strtoul(str, NULL, 10);
848 }
849 else if ((!strcmp(name, "skipDays")) || (!strcmp(name, "skipHours")))
850 {
851 for (xml_node::iterator i = n.first_child().begin(); i != n.first_child().end(); ++i)
852 {
853 xml_node no = *i;
854 const char *str;
855
856 str = no.first_child().child_value();
857 if (!str) continue;
858 if (name[4] == 'D')
859 rss->skipdays |= (1 << strtol(str, NULL, 10));
860 else
861 rss->skiphours |= (1 << strtol(str, NULL, 10));
862 }
863 }
811 } 864 }
812 content->ret = rss; 865 content->ret = rss;
813 return EINA_TRUE; 866 return EINA_TRUE;
@@ -1023,7 +1076,15 @@ azy_content_deserialize_atom_xml(Azy_Content *content,
1023 else SET(id); 1076 else SET(id);
1024#undef SET 1077#undef SET
1025 else if (!strcmp(name, "category")) 1078 else if (!strcmp(name, "category"))
1026 rss->categories = eina_list_append(rss->categories, eina_stringshare_add(n.attribute("term").value())); 1079 {
1080 Azy_Rss_Category *cat;
1081 const char *cg = n.attribute("term").value();
1082
1083 if ((!cg) || (!cg[0])) continue;
1084 cat = azy_rss_category_new();
1085 cat->category = eina_stringshare_add(cg);
1086 rss->categories = eina_list_append(rss->categories, cat);
1087 }
1027 else if (!strcmp(name, "contributor")) 1088 else if (!strcmp(name, "contributor"))
1028 { 1089 {
1029 c = azy_content_deserialize_atom_xml_contact(n); 1090 c = azy_content_deserialize_atom_xml_contact(n);
diff --git a/src/lib/azy/azy_net_cookie.c b/src/lib/azy/azy_net_cookie.c
index add437a..a1c354f 100644
--- a/src/lib/azy/azy_net_cookie.c
+++ b/src/lib/azy/azy_net_cookie.c
@@ -29,32 +29,6 @@ typedef enum
29 AZY_COOKIE_FIELD_EXTENSION 29 AZY_COOKIE_FIELD_EXTENSION
30} Azy_Cookie_Field; 30} Azy_Cookie_Field;
31 31
32typedef enum
33{
34 AZY_DATE_FIELD_IGNORE = -1,
35 AZY_DATE_FIELD_FAILURE = 0,
36 AZY_DATE_FIELD_TIME = (1 << 0),
37 AZY_DATE_FIELD_YEAR = (1 << 1),
38 AZY_DATE_FIELD_MONTH = (1 << 2),
39 AZY_DATE_FIELD_DAY = (1 << 3)
40} Azy_Date_Field;
41
42static const char *const months[] =
43{
44 "jan",
45 "feb",
46 "mar",
47 "apr",
48 "may",
49 "jun",
50 "jul",
51 "aug",
52 "sep",
53 "oct",
54 "nov",
55 "dec"
56};
57
58static const char *const cookie_avs[] = 32static const char *const cookie_avs[] =
59{ 33{
60 [AZY_COOKIE_FIELD_EXPIRES] = "expires", 34 [AZY_COOKIE_FIELD_EXPIRES] = "expires",
@@ -111,180 +85,6 @@ _azy_cookie_tokenize(const char *field)
111 return x; 85 return x;
112} 86}
113 87
114static inline Eina_Bool
115_azy_date_char_is_delimiter(char c)
116{
117 return (c == 0x09) ||
118 ((c >= 0x20) && (c <= 0x2F)) ||
119 ((c >= 0x3B) && (c <= 0x40)) ||
120 ((c >= 0x5B) && (c <= 0x60)) ||
121 ((c >= 0x7B) && (c <= 0x7E))
122 ;
123}
124
125static inline Azy_Date_Field
126_azy_date_field_tokenize(char *start, char **end, Azy_Date_Field exclude)
127{
128 char *p = start;
129
130/*
131 * I feel like I should make a separate file for parsing this
132
133 http://tools.ietf.org/html/rfc6265
134 cookie-date = *delimiter date-token-list *delimiter
135 date-token-list = date-token *( 1*delimiter date-token )
136 date-token = 1*non-delimiter
137
138 delimiter = %x09 / %x20-2F / %x3B-40 / %x5B-60 / %x7B-7E
139 non-delimiter = %x00-08 / %x0A-1F / DIGIT / ":" / ALPHA / %x7F-FF
140 non-digit = %x00-2F / %x3A-FF
141
142 day-of-month = 1*2DIGIT ( non-digit *OCTET )
143 month = ( "jan" / "feb" / "mar" / "apr" /
144 "may" / "jun" / "jul" / "aug" /
145 "sep" / "oct" / "nov" / "dec" ) *OCTET
146 year = 2*4DIGIT ( non-digit *OCTET )
147 time = hms-time ( non-digit *OCTET )
148 hms-time = time-field ":" time-field ":" time-field
149 time-field = 1*2DIGIT
150 */
151 while (!_azy_date_char_is_delimiter(p[0]))
152 p++;
153 *end = p;
154 if (isdigit(start[0]))
155 {
156 switch (p - start)
157 {
158 case 1:
159 case 2:
160 if (!(exclude & AZY_DATE_FIELD_DAY))
161 return AZY_DATE_FIELD_DAY;
162 case 4:
163 if (!(exclude & AZY_DATE_FIELD_YEAR))
164 return AZY_DATE_FIELD_YEAR;
165 return AZY_DATE_FIELD_IGNORE;
166
167 case 8:
168 if (start[2] == ':') // hmstime...hopefully
169 {
170 char *pp = start;
171
172 for (p = start, pp++; isdigit(pp[0]); pp++) ;
173 if ((pp - p != 2) || (pp[0] != ':')) goto error;
174 for (pp++, p = pp; isdigit(pp[0]); pp++) ;
175 if ((pp - p != 2) || (pp[0] != ':')) goto error;
176 for (pp++, p = pp; isdigit(pp[0]); pp++) ;
177 if ((pp - p != 2) || (pp != *end)) goto error;
178 return AZY_DATE_FIELD_TIME;
179 }
180
181 default: goto error;
182 }
183 }
184 if (!(exclude & AZY_DATE_FIELD_MONTH))
185 {
186 if (p - start == 3)
187 {
188 int x;
189
190 /* lowercase here to make memcmp work later */
191 for (x = 0; x < 3; x++)
192 start[x] = tolower(start[x]);
193 return AZY_DATE_FIELD_MONTH;
194 }
195 }
196 return AZY_DATE_FIELD_IGNORE;
197error:
198 *end = strchr(start, ';');
199 return AZY_DATE_FIELD_FAILURE;
200}
201
202static time_t
203_azy_cookie_expires_parse(char *start, char **end)
204{
205 Azy_Date_Field found = 0;
206 char *p;
207 struct tm tm;
208 int x;
209
210 memset(&tm, 0, sizeof(tm));
211 tm.tm_isdst = -1;
212 while (start[0] != ';')
213 {
214 //eg. Wed, 11-Sep-2013 11:51:23 GMT
215 switch (_azy_date_field_tokenize(start, &p, found))
216 {
217 case AZY_DATE_FIELD_IGNORE: break;
218
219 case AZY_DATE_FIELD_TIME:
220 x = strtol(start, NULL, 10);
221 if ((x < 0) || (x > 23)) goto error;
222 tm.tm_hour = x;
223 start += 3;
224 x = strtol(start, NULL, 10);
225 if ((x < 0) || (x > 59)) goto error;
226 tm.tm_min = x;
227 start += 3;
228 x = strtol(start, NULL, 10);
229 if ((x < 0) || (x > 59)) goto error;
230 tm.tm_sec = x;
231 found |= AZY_DATE_FIELD_TIME;
232 break;
233
234 case AZY_DATE_FIELD_YEAR:
235 x = strtol(start, NULL, 10);
236 if (p - start == 2)
237 {
238 if ((x >= 70) && (x <= 99)) x += 1900;
239 else if ((x >= 0) && (x <= 69))
240 x += 2000;
241 else return 0;
242 }
243 else
244 {
245 if (x < 1601) goto error;
246 }
247 tm.tm_year = x - 1900;
248 found |= AZY_DATE_FIELD_YEAR;
249 break;
250
251 case AZY_DATE_FIELD_MONTH:
252 for (x = 0; x < 12; x++)
253 if (!memcmp(start, months[x], 3))
254 {
255 tm.tm_mon = x;
256 found |= AZY_DATE_FIELD_MONTH;
257 break;
258 }
259 break;
260
261 case AZY_DATE_FIELD_DAY:
262 x = strtol(start, NULL, 10);
263 if ((x < 1) || (x > 31)) goto error;
264 tm.tm_mday = x;
265 found |= AZY_DATE_FIELD_DAY;
266 break;
267
268 case AZY_DATE_FIELD_FAILURE:
269 default: goto error;
270 }
271 if (found ==
272 (AZY_DATE_FIELD_TIME | AZY_DATE_FIELD_YEAR | AZY_DATE_FIELD_MONTH | AZY_DATE_FIELD_DAY)
273 )
274 break;
275 start = p;
276 while (_azy_date_char_is_delimiter(start[0]))
277 start++;
278 }
279 if (found ==
280 (AZY_DATE_FIELD_TIME | AZY_DATE_FIELD_YEAR | AZY_DATE_FIELD_MONTH | AZY_DATE_FIELD_DAY)
281 )
282 return mktime(&tm);
283error:
284 *end = strchr(start, ';');
285 return 0;
286}
287
288static void 88static void
289_azy_net_cookie_free(Azy_Net_Cookie *ck) 89_azy_net_cookie_free(Azy_Net_Cookie *ck)
290{ 90{
@@ -731,7 +531,7 @@ azy_net_cookie_parse(const Azy_Net *net, char *txt)
731 /* fml */ 531 /* fml */
732 p = strchr(start, ';'); 532 p = strchr(start, ';');
733 if (ck->max_age) continue; 533 if (ck->max_age) continue;
734 ck->expires = _azy_cookie_expires_parse(start, &p); 534 ck->expires = azy_util_date_parse(start, &p);
735 ck->flags |= AZY_NET_COOKIE_PERSISTENT; 535 ck->flags |= AZY_NET_COOKIE_PERSISTENT;
736 break; 536 break;
737 537
diff --git a/src/lib/azy/azy_rss.c b/src/lib/azy/azy_rss.c
index 57c11f9..4072291 100644
--- a/src/lib/azy/azy_rss.c
+++ b/src/lib/azy/azy_rss.c
@@ -24,13 +24,18 @@
24 */ 24 */
25 25
26static Eina_Mempool *rss_mempool = NULL; 26static Eina_Mempool *rss_mempool = NULL;
27static Eina_Mempool *rss_category_mempool = NULL;
27 28
28Eina_Bool 29Eina_Bool
29azy_rss_init(const char *type) 30azy_rss_init(const char *type)
30{ 31{
31 if (!azy_rss_item_init(type)) return EINA_FALSE; 32 if (!azy_rss_item_init(type)) return EINA_FALSE;
32 rss_mempool = eina_mempool_add(type, "Azy_Rss", NULL, sizeof(Azy_Rss), 64); 33 rss_mempool = eina_mempool_add(type, "Azy_Rss", NULL, sizeof(Azy_Rss), 64);
33 if (rss_mempool) return EINA_TRUE; 34 if (rss_mempool)
35 {
36 rss_category_mempool = eina_mempool_add(type, "Azy_Rss_Category", NULL, sizeof(Azy_Rss_Category), 64);
37 return EINA_TRUE;
38 }
34 39
35 if (strcmp(type, "pass_through")) return azy_rss_init("pass_through"); 40 if (strcmp(type, "pass_through")) return azy_rss_init("pass_through");
36 azy_rss_item_shutdown(); 41 azy_rss_item_shutdown();
@@ -44,6 +49,8 @@ azy_rss_shutdown(void)
44 azy_rss_item_shutdown(); 49 azy_rss_item_shutdown();
45 eina_mempool_del(rss_mempool); 50 eina_mempool_del(rss_mempool);
46 rss_mempool = NULL; 51 rss_mempool = NULL;
52 eina_mempool_del(rss_category_mempool);
53 rss_category_mempool = NULL;
47} 54}
48 55
49/* 56/*
@@ -65,6 +72,21 @@ azy_rss_new(void)
65 return rss; 72 return rss;
66} 73}
67 74
75Azy_Rss_Category *
76azy_rss_category_new(void)
77{
78 return eina_mempool_calloc(rss_category_mempool, sizeof(Azy_Rss_Category));
79}
80
81void
82azy_rss_category_free(Azy_Rss_Category *cat)
83{
84 if (!cat) return;
85 eina_stringshare_del(cat->category);
86 eina_stringshare_del(cat->domain);
87 eina_mempool_free(rss_category_mempool, cat);
88}
89
68/** 90/**
69 * @brief Free an #Azy_Rss_Contact object 91 * @brief Free an #Azy_Rss_Contact object
70 * 92 *
@@ -128,7 +150,7 @@ azy_rss_free(Azy_Rss *rss)
128 eina_stringshare_del(rss->generator); 150 eina_stringshare_del(rss->generator);
129 eina_stringshare_del(rss->subtitle); 151 eina_stringshare_del(rss->subtitle);
130 EINA_LIST_FREE(rss->categories, item) 152 EINA_LIST_FREE(rss->categories, item)
131 eina_stringshare_del(item); 153 azy_rss_category_free(item);
132 EINA_LIST_FREE(rss->contributors, item) 154 EINA_LIST_FREE(rss->contributors, item)
133 azy_rss_contact_free(item); 155 azy_rss_contact_free(item);
134 EINA_LIST_FREE(rss->authors, item) 156 EINA_LIST_FREE(rss->authors, item)
@@ -157,7 +179,7 @@ azy_rss_free(Azy_Rss *rss)
157 * @return An #Eina_List of #Azy_Rss_Item objects 179 * @return An #Eina_List of #Azy_Rss_Item objects
158 */ 180 */
159const Eina_List * 181const Eina_List *
160azy_rss_items_get(Azy_Rss *rss) 182azy_rss_items_get(const Azy_Rss *rss)
161{ 183{
162 if (!AZY_MAGIC_CHECK(rss, AZY_MAGIC_RSS)) 184 if (!AZY_MAGIC_CHECK(rss, AZY_MAGIC_RSS))
163 { 185 {
@@ -196,8 +218,8 @@ azy_rss_items_steal(Azy_Rss *rss)
196 * @param rss The #Azy_Rss (NOT NULL) 218 * @param rss The #Azy_Rss (NOT NULL)
197 * @return An #Eina_List of #Azy_Rss_Contact objects 219 * @return An #Eina_List of #Azy_Rss_Contact objects
198 */ 220 */
199Eina_List * 221const Eina_List *
200azy_rss_authors_get(Azy_Rss *rss) 222azy_rss_authors_get(const Azy_Rss *rss)
201{ 223{
202 if (!AZY_MAGIC_CHECK(rss, AZY_MAGIC_RSS)) 224 if (!AZY_MAGIC_CHECK(rss, AZY_MAGIC_RSS))
203 { 225 {
@@ -214,8 +236,8 @@ azy_rss_authors_get(Azy_Rss *rss)
214 * @param rss The #Azy_Rss (NOT NULL) 236 * @param rss The #Azy_Rss (NOT NULL)
215 * @return An #Eina_List of #Azy_Rss_Contact objects 237 * @return An #Eina_List of #Azy_Rss_Contact objects
216 */ 238 */
217Eina_List * 239const Eina_List *
218azy_rss_contributors_get(Azy_Rss *rss) 240azy_rss_contributors_get(const Azy_Rss *rss)
219{ 241{
220 if (!AZY_MAGIC_CHECK(rss, AZY_MAGIC_RSS)) 242 if (!AZY_MAGIC_CHECK(rss, AZY_MAGIC_RSS))
221 { 243 {
@@ -232,8 +254,8 @@ azy_rss_contributors_get(Azy_Rss *rss)
232 * @param rss The #Azy_Rss (NOT NULL) 254 * @param rss The #Azy_Rss (NOT NULL)
233 * @return An #Eina_List of #Azy_Rss_Link objects 255 * @return An #Eina_List of #Azy_Rss_Link objects
234 */ 256 */
235Eina_List * 257const Eina_List *
236azy_rss_links_get(Azy_Rss *rss) 258azy_rss_links_get(const Azy_Rss *rss)
237{ 259{
238 if (!AZY_MAGIC_CHECK(rss, AZY_MAGIC_RSS)) 260 if (!AZY_MAGIC_CHECK(rss, AZY_MAGIC_RSS))
239 { 261 {
@@ -250,8 +272,8 @@ azy_rss_links_get(Azy_Rss *rss)
250 * @param rss The #Azy_Rss (NOT NULL) 272 * @param rss The #Azy_Rss (NOT NULL)
251 * @return An #Eina_List of stringshared strings 273 * @return An #Eina_List of stringshared strings
252 */ 274 */
253Eina_List * 275const Eina_List *
254azy_rss_categories_get(Azy_Rss *rss) 276azy_rss_categories_get(const Azy_Rss *rss)
255{ 277{
256 if (!AZY_MAGIC_CHECK(rss, AZY_MAGIC_RSS)) 278 if (!AZY_MAGIC_CHECK(rss, AZY_MAGIC_RSS))
257 { 279 {
@@ -261,6 +283,28 @@ azy_rss_categories_get(Azy_Rss *rss)
261 return rss->categories; 283 return rss->categories;
262} 284}
263 285
286unsigned int
287azy_rss_skipdays_get(const Azy_Rss *rss)
288{
289 if (!AZY_MAGIC_CHECK(rss, AZY_MAGIC_RSS))
290 {
291 AZY_MAGIC_FAIL(rss, AZY_MAGIC_RSS);
292 return 0;
293 }
294 return rss->skipdays;
295}
296
297unsigned long long
298azy_rss_skiphours_get(const Azy_Rss *rss)
299{
300 if (!AZY_MAGIC_CHECK(rss, AZY_MAGIC_RSS))
301 {
302 AZY_MAGIC_FAIL(rss, AZY_MAGIC_RSS);
303 return 0;
304 }
305 return rss->skiphours;
306}
307
264#define DEF(NAME) \ 308#define DEF(NAME) \
265/** 309/**
266 @brief Retrieve the NAME of an rss object 310 @brief Retrieve the NAME of an rss object
@@ -269,8 +313,8 @@ azy_rss_categories_get(Azy_Rss *rss)
269 @param rss The #Azy_Rss object (NOT NULL) 313 @param rss The #Azy_Rss object (NOT NULL)
270 @return The NAME, or NULL on failure 314 @return The NAME, or NULL on failure
271 */ \ 315 */ \
272 const char * \ 316 Eina_Stringshare * \
273 azy_rss_##NAME##_get(Azy_Rss * rss) \ 317 azy_rss_##NAME##_get(const Azy_Rss * rss) \
274 { \ 318 { \
275 if (!AZY_MAGIC_CHECK(rss, AZY_MAGIC_RSS)) \ 319 if (!AZY_MAGIC_CHECK(rss, AZY_MAGIC_RSS)) \
276 { \ 320 { \
@@ -304,7 +348,7 @@ DEF(subtitle)
304void 348void
305azy_rss_contact_print(const char *pre, 349azy_rss_contact_print(const char *pre,
306 int indent, 350 int indent,
307 Azy_Rss_Contact *c) 351 const Azy_Rss_Contact *c)
308{ 352{
309 int i; 353 int i;
310 if (!c) return; 354 if (!c) return;
@@ -338,7 +382,7 @@ azy_rss_contact_print(const char *pre,
338void 382void
339azy_rss_link_print(const char *pre, 383azy_rss_link_print(const char *pre,
340 int indent, 384 int indent,
341 Azy_Rss_Link *li) 385 const Azy_Rss_Link *li)
342{ 386{
343 int i; 387 int i;
344 if (!li) return; 388 if (!li) return;
@@ -380,7 +424,7 @@ azy_rss_link_print(const char *pre,
380void 424void
381azy_rss_print(const char *pre, 425azy_rss_print(const char *pre,
382 int indent, 426 int indent,
383 Azy_Rss *rss) 427 const Azy_Rss *rss)
384{ 428{
385 int i; 429 int i;
386 const char *str; 430 const char *str;
diff --git a/src/lib/azy/azy_rss_item.c b/src/lib/azy/azy_rss_item.c
index 9f4c045..0342e68 100644
--- a/src/lib/azy/azy_rss_item.c
+++ b/src/lib/azy/azy_rss_item.c
@@ -88,7 +88,7 @@ azy_rss_item_free(Azy_Rss_Item *item)
88 eina_stringshare_del(item->id); 88 eina_stringshare_del(item->id);
89 eina_stringshare_del(item->icon); 89 eina_stringshare_del(item->icon);
90 EINA_LIST_FREE(item->categories, d) 90 EINA_LIST_FREE(item->categories, d)
91 eina_stringshare_del(d); 91 azy_rss_category_free(d);
92 EINA_LIST_FREE(item->contributors, d) 92 EINA_LIST_FREE(item->contributors, d)
93 azy_rss_contact_free(d); 93 azy_rss_contact_free(d);
94 EINA_LIST_FREE(item->authors, d) 94 EINA_LIST_FREE(item->authors, d)
@@ -100,7 +100,6 @@ azy_rss_item_free(Azy_Rss_Item *item)
100 { 100 {
101 eina_stringshare_del(item->link); 101 eina_stringshare_del(item->link);
102 eina_stringshare_del(item->desc); 102 eina_stringshare_del(item->desc);
103 eina_stringshare_del(item->date);
104 eina_stringshare_del(item->guid); 103 eina_stringshare_del(item->guid);
105 eina_stringshare_del(item->comment_url); 104 eina_stringshare_del(item->comment_url);
106 eina_stringshare_del(item->author); 105 eina_stringshare_del(item->author);
@@ -119,8 +118,8 @@ azy_rss_item_free(Azy_Rss_Item *item)
119 * @param item The #Azy_Rss_Item (NOT NULL) 118 * @param item The #Azy_Rss_Item (NOT NULL)
120 * @return An #Eina_List of #Azy_Rss_Contact objects 119 * @return An #Eina_List of #Azy_Rss_Contact objects
121 */ 120 */
122Eina_List * 121const Eina_List *
123azy_rss_item_authors_get(Azy_Rss_Item *item) 122azy_rss_item_authors_get(const Azy_Rss_Item *item)
124{ 123{
125 if (!AZY_MAGIC_CHECK(item, AZY_MAGIC_RSS_ITEM)) 124 if (!AZY_MAGIC_CHECK(item, AZY_MAGIC_RSS_ITEM))
126 { 125 {
@@ -137,8 +136,8 @@ azy_rss_item_authors_get(Azy_Rss_Item *item)
137 * @param item The #Azy_Rss_Item (NOT NULL) 136 * @param item The #Azy_Rss_Item (NOT NULL)
138 * @return An #Eina_List of #Azy_Rss_Contact objects 137 * @return An #Eina_List of #Azy_Rss_Contact objects
139 */ 138 */
140Eina_List * 139const Eina_List *
141azy_rss_item_contributors_get(Azy_Rss_Item *item) 140azy_rss_item_contributors_get(const Azy_Rss_Item *item)
142{ 141{
143 if (!AZY_MAGIC_CHECK(item, AZY_MAGIC_RSS_ITEM)) 142 if (!AZY_MAGIC_CHECK(item, AZY_MAGIC_RSS_ITEM))
144 { 143 {
@@ -155,8 +154,8 @@ azy_rss_item_contributors_get(Azy_Rss_Item *item)
155 * @param item The #Azy_Rss_Item (NOT NULL) 154 * @param item The #Azy_Rss_Item (NOT NULL)
156 * @return An #Eina_List of #Azy_Rss_Link objects 155 * @return An #Eina_List of #Azy_Rss_Link objects
157 */ 156 */
158Eina_List * 157const Eina_List *
159azy_rss_item_links_get(Azy_Rss_Item *item) 158azy_rss_item_links_get(const Azy_Rss_Item *item)
160{ 159{
161 if (!AZY_MAGIC_CHECK(item, AZY_MAGIC_RSS_ITEM)) 160 if (!AZY_MAGIC_CHECK(item, AZY_MAGIC_RSS_ITEM))
162 { 161 {
@@ -167,14 +166,44 @@ azy_rss_item_links_get(Azy_Rss_Item *item)
167} 166}
168 167
169/** 168/**
169 * @brief Retrieve the publish date from an item object
170 *
171 * @param item The #Azy_Rss_Item (NOT NULL)
172 * @return The publish date in unixtime
173 */
174time_t
175azy_rss_item_date_get(const Azy_Rss_Item *item)
176{
177 if (!AZY_MAGIC_CHECK(item, AZY_MAGIC_RSS_ITEM))
178 {
179 AZY_MAGIC_FAIL(item, AZY_MAGIC_RSS_ITEM);
180 return 0;
181 }
182 return item->date;
183}
184
185void
186azy_rss_item_enclosure_get(const Azy_Rss_Item *item, Eina_Stringshare **url, Eina_Stringshare **content_type, size_t *length)
187{
188 if (!AZY_MAGIC_CHECK(item, AZY_MAGIC_RSS_ITEM))
189 {
190 AZY_MAGIC_FAIL(item, AZY_MAGIC_RSS_ITEM);
191 return;
192 }
193 if (url) *url = item->enclosure.url;
194 if (content_type) *content_type = item->enclosure.type;
195 if (length) *length = item->enclosure.length;
196}
197
198/**
170 * @brief Retrieve the list of categories from an item object 199 * @brief Retrieve the list of categories from an item object
171 * 200 *
172 * This function returns a list of strings belonging to @p item 201 * This function returns a list of Azy_Rss_Category belonging to @p item
173 * @param item The #Azy_Rss_Item (NOT NULL) 202 * @param item The #Azy_Rss_Item (NOT NULL)
174 * @return An #Eina_List of stringshared strings 203 * @return An #Eina_List of Azy_Rss_Category strings
175 */ 204 */
176Eina_List * 205const Eina_List *
177azy_rss_item_categories_get(Azy_Rss_Item *item) 206azy_rss_item_categories_get(const Azy_Rss_Item *item)
178{ 207{
179 if (!AZY_MAGIC_CHECK(item, AZY_MAGIC_RSS_ITEM)) 208 if (!AZY_MAGIC_CHECK(item, AZY_MAGIC_RSS_ITEM))
180 { 209 {
@@ -192,8 +221,8 @@ azy_rss_item_categories_get(Azy_Rss_Item *item)
192 @param item The #Azy_Rss object (NOT NULL) 221 @param item The #Azy_Rss object (NOT NULL)
193 @return The NAME, or NULL on failure 222 @return The NAME, or NULL on failure
194 */ \ 223 */ \
195 const char * \ 224 Eina_Stringshare * \
196 azy_rss_item_##NAME##_get(Azy_Rss_Item * item) \ 225 azy_rss_item_##NAME##_get(const Azy_Rss_Item * item) \
197 { \ 226 { \
198 if (!AZY_MAGIC_CHECK(item, AZY_MAGIC_RSS_ITEM)) \ 227 if (!AZY_MAGIC_CHECK(item, AZY_MAGIC_RSS_ITEM)) \
199 { \ 228 { \
@@ -206,7 +235,6 @@ azy_rss_item_categories_get(Azy_Rss_Item *item)
206DEF(title) 235DEF(title)
207DEF(link) 236DEF(link)
208DEF(desc) 237DEF(desc)
209DEF(date)
210DEF(guid) 238DEF(guid)
211DEF(comment_url) 239DEF(comment_url)
212DEF(author) 240DEF(author)
@@ -231,7 +259,7 @@ DEF(content_encoded)
231void 259void
232azy_rss_item_print(const char *pre, 260azy_rss_item_print(const char *pre,
233 int indent, 261 int indent,
234 Azy_Rss_Item *item) 262 const Azy_Rss_Item *item)
235{ 263{
236 int i; 264 int i;
237 Eina_List *l; 265 Eina_List *l;
@@ -314,7 +342,12 @@ azy_rss_item_print(const char *pre,
314 { 342 {
315 PRINT(link); 343 PRINT(link);
316 PRINT(desc); 344 PRINT(desc);
317 PRINT(date); 345 if (item->date)
346 {
347 for (i = 0; i < indent; i++)
348 printf("%s", pre);
349 printf("%s: %ld\n", "date", item->date);
350 }
318 PRINT(guid); 351 PRINT(guid);
319 PRINT(comment_url); 352 PRINT(comment_url);
320 PRINT(author); 353 PRINT(author);
diff --git a/src/lib/azy/azy_utils.c b/src/lib/azy/azy_utils.c
index a45044b..b0289c1 100644
--- a/src/lib/azy/azy_utils.c
+++ b/src/lib/azy/azy_utils.c
@@ -30,6 +30,32 @@
30# endif 30# endif
31#endif 31#endif
32 32
33typedef enum
34{
35 AZY_DATE_FIELD_IGNORE = -1,
36 AZY_DATE_FIELD_FAILURE = 0,
37 AZY_DATE_FIELD_TIME = (1 << 0),
38 AZY_DATE_FIELD_YEAR = (1 << 1),
39 AZY_DATE_FIELD_MONTH = (1 << 2),
40 AZY_DATE_FIELD_DAY = (1 << 3)
41} Azy_Date_Field;
42
43static const char *const months[] =
44{
45 "jan",
46 "feb",
47 "mar",
48 "apr",
49 "may",
50 "jun",
51 "jul",
52 "aug",
53 "sep",
54 "oct",
55 "nov",
56 "dec"
57};
58
33/* length of a uuid */ 59/* length of a uuid */
34#define UUID_LEN 36 60#define UUID_LEN 36
35/** 61/**
@@ -290,6 +316,181 @@ azy_util_domain_match(const char *domain, const char *match)
290 return eina_str_has_suffix(domain, match); 316 return eina_str_has_suffix(domain, match);
291} 317}
292 318
319
320static inline Eina_Bool
321_azy_date_char_is_delimiter(char c)
322{
323 return (c == 0x09) ||
324 ((c >= 0x20) && (c <= 0x2F)) ||
325 ((c >= 0x3B) && (c <= 0x40)) ||
326 ((c >= 0x5B) && (c <= 0x60)) ||
327 ((c >= 0x7B) && (c <= 0x7E))
328 ;
329}
330
331static inline Azy_Date_Field
332_azy_date_field_tokenize(char *start, char **end, Azy_Date_Field exclude)
333{
334 char *p = start;
335
336/*
337 * I feel like I should make a separate file for parsing this
338
339 http://tools.ietf.org/html/rfc6265
340 cookie-date = *delimiter date-token-list *delimiter
341 date-token-list = date-token *( 1*delimiter date-token )
342 date-token = 1*non-delimiter
343
344 delimiter = %x09 / %x20-2F / %x3B-40 / %x5B-60 / %x7B-7E
345 non-delimiter = %x00-08 / %x0A-1F / DIGIT / ":" / ALPHA / %x7F-FF
346 non-digit = %x00-2F / %x3A-FF
347
348 day-of-month = 1*2DIGIT ( non-digit *OCTET )
349 month = ( "jan" / "feb" / "mar" / "apr" /
350 "may" / "jun" / "jul" / "aug" /
351 "sep" / "oct" / "nov" / "dec" ) *OCTET
352 year = 2*4DIGIT ( non-digit *OCTET )
353 time = hms-time ( non-digit *OCTET )
354 hms-time = time-field ":" time-field ":" time-field
355 time-field = 1*2DIGIT
356 */
357 while (!_azy_date_char_is_delimiter(p[0]))
358 p++;
359 *end = p;
360 if (isdigit(start[0]))
361 {
362 switch (p - start)
363 {
364 case 1:
365 case 2:
366 if (!(exclude & AZY_DATE_FIELD_DAY))
367 return AZY_DATE_FIELD_DAY;
368 case 4:
369 if (!(exclude & AZY_DATE_FIELD_YEAR))
370 return AZY_DATE_FIELD_YEAR;
371 return AZY_DATE_FIELD_IGNORE;
372
373 case 8:
374 if (start[2] == ':') // hmstime...hopefully
375 {
376 char *pp = start;
377
378 for (p = start, pp++; isdigit(pp[0]); pp++) ;
379 if ((pp - p != 2) || (pp[0] != ':')) goto error;
380 for (pp++, p = pp; isdigit(pp[0]); pp++) ;
381 if ((pp - p != 2) || (pp[0] != ':')) goto error;
382 for (pp++, p = pp; isdigit(pp[0]); pp++) ;
383 if ((pp - p != 2) || (pp != *end)) goto error;
384 return AZY_DATE_FIELD_TIME;
385 }
386
387 default: goto error;
388 }
389 }
390 if (!(exclude & AZY_DATE_FIELD_MONTH))
391 {
392 if (p - start == 3)
393 {
394 int x;
395
396 /* lowercase here to make memcmp work later */
397 for (x = 0; x < 3; x++)
398 start[x] = tolower(start[x]);
399 return AZY_DATE_FIELD_MONTH;
400 }
401 }
402 return AZY_DATE_FIELD_IGNORE;
403error:
404 *end = strchr(start, ';');
405 return AZY_DATE_FIELD_FAILURE;
406}
407
408time_t
409azy_util_date_parse(char *start, char **end)
410{
411 Azy_Date_Field found = 0;
412 char *p;
413 struct tm tm;
414 int x;
415
416 memset(&tm, 0, sizeof(tm));
417 tm.tm_isdst = -1;
418 while (start[0] && (start[0] != ';'))
419 {
420 //eg. Wed, 11-Sep-2013 11:51:23 GMT
421 switch (_azy_date_field_tokenize(start, &p, found))
422 {
423 case AZY_DATE_FIELD_IGNORE: break;
424
425 case AZY_DATE_FIELD_TIME:
426 x = strtol(start, NULL, 10);
427 if ((x < 0) || (x > 23)) goto error;
428 tm.tm_hour = x;
429 start += 3;
430 x = strtol(start, NULL, 10);
431 if ((x < 0) || (x > 59)) goto error;
432 tm.tm_min = x;
433 start += 3;
434 x = strtol(start, NULL, 10);
435 if ((x < 0) || (x > 59)) goto error;
436 tm.tm_sec = x;
437 found |= AZY_DATE_FIELD_TIME;
438 break;
439
440 case AZY_DATE_FIELD_YEAR:
441 x = strtol(start, NULL, 10);
442 if (p - start == 2)
443 {
444 if ((x >= 70) && (x <= 99)) x += 1900;
445 else if ((x >= 0) && (x <= 69))
446 x += 2000;
447 else return 0;
448 }
449 else
450 {
451 if (x < 1601) goto error;
452 }
453 tm.tm_year = x - 1900;
454 found |= AZY_DATE_FIELD_YEAR;
455 break;
456
457 case AZY_DATE_FIELD_MONTH:
458 for (x = 0; x < 12; x++)
459 if (!memcmp(start, months[x], 3))
460 {
461 tm.tm_mon = x;
462 found |= AZY_DATE_FIELD_MONTH;
463 break;
464 }
465 break;
466
467 case AZY_DATE_FIELD_DAY:
468 x = strtol(start, NULL, 10);
469 if ((x < 1) || (x > 31)) goto error;
470 tm.tm_mday = x;
471 found |= AZY_DATE_FIELD_DAY;
472 break;
473
474 case AZY_DATE_FIELD_FAILURE:
475 default: goto error;
476 }
477 if (found ==
478 (AZY_DATE_FIELD_TIME | AZY_DATE_FIELD_YEAR | AZY_DATE_FIELD_MONTH | AZY_DATE_FIELD_DAY)
479 )
480 break;
481 start = p;
482 while (_azy_date_char_is_delimiter(start[0]))
483 start++;
484 }
485 if (found ==
486 (AZY_DATE_FIELD_TIME | AZY_DATE_FIELD_YEAR | AZY_DATE_FIELD_MONTH | AZY_DATE_FIELD_DAY)
487 )
488 return mktime(&tm);
489error:
490 if (end && start[0]) *end = strchr(start, ';');
491 return 0;
492}
493
293/** 494/**
294 * @brief strdup function which takes @c NULL 495 * @brief strdup function which takes @c NULL
295 * @param str The string to copy 496 * @param str The string to copy