<?php
/**
 * XML-RPC : MetaWeblog API
 *
 * b2evolution - {@link http://b2evolution.net/}
 * Released under GNU GPL License - {@link http://b2evolution.net/about/gnu-gpl-license}
 * @copyright (c)2003-2020 by Francois Planque - {@link http://fplanque.com/}
 *
 * @author tor
 *
 * @see http://b2evolution.net/man/metaweblog-api
 * @see http://www.xmlrpc.com/metaWeblogApi
 *
 * @package xmlsrv
 */
if( !defined('EVO_MAIN_INIT') ) die( 'Please, do not access this page directly.' );


$mwnewMediaObject_doc = 'Uploads a file to the media library of the blog';
$mwnewMediaObject_sig = array(
		array($xmlrpcStruct,$xmlrpcInt,$xmlrpcString,$xmlrpcString, $xmlrpcStruct),
		array($xmlrpcStruct,$xmlrpcString,$xmlrpcString,$xmlrpcString, $xmlrpcStruct)
	);
/**
 * metaWeblog.newMediaObject  image upload
 *
 * image is supplied coded in the info struct as bits
 *
 * @see http://www.xmlrpc.com/metaWeblogApi#metaweblognewmediaobject
 *
 *
 * @param xmlrpcmsg XML-RPC Message
 *					0 blogid (string): Unique identifier of the blog the post will be added to.
 *						Currently ignored in b2evo, in favor of the category.
 *					1 username (string): Login for a Blogger user who has permission to edit the given
 *						post (either the user who originally created it or an admin of the blog).
 *					2 password (string): Password for said username.
 *					3 struct (struct)
 * 							- name : filename
 * 							- type : mimetype
 * 							- bits : base64 encoded file
 * @return xmlrpcresp XML-RPC Response
 */
function mw_newmediaobject($m)
{
	return _wp_mw_newmediaobject( $m );
}


$mwnewpost_doc='Adds a post, blogger-api like, +title +category +postdate';
$mwnewpost_sig = array(
		array($xmlrpcStruct,$xmlrpcInt,$xmlrpcString,$xmlrpcString,$xmlrpcStruct),
		array($xmlrpcStruct,$xmlrpcInt,$xmlrpcString,$xmlrpcString,$xmlrpcStruct,$xmlrpcBoolean),
		array($xmlrpcStruct,$xmlrpcString,$xmlrpcString,$xmlrpcString,$xmlrpcStruct,$xmlrpcBoolean)
	);
/**
 * metaWeblog.newPost
 *
 * @param xmlrpcmsg XML-RPC Message
 *					0 blogid (string): Unique identifier of the blog the post will be added to.
 *						Currently ignored in b2evo, in favor of the category.
 *					1 username (string): Login for a Blogger user who has permission to edit the given
 *						post (either the user who originally created it or an admin of the blog).
 *					2 password (string): Password for said username.
 *					3 struct (struct)
 * 					4 publish (bool)
 * @param string post type 'post' or 'page'
 */
