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

Лабы / 2 / lab.02.by mice / dormouse / src / main / dm_core

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

////////////////////////////////////////////////////////////////////////////////
// headers
#include "dm_core.h"


namespace dm_core {

////////////////////////////////////////////////////////////////////////////////
// cdm_dl_list public definition
cdm_dls_list::cdm_dls_list(): _first(NULL), _inum(0) {
	_mutex = CreateMutex(NULL, FALSE, NULL);
}

cdm_dls_list::~cdm_dls_list() {
	WaitForSingleObject(_mutex, INFINITE);
	CloseHandle(_mutex);
}


int cdm_dls_list::add(cdm_dl_item *const item, const int timeout) {
	if (WAIT_TIMEOUT == WaitForSingleObject(_mutex, timeout)) return -1;

	item->set(_first);
	if (NULL != _first) _first->set_prev(item);
	_first = item;

	++_inum;

	ReleaseMutex(_mutex);

	return _inum;
}


int cdm_dls_list::del(cdm_dl_item *const item, const int timeout) {
	if (WAIT_TIMEOUT == WaitForSingleObject(_mutex, timeout)) return -1;

	if (NULL != item->prev())
		item->prev()->set_next(item->next());
	else
		_first = item->next();

	if (NULL != item->next())
		item->next()->set_prev(item->prev());

	ReleaseMutex(_mutex);

	return --_inum;
}


int cdm_dls_list::lock(const int timeout) {
	int rev = WaitForSingleObject(_mutex, (timeout == -1)? INFINITE: timeout);
	if (WAIT_TIMEOUT == rev) return -1;

	return 0;
}


int cdm_dls_list::ulock() {
	if (0 == ReleaseMutex(_mutex)) return -1;

	return 0;
}



////////////////////////////////////////////////////////////////////////////////
// cdm_addr_info public definition
cdm_addr_info::cdm_addr_info() {
	_saddr_in.sin_addr.s_addr = 0;
	_saddr_in.sin_port = 0;
	_saddr_in.sin_family = AF_INET;

	_ip_str = NULL;
	_port_str = (char *)malloc(MX_PORTLEN * sizeof(char));
	_host = NULL;
}


cdm_addr_info::cdm_addr_info(const char *const host, const unsigned short port) {
	_ip_str = NULL;
	set(host, port);
}


cdm_addr_info::~cdm_addr_info() {
	free(_ip_str);
	free(_port_str);
	free(_host);
}


int cdm_addr_info::set(const sockaddr_in *const saddrin) {
	int rev = set_port(saddrin->sin_port);
	if (0 != rev) return rev;

	rev = _set_ip(saddrin->sin_addr);
	if (0 != rev) {
		if (NULL != _host) _host[0] = '\0';
		return rev;
	}

	return _updt_host();
}


int cdm_addr_info::set(const char *const host, const unsigned short port) {
	int rev = set_port(port);
	if (0 != rev) return rev;

	return set_host(host);
}


int cdm_addr_info::set_port(const unsigned short port) {
	_saddr_in.sin_port = port;
	if (NULL == _port_str) {
		// try to alloc some memory
		_port_str = (char *)malloc(MX_PORTLEN * sizeof(char));
		if (NULL == _port_str) return ERC_NOMEM;
	}
	_port_str = itoa(port, _port_str, 10);

	return 0;
}


int cdm_addr_info::set_host(const char *const host) {
	int rev = resolve_host(host, &_saddr_in.sin_addr);
	if (0 != rev) return rev;

	rev = _set_ip(_saddr_in.sin_addr);
	if (0 != rev) {
		if (NULL != _host) _host[0] = '\0';
		return rev;
	}

	return _updt_host();
}


int cdm_addr_info::resolve_host(const char *const host, in_addr *const addr) {
	addr->s_addr = inet_addr(host);
	// If the supplied server address wasn’t in the form "aaa.bbb.ccc.ddd"
	// it’s a host name, so try to resolve it
	if (INADDR_NONE == addr->s_addr) {
		hostent *ht;
		ht = gethostbyname(host);
		if (NULL == ht) return ERC_CANTRESOLVE;
		memcpy(addr, ht->h_addr_list[0], ht->h_length);
	}

	return 0;
}


////////////////////////////////////////////////////////////////////////////////
// cdm_addr_info protected definition
int cdm_addr_info::_set_ip(const in_addr ip) {
	_saddr_in.sin_addr = ip;
	
	char *ip_str = inet_ntoa(_saddr_in.sin_addr);
	if (NULL == ip_str) {
		if (NULL != _ip_str) _ip_str[0] = '\0';
		return ERC_WSA;
	}
	size_t len = strlen(ip_str) + 1;

	char *tmp = (char *)realloc(_ip_str, len * sizeof(char));
	if (NULL == tmp) {
		_ip_str = '\0';
		return ERC_NOMEM;
	}
	_ip_str = tmp;

	memcpy(_ip_str, ip_str, len * sizeof(char));

	return 0;
}


int cdm_addr_info::_updt_host() {
	hostent *ht = gethostbyaddr((char *)&_saddr_in.sin_addr,
				sizeof(_saddr_in.sin_addr), _saddr_in.sin_family);
	if (NULL == ht || NULL == ht->h_name) {
		if (NULL != _host) _host[0] = '\0';
		return 0;	// ERC_CANTRESOLVE;	// can't resolve
	}

	size_t len = strlen(ht->h_name) + 1;
	char *tmp = (char *)realloc(_host, len * sizeof(char));
	if (NULL == tmp) {
		if (NULL != _host) _host[0] = '\0';
		return ERC_NOMEM;
	}
	_host = tmp;

	memcpy(_host, ht->h_name, len * sizeof(char));

	return 0;
}



////////////////////////////////////////////////////////////////////////////////
// cdm_file_info public definition
cdm_file_info::cdm_file_info() {
	_init();
}


cdm_file_info::cdm_file_info(const cdm_file_info &ifile) {
	_init();
	set_path(ifile.path(), false);
	set_name(ifile.name());
	set_sz(ifile.sz());
}


cdm_file_info::cdm_file_info(const char *const path, const bool ch_name) {
	_init();
	set_path(path, ch_name);
}


cdm_file_info::cdm_file_info(const char *const name) {
	_init();
	set_name(name);
}


cdm_file_info::~cdm_file_info() {
	free(_name);
	free(_path);
}


int cdm_file_info::set_name(const char *const name) {
	if (NULL == name) {
		free(_name);
		_name = NULL;
		return 0;
	}

	int tmp_len = (int)strlen(name) + 1;
	char *tmp = (char *)realloc(_name, tmp_len * sizeof(char));
	if (NULL == tmp) return -1;
	memcpy(_name = tmp, name, tmp_len);
	_name_len = tmp_len - 1;

	return 0;
}


int cdm_file_info::set_path(const char *const path, const bool ch_name) {
	if (NULL == path) {
		free(_path);
		_path = NULL;
		if (ch_name) {
			free(_name);
			_name = NULL;
		}
		return 0;
	}

	int tmp_len = (int)strlen(path) + 1;
	char *tmp = (char *)realloc(_path, tmp_len * sizeof(char));
	if (NULL == tmp) return -1;
	memcpy(_path = tmp, path, tmp_len);
	_path_len = tmp_len - 1;

	if (ch_name) {
		set_name(_path + extract_filename(_path, _path_len));
	}

	return 0;
}


int cdm_file_info::extract_filename(const char *const path, const int len) {
	int i = (0 == len)? (int)strlen(path): len;
	for (--i; 0 <= i; --i) {
		if ('\\' == path[i] || '/' == path[i]) return i+1;
	}

	return 0;
}


////////////////////////////////////////////////////////////////////////////////
// cdm_file_info protected definition
void cdm_file_info::_init() {
	_name = NULL;
	_path = NULL;
	_name_len = 0;
	_path_len = 0;
	_sz = 0;
}



////////////////////////////////////////////////////////////////////////////////
// cdm_fwriter public definition
cdm_fwriter::cdm_fwriter() {
	_id = 0;
	_next_num = 0;
	_file = NULL;
}


cdm_fwriter::~cdm_fwriter() {
	if (NULL != _file) close();
}


int cdm_fwriter::open(const char *const mode) {
	_file = fopen(_file_info.path(), mode);
	if (NULL == _file) return -1;

	return 0;
}


int cdm_fwriter::close() {
	fclose(_file);

	return 0;
}


int cdm_fwriter::write(const char *const buf, const int len) {
	if (NULL == _file) return -1;
	
	if (len != fwrite(buf, sizeof(char), len, _file)) return -1;

	return 0;
}



////////////////////////////////////////////////////////////////////////////////
// cdm_socket public definition
cdm_socket::cdm_socket() {
	_state = STATE_NONE;
	_ext_state = EXTSTATE_NONE;

	_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	_pckt_sz = _DEF_PCKT_SZ;	
	_buf = (char *)malloc(_pckt_sz * sizeof(char));

	_ui_data = NULL;
}


cdm_socket::~cdm_socket() {
	free(_buf);
	if (INVALID_SOCKET != _sock)
		closesocket(_sock);
}


int cdm_socket::set_pckt_sz(const int pckt_sz) {
	if (pckt_sz <= _PCKT_MNSZ) return _pckt_sz;
	char *tmp = (char *)realloc(_buf, sizeof(char));
	if (NULL == tmp) return _pckt_sz;
	_buf = tmp;
	
	return (_pckt_sz = pckt_sz);
}


int cdm_socket::bind() {
	int rev = ::bind(_sock, _addr_info.saddr(), _addr_info.saddr_sz());
	if (0 != rev) return -1;

	return 0;
}


int cdm_socket::can_read() {
	fd_set set;
	set.fd_count = 1;
	set.fd_array[0] = _sock;
	timeval tv = {0, 0};

	int rev = select(0, &set, NULL, NULL, &tv);
	if (1 == rev) return 1;
	if (SOCKET_ERROR == rev) return -1;

	return 0;
}


int cdm_socket::can_write() {
	fd_set set;
	set.fd_count = 1;
	set.fd_array[0] = _sock;
	timeval tv = {0, 0};

	int rev = select(0, NULL, &set, NULL, &tv);
	if (1 == rev) return 1;
	if (SOCKET_ERROR == rev) return -1;

	return 0;
}


int cdm_socket::can_read_size() {
	unsigned long arg;
	if (0 != ioctlsocket(_sock, FIONREAD, &arg)) return -1;

	return (int)arg;
}


int cdm_socket::recv_dtgm() {
	if (NULL == _buf) return -1;

	_saddrin_sz = sizeof(_saddrin);
		if (cdm_core::use_bugs() && rand() > RAND_MAX/16) return 0;
	int rev = ::recvfrom(_sock, _buf, _pckt_sz, 0, (sockaddr *)&_saddrin, &_saddrin_sz);
	if (rev != _pckt_sz) return -1;

	return 0;
}


int cdm_socket::send_dtgm() {
		if (cdm_core::use_bugs() && rand() < RAND_MAX/16) return 0;
	int rev = ::sendto(_sock, _buf, _pckt_sz, 0, _addr_info.saddr(), _addr_info.saddr_sz());
	if (rev != _pckt_sz) return -1;

	return 0;
}


int cdm_socket::send_dtgm(char *const dtgm) {
		if (cdm_core::use_bugs() && rand() < RAND_MAX/16) return 0;
	int rev = ::sendto(_sock, dtgm, _pckt_sz, 0, _addr_info.saddr(), _addr_info.saddr_sz());
	if (rev != _pckt_sz) return -1;

	return 0;
}


int cdm_socket::send_dtgm(const char *const dtgm, const int dtgm_size,
				  sockaddr_in *const saddrin, const int saddrin_sz)
{
		if (cdm_core::use_bugs() && rand() < RAND_MAX/16) return 0;
	int rev = ::sendto(_sock, dtgm, dtgm_size, 0, (sockaddr *)saddrin, saddrin_sz);
	if (rev != dtgm_size) return -1;

	return 0;
}


int cdm_socket::send_dtgm(sockaddr_in *const saddrin, const int saddrin_sz) {
		if (cdm_core::use_bugs() && rand() < RAND_MAX/16) return 0;
	int rev = ::sendto(_sock, _buf, _pckt_sz, 0, (sockaddr *)saddrin, saddrin_sz);
	if (rev != _pckt_sz) return -1;

	return 0;
}



////////////////////////////////////////////////////////////////////////////////
// cdm_frecv_subs public definition
cdm_frecv_subs::cdm_frecv_subs() {
	_init();
}


cdm_frecv_subs::~cdm_frecv_subs() {
	_free_dtgms();
	_free_appeal();
	free(_dtgms);
	free(_free_tbl);
	if (NULL != _hfile && INVALID_HANDLE_VALUE != _hfile)
		CloseHandle(_hfile);
}


int cdm_frecv_subs::init() {
	_last_appeal_time = 0;

	_set_state(STATE_STOPED);
	_set_ext_state(EXTSTATE_NONE);

	_realloc_bufs();
	_realloc_appeal();

	return 0;
}


int cdm_frecv_subs::start(const bool delayed) {
	if (delayed) {
		_set_state(STATE_LAUNCHED);
		_set_ext_state(EXTSTATE_NONE);

		return 0;
	}

	stop();
	_run_time = 0;
	_last_time = clock();
	_hfile = CreateFile(_ifile.path(), GENERIC_WRITE,
						FILE_SHARE_READ, NULL, CREATE_ALWAYS,
						FILE_ATTRIBUTE_NORMAL, NULL);
	if (INVALID_HANDLE_VALUE == _hfile) return -1;

	_appeal_max = _ifile.sz() / _data_size;
	if (0 != _ifile.sz() % _data_size) ++_appeal_max;
	_done_parts = 0;
	_total_parts = _appeal_max;

	int doto;
	if (_appeal_max < _appeals_count) {
		for (int i = _appeals_count - 1; i >= _appeal_max; --i)
			_appeal->nums[i] = -1;
		doto = _appeal_max;
		_appeal_next = -1;
	} else {
		doto = _appeals_count;
		_appeal_next = _appeals_count;
	}
	for (int i = doto-1; 0 <= i; --i) {
		_appeal->nums[i] = i;
	}

	_appeal->type = DTGM_TYPE_APPEAL;
	_appeal->ss_id = _id;
	_appeal->pckt_sz = _dtgm_size;
	_appeal->count = _appeals_count;
	_appeals_changed = 0;
	_appeal->session_id = _session_id;

	_set_state(STATE_RUNNING);
	_set_ext_state(EXTSTATE_RECVING);

	return 0;
}


int cdm_frecv_subs::stop(const int ext_state) {
	_set_state(STATE_STOPED);
	_set_ext_state(ext_state);

	if (NULL != _hfile && INVALID_HANDLE_VALUE != _hfile) {
		CloseHandle(_hfile);
		_hfile = NULL;
	}

	return 0;
}


int cdm_frecv_subs::add_dtgm(sdm_dtgm_data *const dtgm) {
	for (int i = 0; _appeals_count > i; ++i) {
		if (dtgm->num != _appeal->nums[i]) continue;
		if (dtgm->data_sz > _data_size) dtgm->data_sz = _data_size;

		unsigned int fgoto = (unsigned int)(dtgm->num) * (unsigned int)_data_size;
		LARGE_INTEGER pos_64;
		pos_64.QuadPart = fgoto;
		SetFilePointer(_hfile, pos_64.LowPart, &pos_64.HighPart, FILE_BEGIN);
		DWORD w;
		BOOL br = WriteFile(_hfile, dtgm->data, dtgm->data_sz, &w, NULL);
		if (TRUE != br || w != dtgm->data_sz) return -1;

		if (_appeal_next < _appeal_max) {
			_appeal->nums[i] = _appeal_next++;
			++_appeals_changed;
		} else {
			_appeal->nums[i] = -1;
		}
		++_done_parts;

		return 0;
	}

	return -1;
}


int cdm_frecv_subs::terminate(const int ext_state) {
	_set_state(STATE_RUNNING);
	_set_ext_state(ext_state);

	return 0;
}


bool cdm_frecv_subs::can_appeal() {
	for (int i = 0; i < _appeals_count; ++i) {
		if (-1 != _appeal->nums[i]) return true;
	}

	return false;
}


bool cdm_frecv_subs::need_appeal() {
	// if (!can_appeal()) return false;

	//[debug] CString str;
	//[debug] str.AppendFormat("_appeals_count: %i\n", _appeals_count);
	//[debug] str.AppendFormat("_appeals_need_ahead: %i\n", _appeals_need_ahead);
	int max_apch = _appeals_count - _appeals_need_ahead;
	//[debug] str.AppendFormat("max_apch: %i\n", max_apch);
	//[debug] str.AppendFormat("_appeals_changed: %i\n", _appeals_changed);
	//[debug]MessageBox(NULL, str+" 1", "dormouse core", MB_OK|MB_APPLMODAL|MB_ICONINFORMATION);
	if (_appeals_changed >= max_apch) return true;

	clock_t max_apt = _reappeal_timeout * CLOCKS_PER_SEC/1000;
	//[debug] str.AppendFormat("_reappeal_timeout: %i\n", _reappeal_timeout);
	//[debug] str.AppendFormat("max_apt 1: %i\n", max_apt);
	//[debug] str.AppendFormat("_last_appeal_time: %i\n", _last_appeal_time);
	max_apt += _last_appeal_time;
	//[debug] str.AppendFormat("max_apt 2: %i\n", max_apt);
	//[debug] str.AppendFormat("clock(): %i\n", clock());
	//[debug] MessageBox(NULL, str+" 2", "dormouse core", MB_OK|MB_APPLMODAL|MB_ICONINFORMATION);
	if (max_apt < clock()) return true;

	//[debug] MessageBox(NULL, str+" 3", "dormouse core", MB_OK|MB_APPLMODAL|MB_ICONINFORMATION);
	return false;
}


sdm_dtgm_appeal *cdm_frecv_subs::get_appeal(const bool will_send) {
	if (will_send) {
		_appeals_changed = 0;
		_last_appeal_time = clock();
	}
	_appeal->done_parts = _done_parts;

	return _appeal;
}


int cdm_frecv_subs::send_discard(const int dtype, const int data, const int param) {
	if (1 != _parent->can_write()) return 0;

	sdm_dtgm_discard *discard = (sdm_dtgm_discard *)malloc(_dtgm_size * sizeof(char));
	if (NULL == discard) return -1;

	discard->type = DTGM_TYPE_DISCARD;
	discard->session_id = _session_id;
	discard->ss_id = _id;
	discard->dtype = dtype;
	discard->data = data;
	discard->param = param;

	_parent->send_dtgm((char *)discard, _dtgm_size, _iaddr.saddrin(), _iaddr.saddr_sz());

	free(discard);
	return 0;
}


int cdm_frecv_subs::set_dtgm_size(const int dtgm_size) {
	_dtgm_size = dtgm_size;
	_data_size = _dtgm_size - sizeof(sdm_dtgm_data) + sizeof(char);
	_realloc_bufs();
	
	return 0;
}


int cdm_frecv_subs::set_dtgms_count(const int dtgms_count) {
	_dtgms_count = dtgms_count;
	_realloc_bufs();

	return 0;
}


int cdm_frecv_subs::set_appeals_count(const int appeals_count) {
	_appeals_count = appeals_count;
	_realloc_appeal();

	return 0;
}


int cdm_frecv_subs::die(const bool delayed) {
	if (delayed) {
		_dying = true;
		return 0;
	}

	return 0;
}


////////////////////////////////////////////////////////////////////////////////
// cdm_frecv_subs protected definition
void cdm_frecv_subs::_init() {
	_set_state(STATE_NONE);
	_set_ext_state(EXTSTATE_NONE);

	_dtgms_count = _DEF_DTGMS_COUNT;
	_dtgm_size = _DEF_DTGM_SIZE;
	_appeals_count = _DEF_APEALS_COUNT;
	_reappeal_timeout = _DEF_REAPPEAL_TIMEOUT;
	_appeals_need_ahead = _DEF_APPEALS_NEEDAHEAD;
	_data_size = _dtgm_size - sizeof(sdm_dtgm_data) + sizeof(char);

	_dtgms = NULL;
	_free_tbl = NULL;
	_free_i = 0;
	_hfile = NULL;
	_appeal = NULL;
	_done_parts = 0;
	_total_parts = 0;

	_id = -1;
	_session_id = -1;
	_dying = false;

	_last_time = clock();
	_run_time = 0;
}


void cdm_frecv_subs::_alloc_dtgms() {
	for (int i = _dtgms_count - 1; 0 <= i; --i) {
		_dtgms[i] = (char *)malloc(_dtgm_size * sizeof(char));
	}
}


void cdm_frecv_subs::_free_dtgms() {
	if (NULL == _dtgms) return;

	for (int i = _dtgms_count - 1; 0 <= i; --i) {
		free(_dtgms[i]);
		_dtgms[i] = NULL;
	}
}


void cdm_frecv_subs::_realloc_free_tbl() {
	_free_tbl = (int *)realloc(_free_tbl, _dtgms_count * sizeof(int));
	for (int i = _dtgms_count - 1; 0 <= i; --i) {
		_free_tbl[i] = i;
	}
}

void cdm_frecv_subs::_realloc_bufs() {
	_free_dtgms();
	_dtgms = (char **)realloc(_dtgms, _dtgms_count * sizeof(char *));
	_alloc_dtgms();
	_realloc_free_tbl();
}


void cdm_frecv_subs::_realloc_appeal() {
	_appeal = (sdm_dtgm_appeal *)realloc(_appeal, _dtgm_size * sizeof(char));
	for (int i = _appeals_count - 1; 0 <= i; --i) {
		_appeal->nums[i] = -1;
	}
}


void cdm_frecv_subs::_free_appeal() {
	free(_appeal);
	_appeal = NULL;
}



////////////////////////////////////////////////////////////////////////////////
// cdm_frecver public definition
cdm_frecver::cdm_frecver() {
	_binded = false;
	_dying = false;
}


cdm_frecver::~cdm_frecver() {
	die();
}


void cdm_frecver::add_subs(cdm_frecv_subs *const subs) {
	int id = 0;
	while (NULL != get_subs(id)) ++id;

	subs->set_id(id);
	subs->init();
	_subs.add((_cdm_frecv_subs *)subs);
}


void cdm_frecver::remove_subs(cdm_frecv_subs *const subs) {
	_subs.del((_cdm_frecv_subs *)subs);
}


cdm_frecv_subs *cdm_frecver::get_subs(const int id) {
	_cdm_frecv_subs *item = (_cdm_frecv_subs *)_subs.first();
	for (; NULL != item; item = (_cdm_frecv_subs *)item->next()) {
		if (id == item->id()) return item;
	}

	return NULL;
}


cdm_frecv_subs *cdm_frecver::get_session(const int session_id) {
	_cdm_frecv_subs *item = (_cdm_frecv_subs *)_subs.first();
	for (; NULL != item; item = (_cdm_frecv_subs *)item->next()) {
		if (session_id == item->session_id()) return item;
	}

	return NULL;
}


int cdm_frecver::start(const bool delayed) {
	if (delayed) {
		_set_state(STATE_LAUNCHED);
		_set_ext_state(EXTSTATE_NONE);

		return 0;
	}

	if (!_binded && 0 != bind()) {
		_set_state(STATE_STOPED);
		_set_ext_state(EXTSTATE_NONE);

		return -1;
	}
	_binded = true;

	_set_state(STATE_RUNNING);
	_set_ext_state(EXTSTATE_LISTENING);

	return 0;
}


int cdm_frecver::stop() {
	_set_state(STATE_STOPED);
	_set_ext_state(EXTSTATE_NONE);

	return 0;
}


int cdm_frecver::die(const bool delayed) {
	if (delayed) {
		_dying = true;
		return 0;
	}

	_cdm_frecv_subs *subs = (_cdm_frecv_subs *)_subs.first();
	while (NULL != subs) {
		_cdm_frecv_subs *victim = subs;
		subs = (_cdm_frecv_subs *)subs->next();
		kill_subs(victim);
	}

	return 0;
}


int cdm_frecver::kill_subs(cdm_frecv_subs *const subs) {
	if (NULL == subs) return -1;
	
	subs->stop();
	_subs.del((_cdm_frecv_subs *)subs);
	delete (_cdm_frecv_subs *)subs;

	return 0;
}


////////////////////////////////////////////////////////////////////////////////
// cdm_fsender public definition
cdm_fsender::cdm_fsender() {
	_set_state(STATE_STOPED);

	_rerequest_timeout = _DEF_REREQUEST_TIMEOUT;

	_send_dtgm = NULL;
	_hfile = NULL;
	_total_parts = 0;
	_done_parts = 0;
	_session_id = 0;
	_dying = false;

	_run_time = 0;
	_last_time = clock();
}


cdm_fsender::~cdm_fsender() {
	free(_send_dtgm);
	if (NULL != _hfile && INVALID_HANDLE_VALUE != _hfile) {
		CloseHandle(_hfile);
		_hfile = NULL;
	}
}


int cdm_fsender::start(const bool delayed) {
	if (delayed) {	// if start is delayed
		_set_state(STATE_LAUNCHED);
		_set_ext_state(EXTSTATE_NONE);
		return 0;
	}

	stop();
	_run_time = 0;
	_last_time = clock();

	// _data_sz is a number of bytes can be sent in one datagram
	_data_sz = pckt_sz() - sizeof(sdm_dtgm_data) + sizeof(char);

	if (NULL != _hfile && INVALID_HANDLE_VALUE != _hfile) CloseHandle(_hfile);
	_hfile = CreateFile(_ifile.path(), GENERIC_READ, FILE_SHARE_READ,
		NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	if (INVALID_HANDLE_VALUE == _hfile) {
		_set_state(STATE_STOPED);
		_set_ext_state(EXTSTATE_NONE);
		return -1;
	}
	_ifile.set_sz(GetFileSize(_hfile, NULL));
	_total_parts = _ifile.sz() / _data_sz;
	if (0 != _ifile.sz() % _data_sz) ++_total_parts;
	_done_parts = 0;

	// try to alloc buffer for "send out" datagrams.
	// internal buffer (from cdm_socket) will be used to recv datagrams
	char *tmp = (char *)realloc(_send_dtgm, pckt_sz() * sizeof(char));
	if (NULL == tmp) {
		CloseHandle(_hfile);
		_hfile = NULL;
		_set_state(STATE_STOPED);
		_set_ext_state(EXTSTATE_NONE);
		return -1;
	}
	_send_dtgm = (sdm_dtgm_data *)tmp;

	//TODO: use other way to init session id
	_session_id = rand() + RAND_MAX*rand();
	_last_request_time = clock() - _rerequest_timeout;

	_set_state(STATE_RUNNING);
	_set_ext_state(EXTSTATE_REQUESTING);

	return 0;
}


int cdm_fsender::stop(const int ext_state) {
	if (NULL != _hfile && INVALID_HANDLE_VALUE != _hfile) {
		CloseHandle(_hfile);
		_hfile = NULL;
	}
	_set_state(STATE_STOPED);
	_set_ext_state(ext_state);

	return 0;
}


bool cdm_fsender::need_request() {
	int dtime = (clock() - _last_request_time)*1000/CLOCKS_PER_SEC;
	if (_rerequest_timeout > dtime) return false;

	return true;
}


int cdm_fsender::send_request() {
	if (STATE_RUNNING != state()) return 0;
	_set_ext_state(EXTSTATE_REQUESTING);

	int rev = can_write();
	if (1 != rev) return rev;

	sdm_dtgm_request *request = (sdm_dtgm_request *)_send_dtgm;
	request->type = DTGM_TYPE_REQUEST;
	request->session_id = _session_id;
	request->pckt_sz = pckt_sz();
	request->file_sz = _ifile.sz();
	request->fn_len = _ifile.name_len();

	int mx_fn_len = pckt_sz() - sizeof(sdm_dtgm_request);
	int cp_len = (mx_fn_len > _ifile.name_len())? _ifile.name_len(): mx_fn_len;
	memcpy(request->fname, _ifile.name(), cp_len);
	request->fname[cp_len] = '\0';

	rev = send_dtgm((char *)request);
	if (0 != rev) return rev;
	_last_request_time = clock();

	return 0;
}


int cdm_fsender::send_data(const int id, int *const appeals, const int count) {
	if (STATE_RUNNING != state()) return 0;
	_set_ext_state(EXTSTATE_SENDING);

	_send_dtgm->type = DTGM_TYPE_DATA;
	_send_dtgm->session_id = _session_id;
	_send_dtgm->ss_id = id;

	for (int i = 0; i < count; ++i) {
		if (-1 == appeals[i]) continue;
		unsigned int fgoto = (unsigned int)appeals[i] * (unsigned int)_data_sz;
		if (_ifile.sz() <= fgoto) continue;
		if (1 != can_write()) continue;

		LARGE_INTEGER pos_64;
		pos_64.QuadPart = fgoto;
		if (INVALID_SET_FILE_POINTER == SetFilePointer(_hfile, pos_64.LowPart, &pos_64.HighPart, FILE_BEGIN))
			continue;
		DWORD rsz;
		if (FALSE == ReadFile(_hfile, (void *)_send_dtgm->data, _data_sz, &rsz, NULL))
			continue;
		if (0 >= (int)rsz) continue;

		_send_dtgm->num = appeals[i];
		_send_dtgm->data_sz = (int)rsz;
		
		send_dtgm((char *)_send_dtgm);
	}

	return 0;

}


int cdm_fsender::send_discard(const int ss_id, const int dtype, const int data, const int param) {
	if (1 != can_write()) return 0;

	sdm_dtgm_discard *discard = (sdm_dtgm_discard *)malloc(pckt_sz() * sizeof(char));
	if (NULL == discard) return -1;

	discard->type = DTGM_TYPE_DISCARD;
	discard->session_id = _session_id;
	discard->ss_id = ss_id;
	discard->dtype = dtype;
	discard->data = data;
	discard->param = param;

	send_dtgm((char *)discard);

	free(discard);
	return 0;
}


int cdm_fsender::add_parts(const int num) {
	_done_parts += num;	//XXX: the string was "++_done_parts;"
	if (_total_parts < _done_parts) {
		_done_parts = _total_parts;
		return -1;
	}

	return _done_parts;
}


int cdm_fsender::die(const bool delayed) {
	if (delayed) {
		_dying = true;
		return 0;
	}

	return 0;
}



////////////////////////////////////////////////////////////////////////////////
// cdm_core public definition
cdm_core::cdm_core() {
	_thread_h = NULL;
	_hostname = NULL;
	_running_mtx = NULL;
	_ui = NULL;
	_nulljob_delay = _DEF_NULLJOB_DELAY;

	_last_dm_error = 0;
	_last_error = 0;
	_def_dir = NULL;

	_running_mtx = CreateMutex(NULL, FALSE, NULL);
}


cdm_core::~cdm_core() {
	if (NULL != _running_mtx) {
		_must_stop = true;
		WaitForSingleObject(_running_mtx, INFINITE);
		CloseHandle(_running_mtx);
	}

	WSACleanup();
	free(_def_dir);
}


int cdm_core::attach_ui(cdm_uiproto *const ui) {
	_ui = ui;

	return 0;
}


int cdm_core::init() {
	if (0 != WSAStartup(MAKEWORD(2,0), &_wsa_data)) return _error(ERC_SOCKSINIT);

	_def_dir = (char *)malloc(MAX_PATH*2);
	GetCurrentDirectory(MAX_PATH*2, _def_dir);

	_hostname = (char *)malloc(_HOSTNAME_MXLEN * sizeof(char));
	if (NULL != _hostname) {
		int rev = gethostname(_hostname, _HOSTNAME_MXLEN);
		if (SOCKET_ERROR == rev) _hostname[0] = '\0';
	}

	return 0;
}


int cdm_core::run(const bool nt) {
	_must_stop = false;

	if (!nt) return _run();

	_thread_h = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)_run_helper, this, 0, &_thread_id);
	if (NULL == _thread_h) return _error(ERC_CRTHREAD);

