/* This file is part of dc_qt. dc_qt 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. dc_qt 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 dc_qt; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "dctc_connection.h" #include "util.h" #include "dc_settings.h" #include "debugdlg.h" #include "mainwdgt.h" //#include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef _DEBUG #include #include #endif using namespace std; const int MAX_WAIT = 180; dctc_connection::dctc_connection(QObject *parent, const char *name) : QObject(parent, name) { pid = 0; dctcproc = 0; } void dctc_connection::kill_client() { if(dctcproc) dctcproc->kill(); // else kill(pid, SIGQUIT); pid = 0; } #define GET_SETTING(x, y) \ if (!settings.get_setting(x).isEmpty()) \ { \ arglst += y; \ arglst += settings.get_setting(x); \ } /// Handles the forking of the client to start the dctc text client. int dctc_connection::fork_client(const char *hubip,const QString &pwd,const QString &profile) { settings.set_profile(profile); QStringList arglst; arglst += "dctc"; arglst += "-l"; // arglst += "-k"; arglst += "-t"; arglst += "-g"; arglst += hubip; GET_SETTING("nick", "-n"); // GET_SETTING("description", "-i"); GET_SETTING("email","-e"); GET_SETTING("slots", "-d"); GET_SETTING("local_ip", "-a"); GET_SETTING("port", "-p"); if( settings.get_setting("use_proxy")=="true") { if( settings.get_setting("use_socks")=="true") { arglst += "--socksaddr=" + settings.get_setting("proxy_ip"); arglst += "--socksport=" + settings.get_setting("proxy_port"); GET_SETTING("proxy_username","-U"); GET_SETTING("proxy_password","-K"); } else { arglst += "--connectproxy=" + settings.get_setting("proxy_ip") + ":" + settings.get_setting("proxy_port"); } } if(!settings.get_setting("connection").isEmpty()) { arglst += "-c"; arglst += settings.get_setting("connection"); } arglst += "--precmd"; arglst += "/RECOND 30"; arglst += "--precmd"; arglst += "/REBUILD 900"; arglst += "--precmd"; arglst += "/LPATH " + settings.get_setting("dl_dir"); // bra eller inte? // arglst += "--precmd"; // arglst += "/DDL"; arglst += "--precmd"; arglst += "/FOLLOWFORCE"; if(!pwd.isEmpty()) { arglst += "--precmd"; arglst += "/PASSWD " + pwd; } #ifdef _DEBUG arglst += "--precmd"; arglst += "/LOG out.log"; #endif if(settings.get_setting("desc_tag")=="dc++") { arglst+="--precmd"; arglst += "/DFLAG fake_dcpp_client 1"; arglst+="--precmd"; arglst += "/DFLAG fake_dcpp_version 0.261"; } else if(settings.get_setting("desc_tag")=="dctc") { arglst+="-m"; } arglst+="-i"; // DO NOT MOVE if(settings.get_setting("desc_tag")=="dcgui") { int numConnections = MainWdgt::getInstance().getNumConnections(); QString mode; if( settings.get_setting("passive_mode")=="true") mode = "P"; else mode="A"; QString sl(settings.get_setting("slots")); QString limit(settings.get_setting("ul_rate_limit")); QString tag = ""; arglst+=settings.get_setting("description")+" "+tag; } else arglst+=settings.get_setting("description"); if(settings.get_setting("allow_file_list_ul")=="true") { arglst+="--precmd"; arglst += "\"/DFLAG sharelist_dl 1\""; } if(settings.get_setting("same_hub_only")=="true") { arglst+="--precmd"; arglst += "/ABORTLEAVED"; } else { arglst+="--precmd"; arglst += "/NOABORTLEAVED"; } // TODO: proper error handling here! if( settings.get_setting("dl_dir").isEmpty()) debugWin->print("dctc_connection: ERROR: no DLDIR specified, this is gonna fuck.\n"); QStringList shares = QStringList::split(";",settings.get_setting("shares")); for ( QStringList::Iterator it = shares.begin(); it != shares.end(); ++it ) { arglst += "-s"; arglst += *it; } if (settings.get_setting("passive_mode") == "true") arglst += "-f"; // Print the arglist for ( QStringList::Iterator it = arglst.begin(); it != arglst.end(); ++it ) debugWin->print(*it + " "); debugWin->print("\n"); dctcproc = new QProcess(arglst,this,"dctcprocess"); if(!dctcproc->start()) { debugWin->print("Error starting dctc\n"); return SLEEP_TIMED_OUT; } connect(dctcproc,SIGNAL(readyReadStderr()), this, SLOT(hub_stderr())); // we use the pid later to kill the correct dctc client pid = dctcproc->processIdentifier(); // Wait for the socket to pop up char pidhex[9]; sprintf(pidhex, "%08X", pid); QString socket_path = settings.get_home_dir() + "/.dctc/running/"; socket_path += "dctc-"; socket_path += pidhex; socket_path += "-"; socket_path += hubip; debugWin->print("Waiting for socket file..."); bool bajs = false; int snopp = 20; // 20*250 = 5 sec while(!QFile::exists(socket_path) && !bajs & snopp>0) { if(!dctcproc->isRunning()) bajs = true; usleep(250*1000); // sleep 250ms snopp--; } debugWin->print("Najs!\n"); if(bajs) return COULD_NOT_CONNECT; if(snopp==0) return SLEEP_TIMED_OUT; connect(dctcproc,SIGNAL(processExited()), this, SIGNAL(hub_died())); return connect_to_running(hubip); } void dctc_connection::hub_stderr() { if( dctcproc->canReadLineStderr() ) debugWin->print( "From dctc-stderr: " + dctcproc->readLineStderr() + "\n"); } int dctc_connection::connectToMaster() { dctc_fd = socket(PF_UNIX, SOCK_STREAM, 0); fcntl(dctc_fd,F_SETFL,O_NONBLOCK); if (dctc_fd == -1) return COULD_NOT_CREATE_SOCKET; name.sun_family = AF_UNIX; QString socket_path = settings.get_home_dir() + "/.dctc/running/master"; debugWin->print("dctc_connection: Waiting for master socket file..."); while(!QFile::exists(socket_path)) usleep(250*1000); strcpy(name.sun_path, socket_path.latin1()); int retcode = ::connect(dctc_fd, (sockaddr*)&name, sizeof(struct sockaddr_un)); if (retcode == -1) { return COULD_NOT_CONNECT_TO_SOCKET; } dctc_socket = new QSocketDevice( dctc_fd, QSocketDevice::Stream ); read_notifier = new QSocketNotifier(dctc_fd, QSocketNotifier::Read); QObject::connect(read_notifier, SIGNAL(activated(int)), this, SLOT(dataReceived(int))); pid = 666; return CONNECT_SUCCESS; } int dctc_connection::connect_to_running(const char *hubip, int new_pid) { QString debug; debug.sprintf("pid = %d", new_pid); if (new_pid) pid = new_pid; // We use dctc_fd later when creating the QSocketDevice dctc_fd = socket(PF_UNIX, SOCK_STREAM, 0); fcntl(dctc_fd,F_SETFL,O_NONBLOCK); if (dctc_fd == -1) { return COULD_NOT_CREATE_SOCKET; } name.sun_family = AF_UNIX; char pidhex[9]; sprintf(pidhex, "%08X", pid); // we open the socket created by dctc in $HOME/.dctc/running/dctc-xxxxx-yyyyy // where xxxxx == the pid of the client in hex // yyyyy == the ip of the hub QString socket_path = settings.get_home_dir() + "/.dctc/running/"; socket_path += "dctc-"; socket_path += pidhex; socket_path += "-"; socket_path += hubip; strcpy(name.sun_path, socket_path.latin1()); int retcode = ::connect(dctc_fd, (sockaddr*)&name, sizeof(struct sockaddr_un)); dctc_socket = new QSocketDevice( dctc_fd, QSocketDevice::Stream ); if (retcode == -1) { debugWin->print("Could not connect to socket " + socket_path + "\n"); perror("connect"); // kill_client(); return COULD_NOT_CONNECT_TO_SOCKET; } // the notifier calls dataReceived whenever the socket has something to read read_notifier = new QSocketNotifier(dctc_fd, QSocketNotifier::Read); QObject::connect(read_notifier, SIGNAL(activated(int)), this, SLOT(dataReceived(int))); return CONNECT_SUCCESS; } // handler for receiving data through the socket void dctc_connection::dataReceived(int) { // It seemed like this function kept getting called and just returned // immediately. That "if (!pid) return;" thing seemed like a possible // cause for that behaviour. // I thought that getting all available data before trying to disconnect // could be a good idea. It was, CPU usage dropped. // Problem is, WTF do we need to disconnect in the first place? Why is pid == 0? Q_LONG n; // Get the available amount of data n = dctc_socket->bytesAvailable(); char c[n+1]; if( dctc_socket->readBlock( c, n ) == -1 ) { debugWin->print("Error while reading from socket\n"); return; } c[n] = '\0'; // Provar att flytta skiten ner hit... // if there is no running dctc, then we can't receive anything if (!pid) { // How about adding read_notifier->setEnabled(false) here? --est QObject::disconnect(this, SLOT(dataReceived(int))); return; } // message = c; QTextCodec::setCodecForCStrings( QTextCodec::codecForName( settings.get_setting("font_encoding"))); message = QString::fromAscii(c,n); #ifdef _DEBUG // cerr << "Message from dctc: " + message + "\n"; #endif message.replace(QChar(0xd),"\n"); // deal with the message that we got back emit dctc_data(message); } // send the string command to the text client // the required newline is added by the function void dctc_connection::send_command(const QString &command) { QString cmd(command + "\n"); unsigned int ret = dctc_socket->writeBlock( cmd.ascii(), cmd.length() ); // everything ok if (ret == cmd.length()) return; // if(ret == -1) cerr << "Tried to write to invalid socket\n"; //else cerr << "All the data was not written\n"; } /* bool dctc_connection::sleep_on_client(const QString& socket) { struct stat st; for (int i = 0; i < MAX_WAIT; i++) { if (stat(socket.latin1(), &st) == 0) return true; if (waitpid(pid, NULL, WNOHANG) == -1) return false; usleep( (1000000/180)*20); } return false; } */ int dctc_connection::hubconnect(const QString &hubip, const QString &hubpwd, int new_pid,const QString& profile) { if (new_pid) return connect_to_running(hubip.latin1(), new_pid); // handle the starting of the dctc text client int ret = fork_client(hubip.latin1(),hubpwd,profile); if (ret != CONNECT_SUCCESS) { debugWin->print("Could not connect"); kill_client(); } return ret; } // disconnect from this hub and kill the associated dctc client void dctc_connection::hubdisconnect() { send_command("/FORCEQUIT"); delete read_notifier; delete dctc_socket; if(dctcproc) delete dctcproc; pid = 0; } void dctc_connection::data_to_send(const QString &cmd) { send_command(cmd); }