Comparing version
@@ -7,32 +7,48 @@ /* global angular, app, prompt */ | ||
app.controller('ItemCtrl', [ | ||
'$scope', '$rootScope', '$routeParams', '$location', 'Email', '$http', '$cookies', | ||
function ($scope, $rootScope, $routeParams, $location, Email, $http, $cookies) { | ||
const iframe = null | ||
app.controller("ItemCtrl", [ | ||
"$scope", | ||
"$rootScope", | ||
"$routeParams", | ||
"$location", | ||
"Email", | ||
"$http", | ||
"$cookies", | ||
function ( | ||
$scope, | ||
$rootScope, | ||
$routeParams, | ||
$location, | ||
Email, | ||
$http, | ||
$cookies | ||
) { | ||
// Get the item data by route parameter | ||
const getItem = function () { | ||
Email.get({ id: $routeParams.itemId }, function (email) { | ||
$scope.item = new Email(email) | ||
Email.get( | ||
{ id: $routeParams.itemId }, | ||
function (email) { | ||
$scope.item = new Email(email); | ||
if ($scope.item.html) { | ||
$scope.item.iframeUrl = 'email/' + $scope.item.id + '/html' | ||
prepIframe() | ||
$scope.panelVisibility = 'html' | ||
} else { | ||
$scope.htmlView = 'disabled' | ||
$scope.panelVisibility = 'plain' | ||
if ($scope.item.html) { | ||
$scope.item.iframeUrl = "email/" + $scope.item.id + "/html"; | ||
prepIframe(); | ||
$scope.panelVisibility = "html"; | ||
} else { | ||
$scope.htmlView = "disabled"; | ||
$scope.panelVisibility = "plain"; | ||
} | ||
}, | ||
function () { | ||
console.error("404: Email not found"); | ||
$location.path("/"); | ||
} | ||
}, function () { | ||
console.error('404: Email not found') | ||
$location.path('/') | ||
}) | ||
} | ||
); | ||
}; | ||
// Get email source | ||
const getSource = function () { | ||
if (typeof $scope.rawEmail === 'undefined') { | ||
$scope.rawEmail = 'email/' + $scope.item.id + '/source' | ||
if (typeof $scope.rawEmail === "undefined") { | ||
$scope.rawEmail = "email/" + $scope.item.id + "/source"; | ||
} | ||
} | ||
}; | ||
@@ -43,38 +59,46 @@ // Prepares the iframe for interaction | ||
setTimeout(function () { | ||
const [iframe] = document.getElementsByTagName('iframe') | ||
const [head] = iframe.contentDocument.getElementsByTagName('head') | ||
const baseEl = iframe.contentDocument.createElement('base') | ||
const [iframe] = document.getElementsByTagName("iframe"); | ||
const [head] = iframe.contentDocument.getElementsByTagName("head"); | ||
const baseEl = iframe.contentDocument.createElement("base"); | ||
// Append <base target="_blank" /> to <head> in the iframe so all links open in new window | ||
baseEl.setAttribute('target', '_blank') | ||
baseEl.setAttribute("target", "_blank"); | ||
if (head) head.appendChild(baseEl) | ||
if (head) head.appendChild(baseEl); | ||
replaceMediaQueries() | ||
fixIframeHeight() | ||
replaceMediaQueries(iframe); | ||
fixIframeHeight(iframe); | ||
addHideDropdownHandler(iframe.contentDocument.getElementsByTagName('body')[0]) | ||
}, 500) | ||
} | ||
addHideDropdownHandler( | ||
iframe.contentDocument.getElementsByTagName("body")[0] | ||
); | ||
}, 500); | ||
}; | ||
// Updates the iframe height so it matches it's content | ||
// This prevents the iframe from having scrollbars | ||
const fixIframeHeight = function () { | ||
const body = iframe.contentDocument.getElementsByTagName('body')[0] | ||
const newHeight = body.scrollHeight | ||
const fixIframeHeight = function (iframe) { | ||
const body = iframe.contentDocument.getElementsByTagName("body")[0]; | ||
const newHeight = body.scrollHeight; | ||
iframe.height = newHeight | ||
} | ||
iframe.height = newHeight; | ||
}; | ||
// Updates all media query rules to use 'width' instead of device width | ||
const replaceMediaQueries = function () { | ||
angular.forEach(iframe.contentDocument.styleSheets, function (styleSheet) { | ||
angular.forEach(styleSheet.cssRules, function (rule) { | ||
if (rule.media && rule.media.mediaText) { | ||
// TODO -- Add future warning if email doesn't use '[max|min]-device-width' media queries | ||
rule.media.mediaText = rule.media.mediaText.replace('device-width', 'width') | ||
} | ||
}) | ||
}) | ||
} | ||
const replaceMediaQueries = function (iframe) { | ||
angular.forEach( | ||
iframe.contentDocument.styleSheets, | ||
function (styleSheet) { | ||
angular.forEach(styleSheet.cssRules, function (rule) { | ||
if (rule.media && rule.media.mediaText) { | ||
// TODO -- Add future warning if email doesn't use '[max|min]-device-width' media queries | ||
rule.media.mediaText = rule.media.mediaText.replace( | ||
"device-width", | ||
"width" | ||
); | ||
} | ||
}); | ||
} | ||
); | ||
}; | ||
@@ -84,23 +108,26 @@ // NOTE: This is kind of a hack to get these dropdowns working. Should be revisited in the future | ||
$scope.toggleDropdown = function ($event, dropdownName) { | ||
$event.stopPropagation() | ||
$scope.dropdownOpen = dropdownName === $scope.dropdownOpen ? '' : dropdownName | ||
} | ||
$event.stopPropagation(); | ||
$scope.dropdownOpen = | ||
dropdownName === $scope.dropdownOpen ? "" : dropdownName; | ||
}; | ||
function hideDropdown (e) { | ||
function hideDropdown(e) { | ||
$scope.$apply(function () { | ||
$scope.dropdownOpen = '' | ||
}) | ||
$scope.dropdownOpen = ""; | ||
}); | ||
} | ||
function addHideDropdownHandler (element) { | ||
angular.element(element) | ||
.off('click', hideDropdown) | ||
.on('click', hideDropdown) | ||
function addHideDropdownHandler(element) { | ||
angular | ||
.element(element) | ||
.off("click", hideDropdown) | ||
.on("click", hideDropdown); | ||
} | ||
addHideDropdownHandler(window) | ||
addHideDropdownHandler(window); | ||
function validateEmail (email) { | ||
const re = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ | ||
return re.test(email) | ||
function validateEmail(email) { | ||
const re = | ||
/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; | ||
return re.test(email); | ||
} | ||
@@ -110,35 +137,40 @@ | ||
$scope.show = function (type) { | ||
if ((type === 'html' || type === 'attachments') && !$scope.item[type]) return | ||
if (type === 'source') getSource() | ||
if ((type === "html" || type === "attachments") && !$scope.item[type]) | ||
return; | ||
if (type === "source") getSource(); | ||
$scope.panelVisibility = type | ||
} | ||
$scope.panelVisibility = type; | ||
}; | ||
// Sends a DELETE request to the server | ||
$scope.delete = function (item) { | ||
Email.delete({ id: item.id }) | ||
} | ||
Email.delete({ id: item.id }); | ||
}; | ||
// Updates iframe to have a width of newSize, i.e. '320px' | ||
$scope.resize = function (newSize) { | ||
iframe.style.width = newSize || '100%' | ||
fixIframeHeight() | ||
$scope.iframeSize = newSize | ||
} | ||
const [iframe] = document.getElementsByTagName("iframe"); | ||
iframe.style.width = newSize || "100%"; | ||
fixIframeHeight(); | ||
$scope.iframeSize = newSize; | ||
}; | ||
// Relay email to | ||
$scope.relayTo = function (item) { | ||
const lastRelayTo = $cookies.relayTo | ||
const lastRelayTo = $cookies.relayTo; | ||
const relayTo = prompt('Please enter email address to relay', lastRelayTo) | ||
const relayTo = prompt( | ||
"Please enter email address to relay", | ||
lastRelayTo | ||
); | ||
if (relayTo) { | ||
if (validateEmail(relayTo)) { | ||
$scope.relay(item, relayTo) | ||
$cookies.relayTo = relayTo | ||
$scope.relay(item, relayTo); | ||
$cookies.relayTo = relayTo; | ||
} else { | ||
window.alert('The specified email address is not correct.') | ||
window.alert("The specified email address is not correct."); | ||
} | ||
} | ||
} | ||
}; | ||
@@ -149,6 +181,6 @@ // Relay email | ||
window.alert( | ||
'Relay feature has not been configured.\n' + | ||
'Run maildev --help for configuration info.' | ||
) | ||
return | ||
"Relay feature has not been configured.\n" + | ||
"Run maildev --help for configuration info." | ||
); | ||
return; | ||
} | ||
@@ -158,24 +190,31 @@ | ||
window.confirm( | ||
'Are you sure you want to REALLY SEND email to ' + | ||
(relayTo || item.to.map(function (to) { return to.address }).join()) + ' through ' + | ||
$rootScope.config.outgoingHost + '?' | ||
"Are you sure you want to REALLY SEND email to " + | ||
(relayTo || | ||
item.to | ||
.map(function (to) { | ||
return to.address; | ||
}) | ||
.join()) + | ||
" through " + | ||
$rootScope.config.outgoingHost + | ||
"?" | ||
) | ||
) { | ||
$http({ | ||
method: 'POST', | ||
url: 'email/' + item.id + '/relay' + (relayTo ? '/' + relayTo : '') | ||
method: "POST", | ||
url: "email/" + item.id + "/relay" + (relayTo ? "/" + relayTo : ""), | ||
}) | ||
.success(function (data, status) { | ||
console.log('Relay result: ', data, status) | ||
window.alert('Relay successful') | ||
console.log("Relay result: ", data, status); | ||
window.alert("Relay successful"); | ||
}) | ||
.error(function (data) { | ||
window.alert('Relay failed: ' + data.error) | ||
}) | ||
window.alert("Relay failed: " + data.error); | ||
}); | ||
} | ||
} | ||
}; | ||
// Initialize the view by getting the email | ||
getItem() | ||
} | ||
]) | ||
getItem(); | ||
}, | ||
]); |
@@ -19,4 +19,4 @@ 'use strict' | ||
const outgoing = require('./outgoing') | ||
const TurndownService = require('turndown') | ||
const marked = require('marked') | ||
const createDOMPurify = require('dompurify') | ||
const { JSDOM } = require('jsdom') | ||
@@ -272,4 +272,10 @@ const store = [] | ||
if (email.html) { | ||
const turndownService = new TurndownService() | ||
email.html = marked.parse(turndownService.turndown(email.html)) | ||
// sanitize html | ||
const window = new JSDOM('').window | ||
const DOMPurify = createDOMPurify(window) | ||
email.html = DOMPurify.sanitize(email.html, { | ||
WHOLE_DOCUMENT: true, // preserve html,head,body elements | ||
SANITIZE_DOM: false, // ignore DOM cloberring to preserve form id/name attributes | ||
ADD_TAGS: ['link'] // allow link element to preserve external style sheets | ||
}) | ||
} | ||
@@ -276,0 +282,0 @@ done(null, email) |
{ | ||
"name": "maildev", | ||
"description": "SMTP Server and Web Interface for reading and testing emails during development", | ||
"version": "2.0.2", | ||
"private": false, | ||
"version": "2.0.3", | ||
"keywords": [ | ||
@@ -48,5 +47,5 @@ "email", | ||
"css-watch": "node-sass -wr --output-style compressed -o app/styles assets/styles/style.scss", | ||
"docker-build": "docker build -t soulteary/maildev:$npm_package_version . && docker tag soulteary/maildev:$npm_package_version soulteary/maildev:latest", | ||
"docker-run": "docker run --rm -p 1080:1080 -p 1025:1025 soulteary/maildev:$npm_package_version", | ||
"docker-push": "docker push soulteary/maildev:$npm_package_version && docker push soulteary/maildev:latest", | ||
"docker-build": "./scripts/dockerBuild.sh", | ||
"docker-run": "docker run --rm -p 1080:1080 -p 1025:1025 maildev/maildev:$npm_package_version", | ||
"docker-push": "./scripts/dockerPush.sh", | ||
"update-readme": "node ./scripts/updateUsageREADME.js" | ||
@@ -65,5 +64,5 @@ }, | ||
"cors": "^2.8.5", | ||
"dompurify": "^2.3.6", | ||
"express": "^4.17.3", | ||
"iconv-lite": "0.5.0", | ||
"marked": "^4.0.12", | ||
"mime": "2.4.4", | ||
@@ -74,3 +73,2 @@ "nodemailer": "^6.7.2", | ||
"socket.io": "4.4.1", | ||
"turndown": "^7.1.1", | ||
"uue": "3.1.2" | ||
@@ -77,0 +75,0 @@ }, |
@@ -5,3 +5,3 @@ # MailDev | ||
**MailDev** is a simple way to test your project's generated emails during development with an easy to use web interface that runs on your machine. Built on top of [Node.js](http://www.nodejs.org). | ||
**MailDev** is a simple way to test your project's generated email during development, with an easy to use web interface that runs on your machine built on top of [Node.js](http://www.nodejs.org). | ||
@@ -13,7 +13,7 @@  | ||
If you want to use MailDev with [Docker](https://www.docker.com/), you can use the | ||
[**soulteary/maildev** image on Docker Hub](https://hub.docker.com/r/soulteary/maildev). | ||
[**maildev/maildev** image on Docker Hub](https://hub.docker.com/r/maildev/maildev). | ||
For a guide for usage with Docker, | ||
[checkout the docs](https://github.com/maildev/maildev/blob/master/docs/docker.md). | ||
$ docker run -p 1080:1080 -p 1025:1025 soulteary/maildev | ||
$ docker run -p 1080:1080 -p 1025:1025 maildev/maildev | ||
@@ -28,5 +28,5 @@ ## Usage | ||
| -------------------------------- | -------------------------- | ----------------------------------------------------------------------------------------- | | ||
| `-s, --smtp <port>` | `MAILDEV_SMTP_PORT` | SMTP port to catch emails | | ||
| `-s, --smtp <port>` | `MAILDEV_SMTP_PORT` | SMTP port to catch mail | | ||
| `-w, --web <port>` | `MAILDEV_WEB_PORT` | Port to run the Web GUI | | ||
| `--mail-directory <path>` | `MAILDEV_MAIL_DIRECTORY` | Directory for persisting mails | | ||
| `--mail-directory <path>` | `MAILDEV_MAIL_DIRECTORY` | Directory for persisting mail | | ||
| `--https` | `MAILDEV_HTTPS` | Switch from http to https protocol | | ||
@@ -36,11 +36,11 @@ | `--https-key <file>` | `MAILDEV_HTTPS_KEY` | The file path to the ssl private key | | ||
| `--ip <ip address>` | `MAILDEV_IP` | IP Address to bind SMTP service to | | ||
| `--outgoing-host <host>` | `MAILDEV_OUTGOING_HOST` | SMTP host for outgoing emails | | ||
| `--outgoing-port <port>` | `MAILDEV_OUTGOING_PORT` | SMTP port for outgoing emails | | ||
| `--outgoing-user <user>` | `MAILDEV_OUTGOING_USER` | SMTP user for outgoing emails | | ||
| `--outgoing-pass <password>` | `MAILDEV_OUTGOING_PASS` | SMTP password for outgoing emails | | ||
| `--outgoing-secure` | `MAILDEV_OUTGOING_SECURE` | Use SMTP SSL for outgoing emails | | ||
| `--outgoing-host <host>` | `MAILDEV_OUTGOING_HOST` | SMTP host for outgoing mail | | ||
| `--outgoing-port <port>` | `MAILDEV_OUTGOING_PORT` | SMTP port for outgoing mail | | ||
| `--outgoing-user <user>` | `MAILDEV_OUTGOING_USER` | SMTP user for outgoing mail | | ||
| `--outgoing-pass <password>` | `MAILDEV_OUTGOING_PASS` | SMTP password for outgoing mail | | ||
| `--outgoing-secure` | `MAILDEV_OUTGOING_SECURE` | Use SMTP SSL for outgoing mail | | ||
| `--auto-relay [email]` | `MAILDEV_AUTO_RELAY` | Use auto-relay mode. Optional relay email address | | ||
| `--auto-relay-rules <file>` | `MAILDEV_AUTO_RELAY_RULES` | Filter rules for auto relay mode | | ||
| `--incoming-user <user>` | `MAILDEV_INCOMING_USER` | SMTP user for incoming emails | | ||
| `--incoming-pass <pass>` | `MAILDEV_INCOMING_PASS` | SMTP password for incoming emails | | ||
| `--incoming-user <user>` | `MAILDEV_INCOMING_USER` | SMTP user for incoming mail | | ||
| `--incoming-pass <pass>` | `MAILDEV_INCOMING_PASS` | SMTP password for incoming mail | | ||
| `--web-ip <ip address>` | `MAILDEV_WEB_IP` | IP Address to bind HTTP service to, defaults to --ip | | ||
@@ -55,3 +55,3 @@ | `--web-user <user>` | `MAILDEV_WEB_USER` | HTTP user for GUI | | ||
| `--silent` | | | | ||
| `--log-mail-contents` | | Log a JSON representation of each incoming mail | | ||
| `--log-mail-contents` | | Log a JSON representation of each incoming mail | | ||
@@ -58,0 +58,0 @@ ## API |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Deprecated
MaintenanceThe maintainer of the package marked it as deprecated. This could indicate that a single version should not be used, or that the package is no longer maintained and any new vulnerabilities will not be fixed.
Found 1 instance in 1 package
5309707
15.78%14
-6.67%83
1.22%47409
69.06%1
Infinity%+ Added
+ Added
- Removed
- Removed
- Removed
- Removed
- Removed