<?php
/**
 * Wiki farm SOAP server
 *
 * @license  GPL 2 (http://www.gnu.org/licenses/gpl.html)
 * @author   Etienne MELEARD <etienne.meleard@cru.fr>
 * @desc     Handle SOAP requests
 */

/**
 * Specific handler to gather errors
 */
class SOAP_errorHandler {
	var $errors = array();
	
	function __construct() {}
	
	function getErrors() {
		return array_values($this->errors);
	}
}

/**
 * Class that handles SOAP requests
 */
class farmSOAP {
	private $server = null;
	
	/**
	 * @param $s reference to the SOAP handler
	 */
	function __construct($s) {
		$this->server = $s;
	}
	
	/**
	 * Identify remote application and process SOAP requests
	 * @return array returned from service call
	 */
	function authenticateRemoteAppAndRun() {
		$args = func_get_args();
		if(count($args) < 3) return array();
		
		// get call args
		$appname = $args[0];
		$apppwd = $args[1];
		$service = $args[2];
		
		if($appname == '' || $apppwd == '' || $service == '') return array();
		
		$serviceargs = isset($args[3]) ? (is_array($args[3]) ? $args[3] : array($args[3])) : array();
		
		// identify app
		$app = $this->getApp($appname);
		if(!$app) return array();
		if(bin2hex(mhash(1, $apppwd)) != $app['pwd']) return array();
		
		// check rights
		if(!isset($app['allowed'][$service])) return array();
		if(!method_exists($this, 'service_'.$service)) return array();
		
		// call and return
		foreach($app['allowed'][$service] as $k => $v) {
			if(is_array($v)) {
				if(!in_array($serviceargs[$k], $v)) $serviceargs[$k] = reset($v);
			}else $serviceargs[$k] = $v;
		}
		return call_user_func(array($this, 'service_'.$service), $serviceargs);
	}
	
	/**
	 * Check whether an application is in the trust list and gather its rights
	 * @param $name application name
	 * @return application descriptor
	 */
	function getApp($name) {
		foreach(explode("\n\n", preg_replace('`<\?php[^?]+\?>\s*`i', '', file_get_contents('./trusted_apps.php'))) as $a) {
			$app = array();
			foreach(explode("\n", trim($a)) as $f) {
				if(preg_match('`^(name|pwd)\s*=\s*([^#]+)(#.*)?$`i', trim($f), $m)) $app[$m[1]] = trim($m[2]);
				if(preg_match('`^allowed\s*=\s*([^#]+)(#.*)?$`i', trim($f), $m)) {
					$app['allowed'] = array();
					$list = trim($m[1]);
					while(preg_match('`^([^(,]+)(\(([^)]+)\))?(\s*,\s*(.*))?$`', $list, $m)) {
						$sname = trim($m[1]);
						$ovr = trim($m[3]);
						$list = isset($m[5]) ? trim($m[5]) : '';
						
						$app['allowed'][$sname] = array();
						if($ovr != '') {
							foreach(array_map('trim', explode(',', $ovr)) as $o) {
								$o = array_map('trim', explode('=', $o));
								if(count($o) == 1) {
									$app['allowed'][$sname][$o[0]] = true;
								}elseif(count($o) == 2) {
									if(preg_match('`^\[[^]]+\]$`', $o[1])) $o[1] = array_map('trim', explode(',', substr($o[1], 1, -1)));
									elseif(preg_match('`^[0-9]+$`', $o[1])) $o[1] = (int)$o[1];
									elseif(preg_match('`^[0-9]+\.[0-9]+$`', $o[1])) $o[1] = (float)$o[1];
									elseif(preg_match('`^(true|false)$`i', $o[1])) $o[1] = (strtolower($o[1]) == 'true');
									elseif(preg_match('`^null$`i', $o[1])) $o[1] = null;
									$app['allowed'][$sname][$o[0]] = $o[1];
								}
							}
						}
					}
				}
			}
			if(isset($app['name']) && $app['name'] == $name) return $app;
		}
		return null;
	}
	
	/*
	 *==========*
	 * SERVICES *
	 *==========*
	 */
	
	/**
	 * Simple service that returns args to check app auth & passed args
	 * @param $args SOAP arguments
	 * @return stuff
	 */
	function service_test($args) {
		return array('appauth' => 'ok', 'args' => $args);
	}
	
