<?php
if (!defined('VB_ENTRY')) die('Access denied.');
/*======================================================================*\
   || #################################################################### ||
   || # vBulletin 5.0.0 Beta 10
   || # ---------------------------------------------------------------- # ||
   || # Copyright �2000-2012 vBulletin Solutions Inc. All Rights Reserved. ||
   || # This file may not be redistributed in whole or significant part. # ||
   || # ---------------- VBULLETIN IS NOT FREE SOFTWARE ---------------- # ||
   || # http://www.vbulletin.com | http://www.vbulletin.com/license.html # ||
   || #################################################################### ||
   \*======================================================================*/


/**
 * vB_Api_Content_Attach
 *
 * @package vBApi
 * @access public
 */
class vB_Api_Content_Attach extends vB_Api_Content
{
	protected $types;

	protected $extension_map;

	//override in client- the text name
	protected $contenttype = 'vBForum_Attach';

	//The table for the type-specific data.
	protected $tablename = 'attach';

	//Control whether this record will display on a channel page listing.
	protected $inlist = 0;

	//Image processing functions
	protected $imageHandler;

	protected function __construct()
	{
		parent::__construct();
		$this->library = vB_Library::instance('Content_Attach');

		$this->imageHandler = vB_Image::instance();
	}


	/*** This validates that a user can upload attachments. Currently that's just verifying that they are logged in.
	 *
	 *
	***/
	protected function checkPermission($userid)
	{
		if (!intval($userid))
		{
			if (isset($_FILES['tmp_name']))
			{
					unlink($_FILES['tmp_name']);
			}

			throw new Exception('please_login_first');
		}
	}


	/** fetch image information about an attachment
	 *
	 * 	@param 	int		node id
	 * 	@param	bool	thumbnail version requested?
	 * 	@param	bool	should we include the image content
	 *
	 *	@return	mixed	array of data, includes filesize, dateline, htmltype, filename, extension, and filedataid

	 **/
	public function fetchImage($id, $thumb = false, $includeData = true)
	{
		if (empty($id) OR !intval($id))
		{
			throw new vB_Exception_Api('invalid_request');
		}

		$userContext = vB::getUserContext();

		if (!$userContext->getChannelPermission('forumpermissions', 'cangetattachment', $id))
		{
			$node = vB_Api::instanceInternal('node')->getNode($id);
			if ($node['userid'] != $userContext->fetchUserId())
			{
				throw new vB_Exception_Api('no_view_permissions');
			}
		}
		$attachdata = vB::getDbAssertor()->getRow('attach', array(
				vB_dB_Query::TYPE_KEY => vB_dB_Query::QUERY_SELECT,
				'nodeid' => $id));

		//If the record belongs to this user, or if this user can view attachments
		//in this section, then this is O.K.

		if (!empty($attachdata) && $attachdata['filedataid'])
				{
			$record = vB::getDbAssertor()->getRow('filedata', array(
					vB_dB_Query::TYPE_KEY => vB_dB_Query::QUERY_SELECT,
					'filedataid' => $attachdata['filedataid']
			));
				}

		if (empty($record))
		{
				return false;
		}
		return $this->loadFileData($record, $thumb, $includeData);
	}

	/**
	*
	*
	***/
	protected function saveUpload($filearray, $fileContents, $filesize, $extension)
	{
		$assertor = vB::getDbAssertor();
		$datastore = vB::getDatastore();
		$options = $datastore->get_value('options');
		$config = vB::getConfig();
		$usercontext = vB::getUserContext();
		$userid = vB::getCurrentSession()->get('userid');

		//make sure there's a place to put attachments.
		if ($options['attachfile'] AND
			(empty($options['attachpath']) OR !file_exists($options['attachpath']) OR !is_writable($options['attachpath']) OR !is_dir($options['attachpath'])))
		{
			throw new Exception('invalid_attachment_storage');

		}

		//make sure the file is good.
		if (! $this->imageHandler->verifyImageFile($fileContents))
		{
			unlink($filearray['tmp_name']);
			throw new Exception('dangerous_image_rejected');
		}

		//We check to see if this file already exists.
		$filehash = md5($fileContents);

		$fileCheck = $assertor->getRow('filedata', array(vB_dB_Query::TYPE_KEY => vB_dB_Query::QUERY_SELECT,
			'filehash' => $filehash, 'filesize' => $filesize));

		// Does filedata already exist?
		if (empty($fileCheck) OR ($fileCheck['userid'] != $userid))
		{
			if (!$usercontext->canUpload($filesize, $extension))
			{
				unlink($filearray['tmp_name']);
				throw new Exception('cannot_create_file');
			}

			//Get the image size information.
			$imageInfo = $this->imageHandler->fetchImageInfo($filearray['tmp_name']);

			if (!$imageInfo)
			{
				throw new vB_Exception_Api('upload_invalid_image');
			}

			$timenow =  vB::getRequest()->getTimeNow();
			
			$thumbnail = $this->imageHandler->fetchThumbnail($filearray['name'], $filearray['tmp_name'], $options['attachthumbssize'],
				$options['attachthumbssize'],$options['thumbquality']);


			$data = array(vB_dB_Query::TYPE_KEY => vB_dB_Query::QUERY_INSERT, 'userid' => $userid, 'dateline' => $timenow, 'thumbnail_dateline' => $timenow,
			 'filesize' => $filesize, 'filehash' => $filehash, 'thumbnail_filesize' => $thumbnail['filesize'],
			 'extension' => $extension, 'refcount' => 0, 'width' => $imageInfo[0], 'height' => $imageInfo[1],
			 'thumbnail_width' => $thumbnail['width'], 'thumbnail_height' => $thumbnail['height']);
			//Looks like we're ready to store. But do we put it in the database or the filesystem?
			if ($options['attachfile'])
			{
				//We name the files based on the filedata record, but we don't have that until we create the record. So we need
				// to do an insert, then create/move the files.
				$filedataid = $assertor->assertQuery('filedata', $data);

				if (is_array($filedataid))
				{
					$filedataid = $filedataid[0];
				}

				if (!intval($filedataid))
				{
					throw new Exception('unable_to_add_filedata');
				}

				if ($options['attachfile'] == ATTACH_AS_FILES_NEW) // expanded paths
				{
					$path = $options['attachpath'] . '/' . implode('/', preg_split('//', $userid,  -1, PREG_SPLIT_NO_EMPTY)) . '/';
				}
				else
				{
					$path = $options['attachpath'] . '/' . $userid . '/';
				}

				file_put_contents($path . $filedataid . '.thumb', $thumbnail['filedata']);
				move_uploaded_file ($filearray['tmp_name'] , $path . $filedataid . '.attach');
			}
			else
			{
				//We put the file contents into the data record.
				$data['filedata'] = $fileContents;
				$data['thumbnail'] = $thumbnail['filedata'];
				$filedataid = $assertor->assertQuery('filedata', $data);

				if (is_array($filedataid))
				{
					$filedataid = $filedataid[0];
				}

			}

			if (!empty( $filearray['name']))
			{
				 $filename = $filearray['name'];
			}
			else
			{
				$filename = '';
			}

			$result = array('filedataid' => $filedataid, 'filesize' => $filesize,
			'thumbsize' => $thumbnail['filesize'], 'extension' => $extension, 'filename' => $filename,
			'htmlType' => 'image/' . $extension);
		}
		else
		{
			// file already exists so we are not going to insert a new one
			$filedataid = $fileCheck['filedataid'];
			$result = array('filedataid' => $filedataid, 'filesize' => $fileCheck['filesize'] ,
				'thumbsize' => $fileCheck['thumbnail_filesize'], 'extension' => $extension, 'filename' => $filearray['name'],
				'htmlType' => 'image/' . $extension);
		}
		
		return $result;
	}


	/** sets the main logo for a file
	 *
	 * 	@param 	int		filedataid
	 * 	@param 	int		styleid. Defaults to -1, the default style
	 *
	 *	@return	mixed	array of data, includes error message or an int- normally 1.
	 **/
	public function setLogo($filedataid, $styleid = -1)
	{
		$userContext = vB::getUserContext();

		if (!intval($filedataid))
		{
			throw new Exception('invalid_data');
		}

		if ($userContext->isAdministrator() and $userContext->hasAdminPermission('canadminstyles'))
		{
			//validdate that the filedata record exists;
			$assertor = vB::getDbAssertor();
			$check = $assertor->getRow('filedata', array(vB_dB_Query::TYPE_KEY => vB_dB_Query::QUERY_SELECT,
				'filedataid' => $filedataid));
			if (empty($check) OR !empty($check['errors']))
			{
				throw new Exception('invalid_data');
			}
			$updates = array(vB_dB_Query::TYPE_KEY => vB_dB_Query::QUERY_UPDATE, 'filedataid' => $filedataid,
				'publicview' => 1);

			$assertor->assertQuery('filedata', $updates);
			$baseurl = vB::getDatastore()->get_value('baseurl');

			$userinfo = vB::getCurrentSession()->fetch_userinfo();
			$dm = new vB_DataManager_StyleVarImage();
			$existing = array('styleid' => $styleid,
				'stylevarid' => 'titleimage');
			$dm->set_existing($existing);
			$value = array('url' => 'attachment.php?filedataid=' . $filedataid);
			$dm->set('value', $value);
			$dm->set('dateline',vB::getRequest()->getTimeNow());
			$dm->set('username', $userinfo['username']);
			$dm->save();
			
			if ($dm->has_errors(false))
			{
				throw $dm->get_exception();
			}
			
			vB_Library::instance('Style')->buildStyleDatastore();
				
			return true;
		}
		else
		{
			throw new Exception('no_permission');
		}

	}

	/** Uploads a file
	 *
	 * 	@param 	mixed	data from $_FILES
	 *
	 *	@return	mixed	array of data, which will include either error info or a filedataid
	 **/
	public function upload($file)
	{
		//Leave for consistency with admincp
		if (!defined('ATTACH_AS_FILES_NEW'))
		{
			define('ATTACH_AS_FILES_NEW', 2);
		}
		//Only logged-in-users can upload files
		$userid = vB::getCurrentSession()->get('userid');
		$this->checkPermission($userid);

		//We can get either an uploaded file or an object. If we have an object let's make it into an array.

		if (is_object($file) AND isset($file->name))
		{
			$filearray = array('name' => $file->name, 'size' => $file->size,'type' => $file->type);
			$filebits = explode('.', $file->name);
			$extension = end($filebits);
			if (isset($file->contents) AND !empty($file->contents))
			{
				$filesize = strlen ($file->contents);
				$tempdir = sys_get_temp_dir();
				$filename = $tempdir . '/' . time() . $filesize . '.' . $extension;
				file_put_contents($filename, $file->contents);
				$filearray['tmp_name'] = $filename;
				$fileContents = $file->contents;
			}
		}
		else
		{

			if (!file_exists($file['tmp_name']))
			{
				throw new Exception('file_does_not_exist');
			}
			$filearray = $file;
			$filebits = explode('.', $file['name']);
			$extension = end($filebits);
			$filesize = filesize($file['tmp_name']);
			$fileContents = file_get_contents($file['tmp_name']);
		}
		//make sure there's a valid file here
		if (empty($fileContents))
		{
			throw new Exception('invalid_file_data');
		}

		$result = $this->saveUpload($filearray, $fileContents, $filesize, $extension);

		if (file_exists($filearray['tmp_name']))
		{
			unlink($filearray['tmp_name']);
		}

		return $result;
	}


