1<?php 2 3namespace RainLoop; 4 5class Utils 6{ 7 /** 8 * @var string 9 */ 10 static $CookieDefaultPath = ''; 11 12 /** 13 * @var bool|null 14 */ 15 static $CookieDefaultSecure = null; 16 17 static $Cookies = null; 18 19 static $RsaKey = null; 20 21 /** 22 * @return void 23 */ 24 private function __construct() 25 { 26 } 27 28 /** 29 * @param string $sFileName 30 * @param string $sSignature 31 * 32 * @return bool 33 */ 34 static public function PgpVerifyFile($sFileName, $sSignature) 35 { 36 $sKeyFile = APP_VERSION_ROOT_PATH.'app/resources/RainLoop.asc'; 37 if (\file_exists($sKeyFile) && \file_exists($sFileName) && !empty($sSignature)) 38 { 39 $sKeyFile = @\file_get_contents($sKeyFile); 40 return !empty($sKeyFile); // TODO 41 } 42 43 return false; 44 } 45 46 /** 47 * @return string 48 */ 49 static public function RsaPrivateKey() 50 { 51 if (!empty(\RainLoop\Utils::$RsaKey)) 52 { 53 return \RainLoop\Utils::$RsaKey; 54 } 55 56 \RainLoop\Utils::$RsaKey = \file_exists(APP_PRIVATE_DATA.'rsa/private') ? 57 \file_get_contents(APP_PRIVATE_DATA.'rsa/private') : ''; 58 59 \RainLoop\Utils::$RsaKey = \is_string(\RainLoop\Utils::$RsaKey) ? \RainLoop\Utils::$RsaKey : ''; 60 } 61 62 /** 63 * @param string $sString 64 * @param string $sKey = '' 65 * 66 * @return string|false 67 */ 68 static public function EncryptStringRSA($sString, $sKey = '') 69 { 70 $sResult = ''; 71 $sKey = \md5($sKey); 72 73 $sPrivateKey = \RainLoop\Utils::RsaPrivateKey(); 74 if (!empty($sPrivateKey)) 75 { 76 $oPrivKey = \openssl_pkey_get_private($sPrivateKey); 77 $oKeyDetails = \openssl_pkey_get_details($oPrivKey); 78 79 if (!empty($oKeyDetails['key']) && !empty($oKeyDetails['bits'])) 80 { 81 $oPubKey = \openssl_pkey_get_public($oKeyDetails['key']); 82 83 $iC = (($oKeyDetails['bits'] / 8) - 15); 84 $aString = \str_split($sString, $iC); 85 86 foreach ($aString as $iIndex => $sLine) 87 { 88 $sEncrypted = ''; 89 \openssl_public_encrypt($sLine, $sEncrypted, $oPubKey); 90 $aString[$iIndex] = $sEncrypted; 91 } 92 93 $aString[] = $sKey; 94 $sResult = @\serialize($aString); 95 96 \openssl_free_key($oPubKey); 97 } 98 99 \openssl_free_key($oPrivKey); 100 } 101 102 return $sResult; 103 } 104 105 /** 106 * @param string $sString 107 * @param string $sKey = '' 108 * 109 * @return string|false 110 */ 111 static public function DecryptStringRSA($sString, $sKey = '') 112 { 113 $sResult = ''; 114 $sKey = \md5($sKey); 115 116 $sPrivateKey = \RainLoop\Utils::RsaPrivateKey(); 117 if (!empty($sPrivateKey) && !empty($sString)) 118 { 119 $oPrivKey = \openssl_pkey_get_private($sPrivateKey); 120 121 $aString = @\unserialize($sString); 122 if (\is_array($aString)) 123 { 124 if ($sKey === \array_pop($aString)) 125 { 126 foreach ($aString as $iIndex => $sLine) 127 { 128 $sDecrypted = ''; 129 \openssl_private_decrypt($sLine, $sDecrypted, $oPrivKey); 130 $aString[$iIndex] = $sDecrypted; 131 } 132 133 $sResult = \implode('', $aString); 134 } 135 } 136 137 \openssl_free_key($oPrivKey); 138 } 139 140 return $sResult; 141 } 142 143 /** 144 * @param string $sString 145 * @param string $sKey 146 * 147 * @return string 148 */ 149 static public function EncryptString($sString, $sKey) 150 { 151 return \MailSo\Base\Crypt::XxteaEncrypt($sString, $sKey); 152 } 153 154 /** 155 * @param string $sEncriptedString 156 * @param string $sKey 157 * 158 * @return string 159 */ 160 static public function DecryptString($sEncriptedString, $sKey) 161 { 162 return \MailSo\Base\Crypt::XxteaDecrypt($sEncriptedString, $sKey); 163 } 164 165 /** 166 * @param string $sString 167 * @param string $sKey 168 * 169 * @return string 170 */ 171 static public function EncryptStringQ($sString, $sKey) 172 { 173// if (\MailSo\Base\Utils::FunctionExistsAndEnabled('openssl_pkey_get_private')) 174// { 175// return \RainLoop\Utils::EncryptStringRSA($sString, 176// $sKey.'Q'.\RainLoop\Utils::GetShortToken()); 177// } 178 179 return \MailSo\Base\Crypt::XxteaEncrypt($sString, 180 $sKey.'Q'.\RainLoop\Utils::GetShortToken()); 181 } 182 183 /** 184 * @param string $sEncriptedString 185 * @param string $sKey 186 * 187 * @return string 188 */ 189 static public function DecryptStringQ($sEncriptedString, $sKey) 190 { 191// if (\MailSo\Base\Utils::FunctionExistsAndEnabled('openssl_pkey_get_private')) 192// { 193// return \RainLoop\Utils::DecryptStringRSA($sEncriptedString, 194// $sKey.'Q'.\RainLoop\Utils::GetShortToken()); 195// } 196 197 return \MailSo\Base\Crypt::XxteaDecrypt($sEncriptedString, 198 $sKey.'Q'.\RainLoop\Utils::GetShortToken()); 199 } 200 201 /** 202 * @param array $aValues 203 * @param string $sCustomKey = '' 204 * 205 * @return string 206 */ 207 static public function EncodeKeyValues(array $aValues, $sCustomKey = '') 208 { 209 return \MailSo\Base\Utils::UrlSafeBase64Encode( 210 \RainLoop\Utils::EncryptString(@\serialize($aValues), \md5(APP_SALT.$sCustomKey))); 211 } 212 213 /** 214 * @param string $sEncodedValues 215 * @param string $sCustomKey = '' 216 * 217 * @return array 218 */ 219 static public function DecodeKeyValues($sEncodedValues, $sCustomKey = '') 220 { 221 $aResult = @\unserialize( 222 \RainLoop\Utils::DecryptString( 223 \MailSo\Base\Utils::UrlSafeBase64Decode($sEncodedValues), \md5(APP_SALT.$sCustomKey))); 224 225 return \is_array($aResult) ? $aResult : array(); 226 } 227 228 /** 229 * @param array $aValues 230 * @param string $sCustomKey = '' 231 * 232 * @return string 233 */ 234 static public function EncodeKeyValuesQ(array $aValues, $sCustomKey = '') 235 { 236 return \MailSo\Base\Utils::UrlSafeBase64Encode( 237 \RainLoop\Utils::EncryptStringQ( 238 @\serialize($aValues), \md5(APP_SALT.$sCustomKey))); 239 } 240 241 /** 242 * @param string $sEncodedValues 243 * @param string $sCustomKey = '' 244 * 245 * @return array 246 */ 247 static public function DecodeKeyValuesQ($sEncodedValues, $sCustomKey = '') 248 { 249 $aResult = @\unserialize( 250 \RainLoop\Utils::DecryptStringQ( 251 \MailSo\Base\Utils::UrlSafeBase64Decode($sEncodedValues), \md5(APP_SALT.$sCustomKey))); 252 253 return \is_array($aResult) ? $aResult : array(); 254 } 255 256 /** 257 * @return string 258 */ 259 static public function GetConnectionToken() 260 { 261 $sKey = 'rltoken'; 262 263 $sToken = \RainLoop\Utils::GetCookie($sKey, null); 264 if (null === $sToken) 265 { 266 $sToken = \MailSo\Base\Utils::Md5Rand(APP_SALT); 267 \RainLoop\Utils::SetCookie($sKey, $sToken, \time() + 60 * 60 * 24 * 30); 268 } 269 270 return \md5('Connection'.APP_SALT.$sToken.'Token'.APP_SALT); 271 } 272 273 /** 274 * @return string 275 */ 276 static public function Fingerprint() 277 { 278 return \md5(empty($_SERVER['HTTP_USER_AGENT']) ? 'RainLoopFingerprint' : $_SERVER['HTTP_USER_AGENT']); 279 } 280 281 /** 282 * @return string 283 */ 284 static public function GetShortToken() 285 { 286 $sKey = 'rlsession'; 287 288 $sToken = \RainLoop\Utils::GetCookie($sKey, null); 289 if (null === $sToken) 290 { 291 $sToken = \MailSo\Base\Utils::Md5Rand(APP_SALT); 292 \RainLoop\Utils::SetCookie($sKey, $sToken, 0); 293 } 294 295 return \md5('Session'.APP_SALT.$sToken.'Token'.APP_SALT); 296 } 297 298 /** 299 * @return void 300 */ 301 static public function UpdateConnectionToken() 302 { 303 $sKey = 'rltoken'; 304 305 $sToken = \RainLoop\Utils::GetCookie($sKey, ''); 306 if (!empty($sToken)) 307 { 308 \RainLoop\Utils::SetCookie($sKey, $sToken, \time() + 60 * 60 * 24 * 30); 309 } 310 } 311 312 /** 313 * @return string 314 */ 315 static public function GetCsrfToken() 316 { 317 return \md5('Csrf'.APP_SALT.self::GetConnectionToken().'Token'.APP_SALT); 318 } 319 320 /** 321 * @param string $sPath 322 * 323 * @return string 324 */ 325 public static function PathMD5($sPath) 326 { 327 $sResult = ''; 328 if (@\is_dir($sPath)) 329 { 330 $oDirIterator = new \RecursiveDirectoryIterator($sPath); 331 $oIterator = new \RecursiveIteratorIterator($oDirIterator, \RecursiveIteratorIterator::SELF_FIRST); 332 333 foreach ($oIterator as $oFile) 334 { 335 $sResult = \md5($sResult.($oFile->isFile() ? \md5_file($oFile) : $oFile)); 336 } 337 } 338 339 return $sResult; 340 } 341 342 /** 343 * @param string $sFileName 344 * @param array $aResultLang 345 * 346 * @return void 347 */ 348 public static function ReadAndAddLang($sFileName, &$aResultLang) 349 { 350 if (\file_exists($sFileName)) 351 { 352 $isYml = '.yml' === substr($sFileName, -4); 353 if ($isYml) 354 { 355 $aLang = \spyc_load(\str_replace(array(': >-', ': |-', ': |+'), array(': >', ': |', ': |'), \file_get_contents($sFileName))); 356 if (\is_array($aLang)) 357 { 358 \reset($aLang); 359 $sLangKey = key($aLang); 360 if (isset($aLang[$sLangKey]) && is_array($aLang[$sLangKey])) 361 { 362 $aLang = $aLang[$sLangKey]; 363 } 364 else 365 { 366 $aLang = null; 367 } 368 } 369 } 370 else 371 { 372 $aLang = \RainLoop\Utils::CustomParseIniFile($sFileName, true); 373 } 374 375 if (\is_array($aLang)) 376 { 377 foreach ($aLang as $sKey => $mValue) 378 { 379 if (\is_array($mValue)) 380 { 381 foreach ($mValue as $sSecKey => $mSecValue) 382 { 383 $aResultLang[$sKey.'/'.$sSecKey] = $mSecValue; 384 } 385 } 386 else 387 { 388 $aResultLang[$sKey] = $mValue; 389 } 390 } 391 } 392 } 393 } 394 395 /** 396 * @param string $sDir 397 * @param string $sType = '' 398 * @return array 399 */ 400 public static function FolderFiles($sDir, $sType = '') 401 { 402 $aResult = array(); 403 if (@\is_dir($sDir)) 404 { 405 if (false !== ($rDirHandle = @\opendir($sDir))) 406 { 407 while (false !== ($sFile = @\readdir($rDirHandle))) 408 { 409 if (empty($sType) || $sType === \substr($sFile, -\strlen($sType))) 410 { 411 if (\is_file($sDir.'/'.$sFile)) 412 { 413 $aResult[] = $sFile; 414 } 415 } 416 } 417 418 @\closedir($rDirHandle); 419 } 420 } 421 422 return $aResult; 423 } 424 425 /** 426 * @param string $sHtml 427 * 428 * @return string 429 */ 430 public static function ClearHtmlOutput($sHtml) 431 { 432// return $sHtml; 433 return \trim(\str_replace('> <', '><', 434 \str_replace('" />', '"/>', 435 \preg_replace('/[\s]+ /i', ' ', 436 \preg_replace('/ [\s]+/i', ' ', 437 \preg_replace('/[\r\n\t]+/', ' ', 438 $sHtml 439 )))))); 440 } 441 442 /** 443 * @param string $sKey 444 * @return bool 445 */ 446 public static function FastCheck($sKey) 447 { 448 $bResult = false; 449 450 $aMatches = array(); 451 if (!empty($sKey) && 0 < \strlen($sKey) && \preg_match('/^(RL[\d]+)\-(.+)\-([^\-]+)$/', $sKey, $aMatches) && 3 === \count($aMatches)) 452 { 453 $bResult = $aMatches[3] === \strtoupper(\base_convert(\crc32(\md5( 454 $aMatches[1].'-'.$aMatches[2].'-')), 10, 32)); 455 } 456 457 return $bResult; 458 } 459 460 /** 461 * @param array $aList 462 * @param string $sDirName 463 * @param string $sNameSuffix = '' 464 */ 465 public static function CompileTemplates(&$aList, $sDirName, $sNameSuffix = '') 466 { 467 if (\file_exists($sDirName)) 468 { 469 $aFileList = \RainLoop\Utils::FolderFiles($sDirName, '.html'); 470 471 foreach ($aFileList as $sName) 472 { 473 $sTemplateName = \substr($sName, 0, -5).$sNameSuffix; 474 $aList[$sTemplateName] = $sDirName.'/'.$sName; 475 } 476 } 477 } 478 479 /** 480 * @param string $sName 481 * @param mixed $mDefault = null 482 * @return mixed 483 */ 484 public static function GetCookie($sName, $mDefault = null) 485 { 486 if (null === \RainLoop\Utils::$Cookies) 487 { 488 \RainLoop\Utils::$Cookies = is_array($_COOKIE) ? $_COOKIE : array(); 489 } 490 491 return isset(\RainLoop\Utils::$Cookies[$sName]) ? \RainLoop\Utils::$Cookies[$sName] : $mDefault; 492 } 493 494 public static function SetCookie($sName, $sValue = '', $iExpire = 0, $sPath = null, $sDomain = null, $bSecure = null, $bHttpOnly = true) 495 { 496 if (null === \RainLoop\Utils::$Cookies) 497 { 498 \RainLoop\Utils::$Cookies = is_array($_COOKIE) ? $_COOKIE : array(); 499 } 500 501 if (null === $sPath) 502 { 503 $sPath = \RainLoop\Utils::$CookieDefaultPath; 504 $sPath = $sPath && 0 < \strlen($sPath) ? $sPath : null; 505 } 506 507 if (null === $bSecure) 508 { 509 $bSecure = \RainLoop\Utils::$CookieDefaultSecure; 510 } 511 512 \RainLoop\Utils::$Cookies[$sName] = $sValue; 513 @\setcookie($sName, $sValue, $iExpire, $sPath, $sDomain, $bSecure, $bHttpOnly); 514 } 515 516 public static function ClearCookie($sName) 517 { 518 if (null === \RainLoop\Utils::$Cookies) 519 { 520 \RainLoop\Utils::$Cookies = is_array($_COOKIE) ? $_COOKIE : array(); 521 } 522 523 $sPath = \RainLoop\Utils::$CookieDefaultPath; 524 $sPath = $sPath && 0 < \strlen($sPath) ? $sPath : null; 525 526 unset(\RainLoop\Utils::$Cookies[$sName]); 527 @\setcookie($sName, '', \time() - 3600 * 24 * 30, $sPath); 528 } 529 530 /** 531 * @return bool 532 */ 533 public static function IsOwnCloud() 534 { 535 return isset($_ENV['RAINLOOP_OWNCLOUD']) && $_ENV['RAINLOOP_OWNCLOUD'] && 536 \class_exists('OC'); 537 } 538 /** 539 * @return bool 540 */ 541 public static function IsOwnCloudLoggedIn() 542 { 543 return self::IsOwnCloud() && \class_exists('OCP\User') && \OCP\User::isLoggedIn(); 544 } 545 546 /** 547 * @param string $sV 548 * @param bool $bEncode = false 549 * 550 * @return string 551 */ 552 public static function UrlEncode($sV, $bEncode = false) 553 { 554 return $bEncode ? \urlencode($sV) : $sV; 555 } 556 557 /** 558 * @return string 559 */ 560 public static function WebPath() 561 { 562 $sAppPath = ''; 563 if (\RainLoop\Utils::IsOwnCloud()) 564 { 565 if (\class_exists('OC_App')) 566 { 567 $sAppPath = \rtrim(\trim(\OC_App::getAppWebPath('rainloop')), '\\/').'/app/'; 568 } 569 570 if (empty($sAppPath)) 571 { 572 $sUrl = \MailSo\Base\Http::SingletonInstance()->GetUrl(); 573 if ($sUrl && \preg_match('/\/index\.php\/apps\/rainloop/', $sUrl)) 574 { 575 $sAppPath = \preg_replace('/\/index\.php\/apps\/rainloop.+$/', 576 '/apps/rainloop/app/', $sUrl); 577 } 578 } 579 } 580 581 return $sAppPath; 582 } 583 /** 584 * @return string 585 */ 586 public static function WebVersionPath() 587 { 588 return self::WebPath().'rainloop/v/'.APP_VERSION.'/'; 589 } 590 591 /** 592 * @return string 593 */ 594 public static function WebStaticPath() 595 { 596 return self::WebVersionPath().'static/'; 597 } 598 599 /** 600 * @param array $aSuggestions 601 * 602 * @return array 603 */ 604 public static function RemoveSuggestionDuplicates($aSuggestions) 605 { 606 $aResult = array(); 607 608 if (is_array($aSuggestions)) 609 { 610 $aCache = array(); 611 foreach ($aSuggestions as $aItem) 612 { 613 $sLine = \implode('~~', $aItem); 614 if (!isset($aCache[$sLine])) 615 { 616 $aCache[$sLine] = true; 617 $aResult[] = $aItem; 618 } 619 } 620 } 621 622 return $aResult; 623 } 624 625 /** 626 * @param string $sFileName 627 * @param bool $bProcessSections = false 628 * 629 * @return array 630 */ 631 public static function CustomParseIniFile($sFileName, $bProcessSections = false) 632 { 633// if (\MailSo\Base\Utils::FunctionExistsAndEnabled('parse_ini_file')) 634// { 635// return @\parse_ini_file($sFileName, !!$bProcessSections); 636// } 637 638 $sData = @\file_get_contents($sFileName); 639 return \is_string($sData) ? @\parse_ini_string($sData, !!$bProcessSections) : null; 640 } 641 642 public static function CustomBaseConvert($sNumberInput, $sFromBaseInput = '0123456789', $sToBaseInput = '0123456789') 643 { 644 if ($sFromBaseInput === $sToBaseInput) 645 { 646 return $sNumberInput; 647 } 648 649 $mFromBase = \str_split($sFromBaseInput, 1); 650 $mToBase = \str_split($sToBaseInput, 1); 651 $aNumber = \str_split($sNumberInput, 1); 652 $iFromLen = \strlen($sFromBaseInput); 653 $iToLen = \strlen($sToBaseInput); 654 $numberLen = \strlen($sNumberInput); 655 $mRetVal = ''; 656 657 if ($sToBaseInput === '0123456789') 658 { 659 $mRetVal = 0; 660 for ($iIndex = 1; $iIndex <= $numberLen; $iIndex++) 661 { 662 $mRetVal = \bcadd($mRetVal, \bcmul(\array_search($aNumber[$iIndex - 1], $mFromBase), \bcpow($iFromLen, $numberLen - $iIndex))); 663 } 664 665 return $mRetVal; 666 } 667 668 if ($sFromBaseInput != '0123456789') 669 { 670 $sBase10 = \RainLoop\Utils::CustomBaseConvert($sNumberInput, $sFromBaseInput, '0123456789'); 671 } 672 else 673 { 674 $sBase10 = $sNumberInput; 675 } 676 677 if ($sBase10 < \strlen($sToBaseInput)) 678 { 679 return $mToBase[$sBase10]; 680 } 681 682 while ($sBase10 !== '0') 683 { 684 $mRetVal = $mToBase[\bcmod($sBase10, $iToLen)].$mRetVal; 685 $sBase10 = \bcdiv($sBase10, $iToLen, 0); 686 } 687 688 return $mRetVal; 689 } 690}