diff -urN feedapi_itemfilter.orig/category_filter/feedapi_category_filter.info feedapi_itemfilter/category_filter/feedapi_category_filter.info
--- feedapi_itemfilter.orig/category_filter/feedapi_category_filter.info	1970-01-01 01:00:00.000000000 +0100
+++ feedapi_itemfilter/category_filter/feedapi_category_filter.info	2009-10-15 15:20:56.000000000 +0100
@@ -0,0 +1,20 @@
+name = FeedAPI category filter
+description = "Filters feed items based on keywords. Only these feed items, that have matched keyword(s) in the feed item description field will be saved."
+core = 6.x
+package = "FeedAPI Filter"
+version = 6.x-1.0-beta
+dependencies[] = feedapi 
+dependencies[] = feedapi_itemfilter
+
+; Information added by drupal.org packaging script on 2008-05-28
+version = "6.x-1.x-dev"
+project = "feedapi_itemfilter"
+datestamp = "1211933074"
+
+
+; Information added by drupal.org packaging script on 2009-02-11
+version = "6.x-1.1"
+core = "6.x"
+project = "feedapi_itemfilter"
+datestamp = "1234385434"
+
diff -urN feedapi_itemfilter.orig/category_filter/feedapi_category_filter.module feedapi_itemfilter/category_filter/feedapi_category_filter.module
--- feedapi_itemfilter.orig/category_filter/feedapi_category_filter.module	1970-01-01 01:00:00.000000000 +0100
+++ feedapi_itemfilter/category_filter/feedapi_category_filter.module	2009-10-15 15:22:44.000000000 +0100
@@ -0,0 +1,133 @@
+<?php
+// $Id: feedapi_category_filter.module,v 1.1 2008/05/27 19:54:22 milosh Exp $
+
+/**
+ * @file
+ * FeedAPI Category Filter module
+ *
+ * This is filtering module for FeedAPI Item Filter module.
+ * The module filters feed items based on user defined keywords.
+ *
+ */
+
+
+/**
+ * Implementation of hook_help()
+ *
+ */
+function feedapi_category_filter_help($section) {
+  switch ($section) {
+    case 'feedapi/itemfilter/full_name':
+    return t('Category filter');
+    break;
+  }
+}
+
+
+/**
+ * Implementation of hook_feedapi_itemfilter()
+ *
+ * @param string $op
+ *    $op = 'settings' will modify FeedAPI item filter settings page. You can define
+ *           the form for any filter-specific settings here.
+ *    $op = 'process' will process the feed with your filter.
+ * @param array $feed
+ *    This is feed array, which was returned from parsers. It is used under 'process' stage
+ * @param array $options
+ *    Array of FeedAPI options. It includes the filter options that you required
+ *           when the 'settings' page was altered.
+ * @return  if $op = 'settings' then the form array for filter settings is returned
+ *          if $op = 'process' then nothing is returned: it is expected that you
+ *                    modified feed->items directly in your code (see line 116 below).
+ */
+function feedapi_category_filter_feedapi_itemfilter($op, &$feed, &$options)
+{
+  switch ($op) {
+    // Prepare settings for the filter.
+    case 'settings':
+      $form['info'] = array(
+      '#type' => 'item',
+      //        '#title' => t('Information about filter'),
+      //        '#value' => t('Keyword filter will check ...'),
+      '#description' => t('Category filter will check the &lt;category&gt; fields of feed items against a set of keyword(s)
+      immediately after parsing. Any item that has no matching keywords will not be saved with the feed.'),
+      '#weight' => -15,
+      );
+      $form['categories'] = array(
+      '#type' => 'textarea',
+      '#title' => t('Include Tags'),
+      '#default_value' => $node_type_settings['feedapi']['itemfilter']['category'],
+      '#attributes' => '',
+      '#description' => t('Keywords that must be included in a feed item for it to be displayed - e.g. "iPhone, +battery, -problem".
+        Keywords that start with minus (-) will be used as negative keywords.
+        Please separate different keywords with comma (,). You can also use phrases - e.g. "top performance, -bad problems".'),
+        );
+        return $form;
+        break;
+
+      // Process the items.
+      case 'process':
+        // Is the filter enabled and do we have something to process?
+        if(!empty($options['categories'])) { // is_null?
+
+          // Arrays for new feed item set and keywords is necessary
+          $newfeed = array();
+          $words = explode(",", $options['categories']);
+          // If delimiter does not exist then there is only one keyword
+          if ($words == FALSE) {
+            $words = array($options['categories']);
+          }
+
+          // There are two types of keywords -- negative and positive.
+          $wordlist = array('neg' => array(), 'pos' => array());
+
+          // So split the list into two sets.
+          foreach ($words as $word) {
+            $word = trim($word);
+            if(mb_substr($word, 0, 1) == '-') {
+              $wordlist['neg'][] = mb_substr($word, 1);
+            }
+            elseif(mb_substr($word, 0, 1) == '+') {
+              $wordlist['pos'][] = mb_substr($word, 1);
+            }
+            else {
+              $wordlist['pos'][] = $word;
+            }
+          }
+
+          // Get through each feed item.
+          foreach ($feed->items as $key=>$item) {
+            // If negative keyword has been found then move directly to next item.
+            foreach ($wordlist['neg'] as $value) {
+              foreach ($item->options->tags as $tag) {
+                // Number 2 here means that we are stepping out from both foreach statement.
+                if (strcasecmp($tag, $value) == 0) continue 3;
+              }
+            }
+
+            // And now we process positive keywords.
+            if(!empty($wordlist['pos'])) {
+              foreach ($wordlist['pos'] as $value) {
+                foreach ($item->options->tags as $tag) {
+                  if (strcasecmp($tag, $value) == 0) {
+                    // If keyword found then the item is worth saving.
+                    $newfeed[] = $feed->items[$key];
+                    // And there is no reason to try out remaining keywords --> move to the next item.
+                    continue 3;
+                  }
+                }
+              }
+            } else {
+              // If no positive items are provided, pick the item by default
+              $newfeed[] = $feed->items[$key];
+            } // End of positive items parsing
+          } // end of items parsing
+
+          // Let our users know about the results.
+          drupal_set_message(t('The feed %name was filtered, based on the following categories: "%categories". Items kept after filtering: %new out of %old.', array('%name' => $feed->title, '%categories' => $options['categories'], '%new' => count($newfeed), '%old' => count($feed->items))));
+            // And finally - let's replace the items with the ones that we need.
+          $feed->items = $newfeed;
+        } // endif
+        break;
+  } // end switch
+}
diff -urN feedapi_itemfilter.orig/category_filter/README.txt feedapi_itemfilter/category_filter/README.txt
--- feedapi_itemfilter.orig/category_filter/README.txt	1970-01-01 01:00:00.000000000 +0100
+++ feedapi_itemfilter/category_filter/README.txt	2009-10-15 15:20:56.000000000 +0100
@@ -0,0 +1,21 @@
+==================================================================================
+Category Filter
+==================================================================================
+
+Category filter will check the <category> fields (tags) of feed items against a set of
+keyword(s) immediately after parsing. Any item that has no matching keywords will
+not be saved with the feed. Items matching a negative keyword will also be discarded.
+
+Keywords should be separated by comma  e.g. "iPhone, +battery, -problem".
+
+You can use plus sign (+) in front of positive keywords if you want, but this
+is not required.
+
+If you define more than one keyword then the feed item will be included if
+any of the keywords is found.
+
+The negative keywords dominate over positive ones. So if the negative keyword is
+found the feed item will be always dropped.
+
+The search is case insensitive. The keyword 'iPhone' is just as good as the
+keyword 'IPHONE' or 'iphone'.
diff -urN feedapi_itemfilter.orig/feedapi_itemfilter.module~ feedapi_itemfilter/feedapi_itemfilter.module~
--- feedapi_itemfilter.orig/feedapi_itemfilter.module~	1970-01-01 01:00:00.000000000 +0100
+++ feedapi_itemfilter/feedapi_itemfilter.module~	2009-10-15 15:20:56.000000000 +0100
@@ -0,0 +1,159 @@
+<?php
+// $Id: feedapi_itemfilter.module,v 1.1 2008/05/27 19:54:22 milosh Exp $
+
+/**
+ * @file
+ * FeedAPI Item Filter module
+ *
+ * The purpose of this module is to provide a possibility to extend a FeedAPI module with
+ * a filter, that filters out such feed items that are not worth saving in the database at all.
+ *
+ * This filter implements FeedAPI's hook_feedapi_after_parse()
+ *
+ * This module provides its own hook -- hook_feedaapi_itemfilter($op, &$feed, &$options).
+ * This hook has two $op parameters_
+ *  - $op = 'settings' is used in order to add settings form into feed.
+ *  - $op = 'process' is used in order to process filtering on the feed
+ *
+ * ---
+ * @todo Separate module: Preg_match support for regular expressions
+ * @todo Let user choose the field(s) where keyword search is implemented
+ */
+
+/**
+ * Implementation of FeedAPI's hook_feedapi_settings_form()
+ *
+ * @param $type
+ *  A string of form type ('general', 'processor', 'parser')
+ * @return
+ *  Drupal Form API array
+ */
+function feedapi_itemfilter_feedapi_settings_form($type) {
+  // Content type form.
+  if ($type == 'general') { //general
+
+    // General array
+    $form['feedapi']['itemfilter'] = array(
+      '#type' => 'fieldset',
+      '#title' => t('Item filters'),
+      '#description' => t('Feedapi item filters will check feed items against filter critera -- any item, that does not fulfill criteria will not be included in the feed.'),
+      '#tree' => TRUE,
+      '#collapsible' => TRUE,
+      '#collapsed' => TRUE,
+    );
+
+    // Collect filters' settings trough hook
+    $filters = module_implements('feedapi_itemfilter', TRUE);
+    rsort($filters);
+    foreach ($filters as $module) {
+      $func = $module .'_feedapi_itemfilter';
+      // Get additional settings from filters
+      $form['feedapi']['itemfilter'][$module] = array(
+        '#type' => 'fieldset',
+        '#title' => feedapi_itemfilter_get_natural_name($module),
+        '#tree' => TRUE,
+        '#collapsible' => TRUE,
+        '#collapsed' => TRUE,
+      );
+      //$form_additional = $func('settings', $df=array(), $tr=array());
+      $form['feedapi']['itemfilter'][$module] = array_merge_recursive($form['feedapi']['itemfilter'][$module], $func('settings', $df=array(), $tr=array()));
+      $form['feedapi']['itemfilter'][$module]['#weight'] = $node_type_settings['itemfilter'][$module]['weight'];
+      $form['feedapi']['itemfilter'][$module]['enabled'] = array(
+        '#type' => 'checkbox',
+        '#title' => t('Enable filter'),
+        '#description' => t('Check if you want to use this filter.'),
+        '#default_value' => $node_type_settings['itemfilter'][$module]['enabled'],
+        '#weight' => -14,
+      );
+      $form['feedapi']['itemfilter'][$module]['weight'] = array(
+        '#type' => 'weight',
+        '#delta' => 15,
+        '#title' => t('Weight'),
+        '#description' => t('Control the execution order. Filters with lower weights are called before filters with higher weights.'),
+        '#default_value' => $node_type_settings['itemfilter'][$module]['weight'],
+        '#weight' => -13,
+      );
+    }
+
+    return $form;
+  }
+}
+
+/**
+ * Implementation of FeedAPI's feedapi_after_parse call
+ *
+ * @param &$feed
+ *  Array of parsed feed
+ */
+function feedapi_itemfilter_feedapi_after_parse(&$feed)
+{
+  // First, we have to find out keyword filter settings.
+
+  // When the node is refreshed, then the settings are available from $feed->settings.
+  if (!empty($feed->settings['feedapi']['itemfilter'])) {
+    $options = $feed->settings['feedapi']['itemfilter'];
+  }
+  // But when the node is inserted then the $feed->settings is not available
+  // and we have to obatin them from $_POST.
+  elseif (!empty($_POST['feedapi']['feedapi']['itemfilter'])) {
+    $options = $_POST['feedapi']['feedapi']['itemfilter'];
+  }
+  // When the refresh is called from cron.php neither of the above
+  // may not work, but fortunatelly we have $feed->nid present.
+  elseif ($feed->nid > 0) {
+    $node = node_load($feed->nid);
+    $options = $node->feed->settings['feedapi']['itemfilter'];
+  }
+  // This is last try - when the node is updated then the none of above will work and the only way
+  // to recover the settings is obataining nid from http address arguments.
+  //
+  // @todo Is this REALLY the only option here? The relyable results are not guaranteed, especially
+  // when user has played around with the path.
+  elseif (is_numeric(arg(1))) {
+    $node = node_load(arg(1));
+    $options = $node->feed->settings['feedapi']['itemfilter'];
+  }
+  // Anyway, if none of above worked then we are skrewed. Let the user know about it.
+  else {
+    drupal_set_message(t('Feedapi filter could not detect the feed. Filtering was not carried out. You may need to remove the feed items and try to refresh the feed manually.'), 'error');
+    return;
+  }
+
+  // Provide hook so we can have extendable filtering system.
+
+  // Find filters.
+  $filters = module_implements('feedapi_itemfilter', TRUE);
+  $modules = array();
+
+  // Set filter order.
+  foreach ($filters as $module) {
+    if ($options[$module]['enabled']) {
+      $modules[$options[$module]['weight']] = $module;
+    }
+  }
+  arsort($modules);
+
+  // Run enabled filters.
+  foreach ($modules as $weight => $module) {
+    $func = $module . '_feedapi_itemfilter';
+    $func('process', $feed, $options[$module]);
+  }
+}
+
+/**
+ * Get the module-defined natural name of FeedAPI filter
+ * Define this name in hook_help():
+ *
+ *  function hook_help($section) {
+ *    switch ($section) {
+ *      case 'feedapi/itemfilter/full_name':
+ *        return t('Natural name');
+ *        break;
+ *      }
+ *    }
+ */
+function feedapi_itemfilter_get_natural_name($module) {
+  $help = $module .'_help';
+  $module_natural = function_exists($help) ? $help('feedapi/itemfilter/full_name') : t($module);
+  return empty($module_natural) ? t($module) : $module_natural;
+}
