mailsrvr.c 211 KB
Newer Older
1 2 3 4 5 6
/* Synchronet Mail (SMTP/POP3) server and sendmail threads */

/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
7
 * Copyright Rob Swindell - http://www.synchro.net/copyright.html			*
8 9 10 11 12 13 14 15 16 17 18 19 20 21
 *																			*
 * This program is free software; you can redistribute it and/or			*
 * modify it under the terms of the GNU General Public License				*
 * as published by the Free Software Foundation; either version 2			*
 * of the License, or (at your option) any later version.					*
 * See the GNU General Public License for more details: gpl.txt or			*
 * http://www.fsf.org/copyleft/gpl.html										*
 *																			*
 * For Synchronet coding style and modification guidelines, see				*
 * http://www.synchro.net/source.html										*
 *																			*
 * Note: If this box doesn't appear square, then you need to fix your tabs.	*
 ****************************************************************************/

rswindell's avatar
rswindell committed
22
/* ANSI C Library headers */
deuce's avatar
deuce committed
23
#include <limits.h>			/* UINT_MAX */
rswindell's avatar
rswindell committed
24
#include <stdio.h>
25 26 27 28 29
#include <stdlib.h>			/* ltoa in GNU C lib */
#include <stdarg.h>			/* va_list */
#include <string.h>			/* strrchr */
#include <fcntl.h>			/* Open flags */
#include <errno.h>			/* errno */
rswindell's avatar
rswindell committed
30 31

/* Synchronet-specific headers */
32
#undef SBBS	/* this shouldn't be defined unless building sbbs.dll/libsbbs.so */
33
#include "sbbs.h"
rswindell's avatar
rswindell committed
34
#include "mailsrvr.h"
35
#include "utf8.h"
36
#include "mime.h"
37
#include "md5.h"
38
#include "crc32.h"
39
#include "base64.h"
40
#include "ini_file.h"
41
#include "netwrap.h"	/* getNameServerList() */
42
#include "xpendian.h"
43
#include "js_rtpool.h"
44
#include "js_request.h"
deuce's avatar
deuce committed
45
#include "multisock.h"
46 47
#include "ssl.h"
#include "cryptlib.h"
48 49
#include "git_branch.h"
#include "git_hash.h"
50

rswindell's avatar
rswindell committed
51
/* Constants */
52
static const char*	server_name="Synchronet Mail Server";
53 54
#define FORWARD			"forward:"
#define NO_FORWARD		"local:"
55
#define NO_SPAM			"#nospam"
56

57 58
int dns_getmx(char* name, char* mx, char* mx2
			  ,DWORD intf, DWORD ip_addr, BOOL use_tcp, int timeout);
59

60 61
#define pop_error		"-ERR System Error: %s, try again later"
#define pop_auth_error	"-ERR Authentication failure"
62 63
#define ok_rsp			"250 OK"
#define auth_ok			"235 User Authenticated"
64
#define smtp_error		"421 System Error: %s, try again later"
65 66 67 68 69
#define insuf_stor		"452 Insufficient system storage"
#define badarg_rsp 		"501 Bad argument"
#define badseq_rsp		"503 Bad sequence of commands"
#define badauth_rsp		"535 Authentication failure"
#define badrsp_err		"%s replied with:\r\n\"%s\"\r\ninstead of the expected reply:\r\n\"%s ...\""
70

71
#define TIMEOUT_THREAD_WAIT		60		/* Seconds */
72
#define DNSBL_THROTTLE_VALUE	1000	/* Milliseconds */
73
#define SMTP_MAX_BAD_CMDS		9
74 75 76 77 78

#define STATUS_WFC	"Listening"

static mail_startup_t* startup=NULL;
static scfg_t	scfg;
79
static char*	text[TOTAL_TEXT];
deuce's avatar
deuce committed
80 81
static struct xpms_set	*mail_set=NULL;
static BOOL terminated=FALSE;
82 83
static protected_uint32_t active_clients;
static protected_uint32_t thread_count;
84 85 86 87
static volatile int		active_sendmail=0;
static volatile BOOL	sendmail_running=FALSE;
static volatile BOOL	terminate_server=FALSE;
static volatile BOOL	terminate_sendmail=FALSE;
88
static sem_t	sendmail_wakeup_sem;
89
static volatile time_t	uptime;
90 91
static str_list_t recycle_semfiles;
static str_list_t shutdown_semfiles;
92
static int		mailproc_count;
93
static js_server_props_t js_server_props;
94
static link_list_t current_logins;
95
static link_list_t current_connections;
96
static bool savemsg_mutex_created = false;
97
static pthread_mutex_t savemsg_mutex;
98 99 100 101 102 103

