Node.JS module for creating SMTP and LMTP server instances on the fly.
See smtp-server homepage for documentation and terms.
Create custom SMTP servers on the fly
License: Other
Node.JS module for creating SMTP and LMTP server instances on the fly.
See smtp-server homepage for documentation and terms.
While some UTF8-encoded characters make it through just fine, others appear to be mangled.
I can open a connection to my server and send things such as "äöü€" in the message body just fine. But if I want to send characters from the Latin Extended Additional page (e.g. "ẅ"), it gets corrupted. As far as I can tell, there is no way for my code to ever retrieve the original character. It inevitably gets turned into � (U+FFFD). I believe that means that Javascript tried to decode the input and failed.
But if I understand correctly, "ẅ" has been part of Unicode for a long time and should be supported by Javascript. It is U+1E8B. I am a little puzzled where things go wrong, but maybe you could consider a option to turn the server into "binary" mode. Don't even try to interpret data as UTF-8. Just pass it along as an uninterpreted byte stream. That would presumably avoid this and all similar problems.
I will sometimes get this error after the client calls STARTTLS:
_tls_wrap.js:133
if (ctx.context)
^
TypeError: Cannot read property 'context' of undefined
at requestOCSP (_tls_wrap.js:133:10)
at _tls_wrap.js:185:7
at loadSNI (_tls_wrap.js:107:12)
at _tls_wrap.js:182:5
at loadSession (_tls_wrap.js:100:5)
at TLSSocket.onclienthello (_tls_wrap.js:164:3)
The connection is from the blacklisted [61.240.144.64] ip address, but I need the server to stay up to receive more emails. What should I do?
Hi @andris9,
Loving the library - simple yet straightforward and already have it handling a whole lot of automated incoming emails.
I have an observation that I'm not sure is something you'll want to fix in the core library, however it appears that any email coming from Office365 throws an EPIPE error once the email is received properly. I guess they're closing the socket before we've had the chance to send the TCP FIN? Using node v4.4.0
and smtp-server v1.14.0
Currently I'm just ignoring those errors but happy to help if this is something you want to look into further? Otherwise happy to close the issue as wontfix based on Microsoft's SMTP implementation :-)
For a project of mine, I'm planning on using a tunneling proxy to communicate with smtp-server. In the SMTPServer constructor, there is no option to provide a stream/client connection instead of relying on (tls : net).createServer() to listen for clients. I don't want this because I want to handle the "socket" connection layer on my own (to provide a tunneling implementation with additional authentication).
Ideally, I’d pass in some object that enables SMTPServer to listen to client connection/disconnection events and integrate that with an outside class that handles listening for and setting up connections.
I’d like to push this back to the repository and make it available, but wanted to see if you had thought about something like this before. Do you have recommendations on how this should be exposed?
I was thinking of extending options and allowing an object to be passed in with onConnected(id, stream), onDisconnected(id, stream) handlers that could be set. And then in server.listen, make port optional. This keeps changes isolated to smtp-server.js. Lastly, I'd think that an id could be used to correlate SMTP events with the underlying channel; this change would touch multiple files.
Happy to discuss over e-mail if the issue isn’t an appropriate forum.
I have to implement a proxy software which will act as MX record relay.
I had a look at Haraka.js which is full fledged framework and found it little bulky.
Can i use this library for the same.
(Outlook/Thunderbird Mail client) => (local SMTP server) => (:Proxy) => (Remote SMTP server) => ....
:Proxy is what i am trying to implement which will be responsible for doing spam checking, copying the mail body over to RabbitMq and finally relaying it over remote SMTP server.
Is it possible to get MX record as stream so that i can deal with by passing it to various number of custom middleware before relaying it over.
I am wondering how an email server can drop e-mail onto a server I implement on top of smtp-server. How would an email server such as yahoo or godaddy send email to my server if the module demands that they authenticate? Isn't there a way to accept email from other servers and then check to make sure that the recipient is one whom the system will accept mail on behalf of?
Is there any simple way to extract headers from received message?
Yep, but before you say "v0.12 is required to run smtp-server", I am.
Unlike #11 and #12, I am currently using v0.12.7 when attempting to run a basic SMTP server.
node -v
outputs: v0.12.7
console.log(process.version)
outputs v0.12.7
It was working correctly up until a few minutes ago, I think here is where it all went wrong: I was sshing into my server, running the smtp-server, my internet dropped out so I lost connection, connected again and now I receive this error.
Probably not an issue with smtp-server persay, but is a weird issue I am experiencing.
We're using simplesmtp's disableDNSValidation
. Is there an equivalent if we migrate to smtp-server
?
In smtp-connection's auth handler, it directly uses network input as a key with an object. If that key does not exist, then undefined will be returned, and immediately bind will be called, resulting in an exception. Can reproduce by:
EHLO localhost
AUTH evil
Causes sasl["sasl_evil"].bind(this) to be executed.
I created a pull request with a possible fix at #19
Java error:
service_admin_notifier_1 | com.sun.mail.smtp.SMTPSendFailedException: 554 Error: "From" address missing"
SMTP Server error:
smtp_server_1 | 250 SIZE Infinity
smtp_server_1 | [2016-02-16 10:36:03] DEBUG: [svNRNqO/BoHI] C: STARTTLS
smtp_server_1 | [2016-02-16 10:36:03] DEBUG: [svNRNqO/BoHI] S: 220 Ready to start TLS
smtp_server_1 | [2016-02-16 10:36:03] INFO: [svNRNqO/BoHI] Connection upgraded to TLS
smtp_server_1 | [2016-02-16 10:36:03] DEBUG: [svNRNqO/BoHI] C: EHLO 1e5374913756
smtp_server_1 | [2016-02-16 10:36:03] DEBUG: [svNRNqO/BoHI] S: 250-OK: Nice to meet you [::ffff:172.17
.0.11]
smtp_server_1 | 250-PIPELINING
smtp_server_1 | 250-8BITMIME
smtp_server_1 | 250-SMTPUTF8
smtp_server_1 | 250-AUTH PLAIN LOGIN
smtp_server_1 | 250 SIZE Infinity
smtp_server_1 | [2016-02-16 10:36:03] DEBUG: [svNRNqO/BoHI] C: AUTH LOGIN
smtp_server_1 | [2016-02-16 10:36:03] DEBUG: [svNRNqO/BoHI] S: 334 VXNlcm5hbWU6
smtp_server_1 | [2016-02-16 10:36:03] DEBUG: [svNRNqO/BoHI] C: [SECRET]
smtp_server_1 | [2016-02-16 10:36:03] DEBUG: [svNRNqO/BoHI] S: 334 UGFzc3dvcmQ6
smtp_server_1 | [2016-02-16 10:36:03] DEBUG: [svNRNqO/BoHI] C: [SECRET]
smtp_server_1 | [2016-02-16 10:36:03] INFO: [svNRNqO/BoHI] secrett authenticated using LOGIN
smtp_server_1 | [2016-02-16 10:36:03] DEBUG: [svNRNqO/BoHI] S: 235 Authentication successful
smtp_server_1 | [2016-02-16 10:36:03] DEBUG: [svNRNqO/BoHI] C: MAIL FROM:<[email protected]>
smtp_server_1 | [2016-02-16 10:36:03] DEBUG: [svNRNqO/BoHI] S: 250 Accepted
smtp_server_1 | [2016-02-16 10:36:03] DEBUG: [svNRNqO/BoHI] C: RCPT TO:<[email protected]>
smtp_server_1 | [2016-02-16 10:36:03] DEBUG: [svNRNqO/BoHI] S: 250 Accepted
smtp_server_1 | [2016-02-16 10:36:03] DEBUG: [svNRNqO/BoHI] C: DATA
smtp_server_1 | [2016-02-16 10:36:03] DEBUG: [svNRNqO/BoHI] S: 354 End data with <CR><LF>.<CR><LF>
smtp_server_1 | From: [email protected]
smtp_server_1 | To: [email protected]
smtp_server_1 | Message-ID: <64830413.1.1455618963087.JavaMail.root@1e5374913756>
smtp_server_1 | Subject: Notification
smtp_server_1 | MIME-Version: 1.0
smtp_server_1 | Content-Type: text/html; charset=utf-8
smtp_server_1 | Content-Transfer-Encoding: 7bit
smtp_server_1 |
smtp_server_1 | <h1>Lets celebrate.</h1>
smtp_server_1 | </ol>[2016-02-16 10:36:03] DEBUG: [svNRNqO/BoHI] C: <546 bytes of DATA>
smtp_server_1 | [2016-02-16 10:36:03] DEBUG: [svNRNqO/BoHI] S: 554 Error: "From" address missing
smtp_server_1 | undefined
smtp_server_1 | [2016-02-16 10:36:03] DEBUG: [svNRNqO/BoHI] C: RSET
smtp_server_1 | [2016-02-16 10:36:03] DEBUG: [svNRNqO/BoHI] S: 250 Flushed
smtp_server_1 | [2016-02-16 10:36:03] DEBUG: [svNRNqO/BoHI] C: QUIT
smtp_server_1 | [2016-02-16 10:36:03] DEBUG: [svNRNqO/BoHI] S: 221 Bye
smtp_server_1 | [2016-02-16 10:36:03] INFO: [svNRNqO/BoHI] Connection closed to [::ffff:172.17.0.11]
I see the From
header but the server doesn't seem to see it?
There are a few places in the SMTP protocol (most notably optional arguments to "MAIL FROM:") that are encoded as "xtext". It would be helpful, if they were transparently decoded.
As far as I can tell, decoding is really simple in Node:
function xtext(s) {
return s.replace(/[^!-*,-<>-~]/g,
function(ch) { return encodeURIComponent(ch).replace(/%/g, '+'); });
}
function xtextDecode(s) {
return s.replace(/\+[0-9A-F]{2}/g,
function(ch) { return decodeURIComponent(ch.replace(/\+/g, '%')); });
}
There currently is an onConnect handler, but I could really use an onClose handler to clean up resources. Would it be too much trouble to add this callback?
When running the attached example and receiving an email from GMail on port 25, I got an error and the node process is stopped.
/mypath/node_modules/smtp-server/lib/smtp-connection.js:357
if (!this.session.user && this._isSupported('AUTH') && ['MAIL', 'RCPT', 'DATA'].indexOf(commandName) >= 0 && !this.server.options.authOptional) {
^
TypeError: Cannot read property 'options' of undefined
at SMTPConnection._onCommand (/mypath/node_modules/smtp-server/lib/smtp-connection.js:357:126)
at SMTPStream.<anonymous> (/mypath/node_modules/smtp-server/lib/smtp-stream.js:134:18)
at SMTPStream._write (/mypath/node_modules/smtp-server/lib/smtp-stream.js:138:9)
at doWrite (_stream_writable.js:307:12)
at writeOrBuffer (_stream_writable.js:293:5)
at SMTPStream.Writable.write (_stream_writable.js:220:11)
When I'm parsing email using mailparser I have still the same mail_object.text
value. I'm sending different emails but output is alway the first email. Should I add somewhere mailparser.end()
?
My code:
mailparser.on("end", function(mail_object){
console.log( mail_object.text);
});
...
onData: function(stream, session, callback){
stream.pipe(mailparser)
stream.on('end', callback)
}
This is a significant security issue. Obviously anyone serious about security should be using their own private keys. But this library shouldn't be making it easy for people to build insecure mail servers. A private key isn't private if its in a public repository like this. If the user doesn't give their own TLS keys, the server simply shouldn't accept TLS.
Doing it this way also makes it pretty confusing if you want to set up a mail server entirely without TLS.
Hello again,
Got a random error using latest v1.5.2. I don't know how to reproduce since it has been reported by my log manager, and may come from a random user using a random SMTP server trying to send a mail to my inbound mailer (which is running smtp-server).
Hope the trace is enough to fix it on your side.
Error: Name must be a string
at Object.query [as reverse] (dns.js:220:13)
at SMTPConnection.connectionReady (/srv/data_crisp/svc/crisp-relay-mailer-inbound/node_modules/smtp-server/lib/smtp-connection.js:107:9)
at Timer.listOnTimeout (timers.js:89:15)
Ping me if you need further tests on my side, or debugging on my side.
Cheers 🎉
Would this require the use of smtp-connection?
Is it possible to receive message with sendmail? I've created smtp.js:
const {SMTPServer} = require('smtp-server');
const server = new SMTPServer({
logger: true,
onData: function(stream, session, callback){
stream.pipe(process.stdout); // print message to console
stream.on('end', callback);
},
});
server.listen(425, 'localhost');
Than I created email.txt
:
Subject: Terminal Email Send
Email Content line 1
Email Content line 2
And send it via sendmail:
sudo node smtp.js &
sendmail test@localhost < email.txt
Logger output listening message:
[2016-10-30 20:28:43] INFO: SMTP Server listening on 127.0.0.1:425
But my email got into ~/dead.letter. Where am I wrong?
This line doesn't do anything https://github.com/andris9/smtp-server/blob/187212d26e962c0e7553737666056d16ebee57b4/lib/smtp-connection.js#L604
After setting the secure
option to true
(optionally adding certificates) the server stops working. I'm using node v6.2.0|v6.3.1
and smtp-server v1.12.0
. It seams that SMTPConnection within the socket callback is never triggered.
Testing command (swaks):
$ swaks -tls -t [email protected] -f '[email protected]' --h-From: '"Fred Example" <[email protected]>' -s 127.0.0.1 -p 5870 -au testuser -ap testpass
=== Trying 127.0.0.1:5870...
=== Connected to 127.0.0.1.
//-> it stops here, nothing happens
Server script (from the example):
var SMTPServer = require('smtp-server').SMTPServer;
var util = require('util');
var SERVER_PORT = 5870;
var SERVER_HOST = '0.0.0.0';
var server = new SMTPServer({
logger: true,
banner: 'Welcome to My Awesome SMTP Server',
secure: true,
key: fs.readFileSync('./src/tls_key.pem'),
cert: fs.readFileSync('./src/tls_cert.pem'),
// disabledCommands: ['STARTTLS'],
authMethods: ['PLAIN', 'LOGIN', 'CRAM-MD5'],
size: 10 * 1024 * 1024,
useXClient: true,
hidePIPELINING: true,
useXForward: true,
onAuth: function (auth, session, callback) {
callback(null, {user: 'userdata'});
},
onMailFrom: function (address, session, callback) {
callback();
},
onRcptTo: function (address, session, callback) {
callback();
},
onData: function (stream, session, callback) {
stream.pipe(process.stdout);
stream.on('end', function () {
callback(null, 'Message queued as abcdef');
});
}
});
server.on('error', function (err) {
console.log('Error occurred');
console.log(err);
});
server.listen(SERVER_PORT, SERVER_HOST);
Have I missed something or is it a bug?
Hi @andris9 and all the community!
I'm starting this REQUEST because i've noticed an read, the we still don't have an implementation to responde the sender with a 200 OK message before closing the connection.
@andris9 , any help is really appreciated really. Do you think it is possible to implement it in a near future? If not, can you please guide me a little? I'll try to do it myself and make a merge request :D
This module does not support CRAM based authentications since these require access to unencrypted user password during the authentication process. You shouldn't store passwords unencrypted anyway, so supporting it doesn't make much sense.
In general I agree. However, anyone wishing to use this module to create a drop-in MSA to replace their existing one is constrained by the settings of their users MUA. Unfortunately, MUA's tend to remember settings like PLAIN -vs- CRAM-MD5, and if the server suddenly stopped supporting CRAM, then their ability to send mail is suddenly broken.
Would you accept patches that add CRAM support?
ReferenceError: Set is not defined
at new SMTPServer (/root/email/node_modules/smtp-server/lib/smtp-server.js:83:28)
at Object.<anonymous> (/root/email/emailServer.js:4:14)
at Module._compile (module.js:456:26)
at Object.Module._extensions..js (module.js:474:10)
at Module.load (module.js:356:32)
at Function.Module._load (module.js:312:12)
at Function.Module.runMain (module.js:497:10)
at startup (node.js:119:16)
at node.js:906:3
Looks like you're not even doing anything that couldn't be done just as well with a regular array. Any reason you're using a Set
? Yes I know I should upgrade node, but i'm trying to do something quick with an email server and I don't want to go to all the trouble to figure out how to correctly update node (not only on the machine i'm using, but on all my dev's machines as well - automatic you know?).
require_once '../../vendor/swiftmailer/swiftmailer/lib/swift_required.php';
$transport = Swift_SmtpTransport::newInstance("localhost", 2525);
// Create the message
$message = Swift_Message::newInstance();
$message->setTo("[email protected]", "Aurelio De Rosa");
$message->setSubject("This email is sent using Swift Mailer");
$message->setBody("this is a test email.");
$message->setFrom("[email protected]", "hung nguyen");
// Send the email
$mailer = Swift_Mailer::newInstance($transport);
$mailer->send($message, $failedRecipients);
the root cause of issue is because swiftmailer send email with from address in format "MAIL FROM**:** [email protected]" and at the function SMTPConnection.prototype._parseAddressCommand
line "if (new RegExp('^' + name + '$', 'i').test(address)) " does not match, should check "if (new RegExp('^' + name + ':{0,1}$', 'i').test(address))"
Would you be interested in a PR that adds LMTP support?
LMTP (RFC 2033) is a stripped down version of SMTP that's used for local mail delivery behind a front end mail server that doesn't want to support local queueing of email but instead wants to just deliver mail.
The protocol is more or less the same as SMTP with three small changes:
HELO
, a client (often an upstream mail server) will send LHLO
DATA
is only allowed after at least one RCPT TO
has been issued.DATA
, the server returns one status line for each previously seen RCPT TO
line.Is this still within the scope of this project? Would you be interested in a PR?
Hi!
I have response: '250 Message queued' but don't recieve emails.
What should I do?
Thanks!
Hi. I use the smtp-server for the creation of the application and I want to be able to specify the STARTTLS not mandatory.
I know that is hideSTARTTLS, but it hides the STARTTLS in the EHLO
EHLO test
250-OK: Nice to meet you [0.0.0.0]
250-PIPELINING
250-8BITMIME
250-SMTPUTF8
250 AUTH PLAIN LOGIN CRAM-MD5
Can you add option requireSTARTTLS?
When calling server.close()
without any parameters, I get the following error:
TypeError: callback is not a function
at SMTPServer.<anonymous> (D:\billysFile\code\tests\node.js\node_modules\smtp-server\lib\smtp-server.js:126:9)
at Server.g (events.js:260:16)
at emitNone (events.js:72:20)
at Server.emit (events.js:166:7)
at emitCloseNT (net.js:1521:8)
at doNTCallback1 (node.js:441:9)
at process._tickCallback (node.js:363:17)
It seems it requires a callback. Shouldn't the callback be optional?
i made a test program with smtp-server, smtp-connection
i ran the smtp-server and run the smtp-connection.
i encountered error on the smtp-connection.
events.js:141
throw er; // Unhandled 'error' event
^
Error: Invalid greeting from server:
421 user-PC You talk too soon: 421 user-PC You talk too soon
at SMTPConnection._actionGreeting (C:\Users\kjin\AppData\Roaming\npm\node_modules\smtp-connection\lib\smtp-connection.js:718:23)
at SMTPConnection._processResponse (C:\Users\kjin\AppData\Roaming\npm\node_modules\smtp-connection\lib\smtp-connection.js:604:16)
at SMTPConnection._onData (C:\Users\kjin\AppData\Roaming\npm\node_modules\smtp-connection\lib\smtp-connection.js:431:10)
at emitOne (events.js:77:13)
at Socket.emit (events.js:169:7)
at readableAddChunk (_stream_readable.js:146:16)
at Socket.Readable.push (_stream_readable.js:110:10)
at TCP.onread (net.js:523:20)
Process finished with exit code 1
so, i think that it appears because of smtp-server ehlo command.
so how can i do ehlo command in smtp-server?
Why am I connecting successfully and asking smtp-server to send an e-mail successfully but the e-mail is never dispatched? It says that it is queued, however never sends. Look at my log:
o:99garcons-api igor$ node qdt-mailer.js
[2016-08-30 18:21:37] INFO: SMTP Server listening on [::]:41085
[2016-08-30 18:21:37] INFO: [/Zy42mtho4y/] Connection from [::ffff:127.0.0.1]
[2016-08-30 18:21:37] DEBUG: [/Zy42mtho4y/] S: 220 Igors-MacBook-Pro.local ESMTP
[2016-08-30 18:21:37] DEBUG: [/Zy42mtho4y/] C: EHLO Igors-MacBook-Pro.local
[2016-08-30 18:21:37] DEBUG: [/Zy42mtho4y/] S: 250-Igors-MacBook-Pro.local Nice to meet you, [::ffff:127.0.0.1]
250-PIPELINING
250-8BITMIME
250 SMTPUTF8
[2016-08-30 18:21:37] DEBUG: [/Zy42mtho4y/] C: MAIL FROM:[email protected]
[2016-08-30 18:21:37] DEBUG: [/Zy42mtho4y/] S: 250 Accepted
[2016-08-30 18:21:37] DEBUG: [/Zy42mtho4y/] C: RCPT TO:[email protected]
[2016-08-30 18:21:37] DEBUG: [/Zy42mtho4y/] S: 250 Accepted
[2016-08-30 18:21:37] DEBUG: [/Zy42mtho4y/] C: DATA
[2016-08-30 18:21:37] DEBUG: [/Zy42mtho4y/] S: 354 End data with .
[2016-08-30 18:21:37] INFO: <received 824 bytes>
[2016-08-30 18:21:37] DEBUG: [/Zy42mtho4y/] C: <824 bytes of DATA>
[2016-08-30 18:21:37] DEBUG: [/Zy42mtho4y/] S: 250 OK: message queued
SEND EMAIL SUCCESS { accepted: [ '[email protected]' ],
rejected: [],
response: '250 OK: message queued',
envelope:
{ from: '[email protected]',
to: [ '[email protected]' ] },
messageId: '[email protected]' }
[2016-08-30 18:21:37] INFO: [/Zy42mtho4y/] Connection closed to [::ffff:127.0.0.1]
It would be great to have a promise support for onAuth
, onMailFrom
and other onX
methods out of the box. We could beautify our code by using the async/await syntax (soon in node, babel for now):
var server = new SMTPServer({
onAuth: async (auth, session, next) {
let isValid = await db.findUser({id: session.id});
if (isValid) {
await next();
} else {
throw new Error('not authenticated');
}
},
onError: async (err, next) {
console.log(err.message);
await next();
}
});
Hello,
I got an error when creating a new SMTPServer:
ReferenceError: Set is not defined
in lib/smtp-server.js on line 83
And yes, it's not defined... Where does this Set come from ?
Thanks
I was using the examples/server.js
and using nodemailer to connect
but some error occur
[2016-11-20 10:38:09] INFO: SMTP Server listening on [::]:2525
[2016-11-20 10:39:02] INFO: [wMTJj9/pXmX7] Connection from [127.0.0.1]
[2016-11-20 10:39:02] DEBUG: [wMTJj9/pXmX7] S: 220 easonwang-mbp.local ESMTP Welcome to My Awesome SMTP Server
[2016-11-20 10:39:03] DEBUG: [wMTJj9/pXmX7] C: EHLO easonwang-mbp.local
[2016-11-20 10:39:03] DEBUG: [wMTJj9/pXmX7] S: 250-easonwang-mbp.local Nice to meet you, [127.0.0.1]
250-8BITMIME
250-SMTPUTF8
250-SIZE 10485760
250-XCLIENT NAME ADDR PORT PROTO HELO LOGIN
250 XFORWARD NAME ADDR PORT PROTO HELO IDENT SOURCE
[2016-11-20 10:39:03] DEBUG: [wMTJj9/pXmX7] C: AUTH PLAIN AGphc29uNzEyNDgAMjkxMTQwNTU=
[2016-11-20 10:39:03] DEBUG: [wMTJj9/pXmX7] S: 500 Error: command not recognized
[2016-11-20 10:39:03] INFO: [wMTJj9/pXmX7] Connection closed to [127.0.0.1]
it'd be nice to have support for the PROXY extension to use smtp-server in a load balanced environment as a drop-in replacement for postfix.
I set up a server and it can receive my connection but can not send the mail to my destination.
the session is abnormal:
in onMailFrom(), it is:
{ id: 'exboII9SStOa',
remoteAddress: '115.175.24.76',
clientHostname: '[115.175.24.76]',
openingCommand: 'EHLO',
hostNameAppearsAs: '[127.0.0.1]',
xClient: Map {},
xForward: Map {},
transmissionType: 'ESMTPA',
tlsOptions: false,
envelope:
{ mailFrom: { address: '[email protected]', args: false },
rcptTo: [] },
transaction: 1,
user: 'squarance' }
in onRcptTo(), it is:
{ id: 'exboII9SStOa',
remoteAddress: '115.175.24.76',
clientHostname: '[115.175.24.76]',
openingCommand: 'EHLO',
hostNameAppearsAs: '[127.0.0.1]',
xClient: Map {},
xForward: Map {},
transmissionType: 'ESMTPA',
tlsOptions: false,
envelope: { mailFrom: false, rcptTo: [] },
transaction: 2,
user: 'squarance' }
the envelope object didn't set the rcptTo and reset mailFrom, why would it happen?
For use as a Postfix content filter, it would make a lot of sense to support XFORWARD in addition to XCLIENT. I suspect, the code would look extremely similar to what you already had to do for XCLIENT. So, hopefully, this is an easy fix.
I'm trying to create a quick example showing how smtp-server and smtp-connection play with eachother. I do not get any errors, but the mail is not sending. Hoping it is just a simple mistake/wrong assumption on my part. I'm running this on OS X. Help appreciated!
var SMTPServer = require('smtp-server').SMTPServer;
var server = new SMTPServer({
secure: false,
authMethods: ['PLAIN'],
disabledCommands: ['STARTTLS'],
logger: true,
onAuth: function (auth, session, callback) {
if (auth.username === 'foo' && auth.password === 'bar') {
console.log('Logging in `foo`.');
callback(null, { user: 'foo' });
} else {
console.log('Invalid username or password for `foo`.');
callback(new Error('Invalid username or password.'));
}
}
});
server.listen(2500, function () {
console.log('SMTP server listening on port 2500.');
});
server.on('error', function(err){
console.log('Error %s', err.message);
});
var SMTPConnection = require('smtp-connection');
var options = {
port: 2500,
host: 'localhost',
secure: false,
ignoreTLS: true,
logger: true,
debug: true,
authMethod: 'PLAIN'
};
var mailcomposer = require("mailcomposer");
var connection = new SMTPConnection(options);
connection.connect(function (err) {
console.log('Connected to SMTP server.');
connection.login({
user: 'foo',
pass: 'bar'
}, function (err) {
if (err) {
console.log('Authentication failed!', err);
} else {
console.log('Authentication to SMTP server successful.');
var message = mailcomposer({
from: '[email protected]',
to: '[email protected]',
subject: 'A test subject.',
text: 'A test body.'
});
connection.send({
from: '[email protected]',
to: '[email protected]'
}, message.createReadStream(), function (err, info) {
if (err) {
console.log('Message not sent.', err);
} else {
console.log('Message sent.', info);
}
});
}
});
});
Here is the console output log:
artz@airtz ~/Sites/smtp-server node smtp.js
[2016-06-05 14:33:11] INFO: SMTP Server listening on [::]:2500
SMTP server listening on port 2500.
[2016-06-05 14:33:11] INFO: [txlxMgMHDTc] Connection established to 127.0.0.1:2500
[2016-06-05 14:33:11] INFO: [E/f6A62SIxii] Connection from [::ffff:127.0.0.1]
[2016-06-05 14:33:11] DEBUG: [E/f6A62SIxii] S: 220 airtz.fios-router.home ESMTP
[2016-06-05 14:33:11] DEBUG: [txlxMgMHDTc] S: 220 airtz.fios-router.home ESMTP
[2016-06-05 14:33:11] DEBUG: [txlxMgMHDTc] C: EHLO airtz.fios-router.home
[2016-06-05 14:33:11] DEBUG: [E/f6A62SIxii] C: EHLO airtz.fios-router.home
[2016-06-05 14:33:11] DEBUG: [E/f6A62SIxii] S: 250-OK: Nice to meet you [::ffff:127.0.0.1]
250-PIPELINING
250-8BITMIME
250-SMTPUTF8
250 AUTH PLAIN
[2016-06-05 14:33:11] DEBUG: [txlxMgMHDTc] S: 250-OK: Nice to meet you [::ffff:127.0.0.1]
250-PIPELINING
250-8BITMIME
250-SMTPUTF8
250 AUTH PLAIN
[2016-06-05 14:33:11] DEBUG: [txlxMgMHDTc] SMTP handshake finished
Connected to SMTP server.
[2016-06-05 14:33:11] DEBUG: [txlxMgMHDTc] C: AUTH PLAIN AGZvbwBiYXI=
[2016-06-05 14:33:11] DEBUG: [E/f6A62SIxii] C: AUTH PLAIN AGZvbwBiYXI=
Logging in `foo`.
[2016-06-05 14:33:11] INFO: [E/f6A62SIxii] foo authenticated using PLAIN
[2016-06-05 14:33:11] DEBUG: [E/f6A62SIxii] S: 235 Authentication successful
[2016-06-05 14:33:11] DEBUG: [txlxMgMHDTc] S: 235 Authentication successful
[2016-06-05 14:33:11] INFO: [txlxMgMHDTc] User "foo" authenticated
Authentication to SMTP server successful.
[2016-06-05 14:33:11] DEBUG: [txlxMgMHDTc] C: MAIL FROM:<[email protected]>
[2016-06-05 14:33:11] DEBUG: [E/f6A62SIxii] C: MAIL FROM:<[email protected]>
[2016-06-05 14:33:11] DEBUG: [E/f6A62SIxii] S: 250 Accepted
[2016-06-05 14:33:11] DEBUG: [txlxMgMHDTc] S: 250 Accepted
[2016-06-05 14:33:11] DEBUG: [txlxMgMHDTc] C: RCPT TO:<[email protected]>
[2016-06-05 14:33:11] DEBUG: [E/f6A62SIxii] C: RCPT TO:<[email protected]>
[2016-06-05 14:33:11] DEBUG: [E/f6A62SIxii] S: 250 Accepted
[2016-06-05 14:33:11] DEBUG: [txlxMgMHDTc] S: 250 Accepted
[2016-06-05 14:33:11] DEBUG: [txlxMgMHDTc] C: DATA
[2016-06-05 14:33:11] DEBUG: [E/f6A62SIxii] C: DATA
[2016-06-05 14:33:11] DEBUG: [E/f6A62SIxii] S: 354 End data with <CR><LF>.<CR><LF>
[2016-06-05 14:33:11] DEBUG: [txlxMgMHDTc] S: 354 End data with <CR><LF>.<CR><LF>
[2016-06-05 14:33:11] DEBUG: [txlxMgMHDTc] C: Content-Type: text/plain
From: [email protected]
To: [email protected]
Subject: A test subject.
Message-Id: <[email protected]>
Content-Transfer-Encoding: 7bit
Date: Sun, 05 Jun 2016 14:33:11 +0000
MIME-Version: 1.0
[2016-06-05 14:33:11] DEBUG: [txlxMgMHDTc] C: A test body.
[2016-06-05 14:33:11] DEBUG: [txlxMgMHDTc] C:
.
[2016-06-05 14:33:11] INFO: [txlxMgMHDTc] C: <282 bytes encoded mime message (source size 277 bytes)>
[2016-06-05 14:33:11] INFO: <received 277 bytes>
[2016-06-05 14:33:11] DEBUG: [E/f6A62SIxii] C: <277 bytes of DATA>
[2016-06-05 14:33:11] DEBUG: [E/f6A62SIxii] S: 250 OK: message queued
[2016-06-05 14:33:11] DEBUG: [txlxMgMHDTc] S: 250 OK: message queued
Message sent. { accepted: [ '[email protected]' ],
rejected: [],
response: '250 OK: message queued' }
[2016-06-05 14:34:11] DEBUG: [E/f6A62SIxii] S: 451 Timeout - closing connection
[2016-06-05 14:34:11] DEBUG: [txlxMgMHDTc] S: 451 Timeout - closing connection
[2016-06-05 14:34:11] ERROR: [txlxMgMHDTc] Unexpected Response: 451 Timeout - closing connection
events.js:160
throw er; // Unhandled 'error' event
^
Error: Unexpected Response: 451 Timeout - closing connection
at SMTPConnection._processResponse (/Users/artz/Sites/smtp-server/node_modules/smtp-connection/lib/smtp-connection.js:625:30)
at SMTPConnection._onData (/Users/artz/Sites/smtp-server/node_modules/smtp-connection/lib/smtp-connection.js:449:10)
at emitOne (events.js:96:13)
at Socket.emit (events.js:188:7)
at readableAddChunk (_stream_readable.js:172:18)
at Socket.Readable.push (_stream_readable.js:130:10)
at TCP.onread (net.js:542:20)
Even though it says the message was queued successfully, it never arrives in my inbox (gmail account).
I'm also not sure how to correctly handle the timeout error.
Thanks in advance for your assistance and guidance!
Just tried out your script and it seems to work at first, but it always stops responding after DATA has been sent, here's the (client) log:
2016-04-21 04:42:11 Connection: opening to myserver, timeout=300, options=array (
)
2016-04-21 04:42:11 Connection: opened
2016-04-21 04:42:11 SERVER -> CLIENT: 220 myserver ESMTP
2016-04-21 04:42:11 CLIENT -> SERVER: EHLO a domain
2016-04-21 04:42:11 SERVER -> CLIENT: 250-OK: Nice to meet you [an ip]
250-PIPELINING
250-8BITMIME
250-SMTPUTF8
250-AUTH LOGIN PLAIN
250 STARTTLS
2016-04-21 04:42:11 CLIENT -> SERVER: STARTTLS
2016-04-21 04:42:11 SERVER -> CLIENT: 220 Ready to start TLS
2016-04-21 04:42:11 CLIENT -> SERVER: EHLO a domain name
2016-04-21 04:42:11 SERVER -> CLIENT: 250-OK: Nice to meet you [an ip]
250-PIPELINING
250-8BITMIME
250-SMTPUTF8
250 AUTH LOGIN PLAIN
2016-04-21 04:42:11 CLIENT -> SERVER: AUTH LOGIN
2016-04-21 04:42:11 SERVER -> CLIENT: 334 VXNlcm5hbWU6
2016-04-21 04:42:11 CLIENT -> SERVER: xyz
2016-04-21 04:42:11 SERVER -> CLIENT: 334 UGFzc3dvcmQ6
2016-04-21 04:42:11 CLIENT -> SERVER: xyz (this is valid too)
2016-04-21 04:42:11 SERVER -> CLIENT: 235 Authentication successful
2016-04-21 04:42:11 CLIENT -> SERVER: MAIL FROM:<a mail(this is valid)>
2016-04-21 04:42:11 SERVER -> CLIENT: 250 Accepted
2016-04-21 04:42:11 CLIENT -> SERVER: RCPT TO:<a mail(this is valid)>
2016-04-21 04:42:11 SERVER -> CLIENT: 250 Accepted
2016-04-21 04:42:11 CLIENT -> SERVER: DATA
2016-04-21 04:42:11 SERVER -> CLIENT: 354 End data with <CR><LF>.<CR><LF>
2016-04-21 04:42:11 CLIENT -> SERVER: Date: Thu, 21 Apr 2016 06:42:11 +0200
2016-04-21 04:42:11 CLIENT -> SERVER: To: Tijl Van den Brugghen <a mail(this is valid)>
2016-04-21 04:42:11 CLIENT -> SERVER: From: Ok <a mail(this is valid)>
2016-04-21 04:42:11 CLIENT -> SERVER: Subject: Here is the subject
2016-04-21 04:42:11 CLIENT -> SERVER: Message-ID: <6d8735ee101485fa48957b36919ace80@a mail(this is valid & without spaces)>
2016-04-21 04:42:11 CLIENT -> SERVER: X-Mailer: PHPMailer 5.2.14 (https://github.com/PHPMailer/PHPMailer)
2016-04-21 04:42:11 CLIENT -> SERVER: MIME-Version: 1.0
2016-04-21 04:42:11 CLIENT -> SERVER: Content-Type: multipart/alternative;
2016-04-21 04:42:11 CLIENT -> SERVER: boundary="b1_6d8735ee101485fa48957b36919ace80"
2016-04-21 04:42:11 CLIENT -> SERVER: Content-Transfer-Encoding: 8bit
2016-04-21 04:42:11 CLIENT -> SERVER:
2016-04-21 04:42:11 CLIENT -> SERVER: This is a multi-part message in MIME format.
2016-04-21 04:42:11 CLIENT -> SERVER:
2016-04-21 04:42:11 CLIENT -> SERVER: --b1_6d8735ee101485fa48957b36919ace80
2016-04-21 04:42:11 CLIENT -> SERVER: Content-Type: text/plain; charset=us-ascii
2016-04-21 04:42:11 CLIENT -> SERVER:
2016-04-21 04:42:11 CLIENT -> SERVER: This is the body in plain text for non-HTML mail clients
2016-04-21 04:42:11 CLIENT -> SERVER:
2016-04-21 04:42:11 CLIENT -> SERVER:
2016-04-21 04:42:11 CLIENT -> SERVER: --b1_6d8735ee101485fa48957b36919ace80
2016-04-21 04:42:11 CLIENT -> SERVER: Content-Type: text/html; charset=us-ascii
2016-04-21 04:42:11 CLIENT -> SERVER:
2016-04-21 04:42:11 CLIENT -> SERVER: This is the HTML message body <b>in bold!</b>
2016-04-21 04:42:11 CLIENT -> SERVER:
2016-04-21 04:42:11 CLIENT -> SERVER:
2016-04-21 04:42:11 CLIENT -> SERVER:
2016-04-21 04:42:11 CLIENT -> SERVER: --b1_6d8735ee101485fa48957b36919ace80--
2016-04-21 04:42:11 CLIENT -> SERVER:
2016-04-21 04:42:11 CLIENT -> SERVER: .
2016-04-21 04:43:11 SERVER -> CLIENT: 451 Timeout - closing connection
2016-04-21 04:43:11 SMTP ERROR: DATA END command failed: 451 Timeout - closing connection
2016-04-21 04:43:11 SMTP Error: data not accepted.
Message could not be sent.Mailer Error: SMTP Error: data not accepted.SMTP server error: DATA END command failed Detail: Timeout - closing connection
SMTP code: 4512016-04-21 04:43:11 CLIENT -> SERVER: QUIT
2016-04-21 04:43:11 SERVER -> CLIENT:
2016-04-21 04:43:11 SMTP ERROR: QUIT command failed:
2016-04-21 04:43:11 Connection: closed
I tried this on multiple SMTP clients, all ended up with the same error. It looks like the server isn't responding after the closing dot. onData
in the options
object is also never called. I tried doing the same by hand using telnet, works fine until I need to end the DATA, the dot is not doing anything.
My config sets secure
to false and allowInsecureAuth
to true for testing.
I've been having trouble setting up a server and testing it. I tried setting up a nodemailer
script, but it doesn't seem to want to send it, or possibly smtp-server doesn't want to receive it. I wrote an SO question that hasn't been getting much love: http://stackoverflow.com/questions/34756908/can-i-send-an-email-from-my-local-machine-to-my-local-machine-using-node-smtp-se
I'm just testing this out, sending email to it from gmail:
var smtp = require('smtp-server')
var SMTPServer = smtp.SMTPServer
var port = 465
var server = new SMTPServer({
onData: function(stream, session, callback){
stream.pipe(process.stdout); // print message to console
stream.on('end', callback);
},
onRcptTo: function(address, session, callback){
console.log("RcptTo: "+address)
return callback(); // Accept the address
} ,
onMailFrom: function(address, session, callback){
console.log("MailFrom: "+address)
return callback(); // Accept the address
},
onAuth: function(auth, session, callback){
console.log("Auth: "+auth.method+' by '+auth.username)
callback(null, {})
}
})
server.listen(port, function() {
console.log("Listening for email on port "+port)
});
server.on('error', function(err){
console.log('Error %s', err.message);
});
This is the output for the whole run of the program, when i start it, then send it an email from gmail:
[2015-07-26 23:17:23] INFO: SMTP Server listening on 0.0.0.0:25
Listening for email on port 465
[2015-07-26 23:18:19] INFO: [XzmZ5PT5c/s/] Connection from mail-wi0-f180.google.com
[2015-07-26 23:18:19] DEBUG: [XzmZ5PT5c/s/] S: 220 ecto-1 ESMTP
[2015-07-26 23:18:19] DEBUG: [XzmZ5PT5c/s/] C: EHLO mail-wi0-f180.google.com
[2015-07-26 23:18:19] DEBUG: [XzmZ5PT5c/s/] S: 250-OK: Nice to meet you mail-wi0-f180.google.com
250-PIPELINING
250-8BITMIME
250-SMTPUTF8
250-AUTH LOGIN PLAIN
250 STARTTLS
[2015-07-26 23:18:20] DEBUG: [XzmZ5PT5c/s/] C: STARTTLS
[2015-07-26 23:18:20] DEBUG: [XzmZ5PT5c/s/] S: 220 Ready to start TLS
/root/email/node_modules/smtp-server/lib/smtp-connection.js:503
var secureContext = tls.createSecureContext(tlsOptions(this._server.option
^
TypeError: Object #<Object> has no method 'createSecureContext'
at SMTPConnection.handler_STARTTLS (/root/email/node_modules/smtp-server/lib/smtp-connection.js:503:29)
at SMTPConnection._onCommand (/root/email/node_modules/smtp-server/lib/smtp-connection.js:297:13)
at SMTPStream.<anonymous> (/root/email/node_modules/smtp-server/lib/smtp-stream.js:134:18)
at SMTPStream._write (/root/email/node_modules/smtp-server/lib/smtp-stream.js:138:9)
at doWrite (_stream_writable.js:225:10)
at writeOrBuffer (_stream_writable.js:215:5)
at SMTPStream.Writable.write (_stream_writable.js:182:11)
at write (_stream_readable.js:601:24)
at flow (_stream_readable.js:610:7)
at Socket.pipeOnReadable (_stream_readable.js:642:5)
Is this simply because I don't have tls set up? I would have thought gmail would be able to send to servers that don't have tls set up..
It would be super useful if I could subscribe to specific events from the server, instead of having to specify callbacks. As an example, currently, when I want to handle the receipt of message data, I need to pass in an onData
handler, which winds up creating more tightly coupled code, and restricts the patterns I can use when implementing this modules. I'd like to be able to register listeners on these events instead, like so:
// some-program.js
var mailRelayServer = require("./my-server"); // is an SMTPServer instance
relayServer.on('connection', connectionHandler);
relayServer.on('message', messageHandler1);
relayServer.on('message', messageHandler2);
Obviously, there are ways to do what I want without altering this source code, but the utilities gained from what I'm doing on top of this module would potentially be very useful inside this module, and I hate to expand the footprint of the npm world without good reason.
If this is something you've considered, but haven't wanted to implement due to complicating the api, I totally understand. On the other hand, if it's just a time issue, I'd be happy to submit a PR if the idea is something you'd like to implement.
I don't know if this is directly related to being on Windows or not as testing on OSX didn't have this problem but it's mail from different sources on Windows and only a single, controlled source on OSX.
C:\mycode\trunk\node_modules\smtp-server\lib\smtp-stream.js:144
if (!this.dataBytes && len >= 3 && Buffer.compare(chunk.slice(0, 3), new B
^
TypeError: Object function Buffer(subject, encoding, offset) {
if (!(this instanceof Buffer)) {
return new Buffer(subject, encoding, offset);
}
var type;
// Are we slicing?
if (typeof offset === 'number') {
if (!Buffer.isBuffer(subject)) {
throw new TypeError('First argument must be a Buffer when slicing');
}
this.length = +encoding > 0 ? Math.ceil(encoding) : 0;
this.parent = subject.parent ? subject.parent : subject;
this.offset = offset;
} else {
// Find the length
switch (type = typeof subject) {
case 'number':
this.length = +subject > 0 ? Math.ceil(subject) : 0;
break;
case 'string':
this.length = Buffer.byteLength(subject, encoding);
break;
case 'object': // Assume object is array-ish
this.length = +subject.length > 0 ? Math.ceil(subject.length) : 0;
break;
default:
throw new TypeError('First argument needs to be a number, ' +
'array or string.');
}
if (this.length > Buffer.poolSize) {
// Big buffer, just alloc one.
this.parent = new SlowBuffer(this.length);
this.offset = 0;
} else if (this.length > 0) {
// Small buffer.
if (!pool || pool.length - pool.used < this.length) allocPool();
this.parent = pool;
this.offset = pool.used;
// Align on 8 byte boundary to avoid alignment issues on ARM.
pool.used = (pool.used + this.length + 7) & ~7;
} else {
// Zero-length buffer
this.parent = zeroBuffer;
this.offset = 0;
}
// optimize by branching logic for new allocations
if (typeof subject !== 'number') {
if (type === 'string') {
// We are a string
this.length = this.write(subject, 0, encoding);
// if subject is buffer then use built-in copy method
} else if (Buffer.isBuffer(subject)) {
if (subject.parent)
subject.parent.copy(this.parent,
this.offset,
subject.offset,
this.length + subject.offset);
else
subject.copy(this.parent, this.offset, 0, this.length);
} else if (isArrayIsh(subject)) {
for (var i = 0; i < this.length; i++)
this.parent[i + this.offset] = subject[i];
}
}
}
SlowBuffer.makeFastBuffer(this.parent, this, this.offset, this.length);
} has no method 'compare'
at SMTPStream._feedDataStream (C:\mycode\trunk\node_modules\smtp-ser
ver\lib\smtp-stream.js:144:47)
at SMTPStream._write (C:\mycode\trunk\node_modules\smtp-server\lib\s
mtp-stream.js:122:14)
at doWrite (_stream_writable.js:226:10)
at writeOrBuffer (_stream_writable.js:216:5)
at SMTPStream.Writable.write (_stream_writable.js:183:11)
at write (_stream_readable.js:602:24)
at flow (_stream_readable.js:611:7)
at Socket.pipeOnReadable (_stream_readable.js:643:5)
at Socket.emit (events.js:92:17)
at emitReadable_ (_stream_readable.js:427:10)
at emitReadable (_stream_readable.js:423:5)
at readableAddChunk (_stream_readable.js:166:9)
Thank you for this software.
As mentioned in issue #64 this module is a black hole if the dispatching is not taking place. I believe this should be part of the documentation, I have been debugging for a while before I found that issue :)
TypeError Exception is thrown at line 817 of smtp-connection.js when context stream is not an object. This causes a crash in the process running this module.
TypeError: Cannot read property 'readable' of null
at SMTPConnection.<anonymous> (/home/****/GitHub/Galleon/node_modules/smtp-server/lib/smtp-connection.js:817:29)
This issue was caught by the Process Manager which restarted the process immediately. I've attempted to reproduce this issue by manipulating the requests sent to the server but so far have been unsuccessful as all requests have been handled gracefully.
this._server.onData(this._dataStream, this.session, function(err, message) {
// do not continue until the stream has actually ended
if (this._dataStream.readable) { // < Throws on this line
this._dataStream.on('end', function() {
close(err, message);
});
return;
}
close(err, message);
}.bind(this));
TypeError is thrown after this._dataStream.readable
is validated to identify whether end of stream has been reached as a type Object (Stream Object) is expected, yet as seen in the trace the value is in fact set to null
.
I suspect the issue originates from _onClose function where _dataStream
is set to null
after it is unpiped. As of this moment I assume a call to onData is made after the connection has been closed using the onData's callback argument.
This brings me to the point that although it is a rare incident (one incident yet I do receive a few hundred emails on the server from different shady sources) it can actually contribute to a DoS attack with an SMTP transaction designed to trigger this issue.
#26 A simple patch to avoid this problem altogether.
Using version 1.2.0 and above, starting smtp-server/examples/server.js
fails with:
ReferenceError: Set is not defined
at new SMTPServer (/home/younes.ouadi/Workspace/Dev/Ouzoud/NodeJs/io.ouzoud/node_modules/smtp-server/lib/smtp-server.js:73:28)
Versions 1.0.0 to 1.1.1 works fine.
I've recently noticed my app crashing every now and then with the error message below.
/opt/eftrap/node_modules/smtp-server/lib/smtp-connection.js:945
this._dataStream = this._parser.startDataMode(this._server.options.size);
^
TypeError: this._parser.startDataMode is not a function
at SMTPConnection.handler_DATA (/opt/eftrap/node_modules/smtp-server/lib/smtp-connection.js:945:37)
at SMTPConnection._onCommand (/opt/eftrap/node_modules/smtp-server/lib/smtp-connection.js:354:13)
at SMTPStream.<anonymous> (/opt/eftrap/node_modules/smtp-server/lib/smtp-stream.js:134:18)
at SMTPConnection.<anonymous> (/opt/eftrap/node_modules/smtp-server/lib/smtp-connection.js:932:9)
at processImmediate [as _immediateCallback] (timers.js:383:17)
My guess is that we're running into some kind of race condition when a client disconnects unexpectedly at some point.
Using
var smtpServer = new SMTPServer({
allowInsecureAuth: true,
authOptional: true,
onData: function(stream, session, callback){
stream.pipe(process.stdout);
stream.pipe(mailparser);
stream.on('end', callback);
}
});
The response body's key "date" will always contain the same value.
For instance
When i started the server i will always get the date of first email on every email.
// Email 1
date: Sat Sep 03 2016 12:38:23 GMT+0200 (CEST)
// Email 2
date: Sat Sep 03 2016 12:38:23 GMT+0200 (CEST)
// Email 3
date: Sat Sep 03 2016 12:38:23 GMT+0200 (CEST)
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.