Index: podcast/rb-podcast-parse.c
===================================================================
RCS file: /cvs/gnome/rhythmbox/podcast/rb-podcast-parse.c,v
retrieving revision 1.30
diff -u -r1.30 rb-podcast-parse.c
--- podcast/rb-podcast-parse.c	14 Jul 2006 12:28:24 -0000	1.30
+++ podcast/rb-podcast-parse.c	13 Sep 2006 22:43:02 -0000
@@ -54,19 +54,30 @@
 		RB_PODCAST_PARSER_STATE_IMG_PROPERTY,
 		RB_PODCAST_PARSER_STATE_ITEM,
 		RB_PODCAST_PARSER_STATE_ITEM_PROPERTY,
+		RB_PODCAST_PARSER_STATE_ATOM_FEED,
+		RB_PODCAST_PARSER_STATE_ATOM_FEED_PROPERTY,
+		RB_PODCAST_PARSER_STATE_ATOM_ENTRY,
+		RB_PODCAST_PARSER_STATE_ATOM_ENTRY_PROPERTY,
+		RB_PODCAST_PARSER_STATE_ATOM_ENTRY_AUTHOR,
+		RB_PODCAST_PARSER_STATE_ATOM_ENTRY_AUTHOR_PROPERTY,
 		RB_PODCAST_PARSER_STATE_END,
 	} state;
 };
 
 static gboolean rb_validate_channel_propert (const char *name);
 static gboolean rb_validate_item_propert (const char *name);
+static gboolean rb_validate_atom_feed_propert (const char *name);
+static gboolean rb_validate_atom_entry_propert (const char *name);
 static uintmax_t rb_podcast_parse_date (const char* date_str);
+static uintmax_t rb_podcast_parse_atom_date (const char* date_str);
 static gulong rb_podcast_parse_time (const char *time_str);
 static void rb_podcast_parser_start_element (struct RBPoadcastLoadContext* ctx, const char *name, const char **attrs);
 static void rb_podcast_parser_end_element (struct RBPoadcastLoadContext* ctx, const char *name);
 static void rb_podcast_parser_characters (struct RBPoadcastLoadContext* ctx, const char *data, guint len);
 static void rb_set_channel_value (struct RBPoadcastLoadContext* ctx, const char* name, const char* value);
 static void rb_set_item_value (struct RBPoadcastLoadContext* ctx, const char* name, const char* value);
+static void rb_set_atom_feed_value (struct RBPoadcastLoadContext* ctx, const char* name, const char* value);
+static void rb_set_atom_entry_value (struct RBPoadcastLoadContext* ctx, const char* name, const char* value);
 
 static RBPodcastItem *
 rb_podcast_initializa_item ()
@@ -153,6 +164,82 @@
 }
 
 static void
