Upstream tarball 10142
[amule.git] / src / amule.cpp
blobb32604997a1b255f8bb3b49674ab7a38b3de25ce
1 //
2 // This file is part of the aMule Project.
3 //
4 // Copyright (c) 2003-2008 aMule Team ( admin@amule.org / http://www.amule.org )
5 // Copyright (c) 2002-2008 Merkur ( devs@emule-project.net / http://www.emule-project.net )
6 //
7 // Any parts of this program derived from the xMule, lMule or eMule project,
8 // or contributed by third-party developers are copyrighted by their
9 // respective authors.
11 // This program is free software; you can redistribute it and/or modify
12 // it under the terms of the GNU General Public License as published by
13 // the Free Software Foundation; either version 2 of the License, or
14 // (at your option) any later version.
16 // This program is distributed in the hope that it will be useful,
17 // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 // GNU General Public License for more details.
20 //
21 // You should have received a copy of the GNU General Public License
22 // along with this program; if not, write to the Free Software
23 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
27 #include "amule.h" // Interface declarations.
29 #include <csignal>
30 #include <cstring>
31 #include <wx/process.h>
32 #include <wx/sstream.h>
34 #ifdef HAVE_CONFIG_H
35 #include "config.h" // Needed for HAVE_GETRLIMIT, HAVE_SETRLIMIT,
36 // HAVE_SYS_RESOURCE_H, HAVE_SYS_STATVFS_H, VERSION
37 // and ENABLE_NLS
38 #endif
40 #include <common/ClientVersion.h>
42 #include <wx/cmdline.h> // Needed for wxCmdLineParser
43 #include <wx/config.h> // Do_not_auto_remove (win32)
44 #include <wx/fileconf.h>
45 #include <wx/tokenzr.h>
46 #include <wx/wfstream.h>
49 #include <common/Format.h> // Needed for CFormat
50 #include "kademlia/kademlia/Kademlia.h"
51 #include "kademlia/kademlia/Prefs.h"
52 #include "kademlia/kademlia/UDPFirewallTester.h"
53 #include "CanceledFileList.h"
54 #include "ClientCreditsList.h" // Needed for CClientCreditsList
55 #include "ClientList.h" // Needed for CClientList
56 #include "ClientUDPSocket.h" // Needed for CClientUDPSocket & CMuleUDPSocket
57 #include "ExternalConn.h" // Needed for ExternalConn & MuleConnection
58 #include <common/FileFunctions.h> // Needed for CDirIterator
59 #include "FriendList.h" // Needed for CFriendList
60 #include "HTTPDownload.h" // Needed for CHTTPDownloadThread
61 #include "InternalEvents.h" // Needed for CMuleInternalEvent
62 #include "IPFilter.h" // Needed for CIPFilter
63 #include "KnownFileList.h" // Needed for CKnownFileList
64 #include "ListenSocket.h" // Needed for CListenSocket
65 #include "Logger.h" // Needed for CLogger // Do_not_auto_remove
66 #include "MagnetURI.h" // Needed for CMagnetURI
67 #include "OtherFunctions.h"
68 #include "PartFile.h" // Needed for CPartFile
69 #include "Preferences.h" // Needed for CPreferences
70 #include "SearchList.h" // Needed for CSearchList
71 #include "Server.h" // Needed for GetListName
72 #include "ServerList.h" // Needed for CServerList
73 #include "ServerConnect.h" // Needed for CServerConnect
74 #include "ServerUDPSocket.h" // Needed for CServerUDPSocket
75 #include "Statistics.h" // Needed for CStatistics
76 #include "TerminationProcessAmuleweb.h" // Needed for CTerminationProcessAmuleweb
77 #include "ThreadTasks.h"
78 #include "updownclient.h" // Needed for CUpDownClient
79 #include "UploadQueue.h" // Needed for CUploadQueue
80 #include "UploadBandwidthThrottler.h"
81 #include "UserEvents.h"
82 #include "ScopedPtr.h"
84 #ifdef ENABLE_UPNP
85 #include "UPnPBase.h" // Needed for UPnP
86 #endif
88 #ifdef __WXMAC__
89 #include <wx/sysopt.h> // Do_not_auto_remove
90 #endif
92 #ifndef AMULE_DAEMON
93 #ifdef __WXMAC__
94 #include <CoreFoundation/CFBundle.h> // Do_not_auto_remove
95 #if wxCHECK_VERSION(2, 9, 0)
96 #include <wx/osx/core/cfstring.h> // Do_not_auto_remove
97 #else
98 #include <wx/mac/corefoundation/cfstring.h> // Do_not_auto_remove
99 #endif
100 #endif
101 #include <wx/msgdlg.h>
103 #include "amuleDlg.h"
104 #endif
107 #ifdef HAVE_SYS_RESOURCE_H
108 #include <sys/resource.h>
109 #endif
111 #ifdef HAVE_SYS_STATVFS_H
112 #include <sys/statvfs.h> // Do_not_auto_remove
113 #endif
116 #ifdef __GLIBC__
117 # define RLIMIT_RESOURCE __rlimit_resource
118 #else
119 # define RLIMIT_RESOURCE int
120 #endif
122 #ifdef AMULE_DAEMON
123 CamuleDaemonApp *theApp;
124 #else
125 CamuleGuiApp *theApp;
126 #endif
128 static void UnlimitResource(RLIMIT_RESOURCE resType)
130 #if defined(HAVE_GETRLIMIT) && defined(HAVE_SETRLIMIT)
131 struct rlimit rl;
132 getrlimit(resType, &rl);
133 rl.rlim_cur = rl.rlim_max;
134 setrlimit(resType, &rl);
135 #endif
139 static void SetResourceLimits()
141 #ifdef HAVE_SYS_RESOURCE_H
142 UnlimitResource(RLIMIT_DATA);
143 #ifndef __UCLIBC__
144 UnlimitResource(RLIMIT_FSIZE);
145 #endif
146 UnlimitResource(RLIMIT_NOFILE);
147 #ifdef RLIMIT_RSS
148 UnlimitResource(RLIMIT_RSS);
149 #endif
150 #endif
153 // We store the received signal in order to avoid race-conditions
154 // in the signal handler.
155 bool g_shutdownSignal = false;
157 void OnShutdownSignal( int /* sig */ )
159 signal(SIGINT, SIG_DFL);
160 signal(SIGTERM, SIG_DFL);
162 g_shutdownSignal = true;
164 #ifdef AMULE_DAEMON
165 theApp->ExitMainLoop();
166 #endif
170 CamuleApp::CamuleApp()
172 // Madcat - Initialize timer as the VERY FIRST thing to avoid any issues later.
173 // Kry - I love to init the vars on init, even before timer.
174 StartTickTimer();
176 // Initialization
177 m_app_state = APP_STATE_STARTING;
179 theApp = &wxGetApp();
181 clientlist = NULL;
182 searchlist = NULL;
183 knownfiles = NULL;
184 canceledfiles = NULL;
185 serverlist = NULL;
186 serverconnect = NULL;
187 sharedfiles = NULL;
188 listensocket = NULL;
189 clientudp = NULL;
190 clientcredits = NULL;
191 friendlist = NULL;
192 downloadqueue = NULL;
193 uploadqueue = NULL;
194 ipfilter = NULL;
195 ECServerHandler = NULL;
196 glob_prefs = NULL;
197 m_statistics = NULL;
198 uploadBandwidthThrottler = NULL;
199 #ifdef ENABLE_UPNP
200 m_upnp = NULL;
201 m_upnpMappings.resize(4);
202 #endif
203 core_timer = NULL;
205 m_localip = 0;
206 m_dwPublicIP = 0;
207 webserver_pid = 0;
209 enable_daemon_fork = false;
211 strFullMuleVersion = NULL;
212 strOSDescription = NULL;
214 // Apprently needed for *BSD
215 SetResourceLimits();
217 #ifdef _MSC_VER
218 _CrtSetDbgFlag(0); // Disable useless memleak debugging
219 #endif
222 CamuleApp::~CamuleApp()
224 // Closing the log-file as the very last thing, since
225 // wxWidgets log-events are saved in it as well.
226 theLogger.CloseLogfile();
228 free(strFullMuleVersion);
229 free(strOSDescription);
232 int CamuleApp::OnExit()
234 if (m_app_state!=APP_STATE_STARTING) {
235 AddLogLineMS(false, _("Now, exiting main app..."));
238 // From wxWidgets docs, wxConfigBase:
239 // ...
240 // Note that you must delete this object (usually in wxApp::OnExit)
241 // in order to avoid memory leaks, wxWidgets won't do it automatically.
243 // As it happens, you may even further simplify the procedure described
244 // above: you may forget about calling Set(). When Get() is called and
245 // there is no current object, it will create one using Create() function.
246 // To disable this behaviour DontCreateOnDemand() is provided.
247 delete wxConfigBase::Set((wxConfigBase *)NULL);
249 // Save credits
250 clientcredits->SaveList();
252 // Kill amuleweb if running
253 if (webserver_pid) {
254 AddLogLineNS(CFormat(_("Terminating amuleweb instance with pid `%ld' ... ")) % webserver_pid);
255 wxKillError rc;
256 if (wxKill(webserver_pid, wxSIGTERM, &rc) == -1) {
257 AddLogLineNS(CFormat(_("Killing amuleweb instance with pid `%ld' ... ")) % webserver_pid);
258 if (wxKill(webserver_pid, wxSIGKILL, &rc) == -1) {
259 AddLogLineNS(_("Failed"));
264 if (m_app_state!=APP_STATE_STARTING) {
265 AddLogLineMS(false, _("aMule OnExit: Terminating core."));
268 delete serverlist;
269 serverlist = NULL;
271 delete searchlist;
272 searchlist = NULL;
274 delete clientcredits;
275 clientcredits = NULL;
277 delete friendlist;
278 friendlist = NULL;
280 // Destroying CDownloadQueue calls destructor for CPartFile
281 // calling CSharedFileList::SafeAddKFile occasionally.
282 delete sharedfiles;
283 sharedfiles = NULL;
285 delete serverconnect;
286 serverconnect = NULL;
288 delete listensocket;
289 listensocket = NULL;
291 delete clientudp;
292 clientudp = NULL;
294 delete knownfiles;
295 knownfiles = NULL;
297 delete canceledfiles;
298 canceledfiles = NULL;
300 delete clientlist;
301 clientlist = NULL;
303 delete uploadqueue;
304 uploadqueue = NULL;
306 delete downloadqueue;
307 downloadqueue = NULL;
309 delete ipfilter;
310 ipfilter = NULL;
312 #ifdef ENABLE_UPNP
313 delete m_upnp;
314 m_upnp = NULL;
315 #endif
317 delete ECServerHandler;
318 ECServerHandler = NULL;
320 delete m_statistics;
321 m_statistics = NULL;
323 delete glob_prefs;
324 glob_prefs = NULL;
325 CPreferences::EraseItemList();
327 delete uploadBandwidthThrottler;
328 uploadBandwidthThrottler = NULL;
330 if (m_app_state!=APP_STATE_STARTING) {
331 AddLogLineNS(_("aMule shutdown completed."));
334 #if wxUSE_MEMORY_TRACING
335 AddLogLineNS(_("Memory debug results for aMule exit:"));
336 // Log mem debug mesages to wxLogStderr
337 wxLog* oldLog = wxLog::SetActiveTarget(new wxLogStderr);
338 //AddLogLineNS(wxT("**************Classes**************");
339 //wxDebugContext::PrintClasses();
340 //AddLogLineNS(wxT("***************Dump***************");
341 //wxDebugContext::Dump();
342 AddLogLineNS(wxT("***************Stats**************"));
343 wxDebugContext::PrintStatistics(true);
345 // Set back to wxLogGui
346 delete wxLog::SetActiveTarget(oldLog);
347 #endif
349 StopTickTimer();
351 // Return 0 for succesful program termination
352 return AMULE_APP_BASE::OnExit();
356 int CamuleApp::InitGui(bool, wxString &)
358 return 0;
363 // Application initialization
365 bool CamuleApp::OnInit()
367 #if wxUSE_MEMORY_TRACING
368 // any text before call of Localize_mule needs not to be translated.
369 AddLogLineMS(false, wxT("Checkpoint set on app init for memory debug"));
370 wxDebugContext::SetCheckpoint();
371 #endif
373 // Forward wxLog events to CLogger
374 wxLog::SetActiveTarget(new CLoggerTarget);
376 m_localip = StringHosttoUint32(::wxGetFullHostName());
378 #ifndef __WXMSW__
379 // get rid of sigpipe
380 signal(SIGPIPE, SIG_IGN);
381 #else
382 // Handle CTRL-Break
383 signal(SIGBREAK, OnShutdownSignal);
384 #endif
385 // Handle sigint and sigterm
386 signal(SIGINT, OnShutdownSignal);
387 signal(SIGTERM, OnShutdownSignal);
389 #ifdef __WXMAC__
390 // For listctrl's to behave on Mac
391 wxSystemOptions::SetOption(wxT("mac.listctrl.always_use_generic"), 1);
392 #endif
394 // Handle uncaught exceptions
395 InstallMuleExceptionHandler();
397 if (!InitCommon(AMULE_APP_BASE::argc, AMULE_APP_BASE::argv)) {
398 return false;
401 glob_prefs = new CPreferences();
403 CPath outDir;
404 if (CheckMuleDirectory(wxT("temp"), thePrefs::GetTempDir(), ConfigDir + wxT("Temp"), outDir)) {
405 thePrefs::SetTempDir(outDir);
406 } else {
407 return false;
410 if (CheckMuleDirectory(wxT("incoming"), thePrefs::GetIncomingDir(), ConfigDir + wxT("Incoming"), outDir)) {
411 thePrefs::SetIncomingDir(outDir);
412 } else {
413 return false;
416 // Some sanity check
417 if (!thePrefs::UseTrayIcon()) {
418 thePrefs::SetMinToTray(false);
421 // Build the filenames for the two OS files
422 SetOSFiles(thePrefs::GetOSDir().GetRaw());
424 #ifdef ENABLE_NLS
425 // Load localization settings
426 Localize_mule();
427 #endif
429 // Configure EC for amuled when invoked with ec-config
430 if (ec_config) {
431 AddLogLineMS(false, _("\nEC configuration"));
432 thePrefs::SetECPass(GetPassword());
433 thePrefs::EnableExternalConnections(true);
434 AddLogLineMS(false, _("Password set and external connections enabled."));
437 #ifndef __WXMSW__
438 if (getuid() == 0) {
439 wxString msg =
440 wxT("Warning! You are running aMule as root.\n")
441 wxT("Doing so is not recommended for security reasons,\n")
442 wxT("and you are advised to run aMule as an normal\n")
443 wxT("user instead.");
445 ShowAlert(msg, _("WARNING"), wxCENTRE | wxOK | wxICON_ERROR);
447 fprintf(stderr, "\n--------------------------------------------------\n");
448 fprintf(stderr, "%s", (const char*)unicode2UTF8(msg));
449 fprintf(stderr, "\n--------------------------------------------------\n\n");
451 #endif
453 // Display notification on new version or first run
454 wxTextFile vfile( ConfigDir + wxT("lastversion") );
455 wxString newMule(wxT( VERSION ));
457 if ( !wxFileExists( vfile.GetName() ) ) {
458 vfile.Create();
461 if ( vfile.Open() ) {
462 // Check if this version has been run before
463 bool found = false;
464 for ( size_t i = 0; i < vfile.GetLineCount(); i++ ) {
465 // Check if this version has been run before
466 if ( vfile.GetLine(i) == newMule ) {
467 found = true;
468 break;
472 // We havent run this version before?
473 if ( !found ) {
474 // Insert new at top to provide faster searches
475 vfile.InsertLine( newMule, 0 );
477 Trigger_New_version( newMule );
480 // Keep at most 10 entires
481 while ( vfile.GetLineCount() > 10 )
482 vfile.RemoveLine( vfile.GetLineCount() - 1 );
484 vfile.Write();
485 vfile.Close();
488 // Check if we have the old style locale config
489 wxString langId = thePrefs::GetLanguageID();
490 if (!langId.IsEmpty() && (langId.GetChar(0) >= '0' && langId.GetChar(0) <= '9')) {
491 wxString info(_("Your locale has been changed to System Default due to a configuration change. Sorry."));
492 thePrefs::SetLanguageID(wxLang2Str(wxLANGUAGE_DEFAULT));
493 ShowAlert(info, _("Info"), wxCENTRE | wxOK | wxICON_ERROR);
496 m_statistics = new CStatistics();
498 clientlist = new CClientList();
499 friendlist = new CFriendList();
500 searchlist = new CSearchList();
501 knownfiles = new CKnownFileList();
502 canceledfiles = new CCanceledFileList;
503 serverlist = new CServerList();
505 sharedfiles = new CSharedFileList(knownfiles);
506 clientcredits = new CClientCreditsList();
508 // bugfix - do this before creating the uploadqueue
509 downloadqueue = new CDownloadQueue();
510 uploadqueue = new CUploadQueue();
511 ipfilter = new CIPFilter();
513 // Creates all needed listening sockets
514 wxString msg;
515 if (!ReinitializeNetwork(&msg)) {
516 AddLogLineMS(false, wxT("\n"));
517 AddLogLineMS(false, msg);
520 // Test if there's any new version
521 if (thePrefs::GetCheckNewVersion()) {
522 // We use the thread base because I don't want a dialog to pop up.
523 CHTTPDownloadThread* version_check =
524 new CHTTPDownloadThread(wxT("http://amule.sourceforge.net/lastversion"),
525 theApp->ConfigDir + wxT("last_version_check"), theApp->ConfigDir + wxT("last_version"), HTTP_VersionCheck, false);
526 version_check->Create();
527 version_check->Run();
530 // Create main dialog, or fork to background (daemon).
531 InitGui(m_geometryEnabled, m_geometryString);
533 #ifdef AMULE_DAEMON
534 // Need to refresh wxSingleInstanceChecker after the daemon fork() !
535 if (enable_daemon_fork) {
536 RefreshSingleInstanceChecker();
537 // No need to check IsAnotherRunning() - we've done it before.
539 #endif
541 // Has to be created after the call to InitGui, as fork
542 // (when using posix threads) only replicates the mainthread,
543 // and the UBT constructor creates a thread.
544 uploadBandwidthThrottler = new UploadBandwidthThrottler();
546 // Start performing background tasks
547 // This will start loading the IP filter. It will start right away.
548 // Log is confusing, because log entries from background will only be printed
549 // once foreground becomes idle, and that will only be after loading
550 // of the partfiles has finished.
551 CThreadScheduler::Start();
553 // These must be initialized after the gui is loaded.
554 if (thePrefs::GetNetworkED2K()) {
555 serverlist->Init();
557 downloadqueue->LoadMetFiles(thePrefs::GetTempDir());
558 sharedfiles->Reload();
560 // Ensure that the up/down ratio is used
561 CPreferences::CheckUlDlRatio();
563 // The user can start pressing buttons like mad if he feels like it.
564 m_app_state = APP_STATE_RUNNING;
566 // Kry - Load the sources seeds on app init
567 if (thePrefs::GetSrcSeedsOn() && ipfilter->IsReady()) {
568 downloadqueue->LoadSourceSeeds();
571 if (!serverlist->GetServerCount() && thePrefs::GetNetworkED2K()) {
572 // There are no servers and ED2K active -> ask for download.
573 // As we cannot ask in amuled, we just update there
574 // Kry TODO: Store server.met URL on preferences and use it here and in GUI.
575 #ifndef AMULE_DAEMON
576 if (wxYES == wxMessageBox(
577 wxString(
578 _("You don't have any server in the server list.\nDo you want aMule to download a new list now?")),
579 wxString(_("Server list download")),
580 wxYES_NO,
581 static_cast<wxWindow*>(theApp->amuledlg)))
582 #endif
584 // workaround amuled crash
585 #ifndef AMULE_DAEMON
586 serverlist->UpdateServerMetFromURL(
587 wxT("http://gruk.org/server.met.gz"));
588 #endif
593 // Autoconnect if that option is enabled
594 if (thePrefs::DoAutoConnect() && (thePrefs::GetNetworkED2K() || thePrefs::GetNetworkKademlia())) {
595 if (ipfilter->IsReady()) {
596 // If it's not ready it will connect later, so don't print it now.
597 AddLogLineC(_("Connecting"));
599 if (thePrefs::GetNetworkED2K()) {
600 theApp->serverconnect->ConnectToAnyServer();
603 StartKad();
607 // Enable GeoIP
608 #ifdef ENABLE_IP2COUNTRY
609 theApp->amuledlg->EnableIP2Country();
610 #endif
612 // Run webserver?
613 if (thePrefs::GetWSIsEnabled()) {
614 wxString aMuleConfigFile = ConfigDir + m_configFile;
615 wxString amulewebPath = thePrefs::GetWSPath();
617 #if defined(__WXMAC__) && !defined(AMULE_DAEMON)
618 // For the Mac GUI application, look for amuleweb in the bundle
619 CFURLRef amulewebUrl = CFBundleCopyAuxiliaryExecutableURL(
620 CFBundleGetMainBundle(), CFSTR("amuleweb"));
622 if (amulewebUrl) {
623 CFURLRef absoluteUrl = CFURLCopyAbsoluteURL(amulewebUrl);
624 CFRelease(amulewebUrl);
626 if (absoluteUrl) {
627 CFStringRef amulewebCfstr = CFURLCopyFileSystemPath(absoluteUrl, kCFURLPOSIXPathStyle);
628 CFRelease(absoluteUrl);
629 #if wxCHECK_VERSION(2, 9, 0)
630 amulewebPath = wxCFStringRef(amulewebCfstr).AsString(wxLocale::GetSystemEncoding());
631 #else
632 amulewebPath = wxMacCFStringHolder(amulewebCfstr).AsString(wxLocale::GetSystemEncoding());
633 #endif
636 #endif
638 #ifdef __WXMSW__
639 # define QUOTE wxT("\"")
640 #else
641 # define QUOTE wxT("\'")
642 #endif
644 wxString cmd =
645 QUOTE +
646 amulewebPath +
647 QUOTE wxT(" ") QUOTE wxT("--amule-config-file=") +
648 aMuleConfigFile +
649 QUOTE;
650 CTerminationProcessAmuleweb *p = new CTerminationProcessAmuleweb(cmd, &webserver_pid);
651 webserver_pid = wxExecute(cmd, wxEXEC_ASYNC, p);
652 bool webserver_ok = webserver_pid > 0;
653 if (webserver_ok) {
654 AddLogLineM(true, CFormat(_("web server running on pid %d")) % webserver_pid);
655 } else {
656 delete p;
657 ShowAlert(_(
658 "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"),
659 _("ERROR"), wxOK | wxICON_ERROR);
663 return true;
666 bool CamuleApp::ReinitializeNetwork(wxString* msg)
668 bool ok = true;
669 static bool firstTime = true;
671 if (!firstTime) {
672 // TODO: Destroy previously created sockets
674 firstTime = false;
676 // Some sanity checks first
677 if (thePrefs::ECPort() == thePrefs::GetPort()) {
678 // Select a random usable port in the range 1025 ... 2^16 - 1
679 uint16 port = thePrefs::ECPort();
680 while ( port < 1024 || port == thePrefs::GetPort() ) {
681 port = (uint16)rand();
683 thePrefs::SetECPort( port );
685 wxString err =
686 wxT("Network configuration failed! You cannot use the same port\n")
687 wxT("for the main TCP port and the External Connections port.\n")
688 wxT("The EC port has been changed to avoid conflict, see the\n")
689 wxT("preferences for the new value.\n");
690 *msg << err;
692 AddLogLineM( false, wxEmptyString );
693 AddLogLineM( true, err );
694 AddLogLineM( false, wxEmptyString );
696 ok = false;
699 if (thePrefs::GetUDPPort() == thePrefs::GetPort() + 3) {
700 // Select a random usable value in the range 1025 ... 2^16 - 1
701 uint16 port = thePrefs::GetUDPPort();
702 while ( port < 1024 || port == thePrefs::GetPort() + 3 ) {
703 port = (uint16)rand();
705 thePrefs::SetUDPPort( port );
707 wxString err =
708 wxT("Network configuration failed! You set your UDP port to\n")
709 wxT("the value of the main TCP port plus 3.\n")
710 wxT("This port has been reserved for the Server-UDP port. The\n")
711 wxT("port value has been changed to avoid conflict, see the\n")
712 wxT("preferences for the new value\n");
713 *msg << err;
715 AddLogLineM( false, wxEmptyString );
716 AddLogLineM( true, err );
717 AddLogLineM( false, wxEmptyString );
719 ok = false;
722 // Create the address where we are going to listen
723 // TODO: read this from configuration file
724 amuleIPV4Address myaddr[4];
726 // Create the External Connections Socket.
727 // Default is 4712.
728 // Get ready to handle connections from apps like amulecmd
729 if (thePrefs::GetECAddress().IsEmpty() || !myaddr[0].Hostname(thePrefs::GetECAddress())) {
730 myaddr[0].AnyAddress();
732 myaddr[0].Service(thePrefs::ECPort());
733 ECServerHandler = new ExternalConn(myaddr[0], msg);
735 // Create the UDP socket TCP+3.
736 // Used for source asking on servers.
737 if (thePrefs::GetAddress().IsEmpty()) {
738 myaddr[1].AnyAddress();
739 } else if (!myaddr[1].Hostname(thePrefs::GetAddress())) {
740 myaddr[1].AnyAddress();
741 AddLogLineM(true, CFormat(_("Could not bind ports to the specified address: %s"))
742 % thePrefs::GetAddress());
745 wxString ip = myaddr[1].IPAddress();
746 myaddr[1].Service(thePrefs::GetPort()+3);
747 serverconnect = new CServerConnect(serverlist, myaddr[1]);
748 *msg << CFormat( wxT("*** Server UDP socket (TCP+3) at %s:%u\n") )
749 % ip % ((unsigned int)thePrefs::GetPort() + 3u);
751 // Create the ListenSocket (aMule TCP socket).
752 // Used for Client Port / Connections from other clients,
753 // Client to Client Source Exchange.
754 // Default is 4662.
755 myaddr[2] = myaddr[1];
756 myaddr[2].Service(thePrefs::GetPort());
757 listensocket = new CListenSocket(myaddr[2]);
758 *msg << CFormat( wxT("*** TCP socket (TCP) listening on %s:%u\n") )
759 % ip % (unsigned int)(thePrefs::GetPort());
760 // This command just sets a flag to control maximum number of connections.
761 // Notify(true) has already been called to the ListenSocket, so events may
762 // be already comming in.
763 if (listensocket->Ok()) {
764 listensocket->StartListening();
765 } else {
766 // If we wern't able to start listening, we need to warn the user
767 wxString err;
768 err = CFormat(_("Port %u is not available. You will be LOWID\n")) %
769 (unsigned int)(thePrefs::GetPort());
770 *msg << err;
771 AddLogLineM(true, err);
772 err.Clear();
773 err = CFormat(
774 _("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.")) %
775 (unsigned int)(thePrefs::GetPort());
776 ShowAlert(err, _("ERROR"), wxOK | wxICON_ERROR);
779 // Create the UDP socket.
780 // Used for extended eMule protocol, Queue Rating, File Reask Ping.
781 // Also used for Kademlia.
782 // Default is port 4672.
783 myaddr[3] = myaddr[1];
784 myaddr[3].Service(thePrefs::GetUDPPort());
785 clientudp = new CClientUDPSocket(myaddr[3], thePrefs::GetProxyData());
786 if (!thePrefs::IsUDPDisabled()) {
787 *msg << CFormat( wxT("*** Client UDP socket (extended eMule) at %s:%u") )
788 % ip % thePrefs::GetUDPPort();
789 } else {
790 *msg << wxT("*** Client UDP socket (extended eMule) disabled on preferences");
793 #ifdef ENABLE_UPNP
794 if (thePrefs::GetUPnPEnabled()) {
795 try {
796 m_upnpMappings[0] = CUPnPPortMapping(
797 myaddr[0].Service(),
798 "TCP",
799 thePrefs::GetUPnPECEnabled(),
800 "aMule TCP External Connections Socket");
801 m_upnpMappings[1] = CUPnPPortMapping(
802 myaddr[1].Service(),
803 "UDP",
804 thePrefs::GetUPnPEnabled(),
805 "aMule UDP socket (TCP+3)");
806 m_upnpMappings[2] = CUPnPPortMapping(
807 myaddr[2].Service(),
808 "TCP",
809 thePrefs::GetUPnPEnabled(),
810 "aMule TCP Listen Socket");
811 m_upnpMappings[3] = CUPnPPortMapping(
812 myaddr[3].Service(),
813 "UDP",
814 thePrefs::GetUPnPEnabled(),
815 "aMule UDP Extended eMule Socket");
816 m_upnp = new CUPnPControlPoint(thePrefs::GetUPnPTCPPort());
817 m_upnp->AddPortMappings(m_upnpMappings);
818 } catch(CUPnPException &e) {
819 wxString error_msg;
820 error_msg << e.what();
821 AddLogLineM(true, error_msg);
822 fprintf(stderr, "%s\n", (const char *)unicode2char(error_msg));
825 #endif
827 return ok;
830 /* Original implementation by Bouc7 of the eMule Project.
831 aMule Signature idea was designed by BigBob and implemented
832 by Un-Thesis, with design inputs and suggestions from bothie.
834 void CamuleApp::OnlineSig(bool zero /* reset stats (used on shutdown) */)
836 // Do not do anything if online signature is disabled in Preferences
837 if (!thePrefs::IsOnlineSignatureEnabled() || m_emulesig_path.IsEmpty()) {
838 // We do not need to check m_amulesig_path because if m_emulesig_path is empty,
839 // that means m_amulesig_path is empty too.
840 return;
843 // Remove old signature files
844 if ( wxFileExists( m_emulesig_path ) ) { wxRemoveFile( m_emulesig_path ); }
845 if ( wxFileExists( m_amulesig_path ) ) { wxRemoveFile( m_amulesig_path ); }
848 wxTextFile amulesig_out;
849 wxTextFile emulesig_out;
851 // Open both files if needed
852 if ( !emulesig_out.Create( m_emulesig_path) ) {
853 AddLogLineM(true, _("Failed to create OnlineSig File"));
854 // Will never try again.
855 m_amulesig_path.Clear();
856 m_emulesig_path.Clear();
857 return;
860 if ( !amulesig_out.Create(m_amulesig_path) ) {
861 AddLogLineM(true, _("Failed to create aMule OnlineSig File"));
862 // Will never try again.
863 m_amulesig_path.Clear();
864 m_emulesig_path.Clear();
865 return;
868 wxString emulesig_string;
869 wxString temp;
871 if (zero) {
872 emulesig_string = wxT("0\xA0.0|0.0|0");
873 amulesig_out.AddLine(wxT("0\n0\n0\n0\n0\n0\n0.0\n0.0\n0\n0"));
874 } else {
875 if (IsConnectedED2K()) {
877 temp = wxString::Format(wxT("%d"),serverconnect->GetCurrentServer()->GetPort());
879 // We are online
880 emulesig_string =
881 // Connected
882 wxT("1|")
883 //Server name
884 + serverconnect->GetCurrentServer()->GetListName()
885 + wxT("|")
886 // IP and port of the server
887 + serverconnect->GetCurrentServer()->GetFullIP()
888 + wxT("|")
889 + temp;
892 // Now for amule sig
894 // Connected. State 1, full info
895 amulesig_out.AddLine(wxT("1"));
896 // Server Name
897 amulesig_out.AddLine(serverconnect->GetCurrentServer()->GetListName());
898 // Server IP
899 amulesig_out.AddLine(serverconnect->GetCurrentServer()->GetFullIP());
900 // Server Port
901 amulesig_out.AddLine(temp);
903 if (serverconnect->IsLowID()) {
904 amulesig_out.AddLine(wxT("L"));
905 } else {
906 amulesig_out.AddLine(wxT("H"));
909 } else if (serverconnect->IsConnecting()) {
910 emulesig_string = wxT("0");
912 // Connecting. State 2, No info.
913 amulesig_out.AddLine(wxT("2\n0\n0\n0\n0"));
914 } else {
915 // Not connected to a server
916 emulesig_string = wxT("0");
918 // Not connected, state 0, no info
919 amulesig_out.AddLine(wxT("0\n0\n0\n0\n0"));
921 if (IsConnectedKad()) {
922 if(Kademlia::CKademlia::IsFirewalled()) {
923 // Connected. Firewalled. State 1.
924 amulesig_out.AddLine(wxT("1"));
925 } else {
926 // Connected. State 2.
927 amulesig_out.AddLine(wxT("2"));
929 } else {
930 // Not connected.State 0.
931 amulesig_out.AddLine(wxT("0"));
933 emulesig_string += wxT("\xA");
935 // Datarate for downloads
936 temp = wxString::Format(wxT("%.1f"), theStats::GetDownloadRate() / 1024.0);
938 emulesig_string += temp + wxT("|");
939 amulesig_out.AddLine(temp);
941 // Datarate for uploads
942 temp = wxString::Format(wxT("%.1f"), theStats::GetUploadRate() / 1024.0);
944 emulesig_string += temp + wxT("|");
945 amulesig_out.AddLine(temp);
947 // Number of users waiting for upload
948 temp = wxString::Format(wxT("%d"), theStats::GetWaitingUserCount());
950 emulesig_string += temp;
951 amulesig_out.AddLine(temp);
953 // Number of shared files (not on eMule)
954 amulesig_out.AddLine(wxString::Format(wxT("%d"), theStats::GetSharedFileCount()));
957 // eMule signature finished here. Write the line to the wxTextFile.
958 emulesig_out.AddLine(emulesig_string);
960 // Now for aMule signature extras
962 // Nick on the network
963 amulesig_out.AddLine(thePrefs::GetUserNick());
965 // Total received in bytes
966 amulesig_out.AddLine( CFormat( wxT("%llu") ) % (theStats::GetSessionReceivedBytes() + thePrefs::GetTotalDownloaded()) );
968 // Total sent in bytes
969 amulesig_out.AddLine( CFormat( wxT("%llu") ) % (theStats::GetSessionSentBytes() + thePrefs::GetTotalUploaded()) );
971 // amule version
972 #ifdef SVNDATE
973 amulesig_out.AddLine(wxT(VERSION) wxT(" ") wxT(SVNDATE));
974 #else
975 amulesig_out.AddLine(wxT(VERSION));
976 #endif
978 if (zero) {
979 amulesig_out.AddLine(wxT("0"));
980 amulesig_out.AddLine(wxT("0"));
981 amulesig_out.AddLine(wxT("0"));
982 } else {
983 // Total received bytes in session
984 amulesig_out.AddLine( CFormat( wxT("%llu") ) %
985 theStats::GetSessionReceivedBytes() );
987 // Total sent bytes in session
988 amulesig_out.AddLine( CFormat( wxT("%llu") ) %
989 theStats::GetSessionSentBytes() );
991 // Uptime
992 amulesig_out.AddLine(CFormat(wxT("%llu")) % theStats::GetUptimeSeconds());
995 // Flush the files
996 emulesig_out.Write();
997 amulesig_out.Write();
998 } //End Added By Bouc7
1001 // Gracefully handle fatal exceptions and print backtrace if possible
1002 void CamuleApp::OnFatalException()
1004 /* Print the backtrace */
1005 fprintf(stderr, "\n--------------------------------------------------------------------------------\n");
1006 fprintf(stderr, "A fatal error has occurred and aMule has crashed.\n");
1007 fprintf(stderr, "Please assist us in fixing this problem by posting the backtrace below in our\n");
1008 fprintf(stderr, "'aMule Crashes' forum and include as much information as possible regarding the\n");
1009 fprintf(stderr, "circumstances of this crash. The forum is located here:\n");
1010 fprintf(stderr, " http://forum.amule.org/index.php?board=67.0\n");
1011 fprintf(stderr, "If possible, please try to generate a real backtrace of this crash:\n");
1012 fprintf(stderr, " http://wiki.amule.org/index.php/Backtraces\n\n");
1013 fprintf(stderr, "----------------------------=| BACKTRACE FOLLOWS: |=----------------------------\n");
1014 fprintf(stderr, "Current version is: %s\n", strFullMuleVersion);
1015 fprintf(stderr, "Running on: %s\n\n", strOSDescription);
1017 print_backtrace(1); // 1 == skip this function.
1019 fprintf(stderr, "\n--------------------------------------------------------------------------------\n");
1023 // Sets the localization of aMule
1024 void CamuleApp::Localize_mule()
1026 InitCustomLanguages();
1027 InitLocale(m_locale, StrLang2wx(thePrefs::GetLanguageID()));
1028 if (!m_locale.IsOk()) {
1029 AddLogLineM(false,_("The selected locale seems not to be installed on your box. (Note: I'll try to set it anyway)"));
1034 // Displays information related to important changes in aMule.
1035 // Is called when the user runs a new version of aMule
1036 void CamuleApp::Trigger_New_version(wxString new_version)
1038 wxString info = wxT(" --- ") + CFormat(_("This is the first time you run aMule %s")) % new_version + wxT(" ---\n\n");
1039 if (new_version == wxT("SVN")) {
1040 info += _("This version is a testing version, updated daily, and\n");
1041 info += _("we give no warranty it won't break anything, burn your house,\n");
1042 info += _("or kill your dog. But it *should* be safe to use anyway.\n");
1045 // General info
1046 info += wxT("\n");
1047 info += _("More information, support and new releases can found at our homepage,\n");
1048 info += _("at www.aMule.org, or in our IRC channel #aMule at irc.freenode.net.\n");
1049 info += wxT("\n");
1050 info += _("Feel free to report any bugs to http://forum.amule.org");
1052 ShowAlert(info, _("Info"), wxCENTRE | wxOK | wxICON_ERROR);
1056 void CamuleApp::SetOSFiles(const wxString new_path)
1058 if ( thePrefs::IsOnlineSignatureEnabled() ) {
1059 if ( ::wxDirExists(new_path) ) {
1060 m_emulesig_path = JoinPaths(new_path, wxT("onlinesig.dat"));
1061 m_amulesig_path = JoinPaths(new_path, wxT("amulesig.dat"));
1062 } else {
1063 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);
1064 m_emulesig_path.Clear();
1065 m_amulesig_path.Clear();
1067 } else {
1068 m_emulesig_path.Clear();
1069 m_amulesig_path.Clear();
1074 #ifdef __WXDEBUG__
1075 #ifndef wxUSE_STACKWALKER
1076 #define wxUSE_STACKWALKER 0
1077 #endif
1078 void CamuleApp::OnAssertFailure(const wxChar* file, int line,
1079 const wxChar* func, const wxChar* cond, const wxChar* msg)
1081 if (!wxUSE_STACKWALKER || !wxThread::IsMain() || !IsRunning()) {
1082 wxString errmsg = CFormat( wxT("%s:%s:%d: Assertion '%s' failed. %s") )
1083 % file % func % line % cond % ( msg ? msg : wxT("") );
1085 fprintf(stderr, "Assertion failed: %s\n", (const char*)unicode2char(errmsg));
1087 // Skip the function-calls directly related to the assert call.
1088 fprintf(stderr, "\nBacktrace follows:\n");
1089 print_backtrace(3);
1090 fprintf(stderr, "\n");
1093 if (wxThread::IsMain() && IsRunning()) {
1094 AMULE_APP_BASE::OnAssertFailure(file, line, func, cond, msg);
1095 } else {
1096 // Abort, allows gdb to catch the assertion
1097 raise( SIGABRT );
1100 #endif
1103 void CamuleApp::OnUDPDnsDone(CMuleInternalEvent& evt)
1105 CServerUDPSocket* socket =(CServerUDPSocket*)evt.GetClientData();
1106 socket->OnHostnameResolved(evt.GetExtraLong());
1110 void CamuleApp::OnSourceDnsDone(CMuleInternalEvent& evt)
1112 downloadqueue->OnHostnameResolved(evt.GetExtraLong());
1116 void CamuleApp::OnServerDnsDone(CMuleInternalEvent& evt)
1118 AddLogLineMS(false, _("Server hostname notified"));
1119 serverconnect->OnServerHostnameResolved(evt.GetClientData(), evt.GetExtraLong());
1123 void CamuleApp::OnTCPTimer(CTimerEvent& WXUNUSED(evt))
1125 if(!IsRunning()) {
1126 return;
1128 serverconnect->StopConnectionTry();
1129 if (IsConnectedED2K() ) {
1130 return;
1132 serverconnect->ConnectToAnyServer();
1136 void CamuleApp::OnCoreTimer(CTimerEvent& WXUNUSED(evt))
1138 // Former TimerProc section
1139 static uint64 msPrev1, msPrev5, msPrevSave, msPrevHist, msPrevOS, msPrevKnownMet;
1140 uint64 msCur = theStats::GetUptimeMillis();
1141 TheTime = msCur / 1000;
1143 if (!IsRunning()) {
1144 return;
1147 #ifndef AMULE_DAEMON
1148 // Check if we should terminate the app
1149 if ( g_shutdownSignal ) {
1150 wxWindow* top = GetTopWindow();
1152 if ( top ) {
1153 top->Close(true);
1154 } else {
1155 // No top-window, have to force termination.
1156 wxExit();
1159 #endif
1161 // There is a theoretical chance that the core time function can recurse:
1162 // if an event function gets blocked on a mutex (communicating with the
1163 // UploadBandwidthThrottler) wx spawns a new event loop and processes more events.
1164 // If CPU load gets high a new core timer event could be generated before the last
1165 // one was finished and so recursion could occur, which would be bad.
1166 // Detect this and do an early return then.
1167 static bool recurse = false;
1168 if (recurse) {
1169 return;
1171 recurse = true;
1173 uploadqueue->Process();
1174 downloadqueue->Process();
1175 //theApp->clientcredits->Process();
1176 theStats::CalculateRates();
1178 if (msCur-msPrevHist > 1000) {
1179 // unlike the other loop counters in this function this one will sometimes
1180 // produce two calls in quick succession (if there was a gap of more than one
1181 // second between calls to TimerProc) - this is intentional! This way the
1182 // history list keeps an average of one node per second and gets thinned out
1183 // correctly as time progresses.
1184 msPrevHist += 1000;
1186 m_statistics->RecordHistory();
1191 if (msCur-msPrev1 > 1000) { // approximately every second
1192 msPrev1 = msCur;
1193 clientcredits->Process();
1194 clientlist->Process();
1196 // Publish files to server if needed.
1197 sharedfiles->Process();
1199 if( Kademlia::CKademlia::IsRunning() ) {
1200 Kademlia::CKademlia::Process();
1201 if(Kademlia::CKademlia::GetPrefs()->HasLostConnection()) {
1202 StopKad();
1203 clientudp->Close();
1204 clientudp->Open();
1205 if (thePrefs::Reconnect()) {
1206 StartKad();
1211 if( serverconnect->IsConnecting() && !serverconnect->IsSingleConnect() ) {
1212 serverconnect->TryAnotherConnectionrequest();
1214 if (serverconnect->IsConnecting()) {
1215 serverconnect->CheckForTimeout();
1217 listensocket->UpdateConnectionsStatus();
1222 if (msCur-msPrev5 > 5000) { // every 5 seconds
1223 msPrev5 = msCur;
1224 listensocket->Process();
1227 if (msCur-msPrevSave >= 60000) {
1228 msPrevSave = msCur;
1229 wxString buffer;
1231 // Save total upload/download to preferences
1232 wxConfigBase* cfg = wxConfigBase::Get();
1233 buffer = CFormat(wxT("%llu")) % (theStats::GetSessionReceivedBytes() + thePrefs::GetTotalDownloaded());
1234 cfg->Write(wxT("/Statistics/TotalDownloadedBytes"), buffer);
1236 buffer = CFormat(wxT("%llu")) % (theStats::GetSessionSentBytes() + thePrefs::GetTotalUploaded());
1237 cfg->Write(wxT("/Statistics/TotalUploadedBytes"), buffer);
1239 // Write changes to file
1240 cfg->Flush();
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 int filecount;
1282 static uint64 bytecount;
1284 if (knownfiles->SafeAddKFile(result)) {
1285 AddDebugLogLineM(false, logKnownFiles,
1286 CFormat(wxT("Safe adding file to sharedlist: %s")) % result->GetFileName());
1287 sharedfiles->SafeAddKFile(result);
1289 filecount++;
1290 bytecount += result->GetFileSize();
1291 // If we have added 30 files or files with a total size of ~300mb
1292 if ( ( filecount == 30 ) || ( bytecount >= 314572800 ) ) {
1293 AddDebugLogLineM(false, logKnownFiles, wxT("Failsafe for crash on file hashing creation"));
1294 if ( m_app_state != APP_STATE_SHUTTINGDOWN ) {
1295 knownfiles->Save();
1296 filecount = 0;
1297 bytecount = 0;
1300 } else {
1301 AddDebugLogLineM(false, logKnownFiles,
1302 CFormat(wxT("File not added to sharedlist: %s")) % result->GetFileName());
1303 delete result;
1309 void CamuleApp::OnFinishedAICHHashing(CHashingEvent& evt)
1311 wxCHECK_RET(evt.GetResult(), wxT("No result of AICH-hashing"));
1313 CKnownFile* owner = const_cast<CKnownFile*>(evt.GetOwner());
1314 CScopedPtr<CKnownFile> result(evt.GetResult());
1316 // Check that the owner is still valid
1317 if (knownfiles->IsKnownFile(owner)) {
1318 if (result->GetAICHHashset()->GetStatus() == AICH_HASHSETCOMPLETE) {
1319 CAICHHashSet* oldSet = owner->GetAICHHashset();
1320 CAICHHashSet* newSet = result->GetAICHHashset();
1322 owner->SetAICHHashset(newSet);
1323 newSet->SetOwner(owner);
1325 result->SetAICHHashset(oldSet);
1326 oldSet->SetOwner(result.get());
1332 void CamuleApp::OnFinishedCompletion(CCompletionEvent& evt)
1334 CPartFile* completed = const_cast<CPartFile*>(evt.GetOwner());
1335 wxCHECK_RET(completed, wxT("Completion event sent for unspecified file"));
1336 wxASSERT_MSG(downloadqueue->IsPartFile(completed), wxT("CCompletionEvent for unknown partfile."));
1338 completed->CompleteFileEnded(evt.ErrorOccured(), evt.GetFullPath());
1339 if (evt.ErrorOccured()) {
1340 CUserEvents::ProcessEvent(CUserEvents::ErrorOnCompletion, completed);
1343 // Check if we should execute an script/app/whatever.
1344 CUserEvents::ProcessEvent(CUserEvents::DownloadCompleted, completed);
1347 void CamuleApp::OnFinishedAllocation(CAllocFinishedEvent& evt)
1349 CPartFile *file = evt.GetFile();
1350 wxCHECK_RET(file, wxT("Allocation finished event sent for unspecified file"));
1351 wxASSERT_MSG(downloadqueue->IsPartFile(file), wxT("CAllocFinishedEvent for unknown partfile"));
1353 file->SetPartFileStatus(PS_EMPTY);
1355 if (evt.Succeeded()) {
1356 if (evt.IsPaused()) {
1357 file->StopFile();
1358 } else {
1359 file->ResumeFile();
1361 } else {
1362 AddLogLineM(false, CFormat(_("Disk space preallocation for file '%s' failed: %s")) % file->GetFileName() % wxString(UTF82unicode(std::strerror(evt.GetResult()))));
1363 file->StopFile();
1366 file->AllocationFinished();
1369 void CamuleApp::OnNotifyEvent(CMuleGUIEvent& evt)
1371 #if defined(AMULE_DAEMON)
1372 evt.Notify();
1373 #else
1374 if (theApp->amuledlg) {
1375 evt.Notify();
1377 #endif
1381 void CamuleApp::ShutDown()
1383 // Log
1384 AddDebugLogLineM(false, logGeneral, wxT("CamuleApp::ShutDown() has started."));
1386 // Signal the hashing thread to terminate
1387 m_app_state = APP_STATE_SHUTTINGDOWN;
1389 StopKad();
1391 // Kry - Save the sources seeds on app exit
1392 if (thePrefs::GetSrcSeedsOn()) {
1393 downloadqueue->SaveSourceSeeds();
1396 OnlineSig(true); // Added By Bouc7
1398 // Exit HTTP downloads
1399 CHTTPDownloadThread::StopAll();
1401 // Exit thread scheduler and upload thread
1402 CThreadScheduler::Terminate();
1404 AddDebugLogLineN(logGeneral, wxT("Terminate upload thread."));
1405 uploadBandwidthThrottler->EndThread();
1407 // Close sockets to avoid new clients coming in
1408 if (listensocket) {
1409 listensocket->Close();
1410 listensocket->KillAllSockets();
1413 if (serverconnect) {
1414 serverconnect->Disconnect();
1417 ECServerHandler->KillAllSockets();
1419 #ifdef ENABLE_UPNP
1420 if (thePrefs::GetUPnPEnabled()) {
1421 if (m_upnp) {
1422 m_upnp->DeletePortMappings(m_upnpMappings);
1425 #endif
1427 // saving data & stuff
1428 if (knownfiles) {
1429 knownfiles->Save();
1432 thePrefs::Add2TotalDownloaded(theStats::GetSessionReceivedBytes());
1433 thePrefs::Add2TotalUploaded(theStats::GetSessionSentBytes());
1435 if (glob_prefs) {
1436 glob_prefs->Save();
1439 CPath configFileName = CPath(ConfigDir + m_configFile);
1440 CPath::BackupFile(configFileName, wxT(".bak"));
1442 if (clientlist) {
1443 clientlist->DeleteAll();
1446 // Log
1447 AddDebugLogLineM(false, logGeneral, wxT("CamuleApp::ShutDown() has ended."));
1451 bool CamuleApp::AddServer(CServer *srv, bool fromUser)
1453 if ( serverlist->AddServer(srv, fromUser) ) {
1454 Notify_ServerAdd(srv);
1455 return true;
1457 return false;
1461 uint32 CamuleApp::GetPublicIP(bool ignorelocal) const
1463 if (m_dwPublicIP == 0) {
1464 if (Kademlia::CKademlia::IsConnected() && Kademlia::CKademlia::GetIPAddress() ) {
1465 return wxUINT32_SWAP_ALWAYS(Kademlia::CKademlia::GetIPAddress());
1466 } else {
1467 return ignorelocal ? 0 : m_localip;
1471 return m_dwPublicIP;
1475 void CamuleApp::SetPublicIP(const uint32 dwIP)
1477 wxASSERT((dwIP == 0) || !IsLowID(dwIP));
1479 if (dwIP != 0 && dwIP != m_dwPublicIP && serverlist != NULL) {
1480 m_dwPublicIP = dwIP;
1481 serverlist->CheckForExpiredUDPKeys();
1482 } else {
1483 m_dwPublicIP = dwIP;
1488 wxString CamuleApp::GetLog(bool reset)
1490 wxFile logfile;
1491 logfile.Open(ConfigDir + wxT("logfile"));
1492 if ( !logfile.IsOpened() ) {
1493 return _("ERROR: can't open logfile");
1495 int len = logfile.Length();
1496 if ( len == 0 ) {
1497 return _("WARNING: logfile is empty. Something is wrong.");
1499 char *tmp_buffer = new char[len + sizeof(wxChar)];
1500 logfile.Read(tmp_buffer, len);
1501 memset(tmp_buffer + len, 0, sizeof(wxChar));
1503 // try to guess file format
1504 wxString str;
1505 if (tmp_buffer[0] && tmp_buffer[1]) {
1506 str = wxString(UTF82unicode(tmp_buffer));
1507 } else {
1508 str = wxWCharBuffer((wchar_t *)tmp_buffer);
1511 delete [] tmp_buffer;
1512 if ( reset ) {
1513 theLogger.CloseLogfile();
1514 if (theLogger.OpenLogfile(ConfigDir + wxT("logfile"))) {
1515 AddLogLineM(false, _("Log has been reset"));
1518 return str;
1522 wxString CamuleApp::GetServerLog(bool reset)
1524 wxString ret = server_msg;
1525 if ( reset ) {
1526 server_msg.Clear();
1528 return ret;
1531 wxString CamuleApp::GetDebugLog(bool reset)
1533 return GetLog(reset);
1537 void CamuleApp::AddServerMessageLine(wxString &msg)
1539 server_msg += msg + wxT("\n");
1540 AddLogLineM(false, CFormat(_("ServerMessage: %s")) % msg);
1545 void CamuleApp::OnFinishedHTTPDownload(CMuleInternalEvent& event)
1547 switch (event.GetInt()) {
1548 case HTTP_IPFilter:
1549 ipfilter->DownloadFinished(event.GetExtraLong());
1550 break;
1551 case HTTP_ServerMet:
1552 serverlist->DownloadFinished(event.GetExtraLong());
1553 break;
1554 case HTTP_ServerMetAuto:
1555 serverlist->AutoDownloadFinished(event.GetExtraLong());
1556 break;
1557 case HTTP_VersionCheck:
1558 CheckNewVersion(event.GetExtraLong());
1559 break;
1560 case HTTP_NodesDat:
1561 if (event.GetExtraLong() == HTTP_Success) {
1563 wxString file = ConfigDir + wxT("nodes.dat");
1564 if (wxFileExists(file)) {
1565 wxRemoveFile(file);
1568 if ( Kademlia::CKademlia::IsRunning() ) {
1569 Kademlia::CKademlia::Stop();
1572 wxRenameFile(file + wxT(".download"),file);
1574 Kademlia::CKademlia::Start();
1575 theApp->ShowConnectionState();
1577 } else if (event.GetExtraLong() == HTTP_Skipped) {
1578 AddLogLineN(CFormat(_("Skipped download of %s, because requested file is not newer.")) % wxT("nodes.dat"));
1579 } else {
1580 AddLogLineC(_("Failed to download the nodes list."));
1582 break;
1583 #ifdef ENABLE_IP2COUNTRY
1584 case HTTP_GeoIP:
1585 theApp->amuledlg->IP2CountryDownloadFinished(event.GetExtraLong());
1586 // If we updated, the dialog is already up. Redraw it to show the flags.
1587 theApp->amuledlg->Refresh();
1588 break;
1589 #endif
1593 void CamuleApp::CheckNewVersion(uint32 result)
1595 if (result == HTTP_Success) {
1596 wxString filename = ConfigDir + wxT("last_version_check");
1597 wxTextFile file;
1599 if (!file.Open(filename)) {
1600 AddLogLineM(true, _("Failed to open the downloaded version check file") );
1601 return;
1602 } else if (!file.GetLineCount()) {
1603 AddLogLineM(true, _("Corrupted version check file"));
1604 } else {
1605 wxString versionLine = file.GetFirstLine();
1606 wxStringTokenizer tkz(versionLine, wxT("."));
1608 AddDebugLogLineM(false, logGeneral, wxString(wxT("Running: ")) + wxT(VERSION) + wxT(", Version check: ") + versionLine);
1610 long fields[] = {0, 0, 0};
1611 for (int i = 0; i < 3; ++i) {
1612 if (!tkz.HasMoreTokens()) {
1613 AddLogLineM(true, _("Corrupted version check file"));
1614 return;
1615 } else {
1616 wxString token = tkz.GetNextToken();
1618 if (!token.ToLong(&fields[i])) {
1619 AddLogLineM(true, _("Corrupted version check file"));
1620 return;
1625 long curVer = make_full_ed2k_version(VERSION_MJR, VERSION_MIN, VERSION_UPDATE);
1626 long newVer = make_full_ed2k_version(fields[0], fields[1], fields[2]);
1628 if (curVer < newVer) {
1629 AddLogLineM(true, _("You are using an outdated version of aMule!"));
1630 AddLogLineM(false, wxString::Format(_("Your aMule version is %i.%i.%i and the latest version is %li.%li.%li"), VERSION_MJR, VERSION_MIN, VERSION_UPDATE, fields[0], fields[1], fields[2]));
1631 AddLogLineM(false, _("The latest version can always be found at http://www.amule.org"));
1632 #ifdef AMULE_DAEMON
1633 AddLogLineMS(true, CFormat(_("WARNING: Your aMuled version is outdated: %i.%i.%i < %li.%li.%li"))
1634 % VERSION_MJR % VERSION_MIN % VERSION_UPDATE % fields[0] % fields[1] % fields[2]);
1635 #endif
1636 } else {
1637 AddLogLineM(false, _("Your copy of aMule is up to date."));
1641 file.Close();
1642 wxRemoveFile(filename);
1643 } else {
1644 AddLogLineM(true, _("Failed to download the version check file"));
1650 bool CamuleApp::IsConnected() const
1652 return (IsConnectedED2K() || IsConnectedKad());
1656 bool CamuleApp::IsConnectedED2K() const
1658 return serverconnect && serverconnect->IsConnected();
1662 bool CamuleApp::IsConnectedKad() const
1664 return Kademlia::CKademlia::IsConnected();
1668 bool CamuleApp::IsFirewalled() const
1670 if (theApp->IsConnectedED2K() && !theApp->serverconnect->IsLowID()) {
1671 return false; // we have an eD2K HighID -> not firewalled
1674 return IsFirewalledKad(); // If kad says ok, it's ok.
1677 bool CamuleApp::IsFirewalledKad() const
1679 return !Kademlia::CKademlia::IsConnected() // not connected counts as firewalled
1680 || Kademlia::CKademlia::IsFirewalled();
1683 bool CamuleApp::IsFirewalledKadUDP() const
1685 return !Kademlia::CKademlia::IsConnected() // not connected counts as firewalled
1686 || Kademlia::CUDPFirewallTester::IsFirewalledUDP(true);
1689 bool CamuleApp::IsKadRunning() const
1691 return Kademlia::CKademlia::IsRunning();
1694 // Kad stats
1695 uint32 CamuleApp::GetKadUsers() const
1697 return Kademlia::CKademlia::GetKademliaUsers();
1700 uint32 CamuleApp::GetKadFiles() const
1702 return Kademlia::CKademlia::GetKademliaFiles();
1705 uint32 CamuleApp::GetKadIndexedSources() const
1707 return Kademlia::CKademlia::GetIndexed()->m_totalIndexSource;
1710 uint32 CamuleApp::GetKadIndexedKeywords() const
1712 return Kademlia::CKademlia::GetIndexed()->m_totalIndexKeyword;
1715 uint32 CamuleApp::GetKadIndexedNotes() const
1717 return Kademlia::CKademlia::GetIndexed()->m_totalIndexNotes;
1720 uint32 CamuleApp::GetKadIndexedLoad() const
1722 return Kademlia::CKademlia::GetIndexed()->m_totalIndexLoad;
1726 // True IP of machine
1727 uint32 CamuleApp::GetKadIPAdress() const
1729 return wxUINT32_SWAP_ALWAYS(Kademlia::CKademlia::GetPrefs()->GetIPAddress());
1732 // Buddy status
1733 uint8 CamuleApp::GetBuddyStatus() const
1735 return clientlist->GetBuddyStatus();
1738 uint32 CamuleApp::GetBuddyIP() const
1740 return clientlist->GetBuddy()->GetIP();
1743 uint32 CamuleApp::GetBuddyPort() const
1745 return clientlist->GetBuddy()->GetUDPPort();
1748 bool CamuleApp::CanDoCallback(CUpDownClient *client)
1750 if (Kademlia::CKademlia::IsConnected()) {
1751 if (IsConnectedED2K()) {
1752 if (serverconnect->IsLowID()) {
1753 if (Kademlia::CKademlia::IsFirewalled()) {
1754 //Both Connected - Both Firewalled
1755 return false;
1756 } else {
1757 if (client->GetServerIP() == theApp->serverconnect->GetCurrentServer()->GetIP() &&
1758 client->GetServerPort() == theApp->serverconnect->GetCurrentServer()->GetPort()) {
1759 // Both Connected - Server lowID, Kad Open - Client on same server
1760 // We prevent a callback to the server as this breaks the protocol
1761 // and will get you banned.
1762 return false;
1763 } else {
1764 // Both Connected - Server lowID, Kad Open - Client on remote server
1765 return true;
1768 } else {
1769 //Both Connected - Server HighID, Kad don't care
1770 return true;
1772 } else {
1773 if (Kademlia::CKademlia::IsFirewalled()) {
1774 //Only Kad Connected - Kad Firewalled
1775 return false;
1776 } else {
1777 //Only Kad Conected - Kad Open
1778 return true;
1781 } else {
1782 if (IsConnectedED2K()) {
1783 if (serverconnect->IsLowID()) {
1784 //Only Server Connected - Server LowID
1785 return false;
1786 } else {
1787 //Only Server Connected - Server HighID
1788 return true;
1790 } else {
1791 //We are not connected at all!
1792 return false;
1797 void CamuleApp::ShowUserCount() {
1798 uint32 totaluser = 0, totalfile = 0;
1800 theApp->serverlist->GetUserFileStatus( totaluser, totalfile );
1802 wxString buffer;
1804 static const wxString s_singlenetstatusformat = _("Users: %s | Files: %s");
1805 static const wxString s_bothnetstatusformat = _("Users: E: %s K: %s | Files: E: %s K: %s");
1807 if (thePrefs::GetNetworkED2K() && thePrefs::GetNetworkKademlia()) {
1808 buffer = CFormat(s_bothnetstatusformat) % CastItoIShort(totaluser) % CastItoIShort(Kademlia::CKademlia::GetKademliaUsers()) % CastItoIShort(totalfile) % CastItoIShort(Kademlia::CKademlia::GetKademliaFiles());
1809 } else if (thePrefs::GetNetworkED2K()) {
1810 buffer = CFormat(s_singlenetstatusformat) % CastItoIShort(totaluser) % CastItoIShort(totalfile);
1811 } else if (thePrefs::GetNetworkKademlia()) {
1812 buffer = CFormat(s_singlenetstatusformat) % CastItoIShort(Kademlia::CKademlia::GetKademliaUsers()) % CastItoIShort(Kademlia::CKademlia::GetKademliaFiles());
1813 } else {
1814 buffer = _("No networks selected");
1817 Notify_ShowUserCount(buffer);
1821 void CamuleApp::ListenSocketHandler(wxSocketEvent& event)
1823 wxCHECK_RET(listensocket, wxT("Connection-event for NULL'd listen-socket"));
1824 wxCHECK_RET(event.GetSocketEvent() == wxSOCKET_CONNECTION,
1825 wxT("Invalid event received for listen-socket"));
1827 if (m_app_state == APP_STATE_RUNNING) {
1828 listensocket->OnAccept(0);
1829 } else if (m_app_state == APP_STATE_STARTING) {
1830 // When starting up, connection may be made before we are able
1831 // to handle them. However, if these are ignored, no futher
1832 // connection-events will be triggered, so we have to accept it.
1833 wxSocketBase* socket = listensocket->Accept(false);
1835 wxCHECK_RET(socket, wxT("NULL returned by Accept() during startup"));
1837 socket->Destroy();
1842 void CamuleApp::ShowConnectionState()
1844 static uint8 old_state = (1<<7); // This flag doesn't exist
1846 uint8 state = 0;
1848 if (theApp->serverconnect->IsConnected()) {
1849 state |= CONNECTED_ED2K;
1852 if (Kademlia::CKademlia::IsRunning()) {
1853 if (Kademlia::CKademlia::IsConnected()) {
1854 if (!Kademlia::CKademlia::IsFirewalled()) {
1855 state |= CONNECTED_KAD_OK;
1856 } else {
1857 state |= CONNECTED_KAD_FIREWALLED;
1859 } else {
1860 state |= CONNECTED_KAD_NOT;
1864 Notify_ShowConnState(state);
1866 if (old_state != state) {
1867 // Get the changed value
1868 int changed_flags = old_state ^ state;
1870 if (changed_flags & CONNECTED_ED2K) {
1871 // ED2K status changed
1872 wxString connected_server;
1873 CServer* ed2k_server = theApp->serverconnect->GetCurrentServer();
1874 if (ed2k_server) {
1875 connected_server = ed2k_server->GetListName();
1877 if (state & CONNECTED_ED2K) {
1878 // We connected to some server
1879 const wxString id = theApp->serverconnect->IsLowID() ? _("with LowID") : _("with HighID");
1881 AddLogLineM(true, CFormat(_("Connected to %s %s")) % connected_server % id);
1882 } else {
1883 if ( theApp->serverconnect->IsConnecting() ) {
1884 AddLogLineM(true, CFormat(_("Connecting to %s")) % connected_server);
1885 } else {
1886 AddLogLineM(true, _("Disconnected from eD2k"));
1891 if (changed_flags & CONNECTED_KAD_NOT) {
1892 if (state & CONNECTED_KAD_NOT) {
1893 AddLogLineM(true, _("Kad started."));
1894 } else {
1895 AddLogLineM(true, _("Kad stopped."));
1899 if (changed_flags & (CONNECTED_KAD_OK | CONNECTED_KAD_FIREWALLED)) {
1900 if (state & (CONNECTED_KAD_OK | CONNECTED_KAD_FIREWALLED)) {
1901 if (state & CONNECTED_KAD_OK) {
1902 AddLogLineM(true, _("Connected to Kad (ok)"));
1903 } else {
1904 AddLogLineM(true, _("Connected to Kad (firewalled)"));
1906 } else {
1907 AddLogLineM(true, _("Disconnected from Kad"));
1911 old_state = state;
1913 theApp->downloadqueue->OnConnectionState(IsConnected());
1916 ShowUserCount();
1917 Notify_ShowConnState(state);
1921 void CamuleApp::UDPSocketHandler(wxSocketEvent& event)
1923 CMuleUDPSocket* socket = (CMuleUDPSocket*)(event.GetClientData());
1924 wxCHECK_RET(socket, wxT("No socket owner specified."));
1926 if (IsOnShutDown() || thePrefs::IsUDPDisabled()) return;
1928 if (!IsRunning()) {
1929 if (event.GetSocketEvent() == wxSOCKET_INPUT) {
1930 // Back to the queue!
1931 theApp->AddPendingEvent(event);
1932 return;
1936 switch (event.GetSocketEvent()) {
1937 case wxSOCKET_INPUT:
1938 socket->OnReceive(0);
1939 break;
1941 case wxSOCKET_OUTPUT:
1942 socket->OnSend(0);
1943 break;
1945 case wxSOCKET_LOST:
1946 socket->OnDisconnected(0);
1947 break;
1949 default:
1950 wxFAIL;
1951 break;
1956 void CamuleApp::OnUnhandledException()
1958 // Call the generic exception-handler.
1959 fprintf(stderr, "\taMule Version: %s\n", (const char*)unicode2char(GetFullMuleVersion()));
1960 ::OnUnhandledException();
1963 void CamuleApp::StartKad()
1965 if (!Kademlia::CKademlia::IsRunning() && thePrefs::GetNetworkKademlia()) {
1966 // Kad makes no sense without the Client-UDP socket.
1967 if (!thePrefs::IsUDPDisabled()) {
1968 if (ipfilter->IsReady()) {
1969 Kademlia::CKademlia::Start();
1970 } else {
1971 ipfilter->StartKADWhenReady();
1973 } else {
1974 AddLogLineM(true,_("Kad network cannot be used if UDP port is disabled on preferences, not starting."));
1976 } else if (!thePrefs::GetNetworkKademlia()) {
1977 AddLogLineM(true,_("Kad network disabled on preferences, not connecting."));
1981 void CamuleApp::StopKad()
1983 // Stop Kad if it's running
1984 if (Kademlia::CKademlia::IsRunning()) {
1985 Kademlia::CKademlia::Stop();
1990 void CamuleApp::BootstrapKad(uint32 ip, uint16 port)
1992 if (!Kademlia::CKademlia::IsRunning()) {
1993 Kademlia::CKademlia::Start();
1994 theApp->ShowConnectionState();
1997 Kademlia::CKademlia::Bootstrap(ip, port, true);
2001 void CamuleApp::UpdateNotesDat(const wxString& url)
2003 wxString strTempFilename(theApp->ConfigDir + wxT("nodes.dat.download"));
2005 CHTTPDownloadThread *downloader = new CHTTPDownloadThread(url, strTempFilename, theApp->ConfigDir + wxT("nodes.dat"), HTTP_NodesDat);
2006 downloader->Create();
2007 downloader->Run();
2011 void CamuleApp::DisconnectED2K()
2013 // Stop ED2K if it's running
2014 if (IsConnectedED2K()) {
2015 serverconnect->Disconnect();
2019 bool CamuleApp::CryptoAvailable() const
2021 return clientcredits && clientcredits->CryptoAvailable();
2024 uint32 CamuleApp::GetED2KID() const {
2025 return serverconnect ? serverconnect->GetClientID() : 0;
2028 uint32 CamuleApp::GetID() const {
2029 uint32 ID;
2031 if( Kademlia::CKademlia::IsConnected() && !Kademlia::CKademlia::IsFirewalled() ) {
2032 // We trust Kad above ED2K
2033 ID = ENDIAN_NTOHL(Kademlia::CKademlia::GetIPAddress());
2034 } else if( theApp->serverconnect->IsConnected() ) {
2035 ID = theApp->serverconnect->GetClientID();
2036 } else if ( Kademlia::CKademlia::IsConnected() && Kademlia::CKademlia::IsFirewalled() ) {
2037 // A firewalled Kad client get's a "1"
2038 ID = 1;
2039 } else {
2040 ID = 0;
2043 return ID;
2046 DEFINE_LOCAL_EVENT_TYPE(wxEVT_CORE_FINISHED_HTTP_DOWNLOAD)
2047 DEFINE_LOCAL_EVENT_TYPE(wxEVT_CORE_SOURCE_DNS_DONE)
2048 DEFINE_LOCAL_EVENT_TYPE(wxEVT_CORE_UDP_DNS_DONE)
2049 DEFINE_LOCAL_EVENT_TYPE(wxEVT_CORE_SERVER_DNS_DONE)
2050 // File_checked_for_headers