1 /***************************************************************************
2 *                                                                         *
3 *   This program is free software; you can redistribute it and/or modify  *
4 *   it under the terms of the GNU General Public License as published by  *
5 *   the Free Software Foundation; either version 3 of the License, or     *
6 *   (at your option) any later version.                                   *
7 *                                                                         *
8 ***************************************************************************/
9 
10 #include <stdlib.h>
11 #include <iostream>
12 #include <string>
13 
14 using namespace std;
15 
16 #include "dcpp/stdinc.h"
17 #include "dcpp/DCPlusPlus.h"
18 
19 #include "dcpp/forward.h"
20 #include "dcpp/QueueManager.h"
21 #include "dcpp/HashManager.h"
22 #include "dcpp/Thread.h"
23 #include "dcpp/Singleton.h"
24 
25 #include "WulforUtil.h"
26 #include "WulforSettings.h"
27 #include "HubManager.h"
28 #include "Notification.h"
29 #include "VersionGlobal.h"
30 #include "IPFilter.h"
31 #include "EmoticonFactory.h"
32 #include "FinishedTransfers.h"
33 #include "QueuedUsers.h"
34 #include "ArenaWidgetManager.h"
35 #include "ArenaWidgetFactory.h"
36 #include "MainWindow.h"
37 #include "GlobalTimer.h"
38 
39 #if defined (__HAIKU__)
40 #include "EiskaltApp_haiku.h"
41 #elif defined(Q_WS_MAC)
42 #include "EiskaltApp_mac.h"
43 #else
44 #include "EiskaltApp.h"
45 #endif
46 
47 #ifdef USE_ASPELL
48 #include "SpellCheck.h"
49 #endif
50 
51 #ifdef USE_JS
52 #include "ScriptEngine.h"
53 #endif
54 
55 #include <QApplication>
56 #include <QMainWindow>
57 #include <QRegExp>
58 #include <QObject>
59 #include <QTextCodec>
60 
61 #ifdef DBUS_NOTIFY
62 #include <QtDBus>
63 #endif
64 
callBack(void * x,const std::string & a)65 void callBack(void* x, const std::string& a)
66 {
67     std::cout << QObject::tr("Loading: ").toStdString() << a << std::endl;
68 }
69 
70 void parseCmdLine(const QStringList &);
71 
72 #if !defined(Q_WS_WIN)
73 #include <unistd.h>
74 #include <signal.h>
75 #if !defined (__HAIKU__)
76 
77 #ifdef ENABLE_STACKTRACE
78 #include "extra/stacktrace.h"
79 #endif // ENABLE_STACKTRACE
80 
81 void installHandlers();
82 #endif
83 
84 #ifdef FORCE_XDG
85 #include <QTextStream>
86 void migrateConfig();
87 #endif
88 
89 #else //WIN32
90 #include <locale.h>
91 #endif
92 
93 #if defined(Q_WS_MAC)
94 #include <objc/objc.h>
95 #include <objc/message.h>
96 
dockClickHandler(id self,SEL _cmd,...)97 bool dockClickHandler(id self,SEL _cmd,...)
98 {
99     Q_UNUSED(self)
100     Q_UNUSED(_cmd)
101     Notification *N = Notification::getInstance();
102     if (N)
103         N->slotShowHide();
104     return true;
105 }
106 #endif
107 
main(int argc,char * argv[])108 int main(int argc, char *argv[])
109 {
110 #if QT_VERSION < 0x050000
111     QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));
112 #endif
113     setlocale(LC_ALL, "");
114 
115     EiskaltApp app(argc, argv, _q(dcpp::Util::getLoginName()+"EDCPP"));
116     int ret = 0;
117 
118     parseCmdLine(app.arguments());
119 
120     if (app.isRunning()){
121         QStringList args = app.arguments();
122         args.removeFirst();//remove path to executable
123 #if !defined (__HAIKU__)
124         app.sendMessage(args.join("\n"));
125 #endif
126         return 0;
127     }
128 
129 #if !defined (Q_WS_WIN) && !defined (__HAIKU__)
130     installHandlers();
131 #endif
132 
133 #if defined(FORCE_XDG) && !defined(Q_WS_WIN)
134     migrateConfig();
135 #endif
136 
137     dcpp::startup(callBack, NULL);
138     dcpp::TimerManager::getInstance()->start();
139 
140     HashManager::getInstance()->setPriority(Thread::IDLE);
141 #if QT_VERSION < 0x050000
142     QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));
143 #endif
144     app.setOrganizationName("EiskaltDC++ Team");
145     app.setApplicationName("EiskaltDC++ Qt");
146     app.setApplicationVersion(EISKALTDCPP_VERSION);
147 
148     GlobalTimer::newInstance();
149 
150     WulforSettings::newInstance();
151     WulforSettings::getInstance()->load();
152     WulforSettings::getInstance()->loadTranslation();
153     WulforSettings::getInstance()->loadTheme();
154 
155     WulforUtil::newInstance();
156 #if defined(Q_WS_MAC)
157     // Disable system tray functionality in Mac OS X:
158     WBSET(WB_TRAY_ENABLED, false);
159 #endif
160 
161     Text::hubDefaultCharset = WulforUtil::getInstance()->qtEnc2DcEnc(WSGET(WS_DEFAULT_LOCALE)).toStdString();
162 
163     if (WulforUtil::getInstance()->loadUserIcons())
164         std::cout << QObject::tr("UserList icons has been loaded").toStdString() << std::endl;
165 
166     if (WulforUtil::getInstance()->loadIcons())
167         std::cout << QObject::tr("Application icons has been loaded").toStdString() << std::endl;
168 
169     app.setWindowIcon(WICON(WulforUtil::eiICON_APPL));
170 
171     ArenaWidgetManager::newInstance();
172 
173     MainWindow::newInstance();
174 #if defined(Q_WS_MAC)
175     MainWindow::getInstance()->setUnload(false);
176 #else // defined(Q_WS_MAC)
177     MainWindow::getInstance()->setUnload(!WBGET(WB_TRAY_ENABLED));
178 #endif // defined(Q_WS_MAC)
179 
180     app.connect(&app, SIGNAL(messageReceived(QString)), MainWindow::getInstance(), SLOT(parseInstanceLine(QString)));
181 
182     HubManager::newInstance();
183 
184     WulforSettings::getInstance()->loadTheme();
185 
186     if (WBGET(WB_APP_ENABLE_EMOTICON)){
187         EmoticonFactory::newInstance();
188         EmoticonFactory::getInstance()->load();
189     }
190 
191 #ifdef USE_ASPELL
192     if (WBGET(WB_APP_ENABLE_ASPELL))
193         SpellCheck::newInstance();
194 #endif
195 
196     Notification::newInstance();
197 
198 #ifdef USE_JS
199     ScriptEngine::newInstance();
200     QObject::connect(ScriptEngine::getInstance(), SIGNAL(scriptChanged(QString)), MainWindow::getInstance(), SLOT(slotJSFileChanged(QString)));
201 #endif
202 
203     ArenaWidgetFactory().create< dcpp::Singleton, FinishedUploads >();
204     ArenaWidgetFactory().create< dcpp::Singleton, FinishedDownloads >();
205     ArenaWidgetFactory().create< dcpp::Singleton, QueuedUsers >();
206 
207     MainWindow::getInstance()->autoconnect();
208     MainWindow::getInstance()->parseCmdLine(app.arguments());
209 
210     if (!WBGET(WB_MAINWINDOW_HIDE) || !WBGET(WB_TRAY_ENABLED))
211         MainWindow::getInstance()->show();
212 
213     ret = app.exec();
214 
215     std::cout << QObject::tr("Shutting down libdcpp...").toStdString() << std::endl;
216 
217     WulforSettings::getInstance()->save();
218 
219     EmoticonFactory::deleteInstance();
220 
221 #ifdef USE_ASPELL
222     if (SpellCheck::getInstance())
223         SpellCheck::deleteInstance();
224 #endif
225     Notification::deleteInstance();
226 
227 #ifdef USE_JS
228     ScriptEngine::deleteInstance();
229 #endif
230 
231     GlobalTimer::deleteInstance();
232 
233     ArenaWidgetManager::deleteInstance();
234 
235     HubManager::getInstance()->release();
236 
237     MainWindow::deleteInstance();
238 
239     WulforUtil::deleteInstance();
240 
241     WulforSettings::deleteInstance();
242 
243     dcpp::shutdown();
244 
245     if (IPFilter::getInstance()){
246         IPFilter::getInstance()->saveList();
247         IPFilter::deleteInstance();
248     }
249 
250     std::cout << QObject::tr("Quit...").toStdString() << std::endl;
251 
252     return ret;
253 }
254 
parseCmdLine(const QStringList & args)255 void parseCmdLine(const QStringList &args){
256     for (const auto &arg : args){
257         if (arg == "-h" || arg == "--help"){
258             About().printHelp();
259 
260             exit(0);
261         }
262         else if (arg == "-V" || arg == "--version"){
263             About().printVersion();
264 
265             exit(0);
266         }
267     }
268 }
269 
270 #if !defined (Q_WS_WIN) && !defined (__HAIKU__)
271 
catchSIG(int sigNum)272 void catchSIG(int sigNum) {
273     psignal(sigNum, "Catching signal ");
274 
275 #ifdef ENABLE_STACKTRACE
276     printBacktrace(sigNum);
277 #endif // ENABLE_STACKTRACE
278 
279     EiskaltApp *eapp = dynamic_cast<EiskaltApp*>(qApp);
280 
281     if (eapp) {
282         eapp->getSharedMemory().unlock();
283         eapp->getSharedMemory().detach();
284     }
285 
286     raise(SIGINT);
287 
288     std::abort();
289 }
290 
291 template <int sigNum = 0, int ... Params>
catchSignals()292 void catchSignals() {
293     if (!sigNum)
294         return;
295 
296     psignal(sigNum, "Installing handler for");
297 
298     signal(sigNum, catchSIG);
299 
300     catchSignals<Params ... >();
301 }
302 
installHandlers()303 void installHandlers(){
304     struct sigaction sa;
305     memset(&sa, 0, sizeof(sa));
306     sa.sa_handler = SIG_IGN;
307 
308     if (sigaction(SIGPIPE, &sa, NULL) == -1)
309         printf("Cannot handle SIGPIPE\n");
310     else {
311         sigset_t set;
312         sigemptyset (&set);
313         sigaddset (&set, SIGPIPE);
314         pthread_sigmask(SIG_BLOCK, &set, NULL);
315     }
316 
317     catchSignals<SIGSEGV, SIGABRT, SIGBUS, SIGTERM>();
318 
319     printf("Signal handlers installed.\n");
320 }
321 
322 #endif
323 
324 #ifdef FORCE_XDG
325 
copy(const QDir & from,const QDir & to)326 void copy(const QDir &from, const QDir &to){
327     if (!from.exists() || to.exists())
328         return;
329 
330     QString to_path = to.absolutePath();
331     QString from_path = from.absolutePath();
332 
333     if (!to_path.endsWith(QDir::separator()))
334         to_path += QDir::separator();
335 
336     if (!from_path.endsWith(QDir::separator()))
337         from_path += QDir::separator();
338 
339     for (const auto &s : from.entryList(QDir::Dirs)){
340         QDir new_dir(to_path+s);
341 
342         if (new_dir.exists())
343             continue;
344         else{
345             if (!new_dir.mkpath(new_dir.absolutePath()))
346                 continue;
347 
348             copy(QDir(from_path+s), new_dir);
349         }
350     }
351 
352     for (const auto &f : from.entryList(QDir::Files)){
353         QFile orig(from_path+f);
354 
355         if (!orig.copy(to_path+f))
356             continue;
357     }
358 }
359 
migrateConfig()360 void migrateConfig(){
361     const char* home_ = getenv("HOME");
362     string home = home_ ? Text::toUtf8(home_) : "/tmp/";
363     string old_config = home + "/.eiskaltdc++/";
364 
365     const char *xdg_config_home_ = getenv("XDG_CONFIG_HOME");
366     string xdg_config_home = xdg_config_home_? Text::toUtf8(xdg_config_home_) : (home+"/.config");
367     string new_config = xdg_config_home + "/eiskaltdc++/";
368 
369     if (!QDir().exists(old_config.c_str()) || QDir().exists(new_config.c_str())){
370         if (!QDir().exists(new_config.c_str())){
371             old_config = _DATADIR + string("/config/");
372 
373             if (!QDir().exists(old_config.c_str()))
374                 return;
375         }
376         else
377             return;
378     }
379 
380     try{
381         printf("Migrating to XDG paths...\n");
382 
383         copy(QDir(old_config.c_str()), QDir(new_config.c_str()));
384 
385         QFile orig(new_config.c_str()+QString("DCPlusPlus.xml"));
386         QFile new_file(new_config.c_str()+QString("DCPlusPlus.xml.new"));
387 
388         if (!(orig.open(QIODevice::ReadOnly | QIODevice::Text) && new_file.open(QIODevice::WriteOnly | QIODevice::Text))){
389             orig.close();
390             new_file.close();
391 
392             printf("Migration failed.\n");
393 
394             return;
395         }
396 
397         QTextStream rstream(&orig);
398         QTextStream wstream(&new_file);
399 
400         QRegExp replace_str("/(\\S+)/\\.eiskaltdc\\+\\+/");
401         QString line = "";
402 
403         while (!rstream.atEnd()){
404             line = rstream.readLine();
405 
406             line.replace(replace_str, QString(new_config.c_str()));
407 
408             wstream << line << "\n";
409         }
410 
411         wstream.flush();
412 
413         orig.close();
414         new_file.close();
415 
416         orig.remove();
417         new_file.rename(orig.fileName());
418 
419         printf("Ok. Migrated.\n");
420     }
421     catch(const std::exception&){
422         printf("Migration failed.\n");
423     }
424 }
425 #endif
426