mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-07-09 06:28:28 +03:00
Improved open_basedir and proxy
This commit is contained in:
parent
7e2dfbc38a
commit
3aa319b230
4 changed files with 30 additions and 276 deletions
|
|
@ -175,251 +175,6 @@ class Http
|
|||
return $sIp;
|
||||
}
|
||||
|
||||
public function SendPostRequest(string $sUrl, array $aPost = array(), string $sCustomUserAgent = 'MailSo Http User Agent (v1)', int &$iCode = 0,
|
||||
?\MailSo\Log\Logger $oLogger = null, int $iTimeout = 20, string $sProxy = '', string $sProxyAuth = '') : string
|
||||
{
|
||||
$aOptions = array(
|
||||
CURLOPT_URL => $sUrl,
|
||||
CURLOPT_HEADER => false,
|
||||
CURLOPT_FAILONERROR => true,
|
||||
CURLOPT_SSL_VERIFYPEER => false,
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_POST => true,
|
||||
CURLOPT_POSTFIELDS => \http_build_query($aPost, '', '&'),
|
||||
CURLOPT_TIMEOUT => (int) $iTimeout
|
||||
);
|
||||
|
||||
if (\strlen($sCustomUserAgent))
|
||||
{
|
||||
$aOptions[CURLOPT_USERAGENT] = $sCustomUserAgent;
|
||||
}
|
||||
|
||||
if (\strlen($sProxy))
|
||||
{
|
||||
$aOptions[CURLOPT_PROXY] = $sProxy;
|
||||
if (\strlen($sProxyAuth))
|
||||
{
|
||||
$aOptions[CURLOPT_PROXYUSERPWD] = $sProxyAuth;
|
||||
}
|
||||
}
|
||||
|
||||
$oCurl = \curl_init();
|
||||
\curl_setopt_array($oCurl, $aOptions);
|
||||
|
||||
if ($oLogger)
|
||||
{
|
||||
$oLogger->Write('cURL: Send post request: '.$sUrl);
|
||||
}
|
||||
|
||||
$mResult = \curl_exec($oCurl);
|
||||
|
||||
$iCode = (int) \curl_getinfo($oCurl, CURLINFO_HTTP_CODE);
|
||||
$sContentType = (string) \curl_getinfo($oCurl, CURLINFO_CONTENT_TYPE);
|
||||
|
||||
if ($oLogger)
|
||||
{
|
||||
$oLogger->Write('cURL: Post request result: (Status: '.$iCode.', ContentType: '.$sContentType.')');
|
||||
if (false === $mResult || 200 !== $iCode)
|
||||
{
|
||||
$oLogger->Write('cURL: Error: '.\curl_error($oCurl), \MailSo\Log\Enumerations\Type::WARNING);
|
||||
}
|
||||
}
|
||||
|
||||
if (\is_resource($oCurl))
|
||||
{
|
||||
\curl_close($oCurl);
|
||||
}
|
||||
|
||||
return $mResult;
|
||||
}
|
||||
|
||||
static public function DetectAndHackFollowLocationUrl(string $sUrl, array &$aOptions, ?\MailSo\Log\Logger $oLogger = null) : string
|
||||
{
|
||||
$sNewUrl = null;
|
||||
$sUrl = isset($aOptions[CURLOPT_URL]) ? $aOptions[CURLOPT_URL] : $sUrl;
|
||||
|
||||
if (isset($aOptions[CURLOPT_FOLLOWLOCATION]) && $aOptions[CURLOPT_FOLLOWLOCATION] && \strlen($sUrl) &&
|
||||
\ini_get('open_basedir') !== '')
|
||||
{
|
||||
$aOptions[CURLOPT_FOLLOWLOCATION] = false;
|
||||
|
||||
$iMaxRedirects = isset($aOptions[CURLOPT_MAXREDIRS]) ? $aOptions[CURLOPT_MAXREDIRS] : 5;
|
||||
$iRedirectLimit = $iMaxRedirects;
|
||||
|
||||
if ($iRedirectLimit > 0)
|
||||
{
|
||||
$sNewUrl = $sUrl;
|
||||
|
||||
$oCurl = \curl_init($sUrl);
|
||||
|
||||
$aAddOptions = array(
|
||||
CURLOPT_URL => $sUrl,
|
||||
CURLOPT_HEADER => true,
|
||||
CURLOPT_NOBODY => true,
|
||||
CURLOPT_FAILONERROR => false,
|
||||
CURLOPT_SSL_VERIFYPEER => false,
|
||||
CURLOPT_FOLLOWLOCATION => false,
|
||||
CURLOPT_FORBID_REUSE => false,
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_TIMEOUT => 5
|
||||
);
|
||||
|
||||
if (isset($aOptions[CURLOPT_HTTPHEADER]) && \is_array($aOptions[CURLOPT_HTTPHEADER]) && \count($aOptions[CURLOPT_HTTPHEADER]))
|
||||
{
|
||||
$aAddOptions[CURLOPT_HTTPHEADER] = $aOptions[CURLOPT_HTTPHEADER];
|
||||
}
|
||||
|
||||
\curl_setopt_array($oCurl, $aAddOptions);
|
||||
|
||||
do
|
||||
{
|
||||
\curl_setopt($oCurl, CURLOPT_URL, $sNewUrl);
|
||||
|
||||
$sHeader = \curl_exec($oCurl);
|
||||
if (\curl_errno($oCurl))
|
||||
{
|
||||
$iCode = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
$iCode = \curl_getinfo($oCurl, CURLINFO_HTTP_CODE);
|
||||
if ($iCode === 301 || $iCode === 302)
|
||||
{
|
||||
$aMatches = array();
|
||||
\preg_match('/Location:(.*?)\n/', $sHeader, $aMatches);
|
||||
$sNewUrl = \trim(\array_pop($aMatches));
|
||||
|
||||
if ($oLogger)
|
||||
{
|
||||
$oLogger->Write('cUrl: Location URL: '.$sNewUrl);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$iCode = 0;
|
||||
}
|
||||
}
|
||||
|
||||
} while ($iCode && --$iRedirectLimit);
|
||||
|
||||
\curl_close($oCurl);
|
||||
if ($iRedirectLimit > 0 && \strlen($sNewUrl))
|
||||
{
|
||||
$aOptions[CURLOPT_URL] = $sNewUrl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null === $sNewUrl ? $sUrl : $sNewUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param resource $rFile
|
||||
*/
|
||||
public function SaveUrlToFile(string $sUrl, $rFile, string $sCustomUserAgent = 'MailSo Http User Agent (v1)', string &$sContentType = '', int &$iCode = 0,
|
||||
?\MailSo\Log\Logger $oLogger = null, int $iTimeout = 10, string $sProxy = '', string $sProxyAuth = '', array $aHttpHeaders = array(), bool $bFollowLocation = true) : bool
|
||||
{
|
||||
if (null === $sCustomUserAgent)
|
||||
{
|
||||
$sCustomUserAgent = 'MailSo Http User Agent (v1)';
|
||||
}
|
||||
|
||||
if (!is_resource($rFile))
|
||||
{
|
||||
if ($oLogger)
|
||||
{
|
||||
$oLogger->Write('cURL: input resource invalid.', \MailSo\Log\Enumerations\Type::WARNING);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$sUrl = \trim($sUrl);
|
||||
if ('//' === substr($sUrl, 0, 2))
|
||||
{
|
||||
$sUrl = 'http:'.$sUrl;
|
||||
}
|
||||
|
||||
$aOptions = array(
|
||||
CURLOPT_URL => $sUrl,
|
||||
CURLOPT_HEADER => false,
|
||||
CURLOPT_FAILONERROR => true,
|
||||
CURLOPT_SSL_VERIFYPEER => false,
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_FOLLOWLOCATION => $bFollowLocation,
|
||||
CURLOPT_MAXREDIRS => 7,
|
||||
CURLOPT_FILE => $rFile,
|
||||
CURLOPT_TIMEOUT => (int) $iTimeout
|
||||
);
|
||||
|
||||
if (\strlen($sCustomUserAgent))
|
||||
{
|
||||
$aOptions[CURLOPT_USERAGENT] = $sCustomUserAgent;
|
||||
}
|
||||
|
||||
if (\strlen($sProxy))
|
||||
{
|
||||
$aOptions[CURLOPT_PROXY] = $sProxy;
|
||||
if (\strlen($sProxyAuth))
|
||||
{
|
||||
$aOptions[CURLOPT_PROXYUSERPWD] = $sProxyAuth;
|
||||
}
|
||||
}
|
||||
|
||||
if (\count($aHttpHeaders))
|
||||
{
|
||||
$aOptions[CURLOPT_HTTPHEADER] = $aHttpHeaders;
|
||||
}
|
||||
|
||||
if ($oLogger)
|
||||
{
|
||||
$oLogger->Write('cUrl: URL: '.$sUrl);
|
||||
// if (isset($aOptions[CURLOPT_HTTPHEADER]) && \is_array($aOptions[CURLOPT_HTTPHEADER]) && \count($aOptions[CURLOPT_HTTPHEADER]))
|
||||
// {
|
||||
// $oLogger->Write('cUrl: Headers: '.\print_r($aOptions[CURLOPT_HTTPHEADER], true));
|
||||
// }
|
||||
}
|
||||
|
||||
static::DetectAndHackFollowLocationUrl($sUrl, $aOptions, $oLogger);
|
||||
|
||||
$oCurl = \curl_init();
|
||||
\curl_setopt_array($oCurl, $aOptions);
|
||||
|
||||
$bResult = \curl_exec($oCurl);
|
||||
|
||||
$iCode = (int) \curl_getinfo($oCurl, CURLINFO_HTTP_CODE);
|
||||
$sContentType = (string) \curl_getinfo($oCurl, CURLINFO_CONTENT_TYPE);
|
||||
|
||||
if ($oLogger)
|
||||
{
|
||||
$oLogger->Write('cUrl: Request result: '.($bResult ? 'true' : 'false').' (Status: '.$iCode.', ContentType: '.$sContentType.')');
|
||||
if (!$bResult || 200 !== $iCode)
|
||||
{
|
||||
$oLogger->Write('cUrl: Error: '.\curl_error($oCurl), \MailSo\Log\Enumerations\Type::WARNING);
|
||||
}
|
||||
}
|
||||
|
||||
if (\is_resource($oCurl))
|
||||
{
|
||||
\curl_close($oCurl);
|
||||
}
|
||||
|
||||
return $bResult;
|
||||
}
|
||||
|
||||
public function GetUrlAsString(string $sUrl, string $sCustomUserAgent = 'MailSo Http User Agent (v1)', string &$sContentType = '', int &$iCode = 0,
|
||||
?\MailSo\Log\Logger $oLogger = null, int $iTimeout = 10, string $sProxy = '', string $sProxyAuth = '', array $aHttpHeaders = array(), bool $bFollowLocation = true) : string
|
||||
{
|
||||
$rMemFile = \MailSo\Base\ResourceRegistry::CreateMemoryResource();
|
||||
if ($this->SaveUrlToFile($sUrl, $rMemFile, $sCustomUserAgent, $sContentType, $iCode, $oLogger, $iTimeout, $sProxy, $sProxyAuth, $aHttpHeaders, $bFollowLocation) && \is_resource($rMemFile))
|
||||
{
|
||||
\rewind($rMemFile);
|
||||
return \stream_get_contents($rMemFile);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function ServerNotModifiedCache(int $iExpireTime, bool $bSetCacheHeader = true, string $sEtag = '') : bool
|
||||
{
|
||||
$bResult = false;
|
||||
|
|
|
|||
|
|
@ -334,7 +334,6 @@ class ServiceActions
|
|||
|
||||
public function ServiceProxyExternal() : string
|
||||
{
|
||||
$bResult = false;
|
||||
$sData = empty($this->aPaths[1]) ? '' : $this->aPaths[1];
|
||||
if (!empty($sData) && $this->Config()->Get('labs', 'use_local_proxy_for_external_images', false))
|
||||
{
|
||||
|
|
@ -350,30 +349,25 @@ class ServiceActions
|
|||
if (\is_array($aData) && !empty($aData['Token']) && !empty($aData['Url']) && $aData['Token'] === Utils::GetConnectionToken())
|
||||
{
|
||||
\header('X-Content-Location: '.$aData['Url']);
|
||||
$iCode = 404;
|
||||
$sContentType = '';
|
||||
$mResult = $this->oHttp->GetUrlAsString($aData['Url'], 'SnappyMail External Proxy', $sContentType, $iCode);
|
||||
|
||||
if (false !== $mResult && 200 === $iCode &&
|
||||
\in_array($sContentType, array('image/png', 'image/jpeg', 'image/jpg', 'image/bmp', 'image/gif')))
|
||||
{
|
||||
$bResult = true;
|
||||
|
||||
$tmp = \tmpfile();
|
||||
$HTTP = \SnappyMail\HTTP\Request::factory();
|
||||
$HTTP->streamBodyTo($tmp);
|
||||
$oResponse = $HTTP->doRequest('GET', $aData['Url']);
|
||||
if ($oResponse && 200 === $oResponse->status
|
||||
&& \str_starts_with($oResponse->getHeader('content-type'), 'image/')
|
||||
) {
|
||||
$this->oActions->cacheByKey($sData);
|
||||
|
||||
\header('Content-Type: '.$sContentType);
|
||||
\header('Cache-Control: public');
|
||||
\header('Expires: '.\gmdate('D, j M Y H:i:s', 2592000 + \time()).' UTC');
|
||||
echo $mResult;
|
||||
\rewind($tmp);
|
||||
\fpassthru($tmp);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$bResult)
|
||||
{
|
||||
\MailSo\Base\Http::StatusHeader(404);
|
||||
}
|
||||
|
||||
\MailSo\Base\Http::StatusHeader(404);
|
||||
return '';
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -215,6 +215,23 @@ class Utils
|
|||
// return @\parse_ini_string(\file_get_contents($sFileName), $bProcessSections) ?: array();
|
||||
}
|
||||
|
||||
public static function inOpenBasedir(string $name) : string
|
||||
{
|
||||
static $open_basedir;
|
||||
if (null === $open_basedir) {
|
||||
$open_basedir = \array_filter(\explode(PATH_SEPARATOR, \ini_get('open_basedir')));
|
||||
}
|
||||
if ($open_basedir) {
|
||||
foreach ($open_basedir as $dir) {
|
||||
if (\str_starts_with($name, $dir)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace control characters, ampersand, spaces and reserved characters (based on Win95 VFAT)
|
||||
* en.wikipedia.org/wiki/Filename#Reserved_characters_and_words
|
||||
|
|
|
|||
|
|
@ -1385,25 +1385,13 @@ class GPG
|
|||
if ($binary && \is_executable($binary)) {
|
||||
return $binary;
|
||||
}
|
||||
$locations = [
|
||||
$locations = \array_filter([
|
||||
'/sw/bin/',
|
||||
'/usr/bin/',
|
||||
'/usr/local/bin/',
|
||||
'/opt/local/bin/',
|
||||
'/run/current-system/sw/bin/'
|
||||
];
|
||||
$open_basedir = \ini_get('open_basedir');
|
||||
if ($open_basedir) {
|
||||
$open_basedir = \explode(PATH_SEPARATOR, $open_basedir);
|
||||
$locations = \array_filter($locations, function($path) use ($open_basedir) {
|
||||
foreach ($open_basedir as $dir) {
|
||||
if (\str_starts_with($path, $open_basedir)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
], '\RainLoop\Utils::inOpenBasedir');
|
||||
foreach ($locations as $location) {
|
||||
if (\is_executable($location . $name)) {
|
||||
return $location . $name;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue