Merge remote branch 'upstream/master'

This commit is contained in:
habeascodice 2014-10-15 17:26:07 -07:00
commit cf58122b29
26 changed files with 490 additions and 280 deletions

View File

@ -48,7 +48,7 @@ define ( 'RED_PLATFORM', 'redmatrix' );
define ( 'RED_VERSION', trim(file_get_contents('version.inc')) . 'R'); define ( 'RED_VERSION', trim(file_get_contents('version.inc')) . 'R');
define ( 'ZOT_REVISION', 1 ); define ( 'ZOT_REVISION', 1 );
define ( 'DB_UPDATE_VERSION', 1129 ); define ( 'DB_UPDATE_VERSION', 1130 );
define ( 'EOL', '<br />' . "\r\n" ); define ( 'EOL', '<br />' . "\r\n" );
define ( 'ATOM_TIME', 'Y-m-d\TH:i:s\Z' ); define ( 'ATOM_TIME', 'Y-m-d\TH:i:s\Z' );

View File

@ -98,3 +98,6 @@ This document assumes you're an administrator.
Logfile to use for logging auth errors. Used to plug in to server Logfile to use for logging auth errors. Used to plug in to server
side software such as fail2ban. Auth failures are still logged to side software such as fail2ban. Auth failures are still logged to
the main logs as well. the main logs as well.
[b]system > hide_in_statistics[/b]
Tell the red statistics servers to completely hide this hub in hub lists.

View File

@ -55,7 +55,7 @@ server {
ssl_certificate /etc/nginx/ssl/red.example.net.chain.pem; ssl_certificate /etc/nginx/ssl/red.example.net.chain.pem;
ssl_certificate_key /etc/nginx/ssl/example.net.key; ssl_certificate_key /etc/nginx/ssl/example.net.key;
ssl_session_timeout 5m; ssl_session_timeout 5m;
ssl_protocols SSLv3 TLSv1; ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv3:+EXP; ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv3:+EXP;
ssl_prefer_server_ciphers on; ssl_prefer_server_ciphers on;

View File

@ -46,7 +46,7 @@ class RedBasicAuth extends DAV\Auth\Backend\AbstractBasic {
/** /**
* *
* @see RedBrowser::set_writeable() * @see RedBrowser::set_writeable()
* @var DAV\Browser\Plugin * @var \Sabre\DAV\Browser\Plugin
*/ */
public $browser; public $browser;
/** /**
@ -85,7 +85,7 @@ class RedBasicAuth extends DAV\Auth\Backend\AbstractBasic {
*/ */
protected function validateUserPass($username, $password) { protected function validateUserPass($username, $password) {
if (trim($password) === '+++') { if (trim($password) === '+++') {
logger('(DAV): RedBasicAuth::validateUserPass(): guest ' . $username); logger('guest: ' . $username);
return true; return true;
} }
@ -112,13 +112,14 @@ class RedBasicAuth extends DAV\Auth\Backend\AbstractBasic {
foreach ($x as $record) { foreach ($x as $record) {
if (($record['account_flags'] == ACCOUNT_OK) || ($record['account_flags'] == ACCOUNT_UNVERIFIED) if (($record['account_flags'] == ACCOUNT_OK) || ($record['account_flags'] == ACCOUNT_UNVERIFIED)
&& (hash('whirlpool', $record['account_salt'] . $password) === $record['account_password'])) { && (hash('whirlpool', $record['account_salt'] . $password) === $record['account_password'])) {
logger('(DAV) RedBasicAuth: password verified for ' . $username); logger('password verified for ' . $username);
return $this->setAuthenticated($r[0]); return $this->setAuthenticated($r[0]);
} }
} }
} }
} }
logger('(DAV) RedBasicAuth: password failed for ' . $username); logger('password failed for ' . $username);
// @TODO add security logger
return false; return false;
} }
@ -186,23 +187,23 @@ class RedBasicAuth extends DAV\Auth\Backend\AbstractBasic {
* @brief Set browser plugin for SabreDAV. * @brief Set browser plugin for SabreDAV.
* *
* @see RedBrowser::set_writeable() * @see RedBrowser::set_writeable()
* @param DAV\Browser\Plugin $browser * @param \Sabre\DAV\Browser\Plugin $browser
*/ */
public function setBrowserPlugin($browser) { public function setBrowserPlugin($browser) {
$this->browser = $browser; $this->browser = $browser;
} }
/** /**
* Prints out all RedBasicAuth variables to logger(). * @brief Prints out all RedBasicAuth variables to logger().
* *
* @return void * @return void
*/ */
public function log() { public function log() {
logger('dav: auth: channel_name ' . $this->channel_name, LOGGER_DATA); logger('channel_name ' . $this->channel_name, LOGGER_DATA);
logger('dav: auth: channel_id ' . $this->channel_id, LOGGER_DATA); logger('channel_id ' . $this->channel_id, LOGGER_DATA);
logger('dav: auth: channel_hash ' . $this->channel_hash, LOGGER_DATA); logger('channel_hash ' . $this->channel_hash, LOGGER_DATA);
logger('dav: auth: observer ' . $this->observer, LOGGER_DATA); logger('observer ' . $this->observer, LOGGER_DATA);
logger('dav: auth: owner_id ' . $this->owner_id, LOGGER_DATA); logger('owner_id ' . $this->owner_id, LOGGER_DATA);
logger('dav: auth: owner_nick ' . $this->owner_nick, LOGGER_DATA); logger('owner_nick ' . $this->owner_nick, LOGGER_DATA);
} }
} }

View File

