www.pudn.com > DCPlusPlus-src.zip > AdcHub.cpp


/*  
 * Copyright (C) 2001-2004 Jacek Sieka, j_s at telia com 
 * 
 * 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. 
 * 
 * This program is distributed in the hope that it will be useful, 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 * GNU General Public License for more details. 
 * 
 * You should have received a copy of the GNU General Public License 
 * along with this program; if not, write to the Free Software 
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 
 */ 
 
#include "stdinc.h" 
#include "DCPlusPlus.h" 
 
#include "AdcHub.h" 
#include "ClientManager.h" 
#include "ShareManager.h" 
#include "StringTokenizer.h" 
#include "AdcCommand.h" 
 
void Command::parse(const string& aLine, bool nmdc /* = false */) { 
	string::size_type i = 5; 
 
	if(nmdc) { 
		// "$ADCxxx ..." 
		if(aLine.length() < 7) 
			return; 
		type = Command::TYPE_CLIENT; 
		memcpy(cmd, &aLine[4], 3); 
		i += 3; 
	} else { 
		// "yxxx ..." 
		if(aLine.length() < 4) 
			return; 
		type = aLine[0]; 
		memcpy(cmd, &aLine[1], 3); 
	} 
 
	string::size_type len = aLine.length(); 
	const char* buf = aLine.c_str(); 
	string cur; 
	cur.reserve(128); 
 
	bool toSet = false; 
	bool fromSet = false; 
 
	while(i < len) { 
		switch(buf[i]) { 
		case '\\': i++; cur += buf[i]; break; 
		case ' ':  
			// New parameter... 
			{ 
				if(type == TYPE_DIRECT && !toSet) { 
					to = CID(cur); 
					toSet = true; 
				} else if(!fromSet && type != TYPE_CLIENT) { 
					from = CID(cur); 
					fromSet = true; 
				} else { 
					parameters.push_back(cur); 
				} 
				cur.clear(); 
			} 
			break; 
		default: 
			cur += buf[i]; 
		} 
		i++; 
	} 
	if(!cur.empty()) { 
		if(!fromSet && type != TYPE_CLIENT) { 
			from = CID(cur); 
			fromSet = true; 
		} else 	if(type == TYPE_DIRECT && !toSet) { 
			to = CID(cur); 
			toSet = true; 
		} else { 
			parameters.push_back(cur); 
		} 
		cur.clear(); 
	} 
} 
 
AdcHub::AdcHub(const string& aHubURL) : Client(aHubURL, '\n', true), state(STATE_PROTOCOL) { 
} 
 
void AdcHub::handle(Command::INF, Command& c) throw() { 
	if(c.getFrom().isZero() || c.getParameters().empty()) 
		return; 
 
	User::Ptr u = ClientManager::getInstance()->getUser(c.getFrom(), this, true); 
 
	int op = 0; 
	int reg = 0; 
	int norm = 0; 
	string ve; 
	int sl = 0; 
 
	for(StringIterC i = c.getParameters().begin(); i != c.getParameters().end(); ++i) { 
		if(i->length() < 2) 
			continue; 
 
		if(i->compare(0, 2, "NI") == 0) { 
			u->setNick(i->substr(2)); 
		} else if(i->compare(0, 2, "HU") == 0) { 
			hub = u; 
		} else if(i->compare(0, 2, "DE") == 0) { 
			u->setDescription(i->substr(2)); 
		} else if(i->compare(0, 2, "I4") == 0) { 
			u->setIp(i->substr(2)); 
		} else if(i->compare(0, 2, "SS") == 0) { 
			u->setBytesShared(i->substr(2)); 
		} else if(i->compare(0, 2, "VE") == 0) { 
			ve = i->substr(2); 
		} else if(i->compare(0, 2, "EM") == 0) { 
			u->setEmail(i->substr(2)); 
		} else if(i->compare(0, 2, "OP") == 0) { 
			if(i->length() == 2) { 
				u->unsetFlag(User::OP); 
			} else { 
				u->setFlag(User::OP); 
			} 
		} else if(i->compare(0, 2, "HO") == 0) { 
			op = Util::toInt(i->substr(2)); 
		} else if(i->compare(0, 2, "HR") == 0) { 
			reg = Util::toInt(i->substr(2)); 
		} else if(i->compare(0, 2, "HN") == 0) { 
			norm = Util::toInt(i->substr(2)); 
		} else if(i->compare(0, 2, "SL") == 0) { 
			sl = Util::toInt(i->substr(2)); 
		} else if(i->compare(0, 2, "BO") == 0) { 
			if(i->length() == 2) { 
				u->unsetFlag(User::BOT); 
			} else { 
				u->setFlag(User::BOT); 
			} 
		} else if(i->compare(0, 2, "HI") == 0) { 
			if(i->length() == 2) { 
				u->unsetFlag(User::HIDDEN); 
			} else { 
				u->setFlag(User::HIDDEN); 
			} 
		} else if(i->compare(0, 2, "HU") == 0) { 
			if(i->length() == 2) { 
				u->unsetFlag(User::HUB); 
			} else { 
				u->setFlag(User::HUB); 
			} 
		} 
	} 
 
	if(!ve.empty()) { 
		if(ve.find(' ') != string::npos) { 
			ve.insert(ve.find(' ') + 1, "V:"); 
		} 
		u->setTag("<" + ve + ",M:" + string(u->getIp().empty() ? "P" : "A") + ",H:" + Util::toString(norm) + "/" +  
			Util::toString(reg) + "/" + Util::toString(op) + ",S:" +  
			Util::toString(sl) + ">" ); 
	} 
 
	if(u == getMe()) 
		state = STATE_NORMAL; 
 
	fire(ClientListener::UserUpdated(), this, u); 
} 
 
