From: garvinhicking Date: Wed, 8 Feb 2006 16:48:28 +0000 (+0000) Subject: 1.1-alpha1: Commit directory image permission system. X-Git-Tag: 1.0~103 X-Git-Url: http://git.mjollnir.org/gw?a=commitdiff_plain;h=e41c88e7c5f71bd7a190ea84e28ffb1858ca8202;p=s9y.git 1.1-alpha1: Commit directory image permission system. Version bump. --- diff --git a/bundled-libs/HTTP/Request.php b/bundled-libs/HTTP/Request.php index 5b7ed0f..7db3634 100644 --- a/bundled-libs/HTTP/Request.php +++ b/bundled-libs/HTTP/Request.php @@ -32,7 +32,7 @@ // | Author: Richard Heyes | // +-----------------------------------------------------------------------+ // -// $Id: Request.php,v 1.41 2004/12/10 14:42:57 avb Exp $ +// $Id: Request.php,v 1.43 2005/11/06 18:29:14 avb Exp $ // // HTTP_Request Class // @@ -128,10 +128,22 @@ class HTTP_Request { /** * Post data - * @var mixed + * @var array */ var $_postData; + /** + * Request body + * @var string + */ + var $_body; + + /** + * A list of methods that MUST NOT have a request body, per RFC 2616 + * @var array + */ + var $_bodyDisallowed = array('TRACE'); + /** * Files to post * @var array @@ -229,7 +241,8 @@ class HTTP_Request { $this->_method = HTTP_REQUEST_METHOD_GET; $this->_http = HTTP_REQUEST_HTTP_VER_1_1; $this->_requestHeaders = array(); - $this->_postData = null; + $this->_postData = array(); + $this->_body = null; $this->_user = null; $this->_pass = null; @@ -262,7 +275,7 @@ class HTTP_Request { // Basic authentication if (!empty($this->_user)) { - $this->_requestHeaders['Authorization'] = 'Basic ' . base64_encode($this->_user . ':' . $this->_pass); + $this->addHeader('Authorization', 'Basic ' . base64_encode($this->_user . ':' . $this->_pass)); } // Use gzip encoding if possible @@ -398,7 +411,7 @@ class HTTP_Request { */ function addHeader($name, $value) { - $this->_requestHeaders[$name] = $value; + $this->_requestHeaders[strtolower($name)] = $value; } /** @@ -409,8 +422,8 @@ class HTTP_Request { */ function removeHeader($name) { - if (isset($this->_requestHeaders[$name])) { - unset($this->_requestHeaders[$name]); + if (isset($this->_requestHeaders[strtolower($name)])) { + unset($this->_requestHeaders[strtolower($name)]); } } @@ -509,15 +522,27 @@ class HTTP_Request { } /** - * Adds raw postdata + * Adds raw postdata (DEPRECATED) * * @param string The data * @param bool Whether data is preencoded or not, default = already encoded * @access public + * @deprecated deprecated since 1.3.0, method addBody() should be used instead */ function addRawPostData($postdata, $preencoded = true) { - $this->_postData = $preencoded ? $postdata : urlencode($postdata); + $this->_body = $preencoded ? $postdata : urlencode($postdata); + } + + /** + * Sets the request body (for POST, PUT and similar requests) + * + * @param string Request body + * @access public + */ + function setBody($body) + { + $this->_body = $body; } /** @@ -542,7 +567,7 @@ class HTTP_Request { */ function addCookie($name, $value) { - $cookies = isset($this->_requestHeaders['Cookie']) ? $this->_requestHeaders['Cookie']. '; ' : ''; + $cookies = isset($this->_requestHeaders['cookie']) ? $this->_requestHeaders['cookie']. '; ' : ''; $this->addHeader('Cookie', $cookies . $name . '=' . $value); } @@ -585,28 +610,35 @@ class HTTP_Request { $host = 'ssl://' . $host; } + // magic quotes may fuck up file uploads and chunked response processing + $magicQuotes = ini_get('magic_quotes_runtime'); + ini_set('magic_quotes_runtime', false); + // If this is a second request, we may get away without // re-connecting if they're on the same server - if (PEAR::isError($err = $this->_sock->connect($host, $port, null, $this->_timeout, $this->_socketOptions)) || - PEAR::isError($err = $this->_sock->write($this->_buildRequest()))) { + $err = $this->_sock->connect($host, $port, null, $this->_timeout, $this->_socketOptions); + PEAR::isError($err) or $err = $this->_sock->write($this->_buildRequest()); - return $err; - } - if (!empty($this->_readTimeout)) { - $this->_sock->setTimeout($this->_readTimeout[0], $this->_readTimeout[1]); + if (!PEAR::isError($err)) { + if (!empty($this->_readTimeout)) { + $this->_sock->setTimeout($this->_readTimeout[0], $this->_readTimeout[1]); + } + + $this->_notify('sentRequest'); + + // Read the response + $this->_response = &new HTTP_Response($this->_sock, $this->_listeners); + $err = $this->_response->process($this->_saveBody && $saveBody); } - $this->_notify('sentRequest'); + ini_set('magic_quotes_runtime', $magicQuotes); - // Read the response - $this->_response = &new HTTP_Response($this->_sock, $this->_listeners); - if (PEAR::isError($err = $this->_response->process($this->_saveBody && $saveBody)) ) { + if (PEAR::isError($err)) { return $err; } + // Check for redirection - // Bugfix (PEAR) bug #18, 6 oct 2003 by Dave Mertens (headers are also stored lowercase, so we're gonna use them here) - // some non RFC2616 compliant servers (scripts) are returning lowercase headers ('location: xxx') if ( $this->_allowRedirects AND $this->_redirects <= $this->_maxRedirects AND $this->getResponseCode() > 300 @@ -681,6 +713,7 @@ class HTTP_Request { if (!isset($headername)) { return isset($this->_response->_headers)? $this->_response->_headers: array(); } else { + $headername = strtolower($headername); return isset($this->_response->_headers[$headername]) ? $this->_response->_headers[$headername] : false; } } @@ -727,13 +760,16 @@ class HTTP_Request { $request = $this->_method . ' ' . $url . ' HTTP/' . $this->_http . "\r\n"; - if (HTTP_REQUEST_METHOD_POST != $this->_method && HTTP_REQUEST_METHOD_PUT != $this->_method) { + if (in_array($this->_method, $this->_bodyDisallowed) || + (HTTP_REQUEST_METHOD_POST != $this->_method && empty($this->_body)) || + (HTTP_REQUEST_METHOD_POST != $this->_method && empty($this->_postData) && empty($this->_postFiles))) { + $this->removeHeader('Content-Type'); } else { - if (empty($this->_requestHeaders['Content-Type'])) { + if (empty($this->_requestHeaders['content-type'])) { // Add default content-type $this->addHeader('Content-Type', 'application/x-www-form-urlencoded'); - } elseif ('multipart/form-data' == $this->_requestHeaders['Content-Type']) { + } elseif ('multipart/form-data' == $this->_requestHeaders['content-type']) { $boundary = 'HTTP_Request_' . md5(uniqid('request') . microtime()); $this->addHeader('Content-Type', 'multipart/form-data; boundary=' . $boundary); } @@ -742,17 +778,21 @@ class HTTP_Request { // Request Headers if (!empty($this->_requestHeaders)) { foreach ($this->_requestHeaders as $name => $value) { - $request .= $name . ': ' . $value . "\r\n"; + $canonicalName = implode('-', array_map('ucfirst', explode('-', $name))); + $request .= $canonicalName . ': ' . $value . "\r\n"; } } // No post data or wrong method, so simply add a final CRLF - if ((HTTP_REQUEST_METHOD_POST != $this->_method && HTTP_REQUEST_METHOD_PUT != $this->_method) || - (empty($this->_postData) && empty($this->_postFiles))) { + if (in_array($this->_method, $this->_bodyDisallowed) || + (HTTP_REQUEST_METHOD_POST != $this->_method && empty($this->_body))) { $request .= "\r\n"; + // Post data if it's an array - } elseif ((!empty($this->_postData) && is_array($this->_postData)) || !empty($this->_postFiles)) { + } elseif (HTTP_REQUEST_METHOD_POST == $this->_method && + (!empty($this->_postData) || !empty($this->_postFiles))) { + // "normal" POST request if (!isset($boundary)) { $postdata = implode('&', array_map( @@ -791,15 +831,16 @@ class HTTP_Request { $postdata .= "\r\n\r\n" . $data . "\r\n"; } } - $postdata .= '--' . $boundary . "\r\n"; + $postdata .= '--' . $boundary . "--\r\n"; } $request .= 'Content-Length: ' . strlen($postdata) . "\r\n\r\n"; $request .= $postdata; - // Post data if it's raw - } elseif(!empty($this->_postData)) { - $request .= 'Content-Length: ' . strlen($this->_postData) . "\r\n\r\n"; - $request .= $this->_postData; + // Explicitly set request body + } elseif (!empty($this->_body)) { + + $request .= 'Content-Length: ' . strlen($this->_body) . "\r\n\r\n"; + $request .= $this->_body; } return $request; @@ -995,18 +1036,22 @@ class HTTP_Response $chunked = isset($this->_headers['transfer-encoding']) && ('chunked' == $this->_headers['transfer-encoding']); $gzipped = isset($this->_headers['content-encoding']) && ('gzip' == $this->_headers['content-encoding']); $hasBody = false; - while (!$this->_sock->eof()) { - if ($chunked) { - $data = $this->_readChunked(); - } else { - $data = $this->_sock->read(4096); - } - if ('' != $data) { - $hasBody = true; - if ($saveBody || $gzipped) { - $this->_body .= $data; + if (!isset($this->_headers['content-length']) || 0 != $this->_headers['content-length']) { + while (!$this->_sock->eof()) { + if ($chunked) { + $data = $this->_readChunked(); + } else { + $data = $this->_sock->read(4096); + } + if ('' == $data) { + break; + } else { + $hasBody = true; + if ($saveBody || $gzipped) { + $this->_body .= $data; + } + $this->_notify($gzipped? 'gzTick': 'tick', $data); } - $this->_notify($gzipped? 'gzTick': 'tick', $data); } } if ($hasBody) { @@ -1031,12 +1076,15 @@ class HTTP_Response function _processHeader($header) { list($headername, $headervalue) = explode(':', $header, 2); - $headername_i = strtolower($headername); - $headervalue = ltrim($headervalue); + $headername = strtolower($headername); + $headervalue = ltrim($headervalue); - if ('set-cookie' != $headername_i) { - $this->_headers[$headername] = $headervalue; - $this->_headers[$headername_i] = $headervalue; + if ('set-cookie' != $headername) { + if (isset($this->_headers[$headername])) { + $this->_headers[$headername] .= ',' . $headervalue; + } else { + $this->_headers[$headername] = $headervalue; + } } else { $this->_parseCookie($headervalue); } @@ -1109,10 +1157,10 @@ class HTTP_Response $this->_chunkLength = hexdec($matches[1]); // Chunk with zero length indicates the end if (0 == $this->_chunkLength) { - $this->_sock->readAll(); // make this an eof() + $this->_sock->readLine(); // make this an eof() return ''; } - } elseif ($this->_sock->eof()) { + } else { return ''; } } diff --git a/docs/NEWS b/docs/NEWS index 332a7d7..6c6a7ed 100644 --- a/docs/NEWS +++ b/docs/NEWS @@ -1,5 +1,10 @@ # $Id$ +Version 1.1-alpha1() +------------------------------------------------------------------------ + + * Added first patches for image directory permissions. (garvinhicking) + Version 1.0-beta2 () ------------------------------------------------------------------------ diff --git a/include/admin/images.inc.php b/include/admin/images.inc.php index 4d57de9..c94d593 100644 --- a/include/admin/images.inc.php +++ b/include/admin/images.inc.php @@ -44,7 +44,7 @@ switch ($serendipity['GET']['adminAction']) { case 'delete': $file = serendipity_fetchImageFromDatabase($serendipity['GET']['fid']); - if (!serendipity_checkPermission('adminImagesDelete') || (!serendipity_checkPermission('adminImagesMaintainOthers') && $file['authorid'] != '0' && $file['authorid'] != $serendipity['authorid'])) { + if (!is_array($file) || !serendipity_checkPermission('adminImagesDelete') || (!serendipity_checkPermission('adminImagesMaintainOthers') && $file['authorid'] != '0' && $file['authorid'] != $serendipity['authorid'])) { return; } @@ -52,7 +52,7 @@ switch ($serendipity['GET']['adminAction']) { $serendipity['adminFile'] = 'serendipity_admin.php'; } $abortLoc = $serendipity['serendipityHTTPPath'] . $serendipity['adminFile'] . '?serendipity[adminModule]=images'; - $newLoc = $abortLoc . '&serendipity[adminAction]=DoDelete&serendipity[fid]=' . $serendipity['GET']['fid'] . '&' . serendipity_setFormToken('url'); + $newLoc = $abortLoc . '&serendipity[adminAction]=DoDelete&serendipity[fid]=' . (int)$serendipity['GET']['fid'] . '&' . serendipity_setFormToken('url'); printf(ABOUT_TO_DELETE_FILE, $file['name'] .'.'. $file['extension']); ?> @@ -70,12 +70,12 @@ switch ($serendipity['GET']['adminAction']) { $file = serendipity_fetchImageFromDatabase($serendipity['GET']['fid']); $serendipity['GET']['newname'] = serendipity_uploadSecure($serendipity['GET']['newname'], true); - if (!serendipity_checkFormToken() || !serendipity_checkPermission('adminImagesDelete') || (!serendipity_checkPermission('adminImagesMaintainOthers') && $file['authorid'] != '0' && $file['authorid'] != $serendipity['authorid'])) { + if (!is_array($file) || !serendipity_checkFormToken() || !serendipity_checkPermission('adminImagesDelete') || (!serendipity_checkPermission('adminImagesMaintainOthers') && $file['authorid'] != '0' && $file['authorid'] != $serendipity['authorid'])) { return; } if (serendipity_isActiveFile(basename($serendipity['GET']['newname']))) { - printf(ERROR_FILE_FORBIDDEN, $serendipity['GET']['newname']); + printf(ERROR_FILE_FORBIDDEN, htmlspecialchars($serendipity['GET']['newname'])); return; } @@ -302,6 +302,34 @@ switch ($serendipity['GET']['adminAction']) { break; + case 'directoryEdit': + if (!serendipity_checkPermission('adminImagesDirectories')) { + return; + } + +?> + +
+
+
+ + + + + + + +
+
+
+
+ +
+
+ +

