From a6d58bb6052ac8cb01805a48c4ad2f129126116f Mon Sep 17 00:00:00 2001 From: tpearson Date: Wed, 24 Feb 2010 02:13:59 +0000 Subject: Added KDE3 version of kvirc git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/kvirc@1095341 283d02a7-25f6-0310-bc7c-ecb5cbfe19da --- src/kvilib/ext/kvi_regusersdb.cpp | 743 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 743 insertions(+) create mode 100644 src/kvilib/ext/kvi_regusersdb.cpp (limited to 'src/kvilib/ext/kvi_regusersdb.cpp') diff --git a/src/kvilib/ext/kvi_regusersdb.cpp b/src/kvilib/ext/kvi_regusersdb.cpp new file mode 100644 index 00000000..6d36c975 --- /dev/null +++ b/src/kvilib/ext/kvi_regusersdb.cpp @@ -0,0 +1,743 @@ +//================================================================================================= +// +// File : kvi_regusersdb.cpp +// Creation date : Sat Sep 09 2000 15:46:12 by Szymon Stefanek +// +// This file is part of the KVirc irc client distribution +// Copyright (C) 2000-2004 Szymon Stefanek (pragma at kvirc dot net) +// +// 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 opinion) any later version. +// +// This program is distributed in the HOPE that it will be USEFUL, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, write to the Free Software Foundation, +// Inc. ,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +//================================================================================================= + + +#define __KVILIB__ + +#include "kvi_debug.h" + +#define _KVI_REGUSERDB_CPP_ +#include "kvi_regusersdb.h" + +#include "kvi_config.h" +#include "kvi_locale.h" + +/* + @doc: registered_users + @title: + Registered users + @type: + generic + @short: + Registration of users in KVIrc + @keyterms: + registered users, registration mask, registered user properties, + user properties, notify property, avatar property + @body: + [big]Introduction[/big][br] + The "registered user database" is basically a set of users with associated + [doc:irc_masks]irc-masks[/doc] and properties.[br] + It is used to recognize users on IRC and associate properties to them.[br] + This works more or less like the IRC ban list, K-Line list, or invite list.[br] + [big]User entry[/big][br] + A registered user database entry is identified by an [b]unique[/b] name.[br] + It may be the nickname of the user that you want to match, or the real name (if you know it) + or any other string (even with spaces). The name is an "internal identifier" for the user entry: + each name maps to a single entry and each entry has a single name.[br] + Each entry has a set of registration [doc:irc_masks]irc-masks[/doc]: these masks + are used to recognize the user on irc.[br] + [br] + [big]Registration masks[/big][br] + The masks have the common IRC mask format: [b]!@[/b][br] + The masks may contain '*' and '?' wildcards that match any portion of text.[br] + [b]*!*@*[/b][br] + [b]Pragma!*@*[/b][br] + [b]*!~daemon@*[/b][br] + [b]Pragma!*daemon@*.it[/b][br] + [b]Pragma!?daemon@some*.it[/b][br] + [b]Pragma!~daemon@some.host.it[/b][br] + Are examples of valid registration masks.[br] + The masks with wildcards can actually match more than a single user.[br] + For example the mask *!root@*.host.com will match all the users + having root as username and coming from the host.com domain.[br] + For this reason putting wildcards in nicknames could become a problem + if not used carefully (but may also be used to achieve interesting tricks).[br] + If you don't use wildcards in nicknames you are sure that + in a single irc connection , a mask will always refer to a single user.[br] + You will commonly use the following format:[br] + !*@*..[br] + or[br] + !*@...*[br] + In this way you can be 95% sure that the mask will really match the correct user.[br] + [br] + [big]Example of registration and lookups[/big] + Assume that you want to registere a friend of yours: Derek Riggs.[br] + Derek often uses "Eddie" as his nickname + "stranger" as username and has a dial-up connection that makes his IP address appear as + .somewhere.in.time.org.[br] + You will add an entry with name "Derek Riggs" and a registration mask like the following: + Eddie!stranger@*.somewhere.in.time.org.[br] + If the IRC servers keep adding strange characters ([doc:irc_masks]prefixes[/doc]) at the beginning of his username you may use + Eddie!*stranger@*.somewhere.in.time.org.[br] + If Eddie also often connects from the wasted.years.org domain and gets 'eddie' as username there, you might add a second registration mask as follows: + Eddie!*eddie@*.wasted.years.org.[br] + An alternative could be use only one mask with *.org as domain and allow any username (Eddie!*@*.org) but this + could become dangerous since it could match the users that you don't want to.[br] + On the other hand, if you dislike the users with the nickname Eddie that come from .org + and you're implementing an auto-kick system, the correct mask to register is "Eddie!*@*.org".[br] + [br] + KVirc ties to be smart , and always find the most correct match for an user: + If you have two masks registered: Pragma!*xor@*.myisp.it and *!*@*.myisp.it, + kvirc will match Pragma!~xor@233-dyn.myisp.it with the first one even if the second + one matches too; the firs one is a best match.[br] + [br] + [big]Properties[/big][br] + A registered user has an (eventually empty) set of properties + defined by name/value pairs. (In versions prior to 3.0.0 flags were used instead, + but revealed to be insufficient).[br] + KVirc recognizes some of these proprietes and associates semantic actions to it; other properties + are left for scripting extension. Property names are case insensitive.[br] + One of the recognized properties is the "[doc:notify_list]notify[/doc]" property. + When an user is found to have this property set to a special value + KVIrc will attempt to track the user presence on IRC. + Another one is the [doc:avatar]avatar[/doc] property. Its value should be the + name of the "default" [doc:avatar]avatar image file[/doc] for the specified user.[br] + The "ignore" property should be set to "1" (or "true") for users that have to be ignored (:D).[br] + [br] + [big]The interface to the database[/big][br] + The [module:reguser]reguser module[/module] is the interface to the "registered users database".[br] + It provides a set of commands for adding and removing masks and manipulating properties.[br] +*/ + +//============================================================================================================ +// +// KviRegisteredMask +// + +KVILIB_API KviRegisteredUserDataBase* g_pRegisteredUserDataBase = 0; + +KviRegisteredMask::KviRegisteredMask(KviRegisteredUser * u,KviIrcMask * m) +{ + m_pUser = u; + m_pMask = m; + m_iMaskNonWildChars = m_pMask->nonWildChars(); +} + +//============================================================================================================ +// +// KviRegisteredUser +// + + +KviRegisteredUser::KviRegisteredUser(const QString & name) +{ + m_iIgnoreFlags =0; + m_bIgnoreEnabled=false; + m_szName = name; + m_pPropertyDict = 0; + m_pMaskList = new KviPointerList; + m_pMaskList->setAutoDelete(true); +} + +KviRegisteredUser::~KviRegisteredUser() +{ + if(m_pPropertyDict)delete m_pPropertyDict; + delete m_pMaskList; +} + +bool KviRegisteredUser::isIgnoreEnabledFor(IgnoreFlags flag) +{ + if(!m_bIgnoreEnabled) return false; + return m_iIgnoreFlags & flag; +} + +KviIrcMask * KviRegisteredUser::findMask(const KviIrcMask &mask) +{ + for(KviIrcMask * m = m_pMaskList->first();m;m = m_pMaskList->next()) + { + if(*m == mask)return m; + } + return 0; +} + +bool KviRegisteredUser::addMask(KviIrcMask * mask) +{ + if(findMask(*mask)) + { + delete mask; + mask = 0; + return false; + } + m_pMaskList->append(mask); + return true; +} + +bool KviRegisteredUser::removeMask(KviIrcMask * mask) +{ + if(!mask)return false; + return m_pMaskList->removeRef(mask); +} + +bool KviRegisteredUser::matches(const KviIrcMask &mask) +{ + for(KviIrcMask * m = m_pMaskList->first();m;m = m_pMaskList->next()) + { + if(m->matches(mask))return true; + } + return false; +} + +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; +} + +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; +} + +void KviRegisteredUser::setProperty(const QString &name,bool value) +{ + setProperty(name,value ? QString("true") : QString("false")); +} + +void KviRegisteredUser::setProperty(const QString & name,const QString & value) +{ + if(!value.isEmpty()) + { + if(!m_pPropertyDict) + { + m_pPropertyDict = new KviPointerHashTable(7,false); + m_pPropertyDict->setAutoDelete(true); + } +#ifdef COMPILE_USE_QT4 + QString * val = new QString(value.trimmed()); +#else + QString * val = new QString(value.stripWhiteSpace()); +#endif + if(!val->isEmpty()) + { + m_pPropertyDict->replace(name,val); + } else { + delete val; + val = 0; + } + } else { + if(m_pPropertyDict)m_pPropertyDict->remove(name); + } +} + +bool KviRegisteredUser::getProperty(const QString & name,QString &value) +{ + if(!m_pPropertyDict)return false; + if(name.isEmpty()) return false; + QString * pValue = m_pPropertyDict->find(name); + if(pValue)value = *pValue; + else return false; + return true; +} + +const QString & KviRegisteredUser::getProperty(const QString & name) +{ + if(!m_pPropertyDict)return KviQString::empty; + if(name.isEmpty())return KviQString::empty; + QString * pValue = m_pPropertyDict->find(name); + if(pValue)return *pValue; + return KviQString::empty; +} + +bool KviRegisteredUser::getBoolProperty(const QString & name,bool def) +{ + if(!m_pPropertyDict)return def; + if(name.isEmpty()) return def; + QString * pValue = m_pPropertyDict->find(name); + if(pValue) + { + // be flexible , allow more "true" values (pragma) + if(KviQString::equalCS(*pValue,"1"))return true; + if(KviQString::equalCI(*pValue,"true"))return true; + if(KviQString::equalCI(*pValue,"yes"))return true; + //if(KviQString::equalCI(*pValue,"yeah"))return true; + //if(KviQString::equalCI(*pValue,"sure"))return true; + //if(KviQString::equalCI(*pValue,"sureashell"))return true; + } + return def; +} + +//============================================================================================================ +// +// KviRegisteredUserGroup +// + +KviRegisteredUserGroup::KviRegisteredUserGroup(const QString &name) +{ + setName(name); +} + +KviRegisteredUserGroup::~KviRegisteredUserGroup() +{ +} + + +//============================================================================================================ +// +// KviRegisteredUserDb +// + +KviRegisteredUserDataBase::KviRegisteredUserDataBase() +{ + m_pUserDict = new KviPointerHashTable(31,false); // do not copy keys + m_pUserDict->setAutoDelete(true); + + m_pWildMaskList = new KviRegisteredMaskList; + m_pWildMaskList->setAutoDelete(true); + + m_pMaskDict = new KviPointerHashTable(49,false); // copy keys here! + m_pMaskDict->setAutoDelete(true); + + m_pGroupDict = new KviPointerHashTable(5,false); // copy keys here! + m_pGroupDict->setAutoDelete(true); +} + +KviRegisteredUserDataBase::~KviRegisteredUserDataBase() +{ + emit(databaseCleared()); + delete m_pUserDict; + delete m_pWildMaskList; + delete m_pMaskDict; + delete m_pGroupDict; +} + +KviRegisteredUser * KviRegisteredUserDataBase::addUser(const QString & name) +{ + if(name.isEmpty()) return false; + if(m_pUserDict->find(name))return 0; + KviRegisteredUser * u = new KviRegisteredUser(name); + m_pUserDict->replace(u->name(),u); //u->name() because we're NOT copying keys! + emit(userAdded(name)); + return u; +} + +KviRegisteredUserGroup * KviRegisteredUserDataBase::addGroup(const QString & name) +{ + if(name.isEmpty()) return false; + if(m_pGroupDict->find(name))return 0; + KviRegisteredUserGroup * pGroup = new KviRegisteredUserGroup(name); + m_pGroupDict->replace(pGroup->name(),pGroup); //u->name() because we're NOT copying keys! + return pGroup; +} + +KviRegisteredUser * KviRegisteredUserDataBase::getUser(const QString & name) +{ + if(name.isEmpty()) return 0; + KviRegisteredUser * u = m_pUserDict->find(name); + if(!u) + { + u = new KviRegisteredUser(name); + m_pUserDict->replace(u->name(),u); //u->name() because we're NOT copying keys! + } + return u; +} + +static void append_mask_to_list(KviRegisteredMaskList *l,KviRegisteredUser *u,KviIrcMask *mask) +{ + KviRegisteredMask * newMask = new KviRegisteredMask(u,mask); + int idx = 0; + for(KviRegisteredMask * m = l->first();m;m = l->next()) + { + if(m->nonWildChars() < newMask->nonWildChars()) + { + l->insert(idx,newMask); + return; + } + idx++; + } + l->append(newMask); +} + +KviRegisteredUser * KviRegisteredUserDataBase::addMask(KviRegisteredUser * u,KviIrcMask * mask) +{ + if(!u || !mask) return 0; + __range_valid(u == m_pUserDict->find(u->name())); + + KviRegisteredMaskList * l; + if(mask->hasWildNick()) + { + for(KviRegisteredMask * m = m_pWildMaskList->first();m;m = m_pWildMaskList->next()) + { + if(*(m->mask()) == *mask) + { + delete mask; + mask = 0; + return m->user(); + } + } + // not found ...ok... add it + // masks with more info go first in the list + l = m_pWildMaskList; + } else { + l = m_pMaskDict->find(mask->nick()); + if(l) + { + // FIXME: #warning "Here we could compare the host and username only: nick matches for sure" + for(KviRegisteredMask * m = l->first();m;m = l->next()) + { + if(*(m->mask()) == *mask) + { + delete mask; + mask = 0; + return m->user(); + } + } + // not found ...ok... add it + } else { + // not found ...ok... add it + // this is the first mask in the list + l = new KviRegisteredMaskList; + l->setAutoDelete(true); + if(!u->addMask(mask)) + { + debug(" Ops...got an incoherent regusers action...recovered ?"); + delete l; + l = 0; + } else { + append_mask_to_list(l,u,mask); + m_pMaskDict->insert(mask->nick(),l); + } + return 0; + } + } + // Ok...add it + if(!u->addMask(mask)) + { + debug("ops...got an incoherent regusers action...recovered ?"); + return 0; // ops...already there ? + } + append_mask_to_list(l,u,mask); + return 0; +} + +void KviRegisteredUserDataBase::copyFrom(KviRegisteredUserDataBase * db) +{ + m_pUserDict->clear(); + m_pWildMaskList->clear(); + m_pMaskDict->clear(); + m_pGroupDict->clear(); + emit(databaseCleared()); + + KviPointerHashTableIterator it(*(db->m_pUserDict)); + + while(KviRegisteredUser * theCur = it.current()) + { + KviRegisteredUser * u = getUser(theCur->name()); + // copy masks + KviPointerList * l = theCur->maskList(); + for(KviIrcMask * m=l->first();m;m = l->next()) + { + KviIrcMask * m2 = new KviIrcMask(*m); + addMask(u,m2); + } + // copy properties + KviPointerHashTable * pd = theCur->propertyDict(); + if(pd) + { + KviPointerHashTableIterator pdi(*pd); + while(pdi.current()) + { + u->setProperty(pdi.currentKey(),*(pdi.current())); + ++pdi; + } + } + u->m_iIgnoreFlags=theCur->m_iIgnoreFlags; + u->m_bIgnoreEnabled=theCur->m_bIgnoreEnabled; + u->setGroup(theCur->group()); + ++it; + } + + KviPointerHashTableIterator git(*db->m_pGroupDict); + while(git.current()) + { + addGroup(git.currentKey()); + ++git; + } +} + + +bool KviRegisteredUserDataBase::removeUser(const QString & name) +{ + if(name.isEmpty()) return false; + KviRegisteredUser * u = m_pUserDict->find(name); + if(!u)return false; + while(KviIrcMask * mask = u->maskList()->first()) + { + if(!removeMaskByPointer(mask)) + debug("Ops... removeMaskByPointer(%s) failed ?",KviQString::toUtf8(name).data()); + } + emit(userRemoved(name)); + m_pUserDict->remove(name); + return true; +} +bool KviRegisteredUserDataBase::removeGroup(const QString & name) +{ + if(name.isEmpty()) return false; + m_pGroupDict->remove(name); + return true; +} + +bool KviRegisteredUserDataBase::removeMask(const KviIrcMask &mask) +{ + // find the mask pointer + KviRegisteredMask * m = findExactMask(mask); + // and remove it + if(m){ + if(removeMaskByPointer(m->mask())) + { + return true; + } + } + return 0; +} + +bool KviRegisteredUserDataBase::removeMaskByPointer(KviIrcMask * mask) +{ + if(!mask) return 0; + if(mask->hasWildNick()) + { + // remove from the wild list + for(KviRegisteredMask * m = m_pWildMaskList->first();m;m = m_pWildMaskList->next()) + { + if(m->mask() == mask) + { + // ok..got it, remove from the list and from the user struct (user struct deletes it!) + emit(userChanged(mask->nick())); + m->user()->removeMask(mask); // this one deletes m->mask() + m_pWildMaskList->removeRef(m); // this one deletes m + return true; + } + } + // not found ...opz :) + } else { + KviRegisteredMaskList * l = m_pMaskDict->find(mask->nick()); + if(l) + { + // FIXME: #warning "Here we could compare the host and username only: nick matches for sure" + for(KviRegisteredMask * m = l->first();m;m = l->next()) + { + if(m->mask() == mask) + { + QString nick = mask->nick(); + emit(userChanged(nick)); + m->user()->removeMask(mask); // this one deletes m->mask() (or mask) + l->removeRef(m); // this one deletes m + if(l->count() == 0)m_pMaskDict->remove(nick); + return true; + } + } + // not found ...opz + } + } + // not found... + return false; +} + + + +/* +KviRegisteredUser * KviRegisteredUserDataBase::findMatchingUser(const KviIrcMask &mask) +{ + // first lookup the nickname in the maskDict + KviRegisteredMaskList * l = m_pMaskDict->find(mask.nick()); + if(l) + { + for(KviRegisteredMask *m = l->first();m;m = l->next()) + { + if(m->mask()->matchesFixed(0,mask.user(),mask.host()))return m->user(); + } + } + // not found....lookup the wild ones + for(KviRegisteredMask * m = m_pWildMaskList->first();m;m = m_pWildMaskList->next()) + { + if(m->mask()->matchesFixed(mask))return m->user(); + } + return 0; // no match at all +} +*/ +KviRegisteredUser * KviRegisteredUserDataBase::findMatchingUser(const QString & nick,const QString &user,const QString & host) +{ + KviRegisteredMask * m = findMatchingMask(nick,user,host); + if(m)return m->user(); + return 0; // no match at all +} + +KviRegisteredMask * KviRegisteredUserDataBase::findMatchingMask(const QString & nick,const QString &user,const QString & host) +{ + // first lookup the nickname in the maskDict + if(nick.isEmpty()) return false; + KviRegisteredMaskList * l = m_pMaskDict->find(nick); + if(l) + { + for(KviRegisteredMask *m = l->first();m;m = l->next()) + { + if(m->mask()->matchesFixed(nick,user,host))return m; + } + } + // not found....lookup the wild ones + for(KviRegisteredMask * m = m_pWildMaskList->first();m;m = m_pWildMaskList->next()) + { + if(m->mask()->matchesFixed(nick,user,host))return m; + } + return 0; // no match at all +} + +KviRegisteredUser * KviRegisteredUserDataBase::findUserWithMask(const KviIrcMask &mask) +{ + KviRegisteredMask * m = findExactMask(mask); + if(m)return m->user(); + return 0; +} + +KviRegisteredMask * KviRegisteredUserDataBase::findExactMask(const KviIrcMask &mask) +{ + // first lookup the nickname in the maskDict + if(mask.nick()=="") return 0; + KviRegisteredMaskList * l = m_pMaskDict->find(mask.nick()); + if(l) + { + for(KviRegisteredMask *m = l->first();m;m = l->next()) + { + if(*(m->mask()) == mask)return m; + } + } + // not found....lookup the wild ones + for(KviRegisteredMask * m = m_pWildMaskList->first();m;m = m_pWildMaskList->next()) + { + if(*(m->mask()) == mask)return m; + } + return 0; // no match at all +} +/* +bool KviRegisteredUserDataBase::isIgnoredUser(const QString & nick,const QString & user,const QString & host) +{ + KviRegisteredUser * u = findMatchingUser(nick,user,host); + if(u)return u->getBoolProperty("IGNORE"); + else return false; +} +*/ +void KviRegisteredUserDataBase::load(const QString & filename) +{ + QString szCurrent; + KviConfig cfg(filename,KviConfig::Read); + + KviConfigIterator it(*cfg.dict()); + while(it.current()) + { + cfg.setGroup(it.currentKey()); + szCurrent=it.currentKey(); + if(KviQString::equalCSN("#Group ",szCurrent,7)) + { + szCurrent.remove(0,7); + addGroup(szCurrent); + } else { + KviRegisteredUser * u = addUser(szCurrent); + + if(u) + { + u->setIgnoreEnabled(cfg.readBoolEntry("IgnoreEnabled",false)); + u->setIgnoreFlags(cfg.readIntEntry("IgnoreFlags",0)); + KviConfigGroupIterator sdi(*(it.current())); + while(sdi.current()) + { + QString tmp = sdi.currentKey(); + if(KviQString::equalCSN("prop_",tmp,5)) + { + tmp.remove(0,5); + u->setProperty(tmp,*(sdi.current())); + } else if(KviQString::equalCSN("mask_",tmp,5)) + { + KviIrcMask * mask = new KviIrcMask(*(sdi.current())); + addMask(u,mask); + } else if(KviQString::equalCI(tmp,"Group")) + { + u->setGroup(*(sdi.current())); + } + ++sdi; + } + } + } + ++it; + } + if(!m_pGroupDict->find(__tr("Default"))) + addGroup(__tr("Default")); +} + + +void KviRegisteredUserDataBase::save(const QString & filename) +{ + KviConfig cfg(filename,KviConfig::Write); + cfg.clear(); + cfg.preserveEmptyGroups(true); + + KviPointerHashTableIterator it(*m_pUserDict); + + while(it.current()) + { + cfg.setGroup(it.current()->name()); + // Write properties + cfg.writeEntry("IgnoreEnabled",it.current()->ignoreEnagled()); + cfg.writeEntry("IgnoreFlags",it.current()->ignoreFlags()); + if(it.current()->propertyDict()) + { + KviPointerHashTableIterator pit(*(it.current()->propertyDict())); + while(pit.current()) + { + QString tmp = "prop_"; + tmp.append(pit.currentKey()); + cfg.writeEntry(tmp,*(pit.current())); + ++pit; + } + } + // Write masks + int idx = 0; + for(KviIrcMask * m = it.current()->maskList()->first();m;m = it.current()->maskList()->next()) + { + QString tmp; + KviQString::sprintf(tmp,"mask_%d",idx); + QString mask; + m->mask(mask,KviIrcMask::NickUserHost); + cfg.writeEntry(tmp,mask); + ++idx; + } + cfg.writeEntry("Group",it.current()->group()); + ++it; + } + + KviPointerHashTableIterator git(*m_pGroupDict); + QString szTmp; + while(git.current()) + { + KviQString::sprintf(szTmp,"#Group %Q",&(git.current()->name())); + cfg.setGroup(szTmp); + ++git; + } + +} -- cgit v1.2.1