	/** Upload an image based on the url
	 *
	 * 	@param 	string	remote url
	 *
	 *	@return	mixed	array of data, includes filesize, dateline, htmltype, filename, extension, and filedataid
	 **/
	public function uploadUrl($url)
	{
		//Leave for consistency with admincp
		define('ATTACH_AS_FILES_NEW', 2);
		//Only logged-in-users can upload files
		$userid = vB::getCurrentSession()->get('userid');
		$this->checkPermission($userid);

		//Did we get a valid url?
		if (!empty($url))
		{
			$fileContents = file_get_contents($url);
			$pathinfo = pathinfo($url);

			if ($fileContents AND !empty($pathinfo))
			{
				// if there's no extension here try get one from imageHandler
				if (!$pathinfo['extension'])
				{
					$extension = $this->imageHandler->fetchImageExtension($url);
				}
				else
				{
					$extension = $pathinfo['extension'];
				}
				
				$tempdir = sys_get_temp_dir();
				$filename = $tempdir . '/' . time() . $extension;
				file_put_contents($filename, $fileContents);
				$filesize = strlen($fileContents);
				$extension_map = $this->imageHandler->getExtensionMap();

				//Make a local copy
				$filearray = array('name' => $pathinfo['basename'], 'size' => $filesize,
					'type' => 'image/' . $extension_map[strtolower($extension)]);
				$filearray['tmp_name'] = $filename;
				
				//If the there's no extension then add one (friendly urls)
				if (!$this->imageHandler->fetchImageExtension($filearray['name']))
				{
					$filearray['name'] .= '.' . $extension;
				}

				$result = $this->saveUpload($filearray, $fileContents, $filesize, $extension);

				if (file_exists($filearray['tmp_name']))
				{
					unlink($filearray['tmp_name']);
				}

				return $result;
			}
		}
		
		return array('error' => 'invalid_url');
	}

	/** fetch image information about an attachment based on file data id
	 *
	 *      @param  int             filedataid
	 *      @param  bool    thumbnail version requested?
	 *      @param  bool    should we include the image content
	 *
	 *      @return mixed   array of data, includes filesize, dateline, htmltype, filename, extension, and filedataid
	 * */
	public function fetchImageByFiledataid($id, $thumb = false, $includeData = true)
	{
		if (empty($id) OR !intval($id))
		{
			throw new Exception('invalid_request');
		}

		//If the record belongs to this user, or if this user can view attachments
		//in this section, then this is O.K.
		$userinfo = vB::getCurrentSession()->fetch_userinfo();
		$record = vB::getDbAssertor()->getRow('filedata', array(
			vB_dB_Query::TYPE_KEY => vB_dB_Query::QUERY_SELECT,
			'filedataid' => $id));

		if (empty($record))
		{
			return false;
		}

		if (($userinfo['userid'] == $record['userid']) OR ($record['publicview'] > 0))
		{
			return $this->loadFileData($record, $thumb, $includeData);
		}
		throw new vB_Exception_Api('no_view_permissions');
	}

	/**
	 * Fetch information of attachments without data
	 *
	 * @param array $filedataids Array of file data ID
	 *
	 * @return array
	 */
	public function fetchAttachByFiledataids(array $filedataids)
	{
		$userContext = vB::getUserContext();

		$attachments = vB::getDbAssertor()->getRows('vBForum:fetchAttach2', array(
			'filedataid' => $filedataids
		), false, 'filedataid');

		foreach ($attachments as $k => $v)
		{
			// Permission check
			if (!$userContext->getChannelPermission('forumpermissions', 'cangetattachment', $v['nodeid']))
			{
				unset($attachments[$k]);
			}
		}
		return $attachments;

	}