@ -262,6 +262,14 @@ class RedBrowser extends DAV\Browser\Plugin {
construct_page(get_app()); construct_page(get_app());
} }
/**
* @brief Returns a human readable formatted string for filesizes.
*
* Don't we need such a functionality in other places, too?
*
* @param int $size filesize in bytes
* @return string
*/
function userReadableSize($size) { function userReadableSize($size) {
$ret = ""; $ret = "";
if (is_numeric($size)) { if (is_numeric($size)) {

View File

@ -49,7 +49,7 @@ class RedDirectory extends DAV\Node implements DAV\ICollection, DAV\IQuota {
* @param RedBasicAuth &$auth_plugin * @param RedBasicAuth &$auth_plugin
*/ */
public function __construct($ext_path, &$auth_plugin) { public function __construct($ext_path, &$auth_plugin) {
logger('RedDirectory::__construct() ' . $ext_path, LOGGER_DATA); //logger('directory ' . $ext_path, LOGGER_DATA);
$this->ext_path = $ext_path; $this->ext_path = $ext_path;
// remove "/cloud" from the beginning of the path // remove "/cloud" from the beginning of the path
$this->red_path = ((strpos($ext_path, '/cloud') === 0) ? substr($ext_path, 6) : $ext_path); $this->red_path = ((strpos($ext_path, '/cloud') === 0) ? substr($ext_path, 6) : $ext_path);
@ -66,19 +66,19 @@ class RedDirectory extends DAV\Node implements DAV\ICollection, DAV\IQuota {
} }
private function log() { private function log() {
logger('RedDirectory::log() ext_path ' . $this->ext_path, LOGGER_DATA); logger('ext_path ' . $this->ext_path, LOGGER_DATA);
logger('RedDirectory::log() os_path ' . $this->os_path, LOGGER_DATA); logger('os_path ' . $this->os_path, LOGGER_DATA);
logger('RedDirectory::log() red_path ' . $this->red_path, LOGGER_DATA); logger('red_path ' . $this->red_path, LOGGER_DATA);
} }
/** /**
* @brief Returns an array with all the child nodes. * @brief Returns an array with all the child nodes.
* *
* @throws DAV\Exception\Forbidden * @throw \Sabre\DAV\Exception\Forbidden
* @return array DAV\INode[] * @return array \Sabre\DAV\INode[]
*/ */
public function getChildren() { public function getChildren() {
logger('RedDirectory::getChildren() called for ' . $this->ext_path, LOGGER_DATA); //logger('children for ' . $this->ext_path, LOGGER_DATA);
$this->log(); $this->log();
if (get_config('system', 'block_public') && (! $this->auth->channel_id) && (! $this->auth->observer)) { if (get_config('system', 'block_public') && (! $this->auth->channel_id) && (! $this->auth->observer)) {
@ -97,12 +97,12 @@ class RedDirectory extends DAV\Node implements DAV\ICollection, DAV\IQuota {
* @brief Returns a child by name. * @brief Returns a child by name.
* *
* *
* @throw DAV\Exception\Forbidden * @throw \Sabre\DAV\Exception\Forbidden
* @throw DAV\Exception\NotFound * @throw \Sabre\DAV\Exception\NotFound
* @param string $name * @param string $name
*/ */
public function getChild($name) { public function getChild($name) {
logger('RedDirectory::getChild(): ' . $name, LOGGER_DATA); logger($name, LOGGER_DATA);
if (get_config('system', 'block_public') && (! $this->auth->channel_id) && (! $this->auth->observer)) { if (get_config('system', 'block_public') && (! $this->auth->channel_id) && (! $this->auth->observer)) {
throw new DAV\Exception\Forbidden('Permission denied.'); throw new DAV\Exception\Forbidden('Permission denied.');
@ -130,7 +130,7 @@ class RedDirectory extends DAV\Node implements DAV\ICollection, DAV\IQuota {
* @return string * @return string
*/ */
public function getName() { public function getName() {
logger('RedDirectory::getName() returns: ' . basename($this->red_path), LOGGER_DATA); //logger(basename($this->red_path), LOGGER_DATA);
return (basename($this->red_path)); return (basename($this->red_path));
} }
@ -139,20 +139,20 @@ class RedDirectory extends DAV\Node implements DAV\ICollection, DAV\IQuota {
* *
* @todo handle duplicate directory name * @todo handle duplicate directory name
* *
* @throw DAV\Exception\Forbidden * @throw \Sabre\DAV\Exception\Forbidden
* @param string $name The new name of the directory. * @param string $name The new name of the directory.
* @return void * @return void
*/ */
public function setName($name) { public function setName($name) {
logger('RedDirectory::setName(): ' . basename($this->red_path) . ' -> ' . $name, LOGGER_DATA); logger('old name ' . basename($this->red_path) . ' -> ' . $name, LOGGER_DATA);
if ((! $name) || (! $this->auth->owner_id)) { if ((! $name) || (! $this->auth->owner_id)) {
logger('RedDirectory::setName(): permission denied'); logger('permission denied ' . $name);
throw new DAV\Exception\Forbidden('Permission denied.'); throw new DAV\Exception\Forbidden('Permission denied.');
} }
if (! perm_is_allowed($this->auth->owner_id, $this->auth->observer, 'write_storage')) { if (! perm_is_allowed($this->auth->owner_id, $this->auth->observer, 'write_storage')) {
logger('RedDirectory::setName(): permission denied'); logger('permission denied '. $name);
throw new DAV\Exception\Forbidden('Permission denied.'); throw new DAV\Exception\Forbidden('Permission denied.');
} }
@ -177,21 +177,21 @@ class RedDirectory extends DAV\Node implements DAV\ICollection, DAV\IQuota {
* After successful creation of the file, you may choose to return the ETag * After successful creation of the file, you may choose to return the ETag
* of the new file here. * of the new file here.
* *
* @throws DAV\Exception\Forbidden * @throw \Sabre\DAV\Exception\Forbidden
* @param string $name Name of the file * @param string $name Name of the file
* @param resource|string $data Initial payload * @param resource|string $data Initial payload
* @return null|string ETag * @return null|string ETag
*/ */
public function createFile($name, $data = null) { public function createFile($name, $data = null) {
logger('RedDirectory::createFile(): ' . $name, LOGGER_DATA); logger($name, LOGGER_DEBUG);
if (! $this->auth->owner_id) { if (! $this->auth->owner_id) {
logger('RedDirectory::createFile(): permission denied'); logger('permission denied ' . $name);
throw new DAV\Exception\Forbidden('Permission denied.'); throw new DAV\Exception\Forbidden('Permission denied.');
} }
if (! perm_is_allowed($this->auth->owner_id, $this->auth->observer, 'write_storage')) { if (! perm_is_allowed($this->auth->owner_id, $this->auth->observer, 'write_storage')) {
logger('RedDirectory::createFile(): permission denied'); logger('permission denied ' . $name);
throw new DAV\Exception\Forbidden('Permission denied.'); throw new DAV\Exception\Forbidden('Permission denied.');
} }
@ -203,7 +203,7 @@ class RedDirectory extends DAV\Node implements DAV\ICollection, DAV\IQuota {
); );
if (! $c) { if (! $c) {
logger('RedDirectory::createFile(): no channel'); logger('no channel');
throw new DAV\Exception\Forbidden('Permission denied.'); throw new DAV\Exception\Forbidden('Permission denied.');
} }
@ -237,7 +237,7 @@ class RedDirectory extends DAV\Node implements DAV\ICollection, DAV\IQuota {
$size = file_put_contents($f, $data); $size = file_put_contents($f, $data);
// delete attach entry if file_put_contents() failed // delete attach entry if file_put_contents() failed
if ($size === false) { if ($size === false) {
logger('RedDirectory::createFile(): file_put_contents() failed for ' . $name, LOGGER_DEBUG); logger('file_put_contents() failed to ' . $f);
attach_delete($c[0]['channel_id'], $hash); attach_delete($c[0]['channel_id'], $hash);
return; return;
} }
@ -273,7 +273,7 @@ class RedDirectory extends DAV\Node implements DAV\ICollection, DAV\IQuota {
intval($c[0]['channel_account_id']) intval($c[0]['channel_account_id'])
); );
if (($x) && ($x[0]['total'] + $size > $limit)) { if (($x) && ($x[0]['total'] + $size > $limit)) {
logger('reddav: service class limit exceeded for ' . $c[0]['channel_name'] . ' total usage is ' . $x[0]['total'] . ' limit is ' . $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); attach_delete($c[0]['channel_id'], $hash);
return; return;
} }
@ -287,7 +287,7 @@ class RedDirectory extends DAV\Node implements DAV\ICollection, DAV\IQuota {
* @return void * @return void
*/ */
public function createDirectory($name) { public function createDirectory($name) {
logger('RedDirectory::createDirectory(): ' . $name, LOGGER_DEBUG); logger($name, LOGGER_DEBUG);
if ((! $this->auth->owner_id) || (! perm_is_allowed($this->auth->owner_id, $this->auth->observer, 'write_storage'))) { if ((! $this->auth->owner_id) || (! perm_is_allowed($this->auth->owner_id, $this->auth->observer, 'write_storage'))) {
throw new DAV\Exception\Forbidden('Permission denied.'); throw new DAV\Exception\Forbidden('Permission denied.');
@ -301,7 +301,7 @@ class RedDirectory extends DAV\Node implements DAV\ICollection, DAV\IQuota {
if ($r) { if ($r) {
$result = attach_mkdir($r[0], $this->auth->observer, array('filename' => $name, 'folder' => $this->folder_hash)); $result = attach_mkdir($r[0], $this->auth->observer, array('filename' => $name, 'folder' => $this->folder_hash));
if (! $result['success']) { if (! $result['success']) {
logger('RedDirectory::createDirectory(): ' . print_r($result, true), LOGGER_DEBUG); logger('error ' . print_r($result, true), LOGGER_DEBUG);
} }
} }
} }
@ -310,31 +310,33 @@ class RedDirectory extends DAV\Node implements DAV\ICollection, DAV\IQuota {
* @brief Checks if a child exists. * @brief Checks if a child exists.
* *
* @param string $name * @param string $name
* The name to check if it exists.
* @return boolean * @return boolean
*/ */
public function childExists($name) { public function childExists($name) {
// On /cloud we show a list of available channels. // On /cloud we show a list of available channels.
// @todo what happens if no channels are available? // @todo what happens if no channels are available?
if ($this->red_path === '/' && $name === 'cloud') { if ($this->red_path === '/' && $name === 'cloud') {
logger('RedDirectory::childExists() /cloud: true', LOGGER_DATA); //logger('We are at /cloud show a channel list', LOGGER_DEBUG);
return true; return true;
} }
$x = RedFileData($this->ext_path . '/' . $name, $this->auth, true); $x = RedFileData($this->ext_path . '/' . $name, $this->auth, true);
logger('RedFileData returns: ' . print_r($x, true), LOGGER_DATA); //logger('RedFileData returns: ' . print_r($x, true), LOGGER_DATA);
if ($x) if ($x)
return true; return true;
return false; return false;
} }
/** /**
* @todo add description of what this function does. * @todo add description of what this function does.
* *
* @throw DAV\Exception\NotFound * @throw \Sabre\DAV\Exception\NotFound
* @return void * @return void
*/ */
function getDir() { function getDir() {
logger('RedDirectory::getDir(): ' . $this->ext_path, LOGGER_DEBUG); //logger($this->ext_path, LOGGER_DEBUG);
$this->auth->log(); $this->auth->log();
$file = $this->ext_path; $file = $this->ext_path;
@ -356,7 +358,7 @@ class RedDirectory extends DAV\Node implements DAV\ICollection, DAV\IQuota {
if (! $path_arr) if (! $path_arr)
return; return;
logger('RedDirectory::getDir(): path: ' . print_r($path_arr, true), LOGGER_DATA); logger('paths: ' . print_r($path_arr, true), LOGGER_DATA);
$channel_name = $path_arr[0]; $channel_name = $path_arr[0];
@ -367,7 +369,6 @@ class RedDirectory extends DAV\Node implements DAV\ICollection, DAV\IQuota {
if (! $r) { if (! $r) {
throw new DAV\Exception\NotFound('The file with name: ' . $channel_name . ' could not be found.'); throw new DAV\Exception\NotFound('The file with name: ' . $channel_name . ' could not be found.');
return;
} }
$channel_id = $r[0]['channel_id']; $channel_id = $r[0]['channel_id'];
@ -397,12 +398,11 @@ class RedDirectory extends DAV\Node implements DAV\ICollection, DAV\IQuota {
} }
$this->folder_hash = $folder; $this->folder_hash = $folder;
$this->os_path = $os_path; $this->os_path = $os_path;
return;
} }
/** /**
* @brief Returns the last modification time for the directory, as a UNIX * @brief Returns the last modification time for the directory, as a UNIX
* timestamp. * timestamp.
* *
* It looks for the last edited file in the folder. If it is an empty folder * 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 * it returns the lastmodified time of the folder itself, to prevent zero
@ -429,7 +429,8 @@ class RedDirectory extends DAV\Node implements DAV\ICollection, DAV\IQuota {
/** /**
* @brief Return quota usage. * @brief Return quota usage.
* *
* Do guests relly see the used/free values from filesystem of the complete store directory? * @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. * @return array with used and free values in bytes.
*/ */

View File

@ -49,7 +49,7 @@ class RedFile extends DAV\Node implements DAV\IFile {
$this->data = $data; $this->data = $data;
$this->auth = $auth; $this->auth = $auth;
logger('RedFile::__construct(): ' . print_r($this->data, true), LOGGER_DATA); //logger(print_r($this->data, true), LOGGER_DATA);
} }
/** /**
@ -58,7 +58,7 @@ class RedFile extends DAV\Node implements DAV\IFile {
* @return string * @return string
*/ */
public function getName() { public function getName() {
logger('RedFile::getName(): ' . basename($this->name), LOGGER_DEBUG); //logger(basename($this->name), LOGGER_DATA);
return basename($this->name); return basename($this->name);
} }
@ -70,9 +70,10 @@ class RedFile extends DAV\Node implements DAV\IFile {
* @return void * @return void
*/ */
public function setName($newName) { public function setName($newName) {
logger('RedFile::setName(): ' . basename($this->name) . ' -> ' . $newName, LOGGER_DEBUG); 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'))) { 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.'); throw new DAV\Exception\Forbidden('Permission denied.');
} }
@ -91,7 +92,7 @@ class RedFile extends DAV\Node implements DAV\IFile {
* @return void * @return void
*/ */
public function put($data) { public function put($data) {
logger('RedFile::put(): ' . basename($this->name), LOGGER_DEBUG); logger('put file: ' . basename($this->name), LOGGER_DEBUG);
$size = 0; $size = 0;
// @todo only 3 values are needed // @todo only 3 values are needed
@ -110,7 +111,7 @@ class RedFile extends DAV\Node implements DAV\IFile {
// @todo check return value and set $size directly // @todo check return value and set $size directly
@file_put_contents($f, $data); @file_put_contents($f, $data);
$size = @filesize($f); $size = @filesize($f);
logger('RedFile::put(): filename: ' . $f . ' size: ' . $size, LOGGER_DEBUG); logger('filename: ' . $f . ' size: ' . $size, LOGGER_DEBUG);
} else { } else {
$r = q("UPDATE attach SET data = '%s' WHERE hash = '%s' AND uid = %d LIMIT 1", $r = q("UPDATE attach SET data = '%s' WHERE hash = '%s' AND uid = %d LIMIT 1",
dbesc(stream_get_contents($data)), dbesc(stream_get_contents($data)),
@ -161,7 +162,7 @@ class RedFile extends DAV\Node implements DAV\IFile {
intval($c[0]['channel_account_id']) intval($c[0]['channel_account_id'])
); );
if (($x) && ($x[0]['total'] + $size > $limit)) { if (($x) && ($x[0]['total'] + $size > $limit)) {
logger('RedFile::put(): service class limit exceeded for ' . $c[0]['channel_name'] . ' total usage is ' . $x[0]['total'] . ' limit is ' . $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']); attach_delete($c[0]['channel_id'], $this->data['hash']);
return; return;
} }
@ -174,7 +175,7 @@ class RedFile extends DAV\Node implements DAV\IFile {
* @return string * @return string
*/ */
public function get() { public function get() {
logger('RedFile::get(): ' . basename($this->name), LOGGER_DEBUG); logger('get file ' . basename($this->name), LOGGER_DEBUG);
$r = q("SELECT data, flags, filename, filetype FROM attach WHERE hash = '%s' AND uid = %d LIMIT 1", $r = q("SELECT data, flags, filename, filetype FROM attach WHERE hash = '%s' AND uid = %d LIMIT 1",
dbesc($this->data['hash']), dbesc($this->data['hash']),
@ -206,7 +207,7 @@ class RedFile extends DAV\Node implements DAV\IFile {
* *
* Return null if the ETag can not effectively be determined. * Return null if the ETag can not effectively be determined.
* *
* @return mixed * @return null|string
*/ */
public function getETag() { public function getETag() {
$ret = null; $ret = null;
@ -236,6 +237,7 @@ class RedFile extends DAV\Node implements DAV\IFile {
* @brief Returns the size of the node, in bytes. * @brief Returns the size of the node, in bytes.
* *
* @return int * @return int
* filesize in bytes
*/ */
public function getSize() { public function getSize() {
return $this->data['filesize']; return $this->data['filesize'];
@ -254,11 +256,13 @@ class RedFile extends DAV\Node implements DAV\IFile {
/** /**
* @brief Delete the file. * @brief Delete the file.
* *
* @throw Sabre\DAV\Exception\Forbidden * This method checks the permissions and then calls attach_delete() function
* @return void * to actually remove the file.
*
* @throw \Sabre\DAV\Exception\Forbidden
*/ */
public function delete() { public function delete() {
logger('RedFile::delete(): ' . basename($this->name), LOGGER_DEBUG); 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'))) { if ((! $this->auth->owner_id) || (! perm_is_allowed($this->auth->owner_id, $this->auth->observer, 'write_storage'))) {
throw new DAV\Exception\Forbidden('Permission denied.'); throw new DAV\Exception\Forbidden('Permission denied.');

View File

@ -26,77 +26,74 @@ function z_mime_content_type($filename) {
$mime_types = array( $mime_types = array(
'txt' => 'text/plain', 'txt' => 'text/plain',
'htm' => 'text/html', 'htm' => 'text/html',
'html' => 'text/html', 'html' => 'text/html',
'php' => 'text/html', 'php' => 'text/html',
'css' => 'text/css', 'css' => 'text/css',
'js' => 'application/javascript', 'js' => 'application/javascript',
'json' => 'application/json', 'json' => 'application/json',
'xml' => 'application/xml', 'xml' => 'application/xml',
'swf' => 'application/x-shockwave-flash', 'swf' => 'application/x-shockwave-flash',
'flv' => 'video/x-flv', 'flv' => 'video/x-flv',
'epub' => 'application/epub+zip', 'epub' => 'application/epub+zip',
// images // images
'png' => 'image/png', 'png' => 'image/png',
'jpe' => 'image/jpeg', 'jpe' => 'image/jpeg',
'jpeg' => 'image/jpeg', 'jpeg' => 'image/jpeg',
'jpg' => 'image/jpeg', 'jpg' => 'image/jpeg',
'gif' => 'image/gif', 'gif' => 'image/gif',
'bmp' => 'image/bmp', 'bmp' => 'image/bmp',
'ico' => 'image/vnd.microsoft.icon', 'ico' => 'image/vnd.microsoft.icon',
'tiff' => 'image/tiff', 'tiff' => 'image/tiff',
'tif' => 'image/tiff', 'tif' => 'image/tiff',
'svg' => 'image/svg+xml', 'svg' => 'image/svg+xml',
'svgz' => 'image/svg+xml', 'svgz' => 'image/svg+xml',
// archives // archives
'zip' => 'application/zip', 'zip' => 'application/zip',
'rar' => 'application/x-rar-compressed', 'rar' => 'application/x-rar-compressed',
'exe' => 'application/x-msdownload', 'exe' => 'application/x-msdownload',
'msi' => 'application/x-msdownload', 'msi' => 'application/x-msdownload',
'cab' => 'application/vnd.ms-cab-compressed', 'cab' => 'application/vnd.ms-cab-compressed',
// audio/video // audio/video
'mp3' => 'audio/mpeg', 'mp3' => 'audio/mpeg',
'wav' => 'audio/wav', 'wav' => 'audio/wav',
'qt' => 'video/quicktime', 'qt' => 'video/quicktime',
'mov' => 'video/quicktime', 'mov' => 'video/quicktime',
'ogg' => 'application/ogg', 'ogg' => 'application/ogg',
// adobe // adobe
'pdf' => 'application/pdf', 'pdf' => 'application/pdf',
'psd' => 'image/vnd.adobe.photoshop', 'psd' => 'image/vnd.adobe.photoshop',
'ai' => 'application/postscript', 'ai' => 'application/postscript',
'eps' => 'application/postscript', 'eps' => 'application/postscript',
'ps' => 'application/postscript', 'ps' => 'application/postscript',
// ms office // ms office
'doc' => 'application/msword', 'doc' => 'application/msword',
'rtf' => 'application/rtf', 'rtf' => 'application/rtf',
'xls' => 'application/vnd.ms-excel', 'xls' => 'application/vnd.ms-excel',
'ppt' => 'application/vnd.ms-powerpoint', 'ppt' => 'application/vnd.ms-powerpoint',
// open office
// open office 'odt' => 'application/vnd.oasis.opendocument.text',
'odt' => 'application/vnd.oasis.opendocument.text', 'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
); );
$dot = strpos($filename,'.'); $dot = strpos($filename, '.');
if($dot !== false) { if ($dot !== false) {
$ext = strtolower(substr($filename,$dot+1)); $ext = strtolower(substr($filename, $dot + 1));
if (array_key_exists($ext, $mime_types)) { if (array_key_exists($ext, $mime_types)) {
return $mime_types[$ext]; return $mime_types[$ext];
} }
} }
return 'application/octet-stream'; return 'application/octet-stream';
} }
/** /**
* @brief Count files/attachments. * @brief Count files/attachments.
* *
@ -138,8 +135,8 @@ function attach_count_files($channel_id, $observer, $hash = '', $filename = '',
$ret['success'] = ((is_array($r)) ? true : false); $ret['success'] = ((is_array($r)) ? true : false);
$ret['results'] = ((is_array($r)) ? count($r) : false); $ret['results'] = ((is_array($r)) ? count($r) : false);
return $ret;
return $ret;
} }
/** /**
@ -190,8 +187,8 @@ function attach_list_files($channel_id, $observer, $hash = '', $filename = '', $
$ret['success'] = ((is_array($r)) ? true : false); $ret['success'] = ((is_array($r)) ? true : false);
$ret['results'] = ((is_array($r)) ? $r : false); $ret['results'] = ((is_array($r)) ? $r : false);
return $ret;
return $ret;
} }
/** /**
@ -246,8 +243,8 @@ function attach_by_hash($hash, $rev = 0) {
$ret['success'] = true; $ret['success'] = true;
$ret['data'] = $r[0]; $ret['data'] = $r[0];
return $ret;
return $ret;
} }
/** /**
@ -301,7 +298,6 @@ function attach_by_hash_nodata($hash, $rev = 0) {
$ret['success'] = true; $ret['success'] = true;
$ret['data'] = $r[0]; $ret['data'] = $r[0];
return $ret; return $ret;
} }
/** /**
@ -400,6 +396,7 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) {
if(! isset($hash)) if(! isset($hash))
$hash = random_string(); $hash = random_string();
$created = datetime_convert(); $created = datetime_convert();
if($options === 'replace') { if($options === 'replace') {
@ -432,7 +429,7 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) {
dbesc($x[0]['deny_cid']), dbesc($x[0]['deny_cid']),
dbesc($x[0]['deny_gid']) dbesc($x[0]['deny_gid'])
); );
} }
elseif($options === 'update') { elseif($options === 'update') {
$r = q("update attach set filename = '%s', filetype = '%s', edited = '%s', $r = q("update attach set filename = '%s', filetype = '%s', edited = '%s',
allow_cid = '%s', allow_gid = '%s', deny_cid = '%s', deny_gid = '%s' where id = %d and uid = %d limit 1", allow_cid = '%s', allow_gid = '%s', deny_cid = '%s', deny_gid = '%s' where id = %d and uid = %d limit 1",
@ -446,7 +443,7 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) {
intval($x[0]['id']), intval($x[0]['id']),
intval($x[0]['uid']) intval($x[0]['uid'])
); );
} }
else { else {
$r = q("INSERT INTO attach ( aid, uid, hash, creator, filename, filetype, filesize, revision, data, created, edited, allow_cid, allow_gid,deny_cid, deny_gid ) $r = q("INSERT INTO attach ( aid, uid, hash, creator, filename, filetype, filesize, revision, data, created, edited, allow_cid, allow_gid,deny_cid, deny_gid )
VALUES ( %d, %d, '%s', '%s', '%s', '%s', %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s' ) ", VALUES ( %d, %d, '%s', '%s', '%s', '%s', %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s' ) ",
@ -466,7 +463,7 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) {
dbesc(($arr && array_key_exists('deny_cid',$arr)) ? $arr['deny_cid'] : ''), dbesc(($arr && array_key_exists('deny_cid',$arr)) ? $arr['deny_cid'] : ''),
dbesc(($arr && array_key_exists('deny_gid',$arr)) ? $arr['deny_gid'] : '') dbesc(($arr && array_key_exists('deny_gid',$arr)) ? $arr['deny_gid'] : '')
); );
} }
if($options !== 'update') if($options !== 'update')
@unlink($src); @unlink($src);
@ -490,6 +487,7 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) {
$ret['success'] = true; $ret['success'] = true;
$ret['data'] = $r[0]; $ret['data'] = $r[0];
return $ret; return $ret;
} }
@ -507,8 +505,8 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) {
* $ret['data'] = array of attach DB entries without data component * $ret['data'] = array of attach DB entries without data component
*/ */
function z_readdir($channel_id, $observer_hash, $pathname, $parent_hash = '') { function z_readdir($channel_id, $observer_hash, $pathname, $parent_hash = '') {
$ret = array('success' => false); $ret = array('success' => false);
if(! perm_is_allowed($r[0]['uid'], get_observer_hash(), 'view_storage')) { if(! perm_is_allowed($r[0]['uid'], get_observer_hash(), 'view_storage')) {
$ret['message'] = t('Permission denied.'); $ret['message'] = t('Permission denied.');
return $ret; return $ret;
@ -547,6 +545,7 @@ function z_readdir($channel_id, $observer_hash, $pathname, $parent_hash = '') {
} }
$ret['success'] = true; $ret['success'] = true;
$ret['data'] = $r; $ret['data'] = $r;
return $ret; return $ret;
} }
@ -686,7 +685,6 @@ function attach_mkdir($channel, $observer_hash, $arr = null) {
} }
return $ret; return $ret;
} }
/** /**
@ -732,15 +730,19 @@ function attach_change_permissions($channel_id, $resource, $allow_cid, $allow_gi
dbesc($resource), dbesc($resource),
intval($channel_id) intval($channel_id)
); );
return;
} }
/** /**
* @brief Delete a file/directory. * @brief Delete a file/directory from a channel.
* *
* If the provided resource hash is from a directory it will delete everything
* recursively under this directory.
*
* @param int $channel_id * @param int $channel_id
* @param string $resource a hash to delete * The id of the channel
* @param string $resource
* The hash to delete
* @return void
*/ */
function attach_delete($channel_id, $resource) { function attach_delete($channel_id, $resource) {
@ -760,7 +762,7 @@ function attach_delete($channel_id, $resource) {
// If resource is a directory delete everything in the directory recursive // If resource is a directory delete everything in the directory recursive
if($r[0]['flags'] & ATTACH_FLAG_DIR) { if($r[0]['flags'] & ATTACH_FLAG_DIR) {
$x = q("select hash, flags from attach where folder = '%s' and uid = %d", $x = q("SELECT hash, flags FROM attach WHERE folder = '%s' AND uid = %d",
dbesc($resource), dbesc($resource),
intval($channel_id) intval($channel_id)
); );
@ -799,19 +801,21 @@ function attach_delete($channel_id, $resource) {
dbesc($r[0]['folder']), dbesc($r[0]['folder']),
intval($channel_id) intval($channel_id)
); );
return;
} }
/** /**
* @brief Returns path to file in cloud/. * @brief Returns path to file in cloud/.
* *
* @param $arr * @param array
* @return string with the path the file to cloud/ * $arr[uid] int the channels uid
* $arr[folder] string
* $arr[filename]] string
* @return string
* path to the file in cloud/
*/ */
function get_cloudpath($arr) { function get_cloudpath($arr) {
$basepath = 'cloud/'; $basepath = 'cloud/';
if($arr['uid']) { if($arr['uid']) {
$r = q("select channel_address from channel where channel_id = %d limit 1", $r = q("select channel_address from channel where channel_id = %d limit 1",
intval($arr['uid']) intval($arr['uid'])
@ -823,7 +827,6 @@ function get_cloudpath($arr) {
$path = $basepath; $path = $basepath;
if($arr['folder']) { if($arr['folder']) {
$lpath = ''; $lpath = '';
$lfile = $arr['folder']; $lfile = $arr['folder'];
@ -842,59 +845,82 @@ function get_cloudpath($arr) {
$lpath = $r[0]['filename'] . '/' . $lpath; $lpath = $r[0]['filename'] . '/' . $lpath;
$lfile = $r[0]['folder']; $lfile = $r[0]['folder'];
} while ( ($r[0]['folder']) && ($r[0]['flags'] & ATTACH_FLAG_DIR)) ; } while ( ($r[0]['folder']) && ($r[0]['flags'] & ATTACH_FLAG_DIR));
$path .= $lpath; $path .= $lpath;
} }
$path .= $arr['filename']; $path .= $arr['filename'];
return $path; return $path;
} }
/** /**
* @brief Returns path to parent folder in cloud/. * @brief Returns path to parent folder in cloud/.
* *
* @param $arr * @param int $channel_id
* @return string with the folder path * The id of the channel
* @param string $channel_name
* The name of the channel
* @param string $attachHash
* @return string with the full folder path
*/ */
function get_parent_cloudpath($channel_id, $channel_name, $attachHash) { function get_parent_cloudpath($channel_id, $channel_name, $attachHash) {
//Build directory tree and redirect // build directory tree
$parentHash = $attachHash; $parentHash = $attachHash;
do { do {
$parentHash = find_folder_hash_by_attach_hash($channel_id, $parentHash); $parentHash = find_folder_hash_by_attach_hash($channel_id, $parentHash);
if ($parentHash) { if ($parentHash) {
$parentName = find_filename_by_hash($channel_id, $parentHash); $parentName = find_filename_by_hash($channel_id, $parentHash);
$parentFullPath = $parentName."/".$parentFullPath; $parentFullPath = $parentName . '/' . $parentFullPath;
} }
} while ($parentHash); } while ($parentHash);
$parentFullPath = z_root() . "/cloud/" . $channel_name . "/" . $parentFullPath; $parentFullPath = z_root() . '/cloud/' . $channel_name . '/' . $parentFullPath;
return $parentFullPath; return $parentFullPath;
} }
/**
* @brief Return the hash of an attachment's folder.
*
* @param int $channel_id
* The id of the channel
* @param string $attachHash
* The hash of the attachment
* @return string
*/
function find_folder_hash_by_attach_hash($channel_id, $attachHash) { function find_folder_hash_by_attach_hash($channel_id, $attachHash) {
$r = q("select * from attach where uid = %d and hash = '%s' limit 1", $r = q("SELECT folder FROM attach WHERE uid = %d AND hash = '%s' LIMIT 1",
intval($channel_id), dbesc($attachHash) intval($channel_id),
dbesc($attachHash)
); );
$hash = ""; $hash = '';
if($r) { if ($r) {
foreach($r as $rr) { $hash = $r[0]['folder'];
$hash = $rr['folder'];
}
} }
return $hash; return $hash;
}
function find_filename_by_hash($channel_id, $attachHash) {
$r = q("select * from attach where uid = %d and hash = '%s' limit 1",
intval($channel_id), dbesc($attachHash)
);
$filename = "";
if($r) {
foreach($r as $rr) {
$filename = $rr['filename'];
}
}
return $filename;
} }
/**
* @brief Returns the filename of an attachment in a given channel.
*
* @param mixed $channel_id
* The id of the channel
* @param mixed $attachHash
* The hash of the attachment
* @return string
* The filename of the attachment
*/
function find_filename_by_hash($channel_id, $attachHash) {
$r = q("SELECT filename FROM attach WHERE uid = %d AND hash = '%s' LIMIT 1",
intval($channel_id),
dbesc($attachHash)
);
$filename = '';
if ($r) {
$filename = $r[0]['filename'];
}
return $filename;
}
/** /**
* *
@ -904,6 +930,6 @@ function find_filename_by_hash($channel_id, $attachHash) {
function pipe_streams($in, $out) { function pipe_streams($in, $out) {
$size = 0; $size = 0;
while (!feof($in)) while (!feof($in))
$size += fwrite($out, fread($in,8192)); $size += fwrite($out, fread($in, 8192));
return $size; return $size;
} }

View File

@ -927,9 +927,14 @@ function get_diaspora_reshare_xml($url,$recurse = 0) {
// see if it's a reshare of a reshare // see if it's a reshare of a reshare
if($source_xml->root_diaspora_id && $source_xml->root_guid && $recurse < 15) { if($source_xml->post->reshare)
$orig_author = notags(unxmlify($source_xml->root_diaspora_id)); $xml = $source_xml->post->reshare;
$orig_guid = notags(unxmlify($source_xml->root_guid)); else
return false;
if($xml->root_diaspora_id && $xml->root_guid && $recurse < 15) {
$orig_author = notags(unxmlify($xml->root_diaspora_id));
$orig_guid = notags(unxmlify($xml->root_guid));
$source_url = 'https://' . substr($orig_author,strpos($orig_author,'@')+1) . '/p/' . $orig_guid . '.xml'; $source_url = 'https://' . substr($orig_author,strpos($orig_author,'@')+1) . '/p/' . $orig_guid . '.xml';
$y = get_diaspora_reshare_xml($source_url,$recurse+1); $y = get_diaspora_reshare_xml($source_url,$recurse+1);
if($y) if($y)

View File

@ -113,4 +113,61 @@ function remove_obsolete_hublocs() {
} }
// This actually changes other structures to match the given (presumably current) hubloc primary selection
function hubloc_change_primary($hubloc) {
if(! is_array($hubloc)) {
logger('no hubloc');
return false;
}
if(! ($hubloc['hubloc_flags'] & HUBLOC_FLAGS_PRIMARY)) {
logger('not primary: ' . $hubloc['hubloc_url']);
return false;
}
logger('setting primary: ' . $hubloc['hubloc_url']);
// See if there's a local channel
$r = q("select channel_id, channel_primary from channel where channel_hash = '%s' limit 1",
dbesc($hubloc['hubloc_hash'])
);
if(($r) && (! $r[0]['channel_primary'])) {
q("update channel set channel_primary = 1 where channel_id = %d limit 1",
intval($r[0]['channel_id'])
);
}
// do we even have an xchan for this hubloc and if so is it already set as primary?
$r = q("select * from xchan where xchan_hash = '%s' limit 1",
dbesc($hubloc['hubloc_hash'])
);
if(! $r) {
logger('xchan not found');
return false;
}
if($r[0]['xchan_addr'] === $hubloc['hubloc_addr']) {
logger('xchan already changed');
return false;
}
$url = $hubloc['hubloc_url'];
$lwebbie = substr($hubloc['hubloc_addr'],0,strpos($hubloc['hubloc_addr'],'@'));
$r = q("update xchan set xchan_addr = '%s', xchan_url = '%s', xchan_follow = '%s', xchan_connurl = '%s' where xchan_hash = '%s' limit 1",
dbesc($hubloc['hubloc_addr']),
dbesc($url . '/channel/' . $lwebbie),
dbesc($url . '/follow?f=&url=%s'),
dbesc($url . '/poco/' . $lwebbie),
dbesc($hubloc['hubloc_hash'])
);
if(! $r)
logger('xchan_update failed.');
logger('primary hubloc changed.' . print_r($hubloc,true),LOGGER_DEBUG);
return true;
}

View File

@ -291,8 +291,8 @@ function create_identity($arr) {
// Create a verified hub location pointing to this site. // Create a verified hub location pointing to this site.
$r = q("insert into hubloc ( hubloc_guid, hubloc_guid_sig, hubloc_hash, hubloc_addr, hubloc_flags, $r = q("insert into hubloc ( hubloc_guid, hubloc_guid_sig, hubloc_hash, hubloc_addr, hubloc_flags,
hubloc_url, hubloc_url_sig, hubloc_host, hubloc_callback, hubloc_sitekey ) hubloc_url, hubloc_url_sig, hubloc_host, hubloc_callback, hubloc_sitekey, hubloc_network )
values ( '%s', '%s', '%s', '%s', %d, '%s', '%s', '%s', '%s', '%s' )", values ( '%s', '%s', '%s', '%s', %d, '%s', '%s', '%s', '%s', '%s', '%s' )",
dbesc($guid), dbesc($guid),
dbesc($sig), dbesc($sig),
dbesc($hash), dbesc($hash),
@ -302,7 +302,8 @@ function create_identity($arr) {
dbesc(base64url_encode(rsa_sign(z_root(),$ret['channel']['channel_prvkey']))), dbesc(base64url_encode(rsa_sign(z_root(),$ret['channel']['channel_prvkey']))),
dbesc(get_app()->get_hostname()), dbesc(get_app()->get_hostname()),
dbesc(z_root() . '/post'), dbesc(z_root() . '/post'),
dbesc(get_config('system','pubkey')) dbesc(get_config('system','pubkey')),
dbesc('zot')
); );
if(! $r) if(! $r)
logger('create_identity: Unable to store hub location'); logger('create_identity: Unable to store hub location');

View File

@ -402,7 +402,7 @@ function validate_email($addr) {
return false; return false;
$h = substr($addr,strpos($addr,'@') + 1); $h = substr($addr,strpos($addr,'@') + 1);
if(($h) && (dns_get_record($h, DNS_A + DNS_CNAME + DNS_PTR + DNS_MX) || filter_var($h['host'], FILTER_VALIDATE_IP) )) { if(($h) && (dns_get_record($h, DNS_A + DNS_CNAME + DNS_PTR + DNS_MX) || filter_var($h, FILTER_VALIDATE_IP) )) {
return true; return true;
} }
return false; return false;

View File

@ -36,8 +36,8 @@ require_once('include/RedDAV/RedBasicAuth.php');
* @todo Is there any reason why this is not inside RedDirectory class? * @todo Is there any reason why this is not inside RedDirectory class?
* @fixme function name looks like a class name, should we rename it? * @fixme function name looks like a class name, should we rename it?
* *
* @param $auth * @param RedBasicAuth &$auth
* @return array containing RedDirectory objects * @return array RedDirectory[]
*/ */
function RedChannelList(&$auth) { function RedChannelList(&$auth) {
$ret = array(); $ret = array();
@ -50,7 +50,7 @@ function RedChannelList(&$auth) {
if ($r) { if ($r) {
foreach ($r as $rr) { foreach ($r as $rr) {
if (perm_is_allowed($rr['channel_id'], $auth->observer, 'view_storage')) { if (perm_is_allowed($rr['channel_id'], $auth->observer, 'view_storage')) {
logger('RedChannelList: ' . '/cloud/' . $rr['channel_address'], LOGGER_DATA); logger('found channel: /cloud/' . $rr['channel_address'], LOGGER_DEBUG);
// @todo can't we drop '/cloud'? It gets stripped off anyway in RedDirectory // @todo can't we drop '/cloud'? It gets stripped off anyway in RedDirectory
$ret[] = new RedDAV\RedDirectory('/cloud/' . $rr['channel_address'], $auth); $ret[] = new RedDAV\RedDirectory('/cloud/' . $rr['channel_address'], $auth);
} }
@ -70,8 +70,10 @@ function RedChannelList(&$auth) {
* @fixme function name looks like a class name, should we rename it? * @fixme function name looks like a class name, should we rename it?
* *
* @param string $file path to a directory * @param string $file path to a directory
* @param &$auth * @param RedBasicAuth &$auth
* @returns array DAV\INode[] * @returns null|array \Sabre\DAV\INode[]
* @throw \Sabre\DAV\Exception\Forbidden
* @throw \Sabre\DAV\Exception\NotFound
*/ */
function RedCollectionData($file, &$auth) { function RedCollectionData($file, &$auth) {
$ret = array(); $ret = array();
@ -88,7 +90,7 @@ function RedCollectionData($file, &$auth) {
$file = trim($file, '/'); $file = trim($file, '/');
$path_arr = explode('/', $file); $path_arr = explode('/', $file);
if (! $path_arr) if (! $path_arr)
return null; return null;
@ -150,7 +152,7 @@ function RedCollectionData($file, &$auth) {
// This should no longer be needed since we just returned errors for paths not found // This should no longer be needed since we just returned errors for paths not found
if ($path !== '/' . $file) { if ($path !== '/' . $file) {
logger("RedCollectionData: Path mismatch: $path !== /$file"); logger("Path mismatch: $path !== /$file");
return NULL; return NULL;
} }
@ -160,8 +162,7 @@ function RedCollectionData($file, &$auth) {
); );
foreach ($r as $rr) { foreach ($r as $rr) {
logger('RedCollectionData: filename: ' . $rr['filename'], LOGGER_DATA); //logger('filename: ' . $rr['filename'], LOGGER_DEBUG);
if ($rr['flags'] & ATTACH_FLAG_DIR) { if ($rr['flags'] & ATTACH_FLAG_DIR) {
// @todo can't we drop '/cloud'? it gets stripped off anyway in RedDirectory // @todo can't we drop '/cloud'? it gets stripped off anyway in RedDirectory
$ret[] = new RedDAV\RedDirectory('/cloud' . $path . '/' . $rr['filename'], $auth); $ret[] = new RedDAV\RedDirectory('/cloud' . $path . '/' . $rr['filename'], $auth);
@ -180,11 +181,14 @@ function RedCollectionData($file, &$auth) {
* @fixme function name looks like a class name, should we rename it? * @fixme function name looks like a class name, should we rename it?
* *
* @param string $file * @param string $file
* @param &$auth * path to file or directory
* @param RedBasicAuth &$auth
* @param boolean $test (optional) enable test mode * @param boolean $test (optional) enable test mode
* @return RedFile|RedDirectory|boolean|null
* @throw \Sabre\DAV\Exception\Forbidden
*/ */
function RedFileData($file, &$auth, $test = false) { function RedFileData($file, &$auth, $test = false) {
logger('RedFileData:' . $file . (($test) ? ' (test mode) ' : ''), LOGGER_DEBUG); logger($file . (($test) ? ' (test mode) ' : ''), LOGGER_DEBUG);
$x = strpos($file, '/cloud'); $x = strpos($file, '/cloud');
if ($x === 0) { if ($x === 0) {
@ -198,7 +202,7 @@ function RedFileData($file, &$auth, $test = false) {
$file = trim($file, '/'); $file = trim($file, '/');
$path_arr = explode('/', $file); $path_arr = explode('/', $file);
if (! $path_arr) if (! $path_arr)
return null; return null;
@ -237,7 +241,7 @@ function RedFileData($file, &$auth, $test = false) {
if ($r && ( $r[0]['flags'] & ATTACH_FLAG_DIR)) { if ($r && ( $r[0]['flags'] & ATTACH_FLAG_DIR)) {
$folder = $r[0]['hash']; $folder = $r[0]['hash'];
$path = $path . '/' . $r[0]['filename']; $path = $path . '/' . $r[0]['filename'];
} }
if (! $r) { if (! $r) {
$r = q("select id, uid, hash, filename, filetype, filesize, revision, folder, flags, created, edited from attach $r = q("select id, uid, hash, filename, filetype, filesize, revision, folder, flags, created, edited from attach
where folder = '%s' and filename = '%s' and uid = %d $perms group by filename limit 1", where folder = '%s' and filename = '%s' and uid = %d $perms group by filename limit 1",
@ -267,11 +271,11 @@ function RedFileData($file, &$auth, $test = false) {
} }
if ($errors) { if ($errors) {
logger('RedFileData: not found'); logger('not found ' . $file);
if ($test) if ($test)
return false; return false;
if ($permission_error) { if ($permission_error) {
logger('RedFileData: permission error'); logger('permission error ' . $file);
throw new DAV\Exception\Forbidden('Permission denied.'); throw new DAV\Exception\Forbidden('Permission denied.');
} }
return; return;

View File

@ -2,6 +2,7 @@
require_once('include/crypto.php'); require_once('include/crypto.php');
require_once('include/items.php'); require_once('include/items.php');
require_once('include/hubloc.php');
/** /**
* Red implementation of zot protocol. * Red implementation of zot protocol.
@ -1714,9 +1715,14 @@ function process_location_delivery($sender,$arr,$deliveries) {
); );
if($r) if($r)
$sender['key'] = $r[0]['xchan_pubkey']; $sender['key'] = $r[0]['xchan_pubkey'];
if(array_key_exists('locations',$arr) && $arr['locations']) {
$x = sync_locations($sender,$arr,true); $x = sync_locations($sender,$arr,true);
logger('process_location_delivery: results: ' . print_r($x,true), LOGGER_DEBUG); logger('process_location_delivery: results: ' . print_r($x,true), LOGGER_DEBUG);
if($x['changed']) {
$guid = random_string() . '@' . get_app()->get_hostname();
update_modtime($sender['hash'],$sender['guid'],$arr['locations'][0]['address'],UPDATE_FLAGS_UPDATED);
}
}
} }
@ -1832,17 +1838,28 @@ function sync_locations($sender,$arr,$absolute = false) {
if((($r[0]['hubloc_flags'] & HUBLOC_FLAGS_PRIMARY) && (! $location['primary'])) if((($r[0]['hubloc_flags'] & HUBLOC_FLAGS_PRIMARY) && (! $location['primary']))
|| ((! ($r[0]['hubloc_flags'] & HUBLOC_FLAGS_PRIMARY)) && ($location['primary']))) { || ((! ($r[0]['hubloc_flags'] & HUBLOC_FLAGS_PRIMARY)) && ($location['primary']))) {
$r = q("update hubloc set hubloc_flags = (hubloc_flags ^ %d), hubloc_updated = '%s' where hubloc_id = %d limit 1", $m = q("update hubloc set hubloc_flags = (hubloc_flags ^ %d), hubloc_updated = '%s' where hubloc_id = %d limit 1",
intval(HUBLOC_FLAGS_PRIMARY), intval(HUBLOC_FLAGS_PRIMARY),
dbesc(datetime_convert()), dbesc(datetime_convert()),
intval($r[0]['hubloc_id']) intval($r[0]['hubloc_id'])
); );
// make sure hubloc_change_primary() has current data
$r[0]['hubloc_flags'] = $r[0]['hubloc_flags'] ^ HUBLOC_FLAGS_PRIMARY;
hubloc_change_primary($r[0]);
$what .= 'primary_hub '; $what .= 'primary_hub ';
$changed = true; $changed = true;
} }
elseif($absolute) {
// Absolute sync - make sure the current primary is correctly reflected in the xchan
$pr = hubloc_change_primary($r[0]);
if($pr) {
$what .= 'xchan_primary';
$changed = true;
}
}
if((($r[0]['hubloc_flags'] & HUBLOC_FLAGS_DELETED) && (! $location['deleted'])) if((($r[0]['hubloc_flags'] & HUBLOC_FLAGS_DELETED) && (! $location['deleted']))
|| ((! ($r[0]['hubloc_flags'] & HUBLOC_FLAGS_DELETED)) && ($location['deleted']))) { || ((! ($r[0]['hubloc_flags'] & HUBLOC_FLAGS_DELETED)) && ($location['deleted']))) {
$r = q("update hubloc set hubloc_flags = (hubloc_flags ^ %d), hubloc_updated = '%s' where hubloc_id = %d limit 1", $n = q("update hubloc set hubloc_flags = (hubloc_flags ^ %d), hubloc_updated = '%s' where hubloc_id = %d limit 1",
intval(HUBLOC_FLAGS_DELETED), intval(HUBLOC_FLAGS_DELETED),
dbesc(datetime_convert()), dbesc(datetime_convert()),
intval($r[0]['hubloc_id']) intval($r[0]['hubloc_id'])
@ -1884,6 +1901,14 @@ function sync_locations($sender,$arr,$absolute = false) {
$what .= 'newhub '; $what .= 'newhub ';
$changed = true; $changed = true;
if($location['primary']) {
$r = q("select * from hubloc where hubloc_addr = '%s' and hubloc_sitekey = '%s' limit 1",
dbesc($location['address']),
dbesc($location['sitekey'])
);
if($r)
hubloc_change_primary($r[0]);
}
} }
// get rid of any hubs we have for this channel which weren't reported. // get rid of any hubs we have for this channel which weren't reported.

View File

@ -1,6 +1,6 @@
<?php <?php
define( 'UPDATE_VERSION' , 1129 ); define( 'UPDATE_VERSION' , 1130 );
/** /**
* *
@ -1457,3 +1457,10 @@ function update_r1128() {
} }
function update_r1129() {
$r = q("update hubloc set hubloc_network = 'zot' where hubloc_network = ''");
if($r)
return UPDATE_SUCCESS;
return UPDATE_FAILED;
}

View File

@ -1,31 +0,0 @@
<?php /** @file */
/**
Placeholder file at present. This is going to involve a bit of work.
This file will deal with the deletion of channels and management of hublocs.
We need to provide the following functionality:
- Delete my account and all channels from the entire network
- Delete my account and all channels from this server
- Delete a channel from the entire network
- Delete a channel from this server
- List all hub locations for this channel
- Remove this/some hub location from this channel
- promote this/some hub location to primary
- Remove hub location 'xyz' from this channel, (this should possibly only be allowed if that hub has been down for a period of time)
- Some of these actions should probably require email verification
*/

View File

@ -1,18 +1,26 @@
<?php <?php
/**
* @file mod/filestorage.php
*
*/
require_once('include/attach.php'); require_once('include/attach.php');
/**
*
* @param object &$a
*/
function filestorage_post(&$a) { function filestorage_post(&$a) {
$channel_id = ((x($_POST,'uid')) ? intval($_POST['uid']) : 0); $channel_id = ((x($_POST, 'uid')) ? intval($_POST['uid']) : 0);
if((! $channel_id) || (! local_user()) || ($channel_id != local_user())) { if((! $channel_id) || (! local_user()) || ($channel_id != local_user())) {
notice( t('Permission denied.') . EOL); notice( t('Permission denied.') . EOL);
return; return;
} }
$recurse = ((x($_POST,'recurse')) ? intval($_POST['recurse']) : 0); $recurse = ((x($_POST, 'recurse')) ? intval($_POST['recurse']) : 0);
$resource = ((x($_POST,'filehash')) ? notags($_POST['filehash']) : ''); $resource = ((x($_POST, 'filehash')) ? notags($_POST['filehash']) : '');
if(! $resource) { if(! $resource) {
notice(t('Item not found.') . EOL); notice(t('Item not found.') . EOL);
@ -24,11 +32,11 @@ function filestorage_post(&$a) {
$str_group_deny = perms2str($_REQUEST['group_deny']); $str_group_deny = perms2str($_REQUEST['group_deny']);
$str_contact_deny = perms2str($_REQUEST['contact_deny']); $str_contact_deny = perms2str($_REQUEST['contact_deny']);
attach_change_permissions($channel_id,$resource,$str_contact_allow,$str_group_allow,$str_contact_deny,$str_group_deny,$recurse = false); attach_change_permissions($channel_id, $resource, $str_contact_allow, $str_group_allow, $str_contact_deny,$str_group_deny, $recurse = false);
//Build directory tree and redirect //Build directory tree and redirect
$channel = $a->get_channel(); $channel = $a->get_channel();
$cloudPath = get_parent_cloudpath($channel_id, $channel['channel_address'], $resource) ; $cloudPath = get_parent_cloudpath($channel_id, $channel['channel_address'], $resource);
goaway($cloudPath); goaway($cloudPath);
} }
@ -53,15 +61,15 @@ function filestorage_content(&$a) {
$observer = $a->get_observer(); $observer = $a->get_observer();
$ob_hash = (($observer) ? $observer['xchan_hash'] : ''); $ob_hash = (($observer) ? $observer['xchan_hash'] : '');
$perms = get_all_perms($owner,$ob_hash); $perms = get_all_perms($owner, $ob_hash);
if(! $perms['view_storage']) { if(! $perms['view_storage']) {
notice( t('Permission denied.') . EOL); notice( t('Permission denied.') . EOL);
return; return;
} }
// Since we have ACL'd files in the wild, but don't have ACL here yet, we // Since we have ACL'd files in the wild, but don't have ACL here yet, we
// need to return for anyone other than the owner, despite the perms check for now. // need to return for anyone other than the owner, despite the perms check for now.
$is_owner = (((local_user()) && ($owner == local_user())) ? true : false); $is_owner = (((local_user()) && ($owner == local_user())) ? true : false);
if(! $is_owner) { if(! $is_owner) {
@ -69,7 +77,6 @@ function filestorage_content(&$a) {
return; return;
} }
if(argc() > 3 && argv(3) === 'delete') { if(argc() > 3 && argv(3) === 'delete') {
if(! $perms['write_storage']) { if(! $perms['write_storage']) {
notice( t('Permission denied.') . EOL); notice( t('Permission denied.') . EOL);
@ -77,7 +84,7 @@ function filestorage_content(&$a) {
} }
$file = intval(argv(2)); $file = intval(argv(2));
$r = q("select hash from attach where id = %d and uid = %d limit 1", $r = q("SELECT hash FROM attach WHERE id = %d AND uid = %d LIMIT 1",
dbesc($file), dbesc($file),
intval($owner) intval($owner)
); );
@ -86,11 +93,15 @@ function filestorage_content(&$a) {
goaway(z_root() . '/cloud/' . $which); goaway(z_root() . '/cloud/' . $which);
} }
attach_delete($owner,$r[0]['hash']); $f = $r[0];
$channel = $a->get_channel();
goaway(z_root() . '/cloud/' . $which);
}
$parentpath = get_parent_cloudpath($channel['channel_id'], $channel['channel_address'], $f['hash']);
attach_delete($owner, $f['hash']);
goaway($parentpath);
}
if(argc() > 3 && argv(3) === 'edit') { if(argc() > 3 && argv(3) === 'edit') {
require_once('include/acl_selectors.php'); require_once('include/acl_selectors.php');
@ -106,18 +117,16 @@ function filestorage_content(&$a) {
); );
$f = $r[0]; $f = $r[0];
$channel = $a->get_channel(); $channel = $a->get_channel();
$cloudpath = get_cloudpath($f) . (($f['flags'] & ATTACH_FLAG_DIR) ? '?f=&davguest=1' : ''); $cloudpath = get_cloudpath($f) . (($f['flags'] & ATTACH_FLAG_DIR) ? '?f=&davguest=1' : '');
$parentpath = get_parent_cloudpath($channel['channel_id'], $channel['channel_address'], $f['hash']); $parentpath = get_parent_cloudpath($channel['channel_id'], $channel['channel_address'], $f['hash']);
$aclselect_e = populate_acl($f,false); $aclselect_e = populate_acl($f, false);
$is_a_dir = (($f['flags'] & ATTACH_FLAG_DIR) ? true : false); $is_a_dir = (($f['flags'] & ATTACH_FLAG_DIR) ? true : false);
$lockstate = (($f['allow_cid'] || $f['allow_gid'] || $f['deny_cid'] || $f['deny_gid']) ? 'lock' : 'unlock'); $lockstate = (($f['allow_cid'] || $f['allow_gid'] || $f['deny_cid'] || $f['deny_gid']) ? 'lock' : 'unlock');
$o = replace_macros(get_markup_template('attach_edit.tpl'), array( $o = replace_macros(get_markup_template('attach_edit.tpl'), array(
'$header' => t('Edit file permissions'), '$header' => t('Edit file permissions'),
'$file' => $f, '$file' => $f,
@ -135,12 +144,10 @@ function filestorage_content(&$a) {
'$cpdesc' => t('Copy/paste this code to attach file to a post'), '$cpdesc' => t('Copy/paste this code to attach file to a post'),
'$cpldesc' => t('Copy/paste this URL to link file from a web page'), '$cpldesc' => t('Copy/paste this URL to link file from a web page'),
'$submit' => t('Submit') '$submit' => t('Submit')
)); ));
return $o; return $o;
} }
goaway(z_root() . '/cloud/' . $which); goaway(z_root() . '/cloud/' . $which);
} }

View File

@ -344,7 +344,7 @@ function import_post(&$a) {
unset($group['id']); unset($group['id']);
$group['uid'] = $channel['channel_id']; $group['uid'] = $channel['channel_id'];
dbesc_array($group); dbesc_array($group);
$r = dbq("INSERT INTO group (`" $r = dbq("INSERT INTO groups (`"
. implode("`, `", array_keys($group)) . implode("`, `", array_keys($group))
. "`) VALUES ('" . "`) VALUES ('"
. implode("', '", array_values($group)) . implode("', '", array_values($group))

90
mod/locs.php Normal file
View File

@ -0,0 +1,90 @@
<?php /** @file */
/**
Placeholder file at present. This is going to involve a bit of work.
This file will deal with the deletion of channels and management of hublocs.
We need to provide the following functionality:
- Delete my account and all channels from the entire network
- Delete my account and all channels from this server
- Delete a channel from the entire network
- Delete a channel from this server
- List all hub locations for this channel
- Remove this/some hub location from this channel
- promote this/some hub location to primary
- Remove hub location 'xyz' from this channel, (this should possibly only be allowed if that hub has been down for a period of time)
- Some of these actions should probably require email verification
*/
function locs_post(&$a) {
if(! local_user())
return;
$channel = $a->get_channel();
if($_REQUEST['primary']) {
$hubloc_id = intval($_REQUEST['primary']);
if($hubloc_id) {
$r = q("select hubloc_id from hubloc where hubloc_id = %d and hubloc_hash = '%s' limit 1",
intval($hubloc_id),
dbesc($channel['channel_hash'])
);
if(! $r) {
notice( t('Location not found.') . EOL);
return;
}
$r = q("update hubloc set hubloc_flags = (hubloc_flags ^ %d) where (hubloc_flags & %d) and hubloc_hash = '%s' ",
intval(HUBLOC_FLAGS_PRIMARY),
intval(HUBLOC_FLAGS_PRIMARY),
dbesc($channel['channel_hash'])
);
$r = q("update hubloc set hubloc_flags = (hubloc_flags & %d) where hubloc_id = %d and hubloc_hash = '%s' limit 1",
intval(HUBLOC_FLAGS_PRIMARY),
intval($hubloc_id),
dbesc($channel['channel_hash'])
);
proc_run('php','include/notifier.php','location',$channel['channel_id']);
return;
}
}
if($_REQUEST['drop']) {
$hubloc_id = intval($_REQUEST['drop']);
if($hubloc_id) {
$r = q("select hubloc_id, hubloc_flags from hubloc where hubloc_id = %d and hubloc_url != '%s' and hubloc_hash = '%s' limit 1",
intval($hubloc_id),
dbesc(z_root()),
dbesc($channel['channel_hash'])
);
if(! $r) {
notice( t('Location not found.') . EOL);
return;
}
if($r[0]['hubloc_flags'] & HUBLOC_FLAGS_PRIMARY) {
notice( t('Primary location cannot be removed.') . EOL);
return;
}
$r = q("update hubloc set hubloc_flags = (hubloc_flags & %d) where hubloc_id = %d and hubloc_hash = '%s' limit 1",
intval(HUBLOC_FLAGS_DELETED),
intval($hubloc_id),
dbesc($channel['channel_hash'])
);
proc_run('php','include/notifier.php','location',$channel['channel_id']);
return;
}
}
}

View File

@ -999,7 +999,7 @@ function photos_content(&$a) {
$comments = ''; $comments = '';
if(! count($r)) { if(! count($r)) {
if($can_post || $can_comment) { if($can_post || $can_comment) {
$comments .= replace_macros($cmnt_tpl,array( $commentbox = replace_macros($cmnt_tpl,array(
'$return_path' => '', '$return_path' => '',
'$mode' => 'photos', '$mode' => 'photos',
'$jsreload' => $return_url, '$jsreload' => $return_url,
@ -1070,7 +1070,7 @@ function photos_content(&$a) {
$body_e = prepare_text($item['body'],$item['mimetype']); $body_e = prepare_text($item['body'],$item['mimetype']);
$comments .= replace_macros($template,array( $comments .= replace_macros($template,array(
'$id' => $item['item_id'], '$id' => $item['id'],
'$mode' => 'photos', '$mode' => 'photos',
'$profile_url' => $profile_link, '$profile_url' => $profile_link,
'$name' => $name_e, '$name' => $name_e,
@ -1079,7 +1079,7 @@ function photos_content(&$a) {
'$title' => $title_e, '$title' => $title_e,
'$body' => $body_e, '$body' => $body_e,
'$ago' => relative_date($item['created']), '$ago' => relative_date($item['created']),
'$indent' => (($item['parent'] != $item['item_id']) ? ' comment' : ''), '$indent' => (($item['parent'] != $item['id']) ? ' comment' : ''),
'$drop' => $drop, '$drop' => $drop,
'$comment' => $comment '$comment' => $comment
)); ));
@ -1087,7 +1087,7 @@ function photos_content(&$a) {
} }
if($can_post || $can_comment) { if($can_post || $can_comment) {
$comments .= replace_macros($cmnt_tpl,array( $commentbox = replace_macros($cmnt_tpl,array(
'$return_path' => '', '$return_path' => '',
'$jsreload' => $return_url, '$jsreload' => $return_url,
'$type' => 'wall-comment', '$type' => 'wall-comment',
@ -1129,6 +1129,7 @@ function photos_content(&$a) {
'$like' => $like_e, '$like' => $like_e,
'$dislike' => $dislike_e, '$dislike' => $dislike_e,
'$comments' => $comments, '$comments' => $comments,
'$commentbox' => $commentbox,
'$paginate' => $paginate, '$paginate' => $paginate,
)); ));

View File

@ -1 +1 @@
2014-10-13.827 2014-10-14.828

View File

@ -81,13 +81,8 @@
/* group */ /* group */
input.group-edit-checkbox {
margin: unset;
height: 1em;
width: 1em;
}
a.group-edit-link { a.group-edit-tool {
z-index: 1; z-index: 1;
} }

View File

@ -986,6 +986,7 @@ function updateConvItems(mode,data) {
$('body').css('cursor', 'wait'); $('body').css('cursor', 'wait');
$.get('contactgroup/' + gid + '/' + cid, function(data) { $.get('contactgroup/' + gid + '/' + cid, function(data) {
$('body').css('cursor', 'auto'); $('body').css('cursor', 'auto');
$('#group-' + gid).toggleClass('icon-check icon-check-empty');
}); });
} }

View File

@ -5,16 +5,12 @@
{{foreach $groups as $group}} {{foreach $groups as $group}}
<li> <li>
{{if $group.cid}} {{if $group.cid}}
<a class="pull-right group-edit-link"> <a class="pull-right group-edit-tool fakelink" onclick="contactgroupChangeMember('{{$group.id}}','{{$group.enc_cid}}'); return true;"/>
<input type="checkbox" <i id="group-{{$group.id}}" class="{{if $group.ismember}}icon-check{{else}}icon-check-empty{{/if}}"></i>
class="{{if $group.selected}}ticked{{else}}unticked {{/if}} group-edit-checkbox"
onclick="contactgroupChangeMember('{{$group.id}}','{{$group.enc_cid}}');return true;"
{{if $group.ismember}}checked="checked"{{/if}}
/>
</a> </a>
{{/if}} {{/if}}
{{if $group.edit}} {{if $group.edit}}
<a class="pull-right group-edit-link" href="{{$group.edit.href}}" title="{{$edittext}}"><i class="group-edit-icon icon-pencil"></i></a> <a class="pull-right group-edit-tool" href="{{$group.edit.href}}" title="{{$edittext}}"><i class="group-edit-icon icon-pencil"></i></a>
{{/if}} {{/if}}
<a{{if $group.selected}} class="group-selected"{{/if}} href="{{$group.href}}">{{$group.text}}</a> <a{{if $group.selected}} class="group-selected"{{/if}} href="{{$group.href}}">{{$group.text}}</a>
</li> </li>

View File

@ -1,22 +1,27 @@
<div class="wall-item-outside-wrapper{{$indent}}" id="wall-item-outside-wrapper-{{$id}}" > <div class="wall-item-outside-wrapper {{$indent}}" id="wall-item-outside-wrapper-{{$id}}" >
<div class="wall-item-photo-wrapper" id="wall-item-photo-wrapper-{{$id}}" > <div class="wall-item-content-wrapper {{$indent}}" id="wall-item-content-wrapper-{{$id}}" style="clear:both;">
<a href="{{$profile_url}}" title="View {{$name}}'s profile" class="wall-item-photo-link" id="wall-item-photo-link-{{$id}}"> <div class="wall-item-info" id="wall-item-info-{{$item.id}}" >
<img src="{{$thumb}}" class="wall-item-photo" id="wall-item-photo-{{$id}}" style="height: 80px; width: 80px;" alt="{{$name}}" /></a> <div class="wall-item-photo-wrapper" id="wall-item-photo-wrapper-{{$id}}" >
</div> <a href="{{$profile_url}}" title="View {{$name}}'s profile" class="wall-item-photo-link" id="wall-item-photo-link-{{$id}}">
<img src="{{$thumb}}" class="wall-item-photo" id="wall-item-photo-{{$id}}" style="height: 80px; width: 80px;" alt="{{$name}}" /></a>
</div>
</div>
<div class="wall-item-wrapper" id="wall-item-wrapper-{{$id}}" >
<div class="wall-item-author">
<a href="{{$profile_url}}" title="View {{$name}}'s profile" class="wall-item-name-link"><span class="wall-item-name" id="wall-item-name-{{$id}}" >{{$name}}</span></a>
</div>
<div class="wall-item-ago" id="wall-item-ago-{{$id}}">{{$ago}}</div>
</div>
<div class="wall-item-content" id="wall-item-content-{{$id}}" >
<div class="wall-item-title" id="wall-item-title-{{$id}}">{{$title}}</div>
<div class="wall-item-body" id="wall-item-body-{{$id}}" >{{$body}}</div>
</div>
{{$drop}}
<div class="wall-item-wrapper-end"></div>
<div class="wall-item-wrapper" id="wall-item-wrapper-{{$id}}" > {{$comment}}
<a href="{{$profile_url}}" title="View {{$name}}'s profile" class="wall-item-name-link"><span class="wall-item-name" id="wall-item-name-{{$id}}" >{{$name}}</span></a>
<div class="wall-item-ago" id="wall-item-ago-{{$id}}">{{$ago}}</div>
</div>
<div class="wall-item-content" id="wall-item-content-{{$id}}" >
<div class="wall-item-title" id="wall-item-title-{{$id}}">{{$title}}</div>
<div class="wall-item-body" id="wall-item-body-{{$id}}" >{{$body}}</div>
</div>
{{$drop}}
<div class="wall-item-wrapper-end"></div>
<div class="wall-item-comment-separator"></div>
{{$comment}}
<div class="wall-item-outside-wrapper-end{{$indent}}" ></div> <div class="wall-item-outside-wrapper-end{{$indent}}" ></div>
</div>
</div> </div>

View File

@ -92,6 +92,10 @@
{{$comments}} {{$comments}}
<div class="wall-item-comment-wrapper{{if $comments}} wall-item-comment-wrapper-wc{{/if}}" >
{{$commentbox}}
</div>
</div> </div>
{{if $nextlink}}<div id="photo-next-link"><a href="{{$nextlink.0}}"><i class="icon-forward photo-icons"></i></a></div>{{/if}} {{if $nextlink}}<div id="photo-next-link"><a href="{{$nextlink.0}}"><i class="icon-forward photo-icons"></i></a></div>{{/if}}