	return 0;
}


int cdm_core::pause() {
	if (NULL != _thread_h) return ERC_PAUSEMAIN;
	if ((DWORD)-1 == SuspendThread(_thread_h)) return _error(ERC_THRSUSPEND);
	return 0;
}


int cdm_core::upause() {
	if (NULL == _thread_h) return ERC_PAUSEMAIN;
	if ((DWORD)-1 == ResumeThread(_thread_h)) return _error(ERC_THRRESUME);
	return 0;
}


int cdm_core::quit() {
	_must_stop = true;

	return 0;
}


int cdm_core::swallow(cdm_frecver *const recver, int *const ch_mask) {
	int pen_sz = recver->can_read_size();

	// parsing incoming datagram
	for (; recver->pckt_sz() <= pen_sz; pen_sz -= recver->pckt_sz()) {
		
		if (0 != recver->recv_dtgm()) break;

		switch (recver->dtgm_type()) {
			case DTGM_TYPE_REQUEST:	// request for connect
				accept(recver, ch_mask);
				break;

			case DTGM_TYPE_DATA:
				get_data(recver, ch_mask);
				break;

			case DTGM_TYPE_DISCARD:
				discard(recver, ch_mask);
				break;

			default:
				// unknow datagram type. Not use it
				break;
		}
	}

	// check all sub systems
	cdm_frecv_subs *subs = recver->subs_first();
	while (NULL != subs) {
		if (subs->is_dying()) {
			cdm_frecv_subs *victim = subs;
			subs = recver->subs_next(subs);
			_ui->on_subs_rm(victim);
			recver->kill_subs(victim);
			continue;
		}

		if (cdm_frecv_subs::STATE_LAUNCHED == subs->state()) {
			if (0 != subs->start()) {
				_error(ERC_STARTFSUBS);
				_ui_on_error();
				subs->stop();
				*ch_mask |= cdm_uiproto::FRECVER_CH_STATE;
			}
			_ui->on_subs_ch(subs, cdm_uiproto::FSUBS_CH_ALL);
			subs = recver->subs_next(subs);
			continue;
		}

		if (cdm_frecv_subs::STATE_RUNNING != subs->state()) {
			subs = recver->subs_next(subs);
			continue;
		}

		subs->update_time();

		if (cdm_frecv_subs::EXTSTATE_TERMINATING == subs->ext_state()) {
			subs->send_discard(DISCARD_TYPE_CANCEL, -1);
			subs = recver->subs_next(subs);
			continue;
		} else if (cdm_frecv_subs::EXTSTATE_FINALIZING == subs->ext_state()) {
			subs->send_discard(DISCARD_TYPE_DONE, subs->done_parts());
			subs = recver->subs_next(subs);
			continue;
		}

		if (!subs->can_appeal()) {
			// all data received
			// subs->send_discard(DISCARD_TYPE_DONE, subs->total_parts());
			subs->terminate();	// stop(cdm_frecv_subs::EXTSTATE_FINALIZING);
			_ui->on_subs_ch(subs, cdm_uiproto::FSUBS_CH_ALL);
			subs = recver->subs_next(subs);
			continue;
		}

		if (subs->need_appeal()) {
			sdm_dtgm_appeal *appeal = subs->get_appeal();
			recver->send_dtgm((char *)appeal, subs->dtgm_size(),
				subs->iaddr()->saddrin(), subs->iaddr()->saddr_sz());
		}

		subs = recver->subs_next(subs);
	}

	return 0;
}


