Upstream tarball 10101
[amule.git] / src / amule.cpp
blob289b5c858e37c14f9d241e84bfa148e6b0823154
1 //
2 // This file is part of the aMule Project.
3 //
4 // Copyright (c) 2003-2008 aMule Team ( admin@amule.org / http://www.amule.org )
5 // Copyright (c) 2002-2008 Merkur ( devs@emule-project.net / http://www.emule-project.net )
6 //
7 // Any parts of this program derived from the xMule, lMule or eMule project,
8 // or contributed by third-party developers are copyrighted by their
9 // respective authors.
11 // This program is free software; you can redistribute it and/or modify
12 // it under the terms of the GNU General Public License as published by
13 // the Free Software Foundation; either version 2 of the License, or
14 // (at your option) any later version.
16 // This program is distributed in the hope that it will be useful,
17 // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 // GNU General Public License for more details.
20 //
21 // You should have received a copy of the GNU General Public License
22 // along with this program; if not, write to the Free Software
23 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
27 #include "amule.h" // Interface declarations.
29 #include <csignal>
30 #include <cstring>
31 #include <wx/process.h>
32 #include <wx/sstream.h>
34 #ifdef HAVE_CONFIG_H
35 #include "config.h" // Needed for HAVE_GETRLIMIT, HAVE_SETRLIMIT,
36 // HAVE_SYS_RESOURCE_H, HAVE_SYS_STATVFS_H, VERSION
37 // and ENABLE_NLS
38 #endif
40 #include <common/ClientVersion.h>
42 #include <wx/cmdline.h> // Needed for wxCmdLineParser
43 #include <wx/config.h> // Do_not_auto_remove (win32)
44 #include <wx/fileconf.h>
45 #include <wx/tokenzr.h>
46 #include <wx/wfstream.h>
49 #include <common/Format.h> // Needed for CFormat
50 #include "kademlia/kademlia/Kademlia.h"
51 #include "kademlia/kademlia/Prefs.h"
52 #include "kademlia/kademlia/UDPFirewallTester.h"
53 #include "CanceledFileList.h"
54 #include "ClientCreditsList.h" // Needed for CClientCreditsList
55 #include "ClientList.h" // Needed for CClientList
56 #include "ClientUDPSocket.h" // Needed for CClientUDPSocket & CMuleUDPSocket
57 #include "ExternalConn.h" // Needed for ExternalConn & MuleConnection
58 #include <common/FileFunctions.h> // Needed for CDirIterator
59 #include "FriendList.h" // Needed for CFriendList
60 #include "HTTPDownload.h" // Needed for CHTTPDownloadThread
61 #include "InternalEvents.h" // Needed for CMuleInternalEvent
62 #include "IPFilter.h" // Needed for CIPFilter
63 #include "KnownFileList.h" // Needed for CKnownFileList
64 #include "ListenSocket.h" // Needed for CListenSocket
65 #include "Logger.h" // Needed for CLogger // Do_not_auto_remove
66 #include "MagnetURI.h" // Needed for CMagnetURI
67 #include "OtherFunctions.h"
68 #include "PartFile.h" // Needed for CPartFile
69 #include "Preferences.h" // Needed for CPreferences
70 #include "SearchList.h" // Needed for CSearchList
71 #include "Server.h" // Needed for GetListName
72 #include "ServerList.h" // Needed for CServerList
73 #include "ServerConnect.h" // Needed for CServerConnect
74 #include "ServerUDPSocket.h" // Needed for CServerUDPSocket
75 #include "Statistics.h" // Needed for CStatistics
76 #include "TerminationProcessAmuleweb.h" // Needed for CTerminationProcessAmuleweb
77 #include "ThreadTasks.h"
78 #include "updownclient.h" // Needed for CUpDownClient
79 #include "UploadQueue.h" // Needed for CUploadQueue
80 #include "UploadBandwidthThrottler.h"
81 #include "UserEvents.h"
82 #include "ScopedPtr.h"
84 #ifdef ENABLE_UPNP
85 #include "UPnPBase.h" // Needed for UPnP
86 #endif
88 #ifdef __WXMAC__
89 #include <wx/sysopt.h> // Do_not_auto_remove
90 #endif
92 #ifndef AMULE_DAEMON
93 #ifdef __WXMAC__
94 #include <CoreFoundation/CFBundle.h> // Do_not_auto_remove
95 #if wxCHECK_VERSION(2, 9, 0)
96 #include <wx/osx/core/cfstring.h> // Do_not_auto_remove
97 #else
98 #include <wx/mac/corefoundation/cfstring.h> // Do_not_auto_remove
99 #endif
100 #endif
101 #include <wx/msgdlg.h>
103 #include "amuleDlg.h"
104 #endif
107 #ifdef HAVE_SYS_RESOURCE_H
108 #include <sys/resource.h>
109 #endif
111 #ifdef HAVE_SYS_STATVFS_H
112 #include <sys/statvfs.h> // Do_not_auto_remove
113 #endif
116 #ifdef __GLIBC__
117 # define RLIMIT_RESOURCE __rlimit_resource
118 #else
119 # define RLIMIT_RESOURCE int
120 #endif
122 #ifdef AMULE_DAEMON
123 CamuleDaemonApp *theApp;
124 #else
125 CamuleGuiApp *theApp;
126 #endif
128 static void UnlimitResource(RLIMIT_RESOURCE resType)
130 #if defined(HAVE_GETRLIMIT) && defined(HAVE_SETRLIMIT)
131 struct rlimit rl;
132 getrlimit(resType, &rl);
133 rl.rlim_cur = rl.rlim_max;
134 setrlimit(resType, &rl);
135 #endif
139 static void SetResourceLimits()
141 #ifdef HAVE_SYS_RESOURCE_H
142 UnlimitResource(RLIMIT_DATA);
143 #ifndef __UCLIBC__
144 UnlimitResource(RLIMIT_FSIZE);
145 #endif
146 UnlimitResource(RLIMIT_NOFILE);
147 #ifdef RLIMIT_RSS
148 UnlimitResource(RLIMIT_RSS);
149 #endif
150 #endif
153 // We store the received signal in order to avoid race-conditions
154 // in the signal handler.
155 bool g_shutdownSignal = false;
157 void OnShutdownSignal( int /* sig */ )
159 signal(SIGINT, SIG_DFL);
160 signal(SIGTERM, SIG_DFL);
162 g_shutdownSignal = true;
164 #ifdef AMULE_DAEMON
165 theApp->ExitMainLoop();
166 #endif
170 CamuleApp::CamuleApp()
172 // Madcat - Initialize timer as the VERY FIRST thing to avoid any issues later.
173 // Kry - I love to init the vars on init, even before timer.
174 StartTickTimer();
176 // Initialization
177 m_app_state = APP_STATE_STARTING;
179 theApp = &wxGetApp();
181 clientlist = NULL;
182 searchlist = NULL;
183 knownfiles = NULL;
184 canceledfiles = NULL;
185 serverlist = NULL;
186 serverconnect = NULL;
187 sharedfiles = NULL;
188 listensocket = NULL;
189 clientudp = NULL;
190 clientcredits = NULL;
191 friendlist = NULL;
192 downloadqueue = NULL;
193 uploadqueue = NULL;
194 ipfilter = NULL;
195 ECServerHandler = NULL;
196 glob_prefs = NULL;
197 m_statistics = NULL;
198 uploadBandwidthThrottler = NULL;
199 #ifdef ENABLE_UPNP
200 m_upnp = NULL;
201 m_upnpMappings.resize(4);
202 #endif
203 core_timer = NULL;
205 m_localip = 0;
206 m_dwPublicIP = 0;
207 webserver_pid = 0;
209 enable_daemon_fork = false;
211 strFullMuleVersion = NULL;
212 strOSDescription = NULL;
214 // Apprently needed for *BSD
215 SetResourceLimits();
218 CamuleApp::~CamuleApp()
220 // Closing the log-file as the very last thing, since
221 // wxWidgets log-events are saved in it as well.
222 theLogger.CloseLogfile();
224 free(strFullMuleVersion);
225 free(strOSDescription);
228 int CamuleApp::OnExit()
230 if (m_app_state!=APP_STATE_STARTING) {
231 AddLogLineMS(false, _("Now, exiting main app..."));
234 // From wxWidgets docs, wxConfigBase:
235 // ...
236 // Note that you must delete this object (usually in wxApp::OnExit)
237 // in order to avoid memory leaks, wxWidgets won't do it automatically.
239 // As it happens, you may even further simplify the procedure described
240 // above: you may forget about calling Set(). When Get() is called and
241 // there is no current object, it will create one using Create() function.
242 // To disable this behaviour DontCreateOnDemand() is provided.
243 delete wxConfigBase::Set((wxConfigBase *)NULL);
245 // Save credits
246 clientcredits->SaveList();
248 // Kill amuleweb if running
249 if (webserver_pid) {
250 AddLogLineNS(CFormat(_("Terminating amuleweb instance with pid `%ld' ... ")) % webserver_pid);
251 wxKillError rc;
252 if (wxKill(webserver_pid, wxSIGTERM, &rc) == -1) {
253 AddLogLineNS(CFormat(_("Killing amuleweb instance with pid `%ld' ... ")) % webserver_pid);
254 if (wxKill(webserver_pid, wxSIGKILL, &rc) == -1) {
255 AddLogLineNS(_("Failed"));
260 if (m_app_state!=APP_STATE_STARTING) {
261 AddLogLineMS(false, _("aMule OnExit: Terminating core."));
264 delete serverlist;
265 serverlist = NULL;
267 delete searchlist;
268 searchlist = NULL;
270 delete clientcredits;
271 clientcredits = NULL;
273 delete friendlist;
274 friendlist = NULL;
276 // Destroying CDownloadQueue calls destructor for CPartFile
277 // calling CSharedFileList::SafeAddKFile occasionally.
278 delete sharedfiles;
279 sharedfiles = NULL;
281 delete serverconnect;
282 serverconnect = NULL;
284 delete listensocket;
285 listensocket = NULL;
287 delete clientudp;
288 clientudp = NULL;
290 delete knownfiles;
291 knownfiles = NULL;
293 delete canceledfiles;
294 canceledfiles = NULL;
296 delete clientlist;
297 clientlist = NULL;
299 delete uploadqueue;
300 uploadqueue = NULL;
302 delete downloadqueue;
303 downloadqueue = NULL;
305 delete ipfilter;
306 ipfilter = NULL;
308 #ifdef ENABLE_UPNP
309 delete m_upnp;
310 m_upnp = NULL;
311 #endif
313 delete ECServerHandler;
314 ECServerHandler = NULL;
316 delete m_statistics;
317 m_statistics = NULL;
319 delete glob_prefs;
320 glob_prefs = NULL;
321 CPreferences::EraseItemList();
323 delete uploadBandwidthThrottler;
324 uploadBandwidthThrottler = NULL;
326 if (m_app_state!=APP_STATE_STARTING) {
327 AddLogLineNS(_("aMule shutdown completed."));
330 #if wxUSE_MEMORY_TRACING
331 AddLogLineNS(_("Memory debug results for aMule exit:"));
332 // Log mem debug mesages to wxLogStderr
333 wxLog* oldLog = wxLog::SetActiveTarget(new wxLogStderr);
334 //AddLogLineNS(wxT("**************Classes**************");
335 //wxDebugContext::PrintClasses();
336 //AddLogLineNS(wxT("***************Dump***************");
337 //wxDebugContext::Dump();
338 AddLogLineNS(wxT("***************Stats**************"));
339 wxDebugContext::PrintStatistics(true);
341 // Set back to wxLogGui
342 delete wxLog::SetActiveTarget(oldLog);
343 #endif
345 StopTickTimer();
347 // Return 0 for succesful program termination
348 return AMULE_APP_BASE::OnExit();
352 int CamuleApp::InitGui(bool, wxString &)
354 return 0;
359 // Application initialization
361 bool CamuleApp::OnInit()
363 #if wxUSE_MEMORY_TRACING
364 // any text before call of Localize_mule needs not to be translated.
365 AddLogLineMS(false, wxT("Checkpoint set on app init for memory debug"));
366 wxDebugContext::SetCheckpoint();
367 #endif
369 // Forward wxLog events to CLogger
370 wxLog::SetActiveTarget(new CLoggerTarget);
372 m_localip = StringHosttoUint32(::wxGetFullHostName());
374 #ifndef __WXMSW__
375 // get rid of sigpipe
376 signal(SIGPIPE, SIG_IGN);
377 #else
378 // Handle CTRL-Break
379 signal(SIGBREAK, OnShutdownSignal);
380 #endif
381 // Handle sigint and sigterm
382 signal(SIGINT, OnShutdownSignal);
383 signal(SIGTERM, OnShutdownSignal);
385 #ifdef __WXMAC__
386 // For listctrl's to behave on Mac
387 wxSystemOptions::SetOption(wxT("mac.listctrl.always_use_generic"), 1);
388 #endif
390 // Handle uncaught exceptions
391 InstallMuleExceptionHandler();
393 if (!InitCommon(AMULE_APP_BASE::argc, AMULE_APP_BASE::argv)) {
394 return false;
397 glob_prefs = new CPreferences();
399 CPath outDir;
400 if (CheckMuleDirectory(wxT("temp"), thePrefs::GetTempDir(), ConfigDir + wxT("Temp"), outDir)) {
401 thePrefs::SetTempDir(outDir);
402 } else {
403 return false;
406 if (CheckMuleDirectory(wxT("incoming"), thePrefs::GetIncomingDir(), ConfigDir + wxT("Incoming"), outDir)) {
407 thePrefs::SetIncomingDir(outDir);
408 } else {
409 return false;
412 // Some sanity check
413 if (!thePrefs::UseTrayIcon()) {
414 thePrefs::SetMinToTray(false);
417 // Build the filenames for the two OS files
418 SetOSFiles(thePrefs::GetOSDir().GetRaw());
420 #ifdef ENABLE_NLS
421 // Load localization settings
422 Localize_mule();
423 #endif
425 // Configure EC for amuled when invoked with ec-config
426 if (ec_config) {
427 AddLogLineMS(false, _("\nEC configuration"));
428 thePrefs::SetECPass(GetPassword());
429 thePrefs::EnableExternalConnections(true);
430 AddLogLineMS(false, _("Password set and external connections enabled."));
433 #ifndef __WXMSW__
434 if (getuid() == 0) {
435 wxString msg =
436 wxT("Warning! You are running aMule as root.\n")
437 wxT("Doing so is not recommended for security reasons,\n")
438 wxT("and you are advised to run aMule as an normal\n")
439 wxT("user instead.");
441 ShowAlert(msg, _("WARNING"), wxCENTRE | wxOK | wxICON_ERROR);
443 fprintf(stderr, "\n--------------------------------------------------\n");
444 fprintf(stderr, "%s", (const char*)unicode2UTF8(msg));
445 fprintf(stderr, "\n--------------------------------------------------\n\n");
447 #endif
449 // Display notification on new version or first run
450 wxTextFile vfile( ConfigDir + wxT("lastversion") );
451 wxString newMule(wxT( VERSION ));
453 if ( !wxFileExists( vfile.GetName() ) ) {
454 vfile.Create();
457 if ( vfile.Open() ) {
458 // Check if this version has been run before
459 bool found = false;
460 for ( size_t i = 0; i < vfile.GetLineCount(); i++ ) {
461 // Check if this version has been run before
462 if ( vfile.GetLine(i) == newMule ) {
463 found = true;
464 break;
468 // We havent run this version before?
469 if ( !found ) {
470 // Insert new at top to provide faster searches
471 vfile.InsertLine( newMule, 0 );
473 Trigger_New_version( newMule );
476 // Keep at most 10 entires
477 while ( vfile.GetLineCount() > 10 )
478 vfile.RemoveLine( vfile.GetLineCount() - 1 );
480 vfile.Write();
481 vfile.Close();
484 // Check if we have the old style locale config
485 wxString langId = thePrefs::GetLanguageID();
486 if (!langId.IsEmpty() && (langId.GetChar(0) >= '0' && langId.GetChar(0) <= '9')) {
487 wxString info(_("Your locale has been changed to System Default due to a configuration change. Sorry."));
488 thePrefs::SetLanguageID(wxLang2Str(wxLANGUAGE_DEFAULT));
489 ShowAlert(info, _("Info"), wxCENTRE | wxOK | wxICON_ERROR);
492 m_statistics = new CStatistics();
494 clientlist = new CClientList();
495 friendlist = new CFriendList();
496 searchlist = new CSearchList();
497 knownfiles = new CKnownFileList();
498 canceledfiles = new CCanceledFileList;
499 serverlist = new CServerList();
501 sharedfiles = new CSharedFileList(knownfiles);
502 clientcredits = new CClientCreditsList();
504 // bugfix - do this before creating the uploadqueue
505 downloadqueue = new CDownloadQueue();
506 uploadqueue = new CUploadQueue();
507 ipfilter = new CIPFilter();
509 // Creates all needed listening sockets
510 wxString msg;
511 if (!ReinitializeNetwork(&msg)) {
512 AddLogLineMS(false, wxT("\n"));
513 AddLogLineMS(false, msg);
516 // Test if there's any new version
517 if (thePrefs::GetCheckNewVersion()) {
518 // We use the thread base because I don't want a dialog to pop up.
519 CHTTPDownloadThread* version_check =
520 new CHTTPDownloadThread(wxT("http://amule.sourceforge.net/lastversion"),
521 theApp->ConfigDir + wxT("last_version_check"), theApp->ConfigDir + wxT("last_version"), HTTP_VersionCheck, false);
522 version_check->Create();
523 version_check->Run();
526 // Create main dialog, or fork to background (daemon).
527 InitGui(m_geometryEnabled, m_geometryString);
529 #ifdef AMULE_DAEMON
530 // Need to refresh wxSingleInstanceChecker after the daemon fork() !
531 if (enable_daemon_fork) {
532 RefreshSingleInstanceChecker();
533 // No need to check IsAnotherRunning() - we've done it before.
535 #endif
537 // Has to be created after the call to InitGui, as fork
538 // (when using posix threads) only replicates the mainthread,
539 // and the UBT constructor creates a thread.
540 uploadBandwidthThrottler = new UploadBandwidthThrottler();
542 // Start performing background tasks
543 // This will start loading the IP filter. It will start right away.
544 // Log is confusing, because log entries from background will only be printed
545 // once foreground becomes idle, and that will only be after loading
546 // of the partfiles has finished.
547 CThreadScheduler::Start();
549 // These must be initialized after the gui is loaded.
550 serverlist->Init();
551 downloadqueue->LoadMetFiles(thePrefs::GetTempDir());
552 sharedfiles->Reload();
554 // Ensure that the up/down ratio is used
555 CPreferences::CheckUlDlRatio();
557 // The user can start pressing buttons like mad if he feels like it.
558 m_app_state = APP_STATE_RUNNING;
560 // Kry - Load the sources seeds on app init
561 if (thePrefs::GetSrcSeedsOn()) {
562 downloadqueue->LoadSourceSeeds();
565 if (!serverlist->GetServerCount() && thePrefs::GetNetworkED2K()) {
566 // There are no servers and ED2K active -> ask for download.
567 // As we cannot ask in amuled, we just update there
568 // Kry TODO: Store server.met URL on preferences and use it here and in GUI.
569 #ifndef AMULE_DAEMON
570 if (wxYES == wxMessageBox(
571 wxString(
572 _("You don't have any server in the server list.\nDo you want aMule to download a new list now?")),
573 wxString(_("Server list download")),
574 wxYES_NO,
575 static_cast<wxWindow*>(theApp->amuledlg)))
576 #endif
578 // workaround amuled crash
579 #ifndef AMULE_DAEMON
580 serverlist->UpdateServerMetFromURL(
581 wxT("http://gruk.org/server.met.gz"));
582 #endif
587 // Autoconnect if that option is enabled
588 if (thePrefs::DoAutoConnect() && (thePrefs::GetNetworkED2K() || thePrefs::GetNetworkKademlia())) {
589 if (theApp->ipfilter->IsReady()) {
590 // If it's not ready it will connect later, so don't print it now.
591 AddLogLineC(_("Connecting"));
593 if (thePrefs::GetNetworkED2K()) {
594 theApp->serverconnect->ConnectToAnyServer();
597 StartKad();
601 // Enable GeoIP
602 #ifdef ENABLE_IP2COUNTRY
603 theApp->amuledlg->EnableIP2Country();
604 #endif
606 // Run webserver?
607 if (thePrefs::GetWSIsEnabled()) {
608 wxString aMuleConfigFile = ConfigDir + m_configFile;
609 wxString amulewebPath = thePrefs::GetWSPath();
611 #if defined(__WXMAC__) && !defined(AMULE_DAEMON)
612 // For the Mac GUI application, look for amuleweb in the bundle
613 CFURLRef amulewebUrl = CFBundleCopyAuxiliaryExecutableURL(
614 CFBundleGetMainBundle(), CFSTR("amuleweb"));
616 if (amulewebUrl) {
617 CFURLRef absoluteUrl = CFURLCopyAbsoluteURL(amulewebUrl);
618 CFRelease(amulewebUrl);
620 if (absoluteUrl) {
621 CFStringRef amulewebCfstr = CFURLCopyFileSystemPath(absoluteUrl, kCFURLPOSIXPathStyle);
622 CFRelease(absoluteUrl);
623 #if wxCHECK_VERSION(2, 9, 0)
624 amulewebPath = wxCFStringRef(amulewebCfstr).AsString(wxLocale::GetSystemEncoding());
625 #else
626 amulewebPath = wxMacCFStringHolder(amulewebCfstr).AsString(wxLocale::GetSystemEncoding());
627 #endif
630 #endif
632 #ifdef __WXMSW__
633 # define QUOTE wxT("\"")
634 #else
635 # define QUOTE wxT("\'")
636 #endif
638 wxString cmd =
639 QUOTE +
640 amulewebPath +
641 QUOTE wxT(" ") QUOTE wxT("--amule-config-file=") +
642 aMuleConfigFile +
643 QUOTE;
644 CTerminationProcessAmuleweb *p = new CTerminationProcessAmuleweb(cmd, &webserver_pid);
645 webserver_pid = wxExecute(cmd, wxEXEC_ASYNC, p);
646 bool webserver_ok = webserver_pid > 0;
647 if (webserver_ok) {
648 AddLogLineM(true, CFormat(_("web server running on pid %d")) % webserver_pid);
649 } else {
650 delete p;
651 ShowAlert(_(
652 "You requested to run web server on startup, but the amuleweb binary cannot be run. Please install the package containing aMule web server, or compile aMule using --enable-webserver and run make install"),
653 _("ERROR"), wxOK | wxICON_ERROR);
657 return true;
660 bool CamuleApp::ReinitializeNetwork(wxString* msg)
662 bool ok = true;
663 static bool firstTime = true;
665 if (!firstTime) {
666 // TODO: Destroy previously created sockets
668 firstTime = false;
670 // Some sanity checks first
671 if (thePrefs::ECPort() == thePrefs::GetPort()) {
672 // Select a random usable port in the range 1025 ... 2^16 - 1
673 uint16 port = thePrefs::ECPort();
674 while ( port < 1024 || port == thePrefs::GetPort() ) {
675 port = (uint16)rand();
677 thePrefs::SetECPort( port );
679 wxString err =
680 wxT("Network configuration failed! You cannot use the same port\n")
681 wxT("for the main TCP port and the External Connections port.\n")
682 wxT("The EC port has been changed to avoid conflict, see the\n")
683 wxT("preferences for the new value.\n");
684 *msg << err;
686 AddLogLineM( false, wxEmptyString );
687 AddLogLineM( true, err );
688 AddLogLineM( false, wxEmptyString );
690 ok = false;
693 if (thePrefs::GetUDPPort() == thePrefs::GetPort() + 3) {
694 // Select a random usable value in the range 1025 ... 2^16 - 1
695 uint16 port = thePrefs::GetUDPPort();
696 while ( port < 1024 || port == thePrefs::GetPort() + 3 ) {
697 port = (uint16)rand();
699 thePrefs::SetUDPPort( port );
701 wxString err =
702 wxT("Network configuration failed! You set your UDP port to\n")
703 wxT("the value of the main TCP port plus 3.\n")
704 wxT("This port has been reserved for the Server-UDP port. The\n")
705 wxT("port value has been changed to avoid conflict, see the\n")
706 wxT("preferences for the new value\n");
707 *msg << err;
709 AddLogLineM( false, wxEmptyString );
710 AddLogLineM( true, err );
711 AddLogLineM( false, wxEmptyString );
713 ok = false;
716 // Create the address where we are going to listen
717 // TODO: read this from configuration file
718 amuleIPV4Address myaddr[4];
720 // Create the External Connections Socket.
721 // Default is 4712.
722 // Get ready to handle connections from apps like amulecmd
723 if (thePrefs::GetECAddress().IsEmpty() || !myaddr[0].Hostname(thePrefs::GetECAddress())) {
724 myaddr[0].AnyAddress();
726 myaddr[0].Service(thePrefs::ECPort());
727 ECServerHandler = new ExternalConn(myaddr[0], msg);
729 // Create the UDP socket TCP+3.
730 // Used for source asking on servers.
731 if (thePrefs::GetAddress().IsEmpty()) {
732 myaddr[1].AnyAddress();
733 } else if (!myaddr[1].Hostname(thePrefs::GetAddress())) {
734 myaddr[1].AnyAddress();
735 AddLogLineM(true, CFormat(_("Could not bind ports to the specified address: %s"))
736 % thePrefs::GetAddress());
739 wxString ip = myaddr[1].IPAddress();
740 myaddr[1].Service(thePrefs::GetPort()+3);
741 serverconnect = new CServerConnect(serverlist, myaddr[1]);
742 *msg << CFormat( wxT("*** Server UDP socket (TCP+3) at %s:%u\n") )
743 % ip % ((unsigned int)thePrefs::GetPort() + 3u);
745 // Create the ListenSocket (aMule TCP socket).
746 // Used for Client Port / Connections from other clients,
747 // Client to Client Source Exchange.
748 // Default is 4662.
749 myaddr[2] = myaddr[1];
750 myaddr[2].Service(thePrefs::GetPort());
751 listensocket = new CListenSocket(myaddr[2]);
752 *msg << CFormat( wxT("*** TCP socket (TCP) listening on %s:%u\n") )
753 % ip % (unsigned int)(thePrefs::GetPort());
754 // This command just sets a flag to control maximum number of connections.
755 // Notify(true) has already been called to the ListenSocket, so events may
756 // be already comming in.
757 if (listensocket->Ok()) {
758 listensocket->StartListening();
759 } else {
760 // If we wern't able to start listening, we need to warn the user
761 wxString err;
762 err = CFormat(_("Port %u is not available. You will be LOWID\n")) %
763 (unsigned int)(thePrefs::GetPort());
764 *msg << err;
765 AddLogLineM(true, err);
766 err.Clear();
767 err = CFormat(
768 _("Port %u is not available!\n\nThis means that you will be LOWID.\n\nCheck your network to make sure the port is open for output and input.")) %
769 (unsigned int)(thePrefs::GetPort());
770 ShowAlert(err, _("ERROR"), wxOK | wxICON_ERROR);
773 // Create the UDP socket.
774 // Used for extended eMule protocol, Queue Rating, File Reask Ping.
775 // Also used for Kademlia.
776 // Default is port 4672.
777 myaddr[3] = myaddr[1];
778 myaddr[3].Service(thePrefs::GetUDPPort());
779 clientudp = new CClientUDPSocket(myaddr[3], thePrefs::GetProxyData());
780 if (!thePrefs::IsUDPDisabled()) {
781 *msg << CFormat( wxT("*** Client UDP socket (extended eMule) at %s:%u") )
782 % ip % thePrefs::GetUDPPort();
783 } else {
784 *msg << wxT("*** Client UDP socket (extended eMule) disabled on preferences");
787 #ifdef ENABLE_UPNP
788 if (thePrefs::GetUPnPEnabled()) {
789 try {
790 m_upnpMappings[0] = CUPnPPortMapping(
791 myaddr[0].Service(),
792 "TCP",
793 thePrefs::GetUPnPECEnabled(),
794 "aMule TCP External Connections Socket");
795 m_upnpMappings[1] = CUPnPPortMapping(
796 myaddr[1].Service(),
797 "UDP",
798 thePrefs::GetUPnPEnabled(),
799 "aMule UDP socket (TCP+3)");
800 m_upnpMappings[2] = CUPnPPortMapping(
801 myaddr[2].Service(),
802 "TCP",
803 thePrefs::GetUPnPEnabled(),
804 "aMule TCP Listen Socket");
805 m_upnpMappings[3] = CUPnPPortMapping(
806 myaddr[3].Service(),
807 "UDP",
808 thePrefs::GetUPnPEnabled(),
809 "aMule UDP Extended eMule Socket");
810 m_upnp = new CUPnPControlPoint(thePrefs::GetUPnPTCPPort());
811 m_upnp->AddPortMappings(m_upnpMappings);
812 } catch(CUPnPException &e) {
813 wxString error_msg;
814 error_msg << e.what();
815 AddLogLineM(true, error_msg);
816 fprintf(stderr, "%s\n", (const char *)unicode2char(error_msg));
819 #endif
821 return ok;
824 /* Original implementation by Bouc7 of the eMule Project.
825 aMule Signature idea was designed by BigBob and implemented
826 by Un-Thesis, with design inputs and suggestions from bothie.
828 void CamuleApp::OnlineSig(bool zero /* reset stats (used on shutdown) */)
830 // Do not do anything if online signature is disabled in Preferences
831 if (!thePrefs::IsOnlineSignatureEnabled() || m_emulesig_path.IsEmpty()) {
832 // We do not need to check m_amulesig_path because if m_emulesig_path is empty,
833 // that means m_amulesig_path is empty too.
834 return;
837 // Remove old signature files
838 if ( wxFileExists( m_emulesig_path ) ) { wxRemoveFile( m_emulesig_path ); }
839 if ( wxFileExists( m_amulesig_path ) ) { wxRemoveFile( m_amulesig_path ); }
842 wxTextFile amulesig_out;
843 wxTextFile emulesig_out;
845 // Open both files if needed
846 if ( !emulesig_out.Create( m_emulesig_path) ) {
847 AddLogLineM(true, _("Failed to create OnlineSig File"));
848 // Will never try again.
849 m_amulesig_path.Clear();
850 m_emulesig_path.Clear();
851 return;
854 if ( !amulesig_out.Create(m_amulesig_path) ) {
855 AddLogLineM(true, _("Failed to create aMule OnlineSig File"));
856 // Will never try again.
857 m_amulesig_path.Clear();
858 m_emulesig_path.Clear();
859 return;
862 wxString emulesig_string;
863 wxString temp;
865 if (zero) {
866 emulesig_string = wxT("0\xA0.0|0.0|0");
867 amulesig_out.AddLine(wxT("0\n0\n0\n0\n0\n0\n0.0\n0.0\n0\n0"));
868 } else {
869 if (IsConnectedED2K()) {
871 temp = wxString::Format(wxT("%d"),serverconnect->GetCurrentServer()->GetPort());
873 // We are online
874 emulesig_string =
875 // Connected
876 wxT("1|")
877 //Server name
878 + serverconnect->GetCurrentServer()->GetListName()
879 + wxT("|")
880 // IP and port of the server
881 + serverconnect->GetCurrentServer()->GetFullIP()
882 + wxT("|")
883 + temp;
886 // Now for amule sig
888 // Connected. State 1, full info
889 amulesig_out.AddLine(wxT("1"));
890 // Server Name
891 amulesig_out.AddLine(serverconnect->GetCurrentServer()->GetListName());
892 // Server IP
893 amulesig_out.AddLine(serverconnect->GetCurrentServer()->GetFullIP());
894 // Server Port
895 amulesig_out.AddLine(temp);
897 if (serverconnect->IsLowID()) {
898 amulesig_out.AddLine(wxT("L"));
899 } else {
900 amulesig_out.AddLine(wxT("H"));
903 } else if (serverconnect->IsConnecting()) {
904 emulesig_string = wxT("0");
906 // Connecting. State 2, No info.
907 amulesig_out.AddLine(wxT("2\n0\n0\n0\n0"));
908 } else {
909 // Not connected to a server
910 emulesig_string = wxT("0");
912 // Not connected, state 0, no info
913 amulesig_out.AddLine(wxT("0\n0\n0\n0\n0"));
915 if (IsConnectedKad()) {
916 if(Kademlia::CKademlia::IsFirewalled()) {
917 // Connected. Firewalled. State 1.
918 amulesig_out.AddLine(wxT("1"));
919 } else {
920 // Connected. State 2.
921 amulesig_out.AddLine(wxT("2"));
923 } else {
924 // Not connected.State 0.
925 amulesig_out.AddLine(wxT("0"));
927 emulesig_string += wxT("\xA");
929 // Datarate for downloads
930 temp = wxString::Format(wxT("%.1f"), theStats::GetDownloadRate() / 1024.0);
932 emulesig_string += temp + wxT("|");
933 amulesig_out.AddLine(temp);
935 // Datarate for uploads
936 temp = wxString::Format(wxT("%.1f"), theStats::GetUploadRate() / 1024.0);
938 emulesig_string += temp + wxT("|");
939 amulesig_out.AddLine(temp);
941 // Number of users waiting for upload
942 temp = wxString::Format(wxT("%d"), theStats::GetWaitingUserCount());
944 emulesig_string += temp;
945 amulesig_out.AddLine(temp);
947 // Number of shared files (not on eMule)
948 amulesig_out.AddLine(wxString::Format(wxT("%d"), theStats::GetSharedFileCount()));
951 // eMule signature finished here. Write the line to the wxTextFile.
952 emulesig_out.AddLine(emulesig_string);
954 // Now for aMule signature extras
956 // Nick on the network
957 amulesig_out.AddLine(thePrefs::GetUserNick());
959 // Total received in bytes
960 amulesig_out.AddLine( CFormat( wxT("%llu") ) % (theStats::GetSessionReceivedBytes() + thePrefs::GetTotalDownloaded()) );
962 // Total sent in bytes
963 amulesig_out.AddLine( CFormat( wxT("%llu") ) % (theStats::GetSessionSentBytes() + thePrefs::GetTotalUploaded()) );
965 // amule version
966 #ifdef SVNDATE
967 amulesig_out.AddLine(wxT(VERSION) wxT(" ") wxT(SVNDATE));
968 #else
969 amulesig_out.AddLine(wxT(VERSION));
970 #endif
972 if (zero) {
973 amulesig_out.AddLine(wxT("0"));
974 amulesig_out.AddLine(wxT("0"));
975 amulesig_out.AddLine(wxT("0"));
976 } else {
977 // Total received bytes in session
978 amulesig_out.AddLine( CFormat( wxT("%llu") ) %
979 theStats::GetSessionReceivedBytes() );
981 // Total sent bytes in session
982 amulesig_out.AddLine( CFormat( wxT("%llu") ) %
983 theStats::GetSessionSentBytes() );
985 // Uptime
986 amulesig_out.AddLine(CFormat(wxT("%llu")) % theStats::GetUptimeSeconds());
989 // Flush the files
990 emulesig_out.Write();
991 amulesig_out.Write();
992 } //End Added By Bouc7
995 // Gracefully handle fatal exceptions and print backtrace if possible
996 void CamuleApp::OnFatalException()
998 /* Print the backtrace */
999 fprintf(stderr, "\n--------------------------------------------------------------------------------\n");
1000 fprintf(stderr, "A fatal error has occurred and aMule has crashed.\n");
1001 fprintf(stderr, "Please assist us in fixing this problem by posting the backtrace below in our\n");
1002 fprintf(stderr, "'aMule Crashes' forum and include as much information as possible regarding the\n");
1003 fprintf(stderr, "circumstances of this crash. The forum is located here:\n");
1004 fprintf(stderr, " http://forum.amule.org/index.php?board=67.0\n");
1005 fprintf(stderr, "If possible, please try to generate a real backtrace of this crash:\n");
1006 fprintf(stderr, " http://wiki.amule.org/index.php/Backtraces\n\n");
1007 fprintf(stderr, "----------------------------=| BACKTRACE FOLLOWS: |=----------------------------\n");
1008 fprintf(stderr, "Current version is: %s\n", strFullMuleVersion);
1009 fprintf(stderr, "Running on: %s\n\n", strOSDescription);
1011 print_backtrace(1); // 1 == skip this function.
1013 fprintf(stderr, "\n--------------------------------------------------------------------------------\n");
1017 // Sets the localization of aMule
1018 void CamuleApp::Localize_mule()
1020 InitCustomLanguages();
1021 InitLocale(m_locale, StrLang2wx(thePrefs::GetLanguageID()));
1022 if (!m_locale.IsOk()) {
1023 AddLogLineM(false,_("The selected locale seems not to be installed on your box. (Note: I'll try to set it anyway)"));
1028 // Displays information related to important changes in aMule.
1029 // Is called when the user runs a new version of aMule
1030 void CamuleApp::Trigger_New_version(wxString new_version)
1032 wxString info = wxT(" --- ") + CFormat(_("This is the first time you run aMule %s")) % new_version + wxT(" ---\n\n");
1033 if (new_version == wxT("SVN")) {
1034 info += _("This version is a testing version, updated daily, and\n");
1035 info += _("we give no warranty it won't break anything, burn your house,\n");
1036 info += _("or kill your dog. But it *should* be safe to use anyway.\n");
1039 // General info
1040 info += wxT("\n");
1041 info += _("More information, support and new releases can found at our homepage,\n");
1042 info += _("at www.aMule.org, or in our IRC channel #aMule at irc.freenode.net.\n");
1043 info += wxT("\n");
1044 info += _("Feel free to report any bugs to http://forum.amule.org");
1046 ShowAlert(info, _("Info"), wxCENTRE | wxOK | wxICON_ERROR);
1050 void CamuleApp::SetOSFiles(const wxString new_path)
1052 if ( thePrefs::IsOnlineSignatureEnabled() ) {
1053 if ( ::wxDirExists(new_path) ) {
1054 m_emulesig_path = JoinPaths(new_path, wxT("onlinesig.dat"));
1055 m_amulesig_path = JoinPaths(new_path, wxT("amulesig.dat"));
1056 } else {
1057 ShowAlert(_("The folder for Online Signature files you specified is INVALID!\n OnlineSignature will be DISABLED until you fix it on preferences."), _("ERROR"), wxOK | wxICON_ERROR);
1058 m_emulesig_path.Clear();
1059 m_amulesig_path.Clear();
1061 } else {
1062 m_emulesig_path.Clear();
1063 m_amulesig_path.Clear();
1068 #ifdef __WXDEBUG__
1069 #ifndef wxUSE_STACKWALKER
1070 #define wxUSE_STACKWALKER 0
1071 #endif
1072 void CamuleApp::OnAssertFailure(const wxChar* file, int line,
1073 const wxChar* func, const wxChar* cond, const wxChar* msg)
1075 if (!wxUSE_STACKWALKER || !wxThread::IsMain() || !IsRunning()) {
1076 wxString errmsg = CFormat( wxT("%s:%s:%d: Assertion '%s' failed. %s") )
1077 % file % func % line % cond % ( msg ? msg : wxT("") );
1079 fprintf(stderr, "Assertion failed: %s\n", (const char*)unicode2char(errmsg));
1081 // Skip the function-calls directly related to the assert call.
1082 fprintf(stderr, "\nBacktrace follows:\n");
1083 print_backtrace(3);
1084 fprintf(stderr, "\n");
1087 if (wxThread::IsMain() && IsRunning()) {
1088 AMULE_APP_BASE::OnAssertFailure(file, line, func, cond, msg);
1089 } else {
1090 // Abort, allows gdb to catch the assertion
1091 raise( SIGABRT );
1094 #endif
1097 void CamuleApp::OnUDPDnsDone(CMuleInternalEvent& evt)
1099 CServerUDPSocket* socket =(CServerUDPSocket*)evt.GetClientData();
1100 socket->OnHostnameResolved(evt.GetExtraLong());
1104 void CamuleApp::OnSourceDnsDone(CMuleInternalEvent& evt)
1106 downloadqueue->OnHostnameResolved(evt.GetExtraLong());
1110 void CamuleApp::OnServerDnsDone(CMuleInternalEvent& evt)
1112 AddLogLineMS(false, _("Server hostname notified"));
1113 serverconnect->OnServerHostnameResolved(evt.GetClientData(), evt.GetExtraLong());
1117 void CamuleApp::OnTCPTimer(CTimerEvent& WXUNUSED(evt))
1119 if(!IsRunning()) {
1120 return;
1122 serverconnect->StopConnectionTry();
1123 if (IsConnectedED2K() ) {
1124 return;
1126 serverconnect->ConnectToAnyServer();
1130 void CamuleApp::OnCoreTimer(CTimerEvent& WXUNUSED(evt))
1132 // Former TimerProc section
1133 static uint64 msPrev1, msPrev5, msPrevSave, msPrevHist, msPrevOS, msPrevKnownMet;
1134 uint64 msCur = theStats::GetUptimeMillis();
1135 TheTime = msCur / 1000;
1137 if (!IsRunning()) {
1138 return;
1141 #ifndef AMULE_DAEMON
1142 // Check if we should terminate the app
1143 if ( g_shutdownSignal ) {
1144 wxWindow* top = GetTopWindow();
1146 if ( top ) {
1147 top->Close(true);
1148 } else {
1149 // No top-window, have to force termination.
1150 wxExit();
1153 #endif
1155 // There is a theoretical chance that the core time function can recurse:
1156 // if an event function gets blocked on a mutex (communicating with the
1157 // UploadBandwidthThrottler) wx spawns a new event loop and processes more events.
1158 // If CPU load gets high a new core timer event could be generated before the last
1159 // one was finished and so recursion could occur, which would be bad.
1160 // Detect this and do an early return then.
1161 static bool recurse = false;
1162 if (recurse) {
1163 return;
1165 recurse = true;
1167 uploadqueue->Process();
1168 downloadqueue->Process();
1169 //theApp->clientcredits->Process();
1170 theStats::CalculateRates();
1172 if (msCur-msPrevHist > 1000) {
1173 // unlike the other loop counters in this function this one will sometimes
1174 // produce two calls in quick succession (if there was a gap of more than one
1175 // second between calls to TimerProc) - this is intentional! This way the
1176 // history list keeps an average of one node per second and gets thinned out
1177 // correctly as time progresses.
1178 msPrevHist += 1000;
1180 m_statistics->RecordHistory();
1185 if (msCur-msPrev1 > 1000) { // approximately every second
1186 msPrev1 = msCur;
1187 clientcredits->Process();
1188 clientlist->Process();
1190 // Publish files to server if needed.
1191 sharedfiles->Process();
1193 if( Kademlia::CKademlia::IsRunning() ) {
1194 Kademlia::CKademlia::Process();
1195 if(Kademlia::CKademlia::GetPrefs()->HasLostConnection()) {
1196 StopKad();
1197 clientudp->Close();
1198 clientudp->Open();
1199 if (thePrefs::Reconnect()) {
1200 StartKad();
1205 if( serverconnect->IsConnecting() && !serverconnect->IsSingleConnect() ) {
1206 serverconnect->TryAnotherConnectionrequest();
1208 if (serverconnect->IsConnecting()) {
1209 serverconnect->CheckForTimeout();
1211 listensocket->UpdateConnectionsStatus();
1216 if (msCur-msPrev5 > 5000) { // every 5 seconds
1217 msPrev5 = msCur;
1218 listensocket->Process();
1221 if (msCur-msPrevSave >= 60000) {
1222 msPrevSave = msCur;
1223 wxString buffer;
1225 // Save total upload/download to preferences
1226 wxConfigBase* cfg = wxConfigBase::Get();
1227 buffer = CFormat(wxT("%llu")) % (theStats::GetSessionReceivedBytes() + thePrefs::GetTotalDownloaded());
1228 cfg->Write(wxT("/Statistics/TotalDownloadedBytes"), buffer);
1230 buffer = CFormat(wxT("%llu")) % (theStats::GetSessionSentBytes() + thePrefs::GetTotalUploaded());
1231 cfg->Write(wxT("/Statistics/TotalUploadedBytes"), buffer);
1233 // Write changes to file
1234 cfg->Flush();
1238 // Special
1239 if (msCur - msPrevOS >= thePrefs::GetOSUpdate() * 1000ull) {
1240 OnlineSig(); // Added By Bouc7
1241 msPrevOS = msCur;
1244 if (msCur - msPrevKnownMet >= 30*60*1000/*There must be a prefs option for this*/) {
1245 // Save Shared Files data
1246 knownfiles->Save();
1247 msPrevKnownMet = msCur;
1251 // Recomended by lugdunummaster himself - from emule 0.30c
1252 serverconnect->KeepConnectionAlive();
1254 // Disarm recursion protection
1255 recurse = false;
1259 void CamuleApp::OnFinishedHashing(CHashingEvent& evt)
1261 wxCHECK_RET(evt.GetResult(), wxT("No result of hashing"));
1263 CKnownFile* owner = const_cast<CKnownFile*>(evt.GetOwner());
1264 CKnownFile* result = evt.GetResult();
1266 if (owner) {
1267 // Check if the partfile still exists, as it might have
1268 // been deleted in the mean time.
1269 if (downloadqueue->IsPartFile(owner)) {
1270 // This cast must not be done before the IsPartFile
1271 // call, as dynamic_cast will barf on dangling pointers.
1272 dynamic_cast<CPartFile*>(owner)->PartFileHashFinished(result);
1274 } else {
1275 static int filecount;
1276 static uint64 bytecount;
1278 if (knownfiles->SafeAddKFile(result)) {
1279 AddDebugLogLineM(false, logKnownFiles,
1280 CFormat(wxT("Safe adding file to sharedlist: %s")) % result->GetFileName());
1281 sharedfiles->SafeAddKFile(result);
1283 filecount++;
1284 bytecount += result->GetFileSize();
1285 // If we have added 30 files or files with a total size of ~300mb
1286 if ( ( filecount == 30 ) || ( bytecount >= 314572800 ) ) {
1287 AddDebugLogLineM(false, logKnownFiles, wxT("Failsafe for crash on file hashing creation"));
1288 if ( m_app_state != APP_STATE_SHUTTINGDOWN ) {
1289 knownfiles->Save();
1290 filecount = 0;
1291 bytecount = 0;
1294 } else {
1295 AddDebugLogLineM(false, logKnownFiles,
1296 CFormat(wxT("File not added to sharedlist: %s")) % result->GetFileName());
1297 delete result;
1303 void CamuleApp::OnFinishedAICHHashing(CHashingEvent& evt)
1305 wxCHECK_RET(evt.GetResult(), wxT("No result of AICH-hashing"));
1307 CKnownFile* owner = const_cast<CKnownFile*>(evt.GetOwner());
1308 CScopedPtr<CKnownFile> result(evt.GetResult());
1310 // Check that the owner is still valid
1311 if (knownfiles->IsKnownFile(owner)) {
1312 if (result->GetAICHHashset()->GetStatus() == AICH_HASHSETCOMPLETE) {
1313 CAICHHashSet* oldSet = owner->GetAICHHashset();
1314 CAICHHashSet* newSet = result->GetAICHHashset();
1316 owner->SetAICHHashset(newSet);
1317 newSet->SetOwner(owner);
1319 result->SetAICHHashset(oldSet);
1320 oldSet->SetOwner(result.get());
1326 void CamuleApp::OnFinishedCompletion(CCompletionEvent& evt)
1328 CPartFile* completed = const_cast<CPartFile*>(evt.GetOwner());
1329 wxCHECK_RET(completed, wxT("Completion event sent for unspecified file"));
1330 wxASSERT_MSG(downloadqueue->IsPartFile(completed), wxT("CCompletionEvent for unknown partfile."));
1332 completed->CompleteFileEnded(evt.ErrorOccured(), evt.GetFullPath());
1333 if (evt.ErrorOccured()) {
1334 CUserEvents::ProcessEvent(CUserEvents::ErrorOnCompletion, completed);
1337 // Check if we should execute an script/app/whatever.
1338 CUserEvents::ProcessEvent(CUserEvents::DownloadCompleted, completed);
1341 void CamuleApp::OnFinishedAllocation(CAllocFinishedEvent& evt)
1343 CPartFile *file = evt.GetFile();
1344 wxCHECK_RET(file, wxT("Allocation finished event sent for unspecified file"));
1345 wxASSERT_MSG(downloadqueue->IsPartFile(file), wxT("CAllocFinishedEvent for unknown partfile"));
1347 file->SetPartFileStatus(PS_EMPTY);
1349 if (evt.Succeeded()) {
1350 if (evt.IsPaused()) {
1351 file->StopFile();
1352 } else {
1353 file->ResumeFile();
1355 } else {
1356 AddLogLineM(false, CFormat(_("Disk space preallocation for file '%s' failed: %s")) % file->GetFileName() % wxString(UTF82unicode(std::strerror(evt.GetResult()))));
1357 file->StopFile();
1360 file->AllocationFinished();
1363 void CamuleApp::OnNotifyEvent(CMuleGUIEvent& evt)
1365 #if defined(AMULE_DAEMON)
1366 evt.Notify();
1367 #else
1368 if (theApp->amuledlg) {
1369 evt.Notify();
1371 #endif
1375 void CamuleApp::ShutDown()
1377 // Log
1378 AddDebugLogLineM(false, logGeneral, wxT("CamuleApp::ShutDown() has started."));
1380 // Signal the hashing thread to terminate
1381 m_app_state = APP_STATE_SHUTTINGDOWN;
1383 StopKad();
1385 // Kry - Save the sources seeds on app exit
1386 if (thePrefs::GetSrcSeedsOn()) {
1387 downloadqueue->SaveSourceSeeds();
1390 OnlineSig(true); // Added By Bouc7
1392 // Exit thread scheduler and upload thread
1393 CThreadScheduler::Terminate();
1395 AddDebugLogLineN(logGeneral, wxT("Terminate upload thread."));
1396 uploadBandwidthThrottler->EndThread();
1398 // Close sockets to avoid new clients coming in
1399 if (listensocket) {
1400 listensocket->Close();
1401 listensocket->KillAllSockets();
1404 if (serverconnect) {
1405 serverconnect->Disconnect();
1408 ECServerHandler->KillAllSockets();
1410 #ifdef ENABLE_UPNP
1411 if (thePrefs::GetUPnPEnabled()) {
1412 if (m_upnp) {
1413 m_upnp->DeletePortMappings(m_upnpMappings);
1416 #endif
1418 // saving data & stuff
1419 if (knownfiles) {
1420 knownfiles->Save();
1423 thePrefs::Add2TotalDownloaded(theStats::GetSessionReceivedBytes());
1424 thePrefs::Add2TotalUploaded(theStats::GetSessionSentBytes());
1426 if (glob_prefs) {
1427 glob_prefs->Save();
1430 CPath configFileName = CPath(ConfigDir + m_configFile);
1431 CPath::BackupFile(configFileName, wxT(".bak"));
1433 if (clientlist) {
1434 clientlist->DeleteAll();
1437 // Log
1438 AddDebugLogLineM(false, logGeneral, wxT("CamuleApp::ShutDown() has ended."));
1442 bool CamuleApp::AddServer(CServer *srv, bool fromUser)
1444 if ( serverlist->AddServer(srv, fromUser) ) {
1445 Notify_ServerAdd(srv);
1446 return true;
1448 return false;
1452 uint32 CamuleApp::GetPublicIP(bool ignorelocal) const
1454 if (m_dwPublicIP == 0) {
1455 if (Kademlia::CKademlia::IsConnected() && Kademlia::CKademlia::GetIPAddress() ) {
1456 return wxUINT32_SWAP_ALWAYS(Kademlia::CKademlia::GetIPAddress());
1457 } else {
1458 return ignorelocal ? 0 : m_localip;
1462 return m_dwPublicIP;
1466 void CamuleApp::SetPublicIP(const uint32 dwIP)
1468 wxASSERT((dwIP == 0) || !IsLowID(dwIP));
1470 if (dwIP != 0 && dwIP != m_dwPublicIP && serverlist != NULL) {
1471 m_dwPublicIP = dwIP;
1472 serverlist->CheckForExpiredUDPKeys();
1473 } else {
1474 m_dwPublicIP = dwIP;
1479 wxString CamuleApp::GetLog(bool reset)
1481 wxFile logfile;
1482 logfile.Open(ConfigDir + wxT("logfile"));
1483 if ( !logfile.IsOpened() ) {
1484 return _("ERROR: can't open logfile");
1486 int len = logfile.Length();
1487 if ( len == 0 ) {
1488 return _("WARNING: logfile is empty. Something is wrong.");
1490 char *tmp_buffer = new char[len + sizeof(wxChar)];
1491 logfile.Read(tmp_buffer, len);
1492 memset(tmp_buffer + len, 0, sizeof(wxChar));
1494 // try to guess file format
1495 wxString str;
1496 if (tmp_buffer[0] && tmp_buffer[1]) {
1497 str = wxString(UTF82unicode(tmp_buffer));
1498 } else {
1499 str = wxWCharBuffer((wchar_t *)tmp_buffer);
1502 delete [] tmp_buffer;
1503 if ( reset ) {
1504 theLogger.CloseLogfile();
1505 if (theLogger.OpenLogfile(ConfigDir + wxT("logfile"))) {
1506 AddLogLineM(false, _("Log has been reset"));
1509 return str;
1513 wxString CamuleApp::GetServerLog(bool reset)
1515 wxString ret = server_msg;
1516 if ( reset ) {
1517 server_msg.Clear();
1519 return ret;
1522 wxString CamuleApp::GetDebugLog(bool reset)
1524 return GetLog(reset);
1528 void CamuleApp::AddServerMessageLine(wxString &msg)
1530 server_msg += msg + wxT("\n");
1531 AddLogLineM(false, CFormat(_("ServerMessage: %s")) % msg);
1536 void CamuleApp::OnFinishedHTTPDownload(CMuleInternalEvent& event)
1538 switch (event.GetInt()) {
1539 case HTTP_IPFilter:
1540 ipfilter->DownloadFinished(event.GetExtraLong());
1541 break;
1542 case HTTP_ServerMet:
1543 serverlist->DownloadFinished(event.GetExtraLong());
1544 break;
1545 case HTTP_ServerMetAuto:
1546 serverlist->AutoDownloadFinished(event.GetExtraLong());
1547 break;
1548 case HTTP_VersionCheck:
1549 CheckNewVersion(event.GetExtraLong());
1550 break;
1551 case HTTP_NodesDat:
1552 if (event.GetExtraLong() == HTTP_Success) {
1554 wxString file = ConfigDir + wxT("nodes.dat");
1555 if (wxFileExists(file)) {
1556 wxRemoveFile(file);
1559 if ( Kademlia::CKademlia::IsRunning() ) {
1560 Kademlia::CKademlia::Stop();
1563 wxRenameFile(file + wxT(".download"),file);
1565 Kademlia::CKademlia::Start();
1566 theApp->ShowConnectionState();
1568 } else if (event.GetExtraLong() == HTTP_Skipped) {
1569 AddLogLineN(CFormat(_("Skipped download of %s, because requested file is not newer.")) % wxT("nodes.dat"));
1570 } else {
1571 AddLogLineC(_("Failed to download the nodes list."));
1573 break;
1574 #ifdef ENABLE_IP2COUNTRY
1575 case HTTP_GeoIP:
1576 theApp->amuledlg->IP2CountryDownloadFinished(event.GetExtraLong());
1577 // If we updated, the dialog is already up. Redraw it to show the flags.
1578 theApp->amuledlg->Refresh();
1579 break;
1580 #endif
1584 void CamuleApp::CheckNewVersion(uint32 result)
1586 if (result == HTTP_Success) {
1587 wxString filename = ConfigDir + wxT("last_version_check");
1588 wxTextFile file;
1590 if (!file.Open(filename)) {
1591 AddLogLineM(true, _("Failed to open the downloaded version check file") );
1592 return;
1593 } else if (!file.GetLineCount()) {
1594 AddLogLineM(true, _("Corrupted version check file"));
1595 } else {
1596 wxString versionLine = file.GetFirstLine();
1597 wxStringTokenizer tkz(versionLine, wxT("."));
1599 AddDebugLogLineM(false, logGeneral, wxString(wxT("Running: ")) + wxT(VERSION) + wxT(", Version check: ") + versionLine);
1601 long fields[] = {0, 0, 0};
1602 for (int i = 0; i < 3; ++i) {
1603 if (!tkz.HasMoreTokens()) {
1604 AddLogLineM(true, _("Corrupted version check file"));
1605 return;
1606 } else {
1607 wxString token = tkz.GetNextToken();
1609 if (!token.ToLong(&fields[i])) {
1610 AddLogLineM(true, _("Corrupted version check file"));
1611 return;
1616 long curVer = make_full_ed2k_version(VERSION_MJR, VERSION_MIN, VERSION_UPDATE);
1617 long newVer = make_full_ed2k_version(fields[0], fields[1], fields[2]);
1619 if (curVer < newVer) {
1620 AddLogLineM(true, _("You are using an outdated version of aMule!"));
1621 AddLogLineM(false, wxString::Format(_("Your aMule version is %i.%i.%i and the latest version is %li.%li.%li"), VERSION_MJR, VERSION_MIN, VERSION_UPDATE, fields[0], fields[1], fields[2]));
1622 AddLogLineM(false, _("The latest version can always be found at http://www.amule.org"));
1623 #ifdef AMULE_DAEMON
1624 AddLogLineMS(true, CFormat(_("WARNING: Your aMuled version is outdated: %i.%i.%i < %li.%li.%li"))
1625 % VERSION_MJR % VERSION_MIN % VERSION_UPDATE % fields[0] % fields[1] % fields[2]);
1626 #endif
1627 } else {
1628 AddLogLineM(false, _("Your copy of aMule is up to date."));
1632 file.Close();
1633 wxRemoveFile(filename);
1634 } else {
1635 AddLogLineM(true, _("Failed to download the version check file"));
1641 bool CamuleApp::IsConnected() const
1643 return (IsConnectedED2K() || IsConnectedKad());
1647 bool CamuleApp::IsConnectedED2K() const
1649 return serverconnect && serverconnect->IsConnected();
1653 bool CamuleApp::IsConnectedKad() const
1655 return Kademlia::CKademlia::IsConnected();
1659 bool CamuleApp::IsFirewalled() const
1661 if (theApp->IsConnectedED2K() && !theApp->serverconnect->IsLowID()) {
1662 return false; // we have an eD2K HighID -> not firewalled
1665 return IsFirewalledKad(); // If kad says ok, it's ok.
1668 bool CamuleApp::IsFirewalledKad() const
1670 return !Kademlia::CKademlia::IsConnected() // not connected counts as firewalled
1671 || Kademlia::CKademlia::IsFirewalled();
1674 bool CamuleApp::IsFirewalledKadUDP() const
1676 return !Kademlia::CKademlia::IsConnected() // not connected counts as firewalled
1677 || Kademlia::CUDPFirewallTester::IsFirewalledUDP(true);
1680 bool CamuleApp::IsKadRunning() const
1682 return Kademlia::CKademlia::IsRunning();
1685 // Kad stats
1686 uint32 CamuleApp::GetKadUsers() const
1688 return Kademlia::CKademlia::GetKademliaUsers();
1691 uint32 CamuleApp::GetKadFiles() const
1693 return Kademlia::CKademlia::GetKademliaFiles();
1696 uint32 CamuleApp::GetKadIndexedSources() const
1698 return Kademlia::CKademlia::GetIndexed()->m_totalIndexSource;
1701 uint32 CamuleApp::GetKadIndexedKeywords() const
1703 return Kademlia::CKademlia::GetIndexed()->m_totalIndexKeyword;
1706 uint32 CamuleApp::GetKadIndexedNotes() const
1708 return Kademlia::CKademlia::GetIndexed()->m_totalIndexNotes;
1711 uint32 CamuleApp::GetKadIndexedLoad() const
1713 return Kademlia::CKademlia::GetIndexed()->m_totalIndexLoad;
1717 // True IP of machine
1718 uint32 CamuleApp::GetKadIPAdress() const
1720 return wxUINT32_SWAP_ALWAYS(Kademlia::CKademlia::GetPrefs()->GetIPAddress());
1723 // Buddy status
1724 uint8 CamuleApp::GetBuddyStatus() const
1726 return clientlist->GetBuddyStatus();
1729 uint32 CamuleApp::GetBuddyIP() const
1731 return clientlist->GetBuddy()->GetIP();
1734 uint32 CamuleApp::GetBuddyPort() const
1736 return clientlist->GetBuddy()->GetUDPPort();
1739 bool CamuleApp::CanDoCallback(CUpDownClient *client)
1741 if (Kademlia::CKademlia::IsConnected()) {
1742 if (IsConnectedED2K()) {
1743 if (serverconnect->IsLowID()) {
1744 if (Kademlia::CKademlia::IsFirewalled()) {
1745 //Both Connected - Both Firewalled
1746 return false;
1747 } else {
1748 if (client->GetServerIP() == theApp->serverconnect->GetCurrentServer()->GetIP() &&
1749 client->GetServerPort() == theApp->serverconnect->GetCurrentServer()->GetPort()) {
1750 // Both Connected - Server lowID, Kad Open - Client on same server
1751 // We prevent a callback to the server as this breaks the protocol
1752 // and will get you banned.
1753 return false;
1754 } else {
1755 // Both Connected - Server lowID, Kad Open - Client on remote server
1756 return true;
1759 } else {
1760 //Both Connected - Server HighID, Kad don't care
1761 return true;
1763 } else {
1764 if (Kademlia::CKademlia::IsFirewalled()) {
1765 //Only Kad Connected - Kad Firewalled
1766 return false;
1767 } else {
1768 //Only Kad Conected - Kad Open
1769 return true;
1772 } else {
1773 if (IsConnectedED2K()) {
1774 if (serverconnect->IsLowID()) {
1775 //Only Server Connected - Server LowID
1776 return false;
1777 } else {
1778 //Only Server Connected - Server HighID
1779 return true;
1781 } else {
1782 //We are not connected at all!
1783 return false;
1788 void CamuleApp::ShowUserCount() {
1789 uint32 totaluser = 0, totalfile = 0;
1791 theApp->serverlist->GetUserFileStatus( totaluser, totalfile );
1793 wxString buffer;
1795 static const wxString s_singlenetstatusformat = _("Users: %s | Files: %s");
1796 static const wxString s_bothnetstatusformat = _("Users: E: %s K: %s | Files: E: %s K: %s");
1798 if (thePrefs::GetNetworkED2K() && thePrefs::GetNetworkKademlia()) {
1799 buffer = CFormat(s_bothnetstatusformat) % CastItoIShort(totaluser) % CastItoIShort(Kademlia::CKademlia::GetKademliaUsers()) % CastItoIShort(totalfile) % CastItoIShort(Kademlia::CKademlia::GetKademliaFiles());
1800 } else if (thePrefs::GetNetworkED2K()) {
1801 buffer = CFormat(s_singlenetstatusformat) % CastItoIShort(totaluser) % CastItoIShort(totalfile);
1802 } else if (thePrefs::GetNetworkKademlia()) {
1803 buffer = CFormat(s_singlenetstatusformat) % CastItoIShort(Kademlia::CKademlia::GetKademliaUsers()) % CastItoIShort(Kademlia::CKademlia::GetKademliaFiles());
1804 } else {
1805 buffer = _("No networks selected");
1808 Notify_ShowUserCount(buffer);
1812 void CamuleApp::ListenSocketHandler(wxSocketEvent& event)
1814 wxCHECK_RET(listensocket, wxT("Connection-event for NULL'd listen-socket"));
1815 wxCHECK_RET(event.GetSocketEvent() == wxSOCKET_CONNECTION,
1816 wxT("Invalid event received for listen-socket"));
1818 if (m_app_state == APP_STATE_RUNNING) {
1819 listensocket->OnAccept(0);
1820 } else if (m_app_state == APP_STATE_STARTING) {
1821 // When starting up, connection may be made before we are able
1822 // to handle them. However, if these are ignored, no futher
1823 // connection-events will be triggered, so we have to accept it.
1824 wxSocketBase* socket = listensocket->Accept(false);
1826 wxCHECK_RET(socket, wxT("NULL returned by Accept() during startup"));
1828 socket->Destroy();
1833 void CamuleApp::ShowConnectionState()
1835 static uint8 old_state = (1<<7); // This flag doesn't exist
1837 uint8 state = 0;
1839 if (theApp->serverconnect->IsConnected()) {
1840 state |= CONNECTED_ED2K;
1843 if (Kademlia::CKademlia::IsRunning()) {
1844 if (Kademlia::CKademlia::IsConnected()) {
1845 if (!Kademlia::CKademlia::IsFirewalled()) {
1846 state |= CONNECTED_KAD_OK;
1847 } else {
1848 state |= CONNECTED_KAD_FIREWALLED;
1850 } else {
1851 state |= CONNECTED_KAD_NOT;
1855 Notify_ShowConnState(state);
1857 if (old_state != state) {
1858 // Get the changed value
1859 int changed_flags = old_state ^ state;
1861 if (changed_flags & CONNECTED_ED2K) {
1862 // ED2K status changed
1863 wxString connected_server;
1864 CServer* ed2k_server = theApp->serverconnect->GetCurrentServer();
1865 if (ed2k_server) {
1866 connected_server = ed2k_server->GetListName();
1868 if (state & CONNECTED_ED2K) {
1869 // We connected to some server
1870 const wxString id = theApp->serverconnect->IsLowID() ? _("with LowID") : _("with HighID");
1872 AddLogLineM(true, CFormat(_("Connected to %s %s")) % connected_server % id);
1873 } else {
1874 if ( theApp->serverconnect->IsConnecting() ) {
1875 AddLogLineM(true, CFormat(_("Connecting to %s")) % connected_server);
1876 } else {
1877 AddLogLineM(true, _("Disconnected from eD2k"));
1882 if (changed_flags & CONNECTED_KAD_NOT) {
1883 if (state & CONNECTED_KAD_NOT) {
1884 AddLogLineM(true, _("Kad started."));
1885 } else {
1886 AddLogLineM(true, _("Kad stopped."));
1890 if (changed_flags & (CONNECTED_KAD_OK | CONNECTED_KAD_FIREWALLED)) {
1891 if (state & (CONNECTED_KAD_OK | CONNECTED_KAD_FIREWALLED)) {
1892 if (state & CONNECTED_KAD_OK) {
1893 AddLogLineM(true, _("Connected to Kad (ok)"));
1894 } else {
1895 AddLogLineM(true, _("Connected to Kad (firewalled)"));
1897 } else {
1898 AddLogLineM(true, _("Disconnected from Kad"));
1902 old_state = state;
1904 theApp->downloadqueue->OnConnectionState(IsConnected());
1907 ShowUserCount();
1908 Notify_ShowConnState(state);
1912 void CamuleApp::UDPSocketHandler(wxSocketEvent& event)
1914 CMuleUDPSocket* socket = (CMuleUDPSocket*)(event.GetClientData());
1915 wxCHECK_RET(socket, wxT("No socket owner specified."));
1917 if (IsOnShutDown() || thePrefs::IsUDPDisabled()) return;
1919 if (!IsRunning()) {
1920 if (event.GetSocketEvent() == wxSOCKET_INPUT) {
1921 // Back to the queue!
1922 theApp->AddPendingEvent(event);
1923 return;
1927 switch (event.GetSocketEvent()) {
1928 case wxSOCKET_INPUT:
1929 socket->OnReceive(0);
1930 break;
1932 case wxSOCKET_OUTPUT:
1933 socket->OnSend(0);
1934 break;
1936 case wxSOCKET_LOST:
1937 socket->OnDisconnected(0);
1938 break;
1940 default:
1941 wxFAIL;
1942 break;
1947 void CamuleApp::OnUnhandledException()
1949 // Call the generic exception-handler.
1950 fprintf(stderr, "\taMule Version: %s\n", (const char*)unicode2char(GetFullMuleVersion()));
1951 ::OnUnhandledException();
1954 void CamuleApp::StartKad()
1956 if (!Kademlia::CKademlia::IsRunning() && thePrefs::GetNetworkKademlia()) {
1957 // Kad makes no sense without the Client-UDP socket.
1958 if (!thePrefs::IsUDPDisabled()) {
1959 if (theApp->ipfilter->IsReady()) {
1960 Kademlia::CKademlia::Start();
1961 } else {
1962 theApp->ipfilter->StartKADWhenReady();
1964 } else {
1965 AddLogLineM(true,_("Kad network cannot be used if UDP port is disabled on preferences, not starting."));
1967 } else if (!thePrefs::GetNetworkKademlia()) {
1968 AddLogLineM(true,_("Kad network disabled on preferences, not connecting."));
1972 void CamuleApp::StopKad()
1974 // Stop Kad if it's running
1975 if (Kademlia::CKademlia::IsRunning()) {
1976 Kademlia::CKademlia::Stop();
1981 void CamuleApp::BootstrapKad(uint32 ip, uint16 port)
1983 if (!Kademlia::CKademlia::IsRunning()) {
1984 Kademlia::CKademlia::Start();
1985 theApp->ShowConnectionState();
1988 Kademlia::CKademlia::Bootstrap(ip, port, true);
1992 void CamuleApp::UpdateNotesDat(const wxString& url)
1994 wxString strTempFilename(theApp->ConfigDir + wxT("nodes.dat.download"));
1996 CHTTPDownloadThread *downloader = new CHTTPDownloadThread(url, strTempFilename, theApp->ConfigDir + wxT("nodes.dat"), HTTP_NodesDat);
1997 downloader->Create();
1998 downloader->Run();
2002 void CamuleApp::DisconnectED2K()
2004 // Stop ED2K if it's running
2005 if (IsConnectedED2K()) {
2006 serverconnect->Disconnect();
2010 bool CamuleApp::CryptoAvailable() const
2012 return clientcredits && clientcredits->CryptoAvailable();
2015 uint32 CamuleApp::GetED2KID() const {
2016 return serverconnect ? serverconnect->GetClientID() : 0;
2019 uint32 CamuleApp::GetID() const {
2020 uint32 ID;
2022 if( Kademlia::CKademlia::IsConnected() && !Kademlia::CKademlia::IsFirewalled() ) {
2023 // We trust Kad above ED2K
2024 ID = ENDIAN_NTOHL(Kademlia::CKademlia::GetIPAddress());
2025 } else if( theApp->serverconnect->IsConnected() ) {
2026 ID = theApp->serverconnect->GetClientID();
2027 } else if ( Kademlia::CKademlia::IsConnected() && Kademlia::CKademlia::IsFirewalled() ) {
2028 // A firewalled Kad client get's a "1"
2029 ID = 1;
2030 } else {
2031 ID = 0;
2034 return ID;
2037 DEFINE_LOCAL_EVENT_TYPE(wxEVT_CORE_FINISHED_HTTP_DOWNLOAD)
2038 DEFINE_LOCAL_EVENT_TYPE(wxEVT_CORE_SOURCE_DNS_DONE)
2039 DEFINE_LOCAL_EVENT_TYPE(wxEVT_CORE_UDP_DNS_DONE)
2040 DEFINE_LOCAL_EVENT_TYPE(wxEVT_CORE_SERVER_DNS_DONE)
2041 // File_checked_for_headers