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,79 @@
1
+ 'use strict';
2
+
3
+ const { Writable, Readable } = require('stream');
4
+
5
+ // const { MessageChannel } = require('worker_threads');
6
+ // const { port1, port2 } = new MessageChannel();
7
+
8
+ class MessagePortWritable extends Writable {
9
+ constructor(messagePort) {
10
+ super();
11
+ this.messagePort = messagePort;
12
+ }
13
+
14
+ _write(chunk, encoding, done) {
15
+ if (!chunk || !chunk.length) {
16
+ return done();
17
+ }
18
+
19
+ if (typeof chunk === 'string') {
20
+ chunk = Buffer.from(chunk, encoding);
21
+ }
22
+
23
+ this.messagePort.postMessage({
24
+ value: chunk,
25
+ done: false
26
+ });
27
+ done();
28
+ }
29
+
30
+ _final(done) {
31
+ this.messagePort.postMessage({
32
+ done: true
33
+ });
34
+ try {
35
+ this.messagePort.close();
36
+ } catch (err) {
37
+ //ignore
38
+ }
39
+ done();
40
+ }
41
+ }
42
+
43
+ class MessagePortReadable extends Readable {
44
+ constructor(messagePort) {
45
+ super();
46
+ this.messagePort = messagePort;
47
+
48
+ this.canRead = false;
49
+
50
+ this.readableQueue = [];
51
+ this.messagePort.on('message', message => {
52
+ if (message && (message.done || message.value)) {
53
+ this.readableQueue.push(message);
54
+
55
+ if (this.canRead && this.readableQueue.length === 1) {
56
+ this._processReading();
57
+ }
58
+ }
59
+ });
60
+ }
61
+
62
+ _processReading() {
63
+ while (this.canRead && this.readableQueue.length) {
64
+ let message = this.readableQueue.shift();
65
+ if (message.done) {
66
+ return this.push(null);
67
+ }
68
+ this.canRead = this.push(Buffer.from(message.value));
69
+ }
70
+ }
71
+
72
+ _read() {
73
+ this.canRead = true;
74
+ this._processReading();
75
+ }
76
+ }
77
+
78
+ module.exports.MessagePortWritable = MessagePortWritable;
79
+ module.exports.MessagePortReadable = MessagePortReadable;
package/lib/schemas.js ADDED
@@ -0,0 +1,311 @@
1
+ 'use strict';
2
+
3
+ const Joi = require('joi');
4
+
5
+ const RESYNC_DELAY = 15 * 60;
6
+
7
+ // allowed configuration keys
8
+ const settingsSchema = {
9
+ webhooks: Joi.string()
10
+ .uri({
11
+ scheme: ['http', 'https'],
12
+ allowRelative: false
13
+ })
14
+ .allow('')
15
+ .example('https://myservice.com/imap/webhooks')
16
+ .description('Webhook URL'),
17
+
18
+ webhookEvents: Joi.array().items(Joi.string().max(256).example('messageNew')),
19
+
20
+ notifyHeaders: Joi.array().items(Joi.string().max(256).example('List-ID')),
21
+
22
+ authServer: Joi.string()
23
+ .uri({
24
+ scheme: ['http', 'https'],
25
+ allowRelative: false
26
+ })
27
+ .allow('')
28
+ .example('https://myservice.com/authentication')
29
+ .description('URL to fetch authentication data from')
30
+ .label('AuthServer'),
31
+
32
+ notifyText: Joi.boolean().truthy('Y', 'true', '1').falsy('N', 'false', 0).description('Include message text in webhook notification'),
33
+
34
+ notifyTextSize: Joi.number().min(0),
35
+
36
+ gmailClientId: Joi.string().allow('').max(256).description('Gmail OAuth2 Client ID'),
37
+ gmailClientSecret: Joi.string().empty('').max(256).description('Gmail OAuth2 Client Secret'),
38
+ gmailRedirectUrl: Joi.string()
39
+ .allow('')
40
+ .uri({ scheme: ['http', 'https'], allowRelative: false })
41
+ .description('Gmail OAuth2 Callback URL'),
42
+
43
+ logs: Joi.object({
44
+ all: Joi.boolean().truthy('Y', 'true', '1').falsy('N', 'false', 0).default(false).description('Enable logs for all accounts'),
45
+ resetLoggedAccounts: Joi.boolean().truthy('Y', 'true', '1').falsy('N', 'false', 0).default(false).description('Reconnect logged accounts'),
46
+ accounts: Joi.array()
47
+ .items(Joi.string().max(256))
48
+ .default([])
49
+ .example(['account-id-1', 'account-id-2'])
50
+ .description('Enable logs for listed accounts')
51
+ .label('LoggedAccounts'),
52
+ maxLogLines: Joi.number().min(0).max(1000000).default(10000)
53
+ }).label('LogSettings')
54
+ };
55
+
56
+ const addressSchema = Joi.object({
57
+ name: Joi.string().max(256).example('Some Name'),
58
+ address: Joi.string()
59
+ .email({
60
+ ignoreLength: false
61
+ })
62
+ .example('user@example.com')
63
+ .required()
64
+ }).label('Address');
65
+
66
+ // generate a list of boolean values
67
+ const settingsQuerySchema = Object.fromEntries(
68
+ Object.keys(Object.assign({ eventTypes: true }, settingsSchema)).map(key => [
69
+ key,
70
+ Joi.boolean().truthy('Y', 'true', '1').falsy('N', 'false', 0).default(false)
71
+ ])
72
+ );
73
+
74
+ const imapSchema = {
75
+ auth: Joi.object({
76
+ user: Joi.string().max(256).required().example('myuser@gmail.com').description('Account username'),
77
+ pass: Joi.string().max(256).example('verysecret').description('Account password'),
78
+ accessToken: Joi.string().max(4096).description('Access Token for OAuth2')
79
+ })
80
+ .xor('pass', 'accessToken')
81
+ .description('Authentication info')
82
+ .label('Authentication'),
83
+
84
+ useAuthServer: Joi.boolean().example(false).description('Set to true to use authentication server instead of username/password'),
85
+
86
+ host: Joi.string().hostname().required().example('imap.gmail.com').description('Hostname to connect to'),
87
+ port: Joi.number()
88
+ .min(1)
89
+ .max(64 * 1024)
90
+ .required()
91
+ .example(993)
92
+ .description('Service port number'),
93
+ secure: Joi.boolean().default(false).example(true).description('Should connection use TLS. Usually true for port 993'),
94
+ tls: Joi.object({
95
+ rejectUnauthorized: Joi.boolean().default(true).example(true).description('How to treat invalid certificates'),
96
+ minVersion: Joi.string().max(256).example('TLSv1.2').description('Minimal TLS version')
97
+ })
98
+ .description('Optional TLS configuration')
99
+ .label('TLS'),
100
+ resyncDelay: Joi.number().example(RESYNC_DELAY).description('Full resync delay in seconds').default(RESYNC_DELAY)
101
+ };
102
+
103
+ const smtpSchema = {
104
+ auth: Joi.object({
105
+ user: Joi.string().max(256).required().example('myuser@gmail.com').description('Account username'),
106
+ pass: Joi.string().max(256).example('verysecret').description('Account password'),
107
+ accessToken: Joi.string().max(4096).description('Access Token for OAuth2')
108
+ })
109
+ .xor('pass', 'accessToken')
110
+ .description('Authentication info')
111
+ .label('Authentication'),
112
+
113
+ useAuthServer: Joi.boolean().example(false).description('Set to true to use authentication server instead of username/password'),
114
+
115
+ host: Joi.string().hostname().required().example('smtp.gmail.com').description('Hostname to connect to'),
116
+ port: Joi.number()
117
+ .min(1)
118
+ .max(64 * 1024)
119
+ .required()
120
+ .example(587)
121
+ .description('Service port number'),
122
+ secure: Joi.boolean().default(false).example(false).description('Should connection use TLS. Usually true for port 465'),
123
+ tls: Joi.object({
124
+ rejectUnauthorized: Joi.boolean().default(true).example(true).description('How to treat invalid certificates'),
125
+ minVersion: Joi.string().max(256).example('TLSv1.2').description('Minimal TLS version')
126
+ })
127
+ .description('Optional TLS configuration')
128
+ .label('TLS')
129
+ };
130
+
131
+ const oauth2Schema = Joi.object({
132
+ authorize: Joi.boolean().example(true).description('Return a redirect link to the OAuth2 consent screen'),
133
+ provider: Joi.string().default('gmail').valid('gmail').description('OAuth provider'),
134
+ auth: Joi.object({
135
+ user: Joi.string().max(256).required().example('myuser@gmail.com').description('Account username')
136
+ }),
137
+ accessToken: Joi.string().max(1024).example('ya29.a0ARrdaM8a...'),
138
+ refreshToken: Joi.string().max(1024).example('1//09Ie3CtORQYm...'),
139
+ expires: Joi.date().iso().example('2021-03-22T13:13:31.000Z').description('Token expiration date')
140
+ })
141
+ .xor('authorize', 'auth')
142
+ .label('OAuth2Auth');
143
+
144
+ const attachmentSchema = Joi.object({
145
+ id: Joi.string().example('AAAAAgAACrIyLjI').description('Attachment ID').label('AttachmentId'),
146
+ contentType: Joi.string().example('image/gif').description('Mime type of the attachment'),
147
+ encodedSize: Joi.number().example(48).description('Encoded size of the attachment. Actual file size is usually smaller depending on the encoding'),
148
+ embedded: Joi.boolean().example(true).description('Is this image used in HTML img tag'),
149
+ inline: Joi.boolean().example(true).description('Should this file be included in the message preview somehow'),
150
+ contentId: Joi.string().example('<unique-image-id@localhost>').description('Usually used only for embedded images')
151
+ });
152
+
153
+ const messageEntrySchema = Joi.object({
154
+ id: Joi.string().example('AAAAAgAACrI').description('Message ID').label('MessageEntryId'),
155
+ uid: Joi.number().example(12345).description('UID of the message').label('MessageUid'),
156
+ emailId: Joi.string().example('1694937972638499881').description('Globally unique ID (if server supports it)').label('MessageEmailId'),
157
+ threadId: Joi.string().example('1694936993596975454').description('Thread ID (if server supports it)').label('MessageThreadId'),
158
+ date: Joi.date().iso().example('2021-03-22T13:13:31.000Z').description('Date (internal)'),
159
+ draft: Joi.boolean().example(false).description('Is this message marked as a draft'),
160
+ unseen: Joi.boolean().example(true).description('Is this message unseen'),
161
+ flagged: Joi.boolean().example(true).description('Is this message marked as flagged'),
162
+ size: Joi.number().example(1040).description('Message size in bytes'),
163
+ subject: Joi.string().example('What a wonderful message').description('Message subject (decoded into unicode, applies to other string values as well)'),
164
+
165
+ from: addressSchema.example({ name: 'From Me', address: 'sender@example.com' }),
166
+
167
+ to: Joi.array()
168
+ .items(addressSchema)
169
+ .description('List of addresses')
170
+ .example([{ address: 'recipient@example.com' }])
171
+ .label('AddressList'),
172
+
173
+ cc: Joi.array().items(addressSchema).description('List of addresses').label('AddressList'),
174
+
175
+ bcc: Joi.array().items(addressSchema).description('List of addresses').label('AddressList'),
176
+ messageId: Joi.string().example('<test123@example.com>').description('Message ID'),
177
+ inReplyTo: Joi.string().example('<7JBUMt0WOn+_==MOkaCOQ@mail.gmail.com>').description('Replied Message ID'),
178
+
179
+ labels: Joi.array().items(Joi.string().example('\\Important')).description('Gmail labels').label('LabelList'),
180
+
181
+ attachments: Joi.array().items(attachmentSchema).description('List of attachments').label('AttachmentList'),
182
+
183
+ text: Joi.object({
184
+ id: Joi.string().example('AAAAAgAACqiTkaExkaEykA').description('Pointer to message text content'),
185
+ encodedSize: Joi.object({
186
+ plain: Joi.number().example(1013).description('How many bytes for plain text'),
187
+ html: Joi.number().example(1013).description('How many bytes for html content')
188
+ }).description('Encoded message part sizes')
189
+ }).label('TextInfo')
190
+ }).label('MessageListEntry');
191
+
192
+ const messageDetailsSchema = Joi.object({
193
+ id: Joi.string().example('AAAAAgAACrI').description('Message ID').label('MessageEntryId'),
194
+ uid: Joi.number().example(12345).description('UID of the message').label('MessageUid'),
195
+ emailId: Joi.string().example('1694937972638499881').description('Globally unique ID (if server supports it)').label('MessageEmailId'),
196
+ threadId: Joi.string().example('1694936993596975454').description('Thread ID (if server supports it)').label('MessageThreadId'),
197
+ date: Joi.date().iso().example('2021-03-22T13:13:31.000Z').description('Date (internal)'),
198
+ draft: Joi.boolean().example(false).description('Is this message marked as a draft'),
199
+ unseen: Joi.boolean().example(true).description('Is this message unseen'),
200
+ flagged: Joi.boolean().example(true).description('Is this message marked as flagged'),
201
+ size: Joi.number().example(1040).description('Message size in bytes'),
202
+ subject: Joi.string().example('What a wonderful message').description('Message subject (decoded into unicode, applies to other string values as well)'),
203
+
204
+ from: addressSchema.example({ name: 'From Me', address: 'sender@example.com' }),
205
+ sender: addressSchema.example({ name: 'From Me', address: 'sender@example.com' }),
206
+
207
+ to: Joi.array()
208
+ .items(addressSchema)
209
+ .description('List of addresses')
210
+ .example([{ address: 'recipient@example.com' }])
211
+ .label('AddressList'),
212
+
213
+ cc: Joi.array().items(addressSchema).description('List of addresses').label('AddressList'),
214
+
215
+ bcc: Joi.array().items(addressSchema).description('List of addresses').label('AddressList'),
216
+ messageId: Joi.string().example('<test123@example.com>').description('Message ID'),
217
+ inReplyTo: Joi.string().example('<7JBUMt0WOn+_==MOkaCOQ@mail.gmail.com>').description('Replied Message ID'),
218
+
219
+ flags: Joi.array().items(Joi.string().example('\\Seen')).description('IMAP flags').label('FlagList'),
220
+ labels: Joi.array().items(Joi.string().example('\\Important')).description('Gmail labels').label('LabelList'),
221
+
222
+ attachments: Joi.array().items(attachmentSchema).description('List of attachments').label('AttachmentList'),
223
+
224
+ headers: Joi.object()
225
+ .example({ from: ['From Me <sender@example.com>'], subject: ['What a wonderful message'] })
226
+ .description('Object where header key is object key and value is an array'),
227
+
228
+ text: Joi.object({
229
+ id: Joi.string().example('AAAAAgAACqiTkaExkaEykA').description('Pointer to message text content'),
230
+ encodedSize: Joi.object({
231
+ plain: Joi.number().example(1013).description('How many bytes for plain text'),
232
+ html: Joi.number().example(1013).description('How many bytes for html content')
233
+ }).description('Encoded message part sizes'),
234
+ plain: Joi.string().example('Hello from myself!').description('Plaintext content of the message'),
235
+ html: Joi.string().example('<p>Hello from myself!</p>').description('HTML content of the message'),
236
+ hasMore: Joi.boolean()
237
+ .example(false)
238
+ .description('If partial message content was requested then this value indicates if it includes all the content or there is more')
239
+ }).label('TextInfo'),
240
+
241
+ bounces: Joi.array()
242
+ .items(
243
+ Joi.object({
244
+ message: Joi.string().max(256).required().example('AAAAAQAACnA').description('Bounce email ID'),
245
+ recipient: Joi.string().email().example('recipient@example.com'),
246
+ action: Joi.string().example('failed'),
247
+ response: Joi.object({
248
+ message: Joi.string().example('550 5.1.1 No such user'),
249
+ status: Joi.string().example('5.1.1')
250
+ }).label('BounceResponse'),
251
+ date: Joi.date().iso().example('2021-03-22T13:13:31.000Z').description('Time the bounce was registered by EmailEngine')
252
+ }).label('BounceEntry')
253
+ )
254
+ .label('BounceList')
255
+ }).label('MessageListEntry');
256
+
257
+ const messageListSchema = Joi.object({
258
+ total: Joi.number().example(120).description('How many matching entries').label('TotalNumber'),
259
+ page: Joi.number().example(0).description('Current page (0-based index)').label('PageNumber'),
260
+ pages: Joi.number().example(24).description('Total page count').label('PagesNumber'),
261
+ messages: Joi.array().items(messageEntrySchema).label('PageMessages')
262
+ }).label('MessageList');
263
+
264
+ const mailboxesSchema = Joi.array().items(
265
+ Joi.object({
266
+ path: Joi.string().required().example('Kalender/S&APw-nnip&AOQ-evad').description('Full path to mailbox').label('MailboxPath'),
267
+ delimiter: Joi.string().example('/'),
268
+ parentPath: Joi.string().required().example('Kalender').description('Full path to parent mailbox').label('MailboxParentPath'),
269
+ name: Joi.string().required().example('Sünnipäevad').description('Maibox name').label('MailboxName'),
270
+ listed: Joi.boolean().example(true).description('Was the mailbox found from the output of LIST command').label('MailboxListed'),
271
+ subscribed: Joi.boolean().example(true).description('Was the mailbox found from the output of LSUB command').label('MailboxSubscribed'),
272
+ specialUse: Joi.string()
273
+ .example('\\Sent')
274
+ .valid('\\All', '\\Archive', '\\Drafts', '\\Flagged', '\\Junk', '\\Sent', '\\Trash', '\\Inbox')
275
+ .description('Special use flag of the mailbox if set')
276
+ .label('MailboxSpecialUse'),
277
+ messages: Joi.number().example(120).description('Count of messages in mailbox').label('MailboxMessages'),
278
+ uidNext: Joi.number().example(121).description('Next expected UID').label('MailboxMUidNext')
279
+ }).label('MailboxResponseItem')
280
+ );
281
+
282
+ const shortMailboxesSchema = Joi.array().items(
283
+ Joi.object({
284
+ path: Joi.string().required().example('Kalender/S&APw-nnip&AOQ-evad').description('Full path to mailbox').label('MailboxPath'),
285
+ delimiter: Joi.string().example('/'),
286
+ parentPath: Joi.string().required().example('Kalender').description('Full path to parent mailbox').label('MailboxParentPath'),
287
+ name: Joi.string().required().example('Sünnipäevad').description('Maibox name').label('MailboxName'),
288
+ listed: Joi.boolean().example(true).description('Was the mailbox found from the output of LIST command').label('MailboxListed'),
289
+ subscribed: Joi.boolean().example(true).description('Was the mailbox found from the output of LSUB command').label('MailboxSubscribed'),
290
+ specialUse: Joi.string()
291
+ .example('\\Sent')
292
+ .valid('\\All', '\\Archive', '\\Drafts', '\\Flagged', '\\Junk', '\\Sent', '\\Trash', '\\Inbox')
293
+ .description('Special use flag of the mailbox if set')
294
+ .label('MailboxSpecialUse')
295
+ }).label('MailboxResponseItem')
296
+ );
297
+
298
+ module.exports = {
299
+ settingsSchema,
300
+ addressSchema,
301
+ settingsQuerySchema,
302
+ imapSchema,
303
+ smtpSchema,
304
+ oauth2Schema,
305
+ attachmentSchema,
306
+ messageEntrySchema,
307
+ messageDetailsSchema,
308
+ messageListSchema,
309
+ mailboxesSchema,
310
+ shortMailboxesSchema
311
+ };
@@ -0,0 +1,63 @@
1
+ 'use strict';
2
+
3
+ const { redis } = require('./db');
4
+ const logger = require('./logger');
5
+ const { encrypt, decrypt } = require('./encrypt');
6
+ const config = require('wild-config');
7
+ const getSecret = require('./get-secret');
8
+
9
+ config.service = config.service || {};
10
+
11
+ const DEFAULT_MAX_LOG_LINES = 10000;
12
+ const ENCRYPTED_KEYS = ['gmailClientSecret'];
13
+
14
+ module.exports = {
15
+ encryptedKeys: ENCRYPTED_KEYS,
16
+
17
+ async get(key) {
18
+ const encryptSecret = await getSecret();
19
+
20
+ let value = await redis.hget('settings', key);
21
+ if (typeof value === 'string') {
22
+ try {
23
+ if (encryptSecret && ENCRYPTED_KEYS.includes(key) && typeof value === 'string') {
24
+ // NB! throws if password is invalid
25
+ value = decrypt(value, encryptSecret);
26
+ }
27
+
28
+ value = JSON.parse(value);
29
+ } catch (err) {
30
+ logger.debug({ key, value, err });
31
+ return null;
32
+ }
33
+ }
34
+
35
+ return value;
36
+ },
37
+
38
+ async set(key, value) {
39
+ const encryptSecret = await getSecret();
40
+
41
+ value = JSON.stringify(value);
42
+
43
+ if (encryptSecret && ENCRYPTED_KEYS.includes(key)) {
44
+ value = encrypt(value, encryptSecret);
45
+ }
46
+
47
+ return await redis.hset('settings', key, value);
48
+ },
49
+
50
+ async getLoggingInfo(account, settingData) {
51
+ let loggingSettings = settingData || (await this.get('logs')) || {};
52
+ if (loggingSettings.all || (account && loggingSettings.accounts && loggingSettings.accounts.includes(account))) {
53
+ return {
54
+ enabled: true,
55
+ maxLogLines: 'maxLogLines' in loggingSettings ? loggingSettings.maxLogLines : DEFAULT_MAX_LOG_LINES
56
+ };
57
+ }
58
+ return {
59
+ enabled: false,
60
+ maxLogLines: 'maxLogLines' in loggingSettings ? loggingSettings.maxLogLines : DEFAULT_MAX_LOG_LINES
61
+ };
62
+ }
63
+ };