int cdm_core::spit(cdm_fsender *const sender, int *const ch_mask) {
	if (cdm_fsender::EXTSTATE_REQUESTING == sender->ext_state()) {
		if (sender->need_request()) {
			*ch_mask |= cdm_uiproto::FRECVER_CH_STATE|cdm_uiproto::FSENDER_CH_PROGRESS;
			if (0 != sender->send_request()) {
				_sock_error(ERC_FSENDER_SPIT);
				sender->stop();
				return -1;
			}
		}
	}

	int pen_sz = sender->can_read_size();

	// parsing incoming datagram
	for (; sender->pckt_sz() <= pen_sz; pen_sz -= sender->pckt_sz()) {
		// MessageBeep(MB_ICONASTERISK);
		if (0 != sender->recv_dtgm()) break;
		switch(sender->dtgm_type()) {
			case DTGM_TYPE_APPEAL:
				appeal(sender, ch_mask);
				break;

			case DTGM_TYPE_DISCARD:
				discard(sender, ch_mask);
				*ch_mask |= cdm_uiproto::FSENDER_CH_STATE;
				break;

			default:
				break;
		}
	}
			
	return 0;
}


int cdm_core::accept(cdm_frecver *const recver, int *const ch_mask) {
	sdm_dtgm_request *request = (sdm_dtgm_request *)recver->dtgm();

	if (NULL != recver->get_session(request->session_id)) return 0;

	if (0 > request->file_sz) return 0;
	int mx_bdy_sz = recver->pckt_sz() - sizeof(sdm_dtgm_request);
	if (0 >= mx_bdy_sz || mx_bdy_sz <= request->fn_len) return 0;
	// MessageBox(NULL, recver->addr_info()->ip_str(), "before", MB_OK|MB_APPLMODAL); // [bug fixed 12.04.2005]
	request->fname[mx_bdy_sz-1] = '\0';	// bug was: request->fname[recver->pckt_sz()-1] = '\0';

	// MessageBox(NULL, recver->addr_info()->ip_str(), "after", MB_OK|MB_APPLMODAL); // [/bug fixed 12.04.2005]

	cdm_frecv_subs *subs = recver->new_subs();
	subs->set_parent(recver);
	subs->iaddr()->set(recver->saddrin());
	subs->ifile()->set_name(request->fname);
	subs->ifile()->set_sz(request->file_sz);
	subs->set_session_id(request->session_id);

	recver->add_subs(subs);

	if (0 == _ui->on_accept(subs)) {
		subs->start(true);
	} else {
		subs->terminate(cdm_frecv_subs::EXTSTATE_TERMINATING);
	}
	_ui->on_subs_ch(subs, cdm_uiproto::FSUBS_CH_ALL);

	return 0;
}