void AdcHub::handle(Command::SUP, Command& c) throw() { 
	if(find(c.getParameters().begin(), c.getParameters().end(), "+BASE") == c.getParameters().end()) { 
		disconnect(); 
		return; 
	} 
	state = STATE_IDENTIFY; 
	info(); 
} 
 
void AdcHub::handle(Command::MSG, Command& c) throw() { 
	if(c.getFrom().isZero() || c.getParameters().empty()) 
		return; 
	User::Ptr p = ClientManager::getInstance()->getUser(c.getFrom(), false); 
	if(!p) 
		return; 
	if(c.getParameters().size() == 2 && c.getParameters()[1] == "PM") { // add PM as well 
		const string& msg = c.getParameters()[0]; 
		if(c.getFrom() == getMe()->getCID()) { 
			p = ClientManager::getInstance()->getUser(c.getTo(), false); 
			if(!p) 
				return; 
		} 
		fire(ClientListener::PrivateMessage(), this, p, msg); 
	} else { 
		string msg = '<' + p->getNick() + "> " + c.getParameters()[0]; 
		fire(ClientListener::Message(), this, msg); 
	}		 
} 
 
void AdcHub::handle(Command::GPA, Command& c) throw() { 
	if(c.getParameters().empty()) 
		return; 
	salt = c.getParameters()[0]; 
	state = STATE_VERIFY; 
 
	fire(ClientListener::GetPassword(), this); 
} 
 
void AdcHub::handle(Command::QUI, Command& c) throw() { 
	if(c.getFrom().isZero()) 
		return; 
	User::Ptr p = ClientManager::getInstance()->getUser(c.getFrom(), false); 
	if(!p) 
		return; 
	ClientManager::getInstance()->putUserOffline(p); 
	fire(ClientListener::UserRemoved(), this, p); 
} 
 
void AdcHub::connect(const User* user) { 
	if(state != STATE_NORMAL) 
		return; 
	string tmp = (SETTING(CONNECTION_TYPE) == SettingsManager::CONNECTION_ACTIVE) ? "DCTM " : "DRCM "; 
	tmp += user->getCID().toBase32(); 
	tmp += " 0 NMDC/1.0\n"; 
	send(tmp); 
} 
 
void AdcHub::disconnect() { 
	state = STATE_PROTOCOL; 
	Client::disconnect(); 
} 
 
void AdcHub::hubMessage(const string& aMessage) { 
	if(state != STATE_NORMAL) 
		return; 
	string strtmp; 
	send("BMSG " + getMe()->getCID().toBase32() + " " + Command::escape(aMessage) + "\n");  
} 
 
void AdcHub::privateMessage(const User* user, const string& aMessage) {  
	if(state != STATE_NORMAL) 
		return; 
	string strtmp; 
	send("DMSG " + user->getCID().toBase32() + " " + getMe()->getCID().toBase32() + " " + Command::escape(aMessage) + " PM\n");  
} 
 
void AdcHub::kick(const User* user, const string& aMessage) {  
	if(state != STATE_NORMAL) 
		return; 
	string strtmp; 
	send("HDSC " + user->getCID().toBase32() + " KK KK " + getMe()->getCID().toBase32() + " " + Command::escape(aMessage) + "\n");  
} 
void AdcHub::ban(const User* user, const string& aMessage, time_t aSeconds) {  
	if(state != STATE_NORMAL) 
		return; 
	string strtmp; 
	send("HDSC " + user->getCID().toBase32() + " BA BA " + getMe()->getCID().toBase32() + " " + Util::toString(aSeconds) + " " + Command::escape(aMessage) + "\n");  
} 
 
