/***************************************************************************
                          dcclient.cpp  -  description
                             -------------------
    begin                : Thu Oct 4 2001
    copyright            : (C) 2001-2005 by Mathias Küster
    email                : mathen@users.berlios.de
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#include "dcclient.h"

#include <stdlib.h>

#include <QApplication>
#include <QLineEdit>
#include <QCheckBox>
#include <QRegExp>
#include <QComboBox>
#include <QCursor>
#include <QFile>
#include <QMessageBox>
#include <QLabel>
#include <QPushButton>
#include <QListWidgetItem>
#include <QTabWidget>
#include <QInputDialog>
#include <QClipboard>
#include <QLayout>
#include <QSplitter>
#include <QToolButton>
#include <QShowEvent>
#include <QPixmap>
#include <QMouseEvent>
#include <QCloseEvent>
#include <QKeyEvent>
#include <QEvent>
#include <QHeaderView>
#include <QDateTime>
#include <QMdiArea>
#include <QRegExp>
#include <QHostInfo>

#include "dcevent.h"
#include "dcspy.h"
#include "dctransferview.h"
#include "dcconfig.h"
#include "dcchat.h"
#include "dcmenuhandler.h"
#include "dcuserslist.h"
#include "dcfriendobject.h"
#include "dciconloader.h"
#include "dcguiutils.h"
#include "dcconnectionmanager.h"
#include "dcantispam.h"
#include "dcipfilter.h"

#include "userlistmodel.h"
#include "filteronlyproxy.h"

#include <dclib/cfilemanager.h>
#include <dclib/dcos.h>
#include <dclib/dcobject.h>

#include "ui/DCDialogForceMove.h"
#include "ui/DCDialogUserCommandLines.h"


/** */
DCClient::DCClient(QWidget * parent, CString remoteEncoding) : QWidget(parent), CClient(remoteEncoding), m_bInitSizesDone(false) {
    setupUi(this);

    if (DCConnectionManager::Instance()->GetMdiArea()) {
        m_pContainerWindow = DCConnectionManager::Instance()->GetMdiArea()->addSubWindow(this);
    } else {
        m_pContainerWindow = 0;
    }

    // set default icon
    setWindowIcon(DCIconLoader::Instance()->GetPixmap(eiNOTCONNECTED));
    if (m_pContainerWindow != 0) {
        m_pContainerWindow->setWindowIcon(DCIconLoader::Instance()->GetPixmap(eiNOTCONNECTED));
    }

    /* default to sorting A-Z */
    TreeView_USERLIST->sortByColumn(COLUMN_NICK, Qt::AscendingOrder);

    m_pProxyModel = 0;
    m_pUserListModel = new UserListModel(this);

    for (int i = 0; i < m_pUserListModel->columnCount(); ++i) {
        ComboBox_FILTER->addItem(m_pUserListModel->headerData(i, Qt::Horizontal).toString());
    }
    ComboBox_FILTER->addItem(tr("Any"));
    ComboBox_FILTER->setCurrentIndex(0);

    TreeView_USERLIST->setModel(m_pUserListModel);

    m_pMessageList = new QList<CDCMessage*>();
    m_pMyInfoHash = new QHash<QString, CMessageMyInfo*>();

    m_bUseTabWidget = DCConfig::Instance()->GetShowChatInTab();

    m_nChatFloodCounter = 0;

    allow_show_joins = false;

    InitDocument();
}

/** */
DCClient::~DCClient() {
    m_Timer.stop();

    m_pHubChat->removeEventFilter(this);
    delete m_pHubChat;

    if (DCUsersList::Instance()) {
        // force recheck all friend state
        DCFriendObject m_pFriendObject;
        QApplication::postEvent(DCUsersList::Instance(), new DC_FriendEvent(&m_pFriendObject));
    }

    Disconnect();

    m_Mutex.lock();

    if (m_pMessageList) {
        QList<CDCMessage*> * tmp = m_pMessageList;
        m_pMessageList = 0;

        for (QList<CDCMessage*>::const_iterator it = tmp->constBegin(); it != tmp->constEnd(); ++it) {
            delete *it;
        }

        delete tmp;
    }

    m_Mutex.unlock();

    m_ChatMap.clear();

    clearMyInfoCache();
    delete m_pMyInfoHash;
    m_pMyInfoHash = 0;

    /* model(s) deleted by QObject auto child deletion */

    qDeleteAll(hubcommands);
    hubcommands.clear();
}

/** */
void DCClient::InitDocument() {
    SetCrypt(false);

    // add hubchat widget
    m_pHubChat = new DCChat(TabWidget_CHAT, this, false);
    m_pHubChat->setObjectName("HUB-Chat");
    m_pHubChat->setAttribute(Qt::WA_DeleteOnClose);
    m_pHubChat->installEventFilter(this);
    m_pHubChat->SetNick(QString(), tr("Hub"));

    TabWidget_CHAT->addTab(m_pHubChat, tr("Hub"));

    // remove chat list tab
    if (m_bUseTabWidget) {
        TabWidget_CHAT->removeTab(0);
        delete Tab_CHATUSERLIST;
        Tab_CHATUSERLIST = 0;
        ListWidget_CHATUSERLIST = 0; /* deleted with Tab_CHATUSERLIST */

        TabWidget_CHAT->setCurrentIndex(0);

        //TabWidget_CHAT->setCornerWidget( new QPushButton( QIcon( DCIconLoader::Instance()->GetPixmap(eiFILECLOSE) ), QString(), TabWidget_CHAT ) );
        //TabWidget_CHAT->cornerWidget()->setToolTip(tr("Close chat tab"));
        //TabWidget_CHAT->cornerWidget()->setEnabled(false);
    } else {
        connect(ListWidget_CHATUSERLIST, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(slotDoubleClickedChatUserList(QListWidgetItem*)));

        TabWidget_CHAT->setTabText(0, tr("Chat List") + " (" + QString().setNum(ListWidget_CHATUSERLIST->count()) + ")");

        TabWidget_CHAT->setCurrentIndex(1);
    }

    connect(TabWidget_CHAT, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(slotContextMenuTabWidgetChat(const QPoint &)));
    connect(TreeView_USERLIST, SIGNAL(doubleClicked(const QModelIndex &)), this, SLOT(slotDoubleClickedUserList(const QModelIndex &)));
    connect(TreeView_USERLIST, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(slotContextMenuUserList(const QPoint &)));
    connect(TabWidget_CHAT, SIGNAL(currentChanged(QWidget*)), this, SLOT(slotTabWidgetChatCurrentChange(QWidget*)));
    connect(ToolButton_CONNECT, SIGNAL(clicked()), this, SLOT(slotHubConnect()));
    connect(ToolButton_SSL, SIGNAL(clicked()), this, SLOT(slotSSLInfo()));

    /*if (m_bUseTabWidget)
{
    connect( TabWidget_CHAT->cornerWidget(), SIGNAL(clicked()), this, SLOT(slotTabCornerCloseWidgetClicked()) );
    }*/

    /* filter control widgets */
    connect(CheckBox_FILTER, SIGNAL(toggled(bool)), this, SLOT(slotFilterToggled(bool)));
    connect(ComboBox_FILTER, SIGNAL(currentIndexChanged(int)), this, SLOT(slotFilterColumn(int)));
    connect(LineEdit_FILTER, SIGNAL(textChanged(const QString &)), this, SLOT(slotFilterString(const QString &)));

    /* context menu for user list header */
    TreeView_USERLIST->header()->setContextMenuPolicy(Qt::CustomContextMenu);
    connect(TreeView_USERLIST->header(), SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(slotContextMenuHeader(const QPoint &)));

    // set connect disconnect button pixmap
    SetConnection(false);

    TreeView_USERLIST->setAlternatingRowColors(DCConfig::Instance()->GetAlternatingRowColors());
    connect(DCConfig::Instance(), SIGNAL(alternatingRowColorsChanged(bool)), this, SLOT(slotAltRowColors(bool)));

    TreeView_USERLIST->installEventFilter(this);

    connect(&m_Timer, SIGNAL(timeout()), this, SLOT(timerDone()));

    m_Timer.setSingleShot(true);
    m_Timer.start(600);

    if (m_bUseTabWidget) {

        TabWidget_CHAT->setTabsClosable(true);
        connect(TabWidget_CHAT, SIGNAL(tabCloseRequested(int)), this, SLOT(slotCloseTabButtonClicked(int)));

    }
}

/** */
void DCClient::InitSizes() {
    StringMap * map;

    // restore settings
    if (DCConfig::Instance()->GetMap("CLIENTVIEW", map)) {
        if (((*map)["WIDTH"].toInt() > 0) && ((*map)["HEIGHT"].toInt() > 0)) {
            if (m_pContainerWindow && !isMinimized() && !isMaximized()) {
                m_pContainerWindow->resize((*map)["WIDTH"].toInt(), (*map)["HEIGHT"].toInt());
            }
        }

        TreeView_USERLIST->sortByColumn((*map)["SORTCOLUMN"].toInt(), DCGuiUtils::SortOrderFromName((*map)["SORTORDER"]));
    }

    if (DCConfig::Instance()->GetUserListRightAlignment()) {
        // insertWidget moves the widget if it is already in the splitter
        Splitter_CLIENT->insertWidget(0, Frame_CHATLIST);

        Frame_CHATLIST->adjustSize();
        Frame_USERLIST->adjustSize();
    }

    if (DCConfig::Instance()->GetMap("USERLISTVIEW", map)) {
        if ((map->value("WIDTH").toInt() > 0) && !isMinimized()) {
            QList<int> frames = Splitter_CLIENT->sizes();

            if (frames.size() == 2) {
                int total = frames[0] + frames[1];
                if (DCConfig::Instance()->GetUserListRightAlignment()) {
                    frames[0] = total - map->value("WIDTH").toInt();
                    frames[1] = map->value("WIDTH").toInt();
                } else {
                    frames[0] = map->value("WIDTH").toInt();
                    frames[1] = total - map->value("WIDTH").toInt();
                }

                Splitter_CLIENT->setSizes(frames);
            }
        }
    }

    // restore column order, widths and visibility or set defaults
    bool restored = false;
    if (DCConfig::Instance()->GetMap("USERLISTCOLUMNORDER", map)) {
        restored = TreeView_USERLIST->header()->restoreState(
                QByteArray::fromBase64(map->value(QString("USERLISTHEADERSTATE")).toAscii())
                );
    }

    if (!restored) {
        /* TODO initialize default column width ratios if necessary */

        TreeView_USERLIST->header()->hideSection(COLUMN_LOCKPK);
        TreeView_USERLIST->header()->hideSection(COLUMN_SUPPORTS);
    }
}

