mirror of
https://github.com/RetroShare/RetroShare.git
synced 2025-01-16 01:47:17 -05:00
Merge branch 'master' into TheWire-rework-ui
This commit is contained in:
commit
0522c7907a
@ -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
|
@ -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))
|
||||
{
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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 ))
|
||||
{
|
||||
|
@ -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]) ;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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 ;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -209,7 +209,7 @@ RsGxsMsgMetaData::~RsGxsMsgMetaData(){
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t RsGxsMsgMetaData::serial_size()
|
||||
uint32_t RsGxsMsgMetaData::serial_size() const
|
||||
{
|
||||
|
||||
uint32_t s = 8; // header size
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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() )
|
||||
{
|
||||
|
@ -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:
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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) ;
|
||||
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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))) ;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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")
|
||||
|
@ -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\
|
||||
|
@ -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
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 ;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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.
|
||||
|
@ -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)
|
||||
};
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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)
|
||||
};
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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()
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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; \
|
||||
} \
|
||||
\
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
};
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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)
|
||||
{}
|
||||
|
@ -102,3 +102,8 @@ android-* {
|
||||
CONFIG *= qt
|
||||
QT *= network
|
||||
}
|
||||
|
||||
################################### Pkg-Config Stuff #############################
|
||||
|
||||
LIBS *= $$system(pkg-config --libs $$PKGCONFIG)
|
||||
|
||||
|
163
libretroshare/src/util/i2pcommon.cpp
Normal file
163
libretroshare/src/util/i2pcommon.cpp
Normal 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
|
213
libretroshare/src/util/i2pcommon.h
Normal file
213
libretroshare/src/util/i2pcommon.h
Normal 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
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -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 {
|
||||
|
42
libretroshare/src/util/rsdebuglevel0.h
Normal file
42
libretroshare/src/util/rsdebuglevel0.h
Normal 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("")
|
42
libretroshare/src/util/rsdebuglevel1.h
Normal file
42
libretroshare/src/util/rsdebuglevel1.h
Normal 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()
|
42
libretroshare/src/util/rsdebuglevel2.h
Normal file
42
libretroshare/src/util/rsdebuglevel2.h
Normal 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()
|
42
libretroshare/src/util/rsdebuglevel3.h
Normal file
42
libretroshare/src/util/rsdebuglevel3.h
Normal 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()
|
42
libretroshare/src/util/rsdebuglevel4.h
Normal file
42
libretroshare/src/util/rsdebuglevel4.h
Normal 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__)
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -22,6 +22,7 @@
|
||||
/** \file
|
||||
*/
|
||||
|
||||
#include <openssl/bio.h>
|
||||
#include <openssl/md5.h>
|
||||
#include <openssl/sha.h>
|
||||
#include <openssl/dsa.h>
|
||||
|
@ -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 );
|
||||
});
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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:
|
||||
|
@ -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]()
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -55,6 +55,7 @@ private slots:
|
||||
void readAndClearItem();
|
||||
void copyMessageLink();
|
||||
void viewPicture();
|
||||
void showAuthorInPeople();
|
||||
|
||||
signals:
|
||||
void vote(const RsGxsGrpMsgIdPair& msgId, bool up);
|
||||
|
@ -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>
|
||||
|
@ -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 );
|
||||
});
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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 );
|
||||
});
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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"
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -58,6 +58,7 @@ private slots:
|
||||
void addExtraFile();
|
||||
void checkAttachmentReady();
|
||||
void deleteAttachment();
|
||||
void updatePreviewText(const QString &);
|
||||
|
||||
void cancelMsg();
|
||||
void sendMsg();
|
||||
|
@ -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><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
|
||||
<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><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
|
||||
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
|
||||
p, li { white-space: pre-wrap; }
|
||||
</style></head><body style=" font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;">
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:10pt; font-weight:600;">Attachments:</span></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><img src=":/images/feedback_arrow.png" /><span style=" font-family:'MS Shell Dlg 2'; font-size:8pt;"> Use Drag and Drop / Add Files button, to Hash new files.</span></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><img src=":/images/feedback_arrow.png" /><span style=" font-family:'MS Shell Dlg 2'; font-size:8pt;"> Copy/Paste RetroShare links from your shares</span></p></body></html></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>
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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>
|
||||
|
@ -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);
|
||||
|
476
retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp
Normal file
476
retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp
Normal 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();
|
||||
}
|
173
retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.h
Normal file
173
retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.h
Normal 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;
|
||||
};
|
125
retroshare-gui/src/gui/gxschannels/GxsChannelPostThumbnail.h
Normal file
125
retroshare-gui/src/gui/gxschannels/GxsChannelPostThumbnail.h
Normal 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
Loading…
Reference in New Issue
Block a user