Beispiel #1
0
def rewrite_sql(query, primary_table = 'n', primary_field = 'nid',  args = []):
  join_, where, distinct = _rewrite_sql(query, primary_table, \
    primary_field, args)
  if (distinct):
    query = distinct_field(primary_table, primary_field, query)
  if (not php.empty(where) or not php.empty(join_)):
    pattern = \
      '{ ' + \
      '  # Beginning of the string ' + \
      '  ^ ' + \
      '  ((?P<anonymous_view> ' + \
      '    # Everything within this set of parentheses ' + \
      '    # is named "anonymous view ' + \
      '    (?: ' + \
      '      # anything not parentheses ' + \
      '      [^()]++ ' + \
      '      | ' + \
      '      # an open parenthesis, more anonymous view and ' + \
      '      # finally a close parenthesis. ' + \
      '      \( (?P>anonymous_view) \) ' + \
      '    )* ' + \
      '  )[^()]+WHERE) ' + \
      '}X'
    matches = []
    php.preg_match(pattern, query, matches)
    if (where):
      n = php.strlen(matches[1])
      second_part = php.substr(query, n)
      first_part = php.substr(matches[1], 0, n - 5) + \
        " join WHERE where AND ( "
      # PHP 4 does not support strrpos for strings. We emulate it.
      haystack_reverse = php.strrev(second_part)
      # No need to use strrev on the needle, we supply GROUP, ORDER, LIMIT
      # reversed.
      for needle_reverse in ('PUORG', 'REDRO', 'TIMIL'):
        pos = php.strpos(haystack_reverse, needle_reverse)
        if (pos != False):
          # All needles are five characters long.
          pos += 5
          break
      if (pos == False):
        query = first_part +  second_part  + ')'
      else:
        query = first_part +  substr(second_part, 0, -pos)  + ')' + \
          php.substr(second_part, -pos)
    else:
      query = matches[1] +  " join "  + \
        php.substr(query, php.strlen(matches[1]))
  return query
Beispiel #2
0
def drupal_match_path(path_, patterns):
  """
   Check if a path matches any pattern in a set of patterns.
  
   @note DRUPY:
     This function was substantially modified
   @param path
     The path to match.
   @param patterns
     String containing a set of patterns separated by \n, \r or \r\n.  
   @return
     Boolean value: TRUE if the path matches a pattern, FALSE otherwise.
  """
  php.static(drupal_match_path, 'regexps')
  if (not php.isset(drupal_match_path.regexps, patterns)):
    frnt = variable_get('site_frontpage', 'node');
    frnt_q = php.preg_quote(frnt, '/');
    frnt_p = '\1' + frnt_q + '\2';
    pra2 = ['|', '.*', frnt_p];
    pra1 = ['/(\r\n?|\n)/', '/\\\\\*/', '/(^|\|)\\\\<front\\\\>($|\|)/'];
    pat_q = php.preg_quote(patterns, '/');
    pat_prep = php.preg_replace(pra1, pra2, pat_q);
    pat_final = '/^(' + pat_prep + ')$/';
    drupal_match_path.regexps[patterns] = pat_final;
    return (php.preg_match(drupal_match_path.regexps[patterns], path_) > 0)
  else:
    return False
Beispiel #3
0
def drupal_validate_utf8(text):
    """
   Checks whether a string is valid UTF-8.
  
   All functions designed to filter input should use drupal_validate_utf8
   to ensure they operate on valid UTF-8 strings to prevent bypass of the
   filter.
  
   When text containing an invalid UTF-8 lead byte (0xC0 - 0xFF) is presented
   as UTF-8 to Internet Explorer 6, the program may misinterpret subsequent
   bytes. When these subsequent bytes are HTML control characters such as
   quotes or angle brackets, parts of the text that were deemed safe by filters
   end up in locations that are potentially unsafe; An onerror attribute that
   is outside of a tag, and thus deemed safe by a filter, can be interpreted
   by the browser as if it were inside the tag.
  
   This def exploits preg_match behaviour (since PHP 4.3.5) when used
   with the u modifier, as a fast way to find invalid UTF-8. When the matched
   string contains an invalid byte sequence, it will fail silently.
  
   preg_match may not fail on 4 and 5 octet sequences, even though they
   are not supported by the specification.
  
   The specific preg_match behaviour is present since PHP 4.3.5.
  
   @param text
     The text to check.
   @return
     TRUE if the text is valid UTF-8, FALSE if not.
  """
    if php.strlen(text) == 0:
        return True
    return php.preg_match("/^./us", text) == 1