void AdcHub::redirect(const User* user, const string& aHub, const string& aMessage) {  
	if(state != STATE_NORMAL) 
		return; 
	string strtmp; 
	send("HDSC " + user->getCID().toBase32() + " RD RD " + getMe()->getCID().toBase32() + " " + aHub + " " + Command::escape(aMessage) + "\n");  
} 
void AdcHub::search(int aSizeMode, int64_t aSize, int aFileType, const string& aString) {  
	if(state != STATE_NORMAL) 
		return; 
	string strtmp; 
	strtmp += "BSCH " + getMe()->getCID().toBase32() + " "; 
	if(aSizeMode == SearchManager::SIZE_ATLEAST) { 
		strtmp += ">=" + Util::toString(aSize) + " "; 
	} else if(aSizeMode == SearchManager::SIZE_ATMOST) { 
		strtmp += "<=" + Util::toString(aSize) + " "; 
	} 
	StringTokenizer st(aString, ' '); 
	string tmp; 
	for(StringIter i = st.getTokens().begin(); i != st.getTokens().end(); ++i) { 
		strtmp += "++" + Command::escape(*i) + " "; 
	} 
	strtmp[strtmp.length() - 1] = '\n'; 
	send(strtmp); 
} 
 
void AdcHub::password(const string& pwd) {  
	if(state != STATE_VERIFY) 
		return; 
	if(!salt.empty()) { 
		static const int SALT_SIZE = 192/8; 
		u_int8_t buf[SALT_SIZE]; 
		Encoder::fromBase32(salt.c_str(), buf, SALT_SIZE); 
		const string& x = pwd; 
		TigerHash th; 
		th.update(getMe()->getCID().getData(), CID::SIZE); 
		th.update(x.data(), x.length()); 
		th.update(buf, SALT_SIZE); 
		send("HPAS " + getMe()->getCID().toBase32() + " " + Encoder::toBase32(th.finalize(), TigerHash::HASH_SIZE) + "\n"); 
		salt.clear(); 
	} 
} 
 
void AdcHub::info() { 
	if(state != STATE_IDENTIFY && state != STATE_NORMAL) 
		return; 
	if(!getMe()) 
		return; 
 
	string minf = "BINF " + getMe()->getCID().toBase32(); 
	unsigned size = minf.size(); 
	string tmp; 
 
	StringMapIter i; 
#define ADDPARAM(var, content) \ 
	tmp = content; \ 
	if((i = lastInfoMap.find(var)) != lastInfoMap.end()) { \ 
		if(i->second != tmp) { \ 
			if(tmp.empty()) \ 
				lastInfoMap.erase(i); \ 
			else \ 
				i->second = tmp; \ 
			minf += var + tmp; \ 
		} \ 
	} else if(!tmp.empty()) { \ 
		minf += var + tmp; \ 
		lastInfoMap[var] = tmp; \ 
	} 
 
	ADDPARAM(" NI", Command::escape(getNick())); 
	ADDPARAM(" DE", Command::escape(getDescription())); 
	ADDPARAM(" SL", Util::toString(SETTING(SLOTS))); 
	ADDPARAM(" SS", ShareManager::getInstance()->getShareSizeString()); 
	ADDPARAM(" HN", Util::toString(counts.normal)); 
	ADDPARAM(" HR", Util::toString(counts.registered)); 
	ADDPARAM(" HO", Util::toString(counts.op)); 
	ADDPARAM(" VE", "++\\ " VERSIONSTRING); 
	if(SETTING(CONNECTION_TYPE) == SettingsManager::CONNECTION_ACTIVE) { 
		ADDPARAM(" I4", "0.0.0.0"); 
		ADDPARAM(" U4", Util::toString(SETTING(IN_PORT))); 
	} else { 
		ADDPARAM(" I4", ""); 
		ADDPARAM(" U4", ""); 
	} 
 
#undef ADDPARAM 
 
	if(minf.size() != size) { 
		minf += "\n"; 
		send(minf); 
	} 
} 
 
string AdcHub::checkNick(const string& aNick) { 
	string tmp = aNick; 
	string::size_type i = 0; 
	while( (i = tmp.find_first_of(" ", i)) != string::npos) { 
		tmp[i++]='_'; 
	} 
	return tmp; 
} 
 
void AdcHub::on(Connected) throw() {  
	dcassert(state == STATE_PROTOCOL); 
	setMe(ClientManager::getInstance()->getUser(CID(SETTING(CLIENT_ID)), this, false)); 
	lastInfoMap.clear(); 
	send("HSUP +BAS0\n"); 
	 
	fire(ClientListener::Connected(), this); 
} 
 
void AdcHub::on(Failed, const string& aLine) throw() {  
	if(getMe()) 
		ClientManager::getInstance()->putUserOffline(getMe()); 
	setMe(NULL); 
	state = STATE_PROTOCOL; 
	fire(ClientListener::Failed(), this, aLine); 
} 
/** 
 * @file 
 * $Id: AdcHub.cpp,v 1.18 2004/10/01 22:45:03 arnetheduck Exp $ 
 */