emailengine-app 1.14.0

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 (405) hide show
  1. package/.eslintrc +14 -0
  2. package/.github/CODE_OF_CONDUCT.md +76 -0
  3. package/.github/FUNDING.yml +4 -0
  4. package/.github/ISSUE_TEMPLATE/bug_report.md +38 -0
  5. package/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
  6. package/.github/contributing.md +17 -0
  7. package/.ncurc.js +10 -0
  8. package/.prettierrc.js +8 -0
  9. package/Dockerfile +17 -0
  10. package/Gruntfile.js +16 -0
  11. package/LICENSE.txt +661 -0
  12. package/README.md +524 -0
  13. package/bin/emailengine.js +14 -0
  14. package/config/default.toml +39 -0
  15. package/docker-compose.yml +47 -0
  16. package/encrypt.js +179 -0
  17. package/examples/api.md +137 -0
  18. package/examples/auth-server.js +104 -0
  19. package/getswagger.sh +8 -0
  20. package/lib/account.js +562 -0
  21. package/lib/append-list.js +67 -0
  22. package/lib/bounce-detect.js +380 -0
  23. package/lib/connection.js +1753 -0
  24. package/lib/consts.js +22 -0
  25. package/lib/db.js +72 -0
  26. package/lib/encrypt.js +100 -0
  27. package/lib/enum-message-flags.js +6 -0
  28. package/lib/get-raw-email.js +292 -0
  29. package/lib/get-secret.js +83 -0
  30. package/lib/logger.js +35 -0
  31. package/lib/lua/s-list-accounts.lua +51 -0
  32. package/lib/lua/z-expunge.lua +20 -0
  33. package/lib/lua/z-get-by-uid.lua +16 -0
  34. package/lib/lua/z-get-mailbox-id.lua +15 -0
  35. package/lib/lua/z-get-mailbox-path.lua +4 -0
  36. package/lib/lua/z-get.lua +15 -0
  37. package/lib/lua/z-push.lua +14 -0
  38. package/lib/lua/z-set.lua +17 -0
  39. package/lib/mailbox.js +1545 -0
  40. package/lib/message-port-stream.js +79 -0
  41. package/lib/schemas.js +311 -0
  42. package/lib/settings.js +63 -0
  43. package/lib/tools.js +488 -0
  44. package/package.json +79 -0
  45. package/scan.js +111 -0
  46. package/server.js +672 -0
  47. package/static/bootstrap-4.6.0-dist/css/bootstrap-grid.css +3872 -0
  48. package/static/bootstrap-4.6.0-dist/css/bootstrap-grid.css.map +1 -0
  49. package/static/bootstrap-4.6.0-dist/css/bootstrap-grid.min.css +7 -0
  50. package/static/bootstrap-4.6.0-dist/css/bootstrap-grid.min.css.map +1 -0
  51. package/static/bootstrap-4.6.0-dist/css/bootstrap-reboot.css +325 -0
  52. package/static/bootstrap-4.6.0-dist/css/bootstrap-reboot.css.map +1 -0
  53. package/static/bootstrap-4.6.0-dist/css/bootstrap-reboot.min.css +8 -0
  54. package/static/bootstrap-4.6.0-dist/css/bootstrap-reboot.min.css.map +1 -0
  55. package/static/bootstrap-4.6.0-dist/css/bootstrap.css +10298 -0
  56. package/static/bootstrap-4.6.0-dist/css/bootstrap.css.map +1 -0
  57. package/static/bootstrap-4.6.0-dist/css/bootstrap.min.css +7 -0
  58. package/static/bootstrap-4.6.0-dist/css/bootstrap.min.css.map +1 -0
  59. package/static/bootstrap-4.6.0-dist/js/bootstrap.bundle.js +7045 -0
  60. package/static/bootstrap-4.6.0-dist/js/bootstrap.bundle.js.map +1 -0
  61. package/static/bootstrap-4.6.0-dist/js/bootstrap.bundle.min.js +7 -0
  62. package/static/bootstrap-4.6.0-dist/js/bootstrap.bundle.min.js.map +1 -0
  63. package/static/bootstrap-4.6.0-dist/js/bootstrap.js +4432 -0
  64. package/static/bootstrap-4.6.0-dist/js/bootstrap.js.map +1 -0
  65. package/static/bootstrap-4.6.0-dist/js/bootstrap.min.js +7 -0
  66. package/static/bootstrap-4.6.0-dist/js/bootstrap.min.js.map +1 -0
  67. package/static/css/callout.css +63 -0
  68. package/static/css/emailengine.css +33 -0
  69. package/static/favicon/android-chrome-192x192.png +0 -0
  70. package/static/favicon/android-chrome-512x512.png +0 -0
  71. package/static/favicon/apple-touch-icon.png +0 -0
  72. package/static/favicon/favicon-16x16.png +0 -0
  73. package/static/favicon/favicon-32x32.png +0 -0
  74. package/static/favicon/manifest.json +20 -0
  75. package/static/favicon.ico +0 -0
  76. package/static/icons/alarm-fill.svg +3 -0
  77. package/static/icons/alarm.svg +7 -0
  78. package/static/icons/alert-circle-fill.svg +3 -0
  79. package/static/icons/alert-circle.svg +4 -0
  80. package/static/icons/alert-octagon-fill.svg +3 -0
  81. package/static/icons/alert-octagon.svg +5 -0
  82. package/static/icons/alert-square-fill.svg +3 -0
  83. package/static/icons/alert-square.svg +5 -0
  84. package/static/icons/alert-triangle-fill.svg +3 -0
  85. package/static/icons/alert-triangle.svg +5 -0
  86. package/static/icons/archive-fill.svg +3 -0
  87. package/static/icons/archive.svg +4 -0
  88. package/static/icons/arrow-bar-bottom.svg +4 -0
  89. package/static/icons/arrow-bar-left.svg +4 -0
  90. package/static/icons/arrow-bar-right.svg +4 -0
  91. package/static/icons/arrow-bar-up.svg +4 -0
  92. package/static/icons/arrow-clockwise.svg +4 -0
  93. package/static/icons/arrow-counterclockwise.svg +4 -0
  94. package/static/icons/arrow-down-left.svg +4 -0
  95. package/static/icons/arrow-down-right.svg +4 -0
  96. package/static/icons/arrow-down-short.svg +4 -0
  97. package/static/icons/arrow-down.svg +4 -0
  98. package/static/icons/arrow-left-right.svg +5 -0
  99. package/static/icons/arrow-left-short.svg +4 -0
  100. package/static/icons/arrow-left.svg +4 -0
  101. package/static/icons/arrow-repeat.svg +5 -0
  102. package/static/icons/arrow-right-short.svg +4 -0
  103. package/static/icons/arrow-right.svg +4 -0
  104. package/static/icons/arrow-up-down.svg +5 -0
  105. package/static/icons/arrow-up-left.svg +4 -0
  106. package/static/icons/arrow-up-right.svg +4 -0
  107. package/static/icons/arrow-up-short.svg +4 -0
  108. package/static/icons/arrow-up.svg +4 -0
  109. package/static/icons/arrows-angle-contract.svg +5 -0
  110. package/static/icons/arrows-angle-expand.svg +5 -0
  111. package/static/icons/arrows-collapse.svg +5 -0
  112. package/static/icons/arrows-expand.svg +5 -0
  113. package/static/icons/arrows-fullscreen.svg +7 -0
  114. package/static/icons/at.svg +3 -0
  115. package/static/icons/award.svg +4 -0
  116. package/static/icons/backspace-fill.svg +3 -0
  117. package/static/icons/backspace-reverse-fill.svg +3 -0
  118. package/static/icons/backspace-reverse.svg +5 -0
  119. package/static/icons/backspace.svg +5 -0
  120. package/static/icons/bar-chart-fill.svg +5 -0
  121. package/static/icons/bar-chart.svg +3 -0
  122. package/static/icons/battery-charging.svg +5 -0
  123. package/static/icons/battery-full.svg +4 -0
  124. package/static/icons/battery.svg +4 -0
  125. package/static/icons/bell-fill.svg +3 -0
  126. package/static/icons/bell.svg +4 -0
  127. package/static/icons/blockquote-left.svg +4 -0
  128. package/static/icons/blockquote-right.svg +4 -0
  129. package/static/icons/book-half-fill.svg +4 -0
  130. package/static/icons/book.svg +4 -0
  131. package/static/icons/bookmark-fill.svg +3 -0
  132. package/static/icons/bookmark.svg +3 -0
  133. package/static/icons/bootstrap-fill.svg +3 -0
  134. package/static/icons/bootstrap-reboot.svg +3 -0
  135. package/static/icons/bootstrap.svg +4 -0
  136. package/static/icons/box-arrow-bottom-left.svg +4 -0
  137. package/static/icons/box-arrow-bottom-right.svg +4 -0
  138. package/static/icons/box-arrow-down.svg +5 -0
  139. package/static/icons/box-arrow-left.svg +5 -0
  140. package/static/icons/box-arrow-right.svg +5 -0
  141. package/static/icons/box-arrow-up-left.svg +4 -0
  142. package/static/icons/box-arrow-up-right.svg +4 -0
  143. package/static/icons/box-arrow-up.svg +5 -0
  144. package/static/icons/braces.svg +3 -0
  145. package/static/icons/brightness-fill-high.svg +4 -0
  146. package/static/icons/brightness-fill-low.svg +11 -0
  147. package/static/icons/brightness-high.svg +3 -0
  148. package/static/icons/brightness-low.svg +11 -0
  149. package/static/icons/brush.svg +4 -0
  150. package/static/icons/bucket-fill.svg +4 -0
  151. package/static/icons/bucket.svg +4 -0
  152. package/static/icons/building.svg +5 -0
  153. package/static/icons/bullseye.svg +6 -0
  154. package/static/icons/calendar-fill.svg +4 -0
  155. package/static/icons/calendar.svg +4 -0
  156. package/static/icons/camera-video-fill.svg +4 -0
  157. package/static/icons/camera-video.svg +4 -0
  158. package/static/icons/camera.svg +5 -0
  159. package/static/icons/capslock-fill.svg +3 -0
  160. package/static/icons/capslock.svg +3 -0
  161. package/static/icons/chat-fill.svg +3 -0
  162. package/static/icons/chat.svg +3 -0
  163. package/static/icons/check-box.svg +4 -0
  164. package/static/icons/check-circle.svg +4 -0
  165. package/static/icons/check.svg +3 -0
  166. package/static/icons/chevron-compact-down.svg +3 -0
  167. package/static/icons/chevron-compact-left.svg +3 -0
  168. package/static/icons/chevron-compact-right.svg +3 -0
  169. package/static/icons/chevron-compact-up.svg +3 -0
  170. package/static/icons/chevron-down.svg +3 -0
  171. package/static/icons/chevron-left.svg +3 -0
  172. package/static/icons/chevron-right.svg +3 -0
  173. package/static/icons/chevron-up.svg +3 -0
  174. package/static/icons/circle-fill.svg +3 -0
  175. package/static/icons/circle-half.svg +3 -0
  176. package/static/icons/circle-slash.svg +3 -0
  177. package/static/icons/circle.svg +3 -0
  178. package/static/icons/clock-fill.svg +3 -0
  179. package/static/icons/clock.svg +4 -0
  180. package/static/icons/cloud-download.svg +5 -0
  181. package/static/icons/cloud-fill.svg +3 -0
  182. package/static/icons/cloud-upload.svg +5 -0
  183. package/static/icons/cloud.svg +3 -0
  184. package/static/icons/code-slash.svg +3 -0
  185. package/static/icons/code.svg +3 -0
  186. package/static/icons/columns-gutters.svg +3 -0
  187. package/static/icons/columns.svg +4 -0
  188. package/static/icons/command.svg +4 -0
  189. package/static/icons/compass.svg +5 -0
  190. package/static/icons/cone-striped.svg +4 -0
  191. package/static/icons/cone.svg +4 -0
  192. package/static/icons/controller.svg +5 -0
  193. package/static/icons/credit-card.svg +5 -0
  194. package/static/icons/cursor-fill.svg +3 -0
  195. package/static/icons/cursor.svg +3 -0
  196. package/static/icons/dash.svg +3 -0
  197. package/static/icons/diamond-half.svg +3 -0
  198. package/static/icons/diamond.svg +3 -0
  199. package/static/icons/display-fill.svg +5 -0
  200. package/static/icons/display.svg +4 -0
  201. package/static/icons/document-code.svg +4 -0
  202. package/static/icons/document-diff.svg +5 -0
  203. package/static/icons/document-richtext.svg +4 -0
  204. package/static/icons/document-spreadsheet.svg +5 -0
  205. package/static/icons/document-text.svg +4 -0
  206. package/static/icons/document.svg +3 -0
  207. package/static/icons/documents-alt.svg +4 -0
  208. package/static/icons/documents.svg +4 -0
  209. package/static/icons/dot.svg +3 -0
  210. package/static/icons/download.svg +5 -0
  211. package/static/icons/egg-fried.svg +4 -0
  212. package/static/icons/eject-fill.svg +3 -0
  213. package/static/icons/eject.svg +3 -0
  214. package/static/icons/envelope-fill.svg +3 -0
  215. package/static/icons/envelope-open-fill.svg +3 -0
  216. package/static/icons/envelope-open.svg +5 -0
  217. package/static/icons/envelope.svg +4 -0
  218. package/static/icons/eye-fill.svg +4 -0
  219. package/static/icons/eye-slash-fill.svg +5 -0
  220. package/static/icons/eye-slash.svg +6 -0
  221. package/static/icons/eye.svg +4 -0
  222. package/static/icons/filter.svg +3 -0
  223. package/static/icons/flag-fill.svg +4 -0
  224. package/static/icons/flag.svg +4 -0
  225. package/static/icons/folder-fill.svg +3 -0
  226. package/static/icons/folder-symlink-fill.svg +3 -0
  227. package/static/icons/folder-symlink.svg +5 -0
  228. package/static/icons/folder.svg +4 -0
  229. package/static/icons/fonts.svg +3 -0
  230. package/static/icons/forward-fill.svg +3 -0
  231. package/static/icons/forward.svg +3 -0
  232. package/static/icons/gear-fill.svg +3 -0
  233. package/static/icons/gear-wide-connected.svg +4 -0
  234. package/static/icons/gear-wide.svg +3 -0
  235. package/static/icons/gear.svg +4 -0
  236. package/static/icons/geo.svg +5 -0
  237. package/static/icons/graph-down.svg +5 -0
  238. package/static/icons/graph-up.svg +5 -0
  239. package/static/icons/grid-fill.svg +6 -0
  240. package/static/icons/grid.svg +3 -0
  241. package/static/icons/hammer.svg +4 -0
  242. package/static/icons/hash.svg +3 -0
  243. package/static/icons/heart-fill.svg +3 -0
  244. package/static/icons/heart.svg +3 -0
  245. package/static/icons/house-fill.svg +4 -0
  246. package/static/icons/house.svg +4 -0
  247. package/static/icons/image-alt.svg +4 -0
  248. package/static/icons/image-fill.svg +3 -0
  249. package/static/icons/image.svg +5 -0
  250. package/static/icons/images.svg +5 -0
  251. package/static/icons/inbox-fill.svg +4 -0
  252. package/static/icons/inbox.svg +4 -0
  253. package/static/icons/inboxes-fill.svg +4 -0
  254. package/static/icons/inboxes.svg +4 -0
  255. package/static/icons/info-fill.svg +3 -0
  256. package/static/icons/info-square-fill.svg +3 -0
  257. package/static/icons/info-square.svg +5 -0
  258. package/static/icons/info.svg +5 -0
  259. package/static/icons/justify-left.svg +3 -0
  260. package/static/icons/justify-right.svg +3 -0
  261. package/static/icons/justify.svg +3 -0
  262. package/static/icons/kanban-fill.svg +3 -0
  263. package/static/icons/kanban.svg +6 -0
  264. package/static/icons/laptop.svg +4 -0
  265. package/static/icons/layout-sidebar-reverse.svg +4 -0
  266. package/static/icons/layout-sidebar.svg +4 -0
  267. package/static/icons/layout-split.svg +3 -0
  268. package/static/icons/list-check.svg +3 -0
  269. package/static/icons/list-ol.svg +4 -0
  270. package/static/icons/list-task.svg +5 -0
  271. package/static/icons/list-ul.svg +3 -0
  272. package/static/icons/list.svg +3 -0
  273. package/static/icons/lock-fill.svg +4 -0
  274. package/static/icons/lock.svg +3 -0
  275. package/static/icons/map.svg +3 -0
  276. package/static/icons/mic.svg +4 -0
  277. package/static/icons/moon.svg +3 -0
  278. package/static/icons/music-player-fill.svg +4 -0
  279. package/static/icons/music-player.svg +5 -0
  280. package/static/icons/option.svg +3 -0
  281. package/static/icons/outlet.svg +5 -0
  282. package/static/icons/pause-fill.svg +3 -0
  283. package/static/icons/pause.svg +3 -0
  284. package/static/icons/pen.svg +5 -0
  285. package/static/icons/pencil.svg +4 -0
  286. package/static/icons/people-fill.svg +3 -0
  287. package/static/icons/people.svg +3 -0
  288. package/static/icons/person-fill.svg +3 -0
  289. package/static/icons/person.svg +3 -0
  290. package/static/icons/phone-landscape.svg +4 -0
  291. package/static/icons/phone.svg +4 -0
  292. package/static/icons/pie-chart-fill.svg +3 -0
  293. package/static/icons/pie-chart.svg +4 -0
  294. package/static/icons/play-fill.svg +3 -0
  295. package/static/icons/play.svg +3 -0
  296. package/static/icons/plug.svg +4 -0
  297. package/static/icons/plus.svg +4 -0
  298. package/static/icons/power.svg +4 -0
  299. package/static/icons/question-fill.svg +3 -0
  300. package/static/icons/question-square-fill.svg +3 -0
  301. package/static/icons/question-square.svg +4 -0
  302. package/static/icons/question.svg +4 -0
  303. package/static/icons/reply-all-fill.svg +4 -0
  304. package/static/icons/reply-all.svg +4 -0
  305. package/static/icons/reply-fill.svg +3 -0
  306. package/static/icons/reply.svg +3 -0
  307. package/static/icons/screwdriver.svg +3 -0
  308. package/static/icons/search.svg +4 -0
  309. package/static/icons/shield-fill.svg +3 -0
  310. package/static/icons/shield-lock-fill.svg +3 -0
  311. package/static/icons/shield-lock.svg +5 -0
  312. package/static/icons/shield-shaded.svg +4 -0
  313. package/static/icons/shield.svg +3 -0
  314. package/static/icons/shift-fill.svg +3 -0
  315. package/static/icons/shift.svg +3 -0
  316. package/static/icons/skip-backward-fill.svg +4 -0
  317. package/static/icons/skip-backward.svg +3 -0
  318. package/static/icons/skip-end-fill.svg +5 -0
  319. package/static/icons/skip-end.svg +4 -0
  320. package/static/icons/skip-forward-fill.svg +5 -0
  321. package/static/icons/skip-forward.svg +3 -0
  322. package/static/icons/skip-start-fill.svg +4 -0
  323. package/static/icons/skip-start.svg +4 -0
  324. package/static/icons/speaker.svg +4 -0
  325. package/static/icons/square-fill.svg +3 -0
  326. package/static/icons/square-half.svg +3 -0
  327. package/static/icons/square.svg +3 -0
  328. package/static/icons/star-fill.svg +3 -0
  329. package/static/icons/star-half.svg +3 -0
  330. package/static/icons/star.svg +3 -0
  331. package/static/icons/stop-fill.svg +3 -0
  332. package/static/icons/stop.svg +3 -0
  333. package/static/icons/stopwatch-fill.svg +3 -0
  334. package/static/icons/stopwatch.svg +5 -0
  335. package/static/icons/sun.svg +4 -0
  336. package/static/icons/table.svg +7 -0
  337. package/static/icons/tablet-landscape.svg +4 -0
  338. package/static/icons/tablet.svg +4 -0
  339. package/static/icons/tag-fill.svg +3 -0
  340. package/static/icons/tag.svg +4 -0
  341. package/static/icons/terminal-fill.svg +3 -0
  342. package/static/icons/terminal.svg +4 -0
  343. package/static/icons/text-center.svg +3 -0
  344. package/static/icons/text-indent-left.svg +3 -0
  345. package/static/icons/text-indent-right.svg +3 -0
  346. package/static/icons/text-left.svg +3 -0
  347. package/static/icons/text-right.svg +3 -0
  348. package/static/icons/three-dots-vertical.svg +3 -0
  349. package/static/icons/three-dots.svg +3 -0
  350. package/static/icons/toggle-off.svg +3 -0
  351. package/static/icons/toggle-on.svg +3 -0
  352. package/static/icons/toggles.svg +4 -0
  353. package/static/icons/tools.svg +4 -0
  354. package/static/icons/trash-fill.svg +3 -0
  355. package/static/icons/trash.svg +4 -0
  356. package/static/icons/triangle-fill.svg +3 -0
  357. package/static/icons/triangle-half.svg +3 -0
  358. package/static/icons/triangle.svg +3 -0
  359. package/static/icons/trophy.svg +6 -0
  360. package/static/icons/tv-fill.svg +3 -0
  361. package/static/icons/tv.svg +3 -0
  362. package/static/icons/type-bold.svg +3 -0
  363. package/static/icons/type-h1.svg +3 -0
  364. package/static/icons/type-h2.svg +3 -0
  365. package/static/icons/type-h3.svg +3 -0
  366. package/static/icons/type-italic.svg +3 -0
  367. package/static/icons/type-strikethrough.svg +4 -0
  368. package/static/icons/type-underline.svg +4 -0
  369. package/static/icons/type.svg +3 -0
  370. package/static/icons/unlock-fill.svg +4 -0
  371. package/static/icons/unlock.svg +3 -0
  372. package/static/icons/upload.svg +4 -0
  373. package/static/icons/volume-down-fill.svg +4 -0
  374. package/static/icons/volume-down.svg +4 -0
  375. package/static/icons/volume-mute-fill.svg +4 -0
  376. package/static/icons/volume-mute.svg +4 -0
  377. package/static/icons/volume-up-fill.svg +6 -0
  378. package/static/icons/volume-up.svg +6 -0
  379. package/static/icons/wallet.svg +3 -0
  380. package/static/icons/watch.svg +5 -0
  381. package/static/icons/wifi.svg +5 -0
  382. package/static/icons/window.svg +5 -0
  383. package/static/icons/wrench.svg +3 -0
  384. package/static/icons/x-circle-fill.svg +3 -0
  385. package/static/icons/x-circle.svg +5 -0
  386. package/static/icons/x-octagon-fill.svg +3 -0
  387. package/static/icons/x-octagon.svg +4 -0
  388. package/static/icons/x-square-fill.svg +3 -0
  389. package/static/icons/x-square.svg +4 -0
  390. package/static/icons/x.svg +4 -0
  391. package/static/index.html +752 -0
  392. package/static/js/emailengine.js +581 -0
  393. package/static/js/jquery-3.4.1.slim.min.js +2 -0
  394. package/static/js/moment-with-locales-2.24.0.min.js +1 -0
  395. package/static/js/popper.min.js +5 -0
  396. package/static/logo.png +0 -0
  397. package/systemd/emailengine.service +89 -0
  398. package/systemd/nginx-proxy.conf +77 -0
  399. package/views/error.hbs +2 -0
  400. package/workers/api.js +2266 -0
  401. package/workers/arena.js +89 -0
  402. package/workers/imap.js +611 -0
  403. package/workers/smtp.js +278 -0
  404. package/workers/submit.js +214 -0
  405. package/workers/webhooks.js +134 -0
