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