Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Лабы / 2 / lab.67.by mice / tube / src / tb_mail

.cpp
Скачиваний:
9
Добавлен:
17.04.2013
Размер:
14.01 Кб
Скачать
/*******************************************************************************
* file:         tb_mail.cpp                                                    *
* version:      0.0.1                                                          *
* author:       d-evil [tmd] (mailto:d-evil.tmd@mail.ru)                       *
* description:  not available                                                  *
*******************************************************************************/

#include "tb_mail.h"


////////////////////////////////////////////////////////////////////////////////
// ctb_link public definitions
ctb_link::ctb_link() {
	_sock = INVALID_SOCKET;
	_buf_top = 0;
	_logf = NULL;
}


ctb_link::~ctb_link() {
	disconnect();
	close_log();
}


int ctb_link::connect(const char *const host, const unsigned short port) {
	disconnect();

	_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (INVALID_SOCKET == _sock) return -1;

	if (0 != _ai.set(host, port)) {
		disconnect();
		return -1;
	}

	if (0 != ::connect(_sock, _ai.saddr(), _ai.saddr_sz())) {
		disconnect();
		return -1;
	}

	return 0;
}


int ctb_link::disconnect() {
	if (!_sock_invalid()) {
		closesocket(_sock);
		_sock = INVALID_SOCKET;
	}

	return 0;
}


int ctb_link::send_str(const char *const str, const bool ap_crlf) {
	if (_sock_invalid()) return -1;

	const int len = (int)strlen(str);
	const int crlf_len = (int)strlen(_CRLF_STR);
	const int flen = len + ((ap_crlf)? crlf_len: 0);
	if (_MX_STR_LEN < flen) return -1;

	memcpy(_send_buf, str, len);
	if (ap_crlf) {
		memcpy(_send_buf + len, _CRLF_STR, crlf_len);
	}

	int wr = ::send(_sock, _send_buf, flen, 0);
	if (wr != flen) return -1;

	if (NULL != _logf) fprintf(_logf, "S: %s\n", str);

	return 0;
}


int ctb_link::write_str(char *const str, const unsigned int to_ms) {
	if (0 >= can_write(HLOPS_TIMEOUT)) return -1;

	if (NULL == str) return send_str("");
	else return send_str(str);
}


int ctb_link::recv_str(char *const str, const int mx) {
	for (;;) {	// try to read more data from socket
		const int len = _can_read_nb();
		if (0 > len) return -1;
		if (0 == len) break;
		if (_BUF_SZ < _buf_top + len) break;

		const int wr = ::recv(_sock, _recv_buf, len, 0);
		if (wr <= 0/*!= len*/) return -1;

		// memcpy(_buf + _buf_top, _recv_buf, len);
		// _buf_top += len;
		memcpy(_buf + _buf_top, _recv_buf, wr);
		_buf_top += wr;
	}

	// search for string end
	int len = -1;
	int w;
	for (int i = 0; _buf_top > i && -1 == len; ++i) {
		int k = 0;
		while (_buf[i+k] == _CRLF_STR[k]) {
			++k;
			if ('\0' == _CRLF[k]) {
				len = i;
				w = i + k;
				break;
			}
			if (i+k >= _buf_top) break;
		}
	}
	if (0 > len || mx < len) return -1;

	_buf[len] = '\0';
	strcpy(str, _buf);
	if (NULL != _logf) fprintf(_logf, "R: %s\n", str);

	_buf_top -= w;
	if (0 < _buf_top) memmove(_buf, _buf + w, _buf_top);

	return len;
}


int ctb_link::read_str(char *const str, const int mx, const unsigned int to_ms) {
	ctb_timeout t(ms_to_clocks(to_ms));
	do {
		int rev = _can_read();
		if (0 > rev) return -1;
		if (0 == rev && 0 == _buf_top) continue;

		rev = recv_str(str, mx);
		if (0 < rev) return rev;
	} while (!t.out());

	return -1;
}


int ctb_link::clean_buf() {
	_buf_top = 0;

	return 0;
}


int ctb_link::open_log(const char *const fn, const bool erase) {
	close_log();

	if (erase) _logf = fopen(fn, "w");
	else _logf = fopen(fn, "a");
	if (NULL == _logf) return -1;

	SYSTEMTIME now;
	GetLocalTime(&now);
	fprintf(_logf, "\n\n---- Log started at %02d:%02d:%02d ----\n\n",
			now.wHour, now.wMinute, now.wSecond);

	return 0;
}