int cdm_core::get_data(cdm_frecver *const recver, int *const ch_mask) {
	sdm_dtgm_data *dtgm = (sdm_dtgm_data *)recver->dtgm();
	cdm_frecv_subs *subs = recver->get_subs(dtgm->ss_id);
	if (NULL == subs) return 0;
	if (cdm_frecv_subs::STATE_RUNNING != subs->state()) return 0;
	if (cdm_frecv_subs::EXTSTATE_RECVING != subs->ext_state()) return 0;
		
		_speeed_prev = subs->done_parts();
	subs->add_dtgm(dtgm);
		_speeed += subs->done_parts() - _speeed_prev;

	_ui->on_subs_ch(subs, cdm_uiproto::FSENDER_CH_PROGRESS);

	return 0;
}


int cdm_core::discard(cdm_frecver *const recver, int *const ch_mask) {
	sdm_dtgm_discard *discard = (sdm_dtgm_discard *)recver->dtgm();
	cdm_frecv_subs *subs = recver->get_subs(discard->ss_id);
	if (NULL == subs) return -1;
	if (cdm_frecv_subs::STATE_RUNNING != subs->state()) return 0;

	switch (discard->dtype) {
		case DISCARD_TYPE_CANCEL:
			if (-1 == discard->data.i) {	// discard all (close connection)
				subs->send_discard(DISCARD_TYPE_DONE, subs->done_parts());
				subs->stop(cdm_frecv_subs::EXTSTATE_ABORTED);
			} else if (0 == discard->data.i) {
			} else if (0 < discard->data.i) {
			}
			break;

		case DISCARD_TYPE_DONE:
			if (0 == subs->total_parts() || !subs->total_done())
				subs->stop(cdm_frecv_subs::EXTSTATE_ABORTED);
			else 
				subs->stop(cdm_frecv_subs::EXTSTATE_DONE);
			break;

		default:
			break;
	}
	_ui->on_subs_ch(subs, cdm_uiproto::FSENDER_CH_ALL);

	*ch_mask |= cdm_uiproto::FSENDER_CH_STATE;

	return 0;
}


