There were just a couple of other changes that I needed to make in order for this to work.
File: XenForo_Helper_File
method: createDirectory()
This makes it so that the file being checked keeps the trailing / which is needed for the Zend_Service_Amazon_S3_Stream class to recognize that you want the url_stat method to check for a directory and not a file. Removing that trailing / isn't necessary for either of the checks.
PHP:
if (file_exists($path) && is_dir($path))
{
return true;
}
$path = preg_replace('#/+$#', '', $path);
The next two changes I can do via an extension but it'd be nice to have it in the core so other users can take advantage of it too.
File: XenForo_DataWriter_AttachmentData
method: _writeAttachmentFile()
line: 179
PHP:
$directory = dirname($filePath);
if (substr($directory, -1) != '/') {
$directory .= '/';
}
File: XenForo_DataWriter_AttachmentData
method: _moveFile()
The problem here is that you cannot rename() or
move_uploaded_file() files across stream types. So the locally uploaded file cannot be moved to the s3:// stream. I added a simple check to see if the source is a local file path and did a copy/unlink if the destination was not local. I'm fairly confident that this doesn't introduce any security issues but a peer review would be good.
PHP:
protected function _moveFile($source, $destination)
{
if (is_uploaded_file($source))
{
if ($this->_fileIsLocal($source) && !$this->_fileIsLocal($destination)) {
$success = copy($source, $destination);
unlink($source);
} else {
$success = move_uploaded_file($source, $destination);
}
}
else
{
if ($this->_fileIsLocal($source) && !$this->_fileIsLocal($destination)) {
$success = copy($source, $destination);
unlink($source);
} else {
$success = rename($source, $destination);
}
}
if ($success)
{
XenForo_Helper_File::makeWritableByFtpUser($destination);
}
return $success;
}
protected function _fileIsLocal($filename)
{
if (preg_match('#^/|[a-z]:#i', $filename)) {
return true;
} else {
return false;
}
}
The last change I had to make I'm not sure how to address. I had to make a minor tweak to Zend_Service_Amazon_S3_Stream. Since ZendFramework has moved on to version 2 I doubt this will get accepted into it but I'm going to try and get it accepted.
File: Zend_Service_Amazon_S3_Stream
Line: 454
I had to add a check if the end of the name is a / to see if it is a directory or not. Otherwise it assumed it was a file and the is_dir() checks failed.
PHP:
if(($slash = strchr($name, '/')) === false || $slash == strlen($name)-1 || substr($name, -1) == '/') {