@@ -0,0 +1,89 @@
1
+ 'use strict';
2
+
3
+ const config = require('wild-config');
4
+
5
+ const arena = require('bull-arena');
6
+ const Bull = require('bull');
7
+ const express = require('express');
8
+ const logger = require('../lib/logger');
9
+ const packageData = require('../package.json');
10
+
11
+ const app = express();
12
+ const router = new express.Router();
13
+
14
+ config.dbs = config.dbs || {
15
+ redis: 'redis://127.0.0.1:6379/8'
16
+ };
17
+
18
+ config.arena = config.arena || {
19
+ enabled: false,
20
+ port: 3001,
21
+ host: '127.0.0.1'
22
+ };
23
+
24
+ const REDIS_CONF_DEFAULT = process.env.EENGINE_REDIS || config.dbs.redis;
25
+
26
+ const REDIS_CONF =
27
+ typeof REDIS_CONF_DEFAULT === 'string'
28
+ ? {
29
+ url: REDIS_CONF_DEFAULT
30
+ }
31
+ : REDIS_CONF_DEFAULT;
32
+
33
+ const ARENA_PORT = (process.env.EENGINE_ARENA_PORT && Number(process.env.EENGINE_ARENA_PORT)) || config.arena.port || 3001;
34
+ const ARENA_HOST = process.env.EENGINE_ARENA_HOST || config.arena.host || '127.0.0.1';
35
+
36
+ const bulUi = arena(
37
+ {
38
+ Bull,
39
+ queues: [
40
+ {
41
+ name: 'submit',
42
+ hostId: 'EmailEngine',
43
+ redis: REDIS_CONF
44
+ },
45
+ {
46
+ name: 'notify',
47
+ hostId: 'EmailEngine',
48
+ redis: REDIS_CONF
49
+ }
50
+ ]
51
+ },
52
+ {
53
+ basePath: '/',
54
+ disableListen: true
55
+ }
56
+ );
57
+
58
+ let init = async () => {
59
+ router.use('/', bulUi);
60
+
61
+ app.use(router);
62
+
63
+ return await new Promise((resolve, reject) => {
64
+ app.once('error', err => reject(err));
65
+ app.listen(ARENA_PORT, ARENA_HOST, () => {
66
+ app.on('error', err => {
67
+ logger.error({
68
+ msg: 'SMTP Server Error',
69
+ err
70
+ });
71
+ });
72
+ resolve();
73
+ });
74
+ });
75
+ };
76
+
77
+ init()
78
+ .then(() => {
79
+ logger.debug({
80
+ msg: 'Started Bull Arena server thread',
81
+ port: ARENA_PORT,
82
+ host: ARENA_HOST,
83
+ version: packageData.version
84
+ });
85
+ })
86
+ .catch(err => {
87
+ logger.error(err);
88
+ setImmediate(() => process.exit(3));
89
+ });
@@ -0,0 +1,611 @@
1
+ 'use strict';
2
+ const { parentPort } = require('worker_threads');
3
+ const { Connection } = require('../lib/connection');
4
+ const { Account } = require('../lib/account');
5
+ const logger = require('../lib/logger');
6
+ const { redis, notifyQueue, submitQueue } = require('../lib/db');
7
+ const { MessagePortWritable } = require('../lib/message-port-stream');
8
+ const settings = require('../lib/settings');
9
+ const msgpack = require('msgpack5')();
10
+ const packageData = require('../package.json');
11
+
12
+ const config = require('wild-config');
13
+ const net = require('net');
14
+
15
+ const { getDuration } = require('../lib/tools');
16
+ const getSecret = require('../lib/get-secret');
17
+
18
+ config.service = config.service || {};
19
+
20
+ const DEFAULT_EENGINE_TIMEOUT = 10 * 1000;
21
+
22
+ const EENGINE_TIMEOUT = getDuration(process.env.EENGINE_TIMEOUT || config.service.commandTimeout) || DEFAULT_EENGINE_TIMEOUT;
23
+
24
+ const EENGINE_ADDRESSES = []
25
+ .concat(process.env.EENGINE_ADDRESSES || config.service.localAddresses || [])
26
+ .flatMap(addr => {
27
+ if (Array.isArray(addr)) {
28
+ return addr;
29
+ }
30
+
31
+ if (typeof addr === 'object' && addr && typeof addr.address === 'string') {
32
+ return addr;
33
+ }
34
+
35
+ if (typeof addr !== 'string') {
36
+ return false;
37
+ }
38
+
39
+ return addr.split(/[,;]+/).map(part => part.trim());
40
+ })
41
+ .filter(addr => addr)
42
+ .map(addr => {
43
+ if (typeof addr === 'object') {
44
+ return addr;
45
+ }
46
+ let [address, name] = addr.split('|').map(part => part.trim());
47
+ return { address, name };
48
+ })
49
+ .filter(addr => addr && addr.address && net.isIP(addr.address));
50
+
51
+ const DEFAULT_STATES = {
52
+ init: 0,
53
+ connected: 0,
54
+ connecting: 0,
55
+ authenticationError: 0,
56
+ connectError: 0,
57
+ unset: 0,
58
+ disconnected: 0
59
+ };
60
+
61
+ const NO_ACTIVE_HANDLER_RESP = {
62
+ error: 'No active handler for requested account. Try again later.',
63
+ statusCode: 503
64
+ };
65
+
66
+ class ConnectionHandler {
67
+ constructor() {
68
+ this.callQueue = new Map();
69
+ this.mids = 0;
70
+
71
+ this.accounts = new Map();
72
+ }
73
+
74
+ async init() {
75
+ // indicate that we are ready to process connections
76
+ parentPort.postMessage({ cmd: 'ready' });
77
+ }
78
+
79
+ getLogKey(account) {
80
+ // this format ensures that the key is deleted when user is removed
81
+ return `iam:${account}:g`;
82
+ }
83
+
84
+ async getAccountLogger(account) {
85
+ let logKey = this.getLogKey(account);
86
+ let logging = await settings.getLoggingInfo(account);
87
+
88
+ return {
89
+ enabled: logging.enabled,
90
+ maxLogLines: logging.maxLogLines,
91
+ log(entry) {
92
+ if (!this.maxLogLines || !this.enabled) {
93
+ return;
94
+ }
95
+
96
+ let logRow = msgpack.encode(entry);
97
+ redis
98
+ .multi()
99
+ .rpush(logKey, logRow)
100
+ .ltrim(logKey, -this.maxLogLines, -1)
101
+ .exec()
102
+ .catch(err => this.logger.error(err));
103
+ }
104
+ };
105
+ }
106
+
107
+ async assignConnection(account) {
108
+ logger.info({ msg: 'Assigned account to worker', account });
109
+
110
+ let accountObject = new Account({ redis, account, secret: await getSecret() });
111
+
112
+ this.accounts.set(account, accountObject);
113
+ accountObject.connection = new Connection({
114
+ account,
115
+ accountObject,
116
+ redis,
117
+ notifyQueue,
118
+ submitQueue,
119
+ accountLogger: await this.getAccountLogger(account),
120
+ localAddresses: EENGINE_ADDRESSES
121
+ });
122
+ accountObject.logger = accountObject.connection.logger;
123
+
124
+ let accountData = await accountObject.loadAccountData();
125
+
126
+ if (accountData.state) {
127
+ await redis.hset(accountObject.connection.getAccountKey(), 'state', accountData.state);
128
+ }
129
+
130
+ // do not wait before returning as it may take forever
131
+ accountObject.connection.init().catch(err => {
132
+ logger.error({ account, err });
133
+ });
134
+ }
135
+
136
+ async deleteConnection(account) {
137
+ logger.info({ msg: 'Deleting connection', account });
138
+ if (this.accounts.has(account)) {
139
+ let accountObject = this.accounts.get(account);
140
+ if (accountObject.connection) {
141
+ await accountObject.connection.delete();
142
+ }
143
+ this.accounts.delete(account);
144
+ }
145
+ }
146
+
147
+ async updateConnection(account) {
148
+ logger.info({ msg: 'Account reconnect requested', account });
149
+ if (this.accounts.has(account)) {
150
+ let accountObject = this.accounts.get(account);
151
+ if (accountObject.connection) {
152
+ accountObject.connection.accountLogger.log({
153
+ level: 'info',
154
+ t: Date.now(),
155
+ cid: accountObject.connection.cid,
156
+ msg: 'Account reconnect requested'
157
+ });
158
+ await redis.hmset(accountObject.connection.getAccountKey(), {
159
+ state: 'connecting'
160
+ });
161
+ await accountObject.connection.reconnect(true);
162
+ }
163
+ }
164
+ }
165
+
166
+ async listMessages(message) {
167
+ if (!this.accounts.has(message.account)) {
168
+ return NO_ACTIVE_HANDLER_RESP;
169
+ }
170
+
171
+ let accountData = this.accounts.get(message.account);
172
+ if (!accountData.connection) {
173
+ return NO_ACTIVE_HANDLER_RESP;
174
+ }
175
+
176
+ return await accountData.connection.listMessages(message);
177
+ }
178
+
179
+ async buildContacts(message) {
180
+ if (!this.accounts.has(message.account)) {
181
+ return NO_ACTIVE_HANDLER_RESP;
182
+ }
183
+
184
+ let accountData = this.accounts.get(message.account);
185
+ if (!accountData.connection) {
186
+ return NO_ACTIVE_HANDLER_RESP;
187
+ }
188
+
189
+ return await accountData.connection.buildContacts(message);
190
+ }
191
+
192
+ async getText(message) {
193
+ if (!this.accounts.has(message.account)) {
194
+ return NO_ACTIVE_HANDLER_RESP;
195
+ }
196
+
197
+ let accountData = this.accounts.get(message.account);
198
+ if (!accountData.connection) {
199
+ return NO_ACTIVE_HANDLER_RESP;
200
+ }
201
+
202
+ return await accountData.connection.getText(message.text, message.options);
203
+ }
204
+
205
+ async getMessage(message) {
206
+ if (!this.accounts.has(message.account)) {
207
+ return NO_ACTIVE_HANDLER_RESP;
208
+ }
209
+
210
+ let accountData = this.accounts.get(message.account);
211
+ if (!accountData.connection) {
212
+ return NO_ACTIVE_HANDLER_RESP;
213
+ }
214
+
215
+ return await accountData.connection.getMessage(message.message, message.options);
216
+ }
217
+
218
+ async updateMessage(message) {
219
+ if (!this.accounts.has(message.account)) {
220
+ return NO_ACTIVE_HANDLER_RESP;
221
+ }
222
+
223
+ let accountData = this.accounts.get(message.account);
224
+ if (!accountData.connection) {
225
+ return NO_ACTIVE_HANDLER_RESP;
226
+ }
227
+
228
+ return await accountData.connection.updateMessage(message.message, message.updates);
229
+ }
230
+
231
+ async moveMessage(message) {
232
+ if (!this.accounts.has(message.account)) {
233
+ return NO_ACTIVE_HANDLER_RESP;
234
+ }
235
+
236
+ let accountData = this.accounts.get(message.account);
237
+ if (!accountData.connection) {
238
+ return NO_ACTIVE_HANDLER_RESP;
239
+ }
240
+
241
+ return await accountData.connection.moveMessage(message.message, message.target);
242
+ }
243
+
244
+ async deleteMessage(message) {
245
+ if (!this.accounts.has(message.account)) {
246
+ return NO_ACTIVE_HANDLER_RESP;
247
+ }
248
+
249
+ let accountData = this.accounts.get(message.account);
250
+ if (!accountData.connection) {
251
+ return NO_ACTIVE_HANDLER_RESP;
252
+ }
253
+
254
+ return await accountData.connection.deleteMessage(message.message);
255
+ }
256
+
257
+ async submitMessage(message) {
258
+ if (!this.accounts.has(message.account)) {
259
+ return NO_ACTIVE_HANDLER_RESP;
260
+ }
261
+
262
+ let accountData = this.accounts.get(message.account);
263
+ if (!accountData.connection) {
264
+ return NO_ACTIVE_HANDLER_RESP;
265
+ }
266
+
267
+ return await accountData.connection.submitMessage(message.data);
268
+ }
269
+
270
+ async queueMessage(message) {
271
+ if (!this.accounts.has(message.account)) {
272
+ return NO_ACTIVE_HANDLER_RESP;
273
+ }
274
+
275
+ let accountData = this.accounts.get(message.account);
276
+ if (!accountData.connection) {
277
+ return NO_ACTIVE_HANDLER_RESP;
278
+ }
279
+
280
+ return await accountData.connection.queueMessage(message.data);
281
+ }
282
+
283
+ async uploadMessage(message) {
284
+ if (!this.accounts.has(message.account)) {
285
+ return NO_ACTIVE_HANDLER_RESP;
286
+ }
287
+
288
+ let accountData = this.accounts.get(message.account);
289
+ if (!accountData.connection) {
290
+ return NO_ACTIVE_HANDLER_RESP;
291
+ }
292
+
293
+ return await accountData.connection.uploadMessage(message.data);
294
+ }
295
+
296
+ async createMailbox(message) {
297
+ if (!this.accounts.has(message.account)) {
298
+ return NO_ACTIVE_HANDLER_RESP;
299
+ }
300
+
301
+ let accountData = this.accounts.get(message.account);
302
+ if (!accountData.connection) {
303
+ return NO_ACTIVE_HANDLER_RESP;
304
+ }
305
+
306
+ return await accountData.connection.createMailbox(message.path);
307
+ }
308
+
309
+ async deleteMailbox(message) {
310
+ if (!this.accounts.has(message.account)) {
311
+ return NO_ACTIVE_HANDLER_RESP;
312
+ }
313
+
314
+ let accountData = this.accounts.get(message.account);
315
+ if (!accountData.connection) {
316
+ return NO_ACTIVE_HANDLER_RESP;
317
+ }
318
+ return await accountData.connection.deleteMailbox(message.path);
319
+ }
320
+
321
+ async getRawMessage(message) {
322
+ if (!this.accounts.has(message.account)) {
323
+ return NO_ACTIVE_HANDLER_RESP;
324
+ }
325
+
326
+ let accountData = this.accounts.get(message.account);
327
+ if (!accountData.connection) {
328
+ return NO_ACTIVE_HANDLER_RESP;
329
+ }
330
+ let stream = new MessagePortWritable(message.port);
331
+
332
+ let source = await accountData.connection.getRawMessage(message.message);
333
+ if (!source) {
334
+ let err = new Error('Requested file not found');
335
+ err.statusCode = 404;
336
+ throw err;
337
+ }
338
+
339
+ setImmediate(() => {
340
+ source.pipe(stream);
341
+ });
342
+
343
+ return {
344
+ headers: source.headers,
345
+ contentType: source.contentType
346
+ };
347
+ }
348
+
349
+ async getAttachment(message) {
350
+ if (!this.accounts.has(message.account)) {
351
+ return NO_ACTIVE_HANDLER_RESP;
352
+ }
353
+
354
+ let accountData = this.accounts.get(message.account);
355
+ if (!accountData.connection) {
356
+ return NO_ACTIVE_HANDLER_RESP;
357
+ }
358
+
359
+ let stream = new MessagePortWritable(message.port);
360
+
361
+ let source = await accountData.connection.getAttachment(message.attachment);
362
+ if (!source) {
363
+ let err = new Error('Requested file not found');
364
+ err.statusCode = 404;
365
+ throw err;
366
+ }
367
+
368
+ setImmediate(() => {
369
+ source.pipe(stream);
370
+ });
371
+
372
+ return {
373
+ headers: source.headers,
374
+ contentType: source.contentType
375
+ };
376
+ }
377
+
378
+ async kill() {
379
+ if (this.killed) {
380
+ return;
381
+ }
382
+ logger.error({ msg: 'Terminating process' });
383
+ this.killed = true;
384
+
385
+ this.accounts.forEach(account => {
386
+ if (account.connection) {
387
+ account.connection.close();
388
+ }
389
+ });
390
+
391
+ process.exit(0);
392
+ }
393
+
394
+ // some general message
395
+ async onMessage(message) {
396
+ /*
397
+ let dataview = new DataView(message);
398
+ dataview.setUint8(Number(threadId), Number(threadId));
399
+ */
400
+
401
+ switch (message.cmd) {
402
+ case 'settings':
403
+ if (message.data && message.data.logs) {
404
+ for (let [account, accountObject] of this.accounts) {
405
+ // update log handling
406
+ let logging = await settings.getLoggingInfo(account, message.data.logs);
407
+ if (accountObject.connection) {
408
+ accountObject.connection.accountLogger.maxLogLines = logging.maxLogLines;
409
+ accountObject.connection.accountLogger.enabled = logging.enabled;
410
+ accountObject.connection.emitLogs = logging.enabled;
411
+ if (accountObject.connection.imapClient) {
412
+ accountObject.connection.imapClient.emitLogs = logging.enabled;
413
+ }
414
+ }
415
+ if (!logging.enabled) {
416
+ await redis.del(this.getLogKey(account));
417
+ }
418
+ }
419
+ }
420
+ return;
421
+ }
422
+
423
+ logger.debug({ msg: 'Unhandled message', message });
424
+ }
425
+
426
+ // message that expects a response
427
+ async onCommand(message) {
428
+ switch (message.cmd) {
429
+ case 'assign':
430
+ return await this.assignConnection(message.account);
431
+
432
+ case 'delete':
433
+ return await this.deleteConnection(message.account);
434
+
435
+ case 'update':
436
+ return await this.updateConnection(message.account);
437
+
438
+ case 'listMessages':
439
+ return await this.listMessages(message);
440
+
441
+ case 'buildContacts':
442
+ return await this.buildContacts(message);
443
+
444
+ case 'getText':
445
+ return await this.getText(message);
446
+
447
+ case 'getMessage':
448
+ return await this.getMessage(message);
449
+
450
+ case 'updateMessage':
451
+ return await this.updateMessage(message);
452
+
453
+ case 'moveMessage':
454
+ return await this.moveMessage(message);
455
+
456
+ case 'deleteMessage':
457
+ return await this.deleteMessage(message);
458
+
459
+ case 'getRawMessage':
460
+ return await this.getRawMessage(message);
461
+
462
+ case 'createMailbox':
463
+ return await this.createMailbox(message);
464
+
465
+ case 'deleteMailbox':
466
+ return await this.deleteMailbox(message);
467
+
468
+ case 'getAttachment':
469
+ return await this.getAttachment(message);
470
+
471
+ case 'submitMessage':
472
+ return await this.submitMessage(message);
473
+
474
+ case 'queueMessage':
475
+ return await this.queueMessage(message);
476
+
477
+ case 'uploadMessage':
478
+ return await this.uploadMessage(message);
479
+
480
+ case 'countConnections': {
481
+ let results = Object.assign({}, DEFAULT_STATES);
482
+
483
+ let count = status => {
484
+ if (!results[status]) {
485
+ results[status] = 0;
486
+ }
487
+ results[status] += 1;
488
+ };
489
+
490
+ this.accounts.forEach(accountObject => {
491
+ let state;
492
+
493
+ if (!accountObject || !accountObject.connection) {
494
+ state = 'unassigned';
495
+ } else {
496
+ state = accountObject.connection.currentState();
497
+ }
498
+
499
+ return count(state);
500
+ });
501
+
502
+ return results;
503
+ }
504
+
505
+ default:
506
+ return false;
507
+ }
508
+ }
509
+
510
+ async call(message) {
511
+ return new Promise((resolve, reject) => {
512
+ let mid = `${Date.now()}:${++this.mids}`;
513
+
514
+ let timer = setTimeout(() => {
515
+ let err = new Error('Timeout waiting for command response');
516
+ err.statusCode = 504;
517
+ err.code = 'Timeout';
518
+ reject(err);
519
+ }, message.timeout || EENGINE_TIMEOUT);
520
+
521
+ this.callQueue.set(mid, { resolve, reject, timer });
522
+ parentPort.postMessage({
523
+ cmd: 'call',
524
+ mid,
525
+ message
526
+ });
527
+ });
528
+ }
529
+
530
+ metrics(key, method, ...args) {
531
+ parentPort.postMessage({
532
+ cmd: 'metrics',
533
+ key,
534
+ method,
535
+ args
536
+ });
537
+ }
538
+ }
539
+
540
+ let connectionHandler = new ConnectionHandler();
541
+
542
+ async function main() {
543
+ logger.info({ msg: 'Started IMAP worker thread', version: packageData.version });
544
+ await connectionHandler.init();
545
+ }
546
+
547
+ parentPort.on('message', message => {
548
+ if (message && message.cmd === 'resp' && message.mid && connectionHandler.callQueue.has(message.mid)) {
549
+ let { resolve, reject, timer } = connectionHandler.callQueue.get(message.mid);
550
+ clearTimeout(timer);
551
+ connectionHandler.callQueue.delete(message.mid);
552
+ if (message.error) {
553
+ let err = new Error(message.error);
554
+ if (message.code) {
555
+ err.code = message.code;
556
+ }
557
+ if (message.statusCode) {
558
+ err.statusCode = message.statusCode;
559
+ }
560
+ return reject(err);
561
+ } else {
562
+ return resolve(message.response);
563
+ }
564
+ }
565
+
566
+ if (message && message.cmd === 'call' && message.mid) {
567
+ return connectionHandler
568
+ .onCommand(message.message)
569
+ .then(response => {
570
+ parentPort.postMessage({
571
+ cmd: 'resp',
572
+ mid: message.mid,
573
+ response
574
+ });
575
+ })
576
+ .catch(err => {
577
+ if (message.message && message.message.data && message.message.data.raw) {
578
+ message.message.data.raw = message.message.data.raw.length;
579
+ }
580
+ logger.error(Object.assign({ msg: 'Command failed' }, message, { err }));
581
+ parentPort.postMessage({
582
+ cmd: 'resp',
583
+ mid: message.mid,
584
+ error: err.message,
585
+ code: err.code,
586
+ statusCode: err.statusCode
587
+ });
588
+ });
589
+ }
590
+
591
+ connectionHandler.onMessage(message).catch(err => logger.error(err));
592
+ });
593
+
594
+ process.on('SIGTERM', () => {
595
+ connectionHandler.kill().catch(err => {
596
+ logger.error({ msg: 'Execution failed', err });
597
+ process.exit(4);
598
+ });
599
+ });
600
+
601
+ process.on('SIGINT', () => {
602
+ connectionHandler.kill().catch(err => {
603
+ logger.error({ msg: 'Execution failed', err });
604
+ process.exit(5);
605
+ });
606
+ });
607
+
608
+ main().catch(err => {
609
+ logger.error({ msg: 'Execution failed', err });
610
+ setImmediate(() => process.exit(6));
611
+ });