int cdm_core::appeal(cdm_fsender *const sender, int *const ch_mask) {
	sdm_dtgm_appeal *appeal = (sdm_dtgm_appeal *)sender->dtgm();
	if (appeal->session_id != sender->session_id()) return 0;

	int mx_ap_sz = sender->pckt_sz() - sizeof(sdm_dtgm_appeal);
	if ((appeal->count * (int)sizeof(int)) > mx_ap_sz) {
		appeal->count = mx_ap_sz;
	}

	sender->send_data(appeal->ss_id, appeal->nums, appeal->count);

	*ch_mask |= cdm_uiproto::FSENDER_CH_PROGRESS;
	sender->set_done_parts(appeal->done_parts);

	return 0;
}


int cdm_core::discard(cdm_fsender *const sender, int *const ch_mask) {
	sdm_dtgm_discard *discard = (sdm_dtgm_discard *)sender->dtgm();
	if (discard->session_id != sender->session_id()) return 0;

	switch (discard->dtype) {
		case DISCARD_TYPE_CANCEL:
			if (-1 == discard->data.i) {
				sender->send_discard(discard->ss_id, DISCARD_TYPE_DONE, sender->done_parts());
				sender->stop(cdm_fsender::EXTSTATE_CANCELED);
			} else if (0 == discard->data.i) {
				sender->accepted();
			} else if (0 < discard->data.i) {
				if (0 > sender->add_parts(discard->data.i)) {
					_pure_dm_error(ERC_FSENDER_FSIZE);
					_ui_on_dm_error();
					sender->stop();
				}
			}
			break;

		case DISCARD_TYPE_DONE:
			sender->set_done_parts(discard->data.i);
			sender->send_discard(discard->ss_id, DISCARD_TYPE_DONE, sender->done_parts());
			sender->stop(cdm_fsender::EXTSTATE_DONE);
			break;

		default:
			break;
	}

	*ch_mask |= cdm_uiproto::FSENDER_CH_PROGRESS;
	*ch_mask |= cdm_uiproto::FSENDER_CH_STATE;

	return 0;
}


