diff options
author | Arno Richter <oelna@oelna.de> | 2022-12-13 22:27:21 +0100 |
---|---|---|
committer | Arno Richter <oelna@oelna.de> | 2022-12-13 22:27:44 +0100 |
commit | 0b075f3ea2616cfde4d976199a69ba631174a336 (patch) | |
tree | bb1feef58d9c714f6c3fb0b75f1e52b0181cf2af /lib/xmlrpc.php | |
parent | f0e3ff408db8ee40611f75cdf96892f90034bd60 (diff) | |
download | microblog-0b075f3ea2616cfde4d976199a69ba631174a336.tar.gz microblog-0b075f3ea2616cfde4d976199a69ba631174a336.tar.bz2 microblog-0b075f3ea2616cfde4d976199a69ba631174a336.zip |
gave up and sorted files into a directory structure. made snippets for header, nav and footer. made it easier to add additional css files as themes. prepare a little for addition of a real template engine. added a css reset file to share between themes, if warranted.
Diffstat (limited to 'lib/xmlrpc.php')
-rw-r--r-- | lib/xmlrpc.php | 428 |
1 files changed, 428 insertions, 0 deletions
diff --git a/lib/xmlrpc.php b/lib/xmlrpc.php new file mode 100644 index 0000000..47948eb --- /dev/null +++ b/lib/xmlrpc.php @@ -0,0 +1,428 @@ +<?php + +$request_xml = file_get_contents("php://input"); + +// check prerequisites +if(!function_exists('xmlrpc_server_create')) { exit('No XML-RPC support detected!'); } +if(empty($request_xml)) { exit('XML-RPC server accepts POST requests only.'); } + +// load config +require_once(__DIR__.DIRECTORY_SEPARATOR.'config.php'); +$logfile = __DIR__.DS.'log.txt'; + +if(!function_exists('str_starts_with')) { + function str_starts_with($haystack, $needle) { + if(empty($needle)) return true; + return mb_substr($haystack, 0, mb_strlen($needle)) === $needle; + } +} + +function check_credentials($username, $password) { + global $config; + + $xmlrpc_auth = $config['admin_pass']; + if(!empty($config['app_token'])) { + $xmlrpc_auth = $config['app_token']; + } + + if($username == $config['admin_user'] && $password == $xmlrpc_auth) { + return true; + } else { + return false; + } +} + +function say_hello($method_name, $args) { + return 'Hello'; +} + +function make_post($post, $method='metaWeblog') { + global $config; + + $date_created = date('Y-m-d\TH:i:s', $post['post_timestamp']).$config['local_time_offset']; + $date_created_gmt = gmdate('Y-m-d\TH:i:s', $post['post_timestamp']).'Z'; + if(!empty($post['post_edited']) && !is_null($post['post_edited'])) { + $date_modified = date('Y-m-d\TH:i:s', $post['post_edited']).$config['local_time_offset']; + $date_modified_gmt = gmdate('Y-m-d\TH:i:s', $post['post_edited']).'Z'; + } else { + $date_modified = date('Y-m-d\TH:i:s', 0).$config['local_time_offset']; + $date_modified = null; + $date_modified_gmt = gmdate('Y-m-d\TH:i:s', 0).'Z'; + } + + @xmlrpc_set_type($date_created, 'datetime'); + @xmlrpc_set_type($date_created_gmt, 'datetime'); + @xmlrpc_set_type($date_modified, 'datetime'); + @xmlrpc_set_type($date_modified_gmt, 'datetime'); + + if(str_starts_with($method, 'microblog')) { + // convert the post format to a microblog post + // similar to metaWeblog.recentPosts but with offset parameter for paging, + // consistent field names + $mb_post = [ + 'id' => (int) $post['id'], + 'date_created' => $date_created, + 'date_modified' => $date_modified, + 'permalink' => $config['url'].'/'.$post['id'], + 'title' => '', + 'description' => ($post['post_content']), + 'categories' => [], + 'post_status' => 'published', + 'author' => [ + 'name' => $config['microblog_account'], + 'username' => $config['admin_user'] + ] + ]; + + return $mb_post; + } else { + // convert the post format to a standard metaWeblog post + $mw_post = [ + 'postid' => (int) $post['id'], + 'title' => '', + 'description' => ($post['post_content']), // Post content + 'link' => $config['url'].'/'.$post['id'], // Post URL + // string userid†: ID of post author. + 'dateCreated' => $date_created, + 'date_created_gmt' => $date_created_gmt, + 'date_modified' => $date_modified, + 'date_modified_gmt' => $date_modified_gmt, + // string wp_post_thumbnail†+ 'permalink' => $config['url'].'/'.$post['id'], // Post URL, equivalent to link. + 'categories' => [], // Names of categories assigned to the post. + 'mt_keywords' => '', // Names of tags assigned to the post. + 'mt_excerpt' => '', + 'mt_text_more' => '', // Post "Read more" text. + ]; + + return $mw_post; + } +} + +function mw_get_users_blogs($method_name, $args) { + global $config; + + list($_, $username, $password) = $args; + + if(!check_credentials($username, $password)) { + return [ + 'faultCode' => 403, + 'faultString' => 'Incorrect username or password.' + ]; + } + + $bloginfo = [ + 'blogid' => '1', + 'url' => $config['url'], + 'blogName' => (empty($config['microblog_account']) ? "" : $config['microblog_account'] . "'s ").' microblog', + ]; + + return $bloginfo; +} + +function mw_get_categories($method_name, $args) { + + list($_, $username, $password) = $args; + + if(!check_credentials($username, $password)) { + return [ + 'faultCode' => 403, + 'faultString' => 'Incorrect username or password.' + ]; + } + + // we don't support categories, so only return a fake one + if($method_name == 'microblog.getCategories') { + $categories = [ + /* + [ + 'id' => '1', + 'name' => 'default', + ] + */ + ]; + } else { + $categories = [ + /* + [ + 'description' => 'Default', + 'htmlUrl' => '', + 'rssUrl' => '', + 'title' => 'default', + 'categoryid' => '1', + ] + */ + ]; + } + + return $categories; +} + +function mw_get_user_info($method_name, $args) { + global $config; + + list($_, $username, $password) = $args; + + if(!check_credentials($username, $password)) { + return [ + 'faultCode' => 403, + 'faultString' => 'Incorrect username or password.' + ]; + } + + $userinfo = [ + 'userid' => '1', + 'firstname' => '', + 'lastname' => '', + 'nickname' => $config['microblog_account'], + 'email' => '', + 'url' => $config['url'], + ]; + + return $userinfo; +} + +function mw_get_recent_posts($method_name, $args) { + + list($_, $username, $password, $amount) = $args; + $offset = 0; + if($method_name == 'microblog.getPosts' && !empty($args[4])) { + $offset = $args[4]; + } + + if(!check_credentials($username, $password)) { + return [ + 'faultCode' => 403, + 'faultString' => 'Incorrect username or password.' + ]; + } + + if(!$amount) $amount = 25; + $amount = min($amount, 200); // cap the max available posts at 200 (?) + + $posts = db_select_posts(null, $amount, 'asc', $offset); + if(empty($posts)) return []; + + // call make_post() on all items + $mw_posts = array_map('make_post', $posts, array_fill(0, count($posts), $method_name)); + + return $mw_posts; +} + +function mw_get_post($method_name, $args) { + + list($post_id, $username, $password) = $args; + + if(!check_credentials($username, $password)) { + return [ + 'faultCode' => 403, + 'faultString' => 'Incorrect username or password.' + ]; + } + + $post = db_select_post($post_id); + if($post) { + if($method_name == 'microblog.getPost') { + $mw_post = make_post($post, $method_name); + } else { + $mw_post = make_post($post); + } + + return $mw_post; + } else { + return [ + 'faultCode' => 400, + 'faultString' => 'Could not fetch post.' + ]; + } +} + +function mw_new_post($method_name, $args) { + + // blog_id, unknown, unknown, array of post content, unknown + list($blog_id, $username, $password, $content, $_) = $args; + + if(!check_credentials($username, $password)) { + return [ + 'faultCode' => 403, + 'faultString' => 'Incorrect username or password.' + ]; + } + + if($method_name == 'microblog.newPost') { + $post = [ + // 'post_title' => $content['title'], + 'post_content' => $content['description'], + 'post_timestamp' => time(), + // 'post_categories' => $content['categories'], + // 'post_status' => $content['post_status'], + ]; + + // use a specific timestamp, if provided + if(isset($content['date_created'])) { + $post['post_timestamp'] = $content['date_created']->timestamp; + } + } else { + $post = [ + // 'post_hp' => $content['flNotOnHomePage'], + 'post_timestamp' => time(), + // 'post_title' => $content['title'], + 'post_content' => $content['description'], + // 'post_url' => $content['link'], + ]; + + // use a specific timestamp, if provided + if(isset($content['dateCreated'])) { + $post['post_timestamp'] = $content['dateCreated']->timestamp; + } + } + + $insert_id = db_insert($post['post_content'], $post['post_timestamp']); + if($insert_id && $insert_id > 0) { + // success + rebuild_feeds(); + + return (int) $insert_id; + } else { + // insert failed + // error codes: https://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php + // more error codes? https://github.com/zendframework/zend-xmlrpc/blob/master/src/Fault.php + return [ + 'faultCode' => 400, + 'faultString' => 'Could not create post.' + ]; + } +} + +function mw_edit_post($method_name, $args) { + + // post_id, unknown, unknown, array of post content + list($post_id, $username, $password, $content, $_) = $args; + + if(!check_credentials($username, $password)) { + return [ + 'faultCode' => 403, + 'faultString' => 'Incorrect username or password.' + ]; + } + + if($method_name == 'microblog.editPost') { + $post = [ + // 'post_title' => $content['title'], + 'post_content' => $content['description'], + 'post_timestamp' => null, + // 'post_categories' => $content['categories'], + // 'post_status' => $content['post_status'], + ]; + + if(!empty($content['date_created'])) { + $post['post_timestamp'] = $content['date_created']->timestamp; + } + } else { + $post = [ + // 'post_hp' => $content['flNotOnHomePage'], + // 'post_title' => $content['title'], + 'post_timestamp' => null, + 'post_content' => $content['description'], + // 'post_url' => $content['link'], + ]; + + if(!empty($content['dateCreated'])) { + $post['post_timestamp'] = $content['dateCreated']->timestamp; + } + } + + $update = db_update($post_id, $post['post_content'], $post['post_timestamp']); + if($update && $update > 0) { + // success + rebuild_feeds(); + + return true; + } else { + return [ + 'faultCode' => 400, + 'faultString' => 'Could not write post update.' + ]; + } +} + +function mw_delete_post($method_name, $args) { + + if($method_name == 'microblog.deletePost') { + list($post_id, $username, $password) = $args; + } else { + // blogger.deletePost + list($_, $post_id, $username, $password, $_) = $args; + } + + if(!check_credentials($username, $password)) { + return [ + 'faultCode' => 403, + 'faultString' => 'Incorrect username or password.' + ]; + } + + $success = db_delete($post_id); + if($success > 0) { + rebuild_feeds(); + + return true; + } else { + return [ + 'faultCode' => 400, + 'faultString' => 'Could not delete post.' + ]; + } +} + +// https://codex.wordpress.org/XML-RPC_MetaWeblog_API +// https://community.devexpress.com/blogs/theprogressbar/metablog.ashx +// idea: http://www.hixie.ch/specs/pingback/pingback#TOC3 +$server = xmlrpc_server_create(); +xmlrpc_server_register_method($server, 'demo.sayHello', 'say_hello'); + +xmlrpc_server_register_method($server, 'blogger.getUsersBlogs', 'mw_get_users_blogs'); +xmlrpc_server_register_method($server, 'blogger.getUserInfo', 'mw_get_user_info'); +xmlrpc_server_register_method($server, 'blogger.deletePost', 'mw_delete_post'); + +xmlrpc_server_register_method($server, 'metaWeblog.getCategories', 'mw_get_categories'); +xmlrpc_server_register_method($server, 'metaWeblog.getRecentPosts', 'mw_get_recent_posts'); +xmlrpc_server_register_method($server, 'metaWeblog.newPost', 'mw_new_post'); +xmlrpc_server_register_method($server, 'metaWeblog.editPost', 'mw_edit_post'); +xmlrpc_server_register_method($server, 'metaWeblog.getPost', 'mw_get_post'); +// xmlrpc_server_register_method($server, 'metaWeblog.newMediaObject', 'mw_new_mediaobject'); + +// non-standard convenience? +xmlrpc_server_register_method($server, 'metaWeblog.getPosts', 'mw_get_recent_posts'); +xmlrpc_server_register_method($server, 'metaWeblog.deletePost', 'mw_delete_post'); + +// micro.blog API methods (currently just using the metaWeblog functions) +// https://help.micro.blog/t/micro-blog-xml-rpc-api/108 +xmlrpc_server_register_method($server, 'microblog.getCategories', 'mw_get_categories'); +xmlrpc_server_register_method($server, 'microblog.getPosts', 'mw_get_recent_posts'); +xmlrpc_server_register_method($server, 'microblog.getPost', 'mw_get_post'); +xmlrpc_server_register_method($server, 'microblog.newPost', 'mw_new_post'); +xmlrpc_server_register_method($server, 'microblog.editPost', 'mw_edit_post'); +xmlrpc_server_register_method($server, 'microblog.deletePost', 'mw_delete_post'); +// xmlrpc_server_register_method($server, 'microblog.newMediaObject', 'mw_new_mediaobject'); + +// micro.blog pages are not supported +/* +xmlrpc_server_register_method($server, 'microblog.getPages', 'say_hello'); +xmlrpc_server_register_method($server, 'microblog.getPage', 'say_hello'); +xmlrpc_server_register_method($server, 'microblog.newPage', 'say_hello'); +xmlrpc_server_register_method($server, 'microblog.editPage', 'say_hello'); +xmlrpc_server_register_method($server, 'microblog.deletePage', 'say_hello'); +*/ + +// https://docstore.mik.ua/orelly/webprog/pcook/ch12_08.htm +$response = xmlrpc_server_call_method($server, $request_xml, null, [ + 'escaping' => 'markup', + 'encoding' => 'UTF-8' +]); + +if($response) { + header('Content-Type: text/xml; charset=utf-8'); + // error_log($request_xml."\n\n".$response."\n\n", 3, $logfile); + echo($response); +} |