	/** Load information about a file base on the data
	 *
	 *       @param  mixed   database record
	 *       @param  bool    thumbnail version requested?
	 *       @param  bool    should we include the image content
	 *
	 *       @return mixed   array of data, includes filesize, dateline, htmltype, filename, extension, and filedataid

	 * */
	protected function loadFileData($record, $thumb = false, $includeData = true)
	{
		$userid = vB::getCurrentSession()->get('userid');

		$options = vB::getDatastore()->get_value('options');
		if ($thumb)
		{
			$results = array('filesize' => $record['thumbnail_filesize'], 'dateline' => $record['thumbnail_dateline'],
				'htmlType' => 'image/' . strtolower($record['extension']), 'filename' => 'thumb_' . $record['filedataid'] . "." . strtolower($record['extension']),
				'extension' => $record['extension'], 'filedataid' => $record['filedataid']);

			if ($options['attachfile'] AND $includeData)
			{

				if ($options['attachfile'] == ATTACH_AS_FILES_NEW) // expanded paths
				{
					$path = $options['attachpath'] . '/' . implode('/', preg_split('//', $userid, -1, PREG_SPLIT_NO_EMPTY)) . '/';
				}
				else
				{
					$path = $options['attachpath'] . '/' . $userid . '/';
				}
				$results['filedata'] = file_get_contents($path . $record['filedataid'] . '.thumb');
			}
			else if ($includeData)
			{
				$results['filedata'] = $record['thumbnail'];
			}
		}
		else
		{
			$results = array('filesize' => $record['filesize'], 'dateline' => $record['dateline'],
				'htmlType' => 'image/' . strtolower($record['extension']),
				'filename' => 'image_' . $record['filedataid'] . "." . strtolower($record['extension']),
				'extension' => $record['extension'], 'filedataid' => $record['filedataid']);

			if ($options['attachfile'] AND $includeData)
			{

				if ($options['attachfile'] == ATTACH_AS_FILES_NEW) // expanded paths
				{
					$path = $options['attachpath'] . '/' . implode('/', preg_split('//', $userid, -1, PREG_SPLIT_NO_EMPTY)) . '/';
				}
				else
				{
					$path = $options['attachpath'] . '/' . $userid . '/';
				}
				$results['filedata'] = file_get_contents($path . $record['filedataid'] . '.attach');
			}
			else if ($includeData)
			{
				$results['filedata'] = $record['filedata'];
			}
		}

		return $results;
	}

	/** Remove an attachment
	* 	@param	INT	nodeid
	*
	**/
	public function delete($nodeid)
	{
		$data = array();

		if ($this->validate($data, $action = vB_Api_Content::ACTION_DELETE, $nodeid))
		{
			return $this->library->delete($nodeid);
		}
	}

	/** Remove an attachment
	 * 	@param	INT	nodeid
	 *
	 **/
	public function deleteAttachment($id)
	{
		if (empty($id) OR !intval($id))	{
			throw new Exception('invalid_request');
		}

		//Only the owner or an admin can delete an attachment.
		$userContext = vB::getUserContext();

		if (!$userContext->getChannelPermission('moderatorpermissions', 'canmoderateattachments', $id))
		{
			$node = $this->assertor->getRow('vBForum:node', array('nodeid' => $id));
			$userinfo = vB::getCurrentSession()->fetch_userinfo();

			if ($node['userid'] != $userinfo['userid'])
			{
				throw new vB_Exception_Api('no_permission');
			}
		}
		return $this->library->removeAttachment($id);
	}


	/** Remove an attachment, no permission checks
	 * 	@param	INT	nodeid
	 *
	 **/
	public function zapAttachment($id)
	{
		return $this->library->removeAttachment($id);
	}


	/**
	 *	See base class for information
	 */
	public function getIndexableFromNode($node, $include_attachments = true)
	{
		//deliberately don't call the parent class.  We don't want to load the content
		//twice and there isn't good way to get at the loaded content object.
		//merge in the attachments if any
		if($include_attachments)
		{
			$indexableContent = $this->getIndexableContentForAttachments($node['nodeid']);
		}
		else
		{
			$indexableContnet = array();
		}

		$indexableContent['title'] = $node['title'];
		$indexableContent['description'] = $node['description'];

		return $indexableContent;
	}



}