cdm_frecver *cdm_core::new_frecver(const char *const host, const unsigned short port) {
	_cdm_frecver *recver = new _cdm_frecver;
	if (0 != recver->addr_info()->set(host, port)) {
		delete recver;
		_sock_error(ERC_SOCKSRESOLVE);
		return NULL;
	}

	return recver;
}


cdm_fsender *cdm_core::new_fsender(const char *const host, const unsigned short port) {
	_cdm_fsender *sender = new _cdm_fsender;
	if (0 != sender->addr_info()->set(host, port)) {
		delete sender;
		_sock_error(ERC_SOCKSRESOLVE);
		return NULL;
	}
	sender->iaddr()->set(host, port);

	return sender;
}


int cdm_core::add_frecver(cdm_frecver *const recver) {
	if (0 >= _frecvers.add((_cdm_frecver *)recver, 3000)) {
		return _pure_dm_error(ERC_ADDFRECVER);
	}

	return 0;
}


int cdm_core::add_fsender(cdm_fsender *const sender) {
	if (0 >= _fsenders.add((_cdm_fsender *)sender, 3000)) {
		return _pure_dm_error(ERC_ADDFSENDER);
	}

	return 0;
}


int cdm_core::start_frecver(cdm_frecver *const recver, const bool delayed) {
	if (0 != recver->start(delayed)) return _error(ERC_STARTFRECVER);

	return 0;
}


