Index: modules/aggregator/aggregator.api.php
===================================================================
RCS file: /cvs/drupal/drupal/modules/aggregator/aggregator.api.php,v
retrieving revision 1.2
diff -u -r1.2 aggregator.api.php
--- modules/aggregator/aggregator.api.php	26 Jan 2009 14:08:42 -0000	1.2
+++ modules/aggregator/aggregator.api.php	15 Mar 2009 02:17:39 -0000
@@ -82,6 +82,14 @@
  *   $feed->source_string contains the raw feed data as a string. Parse data
  *   from $feed->source_string and expose it to other modules as an array of
  *   data items on $feed->items.
+ * 
+ *   Feed format:
+ *   - $feed->description (string) - description of the feed
+ *   - $feed->image (string) - image for the feed
+ *   - $feed->etag (string) - value of feed's entity tag header field
+ *   - $feed->modified (UNIX timestamp) - value of feed's last modified header 
+ *     field
+ *   - $feed->items (Array) - array of feed items.
  *
  *   By convention, the common format for a single feed item is:
  *   $item[key-name] = value;
@@ -93,6 +101,9 @@
  *   AUTHOR (string) - the feed item's author
  *   GUID (string) - RSS/Atom global unique identifier
  *   LINK (string) - the feed item's URL
+ * 
+ * @return 
+ *   TRUE if parsing was successful, FALSE otherwise.
  *
  * @see hook_aggregator_parse_info()
  * @see hook_aggregator_fetch()
@@ -101,7 +112,11 @@
  * @ingroup aggregator
  */
 function hook_aggregator_parse($feed) {
-  $feed->items = mymodule_parse($feed->source_string);
+  if ($items = mymodule_parse($feed->source_string)) {
+    $feed->items = $items;
+    return TRUE;
+  }
+  return FALSE;  
 }
 
 /**
Index: modules/aggregator/aggregator.fetcher.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/aggregator/aggregator.fetcher.inc,v
retrieving revision 1.4
diff -u -r1.4 aggregator.fetcher.inc
--- modules/aggregator/aggregator.fetcher.inc	26 Jan 2009 14:08:42 -0000	1.4
+++ modules/aggregator/aggregator.fetcher.inc	15 Mar 2009 02:17:39 -0000
@@ -46,7 +46,6 @@
       break;
     case 301:
       $feed->url = $result->redirect_url;
-      $feed->redirected = TRUE;
       // Do not break here.
     case 200:
     case 302:
Index: modules/aggregator/aggregator.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/aggregator/aggregator.module,v
retrieving revision 1.405
diff -u -r1.405 aggregator.module
--- modules/aggregator/aggregator.module	1 Mar 2009 07:21:02 -0000	1.405
+++ modules/aggregator/aggregator.module	15 Mar 2009 02:17:39 -0000
@@ -530,6 +530,9 @@
  *   An object describing the feed to be refreshed.
  */
 function aggregator_refresh($feed) {
+  // Store feed URL to track changes.
+  $feed_url = $feed->url;
+  
   // Fetch the feed.
   $fetcher = variable_get('aggregator_fetcher', 'aggregator');
   module_invoke($fetcher, 'aggregator_fetch', $feed);
@@ -537,13 +540,36 @@
   if ($feed->source_string !== FALSE) {
     // Parse the feed.
     $parser = variable_get('aggregator_parser', 'aggregator');
-    module_invoke($parser, 'aggregator_parse', $feed);
+    if (module_invoke($parser, 'aggregator_parse', $feed)) {
 
-    // If there are items on the feed, let all enabled processors do their work on it.
-    if (@count($feed->items)) {
-      $processors = variable_get('aggregator_processors', array('aggregator'));
-      foreach ($processors as $processor) {
-        module_invoke($processor, 'aggregator_process', $feed);
+      // Update feed with parsed data.
+      db_merge('aggregator_feed')
+        ->key(array('fid' => $feed->fid))
+        ->fields(array(
+          'url' => $feed->url,
+          'checked' => REQUEST_TIME,
+          'link' => empty($feed->link) ? $feed->url : $feed->link,
+          'description' => empty($feed->description) ? '' : $feed->description,
+          'image' => empty($feed->image) ? '' : $feed->image,
+          'hash' => md5($feed->source_string),
+          'etag' => empty($feed->etag) ? '' : $feed->etag,
+          'modified' => empty($feed->modified) ? '' : $feed->modified,
+        ))
+        ->execute();
+      // Log if feed URL has changed.
+      if ($feed->url != $feed_url) {
+        watchdog('aggregator', 'Updated URL for feed %title to %url.', array('%title' => $feed->title, '%url' => $feed->url));
+      }
+
+      watchdog('aggregator', 'There is new syndicated content from %site.', array('%site' => $feed->title));
+      drupal_set_message(t('There is new syndicated content from %site.', array('%site' => $feed->title)));
+
+      // If there are items on the feed, let all enabled processors do their work on it.
+      if (@count($feed->items)) {
+        $processors = variable_get('aggregator_processors', array('aggregator'));
+        foreach ($processors as $processor) {
+          module_invoke($processor, 'aggregator_process', $feed);
+        }
       }
     }
   }
Index: modules/aggregator/aggregator.parser.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/aggregator/aggregator.parser.inc,v
retrieving revision 1.1
diff -u -r1.1 aggregator.parser.inc
--- modules/aggregator/aggregator.parser.inc	22 Dec 2008 19:38:31 -0000	1.1
+++ modules/aggregator/aggregator.parser.inc	15 Mar 2009 02:17:39 -0000
@@ -44,32 +44,20 @@
     }
 
     $etag = empty($feed->http_headers['ETag']) ? '' : $feed->http_headers['ETag'];
-    // Update the feed data.
-    db_merge('aggregator_feed')
-      ->key(array('fid' => $feed->fid))
-      ->fields(array(
-        'url' => $feed->url,
-        'checked' => REQUEST_TIME,
-        'link' => !empty($channel['LINK']) ? $channel['LINK'] : '',
-        'description' => !empty($channel['DESCRIPTION']) ? $channel['DESCRIPTION'] : '',
-        'image' => $image,
-        'hash' => md5($feed->source_string),
-        'etag' => $etag,
-        'modified' => $modified,
-      ))
-      ->execute();
+
+    // Add parsed data to the feed object.
+    $feed->link = !empty($channel['LINK']) ? $channel['LINK'] : '';
+    $feed->description = !empty($channel['DESCRIPTION']) ? $channel['DESCRIPTION'] : '';
+    $feed->image = $image;
+    $feed->etag = $etag;
+    $feed->modified = $modified;
 
     // Clear the cache.
     cache_clear_all();
 
-    if (isset($feed->redirected)) {
-      watchdog('aggregator', 'Updated URL for feed %title to %url.', array('%title' => $feed->title, '%url' => $feed->url));
-    }
-
-    watchdog('aggregator', 'There is new syndicated content from %site.', array('%site' => $feed->title));
-    drupal_set_message(t('There is new syndicated content from %site.', array('%site' => $feed->title)));
-
+    return TRUE;
   }
+  return FALSE;
 }
 
 /**
