[Licq-devel] ICQ_SNACxLIST_ROSTxREPLY processing revised
"Eugene Paskevich" <[email protected]> Tue, 24 Jun 2008 22:02:12 +0300
| Newsgroups | gmane.network.licq.devel |
|---|---|
| Organization | Raptor's NestSite |
| Message-ID | <[email protected]> |
Given that this reply is usually received in several packets and different parameters of a single contact are spread through different subpackets, it's quite inconvenitent to process users step-by-step, as opposed to other subtypes like groups, permit/deny preferences, etc... The patch in attachment makes users to be collected into a separate map and processed all at once. Ergo, only one user_updated signal for a single contact. Please review the patch and pay additional attention to memory usage and data types, since I suspect that this patch could be improved in these aspects. -- Eugene Paskevich | *==)----------- | Plug me into [email protected] | -----------(==* | The Matrix
licq_server_userlist.patch
(application/octet-stream, 14.7 KB)
Index: include/licq_icqd.h
===================================================================
--- include/licq_icqd.h (revision 6356)
+++ include/licq_icqd.h (working copy)
@@ -140,6 +140,36 @@
typedef std::list<CConversation *> ConversationList;
+/**
+ * Internal template class for storing and processing received contact list.
+ */
+class CUserProperties
+{
+public:
+ CUserProperties();
+ CUserProperties(const CUserProperties& object);
+ ~CUserProperties();
+
+private:
+ char* newAlias;
+ char* newCellular;
+ bool awaitingAuth;
+
+ unsigned short normalSid;
+ unsigned short groupId;
+
+ unsigned short visibleSid;
+ unsigned short invisibleSid;
+ bool inIgnoreList;
+
+ TLVList tlvs;
+
+friend class CICQDaemon;
+};
+
+typedef std::map<std::string, CUserProperties> ContactUserList;
+typedef ContactUserList::iterator ContactUserListIter;
+
//=====CICQDaemon===============================================================
enum EDaemonStatus {STATUS_ONLINE, STATUS_OFFLINE_MANUAL, STATUS_OFFLINE_FORCED };
@@ -752,6 +782,8 @@
static pthread_mutex_t mutex_reverseconnect;
static pthread_cond_t cond_reverseconnect_done;
+ ContactUserList receivedUserList;
+
ConversationList m_lConversations;
pthread_mutex_t mutex_conversations;
@@ -838,6 +870,8 @@
void ProcessListFam(CBuffer &, unsigned short);
void ProcessAuthFam(CBuffer &, unsigned short);
+ void ProcessUserList();
+
void ProcessSystemMessage(CBuffer &packet, unsigned long checkUin, unsigned short newCommand, time_t timeSent);
void ProcessMetaCommand(CBuffer &packet, unsigned short nMetaCommand, ICQEvent *e);
bool ProcessTcpPacket(TCPSocket *);
Index: src/icqd.cpp
===================================================================
--- src/icqd.cpp (revision 6356)
+++ src/icqd.cpp (working copy)
@@ -167,6 +167,8 @@
fifo_fs = NULL;
+ receivedUserList.clear();
+
// Begin parsing the config file
snprintf(m_szConfigFile, MAX_FILENAME_LEN, "%s/%s", BASE_DIR, "licq.conf");
m_szConfigFile[MAX_FILENAME_LEN - 1] = '\0';
@@ -2843,3 +2845,60 @@
return (!*pcEnd);
}
+
+CUserProperties::CUserProperties()
+ : newAlias(NULL),
+ newCellular(NULL),
+ awaitingAuth(false),
+ normalSid(0),
+ groupId(0),
+ visibleSid(0),
+ invisibleSid(0),
+ inIgnoreList(false)
+{
+ tlvs.clear();
+}
+
+CUserProperties::CUserProperties(const CUserProperties& object)
+{
+#define COPY(var) \
+ if (object.var == NULL) \
+ var = NULL; \
+ else \
+ { \
+ size_t len = strlen(object.var); \
+ var = new char[++len]; \
+ memcpy(var, object.var, len); \
+ }
+
+ COPY(newAlias);
+ COPY(newCellular);
+#undef COPY
+
+ awaitingAuth = object.awaitingAuth;
+ normalSid = object.normalSid;
+ groupId = object.groupId;
+ visibleSid = object.visibleSid;
+ invisibleSid = object.invisibleSid;
+ inIgnoreList = object.inIgnoreList;
+
+ tlvs.clear();
+ tlvs = object.tlvs;
+}
+
+CUserProperties::~CUserProperties()
+{
+ if (newAlias != NULL)
+ {
+ delete[] newAlias;
+ newAlias = NULL;
+ }
+
+ if (newCellular != NULL)
+ {
+ delete[] newCellular;
+ newCellular = NULL;
+ }
+
+ tlvs.clear();
+}
Index: src/icqd-srv.cpp
===================================================================
--- src/icqd-srv.cpp (revision 6356)
+++ src/icqd-srv.cpp (working copy)
@@ -4131,66 +4131,24 @@
case ICQ_SNACxLIST_ROSTxREPLY:
{
- static unsigned short nCount;
+ static unsigned short nCount = 0;
static bool sCheckExport = false;
- unsigned short nPacketCount;
- unsigned long nTime;
- if (nFlags & 0x0001)
- {
- if (!hasServerEvent(nSubSequence))
- gLog.Warn(tr("%sContact list without request.\n"), L_SRVxSTR);
- else
- gLog.Info(tr("%sReceived contact list.\n"), L_SRVxSTR);
- }
- else
- {
- // This is the last packet so mark it as done
- ICQEvent *e = DoneServerEvent(nSubSequence, EVENT_SUCCESS);
-
- if (e == NULL)
- {
- // Try acking it, even if we didn't expect it
- //gLog.Warn(tr("%sContact list without request.\n"), L_SRVxSTR);
- gLog.Info(tr("%sActivate server contact list.\n"), L_SRVxSTR);
- CSrvPacketTcp *p = new CPU_GenericFamily(ICQ_SNACxFAM_LIST, ICQ_SNACxLIST_ROSTxACK);
- SendEvent_Server(p);
- //This packet had the user list in it, but we didn't ask for it.
- //Nonetheless, it appears we won't get a second chance for this packet, so process it
- //break;
- }
-
- /* This isn't used anymore. At least with SSI Version 0.
- if (e->SNAC() == MAKESNAC(ICQ_SNACxFAM_LIST, ICQ_SNACxLIST_REQUESTxRIGHTS) ||
- e->SNAC() == MAKESNAC(ICQ_SNACxFAM_LIST, ICQ_SNACxLIST_REQUESTxROST))
- {
- packet.UnpackUnsignedLong();
- packet.UnpackUnsignedLong();
- }
- */
-
- gLog.Info(tr("%sReceived end of contact list.\n"), L_SRVxSTR);
- }
-
m_bOnlineNotifies = true;
packet.UnpackChar(); // SSI Version
- nPacketCount = packet.UnpackUnsignedShortBE();
+ unsigned short nPacketCount = packet.UnpackUnsignedShortBE();
nCount += nPacketCount;
- for (unsigned short i = 0; i < nPacketCount; i++)
+ while (nPacketCount-- != 0)
{
- char *szId;
- unsigned short nTag, nID, nType, nByteLen;
-
// Can't use UnpackUserString because this may be a group name
- szId = packet.UnpackStringBE();
- nTag = packet.UnpackUnsignedShortBE();
- nID = packet.UnpackUnsignedShortBE();
- nType = packet.UnpackUnsignedShortBE();
- nByteLen = packet.UnpackUnsignedShortBE();
+ char* szId = packet.UnpackStringBE();
+ unsigned short nTag = packet.UnpackUnsignedShortBE();
+ unsigned short nID = packet.UnpackUnsignedShortBE();
+ unsigned short nType = packet.UnpackUnsignedShortBE();
- char *szUnicodeName = gTranslator.FromUnicode(szId);
+ unsigned short nByteLen = packet.UnpackUnsignedShortBE();
if (nByteLen)
{
@@ -4210,97 +4168,44 @@
case ICQ_ROSTxINVISIBLE:
case ICQ_ROSTxIGNORE:
{
- if (!UseServerContactList()) break;
-
- char *szNewName = packet.UnpackStringTLV(0x0131);
- char *szSMSNumber = packet.UnpackStringTLV(0x013A);
- bool bAwaitingAuth = packet.hasTLV(0x0066);
+ if (!UseServerContactList())
+ break;
- bool isOnList = true;
- if (szId && !gUserManager.IsOnList(szId, LICQ_PPID))
- {
- isOnList = false;
- AddUserToList(szId, LICQ_PPID, false); // Don't notify server
+#define COPYTLV(type, var) \
+ if (packet.hasTLV(type)) \
+ { \
+ if (receivedUserList[szId].var != NULL) \
+ delete[] receivedUserList[szId].var; \
+ receivedUserList[szId].var = packet.UnpackStringTLV(type); \
}
- ICQUser *u = gUserManager.FetchUser(szId, LICQ_PPID, LOCK_W);
- if (u)
- {
- // For now, just save all the TLVs. We should change this to have awaiting auth check
- // for the 0x0066 TLV, SMS number if it has the 0x013A TLV, etc
- TLVList tlvList = packet.getTLVList();
- TLVListIter iter;
- for (iter = tlvList.begin(); iter != tlvList.end(); ++iter)
- {
- TLVPtr tlv = iter->second;
- u->AddTLV(tlv);
- }
+ COPYTLV(0x0131, newAlias);
+ COPYTLV(0x013A, newCellular);
+#undef COPYTLV
+ if (packet.hasTLV(0x0066))
+ receivedUserList[szId].awaitingAuth = true;
- u->SetGSID(nTag);
+ if (nTag != 0)
+ receivedUserList[szId].groupId = nTag;
- if (szNewName)
- u->SetAlias(szNewName);
+ if (nType == ICQ_ROSTxIGNORE)
+ receivedUserList[szId].inIgnoreList = true;
- if (szSMSNumber)
+ if (nID != 0)
+ {
+ if (nType == ICQ_ROSTxVISIBLE)
{
- char *szUnicodeSMS = gTranslator.FromUnicode(szSMSNumber);
- if (szUnicodeSMS)
- {
- u->SetCellularNumber(szUnicodeSMS);
- delete [] szUnicodeSMS;
- }
+ receivedUserList[szId].visibleSid = nID;
}
-
- u->SetAwaitingAuth(bAwaitingAuth);
-
- if (nType == ICQ_ROSTxINVISIBLE)
+ else if (nType == ICQ_ROSTxINVISIBLE)
{
- u->SetInvisibleList(true);
- u->SetInvisibleSID(nID);
+ receivedUserList[szId].invisibleSid = nID;
}
- else if (nType == ICQ_ROSTxVISIBLE)
- {
- u->SetVisibleList(true);
- u->SetVisibleSID(nID);
- }
else
{
- u->SetSID(nID);
-
- if (nType == ICQ_ROSTxNORMAL)
- {
- // Save the group that they are in
- u->AddToGroup(GROUPS_USER, gUserManager.GetGroupFromID(nTag));
- }
+ receivedUserList[szId].normalSid = nID;
}
-
- u->SetIgnoreList(nType == ICQ_ROSTxIGNORE);
-
- if (!isOnList)
- {
- // They aren't a new user if we added them to a server list
- u->SetNewUser(false);
- }
-
- // Save GSID, SID and group memberships
- u->SaveLicqInfo();
-
- PushPluginSignal(new CICQSignal(SIGNAL_UPDATExUSER, USER_GENERAL,
- u->IdString(), u->PPID()));
- gUserManager.DropUser(u);
}
-
- if (!isOnList)
- {
- gLog.Info(tr("%sAdded %s (%s) to list from server.\n"), L_SRVxSTR,
- (szNewName ? szNewName : szId), szId);
- }
-
- if (szNewName)
- delete [] szNewName;
- if (szSMSNumber)
- delete [] szSMSNumber;
-
break;
}
@@ -4312,6 +4217,8 @@
{
// Rename the group if we have it already or else add it
unsigned short nGroup = gUserManager.GetGroupFromID(nTag);
+ char* szUnicodeName = gTranslator.FromUnicode(szId);
+
if (nGroup == 0)
{
if (!gUserManager.AddGroup(szUnicodeName, nTag))
@@ -4322,6 +4229,9 @@
gUserManager.RenameGroup(nGroup, szUnicodeName, false);
}
+ if (szUnicodeName)
+ delete[] szUnicodeName;
+
// This is bad, i don't think we want to call this at all..
// it will add users to different groups that they werent even
// assigned to
@@ -4353,9 +4263,6 @@
}
} // switch (nType)
- if (szUnicodeName)
- delete [] szUnicodeName;
-
if (szId)
delete[] szId;
} // for count
@@ -4368,17 +4275,33 @@
}
// Update local info about contact list
- nTime = packet.UnpackUnsignedLongBE();
- ICQOwner *o = gUserManager.FetchOwner(LOCK_W);
+ unsigned long nTime = packet.UnpackUnsignedLongBE();
+ ICQOwner* o = gUserManager.FetchOwner(LICQ_PPID, LOCK_W);
o->SetSSTime(nTime);
o->SetSSCount(nCount);
gUserManager.DropOwner();
- gLog.Info(tr("%sActivate server contact list.\n"), L_SRVxSTR);
- CSrvPacketTcp *p = new CPU_GenericFamily(ICQ_SNACxFAM_LIST, ICQ_SNACxLIST_ROSTxACK);
- SendEvent_Server(p);
+ if (nFlags & 0x0001)
+ {
+ if (!hasServerEvent(nSubSequence))
+ gLog.Warn(tr("%sContact list without request.\n"), L_SRVxSTR);
+ else
+ gLog.Info(tr("%sReceived contact list.\n"), L_SRVxSTR);
+ }
+ else
+ {
+ // This is the last packet so mark it as done
+ DoneServerEvent(nSubSequence, EVENT_SUCCESS);
-
+ gLog.Info(tr("%sReceived end of contact list.\n"), L_SRVxSTR);
+
+ ProcessUserList();
+
+ gLog.Info(tr("%sActivating server contact list.\n"), L_SRVxSTR);
+ CSrvPacketTcp *p = new CPU_GenericFamily(ICQ_SNACxFAM_LIST, ICQ_SNACxLIST_ROSTxACK);
+ SendEvent_Server(p);
+ }
+
break;
} // case rost reply
@@ -6388,6 +6311,88 @@
}
}
+void CICQDaemon::ProcessUserList()
+{
+ if (receivedUserList.empty())
+ return;
+
+ ContactUserListIter iter;
+
+ for (iter = receivedUserList.begin(); iter != receivedUserList.end(); iter++)
+ {
+ const char* id = iter->first.c_str();
+ CUserProperties data = iter->second;
+
+ if (id == NULL || id[0] == '\0')
+ {
+ gLog.Warn(tr("%sEmpty User ID was received in the contact list.\n"),
+ L_SRVxSTR);
+ continue;
+ }
+
+ bool isOnList = gUserManager.IsOnList(id, LICQ_PPID);
+
+ if (!isOnList)
+ {
+ AddUserToList(id, LICQ_PPID, false, false); // Don't notify server
+ gLog.Info(tr("%sAdded %s (%s) to list from server.\n"),
+ L_SRVxSTR, (data.newAlias ? data.newAlias : id), id);
+ }
+
+ ICQUser* u = gUserManager.FetchUser(id, LICQ_PPID, LOCK_W);
+ if (u == NULL)
+ continue;
+
+ if (data.newAlias != NULL)
+ u->SetAlias(data.newAlias);
+
+ u->SetSID(data.normalSid);
+ u->SetGSID(data.groupId);
+ u->SetVisibleSID(data.visibleSid);
+ u->SetVisibleList(data.visibleSid != 0);
+ u->SetInvisibleSID(data.invisibleSid);
+ u->SetInvisibleList(data.invisibleSid != 0);
+ u->SetIgnoreList(data.inIgnoreList);
+
+ u->AddToGroup(GROUPS_USER, gUserManager.GetGroupFromID(data.groupId));
+
+ u->SetAwaitingAuth(data.awaitingAuth);
+
+ if (!isOnList)
+ {
+ // They aren't a new user if we added them to a server list
+ u->SetNewUser(false);
+ }
+
+ if (data.newCellular != NULL)
+ {
+ char* tmp = gTranslator.FromUnicode(data.newCellular);
+ if (tmp != NULL)
+ {
+ u->SetCellularNumber(tmp);
+ delete[] tmp;
+ }
+ }
+
+ // For now, just save all the TLVs. We should change this to have awaiting auth check
+ // for the 0x0066 TLV, SMS number if it has the 0x013A TLV, etc
+ TLVListIter tlvIter;
+ for (tlvIter = data.tlvs.begin(); tlvIter != data.tlvs.end(); tlvIter++)
+ {
+ TLVPtr tlv = tlvIter->second;
+ u->AddTLV(tlv);
+ }
+
+ // Save GSID, SID and group memberships
+ u->SaveLicqInfo();
+ gUserManager.DropUser(u);
+
+ PushPluginSignal(new CICQSignal(SIGNAL_UPDATExUSER, USER_GENERAL, id, LICQ_PPID));
+ }
+
+ receivedUserList.clear();
+}
+
//--------ProcessDataChannel---------------------------------------------------
void CICQDaemon::ProcessDataChannel(CBuffer &packet)