<?php
/**
 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
 * @author     Andreas Gohr <andi@splitbrain.org>
 */
// must be run within Dokuwiki
if(!defined('DOKU_INC')) die();

if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/');
require_once(DOKU_PLUGIN.'syntax.php');
require_once(DOKU_INC.'inc/infoutils.php');


/**
 * This is the base class for all syntax classes, providing some general stuff
 */
class helper_plugin_freesync extends DokuWiki_Plugin {

	var $_profilePath;
	var $_client = null;
	var $_profile = array();

	/**
	 * constructor
	 */
	function helper_plugin_freesync(){
		global $conf;

		$this->_profilePath = $conf['cachedir'].'/freesync';
		if(!file_exists($this->_profilePath))
		@mkdir($this->_profilePath);
	}

	/**
	 * return some info
	 */
	function getInfo(){
		return array(
            'author' => 'Mikhail I. Izmestev',
            'email'  => 'izmmishao5@gmail.com',
            'date'   => '2009-03-11',
            'name'   => 'freesync helper',
            'desc'   => 'Dirty work for freesync',
            'url'    => '',
		);
	}

	function getProfileName() {
		return $this->_profile['name'];
	}

	function getProfile() {
		return $this->_profile;
	}

	function saveProfile($oldname, $profile) {
		$this->loadProfile($oldname);
		$this->_profile["pages"] = null;
		$this->_profile["files"] = null;
		
		$profile = array_merge($this->_profile, $profile);
		$oldpfFN = $this->_profilePath.'/'.$oldname.".ini";
		if(preg_match('/^http:\/\/([a-zA-Z0-9][a-zA-Z0-9_.]+)\//', $profile['xmlrpcurl'], $match)) {
			$profile['name'] = $match[1];
			$pfFN = $this->_profilePath.'/'.$profile['name'].".ini";
			$fprofile = fopen($pfFN, "w");
			foreach($profile as $key => $val) {
				fprintf($fprofile, "%s=\"%s\"\n", $key, $val);
			}
			fclose($fprofile);

			if($oldpfFN != $pfFN && file_exists($oldpfFN))
			unlink($oldpfFN);

			$this->loadProfile($profile['name']);
		}
		elseif(empty($profile['xmlrpcurl']) && file_exists($oldpfFN))
		unlink($oldpfFN);
		else
		msg('Bad XMLRPC URL');
	}

	function getProfiles() {
		$profiles = array();
		if($dh = opendir($this->_profilePath)) {
			while($profile = readdir($dh)) {
				if(preg_match("/^(.*).ini$/", $profile, $match))
				$profiles[] = $match[1];
			}
			closedir($dh);
		}
		return $profiles;
	}

	function loadProfile($name) {
		$pfFN = $this->_profilePath.'/'.$name.".ini";
		if(file_exists($pfFN))
		$this->_profile = parse_ini_file($pfFN);
		else
		$this->_profile = array();
		if(!empty($this->_profile['namespace']) && !preg_match('/.*:$/', $this->_profile['namespace']))
		$this->_profile['namespace'] .= ":";
	}

	function _xmlrpcInit() {
		global $conf;
		require_once(DOKU_INC.'inc/IXR_Library.php');

		if(!$this->_client) {
			$this->_client = new IXR_Client($this->_profile['xmlrpcurl']);
			if(!empty($this->_profile['user'])) {
				$loginurl = str_replace('lib/exe/xmlrpc.php', 'doku.php?do=login', $this->_profile['xmlrpcurl']);
				$this->_client->sendRequest($loginurl);
				$this->_client->sendRequest($loginurl,
				array("id" => "start", "u" => $this->_profile['user'], "p" => $this->_profile['pass']),
				'POST');
			}
		}
	}

	function _rpcQuery($call, $data, &$response) {
		if(!$this->_client)
		$this->_xmlrpcInit();

		if(!is_array($data))
		$data = array($data);
			
		array_unshift($data, $call);

		if(call_user_func_array(array(&$this->{'_client'},"query"), $data)) {
			$response = $this->_client->getResponse();
			return TRUE;
		}
			
		return false;
	}