	/**
	 * Service that creates an animal
	 * @param $args SOAP arguments
	 * @return success indicator and errors, also contains accepted name if successful (only different from original if it contained funky characters)
	 */
	function service_animalCreate($args) {
		$name = isset($args['name']) ? $args['name'] : '';
		$template = isset($args['template']) ? $args['template'] : '';
		$host = isset($args['host']) ? $args['host'] : ''; // only used if farm is in virtual mode
		$eh = new SOAP_errorHandler();
		if($n = dokuwiki_farm_animal::createNew($name, $template, $host, $eh)) {
			return array('success' => true, 'name' => $n);
		}else return array('success' => false, 'errors' => $eh->getErrors());
	}
	
	/**
	 * Service that deletes an animal
	 * @param $args SOAP arguments
	 * @return success indicator
	 */
	function service_animalDelete($args) {
		$name = isset($args['name']) ? $args['name'] :  reset($args);
		if(!dokuwiki_farm_animal::exists($name)) return array('success' => false);
		$a = new dokuwiki_farm_animal($name);
		return array('success' => $a->delete());
	}
	
	/**
	 * Service that returns animal's status (existence/state)
	 * @param $args SOAP arguments
	 * @return animal status
	 */
	function service_animalExists($args) {
		$name = isset($args['name']) ? $args['name'] : reset($args);
		if(!dokuwiki_farm_animal::exists($args['name'])) return array('exists' => 0);
		$a = new dokuwiki_farm_animal($args['name']);
		return array(
			'exists' => 1,
			'status' => $a->getStatus(),
			'locked' => $a->getLockState()
		);
	}
	
	/**
	 * Service that changes the status of an animal
	 * @param $args SOAP arguments
	 * @return success indicator
	 */
	function service_animalStatus($args) {
		$name = isset($args['name']) ? $args['name'] : '';
		$status = isset($args['status']) ? $args['status'] : '';
		if($name == '' || in_array($status, array('open', 'maintenance', 'closed', 'abuse'))) return array();
		if(!dokuwiki_farm_animal::exists($name)) return array();
		$a = new dokuwiki_farm_animal($name);
		return array('success' => $a->setMetadata('status', $status));
	}
	
	/**
	 * Service that changes the lock state of an animal
	 * @param $args SOAP arguments
	 * @return success indicator
	 */
	function service_animalLockState($args) {
		$name = isset($args['name']) ? $args['name'] : '';
		$lockstate = isset($args['lockstate']) ? $args['lockstate'] : '';
		if($name == '' || in_array($lockstate, array('', 'none', 'edits', 'admin', 'all'))) return array();
		if(!dokuwiki_farm_animal::exists($name)) return array();
		$a = new dokuwiki_farm_animal($name);
		return array('success' => $a->setMetadata('lockstate', $lockstate));
	}
	
	/**
	 * Service that returns animals list
	 * @param $args SOAP arguments
	 * @return animal list
	 */
	function service_animalsList($args) {
		return array_map(create_function('$a', 'return array(\'name\' => $a->getName(), \'url\' => $a->getUrl());'), dokuwiki_farm_animal::listAnimals());
	}
}

// Loads farm config and register SOAP handler, if no POST data then prints wsdl
if(!defined('DOKU_CONF')) {
	define('DOKU_FARM_SOAPCONTEXT', true);
	ini_set('soap.wsdl_cache_enabled', '0');
	
	if(!@file_exists('config.php')) die('No config found');
	
	$farmconf = array();
	include 'config.php';
	
	if(!isset($farmconf['enablesoap'])) die();
	if(!$farmconf['enablesoap']) die();
	
	include 'animal.class.php';
	
	$wsdl = $farmconf['farmfsroot'].'farm.wsdl';
	$trace = 0;
	$soap_version = SOAP_1_1;
	
	if($_SERVER['REQUEST_METHOD'] == 'POST') {
		try {
			$server = new SoapServer($wsdl,  array('trace' => $trace, 'soap_version' => $soap_version));
			$server->setClass('farmSOAP', $server);
			$server->handle();
		}catch(Exception $e) {
			die($e->getMessage());
		}
	}else echo @file_get_contents($wsdl);
}
