/*! \file plusemail.cpp * \brief Quicky module implementing \@email function. * * \author Rachel Blackman * * \verbatim * Config options: * mail_server SMTP server to use (mail.bar.com) * mail_ehlo ehlo server (my.baz.com) * mail_sendaddr return address (foo@bar.com) * mail_sendname RFC822 'name' (FooMUSH) * mail_subject Defaults subject link (FooMUSH Mail) * \endverbatim */ #include "autoconf.h" #include "config.h" #include "externs.h" #include "ganl_stub.h" #include // Encode mail body such that CRLF.CRLF only appears at the end of the // message. Here are some example transforms. // // . --> .. // . --> .. // --> . // --> . // // and are only allowed to occur as the pair -- never alone. // // Code 0 - Any byte (including non-ASCII / UTF-8 continuation bytes). // Code 1 - NUL (0x00) // Code 2 - LF (0x0A) // Code 3 - CR (0x0D) // Code 4 - '.' (0x2E) // static const UTF8 BodyClasses[256] = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F // 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 3, 0, 0, // 0 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, // 2 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 5 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 7 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // C 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // D 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // E 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // F }; #define STATE_NOTHING 0 #define STATE_BOM 1 #define STATE_HAVE_CR 2 #define STATE_HAVE_CRLF 3 #define STATE_HAVE_CRLF_DOT 4 #define STATE_HAVE_CRLF_DOT_CR 5 #define STATE_HAVE_DOT 6 #define STATE_HAVE_DOT_CR 7 // Action 0 - Remain in current state. // Action 1 - Emit ch and remain in current state. // Action 2 - Emit ch and move to STATE_NOTHING. // Action 3 - Emit CRLF.CRLF and terminate. // Action 4 - Move to STATE_HAVE_CR. // Action 5 - Move to STATE_HAVE_DOT. // Action 6 - Move to STATE_HAVE_CRLF. // Action 7 - Emit CRLF+ch and move to STATE_NOTHING. // Action 8 - Emit CRLF and move to STATE_HAVE_CR. // Action 9 - Move to STATE_HAVE_CRLF_DOT. // Action 10 - Emit CRLF..+ch and move to STATE_NOTHING. // Action 11 - Emit CRLF..CRLF.CRLF and terminate. // Action 12 - Move to STATE_HAVE_CRLF_DOT_CR. // Action 13 - Emit CRLF.. and move to STATE_HAVE_CRLF. // Action 14 - Emit ..+ch and move to STATE_NOTHING. // Action 15 - Emit ..CRLF.CRLF and terminate. // Action 16 - Move to STATE_HAVE_DOT_CR. // Action 17 - Emit .. and move to STATE_HAVE_CRLF. // static const int BodyActions[8][5] = { // Any '\0' LF CR '.' { 1, 3, 0, 4, 1}, // STATE_NOTHING { 2, 3, 0, 4, 5}, // STATE_BOM { 2, 3, 6, 0, 5}, // STATE_HAVE_CR { 7, 3, 0, 8, 9}, // STATE_HAVE_CRLF { 10, 11, 0, 12, 10}, // STATE_HAVE_CRLF_DOT { 10, 11, 13, 0, 10}, // STATE_HAVE_CRLF_DOT_CR { 14, 15, 0, 16, 14}, // STATE_HAVE_DOT { 14, 15, 17, 0, 14} // STATE_HAVE_DOT_CR }; UTF8 *EncodeBody(UTF8 *pBody) { thread_local UTF8 buf[2*LBUF_SIZE]; UTF8 *bp = buf; int iState = STATE_BOM; for (;;) { UTF8 ch = *pBody++; int iAction = BodyActions[iState][BodyClasses[static_cast(ch)]]; switch (iAction) { case 0: // Action 0 - Remain in current state. // break; case 1: // Action 1 - Emit ch and remain in current state. // *bp++ = ch; break; case 2: // Action 2 - Emit ch and move to STATE_NOTHING. // *bp++ = ch; iState = STATE_NOTHING; break; case 3: // Action 3 - Emit CRLF.CRLF and terminate. // *bp++ = '\r'; *bp++ = '\n'; *bp++ = '.'; *bp++ = '\r'; *bp++ = '\n'; *bp++ = '\0'; return buf; break; case 4: // Action 4 - Move to STATE_HAVE_CR. // iState = STATE_HAVE_CR; break; case 5: // Action 5 - Move to STATE_HAVE_DOT. // iState = STATE_HAVE_DOT; break; case 6: // Action 6 - Move to STATE_HAVE_CRLF. // iState = STATE_HAVE_CRLF; break; case 7: // Action 7 - Emit CRLF+ch and move to STATE_NOTHING. // *bp++ = '\r'; *bp++ = '\n'; *bp++ = ch; iState = STATE_NOTHING; break; case 8: // Action 8 - Emit CRLF and move to STATE_HAVE_CR. // *bp++ = '\r'; *bp++ = '\n'; iState = STATE_HAVE_CR; break; case 9: // Action 9 - Move to STATE_HAVE_CRLF_DOT. // iState = STATE_HAVE_CRLF_DOT; break; case 10: // Action 10 - Emit CRLF..+ch and move to STATE_NOTHING. // *bp++ = '\r'; *bp++ = '\n'; *bp++ = '.'; *bp++ = '.'; *bp++ = ch; iState = STATE_NOTHING; break; case 11: // Action 11 - Emit CRLF..CRLF.CRLF and terminate. // *bp++ = '\r'; *bp++ = '\n'; *bp++ = '.'; *bp++ = '.'; *bp++ = '\r'; *bp++ = '\n'; *bp++ = '.'; *bp++ = '\r'; *bp++ = '\n'; *bp++ = '\0'; return buf; break; case 12: // Action 12 - Move to STATE_HAVE_CRLF_DOT_CR. // iState = STATE_HAVE_CRLF_DOT_CR; break; case 13: // Action 13 - Emit CRLF.. and move to STATE_HAVE_CRLF. // *bp++ = '\r'; *bp++ = '\n'; *bp++ = '.'; *bp++ = '.'; iState = STATE_HAVE_CRLF; break; case 14: // Action 14 - Emit ..+ch and move to STATE_NOTHING. // *bp++ = '.'; *bp++ = '.'; *bp++ = ch; iState = STATE_NOTHING; break; case 15: // Action 15 - Emit ..CRLF.CRLF and terminate. // *bp++ = '.'; *bp++ = '.'; *bp++ = '\r'; *bp++ = '\n'; *bp++ = '.'; *bp++ = '\r'; *bp++ = '\n'; *bp++ = '\0'; return buf; break; case 16: // Action 16 - Move to STATE_HAVE_DOT_CR. // iState = STATE_HAVE_DOT_CR; break; case 17: // Action 17 - Emit .. and move to STATE_HAVE_CRLF. // *bp++ = '.'; *bp++ = '.'; iState = STATE_HAVE_CRLF; break; } } } // Transform CRLF runs to a space. // // ConvertCRLFtoSpace moved to stringutil.cpp (libmux.so). void do_plusemail(dbref executor, dbref cause, dbref enactor, int eval, int key, int nargs, UTF8 *arg1, UTF8 *arg2, const UTF8 *cargs[], int ncargs) { UNUSED_PARAMETER(cause); UNUSED_PARAMETER(enactor); UNUSED_PARAMETER(eval); UNUSED_PARAMETER(key); UNUSED_PARAMETER(nargs); UNUSED_PARAMETER(cargs); UNUSED_PARAMETER(ncargs); if ( !Wizard(executor) && ThrottleEmail(executor)) { notify(executor, M_("@email: Too many emails sent recently.")); return; } if ('\0' == mudconf.mail_server[0]) { notify(executor, M_("@email: Not configured")); return; } if (!arg1 || !*arg1) { notify(executor, M_("@email: I don’t know who you want to e-mail!")); return; } if (!arg2 || !*arg2) { notify(executor, M_("@email: Not sending an empty e-mail!")); return; } LBuf addy = LBuf_Src("mod_email_do_email.headers"); UTF8 *bp = addy.get(); safe_str(arg1, addy, &bp); *bp = '\0'; UTF8 *subject = reinterpret_cast(strchr(reinterpret_cast(addy.get()), '/')); if (subject) { *subject = '\0'; subject++; } else { subject = mudconf.mail_subject; } LBuf body = LBuf_Src("mod_email_do_email.body"); UTF8 *bodyptr = body.get(); mux_exec(arg2, LBUF_SIZE-1, body, &bodyptr, executor, executor, executor, EV_TOP | EV_STRIP_CURLY | EV_FCHECK | EV_EVAL, nullptr, 0); *bodyptr = '\0'; // GANL event-driven SMTP path. // // ConvertCRLFtoSpace() uses a single static buffer, so we must copy // the recipient result before calling it again for the subject. // UTF8 *pRecipient = ConvertCRLFtoSpace(addy); std::string recipientStr(reinterpret_cast(pRecipient)); UTF8 *pSubject = ConvertCRLFtoSpace(subject); g_GanlAdapter.start_email_send(executor, reinterpret_cast(recipientStr.c_str()), pSubject, EncodeBody(body)); }