Upstream tarball 10013
[amule.git] / src / amuleAppCommon.cpp
blobd8d542f11bead04b41dbb102d13b969d1f3a566b
1 //
2 // This file is part of the aMule Project.
3 //
4 // Copyright (c) 2003-2009 aMule Team ( admin@amule.org / http://www.amule.org )
5 //
6 // Any parts of this program derived from the xMule, lMule or eMule project,
7 // or contributed by third-party developers are copyrighted by their
8 // respective authors.
9 //
10 // This program is free software; you can redistribute it and/or modify
11 // it under the terms of the GNU General Public License as published by
12 // the Free Software Foundation; either version 2 of the License, or
13 // (at your option) any later version.
15 // This program is distributed in the hope that it will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU General Public License for more details.
19 //
20 // You should have received a copy of the GNU General Public License
21 // along with this program; if not, write to the Free Software
22 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 // This file is for functions common to all three apps (amule, amuled, amulegui),
27 // but preprocessor-dependent (using theApp, thePrefs), so it is compiled seperately for each app.
31 #include <wx/wx.h>
32 #include <wx/cmdline.h> // Needed for wxCmdLineParser
33 #include <wx/snglinst.h> // Needed for wxSingleInstanceChecker
34 #include <wx/textfile.h> // Needed for wxTextFile
35 #include <wx/config.h> // Do_not_auto_remove (win32)
36 #include <wx/fileconf.h>
38 #include "amule.h" // Interface declarations.
39 #include <common/Format.h> // Needed for CFormat
40 #include "CFile.h" // Needed for CFile
41 #include "ED2KLink.h" // Needed for command line passing of links
42 #include "FileLock.h" // Needed for CFileLock
43 #include "GuiEvents.h" // Needed for Notify_*
44 #include "KnownFile.h"
45 #include "Logger.h"
46 #include "MagnetURI.h" // Needed for CMagnetURI
47 #include "Preferences.h"
48 #include "ScopedPtr.h"
50 #ifndef CLIENT_GUI
51 #include "DownloadQueue.h"
52 #endif
54 CamuleAppCommon::CamuleAppCommon()
56 m_singleInstance = NULL;
57 ec_config = false;
58 m_geometryEnabled = false;
59 if (IsRemoteGui()) {
60 m_appName = wxT("aMuleGUI");
61 m_configFile = wxT("remote.conf");
62 m_logFile = wxT("remotelogfile");
63 } else {
64 m_configFile = wxT("amule.conf");
65 m_logFile = wxT("logfile");
67 if (IsDaemon()) {
68 m_appName = wxT("aMuleD");
69 } else {
70 m_appName = wxT("aMule");
75 CamuleAppCommon::~CamuleAppCommon()
77 #if defined(__WXMAC__) && defined(AMULE_DAEMON)
78 //#warning TODO: fix wxSingleInstanceChecker for amuled on Mac (wx link problems)
79 #else
80 delete m_singleInstance;
81 #endif
84 void CamuleAppCommon::RefreshSingleInstanceChecker()
86 #if defined(__WXMAC__) && defined(AMULE_DAEMON)
87 //#warning TODO: fix wxSingleInstanceChecker for amuled on Mac (wx link problems)
88 #else
89 delete m_singleInstance;
90 m_singleInstance = new wxSingleInstanceChecker(wxT("muleLock"), ConfigDir);
91 #endif
94 void CamuleAppCommon::AddLinksFromFile()
96 const wxString fullPath = ConfigDir + wxT("ED2KLinks");
97 if (!wxFile::Exists(fullPath)) {
98 return;
101 // Attempt to lock the ED2KLinks file.
102 CFileLock lock((const char*)unicode2char(fullPath));
104 wxTextFile file(fullPath);
105 if ( file.Open() ) {
106 for ( unsigned int i = 0; i < file.GetLineCount(); i++ ) {
107 wxString line = file.GetLine( i ).Strip( wxString::both );
109 if ( !line.IsEmpty() ) {
110 // Special case! used by a secondary running mule to raise this one.
111 if (line == wxT("RAISE_DIALOG")) {
112 Notify_ShowGUI();
113 continue;
115 unsigned long category = 0;
116 if (line.AfterLast(wxT(':')).ToULong(&category) == true) {
117 line = line.BeforeLast(wxT(':'));
118 } else { // If ToULong returns false the category still can have been changed!
119 // This is fixed in wx 2.9
120 category = 0;
122 theApp->downloadqueue->AddLink(line, category);
126 file.Close();
127 } else {
128 AddLogLineNS(_("Failed to open ED2KLinks file."));
131 // Delete the file.
132 wxRemoveFile(theApp->ConfigDir + wxT("ED2KLinks"));
136 // Returns a magnet ed2k URI
137 wxString CamuleAppCommon::CreateMagnetLink(const CAbstractFile *f)
139 CMagnetURI uri;
141 uri.AddField(wxT("dn"), f->GetFileName().Cleanup(false).GetPrintable());
142 uri.AddField(wxT("xt"), wxString(wxT("urn:ed2k:")) + f->GetFileHash().Encode().Lower());
143 uri.AddField(wxT("xt"), wxString(wxT("urn:ed2khash:")) + f->GetFileHash().Encode().Lower());
144 uri.AddField(wxT("xl"), CFormat(wxT("%d")) % f->GetFileSize());
146 return uri.GetLink();
149 // Returns a ed2k file URL
150 wxString CamuleAppCommon::CreateED2kLink(const CAbstractFile *f, bool add_source, bool use_hostname, bool addcryptoptions)
152 wxASSERT(!(!add_source && (use_hostname || addcryptoptions)));
153 // Construct URL like this: ed2k://|file|<filename>|<size>|<hash>|/
154 wxString strURL = CFormat(wxT("ed2k://|file|%s|%i|%s|/"))
155 % f->GetFileName().Cleanup(false)
156 % f->GetFileSize() % f->GetFileHash().Encode();
158 if (add_source && theApp->IsConnected() && !theApp->IsFirewalled()) {
159 // Create the first part of the URL
160 strURL << wxT("|sources,");
161 if (use_hostname) {
162 strURL << thePrefs::GetYourHostname();
163 } else {
164 uint32 clientID = theApp->GetID();
165 strURL << (uint8) clientID << wxT(".") <<
166 (uint8)(clientID >> 8) << wxT(".") <<
167 (uint8)(clientID >> 16) << wxT(".") <<
168 (uint8)(clientID >> 24);
171 strURL << wxT(":") <<
172 thePrefs::GetPort();
174 if (addcryptoptions) {
175 const uint8 uSupportsCryptLayer = thePrefs::IsClientCryptLayerSupported() ? 1 : 0;
176 const uint8 uRequestsCryptLayer = thePrefs::IsClientCryptLayerRequested() ? 1 : 0;
177 const uint8 uRequiresCryptLayer = thePrefs::IsClientCryptLayerRequired() ? 1 : 0;
178 const uint8 byCryptOptions = (uRequiresCryptLayer << 2) | (uRequestsCryptLayer << 1) | (uSupportsCryptLayer << 0) | (uSupportsCryptLayer ? 0x80 : 0x00);
180 strURL << wxT(":") << byCryptOptions;
182 if (byCryptOptions & 0x80) {
183 strURL << wxT(":") << thePrefs::GetUserHash().Encode();
186 strURL << wxT("|/");
187 } else if (add_source) {
188 AddLogLineM(true, _("WARNING: You can't add yourself as a source for an eD2k link while having a lowid."));
191 // Result is "ed2k://|file|<filename>|<size>|<hash>|/|sources,[(<ip>|<hostname>):<port>[:cryptoptions[:hash]]]|/"
192 return strURL;
195 // Returns a ed2k link with AICH info if available
196 wxString CamuleAppCommon::CreateED2kAICHLink(const CKnownFile* f)
198 // Create the first part of the URL
199 wxString strURL = CreateED2kLink(f);
200 // Append the AICH info
201 if (f->HasProperAICHHashSet()) {
202 strURL.RemoveLast(); // remove trailing '/'
203 strURL << wxT("h=") << f->GetAICHMasterHash() << wxT("|/");
206 // Result is "ed2k://|file|<filename>|<size>|<hash>|h=<AICH master hash>|/"
207 return strURL;
210 bool CamuleAppCommon::InitCommon(int argc, wxChar ** argv)
212 theApp->SetAppName(wxT("aMule"));
213 wxString FullMuleVersion = GetFullMuleVersion();
214 wxString OSDescription = wxGetOsDescription();
215 strFullMuleVersion = strdup((const char *)unicode2char(FullMuleVersion));
216 strOSDescription = strdup((const char *)unicode2char(OSDescription));
217 OSType = OSDescription.BeforeFirst( wxT(' ') );
218 if ( OSType.IsEmpty() ) {
219 OSType = wxT("Unknown");
222 // Parse cmdline arguments.
223 wxCmdLineParser cmdline(argc, argv);
225 // Handle these arguments.
226 cmdline.AddSwitch(wxT("v"), wxT("version"), wxT("Displays the current version number."));
227 cmdline.AddSwitch(wxT("h"), wxT("help"), wxT("Displays this information."));
228 cmdline.AddOption(wxT("c"), wxT("config-dir"), wxT("read config from <dir> instead of home"));
229 #ifdef AMULE_DAEMON
230 cmdline.AddSwitch(wxT("f"), wxT("full-daemon"), wxT("Fork to background."));
231 cmdline.AddOption(wxT("p"), wxT("pid-file"), wxT("After fork, create a pid-file in the given fullname file."));
232 cmdline.AddSwitch(wxT("e"), wxT("ec-config"), wxT("Configure EC (External Connections)."));
233 #else
235 #ifdef __WXMSW__
236 // MSW shows help otions in a dialog box, and the formatting doesn't fit there
237 #define HELPTAB wxT("\t")
238 #else
239 #define HELPTAB wxT("\t\t\t")
240 #endif
242 cmdline.AddOption(wxT("geometry"), wxEmptyString,
243 wxT("Sets the geometry of the app.\n")
244 HELPTAB wxT("<str> uses the same format as standard X11 apps:\n")
245 HELPTAB wxT("[=][<width>{xX}<height>][{+-}<xoffset>{+-}<yoffset>]"));
246 #endif // !AMULE_DAEMON
248 cmdline.AddSwitch(wxT("o"), wxT("log-stdout"), wxT("Print log messages to stdout."));
249 cmdline.AddSwitch(wxT("r"), wxT("reset-config"), wxT("Resets config to default values."));
251 #ifdef CLIENT_GUI
252 cmdline.AddSwitch(wxT("s"), wxT("skip"), wxT("Skip connection dialog."));
253 #else
254 // Change webserver path. This is also a config option, so this switch will go at some time.
255 cmdline.AddOption(wxT("w"), wxT("use-amuleweb"), wxT("Specify location of amuleweb binary."));
256 #ifndef __WXMSW__
257 cmdline.AddSwitch(wxT("d"), wxT("disable-fatal"), wxT("Do not handle fatal exception."));
258 // Keep stdin open to run valgrind --gen_suppressions
259 cmdline.AddSwitch(wxT("i"), wxT("enable-stdin"), wxT("Do not disable stdin."));
260 #endif
261 #endif
263 // Allow passing of links to the app
264 cmdline.AddOption(wxT("t"), wxT("category"), wxT("Set category for passed ED2K links."), wxCMD_LINE_VAL_NUMBER);
265 cmdline.AddParam(wxT("ED2K link"), wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL | wxCMD_LINE_PARAM_MULTIPLE);
267 // wx asserts in debug mode if there is a check for an option that wasn't added.
268 // So we have to wrap around the same #ifdefs as above. >:(
270 // Show help on --help or invalid commands
271 if ( cmdline.Parse() ) {
272 return false;
273 } else if (cmdline.Found(wxT("help"))) {
274 cmdline.Usage();
275 return false;
278 if ( cmdline.Found(wxT("version"))) {
279 // This looks silly with logging macros that add a timestamp.
280 printf("%s\n", (const char*)unicode2char(wxString(CFormat(wxT("%s (OS: %s)")) % FullMuleVersion % OSType)));
281 return false;
284 if ( cmdline.Found(wxT("config-dir"), &ConfigDir) ) {
285 // Make an absolute path from the config dir
286 wxFileName fn(ConfigDir);
287 fn.MakeAbsolute();
288 ConfigDir = fn.GetFullPath();
289 if (ConfigDir.Last() != wxFileName::GetPathSeparator()) {
290 ConfigDir += wxFileName::GetPathSeparator();
292 } else {
293 ConfigDir = GetConfigDir();
296 #ifndef __WXMSW__
297 if ( !cmdline.Found(wxT("disable-fatal")) ) {
298 // catch fatal exceptions
299 wxHandleFatalExceptions(true);
301 #endif
303 theLogger.SetEnabledStdoutLog(cmdline.Found(wxT("log-stdout")));
304 #ifdef AMULE_DAEMON
305 enable_daemon_fork = cmdline.Found(wxT("full-daemon"));
306 if ( cmdline.Found(wxT("pid-file"), &m_PidFile) ) {
307 // Remove any existing PidFile
308 if ( wxFileExists (m_PidFile) ) wxRemoveFile (m_PidFile);
310 ec_config = cmdline.Found(wxT("ec-config"));
311 #else
312 enable_daemon_fork = false;
314 // Default geometry of the GUI. Can be changed with a cmdline argument...
315 if ( cmdline.Found(wxT("geometry"), &m_geometryString) ) {
316 m_geometryEnabled = true;
318 #endif
320 if (theLogger.IsEnabledStdoutLog()) {
321 if ( enable_daemon_fork ) {
322 AddLogLineMS(false, wxT("Daemon will fork to background - log to stdout disabled"));
323 theLogger.SetEnabledStdoutLog(false);
324 } else {
325 AddLogLineMS(false, wxT("Logging to stdout enabled"));
329 AddLogLineMS(false, wxT("Initialising ") + FullMuleVersion);
331 // Ensure that "~/.aMule/" is accessible.
332 CPath outDir;
333 if (!CheckMuleDirectory(wxT("configuration"), CPath(ConfigDir), wxEmptyString, outDir)) {
334 return false;
337 if (cmdline.Found(wxT("reset-config"))) {
338 // Make a backup first.
339 wxRemoveFile(ConfigDir + m_configFile + wxT(".backup"));
340 wxRenameFile(ConfigDir + m_configFile, ConfigDir + m_configFile + wxT(".backup"));
341 AddLogLineMS(false, CFormat(wxT("Your settings have been reset to default values.\nThe old config file has been saved as %s.backup\n")) % m_configFile);
344 size_t linksPassed = cmdline.GetParamCount(); // number of links from the command line
345 int linksActuallyPassed = 0; // number of links that pass the syntax check
346 if (linksPassed) {
347 long cat = 0;
348 if (!cmdline.Found(wxT("t"), &cat)) {
349 cat = 0;
352 wxTextFile ed2kFile(ConfigDir + wxT("ED2KLinks"));
353 if (!ed2kFile.Exists()) {
354 ed2kFile.Create();
356 if (ed2kFile.Open()) {
357 for (size_t i = 0; i < linksPassed; i++) {
358 wxString link;
359 if (CheckPassedLink(cmdline.GetParam(i), link, cat)) {
360 ed2kFile.AddLine(link);
361 linksActuallyPassed++;
364 ed2kFile.Write();
365 } else {
366 AddLogLineCS(wxT("Failed to open 'ED2KLinks', cannot add links."));
370 #if defined(__WXMAC__) && defined(AMULE_DAEMON)
371 //#warning TODO: fix wxSingleInstanceChecker for amuled on Mac (wx link problems)
372 AddLogLineCS(wxT("WARNING: The check for other instances is currently disabled in amuled.\n"
373 "Please make sure that no other instance of aMule is running or your files might be corrupted.\n"));
374 #else
375 AddLogLineNS(wxT("Checking if there is an instance already running..."));
377 m_singleInstance = new wxSingleInstanceChecker();
378 wxString lockfile = IsRemoteGui() ? wxT("muleLockRGUI") : wxT("muleLock");
379 if (m_singleInstance->Create(lockfile, ConfigDir)
380 && m_singleInstance->IsAnotherRunning()) {
381 AddLogLineCS(CFormat(wxT("There is an instance of %s already running")) % m_appName);
382 AddLogLineNS(CFormat(wxT("(lock file: %s%s)")) % ConfigDir % lockfile);
383 if (linksPassed) {
384 AddLogLineNS(CFormat(wxT("passed %d %s to it, finished")) % linksActuallyPassed
385 % (linksPassed == 1 ? wxT("link") : wxT("links")));
386 return false;
389 // This is very tricky. The most secure way to communicate is via ED2K links file
390 wxTextFile ed2kFile(ConfigDir + wxT("ED2KLinks"));
391 if (!ed2kFile.Exists()) {
392 ed2kFile.Create();
395 if (ed2kFile.Open()) {
396 ed2kFile.AddLine(wxT("RAISE_DIALOG"));
397 ed2kFile.Write();
399 AddLogLineNS(wxT("Raising current running instance."));
400 } else {
401 AddLogLineCS(wxT("Failed to open 'ED2KFile', cannot signal running instance."));
404 return false;
405 } else {
406 AddLogLineNS(wxT("No other instances are running."));
408 #endif
410 #ifndef __WXMSW__
411 // Close standard-input
412 if ( !cmdline.Found(wxT("enable-stdin")) ) {
413 // The full daemon will close all std file-descriptors by itself,
414 // so closing it here would lead to the closing on the first open
415 // file, which is the logfile opened below
416 if (!enable_daemon_fork) {
417 close(0);
420 #endif
422 // Create the CFG file we shall use and set the config object as the global cfg file
423 wxConfig::Set(new wxFileConfig( wxEmptyString, wxEmptyString, ConfigDir + m_configFile));
425 // Make a backup of the log file
426 CPath logfileName = CPath(ConfigDir + m_logFile);
427 if (logfileName.FileExists()) {
428 CPath::BackupFile(logfileName, wxT(".bak"));
431 // Open the log file
432 if (!theLogger.OpenLogfile(logfileName.GetRaw())) {
433 // use std err as last resolt to indicate problem
434 fputs("ERROR: unable to open log file\n", stderr);
435 // failure to open log is serious problem
436 return false;
439 // Load Preferences
440 CPreferences::BuildItemList(ConfigDir);
441 CPreferences::LoadAllItems( wxConfigBase::Get() );
443 #ifdef CLIENT_GUI
444 m_skipConnectionDialog = cmdline.Found(wxT("skip"));
445 #else
446 wxString amulewebPath;
447 if (cmdline.Found(wxT("use-amuleweb"), &amulewebPath)) {
448 thePrefs::SetWSPath(amulewebPath);
449 AddLogLineNS(CFormat(wxT("Using amuleweb in '%s'.")) % amulewebPath);
451 #endif
453 return true;
456 bool CamuleAppCommon::CheckPassedLink(const wxString &in, wxString &out, int cat)
458 wxString link(in);
460 if (link.compare(0, 7, wxT("magnet:")) == 0) {
461 link = CMagnetED2KConverter(link);
462 if (link.empty()) {
463 AddLogLineCS(CFormat(wxT("Cannot convert magnet link to eD2k: %s")) % in);
464 return false;
468 try {
469 CScopedPtr<CED2KLink> uri(CED2KLink::CreateLinkFromUrl(link));
470 out = uri.get()->GetLink();
471 if (cat && uri.get()->GetKind() == CED2KLink::kFile) {
472 out += CFormat(wxT(":%d")) % cat;
474 return true;
475 } catch ( const wxString& err ) {
476 AddLogLineCS(CFormat(wxT("Invalid eD2k link \"%s\" - ERROR: %s")) % link % err);
478 return false;
483 * Checks permissions on a aMule directory, creating if needed.
485 * @param desc A description of the directory in question, used for error messages.
486 * @param directory The directory in question.
487 * @param alternative If the dir specified with 'directory' could not be created, try this instead.
488 * @param outDir Returns the used path.
489 * @return False on error.
491 bool CamuleAppCommon::CheckMuleDirectory(const wxString& desc, const CPath& directory, const wxString& alternative, CPath& outDir)
493 wxString msg;
495 if (directory.IsDir(CPath::readwritable)) {
496 outDir = directory;
497 return true;
498 } else if (directory.DirExists()) {
499 // Strings are not translated here because translation isn't up yet.
500 msg = CFormat(wxT("Permissions on the %s directory too strict!\n")
501 wxT("aMule cannot proceed. To fix this, you must set read/write/exec\n")
502 wxT("permissions for the folder '%s'"))
503 % desc % directory;
504 } else if (CPath::MakeDir(directory)) {
505 outDir = directory;
506 return true;
507 } else {
508 msg << CFormat(wxT("Could not create the %s directory at '%s'."))
509 % desc % directory;
512 // Attempt to use fallback directory.
513 const CPath fallback(alternative);
514 if (fallback.IsOk() && (directory != fallback)) {
515 msg << wxT("\nAttempting to use default directory at location \n'")
516 << alternative << wxT("'.");
517 if (theApp->ShowAlert(msg, wxT("Error accessing directory."), wxICON_ERROR | wxOK | wxCANCEL) == wxCANCEL) {
518 outDir = CPath(wxEmptyString);
519 return false;
522 return CheckMuleDirectory(desc, fallback, wxEmptyString, outDir);
525 theApp->ShowAlert(msg, wxT("Fatal error."), wxICON_ERROR | wxOK);
526 outDir = CPath(wxEmptyString);
527 return false;