Beispiel #4
0
def drupal_validate_utf8(text):
    """
   Checks whether a string is valid UTF-8.
  
   All functions designed to filter input should use drupal_validate_utf8
   to ensure they operate on valid UTF-8 strings to prevent bypass of the
   filter.
  
   When text containing an invalid UTF-8 lead byte (0xC0 - 0xFF) is presented
   as UTF-8 to Internet Explorer 6, the program may misinterpret subsequent
   bytes. When these subsequent bytes are HTML control characters such as
   quotes or angle brackets, parts of the text that were deemed safe by filters
   end up in locations that are potentially unsafe; An onerror attribute that
   is outside of a tag, and thus deemed safe by a filter, can be interpreted
   by the browser as if it were inside the tag.
  
   This def exploits preg_match behaviour (since PHP 4.3.5) when used
   with the u modifier, as a fast way to find invalid UTF-8. When the matched
   string contains an invalid byte sequence, it will fail silently.
  
   preg_match may not fail on 4 and 5 octet sequences, even though they
   are not supported by the specification.
  
   The specific preg_match behaviour is present since PHP 4.3.5.
  
   @param text
     The text to check.
   @return
     TRUE if the text is valid UTF-8, FALSE if not.
  """
    if (php.strlen(text) == 0):
        return True
    return (php.preg_match('/^./us', text) == 1)
Beispiel #5
0
def munge_filename(filename, extensions, alerts = True):
  """
   Munge the filename as needed for security purposes + For instance the file
   name "exploit.php.pps" would become "exploit.php_.pps".
  
   @param filename The name of a file to modify.
   @param extensions A space separated list of extensions that should not
     be altered.
   @param alerts Whether alerts (watchdog, drupal_set_message()) should be
     displayed.
   @return filename The potentially modified filename.
  """
  original = filename
  # Allow potentially insecure uploads for very savvy users and admin
  if (not variable_get('allow_insecure_uploads', 0)):
    whitelist = array_unique(php.explode(' ', php.trim(extensions)))
    # Split the filename up by periods + The first part becomes the basename
    # the last part the final extension.
    filename_parts = php.explode('.', filename)
    new_filename = php.array_shift(filename_parts); # Remove file basename.
    final_extension = php.array_pop(filename_parts); # Remove final extension.
    # Loop through the middle parts of the name and add an underscore to the
    # end of each section that could be a file extension but isn't in the list
    # of allowed extensions.
    for filename_part in filename_parts:
      new_filename += '.' + filename_part
      if (not php.in_array(filename_part, whitelist) and \
          php.preg_match("/^[a-zA-Z]{2,5}\d?$/", filename_part)):
        new_filename += '_'
    filename = new_filename + '.' + final_extension
    if (alerts and original != filename):
      drupal_set_message(t('For security reasons, your upload has ' + \
        'been renamed to %filename.', {'%filename' : filename}))
  return filename