-
+ - + @@ -326,8 +354,8 @@ switch ($serendipity['GET']['adminAction']) {

-
- +
+
@@ -357,6 +385,16 @@ switch ($serendipity['GET']['adminAction']) { if (!serendipity_checkPermission('adminImagesDirectories')) { return; } + + $folders = serendipity_traversePath( + $serendipity['serendipityPath'] . $serendipity['uploadPath'], + '', + true, + NULL, + 1, + NULL, + 'write' + ); ?>
@@ -373,7 +411,7 @@ switch ($serendipity['GET']['adminAction']) {
@@ -390,17 +428,28 @@ switch ($serendipity['GET']['adminAction']) { return; } + $folders = serendipity_traversePath( + $serendipity['serendipityPath'] . $serendipity['uploadPath'], + '', + true, + NULL, + 1, + NULL, + 'write' + ); + ?>

- + - + - + + @@ -417,6 +466,15 @@ switch ($serendipity['GET']['adminAction']) { } serendipity_restoreVar($serendipity['COOKIE']['addmedia_directory'], $serendipity['GET']['only_path']); + $folders = serendipity_traversePath( + $serendipity['serendipityPath'] . $serendipity['uploadPath'], + '', + true, + NULL, + 1, + NULL, + 'write' + ); ?> @@ -650,7 +708,7 @@ switch ($serendipity['GET']['adminAction']) {
<?php echo DELETE ?><?php echo EDIT ?><?php echo DELETE ?>
@@ -685,7 +743,7 @@ switch ($serendipity['GET']['adminAction']) { case 'rotateCW': $file = serendipity_fetchImageFromDatabase($serendipity['GET']['fid']); - if (!serendipity_checkPermission('adminImagesDelete') || (!serendipity_checkPermission('adminImagesMaintainOthers') && $file['authorid'] != '0' && $file['authorid'] != $serendipity['authorid'])) { + if (!is_array($file) || !serendipity_checkPermission('adminImagesDelete') || (!serendipity_checkPermission('adminImagesMaintainOthers') && $file['authorid'] != '0' && $file['authorid'] != $serendipity['authorid'])) { return; } @@ -701,7 +759,7 @@ switch ($serendipity['GET']['adminAction']) { case 'rotateCCW': $file = serendipity_fetchImageFromDatabase($serendipity['GET']['fid']); - if (!serendipity_checkPermission('adminImagesDelete') || (!serendipity_checkPermission('adminImagesMaintainOthers') && $file['authorid'] != '0' && $file['authorid'] != $serendipity['authorid'])) { + if (!is_array($file) || !serendipity_checkPermission('adminImagesDelete') || (!serendipity_checkPermission('adminImagesMaintainOthers') && $file['authorid'] != '0' && $file['authorid'] != $serendipity['authorid'])) { return; } @@ -718,7 +776,7 @@ switch ($serendipity['GET']['adminAction']) { case 'scale': $file = serendipity_fetchImageFromDatabase($serendipity['GET']['fid']); - if (!serendipity_checkFormToken() || !serendipity_checkPermission('adminImagesDelete') || (!serendipity_checkPermission('adminImagesMaintainOthers') && $file['authorid'] != '0' && $file['authorid'] != $serendipity['authorid'])) { + if (!is_array($file) || !serendipity_checkFormToken() || !serendipity_checkPermission('adminImagesDelete') || (!serendipity_checkPermission('adminImagesMaintainOthers') && $file['authorid'] != '0' && $file['authorid'] != $serendipity['authorid'])) { return; } @@ -726,8 +784,8 @@ switch ($serendipity['GET']['adminAction']) { SCALING_IMAGE . '
', $file['path'] . $file['name'] .'.'. $file['extension'], - $serendipity['GET']['width'], - $serendipity['GET']['height'] + (int)$serendipity['GET']['width'], + (int)$serendipity['GET']['height'] ); echo serendipity_scaleImg($serendipity['GET']['fid'], $serendipity['GET']['width'], $serendipity['GET']['height']) . '
'; @@ -744,7 +802,7 @@ switch ($serendipity['GET']['adminAction']) { case 'scaleSelect': $file = serendipity_fetchImageFromDatabase($serendipity['GET']['fid']); - if (!serendipity_checkPermission('adminImagesDelete') || (!serendipity_checkPermission('adminImagesMaintainOthers') && $file['authorid'] != '0' && $file['authorid'] != $serendipity['authorid'])) { + if (!is_array($file) || !serendipity_checkPermission('adminImagesDelete') || (!serendipity_checkPermission('adminImagesMaintainOthers') && $file['authorid'] != '0' && $file['authorid'] != $serendipity['authorid'])) { return; } @@ -774,7 +832,7 @@ switch ($serendipity['GET']['adminAction']) { diff --git a/include/functions_config.inc.php b/include/functions_config.inc.php index 1005f4d..ea01de6 100644 --- a/include/functions_config.inc.php +++ b/include/functions_config.inc.php @@ -1351,9 +1351,10 @@ function serendipity_addDefaultGroup($name, $level) { * @param string The type of an artifact (category|entry) * @param string The type of access to grant (read|write) * @param array The ID of the group to grant access to + * @param string A variable option for an artifact * @return boolean True if ACL was applied, false if not. */ -function serendipity_ACLGrant($artifact_id, $artifact_type, $artifact_mode, $groups) { +function serendipity_ACLGrant($artifact_id, $artifact_type, $artifact_mode, $groups, $artifact_index = '') { global $serendipity; if (empty($groups) || !is_array($groups)) { @@ -1362,15 +1363,16 @@ function serendipity_ACLGrant($artifact_id, $artifact_type, $artifact_mode, $gro // Delete all old existing relations. serendipity_db_query("DELETE FROM {$serendipity['dbPrefix']}access - WHERE artifact_id = " . (int)$artifact_id . " - AND artifact_type = '" . serendipity_db_escape_string($artifact_type) . "' - AND artifact_mode = '" . serendipity_db_escape_string($artifact_mode) . "'"); + WHERE artifact_id = " . (int)$artifact_id . " + AND artifact_type = '" . serendipity_db_escape_string($artifact_type) . "' + AND artifact_mode = '" . serendipity_db_escape_string($artifact_mode) . "' + AND artifact_index = '" . serendipity_db_escape_string($artifact_index) . "'"); $data = array( 'artifact_id' => (int)$artifact_id, 'artifact_type' => $artifact_type, 'artifact_mode' => $artifact_mode, - 'artifact_index' => '' + 'artifact_index' => $artifact_index ); if (count($data) < 1) { @@ -1398,15 +1400,17 @@ function serendipity_ACLGrant($artifact_id, $artifact_type, $artifact_mode, $gro * @param int The ID of the artifact to set the access * @param string The type of an artifact (category|entry) * @param string The type of access to check for (read|write) + * @param string A variable option for an artifact * @return array Returns an array of all groups that are allowed for this kind of access. You can then check if you are the member of any of the groups returned here. */ -function serendipity_ACLGet($artifact_id, $artifact_type, $artifact_mode) { +function serendipity_ACLGet($artifact_id, $artifact_type, $artifact_mode, $artifact_index) { global $serendipity; $sql = "SELECT groupid, artifact_index FROM {$serendipity['dbPrefix']}access - WHERE artifact_type = '" . serendipity_db_escape_string($artifact_type) . "' - AND artifact_id = '" . (int)$artifact_id . "' - AND artifact_mode = '" . serendipity_db_escape_string($artifact_mode) . "'"; + WHERE artifact_type = '" . serendipity_db_escape_string($artifact_type) . "' + AND artifact_id = '" . (int)$artifact_id . "' + AND artifact_mode = '" . serendipity_db_escape_string($artifact_mode) . "' + AND artifact_index = '" . serendipity_db_escape_string($artifact_index) . "'"; $rows = serendipity_db_query($sql, false, 'assoc'); if (!is_array($rows)) { @@ -1491,9 +1495,10 @@ function serendipity_ACLCheck($authorid, $artifact_id, $artifact_type, $artifact * @access private * @param array Associative array that holds the SQL part array to be used in other functions like serendipity_fetchEntries() * @param boolean Some queries do not need to joins categories. When ACLs need to be applied, this column is required, so if $append_category is set to true it will perform this missing JOIN. + * @param string The ACL type ('category', 'directory') * @return true True if ACLs were applied, false if not. */ -function serendipity_ACL_SQL(&$cond, $append_category = false) { +function serendipity_ACL_SQL(&$cond, $append_category = false, $type = 'category') { global $serendipity; // A global configuration item controls whether the blog should apply ACLs or not! @@ -1518,13 +1523,26 @@ function serendipity_ACL_SQL(&$cond, $append_category = false) { $cond['joins'] .= " LEFT JOIN {$serendipity['dbPrefix']}category c ON ec.categoryid = c.categoryid"; } + + switch($type) { + case 'directory': + $sql_artifact_column = 'i.path IS NULL OR + acl_acc.groupid IS NULL'; + $sql_artifact = 'AND acl_acc.artifact_index = i.path'; + break; + + case 'category': + $sql_artifact_column = 'c.categoryid IS NULL'; + $sql_artifact = 'AND acl_acc.artifact_id = c.categoryid'; + break; + } $cond['joins'] .= " LEFT JOIN {$serendipity['dbPrefix']}authorgroups AS acl_a ON acl_a.authorid = " . $read_id . " LEFT JOIN {$serendipity['dbPrefix']}access AS acl_acc ON ( acl_acc.artifact_mode = 'read' - AND acl_acc.artifact_type = 'category' - AND acl_acc.artifact_id = c.categoryid + AND acl_acc.artifact_type = '" . $type . "' + " . $sql_artifact . " )"; if (empty($cond['and'])) { @@ -1535,7 +1553,7 @@ function serendipity_ACL_SQL(&$cond, $append_category = false) { // When in Admin-Mode, apply readership permissions. $cond['and'] .= " ( - c.categoryid IS NULL + " . $sql_artifact_column . " OR ( acl_acc.groupid = " . $read_id_sql . ") OR ( acl_acc.artifact_id IS NULL " . (isset($serendipity['GET']['adminModule']) && diff --git a/include/functions_images.inc.php b/include/functions_images.inc.php index 953420a..6861a4e 100644 --- a/include/functions_images.inc.php +++ b/include/functions_images.inc.php @@ -71,13 +71,30 @@ function serendipity_fetchImagesFromDatabase($start=0, $limit=0, &$total, $order $permsql = " WHERE $perm"; } - $query = "SELECT i.*, a.realname AS authorname FROM {$serendipity['dbPrefix']}images AS i LEFT OUTER JOIN {$serendipity['dbPrefix']}authors AS a ON i.authorid = a.authorid $directorysql ORDER BY $order $ordermode $limitsql"; + $cond = array( + 'and' => $directorysql + ); + serendipity_ACL_SQL($cond, false, 'directory'); + + $basequery = "FROM {$serendipity['dbPrefix']}images AS i + LEFT OUTER JOIN {$serendipity['dbPrefix']}authors AS a + ON i.authorid = a.authorid + {$cond['joins']} + + {$cond['and']}"; + + $query = "SELECT i.*, + a.realname AS authorname + $basequery + ORDER BY $order $ordermode $limitsql"; + $rs = serendipity_db_query($query, false, 'assoc'); if (!is_array($rs)) { return array(); } - $total_query = "SELECT count(i.id) FROM {$serendipity['dbPrefix']}images AS i LEFT OUTER JOIN {$serendipity['dbPrefix']}authors AS a on i.authorid = a.authorid $permsql"; + $total_query = "SELECT count(i.id) + $basequery"; $total_rs = serendipity_db_query($total_query, true, 'num'); if (is_array($total_rs)) { $total = $total_rs[0]; @@ -95,7 +112,16 @@ function serendipity_fetchImagesFromDatabase($start=0, $limit=0, &$total, $order */ function serendipity_fetchImageFromDatabase($id) { global $serendipity; - $rs = serendipity_db_query("SELECT * FROM {$serendipity['dbPrefix']}images WHERE id = ". (int)$id, true, 'assoc'); + + $cond = array( + 'and' => "WHERE id = " . (int)$id + ); + serendipity_ACL_SQL($cond, false, 'directory'); + + $rs = serendipity_db_query("SELECT i.* + FROM {$serendipity['dbPrefix']}images AS i + {$cond['joins']} + {$cond['and']}", true, 'assoc'); return $rs; } @@ -138,6 +164,12 @@ function serendipity_deleteImage($id) { $dThumb = array(); $file = serendipity_fetchImageFromDatabase($id); + + if (!is_array($file) || !isset($file['path'])) { + printf(FILE_NOT_FOUND . '
', $id); + return false; + } + $dFile = $file['path'] . $file['name'] . '.' . $file['extension']; $dThumb = array(array( @@ -459,6 +491,9 @@ function serendipity_scaleImg($id, $width, $height) { global $serendipity; $file = serendipity_fetchImageFromDatabase($id); + if (!is_array($file)) { + return false; + } $admin = ''; if (!serendipity_checkPermission('adminImagesMaintainOthers') && $file['authorid'] != '0' && $file['authorid'] != $serendipity['authorid']) { @@ -496,6 +531,9 @@ function serendipity_rotateImg($id, $degrees) { global $serendipity; $file = serendipity_fetchImageFromDatabase($id); + if (!is_array($file)) { + return false; + } $admin = ''; if (!serendipity_checkPermission('adminImagesMaintainOthers') && $file['authorid'] != '0' && $file['authorid'] != $serendipity['authorid']) { @@ -1107,7 +1145,15 @@ function serendipity_displayImageList($page = 0, $lineBreak = NULL, $manage = fa $linkPrevious = '?' . $extraParems . 'serendipity[page]=' . ($page-1); $linkNext = '?' . $extraParems . 'serendipity[page]=' . ($page+1); $sort_order = serendipity_getImageFields(); - $paths = serendipity_traversePath($serendipity['serendipityPath'] . $serendipity['uploadPath']. $limit_path); + $paths = serendipity_traversePath( + $serendipity['serendipityPath'] . $serendipity['uploadPath']. $limit_path, + '', + true, + NULL, + 1, + NULL, + 'read' + ); if (is_null($lineBreak)) { $lineBreak = floor(750 / ($serendipity['thumbSize'] + 20)); @@ -1407,6 +1453,9 @@ function serendipity_killPath($basedir, $directory = '', $forceDelete = false) { /** * Recursively walk a directory tree * + * + * @TODO - Check all serendipity_traversePath calls to see where ACL need to be applied + * * @access public * @param string The core directory * @param string The subdirectory @@ -1414,35 +1463,43 @@ function serendipity_killPath($basedir, $directory = '', $forceDelete = false) { * @param string A regexp patter to include files * @param int Level of nesting (recursive use) * @param int The maximum level of nesting (recursive use) + * @param mixed Toggle whether to apply serendipity_directoryACL (false / 'read' / 'write') * @return array Array of files/directories */ -function serendipity_traversePath($basedir, $dir='', $onlyDirs=true, $pattern = NULL, $depth = 1, $max_depth = null) { - +function serendipity_traversePath($basedir, $dir='', $onlyDirs = true, $pattern = NULL, $depth = 1, $max_depth = null, $apply_ACL = false) { - $dh = @opendir($basedir . '/' . $dir); - if ( !$dh ) { + $odir = serendipity_dirSlash('end', $basedir) . serendipity_dirSlash('end', $dir); + $dh = @opendir($odir); + if (!$dh) { return array(); } $files = array(); while (($file = @readdir($dh)) !== false) { if ( $file != '.' && $file != '..' ) { - if ( $onlyDirs === false || ($onlyDirs === true && is_dir($basedir . '/' . $dir . '/' . $file)) ) { + if ( $onlyDirs === false || ($onlyDirs === true && is_dir($odir . $file)) ) { if ( is_null($pattern) || preg_match($pattern, $file) ) { $files[] = array( 'name' => $file, 'depth' => $depth, - 'relpath' => ltrim(str_replace('\\', '/', $dir) . basename($file) . '/', '/') + 'relpath' => ltrim(str_replace('\\', '/', serendipity_dirSlash('end', $dir)) . basename($file) . '/', '/') ); } } - if ( is_dir($basedir . '/' . $dir . '/' . $file) && ($max_depth === null || $depth < $max_depth)) { - $files = array_merge($files, serendipity_traversePath($basedir, $dir . '/' . basename($file) . '/', $onlyDirs, $pattern, ($depth+1), $max_depth)); + + if (is_dir($odir . $file) && ($max_depth === null || $depth < $max_depth)) { + $next_dir = serendipity_dirSlash('end', $dir) . basename($file); + $files = array_merge($files, serendipity_traversePath($basedir, $next_dir, $onlyDirs, $pattern, ($depth+1), $max_depth)); } } } @closedir($dh); + + if ($depth == 1 && $apply_ACL !== FALSE) { + serendipity_directoryACL($files, $apply_ACL); + } + return $files; } @@ -1569,3 +1626,120 @@ function serendipity_getImageFields() { function serendipity_escapeshellarg($string) { return escapeshellarg(str_replace('%', '', $string)); } + +/** + * Rename a media directory + * + * @access public + * @param string Old directory name + * @param string New directory name + */ +function serendipity_renameDir($old, $new) { +} + +/** + * Makes sure a directory begins with or ends with a "/" + * + * @access public + * @param string Type of where to append/prepend slash ('end', 'start', 'both') + * @param string Directory name + * @return string Output argument + */ +function serendipity_dirSlash($type, $dir) { + + if ($dir == '') { + return $dir; + } + + if ($type == 'start' || $type == 'both') { + if (substr($dir, 0, 1) != '/') { + $dir = '/' . $dir; + } + } + + if ($type == 'end' || $type == 'both') { + if (substr($dir, -1) != '/') { + $dir .= '/'; + } + } + + return $dir; +} + +/** + * Cycle a serendipity_traversePath resultset and apply read/write ACLs. + * + * @access public + * @param array serendipity_traversePath result array + * @param string ACL type ('read', 'write') + */ +function serendipity_directoryACL(&$paths, $type = 'read') { + global $serendipity; + static $debug = false; + + if ($debug) { + echo "Applying ACL for mode '$type'.
\n"; + } + + if (serendipity_userLoggedIn() && (!isset($serendipity['enableACL']) || $serendipity['enableACL'] == true)) { + // Check if we are a cool superuser. Bail out if we are. + if (serendipity_checkPermission('adminImagesMaintainOthers') && serendipity_checkPermission('adminImagesDirectories')) { + if (!$debug) { + return true; + } + } + + // Get list of all ACLs for directories. + $q = "SELECT a.artifact_index AS directory, + a.groupid + FROM {$serendipity['dbPrefix']}access AS a + WHERE a.artifact_type = 'directory' + AND a.artifact_mode = '" . serendipity_db_escape_string($type) . "'"; + $allowed = serendipity_db_query($q); + if (!is_array($allowed)) { + return false; + } + + // Get a list of all the groups for this user. Pipe it into a usable array. + $my_groups =& serendipity_getGroups($serendipity['authorid']); + $acl_allowed_groups = array(); + foreach($my_groups AS $my_group) { + $acl_allowed_groups[$my_group['id']] = true; + } + + // Iterate every ACL and check if we are allowed to use it. + $acl_allowed = array(); + foreach($allowed AS $row) { + $acl_allowed[$row['directory']][$row['groupid']] = true; + } + + // Iterate the input path array and check it against ACL. + foreach($paths AS $idx => $info) { + if (!isset($acl_allowed[$info['relpath']])) { + // ACL for directory not set. Assume we are allowed to access. + continue; + } + + $granted = false; + foreach($acl_allowed[$info['relpath']] AS $groupid => $set) { + if (isset($acl_allowed_groups[$groupid])) { + // We are allowed to access this element + $granted = true; + break; + } + } + + if ($granted === false) { + // We are not allowed to access this element + if ($debug) { + echo "ACL for " . $info['relpath'] . " DENIED.
\n"; + } + unset($paths[$idx]); + } else { + if ($debug) { + echo "ACL for " . $info['relpath'] . " granted.
\n"; + } + } + } + } +} \ No newline at end of file diff --git a/serendipity_admin_image_selector.php b/serendipity_admin_image_selector.php index 386b40d..9708d74 100644 --- a/serendipity_admin_image_selector.php +++ b/serendipity_admin_image_selector.php @@ -111,6 +111,10 @@ switch ($serendipity['GET']['step']) { } $file = serendipity_fetchImageFromDatabase($serendipity['GET']['image']); + if (!is_array($file)) { + echo PERM_DENIED; + break; + } $file['imgsrc'] = $serendipity['serendipityHTTPPath'] . $serendipity['uploadHTTPPath'] . $file['path'] . $file['name'] . (!empty($file['thumbnail_name']) ? '.' . $file['thumbnail_name'] : '') . '.' . $file['extension']; if ($file['hotlink']) { $imgName = $file['path']; diff --git a/serendipity_config.inc.php b/serendipity_config.inc.php index 90b2dac..307f475 100644 --- a/serendipity_config.inc.php +++ b/serendipity_config.inc.php @@ -21,7 +21,7 @@ if (IS_installed === true && !defined('IN_serendipity')) { include_once(S9Y_INCLUDE_PATH . 'include/compat.inc.php'); // The version string -$serendipity['version'] = '1.0-beta2'; +$serendipity['version'] = '1.1-alpha1'; // Name of folder for the default theme $serendipity['defaultTemplate'] = 'default';