/** * Constructor */ function __construct($props = array()) { parent::__construct(); // Make a local reference to the ExpressionEngine super object $this->EE =& get_instance(); ee()->load->helper('xss'); $props['xss_clean'] = xss_check(); $this->initialize($props); log_message('debug', "Upload Class Initialized"); }
/** * Constructor */ function __construct($props = array()) { if (count($props) > 0) { $this->initialize($props); } ee()->load->helper('xss'); $props['xss_clean'] = xss_check(); $this->initialize($props); ee()->load->library('mime_type'); log_message('debug', "Upload Class Initialized"); }
/** * Parse Email * * @param mixed - Email Data * @param */ function parse_email($email_data, $type = 'norm') { $this->EE->load->library('filemanager'); $boundary = $type != 'norm' ? $this->multi_boundary : $this->boundary; $email_data = str_replace('boundary=' . substr($boundary, 2), 'BOUNDARY_HERE', $email_data); $email_parts = explode($boundary, $email_data); if (count($email_parts) < 2) { $boundary = str_replace("+", "\\+", $boundary); $email_parts = explode($boundary, $email_data); } if (count($email_parts) < 2) { return FALSE; unset($email_parts); unset($email_data); } $upload_dir_id = $this->moblog_array['moblog_upload_directory']; if ($upload_dir_id != 0) { $this->upload_dir_code = '{filedir_' . $upload_dir_id . '}'; } // Find Attachments foreach ($email_parts as $key => $value) { // Skip headers and those with no content-type if ($key == '0' or stristr($value, 'Content-Type:') === FALSE) { continue; } $contents = $this->find_data($value, "Content-Type:", $this->newline); $x = explode(';', $contents); $content_type = $x['0']; $content_type = strtolower($content_type); $pieces = explode('/', trim($content_type)); $type = trim($pieces['0']); $subtype = !isset($pieces['1']) ? '0' : trim($pieces['1']); $charset = 'auto'; /** -------------------------- /** Outlook Exception /** --------------------------*/ if ($type == 'multipart' && $subtype != 'appledouble') { if (!stristr($value, 'boundary=')) { continue; } $this->multi_boundary = "--" . $this->find_data($value, "boundary=", $this->newline); $this->multi_boundary = trim(str_replace('"', '', $this->multi_boundary)); if (strlen($this->multi_boundary) == 0) { continue; } $this->parse_email($value, 'multi'); $this->multi_boundary = ''; continue; } /** -------------------------- /** Quick Grab of Headers /** --------------------------*/ $headers = $this->find_data($value, '', $this->newline . $this->newline); /** --------------------------- /** Text : plain, html, rtf /** ---------------------------*/ if ($type == 'text' && $headers != '' && ($this->txt_override === TRUE && $subtype == 'plain' or !stristr($headers, 'name='))) { $duo = $this->newline . $this->newline; $text = $this->find_data($value, $duo, ''); if ($text == '') { $text = $this->find_data($value, $this->newline, ''); } /** ------------------------------------ /** Charset Available? /** ------------------------------------*/ if (preg_match("/charset=(.*?)(\\s|" . $this->newline . ")/is", $headers, $match)) { $charset = trim(str_replace(array("'", '"', ';'), '', $match['1'])); } /** ------------------------------------ /** Check for Encoding of Text /** ------------------------------------*/ if (stristr($value, 'Content-Transfer-Encoding')) { $encoding = $this->find_data($value, "Content-Transfer-Encoding:", $this->newline); /** ------------------------------------ /** Check for Quoted-Printable encoding /** ------------------------------------*/ if (stristr($encoding, "quoted-printable")) { $text = str_replace($this->newline, "\n", $text); $text = quoted_printable_decode($text); $text = substr($text, 0, 1) != '=' ? $text : substr($text, 1); $text = substr($text, -1) != '=' ? $text : substr($text, 0, -1); $text = $this->remove_newlines($text, $this->newline); } elseif (stristr($encoding, "base64")) { $text = str_replace($this->newline, "\n", $text); $text = base64_decode(trim($text)); $text = $this->remove_newlines($text, $this->newline); } } /** ---------------------------------- /** T-Mobile - In cyberspace, no one can hear you cream. /** ----------------------------------*/ if (trim($text) != '' && stristr($text, 'This message was sent from a T-Mobile wireless phone') !== FALSE) { $text = ''; } if ($this->charset != $this->EE->config->item('charset')) { if (function_exists('mb_convert_encoding')) { $text = mb_convert_encoding($text, strtoupper($this->EE->config->item('charset')), strtoupper($this->charset)); } elseif (function_exists('iconv') and ($iconvstr = @iconv(strtoupper($this->charset), strtoupper($this->EE->config->item('charset')), $text)) !== FALSE) { $text = $iconvstr; } elseif (strtolower($this->EE->config->item('charset')) == 'utf-8' && strtolower($this->charset) == 'iso-8859-1') { $text = utf8_encode($text); } elseif (strtolower($this->EE->config->item('charset')) == 'iso-8859-1' && strtolower($this->charset) == 'utf-8') { $text = utf8_decode($text); } } // RTF and HTML are considered alternative text $subtype = $subtype != 'html' && $subtype != 'rtf' ? 'plain' : 'alt'; // Same content type, then join together $this->post_data[$type][$subtype] = isset($this->post_data[$type][$subtype]) ? $this->post_data[$type][$subtype] . " {$text}" : $text; // Plain text takes priority for body data. $this->body = !isset($this->post_data[$type]['plain']) ? $this->post_data[$type]['alt'] : $this->post_data[$type]['plain']; } elseif ($type == 'image' or $type == 'application' or $type == 'audio' or $type == 'video' or $subtype == 'appledouble' or $type == 'text') { // no upload directory? skip if ($upload_dir_id == 0) { continue; } if ($subtype == 'appledouble') { if (!($data = $this->appledouble($value))) { continue; } else { $value = $data['value']; $subtype = $data['subtype']; $type = $data['type']; unset($data); } } /** ------------------------------ /** Determine Filename /** ------------------------------*/ $contents = $this->find_data($value, "name=", $this->newline); if ($contents == '') { $contents = $this->find_data($value, 'Content-Location:', $this->newline); } if ($contents == '') { $contents = $this->find_data($value, 'Content-ID:', $this->newline); $contents = str_replace('<', '', $contents); $contents = str_replace('<', '', $contents); } $x = explode(';', trim($contents)); $filename = $x['0'] == '' ? 'moblogfile' : $x['0']; $filename = trim(str_replace('"', '', $filename)); $filename = str_replace($this->newline, '', $filename); if (stristr($filename, 'dottedline') or stristr($filename, 'spacer.gif') or stristr($filename, 'masthead.jpg')) { continue; } /** -------------------------------- /** File/Image Code and Cleanup /** --------------------------------*/ $duo = $this->newline . $this->newline; $file_code = $this->find_data($value, $duo, ''); if ($file_code == '') { $file_code = $this->find_data($value, $this->newline, ''); if ($file_code == '') { $this->message_array = 'invalid_file_data'; return FALSE; } } /** -------------------------------- /** Determine Encoding /** --------------------------------*/ $contents = $this->find_data($value, "Content-Transfer-Encoding:", $this->newline); $x = explode(';', $contents); $encoding = $x['0']; $encoding = trim(str_replace('"', '', $encoding)); $encoding = str_replace($this->newline, '', $encoding); if (!stristr($encoding, "base64") && !stristr($encoding, "7bit") && !stristr($encoding, "8bit") && !stristr($encoding, "quoted-printable")) { if ($type == 'text') { // RTF and HTML are considered alternative text $subtype = $subtype != 'html' && $subtype != 'rtf' ? 'plain' : 'alt'; // Same content type, then join together $this->post_data[$type][$subtype] = isset($this->post_data[$type][$subtype]) ? $this->post_data[$type][$subtype] . ' ' . $file_code : $file_code; // Plain text takes priority for body data. $this->body = !isset($this->post_data[$type]['plain']) ? $this->post_data[$type]['alt'] : $this->post_data[$type]['plain']; } continue; } // Eudora and Mail.app use this by default if (stristr($encoding, "quoted-printable")) { $file_code = quoted_printable_decode($file_code); } // Base64 gets no space and no line breaks $replace = !stristr($encoding, "base64") ? "\n" : ''; $file_code = trim(str_replace($this->newline, $replace, $file_code)); // PHP function sometimes misses opening and closing equal signs if (stristr($encoding, "quoted-printable")) { $file_code = substr($file_code, 0, 1) != '=' ? $file_code : substr($file_code, 1); $file_code = substr($file_code, -1) != '=' ? $file_code : substr($file_code, 0, -1); } // Decode so that we can run xss clean on the raw // data once we've determined the file type if (stristr($encoding, "base64")) { $file_code = base64_decode($file_code); $this->message_array[] = 'base64 decoded.'; } /** ------------------------------ /** Check and adjust for multiple files with same file name /** ------------------------------*/ $file_path = $this->EE->filemanager->clean_filename($filename, $upload_dir_id, array('ignore_dupes' => FALSE)); $filename = basename($file_path); /** --------------------------- /** Put Info in Post Data array /** ---------------------------*/ $ext = trim(strrchr($filename, '.'), '.'); $is_image = FALSE; // This is needed for XSS cleaning if (in_array(strtolower($ext), $this->movie)) { $this->post_data['movie'][] = $filename; } elseif (in_array(strtolower($ext), $this->audio)) { $this->post_data['audio'][] = $filename; } elseif (in_array(strtolower($ext), $this->image)) { $this->post_data['images'][] = $filename; $key = count($this->post_data['images']) - 1; $type = 'image'; // For those crazy application/octet-stream images $is_image = TRUE; } elseif (in_array(strtolower($ext), $this->files)) { $this->post_data['files'][] = $filename; } else { continue; } // Clean the file $this->EE->load->helper('xss'); if (xss_check()) { $xss_result = $this->EE->security->xss_clean($file_code, $is_image); // XSS Clean Failed - bail out if ($xss_result === FALSE) { $this->message_array[] = 'error_writing_attachment'; return FALSE; } if (!$is_image) { $file_code = $xss_result; } } // AT&T phones send the message as a .txt file // This checks to see if this email is from an AT&T phone, // not an encoded file, and has a .txt file extension in the filename if ($this->attach_as_txt === TRUE && !stristr($encoding, "base64")) { if ($ext == 'txt' && preg_match("/Content-Disposition:\\s*inline/i", $headers, $found)) { $this->attach_text = $file_code; $this->attach_name = $filename; continue; // No upload of file. } } // Check to see if we're dealing with relative paths if (strncmp($file_path, '..', 2) == 0) { $directory = dirname($file_path); $file_path = realpath(substr($directory, 1)) . '/' . $filename; } // Upload the file and check for errors if (file_put_contents($file_path, $file_code) === FALSE) { $this->message_array[] = 'error_writing_attachment'; return FALSE; } // Disable xss cleaning in the filemanager $this->EE->filemanager->xss_clean_off(); // Send the file $result = $this->EE->filemanager->save_file($file_path, $upload_dir_id, array('title' => $filename, 'rel_path' => dirname($file_path), 'file_name' => $filename)); unset($file_code); // Check to see the result if ($result['status'] === FALSE) { // $result['message'] $this->message_array[] = 'error_writing_attachment'; $this->message_array[] = print_r($result, TRUE); return FALSE; } $this->email_files[] = $filename; $this->uploads++; } // End files/images section } // End foreach return TRUE; }
/** * Upload File Callback * * The function that handles the file upload logic (allowed upload? etc.) * * 1. Establish the allowed types for the directory * - If the field is a custom field, make sure it's permissions aren't stricter * 2. Upload the file * - Checks to see if XSS cleaning needs to be on * - Returns errors * 3. Send file to save_file, which does more security, creates thumbs * and adds it to the database. * * @access private * @param array $dir Directory information from the database in array form * @param string $field_name Provide the field name in case it's a custom field * @return array Array of file_data sent to Filemanager->save_file */ private function _upload_file($dir, $field_name) { // -------------------------------------------------------------------- // Make sure the file is allowed // Restricted upload directory? switch ($dir['allowed_types']) { case 'all': $allowed_types = '*'; break; case 'img': $allowed_types = 'gif|jpg|jpeg|png|jpe'; break; default: $allowed_types = ''; } // Is this a custom field? if (strpos($field_name, 'field_id_') === 0) { $field_id = str_replace('field_id_', '', $field_name); $this->EE->db->select('field_type, field_settings'); $type_query = $this->EE->db->get_where('channel_fields', array('field_id' => $field_id)); if ($type_query->num_rows()) { $settings = unserialize(base64_decode($type_query->row('field_settings'))); // Permissions can only get more strict! if (isset($settings['field_content_type']) && $settings['field_content_type'] == 'image') { $allowed_types = 'gif|jpg|jpeg|png|jpe'; } } $type_query->free_result(); } // -------------------------------------------------------------------- // Upload the file $field = $field_name ? $field_name : 'userfile'; $original_filename = $_FILES[$field]['name']; $clean_filename = basename($this->clean_filename($_FILES[$field]['name'], $dir['id'], array('ignore_dupes' => TRUE))); $config = array('file_name' => $clean_filename, 'upload_path' => $dir['server_path'], 'allowed_types' => $allowed_types, 'max_size' => round($dir['max_size'] / 1024, 3)); $this->EE->load->helper('xss'); // Check to see if the file needs to be XSS Cleaned if (xss_check()) { $config['xss_clean'] = TRUE; } else { $config['xss_clean'] = FALSE; $this->xss_clean_off(); } // Upload the file $this->EE->load->library('upload'); $this->EE->upload->initialize($config); if (!$this->EE->upload->do_upload($field_name)) { return $this->_upload_error($this->EE->upload->display_errors()); } $file = $this->EE->upload->data(); // (try to) Set proper permissions @chmod($file['full_path'], FILE_WRITE_MODE); // -------------------------------------------------------------------- // Add file the database // Make sure the file has a valid MIME Type if (!$file['file_type']) { return $this->_upload_error(lang('invalid_mime'), array('file_name' => $file['file_name'], 'directory_id' => $dir['id'])); } $thumb_info = $this->get_thumb($file['file_name'], $dir['id']); // Build list of information to save and return $file_data = array('upload_location_id' => $dir['id'], 'site_id' => $this->EE->config->item('site_id'), 'file_name' => $file['file_name'], 'orig_name' => $original_filename, 'file_data_orig_name' => $file['orig_name'], 'is_image' => $file['is_image'], 'mime_type' => $file['file_type'], 'rel_path' => $file['full_path'], 'file_thumb' => $thumb_info['thumb'], 'thumb_class' => $thumb_info['thumb_class'], 'modified_by_member_id' => $this->EE->session->userdata('member_id'), 'uploaded_by_member_id' => $this->EE->session->userdata('member_id'), 'file_size' => $file['file_size'] * 1024, 'file_height' => $file['image_height'], 'file_width' => $file['image_width'], 'file_hw_original' => $file['image_height'] . ' ' . $file['image_width'], 'max_width' => $dir['max_width'], 'max_height' => $dir['max_height']); // Check to see if its an editable image, if it is, check max h/w if ($this->is_editable_image($file['full_path'], $file['file_type'])) { $file_data = $this->max_hw_check($file['full_path'], $file_data); if (!$file_data) { return $this->_upload_error(lang('exceeds_max_dimensions'), array('file_name' => $file['file_name'], 'directory_id' => $dir['id'])); } } // Save file to database $saved = $this->save_file($file['full_path'], $dir['id'], $file_data); // Return errors from the filemanager if (!$saved['status']) { return $this->_upload_error($saved['message'], array('file_name' => $file['file_name'], 'directory_id' => $dir['id'])); } // Merge in information from database $file_data = array_merge($file_data, $this->_file_info($saved['file_id'])); // Stash upload directory prefs in case $file_data['upload_directory_prefs'] = $dir; $file_data['directory'] = $dir['id']; // Change file size to human readable $this->EE->load->helper('number'); $file_data['file_size'] = byte_format($file_data['file_size']); return $file_data; }
/** * upload prefs * * @access private * @return array upload prefs */ private function upload_prefs() { if (isset($this->cache[$this->field_name]['upload_prefs'])) { return $this->cache[$this->field_name]['upload_prefs']; } $settings = array_merge($this->default_settings, $this->settings); ee()->load->model('file_upload_preferences_model'); $upload_prefs = ee()->file_upload_preferences_model->get_file_upload_preferences(1, $settings['file_upload_location'], TRUE); //seems in newer versions of the file upload prefs model, upload path //isnt set. if (empty($upload_prefs['upload_path']) and !empty($upload_prefs['server_path'])) { $upload_prefs['upload_path'] = $upload_prefs['server_path']; } $extra_options = array('allowed_types' => $settings['allowed_file_types'], 'temp_prefix' => ''); // ------------------------------------- // So if you init upload AFTER construction // it doesn't load xss clean options // if you do it during, it gets overriden // no matter what! Yay! // ------------------------------------- ee()->load->helper('xss'); $extra_options['xss_clean'] = xss_check(); if ($extra_options['xss_clean'] and $settings['disable_xss_clean'] == 'y') { $extra_options['xss_clean'] = FALSE; } $this->cache[$this->field_name]['upload_prefs'] = array_merge($upload_prefs, $extra_options); return $this->cache[$this->field_name]['upload_prefs']; }
/** * Parses out file data and saves it to moblog upload directory * * @param string $value Raw email data * @param string $type Type of content (first half of MIME) * @param string $subtype Subtype type of content (second half of MIME) * @return boolean Success or failure */ private function _process_attachment($value, $type, $subtype) { $upload_dir_id = $this->moblog_array['moblog_upload_directory']; if ($upload_dir_id != 0) { $this->upload_dir_code = '{filedir_' . $upload_dir_id . '}'; } // no upload directory? skip if ($upload_dir_id == 0) { continue; } if ($subtype == 'appledouble') { if (!($data = $this->appledouble($value))) { continue; } else { $value = $data['value']; $subtype = $data['subtype']; $type = $data['type']; unset($data); } } /** ------------------------------ /** Determine Filename /** ------------------------------*/ $contents = $this->find_data($value, "name=", $this->newline); if ($contents == '') { $contents = $this->find_data($value, 'Content-Location: ', $this->newline); } if ($contents == '') { $contents = $this->find_data($value, 'Content-ID: ', $this->newline); $contents = str_replace('<', '', $contents); $contents = str_replace('<', '', $contents); } $x = explode(';', trim($contents)); $filename = $x['0'] == '' ? 'moblogfile' : $x['0']; $filename = trim(str_replace('"', '', $filename)); $filename = str_replace($this->newline, '', $filename); if (stristr($filename, 'dottedline') or stristr($filename, 'spacer.gif') or stristr($filename, 'masthead.jpg')) { continue; } /** -------------------------------- /** File/Image Code and Cleanup /** --------------------------------*/ $duo = $this->newline . $this->newline; $file_code = $this->find_data($value, $duo, ''); if ($file_code == '') { $file_code = $this->find_data($value, $this->newline, ''); if ($file_code == '') { $this->message_array = 'invalid_file_data'; return FALSE; } } /** -------------------------------- /** Determine Encoding /** --------------------------------*/ $contents = $this->find_data($value, "Content-Transfer-Encoding: ", $this->newline); $x = explode(';', $contents); $encoding = $x['0']; $encoding = trim(str_replace('"', '', $encoding)); $encoding = str_replace($this->newline, '', $encoding); if (!stristr($encoding, "base64") && !stristr($encoding, "7bit") && !stristr($encoding, "8bit") && !stristr($encoding, "quoted-printable")) { if ($type == 'text') { // RTF and HTML are considered alternative text $subtype = $subtype != 'html' && $subtype != 'rtf' ? 'plain' : 'alt'; // Same content type, then join together $this->post_data[$type][$subtype] = isset($this->post_data[$type][$subtype]) ? $this->post_data[$type][$subtype] . ' ' . $file_code : $file_code; // Plain text takes priority for body data. $this->body = !isset($this->post_data[$type]['plain']) ? $this->post_data[$type]['alt'] : $this->post_data[$type]['plain']; } continue; } // Eudora and Mail.app use this by default if (stristr($encoding, "quoted-printable")) { $file_code = quoted_printable_decode($file_code); } // Base64 gets no space and no line breaks $replace = !stristr($encoding, "base64") ? "\n" : ''; $file_code = trim(str_replace($this->newline, $replace, $file_code)); // PHP function sometimes misses opening and closing equal signs if (stristr($encoding, "quoted-printable")) { $file_code = substr($file_code, 0, 1) != '=' ? $file_code : substr($file_code, 1); $file_code = substr($file_code, -1) != '=' ? $file_code : substr($file_code, 0, -1); } // Decode so that we can run xss clean on the raw // data once we've determined the file type if (stristr($encoding, "base64")) { $file_code = base64_decode($file_code); $this->message_array[] = 'base64 decoded.'; } /** ------------------------------ /** Check and adjust for multiple files with same file name /** ------------------------------*/ ee()->load->library('filemanager'); $file_path = ee()->filemanager->clean_filename($filename, $upload_dir_id, array('ignore_dupes' => FALSE)); $filename = basename($file_path); /** --------------------------- /** Put Info in Post Data array /** ---------------------------*/ $ext = trim(strrchr($filename, '.'), '.'); $is_image = FALSE; // This is needed for XSS cleaning if (in_array(strtolower($ext), $this->movie)) { $this->post_data['movie'][] = $filename; } elseif (in_array(strtolower($ext), $this->audio)) { $this->post_data['audio'][] = $filename; } elseif (in_array(strtolower($ext), $this->image)) { $this->post_data['images'][] = $filename; $key = count($this->post_data['images']) - 1; $type = 'image'; // For those crazy application/octet-stream images $is_image = TRUE; } elseif (in_array(strtolower($ext), $this->files)) { $this->post_data['files'][] = $filename; } else { continue; } // Clean the file ee()->load->helper('xss'); if (xss_check()) { $xss_result = ee()->security->xss_clean($file_code, $is_image); // XSS Clean Failed - bail out if ($xss_result === FALSE) { $this->message_array[] = 'error_writing_attachment'; return FALSE; } if (!$is_image) { $file_code = $xss_result; } } // AT&T phones send the message as a .txt file // This checks to see if this email is from an AT&T phone, // not an encoded file, and has a .txt file extension in the filename if ($this->attach_as_txt === TRUE && !stristr($encoding, "base64")) { if ($ext == 'txt' && preg_match("/Content-Disposition:\\s*inline/i", $headers, $found)) { $this->attach_text = $file_code; $this->attach_name = $filename; continue; // No upload of file. } } // Check to see if we're dealing with relative paths if (strncmp($file_path, '..', 2) == 0) { $directory = dirname($file_path); $file_path = realpath(substr($directory, 1)) . '/' . $filename; } // Upload the file and check for errors if (file_put_contents($file_path, $file_code) === FALSE) { $this->message_array[] = 'error_writing_attachment'; return FALSE; } // Disable xss cleaning in the filemanager ee()->filemanager->xss_clean_off(); // Send the file $result = ee()->filemanager->save_file($file_path, $upload_dir_id, array('title' => $filename, 'rel_path' => dirname($file_path), 'file_name' => $filename)); unset($file_code); // Check to see the result if ($result['status'] === FALSE) { // $result['message'] $this->message_array[] = 'error_writing_attachment'; $this->message_array[] = print_r($result, TRUE); return FALSE; } $this->email_files[] = $filename; $this->uploads++; return TRUE; }