Beispiel #6
0
def drupal_match_path(path_, patterns):
    """
   Check if a path matches any pattern in a set of patterns.
  
   @note DRUPY:
     This function was substantially modified
   @param path
     The path to match.
   @param patterns
     String containing a set of patterns separated by \n, \r or \r\n.  
   @return
     Boolean value: TRUE if the path matches a pattern, FALSE otherwise.
  """
    php.static(drupal_match_path, 'regexps')
    if (not php.isset(drupal_match_path.regexps, patterns)):
        frnt = variable_get('site_frontpage', 'node')
        frnt_q = php.preg_quote(frnt, '/')
        frnt_p = '\1' + frnt_q + '\2'
        pra2 = ['|', '.*', frnt_p]
        pra1 = ['/(\r\n?|\n)/', '/\\\\\*/', '/(^|\|)\\\\<front\\\\>($|\|)/']
        pat_q = php.preg_quote(patterns, '/')
        pat_prep = php.preg_replace(pra1, pra2, pat_q)
        pat_final = '/^(' + pat_prep + ')$/'
        drupal_match_path.regexps[patterns] = pat_final
        return (php.preg_match(drupal_match_path.regexps[patterns], path_) > 0)
    else:
        return False
Beispiel #7
0
def set_active(name = 'default'):
  """
   Activate a database for future queries.
  
   If it is necessary to use external databases in a project, this function can
   be used to change where database queries are sent. If the database has not
   yet been used, it is initialized using the URL specified for that name in
   Drupal's configuration file. If this name is not defined, a duplicate of the
   default connection is made instead.
  
   Be sure to change the connection back to the default when done with custom
   code.
  
   @param name
     The name assigned to the newly active database connection. If omitted, the
     default connection will be made active.
  
   @return the name of the previously active database or FALSE if non was
   found.
  
   @todo BC: Need to eventually resolve the database importing mechanism here
   right now we are statically loading mysql at the top, but eventually we need
   to get this figured out 
  """
  php.static(set_active, 'db_conns', {})
  php.static(set_active, 'active_name', False)
  if (settings.db_url == None):
    install_goto('install.py');
  if (not php.isset(set_active.db_conns, name)):
    # Initiate a new connection, using the named DB URL specified.
    if (isinstance(settings.db_url, dict)):
      connect_url = (settings.db_url[name] if \
        php.array_key_exists(name, settings.db_url) else \
        settings.db_url['default']);
    else:
      connect_url = settings.db_url;
    lib_appglobals.db_type = \
      php.substr(connect_url, 0, php.strpos(connect_url, '://'));
    #handler = "includes/database_%(db_type)s.py" % {'db_type' : db_type};
    #try:
    #  import db file here
    #except ImportError:
    #  _db_error_page("The database type '" + db_type + \
    #    "' is unsupported. Please use either 'mysql' or " + \
    #    "'mysqli' for MySQL, or 'pgsql' for PostgreSQL databases.");
    set_active.db_conns[name] = db.connect(connect_url);
    # We need to pass around the simpletest database prefix in the request
    # and we put that in the user_agent php.header.
    if (php.preg_match("/^simpletest\d+$/", php.SERVER['HTTP_USER_AGENT'])):
      settings.db_prefix = php.SERVER['HTTP_USER_AGENT'];
  previous_name = set_active.active_name;
  # Set the active connection.
  set_active.active_name = name;
  lib_appglobals.active_db = set_active.db_conns[name];
  return previous_name;
Beispiel #8
0
def validate_extensions(file, extensions):
    """
   Check that the filename ends with an allowed extension + This check is not
   enforced for the user #1.
  
   @param file
     A Drupal file object.
   @param extensions
     A string with a space separated
   @return
     An array + If the file extension is not allowed,
     it will contain an error message.
  """
    errors = []
    # Bypass validation for uid  = 1.
    if (lib_appglobals.user.uid != 1):
        regex = '/\.(' + ereg_replace(' +', '|', \
          php.preg_quote(extensions)) + ')$/i'
        if (not php.preg_match(regex, file.filename)):
            errors.append( t('Only files with the following extensions ' + \
              'are allowed: %files-allowed.', {'%files-allowed' : extensions}) )
    return errors
