diff --git a/includes/rules.processor.inc b/includes/rules.processor.inc
index 8ff4a05..d3a285c 100644
--- a/includes/rules.processor.inc
+++ b/includes/rules.processor.inc
@@ -245,10 +245,14 @@ abstract class RulesDataInputEvaluator extends RulesDataProcessor {
     if (!isset($options)) {
       $cache = rules_get_cache();
       $languages = language_list();
+      $info += array(
+        'cleaning callback' => isset($cache['data info'][$info['type']]['cleaning callback']) ? $cache['data info'][$info['type']]['cleaning callback'] : FALSE,
+        'sanitize' => FALSE,
+      );
       $options = array_filter(array(
         'language' => isset($element->settings['language']) && isset($languages[$element->settings['language']]) ? $languages[$element->settings['language']] : NULL,
-        'callback' => isset($cache['data info'][$info['type']]['cleaning callback']) ? $cache['data info'][$info['type']]['cleaning callback'] : FALSE,
-        'sanitize' => !empty($info['sanitize']),
+        'callback' => $info['cleaning callback'],
+        'sanitize' => $info['sanitize'],
       ));
     }
     $value = isset($this->processor) ? $this->processor->process($value, $info, $state, $element, $options) : $value;
diff --git a/modules/path.eval.inc b/modules/path.eval.inc
index bfef75e..9f74acd 100644
--- a/modules/path.eval.inc
+++ b/modules/path.eval.inc
@@ -12,7 +12,6 @@
  * Action implementation: Path alias.
  */
 function rules_action_path_alias($source, $alias, $langcode = LANGUAGE_NONE) {
-  $alias = rules_clean_path($alias);
   if (!$alias) {
     path_delete(array('source' => $source, 'language' => $langcode));
   }
@@ -39,7 +38,6 @@ function rules_action_path_alias($source, $alias, $langcode = LANGUAGE_NONE) {
  * Action Implementation: Set the URL alias for a node.
  */
 function rules_action_node_path_alias($node, $alias) {
-  $alias = rules_clean_path($alias);
   $langcode = isset($node->language) ? $node->language : LANGUAGE_NONE;
   // Only set the alias if the alias is not taken yet.
   if (($path = path_load(array('alias' => $alias, 'language' => $langcode))) && (empty($node->path['pid']) || $node->path['pid'] != $path['pid'])) {
@@ -53,7 +51,6 @@ function rules_action_node_path_alias($node, $alias) {
  * Action Implementation: Set the URL alias for a node.
  */
 function rules_action_taxonomy_term_path_alias($term, $alias) {
-  $alias = rules_clean_path($alias);
   // Only set the alias if the alias is not taken yet.
   if (($path = path_load(array('alias' => $alias, 'language' => LANGUAGE_NONE))) && (empty($term->path['pid']) || $term->path['pid'] != $path['pid'])) {
     rules_log('The configured alias %alias already exists. Aborting.', array('%alias' => $alias));
@@ -73,20 +70,10 @@ function rules_condition_path_has_alias($source, $langcode = LANGUAGE_NONE) {
  * Condition implementation: Check if the URL alias exists.
  */
 function rules_condition_path_alias_exists($alias, $langcode = LANGUAGE_NONE) {
-  $alias = rules_clean_path($alias);
   return (bool) drupal_lookup_path('source', $alias, $langcode);
 }
 
 /**
- * Cleans the given path using the configured path cleaning method.
- */
-function rules_clean_path($path) {
-  // @todo Add a setting for choosing path cleaning callbacks.
-  $function = variable_get('rules_path_cleaning_callback', 'rules_path_default_cleaning_method');
-  return $function($path);
-}
-
-/**
  * Cleans the given path by replacing non ASCII characters with the replacment character.
  *
  * Path cleaning may be adapted by overriding the configuration variables
@@ -103,10 +90,15 @@ function rules_path_default_cleaning_method($path) {
       $path = transliteration_get($path);
     }
 
-    $array = variable_get('rules_clean_path', array('/[^a-zA-Z0-9\/\-_]+/', $replace));
+    $array = variable_get('rules_clean_path', array('/[^a-zA-Z0-9\-_]+/', $replace));
     $array[2] = $path;
     // Replace it and remove trailing and leading replacement characters.
-    return trim(call_user_func_array('preg_replace', $array), $replace);
+    $output = trim(call_user_func_array('preg_replace', $array), $replace);
+
+    if (variable_get('rules_path_lower_case', TRUE)) {
+      $output = drupal_strtolower($output);
+    }
+    return $output;
   }
   else {
     return $path;
@@ -114,5 +106,43 @@ function rules_path_default_cleaning_method($path) {
 }
 
 /**
+ * Cleans the given string so it can be used as part of a URL path.
+ */
+function rules_clean_path($path) {
+  $function = variable_get('rules_path_cleaning_callback', 'rules_path_default_cleaning_method');
+  if (!function_exists($function)) {
+    rules_log('An invalid URL path cleaning callback has been configured. Falling back to the default cleaning method.', array(), RulesLog::WARN);
+    $function = 'rules_path_default_cleaning_method';
+  }
+  return $function($path);
+}
+
+/**
+ * CTools path cleaning callback.
+ *
+ * @see rules_admin_settings()
+ */
+function rules_path_clean_ctools($path) {
+  // Make use of the CTools cleanstring implementation.
+  ctools_include('cleanstring');
+  $settings = array(
+    'separator' => variable_get('rules_path_replacement_char', '-'),
+    'transliterate' => module_exists('transliteration') && variable_get('rules_path_transliteration', TRUE),
+    'lower case' => variable_get('rules_path_lower_case', TRUE),
+  );
+  return ctools_cleanstring($path, $settings);
+}
+
+/**
+ * Pathauto path cleaning callback.
+ *
+ * @see rules_admin_settings()
+ */
+function rules_path_clean_pathauto($path) {
+  module_load_include('inc', 'pathauto');
+  return pathauto_cleanstring($path);
+}
+
+/**
  * @}
  */
diff --git a/modules/path.rules.inc b/modules/path.rules.inc
index 9300a9b..2d62ec4 100644
--- a/modules/path.rules.inc
+++ b/modules/path.rules.inc
@@ -34,6 +34,7 @@ function rules_path_action_info() {
           'label' => t('URL alias'),
           'description' => t('Specify an alternative path by which this data can be accessed. For example, "about" for an about page. Use a relative path and do not add a trailing slash.') .' '. t('Leave it empty to delete URL aliases pointing to the given system path.'),
           'optional' => TRUE,
+          'cleaning callback' => 'rules_path_clean_replacement_values',
         ),
         'language' => array(
           'type' => 'token',
@@ -62,6 +63,7 @@ function rules_path_action_info() {
           'label' => t('URL alias'),
           'description' => t('Specify an alternative path by which the content can be accessed. For example, "about" for an about page. Use a relative path and do not add a trailing slash.') .' '. t('Leave it empty to delete the URL alias.'),
           'optional' => TRUE,
+          'cleaning callback' => 'rules_path_clean_replacement_values',
         ),
       ),
       'base' => 'rules_action_node_path_alias',
@@ -82,6 +84,7 @@ function rules_path_action_info() {
           'label' => t('URL alias'),
           'description' => t('Specify an alternative path by which the term can be accessed. For example, "content/drupal" for a Drupal term. Use a relative path and do not add a trailing slash.') .' '. t('Leave it empty to delete the URL alias.'),
           'optional' => TRUE,
+          'cleaning callback' => 'rules_path_clean_replacement_values',
         ),
       ),
       'base' => 'rules_action_node_path_alias',
@@ -145,6 +148,7 @@ function rules_path_condition_info() {
           'label' => t('URL alias'),
           'description' => t('Specify the URL alias to check for. For example, "about" for an about page.'),
           'optional' => TRUE,
+          'cleaning callback' => 'rules_path_clean_replacement_values',
         ),
         'language' => array(
           'type' => 'token',
diff --git a/rules.api.php b/rules.api.php
index eb94960..807b9db 100644
--- a/rules.api.php
+++ b/rules.api.php
@@ -96,6 +96,8 @@
  *     already sanitized argument. If enabled, any user specified value won't be
  *     sanitized itself, but replacements applied by input evaluators are as
  *     well as values retrieved from selected data sources.
+ *   - cleaning callback: (optional) A callback that input evaluators may use
+ *     to clean inserted replacements; e.g. this is used by the token evaluator.
  *   - wrapped: (optional) Set this to TRUE in case the data should be passed
  *     wrapped. This only applies to wrapped data types, e.g. entities.
  *  Each 'provides' array may contain the following properties:
diff --git a/rules.install b/rules.install
index 5d24561..370226e 100644
--- a/rules.install
+++ b/rules.install
@@ -399,3 +399,10 @@ function rules_update_7205() {
 function rules_update_7206() {
   // The update system is going to flush all caches anyway, so nothing to do.
 }
+
+/**
+ * Flush all caches.
+ */
+function rules_update_7207() {
+  // The update system is going to flush all caches anyway, so nothing to do.
+}
diff --git a/rules.module b/rules.module
index 6e9a3da..aca08c9 100644
--- a/rules.module
+++ b/rules.module
@@ -897,6 +897,28 @@ function rules_array_key($array) {
 }
 
 /**
+ * Clean replacements so they are URL friendly.
+ *
+ * Can be used as 'cleaning callback' for action or condition parameters.
+ *
+ * @param $replacements
+ *   An array of token replacements that need to be "cleaned" for use in the URL.
+ * @param $data
+ *   An array of objects used to generate the replacements.
+ * @param $options
+ *   An array of options used to generate the replacements.
+ *
+ * @see rules_path_action_info()
+ */
+function rules_path_clean_replacement_values(&$replacements, $data = array(), $options = array()) {
+  // Include path.eval.inc which contains path cleaning functions.
+  module_load_include('inc', 'rules', 'modules/path.eval');
+  foreach ($replacements as $token => $value) {
+    $replacements[$token] = rules_clean_path($value);
+  }
+}
+
+/**
  * Implements hook_theme().
  */
 function rules_theme() {
diff --git a/rules_admin/rules_admin.inc b/rules_admin/rules_admin.inc
index cbff2c5..cbaeae9 100644
--- a/rules_admin/rules_admin.inc
+++ b/rules_admin/rules_admin.inc
@@ -208,6 +208,34 @@ function rules_admin_settings($form, &$form_state) {
   if (db_table_exists('rules_rules')) {
     drupal_set_message(t('There are left over rule configurations from a previous Rules 1.x installation. Proceed to the <a href="!url">upgrade page</a> to convert them and consult the README.txt for more details.', array('!url' => url('admin/config/workflow/rules/upgrade'))), 'warning');
   }
+
+  if (module_exists('path')) {
+
+    // Present a list of available path cleaning callbacks.
+    // @see rules_clean_path()
+    $options = array(
+      'rules_path_default_cleaning_method' => t('Rules (built in)'),
+    );
+    if (module_exists('ctools')) {
+      $options['rules_path_clean_ctools'] = t('CTools');
+    }
+    if (module_exists('pathauto')) {
+      $options['rules_path_clean_pathauto'] = t('Pathauto');
+      $pathauto_help = t("Note that Pathauto's URL path cleaning method can be configured at <a href='!url'>admin/config/search/path/settings</a>.", array('!url' => url('admin/config/search/path/settings')));
+    }
+    else {
+      $pathauto_help = t('Install the <a href="http://drupal.org/project/pathauto">Pathauto module</a> in order to get a configurable URL path cleaning method.');
+    }
+
+    $form['path']['rules_path_cleaning_callback'] = array(
+      '#type' => 'select',
+      '#title' => t('URL path cleaning method'),
+      '#description' => t('Choose the path cleaning method to be applied when generating URL path aliases.') . ' ' . $pathauto_help,
+      '#default_value' => variable_get('rules_path_cleaning_callback', 'rules_path_default_cleaning_method'),
+      '#options' => $options,
+    );
+
+  }
   return system_settings_form($form);
 }
 