static const char* servprot_smtp = "SMTP";
static const char* servprot_submission = "SMTP";
static const char* servprot_submissions = "SMTPS";
static const char* servprot_pop3 = "POP3";
static const char* servprot_pop3s = "POP3S";
104

105
struct {
106 107 108
	volatile ulong	sockets;
	volatile ulong	errors;
	volatile ulong	crit_errors;
109
	volatile ulong	connections_exceeded;
110 111 112 113 114
	volatile ulong	connections_ignored;
	volatile ulong	connections_refused;
	volatile ulong	connections_served;
	volatile ulong	pop3_served;
	volatile ulong	smtp_served;
115
	/* SMTP: */
116 117 118 119
	volatile ulong	sessions_refused;
	volatile ulong	msgs_ignored;
	volatile ulong	msgs_refused;
	volatile ulong	msgs_received;
120 121
} stats;

122
struct mailproc {
rswindell's avatar
rswindell committed
123
	char		name[INI_MAX_VALUE_LEN];
124
	char		cmdline[INI_MAX_VALUE_LEN];
rswindell's avatar
rswindell committed
125
	char		eval[INI_MAX_VALUE_LEN];
126
	str_list_t	to;
127
	str_list_t	from;
128 129
	BOOL		passthru;
	BOOL		native;
130
	BOOL		ignore_on_error;	/* Ignore mail message if cmdline fails */
131
	BOOL		disabled;
132 133
	BOOL		process_spam;
	BOOL		process_dnsbl;
134
	uint8_t*	ar;
135
	ulong		handled;			/* counter (for stats display) */
136
} *mailproc_list;
137 138 139

typedef struct {
	SOCKET			socket;
deuce's avatar
deuce committed
140 141
	union xp_sockaddr	client_addr;
	socklen_t		client_addr_len;
142
	BOOL			tls_port;
143 144
} smtp_t,pop3_t;

deuce's avatar
deuce committed
145
#define GCES(status, server, sock, sess, action) do {                             \
deuce's avatar
deuce committed
146 147
	char *GCES_estr;                                                               \
	int GCES_level;                                                                 \
deuce's avatar
deuce committed
148
	get_crypt_error_string(status, sess, &GCES_estr, action, &GCES_level);  \
deuce's avatar
deuce committed
149
	if (GCES_estr) {                                                                  \
150 151
		if(GCES_level < startup->tls_error_level)                                     \
			GCES_level = startup->tls_error_level;                                     \
152
		lprintf(GCES_level, "%04d %s %s", sock, server, GCES_estr);                     \
deuce's avatar
deuce committed
153
		free_crypt_attrstr(GCES_estr);                                                  \
154 155 156
	}                                                                                    \
} while(0)

deuce's avatar
deuce committed
157
#define GCESH(status, server, sock, host, sess, action) do {                      \
deuce's avatar
deuce committed
158 159
	char *GCES_estr;                                                               \
	int GCES_level;                                                                 \
deuce's avatar
deuce committed
160
	get_crypt_error_string(status, sess, &GCES_estr, action, &GCES_level);  \
deuce's avatar
deuce committed
161
	if (GCES_estr) {                                                                  \
162 163
		if(GCES_level < startup->tls_error_level)                                     \
			GCES_level = startup->tls_error_level;                                    \
164 165 166 167 168
		lprintf(GCES_level, "%04d %s [%s] %s", sock, server, host, GCES_estr);         \
		free_crypt_attrstr(GCES_estr);                                                  \
	}                                                                                    \
} while(0)

169 170 171 172 173 174 175 176 177
#define GCESHL(status, server, sock, host, log_level, sess, action) do {						\
	char *GCES_estr;																			\
	int GCES_level;																				\
	get_crypt_error_string(status, sess, &GCES_estr, action, &GCES_level);						\
	if (GCES_estr) {																			\
		lprintf((log_level < startup->tls_error_level) ? startup->tls_error_level : log_level	\
			, "%04d %s [%s] %s", sock, server, host, GCES_estr);								\
		free_crypt_attrstr(GCES_estr);															\
	}																							\
178 179
} while(0)