/** */
void DCClient::DeInitDocument() {
    StringMap * map;

    // save client view settings
    DCConfig::Instance()->GetMap("CLIENTVIEW", map);

    if (m_pContainerWindow != 0) {
        (*map)["WIDTH"] = QString().setNum(m_pContainerWindow->width());
        (*map)["HEIGHT"] = QString().setNum(m_pContainerWindow->height());
    }

    (*map)["SORTCOLUMN"] = QString::number(m_pUserListModel->getSortColumn());
    (*map)["SORTORDER"] = DCGuiUtils::SortOrderName(m_pUserListModel->getSortOrder());

    DCConfig::Instance()->GetMap("USERLISTVIEW", map);

    QList<int> sizes = Splitter_CLIENT->sizes();
    int index = Splitter_CLIENT->indexOf(Frame_USERLIST);
    if (index < sizes.size()) {
        (*map)["WIDTH"] = QString().setNum(sizes[index]);
    }

    saveColumns();

    if (!m_ChatMap.isEmpty()) {
        QMap<QString, DCChat*>::const_iterator it = m_ChatMap.constBegin();
        while (it != m_ChatMap.constEnd()) {
            it.value()->removeEventFilter(this);
            it.value()->close();
            ++it;
        }
    }
}

/** */
void DCClient::SetCrypt(bool b) {
    ToolButton_SSL->setEnabled(b);

    if (b) {
        ToolButton_SSL->setToolTip(tr("Line is encrypted."));
        ToolButton_SSL->setIcon(QIcon(DCIconLoader::Instance()->GetPixmap(eiSSL_YES)));
    } else {
        ToolButton_SSL->setToolTip(tr("Line is not encrypted."));
        ToolButton_SSL->setIcon(QIcon(DCIconLoader::Instance()->GetPixmap(eiSSL_NO)));
    }
}

/** */
void DCClient::SetConnection(bool b) {
    if (b) {
        ToolButton_CONNECT->setToolTip(tr("Disconnect."));
        ToolButton_CONNECT->setIcon(QIcon(DCIconLoader::Instance()->GetPixmap(eiCONNECT_NO)));
    } else {
        ToolButton_CONNECT->setToolTip(tr("Connect."));
        ToolButton_CONNECT->setIcon(QIcon(DCIconLoader::Instance()->GetPixmap(eiCONNECT_CREATING)));
    }
}

/** */
void DCClient::slotSSLInfo() {
    /* a non blocking info dialog, deleted when closed or when the hub is closed */
    QMessageBox * mb = new QMessageBox(this);
    mb->setAttribute(Qt::WA_DeleteOnClose);
    mb->setIcon(QMessageBox::Information);
    mb->setWindowTitle(QString::fromAscii(GetHubName().Data()));
    mb->setText(QString::fromAscii((GetSSLVersion() + '\n' + GetSSLCipher()).Data()));
    mb->setInformativeText(QString::fromAscii(VerifyPeerCertificate().Data()));
    mb->setModal(false);
    mb->show();
}

/** event filter */
bool DCClient::eventFilter(QObject* object, QEvent* event) {
    if ((event->type() == QEvent::Close) && ((DCClient*) object != this)) {
        if (m_pHubChat == object) {
            event->ignore();
            return true;
        }

        CloseChat((DCChat*) object);
    } else if (event->type() == QEvent::KeyPress) {
        QKeyEvent * e = (QKeyEvent*) event;

        // reconnect
        if ((e->modifiers() == Qt::AltModifier) && (e->key() == Qt::Key_R)) {
            slotHubConnect();
        }

        if (TreeView_USERLIST->hasFocus()) {
            // TODO: add userlist shortcuts
        }
    }

    return QWidget::eventFilter(object, event); // standard event processing
}

/** */
void DCClient::reconnect(){
    if (GetConnectionState() == estNONE) {
        SetConnection(true);
        Connect();
        UpdateReconnect(ersNONE, 0);
    }
}

/** current tab widget change slot */
void DCClient::slotTabWidgetChatCurrentChange(QWidget * w) {
    DCChat * chat = (DCChat*) w;

    int index = TabWidget_CHAT->indexOf(w);

    if (!TabWidget_CHAT->tabIcon(index).isNull()) {
        TabWidget_CHAT->setTabIcon(index, QIcon());

        // reset nick color
        m_pUserListModel->setHighlight(chat->GetNick(), false);
    }

    /*if (m_bUseTabWidget)
{
    if (TabWidget_CHAT->currentWidget() == m_pHubChat)
    {
        TabWidget_CHAT->cornerWidget()->setEnabled(false);
    }
    else
    {
        TabWidget_CHAT->cornerWidget()->setEnabled(true);
    }
    }*/
}

/** overridden so that sizes are initialized on first show */
void DCClient::showEvent(QShowEvent * event) {
    QWidget::showEvent(event);

    if ((m_bInitSizesDone == false) && isVisible()) {
        InitSizes();
        m_bInitSizesDone = true;
    }
}

/** close event - remove client from connection manager before accepting */
void DCClient::closeEvent(QCloseEvent * event) {
    DCChat::SetActiveChat(NULL);

    emit onDie(this->GetMdiSubWindow());

    DCConnectionManager::Instance()->HubClosing(this);

    DeInitDocument();

    event->accept();
}

/** */
void DCClient::UpdateCaption() {
    QString s;

    switch (GetConnectionState()) {
        case estCONNECTED:
        {
            s = "[+";
            if (server != "")
                s += server;
            s += "]";
            break;
        }
        default:
            s = "[-]";
            break;
    }

    s += " ";
    s += QString::fromAscii(GetHubName().Data());

    if (GetHubTopic().NotEmpty()) {
        s += " (";
        s += QString::fromAscii(GetHubTopic().Data());
        s += ")";
    }

    if (s.length() > 50) {
        s = s.left(50) + "...";
    }

    setWindowTitle(s);
    DCConnectionManager::Instance()->CaptionChanged(this);
}

/** */
void DCClient::UpdateStatusBar() {
    if (m_pProxyModel && m_pProxyModel->rowCount() != m_pUserListModel->rowCount()) {
        TextLabel_USERCOUNT->setText(QString("%1/%2").arg(m_pProxyModel->rowCount()).arg(m_pUserListModel->rowCount()));
    } else {
        TextLabel_USERCOUNT->setText(QString::number(m_pUserListModel->rowCount()));
    }
    TextLabel_SHARESIZE->setText(DCGuiUtils::GetSizeString(UserList()->ShareSize()));
}

