Newer
Older
else
cmd[j++]=instr[i];
}
cmd[j]=0;
return(cmd);
}
#ifdef JAVASCRIPT
typedef struct {
SOCKET sock;
const char* log_prefix;
const char* proc_name;
} private_t;
static void
js_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report)
{
char line[64];
char file[MAX_PATH+1];
char* warning;
jsrefcount rc;
int log_level;
return;
if(report==NULL) {
lprintf(LOG_ERR,"%04d %s %s !JavaScript: %s"
, p->sock, p->log_prefix, p->proc_name, message);
return;
}
if(report->filename)
SAFEPRINTF(file," %s",report->filename);
else
file[0]=0;
if(report->lineno)
SAFEPRINTF(line," line %u",report->lineno);
else
line[0]=0;
if(JSREPORT_IS_WARNING(report->flags)) {
if(JSREPORT_IS_STRICT(report->flags))
warning="strict warning";
else
warning="warning";
log_level=LOG_WARNING;
} else {
log_level=LOG_ERR;
warning="";
rc=JS_SUSPENDREQUEST(cx);
lprintf(log_level,"%04d %s %s !JavaScript %s%s%s: %s"
,p->sock, p->log_prefix, p->proc_name
,warning ,file, line, message);
JS_RESUMEREQUEST(cx, rc);
static JSBool
js_log(JSContext *cx, uintN argc, jsval *arglist)
{
jsval *argv=JS_ARGV(cx, arglist);
uintN i=0;
int32 level=LOG_INFO;
jsrefcount rc;
char *lstr=NULL;
size_t lstr_sz=0;
JS_SET_RVAL(cx, arglist, JSVAL_VOID);
return(JS_FALSE);
if(JSVAL_IS_NUMBER(argv[i])) {
if(!JS_ValueToInt32(cx,argv[i++],&level))
return JS_FALSE;
}
for(; i<argc; i++) {
JSVALUE_TO_RASTRING(cx, argv[i], lstr, &lstr_sz, NULL);
HANDLE_PENDING(cx, lstr);
return(JS_TRUE);
rc=JS_SUSPENDREQUEST(cx);
lprintf(level,"%04d %s %s %s"
JS_SET_RVAL(cx, arglist, argv[i]);
JS_RESUMEREQUEST(cx, rc);
}
return(JS_TRUE);
}
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
static JSBool
js_alert(JSContext *cx, uintN argc, jsval *arglist)
{
jsval *argv=JS_ARGV(cx, arglist);
private_t* p;
jsrefcount rc;
char *line;
JS_SET_RVAL(cx, arglist, JSVAL_VOID);
if((p=(private_t*)JS_GetContextPrivate(cx))==NULL)
return(JS_FALSE);
JSVALUE_TO_MSTRING(cx, argv[0], line, NULL);
if(line==NULL)
return(JS_FALSE);
rc=JS_SUSPENDREQUEST(cx);
lprintf(LOG_ERR,"%04d %s %s %s"
,p->sock, p->log_prefix, p->proc_name, line);
free(line);
JS_RESUMEREQUEST(cx, rc);
JS_SET_RVAL(cx, arglist, argv[0]);
return(JS_TRUE);
}
static JSFunctionSpec js_global_functions[] = {
{"write", js_log, 0},
{"writeln", js_log, 0},
{"print", js_log, 0},
{"log", js_log, 0},
{"alert", js_alert, 1},
{0}
};
static BOOL
js_mailproc(SOCKET sock, client_t* client, user_t* user, struct mailproc* mailproc
,char* cmdline
,char* msgtxt_fname, char* newtxt_fname, char* logtxt_fname
,char* rcpt_addr
,char* rcptlst_fname, char* proc_err_fname
,char* sender, char* sender_addr, char* reverse_path, char* hello_name
,JSRuntime** js_runtime
,JSContext** js_cx
,JSObject** js_glob
,const char* log_prefix
)
{
char* p;
char fname[MAX_PATH+1];
char path[MAX_PATH+1];
char arg[MAX_PATH+1];
BOOL success=FALSE;
JSObject* argv;
jsuint argc;
JSObject* js_script;
js_callback_t js_callback;
jsval val;
jsval rval=JSVAL_VOID;
ZERO_VAR(js_callback);
SAFECOPY(fname,cmdline);
truncstr(fname," \t");
if(getfext(fname)==NULL) /* No extension specified, assume '.js' */
strcat(fname,".js");
SAFECOPY(path,fname);

rswindell
committed
if(getfname(path)==path) { /* No path specified, assume mods or exec dir */
SAFEPRINTF2(path,"%s%s",scfg.mods_dir,fname);

rswindell
committed
if(scfg.mods_dir[0]==0 || !fexist(path))
SAFEPRINTF2(path,"%s%s",scfg.exec_dir,fname);

rswindell
committed
}
*result = 0;
if(*js_runtime==NULL) {
lprintf(LOG_DEBUG,"%04d %s JavaScript: Creating runtime: %lu bytes\n"
,sock, log_prefix, startup->js.max_bytes);
if((*js_runtime = jsrt_GetNew(startup->js.max_bytes, 1000, __FILE__, __LINE__))==NULL)
return FALSE;
if(*js_cx==NULL) {
lprintf(LOG_DEBUG,"%04d %s JavaScript: Initializing context (stack: %lu bytes)\n"
,sock, log_prefix, startup->js.cx_stack);
if((*js_cx = JS_NewContext(*js_runtime, startup->js.cx_stack))==NULL)
return FALSE;
}
JS_BEGINREQUEST(*js_cx);
JS_SetErrorReporter(*js_cx, js_ErrorReporter);
priv.sock=sock;
priv.log_prefix=log_prefix;
priv.proc_name=mailproc->name;
JS_SetContextPrivate(*js_cx, &priv);
if(*js_glob==NULL) {
/* Global Objects (including system, js, client, Socket, MsgBase, File, User, etc. */
if(!js_CreateCommonObjects(*js_cx, &scfg, &scfg, NULL
,&js_callback /* js */
,client, sock, -1 /* client */
,js_glob
))
if(!JS_DefineFunctions(*js_cx, *js_glob, js_global_functions))
break;
if(!js_CreateUserObjects(*js_cx, *js_glob, &scfg, user, client, NULL, NULL))
/* Mailproc "API" filenames */
JS_DefineProperty(*js_cx, *js_glob, "message_text_filename"
,STRING_TO_JSVAL(JS_NewStringCopyZ(*js_cx,msgtxt_fname))
,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY);
JS_DefineProperty(*js_cx, *js_glob, "new_message_text_filename"
,STRING_TO_JSVAL(JS_NewStringCopyZ(*js_cx,newtxt_fname))
,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY);
JS_DefineProperty(*js_cx, *js_glob, "log_text_filename"
,STRING_TO_JSVAL(JS_NewStringCopyZ(*js_cx,logtxt_fname))
,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY);
JS_DefineProperty(*js_cx, *js_glob, "recipient_address"
,STRING_TO_JSVAL(JS_NewStringCopyZ(*js_cx,rcpt_addr))
,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY);
JS_DefineProperty(*js_cx, *js_glob, "recipient_list_filename"
,STRING_TO_JSVAL(JS_NewStringCopyZ(*js_cx,rcptlst_fname))
,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY);
JS_DefineProperty(*js_cx, *js_glob, "processing_error_filename"
,STRING_TO_JSVAL(JS_NewStringCopyZ(*js_cx,proc_err_fname))
,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY);
JS_DefineProperty(*js_cx, *js_glob, "sender_name"
,STRING_TO_JSVAL(JS_NewStringCopyZ(*js_cx,sender))
,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY);
JS_DefineProperty(*js_cx, *js_glob, "sender_address"
,STRING_TO_JSVAL(JS_NewStringCopyZ(*js_cx,sender_addr))
,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY);
JS_DefineProperty(*js_cx, *js_glob, "reverse_path"
,STRING_TO_JSVAL(JS_NewStringCopyZ(*js_cx,reverse_path))
,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY);
JS_DefineProperty(*js_cx, *js_glob, "hello_name"
,STRING_TO_JSVAL(JS_NewStringCopyZ(*js_cx,hello_name))
,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY);
}
if((js_scope=JS_NewObject(*js_cx, NULL, NULL, *js_glob))==NULL)
break;
/* Convert command-line to argv/argc */
argv=JS_NewArrayObject(*js_cx, 0, NULL);
JS_DefineProperty(*js_cx, js_scope, "argv", OBJECT_TO_JSVAL(argv)
,NULL,NULL,JSPROP_READONLY|JSPROP_ENUMERATE);
p=cmdline;
FIND_WHITESPACE(p);
SKIP_WHITESPACE(p);
for(argc=0;*p;argc++) {
SAFECOPY(arg,p);
truncstr(arg," \t");
val=STRING_TO_JSVAL(JS_NewStringCopyZ(*js_cx,arg));
if(!JS_SetElement(*js_cx, argv, argc, &val))
break;
FIND_WHITESPACE(p);
SKIP_WHITESPACE(p);
}
JS_DefineProperty(*js_cx, js_scope, "argc", INT_TO_JSVAL(argc)
,NULL,NULL,JSPROP_READONLY|JSPROP_ENUMERATE);
lprintf(LOG_DEBUG,"%04d %s Evaluating: %s"
,sock, log_prefix, mailproc->eval);
js_script=JS_CompileScript(*js_cx, js_scope, mailproc->eval, strlen(mailproc->eval), NULL, 1);
} else {
lprintf(LOG_DEBUG,"%04d %s Executing: %s"
if((js_script=JS_CompileFile(*js_cx, js_scope, path)) != NULL)
js_PrepareToExecute(*js_cx, js_scope, path, /* startup_dir: */NULL, js_scope);
/* ToDo: Set operational callback */
success=JS_ExecuteScript(*js_cx, js_scope, js_script, &rval);
JS_GetProperty(*js_cx, js_scope, "exit_code", &rval);
if(rval!=JSVAL_VOID && JSVAL_IS_NUMBER(rval))
JS_ValueToInt32(*js_cx,rval,result);
js_EvalOnExit(*js_cx, js_scope, &js_callback);
JS_ReportPendingException(*js_cx);
} while(0);
JS_ENDREQUEST(*js_cx);
return(success);
}
void js_cleanup(JSRuntime* js_runtime, JSContext* js_cx, JSObject** js_glob)
if(js_cx!=NULL) {
JS_BEGINREQUEST(js_cx);
JS_RemoveObjectRoot(js_cx, js_glob);
JS_ENDREQUEST(js_cx);
}
if(js_runtime!=NULL)
jsrt_Release(js_runtime);
}
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
bool strIsPlainAscii(const char* str)
{
for(const char* p = str; *p != 0; p++) {
if(*p < 0)
return false;
}
return true;
}
static size_t strStartsWith_i(const char* buf, const char* match)
{
size_t len = strlen(match);
if (strnicmp(buf, match, len) == 0)
return len;
return 0;
}
/* Decode RFC2047 'Q' encoded-word (in-place), "similar to" Quoted-printable */
char* mimehdr_q_decode(char* buf)
{
uchar* p=(uchar*)buf;
uchar* dest=p;
for(;*p != 0; p++) {
if(*p == '=') {
p++;
if(isxdigit(*p) && isxdigit(*(p + 1))) {
uchar ch = HEX_CHAR_TO_INT(*p) << 4;
p++;
ch |= HEX_CHAR_TO_INT(*p);
if(ch >= ' ')
*dest++ = ch;
} else { /* bad encoding */
*dest++ = '=';
*dest++ = *p;
}
}
else if(*p == '_')
*dest++ = ' ';
else if(*p >= '!' && *p <= '~')
*dest++ = *p;
}
*dest=0;
return buf;
}
enum mimehdr_charset {
MIMEHDR_CHARSET_ASCII,
MIMEHDR_CHARSET_UTF8,
MIMEHDR_CHARSET_CP437,
MIMEHDR_CHARSET_OTHER
};
static enum mimehdr_charset mimehdr_charset_decode(const char* str)
{
if (strStartsWith_i(str, "ascii?") || strStartsWith_i(str, "us-ascii?"))
return MIMEHDR_CHARSET_ASCII;
if (strStartsWith_i(str, "utf-8?"))
return MIMEHDR_CHARSET_UTF8;
if (strStartsWith_i(str, "cp437?") || strStartsWith_i(str, "ibm437?"))
return MIMEHDR_CHARSET_CP437;
return MIMEHDR_CHARSET_OTHER;
}
// Replace unnecessary MIME (RFC 2047) "encoded-words" with their decoded-values
// Returns true if the last 'atom' in 'str' was an 'encoded-word' that was normalized
bool normalize_hfield_value(char* str)
{
bool normalized = false;
if (str == NULL)
return false;
char* buf = strdup(str);
if (buf == NULL)
return false;
char* state = NULL;
*str = 0;
char tmp[256]; // "An 'encoded-word' may not be more than 75 characters long"
for(char* p = strtok_r(buf, " \t", &state); p != NULL; p = strtok_r(NULL, " \t", &state)) {
if(*str)
strcat(str, " ");
char* end = lastchar(p);
if(*p == '=' && *(p+1) == '?' && *(end - 1) == '?' && *end == '=' && end - p < sizeof(tmp)) {
char* cp = p + 2;
enum mimehdr_charset charset = mimehdr_charset_decode(cp);
FIND_CHAR(cp, '?'); // we don't actually care what the charset is
if(*cp == '?' && *(cp + 1) != 0 && *(cp + 2) == '?') {
cp++;
char encoding = toupper(*cp);
cp += 2;
SAFECOPY(tmp, cp);
char* tp = lastchar(tmp);
*(tp - 1) = 0; // remove the terminating "?="
if(encoding == 'Q') {
mimehdr_q_decode(tmp);
if(charset == MIMEHDR_CHARSET_UTF8)
utf8_normalize_str(tmp);
if(charset == MIMEHDR_CHARSET_CP437 || strIsPlainAscii(tmp))
p = tmp;
}
else if(encoding == 'B'
&& b64_decode(tmp, sizeof(tmp), tmp, strlen(tmp)) > 0) { // base64
if(charset == MIMEHDR_CHARSET_UTF8)
utf8_normalize_str(tmp);
if(charset == MIMEHDR_CHARSET_CP437 || strIsPlainAscii(tmp))
p = tmp;
}
}
}
normalized = (p == tmp);
strcat(str, p);
}
free(buf);
return normalized;
}
static char* get_header_field(char* buf, char* name, size_t maxlen)
{
size_t len;
if(buf[0]<=' ') /* folded header */
return NULL;
if((p=strchr(buf,':'))==NULL)
return NULL;
len = p-buf;
if(len >= maxlen)
len = maxlen-1;
truncsp(name);
p++; /* skip colon */
SKIP_WHITESPACE(p);
return p;
}
static int parse_header_field(char* buf, smbmsg_t* msg, ushort* type, bool* normalized)
{
char* p;
char* tp;
char field[128];
int len;
ushort nettype;
if(buf[0]<=' ' && *type!=UNKNOWN) { /* folded header, append to previous */
p=buf;
truncsp(p);
if(*type==RFC822HEADER || *type==SMTPRECEIVED)
smb_hfield_append_str(msg,*type,"\r\n");
else { /* Unfold other common header field types (e.g. Subject, From, To) */
if(!*normalized)
smb_hfield_append_str(msg,*type," ");
SKIP_WHITESPACE(p);
*normalized = normalize_hfield_value(p);
return smb_hfield_append_str(msg, *type, p);
}
if((p=strchr(buf,':'))==NULL)
return smb_hfield_str(msg, *type=RFC822HEADER, buf);
len=(ulong)p-(ulong)buf;
if(len>sizeof(field)-1)
len=sizeof(field)-1;
sprintf(field,"%.*s",len,buf);
truncsp(field);
p++; /* skip colon */
SKIP_WHITESPACE(p);
truncsp(p);
if(!stricmp(field, "TO"))
return smb_hfield_str(msg, *type=RFC822TO, p);
if(!stricmp(field, "REPLY-TO")) {
smb_hfield_str(msg, *type=RFC822REPLYTO, p);
if((tp=strrchr(p,'<'))!=NULL) {
tp++;
truncstr(tp,">");
p=tp;
}
nettype=NET_INTERNET;
smb_hfield(msg, REPLYTONETTYPE, sizeof(nettype), &nettype);
return smb_hfield_str(msg, *type=REPLYTONETADDR, p);
}
if(!stricmp(field, "FROM"))
return smb_hfield_str(msg, *type=RFC822FROM, p);
if(!stricmp(field, "ORGANIZATION"))
return smb_hfield_str(msg, *type=SENDERORG, p);
if(!stricmp(field, "DATE")) {
msg->hdr.when_written=rfc822date(p);
*type=UNKNOWN;
return SMB_SUCCESS;
}
if(!stricmp(field, "MESSAGE-ID"))
return smb_hfield_str(msg, *type=RFC822MSGID, p);
if(!stricmp(field, "IN-REPLY-TO"))
return smb_hfield_str(msg, *type=RFC822REPLYID, p);
if(!stricmp(field, "CC"))
return smb_hfield_str(msg, *type=SMB_CARBONCOPY, p);
if(!stricmp(field, "RECEIVED"))
return smb_hfield_str(msg, *type=SMTPRECEIVED, p);
if(!stricmp(field, "RETURN-PATH")) {
*type=UNKNOWN;
return SMB_SUCCESS; /* Ignore existing "Return-Path" header fields */
}
if(!stricmp(field, "X-PRIORITY")) {
msg->hdr.priority = atoi(p);
if(msg->hdr.priority > SMB_PRIORITY_LOWEST)
msg->hdr.priority = SMB_PRIORITY_UNSPECIFIED;
*type=UNKNOWN;
return SMB_SUCCESS;
}
/* Fall-through */
return smb_hfield_str(msg, *type=RFC822HEADER, buf);
}
static int chk_received_hdr(SOCKET socket,const char* prot,const char *buf,IN_ADDR *dnsbl_result, char *dnsbl, char *dnsbl_ip)
{
char host_name[128];
char *fromstr;
char ip[16];
char *p;
char *p2;
if(fromstr==NULL)
return(0);
strlwr(fromstr);
do {
p=strstr(fromstr,"from ");
if(p==NULL)
break;
p+=4;
SKIP_WHITESPACE(p);
if(*p==0)
break;
p2=host_name;
for(;*p && !isspace((unsigned char)*p) && p2<host_name+126;p++) {
*p2++=*p;
}
*p2=0;
p=strtok_r(fromstr,"[",&last);
if(p==NULL)
break;
p=strtok_r(NULL,"]",&last);
if(p==NULL)
break;
if(strnicmp("IPv6:", p, 5)) {
p+=5;
SKIP_WHITESPACE(p);
memset(&ai, 0, sizeof(ai));
ai.ai_flags = AI_NUMERICHOST|AI_NUMERICSERV|AI_PASSIVE;
if(getaddrinfo(p, NULL, &ai, &res)!=0)
break;
freeaddrinfo(res);
} else {
freeaddrinfo(res);
}
else {
strncpy(ip,p,16);
ip[15]=0;
addr.in.sin_family=AF_INET;
addr.in.sin_addr.s_addr=inet_addr(ip);
lprintf(LOG_DEBUG,"%04d %s DNSBL checking received header address %s [%s]",socket,prot,host_name,ip);
if((dnsbl_result->s_addr=dns_blacklisted(socket,prot,&addr,host_name,dnsbl,dnsbl_ip))!=0)
lprintf(LOG_NOTICE,"%04d %s BLACKLISTED SERVER on %s: %s [%s] = %s"
,socket, prot, dnsbl, host_name, ip, inet_ntoa(*dnsbl_result));
} while(0);
free(fromstr);
return(dnsbl_result->s_addr);
}
static void strip_char(char* str, char ch)
{
char* src;
char* p;
char* tmp = strdup(str);
if(tmp == NULL)
return;
p=tmp;
for(src = str; *src; src++) {
if(*src != ch)
*(p++) = *src;
}
*p=0;
strcpy(str, tmp);
free(tmp);
}
static void parse_mail_address(char* p
,char* name, size_t name_len
,char* addr, size_t addr_len)
{
char* tp;
char tmp[128];
SKIP_WHITESPACE(p);
/* Get the address */
if((tp=strchr(p,'<'))!=NULL)
tp++;
else
tp=p;
SKIP_WHITESPACE(tp);
truncstr(addr,">( ");
SAFECOPY(tmp,p);
p=tmp;
/* Get the "name" (if possible) */
if((tp=strchr(p,'"'))!=NULL) { /* name in quotes? */
p=tp+1;
tp=strchr(p,'"');
} else if((tp=strchr(p,'('))!=NULL) { /* name in parenthesis? */
p=tp+1;
tp=strchr(p,')');
} else if(*p=='<') { /* address in brackets? */
p++;
tp=strchr(p,'>');
} else /* name, then address in brackets */
tp=strchr(p,'<');
if(tp) *tp=0;
truncsp(name);
}
/* Decode quoted-printable content-transfer-encoded text */
/* Ignores (strips) unsupported ctrl chars and non-ASCII chars */
/* Does not enforce 76 char line length limit */
static char* qp_decode(char* buf)
{
uchar* p=(uchar*)buf;
uchar* dest=p;
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
for(;;p++) {
if(*p==0) {
*dest++='\r';
*dest++='\n';
break;
}
if(*p==' ' || (*p>='!' && *p<='~' && *p!='=') || *p=='\t')
*dest++=*p;
else if(*p=='=') {
p++;
if(*p==0) /* soft link break */
break;
if(isxdigit(*p) && isxdigit(*(p+1))) {
char hex[3];
hex[0]=*p;
hex[1]=*(p+1);
hex[2]=0;
/* ToDo: what about encoded NULs and the like? */
*dest++=(uchar)strtoul(hex,NULL,16);
p++;
} else { /* bad encoding */
*dest++='=';
*dest++=*p;
}
}
}
*dest=0;
return buf;
}
static BOOL checktag(scfg_t *scfg, char *tag, uint usernum)
{
char fname[MAX_PATH+1];
if(tag==NULL)
return(FALSE);
SAFEPRINTF2(fname,"%suser/%04d.smtpblock",scfg->data_dir,usernum);
return(findstr(tag, fname));
}
static BOOL smtp_splittag(char *in, char **name, char **tag)
{
char *last;
if(in==NULL)
return(FALSE);
*name=strtok_r(in, "#", &last);
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
if(*name) {
*tag=strtok_r(NULL, "", &last);
return(TRUE);
}
return(FALSE);
}
static uint smtp_matchuser(scfg_t *scfg, char *str, BOOL aliases, BOOL datdupe)
{
char *user=strdup(str);
char *name;
char *tag=NULL;
uint usernum=0;
if(!user)
return(0);
if(!smtp_splittag(user, &name, &tag))
goto end;
if(datdupe)
usernum=userdatdupe(scfg, 0, U_NAME, LEN_NAME, name, /* del */FALSE, /* next */FALSE, NULL, NULL);
else
usernum=matchuser(scfg, name, aliases);
if(!usernum)
goto end;
if(checktag(scfg, tag, usernum))
usernum=UINT_MAX;
end:
free(user);
return(usernum);
}
#define WITH_ESMTP (1<<0)
#define WITH_AUTH (1<<1)
#define WITH_TLS (1<<2)
char *with_clauses[] = {
"SMTP", // No WITH_*
"ESMTP", // WITH_ESMTP
"SMTP", // WITH_AUTH
"ESMTPA", // WITH_ESMTP | WITH_AUTH
"SMTP", // WITH_TLS
"ESMTPS", // WITH_ESMTP | WITH_TLS
"SMTP", // WITH_TLS | WITH_AUTH
"ESMTPSA" // WITH_TLS | WITH_AUTH | WITH_ESMTP
};
static void smtp_thread(void* arg)
{
int i,j;
char path[MAX_PATH+1];
char value[INI_MAX_VALUE_LEN];
str_list_t sec_list;
char* section;
char buf[1024],*p,*tp,*cp;
char hdrfield[512];
char alias_buf[128];
char forward_path[128];
char qwkid[32];
char rcpt_to[128];
char rcpt_name[128];
char rcpt_addr[128];

rswindell
committed
char sender[128];
char sender_ext[128];

rswindell
committed
char sender_addr[128];
char user_name[128];
char user_pass[128];
char relay_list[MAX_PATH+1];
char domain_list[MAX_PATH+1];
char spam_bait[MAX_PATH+1];
char spam_block[MAX_PATH+1];
char spam_block_exemptions[MAX_PATH+1];
BOOL spam_block_exempt=FALSE;
char host_ip[INET6_ADDRSTRLEN];
char server_ip[INET6_ADDRSTRLEN];

rswindell
committed
char* telegram_buf;
char* msgbuf;
char challenge[256];
char response[128];
char secret[64];
char md5_data[384];
uchar digest[MD5_DIGEST_SIZE];
char dest_host[128];
char* errmsg;
ushort dest_port;
ushort hfield_type;
ushort agent;
ulong hdr_lines=0;
ulong hdr_len=0;
ulong badcmds=0;

rswindell
committed
BOOL telegram=FALSE;
BOOL forward=FALSE;
BOOL no_forward=FALSE;
BOOL auth_login;
BOOL routed=FALSE;
BOOL dnsbl_recvhdr;
BOOL msg_handled;
uint subnum=INVALID_SUB;
char msgtxt_fname[MAX_PATH+1];
char newtxt_fname[MAX_PATH+1];
char logtxt_fname[MAX_PATH+1];
char rcptlst_fname[MAX_PATH+1];
ushort rcpt_count=0;
FILE* proc_out;
char proc_err_fname[MAX_PATH+1];
char session_id[MAX_PATH+1];
FILE* spy=NULL;
SOCKET socket;
int smb_error;
smbmsg_t msg;
smbmsg_t newmsg;
user_t user;
user_t relay_user;

rswindell
committed
node_t node;
client_t client;
smtp_t smtp=*(smtp_t*)arg;
IN_ADDR dnsbl_result;
int mailproc_match;
JSRuntime* js_runtime=NULL;
JSContext* js_cx=NULL;
JSObject* js_glob=NULL;
struct mailproc* mailproc;
int session = -1;
BOOL nodelay=TRUE;
ulong nb = 0;
unsigned with_val;
int level;
int cstat;
char *estr;
enum {
SMTP_STATE_INITIAL
,SMTP_STATE_HELO
,SMTP_STATE_DATA_HEADER
,SMTP_STATE_DATA_BODY
} state = SMTP_STATE_INITIAL;
enum {
SMTP_CMD_NONE
,SMTP_CMD_MAIL
,SMTP_CMD_SEND
,SMTP_CMD_SOML
,SMTP_CMD_SAML
} cmd = SMTP_CMD_NONE;
enum {
ENCODING_NONE
,ENCODING_BASE64
,ENCODING_QUOTED_PRINTABLE
} content_encoding = ENCODING_NONE;
SetThreadName("sbbs/smtp");
thread_up(TRUE /* setuid */);
free(arg);
socket=smtp.socket;
client.protocol = smtp.tls_port ? "SMTPS" : "SMTP";
lprintf(LOG_DEBUG,"%04d %s Session thread started", socket, client.protocol);
if(startup->inbound_sound[0] && !(startup->options&MAIL_OPT_MUTE))
PlaySound(startup->inbound_sound, NULL, SND_ASYNC|SND_FILENAME);
addr_len=sizeof(server_addr);
if(smtp.tls_port) {
if (get_ssl_cert(&scfg, &estr, &level) == -1) {
if (estr) {
lprintf(level, "%04d %s !%s", socket, client.protocol, estr);
free_crypt_attrstr(estr);
mail_close_socket(&socket, &session);
thread_down();
return;
}
if ((cstat = cryptCreateSession(&session, CRYPT_UNUSED, CRYPT_SESSION_SSL_SERVER)) != CRYPT_OK) {
GCES(cstat, client.protocol, socket, CRYPT_UNUSED, "creating session");
mail_close_socket(&socket, &session);
thread_down();
return;
}
if ((cstat = cryptSetAttribute(session, CRYPT_SESSINFO_SSL_OPTIONS, CRYPT_SSLOPTION_DISABLE_CERTVERIFY)) != CRYPT_OK) {
GCES(cstat, client.protocol, socket, CRYPT_UNUSED, "disabling certificate verification");
mail_close_socket(&socket, &session);
thread_down();
return;
}
lock_ssl_cert();
if ((cstat = cryptSetAttribute(session, CRYPT_SESSINFO_PRIVATEKEY, scfg.tls_certificate)) != CRYPT_OK) {
unlock_ssl_cert();
GCES(cstat, client.protocol, socket, CRYPT_UNUSED, "setting private key");
mail_close_socket(&socket, &session);
thread_down();
return;
}
nodelay = TRUE;
setsockopt(socket,IPPROTO_TCP,TCP_NODELAY,(char*)&nodelay,sizeof(nodelay));
nb=0;
ioctlsocket(socket,FIONBIO,&nb);
if ((cstat = cryptSetAttribute(session, CRYPT_SESSINFO_NETWORKSOCKET, socket)) != CRYPT_OK) {
unlock_ssl_cert();
GCES(cstat, client.protocol, socket, CRYPT_UNUSED, "setting network socket");
mail_close_socket(&socket, &session);
thread_down();
return;
}
if ((cstat = cryptSetAttribute(session, CRYPT_SESSINFO_ACTIVE, 1)) != CRYPT_OK) {
unlock_ssl_cert();
GCES(cstat, client.protocol, socket, CRYPT_UNUSED, "setting session active");
mail_close_socket(&socket, &session);
thread_down();
return;
}
unlock_ssl_cert();
if (startup->max_inactivity) {
if ((cstat = cryptSetAttribute(session, CRYPT_OPTION_NET_READTIMEOUT, startup->max_inactivity)) != CRYPT_OK) {
GCES(cstat, client.protocol, socket, CRYPT_UNUSED, "setting read timeout");