diff --git a/libretroshare/src/chat/distributedchat.cc b/libretroshare/src/chat/distributedchat.cc index 9f4a4274e..8fbc8f624 100644 --- a/libretroshare/src/chat/distributedchat.cc +++ b/libretroshare/src/chat/distributedchat.cc @@ -138,7 +138,7 @@ bool DistributedChatService::handleRecvChatLobbyMsgItem(RsChatMsgItem *ci) return false ; } - if(rsIdentity->isBanned(cli->signature.keyId)) + if(rsIdentity->overallReputationLevel(cli->signature.keyId) == RsReputations::REPUTATION_LOCALLY_NEGATIVE) { std::cerr << "(WW) Received lobby msg/item from banned identity " << cli->signature.keyId << ". Dropping it." << std::endl; return false ; @@ -220,7 +220,7 @@ bool DistributedChatService::checkSignature(RsChatLobbyBouncingObject *obj,const // network pre-request key to allow message authentication. - mGixs->requestKey(obj->signature.keyId,peer_list); + mGixs->requestKey(obj->signature.keyId,peer_list,"Needed for chat lobby "+RsUtil::NumberToString(obj->lobby_id,true)); uint32_t size = obj->signed_serial_size() ; RsTemporaryMemory memory(size) ; @@ -238,7 +238,7 @@ bool DistributedChatService::checkSignature(RsChatLobbyBouncingObject *obj,const uint32_t error_status ; - if(!mGixs->validateData(memory,obj->signed_serial_size(),obj->signature,false,error_status)) + if(!mGixs->validateData(memory,obj->signed_serial_size(),obj->signature,false,"Chat lobby "+RsUtil::NumberToString(obj->lobby_id,true),error_status)) { bool res = false ; @@ -647,7 +647,7 @@ void DistributedChatService::handleRecvChatLobbyEventItem(RsChatLobbyEventItem * #endif time_t now = time(NULL) ; - if(rsIdentity->isBanned(item->signature.keyId)) + if(rsIdentity->overallReputationLevel(item->signature.keyId) == RsReputations::REPUTATION_LOCALLY_NEGATIVE) { std::cerr << "(WW) Received lobby msg/item from banned identity " << item->signature.keyId << ". Dropping it." << std::endl; return ; diff --git a/libretroshare/src/grouter/p3grouter.cc b/libretroshare/src/grouter/p3grouter.cc index 5c51a196d..1cb8cb360 100644 --- a/libretroshare/src/grouter/p3grouter.cc +++ b/libretroshare/src/grouter/p3grouter.cc @@ -1555,7 +1555,7 @@ void p3GRouter::handleIncomingReceiptItem(RsGRouterSignedReceiptItem *receipt_it uint32_t error_status ; - if(! verifySignedDataItem(receipt_item,error_status)) + if(! verifySignedDataItem(receipt_item,"GRouter incoming receipt item",error_status)) if( (it->second.routing_flags & GRouterRoutingInfo::ROUTING_FLAGS_IS_ORIGIN) || (error_status != RsGixs::RS_GIXS_ERROR_KEY_NOT_AVAILABLE)) { std::cerr << " checking receipt signature : FAILED. Receipt is dropped. Error status=" << error_status << std::endl; @@ -1711,7 +1711,7 @@ void p3GRouter::handleIncomingDataItem(RsGRouterGenericDataItem *data_item) #endif uint32_t error_status ; - if(!verifySignedDataItem(data_item,error_status)) // we should get proper flags out of this + if(!verifySignedDataItem(data_item,"Incoming distant message",error_status)) // we should get proper flags out of this { std::cerr << " verifying item signature: FAILED! Droping that item" ; std::cerr << " You probably received a message from a person you don't have key." << std::endl; @@ -1980,11 +1980,11 @@ bool p3GRouter::signDataItem(RsGRouterAbstractMsgItem *item,const RsGxsId& signi return false ; } } -bool p3GRouter::verifySignedDataItem(RsGRouterAbstractMsgItem *item,uint32_t& error_status) +bool p3GRouter::verifySignedDataItem(RsGRouterAbstractMsgItem *item,const std::string& info,uint32_t& error_status) { try { - if(rsIdentity->isBanned(item->signature.keyId)) + if(rsIdentity->overallReputationLevel(item->signature.keyId) == RsReputations::REPUTATION_LOCALLY_NEGATIVE) { std::cerr << "(WW) received global router message from banned identity " << item->signature.keyId << ". Rejecting the message." << std::endl; return false ; @@ -1999,7 +1999,7 @@ bool p3GRouter::verifySignedDataItem(RsGRouterAbstractMsgItem *item,uint32_t& er if(!item->serialise_signed_data(data,data_size)) throw std::runtime_error("Cannot serialise signed data.") ; - if(!mGixs->validateData(data,data_size,item->signature,true,error_status)) + if(!mGixs->validateData(data,data_size,item->signature,true,info, error_status)) { switch(error_status) { @@ -2010,7 +2010,7 @@ bool p3GRouter::verifySignedDataItem(RsGRouterAbstractMsgItem *item,uint32_t& er std::cerr << "(EE) Key for GXS Id " << item->signature.keyId << " is not available. Cannot verify. Asking key to peer " << item->PeerId() << std::endl; - mGixs->requestKey(item->signature.keyId,peer_ids) ; // request the key around + mGixs->requestKey(item->signature.keyId,peer_ids,info) ; // request the key around } break ; case RsGixs::RS_GIXS_ERROR_SIGNATURE_MISMATCH: std::cerr << "(EE) Signature mismatch. Spoofing/Corrupted/MITM?." << std::endl; @@ -2116,7 +2116,7 @@ bool p3GRouter::sendData(const RsGxsId& destination,const GRouterServiceId& clie // Verify the signature. If that fails, there's a bug somewhere!! uint32_t error_status; - if(!verifySignedDataItem(data_item,error_status)) + if(!verifySignedDataItem(data_item,"GRouter own signature check for outgoing msg",error_status)) { std::cerr << "Cannot verify data item that was just signed. Some error occured!" << std::endl; delete data_item; diff --git a/libretroshare/src/grouter/p3grouter.h b/libretroshare/src/grouter/p3grouter.h index 9ddf50149..19d704add 100644 --- a/libretroshare/src/grouter/p3grouter.h +++ b/libretroshare/src/grouter/p3grouter.h @@ -254,7 +254,7 @@ private: // signs an item with the given key. bool signDataItem(RsGRouterAbstractMsgItem *item,const RsGxsId& id) ; - bool verifySignedDataItem(RsGRouterAbstractMsgItem *item, uint32_t &error_status) ; + bool verifySignedDataItem(RsGRouterAbstractMsgItem *item, const std::string &info, uint32_t &error_status) ; bool encryptDataItem(RsGRouterGenericDataItem *item,const RsGxsId& destination_key) ; bool decryptDataItem(RsGRouterGenericDataItem *item) ; diff --git a/libretroshare/src/gxs/rsgenexchange.cc b/libretroshare/src/gxs/rsgenexchange.cc index c9d49fef0..7e1331396 100644 --- a/libretroshare/src/gxs/rsgenexchange.cc +++ b/libretroshare/src/gxs/rsgenexchange.cc @@ -86,14 +86,14 @@ RsGenExchange::RsGenExchange(RsGeneralDataService *gds, RsNetworkExchangeService CREATE_FAIL(0), CREATE_SUCCESS(1), CREATE_FAIL_TRY_LATER(2), - SIGN_MAX_ATTEMPTS(5), + SIGN_MAX_WAITING_TIME(60), SIGN_FAIL(0), SIGN_SUCCESS(1), SIGN_FAIL_TRY_LATER(2), VALIDATE_FAIL(0), VALIDATE_SUCCESS(1), VALIDATE_FAIL_TRY_LATER(2), - VALIDATE_MAX_ATTEMPTS(5) + VALIDATE_MAX_WAITING_TIME(60) { mDataAccess = new RsGxsDataAccess(gds); @@ -472,8 +472,8 @@ 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) ; - signSet.keySignSet[INDEX_AUTHEN_IDENTITY] = sign; + mGixs->timeStampKey(grpMeta.mAuthorId,"Creation of group author signature for GrpId" + grpMeta.mGroupId.toStdString()) ; + signSet.keySignSet[INDEX_AUTHEN_IDENTITY] = sign; } else id_ret = SIGN_FAIL; @@ -640,7 +640,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) ; + mGixs->timeStampKey(msgMeta.mAuthorId,"Creating author signature in group " + msgMeta.mGroupId.toStdString() + ", msg " + msgMeta.mMsgId.toStdString()) ; signSet.keySignSet[INDEX_AUTHEN_IDENTITY] = sign; } else @@ -857,7 +857,7 @@ 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) ; + mGixs->timeStampKey(metaData.mAuthorId,"Validation of author signature, service: " + rsServiceControl->getServiceName(serviceFullType()) + ". Grp="+metaData.mGroupId.toStdString()+", msg="+metaData.mMsgId.toStdString()) ; } else { @@ -882,20 +882,13 @@ int RsGenExchange::validateMsg(RsNxsMsg *msg, const uint32_t& grpFlag, const uin else { - // now check reputation of the message author - float reputation_threshold = RsReputations::REPUTATION_THRESHOLD_DEFAULT; - - if( (signFlag & GXS_SERV::FLAG_AUTHOR_AUTHENTICATION_GPG_KNOWN) && !(details.mFlags & RS_IDENTITY_FLAGS_PGP_KNOWN)) - reputation_threshold = RsReputations::REPUTATION_THRESHOLD_ANTI_SPAM; - else if( (signFlag & GXS_SERV::FLAG_AUTHOR_AUTHENTICATION_GPG) && !(details.mFlags & RS_IDENTITY_FLAGS_PGP_LINKED)) - reputation_threshold = RsReputations::REPUTATION_THRESHOLD_ANTI_SPAM; - else - reputation_threshold = RsReputations::REPUTATION_THRESHOLD_DEFAULT; - - if(details.mReputation.mOverallReputationScore < reputation_threshold) + // now check reputation of the message author. The reputation will need to be at least as high as this value for the msg to validate. + // At validation step, we accept all messages, except the ones signed by locally rejected identities. + + if(details.mReputation.mOverallReputationLevel == RsReputations::REPUTATION_LOCALLY_NEGATIVE) { #ifdef GEN_EXCH_DEBUG - std::cerr << "RsGenExchange::validateMsg(): message from " << metaData.mAuthorId << ", rejected because reputation score (" << details.mReputation.mOverallReputationScore <<") is below the accepted threshold (" << reputation_threshold << ")" << std::endl; + std::cerr << "RsGenExchange::validateMsg(): message from " << metaData.mAuthorId << ", rejected because reputation score (" << details.mReputation.mOverallReputationLevel <<") is below the accepted threshold (" << reputation_threshold << ")" << std::endl; #endif idValidate = false ; } @@ -905,13 +898,13 @@ int RsGenExchange::validateMsg(RsNxsMsg *msg, const uint32_t& grpFlag, const uin #endif } - } + } } else { std::list peers; peers.push_back(msg->PeerId()); - mGixs->requestKey(metaData.mAuthorId, peers); + mGixs->requestKey(metaData.mAuthorId, peers,"Validation of author signature, service: " + rsServiceControl->getServiceName(serviceFullType()) + ". Grp="+metaData.mGroupId.toStdString()+", msg="+metaData.mMsgId.toStdString()); #ifdef GEN_EXCH_DEBUG std::cerr << ", Key missing. Retry later." << std::endl; @@ -988,7 +981,7 @@ int RsGenExchange::validateGrp(RsNxsGrp* grp) #ifdef GEN_EXCH_DEBUG std::cerr << " key ID validation result: " << idValidate << std::endl; #endif - mGixs->timeStampKey(metaData.mAuthorId) ; + mGixs->timeStampKey(metaData.mAuthorId,"Group author signature validation. GrpId=" + metaData.mGroupId.toStdString()) ; } else { @@ -1006,7 +999,7 @@ int RsGenExchange::validateGrp(RsNxsGrp* grp) #endif std::list peers; peers.push_back(grp->PeerId()); - mGixs->requestKey(metaData.mAuthorId, peers); + mGixs->requestKey(metaData.mAuthorId, peers,"Group author signature validation. GrpId=" + metaData.mGroupId.toStdString()); return VALIDATE_FAIL_TRY_LATER; } } @@ -1485,7 +1478,7 @@ void RsGenExchange::notifyNewGroups(std::vector &groups) std::cerr << std::endl; #endif - GxsPendingItem gpsi(grp, grp->grpId); + GxsPendingItem gpsi(grp, grp->grpId,time(NULL)); mReceivedGrps.push_back(gpsi); } else @@ -1623,6 +1616,10 @@ uint32_t RsGenExchange::getDefaultSyncPeriod() } } +RsReputations::ReputationLevel RsGenExchange::minReputationForForwardingMessages(uint32_t group_sign_flags,uint32_t identity_sign_flags) +{ + return RsNetworkExchangeService::minReputationForForwardingMessages(group_sign_flags,identity_sign_flags); +} uint32_t RsGenExchange::getSyncPeriod(const RsGxsGroupId& grpId) { RS_STACK_MUTEX(mGenMtx) ; @@ -1926,7 +1923,9 @@ bool RsGenExchange::processGrpMask(const RsGxsGroupId& grpId, ContentValue &grpC void RsGenExchange::publishMsgs() { - RS_STACK_MUTEX(mGenMtx) ; + RS_STACK_MUTEX(mGenMtx) ; + + time_t now = time(NULL); // stick back msgs pending signature typedef std::map > PendSignMap; @@ -1995,22 +1994,20 @@ void RsGenExchange::publishMsgs() // sign attempt if(pit == mMsgPendingSign.end()) { - GxsPendingItem gsi(msgItem, token); + GxsPendingItem gsi(msgItem, token,time(NULL)); mMsgPendingSign.insert(std::make_pair(token, gsi)); } else { // remove from attempts queue if over sign // attempts limit - if(pit->second.mAttempts == SIGN_MAX_ATTEMPTS) + if(pit->second.mFirstTryTS + SIGN_MAX_WAITING_TIME < now) { + std::cerr << "Pending signature grp=" << pit->second.mItem->meta.mGroupId << ", msg=" << pit->second.mItem->meta.mMsgId << ", has exceeded validation time limit. The author's key can probably not be obtained. This is unexpected." << std::endl; + mMsgPendingSign.erase(token); tryLater = false; } - else - { - ++pit->second.mAttempts; - } } createOk = false; @@ -2656,28 +2653,22 @@ void RsGenExchange::processRecvdMessages() { RS_STACK_MUTEX(mGenMtx) ; + time_t now = time(NULL); + #ifdef GEN_EXCH_DEBUG if(!mMsgPendingValidate.empty()) std::cerr << "processing received messages" << std::endl; #endif NxsMsgPendingVect::iterator pend_it = mMsgPendingValidate.begin(); -#ifdef GEN_EXCH_DEBUG - if(!mMsgPendingValidate.empty()) - std::cerr << " pending validation" << std::endl; -#endif for(; pend_it != mMsgPendingValidate.end();) { GxsPendingItem& gpsi = *pend_it; -#ifdef GEN_EXCH_DEBUG - std::cerr << " grp=" << gpsi.mId.first << ", msg=" << gpsi.mId.second << ", attempts=" << gpsi.mAttempts ; -#endif - if(gpsi.mAttempts == VALIDATE_MAX_ATTEMPTS) + if(gpsi.mFirstTryTS + VALIDATE_MAX_WAITING_TIME < now) { -#ifdef GEN_EXCH_DEBUG - std::cerr << " = max! deleting." << std::endl; -#endif + std::cerr << "Pending validation grp=" << gpsi.mId.first << ", msg=" << gpsi.mId.second << ", has exceeded validation time limit. The author's key can probably not be obtained. This is unexpected." << std::endl; + delete gpsi.mItem; pend_it = mMsgPendingValidate.erase(pend_it); } @@ -2839,16 +2830,12 @@ void RsGenExchange::processRecvdMessages() // first check you haven't made too many attempts - NxsMsgPendingVect::iterator vit = std::find( - mMsgPendingValidate.begin(), mMsgPendingValidate.end(), id); + NxsMsgPendingVect::iterator vit = std::find(mMsgPendingValidate.begin(), mMsgPendingValidate.end(), id); if(vit == mMsgPendingValidate.end()) { - GxsPendingItem item(msg, id); + GxsPendingItem item(msg, id,time(NULL)); mMsgPendingValidate.push_back(item); - }else - { - vit->mAttempts++; } } } @@ -2981,18 +2968,16 @@ void RsGenExchange::processRecvdGroups() std::cerr << " failed to validate incoming grp, trying again. grpId: " << grp->grpId << std::endl; #endif - if(gpsi.mAttempts == VALIDATE_MAX_ATTEMPTS) + if(gpsi.mFirstTryTS + VALIDATE_MAX_WAITING_TIME < time(NULL)) { #ifdef GEN_EXCH_DEBUG - std::cerr << " max attempts " << VALIDATE_MAX_ATTEMPTS << " reached. Will delete group " << grp->grpId << std::endl; + std::cerr << " validation time got group " << grp->grpId << " exceeded maximum. Will delete group " << std::endl; #endif delete grp; erase = true; } else - { erase = false; - } } } else @@ -3147,7 +3132,7 @@ bool RsGenExchange::updateValid(RsGxsGrpMetaData& oldGrpMeta, RsNxsGrp& newGrp) // also check this is the latest published group bool latest = newGrp.metaData->mPublishTs > oldGrpMeta.mPublishTs; - mGixs->timeStampKey(newGrp.metaData->mAuthorId) ; + mGixs->timeStampKey(newGrp.metaData->mAuthorId,"Validation of signature for updated grp " + oldGrpMeta.mGroupId.toStdString()) ; return GxsSecurity::validateNxsGrp(newGrp, adminSign, keyMit->second) && latest; } diff --git a/libretroshare/src/gxs/rsgenexchange.h b/libretroshare/src/gxs/rsgenexchange.h index a391d47e0..735e8ec38 100644 --- a/libretroshare/src/gxs/rsgenexchange.h +++ b/libretroshare/src/gxs/rsgenexchange.h @@ -43,17 +43,10 @@ template class GxsPendingItem { public: - GxsPendingItem(GxsItem item, Identity id) : - mItem(item), mId(id), mAttempts(0) + GxsPendingItem(GxsItem item, Identity id,time_t ts) : + mItem(item), mId(id), mFirstTryTS(ts) {} - GxsPendingItem(const GxsPendingItem& gpsi) - { - this->mItem = gpsi.mItem; - this->mId = gpsi.mId; - this->mAttempts = gpsi.mAttempts; - } - bool operator==(const Identity& id) { return this->mId == id; @@ -61,7 +54,7 @@ public: GxsItem mItem; Identity mId; - uint8_t mAttempts; + time_t mFirstTryTS; }; class GxsGrpPendingSign @@ -656,6 +649,9 @@ public: virtual void setSyncPeriod(const RsGxsGroupId& grpId,uint32_t age_in_secs) ; uint16_t serviceType() const { return mServType ; } + uint32_t serviceFullType() const { return ((uint32_t)mServType << 8) + (((uint32_t) RS_PKT_VERSION_SERVICE) << 24); } + + virtual RsReputations::ReputationLevel minReputationForForwardingMessages(uint32_t group_sign_flags,uint32_t identity_flags); protected: /** Notifications **/ @@ -880,9 +876,9 @@ private: private: - const uint8_t CREATE_FAIL, CREATE_SUCCESS, CREATE_FAIL_TRY_LATER, SIGN_MAX_ATTEMPTS; + const uint8_t CREATE_FAIL, CREATE_SUCCESS, CREATE_FAIL_TRY_LATER, SIGN_MAX_WAITING_TIME; const uint8_t SIGN_FAIL, SIGN_SUCCESS, SIGN_FAIL_TRY_LATER; - const uint8_t VALIDATE_FAIL, VALIDATE_SUCCESS, VALIDATE_FAIL_TRY_LATER, VALIDATE_MAX_ATTEMPTS; + const uint8_t VALIDATE_FAIL, VALIDATE_SUCCESS, VALIDATE_FAIL_TRY_LATER, VALIDATE_MAX_WAITING_TIME; private: diff --git a/libretroshare/src/gxs/rsgixs.h b/libretroshare/src/gxs/rsgixs.h index 5b12aa873..a210064c2 100644 --- a/libretroshare/src/gxs/rsgixs.h +++ b/libretroshare/src/gxs/rsgixs.h @@ -117,7 +117,7 @@ public: */ virtual bool signData(const uint8_t *data,uint32_t data_size,const RsGxsId& signer_id,RsTlvKeySignature& signature,uint32_t& signing_error) = 0 ; - virtual bool validateData(const uint8_t *data,uint32_t data_size,const RsTlvKeySignature& signature,bool force_load,uint32_t& signing_error) = 0 ; + virtual bool validateData(const uint8_t *data,uint32_t data_size,const RsTlvKeySignature& signature,bool force_load,const std::string& info_string,uint32_t& signing_error) = 0 ; virtual bool encryptData(const uint8_t *clear_data,uint32_t clear_data_size,uint8_t *& encrypted_data,uint32_t& encrypted_data_size,const RsGxsId& encryption_key_id,bool force_load,uint32_t& encryption_error) = 0 ; virtual bool decryptData(const uint8_t *encrypted_data,uint32_t encrypted_data_size,uint8_t *& clear_data,uint32_t& clear_data_size,const RsGxsId& encryption_key_id,uint32_t& encryption_error) = 0 ; @@ -125,7 +125,7 @@ public: virtual bool getOwnIds(std::list& ids) = 0; virtual bool isOwnId(const RsGxsId& key_id) = 0 ; - virtual void timeStampKey(const RsGxsId& key_id) = 0 ; + virtual void timeStampKey(const RsGxsId& key_id,const std::string& reason) = 0 ; // Key related interface - used for validating msgs and groups. /*! @@ -149,7 +149,7 @@ public: * @param keyref the KeyRef of the key being requested * @return will */ - virtual bool requestKey(const RsGxsId &id, const std::list &peers) = 0; + virtual bool requestKey(const RsGxsId &id, const std::list &peers,const std::string& info) = 0; virtual bool requestPrivateKey(const RsGxsId &id) = 0; diff --git a/libretroshare/src/gxs/rsgxsnetservice.cc b/libretroshare/src/gxs/rsgxsnetservice.cc index 4c9291e7d..089f97a8c 100644 --- a/libretroshare/src/gxs/rsgxsnetservice.cc +++ b/libretroshare/src/gxs/rsgxsnetservice.cc @@ -1954,8 +1954,6 @@ void RsGxsNetService::updateServerSyncTS() for(std::map::const_iterator mit = gxsMap.begin();mit != gxsMap.end(); ++mit) { - //const RsGxsGroupId& grpId = mit->first; - // Check if the group is subscribed and restricted to a circle. If the circle has changed, update the // global TS to reflect that change to clients who may be able to see/subscribe to that particular group. @@ -2810,16 +2808,13 @@ void RsGxsNetService::locked_genReqMsgTransaction(NxsTransaction* tr) if(reqListSize < (int)MAX_REQLIST_SIZE && msgIdSet.find(msgId) == msgIdSet.end()) { - // if reputation is in reputations cache then proceed - // or if there isn't an author (note as author requirement is - // enforced at service level, if no author is needed then reputation - // filtering is optional) bool noAuthor = syncItem->authorId.isNull(); #ifdef NXS_NET_DEBUG_1 GXSNETDEBUG_PG(item->PeerId(),grpId) << ", reqlist size=" << reqListSize << ", message not present." ; #endif // grp meta must be present if author present + if(!noAuthor && grpMeta == NULL) { #ifdef NXS_NET_DEBUG_1 @@ -2828,7 +2823,13 @@ void RsGxsNetService::locked_genReqMsgTransaction(NxsTransaction* tr) continue; } - if(rsIdentity && rsIdentity->isBanned(syncItem->authorId)) + // The algorithm on request of message is: + // + // - always re-check for author ban level + // - if author is locally banned, do not download. + // - if author is not locally banned, download, whatever friends' opinion might be. + + if(rsIdentity && rsIdentity->overallReputationLevel(syncItem->authorId) == RsReputations::REPUTATION_LOCALLY_NEGATIVE) { #ifdef NXS_NET_DEBUG_1 GXSNETDEBUG_PG(item->PeerId(),grpId) << ", Identity " << syncItem->authorId << " is banned. Not requesting message!" << std::endl; @@ -2844,7 +2845,7 @@ void RsGxsNetService::locked_genReqMsgTransaction(NxsTransaction* tr) continue ; } - +#ifdef TO_BE_REMOVED if(mReputations->haveReputation(syncItem->authorId) || noAuthor) { GixsReputation rep; @@ -2859,17 +2860,20 @@ void RsGxsNetService::locked_genReqMsgTransaction(NxsTransaction* tr) // at genexchange side of things if(rep.score >= (int)grpMeta->mReputationCutOff || noAuthor) { -#ifdef NXS_NET_DEBUG_1 - GXSNETDEBUG_PG(item->PeerId(),grpId) << ", passed! Adding message to req list." << std::endl; #endif - RsNxsSyncMsgItem* msgItem = new RsNxsSyncMsgItem(mServType); - msgItem->grpId = grpId; - msgItem->msgId = msgId; - msgItem->flag = RsNxsSyncMsgItem::FLAG_REQUEST; - msgItem->transactionNumber = transN; - msgItem->PeerId(peerFrom); - reqList.push_back(msgItem); - ++reqListSize ; +#ifdef NXS_NET_DEBUG_1 + GXSNETDEBUG_PG(item->PeerId(),grpId) << ", passed! Adding message to req list." << std::endl; +#endif + RsNxsSyncMsgItem* msgItem = new RsNxsSyncMsgItem(mServType); + msgItem->grpId = grpId; + msgItem->msgId = msgId; + msgItem->flag = RsNxsSyncMsgItem::FLAG_REQUEST; + msgItem->transactionNumber = transN; + msgItem->PeerId(peerFrom); + reqList.push_back(msgItem); + ++reqListSize ; + +#ifdef TO_BE_REMOVED } #ifdef NXS_NET_DEBUG_1 else @@ -2889,6 +2893,7 @@ void RsGxsNetService::locked_genReqMsgTransaction(NxsTransaction* tr) entry.mMsgId = syncItem->msgId; toVet.push_back(entry); } +#endif } #ifdef NXS_NET_DEBUG_1 else @@ -3061,13 +3066,14 @@ void RsGxsNetService::locked_genReqGrpTransaction(NxsTransaction* tr) } // FIXTESTS global variable rsReputations not available in unittests! - if(!grpSyncItem->authorId.isNull() && rsIdentity && rsIdentity->isBanned(grpSyncItem->authorId)) - { +#warning Update the code below to correctly send/recv dependign on reputation + if(!grpSyncItem->authorId.isNull() && rsIdentity && rsIdentity->overallReputationLevel(grpSyncItem->authorId) == RsReputations::REPUTATION_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->authorId << " is banned. Not syncing group." << std::endl; #endif - continue ; - } + continue ; + } if( (mGrpAutoSync && !haveItem) || latestVersion) { @@ -4202,59 +4208,83 @@ void RsGxsNetService::handleRecvSyncMessage(RsNxsSyncMsgReqItem *item,bool item_ uint32_t transN = locked_getTransactionId(); RsGxsCircleId should_encrypt_to_this_circle_id ; + time_t now = time(NULL) ; + + uint32_t max_send_delay = mServerGrpConfigMap[item->grpId].msg_req_delay; // we should use "sync" but there's only one variable used in the GUI: the req one. + if(canSendMsgIds(msgMetas, *grpMeta, peer, should_encrypt_to_this_circle_id)) { for(std::vector::iterator vit = msgMetas.begin();vit != msgMetas.end(); ++vit) - if(item->createdSinceTS < (*vit)->mPublishTs) + { + RsGxsMsgMetaData* m = *vit; + + RsIdentityDetails details ; + + if(!rsIdentity->getIdDetails(m->mAuthorId,details)) { - RsGxsMsgMetaData* m = *vit; - - RsNxsSyncMsgItem* mItem = new RsNxsSyncMsgItem(mServType); - mItem->flag = RsNxsSyncGrpItem::FLAG_RESPONSE; - mItem->grpId = m->mGroupId; - mItem->msgId = m->mMsgId; - mItem->authorId = m->mAuthorId; - mItem->PeerId(peer); - mItem->transactionNumber = transN; - - if(!should_encrypt_to_this_circle_id.isNull()) - { -#ifdef NXS_NET_DEBUG_7 - GXSNETDEBUG_PG(item->PeerId(),item->grpId) << " sending info item for msg id " << mItem->msgId << ". Transaction will be encrypted for group " << should_encrypt_to_this_circle_id << std::endl; -#endif - RsNxsItem *encrypted_item = NULL ; - uint32_t status = RS_NXS_ITEM_ENCRYPTION_STATUS_UNKNOWN ; - - if(encryptSingleNxsItem(mItem, grpMeta->mCircleId,m->mGroupId, encrypted_item,status)) - { - itemL.push_back(encrypted_item) ; - delete mItem ; - } - else - { - // Something's not ready (probably the circle content. We could put on a vetting list, but actually the client will re-ask the list asap. - - std::cerr << " (EE) Cannot encrypt msg meta data. MsgId=" << mItem->msgId << ", grpId=" << mItem->grpId << ", circleId=" << should_encrypt_to_this_circle_id << ". Dropping the whole list." << std::endl; - - for(std::list::const_iterator it(itemL.begin());it!=itemL.end();++it) - delete *it ; - - itemL.clear() ; - break ; - } - } - else - { -#ifdef NXS_NET_DEBUG_7 - GXSNETDEBUG_PG(item->PeerId(),item->grpId) << " sending info item for msg id " << mItem->msgId << " in clear." << std::endl; -#endif - itemL.push_back(mItem); - } + std::cerr << /* GXSNETDEBUG_PG(item->PeerId(),item->grpId) << */ " not sending grp message ID " << (*vit)->mMsgId << ", because the identity of the author is not accessible (unknown/not cached)" << std::endl; + continue ; } + + if(details.mReputation.mOverallReputationLevel < minReputationForForwardingMessages(grpMeta->mSignFlags, details.mFlags)) + { +//#ifdef NXS_NET_DEBUG_0 + std::cerr << /* GXSNETDEBUG_PG(item->PeerId(),item->grpId) << */ " not sending item ID " << (*vit)->mMsgId << ", because the author is flags " << std::hex << details.mFlags << std::dec << " and reputation level " << details.mReputation.mOverallReputationLevel << std::endl; +//#endif + continue ; + } + // Check publish TS + + if(item->createdSinceTS > (*vit)->mPublishTs || (*vit)->mPublishTs + max_send_delay < now) + { #ifdef NXS_NET_DEBUG_0 - else - GXSNETDEBUG_PG(item->PeerId(),item->grpId) << " not sending item ID " << (*vit)->mMsgId << ", because it is too old (publishTS = " << (time(NULL)-(*vit)->mPublishTs)/86400 << " days ago" << std::endl; + GXSNETDEBUG_PG(item->PeerId(),item->grpId) << " not sending item ID " << (*vit)->mMsgId << ", because it is too old (publishTS = " << (time(NULL)-(*vit)->mPublishTs)/86400 << " days ago" << std::endl; #endif + continue ; + } + + RsNxsSyncMsgItem* mItem = new RsNxsSyncMsgItem(mServType); + mItem->flag = RsNxsSyncGrpItem::FLAG_RESPONSE; + mItem->grpId = m->mGroupId; + mItem->msgId = m->mMsgId; + mItem->authorId = m->mAuthorId; + mItem->PeerId(peer); + mItem->transactionNumber = transN; + + if(!should_encrypt_to_this_circle_id.isNull()) + { +#ifdef NXS_NET_DEBUG_7 + GXSNETDEBUG_PG(item->PeerId(),item->grpId) << " sending info item for msg id " << mItem->msgId << ". Transaction will be encrypted for group " << should_encrypt_to_this_circle_id << std::endl; +#endif + RsNxsItem *encrypted_item = NULL ; + uint32_t status = RS_NXS_ITEM_ENCRYPTION_STATUS_UNKNOWN ; + + if(encryptSingleNxsItem(mItem, grpMeta->mCircleId,m->mGroupId, encrypted_item,status)) + { + itemL.push_back(encrypted_item) ; + delete mItem ; + } + else + { + // Something's not ready (probably the circle content. We could put on a vetting list, but actually the client will re-ask the list asap. + + std::cerr << " (EE) Cannot encrypt msg meta data. MsgId=" << mItem->msgId << ", grpId=" << mItem->grpId << ", circleId=" << should_encrypt_to_this_circle_id << ". Dropping the whole list." << std::endl; + + for(std::list::const_iterator it(itemL.begin());it!=itemL.end();++it) + delete *it ; + + itemL.clear() ; + break ; + } + } + else + { +#ifdef NXS_NET_DEBUG_7 + GXSNETDEBUG_PG(item->PeerId(),item->grpId) << " sending info item for msg id " << mItem->msgId << " in clear." << std::endl; +#endif + itemL.push_back(mItem); + } + } } #ifdef NXS_NET_DEBUG_0 else diff --git a/libretroshare/src/gxs/rsgxsutil.cc b/libretroshare/src/gxs/rsgxsutil.cc index 29c9ae337..694f34972 100644 --- a/libretroshare/src/gxs/rsgxsutil.cc +++ b/libretroshare/src/gxs/rsgxsutil.cc @@ -139,7 +139,7 @@ bool RsGxsIntegrityCheck::check() GxsMsgReq msgIds; GxsMsgReq grps; - std::set used_gxs_ids ; + std::map used_gxs_ids ; std::set subscribed_groups ; // compute hash and compare to stored value, if it fails then simply add it @@ -171,8 +171,8 @@ bool RsGxsIntegrityCheck::check() GXSUTIL_DEBUG() << "TimeStamping group authors' key ID " << grp->metaData->mAuthorId << " in group ID " << grp->grpId << std::endl; #endif - if(rsIdentity!=NULL && !rsIdentity->isBanned(grp->metaData->mAuthorId)) - used_gxs_ids.insert(grp->metaData->mAuthorId) ; + if(rsIdentity!=NULL && rsIdentity->overallReputationLevel(grp->metaData->mAuthorId) > RsReputations::REPUTATION_LOCALLY_NEGATIVE) + used_gxs_ids.insert(std::make_pair(grp->metaData->mAuthorId,grp->grpId)) ; } } } @@ -269,8 +269,8 @@ bool RsGxsIntegrityCheck::check() #ifdef DEBUG_GXSUTIL GXSUTIL_DEBUG() << "TimeStamping message authors' key ID " << msg->metaData->mAuthorId << " in message " << msg->msgId << ", group ID " << msg->grpId<< std::endl; #endif - if(rsIdentity!=NULL && !rsIdentity->isBanned(msg->metaData->mAuthorId)) - used_gxs_ids.insert(msg->metaData->mAuthorId) ; + if(rsIdentity!=NULL && rsIdentity->overallReputationLevel(msg->metaData->mAuthorId) > RsReputations::REPUTATION_LOCALLY_NEGATIVE) + used_gxs_ids.insert(std::make_pair(msg->metaData->mAuthorId,msg->metaData->mGroupId)) ; } delete msg; @@ -297,9 +297,9 @@ bool RsGxsIntegrityCheck::check() std::list connected_friends ; rsPeers->getOnlineList(connected_friends) ; - std::vector gxs_ids ; + std::vector > gxs_ids ; - for(std::set::const_iterator it(used_gxs_ids.begin());it!=used_gxs_ids.end();++it) + for(std::map::const_iterator it(used_gxs_ids.begin());it!=used_gxs_ids.end();++it) { gxs_ids.push_back(*it) ; #ifdef DEBUG_GXSUTIL @@ -321,9 +321,9 @@ bool RsGxsIntegrityCheck::check() GXSUTIL_DEBUG() << " requesting ID " << gxs_ids[n] ; #endif - if(!mGixs->haveKey(gxs_ids[n])) // checks if we have it already in the cache (conservative way to ensure that we atually have it) + if(!mGixs->haveKey(gxs_ids[n].first)) // checks if we have it already in the cache (conservative way to ensure that we atually have it) { - mGixs->requestKey(gxs_ids[n],connected_friends); + mGixs->requestKey(gxs_ids[n].first,connected_friends,"Author in service \"" + rsServiceControl->getServiceName(mGenExchangeClient->serviceFullType())+"\" (group ID " + gxs_ids[n].second.toStdString() + ")" ) ; ++nb_requested_not_in_cache ; #ifdef DEBUG_GXSUTIL @@ -335,12 +335,8 @@ bool RsGxsIntegrityCheck::check() #ifdef DEBUG_GXSUTIL GXSUTIL_DEBUG() << " ... already in cache" << std::endl; #endif - - // Note: we could time_stamp even in the case where the id is not cached. Anyway, it's not really a problem here, since IDs have a high chance of - // behing eventually stamped. - - mGixs->timeStampKey(gxs_ids[n]) ; } + mGixs->timeStampKey(gxs_ids[n].first,"Author in service \"" + rsServiceControl->getServiceName(mGenExchangeClient->serviceFullType())+"\" (group ID " + gxs_ids[n].second.toStdString() + ")"); gxs_ids[n] = gxs_ids[gxs_ids.size()-1] ; gxs_ids.pop_back() ; diff --git a/libretroshare/src/gxs/rsnxs.h b/libretroshare/src/gxs/rsnxs.h index 0613560b0..9b879d2d0 100644 --- a/libretroshare/src/gxs/rsnxs.h +++ b/libretroshare/src/gxs/rsnxs.h @@ -34,6 +34,8 @@ #include #include "services/p3service.h" +#include "retroshare/rsreputations.h" +#include "retroshare/rsidentity.h" #include "rsgds.h" /*! @@ -159,6 +161,55 @@ public: * \return */ virtual bool stampMsgServerUpdateTS(const RsGxsGroupId& gid) =0; + + /*! + * \brief minReputationForForwardingMessages + * Encodes the policy for sending/requesting messages depending on anti-spam settings. + * + * \param group_sign_flags Sign flags from the group meta data + * \param identity_flags Flags of the identity + * \return + */ + static RsReputations::ReputationLevel minReputationForRequestingMessages(uint32_t /* group_sign_flags */, uint32_t /* identity_flags */) + { + // We always request messages, except if the author identity is locally banned. + + return RsReputations::REPUTATION_REMOTELY_NEGATIVE; + } + static RsReputations::ReputationLevel minReputationForForwardingMessages(uint32_t group_sign_flags, uint32_t identity_flags) + { + // If anti-spam is enabled, do not send messages from authors with bad reputation. The policy is to only forward messages if the reputation of the author is at least + // equal to the minimal reputation in the table below (R=remotely, L=locally, P=positive, N=negative, O=neutral) : + // + // + // +----------------------------------------------------+ + // | Identity flags | + // +----------------------------------------------------+ + // | Anonymous Signed Signed+Known | + // +-------------+-----------+----------------------------------------------------+ + // | |NONE | O O O | + // | Forum flags |GPG_AUTHED | RP O O | + // | |GPG_KNOWN | RP RP O | + // +-------------+-----------+----------------------------------------------------+ + // + + if(identity_flags & RS_IDENTITY_FLAGS_PGP_KNOWN) + return RsReputations::REPUTATION_NEUTRAL; + else if(identity_flags & RS_IDENTITY_FLAGS_PGP_LINKED) + { + if(group_sign_flags & GXS_SERV::FLAG_AUTHOR_AUTHENTICATION_GPG_KNOWN) + return RsReputations::REPUTATION_REMOTELY_POSITIVE; + else + return RsReputations::REPUTATION_NEUTRAL; + } + else + { + if( (group_sign_flags & GXS_SERV::FLAG_AUTHOR_AUTHENTICATION_GPG_KNOWN) || (group_sign_flags & GXS_SERV::FLAG_AUTHOR_AUTHENTICATION_GPG)) + return RsReputations::REPUTATION_REMOTELY_POSITIVE; + else + return RsReputations::REPUTATION_NEUTRAL; + } + } }; #endif // RSGNP_H diff --git a/libretroshare/src/gxstunnel/p3gxstunnel.cc b/libretroshare/src/gxstunnel/p3gxstunnel.cc index 0838bef50..c64cf6572 100644 --- a/libretroshare/src/gxstunnel/p3gxstunnel.cc +++ b/libretroshare/src/gxstunnel/p3gxstunnel.cc @@ -937,7 +937,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) ; + mGixs->timeStampKey(item->signature.keyId,"Used to validate GXS tunnel DH half-key.") ; #ifdef DEBUG_GXS_TUNNEL std::cerr << " Signature checks! Sender's ID = " << senders_id << std::endl; diff --git a/libretroshare/src/pqi/p3servicecontrol.cc b/libretroshare/src/pqi/p3servicecontrol.cc index 11dd01520..a397a4e1c 100644 --- a/libretroshare/src/pqi/p3servicecontrol.cc +++ b/libretroshare/src/pqi/p3servicecontrol.cc @@ -362,6 +362,20 @@ void p3ServiceControl::getServiceChanges(std::set &updateSet) mUpdatedSet.clear(); } +std::string p3ServiceControl::getServiceName(uint32_t service_id) +{ + RsStackMutex stack(mCtrlMtx); /***** LOCK STACK MUTEX ****/ + + std::map::const_iterator it = mOwnServices.find(service_id) ; + + if(it == mOwnServices.end()) + { + std::cerr << "(EE) Cannot find own service for ID = " << std::hex << service_id << std::dec << std::endl; + return std::string(); + } + + return it->second.mServiceName; +} bool p3ServiceControl::getOwnServices(RsPeerServiceInfo &info) { diff --git a/libretroshare/src/pqi/p3servicecontrol.h b/libretroshare/src/pqi/p3servicecontrol.h index e2a27ddc3..948481a2b 100644 --- a/libretroshare/src/pqi/p3servicecontrol.h +++ b/libretroshare/src/pqi/p3servicecontrol.h @@ -85,6 +85,7 @@ virtual const RsPeerId& getOwnId(); */ virtual bool getOwnServices(RsPeerServiceInfo &info); +virtual std::string getServiceName(uint32_t service_id) ; // This is what is passed to peers, can be displayed by GUI too. virtual bool getServicesAllowed(const RsPeerId &peerId, RsPeerServiceInfo &info); diff --git a/libretroshare/src/retroshare/rsgxsforums.h b/libretroshare/src/retroshare/rsgxsforums.h index cf7ca26ef..e1835928e 100644 --- a/libretroshare/src/retroshare/rsgxsforums.h +++ b/libretroshare/src/retroshare/rsgxsforums.h @@ -61,37 +61,36 @@ std::ostream &operator<<(std::ostream &out, const RsGxsForumMsg &msg); class RsGxsForums: public RsGxsIfaceHelper { - public: +public: RsGxsForums(RsGxsIface *gxs) - :RsGxsIfaceHelper(gxs) { return; } -virtual ~RsGxsForums() { return; } + :RsGxsIfaceHelper(gxs) { return; } + virtual ~RsGxsForums() { return; } /* Specific Service Data */ -virtual bool getGroupData(const uint32_t &token, std::vector &groups) = 0; -virtual bool getMsgData(const uint32_t &token, std::vector &msgs) = 0; -//Not currently used -//virtual bool getRelatedMessages(const uint32_t &token, std::vector &msgs) = 0; + virtual bool getGroupData(const uint32_t &token, std::vector &groups) = 0; + virtual bool getMsgData(const uint32_t &token, std::vector &msgs) = 0; + //Not currently used + //virtual bool getRelatedMessages(const uint32_t &token, std::vector &msgs) = 0; - ////////////////////////////////////////////////////////////////////////////// -virtual void setMessageReadStatus(uint32_t& token, const RsGxsGrpMsgIdPair& msgId, bool read) = 0; + ////////////////////////////////////////////////////////////////////////////// + virtual void setMessageReadStatus(uint32_t& token, const RsGxsGrpMsgIdPair& msgId, bool read) = 0; -//virtual bool setMessageStatus(const std::string &msgId, const uint32_t status, const uint32_t statusMask); -//virtual bool setGroupSubscribeFlags(const std::string &groupId, uint32_t subscribeFlags, uint32_t subscribeMask); + //virtual bool setMessageStatus(const std::string &msgId, const uint32_t status, const uint32_t statusMask); + //virtual bool setGroupSubscribeFlags(const std::string &groupId, uint32_t subscribeFlags, uint32_t subscribeMask); -//virtual bool groupRestoreKeys(const std::string &groupId); -//virtual bool groupShareKeys(const std::string &groupId, std::list& peers); + //virtual bool groupRestoreKeys(const std::string &groupId); + //virtual bool groupShareKeys(const std::string &groupId, std::list& peers); -virtual bool createGroup(uint32_t &token, RsGxsForumGroup &group) = 0; -virtual bool createMsg(uint32_t &token, RsGxsForumMsg &msg) = 0; - -/*! + virtual bool createGroup(uint32_t &token, RsGxsForumGroup &group) = 0; + virtual bool createMsg(uint32_t &token, RsGxsForumMsg &msg) = 0; + /*! * To update forum group with new information * @param token the token used to check completion status of update * @param group group to be updated, groupId element must be set or will be rejected * @return false groupId not set, true if set and accepted (still check token for completion) */ -virtual bool updateGroup(uint32_t &token, RsGxsForumGroup &group) = 0; + virtual bool updateGroup(uint32_t &token, RsGxsForumGroup &group) = 0; }; diff --git a/libretroshare/src/retroshare/rsgxsiface.h b/libretroshare/src/retroshare/rsgxsiface.h index 8eb1ac6ae..3e2ae55ae 100644 --- a/libretroshare/src/retroshare/rsgxsiface.h +++ b/libretroshare/src/retroshare/rsgxsiface.h @@ -27,6 +27,7 @@ #ifndef RSGXSIFACE_H_ #define RSGXSIFACE_H_ +#include "retroshare/rsreputations.h" #include "retroshare/rsgxsservice.h" #include "gxs/rsgxsdata.h" #include "retroshare/rsgxsifacetypes.h" @@ -181,6 +182,8 @@ public: virtual uint32_t getDefaultSyncPeriod() = 0; virtual uint32_t getSyncPeriod(const RsGxsGroupId& grpId) = 0; virtual void setSyncPeriod(const RsGxsGroupId& grpId,uint32_t age_in_secs) = 0; + + virtual RsReputations::ReputationLevel minReputationForForwardingMessages(uint32_t group_sign_flags,uint32_t identity_flags)=0; }; diff --git a/libretroshare/src/retroshare/rsgxsifacehelper.h b/libretroshare/src/retroshare/rsgxsifacehelper.h index e23f1aeb1..fbe12570e 100644 --- a/libretroshare/src/retroshare/rsgxsifacehelper.h +++ b/libretroshare/src/retroshare/rsgxsifacehelper.h @@ -27,6 +27,7 @@ */ #include "retroshare/rsgxsiface.h" +#include "retroshare/rsreputations.h" #include "rsgxsflags.h" /*! @@ -236,6 +237,10 @@ public: mGxs->setSyncPeriod(grpId,age_in_secs); } + RsReputations::ReputationLevel minReputationForForwardingMessages(uint32_t group_sign_flags,uint32_t identity_flags) + { + return mGxs->minReputationForForwardingMessages(group_sign_flags,identity_flags); + } private: RsGxsIface* mGxs; diff --git a/libretroshare/src/retroshare/rsidentity.h b/libretroshare/src/retroshare/rsidentity.h index e7a7abbe8..dff973204 100644 --- a/libretroshare/src/retroshare/rsidentity.h +++ b/libretroshare/src/retroshare/rsidentity.h @@ -184,8 +184,6 @@ public: // Cyril: Reputation details. At some point we might want to merge information // between the two into a single global score. Since the old reputation system // is not finished yet, I leave this in place. We should decide what to do with it. - - GxsReputation mReputation_oldSystem; // this is the old "mReputation" field, which apparently is not used. RsReputations::ReputationInfo mReputation; // avatar @@ -193,6 +191,7 @@ public: // last usage time_t mLastUsageTS ; + std::map mUseCases ; }; @@ -254,7 +253,14 @@ public: virtual bool setAsRegularContact(const RsGxsId& id,bool is_a_contact) = 0 ; virtual bool isARegularContact(const RsGxsId& id) = 0 ; - virtual bool isBanned(const RsGxsId& id) =0; + + /*! + * \brief overallReputationLevel + * Returns the overall reputation level of the supplied identity. See rsreputations.h + * \param id + * \return + */ + virtual RsReputations::ReputationLevel overallReputationLevel(const RsGxsId& id)=0; virtual time_t getLastUsageTS(const RsGxsId &id) =0; // Specific RsIdentity Functions.... diff --git a/libretroshare/src/retroshare/rsreputations.h b/libretroshare/src/retroshare/rsreputations.h index dac28ad38..1102a45be 100644 --- a/libretroshare/src/retroshare/rsreputations.h +++ b/libretroshare/src/retroshare/rsreputations.h @@ -36,17 +36,28 @@ public: // This is the interface file for the reputation system // - enum Opinion { OPINION_NEGATIVE = 0, OPINION_NEUTRAL = 1, OPINION_POSITIVE = 2 } ; - enum Assessment { ASSESSMENT_BAD = 0, ASSESSMENT_OK = 1 } ; + enum Opinion { OPINION_NEGATIVE = 0, OPINION_NEUTRAL = 1, OPINION_POSITIVE = 2 } ; + + enum ReputationLevel { REPUTATION_LOCALLY_NEGATIVE = 0x00, // local opinion is positive + REPUTATION_REMOTELY_NEGATIVE = 0x01, // local opinion is neutral and friends are positive in average + REPUTATION_NEUTRAL = 0x02, // no reputation information ; + REPUTATION_REMOTELY_POSITIVE = 0x03, // local opinion is neutral and friends are negative in average + REPUTATION_LOCALLY_POSITIVE = 0x04, // local opinion is negative + REPUTATION_UNKNOWN = 0x05 // missing info + }; struct ReputationInfo { - ReputationInfo() : mOwnOpinion(OPINION_NEUTRAL), mOverallReputationScore(REPUTATION_THRESHOLD_DEFAULT), mFriendAverage(REPUTATION_THRESHOLD_DEFAULT),mAssessment(ASSESSMENT_OK){} + ReputationInfo() : mOwnOpinion(OPINION_NEUTRAL), mFriendAverageScore(REPUTATION_THRESHOLD_DEFAULT),mOverallReputationLevel(REPUTATION_NEUTRAL){} RsReputations::Opinion mOwnOpinion ; - float mOverallReputationScore ; - float mFriendAverage ; - RsReputations::Assessment mAssessment; // this should help clients in taking decisions + + uint32_t mFriendsPositiveVotes ; + uint32_t mFriendsNegativeVotes ; + + float mFriendAverageScore ; + + RsReputations::ReputationLevel mOverallReputationLevel; // this should help clients in taking decisions }; virtual bool setOwnOpinion(const RsGxsId& key_id, const Opinion& op) =0; @@ -54,16 +65,16 @@ public: // parameters - // virtual void setNodeAutoBanThreshold(uint32_t n) =0; - // virtual uint32_t nodeAutoBanThreshold() =0; - virtual void setNodeAutoPositiveOpinionForContacts(bool b) =0; virtual bool nodeAutoPositiveOpinionForContacts() =0; - virtual float nodeAutoBanIdentitiesLimit() =0; - virtual void setNodeAutoBanIdentitiesLimit(float f) =0; - // This one is a proxy designed to allow fast checking of a GXS id. - // it basically returns true if assessment is not ASSESSMENT_OK + virtual uint32_t thresholdForRemotelyNegativeReputation()=0; + virtual uint32_t thresholdForRemotelyPositiveReputation()=0; + virtual void setThresholdForRemotelyNegativeReputation(uint32_t thresh)=0; + virtual void setThresholdForRemotelyPositiveReputation(uint32_t thresh)=0; + + // This one is a proxy designed to allow fast checking of a GXS id. + // it basically returns true if assessment is not ASSESSMENT_OK virtual bool isIdentityBanned(const RsGxsId& id) =0; diff --git a/libretroshare/src/retroshare/rsservicecontrol.h b/libretroshare/src/retroshare/rsservicecontrol.h index 76c2ee811..a6d8fde7c 100644 --- a/libretroshare/src/retroshare/rsservicecontrol.h +++ b/libretroshare/src/retroshare/rsservicecontrol.h @@ -108,6 +108,7 @@ class RsServiceControl virtual ~RsServiceControl() { return; } virtual bool getOwnServices(RsPeerServiceInfo &info) = 0; +virtual std::string getServiceName(uint32_t service_id) = 0; virtual bool getServicesAllowed(const RsPeerId &peerId, RsPeerServiceInfo &info) = 0; virtual bool getServicesProvided(const RsPeerId &peerId, RsPeerServiceInfo &info) = 0; diff --git a/libretroshare/src/services/p3gxscircles.cc b/libretroshare/src/services/p3gxscircles.cc index b36dc141e..c9087cea1 100644 --- a/libretroshare/src/services/p3gxscircles.cc +++ b/libretroshare/src/services/p3gxscircles.cc @@ -1094,7 +1094,7 @@ bool p3GxsCircles::locked_processLoadingCacheEntry(RsGxsCircleCache& cache) rsPeers->getOnlineList(peers) ; } - mIdentities->requestKey(pit->first, peers); + mIdentities->requestKey(pit->first, peers,"Membership status check in Circle "+cache.mCircleName+" ("+cache.mCircleId.toStdString()+")"); //isUnprocessedPeers = true; } } diff --git a/libretroshare/src/services/p3gxsreputation.cc b/libretroshare/src/services/p3gxsreputation.cc index 68933e4a8..ea0c7f519 100644 --- a/libretroshare/src/services/p3gxsreputation.cc +++ b/libretroshare/src/services/p3gxsreputation.cc @@ -141,6 +141,9 @@ static const uint32_t BANNED_NODES_UPDATE_DELAY = 313 ; // update static const uint32_t REPUTATION_INFO_KEEP_DELAY = 86400*35; // remove old reputation info 5 days after last usage limit, in case the ID would come back.. static const uint32_t BANNED_NODES_INACTIVITY_KEEP = 86400*60; // remove all info about banned nodes after 2 months of inactivity +static const uint32_t REPUTATION_DEFAULT_MIN_VOTES_FOR_REMOTELY_POSITIVE = 1; // min difference in votes that makes friends opinion globally positive +static const uint32_t REPUTATION_DEFAULT_MIN_VOTES_FOR_REMOTELY_NEGATIVE = 1; // min difference in votes that makes friends opinion globally negative + p3GxsReputation::p3GxsReputation(p3LinkMgr *lm) :p3Service(), p3Config(), mReputationMtx("p3GxsReputation"), mLinkMgr(lm) @@ -157,8 +160,9 @@ p3GxsReputation::p3GxsReputation(p3LinkMgr *lm) mLastBannedNodesUpdate = 0 ; mBannedNodesProxyNeedsUpdate = false; - mAutoBanIdentitiesLimit = REPUTATION_ASSESSMENT_THRESHOLD_X1; mAutoSetPositiveOptionToContacts = true; // default + mMinVotesForRemotelyPositive = REPUTATION_DEFAULT_MIN_VOTES_FOR_REMOTELY_POSITIVE; + mMinVotesForRemotelyNegative = REPUTATION_DEFAULT_MIN_VOTES_FOR_REMOTELY_NEGATIVE; } const std::string GXS_REPUTATION_APP_NAME = "gxsreputation"; @@ -256,27 +260,6 @@ bool p3GxsReputation::nodeAutoPositiveOpinionForContacts() RsStackMutex stack(mReputationMtx); /****** LOCKED MUTEX *******/ return mAutoSetPositiveOptionToContacts ; } -float p3GxsReputation::nodeAutoBanIdentitiesLimit() -{ - RsStackMutex stack(mReputationMtx); /****** LOCKED MUTEX *******/ - return mAutoBanIdentitiesLimit - 1.0f; -} -void p3GxsReputation::setNodeAutoBanIdentitiesLimit(float f) -{ - RsStackMutex stack(mReputationMtx); /****** LOCKED MUTEX *******/ - - if(f < -1.0 || f >= 0.0) - { - std::cerr << "(EE) Unexpected value for auto ban identities limit: " << f << std::endl; - return ; - } - if(f != mAutoBanIdentitiesLimit) - { - mLastBannedNodesUpdate = 0 ; - mAutoBanIdentitiesLimit = f+1.0 ; - IndicateConfigChanged() ; - } -} int p3GxsReputation::status() { @@ -795,8 +778,9 @@ bool p3GxsReputation::getReputationInfo(const RsGxsId& gxsid, const RsPgpId& own if(it == mReputations.end()) { info.mOwnOpinion = RsReputations::OPINION_NEUTRAL ; - info.mOverallReputationScore = RsReputations::REPUTATION_THRESHOLD_DEFAULT ; - info.mFriendAverage = REPUTATION_THRESHOLD_DEFAULT ; + info.mFriendAverageScore = REPUTATION_THRESHOLD_DEFAULT ; + info.mFriendsNegativeVotes = 0 ; + info.mFriendsPositiveVotes = 0 ; owner_id = ownerNode ; } @@ -805,8 +789,9 @@ bool p3GxsReputation::getReputationInfo(const RsGxsId& gxsid, const RsPgpId& own Reputation& rep(it->second) ; info.mOwnOpinion = RsReputations::Opinion(rep.mOwnOpinion) ; - info.mOverallReputationScore = rep.mReputation ; - info.mFriendAverage = rep.mFriendAverage ; + info.mFriendAverageScore = rep.mFriendAverage ; + info.mFriendsNegativeVotes = rep.mFriendsNegative ; + info.mFriendsPositiveVotes = rep.mFriendsPositive ; if(rep.mOwnerNode.isNull()) rep.mOwnerNode = ownerNode ; @@ -814,33 +799,67 @@ bool p3GxsReputation::getReputationInfo(const RsGxsId& gxsid, const RsPgpId& own owner_id = rep.mOwnerNode ; } + // now compute overall score and reputation + + // 0 - check for own opinion. If positive or negative, it decides on the result + + if(info.mOwnOpinion == RsReputations::OPINION_NEGATIVE) + { + // own opinion is always read in priority + + info.mOverallReputationLevel = RsReputations::REPUTATION_LOCALLY_NEGATIVE ; + return true ; + } + if(info.mOwnOpinion == RsReputations::OPINION_POSITIVE) + { + // own opinion is always read in priority + + info.mOverallReputationLevel = RsReputations::REPUTATION_LOCALLY_POSITIVE ; + return true ; + } + + // 1 - check for banned PGP ids. + std::map::iterator it2 ; if(!owner_id.isNull() && (it2 = mBannedPgpIds.find(owner_id))!=mBannedPgpIds.end()) { + // Check if current identity is present in the list of known identities for this banned node. + if(it2->second.known_identities.find(gxsid) == it2->second.known_identities.end()) { it2->second.known_identities.insert(gxsid) ; it2->second.last_activity_TS = now ; + + // if so, update + mBannedNodesProxyNeedsUpdate = true ; } - info.mAssessment = RsReputations::ASSESSMENT_BAD ; #ifdef DEBUG_REPUTATION2 std::cerr << "p3GxsReputations: identity " << gxsid << " is banned because owner node ID " << owner_id << " is banned (found in banned nodes list)." << std::endl; #endif + info.mOverallReputationLevel = RsReputations::REPUTATION_LOCALLY_NEGATIVE ; + return true ; } - else if(mPerNodeBannedIdsProxy.find(gxsid) != mPerNodeBannedIdsProxy.end()) + // also check the proxy + + if(mPerNodeBannedIdsProxy.find(gxsid) != mPerNodeBannedIdsProxy.end()) { #ifdef DEBUG_REPUTATION2 std::cerr << "p3GxsReputations: identity " << gxsid << " is banned because owner node ID " << owner_id << " is banned (found in proxy)." << std::endl; #endif - info.mAssessment = RsReputations::ASSESSMENT_BAD ; + info.mOverallReputationLevel = RsReputations::REPUTATION_LOCALLY_NEGATIVE ; + return true; } - else if(info.mOverallReputationScore <= mAutoBanIdentitiesLimit) - info.mAssessment = RsReputations::ASSESSMENT_BAD ; + // 2 - now, our own opinion is neutral, which means we rely on what our friends tell + + if(info.mFriendsPositiveVotes >= info.mFriendsNegativeVotes + mMinVotesForRemotelyPositive) + info.mOverallReputationLevel = RsReputations::REPUTATION_REMOTELY_POSITIVE ; + else if(info.mFriendsPositiveVotes + mMinVotesForRemotelyNegative <= info.mFriendsNegativeVotes) + info.mOverallReputationLevel = RsReputations::REPUTATION_REMOTELY_NEGATIVE ; else - info.mAssessment = RsReputations::ASSESSMENT_OK ; + info.mOverallReputationLevel = RsReputations::REPUTATION_NEUTRAL ; #ifdef DEBUG_REPUTATION2 std::cerr << " information present. OwnOp = " << info.mOwnOpinion << ", owner node=" << owner_id << ", overall score=" << info.mAssessment << std::endl; @@ -849,6 +868,36 @@ bool p3GxsReputation::getReputationInfo(const RsGxsId& gxsid, const RsPgpId& own return true ; } +uint32_t p3GxsReputation::thresholdForRemotelyNegativeReputation() +{ + RsStackMutex stack(mReputationMtx); /****** LOCKED MUTEX *******/ + return mMinVotesForRemotelyNegative ; +} +uint32_t p3GxsReputation::thresholdForRemotelyPositiveReputation() +{ + RsStackMutex stack(mReputationMtx); /****** LOCKED MUTEX *******/ + return mMinVotesForRemotelyPositive ; +} +void p3GxsReputation::setThresholdForRemotelyPositiveReputation(uint32_t thresh) +{ + RsStackMutex stack(mReputationMtx); /****** LOCKED MUTEX *******/ + if(mMinVotesForRemotelyPositive == thresh || thresh==0) + return ; + + mMinVotesForRemotelyPositive = thresh ; + IndicateConfigChanged(); +} + +void p3GxsReputation::setThresholdForRemotelyNegativeReputation(uint32_t thresh) +{ + RsStackMutex stack(mReputationMtx); /****** LOCKED MUTEX *******/ + if(mMinVotesForRemotelyNegative == thresh || thresh==0) + return ; + + mMinVotesForRemotelyNegative = thresh ; + IndicateConfigChanged(); +} + void p3GxsReputation::banNode(const RsPgpId& id,bool b) { RsStackMutex stack(mReputationMtx); /****** LOCKED MUTEX *******/ @@ -887,7 +936,7 @@ bool p3GxsReputation::isIdentityBanned(const RsGxsId &id) #ifdef DEBUG_REPUTATION std::cerr << "isIdentityBanned(): returning " << (info.mAssessment == RsReputations::ASSESSMENT_BAD) << " for GXS id " << id << std::endl; #endif - return info.mAssessment == RsReputations::ASSESSMENT_BAD ; + return info.mOverallReputationLevel == RsReputations::REPUTATION_LOCALLY_NEGATIVE ; } bool p3GxsReputation::setOwnOpinion(const RsGxsId& gxsid, const RsReputations::Opinion& opinion) @@ -1026,13 +1075,14 @@ bool p3GxsReputation::saveList(bool& cleanup, std::list &savelist) RsConfigKeyValueSet *vitem = new RsConfigKeyValueSet ; RsTlvKeyValue kv; -// kv.key = "AUTO_BAN_NODES_THRESHOLD" ; -// rs_sprintf(kv.value, "%d", mPgpAutoBanThreshold); -// vitem->tlvkvs.pairs.push_back(kv) ; - kv.key = "AUTO_BAN_IDENTITIES_THRESHOLD" ; - rs_sprintf(kv.value, "%f", mAutoBanIdentitiesLimit); - vitem->tlvkvs.pairs.push_back(kv) ; + kv.key = "AUTO_REMOTELY_POSITIVE_THRESHOLD" ; + rs_sprintf(kv.value, "%d", mMinVotesForRemotelyPositive); + vitem->tlvkvs.pairs.push_back(kv) ; + + kv.key = "AUTO_REMOTELY_NEGATIVE_THRESHOLD" ; + rs_sprintf(kv.value, "%d", mMinVotesForRemotelyNegative); + vitem->tlvkvs.pairs.push_back(kv) ; kv.key = "AUTO_POSITIVE_CONTACTS" ; kv.value = mAutoSetPositiveOptionToContacts?"YES":"NO"; @@ -1093,27 +1143,24 @@ bool p3GxsReputation::loadList(std::list& loadList) if(vitem) for(std::list::const_iterator kit = vitem->tlvkvs.pairs.begin(); kit != vitem->tlvkvs.pairs.end(); ++kit) { -// if(kit->key == "AUTO_BAN_NODES_THRESHOLD") -// { -// int val ; -// if (sscanf(kit->value.c_str(), "%d", &val) == 1) -// { -// mPgpAutoBanThreshold = val ; -// std::cerr << "Setting AutoBanNode threshold to " << val << std::endl ; -// mLastBannedNodesUpdate = 0 ; // force update -// } -// }; - if(kit->key == "AUTO_BAN_IDENTITIES_THRESHOLD") - { - float val ; - - if (sscanf(kit->value.c_str(), "%f", &val) == 1) - { - mAutoBanIdentitiesLimit = val ; - std::cerr << "Setting AutoBanIdentity threshold to " << val << std::endl ; - mLastBannedNodesUpdate = 0 ; // force update - } - }; + if(kit->key == "AUTO_REMOTELY_POSITIVE_THRESHOLD") + { + int val ; + if (sscanf(kit->value.c_str(), "%d", &val) == 1) + { + mMinVotesForRemotelyPositive = val ; + std::cerr << "Setting mMinVotesForRemotelyPositive threshold to " << val << std::endl ; + } + }; + if(kit->key == "AUTO_REMOTELY_NEGATIVE_THRESHOLD") + { + int val ; + if (sscanf(kit->value.c_str(), "%d", &val) == 1) + { + mMinVotesForRemotelyNegative = val ; + std::cerr << "Setting mMinVotesForRemotelyNegative threshold to " << val << std::endl ; + } + }; if(kit->key == "AUTO_POSITIVE_CONTACTS") { mAutoSetPositiveOptionToContacts = (kit->value == "YES"); @@ -1292,11 +1339,22 @@ void Reputation::updateReputation() int friend_total = 0; + mFriendsNegative = 0 ; + mFriendsPositive = 0 ; + // accounts for all friends. Neutral opinions count for 1-1=0 // because the average is performed over only accessible peers (not the total number) we need to shift to 1 for(std::map::const_iterator it(mOpinions.begin());it!=mOpinions.end();++it) + { + if(it->second == RsReputations::OPINION_NEGATIVE) + ++mFriendsNegative ; + + if(it->second == RsReputations::OPINION_POSITIVE) + ++mFriendsPositive ; + friend_total += it->second - 1; + } if(mOpinions.empty()) // includes the case of no friends! mFriendAverage = 1.0f ; @@ -1351,9 +1409,9 @@ void Reputation::updateReputation() // now compute a bias for PGP-signed ids. if(mOwnOpinion == RsReputations::OPINION_NEUTRAL) - mReputation = mFriendAverage ; + mReputationScore = mFriendAverage ; else - mReputation = (float)mOwnOpinion ; + mReputationScore = (float)mOwnOpinion ; } void p3GxsReputation::debug_print() @@ -1366,7 +1424,7 @@ void p3GxsReputation::debug_print() for(std::map::const_iterator it(mReputations.begin());it!=mReputations.end();++it) { - std::cerr << " " << it->first << ": own: " << it->second.mOwnOpinion << ", Friend average: " << it->second.mFriendAverage << ", global_score: " << it->second.mReputation + std::cerr << " " << it->first << ": own: " << it->second.mOwnOpinion << ", Friend average: " << it->second.mFriendAverage << ", global_score: " << it->second.mReputationScore << ", last own update: " << now - it->second.mOwnOpinionTs << " secs ago." << std::endl; #ifdef DEBUG_REPUTATION2 for(std::map::const_iterator it2(it->second.mOpinions.begin());it2!=it->second.mOpinions.end();++it2) diff --git a/libretroshare/src/services/p3gxsreputation.h b/libretroshare/src/services/p3gxsreputation.h index 94fb356e1..da7831987 100644 --- a/libretroshare/src/services/p3gxsreputation.h +++ b/libretroshare/src/services/p3gxsreputation.h @@ -69,10 +69,10 @@ class Reputation { public: Reputation() - :mOwnOpinion(RsReputations::OPINION_NEUTRAL), mOwnOpinionTs(0),mFriendAverage(1.0f), mReputation(RsReputations::OPINION_NEUTRAL),mIdentityFlags(REPUTATION_IDENTITY_FLAG_NEEDS_UPDATE){ } + :mOwnOpinion(RsReputations::OPINION_NEUTRAL), mOwnOpinionTs(0),mFriendAverage(1.0f), mReputationScore(RsReputations::OPINION_NEUTRAL),mIdentityFlags(REPUTATION_IDENTITY_FLAG_NEEDS_UPDATE){ } Reputation(const RsGxsId& /*about*/) - :mOwnOpinion(RsReputations::OPINION_NEUTRAL), mOwnOpinionTs(0),mFriendAverage(1.0f), mReputation(RsReputations::OPINION_NEUTRAL),mIdentityFlags(REPUTATION_IDENTITY_FLAG_NEEDS_UPDATE){ } + :mOwnOpinion(RsReputations::OPINION_NEUTRAL), mOwnOpinionTs(0),mFriendAverage(1.0f), mReputationScore(RsReputations::OPINION_NEUTRAL),mIdentityFlags(REPUTATION_IDENTITY_FLAG_NEEDS_UPDATE){ } void updateReputation(); @@ -80,12 +80,15 @@ public: int32_t mOwnOpinion; time_t mOwnOpinionTs; - float mFriendAverage ; - float mReputation; - - RsPgpId mOwnerNode; + float mFriendAverage ; + uint32_t mFriendsPositive ; // number of positive vites from friends + uint32_t mFriendsNegative ; // number of negative vites from friends + + float mReputationScore; + + RsPgpId mOwnerNode; - uint32_t mIdentityFlags; + uint32_t mIdentityFlags; }; @@ -109,13 +112,13 @@ public: virtual bool isNodeBanned(const RsPgpId& id); virtual void banNode(const RsPgpId& id,bool b) ; - //virtual void setNodeAutoBanThreshold(uint32_t n) ; - //virtual uint32_t nodeAutoBanThreshold() ; - virtual void setNodeAutoPositiveOpinionForContacts(bool b) ; virtual bool nodeAutoPositiveOpinionForContacts() ; - virtual float nodeAutoBanIdentitiesLimit() ; - virtual void setNodeAutoBanIdentitiesLimit(float f) ; + + uint32_t thresholdForRemotelyNegativeReputation(); + uint32_t thresholdForRemotelyPositiveReputation(); + void setThresholdForRemotelyNegativeReputation(uint32_t thresh); + void setThresholdForRemotelyPositiveReputation(uint32_t thresh); /***** overloaded from p3Service *****/ virtual int tick(); @@ -180,8 +183,10 @@ private: // PGP Ids auto-banned. This is updated regularly. std::map mBannedPgpIds ; std::set mPerNodeBannedIdsProxy ; - //uint32_t mPgpAutoBanThreshold ; bool mBannedNodesProxyNeedsUpdate ; + + uint32_t mMinVotesForRemotelyPositive ; + uint32_t mMinVotesForRemotelyNegative ; }; #endif //SERVICE_RSGXSREPUTATION_HEADER diff --git a/libretroshare/src/services/p3idservice.cc b/libretroshare/src/services/p3idservice.cc index 6914ebaa5..d0154be6e 100644 --- a/libretroshare/src/services/p3idservice.cc +++ b/libretroshare/src/services/p3idservice.cc @@ -128,8 +128,9 @@ RsIdentity *rsIdentity = NULL; /* delays */ -#define CACHETEST_PERIOD 60 +#define CACHETEST_PERIOD 60 #define DELAY_BETWEEN_CONFIG_UPDATES 300 +#define GXS_MAX_KEY_TS_USAGE_MAP_SIZE 5 #define OWNID_RELOAD_DELAY 10 @@ -255,23 +256,47 @@ void p3IdService::slowIndicateConfigChanged() } time_t p3IdService::locked_getLastUsageTS(const RsGxsId& gxs_id) { - std::map::const_iterator it = mKeysTS.find(gxs_id) ; + std::map::const_iterator it = mKeysTS.find(gxs_id) ; if(it == mKeysTS.end()) return 0 ; else - return it->second ; + return it->second.TS ; } -void p3IdService::timeStampKey(const RsGxsId& gxs_id) +void p3IdService::timeStampKey(const RsGxsId& gxs_id, const std::string& reason) { if(rsReputations->isIdentityBanned(gxs_id) ) { std::cerr << "(II) p3IdService:timeStampKey(): refusing to time stamp key " << gxs_id << " because it is banned." << std::endl; return ; } + std::cerr << "(II) time stamping key " << gxs_id << " for the following reason: " << reason << std::endl; RS_STACK_MUTEX(mIdMtx) ; - mKeysTS[gxs_id] = time(NULL) ; + + time_t now = time(NULL) ; + + keyTSInfo& info(mKeysTS[gxs_id]) ; + + info.TS = now ; + info.usage_map[reason] = now; + + while(info.usage_map.size() > GXS_MAX_KEY_TS_USAGE_MAP_SIZE) + { + // This is very costly, but normally the outerloop should never be rolled more than once. + + std::map::iterator best_it ; + time_t best_time = now+1; + + for(std::map::iterator it(info.usage_map.begin());it!=info.usage_map.end();++it) + if(it->second < best_time) + { + best_time = it->second ; + best_it = it; + } + + info.usage_map.erase(best_it) ; + } slowIndicateConfigChanged() ; } @@ -286,7 +311,7 @@ bool p3IdService::loadList(std::list& items) if( (lii = dynamic_cast(*it)) != NULL) { for(std::map::const_iterator it2 = lii->mTimeStamps.begin();it2!=lii->mTimeStamps.end();++it2) - mKeysTS.insert(*it2) ; + mKeysTS[it2->first].TS = it2->second; mContacts = lii->mContacts ; } @@ -307,7 +332,10 @@ bool p3IdService::saveList(bool& cleanup,std::list& items) RS_STACK_MUTEX(mIdMtx) ; cleanup = true ; RsGxsIdLocalInfoItem *item = new RsGxsIdLocalInfoItem ; - item->mTimeStamps = mKeysTS ; + + for(std::map::const_iterator it(mKeysTS.begin());it!=mKeysTS.end();++it) + item->mTimeStamps[it->first] = it->second.TS; + item->mContacts = mContacts ; items.push_back(item) ; @@ -317,7 +345,7 @@ bool p3IdService::saveList(bool& cleanup,std::list& items) class IdCacheEntryCleaner { public: - IdCacheEntryCleaner(const std::map& last_usage_TSs) : mLastUsageTS(last_usage_TSs) {} + IdCacheEntryCleaner(const std::map& last_usage_TSs) : mLastUsageTS(last_usage_TSs) {} bool processEntry(RsGxsIdCache& entry) { @@ -338,11 +366,11 @@ public: return true ; } - std::map::const_iterator it = mLastUsageTS.find(gxs_id) ; + std::map::const_iterator it = mLastUsageTS.find(gxs_id) ; bool no_ts = (it == mLastUsageTS.end()) ; - time_t last_usage_ts = no_ts?0:(it->second); + time_t last_usage_ts = no_ts?0:(it->second.TS); time_t max_keep_time ; if(no_ts) @@ -370,7 +398,7 @@ public: } std::list ids_to_delete ; - const std::map& mLastUsageTS; + const std::map& mLastUsageTS; }; void p3IdService::cleanUnusedKeys() @@ -495,7 +523,7 @@ void p3IdService::notifyChanges(std::vector &changes) // also time_stamp the key that this group represents - timeStampKey(RsGxsId(*git)) ; + timeStampKey(RsGxsId(*git),"Group meta data changed") ; ++git; } @@ -552,7 +580,16 @@ bool p3IdService::getIdDetails(const RsGxsId &id, RsIdentityDetails &details) rsReputations->setOwnOpinion(id,RsReputations::OPINION_POSITIVE) ; details = data.details; - details.mLastUsageTS = locked_getLastUsageTS(id) ; + + std::map::const_iterator it = mKeysTS.find(id) ; + + if(it == mKeysTS.end()) + details.mLastUsageTS = 0 ; + else + { + details.mLastUsageTS = it->second.TS ; + details.mUseCases = it->second.usage_map ; + } // one utf8 symbol can be at most 4 bytes long - would be better to measure real unicode length !!! if(details.mNickname.length() > RSID_MAXIMUM_NICKNAME_SIZE*4) @@ -570,15 +607,12 @@ bool p3IdService::getIdDetails(const RsGxsId &id, RsIdentityDetails &details) return false; } -bool p3IdService::isBanned(const RsGxsId &id) +RsReputations::ReputationLevel p3IdService::overallReputationLevel(const RsGxsId &id) { RsIdentityDetails det ; getIdDetails(id,det) ; -#ifdef DEBUG_REPUTATION - std::cerr << "isIdentityBanned(): returning " << (det.mReputation.mAssessment == RsReputations::ASSESSMENT_BAD) << " for GXS id " << id << std::endl; -#endif - return det.mReputation.mAssessment == RsReputations::ASSESSMENT_BAD ; + return det.mReputation.mOverallReputationLevel ; } bool p3IdService::isOwnId(const RsGxsId& id) @@ -752,7 +786,7 @@ static void mergeIds(std::map >& idmap,const RsGxsId old_peers.push_back(*it) ; } -bool p3IdService::requestKey(const RsGxsId &id, const std::list& peers) +bool p3IdService::requestKey(const RsGxsId &id, const std::list& peers,const std::string& info) { if(id.isNull()) { @@ -773,12 +807,12 @@ bool p3IdService::requestKey(const RsGxsId &id, const std::list& peers RsReputations::ReputationInfo info ; rsReputations->getReputationInfo(id,RsPgpId(),info) ; - if(info.mAssessment == RsReputations::ASSESSMENT_BAD) + if(info.mOverallReputationLevel == RsReputations::REPUTATION_LOCALLY_NEGATIVE) { std::cerr << "(II) not requesting Key " << id << " because it has been banned." << std::endl; { - RsStackMutex stack(mIdMtx); /********** STACK LOCKED MTX ******/ + RS_STACK_MUTEX(mIdMtx); /********** STACK LOCKED MTX ******/ mIdsNotPresent.erase(id) ; } return true; @@ -796,6 +830,10 @@ bool p3IdService::requestKey(const RsGxsId &id, const std::list& peers return true ; } } + { + RS_STACK_MUTEX(mIdMtx); /********** STACK LOCKED MTX ******/ + mKeysTS[id].usage_map["Requested to friends: "+info] = time(NULL) ; + } return cache_request_load(id, peers); } @@ -892,10 +930,10 @@ 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) ; + timeStampKey(own_gxs_id,"Own GXS id") ; return true ; } -bool p3IdService::validateData(const uint8_t *data,uint32_t data_size,const RsTlvKeySignature& signature,bool force_load,uint32_t& signing_error) +bool p3IdService::validateData(const uint8_t *data,uint32_t data_size,const RsTlvKeySignature& signature,bool force_load,const std::string& info_string,uint32_t& signing_error) { // RsIdentityDetails details ; // getIdDetails(signature.keyId,details); @@ -929,7 +967,7 @@ bool p3IdService::validateData(const uint8_t *data,uint32_t data_size,const RsTl } signing_error = RS_GIXS_ERROR_NO_ERROR ; - timeStampKey(signature.keyId) ; + timeStampKey(signature.keyId,"Used in signature checking: "+info_string ) ; return true ; } bool p3IdService::encryptData(const uint8_t *decrypted_data,uint32_t decrypted_data_size,uint8_t *& encrypted_data,uint32_t& encrypted_data_size,const RsGxsId& encryption_key_id,bool force_load,uint32_t& error_status) @@ -957,7 +995,7 @@ bool p3IdService::encryptData(const uint8_t *decrypted_data,uint32_t decrypted_d return false ; } error_status = RS_GIXS_ERROR_NO_ERROR ; - timeStampKey(encryption_key_id) ; + timeStampKey(encryption_key_id,"Used to encrypt data") ; return true ; } @@ -989,7 +1027,7 @@ bool p3IdService::decryptData(const uint8_t *encrypted_data,uint32_t encrypted_d return false ; } error_status = RS_GIXS_ERROR_NO_ERROR ; - timeStampKey(key_id) ; + timeStampKey(key_id,"Used to decrypt data") ; return true ; } @@ -2399,7 +2437,8 @@ bool p3IdService::cache_load_ownids(uint32_t token) // This prevents automatic deletion to get rid of them. // In other words, own ids are always used. - mKeysTS[RsGxsId(item->meta.mGroupId)] = time(NULL) ; + + mKeysTS[RsGxsId(item->meta.mGroupId)].TS = time(NULL) ; } delete item ; } @@ -2493,7 +2532,7 @@ bool p3IdService::cachetest_handlerequest(uint32_t token) if (!haveKey(*vit)) { std::list nullpeers; - requestKey(*vit, nullpeers); + requestKey(*vit, nullpeers,"Cache test in p3IdService"); #ifdef DEBUG_IDS std::cerr << "p3IdService::cachetest_request() Requested Key Id: " << *vit; @@ -2691,7 +2730,7 @@ RsGenExchange::ServiceCreate_Return p3IdService::service_CreateGroup(RsGxsGrpIte std::cerr << std::endl; return SERVICE_CREATE_FAIL; } - mKeysTS[RsGxsId(item->meta.mGroupId)] = time(NULL) ; + mKeysTS[RsGxsId(item->meta.mGroupId)].TS = time(NULL) ; /********************* TEMP HACK UNTIL GXS FILLS IN GROUP_ID *****************/ @@ -2851,7 +2890,7 @@ RsGenExchange::ServiceCreate_Return p3IdService::service_CreateGroup(RsGxsGrpIte if (std::find(mOwnIds.begin(), mOwnIds.end(), gxsId) == mOwnIds.end()) { mOwnIds.push_back(gxsId); - mKeysTS[gxsId] = time(NULL) ; + mKeysTS[gxsId].TS = time(NULL) ; } } diff --git a/libretroshare/src/services/p3idservice.h b/libretroshare/src/services/p3idservice.h index f31bbaf50..638a9d2e0 100644 --- a/libretroshare/src/services/p3idservice.h +++ b/libretroshare/src/services/p3idservice.h @@ -274,7 +274,7 @@ public: virtual bool setAsRegularContact(const RsGxsId& id,bool is_a_contact) ; virtual bool isARegularContact(const RsGxsId& id) ; - virtual bool isBanned(const RsGxsId& id) ; + virtual RsReputations::ReputationLevel overallReputationLevel(const RsGxsId& id); virtual time_t getLastUsageTS(const RsGxsId &id) ; /**************** RsGixs Implementation ***************/ @@ -287,7 +287,7 @@ public: virtual bool isOwnId(const RsGxsId& key_id) ; virtual bool signData(const uint8_t *data,uint32_t data_size,const RsGxsId& signer_id,RsTlvKeySignature& signature,uint32_t& signing_error) ; - virtual bool validateData(const uint8_t *data,uint32_t data_size,const RsTlvKeySignature& signature,bool force_load,uint32_t& signing_error) ; + virtual bool validateData(const uint8_t *data,uint32_t data_size,const RsTlvKeySignature& signature,bool force_load,const std::string& info_string,uint32_t& signing_error) ; virtual bool encryptData(const uint8_t *decrypted_data,uint32_t decrypted_data_size,uint8_t *& encrypted_data,uint32_t& encrypted_data_size,const RsGxsId& encryption_key_id,bool force_load,uint32_t& encryption_error) ; virtual bool decryptData(const uint8_t *encrypted_data,uint32_t encrypted_data_size,uint8_t *& decrypted_data,uint32_t& decrypted_data_size,const RsGxsId& encryption_key_id,uint32_t& encryption_error) ; @@ -298,7 +298,7 @@ public: virtual bool getKey(const RsGxsId &id, RsTlvPublicRSAKey &key); virtual bool getPrivateKey(const RsGxsId &id, RsTlvPrivateRSAKey &key); - virtual bool requestKey(const RsGxsId &id, const std::list &peers); + virtual bool requestKey(const RsGxsId &id, const std::list &peers, const std::string &info); virtual bool requestPrivateKey(const RsGxsId &id); @@ -467,7 +467,7 @@ private: void cleanUnusedKeys() ; void slowIndicateConfigChanged() ; - virtual void timeStampKey(const RsGxsId& id) ; + virtual void timeStampKey(const RsGxsId& id,const std::string& reason) ; time_t locked_getLastUsageTS(const RsGxsId& gxs_id); std::string genRandomId(int len = 20); @@ -507,10 +507,19 @@ private: private: + struct keyTSInfo + { + keyTSInfo() : TS(0) {} + + time_t TS ; + std::map usage_map ; + }; + friend class IdCacheEntryCleaner; + std::map > mIdsPendingCache; std::map > mGroupNotPresent; std::map > mIdsNotPresent; - std::map mKeysTS ; + std::map mKeysTS ; // keep a list of regular contacts. This is useful to sort IDs, and allow some services to priviledged ids only. std::set mContacts; diff --git a/libretroshare/src/util/rsprint.cc b/libretroshare/src/util/rsprint.cc index 5735088f4..e692f6c26 100644 --- a/libretroshare/src/util/rsprint.cc +++ b/libretroshare/src/util/rsprint.cc @@ -36,9 +36,13 @@ #include #endif -std::string RsUtil::NumberToString(uint64_t n) +std::string RsUtil::NumberToString(uint64_t n,bool hex) { std::ostringstream os ; + + if(hex) + os << std::hex ; + os << n ; os.flush() ; diff --git a/libretroshare/src/util/rsprint.h b/libretroshare/src/util/rsprint.h index ebcccb8ef..37207ebde 100644 --- a/libretroshare/src/util/rsprint.h +++ b/libretroshare/src/util/rsprint.h @@ -36,7 +36,7 @@ namespace RsUtil { std::string BinToHex(const std::string &bin); std::string BinToHex(const char *arr, const uint32_t len); std::string BinToHex(const unsigned char *arr, const uint32_t len); -std::string NumberToString(uint64_t n); +std::string NumberToString(uint64_t n, bool hex=false); std::string HashId(const std::string &id, bool reverse = false); //std::string AccurateTimeString(); diff --git a/retroshare-gui/src/gui/Identity/IdDetailsDialog.cpp b/retroshare-gui/src/gui/Identity/IdDetailsDialog.cpp index f1419b650..a988a8563 100644 --- a/retroshare-gui/src/gui/Identity/IdDetailsDialog.cpp +++ b/retroshare-gui/src/gui/Identity/IdDetailsDialog.cpp @@ -264,10 +264,9 @@ void IdDetailsDialog::insertIdDetails(uint32_t token) RsReputations::ReputationInfo info ; rsReputations->getReputationInfo(RsGxsId(data.mMeta.mGroupId),data.mPgpId,info) ; - ui->neighborNodesOpinion_TF->setText(QString::number(info.mOverallReputationScore-1.0f)); - - ui->overallOpinion_TF->setText(QString::number(info.mOverallReputationScore-1.0f) +" ("+ - ((info.mAssessment == RsReputations::ASSESSMENT_OK)? tr("OK") : tr("Banned")) +")" ) ; +#warning (csoler) Do we need to do this? This code is apparently not used. + ui->neighborNodesOpinion_TF->setText(QString::number(info.mFriendAverageScore)); + ui->overallOpinion_TF->setText(QString::number(info.mOverallReputationLevel)); switch(info.mOwnOpinion) { diff --git a/retroshare-gui/src/gui/Identity/IdDialog.cpp b/retroshare-gui/src/gui/Identity/IdDialog.cpp index 20bef5ccf..dfba6030a 100644 --- a/retroshare-gui/src/gui/Identity/IdDialog.cpp +++ b/retroshare-gui/src/gui/Identity/IdDialog.cpp @@ -26,6 +26,8 @@ #include #include #include +#include +#include #include "IdDialog.h" #include "ui_IdDialog.h" @@ -127,6 +129,7 @@ class TreeWidgetItem : public QTreeWidgetItem { } } }; + /** Constructor */ IdDialog::IdDialog(QWidget *parent) : RsGxsUpdateBroadcastPage(rsIdentity, parent), @@ -142,12 +145,15 @@ IdDialog::IdDialog(QWidget *parent) : ownItem = new QTreeWidgetItem(); ownItem->setText(0, tr("My own identities")); + ownItem->setData(RSID_COL_VOTES, Qt::DecorationRole,0xff); // this is in order to prevent displaying a reputaiton icon next to these items. allItem = new QTreeWidgetItem(); allItem->setText(0, tr("All")); + allItem->setData(RSID_COL_VOTES, Qt::DecorationRole,0xff); contactsItem = new QTreeWidgetItem(); contactsItem->setText(0, tr("My contacts")); + contactsItem->setData(RSID_COL_VOTES, Qt::DecorationRole,0xff); ui->treeWidget_membership->clear(); @@ -318,6 +324,8 @@ IdDialog::IdDialog(QWidget *parent) : ui->idTreeWidget->setColumnWidth(RSID_COL_IDTYPE, 18 * fontWidth); ui->idTreeWidget->setColumnWidth(RSID_COL_VOTES, 7 * fontWidth); + ui->idTreeWidget->setItemDelegateForColumn(RSID_COL_VOTES,new ReputationItemDelegate(RsReputations::ReputationLevel(0xff))) ; + //QHeaderView_setSectionResizeMode(ui->idTreeWidget->header(), QHeaderView::ResizeToContents); mIdQueue = new TokenQueue(rsIdentity->getTokenService(), this); @@ -1392,7 +1400,8 @@ bool IdDialog::fillIdListItem(const RsGxsIdGroup& data, QTreeWidgetItem *&item, bool isOwnId = (data.mMeta.mSubscribeFlags & GXS_SERV::GROUP_SUBSCRIBE_ADMIN); RsIdentityDetails idd ; rsIdentity->getIdDetails(RsGxsId(data.mMeta.mGroupId),idd) ; - bool isBanned = idd.mReputation.mAssessment == RsReputations::ASSESSMENT_BAD; + + bool isBanned = idd.mReputation.mOverallReputationLevel == RsReputations::REPUTATION_LOCALLY_NEGATIVE; uint32_t item_flags = 0 ; /* do filtering */ @@ -1462,7 +1471,7 @@ bool IdDialog::fillIdListItem(const RsGxsIdGroup& data, QTreeWidgetItem *&item, item->setData(RSID_COL_KEYID, Qt::UserRole,QVariant(item_flags)) ; item->setTextAlignment(RSID_COL_VOTES, Qt::AlignRight | Qt::AlignVCenter); - item->setData(RSID_COL_VOTES,Qt::DisplayRole, QString::number(idd.mReputation.mOverallReputationScore - 1.0f,'f',3)); + item->setData(RSID_COL_VOTES,Qt::DecorationRole, idd.mReputation.mOverallReputationLevel); if(isOwnId) { @@ -1836,10 +1845,24 @@ void IdDialog::insertIdDetails(uint32_t token) RsReputations::ReputationInfo info ; rsReputations->getReputationInfo(RsGxsId(data.mMeta.mGroupId),data.mPgpId,info) ; - ui->neighborNodesOpinion_TF->setText(QString::number(info.mFriendAverage - 1.0f)); + QString frep_string ; + if(info.mFriendsPositiveVotes > 0) frep_string += QString::number(info.mFriendsPositiveVotes) + tr(" positive ") ; + if(info.mFriendsNegativeVotes > 0) frep_string += QString::number(info.mFriendsNegativeVotes) + tr(" negative ") ; - ui->overallOpinion_TF->setText(QString::number(info.mOverallReputationScore - 1.0f) +" ("+ - ((info.mAssessment == RsReputations::ASSESSMENT_OK)? tr("OK") : tr("Banned")) +")" ) ; + if(info.mFriendsPositiveVotes==0 && info.mFriendsNegativeVotes==0) + frep_string = tr("No votes from friends") ; + + ui->neighborNodesOpinion_TF->setText(frep_string) ; + + switch(info.mOverallReputationLevel) + { + case RsReputations::REPUTATION_LOCALLY_POSITIVE: ui->overallOpinion_TF->setText(tr("Positive")) ; break ; + case RsReputations::REPUTATION_LOCALLY_NEGATIVE: ui->overallOpinion_TF->setText(tr("Negative (Banned by you)")) ; break ; + case RsReputations::REPUTATION_REMOTELY_POSITIVE: ui->overallOpinion_TF->setText(tr("Positive (according to your friends)")) ; break ; + case RsReputations::REPUTATION_REMOTELY_NEGATIVE: ui->overallOpinion_TF->setText(tr("Negative (according to your friends)")) ; break ; + default: + case RsReputations::REPUTATION_NEUTRAL: ui->overallOpinion_TF->setText(tr("Neutral")) ; break ; + } switch(info.mOwnOpinion) { @@ -1849,6 +1872,24 @@ void IdDialog::insertIdDetails(uint32_t token) default: std::cerr << "Unexpected value in own opinion: " << info.mOwnOpinion << std::endl; } + + // now fill in usage cases + + RsIdentityDetails det ; + rsIdentity->getIdDetails(RsGxsId(data.mMeta.mGroupId),det) ; + + QString usage_txt ; + std::map rmap ; + for(std::map::const_iterator it(det.mUseCases.begin());it!=det.mUseCases.end();++it) + rmap.insert(std::make_pair(it->second,it->first)) ; + + for(std::map::const_iterator it(rmap.begin());it!=rmap.end();++it) + usage_txt += QString("")+ getHumanReadableDuration(now - data.mLastUsageTS) + " \t: " + QString::fromStdString(it->second) + "
" ; + + if(usage_txt.isNull()) + usage_txt = tr("[No record in current session]") ; + + ui->usageStatistics_TB->setText(usage_txt) ; } void IdDialog::modifyReputation() @@ -1900,6 +1941,22 @@ void IdDialog::modifyReputation() return; } +void IdDialog::navigate(const RsGxsId& gxs_id) +{ + std::cerr << "IdDialog::navigate to " << gxs_id.toStdString() << std::endl; + + // in order to do this, we just select the correct ID in the ID list + + QList select = ui->idTreeWidget->findItems(QString::fromStdString(gxs_id.toStdString()),Qt::MatchExactly | Qt::MatchRecursive | Qt::MatchWrap,RSID_COL_KEYID) ; + + if(select.empty()) + { + std::cerr << "Cannot find item with ID " << gxs_id << " in ID list." << std::endl; + return ; + } + ui->idTreeWidget->setCurrentItem(*select.begin(),true); + +} void IdDialog::updateDisplay(bool complete) { /* Update identity list */ diff --git a/retroshare-gui/src/gui/Identity/IdDialog.h b/retroshare-gui/src/gui/Identity/IdDialog.h index 6c849def4..0d8886279 100644 --- a/retroshare-gui/src/gui/Identity/IdDialog.h +++ b/retroshare-gui/src/gui/Identity/IdDialog.h @@ -62,6 +62,7 @@ public: void loadRequest(const TokenQueue *queue, const TokenRequest &req); + void navigate(const RsGxsId& gxs_id) ; // shows the info about this particular ID protected: virtual void updateDisplay(bool complete); diff --git a/retroshare-gui/src/gui/Identity/IdDialog.ui b/retroshare-gui/src/gui/Identity/IdDialog.ui index bb075b56f..a4e096b5f 100644 --- a/retroshare-gui/src/gui/Identity/IdDialog.ui +++ b/retroshare-gui/src/gui/Identity/IdDialog.ui @@ -7,7 +7,7 @@ 0 0 1269 - 911 + 1040 @@ -485,14 +485,14 @@ - Auto-Ban all identities from this node + Auto-Ban all identities signed by the same node - Neighbor nodes: + Friend votes: @@ -595,6 +595,18 @@ p, li { white-space: pre-wrap; } + + + + Usage statistics + + + + + + + + diff --git a/retroshare-gui/src/gui/MainWindow.cpp b/retroshare-gui/src/gui/MainWindow.cpp index c653503b1..807b43667 100644 --- a/retroshare-gui/src/gui/MainWindow.cpp +++ b/retroshare-gui/src/gui/MainWindow.cpp @@ -352,7 +352,6 @@ void MainWindow::initStackedPage() addPage(peopleDialog = new PeopleDialog(ui->stackPages), grp, ¬ify); #endif - IdDialog *idDialog = NULL; addPage(idDialog = new IdDialog(ui->stackPages), grp, ¬ify); //#ifdef RS_USE_CIRCLES @@ -878,6 +877,9 @@ void SetForegroundWindowInternal(HWND hWnd) case Friends: _instance->ui->stackPages->setCurrentPage( _instance->friendsDialog ); break; + case People: + _instance->ui->stackPages->setCurrentPage( _instance->idDialog ); + break; case ChatLobby: _instance->ui->stackPages->setCurrentPage( _instance->chatLobbyDialog ); break; @@ -974,6 +976,8 @@ void SetForegroundWindowInternal(HWND hWnd) return _instance->friendsDialog->networkDialog; case Friends: return _instance->friendsDialog; + case People: + return _instance->idDialog; case ChatLobby: return _instance->chatLobbyDialog; case Transfers: diff --git a/retroshare-gui/src/gui/MainWindow.h b/retroshare-gui/src/gui/MainWindow.h index 98c17b3eb..51fc4835b 100644 --- a/retroshare-gui/src/gui/MainWindow.h +++ b/retroshare-gui/src/gui/MainWindow.h @@ -51,6 +51,7 @@ class GxsChannelDialog ; class GxsForumsDialog ; class PostedDialog; class FriendsDialog; +class IdDialog; class ChatLobbyWidget; class ChatDialog; class NetworkDialog; @@ -103,6 +104,7 @@ public: Links = 10, /** Links page. */ #endif Posted = 11, /** Posted links */ + People = 12 /** People page. */ }; /** Create main window */ @@ -137,6 +139,7 @@ public: NewsFeed *newsFeed; FriendsDialog *friendsDialog; TransfersDialog *transfersDialog; + IdDialog *idDialog ; ChatLobbyWidget *chatLobbyDialog; MessagesDialog *messagesDialog; SharedFilesDialog *sharedfilesDialog; diff --git a/retroshare-gui/src/gui/gxs/GxsCommentTreeWidget.cpp b/retroshare-gui/src/gui/gxs/GxsCommentTreeWidget.cpp index 7631ad23f..2fa1f3b1e 100644 --- a/retroshare-gui/src/gui/gxs/GxsCommentTreeWidget.cpp +++ b/retroshare-gui/src/gui/gxs/GxsCommentTreeWidget.cpp @@ -418,7 +418,7 @@ void GxsCommentTreeWidget::service_loadThread(const uint32_t &token) std::cerr << "GxsCommentTreeWidget::service_loadThread() Got Comment: " << comment.mMeta.mMsgId; std::cerr << std::endl; - GxsIdRSTreeWidgetItem *item = new GxsIdRSTreeWidgetItem(NULL) ; + GxsIdRSTreeWidgetItem *item = new GxsIdRSTreeWidgetItem(NULL,GxsIdDetails::ICON_TYPE_ALL) ; QString text; { diff --git a/retroshare-gui/src/gui/gxs/GxsIdDetails.cpp b/retroshare-gui/src/gui/gxs/GxsIdDetails.cpp index 5c7d00dd7..0ccc4c2a2 100644 --- a/retroshare-gui/src/gui/gxs/GxsIdDetails.cpp +++ b/retroshare-gui/src/gui/gxs/GxsIdDetails.cpp @@ -48,6 +48,13 @@ #define IMAGE_DEV_PATCHER ":/images/tags/dev-patcher.png" #define IMAGE_DEV_DEVELOPER ":/images/tags/developer.png" +#define REPUTATION_LOCALLY_POSITIVE_ICON ":/icons/bullet_green_yellow_star_128.png" +#define REPUTATION_REMOTELY_POSITIVE_ICON ":/icons/bullet_green_128.png" +#define REPUTATION_NEUTRAL_ICON ":/icons/bullet_grey_128.png" +#define REPUTATION_REMOTELY_NEGATIVE_ICON ":/icons/yellow_biohazard64.png" +#define REPUTATION_LOCALLY_NEGATIVE_ICON ":/icons/red_biohazard64.png" +#define REPUTATION_VOID ":/icons/void_128.png" + #define TIMER_INTERVAL 500 #define MAX_ATTEMPTS 10 #define MAX_PROCESS_COUNT_PER_TIMER 50 @@ -60,6 +67,35 @@ const int kRecognTagType_Dev_Translator = 3; const int kRecognTagType_Dev_Patcher = 4; const int kRecognTagType_Dev_Developer = 5; + +void ReputationItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + Q_ASSERT(index.isValid()); + + QStyleOptionViewItemV4 opt = option; + initStyleOption(&opt, index); + // disable default icon + opt.icon = QIcon(); + // draw default item + QApplication::style()->drawControl(QStyle::CE_ItemViewItem, &opt, painter, 0); + + const QRect r = option.rect; + + // get pixmap + unsigned int icon_index = qvariant_cast(index.data(Qt::DecorationRole)); + + if(icon_index > mMaxLevelToDisplay) + return ; + + QIcon icon = GxsIdDetails::getReputationIcon(RsReputations::ReputationLevel(icon_index),0xff); + + QPixmap pix = icon.pixmap(r.size()); + + // draw pixmap at center of item + const QPoint p = QPoint((r.width() - pix.width())/2, (r.height() - pix.height())/2); + painter->drawPixmap(r.topLeft() + p, pix); +} + /* The global object */ GxsIdDetails *GxsIdDetails::mInstance = NULL ; @@ -904,7 +940,7 @@ bool GxsIdDetails::MakeIdDesc(const RsGxsId &id, bool doIcons, QString &str, QLi QString GxsIdDetails::getName(const RsIdentityDetails &details) { - if(rsIdentity->isBanned(details.mId)) + if(details.mReputation.mOverallReputationLevel == RsReputations::REPUTATION_LOCALLY_NEGATIVE) return tr("[Banned]") ; QString name = QString::fromUtf8(details.mNickname.c_str()).left(RSID_MAXIMUM_NICKNAME_SIZE); @@ -923,7 +959,7 @@ QString GxsIdDetails::getComment(const RsIdentityDetails &details) QString comment; QString nickname ; - bool banned = rsIdentity->isBanned(details.mId) ; + bool banned = (details.mReputation.mOverallReputationLevel == RsReputations::REPUTATION_LOCALLY_NEGATIVE); if(details.mNickname.empty()) nickname = tr("[Unknown]") ; @@ -958,17 +994,38 @@ QString nickname ; return comment; } -void GxsIdDetails::getIcons(const RsIdentityDetails &details, QList &icons,uint32_t icon_types) +QIcon GxsIdDetails::getReputationIcon(RsReputations::ReputationLevel icon_index,uint32_t min_reputation) +{ + if(icon_index >= min_reputation) + return QIcon(REPUTATION_VOID); + + switch(icon_index) + { + case RsReputations::REPUTATION_LOCALLY_NEGATIVE: return QIcon(REPUTATION_LOCALLY_NEGATIVE_ICON) ; break ; + case RsReputations::REPUTATION_LOCALLY_POSITIVE: return QIcon(REPUTATION_LOCALLY_POSITIVE_ICON) ; break ; + case RsReputations::REPUTATION_REMOTELY_POSITIVE: return QIcon(REPUTATION_REMOTELY_POSITIVE_ICON) ; break ; + case RsReputations::REPUTATION_REMOTELY_NEGATIVE: return QIcon(REPUTATION_REMOTELY_NEGATIVE_ICON) ; break ; + case RsReputations::REPUTATION_NEUTRAL: return QIcon(REPUTATION_NEUTRAL_ICON) ; break ; + default: + std::cerr << "Asked for unidentified icon index " << icon_index << std::endl; + return QIcon(); // dont draw anything + } +} + +void GxsIdDetails::getIcons(const RsIdentityDetails &details, QList &icons,uint32_t icon_types,uint32_t minimal_required_reputation) { QPixmap pix ; - if(rsIdentity->isBanned(details.mId)) + if(details.mReputation.mOverallReputationLevel == RsReputations::REPUTATION_LOCALLY_NEGATIVE) { icons.clear() ; icons.push_back(QIcon(IMAGE_BANNED)) ; return ; } - + + if(icon_types & ICON_TYPE_REPUTATION) + icons.push_back(getReputationIcon(details.mReputation.mOverallReputationLevel,minimal_required_reputation)) ; + if(icon_types & ICON_TYPE_AVATAR) { if(details.mAvatar.mSize == 0 || !pix.loadFromData(details.mAvatar.mData, details.mAvatar.mSize, "PNG")) diff --git a/retroshare-gui/src/gui/gxs/GxsIdDetails.h b/retroshare-gui/src/gui/gxs/GxsIdDetails.h index ef15c5fdd..39a6ad29d 100644 --- a/retroshare-gui/src/gui/gxs/GxsIdDetails.h +++ b/retroshare-gui/src/gui/gxs/GxsIdDetails.h @@ -29,6 +29,7 @@ #include #include #include +#include #include @@ -45,16 +46,31 @@ enum GxsIdDetailsType typedef void (*GxsIdDetailsCallbackFunction)(GxsIdDetailsType type, const RsIdentityDetails &details, QObject *object, const QVariant &data); +// This class allows to draw the item in a reputation column using an appropriate size. The max_level_to_display parameter allows to replace +// the icon by an empty icon when needed. This allows to keep the focus on the critical icons only. + +class ReputationItemDelegate: public QStyledItemDelegate +{ +public: + ReputationItemDelegate(RsReputations::ReputationLevel max_level_to_display) : mMaxLevelToDisplay(max_level_to_display) {} + + virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const; + +private: + uint32_t mMaxLevelToDisplay ; +}; + + class GxsIdDetails : public QObject { Q_OBJECT public: - static const int ICON_TYPE_AVATAR = 0x0001 ; - static const int ICON_TYPE_PGP = 0x0002 ; - static const int ICON_TYPE_RECOGN = 0x0004 ; - static const int ICON_TYPE_ALL = 0x0007 ; - static const int ICON_TYPE_REDACTED= 0x0008 ; + static const int ICON_TYPE_AVATAR = 0x0001 ; + static const int ICON_TYPE_PGP = 0x0002 ; + static const int ICON_TYPE_RECOGN = 0x0004 ; + static const int ICON_TYPE_REPUTATION = 0x0008 ; + static const int ICON_TYPE_ALL = 0x000f ; GxsIdDetails(); virtual ~GxsIdDetails(); @@ -67,7 +83,13 @@ public: static QString getName(const RsIdentityDetails &details); static QString getComment(const RsIdentityDetails &details); - static void getIcons(const RsIdentityDetails &details, QList &icons,uint32_t icon_types=ICON_TYPE_ALL); + + /*! + * \brief getIcons + * Returns the list of icons to display along with the ID name. The types of icons to show is a compound of the ICON_TYPE_* flags. + * If reputation is needed and exceeds the minimal reputation, an empty/void icon is showsn . This allow to only show reputation for IDs for which a problem exists. + */ + static void getIcons(const RsIdentityDetails &details, QList &icons, uint32_t icon_types=ICON_TYPE_ALL, uint32_t minimal_required_reputation=0xff); static QString getEmptyIdText(); static QString getLoadingText(const RsGxsId &id); @@ -76,6 +98,7 @@ public: static QString getNameForType(GxsIdDetailsType type, const RsIdentityDetails &details); static QIcon getLoadingIcon(const RsGxsId &id); + static QIcon getReputationIcon(RsReputations::ReputationLevel icon_index, uint32_t min_reputation); static void GenerateCombinedPixmap(QPixmap &pixmap, const QList &icons, int iconSize); diff --git a/retroshare-gui/src/gui/gxs/GxsIdTreeWidgetItem.cpp b/retroshare-gui/src/gui/gxs/GxsIdTreeWidgetItem.cpp index 0177f892f..a2535c38e 100644 --- a/retroshare-gui/src/gui/gxs/GxsIdTreeWidgetItem.cpp +++ b/retroshare-gui/src/gui/gxs/GxsIdTreeWidgetItem.cpp @@ -35,12 +35,6 @@ GxsIdRSTreeWidgetItem::GxsIdRSTreeWidgetItem(const RSTreeWidgetItemCompareRole * init(); } -GxsIdRSTreeWidgetItem::GxsIdRSTreeWidgetItem(const RSTreeWidgetItemCompareRole *compareRole, uint32_t icon_mask,QTreeWidgetItem *parent) - : QObject(NULL), RSTreeWidgetItem(compareRole, parent), mColumn(0), mIconTypeMask(icon_mask) -{ - init(); -} - void GxsIdRSTreeWidgetItem::init() { mIdFound = false; @@ -116,14 +110,14 @@ void GxsIdRSTreeWidgetItem::setId(const RsGxsId &id, int column, bool retryWhenF void GxsIdRSTreeWidgetItem::updateBannedState() { - if(mBannedState != rsIdentity->isBanned(mId)) + if(mBannedState != (rsIdentity->overallReputationLevel(mId) == RsReputations::REPUTATION_LOCALLY_NEGATIVE)) forceUpdate() ; } void GxsIdRSTreeWidgetItem::forceUpdate() { mIdFound = false; - mBannedState = rsIdentity->isBanned(mId) ; + mBannedState = (rsIdentity->overallReputationLevel(mId) == RsReputations::REPUTATION_LOCALLY_NEGATIVE); startProcess(); } @@ -169,7 +163,7 @@ QVariant GxsIdRSTreeWidgetItem::data(int column, int role) const if(mId.isNull()) return RSTreeWidgetItem::data(column, role); - else if(rsIdentity->isBanned(mId)) + else if(rsIdentity->overallReputationLevel(mId) == RsReputations::REPUTATION_LOCALLY_NEGATIVE) pix = QImage(BANNED_IMAGE) ; else if (mAvatar.mSize == 0 || !pix.loadFromData(mAvatar.mData, mAvatar.mSize, "PNG")) pix = GxsIdDetails::makeDefaultIcon(mId); diff --git a/retroshare-gui/src/gui/gxs/GxsIdTreeWidgetItem.h b/retroshare-gui/src/gui/gxs/GxsIdTreeWidgetItem.h index 472477841..dde64fdc6 100644 --- a/retroshare-gui/src/gui/gxs/GxsIdTreeWidgetItem.h +++ b/retroshare-gui/src/gui/gxs/GxsIdTreeWidgetItem.h @@ -40,8 +40,7 @@ class GxsIdRSTreeWidgetItem : public QObject, public RSTreeWidgetItem Q_OBJECT public: - GxsIdRSTreeWidgetItem(const RSTreeWidgetItemCompareRole *compareRole, uint32_t icon_mask=GxsIdDetails::ICON_TYPE_ALL,QTreeWidget *parent = NULL); - GxsIdRSTreeWidgetItem(const RSTreeWidgetItemCompareRole *compareRole, uint32_t icon_mask,QTreeWidgetItem *parent); + GxsIdRSTreeWidgetItem(const RSTreeWidgetItemCompareRole *compareRole, uint32_t icon_mask,QTreeWidget *parent = NULL); void setId(const RsGxsId &id, int column, bool retryWhenFailed); bool getId(RsGxsId &id); diff --git a/retroshare-gui/src/gui/gxsforums/GxsForumThreadWidget.cpp b/retroshare-gui/src/gui/gxsforums/GxsForumThreadWidget.cpp index 8dea116fe..428dda5e0 100644 --- a/retroshare-gui/src/gui/gxsforums/GxsForumThreadWidget.cpp +++ b/retroshare-gui/src/gui/gxsforums/GxsForumThreadWidget.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include "GxsForumThreadWidget.h" #include "ui_GxsForumThreadWidget.h" @@ -33,9 +34,11 @@ #include "gui/common/RSElidedItemDelegate.h" #include "gui/settings/rsharesettings.h" #include "gui/gxs/GxsIdTreeWidgetItem.h" +#include "gui/Identity/IdDialog.h" #include "gui/gxs/GxsIdDetails.h" #include "util/HandleRichText.h" #include "CreateGxsForumMsg.h" +#include "gui/MainWindow.h" #include "gui/msgs/MessageComposer.h" #include "util/DateTime.h" #include "gui/common/UIStateHelper.h" @@ -54,26 +57,34 @@ //#define DEBUG_FORUMS /* Images for context menu icons */ -#define IMAGE_MESSAGE ":/images/mail_new.png" -#define IMAGE_MESSAGEREPLY ":/images/mail_reply.png" -#define IMAGE_MESSAGEREMOVE ":/images/mail_delete.png" -#define IMAGE_DOWNLOAD ":/images/start.png" -#define IMAGE_DOWNLOADALL ":/images/startall.png" -#define IMAGE_COPYLINK ":/images/copyrslink.png" -#define IMAGE_BIOHAZARD ":/icons/yellow_biohazard64.png" +#define IMAGE_MESSAGE ":/images/mail_new.png" +#define IMAGE_MESSAGEREPLY ":/images/mail_reply.png" +#define IMAGE_MESSAGEREMOVE ":/images/mail_delete.png" +#define IMAGE_DOWNLOAD ":/images/start.png" +#define IMAGE_DOWNLOADALL ":/images/startall.png" +#define IMAGE_COPYLINK ":/images/copyrslink.png" +#define IMAGE_BIOHAZARD ":/icons/yellow_biohazard64.png" +#define IMAGE_WARNING_YELLOW ":/icons/warning_yellow_128.png" +#define IMAGE_WARNING_RED ":/icons/warning_red_128.png" +#define IMAGE_WARNING_UNKNOWN ":/icons/bullet_grey_128.png" +#define IMAGE_VOID ":/icons/void_128.png" +#define IMAGE_POSITIVE_OPINION ":/icons/png/thumbs-up.png" +#define IMAGE_NEUTRAL_OPINION ":/icons/png/thumbs-neutral.png" +#define IMAGE_NEGATIVE_OPINION ":/icons/png/thumbs-down.png" #define VIEW_LAST_POST 0 #define VIEW_THREADED 1 #define VIEW_FLAT 2 /* Thread constants */ -#define COLUMN_THREAD_COUNT 6 -#define COLUMN_THREAD_TITLE 0 -#define COLUMN_THREAD_READ 1 -#define COLUMN_THREAD_DATE 2 -#define COLUMN_THREAD_AUTHOR 3 -#define COLUMN_THREAD_SIGNED 4 -#define COLUMN_THREAD_CONTENT 5 +#define COLUMN_THREAD_TITLE 0 +#define COLUMN_THREAD_READ 1 +#define COLUMN_THREAD_DATE 2 +#define COLUMN_THREAD_DISTRIBUTION 3 +#define COLUMN_THREAD_AUTHOR 4 +#define COLUMN_THREAD_SIGNED 5 +#define COLUMN_THREAD_CONTENT 6 +#define COLUMN_THREAD_COUNT 7 #define COLUMN_THREAD_DATA 0 // column for storing the userdata like msgid and parentid @@ -88,18 +99,61 @@ #define ROLE_THREAD_COUNT 4 +class DistributionItemDelegate: public QStyledItemDelegate +{ +public: + DistributionItemDelegate() {} + + virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const + { + Q_ASSERT(index.isValid()); + + QStyleOptionViewItemV4 opt = option; + initStyleOption(&opt, index); + // disable default icon + opt.icon = QIcon(); + // draw default item + QApplication::style()->drawControl(QStyle::CE_ItemViewItem, &opt, painter, 0); + + const QRect r = option.rect; + + QIcon icon ; + + // get pixmap + unsigned int warning_level = qvariant_cast(index.data(Qt::DecorationRole)); + + switch(warning_level) + { + case 0: icon = QIcon(IMAGE_VOID); break; + case 1: icon = QIcon(IMAGE_WARNING_YELLOW); break; + case 2: icon = QIcon(IMAGE_WARNING_RED); break; + default: + case 3: icon = QIcon(IMAGE_WARNING_UNKNOWN); break; + } + + QPixmap pix = icon.pixmap(r.size()); + + // draw pixmap at center of item + const QPoint p = QPoint((r.width() - pix.width())/2, (r.height() - pix.height())/2); + painter->drawPixmap(r.topLeft() + p, pix); + } +}; + GxsForumThreadWidget::GxsForumThreadWidget(const RsGxsGroupId &forumId, QWidget *parent) : GxsMessageFrameWidget(rsGxsForums, parent), ui(new Ui::GxsForumThreadWidget) { ui->setupUi(this); - mTokenTypeGroupData = nextTokenType(); - mTokenTypeInsertThreads = nextTokenType(); - mTokenTypeMessageData = nextTokenType(); - mTokenTypeReplyMessage = nextTokenType(); - mTokenTypeReplyForumMessage = nextTokenType(); - mTokenTypeBanAuthor = nextTokenType(); + mTokenTypeGroupData = nextTokenType(); + mTokenTypeInsertThreads = nextTokenType(); + mTokenTypeMessageData = nextTokenType(); + mTokenTypeReplyMessage = nextTokenType(); + mTokenTypeReplyForumMessage = nextTokenType(); + mTokenTypeShowAuthorInPeople = nextTokenType(); + mTokenTypeNegativeAuthor = nextTokenType(); + mTokenTypeNeutralAuthor = nextTokenType(); + mTokenTypePositiveAuthor = nextTokenType(); setUpdateWhenInvisible(true); @@ -137,6 +191,8 @@ GxsForumThreadWidget::GxsForumThreadWidget(const RsGxsGroupId &forumId, QWidget mThreadCompareRole = new RSTreeWidgetItemCompareRole; mThreadCompareRole->setRole(COLUMN_THREAD_DATE, ROLE_THREAD_SORT); + ui->threadTreeWidget->setItemDelegateForColumn(COLUMN_THREAD_DISTRIBUTION,new DistributionItemDelegate()) ; + connect(ui->threadTreeWidget, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(threadListCustomPopupMenu(QPoint))); connect(ui->postText, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextMenuTextBrowser(QPoint))); @@ -219,6 +275,7 @@ GxsForumThreadWidget::GxsForumThreadWidget(const RsGxsGroupId &forumId, QWidget available posts from your subscribed friends, and make the \ forum visible to all other friends.

Afterwards you can unsubscribe from the context menu of the forum list at left.

")); ui->threadTreeWidget->enableColumnCustomize(true); + } GxsForumThreadWidget::~GxsForumThreadWidget() @@ -415,12 +472,23 @@ void GxsForumThreadWidget::threadListCustomPopupMenu(QPoint /*point*/) QAction *replyAct = new QAction(QIcon(IMAGE_MESSAGEREPLY), tr("Reply"), &contextMnu); connect(replyAct, SIGNAL(triggered()), this, SLOT(replytoforummessage())); - QAction *replyauthorAct = new QAction(QIcon(IMAGE_MESSAGEREPLY), tr("Reply with private message"), &contextMnu); + QAction *replyauthorAct = new QAction(QIcon(IMAGE_MESSAGEREPLY), tr("Reply to author with private message"), &contextMnu); connect(replyauthorAct, SIGNAL(triggered()), this, SLOT(replytomessage())); - QAction *flagasbadAct = new QAction(QIcon(IMAGE_BIOHAZARD), tr("Ban this author"), &contextMnu); - flagasbadAct->setToolTip(tr("This will block/hide messages from this person, and notify neighbor nodes.")) ; - connect(flagasbadAct, SIGNAL(triggered()), this, SLOT(flagpersonasbad())); + QAction *flagaspositiveAct = new QAction(QIcon(IMAGE_POSITIVE_OPINION), tr("Give positive opinion"), &contextMnu); + flagaspositiveAct->setToolTip(tr("This will block/hide messages from this person, and notify friend nodes.")) ; + flagaspositiveAct->setData(mTokenTypePositiveAuthor) ; + connect(flagaspositiveAct, SIGNAL(triggered()), this, SLOT(flagperson())); + + QAction *flagasneutralAct = new QAction(QIcon(IMAGE_NEUTRAL_OPINION), tr("Give neutral opinion"), &contextMnu); + flagasneutralAct->setToolTip(tr("Doing this, you trust your friends to decide to forward this message or not.")) ; + flagasneutralAct->setData(mTokenTypeNeutralAuthor) ; + connect(flagasneutralAct, SIGNAL(triggered()), this, SLOT(flagperson())); + + QAction *flagasnegativeAct = new QAction(QIcon(IMAGE_NEGATIVE_OPINION), tr("Give negative opinion"), &contextMnu); + flagasnegativeAct->setToolTip(tr("This will block/hide messages from this person, and notify friend nodes.")) ; + flagasnegativeAct->setData(mTokenTypeNegativeAuthor) ; + connect(flagasnegativeAct, SIGNAL(triggered()), this, SLOT(flagperson())); QAction *newthreadAct = new QAction(QIcon(IMAGE_MESSAGE), tr("Start New Thread"), &contextMnu); newthreadAct->setEnabled (IS_GROUP_SUBSCRIBED(mSubscribeFlags)); @@ -444,6 +512,9 @@ void GxsForumThreadWidget::threadListCustomPopupMenu(QPoint /*point*/) QAction *markMsgAsUnreadChildren = new QAction(QIcon(":/images/message-mail.png"), tr("Mark as unread") + " (" + tr ("with children") + ")", &contextMnu); connect(markMsgAsUnreadChildren, SIGNAL(triggered()), this, SLOT(markMsgAsUnreadChildren())); + QAction *showinpeopleAct = new QAction(QIcon(":/images/message-mail.png"), tr("Show author in people tab"), &contextMnu); + connect(showinpeopleAct, SIGNAL(triggered()), this, SLOT(showInPeopleTab())); + if (IS_GROUP_SUBSCRIBED(mSubscribeFlags)) { QList rows; QList rowsRead; @@ -501,8 +572,13 @@ void GxsForumThreadWidget::threadListCustomPopupMenu(QPoint /*point*/) contextMnu.addAction(collapseAll); contextMnu.addSeparator(); - contextMnu.addAction(flagasbadAct); - contextMnu.addSeparator(); + + QMenu *submenu1 = contextMnu.addMenu(tr("Author's reputation")) ; + submenu1->addAction(flagaspositiveAct); + submenu1->addAction(flagasneutralAct); + submenu1->addAction(flagasnegativeAct); + contextMnu.addAction(showinpeopleAct); + contextMnu.addAction(replyauthorAct); contextMnu.exec(QCursor::pos()); @@ -794,8 +870,8 @@ static QString getDurationString(uint32_t days) tw->ui->forumName->setText(QString::fromUtf8(group.mMeta.mGroupName.c_str())); QString anti_spam_features1 ; - if(IS_GROUP_PGP_KNOWN_AUTHED(tw->mSignFlags)) anti_spam_features1 = tr("Anonymous/unknown node IDs reputation threshold set to 0.4"); - else if(IS_GROUP_PGP_AUTHED(tw->mSignFlags)) anti_spam_features1 = tr("Anonymous IDs reputation threshold set to 0.4"); + if(IS_GROUP_PGP_KNOWN_AUTHED(tw->mSignFlags)) anti_spam_features1 = tr("Anonymous/unknown posts forwarded if reputation is positive"); + else if(IS_GROUP_PGP_AUTHED(tw->mSignFlags)) anti_spam_features1 = tr("Anonymous posts forwarded if reputation is positive"); tw->mForumDescription = QString("%1: \t%2
").arg(tr("Forum name"), QString::fromUtf8( group.mMeta.mGroupName.c_str())); tw->mForumDescription += QString("%1: \t%2
").arg(tr("Subscribers")).arg(group.mMeta.mPop); @@ -996,18 +1072,54 @@ QTreeWidgetItem *GxsForumThreadWidget::convertMsgToThreadWidget(const RsGxsForum // Early check for a message that should be hidden because its author // is flagged with a bad reputation + RsIdentityDetails iddetails ; - bool redacted = rsIdentity->isBanned(msg.mMeta.mAuthorId) ; + RsReputations::ReputationLevel reputation_level = RsReputations::REPUTATION_NEUTRAL ; + bool redacted = false ; - GxsIdRSTreeWidgetItem *item = new GxsIdRSTreeWidgetItem(mThreadCompareRole,GxsIdDetails::ICON_TYPE_ALL || (redacted?(GxsIdDetails::ICON_TYPE_REDACTED):0)); + if(rsIdentity->getIdDetails(msg.mMeta.mAuthorId,iddetails)) + { + reputation_level = iddetails.mReputation.mOverallReputationLevel ; + redacted = (reputation_level == RsReputations::REPUTATION_LOCALLY_NEGATIVE) ; + } + else + reputation_level = RsReputations::REPUTATION_UNKNOWN ; + + GxsIdRSTreeWidgetItem *item = new GxsIdRSTreeWidgetItem(mThreadCompareRole,GxsIdDetails::ICON_TYPE_AVATAR ); item->moveToThread(ui->threadTreeWidget->thread()); - if(redacted) item->setText(COLUMN_THREAD_TITLE, tr("[ ... Redacted message ... ]")); else item->setText(COLUMN_THREAD_TITLE, QString::fromUtf8(msg.mMeta.mMsgName.c_str())); + QString rep_tooltip_str ; + uint32_t rep_warning_level ; + + if(reputation_level == RsReputations::REPUTATION_UNKNOWN) + { + rep_warning_level = 3 ; + rep_tooltip_str = tr("Information for this identity is currently missing.") ; + } + else if(reputation_level == RsReputations::REPUTATION_LOCALLY_NEGATIVE) + { + rep_warning_level = 2 ; + rep_tooltip_str = tr("You have banned this ID. The message will not be\ndisplayed nor forwarded to your friends.") ; + } + else if(reputation_level < rsGxsForums->minReputationForForwardingMessages(mForumGroup.mMeta.mSignFlags,iddetails.mFlags)) + { + rep_warning_level = 1 ; + rep_tooltip_str = tr("You have not set an opinion for this person,\n and your friends do not vote positively: Spam regulation \nprevents the message to be forwarded to your friends.") ; + } + else + { + rep_warning_level = 0 ; + rep_tooltip_str = tr("Message will be forwarded to your friends.") ; + } + std::cerr << "Inserting post from ID " << msg.mMeta.mAuthorId << ", group flags=" << std::hex << mForumGroup.mMeta.mSignFlags << " Identity flags = " << iddetails.mFlags << ": warning level = " << rep_warning_level << std::dec << std::endl; + + item->setData(COLUMN_THREAD_DISTRIBUTION,Qt::ToolTipRole,rep_tooltip_str) ; + item->setData(COLUMN_THREAD_DISTRIBUTION,Qt::DecorationRole,rep_warning_level) ; //msg.mMeta.mChildTs Was not updated when received new child // so do it here. @@ -1096,7 +1208,7 @@ QTreeWidgetItem *GxsForumThreadWidget::convertMsgToThreadWidget(const RsGxsForum QTreeWidgetItem *GxsForumThreadWidget::generateMissingItem(const RsGxsMessageId &msgId) { - GxsIdRSTreeWidgetItem *item = new GxsIdRSTreeWidgetItem(mThreadCompareRole,GxsIdDetails::ICON_TYPE_ALL); + GxsIdRSTreeWidgetItem *item = new GxsIdRSTreeWidgetItem(mThreadCompareRole,GxsIdDetails::ICON_TYPE_AVATAR); item->setText(COLUMN_THREAD_TITLE, tr("[ ... Missing Message ... ]")); item->setData(COLUMN_THREAD_DATA, ROLE_THREAD_MSGID, QString::fromStdString(msgId.toStdString())); @@ -1431,7 +1543,8 @@ void GxsForumThreadWidget::insertMessageData(const RsGxsForumMsg &msg) return; } - bool redacted = rsIdentity->isBanned(msg.mMeta.mAuthorId) ; + uint32_t overall_reputation = rsIdentity->overallReputationLevel(msg.mMeta.mAuthorId) ; + bool redacted = (overall_reputation == RsReputations::REPUTATION_LOCALLY_NEGATIVE) ; mStateHelper->setActive(mTokenTypeMessageData, true); @@ -1644,6 +1757,17 @@ void GxsForumThreadWidget::setMsgReadStatus(QList &rows, bool } } +void GxsForumThreadWidget::showInPeopleTab() +{ + if (groupId().isNull() || mThreadId.isNull()) { + QMessageBox::information(this, tr("RetroShare"),tr("You cant act on the author to a non-existant Message")); + return; + } + + RsGxsGrpMsgIdPair postId = std::make_pair(groupId(), mThreadId); + requestMsgData_ShowAuthorInPeople(postId) ; +} + void GxsForumThreadWidget::markMsgAsReadUnread (bool read, bool children, bool forum) { if (groupId().isNull() || !IS_GROUP_SUBSCRIBED(mSubscribeFlags)) { @@ -1827,24 +1951,26 @@ static QString buildReplyHeader(const RsMsgMetaData &meta) return header; } -void GxsForumThreadWidget::flagpersonasbad() +void GxsForumThreadWidget::flagperson() { - // no need to use the token system for that, since we just need to find out the author's name, which is in the item. - + // no need to use the token system for that, since we just need to find out the author's name, which is in the item. + if (groupId().isNull() || mThreadId.isNull()) { QMessageBox::information(this, tr("RetroShare"),tr("You cant reply to a non-existant Message")); return; } + uint32_t token_type = qobject_cast(sender())->data().toUInt(); + // Get Message ... then complete replyMessageData(). RsGxsGrpMsgIdPair postId = std::make_pair(groupId(), mThreadId); - - RsTokReqOptions opts; + + RsTokReqOptions opts; opts.mReqType = GXS_REQUEST_TYPE_MSG_DATA; #ifdef DEBUG_FORUMS - std::cerr << "GxsForumThreadWidget::requestMsgData_BanAuthor(" << postId.first << "," << postId.second << ")"; - std::cerr << std::endl; + std::cerr << "GxsForumThreadWidget::requestMsgData_BanAuthor(" << postId.first << "," << postId.second << ")"; + std::cerr << std::endl; #endif GxsMsgReq msgIds; @@ -1852,7 +1978,7 @@ void GxsForumThreadWidget::flagpersonasbad() vect.push_back(postId.second); uint32_t token; - mTokenQueue->requestMsgInfo(token, RS_TOKREQ_ANSTYPE_DATA, opts, msgIds, mTokenTypeBanAuthor); + mTokenQueue->requestMsgInfo(token, RS_TOKREQ_ANSTYPE_DATA, opts, msgIds, token_type); } void GxsForumThreadWidget::replytomessage() @@ -1907,6 +2033,18 @@ void GxsForumThreadWidget::replyMessageData(const RsGxsForumMsg &msg) } } +void GxsForumThreadWidget::showAuthorInPeople(const RsGxsForumMsg& msg) +{ + if ((msg.mMeta.mGroupId != groupId()) || (msg.mMeta.mMsgId != mThreadId)) + { + std::cerr << "GxsForumThreadWidget::replyMessageData() ERROR Message Ids have changed!"; + std::cerr << std::endl; + return; + } + RsGxsGrpMsgIdPair postId = std::make_pair(groupId(), mThreadId); + requestMsgData_ShowAuthorInPeople(postId); +} + void GxsForumThreadWidget::replyForumMessageData(const RsGxsForumMsg &msg) { if ((msg.mMeta.mGroupId != groupId()) || (msg.mMeta.mMsgId != mThreadId)) @@ -2069,6 +2207,8 @@ void GxsForumThreadWidget::loadGroupData(const uint32_t &token) mStateHelper->setActive(mTokenTypeGroupData, true); + // Don't show the distribution column if the forum has no anti-spam + ui->threadTreeWidget->setColumnHidden(COLUMN_THREAD_DISTRIBUTION, !IS_GROUP_PGP_KNOWN_AUTHED(mForumGroup.mMeta.mSignFlags) && !(IS_GROUP_PGP_AUTHED(mForumGroup.mMeta.mSignFlags))); ui->subscribeToolButton->setHidden(IS_GROUP_SUBSCRIBED(mSubscribeFlags)) ; } else @@ -2158,6 +2298,24 @@ void GxsForumThreadWidget::requestMsgData_ReplyMessage(const RsGxsGrpMsgIdPair & mTokenQueue->requestMsgInfo(token, RS_TOKREQ_ANSTYPE_DATA, opts, msgIds, mTokenTypeReplyMessage); } +void GxsForumThreadWidget::requestMsgData_ShowAuthorInPeople(const RsGxsGrpMsgIdPair& msgId) +{ + RsTokReqOptions opts; + opts.mReqType = GXS_REQUEST_TYPE_MSG_DATA; + +#ifdef DEBUG_FORUMS + std::cerr << "GxsForumThreadWidget::requestMsgData_ReplyMessage(" << msgId.first << "," << msgId.second << ")"; + std::cerr << std::endl; +#endif + + GxsMsgReq msgIds; + std::vector &vect = msgIds[msgId.first]; + vect.push_back(msgId.second); + + uint32_t token; + mTokenQueue->requestMsgInfo(token, RS_TOKREQ_ANSTYPE_DATA, opts, msgIds, mTokenTypeShowAuthorInPeople); +} + void GxsForumThreadWidget::requestMsgData_ReplyForumMessage(const RsGxsGrpMsgIdPair &msgId) { RsTokReqOptions opts; @@ -2227,7 +2385,43 @@ void GxsForumThreadWidget::loadMsgData_ReplyForumMessage(const uint32_t &token) } } -void GxsForumThreadWidget::loadMsgData_BanAuthor(const uint32_t &token) +void GxsForumThreadWidget::loadMsgData_ShowAuthorInPeople(const uint32_t &token) +{ +#ifdef DEBUG_FORUMS + std::cerr << "GxsForumThreadWidget::loadMsgData_ReplyMessage()"; + std::cerr << std::endl; +#endif + + std::vector msgs; + if (rsGxsForums->getMsgData(token, msgs)) + { + if (msgs.size() != 1) + { + std::cerr << "GxsForumThreadWidget::loadMsgData_showAuthorInPeople() ERROR Wrong number of answers"; + std::cerr << std::endl; + return; + } + + if(msgs[0].mMeta.mAuthorId.isNull()) + { + std::cerr << "GxsForumThreadWidget::loadMsgData_showAuthorInPeople() ERROR Missing Message Data..."; + std::cerr << std::endl; + } + + /* window will destroy itself! */ + IdDialog *idDialog = dynamic_cast(MainWindow::getPage(MainWindow::People)); + + if (!idDialog) + return ; + + MainWindow::showWindow(MainWindow::People); + idDialog->navigate(RsGxsId(msgs[0].mMeta.mAuthorId)); + } + else + std::cerr << "GxsForumThreadWidget::loadMsgData_showAuthorInPeople() ERROR Missing Message Data..."; +} + +void GxsForumThreadWidget::loadMsgData_SetAuthorOpinion(const uint32_t &token,RsReputations::Opinion opinion) { #ifdef DEBUG_FORUMS std::cerr << "GxsForumThreadWidget::loadMsgData_BanAuthor()"; @@ -2245,7 +2439,8 @@ void GxsForumThreadWidget::loadMsgData_BanAuthor(const uint32_t &token) } std::cerr << " banning author id " << msgs[0].mMeta.mAuthorId << std::endl; - rsReputations->setOwnOpinion(msgs[0].mMeta.mAuthorId,RsReputations::OPINION_NEGATIVE) ; + + rsReputations->setOwnOpinion(msgs[0].mMeta.mAuthorId,opinion) ; } else { @@ -2291,8 +2486,23 @@ void GxsForumThreadWidget::loadRequest(const TokenQueue *queue, const TokenReque return; } - if (req.mUserType == mTokenTypeBanAuthor) { - loadMsgData_BanAuthor(req.mToken); + if (req.mUserType == mTokenTypeShowAuthorInPeople) { + loadMsgData_ShowAuthorInPeople(req.mToken); + return; + } + + if (req.mUserType == mTokenTypePositiveAuthor) { + loadMsgData_SetAuthorOpinion(req.mToken,RsReputations::OPINION_POSITIVE); + return; + } + + if (req.mUserType == mTokenTypeNegativeAuthor) { + loadMsgData_SetAuthorOpinion(req.mToken,RsReputations::OPINION_NEGATIVE); + return; + } + + if (req.mUserType == mTokenTypeNeutralAuthor) { + loadMsgData_SetAuthorOpinion(req.mToken,RsReputations::OPINION_NEUTRAL); return; } } diff --git a/retroshare-gui/src/gui/gxsforums/GxsForumThreadWidget.h b/retroshare-gui/src/gui/gxsforums/GxsForumThreadWidget.h index 1cf109a81..a2a6eecee 100644 --- a/retroshare-gui/src/gui/gxsforums/GxsForumThreadWidget.h +++ b/retroshare-gui/src/gui/gxsforums/GxsForumThreadWidget.h @@ -80,7 +80,8 @@ private slots: void replyMessageData(const RsGxsForumMsg &msg); void replyForumMessageData(const RsGxsForumMsg &msg); - + void showAuthorInPeople(const RsGxsForumMsg& msg); + void saveImage(); @@ -94,6 +95,7 @@ private slots: void markMsgAsUnreadChildren(); void copyMessageLink(); + void showInPeopleTab(); /* handle splitter */ void togglethreadview(); @@ -108,7 +110,7 @@ private slots: void downloadAllFiles(); void changedViewBox(); - void flagpersonasbad(); + void flagperson(); void filterColumnChanged(int column); void filterItems(const QString &text); @@ -145,13 +147,15 @@ private: static void loadAuthorIdCallback(GxsIdDetailsType type, const RsIdentityDetails &details, QObject *object, const QVariant &/*data*/); void requestMessageData(const RsGxsGrpMsgIdPair &msgId); - void loadMessageData(const uint32_t &token); void requestMsgData_ReplyMessage(const RsGxsGrpMsgIdPair &msgId); - void loadMsgData_ReplyMessage(const uint32_t &token); - + void requestMsgData_ShowAuthorInPeople(const RsGxsGrpMsgIdPair &msgId); void requestMsgData_ReplyForumMessage(const RsGxsGrpMsgIdPair &msgId); + + void loadMessageData(const uint32_t &token); + void loadMsgData_ReplyMessage(const uint32_t &token); void loadMsgData_ReplyForumMessage(const uint32_t &token); - void loadMsgData_BanAuthor(const uint32_t &token); + void loadMsgData_ShowAuthorInPeople(const uint32_t &token); + void loadMsgData_SetAuthorOpinion(const uint32_t &token, RsReputations::Opinion opinion); private: RsGxsGroupId mLastForumID; @@ -173,7 +177,10 @@ private: uint32_t mTokenTypeMessageData; uint32_t mTokenTypeReplyMessage; uint32_t mTokenTypeReplyForumMessage; - uint32_t mTokenTypeBanAuthor; + uint32_t mTokenTypeShowAuthorInPeople; + uint32_t mTokenTypeNegativeAuthor; + uint32_t mTokenTypePositiveAuthor; + uint32_t mTokenTypeNeutralAuthor; /* Color definitions (for standard see qss.default) */ QColor mTextColorRead; diff --git a/retroshare-gui/src/gui/gxsforums/GxsForumThreadWidget.ui b/retroshare-gui/src/gui/gxsforums/GxsForumThreadWidget.ui index a086d110c..ba0dd9640 100644 --- a/retroshare-gui/src/gui/gxsforums/GxsForumThreadWidget.ui +++ b/retroshare-gui/src/gui/gxsforums/GxsForumThreadWidget.ui @@ -6,7 +6,7 @@ 0 0 - 851 + 1217 721
@@ -238,6 +238,11 @@ Date
+ + + Distribution + + Author diff --git a/retroshare-gui/src/gui/icons.qrc b/retroshare-gui/src/gui/icons.qrc index 0ba95fae7..f7ad9d574 100644 --- a/retroshare-gui/src/gui/icons.qrc +++ b/retroshare-gui/src/gui/icons.qrc @@ -1,149 +1,227 @@ - - icons/svg/profile.svg - icons/svg/download.svg - icons/svg/folders.svg - icons/svg/folders1.svg - icons/svg/magnifying-glass.svg - icons/svg/upload.svg - icons/svg/paste.svg - icons/svg/share.svg - icons/settings/appearance.svg - icons/settings/channels.svg - icons/settings/chat.svg - icons/settings/directories.svg - icons/settings/filesharing.svg - icons/settings/forums.svg - icons/settings/general.svg - icons/settings/messages.svg - icons/settings/network.svg - icons/settings/notify.svg - icons/settings/people.svg - icons/settings/permissions.svg - icons/settings/plugins.svg - icons/settings/posted.svg - icons/settings/profile.svg - icons/settings/server.svg - icons/settings/sound.svg - icons/settings/webinterface.svg - icons/add_user_256.png - icons/aol.png - icons/avatar_128.png - icons/avatar_grey_128.png - icons/blank_red_128.png - icons/void_128.png - icons/blank_green_128.png - icons/blank_blue_128.png - icons/browsable_green_128.png - icons/search_red_128.png - icons/anonymous_blue_128.png - icons/bullet_blue_128.png - icons/bullet_green_128.png - icons/bullet_grey_128.png - icons/bullet_red_128.png - icons/bullet_yellow_128.png - icons/channels_128.png - icons/channels_red_128.png - icons/chat_128.png - icons/chat_red_128.png - icons/circles_128.png - icons/circles_new_128.png - icons/friends_128.png - icons/global_switch_off_128.png - icons/global_switch_on_128.png - icons/gmail.png - icons/help_128.png - icons/help_64.png - icons/information_128.png - icons/internet_128.png - icons/invite64.png - icons/knews_128.png - icons/knews_red_128.png - icons/konversation_128.png - icons/konversation128.png - icons/konversation_red_128.png - icons/ktorrent_128.png - icons/ktorrent_red_128.png - icons/logo_0_connected_128.png - icons/logo_128.png - icons/logo_1_connected_128.png - icons/logo_2_connected_128.png - icons/mail_128.png - icons/mail_old_128.png - icons/mail_red_128.png - icons/newsfeed128.png - icons/outlook.png - icons/plugins_128.png - icons/posted_128.png - icons/posted_red_128.png - icons/quit_128.png - icons/security_high_128.png - icons/security_low_128.png - icons/security_medium_128.png - icons/star_overlay_128.png - icons/switch00_128.png - icons/switch01_128.png - icons/switch10_128.png - icons/switch11_128.png - icons/system_128.png - icons/tile_checking_48.png - icons/tile_downloaded_48.png - icons/tile_downloading_48.png - icons/tile_inactive_48.png - icons/tor-logo.png - icons/tor-off.png - icons/tor-on.png - icons/tor-starting.png - icons/tor-stopping.png - icons/user-away_64.png - icons/user-away-extended_64.png - icons/user-busy_64.png - icons/user-offline_64.png - icons/user-online_64.png - icons/yahoo.png - icons/yandex.png - icons/yellow_biohazard64.png - icons/png/attach.png - icons/png/attach-image.png - icons/png/highlight.png - icons/png/invite.png - icons/png/leave.png - icons/png/search.png - icons/png/send-message.png - icons/png/settings.png - icons/png/smiley.png - icons/png/font.png - icons/png/send-message-blocked.png - icons/png/chat-bubble-notify.png - icons/png/channels.png - icons/png/chat-lobbies.png - icons/png/forums.png - icons/png/info.png - icons/png/messages.png - icons/png/network.png - icons/png/newsfeed.png - icons/png/people.png - icons/png/posted.png - icons/png/exit.png - icons/png/options.png - icons/png/filesharing.png - icons/png/channels-notify.png - icons/png/chat-lobbies-notify.png - icons/png/forums-notify.png - icons/png/messages-notify.png - icons/png/network-notify.png - icons/png/newsfeed-notify.png - icons/png/people-notify.png - icons/png/posted-notify.png - icons/png/filesharing-notify.png - icons/png/thumbs-up.png - icons/png/thumbs-down.png - icons/png/thumbs-neutral.png - icons/png/add.png - icons/png/netgraph.png - icons/png/network-puplic.png - icons/png/circles.png - icons/png/person.png - icons/png/keyring.png - + + icons/add_user_256.png + icons/anonymous_blue_128.png + icons/anonymous_green_128.png + icons/aol.png + icons/avatar_128.png + icons/avatar_grey_128.png + icons/blank_blue_128.png + icons/blank_green_128.png + icons/blank_red_128.png + icons/browsable_blue_128.png + icons/browsable_green_128.png + icons/bullet_blue_128.png + icons/bullet_green_128.png + icons/bullet_green_yellow_star_128.png + icons/bullet_grey_128.png + icons/bullet_red_128.png + icons/bullet_yellow_128.png + icons/channels_128.png + icons/channels_red_128.png + icons/chat_128.png + icons/chat_red_128.png + icons/circles_128.png + icons/circles_new_128.png + icons/friends_128.png + icons/global_switch_off_128.png + icons/global_switch_on_128.png + icons/gmail.png + icons/help_128.png + icons/help_64.png + icons/information_128.png + icons/internet_128.png + icons/invite64.png + icons/knews_128.png + icons/knews_red_128.png + icons/konversation_128.png + icons/konversation128.png + icons/konversation_red_128.png + icons/ktorrent_128.png + icons/ktorrent_red_128.png + icons/logo_0_connected_128.png + icons/logo_128.png + icons/logo_1_connected_128.png + icons/logo_2_connected_128.png + icons/mail_128.png + icons/mail_old_128.png + icons/mail_red_128.png + icons/newsfeed128.png + icons/outlook.png + icons/plugins_128.png + icons/png/add.png + icons/png/attach-image.png + icons/png/attach.png + icons/png/channels-notify.png + icons/png/channels.png + icons/png/chat-bubble-notify.png + icons/png/chat-bubble.png + icons/png/chat-lobbies-notify.png + icons/png/chat-lobbies.png + icons/png/circles.png + icons/png/empty-circle.png + icons/png/exit.png + icons/png/feedreader-notify.png + icons/png/feedreader.png + icons/png/filesharing-notify.png + icons/png/filesharing.png + icons/png/font.png + icons/png/forums-notify.png + icons/png/forums.png + icons/png/fullscreen_arrows.png + icons/png/highlight.png + icons/png/info.png + icons/png/invite.png + icons/png/keyring.png + icons/png/leave.png + icons/png/messages-notify.png + icons/png/messages.png + icons/png/microphone_mute.png + icons/png/microphone.png + icons/png/netgraph.png + icons/png/network-notify.png + icons/png/network.png + icons/png/network-puplic.png + icons/png/newsfeed-notify.png + icons/png/newsfeed.png + icons/png/options.png + icons/png/people-notify.png + icons/png/people.png + icons/png/person.png + icons/png/phone_hangup.png + icons/png/phone.png + icons/png/posted-notify.png + icons/png/posted.png + icons/png/search.png + icons/png/send-message-blocked.png + icons/png/send-message.png + icons/png/settings.png + icons/png/smiley.png + icons/png/speaker_mute.png + icons/png/speaker.png + icons/png/thumbs-down.png + icons/png/thumbs-neutral.png + icons/png/thumbs-up.png + icons/png/video.png + icons/posted_128.png + icons/posted_red_128.png + icons/quit_128.png + icons/red_biohazard64.png + icons/search_red_128.png + icons/security_high_128.png + icons/security_low_128.png + icons/security_medium_128.png + icons/settings/appearance.svg + icons/settings/channels.svg + icons/settings/chat.svg + icons/settings/directories.svg + icons/settings/filesharing.svg + icons/settings/forums.svg + icons/settings/general.svg + icons/settings/messages.svg + icons/settings/network.svg + icons/settings/notify.svg + icons/settings/people.svg + icons/settings/permissions.svg + icons/settings/plugins.svg + icons/settings/posted.svg + icons/settings/profile.svg + icons/settings/server.svg + icons/settings/sound.svg + icons/settings/webinterface.svg + icons/star_overlay_128.png + icons/svg/add.svg + icons/svg/attach-image.svg + icons/svg/attach.svg + icons/svg/channels-notify.svg + icons/svg/channels.svg + icons/svg/chat-bubble-notify.svg + icons/svg/chat-bubble.svg + icons/svg/chat-lobbies-notify.svg + icons/svg/chat-lobbies.svg + icons/svg/circles.svg + icons/svg/download.svg + icons/svg/empty-circle.svg + icons/svg/exit-red.svg + icons/svg/exit.svg + icons/svg/feedreader-notify.svg + icons/svg/feedreader.svg + icons/svg/filesharing-notify.svg + icons/svg/filesharing.svg + icons/svg/folders1.svg + icons/svg/folders.svg + icons/svg/font.svg + icons/svg/forums-notify.svg + icons/svg/forums.svg + icons/svg/fullscreen_arrows.svg + icons/svg/highlight.svg + icons/svg/info.svg + icons/svg/invite.svg + icons/svg/keyring.svg + icons/svg/leave.svg + icons/svg/magnifying-glass.svg + icons/svg/messages-notify.svg + icons/svg/messages.svg + icons/svg/microphone_mute.svg + icons/svg/microphone.svg + icons/svg/netgraph.svg + icons/svg/network-notify.svg + icons/svg/network-puplic.svg + icons/svg/network.svg + icons/svg/newsfeed-notify.svg + icons/svg/newsfeed.svg + icons/svg/options.svg + icons/svg/paste.svg + icons/svg/people-notify.svg + icons/svg/people.svg + icons/svg/person.svg + icons/svg/phone_hangup.svg + icons/svg/phone.svg + icons/svg/posted-notify.svg + icons/svg/posted.svg + icons/svg/profile.svg + icons/svg/retroshare-info.svg + icons/svg/retroshare-splash.svg + icons/svg/retroshare-symbol_black_bg.svg + icons/svg/retroshare-symbol_gradients.svg + icons/svg/retroshare-symbol-minimal.svg + icons/svg/search.svg + icons/svg/send-message-blocked.svg + icons/svg/send-message.svg + icons/svg/settings.svg + icons/svg/share.svg + icons/svg/smiley.svg + icons/svg/speaker_mute.svg + icons/svg/speaker.svg + icons/svg/thumbs-down.svg + icons/svg/thumbs-neutral.svg + icons/svg/thumbs-up.svg + icons/svg/upload.svg + icons/svg/video.svg + icons/switch00_128.png + icons/switch01_128.png + icons/switch10_128.png + icons/switch11_128.png + icons/system_128.png + icons/tile_checking_48.png + icons/tile_downloaded_48.png + icons/tile_downloading_48.png + icons/tile_inactive_48.png + icons/tor-logo.png + icons/tor-off.png + icons/tor-on.png + icons/tor-starting.png + icons/tor-stopping.png + icons/user-away_64.png + icons/user-away-extended_64.png + icons/user-busy_64.png + icons/user-offline_64.png + icons/user-online_64.png + icons/void_128.png + icons/warning_red_128.png + icons/warning_yellow_128.png + icons/yahoo.png + icons/yandex.png + icons/yellow_biohazard64.png + diff --git a/retroshare-gui/src/gui/icons/bullet_green_yellow_star_128.png b/retroshare-gui/src/gui/icons/bullet_green_yellow_star_128.png new file mode 100644 index 000000000..2b49ec384 Binary files /dev/null and b/retroshare-gui/src/gui/icons/bullet_green_yellow_star_128.png differ diff --git a/retroshare-gui/src/gui/icons/red_biohazard64.png b/retroshare-gui/src/gui/icons/red_biohazard64.png new file mode 100644 index 000000000..eacbf9f8d Binary files /dev/null and b/retroshare-gui/src/gui/icons/red_biohazard64.png differ diff --git a/retroshare-gui/src/gui/icons/warning_red_128.png b/retroshare-gui/src/gui/icons/warning_red_128.png new file mode 100644 index 000000000..3399fee0c Binary files /dev/null and b/retroshare-gui/src/gui/icons/warning_red_128.png differ diff --git a/retroshare-gui/src/gui/icons/warning_yellow_128.png b/retroshare-gui/src/gui/icons/warning_yellow_128.png new file mode 100644 index 000000000..0d809ce65 Binary files /dev/null and b/retroshare-gui/src/gui/icons/warning_yellow_128.png differ diff --git a/retroshare-gui/src/gui/icons/yellow_biohazard64.png b/retroshare-gui/src/gui/icons/yellow_biohazard64.png index edb6fb3f2..09accbf83 100644 Binary files a/retroshare-gui/src/gui/icons/yellow_biohazard64.png and b/retroshare-gui/src/gui/icons/yellow_biohazard64.png differ diff --git a/retroshare-gui/src/gui/settings/PeoplePage.cpp b/retroshare-gui/src/gui/settings/PeoplePage.cpp index 6ce71233f..9a52cdb35 100644 --- a/retroshare-gui/src/gui/settings/PeoplePage.cpp +++ b/retroshare-gui/src/gui/settings/PeoplePage.cpp @@ -42,7 +42,8 @@ bool PeoplePage::save(QString &/*errmsg*/) else rsReputations->setNodeAutoPositiveOpinionForContacts(false) ; - rsReputations->setNodeAutoBanIdentitiesLimit(ui.autoBanIdentitiesLimit_SB->value()); + rsReputations->setThresholdForRemotelyPositiveReputation(ui.thresholdForPositive_SB->value()); + rsReputations->setThresholdForRemotelyNegativeReputation(ui.thresholdForNegative_SB->value()); return true; } @@ -51,8 +52,10 @@ bool PeoplePage::save(QString &/*errmsg*/) void PeoplePage::load() { bool auto_positive_contacts = rsReputations->nodeAutoPositiveOpinionForContacts() ; - float node_auto_ban_identities_limit = rsReputations->nodeAutoBanIdentitiesLimit(); + uint32_t threshold_for_positive = rsReputations->thresholdForRemotelyPositiveReputation(); + uint32_t threshold_for_negative = rsReputations->thresholdForRemotelyNegativeReputation(); ui.autoPositiveOpinion_CB->setChecked(auto_positive_contacts); - ui.autoBanIdentitiesLimit_SB->setValue(node_auto_ban_identities_limit); + ui.thresholdForPositive_SB->setValue(threshold_for_positive); + ui.thresholdForNegative_SB->setValue(threshold_for_negative); } diff --git a/retroshare-gui/src/gui/settings/PeoplePage.ui b/retroshare-gui/src/gui/settings/PeoplePage.ui index 08a5c4967..133634c0c 100644 --- a/retroshare-gui/src/gui/settings/PeoplePage.ui +++ b/retroshare-gui/src/gui/settings/PeoplePage.ui @@ -10,13 +10,13 @@ 441 - + Identities handling - + @@ -31,30 +31,44 @@ - - - + + + - Friend average opinion below which identities are banned: + Difference in votes to make friends globally negative: - - + + <html><head/><body><p>The default value of -0.6 needs 3 to 4 friends to set a negative opinion in order for that identity to be banned at your own node. This is a pretty conservative value. If you want to more easily get rid of banned identities, set the value to e.g. -0.2</p></body></html> - -1.000000000000000 + 1 - -0.010000000000000 + 100 - - 0.010000000000000 + + + + + + <html><head/><body><p>The default value of -0.6 needs 3 to 4 friends to set a negative opinion in order for that identity to be banned at your own node. This is a pretty conservative value. If you want to more easily get rid of banned identities, set the value to e.g. -0.2</p></body></html> - - -0.600000000000000 + + 1 + + + 100 + + + + + + + Difference in votes to make friends globally positive: