public function upload_file($source_file, $dest_filename, $dest_folder, $checkExists = true) { $out = array('success' => FALSE, 'filename' => ''); $this->init(); if ($dest_folder !== FALSE) { $dest_folder .= '/'; } else { $dest_folder = ''; } // Extension $extension = substr(strrchr($source_file, '.'), 1); // Subdirectory? $subdir = isset($this->lsettings['directory']) == TRUE && $this->lsettings['directory'] != FALSE ? $this->lsettings['directory'] . '/' : ''; $filename_no_ext = str_replace('.' . $extension, '', $dest_filename); if ($checkExists) { // Does it already exists? if ($this->S3->if_object_exists($this->lsettings['bucket'], $subdir . $dest_folder . $dest_filename)) { for ($i = 2; $i < 30; $i++) { if ($this->S3->if_object_exists($this->lsettings['bucket'], $subdir . $dest_folder . "{$filename_no_ext}_{$i}.{$extension}") == FALSE) { $dest_filename = "{$filename_no_ext}_{$i}.{$extension}"; break; } } } } // Mime type if (class_exists('CFMimeTypes') == FALSE) { include PATH_THIRD . 'channel_files/libraries/mimetypes.class.php'; } $MIME = new CFMimeTypes(); $filemime = $MIME->get_mimetype($extension); $upload_arr = array(); $upload_arr['fileUpload'] = $source_file; $upload_arr['contentType'] = $filemime; $upload_arr['acl'] = $this->lsettings['acl']; $upload_arr['storage'] = $this->lsettings['storage']; $upload_arr['headers'] = array(); if (isset($this->lsettings['force_download']) == TRUE && $this->lsettings['force_download'] == 'yes') { $upload_arr['headers']['Content-Disposition'] = 'attachment'; } $filesize = @filesize($source_file); // MultiPart ? if ($filesize > 31457280) { $upload_arr['partSize'] = 6291456; // 6MB Part Size $response = $this->S3->create_mpu_object($this->lsettings['bucket'], $subdir . $dest_folder . $dest_filename, $upload_arr); } else { $response = $this->S3->create_object($this->lsettings['bucket'], $subdir . $dest_folder . $dest_filename, $upload_arr); } // Success? if ($response->isOK()) { $out['success'] = TRUE; $out['filename'] = $dest_filename; } else { return FALSE; } return $out; }
/** * Authenticates a connection to Amazon S3. Do not use directly unless implementing custom methods for * this class. * * @param string $operation (Required) The name of the bucket to operate on (S3 Only). * @param array $payload (Required) An associative array of parameters for authenticating. See inline comments for allowed keys. * @return CFResponse A <CFResponse> object containing a parsed HTTP response. * @link http://docs.amazonwebservices.com/AmazonS3/latest/dev/S3_Authentication.html REST authentication */ public function authenticate($operation, $payload) { /* * Overriding or extending this class? You can pass the following "magic" keys into $opt. * * ## verb, resource, sub_resource and query_string ## * <verb> /<resource>?<sub_resource>&<query_string> * GET /filename.txt?versions&prefix=abc&max-items=1 * * ## versionId, uploadId, partNumber, response-* ## * These don't follow the same rules as above, in that the they needs to be signed, while * other query_string values do not. * * ## curlopts ## * These values get passed directly to the cURL methods in RequestCore. * * ## fileUpload, fileDownload, seekTo ## * These are slightly modified and then passed to the cURL methods in RequestCore. * * ## headers ## * $opt['headers'] is an array, whose keys are HTTP headers to be sent. * * ## body ## * This is the request body that is sent to the server via PUT/POST. * * ## preauth ## * This is a hook that tells authenticate() to generate a pre-authenticated URL. * * ## returnCurlHandle ## * Tells authenticate() to return the cURL handle for the request instead of executing it. */ // Rename variables (to overcome inheritence issues) $bucket = $operation; $opt = $payload; // Validate the S3 bucket name if (!$this->validate_bucketname_support($bucket)) { // @codeCoverageIgnoreStart throw new S3_Exception('S3 does not support "' . $bucket . '" as a valid bucket name. Review "Bucket Restrictions and Limitations" in the S3 Developer Guide for more information.'); // @codeCoverageIgnoreEnd } // Die if $opt isn't set. if (!$opt) { return false; } $method_arguments = func_get_args(); // Use the caching flow to determine if we need to do a round-trip to the server. if ($this->use_cache_flow) { // Generate an identifier specific to this particular set of arguments. $cache_id = $this->key . '_' . get_class($this) . '_' . $bucket . '_' . sha1(serialize($method_arguments)); // Instantiate the appropriate caching object. $this->cache_object = new $this->cache_class($cache_id, $this->cache_location, $this->cache_expires, $this->cache_compress); if ($this->delete_cache) { $this->use_cache_flow = false; $this->delete_cache = false; return $this->cache_object->delete(); } // Invoke the cache callback function to determine whether to pull data from the cache or make a fresh request. $data = $this->cache_object->response_manager(array($this, 'cache_callback'), $method_arguments); if ($this->parse_the_response) { // Parse the XML body $data = $this->parse_callback($data); } // End! return $data; } // If we haven't already set a resource prefix and the bucket name isn't DNS-valid... if (!$this->resource_prefix && !$this->validate_bucketname_create($bucket) || $this->path_style) { // Fall back to the older path-style URI $this->set_resource_prefix('/' . $bucket); $this->temporary_prefix = true; } // Determine hostname $scheme = $this->use_ssl ? 'https://' : 'http://'; if ($this->resource_prefix || $this->path_style) { $hostname = $this->hostname . $this->resource_prefix . ($bucket === '' || $this->resource_prefix === '/' . $bucket ? '' : '/' . $bucket); } else { $hostname = $this->vhost ? $this->vhost : ($bucket === '' ? $this->hostname : $bucket . '.' . $this->hostname); } // Get the UTC timestamp in RFC 2616 format $date = gmdate(CFUtilities::DATE_FORMAT_RFC2616, time()); // Storage for request parameters. $resource = ''; $sub_resource = ''; $querystringparams = array(); $signable_querystringparams = array(); $string_to_sign = ''; $headers = array('Content-MD5' => '', 'Content-Type' => 'application/x-www-form-urlencoded', 'Date' => $date); /*%******************************************************************************************%*/ // Do we have an authentication token? if ($this->auth_token) { $headers['X-Amz-Security-Token'] = $this->auth_token; } // Handle specific resources if (isset($opt['resource'])) { $resource .= $opt['resource']; } // Merge query string values if (isset($opt['query_string'])) { $querystringparams = array_merge($querystringparams, $opt['query_string']); } $query_string = $this->util->to_query_string($querystringparams); // Merge the signable query string values. Must be alphabetical. $signable_list = array('partNumber', 'response-cache-control', 'response-content-disposition', 'response-content-encoding', 'response-content-language', 'response-content-type', 'response-expires', 'uploadId', 'versionId'); foreach ($signable_list as $item) { if (isset($opt[$item])) { $signable_querystringparams[$item] = $opt[$item]; } } $signable_query_string = $this->util->to_query_string($signable_querystringparams); // Merge the HTTP headers if (isset($opt['headers'])) { $headers = array_merge($headers, $opt['headers']); } // Compile the URI to request $conjunction = '?'; $signable_resource = '/' . str_replace('%2F', '/', rawurlencode($resource)); $non_signable_resource = ''; if (isset($opt['sub_resource'])) { $signable_resource .= $conjunction . rawurlencode($opt['sub_resource']); $conjunction = '&'; } if ($signable_query_string !== '') { $signable_query_string = $conjunction . $signable_query_string; $conjunction = '&'; } if ($query_string !== '') { $non_signable_resource .= $conjunction . $query_string; $conjunction = '&'; } if (substr($hostname, -1) === substr($signable_resource, 0, 1)) { $signable_resource = ltrim($signable_resource, '/'); } $this->request_url = $scheme . $hostname . $signable_resource . $signable_query_string . $non_signable_resource; if (isset($opt['location'])) { $this->request_url = $opt['location']; } // Gather information to pass along to other classes. $helpers = array('utilities' => $this->utilities_class, 'request' => $this->request_class, 'response' => $this->response_class); // Instantiate the request class $request = new $this->request_class($this->request_url, $this->proxy, $helpers, $this->credentials); // Update RequestCore settings $request->request_class = $this->request_class; $request->response_class = $this->response_class; $request->ssl_verification = $this->ssl_verification; // Pass along registered stream callbacks if ($this->registered_streaming_read_callback) { $request->register_streaming_read_callback($this->registered_streaming_read_callback); } if ($this->registered_streaming_write_callback) { $request->register_streaming_write_callback($this->registered_streaming_write_callback); } // Streaming uploads if (isset($opt['fileUpload'])) { if (is_resource($opt['fileUpload'])) { // Determine the length to read from the stream $length = null; // From current position until EOF by default, size determined by set_read_stream() if (isset($headers['Content-Length'])) { $length = $headers['Content-Length']; } elseif (isset($opt['seekTo'])) { // Read from seekTo until EOF by default $stats = fstat($opt['fileUpload']); if ($stats && $stats['size'] >= 0) { $length = $stats['size'] - (int) $opt['seekTo']; } } $request->set_read_stream($opt['fileUpload'], $length); if ($headers['Content-Type'] === 'application/x-www-form-urlencoded') { $headers['Content-Type'] = 'application/octet-stream'; } } else { $request->set_read_file($opt['fileUpload']); // Determine the length to read from the file $length = $request->read_stream_size; // The file size by default if (isset($headers['Content-Length'])) { $length = $headers['Content-Length']; } elseif (isset($opt['seekTo']) && isset($length)) { // Read from seekTo until EOF by default $length -= (int) $opt['seekTo']; } $request->set_read_stream_size($length); // Attempt to guess the correct mime-type if ($headers['Content-Type'] === 'application/x-www-form-urlencoded') { $extension = explode('.', $opt['fileUpload']); $extension = array_pop($extension); $mime_type = CFMimeTypes::get_mimetype($extension); $headers['Content-Type'] = $mime_type; } } $headers['Content-Length'] = $request->read_stream_size; $headers['Content-MD5'] = ''; } // Handle streaming file offsets if (isset($opt['seekTo'])) { // Pass the seek position to RequestCore $request->set_seek_position((int) $opt['seekTo']); } // Streaming downloads if (isset($opt['fileDownload'])) { if (is_resource($opt['fileDownload'])) { $request->set_write_stream($opt['fileDownload']); } else { $request->set_write_file($opt['fileDownload']); } } $curlopts = array(); // Set custom CURLOPT settings if (isset($opt['curlopts'])) { $curlopts = $opt['curlopts']; } // Debug mode if ($this->debug_mode) { $curlopts[CURLOPT_VERBOSE] = true; } // Set the curl options. if (count($curlopts)) { $request->set_curlopts($curlopts); } // Do we have a verb? if (isset($opt['verb'])) { $request->set_method($opt['verb']); $string_to_sign .= $opt['verb'] . "\n"; } // Add headers and content when we have a body if (isset($opt['body'])) { $request->set_body($opt['body']); $headers['Content-Length'] = strlen($opt['body']); if ($headers['Content-Type'] === 'application/x-www-form-urlencoded') { $headers['Content-Type'] = 'application/octet-stream'; } if (!isset($opt['NoContentMD5']) || $opt['NoContentMD5'] !== true) { $headers['Content-MD5'] = $this->util->hex_to_base64(md5($opt['body'])); } } // Handle query-string authentication if (isset($opt['preauth']) && (int) $opt['preauth'] > 0) { unset($headers['Date']); $headers['Content-Type'] = ''; $headers['Expires'] = is_int($opt['preauth']) ? $opt['preauth'] : strtotime($opt['preauth']); } // Sort headers uksort($headers, 'strnatcasecmp'); // Add headers to request and compute the string to sign foreach ($headers as $header_key => $header_value) { // Strip linebreaks from header values as they're illegal and can allow for security issues $header_value = str_replace(array("\r", "\n"), '', $header_value); // Add the header if it has a value if ($header_value !== '') { $request->add_header($header_key, $header_value); } // Generate the string to sign if (strtolower($header_key) === 'content-md5' || strtolower($header_key) === 'content-type' || strtolower($header_key) === 'date' || strtolower($header_key) === 'expires' && isset($opt['preauth']) && (int) $opt['preauth'] > 0) { $string_to_sign .= $header_value . "\n"; } elseif (substr(strtolower($header_key), 0, 6) === 'x-amz-') { $string_to_sign .= strtolower($header_key) . ':' . $header_value . "\n"; } } // Add the signable resource location $string_to_sign .= $this->resource_prefix ? $this->resource_prefix : ''; $string_to_sign .= ($bucket === '' || $this->resource_prefix === '/' . $bucket ? '' : '/' . $bucket) . $signable_resource . urldecode($signable_query_string); // Hash the AWS secret key and generate a signature for the request. $signature = base64_encode(hash_hmac('sha1', $string_to_sign, $this->secret_key, true)); $request->add_header('Authorization', 'AWS ' . $this->key . ':' . $signature); // If we're generating a URL, return the URL to the calling method. if (isset($opt['preauth']) && (int) $opt['preauth'] > 0) { $query_params = array('AWSAccessKeyId' => $this->key, 'Expires' => $headers['Expires'], 'Signature' => $signature); // If using short-term credentials, add the token to the query string if ($this->auth_token) { $query_params['x-amz-security-token'] = $this->auth_token; } return $this->request_url . $conjunction . http_build_query($query_params, '', '&'); } elseif (isset($opt['preauth'])) { return $this->request_url; } /*%******************************************************************************************%*/ // If our changes were temporary, reset them. if ($this->temporary_prefix) { $this->temporary_prefix = false; $this->resource_prefix = null; } // Manage the (newer) batch request API or the (older) returnCurlHandle setting. if ($this->use_batch_flow) { $handle = $request->prep_request(); $this->batch_object->add($handle); $this->use_batch_flow = false; return $handle; } elseif (isset($opt['returnCurlHandle']) && $opt['returnCurlHandle'] === true) { return $request->prep_request(); } // Send! $request->send_request(); // Prepare the response $headers = $request->get_response_header(); $headers['x-aws-request-url'] = $this->request_url; $headers['x-aws-redirects'] = $this->redirects; $headers['x-aws-stringtosign'] = $string_to_sign; $headers['x-aws-requestheaders'] = $request->request_headers; // Did we have a request body? if (isset($opt['body'])) { $headers['x-aws-requestbody'] = $opt['body']; } $data = new $this->response_class($headers, $this->parse_callback($request->get_response_body()), $request->get_response_code()); // Did Amazon tell us to redirect? Typically happens for multiple rapid requests EU datacenters. // @see: http://docs.amazonwebservices.com/AmazonS3/latest/dev/Redirects.html // @codeCoverageIgnoreStart if ((int) $request->get_response_code() === 307) { $this->redirects++; $opt['location'] = $headers['location']; $data = $this->authenticate($bucket, $opt); } elseif ((int) $request->get_response_code() === 500 || (int) $request->get_response_code() === 503) { if ($this->redirects <= $this->max_retries) { // Exponential backoff $delay = (int) (pow(4, $this->redirects) * 100000); usleep($delay); $this->redirects++; $data = $this->authenticate($bucket, $opt); } } // @codeCoverageIgnoreEnd // Return! $this->redirects = 0; return $data; }
/** * Method: authenticate() * Authenticates a connection to Amazon S3. Do not use directly unless implementing custom methods for * this class. * * Access: * public * * Parameters: * $bucket - _string_ (Required) The name of the bucket to use. * $opt - _array_ (Optional) An associative array of parameters for authenticating. See the individual methods for allowed keys. * $location - _string_ (Do Not Use) Used internally by this function on occasions when Amazon S3 returns a redirect code and it needs to call itself recursively. * $redirects - _integer_ (Do Not Use) Used internally by this function on occasions when Amazon S3 returns a redirect code and it needs to call itself recursively. * * Returns: * _CFResponse_ A <CFResponse> object containing a parsed HTTP response. * * See Also: * [REST authentication](http://docs.amazonwebservices.com/AmazonS3/latest/RESTAuthentication.html) */ public function authenticate($bucket, $opt = null, $location = null, $redirects = 0, $nothing = null) { /* * Overriding or extending this class? You can pass the following "magic" keys into $opt. * * ## verb, resource, sub_resource and query_string ## * <verb> /<resource>?<sub_resource>&<query_string> * GET /filename.txt?versions&prefix=abc&max-items=1 * * ## versionId ## * versionId doesn't follow the same rules as above, in that the versionId needs to be * signed, while other query_string values do not. * * ## curlopts, fileUpload and fileDownload ## * These values get passed directly to the cURL methods in RequestCore. * * ## headers ## * $opt['headers'] is an array, whose keys are HTTP headers to be sent. * * ## body ## * This is the request body that is sent to the server via POST. * * ## preauth ## * This is a hook that tells authenticate() to generate a pre-authenticated URL. * * ## returnCurlHandle ## * Tells authenticate() to return the cURL handle for the request instead of executing it. */ /** * @todo: Handle duplicate headers with different values. */ // Validate the S3 bucket name if (!$this->validate_bucketname_support($bucket)) { throw new S3_Exception('S3 does not support "' . $bucket . '" as a valid bucket name. Review "Bucket Restrictions and Limitations" in the S3 Developer Guide for more information.'); } // Die if $opt isn't set. if (!$opt) { return false; } $method_arguments = func_get_args(); // Use the caching flow to determine if we need to do a round-trip to the server. if ($this->use_cache_flow) { // Generate an identifier specific to this particular set of arguments. $cache_id = $this->key . '_' . get_class($this) . '_' . $bucket . '_' . sha1(serialize($method_arguments)); // Instantiate the appropriate caching object. $this->cache_object = new $this->cache_class($cache_id, $this->cache_location, $this->cache_expires, $this->cache_compress); if ($this->delete_cache) { $this->use_cache_flow = false; $this->delete_cache = false; return $this->cache_object->delete(); } // Invoke the cache callback function to determine whether to pull data from the cache or make a fresh request. $data = $this->cache_object->response_manager(array($this, 'cache_callback'), $method_arguments); // Parse the XML body $data = $this->parse_callback($data); // End! return $data; } // If we haven't already set a resource prefix... if (!$this->resource_prefix || $this->path_style) { // And if the bucket name isn't DNS-valid... if (!$this->validate_bucketname_create($bucket) || $this->path_style) { // Fall back to the older path-style URI $this->set_resource_prefix('/' . $bucket); } } // Determine hostname $scheme = $this->use_ssl ? 'https://' : 'http://'; if ($this->resource_prefix || $this->path_style) { $hostname = $this->hostname . $this->resource_prefix . ($bucket === '' || $this->resource_prefix === '/' . $bucket ? '' : '/' . $bucket); } else { $hostname = $this->vhost ? $this->vhost : ($bucket === '' ? $this->hostname : $bucket . '.' . $this->hostname); } // Get the UTC timestamp in RFC 2616 format $date = gmdate($this->util->konst($this->util, 'DATE_FORMAT_RFC2616'), time() + (int) $this->adjust_offset); // Storage for request parameters. $resource = ''; $sub_resource = ''; $query_string_params = array(); $signable_query_string_params = array(); $string_to_sign = ''; $headers = array('Content-MD5' => '', 'Content-Type' => 'application/x-www-form-urlencoded', 'Date' => $date); /*%******************************************************************************************%*/ // Handle specific resources if (isset($opt['resource'])) { $resource .= $opt['resource']; } // Merge query string values if (isset($opt['query_string'])) { $query_string_params = array_merge($query_string_params, $opt['query_string']); } $query_string = $this->util->to_query_string($query_string_params); // Merge the signable query string values if (isset($opt['versionId'])) { $signable_query_string_params['versionId'] = rawurlencode($opt['versionId']); } $signable_query_string = $this->util->to_query_string($signable_query_string_params); // Merge the HTTP headers if (isset($opt['headers'])) { $headers = array_merge($headers, $opt['headers']); } // Compile the URI to request $conjunction = '?'; $signable_resource = '/' . rawurlencode($resource); $non_signable_resource = ''; if (isset($opt['sub_resource'])) { $signable_resource .= $conjunction . rawurlencode($opt['sub_resource']); $conjunction = '&'; } if ($signable_query_string !== '') { $signable_resource .= $conjunction . $signable_query_string; } if ($query_string !== '') { $non_signable_resource .= $conjunction . $query_string; } $this->request_url = $scheme . $hostname . $signable_resource . $non_signable_resource; // Instantiate the request class $request = new $this->request_class($this->request_url, $this->proxy); // Update RequestCore settings $request->request_class = $this->request_class; $request->response_class = $this->response_class; // Enable debug headers if ($this->debug_mode) { $request->set_curlopts(array(CURLOPT_VERBOSE => true)); } // Streaming uploads if (isset($opt['fileUpload'])) { $request->set_read_file($opt['fileUpload']); // Attempt to guess the correct mime-type if ($headers['Content-Type'] === 'application/x-www-form-urlencoded') { $extension = explode('.', $opt['fileUpload']); $extension = array_pop($extension); $mime_type = CFMimeTypes::get_mimetype($extension); $headers['Content-Type'] = $mime_type; } } // Streaming downloads if (isset($opt['fileDownload'])) { $request->set_write_file($opt['fileDownload']); } // Set custom CURLOPT settings if (isset($opt['curlopts'])) { $request->set_curlopts($opt['curlopts']); unset($opt['curlopts']); } // Do we have a verb? if (isset($opt['verb'])) { $request->set_method($opt['verb']); $string_to_sign .= $opt['verb'] . "\n"; } // Add headers and content when we have a body if (isset($opt['body'])) { $request->set_body($opt['body']); $headers['Content-Length'] = strlen($opt['body']); $headers['Content-MD5'] = $this->util->hex_to_base64(md5($opt['body'])); } // Handle query-string authentication if (isset($opt['preauth']) && (int) $opt['preauth'] > 0) { unset($headers['Date']); $headers['Content-Type'] = ''; $headers['Expires'] = strtotime($opt['preauth']); } // Sort headers uksort($headers, 'strnatcasecmp'); // Add headers to request and compute the string to sign foreach ($headers as $header_key => $header_value) { // Strip linebreaks from header values as they're illegal and can allow for security issues $header_value = str_replace(array("\r", "\n"), '', $header_value); // Add the header if it has a value if ($header_value !== '') { $request->add_header($header_key, $header_value); } // Generate the string to sign if (strtolower($header_key) === 'content-md5' || strtolower($header_key) === 'content-type' || strtolower($header_key) === 'date' || strtolower($header_key) === 'expires' && isset($opt['preauth']) && (int) $opt['preauth'] > 0) { $string_to_sign .= $header_value . "\n"; } elseif (substr(strtolower($header_key), 0, 6) === 'x-amz-') { $string_to_sign .= strtolower($header_key) . ':' . $header_value . "\n"; } } // Add the signable resource location $string_to_sign .= $this->resource_prefix ? $this->resource_prefix : ''; $string_to_sign .= ($bucket === '' || $this->resource_prefix === '/' . $bucket ? '' : '/' . $bucket) . $signable_resource; // Hash the AWS secret key and generate a signature for the request. $signature = base64_encode(hash_hmac('sha1', $string_to_sign, $this->secret_key, true)); $request->add_header('Authorization', 'AWS ' . $this->key . ':' . $signature); // If we're generating a URL, return certain data to the calling method. if (isset($opt['preauth']) && (int) $opt['preauth'] > 0) { return $this->request_url . (isset($opt['sub_resource']) ? '&' : '?') . 'AWSAccessKeyId=' . $this->key . '&Expires=' . $headers['Expires'] . '&Signature=' . rawurlencode($signature); } elseif (isset($opt['preauth'])) { return $this->request_url; } /*%******************************************************************************************%*/ // Manage the (newer) batch request API or the (older) returnCurlHandle setting. if ($this->use_batch_flow) { $handle = $request->prep_request(); $this->batch_object->add($handle); $this->use_batch_flow = false; return $handle; } elseif (isset($opt['returnCurlHandle']) && $opt['returnCurlHandle'] === true) { return $request->prep_request(); } // Send! $request->send_request(); // Prepare the response $headers = $request->get_response_header(); $headers['x-aws-request-url'] = $this->request_url; $headers['x-aws-redirects'] = $redirects; $headers['x-aws-stringtosign'] = $string_to_sign; $headers['x-aws-requestheaders'] = $request->request_headers; // Did we have a request body? if (isset($opt['body'])) { $headers['x-aws-requestbody'] = $opt['body']; } $data = new $this->response_class($headers, $this->parse_callback($request->get_response_body()), $request->get_response_code()); // Did Amazon tell us to redirect? Typically happens for multiple rapid requests EU datacenters. // @see: http://docs.amazonwebservices.com/AmazonS3/latest/Redirects.html if ((int) $request->get_response_code() === 307) { $data = $this->authenticate($bucket, $opt, $headers['location'], ++$redirects); } elseif ((int) $request->get_response_code() === 500 || (int) $request->get_response_code() === 503) { if ($redirects <= $this->max_retries) { // Exponential backoff $delay = (int) (pow(4, $redirects) * 100000); usleep($delay); $data = $this->authenticate($bucket, $opt, null, ++$redirects); } } // Return! return $data; }
private function _process_post_save($data, $draft_action = NULL) { $this->EE->load->library('channel_files_helper'); $this->EE->load->helper('url'); // Are we using Better Workflow? if ($draft_action !== NULL) { $is_draft = 1; $entry_id = $this->settings['entry_id']; $field_id = $this->settings['field_id']; $channel_id = $this->settings['channel_id']; $settings = $this->EE->channel_files_helper->grab_field_settings($field_id); } else { $is_draft = 0; $data = isset($this->EE->session->cache['ChannelFiles']['FieldData'][$this->field_id]) ? $this->EE->session->cache['ChannelFiles']['FieldData'][$this->field_id] : FALSE; $entry_id = $this->settings['entry_id']; $channel_id = $this->EE->input->post('channel_id'); $field_id = $this->field_id; // Grab Settings $settings = $this->settings['channel_files']; } // Moving Channels? if ($this->EE->input->get_post('new_channel') != FALSE) { $channel_id = $this->EE->input->get_post('new_channel'); } // Do we need to skip? if (isset($data['files']) == FALSE) { return; } // Our Key $key = $data['key']; // Mimetype! if (class_exists('CFMimeTypes') == FALSE) { include 'libraries/mimetypes.class.php'; } $MIME = new CFMimeTypes(); // ----------------------------------------- // Entry_id FOLDER? // ----------------------------------------- $entry_id_folder = TRUE; $prefix_entry_id = TRUE; if (isset($settings['entry_id_folder']) && $settings['entry_id_folder'] == 'no') { $entry_id_folder = FALSE; } if (isset($settings['prefix_entry_id']) && $settings['prefix_entry_id'] == 'no') { $prefix_entry_id = FALSE; } // ----------------------------------------- // Load Location // ----------------------------------------- $location_type = $settings['upload_location']; $location_class = 'CF_Location_' . $location_type; $location_settings = $settings['locations'][$location_type]; // Load Main Class if (class_exists('Cfile_location') == FALSE) { require PATH_THIRD . 'channel_files/locations/cfile_location.php'; } // Try to load Location Class if (class_exists($location_class) == FALSE) { $location_file = PATH_THIRD . 'channel_files/locations/' . $location_type . '/' . $location_type . '.php'; require $location_file; } // Init! $LOC = new $location_class($location_settings); // We only create the dir if we this is true if ($entry_id_folder == TRUE) { // Create the DIR! $LOC->create_dir($entry_id); } // Load the API if (class_exists('Channel_Files_API') != TRUE) { include 'api.channel_files.php'; } $API = new Channel_Files_API(); // ----------------------------------------- // Upload all Files // ----------------------------------------- $filenames = array(); $temp_dir = APPPATH . 'cache/channel_files/field_' . $field_id . '/' . $key; // Loop over all files $tempfiles = @scandir($temp_dir); if (is_array($tempfiles) == TRUE) { $dir_entry_id = $entry_id; foreach ($tempfiles as $filename) { if ($filename == '.' or $filename == '..') { continue; } $filepath = $temp_dir . '/' . $filename; $original_filename = $filename; // Entry ID as Folders? if ($entry_id_folder == FALSE) { if ($prefix_entry_id === TRUE) { $filename = $entry_id . '-' . $filename; } $dir_entry_id = FALSE; } // ------------------------------------------- // 'channel_files_download_init' hook. // - Executes before the the upload of a file // if ($this->EE->extensions->active_hook('channel_files_upload_start') === TRUE) { $edata = $this->EE->extensions->universal_call('channel_files_upload_start', $field_id, $entry_id, $filepath, $filename, $dir_entry_id); if ($this->EE->extensions->end_script === TRUE) { return; } } // // ------------------------------------------- $res = $LOC->upload_file($filepath, $filename, $dir_entry_id); if (is_array($res) === TRUE && $res['success'] == TRUE) { if ($res['filename'] != FALSE) { $filenames[$original_filename] = $res['filename']; } } else { $filenames[$original_filename] = $filename; } @unlink($filepath); // ------------------------------------------- // 'channel_files_download_init' hook. // - Executes after the upload of a file, the source file already has been deleted. // if ($this->EE->extensions->active_hook('channel_files_upload_end') === TRUE) { $edata = $this->EE->extensions->universal_call('channel_files_upload_end', $field_id, $entry_id, $filename, $dir_entry_id); if ($this->EE->extensions->end_script === TRUE) { return; } } // // ------------------------------------------- } } @rmdir($temp_dir); // ----------------------------------------- // Grab all the files from the DB // ----------------------------------------- $this->EE->db->select('*'); $this->EE->db->from('exp_channel_files'); $this->EE->db->where('entry_id', $entry_id); $this->EE->db->where('field_id', $field_id); if ($is_draft === 1 && $draft_action == 'update') { $this->EE->db->where('is_draft', 1); } else { $this->EE->db->where('is_draft', 0); } $query = $this->EE->db->get(); // Lets create an file hash! So we can do unique image $dbfiles = array(); foreach ($query->result() as $row) { $dbfiles[] = $row->file_id . $row->filename; } if ($is_draft === 1 && $draft_action == 'create') { $dbfiles = array(); } // Any files? if (count($dbfiles) > 0) { // ----------------------------------------- // Not fresh, lets see whats new. // ----------------------------------------- foreach ($data['files'] as $order => $file) { $file = $this->EE->channel_files_helper->decode_json($file['data']); if (isset($file->delete) == TRUE) { $API->delete_file($file); } /* // If we are creating a new draft, we already copied all data.. So lets kill the ones that came through POST if ($is_draft === 1 && $file->file_id > 0) { // ----------------------------------------- // Old File // ----------------------------------------- $data = array( 'entry_id' => $entry_id, 'field_id' => $field_id, 'channel_id'=> $channel_id, 'title' => $API->process_field_string($file->title), 'url_title' => $API->process_field_string($file->url_title), 'description' => $API->process_field_string($file->description), 'category' => (isset($file->category) == true) ? $file->category : '', 'cffield_1' => $API->process_field_string($file->cffield_1), 'cffield_2' => $API->process_field_string($file->cffield_2), 'cffield_3' => $API->process_field_string($file->cffield_3), 'cffield_4' => $API->process_field_string($file->cffield_4), 'cffield_5' => $API->process_field_string($file->cffield_5), 'file_primary' => $file->primary, 'file_order' => $order, 'is_draft' => $is_draft, ); $this->EE->db->update('exp_channel_files', $data, array('file_id' => $file->file_id)); continue; } */ // Mime type $filemime = $MIME->get_mimetype($file->extension); // Check for linked_fileid if (isset($file->link_file_id) == FALSE) { $file->link_file_id = 0; } $file->link_entry_id = 0; $file->link_channel_id = 0; $file->link_field_id = 0; // Is it a new file? Lets grab it's new filename if (isset($filenames[$file->filename]) === TRUE && $file->file_id < 1) { $file->filename = $filenames[$file->filename]; } if ($this->EE->channel_files_helper->in_multi_array($file->file_id . $file->filename, $dbfiles) === FALSE) { // Grab MD5/Filesize if ($file->link_file_id > 0) { $q = $this->EE->db->select('entry_id, field_id, channel_id, md5, filesize')->from('exp_channel_files')->where('file_id', $file->link_file_id)->limit(1)->get(); $file->link_entry_id = $q->row('entry_id'); $file->link_channel_id = $q->row('channel_id'); $file->link_field_id = $q->row('field_id'); $file->filesize = $q->row('filesize'); $file->md5 = $q->row('md5'); $q->free_result(); } // ----------------------------------------- // New Files // ----------------------------------------- $data = array('site_id' => $this->site_id, 'entry_id' => $entry_id, 'field_id' => $field_id, 'channel_id' => $channel_id, 'member_id' => $this->EE->session->userdata['member_id'], 'is_draft' => $is_draft, 'link_file_id' => $file->link_file_id, 'link_entry_id' => $file->link_entry_id, 'link_channel_id' => $file->link_channel_id, 'link_field_id' => $file->link_field_id, 'filename' => $file->filename, 'extension' => $file->extension, 'mime' => $filemime, 'upload_service' => $location_type, 'title' => $API->process_field_string($file->title), 'url_title' => $API->process_field_string($file->url_title), 'description' => $API->process_field_string($file->description), 'category' => isset($file->category) == true ? $file->category : '', 'cffield_1' => $API->process_field_string($file->cffield_1), 'cffield_2' => $API->process_field_string($file->cffield_2), 'cffield_3' => $API->process_field_string($file->cffield_3), 'cffield_4' => $API->process_field_string($file->cffield_4), 'cffield_5' => $API->process_field_string($file->cffield_5), 'filesize' => $file->filesize, 'md5' => $file->md5, 'file_primary' => $file->primary, 'file_order' => $order, 'date' => $this->EE->localize->now); $this->EE->db->insert('exp_channel_files', $data); } else { // ----------------------------------------- // Old File // ----------------------------------------- $data = array('entry_id' => $entry_id, 'field_id' => $field_id, 'channel_id' => $channel_id, 'title' => $API->process_field_string($file->title), 'url_title' => $API->process_field_string($file->url_title), 'description' => $API->process_field_string($file->description), 'category' => isset($file->category) == true ? $file->category : '', 'cffield_1' => $API->process_field_string($file->cffield_1), 'cffield_2' => $API->process_field_string($file->cffield_2), 'cffield_3' => $API->process_field_string($file->cffield_3), 'cffield_4' => $API->process_field_string($file->cffield_4), 'cffield_5' => $API->process_field_string($file->cffield_5), 'file_primary' => $file->primary, 'file_order' => $order, 'is_draft' => $is_draft); $this->EE->db->update('exp_channel_files', $data, array('file_id' => $file->file_id)); } } } else { // ----------------------------------------- // No previous entries, fresh fresh // ----------------------------------------- foreach ($data['files'] as $order => $file) { $file = $this->EE->channel_files_helper->decode_json($file['data']); ///$this->EE->firephp->log($file); ///$this->EE->firephp->log($draft_action); ///$this->EE->firephp->log($is_draft); // If we are creating a new draft, we already copied all data.. So lets kill the ones that came through POST if ($is_draft === 1 && $file->file_id > 0 && $draft_action !== 'create') { // ----------------------------------------- // Old File // ----------------------------------------- $data = array('entry_id' => $entry_id, 'field_id' => $field_id, 'channel_id' => $channel_id, 'title' => $API->process_field_string($file->title), 'url_title' => $API->process_field_string($file->url_title), 'description' => $API->process_field_string($file->description), 'category' => isset($file->category) == true ? $file->category : '', 'cffield_1' => $API->process_field_string($file->cffield_1), 'cffield_2' => $API->process_field_string($file->cffield_2), 'cffield_3' => $API->process_field_string($file->cffield_3), 'cffield_4' => $API->process_field_string($file->cffield_4), 'cffield_5' => $API->process_field_string($file->cffield_5), 'file_primary' => $file->primary, 'file_order' => $order, 'is_draft' => $is_draft); $this->EE->db->update('exp_channel_files', $data, array('file_id' => $file->file_id)); continue; } if ($file->file_id > 0) { continue; } // Mime type $filemime = $MIME->get_mimetype($file->extension); // Check for linked_fileid if (isset($file->link_file_id) == FALSE) { $file->link_file_id = 0; } $file->link_entry_id = 0; $file->link_channel_id = 0; $file->link_field_id = 0; if ($file->link_file_id == 0) { $file->filename = $filenames[$file->filename]; } // Grab MD5/Filesize if ($file->link_file_id > 0) { $q = $this->EE->db->select('entry_id, field_id, channel_id, md5, filesize')->from('exp_channel_files')->where('file_id', $file->link_file_id)->limit(1)->get(); $file->link_entry_id = $q->row('entry_id'); $file->link_channel_id = $q->row('channel_id'); $file->link_field_id = $q->row('field_id'); $file->filesize = $q->row('filesize'); $file->md5 = $q->row('md5'); $q->free_result(); } // ----------------------------------------- // New Files // ----------------------------------------- $data = array('site_id' => $this->site_id, 'entry_id' => $entry_id, 'field_id' => $field_id, 'channel_id' => $channel_id, 'member_id' => $this->EE->session->userdata['member_id'], 'is_draft' => $is_draft, 'link_file_id' => $file->link_file_id, 'link_entry_id' => $file->link_entry_id, 'link_channel_id' => $file->link_channel_id, 'link_field_id' => $file->link_field_id, 'filename' => $file->filename, 'extension' => $file->extension, 'mime' => $filemime, 'upload_service' => $location_type, 'title' => $API->process_field_string($file->title), 'url_title' => $API->process_field_string($file->url_title), 'description' => $API->process_field_string($file->description), 'category' => isset($file->category) == true ? $file->category : '', 'cffield_1' => $API->process_field_string($file->cffield_1), 'cffield_2' => $API->process_field_string($file->cffield_2), 'cffield_3' => $API->process_field_string($file->cffield_3), 'cffield_4' => $API->process_field_string($file->cffield_4), 'cffield_5' => $API->process_field_string($file->cffield_5), 'filesize' => $file->filesize, 'md5' => $file->md5, 'file_primary' => $file->primary, 'file_order' => $order, 'date' => $this->EE->localize->now); $this->EE->db->insert('exp_channel_files', $data); } } // Kill Folder $this->EE->channel_files_helper->delete_files($temp_dir, TRUE); @rmdir($temp_dir); // ----------------------------------------- // Delete old cache folders // -----------------------------------------' $path_temp_dir = APPPATH . 'cache/channel_files/field_' . $field_id . '/'; $day_ago = $this->EE->localize->now - 86400; // Get directory contents $tempdirs = @scandir($path_temp_dir); if (is_array($tempdirs) == TRUE) { // Loop over all files foreach ($tempdirs as $tempdir) { // We don't want those if ($tempdir == '.' or $tempdir == '..' or $temp_dir == FALSE) { continue; } // Is it a Directory? if (@is_dir($path_temp_dir . $tempdir) == TRUE) { // And 24 old? if ($day_ago > $tempdir) { // Kill it $this->EE->channel_files_helper->delete_files($path_temp_dir . $tempdir, TRUE); @rmdir($path_temp_dir . $tempdir); } } } } // ----------------------------------------- // WYGWAM // ----------------------------------------- // Which field_group is assigned to this channel? $query = $this->EE->db->select('field_group')->from('exp_channels')->where('channel_id', $channel_id)->get(); if ($query->num_rows() == 0) { return; } $field_group = $query->row('field_group'); // Which fields are WYGWAM/wyvern $query = $this->EE->db->select('field_id')->from('exp_channel_fields')->where('group_id', $field_group)->where('field_type', 'wygwam')->or_where('field_type', 'wyvern')->get(); if ($query->num_rows() == 0) { return; } // Harvest all of them $fields = array(); foreach ($query->result() as $row) { $fields[] = 'field_id_' . $row->field_id; } if (count($fields) > 0) { // Grab them! foreach ($fields as $field) { $this->EE->db->set($field, " REPLACE({$field}, 'd=CFKEYDIR&', 'd={$entry_id}&') ", FALSE); $this->EE->db->where('entry_id', $entry_id); $this->EE->db->update('exp_channel_data'); } } // Delete old dirs $API->clean_temp_dirs($this->field_id); // ----------------------------------------- // Just to be sure // ----------------------------------------- $query = $this->EE->db->select('file_id')->from('exp_channel_files')->where('field_id', $this->field_id)->where('entry_id', $entry_id)->get(); if ($query->num_rows() == 0) { $this->EE->db->set('field_id_' . $this->field_id, ''); } else { $this->EE->db->set('field_id_' . $this->field_id, 'Channel Files'); } $this->EE->db->where('entry_id', $entry_id); $this->EE->db->update('exp_channel_data'); return; }
public function upload_file($source_file, $dest_filename, $dest_folder, $checkExists = true) { $out = array('success' => FALSE, 'filename' => ''); $this->init(); $this->CF_CONT = $this->CF_CONN->get_container($this->lsettings['container']); if ($dest_folder !== FALSE) { $dest_folder .= '/'; } else { $dest_folder = ''; } // Extension $extension = substr(strrchr($source_file, '.'), 1); $filename_no_ext = str_replace('.' . $extension, '', $dest_filename); if ($checkExists) { // Does it already exists? try { $this->CF_CONT->get_object($dest_folder . $dest_filename, TRUE, TRUE); // It exists.. for ($i = 2; $i < 30; $i++) { try { $this->CF_CONT->get_object($dest_folder . "{$filename_no_ext}_{$i}.{$extension}", TRUE, TRUE); } catch (NoSuchObjectException $e) { $dest_filename = "{$filename_no_ext}_{$i}.{$extension}"; break; } } } catch (NoSuchObjectException $e) { } } // Mime type if (class_exists('CFMimeTypes') == FALSE) { include PATH_THIRD . 'channel_files/libraries/mimetypes.class.php'; } $MIME = new CFMimeTypes(); $filemime = $MIME->get_mimetype($extension); // Create Object $OBJECT = $this->CF_CONT->create_object($dest_folder . $dest_filename); $OBJECT->content_type = $filemime; // UPLOAD FREAKING FILE! $fp = fopen($source_file, "r"); $size = (double) sprintf("%u", @filesize($source_file)); $OBJECT->write($fp, $size, TRUE); fclose($fp); $out['success'] = TRUE; $out['filename'] = $dest_filename; return $out; }
private function stream_file($dir = '', $filename = '', $file = FALSE) { if (file_exists($dir . $filename) == FALSE) { exit('FILE NOT FOUND!'); } if (empty($file) == TRUE) { $file = new StdClass(); $file->filename = $filename; $file->extension = substr(strrchr($file->filename, '.'), 1); // Mime type if (class_exists('CFMimeTypes') == FALSE) { include PATH_THIRD . 'channel_files/libraries/mimetypes.class.php'; } $MIME = new CFMimeTypes(); $file->mime = $MIME->get_mimetype($file->extension); $file->filesize = @filesize($dir . $filename); } /** ---------------------------------------- /** For Local Files we STREAM /** ----------------------------------------*/ header('Pragma: public'); header('Expires: 0'); header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); header('Cache-Control: public', FALSE); header('Content-Description: File Transfer'); header('Content-Type: ' . $file->mime); header('Accept-Ranges: bytes'); header('Content-Disposition: attachment; filename="' . $filename . '";'); header('Content-Transfer-Encoding: binary'); header('Content-Length: ' . $file->filesize); @ob_clean(); @flush(); @readfile($dir . $filename); exit; }