Wait UPnP service responses for 3s before add port mappings
[amule.git] / src / amule.cpp
blob6259f7d159ee49a00e7448373c90e5896efd9247
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/socket.h>
46 #include <wx/tokenzr.h>
47 #include <wx/wfstream.h>
48 #include <wx/stopwatch.h> // Needed for wxStopWatch
51 #include <common/Format.h> // Needed for CFormat
52 #include "kademlia/kademlia/Kademlia.h"
53 #include "kademlia/kademlia/Prefs.h"
54 #include "kademlia/kademlia/UDPFirewallTester.h"
55 #include "CanceledFileList.h"
56 #include "ClientCreditsList.h" // Needed for CClientCreditsList
57 #include "ClientList.h" // Needed for CClientList
58 #include "ClientUDPSocket.h" // Needed for CClientUDPSocket & CMuleUDPSocket
59 #include "ExternalConn.h" // Needed for ExternalConn & MuleConnection
60 #include <common/FileFunctions.h> // Needed for CDirIterator
61 #include "FriendList.h" // Needed for CFriendList
62 #include "HTTPDownload.h" // Needed for CHTTPDownloadThread
63 #include "InternalEvents.h" // Needed for CMuleInternalEvent
64 #include "IPFilter.h" // Needed for CIPFilter
65 #include "KnownFileList.h" // Needed for CKnownFileList
66 #include "ListenSocket.h" // Needed for CListenSocket
67 #include "Logger.h" // Needed for CLogger // Do_not_auto_remove
68 #include "MagnetURI.h" // Needed for CMagnetURI
69 #include "OtherFunctions.h"
70 #include "PartFile.h" // Needed for CPartFile
71 #include "PlatformSpecific.h" // Needed for PlatformSpecific::AllowSleepMode();
72 #include "Preferences.h" // Needed for CPreferences
73 #include "SearchList.h" // Needed for CSearchList
74 #include "Server.h" // Needed for GetListName
75 #include "ServerList.h" // Needed for CServerList
76 #include "ServerConnect.h" // Needed for CServerConnect
77 #include "ServerUDPSocket.h" // Needed for CServerUDPSocket
78 #include "Statistics.h" // Needed for CStatistics
79 #include "TerminationProcessAmuleweb.h" // Needed for CTerminationProcessAmuleweb
80 #include "ThreadTasks.h"
81 #include "UploadQueue.h" // Needed for CUploadQueue
82 #include "UploadBandwidthThrottler.h"
83 #include "UserEvents.h"
84 #include "ScopedPtr.h"
86 #ifdef ENABLE_UPNP
87 #include "UPnPBase.h" // Needed for UPnP
88 #endif
90 #ifdef __WXMAC__
91 #include <wx/sysopt.h> // Do_not_auto_remove
92 #endif
94 #ifndef AMULE_DAEMON
95 #ifdef __WXMAC__
96 #include <CoreFoundation/CFBundle.h> // Do_not_auto_remove
97 #if wxCHECK_VERSION(2, 9, 0)
98 #include <wx/osx/core/cfstring.h> // Do_not_auto_remove
99 #else
100 #include <wx/mac/corefoundation/cfstring.h> // Do_not_auto_remove
101 #endif
102 #endif
103 #include <wx/msgdlg.h>
105 #include "amuleDlg.h"
106 #endif
109 #ifdef HAVE_SYS_RESOURCE_H
110 #include <sys/resource.h>
111 #endif
113 #ifdef HAVE_SYS_STATVFS_H
114 #include <sys/statvfs.h> // Do_not_auto_remove
115 #endif
118 #ifdef __GLIBC__
119 # define RLIMIT_RESOURCE __rlimit_resource
120 #else
121 # define RLIMIT_RESOURCE int
122 #endif
124 #ifdef AMULE_DAEMON
125 CamuleDaemonApp *theApp;
126 #else
127 CamuleGuiApp *theApp;
128 #endif
130 static void UnlimitResource(RLIMIT_RESOURCE resType)
132 #if defined(HAVE_GETRLIMIT) && defined(HAVE_SETRLIMIT)
133 struct rlimit rl;
134 getrlimit(resType, &rl);
135 rl.rlim_cur = rl.rlim_max;
136 setrlimit(resType, &rl);
137 #endif
141 static void SetResourceLimits()
143 #ifdef HAVE_SYS_RESOURCE_H
144 UnlimitResource(RLIMIT_DATA);
145 #ifndef __UCLIBC__
146 UnlimitResource(RLIMIT_FSIZE);
147 #endif
148 UnlimitResource(RLIMIT_NOFILE);
149 #ifdef RLIMIT_RSS
150 UnlimitResource(RLIMIT_RSS);
151 #endif
152 #endif
155 // We store the received signal in order to avoid race-conditions
156 // in the signal handler.
157 bool g_shutdownSignal = false;
159 void OnShutdownSignal( int /* sig */ )
161 signal(SIGINT, SIG_DFL);
162 signal(SIGTERM, SIG_DFL);
164 g_shutdownSignal = true;
166 #ifdef AMULE_DAEMON
167 theApp->ExitMainLoop();
168 #endif
172 CamuleApp::CamuleApp()
174 // Madcat - Initialize timer as the VERY FIRST thing to avoid any issues later.
175 // Kry - I love to init the vars on init, even before timer.
176 StartTickTimer();
178 // Initialization
179 m_app_state = APP_STATE_STARTING;
181 theApp = &wxGetApp();
183 clientlist = NULL;
184 searchlist = NULL;
185 knownfiles = NULL;
186 canceledfiles = NULL;
187 serverlist = NULL;
188 serverconnect = NULL;
189 sharedfiles = NULL;
190 listensocket = NULL;
191 clientudp = NULL;
192 clientcredits = NULL;
193 friendlist = NULL;
194 downloadqueue = NULL;
195 uploadqueue = NULL;
196 ipfilter = NULL;
197 ECServerHandler = NULL;
198 glob_prefs = NULL;
199 m_statistics = NULL;
200 uploadBandwidthThrottler = NULL;
201 #ifdef ENABLE_UPNP
202 m_upnp = NULL;
203 m_upnpMappings.resize(4);
204 #endif
205 core_timer = NULL;
207 m_localip = 0;
208 m_dwPublicIP = 0;
209 webserver_pid = 0;
211 enable_daemon_fork = false;
213 // Apprently needed for *BSD
214 SetResourceLimits();
216 #ifdef _MSC_VER
217 _CrtSetDbgFlag(0); // Disable useless memleak debugging
218 #endif
221 CamuleApp::~CamuleApp()
223 // Closing the log-file as the very last thing, since
224 // wxWidgets log-events are saved in it as well.
225 theLogger.CloseLogfile();
228 int CamuleApp::OnExit()
230 if (m_app_state!=APP_STATE_STARTING) {
231 AddLogLineNS(_("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 AddLogLineNS(_("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 #ifdef ASIO_SOCKETS
327 delete m_AsioService;
328 m_AsioService = NULL;
329 #endif
331 wxSocketBase::Shutdown(); // needed because we also called Initialize() manually
333 if (m_app_state!=APP_STATE_STARTING) {
334 AddLogLineNS(_("aMule shutdown completed."));
337 #if wxUSE_MEMORY_TRACING
338 AddLogLineNS(_("Memory debug results for aMule exit:"));
339 // Log mem debug mesages to wxLogStderr
340 wxLog* oldLog = wxLog::SetActiveTarget(new wxLogStderr);
341 //AddLogLineNS(wxT("**************Classes**************");
342 //wxDebugContext::PrintClasses();
343 //AddLogLineNS(wxT("***************Dump***************");
344 //wxDebugContext::Dump();
345 AddLogLineNS(wxT("***************Stats**************"));
346 wxDebugContext::PrintStatistics(true);
348 // Set back to wxLogGui
349 delete wxLog::SetActiveTarget(oldLog);
350 #endif
352 StopTickTimer();
354 // Return 0 for succesful program termination
355 return AMULE_APP_BASE::OnExit();
359 int CamuleApp::InitGui(bool, wxString &)
361 return 0;
366 // Application initialization
368 bool CamuleApp::OnInit()
370 #if wxUSE_MEMORY_TRACING
371 // any text before call of Localize_mule needs not to be translated.
372 AddLogLineNS(wxT("Checkpoint set on app init for memory debug")); // debug output
373 wxDebugContext::SetCheckpoint();
374 #endif
376 // Forward wxLog events to CLogger
377 wxLog::SetActiveTarget(new CLoggerTarget);
379 m_localip = StringHosttoUint32(::wxGetFullHostName());
381 #ifndef __WINDOWS__
382 // get rid of sigpipe
383 signal(SIGPIPE, SIG_IGN);
384 #else
385 // Handle CTRL-Break
386 signal(SIGBREAK, OnShutdownSignal);
387 #endif
388 // Handle sigint and sigterm
389 signal(SIGINT, OnShutdownSignal);
390 signal(SIGTERM, OnShutdownSignal);
392 #ifdef __WXMAC__
393 // For listctrl's to behave on Mac
394 wxSystemOptions::SetOption(wxT("mac.listctrl.always_use_generic"), 1);
395 #endif
397 // Handle uncaught exceptions
398 InstallMuleExceptionHandler();
400 if (!InitCommon(AMULE_APP_BASE::argc, AMULE_APP_BASE::argv)) {
401 return false;
404 glob_prefs = new CPreferences();
406 CPath outDir;
407 if (CheckMuleDirectory(wxT("temp"), thePrefs::GetTempDir(), thePrefs::GetConfigDir() + wxT("Temp"), outDir)) {
408 thePrefs::SetTempDir(outDir);
409 } else {
410 return false;
413 if (CheckMuleDirectory(wxT("incoming"), thePrefs::GetIncomingDir(), thePrefs::GetConfigDir() + wxT("Incoming"), outDir)) {
414 thePrefs::SetIncomingDir(outDir);
415 } else {
416 return false;
419 // Initialize wx sockets (needed for http download in background with Asio sockets)
420 wxSocketBase::Initialize();
422 // Some sanity check
423 if (!thePrefs::UseTrayIcon()) {
424 thePrefs::SetMinToTray(false);
427 // Build the filenames for the two OS files
428 SetOSFiles(thePrefs::GetOSDir().GetRaw());
430 // Check if we have the old style locale config
431 bool old_localedef = false;
432 wxString langId = thePrefs::GetLanguageID();
433 if (!langId.IsEmpty() && (langId.GetChar(0) >= '0' && langId.GetChar(0) <= '9')) {
434 old_localedef = true;
435 thePrefs::SetLanguageID(wxLang2Str(wxLANGUAGE_DEFAULT));
436 glob_prefs->Save();
439 #ifdef ENABLE_NLS
440 // Load localization settings
441 Localize_mule();
443 if (old_localedef) {
444 ShowAlert(_("Your locale has been changed to System Default due to a configuration change. Sorry."), _("Info"), wxCENTRE | wxOK | wxICON_ERROR);
446 #endif
448 // Configure EC for amuled when invoked with ec-config
449 if (ec_config) {
450 AddLogLineNS(_("\nEC configuration"));
451 thePrefs::SetECPass(GetPassword(false).Encode());
452 thePrefs::EnableExternalConnections(true);
453 AddLogLineNS(_("Password set and external connections enabled."));
456 #ifndef __WINDOWS__
457 if (getuid() == 0) {
458 wxString msg =
459 wxT("Warning! You are running aMule as root.\n")
460 wxT("Doing so is not recommended for security reasons,\n")
461 wxT("and you are advised to run aMule as an normal\n")
462 wxT("user instead.");
464 ShowAlert(msg, _("WARNING"), wxCENTRE | wxOK | wxICON_ERROR);
466 fprintf(stderr, "\n--------------------------------------------------\n");
467 fprintf(stderr, "%s", (const char*)unicode2UTF8(msg));
468 fprintf(stderr, "\n--------------------------------------------------\n\n");
470 #endif
472 // Display notification on new version or first run
473 wxTextFile vfile( thePrefs::GetConfigDir() + wxT("lastversion") );
474 wxString newMule(wxT( VERSION ));
476 if ( !wxFileExists( vfile.GetName() ) ) {
477 vfile.Create();
480 if ( vfile.Open() ) {
481 // Check if this version has been run before
482 bool found = false;
483 for ( size_t i = 0; i < vfile.GetLineCount(); i++ ) {
484 // Check if this version has been run before
485 if ( vfile.GetLine(i) == newMule ) {
486 found = true;
487 break;
491 // We havent run this version before?
492 if ( !found ) {
493 // Insert new at top to provide faster searches
494 vfile.InsertLine( newMule, 0 );
496 Trigger_New_version( newMule );
499 // Keep at most 10 entires
500 while ( vfile.GetLineCount() > 10 )
501 vfile.RemoveLine( vfile.GetLineCount() - 1 );
503 vfile.Write();
504 vfile.Close();
507 m_statistics = new CStatistics();
509 clientlist = new CClientList();
510 friendlist = new CFriendList();
511 searchlist = new CSearchList();
512 knownfiles = new CKnownFileList();
513 canceledfiles = new CCanceledFileList;
514 serverlist = new CServerList();
516 sharedfiles = new CSharedFileList(knownfiles);
517 clientcredits = new CClientCreditsList();
519 // bugfix - do this before creating the uploadqueue
520 downloadqueue = new CDownloadQueue();
521 uploadqueue = new CUploadQueue();
522 ipfilter = new CIPFilter();
524 // Creates all needed listening sockets
525 wxString msg;
526 if (!ReinitializeNetwork(&msg)) {
527 AddLogLineNS(wxT("\n"));
528 AddLogLineNS(msg);
531 // Test if there's any new version
532 if (thePrefs::GetCheckNewVersion()) {
533 // We use the thread base because I don't want a dialog to pop up.
534 CHTTPDownloadThread* version_check =
535 new CHTTPDownloadThread(wxT("http://amule.sourceforge.net/lastversion"),
536 thePrefs::GetConfigDir() + wxT("last_version_check"), thePrefs::GetConfigDir() + wxT("last_version"), HTTP_VersionCheck, false, false);
537 version_check->Create();
538 version_check->Run();
541 // Create main dialog, or fork to background (daemon).
542 InitGui(m_geometryEnabled, m_geometryString);
544 #ifdef AMULE_DAEMON
545 // Need to refresh wxSingleInstanceChecker after the daemon fork() !
546 if (enable_daemon_fork) {
547 RefreshSingleInstanceChecker();
548 // No need to check IsAnotherRunning() - we've done it before.
550 #endif
552 // Has to be created after the call to InitGui, as fork
553 // (when using posix threads) only replicates the mainthread,
554 // and the UBT constructor creates a thread.
555 uploadBandwidthThrottler = new UploadBandwidthThrottler();
557 #ifdef ASIO_SOCKETS
558 m_AsioService = new CAsioService;
559 #endif
561 // Start performing background tasks
562 // This will start loading the IP filter. It will start right away.
563 // Log is confusing, because log entries from background will only be printed
564 // once foreground becomes idle, and that will only be after loading
565 // of the partfiles has finished.
566 CThreadScheduler::Start();
568 // These must be initialized after the gui is loaded.
569 if (thePrefs::GetNetworkED2K()) {
570 serverlist->Init();
572 downloadqueue->LoadMetFiles(thePrefs::GetTempDir());
573 sharedfiles->Reload();
575 // Ensure that the up/down ratio is used
576 CPreferences::CheckUlDlRatio();
578 // Load saved friendlist (now, so it can update in GUI right away)
579 friendlist->LoadList();
581 // The user can start pressing buttons like mad if he feels like it.
582 m_app_state = APP_STATE_RUNNING;
584 if (!serverlist->GetServerCount() && thePrefs::GetNetworkED2K()) {
585 // There are no servers and ED2K active -> ask for download.
586 // As we cannot ask in amuled, we just update there
587 #ifndef AMULE_DAEMON
588 if (wxYES == wxMessageBox(
589 wxString(
590 _("You don't have any server in the server list.\nDo you want aMule to download a new list now?")),
591 wxString(_("Server list download")),
592 wxYES_NO,
593 static_cast<wxWindow*>(theApp->amuledlg)))
594 #endif
596 serverlist->UpdateServerMetFromURL(thePrefs::GetEd2kServersUrl());
601 // Autoconnect if that option is enabled
602 if (thePrefs::DoAutoConnect()) {
603 // IP filter is still loading and will be finished on event.
604 // Tell it to autoconnect.
605 if (thePrefs::GetNetworkED2K()) {
606 ipfilter->ConnectToAnyServerWhenReady();
608 if (thePrefs::GetNetworkKademlia()) {
609 ipfilter->StartKADWhenReady();
613 // Enable GeoIP
614 #ifdef ENABLE_IP2COUNTRY
615 theApp->amuledlg->EnableIP2Country();
616 #endif
618 // Run webserver?
619 if (thePrefs::GetWSIsEnabled()) {
620 wxString aMuleConfigFile = thePrefs::GetConfigDir() + m_configFile;
621 wxString amulewebPath = thePrefs::GetWSPath();
623 #if defined(__WXMAC__) && !defined(AMULE_DAEMON)
624 // For the Mac GUI application, look for amuleweb in the bundle
625 CFURLRef amulewebUrl = CFBundleCopyAuxiliaryExecutableURL(
626 CFBundleGetMainBundle(), CFSTR("amuleweb"));
628 if (amulewebUrl) {
629 CFURLRef absoluteUrl = CFURLCopyAbsoluteURL(amulewebUrl);
630 CFRelease(amulewebUrl);
632 if (absoluteUrl) {
633 CFStringRef amulewebCfstr = CFURLCopyFileSystemPath(absoluteUrl, kCFURLPOSIXPathStyle);
634 CFRelease(absoluteUrl);
635 #if wxCHECK_VERSION(2, 9, 0)
636 amulewebPath = wxCFStringRef(amulewebCfstr).AsString(wxLocale::GetSystemEncoding());
637 #else
638 amulewebPath = wxMacCFStringHolder(amulewebCfstr).AsString(wxLocale::GetSystemEncoding());
639 #endif
642 #endif
644 #ifdef __WINDOWS__
645 # define QUOTE wxT("\"")
646 #else
647 # define QUOTE wxT("\'")
648 #endif
650 wxString cmd =
651 QUOTE +
652 amulewebPath +
653 QUOTE wxT(" ") QUOTE wxT("--amule-config-file=") +
654 aMuleConfigFile +
655 QUOTE;
656 CTerminationProcessAmuleweb *p = new CTerminationProcessAmuleweb(cmd, &webserver_pid);
657 webserver_pid = wxExecute(cmd, wxEXEC_ASYNC, p);
658 bool webserver_ok = webserver_pid > 0;
659 if (webserver_ok) {
660 AddLogLineC(CFormat(_("web server running on pid %d")) % webserver_pid);
661 } else {
662 delete p;
663 ShowAlert(_(
664 "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"),
665 _("ERROR"), wxOK | wxICON_ERROR);
669 return true;
672 bool CamuleApp::ReinitializeNetwork(wxString* msg)
674 bool ok = true;
675 static bool firstTime = true;
677 if (!firstTime) {
678 // TODO: Destroy previously created sockets
680 firstTime = false;
682 // Some sanity checks first
683 if (thePrefs::ECPort() == thePrefs::GetPort()) {
684 // Select a random usable port in the range 1025 ... 2^16 - 1
685 uint16 port = thePrefs::ECPort();
686 while ( port < 1024 || port == thePrefs::GetPort() ) {
687 port = (uint16)rand();
689 thePrefs::SetECPort( port );
691 wxString err =
692 wxT("Network configuration failed! You cannot use the same port\n")
693 wxT("for the main TCP port and the External Connections port.\n")
694 wxT("The EC port has been changed to avoid conflict, see the\n")
695 wxT("preferences for the new value.\n");
696 *msg << err;
698 AddLogLineN(wxEmptyString );
699 AddLogLineC(err );
700 AddLogLineN(wxEmptyString );
702 ok = false;
705 if (thePrefs::GetUDPPort() == thePrefs::GetPort() + 3) {
706 // Select a random usable value in the range 1025 ... 2^16 - 1
707 uint16 port = thePrefs::GetUDPPort();
708 while ( port < 1024 || port == thePrefs::GetPort() + 3 ) {
709 port = (uint16)rand();
711 thePrefs::SetUDPPort( port );
713 wxString err =
714 wxT("Network configuration failed! You set your UDP port to\n")
715 wxT("the value of the main TCP port plus 3.\n")
716 wxT("This port has been reserved for the Server-UDP port. The\n")
717 wxT("port value has been changed to avoid conflict, see the\n")
718 wxT("preferences for the new value\n");
719 *msg << err;
721 AddLogLineN(wxEmptyString );
722 AddLogLineC(err );
723 AddLogLineN(wxEmptyString );
725 ok = false;
728 // Create the address where we are going to listen
729 // TODO: read this from configuration file
730 amuleIPV4Address myaddr[4];
732 // Create the External Connections Socket.
733 // Default is 4712.
734 // Get ready to handle connections from apps like amulecmd
735 if (thePrefs::GetECAddress().IsEmpty() || !myaddr[0].Hostname(thePrefs::GetECAddress())) {
736 myaddr[0].AnyAddress();
738 myaddr[0].Service(thePrefs::ECPort());
739 ECServerHandler = new ExternalConn(myaddr[0], msg);
741 // Create the UDP socket TCP+3.
742 // Used for source asking on servers.
743 if (thePrefs::GetAddress().IsEmpty()) {
744 myaddr[1].AnyAddress();
745 } else if (!myaddr[1].Hostname(thePrefs::GetAddress())) {
746 myaddr[1].AnyAddress();
747 AddLogLineC(CFormat(_("Could not bind ports to the specified address: %s"))
748 % thePrefs::GetAddress());
751 wxString ip = myaddr[1].IPAddress();
752 myaddr[1].Service(thePrefs::GetPort()+3);
753 serverconnect = new CServerConnect(serverlist, myaddr[1]);
754 *msg << CFormat( wxT("*** Server UDP socket (TCP+3) at %s:%u\n") )
755 % ip % ((unsigned int)thePrefs::GetPort() + 3u);
757 // Create the ListenSocket (aMule TCP socket).
758 // Used for Client Port / Connections from other clients,
759 // Client to Client Source Exchange.
760 // Default is 4662.
761 myaddr[2] = myaddr[1];
762 myaddr[2].Service(thePrefs::GetPort());
763 listensocket = new CListenSocket(myaddr[2]);
764 *msg << CFormat( wxT("*** TCP socket (TCP) listening on %s:%u\n") )
765 % ip % (unsigned int)(thePrefs::GetPort());
766 // Notify(true) has already been called to the ListenSocket, so events may
767 // be already comming in.
768 if (!listensocket->IsOk()) {
769 // If we wern't able to start listening, we need to warn the user
770 wxString err;
771 err = CFormat(_("Port %u is not available. You will be LOWID\n")) %
772 (unsigned int)(thePrefs::GetPort());
773 *msg << err;
774 AddLogLineC(err);
775 err.Clear();
776 err = CFormat(
777 _("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.")) %
778 (unsigned int)(thePrefs::GetPort());
779 ShowAlert(err, _("ERROR"), wxOK | wxICON_ERROR);
782 // Create the UDP socket.
783 // Used for extended eMule protocol, Queue Rating, File Reask Ping.
784 // Also used for Kademlia.
785 // Default is port 4672.
786 myaddr[3] = myaddr[1];
787 myaddr[3].Service(thePrefs::GetUDPPort());
788 clientudp = new CClientUDPSocket(myaddr[3], thePrefs::GetProxyData());
789 if (!thePrefs::IsUDPDisabled()) {
790 *msg << CFormat( wxT("*** Client UDP socket (extended eMule) at %s:%u") )
791 % ip % thePrefs::GetUDPPort();
792 } else {
793 *msg << wxT("*** Client UDP socket (extended eMule) disabled on preferences");
796 #ifdef ENABLE_UPNP
797 if (thePrefs::GetUPnPEnabled()) {
798 try {
799 m_upnpMappings[0] = CUPnPPortMapping(
800 myaddr[0].Service(),
801 "TCP",
802 thePrefs::GetUPnPECEnabled(),
803 "aMule TCP External Connections Socket");
804 m_upnpMappings[1] = CUPnPPortMapping(
805 myaddr[1].Service(),
806 "UDP",
807 thePrefs::GetUPnPEnabled(),
808 "aMule UDP socket (TCP+3)");
809 m_upnpMappings[2] = CUPnPPortMapping(
810 myaddr[2].Service(),
811 "TCP",
812 thePrefs::GetUPnPEnabled(),
813 "aMule TCP Listen Socket");
814 m_upnpMappings[3] = CUPnPPortMapping(
815 myaddr[3].Service(),
816 "UDP",
817 thePrefs::GetUPnPEnabled(),
818 "aMule UDP Extended eMule Socket");
819 m_upnp = new CUPnPControlPoint(thePrefs::GetUPnPTCPPort());
821 wxStopWatch count; // Wait UPnP service responses for 3s before add port mappings
822 while (count.Time() < 3000 && !m_upnp->WanServiceDetected());
824 m_upnp->AddPortMappings(m_upnpMappings);
825 } catch(CUPnPException &e) {
826 wxString error_msg;
827 error_msg << e.what();
828 AddLogLineC(error_msg);
829 fprintf(stderr, "%s\n", (const char *)unicode2char(error_msg));
832 #endif
834 return ok;
837 /* Original implementation by Bouc7 of the eMule Project.
838 aMule Signature idea was designed by BigBob and implemented
839 by Un-Thesis, with design inputs and suggestions from bothie.
841 void CamuleApp::OnlineSig(bool zero /* reset stats (used on shutdown) */)
843 // Do not do anything if online signature is disabled in Preferences
844 if (!thePrefs::IsOnlineSignatureEnabled() || m_emulesig_path.IsEmpty()) {
845 // We do not need to check m_amulesig_path because if m_emulesig_path is empty,
846 // that means m_amulesig_path is empty too.
847 return;
850 // Remove old signature files
851 if ( wxFileExists( m_emulesig_path ) ) { wxRemoveFile( m_emulesig_path ); }
852 if ( wxFileExists( m_amulesig_path ) ) { wxRemoveFile( m_amulesig_path ); }
855 wxTextFile amulesig_out;
856 wxTextFile emulesig_out;
858 // Open both files if needed
859 if ( !emulesig_out.Create( m_emulesig_path) ) {
860 AddLogLineC(_("Failed to create OnlineSig File"));
861 // Will never try again.
862 m_amulesig_path.Clear();
863 m_emulesig_path.Clear();
864 return;
867 if ( !amulesig_out.Create(m_amulesig_path) ) {
868 AddLogLineC(_("Failed to create aMule OnlineSig File"));
869 // Will never try again.
870 m_amulesig_path.Clear();
871 m_emulesig_path.Clear();
872 return;
875 wxString emulesig_string;
876 wxString temp;
878 if (zero) {
879 emulesig_string = wxT("0\xA0.0|0.0|0");
880 amulesig_out.AddLine(wxT("0\n0\n0\n0\n0\n0\n0.0\n0.0\n0\n0"));
881 } else {
882 if (IsConnectedED2K()) {
884 temp = CFormat(wxT("%d")) % serverconnect->GetCurrentServer()->GetPort();
886 // We are online
887 emulesig_string =
888 // Connected
889 wxT("1|")
890 //Server name
891 + serverconnect->GetCurrentServer()->GetListName()
892 + wxT("|")
893 // IP and port of the server
894 + serverconnect->GetCurrentServer()->GetFullIP()
895 + wxT("|")
896 + temp;
899 // Now for amule sig
901 // Connected. State 1, full info
902 amulesig_out.AddLine(wxT("1"));
903 // Server Name
904 amulesig_out.AddLine(serverconnect->GetCurrentServer()->GetListName());
905 // Server IP
906 amulesig_out.AddLine(serverconnect->GetCurrentServer()->GetFullIP());
907 // Server Port
908 amulesig_out.AddLine(temp);
910 if (serverconnect->IsLowID()) {
911 amulesig_out.AddLine(wxT("L"));
912 } else {
913 amulesig_out.AddLine(wxT("H"));
916 } else if (serverconnect->IsConnecting()) {
917 emulesig_string = wxT("0");
919 // Connecting. State 2, No info.
920 amulesig_out.AddLine(wxT("2\n0\n0\n0\n0"));
921 } else {
922 // Not connected to a server
923 emulesig_string = wxT("0");
925 // Not connected, state 0, no info
926 amulesig_out.AddLine(wxT("0\n0\n0\n0\n0"));
928 if (IsConnectedKad()) {
929 if(Kademlia::CKademlia::IsFirewalled()) {
930 // Connected. Firewalled. State 1.
931 amulesig_out.AddLine(wxT("1"));
932 } else {
933 // Connected. State 2.
934 amulesig_out.AddLine(wxT("2"));
936 } else {
937 // Not connected.State 0.
938 amulesig_out.AddLine(wxT("0"));
940 emulesig_string += wxT("\xA");
942 // Datarate for downloads
943 temp = CFormat(wxT("%.1f")) % (theStats::GetDownloadRate() / 1024.0);
945 emulesig_string += temp + wxT("|");
946 amulesig_out.AddLine(temp);
948 // Datarate for uploads
949 temp = CFormat(wxT("%.1f")) % (theStats::GetUploadRate() / 1024.0);
951 emulesig_string += temp + wxT("|");
952 amulesig_out.AddLine(temp);
954 // Number of users waiting for upload
955 temp = CFormat(wxT("%d")) % theStats::GetWaitingUserCount();
957 emulesig_string += temp;
958 amulesig_out.AddLine(temp);
960 // Number of shared files (not on eMule)
961 amulesig_out.AddLine(CFormat(wxT("%d")) % theStats::GetSharedFileCount());
964 // eMule signature finished here. Write the line to the wxTextFile.
965 emulesig_out.AddLine(emulesig_string);
967 // Now for aMule signature extras
969 // Nick on the network
970 amulesig_out.AddLine(thePrefs::GetUserNick());
972 // Total received in bytes
973 amulesig_out.AddLine(CFormat(wxT("%llu")) % theStats::GetTotalReceivedBytes());
975 // Total sent in bytes
976 amulesig_out.AddLine(CFormat(wxT("%llu")) % theStats::GetTotalSentBytes());
978 // amule version
979 #ifdef SVNDATE
980 amulesig_out.AddLine(wxT(VERSION) wxT(" ") wxT(SVNDATE));
981 #else
982 amulesig_out.AddLine(wxT(VERSION));
983 #endif
985 if (zero) {
986 amulesig_out.AddLine(wxT("0"));
987 amulesig_out.AddLine(wxT("0"));
988 amulesig_out.AddLine(wxT("0"));
989 } else {
990 // Total received bytes in session
991 amulesig_out.AddLine( CFormat( wxT("%llu") ) %
992 theStats::GetSessionReceivedBytes() );
994 // Total sent bytes in session
995 amulesig_out.AddLine( CFormat( wxT("%llu") ) %
996 theStats::GetSessionSentBytes() );
998 // Uptime
999 amulesig_out.AddLine(CFormat(wxT("%llu")) % theStats::GetUptimeSeconds());
1002 // Flush the files
1003 emulesig_out.Write();
1004 amulesig_out.Write();
1005 } //End Added By Bouc7
1008 #if wxUSE_ON_FATAL_EXCEPTION
1009 // Gracefully handle fatal exceptions and print backtrace if possible
1010 void CamuleApp::OnFatalException()
1012 /* Print the backtrace */
1013 wxString msg;
1014 msg << wxT("\n--------------------------------------------------------------------------------\n")
1015 << wxT("A fatal error has occurred and aMule has crashed.\n")
1016 << wxT("Please assist us in fixing this problem by posting the backtrace below in our\n")
1017 << wxT("'aMule Crashes' forum and include as much information as possible regarding the\n")
1018 << wxT("circumstances of this crash. The forum is located here:\n")
1019 << wxT(" http://forum.amule.org/index.php?board=67.0\n")
1020 << wxT("If possible, please try to generate a real backtrace of this crash:\n")
1021 << wxT(" http://wiki.amule.org/wiki/Backtraces\n\n")
1022 << wxT("----------------------------=| BACKTRACE FOLLOWS: |=----------------------------\n")
1023 << wxT("Current version is: ") << FullMuleVersion
1024 << wxT("\nRunning on: ") << OSDescription
1025 << wxT("\n\n")
1026 << get_backtrace(1) // 1 == skip this function.
1027 << wxT("\n--------------------------------------------------------------------------------\n");
1029 theLogger.EmergencyLog(msg, true);
1031 #endif
1034 // Sets the localization of aMule
1035 void CamuleApp::Localize_mule()
1037 InitCustomLanguages();
1038 InitLocale(m_locale, StrLang2wx(thePrefs::GetLanguageID()));
1039 if (!m_locale.IsOk()) {
1040 AddLogLineN(_("The selected locale seems not to be installed on your box. (Note: I'll try to set it anyway)"));
1045 // Displays information related to important changes in aMule.
1046 // Is called when the user runs a new version of aMule
1047 void CamuleApp::Trigger_New_version(wxString new_version)
1049 wxString info = wxT(" --- ") + CFormat(_("This is the first time you run aMule %s")) % new_version + wxT(" ---\n\n");
1050 if (new_version == wxT("SVN")) {
1051 info += _("This version is a testing version, updated daily, and\n");
1052 info += _("we give no warranty it won't break anything, burn your house,\n");
1053 info += _("or kill your dog. But it *should* be safe to use anyway.\n");
1056 // General info
1057 info += wxT("\n");
1058 info += _("More information, support and new releases can found at our homepage,\n");
1059 info += _("at www.aMule.org, or in our IRC channel #aMule at irc.freenode.net.\n");
1060 info += wxT("\n");
1061 info += _("Feel free to report any bugs to http://forum.amule.org");
1063 ShowAlert(info, _("Info"), wxCENTRE | wxOK | wxICON_ERROR);
1067 void CamuleApp::SetOSFiles(const wxString& new_path)
1069 if ( thePrefs::IsOnlineSignatureEnabled() ) {
1070 if ( ::wxDirExists(new_path) ) {
1071 m_emulesig_path = JoinPaths(new_path, wxT("onlinesig.dat"));
1072 m_amulesig_path = JoinPaths(new_path, wxT("amulesig.dat"));
1073 } else {
1074 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);
1075 m_emulesig_path.Clear();
1076 m_amulesig_path.Clear();
1078 } else {
1079 m_emulesig_path.Clear();
1080 m_amulesig_path.Clear();
1085 #ifdef __WXDEBUG__
1086 #ifndef wxUSE_STACKWALKER
1087 #define wxUSE_STACKWALKER 0
1088 #endif
1089 void CamuleApp::OnAssertFailure(const wxChar* file, int line,
1090 const wxChar* func, const wxChar* cond, const wxChar* msg)
1092 wxString errmsg = CFormat( wxT("Assertion failed: %s:%s:%d: Assertion '%s' failed. %s\nBacktrace follows:\n%s\n") )
1093 % file % func % line % cond % ( msg ? msg : wxT("") )
1094 % get_backtrace(2); // Skip the function-calls directly related to the assert call.
1095 theLogger.EmergencyLog(errmsg, false);
1097 if (wxThread::IsMain() && IsRunning()) {
1098 AMULE_APP_BASE::OnAssertFailure(file, line, func, cond, msg);
1099 } else {
1100 #ifdef _MSC_VER
1101 wxString s = CFormat(wxT("%s in %s")) % cond % func;
1102 if (msg) {
1103 s << wxT(" : ") << msg;
1105 _wassert(s.wc_str(), file, line);
1106 #else
1107 // Abort, allows gdb to catch the assertion
1108 raise( SIGABRT );
1109 #endif
1112 #endif
1115 void CamuleApp::OnUDPDnsDone(CMuleInternalEvent& evt)
1117 CServerUDPSocket* socket =reinterpret_cast<CServerUDPSocket*>(evt.GetClientData());
1118 socket->OnHostnameResolved(evt.GetExtraLong());
1122 void CamuleApp::OnSourceDnsDone(CMuleInternalEvent& evt)
1124 downloadqueue->OnHostnameResolved(evt.GetExtraLong());
1128 void CamuleApp::OnServerDnsDone(CMuleInternalEvent& evt)
1130 AddLogLineNS(_("Server hostname notified"));
1131 serverconnect->OnServerHostnameResolved(evt.GetClientData(), evt.GetExtraLong());
1135 void CamuleApp::OnTCPTimer(CTimerEvent& WXUNUSED(evt))
1137 if(!IsRunning()) {
1138 return;
1140 serverconnect->StopConnectionTry();
1141 if (IsConnectedED2K() ) {
1142 return;
1144 serverconnect->ConnectToAnyServer();
1148 void CamuleApp::OnCoreTimer(CTimerEvent& WXUNUSED(evt))
1150 // Former TimerProc section
1151 static uint64 msPrev1, msPrev5, msPrevSave, msPrevHist, msPrevOS, msPrevKnownMet;
1152 uint64 msCur = theStats::GetUptimeMillis();
1153 TheTime = msCur / 1000;
1155 if (!IsRunning()) {
1156 return;
1159 #ifndef AMULE_DAEMON
1160 // Check if we should terminate the app
1161 if ( g_shutdownSignal ) {
1162 wxWindow* top = GetTopWindow();
1164 if ( top ) {
1165 top->Close(true);
1166 } else {
1167 // No top-window, have to force termination.
1168 wxExit();
1171 #endif
1173 // There is a theoretical chance that the core time function can recurse:
1174 // if an event function gets blocked on a mutex (communicating with the
1175 // UploadBandwidthThrottler) wx spawns a new event loop and processes more events.
1176 // If CPU load gets high a new core timer event could be generated before the last
1177 // one was finished and so recursion could occur, which would be bad.
1178 // Detect this and do an early return then.
1179 static bool recurse = false;
1180 if (recurse) {
1181 return;
1183 recurse = true;
1185 uploadqueue->Process();
1186 downloadqueue->Process();
1187 //theApp->clientcredits->Process();
1188 theStats::CalculateRates();
1190 if (msCur-msPrevHist > 1000) {
1191 // unlike the other loop counters in this function this one will sometimes
1192 // produce two calls in quick succession (if there was a gap of more than one
1193 // second between calls to TimerProc) - this is intentional! This way the
1194 // history list keeps an average of one node per second and gets thinned out
1195 // correctly as time progresses.
1196 msPrevHist += 1000;
1198 m_statistics->RecordHistory();
1203 if (msCur-msPrev1 > 1000) { // approximately every second
1204 msPrev1 = msCur;
1205 clientcredits->Process();
1206 clientlist->Process();
1208 // Publish files to server if needed.
1209 sharedfiles->Process();
1211 if( Kademlia::CKademlia::IsRunning() ) {
1212 Kademlia::CKademlia::Process();
1213 if(Kademlia::CKademlia::GetPrefs()->HasLostConnection()) {
1214 StopKad();
1215 clientudp->Close();
1216 clientudp->Open();
1217 if (thePrefs::Reconnect()) {
1218 StartKad();
1223 if( serverconnect->IsConnecting() && !serverconnect->IsSingleConnect() ) {
1224 serverconnect->TryAnotherConnectionrequest();
1226 if (serverconnect->IsConnecting()) {
1227 serverconnect->CheckForTimeout();
1229 listensocket->UpdateConnectionsStatus();
1234 if (msCur-msPrev5 > 5000) { // every 5 seconds
1235 msPrev5 = msCur;
1236 listensocket->Process();
1239 if (msCur-msPrevSave >= 60000) {
1240 msPrevSave = msCur;
1241 theStats::Save();
1244 // Special
1245 if (msCur - msPrevOS >= thePrefs::GetOSUpdate() * 1000ull) {
1246 OnlineSig(); // Added By Bouc7
1247 msPrevOS = msCur;
1250 if (msCur - msPrevKnownMet >= 30*60*1000/*There must be a prefs option for this*/) {
1251 // Save Shared Files data
1252 knownfiles->Save();
1253 msPrevKnownMet = msCur;
1257 // Recomended by lugdunummaster himself - from emule 0.30c
1258 serverconnect->KeepConnectionAlive();
1260 // Disarm recursion protection
1261 recurse = false;
1265 void CamuleApp::OnFinishedHashing(CHashingEvent& evt)
1267 wxCHECK_RET(evt.GetResult(), wxT("No result of hashing"));
1269 CKnownFile* owner = const_cast<CKnownFile*>(evt.GetOwner());
1270 CKnownFile* result = evt.GetResult();
1272 if (owner) {
1273 // Check if the partfile still exists, as it might have
1274 // been deleted in the mean time.
1275 if (downloadqueue->IsPartFile(owner)) {
1276 // This cast must not be done before the IsPartFile
1277 // call, as dynamic_cast will barf on dangling pointers.
1278 dynamic_cast<CPartFile*>(owner)->PartFileHashFinished(result);
1280 } else {
1281 static uint64 bytecount = 0;
1283 if (knownfiles->SafeAddKFile(result, true)) {
1284 AddDebugLogLineN(logKnownFiles,
1285 CFormat(wxT("Safe adding file to sharedlist: %s")) % result->GetFileName());
1286 sharedfiles->SafeAddKFile(result);
1288 bytecount += result->GetFileSize();
1289 // If we have added files with a total size of ~3000mb
1290 if (bytecount >= wxULL(3145728000)) {
1291 AddDebugLogLineN(logKnownFiles, wxT("Failsafe for crash on file hashing creation"));
1292 if ( m_app_state != APP_STATE_SHUTTINGDOWN ) {
1293 knownfiles->Save();
1294 bytecount = 0;
1297 } else {
1298 AddDebugLogLineN(logKnownFiles,
1299 CFormat(wxT("File not added to sharedlist: %s")) % result->GetFileName());
1300 delete result;
1306 void CamuleApp::OnFinishedAICHHashing(CHashingEvent& evt)
1308 wxCHECK_RET(evt.GetResult(), wxT("No result of AICH-hashing"));
1310 CKnownFile* owner = const_cast<CKnownFile*>(evt.GetOwner());
1311 CScopedPtr<CKnownFile> result(evt.GetResult());
1313 if (result->GetAICHHashset()->GetStatus() == AICH_HASHSETCOMPLETE) {
1314 CAICHHashSet* oldSet = owner->GetAICHHashset();
1315 CAICHHashSet* newSet = result->GetAICHHashset();
1317 owner->SetAICHHashset(newSet);
1318 newSet->SetOwner(owner);
1320 result->SetAICHHashset(oldSet);
1321 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->SetStatus(PS_EMPTY);
1349 if (evt.Succeeded()) {
1350 if (evt.IsPaused()) {
1351 file->StopFile();
1352 } else {
1353 file->ResumeFile();
1355 } else {
1356 AddLogLineN(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 #ifdef AMULE_DAEMON
1366 evt.Notify();
1367 #else
1368 if (theApp->amuledlg) {
1369 evt.Notify();
1371 #endif
1375 void CamuleApp::ShutDown()
1377 // Just in case
1378 PlatformSpecific::AllowSleepMode();
1380 // Log
1381 AddDebugLogLineN(logGeneral, wxT("CamuleApp::ShutDown() has started."));
1383 // Signal the hashing thread to terminate
1384 m_app_state = APP_STATE_SHUTTINGDOWN;
1386 // Stop ASIO thread
1387 #ifdef ASIO_SOCKETS // only needed to suppress the log message in non-Asio build
1388 AddDebugLogLineN(logGeneral, wxT("Terminate ASIO thread."));
1389 m_AsioService->Stop();
1390 #endif
1392 StopKad();
1394 // Kry - Save the sources seeds on app exit
1395 if (thePrefs::GetSrcSeedsOn()) {
1396 downloadqueue->SaveSourceSeeds();
1399 OnlineSig(true); // Added By Bouc7
1401 // Exit HTTP downloads
1402 CHTTPDownloadThread::StopAll();
1404 // Exit thread scheduler and upload thread
1405 CThreadScheduler::Terminate();
1407 AddDebugLogLineN(logGeneral, wxT("Terminate upload thread."));
1408 uploadBandwidthThrottler->EndThread();
1410 // Close sockets to avoid new clients coming in
1411 if (listensocket) {
1412 listensocket->Close();
1413 listensocket->KillAllSockets();
1416 if (serverconnect) {
1417 serverconnect->Disconnect();
1420 ECServerHandler->KillAllSockets();
1422 #ifdef ENABLE_UPNP
1423 if (thePrefs::GetUPnPEnabled()) {
1424 if (m_upnp) {
1425 m_upnp->DeletePortMappings(m_upnpMappings);
1428 #endif
1430 // saving data & stuff
1431 if (knownfiles) {
1432 knownfiles->Save();
1435 theStats::Save();
1437 CPath configFileName = CPath(thePrefs::GetConfigDir() + m_configFile);
1438 CPath::BackupFile(configFileName, wxT(".bak"));
1440 if (clientlist) {
1441 clientlist->DeleteAll();
1444 // Log
1445 AddDebugLogLineN(logGeneral, wxT("CamuleApp::ShutDown() has ended."));
1449 bool CamuleApp::AddServer(CServer *srv, bool fromUser)
1451 if ( serverlist->AddServer(srv, fromUser) ) {
1452 Notify_ServerAdd(srv);
1453 return true;
1455 return false;
1459 uint32 CamuleApp::GetPublicIP(bool ignorelocal) const
1461 if (m_dwPublicIP == 0) {
1462 if (Kademlia::CKademlia::IsConnected() && Kademlia::CKademlia::GetIPAddress() ) {
1463 return wxUINT32_SWAP_ALWAYS(Kademlia::CKademlia::GetIPAddress());
1464 } else {
1465 return ignorelocal ? 0 : m_localip;
1469 return m_dwPublicIP;
1473 void CamuleApp::SetPublicIP(const uint32 dwIP)
1475 wxASSERT((dwIP == 0) || !IsLowID(dwIP));
1477 if (dwIP != 0 && dwIP != m_dwPublicIP && serverlist != NULL) {
1478 m_dwPublicIP = dwIP;
1479 serverlist->CheckForExpiredUDPKeys();
1480 } else {
1481 m_dwPublicIP = dwIP;
1486 wxString CamuleApp::GetLog(bool reset)
1488 wxFile logfile;
1489 logfile.Open(thePrefs::GetConfigDir() + wxT("logfile"));
1490 if ( !logfile.IsOpened() ) {
1491 return _("ERROR: can't open logfile");
1493 int len = logfile.Length();
1494 if ( len == 0 ) {
1495 return _("WARNING: logfile is empty. Something is wrong.");
1497 char *tmp_buffer = new char[len + sizeof(wxChar)];
1498 logfile.Read(tmp_buffer, len);
1499 memset(tmp_buffer + len, 0, sizeof(wxChar));
1501 // try to guess file format
1502 wxString str;
1503 if (tmp_buffer[0] && tmp_buffer[1]) {
1504 str = wxString(UTF82unicode(tmp_buffer));
1505 } else {
1506 str = wxWCharBuffer((wchar_t *)tmp_buffer);
1509 delete [] tmp_buffer;
1510 if ( reset ) {
1511 theLogger.CloseLogfile();
1512 if (theLogger.OpenLogfile(thePrefs::GetConfigDir() + wxT("logfile"))) {
1513 AddLogLineN(_("Log has been reset"));
1515 ECServerHandler->ResetAllLogs();
1517 return str;
1521 wxString CamuleApp::GetServerLog(bool reset)
1523 wxString ret = server_msg;
1524 if ( reset ) {
1525 server_msg.Clear();
1527 return ret;
1530 wxString CamuleApp::GetDebugLog(bool reset)
1532 return GetLog(reset);
1536 void CamuleApp::AddServerMessageLine(wxString &msg)
1538 server_msg += msg + wxT("\n");
1539 AddLogLineN(CFormat(_("ServerMessage: %s")) % msg);
1544 void CamuleApp::OnFinishedHTTPDownload(CMuleInternalEvent& event)
1546 switch (event.GetInt()) {
1547 case HTTP_IPFilter:
1548 ipfilter->DownloadFinished(event.GetExtraLong());
1549 break;
1550 case HTTP_ServerMet:
1551 if (serverlist->DownloadFinished(event.GetExtraLong()) && !IsConnectedED2K()) {
1552 // If successfully downloaded a server list, and are not connected at the moment, try to connect.
1553 // This happens when no server met is available on startup.
1554 serverconnect->ConnectToAnyServer();
1556 break;
1557 case HTTP_ServerMetAuto:
1558 serverlist->AutoDownloadFinished(event.GetExtraLong());
1559 break;
1560 case HTTP_VersionCheck:
1561 CheckNewVersion(event.GetExtraLong());
1562 break;
1563 case HTTP_NodesDat:
1564 if (event.GetExtraLong() == HTTP_Success) {
1566 wxString file = thePrefs::GetConfigDir() + wxT("nodes.dat");
1567 if (wxFileExists(file)) {
1568 wxRemoveFile(file);
1571 if ( Kademlia::CKademlia::IsRunning() ) {
1572 Kademlia::CKademlia::Stop();
1575 wxRenameFile(file + wxT(".download"),file);
1577 Kademlia::CKademlia::Start();
1578 theApp->ShowConnectionState();
1579 // cppcheck-suppress duplicateBranch
1580 } else if (event.GetExtraLong() == HTTP_Skipped) {
1581 AddLogLineN(CFormat(_("Skipped download of %s, because requested file is not newer.")) % wxT("nodes.dat"));
1582 } else {
1583 AddLogLineC(_("Failed to download the nodes list."));
1585 break;
1586 #ifdef ENABLE_IP2COUNTRY
1587 case HTTP_GeoIP:
1588 theApp->amuledlg->IP2CountryDownloadFinished(event.GetExtraLong());
1589 // If we updated, the dialog is already up. Redraw it to show the flags.
1590 theApp->amuledlg->Refresh();
1591 break;
1592 #endif
1596 void CamuleApp::CheckNewVersion(uint32 result)
1598 if (result == HTTP_Success) {
1599 wxString filename = thePrefs::GetConfigDir() + wxT("last_version_check");
1600 wxTextFile file;
1602 if (!file.Open(filename)) {
1603 AddLogLineC(_("Failed to open the downloaded version check file") );
1604 return;
1605 } else if (!file.GetLineCount()) {
1606 AddLogLineC(_("Corrupted version check file"));
1607 } else {
1608 wxString versionLine = file.GetFirstLine();
1609 wxStringTokenizer tkz(versionLine, wxT("."));
1611 AddDebugLogLineN(logGeneral, wxString(wxT("Running: ")) + wxT(VERSION) + wxT(", Version check: ") + versionLine);
1613 long fields[] = {0, 0, 0};
1614 for (int i = 0; i < 3; ++i) {
1615 if (!tkz.HasMoreTokens()) {
1616 AddLogLineC(_("Corrupted version check file"));
1617 return;
1618 } else {
1619 wxString token = tkz.GetNextToken();
1621 if (!token.ToLong(&fields[i])) {
1622 AddLogLineC(_("Corrupted version check file"));
1623 return;
1628 long curVer = make_full_ed2k_version(VERSION_MJR, VERSION_MIN, VERSION_UPDATE);
1629 long newVer = make_full_ed2k_version(fields[0], fields[1], fields[2]);
1631 if (curVer < newVer) {
1632 AddLogLineC(_("You are using an outdated version of aMule!"));
1633 // cppcheck-suppress zerodiv
1634 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]);
1635 AddLogLineN(_("The latest version can always be found at http://www.amule.org"));
1636 #ifdef AMULE_DAEMON
1637 AddLogLineCS(CFormat(_("WARNING: Your aMuled version is outdated: %i.%i.%i < %li.%li.%li"))
1638 % VERSION_MJR % VERSION_MIN % VERSION_UPDATE % fields[0] % fields[1] % fields[2]);
1639 #endif
1640 } else {
1641 AddLogLineN(_("Your copy of aMule is up to date."));
1645 file.Close();
1646 wxRemoveFile(filename);
1647 } else {
1648 AddLogLineC(_("Failed to download the version check file"));
1654 bool CamuleApp::IsConnected() const
1656 return (IsConnectedED2K() || IsConnectedKad());
1660 bool CamuleApp::IsConnectedED2K() const
1662 return serverconnect && serverconnect->IsConnected();
1666 bool CamuleApp::IsConnectedKad() const
1668 return Kademlia::CKademlia::IsConnected();
1672 bool CamuleApp::IsFirewalled() const
1674 if (theApp->IsConnectedED2K() && !theApp->serverconnect->IsLowID()) {
1675 return false; // we have an eD2K HighID -> not firewalled
1678 return IsFirewalledKad(); // If kad says ok, it's ok.
1681 bool CamuleApp::IsFirewalledKad() const
1683 return !Kademlia::CKademlia::IsConnected() // not connected counts as firewalled
1684 || Kademlia::CKademlia::IsFirewalled();
1687 bool CamuleApp::IsFirewalledKadUDP() const
1689 return !Kademlia::CKademlia::IsConnected() // not connected counts as firewalled
1690 || Kademlia::CUDPFirewallTester::IsFirewalledUDP(true);
1693 bool CamuleApp::IsKadRunning() const
1695 return Kademlia::CKademlia::IsRunning();
1698 bool CamuleApp::IsKadRunningInLanMode() const
1700 return Kademlia::CKademlia::IsRunningInLANMode();
1703 // Kad stats
1704 uint32 CamuleApp::GetKadUsers() const
1706 return Kademlia::CKademlia::GetKademliaUsers();
1709 uint32 CamuleApp::GetKadFiles() const
1711 return Kademlia::CKademlia::GetKademliaFiles();
1714 uint32 CamuleApp::GetKadIndexedSources() const
1716 return Kademlia::CKademlia::GetIndexed()->m_totalIndexSource;
1719 uint32 CamuleApp::GetKadIndexedKeywords() const
1721 return Kademlia::CKademlia::GetIndexed()->m_totalIndexKeyword;
1724 uint32 CamuleApp::GetKadIndexedNotes() const
1726 return Kademlia::CKademlia::GetIndexed()->m_totalIndexNotes;
1729 uint32 CamuleApp::GetKadIndexedLoad() const
1731 return Kademlia::CKademlia::GetIndexed()->m_totalIndexLoad;
1735 // True IP of machine
1736 uint32 CamuleApp::GetKadIPAdress() const
1738 return wxUINT32_SWAP_ALWAYS(Kademlia::CKademlia::GetPrefs()->GetIPAddress());
1741 // Buddy status
1742 uint8 CamuleApp::GetBuddyStatus() const
1744 return clientlist->GetBuddyStatus();
1747 uint32 CamuleApp::GetBuddyIP() const
1749 return clientlist->GetBuddyIP();
1752 uint32 CamuleApp::GetBuddyPort() const
1754 return clientlist->GetBuddyPort();
1757 const Kademlia::CUInt128& CamuleApp::GetKadID() const
1759 return Kademlia::CKademlia::GetKadID();
1762 bool CamuleApp::CanDoCallback(uint32 clientServerIP, uint16 clientServerPort)
1764 if (Kademlia::CKademlia::IsConnected()) {
1765 if (IsConnectedED2K()) {
1766 if (serverconnect->IsLowID()) {
1767 if (Kademlia::CKademlia::IsFirewalled()) {
1768 //Both Connected - Both Firewalled
1769 return false;
1770 } else {
1771 if (clientServerIP == theApp->serverconnect->GetCurrentServer()->GetIP() &&
1772 clientServerPort == theApp->serverconnect->GetCurrentServer()->GetPort()) {
1773 // Both Connected - Server lowID, Kad Open - Client on same server
1774 // We prevent a callback to the server as this breaks the protocol
1775 // and will get you banned.
1776 return false;
1777 } else {
1778 // Both Connected - Server lowID, Kad Open - Client on remote server
1779 return true;
1782 } else {
1783 //Both Connected - Server HighID, Kad don't care
1784 return true;
1786 } else {
1787 if (Kademlia::CKademlia::IsFirewalled()) {
1788 //Only Kad Connected - Kad Firewalled
1789 return false;
1790 } else {
1791 //Only Kad Conected - Kad Open
1792 return true;
1795 } else {
1796 if (IsConnectedED2K()) {
1797 if (serverconnect->IsLowID()) {
1798 //Only Server Connected - Server LowID
1799 return false;
1800 } else {
1801 //Only Server Connected - Server HighID
1802 return true;
1804 } else {
1805 //We are not connected at all!
1806 return false;
1811 void CamuleApp::ShowUserCount() {
1812 uint32 totaluser = 0, totalfile = 0;
1814 theApp->serverlist->GetUserFileStatus( totaluser, totalfile );
1816 wxString buffer;
1818 static const wxString s_singlenetstatusformat = _("Users: %s | Files: %s");
1819 static const wxString s_bothnetstatusformat = _("Users: E: %s K: %s | Files: E: %s K: %s");
1821 if (thePrefs::GetNetworkED2K() && thePrefs::GetNetworkKademlia()) {
1822 buffer = CFormat(s_bothnetstatusformat) % CastItoIShort(totaluser) % CastItoIShort(Kademlia::CKademlia::GetKademliaUsers()) % CastItoIShort(totalfile) % CastItoIShort(Kademlia::CKademlia::GetKademliaFiles());
1823 } else if (thePrefs::GetNetworkED2K()) {
1824 buffer = CFormat(s_singlenetstatusformat) % CastItoIShort(totaluser) % CastItoIShort(totalfile);
1825 } else if (thePrefs::GetNetworkKademlia()) {
1826 buffer = CFormat(s_singlenetstatusformat) % CastItoIShort(Kademlia::CKademlia::GetKademliaUsers()) % CastItoIShort(Kademlia::CKademlia::GetKademliaFiles());
1827 } else {
1828 buffer = _("No networks selected");
1831 Notify_ShowUserCount(buffer);
1835 #ifndef ASIO_SOCKETS
1836 void CamuleApp::ListenSocketHandler(wxSocketEvent& event)
1838 { wxCHECK_RET(listensocket, wxT("Connection-event for NULL'd listen-socket")); }
1839 { wxCHECK_RET(event.GetSocketEvent() == wxSOCKET_CONNECTION,
1840 wxT("Invalid event received for listen-socket")); }
1842 if (m_app_state == APP_STATE_RUNNING) {
1843 listensocket->OnAccept();
1844 } else if (m_app_state == APP_STATE_STARTING) {
1845 // When starting up, connection may be made before we are able
1846 // to handle them. However, if these are ignored, no futher
1847 // connection-events will be triggered, so we have to accept it.
1848 CLibSocket* socket = listensocket->Accept(false);
1850 wxCHECK_RET(socket, wxT("NULL returned by Accept() during startup"));
1852 socket->Destroy();
1855 #endif
1858 void CamuleApp::ShowConnectionState(bool forceUpdate)
1860 static uint8 old_state = (1<<7); // This flag doesn't exist
1862 uint8 state = 0;
1864 if (theApp->serverconnect->IsConnected()) {
1865 state |= CONNECTED_ED2K;
1868 if (Kademlia::CKademlia::IsRunning()) {
1869 if (Kademlia::CKademlia::IsConnected()) {
1870 if (!Kademlia::CKademlia::IsFirewalled()) {
1871 state |= CONNECTED_KAD_OK;
1872 } else {
1873 state |= CONNECTED_KAD_FIREWALLED;
1875 } else {
1876 state |= CONNECTED_KAD_NOT;
1880 if (old_state != state) {
1881 // Get the changed value
1882 int changed_flags = old_state ^ state;
1884 if (changed_flags & CONNECTED_ED2K) {
1885 // ED2K status changed
1886 wxString connected_server;
1887 CServer* ed2k_server = theApp->serverconnect->GetCurrentServer();
1888 if (ed2k_server) {
1889 connected_server = ed2k_server->GetListName();
1891 if (state & CONNECTED_ED2K) {
1892 // We connected to some server
1893 const wxString id = theApp->serverconnect->IsLowID() ? _("with LowID") : _("with HighID");
1895 AddLogLineC(CFormat(_("Connected to %s %s")) % connected_server % id);
1896 } else {
1897 // cppcheck-suppress duplicateBranch
1898 if ( theApp->serverconnect->IsConnecting() ) {
1899 AddLogLineC(CFormat(_("Connecting to %s")) % connected_server);
1900 } else {
1901 AddLogLineC(_("Disconnected from eD2k"));
1906 if (changed_flags & CONNECTED_KAD_NOT) {
1907 // cppcheck-suppress duplicateBranch
1908 if (state & CONNECTED_KAD_NOT) {
1909 AddLogLineC(_("Kad started."));
1910 } else {
1911 AddLogLineC(_("Kad stopped."));
1915 if (changed_flags & (CONNECTED_KAD_OK | CONNECTED_KAD_FIREWALLED)) {
1916 if (state & (CONNECTED_KAD_OK | CONNECTED_KAD_FIREWALLED)) {
1917 // cppcheck-suppress duplicateBranch
1918 if (state & CONNECTED_KAD_OK) {
1919 AddLogLineC(_("Connected to Kad (ok)"));
1920 } else {
1921 AddLogLineC(_("Connected to Kad (firewalled)"));
1923 } else {
1924 AddLogLineC(_("Disconnected from Kad"));
1928 old_state = state;
1930 theApp->downloadqueue->OnConnectionState(IsConnected());
1933 ShowUserCount();
1934 Notify_ShowConnState(forceUpdate);
1938 #ifndef ASIO_SOCKETS
1939 void CamuleApp::UDPSocketHandler(wxSocketEvent& event)
1941 CMuleUDPSocket* socket = reinterpret_cast<CMuleUDPSocket*>(event.GetClientData());
1942 wxCHECK_RET(socket, wxT("No socket owner specified."));
1944 if (IsOnShutDown() || thePrefs::IsUDPDisabled()) return;
1946 if (!IsRunning()) {
1947 if (event.GetSocketEvent() == wxSOCKET_INPUT) {
1948 // Back to the queue!
1949 theApp->AddPendingEvent(event);
1950 return;
1954 switch (event.GetSocketEvent()) {
1955 case wxSOCKET_INPUT:
1956 socket->OnReceive(0);
1957 break;
1959 case wxSOCKET_OUTPUT:
1960 socket->OnSend(0);
1961 break;
1963 case wxSOCKET_LOST:
1964 socket->OnDisconnected(0);
1965 break;
1967 default:
1968 wxFAIL;
1969 break;
1972 #endif
1975 void CamuleApp::OnUnhandledException()
1977 // Call the generic exception-handler.
1978 fprintf(stderr, "\taMule Version: %s\n", (const char*)unicode2char(GetFullMuleVersion()));
1979 ::OnUnhandledException();
1982 void CamuleApp::StartKad()
1984 if (!Kademlia::CKademlia::IsRunning() && thePrefs::GetNetworkKademlia()) {
1985 // Kad makes no sense without the Client-UDP socket.
1986 if (!thePrefs::IsUDPDisabled()) {
1987 if (ipfilter->IsReady()) {
1988 Kademlia::CKademlia::Start();
1989 } else {
1990 ipfilter->StartKADWhenReady();
1992 } else {
1993 AddLogLineC(_("Kad network cannot be used if UDP port is disabled on preferences, not starting."));
1995 } else if (!thePrefs::GetNetworkKademlia()) {
1996 AddLogLineC(_("Kad network disabled on preferences, not connecting."));
2000 void CamuleApp::StopKad()
2002 // Stop Kad if it's running
2003 if (Kademlia::CKademlia::IsRunning()) {
2004 Kademlia::CKademlia::Stop();
2009 void CamuleApp::BootstrapKad(uint32 ip, uint16 port)
2011 if (!Kademlia::CKademlia::IsRunning()) {
2012 Kademlia::CKademlia::Start();
2013 theApp->ShowConnectionState();
2016 Kademlia::CKademlia::Bootstrap(ip, port);
2020 void CamuleApp::UpdateNotesDat(const wxString& url)
2022 wxString strTempFilename(thePrefs::GetConfigDir() + wxT("nodes.dat.download"));
2024 CHTTPDownloadThread *downloader = new CHTTPDownloadThread(url, strTempFilename, thePrefs::GetConfigDir() + wxT("nodes.dat"), HTTP_NodesDat, true, false);
2025 downloader->Create();
2026 downloader->Run();
2030 void CamuleApp::DisconnectED2K()
2032 // Stop ED2K if it's running
2033 if (IsConnectedED2K()) {
2034 serverconnect->Disconnect();
2038 bool CamuleApp::CryptoAvailable() const
2040 return clientcredits && clientcredits->CryptoAvailable();
2043 uint32 CamuleApp::GetED2KID() const {
2044 return serverconnect ? serverconnect->GetClientID() : 0;
2047 uint32 CamuleApp::GetID() const {
2048 uint32 ID;
2050 if( Kademlia::CKademlia::IsConnected() && !Kademlia::CKademlia::IsFirewalled() ) {
2051 // We trust Kad above ED2K
2052 ID = ENDIAN_NTOHL(Kademlia::CKademlia::GetIPAddress());
2053 } else if( theApp->serverconnect->IsConnected() ) {
2054 ID = theApp->serverconnect->GetClientID();
2055 } else if ( Kademlia::CKademlia::IsConnected() && Kademlia::CKademlia::IsFirewalled() ) {
2056 // A firewalled Kad client get's a "1"
2057 ID = 1;
2058 } else {
2059 ID = 0;
2062 return ID;
2065 DEFINE_LOCAL_EVENT_TYPE(wxEVT_CORE_FINISHED_HTTP_DOWNLOAD)
2066 DEFINE_LOCAL_EVENT_TYPE(wxEVT_CORE_SOURCE_DNS_DONE)
2067 DEFINE_LOCAL_EVENT_TYPE(wxEVT_CORE_UDP_DNS_DONE)
2068 DEFINE_LOCAL_EVENT_TYPE(wxEVT_CORE_SERVER_DNS_DONE)
2069 // File_checked_for_headers