/** */
void DCClient::timerDone() {
    CDCMessage *DCMsg;
    //int i;

    //for(i=0;i<200;i++)
    while (m_pMessageList && !m_pMessageList->isEmpty()) {
        if (m_Mutex.tryLock() == false) {
            break;
        }

        DCMsg = m_pMessageList->takeFirst();

        m_Mutex.unlock();

        if (DCMsg == 0) {
            break;
        }

        switch (DCMsg->m_eType) {
            case DC_MESSAGE_CONNECTION_STATE:
            {
                CMessageConnectionState *msg = (CMessageConnectionState*) DCMsg;

                if (msg->m_eState == estCONNECTED) {
                    m_nChatFloodCounter = 0;
                    m_bUserPassword = false;

                    UpdateCaption();
                    setWindowIcon(DCIconLoader::Instance()->GetPixmap(eiCONNECTED));
                    if (m_pContainerWindow)
                        m_pContainerWindow->setWindowIcon(DCIconLoader::Instance()->GetPixmap(eiCONNECTED));
                    // repaint new icon in minimized mode
                    if (isMinimized())
                        showMinimized();

                    QString connection_msg = tr("Connected");

                    if (server != "")
                        connection_msg += tr(" to ") + server;

                    m_pHubChat->AddStatus(connection_msg);

                    // init userlist
                    DC_NickList(0);

                    DCConfig::Instance()->PlaySound(eusCONNECT);

                    SetConnection(true);

                    SetCrypt(false);
                }
                if (msg->m_eState == estSSLCONNECTED) {
                    SetCrypt(true);
                } else if (msg->m_eState == estCONNECTIONTIMEOUT) {
                    m_pHubChat->AddStatus(tr("Connection timeout"));

                    SetConnection(false);

                    SetCrypt(false);
                } else if (msg->m_eState == estSOCKETERROR) {
                    QString s;
                    s = tr("Error: '");
                    s += QString::fromAscii(msg->m_sMessage.Data());
                    s += QString("'");
                    m_pHubChat->AddStatus(s);

                    SetConnection(false);

                    SetCrypt(false);
                } else if (msg->m_eState == estDISCONNECTED) {
                    m_nChatFloodCounter = 0;

                    UpdateCaption();
                    setWindowIcon(DCIconLoader::Instance()->GetPixmap(eiNOTCONNECTED));
                    if (m_pContainerWindow)
                        m_pContainerWindow->setWindowIcon(DCIconLoader::Instance()->GetPixmap(eiNOTCONNECTED));

                    // repaint new icon in minimized mode
                    if (isMinimized())
                        showMinimized();

                    m_pHubChat->AddStatus(tr("Disconnected"));
                    DCConfig::Instance()->PlaySound(eusDISCONNECT);

                    QString hub_name = QString::fromAscii(GetHubName().Data());

                    if (hub_name.length() > 18)
                        hub_name = hub_name.left(15) + "...";

                    DCGuiApp::Instance()->showNotification(hub_name, tr("Connection lost"), eTrayMessageTypeDISC);

                    if (DCUsersList::Instance()) {
                        // force recheck all friend state
                        DCFriendObject m_pFriendObject;
                        QApplication::postEvent(DCUsersList::Instance(), new DC_FriendEvent(&m_pFriendObject));
                    }

                    // reset all chats
                    QMap<QString, DCChat*>::const_iterator it = m_ChatMap.constBegin();
                    while (it != m_ChatMap.constEnd()) {
                        it.value()->SetCrypt(esecsNONE);
                        it.value()->AddStatus(tr("We left the hub."));
                        ++it;
                    }

                    SetConnection(false);

                    SetCrypt(false);
                }

                break;
            }

            case DC_MESSAGE_VALIDATEDENIDE:
            {
                m_pHubChat->AddStatus(tr("Your Nick is already in use."));
                Disconnect();

                QString hub_name = QString::fromAscii(GetHubName().Data());

                if (hub_name.length() > 18)
                    hub_name = hub_name.left(15) + "...";

                DCGuiApp::Instance()->showNotification(hub_name,
                        tr("Connection lost") + ": " + tr("Your Nick is already in use."),
                        eTrayMessageTypeDISC);

                break;
            }

            case DC_MESSAGE_NICKLIST:
            {
                DC_NickList((CMessageNickList *) DCMsg);
                delete DCMsg;
                m_Timer.setSingleShot(true);
                m_Timer.start(150); //fast retrieving list of users
                return;
            }

            case DC_MESSAGE_OPLIST:
            {
                DC_OpList((CMessageOpList *) DCMsg);
                break;
            }

            case DC_MESSAGE_REVCONNECTTOME:
            {
                if (GetMode() == ecmPASSIVE) {
                    if (DCConfig::Instance()->GetSendMessageOnActiveModeRequest()) {
                        CMessageRevConnectToMe * msg = (CMessageRevConnectToMe*) DCMsg;
                        SendPrivateMessage(GetNick(), msg->m_sDstNick, tr("Sorry, you will not be able to download from me, because we are both in passive mode. (automated message)").toAscii().constData());
                    }
                }

                break;
            }

            case DC_MESSAGE_CHAT:
            {
                CMessageChat * msg = (CMessageChat *) DCMsg;

                if (m_ChatFloodMessage.m_sMessage == msg->m_sMessage && m_ChatFloodMessage.m_sNick == msg->m_sNick) {
                    // increment flood counter
                    m_nChatFloodCounter++;
                } else {
                    // update message and reset counter
                    m_ChatFloodMessage.m_sMessage = msg->m_sMessage;
                    m_ChatFloodMessage.m_sNick = msg->m_sNick;
                    m_nChatFloodCounter = 0;
                }

                // if flood detect enabled ?
                if (DCConfig::Instance()->GetFloodCount() > 0) {
                    if (m_nChatFloodCounter >= DCConfig::Instance()->GetFloodCount()) {
                        // only one flood message
                        if (m_nChatFloodCounter == DCConfig::Instance()->GetFloodCount()) {
                            m_pHubChat->AddStatus(tr("Flood Detected") + " (" + QString().setNum(m_nChatFloodCounter) + ")");

                            // now the admin kick the user if enabled
                            if ((DCConfig::Instance()->GetFloodOpKick()) && (!UserList()->IsAdmin(msg->m_sNick)) && (UserList()->IsAdmin(GetNick()))) {
                                //if OP kick the user for Flooding
                                CString kickmessage = DCConfig::Instance()->GetFloodOpKickMessage();
                                SendPrivateMessage(GetNick(), msg->m_sNick, ("You are being kicked because: " + kickmessage));
                                SendChat(GetNick(), GetNick() + " is kicking " + msg->m_sNick + " because: " + kickmessage);
                                SendKick(msg->m_sNick);
                            }
                        }

                        // don't show the flood messages
                        msg = 0;
                    }
                }

                if (msg) {
                    DC_Chat(msg);
                }

                break;
            }

            case DC_MESSAGE_MYINFO:
            {
                DC_MyInfo((CMessageMyInfo *) DCMsg);
                break;
            }

            case DC_MESSAGE_QUIT:
            {
                DC_Quit((CMessageQuit *) DCMsg);
                break;
            }

            case DC_MESSAGE_SEARCH_FILE:
            {
                if (DCSpy::Instance())
                    DCSpy::Instance()->DC_Search((CMessageSearchFile *) DCMsg);
                break;
            }

            case DC_MESSAGE_HELLO:
            {
                DC_Hello((CMessageHello *) DCMsg);

                break;
            }

            case DC_MESSAGE_FORCEMOVE:
            {
                CMessageForceMove * msg = (CMessageForceMove*) DCMsg;

                if (DCConfig::Instance()->GetForceMoveEnabled()) {
                    UpdateCaption();

                    m_pHubChat->AddStatus(tr("Redirect to ") + QString::fromAscii(msg->m_sHost.Data()) + QString(":") + QString().setNum(msg->m_nPort));
                } else {
                    m_pHubChat->AddStatus(tr("Redirect disabled ") + QString::fromAscii(msg->m_sHost.Data()) + QString(":") + QString().setNum(msg->m_nPort));
                }

                break;
            }

            case DC_MESSAGE_HUBNAME:
            {
                CMessageHubName * msg = (CMessageHubName*) DCMsg;
                QString qhubname = QString::fromAscii(msg->m_sHubName.Data());

                if ((msg->m_sOldHubName.NotEmpty()) && (msg->m_sOldHubName != msg->m_sHubName)) {
                    m_pHubChat->AddStatus(tr("Hubname change:") + QString(" '") + QString::fromAscii(msg->m_sOldHubName.Data()) + QString("' -> '") + qhubname + QString("'"));
                }

                UpdateCaption();

                // update caption from the main chat
                m_pHubChat->SetNick(QString(), qhubname);

                // update caption from all private chats
                if (!m_ChatMap.isEmpty()) {
                    QMap<QString, DCChat*>::const_iterator it = m_ChatMap.constBegin();

                    while (it != m_ChatMap.constEnd()) {
                        it.value()->SetNick(it.value()->GetNick(), qhubname);
                        ++it;
                    }
                }

                break;
            }

            case DC_MESSAGE_HUB_TOPIC:
            {
                UpdateCaption();
                break;
            }

            case DC_MESSAGE_PRIVATECHAT:
            {
                DC_PrivateChat((CMessagePrivateChat*) DCMsg);

                break;
            }

            case DC_MESSAGE_GETPASS:
            {
                bool ok = false;
                DCConfigHubProfile pConfigHubProfile;

                if (m_bUserPassword == false) {
                    // get password from profile
                    if (DCConfig::Instance()->GetBookmarkHubProfile(GetHubName(), GetHost(), &pConfigHubProfile)) {
                        if (pConfigHubProfile.m_sPassword.NotEmpty()) {
                            m_pHubChat->AddStatus(tr("Use password from profile"));
                            SendPass(pConfigHubProfile.m_sPassword);
                            SetUsedPassword(true);
                            m_bUserPassword = true;
                            break;
                        } else {
                            m_pHubChat->AddStatus(tr("Password from profile is empty"));
                        }
                    } else {
                        m_pHubChat->AddStatus(tr("No profile found for this hub"));
                    }
                }

                if (!DCGuiApp::Instance()->isVisible())
                    DCGuiApp::Instance()->WakeUp();

                QString text = QInputDialog::getText(
                        this,
                        tr("Password - ") + QString(GetHubName().Data()),
                        tr("Please enter your password"),
                        QLineEdit::Password, QString::null, &ok);

                if (ok && !text.isEmpty()) {
                    SendPass(text.toAscii().constData());
                    SetUsedPassword(true);
                } else {
                    Disconnect();
                }

                break;
            }

            case DC_MESSAGE_BADPASS:
            {
                m_pHubChat->AddStatus(tr("Wrong password"));
                SetUsedPassword(false);
                break;
            }

            case DC_MESSAGE_USERIP:
            {
                CMessageUserIP * msg = (CMessageUserIP*) DCMsg;

                std::list<CString>::const_iterator nick_it = msg->m_lNicks.begin();
                std::list<CString>::const_iterator ip_it = msg->m_lIPs.begin();
                QString qnick, qip;
                CMessageMyInfo * myinfo = 0;

                while ((nick_it != msg->m_lNicks.end()) && (ip_it != msg->m_lIPs.end())) {
                    qnick = QString::fromAscii(nick_it->Data());
                    qip = QString::fromAscii(ip_it->Data());

                    // fill in the IP address column for the given nick
                    m_pUserListModel->updateIP(qnick, qip);

                    // update this useless thing
                    myinfo = m_pMyInfoHash->value(qnick);
                    if (myinfo) {
                        myinfo->m_sTransferHost = *ip_it;
                    }

                    // notify when given own IP
                    if (*nick_it == GetNick()) {
                        m_pHubChat->AddStatus(tr("Hub gave us our IP: ") + qip);

                        allow_show_joins = true;

                        if (DCConfig::Instance()->GetUserIP2Enabled() == false) {
                            m_pHubChat->AddStatus(tr("Enable") + " \"" + tr("Use IP address from the hub") + "\" " + tr("in Options -> Connection -> Mode to use this value."));
                        }
                    }

                    ++nick_it;
                    ++ip_it;
                }

                break;
            }

            case DC_MESSAGE_USER_COMMAND:
            {
                DC_UserCommand((CMessageUserCommand*) DCMsg);

                break;
            }

            case DC_MESSAGE_LOG:
            {
                m_pHubChat->AddStatus(QString::fromAscii(((CMessageLog*) DCMsg)->sMessage.Data()));
            }

            default:
            {
                break;
            }
        }

        if (DCMsg)
            delete DCMsg;
    }

    m_Timer.setSingleShot(true);
    m_Timer.start(500);
}

/** */
int DCClient::DC_CallBack(CDCMessage * DCMessage) {
    int err = -1;

    m_Mutex.lock();

    if (DCMessage && m_pMessageList) {
        m_pMessageList->append(DCMessage);
        err = 0;
    }

    m_Mutex.unlock();

    return err;
}

/** chat */
void DCClient::DC_Chat(CMessageChat * MessageChat) {
    QString qnick = QString::fromAscii(MessageChat->m_sNick.Data());
    QString qmessage = QString::fromAscii(MessageChat->m_sMessage.Data());

    // check for ignored nick
    if (DCUsersList::Instance()->ignoreNick(qnick)) {
        //printf("Ignoring \"%s\" from \"%s\"\n", MessageChat->m_sMessage.Data(), MessageChat->m_sNick.Data());
        return;
    }

    m_pHubChat->AddMessage(qnick, qmessage, true);

    if (!m_pHubChat->isVisible()) {
        TabWidget_CHAT->setTabIcon(TabWidget_CHAT->indexOf(m_pHubChat), QIcon(DCIconLoader::Instance()->GetPixmap(eiMESSAGE)));
    }

    // send chat event
    DCConnectionManager::Instance()->HubEvent(this);

    // check for auto responses
    if (DCConfig::Instance()->GetAutoResponderEnabled()) {
        doAutoResponse(qnick, qmessage, m_pHubChat);
    }
}

