Upstream tarball 9931
[amule.git] / src / amule.cpp
blob191c81bddf1c0d6cdf162f99bbfc901b78ec5f0e
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 #define AMULE_CPP
30 #include "amule.h" // Interface declarations.
32 #include <csignal>
33 #include <cstring>
34 #include <wx/process.h>
35 #include <wx/sstream.h>
37 #ifdef HAVE_CONFIG_H
38 #include "config.h" // Needed for HAVE_GETRLIMIT, HAVE_SETRLIMIT,
39 // HAVE_SYS_RESOURCE_H, HAVE_SYS_STATVFS_H, VERSION
40 // and ENABLE_NLS
41 #endif
43 #include <common/ClientVersion.h>
45 #include <wx/cmdline.h> // Needed for wxCmdLineParser
46 #include <wx/config.h> // Do_not_auto_remove (win32)
47 #include <wx/fileconf.h>
48 #include <wx/tokenzr.h>
49 #include <wx/wfstream.h>
52 #include <common/Format.h> // Needed for CFormat
53 #include "kademlia/kademlia/Kademlia.h"
54 #include "kademlia/kademlia/Prefs.h"
55 #include "kademlia/kademlia/UDPFirewallTester.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 "Preferences.h" // Needed for CPreferences
72 #include "SearchList.h" // Needed for CSearchList
73 #include "Server.h" // Needed for GetListName
74 #include "ServerList.h" // Needed for CServerList
75 #include "ServerConnect.h" // Needed for CServerConnect
76 #include "ServerUDPSocket.h" // Needed for CServerUDPSocket
77 #include "Statistics.h" // Needed for CStatistics
78 #include "TerminationProcessAmuleweb.h" // Needed for CTerminationProcessAmuleweb
79 #include "ThreadTasks.h"
80 #include "updownclient.h" // Needed for CUpDownClient
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 static void UnlimitResource(RLIMIT_RESOURCE resType)
126 #if defined(HAVE_GETRLIMIT) && defined(HAVE_SETRLIMIT)
127 struct rlimit rl;
128 getrlimit(resType, &rl);
129 rl.rlim_cur = rl.rlim_max;
130 setrlimit(resType, &rl);
131 #endif
135 static void SetResourceLimits()
137 #ifdef HAVE_SYS_RESOURCE_H
138 UnlimitResource(RLIMIT_DATA);
139 #ifndef __UCLIBC__
140 UnlimitResource(RLIMIT_FSIZE);
141 #endif
142 UnlimitResource(RLIMIT_NOFILE);
143 #ifdef RLIMIT_RSS
144 UnlimitResource(RLIMIT_RSS);
145 #endif
146 #endif
149 // We store the received signal in order to avoid race-conditions
150 // in the signal handler.
151 bool g_shutdownSignal = false;
153 void OnShutdownSignal( int /* sig */ )
155 signal(SIGINT, SIG_DFL);
156 signal(SIGTERM, SIG_DFL);
158 g_shutdownSignal = true;
160 #ifdef AMULE_DAEMON
161 theApp->ExitMainLoop();
162 #endif
166 CamuleApp::CamuleApp()
168 // Madcat - Initialize timer as the VERY FIRST thing to avoid any issues later.
169 // Kry - I love to init the vars on init, even before timer.
170 StartTickTimer();
172 // Initialization
173 m_app_state = APP_STATE_STARTING;
175 theApp = &wxGetApp();
177 clientlist = NULL;
178 searchlist = NULL;
179 knownfiles = NULL;
180 serverlist = NULL;
181 serverconnect = NULL;
182 sharedfiles = NULL;
183 listensocket = NULL;
184 clientudp = NULL;
185 clientcredits = NULL;
186 friendlist = NULL;
187 downloadqueue = NULL;
188 uploadqueue = NULL;
189 ipfilter = NULL;
190 ECServerHandler = NULL;
191 glob_prefs = NULL;
192 m_statistics = NULL;
193 uploadBandwidthThrottler = NULL;
194 #ifdef ENABLE_UPNP
195 m_upnp = NULL;
196 m_upnpMappings.resize(4);
197 #endif
198 core_timer = NULL;
200 m_localip = 0;
201 m_dwPublicIP = 0;
202 webserver_pid = 0;
204 enable_daemon_fork = false;
206 strFullMuleVersion = NULL;
207 strOSDescription = NULL;
209 // Apprently needed for *BSD
210 SetResourceLimits();
213 CamuleApp::~CamuleApp()
215 // Closing the log-file as the very last thing, since
216 // wxWidgets log-events are saved in it as well.
217 theLogger.CloseLogfile();
219 free(strFullMuleVersion);
220 free(strOSDescription);
223 int CamuleApp::OnExit()
225 if (m_app_state!=APP_STATE_STARTING) {
226 AddLogLineMS(false, _("Now, exiting main app..."));
229 // From wxWidgets docs, wxConfigBase:
230 // ...
231 // Note that you must delete this object (usually in wxApp::OnExit)
232 // in order to avoid memory leaks, wxWidgets won't do it automatically.
234 // As it happens, you may even further simplify the procedure described
235 // above: you may forget about calling Set(). When Get() is called and
236 // there is no current object, it will create one using Create() function.
237 // To disable this behaviour DontCreateOnDemand() is provided.
238 delete wxConfigBase::Set((wxConfigBase *)NULL);
240 // Save credits
241 clientcredits->SaveList();
243 // Kill amuleweb if running
244 if (webserver_pid) {
245 AddLogLineNS(CFormat(_("Terminating amuleweb instance with pid `%ld' ... ")) % webserver_pid);
246 wxKillError rc;
247 if (wxKill(webserver_pid, wxSIGTERM, &rc) == -1) {
248 AddLogLineNS(CFormat(_("Killing amuleweb instance with pid `%ld' ... ")) % webserver_pid);
249 if (wxKill(webserver_pid, wxSIGKILL, &rc) == -1) {
250 AddLogLineNS(_("Failed"));
255 if (m_app_state!=APP_STATE_STARTING) {
256 AddLogLineMS(false, _("aMule OnExit: Terminating core."));
259 delete serverlist;
260 serverlist = NULL;
262 delete searchlist;
263 searchlist = NULL;
265 delete clientcredits;
266 clientcredits = NULL;
268 delete friendlist;
269 friendlist = NULL;
271 // Destroying CDownloadQueue calls destructor for CPartFile
272 // calling CSharedFileList::SafeAddKFile occasionally.
273 delete sharedfiles;
274 sharedfiles = NULL;
276 delete serverconnect;
277 serverconnect = NULL;
279 delete listensocket;
280 listensocket = NULL;
282 delete clientudp;
283 clientudp = NULL;
285 delete knownfiles;
286 knownfiles = NULL;
288 delete clientlist;
289 clientlist = NULL;
291 delete uploadqueue;
292 uploadqueue = NULL;
294 delete downloadqueue;
295 downloadqueue = NULL;
297 delete ipfilter;
298 ipfilter = NULL;
300 #ifdef ENABLE_UPNP
301 delete m_upnp;
302 m_upnp = NULL;
303 #endif
305 delete ECServerHandler;
306 ECServerHandler = NULL;
308 delete m_statistics;
309 m_statistics = NULL;
311 delete glob_prefs;
312 glob_prefs = NULL;
313 CPreferences::EraseItemList();
315 delete uploadBandwidthThrottler;
316 uploadBandwidthThrottler = NULL;
318 if (m_app_state!=APP_STATE_STARTING) {
319 AddLogLineNS(_("aMule shutdown completed."));
322 #if wxUSE_MEMORY_TRACING
323 AddLogLineNS(_("Memory debug results for aMule exit:"));
324 // Log mem debug mesages to wxLogStderr
325 wxLog* oldLog = wxLog::SetActiveTarget(new wxLogStderr);
326 //AddLogLineNS(wxT("**************Classes**************");
327 //wxDebugContext::PrintClasses();
328 //AddLogLineNS(wxT("***************Dump***************");
329 //wxDebugContext::Dump();
330 AddLogLineNS(wxT("***************Stats**************"));
331 wxDebugContext::PrintStatistics(true);
333 // Set back to wxLogGui
334 delete wxLog::SetActiveTarget(oldLog);
335 #endif
337 StopTickTimer();
339 // Return 0 for succesful program termination
340 return AMULE_APP_BASE::OnExit();
344 int CamuleApp::InitGui(bool, wxString &)
346 return 0;
351 // Application initialization
353 bool CamuleApp::OnInit()
355 #if wxUSE_MEMORY_TRACING
356 // any text before call of Localize_mule needs not to be translated.
357 AddLogLineMS(false, wxT("Checkpoint set on app init for memory debug"));
358 wxDebugContext::SetCheckpoint();
359 #endif
361 // Forward wxLog events to CLogger
362 wxLog::SetActiveTarget(new CLoggerTarget);
364 m_localip = StringHosttoUint32(::wxGetFullHostName());
366 #ifndef __WXMSW__
367 // get rid of sigpipe
368 signal(SIGPIPE, SIG_IGN);
369 #else
370 // Handle CTRL-Break
371 signal(SIGBREAK, OnShutdownSignal);
372 #endif
373 // Handle sigint and sigterm
374 signal(SIGINT, OnShutdownSignal);
375 signal(SIGTERM, OnShutdownSignal);
377 #ifdef __WXMAC__
378 // For listctrl's to behave on Mac
379 wxSystemOptions::SetOption(wxT("mac.listctrl.always_use_generic"), 1);
380 #endif
382 // Handle uncaught exceptions
383 InstallMuleExceptionHandler();
385 if (!InitCommon(AMULE_APP_BASE::argc, AMULE_APP_BASE::argv)) {
386 return false;
389 glob_prefs = new CPreferences();
391 CPath outDir;
392 if (CheckMuleDirectory(wxT("temp"), thePrefs::GetTempDir(), ConfigDir + wxT("Temp"), outDir)) {
393 thePrefs::SetTempDir(outDir);
394 } else {
395 return false;
398 if (CheckMuleDirectory(wxT("incoming"), thePrefs::GetIncomingDir(), ConfigDir + wxT("Incoming"), outDir)) {
399 thePrefs::SetIncomingDir(outDir);
400 } else {
401 return false;
404 // Some sanity check
405 if (!thePrefs::UseTrayIcon()) {
406 thePrefs::SetMinToTray(false);
409 // Build the filenames for the two OS files
410 SetOSFiles(thePrefs::GetOSDir().GetRaw());
412 #ifdef ENABLE_NLS
413 // Load localization settings
414 Localize_mule();
415 #endif
417 // Configure EC for amuled when invoked with ec-config
418 if (ec_config) {
419 AddLogLineMS(false, _("\nEC configuration"));
420 thePrefs::SetECPass(GetPassword());
421 thePrefs::EnableExternalConnections(true);
422 AddLogLineMS(false, _("Password set and external connections enabled."));
425 #ifndef __WXMSW__
426 if (getuid() == 0) {
427 wxString msg =
428 wxT("Warning! You are running aMule as root.\n")
429 wxT("Doing so is not recommended for security reasons,\n")
430 wxT("and you are advised to run aMule as an normal\n")
431 wxT("user instead.");
433 ShowAlert(msg, _("WARNING"), wxCENTRE | wxOK | wxICON_ERROR);
435 fprintf(stderr, "\n--------------------------------------------------\n");
436 fprintf(stderr, "%s", (const char*)unicode2UTF8(msg));
437 fprintf(stderr, "\n--------------------------------------------------\n\n");
439 #endif
441 // Display notification on new version or first run
442 wxTextFile vfile( ConfigDir + wxT("lastversion") );
443 wxString newMule(wxT( VERSION ));
445 if ( !wxFileExists( vfile.GetName() ) ) {
446 vfile.Create();
449 if ( vfile.Open() ) {
450 // Check if this version has been run before
451 bool found = false;
452 for ( size_t i = 0; i < vfile.GetLineCount(); i++ ) {
453 // Check if this version has been run before
454 if ( vfile.GetLine(i) == newMule ) {
455 found = true;
456 break;
460 // We havent run this version before?
461 if ( !found ) {
462 // Insert new at top to provide faster searches
463 vfile.InsertLine( newMule, 0 );
465 Trigger_New_version( newMule );
468 // Keep at most 10 entires
469 while ( vfile.GetLineCount() > 10 )
470 vfile.RemoveLine( vfile.GetLineCount() - 1 );
472 vfile.Write();
473 vfile.Close();
476 // Check if we have the old style locale config
477 wxString langId = thePrefs::GetLanguageID();
478 if (!langId.IsEmpty() && (langId.GetChar(0) >= '0' && langId.GetChar(0) <= '9')) {
479 wxString info(_("Your locale has been changed to System Default due to a configuration change. Sorry."));
480 thePrefs::SetLanguageID(wxLang2Str(wxLANGUAGE_DEFAULT));
481 ShowAlert(info, _("Info"), wxCENTRE | wxOK | wxICON_ERROR);
484 m_statistics = new CStatistics();
486 clientlist = new CClientList();
487 friendlist = new CFriendList();
488 searchlist = new CSearchList();
489 knownfiles = new CKnownFileList();
490 serverlist = new CServerList();
492 sharedfiles = new CSharedFileList(knownfiles);
493 clientcredits = new CClientCreditsList();
495 // bugfix - do this before creating the uploadqueue
496 downloadqueue = new CDownloadQueue();
497 uploadqueue = new CUploadQueue();
498 ipfilter = new CIPFilter();
500 // Creates all needed listening sockets
501 wxString msg;
502 if (!ReinitializeNetwork(&msg)) {
503 AddLogLineMS(false, wxT("\n"));
504 AddLogLineMS(false, msg);
507 // Test if there's any new version
508 if (thePrefs::CheckNewVersion()) {
509 // We use the thread base because I don't want a dialog to pop up.
510 CHTTPDownloadThread* version_check =
511 new CHTTPDownloadThread(wxT("http://amule.sourceforge.net/lastversion"),
512 theApp->ConfigDir + wxT("last_version_check"), HTTP_VersionCheck, false);
513 version_check->Create();
514 version_check->Run();
517 // Create main dialog, or fork to background (daemon).
518 InitGui(m_geometryEnabled, m_geometryString);
520 #ifdef AMULE_DAEMON
521 // Need to refresh wxSingleInstanceChecker after the daemon fork() !
522 if (enable_daemon_fork) {
523 RefreshSingleInstanceChecker();
524 // No need to check IsAnotherRunning() - we've done it before.
526 #endif
528 // Has to be created after the call to InitGui, as fork
529 // (when using posix threads) only replicates the mainthread,
530 // and the UBT constructor creates a thread.
531 uploadBandwidthThrottler = new UploadBandwidthThrottler();
533 // These must be initialized after the gui is loaded.
534 serverlist->Init();
535 downloadqueue->LoadMetFiles(thePrefs::GetTempDir());
536 sharedfiles->Reload();
538 if (thePrefs::IPFilterAutoLoad()) {
539 ipfilter->Update(thePrefs::IPFilterURL());
543 // Ensure that the up/down ratio is used
544 CPreferences::CheckUlDlRatio();
546 // The user can start pressing buttons like mad if he feels like it.
547 m_app_state = APP_STATE_RUNNING;
549 // Kry - Load the sources seeds on app init
550 if (thePrefs::GetSrcSeedsOn()) {
551 downloadqueue->LoadSourceSeeds();
554 if (!serverlist->GetServerCount() && thePrefs::GetNetworkED2K()) {
555 // There are no servers and ED2K active -> ask for download.
556 // As we cannot ask in amuled, we just update there
557 // Kry TODO: Store server.met URL on preferences and use it here and in GUI.
558 #ifndef AMULE_DAEMON
559 if (wxYES == wxMessageBox(
560 wxString(
561 _("You don't have any server in the server list.\nDo you want aMule to download a new list now?")),
562 wxString(_("Server list download")),
563 wxYES_NO,
564 static_cast<wxWindow*>(theApp->amuledlg)))
565 #endif
567 // workaround amuled crash
568 #ifndef AMULE_DAEMON
569 serverlist->UpdateServerMetFromURL(
570 wxT("http://gruk.org/server.met.gz"));
571 #endif
576 // Autoconnect if that option is enabled
577 if (thePrefs::DoAutoConnect() && (thePrefs::GetNetworkED2K() || thePrefs::GetNetworkKademlia())) {
578 AddLogLineM(true, _("Connecting"));
579 if (thePrefs::GetNetworkED2K()) {
580 theApp->serverconnect->ConnectToAnyServer();
583 StartKad();
587 // Enable GeoIP
588 #ifdef ENABLE_IP2COUNTRY
589 theApp->amuledlg->EnableIP2Country();
590 #endif
592 // Run webserver?
593 if (thePrefs::GetWSIsEnabled()) {
594 wxString aMuleConfigFile = ConfigDir + m_configFile;
595 wxString amulewebPath = thePrefs::GetWSPath();
597 #if defined(__WXMAC__) && !defined(AMULE_DAEMON)
598 // For the Mac GUI application, look for amuleweb in the bundle
599 CFURLRef amulewebUrl = CFBundleCopyAuxiliaryExecutableURL(
600 CFBundleGetMainBundle(), CFSTR("amuleweb"));
602 if (amulewebUrl) {
603 CFURLRef absoluteUrl = CFURLCopyAbsoluteURL(amulewebUrl);
604 CFRelease(amulewebUrl);
606 if (absoluteUrl) {
607 CFStringRef amulewebCfstr = CFURLCopyFileSystemPath(absoluteUrl, kCFURLPOSIXPathStyle);
608 CFRelease(absoluteUrl);
609 #if wxCHECK_VERSION(2, 9, 0)
610 amulewebPath = wxCFStringRef(amulewebCfstr).AsString(wxLocale::GetSystemEncoding());
611 #else
612 amulewebPath = wxMacCFStringHolder(amulewebCfstr).AsString(wxLocale::GetSystemEncoding());
613 #endif
616 #endif
618 #ifdef __WXMSW__
619 # define QUOTE wxT("\"")
620 #else
621 # define QUOTE wxT("\'")
622 #endif
624 wxString cmd =
625 QUOTE +
626 amulewebPath +
627 QUOTE wxT(" ") QUOTE wxT("--amule-config-file=") +
628 aMuleConfigFile +
629 QUOTE;
630 CTerminationProcessAmuleweb *p = new CTerminationProcessAmuleweb(cmd, &webserver_pid);
631 webserver_pid = wxExecute(cmd, wxEXEC_ASYNC, p);
632 bool webserver_ok = webserver_pid > 0;
633 if (webserver_ok) {
634 AddLogLineM(true, CFormat(_("web server running on pid %d")) % webserver_pid);
635 } else {
636 delete p;
637 ShowAlert(_(
638 "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"),
639 _("ERROR"), wxOK | wxICON_ERROR);
643 // Start performing background tasks
644 CThreadScheduler::Start();
646 return true;
649 bool CamuleApp::ReinitializeNetwork(wxString* msg)
651 bool ok = true;
652 static bool firstTime = true;
654 if (!firstTime) {
655 // TODO: Destroy previously created sockets
657 firstTime = false;
659 // Some sanity checks first
660 if (thePrefs::ECPort() == thePrefs::GetPort()) {
661 // Select a random usable port in the range 1025 ... 2^16 - 1
662 uint16 port = thePrefs::ECPort();
663 while ( port < 1024 || port == thePrefs::GetPort() ) {
664 port = (uint16)rand();
666 thePrefs::SetECPort( port );
668 wxString err =
669 wxT("Network configuration failed! You cannot use the same port\n")
670 wxT("for the main TCP port and the External Connections port.\n")
671 wxT("The EC port has been changed to avoid conflict, see the\n")
672 wxT("preferences for the new value.\n");
673 *msg << err;
675 AddLogLineM( false, wxEmptyString );
676 AddLogLineM( true, err );
677 AddLogLineM( false, wxEmptyString );
679 ok = false;
682 if (thePrefs::GetUDPPort() == thePrefs::GetPort() + 3) {
683 // Select a random usable value in the range 1025 ... 2^16 - 1
684 uint16 port = thePrefs::GetUDPPort();
685 while ( port < 1024 || port == thePrefs::GetPort() + 3 ) {
686 port = (uint16)rand();
688 thePrefs::SetUDPPort( port );
690 wxString err =
691 wxT("Network configuration failed! You set your UDP port to\n")
692 wxT("the value of the main TCP port plus 3.\n")
693 wxT("This port has been reserved for the Server-UDP port. The\n")
694 wxT("port value has been changed to avoid conflict, see the\n")
695 wxT("preferences for the new value\n");
696 *msg << err;
698 AddLogLineM( false, wxEmptyString );
699 AddLogLineM( true, err );
700 AddLogLineM( false, wxEmptyString );
702 ok = false;
705 // Create the address where we are going to listen
706 // TODO: read this from configuration file
707 amuleIPV4Address myaddr[4];
709 // Create the External Connections Socket.
710 // Default is 4712.
711 // Get ready to handle connections from apps like amulecmd
712 if (thePrefs::GetECAddress().IsEmpty() || !myaddr[0].Hostname(thePrefs::GetECAddress())) {
713 myaddr[0].AnyAddress();
715 myaddr[0].Service(thePrefs::ECPort());
716 ECServerHandler = new ExternalConn(myaddr[0], msg);
718 // Create the UDP socket TCP+3.
719 // Used for source asking on servers.
720 if (thePrefs::GetAddress().IsEmpty()) {
721 myaddr[1].AnyAddress();
722 } else if (!myaddr[1].Hostname(thePrefs::GetAddress())) {
723 myaddr[1].AnyAddress();
724 AddLogLineM(true, CFormat(_("Could not bind ports to the specified address: %s"))
725 % thePrefs::GetAddress());
728 wxString ip = myaddr[1].IPAddress();
729 myaddr[1].Service(thePrefs::GetPort()+3);
730 serverconnect = new CServerConnect(serverlist, myaddr[1]);
731 *msg << CFormat( wxT("*** Server UDP socket (TCP+3) at %s:%u\n") )
732 % ip % ((unsigned int)thePrefs::GetPort() + 3u);
734 // Create the ListenSocket (aMule TCP socket).
735 // Used for Client Port / Connections from other clients,
736 // Client to Client Source Exchange.
737 // Default is 4662.
738 myaddr[2] = myaddr[1];
739 myaddr[2].Service(thePrefs::GetPort());
740 listensocket = new CListenSocket(myaddr[2]);
741 *msg << CFormat( wxT("*** TCP socket (TCP) listening on %s:%u\n") )
742 % ip % (unsigned int)(thePrefs::GetPort());
743 // This command just sets a flag to control maximum number of connections.
744 // Notify(true) has already been called to the ListenSocket, so events may
745 // be already comming in.
746 if (listensocket->Ok()) {
747 listensocket->StartListening();
748 } else {
749 // If we wern't able to start listening, we need to warn the user
750 wxString err;
751 err = CFormat(_("Port %u is not available. You will be LOWID\n")) %
752 (unsigned int)(thePrefs::GetPort());
753 *msg << err;
754 AddLogLineM(true, err);
755 err.Clear();
756 err = CFormat(
757 _("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.")) %
758 (unsigned int)(thePrefs::GetPort());
759 ShowAlert(err, _("ERROR"), wxOK | wxICON_ERROR);
762 // Create the UDP socket.
763 // Used for extended eMule protocol, Queue Rating, File Reask Ping.
764 // Also used for Kademlia.
765 // Default is port 4672.
766 myaddr[3] = myaddr[1];
767 myaddr[3].Service(thePrefs::GetUDPPort());
768 clientudp = new CClientUDPSocket(myaddr[3], thePrefs::GetProxyData());
769 if (!thePrefs::IsUDPDisabled()) {
770 *msg << CFormat( wxT("*** Client UDP socket (extended eMule) at %s:%u") )
771 % ip % thePrefs::GetUDPPort();
772 } else {
773 *msg << wxT("*** Client UDP socket (extended eMule) disabled on preferences");
776 #ifdef ENABLE_UPNP
777 if (thePrefs::GetUPnPEnabled()) {
778 try {
779 m_upnpMappings[0] = CUPnPPortMapping(
780 myaddr[0].Service(),
781 "TCP",
782 thePrefs::GetUPnPECEnabled(),
783 "aMule TCP External Connections Socket");
784 m_upnpMappings[1] = CUPnPPortMapping(
785 myaddr[1].Service(),
786 "UDP",
787 thePrefs::GetUPnPEnabled(),
788 "aMule UDP socket (TCP+3)");
789 m_upnpMappings[2] = CUPnPPortMapping(
790 myaddr[2].Service(),
791 "TCP",
792 thePrefs::GetUPnPEnabled(),
793 "aMule TCP Listen Socket");
794 m_upnpMappings[3] = CUPnPPortMapping(
795 myaddr[3].Service(),
796 "UDP",
797 thePrefs::GetUPnPEnabled(),
798 "aMule UDP Extended eMule Socket");
799 m_upnp = new CUPnPControlPoint(thePrefs::GetUPnPTCPPort());
800 m_upnp->AddPortMappings(m_upnpMappings);
801 } catch(CUPnPException &e) {
802 wxString error_msg;
803 error_msg << e.what();
804 AddLogLineM(true, error_msg);
805 fprintf(stderr, "%s\n", (const char *)unicode2char(error_msg));
808 #endif
810 return ok;
813 /* Original implementation by Bouc7 of the eMule Project.
814 aMule Signature idea was designed by BigBob and implemented
815 by Un-Thesis, with design inputs and suggestions from bothie.
817 void CamuleApp::OnlineSig(bool zero /* reset stats (used on shutdown) */)
819 // Do not do anything if online signature is disabled in Preferences
820 if (!thePrefs::IsOnlineSignatureEnabled() || m_emulesig_path.IsEmpty()) {
821 // We do not need to check m_amulesig_path because if m_emulesig_path is empty,
822 // that means m_amulesig_path is empty too.
823 return;
826 // Remove old signature files
827 if ( wxFileExists( m_emulesig_path ) ) { wxRemoveFile( m_emulesig_path ); }
828 if ( wxFileExists( m_amulesig_path ) ) { wxRemoveFile( m_amulesig_path ); }
831 wxTextFile amulesig_out;
832 wxTextFile emulesig_out;
834 // Open both files if needed
835 if ( !emulesig_out.Create( m_emulesig_path) ) {
836 AddLogLineM(true, _("Failed to create OnlineSig File"));
837 // Will never try again.
838 m_amulesig_path.Clear();
839 m_emulesig_path.Clear();
840 return;
843 if ( !amulesig_out.Create(m_amulesig_path) ) {
844 AddLogLineM(true, _("Failed to create aMule OnlineSig File"));
845 // Will never try again.
846 m_amulesig_path.Clear();
847 m_emulesig_path.Clear();
848 return;
851 wxString emulesig_string;
852 wxString temp;
854 if (zero) {
855 emulesig_string = wxT("0\xA0.0|0.0|0");
856 amulesig_out.AddLine(wxT("0\n0\n0\n0\n0\n0\n0.0\n0.0\n0\n0"));
857 } else {
858 if (IsConnectedED2K()) {
860 temp = wxString::Format(wxT("%d"),serverconnect->GetCurrentServer()->GetPort());
862 // We are online
863 emulesig_string =
864 // Connected
865 wxT("1|")
866 //Server name
867 + serverconnect->GetCurrentServer()->GetListName()
868 + wxT("|")
869 // IP and port of the server
870 + serverconnect->GetCurrentServer()->GetFullIP()
871 + wxT("|")
872 + temp;
875 // Now for amule sig
877 // Connected. State 1, full info
878 amulesig_out.AddLine(wxT("1"));
879 // Server Name
880 amulesig_out.AddLine(serverconnect->GetCurrentServer()->GetListName());
881 // Server IP
882 amulesig_out.AddLine(serverconnect->GetCurrentServer()->GetFullIP());
883 // Server Port
884 amulesig_out.AddLine(temp);
886 if (serverconnect->IsLowID()) {
887 amulesig_out.AddLine(wxT("L"));
888 } else {
889 amulesig_out.AddLine(wxT("H"));
892 } else if (serverconnect->IsConnecting()) {
893 emulesig_string = wxT("0");
895 // Connecting. State 2, No info.
896 amulesig_out.AddLine(wxT("2\n0\n0\n0\n0"));
897 } else {
898 // Not connected to a server
899 emulesig_string = wxT("0");
901 // Not connected, state 0, no info
902 amulesig_out.AddLine(wxT("0\n0\n0\n0\n0"));
904 if (IsConnectedKad()) {
905 if(Kademlia::CKademlia::IsFirewalled()) {
906 // Connected. Firewalled. State 1.
907 amulesig_out.AddLine(wxT("1"));
908 } else {
909 // Connected. State 2.
910 amulesig_out.AddLine(wxT("2"));
912 } else {
913 // Not connected.State 0.
914 amulesig_out.AddLine(wxT("0"));
916 emulesig_string += wxT("\xA");
918 // Datarate for downloads
919 temp = wxString::Format(wxT("%.1f"), theStats::GetDownloadRate() / 1024.0);
921 emulesig_string += temp + wxT("|");
922 amulesig_out.AddLine(temp);
924 // Datarate for uploads
925 temp = wxString::Format(wxT("%.1f"), theStats::GetUploadRate() / 1024.0);
927 emulesig_string += temp + wxT("|");
928 amulesig_out.AddLine(temp);
930 // Number of users waiting for upload
931 temp = wxString::Format(wxT("%d"), theStats::GetWaitingUserCount());
933 emulesig_string += temp;
934 amulesig_out.AddLine(temp);
936 // Number of shared files (not on eMule)
937 amulesig_out.AddLine(wxString::Format(wxT("%d"), theStats::GetSharedFileCount()));
940 // eMule signature finished here. Write the line to the wxTextFile.
941 emulesig_out.AddLine(emulesig_string);
943 // Now for aMule signature extras
945 // Nick on the network
946 amulesig_out.AddLine(thePrefs::GetUserNick());
948 // Total received in bytes
949 amulesig_out.AddLine( CFormat( wxT("%llu") ) % (theStats::GetSessionReceivedBytes() + thePrefs::GetTotalDownloaded()) );
951 // Total sent in bytes
952 amulesig_out.AddLine( CFormat( wxT("%llu") ) % (theStats::GetSessionSentBytes() + thePrefs::GetTotalUploaded()) );
954 // amule version
955 #ifdef SVNDATE
956 amulesig_out.AddLine(wxT(VERSION) wxT(" ") wxT(SVNDATE));
957 #else
958 amulesig_out.AddLine(wxT(VERSION));
959 #endif
961 if (zero) {
962 amulesig_out.AddLine(wxT("0"));
963 amulesig_out.AddLine(wxT("0"));
964 amulesig_out.AddLine(wxT("0"));
965 } else {
966 // Total received bytes in session
967 amulesig_out.AddLine( CFormat( wxT("%llu") ) %
968 theStats::GetSessionReceivedBytes() );
970 // Total sent bytes in session
971 amulesig_out.AddLine( CFormat( wxT("%llu") ) %
972 theStats::GetSessionSentBytes() );
974 // Uptime
975 amulesig_out.AddLine(CFormat(wxT("%llu")) % theStats::GetUptimeSeconds());
978 // Flush the files
979 emulesig_out.Write();
980 amulesig_out.Write();
981 } //End Added By Bouc7
984 // Gracefully handle fatal exceptions and print backtrace if possible
985 void CamuleApp::OnFatalException()
987 /* Print the backtrace */
988 fprintf(stderr, "\n--------------------------------------------------------------------------------\n");
989 fprintf(stderr, "A fatal error has occurred and aMule has crashed.\n");
990 fprintf(stderr, "Please assist us in fixing this problem by posting the backtrace below in our\n");
991 fprintf(stderr, "'aMule Crashes' forum and include as much information as possible regarding the\n");
992 fprintf(stderr, "circumstances of this crash. The forum is located here:\n");
993 fprintf(stderr, " http://forum.amule.org/index.php?board=67.0\n");
994 fprintf(stderr, "If possible, please try to generate a real backtrace of this crash:\n");
995 fprintf(stderr, " http://wiki.amule.org/index.php/Backtraces\n\n");
996 fprintf(stderr, "----------------------------=| BACKTRACE FOLLOWS: |=----------------------------\n");
997 fprintf(stderr, "Current version is: %s\n", strFullMuleVersion);
998 fprintf(stderr, "Running on: %s\n\n", strOSDescription);
1000 print_backtrace(1); // 1 == skip this function.
1002 fprintf(stderr, "\n--------------------------------------------------------------------------------\n");
1006 // Sets the localization of aMule
1007 void CamuleApp::Localize_mule()
1009 InitCustomLanguages();
1010 InitLocale(m_locale, StrLang2wx(thePrefs::GetLanguageID()));
1011 if (!m_locale.IsOk()) {
1012 AddLogLineM(false,_("The selected locale seems not to be installed on your box. (Note: I'll try to set it anyway)"));
1017 // Displays information related to important changes in aMule.
1018 // Is called when the user runs a new version of aMule
1019 void CamuleApp::Trigger_New_version(wxString new_version)
1021 wxString info = wxT(" --- ") + CFormat(_("This is the first time you run aMule %s")) % new_version + wxT(" ---\n\n");
1022 if (new_version == wxT("SVN")) {
1023 info += _("This version is a testing version, updated daily, and\n");
1024 info += _("we give no warranty it won't break anything, burn your house,\n");
1025 info += _("or kill your dog. But it *should* be safe to use anyway.\n");
1028 // General info
1029 info += wxT("\n");
1030 info += _("More information, support and new releases can found at our homepage,\n");
1031 info += _("at www.aMule.org, or in our IRC channel #aMule at irc.freenode.net.\n");
1032 info += wxT("\n");
1033 info += _("Feel free to report any bugs to http://forum.amule.org");
1035 ShowAlert(info, _("Info"), wxCENTRE | wxOK | wxICON_ERROR);
1039 void CamuleApp::SetOSFiles(const wxString new_path)
1041 if ( thePrefs::IsOnlineSignatureEnabled() ) {
1042 if ( ::wxDirExists(new_path) ) {
1043 m_emulesig_path = JoinPaths(new_path, wxT("onlinesig.dat"));
1044 m_amulesig_path = JoinPaths(new_path, wxT("amulesig.dat"));
1045 } else {
1046 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);
1047 m_emulesig_path.Clear();
1048 m_amulesig_path.Clear();
1050 } else {
1051 m_emulesig_path.Clear();
1052 m_amulesig_path.Clear();
1057 #ifdef __WXDEBUG__
1058 #ifndef wxUSE_STACKWALKER
1059 #define wxUSE_STACKWALKER 0
1060 #endif
1061 void CamuleApp::OnAssertFailure(const wxChar* file, int line,
1062 const wxChar* func, const wxChar* cond, const wxChar* msg)
1064 if (!wxUSE_STACKWALKER || !wxThread::IsMain() || !IsRunning()) {
1065 wxString errmsg = CFormat( wxT("%s:%s:%d: Assertion '%s' failed. %s") )
1066 % file % func % line % cond % ( msg ? msg : wxT("") );
1068 fprintf(stderr, "Assertion failed: %s\n", (const char*)unicode2char(errmsg));
1070 // Skip the function-calls directly related to the assert call.
1071 fprintf(stderr, "\nBacktrace follows:\n");
1072 print_backtrace(3);
1073 fprintf(stderr, "\n");
1076 if (wxThread::IsMain() && IsRunning()) {
1077 AMULE_APP_BASE::OnAssertFailure(file, line, func, cond, msg);
1078 } else {
1079 // Abort, allows gdb to catch the assertion
1080 raise( SIGABRT );
1083 #endif
1086 void CamuleApp::OnUDPDnsDone(CMuleInternalEvent& evt)
1088 CServerUDPSocket* socket =(CServerUDPSocket*)evt.GetClientData();
1089 socket->OnHostnameResolved(evt.GetExtraLong());
1093 void CamuleApp::OnSourceDnsDone(CMuleInternalEvent& evt)
1095 downloadqueue->OnHostnameResolved(evt.GetExtraLong());
1099 void CamuleApp::OnServerDnsDone(CMuleInternalEvent& evt)
1101 AddLogLineMS(false, _("Server hostname notified"));
1102 serverconnect->OnServerHostnameResolved(evt.GetClientData(), evt.GetExtraLong());
1106 void CamuleApp::OnTCPTimer(CTimerEvent& WXUNUSED(evt))
1108 if(!IsRunning()) {
1109 return;
1111 serverconnect->StopConnectionTry();
1112 if (IsConnectedED2K() ) {
1113 return;
1115 serverconnect->ConnectToAnyServer();
1119 void CamuleApp::OnCoreTimer(CTimerEvent& WXUNUSED(evt))
1121 // Former TimerProc section
1122 static uint64 msPrev1, msPrev5, msPrevSave, msPrevHist, msPrevOS, msPrevKnownMet;
1123 uint64 msCur = theStats::GetUptimeMillis();
1124 TheTime = msCur / 1000;
1126 if (!IsRunning()) {
1127 return;
1130 #ifndef AMULE_DAEMON
1131 // Check if we should terminate the app
1132 if ( g_shutdownSignal ) {
1133 wxWindow* top = GetTopWindow();
1135 if ( top ) {
1136 top->Close(true);
1137 } else {
1138 // No top-window, have to force termination.
1139 wxExit();
1142 #endif
1144 uploadqueue->Process();
1145 downloadqueue->Process();
1146 //theApp->clientcredits->Process();
1147 theStats::CalculateRates();
1149 if (msCur-msPrevHist > 1000) {
1150 // unlike the other loop counters in this function this one will sometimes
1151 // produce two calls in quick succession (if there was a gap of more than one
1152 // second between calls to TimerProc) - this is intentional! This way the
1153 // history list keeps an average of one node per second and gets thinned out
1154 // correctly as time progresses.
1155 msPrevHist += 1000;
1157 m_statistics->RecordHistory();
1162 if (msCur-msPrev1 > 1000) { // approximately every second
1163 msPrev1 = msCur;
1164 clientcredits->Process();
1165 clientlist->Process();
1167 // Publish files to server if needed.
1168 sharedfiles->Process();
1170 if( Kademlia::CKademlia::IsRunning() ) {
1171 Kademlia::CKademlia::Process();
1172 if(Kademlia::CKademlia::GetPrefs()->HasLostConnection()) {
1173 StopKad();
1174 clientudp->Close();
1175 clientudp->Open();
1176 if (thePrefs::Reconnect()) {
1177 StartKad();
1182 if( serverconnect->IsConnecting() && !serverconnect->IsSingleConnect() ) {
1183 serverconnect->TryAnotherConnectionrequest();
1185 if (serverconnect->IsConnecting()) {
1186 serverconnect->CheckForTimeout();
1188 listensocket->UpdateConnectionsStatus();
1193 if (msCur-msPrev5 > 5000) { // every 5 seconds
1194 msPrev5 = msCur;
1195 listensocket->Process();
1198 if (msCur-msPrevSave >= 60000) {
1199 msPrevSave = msCur;
1200 wxString buffer;
1202 // Save total upload/download to preferences
1203 wxConfigBase* cfg = wxConfigBase::Get();
1204 buffer = CFormat(wxT("%llu")) % (theStats::GetSessionReceivedBytes() + thePrefs::GetTotalDownloaded());
1205 cfg->Write(wxT("/Statistics/TotalDownloadedBytes"), buffer);
1207 buffer = CFormat(wxT("%llu")) % (theStats::GetSessionSentBytes() + thePrefs::GetTotalUploaded());
1208 cfg->Write(wxT("/Statistics/TotalUploadedBytes"), buffer);
1210 // Write changes to file
1211 cfg->Flush();
1215 // Special
1216 if (msCur - msPrevOS >= thePrefs::GetOSUpdate() * 1000ull) {
1217 OnlineSig(); // Added By Bouc7
1218 msPrevOS = msCur;
1221 if (msCur - msPrevKnownMet >= 30*60*1000/*There must be a prefs option for this*/) {
1222 // Save Shared Files data
1223 knownfiles->Save();
1224 msPrevKnownMet = msCur;
1228 // Recomended by lugdunummaster himself - from emule 0.30c
1229 serverconnect->KeepConnectionAlive();
1234 void CamuleApp::OnFinishedHashing(CHashingEvent& evt)
1236 wxCHECK_RET(evt.GetResult(), wxT("No result of hashing"));
1238 CKnownFile* owner = const_cast<CKnownFile*>(evt.GetOwner());
1239 CKnownFile* result = evt.GetResult();
1241 if (owner) {
1242 // Check if the partfile still exists, as it might have
1243 // been deleted in the mean time.
1244 if (downloadqueue->IsPartFile(owner)) {
1245 // This cast must not be done before the IsPartFile
1246 // call, as dynamic_cast will barf on dangling pointers.
1247 dynamic_cast<CPartFile*>(owner)->PartFileHashFinished(result);
1249 } else {
1250 static int filecount;
1251 static uint64 bytecount;
1253 if (knownfiles->SafeAddKFile(result)) {
1254 AddDebugLogLineM(false, logKnownFiles,
1255 CFormat(wxT("Safe adding file to sharedlist: %s")) % result->GetFileName());
1256 sharedfiles->SafeAddKFile(result);
1258 filecount++;
1259 bytecount += result->GetFileSize();
1260 // If we have added 30 files or files with a total size of ~300mb
1261 if ( ( filecount == 30 ) || ( bytecount >= 314572800 ) ) {
1262 AddDebugLogLineM(false, logKnownFiles, wxT("Failsafe for crash on file hashing creation"));
1263 if ( m_app_state != APP_STATE_SHUTTINGDOWN ) {
1264 knownfiles->Save();
1265 filecount = 0;
1266 bytecount = 0;
1269 } else {
1270 AddDebugLogLineM(false, logKnownFiles,
1271 CFormat(wxT("File not added to sharedlist: %s")) % result->GetFileName());
1272 delete result;
1278 void CamuleApp::OnFinishedAICHHashing(CHashingEvent& evt)
1280 wxCHECK_RET(evt.GetResult(), wxT("No result of AICH-hashing"));
1282 CKnownFile* owner = const_cast<CKnownFile*>(evt.GetOwner());
1283 CScopedPtr<CKnownFile> result(evt.GetResult());
1285 // Check that the owner is still valid
1286 if (knownfiles->IsKnownFile(owner)) {
1287 if (result->GetAICHHashset()->GetStatus() == AICH_HASHSETCOMPLETE) {
1288 CAICHHashSet* oldSet = owner->GetAICHHashset();
1289 CAICHHashSet* newSet = result->GetAICHHashset();
1291 owner->SetAICHHashset(newSet);
1292 newSet->SetOwner(owner);
1294 result->SetAICHHashset(oldSet);
1295 oldSet->SetOwner(result.get());
1301 void CamuleApp::OnFinishedCompletion(CCompletionEvent& evt)
1303 CPartFile* completed = const_cast<CPartFile*>(evt.GetOwner());
1304 wxCHECK_RET(completed, wxT("Completion event sent for unspecified file"));
1305 wxASSERT_MSG(downloadqueue->IsPartFile(completed), wxT("CCompletionEvent for unknown partfile."));
1307 completed->CompleteFileEnded(evt.ErrorOccured(), evt.GetFullPath());
1308 if (evt.ErrorOccured()) {
1309 CUserEvents::ProcessEvent(CUserEvents::ErrorOnCompletion, completed);
1312 // Check if we should execute an script/app/whatever.
1313 CUserEvents::ProcessEvent(CUserEvents::DownloadCompleted, completed);
1316 void CamuleApp::OnFinishedAllocation(CAllocFinishedEvent& evt)
1318 CPartFile *file = evt.GetFile();
1319 wxCHECK_RET(file, wxT("Allocation finished event sent for unspecified file"));
1320 wxASSERT_MSG(downloadqueue->IsPartFile(file), wxT("CAllocFinishedEvent for unknown partfile"));
1322 file->SetPartFileStatus(PS_EMPTY);
1324 if (evt.Succeeded()) {
1325 if (evt.IsPaused()) {
1326 file->StopFile();
1327 } else {
1328 file->ResumeFile();
1330 } else {
1331 AddLogLineM(false, CFormat(_("Disk space preallocation for file '%s' failed: %s")) % file->GetFileName() % wxString(UTF82unicode(std::strerror(evt.GetResult()))));
1332 file->StopFile();
1335 file->AllocationFinished();
1338 void CamuleApp::OnNotifyEvent(CMuleGUIEvent& evt)
1340 #if defined(AMULE_DAEMON)
1341 evt.Notify();
1342 #else
1343 if (theApp->amuledlg) {
1344 evt.Notify();
1346 #endif
1350 void CamuleApp::ShutDown()
1352 // Log
1353 AddDebugLogLineM(false, logGeneral, wxT("CamuleApp::ShutDown() has started."));
1355 // Signal the hashing thread to terminate
1356 m_app_state = APP_STATE_SHUTTINGDOWN;
1358 StopKad();
1360 // Kry - Save the sources seeds on app exit
1361 if (thePrefs::GetSrcSeedsOn()) {
1362 downloadqueue->SaveSourceSeeds();
1365 OnlineSig(true); // Added By Bouc7
1367 // Exit thread scheduler and upload thread
1368 CThreadScheduler::Terminate();
1370 AddDebugLogLineN(logGeneral, wxT("Terminate upload thread."));
1371 uploadBandwidthThrottler->EndThread();
1373 // Close sockets to avoid new clients coming in
1374 if (listensocket) {
1375 listensocket->Close();
1376 listensocket->KillAllSockets();
1379 if (serverconnect) {
1380 serverconnect->Disconnect();
1383 ECServerHandler->KillAllSockets();
1385 #ifdef ENABLE_UPNP
1386 if (thePrefs::GetUPnPEnabled()) {
1387 if (m_upnp) {
1388 m_upnp->DeletePortMappings(m_upnpMappings);
1391 #endif
1393 // saving data & stuff
1394 if (knownfiles) {
1395 knownfiles->Save();
1398 thePrefs::Add2TotalDownloaded(theStats::GetSessionReceivedBytes());
1399 thePrefs::Add2TotalUploaded(theStats::GetSessionSentBytes());
1401 if (glob_prefs) {
1402 glob_prefs->Save();
1405 CPath configFileName = CPath(ConfigDir + m_configFile);
1406 CPath::BackupFile(configFileName, wxT(".bak"));
1408 if (clientlist) {
1409 clientlist->DeleteAll();
1412 // Log
1413 AddDebugLogLineM(false, logGeneral, wxT("CamuleApp::ShutDown() has ended."));
1417 bool CamuleApp::AddServer(CServer *srv, bool fromUser)
1419 if ( serverlist->AddServer(srv, fromUser) ) {
1420 Notify_ServerAdd(srv);
1421 return true;
1423 return false;
1427 uint32 CamuleApp::GetPublicIP(bool ignorelocal) const
1429 if (m_dwPublicIP == 0) {
1430 if (Kademlia::CKademlia::IsConnected() && Kademlia::CKademlia::GetIPAddress() ) {
1431 return wxUINT32_SWAP_ALWAYS(Kademlia::CKademlia::GetIPAddress());
1432 } else {
1433 return ignorelocal ? 0 : m_localip;
1437 return m_dwPublicIP;
1441 void CamuleApp::SetPublicIP(const uint32 dwIP)
1443 wxASSERT((dwIP == 0) || !IsLowID(dwIP));
1445 if (dwIP != 0 && dwIP != m_dwPublicIP && serverlist != NULL) {
1446 m_dwPublicIP = dwIP;
1447 serverlist->CheckForExpiredUDPKeys();
1448 } else {
1449 m_dwPublicIP = dwIP;
1454 wxString CamuleApp::GetLog(bool reset)
1456 wxFile logfile;
1457 logfile.Open(ConfigDir + wxT("logfile"));
1458 if ( !logfile.IsOpened() ) {
1459 return _("ERROR: can't open logfile");
1461 int len = logfile.Length();
1462 if ( len == 0 ) {
1463 return _("WARNING: logfile is empty. Something is wrong.");
1465 char *tmp_buffer = new char[len + sizeof(wxChar)];
1466 logfile.Read(tmp_buffer, len);
1467 memset(tmp_buffer + len, 0, sizeof(wxChar));
1469 // try to guess file format
1470 wxString str;
1471 if (tmp_buffer[0] && tmp_buffer[1]) {
1472 str = wxString(UTF82unicode(tmp_buffer));
1473 } else {
1474 str = wxWCharBuffer((wchar_t *)tmp_buffer);
1477 delete [] tmp_buffer;
1478 if ( reset ) {
1479 theLogger.CloseLogfile();
1480 if (theLogger.OpenLogfile(ConfigDir + wxT("logfile"))) {
1481 AddLogLineM(false, _("Log has been reset"));
1484 return str;
1488 wxString CamuleApp::GetServerLog(bool reset)
1490 wxString ret = server_msg;
1491 if ( reset ) {
1492 server_msg.Clear();
1494 return ret;
1497 wxString CamuleApp::GetDebugLog(bool reset)
1499 return GetLog(reset);
1503 void CamuleApp::AddServerMessageLine(wxString &msg)
1505 server_msg += msg + wxT("\n");
1506 AddLogLineM(false, CFormat(_("ServerMessage: %s")) % msg);
1511 void CamuleApp::OnFinishedHTTPDownload(CMuleInternalEvent& event)
1513 switch (event.GetInt()) {
1514 case HTTP_IPFilter:
1515 ipfilter->DownloadFinished(event.GetExtraLong());
1516 break;
1517 case HTTP_ServerMet:
1518 serverlist->DownloadFinished(event.GetExtraLong());
1519 break;
1520 case HTTP_ServerMetAuto:
1521 serverlist->AutoDownloadFinished(event.GetExtraLong());
1522 break;
1523 case HTTP_VersionCheck:
1524 CheckNewVersion(event.GetExtraLong());
1525 break;
1526 case HTTP_NodesDat:
1527 if (event.GetExtraLong() != -1) {
1529 wxString file = ConfigDir + wxT("nodes.dat");
1530 if (wxFileExists(file)) {
1531 wxRemoveFile(file);
1534 if ( Kademlia::CKademlia::IsRunning() ) {
1535 Kademlia::CKademlia::Stop();
1538 wxRenameFile(file + wxT(".download"),file);
1540 Kademlia::CKademlia::Start();
1541 theApp->ShowConnectionState();
1543 } else {
1544 AddLogLineM(true, _("Failed to download the nodes list."));
1546 break;
1547 #ifdef ENABLE_IP2COUNTRY
1548 case HTTP_GeoIP:
1549 theApp->amuledlg->IP2CountryDownloadFinished(event.GetExtraLong());
1550 // If we updated, the dialog is already up. Redraw it to show the flags.
1551 theApp->amuledlg->Refresh();
1552 break;
1553 #endif
1557 void CamuleApp::CheckNewVersion(uint32 result)
1559 if (result == 1) {
1560 wxString filename = ConfigDir + wxT("last_version_check");
1561 wxTextFile file;
1563 if (!file.Open(filename)) {
1564 AddLogLineM(true, _("Failed to open the downloaded version check file") );
1565 return;
1566 } else if (!file.GetLineCount()) {
1567 AddLogLineM(true, _("Corrupted version check file"));
1568 } else {
1569 wxString versionLine = file.GetFirstLine();
1570 wxStringTokenizer tkz(versionLine, wxT("."));
1572 AddDebugLogLineM(false, logGeneral, wxString(wxT("Running: ")) + wxT(VERSION) + wxT(", Version check: ") + versionLine);
1574 long fields[] = {0, 0, 0};
1575 for (int i = 0; i < 3; ++i) {
1576 if (!tkz.HasMoreTokens()) {
1577 AddLogLineM(true, _("Corrupted version check file"));
1578 return;
1579 } else {
1580 wxString token = tkz.GetNextToken();
1582 if (!token.ToLong(&fields[i])) {
1583 AddLogLineM(true, _("Corrupted version check file"));
1584 return;
1589 long curVer = make_full_ed2k_version(VERSION_MJR, VERSION_MIN, VERSION_UPDATE);
1590 long newVer = make_full_ed2k_version(fields[0], fields[1], fields[2]);
1592 if (curVer < newVer) {
1593 AddLogLineM(true, _("You are using an outdated version of aMule!"));
1594 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]));
1595 AddLogLineM(false, _("The latest version can always be found at http://www.amule.org"));
1596 #ifdef AMULE_DAEMON
1597 AddLogLineMS(true, CFormat(_("WARNING: Your aMuled version is outdated: %i.%i.%i < %li.%li.%li"))
1598 % VERSION_MJR % VERSION_MIN % VERSION_UPDATE % fields[0] % fields[1] % fields[2]);
1599 #endif
1600 } else {
1601 AddLogLineM(false, _("Your copy of aMule is up to date."));
1605 file.Close();
1606 wxRemoveFile(filename);
1607 } else {
1608 AddLogLineM(true, _("Failed to download the version check file") );
1614 bool CamuleApp::IsConnected() const
1616 return (IsConnectedED2K() || IsConnectedKad());
1620 bool CamuleApp::IsConnectedED2K() const
1622 return serverconnect && serverconnect->IsConnected();
1626 bool CamuleApp::IsConnectedKad() const
1628 return Kademlia::CKademlia::IsConnected();
1632 bool CamuleApp::IsFirewalled() const
1634 if (theApp->IsConnectedED2K() && !theApp->serverconnect->IsLowID()) {
1635 return false; // we have an eD2K HighID -> not firewalled
1638 return IsFirewalledKad(); // If kad says ok, it's ok.
1641 bool CamuleApp::IsFirewalledKad() const
1643 return !Kademlia::CKademlia::IsConnected() // not connected counts as firewalled
1644 || Kademlia::CKademlia::IsFirewalled();
1647 bool CamuleApp::IsFirewalledKadUDP() const
1649 return !Kademlia::CKademlia::IsConnected() // not connected counts as firewalled
1650 || Kademlia::CUDPFirewallTester::IsFirewalledUDP(true);
1653 bool CamuleApp::IsKadRunning() const
1655 return Kademlia::CKademlia::IsRunning();
1658 // Kad stats
1659 uint32 CamuleApp::GetKadUsers() const
1661 return Kademlia::CKademlia::GetKademliaUsers();
1664 uint32 CamuleApp::GetKadFiles() const
1666 return Kademlia::CKademlia::GetKademliaFiles();
1669 uint32 CamuleApp::GetKadIndexedSources() const
1671 return Kademlia::CKademlia::GetIndexed()->m_totalIndexSource;
1674 uint32 CamuleApp::GetKadIndexedKeywords() const
1676 return Kademlia::CKademlia::GetIndexed()->m_totalIndexKeyword;
1679 uint32 CamuleApp::GetKadIndexedNotes() const
1681 return Kademlia::CKademlia::GetIndexed()->m_totalIndexNotes;
1684 uint32 CamuleApp::GetKadIndexedLoad() const
1686 return Kademlia::CKademlia::GetIndexed()->m_totalIndexLoad;
1690 // True IP of machine
1691 uint32 CamuleApp::GetKadIPAdress() const
1693 return wxUINT32_SWAP_ALWAYS(Kademlia::CKademlia::GetPrefs()->GetIPAddress());
1696 // Buddy status
1697 uint8 CamuleApp::GetBuddyStatus() const
1699 return clientlist->GetBuddyStatus();
1702 uint32 CamuleApp::GetBuddyIP() const
1704 return clientlist->GetBuddy()->GetIP();
1707 uint32 CamuleApp::GetBuddyPort() const
1709 return clientlist->GetBuddy()->GetUDPPort();
1712 bool CamuleApp::CanDoCallback(CUpDownClient *client)
1714 if (Kademlia::CKademlia::IsConnected()) {
1715 if (IsConnectedED2K()) {
1716 if (serverconnect->IsLowID()) {
1717 if (Kademlia::CKademlia::IsFirewalled()) {
1718 //Both Connected - Both Firewalled
1719 return false;
1720 } else {
1721 if (client->GetServerIP() == theApp->serverconnect->GetCurrentServer()->GetIP() &&
1722 client->GetServerPort() == theApp->serverconnect->GetCurrentServer()->GetPort()) {
1723 // Both Connected - Server lowID, Kad Open - Client on same server
1724 // We prevent a callback to the server as this breaks the protocol
1725 // and will get you banned.
1726 return false;
1727 } else {
1728 // Both Connected - Server lowID, Kad Open - Client on remote server
1729 return true;
1732 } else {
1733 //Both Connected - Server HighID, Kad don't care
1734 return true;
1736 } else {
1737 if (Kademlia::CKademlia::IsFirewalled()) {
1738 //Only Kad Connected - Kad Firewalled
1739 return false;
1740 } else {
1741 //Only Kad Conected - Kad Open
1742 return true;
1745 } else {
1746 if (IsConnectedED2K()) {
1747 if (serverconnect->IsLowID()) {
1748 //Only Server Connected - Server LowID
1749 return false;
1750 } else {
1751 //Only Server Connected - Server HighID
1752 return true;
1754 } else {
1755 //We are not connected at all!
1756 return false;
1761 void CamuleApp::ShowUserCount() {
1762 uint32 totaluser = 0, totalfile = 0;
1764 theApp->serverlist->GetUserFileStatus( totaluser, totalfile );
1766 wxString buffer;
1768 static const wxString s_singlenetstatusformat = _("Users: %s | Files: %s");
1769 static const wxString s_bothnetstatusformat = _("Users: E: %s K: %s | Files: E: %s K: %s");
1771 if (thePrefs::GetNetworkED2K() && thePrefs::GetNetworkKademlia()) {
1772 buffer = CFormat(s_bothnetstatusformat) % CastItoIShort(totaluser) % CastItoIShort(Kademlia::CKademlia::GetKademliaUsers()) % CastItoIShort(totalfile) % CastItoIShort(Kademlia::CKademlia::GetKademliaFiles());
1773 } else if (thePrefs::GetNetworkED2K()) {
1774 buffer = CFormat(s_singlenetstatusformat) % CastItoIShort(totaluser) % CastItoIShort(totalfile);
1775 } else if (thePrefs::GetNetworkKademlia()) {
1776 buffer = CFormat(s_singlenetstatusformat) % CastItoIShort(Kademlia::CKademlia::GetKademliaUsers()) % CastItoIShort(Kademlia::CKademlia::GetKademliaFiles());
1777 } else {
1778 buffer = _("No networks selected");
1781 Notify_ShowUserCount(buffer);
1785 void CamuleApp::ListenSocketHandler(wxSocketEvent& event)
1787 wxCHECK_RET(listensocket, wxT("Connection-event for NULL'd listen-socket"));
1788 wxCHECK_RET(event.GetSocketEvent() == wxSOCKET_CONNECTION,
1789 wxT("Invalid event received for listen-socket"));
1791 if (m_app_state == APP_STATE_RUNNING) {
1792 listensocket->OnAccept(0);
1793 } else if (m_app_state == APP_STATE_STARTING) {
1794 // When starting up, connection may be made before we are able
1795 // to handle them. However, if these are ignored, no futher
1796 // connection-events will be triggered, so we have to accept it.
1797 wxSocketBase* socket = listensocket->Accept(false);
1799 wxCHECK_RET(socket, wxT("NULL returned by Accept() during startup"));
1801 socket->Destroy();
1806 void CamuleApp::ShowConnectionState()
1808 static uint8 old_state = (1<<7); // This flag doesn't exist
1810 uint8 state = 0;
1812 if (theApp->serverconnect->IsConnected()) {
1813 state |= CONNECTED_ED2K;
1816 if (Kademlia::CKademlia::IsRunning()) {
1817 if (Kademlia::CKademlia::IsConnected()) {
1818 if (!Kademlia::CKademlia::IsFirewalled()) {
1819 state |= CONNECTED_KAD_OK;
1820 } else {
1821 state |= CONNECTED_KAD_FIREWALLED;
1823 } else {
1824 state |= CONNECTED_KAD_NOT;
1828 Notify_ShowConnState(state);
1830 if (old_state != state) {
1831 // Get the changed value
1832 int changed_flags = old_state ^ state;
1834 if (changed_flags & CONNECTED_ED2K) {
1835 // ED2K status changed
1836 wxString connected_server;
1837 CServer* ed2k_server = theApp->serverconnect->GetCurrentServer();
1838 if (ed2k_server) {
1839 connected_server = ed2k_server->GetListName();
1841 if (state & CONNECTED_ED2K) {
1842 // We connected to some server
1843 const wxString id = theApp->serverconnect->IsLowID() ? _("with LowID") : _("with HighID");
1845 AddLogLineM(true, CFormat(_("Connected to %s %s")) % connected_server % id);
1846 } else {
1847 if ( theApp->serverconnect->IsConnecting() ) {
1848 AddLogLineM(true, CFormat(_("Connecting to %s")) % connected_server);
1849 } else {
1850 AddLogLineM(true, _("Disconnected from eD2k"));
1855 if (changed_flags & CONNECTED_KAD_NOT) {
1856 if (state & CONNECTED_KAD_NOT) {
1857 AddLogLineM(true, _("Kad started."));
1858 } else {
1859 AddLogLineM(true, _("Kad stopped."));
1863 if (changed_flags & (CONNECTED_KAD_OK | CONNECTED_KAD_FIREWALLED)) {
1864 if (state & (CONNECTED_KAD_OK | CONNECTED_KAD_FIREWALLED)) {
1865 if (state & CONNECTED_KAD_OK) {
1866 AddLogLineM(true, _("Connected to Kad (ok)"));
1867 } else {
1868 AddLogLineM(true, _("Connected to Kad (firewalled)"));
1870 } else {
1871 AddLogLineM(true, _("Disconnected from Kad"));
1875 old_state = state;
1877 theApp->downloadqueue->OnConnectionState(IsConnected());
1880 ShowUserCount();
1881 Notify_ShowConnState(state);
1885 void CamuleApp::UDPSocketHandler(wxSocketEvent& event)
1887 CMuleUDPSocket* socket = (CMuleUDPSocket*)(event.GetClientData());
1888 wxCHECK_RET(socket, wxT("No socket owner specified."));
1890 if (IsOnShutDown() || thePrefs::IsUDPDisabled()) return;
1892 if (!IsRunning()) {
1893 if (event.GetSocketEvent() == wxSOCKET_INPUT) {
1894 // Back to the queue!
1895 theApp->AddPendingEvent(event);
1896 return;
1900 switch (event.GetSocketEvent()) {
1901 case wxSOCKET_INPUT:
1902 socket->OnReceive(0);
1903 break;
1905 case wxSOCKET_OUTPUT:
1906 socket->OnSend(0);
1907 break;
1909 case wxSOCKET_LOST:
1910 socket->OnDisconnected(0);
1911 break;
1913 default:
1914 wxFAIL;
1915 break;
1920 void CamuleApp::OnUnhandledException()
1922 // Call the generic exception-handler.
1923 fprintf(stderr, "\taMule Version: %s\n", (const char*)unicode2char(GetFullMuleVersion()));
1924 ::OnUnhandledException();
1927 void CamuleApp::StartKad()
1929 if (!Kademlia::CKademlia::IsRunning() && thePrefs::GetNetworkKademlia()) {
1930 // Kad makes no sense without the Client-UDP socket.
1931 if (!thePrefs::IsUDPDisabled()) {
1932 Kademlia::CKademlia::Start();
1933 } else {
1934 AddLogLineM(true,_("Kad network cannot be used if UDP port is disabled on preferences, not starting."));
1936 } else if (!thePrefs::GetNetworkKademlia()) {
1937 AddLogLineM(true,_("Kad network disabled on preferences, not connecting."));
1941 void CamuleApp::StopKad()
1943 // Stop Kad if it's running
1944 if (Kademlia::CKademlia::IsRunning()) {
1945 Kademlia::CKademlia::Stop();
1950 void CamuleApp::BootstrapKad(uint32 ip, uint16 port)
1952 if (!Kademlia::CKademlia::IsRunning()) {
1953 Kademlia::CKademlia::Start();
1954 theApp->ShowConnectionState();
1957 Kademlia::CKademlia::Bootstrap(ip, port, true);
1961 void CamuleApp::UpdateNotesDat(const wxString& url)
1963 wxString strTempFilename(theApp->ConfigDir + wxT("nodes.dat.download"));
1965 CHTTPDownloadThread *downloader = new CHTTPDownloadThread(url, strTempFilename, HTTP_NodesDat);
1966 downloader->Create();
1967 downloader->Run();
1971 void CamuleApp::DisconnectED2K()
1973 // Stop ED2K if it's running
1974 if (IsConnectedED2K()) {
1975 serverconnect->Disconnect();
1979 bool CamuleApp::CryptoAvailable() const
1981 return clientcredits && clientcredits->CryptoAvailable();
1984 uint32 CamuleApp::GetED2KID() const {
1985 return serverconnect ? serverconnect->GetClientID() : 0;
1988 uint32 CamuleApp::GetID() const {
1989 uint32 ID;
1991 if( Kademlia::CKademlia::IsConnected() && !Kademlia::CKademlia::IsFirewalled() ) {
1992 // We trust Kad above ED2K
1993 ID = ENDIAN_NTOHL(Kademlia::CKademlia::GetIPAddress());
1994 } else if( theApp->serverconnect->IsConnected() ) {
1995 ID = theApp->serverconnect->GetClientID();
1996 } else if ( Kademlia::CKademlia::IsConnected() && Kademlia::CKademlia::IsFirewalled() ) {
1997 // A firewalled Kad client get's a "1"
1998 ID = 1;
1999 } else {
2000 ID = 0;
2003 return ID;
2006 DEFINE_LOCAL_EVENT_TYPE(wxEVT_CORE_FINISHED_HTTP_DOWNLOAD)
2007 DEFINE_LOCAL_EVENT_TYPE(wxEVT_CORE_SOURCE_DNS_DONE)
2008 DEFINE_LOCAL_EVENT_TYPE(wxEVT_CORE_UDP_DNS_DONE)
2009 DEFINE_LOCAL_EVENT_TYPE(wxEVT_CORE_SERVER_DNS_DONE)
2010 // File_checked_for_headers