Index: includes/form.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/form.inc,v
retrieving revision 1.427
diff -u -p -r1.427 form.inc
--- includes/form.inc	25 Jan 2010 10:38:34 -0000	1.427
+++ includes/form.inc	25 Jan 2010 17:28:07 -0000
@@ -1345,15 +1345,36 @@ function _form_builder_handle_input_elem
     array_unshift($element['#parents'], $name);
   }
 
+  // Setting #disabled to TRUE results in user input being ignored, regardless
+  // of how the element is themed or whether JavaScript is used to change the
+  // control's attributes. However, it's good UI to let the user know that input
+  // is not wanted for the control. HTML supports two attributes for this:
+  // http://www.w3.org/TR/html401/interact/forms.html#h-17.12. If a form wants
+  // to start a control off with one of these attributes for UI purposes only,
+  // but still allow input to be processed if it's sumitted, it should set the
+  // desired attribute in #attributes directly rather than using #disabled.
   if (!empty($element['#disabled'])) {
-    $element['#attributes']['disabled'] = 'disabled';
+    if (empty($element['#disabled_allow_focus'])) {
+      $element['#attributes']['disabled'] = 'disabled';
+    }
+    else {
+      $element['#attributes']['readonly'] = 'readonly';
+    }
   }
 
   // Set the element's #value property.
   if (!isset($element['#value']) && !array_key_exists('#value', $element)) {
     $value_callback = !empty($element['#value_callback']) ? $element['#value_callback'] : 'form_type_' . $element['#type'] . '_value';
 
-    if ($form_state['programmed'] || ($form_state['process_input'] && (!isset($element['#access']) || $element['#access']))) {
+    // With JavaScript or other easy hacking, input can be submitted even for
+    // elements with #access=FALSE or #disabled=TRUE. For security, these must
+    // not be processed. Forms that set #disabled=TRUE on an element do not
+    // expect input for the element, and even forms submitted with
+    // drupal_form_submit() must not be able to get around this. Forms that set
+    // #access=FALSE on an element usually allow access for some users, so forms
+    // submitted with drupal_form_submit() may bypass access restriction and be
+    // treated as high-privelege users instead.
+    if (empty($element['#disabled']) && ($form_state['programmed'] || ($form_state['process_input'] && (!isset($element['#access']) || $element['#access'])))) {
       // Get the input for the current element. NULL values in the input need to
       // be explicitly distinguished from missing input. (see below)
       $input = $form_state['input'];
@@ -1621,18 +1642,11 @@ function form_type_image_button_value($f
  */
 function form_type_checkbox_value($element, $input = FALSE) {
   if ($input !== FALSE) {
-    if (empty($element['#disabled'])) {
-      // Successful (checked) checkboxes are present with a value (possibly '0').
-      // http://www.w3.org/TR/html401/interact/forms.html#successful-controls
-      // For an unchecked checkbox, we return numeric 0, so we can explicitly
-      // test for a value different than string '0'.
-      return isset($input) ? $element['#return_value'] : 0;
-    }
-    else {
-      // Disabled form controls are not submitted by the browser. Ignore any
-      // submitted value and always return default.
-      return $element['#default_value'];
-    }
+    // Successful (checked) checkboxes are present with a value (possibly '0').
+    // http://www.w3.org/TR/html401/interact/forms.html#successful-controls
+    // For an unchecked checkbox, we return numeric 0, so we can explicitly
+    // test for a value different than string '0'.
+    return isset($input) ? $element['#return_value'] : 0;
   }
 }
 