	function getPagesDiff() {
		global $conf;

		$remote_list = array();
		$local_list = array();
		if($this->_profile['pages']) {
			if($this->_rpcQuery('wiki.getAllPages', array(), $rpages)) {
				foreach($rpages as $page) {
					$remote_list[$page["id"]] = array("id" => $page["id"],
			            "rperms" => $page["perms"],
					    "rsize" => $page["size"],
					    "rlastModified" => $page["lastModified"]->getTimestamp());
				}
	
				$pages = file($conf['indexdir'] . '/page.idx');
				foreach(array_keys($pages) as $idx) {
					if(page_exists($pages[$idx])) {
						$perm = auth_quickaclcheck($pages[$idx]);
						if($perm >= AUTH_READ) {
							$local_list[trim($pages[$idx])] = array("id" => trim($pages[$idx]),
							    "lperms" => $perm,
							    "lsize" => @filesize(wikiFN($pages[$idx])),
							    "llastModified" => @filemtime(wikiFN($pages[$idx])));
						}
					}
				}
	
			}
			else
			msg("Error get remote list pages");
		}
		if($this->_profile['files']) {
			if($this->_rpcQuery('wiki.getAttachments', array("", array("recursive" => true)), $rfiles)) {
				if(count($rfiles))
				foreach($rfiles as $file) {
					$remote_list[$file["id"]] = array("id" => $file["id"],
					"rperms" => $file["perms"],
					"rsize" => $file["size"],
					"rlastModified" => $file["lastModified"]->getTimestamp(),
					"file" => 1);
				}
	
				if(auth_quickaclcheck(':*') >= AUTH_READ) {
					$dir = utf8_encodeFN(str_replace(':', '/', $ns));
	
					$lfiles = array();
					require_once(DOKU_INC.'inc/search.php');
					search($lfiles, $conf['mediadir'], 'search_media', array('recursive' => true), $dir);
	
					if(count($lfiles))
					foreach($lfiles as $file) {
						$local_list[$file["id"]] = array("id" => $file["id"],
						"lperms" => auth_quickaclcheck(getNS($file['id']).':*'),
						"lsize" => $file["size"],
						"llastModified" => $file["mtime"],
						"file" => 1);
					}
				}
			}
			else
			msg("Error get remote list files");
		}
		
		$sync_list = array_merge_recursive($remote_list, $local_list);

		$sync_list = array_filter($sync_list, array(&$this, '_pageFilter'));

		ksort($sync_list);
		return $sync_list;

	}

	function _pageFilter($page) {
		$id = is_array($page['id'])?$page['id'][0]:$page['id'];
		if($page['llastModified'].','.$page['rlastModified'] == $this->_profile[$id]) {
			return 0;
		}
		return !strncmp($id, $this->_profile['namespace'], strlen($this->_profile['namespace'])) &&
		$page['llastModified'] != $page['rlastModified'];
	}

	function getLocalPage($id, $onlyinfo = false) {
		if(empty($id)) return false;

		$file = wikiFN($id,'');
		$time = @filemtime($file);
		if(!$time){
			return false;
		}

		$info = getRevisionInfo($id, $time, 1024);

		$page = array(
            'name'         => $id,
            'author'       => (($info['user']) ? $info['user'] : $info['ip']),
            'version'      => $time
		);

		if($onlyinfo)
		return $page;

		$page['text'] = rawWiki($id,'');
		if(!$page['text']) {
			$data = array($id);
			$page['text'] = trigger_event('HTML_PAGE_FROMTEMPLATE',$data,'pageTemplate',true);
		}

		return $page;
	}

	function getRemotePage($id, $onlyinfo = false) {
		if(empty($id)) return false;
		if($this->_rpcQuery('wiki.getPageInfo', $id, $page)) {
			if($page['version']) {
				if(!$onlyinfo) {
					$page['text'] = false;
					$this->_rpcQuery('wiki.getPage', $id, $page['text']);
				}
				return $page;
			}
		}

		return false;
	}