function mw_newpost($m, $item_type = 'post' )
{
	// CHECK LOGIN:
	/**
	 * @var User
	 */
	if( ! $current_User = & xmlrpcs_login( $m, 1, 2 ) )
	{	// Login failed, return (last) error:
		return xmlrpcs_resperror();
	}

	// GET BLOG:
	/**
	 * @var Blog
	 */
	if( ! ( $Collection = $Blog = & xmlrpcs_get_Blog( $m, 0 ) ) )
	{	// Login failed, return (last) error:
		return xmlrpcs_resperror();
	}

	$xcontent = $m->getParam(3);
	$contentstruct = xmlrpc_decode_recurse($xcontent);
	logIO( 'Decoded xcontent' );

	$status = 'published';
	if( isset($m->params[4]) )
	{	// getParam(4) is a flag for publish or draft
		$xstatus = $m->getParam(4);
		$xstatus->scalarval();
		$status = $xstatus ? 'published' : 'draft'; // might be overrided later
	}

	$cat_IDs = _mw_get_cat_IDs( $contentstruct, $Blog );
	$date = _mw_decode_date( $contentstruct );

	if( !empty($contentstruct['post_type']) && $contentstruct['post_type'] != $item_type )
	{	// Overwrite from struct
		$item_type = $contentstruct['post_type'];
	}

	$tags = isset($contentstruct['mt_keywords']) ? $contentstruct['mt_keywords'] : '';
	$content = isset($contentstruct['description']) ? $contentstruct['description'] : '';
	$excerpt = isset($contentstruct['mt_excerpt']) ? $contentstruct['mt_excerpt'] : '';
	$urltitle = isset($contentstruct['wp_slug']) ? $contentstruct['wp_slug'] : '';
	$featured = isset($contentstruct['sticky']) ? $contentstruct['sticky'] : 0;
	$order = isset($contentstruct['wp_page_order']) ? $contentstruct['wp_page_order'] : 0;
	$custom_fields = isset($contentstruct['custom_fields']) ? $contentstruct['custom_fields'] : '';
	$parent_ID = isset($contentstruct['wp_page_parent_id']) ? $contentstruct['wp_page_parent_id'] : '';

	$item_typ_ID = isset($contentstruct['wp_post_format']) ? $contentstruct['wp_post_format'] : 1;
	if( $item_type == 'page' )
	{	// Force post type 'page'
		global $DB;
		$page_type_ID = $DB->get_var( 'SELECT ityp_ID FROM T_items__type WHERE ityp_usage = "page" LIMIT 1' );
		if( ! empty( $page_type_ID ) )
		{
			$item_typ_ID = $page_type_ID;
		}
	}

	if( !empty( $contentstruct[$item_type.'_status'] ) )
	{	// Use WP status
		$status = wp_or_b2evo_item_status( $contentstruct[$item_type.'_status'], 'b2evo' );
	}

	if( !empty($content_struct['mt_text_more']) )
	{	// Add content extension
		$content .= '[teaserbreak]'.$content_struct['mt_text_more'];
	}
	//logIO( "Item content:\n".$content );

	if( !empty($content_struct['enclosure']) && is_array($content_struct['enclosure']) )
	{	// Add content extension
		$enclosure = $content_struct['enclosure'];
		if( isset($enclosure['url']) && isset($enclosure['length']) && isset($enclosure['type']) )
		{
			logIO( "Item enclosure\n".var_export($enclosure, true) );
			// TODO: sam2kb> Handle enclosures
		}
	}

	$comment_status = 'open';
	if( isset($contentstruct['mt_allow_comments']) )
	{
		if( ! $contentstruct['mt_allow_comments'] || in_array( $contentstruct['mt_allow_comments'], array(2,'closed') ) )
		{	// Comments disabled
			$comment_status = 'disabled';
		}
	}

	$params = array(
			'title'				=> $contentstruct['title'],
			'content'			=> $content,
			'cat_IDs'			=> $cat_IDs,
			'status'			=> $status,
			'date'				=> $date,
			'tags'				=> $tags,
			'excerpt'			=> $excerpt,
			'item_typ_ID'		=> $item_typ_ID,
			'comment_status'	=> $comment_status,
			'urltitle'			=> $urltitle,
			'featured'			=> $featured,
			'order'				=> $order,
			'parent_ID'			=> $parent_ID,
			'custom_fields'		=> $custom_fields,
		);

	// COMPLETE VALIDATION & INSERT:
	return xmlrpcs_new_item( $params, $Blog );
}


$mweditpost_doc='Edits a post, blogger-api like, +title +category +postdate';
$mweditpost_sig = array(
		array($xmlrpcBoolean,$xmlrpcInt,$xmlrpcString,$xmlrpcString,$xmlrpcStruct),
		array($xmlrpcBoolean,$xmlrpcInt,$xmlrpcString,$xmlrpcString,$xmlrpcStruct,$xmlrpcBoolean),
		array($xmlrpcBoolean,$xmlrpcString,$xmlrpcString,$xmlrpcString,$xmlrpcStruct,$xmlrpcBoolean),
	);
