Index: drupal/includes/common.inc
diff -u drupal/includes/common.inc:1.1.2.3 drupal/includes/common.inc:1.1.2.3.2.4
--- drupal/includes/common.inc:1.1.2.3	Mon Apr  5 13:09:44 2004
+++ drupal/includes/common.inc	Sun Apr 11 20:02:23 2004
@@ -746,22 +746,125 @@
   return $output;
 }
 
-function format_rss_item($title, $link, $description, $args = array()) {
-  // arbitrary elements may be added using the $args associative array
-
-  $output = "<item>\n";
-  $output .= " <title>". drupal_specialchars(strip_tags($title)) ."</title>\n";
-  $output .= " <link>". drupal_specialchars(strip_tags($link)) ."</link>\n";
-  $output .= " <description>". drupal_specialchars(check_output($description)) ."</description>\n";
-  foreach ($args as $key => $value) {
-    $output .= "<$key>". drupal_specialchars(strip_tags($value)) ."</$key>";
+function format_rss_item(&$namespaces, $node, $version) {
+  $xml['item']->contents['title']->contents = drupal_specialchars(strip_tags($node->title));
+  $xml['item']->contents['link']->contents = drupal_specialchars(strip_tags(url("node/view/$node->nid", NULL, NULL, 1)));
+  $xml['item']->contents['description']->contents = drupal_specialchars(check_output($node->teaser ? $node->teaser : $node->body));
+  $xml['item']->contents['pubDate']->contents = date('r', $node->changed);
+
+  foreach (module_list() as $name) {
+    $function = $name ."_rss_item";
+    if (function_exists($function)) {
+      $function($xml['item']->contents, $namespaces, $node, $version);
+    }
   }
-  $output .= "</item>\n";
+  
+  return format_xml($xml);
+}
 
+/**
+ * Formats an associative array of XML tags as an xml document. Each array key
+ * is the name (including abbreviated namespace) of a top level tag. The array
+ * values are objects containing the attributes and contents of the xml tag.
+ * The object format is described in detail in the documentation for the
+ * format_xml_tag function.
+ *
+ * @param $xml An associative array of tag name => xml tag as object
+ * @return String of indented XML
+ */
+function format_xml($xml) {
+  foreach ($xml as $tag => $content) {
+    $output .= format_xml_tag($content, $tag);
+  }
   return $output;
 }
 
 /**
+ * An xml tag contains two important things, attributes and contents, these are
+ * the two variables in an xml tag object as processed by this function. Either
+ * or both may be omitted if applicable.
+ *
+ * $xml->attributes is an associative array of attribute names as keys with
+ * attribute values as values.
+ * $xml->contents can either be a string or array of tags. If the tag only
+ * contains a string then use a string. If it is an array of tags, then
+ * contents will be an associative array with tag names as keys. The values of
+ * the associative array are other objects constructed in the same way if there
+ * is only one tag with that name; if there are multiples then the value will
+ * be a simple array of objects.
+ *
+ * This function makes no attempt to assure valid xml output, if the input is
+ * not well formed, then the output won't be well formed either. Use proper
+ * entity coding where necessary, especially for angle brackets, ampersands,
+ * and double quotes.
+ *
+ * @param $xml An object structured to represent an XML tag as described above.
+ * @param $name The name of the represented XML tag.
+ * @param $indent The base indentation of the tag as a string of whitespace.
+ * @return A string containing the XML tag in XML format.
+ */
+function format_xml_tag($xml, $name, $indent = '') {
+  $result = $indent .'<'. $name;
+  if ($xml->attributes) {
+    foreach ($xml->attributes as $attribute => $value) {
+      $result .= ' '. $attribute .'="'. $value .'"';
+    }
+  }
+  if (is_array($xml->contents)) {
+    $result .= ">\n";
+    foreach ($xml->contents as $tag => $content) {
+      if (is_array($content)) {
+        foreach($content as $content_value) {
+          $result .= format_xml_tag($content_value, $tag, ' '. $indent);
+        }
+      }
+      else {
+        $result .= format_xml_tag($content, $tag, ' '. $indent);
+      }
+    }
+    $result .= $indent .'</'. $name .'>';
+  }
+  else if ($xml->contents) {
+    $result .= '>'. $xml->contents .'</'. $name .'>';
+  }
+  else {
+    $result .= ' />';
+  }
+  return $result ."\n";
+}
+
+/**
+ * Formats a date accorfing to the W3C pecification at
+ * http://www.w3.org/TR/NOTE-datetime
+ *
+ * @param $timestamp The exact date to format.
+ * @param $precision either 'year', 'month', 'day', or 'time'.
+ * @param $timezone Timezone offset in seconds in case the user timezone
+ *   should not be used.
+ * @return The W3C formatted date with the requested percision.
+ */
+function format_w3c_date($timestamp, $precision = 'time', $timzone = NULL) {
+  if ($timezone === NULL) {
+    global $user;
+    $timezone = $user->uid ? $user->timezone : variable_get('date_default_timezone', 0);
+  }
+  $timestamp += $timezone;
+
+  switch ($precision) {
+    case 'time':
+      $result = 'T'. gmdate('H', $timestamp) .':'. gmdate('i', $timestamp) . sprintf('%s%02d:%02d', ($timezone < 0 ? '-' : '+'), abs($timezone / 3600), abs($timezone % 3600) / 60) . $result;
+    case 'day':
+      $result = '-'. gmdate('d', $timestamp) . $result;
+    case 'month':
+      $result = '-'. gmdate('m', $timestamp) . $result;
+    case 'year':
+      $result = gmdate('Y', $timestamp) . $result;
+  }
+
+  return $result;
+}
+
+/**
  * Formats a string with a count of items so that the string is pluralized
  * correctly. format_plural calls t() by itself, make sure not to pass already
  * localized strings to it.
Index: drupal/modules/node.module
diff -u drupal/modules/node.module:1.1.2.4 drupal/modules/node.module:1.1.2.4.2.4
--- drupal/modules/node.module:1.1.2.4	Wed Apr  7 09:25:26 2004
+++ drupal/modules/node.module	Mon Apr 12 12:55:37 2004
@@ -1002,18 +1002,8 @@
     $nodes = db_query_range('SELECT nid FROM {node} WHERE promote = 1 AND status = 1 ORDER BY created DESC', 0, 15);
   }
 
-  while ($node = db_fetch_object($nodes)) {
-    /*
-    ** Load the specified node:
-    */
-
-    $item = node_load(array('nid' => $node->nid));
-    $link = url("node/view/$node->nid", NULL, NULL, 1);
-    $items .= format_rss_item($item->title, $link, ($item->teaser ? $item->teaser : $item->body), array('pubDate' => date('r', $item->changed)));
-  }
-
   $channel_defaults = array(
-    'version'     => '0.92',
+    'version'     => '2.0',
     'title'       => variable_get('site_name', 'drupal') .' - '. variable_get('site_slogan', ''),
     'link'        => $base_url,
     'description' => variable_get('site_mission', ''),
@@ -1021,9 +1011,18 @@
   );
   $channel = array_merge($channel_defaults, $channel);
 
+  $namespaces = array();
+  while ($node = db_fetch_object($nodes)) {
+    $items .= format_rss_item($namespaces, node_load(array('nid' => $node->nid)), $channel["version"]);
+  }
+
   $output = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
   $output .= "<!DOCTYPE rss [<!ENTITY % HTMLlat1 PUBLIC \"-//W3C//ENTITIES Latin 1 for XHTML//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml-lat1.ent\">]>\n";
-  $output .= "<rss version=\"". $channel["version"] . "\" xml:base=\"". $base_url ."\">\n";
+  $output .= "<rss version=\"". $channel["version"] . "\" xml:base=\"". $base_url ."\"";
+  foreach ($namespaces as $ns => $url) {
+    $output .= " xmlns:$ns=\"$url\"";
+  }
+  $output .= ">\n";
   $output .= format_rss_channel($channel['title'], $channel['link'], $channel['description'], $items, $channel['language']);
   $output .= "</rss>\n";
 
