Improved handling of crashes and assertions
[amule.git] / src / amule.cpp
blob60fab57af70cd4aac31070e7b2c16c7a665440a3
1 //
2 // This file is part of the aMule Project.
3 //
4 // Copyright (c) 2003-2011 aMule Team ( admin@amule.org / http://www.amule.org )
5 // Copyright (c) 2002-2011 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.
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 "PlatformSpecific.h" // Needed for PlatformSpecific::AllowSleepMode();
70 #include "Preferences.h" // Needed for CPreferences
71 #include "SearchList.h" // Needed for CSearchList
72 #include "Server.h" // Needed for GetListName
73 #include "ServerList.h" // Needed for CServerList
74 #include "ServerConnect.h" // Needed for CServerConnect
75 #include "ServerUDPSocket.h" // Needed for CServerUDPSocket
76 #include "Statistics.h" // Needed for CStatistics
77 #include "TerminationProcessAmuleweb.h" // Needed for CTerminationProcessAmuleweb
78 #include "ThreadTasks.h"
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 // Apprently needed for *BSD
212 SetResourceLimits();
214 #ifdef _MSC_VER
215 _CrtSetDbgFlag(0); // Disable useless memleak debugging
216 #endif
219 CamuleApp::~CamuleApp()
221 // Closing the log-file as the very last thing, since
222 // wxWidgets log-events are saved in it as well.
223 theLogger.CloseLogfile();
226 int CamuleApp::OnExit()
228 if (m_app_state!=APP_STATE_STARTING) {
229 AddLogLineNS(_("Now, exiting main app..."));
232 // From wxWidgets docs, wxConfigBase:
233 // ...
234 // Note that you must delete this object (usually in wxApp::OnExit)
235 // in order to avoid memory leaks, wxWidgets won't do it automatically.
237 // As it happens, you may even further simplify the procedure described
238 // above: you may forget about calling Set(). When Get() is called and
239 // there is no current object, it will create one using Create() function.
240 // To disable this behaviour DontCreateOnDemand() is provided.
241 delete wxConfigBase::Set((wxConfigBase *)NULL);
243 // Save credits
244 clientcredits->SaveList();
246 // Kill amuleweb if running
247 if (webserver_pid) {
248 AddLogLineNS(CFormat(_("Terminating amuleweb instance with pid '%ld' ... ")) % webserver_pid);
249 wxKillError rc;
250 if (wxKill(webserver_pid, wxSIGTERM, &rc) == -1) {
251 AddLogLineNS(CFormat(_("Killing amuleweb instance with pid '%ld' ... ")) % webserver_pid);
252 if (wxKill(webserver_pid, wxSIGKILL, &rc) == -1) {
253 AddLogLineNS(_("Failed"));
258 if (m_app_state!=APP_STATE_STARTING) {
259 AddLogLineNS(_("aMule OnExit: Terminating core."));
262 delete serverlist;
263 serverlist = NULL;
265 delete searchlist;
266 searchlist = NULL;
268 delete clientcredits;
269 clientcredits = NULL;
271 delete friendlist;
272 friendlist = NULL;
274 // Destroying CDownloadQueue calls destructor for CPartFile
275 // calling CSharedFileList::SafeAddKFile occasionally.
276 delete sharedfiles;
277 sharedfiles = NULL;
279 delete serverconnect;
280 serverconnect = NULL;
282 delete listensocket;
283 listensocket = NULL;
285 delete clientudp;
286 clientudp = NULL;
288 delete knownfiles;
289 knownfiles = NULL;
291 delete canceledfiles;
292 canceledfiles = NULL;
294 delete clientlist;
295 clientlist = NULL;
297 delete uploadqueue;
298 uploadqueue = NULL;
300 delete downloadqueue;
301 downloadqueue = NULL;
303 delete ipfilter;
304 ipfilter = NULL;
306 #ifdef ENABLE_UPNP
307 delete m_upnp;
308 m_upnp = NULL;
309 #endif
311 delete ECServerHandler;
312 ECServerHandler = NULL;
314 delete m_statistics;
315 m_statistics = NULL;
317 delete glob_prefs;
318 glob_prefs = NULL;
319 CPreferences::EraseItemList();
321 delete uploadBandwidthThrottler;
322 uploadBandwidthThrottler = NULL;
324 if (m_app_state!=APP_STATE_STARTING) {
325 AddLogLineNS(_("aMule shutdown completed."));
328 #if wxUSE_MEMORY_TRACING
329 AddLogLineNS(_("Memory debug results for aMule exit:"));
330 // Log mem debug mesages to wxLogStderr
331 wxLog* oldLog = wxLog::SetActiveTarget(new wxLogStderr);
332 //AddLogLineNS(wxT("**************Classes**************");
333 //wxDebugContext::PrintClasses();
334 //AddLogLineNS(wxT("***************Dump***************");
335 //wxDebugContext::Dump();
336 AddLogLineNS(wxT("***************Stats**************"));
337 wxDebugContext::PrintStatistics(true);
339 // Set back to wxLogGui
340 delete wxLog::SetActiveTarget(oldLog);
341 #endif
343 StopTickTimer();
345 // Return 0 for succesful program termination
346 return AMULE_APP_BASE::OnExit();
350 int CamuleApp::InitGui(bool, wxString &)
352 return 0;
357 // Application initialization
359 bool CamuleApp::OnInit()
361 #if wxUSE_MEMORY_TRACING
362 // any text before call of Localize_mule needs not to be translated.
363 AddLogLineNS(wxT("Checkpoint set on app init for memory debug")); // debug output
364 wxDebugContext::SetCheckpoint();
365 #endif
367 // Forward wxLog events to CLogger
368 wxLog::SetActiveTarget(new CLoggerTarget);
370 m_localip = StringHosttoUint32(::wxGetFullHostName());
372 #ifndef __WXMSW__
373 // get rid of sigpipe
374 signal(SIGPIPE, SIG_IGN);
375 #else
376 // Handle CTRL-Break
377 signal(SIGBREAK, OnShutdownSignal);
378 #endif
379 // Handle sigint and sigterm
380 signal(SIGINT, OnShutdownSignal);
381 signal(SIGTERM, OnShutdownSignal);
383 #ifdef __WXMAC__
384 // For listctrl's to behave on Mac
385 wxSystemOptions::SetOption(wxT("mac.listctrl.always_use_generic"), 1);
386 #endif
388 // Handle uncaught exceptions
389 InstallMuleExceptionHandler();
391 if (!InitCommon(AMULE_APP_BASE::argc, AMULE_APP_BASE::argv)) {
392 return false;
395 glob_prefs = new CPreferences();
397 CPath outDir;
398 if (CheckMuleDirectory(wxT("temp"), thePrefs::GetTempDir(), ConfigDir + wxT("Temp"), outDir)) {
399 thePrefs::SetTempDir(outDir);
400 } else {
401 return false;
404 if (CheckMuleDirectory(wxT("incoming"), thePrefs::GetIncomingDir(), ConfigDir + wxT("Incoming"), outDir)) {
405 thePrefs::SetIncomingDir(outDir);
406 } else {
407 return false;
410 // Some sanity check
411 if (!thePrefs::UseTrayIcon()) {
412 thePrefs::SetMinToTray(false);
415 // Build the filenames for the two OS files
416 SetOSFiles(thePrefs::GetOSDir().GetRaw());
418 #ifdef ENABLE_NLS
419 // Load localization settings
420 Localize_mule();
421 #endif
423 // Configure EC for amuled when invoked with ec-config
424 if (ec_config) {
425 AddLogLineNS(_("\nEC configuration"));
426 thePrefs::SetECPass(GetPassword());
427 thePrefs::EnableExternalConnections(true);
428 AddLogLineNS(_("Password set and external connections enabled."));
431 #ifndef __WXMSW__
432 if (getuid() == 0) {
433 wxString msg =
434 wxT("Warning! You are running aMule as root.\n")
435 wxT("Doing so is not recommended for security reasons,\n")
436 wxT("and you are advised to run aMule as an normal\n")
437 wxT("user instead.");
439 ShowAlert(msg, _("WARNING"), wxCENTRE | wxOK | wxICON_ERROR);
441 fprintf(stderr, "\n--------------------------------------------------\n");
442 fprintf(stderr, "%s", (const char*)unicode2UTF8(msg));
443 fprintf(stderr, "\n--------------------------------------------------\n\n");
445 #endif
447 // Display notification on new version or first run
448 wxTextFile vfile( ConfigDir + wxT("lastversion") );
449 wxString newMule(wxT( VERSION ));
451 if ( !wxFileExists( vfile.GetName() ) ) {
452 vfile.Create();
455 if ( vfile.Open() ) {
456 // Check if this version has been run before
457 bool found = false;
458 for ( size_t i = 0; i < vfile.GetLineCount(); i++ ) {
459 // Check if this version has been run before
460 if ( vfile.GetLine(i) == newMule ) {
461 found = true;
462 break;
466 // We havent run this version before?
467 if ( !found ) {
468 // Insert new at top to provide faster searches
469 vfile.InsertLine( newMule, 0 );
471 Trigger_New_version( newMule );
474 // Keep at most 10 entires
475 while ( vfile.GetLineCount() > 10 )
476 vfile.RemoveLine( vfile.GetLineCount() - 1 );
478 vfile.Write();
479 vfile.Close();
482 // Check if we have the old style locale config
483 wxString langId = thePrefs::GetLanguageID();
484 if (!langId.IsEmpty() && (langId.GetChar(0) >= '0' && langId.GetChar(0) <= '9')) {
485 wxString info(_("Your locale has been changed to System Default due to a configuration change. Sorry."));
486 thePrefs::SetLanguageID(wxLang2Str(wxLANGUAGE_DEFAULT));
487 ShowAlert(info, _("Info"), wxCENTRE | wxOK | wxICON_ERROR);
490 m_statistics = new CStatistics();
492 clientlist = new CClientList();
493 friendlist = new CFriendList();
494 searchlist = new CSearchList();
495 knownfiles = new CKnownFileList();
496 canceledfiles = new CCanceledFileList;
497 serverlist = new CServerList();
499 sharedfiles = new CSharedFileList(knownfiles);
500 clientcredits = new CClientCreditsList();
502 // bugfix - do this before creating the uploadqueue
503 downloadqueue = new CDownloadQueue();
504 uploadqueue = new CUploadQueue();
505 ipfilter = new CIPFilter();
507 // Creates all needed listening sockets
508 wxString msg;
509 if (!ReinitializeNetwork(&msg)) {
510 AddLogLineNS(wxT("\n"));
511 AddLogLineNS(msg);
514 // Test if there's any new version
515 if (thePrefs::GetCheckNewVersion()) {
516 // We use the thread base because I don't want a dialog to pop up.
517 CHTTPDownloadThread* version_check =
518 new CHTTPDownloadThread(wxT("http://amule.sourceforge.net/lastversion"),
519 theApp->ConfigDir + wxT("last_version_check"), theApp->ConfigDir + wxT("last_version"), HTTP_VersionCheck, false, false);
520 version_check->Create();
521 version_check->Run();
524 // Create main dialog, or fork to background (daemon).
525 InitGui(m_geometryEnabled, m_geometryString);
527 #ifdef AMULE_DAEMON
528 // Need to refresh wxSingleInstanceChecker after the daemon fork() !
529 if (enable_daemon_fork) {
530 RefreshSingleInstanceChecker();
531 // No need to check IsAnotherRunning() - we've done it before.
533 #endif
535 // Has to be created after the call to InitGui, as fork
536 // (when using posix threads) only replicates the mainthread,
537 // and the UBT constructor creates a thread.
538 uploadBandwidthThrottler = new UploadBandwidthThrottler();
540 // Start performing background tasks
541 // This will start loading the IP filter. It will start right away.
542 // Log is confusing, because log entries from background will only be printed
543 // once foreground becomes idle, and that will only be after loading
544 // of the partfiles has finished.
545 CThreadScheduler::Start();
547 // These must be initialized after the gui is loaded.
548 if (thePrefs::GetNetworkED2K()) {
549 serverlist->Init();
551 downloadqueue->LoadMetFiles(thePrefs::GetTempDir());
552 sharedfiles->Reload();
554 // Ensure that the up/down ratio is used
555 CPreferences::CheckUlDlRatio();
557 // Load saved friendlist (now, so it can update in GUI right away)
558 friendlist->LoadList();
560 // The user can start pressing buttons like mad if he feels like it.
561 m_app_state = APP_STATE_RUNNING;
563 if (!serverlist->GetServerCount() && thePrefs::GetNetworkED2K()) {
564 // There are no servers and ED2K active -> ask for download.
565 // As we cannot ask in amuled, we just update there
566 // Kry TODO: Store server.met URL on preferences and use it here and in GUI.
567 #ifndef AMULE_DAEMON
568 if (wxYES == wxMessageBox(
569 wxString(
570 _("You don't have any server in the server list.\nDo you want aMule to download a new list now?")),
571 wxString(_("Server list download")),
572 wxYES_NO,
573 static_cast<wxWindow*>(theApp->amuledlg)))
574 #endif
576 // workaround amuled crash
577 #ifndef AMULE_DAEMON
578 serverlist->UpdateServerMetFromURL(
579 wxT("http://gruk.org/server.met.gz"));
580 #endif
585 // Autoconnect if that option is enabled
586 if (thePrefs::DoAutoConnect()) {
587 // IP filter is still loading and will be finished on event.
588 // Tell it to autoconnect.
589 if (thePrefs::GetNetworkED2K()) {
590 ipfilter->ConnectToAnyServerWhenReady();
592 if (thePrefs::GetNetworkKademlia()) {
593 ipfilter->StartKADWhenReady();
597 // Enable GeoIP
598 #ifdef ENABLE_IP2COUNTRY
599 theApp->amuledlg->EnableIP2Country();
600 #endif
602 // Run webserver?
603 if (thePrefs::GetWSIsEnabled()) {
604 wxString aMuleConfigFile = ConfigDir + m_configFile;
605 wxString amulewebPath = thePrefs::GetWSPath();
607 #if defined(__WXMAC__) && !defined(AMULE_DAEMON)
608 // For the Mac GUI application, look for amuleweb in the bundle
609 CFURLRef amulewebUrl = CFBundleCopyAuxiliaryExecutableURL(
610 CFBundleGetMainBundle(), CFSTR("amuleweb"));
612 if (amulewebUrl) {
613 CFURLRef absoluteUrl = CFURLCopyAbsoluteURL(amulewebUrl);
614 CFRelease(amulewebUrl);
616 if (absoluteUrl) {
617 CFStringRef amulewebCfstr = CFURLCopyFileSystemPath(absoluteUrl, kCFURLPOSIXPathStyle);
618 CFRelease(absoluteUrl);
619 #if wxCHECK_VERSION(2, 9, 0)
620 amulewebPath = wxCFStringRef(amulewebCfstr).AsString(wxLocale::GetSystemEncoding());
621 #else
622 amulewebPath = wxMacCFStringHolder(amulewebCfstr).AsString(wxLocale::GetSystemEncoding());
623 #endif
626 #endif
628 #ifdef __WXMSW__
629 # define QUOTE wxT("\"")
630 #else
631 # define QUOTE wxT("\'")
632 #endif
634 wxString cmd =
635 QUOTE +
636 amulewebPath +
637 QUOTE wxT(" ") QUOTE wxT("--amule-config-file=") +
638 aMuleConfigFile +
639 QUOTE;
640 CTerminationProcessAmuleweb *p = new CTerminationProcessAmuleweb(cmd, &webserver_pid);
641 webserver_pid = wxExecute(cmd, wxEXEC_ASYNC, p);
642 bool webserver_ok = webserver_pid > 0;
643 if (webserver_ok) {
644 AddLogLineC(CFormat(_("web server running on pid %d")) % webserver_pid);
645 } else {
646 delete p;
647 ShowAlert(_(
648 "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"),
649 _("ERROR"), wxOK | wxICON_ERROR);
653 return true;
656 bool CamuleApp::ReinitializeNetwork(wxString* msg)
658 bool ok = true;
659 static bool firstTime = true;
661 if (!firstTime) {
662 // TODO: Destroy previously created sockets
664 firstTime = false;
666 // Some sanity checks first
667 if (thePrefs::ECPort() == thePrefs::GetPort()) {
668 // Select a random usable port in the range 1025 ... 2^16 - 1
669 uint16 port = thePrefs::ECPort();
670 while ( port < 1024 || port == thePrefs::GetPort() ) {
671 port = (uint16)rand();
673 thePrefs::SetECPort( port );
675 wxString err =
676 wxT("Network configuration failed! You cannot use the same port\n")
677 wxT("for the main TCP port and the External Connections port.\n")
678 wxT("The EC port has been changed to avoid conflict, see the\n")
679 wxT("preferences for the new value.\n");
680 *msg << err;
682 AddLogLineN(wxEmptyString );
683 AddLogLineC(err );
684 AddLogLineN(wxEmptyString );
686 ok = false;
689 if (thePrefs::GetUDPPort() == thePrefs::GetPort() + 3) {
690 // Select a random usable value in the range 1025 ... 2^16 - 1
691 uint16 port = thePrefs::GetUDPPort();
692 while ( port < 1024 || port == thePrefs::GetPort() + 3 ) {
693 port = (uint16)rand();
695 thePrefs::SetUDPPort( port );
697 wxString err =
698 wxT("Network configuration failed! You set your UDP port to\n")
699 wxT("the value of the main TCP port plus 3.\n")
700 wxT("This port has been reserved for the Server-UDP port. The\n")
701 wxT("port value has been changed to avoid conflict, see the\n")
702 wxT("preferences for the new value\n");
703 *msg << err;
705 AddLogLineN(wxEmptyString );
706 AddLogLineC(err );
707 AddLogLineN(wxEmptyString );
709 ok = false;
712 // Create the address where we are going to listen
713 // TODO: read this from configuration file
714 amuleIPV4Address myaddr[4];
716 // Create the External Connections Socket.
717 // Default is 4712.
718 // Get ready to handle connections from apps like amulecmd
719 if (thePrefs::GetECAddress().IsEmpty() || !myaddr[0].Hostname(thePrefs::GetECAddress())) {
720 myaddr[0].AnyAddress();
722 myaddr[0].Service(thePrefs::ECPort());
723 ECServerHandler = new ExternalConn(myaddr[0], msg);
725 // Create the UDP socket TCP+3.
726 // Used for source asking on servers.
727 if (thePrefs::GetAddress().IsEmpty()) {
728 myaddr[1].AnyAddress();
729 } else if (!myaddr[1].Hostname(thePrefs::GetAddress())) {
730 myaddr[1].AnyAddress();
731 AddLogLineC(CFormat(_("Could not bind ports to the specified address: %s"))
732 % thePrefs::GetAddress());
735 wxString ip = myaddr[1].IPAddress();
736 myaddr[1].Service(thePrefs::GetPort()+3);
737 serverconnect = new CServerConnect(serverlist, myaddr[1]);
738 *msg << CFormat( wxT("*** Server UDP socket (TCP+3) at %s:%u\n") )
739 % ip % ((unsigned int)thePrefs::GetPort() + 3u);
741 // Create the ListenSocket (aMule TCP socket).
742 // Used for Client Port / Connections from other clients,
743 // Client to Client Source Exchange.
744 // Default is 4662.
745 myaddr[2] = myaddr[1];
746 myaddr[2].Service(thePrefs::GetPort());
747 listensocket = new CListenSocket(myaddr[2]);
748 *msg << CFormat( wxT("*** TCP socket (TCP) listening on %s:%u\n") )
749 % ip % (unsigned int)(thePrefs::GetPort());
750 // This command just sets a flag to control maximum number of connections.
751 // Notify(true) has already been called to the ListenSocket, so events may
752 // be already comming in.
753 if (listensocket->IsOk()) {
754 listensocket->StartListening();
755 } else {
756 // If we wern't able to start listening, we need to warn the user
757 wxString err;
758 err = CFormat(_("Port %u is not available. You will be LOWID\n")) %
759 (unsigned int)(thePrefs::GetPort());
760 *msg << err;
761 AddLogLineC(err);
762 err.Clear();
763 err = CFormat(
764 _("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.")) %
765 (unsigned int)(thePrefs::GetPort());
766 ShowAlert(err, _("ERROR"), wxOK | wxICON_ERROR);
769 // Create the UDP socket.
770 // Used for extended eMule protocol, Queue Rating, File Reask Ping.
771 // Also used for Kademlia.
772 // Default is port 4672.
773 myaddr[3] = myaddr[1];
774 myaddr[3].Service(thePrefs::GetUDPPort());
775 clientudp = new CClientUDPSocket(myaddr[3], thePrefs::GetProxyData());
776 if (!thePrefs::IsUDPDisabled()) {
777 *msg << CFormat( wxT("*** Client UDP socket (extended eMule) at %s:%u") )
778 % ip % thePrefs::GetUDPPort();
779 } else {
780 *msg << wxT("*** Client UDP socket (extended eMule) disabled on preferences");
783 #ifdef ENABLE_UPNP
784 if (thePrefs::GetUPnPEnabled()) {
785 try {
786 m_upnpMappings[0] = CUPnPPortMapping(
787 myaddr[0].Service(),
788 "TCP",
789 thePrefs::GetUPnPECEnabled(),
790 "aMule TCP External Connections Socket");
791 m_upnpMappings[1] = CUPnPPortMapping(
792 myaddr[1].Service(),
793 "UDP",
794 thePrefs::GetUPnPEnabled(),
795 "aMule UDP socket (TCP+3)");
796 m_upnpMappings[2] = CUPnPPortMapping(
797 myaddr[2].Service(),
798 "TCP",
799 thePrefs::GetUPnPEnabled(),
800 "aMule TCP Listen Socket");
801 m_upnpMappings[3] = CUPnPPortMapping(
802 myaddr[3].Service(),
803 "UDP",
804 thePrefs::GetUPnPEnabled(),
805 "aMule UDP Extended eMule Socket");
806 m_upnp = new CUPnPControlPoint(thePrefs::GetUPnPTCPPort());
807 m_upnp->AddPortMappings(m_upnpMappings);
808 } catch(CUPnPException &e) {
809 wxString error_msg;
810 error_msg << e.what();
811 AddLogLineC(error_msg);
812 fprintf(stderr, "%s\n", (const char *)unicode2char(error_msg));
815 #endif
817 return ok;
820 /* Original implementation by Bouc7 of the eMule Project.
821 aMule Signature idea was designed by BigBob and implemented
822 by Un-Thesis, with design inputs and suggestions from bothie.
824 void CamuleApp::OnlineSig(bool zero /* reset stats (used on shutdown) */)
826 // Do not do anything if online signature is disabled in Preferences
827 if (!thePrefs::IsOnlineSignatureEnabled() || m_emulesig_path.IsEmpty()) {
828 // We do not need to check m_amulesig_path because if m_emulesig_path is empty,
829 // that means m_amulesig_path is empty too.
830 return;
833 // Remove old signature files
834 if ( wxFileExists( m_emulesig_path ) ) { wxRemoveFile( m_emulesig_path ); }
835 if ( wxFileExists( m_amulesig_path ) ) { wxRemoveFile( m_amulesig_path ); }
838 wxTextFile amulesig_out;
839 wxTextFile emulesig_out;
841 // Open both files if needed
842 if ( !emulesig_out.Create( m_emulesig_path) ) {
843 AddLogLineC(_("Failed to create OnlineSig File"));
844 // Will never try again.
845 m_amulesig_path.Clear();
846 m_emulesig_path.Clear();
847 return;
850 if ( !amulesig_out.Create(m_amulesig_path) ) {
851 AddLogLineC(_("Failed to create aMule OnlineSig File"));
852 // Will never try again.
853 m_amulesig_path.Clear();
854 m_emulesig_path.Clear();
855 return;
858 wxString emulesig_string;
859 wxString temp;
861 if (zero) {
862 emulesig_string = wxT("0\xA0.0|0.0|0");
863 amulesig_out.AddLine(wxT("0\n0\n0\n0\n0\n0\n0.0\n0.0\n0\n0"));
864 } else {
865 if (IsConnectedED2K()) {
867 temp = CFormat(wxT("%d")) % serverconnect->GetCurrentServer()->GetPort();
869 // We are online
870 emulesig_string =
871 // Connected
872 wxT("1|")
873 //Server name
874 + serverconnect->GetCurrentServer()->GetListName()
875 + wxT("|")
876 // IP and port of the server
877 + serverconnect->GetCurrentServer()->GetFullIP()
878 + wxT("|")
879 + temp;
882 // Now for amule sig
884 // Connected. State 1, full info
885 amulesig_out.AddLine(wxT("1"));
886 // Server Name
887 amulesig_out.AddLine(serverconnect->GetCurrentServer()->GetListName());
888 // Server IP
889 amulesig_out.AddLine(serverconnect->GetCurrentServer()->GetFullIP());
890 // Server Port
891 amulesig_out.AddLine(temp);
893 if (serverconnect->IsLowID()) {
894 amulesig_out.AddLine(wxT("L"));
895 } else {
896 amulesig_out.AddLine(wxT("H"));
899 } else if (serverconnect->IsConnecting()) {
900 emulesig_string = wxT("0");
902 // Connecting. State 2, No info.
903 amulesig_out.AddLine(wxT("2\n0\n0\n0\n0"));
904 } else {
905 // Not connected to a server
906 emulesig_string = wxT("0");
908 // Not connected, state 0, no info
909 amulesig_out.AddLine(wxT("0\n0\n0\n0\n0"));
911 if (IsConnectedKad()) {
912 if(Kademlia::CKademlia::IsFirewalled()) {
913 // Connected. Firewalled. State 1.
914 amulesig_out.AddLine(wxT("1"));
915 } else {
916 // Connected. State 2.
917 amulesig_out.AddLine(wxT("2"));
919 } else {
920 // Not connected.State 0.
921 amulesig_out.AddLine(wxT("0"));
923 emulesig_string += wxT("\xA");
925 // Datarate for downloads
926 temp = CFormat(wxT("%.1f")) % (theStats::GetDownloadRate() / 1024.0);
928 emulesig_string += temp + wxT("|");
929 amulesig_out.AddLine(temp);
931 // Datarate for uploads
932 temp = CFormat(wxT("%.1f")) % (theStats::GetUploadRate() / 1024.0);
934 emulesig_string += temp + wxT("|");
935 amulesig_out.AddLine(temp);
937 // Number of users waiting for upload
938 temp = CFormat(wxT("%d")) % theStats::GetWaitingUserCount();
940 emulesig_string += temp;
941 amulesig_out.AddLine(temp);
943 // Number of shared files (not on eMule)
944 amulesig_out.AddLine(CFormat(wxT("%d")) % theStats::GetSharedFileCount());
947 // eMule signature finished here. Write the line to the wxTextFile.
948 emulesig_out.AddLine(emulesig_string);
950 // Now for aMule signature extras
952 // Nick on the network
953 amulesig_out.AddLine(thePrefs::GetUserNick());
955 // Total received in bytes
956 amulesig_out.AddLine(CFormat(wxT("%llu")) % theStats::GetTotalReceivedBytes());
958 // Total sent in bytes
959 amulesig_out.AddLine(CFormat(wxT("%llu")) % theStats::GetTotalSentBytes());
961 // amule version
962 #ifdef SVNDATE
963 amulesig_out.AddLine(wxT(VERSION) wxT(" ") wxT(SVNDATE));
964 #else
965 amulesig_out.AddLine(wxT(VERSION));
966 #endif
968 if (zero) {
969 amulesig_out.AddLine(wxT("0"));
970 amulesig_out.AddLine(wxT("0"));
971 amulesig_out.AddLine(wxT("0"));
972 } else {
973 // Total received bytes in session
974 amulesig_out.AddLine( CFormat( wxT("%llu") ) %
975 theStats::GetSessionReceivedBytes() );
977 // Total sent bytes in session
978 amulesig_out.AddLine( CFormat( wxT("%llu") ) %
979 theStats::GetSessionSentBytes() );
981 // Uptime
982 amulesig_out.AddLine(CFormat(wxT("%llu")) % theStats::GetUptimeSeconds());
985 // Flush the files
986 emulesig_out.Write();
987 amulesig_out.Write();
988 } //End Added By Bouc7
991 #if wxUSE_ON_FATAL_EXCEPTION
992 // Gracefully handle fatal exceptions and print backtrace if possible
993 void CamuleApp::OnFatalException()
995 /* Print the backtrace */
996 wxString msg;
997 msg << wxT("\n--------------------------------------------------------------------------------\n")
998 << wxT("A fatal error has occurred and aMule has crashed.\n")
999 << wxT("Please assist us in fixing this problem by posting the backtrace below in our\n")
1000 << wxT("'aMule Crashes' forum and include as much information as possible regarding the\n")
1001 << wxT("circumstances of this crash. The forum is located here:\n")
1002 << wxT(" http://forum.amule.org/index.php?board=67.0\n")
1003 << wxT("If possible, please try to generate a real backtrace of this crash:\n")
1004 << wxT(" http://wiki.amule.org/index.php/Backtraces\n\n")
1005 << wxT("----------------------------=| BACKTRACE FOLLOWS: |=----------------------------\n")
1006 << wxT("Current version is: ") << FullMuleVersion
1007 << wxT("\nRunning on: ") << OSDescription
1008 << wxT("\n\n")
1009 << get_backtrace(1) // 1 == skip this function.
1010 << wxT("\n--------------------------------------------------------------------------------\n");
1012 theLogger.EmergencyLog(msg, true);
1014 #endif
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 AddLogLineN(_("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 wxString errmsg = CFormat( wxT("Assertion failed: %s:%s:%d: Assertion '%s' failed. %s\nBacktrace follows:\n%s\n") )
1076 % file % func % line % cond % ( msg ? msg : wxT("") )
1077 % get_backtrace(2); // Skip the function-calls directly related to the assert call.
1078 theLogger.EmergencyLog(errmsg, false);
1080 if (wxThread::IsMain() && IsRunning()) {
1081 AMULE_APP_BASE::OnAssertFailure(file, line, func, cond, msg);
1082 } else {
1083 #ifdef _MSC_VER
1084 wxString s = CFormat(wxT("%s in %s")) % cond % func;
1085 if (msg) {
1086 s << wxT(" : ") << msg;
1088 _wassert(s, file, line);
1089 #else
1090 // Abort, allows gdb to catch the assertion
1091 raise( SIGABRT );
1092 #endif
1095 #endif
1098 void CamuleApp::OnUDPDnsDone(CMuleInternalEvent& evt)
1100 CServerUDPSocket* socket =(CServerUDPSocket*)evt.GetClientData();
1101 socket->OnHostnameResolved(evt.GetExtraLong());
1105 void CamuleApp::OnSourceDnsDone(CMuleInternalEvent& evt)
1107 downloadqueue->OnHostnameResolved(evt.GetExtraLong());
1111 void CamuleApp::OnServerDnsDone(CMuleInternalEvent& evt)
1113 AddLogLineNS(_("Server hostname notified"));
1114 serverconnect->OnServerHostnameResolved(evt.GetClientData(), evt.GetExtraLong());
1118 void CamuleApp::OnTCPTimer(CTimerEvent& WXUNUSED(evt))
1120 if(!IsRunning()) {
1121 return;
1123 serverconnect->StopConnectionTry();
1124 if (IsConnectedED2K() ) {
1125 return;
1127 serverconnect->ConnectToAnyServer();
1131 void CamuleApp::OnCoreTimer(CTimerEvent& WXUNUSED(evt))
1133 // Former TimerProc section
1134 static uint64 msPrev1, msPrev5, msPrevSave, msPrevHist, msPrevOS, msPrevKnownMet;
1135 uint64 msCur = theStats::GetUptimeMillis();
1136 TheTime = msCur / 1000;
1138 if (!IsRunning()) {
1139 return;
1142 #ifndef AMULE_DAEMON
1143 // Check if we should terminate the app
1144 if ( g_shutdownSignal ) {
1145 wxWindow* top = GetTopWindow();
1147 if ( top ) {
1148 top->Close(true);
1149 } else {
1150 // No top-window, have to force termination.
1151 wxExit();
1154 #endif
1156 // There is a theoretical chance that the core time function can recurse:
1157 // if an event function gets blocked on a mutex (communicating with the
1158 // UploadBandwidthThrottler) wx spawns a new event loop and processes more events.
1159 // If CPU load gets high a new core timer event could be generated before the last
1160 // one was finished and so recursion could occur, which would be bad.
1161 // Detect this and do an early return then.
1162 static bool recurse = false;
1163 if (recurse) {
1164 return;
1166 recurse = true;
1168 uploadqueue->Process();
1169 downloadqueue->Process();
1170 //theApp->clientcredits->Process();
1171 theStats::CalculateRates();
1173 if (msCur-msPrevHist > 1000) {
1174 // unlike the other loop counters in this function this one will sometimes
1175 // produce two calls in quick succession (if there was a gap of more than one
1176 // second between calls to TimerProc) - this is intentional! This way the
1177 // history list keeps an average of one node per second and gets thinned out
1178 // correctly as time progresses.
1179 msPrevHist += 1000;
1181 m_statistics->RecordHistory();
1186 if (msCur-msPrev1 > 1000) { // approximately every second
1187 msPrev1 = msCur;
1188 clientcredits->Process();
1189 clientlist->Process();
1191 // Publish files to server if needed.
1192 sharedfiles->Process();
1194 if( Kademlia::CKademlia::IsRunning() ) {
1195 Kademlia::CKademlia::Process();
1196 if(Kademlia::CKademlia::GetPrefs()->HasLostConnection()) {
1197 StopKad();
1198 clientudp->Close();
1199 clientudp->Open();
1200 if (thePrefs::Reconnect()) {
1201 StartKad();
1206 if( serverconnect->IsConnecting() && !serverconnect->IsSingleConnect() ) {
1207 serverconnect->TryAnotherConnectionrequest();
1209 if (serverconnect->IsConnecting()) {
1210 serverconnect->CheckForTimeout();
1212 listensocket->UpdateConnectionsStatus();
1217 if (msCur-msPrev5 > 5000) { // every 5 seconds
1218 msPrev5 = msCur;
1219 listensocket->Process();
1222 if (msCur-msPrevSave >= 60000) {
1223 msPrevSave = msCur;
1224 theStats::Save();
1227 // Special
1228 if (msCur - msPrevOS >= thePrefs::GetOSUpdate() * 1000ull) {
1229 OnlineSig(); // Added By Bouc7
1230 msPrevOS = msCur;
1233 if (msCur - msPrevKnownMet >= 30*60*1000/*There must be a prefs option for this*/) {
1234 // Save Shared Files data
1235 knownfiles->Save();
1236 msPrevKnownMet = msCur;
1240 // Recomended by lugdunummaster himself - from emule 0.30c
1241 serverconnect->KeepConnectionAlive();
1243 // Disarm recursion protection
1244 recurse = false;
1248 void CamuleApp::OnFinishedHashing(CHashingEvent& evt)
1250 wxCHECK_RET(evt.GetResult(), wxT("No result of hashing"));
1252 CKnownFile* owner = const_cast<CKnownFile*>(evt.GetOwner());
1253 CKnownFile* result = evt.GetResult();
1255 if (owner) {
1256 // Check if the partfile still exists, as it might have
1257 // been deleted in the mean time.
1258 if (downloadqueue->IsPartFile(owner)) {
1259 // This cast must not be done before the IsPartFile
1260 // call, as dynamic_cast will barf on dangling pointers.
1261 dynamic_cast<CPartFile*>(owner)->PartFileHashFinished(result);
1263 } else {
1264 static uint64 bytecount = 0;
1266 if (knownfiles->SafeAddKFile(result, true)) {
1267 AddDebugLogLineN(logKnownFiles,
1268 CFormat(wxT("Safe adding file to sharedlist: %s")) % result->GetFileName());
1269 sharedfiles->SafeAddKFile(result);
1271 bytecount += result->GetFileSize();
1272 // If we have added files with a total size of ~300mb
1273 if (bytecount >= 314572800) {
1274 AddDebugLogLineN(logKnownFiles, wxT("Failsafe for crash on file hashing creation"));
1275 if ( m_app_state != APP_STATE_SHUTTINGDOWN ) {
1276 knownfiles->Save();
1277 bytecount = 0;
1280 } else {
1281 AddDebugLogLineN(logKnownFiles,
1282 CFormat(wxT("File not added to sharedlist: %s")) % result->GetFileName());
1283 delete result;
1289 void CamuleApp::OnFinishedAICHHashing(CHashingEvent& evt)
1291 wxCHECK_RET(evt.GetResult(), wxT("No result of AICH-hashing"));
1293 CKnownFile* owner = const_cast<CKnownFile*>(evt.GetOwner());
1294 CScopedPtr<CKnownFile> result(evt.GetResult());
1296 if (result->GetAICHHashset()->GetStatus() == AICH_HASHSETCOMPLETE) {
1297 CAICHHashSet* oldSet = owner->GetAICHHashset();
1298 CAICHHashSet* newSet = result->GetAICHHashset();
1300 owner->SetAICHHashset(newSet);
1301 newSet->SetOwner(owner);
1303 result->SetAICHHashset(oldSet);
1304 oldSet->SetOwner(result.get());
1309 void CamuleApp::OnFinishedCompletion(CCompletionEvent& evt)
1311 CPartFile* completed = const_cast<CPartFile*>(evt.GetOwner());
1312 wxCHECK_RET(completed, wxT("Completion event sent for unspecified file"));
1313 wxASSERT_MSG(downloadqueue->IsPartFile(completed), wxT("CCompletionEvent for unknown partfile."));
1315 completed->CompleteFileEnded(evt.ErrorOccured(), evt.GetFullPath());
1316 if (evt.ErrorOccured()) {
1317 CUserEvents::ProcessEvent(CUserEvents::ErrorOnCompletion, completed);
1320 // Check if we should execute an script/app/whatever.
1321 CUserEvents::ProcessEvent(CUserEvents::DownloadCompleted, completed);
1324 void CamuleApp::OnFinishedAllocation(CAllocFinishedEvent& evt)
1326 CPartFile *file = evt.GetFile();
1327 wxCHECK_RET(file, wxT("Allocation finished event sent for unspecified file"));
1328 wxASSERT_MSG(downloadqueue->IsPartFile(file), wxT("CAllocFinishedEvent for unknown partfile"));
1330 file->SetStatus(PS_EMPTY);
1332 if (evt.Succeeded()) {
1333 if (evt.IsPaused()) {
1334 file->StopFile();
1335 } else {
1336 file->ResumeFile();
1338 } else {
1339 AddLogLineN(CFormat(_("Disk space preallocation for file '%s' failed: %s")) % file->GetFileName() % wxString(UTF82unicode(std::strerror(evt.GetResult()))));
1340 file->StopFile();
1343 file->AllocationFinished();
1346 void CamuleApp::OnNotifyEvent(CMuleGUIEvent& evt)
1348 #ifdef AMULE_DAEMON
1349 evt.Notify();
1350 #else
1351 if (theApp->amuledlg) {
1352 evt.Notify();
1354 #endif
1358 void CamuleApp::ShutDown()
1360 // Just in case
1361 PlatformSpecific::AllowSleepMode();
1363 // Log
1364 AddDebugLogLineN(logGeneral, wxT("CamuleApp::ShutDown() has started."));
1366 // Signal the hashing thread to terminate
1367 m_app_state = APP_STATE_SHUTTINGDOWN;
1369 StopKad();
1371 // Kry - Save the sources seeds on app exit
1372 if (thePrefs::GetSrcSeedsOn()) {
1373 downloadqueue->SaveSourceSeeds();
1376 OnlineSig(true); // Added By Bouc7
1378 // Exit HTTP downloads
1379 CHTTPDownloadThread::StopAll();
1381 // Exit thread scheduler and upload thread
1382 CThreadScheduler::Terminate();
1384 AddDebugLogLineN(logGeneral, wxT("Terminate upload thread."));
1385 uploadBandwidthThrottler->EndThread();
1387 // Close sockets to avoid new clients coming in
1388 if (listensocket) {
1389 listensocket->Close();
1390 listensocket->KillAllSockets();
1393 if (serverconnect) {
1394 serverconnect->Disconnect();
1397 ECServerHandler->KillAllSockets();
1399 #ifdef ENABLE_UPNP
1400 if (thePrefs::GetUPnPEnabled()) {
1401 if (m_upnp) {
1402 m_upnp->DeletePortMappings(m_upnpMappings);
1405 #endif
1407 // saving data & stuff
1408 if (knownfiles) {
1409 knownfiles->Save();
1412 theStats::Save();
1414 CPath configFileName = CPath(ConfigDir + m_configFile);
1415 CPath::BackupFile(configFileName, wxT(".bak"));
1417 if (clientlist) {
1418 clientlist->DeleteAll();
1421 // Log
1422 AddDebugLogLineN(logGeneral, wxT("CamuleApp::ShutDown() has ended."));
1426 bool CamuleApp::AddServer(CServer *srv, bool fromUser)
1428 if ( serverlist->AddServer(srv, fromUser) ) {
1429 Notify_ServerAdd(srv);
1430 return true;
1432 return false;
1436 uint32 CamuleApp::GetPublicIP(bool ignorelocal) const
1438 if (m_dwPublicIP == 0) {
1439 if (Kademlia::CKademlia::IsConnected() && Kademlia::CKademlia::GetIPAddress() ) {
1440 return wxUINT32_SWAP_ALWAYS(Kademlia::CKademlia::GetIPAddress());
1441 } else {
1442 return ignorelocal ? 0 : m_localip;
1446 return m_dwPublicIP;
1450 void CamuleApp::SetPublicIP(const uint32 dwIP)
1452 wxASSERT((dwIP == 0) || !IsLowID(dwIP));
1454 if (dwIP != 0 && dwIP != m_dwPublicIP && serverlist != NULL) {
1455 m_dwPublicIP = dwIP;
1456 serverlist->CheckForExpiredUDPKeys();
1457 } else {
1458 m_dwPublicIP = dwIP;
1463 wxString CamuleApp::GetLog(bool reset)
1465 wxFile logfile;
1466 logfile.Open(ConfigDir + wxT("logfile"));
1467 if ( !logfile.IsOpened() ) {
1468 return _("ERROR: can't open logfile");
1470 int len = logfile.Length();
1471 if ( len == 0 ) {
1472 return _("WARNING: logfile is empty. Something is wrong.");
1474 char *tmp_buffer = new char[len + sizeof(wxChar)];
1475 logfile.Read(tmp_buffer, len);
1476 memset(tmp_buffer + len, 0, sizeof(wxChar));
1478 // try to guess file format
1479 wxString str;
1480 if (tmp_buffer[0] && tmp_buffer[1]) {
1481 str = wxString(UTF82unicode(tmp_buffer));
1482 } else {
1483 str = wxWCharBuffer((wchar_t *)tmp_buffer);
1486 delete [] tmp_buffer;
1487 if ( reset ) {
1488 theLogger.CloseLogfile();
1489 if (theLogger.OpenLogfile(ConfigDir + wxT("logfile"))) {
1490 AddLogLineN(_("Log has been reset"));
1492 ECServerHandler->ResetAllLogs();
1494 return str;
1498 wxString CamuleApp::GetServerLog(bool reset)
1500 wxString ret = server_msg;
1501 if ( reset ) {
1502 server_msg.Clear();
1504 return ret;
1507 wxString CamuleApp::GetDebugLog(bool reset)
1509 return GetLog(reset);
1513 void CamuleApp::AddServerMessageLine(wxString &msg)
1515 server_msg += msg + wxT("\n");
1516 AddLogLineN(CFormat(_("ServerMessage: %s")) % msg);
1521 void CamuleApp::OnFinishedHTTPDownload(CMuleInternalEvent& event)
1523 switch (event.GetInt()) {
1524 case HTTP_IPFilter:
1525 ipfilter->DownloadFinished(event.GetExtraLong());
1526 break;
1527 case HTTP_ServerMet:
1528 serverlist->DownloadFinished(event.GetExtraLong());
1529 break;
1530 case HTTP_ServerMetAuto:
1531 serverlist->AutoDownloadFinished(event.GetExtraLong());
1532 break;
1533 case HTTP_VersionCheck:
1534 CheckNewVersion(event.GetExtraLong());
1535 break;
1536 case HTTP_NodesDat:
1537 if (event.GetExtraLong() == HTTP_Success) {
1539 wxString file = ConfigDir + wxT("nodes.dat");
1540 if (wxFileExists(file)) {
1541 wxRemoveFile(file);
1544 if ( Kademlia::CKademlia::IsRunning() ) {
1545 Kademlia::CKademlia::Stop();
1548 wxRenameFile(file + wxT(".download"),file);
1550 Kademlia::CKademlia::Start();
1551 theApp->ShowConnectionState();
1553 } else if (event.GetExtraLong() == HTTP_Skipped) {
1554 AddLogLineN(CFormat(_("Skipped download of %s, because requested file is not newer.")) % wxT("nodes.dat"));
1555 } else {
1556 AddLogLineC(_("Failed to download the nodes list."));
1558 break;
1559 #ifdef ENABLE_IP2COUNTRY
1560 case HTTP_GeoIP:
1561 theApp->amuledlg->IP2CountryDownloadFinished(event.GetExtraLong());
1562 // If we updated, the dialog is already up. Redraw it to show the flags.
1563 theApp->amuledlg->Refresh();
1564 break;
1565 #endif
1569 void CamuleApp::CheckNewVersion(uint32 result)
1571 if (result == HTTP_Success) {
1572 wxString filename = ConfigDir + wxT("last_version_check");
1573 wxTextFile file;
1575 if (!file.Open(filename)) {
1576 AddLogLineC(_("Failed to open the downloaded version check file") );
1577 return;
1578 } else if (!file.GetLineCount()) {
1579 AddLogLineC(_("Corrupted version check file"));
1580 } else {
1581 wxString versionLine = file.GetFirstLine();
1582 wxStringTokenizer tkz(versionLine, wxT("."));
1584 AddDebugLogLineN(logGeneral, wxString(wxT("Running: ")) + wxT(VERSION) + wxT(", Version check: ") + versionLine);
1586 long fields[] = {0, 0, 0};
1587 for (int i = 0; i < 3; ++i) {
1588 if (!tkz.HasMoreTokens()) {
1589 AddLogLineC(_("Corrupted version check file"));
1590 return;
1591 } else {
1592 wxString token = tkz.GetNextToken();
1594 if (!token.ToLong(&fields[i])) {
1595 AddLogLineC(_("Corrupted version check file"));
1596 return;
1601 long curVer = make_full_ed2k_version(VERSION_MJR, VERSION_MIN, VERSION_UPDATE);
1602 long newVer = make_full_ed2k_version(fields[0], fields[1], fields[2]);
1604 if (curVer < newVer) {
1605 AddLogLineC(_("You are using an outdated version of aMule!"));
1606 AddLogLineN(CFormat(_("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]);
1607 AddLogLineN(_("The latest version can always be found at http://www.amule.org"));
1608 #ifdef AMULE_DAEMON
1609 AddLogLineCS(CFormat(_("WARNING: Your aMuled version is outdated: %i.%i.%i < %li.%li.%li"))
1610 % VERSION_MJR % VERSION_MIN % VERSION_UPDATE % fields[0] % fields[1] % fields[2]);
1611 #endif
1612 } else {
1613 AddLogLineN(_("Your copy of aMule is up to date."));
1617 file.Close();
1618 wxRemoveFile(filename);
1619 } else {
1620 AddLogLineC(_("Failed to download the version check file"));
1626 bool CamuleApp::IsConnected() const
1628 return (IsConnectedED2K() || IsConnectedKad());
1632 bool CamuleApp::IsConnectedED2K() const
1634 return serverconnect && serverconnect->IsConnected();
1638 bool CamuleApp::IsConnectedKad() const
1640 return Kademlia::CKademlia::IsConnected();
1644 bool CamuleApp::IsFirewalled() const
1646 if (theApp->IsConnectedED2K() && !theApp->serverconnect->IsLowID()) {
1647 return false; // we have an eD2K HighID -> not firewalled
1650 return IsFirewalledKad(); // If kad says ok, it's ok.
1653 bool CamuleApp::IsFirewalledKad() const
1655 return !Kademlia::CKademlia::IsConnected() // not connected counts as firewalled
1656 || Kademlia::CKademlia::IsFirewalled();
1659 bool CamuleApp::IsFirewalledKadUDP() const
1661 return !Kademlia::CKademlia::IsConnected() // not connected counts as firewalled
1662 || Kademlia::CUDPFirewallTester::IsFirewalledUDP(true);
1665 bool CamuleApp::IsKadRunning() const
1667 return Kademlia::CKademlia::IsRunning();
1670 bool CamuleApp::IsKadRunningInLanMode() const
1672 return Kademlia::CKademlia::IsRunningInLANMode();
1675 // Kad stats
1676 uint32 CamuleApp::GetKadUsers() const
1678 return Kademlia::CKademlia::GetKademliaUsers();
1681 uint32 CamuleApp::GetKadFiles() const
1683 return Kademlia::CKademlia::GetKademliaFiles();
1686 uint32 CamuleApp::GetKadIndexedSources() const
1688 return Kademlia::CKademlia::GetIndexed()->m_totalIndexSource;
1691 uint32 CamuleApp::GetKadIndexedKeywords() const
1693 return Kademlia::CKademlia::GetIndexed()->m_totalIndexKeyword;
1696 uint32 CamuleApp::GetKadIndexedNotes() const
1698 return Kademlia::CKademlia::GetIndexed()->m_totalIndexNotes;
1701 uint32 CamuleApp::GetKadIndexedLoad() const
1703 return Kademlia::CKademlia::GetIndexed()->m_totalIndexLoad;
1707 // True IP of machine
1708 uint32 CamuleApp::GetKadIPAdress() const
1710 return wxUINT32_SWAP_ALWAYS(Kademlia::CKademlia::GetPrefs()->GetIPAddress());
1713 // Buddy status
1714 uint8 CamuleApp::GetBuddyStatus() const
1716 return clientlist->GetBuddyStatus();
1719 uint32 CamuleApp::GetBuddyIP() const
1721 return clientlist->GetBuddyIP();
1724 uint32 CamuleApp::GetBuddyPort() const
1726 return clientlist->GetBuddyPort();
1729 bool CamuleApp::CanDoCallback(uint32 clientServerIP, uint16 clientServerPort)
1731 if (Kademlia::CKademlia::IsConnected()) {
1732 if (IsConnectedED2K()) {
1733 if (serverconnect->IsLowID()) {
1734 if (Kademlia::CKademlia::IsFirewalled()) {
1735 //Both Connected - Both Firewalled
1736 return false;
1737 } else {
1738 if (clientServerIP == theApp->serverconnect->GetCurrentServer()->GetIP() &&
1739 clientServerPort == theApp->serverconnect->GetCurrentServer()->GetPort()) {
1740 // Both Connected - Server lowID, Kad Open - Client on same server
1741 // We prevent a callback to the server as this breaks the protocol
1742 // and will get you banned.
1743 return false;
1744 } else {
1745 // Both Connected - Server lowID, Kad Open - Client on remote server
1746 return true;
1749 } else {
1750 //Both Connected - Server HighID, Kad don't care
1751 return true;
1753 } else {
1754 if (Kademlia::CKademlia::IsFirewalled()) {
1755 //Only Kad Connected - Kad Firewalled
1756 return false;
1757 } else {
1758 //Only Kad Conected - Kad Open
1759 return true;
1762 } else {
1763 if (IsConnectedED2K()) {
1764 if (serverconnect->IsLowID()) {
1765 //Only Server Connected - Server LowID
1766 return false;
1767 } else {
1768 //Only Server Connected - Server HighID
1769 return true;
1771 } else {
1772 //We are not connected at all!
1773 return false;
1778 void CamuleApp::ShowUserCount() {
1779 uint32 totaluser = 0, totalfile = 0;
1781 theApp->serverlist->GetUserFileStatus( totaluser, totalfile );
1783 wxString buffer;
1785 static const wxString s_singlenetstatusformat = _("Users: %s | Files: %s");
1786 static const wxString s_bothnetstatusformat = _("Users: E: %s K: %s | Files: E: %s K: %s");
1788 if (thePrefs::GetNetworkED2K() && thePrefs::GetNetworkKademlia()) {
1789 buffer = CFormat(s_bothnetstatusformat) % CastItoIShort(totaluser) % CastItoIShort(Kademlia::CKademlia::GetKademliaUsers()) % CastItoIShort(totalfile) % CastItoIShort(Kademlia::CKademlia::GetKademliaFiles());
1790 } else if (thePrefs::GetNetworkED2K()) {
1791 buffer = CFormat(s_singlenetstatusformat) % CastItoIShort(totaluser) % CastItoIShort(totalfile);
1792 } else if (thePrefs::GetNetworkKademlia()) {
1793 buffer = CFormat(s_singlenetstatusformat) % CastItoIShort(Kademlia::CKademlia::GetKademliaUsers()) % CastItoIShort(Kademlia::CKademlia::GetKademliaFiles());
1794 } else {
1795 buffer = _("No networks selected");
1798 Notify_ShowUserCount(buffer);
1802 void CamuleApp::ListenSocketHandler(wxSocketEvent& event)
1804 { wxCHECK_RET(listensocket, wxT("Connection-event for NULL'd listen-socket")); }
1805 { wxCHECK_RET(event.GetSocketEvent() == wxSOCKET_CONNECTION,
1806 wxT("Invalid event received for listen-socket")); }
1808 if (m_app_state == APP_STATE_RUNNING) {
1809 listensocket->OnAccept(0);
1810 } else if (m_app_state == APP_STATE_STARTING) {
1811 // When starting up, connection may be made before we are able
1812 // to handle them. However, if these are ignored, no futher
1813 // connection-events will be triggered, so we have to accept it.
1814 wxSocketBase* socket = listensocket->Accept(false);
1816 wxCHECK_RET(socket, wxT("NULL returned by Accept() during startup"));
1818 socket->Destroy();
1823 void CamuleApp::ShowConnectionState(bool forceUpdate)
1825 static uint8 old_state = (1<<7); // This flag doesn't exist
1827 uint8 state = 0;
1829 if (theApp->serverconnect->IsConnected()) {
1830 state |= CONNECTED_ED2K;
1833 if (Kademlia::CKademlia::IsRunning()) {
1834 if (Kademlia::CKademlia::IsConnected()) {
1835 if (!Kademlia::CKademlia::IsFirewalled()) {
1836 state |= CONNECTED_KAD_OK;
1837 } else {
1838 state |= CONNECTED_KAD_FIREWALLED;
1840 } else {
1841 state |= CONNECTED_KAD_NOT;
1845 if (old_state != state) {
1846 // Get the changed value
1847 int changed_flags = old_state ^ state;
1849 if (changed_flags & CONNECTED_ED2K) {
1850 // ED2K status changed
1851 wxString connected_server;
1852 CServer* ed2k_server = theApp->serverconnect->GetCurrentServer();
1853 if (ed2k_server) {
1854 connected_server = ed2k_server->GetListName();
1856 if (state & CONNECTED_ED2K) {
1857 // We connected to some server
1858 const wxString id = theApp->serverconnect->IsLowID() ? _("with LowID") : _("with HighID");
1860 AddLogLineC(CFormat(_("Connected to %s %s")) % connected_server % id);
1861 } else {
1862 if ( theApp->serverconnect->IsConnecting() ) {
1863 AddLogLineC(CFormat(_("Connecting to %s")) % connected_server);
1864 } else {
1865 AddLogLineC(_("Disconnected from eD2k"));
1870 if (changed_flags & CONNECTED_KAD_NOT) {
1871 if (state & CONNECTED_KAD_NOT) {
1872 AddLogLineC(_("Kad started."));
1873 } else {
1874 AddLogLineC(_("Kad stopped."));
1878 if (changed_flags & (CONNECTED_KAD_OK | CONNECTED_KAD_FIREWALLED)) {
1879 if (state & (CONNECTED_KAD_OK | CONNECTED_KAD_FIREWALLED)) {
1880 if (state & CONNECTED_KAD_OK) {
1881 AddLogLineC(_("Connected to Kad (ok)"));
1882 } else {
1883 AddLogLineC(_("Connected to Kad (firewalled)"));
1885 } else {
1886 AddLogLineC(_("Disconnected from Kad"));
1890 old_state = state;
1892 theApp->downloadqueue->OnConnectionState(IsConnected());
1895 ShowUserCount();
1896 Notify_ShowConnState(forceUpdate);
1900 void CamuleApp::UDPSocketHandler(wxSocketEvent& event)
1902 CMuleUDPSocket* socket = (CMuleUDPSocket*)(event.GetClientData());
1903 wxCHECK_RET(socket, wxT("No socket owner specified."));
1905 if (IsOnShutDown() || thePrefs::IsUDPDisabled()) return;
1907 if (!IsRunning()) {
1908 if (event.GetSocketEvent() == wxSOCKET_INPUT) {
1909 // Back to the queue!
1910 theApp->AddPendingEvent(event);
1911 return;
1915 switch (event.GetSocketEvent()) {
1916 case wxSOCKET_INPUT:
1917 socket->OnReceive(0);
1918 break;
1920 case wxSOCKET_OUTPUT:
1921 socket->OnSend(0);
1922 break;
1924 case wxSOCKET_LOST:
1925 socket->OnDisconnected(0);
1926 break;
1928 default:
1929 wxFAIL;
1930 break;
1935 void CamuleApp::OnUnhandledException()
1937 // Call the generic exception-handler.
1938 fprintf(stderr, "\taMule Version: %s\n", (const char*)unicode2char(GetFullMuleVersion()));
1939 ::OnUnhandledException();
1942 void CamuleApp::StartKad()
1944 if (!Kademlia::CKademlia::IsRunning() && thePrefs::GetNetworkKademlia()) {
1945 // Kad makes no sense without the Client-UDP socket.
1946 if (!thePrefs::IsUDPDisabled()) {
1947 if (ipfilter->IsReady()) {
1948 Kademlia::CKademlia::Start();
1949 } else {
1950 ipfilter->StartKADWhenReady();
1952 } else {
1953 AddLogLineC(_("Kad network cannot be used if UDP port is disabled on preferences, not starting."));
1955 } else if (!thePrefs::GetNetworkKademlia()) {
1956 AddLogLineC(_("Kad network disabled on preferences, not connecting."));
1960 void CamuleApp::StopKad()
1962 // Stop Kad if it's running
1963 if (Kademlia::CKademlia::IsRunning()) {
1964 Kademlia::CKademlia::Stop();
1969 void CamuleApp::BootstrapKad(uint32 ip, uint16 port)
1971 if (!Kademlia::CKademlia::IsRunning()) {
1972 Kademlia::CKademlia::Start();
1973 theApp->ShowConnectionState();
1976 Kademlia::CKademlia::Bootstrap(ip, port);
1980 void CamuleApp::UpdateNotesDat(const wxString& url)
1982 wxString strTempFilename(theApp->ConfigDir + wxT("nodes.dat.download"));
1984 CHTTPDownloadThread *downloader = new CHTTPDownloadThread(url, strTempFilename, theApp->ConfigDir + wxT("nodes.dat"), HTTP_NodesDat, true, false);
1985 downloader->Create();
1986 downloader->Run();
1990 void CamuleApp::DisconnectED2K()
1992 // Stop ED2K if it's running
1993 if (IsConnectedED2K()) {
1994 serverconnect->Disconnect();
1998 bool CamuleApp::CryptoAvailable() const
2000 return clientcredits && clientcredits->CryptoAvailable();
2003 uint32 CamuleApp::GetED2KID() const {
2004 return serverconnect ? serverconnect->GetClientID() : 0;
2007 uint32 CamuleApp::GetID() const {
2008 uint32 ID;
2010 if( Kademlia::CKademlia::IsConnected() && !Kademlia::CKademlia::IsFirewalled() ) {
2011 // We trust Kad above ED2K
2012 ID = ENDIAN_NTOHL(Kademlia::CKademlia::GetIPAddress());
2013 } else if( theApp->serverconnect->IsConnected() ) {
2014 ID = theApp->serverconnect->GetClientID();
2015 } else if ( Kademlia::CKademlia::IsConnected() && Kademlia::CKademlia::IsFirewalled() ) {
2016 // A firewalled Kad client get's a "1"
2017 ID = 1;
2018 } else {
2019 ID = 0;
2022 return ID;
2025 DEFINE_LOCAL_EVENT_TYPE(wxEVT_CORE_FINISHED_HTTP_DOWNLOAD)
2026 DEFINE_LOCAL_EVENT_TYPE(wxEVT_CORE_SOURCE_DNS_DONE)
2027 DEFINE_LOCAL_EVENT_TYPE(wxEVT_CORE_UDP_DNS_DONE)
2028 DEFINE_LOCAL_EVENT_TYPE(wxEVT_CORE_SERVER_DNS_DONE)
2029 // File_checked_for_headers