/* * Modification History * * 2003-October-28 Jason Rohrer * Created. * * 2003-November-5 Jason Rohrer * Fixed destruction/stopping behavior of update thread. * * 2004-February-20 Jason Rohrer * Added info about current connection attempt. * * 2004-April-2 Jason Rohrer * Fixed potential deadlock. * Removed use of wxMutexGuiEnter, which was causing freezes on win32. * * 2004-December-24 Jason Rohrer * Added a connection quality gauge. * * 2005-feb-20 Philippe Martin * new hosts can be added to testHostHold.ini, if this file exists * */ #include "ConnectionsPanel.h" #include "minorGems/util/stringUtils.h" #include "minorGems/util/SettingsManager.h" #include "minorGems/util/SimpleVector.h" #include "minorGems/util/StringBufferOutputStream.h" #include "DownloadPanel.h" // for non-precomp compilers, include the necessary headers #ifndef WX_PRECOMP #include "wx/app.h" #include "wx/thread.h" // #include "wx/frame.h" // #include "wx/panel.h" #include "wx/stattext.h" // #include "wx/menu.h" // #include "wx/layout.h" // #include "wx/dialog.h" // #include "wx/msgdlg.h" #include "wx/sizer.h" // #include "wx/textdlg.h" // #include "wx/progdlg.h" // #include "wx/utils.h" // #include "wx/choicdlg.h" #include "wx/button.h" #endif #include "MUTE/otherApps/fileSharing/fileShare.h" #include "MUTE/layers/messageRouting/messageRouter.h" #include "minorGems/network/p2pParts/HostCatcher.h" extern AutoPanel *mAutoPanel; // all from GuiApp.cpp extern int mRunningDownloads; extern int mRunningUploads; extern int mNumQueued; extern time_t appStartTime; extern HostCatcher *muteHostCatcher; // from HostCatcher.cpp // IDs for various controls enum { BUTTON_ADD_HOST = 1, FIELD_ADD_HOST_ADDRESS, FIELD_ADD_HOST_PORT, CONNECTIONS_UPDATE_EVENT }; // event mapping table BEGIN_EVENT_TABLE( ConnectionsPanel, wxPanel ) EVT_BUTTON( BUTTON_ADD_HOST, ConnectionsPanel::OnAddHost ) EVT_TEXT_ENTER( FIELD_ADD_HOST_ADDRESS, ConnectionsPanel::OnAddHost ) EVT_TEXT_ENTER( FIELD_ADD_HOST_PORT, ConnectionsPanel::OnAddHost ) // dummy events sent by thread to trigger an update EVT_MENU( CONNECTIONS_UPDATE_EVENT, ConnectionsPanel::OnConnectionsUpdate ) END_EVENT_TABLE(); ConnectionsPanel::ConnectionsPanel( wxNotebook *inNotebook, wxGauge *inConnectionQualityGauge, wxStaticText *inStatusLabel ) : wxPanel( inNotebook, -1 ), mConnectionQualityGauge( inConnectionQualityGauge ), mStatusLabel( inStatusLabel ), mAddHostAddressField( new wxTextCtrl( this, FIELD_ADD_HOST_ADDRESS, "", wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER ) ), mAddHostPortField( new wxTextCtrl( this, FIELD_ADD_HOST_PORT, "4900", wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER ) ), mConnectionList( new wxListCtrl( this, -1, wxDefaultPosition, wxDefaultSize, wxLC_REPORT | wxLC_VRULES ) ), mUpdateThread( NULL ) { wxBoxSizer *panelSizer = new wxBoxSizer( wxVERTICAL ); this->SetSizer( panelSizer ); wxBoxSizer *addFieldSizer = new wxBoxSizer( wxHORIZONTAL ); panelSizer->Add( addFieldSizer, 0, wxEXPAND | wxALL, 10 ); addFieldSizer->Add( new wxStaticText( this, -1, "Address:" ), 0, wxALIGN_CENTER, 0 ); addFieldSizer->Add( mAddHostAddressField, 1, wxALIGN_CENTER, 0 ); addFieldSizer->Add( new wxStaticText( this, -1, "Port:" ), 0, wxALIGN_CENTER, 0 ); addFieldSizer->Add( mAddHostPortField, 0, wxALIGN_CENTER, 0 ); wxButton *addHostButton = new wxButton( this, BUTTON_ADD_HOST, "Add Host" ); addFieldSizer->Add( addHostButton, 0, wxALIGN_CENTER, 0 ); mConnectionList->InsertColumn( 0, "Address" ); mConnectionList->InsertColumn( 1, "Port" ); mConnectionList->InsertColumn( 2, "Sent" ); mConnectionList->InsertColumn( 3, "Queued" ); mConnectionList->InsertColumn( 4, "Dropped" ); mConnectionList->InsertColumn( 5, "Status" ); mConnectionList->SetColumnWidth( 0, 150 ); mConnectionList->SetColumnWidth( 1, 75 ); mConnectionList->SetColumnWidth( 2, 75 ); mConnectionList->SetColumnWidth( 3, 75 ); mConnectionList->SetColumnWidth( 4, 75 ); mConnectionList->SetColumnWidth( 5, 150 ); panelSizer->Add( mConnectionList, 1, // make vertically stretchable wxEXPAND, // make horizontally stretchable 0 ); wxBoxSizer *statusSizer = new wxBoxSizer( wxHORIZONTAL ); panelSizer->Add( statusSizer, 0, wxEXPAND | wxALL, 0 ); mConnectionStatus = new wxStaticText( this, -1, "Startin Up..." ); statusSizer->Add( mConnectionStatus, 0, wxALIGN_CENTER, 0 ); // start our thread mUpdateThread = new ConnectionUpdateThread( (void *)this ); } ConnectionsPanel::~ConnectionsPanel() { delete mUpdateThread; } // save the HostCatcher list to the seedHosts.ini file void ConnectionsPanel::SaveConnections() { char **addresses; int *ports; int *sentCounts; int *queueCounts; int *dropCounts; unsigned long *startTimes; int hostCount = 0; StringBufferOutputStream *tempHostListStream = new StringBufferOutputStream(); // get current connected host list and write them to stream first // it seems to pick from the top of the list first but I didn't do // a lot of research to confirm that int numHosts = muteGetConnectedHostList( &addresses, &ports, &sentCounts, &queueCounts, &dropCounts, &startTimes ); for( int i=0; iwriteString( hostString ); delete [] addresses[i]; delete [] hostString; hostCount++; } delete [] addresses; delete [] ports; delete [] sentCounts; delete [] queueCounts; delete [] dropCounts; delete [] startTimes; if( muteHostCatcher != NULL ) { SimpleVector *hostList = muteHostCatcher->getHostList( 50, NULL ); int numHosts = hostList->size(); for( int i=0; igetElement( i ) ); char *hostString = autoSprintf( "%s %d\n", currentHost->mAddressString, currentHost->mPort ); tempHostListStream->writeString( hostString ); delete [] hostString; delete currentHost; hostCount++; } delete hostList; } char *hostListString = tempHostListStream->getString(); if( hostCount > 0) { printf( "Saving connections list for use at next startup\n" ); SettingsManager::setSetting( "seedHosts", hostListString ); } delete [] hostListString; delete tempHostListStream; } void ConnectionsPanel::OnConnectionsUpdate( wxCommandEvent& inEvent ) { mConnectionList->DeleteAllItems(); char **addresses; int *ports; int *sentCounts; int *queueCounts; int *dropCounts; unsigned long *startTimes; unsigned long uptime; char *statusUptimeString; int numHosts = muteGetConnectedHostList( &addresses, &ports, &sentCounts, &queueCounts, &dropCounts, &startTimes ); for( int i=0; i 86400) statusUptimeString = autoSprintf("%ud %uh", uptime / 86400, (uptime % 86400) / 3600); else if (uptime > 3600) statusUptimeString = autoSprintf("%uh %um", uptime / 3600, (uptime % 3600) / 60); else if (uptime > 60) statusUptimeString = autoSprintf("%um %us", uptime / 60, uptime % 60); else statusUptimeString = autoSprintf("%us", uptime); char *statusString = autoSprintf( "%s", statusUptimeString); // maybe add more later mConnectionList->InsertItem( i, addresses[i] ); mConnectionList->SetItem( i, 1, portString ); mConnectionList->SetItem( i, 2, sentString ); mConnectionList->SetItem( i, 3, queueString ); mConnectionList->SetItem( i, 4, dropString ); mConnectionList->SetItem( i, 5, statusString ); delete [] addresses[i]; delete [] portString; delete [] sentString; delete [] queueString; delete [] dropString; delete [] statusString; delete [] statusUptimeString; } delete [] addresses; delete [] ports; delete [] sentCounts; delete [] queueCounts; delete [] dropCounts; delete [] startTimes; char *currentAttemptAddress; int currentAttemptPort; char attempting = muteGetCurrentConnectionAttempt( ¤tAttemptAddress, ¤tAttemptPort ); if( attempting ) { char *statusString = autoSprintf( "Trying to connect to %s : %d", currentAttemptAddress, currentAttemptPort ); delete [] currentAttemptAddress; mConnectionStatus->SetLabel( statusString ); delete [] statusString; } else { mConnectionStatus->SetLabel( "" ); } mConnectionQualityGauge->SetRange( muteGetTargetNumberOfConnections() ); mConnectionQualityGauge->SetValue( muteGetConnectionCount() ); uptime = time(NULL) - appStartTime; if (uptime > 86400) statusUptimeString = autoSprintf("%ud %uh", uptime / 86400, (uptime % 86400) / 3600); else if (uptime > 3600) statusUptimeString = autoSprintf("%uh %um", uptime / 3600, (uptime % 3600) / 60); else if (uptime > 60) statusUptimeString = autoSprintf("%um %us", uptime / 60, uptime % 60); else statusUptimeString = autoSprintf("%us", uptime); char *statusString = autoSprintf( " DL %d UL %d Q %d T %s", mRunningDownloads, mRunningUploads, mNumQueued, statusUptimeString ); mStatusLabel->SetLabel( statusString ); delete [] statusString; delete [] statusUptimeString; } void ConnectionsPanel::OnAddHost( wxCommandEvent& inEvent ) { char *address = stringDuplicate( mAddHostAddressField->GetValue().c_str() ); char *portString = stringDuplicate( mAddHostPortField->GetValue().c_str() ); if( strcmp( address, "" ) != 0 ) { // port defaults to 4900 int port = 4900; int numRead = sscanf( portString, "%d", &port ); if( numRead == 1 ) { // clear address field mAddHostAddressField->SetValue( "" ); // leave port field } else { // replace bad port string with default // FIXME a default port is bad: easier traffic analysis mAddHostPortField->SetValue( "4900" ); } muteAddHost( address, port ); if (SettingsManager::getStringSetting( "testHoldHost" )&& strcmp( portString, "" ) ) { SimpleVector *hostVector = SettingsManager::getSetting( "testHoldHost" ); int numTokens = hostVector->size(); if( numTokens%2 ) printf( "OnAddHost: testHoldHost.ini settings file not properly formatted\n" ); else {hostVector->push_back(stringDuplicate( address )); hostVector->push_back(stringDuplicate( portString)); SettingsManager::setSetting( "testHoldHost",hostVector ); } numTokens = hostVector->size(); for( int i=0; igetElement( i ) ) ); } delete hostVector; }// testHoldHost } delete [] address; delete [] portString; } ConnectionUpdateThread::ConnectionUpdateThread( void *inParentPanel ) : mParentPanel( inParentPanel ), mStopLock( new MutexLock() ), mStopped( false ) { start(); } ConnectionUpdateThread::~ConnectionUpdateThread() { mStopLock->lock(); mStopped = true; mStopLock->unlock(); join(); delete mStopLock; } void ConnectionUpdateThread::run() { mStopLock->lock(); char isStopped = mStopped; mStopLock->unlock(); ConnectionsPanel *panel = (ConnectionsPanel *)mParentPanel; while( ! isStopped ) { // fire an update event // post event so that GUI thread can update UI wxCommandEvent event( wxEVT_COMMAND_MENU_SELECTED, CONNECTIONS_UPDATE_EVENT ); // send in a thread-safe way wxPostEvent( panel, event ); // sleep for 5 seconds in 1-second increments for( int i=0; i<5 && !isStopped; i++ ) { sleep( 1000 ); // check if stopped mStopLock->lock(); isStopped = mStopped; mStopLock->unlock(); } } }