diff -NaurbB castget-1.0.1/castget/castget.c castget-1.0.2/castget/castget.c
--- castget-1.0.1/castget/castget.c	2007-10-18 14:46:12.000000000 +0100
+++ castget-1.0.2/castget/castget.c	2010-01-03 14:34:13.000000000 +0000
@@ -385,6 +385,7 @@
   for (i = 0; key_list[i]; i++) {
     if (! (!strcmp(key_list[i], "url") ||
            !strcmp(key_list[i], "spool") ||
+           !strcmp(key_list[i], "filespec") ||
            !strcmp(key_list[i], "playlist") ||
            !strcmp(key_list[i], "id3leadartist") ||
            !strcmp(key_list[i], "id3contentgroup") ||
@@ -452,7 +453,8 @@
   }
 
   c = libcastget_channel_new(channel_configuration->url, channel_file, 
-                             channel_configuration->spool_directory, resume);
+                             channel_configuration->spool_directory,
+                             channel_configuration->user_filename_spec ,resume);
   g_free(channel_file);
 
   if (!c) {
diff -NaurbB castget-1.0.1/castget/configuration.c castget-1.0.2/castget/configuration.c
--- castget-1.0.1/castget/configuration.c	2005-12-09 05:53:00.000000000 +0000
+++ castget-1.0.2/castget/configuration.c	2010-01-03 14:34:24.000000000 +0000
@@ -1,5 +1,6 @@
 /*
   Copyright (C) 2005 Marius L. Jøhndal
+  Copyright (C) 2010 Tony Armitstead
  
   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
@@ -46,6 +47,9 @@
   if (c->spool_directory)
     g_free(c->spool_directory);
 
+  if (c->user_filename_spec)
+    g_free(c->user_filename_spec);
+
   if (c->playlist)
     g_free(c->playlist);
 
@@ -88,6 +92,7 @@
   /* Read keys from configuration file. */
   c->url = _read_channel_configuration_key(kf, identifier, "url");
   c->spool_directory = _read_channel_configuration_key(kf, identifier, "spool");
+  c->user_filename_spec = _read_channel_configuration_key(kf, identifier, "filespec");
   c->playlist = _read_channel_configuration_key(kf, identifier, "playlist");
   c->id3_lead_artist = _read_channel_configuration_key(kf, identifier, "id3leadartist");
   c->id3_content_group = _read_channel_configuration_key(kf, identifier, "id3contentgroup");
@@ -105,6 +110,9 @@
     if (!c->spool_directory && defaults->spool_directory)
       c->spool_directory = g_strdup(defaults->spool_directory);
 
+    if (!c->user_filename_spec && defaults->user_filename_spec)
+      c->user_filename_spec = g_strdup(defaults->user_filename_spec);
+
     if (!c->playlist && defaults->playlist)
       c->playlist = g_strdup(defaults->playlist);
 
diff -NaurbB castget-1.0.1/castget/configuration.h castget-1.0.2/castget/configuration.h
--- castget-1.0.1/castget/configuration.h	2005-12-09 05:53:00.000000000 +0000
+++ castget-1.0.2/castget/configuration.h	2010-01-03 14:34:37.000000000 +0000
@@ -28,6 +28,7 @@
   gchar *identifier;
   gchar *url;
   gchar *spool_directory;
+  gchar *user_filename_spec;
   gchar *playlist;
   gchar *id3_lead_artist;
   gchar *id3_content_group;
diff -NaurbB castget-1.0.1/configure.ac castget-1.0.2/configure.ac
--- castget-1.0.1/configure.ac	2007-11-14 16:13:00.000000000 +0000
+++ castget-1.0.2/configure.ac	2010-01-03 14:41:45.000000000 +0000
@@ -21,7 +21,7 @@
 # 
 
 AC_PREREQ(2.59)
-AC_INIT(castget, 1.0.1, mariuslj@ifi.uio.no)
+AC_INIT(castget, 1.0.2, mariuslj@ifi.uio.no)
 AM_INIT_AUTOMAKE
 AC_CONFIG_SRCDIR([castget/castget.c])
 AC_CONFIG_HEADER([config.h])
diff -NaurbB castget-1.0.1/libcastget/channel.c castget-1.0.2/libcastget/channel.c
--- castget-1.0.1/libcastget/channel.c	2007-11-13 18:49:12.000000000 +0000
+++ castget-1.0.2/libcastget/channel.c	2010-01-03 14:40:06.000000000 +0000
@@ -1,5 +1,6 @@
 /*
   Copyright (C) 2005, 2006, 2007 Marius L. Jøhndal
+  Copyright (C) 2010 Tony Armitstead
  
   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
@@ -52,8 +53,12 @@
                       (gpointer)downloadtime);
 }
 
-libcastget_channel *libcastget_channel_new(const char *url, const char *channel_file, 
-                                           const char *spool_directory, int resume)
+libcastget_channel *libcastget_channel_new(
+                                           const char *url,
+                                           const char *channel_file,
+                                           const char *spool_directory,
+                                           const char *user_filename_spec,
+                                           int resume)
 {
   libcastget_channel *c;
   xmlDocPtr doc;
@@ -64,6 +69,7 @@
   c->url = g_strdup(url);
   c->channel_filename = g_strdup(channel_file);
   c->spool_directory = g_strdup(spool_directory);
+  c->user_filename_spec = g_strdup(user_filename_spec);
   //  c->resume = resume;
   c->rss_last_fetched = NULL;
   c->downloaded_enclosures = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free);
@@ -172,6 +178,127 @@
   return f;
 }
 
+/*!
+ * Returns a string with all characters removed which may cause problems if
+ * included as part of a filename.
+ * @param filename the input filename
+ * @return a gchar* string which is the input string with risky chars removed.
+ *         This string should be freed with g_free().
+ */
+static gchar* remove_bad_filename_chars(const char* filename)
+{
+  static const gchar* bad_filename_chars = "/\\?%*:|\"<>.,'";
+  gchar** parts = g_strsplit_set(filename,  bad_filename_chars, -1);
+  gchar* good_filename = g_strjoinv(NULL, parts);
+  g_strfreev(parts);
+  return good_filename;
+}
+
+/*!
+ * Forms the field string for one of the know field names. Supported field names
+ * are:
+ *  	date 	- returns the publication date or the current date if no publication
+ *  		  date was included in the rss entry
+ *  	title	- the episode title which has been modified to remove any chars
+ *  		  which could cause problems if used as part of a filename
+ *  If an unknown field name is specified it is mapped to a single '_' character.
+ *  @param item the episode rss info
+ *  @param field_name one of the supported field names
+ */
+static gchar* form_field_string(
+                                rss_item *item,
+                                const gchar* field_name
+                                )
+{
+  if (g_ascii_strcasecmp("date", field_name) == 0) {
+    gchar str_date[20];
+    /* Try to get date from RSS feed pubDate entry */
+    GDate* rfc822_date = libcastget_parse_rfc822_date(item->pub_date);
+    if (!g_date_valid(rfc822_date)) {
+      /* If that date is bad or missing use current date */
+      GTimeVal today;
+      g_get_current_time(&today);
+      g_date_set_time_val(rfc822_date, &today);
+    }
+    g_date_strftime(str_date, sizeof(str_date), "%d-%m-%y", rfc822_date);
+    g_date_free(rfc822_date);
+    return g_strdup(str_date);
+  }
+  else if (g_ascii_strcasecmp("title", field_name) == 0) {
+    return remove_bad_filename_chars(item->title);
+  }
+  /* Unknown field string so just use an '_' */
+  return g_strnfill(1, '_');
+}
+
+/*!
+ * Forms a user specified filename for an rss episode.
+ * @param directory the directory path for the filename
+ * @param user_file_spec the pattern string which specifies how we construct
+ * 	  the filename part
+ * @param item the rss episode info
+ */
+
+#define DIM(a) sizeof(a)/sizeof(a[0])
+
+static gchar* form_userspec_filename(
+                                     const gchar* directory,
+                                     const gchar* user_file_spec,
+                                     rss_item *item
+                                     )
+{
+  enum {STATE_COPY, STATE_FIELD, STATE_DONE } state = STATE_COPY;
+  gchar* filename_parts[20];
+  int part = 0;
+  gchar fieldname[80];
+  int fieldname_idx = 0;
+  gchar* processed_filename = NULL;
+  const gchar* cp_start = user_file_spec;
+  const gchar* cp_end = user_file_spec;
+  while (state != STATE_DONE) {
+    switch (state) {
+    case STATE_COPY:
+      /* Process */
+      if (*cp_end == '\0' || *cp_end == '%') {
+        if (cp_end > cp_start)
+          filename_parts[part++] = g_strndup(cp_start, cp_end-cp_start);
+      }
+      /* State update */
+      if (*cp_end == '%') {
+        state = STATE_FIELD;
+        fieldname_idx = 0;
+      }
+      if (*cp_end == '\0')
+        state = STATE_DONE;
+      break;
+    case STATE_FIELD:
+      /* Process */
+      if (*cp_end == '\0' || *cp_end == ')') {
+        fieldname[fieldname_idx] = '\0';
+        if (part < DIM(filename_parts)-1)
+          filename_parts[part++] = form_field_string(item, fieldname);
+      }
+      else if ((*cp_end != '(') && (fieldname_idx < DIM(fieldname)-1))
+        fieldname[fieldname_idx++] = *cp_end;
+      /* State update */
+      if (*cp_end == '\0')
+        state = STATE_DONE;
+      else if (*cp_end == ')') {
+        state = STATE_COPY;
+        cp_start = cp_end + 1;
+      }
+      break;
+    case STATE_DONE:
+      break;
+    }
+    ++cp_end;
+  }
+  filename_parts[part] = NULL;
+  processed_filename = g_strjoinv(NULL, filename_parts);
+  while (part--) g_free(filename_parts[part]);
+  return g_build_filename(directory, processed_filename, NULL);
+}
+
 static int _do_download(libcastget_channel *c, libcastget_channel_info *channel_info, 
                         rss_item *item, void *user_data, libcastget_channel_callback cb, 
                         int resume)
@@ -189,6 +316,13 @@
   }
 
   /* Build enclosure file name and open file. */
+
+  /* If we have a user filename spec ... */
+  if (c->user_filename_spec)
+    /* ... we use it to construct the filename */
+    enclosure_full_filename = form_userspec_filename(c->spool_directory, c->user_filename_spec, item);
+  else
+    /* ... otherwise we user the filename we derived from the download url */
   enclosure_full_filename = g_build_filename(c->spool_directory, item->enclosure->filename, NULL);
 
   if (resume) {
@@ -207,9 +341,8 @@
   enclosure_file = fopen(enclosure_full_filename, resume_from ? "ab" : "wb");
 
   if (!enclosure_file) {
-    g_free(enclosure_full_filename);
-    
     g_fprintf(stderr, "Error opening enclosure file %s.\n", enclosure_full_filename);
+    g_free(enclosure_full_filename);
     return 1;
   }
   
diff -NaurbB castget-1.0.1/libcastget/channel.h castget-1.0.2/libcastget/channel.h
--- castget-1.0.1/libcastget/channel.h	2007-02-15 17:48:30.000000000 +0000
+++ castget-1.0.2/libcastget/channel.h	2010-01-03 14:36:16.000000000 +0000
@@ -28,6 +28,7 @@
   gchar *url;
   gchar *channel_filename;
   gchar *spool_directory;
+  gchar *user_filename_spec;
   GHashTable *downloaded_enclosures;
   gchar *rss_last_fetched;
 };
diff -NaurbB castget-1.0.1/libcastget/libcastget.h castget-1.0.2/libcastget/libcastget.h
--- castget-1.0.1/libcastget/libcastget.h	2007-02-15 17:48:30.000000000 +0000
+++ castget-1.0.2/libcastget/libcastget.h	2010-01-02 18:47:19.000000000 +0000
@@ -53,10 +53,10 @@
                                          libcastget_channel_action action, 
                                          libcastget_channel_info *channel_info, 
                                          libcastget_enclosure *enclosure,
-                                         const char *filename);
+                                         const char *user_filename_spec);
 
 libcastget_channel *libcastget_channel_new(const char *url, const char *channel_file, 
-                                           const char *spool_directory, int resume);
+                                           const char *spool_directory, const char *filename, int resume);
 void libcastget_channel_free(libcastget_channel *c);
 int libcastget_channel_update(libcastget_channel *c, void *user_data, libcastget_channel_callback cb,
                               int no_download, int no_mark_read, int first_only, int resume);
