1<?php
2/**
3 * Wiki farm manager
4 *
5 * @license  GPL 2 (http://www.gnu.org/licenses/gpl.html)
6 * @author   Etienne MELEARD <etienne.meleard@cru.fr>
7 * @desc     Process and renders SOAP server config related requests
8 */
9
10// must be run within Dokuwiki
11if(!defined('DOKU_INC')) die();
12
13if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN', DOKU_INC.'lib/plugins/');
14if(!defined('DOKU_FARM_PLUGIN')) define('DOKU_FARM_PLUGIN', DOKU_PLUGIN.'farm/');
15
16if(!defined('DOKU_FARMPLUGINLOADED')) define('DOKU_FARMPLUGINLOADED', true);
17
18class dokuwiki_farm_soapconfig {
19	var $data = array();
20	var $manager = null;
21	var $errors = array();
22
23	/**
24	 * @param $manager object that must handle error(), success(), nicesize(), getLang() ... calls
25	 */
26	function __construct($manager) {
27		$this->manager = & $manager;
28	}
29
30	/**
31	 * Builds a link inside SOAP config tab
32	 * @param $opts farm option array
33	 * @return url string
34	 */
35	function wl($opts = array()) {
36		return $this->manager->wl('soapconfig', $opts);
37	}
38
39	/**
40	 * Process requests
41	 */
42	function process() {
43		// Parameters check
44		if(
45			!(isset($this->manager->opt['app']) && isset($this->manager->opt['save'])) &&
46			!(isset($this->manager->opt['app']) && isset($this->manager->opt['delete'])) &&
47			!(isset($this->manager->opt['new']) && isset($this->manager->opt['save'])) &&
48			!isset($this->manager->opt['advancedsave'])
49		) return;
50
51		// Security check
52		if(!checkSecurityToken()) {
53			$this->manager->error('system_errors', 'system_badtoken_failure');
54			return; // any changes done by post
55		}
56
57		// Text mode save request
58		if(isset($this->manager->opt['advancedsave'])) {
59			if(!isset($_POST['soap_config_advanced']) || empty($_POST['soap_config_advanced'])) {
60				$this->manager->error('system_errors', 'postparametermissing_failure');
61				return;
62			}
63			if($fp = fopen(DOKU_FARM_PLUGIN.'trusted_apps.php', 'w')) {
64				fwrite($fp, '<?php exit(); ?>'."\n".$_POST['soap_config_advanced']);
65				fclose($fp);
66			}else $this->manager->error('soapconfig_errors', 'soapconfig_save_failure');
67			return;
68		}
69
70		// Trusted application deletion request
71		if(isset($this->manager->opt['delete']) && isset($this->manager->opt['app'])) {
72			$out = array();
73			foreach(explode("\n\n", @file_get_contents(DOKU_FARM_PLUGIN.'trusted_apps.php')) as $p) {
74				$appname = '';
75				foreach(explode("\n", trim($p)) as $f) if(preg_match('`^name\s*=\s*([^#]+)(#.*)?$`i', trim($f), $m)) $appname = trim($m[1]);
76				if($appname != $this->manager->opt['app']) $out[] = $p;
77			}
78			if($fp = fopen(DOKU_FARM_PLUGIN.'trusted_apps.php', 'w')) {
79				fwrite($fp, $out);
80				fclose($fp);
81				unset($this->manager->opt['app']);
82				$this->manager->success('soapconfig_save_success');
83			}else $this->manager->error('soapconfig_errors', 'soapconfig_save_failure');
84			return;
85		}
86
87		// Parameters check
88		if(
89			!isset($_POST['soap_remoteapp_name']) || empty($_POST['soap_remoteapp_name']) || !preg_match('`^[a-z0-9_-]+$`i', $_POST['soap_remoteapp_name']) ||
90			!isset($_POST['soap_remoteapp_namecomment']) ||
91			!isset($_POST['soap_remoteapp_pwd']) ||
92			!isset($_POST['soap_remoteapp_pwdcomment']) ||
93			!isset($_POST['soap_remoteapp_serviceallowed']) ||
94			!isset($_POST['soap_remoteapp_serviceimposedargs']) ||
95			!isset($_POST['soap_remoteapp_servicescomment'])
96		) {
97			$this->manager->error('system_errors', 'postparametermissing_failure');
98			return;
99		}
100
101		// Basic mode save / add
102
103		$name = $_POST['soap_remoteapp_name'];
104		$namecomment = str_replace(array("\n", "\r"), '', $_POST['soap_remoteapp_namecomment']);
105
106		$pwdh = function_exists('md5') ? md5($_POST['soap_remoteapp_pwd']) : (function_exists('mhash') ? bin2hex(mhash(1, $_POST['soap_remoteapp_pwd'])) : null);
107		$pwd = preg_match('`^[0-9abcdef]{32}$`i', $_POST['soap_remoteapp_pwd']) ? strtolower($_POST['soap_remoteapp_pwd']) : $pwdh;
108		$pwdclear = str_replace(array("\n", "\r"), '', $_POST['soap_remoteapp_pwd']);
109		$pwdcomment = str_replace(array("\n", "\r"), '', $_POST['soap_remoteapp_pwdcomment']);
110
111		$allowedcomment = str_replace(array("\n", "\r"), '', $_POST['soap_remoteapp_serviceallowedcomment']);
112
113		$block = '# '.$name.' app'."\n";
114		$block .= 'name = '.$name.($namecomment != '' ? ' # '.$namecomment : '')."\n";
115		$block .= 'pwd = '.$pwd.' '.(($pwdclear != $pwd) || ($pwdcomment != '') ? '# '.($pwd != $pwdclear ? '('.$pwdclear.') ' : '').$pwdcomment : '')."\n";
116		$allowed = array();
117		include 'soapserver.php';
118		foreach(get_class_methods('farmSOAP') as $m) {
119			if(strpos($m, 'service_') !== 0) continue;
120			$m = substr($m, 8);
121			if(isset($_POST['soap_remoteapp_serviceallowed'][$m])) $allowed[] = $m.((isset($_POST['soap_remoteapp_serviceimposedargs'][$m]) && !empty($_POST['soap_remoteapp_serviceimposedargs'][$m])) ? '('.$_POST['soap_remoteapp_serviceimposedargs'][$m].')' : '');
122		}
123		$block .= 'allowed = '.implode(', ', $allowed).($allowedcomment != '' ? ' # '.$allowedcomment : '');
124
125		if(isset($this->manager->opt['new']) && isset($this->manager->opt['save'])) {
126			$app = '';
127			$file = @file_get_contents(DOKU_FARM_PLUGIN.'trusted_apps.php');
128			if(!$file) {
129				$this->manager->error('soapconfig_errors', 'soapconfig_corruptedfile_failure');
130				return;
131			}
132
133			if($fp = fopen(DOKU_FARM_PLUGIN.'trusted_apps.php', 'w')) {
134				fwrite($fp, $file."\n\n".$block);
135				fclose($fp);
136				$this->manager->opt['app'] = $name;
137				$this->manager->success('soapconfig_save_success');
138				return;
139			}else $this->manager->error('soapconfig_errors', 'soapconfig_save_failure');
140
141		}
142
143		if(isset($this->manager->opt['save']) && isset($this->manager->opt['app'])) {
144			$out = array();
145			foreach(explode("\n\n", @file_get_contents(DOKU_FARM_PLUGIN.'trusted_apps.php')) as $p) {
146				$appname = '';
147				foreach(explode("\n", trim($p)) as $f) if(preg_match('`^name\s*=\s*([^#]+)(#.*)?$`i', trim($f), $m)) $appname = trim($m[1]);
148				if($appname != $this->manager->opt['app']) {
149					$out[] = $p;
150				}else $out[] = $block;
151			}
152			if($fp = fopen(DOKU_FARM_PLUGIN.'trusted_apps.php', 'w')) {
153				fwrite($fp, implode("\n\n", $out));
154				fclose($fp);
155				$this->manager->opt['app'] = $name;
156				$this->manager->success('soapconfig_save_success');
157			}else $this->manager->error('soapconfig_errors', 'soapconfig_save_failure');
158			return;
159		}
160	}
161
162	/**
163	 * Renders
164	 */
165	function html() {
166		global $ID;
167		ptln('<div class="farm_cmd_title">'.$this->manager->getLang('soapconfig_title').'</div>');
168		ptln('<div class="farm_cmd_info">'.$this->manager->getLang('soapconfig_info').'</div>');
169		ptln($this->manager->getLang('soapconfig_wsdlurl', array($this->manager->conf['farmwebroot'].'farm.wsdl')));
170		ptln('<div class="soapapp soapapp_new">');
171		ptln('	<a href="'.$this->wl(array('new' => 1)).'" title="'.$this->manager->getLang('soapconfig_newapp').'"><img src="'.DOKU_FARM_WWW.'images/add.png" alt="add remote app" /> '.$this->manager->getLang('soapconfig_newapp').'</a>');
172		ptln('	<a href="'.$this->wl(array('advanced' => 1)).'" title="'.$this->manager->getLang('soapconfig_advancededit').'"><img src="'.DOKU_FARM_WWW.'images/txt.png" alt="advanced edit mode" /> '.$this->manager->getLang('soapconfig_advancededit').'</a>');
173		ptln('</div>');
174
175		if(isset($this->manager->opt['advanced'])) {
176				$this->manager->formHead(array('farm_cmd' => 'soapconfig'));
177				ptln('<fieldset>');
178				ptln('	<textarea rows="20" style="width:100%;overflow:auto;" name="soap_config_advanced">'.preg_replace('`<\?php\s*exit\(\);\s*\?>`', '', @file_get_contents(DOKU_FARM_PLUGIN.'trusted_apps.php')).'</textarea>');
179				ptln('</fieldset>');
180				ptln('	<fieldset class="save">');
181				ptln('		<input type="submit" class="button" name="farm_opt[advancedsave]" value="'.$this->manager->getLang('btn_save').'" />');
182				ptln('	</fieldset>');
183				ptln('</form>');
184		}else{
185			$apps = $this->getApps();
186			foreach($apps as $a) {
187				ptln('<div class="soapapp">');
188				ptln('	'.$this->manager->getLang('soapconfig_field_name_name').' : '.$a['name']['value'].' '.($a['name']['comment'] != '' ? ' <i>('.$this->manager->getLang('soapconfig_comment').' : '.$a['name']['comment'].')</i>' : '').'<br />');
189				ptln('	'.$this->manager->getLang('soapconfig_field_name_passwordhash').' : '.$a['pwd']['value'].($a['pwd']['comment'] != '' ? ' <i>('.$this->manager->getLang('soapconfig_comment').' : '.$a['pwd']['comment'].')</i>' : '').'<br />');
190				ptln('	'.$this->manager->getLang('soapconfig_field_name_allowedservices').' : '.implode(', ', array_keys($a['allowed']['value'])).''.($a['allowed']['comment'] != '' ? ' <i>('.$this->manager->getLang('soapconfig_comment').' : '.$a['allowed']['comment'].')</i>' : '').'<br />');
191				ptln('	<a href="'.$this->wl(array('app' => $a['name']['value'])).'#remoteapp_edit" title="'.$this->manager->getLang('soapconfig_editapp').'"><img src="'.DOKU_FARM_WWW.'images/edit.png" alt="edit remote app" /></a>');
192				ptln('</div>');
193			}
194
195			if(isset($this->manager->opt['app']) || isset($this->manager->opt['new'])) {
196				ptln('<div class="soapedit">'.$this->manager->getLang('soapconfig_'.(isset($this->manager->opt['app']) ? 'edit' : 'new').'app').'</div>');
197				$hf = array('farm_cmd' => 'soapconfig');
198				if(isset($this->manager->opt['app'])) $hf['farm_opt[app]'] = $this->manager->opt['app'];
199				else $hf['farm_opt[new]'] = 1;
200				$a = isset($this->manager->opt['app']) ? $apps[$this->manager->opt['app']] : null;
201				ptln('<a name="remoteapp_edit"></a>');
202				$this->manager->formHead($hf);
203				ptln('<fieldset>');
204				ptln('	'.$this->manager->getLang('soapconfig_field_name_name').' : <input type="text" name="soap_remoteapp_name" value="'.($a ? $a['name']['value'] : '').'" /><br />');
205				ptln('	'.$this->manager->getLang('soapconfig_comment').' : <input type="text" name="soap_remoteapp_namecomment" value="'.($a ? $a['name']['comment'] : '').'" /><br />');
206				ptln('	<small>'.$this->manager->getLang('soapconfig_field_desc_name').'</small>');
207				ptln('</fieldset>');
208
209				ptln('<fieldset>');
210				ptln('	'.$this->manager->getLang('soapconfig_field_name_passwordhash').' : <input type="text" name="soap_remoteapp_pwd" value="'.($a ? $a['pwd']['value'] : '').'" /><br />');
211				ptln('	'.$this->manager->getLang('soapconfig_comment').' : <input type="text" name="soap_remoteapp_pwdcomment" value="'.($a ? $a['pwd']['comment'] : '').'" /><br />');
212				ptln('	<small>'.$this->manager->getLang('soapconfig_field_desc_passwordhash').'</small><br />');
213				if(function_exists('md5') || function_exists('mhash')) {
214					ptln('	'.$this->manager->getLang('soapconfig_field_name_passwordhash_change'));
215				}else{
216					ptln('	'.$this->manager->getLang('soapconfig_field_name_passwordhash_change_nohashfunc'));
217				}
218				ptln('</fieldset>');
219
220				ptln('<fieldset>');
221				ptln('	'.$this->manager->getLang('soapconfig_field_name_allowedservices').' : <br />');
222				ptln('		<small>'.$this->manager->getLang('soapconfig_field_desc_allowedservice_imposedargs').'</small><br />');
223				if(!class_exists('farmSOAP')) include 'soapserver.php';
224				foreach(get_class_methods('farmSOAP') as $m) {
225					if(strpos($m, 'service_') !== 0) continue;
226					$m = substr($m, 8);
227					ptln('	<div class="soapapp_service">');
228					ptln('		<input type="checkbox" name="soap_remoteapp_serviceallowed['.$m.']" '.($a ? (isset($a['allowed']['value'][$m]) ? 'checked="checked"' : '') : '').' /> '.$m.' :<br />');
229					ptln('		<small>'.$this->manager->getLang('soapconfig_field_desc_allowedservice_'.$m).'</small><br />');
230					$imp = '';
231					if($a) {
232						if(isset($a['allowed']['value'][$m])) {
233							$imp = $a['allowed']['value'][$m];
234							foreach($imp as $k => $v) $imp[$k] = $k.'='.$v;
235							$imp = implode(', ', $imp);
236						}
237					}
238					ptln('		'.$this->manager->getLang('soapconfig_field_name_allowedservice_imposedargs').' : <input type="text" name="soap_remoteapp_serviceimposedargs['.$m.']" value="'.$imp.'" /><br />');
239					ptln('	</div>');
240				}
241				ptln('	'.$this->manager->getLang('soapconfig_comment').' : <input type="text" name="soap_remoteapp_servicescomment" value="'.($a ? $a['allowed']['comment'] : '').'" /><br />');
242				ptln('	<small>'.$this->manager->getLang('soapconfig_field_desc_allowedservices').'</small>');
243				ptln('</fieldset>');
244
245				ptln('	<fieldset class="save">');
246				ptln('		<input type="submit" class="button" name="farm_opt[save]" value="'.$this->manager->getLang('btn_save').'" />');
247				if($a) ptln('		<input type="button" class="button" onclick="if(this.type==\'submit\')return true;if(confirm(\''.$this->manager->getLang('soapconfig_appdelete_sure', $a['name']['value'], 'js').'\')){this.type=\'submit\';this.click();}" name="farm_opt[delete]" value="'.$this->manager->getLang('btn_delete').'" />');
248				ptln('	</fieldset>');
249				ptln('</form>');
250			}
251		}
252	}
253
254	/**
255	 * Returns the list of trusted applications
256	 * @return array of application descriptors
257	 */
258	function getApps() {
259		$apps = array();
260		foreach(explode("\n\n", preg_replace('`<\?php[^?]+\?>\s*`i', '', @file_get_contents(DOKU_FARM_PLUGIN.'trusted_apps.php'))) as $a) {
261			$app = array();
262			foreach(explode("\n", trim($a)) as $f) {
263				if(preg_match('`^(name|pwd)\s*=\s*([^#]+)(#(.*))?$`i', trim($f), $m)) {
264					$app[strtolower($m[1])] = array(
265						'value' => trim($m[2]),
266						'comment' => isset($m[4]) ? trim($m[4]) : ''
267					);
268				}
269				if(preg_match('`^allowed\s*=\s*([^#]+)(#(.*))?$`i', trim($f), $m)) {
270					$list = trim($m[1]);
271					$app['allowed'] = array(
272						'value' => array(),
273						'comment' => isset($m[3]) ? trim($m[3]) : ''
274					);
275					while(preg_match('`^([^(,]+)(\(([^)]+)\))?(\s*,\s*(.*))?$`', $list, $m)) {
276						$sname = trim($m[1]);
277						$ovr = trim($m[3]);
278						$list = isset($m[5]) ? trim($m[5]) : '';
279
280						$app['allowed']['value'][$sname] = array();
281						if($ovr != '') {
282							foreach(array_map('trim', explode(',', $ovr)) as $o) {
283								$o = array_map('trim', explode('=', $o));
284								if(count($o) == 1) {
285									$app['allowed']['value'][$sname][$o[0]] = true;
286								}elseif(count($o) == 2) {
287									if(preg_match('`^\[[^]]+\]$`', $o[1])) $o[1] = array_map('trim', explode(',', substr($o[1], 1, -1)));
288									elseif(preg_match('`^[0-9]+$`', $o[1])) $o[1] = (int)$o[1];
289									elseif(preg_match('`^[0-9]+\.[0-9]+$`', $o[1])) $o[1] = (float)$o[1];
290									elseif(preg_match('`^(true|false)$`i', $o[1])) $o[1] = (strtolower($o[1]) == 'true');
291									elseif(preg_match('`^null$`i', $o[1])) $o[1] = null;
292									$app['allowed']['value'][$sname][$o[0]] = $o[1];
293								}
294							}
295						}
296					}
297				}
298			}
299			if(isset($app['name']['value']) && $app['name']['value'] != '') $apps[$app['name']['value']] = $app;
300		}
301		return $apps;
302	}
303}
304?>
305