	function page_l2r($id, $sum='') {
		if(! ($page = $this->getLocalPage($id)) ) {
			$page['text'] = '';
		}
		if($this->_rpcQuery('wiki.putPage', array($id, $page['text'], $sum), $res)) {
			if($res == 0) {
				$rversion = $this->getRemotePage($id, true);
				$this->_profile[$id] = $page['version'].','.$rversion['lastModified']->getTimestamp();
				$this->saveProfile($this->getProfileName(), $this->_profile);
				return "Succesful";
			}
		}
		return "Error!";
	}


	function page_r2l($id, $sum='') {
		global $TEXT;
		global $lang;
		global $conf;

		$id    = cleanID($id);
		if(empty($id))
		return 'Empty page ID';

		$page = $this->getRemotePage($id);
		if(!$page || !$page['text'])
		return 'Error getting page';

		$TEXT  = trim($page['text']);


		if(!page_exists($id) && empty($TEXT)) {
			return 'Refusing to write an empty new wiki page';
		}

		if(auth_quickaclcheck($id) < AUTH_EDIT)
		return 'You are not allowed to edit this page';

		// Check, if page is locked
		if(checklock($id))
		return 'The page is currently locked';

		// SPAM check
		if(checkwordblock())
		return 'Positive wordblock check';

		// autoset summary on new pages
		if(!page_exists($id) && empty($sum)) {
			$sum = $lang['created'];
		}

		// autoset summary on deleted pages
		if(page_exists($id) && empty($TEXT) && empty($sum)) {
			$sum = $lang['deleted'];
		}

		lock($id);

		saveWikiText($id,$TEXT,$sum);

		unlock($id);

		// run the indexer if page wasn't indexed yet
		if(!@file_exists(metaFN($id, '.indexed'))) {
			// try to aquire a lock
			$lock = $conf['lockdir'].'/_indexer.lock';
			while(!@mkdir($lock,$conf['dmode'])){
				usleep(50);
				if(time()-@filemtime($lock) > 60*5){
					// looks like a stale lock - remove it
					@rmdir($lock);
				}else{
					return "Lock time out";
				}
			}
			if($conf['dperm']) chmod($lock, $conf['dperm']);

			require_once(DOKU_INC.'inc/indexer.php');

			// do the work
			idx_addPage($id);

			// we're finished - save and free lock
			io_saveFile(metaFN($id,'.indexed'),INDEXER_VERSION);
			@rmdir($lock);
		}

		$lversion = $this->getLocalPage($id, true);
		$this->_profile[$id] = $lversion['version'].','.$page['lastModified']->getTimestamp();
		$this->saveProfile($this->getProfileName(), $this->_profile);
		return "Succesful";
	}

	function getLocalFile($id, $onlyinfo = false) {
		if(empty($id)) return false;
		if (auth_quickaclcheck(getNS($id).':*') < AUTH_READ)
		return false;

		$filename = mediaFN($id);
		if (!@ file_exists($filename))
		return false;

		$file['lastModified'] = filemtime($filename);
		$file['size'] = filesize($filename);

		if($onlyinfo)
		return $file;

		$data = io_readFile($filename, false);
		$file['content'] = base64_encode($data);
		return $file;
	}

	function getRemoteFile($id, $onlyinfo = false) {
		if(empty($id)) return false;
		if(!$this->_client)
		$this->_xmlrpcInit();
		if($this->_rpcQuery('wiki.getAttachmentInfo', $id, $file) && $file['lastModified']) {
			$file['lastModified'] = $file['lastModified']->getTimestamp();
			if(!$onlyinfo) {
				$file['content'] = false;
				$fetchurl = str_replace('xmlrpc.php', 'fetch.php?media='.$id, $this->_profile['xmlrpcurl']);
				$this->_client->sendRequest($fetchurl);
				if(strlen($this->_client->resp_body) && $this->_client->resp_body != "Not Found")
				$file['content'] = base64_encode($this->_client->resp_body);
			}
			return $file;
		}
		return false;
	}