+rb_set_atom_feed_value (struct RBPoadcastLoadContext *ctx,
+			const char *name,
+			const char *value)
+{
+	xmlChar *dvalue;
+
+	if (value == NULL)
+		return;
+
+	if (name == NULL)
+		return;
+
+	dvalue = xmlCharStrdup (value);
+	g_strstrip ((char *)dvalue);
+   
+	if (!strcmp (name, "title")) {
+		ctx->channel_data->title = dvalue;
+	} else if (!strcmp (name, "subtitle")) {
+		ctx->channel_data->subtitle = dvalue;
+	} else if (!strcmp (name, "tagline")) {
+		ctx->channel_data->description = dvalue;
+	} else if (!strcmp (name, "generator")) {
+		if (ctx->channel_data->author == NULL)
+			ctx->channel_data->author = dvalue;
+	} else if (!strcmp (name, "icon")) {
+		ctx->channel_data->img = dvalue;
+	} else if (!strcmp (name, "updated")) {
+		ctx->channel_data->pub_date = rb_podcast_parse_atom_date ((char *)dvalue);
+		g_free (dvalue);
+	} else if (!strcmp (name, "modified")) {
+		if (!ctx->channel_data->pub_date) {
+			ctx->channel_data->pub_date = rb_podcast_parse_atom_date ((char *)dvalue);
+			g_free (dvalue);
+		}
+	} else if (!strcmp (name, "rights")) {
+		ctx->channel_data->copyright = dvalue;
+	} else if (!strcmp (name, "copyright")) {
+		if (ctx->channel_data->copyright == NULL)
+			ctx->channel_data->copyright = dvalue;
+	} else {
+		g_free (dvalue);
+	}
+}
+
+static void
+rb_set_atom_entry_value (struct RBPoadcastLoadContext *ctx,
+			 const char *name,
+			 const char *value)
+{
+	xmlChar *dvalue;
+
+	dvalue = xmlCharStrdup (value);
+	g_strstrip ((char *)dvalue);
+
+	if (!strcmp (name, "title")) {
+		ctx->item_data->title = dvalue;
+	} else if (!strcmp (name, "summary")) {
+		ctx->item_data->description = dvalue;
+	} else if (!strcmp (name, "updated")) {
+		ctx->item_data->pub_date = rb_podcast_parse_atom_date ((char *)dvalue);
+		g_free (dvalue);
+	} else if (!strcmp (name, "modified")) {
+		if (!ctx->item_data->pub_date) {
+			ctx->item_data->pub_date = rb_podcast_parse_atom_date ((char *)dvalue);
+			g_free (dvalue);
+		}
+	} else if (!strcmp (name, "href")) {
+		ctx->item_data->url = dvalue;
+	} else if (!strcmp (name, "length")) {
+		ctx->item_data->filesize = g_ascii_strtoull ((char *)dvalue, NULL, 10);
+	} else {
+		g_free (dvalue);
+	}
+}
+
+static void
 rb_insert_item (struct RBPoadcastLoadContext *ctx)
 {
 	RBPodcastItem *data = ctx->item_data;
@@ -212,6 +299,67 @@
 	}
 }
 
