1046 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			1046 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | |
| 
 | |
| /** @file
 | |
|  *
 | |
|  * @brief File/attach API with the potential for revision control.
 | |
|  *
 | |
|  * TODO: a filesystem storage abstraction which maintains security (and 'data' contains a system filename
 | |
|  * which is inaccessible from the web). This could get around PHP storage limits and store videos and larger
 | |
|  * items, using fread or OS methods or native code to read/write or chunk it through.
 | |
|  * Also an 'append' option to the storage function might be a useful addition. 
 | |
|  */
 | |
| 
 | |
| require_once('include/permissions.php');
 | |
| require_once('include/security.php');
 | |
| 
 | |
| /**
 | |
|  * @brief Guess the mimetype from file ending.
 | |
|  * 
 | |
|  * This function takes a file name and guess the mimetype from the
 | |
|  * filename extension.
 | |
|  * 
 | |
|  * @param $filename a string filename
 | |
|  * @return string The mimetype according to a file ending.
 | |
|  */
 | |
| function z_mime_content_type($filename) {
 | |
| 
 | |
| 	$mime_types = array(
 | |
| 
 | |
| 	'txt' => 'text/plain',
 | |
| 	'htm' => 'text/html',
 | |
| 	'html' => 'text/html',
 | |
| 	'php' => 'text/html',
 | |
| 	'css' => 'text/css',
 | |
| 	'js' => 'application/javascript',
 | |
| 	'json' => 'application/json',
 | |
| 	'xml' => 'application/xml',
 | |
| 	'swf' => 'application/x-shockwave-flash',
 | |
| 	'flv' => 'video/x-flv',
 | |
| 	'epub' => 'application/epub+zip',
 | |
| 
 | |
| 	// images
 | |
| 	'png' => 'image/png',
 | |
| 	'jpe' => 'image/jpeg',
 | |
| 	'jpeg' => 'image/jpeg',
 | |
| 	'jpg' => 'image/jpeg',
 | |
| 	'gif' => 'image/gif',
 | |
| 	'bmp' => 'image/bmp',
 | |
| 	'ico' => 'image/vnd.microsoft.icon',
 | |
| 	'tiff' => 'image/tiff',
 | |
| 	'tif' => 'image/tiff',
 | |
| 	'svg' => 'image/svg+xml',
 | |
| 	'svgz' => 'image/svg+xml',
 | |
| 
 | |
| 	// archives
 | |
| 	'zip' => 'application/zip',
 | |
| 	'rar' => 'application/x-rar-compressed',
 | |
| 	'exe' => 'application/x-msdownload',
 | |
| 	'msi' => 'application/x-msdownload',
 | |
| 	'cab' => 'application/vnd.ms-cab-compressed',
 | |
| 
 | |
| 	// audio/video
 | |
| 	'mp3' => 'audio/mpeg',
 | |
| 	'wav' => 'audio/wav',
 | |
| 	'qt' => 'video/quicktime',
 | |
| 	'mov' => 'video/quicktime',
 | |
| 	'ogg' => 'application/ogg',
 | |
| 
 | |
| 	// adobe
 | |
| 	'pdf' => 'application/pdf',
 | |
| 	'psd' => 'image/vnd.adobe.photoshop',
 | |
| 	'ai' => 'application/postscript',
 | |
| 	'eps' => 'application/postscript',
 | |
| 	'ps' => 'application/postscript',
 | |
| 
 | |
| 	// ms office
 | |
| 	'doc' => 'application/msword',
 | |
| 	'rtf' => 'application/rtf',
 | |
| 	'xls' => 'application/vnd.ms-excel',
 | |
| 	'ppt' => 'application/vnd.ms-powerpoint',
 | |
| 
 | |
| 	// open office
 | |
| 	'odt' => 'application/vnd.oasis.opendocument.text',
 | |
| 	'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
 | |
| 	);
 | |
| 
 | |
| 	$dot = strpos($filename, '.');
 | |
| 	if ($dot !== false) {
 | |
| 		$ext = strtolower(substr($filename, $dot + 1));
 | |
| 		if (array_key_exists($ext, $mime_types)) {
 | |
| 			return $mime_types[$ext];
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return 'application/octet-stream';
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * @brief Count files/attachments.
 | |
|  * 
 | |
|  * 
 | |
|  * @param $channel_id
 | |
|  * @param $observer
 | |
|  * @param $hash (optional)
 | |
|  * @param $filename (optional)
 | |
|  * @param $filetype (optional)
 | |
|  * @return array
 | |
|  * 	$ret['success'] boolean
 | |
|  * 	$ret['results'] amount of found results, or false
 | |
|  * 	$ret['message'] string with error messages if any
 | |
|  */
 | |
| function attach_count_files($channel_id, $observer, $hash = '', $filename = '', $filetype = '') {
 | |
| 
 | |
| 	$ret = array('success' => false);
 | |
| 
 | |
| 	if(! perm_is_allowed($channel_id,$observer, 'read_storage')) {
 | |
| 		$ret['message'] = t('Permission denied.');
 | |
| 		return $ret;
 | |
| 	}
 | |
| 
 | |
| 	require_once('include/security.php');
 | |
| 	$sql_extra = permissions_sql($channel_id);
 | |
| 
 | |
| 	if($hash)
 | |
| 		$sql_extra .= protect_sprintf(" and hash = '" . dbesc($hash) . "' ");
 | |
| 
 | |
| 	if($filename)
 | |
| 		$sql_extra .= protect_sprintf(" and filename like '@" . dbesc($filename) . "@' ");
 | |
| 
 | |
| 	if($filetype)
 | |
| 		$sql_extra .= protect_sprintf(" and filetype like '@" . dbesc($filetype) . "@' ");
 | |
| 
 | |
| 	$r = q("select id from attach where uid = %d $sql_extra",
 | |
| 		intval($channel_id)
 | |
| 	);
 | |
| 
 | |
| 	$ret['success'] = ((is_array($r)) ? true : false);
 | |
| 	$ret['results'] = ((is_array($r)) ? count($r) : false);
 | |
| 
 | |
| 	return $ret;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * @brief Returns a list of files/attachments.
 | |
|  * 
 | |
|  * @param $channel_id
 | |
|  * @param $observer
 | |
|  * @param $hash (optional)
 | |
|  * @param $filename (optional)
 | |
|  * @param $filetype (optional)
 | |
|  * @param $orderby
 | |
|  * @param $start
 | |
|  * @param $entries
 | |
|  * @return array
 | |
|  * 	$ret['success'] boolean
 | |
|  * 	$ret['results'] array with results, or false
 | |
|  * 	$ret['message'] string with error messages if any
 | |
|  */
 | |
| function attach_list_files($channel_id, $observer, $hash = '', $filename = '', $filetype = '', $orderby = 'created desc', $start = 0, $entries = 0) {
 | |
| 
 | |
| 	$ret = array('success' => false);
 | |
| 
 | |
| 	if(! perm_is_allowed($channel_id,$observer, 'read_storage')) {
 | |
| 		$ret['message'] = t('Permission denied.');
 | |
| 		return $ret;
 | |
| 	}
 | |
| 
 | |
| 	require_once('include/security.php');
 | |
| 	$sql_extra = permissions_sql($channel_id);
 | |
| 
 | |
| 	if($hash)
 | |
| 		$sql_extra .= protect_sprintf(" and hash = '" . dbesc($hash) . "' ");
 | |
| 
 | |
| 	if($filename)
 | |
| 		$sql_extra .= protect_sprintf(" and filename like '@" . dbesc($filename) . "@' ");
 | |
| 
 | |
| 	if($filetype)
 | |
| 		$sql_extra .= protect_sprintf(" and filetype like '@" . dbesc($filetype) . "@' ");
 | |
| 
 | |
| 	if($entries)
 | |
| 		$limit = " limit " . intval($start) . ", " . intval(entries) . " ";
 | |
| 
 | |
| 	// Retrieve all columns except 'data'
 | |
| 
 | |
| 	$r = q("select id, aid, uid, hash, filename, filetype, filesize, revision, folder, flags, created, edited, allow_cid, allow_gid, deny_cid, deny_gid from attach where uid = %d $sql_extra $orderby $limit",
 | |
| 		intval($channel_id)
 | |
| 	);
 | |
| 
 | |
| 	$ret['success'] = ((is_array($r)) ? true : false);
 | |
| 	$ret['results'] = ((is_array($r)) ? $r : false);
 | |
| 
 | |
| 	return $ret;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * @brief Find an attachment by hash and revision.
 | |
|  * 
 | |
|  * Returns the entire attach structure including data.
 | |
|  * 
 | |
|  * This could exhaust memory so most useful only when immediately sending the data.
 | |
|  * 
 | |
|  * @param $hash
 | |
|  * @param $rev
 | |
|  */
 | |
| function attach_by_hash($hash, $rev = 0) {
 | |
| 
 | |
| 	$ret = array('success' => false);
 | |
| 
 | |
| 	// Check for existence, which will also provide us the owner uid
 | |
| 
 | |
| 	$sql_extra = '';
 | |
| 	if($rev == (-1))
 | |
| 		$sql_extra = " order by revision desc ";
 | |
| 	elseif($rev)
 | |
| 		$sql_extra = " and revision = " . intval($rev) . " ";
 | |
| 
 | |
| 
 | |
| 	$r = q("SELECT uid FROM attach WHERE hash = '%s' $sql_extra LIMIT 1",
 | |
| 		dbesc($hash)
 | |
| 	);
 | |
| 	if(! $r) {
 | |
| 		$ret['message'] = t('Item was not found.');
 | |
| 		return $ret;
 | |
| 	}
 | |
| 
 | |
| 	if(! perm_is_allowed($r[0]['uid'], get_observer_hash(), 'view_storage')) {
 | |
| 		$ret['message'] = t('Permission denied.');
 | |
| 		return $ret;
 | |
| 	}
 | |
| 
 | |
| 	$sql_extra = permissions_sql($r[0]['uid']);
 | |
| 
 | |
| 	// Now we'll see if we can access the attachment
 | |
| 
 | |
| 	$r = q("SELECT * FROM attach WHERE hash = '%s' and uid = %d $sql_extra LIMIT 1",
 | |
| 		dbesc($hash),
 | |
| 		intval($r[0]['uid'])
 | |
| 	);
 | |
| 
 | |
| 	if(! $r) {
 | |
| 		$ret['message'] = t('Permission denied.');
 | |
| 		return $ret;
 | |
| 	}
 | |
| 
 | |
| 	$ret['success'] = true;
 | |
| 	$ret['data'] = $r[0];
 | |
| 
 | |
| 	return $ret;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * @brief Find an attachment by hash and revision.
 | |
|  * 
 | |
|  * Returns the entire attach structure excluding data.
 | |
|  * 
 | |
|  * @see attach_by_hash()
 | |
|  * @param $hash
 | |
|  * @param $ref
 | |
|  */
 | |
| function attach_by_hash_nodata($hash, $rev = 0) {
 | |
| 
 | |
| 	$ret = array('success' => false);
 | |
| 
 | |
| 	// Check for existence, which will also provide us the owner uid
 | |
| 
 | |
| 	$sql_extra = '';
 | |
| 	if($rev == (-1))
 | |
| 		$sql_extra = " order by revision desc ";
 | |
| 	elseif($rev)
 | |
| 		$sql_extra = " and revision = " . intval($rev) . " ";
 | |
| 
 | |
| 	$r = q("SELECT uid FROM attach WHERE hash = '%s' $sql_extra LIMIT 1",
 | |
| 		dbesc($hash)
 | |
| 	);
 | |
| 	if(! $r) {
 | |
| 		$ret['message'] = t('Item was not found.');
 | |
| 		return $ret;
 | |
| 	}
 | |
| 
 | |
| 	if(! perm_is_allowed($r[0]['uid'],get_observer_hash(),'view_storage')) {
 | |
| 		$ret['message'] = t('Permission denied.');
 | |
| 		return $ret;
 | |
| 	}
 | |
| 
 | |
| 	$sql_extra = permissions_sql($r[0]['uid']);
 | |
| 
 | |
| 	// Now we'll see if we can access the attachment
 | |
| 
 | |
| 	$r = q("select id, aid, uid, hash, creator, filename, filetype, filesize, revision, folder, flags, created, edited, allow_cid, allow_gid, deny_cid, deny_gid from attach where uid = %d and hash = '%s' $sql_extra limit 1",
 | |
| 		intval($r[0]['uid']),
 | |
| 		dbesc($hash)
 | |
| 	);
 | |
| 
 | |
| 	if(! $r) {
 | |
| 		$ret['message'] = t('Permission denied.');
 | |
| 		return $ret;
 | |
| 	}
 | |
| 
 | |
| 	$ret['success'] = true;
 | |
| 	$ret['data'] = $r[0];
 | |
| 	return $ret;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * @brief 
 | |
|  *
 | |
|  * @param $channel channel array of owner
 | |
|  * @param $observer_hash hash of current observer
 | |
|  * @param $options (optional)
 | |
|  * @param $arr (optional)
 | |
|  */
 | |
| function attach_store($channel, $observer_hash, $options = '', $arr = null) {
 | |
| 
 | |
| 	$ret = array('success' => false);
 | |
| 	$channel_id = $channel['channel_id'];
 | |
| 	$sql_options = '';
 | |
| 
 | |
| 	if(! perm_is_allowed($channel_id,get_observer_hash(), 'write_storage')) {
 | |
| 		$ret['message'] = t('Permission denied.');
 | |
| 		return $ret;
 | |
| 	}
 | |
| 
 | |
| 	// The 'update' option sets db values without uploading a new attachment
 | |
| 	// 'replace' replaces the existing uploaded data
 | |
| 	// 'revision' creates a new revision with new upload data
 | |
| 	// Default is to upload a new file
 | |
| 
 | |
| 	// revise or update must provide $arr['hash'] of the thing to revise/update
 | |
| 
 | |
| 	if($options !== 'update') {
 | |
| 		if(! x($_FILES,'userfile')) {
 | |
| 			$ret['message'] = t('No source file.');
 | |
| 			return $ret;
 | |
| 		}
 | |
| 
 | |
| 		$src      = $_FILES['userfile']['tmp_name'];
 | |
| 		$filename = basename($_FILES['userfile']['name']);
 | |
| 		$filesize = intval($_FILES['userfile']['size']);
 | |
| 	}
 | |
| 
 | |
| 	$existing_size = 0;
 | |
| 
 | |
| 	if($options === 'replace') {
 | |
| 		$x = q("select id, hash, filesize from attach where id = %d and uid = %d limit 1",	
 | |
| 			intval($replace),
 | |
| 			intval($channel_id)
 | |
| 		);
 | |
| 		if(! $x) {
 | |
| 			$ret['message'] = t('Cannot locate file to replace');
 | |
| 			return $ret;
 | |
| 		}
 | |
| 		$existing_id = $x[0]['id'];
 | |
| 		$existing_size = intval($x[0]['filesize']);
 | |
| 		$hash = $x[0]['hash'];
 | |
| 	}
 | |
| 	
 | |
| 	if($options === 'revise' || $options === 'update') {
 | |
| 		$sql_options = " order by revision desc ";
 | |
| 		if($options === 'update' &&  $arr && array_key_exists('revision',$arr))
 | |
| 			$sql_options = " and revision = " . intval($arr['revision']) . " ";
 | |
| 
 | |
| 		$x = q("select id, aid, uid, filename, filetype, filesize, hash, revision, folder, flags, created, edited, allow_cid, allow_gid, deny_cid, deny_gid from attach where hash = '%s' and uid = %d $sql_options limit 1",
 | |
| 			dbesc($arr['hash']),
 | |
| 			intval($channel_id)
 | |
| 		);
 | |
| 		if(! $x) {
 | |
| 			$ret['message'] = t('Cannot locate file to revise/update');
 | |
| 			return $ret;
 | |
| 		}
 | |
| 		$hash = $x[0]['hash'];
 | |
| 	}
 | |
| 
 | |
| 	// Check storage limits
 | |
| 	if($options !== 'update') {
 | |
| 		$maxfilesize = get_config('system','maxfilesize');
 | |
| 
 | |
| 		if(($maxfilesize) && ($filesize > $maxfilesize)) {
 | |
| 			$ret['message'] = sprintf( t('File exceeds size limit of %d'), $maxfilesize);
 | |
| 			@unlink($src);
 | |
| 			return $ret;
 | |
| 		}
 | |
| 
 | |
| 		$limit = service_class_fetch($channel_id, 'attach_upload_limit');
 | |
| 
 | |
| 		if($limit !== false) {
 | |
| 			$r = q("select sum(filesize) as total from attach where aid = %d ",
 | |
| 				intval($channel['channel_account_id'])
 | |
| 			);
 | |
| 			if(($r) &&  (($r[0]['total'] + $filesize) > ($limit - $existing_size))) {
 | |
| 				$ret['message'] = upgrade_message(true) . sprintf(t("You have reached your limit of %1$.0f Mbytes attachment storage."), $limit / 1024000);
 | |
| 				@unlink($src);
 | |
| 				return $ret;
 | |
| 			}
 | |
| 		}
 | |
| 		$mimetype = z_mime_content_type($filename);
 | |
| 	}
 | |
| 
 | |
| 	if(! isset($hash))
 | |
| 		$hash = random_string();
 | |
| 
 | |
| 	$created = datetime_convert();
 | |
| 
 | |
| 	if($options === 'replace') {
 | |
| 		$r = q("update attach set filename = '%s', filetype = '%s', filesize = %d, data = '%s', edited = '%s' where id = %d and uid = %d",
 | |
| 			dbesc($filename),
 | |
| 			dbesc($mimetype),
 | |
| 			intval($filesize),
 | |
| 			dbescbin(@file_get_contents($src)),
 | |
| 			dbesc($created),
 | |
| 			intval($existing_id),
 | |
| 			intval($channel_id)
 | |
| 		);
 | |
| 	}
 | |
| 	elseif($options === 'revise') {
 | |
| 		$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' ) ",
 | |
| 			intval($x[0]['aid']),
 | |
| 			intval($channel_id),
 | |
| 			dbesc($x[0]['hash']),
 | |
| 			dbesc(get_observer_hash()),
 | |
| 			dbesc($filename),
 | |
| 			dbesc($mimetype),
 | |
| 			intval($filesize),
 | |
| 			intval($x[0]['revision'] + 1),
 | |
| 			dbescbin(@file_get_contents($src)),
 | |
| 			dbesc($created),
 | |
| 			dbesc($created),
 | |
| 			dbesc($x[0]['allow_cid']),
 | |
| 			dbesc($x[0]['allow_gid']),
 | |
| 			dbesc($x[0]['deny_cid']),
 | |
| 			dbesc($x[0]['deny_gid'])
 | |
| 		);
 | |
| 	}
 | |
| 	elseif($options === 'update') {
 | |
| 		$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",
 | |
| 			dbesc((array_key_exists('filename',$arr))  ? $arr['filename']  : $x[0]['filename']),
 | |
| 			dbesc((array_key_exists('filetype',$arr))  ? $arr['filetype']  : $x[0]['filetype']),
 | |
| 			dbesc($created),
 | |
| 			dbesc((array_key_exists('allow_cid',$arr)) ? $arr['allow_cid'] : $x[0]['allow_cid']),
 | |
| 			dbesc((array_key_exists('allow_gid',$arr)) ? $arr['allow_gid'] : $x[0]['allow_gid']),
 | |
| 			dbesc((array_key_exists('deny_cid',$arr))  ? $arr['deny_cid']  : $x[0]['deny_cid']),
 | |
| 			dbesc((array_key_exists('deny_gid',$arr))  ? $arr['deny_gid']  : $x[0]['deny_gid']),
 | |
| 			intval($x[0]['id']),
 | |
| 			intval($x[0]['uid'])
 | |
| 		);
 | |
| 	}
 | |
| 	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 )
 | |
| 			VALUES ( %d, %d, '%s', '%s', '%s', '%s', %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s' ) ",
 | |
| 			intval($channel['channel_account_id']),
 | |
| 			intval($channel_id),
 | |
| 			dbesc($hash),
 | |
| 			dbesc(get_observer_hash()),
 | |
| 			dbesc($filename),
 | |
| 			dbesc($mimetype),
 | |
| 			intval($filesize),
 | |
| 			intval(0),
 | |
| 			dbescbin(@file_get_contents($src)),
 | |
| 			dbesc($created),
 | |
| 			dbesc($created),
 | |
| 			dbesc(($arr && array_key_exists('allow_cid',$arr)) ? $arr['allow_cid'] : '<' . $channel['channel_hash'] . '>'),
 | |
| 			dbesc(($arr && array_key_exists('allow_gid',$arr)) ? $arr['allow_gid'] : ''),
 | |
| 			dbesc(($arr && array_key_exists('deny_cid',$arr))  ? $arr['deny_cid']  : ''),
 | |
| 			dbesc(($arr && array_key_exists('deny_gid',$arr))  ? $arr['deny_gid']  : '')
 | |
| 		);
 | |
| 	}
 | |
| 
 | |
| 	if($options !== 'update')
 | |
| 		@unlink($src);
 | |
| 
 | |
| 	if(! $r) {
 | |
| 		$ret['message'] = t('File upload failed. Possible system limit or action terminated.');
 | |
| 		return $ret;
 | |
| 	}
 | |
| 
 | |
| 	// Caution: This re-uses $sql_options set further above
 | |
| 
 | |
| 	$r = q("select id, aid, uid, hash, creator, filename, filetype, filesize, revision, folder, flags, created, edited, allow_cid, allow_gid, deny_cid, deny_gid from attach where uid = %d and hash = '%s' $sql_options limit 1",
 | |
| 		intval($channel_id),
 | |
| 		dbesc($hash)
 | |
| 	);
 | |
| 
 | |
| 	if(! $r) {
 | |
| 		$ret['message'] = t('Stored file could not be verified. Upload failed.');
 | |
| 		return $ret;
 | |
| 	}
 | |
| 
 | |
| 	$ret['success'] = true;
 | |
| 	$ret['data'] = $r[0];
 | |
| 
 | |
| 	return $ret;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Read a virtual directory and return contents, checking permissions of all parent components.
 | |
|  * @function z_readdir
 | |
|  * @param integer $channel_id
 | |
|  * @param string $observer_hash hash of current observer
 | |
|  * @param string $pathname
 | |
|  * @param string $parent_hash (optional)
 | |
|  *
 | |
|  * @returns array $ret
 | |
|  * $ret['success'] = boolean true or false
 | |
|  * $ret['message'] = error message if success is false
 | |
|  * $ret['data'] = array of attach DB entries without data component
 | |
|  */
 | |
| function z_readdir($channel_id, $observer_hash, $pathname, $parent_hash = '') {
 | |
| 	$ret = array('success' => false);
 | |
| 
 | |
| 	if(! perm_is_allowed($r[0]['uid'], get_observer_hash(), 'view_storage')) {
 | |
| 		$ret['message'] = t('Permission denied.');
 | |
| 		return $ret;
 | |
| 	}
 | |
| 
 | |
| 	if(strpos($pathname, '/')) {
 | |
| 		$paths = explode('/', $pathname);
 | |
| 		if(count($paths) > 1) {
 | |
| 			$curpath = array_shift($paths);
 | |
| 
 | |
| 			$r = q("select hash, id from attach where uid = %d and filename = '%s' and (flags & %d )>0 " . permissions_sql($channel_id) . " limit 1",
 | |
| 				intval($channel_id),
 | |
| 				dbesc($curpath),
 | |
| 				intval(ATTACH_FLAG_DIR)
 | |
| 			);
 | |
| 			if(! $r) {
 | |
| 				$ret['message'] = t('Path not available.');		
 | |
| 				return $ret;
 | |
| 			}
 | |
| 
 | |
| 			return z_readdir($channel_id, $observer_hash, implode('/', $paths), $r[0]['hash']);
 | |
| 		}
 | |
| 	}
 | |
| 	else
 | |
| 		$paths = array($pathname);
 | |
| 	
 | |
| 	$r = q("select id, aid, uid, hash, creator, filename, filetype, filesize, revision, folder, flags, created, edited, allow_cid, allow_gid, deny_cid, deny_gid from attach where id = %d and folder = '%s' and filename = '%s' and (flags & %d )>0 " . permissions_sql($channel_id),
 | |
| 		intval($channel_id),
 | |
| 		dbesc($parent_hash),
 | |
| 		dbesc($paths[0]),
 | |
| 		intval(ATTACH_FLAG_DIR)
 | |
| 	);
 | |
| 	if(! $r) {
 | |
| 		$ret['message'] = t('Path not available.');
 | |
| 		return $ret;
 | |
| 	}
 | |
| 	$ret['success'] = true;
 | |
| 	$ret['data'] = $r;
 | |
| 
 | |
| 	return $ret;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * @function attach_mkdir($channel,$observer_hash,$arr);
 | |
|  *
 | |
|  * @brief Create directory.
 | |
|  *
 | |
|  * @param array $channel channel array of owner
 | |
|  * @param string $observer_hash hash of current observer
 | |
|  * @param array $arr parameter array to fulfil request
 | |
|  * Required:
 | |
|  *    $arr['filename']
 | |
|  *    $arr['folder'] // hash of parent directory, empty string for root directory
 | |
|  * Optional:
 | |
|  *    $arr['hash']  // precumputed hash for this node
 | |
|  *    $arr['allow_cid']
 | |
|  *    $arr['allow_gid']
 | |
|  *    $arr['deny_cid']
 | |
|  *    $arr['deny_gid']
 | |
|  */
 | |
| 
 | |
| function attach_mkdir($channel, $observer_hash, $arr = null) {
 | |
| 
 | |
| 	$ret = array('success' => false);
 | |
| 	$channel_id = $channel['channel_id'];
 | |
| 	$sql_options = '';
 | |
| 
 | |
| 	$basepath = 'store/' . $channel['channel_address'];
 | |
| 
 | |
| 	logger('attach_mkdir: basepath: ' . $basepath);
 | |
| 
 | |
| 	if(! is_dir($basepath))
 | |
| 		os_mkdir($basepath,STORAGE_DEFAULT_PERMISSIONS, true);
 | |
| 
 | |
| 	if(! perm_is_allowed($channel_id, $observer_hash, 'write_storage')) {
 | |
| 		$ret['message'] = t('Permission denied.');
 | |
| 		return $ret;
 | |
| 	}
 | |
| 
 | |
| 	if(! $arr['filename']) {
 | |
| 		$ret['message'] = t('Empty pathname');
 | |
| 		return $ret;
 | |
| 	}
 | |
| 
 | |
| 	$arr['hash'] = (($arr['hash']) ? $arr['hash'] : random_string());
 | |
| 
 | |
| 	// Check for duplicate name.
 | |
| 	// Check both the filename and the hash as we will be making use of both.
 | |
| 	
 | |
| 	$r = q("select hash from attach where ( filename = '%s' or hash = '%s' ) and folder = '%s' and uid = %d limit 1",
 | |
| 		dbesc($arr['filename']),
 | |
| 		dbesc($arr['hash']),
 | |
| 		dbesc($arr['folder']),
 | |
| 		intval($channel['channel_id'])
 | |
| 	);
 | |
| 	if($r) {
 | |
| 		$ret['message'] = t('duplicate filename or path');
 | |
| 		return $ret;
 | |
| 	}
 | |
| 
 | |
| 	if($arr['folder']) {
 | |
| 
 | |
| 		// Walk the directory tree from parent back to root to make sure the parent is valid and name is unique and we
 | |
| 		// have permission to see this path. This implies the root directory itself is public since we won't have permissions
 | |
| 		// set on the psuedo-directory. We can however set permissions for anything and everything contained within it.
 | |
| 
 | |
| 		$lpath = '';
 | |
| 		$lfile = $arr['folder'];
 | |
| 		$sql_options = permissions_sql($channel['channel_id']);
 | |
| 
 | |
| 		do {
 | |
| 			$r = q("select filename, hash, flags, folder from attach where uid = %d and hash = '%s' and ( flags & %d )>0 
 | |
| 				$sql_options limit 1",
 | |
| 				intval($channel['channel_id']),
 | |
| 				dbesc($lfile),
 | |
| 				intval(ATTACH_FLAG_DIR)
 | |
| 			);
 | |
| 
 | |
| 			if(! $r) {
 | |
| 				logger('attach_mkdir: hash ' . $lfile . ' not found in ' . $lpath);
 | |
| 				$ret['message'] = t('Path not found.');
 | |
| 				return $ret;
 | |
| 			}
 | |
| 			if($lfile)
 | |
| 				$lpath = $r[0]['hash'] . '/' . $lpath;
 | |
| 			$lfile = $r[0]['folder'];
 | |
| 		} while ( ($r[0]['folder']) && ($r[0]['flags'] & ATTACH_FLAG_DIR)) ;
 | |
| 		$path = $basepath . '/' . $lpath;			
 | |
| 	}
 | |
| 	else
 | |
| 		$path = $basepath . '/';
 | |
| 
 | |
| 	$path .= $arr['hash'];
 | |
| 
 | |
| 	$created = datetime_convert();
 | |
| 
 | |
| 	$r = q("INSERT INTO attach ( aid, uid, hash, creator, filename, filetype, filesize, revision, folder, flags, data, created, edited, allow_cid, allow_gid, deny_cid, deny_gid )
 | |
| 		VALUES ( %d, %d, '%s', '%s', '%s', '%s', %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s' ) ",
 | |
| 		intval($channel['channel_account_id']),
 | |
| 		intval($channel_id),
 | |
| 		dbesc($arr['hash']),
 | |
| 		dbesc(get_observer_hash()),
 | |
| 		dbesc($arr['filename']),
 | |
| 		dbesc('multipart/mixed'),
 | |
| 		intval(0),
 | |
| 		intval(0),
 | |
| 		dbesc($arr['folder']),
 | |
| 		intval(ATTACH_FLAG_DIR|ATTACH_FLAG_OS),
 | |
| 		dbesc($path),
 | |
| 		dbesc($created),
 | |
| 		dbesc($created),
 | |
| 		dbesc(($arr && array_key_exists('allow_cid',$arr)) ? $arr['allow_cid'] : $channel['channel_allow_cid']),
 | |
| 		dbesc(($arr && array_key_exists('allow_gid',$arr)) ? $arr['allow_gid'] : $channel['channel_allow_gid']),
 | |
| 		dbesc(($arr && array_key_exists('deny_cid',$arr))  ? $arr['deny_cid']  : $channel['channel_deny_cid']),
 | |
| 		dbesc(($arr && array_key_exists('deny_gid',$arr))  ? $arr['deny_gid']  : $channel['channel_deny_gid'])
 | |
| 	);
 | |
| 
 | |
| 	if($r) {
 | |
| 		if(os_mkdir($path, STORAGE_DEFAULT_PERMISSIONS, true)) {
 | |
| 			$ret['success'] = true;
 | |
| 			$ret['data'] = $arr;
 | |
| 
 | |
| 			// update the parent folder's lastmodified timestamp
 | |
| 			$e = q("UPDATE attach SET edited = '%s' WHERE hash = '%s' AND uid = %d",
 | |
| 				dbesc($created),
 | |
| 				dbesc($arr['folder']),
 | |
| 				intval($channel_id)
 | |
| 			);
 | |
| 		}
 | |
| 		else {
 | |
| 			logger('attach_mkdir: ' . mkdir . ' ' . $path . 'failed.');
 | |
| 			$ret['message'] = t('mkdir failed.');
 | |
| 		}
 | |
| 	}
 | |
| 	else {
 | |
| 		$ret['message'] = t('database storage failed.');
 | |
| 	}
 | |
| 
 | |
| 	return $ret;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * @brief Changes permissions of a file.
 | |
|  * 
 | |
|  * @param $channel_id
 | |
|  * @param $resource
 | |
|  * @param $allow_cid
 | |
|  * @param $allow_gid
 | |
|  * @param $deny_cid
 | |
|  * @param $deny_gid
 | |
|  * @param $recurse
 | |
|  */
 | |
| function attach_change_permissions($channel_id, $resource, $allow_cid, $allow_gid, $deny_cid, $deny_gid, $recurse = false) {
 | |
| 
 | |
| 	$r = q("select hash, flags from attach where hash = '%s' and uid = %d limit 1",
 | |
| 		dbesc($resource),
 | |
| 		intval($channel_id)
 | |
| 	);
 | |
| 
 | |
| 	if(! $r)
 | |
| 		return;
 | |
| 
 | |
| 	if($r[0]['flags'] & ATTACH_FLAG_DIR) {
 | |
| 		if($recurse) {
 | |
| 			$r = q("select hash, flags from attach where folder = '%s' and uid = %d",
 | |
| 				dbesc($resource),
 | |
| 				intval($channel_id)
 | |
| 			);
 | |
| 			if($r) {
 | |
| 				foreach($r as $rr) {
 | |
| 					attach_change_permissions($channel_id, $rr['hash'], $allow_cid, $allow_gid, $deny_cid, $deny_gid, $recurse);
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	$x = q("update attach set allow_cid = '%s', allow_gid = '%s', deny_cid = '%s', deny_gid = '%s' where hash = '%s' and uid = %d",
 | |
| 		dbesc($allow_cid),
 | |
| 		dbesc($allow_gid),
 | |
| 		dbesc($deny_cid),
 | |
| 		dbesc($deny_gid),
 | |
| 		dbesc($resource),
 | |
| 		intval($channel_id)
 | |
| 	);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * @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
 | |
|  *  The id of the channel
 | |
|  * @param string $resource
 | |
|  *  The hash to delete
 | |
|  * @return void
 | |
|  */
 | |
| function attach_delete($channel_id, $resource) {
 | |
| 
 | |
| 	$c = q("SELECT channel_address FROM channel WHERE channel_id = %d LIMIT 1",
 | |
| 		intval($channel_id)
 | |
| 	);
 | |
| 
 | |
| 	$channel_address = (($c) ? $c[0]['channel_address'] : 'notfound');
 | |
| 
 | |
| 	$r = q("SELECT hash, filename, flags, folder FROM attach WHERE hash = '%s' AND uid = %d limit 1",
 | |
| 		dbesc($resource),
 | |
| 		intval($channel_id)
 | |
| 	);
 | |
| 
 | |
| 
 | |
| 	if(! $r)
 | |
| 		return;
 | |
| 
 | |
| 	$url = get_parent_cloudpath($channel_id, $channel_address, $resource) . $r[0]['filename'];
 | |
| 
 | |
| 	// If resource is a directory delete everything in the directory recursive
 | |
| 	if($r[0]['flags'] & ATTACH_FLAG_DIR) {
 | |
| 		$x = q("SELECT hash, flags FROM attach WHERE folder = '%s' AND uid = %d",
 | |
| 			dbesc($resource),
 | |
| 			intval($channel_id)
 | |
| 		);
 | |
| 		if($x) {
 | |
| 			foreach($x as $xx) {
 | |
| 				attach_delete($channel_id, $xx['hash']);
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// delete a file from filesystem
 | |
| 	if($r[0]['flags'] & ATTACH_FLAG_OS) {
 | |
| 		$y = q("SELECT data FROM attach WHERE hash = '%s' AND uid = %d LIMIT 1",
 | |
| 			dbesc($resource),
 | |
| 			intval($channel_id)
 | |
| 		);
 | |
| 
 | |
| 		if($y) {
 | |
| 			$f = 'store/' . $channel_address . '/' . $y[0]['data'];
 | |
| 			if(is_dir($f))
 | |
| 				@rmdir($f);
 | |
| 			elseif(file_exists($f))
 | |
| 				unlink($f);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// delete from database
 | |
| 	$z = q("DELETE FROM attach WHERE hash = '%s' AND uid = %d",
 | |
| 		dbesc($resource),
 | |
| 		intval($channel_id)
 | |
| 	);
 | |
| 
 | |
| 	// update the parent folder's lastmodified timestamp
 | |
| 	$e = q("UPDATE attach SET edited = '%s' WHERE hash = '%s' AND uid = %d",
 | |
| 		dbesc(datetime_convert()),
 | |
| 		dbesc($r[0]['folder']),
 | |
| 		intval($channel_id)
 | |
| 	);
 | |
| 
 | |
| 	file_activity($channel_id, $resource, $allow_cid='', $allow_gid='', $deny_cid='', $deny_gid='', $url, 'drop', $no_activity=false);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * @brief Returns path to file in cloud/.
 | |
|  * This function cannot be used with mod/dav as it always returns a path valid under mod/cloud
 | |
|  * 
 | |
|  * @param array
 | |
|  *  $arr[uid] int the channels uid
 | |
|  *  $arr[folder] string
 | |
|  *  $arr[filename]] string
 | |
|  * @return string
 | |
|  *  path to the file in cloud/
 | |
|  */
 | |
| function get_cloudpath($arr) {
 | |
| 	$basepath = 'cloud/';
 | |
| 
 | |
| 	if($arr['uid']) {
 | |
| 		$r = q("select channel_address from channel where channel_id = %d limit 1",
 | |
| 			intval($arr['uid'])
 | |
| 		);
 | |
| 		if($r)
 | |
| 			$basepath .= $r[0]['channel_address'] . '/';
 | |
| 	}
 | |
| 
 | |
| 	$path = $basepath;
 | |
| 
 | |
| 	if($arr['folder']) {
 | |
| 		$lpath = '';
 | |
| 		$lfile = $arr['folder'];
 | |
| 
 | |
| 		do {
 | |
| 			$r = q("select filename, hash, flags, folder from attach where uid = %d and hash = '%s' and ( flags & %d )>0 
 | |
| 				limit 1",
 | |
| 				intval($arr['uid']),
 | |
| 				dbesc($lfile),
 | |
| 				intval(ATTACH_FLAG_DIR)
 | |
| 			);
 | |
| 
 | |
| 			if(! $r)
 | |
| 				break;
 | |
| 
 | |
| 			if($lfile)
 | |
| 				$lpath = $r[0]['filename'] . '/' . $lpath;
 | |
| 			$lfile = $r[0]['folder'];
 | |
| 
 | |
| 		} while ( ($r[0]['folder']) && ($r[0]['flags'] & ATTACH_FLAG_DIR));
 | |
| 
 | |
| 		$path .= $lpath;
 | |
| 	}
 | |
| 	$path .= $arr['filename'];
 | |
| 
 | |
| 	return $path;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * @brief Returns path to parent folder in cloud/.
 | |
|  * This function cannot be used with mod/dav as it always returns a path valid under mod/cloud
 | |
|  *
 | |
|  * @param int $channel_id
 | |
|  *  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) {
 | |
| 	// build directory tree
 | |
| 	$parentHash = $attachHash;
 | |
| 	do {
 | |
| 		$parentHash = find_folder_hash_by_attach_hash($channel_id, $parentHash);
 | |
| 		if ($parentHash) {
 | |
| 			$parentName = find_filename_by_hash($channel_id, $parentHash);
 | |
| 			$parentFullPath = $parentName . '/' . $parentFullPath;
 | |
| 		}
 | |
| 	} while ($parentHash);
 | |
| 	$parentFullPath = z_root() . '/cloud/' . $channel_name . '/' . $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) {
 | |
| 	$r = q("SELECT folder FROM attach WHERE uid = %d AND hash = '%s' LIMIT 1",
 | |
| 		intval($channel_id),
 | |
| 		dbesc($attachHash)
 | |
| 	);
 | |
| 	$hash = '';
 | |
| 	if ($r) {
 | |
| 		$hash = $r[0]['folder'];
 | |
| 	}
 | |
| 	return $hash;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * @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;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * 
 | |
|  * @param $in
 | |
|  * @param $out
 | |
|  */
 | |
| function pipe_streams($in, $out) {
 | |
| 	$size = 0;
 | |
| 	while (!feof($in))
 | |
| 		$size += fwrite($out, fread($in, 8192));
 | |
| 	return $size;
 | |
| }
 | |
| 
 | |
| function file_activity($channel_id, $hash, $allow_cid, $allow_gid, $deny_cid, $deny_gid, $url, $action, $no_activity) {
 | |
| 
 | |
| 	require_once('include/items.php');
 | |
| 
 | |
| 	$url = rawurlencode($url);
 | |
| 
 | |
| 	$poster = get_app()->get_observer();
 | |
| 
 | |
| 	$verb = ACTIVITY_FILE . '/' . $action . '/' . $hash;
 | |
| 
 | |
| 	$mid = item_message_id();
 | |
| 
 | |
| 	$item_flags = ITEM_WALL|ITEM_ORIGIN|ITEM_UNSEEN;
 | |
| 
 | |
| 	if($action == 'post') {
 | |
| 		//check if activity item exists
 | |
| 		//if yes send drop activity and create a new one
 | |
| 
 | |
| 		$r = q("SELECT * FROM item WHERE verb = '%s'",
 | |
| 			dbesc($verb)
 | |
| 		);
 | |
| 
 | |
| 		if($r) {
 | |
| 
 | |
| 			$dmid = item_message_id();
 | |
| 			$updateverb = ACTIVITY_FILE . '/drop/' . $hash . '#' . $mid;
 | |
| 
 | |
| 			$arr = array();
 | |
| 
 | |
| 			$arr['aid']           = get_account_id();
 | |
| 			$arr['uid']           = $channel_id;
 | |
| 			$arr['mid']           = $dmid;
 | |
| 			$arr['parent_mid']    = $dmid;
 | |
| 			$arr['item_flags']    = $item_flags;
 | |
| 			$arr['author_xchan']  = $poster['xchan_hash'];
 | |
| 			$arr['owner_xchan']   = $poster['xchan_hash'];
 | |
| 			$arr['title']         = '';
 | |
| 			$arr['allow_cid']     = $allow_cid;
 | |
| 			$arr['allow_gid']     = $allow_gid;
 | |
| 			$arr['deny_cid']      = $deny_cid;
 | |
| 			$arr['deny_gid']      = $deny_gid;
 | |
| 			$arr['item_restrict']  = ITEM_HIDDEN;
 | |
| 			$arr['item_private']  = 0;
 | |
| 			$arr['verb']          = $updateverb;
 | |
| 			$arr['body']          = $url;
 | |
| 
 | |
| 			$post = item_store($arr);
 | |
| 			$item_id = $post['item_id'];
 | |
| 
 | |
| 			if($item_id) {
 | |
| 				proc_run('php',"include/notifier.php","activity",$item_id);
 | |
| 			}
 | |
| 
 | |
| 			//call_hooks('post_local_end', $arr);
 | |
| 
 | |
| 			//notice( t('File activity updated') . EOL);
 | |
| 
 | |
| 			if($no_activity) {
 | |
| 				return;
 | |
| 			}
 | |
| 
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if($no_activity) {
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	$arr = array();
 | |
| 
 | |
| 	$arr['aid']           = get_account_id();
 | |
| 	$arr['uid']           = $channel_id;
 | |
| 	$arr['mid']           = $mid;
 | |
| 	$arr['parent_mid']    = $mid;
 | |
| 	$arr['item_flags']    = $item_flags;
 | |
| 	$arr['author_xchan']  = $poster['xchan_hash'];
 | |
| 	$arr['owner_xchan']   = $poster['xchan_hash'];
 | |
| 	$arr['title']         = '';
 | |
| 	$arr['allow_cid']     = $allow_cid;
 | |
| 	$arr['allow_gid']     = $allow_gid;
 | |
| 	$arr['deny_cid']      = $deny_cid;
 | |
| 	$arr['deny_gid']      = $deny_gid;
 | |
| 	$arr['item_restrict']  = ITEM_HIDDEN;
 | |
| 	$arr['item_private']  = 0;
 | |
| 	$arr['verb']          = $verb;
 | |
| 	$arr['body']          = $url;
 | |
| 
 | |
| 	$post = item_store($arr);
 | |
| 	$item_id = $post['item_id'];
 | |
| 
 | |
| 	if($item_id) {
 | |
| 		proc_run('php',"include/notifier.php","activity",$item_id);
 | |
| 	}
 | |
| 
 | |
| 	//call_hooks('post_local_end', $arr);
 | |
| 
 | |
| 	//(($action === 'post') ?  notice( t('File activity posted') . EOL) : notice( t('File activity dropped') . EOL));
 | |
| 
 | |
| 	return;
 | |
| 
 | |
| }
 |