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
package/encrypt.js ADDED
@@ -0,0 +1,179 @@
1
+ 'use strict';
2
+
3
+ require('dotenv').config();
4
+ try {
5
+ process.chdir(__dirname);
6
+ } catch (err) {
7
+ // ignore
8
+ }
9
+
10
+ process.title = 'emailengine-encrypt';
11
+
12
+ const { redis } = require('./lib/db');
13
+ const config = require('wild-config');
14
+ const { encrypt, decrypt, parseEncryptedData } = require('./lib/encrypt');
15
+ const { encryptedKeys } = require('./lib/settings');
16
+ const getSecret = require('./lib/get-secret');
17
+
18
+ const DECRYPT_PASSWORDS = [].concat(config.decrypt || []);
19
+
20
+ async function processSecret(value, encryptSecret) {
21
+ let lastErr = false;
22
+ let decrypted = value;
23
+
24
+ for (let password of DECRYPT_PASSWORDS) {
25
+ try {
26
+ decrypted = decrypt(value, password);
27
+ if (password === encryptSecret) {
28
+ // nothing was changed
29
+ return value;
30
+ }
31
+ break;
32
+ } catch (err) {
33
+ lastErr = err;
34
+ }
35
+ }
36
+
37
+ let parsed = parseEncryptedData(decrypted);
38
+ if (parsed.format !== 'cleartext') {
39
+ // was not able to decrypt
40
+ if (encryptSecret) {
41
+ try {
42
+ decrypted = decrypt(value, encryptSecret);
43
+ // did not throw, so the value is already encrypted with the new password
44
+ return value;
45
+ } catch (err) {
46
+ // ignore
47
+ }
48
+ }
49
+
50
+ throw lastErr || new Error('Could not decrypt encrypted password');
51
+ }
52
+
53
+ if (encryptSecret) {
54
+ // encrypt
55
+ return encrypt(decrypted, encryptSecret);
56
+ }
57
+
58
+ // return plaintext
59
+ return decrypted;
60
+ }
61
+
62
+ async function main() {
63
+ console.error('EmailEngine account encryption tool');
64
+
65
+ const encryptSecret = await getSecret();
66
+
67
+ if (!encryptSecret && !DECRYPT_PASSWORDS.length) {
68
+ console.error('Usage:');
69
+ console.error(' emailengine encrypt --dbs.redis="redis://url" --service.secret="new-pass" --decrypt="old-pass"');
70
+ console.error('Where');
71
+ console.error(' --dbs.redis is a Redis configuration URL');
72
+ console.error(' --service.secret is the secret value to use for encryption.');
73
+ console.error(' Leave empty to remove encryption.');
74
+ console.error(' --decrypt is the old secret value. Not needed if current passwords are not encrypted.');
75
+ console.error(' You can set this value multiple times if accounts are enrypted with different secrets.');
76
+ return;
77
+ }
78
+
79
+ // convert settings
80
+ for (let key of encryptedKeys) {
81
+ let value = await redis.hget('settings', key);
82
+ if (value && typeof value === 'string') {
83
+ try {
84
+ let updated = await processSecret(value, encryptSecret);
85
+ if (updated !== value) {
86
+ await redis.hset('settings', key, updated);
87
+ console.log(`${key}: Updated setting value`);
88
+ }
89
+ } catch (err) {
90
+ console.error(`${key}: Failed to process setting value`);
91
+ console.error(err);
92
+ }
93
+ }
94
+ }
95
+
96
+ let updatedAccounts = 0;
97
+ let accounts = await redis.smembers('ia:accounts');
98
+ for (let account of accounts) {
99
+ let accountData = await redis.hgetall(`iad:${account}`);
100
+ if (!accountData) {
101
+ continue;
102
+ }
103
+
104
+ let updates = {};
105
+ let updated = false;
106
+ for (let key of ['imap', 'smtp', 'oauth2']) {
107
+ if (!accountData[key]) {
108
+ continue;
109
+ }
110
+
111
+ try {
112
+ accountData[key] = JSON.parse(accountData[key]);
113
+ } catch (err) {
114
+ console.error(`Failed to parse ${key} for ${account}`);
115
+ console.error(err);
116
+ continue;
117
+ }
118
+
119
+ if (!accountData[key]) {
120
+ continue;
121
+ }
122
+
123
+ let changes = false;
124
+
125
+ for (let subKey of ['pass', 'accessToken', 'refreshToken']) {
126
+ if (accountData[key].auth && accountData[key].auth[subKey]) {
127
+ try {
128
+ let value = await processSecret(accountData[key].auth[subKey], encryptSecret);
129
+ if (value !== accountData[key].auth[subKey]) {
130
+ accountData[key].auth[subKey] = value;
131
+ changes = true;
132
+ }
133
+ } catch (err) {
134
+ console.error(`Could not process "${key}.auth.${subKey}" for ${account}. Check decryption secrets.`);
135
+ }
136
+ }
137
+ }
138
+
139
+ for (let subKey of ['accessToken', 'refreshToken']) {
140
+ if (accountData[key] && accountData[key][subKey]) {
141
+ try {
142
+ let value = await processSecret(accountData[key][subKey], encryptSecret);
143
+ if (value !== accountData[key][subKey]) {
144
+ accountData[key][subKey] = value;
145
+ changes = true;
146
+ }
147
+ } catch (err) {
148
+ console.error(`Could not process "${key}.${subKey}" for ${account}. Check decryption secrets.`);
149
+ }
150
+ }
151
+ }
152
+
153
+ if (changes) {
154
+ updates[key] = JSON.stringify(accountData[key]);
155
+ updated = true;
156
+ }
157
+ }
158
+
159
+ if (updated) {
160
+ let result = await redis.hmset(`iad:${account}`, updates);
161
+ if (result === 'OK') {
162
+ console.log(`${account}: updated`);
163
+ } else {
164
+ console.log(`${account}: Unexpected response from DB: ${result}`);
165
+ }
166
+ updatedAccounts++;
167
+ }
168
+ }
169
+
170
+ console.log(`Updated ${updatedAccounts}/${accounts.length} accounts`);
171
+ }
172
+
173
+ main()
174
+ .then(() => process.exit(0))
175
+ .catch(err => {
176
+ console.error(err);
177
+ process.exit(1);
178
+ })
179
+ .finally();
@@ -0,0 +1,137 @@
1
+ # API
2
+
3
+ ```
4
+ curl -XPOST "localhost:3000/v1/account" -H "content-type: application/json" -d '{
5
+ "account": "example",
6
+ "name": "Example",
7
+ "imap": {
8
+ "host": "localhost",
9
+ "port": 9993,
10
+ "secure": true,
11
+ "auth": {
12
+ "user": "myuser2",
13
+ "pass": "verysecret"
14
+ },
15
+ "tls": {
16
+ "rejectUnauthorized": false
17
+ }
18
+ },
19
+ "smtp": {
20
+ "host": "localhost",
21
+ "port": 1025,
22
+ "secure": false,
23
+ "auth": {
24
+ "user": "myuser2",
25
+ "pass": "verysecret"
26
+ },
27
+ "tls": {
28
+ "rejectUnauthorized": false
29
+ }
30
+ }
31
+ }'
32
+ ```
33
+
34
+ ```
35
+ curl -XPUT "localhost:3000/account/example" -H "content-type: application/json" -d '{
36
+ "imap": {
37
+ "host": "localhost",
38
+ "port": 9993,
39
+ "secure": true,
40
+ "auth": {
41
+ "user": "myuser2",
42
+ "pass": "verysecret"
43
+ },
44
+ "tls": {
45
+ "rejectUnauthorized": false
46
+ }
47
+ }
48
+ }'
49
+ ```
50
+
51
+ ```
52
+ curl -XPOST "localhost:3000/v1/verifyAccount" -H "content-type: application/json" -d '{
53
+ "imap": {
54
+ "host": "localhost",
55
+ "port": 9993,
56
+ "secure": true,
57
+ "auth": {
58
+ "user": "myuser2",
59
+ "pass": "verysecret"
60
+ },
61
+ "tls": {
62
+ "rejectUnauthorized": false
63
+ }
64
+ },
65
+ "smtp": {
66
+ "host": "localhost",
67
+ "port": 1025,
68
+ "secure": false,
69
+ "auth": {
70
+ "user": "myuser2",
71
+ "pass": "verysecret"
72
+ },
73
+ "tls": {
74
+ "rejectUnauthorized": false
75
+ }
76
+ }
77
+ }'
78
+ ```
79
+
80
+ ```
81
+ curl -XPOST "localhost:3000/v1/account/pangalink/submit" -H "content-type: application/json" -d '{
82
+ "reference": {
83
+ "message": "AAAAAQAACnA",
84
+ "action": "reply"
85
+ },
86
+ "from": {
87
+ "name": "Pangalink",
88
+ "address": "no-reply@pangalink.net"
89
+ },
90
+ "to": [{
91
+ "name": "Andris Reinman",
92
+ "address": "andris@emailengine.app"
93
+ }],
94
+ "subject": "test kiri",
95
+ "text": "eriti test kiri",
96
+ "html": "<p>eriti test kiri</p>",
97
+ "attachments": [
98
+ {
99
+ "filename": "checkmark.png",
100
+ "content": "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD///+l2Z/dAAAAM0lEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4Ug9C9zwz3gVLMDA/A6P9/AFGGFyjOXZtQAAAAAElFTkSuQmCC"
101
+ }
102
+ ]
103
+ }'
104
+ ```
105
+
106
+ ```
107
+ curl -XDELETE "localhost:3000/v1/account/example"
108
+ ```
109
+
110
+ ```
111
+ curl -XGET "localhost:3000/v1/account/example/mailboxes"
112
+ ```
113
+
114
+ ```
115
+ curl -XGET "localhost:3000/v1/account/example/messages?path=INBOX&page=1"
116
+ ```
117
+
118
+ ```
119
+ curl -XGET "localhost:3000/v1/account/pangalink/message/AAAAAQAAMlw"
120
+ ```
121
+
122
+ ```
123
+ curl -XGET "localhost:3000/v1/account/pangalink/message/AAAAAQAAMlw/source"
124
+ ```
125
+
126
+ ```
127
+ curl -XGET "localhost:3000/v1/account/example/text/AAAAAQAAAeGTkaExkaEykA?textType=html&maxBytes=200"
128
+ ```
129
+
130
+ ```
131
+ curl -XPUT "localhost:3000/v1/account/pangalink/message/AAAAAQAAMlw" -H "content-type: application/json" -d '{
132
+ "flags": {
133
+ "add": ["test2", "test3"],
134
+ "delete": ["test1"]
135
+ }
136
+ }'
137
+ ```
@@ -0,0 +1,104 @@
1
+ 'use strict';
2
+
3
+ // This is an example authentication server
4
+ // It provides fixed credentials for an account called "example" and generates OAuth2 access tokens for an account called "oauth-user"
5
+
6
+ const Hapi = require('@hapi/hapi');
7
+ const hapiPino = require('hapi-pino');
8
+ const XOAuth2 = require('nodemailer/lib/xoauth2');
9
+
10
+ // Gmail oauth app credentials. Must have https://mail.google.com scope set
11
+ const OAUTH2_CLIENT_ID = process.env.OAUTH2_CLIENT_ID;
12
+ const OAUTH2_CLIENT_SECRET = process.env.OAUTH2_CLIENT_SECRET;
13
+
14
+ // Single user specific credentials as our demo only provides tokens for a single user
15
+ const USER_ADDRESS = process.env.USER_ADDRESS;
16
+ const USER_REFRESH_TOKEN = process.env.USER_REFRESH_TOKEN;
17
+
18
+ const init = async () => {
19
+ const server = Hapi.server({
20
+ port: 3080,
21
+ host: 'localhost'
22
+ });
23
+
24
+ await server.register({
25
+ plugin: hapiPino,
26
+ options: {
27
+ level: 'trace'
28
+ }
29
+ });
30
+
31
+ server.route({
32
+ method: 'GET',
33
+ path: '/credentials',
34
+
35
+ async handler(request) {
36
+ switch (request.query.account) {
37
+ // account with id "example" uses password based authentication
38
+ case 'example':
39
+ return {
40
+ user: 'myuser2',
41
+ pass: 'verysecret'
42
+ };
43
+
44
+ // account with id "oauth-user" uses OAuth2 tokens
45
+ case 'oauth-user':
46
+ return {
47
+ user: USER_ADDRESS,
48
+ accessToken: await getAccessToken(USER_ADDRESS, USER_REFRESH_TOKEN)
49
+ };
50
+ }
51
+
52
+ return false;
53
+ }
54
+ });
55
+
56
+ await server.start();
57
+ console.log('Authentication Server URL: %s/credentials', server.info.uri);
58
+ };
59
+
60
+ // The following crux re-uses OAuth2 token generation from the Nodemailer package.
61
+ // Normally you'd probably use something like this instead: https://www.npmjs.com/package/google-auth-library
62
+ const tokens = new Map();
63
+ async function getAccessToken(user, refreshToken) {
64
+ // check cache first
65
+ if (tokens.has(user)) {
66
+ let token = tokens.get(user);
67
+ if (token.expires > new Date()) {
68
+ // use cached token
69
+ return token.accessToken;
70
+ }
71
+ // clear expired token
72
+ tokens.delete(user);
73
+ }
74
+
75
+ // generate new token
76
+ let token = await new Promise((resolve, reject) => {
77
+ let xoauth = new XOAuth2({
78
+ user,
79
+ clientId: OAUTH2_CLIENT_ID,
80
+ clientSecret: OAUTH2_CLIENT_SECRET,
81
+ refreshToken,
82
+ accessUrl: 'https://accounts.google.com/o/oauth2/token'
83
+ });
84
+ xoauth.generateToken(err => {
85
+ if (err) {
86
+ return reject(err);
87
+ }
88
+ if (!xoauth.accessToken) {
89
+ return reject(new Error('Could not generate new access token'));
90
+ }
91
+ resolve({
92
+ accessToken: xoauth.accessToken,
93
+ expires: xoauth.expires
94
+ });
95
+ });
96
+ });
97
+
98
+ // update cache
99
+ tokens.set(user, token);
100
+
101
+ return token.accessToken;
102
+ }
103
+
104
+ init();
package/getswagger.sh ADDED
@@ -0,0 +1,8 @@
1
+ #!/bin/bash
2
+
3
+ export EENGINE_PORT=5678
4
+
5
+ npm start > /dev/null 2>&1 &
6
+ sleep 5
7
+ curl -s "http://127.0.0.1:${EENGINE_PORT}/swagger.json" > swagger.json
8
+ pkill emailengine