emailengine-app 2.61.1 → 2.61.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (136) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/data/google-crawlers.json +1 -1
  3. package/lib/account/account-state.js +248 -0
  4. package/lib/account.js +17 -178
  5. package/lib/api-routes/account-routes.js +1006 -0
  6. package/lib/api-routes/message-routes.js +1377 -0
  7. package/lib/consts.js +12 -2
  8. package/lib/email-client/base-client.js +282 -771
  9. package/lib/email-client/gmail/gmail-api.js +243 -0
  10. package/lib/email-client/gmail-client.js +145 -53
  11. package/lib/email-client/imap/mailbox.js +24 -698
  12. package/lib/email-client/imap/sync-operations.js +812 -0
  13. package/lib/email-client/imap-client.js +1 -1
  14. package/lib/email-client/message-builder.js +566 -0
  15. package/lib/email-client/notification-handler.js +314 -0
  16. package/lib/email-client/outlook/graph-api.js +326 -0
  17. package/lib/email-client/outlook-client.js +159 -113
  18. package/lib/email-client/smtp-pool-manager.js +196 -0
  19. package/lib/imapproxy/imap-server.js +3 -12
  20. package/lib/oauth/gmail.js +4 -4
  21. package/lib/oauth/mail-ru.js +30 -5
  22. package/lib/oauth/outlook.js +57 -3
  23. package/lib/oauth/pubsub/google.js +30 -11
  24. package/lib/oauth/scope-checker.js +202 -0
  25. package/lib/oauth2-apps.js +8 -4
  26. package/lib/redis-operations.js +484 -0
  27. package/lib/routes-ui.js +283 -2582
  28. package/lib/tools.js +4 -196
  29. package/lib/ui-routes/account-routes.js +1931 -0
  30. package/lib/ui-routes/admin-config-routes.js +1233 -0
  31. package/lib/ui-routes/admin-entities-routes.js +2367 -0
  32. package/lib/ui-routes/oauth-routes.js +992 -0
  33. package/lib/utils/network.js +237 -0
  34. package/package.json +9 -9
  35. package/sbom.json +1 -1
  36. package/static/js/app.js +5 -5
  37. package/static/licenses.html +78 -18
  38. package/translations/de.mo +0 -0
  39. package/translations/de.po +85 -82
  40. package/translations/en.mo +0 -0
  41. package/translations/en.po +63 -71
  42. package/translations/et.mo +0 -0
  43. package/translations/et.po +84 -82
  44. package/translations/fr.mo +0 -0
  45. package/translations/fr.po +85 -82
  46. package/translations/ja.mo +0 -0
  47. package/translations/ja.po +84 -82
  48. package/translations/messages.pot +74 -87
  49. package/translations/nl.mo +0 -0
  50. package/translations/nl.po +86 -82
  51. package/translations/pl.mo +0 -0
  52. package/translations/pl.po +84 -82
  53. package/views/account/security.hbs +4 -4
  54. package/views/accounts/account.hbs +13 -13
  55. package/views/accounts/register/imap-server.hbs +12 -12
  56. package/views/config/document-store/pre-processing/index.hbs +4 -2
  57. package/views/config/oauth/app.hbs +6 -7
  58. package/views/config/oauth/index.hbs +2 -2
  59. package/views/config/service.hbs +3 -4
  60. package/views/dashboard.hbs +5 -7
  61. package/views/error.hbs +22 -7
  62. package/views/gateways/gateway.hbs +2 -2
  63. package/views/partials/add_account_modal.hbs +7 -10
  64. package/views/partials/document_store_header.hbs +1 -1
  65. package/views/partials/editor_scope_info.hbs +0 -1
  66. package/views/partials/oauth_config_header.hbs +1 -1
  67. package/views/partials/side_menu.hbs +3 -3
  68. package/views/partials/webhook_form.hbs +2 -2
  69. package/views/templates/index.hbs +1 -1
  70. package/views/templates/template.hbs +8 -8
  71. package/views/tokens/index.hbs +6 -6
  72. package/views/tokens/new.hbs +1 -1
  73. package/views/webhooks/index.hbs +4 -4
  74. package/views/webhooks/webhook.hbs +7 -7
  75. package/workers/api.js +148 -2436
  76. package/workers/smtp.js +2 -1
  77. package/lib/imapproxy/imap-core/test/client.js +0 -46
  78. package/lib/imapproxy/imap-core/test/fixtures/append.eml +0 -1196
  79. package/lib/imapproxy/imap-core/test/fixtures/chunks.js +0 -44
  80. package/lib/imapproxy/imap-core/test/fixtures/fix1.eml +0 -6
  81. package/lib/imapproxy/imap-core/test/fixtures/fix2.eml +0 -599
  82. package/lib/imapproxy/imap-core/test/fixtures/fix3.eml +0 -32
  83. package/lib/imapproxy/imap-core/test/fixtures/fix4.eml +0 -6
  84. package/lib/imapproxy/imap-core/test/fixtures/mimetorture.eml +0 -599
  85. package/lib/imapproxy/imap-core/test/fixtures/mimetorture.js +0 -2740
  86. package/lib/imapproxy/imap-core/test/fixtures/mimetorture.json +0 -1411
  87. package/lib/imapproxy/imap-core/test/fixtures/mimetree.js +0 -85
  88. package/lib/imapproxy/imap-core/test/fixtures/nodemailer.eml +0 -582
  89. package/lib/imapproxy/imap-core/test/fixtures/ryan_finnie_mime_torture.eml +0 -599
  90. package/lib/imapproxy/imap-core/test/fixtures/simple.eml +0 -42
  91. package/lib/imapproxy/imap-core/test/fixtures/simple.json +0 -164
  92. package/lib/imapproxy/imap-core/test/imap-compile-stream-test.js +0 -671
  93. package/lib/imapproxy/imap-core/test/imap-compiler-test.js +0 -272
  94. package/lib/imapproxy/imap-core/test/imap-indexer-test.js +0 -236
  95. package/lib/imapproxy/imap-core/test/imap-parser-test.js +0 -922
  96. package/lib/imapproxy/imap-core/test/memory-notifier.js +0 -129
  97. package/lib/imapproxy/imap-core/test/prepare.sh +0 -74
  98. package/lib/imapproxy/imap-core/test/protocol-test.js +0 -1756
  99. package/lib/imapproxy/imap-core/test/search-test.js +0 -1356
  100. package/lib/imapproxy/imap-core/test/test-client.js +0 -152
  101. package/lib/imapproxy/imap-core/test/test-server.js +0 -623
  102. package/lib/imapproxy/imap-core/test/tools-test.js +0 -22
  103. package/test/api-test.js +0 -899
  104. package/test/autoreply-test.js +0 -327
  105. package/test/bounce-test.js +0 -151
  106. package/test/complaint-test.js +0 -256
  107. package/test/fixtures/autoreply/LICENSE +0 -27
  108. package/test/fixtures/autoreply/rfc3834-01.eml +0 -23
  109. package/test/fixtures/autoreply/rfc3834-02.eml +0 -24
  110. package/test/fixtures/autoreply/rfc3834-03.eml +0 -26
  111. package/test/fixtures/autoreply/rfc3834-04.eml +0 -48
  112. package/test/fixtures/autoreply/rfc3834-05.eml +0 -19
  113. package/test/fixtures/autoreply/rfc3834-06.eml +0 -59
  114. package/test/fixtures/bounces/163.eml +0 -2521
  115. package/test/fixtures/bounces/fastmail.eml +0 -242
  116. package/test/fixtures/bounces/gmail.eml +0 -252
  117. package/test/fixtures/bounces/hotmail.eml +0 -655
  118. package/test/fixtures/bounces/mailru.eml +0 -121
  119. package/test/fixtures/bounces/outlook.eml +0 -1107
  120. package/test/fixtures/bounces/postfix.eml +0 -101
  121. package/test/fixtures/bounces/rambler.eml +0 -116
  122. package/test/fixtures/bounces/workmail.eml +0 -142
  123. package/test/fixtures/bounces/yahoo.eml +0 -139
  124. package/test/fixtures/bounces/zoho.eml +0 -83
  125. package/test/fixtures/bounces/zonemta.eml +0 -100
  126. package/test/fixtures/complaints/LICENSE +0 -27
  127. package/test/fixtures/complaints/amazonses.eml +0 -72
  128. package/test/fixtures/complaints/dmarc.eml +0 -59
  129. package/test/fixtures/complaints/hotmail.eml +0 -49
  130. package/test/fixtures/complaints/optout.eml +0 -40
  131. package/test/fixtures/complaints/standard-arf.eml +0 -68
  132. package/test/fixtures/complaints/yahoo.eml +0 -68
  133. package/test/oauth2-apps-test.js +0 -301
  134. package/test/sendonly-test.js +0 -160
  135. package/test/test-config.js +0 -34
  136. package/test/webhooks-server.js +0 -39