/** myinfo */
void DCClient::DC_MyInfo(CMessageMyInfo * MessageMyInfo) {
    if (MessageMyInfo->m_sNick.IsEmpty()) {
        m_pHubChat->AddStatus(tr("Cannot display user with empty nick."));
        return;
    }

    QPixmap pm;
    QString nick = QString::fromAscii(MessageMyInfo->m_sNick.Data());

    CMessageMyInfo * myinfo = m_pMyInfoHash->value(nick);

    if (myinfo == 0) {
        myinfo = new CMessageMyInfo();
        *myinfo = *MessageMyInfo;
        m_pMyInfoHash->insert(nick, myinfo);

        pm = *DCConfig::Instance()->GetUserIcon(MessageMyInfo);
        m_pUserListModel->addUser(
                nick,
                pm,
                MessageMyInfo->m_sComment.Data(),
                QString::fromAscii(MessageMyInfo->m_sVerComment.Data()),
                MessageMyInfo->m_sUserSpeed.Data(),
                QString::fromAscii(MessageMyInfo->m_sEMail.Data()),
                MessageMyInfo->m_nShared,
                MessageMyInfo->m_sTransferHost.Data(),
                MessageMyInfo->m_MessageLock.m_sData.Data(),
                MessageMyInfo->m_MessageSupports.m_sContent.Data()
                );

        // user not in the list, add it
        addUser(nick);
    } else {
        if ((myinfo->m_eAwayMode != MessageMyInfo->m_eAwayMode) ||
                (myinfo->m_eUserSpeed != MessageMyInfo->m_eUserSpeed) ||
                (myinfo->m_bOperator != MessageMyInfo->m_bOperator) ||
                (myinfo->m_eClientMode != MessageMyInfo->m_eClientMode) ||
                (myinfo->m_eClientVersion != MessageMyInfo->m_eClientVersion) ||
                (myinfo->m_bFireballFlag != MessageMyInfo->m_bFireballFlag) ||
                (myinfo->m_bServerFlag != MessageMyInfo->m_bServerFlag) ||
                (myinfo->m_bTLSFlag != MessageMyInfo->m_bTLSFlag)) {
            pm = *DCConfig::Instance()->GetUserIcon(MessageMyInfo);
            m_pUserListModel->updateIcon(nick, pm);

            myinfo->m_eAwayMode = MessageMyInfo->m_eAwayMode;
            myinfo->m_eUserSpeed = MessageMyInfo->m_eUserSpeed;
            myinfo->m_bOperator = MessageMyInfo->m_bOperator;
            myinfo->m_eClientMode = MessageMyInfo->m_eClientMode;
            myinfo->m_eClientVersion = MessageMyInfo->m_eClientVersion;
            myinfo->m_bFireballFlag = MessageMyInfo->m_bFireballFlag;
            myinfo->m_bServerFlag = MessageMyInfo->m_bServerFlag;
            myinfo->m_bTLSFlag = MessageMyInfo->m_bTLSFlag;
        }

        if (myinfo->m_sComment != MessageMyInfo->m_sComment) {
            myinfo->m_sComment = MessageMyInfo->m_sComment;
            m_pUserListModel->updateComment(nick, QString::fromAscii(MessageMyInfo->m_sComment.Data()));
        }

        if (myinfo->m_sVerComment != MessageMyInfo->m_sVerComment) {
            myinfo->m_sVerComment = MessageMyInfo->m_sVerComment;
            m_pUserListModel->updateTag(nick, MessageMyInfo->m_sVerComment.Data());
        }

        if (myinfo->m_sUserSpeed != MessageMyInfo->m_sUserSpeed) {
            myinfo->m_sUserSpeed = MessageMyInfo->m_sUserSpeed;
            m_pUserListModel->updateSpeed(nick, MessageMyInfo->m_sUserSpeed.Data());
        }

        if (myinfo->m_sEMail != MessageMyInfo->m_sUserSpeed) {
            myinfo->m_sEMail = MessageMyInfo->m_sUserSpeed;
            m_pUserListModel->updateEmail(nick, QString::fromAscii(MessageMyInfo->m_sEMail.Data()));
        }

        if (myinfo->m_nShared != MessageMyInfo->m_nShared) {
            myinfo->m_nShared = MessageMyInfo->m_nShared;
            m_pUserListModel->updateShare(nick, MessageMyInfo->m_nShared);
        }

        if (myinfo->m_sTransferHost != MessageMyInfo->m_sTransferHost) {
            myinfo->m_sTransferHost = MessageMyInfo->m_sTransferHost;
            m_pUserListModel->updateIP(nick, MessageMyInfo->m_sTransferHost.Data());
        }

        if (myinfo->m_MessageLock.m_sData != MessageMyInfo->m_MessageLock.m_sData) {
            myinfo->m_MessageLock.m_sData = MessageMyInfo->m_MessageLock.m_sData;
            m_pUserListModel->updateLockPK(nick, MessageMyInfo->m_MessageLock.m_sData.Data());
        }

        if (myinfo->m_MessageSupports.m_sContent != MessageMyInfo->m_MessageSupports.m_sContent) {
            myinfo->m_MessageSupports.m_sContent = MessageMyInfo->m_MessageSupports.m_sContent;
            m_pUserListModel->updateSupports(nick, MessageMyInfo->m_MessageSupports.m_sContent.Data());
        }
    }

    // send friend event
    if (DCUsersList::Instance()) {
        DCFriendObject m_pFriendObject;

        m_pFriendObject.m_sName = nick;
        m_pFriendObject.m_sDescription = QString::fromAscii(MessageMyInfo->m_sComment.Data());
        m_pFriendObject.m_eAwayMode = MessageMyInfo->m_eAwayMode;
        m_pFriendObject.m_sHubName = QString::fromAscii(GetHubName().Data());
        m_pFriendObject.m_sHubHost = QString::fromAscii(GetHost().Data());
        QApplication::postEvent(DCUsersList::Instance(), new DC_FriendEvent(&m_pFriendObject));
    }

    UpdateStatusBar();
}

/** hello */
void DCClient::DC_Hello(CMessageHello * MessageHello) {
    if (MessageHello->m_sNick.IsEmpty()) {
        m_pHubChat->AddStatus(tr("Cannot display user with empty nick."));
        return;
    }

    QPixmap pm;
    QString nick = QString::fromAscii(MessageHello->m_sNick.Data());

    if (m_pMyInfoHash->contains(nick) == false) {
        CMessageMyInfo * myinfo = new CMessageMyInfo();
        myinfo->m_sNick = MessageHello->m_sNick;
        myinfo->m_eAwayMode = euamNORMAL;

        m_pMyInfoHash->insert(nick, myinfo);

        pm = *DCConfig::Instance()->GetUserIcon(myinfo);

        m_pUserListModel->addUser(
                nick,
                pm
                );

        addUser(nick);
    }

    UpdateStatusBar();
}

void DCClient::setServer(QString s){
    if (DCIPFilter::isIP(s))
        server = s;
    else
        QHostInfo::lookupHost(s, this, SLOT(slotGetHostIP4(QHostInfo)));
}

QString DCClient::getServer(){
    return server;
}

void DCClient::slotGetHostIP4(QHostInfo info){
    if (info.addresses().size() == 0)
        server = "";
    else
        server = info.addresses().at(0).toString();
}

/** quit */
void DCClient::DC_Quit(CMessageQuit * MessageQuit) {
    if (MessageQuit->m_sNick.IsEmpty()) {
        return;
    }

    QString nick = QString::fromAscii(MessageQuit->m_sNick.Data());
    DCChat * chat = m_ChatMap.value(nick);

    QHash<QString, CMessageMyInfo*>::iterator it = m_pMyInfoHash->find(nick);
    if (it != m_pMyInfoHash->end()) {
        m_pUserListModel->removeUser(nick);
        delete it.value();
        m_pMyInfoHash->erase(it);
    }

    if (chat != 0) {
        chat->SetCrypt(esecsNONE);
        chat->AddStatus(tr("User left the hub."));
    }

    // send friend event
    if (DCUsersList::Instance()) {
        DCFriendObject m_pFriendObject;

        m_pFriendObject.m_sName = nick;
        m_pFriendObject.m_eAwayMode = euamOFFLINE;
        m_pFriendObject.m_sHubName = QString::fromAscii(GetHubName().Data());
        m_pFriendObject.m_sHubHost = QString::fromAscii(GetHost().Data());
        QApplication::postEvent(DCUsersList::Instance(), new DC_FriendEvent(&m_pFriendObject));
    }

    if (DCConfig::Instance()->GetChatShowJoinsAndParts()) {
        if (DCConfig::Instance()->GetChatShowJoinsAndPartsOnlyFav()) {
            if (DCUsersList::Instance()->isNickInList(nick)) {
                m_pHubChat->AddStatus(nick + tr(" parted the chat."));
            }
        } else {
            m_pHubChat->AddStatus(nick + tr(" parted the chat."));
        }
    }

    UpdateStatusBar();
}

/** nicklist */
void DCClient::DC_NickList(CMessageNickList * MessageNickList) {
    CString * Nick = 0;
    QPixmap pm;
    QString nick;

    TreeView_USERLIST->setUpdatesEnabled(false);
    m_pUserListModel->clear();

    clearMyInfoCache();

    if (MessageNickList) {
        while ((Nick = MessageNickList->m_NickList.Next(Nick)) != 0) {
            if (Nick->IsEmpty()) {
                m_pHubChat->AddStatus(tr("Cannot display user with empty nick."));
                continue;
            }

            nick = QString::fromAscii(Nick->Data());

            if (m_pMyInfoHash->contains(nick) == false) {
                CMessageMyInfo * myinfo = new CMessageMyInfo();
                myinfo->m_sNick = (*Nick);
                myinfo->m_eAwayMode = euamNORMAL;

                m_pMyInfoHash->insert(nick, myinfo);

                pm = *DCConfig::Instance()->GetUserIcon(myinfo);

                m_pUserListModel->addUser(
                        nick,
                        pm
                        );
            }

            // send friend event
            if (DCUsersList::Instance()) {
                DCFriendObject m_pFriendObject;

                m_pFriendObject.m_sName = nick;
                m_pFriendObject.m_eAwayMode = euamONLINE;
                m_pFriendObject.m_sHubName = QString::fromAscii(GetHubName().Data());
                m_pFriendObject.m_sHubHost = QString::fromAscii(GetHost().Data());
                QApplication::postEvent(DCUsersList::Instance(), new DC_FriendEvent(&m_pFriendObject));
            }
        }
    }

    TreeView_USERLIST->setUpdatesEnabled(true);

    UpdateStatusBar();
}

/** oplist */
void DCClient::DC_OpList(CMessageOpList * MessageOpList) {
    CString * Nick = 0;
    QPixmap pm;
    QString nick;

    TreeView_USERLIST->setUpdatesEnabled(false);

    while ((Nick = MessageOpList->m_NickList.Next(Nick)) != 0) {
        if (Nick->IsEmpty()) {
            m_pHubChat->AddStatus(tr("Cannot display operator with empty nick."));
            continue;
        }

        nick = QString::fromAscii(Nick->Data());

        CMessageMyInfo * myinfo = m_pMyInfoHash->value(nick);

        if (myinfo == 0) {
            myinfo = new CMessageMyInfo();
            myinfo->m_sNick = (*Nick);
            myinfo->m_bOperator = true;
            myinfo->m_eAwayMode = euamNORMAL;

            m_pMyInfoHash->insert(nick, myinfo);

            // user not in the list, add it
            addUser(nick);

            pm = *DCConfig::Instance()->GetUserIcon(myinfo);

            m_pUserListModel->addUser(
                    nick,
                    pm
                    );

            m_pUserListModel->setSortTop(nick, true);
        } else {
            if (myinfo->m_bOperator == false) {
                myinfo->m_bOperator = true;

                pm = *DCConfig::Instance()->GetUserIcon(myinfo);

                m_pUserListModel->updateIcon(nick, pm);

                m_pUserListModel->setSortTop(nick, true);
            }
        }
    }

    TreeView_USERLIST->setUpdatesEnabled(true);

    UpdateStatusBar();
}

void DCClient::DC_PrivateChat(QString nick, QString fromnick, QString message, bool bLocal) {
    CMessagePrivateChat msg;

    msg.m_sSrcNick = nick.toAscii().constData();
    msg.m_sMultiSrcNick = fromnick.toAscii().constData();
    msg.m_sMessage = message.toAscii().constData();

    DC_PrivateChat(&msg, bLocal);
}

/** */
void DCClient::DC_PrivateChat(CMessagePrivateChat * msg, bool bLocal) {
    QWidget * focuswidget = 0;
    bool bShowChat = true;

    if (msg->m_sSrcNick.IsEmpty()) {
        return;
    }

    QString qnick = QString::fromAscii(msg->m_sSrcNick.Data());
    QString qmessage = QString::fromAscii(msg->m_sMessage.Data());

    // also check for ignored nick for PMs

    if (DCUsersList::Instance()->ignoreNick(qnick)) {
        //printf("Ignoring PM \"%s\" from \"%s\"\n", msg->m_sMessage.Data(), msg->m_sSrcNick.Data() );
        return;
    }

    bool in_gray = false;
    bool in_white = false;
    bool just_added = false;

    if (DCConfig::Instance()->GetAntiSpamEnabled()) {
        if (!DCAntiSpam::Instance()) {

            DCAntiSpam::SetInstance(new DCAntiSpam());

            DCAntiSpam::Instance()->loadLists();
            DCAntiSpam::Instance()->loadSettings();

        }

        if (DCAntiSpam::Instance()->isInBlack(qnick)) {
            return;
        } else if (DCAntiSpam::Instance()->isInGray(qnick)) {
            in_gray = true;
        } else if (DCAntiSpam::Instance()->isInWhite(qnick))
            in_white = true;

    } else {
        in_gray = true;
        in_white = true;
    }

    if (bLocal || (qnick == GetNick().Data()))//don't check outgoing messages
        in_gray = (in_white = true);

    DCChat * chat = m_ChatMap.value(qnick);

    // create a new chat window
    if (chat == 0) {
        focuswidget = qApp->focusWidget();

        if (m_bUseTabWidget) {
            chat = new DCChat(TabWidget_CHAT, this);
            chat->setObjectName("chat");
            chat->setAttribute(Qt::WA_DeleteOnClose);
        } else {
            chat = new DCChat(DCConnectionManager::Instance()->GetMdiArea(), this);
            chat->setObjectName("chat");
            chat->setAttribute(Qt::WA_DeleteOnClose);
            new QListWidgetItem(qnick, ListWidget_CHATUSERLIST);
            TabWidget_CHAT->setTabText(0, tr("Chat List") + " (" + QString().setNum(ListWidget_CHATUSERLIST->count()) + ")");
        }

        // fix hide if chat maximized
        /*if (in_gray || in_white) {
            chat->show();
            chat->hide();
        }*/

        chat->installEventFilter(this);
        chat->SetNick(qnick, QString::fromAscii(GetHubName().Data()));

        m_ChatMap.insert(qnick, chat);

        if ((DCConfig::Instance()->GetOpenPrivateChatWindow() == false) && (bLocal == false)) {
            bShowChat = false;
        }            // check for max lines and set chat invisible
        else if ((DCConfig::Instance()->GetChatMessageMaxLines() > 0) &&
                (QString(msg->m_sMessage.Data()).count("\n") > DCConfig::Instance()->GetChatMessageMaxLines())) {
            bShowChat = false;
        }

        // check for open chat windows nick exceptions
        DCConfigHubProfile pConfigHubProfile;
        QString nickregexp;

        if (DCConfig::Instance()->GetBookmarkHubProfile(GetHubName(), GetHost(), &pConfigHubProfile)) {
            nickregexp = pConfigHubProfile.m_sSuppressedNicks.Data();
        }

        if (nickregexp.isEmpty()) {
            nickregexp = DCConfig::Instance()->GetSuppressedNicks();
        }

        if (!nickregexp.isEmpty()) {
            QRegExp rx(nickregexp);
            rx.setCaseSensitivity(Qt::CaseInsensitive);
            if (qnick.contains(rx)) {
                bShowChat = (!DCConfig::Instance()->GetOpenPrivateChatWindow()) || bLocal;
            }
        }

        if (m_bUseTabWidget && (in_gray || in_white)) {
            TabWidget_CHAT->addTab(chat, qnick);
        }

        if ((bShowChat || bLocal) && (in_gray || in_white)) {
            if (m_bUseTabWidget) {
                TabWidget_CHAT->setCurrentWidget(chat);
            } else {
                DCConnectionManager::Instance()->GetMdiArea()->addSubWindow(chat);
                chat->setEnabled(true);
                chat->show();
            }
        } else {
            if (!m_bUseTabWidget) {
                chat->setEnabled(false);
                chat->hide();
            }

            // restore focus
            if (focuswidget)
                focuswidget->setFocus();
        }

        if (!in_gray && !in_white && DCConfig::Instance()->GetAntiSpamEnabled()) {
            sandbox << chat;

            just_added = true;

            chat->TextEdit_CHATINPUT->setPlainText(
                    QString("|AntiSpam Bot|: ") + tr("Your message was not delivered. "
                    "Answer the question and send message again, please. In the"
                    " case of incorrect answer you will be added in the black "
                    "list and your message will never arrive. So, the question is: ")
                    + DCAntiSpam::Instance()->getPhrase()
                    );
            chat->SendChat();
        }

        DCConfig::Instance()->PlaySound(eusFIRSTRECEIVE);
    } else if (bLocal) // enable all local (user input)
    {
        if (chat->isVisible() == false) {
            if (m_bUseTabWidget) {
                TabWidget_CHAT->setCurrentWidget(chat);
            } else {
                DCConnectionManager::Instance()->GetMdiArea()->addSubWindow(chat);
                chat->setEnabled(true);
                chat->show();
            }
        }

        m_pUserListModel->setHighlight(qnick, false);
    }

    if (msg->m_sMessage.NotEmpty()) {
        if (!just_added && !AntiSpamOK(chat, qnick, qmessage))
            return;

        chat->AddMessage(msg);

        if (chat->isVisible() == false) {
            m_pUserListModel->setHighlight(qnick, true);

            if (m_bUseTabWidget)
                TabWidget_CHAT->setTabIcon(TabWidget_CHAT->indexOf(chat), QIcon(DCIconLoader::Instance()->GetPixmap(eiMESSAGE)));
        } else {
            m_pUserListModel->setHighlight(qnick, false);
        }
    }

    if (!just_added && !AntiSpamOK(chat, qnick, qmessage))
        return;

    if ((chat->isVisible() == false) &&
            (DCConfig::Instance()->GetSendHidePrivateChatToPublicChat()) && (in_gray || in_white)) {
        QString nick;

        // send private chat to public chat
        if ((msg->m_sSrcNick != msg->m_sMultiSrcNick) && (msg->m_sMultiSrcNick.NotEmpty()))
            nick = qnick + "::" + QString::fromAscii(msg->m_sMultiSrcNick.Data());
        else
            nick = qnick;
        m_pHubChat->AddMessage(nick, qmessage, true, true);
    }

    // send chat event
    DCConnectionManager::Instance()->HubEvent(this);

    QString hub_name = QString::fromAscii(GetHubName().Data());

    if (hub_name.length() > 18)
        hub_name = hub_name.left(15) + "...";

    if (!bLocal && (in_gray || in_white))
	DCGuiApp::Instance()->showNotification(hub_name + " " + tr(": Private chat"),
					       qnick + ": " + qmessage,
					       eTrayMessageTypePRIV);

    if (DCConfig::Instance()->GetAutoResponderEnabledForPM()) {
        doAutoResponse(qnick, qmessage, chat);
    }
}

/** */
void DCClient::DC_UserCommand(CMessageUserCommand * msg) {
    DC_UserMenuCommand * umc = 0;

    QString qname = QString::fromAscii(msg->name.Data());
    QString qcommand = QString::fromAscii(msg->command.Data());

    if (msg->type == euctClear) {
        if (msg->context == euccMask) // clear all
        {
            qDeleteAll(hubcommands);
            hubcommands.clear();
            return;
        }

        for (int i = 0; i < hubcommands.size(); i++) {
            umc = hubcommands.at(i);

            umc->m_nContext -= (umc->m_nContext & msg->context);

            if (umc->m_nContext == 0) {
                delete umc;
                hubcommands.removeAt(i);
                i--;
            }
        }
    } else if (msg->type == euctRemove) {
        for (int i = 0; i < hubcommands.size(); i++) {
            umc = hubcommands.at(i);

            if (umc->m_sName == qname) {
                umc->m_nContext -= (umc->m_nContext & msg->context);

                if (umc->m_nContext == 0) {
                    delete umc;
                    hubcommands.removeAt(i);
                }

                return; // only remove one command
            }
        }
    } else if (msg->type == euctSeparator) // separators might not have names
    {
        umc = new DC_UserMenuCommand();

        umc->m_nType = msg->type;
        umc->m_nContext = msg->context;
        umc->m_sName = qname;
        umc->m_sCommand = qcommand;

        hubcommands.append(umc);
    } else if ((msg->type == euctRaw) || (msg->type == euctRawOnce)) {
        // update existing command
        for (int i = 0; i < hubcommands.size(); i++) {
            umc = hubcommands.at(i);

            if (umc->m_sName == qname) {
                umc->m_nType = msg->type;
                umc->m_nContext = umc->m_nContext | msg->context;
                umc->m_sCommand = qcommand;
                return;
            }
        }

        umc = new DC_UserMenuCommand();
        umc->m_nType = msg->type;
        umc->m_nContext = msg->context;
        umc->m_sName = qname;
        umc->m_sCommand = qcommand;

        hubcommands.append(umc);
    } else {
        printf("Unknown type %d for command '%s'\n", msg->type, msg->name.Data());
    }
}

/** handling if user join the hub */
void DCClient::addUser(QString nick) {
    DCChat * chat = m_ChatMap.value(nick);

    // show joins
    if (DCConfig::Instance()->GetChatShowJoinsAndParts() && allow_show_joins) {
        if (DCConfig::Instance()->GetChatShowJoinsAndPartsOnlyFav()) {
            if (DCUsersList::Instance()->isNickInList(nick)) {
                m_pHubChat->AddStatus(nick + tr(" joined the chat."));
            }
        } else {
            m_pHubChat->AddStatus(nick + tr(" joined the chat."));
        }
    }

    // show rejoin on private chat
    if (chat != 0 && allow_show_joins) {
        chat->AddStatus(tr("User rejoin the hub."));
    }

    // send event to friendlist
    if (DCUsersList::Instance()) {
        DCFriendObject m_pFriendObject;

        m_pFriendObject.m_sName = nick;
        m_pFriendObject.m_eAwayMode = euamONLINE;
        m_pFriendObject.m_sHubName = QString::fromAscii(GetHubName().Data());
        m_pFriendObject.m_sHubHost = QString::fromAscii(GetHost().Data());
        QApplication::postEvent(DCUsersList::Instance(), new DC_FriendEvent(&m_pFriendObject));
    }
}

/** */
void DCClient::CloseChat(DCChat * chat) {
    chat->removeEventFilter(this);

    m_ChatMap.remove(chat->GetNick());

    if (m_bUseTabWidget) {
        TabWidget_CHAT->removeTab(TabWidget_CHAT->indexOf(chat));
    } else {
        QList<QListWidgetItem*> list = ListWidget_CHATUSERLIST->findItems(chat->GetNick(), Qt::MatchExactly);
        for (int i = 0; i < list.size(); i++) {
            ListWidget_CHATUSERLIST->removeItemWidget(list.at(i));
            delete list.at(i);
            TabWidget_CHAT->setTabText(0, tr("Chat List") + " (" + QString().setNum(ListWidget_CHATUSERLIST->count()) + ")");
        }
    }

    chat->close();
}

/** */
void DCClient::CloseAllChats(bool onlyOffline) {
    if (onlyOffline) {
        QList<DCChat*> toclose;

        for (QMap<QString, DCChat*>::const_iterator it = m_ChatMap.constBegin(); it != m_ChatMap.constEnd(); ++it) {
            if (m_pMyInfoHash->contains(it.key()) == false) {
                toclose.append(it.value());
            }
        }

        for (int i = 0; i < toclose.size(); ++i) {
            CloseChat(toclose.at(i));
        }
    } else {
        while (!m_ChatMap.isEmpty()) {
            CloseChat(m_ChatMap.begin().value());
        }
    }
}

/** */
void DCClient::slotDoubleClickedUserList(const QModelIndex & index) {
    if (!index.isValid()) {
        return;
    }

    QModelIndex nickindex = TreeView_USERLIST->model()->index(index.row(), COLUMN_NICK, QModelIndex());
    QString nick = TreeView_USERLIST->model()->data(nickindex).toString();

    if (DCConfig::Instance()->GetDoubleClickAction() == eDownloadList) {
        CString empty;
        DCTransferView::Instance()->DLM_QueueAdd(
                nick.toAscii().constData(),
                GetHubName(),
                GetHost(),
                DC_USER_FILELIST,
                DC_USER_FILELIST,
                empty,
                empty,
                eltBUFFER,
                0,
                0,
                0,
                empty
                );
    } else {
        DC_PrivateChat(nick, QString(), QString(), true);
    }
}

/** */
void DCClient::slotDoubleClickedChatUserList(QListWidgetItem * item) {
    QString nick;

    if (item == 0) {
        return;
    }

    nick = item->text();

    if (nick.isEmpty()) {
        return;
    }

    DCChat * chat = m_ChatMap.value(nick);

    if (chat != 0) {
        if (!m_bUseTabWidget)
            chat->setEnabled(true);

        // take widget to top
        if (chat->isMinimized()) {
            chat->showNormal();
        } else if (chat->isVisible()) {
            // TODO: dont work fix it
            chat->show();
            chat->raise();
        } else {
            DCConnectionManager::Instance()->GetMdiArea()->addSubWindow(chat);
            chat->show();
        }

        m_pUserListModel->setHighlight(nick, false);
    }
}

/** */
void DCClient::slotHubConnect() {
    if (GetConnectionState() == estCONNECTED) {
        SetConnection(false);
        Disconnect();
    }
    else
        reconnect();
}

/** */
void DCClient::slotContextMenuUserList(const QPoint &) {
    QMap<QAction*, DC_UserMenuCommand*> addedcommands;

    QStringList selectedNicks;

    QModelIndexList milist = TreeView_USERLIST->selectionModel()->selectedRows(COLUMN_NICK);

    for (QModelIndexList::const_iterator it = milist.constBegin(); it != milist.constEnd(); ++it) {
        selectedNicks.append(TreeView_USERLIST->model()->data(*it).toString());
    }

    const int numSelected = selectedNicks.size();

    QMenu * m = new QMenu(this);

    if (numSelected == 1) {
        DCMenuHandler::addAction(m, emiUSER_CAPTION, false, tr("User: ") + selectedNicks.first());
    } else {
        DCMenuHandler::addAction(m, emiUSER_CAPTION, false, QString::number(numSelected) + tr(" users"));
    }

    DCMenuHandler::addAction(m, emiSEPARATOR);
    QAction * privchat = DCMenuHandler::addAction(m, emiPRIVATE_CHAT, (numSelected > 0));
    QAction * to_chat = DCMenuHandler::addAction(m, emiTO_CHAT, (numSelected > 0));
    QAction * addfriend = DCMenuHandler::addAction(m, emiADD_FRIEND, (numSelected > 0));
    QAction * browse = DCMenuHandler::addAction(m, emiBROWSE_USER_FILES, (numSelected > 0));
    DCMenuHandler::addAction(m, emiSEPARATOR);

    QMenu * mslot = DCMenuHandler::addMenu(m, emiUPLOAD_SLOT, true);
    QAction * addslot = DCMenuHandler::addAction(mslot, emiADD, (numSelected > 0));
    QAction * addperm = DCMenuHandler::addAction(mslot, emiADD_PERMANENT, (numSelected > 0));
    QAction * remslot = DCMenuHandler::addAction(mslot, emiREMOVE, (numSelected > 0));

    DCMenuHandler::addAction(m, emiSEPARATOR);
    QAction * upduser = 0;
    if (GetSupportsNoGetInfo() == false) {
        upduser = DCMenuHandler::addAction(m, emiUPDATE_USER, (numSelected > 0));
    }
    QAction * reloadlist = DCMenuHandler::addAction(m, emiRELOAD_USERLIST, (GetConnectionState() == estCONNECTED));
    DCMenuHandler::addAction(m, emiSEPARATOR);
    QAction * copycol = DCMenuHandler::addAction(m, emiCOPY_COLUMN_TO_CLIPBOARD, (numSelected == 1));
    QAction * copyrow = DCMenuHandler::addAction(m, emiCOPY_ROW_TO_CLIPBOARD, (numSelected > 0));

    DCMenuHandler::addAction(m, emiSEPARATOR);
    QAction * checkver = DCMenuHandler::addAction(m, emiCHECK_CLIENT_VERSION, numSelected > 0);

    QAction * kick = 0;
    QAction * forcemove = 0;

    if (UserList()->IsAdmin(GetNick())) {
        kick = DCMenuHandler::addAction(m, emiKICK, (numSelected > 0));
        forcemove = DCMenuHandler::addAction(m, emiFORCE_MOVE, (numSelected > 0));
    }

    addedcommands = AddMenuCommands(m, euccChat);

    QList<QAction*> addedactions = addedcommands.keys();

    for (int i = 0; i < addedactions.size(); i++) {
        addedactions[i]->setEnabled(numSelected > 0);
    }

    QAction * chosen = m->exec(QCursor::pos());

    delete m;

    if (chosen == 0) {
        return;
    } else if (chosen == privchat) {
        for (QStringList::const_iterator it = selectedNicks.constBegin(); it != selectedNicks.constEnd(); ++it) {
            DC_PrivateChat(*it, QString::null, QString::null, true);
        }
    } else if (chosen == to_chat) {
        for (QStringList::const_iterator it = selectedNicks.constBegin(); it != selectedNicks.constEnd(); ++it) {
            DCChat::ActiveChat()->addStringToChat(it->toAscii().constData());
        }
    } else if (chosen == addfriend) {
        QString qhubname = QString::fromAscii(GetHubName().Data());
        QString qhubhost = QString::fromAscii(GetHost().Data());
        for (QStringList::const_iterator it = selectedNicks.constBegin(); it != selectedNicks.constEnd(); ++it) {
            DCUsersList::Instance()->AddFriend(*it, qhubname, qhubhost, QString::null);
        }
    } else if (chosen == browse) {
        if (GetMode() == ecmNONE) {
            return;
        }

        CString empty;
        for (QStringList::const_iterator it = selectedNicks.constBegin(); it != selectedNicks.constEnd(); ++it) {
            /** add transfer to the waitlist */
            DCTransferView::Instance()->DLM_QueueAdd(it->toAscii().constData(), GetHubName(), GetHost(),
                    DC_USER_FILELIST, DC_USER_FILELIST, empty, empty, eltBUFFER,
                    0, 0, 0, empty);
        }
    } else if (chosen == checkver) {
        if (GetMode() == ecmNONE) {
            return;
        }

        CString empty;
        for (QStringList::const_iterator it = selectedNicks.constBegin(); it != selectedNicks.constEnd(); ++it) {
            /** add transfer to the waitlist */
            DCTransferView::Instance()->DLM_QueueAdd(it->toAscii().constData(), GetHubName(), GetHost(),
                    "client check", "client check", empty, empty, eltCLIENTVERSION,
                    0, 0, 0, empty);
        }
    } else if (chosen == upduser) {
        for (QStringList::const_iterator it = selectedNicks.constBegin(); it != selectedNicks.constEnd(); ++it) {
            SendGetInfo(it->toAscii().constData(), GetNick());
        }
    } else if (chosen == copycol) {
        QModelIndex index = TreeView_USERLIST->selectionModel()->currentIndex();

        QApplication::clipboard()->setText(TreeView_USERLIST->model()->data(index).toString());
    } else if (chosen == copyrow) {
        QString s;

        QModelIndexList indexes = TreeView_USERLIST->selectionModel()->selectedRows(COLUMN_NICK);

        for (QModelIndexList::const_iterator it = indexes.constBegin(); it != indexes.constEnd(); ++it) {
            for (int j = 0; j < TreeView_USERLIST->model()->columnCount(); j++) {
                int col = TreeView_USERLIST->header()->logicalIndex(j);
                if (TreeView_USERLIST->header()->isSectionHidden(col) == false) {
                    QModelIndex index = TreeView_USERLIST->model()->index(it->row(), col, QModelIndex());
                    s += TreeView_USERLIST->model()->data(index).toString();
                    s += " ";
                }
            }

            s += "\n";
        }

        s = s.trimmed();
        QApplication::clipboard()->setText(s);
    } else if (chosen == addslot) {
        for (QStringList::const_iterator it = selectedNicks.constBegin(); it != selectedNicks.constEnd(); ++it) {
            DCTransferView::Instance()->DLM_AddUserSlot(it->toAscii().constData(), GetHubName(), 1);
        }
    } else if (chosen == addperm) {
        for (QStringList::const_iterator it = selectedNicks.constBegin(); it != selectedNicks.constEnd(); ++it) {
            DCTransferView::Instance()->DLM_AddUserSlot(it->toAscii().constData(), GetHubName(), 0, true);
        }
    } else if (chosen == remslot) {
        for (QStringList::const_iterator it = selectedNicks.constBegin(); it != selectedNicks.constEnd(); ++it) {
            DCTransferView::Instance()->DLM_AddUserSlot(it->toAscii().constData(), GetHubName(), 0);
        }
    } else if (chosen == kick) {
        QString kickmessage;

        if (!GetOPKickMessage(kickmessage, this)) {
            return;
        }

        for (QStringList::const_iterator it = selectedNicks.constBegin(); it != selectedNicks.constEnd(); ++it) {
            OPKick(*it, kickmessage);
        }
    } else if (chosen == forcemove) {
        QString host, message;

        if (!GetOPForceMoveMessage(message, host, this)) {
            return;
        }

        for (QStringList::const_iterator it = selectedNicks.constBegin(); it != selectedNicks.constEnd(); ++it) {
            OPForceMove(*it, message, host);
        }
    } else if (chosen == reloadlist) {
        RequestNickList();
    } else if (addedcommands.contains(chosen)) {
        DC_UserMenuCommand * umc = addedcommands[chosen];

        QString origUserCommand = umc->m_sCommand;

        QString usercommand;

        for (QStringList::const_iterator it = selectedNicks.constBegin(); it != selectedNicks.constEnd(); ++it) {
            usercommand = replaceCommandTags(origUserCommand, *it);

            if (!usercommand.isEmpty()) {
                m_pHubChat->AddStatus(usercommand);
                SendString(usercommand.toAscii().constData());
            }
        }
    }
}

/** */
bool DCClient::GetOPKickMessage(QString & message, QWidget * parent) {
    bool ok = false;

    message = QInputDialog::getText(
            parent,
            tr("OP Kick"),
            tr("Please enter a reason"),
            QLineEdit::Normal, QString::null, &ok);

    return ok;
}

/** */
bool DCClient::GetOPForceMoveMessage(QString & message, QString & host, QWidget * parent) {
    bool ret = false;

    QDialog * dialog = new QDialog(parent);
    Ui::DCDialogForceMove ui;
    ui.setupUi(dialog);

    if (dialog->exec() == QDialog::Accepted) {
        host = ui.LineEdit_HOST->text();
        message = ui.LineEdit_MESSAGE->text();
        ret = true;
    }

    delete dialog;

    return ret;
}

/** */
bool DCClient::OPKick(QString nick, QString message) {
    bool res = false;

    SendPrivateMessage(GetNick(), nick.toAscii().constData(), ("You are being kicked because: " + message).toAscii().constData());
    SendChat(GetNick(), GetNick() + " is kicking " + nick.toAscii().constData() + " because: " + message.toAscii().constData());
    SendKick(nick.toAscii().constData());

    return res;
}

/** */
bool DCClient::OPForceMove(QString nick, QString message, QString host) {
    bool res = false;

    SendOpForceMove(nick.toAscii().constData(), host.toAscii().constData(), message.toAscii().constData());

    return res;
}

/** */
void DCClient::slotDoubleClickedUserChat(QListWidgetItem * item) {
    QString nick;

    if (item == 0) {
        return;
    }

    nick = item->text();

    if (nick.isEmpty()) {
        return;
    }

    DCChat * chat = m_ChatMap.value(nick);

    if (chat != 0) {
        if (!m_bUseTabWidget)
            chat->setEnabled(true);
        chat->show();
        m_pUserListModel->setHighlight(nick, false);
    }
}

/** */
QString DCClient::findNick(QString part, int which) {
    /*
        for(uint i=0; i < nicks->count(); i++)
        {
            if (matches.contains(nicks->text(i)))
                continue;
            if(qstrlen(nicks->text(i)) >= part.length())
            {
                if(qstrnicmp(part, nicks->text(i), part.length()) == 0)
                {
                    QString qsNick = ksopts->nick;
                    if(qstrcmp(nicks->text(i), qsNick) != 0)
                    { // Don't match your own nick
                        matches.append(nicks->text(i));
                    }
                }
            }
        }
     */
    QStringList matches;

    QString tlpart = part.trimmed().toLower();

    if (tlpart == lastPart) {
        matches = lastMatches;
    } else {
        if (DCConfig::Instance()->GetTabCompleteMatchMode() == eTabContains) {
            matches = m_pUserListModel->matchNicksContaining(tlpart, DCConfig::Instance()->GetTabCompleteStripTags());
        } else {
            matches = m_pUserListModel->matchNicksStartingWith(tlpart, DCConfig::Instance()->GetTabCompleteStripTags());
        }

        lastMatches = matches;
        lastPart = tlpart;
    }

    //printf("matches='%s'\n", matches.join(",").toAscii().constData());

    if (matches.count() > 0) {
        if (which < matches.count())
            return matches.at(which);
        else
            return QString::null;
    }

    return part;
}

/*void DCClient::slotTabCornerCloseWidgetClicked()
{
    CloseChat( (DCChat*) TabWidget_CHAT->currentWidget() );
}*/

/** Replaces the %[tag] s in the command with the proper values */
QString DCClient::replaceCommandTags(QString & command, QString remotenick) {
    QString usercommand = command;

    usercommand.replace("%[mynick]", GetNick().Data());
    usercommand.replace("%[myNI]", GetNick().Data());

    QString comtag = GetComment().Data();
    QString comment = comtag.mid(0, comtag.indexOf('<'));
    QString tag = comtag.mid(comtag.indexOf('<'));

    usercommand.replace("%[mytag]", tag);
    usercommand.replace("%[myTAG]", tag);

    usercommand.replace("%[mydescription]", comment);
    usercommand.replace("%[myDE]", comment);

    usercommand.replace("%[myemail]", GetEMail().Data());
    usercommand.replace("%[myEM]", GetEMail().Data());

    const QString myss = QString::number(GetShareSize());
    usercommand.replace("%[myshare]", myss);
    usercommand.replace("%[mySS]", myss);

    const QString myssshort = DCGuiUtils::GetSizeString(GetShareSize());
    usercommand.replace("%[myshareshort]", myssshort);
    usercommand.replace("%[mySSshort]", myssshort);

    const QString myip = GetExternalIP(false).Data();
    usercommand.replace("%[myip]", myip);
    usercommand.replace("%[myI4]", myip);

    usercommand.replace("%[hub]", GetHubName().Data());
    usercommand.replace("%[hubNI]", GetHubName().Data());

    usercommand.replace("%[hubURL]", GetHost().Data());

    usercommand.replace("%[hubIP]", GetResolvedIP().Data());
    usercommand.replace("%[hubI4]", GetResolvedIP().Data());

    usercommand.replace("%[nick]", remotenick);
    usercommand.replace("%[userNI]", remotenick);

    CMessageMyInfo * myinfo = m_pMyInfoHash->value(remotenick);

    if (myinfo != 0) {
        usercommand.replace("%[userTAG]", myinfo->m_sVerComment.Data());
        usercommand.replace("%[tag]", myinfo->m_sVerComment.Data());

        usercommand.replace("%[userDE]", myinfo->m_sComment.Data());
        usercommand.replace("%[description]", myinfo->m_sComment.Data());

        usercommand.replace("%[userSS]", QString::number(myinfo->m_nShared));
        usercommand.replace("%[share]", QString::number(myinfo->m_nShared));

        QString ss = DCGuiUtils::GetSizeString(myinfo->m_nShared);
        usercommand.replace("%[userSSshort]", ss);
        usercommand.replace("%[shareshort]", ss);

        usercommand.replace("%[userEM]", myinfo->m_sEMail.Data());
        usercommand.replace("%[email]", myinfo->m_sEMail.Data());

        usercommand.replace("%[userI4]", myinfo->m_sTransferHost.Data());
        usercommand.replace("%[ip]", myinfo->m_sTransferHost.Data());
    }

    if (usercommand.contains("%[line:")) {
        QDialog * dialog = new QDialog(this);
        Ui::DCDialogUserCommandLines ui;
        ui.setupUi(dialog);

        ui.LineEdit_NICK->setText(remotenick);
        ui.LineEdit_HUB->setText(QString::fromAscii(GetHubName().Data()));

        QMap<QString, QLineEdit*> lineeditmap;
        QWidget * container = new QWidget(dialog);
        QGridLayout * grid = new QGridLayout(container);

        int loc = 0, line = 0, end = 0;

        for (;;) {
            loc = usercommand.indexOf("%[line:", loc);
            end = usercommand.indexOf("]", loc);

            if ((loc == -1) || (end == -1)) {
                break;
            }

            // 7 is length of "%[line:"
            QString reason = usercommand.mid(loc + 7, end - (loc + 7));

            QString key = "%[line:" + reason + "]";

            if (!lineeditmap.contains(key)) {
                QLabel * label = new QLabel(container);
                label->setText(reason);
                grid->addWidget(label, line, 0);

                QLineEdit * edit = new QLineEdit(container);
                grid->addWidget(edit, line, 1);

                lineeditmap.insert(key, edit);
                ++line;
            }

            loc = end + 1;
        }

        container->setLayout(grid);
        ui.ScrollArea->setWidgetResizable(true);
        ui.ScrollArea->setWidget(container);

        if (dialog->exec() == QDialog::Accepted) {
            for (QMap<QString, QLineEdit*>::const_iterator it = lineeditmap.constBegin(); it != lineeditmap.constEnd(); ++it) {
                QString text = it.value()->text();
                text.replace("$", "&#36;");
                text.replace("|", "&#124;");

                usercommand.replace(it.key(), text);

                if (ui.CheckBox_ALL_NICKS->isChecked()) {
                    command.replace(it.key(), text);
                }
            }
            delete dialog;
        } else {
            delete dialog;
            return QString();
        }
    }

    time_t ti = time(0);
    struct tm * t = localtime(&ti);
    QByteArray timebuf;
    timebuf.resize(usercommand.toAscii().size() + 512);

    /*
     * If strftime returns 0 either the buffer was not big enough
     * or the actual output is zero characters.
     */
    for (int tries = 0; tries < 10; ++tries) {
        if (strftime(timebuf.data(), timebuf.size(), usercommand.toAscii().constData(), t) > 0) {
            usercommand = QString::fromAscii(timebuf.constData());
            break;
        } else {
            timebuf.resize(timebuf.size() + 128);
        }
    }

    return usercommand;
}

void DCClient::doAutoResponse(QString nick, QString message, DCChat * chatobject) {
    long delay = DCConfig::Instance()->GetAutoResponseDelay();
    long now = QDateTime::currentDateTime().toTime_t();

    if ((delay > 0) &&
            (nick == lastAutoNick) &&
            (now < delay + lastAutoResponseTime)
            ) {
        // avoid spamming
        //printf("Auto response spam protection!\n");
        return;
    }

    // null pointer check
    if (chatobject == 0) {
        printf("Error: DCClient::doAutoResponse called with NULL chatobject!\n");
        return;
    }

    // do not respond to ourself
    if (nick == QString::fromAscii(GetNick().Data())) {
        //printf("Not auto-responding to ourself.\n");
        return;
    }

    // check for ignored nicks
    QString ignores = DCConfig::Instance()->GetAutoResponseIgnores();
    if (!ignores.isEmpty()) {
        QRegExp ignore_re;
        ignore_re.setPattern(ignores);
        ignore_re.setCaseSensitivity(Qt::CaseInsensitive);
        if (nick.contains(ignore_re)) {
            //printf("Not auto responding to %s\n", nick.toAscii().constData());
            return;
        }
    }

    QList<DC_AutoResponseObject*> arlist;
    DC_AutoResponseObject * aro = 0;

    DCConfig::Instance()->GetAutoResponses(&arlist);

    QRegExp re;

    for (QList<DC_AutoResponseObject*>::const_iterator it = arlist.constBegin(); it != arlist.constEnd(); ++it) {
        aro = *it;
        re.setPattern(aro->m_sTrigger);

        if (aro->m_bCaseSensitive) {
            re.setCaseSensitivity(Qt::CaseSensitive);
        } else {
            re.setCaseSensitivity(Qt::CaseInsensitive);
        }

        if (message.contains(re)) // pattern matches
        {
            QString origResponse = aro->m_sResponse;
            QString chat = replaceCommandTags(origResponse, nick);

            if (chat.isEmpty()) {
                break;
            }

            //SendChat( GetNick(), chat.toAscii().constData() );
            chatobject->TextEdit_CHATINPUT->setPlainText(chat);
            chatobject->SendChat();

            // update last nick and time for anti-spam
            lastAutoNick = nick;
            lastAutoResponseTime = QDateTime::currentDateTime().toTime_t();

            // stop searching list of triggers
            break;
        }
    }

    for (QList<DC_AutoResponseObject*>::const_iterator it = arlist.constBegin(); it != arlist.constEnd(); ++it) {
        delete *it;
    }
}

/** */
bool DCClient::jumpToNick(const QString & nick) {
    if (nick.isEmpty()) {
        return false;
    }

    QModelIndex index = m_pUserListModel->indexForNick(nick);

    if (!index.isValid()) {
        return false;
    }

    if (m_pProxyModel) {
        index = m_pProxyModel->mapFromSource(index);

        if (!index.isValid()) {
            return false;
        }
    }

    TreeView_USERLIST->selectionModel()->select(index, QItemSelectionModel::Clear | QItemSelectionModel::Select | QItemSelectionModel::Current | QItemSelectionModel::Rows);
    TreeView_USERLIST->scrollTo(index, QAbstractItemView::EnsureVisible);
    return true;
}

/** */
void DCClient::disableUserListSorting() {
    TreeView_USERLIST->setSortingEnabled(false);
    m_pUserListModel->sort(-1);
}

/** */
void DCClient::enableUserListSorting() {
    TreeView_USERLIST->setSortingEnabled(true);
    TreeView_USERLIST->sortByColumn(COLUMN_NICK, Qt::AscendingOrder);
}

/** */
void DCClient::centreOnMdiArea() {
    // centre window within workspace otherwise the top left corner
    // ends up in the centre so most of it is off the screen
    if ((DCConnectionManager::Instance()->GetMdiArea() != 0) &&
            (m_pContainerWindow != 0) &&
            (m_pContainerWindow->isMinimized() == false) &&
            (m_pContainerWindow->isMaximized() == false)
            ) {
        int newx = (DCConnectionManager::Instance()->GetMdiArea()->width() - m_pContainerWindow->width()) / 2;
        int newy = (DCConnectionManager::Instance()->GetMdiArea()->height() - m_pContainerWindow->height()) / 2;

        if (newx < 0) {
            newx = 0;
        }

        if (newy < 0) {
            newy = 0;
        }

        m_pContainerWindow->move(newx, newy);
    }
}

/** */
QMap<QAction*, DC_UserMenuCommand*> DCClient::AddMenuCommands(QMenu * menu, int context) {
    QMap<QAction*, DC_UserMenuCommand*> added;
    QList<DC_UserMenuCommand*> allcommands;
    QMap<QString, QMenu*> createdmenus;
    QMenu * prevmenu = 0;

    if (!menu) {
        return added;
    }

    if (DCConfig::Instance()->GetEnableUserCommand()) {
        allcommands += hubcommands;
    }

    QMap<int, DC_UserMenuCommand*> usercommands;

    DCConfig::Instance()->GetUserMenuCommandsDirect(&usercommands);

    allcommands += usercommands.values();

    usercommands.clear();

    if (DCConfig::Instance()->GetUserCommandSubmenu()) {
        menu = DCMenuHandler::addMenu(menu, emisUSER_COMMANDS);
    }

    for (int i = 0; i < allcommands.size(); i++) {
        if ((allcommands[i]->m_nContext & context) == context) {
            if (!(allcommands[i]->m_sHubIP.isEmpty())) {
                QString hubiplc = allcommands[i]->m_sHubIP.toLower();
                if ((hubiplc == "op") && (UserList()->IsAdmin(GetNick()) == false)) {
                    //printf("Skipping command '%s' because we are not OP here\n",allcommands[i]->m_sName.toAscii().constData());
                    continue;
                } else if ((hubiplc != QString::fromAscii(GetHost().ToLower().Data())) && (hubiplc != QString::fromAscii(GetIP().ToLower().Data()))) {
                    //printf("Skipping command '%s' because host/IP does not match (required=%s our host=%s its ip=%s)\n",allcommands[i]->m_sHubIP.toAscii().constData(),GetHost().Data(),GetIP().Data());
                    continue;
                }
            }

            QMenu * submenu = menu;
            QString name = allcommands[i]->m_sName;

            if (name.contains('\\')) {
                QStringList subMenuNames = name.split('\\', QString::SkipEmptyParts);
                name = subMenuNames.takeLast(); // remove and store command name

                QString subMenuKey;
                QMenu * parentMenu = menu;

                for (int j = 0; j < subMenuNames.size(); j++) {
                    subMenuKey += subMenuNames[j];

                    if (createdmenus.contains(subMenuKey)) {
                        parentMenu = createdmenus[subMenuKey];
                        submenu = parentMenu;
                    } else {
                        parentMenu = parentMenu->addMenu(subMenuNames[j]);
                        createdmenus[subMenuKey] = parentMenu;
                        submenu = parentMenu;
                    }

                    subMenuKey += '\\';
                }
            }

            if (allcommands[i]->m_nType == euctSeparator) {
                // no need to keep the QAction returned
                // the protocol was badly designed, separators do not have names
                // and so cannot be removed from menus
                // whatever docs exist on random forum posts etc.
                // also say separators are supposed to be a "vertical bar"
                // valknut currently uses horizontal bars
                if (allcommands[i]->m_sName.isEmpty() && prevmenu) {
                    prevmenu->addSeparator();
                } else {
                    submenu->addSeparator();
                }
            } else if ((allcommands[i]->m_nType == euctRaw) || (allcommands[i]->m_nType == euctRawOnce)) {
                added.insert(submenu->addAction(name), allcommands[i]);
                prevmenu = submenu;
            }
            //else
            //{
            //	printf("AddMenuCommands: wrong command type %d in lists for command '%s'\n", allcommands[i]->m_nType, allcommands[i]->m_sName.Data());
            //}
        }
    }

    if (DCConfig::Instance()->GetUserCommandSubmenu() && (added.size() == 0)) {
        menu->setEnabled(false);
    }

    return added;
}

/** */
void DCClient::clearMyInfoCache() {
    for (QHash<QString, CMessageMyInfo*>::const_iterator it = m_pMyInfoHash->constBegin(); it != m_pMyInfoHash->constEnd(); ++it) {
        delete it.value();
    }

    m_pMyInfoHash->clear();
}

/** */
void DCClient::slotContextMenuTabWidgetChat(const QPoint & pos) {
    if ((TabWidget_CHAT->currentWidget() != 0)
            && (TabWidget_CHAT->currentWidget() != Tab_CHATUSERLIST)) {
        ((DCChat*) TabWidget_CHAT->currentWidget())->slotRightButtonClickedChatOutput(pos);
    }
}

/** */
void DCClient::slotFilterToggled(bool enabled) {
    LineEdit_FILTER->setEnabled(enabled);
    ComboBox_FILTER->setEnabled(enabled);

    if (enabled) {
        /* it should be NULL */
        delete m_pProxyModel;

        m_pProxyModel = new FilterOnlyProxy(this);
        m_pProxyModel->setDynamicSortFilter(true);
        m_pProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
        m_pProxyModel->setSourceModel(m_pUserListModel);
        m_pProxyModel->setFilterFixedString(LineEdit_FILTER->text());

        if (ComboBox_FILTER->currentIndex() == m_pUserListModel->columnCount()) {
            m_pProxyModel->setFilterKeyColumn(-1);
        } else {
            m_pProxyModel->setFilterKeyColumn(ComboBox_FILTER->currentIndex());
        }

        TreeView_USERLIST->setModel(m_pProxyModel);
    } else {
        TreeView_USERLIST->setModel(m_pUserListModel);
        delete m_pProxyModel;
        m_pProxyModel = 0;
    }

    UpdateStatusBar();
}

/** */
void DCClient::slotFilterColumn(int column) {
    if (m_pProxyModel) {
        if (column == m_pUserListModel->columnCount()) {
            m_pProxyModel->setFilterKeyColumn(-1);
        } else {
            m_pProxyModel->setFilterKeyColumn(column);
        }

        UpdateStatusBar();
    }
}

/** */
void DCClient::slotFilterString(const QString & text) {
    if (m_pProxyModel) {
        m_pProxyModel->setFilterFixedString(text);
        UpdateStatusBar();
    }
}

/** */
void DCClient::slotContextMenuHeader(const QPoint & /* pos */) {
    QMenu * mcols = new QMenu(this);
    QAction * column;
    int index;

    for (int i = 0; i < m_pUserListModel->columnCount(); ++i) {
        index = TreeView_USERLIST->header()->logicalIndex(i);
        column = mcols->addAction(m_pUserListModel->headerData(index, Qt::Horizontal).toString());
        column->setCheckable(true);
        if (index == 0) {
            column->setEnabled(false);
        }
        column->setChecked(TreeView_USERLIST->header()->isSectionHidden(index) == false);
        column->setData(index);
    }

    mcols->addSeparator();
    QAction * save = DCMenuHandler::addAction(mcols, emiSAVE);
    QAction * chosen = mcols->exec(QCursor::pos());

    if (chosen == save) {
        saveColumns();
    } else if (chosen) {
        index = chosen->data().toInt();
        if (TreeView_USERLIST->header()->isSectionHidden(index)) {
            TreeView_USERLIST->header()->showSection(index);
        } else {
            TreeView_USERLIST->header()->hideSection(index);
        }
    }

    delete mcols;
}

/** */
void DCClient::slotAltRowColors(bool enable) {
    TreeView_USERLIST->setAlternatingRowColors(enable);
}

/** */
void DCClient::saveColumns() {
    StringMap * map;
    DCConfig::Instance()->GetMap("USERLISTCOLUMNORDER", map);
    (*map)["USERLISTHEADERSTATE"] = QString(TreeView_USERLIST->header()->saveState().toBase64().constData());
}


/** */
void DCClient::slotCloseTabButtonClicked(int index)
{
    QWidget *wgt = TabWidget_CHAT->widget(index);

    if (wgt){
        wgt->close();
    }
}



/** */
bool DCClient::AntiSpamOK(DCChat *chat, QString qnick, QString qmessage) {
    if (!chat || (qnick == ""))
        return false;

    if (DCAntiSpam::Instance() && sandbox.contains(chat)) {

        if (!DCAntiSpam::Instance()->getKeys().contains(qmessage.toUpper())) {

            if (DCAntiSpam::Instance()->getAttempts() == 0 || (nick_attempts.contains(qnick) && nick_attempts[qnick] <= 1)){//1 - last attempt
                DCAntiSpam::Instance()->addToBlack(QList<QString > () << qnick);

                chat->TextEdit_CHATINPUT->setPlainText("|AntiSpam Bot|: "
                                                       + tr("Now you blacklisted. Bye."));

                chat->SendChat();
                chat->close();

                m_ChatMap.remove(qnick);

                int index = sandbox.indexOf(chat);
                sandbox.removeAt(index);
            }
            else {
                if (!nick_attempts.contains(qnick))
                    nick_attempts.insert(qnick, DCAntiSpam::Instance()->getAttempts()-1);//one attemt is already used
                else
                    nick_attempts[qnick] = nick_attempts[qnick]-1;

                chat->TextEdit_CHATINPUT->setPlainText(
                                                        QString("|AntiSpam Bot|: ") + tr("Your message was not delivered. "
                                                        "Answer the question and send message again, please. In the"
                                                        " case of incorrect answer you will be added in the black "
                                                        "list and your message will never arrive. So, the question is: ")
                                                        + DCAntiSpam::Instance()->getPhrase()
                                                      );
                chat->SendChat();
            }

            return false;
        } else {
            DCAntiSpam::Instance()->addToGray(QList<QString > () << qnick);

            chat->TextEdit_CHATINPUT->setPlainText("|AntiSpam Bot|: "
                    + tr("Ok. Now you can write your message."));
            chat->SendChat();
            chat->close();

            int index = sandbox.indexOf(chat);
            sandbox.removeAt(index);

            return true;
        }

        m_ChatMap.remove(qnick);

    }

    return true;

}

