<?php
/*
		[FuShow.cn] (C) 2013-2014 HXWY Inc.
		FuShow Friend V3.0				Id: class_xmlrpc.php
		Email <service@fushow.cn>		http://fushow.cn/
*/

if(!defined('IN_FUSHOW')) {
	exit('Access Denied');
}

class xmlrpc {

	var $xmlserver;
	var $callback;
	var $xmlmessage;
	var $db;
	var $timestamp;
	var $member;

	function __construct() {
		global $_SGLOBAL,$_SC;
		
		$this->callback = $this->xmlrpcApi();
		$this->xmlmessage = new stdClass();
		$this->siteUrl = getsiteurl();
		$this->db = $_SGLOBAL['db'];
		$this->charset = $_SC['charset'];
		$this->timestamp = $_SGLOBAL['timestamp'];
	}

	function xmlrpc() {
		$this->__construct();
	}

	function xmlrpcSet() {
		return new xmlrpc();
	}

	function xmlrpcServer() {
		if (phpversion() < '4.3.0') {
			$data = empty($GLOBALS['HTTP_RAW_POST_DATA'])?'':$GLOBALS['HTTP_RAW_POST_DATA'];
		} else {
			$data = file_get_contents("php://input");
		}

		if(!$data) {
			$this->sendFault(1, 'Invalid Method Call');
		} else {
			$data = addslashes($data);
		}
		$this->xmlmessage->structTypes = array();
		$this->xmlmessage->structs = array();
		$this->xmlmessage->struct_name = array();
		if ($this->xmlrpcParse($data)) {
			$result = $this->xmlrpcCall($this->xmlmessage->methodname, $this->xmlmessage->params);
			$rxml = $this->xmlrpcValue($result);
			$outxml = $this->xmlrpcValueXML($rxml);
			$outxml = "<methodResponse><params><param><value>$outxml</value></param></params></methodResponse>";
			$outxml = siconv($outxml,'UTF-8',$this->charset);
			$this->xmlrpcOutXML($outxml);
		}
	}

	function xmlrpcApi() {
		$api = array (

			// MetaWeblog API
			'metaWeblog.newPost' => 'newPost',
			'metaWeblog.editPost' => 'editPost',
			'metaWeblog.getPost' => 'getPost',
			'metaWeblog.newMediaObject' => 'newMediaObject',
			'metaWeblog.getCategories' => 'getCategories',
			'metaWeblog.getRecentPosts' => 'getRecentPosts',
			
			//WordPress API
			'mt.getCategoryList' =>  'getCategoryList',
			'mt.setPostCategories' =>  'setPostCategories',

			// Blogger API
			'blogger.getUsersBlogs' => 'getUserBlog',
			'blogger.getUserInfo' => 'getUserInfo',
			'blogger.deletePost' => 'deletePost',
			'blogger.getPost' => 'getPost',
			'blogger.getRecentPosts' => 'getRecentPosts',
			'blogger.newPost' => 'newPost',
			'blogger.editPost' => 'editPost'
		);
		return $api;
	}

