From 8d2ec131709db79e13f90032a1318a11f622ad73 Mon Sep 17 00:00:00 2001 From: Johnnie Lamar Odom II Date: Sat, 12 Oct 2019 20:25:27 -0500 Subject: [PATCH] Sevenoaks Release. Lots of bugfixes from Gordon. Added Blocklists. Enough functionality now exists to build proper clients. --- code/client/index.php | 403 +++++++++++++++++---------------- code/clientlib/3.00/plan.php | 7 +- code/clientlib/3.00/send.php | 167 +++++++------- code/clientlib/3.00/watchlist.php | 143 ------------ code/config.php | 1 + code/lib/Online.php | 206 ++++++++++------- code/lib/Planwatch.php | 39 ++-- code/lib/Planworld.php | 7 +- code/lib/Snoop.php | 432 ++++++++++++++++++------------------ code/lib/User.php | 152 ++++++++++++- code/scaffolding/adduser.php | 2 +- code/scaffolding/debug_client.html | 36 +-- setup/planworld_mysql_db_create.sql | 9 +- setup/readme.md | 97 +++++--- 14 files changed, 909 insertions(+), 792 deletions(-) delete mode 100644 code/clientlib/3.00/watchlist.php diff --git a/code/client/index.php b/code/client/index.php index 167eb2b..916b946 100644 --- a/code/client/index.php +++ b/code/client/index.php @@ -15,25 +15,23 @@ */ - /* INITIAL SETUP - Variables here may need to be modified. */ /* Main Variables - May require editing during setup. */ $pathClientBase = dirname(__FILE__); $pathClientLibBase = dirname(__FILE__) . '/../clientlib'; $arraySupportedFormats = array('txt', 'xml', 'json'); -$pathClientPrefix = '/web/path/to/this/directory/from/the/server/url'; -/* If your server serves this file from http://www.mycorp.com/stuff/planworld/client then the above would be /stuff/planworld/client */ +$pathClientPrefix = '/client'; /* TODO: Do we need a separate config file for the client API? Things like $arraySupportedFormats and the client path? */ /* Import JSON implementation if PHP is before 5.2 */ if (!defined('PHP_VERSION_ID')) { - $version = explode('.', PHP_VERSION); - define('PHP_VERSION_ID', ($version[0] * 10000 + $version[1] * 100 + $version[2])); + $version = explode('.', PHP_VERSION); + define('PHP_VERSION_ID', ($version[0] * 10000 + $version[1] * 100 + $version[2])); } if(PHP_VERSION_ID < 50200){ - include_once('oldjson.php'); + include_once('oldjson.php'); } @@ -45,34 +43,34 @@ if(PHP_VERSION_ID < 50200){ Return: Void. */ function statusCode($code){ - switch($code){ - case 200: - header('HTTP/1.1 200 OK'); - break; - case 201: - header('HTTP/1.1 201 Created'); - break; - case 400: - header('HTTP/1.1 400 Bad Request'); - break; - case 401: - header('HTTP/1.1 401 Not Authorized'); - break; - case 403: - header('HTTP/1.1 403 Forbidden'); - break; - case 404: - header('HTTP/1.1 404 Not Found');; - break; - case 501: - header('HTTP/1.1 501 Not Implemented'); - break; - case 503: - header('HTTP/1.1 503 Service Unavailable'); - break; - default: - header('HTTP/1.1 404 Not Found'); - } + switch($code){ + case 200: + header('HTTP/1.1 200 OK'); + break; + case 201: + header('HTTP/1.1 201 Created'); + break; + case 400: + header('HTTP/1.1 400 Bad Request'); + break; + case 401: + header('HTTP/1.1 401 Not Authorized'); + break; + case 403: + header('HTTP/1.1 403 Forbidden'); + break; + case 404: + header('HTTP/1.1 404 Not Found');; + break; + case 501: + header('HTTP/1.1 501 Not Implemented'); + break; + case 503: + header('HTTP/1.1 503 Service Unavailable'); + break; + default: + header('HTTP/1.1 404 Not Found'); + } } @@ -82,18 +80,18 @@ function statusCode($code){ http://stackoverflow.com/questions/1397036/how-to-convert-array-to-simplexml Where $key = 'row'; the original code had $key = 'row_'. $key; */ function array_to_xml( $data, $xml_data ) { - foreach( $data as $key => $value ) { - if( is_numeric($key) ){ - $key = 'row'; - } - if( is_array($value) ) { - $subnode = $xml_data->addChild($key); - array_to_xml($value, $subnode); - } else { - $xml_data->addChild("$key",htmlspecialchars("$value")); - } - } - return $xml_data; + foreach( $data as $key => $value ) { + if( is_numeric($key) ){ + $key = 'row'; + } + if( is_array($value) ) { + $subnode = $xml_data->addChild($key); + array_to_xml($value, $subnode); + } else { + $xml_data->addChild("$key",htmlspecialchars("$value")); + } + } + return $xml_data; } @@ -103,161 +101,178 @@ function array_to_xml( $data, $xml_data ) { /* Receive the API request. This works in tandem with an .htaccess file in the same directory. */ if(!empty($_REQUEST['apiurl'])){ - /* Key Variables. */ - $stringToken = null; - $floatClientVersion = 0; - $stringCategory = ''; - $stringPlanworldVerb = ''; - $stringPlanworldVerbSuffix = 'Get'; - $stringFormat = 'txt'; - $arrayArguments = array(); - $postData = null; - $boolValidUrl = true; - $boolSecurityCheckPassed = true; - $urlPath = ''; - $urlQuery = ''; - $pathArray = array(); + /* Key Variables. */ + $stringToken = null; + $floatClientVersion = 0; + $stringCategory = ''; + $stringPlanworldVerb = ''; + $stringPlanworldVerbSuffix = 'Get'; + $stringFormat = 'txt'; + $arrayArguments = array(); + $postData = null; + $boolValidUrl = true; + $boolSecurityCheckPassed = true; + $urlPath = ''; + $urlQuery = ''; + $pathArray = array(); - /* Url Parsing, Key Variable Assignment, and Element Validation */ - $urlArray = parse_url($_REQUEST['apiurl']); - if(array_key_exists('path', $urlArray)){ - $urlPath = $urlArray['path']; - } - /* Get Token */ - if(array_key_exists('token', $_REQUEST)){ - if(ctype_alnum($_REQUEST['token'])){ /* Do some proper validation when validators have been built. */ - $stringToken = strval($_REQUEST['token']); - if(file_exists($pathClientLibBase . '/security.php')){ - include_once($pathClientLibBase . '/security.php'); - $boolSecurityCheckPassed = securityCheck($_SERVER['REMOTE_ADDR'], $stringToken); - } - } - } - /*Get Format */ - $urlPath = trim(rtrim($urlPath,'/')); - $intFormatLastPeriod = strrpos($urlPath, '.'); - $intFormatLastSlash = strrpos($urlPath, '/'); - if((($intFormatLastPeriod - $intFormatLastSlash) > 0) && ($intFormatLastPeriod !== false)){ - $stringFormat = strtolower(ltrim(substr($urlPath, $intFormatLastPeriod), '.')); - $urlPath = rtrim(substr($urlPath, 0, $intFormatLastPeriod), '/'); - if(!(in_array($stringFormat, $arraySupportedFormats))){ - $stringFormat = 'txt'; - } - } - /* Examine for Basic Elements */ - /* Check for off-by-one here. */ + /* Url Parsing, Key Variable Assignment, and Element Validation */ + $urlArray = parse_url($_REQUEST['apiurl']); + if(array_key_exists('path', $urlArray)){ + $urlPath = $urlArray['path']; + } + /* Get Token */ + if(array_key_exists('token', $_REQUEST)){ + if(ctype_alnum($_REQUEST['token'])){ /* Do some proper validation when validators have been built. */ + $stringToken = strval($_REQUEST['token']); + if(file_exists($pathClientLibBase . '/security.php')){ + include_once($pathClientLibBase . '/security.php'); + $boolSecurityCheckPassed = securityCheck($_SERVER['REMOTE_ADDR'], $stringToken); + } + } + } + /*Get Format */ + $urlPath = trim(rtrim($urlPath,'/')); + $intFormatLastPeriod = strrpos($urlPath, '.'); + $intFormatLastSlash = strrpos($urlPath, '/'); + if((($intFormatLastPeriod - $intFormatLastSlash) > 0) && ($intFormatLastPeriod !== false)){ + $stringFormat = strtolower(ltrim(substr($urlPath, $intFormatLastPeriod), '.')); + $urlPath = rtrim(substr($urlPath, 0, $intFormatLastPeriod), '/'); + if(!(in_array($stringFormat, $arraySupportedFormats))){ + $stringFormat = 'txt'; + } + } + /* Examine for Basic Elements */ + /* Check for off-by-one here. */ - $pathArray = explode('/', $urlPath); - if(count($pathArray) < 3){ - $boolValidUrl = false; - } - else{ - /* API Call Version */ - $floatClientVersion = number_format(floatval($pathArray[0]), 2); /* A bad value will return 0 anyway. We are hardcoding decimals to 2. */ - if($floatClientVersion == 0){ - $boolValidUrl = false; - } - /* Category */ - if(ctype_alnum($pathArray[1])){ - $stringCategory = $pathArray[1]; - } - else{ - $boolValidUrl = false; - } - /* Verb */ - if(ctype_alnum($pathArray[2])){ - $stringPlanworldVerb = $pathArray[2]; - } - else{ - $boolValidUrl = false; - } - /* Get post data if available. */ - if(!empty($_POST['planworld_post'])){ /* Is this supposed to be hard-coded? */ - $stringPlanworldVerbSuffix = 'Post'; - $postData = $_POST['planworld_post']; - } - /* Save the other array elements as arguments. */ - if(count($pathArray) > 3){ - $arrayArguments = array_splice($pathArray, 3); - } - } + $pathArray = explode('/', $urlPath); + if(count($pathArray) < 3){ + $boolValidUrl = false; + } + else{ + /* API Call Version */ + $floatClientVersion = number_format(floatval($pathArray[0]), 2); /* A bad value will return 0 anyway. We are hardcoding decimals to 2. */ + if($floatClientVersion == 0){ + $boolValidUrl = false; + } + /* Category */ + if(ctype_alnum($pathArray[1])){ + $stringCategory = $pathArray[1]; + } + else{ + $boolValidUrl = false; + } + /* Verb */ + if(ctype_alnum($pathArray[2])){ + $stringPlanworldVerb = $pathArray[2]; + } + else{ + $boolValidUrl = false; + } + /* Get post data if available. */ + if(!empty($_POST['planworld_post'])){ /* Is this supposed to be hard-coded? */ + $stringPlanworldVerbSuffix = 'Post'; + $postData = $_POST['planworld_post']; + } + /* Save the other array elements as arguments. */ + if(count($pathArray) > 3){ + $arrayArguments = array_splice($pathArray, 3); + } + } - /* Execute Planworld Verb */ - if(($boolValidUrl) && ($boolSecurityCheckPassed)){ - if(file_exists($pathClientLibBase . '/' . $floatClientVersion . '/' . $stringCategory . '.php')){ - include_once($pathClientLibBase . '/' . $floatClientVersion . '/' . $stringCategory . '.php'); - $functionVerb = $stringPlanworldVerb . $stringPlanworldVerbSuffix; - if(function_exists($functionVerb)){ - $arrayRestInputs = array( - 'token' => $stringToken, - 'arguments' => $arrayArguments, - 'post' => $postData, - 'enginebasedir' => dirname(__FILE__) . '/../' - ); - $output = $functionVerb($arrayRestInputs); - $finalOutput = ''; - if(is_array($output)){ - if(strcmp($stringFormat, 'xml') == 0){ - $xmlOutput = new SimpleXMLElement('<' . $functionVerb . '>'); - array_to_xml($output, $xmlOutput); - $finalOutput = $xmlOutput->asXML(); - } - else if(strcmp($stringFormat, 'json') == 0){ - $jsonArray = array($stringPlanworldVerb => $output); - $finalOutput = json_encode($jsonArray); - } - else{ - $finalOutput = print_r($output,true); /* This is not debug code, this is to output text for now. */ - } - } - else if(is_object($output)){ - // We do not currently handle objects. - } - else{ - $stringOutput = strval($output); - $finalOutput = $stringOutput; - if(strcmp($stringFormat, 'xml') == 0){ - $finalOutput = '<' . $functionVerb . '>' . htmlspecialchars($stringOutput) . ''; - } - else if(strcmp($stringFormat, 'json') == 0){ - $jsonArray = array($stringPlanworldVerb => $stringOutput); - $finalOutput = json_encode($jsonArray); - } - else{ - $finalOutput = print_r($finalOutput,true); /* This is not debug code, this is to output text for now. */ - } - } - statusCode(200); - echo $finalOutput; - } - else{ - statusCode(400); - echo 'Either you are requesting an incorrect method, or this node has not implemented this method.'; - } - } - else if(file_exists($pathClientLibBase . '/' . $floatClientVersion)){ - statusCode(501); - echo 'Invalid Planworld REST category.'; - } - else{ - statusCode(501); - echo 'Unsupported Planworld API version.'; - } - } - else if(!($boolSecurityCheckPassed)){ - statusCode(403); - echo 'IP Locked by Intruder Detection or Invalid Token.'; + /* Execute Planworld Verb */ + if(($boolValidUrl) && ($boolSecurityCheckPassed)){ + if(file_exists($pathClientLibBase . '/' . $floatClientVersion . '/' . $stringCategory . '.php')){ + include_once($pathClientLibBase . '/' . $floatClientVersion . '/' . $stringCategory . '.php'); + $functionVerb = $stringPlanworldVerb . $stringPlanworldVerbSuffix; + if(function_exists($functionVerb)){ + $arrayRestInputs = array( + 'token' => $stringToken, + 'arguments' => $arrayArguments, + 'post' => $postData, + 'enginebasedir' => dirname(__FILE__) . '/../' + ); + $output = $functionVerb($arrayRestInputs); + $finalOutput = ''; + if(is_array($output)){ + if(strcmp($stringFormat, 'xml') == 0){ + $xmlOutput = new SimpleXMLElement('<' . $functionVerb . '>'); + array_to_xml($output, $xmlOutput); + $finalOutput = $xmlOutput->asXML(); + } + else if(strcmp($stringFormat, 'json') == 0){ + $jsonArray = array($stringPlanworldVerb => $output); + $finalOutput = json_encode($jsonArray); + } + else{ + $finalOutput = print_r($output,true); /* This is not debug code, this is to output text for now. */ + } + } + /* Added to let us do simple boolean returns for methods that post. We can adjust the output results if desired. 20191007 JLO2 */ + else if(is_bool($output)){ + $stringOutput = 'FALSE'; + if($output){ + $stringOutput = 'TRUE'; + } + if(strcmp($stringFormat, 'xml') == 0){ + $finalOutput = '<' . $functionVerb . '>' . htmlspecialchars($stringOutput) . ''; + } + else if(strcmp($stringFormat, 'json') == 0){ + $jsonArray = array($stringPlanworldVerb => $stringOutput); + $finalOutput = json_encode($jsonArray); + } + else{ + $finalOutput = print_r($finalOutput,true); /* This is not debug code, this is to output text for now. */ + } + } + else if(is_object($output)){ + // We do not currently handle objects. + } + else{ + $stringOutput = strval($output); + $finalOutput = $stringOutput; + if(strcmp($stringFormat, 'xml') == 0){ + $finalOutput = '<' . $functionVerb . '>' . htmlspecialchars($stringOutput) . ''; + } + else if(strcmp($stringFormat, 'json') == 0){ + $jsonArray = array($stringPlanworldVerb => $stringOutput); + $finalOutput = json_encode($jsonArray); + } + else{ + $finalOutput = print_r($finalOutput,true); /* This is not debug code, this is to output text for now. */ + } + } + statusCode(200); + echo $finalOutput; + } + else{ + statusCode(400); + echo 'Either you are requesting an incorrect method, or this node has not implemented this method.'; + } + } + else if(file_exists($pathClientLibBase . '/' . $floatClientVersion)){ + statusCode(501); + echo 'Invalid Planworld REST category.'; + } + else{ + statusCode(501); + echo 'Unsupported Planworld API version.'; + } + } + else if(!($boolSecurityCheckPassed)){ + statusCode(403); + echo 'IP Locked by Intruder Detection or Invalid Token.'; - } - else{ - statusCode(400); - echo 'Invalid REST Url.'; - } + } + else{ + statusCode(400); + echo 'Invalid REST Url.'; + } } else{ - statusCode(400); - echo 'No REST Url.'; + statusCode(400); + echo 'No REST Url.'; } diff --git a/code/clientlib/3.00/plan.php b/code/clientlib/3.00/plan.php index 433eb54..9f2e74a 100644 --- a/code/clientlib/3.00/plan.php +++ b/code/clientlib/3.00/plan.php @@ -16,7 +16,12 @@ function planGet($arrayRestInputs){ $userPlanToGet = $arguments[0]; if(Planworld::isUser($userPlanToGet)){ $userPlanToGetObject = new User($userPlanToGet); - return $userPlanToGetObject->getPlanSimple($thisUserObject); + if(!$thisUserObject->doesBlockRelationshipExist($userPlanToGetObject->getUserID())){ + return $userPlanToGetObject->getPlanSimple($thisUserObject); + } + else{ + return 'Either you or this user is blocked by the other.'; + } } else{ return ''; diff --git a/code/clientlib/3.00/send.php b/code/clientlib/3.00/send.php index 9e39250..b9e1e5e 100644 --- a/code/clientlib/3.00/send.php +++ b/code/clientlib/3.00/send.php @@ -5,94 +5,103 @@ /* Get a send conversation. Takes one argument -- the username at the other end of the conversation. */ function sendGet($arrayRestInputs){ - if((file_exists($arrayRestInputs['enginebasedir'] . '/lib/Send.php')) && (file_exists($arrayRestInputs['enginebasedir'] . '/lib/NodeToken.php')) ){ - require_once($arrayRestInputs['enginebasedir'] . '/lib/Send.php'); - require_once($arrayRestInputs['enginebasedir'] . '/lib/NodeToken.php'); - $objectToken = new NodeToken (); - if(($objectToken->retrieveToken($arrayRestInputs['token'])) && (count($arrayRestInputs['arguments']) > 0)){ - $arguments = $arrayRestInputs['arguments']; - $toUser = new User($arguments[0]); - $objectSend = new Send(); - $sendReturn = $objectSend->getMessages($objectToken->uid, $toUser->getUserID()); - if($sendReturn < 0){ - return ''; - } - else{ - if(is_array($sendReturn)){ - $limit = count($sendReturn); - for($i = 0; $i < $limit; $i++){ - $sendReturn[$i]['sent'] = date(DATE_ATOM, $sendReturn[$i]['sent']); - $sendReturn[$i]['seen'] = date(DATE_ATOM, $sendReturn[$i]['seen']); - $sendReturn[$i]['requestinguser'] = $objectToken->username; - } - } - return $sendReturn; - } - } - else{ - return ''; /* BUG: If the client does not send a token, an empty string is returned. An invalid token should result in an error somewhere. */ - } - } - else{ - return ''; - } + if((file_exists($arrayRestInputs['enginebasedir'] . '/lib/Send.php')) && (file_exists($arrayRestInputs['enginebasedir'] . '/lib/NodeToken.php')) ){ + require_once($arrayRestInputs['enginebasedir'] . '/lib/Send.php'); + require_once($arrayRestInputs['enginebasedir'] . '/lib/NodeToken.php'); + $objectToken = new NodeToken (); + if(($objectToken->retrieveToken($arrayRestInputs['token'])) && (count($arrayRestInputs['arguments']) > 0)){ + $arguments = $arrayRestInputs['arguments']; + $toUser = new User($arguments[0]); + $objectSend = new Send(); + $sendReturn = $objectSend->getMessages($objectToken->uid, $toUser->getUserID()); + if($sendReturn < 0){ + return ''; + } + else{ + if(is_array($sendReturn)){ + $limit = count($sendReturn); + for($i = 0; $i < $limit; $i++){ + $sendReturn[$i]['sent'] = date(DATE_ATOM, $sendReturn[$i]['sent']); + $sendReturn[$i]['seen'] = date(DATE_ATOM, $sendReturn[$i]['seen']); + $sendReturn[$i]['requestinguser'] = $objectToken->username; + } + } + return $sendReturn; + } + } + else{ + return ''; /* BUG: If the client does not send a token, an empty string is returned. An invalid token should result in an error somewhere. */ + } + } + else{ + return ''; + } } /* Send a message to a user. Takes two arguments -- user to send to and message to send. ) */ function sendPost($arrayRestInputs){ - if((file_exists($arrayRestInputs['enginebasedir'] . '/lib/Send.php')) && (file_exists($arrayRestInputs['enginebasedir'] . '/lib/NodeToken.php')) ){ - require_once($arrayRestInputs['enginebasedir'] . '/lib/Send.php'); - require_once($arrayRestInputs['enginebasedir'] . '/lib/NodeToken.php'); - $objectToken = new NodeToken (); - if(($objectToken->retrieveToken($arrayRestInputs['token'])) && (count($arrayRestInputs['arguments']) > 0) && (!(empty($arrayRestInputs['post'])))){ - $arguments = $arrayRestInputs['arguments']; - $toUser = new User($arguments[0]); - $objectSend = new Send(); - $sendReturn = $objectSend->sendMessage($objectToken->uid, $toUser->getUserID(), $arrayRestInputs['post']); - return $sendReturn; - } - else{ - return false; - } - } - else{ - return false; - } + if((file_exists($arrayRestInputs['enginebasedir'] . '/lib/Send.php')) && (file_exists($arrayRestInputs['enginebasedir'] . '/lib/NodeToken.php')) ){ + require_once($arrayRestInputs['enginebasedir'] . '/lib/Send.php'); + require_once($arrayRestInputs['enginebasedir'] . '/lib/NodeToken.php'); + $objectToken = new NodeToken (); + if(($objectToken->retrieveToken($arrayRestInputs['token'])) && (count($arrayRestInputs['arguments']) > 0) && (!(empty($arrayRestInputs['post'])))){ + $arguments = $arrayRestInputs['arguments']; + $fromUserId = $objectToken->uid; + $fromUserObject = new User($fromUserId); + $toUserObject = new User($arguments[0]); + if(!$fromUserObject->doesBlockRelationshipExist($toUserObject->getUserID())){ + $objectSend = new Send(); + $sendReturn = $objectSend->sendMessage($fromUserId, $toUserObject->getUserID(), $arrayRestInputs['post']); + return $sendReturn; + + } + else{ + return false; + } + } + else{ + return false; + } + } + else{ + return false; + } } + /* Get a list of send conversations with the other user, the time of the last message, and whether that message was seen. Takes no arguments. */ function sendlistGet($arrayRestInputs){ - if((file_exists($arrayRestInputs['enginebasedir'] . '/lib/Send.php')) && (file_exists($arrayRestInputs['enginebasedir'] . '/lib/NodeToken.php')) ){ - require_once($arrayRestInputs['enginebasedir'] . '/lib/Send.php'); - require_once($arrayRestInputs['enginebasedir'] . '/lib/NodeToken.php'); - $objectToken = new NodeToken (); - if($objectToken->retrieveToken($arrayRestInputs['token'])){ - $objectSend = new Send(); - $sendlistReturn = $objectSend->getSendList($objectToken->uid); - if($sendlistReturn < 0){ - return ''; - } - else{ - if(is_array($sendlistReturn)){ - $limit = count($sendlistReturn); - for($i = 0; $i < $limit; $i++){ - $sendlistReturn[$i]['selfsenddate'] = date(DATE_ATOM, $sendlistReturn[$i]['selfsenddate']); - $sendlistReturn[$i]['selfseen'] = date(DATE_ATOM, $sendlistReturn[$i]['selfseen']); - $sendlistReturn[$i]['othersenddate'] = date(DATE_ATOM, $sendlistReturn[$i]['othersenddate']); - $sendlistReturn[$i]['otherseen'] = date(DATE_ATOM, $sendlistReturn[$i]['otherseen']); - } - } - return $sendlistReturn; - } - } - else{ - return ''; - } - } - else{ - return ''; - } + if((file_exists($arrayRestInputs['enginebasedir'] . '/lib/Send.php')) && (file_exists($arrayRestInputs['enginebasedir'] . '/lib/NodeToken.php')) ){ + require_once($arrayRestInputs['enginebasedir'] . '/lib/Send.php'); + require_once($arrayRestInputs['enginebasedir'] . '/lib/NodeToken.php'); + $objectToken = new NodeToken (); + if($objectToken->retrieveToken($arrayRestInputs['token'])){ + $objectSend = new Send(); + $sendlistReturn = $objectSend->getSendList($objectToken->uid); + if($sendlistReturn < 0){ + return ''; + } + else{ + if(is_array($sendlistReturn)){ + $limit = count($sendlistReturn); + for($i = 0; $i < $limit; $i++){ + $sendlistReturn[$i]['selfsenddate'] = date(DATE_ATOM, $sendlistReturn[$i]['selfsenddate']); + $sendlistReturn[$i]['selfseen'] = date(DATE_ATOM, $sendlistReturn[$i]['selfseen']); + $sendlistReturn[$i]['othersenddate'] = date(DATE_ATOM, $sendlistReturn[$i]['othersenddate']); + $sendlistReturn[$i]['otherseen'] = date(DATE_ATOM, $sendlistReturn[$i]['otherseen']); + } + } + return $sendlistReturn; + } + } + else{ + return ''; + } + } + else{ + return ''; + } } ?> \ No newline at end of file diff --git a/code/clientlib/3.00/watchlist.php b/code/clientlib/3.00/watchlist.php deleted file mode 100644 index e752f0f..0000000 --- a/code/clientlib/3.00/watchlist.php +++ /dev/null @@ -1,143 +0,0 @@ -retrieveToken($arrayRestInputs['token'])){ - $thisUserUid = $objectToken->uid; - $thisUserObject = new User($thisUserUid); - $thisPlanwatch = new Planwatch($thisUserObject); - return $thisPlanwatch->getInteroperableWatchlist(); - } - else{ - return ''; - } - } - else{ - return ''; - } -} - - -function watchlistgroupGet($arrayRestInputs){ - if((file_exists($arrayRestInputs['enginebasedir'] . '/lib/Planwatch.php')) && (file_exists($arrayRestInputs['enginebasedir'] . '/lib/NodeToken.php')) ){ - require_once($arrayRestInputs['enginebasedir'] . '/lib/Planwatch.php'); - require_once($arrayRestInputs['enginebasedir'] . '/lib/NodeToken.php'); - $objectToken = new NodeToken (); - if($objectToken->retrieveToken($arrayRestInputs['token'])){ - $thisUserUid = $objectToken->uid; - $thisUserObject = new User($thisUserUid); - $thisPlanwatch = new Planwatch($thisUserObject); - return $thisPlanwatch->getInteroperableWatchlistGroups(); - } - else{ - return ''; - } - } - else{ - return ''; - } -} - - -function groupcreatePost($arrayRestInputs){ - $boolReturn = false; - if((file_exists($arrayRestInputs['enginebasedir'] . '/lib/Planwatch.php')) && (file_exists($arrayRestInputs['enginebasedir'] . '/lib/NodeToken.php')) ){ - require_once($arrayRestInputs['enginebasedir'] . '/lib/Planwatch.php'); - require_once($arrayRestInputs['enginebasedir'] . '/lib/NodeToken.php'); - $objectToken = new NodeToken (); - if(($objectToken->retrieveToken($arrayRestInputs['token'])) && (count($arrayRestInputs['arguments']) > 0)){ - $arguments = $arrayRestInputs['arguments']; - $stringNewGroupName = $arguments[0]; - if(Planworld::isValidWatchlistGroupName($stringNewGroupName)){ - $thisUserUid = $objectToken->uid; - $thisUserObject = new User($thisUserUid); - $thisPlanwatch = new Planwatch($thisUserObject); - $boolNameDoesNotAlreadyExist = true; - $arrayOfCurrentGroupNames = $thisPlanwatch->getGroupNames(); - foreach($arrayOfCurrentGroupNames as $currentGroupName){ - if(strcasecmp($currentGroupName, $stringNewGroupName) == 0){ - $boolNameDoesNotAlreadyExist = false; - } - } - if($boolNameDoesNotAlreadyExists){ - $boolReturn = $thisPlanwatch->addGroup($stringNewGroupName); - $thisPlanwatch->save(); - } - } - } - } - return $boolReturn; -} - - -// WOAHWOAHWOAH I MAY BE DOING POSTING WRONG HERE. CHECK WHERE THE POST DATA IS COMING FROM. THESE MIGHT BE GETS OR NOT NOT SURE - -function groupmoveuserPost($arrayRestInputs){ - $boolReturn = false; - if((file_exists($arrayRestInputs['enginebasedir'] . '/lib/Planwatch.php')) && (file_exists($arrayRestInputs['enginebasedir'] . '/lib/NodeToken.php')) ){ - require_once($arrayRestInputs['enginebasedir'] . '/lib/Planwatch.php'); - require_once($arrayRestInputs['enginebasedir'] . '/lib/NodeToken.php'); - $objectToken = new NodeToken (); - if(($objectToken->retrieveToken($arrayRestInputs['token'])) && (count($arrayRestInputs['arguments']) > 0)){ - $arguments = $arrayRestInputs['arguments']; - $stringUserToMove = $arguments[0]; - $stringGroupToMoveTo = $arguments[1]; - if((Planworld::isValidWatchlistGroupName($stringGroupToMoveTo)) && (Planworld::isUser($stringUserToMove))){ - $thisUserUid = $objectToken->uid; - $thisUserObject = new User($thisUserUid); - $thisPlanwatch = new Planwatch($thisUserObject); - $boolGroupExists = false; - $boolUserInPlanwatch = $thisPlanwatch->inPlanwatch($stringUserToMove); - $matrixGroups = $thisPlanwatch->getGroups(); - $intGidForGroupMove = -1; - foreach($matrixGroups as $nameGroup => $rowGroup){ - if(strcasecmp($nameGroup, $stringGroupToMoveTo) == 0){ - $boolGroupExists = true; - $intGidForGroupMove = $rowGroup['gid']; - } - } - if($boolGroupExists && $boolUserInPlanwatch){ - $boolReturn = $thisPlanwatch->move($stringUserToMove, $intGidForGroupMove); - $thisPlanwatch->save(); - } - } - } - } - return $boolReturn; -} - - -function addPost($arrayRestInputs){ - $boolReturn = false; - if((file_exists($arrayRestInputs['enginebasedir'] . '/lib/Planwatch.php')) && (file_exists($arrayRestInputs['enginebasedir'] . '/lib/NodeToken.php')) ){ - require_once($arrayRestInputs['enginebasedir'] . '/lib/Planwatch.php'); - require_once($arrayRestInputs['enginebasedir'] . '/lib/NodeToken.php'); - $objectToken = new NodeToken (); - if(($objectToken->retrieveToken($arrayRestInputs['token'])) && (count($arrayRestInputs['arguments']) > 0)){ - $arguments = $arrayRestInputs['arguments']; - $stringUserToAdd = $arguments[0]; - if((Planworld::isUser($stringUserToAdd))){ - $thisUserUid = $objectToken->uid; - $thisUserObject = new User($thisUserUid); - $thisPlanwatch = new Planwatch($thisUserObject); - $boolUserInPlanwatch = $thisPlanwatch->inPlanwatch($stringUserToAdd); - if(!$boolUserInPlanwatch){ - $boolReturn = $thisPlanwatch->add($stringUserToAdd); - $thisPlanwatch->save(); - } - } - } - } - return $boolReturn; -} - - -?> \ No newline at end of file diff --git a/code/config.php b/code/config.php index 4f1343a..6f54599 100644 --- a/code/config.php +++ b/code/config.php @@ -24,6 +24,7 @@ define("TOKEN_LIFE", 2592000); /* Default is six hours - 6 hours * 60 minutes * define("NODE_TOKEN_PREFIX", 999000000); define("TRUSTEDAPPKEY", "BogusKey"); define("TESTLEVEL", "LOCALTEST"); /* Options are PRODUCTION, TEST, and LOCALTEST */ +define("LOCKTIME", 86400); /* Time for values like blocklists and sensitive preferences to be locked before next change. */ /* Cross-library definitions */ define('PLANWORLD_OK', 0); /* PLANWORLD_OK Operation succeeded. */ diff --git a/code/lib/Online.php b/code/lib/Online.php index 593f2a4..3030ac0 100644 --- a/code/lib/Online.php +++ b/code/lib/Online.php @@ -4,103 +4,139 @@ * Utility class for determining online users. */ +/* includes */ +$_base = dirname(__FILE__) . '/../'; /* This file lives in the lib folder, so root is one level above. */ +require_once($_base . 'config.php'); require_once($_base . 'lib/User.php'); require_once($_base . 'lib/Planworld.php'); date_default_timezone_set(PW_TIMEZONE); -class Online { - /** - * void Online::clearIdle () - * Removes users who have been idle too long - */ - function clearIdle () { - $dbh = Planworld::_connect(); - - $query = "DELETE FROM online WHERE last_access < " . (mktime() - PW_IDLE_TIMEOUT); - $dbh->query($query); - } - - /** - * void Online::updateUser (&$user, &$target) - * Update's $user's status to $target - */ - function updateUser (&$user, &$target) { - $dbh = Planworld::_connect(); - - $query = "UPDATE online SET last_access=" . mktime() . ", what='"; - if (is_object($target)) { - $query .= $target->getUsername(); - } else { - $query .= addslashes($target); - } - $query .= "' WHERE uid=" . $user->getUserID(); - - $result = $dbh->query($query); - if (isset($result) && !DB::isError($result)) { - if ($dbh->affectedRows() < 1) { - return Online::addUser($user, $target); - } else { - return true; - } - } else { - return PLANWORLD_ERROR; +class Online +{ + + /** + * void Online::clearIdle () + * Removes users who have been idle too long + */ + public static function clearIdle() + { + try { + $dbh = Planworld::_connect(); + $query = $dbh->prepare('DELETE FROM online WHERE last_access < :timeconsideredidle'); + $queryArray = array('timeconsideredidle' => (time() - PW_IDLE_TIMEOUT)); + $query->execute($queryArray); + return true; + } catch (PDOException $badprequery) { + return PLANWORLD_ERROR; + } } - } - /** - * void Online::addUser($user, $target) - * Adds $user to the list of online users (with status $target) - */ - function addUser (&$user, $target) { - $dbh = Planworld::_connect(); - $query = "INSERT INTO online (uid, login, last_access, what) VALUES (" . $user->getUserID() . ", " . mktime() . ", " . mktime() . ", '"; - - if (is_object($target)) { - $query .= $target->getUserName(); - } else { - $query .= $target; + /** + * void Online::updateUser (&$user, &$target) + * Update's $user's status to $target + */ + public static function updateUser(&$user, $target) + { + $stringTarget = ''; + if (is_object($target)) { + $stringTarget = $target->getUsername(); + } else { + $stringTarget = addslashes($target); + } + if (!(empty($stringTarget))) { + $dbh = Planworld::_connect(); + $uid = $user->getUserID(); + $boolUserIsOnline = false; + try { + $preQuery = $dbh->prepare('select count(uid) as count from online where uid= :uid'); + $preQueryArray = array('uid' => $uid); + $preQuery->execute($preQueryArray); + $result = $preQuery->fetchAll(); + if (!($result)) { + $boolUserIsOnline = false; + } else { + if ($result[0]['count'] > 0) { + $boolUserIsOnline = true; + } else { + $boolUserIsOnline = false; + } + } + } catch (PDOException $badprequery) { + return PLANWORLD_ERROR; + } + /* If the user is indeed already online, we just update their status. */ + if ($boolUserIsOnline) { + try { + $query = $dbh->prepare('UPDATE online SET last_access= :currenttime , what= :target WHERE uid= :uid'); + $queryArray = array('currenttime' => time(), 'target' => $stringTarget, 'uid' => $uid); + $query->execute($queryArray); + return true; + } catch (PDOException $badquery) { + return PLANWORLD_ERROR; + } + } + /* If the user is not already online, make them online. */ + else { + return Online::addUser($user, $target); + } + } else { + return PLANWORLD_ERROR; + } } - $query .= "')"; - - $dbh->query($query); - } - - /** - * void Online::removeUser ($user) - * Removes $user from the list of online users - */ - function removeUser (&$user) { - $dbh = Planworld::_connect(); - $query = "DELETE FROM online WHERE uid=" . $user->getUserID(); - $dbh->query($query); - } + /** + * void Online::addUser($user, $target) + * Adds $user to the list of online users (with status $target) + */ + public static function addUser(&$user, $target) + { + $stringTarget = ''; + if (is_object($target)) { + $stringTarget = $target->getUsername(); + } else { + $stringTarget = addslashes($target); + } + $dbh = Planworld::_connect(); + $uid = $user->getUserID(); + $currentTime = time(); + try { + $dbh = Planworld::_connect(); + $query = $dbh->prepare('INSERT INTO online (uid, login, last_access, what) VALUES ( :uid, :currenttime, :currenttime, :target)'); + $queryArray = array('uid' => $uid, 'currenttime' => $currentTime, 'target' => $stringTarget); + $query->execute($queryArray); + return true; + } catch (PDOException $badprequery) { + return PLANWORLD_ERROR; + } + } - /** - * array Online::getOnlineUsers () - * Returns a list of all users who are currently online. - */ - function getOnlineUsers () { - $dbh = Planworld::_connect(); - $query = "SELECT users.username, online.last_access, online.login, online.what FROM users, online WHERE users.id = online.uid ORDER BY last_access DESC"; - /* execute the query */ - $result = $dbh->query($query); - if (isset($result) && !DB::isError($result)) { - $return = array(); - while ($row = $result->fetchRow()) { - $return[] = array('name' => $row['username'], - 'lastAccess' => (int) $row['last_access'], - 'login' => (int) $row['login'], - 'what' => $row['what']); - } - return $return; - } else { - return PLANWORLD_ERROR; + /** + * array Online::getOnlineUsers () + * Returns a list of all users who are currently online. + */ + public static function getOnlineUsers() + { + try { + $dbh = Planworld::_connect(); + $query = $dbh->prepare('SELECT users.username as name, online.last_access as lastAccess, online.login as login, online.what as what FROM users, online WHERE users.id = online.uid ORDER BY last_access DESC'); + $query->execute(); + $return = $query->fetchall(); + $finalArray = array(); + foreach ($return as $row) { + $finalArray[] = array('name' => $row['name'], + 'lastAccess' => (int) $row['lastAccess'], + 'login' => (int) $row['login'], + 'what' => $row['what']); + } + return $finalArray; + } catch (PDOException $badprequery) { + return PLANWORLD_ERROR; + } } - } + + /* End Class */ } -?> \ No newline at end of file diff --git a/code/lib/Planwatch.php b/code/lib/Planwatch.php index f3f97df..605e605 100644 --- a/code/lib/Planwatch.php +++ b/code/lib/Planwatch.php @@ -136,7 +136,6 @@ class Planwatch { */ function save(){ if ($this->changed) { - $this->dbh = Planworld::_connect(); foreach ($this->planwatch as $u=>$entry) { if (isset($entry[3]) && $entry[3]) { try{ @@ -300,7 +299,7 @@ class Planwatch { function addGroup ($name) { try{ $intUid = $this->user->getUserID(); - $id = (int) $this->dbh->nextId('groupid'); + $id = (int) $this->dbh->nextId('groupid'); // GUESS WHAT THIS DOESN'T WORK OUTSIDE OF PEAR OOPS. FIX $query = $this->dbh->prepare('INSERT INTO pw_groups (gid, uid, name) VALUES (:id, :uid, :name)'); $queryArray = array('id' => $id, 'uid' => $intUid, 'name' => $name); $query->execute($queryArray); @@ -367,22 +366,30 @@ class Planwatch { else{ $intWUid = Planworld::nameToID($uid); } - $query = $this->dbh->prepare('INSERT INTO planwatch (w_uid, uid) VALUES (:wuid, :uid)'); - $queryArray = array('wuid' => $intWUid, 'uid' => $intUid); - $query->execute($queryArray); + try{ + $query = $this->dbh->prepare('INSERT INTO planwatch (w_uid, uid) VALUES (:wuid, :uid)'); + $queryArray = array('wuid' => $intWUid, 'uid' => $intUid); + $query->execute($queryArray); + return true; + } + catch(PDOException $badquery){ + return false; + } } /* JLO2 20191005 - This can't possibly be as simple as I think it is. */ function getInteroperableWatchlist(){ $arrayInteroperableList = array(); foreach ($this->planwatch as $username => $watchrow){ - $watchlineArray = array( - 'username' => $username, - 'lastupdate' => date(DATE_ATOM, $watchrow[1]), - 'lastview' => date(DATE_ATOM, $watchrow[2]), - 'hasmessage' => $watchrow[3] - ); - $arrayInteroperableList[] = $watchlineArray; + if(!($this->user->doesBlockRelationshipExist($username))){ + $watchlineArray = array( + 'username' => $username, + 'lastupdate' => date(DATE_ATOM, $watchrow[1]), + 'lastview' => date(DATE_ATOM, $watchrow[2]), + 'hasmessage' => $watchrow[3] + ); + $arrayInteroperableList[] = $watchlineArray; + } } return $arrayInteroperableList; } @@ -391,8 +398,11 @@ class Planwatch { /* Separate call to get group information. */ function getInteroperableWatchlistGroups(){ $arrayGroupLevelList = array(); + $intCounter = 0; foreach ($this->groupData as $groupname => $grouparray){ - $arrayGroupLevelList[$groupname] = array(); + $arrayGroupLevelList[$intCounter] = array(); + $arrayGroupLevelList[$intCounter]['groupname'] = $groupname; + $arrayGroupLevelList[$intCounter]['membership'] = array(); foreach ($grouparray as $username => $watchrow){ $watchlineArray = array( 'username' => $username, @@ -400,8 +410,9 @@ class Planwatch { 'lastview' => date(DATE_ATOM, $watchrow[2]), 'hasmessage' => $watchrow[3] ); - $arrayGroupLevelList[$groupname][] = $watchlineArray; + $arrayGroupLevelList[$intCounter]['membership'][] = $watchlineArray; } + $intCounter++; } return $arrayGroupLevelList; } diff --git a/code/lib/Planworld.php b/code/lib/Planworld.php index b5e2ac8..446cb20 100644 --- a/code/lib/Planworld.php +++ b/code/lib/Planworld.php @@ -64,7 +64,7 @@ class Planworld { throw new PDOException('Database connection not initialized.'); } $query = $dbh->prepare('INSERT INTO users (id, username, remote, first_login) VALUES (:id, :username, :remote, :first_login)'); - $queryArray = array('id' => $intId, 'username' => addslashes($uid), 'remote' => $remote, 'first_login'=> time()); + $queryArray = array('id' => $intId, 'username' => strtolower(addslashes($uid)), 'remote' => $remote, 'first_login'=> time()); $result = $query->execute($queryArray); if(count($result) < 1){ return PLANWORLD_ERROR; @@ -139,6 +139,7 @@ class Planworld { function nameToID ($uid) { static $table; /* persistent lookup table */ if (is_string($uid)) { + $uid = strtolower($uid); if (isset($table[$uid])) { return $table[$uid]; } @@ -240,7 +241,7 @@ public static function isUser ($uid, $force=false) { $query = $dbh->prepare('SELECT COUNT(id) AS count FROM users WHERE id= :uid'); } else if (is_string($uid)) { - $uid = addslashes($uid); + $uid = strtolower(addslashes($uid)); $query = $dbh->prepare('SELECT COUNT(id) AS count FROM users WHERE username= :uid'); } $queryArray = array('uid' => $uid); @@ -282,7 +283,7 @@ public static function isUser ($uid, $force=false) { $query = $dbh->prepare('SELECT remote FROM users WHERE id= :uid'); } else if (is_string($uid)) { - $uid = addslashes($uid); + $uid = strtolower(addslashes($uid)); $query = $dbh->prepare('SELECT remote FROM users WHERE username= :uid'); } $queryArray = array('uid' => $uid); diff --git a/code/lib/Snoop.php b/code/lib/Snoop.php index e6f6606..7d7f976 100644 --- a/code/lib/Snoop.php +++ b/code/lib/Snoop.php @@ -10,243 +10,243 @@ date_default_timezone_set(PW_TIMEZONE); class Snoop { - /** - * Calls a remote method via xml-rpc. - * @param method Method to call. - * @param params Parameters to use. - * @private - */ - public function _call($server, $method, $params=null) - { - return xu_rpc_http_concise(array('method' => $method, - 'args' => $params, - 'host' => $server['Hostname'], - 'uri' => $server['Path'], - 'port' => $server['Port'], - 'debug' => 0)); // 0=none, 1=some, 2=more + /** + * Calls a remote method via xml-rpc. + * @param method Method to call. + * @param params Parameters to use. + * @private + */ + public function _call($server, $method, $params=null) + { + return xu_rpc_http_concise(array('method' => $method, + 'args' => $params, + 'host' => $server['Hostname'], + 'uri' => $server['Path'], + 'port' => $server['Port'], + 'debug' => 0)); // 0=none, 1=some, 2=more + } + + /** + * Pulls references from $content. + * @param content Content to search. + * @returns matches Array of references. + * @private + */ + public function _getReferences($content) + { + /* find references in plan */ + preg_match_all("/!([a-z0-9\-\.@]+)(!|:[^!]+!)/i", $content, $matches, PREG_PATTERN_ORDER); + return $matches; + } + + /** + * void Snoop::addReference ($from, $to) + * Add a snoop reference by $from for $to + */ + public function addReference($from, $to, $date=null) + { + $dbh = Planworld::_connect(); + + if (!isset($date)) { + $date = mktime(); } - /** - * Pulls references from $content. - * @param content Content to search. - * @returns matches Array of references. - * @private - */ - public function _getReferences($content) - { - /* find references in plan */ - preg_match_all("/!([a-z0-9\-\.@]+)(!|:[^!]+!)/i", $content, $matches, PREG_PATTERN_ORDER); - return $matches; + if ($from == 0 || $from == '' || $to == 0 || $to == '') { + return false; } - /** - * void Snoop::addReference ($from, $to) - * Add a snoop reference by $from for $to - */ - public function addReference($from, $to, $date=null) - { - $dbh = Planworld::_connect(); - - if (!isset($date)) { - $date = mktime(); - } - - if ($from == 0 || $from == '' || $to == 0 || $to == '') { - return false; - } - - $query = $dbh->prepare('INSERT INTO snoop (uid, s_uid, referenced) VALUES(:to, :from, :date)'); - $queryArray = array('to' => $to, 'from' => $from, 'date' => $date); - $result = $query->execute($queryArray); - if ($result) { - return PLANWORLD_OK; - } else { - return PLANWORLD_ERROR; - } - return PLANWORLD_ERROR; + $query = $dbh->prepare('INSERT INTO snoop (uid, s_uid, referenced) VALUES(:to, :from, :date)'); + $queryArray = array('to' => $to, 'from' => $from, 'date' => $date); + $result = $query->execute($queryArray); + if ($result) { + return PLANWORLD_OK; + } else { + return PLANWORLD_ERROR; } - - /** - * void Snoop::removeReference ($from, $to) - * Removes a snoop reference by $from for $to - */ - public function removeReference($from, $to) - { - $dbh = Planworld::_connect(); - $query = $dbh->prepare('DELETE FROM snoop WHERE uid=:to AND s_uid=:from'); - $queryArray = array('to' => $to, 'from' => $from); - $result = $query->execute($queryArray); - if ($result) { - return PLANWORLD_OK; - } else { - return PLANWORLD_ERROR; - } - return PLANWORLD_ERROR; + return PLANWORLD_ERROR; + } + + /** + * void Snoop::removeReference ($from, $to) + * Removes a snoop reference by $from for $to + */ + public function removeReference($from, $to) + { + $dbh = Planworld::_connect(); + $query = $dbh->prepare('DELETE FROM snoop WHERE uid=:to AND s_uid=:from'); + $queryArray = array('to' => $to, 'from' => $from); + $result = $query->execute($queryArray); + if ($result) { + return PLANWORLD_OK; + } else { + return PLANWORLD_ERROR; + } + return PLANWORLD_ERROR; + } + + /** + * void Snoop::clearReferences ($uid) + * Clear all snoop references by $uid + */ + public function clearReferences($uid) + { + $dbh = Planworld::_connect(); + $query = $dbh->prepare('DELETE FROM snoop WHERE uid=:uid'); + $queryArray = array('uid' => $uid); + $result = $query->execute($queryArray); + if ($result) { + return PLANWORLD_OK; + } else { + return PLANWORLD_ERROR; } + return PLANWORLD_ERROR; + } + + /** + * void Snoop::clearRemoteReferences ($uid) + * Clear all remote snoop references by $uid + */ + public function clearRemoteReferences($node, $uid) + { + Snoop::_call($node, 'planworld.snoop.clear', $uid . '@' . PW_NAME); + } + + /** + * Case-insensitive array diff that prunes duplicates + */ + public function snoop_diff($old, $new) + { + $old = array_map('strtolower', $old); + $new = array_map('strtolower', $new); + return array_unique(array_diff($old, $new)); + } + + /** + * void Snoop::process ($user, $new, $old) + * Find new / removed snoop references in $user's plan. + */ + public function process(&$user, $new, $old) + { - /** - * void Snoop::clearReferences ($uid) - * Clear all snoop references by $uid - */ - public function clearReferences($uid) - { - $dbh = Planworld::_connect(); - $query = $dbh->prepare('DELETE FROM snoop WHERE uid=:uid'); - $queryArray = array('uid' => $uid); - $result = $query->execute($queryArray); - if ($result) { - return PLANWORLD_OK; - } else { - return PLANWORLD_ERROR; - } + /* find references in old plan */ + $dbh = Planworld::_connect(); + try { + if (!$dbh) { + throw new PDOException('Database connection not initialized.'); + } + $query = $dbh->prepare('SELECT username FROM snoop, users WHERE snoop.uid = users.id AND s_uid = :uid'); + $queryArray = array('uid' => $user->getUserID()); + $query->execute($queryArray); + $old_matches = $query->fetchAll(PDO::FETCH_COLUMN); + if (!$old_matches) { return PLANWORLD_ERROR; - } + } else { + /* find references in new plan */ + $new_matches = Snoop::_getReferences($new); + + /* find differences */ + $users_to_add = Snoop::snoop_diff($new_matches[1], $old_matches); + $users_to_del = Snoop::snoop_diff($old_matches, $new_matches[1]); + + $success = true; + foreach ($users_to_add as $u) { + if (strstr($u, '@')) { + list($username, $host) = explode('@', $u); + } + + $sid = Planworld::nameToID($u); + /* valid local user who is not in the current user's blocklist */ + if ((!(isset($host))) && ($sid > 0) && (!($user->isUserIdInBlocklist($sid)))) { + $success = $success && Snoop::addReference($user->getUserID(), $sid); + } + /* remote planworld user */ + + else if (isset($host) && $node = Planworld::getNodeInfo($host)) { + unset($host); /* JLO2 4/12/10 Required to stop permasnoops after calling remote users. */ + + if ($node['Version'] < 2) { + Snoop::_call($node, 'snoop.addReference', array($username, $user->getUsername() . '@' . PW_NAME)); + } else { + Snoop::_call($node, 'planworld.snoop.add', array($username, $user->getUsername() . '@' . PW_NAME)); + } + } + } - /** - * void Snoop::clearRemoteReferences ($uid) - * Clear all remote snoop references by $uid - */ - public function clearRemoteReferences($node, $uid) - { - Snoop::_call($node, 'planworld.snoop.clear', $uid . '@' . PW_NAME); - } + foreach ($users_to_del as $u) { + if (strstr($u, '@')) { + list($username, $host) = explode('@', $u); + } - /** - * Case-insensitive array diff that prunes duplicates - */ - public function snoop_diff($old, $new) - { - $old = array_map('strtolower', $old); - $new = array_map('strtolower', $new); - return array_unique(array_diff($old, $new)); - } + $sid = Planworld::nameToID($u); + if (!isset($host) && $sid > 0) { + /* valid local user */ - /** - * void Snoop::process ($user, $new, $old) - * Find new / removed snoop references in $user's plan. - */ - public function process(&$user, $new, $old) - { + $success = $success && Snoop::removeReference($user->getUserID(), $sid); + } elseif (isset($host) && $node = Planworld::getNodeInfo($host)) { + /* remote planworld user */ + unset($host); /* JLO2 4/12/10 Required to stop permasnoops after calling remote users. */ - /* find references in old plan */ - $dbh = Planworld::_connect(); - try { - if (!$dbh) { - throw new PDOException('Database connection not initialized.'); - } - $query = $dbh->prepare('SELECT username FROM snoop, users WHERE snoop.uid = users.id AND s_uid = :uid'); - $queryArray = array('uid' => $user->getUserID()); - $query->execute($queryArray); - $old_matches = $query->fetchAll(PDO::FETCH_COLUMN); - if (!$old_matches) { - return PLANWORLD_ERROR; + if ($node['Version'] < 2) { + Snoop::_call($node, 'snoop.removeReference', array($username, $user->getUsername() . '@' . PW_NAME)); } else { - /* find references in new plan */ - $new_matches = Snoop::_getReferences($new); - - /* find differences */ - $users_to_add = Snoop::snoop_diff($new_matches[1], $old_matches); - $users_to_del = Snoop::snoop_diff($old_matches, $new_matches[1]); - - $success = true; - foreach ($users_to_add as $u) { - if (strstr($u, '@')) { - list($username, $host) = explode('@', $u); - } - - $sid = Planworld::nameToID($u); - /* valid local user */ - if (!isset($host) && $sid > 0) { - $success = $success && Snoop::addReference($user->getUserID(), $sid); - } - /* remote planworld user */ - - elseif (isset($host) && $node = Planworld::getNodeInfo($host)) { - unset($host); /* JLO2 4/12/10 Required to stop permasnoops after calling remote users. */ - - if ($node['Version'] < 2) { - Snoop::_call($node, 'snoop.addReference', array($username, $user->getUsername() . '@' . PW_NAME)); - } else { - Snoop::_call($node, 'planworld.snoop.add', array($username, $user->getUsername() . '@' . PW_NAME)); - } - } - } - - foreach ($users_to_del as $u) { - if (strstr($u, '@')) { - list($username, $host) = explode('@', $u); - } - - $sid = Planworld::nameToID($u); - if (!isset($host) && $sid > 0) { - /* valid local user */ - - $success = $success && Snoop::removeReference($user->getUserID(), $sid); - } elseif (isset($host) && $node = Planworld::getNodeInfo($host)) { - /* remote planworld user */ - unset($host); /* JLO2 4/12/10 Required to stop permasnoops after calling remote users. */ - - if ($node['Version'] < 2) { - Snoop::_call($node, 'snoop.removeReference', array($username, $user->getUsername() . '@' . PW_NAME)); - } else { - Snoop::_call($node, 'planworld.snoop.remove', array($username, $user->getUsername() . '@' . PW_NAME)); - } - } - } - return $success; + Snoop::_call($node, 'planworld.snoop.remove', array($username, $user->getUsername() . '@' . PW_NAME)); } - } catch (PDOException $badquery) { - return PLANWORLD_ERROR; + } } + return $success; + } + } catch (PDOException $badquery) { + return PLANWORLD_ERROR; } + } - /* Previous version had sort arguments. The client should do the sorting, so we removed them. + /* Previous version had sort arguments. The client should do the sorting, so we removed them. * The code allows users to be sent in as usernames, objects, or user ids, and the original code did a different query based on that. * We have simplified so that user id is always used. */ - public static function getReferences(&$user) - { - $dbh = Planworld::_connect(); - $userid = -1; - if (is_int($user)) { - $userid = $user; - } elseif (is_string($user)) { - $userid = Planworld::nameToID($user); - } elseif (is_object($user)) { - $userid = $user->getUserID(); + public static function getReferences(&$user) + { + $dbh = Planworld::_connect(); + $userid = -1; + if (is_int($user)) { + $userid = $user; + } elseif (is_string($user)) { + $userid = Planworld::nameToID($user); + } elseif (is_object($user)) { + $userid = $user->getUserID(); + } + try { + if (!$dbh) { + throw new PDOException('Database connection not initialized.'); + } + $query = $dbh->prepare('SELECT s_uid, referenced, username, last_update FROM snoop,users WHERE uid=:uid AND users.id=s_uid'); + $queryArray = array('uid' => $userid); + $query->execute($queryArray); + $result = $query->fetchAll(); + if (!$result) { + return array(); + } else { + $return = array(); + /* April fool's easter egg */ + if (date('n-j') == '4-1') { + $uid = Planworld::getRandomUser(); + $return[] = array("userID" => $uid, + "userName" => Planworld::idToName($uid), + "date" => mktime(0, 0, 0, 4, 1, date('Y')), + "lastUpdate" => Planworld::getLastUpdate($uid)); } - try { - if (!$dbh) { - throw new PDOException('Database connection not initialized.'); - } - $query = $dbh->prepare('SELECT s_uid, referenced, username, last_update FROM snoop,users WHERE uid=:uid AND users.id=s_uid'); - $queryArray = array('uid' => $userid); - $query->execute($queryArray); - $result = $query->fetchAll(); - if (!$result) { - return array(); - } else { - $return = array(); - /* April fool's easter egg */ - if (date('n-j') == '4-1') { - $uid = Planworld::getRandomUser(); - $return[] = array("userID" => $uid, - "userName" => Planworld::idToName($uid), - "date" => mktime(0, 0, 0, 4, 1, date('Y')), - "lastUpdate" => Planworld::getLastUpdate($uid)); - } - - foreach ($result as $row) { - $return[] = array("userID" => (int) $row['s_uid'], - "userName" => $row['username'], - "date" => (int) $row['referenced'], - "lastUpdate" => (int) $row['last_update']); - } - - return $return; - } - } catch (PDOException $badquery) { - return PLANWORLD_ERROR; + + foreach ($result as $row) { + $return[] = array("userID" => (int) $row['s_uid'], + "userName" => $row['username'], + "date" => (int) $row['referenced'], + "lastUpdate" => (int) $row['last_update']); } + + return $return; + } + } catch (PDOException $badquery) { + return PLANWORLD_ERROR; } + } } diff --git a/code/lib/User.php b/code/lib/User.php index db39367..ca69cf0 100755 --- a/code/lib/User.php +++ b/code/lib/User.php @@ -74,7 +74,7 @@ class User { $this->dbh = Planworld::_connect(); $this->type = 'local'; if (is_string($uid)) { - $this->username = $uid; + $this->username = strtolower($uid); } else if (is_int($uid)) { $this->userID = (int)$uid; @@ -1055,6 +1055,148 @@ class User { } + function isUserIdInBlocklist ($buid) { + $boolReturn = true; + $boolReturn = true; + if(is_string($buid)){ + $buid = Planworld::nameToID($buid); + } + if((is_int($buid)) && ($buid != PLANWORLD_ERROR)){ + try{ + if(!$this->dbh){ + throw new PDOException('Database connection not initialized.'); + } + $query = $this->dbh->prepare('SELECT b_uid FROM block WHERE uid= :uid AND b_uid= :buid'); + $queryArray = array('uid' => $this->userID, 'buid' => $buid); + $query->execute($queryArray); + $result = $query->fetchAll(); + if (!$result){ + $boolReturn = false; + return $boolReturn; + } + else{ + $boolReturn = true; + return $boolReturn; + } + } + catch(PDOException $badquery){ + $boolReturn = true; + return $boolReturn; + } + } + return $boolReturn; + } + + + function doesBlockRelationshipExist($buid){ + $boolReturn = true; + if(is_string($buid)){ + $buid = Planworld::nameToID($buid); + } + if((is_int($buid)) && ($buid != PLANWORLD_ERROR)){ + try{ + if(!$this->dbh){ + throw new PDOException('Database connection not initialized.'); + } + $query = $this->dbh->prepare('SELECT b_uid FROM block WHERE (uid= :uid AND b_uid= :buid) OR (b_uid= :uid AND uid= :buid)'); + $queryArray = array('uid' => $this->userID, 'buid' => $buid); + $query->execute($queryArray); + $result = $query->fetchAll(); + if (!$result){ + $boolReturn = false; + return $boolReturn; + } + else{ + $boolReturn = true; + return $boolReturn; + } + } + catch(PDOException $badquery){ + $boolReturn = true; + return $boolReturn; + } + } + return $boolReturn; + } + + function getBlockListNames(){ + $arrayReturn = array(); + try{ + if(!$this->dbh){ + throw new PDOException('Database connection not initialized.'); + } + $query = $this->dbh->prepare('SELECT username FROM users LEFT JOIN block bl ON bl.b_uid=users.id WHERE uid= :uid ORDER BY username'); + $queryArray = array('uid' => $this->userID); + $query->execute($queryArray); + $result = $query->fetchAll(); + foreach ($result as $row){ + $arrayReturn[] = $row['username']; + } + } + catch(PDOException $badquery){ + $boolReturn = true; + return $arrayReturn; + } + return $arrayReturn; + } + + function getBlockedByNames(){ + $arrayReturn = array(); + try{ + if(!$this->dbh){ + throw new PDOException('Database connection not initialized.'); + } + $query = $this->dbh->prepare('SELECT username FROM users LEFT JOIN block bl ON bl.uid=users.id WHERE bl.b_uid= :uid ORDER BY username'); + $queryArray = array('uid' => $this->userID); + $query->execute($queryArray); + $result = $query->fetchAll(); + foreach ($result as $row){ + $arrayReturn[] = $row['username']; + } + } + catch(PDOException $badquery){ + $boolReturn = true; + return $arrayReturn; + } + return $arrayReturn; + } + + + function addUserToBocklist($uid){ + $intUid = $this->user->getUserID(); + $intBUid = -1; + if(is_int($uid)){ + $intBUid = $uid; + } + else{ + $intBUid = Planworld::nameToID($uid); + } + try{ + $query = $this->dbh->prepare('INSERT INTO block (b_uid, uid, added) VALUES (:buid, :uid, :added)'); + $queryArray = array('buid' => $intBUid, 'uid' => $intUid, 'added' => time()); + $query->execute($queryArray); + return true; + } + catch(PDOException $badquery){ + return false; + } + } + + + /* TODO: Flesh out functions for overriding lock and for getting lock times. */ + function removeUserFromBlocklistIfUnlocked ($uid) { + $intUid = $this->user->getUserID(); + $intBUid = -1; + if(is_int($uid)){ + $intBUid = $uid; + } + else{ + $intBUid = Planworld::nameToID($uid); + } + $query = $this->dbh->prepare('DELETE FROM block WHERE b_uid=:buid AND uid=:uid AND added < :lockthreshold'); + $queryArray = array('buid' => $intBUid, 'uid' => $intUid, 'lockthreshold' => (time() - LOCKTIME)); + $query->execute($queryArray); + } /** @@ -1094,13 +1236,13 @@ class User { } - /** + /** * JLO2 20191005 - A simplified form of setPlan that does not support the Archive pieces, drafts, or journaling. * Implemented so that we would have enough functionality to start building v3 around. */ function setPlanSimple ($plan) { $boolSuccess = true; - $timestamp = mktime(); + $timestamp = mktime(); $oldplan = $this->getPlanSimple($this); try{ @@ -1112,7 +1254,7 @@ class User { $boolSuccess = $query1->execute($queryArray1); } catch(PDOException $badquery1){ - return 'BLUEBERRY'; + return 'LAMPLIGHT'; } /* process snoop references */ @@ -1127,7 +1269,7 @@ class User { $queryArray2 = array('uid' => $this->userID, 'plan' =>$plan); $boolSuccess = $boolSuccess && $query2->execute($queryArray2); } - catch(PDOException $badquery1){ + catch(PDOException $badquery2){ return false; } return $boolSuccess; diff --git a/code/scaffolding/adduser.php b/code/scaffolding/adduser.php index 158cb78..afa8037 100644 --- a/code/scaffolding/adduser.php +++ b/code/scaffolding/adduser.php @@ -23,7 +23,7 @@ $newby->create(); $userExists = new User($username); $userId = $userExists->userID; - $pass = crypt($username, ((int)$userId + 45678)); + $pass = str_replace('.', '',(str_replace('/', '', crypt($stringLoginUser, ((int)$intLoginUser + 45678))))); $message = 'User ' . $username . ' created with password ' . $pass; } } diff --git a/code/scaffolding/debug_client.html b/code/scaffolding/debug_client.html index 5160c83..ecf4460 100644 --- a/code/scaffolding/debug_client.html +++ b/code/scaffolding/debug_client.html @@ -84,26 +84,26 @@ argument1 = ''; } else{ - argument1 = '/' + document.planrequest.arg1.value; + argument1 = '/' + document.planrequest.arg1.value.replace('/',''); } if((document.planrequest.arg2.value==null) || document.planrequest.arg2.value==''){ argument2 = ''; } else{ - argument2 = '/' + document.planrequest.arg2.value; + argument2 = '/' + document.planrequest.arg2.value.replace('/',''); } if((document.planrequest.arg3.value==null) || document.planrequest.arg3.value==''){ argument3 = ''; } else{ - argument3 = '/' + document.planrequest.arg3.value; + argument3 = '/' + document.planrequest.arg3.value.replace('/',''); } if((document.planrequest.arg4.value==null) || document.planrequest.arg4.value==''){ argument4 = ''; } else{ - argument4 = '/' + document.planrequest.arg4.value; + argument4 = '/' + document.planrequest.arg4.value.replace('/',''); } for (var i=0; i < document.planrequest.outputFormat.length; i++){ @@ -181,21 +181,25 @@ Category
Planworld Noun
Argument 1
diff --git a/setup/planworld_mysql_db_create.sql b/setup/planworld_mysql_db_create.sql index de5577c..8a57ee2 100644 --- a/setup/planworld_mysql_db_create.sql +++ b/setup/planworld_mysql_db_create.sql @@ -24,6 +24,13 @@ CREATE TABLE IF NOT EXISTS archive ( PRIMARY KEY (uid,posted) ); +CREATE TABLE IF NOT EXISTS block ( + uid bigint NOT NULL DEFAULT 0, + b_uid bigint NOT NULL DEFAULT 0, + added int NOT NULL DEFAULT 0, + PRIMARY KEY (uid,b_uid) +); + CREATE TABLE IF NOT EXISTS cookies ( id int NOT NULL DEFAULT 0, quote text NOT NULL, @@ -95,7 +102,7 @@ CREATE TABLE IF NOT EXISTS online ( ); CREATE TABLE IF NOT EXISTS plans ( - id int UNIQUE NOT NULL, + id int UNIQUE NOT NULL AUTO_INCREMENT, uid bigint NOT NULL DEFAULT 0, content text NOT NULL, PRIMARY KEY (uid) diff --git a/setup/readme.md b/setup/readme.md index 4ec6d1e..b0a68a3 100644 --- a/setup/readme.md +++ b/setup/readme.md @@ -1,17 +1,21 @@ **Readme For Planworld Alphas** -------------------------------------- -_Last Updated 20180409_ by jlodom00 -_Last Updated 20170406_ by jlodom00 +_Last Updated 220191012_ by jlodom00 _Releases in V3.00 Alpha Series_ -20170406 - "Louisbourg" - Send Only Release Without Clients -20180409 - "Loudon" - Fixed sendlist db query. Also make a note -- PHP 5.5 or later is required. Need to eliminate old JSON file and depedency. -MAKE ANOTHER NOTE -- The NOTE Production Database contains invalid usernames. We filter for this in PW->getallusers, but in the future we should have a cleaner process to eliminate them. ALSO getallusers is part of scaffolding and the API userslocalGet call but it doesn't return all users now, just the local ones who are properly formatted. -20191005 - "Gordon" - Interim Release to get watchlist and basic plan-handling functionality as well as other improvements out to potential programmers before testing is finished. Necessitated by some discussion of how blocklists will be implemented. + + 20191012 - "Sevenoaks" - Proper release of the features targeted in "Gordon" with many additions and bugfixes including always lowercasing usernames in important library calls. Most importantly, blocklists have been implemented. Enough of the API is now built that developers can create a proper client targeting it. We expect that the API will change as developers work with it. Only pseudo-authentication is supported via the "debug" API calls. + + 20191005 - "Gordon" - Interim Release to get watchlist and basic plan-handling functionality as well as other improvements out to potential programmers before testing is finished. Necessitated by some discussion of how blocklists will be implemented. + + 20180409 - "Loudon" - Fixed sendlist db query. Also make a note -- PHP 5.5 or later is required. Need to eliminate old JSON file and depedency. MAKE ANOTHER NOTE -- The NOTE Production Database contains invalid usernames. We filter for this in PW->getallusers, but in the future we should have a cleaner process to eliminate them. ALSO getallusers is part of scaffolding and the API userslocalGet call but it doesn't return all users now, just the local ones who are properly formatted. + + 20170406 - "Louisbourg" - Send Only Release Without Clients + _License_ This release does not yet include proper attribution for authors (Seth Fitzsimmons, Baker Franke, Johnnie Odom et al) but it is licensed using the GPLv2 as the previous version was. Future releases will more properly note this. -As of October 2019 this software uses HTML Purifier ( ) +As of October 2019 this software uses [HTML Purifier]( http://htmlpurifier.org/ ) _Introduction_ @@ -30,25 +34,29 @@ _Installation:_ 3. Edit code/config.php to match your configuration (it may not need any changes). 4. Modify client/htaccess.rename to fit the paths on your server and rename it to .htaccess (with leading period). 5. Edit client/index.php and change the path for $pathClientPrefix. -6. (Optional) Edit scaffolding/debug_client.html and change "var planworldbasedefault" and the nodeURL form input to the base client API url of your server (i.e. the URL a web browser would use to get to the client directory). +6. Make the directory at backend/standalone/HTMLPurifier/DefinitionCache/Serializer writable (probably by using "chmod 777 backend/standalone/HTMLPurifier/DefinitionCache/Serializer" ) +7. (Optional) If testing the environment, create a file in the backend directory called debug.on to enable the "debug" API category. Currently this is the only way to retrieve a token using the API. When transitioning to a production environment both the debug.php file in clientlib and the debug.on file should be removed. +8. (Optional) Edit scaffolding/debug_client.html and change "var planworldbasedefault" and the nodeURL form input to the base client API url of your server (i.e. the URL a web browser would use to get to the client directory). + _Testing Setup And Alpha Administration_ 1. You know that the engine is responding to API calls if you can open a web browser and go to http://myserver.com/path/to/client/3.00/system/version.xml (where /path/to/client/ is the URL path to the client directory) and get a page with "3.00" as a response. If you are having difficulty at this step, the problem is probably with your Apache configuration (especially your website config and very likely your Directory options). -2. Planworld is empty when you start, so you will need to use the webpages in the scaffolding directory to add users, create tokens for them, and see what users exist on your system. If you have difficulty here, check your mysql setup. +2. Planworld is empty when you start, so you will need to use the webpages in the scaffolding directory to add users. If you have difficulty here, check your mysql setup. -3. Once you have users and tokens, you can test API functionality with the debug client included in scaffolding/debug_client.html. See the Client API notes below for rough usage of this page. +3. Once you have users you can test API functionality with the debug client included in scaffolding/debug_client.html. See the Client API notes below for rough usage of this page. _The Planworld Client API_ -The major new feature of Planworld v3 is a RESTful API that can be used to write clients independent of the Planworld server code. This will allow for much more rapid and diverse development of user functionality in the future. It will also allow for major changes to the underlying server engine without disrupting the user experience. To deliver the API there is also a lot of work modernizing and cleaning up the underlying engine. +The major new feature of Planworld v3 is a REST API that can be used to write clients independent of the Planworld server code. This will allow for much more rapid and diverse development of user functionality in the future. It will also allow for major changes to the underlying server engine without disrupting the user experience. To deliver the API there is also a lot of work modernizing and cleaning up the underlying engine. + *URL Structure* -The basic structure of the API REST calls was determined around 2010 and has had only minor changes since then. A typical API call will look like the following: +The basic structure of the API REST calls was determined around [2010](https://github.com/joshuawdavidson/Planworld-v3-Client-API-Documentation) and has had only minor changes since then (although it does differ from the link above at present). A typical API call will look like the following: http://servername.com/some/path/client/0.00/category/verb/argument1/argument2.format?token=tokenumber @@ -66,10 +74,10 @@ VERSION: 0.00/ CATEGORY: category/ - This portion of the path is meant to make grouping planworld calls/functions/verbs easier, especially for those implementing the engine. If you have trouble making an API call, (especially using the debug client), check to make sure that this value is correct for the verb that is being called. Current values are "system", "send", "watchlist", and "plan". + This portion of the path is meant to make grouping planworld calls/functions/verbs easier, especially for those implementing the engine. If you have trouble making an API call, (especially using the debug client), check to make sure that this value is correct for the verb that is being called. Current values are "system", "plan", "send", "watch", and "block". VERB: verb/ - This is the actual verb or planworld function being called. These have different properties depending upon whether information is being retrieved (GET) or sent (POST) and the same verb may perform both roles. Current values are "version" (pairing with the "system" category), "send", and "sendlist" (both pairing with the "send" category). Of the four verbs listed, all support GET but only "send" supports POST. + This is the actual verb or planworld function being called. These have different properties depending upon whether information is being retrieved (GET) or sent (POST) and the same verb may perform both roles. ARGUMENTS: argument1/argument2 Planworld verbs may take an arbitrary number of arguments. Each of these is a separate directory-separator, but the last one should not have a trailing slash. @@ -78,46 +86,66 @@ FORMAT: .format Data will be returned to the client in the format specified here. Allowed values are txt, xml, and json. The URL always replaces the last forward slash with a period followed by format (i.e. /urlend.format instead of /urlend/). For xml and json, data is returned as an array whose key is the name of the Planworld verb. TOKEN: ?token=tokenumber - Authentication is currently handled by a tokening system. The mid-term plan (post v3.00) is to move to OAuth, and so the token is implemented as a parameter rather than an integral part of the URL. In this alpha tokens are numeric, but by release they should be alphanumeric. Not all verbs require a token -- for example the "version" can be called anonymously. + Authentication is currently handled by a tokening system. The mid-term plan (post v3.00) is to move to OAuth2, and so the token is implemented as a parameter rather than an integral part of the URL. In this alpha tokens are numeric, but by release they should be alphanumeric. Not all verbs require a token -- for example the "version" can be called anonymously. + *Posting Data* For calls that send data to the engine (such as posting sends and plans), the data should be posted under the name "planworld_post". They use the same URL format as any other call, just with a HTTP POST instead of a GET. + *Currently Supported Calls* + +Category - debug (The file implementing these API calls should be deleted in production.) + pseudologin : GET Takes two arguments -- the username and password (as returned by the "adduser" scaffolding tool) of the user logging in. Returns a token that should be used for all API calls requiring a token. + Category - system version : GET Returns the version of the Planworld API supported. No arguments. Does not require token. + userslocal : GET Returns all the current users registered locally to this Planworld node. Requires a token. -Category - Send (Tokens required for all) +Category - plan (Tokens required for all) + plan : GET Returns a user's plan. Takes one argument -- the username of the plan to be read. + plan : POST Returns a boolean indicating success or failure. Deletes a user's current plan and replaces it with the data in the POST. The plan data should be in basic HTML format (which could encompass plain text). Scripts and other potentially harmful content will be stripped out by HTMLPurifier. + +Category - send (Tokens required for all) send : GET Returns the full send conversation between the user and another user. Takes one argument -- the username of the other user in the conversation. send : POST Send a new message to another user. Takes one argument -- the username of the other user in the conversation. sendlist : GET Returns a list of every user with whom the current user has had a send conversation (similar to a watchlist). No arguments. -Category - Plan (Tokens required for all) - plan : GET Returns a user's plan. Takes one argument -- the username of the plan to be read. - : POST Returns a boolean indicating success or failure. Deletes a user's current plan and replaces it with the data in the POST. The plan data should be in HTML format (this could encompass plain text). -Category - Watchlist (Tokens required for all) -[NOTE: These calls are not well-documented and the POST versions have not been tested.] +Category - watch (Tokens required for all) watchlist : GET Returns a user's full watchlist. - watchlistgroup : GET Returns the same data as watchlist but in groups - groupcreate : POST Creates a watchlist group. The post data should be empty. Takes one URL argument -- the name of the group to be created. - groupmoveuser : POST Moves a user to a specified watchlist group. The post data should be empty. Takes two URL arguments -- the name of the user and the name of the group. - add: POST POST Adds a user to a watchlist group. The post data should be empty. Takes one URL argument -- the username to be added. + watchlistgroup : GET Returns the same data as watchlist but organized in user-defined groups (the default group is "People"). + groupcreate : POST Creates a watchlist group. The post data should contain the name of the group to be created. + groupmoveuser : POST Moves a user to a specified watchlist group. Takes one URL argument -- the name of the user. The name of the group should be in the post data. + add: POST Adds a user to a watchlist. The post data should contain the username to be added. + remove: POST Removes a user from a watchlist. The post data should contain the username to be removed. + +Category - block (Tokens required for all) + blocklist : GET Returns a user's full watchlist. + blockedby : GET Returns a list of users who are blocking the requested user. + add: POST Adds a user to a blocklist. The post data should contain the username to be added. + remove: POST Removes a user from a blocklist. The post data should contain the username to be removed. + + +_Writing a Client_ + +For this alpha release, you will need to create a user before making any client API calls. Afterwards all client functionality can be simulated using the APIs. Clients may be written on any platform and in any programming language -- as long as they can make GET and POST HTTP calls to the API URLs anything is possible. The API generally conforms to the conventions established by REST but we do expect some changes and improvements as we refine it through real-world client creation. -*Writing a Client* +Future releases will include example clients in a variety of languages. Currently the "Cend" client is available which implements the "send" catgeory calls using ReactJS. Otherwise the debug_client.html file may be used to simulate API calls. As of the "Sevenoaks" release we are turning our attention towards creating other reference clients. -For this alpha release, you will need to create a user and a token for that user before client calls can be made because there is no way to create tokens through the client API. Clients may be written on any platform and in any programming language -- as long as they can make GET and POST HTTP calls to the API URLs. The API is a REST API and generally conforms to the conventions established by that style. -Future releases will include example clients in a variety of languages. They have been omitted from this release because we needed to ship. +_Blocklists_ + +As social media has become more sophisticated and as online harassment has grown there has been a lively debate in the existing Planworld communities regarding what can be done to address the various ills afflicting social media as relates to Planworld. This discussion is not over and will require further sophistication in the future. As an interim step, blocklists have been implemented in the API. When a user blocks another user it cuts off all new contact between the two: Previous sends and watchlist entries remain, but no new sends can be sent and new watchlist information will not be sent. Most vitally, plans cannot be read and snoops will be discarded. This process works both ways: A user who blocks another user will not only be protected from that user, they are also blocking themselves from that user. _Planworld v3 Upgrade Work_ Much work has already been done on Planworld Version 3. Unfortunately, we cannot expose this previous work until some updates are made to the underlying libraries that form the lowest layer of the application. The database library that NOTE Planworld v2 uses is not only obsolete, but the successor library is obsolete also. Therefore it is necessary to replace every database call in the libraries with a much more forward-compatible call to the PHP PDO libraries. -As large sections of the Planworld libraries are rewritten, the pre-existing API code can be layered on top of it and new functionality can be rolled out even before the entire codebase has been upgraded. Even though the current release only supports "send", the plumbing that has been done to make even this functionality possible forms a foundation for the rest of the work. +As large sections of the Planworld libraries are rewritten, the pre-existing API code can be layered on top of it and new functionality can be rolled out even before the entire codebase has been upgraded. The current work is to take functions from the old lib files that make database calls and rewrite them in the new files, one by one, to use PDO. @@ -131,10 +159,10 @@ The basic rules for this work are: *EXAMPLE* -Here is a "before and after" example of the kind of work that needs to happen with the libraries. If you would like to contribute in this area, rather than working on clients, please contact Johnnie Odom and he will make libraries available for you to update. +Here is a "before and after" example of the kind of work that needs to happen with the libraries. If you would like to contribute in this area, rather than working on clients, please contact Johnnie Odom and he will make the current v2 library code available for you to update. *Before* - +``` /* string Planworld::idToName ($uid) converts numeric $uid to string representation @@ -164,11 +192,11 @@ Here is a "before and after" example of the kind of work that needs to happen wi return PLANWORLD_ERROR; } } - +``` *After* - +``` /* string Planworld::idToName ($uid) converts numeric $uid to string representation @@ -204,4 +232,5 @@ Here is a "before and after" example of the kind of work that needs to happen wi return PLANWORLD_ERROR; } return PLANWORLD_ERROR; - } \ No newline at end of file + } +``` \ No newline at end of file -- 1.8.3.1