diff --git modules/node/node.module modules/node/node.module
index f816d76..e5f3055 100644
--- modules/node/node.module
+++ modules/node/node.module
@@ -1539,7 +1539,7 @@ function node_search_admin() {
 /**
  * Implements hook_search_execute().
  */
-function node_search_execute($keys = NULL) {
+function node_search_execute($keys = NULL, $conditions = NULL) {
   // Build matching conditions
   $query = db_select('search_index', 'i', array('target' => 'slave'))->extend('SearchQuery')->extend('PagerDefault');
   $query->join('node', 'n', 'n.nid = i.sid');
diff --git modules/search/search.api.php modules/search/search.api.php
index 49b3954..2b16d3f 100644
--- modules/search/search.api.php
+++ modules/search/search.api.php
@@ -30,9 +30,15 @@
  * node_form_search_form_alter() for an example.
  *
  * @return
- *   Array with the optional keys 'title' for the tab title and 'path' for
- *   the path component after 'search/'.  Both will default to the module
- *   name.
+ *   Array with optional keys:
+ *   - 'title': Title for the tab on the search page for this module. Defaults
+ *     to the module name if not given.
+ *   - 'path': Path component after 'search/' for searching with this module.
+ *     Defaults to the module name if not given.
+ *   - 'conditions_callback': Name of a callback function that is invoked by
+ *     search_view() to get an array of additional search conditions to pass to
+ *     search_data(). Sample callback function:
+ *     sample_search_conditions_callback().
  *
  * @ingroup search
  */
@@ -40,10 +46,28 @@ function hook_search_info() {
   return array(
     'title' => 'Content',
     'path' => 'node',
+    'conditions_callback' => 'sample_search_conditions_callback',
   );
 }
 
 /**
+ * An example conditions callback function for search.
+ *
+ * Pulls additional search keywords out of the $_REQUEST variable.
+ *
+ * @see hook_search_info()
+ * @ingroup search
+ */
+function sample_search_conditions_callback($keys) {
+  $conditions = array();
+
+  if (!empty($_REQUEST['keys'])) {
+    $conditions['keys'] = $_REQUEST['keys'];
+  }
+  return $conditions;
+}
+
+/**
  * Define access to a custom search routine.
  *
  * This hook allows a module to define permissions for a search tab.
@@ -139,6 +163,8 @@ function hook_search_admin() {
  *
  * @param $keys
  *   The search keywords as entered by the user.
+ * @param $conditions
+ *   An optional array of additional conditions, such as filters.
  *
  * @return
  *   An array of search results. To use the default search result
@@ -154,7 +180,7 @@ function hook_search_admin() {
  *
  * @ingroup search
  */
-function hook_search_execute($keys = NULL) {
+function hook_search_execute($keys = NULL, $conditions = NULL) {
   // Build matching conditions
   $query = db_select('search_index', 'i', array('target' => 'slave'))->extend('SearchQuery')->extend('PagerDefault');
   $query->join('node', 'n', 'n.nid = i.sid');
diff --git modules/search/search.module modules/search/search.module
index a24a712..9f7c46a 100644
--- modules/search/search.module
+++ modules/search/search.module
@@ -208,7 +208,7 @@ function search_menu() {
       $items[$path] = array(
         'title' => $search_info['title'],
         'page callback' => 'search_view',
-        'page arguments' => array($module),
+        'page arguments' => array($module, ''),
         'access callback' => '_search_menu_access',
         'access arguments' => array($module),
         'type' => MENU_LOCAL_TASK,
@@ -218,7 +218,7 @@ function search_menu() {
       $items["$path/%menu_tail"] = array(
         'title' => $search_info['title'],
         'page callback' => 'search_view',
-        'page arguments' => array($module),
+        'page arguments' => array($module, 2),
         'access callback' => '_search_menu_access',
         'access arguments' => array($module),
         // The default local task points to its parent, but this item points to
@@ -869,21 +869,6 @@ function search_expression_insert($keys, $option, $value = '') {
 }
 
 /**
- * Helper function for grabbing search keys.
- */
-function search_get_keys() {
-  $return = &drupal_static(__FUNCTION__);
-  if (!isset($return)) {
-    // Extract keys as remainder of path
-    // Note: support old GET format of searches for existing links.
-    $path = explode('/', $_GET['q'], 3);
-    $keys = empty($_REQUEST['keys']) ? '' : $_REQUEST['keys'];
-    $return = count($path) == 3 ? $path[2] : $keys;
-  }
-  return $return;
-}
-
-/**
  * @defgroup search Search interface
  * @{
  * The Drupal search interface manages a global search mechanism.
@@ -1052,14 +1037,16 @@ function template_preprocess_search_block_form(&$variables) {
  *   Keyword query to search on.
  * @param $module
  *   Search module to search.
+ * @param $conditions
+ *   Optional array of additional search conditions.
  *
  * @return
  *   Formatted search results. No return value if $keys are not supplied or
  *   if the given search module is not active.
  */
-function search_data($keys, $module) {
+function search_data($keys, $module, $conditions = NULL) {
   if (module_hook($module, 'search_execute')) {
-    $results = module_invoke($module, 'search_execute', $keys);
+    $results = module_invoke($module, 'search_execute', $keys, $conditions);
     if (module_hook($module, 'search_page')) {
       return module_invoke($module, 'search_page', $results);
     }
diff --git modules/search/search.pages.inc modules/search/search.pages.inc
index 6c0d1b9..1b5ee5b 100644
--- modules/search/search.pages.inc
+++ modules/search/search.pages.inc
@@ -12,9 +12,15 @@
  * @param $module
  *   Search module to use for the search.
  */
-function search_view($module = NULL) {
+function search_view($module = NULL, $keys = '') {
   $info = FALSE;
   $redirect = FALSE;
+  $keys = trim($keys);
+  // Also try to pull search keywords out of the $_REQUEST variable to
+  // support old GET format of searches for existing links.
+  if (!$keys && !empty($_REQUEST['keys'])) {
+    $keys = trim($_REQUEST['keys']);
+  }
 
   if (!empty($module)) {
     $active_module_info = search_get_info();
@@ -28,40 +34,40 @@ function search_view($module = NULL) {
     // are no enabled search modules, this function should never be called,
     // since hook_menu() would not have defined any search paths.
     $info = search_get_default_module_info();
-    $redirect = TRUE;
+    // Redirect from bare /search or an invalid path to the default search path.
+    $path = 'search/' . $info['path'];
+    if ($keys) {
+      $path .= '/' . $keys;
+    }
+    drupal_goto($path);
   }
 
-  // Search form submits with POST but redirects to GET. This way we can keep
-  // the search query URL clean as a whistle:
-  // search/type_path/keyword+keyword
-  if (!isset($_POST['form_id'])) {
-    $keys = search_get_keys();
-    if ($redirect) {
-      // Redirect from bare /search or an invalid path to the default search path.
-      $path = 'search/' . $info['path'];
-      if ($keys) {
-        $path .= '/' . $keys;
-      }
-      drupal_goto($path);
+  $results = '';
+  // Process the search form. Note that if there is $_POST data,
+  // search_form_submit() will cause a redirect to search/[module path]/[keys],
+  // which will get us back to this page callback. In other words, the search
+  // form submits with POST but redirects to GET. This way we can keep
+  // the search query URL clean as a whistle.
+  if (empty($_POST['form_id']) || $_POST['form_id'] != 'search_form') {
+    $conditions =  NULL;
+    if (isset($info['conditions_callback']) && function_exists($info['conditions_callback'])) {
+      // Build an optional array of more search conditions.
+      $conditions = call_user_func($info['conditions_callback'], $keys);
     }
-    // Only perform search if there is a non-whitespace search term.
-    $results = '';
-    if (trim($keys)) {
+    // Only search if there are keywords or non-empty conditions.
+    if ($keys || !empty($conditions)) {
       // Log the search keys.
       watchdog('search', 'Searched %type for %keys.', array('%keys' => $keys, '%type' => $info['title']), WATCHDOG_NOTICE, l(t('results'), 'search/' . $info['path'] . '/' . $keys));
 
       // Collect the search results.
-      $results = search_data($keys, $info['module']);
-
-      // Construct the search form.
-      $build['search_form'] = drupal_get_form('search_form', NULL, $keys, $info['module']);
-      $build['search_results'] = array('#markup' => $results);
-
-      return $build;
+      $results = search_data($keys, $info['module'], $conditions);
     }
   }
+  // The form may be altered based on whether the search was run.
+  $build['search_form'] = drupal_get_form('search_form', NULL, $keys, $info['module']);
+  $build['search_results'] = array('#markup' => $results);
 
-  return drupal_get_form('search_form', NULL, empty($keys) ? '' : $keys, $info['module']);
+  return $build;
 }
 
 /**
diff --git modules/search/search.test modules/search/search.test
index c45e931..bbbb7a9 100644
--- modules/search/search.test
+++ modules/search/search.test
@@ -988,6 +988,58 @@ class SearchSimplifyTestCase extends DrupalWebTestCase {
   }
 }
 
+
+/**
+ * Test config page.
+ */
+class SearchKeywordsConditions extends DrupalWebTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Keywords and conditions',
+      'description' => 'Verify the search pulls in keywords and extra conditions.',
+      'group' => 'Search',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('search', 'search_extra_type');
+    // Create searching user.
+    $this->searching_user = $this->drupalCreateUser(array('search content', 'access content', 'access comments', 'post comments without approval'));
+    // Login with sufficient privileges.
+    $this->drupalLogin($this->searching_user);
+    // Test with all search modules enabled.
+    variable_set('search_active_modules', array('node' => 'node', 'user' => 'user', 'search_extra_type' => 'search_extra_type'));
+    menu_rebuild();
+  }
+
+  /**
+   * Verify the kewords are captured and conditions respected.
+   */
+  function testSearchKeyswordsConditions() {
+    // No keys, not conditions - no results.
+    $this->drupalGet('search/dummy_path');
+    $this->assertNoText('Dummy search snippet to display');
+    // With keys - get results.
+    $keys = 'bike shed ' . $this->randomName();
+    $this->drupalGet("search/dummy_path/{$keys}");
+    $this->assertText("Dummy search snippet to display. Keywords: {$keys}");
+    $keys = 'blue drop ' . $this->randomName();
+    $this->drupalGet("search/dummy_path", array('query' => array('keys' => $keys)));
+    $this->assertText("Dummy search snippet to display. Keywords: {$keys}");
+    // Add some conditions and keys.
+    $keys = 'moving drop ' . $this->randomName();
+    $this->drupalGet("search/dummy_path/bike", array('query' => array('search_conditions' => $keys)));
+    $this->assertText("Dummy search snippet to display.");
+    $this->assertRaw(print_r(array('search_conditions' => $keys), TRUE));
+    // Add some conditions and no keys.
+    $keys = 'drop kick ' . $this->randomName();
+    $this->drupalGet("search/dummy_path", array('query' => array('search_conditions' => $keys)));
+    $this->assertText("Dummy search snippet to display.");
+    $this->assertRaw(print_r(array('search_conditions' => $keys), TRUE));
+  }
+}
+
 /**
  * Test config page.
  */
@@ -1287,3 +1339,79 @@ class SearchTokenizerTestCase extends DrupalWebTestCase {
     return '';
   }
 }
+
+/**
+ * Tests that we can embed a form in search results and submit it.
+ */
+class SearchEmbedForm extends DrupalWebTestCase {
+  /**
+   * Node used for testing.
+   */
+  public $node;
+
+  /**
+   * Count of how many times the form has been submitted.
+   */
+  public $submit_count = 0;
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Embedded forms',
+      'description' => 'Verifies that a form embedded in search results works',
+      'group' => 'Search',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('search', 'search_embed_form');
+
+    // Create a user and a node, and update the search index.
+    $test_user = $this->drupalCreateUser(array('access content', 'search content', 'administer nodes'));
+    $this->drupalLogin($test_user);
+
+    $this->node = $this->drupalCreateNode();
+
+    node_update_index();
+    search_update_totals();
+
+    // Set up a dummy initial count of times the form has been submitted.
+    $this->submit_count = 12;
+    variable_set('search_embed_form_submitted', $this->submit_count);
+    $this->refreshVariables();
+  }
+
+  /**
+   * Tests that the embedded form appears and can be submitted.
+   */
+  function testEmbeddedForm() {
+    // First verify we can submit the form from the module's page.
+    $this->drupalPost('search_embed_form',
+      array('name' => 'John'),
+      t('Send away'));
+    $this->assertText(t('Test form was submitted'), 'Form message appears');
+    $count = variable_get('search_embed_form_submitted', 0);
+    $this->assertEqual($this->submit_count + 1, $count, 'Form submission count is correct');
+    $this->submit_count = $count;
+
+    // Now verify that we can see and submit the form from the search results.
+    $this->drupalGet('search/node/' . $this->node->title);
+    $this->assertText(t('Your name'), 'Form is visible');
+    $this->drupalPost('search/node/' . $this->node->title,
+      array('name' => 'John'),
+      t('Send away'));
+    $this->assertText(t('Test form was submitted'), 'Form message appears');
+    $count = variable_get('search_embed_form_submitted', 0);
+    $this->assertEqual($this->submit_count + 1, $count, 'Form submission count is correct');
+    $this->submit_count = $count;
+
+    // Now verify that if we submit the search form, it doesn't count as
+    // our form being submitted.
+    $this->drupalPost('search',
+      array('keys' => 'foo'),
+      t('Search'));
+    $this->assertNoText(t('Test form was submitted'), 'Form message does not appear');
+    $count = variable_get('search_embed_form_submitted', 0);
+    $this->assertEqual($this->submit_count, $count, 'Form submission count is correct');
+    $this->submit_count = $count;
+  }
+}
diff --git modules/search/tests/search_embed_form.info modules/search/tests/search_embed_form.info
new file mode 100644
index 0000000..ac80c13
--- /dev/null
+++ modules/search/tests/search_embed_form.info
@@ -0,0 +1,8 @@
+; $Id: $
+name = "Test extra form"
+description = "Support module for search module testing."
+package = Testing
+version = VERSION
+core = 7.x
+files[] = search_embed_form.module
+hidden = TRUE
diff --git modules/search/tests/search_embed_form.module modules/search/tests/search_embed_form.module
new file mode 100644
index 0000000..0dc64c4
--- /dev/null
+++ modules/search/tests/search_embed_form.module
@@ -0,0 +1,66 @@
+<?php
+// $Id: $
+
+/**
+ * @file
+ * Test module implementing a form that can be embedded in search results.
+ */
+
+/**
+  * Implements hook_menu().
+  */
+function search_embed_form_menu() {
+  $items['search_embed_form'] = array(
+    'title' => 'Search_Embed_Form',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('search_embed_form_form'),
+    'access arguments' => array('search content'),
+    'type' => MENU_CALLBACK,
+  );
+
+  return $items;
+}
+
+/**
+ * Builds a form for embedding in search results for testing.
+ *
+ * @see search_embed_form_form_submit().
+ */
+function search_embed_form_form($form, &$form_state) {
+  $count = variable_get('search_embed_form_submitted', 0);
+
+  $form['name'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Your name'),
+    '#maxlength' => 255,
+    '#default_value' => '',
+    '#required' => TRUE,
+    '#description' => t('Times form has been submitted: %count', array('%count' => $count)),
+  );
+
+  $form['actions'] = array('#type' => 'actions');
+  $form['actions']['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Send away'),
+  );
+
+  $form['#submit'][] = 'search_embed_form_form_submit';
+
+  return $form;
+}
+
+/**
+ * Submit handler for search_embed_form_form().
+ */
+function search_embed_form_form_submit($form, &$form_state) {
+  $count = variable_get('search_embed_form_submitted', 0) + 1;
+  variable_set('search_embed_form_submitted', $count);
+  drupal_set_message(t('Test form was submitted'));
+}
+
+/**
+ * Adds the test form to search results.
+ */
+function search_embed_form_preprocess_search_result(&$variables) {
+  $variables['snippet'] .= drupal_render(drupal_get_form('search_embed_form_form'));
+}
diff --git modules/search/tests/search_extra_type.module modules/search/tests/search_extra_type.module
index 2f37884..fce8dfd 100644
--- modules/search/tests/search_extra_type.module
+++ modules/search/tests/search_extra_type.module
@@ -13,22 +13,35 @@ function search_extra_type_search_info() {
   return array(
     'title' => 'Dummy search type',
     'path' => 'dummy_path',
+    'conditions_callback' => 'search_extra_type_conditions',
   );
 }
 
+function search_extra_type_conditions() {
+  $conditions = array();
+
+  if (!empty($_REQUEST['search_conditions'])) {
+    $conditions['search_conditions'] = $_REQUEST['search_conditions'];
+  }
+  return $conditions;
+}
+
 /**
  * Implements hook_search_execute().
  *
  * This is a dummy search, so when search "executes", we just return a dummy
- * result.
+ * result containing the keywords and a list of conditions.
  */
-function search_extra_type_search_execute() {
+function search_extra_type_search_execute($keys = NULL, $conditions = NULL) {
+  if (!$keys) {
+    $keys = '';
+  }
   return array(
     array(
       'link' => url('node'),
       'type' => 'Dummy result type',
       'title' => 'Dummy title',
-      'snippet' => 'Dummy search snippet to display',
+      'snippet' => "Dummy search snippet to display. Keywords: {$keys}\n\nConditions: " . print_r($conditions, TRUE),
     ),
   );
 }
diff --git modules/user/user.module modules/user/user.module
index 6978564..3a7cf68 100644
--- modules/user/user.module
+++ modules/user/user.module
@@ -871,7 +871,7 @@ function user_search_access() {
 /**
  * Implements hook_search_execute().
  */
-function user_search_execute($keys = NULL) {
+function user_search_execute($keys = NULL, $conditions = NULL) {
   $find = array();
   // Replace wildcards with MySQL/PostgreSQL wildcards.
   $keys = preg_replace('!\*+!', '%', $keys);