int ctb_link::close_log() {
	if (NULL == _logf) return 0;

	SYSTEMTIME now;
	GetLocalTime(&now);
	fprintf(_logf, "\n---- Log finished at %02d:%02d:%02d ----\n",
			now.wHour, now.wMinute, now.wSecond);
	fclose(_logf);

	return 0;
}


////////////////////////////////////////////////////////////////////////////////
// ctb_link protected definitions
const char ctb_link::_CRLF[] = {'\r', '\n'};
const char *ctb_link::_CRLF_STR = "\r\n";


int ctb_link::_can_read_nb() {
	if (_sock_invalid()) return -1;

	unsigned long arg;
	if (0 != ioctlsocket(_sock, FIONREAD, &arg)) return -1;

	return (int)arg;
}


int ctb_link::_can_read(const unsigned int to_ms) {
	fd_set set;
	set.fd_array[0] = _sock;
	set.fd_count = 1;

	timeval to = {0, to_ms*1000};
	int rev = select(0, &set, NULL, NULL, &to);
	if (SOCKET_ERROR == rev) return -1;

	return rev;
}


int ctb_link::_can_write(const unsigned int to_ms) {
	fd_set set;
	set.fd_array[0] = _sock;
	set.fd_count = 1;

	timeval to = {0, to_ms*1000};
	int rev = select(0, NULL, &set, NULL, &to);
	if (SOCKET_ERROR == rev) return -1;

	return rev;
}


int ctb_link::_escape_str(const char *const from, const int len,
						  char *const to, const int mx)
{
	int ep = 0;
	bool nl = true;
	for (int i = 0; len > i; ++i) {
		if (ep == mx) return -1;

		if (nl && '.' == from[i]) to[ep++] = '.';
		to[ep++] = ('\0' != from[i])? from[i]: 1;

		// if ('\r' == from[i] && '\n' == from[i+1]) nl = true;
		if ('\r' == from[i] || '\n' == from[i]) nl = true;
		else nl = false;
	}
	to[ep] = '\0';

	return 0;
}


char *ctb_link::_set_str(char *const to, const char *const from) {
	if (NULL == from) {
		free(to);
		return NULL;
	}

	int len = (int)strlen(from) + 1;
	char *tmp = (char *)realloc(to, len * sizeof(char));
	if (NULL == tmp) return NULL;

	memcpy(tmp, from, len);

	return tmp;
}


char *ctb_link::_time_str(char *const time_str) {
	if (NULL != time_str) {
		free(time_str);
		return NULL;
	}

	__time64_t tm;
	_time64(&tm);
	char *const tmp = _ctime64(&tm);

	const int len = (int)strlen(tmp);
	tmp[len-1] = '\0';

	char *const str = (char *)malloc(len * sizeof(int));
	if (NULL == str) return NULL;
	memcpy(str, tmp, len);
	
	return str;
}



////////////////////////////////////////////////////////////////////////////////
// ctb_sender public definitions
ctb_sender::ctb_sender() {
	_from = NULL;
	_to = NULL;
	_copy = NULL;
	_subj = NULL;
	_text = NULL;
}


ctb_sender::~ctb_sender() {
	_from = _set_str(_from);
	_to = _set_str(_to);
	_copy = _set_str(_copy);
	_subj = _set_str(_subj);
	_text = _set_str(_text);
}


int ctb_sender::connect(const char *const host, const unsigned short port) {
	if (0 != ctb_link::connect(host, port)) return -1;

	return 0;
}


int ctb_sender::open(const char *const as) {
	if (_SMTP_REPLY_READY != _get_smtp_reply()) return -1;

	cdm_addr_info ai;
	if (NULL != as) ai.set_host(as);
	else ai.set_to_local();
	sprintf(_str_buf, "HELO %s", ai.host());
	write_str(_str_buf);
	if (_SMTP_REPLY_OK != _get_smtp_reply()) return -1;

	return 0;
}


int ctb_sender::close() {
	if (0 != write_str("QUIT")) return -1;

	if (_SMTP_REPLY_CLOSE != _get_smtp_reply()) return -1;

	return 0;
}


int ctb_sender::send_mail() {
	if (0 != _cmd_mail(_from)) return -1;
	if (0 != _cmd_rcpt(_to)) return -1;
	if (0 != _cmd_data()) return -1;

	if (0 != _write_mail(_text)) return -1;

	return 0;
}


