snappymail/dev/boot.js
S-A-L13 876ed221c3
Update Fork (#2)
* Cleanup OpenPgpImportPopupView code

* update polish translation

* small fix

* Added Import S/MIME certificate popup
And much better handling of the sign and encrypt options

* bugfix: store in Passphrases

* Resolve #1448

* pre-verify S/MIME opaque signed messages so we have a body to view

* Fix timestampToString() for future dates

* Move php8.php to /app/libraries/polyfill/

* Improved Settings handling to prevent bugs in outer code

* Changed AbstractProvider::IsActive() to be abstract

* Example for #1449

* bugfix: previous IsActive() commit

* OpenSSL required due to S/MIME

* Use get_debug_type() instead of gettype()

* update polish translation

* Make all Enumerations classes abstract

* Added search functionality in Admin -> Config
And removed the unused ['capa']['quota']

* Cleanup Quota handling

* OPEN_PGP should be OPENPGP as it is one word

* Improve Capa handling

* Resolve #1451

* Bugfix TypeError: b64Encode(...).match(...) is null

* Small StorageType change

* Bugfix: mailvelope editor failed

* Bugfix: undefined getMailvelopePrivateKeyFor()

* Bugfix: MIME parser RegExp didn't escape `boundary` which caused issues

* Return detailed info on PgpImportKey

* Show GnuPG verify error

* Sort PGP keys by email and id

* Sort S/MIME certificates on emailAddress else validTo

* S/MIME import from signature use `BEGIN PKCS7`

* Optionally use existing private key to generate S/MIME certificate

* Chaned some error_log() to MailSo Logger()

* Force reload of S/MIME certificates list on import

* Make better use of SnappyMail\SensitiveString

* Fix view PGP key button

* Mask all POST data that has a key which contains `pass`

* v2.35.1

* Resolve #1455

* Improved GnuPG error handling

* Update pt/pt-PT translation

* update Polish translation

* Drop support for gnupg pecl extension as it fails with "no passphrase" issues

* Resolve #1456

* Resolve #1458

* v2.35.2

* fix changelog

* Resolve #1461

* Update pt/pt-PT translation

* compact-composer plugin v1.0.0

* Resolve #1462

* Fix decrypt error message

* `new Error()` to `Error()`

* Resolve #1463

* Show url for #1466

* Simplify SignMe/Remember me code

* Simplify language Notifications

* Bugfix: SetPassword expects \SnappyMail\SensitiveString

* https://github.com/the-djmaze/snappymail/issues/1450#issuecomment-1972147950

* improve: fire the 'squire2-toolbar' event after more props are added

* improve: add dark theme support and use 'button' element as menu trigger for consistent styling

* fix: use compact template in non-destructive way (do not replace the PopupsCompose template if a different wysiwyg is used)

* Update admin.json

* Update user.json

* CSS rainloopErrorTip location

* Improved error handling on PGP and S/MIME decrypt

* KnockoutJS remove unused `beforeRemove`

* KnockoutJS drop unused `as`

* KnockoutJS simplify renderMode because only 1 option is used

* KnoutJS cleanup templating.js a bit

* KnockoutJS drop unused `bindingRewriteValidators`

* KnockoutJS drop the twoWayBindings code

* KnockoutJS simplify virtualElements binding check

* KnockoutJS simplify applyBindingsToNodeInternal

* KnockoutJS use Array.isArray

* KnockoutJS drop alias `textinput` for `textInput`

* KnockoutJS scramble `createChildContext`

* KnockoutJS scramble `controlsDescendantBindings`

* KnockoutJS scramble `exportDependencies`

* KnockoutJS drop unused `throttleEvaluation`

* KnockoutJS drop unused `valueAllowUnset`

* KnockoutJS drop unused `templateNodes`

* KnockoutJS drop unused `optionsCaption`

* KnockoutJS drop unused `dontLimitMoves`

* KnockoutJS drop unused `uniqueName`

* KnockoutJS drop IE leftovers

* KnockoutJS drop unused `preprocess`

* KnockoutJS drop unused "disposeWhenNodeIsRemoved" and "disposeWhen"

* KnockoutJS don't scramble exportDependencies. controlsDescendantBindings, createChildContext

* KnockoutJS drop unused `$parentContext` and `$parents`

* KnockoutJS drop unused `$rawData`

* Knockoutjs built latest

* KnockoutJS drop unused template options `nodes`, `if`, `ifnot`

* KnockoutJS use more Array.isArray

* KnockoutJS cleanup code a bit

* KnockoutJS primitiveTypes can just be checked with Object()

* KnockoutJS rebuilt

* Verify S/MIME signed automatically and log Exception

* Automatically verify PGP and S/MIME signed messages

* `new Error` to `Error`

* By default throw AccountNotAllowed as confused in #1478

* GPG use pinentries for decrypt, sign and export

* Better GPG error handling

* GPG show error on view/export

* OpenPGP fix handling of importing keys

* Make "verify signatures automatically" optional, as it requires more IMAP fetching

* S/MIME don't post identity key and certificate, just fetch from server

* Show error to old browsers, instead of crashing

* Automatically verify S/MIME decrypted signed message

---------

Co-authored-by: the-djmaze <>
Co-authored-by: tinola <tinola@poczta.onet.pl>
Co-authored-by: Maarten <3752035+the-djmaze@users.noreply.github.com>
Co-authored-by: lmperfis <joint.striker@gmail.com>
Co-authored-by: Sergey Mosin <sergey@srgdev.com>
Co-authored-by: hguilbert <51283484+hguilbert@users.noreply.github.com>
2024-03-04 17:00:27 +01:00

159 lines
4 KiB
JavaScript

(doc => {
const
qUri = path => doc.location.pathname.replace(/\/+$/,'') + '/?/' + path,
eId = id => doc.getElementById('rl-'+id),
admin = '1' == eId('app').dataset.admin,
toggle = div => {
eId('loading').hidden = true;
div.hidden = false;
},
showError = msg => {
let div = eId('loading-error');
div.append(msg);
toggle(div);
},
loadScript = src => src ? new Promise((resolve, reject) => {
const script = doc.createElement('script');
script.onload = () => resolve();
script.onerror = () => reject('Failed loading ' + src);
script.src = src;
// script.async = true;
doc.head.append(script);
}) : Promise.reject('src is empty');
try {
let smctoken = doc.cookie.match(/(^|;) ?smctoken=([^;]+)/);
smctoken = smctoken ? smctoken[2] : localStorage.getItem('smctoken');
if (!smctoken) {
let data = new Uint8Array(16);
crypto.getRandomValues(data);
smctoken = encodeURIComponent(btoa(String.fromCharCode(...data)));
}
localStorage.setItem('smctoken', smctoken);
doc.cookie = 'smctoken='+smctoken+";path=/;samesite=strict";
} catch (e) {
console.error(e);
}
let RL_APP_DATA = {};
window.rl = {
adminArea: () => admin,
settings: {
get: name => RL_APP_DATA[name],
set: (name, value) => RL_APP_DATA[name] = value,
app: name => RL_APP_DATA.System[name]
},
setTitle: title =>
doc.title = (title || '') + (RL_APP_DATA.title ? (title ? ' - ' : '') + RL_APP_DATA.title : ''),
setData: appData => {
RL_APP_DATA = appData;
rl.app.refresh();
},
loadScript: loadScript,
fetch: (resource, init, postData) => {
init = Object.assign({
mode: 'same-origin',
cache: 'no-cache',
redirect: 'error',
referrerPolicy: 'no-referrer',
credentials: 'same-origin',
headers: {}
}, init);
let asJSON = 1,
XToken = (RL_APP_DATA.System || {}).token,
object = {};
if (postData) {
init.method = 'POST';
if (postData instanceof FormData) {
postData.forEach((value, key) => {
if (value instanceof File) {
asJSON = 0;
} else if (!Reflect.has(object, key)) {
object[key] = value;
} else {
Array.isArray(object[key]) || (object[key] = [object[key]]);
object[key].push(value);
}
});
if (asJSON) {
postData = object;
// postData.XToken = XToken;
} else {
XToken && postData.set('XToken', XToken);
}
}
if (asJSON) {
init.headers['Content-Type'] = 'application/json';
postData = JSON.stringify(postData);
}
init.body = postData;
}
XToken && (init.headers['X-SM-Token'] = XToken);
// init.headers = new Headers(init.headers);
return fetch(resource, init);
},
fetchJSON: (resource, init, postData) => {
init = Object.assign({ headers: {} }, init);
init.headers.Accept = 'application/json';
return rl.fetch(resource, init, postData).then(response => {
if (response.ok) {
/* TODO: use this for non-developers?
response.clone()
let data = response.text();
try {
return JSON.parse(data);
} catch (e) {
console.error(e);
// console.log(data);
return Promise.reject(Notifications.JsonParse);
return {
Result: false,
ErrorCode: 952, // Notifications.JsonParse
ErrorMessage: e.message,
ErrorMessageAdditional: data
}
}
*/
return response.json();
}
return Promise.reject('Network response error: ' + response.status);
});
}
};
if (!navigator.cookieEnabled) {
toggle(eId('NoCookie'));
} else if (![].flat) {
toggle(eId('BadBrowser'));
} else {
rl.fetchJSON(qUri(`${admin ? 'Admin' : ''}AppData/0/${Math.random().toString().slice(2)}/`))
.then(appData => {
RL_APP_DATA = appData;
const url = appData.StaticLibsJs,
cb = () => rl.app.bootstart();
loadScript(url)
.then(() => loadScript(url.replace('/libs.', `/${admin?'admin':'app'}.`)))
.then(() => appData.PluginsLink ? loadScript(qUri(appData.PluginsLink)) : Promise.resolve())
.then(() => rl.app
? cb()
: doc.addEventListener('readystatechange', () => 'complete' == doc.readyState && cb())
)
.catch(e => {
showError(e);
throw e;
});
})
.catch(e => showError(e));
}
})(document);