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 "ArenaWidgetFactory.h"
11 #include "HubFrame.h"
12 #include "PMWindow.h"
13 #include "WulforUtil.h"
14 #include "Antispam.h"
15 #include "HubManager.h"
16 #include "Notification.h"
17 #include "ShellCommandRunner.h"
18 #include "EmoticonDialog.h"
19 #include "WulforSettings.h"
20 #include "FlowLayout.h"
21 #include "SearchFrame.h"
22 #ifdef USE_ASPELL
23 #include "SpellCheck.h"
24 #endif
25 #include "ArenaWidgetManager.h"
26 #include "MainWindow.h"
27 #include "GlobalTimer.h"
28 
29 #include "UserListModel.h"
30 #include "EmoticonFactory.h"
31 
32 #include "dcpp/LogManager.h"
33 #include "dcpp/User.h"
34 #include "dcpp/UserCommand.h"
35 #include "dcpp/CID.h"
36 #include "dcpp/HashManager.h"
37 #include "dcpp/Util.h"
38 #include "dcpp/ChatMessage.h"
39 
40 #if HAVE_MALLOC_TRIM
41 #include <malloc.h>
42 #endif
43 
44 #include <QMouseEvent>
45 #include <QTextCodec>
46 #include <QItemSelectionModel>
47 #include <QMenu>
48 #include <QClipboard>
49 #include <QInputDialog>
50 #include <QTextCursor>
51 #include <QTextBlock>
52 #include <QTextLayout>
53 #include <QTextDocument>
54 #include <QUrl>
55 #include <QCloseEvent>
56 #include <QThread>
57 #include <QRegExp>
58 #include <QScrollBar>
59 #include <QShortcut>
60 #include <QHeaderView>
61 
62 #if QT_VERSION >= 0x050000
63 #include <QUrlQuery>
64 #endif
65 
66 #include <QtDebug>
67 
68 #include <exception>
69 
70 class HubFramePrivate {
71     typedef QMap<QString, PMWindow*> PMMap;
72     typedef QMap<QString, QVariant> VarMap;
73     typedef QList<ShellCommandRunner*> ShellList;
74 public:
75     QMenu *arenaMenu;
76 
77     Client *client;
78 
79     // Work data
80     QTextCodec *codec;
81 
82     quint64 total_shared;
83     QString hub_title;
84 
85     bool chatDisabled;
86     bool hasMessages;
87     bool hasHighlightMessages;
88     bool drawLine;
89 
90     QStringList status_msg_history;
91     QStringList out_messages;
92     int out_messages_index;
93     bool out_messages_unsent;
94 
95     PMMap pm;
96     ShellList shell_list;
97 
98     // Userlist data and some helpful functions
99     UserListModel *model;
100     UserListProxyModel *proxy;
101 
102     QCompleter * completer;
103 };
104 
clearLayout(QLayout * l)105 static inline void clearLayout(QLayout *l){
106     if (!l)
107         return;
108 
109     QLayoutItem *item = NULL;
110     while ((item = l->takeAt(0))){
111         l->removeWidget(item->widget());
112         item->widget()->deleteLater();
113 
114         delete item;
115     }
116 
117     l->invalidate();
118 }
119 
120 //replace [tag]some_text[/tag] with <txt>some_text</txt>
parseBasicBBCode(const QString & tag,const QString & txt,QString & input,QString & output)121 static bool parseBasicBBCode(const QString &tag, const QString &txt, QString &input, QString &output){
122     if (tag.isEmpty())
123         return false;
124 
125     const QString &bbCode1 = QString("[%1]").arg(tag);
126     const QString &bbCode2 = QString("[/%1]").arg(tag);
127 
128     if (input.startsWith(bbCode1) && input.indexOf(bbCode2) >= bbCode1.length()){
129         input.remove(0, bbCode1.length());
130         int c_len = input.indexOf(bbCode2);
131 
132         const QString &chunk = HubFrame::LinkParser::parseForLinks(input.left(c_len), false);
133 
134         output += QString("<%1>").arg(txt) + chunk + QString("</%1>").arg(txt);
135         input.remove(0, c_len+bbCode2.length());
136 
137         return true;
138     }
139 
140     return false;
141 }
142 
143 HubFrame::Menu *HubFrame::Menu::instance = NULL;
144 unsigned HubFrame::Menu::counter = 0;
145 
newInstance()146 void HubFrame::Menu::newInstance(){
147     counter++;
148 
149     if (instance)
150         return;
151 
152     instance = new Menu();
153 }
154 
deleteInstance()155 void HubFrame::Menu::deleteInstance(){
156     counter--;
157 
158     if (counter)
159         return;
160 
161     delete instance;
162 
163     instance = NULL;
164 }
165 
getInstance()166 HubFrame::Menu *HubFrame::Menu::getInstance(){
167     return instance;
168 }
169 
Menu()170 HubFrame::Menu::Menu(){
171     menu = new QMenu();
172     WulforUtil *WU = WulforUtil::getInstance();
173 
174     last_user_cmd = "";
175 
176     // Userlist actions
177     QAction *copy_text   = new QAction(WU->getPixmap(WulforUtil::eiEDITCOPY), tr("Copy"), NULL);
178     QAction *search_text = new QAction(WU->getPixmap(WulforUtil::eiFIND), tr("Search text"), NULL);
179     QAction *copy_nick   = new QAction(WU->getPixmap(WulforUtil::eiEDITCOPY), tr("Copy nick"), NULL);
180     QAction *find        = new QAction(WU->getPixmap(WulforUtil::eiFIND), tr("Show in list"), NULL);
181     QAction *browse      = new QAction(WU->getPixmap(WulforUtil::eiFOLDER_BLUE), tr("Browse files"), NULL);
182     QAction *match_queue = new QAction(WU->getPixmap(WulforUtil::eiDOWN), tr("Match Queue"), NULL);
183     QAction *private_msg = new QAction(WU->getPixmap(WulforUtil::eiMESSAGE), tr("Private Message"), NULL);
184     QAction *fav_add     = new QAction(WU->getPixmap(WulforUtil::eiFAVADD), tr("Add to Favorites"), NULL);
185     QAction *fav_del     = new QAction(WU->getPixmap(WulforUtil::eiFAVREM), tr("Remove from Favorites"), NULL);
186     QAction *grant_slot  = new QAction(WU->getPixmap(WulforUtil::eiEDITADD), tr("Grant slot"), NULL);
187     QAction *rem_queue   = new QAction(WU->getPixmap(WulforUtil::eiEDITDELETE), tr("Remove from Queue"), NULL);
188 
189     // Chat actions
190     QAction *sep1        = new QAction(NULL);
191     QAction *clear_chat  = new QAction(WU->getPixmap(WulforUtil::eiCLEAR), tr("Clear chat"), NULL);
192     QAction *find_in_chat= new QAction(WU->getPixmap(WulforUtil::eiFIND), tr("Find in chat"), NULL);
193     QAction *dis_chat    = new QAction(WU->getPixmap(WulforUtil::eiFILECLOSE), tr("Disable/Enable chat"), NULL);
194     QAction *sep2        = new QAction(NULL);
195     QAction *select_all  = new QAction(tr("Select all"), NULL);
196     QAction *sep3        = new QAction(NULL);
197     QAction *zoom_in     = new QAction(WU->getPixmap(WulforUtil::eiZOOM_IN), tr("Zoom In"), NULL);
198     QAction *zoom_out    = new QAction(WU->getPixmap(WulforUtil::eiZOOM_OUT), tr("Zoom Out"), NULL);
199 
200     // submenu copy_data for user list
201     QAction *copy_data_nick  = new QAction(tr("Nick"), NULL);
202     QAction *copy_data_ip    = new QAction(tr("IP"), NULL);
203     QAction *copy_data_share = new QAction(tr("Share"), NULL);
204     QAction *copy_data_tag   = new QAction(tr("Tag"), NULL);
205     QAction *sep4            = new QAction(NULL);
206     QAction *copy_data_all   = new QAction(tr("All"), NULL);
207 
208     QMenu *menuCopyData = new QMenu(NULL);
209     menuCopyData->addActions(QList<QAction*>() << copy_data_nick << copy_data_ip << copy_data_share << copy_data_tag << sep4 << copy_data_all);
210 
211     QAction *copy_data   = new QAction(WU->getPixmap(WulforUtil::eiEDITCOPY), tr("Copy data"), NULL);
212     copy_data->setMenu(menuCopyData);
213     // end submenu
214 
215     sep1->setSeparator(true), sep2->setSeparator(true), sep3->setSeparator(true), sep4->setSeparator(true);
216 
217     actions << copy_text
218             << search_text
219             << copy_nick
220             << find
221             << browse
222             << match_queue
223             << private_msg
224             << fav_add
225             << fav_del
226             << grant_slot
227             << rem_queue;
228 
229     pm_actions << copy_text
230             << copy_nick
231             << browse
232             << match_queue
233             << private_msg
234             << fav_add
235             << fav_del
236             << grant_slot
237             << rem_queue;
238 
239     ul_actions << browse
240             << private_msg
241             << fav_add
242             << fav_del
243             << grant_slot
244             << copy_data
245             << match_queue
246             << rem_queue;
247 
248     chat_actions << sep1
249                  << clear_chat
250                  << find_in_chat
251                  << dis_chat
252                  << sep2
253                  << select_all
254                  << sep3
255                  << zoom_in
256                  << zoom_out;
257 
258     pm_chat_actions << sep1
259                  << clear_chat
260                  << sep2
261                  << select_all
262                  << sep3
263                  << zoom_in
264                  << zoom_out;
265 
266     chat_actions_map.insert(copy_text, CopyText);
267     chat_actions_map.insert(search_text, SearchText);
268     chat_actions_map.insert(copy_nick, CopyNick);
269     chat_actions_map.insert(clear_chat, ClearChat);
270     chat_actions_map.insert(find_in_chat, FindInChat);
271     chat_actions_map.insert(dis_chat, DisableChat);
272     chat_actions_map.insert(select_all, SelectAllChat);
273     chat_actions_map.insert(zoom_in, ZoomInChat);
274     chat_actions_map.insert(zoom_out, ZoomOutChat);
275 
276     chat_actions_map.insert(copy_data_nick,  CopyNick);
277     chat_actions_map.insert(copy_data_ip,    CopyIP);
278     chat_actions_map.insert(copy_data_share, CopyShare);
279     chat_actions_map.insert(copy_data_tag,   CopyTag);
280     chat_actions_map.insert(copy_data_all,   CopyText);
281 }
282 
283 
~Menu()284 HubFrame::Menu::~Menu(){
285     delete menu;
286 
287     qDeleteAll(chat_actions);
288     qDeleteAll(actions);
289 }
290 
execUserMenu(Client * client,const QString & cid=QString ())291 HubFrame::Menu::Action HubFrame::Menu::execUserMenu(Client *client, const QString &cid = QString()){
292    if (!client)
293         return None;
294 
295     menu->clear();
296     menu->setProperty("iconVisibleInMenu", true);
297 
298     menu->setTitle(WulforUtil::getInstance()->getNickViaOnlineUser(cid, _q(client->getHubUrl())));
299 
300     if (menu->title().isEmpty())
301         menu->setTitle(tr("[User went offline]"));
302 
303     menu->addActions(ul_actions);
304 
305     QMenu *user_menu = NULL;
306 
307     if (!cid.isEmpty()){
308         user_menu = WulforUtil::getInstance()->buildUserCmdMenu(client->getHubUrl(), UserCommand::CONTEXT_USER);
309 
310         if (user_menu && !user_menu->actions().empty())
311             menu->addMenu(user_menu);
312     }
313 
314     QMenu *antispam_menu = NULL;
315 
316     if (AntiSpam::getInstance()){
317         antispam_menu = new QMenu(NULL);
318         antispam_menu->setTitle(tr("AntiSpam"));
319         antispam_menu->menuAction()->setIcon(WICON(WulforUtil::eiSPAM));
320         antispam_menu->setProperty("iconVisibleInMenu", true);
321 
322         antispam_menu->addAction(tr("Add to Black"))->setData(static_cast<int>(AntiSpamBlack));
323         antispam_menu->addAction(tr("Add to White"))->setData(static_cast<int>(AntiSpamWhite));
324 
325         menu->addMenu(antispam_menu);
326     }
327 
328     QAction *res = menu->exec(QCursor::pos());
329 
330     if (user_menu)
331         user_menu->deleteLater();
332     if (antispam_menu)
333         antispam_menu->deleteLater();
334 
335     if (actions.contains(res))
336         return static_cast<Action>(actions.indexOf(res));
337     else if (chat_actions_map.contains(res))
338         return chat_actions_map[res];
339     else if (antispam_menu && antispam_menu->actions().contains(res))
340         return static_cast<HubFrame::Menu::Action>(res->data().toInt());
341     else if (res && res->data().canConvert(QVariant::Int)){//User command{
342         int id = res->data().toInt();
343 
344         UserCommand uc;
345         if (id == -1 || !FavoriteManager::getInstance()->getUserCommand(id, uc))
346             return None;
347 
348         StringMap params;
349 
350         if (WulforUtil::getInstance()->getUserCommandParams(uc, params)) {
351             UserPtr user = ClientManager::getInstance()->findUser(CID(cid.toStdString()));
352 
353             if (user)
354                 ClientManager::getInstance()->userCommand(HintedUser(user, client->getHubUrl()), uc, params, true);
355 
356             return UserCommands;
357         }
358         else
359             return None;
360     }
361     else
362         return None;
363 }
364 
getLastUserCmd() const365 QString HubFrame::Menu::getLastUserCmd() const{
366     return last_user_cmd;
367 }
368 
execChatMenu(Client * client,const QString & cid=QString (),bool pmw=false)369 HubFrame::Menu::Action HubFrame::Menu::execChatMenu(Client *client, const QString &cid = QString(), bool pmw = false){
370     if (!client)
371         return None;
372 
373     menu->clear();
374     QAction *title = new QAction(WulforUtil::getInstance()->getNickViaOnlineUser(cid, _q(client->getHubUrl())), menu);
375     QFont f;
376     f.setBold(true);
377     title->setFont(f);
378     title->setEnabled(false);
379 
380     if (title->text().isEmpty())
381         title->setText(tr("[User went offline]"));
382 
383     menu->addAction(title);
384 
385     if(pmw){
386         menu->addActions(pm_actions);
387         menu->addActions(pm_chat_actions);
388     }
389     else{
390         menu->addActions(actions);
391         menu->addActions(chat_actions);
392     }
393 
394     QMenu *user_menu = NULL;
395 
396     if (!cid.isEmpty() && !pmw){
397         user_menu = WulforUtil::getInstance()->buildUserCmdMenu(client->getHubUrl(), UserCommand::CONTEXT_HUB);
398 
399         if (user_menu && !user_menu->actions().isEmpty())
400             menu->addMenu(user_menu);
401     }
402 
403     QMenu *antispam_menu = NULL;
404 
405     if (AntiSpam::getInstance()){
406         antispam_menu = new QMenu(NULL);
407         antispam_menu->setTitle(tr("AntiSpam"));
408 
409         antispam_menu->addAction(tr("Add to Black"))->setData(static_cast<int>(AntiSpamBlack));
410         antispam_menu->addAction(tr("Add to White"))->setData(static_cast<int>(AntiSpamWhite));
411 
412         menu->addMenu(antispam_menu);
413     }
414 
415     QAction *res = menu->exec(QCursor::pos());
416 
417     if (user_menu)
418         user_menu->deleteLater();
419 
420     title->deleteLater();
421 
422     if (actions.contains(res))
423         return static_cast<Action>(actions.indexOf(res));
424     else if (chat_actions_map.contains(res))
425         return chat_actions_map[res];
426     else if (antispam_menu && antispam_menu->actions().contains(res))
427         return static_cast<HubFrame::Menu::Action>(res->data().toInt());
428     else if (res && res->data().canConvert(QVariant::Int)){//User command
429         int id = res->data().toInt();
430 
431         UserCommand uc;
432         if (id == -1 || !FavoriteManager::getInstance()->getUserCommand(id, uc))
433             return None;
434 
435         StringMap params;
436 
437         if (WulforUtil::getInstance()->getUserCommandParams(uc, params)){
438             UserPtr user = ClientManager::getInstance()->findUser(CID(cid.toStdString()));
439 
440             if (user)
441                 ClientManager::getInstance()->userCommand(HintedUser(user, client->getHubUrl()), uc, params, true);
442 
443             return UserCommands;
444         }
445         else
446             return None;
447     }
448     else
449         return None;
450 }
451 
parseForLinks(QString input,bool use_emot)452 QString HubFrame::LinkParser::parseForLinks(QString input, bool use_emot){
453     if (input.isEmpty())
454         return input;
455 
456     static QList<QChar> unwise_chars = QList<QChar>() << '{' << '}' << '|' << '\\' << '^' << '[' << ']' << '`';
457     static QStringList link_types = QStringList() << "http://" << "https://" << "ftp://" << "www."
458                                                   << "dchub://" << "adc://" << "adcs://" << "magnet:";
459 
460     QString output = "";
461 
462     EmoticonMap emoticons;
463 
464     if (use_emot && WBGET(WB_APP_ENABLE_EMOTICON) && EmoticonFactory::getInstance())
465         emoticons = EmoticonFactory::getInstance()->getEmoticons();
466 
467     const QString &emo_theme = WSGET(WS_APP_EMOTICON_THEME);
468     bool force_emot = WBGET(WB_APP_FORCE_EMOTICONS);
469 
470     while (!input.isEmpty()){
471         for (int j = 0; j < link_types.size(); j++){
472             const QString &linktype = link_types.at(j);
473 
474             if (input.startsWith(linktype)){
475                 int l_pos = linktype.length();
476 
477                 while (l_pos < input.size()){
478                     QChar ch = input.at(l_pos);
479 
480                     if (ch.isSpace() || ch == '\n'  ||
481                         ch == '>'    || ch == '<' || unwise_chars.contains(ch)){
482                         break;
483                     }
484                     else
485                         l_pos++;
486                 }
487 
488                 QString link = input.left(l_pos);
489                 QString toshow = link;
490 
491                 if (linktype == "http://"  || linktype == "https://" || linktype == "ftp://"){
492                     while (!QUrl(link).isValid() && !link.isEmpty()){
493                         input.prepend(link.at(link.length()-1));
494                         link.remove(link.length()-1, 1);
495                     }
496 
497                     toshow = QUrl::fromEncoded(link.toUtf8()).toString();
498                 }
499 
500                 if (linktype == "magnet:"){
501                     QString magnet = link;
502 
503 #if QT_VERSION >= 0x050000
504                     QUrlQuery u;
505 #else
506                     QUrl u;
507 #endif
508 
509                     if (!magnet.contains("+")) {
510 #if QT_VERSION >= 0x050000
511                         u.setQuery(magnet.toUtf8());
512 #else
513                         u.setEncodedUrl(magnet.toUtf8());
514 #endif
515                     } else {
516                         QString _l = magnet;
517 
518                         _l.replace("+", "%20");
519 #if QT_VERSION >= 0x050000
520                             u.setQuery(_l.toUtf8());
521 #else
522                             u.setEncodedUrl(_l.toUtf8());
523 #endif
524                     }
525                     if (u.hasQueryItem("kt")) {
526                         QString keywords = u.queryItemValue("kt");
527                         QString hub = u.hasQueryItem("xs")? u.queryItemValue("xs") : "";
528                         if (!(hub.startsWith("dchub://", Qt::CaseInsensitive) ||
529                               hub.startsWith("adc://", Qt::CaseInsensitive) ||
530                               hub.startsWith("adcs://", Qt::CaseInsensitive)) && !hub.isEmpty())
531                             hub.prepend("dchub://");
532 
533                         if (keywords.isEmpty())
534                             keywords = tr("Invalid keywords");
535 
536                         if (!hub.isEmpty())
537 #if QT_VERSION >= 0x050000
538                             toshow = keywords.toHtmlEscaped() + " (" + hub.toHtmlEscaped() + ")";
539                         else
540                             toshow = keywords.toHtmlEscaped();
541 #else
542                             toshow = Qt::escape(keywords) + " (" + Qt::escape(hub) + ")";
543                         else
544                             toshow = Qt::escape(keywords);
545 #endif
546                     }
547                     else {
548                         QString name, tth;
549                         int64_t size;
550 
551                         WulforUtil::splitMagnet(link, size, tth, name);
552                         toshow = QString("%1 (%2)").arg(name).arg(WulforUtil::formatBytes(size));
553                     }
554                 }
555 
556                 if (linktype == "www.")
557                     toshow.prepend("http://");
558 
559                 QString html_link = "";
560 
561                 if (linktype != "magnet:")
562                     html_link = QString("<a href=\"%1\" title=\"%1\" style=\"cursor: hand\">%1</a>").arg(toshow);
563                 else{
564                     html_link = "<a href=\"" + link + "\" title=\"" + toshow + "\" style=\"cursor: hand\">" + toshow + "</a>";
565                 }
566 
567                 output += html_link;
568                 input.remove(0, l_pos);
569             }
570 
571             if (input.isEmpty())
572                 break;
573         }
574 
575         if (input.isEmpty())
576             break;
577 
578         auto it = emoticons.begin();
579         auto end_it = emoticons.end();
580         bool smile_found = false;
581 
582         for (; it != end_it; ++it){//Let's try to parse smiles
583             const QString &emo_text = it.key();
584             EmoticonObject *obj = it.value();
585 
586             if (input.startsWith(emo_text) && obj){
587                 if (force_emot || input == emo_text){
588                      QString img = QString("<img alt=\"%1\" title=\"%1\" align=\"center\" source=\"%2/emoticon%3\" />")
589                                   .arg(emo_text)
590                                   .arg(emo_theme)
591                                   .arg(obj->id);
592 
593                     output += img;
594                     input.remove(0, emo_text.length());
595 
596                     smile_found = true;
597 
598                     break;
599                 }
600                 else if (output.endsWith(' ') || output.endsWith('\t') || output.isEmpty()){
601                     int emo_text_len = emo_text.length();
602                     int input_length = input.length();
603 
604                     bool nextCharisSpace = false;
605 
606                     if (emo_text_len == input_length)
607                         nextCharisSpace = true;
608                     else if (input_length > emo_text_len){
609                         char c = input.at(emo_text_len).unicode();
610 
611                         nextCharisSpace = (c == ' ' || c == '\t');
612                     }
613 
614                     if (!nextCharisSpace)
615                         continue;
616 
617                     QString img = QString("<img alt=\"%1\" title=\"%1\" align=\"center\" source=\"%2/emoticon%3\" />")
618                                   .arg(emo_text)
619                                   .arg(emo_theme)
620                                   .arg(obj->id);
621 
622                     output += img;
623                     input.remove(0, emo_text_len);
624 
625                     smile_found = true;
626 
627                     break;
628                 }
629             }
630         }
631 
632         if(smile_found)
633             continue;
634 
635         if (WBGET("hubframe/use-bb-code", false)){
636             if      (parseBasicBBCode("b", "b", input, output))
637                 continue;
638             else if (parseBasicBBCode("u", "u", input, output))
639                 continue;
640             else if (parseBasicBBCode("i", "i", input, output))
641                 continue;
642             else if (parseBasicBBCode("s", "s", input, output))
643                 continue;
644 
645             if (input.startsWith("[color=") && input.indexOf("[/color]") > 8){
646                 QRegExp exp("\\[color=(\\w+|#.{6,6})\\]((.*))\\[/color\\].*");
647                 QString chunk = input.left(input.indexOf("[/color]")+8);
648 
649                 if (exp.exactMatch(chunk)){
650                     if (exp.captureCount() == 3){
651                         output += "<font color=\"" + exp.cap(1) + "\">" + parseForLinks(exp.cap(2), false) + "</font>";
652 
653                         input.remove(0, chunk.length());
654                     }
655                 }
656             }
657             else if (input.startsWith(("[url")) && input.indexOf("[/url]") > 0){
658                 QRegExp exp("\\[url=*((.+[^\\]\\[]))*\\]((.+))\\[/url\\]");
659                 QString chunk = input.left(input.indexOf("[/url]")+6);
660 
661                 if (exp.exactMatch(chunk) && exp.captureCount() == 4){
662                     QString link = exp.cap(2);
663                     QString title = exp.cap(3);
664 
665                     link = link.isEmpty()? title : link;
666 
667                     if (link.startsWith("="))
668                         link.remove(0, 1);
669 
670                     if (!title.isEmpty()){
671 #if QT_VERSION >= 0x050000
672                             output += "<a href=\"" + link + "\" title=\"" + title.toHtmlEscaped() + "\">" + title.toHtmlEscaped() + "</a>";
673 #else
674                             output += "<a href=\"" + link + "\" title=\"" + Qt::escape(title) + "\">" + Qt::escape(title) + "</a>";
675 #endif
676 
677                         input.remove(0, chunk.length());
678                     }
679                 }
680             }
681             else if (input.startsWith(("[code]")) && input.indexOf("[/code]") > 0){
682                 input.remove(0, 6);
683                 int c_len = input.indexOf("[/code]");
684 
685                 QString chunk = input.left(c_len);
686 
687                 output += "<table border=1 width=100%><tr><td align=\"left\">Code:</td></tr><tr><td align=\"left\"><pre style=\"white-space: pre;\"><tt>" + chunk + "</tt></pre></td></tr></table>";
688                 input.remove(0, c_len+7);
689 
690                 continue;
691             }
692         }
693 
694         if (input.startsWith("<")){
695             output += "&lt;";
696             input.remove(0, 1);
697 
698             continue;
699         }
700         else if (input.startsWith(">")){
701             output += "&gt;";
702             input.remove(0, 1);
703 
704             continue;
705         }
706         else if (input.startsWith("&")){
707             input.remove(0, 1);
708             output += "&amp;";
709 
710             continue;
711         }
712         else if (input.startsWith("[magnet=\"") && input.indexOf("[/magnet]") > 9){//9 - length of [magnet="
713             QString chunk = input.left(input.indexOf("[/magnet]"));
714 
715             do{
716                 if (chunk.isEmpty())
717                     break;
718 
719                 chunk.remove(0, 9);
720 
721                 if (chunk.isEmpty() || chunk.indexOf("\"") <= 0)
722                     break;
723 
724                 QString magnet = chunk.left(chunk.indexOf("\""));
725 
726                 magnet = magnet.trimmed();
727 
728                 if (magnet.isEmpty())
729                     break;
730 
731                 QString name, tth;
732                 int64_t size;
733 
734                 WulforUtil::splitMagnet(magnet, size, tth, name);
735 
736                 chunk.remove(0, magnet.length());
737 
738                 if (chunk.indexOf("]") < 1)
739                     break;
740 
741                 chunk.remove(0, chunk.indexOf("]") + 1);
742 
743                 if (chunk.isEmpty())
744                     break;
745 
746                 QString toshow = tr("%1 (%2)").arg(chunk).arg(WulforUtil::formatBytes(size));
747                 QString html_link = "<a href=\"" + magnet + "\" title=\"" + toshow + "\" style=\"cursor: hand\">" + toshow + "</a>";
748 
749                 output += html_link;
750                 input.remove(0, input.indexOf("[/magnet]")+1+9);
751             }
752             while (0);
753         }
754 
755         if (input.isEmpty())
756             break;
757 
758         output += input.at(0);
759 
760         input.remove(0, 1);
761     }
762 
763     return output;
764 }
765 
parseForMagnetAlias(QString & output)766 void HubFrame::LinkParser::parseForMagnetAlias(QString &output){
767     int pos = 0;
768     QRegExp rx("(<magnet(?:\\s+show=([^>]+))?>(.+)</magnet>)");
769     rx.setMinimal(true);
770     while ((pos = output.indexOf(rx, pos)) >= 0) {
771         QFileInfo fi(rx.cap(3));
772         if (fi.isDir() || !fi.exists()) {
773             pos++;
774             continue;
775         }
776         QString name = fi.fileName();
777         if (!rx.cap(2).isEmpty())
778             name = rx.cap(2);
779 
780         const TTHValue *tth = HashManager::getInstance()->getFileTTHif(_tq(fi.absoluteFilePath()));
781         if (tth) {
782             QString urlStr = WulforUtil::getInstance()->makeMagnet(name, fi.size(), _q(tth->toBase32()));
783             output.replace(pos, rx.cap(1).length(), urlStr);
784         } else {
785             output.replace(pos, rx.cap(1).length(), tr("not shared"));
786         }
787     }
788 }
789 
HubFrame(QWidget * parent,QString hub="",QString encoding="")790 HubFrame::HubFrame(QWidget *parent, QString hub="", QString encoding=""):
791         QWidget(parent),
792         d_ptr(new HubFramePrivate())
793 {
794     Q_D(HubFrame);
795 
796     d->total_shared = 0;
797     d->arenaMenu = NULL;
798     d->codec = NULL;
799     d->chatDisabled = false;
800     d->hasMessages = false;
801     d->hasHighlightMessages = false;
802     d->client = NULL;
803 
804     setupUi(this);
805 
806     Menu::newInstance();
807 
808     d->client = ClientManager::getInstance()->getClient(hub.toStdString());
809     d->client->addListener(this);
810 
811     QString enc = WulforUtil::getInstance()->qtEnc2DcEnc(encoding);
812 
813     if (enc.isEmpty())
814         enc = WulforUtil::getInstance()->qtEnc2DcEnc(WSGET(WS_DEFAULT_LOCALE));
815 
816     if (enc.indexOf(" ") > 0){
817         enc = enc.left(enc.indexOf(" "));
818         enc.replace(" ", "");
819     }
820 
821     d->client->setEncoding(enc.toStdString());
822 
823     d->codec = WulforUtil::getInstance()->codecForEncoding(encoding);
824 
825     init();
826 
827     FavoriteHubEntry* entry = FavoriteManager::getInstance()->getFavoriteHubEntry(_tq(hub));
828 
829     if (entry && entry->getDisableChat())
830         disableChat();
831 
832     d->client->connect();
833 
834     setAttribute(Qt::WA_DeleteOnClose);
835 
836     d->out_messages_index = 0;
837     d->out_messages_unsent = false;
838 
839     FavoriteManager::getInstance()->addListener(this);
840 }
841 
842 
~HubFrame()843 HubFrame::~HubFrame(){
844     Q_D(HubFrame);
845 
846     Menu::deleteInstance();
847 
848     treeView_USERS->setModel(NULL);
849 
850     delete d->proxy;
851     delete d->model;
852 
853     delete d;
854 }
855 
eventFilter(QObject * obj,QEvent * e)856 bool HubFrame::eventFilter(QObject *obj, QEvent *e){
857     Q_D(HubFrame);
858 
859     if (e->type() == QEvent::KeyRelease){
860         QKeyEvent *k_e = reinterpret_cast<QKeyEvent*>(e);
861 
862         if ((static_cast<QTextEdit*>(obj) == plainTextEdit_INPUT) &&
863             (k_e->key() == Qt::Key_Enter || k_e->key() == Qt::Key_Return) &&
864             (k_e->modifiers() != Qt::ShiftModifier))
865         {
866             return true;
867         }
868         else if (static_cast<QLineEdit*>(obj) == lineEdit_FIND){
869             bool ret = QWidget::eventFilter(obj, e);
870 
871             if (k_e->key() == Qt::Key_Enter || k_e->key() == Qt::Key_Return)
872                 slotFindForward();
873 
874             return ret;
875         }
876 
877     }
878     else if (e->type() == QEvent::KeyPress){
879         QKeyEvent *k_e = reinterpret_cast<QKeyEvent*>(e);
880 
881         if ((static_cast<QTextEdit*>(obj) == plainTextEdit_INPUT) &&
882             (!WBGET(WB_USE_CTRL_ENTER) || k_e->modifiers() == Qt::ControlModifier) &&
883             ((k_e->key() == Qt::Key_Enter || k_e->key() == Qt::Key_Return) && k_e->modifiers() != Qt::ShiftModifier) ||
884              (k_e->key() == Qt::Key_Enter && k_e->modifiers() == Qt::KeypadModifier))
885         {
886             sendChat(plainTextEdit_INPUT->toPlainText(), false, false);
887 
888             plainTextEdit_INPUT->setPlainText("");
889 
890             return true;
891         }
892 
893         if (qobject_cast<LineEdit*>(obj) == lineEdit_FIND && k_e->key() == Qt::Key_Escape){
894             lineEdit_FIND->clear();
895 
896             requestFilter();
897 
898             return true;
899         }
900 
901         if (k_e->modifiers() == Qt::ControlModifier){
902             if (k_e->key() == Qt::Key_Equal || k_e->key() == Qt::Key_Plus){
903                 textEdit_CHAT->zoomIn();
904 
905                 return true;
906             }
907             else if (k_e->key() == Qt::Key_Minus){
908                 textEdit_CHAT->zoomOut();
909 
910                 return true;
911             }
912         }
913     }
914     else if (e->type() == QEvent::MouseButtonPress){
915         QMouseEvent *m_e = reinterpret_cast<QMouseEvent*>(e);
916 
917         bool isChat = (static_cast<QWidget*>(obj) == textEdit_CHAT->viewport());
918         bool isUserList = (static_cast<QWidget*>(obj) == treeView_USERS->viewport());
919 
920         if (isChat)
921             textEdit_CHAT->setExtraSelections(QList<QTextEdit::ExtraSelection>());
922 
923         if (isChat && (m_e->button() == Qt::LeftButton)){
924             QString pressedParagraph = textEdit_CHAT->anchorAt(textEdit_CHAT->mapFromGlobal(QCursor::pos()));
925 
926             if (!WulforUtil::getInstance()->openUrl(pressedParagraph)){
927                 /**
928                   Do nothing
929                 */
930             }
931         }
932         else if ((isChat || isUserList) && m_e->button() == Qt::MidButton)
933         {
934             QString nick = "";
935             QString cid = "";
936             bool cursoratnick = false;
937 
938             if (isChat){
939                 QTextCursor cursor = textEdit_CHAT->textCursor();
940                 QString pressedParagraph = cursor.block().text();
941                 int positionCursor = cursor.columnNumber();
942 
943                 int l = pressedParagraph.indexOf(" <");
944                 int r = pressedParagraph.indexOf("> ");
945 
946                 if (l < r){
947                     nick = pressedParagraph.mid(l+2, r-l-2);
948                     cid = d->model->CIDforNick(nick, _q(d->client->getHubUrl()));
949                 }
950                 if ((positionCursor < r) && (positionCursor > l))
951                     cursoratnick = true;
952             }
953             else if (isUserList){
954                 QModelIndex index = treeView_USERS->indexAt(treeView_USERS->viewport()->mapFromGlobal(QCursor::pos()));
955 
956                 if (d->proxy && treeView_USERS->model() == d->proxy)
957                     index = d->proxy->mapToSource(index);
958 
959                 if (index.isValid()){
960                     UserListItem *i = reinterpret_cast<UserListItem*>(index.internalPointer());
961 
962                     nick = i->getNick();
963                     cid = i->getCID();
964                 }
965             }
966 
967             if (!cid.isEmpty()){
968                 if (WIGET(WI_CHAT_MDLCLICK_ACT) == 0){
969                     if(!plainTextEdit_INPUT->textCursor().position())
970                         plainTextEdit_INPUT->textCursor().insertText(nick + WSGET(WS_CHAT_SEPARATOR) + " ");
971                     else
972                         plainTextEdit_INPUT->textCursor().insertText(nick + " ");
973 
974                     plainTextEdit_INPUT->setFocus();
975                 }
976                 else if (WIGET(WI_CHAT_MDLCLICK_ACT) == 2 && (cursoratnick || isUserList))
977                     addPM(cid, "", false);
978                 else if (cursoratnick || isUserList)
979                     browseUserFiles(cid, false);
980             }
981         }
982     }
983     else if (e->type() == QEvent::MouseButtonDblClick){
984         bool isChat = (static_cast<QWidget*>(obj) == textEdit_CHAT->viewport());
985         bool isUserList = (static_cast<QWidget*>(obj) == treeView_USERS->viewport());
986 
987         if (isChat || isUserList){
988             QString nick = "";
989             QString cid = "";
990             bool cursoratnick = false;
991             QTextCursor cursor = textEdit_CHAT->textCursor();
992 
993             if (isChat){
994                 QString nickstatus="",nickmessage="";
995                 QString pressedParagraph = cursor.block().text();
996                 //qDebug() << pressedParagraph;
997                 int positionCursor = cursor.columnNumber();
998                 int l = pressedParagraph.indexOf(" <");
999                 int r = pressedParagraph.indexOf("> ");
1000                 if (l < r)
1001                     nickmessage = pressedParagraph.mid(l+2, r-l-2);
1002                 else {
1003                     int l1 = pressedParagraph.indexOf(" * ");
1004                     //qDebug() << positionCursor << " " << l1 << " " << l << " " << r;
1005                     if (l1 > -1 ) {
1006                         QString pressedParagraphstatus = pressedParagraph.remove(0,l1+3).simplified();
1007                         //qDebug() << pressedParagraphstatus;
1008                         int r1 = pressedParagraphstatus.indexOf(" ");
1009                         //qDebug() << r1;
1010                         nickstatus = pressedParagraphstatus.mid(0, r1);
1011                         //qDebug() << nickstatus;
1012                     }
1013                 }
1014                 if (!nickmessage.isEmpty() || !nickstatus.isEmpty()) {
1015                     //qDebug() << nickstatus;
1016                     //qDebug() << nickmessage;
1017                     nick = nickmessage + nickstatus;
1018                     //qDebug() << nick;
1019                     cid = d->model->CIDforNick(nick, _q(d->client->getHubUrl()));
1020                     //qDebug() << cid;
1021                     }
1022                 if (((positionCursor < r) && (positionCursor > l))/* || positionCursor > l1*/)
1023                     cursoratnick = true;
1024             }
1025             else if (isUserList){
1026                 QModelIndex index = treeView_USERS->indexAt(treeView_USERS->viewport()->mapFromGlobal(QCursor::pos()));
1027 
1028                 if (d->proxy && treeView_USERS->model() == d->proxy)
1029                     index = d->proxy->mapToSource(index);
1030 
1031                 if (index.isValid()){
1032                     UserListItem *i = reinterpret_cast<UserListItem*>(index.internalPointer());
1033 
1034                     nick = i->getNick();
1035                     cid = i->getCID();
1036                 }
1037             }
1038 
1039             if (!cid.isEmpty()){
1040                 if (WIGET(WI_CHAT_DBLCLICK_ACT) == 1 && (cursoratnick || isUserList)){
1041                     browseUserFiles(cid, false);
1042                 }
1043                 else if (WIGET(WI_CHAT_DBLCLICK_ACT) == 2 && (cursoratnick || isUserList)){
1044                     addPM(cid, "", false);
1045                 }
1046                 else if (textEdit_CHAT->anchorAt(textEdit_CHAT->mapFromGlobal(QCursor::pos())).startsWith("user://") || isUserList){//may be dbl click on user nick
1047                     if(!plainTextEdit_INPUT->textCursor().position())
1048                         plainTextEdit_INPUT->textCursor().insertText(nick + WSGET(WS_CHAT_SEPARATOR) + " ");
1049                     else
1050                         plainTextEdit_INPUT->textCursor().insertText(nick + " ");
1051 
1052                     plainTextEdit_INPUT->setFocus();
1053                 }
1054             }
1055         }
1056     }
1057     else if (e->type() == QEvent::MouseMove && (static_cast<QWidget*>(obj) == textEdit_CHAT->viewport())){
1058         if (!textEdit_CHAT->anchorAt(textEdit_CHAT->mapFromGlobal(QCursor::pos())).isEmpty())
1059             textEdit_CHAT->viewport()->setCursor(Qt::PointingHandCursor);
1060         else
1061             textEdit_CHAT->viewport()->setCursor(Qt::IBeamCursor);
1062     }
1063 
1064     return QWidget::eventFilter(obj, e);
1065 }
1066 
closeEvent(QCloseEvent * e)1067 void HubFrame::closeEvent(QCloseEvent *e){
1068     Q_D(HubFrame);
1069 
1070     blockSignals(true);
1071 
1072     QObject::disconnect(this, NULL, this, NULL);
1073 
1074     FavoriteManager::getInstance()->removeListener(this);
1075 
1076     HubManager::getInstance()->unregisterHubUrl(_q(d->client->getHubUrl()));
1077 
1078     d->client->removeListener(this);
1079     d->client->disconnect(true);
1080     ClientManager::getInstance()->putClient(d->client);
1081 
1082     save();
1083 
1084     for (const auto &it : d->pm){
1085         PMWindow *w = const_cast<PMWindow*>(it);
1086 
1087         disconnect(w, SIGNAL(privateMessageClosed(QString)), this, SLOT(slotPMClosed(QString)));
1088 
1089         ArenaWidgetManager::getInstance()->rem(w);
1090     }
1091 
1092     d->pm.clear();
1093 
1094     for (const auto &r : d->shell_list){
1095         r->cancel();
1096         r->exit(0);
1097 
1098         r->wait(100);
1099 
1100         if (r->isRunning())
1101             r->terminate();
1102 
1103         delete r;
1104     }
1105 
1106     if (isVisible())
1107         HubManager::getInstance()->setActiveHub(NULL);
1108 
1109     setAttribute(Qt::WA_DeleteOnClose);
1110 
1111     e->accept();
1112 
1113     blockSignals(false);
1114     {
1115         emit closeRequest();
1116     }
1117     blockSignals(true);
1118 }
1119 
showEvent(QShowEvent * e)1120 void HubFrame::showEvent(QShowEvent *e){
1121     Q_D(HubFrame);
1122 
1123     e->accept();
1124 
1125     d->drawLine = false;
1126 
1127     HubManager::getInstance()->setActiveHub(this);
1128 
1129     d->hasMessages = false;
1130     d->hasHighlightMessages = false;
1131 
1132     MainWindow::getInstance()->redrawToolPanel();
1133 }
1134 
hideEvent(QHideEvent * e)1135 void HubFrame::hideEvent(QHideEvent *e){
1136     Q_D(HubFrame);
1137 
1138     e->accept();
1139 
1140     d->drawLine = true;
1141 
1142     if (!isVisible())
1143         HubManager::getInstance()->setActiveHub(NULL);
1144 }
1145 
init()1146 void HubFrame::init(){
1147     Q_D(HubFrame);
1148 
1149     d->model = new UserListModel(this);
1150     d->proxy = NULL;
1151 
1152     treeView_USERS->setModel(d->model);
1153     treeView_USERS->setSortingEnabled(true);
1154     treeView_USERS->setItemsExpandable(false);
1155     treeView_USERS->setUniformRowHeights(true);
1156     treeView_USERS->setContextMenuPolicy(Qt::CustomContextMenu);
1157     treeView_USERS->header()->setContextMenuPolicy(Qt::CustomContextMenu);
1158     treeView_USERS->viewport()->installEventFilter(this);
1159 
1160     installEventFilter(this);
1161     lineEdit_FILTER->installEventFilter(this);
1162     lineEdit_FIND->installEventFilter(this);
1163 
1164     textEdit_CHAT->document()->setMaximumBlockCount(WIGET(WI_CHAT_MAXPARAGRAPHS));
1165     textEdit_CHAT->setContextMenuPolicy(Qt::CustomContextMenu);
1166     textEdit_CHAT->setReadOnly(true);
1167     textEdit_CHAT->setAutoFormatting(QTextEdit::AutoNone);
1168     textEdit_CHAT->viewport()->installEventFilter(this);//QTextEdit don't receive all mouse events
1169     textEdit_CHAT->setMouseTracking(true);
1170 
1171     if (WBGET(WB_APP_ENABLE_EMOTICON) && EmoticonFactory::getInstance())
1172         EmoticonFactory::getInstance()->addEmoticons(textEdit_CHAT->document());
1173 
1174     frame->setVisible(false);
1175 
1176     for (int i = 0; i < d->model->columnCount(); i++)
1177         comboBox_COLUMNS->addItem(d->model->headerData(i, Qt::Horizontal, Qt::DisplayRole).toString());
1178 
1179     toolButton_SMILE->setVisible(WBGET(WB_APP_ENABLE_EMOTICON) && EmoticonFactory::getInstance());
1180     toolButton_SMILE->setContextMenuPolicy(Qt::CustomContextMenu);
1181     toolButton_SMILE->setIcon(WICON(WulforUtil::eiEMOTICON));
1182 
1183     toolButton_HIDE->setIcon(WICON(WulforUtil::eiEDITDELETE));
1184 
1185     frame_SMILES->setLayout(new FlowLayout(frame_SMILES));
1186     frame_SMILES->setVisible(false);
1187 
1188     QSize sz;
1189     Q_UNUSED(sz);
1190 
1191     if (EmoticonFactory::getInstance())
1192         EmoticonFactory::getInstance()->fillLayout(frame_SMILES->layout(), sz);
1193 
1194     for (const auto &l : frame_SMILES->findChildren<EmoticonLabel*>())
1195         connect(l, SIGNAL(clicked()), this, SLOT(slotSmileClicked()));
1196 
1197     connect(this, SIGNAL(coreConnecting(QString)), this, SLOT(addStatus(QString)), Qt::QueuedConnection);
1198     connect(this, SIGNAL(coreConnected(QString)), this, SLOT(addStatus(QString)), Qt::QueuedConnection);
1199     connect(this, SIGNAL(coreUserUpdated(dcpp::UserPtr,dcpp::Identity)), this, SLOT(userUpdated(dcpp::UserPtr,dcpp::Identity)), Qt::QueuedConnection);
1200     connect(this, SIGNAL(coreUserRemoved(dcpp::UserPtr,dcpp::Identity)), this, SLOT(userRemoved(dcpp::UserPtr,dcpp::Identity)), Qt::QueuedConnection);
1201     connect(this, SIGNAL(coreStatusMsg(QString)), this, SLOT(addStatus(QString)), Qt::QueuedConnection);
1202     connect(this, SIGNAL(coreFollow(QString)), this, SLOT(follow(QString)), Qt::QueuedConnection);
1203     connect(this, SIGNAL(coreFailed()), this, SLOT(clearUsers()), Qt::QueuedConnection);
1204     connect(this, SIGNAL(corePassword()), this, SLOT(getPassword()), Qt::QueuedConnection);
1205     connect(this, SIGNAL(coreMessage(VarMap)), this, SLOT(newMsg(VarMap)), Qt::QueuedConnection);
1206     connect(this, SIGNAL(corePrivateMsg(VarMap)), this, SLOT(newPm(VarMap)), Qt::QueuedConnection);
1207     connect(this, SIGNAL(coreHubUpdated()), MainWindow::getInstance(), SLOT(redrawToolPanel()), Qt::QueuedConnection);
1208     connect(this, SIGNAL(coreFavoriteUserAdded(QString)), this, SLOT(changeFavStatus(QString)), Qt::QueuedConnection);
1209     connect(this, SIGNAL(coreFavoriteUserRemoved(QString)), this, SLOT(changeFavStatus(QString)), Qt::QueuedConnection);
1210 
1211     connect(label_LAST_STATUS, SIGNAL(linkActivated(QString)), this, SLOT(slotStatusLinkOpen(QString)));
1212     connect(treeView_USERS, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(slotUserListMenu(QPoint)));
1213     connect(treeView_USERS->header(), SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(slotHeaderMenu(QPoint)));
1214     connect(GlobalTimer::getInstance(), SIGNAL(second()), this, SLOT(slotUsersUpdated()));
1215     connect(textEdit_CHAT, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(slotChatMenu(QPoint)));
1216     connect(toolButton_BACK, SIGNAL(clicked()), this, SLOT(slotFindBackward()));
1217     connect(toolButton_FORWARD, SIGNAL(clicked()), this, SLOT(slotFindForward()));
1218     connect(toolButton_HIDE, SIGNAL(clicked()), this, SLOT(slotHideFindFrame()));
1219     connect(lineEdit_FIND, SIGNAL(textEdited(QString)), this, SLOT(slotFindTextEdited(QString)));
1220     connect(lineEdit_FILTER, SIGNAL(textChanged(QString)), this, SLOT(slotFilterTextChanged()));
1221     connect(comboBox_COLUMNS, SIGNAL(activated(int)), this, SLOT(slotFilterTextChanged()));
1222     connect(toolButton_SMILE, SIGNAL(clicked()), this, SLOT(slotSmile()));
1223     connect(toolButton_SMILE, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(slotSmileContextMenu()));
1224     connect(toolButton_ALL, SIGNAL(clicked()), this, SLOT(slotFindAll()));
1225     connect(WulforSettings::getInstance(), SIGNAL(strValueChanged(QString, QString)), this, SLOT(slotSettingsChanged(QString,QString)));
1226     connect(WulforSettings::getInstance(), SIGNAL(intValueChanged(QString,int)), this, SLOT(slotBoolSettingsChanged(QString,int)));
1227 
1228 #ifdef USE_ASPELL
1229     connect(plainTextEdit_INPUT, SIGNAL(textChanged()), this, SLOT(slotInputTextChanged()));
1230     connect(plainTextEdit_INPUT, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(slotInputContextMenu()));
1231 
1232     plainTextEdit_INPUT->setContextMenuPolicy(Qt::CustomContextMenu);
1233 #endif
1234 
1235     plainTextEdit_INPUT->setWordWrapMode(QTextOption::NoWrap);
1236     plainTextEdit_INPUT->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
1237     plainTextEdit_INPUT->installEventFilter(this);
1238     plainTextEdit_INPUT->setAcceptRichText(false);
1239 
1240     textEdit_CHAT->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
1241     textEdit_CHAT->setTabStopWidth(40);
1242     updateStyles();
1243 
1244     load();
1245 
1246     d->completer = new QCompleter(this);
1247     d->completer->setMaxVisibleItems(10); // This property was introduced in Qt 4.6.
1248     plainTextEdit_INPUT->setCompleter(d->completer, d->model);
1249 
1250     slotSettingsChanged(WS_APP_EMOTICON_THEME, WSGET(WS_APP_EMOTICON_THEME));//toggle emoticon button
1251 }
1252 
initMenu()1253 void HubFrame::initMenu(){
1254     Q_D(HubFrame);
1255     WulforUtil *WU = WulforUtil::getInstance();
1256 
1257     delete d->arenaMenu;
1258 
1259     d->arenaMenu = new QMenu(tr("Hub menu"), this);
1260 
1261     QAction *reconnect = new QAction(WU->getPixmap(WulforUtil::eiRECONNECT), tr("Reconnect"), d->arenaMenu);
1262     QAction *show_wnd  = new QAction(WU->getPixmap(WulforUtil::eiCHAT), tr("Show widget"), d->arenaMenu);
1263     QAction *addToFav  = new QAction(WU->getPixmap(WulforUtil::eiFAVSERVER), tr("Add to Favorites"), d->arenaMenu);
1264     QMenu   *copyInfo  = new QMenu(tr("Copy"), d->arenaMenu);
1265     QAction *copyIP    = copyInfo->addAction(tr("Hub IP"));
1266     QAction *copyURL   = copyInfo->addAction(tr("Hub URL"));
1267     QAction *copyTitle = copyInfo->addAction(tr("Hub Title"));
1268 
1269     QAction *sep       = new QAction(d->arenaMenu);
1270     sep->setSeparator(true);
1271     QAction *close_wnd = new QAction(WU->getPixmap(WulforUtil::eiEXIT), tr("Close"), d->arenaMenu);
1272 
1273     d->arenaMenu->addActions(QList<QAction*>() << reconnect
1274                                             << show_wnd
1275                                             << addToFav
1276                          );
1277 
1278     d->arenaMenu->addMenu(copyInfo);
1279 
1280     if (d->client && d->client->isConnected()){
1281         QMenu *u_c = WulforUtil::getInstance()->buildUserCmdMenu(d->client->getHubUrl(), UserCommand::CONTEXT_HUB, d->arenaMenu);
1282 
1283         if (u_c){
1284             if (!u_c->actions().isEmpty()){
1285                 u_c->setTitle(tr("Hub Menu"));
1286 
1287                 d->arenaMenu->addMenu(u_c);
1288 
1289                 connect(u_c, SIGNAL(triggered(QAction*)), this, SLOT(slotHubMenu(QAction*)));
1290             }
1291         }
1292     }
1293 
1294     d->arenaMenu->addActions(QList<QAction*>() << sep << close_wnd);
1295 
1296     connect(reconnect,  SIGNAL(triggered()), this, SLOT(slotReconnect()));
1297     connect(show_wnd,   SIGNAL(triggered()), this, SLOT(slotShowWnd()));
1298     connect(addToFav,   SIGNAL(triggered()), this, SLOT(addAsFavorite()));
1299     connect(copyIP,     SIGNAL(triggered()), this, SLOT(slotCopyHubIP()));
1300     connect(copyTitle,  SIGNAL(triggered()), this, SLOT(slotCopyHubTitle()));
1301     connect(copyURL,    SIGNAL(triggered()), this, SLOT(slotCopyHubURL()));
1302     connect(close_wnd,  SIGNAL(triggered()), this, SLOT(slotClose()));
1303 }
1304 
1305 
save()1306 void HubFrame::save(){
1307     Q_D(HubFrame);
1308 
1309     WSSET(WS_CHAT_USERLIST_STATE, treeView_USERS->header()->saveState().toBase64());
1310     WISET(WI_CHAT_WIDTH, textEdit_CHAT->width());
1311     WISET(WI_CHAT_USERLIST_WIDTH, treeView_USERS->width());
1312     WISET(WI_CHAT_SORT_COLUMN, d->model->getSortColumn());
1313     WISET(WI_CHAT_SORT_ORDER, WulforUtil::getInstance()->sortOrderToInt(d->model->getSortOrder()));
1314     WSSET("hubframe/chat-background-color", textEdit_CHAT->palette().color(QPalette::Active, QPalette::Base).name());
1315 }
1316 
load()1317 void HubFrame::load(){
1318     int w_chat = WIGET(WI_CHAT_WIDTH), w_ulist = WIGET(WI_CHAT_USERLIST_WIDTH);
1319 
1320     QString ustate = WSGET(WS_CHAT_USERLIST_STATE);
1321 
1322     if (!ustate.isEmpty())
1323         treeView_USERS->header()->restoreState(QByteArray::fromBase64(ustate.toUtf8()));
1324 
1325     if (w_chat >= 0 && w_ulist >= 0){
1326         QList<int> frames;
1327 
1328         frames << w_chat << w_ulist;
1329 
1330         splitter_2->setSizes(frames);
1331     }
1332 
1333     treeView_USERS->sortByColumn(WIGET(WI_CHAT_SORT_COLUMN), WulforUtil::getInstance()->intToSortOrder(WIGET(WI_CHAT_SORT_ORDER)));
1334 
1335     reloadSomeSettings();
1336 }
1337 
reloadSomeSettings()1338 void HubFrame::reloadSomeSettings(){
1339     label_USERSTATE->setVisible(WBGET(WB_USERS_STATISTICS));
1340 
1341     label_LAST_STATUS->setVisible(WBGET(WB_LAST_STATUS));
1342 
1343     if (!WSGET("hubframe/chat-background-color", "").isEmpty()){
1344         QPalette p = textEdit_CHAT->palette();
1345         QColor clr = p.color(QPalette::Active, QPalette::Base);
1346 
1347         clr.setNamedColor(WSGET("hubframe/chat-background-color"));
1348 
1349         if (clr.isValid()){
1350             p.setColor(QPalette::Base, clr);
1351 
1352             textEdit_CHAT->setPalette(p);
1353         }
1354     }
1355 }
1356 
getWidget()1357 QWidget *HubFrame::getWidget(){
1358     return this;
1359 }
1360 
getArenaTitle()1361 QString HubFrame::getArenaTitle(){
1362     Q_D(HubFrame);
1363     QString ret = tr("Not connected");
1364 
1365     if (d->client && d->client->isConnected()){
1366         ret  = QString("%1 - %2 [%3]").arg(QString(d->client->getHubName().c_str()))
1367                                       .arg(QString(d->client->getHubDescription().c_str()))
1368                                       .arg(QString(d->client->getIp().c_str()));
1369         QString prefix = QString("[+%1] ").arg(d->client->isSecure()? ("S") : (d->client->isTrusted()? ("T"): ("")));
1370 
1371         ret.prepend(prefix);
1372     }
1373     else if (d->client){
1374         ret = QString("[-] %1").arg(d->client->getHubUrl().c_str());
1375     }
1376 
1377     return ret;
1378 }
1379 
getCIDforNick(QString nick)1380 QString HubFrame::getCIDforNick(QString nick){
1381     Q_D(HubFrame);
1382 
1383     return d->model->CIDforNick(nick, _q(d->client->getHubUrl()));
1384 }
1385 
getArenaShortTitle()1386 QString HubFrame::getArenaShortTitle(){
1387     Q_D(HubFrame);
1388     QString ret = tr("Not connected");
1389 
1390     if (d->client && d->client->isConnected()){
1391         ret = QString("[+] %1").arg(QString(d->client->getHubName().c_str()));
1392     }
1393     else if (d->client){
1394         ret = QString("[-] %1").arg(d->client->getHubUrl().c_str());
1395     }
1396 
1397     return ret;
1398 }
1399 
getMenu()1400 QMenu *HubFrame::getMenu(){
1401     initMenu();
1402 
1403     Q_D(HubFrame);
1404 
1405     return d->arenaMenu;
1406 }
1407 
getPixmap()1408 const QPixmap &HubFrame::getPixmap(){
1409     Q_D(HubFrame);
1410 
1411     if (d->hasHighlightMessages)
1412         return WICON(WulforUtil::eiMESSAGE);
1413     else if (d->hasMessages)
1414         return WICON(WulforUtil::eiHUBMSG);
1415     else
1416         return WICON(WulforUtil::eiSERVER);
1417 }
1418 
clearChat()1419 void HubFrame::clearChat(){
1420     textEdit_CHAT->setHtml("");
1421     addStatus(tr("Chat cleared."));
1422 
1423     updateStyles();
1424 
1425     if (WBGET(WB_APP_ENABLE_EMOTICON) && EmoticonFactory::getInstance())
1426         EmoticonFactory::getInstance()->addEmoticons(textEdit_CHAT->document());
1427 }
1428 
disableChat()1429 void HubFrame::disableChat(){
1430     Q_D(HubFrame);
1431 
1432     if (!d->chatDisabled){
1433         addStatus(tr("Chat disabled."));
1434 
1435         d->chatDisabled = true;
1436     }
1437     else{
1438         d->chatDisabled = false;
1439 
1440         addStatus(tr("Chat enabled."));
1441     }
1442 
1443     plainTextEdit_INPUT->setEnabled(!d->chatDisabled);
1444     frame_INPUT->setVisible(!d->chatDisabled);
1445 }
1446 
getStatistic(quint64 & users,quint64 & share) const1447 void HubFrame::getStatistic(quint64 &users, quint64 &share) const{
1448     Q_D(const HubFrame);
1449 
1450     if (d->model)
1451         users = d->model->rowCount();
1452 
1453     share = d->total_shared;
1454 }
1455 
isConnected() const1456 bool HubFrame::isConnected() const {
1457     Q_D(const HubFrame);
1458 
1459     return (d->client? d->client->isConnected() : false);
1460 }
1461 
getUserInfo(UserListItem * item)1462 QString HubFrame::getUserInfo(UserListItem *item){
1463     Q_D(HubFrame);
1464     QString ttip = "";
1465 
1466     ttip += d->model->headerData(COLUMN_NICK, Qt::Horizontal, Qt::DisplayRole).toString() + ": " + item->getNick() + "\n";
1467     ttip += d->model->headerData(COLUMN_COMMENT, Qt::Horizontal, Qt::DisplayRole).toString() + ": " + item->getComment() + "\n";
1468     ttip += d->model->headerData(COLUMN_EMAIL, Qt::Horizontal, Qt::DisplayRole).toString() + ": " + item->getEmail() + "\n";
1469     ttip += d->model->headerData(COLUMN_IP, Qt::Horizontal, Qt::DisplayRole).toString() + ": " + item->getIP() + "\n";
1470     ttip += d->model->headerData(COLUMN_SHARE, Qt::Horizontal, Qt::DisplayRole).toString() + ": " +
1471             WulforUtil::formatBytes(item->getShare()) + "\n";
1472     ttip += d->model->headerData(COLUMN_TAG, Qt::Horizontal, Qt::DisplayRole).toString() + ": " + item->getTag() + "\n";
1473     ttip += d->model->headerData(COLUMN_CONN, Qt::Horizontal, Qt::DisplayRole).toString() + ": " + item->getConnection() + "\n";
1474 
1475     if (item->isOP())
1476         ttip += tr("Hub role: Operator");
1477     else
1478         ttip += tr("Hub role: User");
1479 
1480     if (item->isFav())
1481         ttip += tr("\nFavorite user");
1482 
1483     return ttip;
1484 }
1485 
sendMsg(const QString & msg)1486 void HubFrame::sendMsg(const QString &msg){
1487     sendChat(msg, false, false);
1488 }
1489 
sendChat(QString msg,bool thirdPerson,bool stripNewLines)1490 void HubFrame::sendChat(QString msg, bool thirdPerson, bool stripNewLines){
1491     Q_D(HubFrame);
1492 
1493     if (!d->client || !d->client->isConnected() || msg.isEmpty() || msg.isNull())
1494         return;
1495 
1496     if (stripNewLines)
1497         msg.replace("\n", "");
1498 
1499     if (msg.trimmed().isEmpty())
1500         return;
1501 
1502     if (msg.endsWith("\n"))
1503         msg = msg.left(msg.lastIndexOf("\n"));
1504 
1505     bool script_ret = false;
1506 #ifdef LUA_SCRIPT
1507     script_ret = ((ClientScriptInstance *) (d->client))->onHubFrameEnter(d->client, msg.toStdString());
1508 #endif
1509     if (!script_ret && !parseForCmd(msg, this))
1510         d->client->hubMessage(msg.toStdString(), thirdPerson);
1511 
1512     //qDebug() << "cmd: " << cmd <<" sript_ret: " << script_ret;
1513     if (!thirdPerson){
1514         if (d->out_messages_unsent){
1515             d->out_messages.removeLast();
1516             d->out_messages_unsent = false;
1517         }
1518 
1519         d->out_messages << msg;
1520 
1521         if (d->out_messages.size() > WIGET(WI_OUT_IN_HIST))
1522             d->out_messages.removeFirst();
1523 
1524         d->out_messages_index = d->out_messages.size()-1;
1525     }
1526 }
1527 
parseForCmd(QString line,QWidget * wg)1528 bool HubFrame::parseForCmd(QString line, QWidget *wg){
1529     HubFrame *fr = qobject_cast<HubFrame *>(wg);
1530     PMWindow *pm = qobject_cast<PMWindow *>(wg);
1531     Q_D(HubFrame);
1532 
1533     QStringList list = line.split(" ", QString::SkipEmptyParts);
1534 
1535     if (list.isEmpty())
1536         return false;
1537 
1538     if (!line.startsWith("/"))
1539         return false;
1540 
1541     QString cmd = list.at(0);
1542     QString param = "";
1543     bool emptyParam = true;
1544 
1545     if (list.size() > 1){
1546         param = list.at(1);
1547         emptyParam = false;
1548     }
1549 
1550     if (cmd == "/away"){
1551         if (Util::getAway() && emptyParam){
1552             Util::setAway(false);
1553             Util::setManualAway(false);
1554 
1555             if (fr == this)
1556                 addStatus(tr("Away mode off"));
1557             else if (pm)
1558                 pm->addStatus(tr("Away mode off"));
1559         }
1560         else {
1561             Util::setAway(true);
1562             Util::setManualAway(true);
1563 
1564             if (!emptyParam){
1565                 line.remove(0, 6);
1566                 Util::setAwayMessage(line.toStdString());
1567             }
1568 
1569             if (fr == this)
1570                 addStatus(tr("Away mode on: ") + QString::fromStdString(Util::getAwayMessage()));
1571             else if (pm)
1572                 pm->addStatus(tr("Away mode on: ") + QString::fromStdString(Util::getAwayMessage()));
1573         }
1574     }
1575     else if (cmd == "/alias" && !emptyParam){
1576         QStringList lex = line.split(" ", QString::SkipEmptyParts);
1577 
1578         if (lex.size() >= 2){
1579             QString aliases = QByteArray::fromBase64(WSGET(WS_CHAT_CMD_ALIASES).toUtf8());
1580 
1581             if (lex.at(1) == "list"){
1582                 if (!aliases.isEmpty()){
1583                     if (fr == this)
1584                         addStatus("\n"+aliases);
1585                     else if (pm)
1586                         pm->addStatus("\n"+aliases);
1587                 }
1588                 else{
1589                     if (fr == this)
1590                         addStatus(tr("Aliases not found."));
1591                     else if (pm)
1592                         pm->addStatus(tr("Aliases not found."));
1593                 }
1594             }
1595             else if (lex.at(1) == "purge" && lex.size() == 3){
1596                 QString alias = lex.at(2);
1597                 QStringList alias_list = aliases.split('\n', QString::SkipEmptyParts);
1598 
1599                 for (const auto &line : alias_list){
1600                     QStringList cmds = line.split('\t', QString::SkipEmptyParts);
1601 
1602                     if (cmds.size() == 2 && alias == cmds.at(0)){
1603                         alias_list.removeAt(alias_list.indexOf(line));
1604 
1605                         QString new_aliases;
1606                         for (const auto &line : alias_list)
1607                             new_aliases += line + "\n";
1608 
1609                         WSSET(WS_CHAT_CMD_ALIASES, new_aliases.toUtf8().toBase64());
1610 
1611                         if (fr == this)
1612                             addStatus(tr("Alias removed."));
1613                         else if (pm)
1614                             pm->addStatus(tr("Alias removed."));
1615                     }
1616                 }
1617             }
1618             else if (lex.size() >= 2){
1619                 QString raw = line;
1620 
1621                 raw.remove(0, raw.indexOf(" ")+1);
1622 
1623                 if (raw.indexOf("::") <= 0){
1624                     if (fr == this)
1625                         addStatus(tr("Invalid alias syntax."));
1626                     else if (pm)
1627                         pm->addStatus(tr("Invalid alias syntax."));
1628                 }
1629                 else {
1630                     QStringList new_cmd = raw.split("::", QString::SkipEmptyParts);
1631 
1632                     if (new_cmd.size() < 2 || new_cmd.at(1).isEmpty()){
1633                         if (fr == this)
1634                             addStatus(tr("Invalid alias syntax."));
1635                         else if (pm)
1636                             pm->addStatus(tr("Invalid alias syntax."));
1637                     }
1638                     else if (!aliases.contains(new_cmd.at(0)+'\t')){
1639                         aliases += new_cmd.at(0) + '\t' +  new_cmd.at(1) + '\n';
1640 
1641                         WSSET(WS_CHAT_CMD_ALIASES, aliases.toUtf8().toBase64());
1642 
1643                         if (fr == this)
1644                             addStatus(tr("Alias %1 => %2 has been added").arg(new_cmd.at(0)).arg(new_cmd.at(1)));
1645                         else if (pm)
1646                             pm->addStatus(tr("Alias %1 => %2 has been added").arg(new_cmd.at(0)).arg(new_cmd.at(1)));
1647                     }
1648                 }
1649             }
1650         }
1651     }
1652     else if (cmd == "/kword" && !emptyParam){
1653         if (list.size() < 2 || list.size() > 3)
1654             return false;
1655 
1656         enum { List=0, Add, Remove };
1657 
1658         int kw_action = List;
1659 
1660         if (list.at(1) == QString("add"))
1661             kw_action = Add;
1662         else if (list.at(1) == QString("purge"))
1663             kw_action = Remove;
1664         else if (list.at(1) == QString("list"))
1665             kw_action = List;
1666         else {
1667             if (fr == this)
1668                 addStatus(tr("Invalid command syntax."));
1669             else if (pm)
1670                 pm->addStatus(tr("Invalid command syntax."));
1671 
1672             return false;
1673         }
1674 
1675         if (kw_action != List && list.size() != 3){
1676             if (fr == this)
1677                 addStatus(tr("Invalid command syntax."));
1678             else if (pm)
1679                 pm->addStatus(tr("Invalid command syntax."));
1680 
1681             return false;
1682         }
1683 
1684         QStringList kwords = WVGET("hubframe/chat-keywords", QStringList()).toStringList();
1685 
1686         switch (kw_action){
1687         case List:
1688             {
1689                 QString str = tr("List of keywords:\n");
1690 
1691                 for (const auto &s : kwords)
1692                     str += "\t" + s + "\n";
1693 
1694                 if (fr == this)
1695                     addStatus(str);
1696                 else if (pm)
1697                     pm->addStatus(str);;
1698 
1699                 break;
1700             }
1701         case Remove:
1702             {
1703                 QString kword = list.last();
1704 
1705                 if (kwords.contains(kword))
1706                     kwords.removeOne(kword);
1707 
1708                 break;
1709             }
1710         case Add:
1711             {
1712                 QString kword = list.last();
1713 
1714                 if (!kwords.contains(kword))
1715                     kwords.push_back(kword);
1716 
1717                 break;
1718             }
1719         default:
1720             break;
1721         }
1722 
1723         WVSET("hubframe/chat-keywords", kwords);
1724     }
1725     else if (cmd == "/ratio"){
1726         double ratio;
1727         double up   = static_cast<double>(SETTING(TOTAL_UPLOAD));
1728         double down = static_cast<double>(SETTING(TOTAL_DOWNLOAD));
1729 
1730 
1731         if (down > 0)
1732             ratio = up / down;
1733         else
1734             ratio = 0;
1735 
1736         QString line = tr("ratio: %1 (uploads: %2, downloads: %3)")
1737                           .arg(QString().setNum(ratio, 'f', 3))
1738                           .arg(WulforUtil::formatBytes(up))
1739                           .arg(WulforUtil::formatBytes(down));
1740 
1741         if (param.trimmed() == "show"){
1742             if (fr == this)
1743                 sendChat(line, true, false);
1744             else if (pm)
1745                 pm->sendMessage(line, true, false);
1746         } else if (emptyParam){
1747             if (fr == this)
1748                 addStatus(line);
1749             else if (pm)
1750                 pm->addStatus(line);
1751         }
1752     }
1753     else if (cmd == "/rebuild") {
1754         HashManager::getInstance()->rebuild();
1755     }
1756     else if (cmd == "/refresh") {
1757         ShareManager::getInstance()->setDirty();
1758         ShareManager::getInstance()->refresh(true);
1759     }
1760 #ifdef USE_ASPELL
1761     else if (cmd == "/aspell" && !emptyParam){
1762         WBSET(WB_APP_ENABLE_ASPELL, param.trimmed() == "on");
1763 
1764         if (WBGET(WB_APP_ENABLE_ASPELL) && !SpellCheck::getInstance())
1765             SpellCheck::newInstance();
1766         else if (SpellCheck::getInstance())
1767             SpellCheck::deleteInstance();
1768 
1769         if (fr == this)
1770             addStatus(tr("Aspell switched %1").arg((WBGET(WB_APP_ENABLE_ASPELL)? tr("on") : tr("off"))) );
1771         else if (pm)
1772             pm->addStatus(tr("Aspell switched %1").arg((WBGET(WB_APP_ENABLE_ASPELL)? tr("on") : tr("off"))) );
1773     }
1774 #endif
1775     else if (cmd == "/back"){
1776         Util::setAway(false);
1777 
1778         if (fr == this)
1779             addStatus(tr("Away mode off"));
1780         else if (pm)
1781             pm->addStatus(tr("Away mode off"));
1782     }
1783     else if (cmd == "/clear"){
1784         textEdit_CHAT->setHtml("");
1785 
1786         if (fr == this)
1787             addStatus(tr("Chat has been cleared"));
1788         else if (pm)
1789             pm->addStatus(tr("Chat has been cleared"));
1790 
1791     }
1792     else if (cmd == "/close"){
1793         this->close();
1794     }
1795     else if (cmd == "/fav"){
1796         addAsFavorite();
1797     }
1798     else if (cmd == "/browse" && !emptyParam){
1799         browseUserFiles(d->model->CIDforNick(param, _q(d->client->getHubUrl())), false);
1800     }
1801     else if (cmd == "/grant" && !emptyParam){
1802         grantSlot(d->model->CIDforNick(param, _q(d->client->getHubUrl())));
1803     }
1804     else if (cmd == "/magnet" && !emptyParam){
1805         WISET(WI_DEF_MAGNET_ACTION, param.toInt());
1806     }
1807     else if (cmd == "/info" && !emptyParam){
1808         UserListItem *item = d->model->itemForNick(param, _q(d->client->getHubUrl()));
1809 
1810         if (item){
1811             QString ttip = "\n" + getUserInfo(item);
1812 
1813             if (fr == this)
1814                 addStatus(ttip);
1815             else if (pm)
1816                 pm->addStatus(ttip);
1817         }
1818     }
1819     else if (cmd == "/me" && !emptyParam){
1820         if (line.endsWith("\n"))//remove extra \n char
1821             line = line.left(line.lastIndexOf("\n"));
1822 
1823         // This is temporary. It is need to check ClientManager::privateMessage(...) function
1824         // in dcpp kernel with version > 0.75. And "if (fr == this)" will not be needed.
1825         if (fr == this)
1826             line.remove(0, 4);
1827 
1828         if (fr == this)
1829             sendChat(line, true, false);
1830         else if (pm)
1831             pm->sendMessage(line, true, false);
1832     }
1833     else if (cmd == "/pm" && !emptyParam){
1834         addPM(d->model->CIDforNick(param, _q(d->client->getHubUrl())), "", false);
1835     }
1836     else if (cmd == "/help" || cmd == "/?" || cmd == "/h"){
1837         QString out = "\n";
1838 #ifdef USE_ASPELL
1839         out += tr("/aspell on/off - enable/disable spell checking\n");
1840 #endif
1841         out += tr("/alias <ALIAS_NAME>::<COMMAND> - make alias /ALIAS_NAME to /COMMAND\n");
1842         out += tr("/alias purge <ALIAS_NAME> - remove alias\n");
1843         out += tr("/alias list - list all aliases\n");
1844         out += tr("/away <message> - set away-mode on/off\n");
1845         out += tr("/back - set away-mode off\n");
1846         out += tr("/browse <nick> - browse user files\n");
1847         out += tr("/clear - clear chat window\n");
1848         out += tr("/kword add <keyword> - add user-defined keyword which will be highlighted in the chat\n");
1849         out += tr("/kword purge <keyword> - remove user-defined keyword\n");
1850         out += tr("/kword list - full list of keywords which will be highlighted in the chat\n");
1851         out += tr("/magnet - default action with magnet (0-ask, 1-search, 2-download)\n");
1852         out += tr("/close - close this hub\n");
1853         out += tr("/fav - add this hub to favorites\n");
1854         out += tr("/grant <nick> - grant extra slot to user\n");
1855         out += tr("/help, /?, /h - show this help\n");
1856         out += tr("/info <nick> - show info about user\n");
1857         out += tr("/ratio [show] - show ratio [send in chat]\n");
1858         out += tr("/rebuild - rebuild hash\n");
1859         out += tr("/refresh - update own file list\n");
1860         out += tr("/me - say a third person\n");
1861         out += tr("/pm <nick> - begin private chat with user\n");
1862         out += tr("/ws param value - set gui option param in value (without value return current value of option)\n");
1863         out += tr("/dcpps param value - set core option param in value (without value return current value of option)\n");
1864 #ifdef LUA_SCRIPT
1865         out += tr("/luafile <file> - load Lua file\n");
1866         out += tr("/lua <chunk> - execute Lua chunk\n");
1867 #endif
1868         out = out.trimmed();
1869 
1870         if (fr == this)
1871             addStatus(out);
1872         else if (pm)
1873             pm->addStatus(out);
1874     }
1875 #ifdef LUA_SCRIPT
1876         else if (cmd == "/lua" && !emptyParam) {
1877             ScriptManager::getInstance()->EvaluateChunk(Text::fromT(_tq(param)));
1878         }
1879         else if( cmd == "/luafile" && !emptyParam) {
1880             ScriptManager::getInstance()->EvaluateFile(Text::fromT(_tq(param)));
1881         }
1882 #endif
1883     else if (cmd == "/sh" && !emptyParam){
1884         if (line.endsWith("\n"))//remove extra \n char
1885             line = line.left(line.lastIndexOf("\n"));
1886 
1887         line = line.remove(0, 4);
1888 
1889         ShellCommandRunner *sh = new ShellCommandRunner(line, wg);
1890         connect(sh, SIGNAL(finished(bool,QString)), this, SLOT(slotShellFinished(bool,QString)));
1891 
1892         d->shell_list.append(sh);
1893 
1894         sh->start();
1895     }
1896     else if (cmd == "/ws" && !emptyParam){
1897         line = line.remove(0, 4);
1898         line.replace("\n", "");
1899         QString res;
1900         WSCMD(line,res);
1901         if (fr == this)
1902             addStatus(res);
1903         else if (pm)
1904             pm->addStatus(res);
1905     }
1906     else if (cmd == "/dcpps" && !emptyParam) {
1907         line = line.remove(0,7);
1908         QString out = _q(SettingsManager::getInstance()->parseCoreCmd (_tq(line)));
1909         if (fr == this)
1910             addStatus(out);
1911         else if (pm)
1912             pm->addStatus(out);
1913     }
1914     else if (!WSGET(WS_CHAT_CMD_ALIASES).isEmpty()){
1915         QString aliases = QByteArray::fromBase64(WSGET(WS_CHAT_CMD_ALIASES).toUtf8());
1916         QStringList alias_list = aliases.split('\n', QString::SkipEmptyParts);
1917         bool ok = false;
1918 
1919         for (const auto &line : alias_list){
1920             QStringList cmds = line.split('\t', QString::SkipEmptyParts);
1921 
1922             if (cmds.size() == 2 && cmd == ("/" + cmds.at(0))){
1923                 parseForCmd(cmds.at(1), wg);
1924 
1925                 ok = true;
1926             }
1927         }
1928 
1929         if (!ok)
1930             return ok;
1931     }
1932     else
1933         return false;
1934 
1935     return true;
1936 }
1937 
getHubUrl()1938 QString HubFrame::getHubUrl() {
1939     Q_D(HubFrame);
1940 
1941     if (d->client)
1942         return _q(d->client->getHubUrl());
1943 
1944     return "";
1945 }
1946 
getHubName()1947 QString HubFrame::getHubName() {
1948     Q_D(HubFrame);
1949 
1950     if (d->client)
1951         return _q(d->client->getHubName());
1952 
1953     return "";
1954 }
1955 
getMyNick()1956 QString HubFrame::getMyNick() {
1957     Q_D(HubFrame);
1958 
1959     if (d->client)
1960         return _q(d->client->getMyNick());
1961 
1962     return "";
1963 }
1964 
addStatus(QString msg)1965 void HubFrame::addStatus(QString msg){
1966     Q_D(HubFrame);
1967 
1968     if (d->chatDisabled)
1969         return;
1970 
1971     QString pure_msg;
1972     QString short_msg = "";
1973     QString status = "";
1974     QString nick    = " * ";
1975 
1976     QStringList lines = msg.split(QRegExp("[\\n\\r\\f]+"), QString::SkipEmptyParts);
1977     for (int i = 0; i < lines.size(); ++i) {
1978         if (lines.at(i).contains(QRegExp("\\w+"))) {
1979             short_msg = lines.at(i);
1980             break;
1981         }
1982     }
1983     if (short_msg.isEmpty() && !lines.isEmpty())
1984         short_msg = lines.first();
1985 
1986     pure_msg  = LinkParser::parseForLinks(msg.left(WIGET(WI_CHAT_STATUS_MSG_MAX_LEN)), false);
1987     short_msg = LinkParser::parseForLinks(short_msg, false);
1988     msg       = LinkParser::parseForLinks(msg, true);
1989 
1990     pure_msg        = "<font color=\"" + WSGET(WS_CHAT_STAT_COLOR) + "\">" + pure_msg + "</font>";
1991     short_msg       = "<font color=\"" + WSGET(WS_CHAT_STAT_COLOR) + "\">" + short_msg + "</font>";
1992     msg             = "<font color=\"" + WSGET(WS_CHAT_STAT_COLOR) + "\">" + msg + "</font>";
1993     QString time    = "";
1994 
1995     if (!WSGET(WS_CHAT_TIMESTAMP).isEmpty())
1996         time = "<font color=\"" + WSGET(WS_CHAT_TIME_COLOR)+ "\">[" + QDateTime::currentDateTime().toString(WSGET(WS_CHAT_TIMESTAMP)) + "]</font>";
1997 
1998     status   = time + "<font color=\"" + WSGET(WS_CHAT_STAT_COLOR) + "\"><b>" + nick + "</b> </font>";
1999 
2000     QRegExp rot_msg = QRegExp("is(\\s+)kicking(\\s+)(\\S+)*(\\s+)because:");
2001 
2002     bool isRotating = (msg.indexOf("is kicking because:") >= 0) || (rot_msg.indexIn(msg) >= 0);
2003 
2004     if (!(isRotating && WBGET(WB_CHAT_ROTATING_MSGS)))
2005         addOutput(status + msg);
2006 
2007     label_LAST_STATUS->setText(status + short_msg);
2008 
2009     status += pure_msg;
2010     WulforUtil::getInstance()->textToHtml(status, false);
2011 
2012     d->status_msg_history.push_back(status);
2013 
2014     if (WIGET(WI_CHAT_STATUS_HISTORY_SZ) > 0){
2015         while (d->status_msg_history.size() > WIGET(WI_CHAT_STATUS_HISTORY_SZ))
2016             d->status_msg_history.removeFirst();
2017     }
2018     else
2019         d->status_msg_history.clear();
2020 
2021     label_LAST_STATUS->setToolTip(d->status_msg_history.join("<br/>"));
2022 }
2023 
addOutput(QString msg)2024 void HubFrame::addOutput(QString msg){
2025     msg.replace("\r", "");
2026     msg = "<pre>" + msg + "</pre>";
2027     textEdit_CHAT->append(msg);
2028 }
2029 
addPM(QString cid,QString output,bool keepfocus)2030 void HubFrame::addPM(QString cid, QString output, bool keepfocus){
2031     Q_D(HubFrame);
2032     bool redirectToMainChat = WBGET("hubframe/redirect-pm-to-main-chat", true);
2033 
2034     if (!d->pm.contains(cid)){
2035         PMWindow *p = ArenaWidgetFactory().create<PMWindow, QString, QString>(cid, _q(d->client->getHubUrl()));
2036         p->textEdit_CHAT->setContextMenuPolicy(Qt::CustomContextMenu);
2037 
2038         connect(p, SIGNAL(privateMessageClosed(QString)), this, SLOT(slotPMClosed(QString)));
2039         connect(p, SIGNAL(inputTextChanged()), this, SLOT(slotInputTextChanged()));
2040         connect(p, SIGNAL(inputTextMenu()), this, SLOT(slotInputContextMenu()));
2041         connect(p->textEdit_CHAT, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(slotChatMenu(QPoint)));
2042 
2043         p->setCompleter(d->completer, d->model);
2044         p->addOutput(output);
2045         p->setAttribute(Qt::WA_DeleteOnClose);
2046 
2047         if (!(keepfocus && WBGET(WB_CHAT_KEEPFOCUS))){
2048             ArenaWidgetManager::getInstance()->activate(p);
2049 
2050             p->requestFocus();
2051         }
2052 
2053         d->pm.insert(cid, p);
2054 
2055         if (!p->isVisible() && redirectToMainChat)
2056             addOutput("<b>PM: </b>" + output);
2057     }
2058     else{
2059         auto it = d->pm.find(cid);
2060 
2061         if (output.indexOf(_q(d->client->getMyNick())) >= 0)
2062             it.value()->setHasHighlightMessages(true);
2063 
2064         it.value()->addOutput(output);
2065 
2066         if (!(keepfocus && WBGET(WB_CHAT_KEEPFOCUS))){
2067             ArenaWidgetManager::getInstance()->activate(it.value());
2068 
2069             it.value()->requestFocus();
2070         }
2071 
2072         if (! it.value()->isVisible() && redirectToMainChat)
2073             addOutput("<b>PM: </b>" + output);
2074     }
2075 }
2076 
isOP(const QString & nick)2077 bool HubFrame::isOP(const QString& nick) {
2078     Q_D(HubFrame);
2079 
2080     UserListItem *item = d->model->itemForNick(nick, _q(d->client->getHubUrl()));
2081 
2082     return (item? item->isOP() : false);
2083 }
2084 
2085 
userUpdated(const UserPtr & user,const dcpp::Identity & id)2086 void HubFrame::userUpdated(const UserPtr &user, const dcpp::Identity &id){
2087     Q_D(HubFrame);
2088 
2089     static WulforSettings *WS       = WulforSettings::getInstance();
2090     static bool showFavJoinsOnly    = WS->getBool(WB_CHAT_SHOW_JOINS_FAV);
2091     static bool showJoins           = WS->getBool(WB_CHAT_SHOW_JOINS);
2092     const  bool isFavorite          = FavoriteManager::getInstance()->isFavoriteUser(user);
2093 
2094     if (!d->model)
2095         return;
2096 
2097     UserListItem *item = d->model->itemForPtr(user);
2098 
2099     QString cid = _q(user->getCID().toBase32());
2100 
2101     if (item){
2102         d->total_shared -= item->getShare();
2103 
2104         d->model->updateUser(item, id, cid, isFavorite);
2105     }
2106     else{
2107         item = d->model->addUser(user, id, cid, isFavorite);
2108 
2109         QString nick = item->getNick();
2110 
2111         if (showJoins){
2112             do {
2113                 if (showFavJoinsOnly && !isFavorite)
2114                     break;
2115 
2116                 addStatus(nick + tr(" joins the chat"));
2117             } while (0);
2118         }
2119 
2120         if (isFavorite)
2121             Notification::getInstance()->showMessage(Notification::FAVORITE, tr("Favorites"), tr("%1 is now online").arg(nick));
2122 
2123         if (d->pm.contains(nick)){
2124             PMWindow *wnd = d->pm[nick];
2125 
2126             wnd->cid = cid;
2127             wnd->plainTextEdit_INPUT->setEnabled(true);
2128             wnd->hubUrl = _q(d->client->getHubUrl());
2129 
2130             d->pm.insert(cid, wnd);
2131 
2132             d->pm.remove(nick);
2133 
2134             pmUserEvent(cid, tr("User online."));
2135         }
2136     }
2137 
2138     d->total_shared += qlonglong(id.getBytesShared());
2139 }
2140 
userRemoved(const UserPtr & user,const dcpp::Identity & id)2141 void HubFrame::userRemoved(const UserPtr &user, const dcpp::Identity &id){
2142     Q_D(HubFrame);
2143 
2144     UserListItem *item = d->model->itemForPtr(user);
2145 
2146     if (!item)
2147         return;
2148 
2149     d->total_shared -= item->getShare();
2150 
2151     QString cid = item->getCID();
2152     QString nick = item->getNick();
2153 
2154     if (d->pm.contains(cid)){
2155         pmUserOffline(cid);
2156 
2157         PMWindow *pmw = d->pm[cid];
2158 
2159         d->pm.insert(nick, pmw);
2160 
2161         pmw->cid = nick;
2162         pmw->plainTextEdit_INPUT->setEnabled(false);//we need interface function
2163 
2164         d->pm.remove(cid);
2165     }
2166 
2167     if (WulforSettings::getInstance()->getBool(WB_CHAT_SHOW_JOINS)){
2168         do {
2169             if (WulforSettings::getInstance()->getBool(WB_CHAT_SHOW_JOINS_FAV) &&
2170                 !FavoriteManager::getInstance()->isFavoriteUser(user))
2171                 break;
2172 
2173             addStatus(nick + tr(" left the chat"));
2174         } while (0);
2175     }
2176 
2177     if (FavoriteManager::getInstance()->isFavoriteUser(user))
2178         Notification::getInstance()->showMessage(Notification::FAVORITE, tr("Favorites"), tr("%1 is now offline").arg(nick));
2179 
2180     d->model->removeUser(user);
2181 }
2182 
2183 
getParams(HubFrame::VarMap & map,const Identity & id)2184 void HubFrame::getParams(HubFrame::VarMap &map, const Identity &id){
2185     map["NICK"] = _q(id.getNick());
2186     map["SHARE"] = qlonglong(id.getBytesShared());
2187     map["COMM"] = _q(id.getDescription());
2188     map["TAG"] = _q(id.getTag());
2189     map["CONN"] = _q(id.getConnection());
2190     map["IP"] = _q(id.getIp());
2191     map["EMAIL"] = _q(id.getEmail());
2192     map["ISOP"] = id.isOp();
2193     map["SPEED"] = QString::fromStdString(id.getConnection());
2194     map["AWAY"] = id.isAway();
2195     map["CID"] = _q(id.getUser()->getCID().toBase32());
2196 }
2197 
browseUserFiles(const QString & id,bool match)2198 void HubFrame::browseUserFiles(const QString& id, bool match){
2199     string message;
2200     string cid = id.toStdString();
2201 
2202     if (!cid.empty()){
2203         try{
2204             UserPtr user = ClientManager::getInstance()->findUser(CID(cid));
2205 
2206             if (user){
2207                 Q_D(HubFrame);
2208 
2209                 if (user == ClientManager::getInstance()->getMe())
2210                     MainWindow::getInstance()->browseOwnFiles();
2211                 else if (match)
2212                     QueueManager::getInstance()->addList(HintedUser(user, d->client->getHubUrl()), QueueItem::FLAG_MATCH_QUEUE, "");
2213                 else
2214                     QueueManager::getInstance()->addList(HintedUser(user, d->client->getHubUrl()), QueueItem::FLAG_CLIENT_VIEW, "");
2215             }
2216             else {
2217                 message = QString(tr("User not found")).toStdString();
2218             }
2219         }
2220         catch (const Exception &e){
2221             message = e.getError();
2222 
2223             LogManager::getInstance()->message(message);
2224         }
2225     }
2226 }
2227 
grantSlot(const QString & id)2228 void HubFrame::grantSlot(const QString& id){
2229     Q_D(HubFrame);
2230 
2231     QString message = tr("User not found");
2232 
2233     if (!id.isEmpty()){
2234         UserPtr user = ClientManager::getInstance()->findUser(CID(id.toStdString()));
2235 
2236         if (user){
2237             UploadManager::getInstance()->reserveSlot(HintedUser(user, d->client->getHubUrl()));
2238             message = tr("Slot granted to ") + WulforUtil::getInstance()->getNicks(user->getCID(), _q(d->client->getHubUrl()));
2239         }
2240     }
2241 
2242     MainWindow::getInstance()->setStatusMessage(message);
2243 }
2244 
addUserToFav(const QString & id)2245 void HubFrame::addUserToFav(const QString& id){
2246     if (id.isEmpty())
2247         return;
2248 
2249     string cid = id.toStdString();
2250 
2251     UserPtr user = ClientManager::getInstance()->findUser(CID(cid));
2252 
2253     if (user){
2254         if (user != ClientManager::getInstance()->getMe() && !FavoriteManager::getInstance()->isFavoriteUser(user))
2255             FavoriteManager::getInstance()->addFavoriteUser(user);
2256     }
2257 }
2258 
delUserFromFav(const QString & id)2259 void HubFrame::delUserFromFav(const QString& id){
2260     if (id.isEmpty())
2261         return;
2262 
2263     string cid = id.toStdString();
2264 
2265     UserPtr user = ClientManager::getInstance()->findUser(CID(cid));
2266 
2267     if (user){
2268         if (user != ClientManager::getInstance()->getMe() && FavoriteManager::getInstance()->isFavoriteUser(user))
2269             FavoriteManager::getInstance()->removeFavoriteUser(user);
2270     }
2271 }
2272 
changeFavStatus(const QString & id)2273 void HubFrame::changeFavStatus(const QString &id) {
2274     if (id.isEmpty())
2275         return;
2276 
2277     UserPtr user = ClientManager::getInstance()->findUser(CID(id.toStdString()));
2278 
2279     if (user) {
2280         Q_D(HubFrame);
2281 
2282         UserListItem *item = NULL;
2283 
2284         if (d->model)
2285             item = d->model->itemForPtr(user);
2286 
2287         bool bFav = FavoriteManager::getInstance()->isFavoriteUser(user);
2288 
2289         if (item) {
2290             QModelIndex ixb = d->model->index(item->row(), COLUMN_NICK);
2291             QModelIndex ixe = d->model->index(item->row(), COLUMN_EMAIL);
2292 
2293             d->model->repaintData(ixb, ixe);
2294         }
2295 
2296         QString message = WulforUtil::getInstance()->getNicks(id, _q(d->client->getHubUrl())) +
2297                 (bFav ? tr(" has been added to favorites.") : tr(" has been removed from favorites."));
2298 
2299         MainWindow::getInstance()->setStatusMessage(message);
2300     }
2301 }
2302 
delUserFromQueue(const QString & id)2303 void HubFrame::delUserFromQueue(const QString& id){
2304     if (!id.isEmpty()){
2305         UserPtr user = ClientManager::getInstance()->findUser(CID(id.toStdString()));
2306 
2307         if (user)
2308             QueueManager::getInstance()->removeSource(user, QueueItem::Source::FLAG_REMOVED);
2309     }
2310 }
2311 
addAsFavorite()2312 void HubFrame::addAsFavorite(){
2313     Q_D(HubFrame);
2314     FavoriteHubEntry *existingHub = FavoriteManager::getInstance()->getFavoriteHubEntry(d->client->getHubUrl());
2315 
2316     if (!existingHub){
2317         FavoriteHubEntry aEntry;
2318 
2319         aEntry.setServer(d->client->getHubUrl());
2320         aEntry.setName(d->client->getHubName());
2321         aEntry.setDescription(d->client->getHubDescription());
2322         aEntry.setConnect(false);
2323         aEntry.setNick(d->client->getMyNick());
2324         aEntry.setEncoding(d->client->getEncoding());
2325 
2326         FavoriteManager::getInstance()->addFavorite(aEntry);
2327         FavoriteManager::getInstance()->save();
2328 
2329         addStatus(tr("Favorite hub added."));
2330     }
2331     else{
2332         addStatus(tr("Favorite hub already exists."));
2333     }
2334 }
2335 
disablePrivateMessages(bool disable)2336 void HubFrame::disablePrivateMessages(bool disable) {
2337     if (disable)
2338         disconnect(this, SIGNAL(corePrivateMsg(VarMap)), this, SLOT(newPm(VarMap)));
2339     else
2340         connect(this, SIGNAL(corePrivateMsg(VarMap)), this, SLOT(newPm(VarMap)), Qt::QueuedConnection);
2341 }
2342 
2343 
newMsg(const VarMap & map)2344 void HubFrame::newMsg(const VarMap &map){
2345     Q_D(HubFrame);
2346     QString output = "";
2347 
2348     QString nick = map["NICK"].toString();
2349     QString message = map["MSG"].toString();
2350     QString time = "<font color=\"" + WSGET(WS_CHAT_TIME_COLOR)+ "\">[" + map["TIME"].toString() + "]</font>";;
2351     QString color = map["CLR"].toString();
2352     QString msg_color = WS_CHAT_MSG_COLOR;
2353     QString trigger = "";
2354 
2355     const QStringList &kwords = WVGET("hubframe/chat-keywords", QStringList()).toStringList();
2356 
2357     for (const auto &word : kwords){
2358         if (message.contains(word, Qt::CaseInsensitive)){
2359             msg_color = WS_CHAT_SAY_NICK;
2360             trigger = word;
2361 
2362             break;
2363         }
2364     }
2365 
2366     if (message.indexOf(_q(d->client->getMyNick())) >= 0){
2367         msg_color = WS_CHAT_SAY_NICK;
2368         trigger = _q(d->client->getMyNick());
2369 
2370         Notification::getInstance()->showMessage(Notification::NICKSAY, getArenaTitle().left(20), nick + ": " + message);
2371     }
2372 
2373     emit new_msg(map);
2374 
2375     if (msg_color == WS_CHAT_SAY_NICK){
2376         VarMap tmap = map;
2377         tmap["TRIGGER"] = trigger;
2378 
2379         emit highlighted(tmap);
2380     }
2381 
2382     bool third = map["3RD"].toBool();
2383 
2384     QString nicktoout = third? ("* " + nick + " ") : ("<" + nick + "> ");
2385 
2386     message = LinkParser::parseForLinks(message, true);
2387 
2388     WulforUtil::getInstance()->textToHtml(nicktoout, true);
2389 
2390     message = "<font color=\"" + WSGET(msg_color) + "\">" + message + "</font>";
2391 
2392     output  += time;
2393     string info= Util::formatAdditionalInfo(map["I4"].toString().toStdString(),BOOLSETTING(USE_IP),BOOLSETTING(GET_USER_COUNTRY));
2394 
2395     if (!info.empty())
2396         output  += " <font color=\"" + WSGET(WS_CHAT_TIME_COLOR)+ "\">" + _q(info) + "</font>";
2397 
2398     output  += QString(" <a style=\"text-decoration:none\" href=\"user://%1\"><font color=\"%2\"><b>%3</b></font></a>")
2399                .arg(nicktoout).arg(WSGET(color)).arg(nicktoout.replace("\"", "&quot;"));
2400     output  += message;
2401 
2402     if (!isVisible()){
2403         if (msg_color == WS_CHAT_SAY_NICK)
2404             d->hasHighlightMessages = true;
2405 
2406         d->hasMessages = true;
2407 
2408         MainWindow::getInstance()->redrawToolPanel();
2409     }
2410 
2411     QTextDocument *chatDoc = textEdit_CHAT->document();
2412 
2413     if (d->drawLine && WBGET("hubframe/unreaden-draw-line", true)){
2414         QString hr = "<hr />";
2415 
2416         int scrollbarValue = textEdit_CHAT->verticalScrollBar()->value();
2417 
2418         for (QTextBlock it = chatDoc->begin(); it != chatDoc->end(); it = it.next()){
2419             if (it.userState() == 1){
2420                 if (it.text().isEmpty()){ // additional check that it is not message
2421                     QTextCursor c(it);
2422                     c.select(QTextCursor::BlockUnderCursor);
2423                     c.deleteChar(); // delete string with horizontal line
2424 
2425                     if (scrollbarValue > textEdit_CHAT->verticalScrollBar()->maximum())
2426                         scrollbarValue = textEdit_CHAT->verticalScrollBar()->maximum();
2427 
2428                     textEdit_CHAT->verticalScrollBar()->setValue(scrollbarValue);
2429 
2430                     break;
2431                 }
2432             }
2433         }
2434 
2435         d->drawLine = false;
2436 
2437         chatDoc->lastBlock().setUserState(0); // add label for the last of the old messages
2438 
2439         output.prepend(hr);
2440 
2441         addOutput(output);
2442 
2443         for (QTextBlock itu = chatDoc->lastBlock(); itu != chatDoc->begin(); itu = itu.previous()){
2444             if (!itu.userData())
2445                 itu.setUserData(new UserListUserData(nick));
2446             else
2447                 break;
2448         }
2449 
2450         for (QTextBlock it = chatDoc->begin(); it != chatDoc->end(); it = it.next()){
2451             if(!it.userState()){
2452                 it.setUserState(-1); // delete label for the last of the old messages
2453 
2454                 if (it.blockNumber() < chatDoc->blockCount()-3){
2455                     it = it.next().next();
2456                     it.setUserState(1); // add label for string with horizontal line
2457 
2458                     it = it.previous();
2459                     if (it.text().isEmpty()){ // additional check that it is not message
2460                         QTextCursor c(it);
2461                         c.select(QTextCursor::BlockUnderCursor);
2462                         c.deleteChar(); // delete empty string above horizontal line
2463                     }
2464                 }
2465 
2466                 break;
2467             }
2468         }
2469 
2470         return;
2471     }
2472 
2473     addOutput(output);
2474 
2475     for (QTextBlock itu = chatDoc->lastBlock(); itu != chatDoc->begin(); itu = itu.previous()){
2476         if (!itu.userData())
2477             itu.setUserData(new UserListUserData(nick));
2478         else
2479             break;
2480     }
2481 
2482 }
2483 
newPm(const VarMap & map)2484 void HubFrame::newPm(const VarMap &map){
2485     Q_D(HubFrame);
2486     QString nick = map["NICK"].toString();
2487     QString message = map["MSG"].toString();
2488     QString time    = "<font color=\"" + WSGET(WS_CHAT_TIME_COLOR)+ "\">[" + map["TIME"].toString() + "]</font>";
2489     QString color = map["CLR"].toString();
2490     QString full_message = "";
2491 
2492     if (nick != _q(d->client->getMyNick())){
2493         bool show_msg = false;
2494 
2495         if (!d->pm.contains(map["CID"].toString()))
2496             show_msg = true;
2497         else
2498             show_msg = (!d->pm[map["CID"].toString()]->isVisible() || WBGET("notification/play-sound-with-active-pm", true));
2499 
2500         if (show_msg)
2501             Notification::getInstance()->showMessage(Notification::PM, nick, message);
2502     }
2503 
2504     bool third = map["3RD"].toBool();
2505 
2506     if (message.startsWith("/me ")){
2507         message.remove(0, 4);
2508         third = true;
2509     }
2510 
2511     nick = third? ("* " + nick + " ") : ("<" + nick + "> ");
2512 
2513     message = LinkParser::parseForLinks(message, true);
2514 
2515     WulforUtil::getInstance()->textToHtml(nick, true);
2516 
2517     message       = "<font color=\"" + WSGET(WS_CHAT_MSG_COLOR) + "\">" + message + "</font>";
2518     full_message  += time;
2519     string info= Util::formatAdditionalInfo(map["I4"].toString().toStdString(),BOOLSETTING(USE_IP),BOOLSETTING(GET_USER_COUNTRY));
2520 
2521     if (!info.empty())
2522         full_message += " <font color=\"" + WSGET(WS_CHAT_TIME_COLOR)+ "\">" + _q(info) + "</font>";
2523 
2524     full_message  += QString(" <a style=\"text-decoration:none\" href=\"user://%1\"><font color=\"%2\"><b>%3</b></font></a>")
2525                      .arg(nick).arg(WSGET(color)).arg(nick.replace("\"", "&quot;"));
2526     full_message  += message;
2527 
2528     WulforUtil::getInstance()->textToHtml(full_message, false);
2529 
2530     addPM(map["CID"].toString(), full_message);
2531 }
2532 
createPMWindow(const QString & nick)2533 void HubFrame::createPMWindow(const QString &nick){
2534     Q_D(HubFrame);
2535     createPMWindow(CID(_tq(d->model->CIDforNick(nick, _q(d->client->getHubUrl())))));
2536 }
2537 
createPMWindow(const dcpp::CID & cid)2538 void HubFrame::createPMWindow(const dcpp::CID &cid){
2539     addPM(_q(cid.toBase32()), "");
2540 }
2541 
hasCID(const dcpp::CID & cid,const QString & nick)2542 bool HubFrame::hasCID(const dcpp::CID &cid, const QString &nick){
2543     Q_D(HubFrame);
2544     return (d->model->CIDforNick(nick, _q(d->client->getHubUrl())) == _q(cid.toBase32()));
2545 }
2546 
clearUsers()2547 void HubFrame::clearUsers(){
2548     Q_D(HubFrame);
2549 
2550     if (d->model){
2551         d->model->blockSignals(true);
2552         d->model->clear();
2553         d-> model->blockSignals(false);
2554         treeView_USERS->setModel(d->model);
2555     }
2556 
2557     d->total_shared = 0;
2558 
2559     treeView_USERS->repaint();
2560 
2561     slotUsersUpdated();
2562 
2563     d->model->repaint();
2564 }
2565 
pmUserOffline(QString cid)2566 void HubFrame::pmUserOffline(QString cid){
2567     pmUserEvent(cid, tr("User offline."));
2568 }
2569 
pmUserEvent(QString cid,QString e)2570 void HubFrame::pmUserEvent(QString cid, QString e){
2571     Q_D(HubFrame);
2572 
2573     if (!d->pm.contains(cid))
2574         return;
2575 
2576     QString output = "";
2577     QString nick    = " * ";
2578 
2579     QString msg     = "<font color=\"" + WSGET(WS_CHAT_MSG_COLOR) + "\">" + e + "</font>";
2580     QString time    = "";
2581 
2582     if (!WSGET(WS_CHAT_TIMESTAMP).isEmpty())
2583         time = "<font color=\""+WSGET(WS_CHAT_TIME_COLOR)+">["+QDateTime::currentDateTime().toString(WSGET(WS_CHAT_TIMESTAMP))+"]</font>";
2584 
2585     output = time + "<font color=\"" + WSGET(WS_CHAT_STAT_COLOR) + "\"><b>" + nick + "</b> </font>";
2586     output += msg;
2587 
2588     WulforUtil::getInstance()->textToHtml(output, false);
2589 
2590     d->pm[cid]->addOutput(output);
2591 }
2592 
getPassword()2593 void HubFrame::getPassword(){
2594     Q_D(HubFrame);
2595 
2596     MainWindow *MW = MainWindow::getInstance();
2597 
2598     if (!MW->isVisible() && d->client->getPassword().empty()){
2599         MW->show();
2600         MW->raise();
2601 
2602     }
2603 
2604     if(d->client && !d->client->getPassword().empty()) {
2605         d->client->password(d->client->getPassword());
2606         addStatus(tr("Stored password sent..."));
2607     }
2608     else if (d->client && d->client->isConnected()){
2609         QString pass = QInputDialog::getText(this, _q(d->client->getHubUrl()), tr("Password"), QLineEdit::Password);
2610 
2611         if (!pass.isEmpty()){
2612             d->client->setPassword(pass.toStdString());
2613             d->client->password(pass.toStdString());
2614         }
2615         else
2616             d->client->disconnect(true);
2617     }
2618 }
2619 
follow(QString redirect)2620 void HubFrame::follow(QString redirect){
2621     if(!redirect.isEmpty()) {
2622         if(ClientManager::getInstance()->isConnected(_tq(redirect))) {
2623             addStatus(tr("Redirect request received to a hub that's already connected"));
2624             return;
2625         }
2626 
2627         string url = _tq(redirect);
2628 
2629         Q_D(HubFrame);
2630         // the client is dead, long live the client!
2631         d->client->removeListener(this);
2632         HubManager::getInstance()->unregisterHubUrl(_q(d->client->getHubUrl()));
2633         ClientManager::getInstance()->putClient(d->client);
2634         clearUsers();
2635         d->client = ClientManager::getInstance()->getClient(url);
2636 
2637         d->client->addListener(this);
2638         d->client->connect();
2639     }
2640 }
2641 
findText(QTextDocument::FindFlags flag)2642 void HubFrame::findText(QTextDocument::FindFlags flag){
2643     textEdit_CHAT->setExtraSelections(QList<QTextEdit::ExtraSelection>());
2644 
2645     if (lineEdit_FIND->text().isEmpty())
2646         return;
2647 
2648     QTextCursor c = textEdit_CHAT->textCursor();
2649 
2650     bool ok = textEdit_CHAT->find(lineEdit_FIND->text(), flag);
2651 
2652     if (flag == QTextDocument::FindBackward && !ok)
2653         c.movePosition(QTextCursor::End,QTextCursor::MoveAnchor,1);
2654     else if (!flag && !ok)
2655         c.movePosition(QTextCursor::Start,QTextCursor::MoveAnchor,1);
2656 
2657     c = textEdit_CHAT->document()->find(lineEdit_FIND->text(), c, flag);
2658     if (!c.isNull()) {
2659         textEdit_CHAT->setTextCursor(c);
2660         slotFindAll();
2661     }
2662 }
2663 
updateStyles()2664 void HubFrame::updateStyles(){
2665     QString custom_font_desc = WSGET(WS_CHAT_FONT);
2666     QFont custom_font;
2667 
2668     if (!custom_font_desc.isEmpty() && custom_font.fromString(custom_font_desc)){
2669         textEdit_CHAT->document()->setDefaultStyleSheet(
2670                 QString("pre { margin:0px; white-space:pre-wrap; font-family:'%1'; font-size: %2pt; }")
2671                                                         .arg(custom_font.family()).arg(custom_font.pointSize())
2672                                                        );
2673     }
2674     else {
2675         textEdit_CHAT->document()->setDefaultStyleSheet(
2676                                                         QString("pre { margin:0px; white-space:pre-wrap; font-family:'%1' }")
2677                                                         .arg(QApplication::font().family())
2678                                                        );
2679     }
2680 
2681     custom_font_desc = WSGET(WS_CHAT_ULIST_FONT);
2682 
2683     if (!custom_font_desc.isEmpty() && custom_font.fromString(custom_font_desc))
2684         treeView_USERS->setFont(custom_font);
2685 }
2686 
slotActivate()2687 void HubFrame::slotActivate(){
2688     plainTextEdit_INPUT->setFocus();
2689 }
2690 
slotUsersUpdated()2691 void HubFrame::slotUsersUpdated(){
2692     Q_D(HubFrame);
2693 
2694     if (d->proxy && treeView_USERS->model() == d->proxy){
2695         label_USERSTATE->setText(QString(tr("Users count: %3/%1 | Total share: %2"))
2696                                  .arg(d->model->rowCount())
2697                                  .arg(WulforUtil::formatBytes(d->total_shared))
2698                                  .arg(d->proxy->rowCount()));
2699     }
2700     else {
2701         label_USERSTATE->setText(QString(tr("Users count: %1 | Total share: %2"))
2702                                  .arg(d->model->rowCount())
2703                                  .arg(WulforUtil::formatBytes(d->total_shared)));
2704     }
2705 
2706     label_LAST_STATUS->setMaximumHeight(label_USERSTATE->height());
2707 }
2708 
slotReconnect()2709 void HubFrame::slotReconnect(){
2710     clearUsers();
2711 
2712     Q_D(HubFrame);
2713 
2714     if (d->client)
2715         d->client->reconnect();
2716 }
2717 
slotMapOnArena()2718 void HubFrame::slotMapOnArena(){
2719     ArenaWidgetManager::getInstance()->activate(this);
2720 }
2721 
slotClose()2722 void HubFrame::slotClose(){
2723     ArenaWidgetManager::getInstance()->rem(this);
2724 }
2725 
slotPMClosed(QString cid)2726 void HubFrame::slotPMClosed(QString cid){
2727     Q_D(HubFrame);
2728 
2729     auto it = d->pm.find(cid);
2730 
2731     if (it != d->pm.end())
2732         d->pm.erase(it);
2733 }
2734 
2735 template < QString (UserListItem::*func)() const >
copyTagToClipboard(QModelIndexList & list)2736 static void copyTagToClipboard(QModelIndexList &list){
2737     QString ret = "";
2738     UserListItem *item = NULL;
2739 
2740     for (const auto &i : list) {
2741         item = reinterpret_cast<UserListItem*> ( i.internalPointer() );
2742 
2743         if ( !ret.isEmpty() )
2744             ret += "\n";
2745 
2746         if ( item )
2747             ret += (item->*func)();
2748     }
2749 
2750     qApp->clipboard()->setText ( ret, QClipboard::Clipboard );
2751 }
2752 
2753 template < qulonglong (UserListItem::*func)() const >
copyTagToClipboard(QModelIndexList & list)2754 static void copyTagToClipboard(QModelIndexList &list){
2755     QString ret = "";
2756     UserListItem *item = NULL;
2757 
2758     for (const auto &i : list) {
2759         item = reinterpret_cast<UserListItem*> ( i.internalPointer() );
2760 
2761         if ( !ret.isEmpty() )
2762             ret += "\n";
2763 
2764         if ( item )
2765             ret += WulforUtil::formatBytes((item->*func)());
2766     }
2767 
2768     qApp->clipboard()->setText ( ret, QClipboard::Clipboard );
2769 }
2770 
slotUserListMenu(const QPoint &)2771 void HubFrame::slotUserListMenu(const QPoint&){
2772     QItemSelectionModel *selection_model = treeView_USERS->selectionModel();
2773     QModelIndexList proxy_list = selection_model->selectedRows(0);
2774 
2775     if (proxy_list.size() < 1)
2776         return;
2777 
2778     QString cid = "";
2779 
2780     Q_D(HubFrame);
2781 
2782     if (d->proxy && treeView_USERS->model() == d->proxy){
2783         QModelIndex i = d->proxy->mapToSource(proxy_list.at(0));
2784         cid = reinterpret_cast<UserListItem*>(i.internalPointer())->getCID();
2785     }
2786     else{
2787         QModelIndex i = proxy_list.at(0);
2788         cid = reinterpret_cast<UserListItem*>(i.internalPointer())->getCID();
2789     }
2790 
2791     Menu::Action action = Menu::getInstance()->execUserMenu(d->client, cid);
2792     UserListItem *item = NULL;
2793 
2794     proxy_list = selection_model->selectedRows(0);
2795 
2796     if (proxy_list.size() < 1)
2797         return;
2798 
2799     QModelIndexList list;
2800 
2801     if (d->proxy && treeView_USERS->model() == d->proxy){
2802         for (const auto &i : proxy_list)
2803             list.push_back(d->proxy->mapToSource(i));
2804     }
2805     else
2806         list = proxy_list;
2807 
2808     switch (action){
2809         case Menu::None:
2810         {
2811             return;
2812         }
2813         case Menu::BrowseFilelist:
2814         {
2815             for (const auto &i : list){
2816                 item = reinterpret_cast<UserListItem*>(i.internalPointer());
2817 
2818                 if (item)
2819                     browseUserFiles(item->getCID());
2820             }
2821 
2822             break;
2823         }
2824         case Menu::PrivateMessage:
2825         {
2826             for (const auto &i : list){
2827                 item = reinterpret_cast<UserListItem*>(i.internalPointer());
2828 
2829                 if (item)
2830                     addPM(item->getCID(), "", false);
2831             }
2832 
2833             break;
2834         }
2835         case Menu::CopyText:
2836         {
2837             QString ttip = "";
2838 
2839             for (const auto &i : list){
2840                 item = reinterpret_cast<UserListItem*>(i.internalPointer());
2841 
2842                 if (item)
2843                     ttip += getUserInfo(item) + "\n";
2844 
2845                 ttip += "\n";
2846             }
2847 
2848             if (!ttip.isEmpty())
2849                 qApp->clipboard()->setText(ttip, QClipboard::Clipboard);
2850 
2851             break;
2852         }
2853         case Menu::CopyNick:
2854         {
2855             copyTagToClipboard<&UserListItem::getNick> (list);
2856 
2857             break;
2858         }
2859         case Menu::CopyIP:
2860         {
2861             copyTagToClipboard<&UserListItem::getIP> (list);
2862 
2863             break;
2864         }
2865         case Menu::CopyShare:
2866         {
2867             copyTagToClipboard<&UserListItem::getShare> (list);
2868 
2869             break;
2870         }
2871         case Menu::CopyTag:
2872         {
2873             copyTagToClipboard<&UserListItem::getTag> (list);
2874 
2875             break;
2876         }
2877         case Menu::MatchQueue:
2878         {
2879             for (const auto &i : list){
2880                 item = reinterpret_cast<UserListItem*>(i.internalPointer());
2881 
2882                 if (item)
2883                     browseUserFiles(item->getCID(), true);
2884             }
2885 
2886             break;
2887         }
2888         case Menu::FavoriteAdd:
2889         {
2890             for (const auto &i : list){
2891                 item = reinterpret_cast<UserListItem*>(i.internalPointer());
2892 
2893                 if (item)
2894                     addUserToFav(item->getCID());
2895             }
2896 
2897             break;
2898         }
2899         case Menu::FavoriteRem:
2900         {
2901             for (const auto &i : list){
2902                 item = reinterpret_cast<UserListItem*>(i.internalPointer());
2903 
2904                 if (item)
2905                     delUserFromFav(item->getCID());
2906             }
2907 
2908             break;
2909         }
2910         case Menu::GrantSlot:
2911         {
2912             for (const auto &i : list){
2913                 item = reinterpret_cast<UserListItem*>(i.internalPointer());
2914 
2915                 if (item)
2916                     grantSlot(item->getCID());
2917             }
2918 
2919             break;
2920         }
2921         case Menu::RemoveQueue:
2922         {
2923             for (const auto &i : list){
2924                 item = reinterpret_cast<UserListItem*>(i.internalPointer());
2925 
2926                 if (item)
2927                     delUserFromQueue(item->getCID());
2928             }
2929 
2930             break;
2931         }
2932         case Menu::AntiSpamWhite:
2933         {
2934 
2935             if (AntiSpam::getInstance()){
2936                 for (const auto &i : list){
2937                     item = reinterpret_cast<UserListItem*>(i.internalPointer());
2938 
2939                     (*AntiSpam::getInstance()) << eIN_WHITE << item->getNick();
2940                 }
2941             }
2942 
2943             break;
2944         }
2945         case Menu::AntiSpamBlack:
2946         {
2947             if (AntiSpam::getInstance()){
2948                 for (const auto &i : list){
2949                     item = reinterpret_cast<UserListItem*>(i.internalPointer());
2950 
2951                     (*AntiSpam::getInstance()) << eIN_BLACK << item->getNick();
2952                 }
2953             }
2954 
2955             break;
2956         }
2957         default:
2958         {
2959             break;
2960         }
2961     }
2962 }
2963 
slotChatMenu(const QPoint &)2964 void HubFrame::slotChatMenu(const QPoint &){
2965     QTextEdit *editor = qobject_cast<QTextEdit*>(sender());
2966 
2967     if (!editor)
2968         return;
2969     QTextCursor cursor = editor->cursorForPosition(editor->mapFromGlobal(QCursor::pos()));
2970     QString nick = "";
2971     if(cursor.block().userData())
2972         nick = static_cast<UserListUserData*>(cursor.block().userData())->data;
2973 
2974     Q_D(HubFrame);
2975 
2976     QString cid = d->model->CIDforNick(nick, _q(d->client->getHubUrl()));
2977 
2978     if (cid.isEmpty()){
2979         QMenu *m = editor->createStandardContextMenu(QCursor::pos());
2980         m->exec(QCursor::pos());
2981 
2982         delete m;
2983 
2984         return;
2985     }
2986 
2987     QPoint p = QCursor::pos();
2988 
2989     bool pmw = (editor != this->textEdit_CHAT);
2990 
2991     Menu::Action action = Menu::getInstance()->execChatMenu(d->client, cid, pmw);
2992 
2993     switch (action){
2994         case Menu::CopyText:
2995         {
2996             QString ret = editor->textCursor().selectedText();
2997 
2998             if (ret.isEmpty())
2999                 ret = editor->anchorAt(textEdit_CHAT->mapFromGlobal(p));
3000 
3001             if (ret.startsWith("user://")){
3002                 ret.remove(0, 7);
3003 
3004                 ret = ret.trimmed();
3005 
3006                 if (ret.startsWith("<") && ret.endsWith(">")){
3007                     ret.remove(0, 1);//remove <
3008                     ret = ret.left(ret.lastIndexOf(">"));//remove >
3009                 }
3010             }
3011 
3012             if (ret.isEmpty())
3013                 ret = editor->textCursor().block().text();
3014 
3015             qApp->clipboard()->setText(ret, QClipboard::Clipboard);
3016 
3017             break;
3018         }
3019         case Menu::SearchText:
3020         {
3021             QString ret = editor->textCursor().selectedText();
3022 
3023             if (ret.isEmpty())
3024                 ret = editor->anchorAt(textEdit_CHAT->mapFromGlobal(p));
3025 
3026             if (ret.startsWith("user://")){
3027                 ret.remove(0, 7);
3028 
3029                 ret = ret.trimmed();
3030 
3031                 if (ret.startsWith("<") && ret.endsWith(">")){
3032                     ret.remove(0, 1);//remove <
3033                     ret = ret.left(ret.lastIndexOf(">"));//remove >
3034                 }
3035             }
3036 
3037             if (ret.isEmpty())
3038                 break;
3039 
3040             SearchFrame *sf = ArenaWidgetFactory().create<SearchFrame, QWidget*>(this);
3041             sf->fastSearch(ret, false);
3042 
3043             break;
3044         }
3045         case Menu::CopyNick:
3046         {
3047             qApp->clipboard()->setText(nick, QClipboard::Clipboard);
3048 
3049             break;
3050         }
3051         case Menu::FindInList:
3052         {
3053             UserListItem *item = d->model->itemForNick(nick, _q(d->client->getHubUrl()));
3054 
3055             if (item){
3056                 QModelIndex index = d->model->index(item->row(), 0, QModelIndex());
3057 
3058                 treeView_USERS->clearSelection();
3059                 treeView_USERS->selectionModel()->select(index, QItemSelectionModel::Select | QItemSelectionModel::Rows);
3060                 treeView_USERS->scrollTo(index, QAbstractItemView::PositionAtCenter);
3061             }
3062 
3063             break;
3064         }
3065         case Menu::BrowseFilelist:
3066         {
3067             browseUserFiles(cid, false);
3068 
3069             break;
3070         }
3071         case Menu::MatchQueue:
3072         {
3073             browseUserFiles(cid, true);
3074 
3075             break;
3076         }
3077         case Menu::PrivateMessage:
3078         {
3079             addPM(cid, "", false);
3080 
3081             if (d->pm.contains(cid))
3082                 ArenaWidgetManager::getInstance()->activate(d->pm[cid]);
3083 
3084             break;
3085         }
3086         case Menu::FavoriteAdd:
3087         {
3088             addUserToFav(cid);
3089 
3090             break;
3091         }
3092         case Menu::FavoriteRem:
3093         {
3094             delUserFromFav(cid);
3095 
3096             break;
3097         }
3098         case Menu::GrantSlot:
3099         {
3100             grantSlot(cid);
3101 
3102             break;
3103         }
3104         case Menu::RemoveQueue:
3105         {
3106             delUserFromQueue(cid);
3107 
3108             break;
3109         }
3110         case Menu::UserCommands:
3111         {
3112             //All work already done in Menu::exec*
3113             break;
3114         }
3115         case Menu::ClearChat:
3116         {
3117             if (pmw)
3118                 MainWindow::getInstance()->slotChatClear(); // some hack
3119             else
3120                 clearChat();
3121 
3122             break;
3123         }
3124         case Menu::FindInChat:
3125         {
3126             slotHideFindFrame();
3127 
3128             break;
3129         }
3130         case Menu::DisableChat:
3131         {
3132             disableChat();
3133 
3134             break;
3135         }
3136         case Menu::SelectAllChat:
3137         {
3138             editor->selectAll();
3139 
3140             break;
3141         }
3142         case Menu::ZoomInChat:
3143         {
3144             editor->zoomIn();
3145 
3146             break;
3147         }
3148         case Menu::ZoomOutChat:
3149         {
3150             editor->zoomOut();
3151 
3152             break;
3153         }
3154         case Menu::AntiSpamWhite:
3155         {
3156             if (AntiSpam::getInstance())
3157                 (*AntiSpam::getInstance()) << eIN_WHITE << nick;
3158 
3159             break;
3160         }
3161         case Menu::AntiSpamBlack:
3162         {
3163             if (AntiSpam::getInstance())
3164                 (*AntiSpam::getInstance()) << eIN_BLACK << nick;
3165 
3166             break;
3167         }
3168         case Menu::None:
3169         {
3170             return;
3171         }
3172         default:
3173         {
3174             return;
3175         }
3176     }
3177 }
3178 
slotHeaderMenu(const QPoint &)3179 void HubFrame::slotHeaderMenu(const QPoint&){
3180     WulforUtil::headerMenu(treeView_USERS);
3181 }
3182 
slotShowWnd()3183 void HubFrame::slotShowWnd(){
3184     if (isVisible())
3185         return;
3186 
3187    ArenaWidgetManager::getInstance()->activate(this);
3188 }
3189 
slotShellFinished(bool ok,QString output)3190 void HubFrame::slotShellFinished(bool ok, QString output){
3191     if (ok){
3192         HubFrame *fr = qobject_cast<HubFrame *>(sender()->parent());
3193         PMWindow *pm = qobject_cast<PMWindow *>(sender()->parent());
3194 
3195         LinkParser::parseForMagnetAlias(output);
3196 
3197         if (fr == this)
3198             sendChat(output, false, false);
3199         else if (pm)
3200             pm->sendMessage(output, false, false);
3201     }
3202 
3203     ShellCommandRunner *runner = reinterpret_cast<ShellCommandRunner*>(sender());
3204 
3205     runner->cancel();
3206     runner->exit(0);
3207     runner->wait(100);
3208 
3209     if (runner->isRunning())
3210         runner->terminate();
3211 
3212     Q_D(HubFrame);
3213 
3214     if (d->shell_list.indexOf(runner) >= 0)
3215         d->shell_list.removeAt(d->shell_list.indexOf(runner));
3216 
3217     delete runner;
3218 }
3219 
nextMsg()3220 void HubFrame::nextMsg(){
3221     if (!plainTextEdit_INPUT->hasFocus())
3222         return;
3223 
3224     Q_D(HubFrame);
3225 
3226     if (d->out_messages_index < 0 ||
3227         d->out_messages_index+1 > d->out_messages.size()-1 ||
3228         d->out_messages.isEmpty())
3229         return;
3230 
3231     if (d->out_messages.at(d->out_messages_index) != plainTextEdit_INPUT->toPlainText())
3232         d->out_messages[d->out_messages_index] = plainTextEdit_INPUT->toPlainText();
3233 
3234     if (d->out_messages_index+1 <= d->out_messages.size()-1)
3235         d->out_messages_index++;
3236 
3237     plainTextEdit_INPUT->setPlainText(d->out_messages.at(d->out_messages_index));
3238 
3239     if (d->out_messages_unsent && d->out_messages_index == d->out_messages.size()-1){
3240         d->out_messages.removeLast();
3241         d->out_messages_unsent = false;
3242         d->out_messages_index = d->out_messages.size()-1;
3243     }
3244 }
3245 
prevMsg()3246 void HubFrame::prevMsg(){
3247     if (!plainTextEdit_INPUT->hasFocus())
3248         return;
3249 
3250     Q_D(HubFrame);
3251 
3252     if (d->out_messages_index < 1 ||
3253         d->out_messages_index-1 > d->out_messages.size()-1 ||
3254         d->out_messages.isEmpty())
3255         return;
3256 
3257     if (!d->out_messages_unsent && d->out_messages_index == d->out_messages.size()-1){
3258         d->out_messages << plainTextEdit_INPUT->toPlainText();
3259         d->out_messages_unsent = true;
3260         d->out_messages_index++;
3261     }
3262 
3263     if (d->out_messages.at(d->out_messages_index) != plainTextEdit_INPUT->toPlainText())
3264         d->out_messages[d->out_messages_index] = plainTextEdit_INPUT->toPlainText();
3265 
3266     if (d->out_messages_index >= 1)
3267         d->out_messages_index--;
3268 
3269     plainTextEdit_INPUT->setPlainText(d->out_messages.at(d->out_messages_index));
3270 }
3271 
slotHideFindFrame()3272 void HubFrame::slotHideFindFrame(){
3273     frame->setVisible(!frame->isVisible());
3274 
3275     if (frame->isVisible()){
3276         QString stext = textEdit_CHAT->textCursor().selectedText();
3277 
3278         if (!stext.isEmpty()){
3279             lineEdit_FIND->setText(stext);
3280             lineEdit_FIND->selectAll();
3281         }
3282 
3283         lineEdit_FIND->setFocus();
3284     }
3285     else{
3286         QTextCursor c = textEdit_CHAT->textCursor();
3287 
3288         c.movePosition(QTextCursor::StartOfLine,QTextCursor::MoveAnchor,1);
3289 
3290         textEdit_CHAT->setExtraSelections(QList<QTextEdit::ExtraSelection>());
3291 
3292         textEdit_CHAT->setTextCursor(c);
3293     }
3294 }
3295 
slotFilterTextChanged()3296 void HubFrame::slotFilterTextChanged(){
3297     QString text = lineEdit_FILTER->text();
3298 
3299     Q_D(HubFrame);
3300 
3301     if (!text.isEmpty()){
3302         if (!d->proxy){
3303             d->proxy = new UserListProxyModel();
3304             d->proxy->setDynamicSortFilter(true);
3305             d->proxy->setSourceModel(d->model);
3306         }
3307 
3308         bool isRegExp = false;
3309 
3310         if (text.startsWith("##")){
3311             isRegExp = true;
3312             text.remove(0, 2);
3313         }
3314 
3315         if (!isRegExp){
3316             d->proxy->setFilterFixedString(text);
3317             d->proxy->setFilterCaseSensitivity(Qt::CaseInsensitive);
3318         }
3319         else{
3320             d->proxy->setFilterRegExp(text);
3321             d->proxy->setFilterCaseSensitivity(Qt::CaseSensitive);
3322         }
3323 
3324         d->proxy->setFilterKeyColumn(comboBox_COLUMNS->currentIndex());
3325 
3326         if (treeView_USERS->model() != d->proxy)
3327             treeView_USERS->setModel(d->proxy);
3328     }
3329     else if (treeView_USERS->model() != d->model)
3330         treeView_USERS->setModel(d->model);
3331 
3332     if (comboBox_COLUMNS->hasFocus())
3333         lineEdit_FILTER->setFocus();
3334 }
3335 
slotFindTextEdited(const QString & text)3336 void HubFrame::slotFindTextEdited(const QString & text){
3337     if (text.isEmpty()){
3338         textEdit_CHAT->verticalScrollBar()->setValue(textEdit_CHAT->verticalScrollBar()->maximum());
3339         textEdit_CHAT->textCursor().movePosition(QTextCursor::End, QTextCursor::MoveAnchor, 1);
3340 
3341         return;
3342     }
3343 
3344     QTextCursor c = textEdit_CHAT->textCursor();
3345 
3346     c.movePosition(QTextCursor::StartOfLine,QTextCursor::MoveAnchor,1);
3347     c = textEdit_CHAT->document()->find(lineEdit_FIND->text(), c, 0);
3348     if (!c.isNull()) {
3349         textEdit_CHAT->setExtraSelections(QList<QTextEdit::ExtraSelection>());
3350         textEdit_CHAT->setTextCursor(c);
3351         slotFindAll();
3352     }
3353 }
3354 
slotFindAll()3355 void HubFrame::slotFindAll(){
3356     if (!toolButton_ALL->isChecked()){
3357         textEdit_CHAT->setExtraSelections(QList<QTextEdit::ExtraSelection>());
3358 
3359         return;
3360     }
3361 
3362     QList<QTextEdit::ExtraSelection> extraSelections;
3363 
3364     if (!lineEdit_FIND->text().isEmpty()) {
3365         QTextEdit::ExtraSelection selection;
3366 
3367         QColor color;
3368         color.setNamedColor(WSGET(WS_CHAT_FIND_COLOR));
3369         color.setAlpha(WIGET(WI_CHAT_FIND_COLOR_ALPHA));
3370 
3371         selection.format.setBackground(color);
3372 
3373         QTextCursor c = textEdit_CHAT->document()->find(lineEdit_FIND->text(), 0, 0);
3374 
3375         while (!c.isNull()) {
3376             selection.cursor = c;
3377             extraSelections.append(selection);
3378 
3379             c = textEdit_CHAT->document()->find(lineEdit_FIND->text(), c, 0);
3380         }
3381     }
3382     textEdit_CHAT->setExtraSelections(extraSelections);
3383 }
3384 
slotSmile()3385 void HubFrame::slotSmile(){
3386     if (!(WBGET(WB_APP_ENABLE_EMOTICON) && EmoticonFactory::getInstance()))
3387         return;
3388 
3389     if (WBGET(WB_CHAT_USE_SMILE_PANEL)){
3390         frame_SMILES->setVisible(!frame_SMILES->isVisible());
3391     }
3392     else {
3393         EmoticonDialog *dialog = new EmoticonDialog(this);
3394 
3395         if (dialog->exec() == QDialog::Accepted) {
3396 
3397             QString smiley = dialog->getEmoticonText();
3398 
3399             if (!smiley.isEmpty()) {
3400 
3401                 smiley.replace("&lt;", "<");
3402                 smiley.replace("&gt;", ">");
3403                 smiley.replace("&amp;", "&");
3404                 smiley.replace("&apos;", "\'");
3405                 smiley.replace("&quot;", "\"");
3406 
3407                 smiley += " ";
3408 
3409                 plainTextEdit_INPUT->textCursor().insertText(smiley);
3410                 plainTextEdit_INPUT->setFocus();
3411             }
3412         }
3413 
3414         delete dialog;
3415     }
3416 }
3417 
slotSmileClicked()3418 void HubFrame::slotSmileClicked(){
3419     EmoticonLabel *lbl = qobject_cast<EmoticonLabel* >(sender());
3420 
3421     if (!lbl)
3422         return;
3423 
3424     QString smiley = lbl->toolTip();
3425 
3426     if (!smiley.isEmpty()) {
3427 
3428         smiley.replace("&lt;", "<");
3429         smiley.replace("&gt;", ">");
3430         smiley.replace("&amp;", "&");
3431         smiley.replace("&apos;", "\'");
3432         smiley.replace("&quot;", "\"");
3433 
3434         smiley += " ";
3435 
3436         plainTextEdit_INPUT->textCursor().insertText(smiley);
3437         plainTextEdit_INPUT->setFocus();
3438     }
3439 
3440     if (WBGET(WB_CHAT_HIDE_SMILE_PANEL))
3441         frame_SMILES->setVisible(false);
3442 }
3443 
slotSmileContextMenu()3444 void HubFrame::slotSmileContextMenu(){
3445 #if !defined(Q_WS_WIN)
3446     QString emot = CLIENT_DATA_DIR "/emoticons/";
3447 #else
3448     QString emot = qApp->applicationDirPath()+QDir::separator()+CLIENT_DATA_DIR "/emoticons/";
3449 #endif
3450 
3451     QMenu *m = new QMenu(this);
3452 
3453     for (const auto &f : QDir(emot).entryList(QDir::Dirs | QDir::NoSymLinks | QDir::NoDotAndDotDot)){
3454         if (!f.isEmpty()){
3455             QAction * act = m->addAction(f);
3456             act->setCheckable(true);
3457 
3458             if (f == WSGET(WS_APP_EMOTICON_THEME)){
3459                 act->setChecked(false);
3460                 act->setChecked(true);
3461             }
3462         }
3463     }
3464 
3465     QAction *a = m->exec(QCursor::pos());
3466 
3467     if (a && a->isChecked())
3468         WSSET(WS_APP_EMOTICON_THEME, a->text());
3469 
3470     m->deleteLater();
3471 }
3472 
slotInputTextChanged()3473 void HubFrame::slotInputTextChanged(){
3474 #ifdef USE_ASPELL
3475     PMWindow *p = qobject_cast<PMWindow*>(sender());
3476     QTextEdit *plainTextEdit_INPUT = (p)? qobject_cast<QTextEdit*>(p->inputWidget()) : this->plainTextEdit_INPUT;
3477 
3478     if (!plainTextEdit_INPUT)
3479         return;
3480     QString line = plainTextEdit_INPUT->toPlainText();
3481 
3482     if (line.isEmpty() || !SpellCheck::getInstance())
3483         return;
3484 
3485     SpellCheck *sp = SpellCheck::getInstance();
3486     QStringList words = line.split(QRegExp("\\W+"), QString::SkipEmptyParts);
3487 
3488     if (words.isEmpty())
3489         return;
3490 
3491     QList<QTextEdit::ExtraSelection> extraSelections;
3492 
3493     QTextCursor c = plainTextEdit_INPUT->textCursor();
3494 
3495     QTextEdit::ExtraSelection selection;
3496     selection.format.setUnderlineStyle(QTextCharFormat::WaveUnderline);
3497     selection.format.setUnderlineColor(Qt::red);
3498 
3499     bool ok = false;
3500     while (!words.empty()){
3501         QString s = words.takeLast();
3502 
3503         if ((s.toLongLong(&ok) && ok) || !QUrl(s).scheme().isEmpty())
3504             continue;
3505 
3506         if (plainTextEdit_INPUT->find(s, QTextDocument::FindBackward) && !sp->ok(s)){
3507             selection.cursor = plainTextEdit_INPUT->textCursor();
3508             extraSelections.append(selection);
3509         }
3510     }
3511 
3512     plainTextEdit_INPUT->setTextCursor(c);
3513     plainTextEdit_INPUT->setExtraSelections(extraSelections);
3514 #endif
3515 }
3516 
slotInputContextMenu()3517 void HubFrame::slotInputContextMenu(){
3518     PMWindow *p = qobject_cast<PMWindow*>(sender());
3519     QTextEdit *plainTextEdit_INPUT = (p)? qobject_cast<QTextEdit*>(p->inputWidget()) : this->plainTextEdit_INPUT;
3520 
3521     if (!plainTextEdit_INPUT)
3522         return;
3523 
3524     QMenu *m = plainTextEdit_INPUT->createStandardContextMenu();
3525 
3526 #ifndef USE_ASPELL
3527     m->exec(QCursor::pos());
3528     m->deleteLater();
3529 #else
3530     if (SpellCheck::getInstance()) {
3531         SpellCheck *sp = SpellCheck::getInstance();
3532         QTextCursor c = plainTextEdit_INPUT->cursorForPosition(plainTextEdit_INPUT->mapFromGlobal(QCursor::pos()));
3533         c.select(QTextCursor::WordUnderCursor);
3534 
3535         QString word = c.selectedText();
3536 
3537         if (sp->ok(word) || word.isEmpty()){
3538             c.clearSelection();
3539             m->exec(QCursor::pos());
3540             m->deleteLater();
3541         }
3542         else {
3543             QStringList list;
3544             sp->suggestions(word, list);
3545 
3546             m->addSeparator();
3547             QAction *add_to_dict = new QAction(tr("Add to dictionary"), m);
3548 
3549             m->addAction(add_to_dict);
3550 
3551             QMenu *ss = NULL;
3552             if (!list.isEmpty()) {
3553                 ss = new QMenu(tr("Suggestions"), this);
3554 
3555 
3556                 for (const auto &s : list)
3557                     ss->addAction(s);
3558 
3559                 m->addMenu(ss);
3560             }
3561 
3562             QAction *ret = m->exec(QCursor::pos());
3563 
3564             if (ret == add_to_dict)
3565                 sp->addToDict(word);
3566             else if (ss && ret && ret->parent() == ss){
3567                 c.removeSelectedText();
3568 
3569                 c.insertText(ret->text());
3570             }
3571 
3572             m->deleteLater();
3573 
3574             if (ss)
3575                 ss->deleteLater();
3576 
3577             slotInputTextChanged();
3578         }
3579     }
3580     else {
3581         m->exec(QCursor::pos());
3582         m->deleteLater();
3583     }
3584 #endif
3585 }
3586 
slotStatusLinkOpen(const QString & url)3587 void HubFrame::slotStatusLinkOpen(const QString &url){
3588     WulforUtil::getInstance()->openUrl(url);
3589 }
3590 
slotHubMenu(QAction * res)3591 void HubFrame::slotHubMenu(QAction *res) {
3592     if (res && res->data().canConvert(QVariant::Int)) {//User command
3593         int id = res->data().toInt();
3594 
3595         UserCommand uc;
3596         if (id == -1 || !FavoriteManager::getInstance()->getUserCommand(id, uc))
3597             return;
3598 
3599         StringMap params;
3600         Q_D(HubFrame);
3601 
3602         if (WulforUtil::getInstance()->getUserCommandParams(uc, params)) {
3603             d->client->getMyIdentity().getParams(params, "my", true);
3604             d->client->getHubIdentity().getParams(params, "hub", false);
3605 
3606             d->client->escapeParams(params);
3607             d->client->sendUserCmd(uc, params);
3608         }
3609     }
3610 }
3611 
slotSettingsChanged(const QString & key,const QString & value)3612 void HubFrame::slotSettingsChanged(const QString &key, const QString &value){
3613     if (key == WS_CHAT_FONT || key == WS_CHAT_ULIST_FONT)
3614         updateStyles();
3615     else if (key == WS_APP_EMOTICON_THEME){
3616         if (EmoticonFactory::getInstance()){
3617             EmoticonFactory::getInstance()->load();
3618 
3619             frame_SMILES->setVisible(false);
3620 
3621             clearLayout(frame_SMILES->layout());
3622 
3623             QSize sz;
3624             Q_UNUSED(sz);
3625 
3626             EmoticonFactory::getInstance()->fillLayout(frame_SMILES->layout(), sz);
3627 
3628             for (const auto &l : frame_SMILES->findChildren<EmoticonLabel*>())
3629                 connect(l, SIGNAL(clicked()), this, SLOT(slotSmileClicked()));
3630         }
3631     }
3632     else if (key == "hubframe/chat-background-color"){
3633         QPalette p = textEdit_CHAT->palette();
3634         QColor clr = p.color(QPalette::Active, QPalette::Base);
3635 
3636         clr.setNamedColor(value);
3637 
3638         if (clr.isValid()){
3639             p.setColor(QPalette::Base, clr);
3640 
3641             textEdit_CHAT->setPalette(p);
3642         }
3643     }
3644     else if (key == WS_TRANSLATION_FILE){
3645         retranslateUi(this);
3646     }
3647 }
3648 
slotBoolSettingsChanged(const QString & key,int value)3649 void HubFrame::slotBoolSettingsChanged(const QString &key, int value){
3650     if (key == WB_APP_ENABLE_EMOTICON){
3651         bool enable = static_cast<bool>(value);
3652 
3653         if (enable){
3654             EmoticonFactory::newInstance();
3655             EmoticonFactory::getInstance()->load();
3656 
3657             frame_SMILES->setVisible(false);
3658 
3659             clearLayout(frame_SMILES->layout());
3660 
3661             QSize sz;
3662             Q_UNUSED(sz);
3663 
3664             EmoticonFactory::getInstance()->fillLayout(frame_SMILES->layout(), sz);
3665 
3666             for (const auto &l : frame_SMILES->findChildren<EmoticonLabel*>())
3667                 connect(l, SIGNAL(clicked()), this, SLOT(slotSmileClicked()));
3668 
3669         }
3670         else{
3671             if (EmoticonFactory::getInstance())
3672                 EmoticonFactory::deleteInstance();
3673 
3674             frame_SMILES->setVisible(false);
3675 
3676             clearLayout(frame_SMILES->layout());
3677         }
3678 
3679         toolButton_SMILE->setVisible(enable);
3680     }
3681 }
3682 
slotCopyHubIP()3683 void HubFrame::slotCopyHubIP(){
3684     Q_D(HubFrame);
3685 
3686     if (d->client && d->client->isConnected()){
3687         qApp->clipboard()->setText(_q(d->client->getIp()), QClipboard::Clipboard);
3688     }
3689 }
3690 
slotCopyHubTitle()3691 void HubFrame::slotCopyHubTitle(){
3692     Q_D(HubFrame);
3693 
3694     if (d->client && d->client->isConnected()){
3695         qApp->clipboard()->setText(QString("%1 - %2").arg(_q(d->client->getHubName())).arg(_q(d->client->getHubDescription())), QClipboard::Clipboard);
3696     }
3697 }
3698 
slotCopyHubURL()3699 void HubFrame::slotCopyHubURL(){
3700     Q_D(HubFrame);
3701 
3702     if (d->client && d->client->isConnected()){
3703         qApp->clipboard()->setText(_q(d->client->getHubUrl()), QClipboard::Clipboard);
3704     }
3705 }
3706 
on(FavoriteManagerListener::UserAdded,const FavoriteUser & aUser)3707 void HubFrame::on(FavoriteManagerListener::UserAdded, const FavoriteUser& aUser) noexcept {
3708     emit coreFavoriteUserAdded(_q(aUser.getUser()->getCID().toBase32()));
3709 }
3710 
on(FavoriteManagerListener::UserRemoved,const FavoriteUser & aUser)3711 void HubFrame::on(FavoriteManagerListener::UserRemoved, const FavoriteUser& aUser) noexcept {
3712     emit coreFavoriteUserRemoved(_q(aUser.getUser()->getCID().toBase32()));
3713 }
3714 
on(ClientListener::Connecting,Client * c)3715 void HubFrame::on(ClientListener::Connecting, Client *c) noexcept{
3716     Q_D(HubFrame);
3717 
3718     QString status = tr("Connecting to %1").arg(QString::fromStdString(d->client->getHubUrl()));
3719 
3720     emit coreConnecting(status);
3721 }
3722 
on(ClientListener::Connected,Client *)3723 void HubFrame::on(ClientListener::Connected, Client*) noexcept{
3724     Q_D(HubFrame);
3725 
3726     QString status = tr("Connected to %1").arg(QString::fromStdString(d->client->getHubUrl()));
3727 
3728     emit coreConnected(status);
3729 
3730     HubManager::getInstance()->registerHubUrl(_q(d->client->getHubUrl()), this);
3731 }
3732 
on(ClientListener::UserUpdated,Client *,const OnlineUser & user)3733 void HubFrame::on(ClientListener::UserUpdated, Client*, const OnlineUser &user) noexcept{
3734     if (user.getIdentity().isHidden() && !WBGET(WB_SHOW_HIDDEN_USERS))
3735         return;
3736 
3737     emit coreUserUpdated(user.getUser(), user.getIdentity());
3738 }
3739 
on(ClientListener::UsersUpdated x,Client *,const OnlineUserList & list)3740 void HubFrame::on(ClientListener::UsersUpdated x, Client*, const OnlineUserList &list) noexcept{
3741     for (const auto &it : list){
3742         const OnlineUser &user = *it;
3743         if (user.getIdentity().isHidden() && !WBGET(WB_SHOW_HIDDEN_USERS))
3744             continue;
3745 
3746         emit coreUserUpdated(user.getUser(), user.getIdentity());
3747     }
3748 }
3749 
on(ClientListener::UserRemoved,Client *,const OnlineUser & user)3750 void HubFrame::on(ClientListener::UserRemoved, Client*, const OnlineUser &user) noexcept{
3751     if (user.getIdentity().isHidden() && !WBGET(WB_SHOW_HIDDEN_USERS))
3752         return;
3753 
3754     emit coreUserRemoved(user.getUser(), user.getIdentity());
3755 }
3756 
on(ClientListener::Redirect,Client *,const string & link)3757 void HubFrame::on(ClientListener::Redirect, Client*, const string &link) noexcept{
3758     if(ClientManager::getInstance()->isConnected(link)) {
3759         emit coreStatusMsg(tr("Redirect request received to a hub that's already connected"));
3760 
3761         return;
3762     }
3763 
3764     if(BOOLSETTING(AUTO_FOLLOW))
3765         emit coreFollow(_q(link));
3766 }
3767 
on(ClientListener::Failed,Client *,const string & msg)3768 void HubFrame::on(ClientListener::Failed, Client*, const string &msg) noexcept{
3769     QString status = tr("Fail: %1...").arg(_q(msg));
3770 
3771     emit coreStatusMsg(status);
3772     emit coreFailed();
3773     emit coreHubUpdated();
3774 }
3775 
on(GetPassword,Client *)3776 void HubFrame::on(GetPassword, Client*) noexcept{
3777     emit corePassword();
3778 }
3779 
on(ClientListener::HubUpdated,Client *)3780 void HubFrame::on(ClientListener::HubUpdated, Client*) noexcept{
3781     emit coreHubUpdated();
3782 }
3783 
on(ClientListener::Message,Client *,const ChatMessage & message)3784 void HubFrame::on(ClientListener::Message, Client*, const ChatMessage &message) noexcept{
3785     if (message.text.empty())
3786         return;
3787 
3788     VarMap map;
3789     QString msg = _q(message.text);
3790     bool third = false;
3791 
3792     if (msg.startsWith("/me ")){
3793         msg.remove(0, 4);
3794 
3795         third = true;
3796     }
3797     else
3798         third = message.thirdPerson;
3799 
3800     Q_D(HubFrame);
3801 
3802     map["HUBURL"] = _q(d->client->getHubUrl());
3803 
3804     if(message.to && message.replyTo)
3805     {
3806         //private message
3807         const OnlineUser *user = (message.replyTo->getUser() == ClientManager::getInstance()->getMe())?
3808                                  message.to : message.replyTo;
3809 
3810         bool isBot = user->getIdentity().isBot() || user->getUser()->isSet(User::BOT);
3811         bool isHub = user->getIdentity().isHub();
3812         bool isOp  = user->getIdentity().isOp();
3813 
3814         if (isHub && BOOLSETTING(IGNORE_HUB_PMS))
3815             return;
3816         else if (isBot && BOOLSETTING(IGNORE_BOT_PMS))
3817             return;
3818 
3819         CID id           = user->getUser()->getCID();
3820         QString nick     =  _q(message.from->getIdentity().getNick());
3821         bool isInSandBox = false;
3822         bool isEcho      = (message.from->getUser() == ClientManager::getInstance()->getMe());
3823         bool hasPMWindow = d->pm.contains(_q(id.toBase32()));//PMWindow is created
3824 
3825         if (AntiSpam::getInstance())
3826             isInSandBox = AntiSpam::getInstance()->isInSandBox(_q(id.toBase32()));
3827 
3828         if (AntiSpam::getInstance() && !isEcho){
3829             do {
3830                 if (hasPMWindow)
3831                     break;
3832 
3833                 if (isOp && !WBGET(WB_ANTISPAM_FILTER_OPS) && !isBot)
3834                     break;
3835 
3836                 if (AntiSpam::getInstance()->isInBlack(nick))
3837                     return;
3838                 else if (!(AntiSpam::getInstance()->isInWhite(nick) || AntiSpam::getInstance()->isInGray(nick))){
3839                     AntiSpam::getInstance()->checkUser(_q(id.toBase32()), msg, _q(d->client->getHubUrl()));
3840 
3841                     return;
3842                 }
3843             } while (0);
3844         }
3845         else if (isEcho && isInSandBox && !hasPMWindow)
3846             return;
3847 
3848         map["NICK"]  = nick;
3849         map["MSG"]   = msg;
3850         map["TIME"]  = QDateTime::currentDateTime().toString(WSGET(WS_CHAT_TIMESTAMP));
3851         map["ECHO"]  = isEcho;
3852 
3853         QString color = WS_CHAT_PRIV_USER_COLOR;
3854 
3855         if (nick == _q(d->client->getMyNick()))
3856             color = WS_CHAT_PRIV_LOCAL_COLOR;
3857         else if (isOp)
3858             color = WS_CHAT_OP_COLOR;
3859         else if (isBot)
3860             color = WS_CHAT_BOT_COLOR;
3861         else if (isHub)
3862             color = WS_CHAT_STAT_COLOR;
3863 
3864         map["CLR"] = color;
3865         map["3RD"] = third;
3866         map["CID"] = _q(id.toBase32());
3867         map["I4"]  = _q(message.from->getIdentity().getIp());
3868 
3869         if (WBGET(WB_CHAT_REDIRECT_BOT_PMS) && isBot)
3870             emit coreMessage(map);
3871         else
3872             emit corePrivateMsg(map);
3873 
3874         if (!(isBot || isHub) && (message.from->getUser() != ClientManager::getInstance()->getMe()) && Util::getAway() && !hasPMWindow)
3875             ClientManager::getInstance()->privateMessage(HintedUser(user->getUser(), d->client->getHubUrl()), Util::getAwayMessage(), false);
3876 
3877         if (BOOLSETTING(LOG_PRIVATE_CHAT)){
3878             string info = Util::formatAdditionalInfo(map["I4"].toString().toStdString(),BOOLSETTING(USE_IP),BOOLSETTING(GET_USER_COUNTRY));
3879             QString qinfo = !info.empty() ? _q(info) : "";
3880 
3881             StringMap params;
3882             params["message"] = _tq(qinfo + "<" + nick + "> " + msg);
3883             params["hubNI"] = _tq(WulforUtil::getInstance()->getHubNames(id));
3884             params["hubURL"] = d->client->getHubUrl();
3885             params["userCID"] = id.toBase32();
3886             params["userNI"] = user->getIdentity().getNick();
3887             params["myCID"] = ClientManager::getInstance()->getMe()->getCID().toBase32();
3888             params["userI4"] = message.from->getIdentity().getIp();
3889             LOG(LogManager::PM, params);
3890         }
3891     }
3892     else
3893     {
3894         // chat message
3895         const OnlineUser *user = message.from;
3896 
3897         if (d->chatDisabled)
3898             return;
3899 
3900         if (AntiSpam::getInstance() && AntiSpam::getInstance()->isInBlack(_q(user->getIdentity().getNick())))
3901             return;
3902 
3903         map["NICK"] = _q(user->getIdentity().getNick());
3904         map["MSG"]  = msg;
3905         map["TIME"] = QDateTime::currentDateTime().toString(WSGET(WS_CHAT_TIMESTAMP));
3906 
3907         QString color = WS_CHAT_USER_COLOR;
3908 
3909         if (user->getIdentity().isHub())
3910             color = WS_CHAT_STAT_COLOR;
3911         else if (user->getUser() == d->client->getMyIdentity().getUser())
3912             color = WS_CHAT_LOCAL_COLOR;
3913         else if (user->getIdentity().isOp())
3914             color = WS_CHAT_OP_COLOR;
3915         else if (user->getIdentity().isBot())
3916             color = WS_CHAT_BOT_COLOR;
3917 
3918         if (FavoriteManager::getInstance()->isFavoriteUser(user->getUser()))
3919             color = WS_CHAT_FAVUSER_COLOR;
3920 
3921         map["CLR"] = color;
3922         map["3RD"] = third;
3923         map["I4"]  = _q(user->getIdentity().getIp());
3924 
3925         emit coreMessage(map);
3926 
3927         if (BOOLSETTING(LOG_MAIN_CHAT)){
3928             string info = Util::formatAdditionalInfo(map["I4"].toString().toStdString(),BOOLSETTING(USE_IP),BOOLSETTING(GET_USER_COUNTRY));
3929             QString qinfo = !info.empty() ? _q(info) : "";
3930             QString nick  =  _q(user->getIdentity().getNick());
3931 
3932             StringMap params;
3933             params["message"] = _tq(qinfo + "<" + nick + "> " + msg);
3934             d->client->getHubIdentity().getParams(params, "hub", false);
3935             params["hubURL"] = d->client->getHubUrl();
3936             params["userNI"] = _tq(nick);
3937             params["userI4"] = user->getIdentity().getIp();
3938             d->client->getMyIdentity().getParams(params, "my", true);
3939             LOG(LogManager::CHAT, params);
3940         }
3941     }
3942 }
3943 
on(ClientListener::StatusMessage,Client *,const string & msg,int)3944 void HubFrame::on(ClientListener::StatusMessage, Client*, const string &msg, int) noexcept{
3945     QString status = QString("%1 ").arg(_q(msg));
3946 
3947     emit coreStatusMsg(status);
3948 
3949     Q_D(HubFrame);
3950 
3951     if (BOOLSETTING(LOG_STATUS_MESSAGES)){
3952         StringMap params;
3953         d->client->getHubIdentity().getParams(params, "hub", false);
3954         params["hubURL"] = d->client->getHubUrl();
3955         d->client->getMyIdentity().getParams(params, "my", true);
3956         params["message"] = msg;
3957         LOG(LogManager::STATUS, params);
3958     }
3959 }
3960 
on(ClientListener::NickTaken,Client *)3961 void HubFrame::on(ClientListener::NickTaken, Client*) noexcept{
3962     Q_D(HubFrame);
3963     QString status = tr("Sorry, but nick \"%1\" is already taken by another user.").arg(d->client->getCurrentNick().c_str());
3964 
3965     emit coreStatusMsg(status);
3966 }
3967 
on(ClientListener::SearchFlood,Client *,const string & str)3968 void HubFrame::on(ClientListener::SearchFlood, Client*, const string &str) noexcept{
3969     emit coreStatusMsg(tr("Search flood detected: %1").arg(_q(str)));
3970 }
3971