/* Copyright 2003 Rikard Björklind, Mikael Gransell 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 #include #include #include #include #include #include #include #include #include #include #include #include "hubwdgt.h" #include "debugdlg.h" #include "searchdlg.h" #include "dctc_hub.h" #include "connect_dlg.h" #include "dc_hub.h" #include "dc_settings.h" #include "util.h" #include "transfer_widget.h" #include "chatwdgt.h" #include "chatedit.h" #include "status_monitor.h" #include "pixmaps.h" // #include "alphabutton.h" HubWdgt::HubWdgt( transfer_widget* transferWin, QWidget* parent, const char* name ) : QWidget( parent, name ) { this->transferWin = transferWin; QGridLayout* layout = new QGridLayout( this, 4, 1 ); use_split_view = true;; if (settings.get_setting("use_split_view")=="false") use_split_view = false; if (!use_split_view) { // Tab bar for the chat and user tabs tabs = new QTabWidget( this, "Tab bar" ); // Public chat publicChat = new ChatWdgt(false,this,"public chat"); // User list users = new UserList( tabs, "User list", (ChatEdit*)publicChat->chatEditPtr() ); tabs->addTab( publicChat, "Ch&at" ); tabs->addTab( users, "U&sers" ); // Add all widgets to our layout layout->addWidget( tabs, 0, 0 ); } else { chatusersplit = new QSplitter(this,"kuksplitter"); tabs = new QTabWidget(chatusersplit,"Tab bar"); publicChat = new ChatWdgt(false,this,"public chat"); users = new UserList(chatusersplit, "User List", (ChatEdit*)publicChat->chatEditPtr()); //users->setSorting(-1); // disable sorting for now tabs->addTab(publicChat,"Public Ch&at"); chatusersplit->setResizeMode(tabs,QSplitter::Stretch); chatusersplit->setResizeMode(users,QSplitter::Stretch); chatusersplit->setSizePolicy(QSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding)); // set splitter sizes intlist_setting sslist(settings.get_setting("splitset_hub")); chatusersplit->setSizes(sslist.intlist); // Add all widgets to our layout layout->addWidget(chatusersplit,0,0); } // restore column widths in user list intlist_setting wl_users(settings.get_setting("widthlist_hubwdgt_lv_users")); set_lv_widths(users, wl_users); // Add corner button to tab widget. // Doesn't work in Qt-3.1.x. Use #ifdef ??? Pixmap pixmaps; QPushButton *closeTabBtn = new QPushButton(pixmaps.getCloseTabIcon(), "", tabs, "CloseChatTabButton"); closeTabBtn->setFlat(true); QObject::connect( closeTabBtn, SIGNAL(clicked()), this, SLOT( closeChatTab() ) ); closeTabBtn->setFixedSize(20,20); closeTabBtn->setEnabled(false); tabs->setCornerWidget(closeTabBtn); QObject::connect(tabs,SIGNAL(currentChanged(QWidget *)), this, SLOT(chatTabChanged(QWidget*))); hubInterface = NULL; hubs = new dc_hub(); name = "New Hub"; searchDlg = 0; } void HubWdgt::save_layout() { intlist_setting wl_users; get_lv_widths(users, &wl_users); settings.set_setting("common","widthlist_hubwdgt_lv_users", wl_users.toString()); if (use_split_view) { QValueList svli = chatusersplit->sizes(); intlist_setting sslist(svli); settings.set_setting("common","splitset_hub", sslist.toString()); } settings.save_to_file(); } bool HubWdgt::connect(const QString& ip, const QString &pwd,const QString& profile) { int ret; hubInterface = new dctc_hub(0, "hub-" + ip); connectSignals(); ret = hubInterface->hubconnect(ip,pwd,profile); if (ret != CONNECT_SUCCESS) { if(ret==COULD_NOT_CREATE_SOCKET) QMessageBox::critical(this, "Connect", "Could not create socket"); else if(ret==COULD_NOT_CONNECT_TO_SOCKET) QMessageBox::critical(this, "Connect", "Could not connect to socket"); else QMessageBox::critical(this, "Connect", "Could not connect to hub"); delete hubInterface; hubInterface = 0; } return hubInterface!=0; } bool HubWdgt::connect() { // Bring up the connection dialog ConnectDlg *dlg = new ConnectDlg(hubs, this, "connect_dlg", true); bool running = false; if (dlg->exec() == QDialog::Accepted) { disconnect(); // Disconnect if already connected somewhere. hub_info hub = dlg->selected_hub(); if (hub.ip.isEmpty()) { // QString("") is not == QString::null !!! QMessageBox::warning(this, "Connect", "Connect to what? No hub was selected!"); return false; } hubInterface = new dctc_hub(0, "hub-" + hub.ip); connectSignals(); // if we connected to an already running hub, send the pid // to the connect function as well int ret; if (dlg->selected_running()) { ret = hubInterface->hubconnect(hub.ip, hub.pwd,hub.pid); running = true; } else ret = hubInterface->hubconnect(hub.ip,hub.pwd,dlg->selected_profile()); if (ret != CONNECT_SUCCESS) { if(ret==COULD_NOT_CREATE_SOCKET) QMessageBox::critical(this, "Connect", "Could not create socket"); else if(ret==COULD_NOT_CONNECT_TO_SOCKET) QMessageBox::critical(this, "Connect", "Could not connect to socket"); else QMessageBox::critical(this, "Connect", "Could not connect to hub"); delete hubInterface; hubInterface = 0; } } settings.save_favourites_file(); // To save the new favourites if any delete dlg; if(running) { if(!dctc_hub::hasMasterConnection()) hubInterface->connectToMaster(); hubInterface->get_ulist(); hubInterface->get_hub_name(); users->setUseTimer(true); emit connected(id); // must not forget... } return hubInterface!=0; } void HubWdgt::requestPassword() { bool ok; QString password = QInputDialog::getText("Password required","Enter password:",QLineEdit::Password, QString::null,&ok,this,"passworddialog"); if(ok && !password.isEmpty()) hubInterface->sendPassword( password ); } void HubWdgt::setHubName(const QString& aName) { name = aName; if (searchDlg) searchDlg->setCaption("Search@"+name); emit gotName(name,this); } void HubWdgt::chatTabChanged( QWidget *) { int min_tabs = 1; if (!use_split_view) min_tabs = 2; if (tabs->currentPageIndex()cornerWidget())->setEnabled(false); else ((QPushButton *)tabs->cornerWidget())->setEnabled(true); } /// called when the corner-button of the tab widget is clicked void HubWdgt::closeChatTab() { int min_tabs = 1; if (!use_split_view) min_tabs = 2; if (tabs->count()<=min_tabs) return; if (tabs->currentPageIndex()currentPage(); if (!pchat->is_private()) return; tabs->removePage(pchat); delete pchat; //->emitClose(); if (tabs->count()<=min_tabs) { ((QPushButton *)tabs->cornerWidget())->setEnabled(false); } } /// Called when somone pressed 'send message' on the user list popup menu void HubWdgt::activateChat(const QString& user) { bool found = false; // Try to find an already existing tab // fix for duplicate provchat: // previously, "i=2": for(int i=2; i < tabs->count(); i++) // now: "i=min_tabs" int min_tabs = 1; if (!use_split_view) min_tabs = 2; for(int i=min_tabs; i < tabs->count(); i++) { if( tabs->label(i) == user ) { tabs->setCurrentPage(i); found = true; } } if(!found) // Otherwise create a new tab { ChatWdgt *pchat = new ChatWdgt(true, this, "private chat"); tabs->addTab( pchat, user ); QObject::connect( pchat, SIGNAL(newChatStr(const QString&, QWidget*)), this, SLOT(sendChatMes(const QString&, QWidget*))); QObject::connect( pchat, SIGNAL(close(QWidget*)), tabs, SLOT(removePage(QWidget*))); tabs->setCurrentPage(tabs->count()-1); pchat->show(); /* if (tabs->count()>min_tabs) ((QPushButton *)tabs->cornerWidget())->setEnabled(true); */ } } /// Called when a private chat message is received from the hub interface. void HubWdgt::privChatMsg(const QString& user, const QString& msg) { // The message could be from a user on another hub. // The message is on the form " message" if it comes from another hub. // It could also miss this tag in case of a hub message, which makes it impossible to // know which hub it originated from (bad dctc). if( msg[0]=='<' ) { int i1 = msg.find('('); int i2 = msg.find(')'); if(i1!=-1 && i2!=-1) { QString hubName = msg.mid(i1+1,i2-i1-1); debugWin->print("hubname="+hubName+"\n"); if(hubName != name) return; } } int min_tabs = 1; if (!use_split_view) min_tabs = 2; bool found = false; // Try to find the user among the tabs for(int i=min_tabs; i < tabs->count(); i++) { if(tabs->label(i) == user) { ChatWdgt *pchat = (ChatWdgt *)tabs->page(i); pchat->addChatStr(msg); found = true; break; } } if(!found) // The user was not found, add a new tab { ChatWdgt *pchat = new ChatWdgt(true, this, "private chat"); tabs->addTab( pchat, user ); pchat->addChatStr(msg); QObject::connect( pchat, SIGNAL(newChatStr(const QString&, QWidget*)), this, SLOT(sendChatMes(const QString&, QWidget*))); QObject::connect( pchat, SIGNAL(close(QWidget*)), tabs, SLOT(removePage(QWidget*))); /* if (tabs->count()>min_tabs) ((QPushButton *)tabs->cornerWidget())->setEnabled(true); */ } } /// Called when a ChatWdgt has a new chatstring to send void HubWdgt::sendChatMes(const QString& msg,QWidget* originator) { // If the originator is the public chat just send it if( originator == tabs->page(0) ) hubInterface->send_chatmsg(msg); // Otherwise it is a private chat msg, so find the tab and the user else { int min_tabs = 1; if (!use_split_view) min_tabs = 2; for(int i=min_tabs;i < tabs->count();i++) if( tabs->page(i) == originator ) { hubInterface->send_privmsg( msg, tabs->label(i)); break; } } } void HubWdgt::disconnect() { users->clear(); ((ChatEdit*)publicChat->chatEditPtr())->clearCompletions(); debugWin->print("Disconnecting...\n"); // estr -- killing all searchdialog crap if ( searchDlg ) { QObject::disconnect(searchDlg, 0,0,0); delete searchDlg; searchDlg = 0; } if( hubInterface ) { hubInterface->hubdisconnect(); // First disconnect all signals emitted by the hubInterface QObject::disconnect(hubInterface,0,0,0); // Then disconnect all signals hubwdgt->hubinterface QObject::disconnect(this,0,hubInterface,0); // Other signals connected to the hubInterface QObject::disconnect(transferWin,0,hubInterface,0); QObject::disconnect(publicChat,0,hubInterface,0); QObject::disconnect(users,0,hubInterface,0); delete hubInterface; hubInterface = 0; publicChat->addChatStr(" Disconnected."); emit disconnected(id); } } void HubWdgt::disconnect(int reason) { if( reason == dctc_hub::DISCONNECT_DIED ) { debugWin->print("Connection died unexpectedly, killing everything."); disconnect(); } else if( reason == dctc_hub::DISCONNECT_HUB ) { users->clear(); ((ChatEdit*)publicChat->chatEditPtr())->clearCompletions(); debugWin->print("Hub disconnected, keeping connection to DCTC"); publicChat->addChatStr(" Hub disconnected, trying again in 30 seconds..."); emit disconnected(id); } } void HubWdgt::openSearchDlg() { if( hubInterface ) { if( searchDlg ) { searchDlg->show(); searchDlg->setActiveWindow(); searchDlg->raise(); } else { // Create new search dialog //searchDlg = new SearchDlg( this, "Search dialog" ); searchDlg = new SearchDlg(); searchDlg->setCaption("Search@"+name); searchDlg->show(); QObject::connect( searchDlg, SIGNAL(search(const QString&, bool)), hubInterface, SLOT(search_for( const QString&, bool)) ); QObject::connect( hubInterface, SIGNAL(search_file(const QString&, const QString&, const QString &, const QString &,const QString&)), this, SLOT(handleSearchResult(const QString&,const QString&, const QString&, const QString &, const QString &))); QObject::connect( searchDlg, SIGNAL( downloadFile(const QString&, const QString&, const QString&,unsigned long int)), transferWin, SLOT( download_file(const QString&, const QString&, const QString&,unsigned long int))); /* QObject::connect( searchDlg, SIGNAL( downloadFiles(list)), transferWin, SLOT( downloadFiles(list))); */ QObject::connect( searchDlg, SIGNAL( getUserFiles( const QString& )), hubInterface, SLOT(get_user_list( const QString& ))); } } } void HubWdgt::handleSearchResult(const QString &user, const QString &file, const QString &size, const QString &slot, const QString &hubname) { // rikard, fixa! =) ska inte ignorera result om det inte finns andra hubbar // ok, fixat nu med lite klister och gummisnoddar =) --estr if (statMon->num_open_hubs()==1 && name.findRev(hubname, -1, false)!=0) { setHubName(hubname); } if(name.findRev(hubname, -1, false)==0) // This result is for us { //openSearchDlg(); if(searchDlg) searchDlg->addSearchResult(user,file,size,slot); } else { // If the last search was a multihub one, inform the others if(searchDlg->isMultiSearch()) { emit multiSearchResult(user,file,size,slot,hubname); } } } void HubWdgt::removeFileDlg( const QString& user ) { list::iterator it; for(it = dialogs.begin(); it != dialogs.end(); it++) { UserFileDlg* tmp = *it; if( tmp->getUserName() == user ) { dialogs.remove( tmp ); break; } } } #define LS_CACHE_MAGIC 0x4C536430 struct LsCacheHeader { unsigned int ls_magic; unsigned int packed_size; unsigned int unpacked_size; unsigned int pack_algorithm; time_t retrieve_time; unsigned long long share_size; }; #define LS_CACHE_PACK_NONE 0 #define LS_CACHE_PACK_BZIP 1 void HubWdgt::loadUserList( const QString& user ) { QString filename = QString(getenv("HOME")) + "/.dctc/ls_cache/" + user; char *ret = NULL; FILE *f = fopen(filename,"rb"); if(f!=NULL) { LsCacheHeader header; if(fread(&header,1,sizeof(LsCacheHeader),f) != sizeof(LsCacheHeader)) { debugWin->print("Error reading ls_cache header.\n"); fclose(f); return; } if( header.ls_magic != LS_CACHE_MAGIC ) { debugWin->print("Invalid ls_cache header.\n"); fclose(f); return; } switch(header.pack_algorithm) { case LS_CACHE_PACK_NONE: /* result no compressed */ ret = (char *)malloc(header.unpacked_size+1); if(ret==NULL) { fclose(f); return; } if(fread( ret,1,header.unpacked_size,f) != header.unpacked_size ) { debugWin->print("Trucated ls_cache file detected.\n"); fclose(f); free(ret); return; } fclose(f); ret[header.unpacked_size]='\0'; break; case LS_CACHE_PACK_BZIP: { char *pack_area; unsigned long upack; pack_area=(char*)malloc(header.packed_size); if( pack_area==NULL ) { fclose(f); return; } if( fread(pack_area,1,header.packed_size,f) != header.packed_size ) { debugWin->print("Trucated ls_cache file detected.\n"); fclose(f); free(pack_area); return; } fclose(f); ret=(char*)malloc(header.unpacked_size+1); if( ret==NULL ) { free(pack_area); return; } upack = header.unpacked_size; if( uncompress((Bytef*)ret,&upack,(Bytef*)pack_area,header.packed_size) != Z_OK ) { debugWin->print("Failed to uncompress ls_cache file.\n"); free(pack_area); free(ret); return; } free(pack_area); if( upack != header.unpacked_size ) { debugWin->print("Invalid decompressed result.\n"); free(ret); return; } ret[header.unpacked_size]='\0'; } break; default: debugWin->print("loadUserList: unknown compression algorithm.\n"); fclose(f); return; } } else { debugWin->print("Unable to open ls_cache file: " + filename + "\n"); } UserFileDlg* temp = new UserFileDlg( user, /*this*/0, "user file dlg" ); temp->show(); dialogs.push_back( temp ); QObject::connect( temp, SIGNAL(killed( const QString& )), this, SLOT(removeFileDlg( const QString& ))); // estrato replacing this: // QObject::connect( temp, SIGNAL( downloadFile(const QString&, const QString&,const QString&, int)), // hubInterface, SLOT(get_file(const QString&, const QString&,const QString&, int))); // // with this: QObject::connect( temp, SIGNAL( downloadFile(const QString&, const QString&, const QString&,unsigned long int)), transferWin, SLOT( download_file(const QString&, const QString&, const QString&,unsigned long int))); QStringList strLst = QStringList::split(QChar('\n'), QString(ret)); for ( QStringList::Iterator it = strLst.begin(); it != strLst.end(); ++it ) { temp->addFile( (*it).section('|',0,0), (*it).section('|',1,1) ); } } /// This slot receives the signal unattachedGDL from the hub interface and asks the user what to do. void HubWdgt::queryUnattached(const QString& name) { QDir dir(settings.get_setting("dl_dir") + "GDL/" + name,QString::null,QDir::Name|QDir::IgnoreCase, QDir::Files|QDir::Hidden); QStringList files = dir.entryList(); switch( QMessageBox::information(this, "File exists", "The following file is partly downloaded:\n"+name+"What do you want to do?", "&Resume", "Re&start", "&Cancel",0,2) ) { case 0: // Resume hubInterface->attachFile(name); break; case 1: // Restart for(QStringList::iterator i=files.begin();i!=files.end();i++) { if(*i=="." || *i=="..") continue; debugWin->print("Removing file: " + dir.absPath()+"/"+*i+"\n"); if(!QFile::remove (dir.absPath()+"/"+*i)) { QMessageBox::critical(this,"dc-qt: Error", "Error removing file:\n" + *i); break; } } if(!dir.rmdir(dir.absPath())) { QMessageBox::critical(this,"dc-qt: Error", "Error removing directory:\n" + dir.absPath()); break; } // hubInterface->restart(); // estrato TODO: transferWin->restart( name ); break; case 2: // Cancel, do nothing. break; default: ; } } void HubWdgt::userListArrived(const QStringList& l) { // Fill the userlist. users->addUsers(l); // Request the infos. hubInterface->getUserInfos(); } void HubWdgt::connectSignals() { // File transfers // Uploads QObject::connect( hubInterface, SIGNAL(uploadAdded(const QString&)), transferWin, SLOT(new_ul_info(const QString&))); QObject::connect( hubInterface, SIGNAL(uploadUpdated(const QString&)), transferWin, SLOT(update_ul_info(const QString&))); QObject::connect( hubInterface, SIGNAL(uploadClosed(const QString&, const QString&)), transferWin, SLOT(closed_ul_info(const QString&, const QString&))); // GDLs QObject::connect( transferWin, SIGNAL(request_gdl_update()), hubInterface, SLOT(request_dlist())); QObject::connect( hubInterface, SIGNAL(gdlUpdated(const QString&)), transferWin, SLOT(update_gdl_info(const QString&))); QObject::connect( hubInterface, SIGNAL(gdlListEnd()), transferWin, SLOT(update_gdl_end())); QObject::connect( transferWin, SIGNAL(start_gdl(int, const QString &, const QString &, const QString &, unsigned long int)), hubInterface, SLOT(get_file(int, const QString &, const QString &, const QString &, unsigned long int))); QObject::connect( transferWin, SIGNAL(add_gdl_source(int, const QString &, const QString &, unsigned long int)), hubInterface, SLOT(addSource(int, const QString &, const QString &, unsigned long int))); QObject::connect( transferWin, SIGNAL(attach_gdl(const QString &)), hubInterface,SLOT(attachFile(const QString &))); QObject::connect( transferWin, SIGNAL(detach_gdl(const unsigned int)), hubInterface,SLOT(detachId(const unsigned int))); QObject::connect( transferWin, SIGNAL(close_gdl(unsigned int)), hubInterface,SLOT(cancel_transfer(unsigned int))); QObject::connect(transferWin, SIGNAL(close_dl(unsigned int,const QString&,const QString&)), hubInterface,SLOT(delete_gdl(unsigned int,const QString&,const QString&))); // User file list QObject::connect( transferWin, SIGNAL(getUserFileList(const QString &)), hubInterface, SLOT(get_user_list(const QString &))); QObject::connect(hubInterface, SIGNAL(unattachedGDL(const QString&)), this, SLOT(queryUnattached(const QString&))); // Users QObject::connect( hubInterface, SIGNAL(user_info(const QString&, const QString&, const QString&, const QString&, const QString&,bool)), users,SLOT(addUser(const QString&, const QString&, const QString&, const QString&, const QString&,bool))); QObject::connect( hubInterface, SIGNAL(user_added(const QString&, bool)), users,SLOT(addUser(const QString&, bool))); QObject::connect( hubInterface, SIGNAL(user_removed(const QString&)), users, SLOT(removeUser(const QString&))); QObject::connect( users, SIGNAL(getUserFiles( const QString& )), hubInterface, SLOT(get_user_list( const QString& ))); QObject::connect( users, SIGNAL(getUserInfo(const QString&)), hubInterface, SLOT(get_uinfo(const QString&))); QObject::connect( hubInterface, SIGNAL(userListArrived(const QStringList& )),SLOT(userListArrived(const QStringList&))); QObject::connect( hubInterface, SIGNAL(userIsOp(const QString& )),users,SLOT(setOp(const QString&))); // Public chat stuff QObject::connect( publicChat, SIGNAL(newChatStr(const QString&,QWidget*)), this, SLOT(sendChatMes(const QString&,QWidget*))); QObject::connect( hubInterface, SIGNAL(chat_msg(const QString&)), publicChat, SLOT( addChatStr(const QString&) )); // Private chat stuff QObject::connect( hubInterface, SIGNAL(privchat_msg(const QString&, const QString&)), this, SLOT(privChatMsg(const QString&, const QString&)) ); QObject::connect( users, SIGNAL(activateChat(const QString&)), this, SLOT( activateChat(const QString&))); // Hubname QObject::connect( hubInterface, SIGNAL( hub_name(const QString&)), this, SLOT(setHubName(const QString&))); // User file list QObject::connect( hubInterface, SIGNAL(userFileList(const QString&)), this, SLOT(loadUserList(const QString&))); // Password QObject::connect(hubInterface, SIGNAL(passwordRequired()), this, SLOT(requestPassword())); // Connection stuff QObject::connect(hubInterface, SIGNAL(hubDisconnect(int)), this,SLOT(disconnect(int))); QObject::connect(hubInterface,SIGNAL(progressBar(const QString& ,const QString& ,const QStringList&)), this,SLOT(progressBar(const QString& ,const QString& ,const QStringList&))); QObject::connect(hubInterface,SIGNAL(hubReached()),this,SLOT(hubReached())); } void HubWdgt::hubReached() { if(!dctc_hub::hasMasterConnection()) hubInterface->connectToMaster(); if(!dctc_hub::hasMasterConnection()) QMessageBox::warning(this,"Master connection failure","dc-qt was unable to connect to the dctc master process.\nTherefore, file transfers will not work.\nThe only solution right now is to shut down and kill all dctc processes, then rm -rf ~/.dctc."); emit connected(id); } void HubWdgt::progressBar(const QString& name,const QString& progress,const QStringList& msglst) { // TODO: support more progressbars. Not implemented since DCTC doesen't seem to use this yet. if(name=="init_share") { emit showProgress(msglst.join(":"),progress); } } void HubWdgt::reshare() { if(hubInterface) { publicChat->addChatStr(" Resharing... I have no idea when I'm finished so don't press the button again :)"); hubInterface->reshare(); } } void HubWdgt::sendDCTCCommand( QString &cmd ) { if(hubInterface) hubInterface->send_command(cmd); else debugWin->print("No hubInterface!"); }