added new msg tag feature for retroshare messages.

update rsmsg iface so need full recompile for gui
added serialiser test for new tag rsitems



git-svn-id: http://svn.code.sf.net/p/retroshare/code/trunk@3379 b45a01b8-16f6-495d-af2f-9b41ad6348cc
This commit is contained in:
chrisparker126 2010-08-19 21:47:26 +00:00
parent c6537bbe09
commit 331ed93720
10 changed files with 564 additions and 21 deletions

View File

@ -91,6 +91,25 @@ class MsgInfoSummary
};
class MsgTagInfo
{
public:
MsgTagInfo() {}
uint32_t tagId;
std::string msgId;
};
class MsgTagType
{
public:
MsgTagType() {}
/* map containing tagid-> pair (text, rgb color) */
std::map<uint32_t, std::pair<std::string, uint32_t> > types;
};
#define RS_CHAT_PUBLIC 0x0001
#define RS_CHAT_PRIVATE 0x0002
#define RS_CHAT_AVATAR_AVAILABLE 0x0004
@ -106,6 +125,9 @@ class ChatInfo
std::ostream &operator<<(std::ostream &out, const MessageInfo &info);
std::ostream &operator<<(std::ostream &out, const ChatInfo &info);
//std::ostream &operator<<(std::ostream &out, const MsgTagInfo);
//std::ostream &operator<<(std::ostream &out, const MsgTagType);
class RsMsgs;
extern RsMsgs *rsMsgs;
@ -131,6 +153,14 @@ virtual bool MessageToTrash(std::string mid, bool bTrash) = 0;
virtual bool MessageDelete(std::string mid) = 0;
virtual bool MessageRead(std::string mid) = 0;
/* message tagging */
virtual bool MessageGetTagTypes(MsgTagType& tags) = 0;
virtual bool MessageGetMsgTag(std::string msgId, MsgTagInfo& info) = 0;
virtual bool MessageSetTagType(std::string& text, uint32_t tag_id, uint32_t rgb_color) = 0;
virtual bool MessageSetMsgTag(MsgTagInfo& ) = 0;
/****************************************/
/* Chat */
virtual bool chatAvailable() = 0;

View File

@ -107,6 +107,26 @@ bool p3Msgs::MessageRead(std::string mid)
return 1;
}
bool p3Msgs::MessageGetTagTypes(MsgTagType& tags)
{
mMsgSrv->MessageGetTagTypes(tags);
}
bool p3Msgs::MessageGetMsgTag(std::string msgId, MsgTagInfo& info)
{
mMsgSrv->MessageGetMsgTag(msgId, info);
}
bool p3Msgs::MessageSetTagType(std::string& text, uint32_t tag_id, uint32_t rgb_color)
{
mMsgSrv->MessageSetTagType(text, tag_id, rgb_color);
}
bool p3Msgs::MessageSetMsgTag(MsgTagInfo& tagInfo)
{
mMsgSrv->MessageSetMsgTag(tagInfo);
}
/****************************************/
/****************************************/
bool p3Msgs::ChatSend(ChatInfo &ci)

View File

@ -63,6 +63,12 @@ class p3Msgs: public RsMsgs
virtual bool MessageDelete(std::string mid);
virtual bool MessageRead(std::string mid);
virtual bool MessageGetTagTypes(MsgTagType& tags);
virtual bool MessageGetMsgTag(std::string msgId, MsgTagInfo& info);
virtual bool MessageSetTagType(std::string& text, uint32_t tag_id, uint32_t rgb_color);
virtual bool MessageSetMsgTag(MsgTagInfo& );
/*!
* gets avatar from peer, image data in jpeg format
*/

View File