////////////////////////////////////////////////////////////////////////////////
// ctb_sender protected definitions
int ctb_sender::_get_smtp_code(const char *const str) {
	int i;
	sscanf(str, "%i", &i);

	return i;
}


int ctb_sender::_get_smtp_reply(int *const code) {
	if (0 >= read_str(_str_buf)) {
		if (NULL != code) *code = -1;
		_last_reply[0] = '\0';
		return -1;
	}
	strcpy(_last_reply, _str_buf);

	const int c = _get_smtp_code(_str_buf);
	if (NULL != code) *code = c;

	if (500 <= c) return -1;
	if (400 <= c) return 1;

	return 0;
}


int ctb_sender::_get_smtp_reply() {
	int code;
	_get_smtp_reply(&code);

	return code;
}


int ctb_sender::_write_mail(const char *const text) {
	sprintf(_str_buf, "From: %s", _from);
	write_str(_str_buf);
	write_str("X-Mailer: Tube (by d-evil.tmd@mail.ru)");
	sprintf(_str_buf, "Subject: %s", _subj);
	write_str(_str_buf);
	sprintf(_str_buf, "To: %s", _to);
	write_str(_str_buf);
	sprintf(_str_buf, "CC: %s", _copy);
	write_str(_str_buf);
	sprintf(_str_buf, "Subject: %s", _subj);
	write_str(_str_buf);
	write_str("MIME-Version: 1.0");
	write_str("Content-Type: text/plain; charset=\"windows-1251\"");
	write_str("Content-Transfer-Encoding: 8bit");

	write_str();

	const int len = (int)strlen(_text);
	const int sm_buf_sz = _MX_STR_LEN/2;
	char sm_buf[sm_buf_sz];
	int p = 0;
	while (p <= len - sm_buf_sz) {
		memcpy(sm_buf, _text + p, sm_buf_sz);
		_escape_str(_text, sm_buf_sz, _str_buf, _MX_STR_LEN);
		if (can_write()) send_str(_str_buf, false);
		else return -1;

		p += sm_buf_sz;
	}

	if (p != len) {
		memcpy(sm_buf, _text + p, len - p);
		_escape_str(_text, len - p, _str_buf, _MX_STR_LEN);
		if (can_write()) send_str(_str_buf, false);
		else return -1;
	}

	write_str();
	write_str(".");

	if (_SMTP_REPLY_OK != _get_smtp_reply()) return -1;

	return 0;
}


int ctb_sender::_cmd_mail(const char *const from) {
	sprintf(_str_buf, "MAIL FROM:<%s>", from);
	if (0 != write_str(_str_buf)) return -1;
	if (_SMTP_REPLY_OK != _get_smtp_reply()) return -1;

	return 0;
}


int ctb_sender::_cmd_rcpt(const char *const to) {
	sprintf(_str_buf, "RCPT TO:<%s>", to);
	if (0 != write_str(_str_buf)) return -1;
	switch (_get_smtp_reply()) {
		case _SMTP_REPLY_UNL:
		case _SMTP_REPLY_OK:
			return 0;

		default:
			return -1;
	}

	return -1;	// dummy return
}


int ctb_sender::_cmd_data() {
	sprintf(_str_buf, "DATA");
	if (0 != write_str(_str_buf)) return -1;
	if (_SMTP_REPLY_STDAT != _get_smtp_reply()) return -1;

	return 0;
}



////////////////////////////////////////////////////////////////////////////////
// ctb_recver public definitions
ctb_recver::ctb_recver() {
	_user = NULL;
	_pass = NULL;
}


ctb_recver::~ctb_recver() {
	_user = _set_str(_user, NULL);
	_pass = _set_str(_pass, NULL);
}


int ctb_recver::open() {
	if (0 != _get_pop3_reply()) return -1;

	return 0;
}


int ctb_recver::close() {
	if (0 != write_str("QUIT")) return -1;
	if (0 != _get_pop3_reply()) return -1;

	return 0;
}


int ctb_recver::auth(const char *const user, const char *const pass) {
	if (0 != _cmd_user((NULL != user)? user: _user)) return -1;
	if (0 != _cmd_pass((NULL != pass)? pass: _pass)) return -1;

	return 0;
}


////////////////////////////////////////////////////////////////////////////////
// ctb_recver protected definitions
int ctb_recver::_get_pop3_code(const char *const _str_buf) {
	if (NULL == _str_buf) return -1;
	if ('+' == _str_buf[0]) return 0;
	
	return -1;
}


int ctb_recver::_get_pop3_reply(int *const code) {
	if (0 >= read_str(_str_buf)) {
		if (NULL != code) *code = -1;
		_last_reply[0] = '\0';
		return -1;
	}
	strcpy(_last_reply, _str_buf);
	if (NULL != code) *code = _get_pop3_code(_str_buf);

	return 0;
}


int ctb_recver::_get_pop3_reply() {
	int code;
	if (0 != _get_pop3_reply(&code)) return -1;

	return code;
}


int ctb_recver::_cmd_user(const char *const user) {
	sprintf(_str_buf, "USER %s", user);
	if (0 != write_str(_str_buf)) return -1;
	if (0 != _get_pop3_reply()) return -1;

	return 0;
}


int ctb_recver::_cmd_pass(const char *const pass) {
	sprintf(_str_buf, "PASS %s", pass);
	if (0 != write_str(_str_buf)) return -1;
	if (0 != _get_pop3_reply()) return -1;

	return 0;
}


int ctb_recver::_cmd_stat(int *const total, int *const sz) {
	if (0 != write_str("STAT")) return -1;
	if (0 != _get_pop3_reply()) return -1;
	int t, s;
	sscanf(_last_reply, "%*s%i%i", &t, &s);
	if (NULL != total) *total = t;
	if (NULL != sz) *sz = s;

	return 0;
}


int ctb_recver::_cmd_list(ctb_list *const lst) {
	if (0 != write_str("LIST")) return -1;
	if (0 != _get_pop3_reply()) return -1;

	lst->clear();
	std::string str;
	for (;;) {
		int rev = read_str(_str_buf);
		if (0 > rev) return -1;
		if ('.' == _str_buf[0]) break;

		str = _str_buf;
		lst->push_back(str);
	}

	return 0;
}


int ctb_recver::_cmd_retr(const int num, char **text) {
	*text = NULL;	// for safety
	sprintf(_str_buf, "RETR %i", num);
	if (0 != write_str(_str_buf)) return -1;
	if (0 != _get_pop3_reply()) return -1;

	int req_sz;
	sscanf(_last_reply, "%*s%i", &req_sz);
	if (0 >= req_sz) req_sz = _MX_STR_LEN;

	*text = (char *)malloc(req_sz * sizeof(char));
	if (NULL == *text) return -1;
	(*text)[0] = '\0';

	int buf_sz = 0;
	for (;;) {
		int rev = read_str(_str_buf);
		if (0 > rev) return -1;

		buf_sz += rev + (int)strlen(_CRLF_STR);
		if (buf_sz > req_sz) {
			req_sz *= 2;
			char *tmp = (char *)realloc(*text, req_sz);
			if (NULL == tmp) return -1;
			*text = tmp;
		}

		if ('.' == _str_buf[0]) {
			if ('\0' == _str_buf[1]) break;
			strcat(*text, _str_buf+1);
		} else strcat(*text, _str_buf);

		strcat(*text, _CRLF_STR);
	}

	return 0;
}


int ctb_recver::_cmd_retr_free(char *const text) {
	free(text);
	return 0;
}


int ctb_recver::_cmd_retr2(const int num, std::string &text) {
	text.clear();
	sprintf(_str_buf, "RETR %i", num);
	if (0 != write_str(_str_buf)) return -1;
	if (0 != _get_pop3_reply()) return -1;

	int req_sz;
	sscanf(_last_reply, "%*s%i", &req_sz);
	if (0 >= req_sz) req_sz = _MX_STR_LEN;
	text.reserve(req_sz);

	for (;;) {
		int rev = read_str(_str_buf);
		if (0 > rev) return -1;

		if ('.' == _str_buf[0]) {
			if ('\0' == _str_buf[1]) break;
			text.append(_str_buf+1);
		} else text.append(_str_buf);

		text.append(_CRLF_STR);
	}

	return 0;
}


int ctb_recver::_cmd_dele(const int num) {
	sprintf(_str_buf, "DELE %i", num);
	if (0 != write_str(_str_buf)) return -1;
	if (0 != _get_pop3_reply()) return -1;

	return 0;
}
Соседние файлы в папке src