package/lib/tools.js CHANGED
@@ -24,7 +24,6 @@ const { PassThrough } = require('stream');
24
24
  const socks = require('socks');
25
25
  const os = require('os');
26
26
  const Fs = require('fs');
27
- const net = require('net');
28
27
  const fs = Fs.promises;
29
28
  const pathlib = require('path');
30
29
  const { randomUUID: uuid } = require('crypto');
@@ -41,11 +40,10 @@ const {
41
40
  FETCH_RETRY_MAX,
42
41
  URL_FETCH_RETRY_MAX
43
42
  } = require('./consts');
44
- const ipaddr = require('ipaddr.js');
45
43
  const bullmqPackage = require('bullmq/package.json');
46
- const { reverse: dnsReverse } = require('dns').promises;
47
- const googleCrawlerRanges = require('../data/google-crawlers.json');
48
- const { resolvePublicInterfaces } = require('pubface');
44
+
45
+ // Network utilities - imported for internal use only
46
+ const { getLocalAddress } = require('./utils/network');
49
47
 
50
48
  const { fetch: fetchCmd, Agent, RetryAgent } = require('undici');
51
49
 
@@ -62,20 +60,6 @@ const retryAgent = new RetryAgent(fetchAgent, {
62
60
  statusCodes: [429] // do not retry 5xx errors
63
61
  });