	function xmlrpcParse($data) {
		$this->xmlmessage->messages = preg_replace('/<\?xml(.*)?\?'.'>/', '', $data);
		if (trim($this->xmlmessage->messages) == '') {
			return false;
		}

		$parser = xml_parser_create();
		xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, false);
		xml_set_object($parser, $this->xmlmessage);
		xml_set_element_handler($parser, array (& $this, 'xmltag_open'), array (& $this, 'xmltag_close'));
		xml_set_character_data_handler($parser, array (& $this,	'xml_data'));
		$message = $this->xmlmessage->messages;
		if (!xml_parse($parser, $message)) {
			return false;
		}
		xml_parser_free($parser);
		if ($this->xmlmessage->messageType == 'fault') {
			return false;
		}
		return true;
	}

	function xmltag_open($parser, $tag, $attr) {
		$this->xmlmessage->tag_content = '';
		$this->xmlmessage->last_open = $tag;
		switch ($tag) {
			case 'methodCall' :
			case 'methodResponse' :
			case 'fault' :
				$this->xmlmessage->messageType = $tag;
				break;
			case 'data' :
				$this->xmlmessage->structTypes[] = 'array';
				$this->xmlmessage->structs[] = array();
				break;
			case 'struct' :
				$this->xmlmessage->structTypes[] = 'struct';
				$this->xmlmessage->structs[] = array();
				break;
		}
	}

	function xml_data($parser, $data) {
		$this->xmlmessage->tag_content .= $data;
	}

	function xmltag_close($parser, $tag) {
		$flag = false;
		switch ($tag) {
			case 'int' :
			case 'i4' :
				$value = intval(trim($this->xmlmessage->tag_content));
				$flag = true;
				break;
			case 'double' :
				$value = (double) trim($this->xmlmessage->tag_content);
				$flag = true;
				break;
			case 'string' :
				$value = $this->xmlmessage->tag_content;
				$flag = true;
				break;
			case 'dateTime.iso8601' :
				$value = $this->convertDate($this->xmlmessage->tag_content);
				$flag = true;
				break;
			case 'value' :
				if (trim($this->xmlmessage->tag_content) != '' || $this->xmlmessage->last_open == 'value') {
					$value = (string) trim($this->xmlmessage->tag_content);
					$flag = true;
				}
				break;
			case 'boolean' :
				$value = (boolean) trim($this->xmlmessage->tag_content);
				$flag = true;
				break;
			case 'base64' :
				$value = saddslashes(base64_decode(trim($this->xmlmessage->tag_content)));
				$flag = true;
				break;
			case 'data' :
			case 'struct' :
				$value = array_pop($this->xmlmessage->structs);
				array_pop($this->xmlmessage->structTypes);
				$flag = true;
				break;
			case 'member' :
				array_pop($this->xmlmessage->struct_name);
				break;
			case 'name' :
				$this->xmlmessage->struct_name[] = trim($this->xmlmessage->tag_content);
				break;
			case 'methodName' :
				$this->xmlmessage->methodname = trim($this->xmlmessage->tag_content);
				break;
		}
		if ($flag) {
			if (count($this->xmlmessage->structs) > 0) {
				if ($this->xmlmessage->structTypes[count($this->xmlmessage->structTypes) - 1] == 'struct') {
					$this->xmlmessage->structs[count($this->xmlmessage->structs) - 1][$this->xmlmessage->struct_name[count($this->xmlmessage->struct_name) - 1]] = $value;
				} else {
					$this->xmlmessage->structs[count($this->xmlmessage->structs) - 1][] = $value;
				}
			} else {
				$this->xmlmessage->params[] = $value;
			}
		}

		if (!in_array($tag, array ('data', 'struct', 'member'))) {
			$this->xmlmessage->tag_content = '';
		}
	}

	function xmlrpcValue($data, $type = false) {
		$value = new stdClass();
		$value->data = $data;
		if (!$type) {
			$type = $this->xmlrpcType($value);
		}
		$value->type = $type;
		if ($type == 'struct') {
			foreach ($value->data as $key => $v) {
				$value->data[$key] = $this->xmlrpcValue($v);
			}
		}
		if ($type == 'array') {
			for ($i = 0, $j = count($value->data);$i < $j;$i++) {
				$value->data[$i] = $this->xmlrpcValue($value->data[$i]);
			}
		}
		return $value;
	}

	function xmlrpcValueXML($data) {
		switch ($data->type) {
			case 'boolean' :
				return '<boolean>'.($data->data) ? '1' : '0'.'</boolean>';
				break;
			case 'int' :
				return '<int>'.$data->data.'</int>';
				break;
			case 'double' :
				return '<double>'.$data->data.'</double>';
				break;
			case 'string' :
				return '<string>'.htmlspecialchars($data->data).'</string>';
				break;
			case 'array' :
				$return = '<array><data>';
				foreach ($data->data as $item) {
					$return .= '<value>'.$this->xmlrpcValueXML($item).'</value>';
				}
				$return .= '</data></array>';
				return $return;
				break;
			case 'struct' :
				$return = '<struct>'."\n";
				foreach ($data->data as $name => $item) {
					$return .= '<member><name>'.$name.'</name>';
					$return .= '<value>'.$this->xmlrpcValueXML($item).'</value></member>';
				}
				$return .= '</struct>';
				return $return;
				break;
			case 'date' :
				return '<dateTime.iso8601>'.($data->data->date).'</dateTime.iso8601>';
				break;
			case 'base64' :
				return '<base64>'.base64_encode($data->data).'</base64>';
				break;
			default :
				break;
		}
	}

	function xmlrpcType(& $value) {
		if (is_bool($value->data)) {
			return 'boolean';
		}
		if (is_double($value->data)) {
			return 'double';
		}
		if (is_int($value->data)) {
			return 'int';
		}
		if (is_array($value->data)) {
			return empty ($value->data) || range(0, count($value->data) - 1) === array_keys($value->data) ? 'array' : 'struct';
		}
		if (is_object($value->data)) {
			if ($value->data->is_date) {
				return 'date';
			}
			if ($value->data->is_base64) {
				return 'base64';
			}
			$value->data = get_object_vars($value->data);
			return 'struct';
		}
		return 'string';
	}

	function xmlrpcCall($methodname, $args) {
		$func = $this->callback[$methodname];
		return call_user_func_array(array(&$this, $func), $args);
	}

	function xmlrpcOutXML($xml) {
		$xml = '<?xml version="1.0" encoding="utf-8"?>'."\n".$xml;
		header('Connection: close');
		header('Content-Length: '.strlen($xml));
		header('Content-Type: text/xml');
		header('Date: '.sgmdate('r'));
		echo $xml;
		exit();
	}

	function sendFault($code = 0, $string = 'Error') {
		@header('Content-Type: text/xml');
		echo '<?xml version="1.0" encoding="utf-8"?>';
		echo '<methodResponse><fault><value><struct><member><name>faultCode</name><value><i4>';
		echo $code;
		echo '</i4></value></member><member><name>faultString</name><value><string>';
		echo $string;
		echo '</string></value></member></struct></value></fault></methodResponse>';
		exit();
	}

	function convertDate($time) {
		return sstrtotime($time);
	}

	function authUser($username, $password) {
		global $_SGLOBAL;

		$username = addslashes(siconv($username, $this->charset, 'UTF-8'));
		$password = addslashes(siconv($password, $this->charset, 'UTF-8'));

		if($this->member = getpassport($username, $password)) {
			$_SGLOBAL['supe_uid'] = $this->member['uid'];
			$this->member['username'] = addslashes($this->member['username']);
			$_SGLOBAL['supe_username'] = $this->member['username'];
			$_SGLOBAL['timestamp'] = $this->timestamp;
			return true;
		} else {
			$this->sendFault(1, 'Authoried Error.Please check your password.');
		}
	}

	function getClassId($uid, $classname) {
		$class = array();
		if($classname) {
			$classname = addslashes(siconv($classname, $this->charset, 'UTF-8'));
			$query = $this->db->query("SELECT classid FROM ".tname('class')." WHERE uid='$uid' AND classname='$classname'");
			$class = $this->db->fetch_array($query);
		}
		return empty($class['classid'])?0:$class['classid'];
	}

	function getUserBlog($uid, $username, $password) {
		
		$this->authUser($username, $password);
		
		$struct = array();
		$struct[] = array (
			'url' => $this->siteUrl.'fushow.php?uid='.$this->member['uid'].'&do=blog',
			'blogid' => $this->member['uid'],
			'blogName' => $this->member['username'].'\'s space'
		);

		return $struct;
	}

	function getUserInfo($uid, $username, $password) {
		
		$this->authUser($username, $password);
		
		$struct = array(
			'nickname'  => $this->member['username'],
			'userid'    => $this->member['uid'],
			'url'       => $this->siteUrl.'fushow.php?uid='.$this->member['uid'],
			'email'     => '',
			'lastname'  => $this->member['username'],
			'firstname' => ''
		);

		return $struct;
	}

	function newMediaObject($uid, $username, $password, $mediaobject = array()) {
		global $_SGLOBAL, $space;
		
		$fileext = fileext($mediaobject['name']);
		if(!in_array($fileext, array('jpg', 'gif', 'png'))) {
			$this->sendFault(500, 'You should choose image file to upload.');
		}
		
		$this->authUser($username, $password);
		
		include_once(S_ROOT.'./source/function_cp.php');
		$struct = array();
		if($stream_save = stream_save(sstripslashes($mediaobject['bits']),'0', $fileext)) {
			$struct['url'] = pic_get($stream_save['filepath'], $stream_save['thumb'], $stream_save['remote'], 0);
		} else {
			$this->sendFault(500, 'Sorry, your image could not be uploaded. Something wrong happened.');
		}
		
		if(!preg_match("/^(http\:\/\/|\/)/i", $struct['url'])) $struct['url'] = $this->siteUrl.$struct['url'];
		return $struct;

	}
	
	function newPost($uid, $username, $password, $post, $publish = true) {
		return $this->opPost($username, $password, $post, 0);
	}

	function editPost($postid, $username, $password, $post, $publish = true) {
		return $this->opPost($username, $password, $post, $postid);
	}
	
	function opPost($username, $password, $post, $postid=0) {
		global $_SGLOBAL;

		$this->authUser($username, $password);
		$postid = intval($postid);
		include_once(S_ROOT.'./source/function_blog.php');
		$uid = $this->member['uid'];
		$old_post = array();
		if($postid) {
			$query = $this->db->query("SELECT bf.*, b.* FROM ".tname('blog')." b LEFT JOIN ".tname('blogfield')." bf ON bf.blogid=b.blogid WHERE b.blogid='$postid' AND b.uid='$uid'");
			if(!$old_post = $this->db->fetch_array($query)) {
				$this->sendFault(500, 'Sorry, your entry could not be posted. Something wrong happened.');
			}
		}
		
		$post['title'] = siconv($post['title'],$this->charset,'UTF-8');
		
		$post['description'] = isset($post['content'])?$post['content']:$post['description'];
		$post['description'] = siconv($post['description'],$this->charset,'UTF-8');
		
		$blog_post_data = array(
			'classid' => intval($this->getClassId($uid, $post['categories'][0])),
			'subject' => addslashes($post['title']),
			'message' => addslashes($post['description']),
			'tag' => addslashes(empty($post['tagwords'])?'':siconv(implode(' ', $post['tagwords']), $this->charset, 'UTF-8'))
		);

		if($result = blog_post($blog_post_data, $old_post)) {
			return $result['blogid'];
		} else {
			$this->sendFault(500, 'Sorry, your entry could not be posted. Something wrong happened.');
		}
	}
	
	function deletePost($blogname, $blogid, $username, $password, $boolean = true){
		global $_SGLOBAL;
		
		$this->authUser($username, $password);

		include_once(S_ROOT.'./source/function_delete.php');
		$blogid = intval($blogid);
		if(deleteblogs(array($blogid))) {
			return true;
		} else {
			return false;
		}
	}

	function getCategories($blogid, $username, $password) {
		
		$this->authUser($username, $password);
		
		$struct = array();
		$struct[] = array (
			'description' => '',
			'htmlUrl' => $this->siteUrl.'fushow.php?uid='.$blogid.'&ac=blogs',
			'rssUrl' => '',
			'title' => '',
			'categoryid' => 0
		);

		$uid = $this->member['uid'];
		$query = $this->db->query("SELECT classid, classname FROM ".tname('class')." WHERE uid='$uid'");
		while ($cats = $this->db->fetch_array($query)) {
			$struct[] = array (
				'description' => $cats['classname'],
				'htmlUrl' => $this->siteUrl.'fushow.php?uid='.$blogid.'&ac=blogs&classid='.$cats['classid'],
				'rssUrl' => '',
				'title' => $cats['classname'],
				'categoryid' => $cats['classid']
			);
		}

		return $struct;
	}

	function getCategoryList($uid, $username, $password) {
		
		$this->authUser($username, $password);
		
		$struct = array();
		$struct[] = array ('categoryId' => 0, 'categoryName' => '');

		$uid = $this->member['uid'];
		$query = $this->db->query("SELECT classid, classname FROM ".tname('class')." WHERE uid='$uid'");
		while ($cats = $this->db->fetch_array($query)) {
			$struct[] = array (
				'categoryId' => $cats['classid'],
				'categoryName' => $cats['classname']
			);
		}

		return $struct;
	}

	function getPost($postid, $username, $password) {
		
		$this->authUser($username, $password);
		
		$struct = array();
		$postid = intval($postid);
		$uid = $this->member['uid'];
		$query = $this->db->query("SELECT bf.message, b.blogid, b.subject, b.dateline, c.classid, c.classname FROM ".tname('blog')." b LEFT JOIN ".tname('blogfield')." bf ON bf.blogid=b.blogid LEFT JOIN ".tname('class')." c ON c.classid=b.classid WHERE b.blogid='$postid' AND b.uid='$uid'");
		if($item = $this->db->fetch_array($query)) {
			$item['dateline'] = sgmdate('Ymd\TH:i:s', $item['dateline']);
			$struct = array(
				'userid' => $uid,
				'dateCreated' => $item['dateline'],
				'title' => $item['subject'],
				'categories' => array($item['classname']),
				'description' => $item['message'],
				'content' => $item['message']
			);
		}

		return $struct;
	}

	function getRecentPosts($type, $username, $password, $num = 50) {

		$this->authUser($username, $password);
		
		$struct = array();

		$uid = $this->member['uid'];
		$num = intval($num);
		if($num < 1) $num = 1;
	
		$query = $this->db->query("SELECT bf.message, b.blogid, b.subject, b.dateline FROM ".tname('blog')." b LEFT JOIN ".tname('blogfield')." bf ON bf.blogid=b.blogid WHERE b.uid ='$uid' ORDER BY b.dateline DESC LIMIT 0,$num");
		while($item = $this->db->fetch_array($query)) {
			$item['dateline'] = sgmdate('Ymd\TH:i:s', $item['dateline']);
			$struct[] = array(
				'postid' => $item['blogid'],
				'userid' => $uid,
				'dateCreated' => $item['dateline'],
				'title' => $item['subject'],
				'categories' => array($item['classname']),
				'description' => $item['message'],
				'content' => $item['message']
			);
		}

		return $struct;
	}
}

?>