Skip to content

Instantly share code, notes, and snippets.

@skunkbad
Created September 15, 2011 07:01
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save skunkbad/1218696 to your computer and use it in GitHub Desktop.
Save skunkbad/1218696 to your computer and use it in GitHub Desktop.
CodeIgniter Upload class extension for FTP or Database storage
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class MY_Upload extends CI_Upload {
public $destination_not_file_system = FALSE;
public function __construct($props = array())
{
parent::__construct();
if (count($props) > 0)
{
$this->initialize($props);
}
log_message('debug', "Upload Class Initialized");
}
public function initialize($config = array())
{
$defaults = array(
'max_size' => 0,
'max_width' => 0,
'max_height' => 0,
'max_filename' => 0,
'allowed_types' => "",
'file_temp' => "",
'file_name' => "",
'orig_name' => "",
'file_type' => "",
'file_size' => "",
'file_ext' => "",
'upload_path' => "",
'overwrite' => FALSE,
'encrypt_name' => FALSE,
'is_image' => FALSE,
'image_width' => '',
'image_height' => '',
'image_type' => '',
'image_size_str' => '',
'error_msg' => array(),
'mimes' => array(),
'remove_spaces' => TRUE,
'xss_clean' => FALSE,
'temp_prefix' => "temp_file_",
'client_name' => '',
// new destination_not_file_system key
'destination_not_file_system' => FALSE
);
foreach ($defaults as $key => $val)
{
if (isset($config[$key]))
{
$method = 'set_'.$key;
if (method_exists($this, $method))
{
$this->$method($config[$key]);
}
else
{
$this->$key = $config[$key];
}
}
else
{
$this->$key = $val;
}
}
// if a file_name was provided in the config, use it instead of the user input
// supplied file name for all uploads until initialized again
$this->_file_name_override = $this->file_name;
}
public function do_upload($field = 'userfile')
{
// Is $_FILES[$field] set? If not, no reason to continue.
if ( ! isset($_FILES[$field]))
{
$this->set_error('upload_no_file_selected');
return FALSE;
}
// Is the upload path valid? (only applies if file system is final destination
if( $this->destination_not_file_system === FALSE )
{
if ( ! $this->validate_upload_path())
{
// errors will already be set by validate_upload_path() so just return FALSE
return FALSE;
}
}
// Was the file able to be uploaded? If not, determine the reason why.
if ( ! is_uploaded_file($_FILES[$field]['tmp_name']))
{
$error = ( ! isset($_FILES[$field]['error'])) ? 4 : $_FILES[$field]['error'];
switch($error)
{
case 1: // UPLOAD_ERR_INI_SIZE
$this->set_error('upload_file_exceeds_limit');
break;
case 2: // UPLOAD_ERR_FORM_SIZE
$this->set_error('upload_file_exceeds_form_limit');
break;
case 3: // UPLOAD_ERR_PARTIAL
$this->set_error('upload_file_partial');
break;
case 4: // UPLOAD_ERR_NO_FILE
$this->set_error('upload_no_file_selected');
break;
case 6: // UPLOAD_ERR_NO_TMP_DIR
$this->set_error('upload_no_temp_directory');
break;
case 7: // UPLOAD_ERR_CANT_WRITE
$this->set_error('upload_unable_to_write_file');
break;
case 8: // UPLOAD_ERR_EXTENSION
$this->set_error('upload_stopped_by_extension');
break;
default : $this->set_error('upload_no_file_selected');
break;
}
return FALSE;
}
// Set the uploaded data as class variables
$this->file_temp = $_FILES[$field]['tmp_name'];
$this->file_size = $_FILES[$field]['size'];
$this->file_type = preg_replace("/^(.+?);.*$/", "\\1", $_FILES[$field]['type']);
$this->file_type = strtolower(trim(stripslashes($this->file_type), '"'));
$this->file_name = $this->_prep_filename($_FILES[$field]['name']);
$this->file_ext = $this->get_extension($this->file_name);
$this->client_name = $this->file_name;
// Is the file type allowed to be uploaded?
if ( ! $this->is_allowed_filetype())
{
$this->set_error('upload_invalid_filetype');
return FALSE;
}
// if we're overriding, let's now make sure the new name and type is allowed
if ($this->_file_name_override != '')
{
$this->file_name = $this->_prep_filename($this->_file_name_override);
// If no extension was provided in the file_name config item, use the uploaded one
if (strpos($this->_file_name_override, '.') === FALSE)
{
$this->file_name .= $this->file_ext;
}
// An extension was provided, lets have it!
else
{
$this->file_ext = $this->get_extension($this->_file_name_override);
}
if ( ! $this->is_allowed_filetype(TRUE))
{
$this->set_error('upload_invalid_filetype');
return FALSE;
}
}
// Convert the file size to kilobytes
if ($this->file_size > 0)
{
$this->file_size = round($this->file_size/1024, 2);
}
// Is the file size within the allowed maximum?
if ( ! $this->is_allowed_filesize())
{
$this->set_error('upload_invalid_filesize');
return FALSE;
}
// Are the image dimensions within the allowed size?
// Note: This can fail if the server has an open_basdir restriction.
if ( ! $this->is_allowed_dimensions())
{
$this->set_error('upload_invalid_dimensions');
return FALSE;
}
// Sanitize the file name for security
$this->file_name = $this->clean_file_name($this->file_name);
// Truncate the file name if it's too long
if ($this->max_filename > 0)
{
$this->file_name = $this->limit_filename_length($this->file_name, $this->max_filename);
}
// Remove white spaces in the name
if ($this->remove_spaces == TRUE)
{
$this->file_name = preg_replace("/\s+/", "_", $this->file_name);
}
/*
* Validate the file name
* This function appends an number onto the end of
* the file if one with the same name already exists.
* If it returns false there was a problem.
*/
$this->orig_name = $this->file_name;
if ($this->overwrite == FALSE)
{
$this->file_name = $this->set_filename($this->upload_path, $this->file_name);
if ($this->file_name === FALSE)
{
return FALSE;
}
}
/*
* Run the file through the XSS hacking filter
* This helps prevent malicious code from being
* embedded within a file. Scripts can easily
* be disguised as images or other file types.
*/
if ($this->xss_clean)
{
if ($this->do_xss_clean() === FALSE)
{
$this->set_error('upload_unable_to_write_file');
return FALSE;
}
}
if( $this->destination_not_file_system === FALSE )
{
/*
* Move the file to the final destination
* To deal with different server configurations
* we'll attempt to use copy() first. If that fails
* we'll use move_uploaded_file(). One of the two should
* reliably work in most environments
*/
if ( ! @copy($this->file_temp, $this->upload_path.$this->file_name))
{
if ( ! @move_uploaded_file($this->file_temp, $this->upload_path.$this->file_name))
{
$this->set_error('upload_destination_error');
return FALSE;
}
}
/*
* Set the finalized image dimensions for
* files saved to the file system.
* This sets the image width/height (assuming the
* file was an image). We use this information
* in the "data" function.
*/
$this->set_image_properties($this->upload_path.$this->file_name);
return TRUE;
}
else
{
/*
* Set the finalized image dimensions for
* files that were not saved to the file system.
* This sets the image width/height (assuming the
* file was an image). We use this information
* in the "data" function.
*/
$this->set_image_properties( $this->file_temp );
return TRUE;
}
}
}
/* End of file MY_Upload.php */
/* Location: ./application/libraries/MY_Upload.php */
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class Uploads_manager extends MY_Controller {
public $upload_details = array();
private $upload_dir;
private $primary_dir = FALSE;
private $secondary_dir = FALSE;
private $tertiary_dir = FALSE;
private $quaternary_dir = FALSE;
private $success_callback;
public function __construct()
{
parent::__construct();
$this->config->load('uploads_manager_config');
$this->load->model('uploads_manager_model');
}
/*
* BRIDGE DATABASE method does most of the work behind
* uploading a file whos destination is the database.
*
* Argument 1 - $type - used as a suffix to the upload config set.
* Argument 2 = $return - set to TRUE to return array instead of json.
*
* A mandatory callback function name must be supplied by post
* element "success_callback"
*
*/
public function bridge_database( $type, $return = FALSE )
{
if($this->require_role('Sudo,Admin,VPM,RM,PM'))
{
// Use CSRF protection
$this->load->library('csrf');
// Check if a valid form submission has been made
if( $this->csrf->token_match )
{
// Get special upload config for database storage
$init_config = config_item( 'db_storage_configuration_' . $type );
// Load and initialize file upload class
$this->load->library('upload', $init_config);
// Use upload library for file validation
if ( $this->upload->do_upload() )
{
$handle = fopen( $this->upload->file_temp, "r" );
$file_string = base64_encode( fread( $handle, filesize( $this->upload->file_temp ) ) );
fclose($handle);
// Need to have callback to handle the specific upload details
if( empty( $this->success_callback ) )
{
$this->success_callback = $this->input->post('success_callback');
}
// Need to have callback handle the insertion to DB
if( ! empty( $this->success_callback ) )
{
if( method_exists($this, $this->success_callback) )
{
$callback = $this->success_callback;
// Pass the file string to the callback
if( $callback_response = $this->$callback( $file_string ) )
{
// Send Success Response
$response['status'] = 'success';
$response['callback_response'] = $callback_response;
}
else
{
// Error: Callback Failed
$response['status'] = 'error';
$response['issue'] = 'Callback failed.';
}
}
else
{
// Error: Callback Doesn't Exist
$response['status'] = 'error';
$response['issue'] = 'Callback does not exist.';
}
}
else
{
// Error: No Callback Specified
$response['status'] = 'error';
$response['issue'] = 'No callback specified.';
}
}
else
{
// Error: Upload Failed
$response['status'] = 'error';
$response['issue'] = 'Upload failed. Please try again later, or contact your supervisor.';
}
}
else
{
// Error: No Token Match
$response['status'] = 'error';
$response['issue'] = 'No Token Match. Please reload the page.';
}
$response['token'] = $this->csrf->token;
if( $return )
{
return $response;
}
else
{
echo json_encode( $response );
}
}
}
/*
* BRIDGE FTP method does most of the work behind
* uploading a file whos destination is another server.
*
* Argument 1 - $type - used as a suffix to the upload config set.
* Is is also the prefix of the FTP config set.
* Argument 2 = $return - set to TRUE to return array instead of json.
*
* A mandatory callback function name must be supplied by post
* element "success_callback"
*
*/
public function bridge_ftp( $type, $return = FALSE )
{
if($this->require_role('Sudo,Admin,VPM,RM,PM'))
{
// Use CSRF protection
$this->load->library('csrf');
// Check if a valid form submission has been made
if( $this->csrf->token_match )
{
// Get special upload config for FTP transfer
$init_config = config_item( 'ftp_configuration_' . $type );
// Load and initialize file upload class
$this->load->library('upload', $init_config);
// Use upload library for file validation
if ( $this->upload->do_upload() )
{
// FTP the file to the backoffice
$this->load->library('ftp');
$this->config->load('ftp');
$ftp_config = config_item( $type . '_ftp_settings' );
$this->ftp->connect( $ftp_config );
if( $this->ftp->upload( $this->upload->file_temp, $ftp_config['remote_directory'] . $this->upload->file_name ) )
{
// Need to have callback to handle the specific upload details
if( empty( $this->success_callback ) )
{
$this->success_callback = $this->input->post('success_callback');
}
// If success callback supplied, run it
if( ! empty( $this->success_callback ) )
{
if( method_exists($this, $this->success_callback) )
{
$callback = $this->success_callback;
if( $callback_response = $this->$callback() )
{
// Send Success Response ( Callback Used )
$response['status'] = 'success';
$response['callback_response'] = $callback_response;
}
else
{
// Error: Callback Failed
$response['status'] = 'error';
$response['issue'] = 'Callback failed.';
}
}
else
{
// Error: Callback Doesn't Exist
$response['status'] = 'error';
$response['issue'] = 'Callback does not exist.';
}
}
else
{
// Send Success Response ( No Callback Used )
$response['status'] = 'success';
}
}
else
{
// Get FTP Errors
$errors = $this->ftp->error_stack;
$formatted_errors = '';
foreach( $errors as $error )
{
$formatted_errors .= $error . "\n";
}
// Send FTP Errors as Response
$response['status'] = 'error';
$response['issue'] = $formatted_errors;
}
$this->ftp->close();
}
else
{
// Error: Upload Failed
$response['status'] = 'error';
$response['issue'] = 'Upload failed. Please try again later, or contact your supervisor.';
}
}
else
{
// Error: No Token Match
$response['status'] = 'error';
$response['issue'] = 'No Token Match. Please reload the page.';
}
$response['token'] = $this->csrf->token;
if( $return )
{
return $response;
}
else
{
echo json_encode( $response );
}
}
}
}
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
// upload_dir must be a single public root level directory
$config['upload_dir'] = 'site_uploads';
// FILE SYSTEM ---------------------------------------------
$config['filesystem_configuration_documents'] = array(
'allowed_types' => 'doc|docx|pdf|txt|xls',
'max_size' => '100',
);
$config['filesystem_configuration_listing_image'] = array(
'allowed_types' => 'gif|jpg|jpeg|png',
'max_size' => '250',
'max_width' => '480',
'max_height' => '360',
'primary_dir' => 'region_directory', // <- post key of primary directory
'secondary_dir' => 'unit_id' // <- post key of secondary directory
);
// ----------------------------------------------------------
// FTP ------------------------------------------------------
$config['ftp_configuration_report'] = array(
'allowed_types' => 'pdf',
'max_size' => '2000',
'destination_not_file_system' => TRUE
);
// ----------------------------------------------------------
// DATABASE -------------------------------------------------
$config['db_storage_configuration_photo'] = array(
'allowed_types' => 'gif|jpg|jpeg|png',
'max_size' => '256',
'destination_not_file_system' => TRUE
);
// ----------------------------------------------------------
/* End of file uploads_manager_config.php */
/* Location: /application/config/uploads_manager_config.php */
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment