1
0
Fork 0
mirror of https://github.com/YunoHost-Apps/z-push_ynh.git synced 2024-09-03 18:05:58 +02:00
z-push_ynh/sources/lib/request/request.php
2014-12-18 18:08:26 +01:00

637 lines
No EOL
21 KiB
PHP

<?php
/***********************************************
* File : request.php
* Project : Z-Push
* Descr : This class checks and processes
* all incoming data of the request.
*
* Created : 01.10.2007
*
* Copyright 2007 - 2013 Zarafa Deutschland GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation with the following additional
* term according to sec. 7:
*
* According to sec. 7 of the GNU Affero General Public License, version 3,
* the terms of the AGPL are supplemented with the following terms:
*
* "Zarafa" is a registered trademark of Zarafa B.V.
* "Z-Push" is a registered trademark of Zarafa Deutschland GmbH
* The licensing of the Program under the AGPL does not imply a trademark license.
* Therefore any rights, title and interest in our trademarks remain entirely with us.
*
* However, if you propagate an unmodified version of the Program you are
* allowed to use the term "Z-Push" to indicate that you distribute the Program.
* Furthermore you may use our trademarks where it is necessary to indicate
* the intended purpose of a product or service provided you use it in accordance
* with honest practices in industrial or commercial matters.
* If you want to propagate modified versions of the Program under the name "Z-Push",
* you may only do so if you have a written permission by Zarafa Deutschland GmbH
* (to acquire a permission please contact Zarafa at trademark@zarafa.com).
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Consult LICENSE file for details
************************************************/
class Request {
const UNKNOWN = "unknown";
/**
* self::filterEvilInput() options
*/
const LETTERS_ONLY = 1;
const HEX_ONLY = 2;
const WORDCHAR_ONLY = 3;
const NUMBERS_ONLY = 4;
const NUMBERSDOT_ONLY = 5;
const HEX_EXTENDED = 6;
/**
* Command parameters for base64 encoded requests (AS >= 12.1)
*/
const COMMANDPARAM_ATTACHMENTNAME = 0;
const COMMANDPARAM_COLLECTIONID = 1; //deprecated
const COMMANDPARAM_COLLECTIONNAME = 2; //deprecated
const COMMANDPARAM_ITEMID = 3;
const COMMANDPARAM_LONGID = 4;
const COMMANDPARAM_PARENTID = 5; //deprecated
const COMMANDPARAM_OCCURRENCE = 6;
const COMMANDPARAM_OPTIONS = 7; //used by SmartReply, SmartForward, SendMail, ItemOperations
const COMMANDPARAM_USER = 8; //used by any command
//possible bitflags for COMMANDPARAM_OPTIONS
const COMMANDPARAM_OPTIONS_SAVEINSENT = 0x01;
const COMMANDPARAM_OPTIONS_ACCEPTMULTIPART = 0x02;
static private $input;
static private $output;
static private $headers;
static private $getparameters;
static private $command;
static private $device;
static private $method;
static private $remoteAddr;
static private $getUser;
static private $devid;
static private $devtype;
static private $authUser;
static private $authDomain;
static private $authPassword;
static private $asProtocolVersion;
static private $policykey;
static private $useragent;
static private $attachmentName;
static private $collectionId;
static private $itemId;
static private $longId; //TODO
static private $occurence; //TODO
static private $saveInSent;
static private $acceptMultipart;
/**
* Initializes request data
*
* @access public
* @return
*/
static public function Initialize() {
// try to open stdin & stdout
self::$input = fopen("php://input", "r");
self::$output = fopen("php://output", "w+");
// Parse the standard GET parameters
if(isset($_GET["Cmd"]))
self::$command = self::filterEvilInput($_GET["Cmd"], self::LETTERS_ONLY);
// getUser is unfiltered, as everything is allowed.. even "/", "\" or ".."
if(isset($_GET["User"])) {
self::$getUser = strtolower($_GET["User"]);
if(defined('USE_FULLEMAIL_FOR_LOGIN') && ! USE_FULLEMAIL_FOR_LOGIN) {
self::$getUser = Utils::GetLocalPartFromEmail(self::$getUser);
}
}
if(isset($_GET["DeviceId"]))
self::$devid = strtolower(self::filterEvilInput($_GET["DeviceId"], self::WORDCHAR_ONLY));
if(isset($_GET["DeviceType"]))
self::$devtype = self::filterEvilInput($_GET["DeviceType"], self::LETTERS_ONLY);
if (isset($_GET["AttachmentName"]))
self::$attachmentName = self::filterEvilInput($_GET["AttachmentName"], self::HEX_EXTENDED);
if (isset($_GET["CollectionId"]))
self::$collectionId = self::filterEvilInput($_GET["CollectionId"], self::HEX_ONLY);
if (isset($_GET["ItemId"]))
self::$itemId = self::filterEvilInput($_GET["ItemId"], self::HEX_ONLY);
if (isset($_GET["SaveInSent"]) && $_GET["SaveInSent"] == "T")
self::$saveInSent = true;
if(isset($_SERVER["REQUEST_METHOD"]))
self::$method = self::filterEvilInput($_SERVER["REQUEST_METHOD"], self::LETTERS_ONLY);
// TODO check IPv6 addresses
if(isset($_SERVER["REMOTE_ADDR"]))
self::$remoteAddr = self::filterEvilInput($_SERVER["REMOTE_ADDR"], self::NUMBERSDOT_ONLY);
// in protocol version > 14 mobile send these inputs as encoded query string
if (!isset(self::$command) && !empty($_SERVER['QUERY_STRING']) && Utils::IsBase64String($_SERVER['QUERY_STRING'])) {
$query = Utils::DecodeBase64URI($_SERVER['QUERY_STRING']);
if (!isset(self::$command) && isset($query['Command']))
self::$command = Utils::GetCommandFromCode($query['Command']);
if (!isset(self::$getUser) && isset($query[self::COMMANDPARAM_USER])) {
self::$getUser = strtolower($query[self::COMMANDPARAM_USER]);
if(defined('USE_FULLEMAIL_FOR_LOGIN') && ! USE_FULLEMAIL_FOR_LOGIN) {
self::$getUser = Utils::GetLocalPartFromEmail(self::$getUser);
}
}
if (!isset(self::$devid) && isset($query['DevID']))
self::$devid = strtolower(self::filterEvilInput($query['DevID'], self::WORDCHAR_ONLY));
if (!isset(self::$devtype) && isset($query['DevType']))
self::$devtype = self::filterEvilInput($query['DevType'], self::LETTERS_ONLY);
if (isset($query['PolKey']))
self::$policykey = (int) self::filterEvilInput($query['PolKey'], self::NUMBERS_ONLY);
if (isset($query['ProtVer']))
self::$asProtocolVersion = self::filterEvilInput($query['ProtVer'], self::NUMBERS_ONLY) / 10;
if (isset($query[self::COMMANDPARAM_ATTACHMENTNAME]))
self::$attachmentName = self::filterEvilInput($query[self::COMMANDPARAM_ATTACHMENTNAME], self::HEX_EXTENDED);
if (isset($query[self::COMMANDPARAM_COLLECTIONID]))
self::$collectionId = self::filterEvilInput($query[self::COMMANDPARAM_COLLECTIONID], self::HEX_ONLY);
if (isset($query[self::COMMANDPARAM_ITEMID]))
self::$itemId = self::filterEvilInput($query[self::COMMANDPARAM_ITEMID], self::HEX_ONLY);
if (isset($query[self::COMMANDPARAM_OPTIONS]) && (ord($query[self::COMMANDPARAM_OPTIONS]) & self::COMMANDPARAM_OPTIONS_SAVEINSENT))
self::$saveInSent = true;
if (isset($query[self::COMMANDPARAM_OPTIONS]) && (ord($query[self::COMMANDPARAM_OPTIONS]) & self::COMMANDPARAM_OPTIONS_ACCEPTMULTIPART))
self::$acceptMultipart = true;
}
// in base64 encoded query string user is not necessarily set
if (!isset(self::$getUser) && isset($_SERVER['PHP_AUTH_USER'])) {
list(self::$getUser,) = Utils::SplitDomainUser(strtolower($_SERVER['PHP_AUTH_USER']));
if(defined('USE_FULLEMAIL_FOR_LOGIN') && ! USE_FULLEMAIL_FOR_LOGIN) {
self::$getUser = Utils::GetLocalPartFromEmail(self::$getUser);
}
}
}
/**
* Reads and processes the request headers
*
* @access public
* @return
*/
static public function ProcessHeaders() {
self::$headers = array_change_key_case(apache_request_headers(), CASE_LOWER);
self::$useragent = (isset(self::$headers["user-agent"]))? self::$headers["user-agent"] : self::UNKNOWN;
if (!isset(self::$asProtocolVersion))
self::$asProtocolVersion = (isset(self::$headers["ms-asprotocolversion"]))? self::filterEvilInput(self::$headers["ms-asprotocolversion"], self::NUMBERSDOT_ONLY) : ZPush::GetLatestSupportedASVersion();
//if policykey is not yet set, try to set it from the header
//the policy key might be set in Request::Initialize from the base64 encoded query
if (!isset(self::$policykey)) {
if (isset(self::$headers["x-ms-policykey"]))
self::$policykey = (int) self::filterEvilInput(self::$headers["x-ms-policykey"], self::NUMBERS_ONLY);
else
self::$policykey = 0;
}
if (!empty($_SERVER['QUERY_STRING']) && Utils::IsBase64String($_SERVER['QUERY_STRING'])) {
ZLog::Write(LOGLEVEL_DEBUG, "Using data from base64 encoded query string");
if (isset(self::$policykey))
self::$headers["x-ms-policykey"] = self::$policykey;
if (isset(self::$asProtocolVersion))
self::$headers["ms-asprotocolversion"] = self::$asProtocolVersion;
}
if (!isset(self::$acceptMultipart) && isset(self::$headers["ms-asacceptmultipart"]) && strtoupper(self::$headers["ms-asacceptmultipart"]) == "T") {
self::$acceptMultipart = true;
}
ZLog::Write(LOGLEVEL_DEBUG, sprintf("Request::ProcessHeaders() ASVersion: %s", self::$asProtocolVersion));
if (defined('USE_X_FORWARDED_FOR_HEADER') && USE_X_FORWARDED_FOR_HEADER == true && isset(self::$headers["x-forwarded-for"])) {
$forwardedIP = self::filterEvilInput(self::$headers["x-forwarded-for"], self::NUMBERSDOT_ONLY);
if ($forwardedIP) {
self::$remoteAddr = $forwardedIP;
ZLog::Write(LOGLEVEL_INFO, sprintf("'X-Forwarded-for' indicates remote IP: %s", self::$remoteAddr));
}
}
}
/**
* Reads and parses the HTTP-Basic-Auth data
*
* @access public
* @return boolean data sent or not
*/
static public function AuthenticationInfo() {
// split username & domain if received as one
if (isset($_SERVER['PHP_AUTH_USER'])) {
list(self::$authUser, self::$authDomain) = Utils::SplitDomainUser($_SERVER['PHP_AUTH_USER']);
self::$authPassword = (isset($_SERVER['PHP_AUTH_PW']))?$_SERVER['PHP_AUTH_PW'] : "";
}
if(defined('USE_FULLEMAIL_FOR_LOGIN') && ! USE_FULLEMAIL_FOR_LOGIN) {
self::$authUser = Utils::GetLocalPartFromEmail(self::$authUser);
}
// authUser & authPassword are unfiltered!
return (self::$authUser != "" && self::$authPassword != "");
}
/**----------------------------------------------------------------------------------------------------------
* Getter & Checker
*/
/**
* Returns the input stream
*
* @access public
* @return handle/boolean false if not available
*/
static public function GetInputStream() {
if (isset(self::$input))
return self::$input;
else
return false;
}
/**
* Returns the output stream
*
* @access public
* @return handle/boolean false if not available
*/
static public function GetOutputStream() {
if (isset(self::$output))
return self::$output;
else
return false;
}
/**
* Returns the request method
*
* @access public
* @return string
*/
static public function GetMethod() {
if (isset(self::$method))
return self::$method;
else
return self::UNKNOWN;
}
/**
* Returns the value of the user parameter of the querystring
*
* @access public
* @return string/boolean false if not available
*/
static public function GetGETUser() {
if (isset(self::$getUser))
return self::$getUser;
else
return self::UNKNOWN;
}
/**
* Returns the value of the ItemId parameter of the querystring
*
* @access public
* @return string/boolean false if not available
*/
static public function GetGETItemId() {
if (isset(self::$itemId))
return self::$itemId;
else
return false;
}
/**
* Returns the value of the CollectionId parameter of the querystring
*
* @access public
* @return string/boolean false if not available
*/
static public function GetGETCollectionId() {
if (isset(self::$collectionId))
return self::$collectionId;
else
return false;
}
/**
* Returns if the SaveInSent parameter of the querystring is set
*
* @access public
* @return boolean
*/
static public function GetGETSaveInSent() {
if (isset(self::$saveInSent))
return self::$saveInSent;
else
return true;
}
/**
* Returns if the AcceptMultipart parameter of the querystring is set
*
* @access public
* @return boolean
*/
static public function GetGETAcceptMultipart() {
if (isset(self::$acceptMultipart))
return self::$acceptMultipart;
else
return false;
}
/**
* Returns the value of the AttachmentName parameter of the querystring
*
* @access public
* @return string/boolean false if not available
*/
static public function GetGETAttachmentName() {
if (isset(self::$attachmentName))
return self::$attachmentName;
else
return false;
}
/**
* Returns the authenticated user
*
* @access public
* @return string/boolean false if not available
*/
static public function GetAuthUser() {
if (isset(self::$authUser))
return self::$authUser;
else
return false;
}
/**
* Returns the authenticated domain for the user
*
* @access public
* @return string/boolean false if not available
*/
static public function GetAuthDomain() {
if (isset(self::$authDomain))
return self::$authDomain;
else
return false;
}
/**
* Returns the transmitted password
*
* @access public
* @return string/boolean false if not available
*/
static public function GetAuthPassword() {
if (isset(self::$authPassword))
return self::$authPassword;
else
return false;
}
/**
* Returns the RemoteAddress
*
* @access public
* @return string
*/
static public function GetRemoteAddr() {
if (isset(self::$remoteAddr))
return self::$remoteAddr;
else
return "UNKNOWN";
}
/**
* Returns the command to be executed
*
* @access public
* @return string/boolean false if not available
*/
static public function GetCommand() {
if (isset(self::$command))
return self::$command;
else
return false;
}
/**
* Returns the command code which is being executed
*
* @access public
* @return string/boolean false if not available
*/
static public function GetCommandCode() {
if (isset(self::$command))
return Utils::GetCodeFromCommand(self::$command);
else
return false;
}
/**
* Returns the device id transmitted
*
* @access public
* @return string/boolean false if not available
*/
static public function GetDeviceID() {
if (isset(self::$devid))
return self::$devid;
else
return false;
}
/**
* Returns the device type if transmitted
*
* @access public
* @return string/boolean false if not available
*/
static public function GetDeviceType() {
if (isset(self::$devtype))
return self::$devtype;
else
return false;
}
/**
* Returns the value of supported AS protocol from the headers
*
* @access public
* @return string/boolean false if not available
*/
static public function GetProtocolVersion() {
if (isset(self::$asProtocolVersion))
return self::$asProtocolVersion;
else
return false;
}
/**
* Returns the user agent sent in the headers
*
* @access public
* @return string/boolean false if not available
*/
static public function GetUserAgent() {
if (isset(self::$useragent))
return self::$useragent;
else
return self::UNKNOWN;
}
/**
* Returns policy key sent by the device
*
* @access public
* @return int/boolean false if not available
*/
static public function GetPolicyKey() {
if (isset(self::$policykey))
return self::$policykey;
else
return false;
}
/**
* Indicates if a policy key was sent by the device
*
* @access public
* @return boolean
*/
static public function WasPolicyKeySent() {
return isset(self::$headers["x-ms-policykey"]);
}
/**
* Indicates if Z-Push was called with a POST request
*
* @access public
* @return boolean
*/
static public function IsMethodPOST() {
return (self::$method == "POST");
}
/**
* Indicates if Z-Push was called with a GET request
*
* @access public
* @return boolean
*/
static public function IsMethodGET() {
return (self::$method == "GET");
}
/**
* Indicates if Z-Push was called with a OPTIONS request
*
* @access public
* @return boolean
*/
static public function IsMethodOPTIONS() {
return (self::$method == "OPTIONS");
}
/**
* Sometimes strange device ids are sumbitted
* No device information should be saved when this happens
*
* @access public
* @return boolean false if invalid
*/
static public function IsValidDeviceID() {
if (self::GetDeviceID() === "validate" || self::GetDeviceID() === "webservice")
return false;
else
return true;
}
/**
* Returns the amount of data sent in this request (from the headers)
*
* @access public
* @return int
*/
static public function GetContentLength() {
return (isset(self::$headers["content-length"]))? (int) self::$headers["content-length"] : 0;
}
/**
* Returns the amount of seconds this request is able to be kept open without the client
* closing it. This depends on the vendor.
*
* @access public
* @return boolean
*/
static public function GetExpectedConnectionTimeout() {
// Different vendors implement different connection timeouts.
// In order to optimize processing, we return a specific time for the major
// classes currently known (feedback welcome).
// The amount of time returned is somehow lower than the max timeout so we have
// time for processing.
// Apple and Windows Phone have higher timeouts (4min = 240sec)
if (in_array(self::GetDeviceType(), array("iPod", "iPad", "iPhone", "WP"))) {
return 200;
}
// Samsung devices have a intermediate timeout (90sec)
if (in_array(self::GetDeviceType(), array("SAMSUNGGTI"))) {
return 50;
}
// for all other devices, a timeout of 30 seconds is expected
return 20;
}
/**----------------------------------------------------------------------------------------------------------
* Private stuff
*/
/**
* Replaces all not allowed characters in a string
*
* @param string $input the input string
* @param int $filter one of the predefined filters: LETTERS_ONLY, HEX_ONLY, WORDCHAR_ONLY, NUMBERS_ONLY, NUMBERSDOT_ONLY
* @param char $replacevalue (opt) a character the filtered characters should be replaced with
*
* @access public
* @return string
*/
static private function filterEvilInput($input, $filter, $replacevalue = '') {
$re = false;
if ($filter == self::LETTERS_ONLY) $re = "/[^A-Za-z]/";
else if ($filter == self::HEX_ONLY) $re = "/[^A-Fa-f0-9]/";
else if ($filter == self::WORDCHAR_ONLY) $re = "/[^A-Za-z0-9]/";
else if ($filter == self::NUMBERS_ONLY) $re = "/[^0-9]/";
else if ($filter == self::NUMBERSDOT_ONLY) $re = "/[^0-9\.]/";
else if ($filter == self::HEX_EXTENDED) $re = "/[^A-Fa-f0-9\:]/";
return ($re) ? preg_replace($re, $replacevalue, $input) : '';
}
}
?>