grommunio /
grommunio-sync
| 1 | <?php |
||||
| 2 | |||||
| 3 | /* |
||||
| 4 | * SPDX-License-Identifier: AGPL-3.0-only |
||||
| 5 | * SPDX-FileCopyrightText: Copyright 2007-2016 Zarafa Deutschland GmbH |
||||
| 6 | * SPDX-FileCopyrightText: Copyright 2020-2025 grommunio GmbH |
||||
| 7 | * |
||||
| 8 | * This class checks and processes all incoming data of the request. |
||||
| 9 | */ |
||||
| 10 | |||||
| 11 | class Request { |
||||
| 12 | public const MAXMEMORYUSAGE = 0.9; // use max. 90% of allowed memory when syncing |
||||
| 13 | public const UNKNOWN = "unknown"; |
||||
| 14 | public const IMPERSONATE_DELIM = '#'; |
||||
| 15 | |||||
| 16 | /** |
||||
| 17 | * self::filterEvilInput() options. |
||||
| 18 | */ |
||||
| 19 | public const LETTERS_ONLY = 1; |
||||
| 20 | public const HEX_ONLY = 2; |
||||
| 21 | public const WORDCHAR_ONLY = 3; |
||||
| 22 | public const NUMBERS_ONLY = 4; |
||||
| 23 | public const NUMBERSDOT_ONLY = 5; |
||||
| 24 | public const HEX_EXTENDED = 6; |
||||
| 25 | public const ISO8601 = 7; |
||||
| 26 | public const HEX_EXTENDED2 = 8; |
||||
| 27 | |||||
| 28 | /** |
||||
| 29 | * Command parameters for base64 encoded requests (AS >= 12.1). |
||||
| 30 | */ |
||||
| 31 | public const COMMANDPARAM_ATTACHMENTNAME = 0; |
||||
| 32 | public const COMMANDPARAM_COLLECTIONID = 1; // deprecated |
||||
| 33 | public const COMMANDPARAM_COLLECTIONNAME = 2; // deprecated |
||||
| 34 | public const COMMANDPARAM_ITEMID = 3; |
||||
| 35 | public const COMMANDPARAM_LONGID = 4; |
||||
| 36 | public const COMMANDPARAM_PARENTID = 5; // deprecated |
||||
| 37 | public const COMMANDPARAM_OCCURRENCE = 6; |
||||
| 38 | public const COMMANDPARAM_OPTIONS = 7; // used by SmartReply, SmartForward, SendMail, ItemOperations |
||||
| 39 | public const COMMANDPARAM_USER = 8; // used by any command |
||||
| 40 | // possible bitflags for COMMANDPARAM_OPTIONS |
||||
| 41 | public const COMMANDPARAM_OPTIONS_SAVEINSENT = 0x01; |
||||
| 42 | public const COMMANDPARAM_OPTIONS_ACCEPTMULTIPART = 0x02; |
||||
| 43 | |||||
| 44 | private static $input; |
||||
| 45 | private static $output; |
||||
| 46 | private static $headers; |
||||
| 47 | private static $command; |
||||
| 48 | private static $method; |
||||
| 49 | private static $remoteAddr; |
||||
| 50 | private static $getUser; |
||||
| 51 | private static $devid; |
||||
| 52 | private static $devtype; |
||||
| 53 | private static $authUserString; |
||||
| 54 | private static $authUser; |
||||
| 55 | private static $authDomain; |
||||
| 56 | private static $authPassword; |
||||
| 57 | private static $impersonatedUser; |
||||
| 58 | private static $userIdentifier; |
||||
| 59 | private static $asProtocolVersion; |
||||
| 60 | private static $policykey; |
||||
| 61 | private static $useragent; |
||||
| 62 | private static $attachmentName; |
||||
| 63 | private static $collectionId; |
||||
| 64 | private static $itemId; |
||||
| 65 | private static $longId; // TODO |
||||
|
0 ignored issues
–
show
introduced
by
Loading history...
|
|||||
| 66 | private static $occurrence; // TODO |
||||
|
0 ignored issues
–
show
|
|||||
| 67 | private static $saveInSent; |
||||
| 68 | private static $acceptMultipart; |
||||
| 69 | private static $base64QueryDecoded; |
||||
| 70 | private static $expectedConnectionTimeout; |
||||
| 71 | private static $memoryLimit; |
||||
| 72 | |||||
| 73 | /** |
||||
| 74 | * Initializes request data. |
||||
| 75 | */ |
||||
| 76 | public static function Initialize() { |
||||
| 77 | // try to open stdin & stdout |
||||
| 78 | self::$input = fopen("php://input", "r"); |
||||
| 79 | self::$output = fopen("php://output", "w+"); |
||||
| 80 | |||||
| 81 | // Parse the standard GET parameters |
||||
| 82 | if (isset($_GET["Cmd"])) { |
||||
| 83 | self::$command = self::filterEvilInput($_GET["Cmd"], self::LETTERS_ONLY); |
||||
| 84 | } |
||||
| 85 | |||||
| 86 | // getUser is unfiltered, as everything is allowed.. even "/", "\" or ".." |
||||
| 87 | if (isset($_GET["User"])) { |
||||
| 88 | self::$getUser = strtolower((string) $_GET["User"]); |
||||
| 89 | if (defined('USE_FULLEMAIL_FOR_LOGIN') && !USE_FULLEMAIL_FOR_LOGIN) { |
||||
| 90 | self::$getUser = Utils::GetLocalPartFromEmail(self::$getUser); |
||||
| 91 | } |
||||
| 92 | } |
||||
| 93 | if (isset($_GET["DeviceId"])) { |
||||
| 94 | self::$devid = strtolower(self::filterEvilInput($_GET["DeviceId"], self::WORDCHAR_ONLY)); |
||||
| 95 | } |
||||
| 96 | if (isset($_GET["DeviceType"])) { |
||||
| 97 | self::$devtype = self::filterEvilInput($_GET["DeviceType"], self::LETTERS_ONLY); |
||||
| 98 | } |
||||
| 99 | if (isset($_GET["AttachmentName"])) { |
||||
| 100 | self::$attachmentName = self::filterEvilInput($_GET["AttachmentName"], self::HEX_EXTENDED2); |
||||
| 101 | } |
||||
| 102 | if (isset($_GET["CollectionId"])) { |
||||
| 103 | self::$collectionId = self::filterEvilInput($_GET["CollectionId"], self::HEX_EXTENDED2); |
||||
| 104 | } |
||||
| 105 | if (isset($_GET["ItemId"])) { |
||||
| 106 | self::$itemId = self::filterEvilInput($_GET["ItemId"], self::HEX_EXTENDED2); |
||||
| 107 | } |
||||
| 108 | if (isset($_GET["SaveInSent"]) && $_GET["SaveInSent"] == "T") { |
||||
| 109 | self::$saveInSent = true; |
||||
| 110 | } |
||||
| 111 | |||||
| 112 | if (isset($_SERVER["REQUEST_METHOD"])) { |
||||
| 113 | self::$method = self::filterEvilInput($_SERVER["REQUEST_METHOD"], self::LETTERS_ONLY); |
||||
| 114 | } |
||||
| 115 | // TODO check IPv6 addresses |
||||
| 116 | if (isset($_SERVER["REMOTE_ADDR"])) { |
||||
| 117 | self::$remoteAddr = self::filterIP($_SERVER["REMOTE_ADDR"]); |
||||
| 118 | } |
||||
| 119 | |||||
| 120 | // in protocol version > 14 mobile send these inputs as encoded query string |
||||
| 121 | if (!isset(self::$command) && !empty($_SERVER['QUERY_STRING']) && Utils::IsBase64String($_SERVER['QUERY_STRING'])) { |
||||
| 122 | self::decodeBase64URI(); |
||||
| 123 | if (!isset(self::$command) && isset(self::$base64QueryDecoded['Command'])) { |
||||
| 124 | self::$command = Utils::GetCommandFromCode(self::$base64QueryDecoded['Command']); |
||||
| 125 | } |
||||
| 126 | |||||
| 127 | if (!isset(self::$getUser) && isset(self::$base64QueryDecoded[self::COMMANDPARAM_USER])) { |
||||
| 128 | self::$getUser = strtolower(self::$base64QueryDecoded[self::COMMANDPARAM_USER]); |
||||
| 129 | if (defined('USE_FULLEMAIL_FOR_LOGIN') && !USE_FULLEMAIL_FOR_LOGIN) { |
||||
| 130 | self::$getUser = Utils::GetLocalPartFromEmail(self::$getUser); |
||||
| 131 | } |
||||
| 132 | } |
||||
| 133 | |||||
| 134 | if (!isset(self::$devid) && isset(self::$base64QueryDecoded['DevID'])) { |
||||
| 135 | self::$devid = strtolower(self::filterEvilInput(self::$base64QueryDecoded['DevID'], self::WORDCHAR_ONLY)); |
||||
| 136 | } |
||||
| 137 | |||||
| 138 | if (!isset(self::$devtype) && isset(self::$base64QueryDecoded['DevType'])) { |
||||
| 139 | self::$devtype = self::filterEvilInput(self::$base64QueryDecoded['DevType'], self::LETTERS_ONLY); |
||||
| 140 | } |
||||
| 141 | |||||
| 142 | if (isset(self::$base64QueryDecoded['PolKey'])) { |
||||
| 143 | self::$policykey = (int) self::filterEvilInput(self::$base64QueryDecoded['PolKey'], self::NUMBERS_ONLY); |
||||
| 144 | } |
||||
| 145 | |||||
| 146 | if (isset(self::$base64QueryDecoded['ProtVer'])) { |
||||
| 147 | self::$asProtocolVersion = self::filterEvilInput(self::$base64QueryDecoded['ProtVer'], self::NUMBERS_ONLY) / 10; |
||||
| 148 | } |
||||
| 149 | |||||
| 150 | if (isset(self::$base64QueryDecoded[self::COMMANDPARAM_ATTACHMENTNAME])) { |
||||
| 151 | self::$attachmentName = self::filterEvilInput(self::$base64QueryDecoded[self::COMMANDPARAM_ATTACHMENTNAME], self::HEX_EXTENDED2); |
||||
| 152 | } |
||||
| 153 | |||||
| 154 | if (isset(self::$base64QueryDecoded[self::COMMANDPARAM_COLLECTIONID])) { |
||||
| 155 | self::$collectionId = self::filterEvilInput(self::$base64QueryDecoded[self::COMMANDPARAM_COLLECTIONID], self::HEX_EXTENDED2); |
||||
| 156 | } |
||||
| 157 | |||||
| 158 | if (isset(self::$base64QueryDecoded[self::COMMANDPARAM_ITEMID])) { |
||||
| 159 | self::$itemId = self::filterEvilInput(self::$base64QueryDecoded[self::COMMANDPARAM_ITEMID], self::HEX_EXTENDED2); |
||||
| 160 | } |
||||
| 161 | |||||
| 162 | if (isset(self::$base64QueryDecoded[self::COMMANDPARAM_OPTIONS]) && (ord(self::$base64QueryDecoded[self::COMMANDPARAM_OPTIONS]) & self::COMMANDPARAM_OPTIONS_SAVEINSENT)) { |
||||
| 163 | self::$saveInSent = true; |
||||
| 164 | } |
||||
| 165 | |||||
| 166 | if (isset(self::$base64QueryDecoded[self::COMMANDPARAM_OPTIONS]) && (ord(self::$base64QueryDecoded[self::COMMANDPARAM_OPTIONS]) & self::COMMANDPARAM_OPTIONS_ACCEPTMULTIPART)) { |
||||
| 167 | self::$acceptMultipart = true; |
||||
| 168 | } |
||||
| 169 | } |
||||
| 170 | |||||
| 171 | // in base64 encoded query string user is not necessarily set |
||||
| 172 | if (!isset(self::$getUser) && isset($_SERVER['PHP_AUTH_USER'])) { |
||||
| 173 | [self::$getUser] = Utils::SplitDomainUser(strtolower((string) $_SERVER['PHP_AUTH_USER'])); |
||||
| 174 | if (defined('USE_FULLEMAIL_FOR_LOGIN') && !USE_FULLEMAIL_FOR_LOGIN) { |
||||
| 175 | self::$getUser = Utils::GetLocalPartFromEmail(self::$getUser); |
||||
| 176 | } |
||||
| 177 | } |
||||
| 178 | |||||
| 179 | // authUser & authPassword are unfiltered! |
||||
| 180 | // split username & domain if received as one |
||||
| 181 | if (isset($_SERVER['PHP_AUTH_USER'])) { |
||||
| 182 | [self::$authUserString, self::$authDomain] = Utils::SplitDomainUser($_SERVER['PHP_AUTH_USER']); |
||||
| 183 | self::$authPassword = $_SERVER['PHP_AUTH_PW'] ?? ""; |
||||
| 184 | } |
||||
| 185 | |||||
| 186 | // process impersonation |
||||
| 187 | self::$authUser = self::$authUserString; |
||||
| 188 | |||||
| 189 | if (defined('USE_FULLEMAIL_FOR_LOGIN') && !USE_FULLEMAIL_FOR_LOGIN) { |
||||
| 190 | self::$authUser = Utils::GetLocalPartFromEmail(self::$authUser); |
||||
| 191 | } |
||||
| 192 | |||||
| 193 | // get & convert configured memory limit |
||||
| 194 | $memoryLimit = ini_get('memory_limit'); |
||||
| 195 | if ($memoryLimit == -1) { |
||||
| 196 | self::$memoryLimit = false; |
||||
| 197 | } |
||||
| 198 | else { |
||||
| 199 | preg_replace_callback( |
||||
| 200 | '/(\-?\d+)(.?)/', |
||||
| 201 | function ($m) { |
||||
| 202 | self::$memoryLimit = $m[1] * 1024 ** strpos('BKMG', $m[2]) * self::MAXMEMORYUSAGE; |
||||
| 203 | }, |
||||
| 204 | strtoupper($memoryLimit) |
||||
| 205 | ); |
||||
| 206 | } |
||||
| 207 | } |
||||
| 208 | |||||
| 209 | /** |
||||
| 210 | * Reads and processes the request headers. |
||||
| 211 | */ |
||||
| 212 | public static function ProcessHeaders() { |
||||
| 213 | self::$headers = array_change_key_case(apache_request_headers(), CASE_LOWER); |
||||
|
0 ignored issues
–
show
It seems like
apache_request_headers() can also be of type true; however, parameter $array of array_change_key_case() does only seem to accept array, maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
| 214 | self::$useragent = self::$headers["user-agent"] ?? self::UNKNOWN; |
||||
| 215 | if (!isset(self::$asProtocolVersion)) { |
||||
| 216 | self::$asProtocolVersion = (isset(self::$headers["ms-asprotocolversion"])) ? self::filterEvilInput(self::$headers["ms-asprotocolversion"], self::NUMBERSDOT_ONLY) : GSync::GetLatestSupportedASVersion(); |
||||
| 217 | } |
||||
| 218 | |||||
| 219 | // if policykey is not yet set, try to set it from the header |
||||
| 220 | // the policy key might be set in Request::Initialize from the base64 encoded query |
||||
| 221 | if (!isset(self::$policykey)) { |
||||
| 222 | if (isset(self::$headers["x-ms-policykey"])) { |
||||
| 223 | self::$policykey = (int) self::filterEvilInput(self::$headers["x-ms-policykey"], self::NUMBERS_ONLY); |
||||
| 224 | } |
||||
| 225 | else { |
||||
| 226 | self::$policykey = 0; |
||||
| 227 | } |
||||
| 228 | } |
||||
| 229 | |||||
| 230 | if (isset(self::$base64QueryDecoded)) { |
||||
| 231 | SLog::Write(LOGLEVEL_DEBUG, sprintf("Request::ProcessHeaders(): base64 query string: '%s' (decoded: '%s')", $_SERVER['QUERY_STRING'], http_build_query(self::$base64QueryDecoded, '', ','))); |
||||
| 232 | if (isset(self::$policykey)) { |
||||
| 233 | self::$headers["x-ms-policykey"] = self::$policykey; |
||||
| 234 | } |
||||
| 235 | |||||
| 236 | if (isset(self::$asProtocolVersion)) { |
||||
| 237 | self::$headers["ms-asprotocolversion"] = self::$asProtocolVersion; |
||||
| 238 | } |
||||
| 239 | } |
||||
| 240 | |||||
| 241 | if (!isset(self::$acceptMultipart) && isset(self::$headers["ms-asacceptmultipart"]) && strtoupper(self::$headers["ms-asacceptmultipart"]) == "T") { |
||||
| 242 | self::$acceptMultipart = true; |
||||
| 243 | } |
||||
| 244 | |||||
| 245 | SLog::Write(LOGLEVEL_DEBUG, sprintf("Request::ProcessHeaders() ASVersion: %s", self::$asProtocolVersion)); |
||||
| 246 | |||||
| 247 | if (defined('USE_CUSTOM_REMOTE_IP_HEADER') && USE_CUSTOM_REMOTE_IP_HEADER !== false) { |
||||
|
0 ignored issues
–
show
|
|||||
| 248 | // make custom header compatible with Apache modphp |
||||
| 249 | $header = $apacheHeader = strtolower(USE_CUSTOM_REMOTE_IP_HEADER); |
||||
| 250 | if (str_starts_with($apacheHeader, 'http_')) { |
||||
| 251 | $apacheHeader = substr($apacheHeader, 5); |
||||
| 252 | } |
||||
| 253 | $apacheHeader = str_replace("_", "-", $apacheHeader); |
||||
| 254 | if (isset(self::$headers[$header]) || isset(self::$headers[$apacheHeader])) { |
||||
| 255 | $remoteIP = self::$headers[$header] ?? self::$headers[$apacheHeader]; |
||||
| 256 | // X-Forwarded-For may contain multiple IPs separated by comma: client, proxy1, proxy2. |
||||
| 257 | // In such case we will only check the client IP. |
||||
| 258 | if (str_contains((string) $remoteIP, ',')) { |
||||
| 259 | $remoteIP = trim(explode(',', (string) $remoteIP)[0]); |
||||
| 260 | } |
||||
| 261 | $remoteIP = self::filterIP($remoteIP); |
||||
| 262 | if ($remoteIP) { |
||||
| 263 | SLog::Write(LOGLEVEL_DEBUG, sprintf("Using custom header '%s' to determine remote IP: %s - connect is coming from IP: %s", USE_CUSTOM_REMOTE_IP_HEADER, $remoteIP, self::$remoteAddr)); |
||||
| 264 | self::$remoteAddr = $remoteIP; |
||||
| 265 | } |
||||
| 266 | } |
||||
| 267 | } |
||||
| 268 | } |
||||
| 269 | |||||
| 270 | /** |
||||
| 271 | * @return bool data sent or not |
||||
| 272 | */ |
||||
| 273 | public static function HasAuthenticationInfo() { |
||||
| 274 | return self::$authUser != "" && self::$authPassword != ""; |
||||
| 275 | } |
||||
| 276 | |||||
| 277 | /*---------------------------------------------------------------------------------------------------------- |
||||
| 278 | * Getter & Checker |
||||
| 279 | */ |
||||
| 280 | |||||
| 281 | /** |
||||
| 282 | * Returns the input stream. |
||||
| 283 | * |
||||
| 284 | * @return bool|handle false if not available |
||||
|
0 ignored issues
–
show
The type
handle was not found. Maybe you did not declare it correctly or list all dependencies?
The issue could also be caused by a filter entry in the build configuration.
If the path has been excluded in your configuration, e.g. filter:
dependency_paths: ["lib/*"]
For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths Loading history...
|
|||||
| 285 | */ |
||||
| 286 | public static function GetInputStream() { |
||||
| 287 | return self::$input ?? false; |
||||
| 288 | } |
||||
| 289 | |||||
| 290 | /** |
||||
| 291 | * Returns the output stream. |
||||
| 292 | * |
||||
| 293 | * @return bool|handle false if not available |
||||
| 294 | */ |
||||
| 295 | public static function GetOutputStream() { |
||||
| 296 | return self::$output ?? false; |
||||
| 297 | } |
||||
| 298 | |||||
| 299 | /** |
||||
| 300 | * Returns the request method. |
||||
| 301 | * |
||||
| 302 | * @return string |
||||
| 303 | */ |
||||
| 304 | public static function GetMethod() { |
||||
| 305 | return self::$method ?? self::UNKNOWN; |
||||
| 306 | } |
||||
| 307 | |||||
| 308 | /** |
||||
| 309 | * Returns the value of the user parameter of the querystring. |
||||
| 310 | * |
||||
| 311 | * @return bool|string false if not available |
||||
| 312 | */ |
||||
| 313 | public static function GetGETUser() { |
||||
| 314 | return self::$getUser ?? self::UNKNOWN; |
||||
| 315 | } |
||||
| 316 | |||||
| 317 | /** |
||||
| 318 | * Returns the value of the ItemId parameter of the querystring. |
||||
| 319 | * |
||||
| 320 | * @return bool|string false if not available |
||||
| 321 | */ |
||||
| 322 | public static function GetGETItemId() { |
||||
| 323 | return self::$itemId ?? false; |
||||
| 324 | } |
||||
| 325 | |||||
| 326 | /** |
||||
| 327 | * Returns the value of the CollectionId parameter of the querystring. |
||||
| 328 | * |
||||
| 329 | * @return bool|string false if not available |
||||
| 330 | */ |
||||
| 331 | public static function GetGETCollectionId() { |
||||
| 332 | return self::$collectionId ?? false; |
||||
| 333 | } |
||||
| 334 | |||||
| 335 | /** |
||||
| 336 | * Returns if the SaveInSent parameter of the querystring is set. |
||||
| 337 | * |
||||
| 338 | * @return bool |
||||
| 339 | */ |
||||
| 340 | public static function GetGETSaveInSent() { |
||||
| 341 | return self::$saveInSent ?? true; |
||||
| 342 | } |
||||
| 343 | |||||
| 344 | /** |
||||
| 345 | * Returns if the AcceptMultipart parameter of the querystring is set. |
||||
| 346 | * |
||||
| 347 | * @return bool |
||||
| 348 | */ |
||||
| 349 | public static function GetGETAcceptMultipart() { |
||||
| 350 | return self::$acceptMultipart ?? false; |
||||
| 351 | } |
||||
| 352 | |||||
| 353 | /** |
||||
| 354 | * Returns the value of the AttachmentName parameter of the querystring. |
||||
| 355 | * |
||||
| 356 | * @return bool|string false if not available |
||||
| 357 | */ |
||||
| 358 | public static function GetGETAttachmentName() { |
||||
| 359 | return self::$attachmentName ?? false; |
||||
| 360 | } |
||||
| 361 | |||||
| 362 | /** |
||||
| 363 | * Returns user that is synchronizing data. |
||||
| 364 | * If impersonation is active it returns the impersonated user, |
||||
| 365 | * else the auth user. |
||||
| 366 | * |
||||
| 367 | * @return bool|string false if not available |
||||
| 368 | */ |
||||
| 369 | public static function GetUser() { |
||||
| 370 | if (self::GetImpersonatedUser()) { |
||||
| 371 | return self::GetImpersonatedUser(); |
||||
| 372 | } |
||||
| 373 | |||||
| 374 | return self::GetAuthUser(); |
||||
| 375 | } |
||||
| 376 | |||||
| 377 | /** |
||||
| 378 | * Returns the AuthUser string send by the client. |
||||
| 379 | * |
||||
| 380 | * @return bool|string false if not available |
||||
| 381 | */ |
||||
| 382 | public static function GetAuthUserString() { |
||||
| 383 | return self::$authUserString ?? false; |
||||
| 384 | } |
||||
| 385 | |||||
| 386 | /** |
||||
| 387 | * Returns the impersonated user. If not available, returns false. |
||||
| 388 | * |
||||
| 389 | * @return bool|string false if not available |
||||
| 390 | */ |
||||
| 391 | public static function GetImpersonatedUser() { |
||||
| 392 | return self::$impersonatedUser ?? false; |
||||
| 393 | } |
||||
| 394 | |||||
| 395 | /** |
||||
| 396 | * Returns the authenticated user. |
||||
| 397 | * |
||||
| 398 | * @return bool|string false if not available |
||||
| 399 | */ |
||||
| 400 | public static function GetAuthUser() { |
||||
| 401 | return self::$authUser ?? false; |
||||
| 402 | } |
||||
| 403 | |||||
| 404 | /** |
||||
| 405 | * Returns the authenticated domain for the user. |
||||
| 406 | * |
||||
| 407 | * @return bool|string false if not available |
||||
| 408 | */ |
||||
| 409 | public static function GetAuthDomain() { |
||||
| 410 | return self::$authDomain ?? false; |
||||
| 411 | } |
||||
| 412 | |||||
| 413 | /** |
||||
| 414 | * Returns the transmitted password. |
||||
| 415 | * |
||||
| 416 | * @return bool|string false if not available |
||||
| 417 | */ |
||||
| 418 | public static function GetAuthPassword() { |
||||
| 419 | return self::$authPassword ?? false; |
||||
| 420 | } |
||||
| 421 | |||||
| 422 | /** |
||||
| 423 | * Returns the RemoteAddress. |
||||
| 424 | * |
||||
| 425 | * @return string |
||||
| 426 | */ |
||||
| 427 | public static function GetRemoteAddr() { |
||||
| 428 | return self::$remoteAddr ?? "UNKNOWN"; |
||||
| 429 | } |
||||
| 430 | |||||
| 431 | /** |
||||
| 432 | * Returns the command to be executed. |
||||
| 433 | * |
||||
| 434 | * @return bool|string false if not available |
||||
| 435 | */ |
||||
| 436 | public static function GetCommand() { |
||||
| 437 | return self::$command ?? false; |
||||
| 438 | } |
||||
| 439 | |||||
| 440 | /** |
||||
| 441 | * Returns the command code which is being executed. |
||||
| 442 | * |
||||
| 443 | * @return bool|string false if not available |
||||
| 444 | */ |
||||
| 445 | public static function GetCommandCode() { |
||||
| 446 | if (isset(self::$command)) { |
||||
| 447 | return Utils::GetCodeFromCommand(self::$command); |
||||
| 448 | } |
||||
| 449 | |||||
| 450 | return false; |
||||
| 451 | } |
||||
| 452 | |||||
| 453 | /** |
||||
| 454 | * Returns the device id transmitted. |
||||
| 455 | * |
||||
| 456 | * @return bool|string false if not available |
||||
| 457 | */ |
||||
| 458 | public static function GetDeviceID() { |
||||
| 459 | return self::$devid ?? false; |
||||
| 460 | } |
||||
| 461 | |||||
| 462 | /** |
||||
| 463 | * Returns the device type if transmitted. |
||||
| 464 | * |
||||
| 465 | * @return bool|string false if not available |
||||
| 466 | */ |
||||
| 467 | public static function GetDeviceType() { |
||||
| 468 | return self::$devtype ?? false; |
||||
| 469 | } |
||||
| 470 | |||||
| 471 | /** |
||||
| 472 | * Returns the value of supported AS protocol from the headers. |
||||
| 473 | * |
||||
| 474 | * @return bool|string false if not available |
||||
| 475 | */ |
||||
| 476 | public static function GetProtocolVersion() { |
||||
| 477 | return self::$asProtocolVersion ?? false; |
||||
| 478 | } |
||||
| 479 | |||||
| 480 | /** |
||||
| 481 | * Returns the user agent sent in the headers. |
||||
| 482 | * |
||||
| 483 | * @return bool|string false if not available |
||||
| 484 | */ |
||||
| 485 | public static function GetUserAgent() { |
||||
| 486 | return self::$useragent ?? self::UNKNOWN; |
||||
| 487 | } |
||||
| 488 | |||||
| 489 | /** |
||||
| 490 | * Returns policy key sent by the device. |
||||
| 491 | * |
||||
| 492 | * @return bool|int false if not available |
||||
| 493 | */ |
||||
| 494 | public static function GetPolicyKey() { |
||||
| 495 | return self::$policykey ?? false; |
||||
| 496 | } |
||||
| 497 | |||||
| 498 | /** |
||||
| 499 | * Indicates if a policy key was sent by the device. |
||||
| 500 | * |
||||
| 501 | * @return bool |
||||
| 502 | */ |
||||
| 503 | public static function WasPolicyKeySent() { |
||||
| 504 | return isset(self::$headers["x-ms-policykey"]); |
||||
| 505 | } |
||||
| 506 | |||||
| 507 | /** |
||||
| 508 | * Indicates if grommunio-sync was called with a POST request. |
||||
| 509 | * |
||||
| 510 | * @return bool |
||||
| 511 | */ |
||||
| 512 | public static function IsMethodPOST() { |
||||
| 513 | return self::$method == "POST"; |
||||
| 514 | } |
||||
| 515 | |||||
| 516 | /** |
||||
| 517 | * Indicates if grommunio-sync was called with a GET request. |
||||
| 518 | * |
||||
| 519 | * @return bool |
||||
| 520 | */ |
||||
| 521 | public static function IsMethodGET() { |
||||
| 522 | return self::$method == "GET"; |
||||
| 523 | } |
||||
| 524 | |||||
| 525 | /** |
||||
| 526 | * Indicates if grommunio-sync was called with a OPTIONS request. |
||||
| 527 | * |
||||
| 528 | * @return bool |
||||
| 529 | */ |
||||
| 530 | public static function IsMethodOPTIONS() { |
||||
| 531 | return self::$method == "OPTIONS"; |
||||
| 532 | } |
||||
| 533 | |||||
| 534 | /** |
||||
| 535 | * Sometimes strange device ids are submitted |
||||
| 536 | * No device information should be saved when this happens. |
||||
| 537 | * |
||||
| 538 | * @return bool false if invalid |
||||
| 539 | */ |
||||
| 540 | public static function IsValidDeviceID() { |
||||
| 541 | if (self::GetDeviceID() === "validate") { |
||||
| 542 | return false; |
||||
| 543 | } |
||||
| 544 | |||||
| 545 | return true; |
||||
| 546 | } |
||||
| 547 | |||||
| 548 | /** |
||||
| 549 | * Returns the amount of data sent in this request (from the headers). |
||||
| 550 | * |
||||
| 551 | * @return int |
||||
| 552 | */ |
||||
| 553 | public static function GetContentLength() { |
||||
| 554 | return (isset(self::$headers["content-length"])) ? (int) self::$headers["content-length"] : 0; |
||||
| 555 | } |
||||
| 556 | |||||
| 557 | /** |
||||
| 558 | * Returns the amount of seconds this request is able to be kept open without the client |
||||
| 559 | * closing it. This depends on the vendor. |
||||
| 560 | * |
||||
| 561 | * @return bool |
||||
| 562 | */ |
||||
| 563 | public static function GetExpectedConnectionTimeout() { |
||||
| 564 | // Different vendors implement different connection timeouts. |
||||
| 565 | // In order to optimize processing, we return a specific time for the major |
||||
| 566 | // classes currently known (feedback welcome). |
||||
| 567 | // The amount of time returned is somehow lower than the max timeout so we have |
||||
| 568 | // time for processing. |
||||
| 569 | |||||
| 570 | if (!isset(self::$expectedConnectionTimeout)) { |
||||
| 571 | // Apple and Windows Phone have higher timeouts (4min = 240sec) |
||||
| 572 | if (stripos(SYNC_TIMEOUT_LONG_DEVICETYPES, (string) self::GetDeviceType()) !== false) { |
||||
| 573 | self::$expectedConnectionTimeout = 210; |
||||
| 574 | } |
||||
| 575 | // Samsung devices have a intermediate timeout (90sec) |
||||
| 576 | elseif (stripos(SYNC_TIMEOUT_MEDIUM_DEVICETYPES, (string) self::GetDeviceType()) !== false) { |
||||
| 577 | self::$expectedConnectionTimeout = 85; |
||||
| 578 | } |
||||
| 579 | else { |
||||
| 580 | // for all other devices, a timeout of 30 seconds is expected |
||||
| 581 | self::$expectedConnectionTimeout = 28; |
||||
| 582 | } |
||||
| 583 | } |
||||
| 584 | |||||
| 585 | return self::$expectedConnectionTimeout; |
||||
| 586 | } |
||||
| 587 | |||||
| 588 | /** |
||||
| 589 | * Indicates if the maximum timeout for the devicetype of this request is |
||||
| 590 | * almost reached. |
||||
| 591 | * |
||||
| 592 | * @return bool |
||||
| 593 | */ |
||||
| 594 | public static function IsRequestTimeoutReached() { |
||||
| 595 | return (time() - $_SERVER["REQUEST_TIME"]) >= self::GetExpectedConnectionTimeout(); |
||||
| 596 | } |
||||
| 597 | |||||
| 598 | /** |
||||
| 599 | * Indicates if the memory usage limit is almost reached. |
||||
| 600 | * Processing should stop then to prevent hard out-of-memory issues. |
||||
| 601 | * The threshold is hardcoded at 90% in Request::MAXMEMORYUSAGE. |
||||
| 602 | * |
||||
| 603 | * @return bool |
||||
| 604 | */ |
||||
| 605 | public static function IsRequestMemoryLimitReached() { |
||||
| 606 | if (self::$memoryLimit === false) { |
||||
| 607 | return false; |
||||
| 608 | } |
||||
| 609 | |||||
| 610 | return memory_get_peak_usage(true) >= self::$memoryLimit; |
||||
| 611 | } |
||||
| 612 | |||||
| 613 | /** |
||||
| 614 | * Returns a user identifier coming from the logged user store, mostly an email address. |
||||
| 615 | * |
||||
| 616 | * @return string |
||||
| 617 | */ |
||||
| 618 | public static function GetUserIdentifier() { |
||||
| 619 | return self::$userIdentifier; |
||||
| 620 | } |
||||
| 621 | |||||
| 622 | /** |
||||
| 623 | * Sets a user identifier coming from the logged user store. |
||||
| 624 | * |
||||
| 625 | * @param string $userIdentifier |
||||
| 626 | */ |
||||
| 627 | public static function SetUserIdentifier($userIdentifier) { |
||||
| 628 | self::$userIdentifier = $userIdentifier; |
||||
| 629 | } |
||||
| 630 | |||||
| 631 | /*---------------------------------------------------------------------------------------------------------- |
||||
| 632 | * Private stuff |
||||
| 633 | */ |
||||
| 634 | |||||
| 635 | /** |
||||
| 636 | * Replaces all not allowed characters in a string. |
||||
| 637 | * |
||||
| 638 | * @param string $input the input string |
||||
| 639 | * @param int $filter one of the predefined filters: LETTERS_ONLY, HEX_ONLY, WORDCHAR_ONLY, NUMBERS_ONLY, NUMBERSDOT_ONLY |
||||
| 640 | * @param char $replacevalue (opt) a character the filtered characters should be replaced with |
||||
|
0 ignored issues
–
show
The type
char was not found. Maybe you did not declare it correctly or list all dependencies?
The issue could also be caused by a filter entry in the build configuration.
If the path has been excluded in your configuration, e.g. filter:
dependency_paths: ["lib/*"]
For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths Loading history...
|
|||||
| 641 | * |
||||
| 642 | * @return string |
||||
| 643 | */ |
||||
| 644 | private static function filterEvilInput($input, $filter, $replacevalue = '') { |
||||
| 645 | $re = false; |
||||
| 646 | if ($filter == self::LETTERS_ONLY) { |
||||
| 647 | $re = "/[^A-Za-z]/"; |
||||
| 648 | } |
||||
| 649 | elseif ($filter == self::HEX_ONLY) { |
||||
| 650 | $re = "/[^A-Fa-f0-9]/"; |
||||
| 651 | } |
||||
| 652 | elseif ($filter == self::WORDCHAR_ONLY) { |
||||
| 653 | $re = "/[^A-Za-z0-9]/"; |
||||
| 654 | } |
||||
| 655 | elseif ($filter == self::NUMBERS_ONLY) { |
||||
| 656 | $re = "/[^0-9]/"; |
||||
| 657 | } |
||||
| 658 | elseif ($filter == self::NUMBERSDOT_ONLY) { |
||||
| 659 | $re = "/[^0-9\\.]/"; |
||||
| 660 | } |
||||
| 661 | elseif ($filter == self::HEX_EXTENDED) { |
||||
| 662 | $re = "/[^A-Fa-f0-9\\:\\.]/"; |
||||
| 663 | } |
||||
| 664 | elseif ($filter == self::HEX_EXTENDED2) { |
||||
| 665 | $re = "/[^A-Fa-f0-9\\:USGI]/"; |
||||
| 666 | } // Folder origin constants from DeviceManager::FLD_ORIGIN_* (C already hex) |
||||
| 667 | elseif ($filter == self::ISO8601) { |
||||
| 668 | $re = "/[^\\d{8}T\\d{6}Z]/"; |
||||
| 669 | } |
||||
| 670 | |||||
| 671 | return ($re) ? preg_replace($re, $replacevalue, $input) : ''; |
||||
| 672 | } |
||||
| 673 | |||||
| 674 | /** |
||||
| 675 | * If $input is a valid IPv4 or IPv6 address, returns a valid compact IPv4 or IPv6 address string. |
||||
| 676 | * Otherwise, it will strip all characters that are neither numerical or '.' and prefix with "bad-ip". |
||||
| 677 | * |
||||
| 678 | * @param string $input The ipv4/ipv6 address |
||||
| 679 | * |
||||
| 680 | * @return string |
||||
| 681 | */ |
||||
| 682 | private static function filterIP($input) { |
||||
| 683 | $in_addr = @inet_pton($input); |
||||
| 684 | if ($in_addr === false) { |
||||
| 685 | return 'badip-' . self::filterEvilInput($input, self::HEX_EXTENDED); |
||||
| 686 | } |
||||
| 687 | |||||
| 688 | return inet_ntop($in_addr); |
||||
| 689 | } |
||||
| 690 | |||||
| 691 | /** |
||||
| 692 | * Returns base64 encoded "php://input" |
||||
| 693 | * With POST request (our case), you can open and read |
||||
| 694 | * multiple times "php://input". |
||||
| 695 | * |
||||
| 696 | * @param int $maxLength max. length to be returned. Default: return all |
||||
| 697 | * |
||||
| 698 | * @return string - base64 encoded wbxml |
||||
| 699 | */ |
||||
| 700 | public static function GetInputAsBase64($maxLength = -1) { |
||||
| 701 | $input = fopen('php://input', 'r'); |
||||
| 702 | $wbxml = base64_encode(stream_get_contents($input, $maxLength)); |
||||
| 703 | fclose($input); |
||||
| 704 | |||||
| 705 | return $wbxml; |
||||
| 706 | } |
||||
| 707 | |||||
| 708 | /** |
||||
| 709 | * Decodes base64 encoded query parameters. Based on dw2412 contribution. |
||||
| 710 | */ |
||||
| 711 | private static function decodeBase64URI() { |
||||
| 712 | /* |
||||
| 713 | * The query string has a following structure. Number in () is position: |
||||
| 714 | * 1 byte - protocol version (0) |
||||
| 715 | * 1 byte - command code (1) |
||||
| 716 | * 2 bytes - locale (2) |
||||
| 717 | * 1 byte - device ID length (4) |
||||
| 718 | * variable - device ID (4+device ID length) |
||||
| 719 | * 1 byte - policy key length (5+device ID length) |
||||
| 720 | * 0 or 4 bytes - policy key (5+device ID length + policy key length) |
||||
| 721 | * 1 byte - device type length (6+device ID length + policy key length) |
||||
| 722 | * variable - device type (6+device ID length + policy key length + device type length) |
||||
| 723 | * variable - command parameters, array which consists of: |
||||
| 724 | * 1 byte - tag |
||||
| 725 | * 1 byte - length |
||||
| 726 | * variable - value of the parameter |
||||
| 727 | * |
||||
| 728 | */ |
||||
| 729 | $decoded = base64_decode((string) $_SERVER['QUERY_STRING']); |
||||
| 730 | $devIdLength = ord($decoded[4]); // device ID length |
||||
| 731 | $polKeyLength = ord($decoded[5 + $devIdLength]); // policy key length |
||||
| 732 | $devTypeLength = ord($decoded[6 + $devIdLength + $polKeyLength]); // device type length |
||||
| 733 | // unpack the decoded query string values |
||||
| 734 | self::$base64QueryDecoded = unpack("CProtVer/CCommand/vLocale/CDevIDLen/H" . ($devIdLength * 2) . "DevID/CPolKeyLen" . ($polKeyLength == 4 ? "/VPolKey" : "") . "/CDevTypeLen/A" . $devTypeLength . "DevType", $decoded); |
||||
| 735 | |||||
| 736 | // get the command parameters |
||||
| 737 | $pos = 7 + $devIdLength + $polKeyLength + $devTypeLength; |
||||
| 738 | $decoded = substr($decoded, $pos); |
||||
| 739 | while (strlen($decoded) > 0) { |
||||
| 740 | $paramLength = ord($decoded[1]); |
||||
| 741 | $unpackedParam = unpack("CParamTag/CParamLength/A" . $paramLength . "ParamValue", $decoded); |
||||
| 742 | self::$base64QueryDecoded[ord($decoded[0])] = $unpackedParam['ParamValue']; |
||||
| 743 | // remove parameter from decoded query string |
||||
| 744 | $decoded = substr($decoded, 2 + $paramLength); |
||||
| 745 | } |
||||
| 746 | } |
||||
| 747 | } |
||||
| 748 |