#include "netsoul.h" int NetSoul::m_defaultTimeout = 3000; NetSoul::NetSoul(QObject *parent) : QObject(parent) { m_sock = new QTcpSocket(this); m_sock->setSocketOption(QAbstractSocket::KeepAliveOption, 1); connect(m_sock, SIGNAL(hostFound()), this, SLOT(socketHostFound())); connect(m_sock, SIGNAL(connected()), this, SLOT(socketConnected())); connect(m_sock, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(socketError(QAbstractSocket::SocketError))); connect(m_sock, SIGNAL(readyRead()), this, SLOT(socketReadyRead())); connect(m_sock, SIGNAL(disconnected()), this, SLOT(socketDisconnected())); m_timeout = new QTimer(this); m_timeout->setSingleShot(true); connect(m_timeout, SIGNAL(timeout()), this, SLOT(timedout())); m_state = Disconnected; m_userState = Actif; setLocation(QString()); m_data = "Epimafia NetSoul"; } QAbstractSocket::SocketError NetSoul::getLastNetworkError() { return m_lastNetworkError; } QString NetSoul::getId() const { return m_id; } EpiUser NetSoul::getEpiUser() const { return m_epiUser; } NetSoul::User NetSoul::getUser() const { return m_user; } NetSoul::State NetSoul::getState() const { return m_state; } QString NetSoul::getLocation() const { return m_location; } NetSoul::UserState NetSoul::getUserStateFromString(QString state) { state = state.toLower().trimmed(); if(state == "away") return Away; else if(state == "lock" || state == "locked") return Lock; else if(state == "actif") return Actif; else if(state == "connection" || state =="login") return Login; else if(state == "logout") return Logout; return UnknownState; } QString NetSoul::getStringFromUserState(NetSoul::UserState state) { const QMetaObject & mo = NetSoul::staticMetaObject; QMetaEnum me = mo.enumerator(mo.indexOfEnumerator("UserState")); return me.valueToKey(state); } QString NetSoul::getDecodedString(QString enc) { enc.replace("\\n", "\n"); int min = 0; int i = -1; while((i = enc.indexOf("%", min)) != -1) { bool ok = false; int ch = enc.mid(i + 1, 2).toInt(&ok, 16); if(ok) enc.replace(i, 3, QChar::fromLatin1((char)ch)); else min = i + 1; } return enc; } QString NetSoul::getEncodedString(QString dec) { QString out; foreach(QChar c, dec) { if((c.toAscii() >= 'a' && c.toAscii() <= 'z') || (c.toAscii() >= 'A' && c.toAscii() <= 'Z') || (c.toAscii() >= '0' && c.toAscii() <= '9')) out += c; else out += "%" + QString::number((unsigned char)c.toLatin1(), 16); } return out; } QString NetSoul::getStringFromState(NetSoul::State state) { const QMetaObject & mo = NetSoul::staticMetaObject; QMetaEnum me = mo.enumerator(mo.indexOfEnumerator("State")); return me.valueToKey(state); } void NetSoul::setLocation(QString l) { if(l.isEmpty()) l = "Anywhere..."; m_location = l; } void NetSoul::setLogin(EpiUser user) { m_epiUser = user; } void NetSoul::login(EpiUser user) { setLogin(user); login(); } void NetSoul::login() { if(m_state != Disconnected && m_state != ProtocolError && m_state != NetworkError && m_state != NetSoul::BadLogin) return; m_id.clear(); m_buf.clear(); m_nextCommands.clear(); m_currentCmd = NoCommand; m_lastNetworkError = QAbstractSocket::UnknownSocketError; setState(HostLookUp); startTimeout(); m_sock->connectToHost("ns-server.epita.fr", 4242); } void NetSoul::logout() { if(m_state == Disconnecting || m_state == Disconnected || m_state == ProtocolError || m_state == NetworkError || m_state == NetSoul::BadLogin) return; setState(Disconnecting); if(m_state == Connecting) { m_sock->abort(); setState(Disconnected); } else write("exit\n", 100); } void NetSoul::setUserState(NetSoul::UserState state) { addCommand("state " + NetSoul::getStringFromUserState(state).toLower() + ":" + QString::number(QDateTime::currentDateTime().toTime_t()) + "\n", ChangeStatus, -1); } void NetSoul::whois(QString user) { whois(QStringList() << user); } void NetSoul::whois(QStringList users) { addCommand("user_cmd who {" + users.join(",") + "}\n", Whois); } void NetSoul::addToWatchList(QString user) { addToWatchList(QStringList() << user); } void NetSoul::addToWatchList(QStringList users) { addCommand("user_cmd watch_log_user {" + users.join(",") + "}\n", AddToWatchList, -1); } void NetSoul::listUsers(QObject* receiver, const char* member, QString user) { NSListUsersEmitter* emitter = new NSListUsersEmitter(this); connect(emitter, SIGNAL(listed(NetSoul::Users)), receiver, member); m_listEmitters.append(emitter); if(!user.isEmpty()) user.prepend(" "); addCommand("list_users" + user + "\n", ListUsers); } void NetSoul::sendMessage(QString user, QString location, QString message) { addCommand("user_cmd msg *:" + user + "@*" + getEncodedString(location) + "* msg " + getEncodedString(message) + "\n", SendMessage, -1); } void NetSoul::sendMessage(NetSoul::User user, QString message) { sendMessage(user.login, user.location, message); } void NetSoul::socketConnected() { setState(Connected); startTimeout(); } void NetSoul::socketDisconnected() { if(m_state == Disconnected || m_state == ProtocolError || m_state == NetworkError || m_state == NetSoul::BadLogin) return; if(m_state == Disconnecting) setState(Disconnected); else setState(ProtocolError); } void NetSoul::socketError(QAbstractSocket::SocketError e) { if(m_state == Disconnected || m_state == Disconnecting) return; m_lastNetworkError = e; setState(NetworkError); } void NetSoul::socketReadyRead() { if(m_currentCmd != NoCommand) startTimeout(); if(!m_sock->canReadLine() && !m_buf.contains('\n')) return; if(m_state == Connected) { QString salut = m_sock->readLine(); QRegExp reg("^salut ([0-9]+) ([0-9a-f]{32}) ([0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}) ([0-9]{1,5}) ([0-9]+)\n$"); if(salut.indexOf(reg) != -1) { if(!m_epiUser.hasLogin()) { setState(LoggedIn); nextCommand(); } else { m_user.data = m_data; m_user.id = reg.cap(1); m_user.ip = reg.cap(3); m_user.location = m_location; m_user.login = m_epiUser.getLogin(); setState(LoggingIn); write("auth_ag ext_user none none\next_user_log " + m_epiUser.getLogin() + " " + QString(QCryptographicHash::hash(QString(reg.cap(2) + "-" + reg.cap(3) + "/" + reg.cap(4) + m_epiUser.getPwd_socks()).toStdString().c_str(), QCryptographicHash::Md5).toHex()) + " " + getEncodedString(m_location) + " " + getEncodedString(m_data) + "\n"); } } else protError(); return; } else if(m_state == LoggingIn) { m_buf += m_sock->readAll(); if(m_buf.count("\n") == 2) { if(m_buf == "rep 002 -- cmd end\nrep 002 -- cmd end\n") { setState(LoggedIn); nextCommand(); } else if(m_buf == "rep 002 -- cmd end\nrep 033 -- ext user identification fail\n") { setState(BadLogin); m_sock->abort(); } else protError(); m_buf.clear(); } return; } else if(m_state == LoggedIn) { QString d = m_sock->readAll(); m_buf += d; if(m_currentCmd == Whois) { QRegExp user_cmd("^user_cmd ([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:\\|]*) \\| .* -- cmd end\n"); if(m_buf.contains(user_cmd)) { m_buf.remove(0, user_cmd.cap().size()); foreach(QString cmd, user_cmd.cap().split("\n", QString::SkipEmptyParts)) { QRegExp get_cmd("^user_cmd ([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:\\|]*) \\| "); if(cmd.contains(get_cmd)) { cmd.remove(0, get_cmd.cap().size()); QRegExp reg("^who ([0-9]+) ([^ ]+) ([0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}) ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+) ([^ ]+) ([^ ]+) ([^ ]+) ([^ ]+) ([^ ]+)$"); if(cmd.contains(reg)) { QStringList args = reg.capturedTexts(); args.removeAt(0); for(int i = 0; i <= 4; ++i) args.removeAt(3); User usr; int i = 0; usr.id = args.at(i++); usr.login = args.at(i++); usr.ip = args.at(i++); usr.location = getDecodedString(args.at(i++)); usr.promo = getDecodedString(args.at(i++)); usr.stateString = args.at(i++).split(":").at(0); usr.state = getUserStateFromString(usr.stateString); usr.data = getDecodedString(args.at(i++)); emit userDataChanged(usr); } else if(cmd != "who rep 002 -- cmd end") { protError(); return; } } else { protError(); return; } } commandFinished(); } else return; } else if(m_currentCmd == ListUsers) { QString cmdEnd = "rep 002 -- cmd end\n"; int index = m_buf.indexOf(cmdEnd); if(index == -1) return; QString users = m_buf.left(index + cmdEnd.size()); m_buf.remove(0, users.size()); Users usrs; foreach(QString user, users.split('\n', QString::SkipEmptyParts)) { QRegExp reg("^([0-9]+) ([^ ]+) ([0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}) ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+) ([^ ]+) ([^ ]+) ([^ ]+) ([^ ]+) ([^ ]+)$"); if(user.contains(reg)) { QStringList args = reg.capturedTexts(); args.removeAt(0); for(int i = 0; i <= 4; ++i) args.removeAt(3); User usr; int i = 0; usr.id = args.at(i++); usr.login = args.at(i++); usr.ip = args.at(i++); usr.location = getDecodedString(args.at(i++)); usr.promo = getDecodedString(args.at(i++)); usr.stateString = args.at(i++).split(":").at(0); usr.state = getUserStateFromString(usr.stateString); usr.data = getDecodedString(args.at(i++)); usrs.append(usr); } else if(user != "rep 002 -- cmd end") { protError(); return; } } if(!m_listEmitters.isEmpty()) { NSListUsersEmitter* emitter = m_listEmitters.at(0); emitter->emitList(usrs); emitter->deleteLater(); m_listEmitters.removeAt(0); } commandFinished(); } else { QRegExp user_cmd("^user_cmd ([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:]*):([^:\\|]*) \\| ([^\n]+)\n"); QRegExp ping("^ping ([0-9]+) \n"); if(m_buf.contains(user_cmd)) { m_buf.remove(0, user_cmd.cap().size()); User usr; usr.id = user_cmd.cap(1); usr.login = user_cmd.cap(4).split("@").at(0); usr.ip = user_cmd.cap(4).split("@").at(1); usr.location = getDecodedString(user_cmd.cap(6)); usr.promo = user_cmd.cap(7); QString cmd = user_cmd.cap(8).trimmed(); QRegExp state("^state ([^ ]+):([0-9]+)$"); QRegExp msg("^msg ([^ ]*) dst=(.+)$"); if(cmd == "logout") { usr.state = Logout; usr.stateString = getStringFromUserState(usr.state); emit userDataChanged(usr); commandFinished(); } else if(cmd == "login") { usr.state = Login; usr.stateString = getStringFromUserState(usr.state); emit userDataChanged(usr); commandFinished(); } else if(cmd.contains(msg)) { Message message; message.from = usr; message.message = getDecodedString(msg.cap(1)); emit newMessage(message); commandFinished(); } else if(cmd.contains(state)) { usr.stateString = state.cap(1); usr.state = getUserStateFromString(usr.stateString); emit userDataChanged(usr); commandFinished(); } else emit unknownCommand("user_cmd: " + cmd); } else if(m_buf.contains(ping)) { m_buf.remove(0, ping.cap().size()); addCommand("ping\n", Ping, -1); } else { int i = m_buf.indexOf("\n"); emit unknownCommand(m_buf.left(i + 1)); m_buf.remove(0, i + 1); } } } else { m_buf.clear(); return; } socketReadyRead(); } void NetSoul::socketHostFound() { setState(Connecting); } void NetSoul::setState(NetSoul::State s) { m_state = s; emit stateChanged(m_state); } void NetSoul::protError() { if(m_state == Disconnected || m_state == NetworkError || m_state == ProtocolError || m_state == NetSoul::BadLogin) return; setState(ProtocolError); m_sock->abort(); } void NetSoul::startTimeout() { startTimeout(m_defaultTimeout); } void NetSoul::startTimeout(int t) { m_timeout->start(t); } void NetSoul::stopTimeout() { m_timeout->stop(); } void NetSoul::write(QString c) { write(c, m_defaultTimeout); } void NetSoul::write(QString c, int t) { if(t != -1) startTimeout(t); m_sock->write(c.toAscii().constData()); } void NetSoul::timedout() { if(m_state == Disconnected || m_state == NetworkError || m_state == ProtocolError || m_state == NetSoul::BadLogin) return; if(m_state == Disconnecting) { setState(Disconnected); m_sock->abort(); return; } m_lastNetworkError = QAbstractSocket::SocketTimeoutError; setState(NetworkError); m_sock->abort(); } void NetSoul::nextCommand() { stopTimeout(); if(m_currentCmd == NoCommand && !m_nextCommands.isEmpty() && (m_state == LoggedIn || (m_state == Connected && !m_epiUser.hasLogin()))) { NextCommand cmd = m_nextCommands.at(0); m_nextCommands.removeAt(0); m_currentCmd = cmd.commandName; write(cmd.commandString, cmd.timeout); if(cmd.timeout == -1) { m_currentCmd = NoCommand; nextCommand(); } } } void NetSoul::addCommand(QString cmd, NetSoul::Command command) { addCommand(cmd, command, m_defaultTimeout); } void NetSoul::addCommand(QString cmd, NetSoul::Command command, int t) { NextCommand c; c.commandName = command; c.commandString = cmd; c.timeout = t; m_nextCommands.append(c); nextCommand(); } void NetSoul::commandFinished() { m_currentCmd = NoCommand; nextCommand(); } QDebug &operator<< (QDebug d, NetSoul::Command& cmd) { const QMetaObject & mo = NetSoul::staticMetaObject; QMetaEnum me = mo.enumerator(mo.indexOfEnumerator("Command")); return d<>(QDataStream& stream, NetSoul::Message& msg) { QString m; NetSoul::User u; stream >> u; stream >> m; if(stream.status() == QDataStream::Ok) { msg.from = u; msg.message = m; } return stream; } QDataStream &operator<<(QDataStream& stream, NetSoul::UserState& state) { return stream << (quint8)state; } QDataStream &operator>>(QDataStream& stream, NetSoul::UserState& state) { quint8 s; stream >> s; if(stream.status() == QDataStream::Ok) state = (NetSoul::UserState)s; return stream; } QDataStream &operator<<(QDataStream& stream, NetSoul::State& state) { return stream << (quint8)state; } QDataStream &operator>>(QDataStream& stream, NetSoul::State& state) { quint8 s; stream >> s; if(stream.status() == QDataStream::Ok) state = (NetSoul::State)s; return stream; } QDataStream &operator<<(QDataStream& stream, NetSoul::User& user) { return stream << user.data << user.id << user.ip << user.location << user.login << user.promo << user.state << user.stateString; } QDataStream &operator>>(QDataStream& stream, NetSoul::User& user) { QString data, id, ip, location, login, promo, stateString; NetSoul::UserState state; stream >> data; stream >> id; stream >> ip; stream >> location; stream >> login; stream >> promo; stream >> state; stream >> stateString; if(stream.status() == QDataStream::Ok) { user.data = data; user.id = id; user.ip = ip; user.location = location; user.login = login; user.promo = promo; user.state = state; user.stateString = stateString; } return stream; }