void RegisteredUserEntryDialog::notifyCheckClicked(bool bChecked)
{
	m_pNotifyNick->setEnabled(bChecked);
	m_pNotifyLabel->setEnabled(bChecked);
	if(bChecked && m_pNotifyNick->text().isEmpty())
	{
		QString szMask;
		if(m_pUser)
		{
			for(KviIrcMask * m = m_pUser->maskList()->first();m;m = m_pUser->maskList()->next())
			{
				QString tmp = m->nick();
				if((tmp.indexOf('*') == -1) && (tmp.indexOf('?') == -1) && (!tmp.isEmpty()))
				{
					if(!szMask.isEmpty())szMask.append(' ');
					szMask.append(tmp);
				}
			}
			// if the nickname list is still empty, build a dummy nick to notify
			if(szMask.isEmpty())
			{
				szMask = m_pUser->name();
				szMask.replace(" ","");
				szMask.replace("'","");
				szMask.replace("&","");
				szMask.replace(",","");
			}
			m_pNotifyNick->setText(szMask);
		}
	}
}
Exemplo n.º 2
0
bool KviRegisteredUser::matchesFixed(const QString & nick,const QString & user,const QString & host)
{
	for(KviIrcMask * m = m_pMaskList->first();m;m = m_pMaskList->next())
	{
		if(m->matchesFixed(nick,user,host))return true;
	}
	return false;
}
Exemplo n.º 3
0
bool KviRegisteredUser::matchesFixed(const KviIrcMask &mask)
{
	for(KviIrcMask * m = m_pMaskList->first();m;m = m_pMaskList->next())
	{
		if(m->matchesFixed(mask))return true;
	}
	return false;
}
Exemplo n.º 4
0
void RegisteredUsersDialog::itemPressed(QTreeWidgetItem * it, int c)
{
	if(!it)
		return;
	RegisteredUsersDialogItemBase * b = (RegisteredUsersDialogItemBase *)it;
	if(b->type() == RegisteredUsersDialogItemBase::User)
	{
		RegisteredUsersDialogItem * i = (RegisteredUsersDialogItem *)it;

		QRect r = m_pListView->visualItemRect(i);
		int daw = m_pListView->columnWidth(0);

		QPoint ppp = m_pListView->mapFromGlobal(QCursor::pos());

		if((c == 1) && (ppp.x() < (r.height() + 5 + daw)))
		{
			// notify list toggle
			if(i->user()->getProperty("notify").isEmpty())
			{
				// try to find the nicknames to be notified
				QString szMask;

				for(KviIrcMask * m = i->user()->maskList()->first(); m; m = i->user()->maskList()->next())
				{
					QString tmp = m->nick();
					if((tmp.indexOf('*') == -1) && (tmp.indexOf('?') == -1) && (!tmp.isEmpty()))
					{
						if(!szMask.isEmpty())
							szMask.append(' ');
						szMask.append(tmp);
					}
				}
				// if the nickname list is still empty, build a dummy nick to notify
				if(szMask.isEmpty())
				{
					szMask = i->user()->name();
					szMask.replace(" ", "");
					szMask.replace("'", "");
					szMask.replace("&", "");
					szMask.replace(",", "");
				}

				i->user()->setProperty("notify", szMask);
			}
			else
			{
				i->user()->setProperty("notify", QString("")); // kill that
			}

			m_pListView->repaint(r);
		}
	}
}
void RegisteredUserEntryDialog::addMaskClicked()
{
	KviIrcMask mk;
	RegisteredUserMaskDialog * dlg = new RegisteredUserMaskDialog(this,&mk);
	if(dlg->exec() == QDialog::Accepted)
	{
		QString m = mk.nick();
		m += QChar('!');
		m += mk.user();
		m += QChar('@');
		m += mk.host();
		m_pMaskListBox->addItem(m);
	}
	delete dlg;
}
void KviRegisteredUserDataBase::save(const QString & szFilename)
{
	KviConfigurationFile cfg(szFilename, KviConfigurationFile::Write);
	cfg.clear();
	cfg.preserveEmptyGroups(true);

	KviPointerHashTableIterator<QString, KviRegisteredUser> it(*m_pUserDict);

	while(it.current())
	{
		cfg.setGroup(it.current()->name());
		// Write properties
		cfg.writeEntry("IgnoreEnabled", it.current()->ignoreEnabled());
		cfg.writeEntry("IgnoreFlags", it.current()->ignoreFlags());
		if(it.current()->propertyDict())
		{
			KviPointerHashTableIterator<QString, QString> pit(*(it.current()->propertyDict()));
			while(pit.current())
			{
				QString tmp = "prop_";
				tmp.append(pit.currentKey());
				cfg.writeEntry(tmp, *(pit.current()));
				++pit;
			}
		}
		// Write masks
		int i = 0;
		for(KviIrcMask * pMask = it.current()->maskList()->first(); pMask; pMask = it.current()->maskList()->next())
		{
			QString szTmp = QString("mask_%1").arg(i);
			QString szMask;
			pMask->mask(szMask, KviIrcMask::NickUserHost);
			cfg.writeEntry(szTmp, szMask);
			++i;
		}
		cfg.writeEntry("Group", it.current()->group());
		++it;
	}

	KviPointerHashTableIterator<QString, KviRegisteredUserGroup> git(*m_pGroupDict);
	QString szTmp;
	while(git.current())
	{
		szTmp = QString("#Group %1").arg(git.current()->name());
		cfg.setGroup(szTmp);
		++git;
	}
}
Exemplo n.º 7
0
void KviAvatarCache::remove(const KviIrcMask &mask,const QString &szNetwork)
{
	QString szKey;

	mask.mask(szKey,KviIrcMask::NickCleanUserSmartNet);
	szKey.append(QChar('+'));
	szKey.append(szNetwork);

	m_pAvatarDict->remove(szKey);
}
Exemplo n.º 8
0
bool KviIsOnNotifyListManager::doMatchUser(const QString & notifyString, const KviIrcMask & mask)
{
	const auto i = m_pRegUserDict.find(notifyString);
	if(i != m_pRegUserDict.end())
	{
		const QString & nam = i->second;
		// ok... find the user
		if(KviRegisteredUser * u = g_pRegisteredUserDataBase->findUserByName(nam))
		{
			// ok... match the user
			if(u->matchesFixed(mask))
			{
				// new user online
				if(!(m_pConsole->notifyListView()->findEntry(mask.nick())))
				{
					notifyOnLine(mask.nick(), mask.user(), mask.host());
				} // else already online, and matching... all ok
			}
			else
			{
				// not matched.... has he been online before ?
				if(m_pConsole->notifyListView()->findEntry(mask.nick()))
				{
					// has been online just a sec ago, but now the mask does not match
					// either reguserdb has changed, or the user went offline and another one got his nick
					// in the meantime... (ugly situation anyway)
					notifyOffLine(mask.nick(), mask.user(), mask.host(), __tr2qs("registration mask changed, or nickname is being used by someone else"));
				}
				else
				{
					// has never been online
					if(_OUTPUT_VERBOSE)
						m_pConsole->output(KVI_OUT_SYSTEMMESSAGE, __tr2qs("Notify list: \r!n\r%Q\r appears to be online, but the mask [%Q@\r!h\r%Q\r] does not match (registration mask does not match, or nickname is being used by someone else)"), &(mask.nick()), &(mask.user()), &(mask.host()));
				}
			}
		}
		else
		{
			// oops... unexpected inconsistency.... reguser db modified ?
			m_pConsole->output(KVI_OUT_SYSTEMWARNING, __tr2qs("Notify list: Unexpected inconsistency, registered user DB modified? (restarting)"));
			stop();
			start();
			return false; // critical... exit from the call stack
		}
	}
	else
	{
		// oops... unexpected inconsistency
		m_pConsole->output(KVI_OUT_SYSTEMWARNING, __tr2qs("Notify list: Unexpected inconsistency, expected \r!n\r%Q\r in the registered user DB"), &notifyString);
	}
	return true;
}
Exemplo n.º 9
0
void KviAvatarCache::replace(const QString &szIdString,const KviIrcMask &mask,const QString &szNetwork)
{
	QString szKey;

	mask.mask(szKey,KviIrcMask::NickCleanUserSmartNet);
	szKey.append(QChar('+'));
	szKey.append(szNetwork);

	KviAvatarCacheEntry * e = new KviAvatarCacheEntry;
	e->szIdString = szIdString;
	e->tLastAccess = kvi_unixTime();

	m_pAvatarDict->replace(szKey,e);

	if(m_pAvatarDict->count() > MAX_AVATARS_IN_CACHE)
	{
		cleanup();
	}
}
RegisteredUserEntryDialog::RegisteredUserEntryDialog(QWidget *p,KviRegisteredUser * r,bool bModal)
: KviTalTabDialog(p,"reguser_entry_editor",bModal)
{
	m_pUser = r;
	m_pCustomColor = new QColor();

	if(r)
	{
		QString col=r->getProperty("customColor");
		KviStringConversion::fromString(col,(*m_pCustomColor));
	}

	m_pPropertyDict = new KviPointerHashTable<QString,QString>(17,false);
	m_pPropertyDict->setAutoDelete(true);

	//setMinimumSize(400,450);

	setWindowIcon(*(g_pIconManager->getSmallIcon(KviIconManager::Linux)));
	setWindowTitle(__tr2qs_ctx("Registered User Entry","register"));

	QWidget * p1 = new QWidget(this);

	QGridLayout * g = new QGridLayout(p1);

	QLabel * l = new QLabel(__tr2qs_ctx("Name:","register"),p1);
	g->addWidget(l,0,0);

	m_pNameEdit = new QLineEdit(p1);
	g->addWidget(m_pNameEdit,0,1);

	l = new QLabel(__tr2qs_ctx("Comment:","register"),p1);
	g->addWidget(l,1,0);

	m_pCommentEdit = new QLineEdit(p1);
	g->addWidget(m_pCommentEdit,1,1);

	QFrame * f = new QFrame(p1);
	g->addWidget(f,2,0,1,2);
	f->setFrameStyle(QFrame::HLine | QFrame::Sunken);

	l = new QLabel(__tr2qs_ctx("Masks:","register"),p1);
	g->addWidget(l,3,0,1,2);

	m_pMaskListBox = new QListWidget(p1);
	connect(m_pMaskListBox,SIGNAL(itemSelectionChanged()),this,SLOT(maskCurrentChanged()));
	m_pMaskListBox->setMinimumSize(300,200);

	g->addWidget(m_pMaskListBox,4,0,1,2);

	KviTalHBox * b = new KviTalHBox(p1);
	g->addWidget(b,5,0,1,2);
	b->setSpacing(4);

	m_pAddMaskButton = new QPushButton(__tr2qs_ctx("&Add...","register"),b);
	connect(m_pAddMaskButton,SIGNAL(clicked()),this,SLOT(addMaskClicked()));
	m_pAddMaskButton->setIcon(*(g_pIconManager->getSmallIcon(KviIconManager::NewItem)));

	m_pDelMaskButton = new QPushButton(__tr2qs_ctx("Re&move","register"),b);
	m_pDelMaskButton->setEnabled(false);
	connect(m_pDelMaskButton,SIGNAL(clicked()),this,SLOT(delMaskClicked()));
	m_pDelMaskButton->setIcon(*(g_pIconManager->getSmallIcon(KviIconManager::DeleteItem)));

	m_pEditMaskButton = new QPushButton(__tr2qs_ctx("&Edit","register"),b);
	m_pEditMaskButton->setEnabled(false);
	connect(m_pEditMaskButton,SIGNAL(clicked()),this,SLOT(editMaskClicked()));
	m_pEditMaskButton->setIcon(*(g_pIconManager->getSmallIcon(KviIconManager::EditItem)));

	g->setRowStretch(4,1);
	g->setColumnStretch(1,1);

	addTab(p1,__tr2qs_ctx("Identity","register"));

	QWidget * p2 = new QWidget(this);

	g = new QGridLayout(p2);

	m_pNotifyCheck = new QCheckBox(__tr2qs_ctx("Notify when user is online","register"),p2);
	g->addWidget(m_pNotifyCheck,0,0,1,3);

	m_pNotifyLabel = new QLabel(__tr2qs_ctx("Notify nicknames:","register"),p2);
	m_pNotifyLabel->setEnabled(m_pNotifyCheck->isChecked());
	g->addWidget(m_pNotifyLabel,1,0);
	m_pNotifyCheck->setToolTip(__tr2qs_ctx("<center>You can enter a space separated list of nicknames.</center>","register"));


	m_pNotifyNick = new QLineEdit(p2);
	m_pNotifyNick->setEnabled(false);

	g->addWidget(m_pNotifyNick,1,1,1,2);
	connect(m_pNotifyCheck,SIGNAL(toggled(bool)),this,SLOT(notifyCheckClicked(bool)));


	f = new QFrame(p2);
	f->setFrameStyle(QFrame::HLine | QFrame::Sunken);
	g->addWidget(f,2,0,1,3);

	m_pAvatar = 0;
	if(r)
	{
		const QString av = r->getProperty("avatar");
		if(!av.isEmpty())
		{
			m_pAvatar = new KviPixmap(av.toUtf8().data());
		}
	}
	if(!m_pAvatar)m_pAvatar = new KviPixmap();

	m_pAvatarSelector = new KviPixmapSelector(p2,__tr2qs_ctx("Avatar","register"),m_pAvatar,true);
	g->addWidget(m_pAvatarSelector,3,0,1,3);

	f = new QFrame(p2);
	f->setFrameStyle(QFrame::HLine | QFrame::Sunken);
	g->addWidget(f,4,0,1,3);

	m_pCustomColorCheck = new QCheckBox(__tr2qs_ctx("Use custom color in userlist","register"),p2);
	if(r)
		m_pCustomColorCheck->setChecked(r->getBoolProperty("useCustomColor"));
	g->addWidget(m_pCustomColorCheck,5,0,1,2);

	m_pCustomColorSelector = new KviColorSelector(p2,QString(),m_pCustomColor,1);
	g->addWidget(m_pCustomColorSelector,5,2);

	QPushButton * pb = new QPushButton(__tr2qs_ctx("All Properties...","register"),p2);
	connect(pb,SIGNAL(clicked()),this,SLOT(editAllPropertiesClicked()));
	g->addWidget(pb,6,2);

	g->setColumnStretch(1,1);
	g->setRowStretch(3,1);

	addTab(p2,__tr2qs_ctx("Properties","register"));

	// Ignore TAB
	KviTalVBox * vb = new KviTalVBox(this);
	vb->setMargin(10);

	m_pIgnoreEnabled = new QCheckBox(__tr2qs_ctx("Enable ignore for this user","register"),vb);

	QGroupBox * gb = new QGroupBox(__tr2qs_ctx("Ignore features","register"),vb);
	connect(m_pIgnoreEnabled,SIGNAL(toggled(bool)),gb,SLOT(setEnabled(bool)));

	QVBoxLayout * layout = new QVBoxLayout(gb);
	layout->setMargin(20);
	layout->setSpacing(3);

	m_pIgnoreQuery = new QCheckBox(__tr2qs_ctx("Ignore query messages","register"),gb);
	layout->addWidget(m_pIgnoreQuery);

	m_pIgnoreChannel = new QCheckBox(__tr2qs_ctx("Ignore channel messages","register"),gb);
	layout->addWidget(m_pIgnoreChannel);

	m_pIgnoreNotice = new QCheckBox(__tr2qs_ctx("Ignore notice messages","register"),gb);
	layout->addWidget(m_pIgnoreNotice);

	m_pIgnoreCtcp = new QCheckBox(__tr2qs_ctx("Ignore CTCP messages","register"),gb);
	layout->addWidget(m_pIgnoreCtcp);

	m_pIgnoreInvite = new QCheckBox(__tr2qs_ctx("Ignore invites","register"),gb);
	layout->addWidget(m_pIgnoreInvite);

	m_pIgnoreDcc = new QCheckBox(__tr2qs_ctx("Ignore DCCs","register"),gb);
	layout->addWidget(m_pIgnoreDcc);

	QWidget *w = new QWidget(vb);
	w->setSizePolicy(QSizePolicy::Ignored,QSizePolicy::Ignored);

	addTab(vb,__tr2qs_ctx("Ignore","register"));

	setCancelButton(__tr2qs_ctx("Cancel","register"));
	setOkButton(__tr2qs_ctx("&OK","register"));
	connect(this,SIGNAL(applyButtonPressed()),this,SLOT(okClicked()));
	connect(this,SIGNAL(cancelButtonPressed()),this,SLOT(reject()));

	if(r)
	{
		m_pNameEdit->setText(r->name());
		m_pCommentEdit->setText(r->getProperty("comment"));
		for(KviIrcMask * m = r->maskList()->first();m;m = r->maskList()->next())
		{
			QString mk = m->nick();
			mk += QChar('!');
			mk += m->user();
			mk += QChar('@');
			mk += m->host();
			m_pMaskListBox->addItem(mk);
		}

		QString szNotifyNicks = r->getProperty("notify");
		if(!szNotifyNicks.isEmpty())
		{
			m_pNotifyCheck->setChecked(true);
			m_pNotifyNick->setText(szNotifyNicks);
			m_pNotifyNick->setEnabled(true);
		}

		if(r->propertyDict())
		{
			KviPointerHashTableIterator<QString,QString> it(*(r->propertyDict()));
			while(QString *s = it.current())
			{
				m_pPropertyDict->insert(it.currentKey(),new QString(*s));
				++it;
			}
		}

		m_pIgnoreEnabled->setChecked(r->ignoreEnagled());

		gb->setEnabled(r->ignoreEnagled());

		m_pIgnoreQuery->setChecked(r->ignoreFlags() & KviRegisteredUser::Query);
		m_pIgnoreChannel->setChecked(r->ignoreFlags() & KviRegisteredUser::Channel);
		m_pIgnoreNotice->setChecked(r->ignoreFlags() & KviRegisteredUser::Notice);
		m_pIgnoreCtcp->setChecked(r->ignoreFlags() & KviRegisteredUser::Ctcp);
		m_pIgnoreInvite->setChecked(r->ignoreFlags() & KviRegisteredUser::Invite);
		m_pIgnoreDcc->setChecked(r->ignoreFlags() & KviRegisteredUser::Dcc);
	} else {
		// default values
		if(!m_pIgnoreEnabled->isChecked())
		{
			gb->setEnabled(false);
		}
	}
}
Exemplo n.º 11
0
bool KviIsOnNotifyListManager::handleUserhost(KviIrcMessage * msg)
{
	if(!m_bExpectingUserhost)
		return false;
	// first check for consistency: all the replies must be on the USERHOST list
	KviPointerList<KviIrcMask> tmplist;
	tmplist.setAutoDelete(true);

	KviCString nk;
	const char * aux = msg->trailing();

	while(*aux)
	{
		nk = "";
		aux = kvi_extractToken(nk, aux, ' ');
		if(nk.hasData())
		{
			// split it in a mask
			KviCString nick;
			KviCString user;
			KviCString host;

			int idx = nk.findFirstIdx('=');
			if(idx != -1)
			{
				nick = nk.left(idx);
				if(nick.lastCharIs('*'))
					nick.cutRight(1);
				nk.cutLeft(idx + 1);
				if(nk.firstCharIs('+') || nk.firstCharIs('-'))
					nk.cutLeft(1);

				idx = nk.findFirstIdx('@');
				if(idx != -1)
				{
					user = nk.left(idx);
					nk.cutLeft(idx + 1);
					host = nk;
				}
				else
				{
					user = "******";
					host = nk;
				}

				bool bGotIt = false;
				QString szNick = m_pConnection->decodeText(nick.ptr());
				QString szUser = m_pConnection->decodeText(user.ptr());
				QString szHost = m_pConnection->decodeText(host.ptr());

				for(QString * s = m_pUserhostList->first(); s && (!bGotIt); s = m_pUserhostList->next())
				{
					if(KviQString::equalCI(*s, szNick))
					{
						KviIrcMask * mk = new KviIrcMask(szNick, szUser, szHost);
						tmplist.append(mk);
						bGotIt = true;
						m_pUserhostList->removeRef(s);
					}
				}

				if(!bGotIt)
				{
					// ops...not my userhost!
					if(_OUTPUT_VERBOSE)
						m_pConsole->output(KVI_OUT_SYSTEMWARNING, __tr2qs("Notify list: Hey! You've used USERHOST behind my back? (I might be confused now...)"));
					return false;
				}
			}
			else
			{
				if(_OUTPUT_VERBOSE)
					m_pConsole->output(KVI_OUT_SYSTEMWARNING, __tr2qs("Notify list: Broken USERHOST reply from the server? (%s)"), nk.ptr());
			}
		}
	}

	// Ok...looks to be my usershot (still not sure at 100%, but can't do better)

	if(m_pConnection->lagMeter())
		m_pConnection->lagMeter()->lagCheckComplete("@notify_userhost");

	m_bExpectingUserhost = false;

	for(KviIrcMask * mk = tmplist.first(); mk; mk = tmplist.next())
	{
		if(!doMatchUser(mk->nick(), *mk))
			return true; // have to restart!!!
	}

	if(!(m_pUserhostList->isEmpty()))
	{
		// ops...someone is no longer online ?
		while(QString * s = m_pUserhostList->first())
		{
			if(_OUTPUT_VERBOSE)
				m_pConsole->output(KVI_OUT_SYSTEMMESSAGE, __tr2qs("Notify list: \r!n\r%Q\r appears to have gone offline before USERHOST reply was received, will recheck in the next loop"), s);
			m_pUserhostList->removeFirst();
		}
	}

	if(m_pOnlineList->isEmpty())
	{
		if(m_pNotifyList->isEmpty())
			delayedNotifySession();
		else
			delayedIsOnSession();
	}
	else
		delayedUserhostSession();

	return true;
}
Exemplo n.º 12
0
bool KviWatchNotifyListManager::doMatchUser(KviIrcMessage * msg, const QString & notifyString, const KviIrcMask & mask)
{
	QString * nam = m_pRegUserDict->find(notifyString);

	if(nam)
	{
		// ok...find the user
		if(KviRegisteredUser * u = g_pRegisteredUserDataBase->findUserByName(*nam))
		{
			// ok ... match the user
			if(u->matchesFixed(mask))
			{
				// new user online
				if(!(m_pConsole->notifyListView()->findEntry(mask.nick())))
				{
					notifyOnLine(mask.nick(), mask.user(), mask.host(), "watch");
				}
				else
				{
					// else already online, and matching...all ok
					if(msg->numeric() == RPL_NOWON)
					{
						// This is a reply to a /watch +something (should not happen, unless the user is messing) or to /watch l (user requested)
						notifyOnLine(mask.nick(), mask.user(), mask.host(),
						    __tr2qs("watch entry listing requested by user"), false);
					}
					else
					{
						// This is a RPL_LOGON....we're desynched ?
						notifyOnLine(mask.nick(), mask.user(), mask.host(),
						    __tr2qs("possible watch list desync"), false);
					}
				}
			}
			else
			{
				// not matched.... has he been online before ?
				if(m_pConsole->notifyListView()->findEntry(mask.nick()))
				{
					// has been online just a sec ago, but now the mask does not match
					// prolly the reguserdb has been changed
					notifyOffLine(mask.nick(), mask.user(), mask.host(),
					    __tr2qs("registration mask changed or desync with the watch service"));
				}
				else
				{
					// has never been online
					if(_OUTPUT_VERBOSE)
						m_pConsole->output(KVI_OUT_SYSTEMMESSAGE,
						    __tr("Notify list: \r!n\r%Q\r appears to be online, but the mask [%Q@\r!h\r%Q\r] does not match (watch: registration mask does not match, or nickname is being used by someone else)"),
						    &(mask.nick()), &(mask.user()), &(mask.host()));
				}
			}
		}
		else
		{
			// ops... unexpected inconsistency .... reguser db modified ?
			m_pConsole->output(KVI_OUT_SYSTEMWARNING,
			    __tr2qs("Notify list: Unexpected inconsistency, registered user DB modified? (watch: restarting)"));
			stop();
			start();
			return false; // critical ... exit from the call stack
		}
	}
	else
	{
		// not in our dictionary
		// prolly someone used /WATCH behind our back... bad boy!
		if(!(m_pConsole->notifyListView()->findEntry(mask.nick())))
		{
			notifyOnLine(mask.nick(), mask.user(), mask.host(), __tr2qs("watch entry added by user"));
		}
	}
	return true;
}
Exemplo n.º 13
0
bool KviIsOnNotifyListManager::handleUserhost(KviIrcMessage * msg)
{
	if(!m_bExpectingUserhost)
		return false;
	// first check for consistency: all the replies must be on the USERHOST list

	std::map<std::size_t, std::unique_ptr<KviIrcMask>> tmplist;

	KviCString nk;
	const char * aux = msg->trailing();

	while(*aux)
	{
		nk = "";
		aux = kvi_extractToken(nk, aux, ' ');
		if(nk.hasData())
		{
			// split it in a mask
			KviCString nick;
			KviCString user;
			KviCString host;

			int idx = nk.findFirstIdx('=');
			if(idx != -1)
			{
				nick = nk.left(idx);
				if(nick.lastCharIs('*'))
					nick.cutRight(1);
				nk.cutLeft(idx + 1);
				if(nk.firstCharIs('+') || nk.firstCharIs('-'))
					nk.cutLeft(1);

				idx = nk.findFirstIdx('@');
				if(idx != -1)
				{
					user = nk.left(idx);
					nk.cutLeft(idx + 1);
					host = nk;
				}
				else
				{
					user = "******";
					host = nk;
				}

				bool bGotIt = false;
				QString szNick = m_pConnection->decodeText(nick.ptr());
				QString szUser = m_pConnection->decodeText(user.ptr());
				QString szHost = m_pConnection->decodeText(host.ptr());

				std::size_t i = 0;
				for(const auto & s : m_UserhostList)
				{
					if(KviQString::equalCI(s, szNick))
					{
						tmplist.emplace(i, std::make_unique<KviIrcMask>(szNick, szUser, szHost));
						bGotIt = true;
						break;
					}
				}

				if(!bGotIt)
				{
					// oops... not my userhost!
					if(_OUTPUT_VERBOSE)
						m_pConsole->output(KVI_OUT_SYSTEMWARNING, __tr2qs("Notify list: Hey! You've used USERHOST behind my back? (I might be confused now...)"));
					return false;
				}
			}
			else
			{
				if(_OUTPUT_VERBOSE)
					m_pConsole->output(KVI_OUT_SYSTEMWARNING, __tr2qs("Notify list: Broken USERHOST reply from the server? (%s)"), nk.ptr());
			}
		}
	}

	// Ok... looks to be my usershot (still not sure at 100%, but can't do better)

	if(m_pConnection->lagMeter())
		m_pConnection->lagMeter()->lagCheckComplete("@notify_userhost");

	m_bExpectingUserhost = false;

	for(auto & pair : tmplist)
	{
		KviIrcMask * mk = pair.second.get();

		if(!doMatchUser(mk->nick(), *mk))
			return true; // have to restart!!!
	}

	for(auto i = tmplist.rbegin(); i != tmplist.rend(); ++i)
		m_UserhostList.erase(m_UserhostList.begin() + i->first);

	for(const auto & s : m_UserhostList)
	{
		// oops... someone is no longer online ?
		if(_OUTPUT_VERBOSE)
			m_pConsole->output(KVI_OUT_SYSTEMMESSAGE, __tr2qs("Notify list: \r!n\r%Q\r appears to have gone offline before USERHOST reply was received, will recheck in the next loop"), &s);
	}

	m_UserhostList.clear();

	if(m_OnlineList.empty())
	{
		if(m_NotifyList.empty())
			delayedNotifySession();
		else
			delayedIsOnSession();
	}
	else
	{
		delayedUserhostSession();
	}

	return true;
}