/**
 * metaWeblog.editPost
 *
 * @see http://www.xmlrpc.com/metaWeblogApi#basicEntrypoints
 *
 * @param xmlrpcmsg XML-RPC Message
 *					0 postid (string): Unique identifier of the post to edit
 *					1 username (string): Login for a Blogger user who has permission to edit the given
 *						post (either the user who originally created it or an admin of the blog).
 *					2 password (string): Password for said username.
 *					3 struct (struct)
 *					4 publish (bool)
 * @param string post type 'post' or 'page'
 */
function mw_editpost( $m, $item_type = 'post' )
{
	// CHECK LOGIN:
	/**
	 * @var User
	 */
	if( ! $current_User = & xmlrpcs_login( $m, 1, 2 ) )
	{	// Login failed, return (last) error:
		return xmlrpcs_resperror();
	}

	// GET POST:
	/**
	 * @var Item
	 */
	if( ! $edited_Item = & xmlrpcs_get_Item( $m, 0 ) )
	{	// Failed, return (last) error:
		return xmlrpcs_resperror();
	}

	// We need to be able to edit this post:
	if( ! check_user_perm( 'item_post!CURSTATUS', 'edit', false, $edited_Item ) )
	{
		return xmlrpcs_resperror( 3 ); // Permission denied
	}

	$xcontent = $m->getParam(3);
	$contentstruct = xmlrpc_decode_recurse($xcontent);
	logIO('Decoded xcontent');

	if( isset($m->params[4]) )
	{	// getParam(4) is a flag for publish or draft
		$xstatus = $m->getParam(4);
		$xstatus = $xstatus->scalarval();
		$status = $xstatus ? 'published' : 'draft'; // might be overrided later
	}

	$cat_IDs = _mw_get_cat_IDs( $contentstruct, $edited_Item->get_Blog(), true /* empty is ok */ );
	$date = _mw_decode_date( $contentstruct );

	if( !empty($contentstruct['post_type']) && $contentstruct['post_type'] != $item_type )
	{	// Overwrite from struct
		$item_type = $contentstruct['post_type'];
	}

	// Don't overwrite if not set
	$tags = isset($contentstruct['mt_keywords']) ? $contentstruct['mt_keywords'] : NULL;
	$content = isset($contentstruct['description']) ? $contentstruct['description'] : NULL;
	$excerpt = isset($contentstruct['mt_excerpt']) ? $contentstruct['mt_excerpt'] : NULL;
	$urltitle = isset($contentstruct['wp_slug']) ? $contentstruct['wp_slug'] : NULL;
	$parent_ID = isset($contentstruct['wp_page_parent_id']) ? $contentstruct['wp_page_parent_id'] : NULL;
	$author_ID = isset($contentstruct['wp_author_id']) ? $contentstruct['wp_author_id'] : NULL;
	$featured = isset($contentstruct['sticky']) ? $contentstruct['sticky'] : NULL;
	$order = isset($contentstruct['wp_page_order']) ? $contentstruct['wp_page_order'] : NULL;
	$custom_fields = isset($contentstruct['custom_fields']) ? $contentstruct['custom_fields'] : NULL;
	$item_typ_ID = isset($contentstruct['wp_post_format']) ? $contentstruct['wp_post_format'] : NULL;

	if( isset($contentstruct[$item_type.'_status']) )
	{
		$status = wp_or_b2evo_item_status( $contentstruct[$item_type.'_status'], 'b2evo' );
	}

	if( isset($content_struct['mt_text_more']) )
	{	// Add content extension
		$content .= '[teaserbreak]'.$content_struct['mt_text_more'];
	}
	//logIO( "Item content:\n".$content );

	if( !empty($content_struct['enclosure']) && is_array($content_struct['enclosure']) )
	{	// Add content extension
		$enclosure = $content_struct['enclosure'];
		if( isset($enclosure['url']) && isset($enclosure['length']) && isset($enclosure['type']) )
		{
			logIO( "Item enclosure\n".var_export($enclosure, true) );
			// TODO: sam2kb> Handle enclosures
		}
	}

	$comment_status = ''; // Don't overwrite if not set
	if( isset($contentstruct['mt_allow_comments']) )
	{
		if( ! $contentstruct['mt_allow_comments'] || in_array( $contentstruct['mt_allow_comments'], array(2,'closed') ) )
		{	// Comments disabled
			$comment_status = 'disabled';
		}
		else
		{
			$comment_status = 'open';
		}
	}

	$params = array(
			'title'				=> $contentstruct['title'],
			'content'			=> $content,
			'cat_IDs'			=> $cat_IDs,
			'status'			=> $status,
			'date'				=> $date,
			'tags'				=> $tags,
			'excerpt'			=> $excerpt,
			'item_typ_ID'		=> $item_typ_ID,
			'comment_status'	=> $comment_status,
			'urltitle'			=> $urltitle,
			'parent_ID'			=> $parent_ID,
			'author_ID'			=> $author_ID,
			'featured'			=> $featured,
			'order'				=> $order,
			'custom_fields'		=> $custom_fields,
		);

	// COMPLETE VALIDATION & INSERT:
	return xmlrpcs_edit_item( $edited_Item, $params );


	/*
	// Time to perform trackbacks NB NOT WORKING YET
	//
	// NB Requires a change to the _trackback library
	//
	// function trackbacks( $post_trackbacks, $content, $post_title, $post_ID )

	// first extract these from posting as post_trackbacks array, then rest is easy
	// 	<member>
	//		<name>mt_tb_ping_urls</name>
	//	<value><array><data>
	//		<value><string>http://archive.scripting.com/2005/04/17</string></value>
	//	</data></array></value>
	//	</member>
	// First check that trackbacks are allowed - mt_allow_pings
	$trackback_ok = 0;
	$trackbacks = array();
	$trackback_ok = $contentstruct['mt_allow_pings'];
	logIO("Trackback OK  ...".$trackback_ok);
	if ($trackback_ok == 1)
	{
		$trackbacks = $contentstruct['mt_tb_ping_urls'];
		logIO("Trackback url 0  ...".$trackbacks[0]);
		$no_of_trackbacks = count($trackbacks);
		logIO("Number of Trackbacks  ...".$no_of_trackbacks);
		if ($no_of_trackbacks > 0)
		{
			logIO("Calling Trackbacks  ...");
			load_funcs('comments/_trackback.funcs.php');
 			$result = trackbacks( $trackbacks, $content, $post_title, $post_ID );
			logIO("Returned from  Trackbacks  ...");
 		}

	}
	*/
}


$mwgetcats_sig =  array(
		array($xmlrpcStruct,$xmlrpcInt,$xmlrpcString,$xmlrpcString),
		array($xmlrpcStruct,$xmlrpcString,$xmlrpcString,$xmlrpcString)
	);
$mwgetcats_doc = 'Get categories of a post, MetaWeblog API-style';
/**
 * metaWeblog.getCategories
 *
 * @see http://www.xmlrpc.com/metaWeblogApi#metawebloggetcategories
 *
 * @param xmlrpcmsg XML-RPC Message
 *					0 blogid (string): Unique identifier of the blog the post will be added to.
 *						Currently ignored in b2evo, in favor of the category.
 *					1 username (string): Login for a Blogger user who has permission to edit the given
 *						post (either the user who originally created it or an admin of the blog).
 *					2 password (string): Password for said username.
 */
function mw_getcategories( $m )
{
	return _wp_mw_getcategories ( $m ) ;
}


$metawebloggetrecentposts_doc = 'fetches X most recent posts, blogger-api like';
$metawebloggetrecentposts_sig = array(
		array($xmlrpcArray,$xmlrpcInt,$xmlrpcString,$xmlrpcString,$xmlrpcInt),
		array($xmlrpcArray,$xmlrpcString,$xmlrpcString,$xmlrpcString,$xmlrpcInt),
	);
/**
 * metaWeblog.getRecentPosts
 *
 * @see http://xmlrpc.scripting.com/metaWeblogApi.html#metawebloggetrecentposts
 *
 * @param xmlrpcmsg XML-RPC Message
 *					0 blogid (string): Unique identifier of the blog the post will be added to.
 *						Currently ignored in b2evo, in favor of the category.
 *					1 username (string): Login for a Blogger user who has permission to edit the given
 *						post (either the user who originally created it or an admin of the blog).
 *					2 password (string): Password for said username.
 *					3 numposts (integer): number of posts to retrieve.
 */
function mw_getrecentposts( $m )
{
	// CHECK LOGIN:
	/**
	 * @var User
	 */
	if( ! $current_User = & xmlrpcs_login( $m, 1, 2 ) )
	{	// Login failed, return (last) error:
		return xmlrpcs_resperror();
	}

	// GET BLOG:
	/**
	 * @var Blog
	 */
	if( ! ( $Collection = $Blog = & xmlrpcs_get_Blog( $m, 0 ) ) )
	{	// Login failed, return (last) error:
		return xmlrpcs_resperror();
	}

	$limit = $m->getParam(3);
	$limit = abs($limit->scalarval());

	$items = xmlrpc_get_items( array(
			'limit' => $limit,
		), $Blog );

	if( empty($items) )
	{
		return new xmlrpcresp( new xmlrpcval( array(), 'array' ) );
	}

	$data = array();
	foreach( $items as $item )
	{
		$data[] = new xmlrpcval( $item, 'struct' );
	}

	logIO( 'OK.' );
	return new xmlrpcresp( new xmlrpcval( $data, 'array' ) );
}


$mwgetusersblogs_doc = 'returns the user\'s blogs - this is a dummy function, just so that BlogBuddy and other blogs-retrieving apps work';
$mwgetusersblogs_sig = array(
		array($xmlrpcArray,$xmlrpcInt,$xmlrpcString,$xmlrpcString),
		array($xmlrpcArray,$xmlrpcString,$xmlrpcString,$xmlrpcString)
	);
/**
 * metaweblog.getUsersBlogs returns information about all the blogs a given user is a member of.
 *
 * Data is returned as an array of <struct>s containing the ID (blogid), name (blogName),
 * and URL (url) of each blog.
 *
 * Non official: Also return a boolean stating wether or not the user can edit th eblog templates
 * (isAdmin).
 *
 * @see http://www.xmlrpc.com/stories/storyReader$2460
 *
 * @param xmlrpcmsg XML-RPC Message
 *					0 appkey (string): Unique identifier/passcode of the application sending the post.
 *						(See access info {@link http://www.blogger.com/developers/api/1_docs/#access} .)
 *					1 username (string): Login for the Blogger user who's blogs will be retrieved.
 *					2 password (string): Password for said username.
 *						(currently not required by b2evo)
 * @return xmlrpcresp XML-RPC Response, an array of <struct>s containing for each blog:
 *					- ID (blogid),
 *					- name (blogName),
 *					- URL (url),
 *					- bool: can user edit template? (isAdmin).
 */
function mw_getusersblogs($m)
{
	return _wp_or_blogger_getusersblogs( 'blogger', $m );
}


$mwgetpost_doc = 'Fetches a post, blogger-api like';
$mwgetpost_sig = array(
		array($xmlrpcStruct,$xmlrpcInt,$xmlrpcString,$xmlrpcString),
		array($xmlrpcStruct,$xmlrpcString,$xmlrpcString,$xmlrpcString),
	);
/**
 * metaWeblog.getPost retieves a given post.
 *
 * @see http://www.xmlrpc.com/metaWeblogApi#basicEntrypoints
 *
 * @param xmlrpcmsg XML-RPC Message
 *					0 postid (string): Unique identifier of the post
 *					1 username (string): Login for a Blogger user who has permission to edit the given
 *						post (either the user who originally created it or an admin of the blog).
 *					2 password (string): Password for said username.
 * @return xmlrpcresp XML-RPC Response
 */