Beispiel #9
0
def validate_extensions(file, extensions):
  """
   Check that the filename ends with an allowed extension + This check is not
   enforced for the user #1.
  
   @param file
     A Drupal file object.
   @param extensions
     A string with a space separated
   @return
     An array + If the file extension is not allowed,
     it will contain an error message.
  """
  errors = []
  # Bypass validation for uid  = 1.
  if (lib_appglobals.user.uid != 1):
    regex = '/\.(' + ereg_replace(' +', '|', \
      php.preg_quote(extensions)) + ')$/i'
    if (not php.preg_match(regex, file.filename)):
      errors.append( t('Only files with the following extensions ' + \
        'are allowed: %files-allowed.', {'%files-allowed' : extensions}) )
  return errors
Beispiel #10
0
def munge_filename(filename, extensions, alerts=True):
    """
   Munge the filename as needed for security purposes + For instance the file
   name "exploit.php.pps" would become "exploit.php_.pps".
  
   @param filename The name of a file to modify.
   @param extensions A space separated list of extensions that should not
     be altered.
   @param alerts Whether alerts (watchdog, drupal_set_message()) should be
     displayed.
   @return filename The potentially modified filename.
  """
    original = filename
    # Allow potentially insecure uploads for very savvy users and admin
    if (not variable_get('allow_insecure_uploads', 0)):
        whitelist = array_unique(php.explode(' ', php.trim(extensions)))
        # Split the filename up by periods + The first part becomes the basename
        # the last part the final extension.
        filename_parts = php.explode('.', filename)
        new_filename = php.array_shift(filename_parts)
        # Remove file basename.
        final_extension = php.array_pop(filename_parts)
        # Remove final extension.
        # Loop through the middle parts of the name and add an underscore to the
        # end of each section that could be a file extension but isn't in the list
        # of allowed extensions.
        for filename_part in filename_parts:
            new_filename += '.' + filename_part
            if (not php.in_array(filename_part, whitelist) and \
                php.preg_match("/^[a-zA-Z]{2,5}\d?$/", filename_part)):
                new_filename += '_'
        filename = new_filename + '.' + final_extension
        if (alerts and original != filename):
            drupal_set_message(t('For security reasons, your upload has ' + \
              'been renamed to %filename.', {'%filename' : filename}))
    return filename
Beispiel #11
0
def from_browser():
  """
   Identify language from the Accept-language HTTP php.header we got.
  """
  # Specified by the user via the browser's Accept Language setting
  # Samples: "hu, en-us;q=0.66, en;q=0.33", "hu,en-us;q=0.5"
  browser_langs = []
  if (php.isset(php.SERVER, 'HTTP_ACCEPT_LANGUAGE')):
    browser_accept = php.explode(",", php.SERVER['HTTP_ACCEPT_LANGUAGE'])
    for i in range(php.count(browser_accept)):
      # The language part is either a code or a code with a quality.
      # We cannot do anything with a * code, so it is skipped.
      # If the quality is missing, it is assumed to be 1 according to the RFC.
      if (php.preg_match("not ([a-z-]+)(;q=([0-9\\.]+))?not ", \
          php.trim(browser_accept[i]), found)):
        browser_langs[found[1]] = (float(found[3]) if \
          php.isset(found, 3) else 1.0)
  # Order the codes by quality
  arsort(browser_langs)
  # Try to find the first preferred language we have
  languages = language_list('enabled')
  for langcode,q in browser_langs.items():
    if (php.isset(languages['1'], langcode)):
      return languages['1'][langcode]