@ -412,8 +412,43 @@ std::ostream &RsMsgItem::print(std::ostream &out, uint16_t indent)
return out;
}
void RsMsgTagType::clear()
{
text.clear();
tagId = 0;
rgb_color = 0;
}
uint32_t RsMsgSerialiser::sizeItem(RsMsgItem *item)
void RsMsgTags::clear()
{
msgId.clear();
tagId = 0;
}
std::ostream& RsMsgTagType::print(std::ostream &out, uint16_t indent)
{
return out;
}
std::ostream& RsMsgTags::print(std::ostream &out, uint16_t indent)
{
return out;
}
RsMsgTagType::~RsMsgTagType()
{
return;
}
RsMsgTags::~RsMsgTags()
{
return;
}
uint32_t RsMsgSerialiser::sizeMsgItem(RsMsgItem *item)
{
uint32_t s = 8; /* header */
s += 4; /* msgFlags */
@ -437,9 +472,9 @@ uint32_t RsMsgSerialiser::sizeItem(RsMsgItem *item)
}
/* serialise the data to the buffer */
bool RsMsgSerialiser::serialiseItem(RsMsgItem *item, void *data, uint32_t *pktsize)
bool RsMsgSerialiser::serialiseMsgItem(RsMsgItem *item, void *data, uint32_t *pktsize)
{
uint32_t tlvsize = sizeItem(item);
uint32_t tlvsize = sizeMsgItem(item);
uint32_t offset = 0;
if (*pktsize < tlvsize)
@ -489,7 +524,7 @@ bool RsMsgSerialiser::serialiseItem(RsMsgItem *item, void *data, uint32_t *p
return ok;
}
RsMsgItem *RsMsgSerialiser::deserialiseItem(void *data, uint32_t *pktsize)
RsMsgItem *RsMsgSerialiser::deserialiseMsgItem(void *data, uint32_t *pktsize)
{
/* get the type and size */
uint32_t rstype = getRsItemId(data);
@ -554,19 +589,289 @@ RsMsgItem *RsMsgSerialiser::deserialiseItem(void *data, uint32_t *pktsize)
return item;
}
uint32_t RsMsgSerialiser::size(RsItem *item)
uint32_t RsMsgSerialiser::sizeTagItem(RsMsgTagType* item)
{
return sizeItem((RsMsgItem *) item);
uint32_t s = 8; /* header */
s += GetTlvStringSize(item->text);
s += 4; /* color */
s += 4; /* tag id */
return s;
}
bool RsMsgSerialiser::serialise(RsItem *item, void *data, uint32_t *pktsize)
bool RsMsgSerialiser::serialiseTagItem(RsMsgTagType *item, void *data, uint32_t* pktsize)
{
return serialiseItem((RsMsgItem *) item, data, pktsize);
uint32_t tlvsize = sizeTagItem(item);
uint32_t offset = 0;
if (*pktsize < tlvsize)
return false; /* not enough space */
*pktsize = tlvsize;
bool ok = true;
ok &= setRsItemHeader(data, tlvsize, item->PacketId(), tlvsize);
#ifdef RSSERIAL_DEBUG
std::cerr << "RsMsgSerialiser::serialiseMsgTagItem() Header: " << ok << std::endl;
std::cerr << "RsMsgSerialiser::serialiseMsgTagItem() Size: " << tlvsize << std::endl;
#endif
/* skip the header */
offset += 8;
/* add mandatory parts first */
ok &= SetTlvString(data,tlvsize,&offset, TLV_TYPE_STR_NAME, item->text);
ok &= setRawUInt32(data, tlvsize, &offset, item->rgb_color);
ok &= setRawUInt32(data, tlvsize, &offset, item->tagId);
if (offset != tlvsize)
{
ok = false;
#ifdef RSSERIAL_DEBUG
std::cerr << "RsMsgSerialiser::serialiseMsgTagItem() Size Error! " << std::endl;
#endif
}
return ok;
}
RsItem *RsMsgSerialiser::deserialise(void *data, uint32_t *pktsize)
RsMsgTagType* RsMsgSerialiser::deserialiseTagItem(void *data,uint32_t* pktsize)
{
return deserialiseItem(data, pktsize);
/* get the type and size */
uint32_t rstype = getRsItemId(data);
uint32_t rssize = getRsItemSize(data);
uint32_t offset = 0;
if ((RS_PKT_VERSION_SERVICE != getRsItemVersion(rstype)) ||
(RS_SERVICE_TYPE_MSG != getRsItemService(rstype)) ||
(RS_PKT_SUBTYPE_MSG_TAG_TYPE != getRsItemSubType(rstype)))
{
return NULL; /* wrong type */
}
if (*pktsize < rssize) /* check size */
return NULL; /* not enough data */
/* set the packet length */
*pktsize = rssize;
bool ok = true;
/* ready to load */
RsMsgTagType *item = new RsMsgTagType();
item->clear();
/* skip the header */
offset += 8;
/* get mandatory parts first */
ok &= GetTlvString(data,rssize,&offset,TLV_TYPE_STR_NAME,item->text);
ok &= getRawUInt32(data, rssize, &offset, &(item->rgb_color));
ok &= getRawUInt32(data, rssize, &offset, &(item->tagId));
if (offset != rssize)
{
/* error */
delete item;
return NULL;
}
if (!ok)
{
delete item;
return NULL;
}
return item;
}
uint32_t RsMsgSerialiser::sizeMsgTagItem(RsMsgTags* item)
{
uint32_t s = 8; /* header */
s += GetTlvStringSize(item->msgId);
s += 4; /* tag id */
return s;
}
bool RsMsgSerialiser::serialiseMsgTagItem(RsMsgTags *item, void *data, uint32_t* pktsize)
{
uint32_t tlvsize = sizeMsgTagItem(item);
uint32_t offset = 0;
if (*pktsize < tlvsize)
return false; /* not enough space */
*pktsize = tlvsize;
bool ok = true;
ok &= setRsItemHeader(data, tlvsize, item->PacketId(), tlvsize);
#ifdef RSSERIAL_DEBUG
std::cerr << "RsMsgSerialiser::serialiseMsgTagItem() Header: " << ok << std::endl;
std::cerr << "RsMsgSerialiser::serialiseMsgTagItem() Size: " << tlvsize << std::endl;
#endif
/* skip the header */
offset += 8;
ok &= SetTlvString(data,tlvsize,&offset, TLV_TYPE_STR_MSGID, item->msgId);
ok &= setRawUInt32(data, tlvsize, &offset, item->tagId);
if (offset != tlvsize)
{
ok = false;
#ifdef RSSERIAL_DEBUG
std::cerr << "RsMsgSerialiser::serialiseMsgTagItem() Size Error! " << std::endl;
#endif
}
return ok;
}
RsMsgTags* RsMsgSerialiser::deserialiseMsgTagItem(void* data, uint32_t* pktsize)
{
/* get the type and size */
uint32_t rstype = getRsItemId(data);
uint32_t rssize = getRsItemSize(data);
uint32_t offset = 0;
if ((RS_PKT_VERSION_SERVICE != getRsItemVersion(rstype)) ||
(RS_SERVICE_TYPE_MSG != getRsItemService(rstype)) ||
(RS_PKT_SUBTYPE_MSG_TAGS != getRsItemSubType(rstype)))
{
return NULL; /* wrong type */
}
if (*pktsize < rssize) /* check size */
return NULL; /* not enough data */
/* set the packet length */
*pktsize = rssize;
bool ok = true;
/* ready to load */
RsMsgTags *item = new RsMsgTags();
item->clear();
/* skip the header */
offset += 8;
/* get mandatory parts first */
ok &= GetTlvString(data,rssize,&offset,TLV_TYPE_STR_MSGID,item->msgId);
ok &= getRawUInt32(data, rssize, &offset, &(item->tagId));
if (offset != rssize)
{
/* error */
delete item;
return NULL;
}
if (!ok)
{
delete item;
return NULL;
}
return item;
}
uint32_t RsMsgSerialiser::size(RsItem *i)
{
RsMsgItem *mi;
RsMsgTagType *mtt;
RsMsgTags *mts;
/* in order of frequency */
if (NULL != (mi = dynamic_cast<RsMsgItem *>(i)))
{
return sizeMsgItem(mi);
}
else if (NULL != (mtt = dynamic_cast<RsMsgTagType *>(i)))
{
return sizeTagItem(mtt);
}
else if (NULL != (mts = dynamic_cast<RsMsgTags *>(i)))
{
return sizeMsgTagItem(mts);
}
return 0;
}
bool RsMsgSerialiser::serialise(RsItem *i, void *data, uint32_t *pktsize)
{
#ifdef RSSERIAL_DEBUG
std::cerr << "RsMsgSerialiser::serialise()" << std::endl;
#endif
RsMsgItem *mi;
RsMsgTagType *mtt;
RsMsgTags *mts;
if (NULL != (mi = dynamic_cast<RsMsgItem *>(i)))
{
return serialiseMsgItem(mi, data, pktsize);
}
else if (NULL != (mtt = dynamic_cast<RsMsgTagType *>(i)))
{
return serialiseTagItem(mtt, data, pktsize);
}
else if (NULL != (mts = dynamic_cast<RsMsgTags *>(i)))
{
return serialiseMsgTagItem(mts, data, pktsize);
}
return false;
}
RsItem* RsMsgSerialiser::deserialise(void *data, uint32_t *pktsize)
{
#ifdef RSSERIAL_DEBUG
std::cerr << "RsMsgSerialiser::deserialise()" << std::endl;
#endif
/* get the type and size */
uint32_t rstype = getRsItemId(data);
if ((RS_PKT_VERSION_SERVICE != getRsItemVersion(rstype)) ||
(RS_SERVICE_TYPE_MSG != getRsItemService(rstype)))
{
return NULL; /* wrong type */
}
switch(getRsItemSubType(rstype))
{
case RS_PKT_SUBTYPE_DEFAULT:
return deserialiseMsgItem(data, pktsize);
break;
case RS_PKT_SUBTYPE_MSG_TAG_TYPE:
return deserialiseTagItem(data, pktsize);
break;
case RS_PKT_SUBTYPE_MSG_TAGS:
return deserialiseMsgTagItem(data, pktsize);
break;
default:
return NULL;
break;
}
return NULL;
}

View File

@ -47,6 +47,11 @@ const uint32_t RS_CHAT_FLAG_CUSTOM_STATE_AVAILABLE = 0x0080;
const uint8_t RS_PKT_SUBTYPE_CHAT_AVATAR = 0x03 ; // default is 0x01
const uint8_t RS_PKT_SUBTYPE_CHAT_STATUS = 0x04 ; // default is 0x01
// for defining tags themselves and msg tags
const uint8_t RS_PKT_SUBTYPE_MSG_TAG_TYPE = 0x03;
const uint8_t RS_PKT_SUBTYPE_MSG_TAGS = 0x04;
class RsChatItem: public RsItem
{
public:
@ -178,6 +183,43 @@ std::ostream &print(std::ostream &out, uint16_t indent = 0);
RsTlvFileSet attachment;
};
class RsMsgTagType : public RsItem
{
public:
RsMsgTagType()
:RsItem(RS_PKT_VERSION_SERVICE, RS_SERVICE_TYPE_MSG,
RS_PKT_SUBTYPE_MSG_TAG_TYPE)
{ return; }
std::ostream &print(std::ostream &out, uint16_t indent = 0);
virtual ~RsMsgTagType();
virtual void clear();
std::string text;
uint32_t rgb_color;
uint32_t tagId;
};
class RsMsgTags : public RsItem
{
public:
RsMsgTags()
:RsItem(RS_PKT_VERSION_SERVICE, RS_SERVICE_TYPE_MSG,
RS_PKT_SUBTYPE_MSG_TAGS)
{ return; }
std::ostream &print(std::ostream &out, uint16_t indent = 0);
virtual ~RsMsgTags();
virtual void clear();
std::string msgId;
uint32_t tagId;
};
class RsMsgSerialiser: public RsSerialType
{
public:
@ -198,9 +240,19 @@ virtual RsItem * deserialise(void *data, uint32_t *size);
private:
virtual uint32_t sizeItem(RsMsgItem *);
virtual bool serialiseItem (RsMsgItem *item, void *data, uint32_t *size);
virtual RsMsgItem *deserialiseItem(void *data, uint32_t *size);
virtual uint32_t sizeMsgItem(RsMsgItem *);
virtual bool serialiseMsgItem (RsMsgItem *item, void *data, uint32_t *size);
virtual RsMsgItem *deserialiseMsgItem(void *data, uint32_t *size);
virtual uint32_t sizeTagItem(RsMsgTagType *);
virtual bool serialiseTagItem (RsMsgTagType *item, void *data, uint32_t *size);
virtual RsMsgTagType *deserialiseTagItem(void *data, uint32_t *size);
virtual uint32_t sizeMsgTagItem(RsMsgTags *);
virtual bool serialiseMsgTagItem (RsMsgTags *item, void *data, uint32_t *size);
virtual RsMsgTags *deserialiseMsgTagItem(void *data, uint32_t *size);
bool m_bConfiguration; // is set to true for saving configuration (enables serialising msgId)
};

View File

@ -299,6 +299,16 @@ bool p3MsgService::saveConfiguration()
for(mit = msgOutgoing.begin(); mit != msgOutgoing.end(); mit++)
written = written && pa_out -> SendItem(mit->second) ;
std::map<uint32_t, RsMsgTagType* >::iterator mit2;
std::map<std::string, RsMsgTags* >::iterator mit3;
for(mit2 = mTags.begin(); mit2 != mTags.end(); mit2++)
written = written && pa_out -> SendItem(mit2->second);
for(mit3 = mMsgTags.begin(); mit3 != mMsgTags.end(); mit3++)
written = written && pa_out -> SendItem(mit3->second);
setHash(out->gethash());
delete pa_out;
@ -322,9 +332,14 @@ bool p3MsgService::loadConfiguration(std::string &loadHash)
rss->addSerialType(new RsMsgSerialiser(true)); // create serialiser for configuration
BinFileInterface *in = new BinFileInterface(msgfile.c_str(), BIN_FLAGS_READABLE | BIN_FLAGS_HASH_DATA);
pqiarchive *pa_in = new pqiarchive(rss, in, BIN_FLAGS_READABLE);
RsItem *item;
RsMsgItem *mitem;
pqiarchive *pa_in = new pqiarchive(rss, in, BIN_FLAGS_READABLE);
RsItem *item;
RsMsgItem *mitem;
RsMsgTagType* mtt;
RsMsgTags* mti;
std::list<RsMsgItem*> items;
@ -339,6 +354,14 @@ bool p3MsgService::loadConfiguration(std::string &loadHash)
}
items.push_back(mitem);
}
else if(NULL != (mtt = dynamic_cast<RsMsgTagType *>(item)))
{
mTags.insert(std::pair<uint32_t, RsMsgTagType* >(mtt->tagId, mtt));
}
else if(NULL != (mti = dynamic_cast<RsMsgTags *>(item)))
{
mMsgTags.insert(std::pair<std::string, RsMsgTags* >(mti->msgId, mti));
}
else
{
delete item;
@ -737,6 +760,61 @@ bool p3MsgService::MessageToDraft(MessageInfo &info)
return false;
}
bool p3MsgService::MessageGetTagTypes(MsgTagType& tags)
{
std::map<uint32_t, RsMsgTagType*>::iterator mit;
for(mit = mTags.begin(); mit != mTags.end(); mit++)
{
std::pair<std::string, uint32_t> p(mit->second->text, mit->second->rgb_color);
tags.types.insert(std::pair<uint32_t, std::pair<std::string, uint32_t> >(mit->first, p));
}
return true;
}
bool p3MsgService::MessageGetMsgTag(std::string msgId, MsgTagInfo& info)
{
std::map<std::string, RsMsgTags*>::iterator mit;
if(mMsgTags.end() != (mit = mMsgTags.find(msgId)))
{
info.msgId = mit->second->msgId;
info.tagId = mit->second->tagId;
return true;
}
std::cerr << "p3MsgService::MessageGetMsgTag: no tag found for msgId " << msgId << std::endl;
return false;
}
bool p3MsgService::MessageSetTagType(std::string& text, uint32_t tagId, uint32_t rgb_color)
{
RsMsgTagType* tagType = new RsMsgTagType();
tagType->rgb_color = rgb_color;
tagType->tagId = tagId;
tagType->text = text;
mTags.insert(std::pair<uint32_t, RsMsgTagType*>(tagId, tagType));
}
bool p3MsgService::MessageSetMsgTag(MsgTagInfo& tagInfo)
{
RsMsgTags* tag = new RsMsgTags();
tag->msgId = tagInfo.msgId;
tag->tagId = tagInfo.tagId;
mMsgTags.insert(std::pair<std::string, RsMsgTags*>(tag->msgId, tag));
}
/* move message to trash based on the unique mid */
bool p3MsgService::MessageToTrash(std::string mid, bool bTrash)
{

View File

@ -66,6 +66,11 @@ bool MessageSend(MessageInfo &info);
bool MessageToDraft(MessageInfo &info);
bool MessageToTrash(std::string mid, bool bTrash);
bool MessageGetTagTypes(MsgTagType& tags);
bool MessageGetMsgTag(std::string msgId, MsgTagInfo& info);
bool MessageSetTagType(std::string& text, uint32_t tag_id, uint32_t rgb_color);
bool MessageSetMsgTag(MsgTagInfo& );
void loadWelcomeMsg(); /* startup message */
@ -109,6 +114,12 @@ RsMsgItem *initMIRsMsg(MessageInfo &info, std::string to);
/* List of notifications to post via Toaster */
std::list<MsgInfoSummary> msgNotifications;
/* maps for tags types and msg tags */
std::map<uint32_t, RsMsgTagType*> mTags;
std::map<std::string, RsMsgTags*> mMsgTags;
Indicator msgChanged;
uint32_t mMsgUniqueId;

View File

@ -1,4 +1,3 @@
<<<<<<< .mine
/*
* libretroshare/src/serialiser: distribitem_test.cc
*
@ -30,7 +29,7 @@
#include "serialiser/rschannelitems.h"
#include "serialiser/rsforumitems.h"
#include "serialiser/rsblogitems.h"
#include "rsiface/rsdistrib.h"
#include "retroshare/rsdistrib.h"
#include "serialiser/rstlvutil.h"
#include "util/utest.h"
#include "support.h"

View File

@ -74,13 +74,33 @@ RsSerialType* init_item(RsMsgItem& mi)
randString(LARGE_STR, mi.message);
randString(SHORT_STR, mi.subject);
mi.recvTime = rand()%32;
mi.msgId = rand()%324232;
mi.recvTime = rand()%44252;
mi.sendTime = mi.recvTime;
mi.msgFlags = mi.recvTime;
return new RsMsgSerialiser(true);
}
RsSerialType* init_item(RsMsgTagType& mtt)
{
mtt.rgb_color = rand()%5353;
mtt.tagId = rand()%24242;
randString(SHORT_STR, mtt.text);
return new RsMsgSerialiser();
}
RsSerialType* init_item(RsMsgTags& mt)
{
randString(SHORT_STR, mt.msgId);
mt.tagId = rand()%3334;
return new RsMsgSerialiser();
}
bool operator ==(const RsChatMsgItem& cmiLeft,const RsChatMsgItem& cmiRight)
{
@ -131,6 +151,7 @@ bool operator ==(const RsMsgItem& miLeft, const RsMsgItem& miRight)
if(miLeft.recvTime != miRight.recvTime) return false;
if(miLeft.sendTime != miRight.sendTime) return false;
if(miLeft.subject != miRight.subject) return false;
if(miLeft.msgId != miRight.msgId) return false;
if(!(miLeft.attachment == miRight.attachment)) return false;
if(!(miLeft.msgbcc == miRight.msgbcc)) return false;
@ -140,7 +161,22 @@ bool operator ==(const RsMsgItem& miLeft, const RsMsgItem& miRight)
return true;
}
bool operator ==(const RsMsgTagType& mttLeft, const RsMsgTagType& mttRight)
{
if(mttLeft.rgb_color != mttRight.rgb_color) return false;
if(mttLeft.tagId != mttRight.tagId) return false;
if(mttLeft.text != mttRight.text) return false;
return true;
}
bool operator ==(const RsMsgTags& mtLeft, const RsMsgTags& mtRight)
{
if(mtLeft.msgId != mtRight.msgId) return false;
if(mtLeft.tagId != mtRight.tagId) return false;
return true;
}
int main()
{
@ -148,6 +184,10 @@ int main()
test_RsItem<RsChatStatusItem >(); REPORT("Serialise/Deserialise RsChatStatusItem");
test_RsItem<RsChatAvatarItem >(); REPORT("Serialise/Deserialise RsChatAvatarItem");
test_RsItem<RsMsgItem >(); REPORT("Serialise/Deserialise RsMsgItem");
test_RsItem<RsMsgTagType>(); REPORT("Serialise/Deserialise RsMsgTagType");
test_RsItem<RsMsgTags>(); REPORT("Serialise/Deserialise RsMsgTags");
std::cerr << std::endl;
FINALREPORT("RsMsgItem Tests");

View File

@ -35,12 +35,14 @@ RsSerialType* init_item(RsChatMsgItem& );
RsSerialType* init_item(RsChatStatusItem& );
RsSerialType* init_item(RsChatAvatarItem& );
RsSerialType* init_item(RsMsgItem& );
RsSerialType* init_item(RsMsgTagType& );
RsSerialType* init_item(RsMsgTags& );
bool operator ==(const RsChatMsgItem& ,const RsChatMsgItem& );
bool operator ==(const RsChatStatusItem& , const RsChatStatusItem& );
bool operator ==(const RsChatAvatarItem&, const RsChatAvatarItem& );
// TODO: does not test for msgId
bool operator ==(const RsMsgTagType&, const RsMsgTagType& );
bool operator ==(const RsMsgTags&, const RsMsgTags& );
bool operator ==(const RsMsgItem&, const RsMsgItem& );