180 181 182
#if defined(__GNUC__)   // Catch printf-format errors with lprintf
static int lprintf(int level, const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
#endif
183
static int lprintf(int level, const char *fmt, ...)
184 185 186 187 188
{
	va_list argptr;
	char sbuf[1024];

	va_start(argptr,fmt);
189 190
    vsnprintf(sbuf,sizeof(sbuf),fmt,argptr);
	sbuf[sizeof(sbuf)-1]=0;
191
    va_end(argptr);
192

193
	if(level <= LOG_ERR) {
194 195
		char errmsg[sizeof(sbuf)+16];
		SAFEPRINTF(errmsg, "mail %s", sbuf);
196
		errorlog(&scfg, level, startup==NULL ? NULL:startup->host_name,errmsg), stats.errors++;
197
		if(startup!=NULL && startup->errormsg!=NULL)
198
			startup->errormsg(startup->cbdata,level,errmsg);
199
	}
200 201 202

	if(level <= LOG_CRIT)
		stats.crit_errors++;
203 204 205 206 207 208 209 210 211

    if(startup==NULL || startup->lputs==NULL || level > startup->log_level)
		return(0);

#if defined(_WIN32)
	if(IsBadCodePtr((FARPROC)startup->lputs))
		return(0);
#endif

212
    return(startup->lputs(startup->cbdata, level, condense_whitespace(sbuf)));
213 214 215 216 217
}

#ifdef _WINSOCKAPI_

static WSADATA WSAData;
218
#define SOCKLIB_DESC WSAData.szDescription
219
static BOOL WSAInitialized=FALSE;
220 221 222 223 224 225

static BOOL winsock_startup(void)
{
	int		status;             /* Status Code */

    if((status = WSAStartup(MAKEWORD(1,1), &WSAData))==0) {
226
		lprintf(LOG_DEBUG,"%s %s",WSAData.szDescription, WSAData.szSystemStatus);
227
		WSAInitialized=TRUE;
228 229 230
		return (TRUE);
	}

231
    lprintf(LOG_CRIT,"!WinSock startup ERROR %d", status);
232 233 234 235 236
	return (FALSE);
}

#else /* No WINSOCK */

rswindell's avatar
rswindell committed
237
#define winsock_startup()	(TRUE)
238
#define SOCKLIB_DESC NULL
239 240 241

#endif

242 243 244 245 246
static char* server_host_name(void)
{
	return startup->host_name[0] ? startup->host_name : scfg.sys_inetaddr;
}

247 248
static void update_clients(void)
{
249
	if(startup!=NULL && startup->clients!=NULL)
250
		startup->clients(startup->cbdata,protected_uint32_value(active_clients)+active_sendmail);
251 252
}

253
static void client_on(SOCKET sock, client_t* client, BOOL update)
254
{
255 256
	if(!update)
		listAddNodeData(&current_connections, client->addr, strlen(client->addr)+1, sock, LAST_NODE);
257
	if(startup!=NULL && startup->client_on!=NULL)
258
		startup->client_on(startup->cbdata,TRUE,sock,client,update);
259 260 261 262
}

static void client_off(SOCKET sock)
{
263
	listRemoveTaggedNode(&current_connections, sock, /* free_data */TRUE);
264
	if(startup!=NULL && startup->client_on!=NULL)
265
		startup->client_on(startup->cbdata,FALSE,sock,NULL,FALSE);
266 267
}

268
static void thread_up(BOOL setuid)
269 270
{
	if(startup!=NULL && startup->thread_up!=NULL)
271
		startup->thread_up(startup->cbdata,TRUE,setuid);
272 273
}

274
static int32_t thread_down(void)
275
{
276
	int32_t count = protected_uint32_adjust_fetch(&thread_count,-1);
277
	if(startup!=NULL && startup->thread_up!=NULL)
278
		startup->thread_up(startup->cbdata,FALSE,FALSE);
279
	return count;
280 281
}

deuce's avatar
deuce committed
282
void mail_open_socket(SOCKET sock, void* cb_protocol)
283
{
deuce's avatar
deuce committed
284
	char	*protocol=(char *)cb_protocol;
285
	char	error[256];
286
	char	section[128];
287

deuce's avatar
deuce committed
288
	if(startup!=NULL && startup->socket_open!=NULL)
289
		startup->socket_open(startup->cbdata,TRUE);
deuce's avatar
deuce committed
290 291
	SAFEPRINTF(section,"mail|%s",protocol);
	if(set_socket_options(&scfg, sock, section, error, sizeof(error)))
292
		lprintf(LOG_ERR,"%04d %s !ERROR %s", sock, protocol, error);
293

deuce's avatar
deuce committed
294 295 296 297 298 299 300 301
	stats.sockets++;
}

void mail_close_socket_cb(SOCKET sock, void* cb_protocol)
{
	if(startup!=NULL && startup->socket_open!=NULL)
		startup->socket_open(startup->cbdata,FALSE);
	stats.sockets--;
302 303
}

304
int mail_close_socket(SOCKET *sock, int *sess)
305 306 307
{
	int		result;

308 309 310 311 312
	if (*sess != -1) {
		cryptDestroySession(*sess);
		*sess = -1;
	}
	if(*sock==INVALID_SOCKET)
313 314
		return(-1);

315 316
	shutdown(*sock,SHUT_RDWR);	/* required on Unix */
	result=closesocket(*sock);
317
	if(startup!=NULL && startup->socket_open!=NULL)
318
		startup->socket_open(startup->cbdata,FALSE);
319
	stats.sockets--;
320 321
	if(result!=0) {
		if(ERROR_VALUE!=ENOTSOCK)
322
			lprintf(LOG_WARNING,"%04d !ERROR %d closing socket",*sock, ERROR_VALUE);
323
	}
324
#if 0 /*def _DEBUG */
325
	else 
326
		lprintf(LOG_DEBUG,"%04d Socket closed (%d sockets in use)",*sock,stats.sockets);
327 328
#endif

329 330
	*sock = -1;

331 332 333 334 335
	return(result);
}

static void status(char* str)
{
336
	if(startup!=NULL && startup->status!=NULL)
337
	    startup->status(startup->cbdata,str);
338 339
}

340
int sockprintf(SOCKET sock, const char* prot, CRYPT_SESSION sess, char *fmt, ...)
341 342
{
	int		len;
343
	int		maxlen;
344 345 346 347 348
	int		result;
	va_list argptr;
	char	sbuf[1024];

    va_start(argptr,fmt);
349 350 351
    len=vsnprintf(sbuf,maxlen=sizeof(sbuf)-2,fmt,argptr);
    va_end(argptr);

352
	if(len<0 || len > maxlen) /* format error or output truncated */
353
		len=maxlen;
354
	if(startup->options&MAIL_OPT_DEBUG_TX)
355
		lprintf(LOG_DEBUG,"%04d %s TX: %.*s", sock, prot, len, sbuf);
356
	memcpy(sbuf+len,"\r\n",2);
357
	len+=2;
358

359
	if(sock==INVALID_SOCKET) {
360
		lprintf(LOG_WARNING,"%s !INVALID SOCKET in call to sockprintf", prot);
361 362 363
		return(0);
	}

Deucе's avatar
Deucе committed
364 365 366 367
	/* Check socket for writability */
	if (!socket_writable(sock, 300000)) {
		lprintf(LOG_NOTICE,"%04d %s !NOTICE socket did not become writable"
			,sock, prot);
368 369 370
		return(0);
	}

371 372 373 374 375 376 377
	if (sess != -1) {
		int tls_sent;
		int sent = 0;
		while (sent < len) {
			result = cryptPushData(sess, sbuf+sent, len-sent, &tls_sent);
			if (result == CRYPT_OK)
				sent += tls_sent;
378
			else {
379
				GCES(result, prot, sock, sess, "pushing data");
380
				return 0;
381
			}
382
		}
383
		if ((result = cryptFlushData(sess)) != CRYPT_OK) {
384
			GCES(result, prot, sock, sess, "flushing data");
385
			return 0;
386
		}
387 388 389 390 391 392 393 394 395 396
	}
	else {
		// It looks like this could stutter on partial sends -- Deuce
		while((result=sendsocket(sock,sbuf,len))!=len) {
			if(result==SOCKET_ERROR) {
				if(ERROR_VALUE==EWOULDBLOCK) {
					YIELD();
					continue;
				}
				if(ERROR_VALUE==ECONNRESET) 
397
					lprintf(LOG_NOTICE,"%04d %s Connection reset by peer on send",sock,prot);
398
				else if(ERROR_VALUE==ECONNABORTED) 
399
					lprintf(LOG_NOTICE,"%04d %s Connection aborted by peer on send",sock, prot);
400
				else
401
					lprintf(LOG_NOTICE,"%04d %s !ERROR %d sending on socket",sock,prot,ERROR_VALUE);
402 403
				return(0);
			}
404
			lprintf(LOG_WARNING,"%04d %s !ERROR: short send on socket: %d instead of %d",sock,prot,result,len);
405 406 407 408 409
		}
	}
	return(len);
}

410
static void sockerror(SOCKET socket, const char* prot, int rd, const char* action)
411 412
{
	if(rd==0) 
413 414
		lprintf(LOG_NOTICE,"%04d %s Socket closed by peer on %s"
			,socket, prot, action);
415
	else if(rd==SOCKET_ERROR) {
rswindell's avatar
rswindell committed
416
		if(ERROR_VALUE==ECONNRESET) 
417 418
			lprintf(LOG_NOTICE,"%04d %s Connection reset by peer on %s"
				,socket, prot, action);
419
		else if(ERROR_VALUE==ECONNABORTED) 
420 421
			lprintf(LOG_NOTICE,"%04d %s Connection aborted by peer on %s"
				,socket, prot, action);
422
		else
423 424
			lprintf(LOG_NOTICE,"%04d %s !SOCKET ERROR %d on %s"
				,socket, prot, ERROR_VALUE, action);
425
	} else
426 427
		lprintf(LOG_WARNING,"%04d %s !SOCKET ERROR: unexpected return value %d from %s"
			,socket, prot, rd, action);
428 429
}

430
static int sock_recvbyte(SOCKET sock, const char* prot, CRYPT_SESSION sess, char *buf, time_t start)
431 432 433 434 435 436 437 438
{
	int len=0;
	int ret;
	int i;

	if (sess > -1) {
		while (1) {
			ret = cryptPopData(sess, buf, 1, &len);
439
			GCES(ret, prot, sock, sess, "popping data");
440 441 442 443 444 445 446 447 448 449 450 451 452 453 454
			switch(ret) {
				case CRYPT_OK:
					break;
				case CRYPT_ERROR_TIMEOUT:
					return -1;
				case CRYPT_ERROR_COMPLETE:
					return 0;
				default:
					if (ret < -1)
						return ret;
					return -2;
			}
			if (len)
				return len;
			if(startup->max_inactivity && (time(NULL)-start)>startup->max_inactivity) {
455 456
				lprintf(LOG_WARNING,"%04d %s !TIMEOUT in sock_recvbyte (%u seconds):  INACTIVE SOCKET"
					,sock, prot, startup->max_inactivity);
457 458 459 460 461
				return(-1);
			}
		}
	}
	else {
Deucе's avatar
Deucе committed
462 463 464 465 466
		if (!socket_readable(sock, startup->max_inactivity * 1000)) {
			if(startup->max_inactivity && (time(NULL)-start)>startup->max_inactivity) {
				lprintf(LOG_WARNING,"%04d %s !TIMEOUT in sock_recvbyte (%u seconds):  INACTIVE SOCKET"
					,sock, prot, startup->max_inactivity);
				return(-1);
467 468 469 470 471
			}
			return 0;
		}
		i=recv(sock, buf, 1, 0);
		if(i<1)
472
			sockerror(sock,prot,i,"receive");
473 474 475 476
		return i;
	}
}

477
static int sockreadline(SOCKET socket, const char* prot, CRYPT_SESSION sess, char* buf, int len)
478 479 480 481 482
{
	char	ch;
	int		i,rd=0;
	time_t	start;

483 484
	buf[0]=0;

485
	start=time(NULL);
486 487

	if(socket==INVALID_SOCKET) {
488
		lprintf(LOG_WARNING,"%s !INVALID SOCKET in call to sockreadline", prot);
489
		return(-1);
490
	}
491 492
	
	while(rd<len-1) {
493

deuce's avatar
deuce committed
494
		if(terminated || terminate_server) {
495
			lprintf(LOG_WARNING,"%04d %s !ABORTING sockreadline",socket, prot);
496 497 498
			return(-1);
		}

499
		i = sock_recvbyte(socket, prot, sess, &ch, start);
500

501
		if(i<1)
502
			return(-1);
503

504
		if(ch=='\n' /* && rd>=1 */ ) { /* Mar-9-2003: terminate on sole LF */
505
			break;
506
		}
507 508
		buf[rd++]=ch;
	}
509
	if(rd>0 && buf[rd-1]=='\r')
510 511
		rd--;
	buf[rd]=0;
512 513 514 515
	
	return(rd);
}

516
static BOOL sockgetrsp(SOCKET socket, const char* prot, CRYPT_SESSION sess, char* rsp, char *buf, int len)
517 518 519 520
{
	int rd;

	while(1) {
521
		rd = sockreadline(socket, prot, sess, buf, len);
522
		if(rd<1) {
523
			if(rd==0 && rsp != NULL)
524
				lprintf(LOG_WARNING,"%04d %s !RECEIVED BLANK RESPONSE, Expected '%s'", socket, prot, rsp);
525
			return(FALSE);
526
		}
527 528
		if(buf[3]=='-')	{ /* Multi-line response */
			if(startup->options&MAIL_OPT_DEBUG_RX_RSP) 
529
				lprintf(LOG_DEBUG,"%04d %s RX: %s",socket, prot, buf);
530 531
			continue;
		}
532
		if(rsp!=NULL && strnicmp(buf,rsp,strlen(rsp))) {
533
			lprintf(LOG_WARNING,"%04d %s !INVALID RESPONSE: '%s' Expected: '%s'", socket, prot, buf, rsp);
534 535 536 537 538
			return(FALSE);
		}
		break;
	}
	if(startup->options&MAIL_OPT_DEBUG_RX_RSP) 
539
		lprintf(LOG_DEBUG,"%04d %s RX: %s",socket, prot, buf);
540 541 542
	return(TRUE);
}

543
static int sockgetrsp_opt(SOCKET socket, const char* prot, CRYPT_SESSION sess, char* rsp, char *opt, char *buf, int len)
544 545 546 547 548 549
{
	int rd;
	int ret = 0;
	size_t moptlen;
	char *mopt;

550
	moptlen = strlen(rsp)+strlen(opt) + 1;
551 552 553 554 555
	mopt = malloc(moptlen+1);
	if (mopt == NULL)
		return -1;
	sprintf(mopt, "%s-%s", rsp, opt);
	while(1) {
556
		rd = sockreadline(socket, prot, sess, buf, len);
557 558
		if(rd<1) {
			if(rd==0)
559
				lprintf(LOG_WARNING,"%04d %s !RECEIVED BLANK RESPONSE, Expected '%s'", socket, prot, rsp);
560 561 562 563 564 565 566
			free(mopt);
			return(-1);
		}
		if(buf[3]=='-')	{ /* Multi-line response */
			if (strncmp(buf, mopt, moptlen) == 0)
				ret = 1;
			if(startup->options&MAIL_OPT_DEBUG_RX_RSP) 
567
				lprintf(LOG_DEBUG,"%04d %s RX: %s",socket, prot, buf);
568 569
			continue;
		}
570
		if(strnicmp(buf,rsp,strlen(rsp))) {
571
			lprintf(LOG_WARNING,"%04d %s !INVALID RESPONSE: '%s' Expected: '%s'", socket, prot, buf, rsp);
572 573 574 575 576
			free(mopt);
			return(-1);
		}
		break;
	}
577
	mopt[strlen(rsp)] = ' ';
578
	if (strncmp(buf, mopt, moptlen) == 0)
579
		ret = 1;
580 581
	free(mopt);
	if(startup->options&MAIL_OPT_DEBUG_RX_RSP)
582
		lprintf(LOG_DEBUG,"%04d %s RX: %s",socket, prot, buf);
583 584 585
	return(ret);
}

rswindell's avatar
rswindell committed
586
/* non-standard, but documented (mostly) in draft-newman-msgheader-originfo-05 */
587
void originator_info(SOCKET socket, const char* protocol, CRYPT_SESSION sess, smbmsg_t* msg)
rswindell's avatar
rswindell committed
588 589 590 591 592 593 594 595 596 597 598
{
	char* user		= msg->from_ext;
	char* login		= smb_get_hfield(msg,SENDERUSERID,NULL);
	char* server	= smb_get_hfield(msg,SENDERSERVER,NULL);
	char* client	= smb_get_hfield(msg,SENDERHOSTNAME,NULL);
	char* addr		= smb_get_hfield(msg,SENDERIPADDR,NULL);
	char* prot		= smb_get_hfield(msg,SENDERPROTOCOL,NULL);
	char* port		= smb_get_hfield(msg,SENDERPORT,NULL);
	char* time		= smb_get_hfield(msg,SENDERTIME,NULL);

	if(user || login || server || client || addr || prot || port || time)
599
		sockprintf(socket, protocol, sess
rswindell's avatar
rswindell committed
600 601 602 603 604 605 606 607 608 609 610 611
			,"X-Originator-Info: account=%s; login-id=%s; server=%s; client=%s; addr=%s; prot=%s; port=%s; time=%s"
			,user
			,login
			,server
			,client
			,addr
			,prot
			,port
			,time
			);
}

612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628
char* angle_bracket(char* buf, size_t max, const char* addr)
{
	if(*addr == '<')
		safe_snprintf(buf, max, "%s", addr);
	else
		safe_snprintf(buf, max, "<%s>", addr);
	return buf;
}

int compare_addrs(const char* addr1, const char* addr2)
{
	char tmp1[256];
	char tmp2[256];

	return strcmp(angle_bracket(tmp1, sizeof(tmp1), addr1), angle_bracket(tmp2, sizeof(tmp2), addr2));
}

629 630 631 632 633 634 635
/* RFC822: The maximum total length of a text line including the
   <CRLF> is 1000 characters (but not counting the leading
   dot duplicated for transparency). 

   POP3 (RFC1939) actually calls for a 512 byte line length limit!
*/
#define MAX_LINE_LEN	998		
636

637
static ulong sockmimetext(SOCKET socket, const char* prot, CRYPT_SESSION sess, smbmsg_t* msg, char* msgtxt, ulong maxlines
638
						  ,str_list_t file_list, char* mime_boundary)
639
{
640 641
	char		toaddr[256]="";
	char		fromaddr[256]="";
642
	char		fromhost[256];
rswindell's avatar
rswindell committed
643
	char		msgid[256];
644
	char		tmp[256];
645
	char		date[64];
rswindell's avatar
rswindell committed
646
	char*		p;
647
	char*		np;
648
	int			i;
649
	int			s;
650
	ulong		lines;
651
	int			len,tlen;
652

653
	/* HEADERS (in recommended order per RFC822 4.1) */
654 655

	if(msg->reverse_path!=NULL)
656
		if(!sockprintf(socket,prot,sess,"Return-Path: %s", msg->reverse_path))
657 658
			return(0);

659 660
	for(i=0;i<msg->total_hfields;i++)
		if(msg->hfield[i].type == SMTPRECEIVED && msg->hfield_dat[i]!=NULL) 
661
			if(!sockprintf(socket,prot,sess,"Received: %s", (char*)msg->hfield_dat[i]))
662 663
				return(0);

664
	if(!sockprintf(socket,prot,sess,"Date: %s",msgdate(msg->hdr.when_written,date)))
665
		return(0);
666 667

	if((p=smb_get_hfield(msg,RFC822FROM,NULL))!=NULL)
668
		s=sockprintf(socket,prot,sess,"From: %s",p);	/* use original RFC822 header field */
669
	else {
rswindell's avatar
rswindell committed
670
		char fromname[256];
rswindell's avatar
rswindell committed
671
		SAFEPRINTF(fromname, "\"%s\"", msg->from);
672
		if(msg->from_net.type==NET_QWK && msg->from_net.addr!=NULL)
673
			SAFEPRINTF2(fromaddr,"%s!%s"
674 675
				,(char*)msg->from_net.addr
				,usermailaddr(&scfg,fromhost,msg->from));
rswindell's avatar
rswindell committed
676 677 678
		else if(msg->from_net.type==NET_FIDO && msg->from_net.addr!=NULL) {
			faddr_t* faddr = (faddr_t *)msg->from_net.addr;
			char faddrstr[128];
rswindell's avatar
rswindell committed
679
			SAFEPRINTF2(fromname,"\"%s\" (%s)", msg->from, smb_faddrtoa(faddr, NULL));
rswindell's avatar
rswindell committed
680 681 682 683 684 685 686 687
			if(faddr->point)
				SAFEPRINTF4(faddrstr,"p%hu.f%hu.n%hu.z%hu"FIDO_TLD
					,faddr->point, faddr->node, faddr->net, faddr->zone);
			else
				SAFEPRINTF3(faddrstr,"f%hu.n%hu.z%hu"FIDO_TLD
					,faddr->node, faddr->net, faddr->zone);
			SAFEPRINTF2(fromaddr,"%s@%s", usermailaddr(NULL,fromhost,msg->from), faddrstr);
		} else if(msg->from_net.type!=NET_NONE && msg->from_net.addr!=NULL)
688
			SAFECOPY(fromaddr,(char*)msg->from_net.addr);
689 690
		else 
			usermailaddr(&scfg,fromaddr,msg->from);
691
		s = sockprintf(socket,prot,sess,"From: %s %s", fromname, angle_bracket(tmp, sizeof(tmp), fromaddr));
692
	}
693 694
	if(!s)
		return(0);
695

696 697
	if((p = smb_get_hfield(msg, RFC822ORG, NULL)) != NULL) {
		if(!sockprintf(socket,prot,sess,"Organization: %s", p))
698
			return(0);
699 700 701 702 703 704
	} else {
		if(msg->from_org!=NULL || msg->from_net.type==NET_NONE)
			if(!sockprintf(socket,prot,sess,"Organization: %s"
				,msg->from_org==NULL ? scfg.sys_name : msg->from_org))
				return(0);
	}
705

706 707
	p = smb_get_hfield(msg, RFC822SUBJECT, NULL);
	if(!sockprintf(socket,prot,sess,"Subject: %s", p == NULL ? msg->subj : p))
708
		return(0);
709

710
	if((p = smb_get_hfield(msg, RFC822TO, NULL)) != NULL)
711 712
		s=sockprintf(socket,prot,sess,"To: %s", p);	/* use original RFC822 header field (MIME-Encoded) */
	else if((p = msg->to_list) != NULL)
713
		s=sockprintf(socket,prot,sess,"To: %s", p);	/* use original RFC822 header field */
714 715
	else {
		if(strchr(msg->to,'@')!=NULL || msg->to_net.addr==NULL)
716
			s=sockprintf(socket,prot,sess,"To: %s",msg->to);	/* Avoid double-@ */
717 718
		else if(msg->to_net.type==NET_INTERNET || msg->to_net.type==NET_QWK) {
			if(strchr((char*)msg->to_net.addr,'<')!=NULL)
719
				s=sockprintf(socket,prot,sess,"To: %s",(char*)msg->to_net.addr);
720
			else
721
				s=sockprintf(socket,prot,sess,"To: \"%s\" <%s>",msg->to,(char*)msg->to_net.addr);
rswindell's avatar
rswindell committed
722
		} else if(msg->to_net.type==NET_FIDO) {
723
			s=sockprintf(socket,prot,sess,"To: \"%s\" (%s)",msg->to, smb_faddrtoa((fidoaddr_t*)msg->to_net.addr, NULL));
724 725
		} else {
			usermailaddr(&scfg,toaddr,msg->to);
726
			s=sockprintf(socket,prot,sess,"To: \"%s\" <%s>",msg->to,toaddr);
727
		}
728
	}
729 730
	if(!s)
		return(0);
731 732
	if((p = smb_get_hfield(msg, RFC822CC, NULL)) != NULL || msg->cc_list != NULL)
		if(!sockprintf(socket,prot,sess,"Cc: %s", p == NULL ? msg->cc_list : p))
733
			return(0);
734
	np=NULL;
735 736
	p = smb_get_hfield(msg, RFC822REPLYTO, NULL);
	if(p == NULL && (p = msg->replyto_list) == NULL) {
737
		np=msg->replyto;
738
		if(msg->replyto_net.type==NET_INTERNET)
rswindell's avatar
rswindell committed
739
			p=msg->replyto_net.addr;
740
	}
rswindell's avatar
rswindell committed
741
	if(p!=NULL && strchr((char*)p, '@') != NULL) {
742
		if(np!=NULL)
743
			s=sockprintf(socket,prot,sess,"Reply-To: \"%s\" <%s>",np,p);
744
		else 
745
			s=sockprintf(socket,prot,sess,"Reply-To: %s",p);
746
	}
747 748
	if(!s)
		return(0);
749
	if(!sockprintf(socket,prot,sess,"Message-ID: %s",get_msgid(&scfg,INVALID_SUB,msg,msgid,sizeof(msgid))))
750
		return(0);
751
	if(msg->reply_id!=NULL)
752
		if(!sockprintf(socket,prot,sess,"In-Reply-To: %s",msg->reply_id))
753
			return(0);
754

755 756 757 758
	if(msg->hdr.priority != SMB_PRIORITY_UNSPECIFIED)
		if(!sockprintf(socket,prot,sess,"X-Priority: %u", (uint)msg->hdr.priority))
			return(0);

759
	originator_info(socket, prot, sess, msg);
760

761 762 763 764 765 766 767 768 769 770 771 772
	/* Include all possible FidoNet header fields here */
	for(i=0;i<msg->total_hfields;i++) {
		switch(msg->hfield[i].type) {
			case FIDOCTRL:
			case FIDOAREA:	
			case FIDOSEENBY:
			case FIDOPATH:
			case FIDOMSGID:
			case FIDOREPLYID:
			case FIDOPID:
			case FIDOFLAGS:
			case FIDOTID:
773
				if(!sockprintf(socket, prot, sess, "%s: %s", smb_hfieldtype(msg->hfield[i].type), (char*)msg->hfield_dat[i]))
774 775 776 777 778
					return(0);
				break;
		}
	}
	for(i=0;i<msg->total_hfields;i++) { 
779
		if(msg->hfield[i].type==RFC822HEADER) { 
780
			if(!sockprintf(socket,prot, sess,"%s",(char*)msg->hfield_dat[i]))
781
				return(0);
782
        }
783
    }
784 785 786 787 788 789 790 791 792 793
	const char* charset = msg->text_charset;
	if(charset == NULL) {
		if(smb_msg_is_utf8(msg) || (msg->hdr.auxattr & MSG_HFIELDS_UTF8))
			charset = "UTF-8";
		else