Beispiel #12
0
def from_browser():
    """
   Identify language from the Accept-language HTTP php.header we got.
  """
    # Specified by the user via the browser's Accept Language setting
    # Samples: "hu, en-us;q=0.66, en;q=0.33", "hu,en-us;q=0.5"
    browser_langs = []
    if (php.isset(php.SERVER, 'HTTP_ACCEPT_LANGUAGE')):
        browser_accept = php.explode(",", php.SERVER['HTTP_ACCEPT_LANGUAGE'])
        for i in range(php.count(browser_accept)):
            # The language part is either a code or a code with a quality.
            # We cannot do anything with a * code, so it is skipped.
            # If the quality is missing, it is assumed to be 1 according to the RFC.
            if (php.preg_match("not ([a-z-]+)(;q=([0-9\\.]+))?not ", \
                php.trim(browser_accept[i]), found)):
                browser_langs[found[1]] = (float(found[3]) if \
                  php.isset(found, 3) else 1.0)
    # Order the codes by quality
    arsort(browser_langs)
    # Try to find the first preferred language we have
    languages = language_list('enabled')
    for langcode, q in browser_langs.items():
        if (php.isset(languages['1'], langcode)):
            return languages['1'][langcode]
Beispiel #13
0
def save_upload(source, validators = {}, dest = False, \
    replace = FILE_EXISTS_RENAME):
    """
   Saves a file upload to a new location + The source file is validated as a
   proper upload and handled as such.
  
   The file will be added to the files table as a temporary file.
   Temporary files
   are periodically cleaned + To make the file permanent file call
   file_set_status() to change its status.
  
   @param source
     A string specifying the name of the upload field to save.
   @param validators
     An optional, associative array of callback functions used to validate the
     file + The keys are function names and the values arrays of callback
     parameters which will be passed in after the user and file objects + The
     functions should return an array of error messages, an empty array
     indicates that the file passed validation.
     The functions will be called in
     the order specified.
   @param dest
     A string containing the directory source should be copied to + If this is
     not provided or is not writable, the temporary directory will be used.
   @param replace
     A boolean indicating whether an existing file of the same name in the
     destination directory should overwritten + A False value will generate a
     new, unique filename in the destination directory.
   @return
     An object containing the file information, or False
     in the event of an error.
  """
    php.static(file_save_upload, 'upload_cache', {})
    # Add in our check of the the file name length.
    validators['file_validate_name_length'] = {}
    # Return cached objects without processing since the file will have
    # already been processed and the paths in FILES will be invalid.
    if (php.isset(file_save_upload.uploadcache, source)):
        return file_save_upload.uploadcache[source]
    # If a file was uploaded, process it.
    if (php.isset(p.FILES, 'files') and p.FILES['files']['name'][source] and \
        php.is_uploaded_file(p.FILES['files']['tmp_name'][source])):
        # Check for file upload errors and return False if a
        # lower level system error occurred.
        # @see http://php.net/manual/en/features.file-upload.errors.php
        if p.FILES['files']['error'][source] == UPLOAD_ERR_OK:
            pass
        elif p.FILES['files']['error'][source] == UPLOAD_ERR_INI_SIZE or \
            p.FILES['files']['error'][source] == UPLOAD_ERR_FORM_SIZE:
            drupal_set_message(t(\
              'The file %file could not be saved, because it exceeds %maxsize, ' + \
              'the maximum allowed size for uploads.', \
              {'%file' : source, '%maxsize' : \
              format_size(file_upload_max_size())}), 'error')
            return False
        elif p.FILES['files']['error'][source] == UPLOAD_ERR_PARTIAL or \
            p.FILES['files']['error'][source] == UPLOAD_ERR_NO_FILE:
            drupal_set_message(t('The file %file could not be saved, ' + \
              'because the upload did not complete.', {'%file' : source}), 'error')
            return False
        # Unknown error
        else:
            drupal_set_message(t('The file %file could not be saved. ' + \
              'An unknown error has occurred.', {'%file' : source}), 'error')
            return False
        # Build the list of non-munged extensions.
        # @todo: this should not be here + we need to figure out the right place.
        extensions = ''
        for rid, name in lib_appglobals.user.roles.items():
            extensions += ' ' + variable_get("upload_extensions_rid",
            variable_get('upload_extensions_default', \
              'jpg jpeg gif png txt html doc xls pdf ppt pps odt ods odp'))
        # Begin building file object.
        file = php.stdClass()
        file.filename = file_munge_filename(php.trim(\
          basename(p.FILES['files']['name'][source]), '.'), extensions)
        file.filepath = p.FILES['files']['tmp_name'][source]
        file.filemime = p.FILES['files']['type'][source]
        # Rename potentially executable files, to help prevent exploits.
        if (php.preg_match('/\.(php|pl|py|cgi|asp|js)$/i', file.filename) and \
            (php.substr(file.filename, -4) != '.txt')):
            file.filemime = 'text/plain'
            file.filepath += '.txt'
            file.filename += '.txt'
        # If the destination is not provided, or is not writable, then use the
        # temporary directory.
        if (php.empty(dest) or file_check_path(dest) == False):
            dest = file_directory_temp()
        file.source = source
        file.destination = file_destination(file_create_path(dest + '/' + \
          file.filename), replace)
        file.filesize = FILES['files']['size'][source]
        # Call the validation functions.
        errors = {}
        for function, args in validators.items():
            array_unshift(args, file)
            errors = php.array_merge(errors, function(*args))
        # Check for validation errors.
        if (not php.empty(errors)):
            message = t('The selected file %name could not be uploaded.', \
              {'%name' : file.filename})
            if (php.count(errors) > 1):
                message += '<ul><li>' + php.implode('</li><li>',
                                                    errors) + '</li></ul>'
            else:
                message += ' ' + php.array_pop(errors)
            form_set_error(source, message)
            return False
        # Move uploaded files from PHP's upload_tmp_dir to
        # Drupal's temporary directory.
        # This overcomes open_basedir restrictions for future file operations.
        file.filepath = file.destination
        if (not move_uploaded_file(p.FILES['files']['tmp_name'][source], \
            file.filepath)):
            form_set_error(source, t('File upload error. ' + \
              'Could not move uploaded file.'))
            watchdog('file', 'Upload error + Could not move uploaded file ' + \
              '%file to destination %destination.', \
              {'%file' : file.filename, '%destination' : file.filepath})
            return False
        # If we made it this far it's safe to record this file in the database.
        file.uid = lib_appglobals.user.uid
        file.status = FILE_STATUS_TEMPORARY
        file.timestamp = time()
        drupal_write_record('files', file)
        # Add file to the cache.
        file_save_upload.upload_cache[source] = file
        return file
    return False
