diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..4b6c3357f --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,7 @@ +Thanks for contributing to RainLoop Webmail! + +1. Sign the CLA (individual or corporate) at . +2. Fork the repo, do work in a feature branch. +3. Issue a pull request. + +If you have any questions, open an issue or email [support@rainloop.net](mailto:support@rainloop.net). \ No newline at end of file diff --git a/README.md b/README.md index f45730a1d..96b3c1c2e 100644 --- a/README.md +++ b/README.md @@ -5,9 +5,9 @@ Simple, modern & fast web-based email client. Modest system requirements, decent performance, simple installation and upgrade, no database required - all these make RainLoop Webmail a perfect choice for your email solution. -For more information about the product, check [http://rainloop.net](http://rainloop.net). +For more information about the product, check [rainloop.net](http://www.rainloop.net/). -Information about installing the product, check the [documentation page](http://rainloop.net/docs/installation/). +Information about installing the product, check the [documentation page](http://www.rainloop.net/docs/installation/). ## Attention diff --git a/dev/App/User.js b/dev/App/User.js index 29f08e4dc..5f68b0675 100644 --- a/dev/App/User.js +++ b/dev/App/User.js @@ -1511,7 +1511,7 @@ self.initVerticalLayoutResizer(Enums.ClientSideKeyName.FolderListSize); }); - if (Tinycon) + if (Tinycon && !Settings.settingsGet('Filtered')) { Tinycon.setOptions({ fallback: false diff --git a/dev/Common/HtmlEditor.js b/dev/Common/HtmlEditor.js index 0e64c420a..e607339df 100644 --- a/dev/Common/HtmlEditor.js +++ b/dev/Common/HtmlEditor.js @@ -196,7 +196,7 @@ { this.modeToggle(true); - sHtml = sHtml.replace(/<[pP][^>]*><\/[pP]>/g, ''); + sHtml = sHtml.replace(/]*><\/p>/ig, ''); try { this.editor.setData(sHtml); diff --git a/dev/Common/Utils.js b/dev/Common/Utils.js index 0f0cb58ce..49119012d 100644 --- a/dev/Common/Utils.js +++ b/dev/Common/Utils.js @@ -867,13 +867,16 @@ sText = sHtml .replace(/\u0002([\s\S]*)\u0002/gm, '\u200C$1\u200C') - .replace(/<[pP][^>]*><\/[pP]>/g, '') + .replace(/]*><\/p>/gi, '') .replace(/]*>([\s\S\r\n]*)<\/pre>/gmi, convertPre) .replace(/[\s]+/gm, ' ') .replace(/((?:href|data)\s?=\s?)("[^"]+?"|'[^']+?')/gmi, fixAttibuteValue) .replace(/]*>/gmi, '\n') .replace(/<\/h[\d]>/gi, '\n') .replace(/<\/p>/gi, '\n\n') + .replace(/]*>/gmi, '\n') + .replace(/<\/ul>/gi, '\n') + .replace(/]*>/gmi, ' * ') .replace(/<\/li>/gi, '\n') .replace(/<\/td>/gi, '\n') .replace(/<\/tr>/gi, '\n') diff --git a/dev/Screen/User/MailBox.js b/dev/Screen/User/MailBox.js index 8f79ed316..07a6da8c8 100644 --- a/dev/Screen/User/MailBox.js +++ b/dev/Screen/User/MailBox.js @@ -55,6 +55,11 @@ nFoldersInboxUnreadCount = FolderStore.foldersInboxUnreadCount() ; + if (Settings.settingsGet('Filtered')) + { + nFoldersInboxUnreadCount = 0; + } + require('App/User').setWindowTitle(('' === sEmail ? '' : '' + (0 < nFoldersInboxUnreadCount ? '(' + nFoldersInboxUnreadCount + ') ' : ' ') + sEmail + ' - ') + Translator.i18n('TITLES/MAILBOX')); @@ -82,7 +87,6 @@ * @param {string} sFolderHash * @param {number} iPage * @param {string} sSearch - * @param {boolean=} bPreview = false */ MailBoxUserScreen.prototype.onRoute = function (sFolderHash, iPage, sSearch) { diff --git a/dev/Settings/Admin/Social.js b/dev/Settings/Admin/Social.js index ca2d2ad84..8a6890cc5 100644 --- a/dev/Settings/Admin/Social.js +++ b/dev/Settings/Admin/Social.js @@ -20,6 +20,7 @@ this.googleEnable = SocialStore.google.enabled; this.googleEnableAuth = SocialStore.google.capa.auth; + this.googleEnableAuthFast = SocialStore.google.capa.authFast; this.googleEnableDrive = SocialStore.google.capa.drive; this.googleEnablePreview = SocialStore.google.capa.preview; diff --git a/dev/Settings/User/Social.js b/dev/Settings/User/Social.js index b5521a546..82bbe5d2f 100644 --- a/dev/Settings/User/Social.js +++ b/dev/Settings/User/Social.js @@ -15,6 +15,7 @@ this.googleEnable = SocialStore.google.enabled; this.googleEnableAuth = SocialStore.google.capa.auth; + this.googleEnableAuthFast = SocialStore.google.capa.authFast; this.googleEnableDrive = SocialStore.google.capa.drive; this.googleEnablePreview = SocialStore.google.capa.preview; diff --git a/dev/Stores/Social.js b/dev/Stores/Social.js index 0e870ce5b..ffeb0f6f9 100644 --- a/dev/Stores/Social.js +++ b/dev/Stores/Social.js @@ -33,6 +33,7 @@ this.google.capa = {}; this.google.capa.auth = ko.observable(false); + this.google.capa.authFast = ko.observable(false); this.google.capa.drive = ko.observable(false); this.google.capa.preview = ko.observable(false); @@ -88,6 +89,7 @@ this.google.apiKey(Settings.settingsGet('GoogleApiKey')); this.google.capa.auth(!!Settings.settingsGet('AllowGoogleSocialAuth')); + this.google.capa.authFast(!!Settings.settingsGet('AllowGoogleSocialAuthFast')); this.google.capa.drive(!!Settings.settingsGet('AllowGoogleSocialDrive')); this.google.capa.preview(!!Settings.settingsGet('AllowGoogleSocialPreview')); diff --git a/dev/Styles/Login.less b/dev/Styles/Login.less index 504b1e4d2..8da8dab3d 100644 --- a/dev/Styles/Login.less +++ b/dev/Styles/Login.less @@ -25,6 +25,9 @@ max-width: 450px; } + .wrapper-parent { + } + .loginForm { background-color: #efefef; text-align: left; @@ -34,6 +37,18 @@ width: 303px; } + .loginWelcomeForm { + display: none; + } + + .welcome-on { + display: block; + } + + .welcome-off { + display: none; + } + /* .control-label { font-size: 16px; line-height: 30px; diff --git a/dev/Styles/MessageView.less b/dev/Styles/MessageView.less index 24d392852..25f6dc3cc 100644 --- a/dev/Styles/MessageView.less +++ b/dev/Styles/MessageView.less @@ -331,6 +331,10 @@ html.rl-no-preview-pane { } } + .bodySubHeader { + z-index: 2; + } + .bodyText { color: #000; @@ -338,9 +342,11 @@ html.rl-no-preview-pane { .b-text-part { + height: 100%; + div[data-x-div-type=html] { height: 100%; - div[data-x-div-type=html] { + div[data-x-div-type=body] { height: 100%; } } @@ -357,14 +363,10 @@ html.rl-no-preview-pane { } } - table { - border-collapse: separate; - } - blockquote { border-left: 2px solid #000; - margin: 0; padding: 0px 10px; + margin: 0; } .rl-bq-switcher.hidden-bq { @@ -372,8 +374,10 @@ html.rl-no-preview-pane { } &.html { + div[data-x-div-type=body] { - margin: 15px; + padding: 15px; + // margin: 15px; } pre { diff --git a/dev/Styles/Ui.less b/dev/Styles/Ui.less index 1ac9744da..09afa1136 100644 --- a/dev/Styles/Ui.less +++ b/dev/Styles/Ui.less @@ -33,6 +33,10 @@ min-height: 300px; } +.g-ui-100-proc-height { + height: 100%; +} + .g-ui-absolute-reset { position: absolute; top: 0; diff --git a/dev/Styles/_FontasticToBoot.less b/dev/Styles/_FontasticToBoot.less index 70b25dae9..52d8db353 100644 --- a/dev/Styles/_FontasticToBoot.less +++ b/dev/Styles/_FontasticToBoot.less @@ -17,12 +17,19 @@ width: 17px; } +.iconsize50 { + line-height: 50px; + font-size: 50px; + width: 50px; + height: 50px; +} + .iconsize24 { line-height: 24px; font-size: 24px; } -.iconsize24 { +.iconsize20 { line-height: 20px; font-size: 20px; } diff --git a/dev/View/User/Login.js b/dev/View/User/Login.js index f3359f4b5..db5dc2e79 100644 --- a/dev/View/User/Login.js +++ b/dev/View/User/Login.js @@ -36,6 +36,8 @@ { AbstractView.call(this, 'Center', 'Login'); + this.welcome = ko.observable(!!Settings.settingsGet('UseLoginWelcomePage')); + this.email = ko.observable(''); this.password = ko.observable(''); this.signMe = ko.observable(false); @@ -294,11 +296,12 @@ }); this.googleLoginEnabled = ko.observable(false); + this.googleFastLoginEnabled = ko.observable(false); this.googleCommand = Utils.createCommand(this, function () { window.open(Links.socialGoogle(), 'Google', - 'left=200,top=100,width=650,height=450,menubar=no,status=no,resizable=yes,scrollbars=yes'); + 'left=200,top=100,width=650,height=500,menubar=no,status=no,resizable=yes,scrollbars=yes'); return true; @@ -306,15 +309,15 @@ return !this.submitRequest() && this.googleLoginEnabled(); }); - this.googleXAuthCommand = Utils.createCommand(this, function () { + this.googleFastCommand = Utils.createCommand(this, function () { window.open(Links.socialGoogle(true), 'Google', - 'left=200,top=100,width=650,height=450,menubar=no,status=no,resizable=yes,scrollbars=yes'); + 'left=200,top=100,width=650,height=500,menubar=no,status=no,resizable=yes,scrollbars=yes'); return true; }, function () { - return !this.submitRequest() && this.googleLoginEnabled(); + return !this.submitRequest() && this.googleFastLoginEnabled(); }); this.twitterLoginEnabled = ko.observable(false); @@ -347,6 +350,11 @@ kn.extendAsViewModel(['View/User/Login', 'View/App/Login', 'LoginViewModel'], LoginUserView); _.extend(LoginUserView.prototype, AbstractView.prototype); + LoginUserView.prototype.displayMainForm = function () + { + this.welcome(false); + }; + LoginUserView.prototype.onShow = function () { kn.routeOff(); @@ -404,6 +412,8 @@ this.twitterLoginEnabled(!!Settings.settingsGet('AllowTwitterSocial')); this.googleLoginEnabled(!!Settings.settingsGet('AllowGoogleSocial') && !!Settings.settingsGet('AllowGoogleSocialAuth')); + this.googleFastLoginEnabled(!!Settings.settingsGet('AllowGoogleSocial') && + !!Settings.settingsGet('AllowGoogleSocialAuthFast')); switch (sSignMe) { @@ -433,7 +443,7 @@ this.email(AppStore.devEmail); this.password(AppStore.devPassword); - if (this.googleLoginEnabled()) + if (this.googleLoginEnabled() || this.googleFastLoginEnabled()) { window['rl_' + sJsHash + '_google_login_service'] = fSocial; } diff --git a/dev/View/User/MailBox/MessageView.js b/dev/View/User/MailBox/MessageView.js index f7a3b1aa3..11a4017bc 100644 --- a/dev/View/User/MailBox/MessageView.js +++ b/dev/View/User/MailBox/MessageView.js @@ -24,8 +24,6 @@ Cache = require('Common/Cache'), - EmailModel = require('Model/Email'), - SocialStore = require('Stores/Social'), AppStore = require('Stores/User/App'), SettingsStore = require('Stores/User/Settings'), @@ -66,6 +64,8 @@ this.oHeaderDom = null; this.oMessageScrollerDom = null; + this.bodyBackgroundColor = ko.observable(''); + this.pswp = null; this.allowComposer = !!Settings.capa(Enums.Capa.Composer); @@ -353,6 +353,10 @@ }, this); + this.messageActiveDom.subscribe(function (oDom) { + this.bodyBackgroundColor(oDom ? this.detectDomBackgroundColor(oDom): ''); + }, this); + this.message.subscribe(function (oMessage) { this.messageActiveDom(null); @@ -466,6 +470,59 @@ kn.extendAsViewModel(['View/User/MailBox/MessageView', 'View/App/MailBox/MessageView', 'MailBoxMessageViewViewModel'], MessageViewMailBoxUserView); _.extend(MessageViewMailBoxUserView.prototype, AbstractView.prototype); + MessageViewMailBoxUserView.prototype.detectDomBackgroundColor = function (oDom) + { + var + iLimit = 5, + sResult = '', + aC = null, + fFindDom = function (oDom) { + var aC = oDom ? oDom.children() : null; + return (aC && 1 === aC.length && aC.is('table,div,center')) ? aC : null; + }, + fFindColor = function (oDom) { + var sResult = ''; + if (oDom) + { + sResult = oDom.css('background-color') || ''; + if (!oDom.is('table')) + { + sResult = 'rgba(0, 0, 0, 0)' === sResult || 'transparent' === sResult ? '' : sResult; + } + } + + return sResult; + } + ; + + if (oDom && 1 === oDom.length) + { + aC = oDom; + while ('' === sResult) + { + iLimit--; + if (0 >= iLimit) + { + break; + } + + aC = fFindDom(aC); + if (aC) + { + sResult = fFindColor(aC); + } + else + { + break; + } + } + + sResult = 'rgba(0, 0, 0, 0)' === sResult || 'transparent' === sResult ? '' : sResult; + } + + return sResult; + }; + MessageViewMailBoxUserView.prototype.isPgpActionVisible = function () { return Enums.SignedVerifyStatus.Success !== this.viewPgpSignedVerifyStatus(); @@ -552,27 +609,28 @@ * @todo * @param {string} sEmail */ - MessageViewMailBoxUserView.prototype.displayMailToPopup = function (sMailToUrl) - { - sMailToUrl = sMailToUrl.replace(/\?.+$/, ''); - - var - sResult = '', - aTo = [], - fParseEmailLine = function (sLine) { - return sLine ? _.compact(_.map([window.decodeURIComponent(sLine)], function (sItem) { - var oEmailModel = new EmailModel(); - oEmailModel.mailsoParse(sItem); - return '' !== oEmailModel.email ? oEmailModel : null; - })) : null; - } - ; - - aTo = fParseEmailLine(sMailToUrl); - sResult = aTo && aTo[0] ? aTo[0].email : ''; - - window.console.log(sResult); - }; +// MessageViewMailBoxUserView.prototype.displayMailToPopup = function (sMailToUrl) +// { +// sMailToUrl = sMailToUrl.replace(/\?.+$/, ''); +// +// var +// sResult = '', +// aTo = [], +// EmailModel = require('Model/Email'), +// fParseEmailLine = function (sLine) { +// return sLine ? _.compact(_.map([window.decodeURIComponent(sLine)], function (sItem) { +// var oEmailModel = new EmailModel(); +// oEmailModel.mailsoParse(sItem); +// return '' !== oEmailModel.email ? oEmailModel : null; +// })) : null; +// } +// ; +// +// aTo = fParseEmailLine(sMailToUrl); +// sResult = aTo && aTo[0] ? aTo[0].email : ''; +// +// return sResult; +// }; MessageViewMailBoxUserView.prototype.onBuild = function (oDom) { @@ -592,6 +650,7 @@ } }, this); + this.showAttachmnetControls.subscribe(fCheckHeaderHeight); this.fullScreenMode.subscribe(fCheckHeaderHeight); this.showFullInfo.subscribe(fCheckHeaderHeight); this.message.subscribe(fCheckHeaderHeight); @@ -806,18 +865,6 @@ this.initShortcuts(); }; - MessageViewMailBoxUserView.prototype.selectSelectedThreadMessage = function () - { - var self = this; - _.delay(function () { - var oLast = self.oDom ? $('.thread-list .e-item.thread-list-message.real-msg.selected a.e-link', self.oDom) : null; - if (oLast && oLast[0]) - { - oLast.focus(); - } - }, 30); - }; - /** * @return {boolean} */ @@ -1196,6 +1243,8 @@ { oMessage.showExternalImages(true); } + + this.checkHeaderHeight(); }; /** @@ -1217,6 +1266,8 @@ { oMessage.verifyPgpSignedClearMessage(); } + + this.checkHeaderHeight(); }; /** @@ -1248,6 +1299,8 @@ require('App/User').reloadFlagsCurrentMessageListAndMessageFromCache(); } + + this.checkHeaderHeight(); }; module.exports = MessageViewMailBoxUserView; diff --git a/package.json b/package.json index 0847967c6..cb36f1de7 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "RainLoop", "title": "RainLoop Webmail", "version": "1.9.1", - "release": "333", + "release": "334", "description": "Simple, modern & fast web-based email client", "homepage": "http://rainloop.net", "main": "gulpfile.js", diff --git a/rainloop/v/0.0.0/app/handle.php b/rainloop/v/0.0.0/app/handle.php index 1de59f78c..464a43e3d 100644 --- a/rainloop/v/0.0.0/app/handle.php +++ b/rainloop/v/0.0.0/app/handle.php @@ -59,7 +59,7 @@ if (!\defined('RAINLOOP_APP_LIBRARIES_PATH')) if (\class_exists('RainLoop\Api')) { - if (!\class_exists('MailSo\Version')) + if (!\class_exists('MailSo\Version', false)) { include APP_VERSION_ROOT_PATH.'app/libraries/MailSo/MailSo.php'; } diff --git a/rainloop/v/0.0.0/app/libraries/MailSo/Base/HtmlUtils.php b/rainloop/v/0.0.0/app/libraries/MailSo/Base/HtmlUtils.php index 7adb7fd1f..96f9ac02d 100644 --- a/rainloop/v/0.0.0/app/libraries/MailSo/Base/HtmlUtils.php +++ b/rainloop/v/0.0.0/app/libraries/MailSo/Base/HtmlUtils.php @@ -35,23 +35,32 @@ class HtmlUtils */ public static function GetDomFromText($sText, $sHtmlAttrs = '', $sBodyAttrs = '') { - static $bOnce = true; - if ($bOnce) + $bState = true; + if (\MailSo\Base\Utils::FunctionExistsAndEnabled('libxml_use_internal_errors')) { - $bOnce = false; - if (\MailSo\Base\Utils::FunctionExistsAndEnabled('libxml_use_internal_errors')) - { - @\libxml_use_internal_errors(true); - } + $bState = \libxml_use_internal_errors(true); } $oDom = new \DOMDocument('1.0', 'utf-8'); $oDom->encoding = 'UTF-8'; + $oDom->strictErrorChecking = false; $oDom->formatOutput = false; @$oDom->loadHTML('<'.'?xml version="1.0" encoding="utf-8"?'.'>'. ''.$sText.''); + @$oDom->normalizeDocument(); + + if (\MailSo\Base\Utils::FunctionExistsAndEnabled('libxml_use_internal_errors')) + { + @\libxml_clear_errors(); + } + + if (\MailSo\Base\Utils::FunctionExistsAndEnabled('libxml_use_internal_errors')) + { + \libxml_use_internal_errors($bState); + } + return $oDom; } @@ -65,13 +74,13 @@ class HtmlUtils public static function ClearBodyAndHtmlTag($sHtml, &$sHtmlAttrs = '', &$sBodyAttrs = '') { $aMatch = array(); - if (preg_match('/]+)>/im', $sHtml, $aMatch) && !empty($aMatch[1])) + if (\preg_match('/]+)>/im', $sHtml, $aMatch) && !empty($aMatch[1])) { $sHtmlAttrs = $aMatch[1]; } $aMatch = array(); - if (preg_match('/]+)>/im', $sHtml, $aMatch) && !empty($aMatch[1])) + if (\preg_match('/]+)>/im', $sHtml, $aMatch) && !empty($aMatch[1])) { $sBodyAttrs = $aMatch[1]; } @@ -81,6 +90,29 @@ class HtmlUtils $sHtml = \preg_replace('/]*)>/im', '', $sHtml); $sHtml = \preg_replace('/<\/html>/im', '', $sHtml); + $sHtmlAttrs = \preg_replace('/xmlns:[a-z]="[^"]*"/im', '', $sHtmlAttrs); + $sHtmlAttrs = \preg_replace('/xmlns="[^"]*"/im', '', $sHtmlAttrs); + $sBodyAttrs = \preg_replace('/xmlns:[a-z]="[^"]*"/im', '', $sBodyAttrs); + + return $sHtml; + } + + /** + * @param string $sHtml + * @param bool $bClearEmpty = true + * + * @return string + */ + public static function FixSchemas($sHtml, $bClearEmpty = true) + { + if ($bClearEmpty) + { + $sHtml = \str_replace('', '', $sHtml); + } + + $sHtml = \str_replace('', '', $sHtml); + $sHtml = \str_replace('', '', $sHtml); + return $sHtml; } @@ -104,6 +136,7 @@ class HtmlUtils } $aToRemove = array( + '/]*><\/p>/i', '/]*>/msi', '/<\?xml [^>]*\?>/msi' ); @@ -262,7 +295,7 @@ class HtmlUtils // $mResult = false; // $oCss = null; // -// if (!\class_exists('\\Sabberworm\\CSS\\Parser')) +// if (!\class_exists('Sabberworm\CSS\Parser')) // { // return $mResult; // } @@ -637,15 +670,17 @@ class HtmlUtils * @param bool $bDoNotReplaceExternalUrl = false * @param bool $bFindLinksInHtml = false * @param callback|null $fAdditionalExternalFilter = null + * @param callback|null $fAdditionalDomReader = null * * @return string */ public static function ClearHtml($sHtml, &$bHasExternals = false, &$aFoundCIDs = array(), $aContentLocationUrls = array(), &$aFoundedContentLocationUrls = array(), - $bDoNotReplaceExternalUrl = false, $bFindLinksInHtml = false, $fAdditionalExternalFilter = null) + $bDoNotReplaceExternalUrl = false, $bFindLinksInHtml = false, + $fAdditionalExternalFilter = null, $fAdditionalDomReader = false, + $bTryToDetectHiddenImages = false) { $sResult = ''; -// $aBodyStyles = array(); $sHtml = null === $sHtml ? '' : (string) $sHtml; $sHtml = \trim($sHtml); @@ -659,8 +694,15 @@ class HtmlUtils $fAdditionalExternalFilter = null; } + if ($fAdditionalDomReader && !\is_callable($fAdditionalDomReader)) + { + $fAdditionalDomReader = null; + } + $bHasExternals = false; + $sHtml = \MailSo\Base\HtmlUtils::FixSchemas($sHtml); + $sHtml = \MailSo\Base\HtmlUtils::ClearTags($sHtml, false); $sHtml = \MailSo\Base\HtmlUtils::ClearOn($sHtml); @@ -673,11 +715,37 @@ class HtmlUtils if ($oDom) { + if ($fAdditionalDomReader) + { + $oResDom = \call_user_func($fAdditionalDomReader, $oDom); + if ($oResDom) + { + $oDom = $oResDom; + } + + unset($oResDom); + } + if ($bFindLinksInHtml) { \MailSo\Base\HtmlUtils::FindLinksInDOM($oDom); } + $oXpath = new \DOMXpath($oDom); + $oComments = $oXpath->query('//comment()'); + if ($oComments) + { + foreach ($oComments as $oComment) + { + if (isset($oComment->parentNode)) + { + @$oComment->parentNode->removeChild($oComment); + } + } + } + + unset($oXpath, $oComments); + $aNodes = $oDom->getElementsByTagName('*'); foreach ($aNodes as /* @var $oElement \DOMElement */ $oElement) { @@ -685,19 +753,10 @@ class HtmlUtils { $sTagNameLower = \strtolower($oElement->tagName); - if (\in_array($sTagNameLower, array('svg', 'head', 'link', + if ('' !== $sTagNameLower && \in_array($sTagNameLower, array('svg', 'head', 'link', 'base', 'meta', 'title', 'style', 'x-script', 'script', 'bgsound', 'keygen', 'source', 'object', 'embed', 'applet', 'mocha', 'iframe', 'frame', 'frameset', 'video', 'audio'))) { -// if ('style' === $sTagNameLower) -// { -// $sV = (string) $oElement->textContent; -// if (!empty($sV)) -// { -// $aBodyStyles[] = $sV; -// } -// } - if (isset($oElement->parentNode)) { @$oElement->parentNode->removeChild($oElement); @@ -706,6 +765,7 @@ class HtmlUtils } } + $sLinkColor = ''; $aNodes = $oDom->getElementsByTagName('*'); foreach ($aNodes as /* @var $oElement \DOMElement */ $oElement) { @@ -715,6 +775,7 @@ class HtmlUtils if ('body' === $sTagNameLower) { $aAttrs = array( + 'link' => '', 'text' => '', 'topmargin' => '', 'leftmargin' => '', @@ -746,6 +807,13 @@ class HtmlUtils switch ($sIndex) { + case 'link': + $sLinkColor = \trim($aItem[1]); + if (!\preg_match('/^#[abcdef0-9]{3,6}$/i', $sLinkColor)) + { + $sLinkColor = ''; + } + break; case 'text': $aStyles[] = 'color: '.$aItem[1]; break; @@ -767,7 +835,7 @@ class HtmlUtils if (0 < \count($aStyles)) { - $sStyles = $oElement->hasAttribute('style') ? $oElement->getAttribute('style') : ''; + $sStyles = $oElement->hasAttribute('style') ? \trim(\trim(\trim($oElement->getAttribute('style')), ';')) : ''; $oElement->setAttribute('style', (empty($sStyles) ? '' : $sStyles.'; ').\implode('; ', $aStyles)); } } @@ -777,6 +845,15 @@ class HtmlUtils $oElement->setAttribute('src', 'javascript:false'); } + if ('a' === $sTagNameLower && !empty($sLinkColor)) + { + $sStyles = $oElement->hasAttribute('style') + ? \trim(\trim(\trim($oElement->getAttribute('style')), ';')) : ''; + + $oElement->setAttribute('style', + 'color: '.$sLinkColor.\trim((empty($sStyles) ? '' : '; '.$sStyles))); + } + if (\in_array($sTagNameLower, array('a', 'form', 'area'))) { $oElement->setAttribute('target', '_blank'); @@ -787,15 +864,10 @@ class HtmlUtils $oElement->setAttribute('tabindex', '-1'); } -// if ('blockquote' === $sTagNameLower) -// { -// $oElement->removeAttribute('style'); -// } - foreach (array( 'id', 'class', 'contenteditable', 'designmode', 'formaction', - 'data-bind', 'xmlns', 'srcset' + 'data-bind', 'data-reactid', 'xmlns', 'srcset' ) as $sAttr) { @$oElement->removeAttribute($sAttr); @@ -826,6 +898,35 @@ class HtmlUtils } } + if ($bTryToDetectHiddenImages && 'img' === $sTagNameLower) + { + $sAlt = $oElement->hasAttribute('alt') + ? \trim($oElement->getAttribute('alt')) : ''; + + $sH = $oElement->hasAttribute('height') + ? \trim($oElement->getAttribute('height')) : ''; + + $sW = $oElement->hasAttribute('width') + ? \trim($oElement->getAttribute('width')) : ''; + + $sStyles = $oElement->hasAttribute('style') + ? \trim(\trim(\trim($oElement->getAttribute('style')), ';')) : ''; + + if ($oElement->hasAttribute('src')) + { + $aValues = array('4', '3', '2', '1', '0', '4px', '3px', '2px', '1px', '0px'); + if (('' === $sAlt && \in_array($sH, $aValues) && \in_array($sW, $aValues)) || + \preg_match('/(display:none|visibility:hidden)/i', \preg_replace('/[\s]+/', '', $sStyles))) + { + if (isset($oElement->parentNode)) + { + @$oElement->parentNode->removeChild($oElement); + continue; + } + } + } + } + if ($oElement->hasAttribute('src')) { $sSrc = \trim($oElement->getAttribute('src')); @@ -884,7 +985,7 @@ class HtmlUtils { $aStyles = array(); $sStyles = $oElement->hasAttribute('style') - ? $oElement->getAttribute('style') : ''; + ? \trim(\trim(\trim($oElement->getAttribute('style')), ';')) : ''; if (!empty($sBackground)) { @@ -922,6 +1023,7 @@ class HtmlUtils $sResult = '
'.$sResult.'
'; $sResult = \str_replace(\MailSo\Base\HtmlUtils::$KOS, ':', $sResult); + $sResult = \preg_replace('/[\s]+/u', ' ', $sResult); return \trim($sResult); } diff --git a/rainloop/v/0.0.0/app/libraries/MailSo/Config.php b/rainloop/v/0.0.0/app/libraries/MailSo/Config.php index b40fdf518..6975e6432 100644 --- a/rainloop/v/0.0.0/app/libraries/MailSo/Config.php +++ b/rainloop/v/0.0.0/app/libraries/MailSo/Config.php @@ -66,6 +66,11 @@ class Config */ public static $PreferStartTlsIfAutoDetect = true; + /** + * @var string + */ + public static $BoundaryPrefix = '_Part_'; + /** * @var \MailSo\Log\Logger|null */ diff --git a/rainloop/v/0.0.0/app/libraries/MailSo/Mail/MailClient.php b/rainloop/v/0.0.0/app/libraries/MailSo/Mail/MailClient.php index e290b0a83..ffd9cb0eb 100644 --- a/rainloop/v/0.0.0/app/libraries/MailSo/Mail/MailClient.php +++ b/rainloop/v/0.0.0/app/libraries/MailSo/Mail/MailClient.php @@ -756,7 +756,8 @@ class MailClient { $iUnseenCount = 0; // unneccessery return \md5('FolderHash/'.$sFolder.'-'.$iCount.'-'.$iUnseenCount.'-'.$sUidNext.'-'. - $sHighestModSeq.'-'.$this->GenerateImapClientHash() + $sHighestModSeq.'-'.$this->GenerateImapClientHash().'-'. + \MailSo\Config::$MessageListPermanentFilter ); } @@ -1882,6 +1883,7 @@ class MailClient $oMessageCollection->Limit = $iLimit; $oMessageCollection->Search = $sSearch; $oMessageCollection->ThreadUid = $sThreadUid; + $oMessageCollection->Filtered = '' !== \MailSo\Config::$MessageListPermanentFilter; $aUids = array(); $mAllSortedUids = null; @@ -1988,7 +1990,9 @@ class MailClient if (0 < \strlen($sSearch) && \is_array($aUids)) { - $aSearchedUids = $this->GetUids($oCacher, $sSearch, $sFilter, $oMessageCollection->FolderName, $oMessageCollection->FolderHash); + $aSearchedUids = $this->GetUids($oCacher, $sSearch, $sFilter, + $oMessageCollection->FolderName, $oMessageCollection->FolderHash); + if (\is_array($aSearchedUids) && 0 < \count($aSearchedUids)) { $aFlippedSearchedUids = \array_flip($aSearchedUids); diff --git a/rainloop/v/0.0.0/app/libraries/MailSo/Mail/MessageCollection.php b/rainloop/v/0.0.0/app/libraries/MailSo/Mail/MessageCollection.php index ec176924a..427a18cac 100644 --- a/rainloop/v/0.0.0/app/libraries/MailSo/Mail/MessageCollection.php +++ b/rainloop/v/0.0.0/app/libraries/MailSo/Mail/MessageCollection.php @@ -73,9 +73,9 @@ class MessageCollection extends \MailSo\Base\Collection public $NewMessages; /** - * @var array + * @var bool */ - public $LastCollapsedThreadUids; + public $Filtered; /** * @access protected @@ -116,7 +116,7 @@ class MessageCollection extends \MailSo\Base\Collection $this->ThreadUid = ''; $this->NewMessages = array(); - $this->LastCollapsedThreadUids = array(); + $this->Filtered = false; return $this; } diff --git a/rainloop/v/0.0.0/app/libraries/MailSo/Mime/Message.php b/rainloop/v/0.0.0/app/libraries/MailSo/Mime/Message.php index 7bfaec046..106b3e6ae 100644 --- a/rainloop/v/0.0.0/app/libraries/MailSo/Mime/Message.php +++ b/rainloop/v/0.0.0/app/libraries/MailSo/Mime/Message.php @@ -511,7 +511,8 @@ class Message */ private function generateNewBoundary() { - return '----=_Part_'.rand(100, 999).'_'.rand(100000000, 999999999).'.'.time(); + return '--='.\MailSo\Config::$BoundaryPrefix. + \rand(100, 999).'_'.rand(100000000, 999999999).'.'.\time(); } /** diff --git a/rainloop/v/0.0.0/app/libraries/RainLoop/Actions.php b/rainloop/v/0.0.0/app/libraries/RainLoop/Actions.php index d60264ccc..ba4128cac 100644 --- a/rainloop/v/0.0.0/app/libraries/RainLoop/Actions.php +++ b/rainloop/v/0.0.0/app/libraries/RainLoop/Actions.php @@ -411,16 +411,17 @@ class Actions /** * @param string $sLine * @param \RainLoop\Model\Account $oAccount = null + * @param bool $bUrlEncode = false * * @return string */ - private function compileLogParams($sLine, $oAccount = null) + private function compileLogParams($sLine, $oAccount = null, $bUrlEncode = false) { if (false !== \strpos($sLine, '{date:')) { $iTimeOffset = (int) $this->Config()->Get('logs', 'time_offset', 0); - $sLine = \preg_replace_callback('/\{date:([^}]+)\}/', function ($aMatch) use ($iTimeOffset) { - return \MailSo\Log\Logger::DateHelper($aMatch[1], $iTimeOffset); + $sLine = \preg_replace_callback('/\{date:([^}]+)\}/', function ($aMatch) use ($iTimeOffset, $bUrlEncode) { + return \RainLoop\Utils::UrlEncode(\MailSo\Log\Logger::DateHelper($aMatch[1], $iTimeOffset), $bUrlEncode); }, $sLine); $sLine = \preg_replace('/\{date:([^}]*)\}/', 'date', $sLine); @@ -436,37 +437,38 @@ class Actions if ($oAccount) { - $sLine = \str_replace('{imap:login}', $oAccount->IncLogin(), $sLine); - $sLine = \str_replace('{imap:host}', $oAccount->DomainIncHost(), $sLine); - $sLine = \str_replace('{imap:port}', $oAccount->DomainIncPort(), $sLine); + $sLine = \str_replace('{imap:login}', \RainLoop\Utils::UrlEncode($oAccount->IncLogin(), $bUrlEncode), $sLine); + $sLine = \str_replace('{imap:host}', \RainLoop\Utils::UrlEncode($oAccount->DomainIncHost(), $bUrlEncode), $sLine); + $sLine = \str_replace('{imap:port}', \RainLoop\Utils::UrlEncode($oAccount->DomainIncPort(), $bUrlEncode), $sLine); - $sLine = \str_replace('{smtp:login}', $oAccount->OutLogin(), $sLine); - $sLine = \str_replace('{smtp:host}', $oAccount->DomainOutHost(), $sLine); - $sLine = \str_replace('{smtp:port}', $oAccount->DomainOutPort(), $sLine); + $sLine = \str_replace('{smtp:login}', \RainLoop\Utils::UrlEncode($oAccount->OutLogin(), $bUrlEncode), $sLine); + $sLine = \str_replace('{smtp:host}', \RainLoop\Utils::UrlEncode($oAccount->DomainOutHost(), $bUrlEncode), $sLine); + $sLine = \str_replace('{smtp:port}', \RainLoop\Utils::UrlEncode($oAccount->DomainOutPort(), $bUrlEncode), $sLine); } $sLine = \preg_replace('/\{imap:([^}]*)\}/i', 'imap', $sLine); - $sLine = \preg_replace('/\{smtp:([^}]*)\}/i', 'imap', $sLine); + $sLine = \preg_replace('/\{smtp:([^}]*)\}/i', 'smtp', $sLine); } if (false !== \strpos($sLine, '{request:')) { if (false !== \strpos($sLine, '{request:ip}')) { - $sLine = \str_replace('{request:ip}', $this->Http()->GetClientIp( - $this->Config()->Get('labs', 'http_client_ip_check_proxy', false)), $sLine); + $sLine = \str_replace('{request:ip}', \RainLoop\Utils::UrlEncode($this->Http()->GetClientIp( + $this->Config()->Get('labs', 'http_client_ip_check_proxy', false)), $bUrlEncode), $sLine); } if (false !== \strpos($sLine, '{request:domain}')) { $sLine = \str_replace('{request:domain}', - $this->Http()->GetHost(false, true, true), $sLine); + \RainLoop\Utils::UrlEncode($this->Http()->GetHost(false, true, true), $bUrlEncode), $sLine); } if (false !== \strpos($sLine, '{request:domain-clear}')) { $sLine = \str_replace('{request:domain-clear}', - \MailSo\Base\Utils::GetClearDomainName($this->Http()->GetHost(false, true, true)), $sLine); + \RainLoop\Utils::UrlEncode( + \MailSo\Base\Utils::GetClearDomainName($this->Http()->GetHost(false, true, true)), $bUrlEncode), $sLine); } $sLine = \preg_replace('/\{request:([^}]*)\}/i', 'request', $sLine); @@ -477,15 +479,16 @@ class Actions if (false !== \strpos($sLine, '{user:uid}')) { $sLine = \str_replace('{user:uid}', - \base_convert(\sprintf('%u', \crc32(\md5(\RainLoop\Utils::GetConnectionToken()))), 10, 32), + \RainLoop\Utils::UrlEncode(\base_convert(\sprintf('%u', + \crc32(\md5(\RainLoop\Utils::GetConnectionToken()))), 10, 32), $bUrlEncode), $sLine ); } if (false !== \strpos($sLine, '{user:ip}')) { - $sLine = \str_replace('{user:ip}', $this->Http()->GetClientIp( - $this->Config()->Get('labs', 'http_client_ip_check_proxy', false)), $sLine); + $sLine = \str_replace('{user:ip}', \RainLoop\Utils::UrlEncode($this->Http()->GetClientIp( + $this->Config()->Get('labs', 'http_client_ip_check_proxy', false)), $bUrlEncode), $sLine); } if (\preg_match('/\{user:(email|login|domain)\}/i', $sLine)) @@ -499,13 +502,15 @@ class Actions if ($oAccount) { $sEmail = $oAccount->Email(); - $sLine = \str_replace('{user:email}', $sEmail, $sLine); - $sLine = \str_replace('{user:login}', - \MailSo\Base\Utils::GetAccountNameFromEmail($sEmail), $sLine); - $sLine = \str_replace('{user:domain}', - \MailSo\Base\Utils::GetDomainFromEmail($sEmail), $sLine); - $sLine = \str_replace('{user:domain-clear}', - \MailSo\Base\Utils::GetClearDomainName(\MailSo\Base\Utils::GetDomainFromEmail($sEmail)), $sLine); + + $sLine = \str_replace('{user:email}', \RainLoop\Utils::UrlEncode($sEmail, $bUrlEncode), $sLine); + $sLine = \str_replace('{user:login}', \RainLoop\Utils::UrlEncode( + \MailSo\Base\Utils::GetAccountNameFromEmail($sEmail), $bUrlEncode), $sLine); + $sLine = \str_replace('{user:domain}', \RainLoop\Utils::UrlEncode( + \MailSo\Base\Utils::GetDomainFromEmail($sEmail), $bUrlEncode), $sLine); + $sLine = \str_replace('{user:domain-clear}', \RainLoop\Utils::UrlEncode( + \MailSo\Base\Utils::GetClearDomainName( + \MailSo\Base\Utils::GetDomainFromEmail($sEmail)), $bUrlEncode), $sLine); } } @@ -997,6 +1002,18 @@ class Actions if (!!$this->Config()->Get('logs', 'enable', false)) { + $sSessionFilter = (string) $this->Config()->Get('logs', 'session_filter', ''); + if (!empty($sSessionFilter)) + { + $aSessionParts = \explode(':', $sSessionFilter, 2); + + if (empty($aSessionParts[0]) || empty($aSessionParts[1]) || + (string) $aSessionParts[1] !== (string) \RainLoop\Utils::GetCookie($aSessionParts[0], '')) + { + return $this->oLogger; + } + } + $iTimeOffset = (int) $this->Config()->Get('logs', 'time_offset', 0); $this->oLogger->SetShowSecter(!$this->Config()->Get('logs', 'hide_passwords', true)); @@ -1370,6 +1387,7 @@ class Actions 'LoginDefaultDomain' => $oConfig->Get('login', 'default_domain', ''), 'DetermineUserLanguage' => (bool) $oConfig->Get('login', 'determine_user_language', true), 'DetermineUserDomain' => (bool) $oConfig->Get('login', 'determine_user_domain', false), + 'UseLoginWelcomePage' => (bool) $oConfig->Get('login', 'welcome_page', false), 'ForgotPasswordLinkUrl' => \trim($oConfig->Get('login', 'forgot_password_link_url', '')), 'RegistrationLinkUrl' => \trim($oConfig->Get('login', 'registration_link_url', '')), 'ContactsIsAllowed' => false, @@ -1382,6 +1400,7 @@ class Actions 'MaterialDesign' => (bool) $oConfig->Get('labs', 'use_material_design', true), 'FolderSpecLimit' => (int) $oConfig->Get('labs', 'folders_spec_limit', 50), 'StartupUrl' => \trim(\ltrim(\trim($oConfig->Get('labs', 'startup_url', '')), '#/')), + 'Filtered' => '' !== \trim(\RainLoop\Api::Config()->Get('labs', 'imap_message_list_permanent_filter', '')), 'Community' => true, 'PremType' => false, 'Admin' => array(), @@ -1397,7 +1416,7 @@ class Actions $aResult['AttachmentsActions'][] = 'zip'; } - if (\RainLoop\Utils::IsOwnCloudLoggedIn() && \class_exists('\\OCP\\Files')) + if (\RainLoop\Utils::IsOwnCloudLoggedIn() && \class_exists('OCP\Files')) { $aResult['AttachmentsActions'][] = 'owncloud'; } @@ -1523,7 +1542,15 @@ class Actions $aResult['WelcomePageDisplay'] = ''; } - $aResult['StartupUrl'] = $this->compileLogParams($aResult['StartupUrl'], $oAccount); + if (!empty($aResult['StartupUrl'])) + { + $aResult['StartupUrl'] = $this->compileLogParams($aResult['StartupUrl'], $oAccount, true); + } + + if (!empty($aResult['UserIframeMessage'])) + { + $aResult['UserIframeMessage'] = $this->compileLogParams($aResult['UserIframeMessage'], $oAccount, true); + } } else { @@ -1543,6 +1570,7 @@ class Actions $aResult['AllowGoogleSocial'] = (bool) $oConfig->Get('social', 'google_enable', false); $aResult['AllowGoogleSocialAuth'] = (bool) $oConfig->Get('social', 'google_enable_auth', true); + $aResult['AllowGoogleSocialAuthFast'] = (bool) $oConfig->Get('social', 'google_enable_auth_fast', true); $aResult['AllowGoogleSocialDrive'] = (bool) $oConfig->Get('social', 'google_enable_drive', true); $aResult['AllowGoogleSocialPreview'] = (bool) $oConfig->Get('social', 'google_enable_preview', true); @@ -1553,6 +1581,7 @@ class Actions '' === \trim($oConfig->Get('social', 'google_client_id', '')) || '' === \trim($oConfig->Get('social', 'google_client_secret', ''))))) { $aResult['AllowGoogleSocialAuth'] = false; + $aResult['AllowGoogleSocialAuthFast'] = false; $aResult['AllowGoogleSocialDrive'] = false; $aResult['GoogleClientID'] = ''; $aResult['GoogleApiKey'] = ''; @@ -1563,7 +1592,9 @@ class Actions $aResult['AllowGoogleSocialPreview'] = false; } - if ($aResult['AllowGoogleSocial'] && !$aResult['AllowGoogleSocialAuth'] && !$aResult['AllowGoogleSocialDrive'] && !$aResult['AllowGoogleSocialPreview']) + if ($aResult['AllowGoogleSocial'] && + !$aResult['AllowGoogleSocialAuth'] && !$aResult['AllowGoogleSocialAuthFast'] && + !$aResult['AllowGoogleSocialDrive'] && !$aResult['AllowGoogleSocialPreview']) { $aResult['AllowGoogleSocial'] = false; } @@ -1639,6 +1670,7 @@ class Actions $aResult['AllowGoogleSocial'] = (bool) $oConfig->Get('social', 'google_enable', false); $aResult['AllowGoogleSocialAuth'] = (bool) $oConfig->Get('social', 'google_enable_auth', true); + $aResult['AllowGoogleSocialAuthFast'] = (bool) $oConfig->Get('social', 'google_enable_auth_fast', true); $aResult['AllowGoogleSocialDrive'] = (bool) $oConfig->Get('social', 'google_enable_drive', true); $aResult['AllowGoogleSocialPreview'] = (bool) $oConfig->Get('social', 'google_enable_preview', true); @@ -2882,7 +2914,7 @@ class Actions $mResult = false; - if (\RainLoop\Utils::IsOwnCloudLoggedIn() && \class_exists('\\OCP\\Files')) + if (\RainLoop\Utils::IsOwnCloudLoggedIn() && \class_exists('OCP\Files')) { $sSaveFolder = $this->Config()->Get('labs', 'owncloud_save_folder', ''); if (empty($sSaveFolder)) @@ -3644,6 +3676,7 @@ class Actions $this->setConfigFromParams($oConfig, 'GoogleEnable', 'social', 'google_enable', 'bool'); $this->setConfigFromParams($oConfig, 'GoogleEnableAuth', 'social', 'google_enable_auth', 'bool'); + $this->setConfigFromParams($oConfig, 'GoogleEnableAuthFast', 'social', 'google_enable_auth_fast', 'bool'); $this->setConfigFromParams($oConfig, 'GoogleEnableDrive', 'social', 'google_enable_drive', 'bool'); $this->setConfigFromParams($oConfig, 'GoogleEnablePreview', 'social', 'google_enable_preview', 'bool'); $this->setConfigFromParams($oConfig, 'GoogleClientID', 'social', 'google_client_id', 'string'); @@ -4092,7 +4125,7 @@ class Actions /** * @return string */ - private function rainloopRepo() + private function rainLoopRepo() { $sUrl = APP_REP_PATH; if ('' !== $sUrl) @@ -4281,7 +4314,7 @@ class Actions { $bRainLoopUpdatable = $this->rainLoopUpdatable(); - $aResult = $this->getRepositoryDataByUrl($this->rainloopRepo(), $bReal); + $aResult = $this->getRepositoryDataByUrl($this->rainLoopRepo(), $bReal); $aSub = array(); if (\is_array($aResult)) @@ -4555,7 +4588,7 @@ class Actions $bResult = false; if ('' !== $sRealFile) { - $sUrl = $this->rainloopRepo().$sRealFile; + $sUrl = $this->rainLoopRepo().$sRealFile; $sTmp = $this->downloadRemotePackageByUrl($sUrl); } @@ -6424,7 +6457,12 @@ class Actions */ public function DoQuota() { - $this->initMailClientConnection(); + $oAccount = $this->initMailClientConnection(); + + if (!$this->GetCapa(false, \RainLoop\Enumerations\Capa::QUOTA, $oAccount)) + { + return $this->DefaultResponse(__FUNCTION__, array(0, 0, 0, 0)); + } try { @@ -7960,6 +7998,11 @@ class Actions $aResult[] = \RainLoop\Enumerations\Capa::RELOAD; } + if ($oConfig->Get('capa', 'quota', true)) + { + $aResult[] = \RainLoop\Enumerations\Capa::QUOTA; + } + if ($oConfig->Get('capa', 'settings', true)) { $aResult[] = \RainLoop\Enumerations\Capa::SETTINGS; @@ -9453,6 +9496,24 @@ class Actions $mResult['InReplyTo'] = $mResponse->InReplyTo(); $mResult['References'] = $mResponse->References(); + $fAdditionalDomReader = null; + if (0 < \strlen($sHtml) && $this->Config()->Get('labs', 'emogrifier', false)) + { + if (!\class_exists('Pelago\Emogrifier', false)) + { + include_once APP_VERSION_ROOT_PATH.'app/libraries/emogrifier/Emogrifier.php'; + } + + if (\class_exists('Pelago\Emogrifier', false)) + { + $fAdditionalDomReader = function ($oDom) { + $oEmogrifier = new \Pelago\Emogrifier(); + $oEmogrifier->preserveEncoding = false; + return $oEmogrifier->emogrify($oDom); + }; + } + } + $fAdditionalExternalFilter = null; if (!!$this->Config()->Get('labs', 'use_local_proxy_for_external_images', false)) { @@ -9465,9 +9526,13 @@ class Actions }; } +// $this->Logger()->Write('---'); +// $this->Logger()->Write($sHtml); + $mResult['Html'] = 0 === \strlen($sHtml) ? '' : \MailSo\Base\HtmlUtils::ClearHtml( $sHtml, $bHasExternals, $mFoundedCIDs, $aContentLocationUrls, $mFoundedContentLocationUrls, false, false, - $fAdditionalExternalFilter + $fAdditionalExternalFilter, $fAdditionalDomReader, + !!$this->Config()->Get('labs', 'try_to_detect_hidden_images', false) ); $mResult['ExternalProxy'] = null !== $fAdditionalExternalFilter; @@ -9669,6 +9734,7 @@ class Actions 'UidNext' => $mResponse->UidNext, 'ThreadUid' => $mResponse->ThreadUid, 'NewMessages' => $this->responseObject($mResponse->NewMessages), + 'Filtered' => $mResponse->Filtered, 'Offset' => $mResponse->Offset, 'Limit' => $mResponse->Limit, 'Search' => $mResponse->Search diff --git a/rainloop/v/0.0.0/app/libraries/RainLoop/Api.php b/rainloop/v/0.0.0/app/libraries/RainLoop/Api.php index 7ac756b24..67ba60049 100644 --- a/rainloop/v/0.0.0/app/libraries/RainLoop/Api.php +++ b/rainloop/v/0.0.0/app/libraries/RainLoop/Api.php @@ -32,6 +32,7 @@ class Api if ($bOne) { \RainLoop\Api::SetupDefaultMailSoConfig(); + $bOne = \RainLoop\Api::RunResult(); } } @@ -97,6 +98,7 @@ class Api \MailSo\Config::$LargeThreadLimit = (int) \RainLoop\Api::Config()->Get('labs', 'imap_large_thread_limit', 50); + \MailSo\Config::$BoundaryPrefix = '_RainLoop_'; \MailSo\Config::$SystemLogger = \RainLoop\Api::Logger(); diff --git a/rainloop/v/0.0.0/app/libraries/RainLoop/Config/Application.php b/rainloop/v/0.0.0/app/libraries/RainLoop/Config/Application.php index 0edf2ea53..afda4c8f8 100644 --- a/rainloop/v/0.0.0/app/libraries/RainLoop/Config/Application.php +++ b/rainloop/v/0.0.0/app/libraries/RainLoop/Config/Application.php @@ -142,6 +142,7 @@ class Application extends \RainLoop\Config\AbstractConfig 'composer' => array(true), 'contacts' => array(true), 'settings' => array(true), + 'quota' => array(true), 'help' => array(true), 'reload' => array(true), 'search' => array(true), @@ -164,6 +165,8 @@ class Application extends \RainLoop\Config\AbstractConfig 'determine_user_language' => array(true, ''), 'determine_user_domain' => array(false, ''), + 'welcome_page' => array(false, ''), + 'forgot_password_link_url' => array('', ''), 'registration_link_url' => array('', ''), @@ -184,7 +187,7 @@ Values: 'defaults' => array( 'view_editor_type' => array('Html', 'Editor mode used by default (Plain, Html, HtmlForced or PlainForced)'), - 'view_layout' => array(1, 'layout: 0 - no preview, 1 - side preview, 3 - bottom preview'), + 'view_layout' => array(1, 'layout: 0 - no preview, 1 - side preview, 2 - bottom preview'), 'view_use_checkboxes' => array(true), 'autologout' => array(30), 'show_images' => array(false), @@ -205,6 +208,7 @@ Values: Disabling this option is not recommended.'), 'time_offset' => array(0), + 'session_filter' => array(''), 'filename' => array('log-{date:Y-m-d}.txt', 'Log filename. @@ -246,6 +250,7 @@ Examples: 'social' => array( 'google_enable' => array(false, 'Google'), 'google_enable_auth' => array(false), + 'google_enable_auth_fast' => array(false), 'google_enable_drive' => array(false), 'google_enable_preview' => array(false), 'google_client_id' => array(''), @@ -298,6 +303,7 @@ Enables caching in the system'), 'allow_html_editor_source_button' => array(false), 'allow_html_editor_biti_buttons' => array(false), 'allow_ctrl_enter_on_compose' => array(false), + 'try_to_detect_hidden_images' => array(false), 'hide_dangerous_actions' => array(false), 'use_app_debug_js' => array(false), 'use_app_debug_css' => array(false), @@ -340,6 +346,7 @@ Enables caching in the system'), 'fast_cache_memcache_expire' => array(43200), 'use_local_proxy_for_external_images' => array(false), 'startup_url' => array(''), + 'emogrifier' => array(true), 'dev_email' => array(''), 'dev_password' => array('') ), diff --git a/rainloop/v/0.0.0/app/libraries/RainLoop/Enumerations/Capa.php b/rainloop/v/0.0.0/app/libraries/RainLoop/Enumerations/Capa.php index d7602cdee..56912b5e2 100644 --- a/rainloop/v/0.0.0/app/libraries/RainLoop/Enumerations/Capa.php +++ b/rainloop/v/0.0.0/app/libraries/RainLoop/Enumerations/Capa.php @@ -24,6 +24,7 @@ class Capa const SEARCH = 'SEARCH'; const SEARCH_ADV = 'SEARCH_ADV'; const SETTINGS = 'SETTINGS'; + const QUOTA = 'QUOTA'; const HELP = 'HELP'; const TEMPLATES = 'TEMPLATES'; const MESSAGE_ACTIONS = 'MESSAGE_ACTIONS'; diff --git a/rainloop/v/0.0.0/app/libraries/RainLoop/Plugins/AbstractPlugin.php b/rainloop/v/0.0.0/app/libraries/RainLoop/Plugins/AbstractPlugin.php index 67b147a12..5f4b8101d 100644 --- a/rainloop/v/0.0.0/app/libraries/RainLoop/Plugins/AbstractPlugin.php +++ b/rainloop/v/0.0.0/app/libraries/RainLoop/Plugins/AbstractPlugin.php @@ -304,6 +304,22 @@ abstract class AbstractPlugin return $this; } + /** + * @param string $sFile + * @param bool $bAdminScope = false + * + * @return self + */ + protected function replaceTemplate($sFile, $bAdminScope = false) + { + if ($this->oPluginManager) + { + $this->oPluginManager->AddTemplate($this->sPath.'/'.$sFile, $bAdminScope); + } + + return $this; + } + /** * @param string $sActionName * @param string $sFunctionName diff --git a/rainloop/v/0.0.0/app/libraries/RainLoop/Plugins/Manager.php b/rainloop/v/0.0.0/app/libraries/RainLoop/Plugins/Manager.php index a4ecfb7ca..00d751723 100644 --- a/rainloop/v/0.0.0/app/libraries/RainLoop/Plugins/Manager.php +++ b/rainloop/v/0.0.0/app/libraries/RainLoop/Plugins/Manager.php @@ -230,7 +230,7 @@ class Manager public function HaveJs($bAdminScope = false) { $bResult = false; - + if ($this->bIsEnabled) { $bResult = $bAdminScope ? 0 < \count($this->aAdminJs) : 0 < \count($this->aJs); @@ -274,27 +274,24 @@ class Manager } /** + * @param array $aList * @param bool $bAdminScope = false * @return string */ - public function CompileTemplate($bAdminScope = false) + public function CompileTemplate(&$aList, $bAdminScope = false) { - $sResult = ''; if ($this->bIsEnabled) { $aTemplates = $bAdminScope ? $this->aAdminTemplates : $this->aTemplates; foreach ($aTemplates as $sFile) { - if (file_exists($sFile)) + if (\file_exists($sFile)) { - $sTemplateName = substr(basename($sFile), 0, -5); - $sResult .= ''; + $sTemplateName = \substr(\basename($sFile), 0, -5); + $aList[$sTemplateName] = $sFile; } } } - - return $sResult; } /** diff --git a/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/Suggestions/OwnCloudSuggestions.php b/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/Suggestions/OwnCloudSuggestions.php index a32e9e965..33eae71bf 100644 --- a/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/Suggestions/OwnCloudSuggestions.php +++ b/rainloop/v/0.0.0/app/libraries/RainLoop/Providers/Suggestions/OwnCloudSuggestions.php @@ -25,7 +25,7 @@ class OwnCloudSuggestions implements \RainLoop\Providers\Suggestions\ISuggestion try { if ('' === $sQuery || !$oAccount || !\RainLoop\Utils::IsOwnCloudLoggedIn() || - !\class_exists('\\OCP\\Contacts') || !\OCP\Contacts::isEnabled() + !\class_exists('OCP\Contacts') || !\OCP\Contacts::isEnabled() ) { return $aResult; diff --git a/rainloop/v/0.0.0/app/libraries/RainLoop/ServiceActions.php b/rainloop/v/0.0.0/app/libraries/RainLoop/ServiceActions.php index 1f75a9bb2..19b2cb183 100644 --- a/rainloop/v/0.0.0/app/libraries/RainLoop/ServiceActions.php +++ b/rainloop/v/0.0.0/app/libraries/RainLoop/ServiceActions.php @@ -1220,11 +1220,23 @@ class ServiceActions */ public function compileTemplates($bAdmin = false, $bJsOutput = true) { - $sHtml = - \RainLoop\Utils::CompileTemplates(APP_VERSION_ROOT_PATH.'app/templates/Views/Components', $this->oActions, 'Component'). - \RainLoop\Utils::CompileTemplates(APP_VERSION_ROOT_PATH.'app/templates/Views/'.($bAdmin ? 'Admin' : 'User'), $this->oActions). - \RainLoop\Utils::CompileTemplates(APP_VERSION_ROOT_PATH.'app/templates/Views/Common', $this->oActions). - $this->oActions->Plugins()->CompileTemplate($bAdmin); + $aTemplates = array(); + + \RainLoop\Utils::CompileTemplates($aTemplates, APP_VERSION_ROOT_PATH.'app/templates/Views/Components', 'Component'); + \RainLoop\Utils::CompileTemplates($aTemplates, APP_VERSION_ROOT_PATH.'app/templates/Views/'.($bAdmin ? 'Admin' : 'User')); + \RainLoop\Utils::CompileTemplates($aTemplates, APP_VERSION_ROOT_PATH.'app/templates/Views/Common'); + + $this->oActions->Plugins()->CompileTemplate($aTemplates, $bAdmin); + + $sHtml = ''; + foreach ($aTemplates as $sName => $sFile) + { + $sName = \preg_replace('/[^a-zA-Z0-9]/', '', $sName); + $sHtml .= ''; + } + + unset($aTemplates); return $bJsOutput ? 'window.rainloopTEMPLATES='.\MailSo\Base\Utils::Php2js(array($sHtml), $this->Logger()).';' : $sHtml; } diff --git a/rainloop/v/0.0.0/app/libraries/RainLoop/Utils.php b/rainloop/v/0.0.0/app/libraries/RainLoop/Utils.php index 2eebccc0c..594a2fc8e 100644 --- a/rainloop/v/0.0.0/app/libraries/RainLoop/Utils.php +++ b/rainloop/v/0.0.0/app/libraries/RainLoop/Utils.php @@ -48,7 +48,7 @@ class Utils define('_phpseclib_', true); } - if (!\class_exists('Crypt_RSA')) + if (!\class_exists('Crypt_RSA', false)) { include_once 'Crypt/RSA.php'; \defined('CRYPT_RSA_MODE') || \define('CRYPT_RSA_MODE', CRYPT_RSA_MODE_INTERNAL); @@ -470,30 +470,22 @@ class Utils } /** + * @param array $aList * @param string $sDirName - * @param \RainLoop\Actions $oAction * @param string $sNameSuffix = '' - * - * @return string */ - public static function CompileTemplates($sDirName, $oAction, $sNameSuffix = '') + public static function CompileTemplates(&$aList, $sDirName, $sNameSuffix = '') { - $sResult = ''; if (\file_exists($sDirName)) { - $aList = \RainLoop\Utils::FolderFiles($sDirName, '.html'); + $aFileList = \RainLoop\Utils::FolderFiles($sDirName, '.html'); - foreach ($aList as $sName) + foreach ($aFileList as $sName) { $sTemplateName = \substr($sName, 0, -5).$sNameSuffix; - $sResult .= ''; + $aList[$sTemplateName] = $sDirName.'/'.$sName; } - - $sResult = \trim($sResult); } - - return $sResult; } /** @@ -539,14 +531,25 @@ class Utils public static function IsOwnCloud() { return isset($_ENV['RAINLOOP_OWNCLOUD']) && $_ENV['RAINLOOP_OWNCLOUD'] && - \class_exists('\\OC'); + \class_exists('OC'); } /** * @return bool */ public static function IsOwnCloudLoggedIn() { - return self::IsOwnCloud() && \class_exists('\\OCP\\User') && \OCP\User::isLoggedIn(); + return self::IsOwnCloud() && \class_exists('OCP\User') && \OCP\User::isLoggedIn(); + } + + /** + * @param string $sV + * @param bool $bEncode = false + * + * @return string + */ + public static function UrlEncode($sV, $bEncode = false) + { + return $bEncode ? \urlencode($sV) : $sV; } /** @@ -557,7 +560,7 @@ class Utils $sAppPath = ''; if (\RainLoop\Utils::IsOwnCloud()) { - if (\class_exists('\\OC_App')) + if (\class_exists('OC_App')) { $sAppPath = \rtrim(\trim(\OC_App::getAppWebPath('rainloop')), '\\/').'/app/'; } diff --git a/rainloop/v/0.0.0/app/libraries/emogrifier/CHANGELOG.TXT b/rainloop/v/0.0.0/app/libraries/emogrifier/CHANGELOG.TXT new file mode 100644 index 000000000..55c062e72 --- /dev/null +++ b/rainloop/v/0.0.0/app/libraries/emogrifier/CHANGELOG.TXT @@ -0,0 +1,73 @@ +2012-05-01 +Made removal of invisible nodes operate in a case-insensitive manner... Thanks Juha P.! + +2012-02-07 +Fixed some recent code introductions to use class constants rather than global constants. +Fixed some recent code introductions to make it cleaner to read. + +2012-01-31 +Fixed a bug that was introduced with the 2011-12-22 revision... Thanks Sagi L. and M. Bąkowski! +Added extraction of diff --git a/rainloop/v/0.0.0/app/templates/Themes/template.less b/rainloop/v/0.0.0/app/templates/Themes/template.less index bd4b0bec8..570df86a5 100644 --- a/rainloop/v/0.0.0/app/templates/Themes/template.less +++ b/rainloop/v/0.0.0/app/templates/Themes/template.less @@ -59,12 +59,15 @@ } .thm-login { - color: @login-color !important; border: @login-border !important; .thm-rgba-background-color(@login-background-color, @login-rgba-background-color); .thm-linear-gradient-mixin(@login-gradient-start, @login-gradient-end); .thm-border-radius(@login-border-radius); .thm-box-shadow(@login-box-shadow); +} + +.thm-login-text { + color: @login-color !important; .legend, .e-checkbox-icon, .g-ui-link, .social-button, .language-button { color: @login-color !important; diff --git a/rainloop/v/0.0.0/app/templates/Views/Admin/AdminLogin.html b/rainloop/v/0.0.0/app/templates/Views/Admin/AdminLogin.html index 1dc30c7c2..ab0051716 100644 --- a/rainloop/v/0.0.0/app/templates/Views/Admin/AdminLogin.html +++ b/rainloop/v/0.0.0/app/templates/Views/Admin/AdminLogin.html @@ -5,7 +5,7 @@ -