2013-03-20 18:26:24 -04:00
/*
* libretroshare / src / gxs : rsgxsutil . cc
*
* RetroShare C + + Interface . Generic routines that are useful in GXS
*
* Copyright 2013 - 2013 by Christopher Evi - Parker
*
* This library is free software ; you can redistribute it and / or
* modify it under the terms of the GNU Library General Public
* License Version 2 as published by the Free Software Foundation .
*
* This library is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* Library General Public License for more details .
*
* You should have received a copy of the GNU Library General Public
* License along with this library ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307
* USA .
*
* Please report all bugs and problems to " retroshare@lunamutt.com " .
*
*/
2013-10-23 05:23:32 -04:00
# include <time.h>
2013-03-20 18:26:24 -04:00
# include "rsgxsutil.h"
2013-03-23 10:07:52 -04:00
# include "retroshare/rsgxsflags.h"
2015-12-15 18:01:03 -05:00
# include "retroshare/rspeers.h"
2013-06-04 17:00:43 -04:00
# include "pqi/pqihash.h"
2015-12-15 12:31:03 -05:00
# include "gxs/rsgixs.h"
2013-03-20 18:26:24 -04:00
2015-12-15 18:01:03 -05:00
static const uint32_t MAX_GXS_IDS_REQUESTS_NET = 10 ; // max number of requests from cache/net (avoids killing the system!)
2016-12-13 15:12:34 -05:00
//#define DEBUG_GXSUTIL 1
2016-12-07 16:41:05 -05:00
# define GXSUTIL_DEBUG() std::cerr << time(NULL) << " : GXS_UTIL : " << __FUNCTION__ << " : "
2013-03-20 18:26:24 -04:00
2016-12-07 16:00:50 -05:00
RsGxsMessageCleanUp : : RsGxsMessageCleanUp ( RsGeneralDataService * const dataService , RsGenExchange * genex , uint32_t chunkSize )
: mDs ( dataService ) , mGenExchangeClient ( genex ) , CHUNK_SIZE ( chunkSize )
2013-03-20 18:26:24 -04:00
{
std : : map < RsGxsGroupId , RsGxsGrpMetaData * > grpMeta ;
mDs - > retrieveGxsGrpMetaData ( grpMeta ) ;
std : : map < RsGxsGroupId , RsGxsGrpMetaData * > : : iterator cit = grpMeta . begin ( ) ;
2014-10-24 18:07:26 -04:00
for ( ; cit ! = grpMeta . end ( ) ; + + cit )
2013-03-23 10:07:52 -04:00
mGrpMeta . push_back ( cit - > second ) ;
2013-03-20 18:26:24 -04:00
}
bool RsGxsMessageCleanUp : : clean ( )
{
2016-06-02 08:53:15 -04:00
uint32_t i = 1 ;
2013-03-20 18:26:24 -04:00
time_t now = time ( NULL ) ;
2016-12-07 16:41:05 -05:00
# ifdef DEBUG_GXSUTIL
GXSUTIL_DEBUG ( ) < < " Cleaning up groups in service " < < std : : hex < < mGenExchangeClient - > serviceType ( ) < < std : : dec < < std : : endl ;
# endif
2013-03-23 10:07:52 -04:00
while ( ! mGrpMeta . empty ( ) )
2013-03-20 18:26:24 -04:00
{
2013-03-23 10:07:52 -04:00
RsGxsGrpMetaData * grpMeta = mGrpMeta . back ( ) ;
const RsGxsGroupId & grpId = grpMeta - > mGroupId ;
mGrpMeta . pop_back ( ) ;
2013-03-20 18:26:24 -04:00
GxsMsgReq req ;
GxsMsgMetaResult result ;
2013-03-21 19:17:24 -04:00
req [ grpId ] = std : : vector < RsGxsMessageId > ( ) ;
2013-03-20 18:26:24 -04:00
mDs - > retrieveGxsMsgMetaData ( req , result ) ;
GxsMsgMetaResult : : iterator mit = result . begin ( ) ;
2016-12-07 16:41:05 -05:00
# ifdef DEBUG_GXSUTIL
GXSUTIL_DEBUG ( ) < < " Cleaning up group message for group ID " < < grpId < < std : : endl ;
# endif
2013-03-20 18:26:24 -04:00
req . clear ( ) ;
2016-12-07 16:00:50 -05:00
uint32_t store_period = mGenExchangeClient - > getStoragePeriod ( grpId ) ;
2014-10-24 18:07:26 -04:00
for ( ; mit ! = result . end ( ) ; + + mit )
2013-03-20 18:26:24 -04:00
{
std : : vector < RsGxsMsgMetaData * > & metaV = mit - > second ;
std : : vector < RsGxsMsgMetaData * > : : iterator vit = metaV . begin ( ) ;
for ( ; vit ! = metaV . end ( ) ; )
{
RsGxsMsgMetaData * meta = * vit ;
2013-03-23 10:07:52 -04:00
// check if expired
2016-12-08 03:17:56 -05:00
bool remove = store_period > 0 & & ( meta - > mPublishTs + store_period ) < now ;
2013-03-23 10:07:52 -04:00
// check client does not want the message kept regardless of age
remove & = ! ( meta - > mMsgStatus & GXS_SERV : : GXS_MSG_STATUS_KEEP ) ;
// if not subscribed remove messages (can optimise this really)
2016-06-17 20:46:58 -04:00
remove = remove | | ( grpMeta - > mSubscribeFlags & GXS_SERV : : GROUP_SUBSCRIBE_NOT_SUBSCRIBED ) ;
remove = remove | | ! ( grpMeta - > mSubscribeFlags & GXS_SERV : : GROUP_SUBSCRIBE_SUBSCRIBED ) ;
2013-03-23 10:07:52 -04:00
if ( remove )
2013-03-20 18:26:24 -04:00
{
req [ grpId ] . push_back ( meta - > mMsgId ) ;
2016-06-17 20:46:58 -04:00
2016-12-07 16:41:05 -05:00
GXSUTIL_DEBUG ( ) < < " Scheduling msg id " < < meta - > mMsgId < < " in grp " < < grpId < < " for removal. " < < std : : endl ;
2013-03-20 18:26:24 -04:00
}
delete meta ;
vit = metaV . erase ( vit ) ;
}
}
mDs - > removeMsgs ( req ) ;
2013-03-23 10:07:52 -04:00
delete grpMeta ;
2013-03-20 18:26:24 -04:00
i + + ;
if ( i > CHUNK_SIZE ) break ;
}
2013-03-23 10:07:52 -04:00
return mGrpMeta . empty ( ) ;
2013-03-20 18:26:24 -04:00
}
2013-06-04 17:00:43 -04:00
2016-12-07 16:41:05 -05:00
RsGxsIntegrityCheck : : RsGxsIntegrityCheck ( RsGeneralDataService * const dataService , RsGenExchange * genex , RsGixs * gixs ) :
mDs ( dataService ) , mGenExchangeClient ( genex ) , mDone ( false ) , mIntegrityMutex ( " integrity " ) , mGixs ( gixs )
2013-06-04 17:00:43 -04:00
{ }
void RsGxsIntegrityCheck : : run ( )
{
check ( ) ;
}
bool RsGxsIntegrityCheck : : check ( )
{
2015-12-15 18:01:03 -05:00
// first take out all the groups
std : : map < RsGxsGroupId , RsNxsGrp * > grp ;
mDs - > retrieveNxsGrps ( grp , true , true ) ;
std : : vector < RsGxsGroupId > grpsToDel ;
GxsMsgReq msgIds ;
GxsMsgReq grps ;
2016-12-20 18:34:07 -05:00
std : : map < RsGxsId , RsGxsGroupId > used_gxs_ids ;
2015-12-15 18:01:03 -05:00
std : : set < RsGxsGroupId > subscribed_groups ;
// compute hash and compare to stored value, if it fails then simply add it
// to list
std : : map < RsGxsGroupId , RsNxsGrp * > : : iterator git = grp . begin ( ) ;
for ( ; git ! = grp . end ( ) ; + + git )
{
RsNxsGrp * grp = git - > second ;
RsFileHash currHash ;
pqihash pHash ;
pHash . addData ( grp - > grp . bin_data , grp - > grp . bin_len ) ;
pHash . Complete ( currHash ) ;
if ( currHash = = grp - > metaData - > mHash )
{
// get all message ids of group
if ( mDs - > retrieveMsgIds ( grp - > grpId , msgIds [ grp - > grpId ] ) = = 1 )
{
// store the group for retrieveNxsMsgs
grps [ grp - > grpId ] ;
if ( grp - > metaData - > mSubscribeFlags & GXS_SERV : : GROUP_SUBSCRIBE_SUBSCRIBED )
{
subscribed_groups . insert ( git - > first ) ;
if ( ! grp - > metaData - > mAuthorId . isNull ( ) )
{
2016-12-07 16:41:05 -05:00
# ifdef DEBUG_GXSUTIL
GXSUTIL_DEBUG ( ) < < " TimeStamping group authors' key ID " < < grp - > metaData - > mAuthorId < < " in group ID " < < grp - > grpId < < std : : endl ;
2015-12-15 18:01:03 -05:00
# endif
2016-12-23 11:52:02 -05:00
if ( rsIdentity ! = NULL & & rsIdentity - > overallReputationLevel ( grp - > metaData - > mAuthorId ) > RsReputations : : REPUTATION_LOCALLY_NEGATIVE )
2016-12-20 18:34:07 -05:00
used_gxs_ids . insert ( std : : make_pair ( grp - > metaData - > mAuthorId , grp - > grpId ) ) ;
2015-12-15 18:01:03 -05:00
}
}
}
else
{
msgIds . erase ( msgIds . find ( grp - > grpId ) ) ;
// grpsToDel.push_back(grp->grpId);
}
}
else
{
grpsToDel . push_back ( grp - > grpId ) ;
}
2016-12-07 16:41:05 -05:00
# ifdef TODO
if ( ! ( grp - > metaData - > mSubscribeFlags & GXS_SERV : : GROUP_SUBSCRIBE_SUBSCRIBED ) )
{
RsGroupNetworkStats stats ;
mGenExchangeClient - > getGroupNetworkStats ( grp - > grpId , stats ) ;
if ( stats . mSuppliers = = 0 & & stats . mMaxVisibleCount = = 0 )
{
GXSUTIL_DEBUG ( ) < < " Scheduling group \" " < < grp - > metaData - > mGroupName < < " \" ID= " < < grp - > grpId < < " for deletion because it has no suppliers not any visible data at friends. " < < std : : endl ;
# warning Should we do that here? What happens for groups that are normally empty such as identities?
grpsToDel . push_back ( grp - > grpId ) ;
}
}
# endif
2015-12-15 18:01:03 -05:00
delete grp ;
}
mDs - > removeGroups ( grpsToDel ) ;
// now messages
GxsMsgReq msgsToDel ;
GxsMsgResult msgs ;
mDs - > retrieveNxsMsgs ( grps , msgs , false , true ) ;
// check msg ids and messages
GxsMsgReq : : iterator msgIdsIt ;
for ( msgIdsIt = msgIds . begin ( ) ; msgIdsIt ! = msgIds . end ( ) ; + + msgIdsIt )
{
const RsGxsGroupId & grpId = msgIdsIt - > first ;
std : : vector < RsGxsMessageId > & msgIdV = msgIdsIt - > second ;
std : : vector < RsGxsMessageId > : : iterator msgIdIt ;
for ( msgIdIt = msgIdV . begin ( ) ; msgIdIt ! = msgIdV . end ( ) ; + + msgIdIt )
{
const RsGxsMessageId & msgId = * msgIdIt ;
std : : vector < RsNxsMsg * > & nxsMsgV = msgs [ grpId ] ;
std : : vector < RsNxsMsg * > : : iterator nxsMsgIt ;
for ( nxsMsgIt = nxsMsgV . begin ( ) ; nxsMsgIt ! = nxsMsgV . end ( ) ; + + nxsMsgIt )
{
RsNxsMsg * nxsMsg = * nxsMsgIt ;
if ( nxsMsg & & msgId = = nxsMsg - > msgId )
{
break ;
}
}
if ( nxsMsgIt = = nxsMsgV . end ( ) )
{
msgsToDel [ grpId ] . push_back ( msgId ) ;
}
}
}
GxsMsgResult : : iterator mit = msgs . begin ( ) ;
for ( ; mit ! = msgs . end ( ) ; + + mit )
{
std : : vector < RsNxsMsg * > & msgV = mit - > second ;
std : : vector < RsNxsMsg * > : : iterator vit = msgV . begin ( ) ;
for ( ; vit ! = msgV . end ( ) ; + + vit )
{
RsNxsMsg * msg = * vit ;
RsFileHash currHash ;
pqihash pHash ;
pHash . addData ( msg - > msg . bin_data , msg - > msg . bin_len ) ;
pHash . Complete ( currHash ) ;
if ( msg - > metaData = = NULL | | currHash ! = msg - > metaData - > mHash )
{
std : : cerr < < " (EE) deleting message data with wrong hash or null meta data. meta= " < < ( void * ) msg - > metaData < < std : : endl ;
msgsToDel [ msg - > grpId ] . push_back ( msg - > msgId ) ;
}
else if ( ! msg - > metaData - > mAuthorId . isNull ( ) & & subscribed_groups . find ( msg - > metaData - > mGroupId ) ! = subscribed_groups . end ( ) )
{
2016-12-07 16:41:05 -05:00
# ifdef DEBUG_GXSUTIL
GXSUTIL_DEBUG ( ) < < " TimeStamping message authors' key ID " < < msg - > metaData - > mAuthorId < < " in message " < < msg - > msgId < < " , group ID " < < msg - > grpId < < std : : endl ;
2015-12-15 18:01:03 -05:00
# endif
2016-12-23 11:52:02 -05:00
if ( rsIdentity ! = NULL & & rsIdentity - > overallReputationLevel ( msg - > metaData - > mAuthorId ) > RsReputations : : REPUTATION_LOCALLY_NEGATIVE )
2016-12-20 18:34:07 -05:00
used_gxs_ids . insert ( std : : make_pair ( msg - > metaData - > mAuthorId , msg - > metaData - > mGroupId ) ) ;
2015-12-15 18:01:03 -05:00
}
delete msg ;
}
}
mDs - > removeMsgs ( msgsToDel ) ;
RsStackMutex stack ( mIntegrityMutex ) ;
mDone = true ;
std : : vector < RsGxsGroupId > : : iterator grpIt ;
for ( grpIt = grpsToDel . begin ( ) ; grpIt ! = grpsToDel . end ( ) ; + + grpIt )
{
mDeletedGrps . push_back ( * grpIt ) ;
}
mDeletedMsgs = msgsToDel ;
2016-12-07 16:41:05 -05:00
# ifdef DEBUG_GXSUTIL
GXSUTIL_DEBUG ( ) < < " At end of pass, this is the list used GXS ids: " < < std : : endl ;
GXSUTIL_DEBUG ( ) < < " requesting them to GXS identity service to enforce loading. " < < std : : endl ;
2015-12-15 18:01:03 -05:00
# endif
std : : list < RsPeerId > connected_friends ;
rsPeers - > getOnlineList ( connected_friends ) ;
2016-12-20 18:34:07 -05:00
std : : vector < std : : pair < RsGxsId , RsGxsGroupId > > gxs_ids ;
2015-12-15 18:01:03 -05:00
2016-12-20 18:34:07 -05:00
for ( std : : map < RsGxsId , RsGxsGroupId > : : const_iterator it ( used_gxs_ids . begin ( ) ) ; it ! = used_gxs_ids . end ( ) ; + + it )
2015-12-15 18:01:03 -05:00
{
gxs_ids . push_back ( * it ) ;
2016-12-07 16:41:05 -05:00
# ifdef DEBUG_GXSUTIL
GXSUTIL_DEBUG ( ) < < " " < < * it < < std : : endl ;
2015-12-15 18:01:03 -05:00
# endif
}
2016-06-02 08:53:15 -04:00
uint32_t nb_requested_not_in_cache = 0 ;
2015-12-15 18:01:03 -05:00
2016-12-07 16:41:05 -05:00
# ifdef DEBUG_GXSUTIL
GXSUTIL_DEBUG ( ) < < " issuing random get on friends for non existing IDs " < < std : : endl ;
2015-12-15 18:01:03 -05:00
# endif
2015-12-15 18:13:40 -05:00
// now request a cache update for them, which triggers downloading from friends, if missing.
2016-06-02 20:23:22 -04:00
for ( ; nb_requested_not_in_cache < MAX_GXS_IDS_REQUESTS_NET & & ! gxs_ids . empty ( ) ; )
2015-12-15 18:01:03 -05:00
{
uint32_t n = RSRandom : : random_u32 ( ) % gxs_ids . size ( ) ;
2016-12-07 16:41:05 -05:00
# ifdef DEBUG_GXSUTIL
GXSUTIL_DEBUG ( ) < < " requesting ID " < < gxs_ids [ n ] ;
2015-12-15 18:01:03 -05:00
# endif
2016-12-20 18:34:07 -05:00
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)
2015-12-15 18:01:03 -05:00
{
2016-12-22 05:21:49 -05:00
mGixs - > requestKey ( gxs_ids [ n ] . first , connected_friends , " Author in service \" " + rsServiceControl - > getServiceName ( mGenExchangeClient - > serviceFullType ( ) ) + " \" (group ID " + gxs_ids [ n ] . second . toStdString ( ) + " ) " ) ;
2015-12-15 18:01:03 -05:00
+ + nb_requested_not_in_cache ;
2016-12-07 16:41:05 -05:00
# ifdef DEBUG_GXSUTIL
GXSUTIL_DEBUG ( ) < < " ... from cache/net " < < std : : endl ;
2015-12-15 18:01:03 -05:00
# endif
}
2015-12-15 18:13:40 -05:00
else
{
2016-12-07 16:41:05 -05:00
# ifdef DEBUG_GXSUTIL
GXSUTIL_DEBUG ( ) < < " ... already in cache " < < std : : endl ;
2015-12-15 18:13:40 -05:00
# endif
}
2016-12-22 05:21:49 -05:00
mGixs - > timeStampKey ( gxs_ids [ n ] . first , " Author in service \" " + rsServiceControl - > getServiceName ( mGenExchangeClient - > serviceFullType ( ) ) + " \" (group ID " + gxs_ids [ n ] . second . toStdString ( ) + " ) " ) ;
2015-12-15 18:01:03 -05:00
gxs_ids [ n ] = gxs_ids [ gxs_ids . size ( ) - 1 ] ;
gxs_ids . pop_back ( ) ;
}
2016-12-07 16:41:05 -05:00
# ifdef DEBUG_GXSUTIL
GXSUTIL_DEBUG ( ) < < " total actual cache requests: " < < nb_requested_not_in_cache < < std : : endl ;
2015-12-15 18:01:03 -05:00
# endif
2015-12-15 18:13:40 -05:00
return true ;
2013-06-04 17:00:43 -04:00
}
bool RsGxsIntegrityCheck : : isDone ( )
{
RsStackMutex stack ( mIntegrityMutex ) ;
return mDone ;
}
2014-09-28 08:11:29 -04:00
void RsGxsIntegrityCheck : : getDeletedIds ( std : : list < RsGxsGroupId > & grpIds , std : : map < RsGxsGroupId , std : : vector < RsGxsMessageId > > & msgIds )
{
RsStackMutex stack ( mIntegrityMutex ) ;
grpIds = mDeletedGrps ;
msgIds = mDeletedMsgs ;
}