diff -NaurbB castget-1.0.1/libcastget/rss.c castget-1.0.2/libcastget/rss.c
--- castget-1.0.1/libcastget/rss.c	2007-02-15 17:55:18.000000000 +0000
+++ castget-1.0.2/libcastget/rss.c	2010-01-03 12:27:06.000000000 +0000
@@ -64,6 +64,7 @@
   f->items[i]->title = _dup_child_node_value(node, "title");
   f->items[i]->link = _dup_child_node_value(node, "link");
   f->items[i]->description = _dup_child_node_value(node, "description");
+  f->items[i]->pub_date = _dup_child_node_value(node, "pubDate");
 
   /* Look for mrss information first, if there is any. It may be
      located either directly under the "item" tag, or inside an mrss
@@ -191,8 +192,8 @@
       entity = (xmlEntityPtr)g_new0(xmlEntity, 1);
       entity->type = XML_ENTITY_DECL;
       entity->name = name;
-      entity->orig = contents;
-      entity->content = contents;
+      entity->orig = (unsigned char*)contents;
+      entity->content = (unsigned char*)contents;
       entity->length = g_utf8_strlen(contents, -1);
       entity->etype = XML_INTERNAL_PREDEFINED_ENTITY;
     }
@@ -297,6 +298,9 @@
     if (item->title)
       free(item->title);
 
+    if (item->pub_date)
+      free(item->pub_date);
+
     free(item);
   }
 
diff -NaurbB castget-1.0.1/libcastget/rss.h castget-1.0.2/libcastget/rss.h
--- castget-1.0.1/libcastget/rss.h	2007-01-24 21:28:05.000000000 +0000
+++ castget-1.0.2/libcastget/rss.h	2010-01-03 16:32:51.000000000 +0000
@@ -1,5 +1,5 @@
 /*
-  Copyright (C) 2005, 2007 Marius L. J�hndal
+  Copyright (C) 2005, 2007 Marius L. Jøhndal
  
   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
@@ -28,6 +28,7 @@
   char *title;
   char *link;
   char *description;
+  char *pub_date;
   libcastget_enclosure *enclosure;
 } rss_item;
 
diff -NaurbB castget-1.0.1/libcastget/utils.c castget-1.0.2/libcastget/utils.c
--- castget-1.0.1/libcastget/utils.c	2007-11-13 19:10:54.000000000 +0000
+++ castget-1.0.2/libcastget/utils.c	2010-01-03 14:38:48.000000000 +0000
@@ -26,6 +26,9 @@
 #include <errno.h>
 #include <unistd.h>
 #include <string.h>
+#include <math.h>
+#include <stdio.h>
+#include <ctype.h>
 #include <glib/gprintf.h>
 #include <glib/gstdio.h>
 #include "utils.h"
@@ -118,6 +121,55 @@
     return NULL;
 }
 
+static const char *dayStr[7] = { "Sun","Mon","Tue","Wed","Thu","Fri","Sat" };
+static const char *monthStr[12] = { "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec" };
+
+GDate* libcastget_parse_rfc822_date(const char *rfc822_date_str)
+{
+  GDate* rfc822_date = g_date_new(); /* Create invalid date */
+  const char* dstr = rfc822_date_str;
+  int i = 0;
+
+  while (isspace(*dstr)) dstr++;
+  if (*dstr == '\0') return rfc822_date;
+
+  /* Skip past any valid day, field */
+  for (i=0; i < sizeof(dayStr)/sizeof(dayStr[0]); i++) {
+    if (strncmp(dstr, dayStr[i], 3) == 0)
+      break;
+  }
+  if (i < sizeof(dayStr)/sizeof(dayStr[0])) {
+    dstr += 3;
+    while (isspace(*dstr)) dstr++;
+    if (*dstr == '\0') return rfc822_date;
+    if (*dstr == ',') dstr++;
+    while (isspace(*dstr)) dstr++;
+    if (*dstr == '\0') return rfc822_date;
+  }
+  /* Decode day, month, year */
+  int day;
+  int month;
+  int year;
+  char mStr[20];
+  if (sscanf(dstr, "%d %s %d", &day, mStr, &year) != 3)
+    return rfc822_date;
+  for (i=0; i < sizeof(monthStr)/sizeof(monthStr[0]); i++) {
+    if (strncmp(mStr, monthStr[i], 3) == 0)
+      break;
+  }
+  if (i == sizeof(monthStr)/sizeof(monthStr[0])) return rfc822_date;
+  month = i+1;
+  if (year < 1900) {
+    if (year < 50)
+      year += 2000;
+    else
+      year += 1900;
+  }
+  g_date_set_dmy(rfc822_date, day, month, year);
+  return rfc822_date;
+}
+
+
 /* 
    Local Variables:
    mode:c
diff -NaurbB castget-1.0.1/libcastget/utils.h castget-1.0.2/libcastget/utils.h
--- castget-1.0.1/libcastget/utils.h	2005-11-13 23:01:27.000000000 +0000
+++ castget-1.0.2/libcastget/utils.h	2010-01-02 23:23:12.000000000 +0000
@@ -31,6 +31,8 @@
                                        **used_filename);
 gchar *libcastget_get_rfc822_time(void);
 
+GDate* libcastget_parse_rfc822_date(const char *rfc822_date_str);
+
 #endif /* UTILS_H */
 
 /* 
