mirror of
https://github.com/brazilofmux/tinymux
synced 2026-08-13 00:23:11 -04:00
Next notify slice: @mail and @email player feedback.
mail.cpp: convert constant raw_notify T("…") to M_ (~98 sites). The
static mailmsg[] invalid-spec table uses N_() for extraction and
mail_msg() for runtime gettext so the table does not need a refresh
pass. Leave tprintf formats, DASH_LINE/MAIL_LINE decoration, and logs
as T().
plusemail.cpp: four constant @email denials → M_().
Half-mark pass clean. Regenerate pot/xx without fuzzy entries.
345 lines
9.8 KiB
C++
345 lines
9.8 KiB
C++
/*! \file plusemail.cpp
|
||
* \brief Quicky module implementing \@email function.
|
||
*
|
||
* \author Rachel Blackman <sparks@noderunner.net>
|
||
*
|
||
* \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 <string>
|
||
|
||
// Encode mail body such that CRLF.CRLF only appears at the end of the
|
||
// message. Here are some example transforms.
|
||
//
|
||
// <CR><LF>.<anytext><CR><LF> --> <CR><LF>..<CR><LF>
|
||
// <BOM>.<anytext><CR><LF> --> <BOM>..<anytext><CR><LF>
|
||
// <anytext><EOM> --> <anytext><CR><LF>.<CR><LF>
|
||
// <anytext><CR><LF><EOM> --> <anytext><CR><LF>.<CR><LF>
|
||
//
|
||
// <CR> and <LF> are only allowed to occur as the <CR><LF> 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<unsigned char>(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<UTF8 *>(strchr(reinterpret_cast<char *>(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<const char*>(pRecipient));
|
||
UTF8 *pSubject = ConvertCRLFtoSpace(subject);
|
||
|
||
g_GanlAdapter.start_email_send(executor,
|
||
reinterpret_cast<const UTF8*>(recipientStr.c_str()),
|
||
pSubject, EncodeBody(body));
|
||
|
||
}
|