int cdm_core::start_fsender(cdm_fsender *const sender, const bool delayed) {
	if (0 != sender->start(delayed)) return _error(ERC_STARTFSENDER);

	return 0;
}


int cdm_core::del_frecver(cdm_frecver *const recver) {
	cdm_frecv_subs *subs;
	while (NULL != (subs = recver->subs_first())) {
		_ui->on_subs_rm(subs);
		recver->kill_subs(subs);
	}

	_ui->on_frecver_rm(recver);
	if (0 != _frecvers.del((_cdm_frecver *)recver, 3000)) return -1;

	return 0;
}


int cdm_core::del_fsender(cdm_fsender *const sender) {
	_ui->on_fsender_rm(sender);
	if (0 != _fsenders.del((_cdm_fsender *)sender, 3000)) return -1;

	return 0;
}


int cdm_core::free_frecver(cdm_frecver *const recver) {
	delete (_cdm_frecver  *)recver;

	return 0;
}


int cdm_core::free_fsender(cdm_fsender *const sender) {
	delete (_cdm_fsender *)sender;

	return 0;
}


////////////////////////////////////////////////////////////////////////////////
// cdm_core protected definition
int cdm_core::_run() {
	// if (NULL != _running_mtx) CloseHandle(_running_mtx);
	// _running_mtx = CreateMutex(NULL, FALSE, NULL);
	WaitForSingleObject(_running_mtx, INFINITE);
	// _must_stop = false;

	while (!_must_stop) {
		//_speeed = 0;
		int job_recv;
		int job_send;
		int rev;
		rev = _parse_frecvers(&job_recv);

		rev = _parse_fsenders(&job_send);

		if (0 == job_recv + job_send) Sleep(_nulljob_delay);
	}

	_cdm_fsender *sender;
	while (NULL != (sender = (_cdm_fsender *)_fsenders.first())) {
		del_fsender(sender);
		free_fsender(sender);
	}

	_cdm_frecver *recver;
	while (NULL != (recver = (_cdm_frecver *)_frecvers.first())) {
		cdm_frecv_subs *subs;
		while (NULL != (subs = recver->subs_first())) {
			_ui->on_subs_rm(subs);
			recver->kill_subs(subs);
		}

		del_frecver(recver);
		free_frecver(recver);
	}

	_ui->on_quit();

	ReleaseMutex(_running_mtx);
	// CloseHandle(_running_mtx);
	// _running_mtx = NULL;

	return 0;
}