+static gboolean
+rb_validate_atom_feed_propert (const char *name)
+{
+	if (name == NULL) {
+		return FALSE;
+	}
+
+	if (!strcmp (name, "category") ||
+	    !strcmp (name, "contributor") ||
+	    !strcmp (name, "generator") ||
+	    !strcmp (name, "icon") ||
+	    !strcmp (name, "id") ||
+	    !strcmp (name, "link") ||
+	    !strcmp (name, "logo") ||
+	    !strcmp (name, "rights") ||
+	    !strcmp (name, "subtitle") ||
+	    !strcmp (name, "title") ||
+	    !strcmp (name, "updated") ||
+	    /* Atom 0.3 elements */
+	    !strcmp (name, "copyright") ||
+	    !strcmp (name, "info") ||
+	    !strcmp (name, "modified") ||
+	    !strcmp (name, "tagline")) {
+
+		return TRUE;
+	} else {
+		return FALSE;
+	}
+
+}
+
+static gboolean
+rb_validate_atom_entry_propert (const char *name)
+{
+	if (name == NULL) {
+		return FALSE;
+	}
+
+	if (!strcmp (name, "author") ||
+	    !strcmp (name, "category") ||
+	    !strcmp (name, "content") ||
+	    !strcmp (name, "contributor") ||
+	    !strcmp (name, "id") ||
+	    !strcmp (name, "link") ||
+	    !strcmp (name, "published") ||
+	    !strcmp (name, "rights") ||
+	    !strcmp (name, "source") ||
+	    !strcmp (name, "summary") ||
+	    !strcmp (name, "title") ||
+	    !strcmp (name, "updated") ||
+	    /* Atom 0.3 elements */
+	    !strcmp (name, "created") ||
+	    !strcmp (name, "issued") ||
+	    !strcmp (name, "modified")) {
+	    
+		return TRUE;
+	} else {
+		return FALSE;
+	}
+}
+
 static void
 rb_podcast_parser_start_element (struct RBPoadcastLoadContext *ctx,
 				 const char *name,
@@ -225,6 +373,8 @@
 		{
 			if (!strcmp (name, "rss")) {
 				ctx->state = RB_PODCAST_PARSER_STATE_RSS;
+			} else if (!strcmp (name, "feed")) {
+				ctx->state = RB_PODCAST_PARSER_STATE_ATOM_FEED;
 			} else {
 				ctx->in_unknown_elt++;
 			}
@@ -297,9 +447,73 @@
 			break;
 		}
 
+	case RB_PODCAST_PARSER_STATE_ATOM_FEED:
+		{
+			if (!strcmp (name, "entry")) {
+				ctx->item_data = rb_podcast_initializa_item ();
+				ctx->state = RB_PODCAST_PARSER_STATE_ATOM_ENTRY;
+			} else if (!rb_validate_atom_feed_propert (name)) {
+				rb_debug ("Unknown property");
+				ctx->in_unknown_elt++;
+			} else {
+				ctx->state = RB_PODCAST_PARSER_STATE_ATOM_FEED_PROPERTY;
+			}
+
+			break;
+		}
+
+	case RB_PODCAST_PARSER_STATE_ATOM_ENTRY:
+		{
+			if (!strcmp (name, "author")) {
+				ctx->state = RB_PODCAST_PARSER_STATE_ATOM_ENTRY_AUTHOR;
+			} else if (!strcmp (name, "link")) {
+				const char *rel_value, *href_value, *length_value;
+				rel_value = href_value = length_value = NULL;
+
+				for (; *attrs; attrs +=2) {
+					if (!strcmp (*attrs, "rel")) {
+						rel_value = *(attrs + 1);
+					} else if (!strcmp (*attrs, "href")) {
+						href_value = *(attrs + 1);
+					} else if (!strcmp (*attrs, "length")) {
+						length_value = *(attrs + 1);
+					}
+				}
+
+				if (rel_value && href_value && length_value &&
+				    !strcmp (rel_value, "enclosure")) {
+					rb_set_atom_entry_value (ctx, "href", href_value);
+					rb_set_atom_entry_value (ctx, "length", length_value);
+				}
+
+				ctx->state = RB_PODCAST_PARSER_STATE_ATOM_ENTRY_PROPERTY;
+
+			} else if (!rb_validate_atom_entry_propert (name)) {
+				ctx->in_unknown_elt++;
+			} else {
+				ctx->state = RB_PODCAST_PARSER_STATE_ATOM_ENTRY_PROPERTY;
+			}
+
+			break;
+		}
+
+	case RB_PODCAST_PARSER_STATE_ATOM_ENTRY_AUTHOR:
+		{
+			if (!strcmp (name, "name")) {
+				ctx->state = RB_PODCAST_PARSER_STATE_ATOM_ENTRY_AUTHOR_PROPERTY;
+			} else {
+				ctx->in_unknown_elt++;
+			}
+
+			break;
+		}
+
         case RB_PODCAST_PARSER_STATE_CHANNEL_PROPERTY:
         case RB_PODCAST_PARSER_STATE_ITEM_PROPERTY:
         case RB_PODCAST_PARSER_STATE_IMG_PROPERTY:
+	case RB_PODCAST_PARSER_STATE_ATOM_FEED_PROPERTY:
+	case RB_PODCAST_PARSER_STATE_ATOM_ENTRY_PROPERTY:
+	case RB_PODCAST_PARSER_STATE_ATOM_ENTRY_AUTHOR_PROPERTY:
 		rb_debug ("nested element inside property; treating as unknown");
 		ctx->in_unknown_elt++;
 		break;
@@ -372,6 +586,45 @@
 		ctx->state = RB_PODCAST_PARSER_STATE_CHANNEL;
 		break;
 
+	case RB_PODCAST_PARSER_STATE_ATOM_FEED:
+		ctx->state = RB_PODCAST_PARSER_STATE_START;
+		break;
+
+	case RB_PODCAST_PARSER_STATE_ATOM_FEED_PROPERTY:
+		{
+			rb_set_atom_feed_value (ctx, name, ctx->prop_value->str);
+			ctx->state = RB_PODCAST_PARSER_STATE_ATOM_FEED;
+			g_string_truncate (ctx->prop_value, 0);
+			break;
+		}
+
+	case RB_PODCAST_PARSER_STATE_ATOM_ENTRY:
+		{
+			rb_insert_item (ctx);
+			ctx->state = RB_PODCAST_PARSER_STATE_ATOM_FEED;
+			break;
+		}
+
+	case RB_PODCAST_PARSER_STATE_ATOM_ENTRY_PROPERTY:
+		{
+			rb_set_atom_entry_value (ctx, name, ctx->prop_value->str);
+			ctx->state = RB_PODCAST_PARSER_STATE_ATOM_ENTRY;
+			g_string_truncate (ctx->prop_value, 0);
+			break;
+		}
+
+	case RB_PODCAST_PARSER_STATE_ATOM_ENTRY_AUTHOR:
+		ctx->state = RB_PODCAST_PARSER_STATE_ATOM_ENTRY;
+		break;
+
+	case RB_PODCAST_PARSER_STATE_ATOM_ENTRY_AUTHOR_PROPERTY:
+		{
+			rb_set_atom_entry_value (ctx, "author", ctx->prop_value->str);
+			ctx->state = RB_PODCAST_PARSER_STATE_ATOM_ENTRY_AUTHOR;
+			g_string_truncate (ctx->prop_value, 0);
+			break;
+		}
+
         case RB_PODCAST_PARSER_STATE_END:
 		break;
 
@@ -390,6 +643,9 @@
         case RB_PODCAST_PARSER_STATE_CHANNEL_PROPERTY:
         case RB_PODCAST_PARSER_STATE_ITEM_PROPERTY:
         case RB_PODCAST_PARSER_STATE_IMG_PROPERTY:
+	case RB_PODCAST_PARSER_STATE_ATOM_FEED_PROPERTY:
+	case RB_PODCAST_PARSER_STATE_ATOM_ENTRY_PROPERTY:
+	case RB_PODCAST_PARSER_STATE_ATOM_ENTRY_AUTHOR_PROPERTY:
 		g_string_append_len (ctx->prop_value, data, len);
            	break;
         case RB_PODCAST_PARSER_STATE_START:
@@ -397,6 +653,9 @@
         case RB_PODCAST_PARSER_STATE_RSS:
         case RB_PODCAST_PARSER_STATE_CHANNEL:
         case RB_PODCAST_PARSER_STATE_ITEM:
+	case RB_PODCAST_PARSER_STATE_ATOM_FEED:
+	case RB_PODCAST_PARSER_STATE_ATOM_ENTRY:
+	case RB_PODCAST_PARSER_STATE_ATOM_ENTRY_AUTHOR:
         case RB_PODCAST_PARSER_STATE_END:
 		break;
 	default:
@@ -420,7 +679,9 @@
 
 	data->url = xmlCharStrdup (file_name);
 
-	if (!g_str_has_suffix (file_name, ".rss") && !g_str_has_suffix (file_name, ".xml")) {
+	if (!g_str_has_suffix (file_name, ".rss") &&
+	    !g_str_has_suffix (file_name, ".xml") &&
+	    !g_str_has_suffix (file_name, ".atom") ) {
 		gboolean invalid_mime_type;
 
 		info = gnome_vfs_file_info_new ();
@@ -602,6 +863,22 @@
 
 	if (result == NULL) {
 		memset (&tm, 0, sizeof (struct tm));
+		rb_debug ("unable to convert date string %s", date_str);
+	}
+
+	return (uintmax_t) mktime (&tm);
+}
+
+static uintmax_t
+rb_podcast_parse_atom_date (const char *date_str)
+{
+	struct tm tm;
+	char *result;
+
+	memset (&tm, 0, sizeof (struct tm));	
+	result = strptime (date_str, "%Y-%m-%dT%T%z", &tm);
+	if (result == NULL) {
+		memset (&tm, 0, sizeof (struct tm));	
 		rb_debug ("unable to convert date string %s", date_str);
 	}
 
