aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRedMatrix <info@friendica.com>2014-06-30 09:19:52 +1000
committerRedMatrix <info@friendica.com>2014-06-30 09:19:52 +1000
commit0926c2d849bdc9f8de676a32e2df4fe29d6e2def (patch)
treeae54f0cf2ece4b96b1f71f1c34544555d1fc2dbe
parent362009959e21ff10cc827fdfb1b857f20fe93045 (diff)
parentffea67524026caec14da923bafe8ad34ec63ef08 (diff)
downloadvolse-hubzilla-0926c2d849bdc9f8de676a32e2df4fe29d6e2def.tar.gz
volse-hubzilla-0926c2d849bdc9f8de676a32e2df4fe29d6e2def.tar.bz2
volse-hubzilla-0926c2d849bdc9f8de676a32e2df4fe29d6e2def.zip
Merge pull request #516 from dawnbreak/master
Fixed a wrong timestamp update when adding a file.
-rw-r--r--include/attach.php57
-rw-r--r--include/reddav.php128
-rw-r--r--mod/cloud.php54
-rw-r--r--view/tpl/cloud_directory.tpl23
4 files changed, 172 insertions, 90 deletions
diff --git a/include/attach.php b/include/attach.php
index f5eaaa448..da22e8ed5 100644
--- a/include/attach.php
+++ b/include/attach.php
@@ -552,12 +552,12 @@ function z_readdir($channel_id, $observer_hash, $pathname, $parent_hash = '') {
/**
* @function attach_mkdir($channel,$observer_hash,$arr);
- *
+ *
* @brief Create directory.
- *
- * @param $channel channel array of owner
- * @param $observer_hash hash of current observer
- * @param $arr parameter array to fulfil request
+ *
+ * @param array $channel channel array of owner
+ * @param string $observer_hash hash of current observer
+ * @param array $arr parameter array to fulfil request
* Required:
* $arr['filename']
* $arr['folder'] // hash of parent directory, empty string for root directory
@@ -641,7 +641,7 @@ function attach_mkdir($channel, $observer_hash, $arr = null) {
$path .= $arr['hash'];
- $created = datetime_convert();
+ $created = datetime_convert();
$r = q("INSERT INTO attach ( aid, uid, hash, creator, filename, filetype, filesize, revision, folder, flags, data, created, edited, allow_cid, allow_gid, deny_cid, deny_gid )
VALUES ( %d, %d, '%s', '%s', '%s', '%s', %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s' ) ",
@@ -665,20 +665,28 @@ function attach_mkdir($channel, $observer_hash, $arr = null) {
);
if($r) {
- if(mkdir($path,STORAGE_DEFAULT_PERMISSIONS, true)) {
+ if(mkdir($path, STORAGE_DEFAULT_PERMISSIONS, true)) {
$ret['success'] = true;
$ret['data'] = $arr;
+
+ // update the parent folder's lastmodified timestamp
+ $e = q("UPDATE attach SET edited = '%s' WHERE hash = '%s' AND uid = %d LIMIT 1",
+ dbesc($created),
+ dbesc($arr['folder']),
+ intval($channel_id)
+ );
}
else {
logger('attach_mkdir: ' . mkdir . ' ' . $path . 'failed.');
$ret['message'] = t('mkdir failed.');
}
}
- else
+ else {
$ret['message'] = t('database storage failed.');
+ }
return $ret;
-
+
}
/**
@@ -727,31 +735,30 @@ function attach_change_permissions($channel_id, $resource, $allow_cid, $allow_gi
return;
}
-
+
/**
- * @brief Delete a file.
+ * @brief Delete a file/directory.
*
- * @param $channel_id
- * @param $resource
+ * @param int $channel_id
+ * @param string $resource a hash to delete
*/
function attach_delete($channel_id, $resource) {
-
- $c = q("select channel_address from channel where channel_id = %d limit 1",
+ $c = q("SELECT channel_address FROM channel WHERE channel_id = %d LIMIT 1",
intval($channel_id)
);
$channel_address = (($c) ? $c[0]['channel_address'] : 'notfound');
- $r = q("select hash, flags from attach where hash = '%s' and uid = %d limit 1",
+ $r = q("SELECT hash, flags, folder FROM attach WHERE hash = '%s' AND uid = %d limit 1",
dbesc($resource),
intval($channel_id)
);
-
if(! $r)
return;
+ // If resource is a directory delete everything in the directory recursive
if($r[0]['flags'] & ATTACH_FLAG_DIR) {
$x = q("select hash, flags from attach where folder = '%s' and uid = %d",
dbesc($resource),
@@ -763,8 +770,10 @@ function attach_delete($channel_id, $resource) {
}
}
}
+
+ // delete a file from filesystem
if($r[0]['flags'] & ATTACH_FLAG_OS) {
- $y = q("select data from attach where hash = '%s' and uid = %d limit 1",
+ $y = q("SELECT data FROM attach WHERE hash = '%s' AND uid = %d LIMIT 1",
dbesc($resource),
intval($channel_id)
);
@@ -778,14 +787,22 @@ function attach_delete($channel_id, $resource) {
}
}
- $z = q("delete from attach where hash = '%s' and uid = %d limit 1",
+ // delete from database
+ $z = q("DELETE FROM attach WHERE hash = '%s' AND uid = %d LIMIT 1",
dbesc($resource),
intval($channel_id)
);
+ // update the parent folder's lastmodified timestamp
+ $e = q("UPDATE attach SET edited = '%s' WHERE hash = '%s' AND uid = %d LIMIT 1",
+ dbesc(datetime_convert()),
+ dbesc($r[0]['folder']),
+ intval($channel_id)
+ );
+
return;
}
-
+
/**
* @brief Returns path to file in cloud/.
*
diff --git a/include/reddav.php b/include/reddav.php
index c9b238efb..c4b249598 100644
--- a/include/reddav.php
+++ b/include/reddav.php
@@ -23,22 +23,33 @@ require_once('include/attach.php');
*/
class RedDirectory extends DAV\Node implements DAV\ICollection, DAV\IQuota {
+ /**
+ * @brief The path inside /cloud
+ */
private $red_path;
private $folder_hash;
- // @todo I think this is not used anywhere, we always strip '/cloud' and only use it in debug
+ /**
+ * @brief The full path as seen in the browser.
+ * /cloud + $red_path
+ * @todo I think this is not used anywhere, we always strip '/cloud' and only use it in debug
+ */
private $ext_path;
private $root_dir = '';
private $auth;
+ /**
+ * @brief The real path on the filesystem.
+ * The actual path in store/ with the hashed names.
+ */
private $os_path = '';
/**
- * Sets up the directory node, expects a full path.
+ * @brief Sets up the directory node, expects a full path.
*
* @param string $ext_path a full path
- * @param $auth_plugin
+ * @param RedBasicAuth &$auth_plugin
*/
public function __construct($ext_path, &$auth_plugin) {
- logger('RedDirectory::__construct() ' . $ext_path, LOGGER_DEBUG);
+ logger('RedDirectory::__construct() ' . $ext_path, LOGGER_DATA);
$this->ext_path = $ext_path;
// remove "/cloud" from the beginning of the path
$this->red_path = ((strpos($ext_path, '/cloud') === 0) ? substr($ext_path, 6) : $ext_path);
@@ -54,7 +65,7 @@ class RedDirectory extends DAV\Node implements DAV\ICollection, DAV\IQuota {
}
}
- function log() {
+ private function log() {
logger('RedDirectory::log() ext_path ' . $this->ext_path, LOGGER_DATA);
logger('RedDirectory::log() os_path ' . $this->os_path, LOGGER_DATA);
logger('RedDirectory::log() red_path ' . $this->red_path, LOGGER_DATA);
@@ -113,10 +124,49 @@ class RedDirectory extends DAV\Node implements DAV\ICollection, DAV\IQuota {
throw new DAV\Exception\NotFound('The file with name: ' . $name . ' could not be found.');
}
+ /**
+ * @brief Returns the name of the directory.
+ *
+ * @return string
+ */
public function getName() {
- logger('RedDirectory::getName returns: ' . basename($this->red_path), LOGGER_DATA);
+ logger('RedDirectory::getName() returns: ' . basename($this->red_path), LOGGER_DATA);
return (basename($this->red_path));
}
+
+ /**
+ * @brief Renames the directory.
+ *
+ * @todo handle duplicate directory name
+ *
+ * @throw DAV\Exception\Forbidden
+ * @param string $name The new name of the directory.
+ * @return void
+ */
+ public function setName($name) {
+ logger('RedDirectory::setName(): ' . basename($this->red_path) . ' -> ' . $name, LOGGER_DATA);
+
+ if ((! $name) || (! $this->auth->owner_id)) {
+ logger('RedDirectory::setName(): permission denied');
+ throw new DAV\Exception\Forbidden('Permission denied.');
+ }
+
+ if (! perm_is_allowed($this->auth->owner_id, $this->auth->observer, 'write_storage')) {
+ logger('RedDirectory::setName(): permission denied');
+ throw new DAV\Exception\Forbidden('Permission denied.');
+ }
+
+ list($parent_path, ) = DAV\URLUtil::splitPath($this->red_path);
+ $new_path = $parent_path . '/' . $name;
+
+ $r = q("UPDATE attach SET filename = '%s' WHERE hash = '%s' AND uid = %d LIMIT 1",
+ dbesc($name),
+ dbesc($this->folder_hash),
+ intval($this->auth->owner_id)
+ );
+
+ $this->red_path = $new_path;
+ }
/**
* @brief Creates a new file in the directory.
@@ -130,18 +180,18 @@ class RedDirectory extends DAV\Node implements DAV\ICollection, DAV\IQuota {
* @throws DAV\Exception\Forbidden
* @param string $name Name of the file
* @param resource|string $data Initial payload
- * @return null|string
+ * @return null|string ETag
*/
public function createFile($name, $data = null) {
- logger('RedDirectory::createFile(): ' . $name, LOGGER_DEBUG);
+ logger('RedDirectory::createFile(): ' . $name, LOGGER_DATA);
if (! $this->auth->owner_id) {
- logger('createFile: permission denied');
+ logger('RedDirectory::createFile(): permission denied');
throw new DAV\Exception\Forbidden('Permission denied.');
}
if (! perm_is_allowed($this->auth->owner_id, $this->auth->observer, 'write_storage')) {
- logger('createFile: permission denied');
+ logger('RedDirectory::createFile(): permission denied');
throw new DAV\Exception\Forbidden('Permission denied.');
}
@@ -204,7 +254,7 @@ class RedDirectory extends DAV\Node implements DAV\ICollection, DAV\IQuota {
);
// update the folder's lastmodified timestamp
- $e = q("UPDATE attach SET edited = '%s' WHERE folder = '%s' AND uid = %d LIMIT 1",
+ $e = q("UPDATE attach SET edited = '%s' WHERE hash = '%s' AND uid = %d LIMIT 1",
dbesc($edited),
dbesc($this->folder_hash),
intval($c[0]['channel_id'])
@@ -250,8 +300,9 @@ class RedDirectory extends DAV\Node implements DAV\ICollection, DAV\IQuota {
if ($r) {
$result = attach_mkdir($r[0], $this->auth->observer, array('filename' => $name, 'folder' => $this->folder_hash));
- if (! $result['success'])
+ if (! $result['success']) {
logger('RedDirectory::createDirectory(): ' . print_r($result, true), LOGGER_DEBUG);
+ }
}
}
@@ -305,7 +356,7 @@ class RedDirectory extends DAV\Node implements DAV\ICollection, DAV\IQuota {
if (! $path_arr)
return;
- logger('RedDirectory::getDir(): path: ' . print_r($path_arr, true), LOGGER_DEBUG);
+ logger('RedDirectory::getDir(): path: ' . print_r($path_arr, true), LOGGER_DATA);
$channel_name = $path_arr[0];
@@ -315,7 +366,7 @@ class RedDirectory extends DAV\Node implements DAV\ICollection, DAV\IQuota {
);
if (! $r) {
- throw new DAV\Exception\NotFound('The file with name: ' . $channel_name . ' could not be found');
+ throw new DAV\Exception\NotFound('The file with name: ' . $channel_name . ' could not be found.');
return;
}
@@ -350,9 +401,13 @@ class RedDirectory extends DAV\Node implements DAV\ICollection, DAV\IQuota {
}
/**
- * @brief Returns the last modification time for the directory, as a unix
+ * @brief Returns the last modification time for the directory, as a UNIX
* timestamp.
*
+ * It looks for the last edited file in the folder. If it is an empty folder
+ * it returns the lastmodified time of the folder itself, to prevent zero
+ * timestamps.
+ *
* @return int last modification time in UNIX timestamp
*/
public function getLastModified() {
@@ -360,9 +415,15 @@ class RedDirectory extends DAV\Node implements DAV\ICollection, DAV\IQuota {
dbesc($this->folder_hash),
intval($this->auth->owner_id)
);
- if ($r)
- return datetime_convert('UTC', 'UTC', $r[0]['edited'], 'U');
- return '';
+ if (! $r) {
+ $r = q("SELECT edited FROM attach WHERE hash = '%s' AND uid = %d LIMIT 1",
+ dbesc($this->folder_hash),
+ intval($this->auth->owner_id)
+ );
+ if (! $r)
+ return '';
+ }
+ return datetime_convert('UTC', 'UTC', $r[0]['edited'], 'U');
}
/**
@@ -470,8 +531,8 @@ class RedFile extends DAV\Node implements DAV\IFile {
// @todo only 3 values are needed
$c = q("SELECT * FROM channel WHERE channel_id = %d AND NOT (channel_pageflags & %d) LIMIT 1",
- intval(PAGE_REMOVED),
- intval($this->auth->owner_id)
+ intval($this->auth->owner_id),
+ intval(PAGE_REMOVED)
);
$r = q("SELECT flags, folder, data FROM attach WHERE hash = '%s' AND uid = %d LIMIT 1",
@@ -512,13 +573,16 @@ class RedFile extends DAV\Node implements DAV\IFile {
);
// update the folder's lastmodified timestamp
- $e = q("UPDATE attach SET edited = '%s' WHERE folder = '%s' AND uid = %d LIMIT 1",
+ $e = q("UPDATE attach SET edited = '%s' WHERE hash = '%s' AND uid = %d LIMIT 1",
dbesc($edited),
dbesc($r[0]['folder']),
intval($c[0]['channel_id'])
);
- // @todo do we really want to remove the whole file if an update fails because of maxfilesize or quota?
+ // @todo do we really want to remove the whole file if an update fails
+ // because of maxfilesize or quota?
+ // There is an Exception "InsufficientStorage" or "PaymentRequired" for
+ // our service class from SabreDAV we could use.
$maxfilesize = get_config('system', 'maxfilesize');
if (($maxfilesize) && ($size > $maxfilesize)) {
@@ -622,12 +686,14 @@ class RedFile extends DAV\Node implements DAV\IFile {
}
/**
- * @brief Delete the current file.
+ * @brief Delete the file.
*
* @throw DAV\Exception\Forbidden
* @return void
*/
public function delete() {
+ logger('RedFile::delete(): ' . basename($this->name), LOGGER_DEBUG);
+
if ((! $this->auth->owner_id) || (! perm_is_allowed($this->auth->owner_id, $this->auth->observer, 'write_storage'))) {
throw new DAV\Exception\Forbidden('Permission denied.');
}
@@ -726,7 +792,7 @@ function RedCollectionData($file, &$auth) {
$permission_error = false;
for ($x = 1; $x < count($path_arr); $x++) {
- $r = q("select id, hash, filename, flags from attach where folder = '%s' and filename = '%s' and uid = %d and (flags & %d) $perms limit 1",
+ $r = q("SELECT id, hash, filename, flags FROM attach WHERE folder = '%s' AND filename = '%s' AND uid = %d AND (flags & %d) $perms LIMIT 1",
dbesc($folder),
dbesc($path_arr[$x]),
intval($channel_id),
@@ -989,11 +1055,17 @@ class RedBasicAuth extends DAV\Auth\Backend\AbstractBasic {
$this->currentUser = $name;
}
- function setBrowserPlugin($browser) {
+ /**
+ * @brief Set browser plugin.
+ *
+ * @see RedBrowser::set_writeable()
+ * @param DAV\Browser\Plugin $browser
+ */
+ public function setBrowserPlugin($browser) {
$this->browser = $browser;
}
- // internal? loggin function
+ // internal? logging function
function log() {
logger('dav: auth: channel_name ' . $this->channel_name, LOGGER_DATA);
logger('dav: auth: channel_id ' . $this->channel_id, LOGGER_DATA);
@@ -1037,9 +1109,7 @@ class RedBrowser extends DAV\Browser\Plugin {
}
/**
- * Creates the directory listing for the given path.
- *
- * @todo Why not use RedDirectory for this?
+ * @brief Creates the directory listing for the given path.
*
* @param string $path which should be displayed
*/
diff --git a/mod/cloud.php b/mod/cloud.php
index 51cedd2fd..25773066a 100644
--- a/mod/cloud.php
+++ b/mod/cloud.php
@@ -1,13 +1,16 @@
<?php
+/**
+ * @file mod/cloud.php
+ * @brief Initialize Red Matrix's cloud (SabreDAV)
+ *
+ */
use Sabre\DAV;
-
- require_once('vendor/autoload.php');
-
+ require_once('vendor/autoload.php');
// workaround for HTTP-auth in CGI mode
if(x($_SERVER,'REDIRECT_REMOTE_USER')) {
- $userpass = base64_decode(substr($_SERVER["REDIRECT_REMOTE_USER"],6)) ;
+ $userpass = base64_decode(substr($_SERVER["REDIRECT_REMOTE_USER"], 6)) ;
if(strlen($userpass)) {
list($name, $password) = explode(':', $userpass);
$_SERVER['PHP_AUTH_USER'] = $name;
@@ -16,18 +19,19 @@
}
if(x($_SERVER,'HTTP_AUTHORIZATION')) {
- $userpass = base64_decode(substr($_SERVER["HTTP_AUTHORIZATION"],6)) ;
+ $userpass = base64_decode(substr($_SERVER["HTTP_AUTHORIZATION"], 6)) ;
if(strlen($userpass)) {
- list($name, $password) = explode(':', $userpass);
+ list($name, $password) = explode(':', $userpass);
$_SERVER['PHP_AUTH_USER'] = $name;
$_SERVER['PHP_AUTH_PW'] = $password;
}
}
-
-
-
-
+/**
+ * @brief Fires up the SabreDAV server.
+ *
+ * @param App &$a
+ */
function cloud_init(&$a) {
require_once('include/reddav.php');
@@ -42,13 +46,10 @@ function cloud_init(&$a) {
$profile = 0;
$channel = $a->get_channel();
- $a->page['htmlhead'] .= '<link rel="alternate" type="application/atom+xml" href="' . $a->get_baseurl() . '/feed/' . $which .'" />' . "\r\n" ;
+ $a->page['htmlhead'] .= '<link rel="alternate" type="application/atom+xml" href="' . $a->get_baseurl() . '/feed/' . $which . '" />' . "\r\n" ;
if($which)
- profile_load($a,$which,$profile);
-
-
-
+ profile_load($a, $which, $profile);
$auth = new RedBasicAuth();
@@ -64,15 +65,13 @@ function cloud_init(&$a) {
$auth->channel_account_id = $channel['channel_account_id'];
if($channel['channel_timezone'])
$auth->timezone = $channel['channel_timezone'];
- }
+ }
$auth->observer = $ob_hash;
- }
+ }
if($_GET['davguest'])
$_SESSION['davguest'] = true;
-
-
$_SERVER['QUERY_STRING'] = str_replace(array('?f=','&f='),array('',''),$_SERVER['QUERY_STRING']);
$_SERVER['QUERY_STRING'] = strip_zids($_SERVER['QUERY_STRING']);
$_SERVER['QUERY_STRING'] = preg_replace('/[\?&]davguest=(.*?)([\?&]|$)/ism','',$_SERVER['QUERY_STRING']);
@@ -81,8 +80,11 @@ function cloud_init(&$a) {
$_SERVER['REQUEST_URI'] = strip_zids($_SERVER['REQUEST_URI']);
$_SERVER['REQUEST_URI'] = preg_replace('/[\?&]davguest=(.*?)([\?&]|$)/ism','',$_SERVER['REQUEST_URI']);
- $rootDirectory = new RedDirectory('/',$auth);
+ $rootDirectory = new RedDirectory('/', $auth);
+
+ // A SabreDAV server-object
$server = new DAV\Server($rootDirectory);
+ // prevent overwriting changes each other with a lock backend
$lockBackend = new DAV\Locks\Backend\File('store/[data]/locks');
$lockPlugin = new DAV\Locks\Plugin($lockBackend);
@@ -95,11 +97,11 @@ function cloud_init(&$a) {
// In order to avoid prompting for passwords for viewing a DIRECTORY, add the URL query parameter 'davguest=1'
$isapublic_file = false;
- $davguest = ((x($_SESSION,'davguest')) ? true : false);
+ $davguest = ((x($_SESSION, 'davguest')) ? true : false);
if((! $auth->observer) && ($_SERVER['REQUEST_METHOD'] === 'GET')) {
try {
- $x = RedFileData('/' . $a->cmd,$auth);
+ $x = RedFileData('/' . $a->cmd, $auth);
if($x instanceof RedFile)
$isapublic_file = true;
}
@@ -113,21 +115,19 @@ function cloud_init(&$a) {
$auth->Authenticate($server, t('Red Matrix - Guests: Username: {your email address}, Password: +++'));
}
catch ( Exception $e) {
- logger('mod_cloud: auth exception' . $e->getMessage());
- http_status_exit($e->getHTTPCode(),$e->getMessage());
+ logger('mod_cloud: auth exception' . $e->getMessage());
+ http_status_exit($e->getHTTPCode(), $e->getMessage());
}
}
-// $browser = new DAV\Browser\Plugin();
+ // provide a directory view for the cloud in Red Matrix
$browser = new RedBrowser($auth);
$auth->setBrowserPlugin($browser);
-
$server->addPlugin($browser);
-
// All we need to do now, is to fire up the server
$server->exec();
diff --git a/view/tpl/cloud_directory.tpl b/view/tpl/cloud_directory.tpl
index cdcab1990..fc6b3309a 100644
--- a/view/tpl/cloud_directory.tpl
+++ b/view/tpl/cloud_directory.tpl
@@ -9,7 +9,6 @@
<th>{{t('Last modified')}}</th>
</tr>
<tr><td colspan="8"><hr></td></tr>
-
{{if $parentpath}}
<tr>
<td>{{$parentpath.icon}}</td>
@@ -20,29 +19,25 @@
<td></td>
</tr>
{{/if}}
-
-
{{foreach $entries as $item}}
<tr>
<td>{{$item.icon}}</td>
- <td style="min-width: 15em"><a href="{{$item.fullPath}}">{{$item.displayName}}</a></td>
-
+ <td style="min-width: 15em"><a href="{{$item.fullPath}}">{{$item.displayName}}</a></td>
{{if $item.is_owner}}
- <td>{{$item.attachIcon}}</td>
- <td style="position:relative;"><a href="{{$item.fileStorageUrl}}/{{$item.attachId}}/edit" title="{{t('Edit')}}"><i class="icon-pencil btn btn-default"></i></a></td>
- <td><a href="{{$item.fileStorageUrl}}/{{$item.attachId}}/delete" title="{{t('Delete')}}" onclick="return confirm('{{t('Are you sure you want to delete this item?')}}');"><i class="icon-remove btn btn-default drop-icons"></i></a></td>
+ <td>{{$item.attachIcon}}</td>
+ <td style="position:relative;"><a href="{{$item.fileStorageUrl}}/{{$item.attachId}}/edit" title="{{t('Edit')}}"><i class="icon-pencil btn btn-default"></i></a></td>
+ <td><a href="{{$item.fileStorageUrl}}/{{$item.attachId}}/delete" title="{{t('Delete')}}" onclick="return confirm('{{t('Are you sure you want to delete this item?')}}');"><i class="icon-remove btn btn-default drop-icons"></i></a></td>
{{else}}
<td></td><td></td><td></td>
{{/if}}
- <td>{{$item.type}}</td>
- <td>{{$item.sizeFormatted}}</td>
- <td>{{$item.lastmodified}}</td>
- </tr>
+ <td>{{$item.type}}</td>
+ <td>{{$item.sizeFormatted}}</td>
+ <td>{{$item.lastmodified}}</td>
+ </tr>
{{/foreach}}
-
<tr><td colspan="8"><hr></td></tr>
</table>
{{if $quota.limit || $quota.used}}
<p><strong>{{t('Total')}}</strong> {{$quota.desc}}</p>
-{{/if}}
+{{/if}} \ No newline at end of file