Beispiel #14
0
def save_upload(source, validators = {}, dest = False, \
    replace = FILE_EXISTS_RENAME):
  """
   Saves a file upload to a new location + The source file is validated as a
   proper upload and handled as such.
  
   The file will be added to the files table as a temporary file.
   Temporary files
   are periodically cleaned + To make the file permanent file call
   file_set_status() to change its status.
  
   @param source
     A string specifying the name of the upload field to save.
   @param validators
     An optional, associative array of callback functions used to validate the
     file + The keys are function names and the values arrays of callback
     parameters which will be passed in after the user and file objects + The
     functions should return an array of error messages, an empty array
     indicates that the file passed validation.
     The functions will be called in
     the order specified.
   @param dest
     A string containing the directory source should be copied to + If this is
     not provided or is not writable, the temporary directory will be used.
   @param replace
     A boolean indicating whether an existing file of the same name in the
     destination directory should overwritten + A False value will generate a
     new, unique filename in the destination directory.
   @return
     An object containing the file information, or False
     in the event of an error.
  """
  php.static(file_save_upload, 'upload_cache', {})
  # Add in our check of the the file name length.
  validators['file_validate_name_length'] = {}
  # Return cached objects without processing since the file will have
  # already been processed and the paths in FILES will be invalid.
  if (php.isset(file_save_upload.uploadcache, source)):
    return file_save_upload.uploadcache[source]
  # If a file was uploaded, process it.
  if (php.isset(p.FILES, 'files') and p.FILES['files']['name'][source] and \
      php.is_uploaded_file(p.FILES['files']['tmp_name'][source])):
    # Check for file upload errors and return False if a
    # lower level system error occurred.
    # @see http://php.net/manual/en/features.file-upload.errors.php
    if p.FILES['files']['error'][source] == UPLOAD_ERR_OK:
      pass
    elif p.FILES['files']['error'][source] == UPLOAD_ERR_INI_SIZE or \
        p.FILES['files']['error'][source] == UPLOAD_ERR_FORM_SIZE:
      drupal_set_message(t(\
        'The file %file could not be saved, because it exceeds %maxsize, ' + \
        'the maximum allowed size for uploads.', \
        {'%file' : source, '%maxsize' : \
        format_size(file_upload_max_size())}), 'error')
      return False
    elif p.FILES['files']['error'][source] == UPLOAD_ERR_PARTIAL or \
        p.FILES['files']['error'][source] == UPLOAD_ERR_NO_FILE:
      drupal_set_message(t('The file %file could not be saved, ' + \
        'because the upload did not complete.', {'%file' : source}), 'error')
      return False
    # Unknown error
    else:
      drupal_set_message(t('The file %file could not be saved. ' + \
        'An unknown error has occurred.', {'%file' : source}), 'error')
      return False
    # Build the list of non-munged extensions.
    # @todo: this should not be here + we need to figure out the right place.
    extensions = ''
    for rid,name in lib_appglobals.user.roles.items():
      extensions += ' ' + variable_get("upload_extensions_rid",
      variable_get('upload_extensions_default', \
        'jpg jpeg gif png txt html doc xls pdf ppt pps odt ods odp'))
    # Begin building file object.
    file = php.stdClass()
    file.filename = file_munge_filename(php.trim(\
      basename(p.FILES['files']['name'][source]), '.'), extensions)
    file.filepath = p.FILES['files']['tmp_name'][source]
    file.filemime = p.FILES['files']['type'][source]
    # Rename potentially executable files, to help prevent exploits.
    if (php.preg_match('/\.(php|pl|py|cgi|asp|js)$/i', file.filename) and \
        (php.substr(file.filename, -4) != '.txt')):
      file.filemime = 'text/plain'
      file.filepath += '.txt'
      file.filename += '.txt'
    # If the destination is not provided, or is not writable, then use the
    # temporary directory.
    if (php.empty(dest) or file_check_path(dest) == False):
      dest = file_directory_temp()
    file.source = source
    file.destination = file_destination(file_create_path(dest + '/' + \
      file.filename), replace)
    file.filesize = FILES['files']['size'][source]
    # Call the validation functions.
    errors = {}
    for function,args in validators.items():
      array_unshift(args, file)
      errors = php.array_merge(errors, function(*args))
    # Check for validation errors.
    if (not php.empty(errors)):
      message = t('The selected file %name could not be uploaded.', \
        {'%name' : file.filename})
      if (php.count(errors) > 1):
        message += '<ul><li>' + php.implode('</li><li>', errors) + '</li></ul>'
      else:
        message += ' ' + php.array_pop(errors)
      form_set_error(source, message)
      return False
    # Move uploaded files from PHP's upload_tmp_dir to
    # Drupal's temporary directory.
    # This overcomes open_basedir restrictions for future file operations.
    file.filepath = file.destination
    if (not move_uploaded_file(p.FILES['files']['tmp_name'][source], \
        file.filepath)):
      form_set_error(source, t('File upload error. ' + \
        'Could not move uploaded file.'))
      watchdog('file', 'Upload error + Could not move uploaded file ' + \
        '%file to destination %destination.', \
        {'%file' : file.filename, '%destination' : file.filepath})
      return False
    # If we made it this far it's safe to record this file in the database.
    file.uid = lib_appglobals.user.uid
    file.status = FILE_STATUS_TEMPORARY
    file.timestamp = time()
    drupal_write_record('files', file)
    # Add file to the cache.
    file_save_upload.upload_cache[source] = file
    return file
  return False