UNPKG

postmailer

Version:

HTTP POST -> SMTP proxy, as Express middleware

491 lines (428 loc) 20.8 kB
<html> <head> <title>POST for {{name}}</title> <style> body { max-width: 800px; margin: auto; font-family: 'Open Sans', Tahoma; font-size: 11pt; color: #111; } h1 { font-size: 1.5em; margin: 0.5em 0; text-align: center; } h2 { font-size: 1.2em; margin: 1em 0em; } code { display: inline-block; padding: 0.3em 0.3em 0.1em 0.3em; background-color: #EEE; border-radius: 0.3em; font-family: Consolas, Monaco, 'Andale Mono', monospace; box-shadow: 0px 1px #DDD; } pre { margin: 0.5em 2em; padding: 0; font-size: inherit; } pre code { display: block; width: 100%; overflow: auto; } .cmd-option { color: #048; } .cmd-value { color: #800; } .cmd-value-file { color: #482; } .headers, .text, .email { width: 100%; padding: 0.5em 0em; background-color: #FFF; border: none; border-top: 1px solid #DDD; font-family: Consolas, Monaco, 'Andale Mono', monospace; box-shadow: 0px 1px #DDD; font-size: inherit; height: 5em; } .send { width: 100%; height: 2em; padding:0; color: #000; background-color: #EEE; border: 1px solid #DDD; border-radius: 0.3em; font-family: inherit; font-size: inherit; box-shadow: 0px 1px #DDD; cursor: pointer; } .send:hover { background-color: #DDD; } .log { overflow: auto; margin-top: 1em; margin-bottom: 2em; } .log-send, .log-receive, .log-event { clear: both; display: block; margin: 0.5em 0em; white-space: pre-wrap; max-width: 90%; } .log-send { float: left; background-color: #DEF; } .log-send.pending { font-style: italic; background-color: #EEE; } .log-receive, .log-event { float: right; } .success { background-color: #DDF8DD; } .error { background-color: #F8DDDD; } .overlay { position: fixed; top: 0; left: 0; right: 0; bottom: 0; height: auto; width: auto; padding: 1em; text-align: center; opacity: 0.9; } /* iframe layout */ .iframe-table { position: fixed; top: 0; left: 0; width: 100%; height: 100%; border-spacing: 0; border-collapse: collapse; font-size: inherit; } .iframe-table td { padding: 0; } #email-row, .iframe .email { height: 1.5em; } #text-row { position: relative; } .iframe .text { position: absolute; top: 0; left: 0; height: 100%; resize: none; } #send-row { height: 2em; } .iframe .send { border-top-left-radius: 0; border-top-right-radius: 0; } .iframe .email, .iframe .text { padding: 0.2em 0.3em; } </style> </head> <body data-url="{{url}}" data-ws-url="{{ws-url}}" data-name="{{name}}"> <h1>POST for <span class="self-name">{{name}}</span></h1> <p>This is an HTTP POST endpoint. Any text/document you POST to this URL will be received by the user.</p> <p>You must provide a <code>From:</code> header (borrowed from SMTP), and can include other mail headers as well.</p> <h2>Use with cURL:</h2> <p>To send a plain text message:</p> <pre><code>curl <span class="cmd-option">--data-binary</span> <span class="cmd-value">"Hello, world"</span> <span class="cmd-option">--header</span> <span class="cmd-value">"Content-Type: text/plain"</span> \ <span class="cmd-option">--header</span> <span class="cmd-value">"From: Somebody &lt;somebody@example.com&gt;"</span> \ <span class="cmd-argument"><span class="self-url">{{url}}</span></span></code></pre> <p>To send an inline picture:</p> <pre><code>curl <span class="cmd-option">--data-binary</span> <span class="cmd-value-file">@small-image.png</span> <span class="cmd-option">--header</span> <span class="cmd-value">"Content-Type: image/png"</span> \ <span class="cmd-option">--header</span> <span class="cmd-value">"From: Somebody &lt;somebody@example.com&gt;"</span> \ <span class="cmd-argument"><span class="self-url">{{url}}</span></span></code></pre> <p>To send a picture (or other document) as an attachment:</p> <pre><code>curl <span class="cmd-option">--data-binary</span> <span class="cmd-value-file">@large-image.jpg</span> <span class="cmd-option">--header</span> <span class="cmd-value">"Content-Type: image/jpeg"</span> \ <span class="cmd-option">--header</span> <span class="cmd-value">"Content-Disposition: attachment; filename=\"large-image.jpg\""</span> \ <span class="cmd-option">--header</span> <span class="cmd-value">"From: Somebody &lt;somebody@example.com&gt;"</span> \ <span class="cmd-argument"><span class="self-url">{{url}}</span></span></code></pre> <script> (function () { // Wrap so that minifying is more effective function createElement(tag, textOrProps, parent, before) { var tagParts = tag.split('.'); var element = document.createElement(tagParts.shift()); element.className = tagParts.join(' '); if (typeof textOrProps === 'string') { element.appendChild(document.createTextNode(textOrProps)); } else { for (var key in textOrProps || {}) { element[key] = textOrProps[key]; } } parent = parent || document.body; if (typeof before === 'number') { before = parent.childNodes[before]; } parent.insertBefore(element, before || null); return element; } // Replace template values with JS so we can just use the template on its own var selfUrl = location.href.replace(/[?#].*/, ''); // Strip query and hash var selfUrlEscaped = selfUrl.replace(/&/g, '&amp;').replace(/</g, '&lt;'); var backupName = selfUrl.replace(/.*:\/\//, ''); var backupNameEscaped = backupName.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/"/g, '&quot;'); document.title = document.title.replace(/\{\{name\}\}/g, backupName); document.body.innerHTML = document.body.innerHTML.replace(/\{\{url\}\}/g, selfUrlEscaped).replace(/\{\{name\}\}/g, backupNameEscaped); function inIframe () { try { return window.self !== window.top; } catch (e) { return true; } } function postAjaxMessage(headers, text, callback) { var request = new XMLHttpRequest(); var url = document.body.getAttribute('data-url'); if (!url || url[0] === '{') url = location.href.replace(/#.*/, ''); request.open('POST', url, true); // headerObj is map of arrays/strings var headerObj = headers; if (typeof headers === 'string') { headerObj = {}; headers = headers.split('\n'); for (var i = 0; i < headers.length; i++) { var line = headers[i] + ""; var name = line.match(/^([a-zA-Z\-]*)\:/); if (!name) continue; name = name[1]; var value = line.substring(name.length + 1).replace(/^\s*/, '').replace(/\r/g, ''); headerObj[name] = (headerObj[name] || []).concat(value); } } var headerText = ''; for (var name in headerObj) { var value = headerObj[name] + ""; try { request.setRequestHeader(name, value); headerText += name + ': ' + value + '\n'; } catch (e) { try { console.error(e); } catch (e) {} continue; } } request.onreadystatechange = function () { if (request.readyState !== 4) return; var error = null; if (request.status >= 300 || request.status < 200) { error = new Error(request.status + ' ' + request.statusText); } callback(error, request.responseText); }; request.send(text); return headerText + '\n' + text; } if (!inIframe()) { var h2 = createElement('h2', 'Use with AJAX:'); var inputHeaders = createElement('textarea.headers', {value: 'Content-Type: text/plain\nFrom: ???'}); var inputText = createElement('textarea.text', {value: 'Hello, world'}); var sendButton = createElement('input.send', {type: 'submit', value: 'send'}); var logBox = createElement('div.log'); sendButton.onclick = function () { var receiveEntry = createElement('code.log-receive', 0, logBox, 0); var sentText = postAjaxMessage(inputHeaders.value, inputText.value, function (error, text) { if (error) { receiveEntry.className += ' error'; } else { receiveEntry.className += ' success'; } if (typeof text === 'string') { receiveEntry.appendChild(document.createTextNode(text)); } }); var sendEntry = createElement('code.log-send', sentText, logBox, receiveEntry); }; var wsUrl = document.body.getAttribute('data-ws-url'); if (wsUrl && wsUrl[0] !== '{') { setupWs(wsUrl); } } else { // Sorry, but I need this to work in IE, okay? document.body.innerHTML = '<table class="iframe-table" cellpadding=0 cellspacing=0 border=0>' + '<tr><td id="email-row">' + '<input type="text" id="email-input" class="email" placeholder="name@example.com" value="">' + '<tr><td id="text-row">' + '<textarea id="text-input" class="text" placeholder="Enter your message"></textarea>' + '<tr><td id="send-row">' + '<input type="submit" id="send-input" class="send" value="send">' + '</table>'; document.body.className = 'iframe'; var inputEmail = document.getElementById('email-input'); var inputText = document.getElementById('text-input'); var sendButton = document.getElementById('send-input'); sendButton.onclick = function () { if (!/@/.test(inputEmail.value) && !/\:\/\//.test(inputEmail.value)) { inputEmail.focus(); inputEmail.className = 'email error'; return; } else { inputEmail.className = 'email'; } if (!inputText.value) { inputText.focus(); return; }; sendButton.disabled = true; sendButton.value = 'sending...'; var sentText = postAjaxMessage({ from: inputEmail.value, subject: '(message form)' }, inputText.value, function (error, text) { sendButton.value = "send"; sendButton.disabled = false; var overlay = document.createElement('div'); overlay.className = 'overlay'; document.body.appendChild(overlay); setTimeout(function () { overlay.parentNode.removeChild(overlay); }, 5000); if (error) { overlay.className += ' error'; } else { overlay.className += ' success'; inputText.value = ''; } if (typeof text === 'string') { try { text = JSON.parse(text); } catch (e) { // } overlay.appendChild(document.createTextNode(text)); } }); }; } function setupWs(wsUrl) { var initialHeaders = 'Content-Type: text/plain'; var lastHeaders = initialHeaders; var ws = null, pendingMessages = []; function connectIfNeeded() { if (ws) return; function handleError(error) { ws = null; lastHeaders = initialHeaders; createElement('code.log-event error', error.message || 'connection error', logBox, 0); console.error(error); } try { ws = new WebSocket(wsUrl); } catch (e) { return handleError(e); } ws.onerror = handleError; ws.onclose = function () { if (!ws) { // Already exited, possibly due to error return; } ws = null; lastHeaders = initialHeaders; createElement('code.log-event', 'connection closed', logBox, 0); if (pendingMessages.length) connectIfNeeded(); }; ws.onopen = function () { createElement('code.log-event', 'connection opened', logBox, 0); sendIfReady(); } ws.onmessage = function (event) { var message = event.data; if (typeof message !== 'string') { var blob = message; message += ' (' + message.size + ' bytes)'; if (typeof FileReader === 'function') { var reader = new FileReader(); reader.onprogress = function () { if (reader.result) { postElement.innerHTML = '<I>Binary:</I>\n'; postElement.appendChild(document.createTextNode(reader.result)); } }; reader.readAsBinaryString(blob); } } var postElement = createElement('code.log-receive.success', message, logBox, 0); }; } function sendIfReady() { if (!ws || ws.readyState !== 1) return; while (pendingMessages.length) { var obj = pendingMessages.shift(); ws.send(obj.message); setTimeout(obj.callback, 0); } } function sendMessage(message, headers, callback) { if (headers && headers !== lastHeaders) { lastHeaders = headers; headers = (headers || '').replace(/\r?\n/g, '\r\n').replace(/(^\s+|\s+$)/g, '').replace(/(\r\n)+/, '\r\n'); if (headers) headers += '\r\n'; message = headers + '\r\n' + message; } else if (/^([^\r\n]+\r?\n)*\r?\n/.test(message)) { message = '\r\n' + message; } pendingMessages.push({ message: message, callback: callback }); connectIfNeeded(); sendIfReady(); return message; } /********/ var h2 = createElement('h2', 'Use with WebSockets'); var p1 = createElement('p', 'You can also connect using WebSockets: '); createElement('code', wsUrl, p1); createElement('p', 'If you specify a header block (separated by double-newline) it will replace the existing header block and be used for subsequent messages.'); var inputHeaders = createElement('textarea.headers', {value: 'Content-Type: text/plain\nFrom: ???'}); var inputText = createElement('textarea.text', {value: 'Hello, world'}); var sendButton = createElement('input.send', {type: 'submit', value: 'send'}); var logBox = createElement('div.log'); sendButton.onclick = function () { var sentText = sendMessage(inputText.value, inputHeaders.value, function () { sendEntry.parentNode.removeChild(sendEntry); sendEntry.className = 'log-send'; logBox.insertBefore(sendEntry, logBox.childNodes[0]); }); var sendEntry = createElement('code.log-send.pending', sentText, logBox, 0); }; } })(); </script> </body> </html>