Merge branch 'master' into TheWire-rework-ui

This commit is contained in:
drbob 2020-07-30 21:45:59 +10:00
commit 0522c7907a
128 changed files with 8255 additions and 2140 deletions

View File

@ -57,7 +57,7 @@ before_script:
- if [ $TRAVIS_OS_NAME == linux ]; then qmake; fi
- >
if [ $TRAVIS_OS_NAME == osx ]; then
qmake CONFIG+=rs_macos10.14
qmake CONFIG+=rs_macos10.14 CONFIG+=c++14
INCLUDEPATH+=$(find /usr/local/Cellar/miniupnpc/*/include | head -n 1)
QMAKE_LIBDIR+=$(find /usr/local/Cellar/miniupnpc/*/lib/ | head -n 1)
INCLUDEPATH+=$(find /usr/local/Cellar/openssl*/*/include/ | head -n 1)

@ -1 +1 @@
Subproject commit 879d5dfe8dcd8995be753120cf1b8bab4dd2ec82
Subproject commit b0d7ae39fe9d9192848bbf7b8902d2188da895c9

View File

@ -219,7 +219,13 @@ bool DistributedChatService::checkSignature(RsChatLobbyBouncingObject *obj,const
// network pre-request key to allow message authentication.
mGixs->requestKey(obj->signature.keyId,peer_list,RsIdentityUsage(RS_SERVICE_TYPE_CHAT,RsIdentityUsage::CHAT_LOBBY_MSG_VALIDATION,RsGxsGroupId(),RsGxsMessageId(),obj->lobby_id));
mGixs->requestKey(obj->signature.keyId,peer_list,RsIdentityUsage(RsServiceType::CHAT,
RsIdentityUsage::CHAT_LOBBY_MSG_VALIDATION,
RsGxsGroupId(),
RsGxsMessageId(),
RsGxsMessageId(),
RsGxsMessageId(),
obj->lobby_id));
uint32_t size = RsChatSerialiser(RsSerializationFlags::SIGNATURE)
.size(dynamic_cast<RsItem*>(obj));
@ -238,7 +244,13 @@ bool DistributedChatService::checkSignature(RsChatLobbyBouncingObject *obj,const
}
uint32_t error_status ;
RsIdentityUsage use_info(RS_SERVICE_TYPE_CHAT,RsIdentityUsage::CHAT_LOBBY_MSG_VALIDATION,RsGxsGroupId(),RsGxsMessageId(),obj->lobby_id) ;
RsIdentityUsage use_info(RsServiceType::CHAT,
RsIdentityUsage::CHAT_LOBBY_MSG_VALIDATION,
RsGxsGroupId(),
RsGxsMessageId(),
RsGxsMessageId(),
RsGxsMessageId(),
obj->lobby_id) ;
if(!mGixs->validateData(memory,size,obj->signature,false,use_info,error_status))
{

View File

@ -450,7 +450,7 @@ void p3discovery2::recvIdentityList(const RsPeerId& pid,const std::list<RsGxsId>
std::cerr << "p3discovery2::recvIdentityList(): from peer " << pid << ": " << ids.size() << " identities" << std::endl;
#endif
RsIdentityUsage use_info(RS_SERVICE_TYPE_DISC,RsIdentityUsage::IDENTITY_DATA_UPDATE);
RsIdentityUsage use_info(RsServiceType::GOSSIP_DISCOVERY,RsIdentityUsage::IDENTITY_NEW_FROM_DISCOVERY);
for(auto it(ids.begin());it!=ids.end();++it)
{

View File

@ -2108,7 +2108,7 @@ bool p3GRouter::verifySignedDataItem(const RsGRouterAbstractMsgItem *item,const
if(!signature_serializer.serialise(const_cast<RsGRouterAbstractMsgItem*>(item),data,&data_size))
throw std::runtime_error("Cannot serialise signed data.");
RsIdentityUsage use(RS_SERVICE_TYPE_GROUTER,info);
RsIdentityUsage use(RsServiceType::GROUTER,info);
if(!mGixs->validateData( data, data_size, item->signature, true, use, error_status ))
{

View File

@ -651,6 +651,9 @@ bool GxsSecurity::encrypt(uint8_t *& out, uint32_t &outlen, const uint8_t *in, u
try
{
if(keys.empty())
throw std::runtime_error("EVP_SealInit will not be called with 0 keys. GxsSecurity::encrypt() was called with an empty set of destination keys!") ;
for(uint32_t i=0;i<keys.size();++i)
{
RSA *tmpkey = ::extractPublicKey(keys[i]) ;

View File

@ -110,8 +110,6 @@ const std::string RsGeneralDataService::MSG_META_STATUS = KEY_MSG_STATUS;
const uint32_t RsGeneralDataService::GXS_MAX_ITEM_SIZE = 1572864; // 1.5 Mbytes
static const uint32_t CACHE_ENTRY_GRACE_PERIOD = 600 ; // 10 minutes
static int addColumn(std::list<std::string> &list, const std::string &attribute)
{
list.push_back(attribute);
@ -123,7 +121,6 @@ RsDataService::RsDataService(const std::string &serviceDir, const std::string &d
: RsGeneralDataService(), mDbMutex("RsDataService"), mServiceDir(serviceDir), mDbName(dbName), mDbPath(mServiceDir + "/" + dbName), mServType(serviceType), mDb(NULL)
{
bool isNewDatabase = !RsDirUtil::fileExists(mDbPath);
mGrpMetaDataCache_ContainsAllDatabase = false ;
mDb = new RetroDb(mDbPath, RetroDb::OPEN_READWRITE_CREATE, key);
@ -488,8 +485,7 @@ bool RsDataService::finishReleaseUpdate(int release, bool result)
RsGxsGrpMetaData* RsDataService::locked_getGrpMeta(RetroCursor &c, int colOffset,bool use_cache)
{
#ifdef RS_DATA_SERVICE_DEBUG
std::cerr << "RsDataService::locked_getGrpMeta()";
std::cerr << std::endl;
std::cerr << "RsDataService::locked_getGrpMeta()" << std::endl;
#endif
bool ok = true;
@ -506,21 +502,17 @@ RsGxsGrpMetaData* RsDataService::locked_getGrpMeta(RetroCursor &c, int colOffset
RsGxsGrpMetaData* grpMeta ;
RsGxsGroupId grpId(tempId) ;
if(use_cache)
{
auto it = mGrpMetaDataCache.find(grpId) ;
if(grpId.isNull()) // not in the DB!
return nullptr;
if(it != mGrpMetaDataCache.end())
grpMeta = it->second ;
else
{
grpMeta = new RsGxsGrpMetaData();
mGrpMetaDataCache[grpId] = grpMeta ;
}
}
if(use_cache)
grpMeta = mGrpMetaDataCache.getOrCreateMeta(grpId);
else
grpMeta = new RsGxsGrpMetaData();
if(!grpMeta->mGroupId.isNull()) // the grpMeta is already initialized because it comes from the cache
return grpMeta;
grpMeta->mGroupId = RsGxsGroupId(tempId);
c.getString(mColGrpMeta_NxsIdentity + colOffset, tempId);
grpMeta->mAuthorId = RsGxsId(tempId);
@ -653,24 +645,40 @@ RsNxsGrp* RsDataService::locked_getGroup(RetroCursor &c)
return NULL;
}
RsGxsMsgMetaData* RsDataService::locked_getMsgMeta(RetroCursor &c, int colOffset)
RsGxsMsgMetaData* RsDataService::locked_getMsgMeta(RetroCursor &c, int colOffset,bool use_cache)
{
RsGxsMsgMetaData* msgMeta = new RsGxsMsgMetaData();
bool ok = true;
uint32_t data_len = 0,
offset = 0;
char* data = NULL;
RsGxsGroupId group_id;
RsGxsMessageId msg_id;
std::string gId;
c.getString(mColMsgMeta_GrpId + colOffset, gId);
msgMeta->mGroupId = RsGxsGroupId(gId);
group_id = RsGxsGroupId(gId);
std::string temp;
c.getString(mColMsgMeta_MsgId + colOffset, temp);
msgMeta->mMsgId = RsGxsMessageId(temp);
msg_id = RsGxsMessageId(temp);
// without these, a msg is meaningless
ok &= (!msgMeta->mGroupId.isNull()) && (!msgMeta->mMsgId.isNull());
if(group_id.isNull() || msg_id.isNull())
return nullptr;
RsGxsMsgMetaData* msgMeta = nullptr;
if(use_cache)
msgMeta = mMsgMetaDataCache[group_id].getOrCreateMeta(msg_id);
else
msgMeta = new RsGxsMsgMetaData();
if(!msgMeta->mGroupId.isNull()) // we cannot do that because the cursor needs to advance. Is there a method to skip some data in the db?
return msgMeta;
msgMeta->mGroupId = group_id;
msgMeta->mMsgId = msg_id;
c.getString(mColMsgMeta_OrigMsgId + colOffset, temp);
msgMeta->mOrigMsgId = RsGxsMessageId(temp);
@ -704,7 +712,7 @@ RsGxsMsgMetaData* RsDataService::locked_getMsgMeta(RetroCursor &c, int colOffset
if(ok)
return msgMeta;
else
else if(!use_cache)
delete msgMeta;
return NULL;
@ -834,7 +842,8 @@ int RsDataService::storeMessage(const std::list<RsNxsMsg*>& msg)
// This is needed so that mLastPost is correctly updated in the group meta when it is re-loaded.
locked_clearGrpMetaCache(msgMetaPtr->mGroupId);
mGrpMetaDataCache.clear(msgMetaPtr->mGroupId);
mMsgMetaDataCache[msgMetaPtr->mGroupId].updateMeta(msgMetaPtr->mMsgId,*msgMetaPtr);
}
// finish transaction
@ -926,7 +935,7 @@ int RsDataService::storeGroup(const std::list<RsNxsGrp*>& grp)
cv.put(KEY_GRP_STATUS, (int32_t)grpMetaPtr->mGroupStatus);
cv.put(KEY_GRP_LAST_POST, (int32_t)grpMetaPtr->mLastPost);
locked_updateGrpMetaCache(*grpMetaPtr);
mGrpMetaDataCache.updateMeta(grpMetaPtr->mGroupId,*grpMetaPtr);
if (!mDb->sqlInsert(GRP_TABLE_NAME, "", cv))
{
@ -942,54 +951,6 @@ int RsDataService::storeGroup(const std::list<RsNxsGrp*>& grp)
return ret;
}
void RsDataService::locked_updateGrpMetaCache(const RsGxsGrpMetaData& meta)
{
auto it = mGrpMetaDataCache.find(meta.mGroupId) ;
if(it != mGrpMetaDataCache.end())
*(it->second) = meta ;
else
mGrpMetaDataCache[meta.mGroupId] = new RsGxsGrpMetaData(meta) ;
}
void RsDataService::locked_clearGrpMetaCache(const RsGxsGroupId& gid)
{
rstime_t now = time(NULL) ;
auto it = mGrpMetaDataCache.find(gid) ;
// We dont actually delete the item, because it might be used by a calling client.
// In this case, the memory will not be used for long, so we keep it into a list for a safe amount
// of time and delete it later. Using smart pointers here would be more elegant, but that would need
// to be implemented thread safe, which is difficult in this case.
if(it != mGrpMetaDataCache.end())
{
#ifdef RS_DATA_SERVICE_DEBUG
std::cerr << "(II) moving database cache entry " << (void*)(*it).second << " to dead list." << std::endl;
#endif
mOldCachedItems.push_back(std::make_pair(now,it->second)) ;
mGrpMetaDataCache.erase(it) ;
mGrpMetaDataCache_ContainsAllDatabase = false;
}
// We also take that opportunity to delete old entries.
auto it2(mOldCachedItems.begin());
while(it2!=mOldCachedItems.end() && (*it2).first + CACHE_ENTRY_GRACE_PERIOD < now)
{
#ifdef RS_DATA_SERVICE_DEBUG
std::cerr << "(II) deleting old GXS database cache entry " << (void*)(*it2).second << ", " << now - (*it2).first << " seconds old." << std::endl;
#endif
delete (*it2).second ;
it2 = mOldCachedItems.erase(it2) ;
}
}
int RsDataService::updateGroup(const std::list<RsNxsGrp *> &grp)
{
@ -1058,7 +1019,7 @@ int RsDataService::updateGroup(const std::list<RsNxsGrp *> &grp)
mDb->sqlUpdate(GRP_TABLE_NAME, "grpId='" + grpPtr->grpId.toStdString() + "'", cv);
locked_updateGrpMetaCache(*grpMetaPtr);
mGrpMetaDataCache.updateMeta(grpMetaPtr->mGroupId,*grpMetaPtr);
}
// finish transaction
bool ret = mDb->commitTransaction();
@ -1275,7 +1236,7 @@ void RsDataService::locked_retrieveMessages(RetroCursor *c, std::vector<RsNxsMsg
if(m){
if (metaOffset) {
m->metaData = locked_getMsgMeta(*c, metaOffset);
m->metaData = locked_getMsgMeta(*c, metaOffset,false);
}
msgs.push_back(m);
}
@ -1285,7 +1246,7 @@ void RsDataService::locked_retrieveMessages(RetroCursor *c, std::vector<RsNxsMsg
return;
}
int RsDataService::retrieveGxsMsgMetaData(const GxsMsgReq& reqIds, GxsMsgMetaResult &msgMeta)
int RsDataService::retrieveGxsMsgMetaData(const GxsMsgReq& reqIds, GxsMsgMetaResult& msgMeta)
{
RsStackMutex stack(mDbMutex);
@ -1294,52 +1255,65 @@ int RsDataService::retrieveGxsMsgMetaData(const GxsMsgReq& reqIds, GxsMsgMetaRes
int resultCount = 0;
#endif
GxsMsgReq::const_iterator mit = reqIds.begin();
for(; mit != reqIds.end(); ++mit)
for(auto mit(reqIds.begin()); mit != reqIds.end(); ++mit)
{
const RsGxsGroupId& grpId = mit->first;
const std::set<RsGxsMessageId>& msgIdV = mit->second;
// if vector empty then request all messages
const std::set<RsGxsMessageId>& msgIdV = mit->second;
std::vector<RsGxsMsgMetaData*> metaSet;
if(msgIdV.empty()){
RetroCursor* c = mDb->sqlQuery(MSG_TABLE_NAME, mMsgMetaColumns, KEY_GRP_ID+ "='" + grpId.toStdString() + "'", "");
t_MetaDataCache<RsGxsMessageId,RsGxsMsgMetaData>& cache(mMsgMetaDataCache[grpId]);
if (c)
{
locked_retrieveMsgMeta(c, metaSet);
if(msgIdV.empty())
{
if(cache.isCacheUpToDate())
cache.getFullMetaList(msgMeta[grpId]);
else
{
RetroCursor* c = mDb->sqlQuery(MSG_TABLE_NAME, mMsgMetaColumns, KEY_GRP_ID+ "='" + grpId.toStdString() + "'", "");
if (c)
{
locked_retrieveMsgMetaList(c, msgMeta[grpId]);
cache.setCacheUpToDate(true);
}
delete c;
}
#ifdef RS_DATA_SERVICE_DEBUG_CACHE
std::cerr << mDbName << ": Retrieving (all) Msg metadata grpId=" << grpId << ", " << std::dec << metaSet.size() << " messages" << std::endl;
std::cerr << mDbName << ": Retrieving (all) Msg metadata grpId=" << grpId << ", " << std::dec << metaSet.size() << " messages" << std::endl;
#endif
}
}else{
// request each grp
std::set<RsGxsMessageId>::const_iterator sit = msgIdV.begin();
for(; sit!=msgIdV.end(); ++sit){
const RsGxsMessageId& msgId = *sit;
RetroCursor* c = mDb->sqlQuery(MSG_TABLE_NAME, mMsgMetaColumns, KEY_GRP_ID+ "='" + grpId.toStdString()
+ "' AND " + KEY_MSG_ID + "='" + msgId.toStdString() + "'", "");
if (c)
{
locked_retrieveMsgMeta(c, metaSet);
#ifdef RS_DATA_SERVICE_DEBUG_CACHE
std::cerr << mDbName << ": Retrieving Msg metadata grpId=" << grpId << ", " << std::dec << metaSet.size() << " messages" << std::endl;
#endif
}
}
}
else
{
// request each msg meta
auto& metaSet(msgMeta[grpId]);
#ifdef RS_DATA_SERVICE_DEBUG_TIME
resultCount += metaSet.size();
for(auto sit(msgIdV.begin()); sit!=msgIdV.end(); ++sit)
{
const RsGxsMessageId& msgId = *sit;
RsGxsMsgMetaData *meta = cache.getMeta(msgId);
if(meta)
metaSet.push_back(meta);
else
{
RetroCursor* c = mDb->sqlQuery(MSG_TABLE_NAME, mMsgMetaColumns, KEY_GRP_ID+ "='" + grpId.toStdString() + "' AND " + KEY_MSG_ID + "='" + msgId.toStdString() + "'", "");
c->moveToFirst();
RsGxsMsgMetaData* meta = locked_getMsgMeta(*c, 0,true);
if(meta)
metaSet.push_back(meta);
delete c;
}
}
#ifdef RS_DATA_SERVICE_DEBUG_CACHE
std::cerr << mDbName << ": Retrieving Msg metadata grpId=" << grpId << ", " << std::dec << metaSet.size() << " messages" << std::endl;
#endif
msgMeta[grpId] = metaSet;
}
}
#ifdef RS_DATA_SERVICE_DEBUG_TIME
@ -1350,22 +1324,43 @@ int RsDataService::retrieveGxsMsgMetaData(const GxsMsgReq& reqIds, GxsMsgMetaRes
return 1;
}
void RsDataService::locked_retrieveMsgMeta(RetroCursor *c, std::vector<RsGxsMsgMetaData *> &msgMeta)
void RsDataService::locked_retrieveGrpMetaList(RetroCursor *c, std::map<RsGxsGroupId,RsGxsGrpMetaData *>& grpMeta)
{
if(!c)
{
RsErr() << __PRETTY_FUNCTION__ << ": attempt to retrieve Group Meta data from the DB with null cursor!" << std::endl;
return;
}
if(c)
{
bool valid = c->moveToFirst();
while(valid){
RsGxsMsgMetaData* m = locked_getMsgMeta(*c, 0);
bool valid = c->moveToFirst();
if(m != NULL)
msgMeta.push_back(m);
while(valid)
{
RsGxsGrpMetaData* m = locked_getGrpMeta(*c, 0,true);
valid = c->moveToNext();
}
delete c;
}
if(m)
grpMeta[m->mGroupId] = m;
valid = c->moveToNext();
}
}
void RsDataService::locked_retrieveMsgMetaList(RetroCursor *c, std::vector<const RsGxsMsgMetaData *>& msgMeta)
{
if(!c)
{
RsErr() << __PRETTY_FUNCTION__ << ": attempt to retrieve Msg Meta data from the DB with null cursor!" << std::endl;
return;
}
bool valid = c->moveToFirst();
while(valid){
const RsGxsMsgMetaData* m = locked_getMsgMeta(*c, 0,true);
if(m != NULL)
msgMeta.push_back(m);
valid = c->moveToNext();
}
}
int RsDataService::retrieveGxsGrpMetaData(RsGxsGrpMetaTemporaryMap& grp)
@ -1385,13 +1380,13 @@ int RsDataService::retrieveGxsGrpMetaData(RsGxsGrpMetaTemporaryMap& grp)
if(grp.empty())
{
if(mGrpMetaDataCache_ContainsAllDatabase) // grab all the stash from the cache, so as to avoid decryption costs.
if(mGrpMetaDataCache.isCacheUpToDate()) // grab all the stash from the cache, so as to avoid decryption costs.
{
#ifdef RS_DATA_SERVICE_DEBUG_CACHE
std::cerr << (void*)this << ": RsDataService::retrieveGxsGrpMetaData() retrieving all from cache!" << std::endl;
#endif
grp = mGrpMetaDataCache ;
mGrpMetaDataCache.getFullMetaList(grp) ;
}
else
{
@ -1402,93 +1397,103 @@ int RsDataService::retrieveGxsGrpMetaData(RsGxsGrpMetaTemporaryMap& grp)
RetroCursor* c = mDb->sqlQuery(GRP_TABLE_NAME, mGrpMetaColumns, "", "");
if(c)
if(c)
{
bool valid = c->moveToFirst();
locked_retrieveGrpMetaList(c,grp);
while(valid)
{
RsGxsGrpMetaData* g = locked_getGrpMeta(*c, 0,true);
if(g)
{
grp[g->mGroupId] = g;
#ifdef RS_DATA_SERVICE_DEBUG_CACHE
std::cerr << (void *)this << " " << mDbName << ": Retrieving (all) Grp metadata grpId=" << g->mGroupId << std::endl;
#endif
}
valid = c->moveToNext();
#ifdef RS_DATA_SERVICE_DEBUG_TIME
++resultCount;
#endif
}
delete c;
mGrpMetaDataCache.setCacheUpToDate(true);
}
delete c;
#ifdef RS_DATA_SERVICE_DEBUG_TIME
resultCount += grp.size();
#endif
mGrpMetaDataCache_ContainsAllDatabase = true ;
// if(c)
// {
// bool valid = c->moveToFirst();
//
// while(valid)
// {
// RsGxsGrpMetaData* g = locked_getGrpMeta(*c, 0,true);
//
// if(g)
// {
// grp[g->mGroupId] = g;
//#ifdef RS_DATA_SERVICE_DEBUG_CACHE
// std::cerr << (void *)this << " " << mDbName << ": Retrieving (all) Grp metadata grpId=" << g->mGroupId << std::endl;
//#endif
// }
// valid = c->moveToNext();
//
// }
// delete c;
// }
}
}
else
{
std::map<RsGxsGroupId, RsGxsGrpMetaData *>::iterator mit = grp.begin();
{
for(auto mit(grp.begin()); mit != grp.end(); ++mit)
{
RsGxsGrpMetaData *meta = mGrpMetaDataCache.getMeta(mit->first) ;
for(; mit != grp.end(); ++mit)
{
std::map<RsGxsGroupId, RsGxsGrpMetaData*>::const_iterator itt = mGrpMetaDataCache.find(mit->first) ;
if(itt != mGrpMetaDataCache.end())
{
if(meta)
mit->second = meta;
else
{
#ifdef RS_DATA_SERVICE_DEBUG_CACHE
std::cerr << mDbName << ": Retrieving Grp metadata grpId=" << mit->first << " from cache!" << std::endl;
#endif
grp[mit->first] = itt->second ;
}
else
{
#ifdef RS_DATA_SERVICE_DEBUG_CACHE
std::cerr << mDbName << ": Retrieving Grp metadata grpId=" << mit->first ;
std::cerr << mDbName << ": Retrieving Grp metadata grpId=" << mit->first ;
#endif
const RsGxsGroupId& grpId = mit->first;
RetroCursor* c = mDb->sqlQuery(GRP_TABLE_NAME, mGrpMetaColumns, "grpId='" + grpId.toStdString() + "'", "");
const RsGxsGroupId& grpId = mit->first;
RetroCursor* c = mDb->sqlQuery(GRP_TABLE_NAME, mGrpMetaColumns, "grpId='" + grpId.toStdString() + "'", "");
if(c)
{
bool valid = c->moveToFirst();
c->moveToFirst();
RsGxsGrpMetaData* meta = locked_getGrpMeta(*c, 0,true);
#ifdef RS_DATA_SERVICE_DEBUG_CACHE
if(!valid)
std::cerr << " Empty query! GrpId " << grpId << " is not in database" << std::endl;
#endif
while(valid)
{
RsGxsGrpMetaData* g = locked_getGrpMeta(*c, 0,true);
if(g)
{
grp[g->mGroupId] = g;
#ifdef RS_DATA_SERVICE_DEBUG_CACHE
std::cerr << ". Got it. Updating cache." << std::endl;
#endif
}
valid = c->moveToNext();
if(meta)
mit->second = meta;
#ifdef RS_DATA_SERVICE_DEBUG_TIME
++resultCount;
++resultCount;
#endif
}
delete c;
}
#ifdef RS_DATA_SERVICE_DEBUG_CACHE
else
std::cerr << ". not found!" << std::endl;
#endif
}
}
}
delete c;
// if(c)
// {
// bool valid = c->moveToFirst();
//
//#ifdef RS_DATA_SERVICE_DEBUG_CACHE
// if(!valid)
// std::cerr << " Empty query! GrpId " << grpId << " is not in database" << std::endl;
//#endif
// while(valid)
// {
// RsGxsGrpMetaData* g = locked_getGrpMeta(*c, 0,true);
//
// if(g)
// {
// grp[g->mGroupId] = g;
//#ifdef RS_DATA_SERVICE_DEBUG_CACHE
// std::cerr << ". Got it. Updating cache." << std::endl;
//#endif
// }
// valid = c->moveToNext();
//
//#ifdef RS_DATA_SERVICE_DEBUG_TIME
// ++resultCount;
//#endif
// }
// delete c;
// }
#ifdef RS_DATA_SERVICE_DEBUG_CACHE
else
std::cerr << ". not found!" << std::endl;
#endif
}
}
}
#ifdef RS_DATA_SERVICE_DEBUG_TIME
std::cerr << "RsDataService::retrieveGxsGrpMetaData() " << mDbName << ", Requests: " << requestedGroups << ", Results: " << resultCount << ", Time: " << timer.duration() << std::endl;
@ -1525,35 +1530,37 @@ int RsDataService::resetDataStore()
return 1;
}
int RsDataService::updateGroupMetaData(GrpLocMetaData &meta)
int RsDataService::updateGroupMetaData(const GrpLocMetaData& meta)
{
#ifdef RS_DATA_SERVICE_DEBUG_CACHE
std::cerr << (void*)this << ": Updating Grp Meta data: grpId = " << meta.grpId << std::endl;
#endif
RsStackMutex stack(mDbMutex);
RsGxsGroupId& grpId = meta.grpId;
const RsGxsGroupId& grpId = meta.grpId;
#ifdef RS_DATA_SERVICE_DEBUG_CACHE
std::cerr << (void*)this << ": erasing old entry from cache." << std::endl;
#endif
locked_clearGrpMetaCache(meta.grpId);
mGrpMetaDataCache.clear(meta.grpId);
return mDb->sqlUpdate(GRP_TABLE_NAME, KEY_GRP_ID+ "='" + grpId.toStdString() + "'", meta.val) ? 1 : 0;
}
int RsDataService::updateMessageMetaData(MsgLocMetaData &metaData)
int RsDataService::updateMessageMetaData(const MsgLocMetaData& metaData)
{
#ifdef RS_DATA_SERVICE_DEBUG_CACHE
std::cerr << (void*)this << ": Updating Msg Meta data: grpId = " << metaData.msgId.first << " msgId = " << metaData.msgId.second << std::endl;
#endif
RsStackMutex stack(mDbMutex);
RsGxsGroupId& grpId = metaData.msgId.first;
RsGxsMessageId& msgId = metaData.msgId.second;
return mDb->sqlUpdate(MSG_TABLE_NAME, KEY_GRP_ID+ "='" + grpId.toStdString()
+ "' AND " + KEY_MSG_ID + "='" + msgId.toStdString() + "'", metaData.val) ? 1 : 0;
const RsGxsGroupId& grpId = metaData.msgId.first;
const RsGxsMessageId& msgId = metaData.msgId.second;
mMsgMetaDataCache[grpId].clear(msgId);
return mDb->sqlUpdate(MSG_TABLE_NAME, KEY_GRP_ID+ "='" + grpId.toStdString() + "' AND " + KEY_MSG_ID + "='" + msgId.toStdString() + "'", metaData.val) ? 1 : 0;
}
int RsDataService::removeMsgs(const GxsMsgReq& msgIds)
@ -1678,13 +1685,13 @@ bool RsDataService::locked_removeMessageEntries(const GxsMsgReq& msgIds)
{
const RsGxsGroupId& grpId = mit->first;
const std::set<RsGxsMessageId>& msgsV = mit->second;
std::set<RsGxsMessageId>::const_iterator vit = msgsV.begin();
auto& cache(mMsgMetaDataCache[grpId]);
for(; vit != msgsV.end(); ++vit)
for(auto& msgId:msgsV)
{
const RsGxsMessageId& msgId = *vit;
mDb->sqlDelete(MSG_TABLE_NAME, KEY_GRP_ID+ "='" + grpId.toStdString()
+ "' AND " + KEY_MSG_ID + "='" + msgId.toStdString() + "'", "");
mDb->sqlDelete(MSG_TABLE_NAME, KEY_GRP_ID+ "='" + grpId.toStdString() + "' AND " + KEY_MSG_ID + "='" + msgId.toStdString() + "'", "");
cache.clear(msgId);
}
}
@ -1698,23 +1705,18 @@ bool RsDataService::locked_removeGroupEntries(const std::vector<RsGxsGroupId>& g
// start a transaction
bool ret = mDb->beginTransaction();
std::vector<RsGxsGroupId>::const_iterator vit = grpIds.begin();
for(; vit != grpIds.end(); ++vit)
for(auto grpId:grpIds)
{
const RsGxsGroupId& grpId = *vit;
mDb->sqlDelete(GRP_TABLE_NAME, KEY_GRP_ID+ "='" + grpId.toStdString() + "'", "");
// also remove the group meta from cache.
locked_clearGrpMetaCache(*vit) ;
mGrpMetaDataCache.clear(grpId) ;
}
ret &= mDb->commitTransaction();
mGrpMetaDataCache_ContainsAllDatabase = false ;
return ret;
}
uint32_t RsDataService::cacheSize() const {
return 0;
}
@ -1724,3 +1726,38 @@ int RsDataService::setCacheSize(uint32_t /* size */)
return 0;
}
void RsDataService::debug_printCacheSize() const
{
uint32_t nb_items,nb_items_on_deadlist;
uint64_t total_size,total_size_of_deadlist;
mGrpMetaDataCache.debug_computeSize(nb_items, nb_items_on_deadlist, total_size,total_size_of_deadlist);
RsDbg() << "Cache size: " << std::endl;
RsDbg() << " Groups: " << " total: " << nb_items << " (dead: " << nb_items_on_deadlist << "), size: " << total_size << " (Dead: " << total_size_of_deadlist << ")" << std::endl;
nb_items = 0,nb_items_on_deadlist = 0;
total_size = 0,total_size_of_deadlist = 0;
for(auto it:mMsgMetaDataCache)
{
uint32_t tmp_nb_items,tmp_nb_items_on_deadlist;
uint64_t tmp_total_size,tmp_total_size_of_deadlist;
it.second.debug_computeSize(tmp_nb_items, tmp_nb_items_on_deadlist, tmp_total_size,tmp_total_size_of_deadlist);
nb_items += tmp_nb_items;
nb_items_on_deadlist += tmp_nb_items_on_deadlist;
total_size += tmp_total_size;
total_size_of_deadlist += tmp_total_size_of_deadlist;
}
RsDbg() << " Msgs: " << " total: " << nb_items << " (dead: " << nb_items_on_deadlist << "), size: " << total_size << " (Dead: " << total_size_of_deadlist << ")" << std::endl;
}

View File

@ -35,6 +35,116 @@ public:
ContentValue cv;
};
template<class ID, class MetaDataClass> class t_MetaDataCache
{
public:
t_MetaDataCache() : mCache_ContainsAllMetas(false) {}
bool isCacheUpToDate() const { return mCache_ContainsAllMetas ; }
void setCacheUpToDate(bool b) { mCache_ContainsAllMetas = b; }
void getFullMetaList(std::map<ID,MetaDataClass*>& mp) const { mp = mMetas ; }
void getFullMetaList(std::vector<const MetaDataClass*>& mp) const { for(auto& m:mMetas) mp.push_back(m.second) ; }
MetaDataClass *getMeta(const ID& id)
{
auto itt = mMetas.find(id);
if(itt != mMetas.end())
return itt->second ;
else
return NULL;
}
MetaDataClass *getOrCreateMeta(const ID& id)
{
MetaDataClass *meta = nullptr;
auto it = mMetas.find(id) ;
if(it != mMetas.end())
{
#ifdef RS_DATA_SERVICE_DEBUG
RsDbg() << __PRETTY_FUNCTION__ << ": getting group meta " << grpId << " from cache." << std::endl;
#endif
meta = it->second ;
}
else
{
#ifdef RS_DATA_SERVICE_DEBUG
RsDbg() << __PRETTY_FUNCTION__ << ": group meta " << grpId << " not in cache. Loading it from DB..." << std::endl;
#endif
meta = new MetaDataClass();
mMetas[id] = meta ;
}
return meta;
}
void updateMeta(const ID& id,const MetaDataClass& meta)
{
auto it = mMetas.find(id) ;
if(it != mMetas.end())
*(it->second) = meta ;
else
mMetas[id] = new MetaDataClass(meta) ;
}
void clear(const ID& id)
{
rstime_t now = time(NULL) ;
auto it = mMetas.find(id) ;
// We dont actually delete the item, because it might be used by a calling client.
// In this case, the memory will not be used for long, so we keep it into a list for a safe amount
// of time and delete it later. Using smart pointers here would be more elegant, but that would need
// to be implemented thread safe, which is difficult in this case.
if(it != mMetas.end())
{
#ifdef RS_DATA_SERVICE_DEBUG
std::cerr << "(II) moving database cache entry " << (void*)(*it).second << " to dead list." << std::endl;
#endif
mOldCachedItems.push_back(std::make_pair(now,it->second)) ;
mMetas.erase(it) ;
mCache_ContainsAllMetas = false;
}
// We also take that opportunity to delete old entries.
auto it2(mOldCachedItems.begin());
while(it2!=mOldCachedItems.end() && (*it2).first + CACHE_ENTRY_GRACE_PERIOD < now)
{
#ifdef RS_DATA_SERVICE_DEBUG
std::cerr << "(II) deleting old GXS database cache entry " << (void*)(*it2).second << ", " << now - (*it2).first << " seconds old." << std::endl;
#endif
delete (*it2).second ;
it2 = mOldCachedItems.erase(it2) ;
}
}
void debug_computeSize(uint32_t& nb_items, uint32_t& nb_items_on_deadlist, uint64_t& total_size,uint64_t& total_size_of_deadlist) const
{
nb_items = mMetas.size();
nb_items_on_deadlist = mOldCachedItems.size();
total_size = 0;
total_size_of_deadlist = 0;
for(auto it:mMetas) total_size += it.second->serial_size();
for(auto it:mOldCachedItems) total_size_of_deadlist += it.second->serial_size();
}
private:
std::map<ID,MetaDataClass*> mMetas;
std::list<std::pair<rstime_t,MetaDataClass*> > mOldCachedItems ; // dead list, where items get deleted after being unused for a while. This is due to not using smart ptrs.
static const uint32_t CACHE_ENTRY_GRACE_PERIOD = 600 ; // Unused items are deleted 10 minutes after last usage.
bool mCache_ContainsAllMetas ;
};
class RsDataService : public RsGeneralDataService
{
public:
@ -147,13 +257,13 @@ public:
* @param metaData The meta data item to update
* @return error code
*/
int updateMessageMetaData(MsgLocMetaData& metaData);
int updateMessageMetaData(const MsgLocMetaData& metaData);
/*!
* @param metaData The meta data item to update
* @return error code
*/
int updateGroupMetaData(GrpLocMetaData& meta);
int updateGroupMetaData(const GrpLocMetaData &meta);
/*!
* Completely clear out data stored in
@ -174,6 +284,8 @@ public:
int updateGroupKeys(const RsGxsGroupId& grpId,const RsTlvSecurityKeySet& keys, uint32_t subscribe_flags) ;
void debug_printCacheSize() const;
private:
/*!
@ -194,15 +306,22 @@ private:
/*!
* Retrieves all the msg meta results from a cursor
* @param c cursor to result set
* @param metaSet message metadata retrieved from cursor are stored here
* @param msgMeta message metadata retrieved from cursor are stored here
*/
void locked_retrieveMsgMeta(RetroCursor* c, std::vector<RsGxsMsgMetaData*>& msgMeta);
void locked_retrieveMsgMetaList(RetroCursor* c, std::vector<const RsGxsMsgMetaData*>& msgMeta);
/*!
* Retrieves all the grp meta results from a cursor
* @param c cursor to result set
* @param grpMeta group metadata retrieved from cursor are stored here
*/
void locked_retrieveGrpMetaList(RetroCursor *c, std::map<RsGxsGroupId,RsGxsGrpMetaData *>& grpMeta);
/*!
* extracts a msg meta item from a cursor at its
* current position
*/
RsGxsMsgMetaData* locked_getMsgMeta(RetroCursor& c, int colOffset);
RsGxsMsgMetaData* locked_getMsgMeta(RetroCursor& c, int colOffset, bool use_cache);
/*!
* extracts a grp meta item from a cursor at its
@ -348,10 +467,8 @@ private:
void locked_clearGrpMetaCache(const RsGxsGroupId& gid);
void locked_updateGrpMetaCache(const RsGxsGrpMetaData& meta);
std::map<RsGxsGroupId,RsGxsGrpMetaData*> mGrpMetaDataCache ;
std::list<std::pair<rstime_t,RsGxsGrpMetaData*> > mOldCachedItems ;
bool mGrpMetaDataCache_ContainsAllDatabase ;
t_MetaDataCache<RsGxsGroupId,RsGxsGrpMetaData> mGrpMetaDataCache;
std::map<RsGxsGroupId,t_MetaDataCache<RsGxsMessageId,RsGxsMsgMetaData> > mMsgMetaDataCache;
};
#endif // RSDATASERVICE_H

View File

@ -239,12 +239,12 @@ public:
/*!
* @param metaData
*/
virtual int updateMessageMetaData(MsgLocMetaData& metaData) = 0;
virtual int updateMessageMetaData(const MsgLocMetaData& metaData) = 0;
/*!
* @param metaData
*/
virtual int updateGroupMetaData(GrpLocMetaData& meta) = 0;
virtual int updateGroupMetaData(const GrpLocMetaData& meta) = 0;
virtual int updateGroupKeys(const RsGxsGroupId& grpId,const RsTlvSecurityKeySet& keys,uint32_t subscribed_flags) = 0 ;

View File

@ -512,7 +512,7 @@ int RsGenExchange::createGroupSignatures(RsTlvKeySignatureSet& signSet, RsTlvBin
if(GxsSecurity::getSignature((char*)grpData.bin_data, grpData.bin_len, authorKey, sign))
{
id_ret = SIGN_SUCCESS;
mGixs->timeStampKey(grpMeta.mAuthorId,RsIdentityUsage(mServType,RsIdentityUsage::GROUP_AUTHOR_SIGNATURE_CREATION,grpMeta.mGroupId)) ;
mGixs->timeStampKey(grpMeta.mAuthorId,RsIdentityUsage(RsServiceType(mServType),RsIdentityUsage::GROUP_AUTHOR_SIGNATURE_CREATION,grpMeta.mGroupId)) ;
signSet.keySignSet[INDEX_AUTHEN_IDENTITY] = sign;
}
else
@ -680,7 +680,7 @@ int RsGenExchange::createMsgSignatures(RsTlvKeySignatureSet& signSet, RsTlvBinar
if(GxsSecurity::getSignature((char*)msgData.bin_data, msgData.bin_len, authorKey, sign))
{
id_ret = SIGN_SUCCESS;
mGixs->timeStampKey(msgMeta.mAuthorId,RsIdentityUsage(mServType,RsIdentityUsage::MESSAGE_AUTHOR_SIGNATURE_CREATION,msgMeta.mGroupId,msgMeta.mMsgId)) ;
mGixs->timeStampKey(msgMeta.mAuthorId,RsIdentityUsage(RsServiceType(mServType),RsIdentityUsage::MESSAGE_AUTHOR_SIGNATURE_CREATION,msgMeta.mGroupId,msgMeta.mMsgId,msgMeta.mParentId,msgMeta.mThreadId)) ;
signSet.keySignSet[INDEX_AUTHEN_IDENTITY] = sign;
}
else
@ -905,7 +905,11 @@ int RsGenExchange::validateMsg(RsNxsMsg *msg, const uint32_t& grpFlag, const uin
{
RsTlvKeySignature sign = metaData.signSet.keySignSet[INDEX_AUTHEN_IDENTITY];
idValidate &= GxsSecurity::validateNxsMsg(*msg, sign, authorKey);
mGixs->timeStampKey(metaData.mAuthorId,RsIdentityUsage(mServType,RsIdentityUsage::MESSAGE_AUTHOR_SIGNATURE_VALIDATION,metaData.mGroupId,metaData.mMsgId)) ;
mGixs->timeStampKey(metaData.mAuthorId,RsIdentityUsage(RsServiceType(mServType),RsIdentityUsage::MESSAGE_AUTHOR_SIGNATURE_VALIDATION,
metaData.mGroupId,
metaData.mMsgId,
metaData.mParentId,
metaData.mThreadId)) ;
}
else
{
@ -949,7 +953,12 @@ int RsGenExchange::validateMsg(RsNxsMsg *msg, const uint32_t& grpFlag, const uin
{
std::list<RsPeerId> peers;
peers.push_back(msg->PeerId());
mGixs->requestKey(metaData.mAuthorId, peers, RsIdentityUsage(serviceType(),RsIdentityUsage::MESSAGE_AUTHOR_SIGNATURE_VALIDATION,metaData.mGroupId,metaData.mMsgId));
mGixs->requestKey(metaData.mAuthorId, peers, RsIdentityUsage((RsServiceType)serviceType(),
RsIdentityUsage::MESSAGE_AUTHOR_SIGNATURE_VALIDATION,
metaData.mGroupId,
metaData.mMsgId,
metaData.mParentId,
metaData.mThreadId));
#ifdef GEN_EXCH_DEBUG
std::cerr << ", Key missing. Retry later." << std::endl;
@ -1026,7 +1035,7 @@ int RsGenExchange::validateGrp(RsNxsGrp* grp)
#ifdef GEN_EXCH_DEBUG
std::cerr << " key ID validation result: " << idValidate << std::endl;
#endif
mGixs->timeStampKey(metaData.mAuthorId,RsIdentityUsage(mServType,RsIdentityUsage::GROUP_AUTHOR_SIGNATURE_VALIDATION,metaData.mGroupId));
mGixs->timeStampKey(metaData.mAuthorId,RsIdentityUsage(RsServiceType(mServType),RsIdentityUsage::GROUP_AUTHOR_SIGNATURE_VALIDATION,metaData.mGroupId));
}
else
{
@ -1044,7 +1053,7 @@ int RsGenExchange::validateGrp(RsNxsGrp* grp)
#endif
std::list<RsPeerId> peers;
peers.push_back(grp->PeerId());
mGixs->requestKey(metaData.mAuthorId, peers,RsIdentityUsage(mServType,RsIdentityUsage::GROUP_AUTHOR_SIGNATURE_VALIDATION,metaData.mGroupId));
mGixs->requestKey(metaData.mAuthorId, peers,RsIdentityUsage(RsServiceType(mServType),RsIdentityUsage::GROUP_AUTHOR_SIGNATURE_VALIDATION,metaData.mGroupId));
return VALIDATE_FAIL_TRY_LATER;
}
}
@ -1274,18 +1283,18 @@ bool RsGenExchange::getMsgMeta(const uint32_t &token,
for(; mit != result.end(); ++mit)
{
std::vector<RsGxsMsgMetaData*>& metaV = mit->second;
std::vector<const RsGxsMsgMetaData*>& metaV = mit->second;
std::vector<RsMsgMetaData>& msgInfoV = msgInfo[mit->first];
std::vector<RsGxsMsgMetaData*>::iterator vit = metaV.begin();
std::vector<const RsGxsMsgMetaData*>::iterator vit = metaV.begin();
RsMsgMetaData meta;
for(; vit != metaV.end(); ++vit)
{
RsGxsMsgMetaData& m = *(*vit);
const RsGxsMsgMetaData& m = *(*vit);
meta = m;
msgInfoV.push_back(meta);
delete *vit;
//delete *vit;
}
metaV.clear();
}
@ -1302,18 +1311,18 @@ bool RsGenExchange::getMsgRelatedMeta(const uint32_t &token, GxsMsgRelatedMetaMa
for(; mit != result.end(); ++mit)
{
std::vector<RsGxsMsgMetaData*>& metaV = mit->second;
std::vector<const RsGxsMsgMetaData*>& metaV = mit->second;
std::vector<RsMsgMetaData>& msgInfoV = msgMeta[mit->first];
std::vector<RsGxsMsgMetaData*>::iterator vit = metaV.begin();
std::vector<const RsGxsMsgMetaData*>::iterator vit = metaV.begin();
RsMsgMetaData meta;
for(; vit != metaV.end(); ++vit)
{
RsGxsMsgMetaData& m = *(*vit);
const RsGxsMsgMetaData& m = *(*vit);
meta = m;
msgInfoV.push_back(meta);
delete *vit;
//delete *vit;
}
metaV.clear();
}
@ -1675,13 +1684,7 @@ void RsGenExchange::receiveNewMessages(std::vector<RsNxsMsg *>& messages)
void RsGenExchange::receiveDistantSearchResults(TurtleRequestId id,const RsGxsGroupId &grpId)
{
std::cerr << __PRETTY_FUNCTION__ << " received result for request "
<< std::hex << id << std::dec << std::endl;
RS_STACK_MUTEX(mGenMtx);
RsGxsDistantSearchResultChange* gc = new RsGxsDistantSearchResultChange(id,grpId);
mNotifications.push_back(gc);
std::cerr << __PRETTY_FUNCTION__ << " received result for request " << std::hex << id << std::dec << ": this method should be overloaded in the client service, but it is not. This is a bug!" << std::endl;
}
void RsGenExchange::notifyReceivePublishKey(const RsGxsGroupId &grpId)
@ -2016,15 +2019,15 @@ void RsGenExchange::processMsgMetaChanges()
if(mit != result.end())
{
std::vector<RsGxsMsgMetaData*>& msgMetaV = mit->second;
std::vector<const RsGxsMsgMetaData*>& msgMetaV = mit->second;
if(!msgMetaV.empty())
{
RsGxsMsgMetaData* meta = *(msgMetaV.begin());
const RsGxsMsgMetaData* meta = *(msgMetaV.begin());
value = (meta->mMsgStatus & ~mask) | (mask & value);
changed = (static_cast<int64_t>(meta->mMsgStatus) != value);
m.val.put(RsGeneralDataService::MSG_META_STATUS, value);
delete meta;
//delete meta;
ok = true;
}
}
@ -3352,7 +3355,7 @@ bool RsGenExchange::updateValid(const RsGxsGrpMetaData& oldGrpMeta, const RsNxsG
// also check this is the latest published group
bool latest = newGrp.metaData->mPublishTs > oldGrpMeta.mPublishTs;
mGixs->timeStampKey(newGrp.metaData->mAuthorId, RsIdentityUsage(mServType,RsIdentityUsage::GROUP_ADMIN_SIGNATURE_CREATION, oldGrpMeta.mGroupId)) ;
mGixs->timeStampKey(newGrp.metaData->mAuthorId, RsIdentityUsage(RsServiceType(mServType),RsIdentityUsage::GROUP_ADMIN_SIGNATURE_CREATION, oldGrpMeta.mGroupId)) ;
return GxsSecurity::validateNxsGrp(newGrp, adminSign, keyMit->second) && latest;
}

View File

@ -29,8 +29,8 @@
/* data types used throughout Gxs from netservice to genexchange */
typedef std::map<RsGxsGroupId, std::vector<RsGxsMsgMetaData*> > GxsMsgMetaResult;
typedef std::map<RsGxsGrpMsgIdPair, std::vector<RsGxsMsgMetaData*> > MsgRelatedMetaResult;
typedef std::map<RsGxsGroupId, std::vector<const RsGxsMsgMetaData*> > GxsMsgMetaResult;
typedef std::map<RsGxsGrpMsgIdPair, std::vector<const RsGxsMsgMetaData*> > MsgRelatedMetaResult;
// Default values that are used throughout GXS code

View File

@ -209,7 +209,7 @@ RsGxsMsgMetaData::~RsGxsMsgMetaData(){
return;
}
uint32_t RsGxsMsgMetaData::serial_size()
uint32_t RsGxsMsgMetaData::serial_size() const
{
uint32_t s = 8; // header size

View File

@ -48,6 +48,7 @@ public:
bool deserialise(void *data, uint32_t &pktsize);
bool serialise(void* data, uint32_t &pktsize, uint32_t api_version);
uint32_t serial_size(uint32_t api_version) const;
uint32_t serial_size() const { return serial_size(RS_GXS_GRP_META_DATA_CURRENT_API_VERSION); }
void clear();
void operator =(const RsGroupMetaData& rMeta);
@ -94,7 +95,7 @@ public:
~RsGxsMsgMetaData();
bool deserialise(void *data, uint32_t *size);
bool serialise(void* data, uint32_t *size);
uint32_t serial_size();
uint32_t serial_size() const;
void clear();
void operator =(const RsMsgMetaData& rMeta);

View File

@ -1028,7 +1028,7 @@ bool RsGxsDataAccess::getMsgMetaDataList( const GxsMsgReq& msgIds, const RsTokRe
//auto& filter( metaFilter[grpId] ); // does the initialization of metaFilter[grpId] and avoids further O(log(n)) calls
std::vector<RsGxsMsgMetaData*>& metaV = meta_it->second;
std::vector<const RsGxsMsgMetaData*>& metaV = meta_it->second;
if (onlyLatestMsgs) // if we only consider latest messages, we need to first filter out messages with "children"
{
@ -1062,7 +1062,7 @@ bool RsGxsDataAccess::getMsgMetaDataList( const GxsMsgReq& msgIds, const RsTokRe
for(uint32_t i=0;i<metaV.size();++i)
if(!keep[i])
{
delete metaV[i];
//delete metaV[i];
metaV[i] = nullptr;
}
}
@ -1122,20 +1122,20 @@ bool RsGxsDataAccess::getMsgMetaDataList( const GxsMsgReq& msgIds, const RsTokRe
for(uint32_t i=0;i<metaV.size();++i)
if(metaV[i] != nullptr)
{
RsGxsMsgMetaData* msgMeta = metaV[i];
const RsGxsMsgMetaData* msgMeta = metaV[i];
bool add = false;
/* if we are grabbing thread Head... then parentId == empty. */
if (onlyThreadHeadMsgs && !msgMeta->mParentId.isNull())
{
delete msgMeta;
//delete msgMeta;
metaV[i] = nullptr;
continue;
}
if (onlyOrigMsgs && !msgMeta->mOrigMsgId.isNull() && msgMeta->mMsgId != msgMeta->mOrigMsgId)
{
delete msgMeta;
//delete msgMeta;
metaV[i] = nullptr;
continue;
}
@ -1187,7 +1187,7 @@ bool RsGxsDataAccess::getMsgIdList( const GxsMsgReq& msgIds, const RsTokReqOptio
}
// delete meta data
cleanseMsgMetaMap(result);
//cleanseMsgMetaMap(result);
return true;
}
@ -1296,9 +1296,7 @@ bool RsGxsDataAccess::getMsgRelatedInfo(MsgRelatedInfoReq *req)
return true;
}
std::vector<RsGxsGrpMsgIdPair>::iterator vit_msgIds = req->mMsgIds.begin();
for(; vit_msgIds != req->mMsgIds.end(); ++vit_msgIds)
for(auto vit_msgIds(req->mMsgIds.begin()); vit_msgIds != req->mMsgIds.end(); ++vit_msgIds)
{
MsgMetaFilter filterMap;
@ -1310,8 +1308,8 @@ bool RsGxsDataAccess::getMsgRelatedInfo(MsgRelatedInfoReq *req)
GxsMsgReq msgIds;
msgIds.insert(std::make_pair(grpMsgIdPair.first, std::set<RsGxsMessageId>()));
mDataStore->retrieveGxsMsgMetaData(msgIds, result);
std::vector<RsGxsMsgMetaData*>& metaV = result[grpMsgIdPair.first];
std::vector<RsGxsMsgMetaData*>::iterator vit_meta;
std::vector<const RsGxsMsgMetaData*>& metaV = result[grpMsgIdPair.first];
std::vector<const RsGxsMsgMetaData*>::iterator vit_meta;
// msg id to relate to
const RsGxsMessageId& msgId = grpMsgIdPair.second;
@ -1319,10 +1317,11 @@ bool RsGxsDataAccess::getMsgRelatedInfo(MsgRelatedInfoReq *req)
std::set<RsGxsMessageId> outMsgIds;
RsGxsMsgMetaData* origMeta = nullptr;
const RsGxsMsgMetaData* origMeta = nullptr;
for(vit_meta = metaV.begin(); vit_meta != metaV.end(); ++vit_meta)
{
RsGxsMsgMetaData* meta = *vit_meta;
const RsGxsMsgMetaData* meta = *vit_meta;
if(msgId == meta->mMsgId)
{
@ -1337,12 +1336,11 @@ bool RsGxsDataAccess::getMsgRelatedInfo(MsgRelatedInfoReq *req)
RsDbg() << "RsGxsDataAccess::getMsgRelatedInfo(): Cannot find meta of msgId (to relate to)!"
<< std::endl;
#endif
cleanseMsgMetaMap(result);
return false;
}
const RsGxsMessageId& origMsgId = origMeta->mOrigMsgId;
std::map<RsGxsMessageId, RsGxsMsgMetaData*>& metaMap = filterMap[grpId];
std::map<RsGxsMessageId, const RsGxsMsgMetaData*>& metaMap = filterMap[grpId];
if (onlyLatestMsgs)
{
@ -1354,7 +1352,7 @@ bool RsGxsDataAccess::getMsgRelatedInfo(MsgRelatedInfoReq *req)
for(vit_meta = metaV.begin(); vit_meta != metaV.end(); ++vit_meta)
{
RsGxsMsgMetaData* meta = *vit_meta;
const RsGxsMsgMetaData* meta = *vit_meta;
// skip msgs that aren't children.
if (onlyChildMsgs)
@ -1422,11 +1420,11 @@ bool RsGxsDataAccess::getMsgRelatedInfo(MsgRelatedInfoReq *req)
/* first guess is potentially better than Orig (can't be worse!) */
rstime_t latestTs = 0;
RsGxsMessageId latestMsgId;
RsGxsMsgMetaData* latestMeta=nullptr;
const RsGxsMsgMetaData* latestMeta=nullptr;
for(vit_meta = metaV.begin(); vit_meta != metaV.end(); ++vit_meta)
{
RsGxsMsgMetaData* meta = *vit_meta;
const RsGxsMsgMetaData* meta = *vit_meta;
if (meta->mOrigMsgId == origMsgId)
{
@ -1446,7 +1444,7 @@ bool RsGxsDataAccess::getMsgRelatedInfo(MsgRelatedInfoReq *req)
{
for(vit_meta = metaV.begin(); vit_meta != metaV.end(); ++vit_meta)
{
RsGxsMsgMetaData* meta = *vit_meta;
const RsGxsMsgMetaData* meta = *vit_meta;
if (meta->mOrigMsgId == origMsgId)
{
@ -1482,8 +1480,6 @@ bool RsGxsDataAccess::getMsgRelatedInfo(MsgRelatedInfoReq *req)
outMsgIds.clear();
filteredOutMsgIds.clear();
cleanseMsgMetaMap(result);
}
return true;
}
@ -1496,7 +1492,7 @@ bool RsGxsDataAccess::getGroupStatistic(GroupStatisticRequest *req)
GxsMsgMetaResult metaResult;
mDataStore->retrieveGxsMsgMetaData(metaReq, metaResult);
const std::vector<RsGxsMsgMetaData*>& msgMetaV = metaResult[req->mGrpId];
const std::vector<const RsGxsMsgMetaData*>& msgMetaV = metaResult[req->mGrpId];
req->mGroupStatistic.mGrpId = req->mGrpId;
req->mGroupStatistic.mNumMsgs = msgMetaV.size();
@ -1514,7 +1510,7 @@ bool RsGxsDataAccess::getGroupStatistic(GroupStatisticRequest *req)
for(uint32_t i = 0; i < msgMetaV.size(); ++i)
{
RsGxsMsgMetaData* m = msgMetaV[i];
const RsGxsMsgMetaData* m = msgMetaV[i];
req->mGroupStatistic.mTotalSizeOfMsgs += m->mMsgSize + m->serial_size();
if(obsolete_msgs.find(m->mMsgId) != obsolete_msgs.end()) // skip obsolete messages.
@ -1540,7 +1536,7 @@ bool RsGxsDataAccess::getGroupStatistic(GroupStatisticRequest *req)
}
}
cleanseMsgMetaMap(metaResult);
//cleanseMsgMetaMap(metaResult);
return true;
}
@ -1595,21 +1591,19 @@ bool RsGxsDataAccess::getMsgIdList(MsgIdReq* req)
mDataStore->retrieveGxsMsgMetaData(req->mMsgIds, result);
GxsMsgMetaResult::iterator mit = result.begin(), mit_end = result.end();
for(; mit != mit_end; ++mit)
{
const RsGxsGroupId grpId = mit->first;
std::vector<RsGxsMsgMetaData*>& metaV = mit->second;
std::vector<RsGxsMsgMetaData*>::iterator vit = metaV.begin(),
std::vector<const RsGxsMsgMetaData*>& metaV = mit->second;
std::vector<const RsGxsMsgMetaData*>::iterator vit = metaV.begin(),
vit_end = metaV.end();
for(; vit != vit_end; ++vit)
{
RsGxsMsgMetaData* meta = *vit;
const RsGxsMsgMetaData* meta = *vit;
req->mMsgIdResult[grpId].insert(meta->mMsgId);
delete meta; // discard meta data mem
}
}
@ -1622,24 +1616,24 @@ bool RsGxsDataAccess::getMsgIdList(MsgIdReq* req)
return true;
}
void RsGxsDataAccess::cleanseMsgMetaMap(GxsMsgMetaResult& result)
{
GxsMsgMetaResult::iterator mit = result.begin();
for(; mit !=result.end(); ++mit)
{
std::vector<RsGxsMsgMetaData*>& msgMetaV = mit->second;
std::vector<RsGxsMsgMetaData*>::iterator vit = msgMetaV.begin();
for(; vit != msgMetaV.end(); ++vit)
{
delete *vit;
}
}
result.clear();
return;
}
// void RsGxsDataAccess::cleanseMsgMetaMap(GxsMsgMetaResult& result)
// {
// GxsMsgMetaResult::iterator mit = result.begin();
//
// for(; mit !=result.end(); ++mit)
// {
//
// std::vector<RsGxsMsgMetaData*>& msgMetaV = mit->second;
// std::vector<RsGxsMsgMetaData*>::iterator vit = msgMetaV.begin();
// for(; vit != msgMetaV.end(); ++vit)
// {
// delete *vit;
// }
// }
//
// result.clear();
// return;
// }
void RsGxsDataAccess::filterMsgIdList( GxsMsgIdResult& resultsMap, const RsTokReqOptions& opts, const MsgMetaFilter& msgMetas ) const
{
@ -1659,11 +1653,11 @@ void RsGxsDataAccess::filterMsgIdList( GxsMsgIdResult& resultsMap, const RsTokRe
for( std::set<RsGxsMessageId>::iterator msgIdIt = msgsIdSet.begin(); msgIdIt != msgsIdSet.end(); )
{
const RsGxsMessageId& msgId(*msgIdIt);
const std::map<RsGxsMessageId, RsGxsMsgMetaData*>& msgsMetaMap =
const std::map<RsGxsMessageId, const RsGxsMsgMetaData*>& msgsMetaMap =
cit->second;
bool keep = false;
std::map<RsGxsMessageId, RsGxsMsgMetaData*>::const_iterator msgsMetaMapIt;
std::map<RsGxsMessageId, const RsGxsMsgMetaData*>::const_iterator msgsMetaMapIt;
if( (msgsMetaMapIt = msgsMetaMap.find(msgId)) != msgsMetaMap.end() )
{

View File

@ -28,7 +28,7 @@
#include "rsgds.h"
typedef std::map< RsGxsGroupId, std::map<RsGxsMessageId, RsGxsMsgMetaData*> > MsgMetaFilter;
typedef std::map< RsGxsGroupId, std::map<RsGxsMessageId, const RsGxsMsgMetaData*> > MsgMetaFilter;
typedef std::map< RsGxsGroupId, RsGxsGrpMetaData* > GrpMetaFilter;
bool operator<(const std::pair<uint32_t,GxsRequest*>& p1,const std::pair<uint32_t,GxsRequest*>& p2);
@ -328,11 +328,11 @@ private:
*/
void tokenList(std::list<uint32_t> &tokens);
/*!
* Convenience function to delete the ids
* @param filter the meta filter to clean
*/
void cleanseMsgMetaMap(GxsMsgMetaResult& result);
// /*!
// * Convenience function to delete the ids
// * @param filter the meta filter to clean
// */
// void cleanseMsgMetaMap(GxsMsgMetaResult& result);
public:

View File

@ -272,6 +272,7 @@
NXS_NET_DEBUG_6 group sync statistics (e.g. number of posts at nighbour nodes, etc)
NXS_NET_DEBUG_7 encryption/decryption of transactions
NXS_NET_DEBUG_8 gxs distant sync
NXS_NET_DEBUG_9 gxs distant search
***/
//#define NXS_NET_DEBUG_0 1
@ -283,6 +284,7 @@
//#define NXS_NET_DEBUG_6 1
//#define NXS_NET_DEBUG_7 1
//#define NXS_NET_DEBUG_8 1
//#define NXS_NET_DEBUG_9 1
//#define NXS_FRAG
@ -306,6 +308,7 @@ static const uint32_t GROUP_STATS_UPDATE_DELAY = 240; //
static const uint32_t GROUP_STATS_UPDATE_NB_PEERS = 2; // number of peers to which the group stats are asked
static const uint32_t MAX_ALLOWED_GXS_MESSAGE_SIZE = 199000; // 200,000 bytes including signature and headers
static const uint32_t MIN_DELAY_BETWEEN_GROUP_SEARCH = 40; // dont search same group more than every 40 secs.
static const uint32_t SAFETY_DELAY_FOR_UNSUCCESSFUL_UPDATE = 1800; // avoid re-sending the same msg list to a peer who asks twice for the same update in less than this time
static const uint32_t RS_NXS_ITEM_ENCRYPTION_STATUS_UNKNOWN = 0x00 ;
static const uint32_t RS_NXS_ITEM_ENCRYPTION_STATUS_NO_ERROR = 0x01 ;
@ -318,11 +321,11 @@ static const uint32_t RS_NXS_ITEM_ENCRYPTION_STATUS_GXS_KEY_MISSING = 0x05 ;
#if defined(NXS_NET_DEBUG_0) || defined(NXS_NET_DEBUG_1) || defined(NXS_NET_DEBUG_2) || defined(NXS_NET_DEBUG_3) \
|| defined(NXS_NET_DEBUG_4) || defined(NXS_NET_DEBUG_5) || defined(NXS_NET_DEBUG_6) || defined(NXS_NET_DEBUG_7) \
|| defined(NXS_NET_DEBUG_8)
|| defined(NXS_NET_DEBUG_8) || defined(NXS_NET_DEBUG_9)
static const RsPeerId peer_to_print = RsPeerId();//std::string("a97fef0e2dc82ddb19200fb30f9ac575")) ;
static const RsGxsGroupId group_id_to_print = RsGxsGroupId(std::string("66052380f5d1d0c5992e2b55dc402ce6")) ; // use this to allow to this group id only, or "" for all IDs
static const uint32_t service_to_print = RS_SERVICE_GXS_TYPE_GXSCIRCLE; // use this to allow to this service id only, or 0 for all services
static const RsGxsGroupId group_id_to_print = RsGxsGroupId();//std::string("66052380f5d1d0c5992e2b55dc402ce6")) ; // use this to allow to this group id only, or "" for all IDs
static const uint32_t service_to_print = RS_SERVICE_GXS_TYPE_GXSID; // use this to allow to this service id only, or 0 for all services
// warning. Numbers should be SERVICE IDS (see serialiser/rsserviceids.h. E.g. 0x0215 for forums)
class nullstream: public std::ostream {};
@ -590,32 +593,30 @@ void RsGxsNetService::syncWithPeers()
return;
}
std::set<RsPeerId>::iterator sit = peers.begin();
// for now just grps
for(auto sit = peers.begin(); sit != peers.end(); ++sit)
{
// for now just grps
for(; sit != peers.end(); ++sit)
{
const RsPeerId peerId = *sit;
const RsPeerId peerId = *sit;
ClientGrpMap::const_iterator cit = mClientGrpUpdateMap.find(peerId);
uint32_t updateTS = 0;
ClientGrpMap::const_iterator cit = mClientGrpUpdateMap.find(peerId);
uint32_t updateTS = 0;
if(cit != mClientGrpUpdateMap.end())
{
const RsGxsGrpUpdate *gui = &cit->second;
updateTS = gui->grpUpdateTS;
}
RsNxsSyncGrpReqItem *grp = new RsNxsSyncGrpReqItem(mServType);
grp->clear();
grp->PeerId(*sit);
grp->updateTS = updateTS;
if(cit != mClientGrpUpdateMap.end())
{
const RsGxsGrpUpdate *gui = &cit->second;
updateTS = gui->grpUpdateTS;
}
RsNxsSyncGrpReqItem *grp = new RsNxsSyncGrpReqItem(mServType);
grp->clear();
grp->PeerId(*sit);
grp->updateTS = updateTS;
#ifdef NXS_NET_DEBUG_5
GXSNETDEBUG_P_(*sit) << "Service "<< std::hex << ((mServiceInfo.mServiceType >> 8)& 0xffff) << std::dec << " sending global group TS of peer id: " << *sit << " ts=" << nice_time_stamp(time(NULL),updateTS) << " (secs ago) to himself" << std::endl;
GXSNETDEBUG_P_(*sit) << "Service "<< std::hex << ((mServiceInfo.mServiceType >> 8)& 0xffff) << std::dec << " sending global group TS of peer id: " << *sit << " ts=" << nice_time_stamp(time(NULL),updateTS) << " (secs ago) to himself" << std::endl;
#endif
generic_sendItem(grp);
}
generic_sendItem(grp);
}
if(!mAllowMsgSync)
return ;
@ -641,15 +642,13 @@ void RsGxsNetService::syncWithPeers()
}
}
sit = peers.begin();
// Synchronise group msg for groups which we're subscribed to
// For each peer and each group, we send to the peer the time stamp of the most
// recent modification the peer has sent. If the peer has more recent messages he will send them, because its latest
// modifications will be more recent. This ensures that we always compare timestamps all taken in the same
// computer (the peer's computer in this case)
for(; sit != peers.end(); ++sit)
for(auto sit = peers.begin(); sit != peers.end(); ++sit)
{
const RsPeerId& peerId = *sit;
@ -953,7 +952,7 @@ void RsGxsNetService::handleRecvSyncGrpStatistics(RsNxsSyncGrpStatsItem *grs)
#endif
mDataStore->retrieveGxsMsgMetaData(reqIds, result);
const std::vector<RsGxsMsgMetaData*>& vec(result[grs->grpId]) ;
const std::vector<const RsGxsMsgMetaData*>& vec(result[grs->grpId]) ;
if(vec.empty()) // that means we don't have any, or there isn't any, but since the default is always 0, no need to send.
return ;
@ -970,12 +969,9 @@ void RsGxsNetService::handleRecvSyncGrpStatistics(RsNxsSyncGrpStatsItem *grs)
// be used to discard groups that are not used.
for(uint32_t i=0;i<vec.size();++i)
{
if(grs_resp->last_post_TS < vec[i]->mPublishTs)
grs_resp->last_post_TS = vec[i]->mPublishTs;
delete vec[i] ;
}
#ifdef NXS_NET_DEBUG_6
GXSNETDEBUG_PG(grs->PeerId(),grs->grpId) << " sending back statistics item with " << vec.size() << " elements." << std::endl;
#endif
@ -2953,21 +2949,19 @@ void RsGxsNetService::locked_genReqMsgTransaction(NxsTransaction* tr)
reqIds[grpId] = std::set<RsGxsMessageId>();
GxsMsgMetaResult result;
mDataStore->retrieveGxsMsgMetaData(reqIds, result);
std::vector<RsGxsMsgMetaData*> &msgMetaV = result[grpId];
std::vector<const RsGxsMsgMetaData*> &msgMetaV = result[grpId];
#ifdef NXS_NET_DEBUG_1
GXSNETDEBUG_PG(item->PeerId(),grpId) << " retrieving grp message list..." << std::endl;
GXSNETDEBUG_PG(item->PeerId(),grpId) << " grp locally contains " << msgMetaV.size() << " messsages." << std::endl;
#endif
std::vector<RsGxsMsgMetaData*>::const_iterator vit = msgMetaV.begin();
std::vector<const RsGxsMsgMetaData*>::const_iterator vit = msgMetaV.begin();
std::set<RsGxsMessageId> msgIdSet;
// put ids in set for each searching
for(; vit != msgMetaV.end(); ++vit)
{
msgIdSet.insert((*vit)->mMsgId);
delete(*vit);
}
msgMetaV.clear();
#ifdef NXS_NET_DEBUG_1
@ -3185,7 +3179,8 @@ void RsGxsNetService::locked_genReqGrpTransaction(NxsTransaction* tr)
{
grpItemL.push_back(item);
grpMetaMap[item->grpId] = NULL;
}else
}
else
{
#ifdef NXS_NET_DEBUG_0
GXSNETDEBUG_PG(tr->mTransaction->PeerId(),item->grpId) << "RsGxsNetService::genReqGrpTransaction(): item failed to caste to RsNxsSyncMsgItem* " << std::endl;
@ -3228,7 +3223,7 @@ void RsGxsNetService::locked_genReqGrpTransaction(NxsTransaction* tr)
RsNxsSyncGrpItem*& grpSyncItem = *llit;
const RsGxsGroupId& grpId = grpSyncItem->grpId;
std::map<RsGxsGroupId, RsGxsGrpMetaData*>::const_iterator metaIter = grpMetaMap.find(grpId);
std::map<RsGxsGroupId, RsGxsGrpMetaData*>::const_iterator metaIter = grpMetaMap.find(grpId);
bool haveItem = false;
bool latestVersion = false;
@ -3239,19 +3234,21 @@ void RsGxsNetService::locked_genReqGrpTransaction(NxsTransaction* tr)
}
// FIXTESTS global variable rsReputations not available in unittests!
#warning csoler 2016-12-23: Update the code below to correctly send/recv dependign on reputation
if( !grpSyncItem->authorId.isNull() &&
mReputations->overallReputationLevel(grpSyncItem->authorId) ==
RsReputationLevel::LOCALLY_NEGATIVE )
if( mReputations->overallReputationLevel(RsGxsId(grpSyncItem->grpId)) == RsReputationLevel::LOCALLY_NEGATIVE )
{
#ifdef NXS_NET_DEBUG_0
GXSNETDEBUG_PG(tr->mTransaction->PeerId(),grpId) << " Identity " << grpSyncItem->authorId << " is banned. Not syncing group." << std::endl;
GXSNETDEBUG_PG(tr->mTransaction->PeerId(),grpId) << " Identity " << grpSyncItem->grpId << " is banned. Not GXS-syncing group." << std::endl;
#endif
continue ;
}
if( (mGrpAutoSync && !haveItem) || latestVersion)
{
#ifdef NXS_NET_DEBUG_0
GXSNETDEBUG_PG(tr->mTransaction->PeerId(),grpId) << " Identity " << grpId << " will be sync-ed using GXS. mGrpAutoSync:" << mGrpAutoSync << " haveItem:" << haveItem << " latest_version: " << latestVersion << std::endl;
#endif
addGroupItemToList(tr, grpId, transN, reqList);
}
}
if(!reqList.empty())
@ -3998,7 +3995,7 @@ bool RsGxsNetService::locked_CanReceiveUpdate(const RsNxsSyncGrpReqItem *item)
GXSNETDEBUG_P_(item->PeerId()) << " local modification time stamp: " << std::dec<< time(NULL) - mGrpServerUpdate.grpUpdateTS << " secs ago. Update sent: " <<
((item->updateTS < mGrpServerUpdate.grpUpdateTS)?"YES":"NO") << std::endl;
#endif
return item->updateTS < mGrpServerUpdate.grpUpdateTS;
return item->updateTS < mGrpServerUpdate.grpUpdateTS && locked_checkResendingOfUpdates(item->PeerId(),RsGxsGroupId(),item->updateTS,mGrpServerUpdate.grpUpdateTsRecords[item->PeerId()]) ;
}
void RsGxsNetService::handleRecvSyncGroup(RsNxsSyncGrpReqItem *item)
@ -4243,10 +4240,33 @@ bool RsGxsNetService::checkCanRecvMsgFromPeer(const RsPeerId& sslId, const RsGxs
return true;
}
bool RsGxsNetService::locked_checkResendingOfUpdates(const RsPeerId& pid,const RsGxsGroupId& grpId,rstime_t incoming_ts,RsPeerUpdateTsRecord& rec)
{
rstime_t now = time(NULL);
// Now we check if the peer is sending the same outdated TS for the same time in a short while. This would mean the peer
// hasn't finished processing the updates we're sending and we shouldn't send new data anymore. Of course the peer might
// have disconnected or so, which means that we need to be careful about not sending. As a compromise we still send, but
// after waiting for a while (See
if(rec.mLastTsReceived == incoming_ts && rec.mTs + SAFETY_DELAY_FOR_UNSUCCESSFUL_UPDATE > now)
{
#ifdef NXS_NET_DEBUG_0
GXSNETDEBUG_PG(pid,grpId) << "(II) peer " << pid << " already sent the same TS " << (long int)now-(long int)rec.mTs << " secs ago for that group ID. Will not send msg list again for a while to prevent clogging..." << std::endl;
#endif
return false;
}
rec.mLastTsReceived = incoming_ts;
rec.mTs = now;
return true;
}
bool RsGxsNetService::locked_CanReceiveUpdate(RsNxsSyncMsgReqItem *item,bool& grp_is_known)
{
// Do we have new updates for this peer?
// Here we compare times in the same clock: the friend's clock, so it should be fine.
// Here we compare times in the same clock: our own clock, so it should be fine.
grp_is_known = false ;
@ -4255,7 +4275,7 @@ bool RsGxsNetService::locked_CanReceiveUpdate(RsNxsSyncMsgReqItem *item,bool& gr
// Item contains the hashed group ID in order to protect is from friends who don't know it. So we de-hash it using bruteforce over known group IDs for this peer.
// We could save the de-hash result. But the cost is quite light, since the number of encrypted groups per service is usually low.
for(ServerMsgMap::const_iterator it(mServerMsgUpdateMap.begin());it!=mServerMsgUpdateMap.end();++it)
for(ServerMsgMap::iterator it(mServerMsgUpdateMap.begin());it!=mServerMsgUpdateMap.end();++it)
if(item->grpId == hashGrpId(it->first,item->PeerId()))
{
item->grpId = it->first ;
@ -4265,20 +4285,25 @@ bool RsGxsNetService::locked_CanReceiveUpdate(RsNxsSyncMsgReqItem *item,bool& gr
#endif
grp_is_known = true ;
return item->updateTS < it->second.msgUpdateTS ;
// The order of tests below is important because we want to only modify the map of requests records if the request actually is a valid requests instead of
// a simple check that nothing's changed.
return item->updateTS < it->second.msgUpdateTS && locked_checkResendingOfUpdates(item->PeerId(),item->grpId,item->updateTS,it->second.msgUpdateTsRecords[item->PeerId()]) ;
}
return false ;
}
ServerMsgMap::const_iterator cit = mServerMsgUpdateMap.find(item->grpId);
ServerMsgMap::iterator cit = mServerMsgUpdateMap.find(item->grpId);
if(cit != mServerMsgUpdateMap.end())
{
#ifdef NXS_NET_DEBUG_0
GXSNETDEBUG_PG(item->PeerId(),item->grpId) << " local time stamp: " << std::dec<< time(NULL) - cit->second.msgUpdateTS << " secs ago. Update sent: " << (item->updateTS < cit->second.msgUpdateTS) << std::endl;
#endif
grp_is_known = true ;
return item->updateTS < cit->second.msgUpdateTS ;
return item->updateTS < cit->second.msgUpdateTS && locked_checkResendingOfUpdates(item->PeerId(),item->grpId,item->updateTS,cit->second.msgUpdateTsRecords[item->PeerId()]) ;
}
#ifdef NXS_NET_DEBUG_0
@ -4299,6 +4324,9 @@ void RsGxsNetService::handleRecvSyncMessage(RsNxsSyncMsgReqItem *item,bool item_
bool grp_is_known = false;
bool was_circle_protected = item_was_encrypted || bool(item->flag & RsNxsSyncMsgReqItem::FLAG_USE_HASHED_GROUP_ID);
// This call determines if the peer can receive updates from us, meaning that our last TS is larger than what the peer sent.
// It also changes the items' group id into the un-hashed group ID if the group is a distant group.
bool peer_can_receive_update = locked_CanReceiveUpdate(item, grp_is_known);
if(item_was_encrypted)
@ -4314,7 +4342,7 @@ void RsGxsNetService::handleRecvSyncMessage(RsNxsSyncMsgReqItem *item,bool item_
// We update suppliers in two cases:
// Case 1: the grp is known because it is the hash of an existing group, but it's not yet in the server config map
// Case 2: the gtp is not known, possibly because it was deleted, but there's an entry in mServerGrpConfigMap due to statistics gathering. Still, statistics are only
// Case 2: the grp is not known, possibly because it was deleted, but there's an entry in mServerGrpConfigMap due to statistics gathering. Still, statistics are only
// gathered from known suppliers. So statistics never add new suppliers. These are only added here.
if(grp_is_known || mServerGrpConfigMap.find(item->grpId)!=mServerGrpConfigMap.end())
@ -4367,7 +4395,7 @@ void RsGxsNetService::handleRecvSyncMessage(RsNxsSyncMsgReqItem *item,bool item_
GxsMsgMetaResult metaResult;
mDataStore->retrieveGxsMsgMetaData(req, metaResult);
std::vector<RsGxsMsgMetaData*>& msgMetas = metaResult[item->grpId];
std::vector<const RsGxsMsgMetaData*>& msgMetas = metaResult[item->grpId];
#ifdef NXS_NET_DEBUG_0
GXSNETDEBUG_PG(item->PeerId(),item->grpId) << " retrieving message meta data." << std::endl;
@ -4395,9 +4423,9 @@ void RsGxsNetService::handleRecvSyncMessage(RsNxsSyncMsgReqItem *item,bool item_
if(canSendMsgIds(msgMetas, *grpMeta, peer, should_encrypt_to_this_circle_id))
{
for(std::vector<RsGxsMsgMetaData*>::iterator vit = msgMetas.begin();vit != msgMetas.end(); ++vit)
for(auto vit = msgMetas.begin();vit != msgMetas.end(); ++vit)
{
RsGxsMsgMetaData* m = *vit;
const RsGxsMsgMetaData* m = *vit;
// Check reputation
@ -4497,8 +4525,8 @@ void RsGxsNetService::handleRecvSyncMessage(RsNxsSyncMsgReqItem *item,bool item_
// release meta resource
for(std::vector<RsGxsMsgMetaData*>::iterator vit = msgMetas.begin(); vit != msgMetas.end(); ++vit)
delete *vit;
// for(std::vector<RsGxsMsgMetaData*>::iterator vit = msgMetas.begin(); vit != msgMetas.end(); ++vit)
// delete *vit;
}
void RsGxsNetService::locked_pushMsgRespFromList(std::list<RsNxsItem*>& itemL, const RsPeerId& sslId, const RsGxsGroupId& grp_id,const uint32_t& transN)
@ -4542,7 +4570,7 @@ void RsGxsNetService::locked_pushMsgRespFromList(std::list<RsNxsItem*>& itemL, c
}
}
bool RsGxsNetService::canSendMsgIds(std::vector<RsGxsMsgMetaData*>& msgMetas, const RsGxsGrpMetaData& grpMeta, const RsPeerId& sslId,RsGxsCircleId& should_encrypt_id)
bool RsGxsNetService::canSendMsgIds(std::vector<const RsGxsMsgMetaData*>& msgMetas, const RsGxsGrpMetaData& grpMeta, const RsPeerId& sslId,RsGxsCircleId& should_encrypt_id)
{
#ifdef NXS_NET_DEBUG_4
GXSNETDEBUG_PG(sslId,grpMeta.mGroupId) << "RsGxsNetService::canSendMsgIds() CIRCLE VETTING" << std::endl;
@ -4609,7 +4637,7 @@ bool RsGxsNetService::canSendMsgIds(std::vector<RsGxsMsgMetaData*>& msgMetas, co
GXSNETDEBUG_PG(sslId,grpMeta.mGroupId) << " deleting MsgMeta entry for msg ID " << msgMetas[i]->mMsgId << " signed by " << msgMetas[i]->mAuthorId << " who is not in group circle " << circleId << std::endl;
#endif
delete msgMetas[i] ;
//delete msgMetas[i] ;
msgMetas[i] = msgMetas[msgMetas.size()-1] ;
msgMetas.pop_back() ;
}
@ -5160,7 +5188,7 @@ static bool termSearch(const std::string& src, const std::string& substring)
}
#endif // ndef RS_DEEP_CHANNEL_INDEX
bool RsGxsNetService::retrieveDistantSearchResults(TurtleRequestId req,std::map<RsGxsGroupId,RsGxsGroupSummary>& group_infos)
bool RsGxsNetService::retrieveDistantSearchResults(TurtleRequestId req,std::map<RsGxsGroupId,RsGxsGroupSearchResults>& group_infos)
{
RS_STACK_MUTEX(mNxsMutex) ;
@ -5172,7 +5200,7 @@ bool RsGxsNetService::retrieveDistantSearchResults(TurtleRequestId req,std::map<
group_infos = it->second;
return true ;
}
bool RsGxsNetService::retrieveDistantGroupSummary(const RsGxsGroupId& group_id,RsGxsGroupSummary& gs)
bool RsGxsNetService::retrieveDistantGroupSummary(const RsGxsGroupId& group_id,RsGxsGroupSearchResults& gs)
{
RS_STACK_MUTEX(mNxsMutex) ;
for(auto it(mDistantSearchResults.begin());it!=mDistantSearchResults.end();++it)
@ -5194,8 +5222,7 @@ bool RsGxsNetService::clearDistantSearchResults(const TurtleRequestId& id)
return true ;
}
void RsGxsNetService::receiveTurtleSearchResults(
TurtleRequestId req, const std::list<RsGxsGroupSummary>& group_infos )
void RsGxsNetService::receiveTurtleSearchResults( TurtleRequestId req, const std::list<RsGxsGroupSummary>& group_infos )
{
std::set<RsGxsGroupId> groupsToNotifyResults;
@ -5203,20 +5230,43 @@ void RsGxsNetService::receiveTurtleSearchResults(
RS_STACK_MUTEX(mNxsMutex);
RsGxsGrpMetaTemporaryMap grpMeta;
std::map<RsGxsGroupId,RsGxsGroupSummary>&
search_results_map(mDistantSearchResults[req]);
std::map<RsGxsGroupId,RsGxsGroupSearchResults>& search_results_map(mDistantSearchResults[req]);
#ifdef NXS_NET_DEBUG_9
std::cerr << "Received group summary through turtle search for the following groups:" << std::endl;
#endif
for(const RsGxsGroupSummary& gps : group_infos)
if(search_results_map.find(gps.mGroupId) == search_results_map.end())
grpMeta[gps.mGroupId] = nullptr;
{
std::cerr <<" " << gps.mGroupId << " \"" << gps.mGroupName << "\"" << std::endl;
grpMeta[gps.mGroupId] = nullptr;
}
mDataStore->retrieveGxsGrpMetaData(grpMeta);
#ifdef NXS_NET_DEBUG_9
std::cerr << "Retrieved data store group data for the following groups:" <<std::endl;
for(auto& it:grpMeta)
std::cerr << " " << it.first << " : " << it.second->mGroupName << std::endl;
#endif
for (const RsGxsGroupSummary& gps : group_infos)
{
#ifndef RS_DEEP_CHANNEL_INDEX
/* Only keep groups that are not locally known, and groups that are
* not already in the mDistantSearchResults structure. */
if(grpMeta[gps.mGroupId]) continue;
* not already in the mDistantSearchResults structure.
* mDataStore may in some situations allocate an empty group meta data, so it's important
* to test that the group meta is both non null and actually corresponds to the group id we seek. */
auto& meta(grpMeta[gps.mGroupId]);
if(meta != nullptr && meta->mGroupId == gps.mGroupId)
continue;
#ifdef NXS_NET_DEBUG_9
std::cerr << " group " << gps.mGroupId << " is not known. Adding it to search results..." << std::endl;
#endif
#else // ndef RS_DEEP_CHANNEL_INDEX
/* When deep search is enabled search results may bring more info
* then we already have also about post that are indexed by xapian,
@ -5225,22 +5275,32 @@ void RsGxsNetService::receiveTurtleSearchResults(
const RsGxsGroupId& grpId(gps.mGroupId);
groupsToNotifyResults.insert(grpId);
auto it2 = search_results_map.find(grpId);
if(it2 != search_results_map.end())
{
// update existing data
RsGxsGroupSummary& eGpS(it2->second);
eGpS.mPopularity++;
eGpS.mNumberOfMessages = std::max(
eGpS.mNumberOfMessages,
gps.mNumberOfMessages );
}
else
{
search_results_map[grpId] = gps;
// number of results so far
search_results_map[grpId].mPopularity = 1;
}
// Find search results place for this particular group
#ifdef NXS_NET_DEBUG_9
std::cerr << " Adding gps=" << gps.mGroupId << " name=\"" << gps.mGroupName << "\" gps.mSearchContext=\"" << gps.mSearchContext << "\"" << std::endl;
#endif
RsGxsGroupSearchResults& eGpS(search_results_map[grpId]);
if(eGpS.mGroupId != grpId) // not initialized yet. So we do it now.
{
eGpS.mGroupId = gps.mGroupId;
eGpS.mGroupName = gps.mGroupName;
eGpS.mAuthorId = gps.mAuthorId;
eGpS.mPublishTs = gps.mPublishTs;
eGpS.mSignFlags = gps.mSignFlags;
}
// We should check that the above values are always the same for all info that is received. In the end, we'll
// request the group meta and check the signature, but it may be misleading to receive a forged information
// that is not the real one.
++eGpS.mPopularity; // increase popularity. This is not a real counting, but therefore some heuristic estimate.
eGpS.mNumberOfMessages = std::max( eGpS.mNumberOfMessages, gps.mNumberOfMessages );
eGpS.mLastMessageTs = std::max( eGpS.mLastMessageTs, gps.mLastMessageTs );
if(gps.mSearchContext != gps.mGroupName) // this is a bit of a hack. We should have flags to tell where the search hit happens
eGpS.mSearchContexts.insert(gps.mSearchContext);
}
} // end RS_STACK_MUTEX(mNxsMutex);

View File

@ -140,9 +140,9 @@ public:
virtual void receiveTurtleSearchResults(TurtleRequestId req,const std::list<RsGxsGroupSummary>& group_infos);
virtual void receiveTurtleSearchResults(TurtleRequestId req,const unsigned char *encrypted_group_data,uint32_t encrypted_group_data_len);
virtual bool retrieveDistantSearchResults(TurtleRequestId req, std::map<RsGxsGroupId, RsGxsGroupSummary> &group_infos);
virtual bool retrieveDistantSearchResults(TurtleRequestId req, std::map<RsGxsGroupId, RsGxsGroupSearchResults> &group_infos);
virtual bool clearDistantSearchResults(const TurtleRequestId& id);
virtual bool retrieveDistantGroupSummary(const RsGxsGroupId&,RsGxsGroupSummary&);
virtual bool retrieveDistantGroupSummary(const RsGxsGroupId&, RsGxsGroupSearchResults &);
/*!
* pauses synchronisation of subscribed groups and request for group id
@ -395,7 +395,7 @@ private:
* @return false, if you cannot send to this peer, true otherwise
*/
bool canSendGrpId(const RsPeerId& sslId, const RsGxsGrpMetaData& grpMeta, std::vector<GrpIdCircleVet>& toVet, bool &should_encrypt);
bool canSendMsgIds(std::vector<RsGxsMsgMetaData*>& msgMetas, const RsGxsGrpMetaData&, const RsPeerId& sslId, RsGxsCircleId &should_encrypt_id);
bool canSendMsgIds(std::vector<const RsGxsMsgMetaData*>& msgMetas, const RsGxsGrpMetaData&, const RsPeerId& sslId, RsGxsCircleId &should_encrypt_id);
/*!
* \brief checkPermissionsForFriendGroup
@ -439,6 +439,7 @@ private:
bool locked_CanReceiveUpdate(const RsNxsSyncGrpReqItem *item);
bool locked_CanReceiveUpdate(RsNxsSyncMsgReqItem *item, bool &grp_is_known);
void locked_resetClientTS(const RsGxsGroupId& grpId);
bool locked_checkResendingOfUpdates(const RsPeerId& pid, const RsGxsGroupId &grpId, rstime_t incoming_ts, RsPeerUpdateTsRecord& rec);
static RsGxsGroupId hashGrpId(const RsGxsGroupId& gid,const RsPeerId& pid) ;
@ -609,7 +610,7 @@ private:
std::set<RsGxsGroupId> mNewPublishKeysToNotify ;
// Distant search result map
std::map<TurtleRequestId,std::map<RsGxsGroupId,RsGxsGroupSummary> > mDistantSearchResults ;
std::map<TurtleRequestId,std::map<RsGxsGroupId,RsGxsGroupSearchResults> > mDistantSearchResults ;
void debugDump();

View File

@ -1090,8 +1090,10 @@ void RsGxsNetTunnelService::receiveSearchResult(TurtleSearchRequestId request_id
{
GXS_NET_TUNNEL_DEBUG() << " : result is of type group summary result for service " << result_gs->service << std::dec << ": " << std::endl;
#ifdef DEBUG_RSGXSNETTUNNEL
for(auto it(result_gs->group_infos.begin());it!=result_gs->group_infos.end();++it)
std::cerr << " group " << (*it).mGroupId << ": " << (*it).mGroupName << ", " << (*it).mNumberOfMessages << " messages, last is " << time(NULL)-(*it).mLastMessageTs << " secs ago." << std::endl;
#endif
auto it = mSearchableServices.find(result_gs->service) ;

View File

@ -76,16 +76,6 @@ protected:
bool mMetaChange;
};
class RsGxsDistantSearchResultChange: public RsGxsNotify
{
public:
RsGxsDistantSearchResultChange(TurtleRequestId id,const RsGxsGroupId& gid) : RsGxsNotify(gid), mRequestId(id){}
NotifyType getType() { return TYPE_RECEIVED_DISTANT_SEARCH_RESULTS ; }
TurtleRequestId mRequestId ;
};
/*!
* Relevant to message changes
*/

View File

@ -85,7 +85,7 @@ bool RsGxsMessageCleanUp::clean()
for(; mit != result.end(); ++mit)
{
std::vector<RsGxsMsgMetaData*>& metaV = mit->second;
std::vector<const RsGxsMsgMetaData*>& metaV = mit->second;
// First, make a map of which message have a child message. This allows to only delete messages that dont have child messages.
// A more accurate way to go would be to compute the time of the oldest message and possibly delete all the branch, but in the
@ -99,7 +99,7 @@ bool RsGxsMessageCleanUp::clean()
for( uint32_t i=0;i<metaV.size();++i)
{
RsGxsMsgMetaData* meta = metaV[i];
const RsGxsMsgMetaData* meta = metaV[i];
bool have_kids = (messages_with_kids.find(meta->mMsgId)!=messages_with_kids.end());
@ -132,7 +132,7 @@ bool RsGxsMessageCleanUp::clean()
std::cerr << std::endl;
#endif
delete meta;
//delete meta;
}
}
@ -215,7 +215,8 @@ bool RsGxsIntegrityCheck::check()
rsReputations->overallReputationLevel(
grp->metaData->mAuthorId ) >
RsReputationLevel::LOCALLY_NEGATIVE )
used_gxs_ids.insert(std::make_pair(grp->metaData->mAuthorId, RsIdentityUsage(mGenExchangeClient->serviceType(), RsIdentityUsage::GROUP_AUTHOR_KEEP_ALIVE,grp->grpId)));
used_gxs_ids.insert(std::make_pair(grp->metaData->mAuthorId, RsIdentityUsage(RsServiceType(mGenExchangeClient->serviceType()),
RsIdentityUsage::GROUP_AUTHOR_KEEP_ALIVE,grp->grpId)));
}
}
}
@ -404,7 +405,12 @@ bool RsGxsIntegrityCheck::check()
rsReputations->overallReputationLevel(
msg->metaData->mAuthorId ) >
RsReputationLevel::LOCALLY_NEGATIVE )
used_gxs_ids.insert(std::make_pair(msg->metaData->mAuthorId,RsIdentityUsage(mGenExchangeClient->serviceType(),RsIdentityUsage::MESSAGE_AUTHOR_KEEP_ALIVE,msg->metaData->mGroupId,msg->metaData->mMsgId))) ;
used_gxs_ids.insert(std::make_pair(msg->metaData->mAuthorId,RsIdentityUsage(RsServiceType(mGenExchangeClient->serviceType()),
RsIdentityUsage::MESSAGE_AUTHOR_KEEP_ALIVE,
msg->metaData->mGroupId,
msg->metaData->mMsgId,
msg->metaData->mParentId,
msg->metaData->mThreadId))) ;
}
}

View File

@ -128,7 +128,7 @@ public:
* \return
* false when the request is unknown.
*/
virtual bool retrieveDistantSearchResults(TurtleRequestId req, std::map<RsGxsGroupId, RsGxsGroupSummary> &group_infos)=0;
virtual bool retrieveDistantSearchResults(TurtleRequestId req, std::map<RsGxsGroupId, RsGxsGroupSearchResults> &group_infos)=0;
/*!
* \brief getDistantSearchResults
* \param id
@ -136,7 +136,7 @@ public:
* \return
*/
virtual bool clearDistantSearchResults(const TurtleRequestId& id)=0;
virtual bool retrieveDistantGroupSummary(const RsGxsGroupId&,RsGxsGroupSummary&)=0;
virtual bool retrieveDistantGroupSummary(const RsGxsGroupId&,RsGxsGroupSearchResults&)=0;
virtual bool search(const std::string& substring,std::list<RsGxsGroupSummary>& group_infos) =0;
virtual bool search(const Sha1CheckSum& hashed_group_id,unsigned char *& encrypted_group_data,uint32_t& encrypted_group_data_len)=0;

View File

@ -976,7 +976,7 @@ void p3GxsTunnelService::handleRecvDHPublicKey(RsGxsTunnelDHPublicKeyItem *item)
std::cerr << "(SS) Signature was verified and it doesn't check! This is a security issue!" << std::endl;
return ;
}
mGixs->timeStampKey(item->signature.keyId,RsIdentityUsage(RS_SERVICE_TYPE_GXS_TUNNEL,RsIdentityUsage::GXS_TUNNEL_DH_SIGNATURE_CHECK));
mGixs->timeStampKey(item->signature.keyId,RsIdentityUsage(RsServiceType::GXS_TUNNEL,RsIdentityUsage::GXS_TUNNEL_DH_SIGNATURE_CHECK));
#ifdef DEBUG_GXS_TUNNEL
std::cerr << " Signature checks! Sender's ID = " << senders_id << std::endl;

View File

@ -165,6 +165,7 @@ JsonApiServer::JsonApiServer(): configMutex("JsonApiServer config"),
RsThread::setStopTimeout(10);
#endif
#if !RS_VERSION_AT_LEAST(0,6,6)
registerHandler("/rsLoginHelper/createLocation",
[this](const std::shared_ptr<rb::Session> session)
{
@ -180,6 +181,7 @@ JsonApiServer::JsonApiServer(): configMutex("JsonApiServer config"),
std::string errorMessage;
bool makeHidden = false;
bool makeAutoTor = false;
std::string createToken;
// deserialize input parameters from JSON
{
@ -189,6 +191,7 @@ JsonApiServer::JsonApiServer(): configMutex("JsonApiServer config"),
RS_SERIAL_PROCESS(password);
RS_SERIAL_PROCESS(makeHidden);
RS_SERIAL_PROCESS(makeAutoTor);
RS_SERIAL_PROCESS(createToken);
}
// call retroshare C++ API
@ -196,8 +199,9 @@ JsonApiServer::JsonApiServer(): configMutex("JsonApiServer config"),
location, password, errorMessage, makeHidden,
makeAutoTor );
if(retval)
authorizeUser(location.mLocationId.toStdString(),password);
std::string tokenUser, tokenPw;
if(retval && parseToken(createToken, tokenUser, tokenPw))
authorizeUser(tokenUser,tokenPw);
// serialize out parameters and return value to JSON
{
@ -212,8 +216,9 @@ JsonApiServer::JsonApiServer(): configMutex("JsonApiServer config"),
DEFAULT_API_CALL_JSON_RETURN(rb::OK);
} );
}, false);
#endif // !RS_VERSION_AT_LEAST(0,6,6)
registerHandler("/rsLoginHelper/attemptLogin",
registerHandler("/rsLoginHelper/createLocationV2",
[this](const std::shared_ptr<rb::Session> session)
{
auto reqSize = session->get_request()->get_header("Content-Length", 0);
@ -223,28 +228,51 @@ JsonApiServer::JsonApiServer(): configMutex("JsonApiServer config"),
{
INITIALIZE_API_CALL_JSON_CONTEXT;
RsPeerId account;
RsPeerId locationId;
RsPgpId pgpId;
std::string locationName;
std::string pgpName;
std::string password;
// JSON API only
std::string apiUser;
std::string apiPass;
// deserialize input parameters from JSON
{
RsGenericSerializer::SerializeContext& ctx(cReq);
RsGenericSerializer::SerializeJob j(RsGenericSerializer::FROM_JSON);
RS_SERIAL_PROCESS(account);
RS_SERIAL_PROCESS(locationId);
RS_SERIAL_PROCESS(pgpId);
RS_SERIAL_PROCESS(locationName);
RS_SERIAL_PROCESS(pgpName);
RS_SERIAL_PROCESS(password);
// JSON API only
RS_SERIAL_PROCESS(apiUser);
RS_SERIAL_PROCESS(apiPass);
}
// call retroshare C++ API
RsInit::LoadCertificateStatus retval =
rsLoginHelper->attemptLogin(account, password);
std::error_condition retval;
if( retval == RsInit::OK )
authorizeUser(account.toStdString(), password);
if(apiUser.empty())
retval = RsJsonApiErrorNum::TOKEN_FORMAT_INVALID;
if(!retval)
retval = badApiCredientalsFormat(apiUser, apiPass);
if(!retval) // call retroshare C++ API
retval = rsLoginHelper->createLocationV2(
locationId, pgpId, locationName, pgpName, password );
if(!retval) retval = authorizeUser(apiUser, apiPass);
// serialize out parameters and return value to JSON
{
RsGenericSerializer::SerializeContext& ctx(cAns);
RsGenericSerializer::SerializeJob j(RsGenericSerializer::TO_JSON);
RS_SERIAL_PROCESS(locationId);
RS_SERIAL_PROCESS(pgpId);
RS_SERIAL_PROCESS(retval);
}
@ -467,18 +495,18 @@ void JsonApiServer::registerHandler(
const std::function<void (const std::shared_ptr<rb::Session>)>& callback )
{
/* Declare outside the lambda to avoid returning a dangling
* reference on Android */
* reference */
RsWarn tWarn;
const auto authFail =
[&](int status) -> RsWarn::stream_type&
[&](int status) -> std::ostream&
{
/* Capture session by reference as it is cheaper then copying
* shared_ptr by value which is not needed in this case */
session->close(status, corsOptionsHeaders);
return tWarn << "JsonApiServer authentication handler "
"blocked an attempt to call JSON API "
"authenticated method: " << path;
"blocked an attempt to call JSON API "
"authenticated method: " << path;
};
if(session->get_request()->get_method() == "OPTIONS")

View File

@ -154,6 +154,7 @@ rs_webui {
HEADERS += plugins/pluginmanager.h \
plugins/dlfcn_win32.h \
rsitems/rspluginitems.h \
util/i2pcommon.h \
util/rsinitedptr.h
HEADERS += $$PUBLIC_HEADERS
@ -327,6 +328,8 @@ INCLUDEPATH *= $${OPENPGPSDK_DIR}
PRE_TARGETDEPS *= $${OPENPGPSDK_DIR}/lib/libops.a
LIBS *= $${OPENPGPSDK_DIR}/lib/libops.a -lbz2
################################### HEADERS & SOURCES #############################
HEADERS += ft/ftchunkmap.h \
ft/ftcontroller.h \
ft/ftdata.h \
@ -468,7 +471,12 @@ HEADERS += turtle/p3turtle.h \
turtle/turtleclientservice.h
HEADERS += util/folderiterator.h \
util/rsdebug.h \
util/rsdebug.h \
util/rsdebuglevel0.h \
util/rsdebuglevel1.h \
util/rsdebuglevel2.h \
util/rsdebuglevel3.h \
util/rsdebuglevel4.h \
util/rskbdinput.h \
util/rsmemory.h \
util/smallobject.h \
@ -510,7 +518,8 @@ SOURCES += ft/ftchunkmap.cc \
ft/ftfilesearch.cc \
ft/ftserver.cc \
ft/fttransfermodule.cc \
ft/ftturtlefiletransferitem.cc
ft/ftturtlefiletransferitem.cc \
util/i2pcommon.cpp
SOURCES += crypto/chacha20.cpp \
crypto/hashstream.cc\

View File

@ -1803,15 +1803,16 @@ void p3NetMgrIMPL::updateNatSetting()
#endif
#ifdef RS_USE_DHT_STUNNER
switch(natType)
{
case RSNET_NATTYPE_RESTRICTED_CONE:
if (mProxyStunner) {
switch(natType)
{
case RSNET_NATTYPE_RESTRICTED_CONE:
{
if ((natHole == RSNET_NATHOLE_NONE) || (natHole == RSNET_NATHOLE_UNKNOWN))
{
mProxyStunner->setRefreshPeriod(NET_STUNNER_PERIOD_FAST);
}
else
else
{
mProxyStunner->setRefreshPeriod(NET_STUNNER_PERIOD_SLOW);
}
@ -1826,6 +1827,7 @@ void p3NetMgrIMPL::updateNatSetting()
mProxyStunner->setRefreshPeriod(NET_STUNNER_PERIOD_SLOW);
break;
}
}
#endif // RS_USE_DHT_STUNNER

View File

@ -291,8 +291,8 @@ private:
//p3BitDht *mBitDht;
#ifdef RS_USE_DHT_STUNNER
pqiAddrAssist *mDhtStunner;
pqiAddrAssist *mProxyStunner;
pqiAddrAssist *mDhtStunner = nullptr;
pqiAddrAssist *mProxyStunner = nullptr;
#endif // RS_USE_DHT_STUNNER
RsMutex mNetMtx; /* protects below */

View File

@ -413,9 +413,10 @@ int unix_fcntl_nonblock(int fd)
{
int ret;
/******************* WINDOWS SPECIFIC PART ******************/
/******************* OS SPECIFIC PART ******************/
#ifndef WINDOWS_SYS // ie UNIX
ret = fcntl(fd, F_SETFL, O_NONBLOCK);
int flags = fcntl(fd, F_GETFL);
ret = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
#ifdef NET_DEBUG
std::cerr << "unix_fcntl_nonblock():" << ret << " errno:" << errno << std::endl;

View File

@ -1,5 +1,5 @@
/*******************************************************************************
* libretroshare/src/pqi: pqiservice.h *
* libretroshare/src/pqi: pqiservice.cc *
* *
* libretroshare: retroshare core library *
* *
@ -23,6 +23,22 @@
#include "util/rsdebug.h"
#include "util/rsstring.h"
#include <sstream>
#include <sys/time.h>
static double getCurrentTS()
{
#ifndef WINDOWS_SYS
struct timeval cts_tmp;
gettimeofday(&cts_tmp, NULL);
double cts = (cts_tmp.tv_sec) + ((double) cts_tmp.tv_usec) / 1000000.0;
#else
struct _timeb timebuf;
_ftime( &timebuf);
double cts = (timebuf.time) + ((double) timebuf.millitm) / 1000.0;
#endif
return cts;
}
#ifdef SERVICE_DEBUG
const int pqiservicezone = 60478;
#endif
@ -44,7 +60,7 @@ bool pqiService::send(RsRawItem *item)
p3ServiceServer::p3ServiceServer(pqiPublisher *pub, p3ServiceControl *ctrl) : mPublisher(pub), mServiceControl(ctrl), srvMtx("p3ServiceServer")
{
RsStackMutex stack(srvMtx); /********* LOCKED *********/
RS_STACK_MUTEX(srvMtx); /********* LOCKED *********/
#ifdef SERVICE_DEBUG
pqioutput(PQL_DEBUG_BASIC, pqiservicezone,
@ -56,7 +72,7 @@ p3ServiceServer::p3ServiceServer(pqiPublisher *pub, p3ServiceControl *ctrl) : mP
int p3ServiceServer::addService(pqiService *ts, bool defaultOn)
{
RsStackMutex stack(srvMtx); /********* LOCKED *********/
RS_STACK_MUTEX(srvMtx); /********* LOCKED *********/
#ifdef SERVICE_DEBUG
pqioutput(PQL_DEBUG_BASIC, pqiservicezone,
@ -84,7 +100,7 @@ int p3ServiceServer::addService(pqiService *ts, bool defaultOn)
bool p3ServiceServer::getServiceItemNames(uint32_t service_type,std::map<uint8_t,std::string>& names)
{
RsStackMutex stack(srvMtx); /********* LOCKED *********/
RS_STACK_MUTEX(srvMtx); /********* LOCKED *********/
std::map<uint32_t, pqiService *>::iterator it=services.find(service_type) ;
@ -99,7 +115,7 @@ bool p3ServiceServer::getServiceItemNames(uint32_t service_type,std::map<uint8_t
int p3ServiceServer::removeService(pqiService *ts)
{
RsStackMutex stack(srvMtx); /********* LOCKED *********/
RS_STACK_MUTEX(srvMtx); /********* LOCKED *********/
#ifdef SERVICE_DEBUG
pqioutput(PQL_DEBUG_BASIC, pqiservicezone, "p3ServiceServer::removeService()");
@ -124,60 +140,33 @@ int p3ServiceServer::removeService(pqiService *ts)
bool p3ServiceServer::recvItem(RsRawItem *item)
{
RsStackMutex stack(srvMtx); /********* LOCKED *********/
#ifdef SERVICE_DEBUG
std::cerr << "p3ServiceServer::incoming()";
std::cerr << std::endl;
{
std::string out;
rs_sprintf(out, "p3ServiceServer::incoming() PacketId: %x\nLooking for Service: %x\nItem:\n", item -> PacketId(), (item -> PacketId() & 0xffffff00));
item -> print_string(out);
std::cerr << out;
std::cerr << std::endl;
}
#endif
// Packet Filtering.
// This doesn't need to be in Mutex.
if (!mServiceControl->checkFilter(item->PacketId() & 0xffffff00, item->PeerId()))
{
#ifdef SERVICE_DEBUG
std::cerr << "p3ServiceServer::recvItem() Fails Filtering " << std::endl;
#endif
delete item;
return false;
}
pqiService *s = NULL;
std::map<uint32_t, pqiService *>::iterator it;
it = services.find(item -> PacketId() & 0xffffff00);
if (it == services.end())
// access the service map under mutex lock
{
#ifdef SERVICE_DEBUG
std::cerr << "p3ServiceServer::incoming() Service: No Service - deleting";
std::cerr << std::endl;
#endif
delete item;
return false;
RS_STACK_MUTEX(srvMtx);
auto it = services.find(item -> PacketId() & 0xffffff00);
if (it == services.end())
{
delete item;
return false;
}
s = it->second;
}
{
#ifdef SERVICE_DEBUG
std::cerr << "p3ServiceServer::incoming() Sending to : " << (void *) it -> second;
std::cerr << std::endl;
#endif
return (it->second) -> recv(item);
}
delete item;
return false;
// then call recv off mutex
bool result = s->recv(item);
return result;
}
bool p3ServiceServer::sendItem(RsRawItem *item)
{
#ifdef SERVICE_DEBUG
@ -204,40 +193,27 @@ bool p3ServiceServer::sendItem(RsRawItem *item)
}
mPublisher->sendItem(item);
return true;
}
int p3ServiceServer::tick()
{
mServiceControl->tick();
RsStackMutex stack(srvMtx); /********* LOCKED *********/
#ifdef SERVICE_DEBUG
pqioutput(PQL_DEBUG_ALL, pqiservicezone,
"p3ServiceServer::tick()");
#endif
std::map<uint32_t, pqiService *>::iterator it;
// from the beginning to where we started.
for(it = services.begin();it != services.end(); ++it)
{
#ifdef SERVICE_DEBUG
std::string out;
rs_sprintf(out, "p3ServiceServer::service id: %u -> Service: %p", it -> first, it -> second);
pqioutput(PQL_DEBUG_ALL, pqiservicezone, out);
#endif
// now we should actually tick the service.
(it -> second) -> tick();
// make a copy of the service map
std::map<uint32_t,pqiService *> local_map;
{
RS_STACK_MUTEX(srvMtx);
local_map=services;
}
// tick all services off mutex
for(auto it(local_map.begin());it!=local_map.end();++it)
{
(it->second)->tick();
}
return 1;
}

View File

@ -372,9 +372,11 @@ int pqissl::status()
// tick......
int pqissl::tick()
{
RsStackMutex stack(mSslMtx); /**** LOCKED MUTEX ****/
// there is no reason to lock pqissl mutex now
// we will lock the mutex later if we actually need to call to ConnectAttempt
// RsStackMutex stack(mSslMtx); /**** LOCKED MUTEX ****/
//pqistreamer::tick();
// pqistreamer::tick();
// continue existing connection attempt.
if (!active)
@ -385,7 +387,8 @@ int pqissl::tick()
#ifdef PQISSL_LOG_DEBUG
rslog(RSL_DEBUG_BASIC, pqisslzone, "pqissl::tick() Continuing Connection Attempt!");
#endif
// now lock pqissl mutex, that will take up to 10 ms
RsStackMutex stack(mSslMtx); /**** LOCKED MUTEX ****/
ConnectAttempt();
return 1;
}

View File

@ -1,5 +1,5 @@
/*******************************************************************************
* libretroshare/src/pqi: pqistreamer.h *
* libretroshare/src/pqi: pqistreamer.cc *
* *
* libretroshare: retroshare core library *
* *
@ -102,38 +102,39 @@ pqistreamer::pqistreamer(RsSerialiser *rss, const RsPeerId& id, BinInterface *bi
mAvgDtOut(0), mAvgDtIn(0)
{
// 100 B/s (minimal)
setMaxRate(true, 0.1);
setMaxRate(false, 0.1);
setRate(true, 0); // needs to be off-mutex
setRate(false, 0);
// 100 B/s (minimal)
setMaxRate(true, 0.1);
setMaxRate(false, 0.1);
setRate(true, 0); // needs to be off-mutex
setRate(false, 0);
RsStackMutex stack(mStreamerMtx); /**** LOCKED MUTEX ****/
RsStackMutex stack(mStreamerMtx); /**** LOCKED MUTEX ****/
mAcceptsPacketSlicing = false ; // by default. Will be turned into true when everyone's ready.
mLastSentPacketSlicingProbe = 0 ;
mAcceptsPacketSlicing = false ; // by default. Will be turned into true when everyone's ready.
mLastSentPacketSlicingProbe = 0 ;
mAvgLastUpdate = mCurrSentTS = mCurrReadTS = getCurrentTS();
mAvgLastUpdate = mCurrSentTS = mCurrReadTS = getCurrentTS();
mIncomingSize = 0 ;
mIncomingSize = 0 ;
mIncomingSize_bytes = 0;
mStatisticsTimeStamp = 0 ;
/* allocated once */
mPkt_rpend_size = 0;
mPkt_rpending = 0;
mReading_state = reading_state_initial ;
mStatisticsTimeStamp = 0 ;
/* allocated once */
mPkt_rpend_size = 0;
mPkt_rpending = 0;
mReading_state = reading_state_initial ;
pqioutput(PQL_DEBUG_ALL, pqistreamerzone, "pqistreamer::pqistreamer() Initialisation!");
pqioutput(PQL_DEBUG_ALL, pqistreamerzone, "pqistreamer::pqistreamer() Initialisation!");
if (!bio_in)
{
pqioutput(PQL_ALERT, pqistreamerzone, "pqistreamer::pqistreamer() NULL bio, FATAL ERROR!");
exit(1);
}
if (!bio_in)
{
pqioutput(PQL_ALERT, pqistreamerzone, "pqistreamer::pqistreamer() NULL bio, FATAL ERROR!");
exit(1);
}
mFailed_read_attempts = 0; // reset failed read, as no packet is still read.
mFailed_read_attempts = 0; // reset failed read, as no packet is still read.
return;
return;
}
pqistreamer::~pqistreamer()
@ -159,7 +160,7 @@ pqistreamer::~pqistreamer()
if (mRsSerialiser)
delete mRsSerialiser;
free_pend_locked() ;
free_pend() ;
// clean up incoming.
while (!mIncoming.empty())
@ -177,6 +178,7 @@ pqistreamer::~pqistreamer()
// Get/Send Items.
// This is the entry poing for methods willing to send items through our out queue
int pqistreamer::SendItem(RsItem *si,uint32_t& out_size)
{
#ifdef RSITEM_DEBUG
@ -199,18 +201,30 @@ RsItem *pqistreamer::GetItem()
pqioutput(PQL_DEBUG_ALL, pqistreamerzone, "pqistreamer::GetItem()");
#endif
RsStackMutex stack(mStreamerMtx); /**** LOCKED MUTEX ****/
if(mIncoming.empty())
return NULL;
RsItem *osr = mIncoming.front() ;
mIncoming.pop_front() ;
--mIncomingSize;
mIncoming.pop_front() ;
--mIncomingSize;
// for future use
// mIncomingSize_bytes -=
return osr;
}
float pqistreamer::getMaxRate(bool b)
{
RsStackMutex stack(mStreamerMtx); /**** LOCKED MUTEX ****/
return getMaxRate_locked(b);
}
float pqistreamer::getMaxRate_locked(bool b)
{
return RateInterface::getMaxRate(b) ;
}
float pqistreamer::getRate(bool b)
{
RsStackMutex stack(mStreamerMtx); /**** LOCKED MUTEX ****/
@ -219,26 +233,28 @@ float pqistreamer::getRate(bool b)
void pqistreamer::setMaxRate(bool b,float f)
{
RsStackMutex stack(mStreamerMtx); /**** LOCKED MUTEX ****/
RateInterface::setMaxRate(b,f) ;
RsStackMutex stack(mStreamerMtx); /**** LOCKED MUTEX ****/
setMaxRate_locked(b,f);
}
void pqistreamer::setMaxRate_locked(bool b,float f)
{
RateInterface::setMaxRate(b,f) ;
}
void pqistreamer::setRate(bool b,float f)
{
RsStackMutex stack(mStreamerMtx); /**** LOCKED MUTEX ****/
RateInterface::setRate(b,f) ;
}
void pqistreamer::updateRates()
{
// update rates both ways.
// update actual rates both ways.
double t = getCurrentTS(); // get current timestamp.
double diff ;
{
RsStackMutex stack(mStreamerMtx); /**** LOCKED MUTEX ****/
diff = t - mAvgLastUpdate ;
}
double diff = t - mAvgLastUpdate;
if (diff > PQISTREAM_AVG_PERIOD)
{
@ -263,10 +279,11 @@ void pqistreamer::updateRates()
setRate(false, 0);
}
mAvgLastUpdate = t;
mAvgReadCount = 0;
{
RsStackMutex stack(mStreamerMtx); /**** LOCKED MUTEX ****/
mAvgLastUpdate = t;
mAvgReadCount = 0;
mAvgSentCount = 0;
}
}
@ -277,7 +294,7 @@ int pqistreamer::tick_bio()
RsStackMutex stack(mStreamerMtx); /**** LOCKED MUTEX ****/
mBio->tick();
/* short circuit everything is bio isn't active */
/* short circuit everything if bio isn't active */
if (!(mBio->isactive()))
{
return 0;
@ -285,36 +302,36 @@ int pqistreamer::tick_bio()
return 1;
}
int pqistreamer::tick_recv(uint32_t timeout)
{
RsStackMutex stack(mStreamerMtx); /**** LOCKED MUTEX ****/
// Apart from a few exceptions that are atomic (mLastIncomingTs, mIncomingSize), only this pqi thread reads/writes mIncoming queue and related counters.
// The lock of pqistreamer mutex is thus not needed here.
// The mutex lock is still needed before calling locked_addTrafficClue because this method is also used by the thread pushing packets in mOutPkts.
// Locks around rates are provided internally.
if (mBio->moretoread(timeout))
{
handleincoming_locked();
handleincoming();
}
if(!(mBio->isactive()))
{
free_pend();
}
if(!(mBio->isactive()))
{
free_pend_locked();
}
return 1;
}
int pqistreamer::tick_send(uint32_t timeout)
{
RsStackMutex stack(mStreamerMtx); /**** LOCKED MUTEX ****/
/* short circuit everything is bio isn't active */
/* short circuit everything if bio isn't active */
if (!(mBio->isactive()))
{
free_pend_locked();
free_pend();
return 0;
}
if (mBio->cansend(timeout))
{
RsStackMutex stack(mStreamerMtx); /**** LOCKED MUTEX ****/
handleoutgoing_locked();
}
@ -340,12 +357,11 @@ int pqistreamer::status()
return 0;
}
// this method is overloaded by pqiqosstreamer
void pqistreamer::locked_storeInOutputQueue(void *ptr,int,int)
{
mOutPkts.push_back(ptr);
}
//
/**************** HANDLE OUTGOING TRANSLATION + TRANSMISSION ******/
int pqistreamer::queue_outpqi_locked(RsItem *pqi,uint32_t& pktsize)
{
@ -354,7 +370,6 @@ int pqistreamer::queue_outpqi_locked(RsItem *pqi,uint32_t& pktsize)
std::cerr << "pqistreamer::queue_outpqi() called." << std::endl;
#endif
/* decide which type of packet it is */
pktsize = mRsSerialiser->size(pqi);
@ -362,7 +377,6 @@ int pqistreamer::queue_outpqi_locked(RsItem *pqi,uint32_t& pktsize)
if(ptr == NULL)
return 0 ;
#ifdef DEBUG_PQISTREAMER
std::cerr << "pqistreamer::queue_outpqi() serializing packet with packet size : " << pktsize << std::endl;
@ -403,27 +417,31 @@ int pqistreamer::queue_outpqi_locked(RsItem *pqi,uint32_t& pktsize)
return 1; // keep error internal.
}
int pqistreamer::handleincomingitem_locked(RsItem *pqi,int len)
int pqistreamer::handleincomingitem(RsItem *pqi,int len)
{
#ifdef DEBUG_PQISTREAMER
pqioutput(PQL_DEBUG_ALL, pqistreamerzone, "pqistreamer::handleincomingitem_locked()");
pqioutput(PQL_DEBUG_ALL, pqistreamerzone, "pqistreamer::handleincomingitem()");
#endif
// timestamp last received packet.
mLastIncomingTs = time(NULL);
// Use overloaded Contact function
pqi -> PeerId(PeerId());
mIncoming.push_back(pqi);
++mIncomingSize ;
/*******************************************************************************************/
// keep info for stats for a while. Only keep the items for the last two seconds. sec n is ongoing and second n-1
// is a full statistics chunk that can be used in the GUI
mIncoming.push_back(pqi);
++mIncomingSize;
// for future use
// mIncomingSize_bytes += len;
locked_addTrafficClue(pqi,len,mCurrentStatsChunk_In) ;
/*******************************************************************************************/
/*******************************************************************************************/
// keep info for stats for a while. Only keep the items for the last two seconds. sec n is ongoing and second n-1
// is a full statistics chunk that can be used in the GUI
{
RsStackMutex stack(mStreamerMtx); /**** LOCKED MUTEX ****/
locked_addTrafficClue(pqi,len,mCurrentStatsChunk_In) ;
}
/*******************************************************************************************/
return 1;
}
@ -456,8 +474,8 @@ void pqistreamer::locked_addTrafficClue(const RsItem *pqi,uint32_t pktsize,std::
rstime_t pqistreamer::getLastIncomingTS()
{
RsStackMutex stack(mStreamerMtx); /**** LOCKED MUTEX ****/
// This is the only case where another thread (rs main for pqiperson) will access our data
// Still a mutex lock is not needed because the operation is atomic
return mLastIncomingTs;
}
@ -693,23 +711,23 @@ int pqistreamer::handleoutgoing_locked()
/* Handles reading from input stream.
*/
int pqistreamer::handleincoming_locked()
int pqistreamer::handleincoming()
{
int readbytes = 0;
static const int max_failed_read_attempts = 2000 ;
#ifdef DEBUG_PQISTREAMER
pqioutput(PQL_DEBUG_ALL, pqistreamerzone, "pqistreamer::handleincoming_locked()");
pqioutput(PQL_DEBUG_ALL, pqistreamerzone, "pqistreamer::handleincoming()");
#endif
if(!(mBio->isactive()))
{
mReading_state = reading_state_initial ;
free_pend_locked();
free_pend();
return 0;
}
else
allocate_rpend_locked();
allocate_rpend();
// enough space to read any packet.
uint32_t maxlen = mPkt_rpend_size;
@ -718,7 +736,7 @@ int pqistreamer::handleincoming_locked()
// initial read size: basic packet.
int blen = getRsPktBaseSize(); // this is valid for both packet slices and normal un-sliced packets (same header size)
int maxin = inAllowedBytes_locked();
int maxin = inAllowedBytes();
#ifdef DEBUG_PQISTREAMER
std::cerr << "[" << (void*)pthread_self() << "] " << "reading state = " << mReading_state << std::endl ;
@ -967,19 +985,19 @@ continue_packet:
std::cerr << "Inputing partial packet " << RsUtil::BinToHex((char*)block,8) << std::endl;
#endif
uint32_t packet_length = 0 ;
pkt = addPartialPacket_locked(block,pktlen,slice_packet_id,is_packet_starting,is_packet_ending,packet_length) ;
pkt = addPartialPacket(block,pktlen,slice_packet_id,is_packet_starting,is_packet_ending,packet_length) ;
pktlen = packet_length ;
}
else
pkt = mRsSerialiser->deserialise(block, &pktlen);
if ((pkt != NULL) && (0 < handleincomingitem_locked(pkt,pktlen)))
if ((pkt != NULL) && (0 < handleincomingitem(pkt,pktlen)))
{
#ifdef DEBUG_PQISTREAMER
pqioutput(PQL_DEBUG_BASIC, pqistreamerzone, "Successfully Read a Packet!");
#endif
inReadBytes_locked(pktlen); // only count deserialised packets, because that's what is actually been transfered.
inReadBytes(pktlen); // only count deserialised packets, because that's what is actually been transfered.
}
else if (!is_partial_packet)
{
@ -1012,7 +1030,7 @@ continue_packet:
return 0;
}
RsItem *pqistreamer::addPartialPacket_locked(const void *block, uint32_t len, uint32_t slice_packet_id, bool is_packet_starting, bool is_packet_ending, uint32_t &total_len)
RsItem *pqistreamer::addPartialPacket(const void *block, uint32_t len, uint32_t slice_packet_id, bool is_packet_starting, bool is_packet_ending, uint32_t &total_len)
{
#ifdef DEBUG_PACKET_SLICING
std::cerr << "Receiving partial packet. size=" << len << ", ID=" << std::hex << slice_packet_id << std::dec << ", starting:" << is_packet_starting << ", ending:" << is_packet_ending ;
@ -1134,7 +1152,7 @@ int pqistreamer::outAllowedBytes_locked()
// low pass filter on mAvgDtOut
mAvgDtOut = PQISTREAM_AVG_DT_FRAC * mAvgDtOut + (1 - PQISTREAM_AVG_DT_FRAC) * dt;
double maxout = getMaxRate(false) * 1024.0;
double maxout = getMaxRate_locked(false) * 1024.0;
// this is used to take into account a possible excess of data sent during the previous round
mCurrSent -= int(dt * maxout);
@ -1156,7 +1174,7 @@ int pqistreamer::outAllowedBytes_locked()
return quota;
}
int pqistreamer::inAllowedBytes_locked()
int pqistreamer::inAllowedBytes()
{
double t = getCurrentTS(); // in sec, with high accuracy
@ -1194,7 +1212,7 @@ int pqistreamer::inAllowedBytes_locked()
#ifdef DEBUG_PQISTREAMER
uint64_t t_now = 1000 * getCurrentTS();
std::cerr << std::dec << t_now << " DEBUG_PQISTREAMER pqistreamer::inAllowedBytes_locked PeerId " << this->PeerId().toStdString() << " dt " << (int)(1000 * dt) << "ms, mAvgDtIn " << (int)(1000 * mAvgDtIn) << "ms, maxin " << (int)(maxin) << " bytes/s, mCurrRead " << mCurrRead << " bytes, quota " << (int)(quota) << " bytes" << std::endl;
std::cerr << std::dec << t_now << " DEBUG_PQISTREAMER pqistreamer::inAllowedBytes PeerId " << this->PeerId().toStdString() << " dt " << (int)(1000 * dt) << "ms, mAvgDtIn " << (int)(1000 * mAvgDtIn) << "ms, maxin " << (int)(maxin) << " bytes/s, mCurrRead " << mCurrRead << " bytes, quota " << (int)(quota) << " bytes" << std::endl;
#endif
return quota;
@ -1231,7 +1249,7 @@ void pqistreamer::outSentBytes_locked(uint32_t outb)
return;
}
void pqistreamer::inReadBytes_locked(uint32_t inb)
void pqistreamer::inReadBytes(uint32_t inb)
{
#ifdef DEBUG_PQISTREAMER
{
@ -1248,7 +1266,7 @@ void pqistreamer::inReadBytes_locked(uint32_t inb)
return;
}
void pqistreamer::allocate_rpend_locked()
void pqistreamer::allocate_rpend()
{
if(mPkt_rpending)
return;
@ -1271,17 +1289,17 @@ int pqistreamer::reset()
#ifdef DEBUG_PQISTREAMER
std::cerr << "pqistreamer::reset()" << std::endl;
#endif
free_pend_locked();
free_pend();
return 1 ;
}
void pqistreamer::free_pend_locked()
void pqistreamer::free_pend()
{
if(mPkt_rpending)
{
#ifdef DEBUG_PQISTREAMER
std::cerr << "pqistreamer::free_pend_locked(): pending input packet buffer" << std::endl;
std::cerr << "pqistreamer::free_pend(): pending input packet buffer" << std::endl;
#endif
free(mPkt_rpending);
mPkt_rpending = 0;
@ -1291,7 +1309,7 @@ void pqistreamer::free_pend_locked()
if (mPkt_wpending)
{
#ifdef DEBUG_PQISTREAMER
std::cerr << "pqistreamer::free_pend_locked(): pending output packet buffer" << std::endl;
std::cerr << "pqistreamer::free_pend(): pending output packet buffer" << std::endl;
#endif
free(mPkt_wpending);
mPkt_wpending = NULL;
@ -1300,7 +1318,7 @@ void pqistreamer::free_pend_locked()
#ifdef DEBUG_PQISTREAMER
if(!mPartialPackets.empty())
std::cerr << "pqistreamer::free_pend_locked(): " << mPartialPackets.size() << " pending input partial packets" << std::endl;
std::cerr << "pqistreamer::free_pend(): " << mPartialPackets.size() << " pending input partial packets" << std::endl;
#endif
// also delete any incoming partial packet
for(std::map<uint32_t,PartialPacketRecord>::iterator it(mPartialPackets.begin());it!=mPartialPackets.end();++it)
@ -1318,26 +1336,47 @@ int pqistreamer::gatherStatistics(std::list<RSTrafficClue>& outqueue_lst,std
return locked_gatherStatistics(outqueue_lst,inqueue_lst);
}
// this method is overloaded by pqiqosstreamer
int pqistreamer::getQueueSize(bool in)
{
RsStackMutex stack(mStreamerMtx); /**** LOCKED MUTEX ****/
if (in)
return mIncomingSize;
else
return locked_out_queue_size();
// no mutex is needed here because this is atomic
return mIncomingSize;
else
{
RsStackMutex stack(mStreamerMtx); /**** LOCKED MUTEX ****/
return locked_out_queue_size();
}
}
int pqistreamer::getQueueSize_bytes(bool in)
{
if (in)
// no mutex is needed here because this is atomic
// for future use, mIncomingSize_bytes is not updated yet
return mIncomingSize_bytes;
else
{
RsStackMutex stack(mStreamerMtx); /**** LOCKED MUTEX ****/
return locked_compute_out_pkt_size();
}
}
void pqistreamer::getRates(RsBwRates &rates)
{
RateInterface::getRates(rates);
RsStackMutex stack(mStreamerMtx); /**** LOCKED MUTEX ****/
// no mutex is needed here because this is atomic
rates.mQueueIn = mIncomingSize;
rates.mQueueIn = mIncomingSize;
rates.mQueueOut = locked_out_queue_size();
{
RsStackMutex stack(mStreamerMtx); /**** LOCKED MUTEX ****/
rates.mQueueOut = locked_out_queue_size();
}
}
// this method is overloaded by pqiqosstreamer
int pqistreamer::locked_out_queue_size() const
{
// Warning: because out_pkt is a list, calling size
@ -1347,6 +1386,7 @@ int pqistreamer::locked_out_queue_size() const
return mOutPkts.size() ;
}
// this method is overloaded by pqiqosstreamer
void pqistreamer::locked_clear_out_queue()
{
for(std::list<void*>::iterator it = mOutPkts.begin(); it != mOutPkts.end(); )
@ -1361,6 +1401,7 @@ void pqistreamer::locked_clear_out_queue()
}
}
// this method is overloaded by pqiqosstreamer
int pqistreamer::locked_compute_out_pkt_size() const
{
int total = 0 ;
@ -1379,6 +1420,7 @@ int pqistreamer::locked_gatherStatistics(std::list<RSTrafficClue>& out_lst,std::
return 1 ;
}
// this method is overloaded by pqiqosstreamer
void *pqistreamer::locked_pop_out_data(uint32_t /*max_slice_size*/, uint32_t &size, bool &starts, bool &ends, uint32_t &packet_id)
{
size = 0 ;
@ -1400,4 +1442,3 @@ void *pqistreamer::locked_pop_out_data(uint32_t /*max_slice_size*/, uint32_t &si
return res ;
}

View File

@ -38,8 +38,8 @@ class RsSerialiser;
struct PartialPacketRecord
{
void *mem ;
uint32_t size ;
void *mem ;
uint32_t size ;
};
/**
@ -65,18 +65,23 @@ class pqistreamer: public PQInterface
virtual RsItem *GetItem();
virtual int status();
rstime_t getLastIncomingTS(); // Time of last data packet, for checking a connection is alive.
rstime_t getLastIncomingTS(); // Time of last data packet, for checking a connection is alive.
virtual void getRates(RsBwRates &rates);
virtual int getQueueSize(bool in); // extracting data.
virtual int getQueueSize_bytes(bool in); // size of incoming queue in bytes
virtual int gatherStatistics(std::list<RSTrafficClue>& outqueue_stats,std::list<RSTrafficClue>& inqueue_stats); // extracting data.
// mutex protected versions of RateInterface calls.
virtual void setRate(bool b,float f) ;
virtual void setMaxRate(bool b,float f) ;
virtual float getRate(bool b) ;
virtual void setMaxRate_locked(bool b,float f) ;
protected:
virtual int reset() ;
virtual float getRate(bool b) ;
virtual float getMaxRate(bool b) ;
virtual float getMaxRate_locked(bool b);
protected:
virtual int reset() ;
int tick_bio();
int tick_send(uint32_t timeout);
@ -104,12 +109,12 @@ class pqistreamer: public PQInterface
private:
int queue_outpqi_locked(RsItem *i,uint32_t& serialized_size);
int handleincomingitem_locked(RsItem *i, int len);
int handleincomingitem(RsItem *i, int len);
// ticked regularly (manages out queues and sending
// via above interfaces.
virtual int handleoutgoing_locked();
virtual int handleincoming_locked();
virtual int handleincoming();
// Bandwidth/Streaming Management.
float outTimeSlice_locked();
@ -117,11 +122,11 @@ class pqistreamer: public PQInterface
int outAllowedBytes_locked();
void outSentBytes_locked(uint32_t );
int inAllowedBytes_locked();
void inReadBytes_locked(uint32_t );
int inAllowedBytes();
void inReadBytes(uint32_t );
// cleans up everything that's pending / half finished.
void free_pend_locked();
void free_pend();
// RsSerialiser - determines which packets can be serialised.
RsSerialiser *mRsSerialiser;
@ -129,13 +134,12 @@ class pqistreamer: public PQInterface
void *mPkt_wpending; // storage for pending packet to write.
uint32_t mPkt_wpending_size; // ... and its size.
void allocate_rpend_locked(); // use these two functions to allocate/free the buffer below
void allocate_rpend(); // use these two functions to allocate/free the buffer below
int mPkt_rpend_size; // size of pkt_rpending.
void *mPkt_rpending; // storage for read in pending packets.
enum {reading_state_packet_started=1,
reading_state_initial=0 } ;
enum {reading_state_packet_started=1, reading_state_initial=0 } ;
int mReading_state ;
int mFailed_read_attempts ;
@ -144,7 +148,8 @@ class pqistreamer: public PQInterface
std::list<void *> mOutPkts; // Cntrl / Search / Results queue
std::list<RsItem *> mIncoming;
uint32_t mIncomingSize; // size of mIncoming. To avoid calling linear cost std::list::size()
uint32_t mIncomingSize; // size of mIncoming. To avoid calling linear cost std::list::size()
uint32_t mIncomingSize_bytes; // size of Incoming in btyes
// data for network stats.
int mTotalRead;
@ -154,8 +159,8 @@ class pqistreamer: public PQInterface
int mCurrRead;
int mCurrSent;
double mCurrReadTS; // TS from which these are measured.
double mCurrSentTS;
double mCurrReadTS; // TS from which these are measured.
double mCurrSentTS;
double mAvgLastUpdate; // TS from which these are measured.
uint32_t mAvgReadCount;
@ -174,12 +179,12 @@ class pqistreamer: public PQInterface
std::list<RSTrafficClue> mCurrentStatsChunk_Out ;
rstime_t mStatisticsTimeStamp ;
bool mAcceptsPacketSlicing ;
rstime_t mLastSentPacketSlicingProbe ;
void locked_addTrafficClue(const RsItem *pqi, uint32_t pktsize, std::list<RSTrafficClue> &lst);
RsItem *addPartialPacket_locked(const void *block, uint32_t len, uint32_t slice_packet_id,bool packet_starting,bool packet_ending,uint32_t& total_len);
bool mAcceptsPacketSlicing ;
rstime_t mLastSentPacketSlicingProbe ;
void locked_addTrafficClue(const RsItem *pqi, uint32_t pktsize, std::list<RSTrafficClue> &lst);
RsItem *addPartialPacket(const void *block, uint32_t len, uint32_t slice_packet_id,bool packet_starting,bool packet_ending,uint32_t& total_len);
std::map<uint32_t,PartialPacketRecord> mPartialPackets ;
std::map<uint32_t,PartialPacketRecord> mPartialPackets ;
};
#endif //MRK_PQI_STREAMER_HEADER

View File

@ -23,17 +23,17 @@
#include "pqi/pqithreadstreamer.h"
#include <unistd.h>
#define DEFAULT_STREAMER_TIMEOUT 10000 // 10 ms.
#define DEFAULT_STREAMER_SLEEP 1000 // 1 ms.
#define DEFAULT_STREAMER_TIMEOUT 10000 // 10 ms
#define DEFAULT_STREAMER_SLEEP 30000 // 30 ms
#define DEFAULT_STREAMER_IDLE_SLEEP 1000000 // 1 sec
//#define PQISTREAMER_DEBUG
// #define PQISTREAMER_DEBUG
pqithreadstreamer::pqithreadstreamer(PQInterface *parent, RsSerialiser *rss, const RsPeerId& id, BinInterface *bio_in, int bio_flags_in)
:pqistreamer(rss, id, bio_in, bio_flags_in), mParent(parent), mTimeout(0), mThreadMutex("pqithreadstreamer")
{
mTimeout = DEFAULT_STREAMER_TIMEOUT;
mSleepPeriod = DEFAULT_STREAMER_SLEEP;
mTimeout = DEFAULT_STREAMER_TIMEOUT;
mSleepPeriod = DEFAULT_STREAMER_SLEEP;
}
bool pqithreadstreamer::RecvItem(RsItem *item)
@ -43,55 +43,59 @@ bool pqithreadstreamer::RecvItem(RsItem *item)
int pqithreadstreamer::tick()
{
RsStackMutex stack(mThreadMutex);
tick_bio();
// pqithreadstreamer mutex lock is not needed here
// we will only check if the connection is active, and if not we will try to establish it
tick_bio();
return 0;
}
void pqithreadstreamer::threadTick()
{
uint32_t recv_timeout = 0;
uint32_t sleep_period = 0;
bool isactive = false;
{
RsStackMutex stack(mStreamerMtx);
recv_timeout = mTimeout;
sleep_period = mSleepPeriod;
isactive = mBio->isactive();
}
uint32_t recv_timeout = 0;
uint32_t sleep_period = 0;
bool isactive = false;
{
RsStackMutex stack(mStreamerMtx);
recv_timeout = mTimeout;
sleep_period = mSleepPeriod;
isactive = mBio->isactive();
}
updateRates() ;
// update the connection rates
updateRates() ;
if (!isactive)
{
rstime::rs_usleep(DEFAULT_STREAMER_IDLE_SLEEP);
return ;
}
// if the connection est not active, long sleep then return
if (!isactive)
{
rstime::rs_usleep(DEFAULT_STREAMER_IDLE_SLEEP);
return ;
}
{
RsStackMutex stack(mThreadMutex);
tick_recv(recv_timeout);
}
// fill incoming queue with items from SSL
{
RsStackMutex stack(mThreadMutex);
tick_recv(recv_timeout);
}
// Push Items, Outside of Mutex.
RsItem *incoming = NULL;
while((incoming = GetItem()))
{
RecvItem(incoming);
}
// move items to appropriate service queue or shortcut to fast service
RsItem *incoming = NULL;
while((incoming = GetItem()))
{
RecvItem(incoming);
}
{
RsStackMutex stack(mThreadMutex);
tick_send(0);
}
// parse the outgoing queue and send items to SSL
{
RsStackMutex stack(mThreadMutex);
tick_send(0);
}
if (sleep_period)
{
rstime::rs_usleep(sleep_period);
}
// sleep
if (sleep_period)
{
rstime::rs_usleep(sleep_period);
}
}

View File

@ -33,6 +33,7 @@
#include "serialiser/rsserializable.h"
#include "serialiser/rstypeserializer.h"
#include "util/rstime.h"
#include "util/rsdebug.h"
class RsEvents;
@ -126,8 +127,7 @@ struct RsEventsErrorCategory: std::error_category
case RsEventsErrorNum::INVALID_HANDLER_ID:
return "Invalid handler id";
default:
return "Error message for error: " + std::to_string(ev) +
" not available in category: " + name();
return rsErrorNotInCategory(ev, name());
}
}

View File

@ -37,6 +37,7 @@
#include "util/rstime.h"
#include "retroshare/rsevents.h"
#include "util/rsmemory.h"
#include "util/rsdebug.h"
class RsFiles;
@ -63,8 +64,7 @@ struct RsFilesErrorCategory: std::error_category
case RsFilesErrorNum::FILES_HANDLE_NOT_FOUND:
return "Files handle not found";
default:
return "Error message for error: " + std::to_string(ev) +
" not available in category: " + name();
return rsErrorNotInCategory(ev, name());
}
}

View File

@ -117,14 +117,11 @@ enum class RsChannelEventCode: uint8_t
struct RsGxsChannelEvent: RsEvent
{
RsGxsChannelEvent():
RsEvent(RsEventType::GXS_CHANNELS),
mChannelEventCode(RsChannelEventCode::UNKNOWN) {}
RsGxsChannelEvent(): RsEvent(RsEventType::GXS_CHANNELS), mChannelEventCode(RsChannelEventCode::UNKNOWN) {}
RsChannelEventCode mChannelEventCode;
RsGxsGroupId mChannelGroupId;
RsGxsMessageId mChannelMsgId;
TurtleRequestId mDistantSearchRequestId;
///* @see RsEvent @see RsSerializable
void serial_process( RsGenericSerializer::SerializeJob j,RsGenericSerializer::SerializeContext& ctx) override
@ -134,8 +131,28 @@ struct RsGxsChannelEvent: RsEvent
RS_SERIAL_PROCESS(mChannelEventCode);
RS_SERIAL_PROCESS(mChannelGroupId);
RS_SERIAL_PROCESS(mChannelMsgId);
RS_SERIAL_PROCESS(mDistantSearchRequestId);
}
}
};
// This event is used to factor multiple search results notifications in a single event.
struct RsGxsChannelSearchResultEvent: RsEvent
{
RsGxsChannelSearchResultEvent():
RsEvent(RsEventType::GXS_CHANNELS),
mChannelEventCode(RsChannelEventCode::RECEIVED_DISTANT_SEARCH_RESULT) {}
RsChannelEventCode mChannelEventCode;
std::map<TurtleRequestId,std::set<RsGxsGroupId> > mSearchResultsMap;
///* @see RsEvent @see RsSerializable
void serial_process( RsGenericSerializer::SerializeJob j,RsGenericSerializer::SerializeContext& ctx) override
{
RsEvent::serial_process(j, ctx);
RS_SERIAL_PROCESS(mChannelEventCode);
RS_SERIAL_PROCESS(mSearchResultsMap);
}
};
class RsGxsChannels: public RsGxsIfaceHelper, public RsGxsCommentService
@ -407,48 +424,6 @@ public:
*/
virtual bool getChannelStatistics(const RsGxsGroupId& channelId,GxsGroupStatistic& stat) =0;
/**
* @brief Request remote channels search
* @jsonapi{development}
* @param[in] matchString string to look for in the search
* @param multiCallback function that will be called each time a search
* result is received
* @param[in] maxWait maximum wait time in seconds for search results
* @return false on error, true otherwise
*/
virtual bool turtleSearchRequest(
const std::string& matchString,
const std::function<void (const RsGxsGroupSummary& result)>& multiCallback,
rstime_t maxWait = 300 ) = 0;
/**
* @brief Request remote channel
* @jsonapi{development}
* @param[in] channelId id of the channel to request to distants peers
* @param multiCallback function that will be called each time a result is
* received
* @param[in] maxWait maximum wait time in seconds for search results
* @return false on error, true otherwise
*/
virtual bool turtleChannelRequest(
const RsGxsGroupId& channelId,
const std::function<void (const RsGxsChannelGroup& result)>& multiCallback,
rstime_t maxWait = 300 ) = 0;
/**
* @brief Search local channels
* @jsonapi{development}
* @param[in] matchString string to look for in the search
* @param multiCallback function that will be called for each result
* @param[in] maxWait maximum wait time in seconds for search results
* @return false on error, true otherwise
*/
virtual bool localSearchRequest(
const std::string& matchString,
const std::function<void (const RsGxsGroupSummary& result)>& multiCallback,
rstime_t maxWait = 30 ) = 0;
/// default base URL used for channels links @see exportChannelLink
static const std::string DEFAULT_CHANNEL_BASE_URL;
@ -493,6 +468,7 @@ public:
/**
* @brief Import channel from full link
* @jsonapi{development}
* @param[in] link channel link either in radix or link format
* @param[out] chanId optional storage for parsed channel id
* @param[out] errMsg optional storage for error message, meaningful only in
@ -504,7 +480,58 @@ public:
RsGxsGroupId& chanId = RS_DEFAULT_STORAGE_PARAM(RsGxsGroupId),
std::string& errMsg = RS_DEFAULT_STORAGE_PARAM(std::string) ) = 0;
/**
* @brief Search the turtle reachable network for matching channels
* @jsonapi{development}
* An @see RsGxsChannelSearchResultEvent is emitted when matching channels
* arrives from the network
* @param[in] matchString string to search into the channels
* @return search id
*/
virtual TurtleRequestId turtleSearchRequest(const std::string& matchString)=0;
/**
* @brief Retrieve available search results
* @jsonapi{development}
* @param[in] searchId search id
* @param[out] results storage for search results
* @return false on error, true otherwise
*/
virtual bool retrieveDistantSearchResults(
TurtleRequestId searchId,
std::map<RsGxsGroupId, RsGxsGroupSearchResults>& results ) = 0;
/**
* @brief Request distant channel details
* @jsonapi{development}
* An @see RsGxsChannelSearchResultEvent is emitted once details are
* retrieved from the network
* @param[in] groupId if of the group to request to the network
* @return search id
*/
virtual TurtleRequestId turtleGroupRequest(const RsGxsGroupId& groupId) = 0;
/**
* @brief Retrieve previously requested distant group
* @jsonapi{development}
* @param[in] groupId if of teh group
* @param[out] distantGroup storage for group data
* @return false on error, true otherwise
*/
virtual bool getDistantSearchResultGroupData(
const RsGxsGroupId& groupId, RsGxsChannelGroup& distantGroup ) = 0;
/**
* @brief Clear accumulated search results
* @jsonapi{development}
* @param[in] reqId search id
* @return false on error, true otherwise
*/
virtual bool clearDistantSearchResults(TurtleRequestId reqId) = 0;
~RsGxsChannels() override;
////////////////////////////////////////////////////////////////////////////
/* Following functions are deprecated and should not be considered a safe to
* use API */
@ -690,22 +717,4 @@ public:
*/
RS_DEPRECATED_FOR(editChannel)
virtual bool updateGroup(uint32_t& token, RsGxsChannelGroup& group) = 0;
//////////////////////////////////////////////////////////////////////////////
/// Distant synchronisation methods ///
//////////////////////////////////////////////////////////////////////////////
///
RS_DEPRECATED_FOR(turtleChannelRequest)
virtual TurtleRequestId turtleGroupRequest(const RsGxsGroupId& group_id)=0;
RS_DEPRECATED
virtual TurtleRequestId turtleSearchRequest(const std::string& match_string)=0;
RS_DEPRECATED_FOR(turtleSearchRequest)
virtual bool retrieveDistantSearchResults(TurtleRequestId req, std::map<RsGxsGroupId, RsGxsGroupSummary> &results) =0;
RS_DEPRECATED
virtual bool clearDistantSearchResults(TurtleRequestId req)=0;
RS_DEPRECATED_FOR(turtleChannelRequest)
virtual bool retrieveDistantGroup(const RsGxsGroupId& group_id,RsGxsChannelGroup& distant_group)=0;
//////////////////////////////////////////////////////////////////////////////
~RsGxsChannels() override;
};

View File

@ -72,6 +72,46 @@ struct RsGxsGroupSummary : RsSerializable
~RsGxsGroupSummary();
};
/*!
* This structure is used to locally store group search results for a given service.
* It contains the group information as well as a context
* strings to tell where the information was found. It is more compact than a
* GroupMeta object, so as to make search responses as light as possible.
*/
struct RsGxsGroupSearchResults : RsSerializable
{
RsGxsGroupSearchResults()
: mPublishTs(0), mNumberOfMessages(0),mLastMessageTs(0), mSignFlags(0),mPopularity(0)
{}
RsGxsGroupId mGroupId;
std::string mGroupName;
RsGxsId mAuthorId;
rstime_t mPublishTs;
uint32_t mNumberOfMessages;
rstime_t mLastMessageTs;
uint32_t mSignFlags;
uint32_t mPopularity;
std::set<std::string> mSearchContexts;
/// @see RsSerializable::serial_process
void serial_process( RsGenericSerializer::SerializeJob j,
RsGenericSerializer::SerializeContext& ctx )
{
RS_SERIAL_PROCESS(mGroupId);
RS_SERIAL_PROCESS(mGroupName);
RS_SERIAL_PROCESS(mAuthorId);
RS_SERIAL_PROCESS(mPublishTs);
RS_SERIAL_PROCESS(mNumberOfMessages);
RS_SERIAL_PROCESS(mLastMessageTs);
RS_SERIAL_PROCESS(mSignFlags);
RS_SERIAL_PROCESS(mPopularity);
RS_SERIAL_PROCESS(mSearchContexts);
}
virtual ~RsGxsGroupSearchResults() = default;
};
/*!
* Stores ids of changed gxs groups and messages.

View File

@ -507,25 +507,27 @@ private:
std::map<uint32_t,TokenRequestType> mActiveTokens;
#ifdef DEBUG_GXSIFACEHELPER
void locked_dumpTokens()
{
const uint16_t service_id = mGxs.serviceType();
const auto countSize = static_cast<size_t>(TokenRequestType::__MAX);
uint32_t count[countSize] = {0};
RsDbg() << __PRETTY_FUNCTION__ << "Service 0x" << std::hex << service_id
<< " (" << rsServiceControl->getServiceName(
RsServiceInfo::RsServiceInfoUIn16ToFullServiceId(service_id) )
<< ") this=0x" << static_cast<void*>(this)
<< ") Active tokens (per type): ";
RsDbg rsdbg;
rsdbg << __PRETTY_FUNCTION__ << " Service 0x" << std::hex << service_id
<< " (" << rsServiceControl->getServiceName(
RsServiceInfo::RsServiceInfoUIn16ToFullServiceId(service_id) )
<< ") this=0x" << static_cast<void*>(this)
<< ") Active tokens (per type): ";
// let's count how many token of each type we've got.
for(auto& it: mActiveTokens) ++count[static_cast<size_t>(it.second)];
for(uint32_t i=0; i < countSize; ++i)
RsDbg().uStream() /* << i << ":" */ << count[i] << " ";
RsDbg().uStream() << std::endl;
rsdbg /* << i << ":" */ << count[i] << " ";
}
#endif // def DEBUG_GXSIFACEHELPER
RS_SET_CONTEXT_DEBUG_LEVEL(1)
};

View File

@ -234,31 +234,30 @@ struct RsIdentityUsage : RsSerializable
GXS_TUNNEL_DH_SIGNATURE_CHECK = 0x0c,
GXS_TUNNEL_DH_SIGNATURE_CREATION = 0x0d,
/// Identity received through GXS sync
IDENTITY_NEW_FROM_GXS_SYNC = 0x0e,
/// Group update on that identity data. Can be avatar, name, etc.
IDENTITY_DATA_UPDATE = 0x0e,
IDENTITY_NEW_FROM_DISCOVERY = 0x0f,
/// Explicit request to friend
IDENTITY_NEW_FROM_EXPLICIT_REQUEST = 0x10,
/// Any signature verified for that identity
IDENTITY_GENERIC_SIGNATURE_CHECK = 0x0f,
IDENTITY_GENERIC_SIGNATURE_CHECK = 0x11,
/// Any signature made by that identity
IDENTITY_GENERIC_SIGNATURE_CREATION = 0x10,
IDENTITY_GENERIC_SIGNATURE_CREATION = 0x12,
IDENTITY_GENERIC_ENCRYPTION = 0x11,
IDENTITY_GENERIC_DECRYPTION = 0x12,
CIRCLE_MEMBERSHIP_CHECK = 0x13
IDENTITY_GENERIC_ENCRYPTION = 0x13,
IDENTITY_GENERIC_DECRYPTION = 0x14,
CIRCLE_MEMBERSHIP_CHECK = 0x15
} ;
RS_DEPRECATED
RsIdentityUsage( uint16_t service, const RsIdentityUsage::UsageCode& code,
const RsGxsGroupId& gid = RsGxsGroupId(),
const RsGxsMessageId& mid = RsGxsMessageId(),
uint64_t additional_id=0,
const std::string& comment = std::string() );
RsIdentityUsage( RsServiceType service,
RsIdentityUsage::UsageCode code,
const RsGxsGroupId& gid = RsGxsGroupId(),
const RsGxsMessageId& mid = RsGxsMessageId(),
const RsGxsMessageId& message_id = RsGxsMessageId(),
const RsGxsMessageId& parent_id = RsGxsMessageId(),
const RsGxsMessageId& thread_id = RsGxsMessageId(),
uint64_t additional_id=0,
const std::string& comment = std::string() );
@ -275,6 +274,12 @@ struct RsIdentityUsage : RsSerializable
/// Message ID using the identity
RsGxsMessageId mMsgId;
/// Reference message ID. Useful for votes/comments
RsGxsMessageId mParentId;
/// Reference message ID. Useful for votes/comments
RsGxsMessageId mThreadId;
/// Some additional ID. Can be used for e.g. chat lobbies.
uint64_t mAdditionalId;

View File

@ -20,8 +20,7 @@
*******************************************************************************/
#pragma once
/// RetroShare initialization and login API
/// @file RetroShare initialization and login API
// Initialize ok, result >= 0
#define RS_INIT_OK 0 // Initialize ok
@ -32,11 +31,15 @@
#define RS_INIT_NO_KEYRING -3 // Keyring is empty. Need to import it.
#define RS_INIT_NO_EXECUTABLE -4 // executable path hasn't been set in config options
#include <stdint.h>
#include <list>
#include <map>
#include <vector>
#include <retroshare/rstypes.h>
#include <cstdint>
#include <system_error>
#include "retroshare/rstypes.h"
#include "retroshare/rsversion.h"
class RsLoginHelper;
@ -46,6 +49,71 @@ class RsLoginHelper;
*/
extern RsLoginHelper* rsLoginHelper;
enum class RsInitErrorNum : int32_t
{
ALREADY_LOGGED_IN = 6000,
CANT_ACQUIRE_LOCK = 6001,
INVALID_LOCATION_NAME = 6002,
PGP_NAME_OR_ID_NEEDED = 6003,
PGP_KEY_CREATION_FAILED = 6004,
SSL_KEY_CREATION_FAILED = 6005,
INVALID_SSL_ID = 6006,
LOGIN_FAILED = 6007
};
struct RsInitErrorCategory: std::error_category
{
const char* name() const noexcept override
{ return "RetroShare init"; }
std::string message(int ev) const override
{
switch (static_cast<RsInitErrorNum>(ev))
{
case RsInitErrorNum::ALREADY_LOGGED_IN:
return "Already logged in";
case RsInitErrorNum::CANT_ACQUIRE_LOCK:
return "Cannot aquire lock on location data. Another instance is "
"already running with this profile?";
case RsInitErrorNum::INVALID_LOCATION_NAME:
return "Invalid location name";
case RsInitErrorNum::PGP_NAME_OR_ID_NEEDED:
return "Either PGP name or PGP id is needed";
case RsInitErrorNum::PGP_KEY_CREATION_FAILED:
return "Failure creating PGP key";
case RsInitErrorNum::SSL_KEY_CREATION_FAILED:
return "Failure creating SSL key";
case RsInitErrorNum::INVALID_SSL_ID:
return "Invalid SSL id";
case RsInitErrorNum::LOGIN_FAILED:
return "Generic login failure";
default:
return rsErrorNotInCategory(ev, name());
}
}
const static RsInitErrorCategory instance;
};
namespace std
{
/** Register RsJsonApiErrorNum as an error condition enum, must be in std
* namespace */
template<> struct is_error_condition_enum<RsInitErrorNum> : true_type {};
}
/** Provide RsInitErrorNum conversion to std::error_condition, must be in
* same namespace of RsInitErrorNum */
inline std::error_condition make_error_condition(RsInitErrorNum e) noexcept
{
return std::error_condition(
static_cast<int>(e), RsInitErrorCategory::instance );
};
/**
* @brief The RsInitConfig struct
* This class contains common configuration options, that executables using libretroshare may want to
@ -85,7 +153,7 @@ struct RsConfigOptions
class RsInit
{
public:
enum LoadCertificateStatus : uint8_t
enum RS_DEPRECATED_FOR(RsInitErrorNum) LoadCertificateStatus : uint8_t
{
OK, /// Everything go as expected, no error occurred
ERR_ALREADY_RUNNING, /// Another istance is running already
@ -317,7 +385,7 @@ public:
/**
* @brief Normal way to attempt login
* @jsonapi{development,manualwrapper}
* @jsonapi{development,unauthenticated}
* @param[in] account Id of the account to which attempt login
* @param[in] password Password for the given account
* @return RsInit::OK if login attempt success, error code otherwhise
@ -353,6 +421,44 @@ public:
void getLocations(std::vector<RsLoginHelper::Location>& locations);
/**
* @brief Creates a new RetroShare location, and log in once is created
* @jsonapi{development,manualwrapper}
* @param[out] locationId storage for generated location SSL id
* @param[inout] pgpId specify PGP id to use to sign the location, if a null
* id is passed the PGP key is created too and this param is used as
* storage for its id.
* @param[in] password to protect and unlock the associated PGP key
* param[in] apiUser (JSON API only) string containing username for JSON API
* so it can be later used to authenticate JSON API calls. It is passed
* down to @see RsJsonApi::authorizeUser under the hood.
* param[in] apiPass (JSON API only) string containing password for JSON API
* so it can be later used to authenticate JSON API calls. It is passed
* down to @see RsJsonApi::authorizeUser under the hood.
* To improve security we strongly advise to not use the same as the
* password used for the PGP key.
* @return Success or error information
*/
std::error_condition createLocationV2(
RsPeerId& locationId,
RsPgpId& pgpId,
const std::string& locationName,
const std::string& pgpName,
const std::string& password
/* JSON API only
* const std::string& apiUser
* const std::string& apiPass */ );
/**
* @brief Check if RetroShare is already logged in, this usually return true
* after a successfull attemptLogin() and before closeSession()
* @jsonapi{development,unauthenticated}
* @return true if already logged in, false otherwise
*/
bool isLoggedIn();
#if !RS_VERSION_AT_LEAST(0,6,6)
/**
* @deprecated Use @see createLocationV2 instead
* @brief Creates a new RetroShare location, and log in once is created
* @jsonapi{development,manualwrapper}
* @param[inout] location provide input information to generate the location
@ -365,15 +471,9 @@ public:
* Tor hidden location. UNTESTED!
* @return true if success, false otherwise
*/
RS_DEPRECATED_FOR(createLocationV2)
bool createLocation( RsLoginHelper::Location& location,
const std::string& password, std::string& errorMessage,
bool makeHidden = false, bool makeAutoTor = false );
/**
* @brief Check if RetroShare is already logged in, this usually return true
* after a successfull attemptLogin() and before closeSession()
* @jsonapi{development,unauthenticated}
* @return true if already logged in, false otherwise
*/
bool isLoggedIn();
#endif // !RS_VERSION_AT_LEAST(0,6,6)
};

View File

@ -29,6 +29,7 @@
#include <cstdint>
#include <system_error>
#include "util/rsdebug.h"
#include "util/rsmemory.h"
class RsJsonApi;
@ -74,8 +75,7 @@ struct RsJsonApiErrorCategory: std::error_category
case RsJsonApiErrorNum::NOT_A_MACHINE_GUN:
return "Method must not be called in burst";
default:
return "Error message for error: " + std::to_string(ev) +
" not available in category: " + name();
return rsErrorNotInCategory(ev, name());
}
}

View File

@ -106,12 +106,22 @@ public:
RsPeerId peerID;
};
struct RsPeerUpdateTsRecord
{
RsPeerUpdateTsRecord() : mLastTsReceived(0), mTs(0) {}
rstime_t mLastTsReceived; // last TS that was sent for this group by this peer ID.
rstime_t mTs; // time at which this TS was sent.
};
class RsGxsServerGrpUpdate
{
public:
RsGxsServerGrpUpdate() { grpUpdateTS = 0 ; }
uint32_t grpUpdateTS;
std::map<RsPeerId,RsPeerUpdateTsRecord> grpUpdateTsRecords;
};
class RsGxsServerGrpUpdateItem : public RsGxsNetServiceItem, public RsGxsServerGrpUpdate
@ -168,7 +178,13 @@ class RsGxsServerMsgUpdate
public:
RsGxsServerMsgUpdate() { msgUpdateTS = 0 ;}
uint32_t msgUpdateTS; // local time stamp this group last received a new msg
uint32_t msgUpdateTS; // local time stamp at which this group last received a new msg
// Now we also store for each peer the last own TS the peer sent and when it did so. This allows to detect when transactions are stuck because of
// outqueues clogging. If that happens, we receive multiple times the same TS from the friend, in which case we do not send the list of msgs
// again until a significant amount of time has passed. These values are obviously initialized to 0.
std::map<RsPeerId, RsPeerUpdateTsRecord> msgUpdateTsRecords;
};
class RsGxsServerMsgUpdateItem : public RsGxsNetServiceItem, public RsGxsServerMsgUpdate

View File

@ -143,13 +143,14 @@ void RsServer::threadTick()
// if there is time left, we sleep
double timeToSleep = mTickInterval - mAvgRunDuration;
if (timeToSleep > 0)
{
// never sleep less than 50 ms
if (timeToSleep < 0.050)
timeToSleep = 0.050;
#ifdef TICK_DEBUG
RsDbg() << "TICK_DEBUG will sleep " << timeToSleep << " ms" << std::endl;
RsDbg() << "TICK_DEBUG will sleep " << (int) (1000 * timeToSleep) << " ms" << std::endl;
#endif
rstime::rs_usleep(timeToSleep * 1000000);
}
rstime::rs_usleep(timeToSleep * 1000000);
double ts = getCurrentTS();
mLastts = ts;
@ -229,12 +230,16 @@ void RsServer::threadTick()
// ticking is done, now compute new values of mLastRunDuration, mAvgRunDuration and mTickInterval
ts = getCurrentTS();
mLastRunDuration = ts - mLastts;
// low-pass filter and don't let mAvgRunDuration exceeds maxTickInterval
mAvgRunDuration = 0.1 * mLastRunDuration + 0.9 * mAvgRunDuration;
if (mAvgRunDuration > maxTickInterval)
mAvgRunDuration = maxTickInterval;
#ifdef TICK_DEBUG
RsDbg() << "TICK_DEBUG new mLastRunDuration " << mLastRunDuration << " mAvgRunDuration " << mAvgRunDuration << std::endl;
if (mLastRunDuration > WARN_BIG_CYCLE_TIME)
RsDbg() << "TICK_DEBUG excessively long lycle time " << mLastRunDuration << std::endl;
RsDbg() << "TICK_DEBUG excessively long cycle time " << mLastRunDuration << std::endl;
#endif
// if the core has returned that there is more to tick we decrease the ticking interval, else we increse it
@ -250,7 +255,7 @@ void RsServer::threadTick()
RsDbg() << "TICK_DEBUG new tick interval " << mTickInterval << std::endl;
#endif
// keep the tick interval within allowed limits
// keep the tick interval target within allowed limits
if (mTickInterval < minTickInterval)
mTickInterval = minTickInterval;
else if (mTickInterval > maxTickInterval)

View File

@ -161,7 +161,9 @@ public:
p3ChatService *chatSrv;
p3StatusService *mStatusSrv;
p3GxsTunnelService *mGxsTunnels;
#ifdef RS_USE_I2P_BOB
p3I2pBob *mI2pBob;
#endif
// This list contains all threaded services. It will be used to shut them down properly.

View File

@ -114,6 +114,8 @@ RsLoginHelper* rsLoginHelper = nullptr;
RsAccounts* rsAccounts = nullptr;
const RsInitErrorCategory RsInitErrorCategory::instance;
RsConfigOptions::RsConfigOptions()
:
autoLogin(false),
@ -921,8 +923,10 @@ int RsServer::StartupRetroShare()
mNetMgr->setManagers(mPeerMgr, mLinkMgr);
rsAutoProxyMonitor *autoProxy = rsAutoProxyMonitor::instance();
#ifdef RS_USE_I2P_BOB
mI2pBob = new p3I2pBob(mPeerMgr);
autoProxy->addProxy(autoProxyType::I2PBOB, mI2pBob);
#endif
//load all the SSL certs as friends
// std::list<std::string> sslIds;
@ -1647,7 +1651,9 @@ int RsServer::StartupRetroShare()
mConfigMgr->addConfiguration("wire.cfg", wire_ns);
#endif
#endif //RS_ENABLE_GXS
#ifdef RS_USE_I2P_BOB
mConfigMgr->addConfiguration("I2PBOB.cfg", mI2pBob);
#endif
mPluginsManager->addConfigurations(mConfigMgr) ;
@ -1722,7 +1728,7 @@ int RsServer::StartupRetroShare()
// now enable bob
bobSettings bs;
autoProxy->taskSync(autoProxyType::I2PBOB, autoProxyTask::getSettings, &bs);
bs.enableBob = true;
bs.enable = true;
autoProxy->taskSync(autoProxyType::I2PBOB, autoProxyTask::setSettings, &bs);
} else {
std::cerr << "RsServer::StartupRetroShare failed to receive keys" << std::endl;
@ -1793,7 +1799,9 @@ int RsServer::StartupRetroShare()
/**************************************************************************/
// auto proxy threads
#ifdef RS_USE_I2P_BOB
startServiceThread(mI2pBob, "I2P-BOB");
#endif
#ifdef RS_ENABLE_GXS
// Must Set the GXS pointers before starting threads.
@ -1950,6 +1958,47 @@ void RsLoginHelper::getLocations(std::vector<RsLoginHelper::Location>& store)
}
}
std::error_condition RsLoginHelper::createLocationV2(
RsPeerId& locationId, RsPgpId& pgpId,
const std::string& locationName, const std::string& pgpName,
const std::string& password )
{
if(isLoggedIn()) return RsInitErrorNum::ALREADY_LOGGED_IN;
if(locationName.empty()) return RsInitErrorNum::INVALID_LOCATION_NAME;
if(pgpId.isNull() && pgpName.empty())
return RsInitErrorNum::PGP_NAME_OR_ID_NEEDED;
std::string errorMessage;
if(pgpId.isNull() && !RsAccounts::GeneratePGPCertificate(
pgpName, "", password, pgpId, 4096, errorMessage ) )
{
RS_ERR("Failure creating PGP key: ", errorMessage);
return RsInitErrorNum::PGP_KEY_CREATION_FAILED;
}
std::string sslPassword =
RsRandom::random_alphaNumericString(RsInit::getSslPwdLen());
rsNotify->cachePgpPassphrase(password);
rsNotify->setDisableAskPassword(true);
bool ret = RsAccounts::createNewAccount(
pgpId, "", locationName, "", false, false, sslPassword,
locationId, errorMessage );
if(!ret)
{
RS_ERR("Failure creating SSL key: ", errorMessage);
return RsInitErrorNum::SSL_KEY_CREATION_FAILED;
}
RsInit::LoadPassword(sslPassword);
ret = (RsInit::OK == attemptLogin(locationId, password));
rsNotify->setDisableAskPassword(false);
return (ret ? std::error_condition() : RsInitErrorNum::LOGIN_FAILED);
}
#if !RS_VERSION_AT_LEAST(0,6,6)
bool RsLoginHelper::createLocation(
RsLoginHelper::Location& l, const std::string& password,
std::string& errorMessage, bool makeHidden, bool makeAutoTor )
@ -1991,6 +2040,7 @@ bool RsLoginHelper::createLocation(
rsNotify->setDisableAskPassword(false);
return ret;
}
#endif // !RS_VERSION_AT_LEAST(0,6,6)
bool RsLoginHelper::isLoggedIn()
{

View File

@ -26,6 +26,7 @@
#include "rsloginhandler.h"
#include "util/rsdir.h"
#include "retroshare/rsinit.h"
#include "util/rsdebug.h"
//#define DEBUG_RSLOGINHANDLER 1
@ -497,8 +498,15 @@ bool RsLoginHandler::enableAutoLogin(const RsPeerId& ssl_id,const std::string& s
NULL);
if (error) {
RsErr() << __PRETTY_FUNCTION__
<< " Could not store passwd using libsecret with"
<< " error.code=" << error->code
<< " error.domain=" << error->domain
<< " error.message=\"" << error->message << "\"" << std::endl;
if (error->code == 2)
RsErr() << "Do have a key wallet installed?" << std::endl
<< "Like gnome-keyring or other using \"Secret Service\" by DBus." << std::endl;
g_error_free (error);
std::cerr << "Could not store passwd using libsecret" << std::endl;
return false;
}
std::cout << "Stored passwd " << "************************" << " using libsecret" << std::endl;

View File

@ -235,8 +235,7 @@ template<> bool RsTypeSerializer::from_JSON( \
\
if(!ret) \
{ \
Dbg3() << __PRETTY_FUNCTION__ << " " << memberName << " not found" \
<< std::endl; \
RS_DBG3(memberName, " not found"); \
return false; \
} \
\

View File

@ -39,7 +39,7 @@
#include "serialiser/rsserializer.h"
#include "serialiser/rsserializable.h"
#include "util/rsjson.h"
#include "util/rsdebug.h"
#include "util/rsdebuglevel1.h"
#include "util/cxx14retrocompat.h"
@ -715,12 +715,9 @@ struct RsTypeSerializer
E& member,
const std::string& memberName )
{
#ifdef RSSERIAL_DEBUG
std::cerr << __PRETTY_FUNCTION__ << " processing enum: "
<< typeid(E).name() << " as "
<< typeid(typename std::underlying_type<E>::type).name()
<< std::endl;
#endif
RS_DBG4( "processing enum: ", typeid(E).name(), " as ",
typeid(typename std::underlying_type<E>::type).name() );
serial_process(
j, ctx,
reinterpret_cast<typename std::underlying_type<E>::type&>(member),
@ -1004,14 +1001,16 @@ protected:
uint8_t data[], uint32_t size, uint32_t &offset, T member )
{
std::decay_t<T> backupMember = member;
#if RS_DEBUG_LEVEL >= 3
uint32_t offsetBackup = offset;
#endif
bool ok = true;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wbool-compare"
/* Check with < and not with <= here as we write last byte after
* the loop. Order of && operands very important here! */
while(member > 127 && (ok = offset < size))
while(member > 127 && (ok = (offset < size)))
{
// | 128: Set the next byte flag
data[offset++] = (static_cast<uint8_t>(member & 127)) | 128;
@ -1036,13 +1035,13 @@ protected:
data[offset++] = static_cast<uint8_t>(member & 127);
Dbg3() << __PRETTY_FUNCTION__ << " backupMember: " << backupMember
<< " offsetBackup: " << offsetBackup << " offeset: " << offset
<< " serialized as: ";
#if RS_DEBUG_LEVEL >= 3
RsDbg tdbg( __PRETTY_FUNCTION__, " backupMember: ", backupMember,
" offsetBackup: ", offsetBackup, " offeset: ", offset,
" serialized as: " );
for(; offsetBackup < offset; ++offsetBackup)
Dbg3().uStream() << " " << std::bitset<8>(data[offsetBackup]);
Dbg3().uStream() << std::endl;
tdbg << " " << std::bitset<8>(data[offsetBackup]);
#endif
return ok;
}
@ -1082,13 +1081,13 @@ protected:
/* If return is not triggered inside the for loop, either the buffer
* ended before we encountered the end of the number, or the number
* is VLQ encoded improperly */
RsErr() << __PRETTY_FUNCTION__ << std::errc::illegal_byte_sequence
<< " size: " << size
<< " offsetBackup: " << offsetBackup
<< " offset: " << offset << " bytes: ";
RsErr rserr;
rserr << __PRETTY_FUNCTION__ << std::errc::illegal_byte_sequence
<< " size: " << size
<< " offsetBackup: " << offsetBackup
<< " offset: " << offset << " bytes: ";
for(; offsetBackup < offset; ++offsetBackup)
RsErr().uStream() << " " << std::bitset<8>(data[offsetBackup]);
RsErr().uStream() << std::endl;
rserr << " " << std::bitset<8>(data[offsetBackup]);
print_stacktrace();
return false;
@ -1151,7 +1150,7 @@ protected:
struct ErrConditionWrapper : RsSerializable
{
ErrConditionWrapper(const std::error_condition& ec): mec(ec) {}
explicit ErrConditionWrapper(const std::error_condition& ec): mec(ec) {}
/** supports only TO_JSON if a different SerializeJob is passed it will
* explode at runtime */

View File

@ -43,21 +43,14 @@ static const std::string kConfigKeyOutLength = "OUT_LENGTH";
static const std::string kConfigKeyOutQuantity = "OUT_QUANTITY";
static const std::string kConfigKeyOutVariance = "OUT_VARIANCE";
static const bool kDefaultBOBEnable = false;
static const int8_t kDefaultLength = 3;
static const int8_t kDefaultQuantity = 4;
static const int8_t kDefaultVariance = 0;
/// Sleep duration for receiving loop
static const useconds_t sleepTimeRecv = 10; // times 1000 = 10ms
/// Sleep duration for receiving loop in error/no-data case
static const useconds_t sleepTimeRecv = 250; // times 1000 = 250ms
/// Sleep duration for everything else
static const useconds_t sleepTimeWait = 50; // times 1000 = 50ms or 0.05s
static const int sleepFactorDefault = 10; // 0.5s
static const int sleepFactorFast = 1; // 0.05s
static const int sleepFactorSlow = 20; // 1s
static struct RsLog::logInfo i2pBobLogInfo = {RsLog::Default, "p3I2pBob"};
static const rstime_t selfCheckPeroid = 30;
void doSleep(useconds_t timeToSleepMS) {
@ -74,15 +67,7 @@ p3I2pBob::p3I2pBob(p3PeerMgr *peerMgr)
mProcessing(NULL), mLock("I2P-BOB")
{
// set defaults
mSetting.enableBob = kDefaultBOBEnable;
mSetting.keys = "";
mSetting.addr = "";
mSetting.inLength = kDefaultLength;
mSetting.inQuantity = kDefaultQuantity;
mSetting.inVariance = kDefaultVariance;
mSetting.outLength = kDefaultLength;
mSetting.outQuantity = kDefaultQuantity;
mSetting.outVariance = kDefaultVariance;
mSetting.initDefault();
mCommands.clear();
}
@ -90,12 +75,12 @@ p3I2pBob::p3I2pBob(p3PeerMgr *peerMgr)
bool p3I2pBob::isEnabled()
{
RS_STACK_MUTEX(mLock);
return mSetting.enableBob;
return mSetting.enable;
}
bool p3I2pBob::initialSetup(std::string &addr, uint16_t &/*port*/)
{
std::cout << "p3I2pBob::initialSetup" << std::endl;
RS_DBG("");
// update config
{
@ -108,7 +93,7 @@ bool p3I2pBob::initialSetup(std::string &addr, uint16_t &/*port*/)
}
}
std::cout << "p3I2pBob::initialSetup config updated" << std::endl;
RS_DBG("config updated");
// request keys
// p3I2pBob::stateMachineBOB expects mProcessing to be set therefore
@ -118,12 +103,12 @@ bool p3I2pBob::initialSetup(std::string &addr, uint16_t &/*port*/)
fakeTicket->task = autoProxyTask::receiveKey;
processTaskAsync(fakeTicket);
std::cout << "p3I2pBob::initialSetup fakeTicket requested" << std::endl;
RS_DBG("fakeTicket requested");
// now start thread
start("I2P-BOB gen key");
std::cout << "p3I2pBob::initialSetup thread started" << std::endl;
RS_DBG("thread started");
int counter = 0;
// wait for keys
@ -137,24 +122,24 @@ bool p3I2pBob::initialSetup(std::string &addr, uint16_t &/*port*/)
break;
if (++counter > 30) {
std::cout << "p3I2pBob::initialSetup timeout!" << std::endl;
RS_DBG4("timeout!");
return false;
}
}
std::cout << "p3I2pBob::initialSetup got keys" << std::endl;
RS_DBG("got keys");
// stop thread
fullstop();
std::cout << "p3I2pBob::initialSetup thread stopped" << std::endl;
RS_DBG("thread stopped");
{
RS_STACK_MUTEX(mLock);
addr = mSetting.addr;
addr = mSetting.address.base32;
}
std::cout << "p3I2pBob::initialSetup addr '" << addr << "'" << std::endl;
RS_DBG4("addr ", addr);
return true;
}
@ -172,7 +157,7 @@ void p3I2pBob::processTaskAsync(taskTicket *ticket)
}
break;
default:
rslog(RsLog::Warning, &i2pBobLogInfo, "p3I2pBob::processTaskAsync unknown task");
RS_DBG("unknown task");
rsAutoProxyMonitor::taskError(ticket);
break;
}
@ -187,7 +172,7 @@ void p3I2pBob::processTaskSync(taskTicket *ticket)
case autoProxyTask::status:
// check if everything needed is set
if (!data) {
rslog(RsLog::Warning, &i2pBobLogInfo, "p3I2pBob::status autoProxyTask::status data is missing");
RS_DBG("autoProxyTask::status data is missing");
rsAutoProxyMonitor::taskError(ticket);
break;
}
@ -201,7 +186,7 @@ void p3I2pBob::processTaskSync(taskTicket *ticket)
case autoProxyTask::getSettings:
// check if everything needed is set
if (!data) {
rslog(RsLog::Warning, &i2pBobLogInfo, "p3I2pBob::data_tick autoProxyTask::getSettings data is missing");
RS_DBG("autoProxyTask::getSettings data is missing");
rsAutoProxyMonitor::taskError(ticket);
break;
}
@ -215,7 +200,7 @@ void p3I2pBob::processTaskSync(taskTicket *ticket)
case autoProxyTask::setSettings:
// check if everything needed is set
if (!data) {
rslog(RsLog::Warning, &i2pBobLogInfo, "p3I2pBob::data_tick autoProxyTask::setSettings data is missing");
RS_DBG("autoProxyTask::setSettings data is missing");
rsAutoProxyMonitor::taskError(ticket);
break;
}
@ -235,7 +220,7 @@ void p3I2pBob::processTaskSync(taskTicket *ticket)
break;
case autoProxyTask::getErrorInfo:
if (!data) {
rslog(RsLog::Warning, &i2pBobLogInfo, "p3I2pBob::data_tick autoProxyTask::getErrorInfo data is missing");
RS_DBG("autoProxyTask::getErrorInfo data is missing");
rsAutoProxyMonitor::taskError(ticket);
} else {
RS_STACK_MUTEX(mLock);
@ -244,34 +229,12 @@ void p3I2pBob::processTaskSync(taskTicket *ticket)
}
break;
default:
rslog(RsLog::Warning, &i2pBobLogInfo, "p3I2pBob::processTaskSync unknown task");
RS_DBG("unknown task");
rsAutoProxyMonitor::taskError(ticket);
break;
}
}
std::string p3I2pBob::keyToBase32Addr(const std::string &key)
{
std::string copy(key);
// replace I2P specific chars
std::replace(copy.begin(), copy.end(), '~', '/');
std::replace(copy.begin(), copy.end(), '-', '+');
// decode
std::vector<uint8_t> bin = Radix64::decode(copy);
// hash
std::vector<uint8_t> sha256 = RsUtil::BinToSha256(bin);
// encode
std::string out = Radix32::encode(sha256);
// i2p uses lowercase
std::transform(out.begin(), out.end(), out.begin(), ::tolower);
out.append(".b32.i2p");
return out;
}
bool inline isAnswerOk(const std::string &answer) {
return (answer.compare(0, 2, "OK") == 0);
}
@ -284,10 +247,8 @@ void p3I2pBob::threadTick()
{
int sleepTime = 0;
{
RS_STACK_MUTEX(mLock);
std::stringstream ss;
ss << "data_tick mState: " << mState << " mTask: " << mTask << " mBOBState: " << mBOBState << " mPending: " << mPending.size();
rslog(RsLog::Debug_All, &i2pBobLogInfo, ss.str());
RS_STACK_MUTEX(mLock);
RS_DBG4("data_tick mState: ", mState, " mTask: ", mTask, " mBOBState: ", mBOBState, " mPending: ", mPending.size());
}
sleepTime += stateMachineController();
@ -326,15 +287,13 @@ int p3I2pBob::stateMachineBOB()
if (mBOBState == bsList) {
int counter = 0;
while (answer.find("OK Listing done") == std::string::npos) {
std::stringstream ss;
ss << "stateMachineBOB status check: read loop, counter: " << counter;
rslog(RsLog::Debug_Basic, &i2pBobLogInfo, ss.str());
RS_DBG3("stateMachineBOB status check: read loop, counter: ", counter);
answer += recv();
counter++;
}
if (answer.find(mTunnelName) == std::string::npos) {
rslog(RsLog::Warning, &i2pBobLogInfo, "stateMachineBOB status check: tunnel down!");
RS_DBG("status check: tunnel down!");
// signal error
*((bool *)mProcessing->data) = true;
}
@ -346,12 +305,12 @@ int p3I2pBob::stateMachineBOB()
switch (mBOBState) {
case bsNewkeysN:
key = answer.substr(3, answer.length()-3);
mSetting.addr = keyToBase32Addr(key);
mSetting.address.base32 = i2p::keyToBase32Addr(key);
IndicateConfigChanged();
break;
case bsGetkeys:
key = answer.substr(3, answer.length()-3);
mSetting.keys = key;
mSetting.address.privateKey = key;
IndicateConfigChanged();
break;
default:
@ -374,8 +333,8 @@ int p3I2pBob::stateMachineBOB_locked_failure(const std::string &answer, const bo
return sleepFactorDefault;
}
rslog(RsLog::Warning, &i2pBobLogInfo, "stateMachineBOB FAILED to run command '" + currentState.command + "'");
rslog(RsLog::Warning, &i2pBobLogInfo, "stateMachineBOB '" + answer + "'");
RS_DBG("FAILED to run command: ", currentState.command);
RS_DBG("answer: ", answer);
mErrorMsg.append("FAILED to run command '" + currentState.command + "'" + '\n');
mErrorMsg.append("reason '" + answer + "'" + '\n');
@ -422,14 +381,14 @@ int p3I2pBob::stateMachineController()
return stateMachineController_locked_idle();
case csDoConnect:
if (!connectI2P()) {
rslog(RsLog::Warning, &i2pBobLogInfo, "stateMachineController doConnect: unable to connect");
RS_DBG("doConnect: unable to connect");
mStateOld = mState;
mState = csError;
mErrorMsg = "unable to connect to BOB port";
return sleepFactorSlow;
}
rslog(RsLog::Debug_Basic, &i2pBobLogInfo, "stateMachineController doConnect: connected");
RS_DBG4("doConnect: connected");
mState = csConnected;
break;
case csConnected:
@ -437,7 +396,7 @@ int p3I2pBob::stateMachineController()
case csWaitForBob:
// check connection problems
if (mSocket == 0) {
rslog(RsLog::Warning, &i2pBobLogInfo, "stateMachineController waitForBob: conection lost");
RS_DBG("waitForBob: conection lost");
mStateOld = mState;
mState = csError;
mErrorMsg = "connection lost to BOB";
@ -447,21 +406,21 @@ int p3I2pBob::stateMachineController()
// check for finished BOB protocol
if (mBOBState == bsCleared) {
// done
rslog(RsLog::Debug_Basic, &i2pBobLogInfo, "stateMachineController waitForBob: mBOBState == bsCleared");
RS_DBG4("waitForBob: mBOBState == bsCleared");
mState = csDoDisconnect;
}
break;
case csDoDisconnect:
if (!disconnectI2P() || mSocket != 0) {
// just in case
rslog(RsLog::Warning, &i2pBobLogInfo, "stateMachineController doDisconnect: can't disconnect");
RS_DBG("doDisconnect: can't disconnect");
mStateOld = mState;
mState = csError;
mErrorMsg = "unable to disconnect from BOB";
return sleepFactorDefault;
}
rslog(RsLog::Debug_Basic, &i2pBobLogInfo, "stateMachineController doDisconnect: disconnected");
RS_DBG4("doDisconnect: disconnected");
mState = csDisconnected;
break;
case csDisconnected:
@ -487,12 +446,12 @@ int p3I2pBob::stateMachineController_locked_idle()
mProcessing = mPending.front();
mPending.pop();
if (!mSetting.enableBob && (
if (!mSetting.enable && (
mProcessing->task == autoProxyTask::start ||
mProcessing->task == autoProxyTask::stop ||
mProcessing->task == autoProxyTask::proxyStatusCheck)) {
// skip since we are not enabled
rslog(RsLog::Debug_Alert, &i2pBobLogInfo, "stateMachineController_locked_idle: disabled -> skipping ticket");
RS_DBG1("disabled -> skipping ticket");
rsAutoProxyMonitor::taskDone(mProcessing, autoProxyStatus::disabled);
mProcessing = NULL;
} else {
@ -514,7 +473,7 @@ int p3I2pBob::stateMachineController_locked_idle()
mTask = ctRunCheck;
break;
default:
rslog(RsLog::Debug_Alert, &i2pBobLogInfo, "stateMachineController_locked_idle unknown async task");
RS_DBG1("unknown async task");
rsAutoProxyMonitor::taskError(mProcessing);
mProcessing = NULL;
break;
@ -561,29 +520,29 @@ int p3I2pBob::stateMachineController_locked_connected()
switch (mTask) {
case ctRunSetUp:
// when we have a key use it for server tunnel!
if(mSetting.keys.empty()) {
rslog(RsLog::Debug_Basic, &i2pBobLogInfo, "stateMachineController_locked_connected: setting mBOBState = setnickC");
if(mSetting.address.privateKey.empty()) {
RS_DBG4("setting mBOBState = setnickC");
mBOBState = bsSetnickC;
} else {
rslog(RsLog::Debug_Basic, &i2pBobLogInfo, "stateMachineController_locked_connected: setting mBOBState = setnickS");
RS_DBG4("setting mBOBState = setnickS");
mBOBState = bsSetnickS;
}
break;
case ctRunShutDown:
// shut down existing tunnel
rslog(RsLog::Debug_Basic, &i2pBobLogInfo, "stateMachineController_locked_connected: setting mBOBState = getnick");
RS_DBG4("setting mBOBState = getnick");
mBOBState = bsGetnick;
break;
case ctRunCheck:
rslog(RsLog::Debug_Basic, &i2pBobLogInfo, "stateMachineController_locked_connected: setting mBOBState = list");
RS_DBG4("setting mBOBState = list");
mBOBState = bsList;
break;
case ctRunGetKeys:
rslog(RsLog::Debug_Basic, &i2pBobLogInfo, "stateMachineController_locked_connected: setting mBOBState = setnickN");
RS_DBG4("setting mBOBState = setnickN");
mBOBState = bsSetnickN;
break;
case ctIdle:
rslog(RsLog::Warning, &i2pBobLogInfo, "stateMachineController_locked_connected: task is idle. This should not happen!");
RS_DBG("task is idle. This should not happen!");
break;
}
@ -599,7 +558,7 @@ int p3I2pBob::stateMachineController_locked_disconnected()
if(errorHappened) {
// reset old state
mStateOld = csIdel;
rslog(RsLog::Warning, &i2pBobLogInfo, "stateMachineController_locked_disconnected: error during process!");
RS_DBG("error during process!");
}
// answer ticket
@ -628,12 +587,12 @@ int p3I2pBob::stateMachineController_locked_disconnected()
mTask = mTaskOld;
if (!errorHappened) {
rslog(RsLog::Debug_Basic, &i2pBobLogInfo, "stateMachineController_locked_disconnected: run check result: ok");
RS_DBG4("run check result: ok");
break;
}
// switch to error
newState = csError;
rslog(RsLog::Warning, &i2pBobLogInfo, "stateMachineController_locked_disconnected: run check result: error");
RS_DBG("run check result: error");
mErrorMsg = "Connection check failed. Will try to restart tunnel.";
break;
@ -656,7 +615,7 @@ int p3I2pBob::stateMachineController_locked_disconnected()
mTask = mTaskOld;
break;
case ctIdle:
rslog(RsLog::Warning, &i2pBobLogInfo, "stateMachineController_locked_disconnected: task is idle. This should not happen!");
RS_DBG("task is idle. This should not happen!");
rsAutoProxyMonitor::taskError(mProcessing);
}
mProcessing = NULL;
@ -672,14 +631,12 @@ int p3I2pBob::stateMachineController_locked_error()
{
// wait for bob protocoll
if (mBOBState != bsCleared) {
rslog(RsLog::Debug_All, &i2pBobLogInfo, "stateMachineController_locked_error: waiting for BOB");
RS_DBG4("waiting for BOB");
return sleepFactorFast;
}
#if 0
std::stringstream ss;
ss << "stateMachineController_locked_error: mProcessing: " << (mProcessing ? "not null" : "null");
rslog(RsLog::Debug_All, &i2pBobLogInfo, ss.str());
RS_DBG4("stateMachineController_locked_error: mProcessing: ", (mProcessing ? "not null" : "null"));
#endif
// try to finish ticket
@ -687,7 +644,7 @@ int p3I2pBob::stateMachineController_locked_error()
switch (mTask) {
case ctRunCheck:
// connection check failed at some point
rslog(RsLog::Warning, &i2pBobLogInfo, "stateMachineController_locked_error: failed to check proxy status (it's likely dead)!");
RS_DBG("failed to check proxy status (it's likely dead)!");
*((bool *)mProcessing->data) = true;
mState = csDoDisconnect;
mStateOld = csIdel;
@ -695,7 +652,7 @@ int p3I2pBob::stateMachineController_locked_error()
break;
case ctRunShutDown:
// not a big deal though
rslog(RsLog::Warning, &i2pBobLogInfo, "stateMachineController_locked_error: failed to shut down tunnel (it's likely dead though)!");
RS_DBG("failed to shut down tunnel (it's likely dead though)!");
mState = csDoDisconnect;
mStateOld = csIdel;
mErrorMsg.clear();
@ -703,14 +660,14 @@ int p3I2pBob::stateMachineController_locked_error()
case ctIdle:
// should not happen but we need to deal with it
// this will produce some error messages in the log and finish the task (marked as failed)
rslog(RsLog::Warning, &i2pBobLogInfo, "stateMachineController_locked_error: task is idle. This should not happen!");
RS_DBG("task is idle. This should not happen!");
mState = csDoDisconnect;
mStateOld = csIdel;
mErrorMsg.clear();
break;
case ctRunGetKeys:
case ctRunSetUp:
rslog(RsLog::Warning, &i2pBobLogInfo, "stateMachineController_locked_error: failed to receive key / start up");
RS_DBG("failed to receive key / start up");
mStateOld = csError;
mState = csDoDisconnect;
// keep the error message
@ -721,7 +678,7 @@ int p3I2pBob::stateMachineController_locked_error()
// periodically retry
if (mLastProxyCheck < time(NULL) - (selfCheckPeroid >> 1) && mTask == ctRunSetUp) {
rslog(RsLog::Warning, &i2pBobLogInfo, "stateMachineController_locked_error: retrying");
RS_DBG("retrying");
mLastProxyCheck = time(NULL);
mErrorMsg.clear();
@ -734,7 +691,7 @@ int p3I2pBob::stateMachineController_locked_error()
// check for new tickets
if (!mPending.empty()) {
rslog(RsLog::Debug_Basic, &i2pBobLogInfo, "stateMachineController_locked_error: processing new ticket");
RS_DBG4("processing new ticket");
// reset and try new task
mTask = ctIdle;
@ -765,16 +722,16 @@ RsSerialiser *p3I2pBob::setupSerialiser()
bool p3I2pBob::saveList(bool &cleanup, std::list<RsItem *> &lst)
{
rslog(RsLog::Debug_Basic, &i2pBobLogInfo, "saveList");
RS_DBG4("");
cleanup = true;
RsConfigKeyValueSet *vitem = new RsConfigKeyValueSet;
RsTlvKeyValue kv;
RS_STACK_MUTEX(mLock);
addKVS(vitem, kv, kConfigKeyBOBEnable, mSetting.enableBob ? "TRUE" : "FALSE")
addKVS(vitem, kv, kConfigKeyBOBKey, mSetting.keys)
addKVS(vitem, kv, kConfigKeyBOBAddr, mSetting.addr)
addKVS(vitem, kv, kConfigKeyBOBEnable, mSetting.enable ? "TRUE" : "FALSE")
addKVS(vitem, kv, kConfigKeyBOBKey, mSetting.address.privateKey)
addKVS(vitem, kv, kConfigKeyBOBAddr, mSetting.address.base32)
addKVSInt(vitem, kv, kConfigKeyInLength, mSetting.inLength)
addKVSInt(vitem, kv, kConfigKeyInQuantity, mSetting.inQuantity)
addKVSInt(vitem, kv, kConfigKeyInVariance, mSetting.inVariance)
@ -800,7 +757,7 @@ bool p3I2pBob::saveList(bool &cleanup, std::list<RsItem *> &lst)
bool p3I2pBob::loadList(std::list<RsItem *> &load)
{
rslog(RsLog::Debug_Basic, &i2pBobLogInfo, "loadList");
RS_DBG4("");
for(std::list<RsItem*>::const_iterator it = load.begin(); it!=load.end(); ++it) {
RsConfigKeyValueSet *vitem = dynamic_cast<RsConfigKeyValueSet*>(*it);
@ -808,11 +765,11 @@ bool p3I2pBob::loadList(std::list<RsItem *> &load)
RS_STACK_MUTEX(mLock);
for(std::list<RsTlvKeyValue>::const_iterator kit = vitem->tlvkvs.pairs.begin(); kit != vitem->tlvkvs.pairs.end(); ++kit) {
if (kit->key == kConfigKeyBOBEnable)
mSetting.enableBob = kit->value == "TRUE";
mSetting.enable = kit->value == "TRUE";
else if (kit->key == kConfigKeyBOBKey)
mSetting.keys = kit->value;
mSetting.address.privateKey = kit->value;
else if (kit->key == kConfigKeyBOBAddr)
mSetting.addr = kit->value;
mSetting.address.base32 = kit->value;
getKVSUInt(kit, kConfigKeyInLength, mSetting.inLength)
getKVSUInt(kit, kConfigKeyInQuantity, mSetting.inQuantity)
getKVSUInt(kit, kConfigKeyInVariance, mSetting.inVariance)
@ -820,7 +777,7 @@ bool p3I2pBob::loadList(std::list<RsItem *> &load)
getKVSUInt(kit, kConfigKeyOutQuantity, mSetting.outQuantity)
getKVSUInt(kit, kConfigKeyOutVariance, mSetting.outVariance)
else
rslog(RsLog::Warning, &i2pBobLogInfo, "loadList unknown key: " + kit->key);
RS_DBG("unknown key: ", kit->key);
}
}
delete vitem;
@ -884,7 +841,7 @@ void p3I2pBob::getStates(bobStates *bs)
std::string p3I2pBob::executeCommand(const std::string &command)
{
rslog(RsLog::Debug_Basic, &i2pBobLogInfo, "executeCommand_locked running '" + command + "'");
RS_DBG4("running: ", command);
std::string copy = command;
copy.push_back('\n');
@ -896,7 +853,7 @@ std::string p3I2pBob::executeCommand(const std::string &command)
// receive answer (trailing new line is already removed!)
std::string ans = recv();
rslog(RsLog::Debug_Basic, &i2pBobLogInfo, "executeCommand_locked answer '" + ans + "'");
RS_DBG4("answer: ", ans);
return ans;
}
@ -906,7 +863,7 @@ bool p3I2pBob::connectI2P()
// there is only one thread that touches mSocket - no need for a lock
if (mSocket != 0) {
rslog(RsLog::Warning, &i2pBobLogInfo, "connectI2P_locked mSocket != 0");
RS_DBG("mSocket != 0");
return false;
}
@ -914,21 +871,21 @@ bool p3I2pBob::connectI2P()
mSocket = unix_socket(PF_INET, SOCK_STREAM, 0);
if (mSocket < 0)
{
rslog(RsLog::Warning, &i2pBobLogInfo, "connectI2P_locked Failed to open socket! Socket Error: " + socket_errorType(errno));
RS_DBG("Failed to open socket! Socket Error: ", socket_errorType(errno));
return false;
}
// connect
int err = unix_connect(mSocket, mI2PProxyAddr);
if (err != 0) {
rslog(RsLog::Warning, &i2pBobLogInfo, "connectI2P_locked Failed to connect to BOB! Socket Error: " + socket_errorType(errno));
RS_DBG("Failed to connect to BOB! Socket Error: ", socket_errorType(errno));
return false;
}
// receive hello msg
recv();
rslog(RsLog::Debug_Basic, &i2pBobLogInfo, "connectI2P_locked done");
RS_DBG4("done");
return true;
}
@ -937,17 +894,17 @@ bool p3I2pBob::disconnectI2P()
// there is only one thread that touches mSocket - no need for a lock
if (mSocket == 0) {
rslog(RsLog::Warning, &i2pBobLogInfo, "disconnectI2P_locked mSocket == 0");
RS_DBG("mSocket == 0");
return true;
}
int err = unix_close(mSocket);
if (err != 0) {
rslog(RsLog::Warning, &i2pBobLogInfo, "disconnectI2P_locked Failed to close socket! Socket Error: " + socket_errorType(errno));
RS_DBG("Failed to close socket! Socket Error: ", socket_errorType(errno));
return false;
}
rslog(RsLog::Debug_Basic, &i2pBobLogInfo, "disconnectI2P_locked done");
RS_DBG4("done");
mSocket = 0;
return true;
}
@ -968,7 +925,7 @@ std::string toString(const std::string &a, const int8_t b) {
void p3I2pBob::finalizeSettings_locked()
{
rslog(RsLog::Debug_Basic, &i2pBobLogInfo, "finalizeSettings_locked");
RS_DBG4("");
sockaddr_storage_clear(mI2PProxyAddr);
// get i2p proxy addr
@ -979,8 +936,8 @@ void p3I2pBob::finalizeSettings_locked()
sockaddr_storage_setipv4(mI2PProxyAddr, (sockaddr_in*)&proxy);
sockaddr_storage_setport(mI2PProxyAddr, 2827);
rslog(RsLog::Debug_Basic, &i2pBobLogInfo, "finalizeSettings_locked using " + sockaddr_storage_tostring(mI2PProxyAddr));
rslog(RsLog::Debug_Basic, &i2pBobLogInfo, "finalizeSettings_locked using " + mSetting.addr);
RS_DBG4("using ", mI2PProxyAddr);
RS_DBG4("using ", mSetting.address.base32);
peerState ps;
mPeerMgr->getOwnNetStatus(ps);
@ -988,21 +945,17 @@ void p3I2pBob::finalizeSettings_locked()
// setup commands
// new lines are appended later!
// generate random suffix for name
// RSRandom::random_alphaNumericString can return very weird looking strings like: ,,@z+M
// use base32 instead
size_t len = 5; // 5 characters = 8 base32 symbols
std::vector<uint8_t> tmp(len);
RSRandom::random_bytes(tmp.data(), len);
const std::string location = Radix32::encode(tmp.data(), len);
rslog(RsLog::Debug_Basic, &i2pBobLogInfo, "finalizeSettings_locked using suffix " + location);
// generate 8 characater long random suffix for name
constexpr size_t len = 8;
const std::string location = RsRandom::alphaNumeric(len);
RS_DBG4("using suffix ", location);
mTunnelName = "RetroShare-" + location;
const std::string setnick = "setnick RetroShare-" + location;
const std::string getnick = "getnick RetroShare-" + location;
const std::string newkeys = "newkeys";
const std::string getkeys = "getkeys";
const std::string setkeys = "setkeys " + mSetting.keys;
const std::string setkeys = "setkeys " + mSetting.address.privateKey;
const std::string inhost = "inhost " + sockaddr_storage_iptostring(proxy);
const std::string inport = toString("inport ", sockaddr_storage_port(proxy));
const std::string outhost = "outhost " + sockaddr_storage_iptostring(ps.localaddr);
@ -1063,7 +1016,7 @@ void p3I2pBob::finalizeSettings_locked()
void p3I2pBob::updateSettings_locked()
{
rslog(RsLog::Debug_Basic, &i2pBobLogInfo, "updateSettings_locked");
RS_DBG4("");
sockaddr_storage proxy;
mPeerMgr->getProxyServerAddress(RS_HIDDEN_TYPE_I2P, proxy);
@ -1071,7 +1024,7 @@ void p3I2pBob::updateSettings_locked()
peerState ps;
mPeerMgr->getOwnNetStatus(ps);
const std::string setkeys = "setkeys " + mSetting.keys;
const std::string setkeys = "setkeys " + mSetting.address.privateKey;
const std::string inhost = "inhost " + sockaddr_storage_iptostring(proxy);
const std::string inport = toString("inport ", sockaddr_storage_port(proxy));
const std::string outhost = "outhost " + sockaddr_storage_iptostring(ps.localaddr);
@ -1103,38 +1056,62 @@ void p3I2pBob::updateSettings_locked()
std::string p3I2pBob::recv()
{
// BOB works line based
// -> \n indicates and of the line
constexpr uint16_t bufferSize = 128;
char buffer[bufferSize];
std::string ans;
ssize_t length;
const uint16_t bufferSize = 128;
std::vector<char> buffer(bufferSize);
uint16_t retry = 10;
do {
doSleep(sleepTimeRecv);
memset(buffer, 0, bufferSize);
// there is only one thread that touches mSocket - no need for a lock
length = ::recv(mSocket, buffer.data(), buffer.size(), 0);
if (length < 0)
// peek at data
auto length = ::recv(mSocket, buffer, bufferSize, MSG_PEEK);
if (length <= 0) {
if (length < 0) {
// error
perror(__PRETTY_FUNCTION__);
}
retry--;
doSleep(sleepTimeRecv);
continue;
}
ans.append(buffer.begin(), buffer.end());
// at least one byte was read
// clean received string
ans.erase(std::remove(ans.begin(), ans.end(), '\0'), ans.end());
ans.erase(std::remove(ans.begin(), ans.end(), '\n'), ans.end());
// search for new line
auto bufferStr = std::string(buffer);
size_t pos = bufferStr.find('\n');
#if 0
std::stringstream ss;
ss << "recv length: " << length << " (bufferSize: " << bufferSize << ") ans: " << ans.length();
rslog(RsLog::Debug_All, &i2pBobLogInfo, ss.str());
#endif
if (pos == std::string::npos) {
// no new line found -> more to read
// clear and resize buffer again
buffer.clear();
buffer.resize(bufferSize);
// sanity check
if (length != bufferSize) {
// expectation: a full buffer was peeked)
RS_DBG1("peeked less than bufferSize but also didn't found a new line character");
}
// this should never happen
assert(length <= bufferSize);
} else {
// new line found -> end of message
if (this->shouldStop())
break;
} while(length == bufferSize || ans.size() < 4);
// calculate how much there is to read, read the \n, too!
length = pos + 1;
// end loop
retry = 0;
}
// now read for real
memset(buffer, 0, bufferSize);
length = ::recv(mSocket, buffer, length, 0);
bufferStr = std::string(buffer);
ans.append(bufferStr);
} while(retry > 0);
return ans;
}

View File

@ -30,9 +30,10 @@
#include <sys/socket.h>
#endif
#include "pqi/p3cfgmgr.h"
#include "services/autoproxy/rsautoproxymonitor.h"
#include "util/rsthreads.h"
#include "pqi/p3cfgmgr.h"
#include "util/i2pcommon.h"
/*
* This class implements I2P BOB (BASIC OPEN BRIDGE) communication to allow RS
@ -49,7 +50,7 @@
*
* Note 3:
* BOB needs a unique name as an ID for each tunnel.
* We use 'RetroShare-' + 8 base32 characters.
* We use 'RetroShare-' + 8 random base32 characters.
*
* Design:
* The service uses three state machines to manage its task:
@ -72,7 +73,7 @@
* mCommands[bobState::quit] = {quit, bobState::cleared};
*
* stateMachineController:
* This state machone manages the high level tasks.
* This state machine manages the high level tasks.
* It is controlled by mState and mTask.
*
* mTast:
@ -162,19 +163,7 @@ struct bobStateInfo {
bobState nextState;
};
struct bobSettings {
bool enableBob; ///< This field is used by the pqi subsystem to determinine whether SOCKS proxy or BOB is used for I2P connections
std::string keys; ///< (optional) server keys
std::string addr; ///< (optional) hidden service addr. in base32 form
int8_t inLength;
int8_t inQuantity;
int8_t inVariance;
int8_t outLength;
int8_t outQuantity;
int8_t outVariance;
};
struct bobSettings : i2p::settings {};
///
/// \brief The bobStates struct
@ -203,8 +192,6 @@ public:
void processTaskAsync(taskTicket *ticket);
void processTaskSync(taskTicket *ticket);
static std::string keyToBase32Addr(const std::string &key);
void threadTick() override; /// @see RsTickingThread
private:

View File

@ -22,6 +22,7 @@
#include "rsautoproxymonitor.h"
#include <unistd.h> /* for usleep() */
#include "util/rsdebug.h"
#include "util/rstime.h"
rsAutoProxyMonitor *rsAutoProxyMonitor::mInstance = NULL;
@ -42,8 +43,10 @@ rsAutoProxyMonitor *rsAutoProxyMonitor::instance()
void rsAutoProxyMonitor::addProxy(autoProxyType::autoProxyType_enum type, autoProxyService *service)
{
RS_STACK_MUTEX(mLock);
if (mProxies.find(type) != mProxies.end())
std::cerr << "sAutoProxyMonitor::addProxy type " << type << " already added - OVERWRITING" << std::endl;
if (mProxies.find(type) != mProxies.end()) {
RS_ERR("type ", type, " already added - OVERWRITING");
print_stacktrace();
}
mProxies[type] = service;
}
@ -117,7 +120,7 @@ void rsAutoProxyMonitor::stopAllRSShutdown()
do {
rstime::rs_usleep(1000 * 1000);
RS_STACK_MUTEX(mLock);
std::cout << "(II) waiting for auto proxy service(s) to shut down " << t << "/" << timeout << " (remaining: " << mProxies.size() << ")" << std::endl;
RS_DBG("waiting for auto proxy service(s) to shut down ", t, "/", timeout, " (remaining: ", mProxies.size(), ")");
if (mProxies.empty())
break;
t++;
@ -146,13 +149,16 @@ void rsAutoProxyMonitor::task(taskTicket *ticket)
{
// sanity checks
if (!ticket->async && ticket->types.size() > 1) {
std::cerr << "(WW) rsAutoProxyMonitor::task synchronous call to multiple services. This can cause problems!" << std::endl;
RS_ERR("synchronous call to multiple services. This can cause problems!");
print_stacktrace();
}
if (ticket->async && !ticket->cb && ticket->data) {
std::cerr << "(WW) rsAutoProxyMonitor::task asynchronous call with data but no callback. This will likely causes memory leak!" << std::endl;
RS_ERR("asynchronous call with data but no callback. This will likely causes memory leak!");
print_stacktrace();
}
if (ticket->types.size() > 1 && ticket->data) {
std::cerr << "(WW) rsAutoProxyMonitor::task call with data to multiple services. This will likely causes memory leak!" << std::endl;
RS_ERR("call with data to multiple services. This will likely causes memory leak!");
print_stacktrace();
}
std::vector<autoProxyType::autoProxyType_enum>::const_iterator it;
@ -168,7 +174,11 @@ void rsAutoProxyMonitor::task(taskTicket *ticket)
*tt = *ticket;
tt->types.clear();
tt->types.push_back(*it);
s->processTaskAsync(tt);
// it's async!
RsThread::async([s, tt] {
s->processTaskAsync(tt);
});
} else {
s->processTaskSync(ticket);
}
@ -187,7 +197,8 @@ void rsAutoProxyMonitor::taskAsync(std::vector<autoProxyType::autoProxyType_enum
if (!isAsyncTask(task)) {
// Usually the services will reject this ticket.
// Just print a warning - maybe there is some special case where this is a good idea.
std::cerr << "(WW) rsAutoProxyMonitor::taskAsync called with a synchronous task!" << std::endl;
RS_ERR("called with a synchronous task!");
print_stacktrace();
}
taskTicket *tt = getTicket();
@ -215,7 +226,8 @@ void rsAutoProxyMonitor::taskSync(std::vector<autoProxyType::autoProxyType_enum>
if (isAsyncTask(task)) {
// Usually the services will reject this ticket.
// Just print a warning - maybe there is some special case where this is a good idea.
std::cerr << "(WW) rsAutoProxyMonitor::taskSync called with an asynchronous task!" << std::endl;
RS_ERR("called with an asynchronous task!");
print_stacktrace();
}
taskTicket *tt = getTicket();
@ -244,7 +256,8 @@ void rsAutoProxyMonitor::taskDone(taskTicket *t, autoProxyStatus::autoProxyStatu
t->cb->taskFinished(t);
if (t != NULL) {
// callack did not clean up properly
std::cerr << "(WW) rsAutoProxyMonitor::taskFinish callback did not clean up!" << std::endl;
RS_ERR("callback did not clean up!");
print_stacktrace();
cleanUp = true;
}
} else if (t->async){
@ -252,12 +265,13 @@ void rsAutoProxyMonitor::taskDone(taskTicket *t, autoProxyStatus::autoProxyStatu
// we must take care of deleting
cleanUp = true;
if(t->data)
std::cerr << "(WW) rsAutoProxyMonitor::taskFinish async call with data attached but no callback set!" << std::endl;
RS_ERR("async call with data attached but no callback set!");
}
if (cleanUp) {
if (t->data) {
std::cerr << "(WW) rsAutoProxyMonitor::taskFinish will try to delete void pointer!" << std::endl;
RS_ERR("will try to delete void pointer!");
print_stacktrace();
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdelete-incomplete"
delete t->data;
@ -290,7 +304,8 @@ void rsAutoProxyMonitor::taskFinished(taskTicket *&ticket)
// clean up
if (ticket->data) {
std::cerr << "rsAutoProxyMonitor::taskFinished data set. Will try to delete void pointer" << std::endl;
RS_ERR(" data set. Will try to delete void pointer");
print_stacktrace();
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdelete-incomplete"
delete ticket->data;
@ -308,7 +323,7 @@ autoProxyService *rsAutoProxyMonitor::lookUpService(autoProxyType::autoProxyType
if ((itService = mProxies.find(t)) != mProxies.end()) {
return itService->second;
}
std::cerr << "sAutoProxyMonitor::lookUpService no service for type " << t << " found!" << std::endl;
RS_DBG("no service for type ", t, " found!");
return NULL;
}

View File

@ -80,12 +80,15 @@ p3GxsChannels::p3GxsChannels(
RS_SERVICE_GXS_TYPE_CHANNELS, gixs, channelsAuthenPolicy() ),
RsGxsChannels(static_cast<RsGxsIface&>(*this)), GxsTokenQueue(this),
mSubscribedGroupsMutex("GXS channels subscribed groups cache"),
mKnownChannelsMutex("GXS channels known channels timestamp cache"),
mKnownChannelsMutex("GXS channels known channels timestamp cache")
#ifdef TO_REMOVE
mSearchCallbacksMapMutex("GXS channels search callbacks map"),
mDistantChannelsCallbacksMapMutex("GXS channels distant channels callbacks map")
#endif
{
// For Dummy Msgs.
mGenActive = false;
mLastDistantSearchNotificationTS = 0;
mCommentService = new p3GxsCommentService(this, RS_SERVICE_GXS_TYPE_CHANNELS);
RsTickEvent::schedule_in(CHANNEL_PROCESS, 0);
@ -352,18 +355,6 @@ void p3GxsChannels::notifyChanges(std::vector<RsGxsNotify *> &changes)
}
RsGxsDistantSearchResultChange *dsrChange = dynamic_cast<RsGxsDistantSearchResultChange*>(*it);
if(dsrChange && rsEvents)
{
auto ev = std::make_shared<RsGxsChannelEvent>();
ev->mChannelGroupId = dsrChange->mGroupId;
ev->mChannelEventCode = RsChannelEventCode::RECEIVED_DISTANT_SEARCH_RESULT;
ev->mDistantSearchRequestId = dsrChange->mRequestId;
rsEvents->postEvent(ev);
}
/* shouldn't need to worry about groups - as they need to be subscribed to */
delete *it;
}
@ -383,17 +374,32 @@ void p3GxsChannels::notifyChanges(std::vector<RsGxsNotify *> &changes)
void p3GxsChannels::service_tick()
{
static rstime_t last_dummy_tick = 0;
rstime_t now = time(NULL);
if (time(NULL) > last_dummy_tick + 5)
{
dummy_tick();
last_dummy_tick = time(NULL);
last_dummy_tick = now;
}
RsTickEvent::tick_events();
GxsTokenQueue::checkRequests();
mCommentService->comment_tick();
// Notify distant search results, not more than once per sec. Normally we should
// rather send one item for all, but that needs another class type
if(now > mLastDistantSearchNotificationTS+2 && !mSearchResultsToNotify.empty())
{
auto ev = std::make_shared<RsGxsChannelSearchResultEvent>();
ev->mSearchResultsMap = mSearchResultsToNotify;
mLastDistantSearchNotificationTS = now;
mSearchResultsToNotify.clear();
rsEvents->postEvent(ev);
}
}
bool p3GxsChannels::getGroupData(const uint32_t &token, std::vector<RsGxsChannelGroup> &groups)
@ -2210,7 +2216,9 @@ void p3GxsChannels::dummy_tick()
}
#ifdef TO_REMOVE
cleanTimedOutCallbacks();
#endif
}
@ -2389,18 +2397,18 @@ bool p3GxsChannels::clearDistantSearchResults(TurtleRequestId req)
{
return netService()->clearDistantSearchResults(req);
}
bool p3GxsChannels::retrieveDistantSearchResults(TurtleRequestId req,std::map<RsGxsGroupId,RsGxsGroupSummary>& results)
bool p3GxsChannels::retrieveDistantSearchResults(TurtleRequestId req,std::map<RsGxsGroupId,RsGxsGroupSearchResults>& results)
{
return netService()->retrieveDistantSearchResults(req,results);
}
bool p3GxsChannels::retrieveDistantGroup(const RsGxsGroupId& group_id,RsGxsChannelGroup& distant_group)
bool p3GxsChannels::getDistantSearchResultGroupData(const RsGxsGroupId& group_id,RsGxsChannelGroup& distant_group)
{
RsGxsGroupSummary gs;
RsGxsGroupSearchResults gs;
if(netService()->retrieveDistantGroupSummary(group_id,gs))
{
// This is a placeholder information by the time we receive the full group meta data.
// This is a placeholder information by the time we receive the full group meta data and check the signature.
distant_group.mMeta.mGroupId = gs.mGroupId ;
distant_group.mMeta.mGroupName = gs.mGroupName;
distant_group.mMeta.mGroupFlags = GXS_SERV::FLAG_PRIVACY_PUBLIC ;
@ -2426,6 +2434,7 @@ bool p3GxsChannels::retrieveDistantGroup(const RsGxsGroupId& group_id,RsGxsChann
return false ;
}
#ifdef TO_REMOVE
bool p3GxsChannels::turtleSearchRequest(
const std::string& matchString,
const std::function<void (const RsGxsGroupSummary&)>& multiCallback,
@ -2505,17 +2514,24 @@ bool p3GxsChannels::localSearchRequest(
return true;
}
#endif
void p3GxsChannels::receiveDistantSearchResults(
TurtleRequestId id, const RsGxsGroupId& grpId )
void p3GxsChannels::receiveDistantSearchResults( TurtleRequestId id, const RsGxsGroupId& grpId )
{
std::cerr << __PRETTY_FUNCTION__ << "(" << id << ", " << grpId << ")"
<< std::endl;
if(!rsEvents)
return;
// We temporise here, in order to avoid notifying clients with many events
// So we put some data in there and will send an event with all of them at once every 1 sec at most.
mSearchResultsToNotify[id].insert(grpId);
#ifdef TO_REMOVE
std::cerr << __PRETTY_FUNCTION__ << "(" << id << ", " << grpId << ")" << std::endl;
{
RsGenExchange::receiveDistantSearchResults(id, grpId);
RsGxsGroupSummary gs;
gs.mGroupId = grpId;
RsGxsGroupSearchResults gs;
netService()->retrieveDistantGroupSummary(grpId, gs);
{
@ -2556,8 +2572,10 @@ void p3GxsChannels::receiveDistantSearchResults(
return;
}
} // RS_STACK_MUTEX(mDistantChannelsCallbacksMapMutex);
#endif
}
#ifdef TO_REMOVE
void p3GxsChannels::cleanTimedOutCallbacks()
{
auto now = std::chrono::system_clock::now();
@ -2586,6 +2604,7 @@ void p3GxsChannels::cleanTimedOutCallbacks()
else ++cbpt;
} // RS_STACK_MUTEX(mDistantChannelsCallbacksMapMutex)
}
#endif
bool p3GxsChannels::exportChannelLink(
std::string& link, const RsGxsGroupId& chanId, bool includeGxsData,

View File

@ -68,9 +68,9 @@ protected:
virtual TurtleRequestId turtleGroupRequest(const RsGxsGroupId& group_id);
virtual TurtleRequestId turtleSearchRequest(const std::string& match_string);
virtual bool retrieveDistantSearchResults(TurtleRequestId req, std::map<RsGxsGroupId, RsGxsGroupSummary> &results) ;
virtual bool retrieveDistantSearchResults(TurtleRequestId req, std::map<RsGxsGroupId, RsGxsGroupSearchResults> &results) ;
virtual bool clearDistantSearchResults(TurtleRequestId req);
virtual bool retrieveDistantGroup(const RsGxsGroupId& group_id,RsGxsChannelGroup& distant_group);
virtual bool getDistantSearchResultGroupData(const RsGxsGroupId& group_id,RsGxsChannelGroup& distant_group);
// Overloaded to cache new groups.
virtual RsGenExchange::ServiceCreate_Return service_CreateGroup(RsGxsGrpItem* grpItem, RsTlvSecurityKeySet& keySet);
@ -109,6 +109,7 @@ virtual bool getChannelAutoDownload(const RsGxsGroupId &groupid, bool& enabled);
virtual bool setChannelDownloadDirectory(const RsGxsGroupId &groupId, const std::string& directory);
virtual bool getChannelDownloadDirectory(const RsGxsGroupId &groupId, std::string& directory);
#ifdef TO_REMOVE
/// @see RsGxsChannels::turtleSearchRequest
virtual bool turtleSearchRequest(const std::string& matchString,
const std::function<void (const RsGxsGroupSummary&)>& multiCallback,
@ -124,6 +125,7 @@ virtual bool getChannelDownloadDirectory(const RsGxsGroupId &groupId, std::strin
virtual bool localSearchRequest(const std::string& matchString,
const std::function<void (const RsGxsGroupSummary& result)>& multiCallback,
rstime_t maxWait = 30 ) override;
#endif
/**
* Receive results from turtle search @see RsGenExchange @see RsNxsObserver
@ -374,6 +376,9 @@ bool generateGroup(uint32_t &token, std::string groupName);
std::map<RsGxsGroupId,rstime_t> mKnownChannels;
RsMutex mKnownChannelsMutex;
rstime_t mLastDistantSearchNotificationTS;
std::map<TurtleRequestId,std::set<RsGxsGroupId> > mSearchResultsToNotify;
#ifdef TO_REMOVE
/** Store search callbacks with timeout*/
std::map<
TurtleRequestId,
@ -394,4 +399,5 @@ bool generateGroup(uint32_t &token, std::string groupName);
/// Cleanup mSearchCallbacksMap and mDistantChannelsCallbacksMap
void cleanTimedOutCallbacks();
#endif
};

View File

@ -1463,12 +1463,14 @@ bool p3GxsCircles::locked_processLoadingCacheEntry(RsGxsCircleCache& cache)
}
else
{
#ifdef DEBUG_CIRCLES
std::cerr << " (WW) cache entry for circle " << cache.mCircleId << " has empty originator. Asking info for GXS id " << pit->first << " to all connected friends." << std::endl;
#endif
rsPeers->getOnlineList(peers) ;
}
mIdentities->requestKey(pit->first, peers,RsIdentityUsage(serviceType(),RsIdentityUsage::CIRCLE_MEMBERSHIP_CHECK,RsGxsGroupId(cache.mCircleId)));
mIdentities->requestKey(pit->first, peers,RsIdentityUsage(RsServiceType::GXSCIRCLE,RsIdentityUsage::CIRCLE_MEMBERSHIP_CHECK,RsGxsGroupId(cache.mCircleId)));
all_ids_here = false;
}
@ -1997,7 +1999,9 @@ bool p3GxsCircles::processMembershipRequests(uint32_t token)
// now do another sweep and remove all msgs that are older than the latest
#ifdef DEBUG_CIRCLES
std::cerr << " Cleaning old messages..." << std::endl;
#endif
for(uint32_t i=0;i<it->second.size();++i)
{

View File

@ -55,7 +55,7 @@
#define ID_REQUEST_REPUTATION 0x0003
#define ID_REQUEST_OPINION 0x0004
#define GXSID_MAX_CACHE_SIZE 5000
#define GXSID_MAX_CACHE_SIZE 15000
// unused keys are deleted according to some heuristic that should favor known keys, signed keys etc.
@ -616,11 +616,11 @@ void p3IdService::notifyChanges(std::vector<RsGxsNotify *> &changes)
std::cerr << "p3IdService::notifyChanges() Found Group Change Notification";
std::cerr << std::endl;
#endif
const RsGxsGroupId& gid(groupChange->mGroupId);
#ifdef DEBUG_IDS
std::cerr << "p3IdService::notifyChanges() Auto Subscribe to Incoming Groups: " << *git;
std::cerr << "p3IdService::notifyChanges() Auto Subscribe to Incoming Groups: " << gid;
std::cerr << std::endl;
#endif
const RsGxsGroupId& gid(groupChange->mGroupId);
if(!rsReputations->isIdentityBanned(RsGxsId(gid)))
{
@ -641,7 +641,7 @@ void p3IdService::notifyChanges(std::vector<RsGxsNotify *> &changes)
rsEvents->postEvent(ev);
// also time_stamp the key that this group represents
timeStampKey(RsGxsId(gid),RsIdentityUsage(serviceType(),RsIdentityUsage::IDENTITY_DATA_UPDATE)) ;
timeStampKey(RsGxsId(gid),RsIdentityUsage(RsServiceType(serviceType()),RsIdentityUsage::IDENTITY_NEW_FROM_GXS_SYNC)) ;
should_subscribe = true;
}
break;
@ -654,8 +654,10 @@ void p3IdService::notifyChanges(std::vector<RsGxsNotify *> &changes)
rsEvents->postEvent(ev);
// also time_stamp the key that this group represents
timeStampKey(RsGxsId(gid),RsIdentityUsage(serviceType(),RsIdentityUsage::IDENTITY_DATA_UPDATE)) ;
timeStampKey(RsGxsId(gid),RsIdentityUsage(RsServiceType(serviceType()),RsIdentityUsage::IDENTITY_NEW_FROM_GXS_SYNC)) ;
should_subscribe = true;
std::cerr << "Received new identity " << gid << " and subscribing to it" << std::endl;
}
break;
@ -1214,8 +1216,7 @@ bool p3IdService::requestIdentity(
return false;
}
RsIdentityUsage usageInfo( RsServiceType::GXSID,
RsIdentityUsage::IDENTITY_DATA_UPDATE );
RsIdentityUsage usageInfo( RsServiceType::GXSID, RsIdentityUsage::IDENTITY_NEW_FROM_EXPLICIT_REQUEST );
return requestKey(id, askPeersList, usageInfo);
}
@ -1250,8 +1251,9 @@ bool p3IdService::requestKey(const RsGxsId &id, const std::list<RsPeerId>& peers
if( info.mOverallReputationLevel == RsReputationLevel::LOCALLY_NEGATIVE )
{
RsInfo() << __PRETTY_FUNCTION__ << " not requesting Key " << id
<< " because it has been banned." << std::endl;
#ifdef DEBUG_IDS
RsInfo() << __PRETTY_FUNCTION__ << " not requesting Key " << id << " because it has been banned." << std::endl;
#endif
RS_STACK_MUTEX(mIdMtx);
mIdsNotPresent.erase(id);
@ -1360,7 +1362,7 @@ bool p3IdService::signData(const uint8_t *data,uint32_t data_size,const RsGxsId&
return false ;
}
error_status = RS_GIXS_ERROR_NO_ERROR ;
timeStampKey(own_gxs_id,RsIdentityUsage(serviceType(),RsIdentityUsage::IDENTITY_GENERIC_SIGNATURE_CREATION)) ;
timeStampKey(own_gxs_id,RsIdentityUsage(RsServiceType(serviceType()),RsIdentityUsage::IDENTITY_GENERIC_SIGNATURE_CREATION)) ;
return true ;
}
@ -1433,7 +1435,7 @@ bool p3IdService::encryptData( const uint8_t *decrypted_data,
return false ;
}
error_status = RS_GIXS_ERROR_NO_ERROR ;
timeStampKey(encryption_key_id,RsIdentityUsage(serviceType(),RsIdentityUsage::IDENTITY_GENERIC_ENCRYPTION)) ;
timeStampKey(encryption_key_id,RsIdentityUsage(RsServiceType::GXSID,RsIdentityUsage::IDENTITY_GENERIC_ENCRYPTION)) ;
return true ;
}
@ -1522,7 +1524,7 @@ bool p3IdService::encryptData( const uint8_t* decrypted_data,
{
timeStampKey( *it,
RsIdentityUsage(
serviceType(),
RsServiceType::GXSID,
RsIdentityUsage::IDENTITY_GENERIC_ENCRYPTION ) );
}
@ -1563,7 +1565,7 @@ bool p3IdService::decryptData( const uint8_t *encrypted_data,
error_status = RS_GIXS_ERROR_NO_ERROR;
timeStampKey( key_id,
RsIdentityUsage(
serviceType(),
RsServiceType::GXSID,
RsIdentityUsage::IDENTITY_GENERIC_DECRYPTION) );
return true ;
@ -1656,7 +1658,7 @@ bool p3IdService::decryptData( const uint8_t* encrypted_data,
{
timeStampKey( *it,
RsIdentityUsage(
serviceType(),
RsServiceType::GXSID,
RsIdentityUsage::IDENTITY_GENERIC_DECRYPTION ) );
}
@ -2292,7 +2294,7 @@ bool SSGxsIdGroup::load(const std::string &input)
char scorestr[RSGXSID_MAX_SERVICE_STRING];
// split into parts.
if (3 != sscanf(input.c_str(), "v2 {P:%[^}]} {T:%[^}]} {R:%[^}]}", pgpstr, recognstr, scorestr))
if (3 != sscanf(input.c_str(), "v2 {P:%[^}]}{T:%[^}]}{R:%[^}]}", pgpstr, recognstr, scorestr))
{
#ifdef DEBUG_IDS
std::cerr << "SSGxsIdGroup::load() Failed to extract 4 Parts";
@ -2954,9 +2956,21 @@ void p3IdService::requestIdsFromNet()
for(cit = mIdsNotPresent.begin(); cit != mIdsNotPresent.end();)
{
#ifdef DEBUG_IDS
Dbg2() << __PRETTY_FUNCTION__ << " Processing missing key RsGxsId: "
<< cit->first << std::endl;
Dbg2() << __PRETTY_FUNCTION__ << " Processing missing key RsGxsId: " << cit->first << std::endl;
#endif
RsGxsIdCache data;
if(mKeyCache.fetch(cit->first,data))
{
#ifdef DEBUG_IDS
std::cerr << __PRETTY_FUNCTION__ << ". Dropping request for ID " << cit->first << " at last minute, because it was found in cache"<< std::endl;
#endif
auto tmp(cit);
++tmp;
mIdsNotPresent.erase(cit);
cit = tmp;
continue;
}
const RsGxsId& gxsId = cit->first;
const std::list<RsPeerId>& peers = cit->second;
@ -3011,8 +3025,7 @@ void p3IdService::requestIdsFromNet()
{
const RsPeerId& peer = cit2->first;
std::list<RsGxsGroupId> grpIds;
for( std::list<RsGxsId>::const_iterator gxs_id_it = cit2->second.begin();
gxs_id_it != cit2->second.end(); ++gxs_id_it )
for( std::list<RsGxsId>::const_iterator gxs_id_it = cit2->second.begin(); gxs_id_it != cit2->second.end(); ++gxs_id_it )
{
#ifdef DEBUG_IDS
Dbg2() << __PRETTY_FUNCTION__ << " passing RsGxsId: " << *gxs_id_it
@ -3223,7 +3236,7 @@ bool p3IdService::cachetest_handlerequest(uint32_t token)
if (!haveKey(*vit))
{
std::list<RsPeerId> nullpeers;
requestKey(*vit, nullpeers,RsIdentityUsage(serviceType(),RsIdentityUsage::UNKNOWN_USAGE));
requestKey(*vit, nullpeers,RsIdentityUsage(RsServiceType::GXSID,RsIdentityUsage::UNKNOWN_USAGE));
#ifdef DEBUG_IDS
std::cerr << "p3IdService::cachetest_request() Requested Key Id: " << *vit;
@ -4819,11 +4832,10 @@ void RsGxsIdGroup::serial_process(
RS_SERIAL_PROCESS(mReputation);
}
RsIdentityUsage::RsIdentityUsage(
RsServiceType service, RsIdentityUsage::UsageCode code,
const RsGxsGroupId& gid, const RsGxsMessageId& mid,
RsIdentityUsage::RsIdentityUsage(RsServiceType service, RsIdentityUsage::UsageCode code,
const RsGxsGroupId& gid, const RsGxsMessageId& mid, const RsGxsMessageId &pid, const RsGxsMessageId &tid,
uint64_t additional_id, const std::string& comment ) :
mServiceId(service), mUsageCode(code), mGrpId(gid), mMsgId(mid),
mServiceId(service), mUsageCode(code), mGrpId(gid), mMsgId(mid),mParentId(pid),mThreadId(tid),
mAdditionalId(additional_id), mComment(comment)
{
/* This is a hack, since it will hash also mHash, but because it is
@ -4841,42 +4853,6 @@ RsIdentityUsage::RsIdentityUsage(
mHash = hs.hash();
}
RsIdentityUsage::RsIdentityUsage(
uint16_t service, const RsIdentityUsage::UsageCode& code,
const RsGxsGroupId& gid, const RsGxsMessageId& mid,
uint64_t additional_id,const std::string& comment ) :
mServiceId(static_cast<RsServiceType>(service)), mUsageCode(code),
mGrpId(gid), mMsgId(mid), mAdditionalId(additional_id), mComment(comment)
{
#ifdef DEBUG_IDS
std::cerr << "New identity usage: " << std::endl;
std::cerr << " service=" << std::hex << service << std::endl;
std::cerr << " code =" << std::hex << code << std::endl;
std::cerr << " grpId =" << std::hex << gid << std::endl;
std::cerr << " msgId =" << std::hex << mid << std::endl;
std::cerr << " add id =" << std::hex << additional_id << std::endl;
std::cerr << " commnt =\"" << std::hex << comment << "\"" << std::endl;
#endif
/* This is a hack, since it will hash also mHash, but because it is
* initialized to 0, and only computed in the constructor here, it should
* be ok. */
librs::crypto::HashStream hs(librs::crypto::HashStream::SHA1) ;
hs << (uint32_t)service ; // G10h4ck: Why uint32 if it's 16 bits?
hs << (uint8_t)code ;
hs << gid ;
hs << mid ;
hs << (uint64_t)additional_id ;
hs << comment ;
mHash = hs.hash();
#ifdef DEBUG_IDS
std::cerr << " hash =\"" << std::hex << mHash << "\"" << std::endl;
#endif
}
RsIdentityUsage::RsIdentityUsage() :
mServiceId(RsServiceType::NONE), mUsageCode(UNKNOWN_USAGE), mAdditionalId(0)
{}

View File

@ -102,3 +102,8 @@ android-* {
CONFIG *= qt
QT *= network
}
################################### Pkg-Config Stuff #############################
LIBS *= $$system(pkg-config --libs $$PKGCONFIG)

View File

@ -0,0 +1,163 @@
#include "i2pcommon.h"
#include "util/rsbase64.h"
#include "util/rsdebug.h"
namespace i2p {
std::string keyToBase32Addr(const std::string &key)
{
std::string copy(key);
// replace I2P specific chars
std::replace(copy.begin(), copy.end(), '~', '/');
// replacing the - with a + is not necessary, as RsBase64 can handle base64url encoding, too
// std::replace(copy.begin(), copy.end(), '-', '+');
// decode
std::vector<uint8_t> bin;
RsBase64::decode(copy, bin);
// hash
std::vector<uint8_t> sha256 = RsUtil::BinToSha256(bin);
// encode
std::string out = Radix32::encode(sha256);
// i2p uses lowercase
std::transform(out.begin(), out.end(), out.begin(), ::tolower);
out.append(".b32.i2p");
return out;
}
const std::string makeOption(const std::string &lhs, const int8_t &rhs) {
return lhs + "=" + std::to_string(rhs);
}
uint16_t readTwoBytesBE(std::vector<uint8_t>::const_iterator &p)
{
uint16_t val = 0;
val += *p++;
val <<= 8;
val += *p++;
return val;
}
std::string publicKeyFromPrivate(std::string const &priv)
{
/*
* https://geti2p.net/spec/common-structures#destination
* https://geti2p.net/spec/common-structures#keysandcert
* https://geti2p.net/spec/common-structures#certificate
*/
if (priv.length() < 884) // base64 ( = 663 bytes = KeyCert + priv Keys)
return std::string();
// creat a copy to work on, need to convert it to standard base64
auto priv_copy(priv);
std::replace(priv_copy.begin(), priv_copy.end(), '~', '/');
// replacing the - with a + is not necessary, as RsBase64 can handle base64url encoding, too
// std::replace(copy.begin(), copy.end(), '-', '+');
// get raw data
std::vector<uint8_t> dataPriv;
RsBase64::decode(priv_copy, dataPriv);
auto p = dataPriv.cbegin();
RS_DBG("dataPriv.size ", dataPriv.size());
size_t publicKeyLen = 256 + 128; // default length (bytes)
uint8_t certType = 0;
uint16_t len = 0;
uint16_t signingKeyType = 0;
uint16_t cryptKey = 0;
// only used for easy break
do {
try {
// jump to certificate
p += publicKeyLen;
// try to read type and length
certType = *p++;
len = readTwoBytesBE(p);
// only 0 and 5 are used / valid at this point
// check for == 0
if (certType == static_cast<typename std::underlying_type<CertType>::type>(CertType::Null)) {
/*
* CertType.Null
* type null is followed by 0x00 0x00 <END>
* so has to be 0!
*/
RS_DBG("cert is CertType.Null");
publicKeyLen += 3; // add 0x00 0x00 0x00
if (len != 0)
// weird
RS_DBG("cert is CertType.Null but len != 0");
break;
}
// check for != 5
if (certType != static_cast<typename std::underlying_type<CertType>::type>(CertType::Key)) {
// unsupported
RS_DBG("cert type ", certType, " is unsupported");
return std::string();
}
RS_DBG("cert is CertType.Key");
publicKeyLen += 7; // <type 1B> <len 2B> <keyType1 2B> <keyType2 2B> = 1 + 2 + 2 + 2 = 7 bytes
/*
* "Key certificates were introduced in release 0.9.12. Prior to that release, all PublicKeys were 256-byte ElGamal keys, and all SigningPublicKeys were 128-byte DSA-SHA1 keys."
* --> there is space for 256+128 bytes, longer keys are splitted and appended to the certificate
* We don't need to bother with the splitting here as only the lenght is important!
*/
// Signing Public Key
// likely 7
signingKeyType = readTwoBytesBE(p);
RS_DBG("signing pubkey type ", certType);
if (signingKeyType >= 3 && signingKeyType <= 6) {
RS_DBG("signing pubkey type ", certType, " has oversize");
// calculate oversize
if (signingKeyType >= signingKeyLengths.size()) {
// just in case
RS_DBG("signing pubkey type ", certType, " cannot be found in size data!");
return std::string();
}
auto values = signingKeyLengths[signingKeyType];
if (values.first <= 128) {
// just in case, it's supposed to be larger!
RS_DBG("signing pubkey type ", certType, " is oversize but size calculation would underflow!");
return std::string();
}
publicKeyLen += values.first - 128; // 128 = default DSA key length = the space than can be used before the key must be splitted
}
// Crypto Public Key
// likely 0
cryptKey = readTwoBytesBE(p);
RS_DBG("crypto pubkey type ", cryptKey);
// info: these are all smaller than the default 256 bytes, so no oversize calculation is needed
break;
} catch (const std::out_of_range &e) {
RS_DBG("hit exception! ", e.what());
return std::string();
}
} while(false);
std::string pub;
auto data2 = std::vector<uint8_t>(dataPriv.cbegin(), dataPriv.cbegin() + publicKeyLen);
RsBase64::encode(data2.data(), data2.size(), pub, false, false);
return pub;
}
} // namespace i2p

View File

@ -0,0 +1,213 @@
#ifndef I2PCOMMON_H
#define I2PCOMMON_H
#include <algorithm>
#include <map>
#include <array>
#include <utility>
#include "util/rsrandom.h"
#include "util/radix32.h"
#include "util/rsbase64.h"
#include "util/rsprint.h"
#include "util/rsdebug.h"
/*
* This header provides common code for i2p related code, namely BOB and SAM3 support.
*/
namespace i2p {
static constexpr int8_t kDefaultLength = 3; // i2p default
static constexpr int8_t kDefaultQuantity = 3; // i2p default + 1
static constexpr int8_t kDefaultVariance = 0;
static constexpr int8_t kDefaultBackupQuantity = 0;
/**
* @brief The address struct
* This structure is a container for any i2p address/key. The public key is used for addressing and can be (optionally) hashed to generate the .b32.i2p address.
*/
struct address {
std::string base32;
std::string publicKey;
std::string privateKey;
void clear() {
base32.clear();
publicKey.clear();
privateKey.clear();
}
};
/**
* @brief The settings struct
* Common structure with all settings that are shared between any i2p backends
*/
struct settings {
bool enable;
struct address address;
// connection parameter
int8_t inLength;
int8_t inQuantity;
int8_t inVariance;
int8_t inBackupQuantity;
int8_t outLength;
int8_t outQuantity;
int8_t outVariance;
int8_t outBackupQuantity;
void initDefault() {
enable = false;
address.clear();
inLength = kDefaultLength;
inQuantity = kDefaultQuantity;
inVariance = kDefaultVariance;
inBackupQuantity = kDefaultBackupQuantity;
outLength = kDefaultLength;
outQuantity = kDefaultQuantity;
outVariance = kDefaultVariance;
outBackupQuantity = kDefaultBackupQuantity;
}
};
/*
Type Type Code Payload Length Total Length Notes
Null 0 0 3
HashCash 1 varies varies Experimental, unused. Payload contains an ASCII colon-separated hashcash string.
Hidden 2 0 3 Experimental, unused. Hidden routers generally do not announce that they are hidden.
Signed 3 40 or 72 43 or 75 Experimental, unused. Payload contains a 40-byte DSA signature, optionally followed by the 32-byte Hash of the signing Destination.
Multiple 4 varies varies Experimental, unused. Payload contains multiple certificates.
Key 5 4+ 7+ Since 0.9.12. See below for details.
*/
enum class CertType : uint8_t {
Null = 0,
HashCash = 1,
Hidden = 2,
Signed = 3,
Multiple = 4,
Key = 5
};
/*
* public
Type Type Code Total Public Key Length Since Usage
DSA_SHA1 0 128 0.9.12 Legacy Router Identities and Destinations, never explicitly set
ECDSA_SHA256_P256 1 64 0.9.12 Older Destinations
ECDSA_SHA384_P384 2 96 0.9.12 Rarely if ever used for Destinations
ECDSA_SHA512_P521 3 132 0.9.12 Rarely if ever used for Destinations
RSA_SHA256_2048 4 256 0.9.12 Offline only; never used in Key Certificates for Router Identities or Destinations
RSA_SHA384_3072 5 384 0.9.12 Offline only; never used in Key Certificates for Router Identities or Destinations
RSA_SHA512_4096 6 512 0.9.12 Offline only; never used in Key Certificates for Router Identities or Destinations
EdDSA_SHA512_Ed25519 7 32 0.9.15 Recent Router Identities and Destinations
EdDSA_SHA512_Ed25519ph 8 32 0.9.25 Offline only; never used in Key Certificates for Router Identities or Destinations
reserved (GOST) 9 64 Reserved, see proposal 134
reserved (GOST) 10 128 Reserved, see proposal 134
RedDSA_SHA512_Ed25519 11 32 0.9.39 For Destinations and encrypted leasesets only; never used for Router Identities
reserved 65280-65534 Reserved for experimental use
reserved 65535 Reserved for future expansion
* private
Type Length (bytes) Since Usage
DSA_SHA1 20 Legacy Router Identities and Destinations
ECDSA_SHA256_P256 32 0.9.12 Recent Destinations
ECDSA_SHA384_P384 48 0.9.12 Rarely used for Destinations
ECDSA_SHA512_P521 66 0.9.12 Rarely used for Destinations
RSA_SHA256_2048 512 0.9.12 Offline signing, never used for Router Identities or Destinations
RSA_SHA384_3072 768 0.9.12 Offline signing, never used for Router Identities or Destinations
RSA_SHA512_4096 1024 0.9.12 Offline signing, never used for Router Identities or Destinations
EdDSA_SHA512_Ed25519 32 0.9.15 Recent Router Identities and Destinations
EdDSA_SHA512_Ed25519ph 32 0.9.25 Offline signing, never used for Router Identities or Destinations
RedDSA_SHA512_Ed25519 32 0.9.39 For Destinations and encrypted leasesets only, never used for Router Identities
*/
enum class SigningKeyType : uint16_t {
DSA_SHA1 = 0,
ECDSA_SHA256_P256 = 1,
ECDSA_SHA384_P384 = 2,
ECDSA_SHA512_P521 = 3,
RSA_SHA256_2048 = 4,
RSA_SHA384_3072 = 5,
RSA_SHA512_4096 = 6,
EdDSA_SHA512_Ed25519 = 7,
EdDSA_SHA512_Ed25519ph = 8,
RedDSA_SHA512_Ed25519 = 11
};
/*
* public
Type Type Code Total Public Key Length Usage
ElGamal 0 256 All Router Identities and Destinations
P256 1 64 Reserved, see proposal 145
P384 2 96 Reserved, see proposal 145
P521 3 132 Reserved, see proposal 145
X25519 4 32 Not for use in key certs. See proposal 144
reserved 65280-65534 Reserved for experimental use
reserved 65535 Reserved for future expansion
* private
Type Length (bytes) Since Usage
ElGamal 256 All Router Identities and Destinations
P256 32 TBD Reserved, see proposal 145
P384 48 TBD Reserved, see proposal 145
P521 66 TBD Reserved, see proposal 145
X25519 32 0.9.38 Little-endian. See proposal 144
*/
enum class CryptoKeyType : uint16_t {
ElGamal = 0,
P256 = 1,
P384 = 2,
P521 = 3,
X25519 = 4
};
static const std::array<std::pair<uint16_t, uint16_t>, 5> cryptoKeyLengths {
/*CryptoKeyType::ElGamal*/ std::make_pair<uint16_t, uint16_t>(256, 256),
/*CryptoKeyType::P256, */ std::make_pair<uint16_t, uint16_t>( 64, 32),
/*CryptoKeyType::P384, */ std::make_pair<uint16_t, uint16_t>( 96, 48),
/*CryptoKeyType::P521, */ std::make_pair<uint16_t, uint16_t>(132, 66),
/*CryptoKeyType::X25519,*/ std::make_pair<uint16_t, uint16_t>( 32, 32),
};
static const std::array<std::pair<uint16_t, uint16_t>, 12> signingKeyLengths {
/*SigningKeyType::DSA_SHA1, */ std::make_pair<uint16_t, uint16_t>(128, 128),
/*SigningKeyType::ECDSA_SHA256_P256, */ std::make_pair<uint16_t, uint16_t>( 64, 32),
/*SigningKeyType::ECDSA_SHA384_P384, */ std::make_pair<uint16_t, uint16_t>( 96, 48),
/*SigningKeyType::ECDSA_SHA512_P521, */ std::make_pair<uint16_t, uint16_t>(132, 66),
/*SigningKeyType::RSA_SHA256_2048, */ std::make_pair<uint16_t, uint16_t>(256, 512),
/*SigningKeyType::RSA_SHA384_3072, */ std::make_pair<uint16_t, uint16_t>(384, 768),
/*SigningKeyType::RSA_SHA512_4096, */ std::make_pair<uint16_t, uint16_t>(512,1024),
/*SigningKeyType::EdDSA_SHA512_Ed25519 */ std::make_pair<uint16_t, uint16_t>( 32, 32),
/*SigningKeyType::EdDSA_SHA512_Ed25519ph */ std::make_pair<uint16_t, uint16_t>( 32, 32),
/*reserved (GOST) */ std::make_pair<uint16_t, uint16_t>( 64, 0),
/*reserved (GOST) */ std::make_pair<uint16_t, uint16_t>(128, 0),
/*SigningKeyType::RedDSA_SHA512_Ed25519 */ std::make_pair<uint16_t, uint16_t>( 32, 32),
};
/**
* @brief makeOption Creates the string "lhs=rhs" used by BOB and SAM. Converts rhs
* @param lhs option to set
* @param rhs value to set
* @return concatenated string
*/
const std::string makeOption(const std::string &lhs, const int8_t &rhs);
/**
* @brief keyToBase32Addr generated a base32 address (.b32.i2p) from a given public key
* @param key public key
* @return generated base32 address
*/
std::string keyToBase32Addr(const std::string &key);
/**
* @brief publicKeyFromPrivate parses the private key and calculates the lenght of the public key
* @param priv private key (which includes the public key) to read
* @return public key used for addressing
*/
std::string publicKeyFromPrivate(const std::string &priv);
} // namespace i2p
#endif // I2PCOMMON_H

View File

@ -30,6 +30,12 @@ std::ostream &operator<<(std::ostream& out, const std::error_condition& err)
<< " category: " << err.category().name();
}
std::string rsErrorNotInCategory(int errNum, const std::string& categoryName)
{
return "Error message for error: " + std::to_string(errNum) +
" not available in category: " + categoryName;
}
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

View File

@ -21,19 +21,23 @@
*******************************************************************************/
#pragma once
#include <ostream>
#include <string>
#include <sstream>
#include <system_error>
/** Stream helper for std::error_condition */
std::ostream &operator<<(std::ostream& out, const std::error_condition& err);
#ifdef __ANDROID__
# include <android/log.h>
# include <sstream>
# include <string>
#else // def __ANDROID__
# include <iostream>
# include <chrono>
# include <iomanip>
#endif // def __ANDROID__
# include "util/rsjson.h"
#include "util/rsjson.h"
#ifdef __ANDROID__
enum class RsLoggerCategories
{
DEBUG = ANDROID_LOG_DEBUG,
@ -42,72 +46,7 @@ enum class RsLoggerCategories
ERROR = ANDROID_LOG_ERROR,
FATAL = ANDROID_LOG_FATAL
};
template <RsLoggerCategories CATEGORY>
struct t_RsLogger
{
inline t_RsLogger() = default;
/** Offer variadic style too, as a benefit this has better atomicity then
* << style, but doesn't supports manipulators and things like std::endl
* @see https://stackoverflow.com/a/27375675 */
template <typename Arg, typename... Args>
inline t_RsLogger(Arg&& arg, Args&&... args)
{
ostr << std::forward<Arg>(arg);
using expander = int[];
(void)expander{0, (void(ostr << std::forward<Args>(args)), 0)...};
mFlush();
}
/** On other platforms expose the type of underlying stream.
* On Android it cannot work like that so return the class type itself
* just for code compatibility with other platforms */
using stream_type = t_RsLogger;
template<typename T>
inline stream_type& operator<<(const T& val)
{ ostr << val; return *this; }
template<typename T>
inline stream_type& operator<<(const RsJson& val)
{ ostr << val; return *this; }
/// needed for manipulators and things like std::endl
stream_type& operator<<(std::ostream& (*pf)(std::ostream&))
{
if(pf == static_cast<std::ostream& (*)(std::ostream&)>(
&std::endl< char, std::char_traits<char> > ))
mFlush();
else ostr << pf;
return *this;
}
/** On other platforms return underlying stream to write avoiding additional
* prefixes. On Android it cannot work like that so return the object itself
* just for code compatibility with other platforms */
inline stream_type& uStream() { return *this; }
private:
std::ostringstream ostr;
void mFlush()
{
__android_log_write(
static_cast<int>(CATEGORY),
"RetroShare", ostr.str().c_str() );
ostr.str() = "";
}
};
#else // def __ANDROID__
#include <iostream>
#include <chrono>
#include <iomanip>
#include <sstream>
enum class RsLoggerCategories
{
DEBUG = 'D',
@ -116,89 +55,105 @@ enum class RsLoggerCategories
ERROR = 'E',
FATAL = 'F'
};
#endif // def __ANDROID__
/** Stream helper for std::error_condition */
std::ostream &operator<<(std::ostream& out, const std::error_condition& err);
/** Provide unkown error message for all error categories to avoid duplicating
* the message around */
std::string rsErrorNotInCategory(int errNum, const std::string& categoryName);
template <RsLoggerCategories CATEGORY>
struct t_RsLogger
struct t_RsLogger : std::ostringstream
{
/// Expose the type of underlying stream
using stream_type = decltype(std::cerr);
t_RsLogger() { setPrefix(); }
~t_RsLogger() { flush(); }
/// Return underlying stream to write avoiding additional prefixes
static inline stream_type& uStream() { return std::cerr; }
inline t_RsLogger() = default;
/** Offer variadic style too, as a benefit this has better atomicity then
* << style, but doesn't supports manipulators and things like std::endl
* @see https://stackoverflow.com/a/27375675 */
template <typename Arg, typename... Args>
inline t_RsLogger(Arg&& arg, Args&&... args)
/** Offer variadic style, this doesn't supports things like std::endl as
* paramether but when used toghether with conditional debugging macros
* reduces binary size as paramethers of suppressed calls are not evaluated
* and literally disappear in preprocessing fase @see RsDbg */
template <typename... Args>
explicit inline t_RsLogger(Args&&... args)
{
std::ostringstream ostr;
ostr << getPrefix() << std::forward<Arg>(arg);
setPrefix();
/* Combine initializer list and comma operator so the compiler unpack
* template arguments and feed our own stream without recursion
* see https://stackoverflow.com/a/27375675 */
using expander = int[];
(void)expander{0, (void(ostr << std::forward<Args>(args)), 0)...};
ostr << std::endl;
uStream() << ostr.str();
(void) expander {0, (void((*this) << std::forward<Args>(args)), 0)...};
}
template<typename T>
inline stream_type& operator<<(const T& val)
{ return uStream() << getPrefix() << val; }
/// needed for manipulators and things like std::endl
stream_type& operator<<(std::ostream& (*pf)(std::ostream&))
{ return uStream() << pf; }
/** Dump buffer stream to log */
void flush()
{
#ifdef __ANDROID__
__android_log_write(
static_cast<int>(CATEGORY),
"RetroShare", str().c_str() );
#else // def __ANDROID__
(*this) << std::endl;
std::cerr << str();
#endif // def __ANDROID__
str() = "";
}
private:
std::string getPrefix()
#ifdef __ANDROID__
inline void setPrefix() {}
#else // def __ANDROID__
void setPrefix()
{
using namespace std::chrono;
const auto now = system_clock::now();
const auto sec = time_point_cast<seconds>(now);
const auto msec = duration_cast<milliseconds>(now - sec);
std::ostringstream tstream;
tstream << static_cast<char>(CATEGORY) << " "
(*this) << static_cast<char>(CATEGORY) << " "
<< sec.time_since_epoch().count() << "."
<< std::setfill('0') << std::setw(3) << msec.count()
<< " ";
return tstream.str();
<< std::setfill('0') << std::setw(3) << msec.count() << " ";
}
};
#endif // def __ANDROID__
};
/**
* Comfortable debug message logging, supports chaining like std::cerr but can
* be easly and selectively disabled at compile time to reduce generated binary
* size and performance impact without too many \#ifdef around.
* Comfortable debug message logging, supports both variadic style and chaining
* style like std::cerr.
* Can be easly and selectively disabled at compile time.
* To reduce generated binary size and performance impact when debugging is
* disabled without too many \#ifdef around the code combining the variadic
* style with the leveled debugging macros is the way to go.
*
* To selectively debug your context you can just add something like this in
* in that context, as an example for a class you can just add a line like this
* inside class declaration:
* To selectively debug your file you just need to include the header of desired
* debugging level (0 to 4)
@code{.cpp}
RS_SET_CONTEXT_DEBUG_LEVEL(2)
#include "util/rsdebuglevel2.h"
@endcode
* And the you can write debug messages around the code of the class like this:
* Then where you want to print debug messages use
@code{.cpp}
Dbg1() << "Level 1 debug message example, this will be compiled and "
<< "printed" << std::endl;
Dbg2() << "Level 2 debug message example, this will be compiled and "
<< "printed" << std::endl;
Dbg3() << "Level 3 debug message example, this will not be compiled and "
<< "printed, and without #ifdef around!!" << std::endl;
Dbg4() << "Level 4 debug message example, this will not be compiled and "
<< "printed, and without #ifdef around!!" << std::endl;
RS_DBG0("Hello 0 ", "my debug ", my_variable) << " message " << variable2;
RS_DBG1("Hello 1 ", "my debug ", my_variable) << " message " << variable2;
RS_DBG2("Hello 2 ", "my debug ", my_variable) << " message " << variable2;
RS_DBG3("Hello 3 ", "my debug ", my_variable) << " message " << variable2;
RS_DBG4("Hello 4 ", "my debug ", my_variable) << " message " << variable2;
@endcode
* To change the debugging level, for example to completely disable debug
* messages you can change it to 0
@code{.cpp}
RS_SET_CONTEXT_DEBUG_LEVEL(0)
@endcode
* While to set it to maximim level you have to pass 4.
* To change the debugging level just include a different level header like
* `util/rsdebuglevel1.h`, debug messages with lower or equal level then the
* included header will be printed, the others will not.
* Remember then on messages with debug level higher then the included the
* paramethers you pass as macro arguments (variadic style) will disappear in
* the preprocessing phase, so their evaluation will not be included in the
* final binary and not executed at runtime, instead the paramether passed with
* `<<` (chaining style) will be in the compiled binary and evaluated at runtime
* even if are not printed, due to how C++ is made it is not possible to avoid
* this, so we suggest to use variadic style for debug messages.
*/
using RsDbg = t_RsLogger<RsLoggerCategories::DEBUG>;
using RsDbg = t_RsLogger<RsLoggerCategories::DEBUG>;
#define RS_DBG(...) RsDbg(__PRETTY_FUNCTION__, " ", __VA_ARGS__)
/**
* Comfortable log information reporting helper, supports chaining like
@ -208,17 +163,22 @@ using RsDbg = t_RsLogger<RsLoggerCategories::DEBUG>;
RsInfo() << __PRETTY_FUNCTION__ << "My information message" << std::cerr;
@endcode
*/
using RsInfo = t_RsLogger<RsLoggerCategories::INFO>;
using RsInfo = t_RsLogger<RsLoggerCategories::INFO>;
#define RS_INFO(...) RsInfo(__PRETTY_FUNCTION__, " ", __VA_ARGS__)
/// Similar to @see RsInfo but for warning messages
using RsWarn = t_RsLogger<RsLoggerCategories::WARNING>;
using RsWarn = t_RsLogger<RsLoggerCategories::WARNING>;
#define RS_WARN(...) RsWarn(__PRETTY_FUNCTION__, " ", __VA_ARGS__)
/// Similar to @see RsInfo but for error messages
using RsErr = t_RsLogger<RsLoggerCategories::ERROR>;
using RsErr = t_RsLogger<RsLoggerCategories::ERROR>;
#define RS_ERR(...) RsErr(__PRETTY_FUNCTION__, " ", __VA_ARGS__)
/** Similar to @see RsInfo but for fatal errors (the ones which cause RetroShare
* to terminate) messages */
using RsFatal = t_RsLogger<RsLoggerCategories::FATAL>;
using RsFatal = t_RsLogger<RsLoggerCategories::FATAL>;
#define RS_FATAL(...) RsFatal(__PRETTY_FUNCTION__, " ", __VA_ARGS__)
/**
* Keeps compatible syntax with RsDbg but explicitely do nothing in a way that
@ -228,27 +188,31 @@ using RsFatal = t_RsLogger<RsLoggerCategories::FATAL>;
struct RsNoDbg
{
inline RsNoDbg() = default;
template <typename T, typename... Args> inline RsNoDbg(T, Args...) {}
/** Defined as the type itself just for code compatibility with other
* logging classes */
using stream_type = RsNoDbg;
template <typename... Args> inline explicit RsNoDbg(Args...) {}
/** This match most of the types, but might be not enough for templated
* types */
template<typename T>
inline stream_type& operator<<(const T&) { return *this; }
inline RsNoDbg& operator<<(const T&) { return *this; }
/// needed for manipulators and things like std::endl
inline stream_type& operator<<(std::ostream& (*/*pf*/)(std::ostream&))
inline RsNoDbg& operator<<(std::ostream& (*/*pf*/)(std::ostream&))
{ return *this; }
/** Return the object itself just for code compatibility with other
* logging classes */
inline stream_type& uStream() { return *this; }
/** Do nothing. Just for code compatibility with other logging classes */
inline void flush() {}
};
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// All the following lines are DEPRECATED!!
#include "util/rsdeprecate.h"
/**
* Concatenate preprocessor tokens A and B without expanding macro definitions
* (however, if invoked from a macro, macro arguments are expanded).
@ -267,41 +231,30 @@ struct RsNoDbg
// A bunch of boilerplate, but just in one place
#define RS_SET_CONTEXT_DEBUG_LEVEL0 \
using Dbg1 = RsNoDbg; \
using Dbg2 = RsNoDbg; \
using Dbg3 = RsNoDbg; \
using Dbg4 = RsNoDbg;
using Dbg1 RS_DEPRECATED_FOR(RS_DBG1) = RsNoDbg; \
using Dbg2 RS_DEPRECATED_FOR(RS_DBG2) = RsNoDbg; \
using Dbg3 RS_DEPRECATED_FOR(RS_DBG3) = RsNoDbg; \
using Dbg4 RS_DEPRECATED_FOR(RS_DBG4) = RsNoDbg;
#define RS_SET_CONTEXT_DEBUG_LEVEL1 \
using Dbg1 = RsDbg; \
using Dbg2 = RsNoDbg; \
using Dbg3 = RsNoDbg; \
using Dbg4 = RsNoDbg;
using Dbg1 RS_DEPRECATED_FOR(RS_DBG1) = RsDbg; \
using Dbg2 RS_DEPRECATED_FOR(RS_DBG2) = RsNoDbg; \
using Dbg3 RS_DEPRECATED_FOR(RS_DBG3) = RsNoDbg; \
using Dbg4 RS_DEPRECATED_FOR(RS_DBG4) = RsNoDbg;
#define RS_SET_CONTEXT_DEBUG_LEVEL2 \
using Dbg1 = RsDbg; \
using Dbg2 = RsDbg; \
using Dbg3 = RsNoDbg; \
using Dbg4 = RsNoDbg;
using Dbg1 RS_DEPRECATED_FOR(RS_DBG1) = RsDbg; \
using Dbg2 RS_DEPRECATED_FOR(RS_DBG2) = RsDbg; \
using Dbg3 RS_DEPRECATED_FOR(RS_DBG3) = RsNoDbg; \
using Dbg4 RS_DEPRECATED_FOR(RS_DBG4) = RsNoDbg;
#define RS_SET_CONTEXT_DEBUG_LEVEL3 \
using Dbg1 = RsDbg; \
using Dbg2 = RsDbg; \
using Dbg3 = RsDbg; \
using Dbg4 = RsNoDbg;
using Dbg1 RS_DEPRECATED_FOR(RS_DBG1) = RsDbg; \
using Dbg2 RS_DEPRECATED_FOR(RS_DBG2) = RsDbg; \
using Dbg3 RS_DEPRECATED_FOR(RS_DBG3) = RsDbg; \
using Dbg4 RS_DEPRECATED_FOR(RS_DBG4) = RsNoDbg;
#define RS_SET_CONTEXT_DEBUG_LEVEL4 \
using Dbg1 = RsDbg; \
using Dbg2 = RsDbg; \
using Dbg3 = RsDbg; \
using Dbg4 = RsDbg;
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// All the following lines are DEPRECATED!!
#include <string>
#include "util/rsdeprecate.h"
using Dbg1 RS_DEPRECATED_FOR(RS_DBG1) = RsDbg; \
using Dbg2 RS_DEPRECATED_FOR(RS_DBG2) = RsDbg; \
using Dbg3 RS_DEPRECATED_FOR(RS_DBG3) = RsDbg; \
using Dbg4 RS_DEPRECATED_FOR(RS_DBG4) = RsDbg;
namespace RsLog {
enum RS_DEPRECATED_FOR("RsErr, RsDbg, RsNoDbg") logLvl {

View File

@ -0,0 +1,42 @@
/*******************************************************************************
* RetroShare debugging level *
* *
* Copyright (C) 2020 Gioacchino Mazzurco <gio@eigenlab.org> *
* Copyright (C) 2020 Asociación Civil Altermundi <info@altermundi.net> *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 3 of the *
* License, or (at your option) 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 Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public License *
* along with this program. If not, see <https://www.gnu.org/licenses/>. *
* *
*******************************************************************************/
// #pragma once // This is commented out on purpose!
#include <util/rsdebug.h>
#undef RS_DEBUG_LEVEL
#define RS_DEBUG_LEVEL 0
#undef RS_DBG0
#define RS_DBG0(...) RsDbg(__PRETTY_FUNCTION__, " ", __VA_ARGS__)
#undef RS_DBG1
#define RS_DBG1(...) RsNoDbg("")
#undef RS_DBG2
#define RS_DBG2(...) RsNoDbg("")
#undef RS_DBG3
#define RS_DBG3(...) RsNoDbg("")
#undef RS_DBG4
#define RS_DBG4(...) RsNoDbg("")

View File

@ -0,0 +1,42 @@
/*******************************************************************************
* RetroShare debugging level *
* *
* Copyright (C) 2020 Gioacchino Mazzurco <gio@eigenlab.org> *
* Copyright (C) 2020 Asociación Civil Altermundi <info@altermundi.net> *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 3 of the *
* License, or (at your option) 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 Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public License *
* along with this program. If not, see <https://www.gnu.org/licenses/>. *
* *
*******************************************************************************/
// #pragma once // This is commented out on purpose!
#include <util/rsdebug.h>
#undef RS_DEBUG_LEVEL
#define RS_DEBUG_LEVEL 1
#undef RS_DBG0
#define RS_DBG0(...) RsDbg(__PRETTY_FUNCTION__, " ", __VA_ARGS__)
#undef RS_DBG1
#define RS_DBG1(...) RsDbg(__PRETTY_FUNCTION__, " ", __VA_ARGS__)
#undef RS_DBG2
#define RS_DBG2(...) RsNoDbg()
#undef RS_DBG3
#define RS_DBG3(...) RsNoDbg()
#undef RS_DBG4
#define RS_DBG4(...) RsNoDbg()

View File

@ -0,0 +1,42 @@
/*******************************************************************************
* RetroShare debugging level *
* *
* Copyright (C) 2020 Gioacchino Mazzurco <gio@eigenlab.org> *
* Copyright (C) 2020 Asociación Civil Altermundi <info@altermundi.net> *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 3 of the *
* License, or (at your option) 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 Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public License *
* along with this program. If not, see <https://www.gnu.org/licenses/>. *
* *
*******************************************************************************/
// #pragma once // This is commented out on purpose!
#include <util/rsdebug.h>
#undef RS_DEBUG_LEVEL
#define RS_DEBUG_LEVEL 2
#undef RS_DBG0
#define RS_DBG0(...) RsDbg(__PRETTY_FUNCTION__, " ", __VA_ARGS__)
#undef RS_DBG1
#define RS_DBG1(...) RsDbg(__PRETTY_FUNCTION__, " ", __VA_ARGS__)
#undef RS_DBG2
#define RS_DBG2(...) RsDbg(__PRETTY_FUNCTION__, " ", __VA_ARGS__)
#undef RS_DBG3
#define RS_DBG3(...) RsNoDbg()
#undef RS_DBG4
#define RS_DBG4(...) RsNoDbg()

View File

@ -0,0 +1,42 @@
/*******************************************************************************
* RetroShare debugging level *
* *
* Copyright (C) 2020 Gioacchino Mazzurco <gio@eigenlab.org> *
* Copyright (C) 2020 Asociación Civil Altermundi <info@altermundi.net> *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 3 of the *
* License, or (at your option) 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 Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public License *
* along with this program. If not, see <https://www.gnu.org/licenses/>. *
* *
*******************************************************************************/
// #pragma once // This is commented out on purpose!
#include <util/rsdebug.h>
#undef RS_DEBUG_LEVEL
#define RS_DEBUG_LEVEL 3
#undef RS_DBG0
#define RS_DBG0(...) RsDbg(__PRETTY_FUNCTION__, " ", __VA_ARGS__)
#undef RS_DBG1
#define RS_DBG1(...) RsDbg(__PRETTY_FUNCTION__, " ", __VA_ARGS__)
#undef RS_DBG2
#define RS_DBG2(...) RsDbg(__PRETTY_FUNCTION__, " ", __VA_ARGS__)
#undef RS_DBG3
#define RS_DBG3(...) RsDbg(__PRETTY_FUNCTION__, " ", __VA_ARGS__)
#undef RS_DBG4
#define RS_DBG4(...) RsNoDbg()

View File

@ -0,0 +1,42 @@
/*******************************************************************************
* RetroShare debugging level *
* *
* Copyright (C) 2020 Gioacchino Mazzurco <gio@eigenlab.org> *
* Copyright (C) 2020 Asociación Civil Altermundi <info@altermundi.net> *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 3 of the *
* License, or (at your option) 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 Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public License *
* along with this program. If not, see <https://www.gnu.org/licenses/>. *
* *
*******************************************************************************/
// #pragma once // This is commented out on purpose!
#include <util/rsdebug.h>
#undef RS_DEBUG_LEVEL
#define RS_DEBUG_LEVEL 4
#undef RS_DBG0
#define RS_DBG0(...) RsDbg(__PRETTY_FUNCTION__, " ", __VA_ARGS__)
#undef RS_DBG1
#define RS_DBG1(...) RsDbg(__PRETTY_FUNCTION__, " ", __VA_ARGS__)
#undef RS_DBG2
#define RS_DBG2(...) RsDbg(__PRETTY_FUNCTION__, " ", __VA_ARGS__)
#undef RS_DBG3
#define RS_DBG3(...) RsDbg(__PRETTY_FUNCTION__, " ", __VA_ARGS__)
#undef RS_DBG4
#define RS_DBG4(...) RsDbg(__PRETTY_FUNCTION__, " ", __VA_ARGS__)

View File

@ -121,13 +121,26 @@ double RsRandom::random_f64()
return random_u64() / (double)(~(uint64_t)0) ;
}
std::string RsRandom::random_alphaNumericString(uint32_t len)
/*static*/ std::string RsRandom::alphaNumeric(uint32_t length)
{
std::string s = "" ;
std::string s;
while(s.size() < length)
{
uint8_t rChar; random_bytes(&rChar, 1); rChar = rChar % 123;
/* if(isalnum(val)) isalnum result may vary depend on locale!! */
if( (rChar >= 48 && rChar <= 57) /* 0-9 */ ||
(rChar >= 65 && rChar <= 90) /* A-Z */ ||
(rChar >= 97 && rChar <= 122) /* a-z */ )
s += static_cast<char>(rChar);
}
for(uint32_t i=0;i<len;++i)
s += (char)( (random_u32()%94) + 33) ;
return s ;
return s;
}
/*static*/ std::string RsRandom::printable(uint32_t length)
{
std::string ret(length, 0);
random_bytes(reinterpret_cast<uint8_t*>(&ret[0]), length);
for(uint32_t i=0; i<length; ++i) ret[i] = (ret[i] % 94) + 33;
return ret;
}

View File

@ -46,9 +46,19 @@ public:
static bool seed(uint32_t s);
static std::string random_alphaNumericString(uint32_t length);
static void random_bytes(uint8_t* data, uint32_t length);
/// Return a random alphanumeric *[0-9,A-Z,a-z] string of the given lenght
static std::string alphaNumeric(uint32_t length);
/** Return a random printable string of the given lenght */
static std::string printable(uint32_t length);
/** This return a printable string not an alphanumeric one @deprecated */
RS_DEPRECATED_FOR("RsRandom::printable")
static inline std::string random_alphaNumericString(uint32_t length)
{ return printable(length); }
private:
static RsMutex rndMtx;

View File

@ -22,6 +22,7 @@
/** \file
*/
#include <openssl/bio.h>
#include <openssl/md5.h>
#include <openssl/sha.h>
#include <openssl/dsa.h>

View File

@ -24,6 +24,7 @@
#include <QMenu>
#include <algorithm>
#include <memory>
#include <retroshare/rspeers.h>
#include <retroshare/rsidentity.h>
@ -696,34 +697,32 @@ void CreateCircleDialog::loadIdentities()
{
RsThread::async([this]()
{
std::list<RsGroupMetaData> ids_meta;
std::list<RsGroupMetaData> ids_meta;
if(!rsIdentity->getIdentitiesSummaries(ids_meta))
{
std::cerr << __PRETTY_FUNCTION__ << " failed to retrieve identities ids for all identities" << std::endl;
RS_ERR("failed to retrieve identities ids for all identities");
return;
}
std::set<RsGxsId> ids;
}
for(auto& meta:ids_meta)
ids.insert(RsGxsId(meta.mGroupId)) ;
std::set<RsGxsId> ids;
for(auto& meta:ids_meta) ids.insert(RsGxsId(meta.mGroupId));
std::vector<RsGxsIdGroup> id_groups;
if(!rsIdentity->getIdentitiesInfo(ids,id_groups))
auto id_groups = std::make_unique<std::vector<RsGxsIdGroup>>();
if(!rsIdentity->getIdentitiesInfo(ids, *id_groups))
{
std::cerr << __PRETTY_FUNCTION__ << " failed to retrieve identities group info for all identities" << std::endl;
RS_ERR("failed to retrieve identities group info for all identities");
return;
}
}
RsQThreadUtils::postToObject( [id_groups,this]()
RsQThreadUtils::postToObject(
[id_groups = std::move(id_groups), this]()
{
/* Here it goes any code you want to be executed on the Qt Gui
* thread, for example to update the data model with new information
* after a blocking call to RetroShare API complete */
fillIdentitiesList(id_groups) ;
fillIdentitiesList(*id_groups);
}, this );
});

View File

@ -340,6 +340,10 @@ void GenCertDialog::setupState()
ui.hiddenport_spinBox->setVisible(hidden_state && !tor_auto);
ui.cbUseBob->setVisible(hidden_state && !tor_auto);
#ifndef RS_USE_I2P_BOB
ui.cbUseBob->setDisabled(true);
ui.cbUseBob->setToolTip(tr("BOB support is not available"));
#endif
if(!mAllFieldsOk)
{

View File

@ -47,6 +47,7 @@
#include "util/misc.h"
#include "util/QtVersion.h"
#include "util/rstime.h"
#include "util/rsdebug.h"
#include "retroshare/rsgxsflags.h"
#include "retroshare/rsmsgs.h"
@ -55,6 +56,7 @@
#include <iostream>
#include <algorithm>
#include <memory>
/******
* #define ID_DEBUG 1
@ -506,21 +508,24 @@ void IdDialog::updateCircles()
std::cerr << "Retrieving post data for post " << mThreadId << std::endl;
#endif
std::list<RsGroupMetaData> circle_metas ;
/* This can be big so use a smart pointer to just copy the pointer
* instead of copying the whole list accross the lambdas */
auto circle_metas = std::make_unique<std::list<RsGroupMetaData>>();
if(!rsGxsCircles->getCirclesSummaries(circle_metas))
if(!rsGxsCircles->getCirclesSummaries(*circle_metas))
{
std::cerr << __PRETTY_FUNCTION__ << " failed to retrieve circles group info list" << std::endl;
RS_ERR("failed to retrieve circles group info list");
return;
}
}
RsQThreadUtils::postToObject( [circle_metas,this]()
RsQThreadUtils::postToObject(
[circle_metas = std::move(circle_metas), this]()
{
/* Here it goes any code you want to be executed on the Qt Gui
* thread, for example to update the data model with new information
* after a blocking call to RetroShare API complete */
loadCircles(circle_metas);
loadCircles(*circle_metas);
}, this );
@ -1305,19 +1310,17 @@ void IdDialog::updateIdList()
return;
}
std::map<RsGxsGroupId,RsGxsIdGroup> ids_set;
auto ids_set = std::make_unique<std::map<RsGxsGroupId,RsGxsIdGroup>>();
for(auto it(groups.begin()); it!=groups.end(); ++it)
(*ids_set)[(*it).mMeta.mGroupId] = *it;
for(auto it(groups.begin());it!=groups.end();++it)
ids_set[(*it).mMeta.mGroupId] = *it;
RsQThreadUtils::postToObject( [ids_set,this]()
RsQThreadUtils::postToObject(
[ids_set = std::move(ids_set), this] ()
{
/* Here it goes any code you want to be executed on the Qt Gui
* thread, for example to update the data model with new information
* after a blocking call to RetroShare API complete */
loadIdentities(ids_set);
loadIdentities(*ids_set);
}, this );
});
@ -1575,6 +1578,14 @@ void IdDialog::loadIdentities(const std::map<RsGxsGroupId,RsGxsIdGroup>& ids_set
/* count items */
int itemCount = contactsItem->childCount() + allItem->childCount() + ownItem->childCount();
ui->label_count->setText( "(" + QString::number( itemCount ) + ")" );
int contactsCount = contactsItem->childCount() ;
int allCount = allItem->childCount() ;
int ownCount = ownItem->childCount();
contactsItem->setText(0, tr("My contacts") + " (" + QString::number( contactsCount ) + ")" );
allItem->setText(0, tr("All") + " (" + QString::number( allCount ) + ")" );
ownItem->setText(0, tr("My own identities") + " (" + QString::number( ownCount ) + ")" );
navigate(RsGxsId(oldCurrentId));
filterIds();
@ -1851,10 +1862,17 @@ QString IdDialog::createUsageString(const RsIdentityUsage& u) const
{
case RsServiceType::CHANNELS: service_name = tr("Channels") ;service_type = RetroShareLink::TYPE_CHANNEL ; break ;
case RsServiceType::FORUMS: service_name = tr("Forums") ; service_type = RetroShareLink::TYPE_FORUM ; break ;
case RsServiceType::POSTED: service_name = tr("Posted") ; service_type = RetroShareLink::TYPE_POSTED ; break ;
case RsServiceType::POSTED: service_name = tr("Boards") ; service_type = RetroShareLink::TYPE_POSTED ; break ;
case RsServiceType::CHAT: service_name = tr("Chat") ; service_type = RetroShareLink::TYPE_CHAT_ROOM ; break ;
case RsServiceType::GXS_TRANS: return tr("GxsMail author ");
#ifdef TODO
// We need a RS link for circles if we want to do that.
//
case RsServiceType::GXSCIRCLE: service_name = tr("GxsCircles"); service_type = RetroShareLink::TYPE_CIRCLES; break ;
#endif
default:
service_name = tr("Unknown"); service_type = RetroShareLink::TYPE_UNKNOWN ;
service_name = tr("Unknown (service=")+QString::number((int)u.mServiceId,16)+")"; service_type = RetroShareLink::TYPE_UNKNOWN ;
}
switch(u.mUsageCode)
@ -1874,10 +1892,25 @@ QString IdDialog::createUsageString(const RsIdentityUsage& u) const
return tr("Group author for group %1 in service %2").arg(QString::fromStdString(u.mGrpId.toStdString())).arg(service_name);
break ;
case RsIdentityUsage::MESSAGE_AUTHOR_SIGNATURE_VALIDATION:
case RsIdentityUsage::MESSAGE_AUTHOR_KEEP_ALIVE: // Identities are stamped regularly by crawlign the set of messages for all groups. That helps keepign the useful identities in hand.
case RsIdentityUsage::MESSAGE_AUTHOR_KEEP_ALIVE: // Identities are stamped regularly by crawling the set of messages for all groups. That helps keepign the useful identities in hand.
{
RetroShareLink l = RetroShareLink::createGxsMessageLink(service_type,u.mGrpId,u.mMsgId,tr("Message/vote/comment"));
return tr("%1 in %2 tab").arg(l.toHtml()).arg(service_name) ;
RetroShareLink l;
std::cerr << "Signature validation/keep alive signature:" << std::endl;
std::cerr << " service ID = " << std::hex << (uint16_t)u.mServiceId << std::dec << std::endl;
std::cerr << " u.mGrpId = " << u.mGrpId << std::endl;
std::cerr << " u.mMsgId = " << u.mMsgId << std::endl;
std::cerr << " u.mParentId = " << u.mParentId << std::endl;
std::cerr << " u.mThreadId = " << u.mThreadId << std::endl;
if(service_type == RetroShareLink::TYPE_CHANNEL && !u.mThreadId.isNull())
l = RetroShareLink::createGxsMessageLink(service_type,u.mGrpId,u.mThreadId,tr("Vote/comment"));
else if(service_type == RetroShareLink::TYPE_POSTED && !u.mThreadId.isNull())
l = RetroShareLink::createGxsMessageLink(service_type,u.mGrpId,u.mThreadId,tr("Vote"));
else
l = RetroShareLink::createGxsMessageLink(service_type,u.mGrpId,u.mMsgId,tr("Message"));
return tr("%1 in %2 service").arg(l.toHtml()).arg(service_name) ;
}
case RsIdentityUsage::CHAT_LOBBY_MSG_VALIDATION: // Chat lobby msgs are signed, so each time one comes, or a chat lobby event comes, a signature verificaiton happens.
{
@ -1903,9 +1936,13 @@ QString IdDialog::createUsageString(const RsIdentityUsage& u) const
{
return tr("Signature in distant tunnel system.");
}
case RsIdentityUsage::IDENTITY_DATA_UPDATE: // Group update on that identity data. Can be avatar, name, etc.
case RsIdentityUsage::IDENTITY_NEW_FROM_GXS_SYNC: // Group update on that identity data. Can be avatar, name, etc.
{
return tr("Update of identity data.");
return tr("Received from GXS sync.");
}
case RsIdentityUsage::IDENTITY_NEW_FROM_DISCOVERY: // Own friend sended his own ids
{
return tr("Friend node identity received through discovery.");
}
case RsIdentityUsage::IDENTITY_GENERIC_SIGNATURE_CHECK: // Any signature verified for that identity
{
@ -1913,11 +1950,18 @@ QString IdDialog::createUsageString(const RsIdentityUsage& u) const
}
case RsIdentityUsage::IDENTITY_GENERIC_SIGNATURE_CREATION: // Any signature made by that identity
{
return tr("Generic signature.");
return tr("Generic signature creation (e.g. chat room message, global router,...).");
}
case RsIdentityUsage::IDENTITY_GENERIC_ENCRYPTION: return tr("Generic encryption.");
case RsIdentityUsage::IDENTITY_GENERIC_DECRYPTION: return tr("Generic decryption.");
case RsIdentityUsage::CIRCLE_MEMBERSHIP_CHECK: return tr("Membership verification in circle %1.").arg(QString::fromStdString(u.mGrpId.toStdString()));
case RsIdentityUsage::CIRCLE_MEMBERSHIP_CHECK:
{
RsGxsCircleDetails det;
if(rsGxsCircles->getCircleDetails(RsGxsCircleId(u.mGrpId),det))
return tr("Membership verification in circle \"%1\" (%2).").arg(QString::fromUtf8(det.mCircleName.c_str())).arg(QString::fromStdString(u.mGrpId.toStdString()));
else
return tr("Membership verification in circle (ID=%1).").arg(QString::fromStdString(u.mGrpId.toStdString()));
}
#warning TODO! csoler 2017-01-03: Add the different strings and translations here.
default:

View File

@ -210,8 +210,8 @@ void IdEditDialog::setupExistingId(const RsGxsGroupId& keyId)
RsThread::async([this,keyId]()
{
std::vector<RsGxsIdGroup> datavector;
bool res = rsIdentity->getIdentitiesInfo(std::set<RsGxsId>({(RsGxsId)keyId}),datavector);
bool res = rsIdentity->getIdentitiesInfo(
std::set<RsGxsId>({(RsGxsId)keyId}), datavector );
RsQThreadUtils::postToObject( [this,keyId,res,datavector]()
{

View File

@ -146,6 +146,8 @@ void PostedCardView::setup()
QAction *CopyLinkAction = new QAction(QIcon(""),tr("Copy RetroShare Link"), this);
connect(CopyLinkAction, SIGNAL(triggered()), this, SLOT(copyMessageLink()));
QAction *showInPeopleAct = new QAction(QIcon(), tr("Show author in people tab"), this);
connect(showInPeopleAct, SIGNAL(triggered()), this, SLOT(showAuthorInPeople()));
int S = QFontMetricsF(font()).height() ;
@ -157,6 +159,8 @@ void PostedCardView::setup()
QMenu *menu = new QMenu();
menu->addAction(CopyLinkAction);
menu->addSeparator();
menu->addAction(showInPeopleAct);
ui->shareButton->setMenu(menu);
ui->clearButton->hide();
@ -172,90 +176,106 @@ void PostedCardView::fill()
// return;
// }
QPixmap sqpixmap2 = QPixmap(":/images/thumb-default.png");
RsReputationLevel overall_reputation = rsReputations->overallReputationLevel(mPost.mMeta.mAuthorId);
bool redacted = (overall_reputation == RsReputationLevel::LOCALLY_NEGATIVE);
mInFill = true;
int desired_height = 1.5*(ui->voteDownButton->height() + ui->voteUpButton->height() + ui->scoreLabel->height());
int desired_width = sqpixmap2.width()*desired_height/(float)sqpixmap2.height();
QDateTime qtime;
qtime.setTime_t(mPost.mMeta.mPublishTs);
QString timestamp = qtime.toString("hh:mm dd-MMM-yyyy");
QString timestamp2 = misc::timeRelativeToNow(mPost.mMeta.mPublishTs);
ui->dateLabel->setText(timestamp2);
ui->dateLabel->setToolTip(timestamp);
ui->fromLabel->setId(mPost.mMeta.mAuthorId);
// Use QUrl to check/parse our URL
// The only combination that seems to work: load as EncodedUrl, extract toEncoded().
QByteArray urlarray(mPost.mLink.c_str());
QUrl url = QUrl::fromEncoded(urlarray.trimmed());
QString urlstr = "Invalid Link";
QString sitestr = "Invalid Link";
bool urlOkay = url.isValid();
if (urlOkay)
{
QString scheme = url.scheme();
if ((scheme != "https")
&& (scheme != "http")
&& (scheme != "ftp")
&& (scheme != "retroshare"))
{
urlOkay = false;
sitestr = "Invalid Link Scheme";
}
}
if (urlOkay)
{
urlstr = QString("<a href=\"");
urlstr += QString(url.toEncoded());
urlstr += QString("\" ><span style=\" text-decoration: underline; color:#2255AA;\"> ");
urlstr += messageName();
urlstr += QString(" </span></a>");
QString siteurl = url.toEncoded();
sitestr = QString("<a href=\"%1\" ><span style=\" text-decoration: underline; color:#0079d3;\"> %2 </span></a>").arg(siteurl).arg(siteurl);
ui->titleLabel->setText(urlstr);
}else
{
ui->titleLabel->setText(messageName());
}
if (urlarray.isEmpty())
{
ui->siteLabel->hide();
}
ui->siteLabel->setText(sitestr);
if(mPost.mImage.mData != NULL)
{
QPixmap pixmap;
GxsIdDetails::loadPixmapFromData(mPost.mImage.mData, mPost.mImage.mSize, pixmap,GxsIdDetails::ORIGINAL);
// Wiping data - as its been passed to thumbnail.
QPixmap scaledpixmap;
if(pixmap.width() > 800){
QPixmap scaledpixmap = pixmap.scaledToWidth(800, Qt::SmoothTransformation);
ui->pictureLabel->setPixmap(scaledpixmap);
}else{
ui->pictureLabel->setPixmap(pixmap);
}
}
else if (mPost.mImage.mData == NULL)
{
if(redacted) {
ui->commentButton->setDisabled(true);
ui->voteUpButton->setDisabled(true);
ui->voteDownButton->setDisabled(true);
ui->picture_frame->hide();
}
else
{
ui->picture_frame->show();
}
ui->fromLabel->setId(mPost.mMeta.mAuthorId);
ui->titleLabel->setText(tr( "<p><font color=\"#ff0000\"><b>The author of this message (with ID %1) is banned.</b>").arg(QString::fromStdString(mPost.mMeta.mAuthorId.toStdString()))) ;
QDateTime qtime;
qtime.setTime_t(mPost.mMeta.mPublishTs);
QString timestamp = qtime.toString("hh:mm dd-MMM-yyyy");
ui->dateLabel->setText(timestamp);
} else {
QPixmap sqpixmap2 = FilesDefs::getPixmapFromQtResourcePath(":/images/thumb-default.png");
mInFill = true;
int desired_height = 1.5*(ui->voteDownButton->height() + ui->voteUpButton->height() + ui->scoreLabel->height());
int desired_width = sqpixmap2.width()*desired_height/(float)sqpixmap2.height();
QDateTime qtime;
qtime.setTime_t(mPost.mMeta.mPublishTs);
QString timestamp = qtime.toString("hh:mm dd-MMM-yyyy");
QString timestamp2 = misc::timeRelativeToNow(mPost.mMeta.mPublishTs);
ui->dateLabel->setText(timestamp2);
ui->dateLabel->setToolTip(timestamp);
ui->fromLabel->setId(mPost.mMeta.mAuthorId);
// Use QUrl to check/parse our URL
// The only combination that seems to work: load as EncodedUrl, extract toEncoded().
QByteArray urlarray(mPost.mLink.c_str());
QUrl url = QUrl::fromEncoded(urlarray.trimmed());
QString urlstr = "Invalid Link";
QString sitestr = "Invalid Link";
bool urlOkay = url.isValid();
if (urlOkay)
{
QString scheme = url.scheme();
if ((scheme != "https")
&& (scheme != "http")
&& (scheme != "ftp")
&& (scheme != "retroshare"))
{
urlOkay = false;
sitestr = "Invalid Link Scheme";
}
}
if (urlOkay)
{
urlstr = QString("<a href=\"");
urlstr += QString(url.toEncoded());
urlstr += QString("\" ><span style=\" text-decoration: underline; color:#2255AA;\"> ");
urlstr += messageName();
urlstr += QString(" </span></a>");
QString siteurl = url.toEncoded();
sitestr = QString("<a href=\"%1\" ><span style=\" text-decoration: underline; color:#0079d3;\"> %2 </span></a>").arg(siteurl).arg(siteurl);
ui->titleLabel->setText(urlstr);
}else
{
ui->titleLabel->setText(messageName());
}
if (urlarray.isEmpty())
{
ui->siteLabel->hide();
}
ui->siteLabel->setText(sitestr);
if(mPost.mImage.mData != NULL)
{
QPixmap pixmap;
GxsIdDetails::loadPixmapFromData(mPost.mImage.mData, mPost.mImage.mSize, pixmap,GxsIdDetails::ORIGINAL);
// Wiping data - as its been passed to thumbnail.
QPixmap scaledpixmap;
if(pixmap.width() > 800){
QPixmap scaledpixmap = pixmap.scaledToWidth(800, Qt::SmoothTransformation);
ui->pictureLabel->setPixmap(scaledpixmap);
}else{
ui->pictureLabel->setPixmap(pixmap);
}
}
else if (mPost.mImage.mData == NULL)
{
ui->picture_frame->hide();
}
else
{
ui->picture_frame->show();
}
}
//QString score = "Hot" + QString::number(post.mHotScore);
//score += " Top" + QString::number(post.mTopScore);

View File

@ -32,6 +32,8 @@
#include "gui/common/FilesDefs.h"
#include "util/qtthreadsutils.h"
#include "util/HandleRichText.h"
#include "gui/MainWindow.h"
#include "gui/Identity/IdDialog.h"
#include "PhotoView.h"
#include "ui_PostedItem.h"
@ -338,6 +340,24 @@ void BasePostedItem::viewPicture()
/* window will destroy itself! */
}
void BasePostedItem::showAuthorInPeople()
{
if(mPost.mMeta.mAuthorId.isNull())
{
std::cerr << "(EE) GxsForumThreadWidget::loadMsgData_showAuthorInPeople() ERROR Missing Message Data...";
std::cerr << std::endl;
}
/* window will destroy itself! */
IdDialog *idDialog = dynamic_cast<IdDialog*>(MainWindow::getPage(MainWindow::People));
if (!idDialog)
return ;
MainWindow::showWindow(MainWindow::People);
idDialog->navigate(RsGxsId(mPost.mMeta.mAuthorId));
}
//========================================================================================
// PostedItem //
//========================================================================================
@ -394,6 +414,8 @@ void PostedItem::setup()
QAction *CopyLinkAction = new QAction(QIcon(""),tr("Copy RetroShare Link"), this);
connect(CopyLinkAction, SIGNAL(triggered()), this, SLOT(copyMessageLink()));
QAction *showInPeopleAct = new QAction(QIcon(), tr("Show author in people tab"), this);
connect(showInPeopleAct, SIGNAL(triggered()), this, SLOT(showAuthorInPeople()));
int S = QFontMetricsF(font()).height() ;
@ -407,6 +429,8 @@ void PostedItem::setup()
QMenu *menu = new QMenu();
menu->addAction(CopyLinkAction);
menu->addSeparator();
menu->addAction(showInPeopleAct);
ui->shareButton->setMenu(menu);
ui->clearButton->hide();
@ -438,8 +462,6 @@ void PostedItem::makeUpVote()
emit vote(msgId, true);
}
void PostedItem::setComment(const RsGxsComment& cmt)
{
ui->newCommentLabel->show();
@ -459,97 +481,115 @@ void PostedItem::setCommentsSize(int comNb)
void PostedItem::fill()
{
RetroShareLink link = RetroShareLink::createGxsGroupLink(RetroShareLink::TYPE_POSTED, mGroupMeta.mGroupId, groupName());
ui->nameLabel->setText(link.toHtml());
RsReputationLevel overall_reputation = rsReputations->overallReputationLevel(mPost.mMeta.mAuthorId);
bool redacted = (overall_reputation == RsReputationLevel::LOCALLY_NEGATIVE);
QPixmap sqpixmap2 = QPixmap(":/images/thumb-default.png");
if(redacted) {
ui->expandButton->setDisabled(true);
ui->commentButton->setDisabled(true);
ui->voteUpButton->setDisabled(true);
ui->voteDownButton->setDisabled(true);
mInFill = true;
int desired_height = 1.5*(ui->voteDownButton->height() + ui->voteUpButton->height() + ui->scoreLabel->height());
int desired_width = sqpixmap2.width()*desired_height/(float)sqpixmap2.height();
ui->thumbnailLabel->setPixmap( QPixmap(":/images/thumb-default.png"));
ui->fromLabel->setId(mPost.mMeta.mAuthorId);
ui->titleLabel->setText(tr( "<p><font color=\"#ff0000\"><b>The author of this message (with ID %1) is banned.</b>").arg(QString::fromStdString(mPost.mMeta.mAuthorId.toStdString()))) ;
QDateTime qtime;
qtime.setTime_t(mPost.mMeta.mPublishTs);
QString timestamp = qtime.toString("hh:mm dd-MMM-yyyy");
ui->dateLabel->setText(timestamp);
} else {
RetroShareLink link = RetroShareLink::createGxsGroupLink(RetroShareLink::TYPE_POSTED, mGroupMeta.mGroupId, groupName());
ui->nameLabel->setText(link.toHtml());
QDateTime qtime;
qtime.setTime_t(mPost.mMeta.mPublishTs);
QString timestamp = qtime.toString("hh:mm dd-MMM-yyyy");
QString timestamp2 = misc::timeRelativeToNow(mPost.mMeta.mPublishTs);
ui->dateLabel->setText(timestamp2);
ui->dateLabel->setToolTip(timestamp);
QPixmap sqpixmap2 = FilesDefs::getPixmapFromQtResourcePath(":/images/thumb-default.png");
ui->fromLabel->setId(mPost.mMeta.mAuthorId);
mInFill = true;
int desired_height = 1.5*(ui->voteDownButton->height() + ui->voteUpButton->height() + ui->scoreLabel->height());
int desired_width = sqpixmap2.width()*desired_height/(float)sqpixmap2.height();
// Use QUrl to check/parse our URL
// The only combination that seems to work: load as EncodedUrl, extract toEncoded().
QByteArray urlarray(mPost.mLink.c_str());
QUrl url = QUrl::fromEncoded(urlarray.trimmed());
QString urlstr = "Invalid Link";
QString sitestr = "Invalid Link";
QDateTime qtime;
qtime.setTime_t(mPost.mMeta.mPublishTs);
QString timestamp = qtime.toString("hh:mm dd-MMM-yyyy");
QString timestamp2 = misc::timeRelativeToNow(mPost.mMeta.mPublishTs);
ui->dateLabel->setText(timestamp2);
ui->dateLabel->setToolTip(timestamp);
bool urlOkay = url.isValid();
if (urlOkay)
{
QString scheme = url.scheme();
if ((scheme != "https")
&& (scheme != "http")
&& (scheme != "ftp")
&& (scheme != "retroshare"))
ui->fromLabel->setId(mPost.mMeta.mAuthorId);
// Use QUrl to check/parse our URL
// The only combination that seems to work: load as EncodedUrl, extract toEncoded().
QByteArray urlarray(mPost.mLink.c_str());
QUrl url = QUrl::fromEncoded(urlarray.trimmed());
QString urlstr = "Invalid Link";
QString sitestr = "Invalid Link";
bool urlOkay = url.isValid();
if (urlOkay)
{
urlOkay = false;
sitestr = "Invalid Link Scheme";
QString scheme = url.scheme();
if ((scheme != "https")
&& (scheme != "http")
&& (scheme != "ftp")
&& (scheme != "retroshare"))
{
urlOkay = false;
sitestr = "Invalid Link Scheme";
}
}
}
if (urlOkay)
{
urlstr = QString("<a href=\"");
urlstr += QString(url.toEncoded());
urlstr += QString("\" ><span style=\" text-decoration: underline; color:#2255AA;\"> ");
urlstr += messageName();
urlstr += QString(" </span></a>");
if (urlOkay)
{
urlstr = QString("<a href=\"");
urlstr += QString(url.toEncoded());
urlstr += QString("\" ><span style=\" text-decoration: underline; color:#2255AA;\"> ");
urlstr += messageName();
urlstr += QString(" </span></a>");
QString siteurl = url.toEncoded();
sitestr = QString("<a href=\"%1\" ><span style=\" text-decoration: underline; color:#0079d3;\"> %2 </span></a>").arg(siteurl).arg(siteurl);
QString siteurl = url.toEncoded();
sitestr = QString("<a href=\"%1\" ><span style=\" text-decoration: underline; color:#0079d3;\"> %2 </span></a>").arg(siteurl).arg(siteurl);
ui->titleLabel->setText(urlstr);
}else
{
ui->titleLabel->setText(messageName());
ui->titleLabel->setText(urlstr);
}else
{
ui->titleLabel->setText(messageName());
}
if (urlarray.isEmpty())
{
ui->siteLabel->hide();
}
ui->siteLabel->setText(sitestr);
if(mPost.mImage.mData != NULL)
{
QPixmap pixmap;
GxsIdDetails::loadPixmapFromData(mPost.mImage.mData, mPost.mImage.mSize, pixmap,GxsIdDetails::ORIGINAL);
// Wiping data - as its been passed to thumbnail.
QPixmap sqpixmap = pixmap.scaled(desired_width,desired_height, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
ui->thumbnailLabel->setPixmap(sqpixmap);
ui->thumbnailLabel->setToolTip(tr("Click to view Picture"));
QPixmap scaledpixmap;
if(pixmap.width() > 800){
QPixmap scaledpixmap = pixmap.scaledToWidth(800, Qt::SmoothTransformation);
ui->pictureLabel->setPixmap(scaledpixmap);
}else{
ui->pictureLabel->setPixmap(pixmap);
}
}
else if (urlOkay && (mPost.mImage.mData == NULL))
{
ui->expandButton->setDisabled(true);
ui->thumbnailLabel->setPixmap(FilesDefs::getPixmapFromQtResourcePath(LINK_IMAGE));
}
else
{
ui->expandButton->setDisabled(true);
ui->thumbnailLabel->setPixmap(sqpixmap2);
if (urlarray.isEmpty())
{
ui->siteLabel->hide();
}
ui->siteLabel->setText(sitestr);
if(mPost.mImage.mData != NULL)
{
QPixmap pixmap;
GxsIdDetails::loadPixmapFromData(mPost.mImage.mData, mPost.mImage.mSize, pixmap,GxsIdDetails::ORIGINAL);
// Wiping data - as its been passed to thumbnail.
QPixmap sqpixmap = pixmap.scaled(desired_width,desired_height, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
ui->thumbnailLabel->setPixmap(sqpixmap);
ui->thumbnailLabel->setToolTip(tr("Click to view Picture"));
QPixmap scaledpixmap;
if(pixmap.width() > 800){
QPixmap scaledpixmap = pixmap.scaledToWidth(800, Qt::SmoothTransformation);
ui->pictureLabel->setPixmap(scaledpixmap);
}else{
ui->pictureLabel->setPixmap(pixmap);
}
}
else if (urlOkay && (mPost.mImage.mData == NULL))
{
ui->expandButton->setDisabled(true);
ui->thumbnailLabel->setPixmap(FilesDefs::getPixmapFromQtResourcePath(LINK_IMAGE));
}
else
{
ui->expandButton->setDisabled(true);
ui->thumbnailLabel->setPixmap(sqpixmap2);
}
}
@ -701,5 +741,3 @@ void PostedItem::toggleNotes()
}
}

View File

@ -55,6 +55,7 @@ private slots:
void readAndClearItem();
void copyMessageLink();
void viewPicture();
void showAuthorInPeople();
signals:
void vote(const RsGxsGrpMsgIdPair& msgId, bool up);

View File

@ -298,199 +298,10 @@
<bool>false</bool>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="logoLabel">
<property name="minimumSize">
<size>
<width>64</width>
<height>64</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>64</width>
<height>64</height>
</size>
</property>
<property name="pixmap">
<pixmap resource="../icons.qrc">:/icons/png/postedlinks.png</pixmap>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="namelabel">
<property name="font">
<font>
<pointsize>14</pointsize>
</font>
</property>
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0">
<layout class="QFormLayout" name="formLayout">
<property name="topMargin">
<number>6</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label_4">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Popularity</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="poplabel">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>0</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="infoPostsLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Posts</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="infoPosts">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string notr="true">0</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="createdlabel">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Created</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLabel" name="createdinfolabel">
<property name="text">
<string>unknown</string>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Administrator:</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="GxsIdLabel" name="infoAdministrator">
<property name="text">
<string>unknown</string>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QLabel" name="label_3">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Distribution:</string>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="QLabel" name="infoDistribution">
<property name="text">
<string>unknown</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="infoLastPostLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Last Post:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLabel" name="infoLastPost">
<property name="text">
<string notr="true">unknown</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="1">
<property name="topMargin">
<number>6</number>
</property>
<item row="0" column="1">
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
@ -503,7 +314,7 @@
</property>
</spacer>
</item>
<item row="2" column="0" colspan="2">
<item row="1" column="0" colspan="2">
<widget class="QTextBrowser" name="infoDescription">
<property name="styleSheet">
<string notr="true"/>
@ -526,6 +337,194 @@ p, li { white-space: pre-wrap; }
</property>
</widget>
</item>
<item row="0" column="0">
<layout class="QFormLayout" name="formLayout">
<property name="topMargin">
<number>0</number>
</property>
<item row="1" column="0">
<widget class="QLabel" name="label_4">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Popularity</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="poplabel">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>0</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="infoPostsLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Posts</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="infoPosts">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string notr="true">0</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="createdlabel">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Created</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QLabel" name="createdinfolabel">
<property name="text">
<string>unknown</string>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QLabel" name="label">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Administrator:</string>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="GxsIdLabel" name="infoAdministrator">
<property name="text">
<string>unknown</string>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
</widget>
</item>
<item row="8" column="0">
<widget class="QLabel" name="label_3">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Distribution:</string>
</property>
</widget>
</item>
<item row="8" column="1">
<widget class="QLabel" name="infoDistribution">
<property name="text">
<string>unknown</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="infoLastPostLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Last Post:</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLabel" name="infoLastPost">
<property name="text">
<string notr="true">unknown</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="logoLabel">
<property name="minimumSize">
<size>
<width>64</width>
<height>64</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>64</width>
<height>64</height>
</size>
</property>
<property name="pixmap">
<pixmap resource="../icons.qrc">:/icons/png/postedlinks.png</pixmap>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="namelabel">
<property name="font">
<font>
<pointsize>14</pointsize>
</font>
</property>
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>

View File

@ -36,6 +36,7 @@
#include <retroshare/rsstatus.h>
#include <algorithm>
#include <memory>
#define COLUMN_NAME 0
#define COLUMN_CHECK 0
@ -250,24 +251,21 @@ void FriendSelectionWidget::loadIdentities()
if(!rsIdentity->getIdentitiesSummaries(ids_meta))
{
std::cerr << __PRETTY_FUNCTION__ << " failed to retrieve identities group info for all identities" << std::endl;
RS_ERR("failed to retrieve identities group info for all identities");
return;
}
std::vector<RsGxsGroupId> ids;
}
for(auto& meta:ids_meta)
ids.push_back(meta.mGroupId) ;
auto ids = std::make_unique<std::vector<RsGxsGroupId>>();
for(auto& meta: ids_meta) ids->push_back(meta.mGroupId);
RsQThreadUtils::postToObject( [ids,this]()
RsQThreadUtils::postToObject(
[ids = std::move(ids), this]()
{
/* Here it goes any code you want to be executed on the Qt Gui
* thread, for example to update the data model with new information
* after a blocking call to RetroShare API complete */
gxsIds = ids; // we do that is the GUI thread. Dont try it on another thread!
fillList() ;
// We do that is the GUI thread. Dont try it on another thread!
gxsIds = *ids;
/* TODO: To furter optimize away a copy gxsIds could be a unique_ptr
* too */
fillList();
}, this );
});
}

View File

@ -397,6 +397,19 @@ bool GroupTreeWidget::isSearchRequestResult(QPoint &point,QString& group_id,uint
return search_req_id > 0;
}
bool GroupTreeWidget::isSearchRequestResultItem(QTreeWidgetItem *item,QString& group_id,uint32_t& search_req_id)
{
QTreeWidgetItem *parent = item->parent();
if(parent == NULL)
return false ;
search_req_id = parent->data(COLUMN_DATA, ROLE_REQUEST_ID).toUInt();
group_id = itemId(item) ;
return search_req_id > 0;
}
bool GroupTreeWidget::isSearchRequestItem(QPoint &point,uint32_t& search_req_id)
{
QTreeWidgetItem *item = ui->treeWidget->itemAt(point);
@ -463,6 +476,17 @@ void GroupTreeWidget::fillGroupItems(QTreeWidgetItem *categoryItem, const QList<
item->setData(COLUMN_DATA, ROLE_NAME, itemInfo.name);
item->setData(COLUMN_DATA, ROLE_DESCRIPTION, itemInfo.description);
// Add children for context strings. This happens in the search.
while(nullptr != item->takeChild(0));
for(auto str:itemInfo.context_strings)
if(!str.empty())
{
QTreeWidgetItem *it = new QTreeWidgetItem(QStringList(QString::fromUtf8(str.c_str())));
it->setData(COLUMN_DATA,ROLE_ID,itemInfo.id);
item->addChild(it);
}
/* Set last post */
qlonglong lastPost = itemInfo.lastpost.toTime_t();
item->setData(COLUMN_DATA, ROLE_LASTPOST, -lastPost); // negative for correct sorting

View File

@ -21,6 +21,8 @@
#ifndef GROUPTREEWIDGET_H
#define GROUPTREEWIDGET_H
#include<set>
#include <QTreeWidgetItem>
#include <QDateTime>
@ -47,16 +49,17 @@ public:
{}
public:
QString id;
QString name;
QString description;
int popularity;
QDateTime lastpost;
QIcon icon;
bool publishKey;
bool adminKey;
quint32 subscribeFlags;
quint32 max_visible_posts ;
QString id;
QString name;
QString description;
int popularity;
QDateTime lastpost;
QIcon icon;
bool publishKey;
bool adminKey;
quint32 subscribeFlags;
quint32 max_visible_posts ;
std::set<std::string> context_strings;
};
//cppcheck-suppress noConstructor
@ -94,6 +97,7 @@ public:
bool isSearchRequestItem(QPoint &point,uint32_t& search_req_id);
bool isSearchRequestResult(QPoint &point, QString &group_id, uint32_t& search_req_id);
bool isSearchRequestResultItem(QTreeWidgetItem *item,QString& group_id,uint32_t& search_req_id);
QTreeWidgetItem *getItemFromId(const QString &id);
QTreeWidgetItem *activateId(const QString &id, bool focus);

View File

@ -19,10 +19,43 @@
*******************************************************************************/
#include <QPainter>
#include <QResizeEvent>
#include "RSTreeView.h"
RSTreeView::RSTreeView(QWidget *parent) : QTreeView(parent)
{
setMouseTracking(false); // normally the default, but who knows if it's not goign to change in the future.
}
void RSTreeView::wheelEvent(QWheelEvent *e)
{
if(e->modifiers() == Qt::ControlModifier)
emit zoomRequested(e->delta() > 0);
else
QTreeView::wheelEvent(e);
}
void RSTreeView::mouseMoveEvent(QMouseEvent *e)
{
QModelIndex idx = indexAt(e->pos());
if(idx != selectionModel()->currentIndex())
selectionModel()->setCurrentIndex(idx,QItemSelectionModel::ClearAndSelect);
QTreeView::mouseMoveEvent(e);
}
void RSTreeView::setAutoSelect(bool b)
{
if(b)
setMouseTracking(true);
else
setMouseTracking(false);
}
void RSTreeView::resizeEvent(QResizeEvent *e)
{
emit sizeChanged(e->size());
}
void RSTreeView::setPlaceholderText(const QString &text)

View File

@ -33,8 +33,20 @@ public:
void setPlaceholderText(const QString &text);
// Use this to make selection automatic based on mouse position. This is useful to trigger selection and therefore editing mode
// in trees that show editing widgets using a QStyledItemDelegate
void setAutoSelect(bool b);
signals:
void sizeChanged(QSize);
void zoomRequested(bool zoom_or_unzoom);
protected:
void paintEvent(QPaintEvent *event);
virtual void mouseMoveEvent(QMouseEvent *e) override; // overriding so as to manage auto-selection
virtual void wheelEvent(QWheelEvent *e) override; // overriding so as to manage zoom
virtual void resizeEvent(QResizeEvent *e) override;
virtual void paintEvent(QPaintEvent *event) override;
QString placeholderText;
};

View File

@ -23,6 +23,10 @@
#ifndef ELNODE_H
#define ELNODE_H
#include "graphwidget.h"
#include <retroshare/rstypes.h>
#include <QApplication>
#if QT_VERSION >= 0x040600
#include <QGraphicsObject>
@ -30,9 +34,7 @@
#include <QGraphicsItem>
#endif
#include <QList>
#include <retroshare/rstypes.h>
#include "graphwidget.h"
#include <QPainterPath>
class Edge;
QT_BEGIN_NAMESPACE

View File

@ -36,10 +36,12 @@ GxsCommentDialog::GxsCommentDialog(QWidget *parent, RsTokenService *token_servic
/* Invoke the Qt Designer generated QObject setup routine */
ui->setupUi(this);
//ui->postFrame->setVisible(false);
ui->treeWidget->setup(token_service, comment_service);
setTokenService(token_service,comment_service);
init();
}
void GxsCommentDialog::init()
{
/* Set header resize modes and initial section sizes */
QHeaderView * ttheader = ui->treeWidget->header () ;
ttheader->resizeSection (0, 440);
@ -62,6 +64,20 @@ GxsCommentDialog::GxsCommentDialog(QWidget *parent, RsTokenService *token_servic
ui->sortBox->setIconSize(QSize(S*1.5,S*1.5));
}
void GxsCommentDialog::setTokenService(RsTokenService *token_service, RsGxsCommentService *comment_service)
{
ui->treeWidget->setup(token_service, comment_service);
}
GxsCommentDialog::GxsCommentDialog(QWidget *parent)
: QWidget(parent), ui(new Ui::GxsCommentDialog)
{
/* Invoke the Qt Designer generated QObject setup routine */
ui->setupUi(this);
init();
}
GxsCommentDialog::~GxsCommentDialog()
{
delete(ui);

View File

@ -32,9 +32,11 @@ class GxsCommentDialog: public QWidget
Q_OBJECT
public:
GxsCommentDialog(QWidget *parent);
GxsCommentDialog(QWidget *parent, RsTokenService *token_service, RsGxsCommentService *comment_service);
virtual ~GxsCommentDialog();
void setTokenService(RsTokenService *token_service, RsGxsCommentService *comment_service);
void setCommentHeader(QWidget *header);
void commentLoad(const RsGxsGroupId &grpId, const std::set<RsGxsMessageId> &msg_versions, const RsGxsMessageId &most_recent_msgId);
@ -48,6 +50,8 @@ private slots:
void sortComments(int);
private:
void init();
RsGxsGroupId mGrpId;
RsGxsMessageId mMostRecentMsgId;
std::set<RsGxsMessageId> mMsgVersions;

View File

@ -18,6 +18,14 @@
* *
*******************************************************************************/
#include "GxsCommentTreeWidget.h"
#include "gui/common/FilesDefs.h"
#include "gui/common/RSElidedItemDelegate.h"
#include "gui/common/RSTreeWidgetItem.h"
#include "gui/gxs/GxsCreateCommentDialog.h"
#include "gui/gxs/GxsIdTreeWidgetItem.h"
#include <QAbstractTextDocumentLayout>
#include <QApplication>
#include <QClipboard>
@ -25,15 +33,9 @@
#include <QMenu>
#include <QMimeData>
#include <QPainter>
#include <QPainterPath>
#include <QTextDocument>
#include "gui/common/RSElidedItemDelegate.h"
#include "gui/common/FilesDefs.h"
#include "gui/gxs/GxsCommentTreeWidget.h"
#include "gui/gxs/GxsCreateCommentDialog.h"
#include "gui/gxs/GxsIdTreeWidgetItem.h"
#include "gui/common/RSTreeWidgetItem.h"
#include <iostream>
#define PCITEM_COLUMN_COMMENT 0

View File

@ -29,6 +29,7 @@
#include "gui/settings/rsharesettings.h"
#include "gui/RetroShareLink.h"
#include "gui/gxs/GxsGroupShareKey.h"
#include "gui/common/GroupTreeWidget.h"
#include "gui/common/RSTreeWidget.h"
#include "gui/notifyqt.h"
#include "gui/common/UIStateHelper.h"
@ -89,7 +90,6 @@ GxsGroupFrameDialog::GxsGroupFrameDialog(RsGxsIfaceHelper *ifaceImpl, QWidget *p
mSubscribedGroups = NULL;
mPopularGroups = NULL;
mOtherGroups = NULL;
mMessageWidget = NULL;
/* Setup Queue */
mInterface = ifaceImpl;
@ -251,6 +251,13 @@ void GxsGroupFrameDialog::processSettings(bool load)
Settings->endGroup();
}
bool GxsGroupFrameDialog::useTabs()
{
GroupFrameSettings groupFrameSettings;
return Settings->getGroupFrameSettings(groupFrameSettingsType(), groupFrameSettings) && groupFrameSettings.mOpenAllInNewTab;
}
void GxsGroupFrameDialog::settingsChanged()
{
GroupFrameSettings groupFrameSettings;
@ -262,17 +269,15 @@ void GxsGroupFrameDialog::settingsChanged()
void GxsGroupFrameDialog::setSingleTab(bool singleTab)
{
if (singleTab) {
if (!mMessageWidget) {
mMessageWidget = createMessageWidget(RsGxsGroupId());
// remove close button of the the first tab
ui->messageTabWidget->hideCloseButton(ui->messageTabWidget->indexOf(mMessageWidget));
}
} else {
if (mMessageWidget) {
delete(mMessageWidget);
mMessageWidget = NULL;
}
if (singleTab)
{
while(ui->messageTabWidget->count() > 1)
{
auto w = ui->messageTabWidget->widget(0) ;
ui->messageTabWidget->removeTab(0);
delete w;
}
ui->messageTabWidget->hideCloseButton(0);
}
}
@ -286,55 +291,50 @@ void GxsGroupFrameDialog::updateDisplay(bool complete)
if(complete) // || !getGrpIds().empty() || !getGrpIdsMeta().empty()) {
updateGroupSummary(); /* Update group list */
updateSearchResults() ;
// updateSearchResults() ;
}
void GxsGroupFrameDialog::updateSearchResults()
{
const std::set<TurtleRequestId>& reqs = getSearchRequests();
for(auto& it:mSearchGroupsItems)
updateSearchResults(it.first);
}
for(auto it(reqs.begin());it!=reqs.end();++it)
{
std::cerr << "updating search ID " << std::hex << *it << std::dec << std::endl;
void GxsGroupFrameDialog::updateSearchResults(const TurtleRequestId& sid)
{
std::cerr << "updating search ID " << std::hex << sid << std::dec << std::endl;
std::map<RsGxsGroupId,RsGxsGroupSummary> group_infos;
std::map<RsGxsGroupId,RsGxsGroupSearchResults> group_infos;
getDistantSearchResults(*it,group_infos) ;
getDistantSearchResults(sid,group_infos) ;
std::cerr << "retrieved " << std::endl;
std::cerr << "retrieved " << std::endl;
auto it2 = mSearchGroupsItems.find(*it);
auto it2 = mSearchGroupsItems.find(sid);
if(mSearchGroupsItems.end() == it2)
{
std::cerr << "GxsGroupFrameDialog::updateSearchResults(): received result notification for req " << std::hex << *it << std::dec << " but no item present!" << std::endl;
continue ; // we could create the item just as well but since this situation is not supposed to happen, I prefer to make this a failure case.
}
QList<GroupItemInfo> group_items ;
QList<GroupItemInfo> group_items ;
for(auto it3(group_infos.begin());it3!=group_infos.end();++it3)
{
std::cerr << " adding group " << it3->first << " " << it3->second.mGroupId << " \"" << it3->second.mGroupName << "\"" << std::endl;
for(auto s:it3->second.mSearchContexts)
std::cerr << " Context string \"" << s << "\"" << std::endl;
for(auto it3(group_infos.begin());it3!=group_infos.end();++it3)
if(mCachedGroupMetas.find(it3->first) == mCachedGroupMetas.end())
{
std::cerr << " adding new group " << it3->first << " "
<< it3->second.mGroupId << " \""
<< it3->second.mGroupName << "\"" << std::endl;
GroupItemInfo i;
i.id = QString(it3->second.mGroupId.toStdString().c_str());
i.name = QString::fromUtf8(it3->second.mGroupName.c_str());
i.popularity = 0; // could be set to the number of hits
i.lastpost = QDateTime::fromTime_t(it3->second.mLastMessageTs);
i.subscribeFlags = 0; // irrelevant here
i.publishKey = false ; // IS_GROUP_PUBLISHER(groupInfo.mSubscribeFlags);
i.adminKey = false ; // IS_GROUP_ADMIN(groupInfo.mSubscribeFlags);
i.max_visible_posts = it3->second.mNumberOfMessages;
i.context_strings = it3->second.mSearchContexts;
GroupItemInfo i;
i.id = QString(it3->second.mGroupId.toStdString().c_str());
i.name = QString::fromUtf8(it3->second.mGroupName.c_str());
i.popularity = 0; // could be set to the number of hits
i.lastpost = QDateTime::fromTime_t(it3->second.mLastMessageTs);
i.subscribeFlags = 0; // irrelevant here
i.publishKey = false ; // IS_GROUP_PUBLISHER(groupInfo.mSubscribeFlags);
i.adminKey = false ; // IS_GROUP_ADMIN(groupInfo.mSubscribeFlags);
i.max_visible_posts = it3->second.mNumberOfMessages;
group_items.push_back(i);
}
group_items.push_back(i);
}
ui->groupTreeWidget->fillGroupItems(it2->second, group_items);
}
ui->groupTreeWidget->fillGroupItems(it2->second, group_items);
}
void GxsGroupFrameDialog::todo()
@ -360,13 +360,22 @@ void GxsGroupFrameDialog::removeCurrentSearch()
mSearchGroupsItems.erase(it);
mKnownGroups.erase(search_request_id);
clearDistantSearchResults(search_request_id);
}
void GxsGroupFrameDialog::removeAllSearches()
{
for(auto it(mSearchGroupsItems.begin());it!=mSearchGroupsItems.end();++it)
ui->groupTreeWidget->removeSearchItem(it->second) ;
{
QString group_id;
TurtleRequestId search_request_id;
if(ui->groupTreeWidget->isSearchRequestResultItem(it->second,group_id,search_request_id))
clearDistantSearchResults(search_request_id);
ui->groupTreeWidget->removeSearchItem(it->second) ;
}
mSearchGroupsItems.clear();
mKnownGroups.clear();
}
@ -390,6 +399,7 @@ static uint32_t checkDelay(uint32_t time_in_secs)
return 365 * 86400;
}
void GxsGroupFrameDialog::groupTreeCustomPopupMenu(QPoint point)
{
// First separately handle the case of search top level items
@ -432,12 +442,10 @@ void GxsGroupFrameDialog::groupTreeCustomPopupMenu(QPoint point)
QMenu contextMnu(this);
QAction *action;
if (mMessageWidget) {
action = contextMnu.addAction(QIcon(IMAGE_TABNEW), tr("Open in new tab"), this, SLOT(openInNewTab()));
if (mGroupId.isNull() || messageWidget(mGroupId, true)) {
action->setEnabled(false);
}
}
action = contextMnu.addAction(QIcon(IMAGE_TABNEW), tr("Open in new tab"), this, SLOT(openInNewTab()));
if(mGroupId.isNull()) // dont enable the open in tab if a tab is already here
action->setEnabled(false);
if (isSubscribed) {
action = contextMnu.addAction(QIcon(IMAGE_UNSUBSCRIBE), tr("Unsubscribe"), this, SLOT(unsubscribeGroup()));
@ -673,7 +681,7 @@ bool GxsGroupFrameDialog::getCurrentGroupName(QString& name)
void GxsGroupFrameDialog::markMsgAsRead()
{
GxsMessageFrameWidget *msgWidget = messageWidget(mGroupId, false);
GxsMessageFrameWidget *msgWidget = messageWidget(mGroupId);
if (msgWidget) {
msgWidget->setAllMessagesRead(true);
}
@ -681,7 +689,7 @@ void GxsGroupFrameDialog::markMsgAsRead()
void GxsGroupFrameDialog::markMsgAsUnread()
{
GxsMessageFrameWidget *msgWidget = messageWidget(mGroupId, false);
GxsMessageFrameWidget *msgWidget = messageWidget(mGroupId);
if (msgWidget) {
msgWidget->setAllMessagesRead(false);
}
@ -759,7 +767,7 @@ bool GxsGroupFrameDialog::navigate(const RsGxsGroupId &groupId, const RsGxsMessa
changedCurrentGroup(groupIdString);
/* search exisiting tab */
GxsMessageFrameWidget *msgWidget = messageWidget(mGroupId, false);
GxsMessageFrameWidget *msgWidget = messageWidget(mGroupId);
if (!msgWidget) {
return false;
}
@ -771,17 +779,16 @@ bool GxsGroupFrameDialog::navigate(const RsGxsGroupId &groupId, const RsGxsMessa
return msgWidget->navigate(msgId);
}
GxsMessageFrameWidget *GxsGroupFrameDialog::messageWidget(const RsGxsGroupId &groupId, bool ownTab)
GxsMessageFrameWidget *GxsGroupFrameDialog::messageWidget(const RsGxsGroupId &groupId)
{
int tabCount = ui->messageTabWidget->count();
for (int index = 0; index < tabCount; ++index) {
for (int index = 0; index < tabCount; ++index)
{
GxsMessageFrameWidget *childWidget = dynamic_cast<GxsMessageFrameWidget*>(ui->messageTabWidget->widget(index));
if (ownTab && mMessageWidget && childWidget == mMessageWidget) {
continue;
}
if (childWidget && childWidget->groupId() == groupId) {
if (childWidget && childWidget->groupId() == groupId)
return childWidget;
}
}
return NULL;
@ -790,9 +797,9 @@ GxsMessageFrameWidget *GxsGroupFrameDialog::messageWidget(const RsGxsGroupId &gr
GxsMessageFrameWidget *GxsGroupFrameDialog::createMessageWidget(const RsGxsGroupId &groupId)
{
GxsMessageFrameWidget *msgWidget = createMessageFrameWidget(groupId);
if (!msgWidget) {
if (!msgWidget)
return NULL;
}
int index = ui->messageTabWidget->addTab(msgWidget, msgWidget->groupName(true));
ui->messageTabWidget->setTabIcon(index, msgWidget->groupIcon());
@ -817,40 +824,44 @@ GxsCommentDialog *GxsGroupFrameDialog::commentWidget(const RsGxsMessageId& msgId
return NULL;
}
void GxsGroupFrameDialog::changedCurrentGroup(const QString &groupId)
void GxsGroupFrameDialog::changedCurrentGroup(const QString& groupId)
{
if (mInFill) {
return;
}
if (groupId.isEmpty()) {
if (mMessageWidget) {
mMessageWidget->setGroupId(RsGxsGroupId());
ui->messageTabWidget->setCurrentWidget(mMessageWidget);
}
if (groupId.isEmpty())
{
auto w = currentWidget();
if(w)
w->setGroupId(RsGxsGroupId());
return;
}
mGroupId = RsGxsGroupId(groupId.toStdString());
if (mGroupId.isNull()) {
if (mGroupId.isNull())
return;
}
/* search exisiting tab */
GxsMessageFrameWidget *msgWidget = messageWidget(mGroupId, true);
GxsMessageFrameWidget *msgWidget = messageWidget(mGroupId);
if (!msgWidget) {
if (mMessageWidget) {
/* not found, use standard tab */
msgWidget = mMessageWidget;
msgWidget->setGroupId(mGroupId);
} else {
/* create new tab */
msgWidget = createMessageWidget(mGroupId);
// check that we have at least one tab
if(msgWidget)
ui->messageTabWidget->setCurrentWidget(msgWidget);
else
{
if(useTabs() || ui->messageTabWidget->count()==0)
{
msgWidget = createMessageWidget(RsGxsGroupId(groupId.toStdString()));
ui->messageTabWidget->setCurrentWidget(msgWidget);
}
else
currentWidget()->setGroupId(mGroupId);
}
ui->messageTabWidget->setCurrentWidget(msgWidget);
}
void GxsGroupFrameDialog::groupTreeMiddleButtonClicked(QTreeWidgetItem *item)
@ -870,37 +881,31 @@ void GxsGroupFrameDialog::openGroupInNewTab(const RsGxsGroupId &groupId)
}
/* search exisiting tab */
GxsMessageFrameWidget *msgWidget = messageWidget(groupId, true);
if (!msgWidget) {
/* not found, create new tab */
msgWidget = createMessageWidget(groupId);
}
GxsMessageFrameWidget *msgWidget = createMessageWidget(groupId);
ui->messageTabWidget->setCurrentWidget(msgWidget);
}
void GxsGroupFrameDialog::messageTabCloseRequested(int index)
{
QWidget *widget = ui->messageTabWidget->widget(index);
if (!widget) {
if(ui->messageTabWidget->count() == 1) /* Don't close single tab */
return;
}
GxsMessageFrameWidget *msgWidget = dynamic_cast<GxsMessageFrameWidget*>(widget);
if (msgWidget && msgWidget == mMessageWidget) {
/* Don't close single tab */
return;
}
GxsMessageFrameWidget *msgWidget = dynamic_cast<GxsMessageFrameWidget*>(ui->messageTabWidget->widget(index));
delete msgWidget ;
}
delete(widget);
GxsMessageFrameWidget *GxsGroupFrameDialog::currentWidget() const
{
return dynamic_cast<GxsMessageFrameWidget*>(ui->messageTabWidget->widget(ui->messageTabWidget->currentIndex()));
}
void GxsGroupFrameDialog::messageTabChanged(int index)
{
GxsMessageFrameWidget *msgWidget = dynamic_cast<GxsMessageFrameWidget*>(ui->messageTabWidget->widget(index));
if (!msgWidget) {
if (!msgWidget)
return;
}
ui->groupTreeWidget->activateId(QString::fromStdString(msgWidget->groupId().toStdString()), false);
}
@ -1074,15 +1079,20 @@ void GxsGroupFrameDialog::updateGroupSummary()
{
RsThread::async([this]()
{
std::list<RsGxsGenericGroupData*> groupInfo;
auto groupInfo = new std::list<RsGxsGenericGroupData*>() ;
if(!getGroupData(groupInfo))
if(!getGroupData(*groupInfo))
{
std::cerr << __PRETTY_FUNCTION__ << " failed to collect group info " << std::endl;
std::cerr << __PRETTY_FUNCTION__ << " failed to collect group info." << std::endl;
delete groupInfo;
return;
}
if(groupInfo.empty())
if(groupInfo->empty())
{
std::cerr << __PRETTY_FUNCTION__ << " no group info collected." << std::endl;
delete groupInfo;
return;
}
RsQThreadUtils::postToObject( [this,groupInfo]()
{
@ -1092,7 +1102,7 @@ void GxsGroupFrameDialog::updateGroupSummary()
* Qt::QueuedConnection is important!
*/
insertGroupsData(groupInfo);
insertGroupsData(*groupInfo);
updateSearchResults();
mStateHelper->setLoading(TOKEN_TYPE_GROUP_SUMMARY, false);
@ -1111,12 +1121,14 @@ void GxsGroupFrameDialog::updateGroupSummary()
// now delete the data that is not used anymore
for(auto& g:groupInfo)
for(auto& g:*groupInfo)
{
mCachedGroupMetas[g->mMeta.mGroupId] = g->mMeta;
delete g;
}
delete groupInfo;
}, this );
});
}

View File

@ -161,7 +161,9 @@ private:
virtual void groupTreeCustomActions(RsGxsGroupId /*grpId*/, int /*subscribeFlags*/, QList<QAction*> &/*actions*/) {}
virtual RsGxsCommentService *getCommentService() { return NULL; }
virtual QWidget *createCommentHeaderWidget(const RsGxsGroupId &/*grpId*/, const RsGxsMessageId &/*msgId*/) { return NULL; }
virtual bool getDistantSearchResults(TurtleRequestId /* id */, std::map<RsGxsGroupId,RsGxsGroupSummary>& /* group_infos */){ return false ;}
virtual bool getDistantSearchResults(TurtleRequestId /* id */, std::map<RsGxsGroupId,RsGxsGroupSearchResults>& /* group_infos */){ return false ;}
virtual void clearDistantSearchResults(TurtleRequestId /* id */) {}
virtual RsGxsGenericGroupData *getDistantSearchResultGroupData(const RsGxsGroupId& group_id){ return nullptr ;}
void initUi();
@ -181,24 +183,27 @@ private:
// subscribe/unsubscribe ack.
GxsMessageFrameWidget *messageWidget(const RsGxsGroupId &groupId, bool ownTab);
GxsMessageFrameWidget *messageWidget(const RsGxsGroupId &groupId);
GxsMessageFrameWidget *createMessageWidget(const RsGxsGroupId &groupId);
GxsCommentDialog *commentWidget(const RsGxsMessageId &msgId);
protected:
void updateSearchResults();
void updateSearchResults(const TurtleRequestId &sid);
void updateSearchResults(); // update all searches
bool mCountChildMsgs; // Count unread child messages?
private:
GxsMessageFrameWidget *currentWidget() const;
bool useTabs();
bool mInitialized;
bool mInFill;
bool mDistSyncAllowed;
QString mSettingsName;
RsGxsGroupId mGroupId;
RsGxsIfaceHelper *mInterface;
GxsMessageFrameWidget *mMessageWidget;
QTreeWidgetItem *mYourGroups;
QTreeWidgetItem *mSubscribedGroups;

View File

@ -18,21 +18,23 @@
* *
*******************************************************************************/
#include <QApplication>
#include <QThread>
#include <QTimerEvent>
#include <QMutexLocker>
#include <math.h>
#include <util/rsdir.h>
#include "gui/common/AvatarDialog.h"
#include "GxsIdDetails.h"
#include "gui/common/AvatarDialog.h"
#include "retroshare-gui/RsAutoUpdatePage.h"
#include <retroshare/rspeers.h>
#include <util/rsdir.h>
#include <QApplication>
#include <QMutexLocker>
#include <QPainter>
#include <QPainterPath>
#include <QThread>
#include <QTimerEvent>
#include <iostream>
#include <QPainter>
#include <cmath>
/* Images for tag icons */
#define IMAGE_LOADING ":/images/folder-draft.png"

View File

@ -55,6 +55,7 @@ signals:
void groupChanged(QWidget *widget);
void waitingChanged(QWidget *widget);
void loadComment(const RsGxsGroupId &groupId, const QVector<RsGxsMessageId>& msg_versions,const RsGxsMessageId &msgId, const QString &title);
void groupDataLoaded();
protected:
virtual void setAllMessagesReadDo(bool read, uint32_t &token) = 0;

View File

@ -72,7 +72,8 @@ CreateGxsChannelMsg::CreateGxsChannelMsg(const RsGxsGroupId &cId, RsGxsMessageId
connect(addFileButton, SIGNAL(clicked() ), this , SLOT(addExtraFile()));
connect(addfilepushButton, SIGNAL(clicked() ), this , SLOT(addExtraFile()));
connect(subjectEdit,SIGNAL(textChanged(const QString&)),this,SLOT(updatePreviewText(const QString&)));
connect(addThumbnailButton, SIGNAL(clicked() ), this , SLOT(addThumbnail()));
connect(thumbNailCb, SIGNAL(toggled(bool)), this, SLOT(allowAutoMediaThumbNail(bool)));
connect(stackedWidget, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextMenu(QPoint)));
@ -605,6 +606,11 @@ void CreateGxsChannelMsg::saveChannelInfo(const RsGroupMetaData &meta)
subjectEdit->setFocus();
}
void CreateGxsChannelMsg::updatePreviewText(const QString& s)
{
preview_W->setText(s);
}
void CreateGxsChannelMsg::sendMsg()
{
#ifdef DEBUG_CREATE_GXS_MSG
@ -717,7 +723,7 @@ void CreateGxsChannelMsg::sendMessage(const std::string &subject, const std::str
void CreateGxsChannelMsg::addThumbnail()
{
QPixmap img = misc::getOpenThumbnailedPicture(this, tr("Load thumbnail picture"), 156, 107);
QPixmap img = misc::getOpenThumbnailedPicture(this, tr("Load thumbnail picture"), 107,156); // these absolute sizes are terrible
if (img.isNull())
return;
@ -725,7 +731,7 @@ void CreateGxsChannelMsg::addThumbnail()
picture = img;
// to show the selected
thumbnail_label->setPixmap(picture);
preview_W->setPixmap(picture);
}
void CreateGxsChannelMsg::loadOriginalChannelPostInfo()
@ -769,7 +775,7 @@ void CreateGxsChannelMsg::loadOriginalChannelPostInfo()
if(post.mThumbnail.mData != NULL)
{
GxsIdDetails::loadPixmapFromData(post.mThumbnail.mData,post.mThumbnail.mSize,picture,GxsIdDetails::ORIGINAL);
thumbnail_label->setPixmap(picture);
preview_W->setPixmap(picture);
}

View File

@ -58,6 +58,7 @@ private slots:
void addExtraFile();
void checkAttachmentReady();
void deleteAttachment();
void updatePreviewText(const QString &);
void cancelMsg();
void sendMsg();

View File

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>671</width>
<height>513</height>
<width>736</width>
<height>271</height>
</rect>
</property>
<property name="acceptDrops">
@ -20,7 +20,7 @@
<iconset resource="../images.qrc">
<normaloff>:/images/logo/logo_16.png</normaloff>:/images/logo/logo_16.png</iconset>
</property>
<layout class="QGridLayout" name="CreateGxsChannelMsgGLayout">
<layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
<number>0</number>
</property>
@ -33,7 +33,10 @@
<property name="bottomMargin">
<number>0</number>
</property>
<property name="spacing">
<property name="horizontalSpacing">
<number>6</number>
</property>
<property name="verticalSpacing">
<number>0</number>
</property>
<item row="0" column="0">
@ -52,53 +55,15 @@
<enum>QFrame::Raised</enum>
</property>
<layout class="QGridLayout" name="gridLayout_4">
<item row="0" column="0">
<widget class="QPushButton" name="channelpostButton">
<property name="text">
<string>Channel Post</string>
</property>
<property name="icon">
<iconset resource="../icons.qrc">
<normaloff>:/icons/png/comment.png</normaloff>:/icons/png/comment.png</iconset>
</property>
<property name="iconSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QPushButton" name="attachmentsButton">
<property name="text">
<string>Attachments</string>
</property>
<property name="icon">
<iconset resource="../icons.qrc">
<normaloff>:/icons/png/attachements.png</normaloff>:/icons/png/attachements.png</iconset>
</property>
<property name="iconSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
</widget>
</item>
<item row="0" column="2">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>486</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<property name="topMargin">
<number>3</number>
</property>
<property name="horizontalSpacing">
<number>6</number>
</property>
<property name="verticalSpacing">
<number>0</number>
</property>
<item row="1" column="0" colspan="3">
<widget class="QStackedWidget" name="stackedWidget">
<property name="mouseTracking">
@ -114,7 +79,10 @@
<number>0</number>
</property>
<widget class="QWidget" name="stackedWidgetPage1">
<layout class="QGridLayout" name="gridLayout_3">
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="spacing">
<number>6</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
@ -127,146 +95,158 @@
<property name="bottomMargin">
<number>0</number>
</property>
<item row="1" column="0">
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0" rowspan="2">
<widget class="QLabel" name="thumbnail_label">
<property name="maximumSize">
<size>
<width>156</width>
<height>107</height>
</size>
</property>
<property name="sizeIncrement">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="pixmap">
<pixmap resource="../images.qrc">:/images/thumb-default-video.png</pixmap>
</property>
</widget>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="spacing">
<number>6</number>
</property>
<property name="sizeConstraint">
<enum>QLayout::SetFixedSize</enum>
</property>
<item>
<widget class="ChannelPostThumbnailView" name="preview_W" native="true"/>
</item>
<item row="0" column="1" colspan="4">
<widget class="QLabel" name="channelAttachLabel">
<property name="text">
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>6</number>
</property>
<item>
<layout class="QHBoxLayout" name="channelNameHLayout">
<property name="topMargin">
<number>6</number>
</property>
<item>
<widget class="QLabel" name="channelNameLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Channel Post to:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="channelName">
<property name="enabled">
<bool>true</bool>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="channelAttachLabel">
<property name="text">
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:10pt; font-weight:600;&quot;&gt;Attachments:&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;img src=&quot;:/images/feedback_arrow.png&quot; /&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:8pt;&quot;&gt; Use Drag and Drop / Add Files button, to Hash new files.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;img src=&quot;:/images/feedback_arrow.png&quot; /&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:8pt;&quot;&gt; Copy/Paste RetroShare links from your shares&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="addThumbnailButton">
<property name="text">
<string>Add Channel Thumbnail</string>
</property>
<property name="icon">
<iconset resource="../icons.qrc">
<normaloff>:/icons/png/add-image.png</normaloff>:/icons/png/add-image.png</iconset>
</property>
<property name="iconSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="addfilepushButton">
<property name="text">
<string>Add File to Attach</string>
</property>
<property name="icon">
<iconset resource="../icons.qrc">
<normaloff>:/icons/png/add-file.png</normaloff>:/icons/png/add-file.png</iconset>
</property>
<property name="iconSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
</widget>
</item>
<item row="1" column="3" colspan="2">
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="addThumbnailButton">
<property name="text">
<string>Add Channel Thumbnail</string>
</property>
<property name="icon">
<iconset resource="../icons.qrc">
<normaloff>:/icons/png/add-image.png</normaloff>:/icons/png/add-image.png</iconset>
</property>
<property name="iconSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="addfilepushButton">
<property name="text">
<string>Add File to Attach</string>
</property>
<property name="icon">
<iconset resource="../icons.qrc">
<normaloff>:/icons/png/add-file.png</normaloff>:/icons/png/add-file.png</iconset>
</property>
<property name="iconSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_3">
<property name="topMargin">
<number>9</number>
</property>
<item>
<widget class="QLineEdit" name="subjectEdit">
<property name="placeholderText">
<string>Title</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>1</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</item>
<item row="2" column="0">
<widget class="QGroupBox" name="messageGBox">
<property name="title">
<string>Message</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="bottomMargin">
<number>0</number>
</property>
<item row="0" column="0">
<widget class="QLineEdit" name="subjectEdit">
<property name="placeholderText">
<string>Title</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="RichTextEdit" name="RichTextEditWidget" native="true"/>
</item>
</layout>
</widget>
</item>
<item row="0" column="0">
<layout class="QHBoxLayout" name="channelNameHLayout">
<item>
<widget class="QLabel" name="channelNameLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Channel Post to:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="channelName">
<property name="enabled">
<bool>true</bool>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
<item>
<widget class="RichTextEdit" name="RichTextEditWidget" native="true"/>
</item>
</layout>
</widget>
@ -276,7 +256,7 @@ p, li { white-space: pre-wrap; }
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
<number>6</number>
</property>
<property name="rightMargin">
<number>0</number>
@ -329,7 +309,7 @@ p, li { white-space: pre-wrap; }
<rect>
<x>0</x>
<y>0</y>
<width>632</width>
<width>81</width>
<height>24</height>
</rect>
</property>
@ -403,6 +383,53 @@ p, li { white-space: pre-wrap; }
</widget>
</widget>
</item>
<item row="0" column="0">
<widget class="QPushButton" name="channelpostButton">
<property name="text">
<string>Channel Post</string>
</property>
<property name="icon">
<iconset resource="../icons.qrc">
<normaloff>:/icons/png/comment.png</normaloff>:/icons/png/comment.png</iconset>
</property>
<property name="iconSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
</widget>
</item>
<item row="0" column="2">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>486</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="1">
<widget class="QPushButton" name="attachmentsButton">
<property name="text">
<string>Attachments</string>
</property>
<property name="icon">
<iconset resource="../icons.qrc">
<normaloff>:/icons/png/attachements.png</normaloff>:/icons/png/attachements.png</iconset>
</property>
<property name="iconSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
</widget>
</item>
<item row="2" column="0" colspan="3">
<layout class="QHBoxLayout" name="buttonHLayout">
<item>
@ -455,10 +482,16 @@ p, li { white-space: pre-wrap; }
<header>util/RichTextEdit.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>ChannelPostThumbnailView</class>
<extends>QWidget</extends>
<header>gui/gxschannels/GxsChannelPostThumbnail.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources>
<include location="../icons.qrc"/>
<include location="../images.qrc"/>
<include location="../icons.qrc"/>
</resources>
<connections/>
</ui>

View File

@ -26,7 +26,7 @@
#include "GxsChannelDialog.h"
#include "GxsChannelGroupDialog.h"
#include "GxsChannelPostsWidget.h"
#include "GxsChannelPostsWidgetWithModel.h"
#include "CreateGxsChannelMsg.h"
#include "GxsChannelUserNotify.h"
#include "gui/gxs/GxsGroupShareKey.h"
@ -61,9 +61,7 @@ void GxsChannelDialog::handleEvent_main_thread(std::shared_ptr<const RsEvent> ev
{
const RsGxsChannelEvent *e = dynamic_cast<const RsGxsChannelEvent*>(event.get());
if(!e)
return;
if(e)
switch(e->mChannelEventCode)
{
case RsChannelEventCode::NEW_MESSAGE: // [[fallthrough]];
@ -72,11 +70,6 @@ void GxsChannelDialog::handleEvent_main_thread(std::shared_ptr<const RsEvent> ev
updateGroupStatisticsReal(e->mChannelGroupId); // update the list immediately
break;
case RsChannelEventCode::RECEIVED_DISTANT_SEARCH_RESULT:
mSearchResults.insert(e->mDistantSearchRequestId);
updateSearchResults();
break;
case RsChannelEventCode::NEW_CHANNEL: // [[fallthrough]];
case RsChannelEventCode::SUBSCRIBE_STATUS_CHANGED:
updateDisplay(true);
@ -89,6 +82,13 @@ void GxsChannelDialog::handleEvent_main_thread(std::shared_ptr<const RsEvent> ev
default:
break;
}
const RsGxsChannelSearchResultEvent*f = dynamic_cast<const RsGxsChannelSearchResultEvent*>(event.get());
if(nullptr != f)
for(auto it:f->mSearchResultsMap)
updateSearchResults(it.first);
}
GxsChannelDialog::~GxsChannelDialog()
@ -204,7 +204,7 @@ int GxsChannelDialog::shareKeyType()
GxsMessageFrameWidget *GxsChannelDialog::createMessageFrameWidget(const RsGxsGroupId &groupId)
{
return new GxsChannelPostsWidget(groupId);
return new GxsChannelPostsWidgetWithModel(groupId,this);
}
void GxsChannelDialog::setDefaultDirectory()
@ -396,21 +396,36 @@ void GxsChannelDialog::groupInfoToGroupItemInfo(const RsGxsGenericGroupData *gro
groupItemInfo.description = QString::fromUtf8(channelGroupData->mDescription.c_str());
}
void GxsChannelDialog::clearDistantSearchResults(TurtleRequestId id)
{
rsGxsChannels->clearDistantSearchResults(id);
}
TurtleRequestId GxsChannelDialog::distantSearch(const QString& search_string)
{
return rsGxsChannels->turtleSearchRequest(search_string.toStdString()) ;
}
bool GxsChannelDialog::getDistantSearchResults(TurtleRequestId id, std::map<RsGxsGroupId,RsGxsGroupSummary>& group_infos)
bool GxsChannelDialog::getDistantSearchResults(TurtleRequestId id, std::map<RsGxsGroupId,RsGxsGroupSearchResults>& group_infos)
{
return rsGxsChannels->retrieveDistantSearchResults(id,group_infos);
}
RsGxsGenericGroupData *GxsChannelDialog::getDistantSearchResultGroupData(const RsGxsGroupId& group_id)
{
RsGxsChannelGroup channel_group;
if(rsGxsChannels->getDistantSearchResultGroupData(group_id,channel_group))
return new RsGxsGenericGroupData(channel_group);
else
return nullptr;
}
void GxsChannelDialog::checkRequestGroup(const RsGxsGroupId& grpId)
{
RsGxsChannelGroup distant_group;
if( rsGxsChannels->retrieveDistantGroup(grpId,distant_group)) // normally we should also check that the group meta is not already here.
if( rsGxsChannels->getDistantSearchResultGroupData(grpId,distant_group)) // normally we should also check that the group meta is not already here.
{
std::cerr << "GxsChannelDialog::checkRequestGroup() sending turtle request for group data for group " << grpId << std::endl;
rsGxsChannels->turtleGroupRequest(grpId);

View File

@ -43,10 +43,12 @@ public:
protected:
/* GxsGroupFrameDialog */
virtual bool getDistantSearchResults(TurtleRequestId id, std::map<RsGxsGroupId,RsGxsGroupSummary>& group_infos);
virtual bool getDistantSearchResults(TurtleRequestId id, std::map<RsGxsGroupId, RsGxsGroupSearchResults> &group_infos) override;
virtual RsGxsGenericGroupData *getDistantSearchResultGroupData(const RsGxsGroupId& group_id) override;
virtual TurtleRequestId distantSearch(const QString& search_string) ;
virtual void checkRequestGroup(const RsGxsGroupId& grpId) ;
virtual TurtleRequestId distantSearch(const QString& search_string) override;
virtual void checkRequestGroup(const RsGxsGroupId& grpId) override ;
virtual void clearDistantSearchResults(TurtleRequestId id) override;
// Implementation of some abstract methods in GxsGroupFrameDialog

View File

@ -18,6 +18,7 @@
* *
*******************************************************************************/
#include <QMenu>
#include <QTimer>
#include <QMessageBox>
#include <QFileInfo>
@ -27,11 +28,13 @@
#include "GxsChannelFilesStatusWidget.h"
#include "ui_GxsChannelFilesStatusWidget.h"
#include "gui/common/RsUrlHandler.h"
#include "gui/common/FilesDefs.h"
#include "util/misc.h"
#include "retroshare/rsfiles.h"
GxsChannelFilesStatusWidget::GxsChannelFilesStatusWidget(const RsGxsGroupId &groupId, const RsGxsMessageId &messageId, const RsGxsFile &file, QWidget *parent) :
QWidget(parent), mGroupId(groupId), mMessageId(messageId), mFile(file), ui(new Ui::GxsChannelFilesStatusWidget)
GxsChannelFilesStatusWidget::GxsChannelFilesStatusWidget(const RsGxsFile &file, QWidget *parent) :
QWidget(parent), mFile(file), ui(new Ui::GxsChannelFilesStatusWidget)
{
ui->setupUi(this);
@ -40,11 +43,21 @@ GxsChannelFilesStatusWidget::GxsChannelFilesStatusWidget(const RsGxsGroupId &gro
setSize(mFile.mSize);
/* Connect signals */
connect(ui->downloadToolButton, SIGNAL(clicked()), this, SLOT(download()));
connect(ui->downloadPushButton, SIGNAL(clicked()), this, SLOT(download()));
connect(ui->resumeToolButton, SIGNAL(clicked()), this, SLOT(resume()));
connect(ui->pauseToolButton, SIGNAL(clicked()), this, SLOT(pause()));
connect(ui->cancelToolButton, SIGNAL(clicked()), this, SLOT(cancel()));
connect(ui->openFolderToolButton, SIGNAL(clicked()), this, SLOT(openFolder()));
connect(ui->openFilePushButton, SIGNAL(clicked()), this, SLOT(openFile()));
ui->downloadPushButton->setIcon(FilesDefs::getIconFromQtResourcePath(":/icons/png/download.png"));
ui->openFolderToolButton->setIcon(FilesDefs::getIconFromQtResourcePath(":/icons/png/arrow.png"));
QAction *openfolder = new QAction(tr("Open folder"), this);
connect(openfolder, SIGNAL(triggered()), this, SLOT(openFolder()));
QMenu *menu = new QMenu();
menu->addAction(openfolder);
ui->openFolderToolButton->setMenu(menu);
check();
}
@ -80,6 +93,17 @@ void GxsChannelFilesStatusWidget::check()
if (rsFiles->alreadyHaveFile(mFile.mHash, fileInfo)) {
mState = STATE_LOCAL;
setSize(fileInfo.size);
/* check if the file is a media file */
if (!misc::isPreviewable(QFileInfo(QString::fromUtf8(fileInfo.path.c_str())).suffix()))
{
/* check if the file is not a media file and change text */
ui->openFilePushButton->setText(tr("Open file"));
} else {
ui->openFilePushButton->setText(tr("Play"));
ui->openFilePushButton->setIcon(FilesDefs::getIconFromQtResourcePath(":/icons/png/play.png"));
}
} else {
FileInfo fileInfo;
bool detailsOk = rsFiles->FileDetails(mFile.mHash, RS_FILE_HINTS_DOWNLOAD | RS_FILE_HINTS_SPEC_ONLY, fileInfo);
@ -126,11 +150,12 @@ void GxsChannelFilesStatusWidget::check()
case STATE_ERROR:
repeat = 0;
ui->downloadToolButton->hide();
ui->downloadPushButton->hide();
ui->resumeToolButton->hide();
ui->pauseToolButton->hide();
ui->cancelToolButton->hide();
ui->progressBar->hide();
ui->openFilePushButton->hide();
ui->openFolderToolButton->hide();
statusText = tr("Error");
@ -140,11 +165,12 @@ void GxsChannelFilesStatusWidget::check()
case STATE_REMOTE:
repeat = 30000;
ui->downloadToolButton->show();
ui->downloadPushButton->show();
ui->resumeToolButton->hide();
ui->pauseToolButton->hide();
ui->cancelToolButton->hide();
ui->progressBar->hide();
ui->openFilePushButton->hide();
ui->openFolderToolButton->hide();
break;
@ -152,11 +178,12 @@ void GxsChannelFilesStatusWidget::check()
case STATE_DOWNLOAD:
repeat = 1000;
ui->downloadToolButton->hide();
ui->downloadPushButton->hide();
ui->resumeToolButton->hide();
ui->pauseToolButton->show();
ui->cancelToolButton->show();
ui->progressBar->show();
ui->openFilePushButton->hide();
ui->openFolderToolButton->hide();
break;
@ -164,11 +191,12 @@ void GxsChannelFilesStatusWidget::check()
case STATE_PAUSED:
repeat = 1000;
ui->downloadToolButton->hide();
ui->downloadPushButton->hide();
ui->resumeToolButton->show();
ui->pauseToolButton->hide();
ui->cancelToolButton->show();
ui->progressBar->hide();
ui->openFilePushButton->hide();
ui->openFolderToolButton->hide();
statusText = tr("Paused");
@ -178,11 +206,12 @@ void GxsChannelFilesStatusWidget::check()
case STATE_WAITING:
repeat = 1000;
ui->downloadToolButton->hide();
ui->downloadPushButton->hide();
ui->resumeToolButton->hide();
ui->pauseToolButton->show();
ui->cancelToolButton->show();
ui->progressBar->hide();
ui->openFilePushButton->hide();
ui->openFolderToolButton->hide();
statusText = tr("Waiting");
@ -192,11 +221,12 @@ void GxsChannelFilesStatusWidget::check()
case STATE_CHECKING:
repeat = 1000;
ui->downloadToolButton->hide();
ui->downloadPushButton->hide();
ui->resumeToolButton->hide();
ui->pauseToolButton->hide();
ui->cancelToolButton->show();
ui->progressBar->hide();
ui->openFilePushButton->hide();
ui->openFolderToolButton->hide();
statusText = tr("Checking");
@ -206,11 +236,12 @@ void GxsChannelFilesStatusWidget::check()
case STATE_LOCAL:
repeat = 60000;
ui->downloadToolButton->hide();
ui->downloadPushButton->hide();
ui->resumeToolButton->hide();
ui->pauseToolButton->hide();
ui->cancelToolButton->hide();
ui->progressBar->hide();
ui->openFilePushButton->show();
ui->openFolderToolButton->show();
break;
@ -296,3 +327,24 @@ void GxsChannelFilesStatusWidget::openFolder()
}
}
}
void GxsChannelFilesStatusWidget::openFile()
{
FileInfo fileInfo;
if (!rsFiles->alreadyHaveFile(mFile.mHash, fileInfo)) {
return;
}
/* open file with a suitable application */
QFileInfo qinfo;
qinfo.setFile(QString::fromUtf8(fileInfo.path.c_str()));
if (qinfo.exists()) {
if (!RsUrlHandler::openUrl(QUrl::fromLocalFile(qinfo.absoluteFilePath()))) {
std::cerr << "GxsChannelFilesStatusWidget(): can't open file " << fileInfo.path << std::endl;
}
}else{
QMessageBox::information(this, tr("Play File"),
tr("File %1 does not exist at location.").arg(fileInfo.path.c_str()));
return;
}
}

View File

@ -34,7 +34,7 @@ class GxsChannelFilesStatusWidget : public QWidget
Q_OBJECT
public:
explicit GxsChannelFilesStatusWidget(const RsGxsGroupId &groupId, const RsGxsMessageId &messageId, const RsGxsFile &file, QWidget *parent = 0);
explicit GxsChannelFilesStatusWidget(const RsGxsFile &file, QWidget *parent = 0);
~GxsChannelFilesStatusWidget();
private slots:
@ -44,6 +44,7 @@ private slots:
void pause();
void resume();
void openFolder();
void openFile();
private:
void setSize(uint64_t size);

View File

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>367</width>
<height>27</height>
<width>421</width>
<height>29</height>
</rect>
</property>
<property name="windowTitle">
@ -48,7 +48,7 @@
<number>2</number>
</property>
<item>
<widget class="QToolButton" name="downloadToolButton">
<widget class="QPushButton" name="downloadPushButton">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
@ -126,18 +126,31 @@
</widget>
</item>
<item>
<widget class="QToolButton" name="openFolderToolButton">
<widget class="QPushButton" name="openFilePushButton">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Play</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="openFolderToolButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="text">
<string>Open folder</string>
<property name="popupMode">
<enum>QToolButton::InstantPopup</enum>
</property>
</widget>
</item>
@ -148,6 +161,7 @@
</widget>
<resources>
<include location="../images.qrc"/>
<include location="../icons.qrc"/>
</resources>
<connections/>
</ui>

View File

@ -86,7 +86,7 @@ GxsChannelFilesWidget::~GxsChannelFilesWidget()
delete ui;
}
void GxsChannelFilesWidget::addFiles(const RsGxsChannelPost &post, bool related)
void GxsChannelFilesWidget::addFiles(const RsGxsChannelPost& post, bool related)
{
if (related) {
removeItems(post.mMeta.mGroupId, post.mMeta.mMsgId);
@ -113,7 +113,7 @@ void GxsChannelFilesWidget::addFiles(const RsGxsChannelPost &post, bool related)
ui->treeWidget->addTopLevelItem(treeItem);
QWidget *statusWidget = new GxsChannelFilesStatusWidget(post.mMeta.mGroupId, post.mMeta.mMsgId, file);
QWidget *statusWidget = new GxsChannelFilesStatusWidget(file);
ui->treeWidget->setItemWidget(treeItem, COLUMN_STATUS, statusWidget);
filterItem(treeItem);

View File

@ -0,0 +1,476 @@
/*******************************************************************************
* retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp *
* *
* Copyright 2020 by Cyril Soler <csoler@users.sourceforge.net> *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU Affero General Public License as *
* published by the Free Software Foundation, either version 3 of the *
* License, or (at your option) 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 Affero General Public License for more details. *
* *
* You should have received a copy of the GNU Affero General Public License *
* along with this program. If not, see <https://www.gnu.org/licenses/>. *
* *
*******************************************************************************/
#include <QApplication>
#include <QFontMetrics>
#include <QModelIndex>
#include <QIcon>
#include "gui/common/FilesDefs.h"
#include "util/qtthreadsutils.h"
#include "util/HandleRichText.h"
#include "util/DateTime.h"
#include "retroshare/rsgxsflags.h"
#include "retroshare/rsgxschannels.h"
#include "retroshare/rsexpr.h"
#include "GxsChannelPostFilesModel.h"
//#define DEBUG_CHANNEL_MODEL
Q_DECLARE_METATYPE(ChannelPostFileInfo)
static std::ostream& operator<<(std::ostream& o, const QModelIndex& i);// defined elsewhere
RsGxsChannelPostFilesModel::RsGxsChannelPostFilesModel(QObject *parent)
: QAbstractItemModel(parent)
{
initEmptyHierarchy();
mTimer = new QTimer;
connect(mTimer,SIGNAL(timeout()),this,SLOT(update()));
}
void RsGxsChannelPostFilesModel::initEmptyHierarchy()
{
preMods();
mFiles.clear();
mFilteredFiles.clear();
postMods();
}
void RsGxsChannelPostFilesModel::preMods()
{
//emit layoutAboutToBeChanged(); //Generate SIGSEGV when click on button move next/prev.
beginResetModel();
}
void RsGxsChannelPostFilesModel::postMods()
{
endResetModel();
emit dataChanged(createIndex(0,0,(void*)NULL), createIndex(mFilteredFiles.size(),COLUMN_FILES_NB_COLUMNS-1,(void*)NULL));
}
void RsGxsChannelPostFilesModel::update()
{
emit dataChanged(createIndex(0,0,(void*)NULL), createIndex(mFilteredFiles.size(),COLUMN_FILES_NB_COLUMNS-1,(void*)NULL));
}
int RsGxsChannelPostFilesModel::rowCount(const QModelIndex& parent) const
{
if(parent.column() > 0)
return 0;
if(mFilteredFiles.empty()) // security. Should never happen.
return 0;
if(!parent.isValid())
return mFilteredFiles.size(); // mFilteredPosts always has an item at 0, so size()>=1, and mColumn>=1
RsErr() << __PRETTY_FUNCTION__ << " rowCount cannot figure out the porper number of rows." << std::endl;
return 0;
}
int RsGxsChannelPostFilesModel::columnCount(const QModelIndex &/*parent*/) const
{
return COLUMN_FILES_NB_COLUMNS ;
}
bool RsGxsChannelPostFilesModel::getFileData(const QModelIndex& i,ChannelPostFileInfo& fmpe) const
{
if(!i.isValid())
return true;
quintptr ref = i.internalId();
uint32_t entry = 0;
if(!convertRefPointerToTabEntry(ref,entry) || entry >= mFiles.size())
return false ;
fmpe = mFiles[mFilteredFiles[entry]];
return true;
}
bool RsGxsChannelPostFilesModel::hasChildren(const QModelIndex &parent) const
{
if(!parent.isValid())
return true;
return false; // by default, no channel post has children
}
bool RsGxsChannelPostFilesModel::convertTabEntryToRefPointer(uint32_t entry,quintptr& ref)
{
// the pointer is formed the following way:
//
// [ 32 bits ]
//
// This means that the whole software has the following build-in limitation:
// * 4 B simultaenous posts. Should be enough !
ref = (intptr_t)(entry+1);
return true;
}
bool RsGxsChannelPostFilesModel::convertRefPointerToTabEntry(quintptr ref, uint32_t& entry)
{
intptr_t val = (intptr_t)ref;
if(val > (1<<30)) // make sure the pointer is an int that fits in 32bits and not too big which would look suspicious
{
RsErr() << "(EE) trying to make a ChannelPostsModelIndex out of a number that is larger than 2^32-1 !" << std::endl;
return false ;
}
if(val==0)
{
RsErr() << "(EE) trying to make a ChannelPostsFileModelIndex out of index 0." << std::endl;
return false;
}
entry = val-1;
return true;
}
QModelIndex RsGxsChannelPostFilesModel::index(int row, int column, const QModelIndex & parent) const
{
if(row < 0 || column < 0 || column >= COLUMN_FILES_NB_COLUMNS)
return QModelIndex();
quintptr ref = getChildRef(parent.internalId(),row);
#ifdef DEBUG_CHANNEL_MODEL
std::cerr << "index-3(" << row << "," << column << " parent=" << parent << ") : " << createIndex(row,column,ref) << std::endl;
#endif
return createIndex(row,column,ref) ;
}
QModelIndex RsGxsChannelPostFilesModel::parent(const QModelIndex& index) const
{
if(!index.isValid())
return QModelIndex();
return QModelIndex(); // there's no hierarchy here. So nothing to do!
}
Qt::ItemFlags RsGxsChannelPostFilesModel::flags(const QModelIndex& index) const
{
if (!index.isValid())
return 0;
if(index.column() == COLUMN_FILES_FILE)
return QAbstractItemModel::flags(index) | Qt::ItemIsEditable;
else
return QAbstractItemModel::flags(index);
}
quintptr RsGxsChannelPostFilesModel::getChildRef(quintptr ref,int index) const
{
if (index < 0)
return 0;
if(ref == quintptr(0))
{
quintptr new_ref;
convertTabEntryToRefPointer(index,new_ref);
return new_ref;
}
else
return 0 ;
}
quintptr RsGxsChannelPostFilesModel::getParentRow(quintptr ref,int& row) const
{
ChannelPostFilesModelIndex ref_entry;
if(!convertRefPointerToTabEntry(ref,ref_entry) || ref_entry >= mFilteredFiles.size())
return 0 ;
if(ref_entry == 0)
{
RsErr() << "getParentRow() shouldn't be asked for the parent of NULL" << std::endl;
row = 0;
}
else
row = ref_entry-1;
return 0;
}
int RsGxsChannelPostFilesModel::getChildrenCount(quintptr ref) const
{
uint32_t entry = 0 ;
if(ref == quintptr(0))
return rowCount()-1;
return 0;
}
QVariant RsGxsChannelPostFilesModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (role != Qt::DisplayRole)
return QVariant();
switch(section)
{
case COLUMN_FILES_FILE: return QString("Status");
case COLUMN_FILES_SIZE: return QString("Size");
case COLUMN_FILES_NAME: return QString("File");
case COLUMN_FILES_DATE: return QString("Published");
default:
return QString("[No data]");
}
}
QVariant RsGxsChannelPostFilesModel::data(const QModelIndex &index, int role) const
{
#ifdef DEBUG_CHANNEL_MODEL
std::cerr << "calling data(" << index << ") role=" << role << std::endl;
#endif
if(!index.isValid())
return QVariant();
switch(role)
{
case Qt::SizeHintRole: return sizeHintRole(index.column()) ;
case Qt::StatusTipRole:return QVariant();
default: break;
}
quintptr ref = (index.isValid())?index.internalId():0 ;
uint32_t entry = 0;
#ifdef DEBUG_CHANNEL_MODEL
std::cerr << "data(" << index << ")" ;
#endif
if(!ref)
{
#ifdef DEBUG_CHANNEL_MODEL
std::cerr << " [empty]" << std::endl;
#endif
return QVariant() ;
}
if(!convertRefPointerToTabEntry(ref,entry) || entry >= mFilteredFiles.size())
{
#ifdef DEBUG_CHANNEL_MODEL
std::cerr << "Bad pointer: " << (void*)ref << std::endl;
#endif
return QVariant() ;
}
const ChannelPostFileInfo& fmpe(mFiles[mFilteredFiles[entry]]);
switch(role)
{
case Qt::DisplayRole: return displayRole (fmpe,index.column()) ;
case Qt::UserRole: return userRole (fmpe,index.column()) ;
case SortRole: return sortRole (fmpe,index.column()) ;
default:
return QVariant();
}
}
void RsGxsChannelPostFilesModel::setFilter(const QStringList& strings, uint32_t& count)
{
preMods();
beginRemoveRows(QModelIndex(),0,rowCount()-1);
endRemoveRows();
if(strings.empty())
{
mFilteredFiles.clear();
for(int i=0;i<mFiles.size();++i)
mFilteredFiles.push_back(i);
}
else
{
mFilteredFiles.clear();
//mFilteredPosts.push_back(0);
for(int i=0;i<mFiles.size();++i)
{
bool passes_strings = true;
for(auto& s:strings)
passes_strings = passes_strings && QString::fromStdString(mFiles[i].mName).contains(s,Qt::CaseInsensitive);
if(passes_strings)
mFilteredFiles.push_back(i);
}
}
count = mFilteredFiles.size();
std::cerr << "After filtering: " << count << " posts remain." << std::endl;
beginInsertRows(QModelIndex(),0,rowCount()-1);
endInsertRows();
postMods();
}
class compareOperator
{
public:
compareOperator(int column,Qt::SortOrder order): col(column),ord(order) {}
bool operator()(const ChannelPostFileInfo& f1,const ChannelPostFileInfo& f2) const
{
switch(col)
{
default:
case RsGxsChannelPostFilesModel::COLUMN_FILES_NAME: return (ord==Qt::AscendingOrder)?(f1.mName<f2.mName):(f1.mName>f2.mName);
case RsGxsChannelPostFilesModel::COLUMN_FILES_SIZE: return (ord==Qt::AscendingOrder)?(f1.mSize<f2.mSize):(f1.mSize>f2.mSize);
case RsGxsChannelPostFilesModel::COLUMN_FILES_DATE: return (ord==Qt::AscendingOrder)?(f1.mPublishTime<f2.mPublishTime):(f1.mPublishTime>f2.mPublishTime);
case RsGxsChannelPostFilesModel::COLUMN_FILES_FILE:
{
FileInfo fi1,fi2;
rsFiles->FileDetails(f1.mHash,RS_FILE_HINTS_DOWNLOAD,fi1);
rsFiles->FileDetails(f2.mHash,RS_FILE_HINTS_DOWNLOAD,fi2);
return (ord==Qt::AscendingOrder)?(fi1.transfered<fi2.transfered):(fi1.transfered>fi2.transfered);
}
}
}
private:
int col;
Qt::SortOrder ord;
};
void RsGxsChannelPostFilesModel::sort(int column, Qt::SortOrder order)
{
std::sort(mFiles.begin(),mFiles.end(),compareOperator(column,order));
update();
}
QVariant RsGxsChannelPostFilesModel::sizeHintRole(int col) const
{
float factor = QFontMetricsF(QApplication::font()).height()/14.0f ;
return QVariant( QSize(factor * 170, factor*14 ));
}
QVariant RsGxsChannelPostFilesModel::sortRole(const ChannelPostFileInfo& fmpe,int column) const
{
switch(column)
{
case COLUMN_FILES_NAME: return QVariant(QString::fromUtf8(fmpe.mName.c_str()));
case COLUMN_FILES_SIZE: return QVariant(qulonglong(fmpe.mSize));
case COLUMN_FILES_DATE: return QVariant(qulonglong(fmpe.mPublishTime));
case COLUMN_FILES_FILE:
{
FileInfo finfo;
if(rsFiles->FileDetails(fmpe.mHash,RS_FILE_HINTS_DOWNLOAD,finfo))
return qulonglong(finfo.transfered);
return QVariant(qulonglong(fmpe.mSize));
}
break;
default:
return displayRole(fmpe,column);
}
}
QVariant RsGxsChannelPostFilesModel::displayRole(const ChannelPostFileInfo& fmpe,int col) const
{
switch(col)
{
case COLUMN_FILES_NAME: return QString::fromUtf8(fmpe.mName.c_str());
case COLUMN_FILES_SIZE: return QString::number(fmpe.mSize);
case COLUMN_FILES_DATE: return QString::number(fmpe.mPublishTime);
case COLUMN_FILES_FILE: {
FileInfo finfo;
if(rsFiles->FileDetails(fmpe.mHash,RS_FILE_HINTS_DOWNLOAD,finfo))
return qulonglong(finfo.transfered);
else
return 0;
}
default:
return QString();
}
return QVariant("[ERROR]");
}
QVariant RsGxsChannelPostFilesModel::userRole(const ChannelPostFileInfo& fmpe,int col) const
{
switch(col)
{
default:
return QVariant::fromValue(fmpe);
}
}
void RsGxsChannelPostFilesModel::clear()
{
preMods();
initEmptyHierarchy();
postMods();
emit channelLoaded();
}
void RsGxsChannelPostFilesModel::setFiles(const std::list<ChannelPostFileInfo> &files)
{
preMods();
beginRemoveRows(QModelIndex(),0,mFilteredFiles.size()-1);
endRemoveRows();
initEmptyHierarchy();
for(auto& file:files)
mFiles.push_back(file);
for(uint32_t i=0;i<mFiles.size();++i)
mFilteredFiles.push_back(i);
#ifdef DEBUG_CHANNEL_MODEL
// debug_dump();
#endif
beginInsertRows(QModelIndex(),0,mFilteredFiles.size()-1);
endInsertRows();
postMods();
emit channelLoaded();
if(!files.empty())
mTimer->start(5000);
else
mTimer->stop();
}

View File

@ -0,0 +1,173 @@
/*******************************************************************************
* retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.h *
* *
* Copyright 2020 by Cyril Soler <csoler@users.sourceforge.net> *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU Affero General Public License as *
* published by the Free Software Foundation, either version 3 of the *
* License, or (at your option) 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 Affero General Public License for more details. *
* *
* You should have received a copy of the GNU Affero General Public License *
* along with this program. If not, see <https://www.gnu.org/licenses/>. *
* *
*******************************************************************************/
#pragma once
#include "retroshare/rsfiles.h"
#include "retroshare/rsgxscommon.h"
#include <QModelIndex>
#include <QColor>
// This class holds the actual hierarchy of posts, represented by identifiers
// It is responsible for auto-updating when necessary and holds a mutex to allow the Model to
// safely access the data.
// The model contains a post in place 0 that is the parent of all posts.
typedef uint32_t ChannelPostFilesModelIndex;
class QTimer;
// This class contains the info for a file as well as additional info such as publication date
struct ChannelPostFileInfo: public RsGxsFile
{
ChannelPostFileInfo(const RsGxsFile& gxs_file,rstime_t t)
: RsGxsFile(gxs_file),mPublishTime(t)
{}
ChannelPostFileInfo() : mPublishTime(0) {}
rstime_t mPublishTime;
};
// This class is the item model used by Qt to display the information
class RsGxsChannelPostFilesModel : public QAbstractItemModel
{
Q_OBJECT
public:
explicit RsGxsChannelPostFilesModel(QObject *parent = NULL);
~RsGxsChannelPostFilesModel(){}
enum Columns {
COLUMN_FILES_NAME = 0x00,
COLUMN_FILES_SIZE = 0x01,
COLUMN_FILES_FILE = 0x02,
COLUMN_FILES_DATE = 0x03,
COLUMN_FILES_NB_COLUMNS = 0x04
};
enum Roles{ SortRole = Qt::UserRole+1,
FilterRole = Qt::UserRole+2,
};
#ifdef TODO
enum SortMode{ SORT_MODE_PUBLISH_TS = 0x00,
SORT_MODE_CHILDREN_PUBLISH_TS = 0x01,
};
#endif
QModelIndex root() const{ return createIndex(0,0,(void*)NULL) ;}
// This method will asynchroneously update the data
void setFiles(const std::list<ChannelPostFileInfo>& files);
void setFilter(const QStringList &strings, uint32_t &count) ;
#ifdef TODO
QModelIndex getIndexOfFile(const RsFileHash& hash) const;
void setSortMode(SortMode mode) ;
void setTextColorRead (QColor color) { mTextColorRead = color;}
void setTextColorUnread (QColor color) { mTextColorUnread = color;}
void setTextColorUnreadChildren(QColor color) { mTextColorUnreadChildren = color;}
void setTextColorNotSubscribed (QColor color) { mTextColorNotSubscribed = color;}
void setTextColorMissing (QColor color) { mTextColorMissing = color;}
void setAuthorOpinion(const QModelIndex& indx,RsOpinion op);
#endif
// Helper functions
void clear() ;
// AbstractItemModel functions.
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
bool hasChildren(const QModelIndex &parent = QModelIndex()) const override;
void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override;
QModelIndex index(int row, int column, const QModelIndex & parent = QModelIndex()) const override;
QModelIndex parent(const QModelIndex& child) const override;
Qt::ItemFlags flags(const QModelIndex& index) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
// Custom item roles
QVariant sizeHintRole (int col) const;
QVariant displayRole (const ChannelPostFileInfo& fmpe, int col) const;
QVariant toolTipRole (const ChannelPostFileInfo& fmpe, int col) const;
QVariant userRole (const ChannelPostFileInfo& fmpe, int col) const;
QVariant sortRole (const ChannelPostFileInfo& fmpe, int col) const;
QVariant filterRole (const ChannelPostFileInfo& fmpe, int col) const;
#ifdef TODO
QVariant decorationRole(const ForumModelPostEntry& fmpe, int col) const;
QVariant pinnedRole (const ForumModelPostEntry& fmpe, int col) const;
QVariant missingRole (const ForumModelPostEntry& fmpe, int col) const;
QVariant statusRole (const ForumModelPostEntry& fmpe, int col) const;
QVariant authorRole (const ForumModelPostEntry& fmpe, int col) const;
QVariant fontRole (const ForumModelPostEntry& fmpe, int col) const;
QVariant textColorRole (const ForumModelPostEntry& fmpe, int col) const;
QVariant backgroundRole(const ForumModelPostEntry& fmpe, int col) const;
#endif
/*!
* \brief debug_dump
* Dumps the hierarchy of posts in the terminal, to allow checking whether the internal representation is correct.
*/
void debug_dump();
signals:
void channelLoaded(); // emitted after the posts have been set. Can be used to updated the UI.
private slots:
void update();
private:
#ifdef TODO
bool mUseChildTS;
bool mFilteringEnabled;
SortMode mSortMode;
#endif
void preMods() ;
void postMods() ;
quintptr getParentRow(quintptr ref,int& row) const;
quintptr getChildRef(quintptr ref, int index) const;
int getChildrenCount(quintptr ref) const;
bool getFileData(const QModelIndex& i, ChannelPostFileInfo &fmpe) const;
static bool convertTabEntryToRefPointer(uint32_t entry, quintptr& ref);
static bool convertRefPointerToTabEntry(quintptr ref,uint32_t& entry);
#ifdef TODO
static void generateMissingItem(const RsGxsMessageId &msgId,ChannelPostsModelPostEntry& entry);
#endif
void initEmptyHierarchy();
std::vector<int> mFilteredFiles ; // store the list of files for the post
std::vector<ChannelPostFileInfo> mFiles ; // store the list of files for the post
QTimer *mTimer;
};

View File

@ -0,0 +1,125 @@
/*******************************************************************************
* retroshare-gui/src/gui/gxschannels/GxsChannelPostThumbnail.h *
* *
* Copyright 2020 by Retroshare Team <retroshare.project@gmail.com> *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU Affero General Public License as *
* published by the Free Software Foundation, either version 3 of the *
* License, or (at your option) 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 Affero General Public License for more details. *
* *
* You should have received a copy of the GNU Affero General Public License *
* along with this program. If not, see <https://www.gnu.org/licenses/>. *
* *
*******************************************************************************/
#pragma once
#include <string>
#include <QWidget>
#include <QLabel>
#include <QLayout>
#include "retroshare/rsgxschannels.h"
#include "retroshare/rsidentity.h"
#include "gui/gxs/GxsIdDetails.h"
#include "gui/common/FilesDefs.h"
// Class to paint the thumbnails with title
class ChannelPostThumbnailView: public QWidget
{
Q_OBJECT
public:
// This variable determines the zoom factor on the text below thumbnails. 2.0 is mostly correct for all screen.
static constexpr float THUMBNAIL_OVERSAMPLE_FACTOR = 2.0;
// Size of thumbnails as a function of the height of the font. An aspect ratio of 3/4 is good.
static const int THUMBNAIL_W = 4;
static const int THUMBNAIL_H = 6;
static constexpr char *CHAN_DEFAULT_IMAGE = ":images/thumb-default-video.png";
virtual ~ChannelPostThumbnailView()
{
delete lb;
delete lt;
}
ChannelPostThumbnailView(QWidget *parent=NULL): QWidget(parent)
{
init(FilesDefs::getPixmapFromQtResourcePath(CHAN_DEFAULT_IMAGE), QString("New post"),false);
}
ChannelPostThumbnailView(const RsGxsChannelPost& post,QWidget *parent=NULL)
: QWidget(parent)
{
// now fill the data
QPixmap thumbnail;
if(post.mThumbnail.mSize > 0)
GxsIdDetails::loadPixmapFromData(post.mThumbnail.mData, post.mThumbnail.mSize, thumbnail,GxsIdDetails::ORIGINAL);
else if(post.mMeta.mPublishTs > 0) // this is for testing that the post is not an empty post (happens at the end of the last row)
thumbnail = FilesDefs::getPixmapFromQtResourcePath(CHAN_DEFAULT_IMAGE);
init(thumbnail, QString::fromUtf8(post.mMeta.mMsgName.c_str()), IS_MSG_UNREAD(post.mMeta.mMsgStatus) || IS_MSG_NEW(post.mMeta.mMsgStatus) );
}
void init(const QPixmap& thumbnail,const QString& msg,bool is_msg_new)
{
QVBoxLayout *layout = new QVBoxLayout(this);
lb = new QLabel(this);
lb->setScaledContents(true);
layout->addWidget(lb);
lt = new QLabel(this);
layout->addWidget(lt);
setLayout(layout);
setSizePolicy(QSizePolicy::Maximum,QSizePolicy::Maximum);
QFontMetricsF fm(font());
int W = THUMBNAIL_OVERSAMPLE_FACTOR * THUMBNAIL_W * fm.height() ;
int H = THUMBNAIL_OVERSAMPLE_FACTOR * THUMBNAIL_H * fm.height() ;
lb->setFixedSize(W,H);
lb->setPixmap(thumbnail);
lt->setText(msg);
QFont font = lt->font();
if(is_msg_new)
{
font.setBold(true);
lt->setFont(font);
}
lt->setMaximumWidth(W);
lt->setWordWrap(true);
adjustSize();
update();
}
void setPixmap(const QPixmap& p) { lb->setPixmap(p); }
void setText(const QString& s) { lt->setText(s); }
private:
QLabel *lb;
QLabel *lt;
};

Some files were not shown because too many files have changed in this diff Show More