From 28d07fd6b2b48632edadc94055c1be8a0cb11274 Mon Sep 17 00:00:00 2001 From: redmatrix Date: Fri, 19 Feb 2016 00:06:10 -0800 Subject: [PATCH 1/4] move storage assets to zotlabs/storage --- Zotlabs/Storage/BasicAuth.php | 212 ++++++++++++++ Zotlabs/Storage/Browser.php | 372 +++++++++++++++++++++++ Zotlabs/Storage/Directory.php | 536 ++++++++++++++++++++++++++++++++++ Zotlabs/Storage/File.php | 322 ++++++++++++++++++++ Zotlabs/Zot/ZotHandler.php | 3 - include/network.php | 4 +- include/reddav.php | 22 +- mod/cloud.php | 14 +- mod/dav.php | 16 +- mod/post.php | 4 - mod/wfinger.php | 5 +- 11 files changed, 1471 insertions(+), 39 deletions(-) create mode 100644 Zotlabs/Storage/BasicAuth.php create mode 100644 Zotlabs/Storage/Browser.php create mode 100644 Zotlabs/Storage/Directory.php create mode 100644 Zotlabs/Storage/File.php diff --git a/Zotlabs/Storage/BasicAuth.php b/Zotlabs/Storage/BasicAuth.php new file mode 100644 index 000000000..d93525d20 --- /dev/null +++ b/Zotlabs/Storage/BasicAuth.php @@ -0,0 +1,212 @@ +setAuthenticated($r[0]); + } + } + $r = q("SELECT * FROM channel WHERE channel_address = '%s' LIMIT 1", + dbesc($username) + ); + if ($r) { + $x = q("SELECT account_flags, account_salt, account_password FROM account WHERE account_id = %d LIMIT 1", + intval($r[0]['channel_account_id']) + ); + if ($x) { + // @fixme this foreach should not be needed? + foreach ($x as $record) { + if (($record['account_flags'] == ACCOUNT_OK) || ($record['account_flags'] == ACCOUNT_UNVERIFIED) + && (hash('whirlpool', $record['account_salt'] . $password) === $record['account_password'])) { + logger('password verified for ' . $username); + return $this->setAuthenticated($r[0]); + } + } + } + } + + $error = 'password failed for ' . $username; + logger($error); + log_failed_login($error); + + return false; + } + + /** + * @brief Sets variables and session parameters after successfull authentication. + * + * @param array $r + * Array with the values for the authenticated channel. + * @return bool + */ + protected function setAuthenticated($r) { + $this->channel_name = $r['channel_address']; + $this->channel_id = $r['channel_id']; + $this->channel_hash = $this->observer = $r['channel_hash']; + $_SESSION['uid'] = $r['channel_id']; + $_SESSION['account_id'] = $r['channel_account_id']; + $_SESSION['authenticated'] = true; + return true; + } + + /** + * Sets the channel_name from the currently logged-in channel. + * + * @param string $name + * The channel's name + */ + public function setCurrentUser($name) { + $this->channel_name = $name; + } + /** + * Returns information about the currently logged-in channel. + * + * If nobody is currently logged in, this method should return null. + * + * @see \Sabre\DAV\Auth\Backend\AbstractBasic::getCurrentUser + * @return string|null + */ + public function getCurrentUser() { + return $this->channel_name; + } + + /** + * @brief Sets the timezone from the channel in RedBasicAuth. + * + * Set in mod/cloud.php if the channel has a timezone set. + * + * @param string $timezone + * The channel's timezone. + * @return void + */ + public function setTimezone($timezone) { + $this->timezone = $timezone; + } + /** + * @brief Returns the timezone. + * + * @return string + * Return the channel's timezone. + */ + public function getTimezone() { + return $this->timezone; + } + + /** + * @brief Set browser plugin for SabreDAV. + * + * @see RedBrowser::set_writeable() + * @param \Sabre\DAV\Browser\Plugin $browser + */ + public function setBrowserPlugin($browser) { + $this->browser = $browser; + } + + /** + * @brief Prints out all BasicAuth variables to logger(). + * + * @return void + */ + public function log() { + logger('channel_name ' . $this->channel_name, LOGGER_DATA); + logger('channel_id ' . $this->channel_id, LOGGER_DATA); + logger('channel_hash ' . $this->channel_hash, LOGGER_DATA); + logger('observer ' . $this->observer, LOGGER_DATA); + logger('owner_id ' . $this->owner_id, LOGGER_DATA); + logger('owner_nick ' . $this->owner_nick, LOGGER_DATA); + } +} \ No newline at end of file diff --git a/Zotlabs/Storage/Browser.php b/Zotlabs/Storage/Browser.php new file mode 100644 index 000000000..fde443e6f --- /dev/null +++ b/Zotlabs/Storage/Browser.php @@ -0,0 +1,372 @@ +auth = $auth; + parent::__construct(true, false); + } + + /** + * The DAV browser is instantiated after the auth module and directory classes + * but before we know the current directory and who the owner and observer + * are. So we add a pointer to the browser into the auth module and vice versa. + * Then when we've figured out what directory is actually being accessed, we + * call the following function to decide whether or not to show web elements + * which include writeable objects. + * + * @fixme It only disable/enable the visible parts. Not the POST handler + * which handels the actual requests when uploading files or creating folders. + * + * @todo Maybe this whole way of doing this can be solved with some + * $server->subscribeEvent(). + */ + public function set_writeable() { + if (! $this->auth->owner_id) { + $this->enablePost = false; + } + + if (! perm_is_allowed($this->auth->owner_id, get_observer_hash(), 'write_storage')) { + $this->enablePost = false; + } else { + $this->enablePost = true; + } + } + + /** + * @brief Creates the directory listing for the given path. + * + * @param string $path which should be displayed + */ + public function generateDirectoryIndex($path) { + // (owner_id = channel_id) is visitor owner of this directory? + $is_owner = ((local_channel() && $this->auth->owner_id == local_channel()) ? true : false); + + if ($this->auth->getTimezone()) + date_default_timezone_set($this->auth->getTimezone()); + + require_once('include/conversation.php'); + require_once('include/text.php'); + if ($this->auth->owner_nick) { + $html = profile_tabs(get_app(), (($is_owner) ? true : false), $this->auth->owner_nick); + } + + $files = $this->server->getPropertiesForPath($path, array( + '{DAV:}displayname', + '{DAV:}resourcetype', + '{DAV:}getcontenttype', + '{DAV:}getcontentlength', + '{DAV:}getlastmodified', + ), 1); + + + $parent = $this->server->tree->getNodeForPath($path); + + $parentpath = array(); + // only show parent if not leaving /cloud/; TODO how to improve this? + if ($path && $path != "cloud") { + list($parentUri) = DAV\URLUtil::splitPath($path); + $fullPath = DAV\URLUtil::encodePath($this->server->getBaseUri() . $parentUri); + + $parentpath['icon'] = $this->enableAssets ? '' . t('parent') . '' : ''; + $parentpath['path'] = $fullPath; + } + + $f = array(); + foreach ($files as $file) { + $ft = array(); + $type = null; + + // This is the current directory, we can skip it + if (rtrim($file['href'],'/') == $path) continue; + + list(, $name) = DAV\URLUtil::splitPath($file['href']); + + if (isset($file[200]['{DAV:}resourcetype'])) { + $type = $file[200]['{DAV:}resourcetype']->getValue(); + + // resourcetype can have multiple values + if (!is_array($type)) $type = array($type); + + foreach ($type as $k=>$v) { + // Some name mapping is preferred + switch ($v) { + case '{DAV:}collection' : + $type[$k] = t('Collection'); + break; + case '{DAV:}principal' : + $type[$k] = t('Principal'); + break; + case '{urn:ietf:params:xml:ns:carddav}addressbook' : + $type[$k] = t('Addressbook'); + break; + case '{urn:ietf:params:xml:ns:caldav}calendar' : + $type[$k] = t('Calendar'); + break; + case '{urn:ietf:params:xml:ns:caldav}schedule-inbox' : + $type[$k] = t('Schedule Inbox'); + break; + case '{urn:ietf:params:xml:ns:caldav}schedule-outbox' : + $type[$k] = t('Schedule Outbox'); + break; + case '{http://calendarserver.org/ns/}calendar-proxy-read' : + $type[$k] = 'Proxy-Read'; + break; + case '{http://calendarserver.org/ns/}calendar-proxy-write' : + $type[$k] = 'Proxy-Write'; + break; + } + } + $type = implode(', ', $type); + } + + // If no resourcetype was found, we attempt to use + // the contenttype property + if (!$type && isset($file[200]['{DAV:}getcontenttype'])) { + $type = $file[200]['{DAV:}getcontenttype']; + } + if (!$type) $type = t('Unknown'); + + $size = isset($file[200]['{DAV:}getcontentlength']) ? (int)$file[200]['{DAV:}getcontentlength'] : ''; + $lastmodified = ((isset($file[200]['{DAV:}getlastmodified'])) ? $file[200]['{DAV:}getlastmodified']->getTime()->format('Y-m-d H:i:s') : ''); + + $fullPath = DAV\URLUtil::encodePath('/' . trim($this->server->getBaseUri() . ($path ? $path . '/' : '') . $name, '/')); + + + $displayName = isset($file[200]['{DAV:}displayname']) ? $file[200]['{DAV:}displayname'] : $name; + + $displayName = $this->escapeHTML($displayName); + $type = $this->escapeHTML($type); + + $icon = ''; + + if ($this->enableAssets) { + $node = $this->server->tree->getNodeForPath(($path ? $path . '/' : '') . $name); + foreach (array_reverse($this->iconMap) as $class=>$iconName) { + if ($node instanceof $class) { + $icon = ''; + break; + } + } + } + + $parentHash = ''; + $owner = $this->auth->owner_id; + $splitPath = explode('/', $fullPath); + if (count($splitPath) > 3) { + for ($i = 3; $i < count($splitPath); $i++) { + $attachName = urldecode($splitPath[$i]); + $attachHash = $this->findAttachHash($owner, $parentHash, $attachName); + $parentHash = $attachHash; + } + } + + $attachIcon = ""; // ""; + + // put the array for this file together + $ft['attachId'] = $this->findAttachIdByHash($attachHash); + $ft['fileStorageUrl'] = substr($fullPath, 0, strpos($fullPath, "cloud/")) . "filestorage/" . $this->auth->getCurrentUser(); + $ft['icon'] = $icon; + $ft['attachIcon'] = (($size) ? $attachIcon : ''); + // @todo Should this be an item value, not a global one? + $ft['is_owner'] = $is_owner; + $ft['fullPath'] = $fullPath; + $ft['displayName'] = $displayName; + $ft['type'] = $type; + $ft['size'] = $size; + $ft['sizeFormatted'] = userReadableSize($size); + $ft['lastmodified'] = (($lastmodified) ? datetime_convert('UTC', date_default_timezone_get(), $lastmodified) : ''); + $ft['iconFromType'] = getIconFromType($type); + + $f[] = $ft; + } + + $output = ''; + if ($this->enablePost) { + $this->server->broadcastEvent('onHTMLActionsPanel', array($parent, &$output)); + } + + $html .= replace_macros(get_markup_template('cloud.tpl'), array( + '$header' => t('Files') . ": " . $this->escapeHTML($path) . "/", + '$total' => t('Total'), + '$actionspanel' => $output, + '$shared' => t('Shared'), + '$create' => t('Create'), + '$upload' => t('Upload'), + '$is_owner' => $is_owner, + '$parentpath' => $parentpath, + '$entries' => $f, + '$name' => t('Name'), + '$type' => t('Type'), + '$size' => t('Size'), + '$lastmod' => t('Last Modified'), + '$parent' => t('parent'), + '$edit' => t('Edit'), + '$delete' => t('Delete'), + '$nick' => $this->auth->getCurrentUser() + )); + + $a = get_app(); + $a->page['content'] = $html; + load_pdl($a); + + $theme_info_file = "view/theme/" . current_theme() . "/php/theme.php"; + if (file_exists($theme_info_file)){ + require_once($theme_info_file); + if (function_exists(str_replace('-', '_', current_theme()) . '_init')) { + $func = str_replace('-', '_', current_theme()) . '_init'; + $func($a); + } + } + construct_page($a); + } + + /** + * @brief Creates a form to add new folders and upload files. + * + * @param \Sabre\DAV\INode $node + * @param string &$output + */ + public function htmlActionsPanel(DAV\INode $node, &$output) { + if (! $node instanceof DAV\ICollection) + return; + + // We also know fairly certain that if an object is a non-extended + // SimpleCollection, we won't need to show the panel either. + if (get_class($node) === 'Sabre\\DAV\\SimpleCollection') + return; + + // Storage and quota for the account (all channels of the owner of this directory)! + $limit = service_class_fetch($owner, 'attach_upload_limit'); + $r = q("SELECT SUM(filesize) AS total FROM attach WHERE aid = %d", + intval($this->auth->channel_account_id) + ); + $used = $r[0]['total']; + if ($used) { + $quotaDesc = t('You are using %1$s of your available file storage.'); + $quotaDesc = sprintf($quotaDesc, + userReadableSize($used)); + } + if ($limit && $used) { + $quotaDesc = t('You are using %1$s of %2$s available file storage. (%3$s%)'); + $quotaDesc = sprintf($quotaDesc, + userReadableSize($used), + userReadableSize($limit), + round($used / $limit, 1) * 100); + } + + // prepare quota for template + $quota = array(); + $quota['used'] = $used; + $quota['limit'] = $limit; + $quota['desc'] = $quotaDesc; + $quota['warning'] = ((($limit) && ((round($used / $limit, 1) * 100) >= 90)) ? t('WARNING:') : ''); // 10485760 bytes = 100MB + + $output .= replace_macros(get_markup_template('cloud_actionspanel.tpl'), array( + '$folder_header' => t('Create new folder'), + '$folder_submit' => t('Create'), + '$upload_header' => t('Upload file'), + '$upload_submit' => t('Upload'), + '$quota' => $quota + )); + } + + /** + * This method takes a path/name of an asset and turns it into url + * suiteable for http access. + * + * @param string $assetName + * @return string + */ + protected function getAssetUrl($assetName) { + return z_root() . '/cloud/?sabreAction=asset&assetName=' . urlencode($assetName); + } + + /** + * @brief Return the hash of an attachment. + * + * Given the owner, the parent folder and and attach name get the attachment + * hash. + * + * @param int $owner + * The owner_id + * @param string $hash + * The parent's folder hash + * @param string $attachName + * The name of the attachment + * @return string + */ + + protected function findAttachHash($owner, $parentHash, $attachName) { + $r = q("SELECT hash FROM attach WHERE uid = %d AND folder = '%s' AND filename = '%s' ORDER BY edited DESC LIMIT 1", + intval($owner), + dbesc($parentHash), + dbesc($attachName) + ); + $hash = ""; + if ($r) { + foreach ($r as $rr) { + $hash = $rr['hash']; + } + } + return $hash; + } + + /** + * @brief Returns an attachment's id for a given hash. + * + * This id is used to access the attachment in filestorage/ + * + * @param string $attachHash + * The hash of an attachment + * @return string + */ + protected function findAttachIdByHash($attachHash) { + $r = q("SELECT id FROM attach WHERE hash = '%s' ORDER BY edited DESC LIMIT 1", + dbesc($attachHash) + ); + $id = ""; + if ($r) { + foreach ($r as $rr) { + $id = $rr['id']; + } + } + return $id; + } +} diff --git a/Zotlabs/Storage/Directory.php b/Zotlabs/Storage/Directory.php new file mode 100644 index 000000000..e38d76914 --- /dev/null +++ b/Zotlabs/Storage/Directory.php @@ -0,0 +1,536 @@ +ext_path = $ext_path; + // remove "/cloud" from the beginning of the path + $modulename = get_app()->module; + $this->red_path = ((strpos($ext_path, '/' . $modulename) === 0) ? substr($ext_path, strlen($modulename) + 1) : $ext_path); + if (! $this->red_path) { + $this->red_path = '/'; + } + $this->auth = $auth_plugin; + $this->folder_hash = ''; + $this->getDir(); + + if ($this->auth->browser) { + $this->auth->browser->set_writeable(); + } + } + + private function log() { + logger('ext_path ' . $this->ext_path, LOGGER_DATA); + logger('os_path ' . $this->os_path, LOGGER_DATA); + logger('red_path ' . $this->red_path, LOGGER_DATA); + } + + /** + * @brief Returns an array with all the child nodes. + * + * @throw \Sabre\DAV\Exception\Forbidden + * @return array \Sabre\DAV\INode[] + */ + public function getChildren() { + logger('children for ' . $this->ext_path, LOGGER_DATA); + $this->log(); + + if (get_config('system', 'block_public') && (! $this->auth->channel_id) && (! $this->auth->observer)) { + throw new DAV\Exception\Forbidden('Permission denied.'); + } + + if (($this->auth->owner_id) && (! perm_is_allowed($this->auth->owner_id, $this->auth->observer, 'view_storage'))) { + throw new DAV\Exception\Forbidden('Permission denied.'); + } + + $contents = RedCollectionData($this->red_path, $this->auth); + return $contents; + } + + /** + * @brief Returns a child by name. + * + * + * @throw \Sabre\DAV\Exception\Forbidden + * @throw \Sabre\DAV\Exception\NotFound + * @param string $name + */ + public function getChild($name) { + logger($name, LOGGER_DATA); + + if (get_config('system', 'block_public') && (! $this->auth->channel_id) && (! $this->auth->observer)) { + throw new DAV\Exception\Forbidden('Permission denied.'); + } + + if (($this->auth->owner_id) && (! perm_is_allowed($this->auth->owner_id, $this->auth->observer, 'view_storage'))) { + throw new DAV\Exception\Forbidden('Permission denied.'); + } + + $modulename = get_app()->module; + if ($this->red_path === '/' && $name === $modulename) { + return new Directory('/' . $modulename, $this->auth); + } + + $x = RedFileData($this->ext_path . '/' . $name, $this->auth); + if ($x) { + return $x; + } + + 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(basename($this->red_path), LOGGER_DATA); + return (basename($this->red_path)); + } + + /** + * @brief Renames the directory. + * + * @todo handle duplicate directory name + * + * @throw \Sabre\DAV\Exception\Forbidden + * @param string $name The new name of the directory. + * @return void + */ + public function setName($name) { + logger('old name ' . basename($this->red_path) . ' -> ' . $name, LOGGER_DATA); + + if ((! $name) || (! $this->auth->owner_id)) { + logger('permission denied ' . $name); + throw new DAV\Exception\Forbidden('Permission denied.'); + } + + if (! perm_is_allowed($this->auth->owner_id, $this->auth->observer, 'write_storage')) { + logger('permission denied '. $name); + 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", + dbesc($name), + dbesc($this->folder_hash), + intval($this->auth->owner_id) + ); + + $this->red_path = $new_path; + } + + /** + * @brief Creates a new file in the directory. + * + * Data will either be supplied as a stream resource, or in certain cases + * as a string. Keep in mind that you may have to support either. + * + * After successful creation of the file, you may choose to return the ETag + * of the new file here. + * + * @throw \Sabre\DAV\Exception\Forbidden + * @param string $name Name of the file + * @param resource|string $data Initial payload + * @return null|string ETag + */ + public function createFile($name, $data = null) { + logger($name, LOGGER_DEBUG); + + if (! $this->auth->owner_id) { + logger('permission denied ' . $name); + throw new DAV\Exception\Forbidden('Permission denied.'); + } + + if (! perm_is_allowed($this->auth->owner_id, $this->auth->observer, 'write_storage')) { + logger('permission denied ' . $name); + throw new DAV\Exception\Forbidden('Permission denied.'); + } + + $mimetype = z_mime_content_type($name); + + $c = q("SELECT * FROM channel WHERE channel_id = %d AND channel_removed = 0 LIMIT 1", + intval($this->auth->owner_id) + ); + + if (! $c) { + logger('no channel'); + throw new DAV\Exception\Forbidden('Permission denied.'); + } + + $filesize = 0; + $hash = random_string(); + + $f = 'store/' . $this->auth->owner_nick . '/' . (($this->os_path) ? $this->os_path . '/' : '') . $hash; + + $direct = null; + + if($this->folder_hash) { + $r = q("select * from attach where hash = '%s' and is_dir = 1 and uid = %d limit 1", + dbesc($this->folder_hash), + intval($c[0]['channel_id']) + ); + if($r) + $direct = $r[0]; + } + + if(($direct) && (($direct['allow_cid']) || ($direct['allow_gid']) || ($direct['deny_cid']) || ($direct['deny_gid']))) { + $allow_cid = $direct['allow_cid']; + $allow_gid = $direct['allow_gid']; + $deny_cid = $direct['deny_cid']; + $deny_gid = $direct['deny_gid']; + } + else { + $allow_cid = $c[0]['channel_allow_cid']; + $allow_gid = $c[0]['channel_allow_gid']; + $deny_cid = $c[0]['channel_deny_cid']; + $deny_gid = $c[0]['channel_deny_gid']; + } + + $r = q("INSERT INTO attach ( aid, uid, hash, creator, filename, folder, os_storage, filetype, filesize, revision, is_photo, data, created, edited, allow_cid, allow_gid, deny_cid, deny_gid ) + VALUES ( %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s' ) ", + intval($c[0]['channel_account_id']), + intval($c[0]['channel_id']), + dbesc($hash), + dbesc($this->auth->observer), + dbesc($name), + dbesc($this->folder_hash), + intval(1), + dbesc($mimetype), + intval($filesize), + intval(0), + intval($is_photo), + dbesc($f), + dbesc(datetime_convert()), + dbesc(datetime_convert()), + dbesc($allow_cid), + dbesc($allow_gid), + dbesc($deny_cid), + dbesc($deny_gid) + ); + + + + // returns the number of bytes that were written to the file, or FALSE on failure + $size = file_put_contents($f, $data); + // delete attach entry if file_put_contents() failed + if ($size === false) { + logger('file_put_contents() failed to ' . $f); + attach_delete($c[0]['channel_id'], $hash); + return; + } + + // returns now + $edited = datetime_convert(); + + + + $is_photo = 0; + $x = @getimagesize($f); + logger('getimagesize: ' . print_r($x,true), LOGGER_DATA); + if(($x) && ($x[2] === IMAGETYPE_GIF || $x[2] === IMAGETYPE_JPEG || $x[2] === IMAGETYPE_PNG)) { + $is_photo = 1; + } + + + // updates entry with filesize and timestamp + $d = q("UPDATE attach SET filesize = '%s', is_photo = %d, edited = '%s' WHERE hash = '%s' AND uid = %d", + dbesc($size), + intval($is_photo), + dbesc($edited), + dbesc($hash), + intval($c[0]['channel_id']) + ); + + // update the folder's lastmodified timestamp + $e = q("UPDATE attach SET edited = '%s' WHERE hash = '%s' AND uid = %d", + dbesc($edited), + dbesc($this->folder_hash), + intval($c[0]['channel_id']) + ); + + $maxfilesize = get_config('system', 'maxfilesize'); + if (($maxfilesize) && ($size > $maxfilesize)) { + attach_delete($c[0]['channel_id'], $hash); + return; + } + + // check against service class quota + $limit = service_class_fetch($c[0]['channel_id'], 'attach_upload_limit'); + if ($limit !== false) { + $x = q("SELECT SUM(filesize) AS total FROM attach WHERE aid = %d ", + intval($c[0]['channel_account_id']) + ); + if (($x) && ($x[0]['total'] + $size > $limit)) { + logger('service class limit exceeded for ' . $c[0]['channel_name'] . ' total usage is ' . $x[0]['total'] . ' limit is ' . $limit); + attach_delete($c[0]['channel_id'], $hash); + return; + } + } + + if($is_photo) { + $album = ''; + if($this->folder_hash) { + $f1 = q("select filename from attach WHERE hash = '%s' AND uid = %d", + dbesc($this->folder_hash), + intval($c[0]['channel_id']) + ); + if($f1) + $album = $f1[0]['filename']; + } + + require_once('include/photos.php'); + $args = array( 'resource_id' => $hash, 'album' => $album, 'os_path' => $f, 'filename' => $name, 'getimagesize' => $x, 'directory' => $direct); + $p = photo_upload($c[0],get_app()->get_observer(),$args); + } + + } + + /** + * @brief Creates a new subdirectory. + * + * @param string $name the directory to create + * @return void + */ + public function createDirectory($name) { + logger($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.'); + } + + $r = q("SELECT * FROM channel WHERE channel_id = %d AND channel_removed = 0 LIMIT 1", + intval($this->auth->owner_id) + ); + + if ($r) { + $result = attach_mkdir($r[0], $this->auth->observer, array('filename' => $name, 'folder' => $this->folder_hash)); + if (! $result['success']) { + logger('error ' . print_r($result, true), LOGGER_DEBUG); + } + } + } + + /** + * @brief delete directory + */ + + public function delete() { + logger('delete file ' . basename($this->red_path), 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.'); + } + + if ($this->auth->owner_id !== $this->auth->channel_id) { + if (($this->auth->observer !== $this->data['creator']) || intval($this->data['is_dir'])) { + throw new DAV\Exception\Forbidden('Permission denied.'); + } + } + + attach_delete($this->auth->owner_id, $this->folder_hash); + } + + + /** + * @brief Checks if a child exists. + * + * @param string $name + * The name to check if it exists. + * @return boolean + */ + public function childExists($name) { + // On /cloud we show a list of available channels. + // @todo what happens if no channels are available? + $modulename = get_app()->module; + if ($this->red_path === '/' && $name === $modulename) { + //logger('We are at ' $modulename . ' show a channel list', LOGGER_DEBUG); + return true; + } + + $x = RedFileData($this->ext_path . '/' . $name, $this->auth, true); + //logger('RedFileData returns: ' . print_r($x, true), LOGGER_DATA); + if ($x) + return true; + + return false; + } + + /** + * @todo add description of what this function does. + * + * @throw \Sabre\DAV\Exception\NotFound + * @return void + */ + function getDir() { + + logger('GetDir: ' . $this->ext_path, LOGGER_DEBUG); + $this->auth->log(); + $modulename = get_app()->module; + + $file = $this->ext_path; + + $x = strpos($file, '/' . $modulename); + if ($x === 0) { + $file = substr($file, strlen($modulename) + 1); + } + + if ((! $file) || ($file === '/')) { + return; + } + + $file = trim($file, '/'); + $path_arr = explode('/', $file); + + if (! $path_arr) + return; + + logger('paths: ' . print_r($path_arr, true), LOGGER_DATA); + + $channel_name = $path_arr[0]; + + $r = q("SELECT channel_id FROM channel WHERE channel_address = '%s' AND channel_removed = 0 LIMIT 1", + dbesc($channel_name) + ); + + if (! $r) { + throw new DAV\Exception\NotFound('The file with name: ' . $channel_name . ' could not be found.'); + } + + $channel_id = $r[0]['channel_id']; + $this->auth->owner_id = $channel_id; + $this->auth->owner_nick = $channel_name; + + $path = '/' . $channel_name; + $folder = ''; + $os_path = ''; + + for ($x = 1; $x < count($path_arr); $x++) { + $r = q("select id, hash, filename, flags, is_dir from attach where folder = '%s' and filename = '%s' and uid = %d and is_dir != 0", + dbesc($folder), + dbesc($path_arr[$x]), + intval($channel_id) + ); + if ($r && intval($r[0]['is_dir'])) { + $folder = $r[0]['hash']; + if (strlen($os_path)) + $os_path .= '/'; + $os_path .= $folder; + + $path = $path . '/' . $r[0]['filename']; + } + } + $this->folder_hash = $folder; + $this->os_path = $os_path; + } + + /** + * @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() { + $r = q("SELECT edited FROM attach WHERE folder = '%s' AND uid = %d ORDER BY edited DESC LIMIT 1", + dbesc($this->folder_hash), + intval($this->auth->owner_id) + ); + 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'); + } + + /** + * @brief Return quota usage. + * + * @fixme Should guests relly see the used/free values from filesystem of the + * complete store directory? + * + * @return array with used and free values in bytes. + */ + public function getQuotaInfo() { + // values from the filesystem of the complete store/ directory + $limit = disk_total_space('store'); + $free = disk_free_space('store'); + + if ($this->auth->owner_id) { + $c = q("select * from channel where channel_id = %d and channel_removed = 0 limit 1", + intval($this->auth->owner_id) + ); + + $ulimit = service_class_fetch($c[0]['channel_id'], 'attach_upload_limit'); + $limit = (($ulimit) ? $ulimit : $limit); + + $x = q("select sum(filesize) as total from attach where aid = %d", + intval($c[0]['channel_account_id']) + ); + $free = (($x) ? $limit - $x[0]['total'] : 0); + } + + return array( + $limit - $free, + $free + ); + } +} \ No newline at end of file diff --git a/Zotlabs/Storage/File.php b/Zotlabs/Storage/File.php new file mode 100644 index 000000000..2a2a8b938 --- /dev/null +++ b/Zotlabs/Storage/File.php @@ -0,0 +1,322 @@ +name = $name; + $this->data = $data; + $this->auth = $auth; + + logger(print_r($this->data, true), LOGGER_DATA); + } + + /** + * @brief Returns the name of the file. + * + * @return string + */ + public function getName() { + //logger(basename($this->name), LOGGER_DATA); + return basename($this->name); + } + + /** + * @brief Renames the file. + * + * @throw Sabre\DAV\Exception\Forbidden + * @param string $name The new name of the file. + * @return void + */ + public function setName($newName) { + logger('old name ' . basename($this->name) . ' -> ' . $newName, LOGGER_DATA); + + if ((! $newName) || (! $this->auth->owner_id) || (! perm_is_allowed($this->auth->owner_id, $this->auth->observer, 'write_storage'))) { + logger('permission denied '. $newName); + throw new DAV\Exception\Forbidden('Permission denied.'); + } + + $newName = str_replace('/', '%2F', $newName); + + $r = q("UPDATE attach SET filename = '%s' WHERE hash = '%s' AND id = %d", + dbesc($newName), + dbesc($this->data['hash']), + intval($this->data['id']) + ); + } + + /** + * @brief Updates the data of the file. + * + * @param resource $data + * @return void + */ + public function put($data) { + logger('put file: ' . basename($this->name), LOGGER_DEBUG); + $size = 0; + + // @todo only 3 values are needed + $c = q("SELECT * FROM channel WHERE channel_id = %d AND channel_removed = 0 LIMIT 1", + intval($this->auth->owner_id) + ); + + $is_photo = false; + $album = ''; + + $r = q("SELECT flags, folder, os_storage, filename, is_photo FROM attach WHERE hash = '%s' AND uid = %d LIMIT 1", + dbesc($this->data['hash']), + intval($c[0]['channel_id']) + ); + if ($r) { + if (intval($r[0]['os_storage'])) { + $d = q("select folder, data from attach where hash = '%s' and uid = %d limit 1", + dbesc($this->data['hash']), + intval($c[0]['channel_id']) + ); + if($d) { + if($d[0]['folder']) { + $f1 = q("select * from attach where is_dir = 1 and hash = '%s' and uid = %d limit 1", + dbesc($d[0]['folder']), + intval($c[0]['channel_id']) + ); + if($f1) { + $album = $f1[0]['filename']; + $direct = $f1[0]; + } + } + $fname = dbunescbin($d[0]['data']); + if(strpos($fname,'store') === false) + $f = 'store/' . $this->auth->owner_nick . '/' . $fname ; + else + $f = $fname; + + // @todo check return value and set $size directly + @file_put_contents($f, $data); + $size = @filesize($f); + logger('filename: ' . $f . ' size: ' . $size, LOGGER_DEBUG); + } + $gis = @getimagesize($f); + logger('getimagesize: ' . print_r($gis,true), LOGGER_DATA); + if(($gis) && ($gis[2] === IMAGETYPE_GIF || $gis[2] === IMAGETYPE_JPEG || $gis[2] === IMAGETYPE_PNG)) { + $is_photo = 1; + } + } + else { + // this shouldn't happen any more + $r = q("UPDATE attach SET data = '%s' WHERE hash = '%s' AND uid = %d", + dbescbin(stream_get_contents($data)), + dbesc($this->data['hash']), + intval($this->data['uid']) + ); + $r = q("SELECT length(data) AS fsize FROM attach WHERE hash = '%s' AND uid = %d LIMIT 1", + dbesc($this->data['hash']), + intval($this->data['uid']) + ); + if ($r) { + $size = $r[0]['fsize']; + } + } + } + + // returns now() + $edited = datetime_convert(); + + $d = q("UPDATE attach SET filesize = '%s', is_photo = %d, edited = '%s' WHERE hash = '%s' AND uid = %d", + dbesc($size), + intval($is_photo), + dbesc($edited), + dbesc($this->data['hash']), + intval($c[0]['channel_id']) + ); + + if($is_photo) { + require_once('include/photos.php'); + $args = array( 'resource_id' => $this->data['hash'], 'album' => $album, 'os_path' => $f, 'filename' => $r[0]['filename'], 'getimagesize' => $gis, 'directory' => $direct ); + $p = photo_upload($c[0],get_app()->get_observer(),$args); + } + + // update the folder's lastmodified timestamp + $e = q("UPDATE attach SET edited = '%s' WHERE hash = '%s' AND uid = %d", + 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? + // 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)) { + attach_delete($c[0]['channel_id'], $this->data['hash']); + return; + } + + $limit = service_class_fetch($c[0]['channel_id'], 'attach_upload_limit'); + if ($limit !== false) { + $x = q("select sum(filesize) as total from attach where aid = %d ", + intval($c[0]['channel_account_id']) + ); + if (($x) && ($x[0]['total'] + $size > $limit)) { + logger('service class limit exceeded for ' . $c[0]['channel_name'] . ' total usage is ' . $x[0]['total'] . ' limit is ' . $limit); + attach_delete($c[0]['channel_id'], $this->data['hash']); + return; + } + } + } + + /** + * @brief Returns the raw data. + * + * @return string + */ + public function get() { + logger('get file ' . basename($this->name), LOGGER_DEBUG); + logger('os_path: ' . $this->os_path, LOGGER_DATA); + + $r = q("SELECT data, flags, os_storage, filename, filetype FROM attach WHERE hash = '%s' AND uid = %d LIMIT 1", + dbesc($this->data['hash']), + intval($this->data['uid']) + ); + if ($r) { + // @todo this should be a global definition + $unsafe_types = array('text/html', 'text/css', 'application/javascript'); + + if (in_array($r[0]['filetype'], $unsafe_types)) { + header('Content-disposition: attachment; filename="' . $r[0]['filename'] . '"'); + header('Content-type: text/plain'); + } + + if (intval($r[0]['os_storage'])) { + $x = dbunescbin($r[0]['data']); + if(strpos($x,'store') === false) + $f = 'store/' . $this->auth->owner_nick . '/' . (($this->os_path) ? $this->os_path . '/' : '') . $x; + else + $f = $x; + return fopen($f, 'rb'); + } + return dbunescbin($r[0]['data']); + } + } + + /** + * @brief Returns the ETag for a file. + * + * An ETag is a unique identifier representing the current version of the file. + * If the file changes, the ETag MUST change. + * The ETag is an arbitrary string, but MUST be surrounded by double-quotes. + * + * Return null if the ETag can not effectively be determined. + * + * @return null|string + */ + public function getETag() { + $ret = null; + if ($this->data['hash']) { + $ret = '"' . $this->data['hash'] . '"'; + } + return $ret; + } + + /** + * @brief Returns the mime-type for a file. + * + * If null is returned, we'll assume application/octet-stream + * + * @return mixed + */ + public function getContentType() { + // @todo this should be a global definition. + $unsafe_types = array('text/html', 'text/css', 'application/javascript'); + if (in_array($this->data['filetype'], $unsafe_types)) { + return 'text/plain'; + } + return $this->data['filetype']; + } + + /** + * @brief Returns the size of the node, in bytes. + * + * @return int + * filesize in bytes + */ + public function getSize() { + return $this->data['filesize']; + } + + /** + * @brief Returns the last modification time for the file, as a unix + * timestamp. + * + * @return int last modification time in UNIX timestamp + */ + public function getLastModified() { + return datetime_convert('UTC', 'UTC', $this->data['edited'], 'U'); + } + + /** + * @brief Delete the file. + * + * This method checks the permissions and then calls attach_delete() function + * to actually remove the file. + * + * @throw \Sabre\DAV\Exception\Forbidden + */ + public function delete() { + logger('delete file ' . 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.'); + } + + if ($this->auth->owner_id !== $this->auth->channel_id) { + if (($this->auth->observer !== $this->data['creator']) || intval($this->data['is_dir'])) { + throw new DAV\Exception\Forbidden('Permission denied.'); + } + } + + attach_delete($this->auth->owner_id, $this->data['hash']); + } +} diff --git a/Zotlabs/Zot/ZotHandler.php b/Zotlabs/Zot/ZotHandler.php index f9bb05410..aab336545 100644 --- a/Zotlabs/Zot/ZotHandler.php +++ b/Zotlabs/Zot/ZotHandler.php @@ -2,9 +2,6 @@ namespace Zotlabs\Zot; -require_once('Zotlabs/Zot/IHandler.php'); - - class ZotHandler implements IHandler { function Ping() { diff --git a/include/network.php b/include/network.php index aa6b54945..ecaf4e05f 100644 --- a/include/network.php +++ b/include/network.php @@ -294,8 +294,8 @@ function z_post_url_json($url, $params, $redirects = 0, $opts = array()) { } -function json_return_and_die($x) { - header("content-type: application/json"); +function json_return_and_die($x, $content_type = 'application/json') { + header("Content-type: $content_type"); echo json_encode($x); killme(); } diff --git a/include/reddav.php b/include/reddav.php index c592597a9..835521757 100644 --- a/include/reddav.php +++ b/include/reddav.php @@ -19,13 +19,13 @@ */ use Sabre\DAV; -use RedMatrix\RedDAV; +use Zotlabs\Storage; require_once('vendor/autoload.php'); require_once('include/attach.php'); -require_once('include/RedDAV/RedFile.php'); -require_once('include/RedDAV/RedDirectory.php'); -require_once('include/RedDAV/RedBasicAuth.php'); +require_once('Zotlabs/Storage/File.php'); +require_once('Zotlabs/Storage/Directory.php'); +require_once('Zotlabs/Storage/BasicAuth.php'); /** * @brief Returns an array with viewable channels. @@ -51,7 +51,7 @@ function RedChannelList(&$auth) { if (perm_is_allowed($rr['channel_id'], $auth->observer, 'view_storage')) { logger('found channel: /cloud/' . $rr['channel_address'], LOGGER_DATA); // @todo can't we drop '/cloud'? It gets stripped off anyway in RedDirectory - $ret[] = new RedDAV\RedDirectory('/cloud/' . $rr['channel_address'], $auth); + $ret[] = new Zotlabs\Storage\Directory('/cloud/' . $rr['channel_address'], $auth); } } } @@ -167,9 +167,9 @@ function RedCollectionData($file, &$auth) { foreach ($r as $rr) { //logger('filename: ' . $rr['filename'], LOGGER_DEBUG); if (intval($rr['is_dir'])) { - $ret[] = new RedDAV\RedDirectory($path . '/' . $rr['filename'], $auth); + $ret[] = new Zotlabs\Storage\Directory($path . '/' . $rr['filename'], $auth); } else { - $ret[] = new RedDAV\RedFile($path . '/' . $rr['filename'], $rr, $auth); + $ret[] = new Zotlabs\Storage\File($path . '/' . $rr['filename'], $rr, $auth); } } @@ -204,7 +204,7 @@ function RedFileData($file, &$auth, $test = false) { if ((! $file) || ($file === '/')) { - return new RedDAV\RedDirectory('/', $auth); + return new Zotlabs\Storage\Directory('/', $auth); } $file = trim($file, '/'); @@ -274,7 +274,7 @@ function RedFileData($file, &$auth, $test = false) { if ($test) return true; // final component was a directory. - return new RedDAV\RedDirectory($file, $auth); + return new Zotlabs\Storage\Directory($file, $auth); } if ($errors) { @@ -293,9 +293,9 @@ function RedFileData($file, &$auth, $test = false) { return true; if (intval($r[0]['is_dir'])) { - return new RedDAV\RedDirectory($path . '/' . $r[0]['filename'], $auth); + return new Zotlabs\Storage\Directory($path . '/' . $r[0]['filename'], $auth); } else { - return new RedDAV\RedFile($path . '/' . $r[0]['filename'], $r[0], $auth); + return new Zotlabs\Storage\File($path . '/' . $r[0]['filename'], $r[0], $auth); } } return false; diff --git a/mod/cloud.php b/mod/cloud.php index 67fc199bf..12524e71e 100644 --- a/mod/cloud.php +++ b/mod/cloud.php @@ -7,7 +7,7 @@ */ use Sabre\DAV; -use RedMatrix\RedDAV; +use Zotlabs\Storage; // composer autoloader for SabreDAV require_once('vendor/autoload.php'); @@ -35,7 +35,7 @@ function cloud_init(&$a) { if ($which) profile_load($a, $which, $profile); - $auth = new RedDAV\RedBasicAuth(); + $auth = new Zotlabs\Storage\BasicAuth(); $ob_hash = get_observer_hash(); @@ -63,7 +63,7 @@ function cloud_init(&$a) { $_SERVER['REQUEST_URI'] = strip_zids($_SERVER['REQUEST_URI']); $_SERVER['REQUEST_URI'] = preg_replace('/[\?&]davguest=(.*?)([\?&]|$)/ism', '', $_SERVER['REQUEST_URI']); - $rootDirectory = new RedDAV\RedDirectory('/', $auth); + $rootDirectory = new Zotlabs\Storage\Directory('/', $auth); // A SabreDAV server-object $server = new DAV\Server($rootDirectory); @@ -86,16 +86,16 @@ function cloud_init(&$a) { } } - require_once('include/RedDAV/RedBrowser.php'); + require_once('Zotlabs/Storage/Browser.php'); // provide a directory view for the cloud in Hubzilla - $browser = new RedDAV\RedBrowser($auth); + $browser = new Zotlabs\Storage\Browser($auth); $auth->setBrowserPlugin($browser); $server->addPlugin($browser); // Experimental QuotaPlugin -// require_once('include/RedDAV/QuotaPlugin.php'); -// $server->addPlugin(new RedDAV\QuotaPlugin($auth)); +// require_once('Zotlabs\Storage/QuotaPlugin.php'); +// $server->addPlugin(new Zotlabs\Storage\\QuotaPlugin($auth)); // All we need to do now, is to fire up the server $server->exec(); diff --git a/mod/dav.php b/mod/dav.php index d4695a544..fb5054d0d 100644 --- a/mod/dav.php +++ b/mod/dav.php @@ -7,7 +7,7 @@ */ use Sabre\DAV; -use RedMatrix\RedDAV; +use Zotlabs\Storage; // composer autoloader for SabreDAV require_once('vendor/autoload.php'); @@ -54,7 +54,7 @@ function dav_init(&$a) { if ($which) profile_load($a, $which, $profile); - $auth = new RedDAV\RedBasicAuth(); + $auth = new Zotlabs\Storage\BasicAuth(); $ob_hash = get_observer_hash(); @@ -82,7 +82,7 @@ function dav_init(&$a) { $_SERVER['REQUEST_URI'] = strip_zids($_SERVER['REQUEST_URI']); $_SERVER['REQUEST_URI'] = preg_replace('/[\?&]davguest=(.*?)([\?&]|$)/ism', '', $_SERVER['REQUEST_URI']); - $rootDirectory = new RedDAV\RedDirectory('/', $auth); + $rootDirectory = new Zotlabs\Storage\Directory('/', $auth); // A SabreDAV server-object $server = new DAV\Server($rootDirectory); @@ -108,7 +108,7 @@ function dav_init(&$a) { if ((! $auth->observer) && ($_SERVER['REQUEST_METHOD'] === 'GET')) { try { $x = RedFileData('/' . $a->cmd, $auth); - if($x instanceof RedDAV\RedFile) + if($x instanceof Zotlabs\Storage\File) $isapublic_file = true; } catch (Exception $e) { @@ -126,14 +126,14 @@ function dav_init(&$a) { } } - require_once('include/RedDAV/RedBrowser.php'); + require_once('Zotlabs/Storage/Browser.php'); // provide a directory view for the cloud in Hubzilla - $browser = new RedDAV\RedBrowser($auth); + $browser = new Zotlabs\Storage\Browser($auth); $auth->setBrowserPlugin($browser); // Experimental QuotaPlugin -// require_once('include/RedDAV/QuotaPlugin.php'); -// $server->addPlugin(new RedDAV\QuotaPlugin($auth)); +// require_once('Zotlabs/Storage/QuotaPlugin.php'); +// $server->addPlugin(new Zotlabs\Storage\QuotaPlugin($auth)); // All we need to do now, is to fire up the server $server->exec(); diff --git a/mod/post.php b/mod/post.php index 6555a16c2..481a4a896 100644 --- a/mod/post.php +++ b/mod/post.php @@ -12,7 +12,6 @@ require_once('include/zot.php'); function post_init(&$a) { if (array_key_exists('auth', $_REQUEST)) { - require_once('Zotlabs/Zot/Auth.php'); $x = new Zotlabs\Zot\Auth($_REQUEST); exit; } @@ -22,9 +21,6 @@ function post_init(&$a) { function post_post(&$a) { - require_once('Zotlabs/Zot/Receiver.php'); - require_once('Zotlabs/Zot/ZotHandler.php'); - $z = new Zotlabs\Zot\Receiver($_REQUEST['data'],get_config('system','prvkey'), new Zotlabs\Zot\ZotHandler()); // notreached; diff --git a/mod/wfinger.php b/mod/wfinger.php index 5270c8f31..0c1c03f08 100644 --- a/mod/wfinger.php +++ b/mod/wfinger.php @@ -51,8 +51,6 @@ function wfinger_init(&$a) { header('Access-Control-Allow-Origin: *'); - header('Content-type: application/jrd+json'); - if($resource && $r) { @@ -124,7 +122,6 @@ function wfinger_init(&$a) { $arr = array('channel' => $r[0], 'request' => $_REQUEST, 'result' => $result); call_hooks('webfinger',$arr); - echo json_encode($arr['result']); - killme(); + json_return_and_die($arr['result'],'application/jrd+json'); } \ No newline at end of file From ca52d39abec4c3afba3d11ada38409a25f5b61e8 Mon Sep 17 00:00:00 2001 From: redmatrix Date: Fri, 19 Feb 2016 11:58:17 -0800 Subject: [PATCH 2/4] finish up the storage move --- include/RedDAV/RedBasicAuth.php | 212 ------------- include/RedDAV/RedBrowser.php | 372 ---------------------- include/RedDAV/RedDirectory.php | 536 -------------------------------- include/RedDAV/RedFile.php | 322 ------------------- include/reddav.php | 6 +- mod/cloud.php | 2 +- mod/dav.php | 2 +- 7 files changed, 5 insertions(+), 1447 deletions(-) delete mode 100644 include/RedDAV/RedBasicAuth.php delete mode 100644 include/RedDAV/RedBrowser.php delete mode 100644 include/RedDAV/RedDirectory.php delete mode 100644 include/RedDAV/RedFile.php diff --git a/include/RedDAV/RedBasicAuth.php b/include/RedDAV/RedBasicAuth.php deleted file mode 100644 index 19dd9a5f0..000000000 --- a/include/RedDAV/RedBasicAuth.php +++ /dev/null @@ -1,212 +0,0 @@ -setAuthenticated($r[0]); - } - } - $r = q("SELECT * FROM channel WHERE channel_address = '%s' LIMIT 1", - dbesc($username) - ); - if ($r) { - $x = q("SELECT account_flags, account_salt, account_password FROM account WHERE account_id = %d LIMIT 1", - intval($r[0]['channel_account_id']) - ); - if ($x) { - // @fixme this foreach should not be needed? - foreach ($x as $record) { - if (($record['account_flags'] == ACCOUNT_OK) || ($record['account_flags'] == ACCOUNT_UNVERIFIED) - && (hash('whirlpool', $record['account_salt'] . $password) === $record['account_password'])) { - logger('password verified for ' . $username); - return $this->setAuthenticated($r[0]); - } - } - } - } - - $error = 'password failed for ' . $username; - logger($error); - log_failed_login($error); - - return false; - } - - /** - * @brief Sets variables and session parameters after successfull authentication. - * - * @param array $r - * Array with the values for the authenticated channel. - * @return bool - */ - protected function setAuthenticated($r) { - $this->channel_name = $r['channel_address']; - $this->channel_id = $r['channel_id']; - $this->channel_hash = $this->observer = $r['channel_hash']; - $_SESSION['uid'] = $r['channel_id']; - $_SESSION['account_id'] = $r['channel_account_id']; - $_SESSION['authenticated'] = true; - return true; - } - - /** - * Sets the channel_name from the currently logged-in channel. - * - * @param string $name - * The channel's name - */ - public function setCurrentUser($name) { - $this->channel_name = $name; - } - /** - * Returns information about the currently logged-in channel. - * - * If nobody is currently logged in, this method should return null. - * - * @see \Sabre\DAV\Auth\Backend\AbstractBasic::getCurrentUser - * @return string|null - */ - public function getCurrentUser() { - return $this->channel_name; - } - - /** - * @brief Sets the timezone from the channel in RedBasicAuth. - * - * Set in mod/cloud.php if the channel has a timezone set. - * - * @param string $timezone - * The channel's timezone. - * @return void - */ - public function setTimezone($timezone) { - $this->timezone = $timezone; - } - /** - * @brief Returns the timezone. - * - * @return string - * Return the channel's timezone. - */ - public function getTimezone() { - return $this->timezone; - } - - /** - * @brief Set browser plugin for SabreDAV. - * - * @see RedBrowser::set_writeable() - * @param \Sabre\DAV\Browser\Plugin $browser - */ - public function setBrowserPlugin($browser) { - $this->browser = $browser; - } - - /** - * @brief Prints out all RedBasicAuth variables to logger(). - * - * @return void - */ - public function log() { - logger('channel_name ' . $this->channel_name, LOGGER_DATA); - logger('channel_id ' . $this->channel_id, LOGGER_DATA); - logger('channel_hash ' . $this->channel_hash, LOGGER_DATA); - logger('observer ' . $this->observer, LOGGER_DATA); - logger('owner_id ' . $this->owner_id, LOGGER_DATA); - logger('owner_nick ' . $this->owner_nick, LOGGER_DATA); - } -} \ No newline at end of file diff --git a/include/RedDAV/RedBrowser.php b/include/RedDAV/RedBrowser.php deleted file mode 100644 index 1aa5f435e..000000000 --- a/include/RedDAV/RedBrowser.php +++ /dev/null @@ -1,372 +0,0 @@ -auth = $auth; - parent::__construct(true, false); - } - - /** - * The DAV browser is instantiated after the auth module and directory classes - * but before we know the current directory and who the owner and observer - * are. So we add a pointer to the browser into the auth module and vice versa. - * Then when we've figured out what directory is actually being accessed, we - * call the following function to decide whether or not to show web elements - * which include writeable objects. - * - * @fixme It only disable/enable the visible parts. Not the POST handler - * which handels the actual requests when uploading files or creating folders. - * - * @todo Maybe this whole way of doing this can be solved with some - * $server->subscribeEvent(). - */ - public function set_writeable() { - if (! $this->auth->owner_id) { - $this->enablePost = false; - } - - if (! perm_is_allowed($this->auth->owner_id, get_observer_hash(), 'write_storage')) { - $this->enablePost = false; - } else { - $this->enablePost = true; - } - } - - /** - * @brief Creates the directory listing for the given path. - * - * @param string $path which should be displayed - */ - public function generateDirectoryIndex($path) { - // (owner_id = channel_id) is visitor owner of this directory? - $is_owner = ((local_channel() && $this->auth->owner_id == local_channel()) ? true : false); - - if ($this->auth->getTimezone()) - date_default_timezone_set($this->auth->getTimezone()); - - require_once('include/conversation.php'); - require_once('include/text.php'); - if ($this->auth->owner_nick) { - $html = profile_tabs(get_app(), (($is_owner) ? true : false), $this->auth->owner_nick); - } - - $files = $this->server->getPropertiesForPath($path, array( - '{DAV:}displayname', - '{DAV:}resourcetype', - '{DAV:}getcontenttype', - '{DAV:}getcontentlength', - '{DAV:}getlastmodified', - ), 1); - - - $parent = $this->server->tree->getNodeForPath($path); - - $parentpath = array(); - // only show parent if not leaving /cloud/; TODO how to improve this? - if ($path && $path != "cloud") { - list($parentUri) = DAV\URLUtil::splitPath($path); - $fullPath = DAV\URLUtil::encodePath($this->server->getBaseUri() . $parentUri); - - $parentpath['icon'] = $this->enableAssets ? '' . t('parent') . '' : ''; - $parentpath['path'] = $fullPath; - } - - $f = array(); - foreach ($files as $file) { - $ft = array(); - $type = null; - - // This is the current directory, we can skip it - if (rtrim($file['href'],'/') == $path) continue; - - list(, $name) = DAV\URLUtil::splitPath($file['href']); - - if (isset($file[200]['{DAV:}resourcetype'])) { - $type = $file[200]['{DAV:}resourcetype']->getValue(); - - // resourcetype can have multiple values - if (!is_array($type)) $type = array($type); - - foreach ($type as $k=>$v) { - // Some name mapping is preferred - switch ($v) { - case '{DAV:}collection' : - $type[$k] = t('Collection'); - break; - case '{DAV:}principal' : - $type[$k] = t('Principal'); - break; - case '{urn:ietf:params:xml:ns:carddav}addressbook' : - $type[$k] = t('Addressbook'); - break; - case '{urn:ietf:params:xml:ns:caldav}calendar' : - $type[$k] = t('Calendar'); - break; - case '{urn:ietf:params:xml:ns:caldav}schedule-inbox' : - $type[$k] = t('Schedule Inbox'); - break; - case '{urn:ietf:params:xml:ns:caldav}schedule-outbox' : - $type[$k] = t('Schedule Outbox'); - break; - case '{http://calendarserver.org/ns/}calendar-proxy-read' : - $type[$k] = 'Proxy-Read'; - break; - case '{http://calendarserver.org/ns/}calendar-proxy-write' : - $type[$k] = 'Proxy-Write'; - break; - } - } - $type = implode(', ', $type); - } - - // If no resourcetype was found, we attempt to use - // the contenttype property - if (!$type && isset($file[200]['{DAV:}getcontenttype'])) { - $type = $file[200]['{DAV:}getcontenttype']; - } - if (!$type) $type = t('Unknown'); - - $size = isset($file[200]['{DAV:}getcontentlength']) ? (int)$file[200]['{DAV:}getcontentlength'] : ''; - $lastmodified = ((isset($file[200]['{DAV:}getlastmodified'])) ? $file[200]['{DAV:}getlastmodified']->getTime()->format('Y-m-d H:i:s') : ''); - - $fullPath = DAV\URLUtil::encodePath('/' . trim($this->server->getBaseUri() . ($path ? $path . '/' : '') . $name, '/')); - - - $displayName = isset($file[200]['{DAV:}displayname']) ? $file[200]['{DAV:}displayname'] : $name; - - $displayName = $this->escapeHTML($displayName); - $type = $this->escapeHTML($type); - - $icon = ''; - - if ($this->enableAssets) { - $node = $this->server->tree->getNodeForPath(($path ? $path . '/' : '') . $name); - foreach (array_reverse($this->iconMap) as $class=>$iconName) { - if ($node instanceof $class) { - $icon = ''; - break; - } - } - } - - $parentHash = ''; - $owner = $this->auth->owner_id; - $splitPath = explode('/', $fullPath); - if (count($splitPath) > 3) { - for ($i = 3; $i < count($splitPath); $i++) { - $attachName = urldecode($splitPath[$i]); - $attachHash = $this->findAttachHash($owner, $parentHash, $attachName); - $parentHash = $attachHash; - } - } - - $attachIcon = ""; // ""; - - // put the array for this file together - $ft['attachId'] = $this->findAttachIdByHash($attachHash); - $ft['fileStorageUrl'] = substr($fullPath, 0, strpos($fullPath, "cloud/")) . "filestorage/" . $this->auth->getCurrentUser(); - $ft['icon'] = $icon; - $ft['attachIcon'] = (($size) ? $attachIcon : ''); - // @todo Should this be an item value, not a global one? - $ft['is_owner'] = $is_owner; - $ft['fullPath'] = $fullPath; - $ft['displayName'] = $displayName; - $ft['type'] = $type; - $ft['size'] = $size; - $ft['sizeFormatted'] = userReadableSize($size); - $ft['lastmodified'] = (($lastmodified) ? datetime_convert('UTC', date_default_timezone_get(), $lastmodified) : ''); - $ft['iconFromType'] = getIconFromType($type); - - $f[] = $ft; - } - - $output = ''; - if ($this->enablePost) { - $this->server->broadcastEvent('onHTMLActionsPanel', array($parent, &$output)); - } - - $html .= replace_macros(get_markup_template('cloud.tpl'), array( - '$header' => t('Files') . ": " . $this->escapeHTML($path) . "/", - '$total' => t('Total'), - '$actionspanel' => $output, - '$shared' => t('Shared'), - '$create' => t('Create'), - '$upload' => t('Upload'), - '$is_owner' => $is_owner, - '$parentpath' => $parentpath, - '$entries' => $f, - '$name' => t('Name'), - '$type' => t('Type'), - '$size' => t('Size'), - '$lastmod' => t('Last Modified'), - '$parent' => t('parent'), - '$edit' => t('Edit'), - '$delete' => t('Delete'), - '$nick' => $this->auth->getCurrentUser() - )); - - $a = get_app(); - $a->page['content'] = $html; - load_pdl($a); - - $theme_info_file = "view/theme/" . current_theme() . "/php/theme.php"; - if (file_exists($theme_info_file)){ - require_once($theme_info_file); - if (function_exists(str_replace('-', '_', current_theme()) . '_init')) { - $func = str_replace('-', '_', current_theme()) . '_init'; - $func($a); - } - } - construct_page($a); - } - - /** - * @brief Creates a form to add new folders and upload files. - * - * @param \Sabre\DAV\INode $node - * @param string &$output - */ - public function htmlActionsPanel(DAV\INode $node, &$output) { - if (! $node instanceof DAV\ICollection) - return; - - // We also know fairly certain that if an object is a non-extended - // SimpleCollection, we won't need to show the panel either. - if (get_class($node) === 'Sabre\\DAV\\SimpleCollection') - return; - - // Storage and quota for the account (all channels of the owner of this directory)! - $limit = service_class_fetch($owner, 'attach_upload_limit'); - $r = q("SELECT SUM(filesize) AS total FROM attach WHERE aid = %d", - intval($this->auth->channel_account_id) - ); - $used = $r[0]['total']; - if ($used) { - $quotaDesc = t('You are using %1$s of your available file storage.'); - $quotaDesc = sprintf($quotaDesc, - userReadableSize($used)); - } - if ($limit && $used) { - $quotaDesc = t('You are using %1$s of %2$s available file storage. (%3$s%)'); - $quotaDesc = sprintf($quotaDesc, - userReadableSize($used), - userReadableSize($limit), - round($used / $limit, 1) * 100); - } - - // prepare quota for template - $quota = array(); - $quota['used'] = $used; - $quota['limit'] = $limit; - $quota['desc'] = $quotaDesc; - $quota['warning'] = ((($limit) && ((round($used / $limit, 1) * 100) >= 90)) ? t('WARNING:') : ''); // 10485760 bytes = 100MB - - $output .= replace_macros(get_markup_template('cloud_actionspanel.tpl'), array( - '$folder_header' => t('Create new folder'), - '$folder_submit' => t('Create'), - '$upload_header' => t('Upload file'), - '$upload_submit' => t('Upload'), - '$quota' => $quota - )); - } - - /** - * This method takes a path/name of an asset and turns it into url - * suiteable for http access. - * - * @param string $assetName - * @return string - */ - protected function getAssetUrl($assetName) { - return z_root() . '/cloud/?sabreAction=asset&assetName=' . urlencode($assetName); - } - - /** - * @brief Return the hash of an attachment. - * - * Given the owner, the parent folder and and attach name get the attachment - * hash. - * - * @param int $owner - * The owner_id - * @param string $hash - * The parent's folder hash - * @param string $attachName - * The name of the attachment - * @return string - */ - - protected function findAttachHash($owner, $parentHash, $attachName) { - $r = q("SELECT hash FROM attach WHERE uid = %d AND folder = '%s' AND filename = '%s' ORDER BY edited DESC LIMIT 1", - intval($owner), - dbesc($parentHash), - dbesc($attachName) - ); - $hash = ""; - if ($r) { - foreach ($r as $rr) { - $hash = $rr['hash']; - } - } - return $hash; - } - - /** - * @brief Returns an attachment's id for a given hash. - * - * This id is used to access the attachment in filestorage/ - * - * @param string $attachHash - * The hash of an attachment - * @return string - */ - protected function findAttachIdByHash($attachHash) { - $r = q("SELECT id FROM attach WHERE hash = '%s' ORDER BY edited DESC LIMIT 1", - dbesc($attachHash) - ); - $id = ""; - if ($r) { - foreach ($r as $rr) { - $id = $rr['id']; - } - } - return $id; - } -} diff --git a/include/RedDAV/RedDirectory.php b/include/RedDAV/RedDirectory.php deleted file mode 100644 index 8d8af5bd3..000000000 --- a/include/RedDAV/RedDirectory.php +++ /dev/null @@ -1,536 +0,0 @@ -ext_path = $ext_path; - // remove "/cloud" from the beginning of the path - $modulename = get_app()->module; - $this->red_path = ((strpos($ext_path, '/' . $modulename) === 0) ? substr($ext_path, strlen($modulename) + 1) : $ext_path); - if (! $this->red_path) { - $this->red_path = '/'; - } - $this->auth = $auth_plugin; - $this->folder_hash = ''; - $this->getDir(); - - if ($this->auth->browser) { - $this->auth->browser->set_writeable(); - } - } - - private function log() { - logger('ext_path ' . $this->ext_path, LOGGER_DATA); - logger('os_path ' . $this->os_path, LOGGER_DATA); - logger('red_path ' . $this->red_path, LOGGER_DATA); - } - - /** - * @brief Returns an array with all the child nodes. - * - * @throw \Sabre\DAV\Exception\Forbidden - * @return array \Sabre\DAV\INode[] - */ - public function getChildren() { - logger('children for ' . $this->ext_path, LOGGER_DATA); - $this->log(); - - if (get_config('system', 'block_public') && (! $this->auth->channel_id) && (! $this->auth->observer)) { - throw new DAV\Exception\Forbidden('Permission denied.'); - } - - if (($this->auth->owner_id) && (! perm_is_allowed($this->auth->owner_id, $this->auth->observer, 'view_storage'))) { - throw new DAV\Exception\Forbidden('Permission denied.'); - } - - $contents = RedCollectionData($this->red_path, $this->auth); - return $contents; - } - - /** - * @brief Returns a child by name. - * - * - * @throw \Sabre\DAV\Exception\Forbidden - * @throw \Sabre\DAV\Exception\NotFound - * @param string $name - */ - public function getChild($name) { - logger($name, LOGGER_DATA); - - if (get_config('system', 'block_public') && (! $this->auth->channel_id) && (! $this->auth->observer)) { - throw new DAV\Exception\Forbidden('Permission denied.'); - } - - if (($this->auth->owner_id) && (! perm_is_allowed($this->auth->owner_id, $this->auth->observer, 'view_storage'))) { - throw new DAV\Exception\Forbidden('Permission denied.'); - } - - $modulename = get_app()->module; - if ($this->red_path === '/' && $name === $modulename) { - return new RedDirectory('/' . $modulename, $this->auth); - } - - $x = RedFileData($this->ext_path . '/' . $name, $this->auth); - if ($x) { - return $x; - } - - 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(basename($this->red_path), LOGGER_DATA); - return (basename($this->red_path)); - } - - /** - * @brief Renames the directory. - * - * @todo handle duplicate directory name - * - * @throw \Sabre\DAV\Exception\Forbidden - * @param string $name The new name of the directory. - * @return void - */ - public function setName($name) { - logger('old name ' . basename($this->red_path) . ' -> ' . $name, LOGGER_DATA); - - if ((! $name) || (! $this->auth->owner_id)) { - logger('permission denied ' . $name); - throw new DAV\Exception\Forbidden('Permission denied.'); - } - - if (! perm_is_allowed($this->auth->owner_id, $this->auth->observer, 'write_storage')) { - logger('permission denied '. $name); - 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", - dbesc($name), - dbesc($this->folder_hash), - intval($this->auth->owner_id) - ); - - $this->red_path = $new_path; - } - - /** - * @brief Creates a new file in the directory. - * - * Data will either be supplied as a stream resource, or in certain cases - * as a string. Keep in mind that you may have to support either. - * - * After successful creation of the file, you may choose to return the ETag - * of the new file here. - * - * @throw \Sabre\DAV\Exception\Forbidden - * @param string $name Name of the file - * @param resource|string $data Initial payload - * @return null|string ETag - */ - public function createFile($name, $data = null) { - logger($name, LOGGER_DEBUG); - - if (! $this->auth->owner_id) { - logger('permission denied ' . $name); - throw new DAV\Exception\Forbidden('Permission denied.'); - } - - if (! perm_is_allowed($this->auth->owner_id, $this->auth->observer, 'write_storage')) { - logger('permission denied ' . $name); - throw new DAV\Exception\Forbidden('Permission denied.'); - } - - $mimetype = z_mime_content_type($name); - - $c = q("SELECT * FROM channel WHERE channel_id = %d AND channel_removed = 0 LIMIT 1", - intval($this->auth->owner_id) - ); - - if (! $c) { - logger('no channel'); - throw new DAV\Exception\Forbidden('Permission denied.'); - } - - $filesize = 0; - $hash = random_string(); - - $f = 'store/' . $this->auth->owner_nick . '/' . (($this->os_path) ? $this->os_path . '/' : '') . $hash; - - $direct = null; - - if($this->folder_hash) { - $r = q("select * from attach where hash = '%s' and is_dir = 1 and uid = %d limit 1", - dbesc($this->folder_hash), - intval($c[0]['channel_id']) - ); - if($r) - $direct = $r[0]; - } - - if(($direct) && (($direct['allow_cid']) || ($direct['allow_gid']) || ($direct['deny_cid']) || ($direct['deny_gid']))) { - $allow_cid = $direct['allow_cid']; - $allow_gid = $direct['allow_gid']; - $deny_cid = $direct['deny_cid']; - $deny_gid = $direct['deny_gid']; - } - else { - $allow_cid = $c[0]['channel_allow_cid']; - $allow_gid = $c[0]['channel_allow_gid']; - $deny_cid = $c[0]['channel_deny_cid']; - $deny_gid = $c[0]['channel_deny_gid']; - } - - $r = q("INSERT INTO attach ( aid, uid, hash, creator, filename, folder, os_storage, filetype, filesize, revision, is_photo, data, created, edited, allow_cid, allow_gid, deny_cid, deny_gid ) - VALUES ( %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s' ) ", - intval($c[0]['channel_account_id']), - intval($c[0]['channel_id']), - dbesc($hash), - dbesc($this->auth->observer), - dbesc($name), - dbesc($this->folder_hash), - intval(1), - dbesc($mimetype), - intval($filesize), - intval(0), - intval($is_photo), - dbesc($f), - dbesc(datetime_convert()), - dbesc(datetime_convert()), - dbesc($allow_cid), - dbesc($allow_gid), - dbesc($deny_cid), - dbesc($deny_gid) - ); - - - - // returns the number of bytes that were written to the file, or FALSE on failure - $size = file_put_contents($f, $data); - // delete attach entry if file_put_contents() failed - if ($size === false) { - logger('file_put_contents() failed to ' . $f); - attach_delete($c[0]['channel_id'], $hash); - return; - } - - // returns now - $edited = datetime_convert(); - - - - $is_photo = 0; - $x = @getimagesize($f); - logger('getimagesize: ' . print_r($x,true), LOGGER_DATA); - if(($x) && ($x[2] === IMAGETYPE_GIF || $x[2] === IMAGETYPE_JPEG || $x[2] === IMAGETYPE_PNG)) { - $is_photo = 1; - } - - - // updates entry with filesize and timestamp - $d = q("UPDATE attach SET filesize = '%s', is_photo = %d, edited = '%s' WHERE hash = '%s' AND uid = %d", - dbesc($size), - intval($is_photo), - dbesc($edited), - dbesc($hash), - intval($c[0]['channel_id']) - ); - - // update the folder's lastmodified timestamp - $e = q("UPDATE attach SET edited = '%s' WHERE hash = '%s' AND uid = %d", - dbesc($edited), - dbesc($this->folder_hash), - intval($c[0]['channel_id']) - ); - - $maxfilesize = get_config('system', 'maxfilesize'); - if (($maxfilesize) && ($size > $maxfilesize)) { - attach_delete($c[0]['channel_id'], $hash); - return; - } - - // check against service class quota - $limit = service_class_fetch($c[0]['channel_id'], 'attach_upload_limit'); - if ($limit !== false) { - $x = q("SELECT SUM(filesize) AS total FROM attach WHERE aid = %d ", - intval($c[0]['channel_account_id']) - ); - if (($x) && ($x[0]['total'] + $size > $limit)) { - logger('service class limit exceeded for ' . $c[0]['channel_name'] . ' total usage is ' . $x[0]['total'] . ' limit is ' . $limit); - attach_delete($c[0]['channel_id'], $hash); - return; - } - } - - if($is_photo) { - $album = ''; - if($this->folder_hash) { - $f1 = q("select filename from attach WHERE hash = '%s' AND uid = %d", - dbesc($this->folder_hash), - intval($c[0]['channel_id']) - ); - if($f1) - $album = $f1[0]['filename']; - } - - require_once('include/photos.php'); - $args = array( 'resource_id' => $hash, 'album' => $album, 'os_path' => $f, 'filename' => $name, 'getimagesize' => $x, 'directory' => $direct); - $p = photo_upload($c[0],get_app()->get_observer(),$args); - } - - } - - /** - * @brief Creates a new subdirectory. - * - * @param string $name the directory to create - * @return void - */ - public function createDirectory($name) { - logger($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.'); - } - - $r = q("SELECT * FROM channel WHERE channel_id = %d AND channel_removed = 0 LIMIT 1", - intval($this->auth->owner_id) - ); - - if ($r) { - $result = attach_mkdir($r[0], $this->auth->observer, array('filename' => $name, 'folder' => $this->folder_hash)); - if (! $result['success']) { - logger('error ' . print_r($result, true), LOGGER_DEBUG); - } - } - } - - /** - * @brief delete directory - */ - - public function delete() { - logger('delete file ' . basename($this->red_path), 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.'); - } - - if ($this->auth->owner_id !== $this->auth->channel_id) { - if (($this->auth->observer !== $this->data['creator']) || intval($this->data['is_dir'])) { - throw new DAV\Exception\Forbidden('Permission denied.'); - } - } - - attach_delete($this->auth->owner_id, $this->folder_hash); - } - - - /** - * @brief Checks if a child exists. - * - * @param string $name - * The name to check if it exists. - * @return boolean - */ - public function childExists($name) { - // On /cloud we show a list of available channels. - // @todo what happens if no channels are available? - $modulename = get_app()->module; - if ($this->red_path === '/' && $name === $modulename) { - //logger('We are at ' $modulename . ' show a channel list', LOGGER_DEBUG); - return true; - } - - $x = RedFileData($this->ext_path . '/' . $name, $this->auth, true); - //logger('RedFileData returns: ' . print_r($x, true), LOGGER_DATA); - if ($x) - return true; - - return false; - } - - /** - * @todo add description of what this function does. - * - * @throw \Sabre\DAV\Exception\NotFound - * @return void - */ - function getDir() { - - logger('GetDir: ' . $this->ext_path, LOGGER_DEBUG); - $this->auth->log(); - $modulename = get_app()->module; - - $file = $this->ext_path; - - $x = strpos($file, '/' . $modulename); - if ($x === 0) { - $file = substr($file, strlen($modulename) + 1); - } - - if ((! $file) || ($file === '/')) { - return; - } - - $file = trim($file, '/'); - $path_arr = explode('/', $file); - - if (! $path_arr) - return; - - logger('paths: ' . print_r($path_arr, true), LOGGER_DATA); - - $channel_name = $path_arr[0]; - - $r = q("SELECT channel_id FROM channel WHERE channel_address = '%s' AND channel_removed = 0 LIMIT 1", - dbesc($channel_name) - ); - - if (! $r) { - throw new DAV\Exception\NotFound('The file with name: ' . $channel_name . ' could not be found.'); - } - - $channel_id = $r[0]['channel_id']; - $this->auth->owner_id = $channel_id; - $this->auth->owner_nick = $channel_name; - - $path = '/' . $channel_name; - $folder = ''; - $os_path = ''; - - for ($x = 1; $x < count($path_arr); $x++) { - $r = q("select id, hash, filename, flags, is_dir from attach where folder = '%s' and filename = '%s' and uid = %d and is_dir != 0", - dbesc($folder), - dbesc($path_arr[$x]), - intval($channel_id) - ); - if ($r && intval($r[0]['is_dir'])) { - $folder = $r[0]['hash']; - if (strlen($os_path)) - $os_path .= '/'; - $os_path .= $folder; - - $path = $path . '/' . $r[0]['filename']; - } - } - $this->folder_hash = $folder; - $this->os_path = $os_path; - } - - /** - * @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() { - $r = q("SELECT edited FROM attach WHERE folder = '%s' AND uid = %d ORDER BY edited DESC LIMIT 1", - dbesc($this->folder_hash), - intval($this->auth->owner_id) - ); - 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'); - } - - /** - * @brief Return quota usage. - * - * @fixme Should guests relly see the used/free values from filesystem of the - * complete store directory? - * - * @return array with used and free values in bytes. - */ - public function getQuotaInfo() { - // values from the filesystem of the complete store/ directory - $limit = disk_total_space('store'); - $free = disk_free_space('store'); - - if ($this->auth->owner_id) { - $c = q("select * from channel where channel_id = %d and channel_removed = 0 limit 1", - intval($this->auth->owner_id) - ); - - $ulimit = service_class_fetch($c[0]['channel_id'], 'attach_upload_limit'); - $limit = (($ulimit) ? $ulimit : $limit); - - $x = q("select sum(filesize) as total from attach where aid = %d", - intval($c[0]['channel_account_id']) - ); - $free = (($x) ? $limit - $x[0]['total'] : 0); - } - - return array( - $limit - $free, - $free - ); - } -} \ No newline at end of file diff --git a/include/RedDAV/RedFile.php b/include/RedDAV/RedFile.php deleted file mode 100644 index 3283a6e88..000000000 --- a/include/RedDAV/RedFile.php +++ /dev/null @@ -1,322 +0,0 @@ -name = $name; - $this->data = $data; - $this->auth = $auth; - - logger(print_r($this->data, true), LOGGER_DATA); - } - - /** - * @brief Returns the name of the file. - * - * @return string - */ - public function getName() { - //logger(basename($this->name), LOGGER_DATA); - return basename($this->name); - } - - /** - * @brief Renames the file. - * - * @throw Sabre\DAV\Exception\Forbidden - * @param string $name The new name of the file. - * @return void - */ - public function setName($newName) { - logger('old name ' . basename($this->name) . ' -> ' . $newName, LOGGER_DATA); - - if ((! $newName) || (! $this->auth->owner_id) || (! perm_is_allowed($this->auth->owner_id, $this->auth->observer, 'write_storage'))) { - logger('permission denied '. $newName); - throw new DAV\Exception\Forbidden('Permission denied.'); - } - - $newName = str_replace('/', '%2F', $newName); - - $r = q("UPDATE attach SET filename = '%s' WHERE hash = '%s' AND id = %d", - dbesc($newName), - dbesc($this->data['hash']), - intval($this->data['id']) - ); - } - - /** - * @brief Updates the data of the file. - * - * @param resource $data - * @return void - */ - public function put($data) { - logger('put file: ' . basename($this->name), LOGGER_DEBUG); - $size = 0; - - // @todo only 3 values are needed - $c = q("SELECT * FROM channel WHERE channel_id = %d AND channel_removed = 0 LIMIT 1", - intval($this->auth->owner_id) - ); - - $is_photo = false; - $album = ''; - - $r = q("SELECT flags, folder, os_storage, filename, is_photo FROM attach WHERE hash = '%s' AND uid = %d LIMIT 1", - dbesc($this->data['hash']), - intval($c[0]['channel_id']) - ); - if ($r) { - if (intval($r[0]['os_storage'])) { - $d = q("select folder, data from attach where hash = '%s' and uid = %d limit 1", - dbesc($this->data['hash']), - intval($c[0]['channel_id']) - ); - if($d) { - if($d[0]['folder']) { - $f1 = q("select * from attach where is_dir = 1 and hash = '%s' and uid = %d limit 1", - dbesc($d[0]['folder']), - intval($c[0]['channel_id']) - ); - if($f1) { - $album = $f1[0]['filename']; - $direct = $f1[0]; - } - } - $fname = dbunescbin($d[0]['data']); - if(strpos($fname,'store') === false) - $f = 'store/' . $this->auth->owner_nick . '/' . $fname ; - else - $f = $fname; - - // @todo check return value and set $size directly - @file_put_contents($f, $data); - $size = @filesize($f); - logger('filename: ' . $f . ' size: ' . $size, LOGGER_DEBUG); - } - $gis = @getimagesize($f); - logger('getimagesize: ' . print_r($gis,true), LOGGER_DATA); - if(($gis) && ($gis[2] === IMAGETYPE_GIF || $gis[2] === IMAGETYPE_JPEG || $gis[2] === IMAGETYPE_PNG)) { - $is_photo = 1; - } - } - else { - // this shouldn't happen any more - $r = q("UPDATE attach SET data = '%s' WHERE hash = '%s' AND uid = %d", - dbescbin(stream_get_contents($data)), - dbesc($this->data['hash']), - intval($this->data['uid']) - ); - $r = q("SELECT length(data) AS fsize FROM attach WHERE hash = '%s' AND uid = %d LIMIT 1", - dbesc($this->data['hash']), - intval($this->data['uid']) - ); - if ($r) { - $size = $r[0]['fsize']; - } - } - } - - // returns now() - $edited = datetime_convert(); - - $d = q("UPDATE attach SET filesize = '%s', is_photo = %d, edited = '%s' WHERE hash = '%s' AND uid = %d", - dbesc($size), - intval($is_photo), - dbesc($edited), - dbesc($this->data['hash']), - intval($c[0]['channel_id']) - ); - - if($is_photo) { - require_once('include/photos.php'); - $args = array( 'resource_id' => $this->data['hash'], 'album' => $album, 'os_path' => $f, 'filename' => $r[0]['filename'], 'getimagesize' => $gis, 'directory' => $direct ); - $p = photo_upload($c[0],get_app()->get_observer(),$args); - } - - // update the folder's lastmodified timestamp - $e = q("UPDATE attach SET edited = '%s' WHERE hash = '%s' AND uid = %d", - 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? - // 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)) { - attach_delete($c[0]['channel_id'], $this->data['hash']); - return; - } - - $limit = service_class_fetch($c[0]['channel_id'], 'attach_upload_limit'); - if ($limit !== false) { - $x = q("select sum(filesize) as total from attach where aid = %d ", - intval($c[0]['channel_account_id']) - ); - if (($x) && ($x[0]['total'] + $size > $limit)) { - logger('service class limit exceeded for ' . $c[0]['channel_name'] . ' total usage is ' . $x[0]['total'] . ' limit is ' . $limit); - attach_delete($c[0]['channel_id'], $this->data['hash']); - return; - } - } - } - - /** - * @brief Returns the raw data. - * - * @return string - */ - public function get() { - logger('get file ' . basename($this->name), LOGGER_DEBUG); - logger('os_path: ' . $this->os_path, LOGGER_DATA); - - $r = q("SELECT data, flags, os_storage, filename, filetype FROM attach WHERE hash = '%s' AND uid = %d LIMIT 1", - dbesc($this->data['hash']), - intval($this->data['uid']) - ); - if ($r) { - // @todo this should be a global definition - $unsafe_types = array('text/html', 'text/css', 'application/javascript'); - - if (in_array($r[0]['filetype'], $unsafe_types)) { - header('Content-disposition: attachment; filename="' . $r[0]['filename'] . '"'); - header('Content-type: text/plain'); - } - - if (intval($r[0]['os_storage'])) { - $x = dbunescbin($r[0]['data']); - if(strpos($x,'store') === false) - $f = 'store/' . $this->auth->owner_nick . '/' . (($this->os_path) ? $this->os_path . '/' : '') . $x; - else - $f = $x; - return fopen($f, 'rb'); - } - return dbunescbin($r[0]['data']); - } - } - - /** - * @brief Returns the ETag for a file. - * - * An ETag is a unique identifier representing the current version of the file. - * If the file changes, the ETag MUST change. - * The ETag is an arbitrary string, but MUST be surrounded by double-quotes. - * - * Return null if the ETag can not effectively be determined. - * - * @return null|string - */ - public function getETag() { - $ret = null; - if ($this->data['hash']) { - $ret = '"' . $this->data['hash'] . '"'; - } - return $ret; - } - - /** - * @brief Returns the mime-type for a file. - * - * If null is returned, we'll assume application/octet-stream - * - * @return mixed - */ - public function getContentType() { - // @todo this should be a global definition. - $unsafe_types = array('text/html', 'text/css', 'application/javascript'); - if (in_array($this->data['filetype'], $unsafe_types)) { - return 'text/plain'; - } - return $this->data['filetype']; - } - - /** - * @brief Returns the size of the node, in bytes. - * - * @return int - * filesize in bytes - */ - public function getSize() { - return $this->data['filesize']; - } - - /** - * @brief Returns the last modification time for the file, as a unix - * timestamp. - * - * @return int last modification time in UNIX timestamp - */ - public function getLastModified() { - return datetime_convert('UTC', 'UTC', $this->data['edited'], 'U'); - } - - /** - * @brief Delete the file. - * - * This method checks the permissions and then calls attach_delete() function - * to actually remove the file. - * - * @throw \Sabre\DAV\Exception\Forbidden - */ - public function delete() { - logger('delete file ' . 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.'); - } - - if ($this->auth->owner_id !== $this->auth->channel_id) { - if (($this->auth->observer !== $this->data['creator']) || intval($this->data['is_dir'])) { - throw new DAV\Exception\Forbidden('Permission denied.'); - } - } - - attach_delete($this->auth->owner_id, $this->data['hash']); - } -} diff --git a/include/reddav.php b/include/reddav.php index 835521757..a0bd1b1fc 100644 --- a/include/reddav.php +++ b/include/reddav.php @@ -23,9 +23,9 @@ use Zotlabs\Storage; require_once('vendor/autoload.php'); require_once('include/attach.php'); -require_once('Zotlabs/Storage/File.php'); -require_once('Zotlabs/Storage/Directory.php'); -require_once('Zotlabs/Storage/BasicAuth.php'); +//require_once('Zotlabs/Storage/File.php'); +//require_once('Zotlabs/Storage/Directory.php'); +//require_once('Zotlabs/Storage/BasicAuth.php'); /** * @brief Returns an array with viewable channels. diff --git a/mod/cloud.php b/mod/cloud.php index 12524e71e..82d454477 100644 --- a/mod/cloud.php +++ b/mod/cloud.php @@ -86,7 +86,7 @@ function cloud_init(&$a) { } } - require_once('Zotlabs/Storage/Browser.php'); +// require_once('Zotlabs/Storage/Browser.php'); // provide a directory view for the cloud in Hubzilla $browser = new Zotlabs\Storage\Browser($auth); $auth->setBrowserPlugin($browser); diff --git a/mod/dav.php b/mod/dav.php index fb5054d0d..272dae4c8 100644 --- a/mod/dav.php +++ b/mod/dav.php @@ -126,7 +126,7 @@ function dav_init(&$a) { } } - require_once('Zotlabs/Storage/Browser.php'); +// require_once('Zotlabs/Storage/Browser.php'); // provide a directory view for the cloud in Hubzilla $browser = new Zotlabs\Storage\Browser($auth); $auth->setBrowserPlugin($browser); From 50c7c33ed199fad3b17b8beef544743e107e547f Mon Sep 17 00:00:00 2001 From: redmatrix Date: Fri, 19 Feb 2016 12:02:23 -0800 Subject: [PATCH 3/4] no linefeed at end --- mod/wfinger.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mod/wfinger.php b/mod/wfinger.php index 0c1c03f08..a745d9f84 100644 --- a/mod/wfinger.php +++ b/mod/wfinger.php @@ -124,4 +124,4 @@ function wfinger_init(&$a) { json_return_and_die($arr['result'],'application/jrd+json'); -} \ No newline at end of file +} From 8e586008dd8cceb02fc5378d11fce9673a0d5e41 Mon Sep 17 00:00:00 2001 From: redmatrix Date: Fri, 19 Feb 2016 13:18:51 -0800 Subject: [PATCH 4/4] issue #287 require old password to change password --- mod/settings.php | 68 ++++++++++++++++++++--------------- view/tpl/settings_account.tpl | 1 + 2 files changed, 40 insertions(+), 29 deletions(-) diff --git a/mod/settings.php b/mod/settings.php index 467cccfc7..2f90b037f 100644 --- a/mod/settings.php +++ b/mod/settings.php @@ -225,10 +225,44 @@ function settings_post(&$a) { $errs = array(); + $email = ((x($_POST,'email')) ? trim(notags($_POST['email'])) : ''); + $account = $a->get_account(); + if($email != $account['account_email']) { + if(! valid_email($email)) + $errs[] = t('Not valid email.'); + $adm = trim(get_config('system','admin_email')); + if(($adm) && (strcasecmp($email,$adm) == 0)) { + $errs[] = t('Protected email address. Cannot change to that email.'); + $email = $a->user['email']; + } + if(! $errs) { + $r = q("update account set account_email = '%s' where account_id = %d", + dbesc($email), + intval($account['account_id']) + ); + if(! $r) + $errs[] = t('System failure storing new email. Please try again.'); + } + } + + if($errs) { + foreach($errs as $err) + notice($err . EOL); + $errs = array(); + } + + if((x($_POST,'npassword')) || (x($_POST,'confirm'))) { - $newpass = $_POST['npassword']; - $confirm = $_POST['confirm']; + $origpass = trim($_POST['origpass']); + + require_once('include/auth.php'); + if(! account_verify_password($email,$origpass)) { + $errs[] = t('Password verification failed.'); + } + + $newpass = trim($_POST['npassword']); + $confirm = trim($_POST['confirm']); if($newpass != $confirm ) { $errs[] = t('Passwords do not match. Password unchanged.'); @@ -255,31 +289,6 @@ function settings_post(&$a) { } } - if($errs) { - foreach($errs as $err) - notice($err . EOL); - $errs = array(); - } - - $email = ((x($_POST,'email')) ? trim(notags($_POST['email'])) : ''); - $account = $a->get_account(); - if($email != $account['account_email']) { - if(! valid_email($email)) - $errs[] = t('Not valid email.'); - $adm = trim(get_config('system','admin_email')); - if(($adm) && (strcasecmp($email,$adm) == 0)) { - $errs[] = t('Protected email address. Cannot change to that email.'); - $email = $a->user['email']; - } - if(! $errs) { - $r = q("update account set account_email = '%s' where account_id = %d", - dbesc($email), - intval($account['account_id']) - ); - if(! $r) - $errs[] = t('System failure storing new email. Please try again.'); - } - } if($errs) { foreach($errs as $err) @@ -695,8 +704,9 @@ function settings_content(&$a) { $o .= replace_macros($tpl, array( '$form_security_token' => get_form_security_token("settings_account"), '$title' => t('Account Settings'), - '$password1'=> array('npassword', t('Enter New Password:'), '', ''), - '$password2'=> array('confirm', t('Confirm New Password:'), '', t('Leave password fields blank unless changing')), + '$origpass' => array('origpass', t('Current Password'), ' ',''), + '$password1'=> array('npassword', t('Enter New Password'), '', ''), + '$password2'=> array('confirm', t('Confirm New Password'), '', t('Leave password fields blank unless changing')), '$submit' => t('Submit'), '$email' => array('email', t('Email Address:'), $email, ''), '$removeme' => t('Remove Account'), diff --git a/view/tpl/settings_account.tpl b/view/tpl/settings_account.tpl index 9f550fe4a..4a93e0610 100755 --- a/view/tpl/settings_account.tpl +++ b/view/tpl/settings_account.tpl @@ -8,6 +8,7 @@
{{include file="field_input.tpl" field=$email}} + {{include file="field_password.tpl" field=$origpass}} {{include file="field_password.tpl" field=$password1}} {{include file="field_password.tpl" field=$password2}}