	function file_l2r($id, $sum='') {
		$id = cleanID($id);
		$file = $this->getLocalFile($id);
		if($file && $this->_rpcQuery('wiki.putAttachment', array($id, $file['content'], array("ow" => true)), $res)) {
			
			$rversion = $this->getRemoteFile($id, true);
			$this->_profile[$id] = $file['lastModified'].','.$rversion['lastModified'];
			$this->saveProfile($this->getProfileName(), $this->_profile);
			return "Succesful";
		}
		return "Error!";
	}
	
	
	function file_r2l($id, $sum='') {
		global $conf;
		global $lang;

		if(!isset($id)) {
			return 'Filename not given.';
		}
		
		$file = $this->getRemoteFile($id);
		if(!$file) {
			return 'Error get remote file';
		}
		if(!$file['content']) {
			return 'File is empty';
		}

		$auth = auth_quickaclcheck(getNS($id).':*');
		if($auth >= AUTH_UPLOAD) {

			$ftmp = $conf['tmpdir'] . '/' . $id;

			// save temporary file
			@unlink($ftmp);
			$buff = base64_decode($file['content']);
			io_saveFile($ftmp, $buff);

			// get filename
			list($iext, $imime,$dl) = mimetype($id);
			$id = cleanID($id);
			$fn = mediaFN($id);

			// get filetype regexp
			$types = array_keys(getMimeTypes());
			$types = array_map(create_function('$q','return preg_quote($q,"/");'),$types);
			$regex = join('|',$types);

			// because a temp file was created already
			if(preg_match('/\.('.$regex.')$/i',$fn)) {
				//check for overwrite
				$overwrite = @file_exists($fn);
				if($overwrite && $auth < AUTH_DELETE) {
					return $lang['uploadexist'];
				}
				// check for valid content
				@require_once(DOKU_INC.'inc/media.php');
				$ok = media_contentcheck($ftmp, $imime);
				if($ok == -1) {
					return sprintf($lang['uploadexist'], ".$iext");
				} elseif($ok == -2) {
					return $lang['uploadspam'];
				} elseif($ok == -3) {
					return $lang['uploadxss'];
				}

				// prepare event data
				$data[0] = $ftmp;
				$data[1] = $fn;
				$data[2] = $id;
				$data[3] = $imime;
				$data[4] = $overwrite;

				// trigger event
				require_once(DOKU_INC.'inc/events.php');
				if(true == trigger_event('MEDIA_UPLOAD_FINISH', $data, array($this, '_media_upload_action'), true)) {
					$lversion = $this->getLocalFile($id, true);
					$this->_profile[$id] = $lversion['lastModified'].','.$file['lastModified'];
					$this->saveProfile($this->getProfileName(), $this->_profile);
					return "Succesful";
				}

			} else {
				return $lang['uploadwrong'];
			}
		} else {
			return "You don't have permissions to upload files.";
		}
	}

	/**
	 * Moves the temporary file to its final destination.
	 *
	 * Michael Klier <chi@chimeric.de>
	 */
	function _media_upload_action($data) {
		global $conf;

		if(is_array($data) && count($data)===5) {
			io_createNamespace($data[2], 'media');
			if(rename($data[0], $data[1])) {
				chmod($data[1], $conf['fmode']);
				media_notify($data[2], $data[1], $data[3]);
				// add a log entry to the media changelog
				require_once(DOKU_INC.'inc/changelog.php');
				if ($data[4]) {
					addMediaLogEntry(time(), $data[2], DOKU_CHANGE_TYPE_EDIT);
				} else {
					addMediaLogEntry(time(), $data[2], DOKU_CHANGE_TYPE_CREATE);
				}
				return true;
			} else {
				return 'Upload failed.';
			}
		} else {
			return 'Upload failed.';
		}
	}

	function getDiff($id) {
		$id    = cleanID($id);
		if(empty($id))
		return 'Empty page ID';
		$lpage = $this->getLocalPage($id);
		$rpage = $this->getRemotePage($id);

		require_once(DOKU_INC.'inc/DifferenceEngine.php');

		$tdf = new TableDiffFormatter();
		return '<div class="dokuwiki"><table class="diff">'.
	    '<tr><th colspan="2">Local Wiki</th><th colspan="2">Remote Wiki</th></tr>'.
		$tdf->format(new Diff(split("\n", $lpage['text']), split("\n", $rpage['text']))).
	    '</table></div>';
	}

}