64
62
 
65
- const googleCrawlerMap = new Map();
66
- for (let prefixEntry of googleCrawlerRanges.prefixes) {
67
- for (let prefixKey of ['ipv6Prefix', 'ipv4Prefix']) {
68
- if (prefixEntry[prefixKey]) {
69
- // check if remote address is in range
70
- let parsed = ipaddr.parseCIDR(prefixEntry[prefixKey]);
71
- if (!googleCrawlerMap.has(prefixKey)) {
72
- googleCrawlerMap.set(prefixKey, []);
73
- }
74
- googleCrawlerMap.get(prefixKey).push(parsed);
75
- }
76
- }
77
- }
78
-
79
63
  class LRUCache extends Map {
80
64
  constructor(maxSize = 1000) {
81
65
  super();
@@ -911,7 +895,7 @@ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEV3QUiYsp13nD9suD1/ZkEXnuMoSg
911
895
  verifyPromises.push(
912
896
  (async () => {
913
897
  try {
914
- let { localAddress: address, name } = await module.exports.getLocalAddress(redis, 'smtp', 'test');
898
+ let { localAddress: address, name } = await getLocalAddress(redis, 'smtp', 'test');
915
899
 
916
900
  let smtpLogger = {};
917
901
  let smtpConfig = Object.assign(
@@ -1561,110 +1545,6 @@ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEV3QUiYsp13nD9suD1/ZkEXnuMoSg
1561
1545
  return hostname;
1562
1546
  },
1563
1547
 
1564
- async updatePublicInterfaces(redis) {
1565
- let interfaces = await resolvePublicInterfaces();
1566
-
1567
- for (let iface of interfaces) {
1568
- if (!iface.localAddress) {
1569
- continue;
1570
- }
1571
-
1572
- if (iface.defaultInterface) {
1573
- await redis.hset(`${REDIS_PREFIX}interfaces`, `default:${iface.family}`, iface.localAddress);
1574
- }
1575
-
1576
- let existingEntry = await redis.hget(`${REDIS_PREFIX}interfaces`, iface.localAddress);
1577
- if (existingEntry) {
1578
- try {
1579
- existingEntry = JSON.parse(existingEntry);
1580
-
1581
- iface.name = iface.name || existingEntry.name;
1582
-
1583
- if (!iface.localAddress || !iface.ip || !iface.name) {
1584
- // not much point in updating
1585
- continue;
1586
- }
1587
- } catch (err) {
1588
- // ignore?
1589
- }
1590
- }
1591
-
1592
- delete iface.defaultInterface;
1593
- await redis.hset(`${REDIS_PREFIX}interfaces`, iface.localAddress, JSON.stringify(iface));
1594
- }
1595
- },
1596
-
1597
- async getLocalAddress(redis, protocol, account, hint) {
1598
- let existingAddresses = Object.values(os.networkInterfaces())
1599
- .flatMap(entry => entry)
1600
- .map(entry => entry.address);
1601
-
1602
- if (hint) {
1603
- let parsedHint = ipaddr.parse(hint);
1604
- let normalizedHint = parsedHint.toNormalizedString();
1605
- let iface = await redis.hget(`${REDIS_PREFIX}interfaces`, normalizedHint);
1606
- try {
1607
- iface = iface ? JSON.parse(iface) : null;
1608
- } catch (err) {
1609
- // ignore
1610
- }
1611
- if (iface && existingAddresses.includes(iface.localAddress)) {
1612
- iface.addressSelector = 'hint';
1613
- return iface;
1614
- }
1615
- }
1616
-
1617
- let addressStrategy = await settings.get(`${protocol}Strategy`);
1618
- let localAddresses = []
1619
- .concat((await settings.get(`localAddresses`)) || [])
1620
- .filter(address => existingAddresses.includes(address))
1621
- .filter(address => net.isIPv4(address));
1622
- let localAddress;
1623
-
1624
- let hostname = await module.exports.getServiceHostname(await settings.get('smtpEhloName'));
1625
-
1626
- let addressSelector;
1627
-
1628
- if (!localAddresses.length) {
1629
- addressSelector = 'default';
1630
- return { address: false, name: hostname, addressSelector };
1631
- } else if (localAddresses.length === 1) {
1632
- addressSelector = 'single';
1633
- localAddress = localAddresses[0];
1634
- } else {
1635
- switch (addressStrategy) {
1636
- case 'random': {
1637
- addressSelector = 'random';
1638
- localAddress = localAddresses[Math.floor(Math.random() * localAddresses.length)];
1639
- break;
1640
- }
1641
- case 'dedicated':
1642
- addressSelector = 'dedicated';
1643
- localAddress = module.exports.selectRendezvousAddress(account, localAddresses);
1644
- break;
1645
- default:
1646
- addressSelector = 'unknown';
1647
- return { address: false, name: hostname, addressSelector };
1648
- }
1649
- }
1650
-
1651
- if (!localAddress) {
1652
- addressSelector = 'unset';
1653
- return { address: false, name: hostname, addressSelector };
1654
- }
1655
-
1656
- try {
1657
- let addressData = JSON.parse(await redis.hget(`${REDIS_PREFIX}interfaces`, localAddress));
1658
- addressData.name = addressData.name || hostname;
1659
- addressData.addressSelector = addressSelector;
1660
- return addressData;
1661
- } catch (err) {
1662
- logger.error({ msg: 'Failed to load address data', localAddress, err });
1663
- addressSelector = 'error';
1664
- return { address: false, name: hostname, addressSelector };
1665
- }
1666
- },
1667
-
1668
1548
  unpackUIDRangeForSearch(uid) {
1669
1549
  uid = (uid || '').toString();
1670
1550
  let parts = uid
@@ -1764,27 +1644,6 @@ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEV3QUiYsp13nD9suD1/ZkEXnuMoSg
1764
1644
  }
1765
1645
  },
1766
1646
 
1767
- matchIp(ip, addresses) {
1768
- let parsed = ipaddr.parse(ip);
1769
- for (let addr of addresses) {
1770
- try {
1771
- let match;
1772
- if (/\/\d+$/.test(addr)) {
1773
- match = parsed.match(ipaddr.parseCIDR(addr));
1774
- } else {
1775
- match = parsed.toNormalizedString() === ipaddr.parse(addr).toNormalizedString();
1776
- }
1777
- if (match) {
1778
- return true;
1779
- }
1780
- } catch (err) {
1781
- logger.error({ msg: 'Failed to parse IP address', ip, addr, err });
1782
- }
1783
- }
1784
-
1785
- return false;
1786
- },
1787
-
1788
1647
  threadStats: {
1789
1648
  startTime: Date.now(),
1790
1649
 
@@ -1806,57 +1665,6 @@ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEV3QUiYsp13nD9suD1/ZkEXnuMoSg
1806
1665
  }
1807
1666
  },
1808
1667
 
1809
- async detectAutomatedRequest(ip) {
1810
- let prefixKey;
1811
- if (net.isIPv4(ip)) {
1812
- prefixKey = 'ipv4Prefix';
1813
- } else if (net.isIPv6(ip)) {
1814
- prefixKey = 'ipv6Prefix';
1815
- } else {
1816
- return false;
1817
- }
1818
-
1819
- const addr = ipaddr.parse(ip);
1820
-
1821
- // Check if it is a Google security scanner
1822
- for (let prefixEntry of googleCrawlerMap.get(prefixKey)) {
1823
- // check if remote address is in range
1824
- if (addr.match(prefixEntry)) {
1825
- return true;
1826
- }
1827
- }
1828
-
1829
- // Check known scanners
1830
- let hostnames;
1831
- try {
1832
- hostnames = await dnsReverse(ip);
1833
- } catch (err) {
1834
- logger.trace({
1835
- msg: 'Failed to reverse resolve IP',
1836
- ip,
1837
- err
1838
- });
1839
- }
1840
-
1841
- if (!hostnames || !hostnames.length) {
1842
- return false;
1843
- }
1844
-
1845
- const hostname = []
1846
- .concat(hostnames || [])
1847
- .shift()
1848
- .toString()
1849
- .trim()
1850
- .toLowerCase();
1851
-
1852
- // Barracuda, spfbl
1853
- if (/\bbarracuda\.com$|\bspfbl\.net$/gi.test(hostname)) {
1854
- return true;
1855
- }
1856
-
1857
- return false;
1858
- },
1859
-
1860
1668
  structuredClone: typeof structuredClone === 'function' ? structuredClone : data => JSON.parse(JSON.stringify(data)),
1861
1669
 
1862
1670
  loadTlsConfig(ref, envPrefix) {