int  cdm_core::_parse_frecvers(int *const num) {
	if (NULL != num) *num = 0;
	if (0 != _frecvers.lock()) return -1;

	int c = 0;
	_cdm_frecver *recver = (_cdm_frecver *)_frecvers.first();

	while (NULL != recver) {
		int ch_mask = 0;
		switch (recver->state()) {
			case cdm_frecver::STATE_RUNNING:
				if (0 != swallow(recver, &ch_mask)) {
					_ui_on_error();
					recver->stop();
					ch_mask = cdm_uiproto::FRECVER_CH_ALL;
				} else ++c;
				break;

			case cdm_frecver::STATE_LAUNCHED:
				if (0 != recver->start()) {
					_sock_error(ERC_STARTFSENDER);
					_ui_on_error();
					recver->stop();
				}
				ch_mask |= cdm_uiproto::FRECVER_CH_ALL;
				break;

			default:
				break;
		}

		if (0 != ch_mask) _ui->on_frecver_ch(recver, ch_mask);

		if (recver->is_dying()) {
			_cdm_frecver *victim = recver;
			recver = (_cdm_frecver *)recver->next();
			del_frecver(victim);
			free_frecver(victim);
		} else recver = (_cdm_frecver *)recver->next();
	}

	while (0 != _frecvers.ulock());

	if (NULL != num) *num = c;
	return 0;
}


int  cdm_core::_parse_fsenders(int *const num) {
	if (NULL != num) *num = 0;
	if (0 != _fsenders.lock()) return -1;

	int c = 0;
	_cdm_fsender *sender = (_cdm_fsender *)_fsenders.first();

	while (NULL != sender) {
		int ch_mask = 0;
		switch (sender->state()) {
			case cdm_fsender::STATE_RUNNING:
						_speeed_prev = sender->done_parts();
				if (0 != spit(sender, &ch_mask)) {
					_ui_on_error();
				} else ++c;
						_speeed += sender->done_parts() - _speeed_prev;
				sender->update_time();
				break;

			case cdm_fsender::STATE_LAUNCHED:
				if (0 != sender->start()) {
					_error(ERC_STARTFSENDER);
					_ui_on_error();
					sender->stop();
				}
				ch_mask = cdm_uiproto::FSENDER_CH_ALL;
				break;

			default:
				break;
		}

		if (0 != ch_mask) _ui->on_fsender_ch(sender, ch_mask);

		if (sender->is_dying()) {
			_cdm_fsender *victim = sender;
			sender = (_cdm_fsender *)sender->next();
			del_fsender(victim);
			free_fsender(victim);
		} else sender = (_cdm_fsender *)sender->next();
	}

	while (0 != _fsenders.ulock());

	if (NULL != num) *num = c;
	return 0;
}



// private
bool cdm_core::_use_bugs = false;



}	// end of namespace "dm_core"
Соседние файлы в папке main