function mw_getpost($m)
{
	// CHECK LOGIN:
	/**
	 * @var User
	 */
	if( ! $current_User = & xmlrpcs_login( $m, 1, 2 ) )
	{	// Login failed, return (last) error:
		return xmlrpcs_resperror();
	}

	// GET POST:
	/**
	 * @var Item
	 */
	if( ! $edited_Item = & xmlrpcs_get_Item( $m, 0 ) )
	{	// Failed, return (last) error:
		return xmlrpcs_resperror();
	}

	// CHECK PERMISSION:
	if( ! xmlrpcs_can_view_item( $edited_Item, $current_User ) )
	{	// Permission denied
		return xmlrpcs_resperror( 3 );	// User error 3
	}

	$item = _wp_mw_get_item_struct($edited_Item);

	logIO( 'OK.' );
	return new xmlrpcresp( new xmlrpcval( $item, 'struct' ) );
}


$mwdeletepost_doc = 'Deletes a post, blogger-api like';
$mwdeletepost_sig = array(array($xmlrpcBoolean, $xmlrpcString, $xmlrpcString, $xmlrpcString, $xmlrpcString, $xmlrpcBoolean));
/**
 * metaWeblog.deletePost deletes a given post.
 *
 * This API call is not documented on
 * {@link http://www.blogger.com/developers/api/1_docs/}
 * @see http://www.xmlrpc.com/stories/storyReader$2460
 *
 * @param xmlrpcmsg XML-RPC Message
 *					0 appkey (string): Unique identifier/passcode of the application sending the post.
 *						(See access info {@link http://www.blogger.com/developers/api/1_docs/#access} .)
 *					1 postid (string): Unique identifier of the post to be deleted.
 *					2 username (string): Login for a Blogger user who has permission to edit the given
 *						post (either the user who originally created it or an admin of the blog).
 *					3 password (string): Password for said username.
 * @return xmlrpcresp XML-RPC Response
 */
function mw_deletepost($m)
{
	// CHECK LOGIN:
	if( ! $current_User = & xmlrpcs_login( $m, 2, 3 ) )
	{	// Login failed, return (last) error:
		return xmlrpcs_resperror();
	}

	// GET POST:
	/**
	 * @var Item
	 */
	if( ! $edited_Item = & xmlrpcs_get_Item( $m, 1 ) )
	{	// Failed, return (last) error:
		return xmlrpcs_resperror();
	}

	return xmlrpcs_delete_item( $edited_Item );
}


$xmlrpc_procs['metaWeblog.newMediaObject'] = array( // OK
				'function' => 'mw_newmediaobject',
				'signature' => $mwnewMediaObject_sig,
				'docstring' => $mwnewMediaObject_doc);

$xmlrpc_procs['metaWeblog.newPost'] = array( // Incomplete (minor): handle 'enclosure', 'custom_fields' in WP
				'function' => 'mw_newpost',
				'signature' => $mwnewpost_sig,
				'docstring' => $mwnewpost_doc );

$xmlrpc_procs['metaWeblog.editPost'] = array( // Incomplete (minor): handle 'enclosure', 'custom_fields' in WP
				'function' => 'mw_editpost',
				'signature' => $mweditpost_sig,
				'docstring' => $mweditpost_doc );

$xmlrpc_procs['metaWeblog.getPost'] = array( // OK
				'function' => 'mw_getpost',
				'signature' => $mwgetpost_sig,
				'docstring' => $mwgetpost_doc );

$xmlrpc_procs['metaWeblog.getCategories'] = array(
				'function' => 'mw_getcategories',
				'signature' => $mwgetcats_sig,
				'docstring' => $mwgetcats_doc );

$xmlrpc_procs['metaWeblog.getRecentPosts'] = array(
				'function' => 'mw_getrecentposts',
				'signature' => $metawebloggetrecentposts_sig,
				'docstring' => $metawebloggetrecentposts_doc );

// Blogger aliases, as in http://www.xmlrpc.com/stories/storyReader$2460

$xmlrpc_procs['metaWeblog.deletePost'] = array(
				'function' => 'mw_deletepost',
				'signature' => $mwdeletepost_sig,
				'docstring' => $mwdeletepost_doc );

$xmlrpc_procs['metaWeblog.getUsersBlogs'] = array(
				'function' => 'mw_getusersblogs',
				'signature' => $mwgetusersblogs_sig,
				'docstring' => $mwgetusersblogs_doc );

?>