/*
 * libretroshare/src/services p3idservice.cc
 *
 * Id interface for RetroShare.
 *
 * Copyright 2012-2012 by Robert Fernie.
 *
 * 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.1 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".
 *
 */

#include <unistd.h>

#include "services/p3idservice.h"
#include "pgp/pgpauxutils.h"
#include "rsitems/rsgxsiditems.h"
#include "rsitems/rsconfigitems.h"
#include "retroshare/rsgxsflags.h"
#include "util/rsrandom.h"
#include "util/rsstring.h"
#include "util/radix64.h"
#include "util/rsdir.h"
#include "crypto/hashstream.h"
#include "gxs/gxssecurity.h"
#include "retroshare/rspeers.h"


//#include "pqi/authgpg.h"

//#include <retroshare/rspeers.h>

#include <sstream>
#include <stdio.h>

/****
 * #define DEBUG_IDS	1
 * #define DEBUG_RECOGN	1
 * #define DEBUG_OPINION 1
 * #define GXSID_GEN_DUMMY_DATA	1
 ****/

#define ID_REQUEST_LIST		    0x0001
#define ID_REQUEST_IDENTITY	    0x0002
#define ID_REQUEST_REPUTATION	0x0003
#define ID_REQUEST_OPINION	    0x0004

#define GXSID_MAX_CACHE_SIZE 5000

// unused keys are deleted according to some heuristic that should favor known keys, signed keys etc. 

static const time_t MAX_KEEP_KEYS_BANNED_DEFAULT =     2 * 86400 ; // get rid of banned ids after 1 days. That gives a chance to un-ban someone before he gets definitely kicked out

static const time_t MAX_KEEP_KEYS_DEFAULT      =     5 * 86400 ; // default for unsigned identities: 5 days
static const time_t MAX_KEEP_KEYS_SIGNED       =     8 * 86400 ; // signed identities by unknown key
static const time_t MAX_KEEP_KEYS_SIGNED_KNOWN =    30 * 86400 ; // signed identities by known node keys

static const uint32_t MAX_DELAY_BEFORE_CLEANING=    1800 ; // clean old keys every 30 mins

static const uint32_t MAX_SERIALISED_IDENTITY_AGE  = 600 ; // after 10 mins, a serialised identity record must be renewed.

RsIdentity *rsIdentity = NULL;

/******
 * Some notes:
 * Identity tasks:
 *   - Provide keys for signing / validating author signatures.
 *   - Reputations
 *   - Identify Known Friend's IDs.
 *   - Provide details to other services (nicknames, reputations, gpg ids, etc)
 *
 * Background services:
 *   - Lookup and cache keys / details of identities.
 *   - Check GPGHashes.
 *   - Calculate Reputations.
 *
 * We have a lot of information to store in Service Strings.
 *   - GPGId or last check ts.
 *   - Reputation stuff.
 */

#define RSGXSID_MAX_SERVICE_STRING 1024

#define BG_PGPHASH 	1
#define BG_RECOGN 	2
#define BG_REPUTATION 	3


#define GXSIDREQ_CACHELOAD	         0x0001
#define GXSIDREQ_CACHEOWNIDS	     0x0002
#define GXSIDREQ_PGPHASH 	         0x0010
#define GXSIDREQ_RECOGN 	         0x0020
#define GXSIDREQ_OPINION 	         0x0030
#define GXSIDREQ_SERIALIZE_TO_MEMORY 0x0040

#define GXSIDREQ_CACHETEST 	0x1000

// Events.
#define GXSID_EVENT_CACHEOWNIDS		0x0001
#define GXSID_EVENT_CACHELOAD 		0x0002

#define GXSID_EVENT_PGPHASH 		0x0010
#define GXSID_EVENT_PGPHASH_PROC 	0x0011

#define GXSID_EVENT_RECOGN 		0x0020
#define GXSID_EVENT_RECOGN_PROC 	0x0021

#define GXSID_EVENT_REPUTATION 		0x0030

#define GXSID_EVENT_CACHETEST 		0x1000

#define GXSID_EVENT_DUMMYDATA		0x2000
#define GXSID_EVENT_DUMMY_OWNIDS	0x2001
#define GXSID_EVENT_DUMMY_PGPID		0x2002
#define GXSID_EVENT_DUMMY_UNKNOWN_PGPID	0x2003
#define GXSID_EVENT_DUMMY_PSEUDOID	0x2004
#define GXSID_EVENT_REQUEST_IDS     0x2005


/* delays */

#define CACHETEST_PERIOD	            60
#define DELAY_BETWEEN_CONFIG_UPDATES   300
#define GXS_MAX_KEY_TS_USAGE_MAP_SIZE    5

#define OWNID_RELOAD_DELAY		10

#define PGPHASH_PERIOD			60
#define PGPHASH_RETRY_PERIOD		11
#define PGPHASH_PROC_PERIOD		1

#define RECOGN_PERIOD			90
#define RECOGN_RETRY_PERIOD		17
#define RECOGN_PROC_PERIOD		1

#define REPUTATION_PERIOD		60
#define REPUTATION_RETRY_PERIOD		13
#define REPUTATION_PROC_PERIOD		1

/********************************************************************************/
/******************* Startup / Tick    ******************************************/
/********************************************************************************/

p3IdService::p3IdService(RsGeneralDataService *gds, RsNetworkExchangeService *nes, PgpAuxUtils *pgpUtils)
	: RsGxsIdExchange(gds, nes, new RsGxsIdSerialiser(), RS_SERVICE_GXS_TYPE_GXSID, idAuthenPolicy()), 
	RsIdentity(this), GxsTokenQueue(this), RsTickEvent(), 
	mKeyCache(GXSID_MAX_CACHE_SIZE, "GxsIdKeyCache"), 
	mIdMtx("p3IdService"), mNes(nes),
	mPgpUtils(pgpUtils)
{
	mBgSchedule_Mode = 0;
    mBgSchedule_Active = false;
    mLastKeyCleaningTime = time(NULL) - int(MAX_DELAY_BEFORE_CLEANING * 0.9) ;
    mLastConfigUpdate = 0 ;
    mOwnIdsLoaded = false ;
    mMaxKeepKeysBanned = MAX_KEEP_KEYS_BANNED_DEFAULT;

	// Kick off Cache Testing, + Others.
	RsTickEvent::schedule_in(GXSID_EVENT_PGPHASH, PGPHASH_PERIOD);
	RsTickEvent::schedule_in(GXSID_EVENT_REPUTATION, REPUTATION_PERIOD);
	RsTickEvent::schedule_now(GXSID_EVENT_CACHEOWNIDS);

	//RsTickEvent::schedule_in(GXSID_EVENT_CACHETEST, CACHETEST_PERIOD);

#ifdef GXSID_GEN_DUMMY_DATA
	//RsTickEvent::schedule_now(GXSID_EVENT_DUMMYDATA);
#endif

	loadRecognKeys();
}

const std::string GXSID_APP_NAME = "gxsid";
const uint16_t GXSID_APP_MAJOR_VERSION  =       1;
const uint16_t GXSID_APP_MINOR_VERSION  =       0;
const uint16_t GXSID_MIN_MAJOR_VERSION  =       1;
const uint16_t GXSID_MIN_MINOR_VERSION  =       0;

RsServiceInfo p3IdService::getServiceInfo()
{
        return RsServiceInfo(RS_SERVICE_GXS_TYPE_GXSID,
                GXSID_APP_NAME,
                GXSID_APP_MAJOR_VERSION,
                GXSID_APP_MINOR_VERSION,
                GXSID_MIN_MAJOR_VERSION,
                GXSID_MIN_MINOR_VERSION);
}



void p3IdService::setNes(RsNetworkExchangeService *nes)
{
    RsStackMutex stack(mIdMtx);
    mNes = nes;
}

uint32_t p3IdService::idAuthenPolicy()
{
	uint32_t policy = 0;
	uint8_t flag = 0;

	// Messages are send reputations. normally not by ID holder - so need signatures.
	flag = GXS_SERV::MSG_AUTHEN_ROOT_AUTHOR_SIGN | GXS_SERV::MSG_AUTHEN_CHILD_AUTHOR_SIGN;
	RsGenExchange::setAuthenPolicyFlag(flag, policy, RsGenExchange::PUBLIC_GRP_BITS);
	RsGenExchange::setAuthenPolicyFlag(flag, policy, RsGenExchange::RESTRICTED_GRP_BITS);
	RsGenExchange::setAuthenPolicyFlag(flag, policy, RsGenExchange::PRIVATE_GRP_BITS);

	// No ID required.
	flag = 0;
	RsGenExchange::setAuthenPolicyFlag(flag, policy, RsGenExchange::GRP_OPTION_BITS);

	return policy;
}

bool p3IdService::isARegularContact(const RsGxsId& id) 
{
    RsStackMutex stack(mIdMtx);
    return mContacts.find(id) != mContacts.end() ;
}

bool p3IdService::setAsRegularContact(const RsGxsId& id,bool b)
{
    std::set<RsGxsId>::iterator it = mContacts.find(id) ;
    
    if(b && (it == mContacts.end()))
    {
        mContacts.insert(id) ;
        slowIndicateConfigChanged() ;
    }
    
    if( (!b) &&(it != mContacts.end()))
    {
        mContacts.erase(it) ;
        slowIndicateConfigChanged() ;
    }
    
    return true ;
}

void p3IdService::slowIndicateConfigChanged()
{
    time_t now = time(NULL) ;

    if(mLastConfigUpdate + DELAY_BETWEEN_CONFIG_UPDATES < now)
    {
        IndicateConfigChanged() ;
    mLastConfigUpdate = now ;
    }
}
time_t p3IdService::locked_getLastUsageTS(const RsGxsId& gxs_id)
{
    std::map<RsGxsId,keyTSInfo>::const_iterator it = mKeysTS.find(gxs_id) ;

    if(it == mKeysTS.end())
        return 0 ;
    else
        return it->second.TS ;
}
void p3IdService::timeStampKey(const RsGxsId& gxs_id, const RsIdentityUsage& 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 ;
    }
#ifdef DEBUG_IDS
    std::cerr << "(II) time stamping key " << gxs_id << " for the following reason: " << reason << std::endl;
#endif

    RS_STACK_MUTEX(mIdMtx) ;

    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<RsIdentityUsage,time_t>::iterator best_it ;
        time_t best_time = now+1;

        for(std::map<RsIdentityUsage,time_t>::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() ;
}

bool p3IdService::loadList(std::list<RsItem*>& items)
{
    RS_STACK_MUTEX(mIdMtx) ;
    RsGxsIdLocalInfoItem *lii;

    for(std::list<RsItem*>::const_iterator it = items.begin();it!=items.end();++it)
    {
        if( (lii = dynamic_cast<RsGxsIdLocalInfoItem*>(*it)) != NULL)
        {
            for(std::map<RsGxsId,time_t>::const_iterator it2 = lii->mTimeStamps.begin();it2!=lii->mTimeStamps.end();++it2)
                mKeysTS[it2->first].TS = it2->second;

            mContacts = lii->mContacts ;
        }

	    RsConfigKeyValueSet *vitem = dynamic_cast<RsConfigKeyValueSet *>(*it);

	    if(vitem)
		    for(std::list<RsTlvKeyValue>::const_iterator kit = vitem->tlvkvs.pairs.begin(); kit != vitem->tlvkvs.pairs.end(); ++kit)
		    {
			    if(kit->key == "REMOVE_BANNED_IDENTITIES_DELAY")
			    {
				    int val ;
				    if (sscanf(kit->value.c_str(), "%d", &val) == 1)
				    {
					    mMaxKeepKeysBanned = val ;
					    std::cerr << "Setting mMaxKeepKeysBanned threshold to " << val << std::endl ;
				    }
			    };
            }

        delete *it ;
    }

    items.clear() ;
    return true ;
}

void p3IdService::setDeleteBannedNodesThreshold(uint32_t days)
{
    RsStackMutex stack(mIdMtx); /****** LOCKED MUTEX *******/
    if(mMaxKeepKeysBanned != days*86400)
    {
        mMaxKeepKeysBanned = days*86400 ;
        IndicateConfigChanged();
    }
}
uint32_t p3IdService::deleteBannedNodesThreshold()
{
    RsStackMutex stack(mIdMtx); /****** LOCKED MUTEX *******/

    return mMaxKeepKeysBanned/86400;
}


bool p3IdService::saveList(bool& cleanup,std::list<RsItem*>& items)
{
#ifdef DEBUG_IDS
    std::cerr << "p3IdService::saveList()" << std::endl;
#endif

    RS_STACK_MUTEX(mIdMtx) ;
    cleanup = true ;
    RsGxsIdLocalInfoItem *item = new RsGxsIdLocalInfoItem ;

    for(std::map<RsGxsId,keyTSInfo>::const_iterator it(mKeysTS.begin());it!=mKeysTS.end();++it)
		item->mTimeStamps[it->first] = it->second.TS;

    item->mContacts = mContacts ;

    items.push_back(item) ;

    RsConfigKeyValueSet *vitem = new RsConfigKeyValueSet ;
	RsTlvKeyValue kv;

	kv.key = "REMOVE_BANNED_IDENTITIES_DELAY" ;
	rs_sprintf(kv.value, "%d", mMaxKeepKeysBanned);
	vitem->tlvkvs.pairs.push_back(kv) ;

    items.push_back(vitem) ;

    return true ;
}

class IdCacheEntryCleaner
{
public:
    IdCacheEntryCleaner(const std::map<RsGxsId,p3IdService::keyTSInfo>& last_usage_TSs,uint32_t m) : mLastUsageTS(last_usage_TSs),mMaxKeepKeysBanned(m) {}

    bool processEntry(RsGxsIdCache& entry)
    {
        time_t now = time(NULL);
        const RsGxsId& gxs_id = entry.details.mId ;

        bool is_id_banned = rsReputations->isIdentityBanned(gxs_id) ;
        bool is_own_id    = (bool)(entry.details.mFlags & RS_IDENTITY_FLAGS_IS_OWN_ID) ;
        bool is_known_id  = (bool)(entry.details.mFlags & RS_IDENTITY_FLAGS_PGP_KNOWN) ;
        bool is_signed_id = (bool)(entry.details.mFlags & RS_IDENTITY_FLAGS_PGP_LINKED) ;
        bool is_a_contact = (bool)(entry.details.mFlags & RS_IDENTITY_FLAGS_IS_A_CONTACT) ;

#ifdef DEBUG_IDS
        std::cerr << "Identity: " << gxs_id << ": banned: " << is_id_banned << ", own: " << is_own_id << ", contact: " << is_a_contact << ", signed: " << is_signed_id << ", known: " << is_known_id;
#endif

        if(is_own_id || is_a_contact)
        {
#ifdef DEBUG_IDS
            std::cerr << " => kept" << std::endl;
#endif
            return true ;
        }

        std::map<RsGxsId,p3IdService::keyTSInfo>::const_iterator it = mLastUsageTS.find(gxs_id) ;

        bool no_ts = (it == mLastUsageTS.end()) ;

        time_t last_usage_ts = no_ts?0:(it->second.TS);
        time_t max_keep_time = 0;
        bool should_check = true ;

        if(no_ts)
            max_keep_time = 0 ;
		else if(is_id_banned)
        {
			if(mMaxKeepKeysBanned == 0)
				should_check = false ;
			else
				max_keep_time = mMaxKeepKeysBanned ;
        }
		else if(is_known_id)
            max_keep_time = MAX_KEEP_KEYS_SIGNED_KNOWN ;
        else if(is_signed_id)
            max_keep_time = MAX_KEEP_KEYS_SIGNED ;
        else
            max_keep_time = MAX_KEEP_KEYS_DEFAULT ;

#ifdef DEBUG_IDS
        std::cerr << ". Max keep = " << max_keep_time/86400 << " days. Unused for " << (now - last_usage_ts + 86399)/86400 << " days " ;
#endif

        if(should_check && now > last_usage_ts + max_keep_time)
        {
#ifdef DEBUG_IDS
            std::cerr << " => delete " << std::endl;
#endif
            ids_to_delete.push_back(gxs_id) ;
        }
#ifdef DEBUG_IDS
        else
            std::cerr << " => keep " << std::endl;
#endif

        return true;
    }

    std::list<RsGxsId> ids_to_delete ;
    const std::map<RsGxsId,p3IdService::keyTSInfo>& mLastUsageTS;
    uint32_t mMaxKeepKeysBanned ;
};

void p3IdService::cleanUnusedKeys()
{
    std::list<RsGxsId> ids_to_delete ;

    std::cerr << "Cleaning unused keys:" << std::endl;

    // we need to stash all ids to delete into an off-mutex structure since deleteIdentity() will trigger the lock
    {
        RS_STACK_MUTEX(mIdMtx) ;

        if(!mOwnIdsLoaded)
        {
            std::cerr << "(EE) Own ids not loaded. Cannot clean unused keys." << std::endl;
            return ;
        }

        // grab at most 10 identities to delete. No need to send too many requests to the token queue at once.
        IdCacheEntryCleaner idcec(mKeysTS,mMaxKeepKeysBanned) ;

        mKeyCache.applyToAllCachedEntries(idcec,&IdCacheEntryCleaner::processEntry);

        ids_to_delete = idcec.ids_to_delete ;
    }
    std::cerr << "Collected " << ids_to_delete.size() << " keys to delete among " << mKeyCache.size() << std::endl;

    for(std::list<RsGxsId>::const_iterator it(ids_to_delete.begin());it!=ids_to_delete.end();++it)
    {
#ifdef DEBUG_IDS
        std::cerr << "Deleting identity " << *it << " which is too old." << std::endl;
#endif
        uint32_t token ;
        RsGxsIdGroup group;
        group.mMeta.mGroupId=RsGxsGroupId(*it);
        rsIdentity->deleteIdentity(token, group);

        {
            RS_STACK_MUTEX(mIdMtx) ;
            mKeysTS.erase(*it) ;

            // mPublicKeyCache.erase(*it) ; no need to do it now. It's done in p3IdService::deleteGroup()
        }
    }
}

void	p3IdService::service_tick()
{
    RsTickEvent::tick_events();
    GxsTokenQueue::checkRequests(); // GxsTokenQueue handles all requests.

    time_t now = time(NULL) ;

    if(mLastKeyCleaningTime + MAX_DELAY_BEFORE_CLEANING < now)
    {
        cleanUnusedKeys() ;
        mLastKeyCleaningTime = now ;
    }
    return;
}

bool p3IdService::acceptNewGroup(const RsGxsGrpMetaData *grpMeta)
{
    bool res = !rsReputations->isIdentityBanned(RsGxsId(grpMeta->mGroupId)) ;

#ifdef DEBUG_IDS
    std::cerr << "p3IdService::acceptNewGroup: ID=" << grpMeta->mGroupId << ": " << (res?"ACCEPTED":"DENIED") << std::endl;
#endif

    return res ;
}

void p3IdService::notifyChanges(std::vector<RsGxsNotify *> &changes)
{
#ifdef DEBUG_IDS
    std::cerr << "p3IdService::notifyChanges()";
    std::cerr << std::endl;
#endif

    /* iterate through and grab any new messages */
    std::list<RsGxsGroupId> unprocessedGroups;

    for(uint32_t i = 0;i<changes.size();++i)
    {
        RsGxsGroupChange *groupChange = dynamic_cast<RsGxsGroupChange *>(changes[i]);
        RsGxsMsgChange *msgChange = dynamic_cast<RsGxsMsgChange *>(changes[i]);

        if (msgChange && !msgChange->metaChange())
        {
#ifdef DEBUG_IDS
            std::cerr << "p3IdService::notifyChanges() Found Message Change Notification";
            std::cerr << std::endl;
#endif

            std::map<RsGxsGroupId, std::vector<RsGxsMessageId> > &msgChangeMap = msgChange->msgChangeMap;
            std::map<RsGxsGroupId, std::vector<RsGxsMessageId> >::iterator mit;
            for(mit = msgChangeMap.begin(); mit != msgChangeMap.end(); ++mit)
            {
#ifdef DEBUG_IDS
                std::cerr << "p3IdService::notifyChanges() Msgs for Group: " << mit->first;
                std::cerr << std::endl;
#endif
            }
        }

        /* shouldn't need to worry about groups - as they need to be subscribed to */
        if (groupChange && !groupChange->metaChange())
        {
#ifdef DEBUG_IDS
            std::cerr << "p3IdService::notifyChanges() Found Group Change Notification";
            std::cerr << std::endl;
#endif
            std::list<RsGxsGroupId> &groupList = groupChange->mGrpIdList;
            std::list<RsGxsGroupId>::iterator git;

            for(git = groupList.begin(); git != groupList.end();)
            {
#ifdef DEBUG_IDS
                std::cerr << "p3IdService::notifyChanges() Auto Subscribe to Incoming Groups: " << *git;
                std::cerr << std::endl;
#endif
                if(!rsReputations->isIdentityBanned(RsGxsId(*git)))
                {
                    uint32_t token;
                    RsGenExchange::subscribeToGroup(token, *git, true);

                    // also time_stamp the key that this group represents

                    timeStampKey(RsGxsId(*git),RsIdentityUsage(serviceType(),RsIdentityUsage::IDENTITY_DATA_UPDATE)) ;

                    ++git;
                }
                else
                    git = groupList.erase(git) ;
            }

            if(groupList.empty())
            {
                delete changes[i] ;
                changes[i] = NULL ;
            }
        }
    }

    RsGxsIfaceHelper::receiveChanges(changes);
}

/********************************************************************************/
/******************* RsIdentity Interface ***************************************/
/********************************************************************************/

#if 0
bool p3IdService:: getNickname(const RsGxsId &id, std::string &nickname)
{
    return false;
}
#endif

time_t p3IdService::getLastUsageTS(const RsGxsId &id)
{
    RsStackMutex stack(mIdMtx); /********** STACK LOCKED MTX ******/
    return locked_getLastUsageTS(id) ;
}

bool p3IdService::getIdDetails(const RsGxsId &id, RsIdentityDetails &details)
{
#ifdef DEBUG_IDS
    std::cerr << "p3IdService::getIdDetails(" << id << ")";
    std::cerr << std::endl;
#endif

    {
        RsStackMutex stack(mIdMtx); /********** STACK LOCKED MTX ******/

        RsGxsIdCache data;

        if (mKeyCache.fetch(id, data))
        {
            // This step is needed, because p3GxsReputation does not know all identities, and might not have any data for
            // the ones in the contact list. So we change them on demand.

            if(mContacts.find(id) != mContacts.end() && rsReputations->nodeAutoPositiveOpinionForContacts())
                rsReputations->setOwnOpinion(id,RsReputations::OPINION_POSITIVE) ;

            details = data.details;

			std::map<RsGxsId,keyTSInfo>::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)
                details.mNickname = "[too long a name]" ;

            rsReputations->getReputationInfo(id,details.mPgpId,details.mReputation) ;

            return true;
        }
    }

    /* it isn't there - add to public requests */
    cache_request_load(id);

    return false;
}

bool p3IdService::isOwnId(const RsGxsId& id)
{
    RsStackMutex stack(mIdMtx); /********** STACK LOCKED MTX ******/

    return std::find(mOwnIds.begin(),mOwnIds.end(),id) != mOwnIds.end() ;
}
bool p3IdService::getOwnIds(std::list<RsGxsId> &ownIds)
{
    RsStackMutex stack(mIdMtx); /********** STACK LOCKED MTX ******/

    if(!mOwnIdsLoaded)
    {
        std::cerr << "p3IdService::getOwnIds(): own identities are not loaded yet." << std::endl;
        return false ;
    }

    ownIds = mOwnIds;
    return true ;
}

bool p3IdService::serialiseIdentityToMemory( const RsGxsId& id,
                                             std::string& radix_string )
{
	RS_STACK_MUTEX(mIdMtx);

    // look into cache. If available, return the data. If not, request it.

    std::map<RsGxsId,SerialisedIdentityStruct>::const_iterator it = mSerialisedIdentities.find(id);

    if(it != mSerialisedIdentities.end())
    {
        Radix64::encode(it->second.mMem,it->second.mSize,radix_string) ;

        if(it->second.mLastUsageTS + MAX_SERIALISED_IDENTITY_AGE > time(NULL))
			return true ;

        std::cerr << "Identity " << id << " will be re-serialised, because the last record is too old." << std::endl;
    }

	RsTokReqOptions opts;
	opts.mReqType = GXS_REQUEST_TYPE_GROUP_DATA;
	uint32_t token = 0;
    std::list<RsGxsGroupId> groupIds;

    groupIds.push_back(RsGxsGroupId(id)) ;

	RsGenExchange::getTokenService()->requestGroupInfo(token, RS_TOKREQ_ANSTYPE_DATA, opts, groupIds);
	GxsTokenQueue::queueRequest(token, GXSIDREQ_SERIALIZE_TO_MEMORY);

	return false;
}

void p3IdService::handle_get_serialized_grp(uint32_t token)
{
    // store the serialized data in cache.

    unsigned char *mem = NULL;
    uint32_t size;
    RsGxsGroupId id ;

	if(!RsGenExchange::getSerializedGroupData(token,id, mem,size))
    {
        std::cerr << "(EE) call to RsGenExchage::getSerializedGroupData() failed." << std::endl;
        return ;
    }

    std::cerr << "Received serialised group from RsGenExchange." << std::endl;

    std::map<RsGxsId,SerialisedIdentityStruct>::const_iterator it = mSerialisedIdentities.find(RsGxsId(id));

    if(it != mSerialisedIdentities.end())
        free(it->second.mMem) ;

    SerialisedIdentityStruct s ;
    s.mMem = mem ;
    s.mSize = size ;
    s.mLastUsageTS = time(NULL) ;

    mSerialisedIdentities[RsGxsId(id)] = s ;
}

bool p3IdService::deserialiseIdentityFromMemory(const std::string& radix_string,
                                                RsGxsId* id /* = nullptr */)
{
	std::vector<uint8_t> mem = Radix64::decode(radix_string);

	if(mem.empty())
	{
		std::cerr << __PRETTY_FUNCTION__ << "Cannot decode radix string \""
		          << radix_string << "\"" << std::endl;
		return false;
	}

	if( !RsGenExchange::deserializeGroupData(
	            mem.data(), mem.size(), reinterpret_cast<RsGxsGroupId*>(id)) )
	{
		std::cerr << __PRETTY_FUNCTION__ << "Cannot load identity from radix "
		          << "string \"" << radix_string << "\"" << std::endl;
		return false;
	}

	return true;
}

bool p3IdService::createIdentity(uint32_t& token, RsIdentityParameters &params)
{

    RsGxsIdGroup id;

    id.mMeta.mGroupName = params.nickname;
    id.mMeta.mCircleType = GXS_CIRCLE_TYPE_PUBLIC ;
    id.mImage = params.mImage;

    if (params.isPgpLinked)
    {
#warning csoler 2017-02-07: Backward compatibility issue to fix here in v0.7.0

        // This is a hack, because a bad decision led to having RSGXSID_GROUPFLAG_REALID be equal to GXS_SERV::FLAG_PRIVACY_PRIVATE.
        // In order to keep backward compatibility, we'll also add the new value
        // When the ID is not PGP linked, the group flag cannot be let empty, so we use PUBLIC.
        //
        // The correct combination of flags should be:
        //		PGP-linked:		GXS_SERV::FLAGS_PRIVACY_PUBLIC | RSGXSID_GROUPFLAG_REALID
        //		Anonymous :		GXS_SERV::FLAGS_PRIVACY_PUBLIC

        id.mMeta.mGroupFlags |= GXS_SERV::FLAG_PRIVACY_PRIVATE;	// this is also equal to RSGXSID_GROUPFLAG_REALID_deprecated
        id.mMeta.mGroupFlags |= RSGXSID_GROUPFLAG_REALID;

        // The current version should be able to produce new identities that old peers will accept as well.
        // In the future, we need to:
        //     - set the current group flags here (see above)
        //	   - replace all occurences of RSGXSID_GROUPFLAG_REALID_deprecated by RSGXSID_GROUPFLAG_REALID in the code.
    }
    else
        id.mMeta.mGroupFlags |= GXS_SERV::FLAG_PRIVACY_PUBLIC;

    createGroup(token, id);

    return true;
}

bool p3IdService::updateIdentity(uint32_t& token, RsGxsIdGroup &group)
{
#ifdef DEBUG_IDS
    std::cerr << "p3IdService::updateIdentity()";
    std::cerr << std::endl;
#endif
    group.mMeta.mCircleType = GXS_CIRCLE_TYPE_PUBLIC ;

    updateGroup(token, group);

    return false;
}

bool p3IdService::deleteIdentity(uint32_t& token, RsGxsIdGroup &group)
{
#ifdef DEBUG_IDS
    std::cerr << "p3IdService::deleteIdentity()";
    std::cerr << std::endl;
#endif

    deleteGroup(token, group);

    return false;
}


bool p3IdService::parseRecognTag(const RsGxsId &id, const std::string &nickname,
                                 const std::string &tag, RsRecognTagDetails &details)
{
#ifdef DEBUG_RECOGN
    std::cerr << "p3IdService::parseRecognTag()";
    std::cerr << std::endl;
#endif

    RsGxsRecognTagItem *tagitem = RsRecogn::extractTag(tag);
    if (!tagitem)
    {
        return false;
    }

    bool isPending = false;
    bool isValid = recogn_checktag(id, nickname, tagitem, true, isPending);

    details.valid_from = tagitem->valid_from;
    details.valid_to = tagitem->valid_to;
    details.tag_class = tagitem->tag_class;
    details.tag_type = tagitem->tag_type;
    details.signer = tagitem->sign.keyId.toStdString();

    details.is_valid = isValid;
    details.is_pending = isPending;

    delete tagitem;

    return true;
}

bool p3IdService::getRecognTagRequest(const RsGxsId &id, const std::string &comment, uint16_t tag_class, uint16_t tag_type, std::string &tag)
{
#ifdef DEBUG_RECOGN
    std::cerr << "p3IdService::getRecognTagRequest()";
    std::cerr << std::endl;
#endif
    if(!isOwnId(id))
    {
        std::cerr << "(EE) cannot retrieve own key to create tag request. KeyId=" << id << std::endl;
        return false ;
    }

    RsTlvPrivateRSAKey key;
    std::string nickname;
    RsGxsIdCache data ;

    {
        RsStackMutex stack(mIdMtx); /********** STACK LOCKED MTX ******/

        if(!mKeyCache.fetch(id, data))
            return false ;

        nickname = data.details.mNickname ;
        key = data.priv_key ;
    }

    return RsRecogn::createTagRequest(key, id, nickname, tag_class, tag_type, comment,  tag);
}



/********************************************************************************/
/******************* RsGixs Interface     ***************************************/
/********************************************************************************/

bool p3IdService::haveKey(const RsGxsId &id)
{
    RsStackMutex stack(mIdMtx); /********** STACK LOCKED MTX ******/
    return mKeyCache.is_cached(id);
}

bool p3IdService::havePrivateKey(const RsGxsId &id)
{
    if(! isOwnId(id))
        return false ;

    RsStackMutex stack(mIdMtx); /********** STACK LOCKED MTX ******/
    return mKeyCache.is_cached(id) ;
}

static void mergeIds(std::map<RsGxsId,std::list<RsPeerId> >& idmap,const RsGxsId& id,const std::list<RsPeerId>& peers)
{
    // merge the two lists (I use a std::set to make it more efficient)
#ifdef DEBUG_IDS
    std::cerr << "p3IdService::requestKey(): merging list with existing pending request." << std::endl;
#endif

    std::list<RsPeerId>& old_peers(idmap[id]) ;	// create if necessary
    std::set<RsPeerId> new_peers ;

    for(std::list<RsPeerId>::const_iterator it(peers.begin());it!=peers.end();++it)
        new_peers.insert(*it) ;

    for(std::list<RsPeerId>::iterator it(old_peers.begin());it!=old_peers.end();++it)
        new_peers.insert(*it) ;

    old_peers.clear();

    for(std::set<RsPeerId>::iterator it(new_peers.begin());it!=new_peers.end();++it)
        old_peers.push_back(*it) ;
}

bool p3IdService::requestKey(const RsGxsId &id, const std::list<RsPeerId>& peers,const RsIdentityUsage& use_info)
{
    if(id.isNull())
    {
        std::cerr << "(EE) nul ID requested to p3IdService. This should not happen. Callstack:" << std::endl;
        print_stacktrace();
        return false ;
    }

    if (haveKey(id))
        return true;
    else
    {
        // Normally we should call getIdDetails(), but since the key is not known, we need to digg a possibly old information
        // from the reputation system, which keeps its own list of banned keys. Of course, the owner ID is not known at this point.

#ifdef DEBUG_IDS
        std::cerr << "p3IdService::requesting key " << id <<std::endl;
#endif

        RsReputations::ReputationInfo info ;
        rsReputations->getReputationInfo(id,RsPgpId(),info) ;

        if(info.mOverallReputationLevel == RsReputations::REPUTATION_LOCALLY_NEGATIVE)
        {
            std::cerr << "(II) not requesting Key " << id << " because it has been banned." << std::endl;

            {
                RS_STACK_MUTEX(mIdMtx); /********** STACK LOCKED MTX ******/
                mIdsNotPresent.erase(id) ;
            }
            return true;
        }

        RsStackMutex stack(mIdMtx); /********** STACK LOCKED MTX ******/

        std::map<RsGxsId,std::list<RsPeerId> >::iterator rit = mIdsNotPresent.find(id) ;

        if(rit != mIdsNotPresent.end())
        {
            if(!peers.empty())
                mergeIds(mIdsNotPresent,id,peers) ;

            return true ;
        }
    }
    {
		RS_STACK_MUTEX(mIdMtx); /********** STACK LOCKED MTX ******/
		mKeysTS[id].usage_map[use_info] = time(NULL) ;
    }

    return cache_request_load(id, peers);
}

bool p3IdService::isPendingNetworkRequest(const RsGxsId& gxsId)
{
    RsStackMutex stack(mIdMtx); /********** STACK LOCKED MTX ******/
    // if ids has beens confirmed as not physically present return
    // immediately, id will be removed from list if found by auto nxs net search
    if(mIdsNotPresent.find(gxsId) != mIdsNotPresent.end())
        return true;

    return false;
}

bool p3IdService::getKey(const RsGxsId &id, RsTlvPublicRSAKey &key)
{
    {
        RsStackMutex stack(mIdMtx); /********** STACK LOCKED MTX ******/
        RsGxsIdCache data;

        if (mKeyCache.fetch(id, data))
        {
            key = data.pub_key;
            return true;
        }
    }

    cache_request_load(id);

    key.keyId.clear() ;
    return false;
}

bool p3IdService::requestPrivateKey(const RsGxsId &id)
{
    if (havePrivateKey(id))
        return true;

    return cache_request_load(id);
}

bool p3IdService::getPrivateKey(const RsGxsId &id, RsTlvPrivateRSAKey &key)
{
    {
        RsStackMutex stack(mIdMtx); /********** STACK LOCKED MTX ******/
        RsGxsIdCache data;

        if (mKeyCache.fetch(id, data))
        {
            key = data.priv_key;
            return true;
        }
    }

    key.keyId.clear() ;
    cache_request_load(id);

    return false ;
}


bool p3IdService::signData(const uint8_t *data,uint32_t data_size,const RsGxsId& own_gxs_id,RsTlvKeySignature& signature,uint32_t& error_status)
{
    RsTlvPrivateRSAKey signature_key ;

    int i ;
    for(i=0;i<6;++i)
        if(!getPrivateKey(own_gxs_id,signature_key) || signature_key.keyData.bin_data == NULL)
        {
#ifdef DEBUG_IDS
            std::cerr << "  Cannot get key. Waiting for caching. try " << i << "/6" << std::endl;
#endif
            usleep(500 * 1000) ;	// sleep for 500 msec.
        }
        else
            break ;

    if(i == 6)
    {
        std::cerr << "  (EE) Could not retrieve own private key for ID = " << own_gxs_id << ". Giging up sending DH session params. This should not happen." << std::endl;
        error_status = RS_GIXS_ERROR_KEY_NOT_AVAILABLE ;
        return false ;
    }

#ifdef DEBUG_IDS
    std::cerr << "  Signing..." << std::endl;
#endif

    if(!GxsSecurity::getSignature((char *)data,data_size,signature_key,signature))
    {
        std::cerr << "  (EE) Cannot sign for id " << own_gxs_id << ". Signature call failed." << std::endl;
        error_status = RS_GIXS_ERROR_UNKNOWN ;
        return false ;
    }
    error_status = RS_GIXS_ERROR_NO_ERROR ;
    timeStampKey(own_gxs_id,RsIdentityUsage(serviceType(),RsIdentityUsage::IDENTITY_GENERIC_SIGNATURE_CREATION)) ;

    return true ;
}
bool p3IdService::validateData(const uint8_t *data,uint32_t data_size,const RsTlvKeySignature& signature,bool force_load,const RsIdentityUsage& info,uint32_t& signing_error)
{
    // RsIdentityDetails details ;
    // getIdDetails(signature.keyId,details);
    RsTlvPublicRSAKey signature_key ;

    for(int i=0;i< (force_load?6:1);++i)
        if(!getKey(signature.keyId,signature_key) || signature_key.keyData.bin_data == NULL)
        {
#ifdef DEBUG_IDS
            std::cerr << "  Cannot get key. Waiting for caching. try " << i << "/6" << std::endl;
#endif
            if(force_load) usleep(500 * 1000) ;	// sleep for 500 msec.
        }
        else
            break ;

    if(signature_key.keyData.bin_data == NULL)
    {
#ifdef DEBUG_IDS
        std::cerr << "(EE) Cannot validate signature for unknown key " << signature.keyId << std::endl;
#endif
        signing_error = RS_GIXS_ERROR_KEY_NOT_AVAILABLE ;
        return false;
    }

    if(!GxsSecurity::validateSignature((char*)data,data_size,signature_key,signature))
    {
        std::cerr << "(SS) Signature was verified and it doesn't check! This is a security issue!" << std::endl;
        signing_error = RS_GIXS_ERROR_SIGNATURE_MISMATCH ;
        return false;
    }
    signing_error = RS_GIXS_ERROR_NO_ERROR ;

    timeStampKey(signature.keyId,info);
    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,
                               uint32_t& error_status,
                               bool force_load )
{
    RsTlvPublicRSAKey encryption_key ;

    // get the key, and let the cache find it.
	for(int i=0; i<(force_load?6:1);++i)
		if(getKey(encryption_key_id,encryption_key))
            break ;
        else
            usleep(500*1000) ; // sleep half a sec.

    if(encryption_key.keyId.isNull())
    {
        std::cerr << "    (EE) Cannot get encryption key for id " << encryption_key_id << std::endl;
        error_status = RS_GIXS_ERROR_KEY_NOT_AVAILABLE ;
        return false ;
    }

    if(!GxsSecurity::encrypt(encrypted_data,encrypted_data_size,decrypted_data,decrypted_data_size,encryption_key))
    {
        std::cerr << "    (EE) Encryption failed." << std::endl;
        error_status = RS_GIXS_ERROR_UNKNOWN ;
        return false ;
    }
    error_status = RS_GIXS_ERROR_NO_ERROR ;
    timeStampKey(encryption_key_id,RsIdentityUsage(serviceType(),RsIdentityUsage::IDENTITY_GENERIC_ENCRYPTION)) ;

    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 std::set<RsGxsId>& encrypt_ids,
                               uint32_t& error_status, bool force_load )
{
	std::set<const RsGxsId*> keyNotYetFoundIds;

	for( std::set<RsGxsId>::const_iterator it = encrypt_ids.begin();
	     it != encrypt_ids.end(); ++it )
	{
		const RsGxsId& gId(*it);
		if(gId.isNull())
		{
			std::cerr << "p3IdService::encryptData(...) (EE) got null GXS id"
			          << std::endl;
			return false;
		}
		else keyNotYetFoundIds.insert(&gId);
	}

	if(keyNotYetFoundIds.empty())
	{
		std::cerr << "p3IdService::encryptData(...) (EE) got empty GXS ids set"
		          << std::endl;
		print_stacktrace();
		return false;
	}

	std::vector<RsTlvPublicRSAKey> encryption_keys;
	int maxRounds = force_load ? 6 : 1;
	for( int i=0; i < maxRounds; ++i )
	{
		for( std::set<const RsGxsId*>::iterator it = keyNotYetFoundIds.begin();
		     it !=keyNotYetFoundIds.end(); )
		{
			RsTlvPublicRSAKey encryption_key;
			if(getKey(**it, encryption_key) && !encryption_key.keyId.isNull())
			{
				encryption_keys.push_back(encryption_key);
				it = keyNotYetFoundIds.erase(it);
			}
			else
			{
				++it;
			}
		}

		if(keyNotYetFoundIds.empty()) break;
		else usleep(500*1000);
	}

	if(!keyNotYetFoundIds.empty())
	{
		std::cerr << "p3IdService::encryptData(...) (EE) Cannot get "
		          << "encryption key for: ";
		for( std::set<const RsGxsId*>::iterator it = keyNotYetFoundIds.begin();
		     it !=keyNotYetFoundIds.end(); ++it )
			std::cerr << **it << " ";
		std::cerr << std::endl;
		print_stacktrace();

		error_status = RS_GIXS_ERROR_KEY_NOT_AVAILABLE;
		return false;
	}

	if(!GxsSecurity::encrypt( encrypted_data, encrypted_data_size,
	                          decrypted_data, decrypted_data_size,
	                          encryption_keys ))
	{
		std::cerr << "p3IdService::encryptData(...) (EE) Encryption failed."
		          << std::endl;
		print_stacktrace();

		error_status = RS_GIXS_ERROR_UNKNOWN;
		return false ;
	}

	for( std::set<RsGxsId>::const_iterator it = encrypt_ids.begin();
	     it != encrypt_ids.end(); ++it )
	{
		timeStampKey( *it,
		              RsIdentityUsage(
		                  serviceType(),
		                  RsIdentityUsage::IDENTITY_GENERIC_ENCRYPTION ) );
	}

	error_status = RS_GIXS_ERROR_NO_ERROR;
	return true;
}

bool p3IdService::decryptData( const uint8_t *encrypted_data,
                               uint32_t encrypted_data_size,
                               uint8_t *& decrypted_data,
                               uint32_t& decrypted_size,
                               const RsGxsId& key_id, uint32_t& error_status,
                               bool force_load )
{
    RsTlvPrivateRSAKey encryption_key ;

    // Get the key, and let the cache find it. It's our own key, so we should be able to find it, even if it takes
    // some seconds.

	int maxRounds = force_load ? 6 : 1;
	for(int i=0; i<maxRounds ;++i)
		if(getPrivateKey(key_id,encryption_key)) break;
		else usleep(500*1000) ; // sleep half a sec.

    if(encryption_key.keyId.isNull())
    {
        std::cerr << "  (EE) Cannot get own encryption key for id " << key_id << " to decrypt data. This should not happen." << std::endl;
        error_status = RS_GIXS_ERROR_KEY_NOT_AVAILABLE;
        return false ;
    }

    if(!GxsSecurity::decrypt(decrypted_data,decrypted_size,encrypted_data,encrypted_data_size,encryption_key))
    {
        std::cerr << "  (EE) Decryption failed." << std::endl;
        error_status = RS_GIXS_ERROR_UNKNOWN ;
        return false ;
	}
	error_status = RS_GIXS_ERROR_NO_ERROR;
	timeStampKey( key_id,
	              RsIdentityUsage(
	                  serviceType(),
	                  RsIdentityUsage::IDENTITY_GENERIC_DECRYPTION) );

    return true ;
}

bool p3IdService::decryptData( const uint8_t* encrypted_data,
                               uint32_t encrypted_data_size,
                               uint8_t*& decrypted_data,
                               uint32_t& decrypted_data_size,
                               const std::set<RsGxsId>& decrypt_ids,
                               uint32_t& error_status,
                               bool force_load )
{
	std::set<const RsGxsId*> keyNotYetFoundIds;

	for( std::set<RsGxsId>::const_iterator it = decrypt_ids.begin();
	     it != decrypt_ids.end(); ++it )
	{
		const RsGxsId& gId(*it);
		if(gId.isNull())
		{
			std::cerr << "p3IdService::decryptData(...) (EE) got null GXS id"
			          << std::endl;
			print_stacktrace();
			return false;
		}
		else keyNotYetFoundIds.insert(&gId);
	}

	if(keyNotYetFoundIds.empty())
	{
		std::cerr << "p3IdService::decryptData(...) (EE) got empty GXS ids set"
		          << std::endl;
		print_stacktrace();
		return false;
	}

	std::vector<RsTlvPrivateRSAKey> decryption_keys;
	int maxRounds = force_load ? 6 : 1;
	for( int i=0; i < maxRounds; ++i )
	{
		for( std::set<const RsGxsId*>::iterator it = keyNotYetFoundIds.begin();
		     it !=keyNotYetFoundIds.end(); )
		{
			RsTlvPrivateRSAKey decryption_key;
			if( getPrivateKey(**it, decryption_key)
			        && !decryption_key.keyId.isNull() )
			{
				decryption_keys.push_back(decryption_key);
				it = keyNotYetFoundIds.erase(it);
			}
			else
			{
				++it;
			}
		}

		if(keyNotYetFoundIds.empty()) break;
		else usleep(500*1000);
	}

	if(!keyNotYetFoundIds.empty())
	{
		std::cerr << "p3IdService::decryptData(...) (EE) Cannot get private key"
		          << " for: ";
		for( std::set<const RsGxsId*>::iterator it = keyNotYetFoundIds.begin();
		     it !=keyNotYetFoundIds.end(); ++it )
			std::cerr << **it << " ";
		std::cerr << std::endl;
		print_stacktrace();

		error_status = RS_GIXS_ERROR_KEY_NOT_AVAILABLE;
		return false;
	}

	if(!GxsSecurity::decrypt( decrypted_data, decrypted_data_size,
	                          encrypted_data, encrypted_data_size,
	                          decryption_keys ))
	{
		std::cerr << "p3IdService::decryptData(...) (EE) Decryption failed."
		          << std::endl;
		print_stacktrace();

		error_status = RS_GIXS_ERROR_UNKNOWN;
		return false ;
	}

	for( std::set<RsGxsId>::const_iterator it = decrypt_ids.begin();
	     it != decrypt_ids.end(); ++it )
	{
		timeStampKey( *it,
		              RsIdentityUsage(
		                  serviceType(),
		                  RsIdentityUsage::IDENTITY_GENERIC_DECRYPTION ) );
	}

	error_status = RS_GIXS_ERROR_NO_ERROR;
	return true;
}

#ifdef TO_BE_REMOVED
/********************************************************************************/
/******************* RsGixsReputation     ***************************************/
/********************************************************************************/

bool p3IdService::haveReputation(const RsGxsId &id)
{
    return haveKey(id);
}

bool p3IdService::loadReputation(const RsGxsId &id, const std::list<RsPeerId>& peers)
{
    if (haveKey(id))
        return true;
    else
    {
        if(isPendingNetworkRequest(id))
            return true;
    }


    return cache_request_load(id, peers);
}

bool p3IdService::getReputation(const RsGxsId &id, GixsReputation &rep)
{
    /* this is the key part for accepting messages */

    RsStackMutex stack(mIdMtx); /********** STACK LOCKED MTX ******/
    RsGxsIdCache data;

    if (mKeyCache.fetch(id, data))
    {
        rep.id = id;
        rep.score = 0;//data.details.mReputation.mOverallScore;
#ifdef DEBUG_IDS
        std::cerr << "p3IdService::getReputation() id: ";
        std::cerr << id.toStdString() << " score: " <<
                     rep.score;
        std::cerr << std::endl;
#endif

        return true;
    }
    else
    {
#ifdef DEBUG_IDS
        std::cerr << "p3IdService::getReputation() id: ";
        std::cerr << id.toStdString() << " not cached";
        std::cerr << std::endl;
#endif
    }
    return false;
}
#endif

#if 0
class RegistrationRequest
{
public:
    RegistrationRequest(uint32_t token, RsGxsId &id, int score)
        :m_extToken(token), m_id(id), m_score(score) { return; }

    uint32_t m_intToken;
    uint32_t m_extToken;
    RsGxsId m_id;
    int m_score;
};
#endif


bool p3IdService::submitOpinion(uint32_t& token, const RsGxsId &id, bool absOpinion, int score)
{
#ifdef DEBUG_OPINION
    std::cerr << "p3IdService::submitOpinion()";
    std::cerr << std::endl;
#endif

    uint32_t ansType = RS_TOKREQ_ANSTYPE_SUMMARY;
    RsTokReqOptions opts;
    opts.mReqType = GXS_REQUEST_TYPE_GROUP_META;

    token = RsGenExchange::generatePublicToken();

    uint32_t intToken;
    std::list<RsGxsGroupId> groups;
    groups.push_back(RsGxsGroupId(id));

    RsGenExchange::getTokenService()->requestGroupInfo(intToken, ansType, opts, groups);
    GxsTokenQueue::queueRequest(intToken, GXSIDREQ_OPINION);

    RsStackMutex stack(mIdMtx); /********** STACK LOCKED MTX ******/
    mPendingOpinion[intToken] = OpinionRequest(token, id, absOpinion, score);
    return true;
}



bool p3IdService::opinion_handlerequest(uint32_t token)
{
#ifdef DEBUG_OPINION
    std::cerr << "p3IdService::opinion_handlerequest()";
    std::cerr << std::endl;
#endif

    OpinionRequest req;
    {
        RsStackMutex stack(mIdMtx); /********** STACK LOCKED MTX ******/

        /* find in pendingReputation */
        std::map<uint32_t, OpinionRequest>::iterator it;
        it = mPendingOpinion.find(token);
        if (it == mPendingOpinion.end())
        {
            std::cerr << "p3IdService::opinion_handlerequest() ERROR finding PendingOpinion";
            std::cerr << std::endl;

            return false;
        }
        req = it->second;
        mPendingOpinion.erase(it);
    }

#ifdef DEBUG_OPINION
    std::cerr << "p3IdService::opinion_handlerequest() Id: " << req.mId << " score: " << req.mScore;
    std::cerr << std::endl;
#endif

    std::list<RsGroupMetaData> groups;
    std::list<RsGxsGroupId> groupList;

    if (!getGroupMeta(token, groups))
    {
        std::cerr << "p3IdService::opinion_handlerequest() ERROR getGroupMeta()";
        std::cerr << std::endl;

        updatePublicRequestStatus(req.mToken, RsTokenService::GXS_REQUEST_V2_STATUS_FAILED);
        return false;
    }

    if (groups.size() != 1)
    {
        std::cerr << "p3IdService::opinion_handlerequest() ERROR group.size() != 1";
        std::cerr << std::endl;

        // error.
        updatePublicRequestStatus(req.mToken, RsTokenService::GXS_REQUEST_V2_STATUS_FAILED);
        return false;
    }
    RsGroupMetaData &meta = *(groups.begin());

    if (meta.mGroupId != RsGxsGroupId(req.mId))
    {
        std::cerr << "p3IdService::opinion_handlerequest() ERROR Id mismatch";
        std::cerr << std::endl;

        // error.
        updatePublicRequestStatus(req.mToken, RsTokenService::GXS_REQUEST_V2_STATUS_FAILED);
        return false;
    }

    /* get the string */
    SSGxsIdGroup ssdata;
    ssdata.load(meta.mServiceString); // attempt load - okay if fails.

    /* modify score */
    if (req.mAbsOpinion)
    {
        ssdata.score.rep.mOwnOpinion = req.mScore;
    }
    else
    {
        ssdata.score.rep.mOwnOpinion += req.mScore;
    }

    // update IdScore too.
    bool pgpId = (meta.mGroupFlags & RSGXSID_GROUPFLAG_REALID_kept_for_compatibility);
    ssdata.score.rep.updateIdScore(pgpId, ssdata.pgp.validatedSignature);
    ssdata.score.rep.update();

    /* save string */
    std::string serviceString = ssdata.save();
#ifdef DEBUG_OPINION
    std::cerr << "p3IdService::opinion_handlerequest() new service_string: " << serviceString;
    std::cerr << std::endl;
#endif

    /* set new Group ServiceString */
    uint32_t dummyToken = 0;
    setGroupServiceString(dummyToken, meta.mGroupId, serviceString);
    cache_update_if_cached(RsGxsId(meta.mGroupId), serviceString);

    updatePublicRequestStatus(req.mToken, RsTokenService::GXS_REQUEST_V2_STATUS_COMPLETE);
    return true;
}


/********************************************************************************/
/******************* Get/Set Data      ******************************************/
/********************************************************************************/

RsSerialiser *p3IdService::setupSerialiser()
{
    RsSerialiser *rss = new RsSerialiser ;

    rss->addSerialType(new RsGxsIdSerialiser()) ;
    rss->addSerialType(new RsGeneralConfigSerialiser());

    return rss ;
}

bool p3IdService::getGroupData(const uint32_t &token, std::vector<RsGxsIdGroup> &groups)
{
    std::vector<RsGxsGrpItem*> grpData;
    bool ok = RsGenExchange::getGroupData(token, grpData);

    if(ok)
    {
        std::vector<RsGxsGrpItem*>::iterator vit = grpData.begin();

        for(; vit != grpData.end(); ++vit)
        {
            RsGxsIdGroupItem* item = dynamic_cast<RsGxsIdGroupItem*>(*vit);
            if (item)
            {
#ifdef DEBUG_IDS
                std::cerr << "p3IdService::getGroupData() Item is:";
                std::cerr << std::endl;
                item->print(std::cerr);
                std::cerr << std::endl;
#endif // DEBUG_IDS
                RsGxsIdGroup group ;
                item->toGxsIdGroup(group,false) ;

                {
                    RS_STACK_MUTEX(mIdMtx) ;
                    group.mLastUsageTS = locked_getLastUsageTS(RsGxsId(group.mMeta.mGroupId)) ;
                }

                // Decode information from serviceString.
                SSGxsIdGroup ssdata;
                if (ssdata.load(group.mMeta.mServiceString))
                {
                    group.mPgpKnown = ssdata.pgp.validatedSignature;
                    group.mPgpId    = ssdata.pgp.pgpId;
                    group.mReputation = ssdata.score.rep;
#ifdef DEBUG_IDS
                    std::cerr << "p3IdService::getGroupData() Success decoding ServiceString";
                    std::cerr << std::endl;
                    std::cerr << "\t mGpgKnown: " << group.mPgpKnown;
                    std::cerr << std::endl;
                    std::cerr << "\t mGpgId: " << group.mPgpId;
                    std::cerr << std::endl;
#endif // DEBUG_IDS
                }
                else
                {
                    group.mPgpKnown = false;
                    group.mPgpId.clear();

                    std::cerr << "p3IdService::getGroupData() Failed to decode ServiceString \""
                              << group.mMeta.mServiceString << "\"" ;
                    std::cerr << std::endl;
                }

                group.mIsAContact =  (mContacts.find(RsGxsId(group.mMeta.mGroupId)) != mContacts.end());

                groups.push_back(group);
                delete(item);
            }
            else
            {
                std::cerr << "Not a Id Item, deleting!" << std::endl;
                delete(*vit);
            }
        }
    }

    return ok;
}

bool p3IdService::getGroupSerializedData(const uint32_t &token, std::map<RsGxsId,std::string>& serialized_groups)
{
    unsigned char *mem = NULL;
    uint32_t size;
    RsGxsGroupId id ;

    serialized_groups.clear() ;

	if(!RsGenExchange::getSerializedGroupData(token,id, mem,size))
    {
        std::cerr << "(EE) call to RsGenExchage::getSerializedGroupData() failed." << std::endl;
        return false;
    }

    std::string radix ;

    Radix64::encode(mem,size,radix) ;

    serialized_groups[RsGxsId(id)] = radix ;

    return true;
}

/********************************************************************************/
/********************************************************************************/
/********************************************************************************/

bool 	p3IdService::createGroup(uint32_t& token, RsGxsIdGroup &group)
{
    RsGxsIdGroupItem* item = new RsGxsIdGroupItem();

    item->meta = group.mMeta;
    item->mImage.binData.setBinData(group.mImage.mData, group.mImage.mSize);

    RsGenExchange::publishGroup(token, item);
    return true;
}

bool p3IdService::updateGroup(uint32_t& token, RsGxsIdGroup &group)
{
    RsGxsId id(group.mMeta.mGroupId);
    RsGxsIdGroupItem* item = new RsGxsIdGroupItem();

    item->fromGxsIdGroup(group,false) ;

#ifdef DEBUG_IDS
    std::cerr << "p3IdService::updateGroup() Updating RsGxsId: " << id;
    std::cerr << std::endl;
#endif

    RsGenExchange::updateGroup(token, item);

    // if its in the cache - clear it.
    {
        RsStackMutex stack(mIdMtx); /********** STACK LOCKED MTX ******/
        if (mKeyCache.erase(id))
        {
#ifdef DEBUG_IDS
            std::cerr << "p3IdService::updateGroup() Removed from PublicKeyCache";
            std::cerr << std::endl;
#endif
        }
        else
        {
#ifdef DEBUG_IDS
            std::cerr << "p3IdService::updateGroup() Not in PublicKeyCache";
            std::cerr << std::endl;
#endif
        }
    }

    return true;
}

bool 	p3IdService::deleteGroup(uint32_t& token, RsGxsIdGroup &group)
{
    RsGxsId id(group.mMeta.mGroupId);

#ifdef DEBUG_IDS
    std::cerr << "p3IdService::deleteGroup() Deleting RsGxsId: " << id;
    std::cerr << std::endl;
#endif

    RsGenExchange::deleteGroup(token,group.mMeta.mGroupId);

    // if its in the cache - clear it.
    {
        RsStackMutex stack(mIdMtx); /********** STACK LOCKED MTX ******/
        if (mKeyCache.erase(id))
        {
#ifdef DEBUG_IDS
            std::cerr << "p3IdService::deleteGroup() Removed from PublicKeyCache";
            std::cerr << std::endl;
#endif
        }
        else
        {
#ifdef DEBUG_IDS
            std::cerr << "p3IdService::deleteGroup() Not in PublicKeyCache";
            std::cerr << std::endl;
#endif
        }

        std::list<RsGxsId>::iterator lit = std::find( mOwnIds.begin(), mOwnIds.end(), id);
        if (lit != mOwnIds.end())
        {
            mOwnIds.remove((RsGxsId)*lit);
#ifdef DEBUG_IDS
            std::cerr << "p3IdService::deleteGroup() Removed from OwnIds";
            std::cerr << std::endl;
#endif
        }
        else
        {
#ifdef DEBUG_IDS
            std::cerr << "p3IdService::deleteGroup() Not in OwnIds";
            std::cerr << std::endl;
#endif
        }
    }

    return true;
}

/************************************************************************************/
/************************************************************************************/
/************************************************************************************/

/* Encoding / Decoding Group Service String stuff
 *
 * Pgp stuff.
 *
 * If flagged as pgp id....
 *   then we need to know if its been matched, or when we last tried to match.
 *
 */

bool SSGxsIdPgp::load(const std::string &input)
{
    char pgpline[RSGXSID_MAX_SERVICE_STRING];
    int timestamp = 0;
    uint32_t attempts = 0;
    if (1 == sscanf(input.c_str(), "K:1 I:%[^)]", pgpline))
    {
        validatedSignature = true;
        std::string str_line = pgpline;
        pgpId = RsPgpId(str_line);
        return true;
    }
    else if (3 == sscanf(input.c_str(), "K:0 T:%d C:%d I:%[^)]", &timestamp, &attempts,pgpline))
    {
        lastCheckTs = timestamp;
        checkAttempts = attempts;
        validatedSignature = false;
        std::string str_line = pgpline;
        pgpId = RsPgpId(str_line);
        return true;
    }
    else if (2 == sscanf(input.c_str(), "K:0 T:%d C:%d", &timestamp, &attempts))
    {
        lastCheckTs = timestamp;
        checkAttempts = attempts;
        validatedSignature = false;
        return true;
    }
    else if (1 == sscanf(input.c_str(), "K:0 T:%d", &timestamp))
    {
        lastCheckTs = timestamp;
        checkAttempts = 0;
        validatedSignature = false;
        return true;
    }
    else
    {
        lastCheckTs = 0;
        checkAttempts = 0;
        validatedSignature = false;
        return false;
    }
}

std::string SSGxsIdPgp::save() const
{
    std::string output;
    if (validatedSignature)
    {
        output += "K:1 I:";
        output += pgpId.toStdString();
    }
    else
    {
        rs_sprintf(output, "K:0 T:%d C:%d", lastCheckTs, checkAttempts);

        if(!pgpId.isNull())
            output += " I:"+pgpId.toStdString();
    }
    return output;
}


/* Encoding / Decoding Group Service String stuff
 *
 * RecognTags.
 */

bool SSGxsIdRecognTags::load(const std::string &input)
{
    //char pgpline[RSGXSID_MAX_SERVICE_STRING];
    int pubTs = 0;
    int lastTs = 0;
    uint32_t flags = 0;

    if (3 == sscanf(input.c_str(), "F:%u P:%d T:%d", &flags, &pubTs, &lastTs))
    {
        publishTs = pubTs;
        lastCheckTs = lastTs;
        tagFlags = flags;
    }
    else
    {
        return false;
    }
    return true;
}

std::string SSGxsIdRecognTags::save() const
{
    std::string output;
    rs_sprintf(output, "F:%u P:%d T:%d", tagFlags, publishTs, lastCheckTs);
    return output;
}

bool SSGxsIdRecognTags::tagsProcessed() const
{
    return (tagFlags & 0x1000);
}

bool SSGxsIdRecognTags::tagsPending() const
{
    return (tagFlags & 0x2000);
}

bool SSGxsIdRecognTags::tagValid(int i) const
{
    uint32_t idx = 0x01 << i;

#ifdef DEBUG_RECOGN
    std::cerr << "SSGxsIdRecognTags::tagValid(" << i << ") idx: " << idx;
    std::cerr << " result: " << (tagFlags & idx);
    std::cerr << std::endl;
#endif // DEBUG_RECOGN

    return (tagFlags & idx);
}

void SSGxsIdRecognTags::setTags(bool processed, bool pending, uint32_t flags)
{
    flags &= 0x00ff; // clear top bits;
    if (processed)
    {
        flags |= 0x1000;
    }
    if (pending)
    {
        flags |= 0x2000;
    }

#ifdef DEBUG_RECOGN
    std::cerr << "SSGxsIdRecognTags::setTags(" << processed << "," << pending << "," << flags << ")";
    std::cerr << " tagFlags: " << tagFlags;
    std::cerr << std::endl;
#endif // DEBUG_RECOGN

    tagFlags = flags;
}


GxsReputation::GxsReputation()
    :mOverallScore(0), mIdScore(0), mOwnOpinion(0), mPeerOpinion(0)
{
    updateIdScore(false, false);
    update();
}

static const int kIdReputationPgpKnownScore 	= 50;
static const int kIdReputationPgpUnknownScore 	= 20;
static const int kIdReputationAnonScore 	= 5;

bool GxsReputation::updateIdScore(bool pgpLinked, bool pgpKnown)
{
    if (pgpLinked)
    {
        if (pgpKnown)
        {
            mIdScore = kIdReputationPgpKnownScore;
        }
        else
        {
            mIdScore = kIdReputationPgpUnknownScore;
        }
    }
    else
    {
        mIdScore = kIdReputationAnonScore;
    }
    return true;
}

bool GxsReputation::update()
{
    mOverallScore = mIdScore + mOwnOpinion + mPeerOpinion;
    return true;
}


bool SSGxsIdReputation::load(const std::string &input)
{
    return (4 == sscanf(input.c_str(), "%d %d %d %d",
                        &(rep.mOverallScore), &(rep.mIdScore), &(rep.mOwnOpinion), &(rep.mPeerOpinion)));
}

std::string SSGxsIdReputation::save() const
{
    std::string output;
    rs_sprintf(output, "%d %d %d %d", rep.mOverallScore, rep.mIdScore, rep.mOwnOpinion, rep.mPeerOpinion);
    return output;
}


bool SSGxsIdCumulator::load(const std::string &input)
{
    return (4 == sscanf(input.c_str(), "%d %d %lf %lf", &count, &nullcount, &sum, &sumsq));
}

std::string SSGxsIdCumulator::save() const
{
    std::string output;
    rs_sprintf(output, "%d %d %lf %lf", count, nullcount, sum, sumsq);
    return output;
}

bool SSGxsIdGroup::load(const std::string &input)
{
    char pgpstr[RSGXSID_MAX_SERVICE_STRING];
    char recognstr[RSGXSID_MAX_SERVICE_STRING];
    char scorestr[RSGXSID_MAX_SERVICE_STRING];

    // split into parts.
    if (3 != sscanf(input.c_str(), "v2 {P:%[^}]} {T:%[^}]} {R:%[^}]}", pgpstr, recognstr, scorestr))
    {
#ifdef DEBUG_IDS
        std::cerr << "SSGxsIdGroup::load() Failed to extract 4 Parts";
        std::cerr << std::endl;
#endif // DEBUG_IDS
        return false;
    }

    bool ok = true;
    if (pgp.load(pgpstr))
    {
#ifdef DEBUG_IDS
        std::cerr << "SSGxsIdGroup::load() pgpstr: " << pgpstr;
        std::cerr << std::endl;
#endif // DEBUG_IDS
    }
    else
    {
#ifdef DEBUG_IDS
        std::cerr << "SSGxsIdGroup::load() Invalid pgpstr: " << pgpstr;
        std::cerr << std::endl;
#endif // DEBUG_IDS
        ok = false;
    }

    if (recogntags.load(recognstr))
    {
#ifdef DEBUG_RECOGN
        std::cerr << "SSGxsIdGroup::load() recognstr: " << recognstr;
        std::cerr << std::endl;
#endif // DEBUG_RECOGN
    }
    else
    {
#ifdef DEBUG_RECOGN
        std::cerr << "SSGxsIdGroup::load() Invalid recognstr: " << recognstr;
        std::cerr << std::endl;
#endif // DEBUG_RECOGN
        ok = false;
    }

    if (score.load(scorestr))
    {
#ifdef DEBUG_IDS
        std::cerr << "SSGxsIdGroup::load() scorestr: " << scorestr;
        std::cerr << std::endl;
#endif // DEBUG_IDS
    }
    else
    {
#ifdef DEBUG_IDS
        std::cerr << "SSGxsIdGroup::load() Invalid scorestr: " << scorestr;
        std::cerr << std::endl;
#endif // DEBUG_IDS
        ok = false;
    }

#if 0
    if (opinion.load(opinionstr))
    {
#ifdef DEBUG_IDS
        std::cerr << "SSGxsIdGroup::load() opinionstr: " << opinionstr;
        std::cerr << std::endl;
#endif // DEBUG_IDS
    }
    else
    {
#ifdef DEBUG_IDS
        std::cerr << "SSGxsIdGroup::load() Invalid opinionstr: " << opinionstr;
        std::cerr << std::endl;
#endif // DEBUG_IDS
        ok = false;
    }

    if (reputation.load(repstr))
    {
#ifdef DEBUG_IDS
        std::cerr << "SSGxsIdGroup::load() repstr: " << repstr;
        std::cerr << std::endl;
#endif // DEBUG_IDS
    }
    else
    {
#ifdef DEBUG_IDS
        std::cerr << "SSGxsIdGroup::load() Invalid repstr: " << repstr;
        std::cerr << std::endl;
#endif // DEBUG_IDS
        ok = false;
    }
#endif

#ifdef DEBUG_IDS
    std::cerr << "SSGxsIdGroup::load() regurgitated: " << save();
    std::cerr << std::endl;

    std::cerr << "SSGxsIdGroup::load() isOkay?: " << ok;
    std::cerr << std::endl;
#endif // DEBUG_IDS
    return ok;
}

std::string SSGxsIdGroup::save() const
{
    std::string output = "v2 ";

    output += "{P:";
    output += pgp.save();
    output += "}";

    output += "{T:";
    output += recogntags.save();
    output += "}";

    output += "{R:";
    output += score.save();
    output += "}";

    //std::cerr << "SSGxsIdGroup::save() output: " << output;
    //std::cerr << std::endl;

    return output;
}

/************************************************************************************/
/************************************************************************************/
/************************************************************************************/

/* Cache of recently used keys
 *
 * It is expensive to fetch the keys, so we want to keep them around if possible.
 * It only stores the immutable stuff.
 *
 * This is probably crude and crap to start with.
 * Want Least Recently Used (LRU) discard policy, without having to search whole cache.
 * Use two maps:
 *   - CacheMap[key] => data.
 *   - LRUMultiMap[AccessTS] => key
 *
 * NOTE: This could be moved to a seperate class and templated to make generic
 * as it might be generally useful.
 *
 */

RsGxsIdCache::RsGxsIdCache() {}

RsGxsIdCache::RsGxsIdCache(const RsGxsIdGroupItem *item, const RsTlvPublicRSAKey& in_pkey, const std::list<RsRecognTag> &tagList)
{
    init(item,in_pkey,RsTlvPrivateRSAKey(),tagList) ;
}

RsGxsIdCache::RsGxsIdCache(const RsGxsIdGroupItem *item, const RsTlvPublicRSAKey& in_pkey, const RsTlvPrivateRSAKey& privkey, const std::list<RsRecognTag> &tagList)
{
    init(item,in_pkey,privkey,tagList) ;
}

void RsGxsIdCache::init(const RsGxsIdGroupItem *item, const RsTlvPublicRSAKey& in_pub_key, const RsTlvPrivateRSAKey& in_priv_key,const std::list<RsRecognTag> &tagList)
{
    // Save Keys.
    pub_key = in_pub_key;
    priv_key = in_priv_key;

    // Save Time for ServiceString comparisions.
    mPublishTs = item->meta.mPublishTs;

    // Save RecognTags.
    mRecognTags = tagList;

    details.mAvatar.copy((uint8_t *) item->mImage.binData.bin_data, item->mImage.binData.bin_len);

    // Fill in Details.
    details.mNickname = item->meta.mGroupName;
    details.mId = RsGxsId(item->meta.mGroupId);

#ifdef DEBUG_IDS
    std::cerr << "RsGxsIdCache::RsGxsIdCache() for: " << details.mId;
    std::cerr << std::endl;
#endif // DEBUG_IDS

    details.mFlags = 0 ;

    if(item->meta.mSubscribeFlags & GXS_SERV::GROUP_SUBSCRIBE_ADMIN)		details.mFlags |= RS_IDENTITY_FLAGS_IS_OWN_ID;
    if(item->meta.mGroupFlags     & RSGXSID_GROUPFLAG_REALID_kept_for_compatibility)			details.mFlags |= RS_IDENTITY_FLAGS_PGP_LINKED;

    // do some tests
    if(details.mFlags & RS_IDENTITY_FLAGS_IS_OWN_ID)
    {
        if(!priv_key.checkKey())
            std::cerr << "(EE) Private key missing for own identity " << pub_key.keyId << std::endl;

    }
    if(!pub_key.checkKey())
        std::cerr << "(EE) Public key missing for identity " << pub_key.keyId << std::endl;

    if(!GxsSecurity::checkFingerprint(pub_key))
        details.mFlags |= RS_IDENTITY_FLAGS_IS_DEPRECATED;

    /* rest must be retrived from ServiceString */
    updateServiceString(item->meta.mServiceString);
}

void RsGxsIdCache::updateServiceString(std::string serviceString)
{
    details.mRecognTags.clear();

    SSGxsIdGroup ssdata;
    if (ssdata.load(serviceString))
    {
        if (details.mFlags & RS_IDENTITY_FLAGS_PGP_LINKED)
        {
            if(ssdata.pgp.validatedSignature) details.mFlags |= RS_IDENTITY_FLAGS_PGP_KNOWN ;

            if (details.mFlags & RS_IDENTITY_FLAGS_PGP_LINKED)
                details.mPgpId = ssdata.pgp.pgpId;
            else
                details.mPgpId.clear();
        }


        // process RecognTags.
        if (ssdata.recogntags.tagsProcessed())
        {
#ifdef DEBUG_RECOGN
            std::cerr << "RsGxsIdCache::updateServiceString() updating recogntags";
            std::cerr << std::endl;
#endif // DEBUG_RECOGN
            if (ssdata.recogntags.publishTs == mPublishTs)
            {
                std::list<RsRecognTag>::iterator it;
                int i = 0;
                for(it = mRecognTags.begin(); it != mRecognTags.end(); ++it, i++)
                {
                    if (ssdata.recogntags.tagValid(i) && it->valid)
                    {
#ifdef DEBUG_RECOGN
                        std::cerr << "RsGxsIdCache::updateServiceString() Valid Tag: " << it->tag_class << ":" << it->tag_type;
                        std::cerr << std::endl;
#endif // DEBUG_RECOGN
                        details.mRecognTags.push_back(*it);
                    }
                    else
                    {
#ifdef DEBUG_RECOGN
                        std::cerr << "RsGxsIdCache::updateServiceString() Invalid Tag: " << it->tag_class << ":" << it->tag_type;
                        std::cerr << std::endl;
#endif // DEBUG_RECOGN
                    }
                }
            }
            else
            {
#ifdef DEBUG_RECOGN
                std::cerr << "RsGxsIdCache::updateServiceString() recogntags old publishTs";
                std::cerr << std::endl;
#endif // DEBUG_RECOGN
            }

        }
        else
        {
#ifdef DEBUG_RECOGN
            std::cerr << "RsGxsIdCache::updateServiceString() recogntags unprocessed";
            std::cerr << std::endl;
#endif // DEBUG_RECOGN
        }

        // copy over Reputation scores.
        //details.mReputation = ssdata.score.rep;
    }
    else
    {
        details.mFlags &= ~RS_IDENTITY_FLAGS_PGP_KNOWN ;
        details.mPgpId.clear();
        //details.mReputation.updateIdScore(false, false);
        //details.mReputation.update();
    }
}


bool p3IdService::recogn_extract_taginfo(const RsGxsIdGroupItem *item, std::list<RsGxsRecognTagItem *> &tagItems)
{
#ifdef DEBUG_RECOGN
    std::cerr << "p3IdService::recogn_extract_taginfo()";
    std::cerr << std::endl;
#endif // DEBUG_RECOGN

    /* process Recogn Tags */

    std::list<std::string>::const_iterator rit;
    int count = 0;
    for(rit = item->mRecognTags.begin(); rit != item->mRecognTags.end(); ++rit)
    {
        if (++count > RSRECOGN_MAX_TAGINFO)
        {
#ifdef DEBUG_RECOGN
            std::cerr << "p3IdService::recogn_extract_taginfo() Too many tags.";
            std::cerr << std::endl;
#endif // DEBUG_RECOGN

            return true;
        }

        RsGxsRecognTagItem *tagitem = RsRecogn::extractTag(*rit);

        if (!tagitem)
        {
            continue;
        }

#ifdef DEBUG_RECOGN
        std::cerr << "p3IdService::recogn_extract_taginfo() Got TagItem: ";
        std::cerr << std::endl;
        tagitem->print(std::cerr);
#endif // DEBUG_RECOGN

        tagItems.push_back(tagitem);
    }
    return true;
}


bool p3IdService::cache_process_recogntaginfo(const RsGxsIdGroupItem *item, std::list<RsRecognTag> &tagList)
{
    /* ServiceString decode */
    SSGxsIdGroup ssdata;
    bool recognProcess = false;
    if (ssdata.load(item->meta.mServiceString))
    {
        if (!ssdata.recogntags.tagsProcessed())
        {
#ifdef DEBUG_RECOGN
            std::cerr << "p3IdService::cache_process_recogntaginfo() tags not processed";
            std::cerr << std::endl;
#endif // DEBUG_RECOGN

            /* we need to reprocess it */
            recognProcess = true;
        }
        else
        {
            if (item->meta.mPublishTs != ssdata.recogntags.publishTs)
            {
#ifdef DEBUG_RECOGN
                std::cerr << "p3IdService::cache_process_recogntaginfo() publishTs old";
                std::cerr << std::endl;
#endif // DEBUG_RECOGN
                recognProcess = true;
            }
            else if (ssdata.recogntags.tagsPending())
            {
#ifdef DEBUG_RECOGN
                std::cerr << "p3IdService::cache_process_recogntaginfo() tagsPending";
                std::cerr << std::endl;
#endif // DEBUG_RECOGN
                /* reprocess once a day */
                recognProcess = true;
            }
        }
    }
    else
    {
#ifdef DEBUG_RECOGN
        std::cerr << "p3IdService::cache_process_recogntaginfo() ServiceString invalid";
        std::cerr << std::endl;
#endif // DEBUG_RECOGN
        recognProcess = true;
    }

    std::list<RsGxsRecognTagItem *> tagItems;
    std::list<RsGxsRecognTagItem *>::iterator it;

    recogn_extract_taginfo(item, tagItems);

    //time_t now = time(NULL);
    for(it = tagItems.begin(); it != tagItems.end(); ++it)
    {
        RsRecognTag info((*it)->tag_class, (*it)->tag_type, false);
        bool isPending = false;
        if (recogn_checktag(RsGxsId(item->meta.mGroupId.toStdString()), item->meta.mGroupName, *it, false, isPending))
        {
            info.valid = true;
        }
#ifdef DEBUG_RECOGN
        std::cerr << "p3IdService::cache_process_recogntaginfo() Adding Tag: ";
        std::cerr << info.tag_class << ":";
        std::cerr << info.tag_type << ":";
        std::cerr << info.valid;
        std::cerr << std::endl;
#endif // DEBUG_RECOGN

        tagList.push_back(info);
        delete *it;
    }


    if (recognProcess)
    {
        RsStackMutex stack(mIdMtx); /********** STACK LOCKED MTX ******/
        mRecognGroupIds.push_back(item->meta.mGroupId);

#ifdef DEBUG_RECOGN
        std::cerr << "p3IdService::cache_process_recogntaginfo() Reprocessing groupId: ";
        std::cerr << item->meta.mGroupId;
        std::cerr << std::endl;
#endif // DEBUG_RECOGN

        recogn_schedule();
    }

    return true;
}

// Loads in the cache the group data from the given group item, retrieved from sqlite storage.

bool p3IdService::cache_store(const RsGxsIdGroupItem *item)
{
#ifdef DEBUG_IDS
    std::cerr << "p3IdService::cache_store() Item: " << item->meta.mGroupId;
    std::cerr << std::endl;
#endif // DEBUG_IDS
    //item->print(std::cerr, 0); NEEDS CONST!!!! TODO
    //std::cerr << std::endl;

    /* extract key from keys */
    RsTlvSecurityKeySet keySet;

    RsTlvPublicRSAKey   pubkey;
    RsTlvPrivateRSAKey  fullkey;

    bool pub_key_ok = false;
    bool full_key_ok = false;

    RsGxsId id (item->meta.mGroupId.toStdString());

    if (!getGroupKeys(RsGxsGroupId(id.toStdString()), keySet))
    {
        std::cerr << "p3IdService::cache_store() ERROR getting GroupKeys for: "<< item->meta.mGroupId << std::endl;
        return false;
    }

    for (std::map<RsGxsId, RsTlvPrivateRSAKey>::iterator kit = keySet.private_keys.begin(); kit != keySet.private_keys.end(); ++kit)
        if (kit->second.keyFlags & RSTLV_KEY_DISTRIB_ADMIN)
        {
#ifdef DEBUG_IDS
            std::cerr << "p3IdService::cache_store() Found Admin Key" << std::endl;
#endif 
            fullkey = kit->second;
            full_key_ok = true;
        }
    for (std::map<RsGxsId, RsTlvPublicRSAKey>::iterator kit = keySet.public_keys.begin(); kit != keySet.public_keys.end(); ++kit)
        if (kit->second.keyFlags & RSTLV_KEY_DISTRIB_ADMIN)
        {
#ifdef DEBUG_IDS
            std::cerr << "p3IdService::cache_store() Found Admin public Key" << std::endl;
#endif
            pubkey = kit->second;
            pub_key_ok = true ;
        }

    assert(!( pubkey.keyFlags & RSTLV_KEY_TYPE_FULL)) ;
    assert(!full_key_ok || (fullkey.keyFlags & RSTLV_KEY_TYPE_FULL)) ;

    if (!pub_key_ok)
    {
        std::cerr << "p3IdService::cache_store() ERROR No Public Key Found";
        std::cerr << std::endl;
        return false;
    }

    // extract tags.
    std::list<RsRecognTag> tagList;
    cache_process_recogntaginfo(item, tagList);

    RsStackMutex stack(mIdMtx); /********** STACK LOCKED MTX ******/

    // Create Cache Data.
    RsGxsIdCache keycache(item, pubkey, fullkey,tagList);

    if(mContacts.find(id) != mContacts.end())
        keycache.details.mFlags |= RS_IDENTITY_FLAGS_IS_A_CONTACT;

    mKeyCache.store(id, keycache);
    mKeyCache.resize();

    return true;
}



/***** BELOW LOADS THE CACHE FROM GXS DATASTORE *****/

#define MIN_CYCLE_GAP	2

bool p3IdService::cache_request_load(const RsGxsId &id, const std::list<RsPeerId> &peers)
{
#ifdef DEBUG_IDS
    std::cerr << "p3IdService::cache_request_load(" << id << ")" << std::endl;
#endif // DEBUG_IDS

    {
        RsStackMutex stack(mIdMtx); /********** STACK LOCKED MTX ******/

        mergeIds(mCacheLoad_ToCache,id,peers) ;	// merge, even if peers is empty
    }

    if (RsTickEvent::event_count(GXSID_EVENT_CACHELOAD) > 0)
    {
        /* its already scheduled */
        return true;
    }

    int32_t age = 0;
    if (RsTickEvent::prev_event_ago(GXSID_EVENT_CACHELOAD, age))
    {
        if (age < MIN_CYCLE_GAP)
        {
            RsTickEvent::schedule_in(GXSID_EVENT_CACHELOAD, MIN_CYCLE_GAP - age);
            return true;
        }
    }

    RsTickEvent::schedule_now(GXSID_EVENT_CACHELOAD);
    return true;
}


bool p3IdService::cache_start_load()
{
    /* trigger request to load missing ids into cache */
    std::list<RsGxsGroupId> groupIds;
    {
        RsStackMutex stack(mIdMtx); /********** STACK LOCKED MTX ******/

        /* now we process the modGroupList -> a map so we can use it easily later, and create id list too */
        std::map<RsGxsId, std::list<RsPeerId> >::iterator it;
        for(it = mCacheLoad_ToCache.begin(); it != mCacheLoad_ToCache.end(); ++it)
        {
#ifdef DEBUG_IDS
            std::cerr << "p3IdService::cache_start_load() GroupId: " << it->first;
            std::cerr << std::endl;
#endif // DEBUG_IDS
            groupIds.push_back(RsGxsGroupId(it->first)); // might need conversion?
        }

        for(std::map<RsGxsId,std::list<RsPeerId> >::const_iterator it(mCacheLoad_ToCache.begin());it!=mCacheLoad_ToCache.end();++it)
            mergeIds(mPendingCache,it->first,it->second) ;

        mCacheLoad_ToCache.clear();
    }

    if (groupIds.size() > 0)
    {
#ifdef DEBUG_IDS
        std::cerr << "p3IdService::cache_start_load() #Groups: " << groupIds.size();
        std::cerr << std::endl;
#endif // DEBUG_IDS

        uint32_t ansType = RS_TOKREQ_ANSTYPE_DATA;
        RsTokReqOptions opts;
        opts.mReqType = GXS_REQUEST_TYPE_GROUP_DATA;
        uint32_t token = 0;

        RsGenExchange::getTokenService()->requestGroupInfo(token, ansType, opts, groupIds);
        GxsTokenQueue::queueRequest(token, GXSIDREQ_CACHELOAD);
    }
    return 1;
}


bool p3IdService::cache_load_for_token(uint32_t token)
{
#ifdef DEBUG_IDS
    std::cerr << "p3IdService::cache_load_for_token() : " << token;
    std::cerr << std::endl;
#endif // DEBUG_IDS

    std::vector<RsGxsGrpItem*> grpData;
    bool ok = RsGenExchange::getGroupData(token, grpData);

    if(ok)
    {
        std::vector<RsGxsGrpItem*>::iterator vit = grpData.begin();

        for(; vit != grpData.end(); ++vit)
        {
            RsGxsIdGroupItem* item = dynamic_cast<RsGxsIdGroupItem*>(*vit);
            if (!item)
            {
                std::cerr << "Not a RsGxsIdGroupItem Item, deleting!" << std::endl;
                delete(*vit);
                continue;
            }

#ifdef DEBUG_IDS
            std::cerr << "p3IdService::cache_load_for_token() Loaded Id with Meta: ";
            std::cerr << item->meta;
            std::cerr << std::endl;
#endif // DEBUG_IDS


            {
                // remove identities that are present
                RsStackMutex stack(mIdMtx);
                mPendingCache.erase(RsGxsId(item->meta.mGroupId.toStdString()));
            }

            /* cache the data */
            cache_store(item);
            delete item;
        }

        {
            // now store identities that aren't present

            RsStackMutex stack(mIdMtx);

            // No need to merge empty peers since the request would fail.

            for(std::map<RsGxsId,std::list<RsPeerId> >::const_iterator itt(mPendingCache.begin());itt!=mPendingCache.end();++itt)
                if(!itt->second.empty())
                    mergeIds(mIdsNotPresent,itt->first,itt->second) ;
#ifdef DEBUG_IDS
				else
                    std::cerr << "(WW) empty list of peers to request ID " << itt->first << ": cannot request" << std::endl;
#endif


			mPendingCache.clear();

            if(!mIdsNotPresent.empty())
                schedule_now(GXSID_EVENT_REQUEST_IDS);
		}

	}
	else
	{
		std::cerr << "p3IdService::cache_load_for_token() ERROR no data";
		std::cerr << std::endl;

		return false;
	}
	return true;
}

void p3IdService::requestIdsFromNet()
{
    RsStackMutex stack(mIdMtx);

    if(!mNes)
    {
        std::cerr << "(WW) cannot request missing GXS IDs because network service is not present." << std::endl;
        return ;
    }

    std::map<RsGxsId, std::list<RsPeerId> >::iterator cit;
    std::map<RsPeerId, std::list<RsGxsId> > requests;

    // Transform to appropriate structure (<peer, std::list<RsGxsId> > map) to make request to nes per peer ID
    // Only delete entries in mIdsNotPresent that can actually be performed.

    for(cit = mIdsNotPresent.begin(); cit != mIdsNotPresent.end();)
    {
#ifdef DEBUG_IDS
        std::cerr << "p3IdService::requestIdsFromNet() Id not found, deferring for net request: " << cit->first << std::endl;
#endif

        const std::list<RsPeerId>& peers = cit->second;
        std::list<RsPeerId>::const_iterator cit2;

        bool request_can_proceed = false ;

        for(cit2 = peers.begin(); cit2 != peers.end(); ++cit2)
            if(rsPeers->isOnline(*cit2)) 		// make sure that the peer in online, so that we know that the request has some chance to succeed.
            {
                requests[*cit2].push_back(cit->first);
                request_can_proceed = true ;
#ifdef DEBUG_IDS
                std::cerr << "       will ask ID " << cit->first << " to peer ID " << *cit2 << std::endl;
#endif
            }

        if(request_can_proceed || peers.empty())
        {
            std::map<RsGxsId, std::list<RsPeerId> >::iterator tmp(cit);
            ++tmp ;
            mIdsNotPresent.erase(cit) ;
            cit = tmp ;
        }
        else
        {
#ifdef DEBUG_IDS
            std::cerr << "(EE) no online peers among supply list in ID request for groupId " << cit->first << ". Keeping it until peers show up."<< std::endl;
#endif
            ++cit ;
        }
    }

    for(std::map<RsPeerId, std::list<RsGxsId> >::const_iterator cit2(requests.begin()); cit2 != requests.end(); ++cit2)
    {
        std::list<RsGxsId>::const_iterator gxs_id_it = cit2->second.begin();
        std::list<RsGxsGroupId> grpIds;
        for(; gxs_id_it != cit2->second.end(); ++gxs_id_it)
        {
#ifdef DEBUG_IDS
            std::cerr << "  asking ID " << *gxs_id_it << " to peer ID " << cit2->first << std::endl;
#endif
            grpIds.push_back(RsGxsGroupId(*gxs_id_it));
        }

        mNes->requestGrp(grpIds, cit2->first);
    }
}

bool p3IdService::cache_update_if_cached(const RsGxsId &id, std::string serviceString)
{
	/* if these entries are cached - update with new info */
#ifdef DEBUG_IDS
	std::cerr << "p3IdService::cache_update_if_cached(" << id << ")";
	std::cerr << std::endl;
#endif // DEBUG_IDS

	/* retrieve - update, save */
	RsStackMutex stack(mIdMtx); /********** STACK LOCKED MTX ******/

	RsGxsIdCache updated_data;
    
	if(mKeyCache.fetch(id, updated_data))
	{
#ifdef DEBUG_IDS
		std::cerr << "p3IdService::cache_update_if_cached() Updating Public Cache";
		std::cerr << std::endl;
#endif // DEBUG_IDS

		updated_data.updateServiceString(serviceString);
        
		mKeyCache.store(id, updated_data);
	}

	return true;
}

/************************************************************************************/
/************************************************************************************/

bool p3IdService::cache_request_ownids()
{
	/* trigger request to load missing ids into cache */
	std::list<RsGxsGroupId> groupIds;
#ifdef DEBUG_IDS
	std::cerr << "p3IdService::cache_request_ownids()";
	std::cerr << std::endl;
#endif // DEBUG_IDS

	uint32_t ansType = RS_TOKREQ_ANSTYPE_DATA; 
	RsTokReqOptions opts;
	opts.mReqType = GXS_REQUEST_TYPE_GROUP_DATA;
	//opts.mSubscribeFlags = GXS_SERV::GROUP_SUBSCRIBE_ADMIN;

	uint32_t token = 0;
	
	RsGenExchange::getTokenService()->requestGroupInfo(token, ansType, opts);
	GxsTokenQueue::queueRequest(token, GXSIDREQ_CACHEOWNIDS);	
	return 1;
}


bool p3IdService::cache_load_ownids(uint32_t token)
{
#ifdef DEBUG_IDS
	std::cerr << "p3IdService::cache_load_ownids() : " << token;
	std::cerr << std::endl;
#endif // DEBUG_IDS

	std::vector<RsGxsGrpItem*> grpData;
	bool ok = RsGenExchange::getGroupData(token, grpData);

	if(ok)
	{
		std::vector<RsGxsGrpItem*>::iterator vit = grpData.begin();

		// Save List
		{
			RsStackMutex stack(mIdMtx); /********** STACK LOCKED MTX ******/

			mOwnIds.clear();
			for(vit = grpData.begin(); vit != grpData.end(); ++vit)
			{
				RsGxsIdGroupItem* item = dynamic_cast<RsGxsIdGroupItem*>(*vit);
				if (!item)
				{
					std::cerr << "Not a IdOpinion Item, deleting!" << std::endl;
					delete(*vit);
					continue;
				}

				if (item->meta.mSubscribeFlags & GXS_SERV::GROUP_SUBSCRIBE_ADMIN)
				{
                    mOwnIds.push_back(RsGxsId(item->meta.mGroupId));

                    // This prevents automatic deletion to get rid of them.
                    // In other words, own ids are always used.

                    mKeysTS[RsGxsId(item->meta.mGroupId)].TS = time(NULL) ;
				}
				delete item ;
            }
            mOwnIdsLoaded = true ;
		}

		// No need to cache these items...
		// as it just causes the cache to be flushed.
#if 0
		// Cache Items too.
		for(vit = grpData.begin(); vit != grpData.end(); ++vit)
		{
			RsGxsIdGroupItem* item = dynamic_cast<RsGxsIdGroupItem*>(*vit);
			if (item->meta.mSubscribeFlags & GXS_SERV::GROUP_SUBSCRIBE_ADMIN)
			{

				std::cerr << "p3IdService::cache_load_ownids() Loaded Id with Meta: ";
				std::cerr << item->meta;
				std::cerr << std::endl;

				/* cache the data */
				cache_store(item);
			}
			delete item;
		}
#endif

	}
	else
	{
		std::cerr << "p3IdService::cache_load_ownids() ERROR no data";
		std::cerr << std::endl;

		return false;
	}
	return true;
}

/************************************************************************************/
/************************************************************************************/

bool p3IdService::cachetest_getlist()
{
#ifdef DEBUG_IDS
	std::cerr << "p3IdService::cachetest_getlist() making request";
	std::cerr << std::endl;
#endif // DEBUG_IDS

	uint32_t ansType = RS_TOKREQ_ANSTYPE_LIST; 
	RsTokReqOptions opts;
	opts.mReqType = GXS_REQUEST_TYPE_GROUP_IDS;
	uint32_t token = 0;
	
	RsGenExchange::getTokenService()->requestGroupInfo(token, ansType, opts);
	GxsTokenQueue::queueRequest(token, GXSIDREQ_CACHETEST);	

	// Schedule Next Event.
	RsTickEvent::schedule_in(GXSID_EVENT_CACHETEST, CACHETEST_PERIOD);
	return true;
}

bool p3IdService::cachetest_handlerequest(uint32_t token)
{
#ifdef DEBUG_IDS
	std::cerr << "p3IdService::cachetest_handlerequest() token: " << token;
	std::cerr << std::endl;
#endif // DEBUG_IDS

       	std::list<RsGxsId> grpIds;
       	std::list<RsGxsGroupId> grpIdsC;
       	bool ok = RsGenExchange::getGroupList(token, grpIdsC);

       	std::list<RsGxsGroupId>::const_iterator cit = grpIdsC.begin();
       	for(; cit != grpIdsC.end(); ++cit)
       		grpIds.push_back(RsGxsId(cit->toStdString()));

	if(ok)
	{
		std::list<RsGxsId>::iterator vit = grpIds.begin();
		for(; vit != grpIds.end(); ++vit)
		{
			/* 5% chance of checking it! */
			if (RSRandom::random_f32() < 0.25)
			{
#ifdef DEBUG_IDS
				std::cerr << "p3IdService::cachetest_request() Testing Id: " << *vit;
				std::cerr << std::endl;
#endif // DEBUG_IDS

				/* try the cache! */
				if (!haveKey(*vit))
				{
                    std::list<RsPeerId> nullpeers;
					requestKey(*vit, nullpeers,RsIdentityUsage(serviceType(),RsIdentityUsage::UNKNOWN_USAGE));

#ifdef DEBUG_IDS
					std::cerr << "p3IdService::cachetest_request() Requested Key Id: " << *vit;
					std::cerr << std::endl;
#endif // DEBUG_IDS
				}
				else
				{
					RsTlvPublicRSAKey seckey;
                    if (getKey(*vit, seckey))
					{
#ifdef DEBUG_IDS
						std::cerr << "p3IdService::cachetest_request() Got Key OK Id: " << *vit;
						std::cerr << std::endl;
#endif // DEBUG_IDS

						// success!
							seckey.print(std::cerr, 10);
						std::cerr << std::endl;


					}
					else
					{
						std::cerr << "p3IdService::cachetest_request() ERROR no Key for Id: " << *vit;
						std::cerr << std::endl;
					}
				}

				/* try private key too! */
				if (!havePrivateKey(*vit))
				{
					requestPrivateKey(*vit);
#ifdef DEBUG_IDS
					std::cerr << "p3IdService::cachetest_request() Requested PrivateKey Id: " << *vit;
					std::cerr << std::endl;
#endif // DEBUG_IDS
				}
				else
				{
					RsTlvPrivateRSAKey seckey;
					if (getPrivateKey(*vit, seckey))
					{
						// success!
#ifdef DEBUG_IDS
						std::cerr << "p3IdService::cachetest_request() Got PrivateKey OK Id: " << *vit;
						std::cerr << std::endl;
#endif // DEBUG_IDS
					}
					else
					{
						std::cerr << "p3IdService::cachetest_request() ERROR no PrivateKey for Id: " << *vit;
						std::cerr << std::endl;
					}
				}
			}
		}
	}
	else
	{
		std::cerr << "p3IdService::cache_load_for_token() ERROR no data";
		std::cerr << std::endl;

		return false;
	}
	return true;
}

/************************************************************************************/
/************************************************************************************/
/************************************************************************************/

/*
 * We have three background tasks that use the ServiceString: PGPHash & Reputation & Recogn
 *
 * Only one task can be run at a time - otherwise potential overwrite issues.
 * So this part coordinates that part of the code.
 *
 * We are going to have a "fetcher task", which gets all the UNPROCESSED / UPDATED GROUPS.
 * and sets the CHECK_PGP, CHECK_RECOGN, etc... this will reduce the "Get All" calls.
 *
 */


bool	p3IdService::CacheArbitration(uint32_t mode)
{
	RsStackMutex stack(mIdMtx); /********** STACK LOCKED MTX ******/

	if (!mBgSchedule_Active)
	{
#ifdef DEBUG_IDS
		std::cerr << "p3IdService::CacheArbitration() Okay: mode " << mode;
		std::cerr << std::endl;
#endif // DEBUG_IDS

		mBgSchedule_Active = true;
		mBgSchedule_Mode = mode;
		return true;
	}

#ifdef DEBUG_IDS
	std::cerr << "p3IdService::CacheArbitration() Is busy in mode: " << mBgSchedule_Mode;
	std::cerr << std::endl;
#endif // DEBUG_IDS

	return false;
}

void	p3IdService::CacheArbitrationDone(uint32_t mode)
{
	RsStackMutex stack(mIdMtx); /********** STACK LOCKED MTX ******/

	if (mBgSchedule_Mode != mode)
	{
		/* issues */
		std::cerr << "p3IdService::CacheArbitrationDone() ERROR Wrong Current Mode";
		std::cerr << std::endl;
		return;
	}

#ifdef DEBUG_IDS
	std::cerr << "p3IdService::CacheArbitrationDone()";
	std::cerr << std::endl;
#endif // DEBUG_IDS

	mBgSchedule_Active = false;
}

/************************************************************************************/
/************************************************************************************/
/************************************************************************************/

/* Task to determine GPGHash matches
 *
 * Info to be stored in GroupServiceString + Cache.
 *
 * Actually - it must be a Signature here - otherwise, you could 
 * put in a hash from someone else!
 *
 * Don't think that we need to match very often - maybe once a day?
 * Actually - we should scale the matching based on number of keys we have.
 *
 * imagine - 10^6 rsa keys + 10^3 gpg keys  => 10^9 combinations.
 *  -- far too many to check all quickly.
 *
 * Need to grab and cache data we need... then check over slowly.
 *
 * maybe grab a list of all gpgids - that we know of: store id list.
 * then big GroupRequest, and iterate through these.
 **/

//const int SHA_DIGEST_LENGTH = 20;

static void calcPGPHash(const RsGxsId &id, const PGPFingerprintType &pgp, Sha1CheckSum &hash);


// Must Use meta.
RsGenExchange::ServiceCreate_Return p3IdService::service_CreateGroup(RsGxsGrpItem* grpItem, RsTlvSecurityKeySet& keySet)
{

#ifdef DEBUG_IDS
    std::cerr << "p3IdService::service_CreateGroup()";
    std::cerr << std::endl;
#endif // DEBUG_IDS

    RsGxsIdGroupItem *item = dynamic_cast<RsGxsIdGroupItem *>(grpItem);
    if (!item)
    {
        std::cerr << "p3IdService::service_CreateGroup() ERROR invalid cast";
        std::cerr << std::endl;
        return SERVICE_CREATE_FAIL;
    }

#ifdef DEBUG_IDS
    std::cerr << "p3IdService::service_CreateGroup() Item is:";
    std::cerr << std::endl;
    item->print(std::cerr);
    std::cerr << std::endl;
#endif // DEBUG_IDS
    
    item->meta.mGroupId.clear();

    /********************* TEMP HACK UNTIL GXS FILLS IN GROUP_ID *****************/
    // find private admin key
    for(std::map<RsGxsId, RsTlvPrivateRSAKey>::iterator mit = keySet.private_keys.begin();mit != keySet.private_keys.end(); ++mit)
        if(mit->second.keyFlags == (RSTLV_KEY_DISTRIB_ADMIN | RSTLV_KEY_TYPE_FULL))
        {
            item->meta.mGroupId = RsGxsGroupId(mit->second.keyId);
            break;
        }

    if(item->meta.mGroupId.isNull())
    {
        std::cerr << "p3IdService::service_CreateGroup() ERROR no admin key";
        std::cerr << std::endl;
        return SERVICE_CREATE_FAIL;
    }
    mKeysTS[RsGxsId(item->meta.mGroupId)].TS = time(NULL) ;

    /********************* TEMP HACK UNTIL GXS FILLS IN GROUP_ID *****************/

    // SANITY CHECK.
    //    if (item->mMeta.mAuthorId != item->meta.mAuthorId)
    //	{
    //		std::cerr << "p3IdService::service_CreateGroup() AuthorId mismatch(";
    //        std::cerr << item->mMeta.mAuthorId;
    //		std::cerr << " vs ";
    //		std::cerr << item->meta.mAuthorId;
    //		std::cerr << std::endl;
    //	}
    //
    //	if (item->group.mMeta.mGroupId != item->meta.mGroupId)
    //	{
    //		std::cerr << "p3IdService::service_CreateGroup() GroupId mismatch(";
    //        std::cerr << item->mMeta.mGroupId;
    //		std::cerr << " vs ";
    //		std::cerr << item->meta.mGroupId;
    //		std::cerr << std::endl;
    //	}
    //
    //
    //	if (item->group.mMeta.mGroupFlags != item->meta.mGroupFlags)
    //	{
    //		std::cerr << "p3IdService::service_CreateGroup() GroupFlags mismatch(";
    //        std::cerr << item->mMeta.mGroupFlags;
    //		std::cerr << " vs ";
    //		std::cerr << item->meta.mGroupFlags;
    //		std::cerr << std::endl;
    //	}

#ifdef DEBUG_IDS
    std::cerr << "p3IdService::service_CreateGroup() for : " << item->meta.mGroupId;
    std::cerr << std::endl;
    std::cerr << "p3IdService::service_CreateGroup() Alt GroupId : " << item->meta.mGroupId;
    std::cerr << std::endl;
#endif // DEBUG_IDS

    ServiceCreate_Return createStatus;

    if (item->meta.mGroupFlags & RSGXSID_GROUPFLAG_REALID_kept_for_compatibility)
    {
        /* create the hash */
        Sha1CheckSum hash;

        /* */
        PGPFingerprintType ownFinger;
        RsPgpId ownId(mPgpUtils->getPGPOwnId());

#ifdef DEBUG_IDS
        std::cerr << "p3IdService::service_CreateGroup() OwnPgpID: " << ownId.toStdString();
        std::cerr << std::endl;
#endif

#ifdef GXSID_GEN_DUMMY_DATA
        //		if (item->group.mMeta.mAuthorId != "")
        //		{
        //			ownId = RsPgpId(item->group.mMeta.mAuthorId);
        //		}
#endif

        if (!mPgpUtils->getKeyFingerprint(ownId,ownFinger))
        {
            std::cerr << "p3IdService::service_CreateGroup() ERROR Own Finger is stuck";
            std::cerr << std::endl;
            return SERVICE_CREATE_FAIL; // abandon attempt!
        }

#ifdef DEBUG_IDS
        std::cerr << "p3IdService::service_CreateGroup() OwnFingerprint: " << ownFinger.toStdString();
        std::cerr << std::endl;
#endif

        RsGxsId gxsId(item->meta.mGroupId.toStdString());
        calcPGPHash(gxsId, ownFinger, hash);
        item->mPgpIdHash = hash;

#ifdef DEBUG_IDS

        std::cerr << "p3IdService::service_CreateGroup() Calculated PgpIdHash : " << item->mPgpIdHash;
        std::cerr << std::endl;
#endif // DEBUG_IDS

        /* do signature */


#define MAX_SIGN_SIZE 2048
        uint8_t signarray[MAX_SIGN_SIZE];
        unsigned int sign_size = MAX_SIGN_SIZE;
        int result ;

        memset(signarray,0,MAX_SIGN_SIZE) ;	// just in case.

        mPgpUtils->askForDeferredSelfSignature((void *) hash.toByteArray(), hash.SIZE_IN_BYTES, signarray, &sign_size,result, "p3IdService::service_CreateGroup()") ;

    /* error */
    switch(result)
    {
    case SELF_SIGNATURE_RESULT_PENDING : createStatus = SERVICE_CREATE_FAIL_TRY_LATER;
        std::cerr << "p3IdService::service_CreateGroup() signature still pending" << std::endl;
        break ;
    default:
    case SELF_SIGNATURE_RESULT_FAILED:  return SERVICE_CREATE_FAIL ;
        std::cerr << "p3IdService::service_CreateGroup() signature failed" << std::endl;
        break ;

    case SELF_SIGNATURE_RESULT_SUCCESS:
    {
        // Additional consistency checks.

        if(sign_size == MAX_SIGN_SIZE)
        {
            std::cerr << "Inconsistent result. Signature uses full buffer. This is probably an error." << std::endl;
            return SERVICE_CREATE_FAIL; // abandon attempt!
        }
#ifdef DEBUG_IDS
        std::cerr << "p3IdService::service_CreateGroup() Signature: ";
        std::string strout;
#endif
        /* push binary into string -> really bad! */
        item->mPgpIdSign = "";
        for(unsigned int i = 0; i < sign_size; i++)
        {
#ifdef DEBUG_IDS
            rs_sprintf_append(strout, "%02x", (uint32_t) signarray[i]);
#endif
            item->mPgpIdSign += signarray[i];
        }
        createStatus = SERVICE_CREATE_SUCCESS;

#ifdef DEBUG_IDS
        std::cerr << strout;
        std::cerr << std::endl;
#endif
    }
    }
        /* done! */
    }
    else
    {
        createStatus = SERVICE_CREATE_SUCCESS;
    }

    // Enforce no AuthorId.
    item->meta.mAuthorId.clear() ;
    //item->mMeta.mAuthorId.clear() ;
    // copy meta data to be sure its all the same.
    //item->group.mMeta = item->meta;

    // do it like p3gxscircles: save the new grp id
    // this allows the user interface
    // to see the grp id on the list of ownIds immediately after the group was created
    {
        RsStackMutex stack(mIdMtx);
        RsGxsId gxsId(item->meta.mGroupId);
        if (std::find(mOwnIds.begin(), mOwnIds.end(), gxsId) == mOwnIds.end())
        {
            mOwnIds.push_back(gxsId);
            mKeysTS[gxsId].TS = time(NULL) ;
        }
    }

    return createStatus;
}


#define HASHPGP_PERIOD		180


bool p3IdService::pgphash_start()
{
	if (!CacheArbitration(BG_PGPHASH))
	{
#ifdef DEBUG_IDS
		std::cerr << "p3IdService::pgphash_start() Other Events running... Rescheduling";
		std::cerr << std::endl;
#endif // DEBUG_IDS

		/* reschedule in a bit */
		RsTickEvent::schedule_in(GXSID_EVENT_PGPHASH, PGPHASH_RETRY_PERIOD);
		return false;
	}

	// SCHEDULE NEXT ONE.
	RsTickEvent::schedule_in(GXSID_EVENT_PGPHASH, PGPHASH_PERIOD);

#ifdef DEBUG_IDS
	std::cerr << "p3IdService::pgphash_start() making request";
	std::cerr << std::endl;
#endif // DEBUG_IDS

	// ACTUALLY only need summary - but have written code for data.
	// Also need to use opts.groupFlags to filter stuff properly to REALID's only.
	// TODO

	uint32_t ansType = RS_TOKREQ_ANSTYPE_DATA;
	RsTokReqOptions opts;
	opts.mReqType = GXS_REQUEST_TYPE_GROUP_DATA;
	uint32_t token = 0;
	
	RsGenExchange::getTokenService()->requestGroupInfo(token, ansType, opts);
	GxsTokenQueue::queueRequest(token, GXSIDREQ_PGPHASH);	
	return true;
}


bool p3IdService::pgphash_handlerequest(uint32_t token)
{
#ifdef DEBUG_IDS
	std::cerr << "p3IdService::pgphash_handlerequest(" << token << ")";
	std::cerr << std::endl;
#endif // DEBUG_IDS

	// We need full data - for access to Hash & Signature.
	// Perhaps we will change this to an initial pass through Meta, 
	// and use this to discard lots of things.

	// Even better - we can set flags in the Meta Data, (IdType), 
	// And use GXS to filter out all the AnonIds, and only have to process
	// Proper Ids.

	// We Will do this later!

	std::vector<RsGxsIdGroup> groups;
	bool groupsToProcess = false;
	bool ok = getGroupData(token, groups);

	if(ok)
	{
#ifdef DEBUG_IDS
		std::cerr << "p3IdService::pgphash_request() Have " << groups.size() << " Groups";
		std::cerr << std::endl;
#endif // DEBUG_IDS

		std::vector<RsGxsIdGroup>::iterator vit;
		for(vit = groups.begin(); vit != groups.end(); ++vit)
		{
#ifdef DEBUG_IDS
			std::cerr << "p3IdService::pgphash_request() Group Id: " << vit->mMeta.mGroupId;
			std::cerr << std::endl;
#endif // DEBUG_IDS

			/* Filter based on IdType */
			if (!(vit->mMeta.mGroupFlags & RSGXSID_GROUPFLAG_REALID_kept_for_compatibility))
			{
#ifdef DEBUG_IDS
				std::cerr << "p3IdService::pgphash_request() discarding AnonID";
				std::cerr << std::endl;
#endif // DEBUG_IDS
				continue;
			}

			/* now we need to decode the Service String - see what is saved there */
			SSGxsIdGroup ssdata;
			if (ssdata.load(vit->mMeta.mServiceString))
			{
				if (ssdata.pgp.validatedSignature)
				{
#ifdef DEBUG_IDS
					std::cerr << "p3IdService::pgphash_request() discarding Already Known";
					std::cerr << std::endl;
#endif // DEBUG_IDS
					continue;
				}

				/* Have a linear attempt policy -	
				 * if zero checks - try now.
				 * if 1 check, at least a day.
				 * if 2 checks: 2days, etc.
				 */

#define SECS_PER_DAY (3600 * 24)
				time_t age = time(NULL) - ssdata.pgp.lastCheckTs;
				time_t wait_period = ssdata.pgp.checkAttempts * SECS_PER_DAY;
				if (wait_period > 30 * SECS_PER_DAY)
				{
					wait_period = 30 * SECS_PER_DAY;
                }
#ifdef DEBUG_IDS
                std::cerr << "p3IdService: group " << *vit << " age=" << age << ", attempts=" << ssdata.pgp.checkAttempts  << ", wait period = " << wait_period ;
#endif

                if (age < wait_period)
                {
#ifdef DEBUG_IDS
                    std::cerr << " => discard." << std::endl;
#endif // DEBUG_IDS
                    continue;
                }
#ifdef DEBUG_IDS
                std::cerr << " => recheck!" << std::endl;
#endif
			}

			/* if we get here -> then its to be processed */
#ifdef DEBUG_IDS
			std::cerr << "p3IdService::pgphash_request() ToProcess Group: " << vit->mMeta.mGroupId;
			std::cerr << std::endl;
#endif // DEBUG_IDS

			RsStackMutex stack(mIdMtx); /********** STACK LOCKED MTX ******/
			mGroupsToProcess.push_back(*vit);
			groupsToProcess = true;
		}
	}
	else
	{
		std::cerr << "p3IdService::pgphash_request() getGroupData ERROR";
		std::cerr << std::endl;
	}

	if (groupsToProcess)
	{
		// update PgpIdList -> if there are groups to process.
		getPgpIdList();
	}

	// Schedule Processing.
	RsTickEvent::schedule_in(GXSID_EVENT_PGPHASH_PROC, PGPHASH_PROC_PERIOD);
	return true;
}

bool p3IdService::pgphash_process()
{
	/* each time this is called - process one Id from mGroupsToProcess */
	RsGxsIdGroup pg;
	bool isDone = false;
	{
		RsStackMutex stack(mIdMtx); /********** STACK LOCKED MTX ******/
		if (!mGroupsToProcess.empty())
		{
			pg = mGroupsToProcess.front();
			mGroupsToProcess.pop_front();

#ifdef DEBUG_IDS
			std::cerr << "p3IdService::pgphash_process() Popped Group: " << pg.mMeta.mGroupId;
			std::cerr << std::endl;
#endif // DEBUG_IDS
		}
		else
		{
			isDone = true;
		}
	}

	if (isDone)
	{
#ifdef DEBUG_IDS
		std::cerr << "p3IdService::pgphash_process() List Empty... Done";
		std::cerr << std::endl;
#endif // DEBUG_IDS
		// FINISHED.
		CacheArbitrationDone(BG_PGPHASH);
		return true;
	}
	

	SSGxsIdGroup ssdata;
	ssdata.load(pg.mMeta.mServiceString); // attempt load - okay if fails.

    RsPgpId pgpId;
    bool error = false ;

    if (checkId(pg, pgpId,error))
	{
		/* found a match - update everything */
		/* Consistency issues here - what if Reputation was recently updated? */

#ifdef DEBUG_IDS
		std::cerr << "p3IdService::pgphash_process() CheckId Success for Group: " << pg.mMeta.mGroupId;
        std::cerr << " PgpId: " << pgpId;
		std::cerr << std::endl;
#endif // DEBUG_IDS

		/* update */
		ssdata.pgp.validatedSignature = true;
		ssdata.pgp.pgpId = pgpId;

	}
    else if(error)
    {
            std::cerr << "Identity has an invalid signature. It will be deleted." << std::endl;

            uint32_t token ;
            deleteIdentity(token,pg) ;
    }
    else
	{
#ifdef DEBUG_IDS
		std::cerr << "p3IdService::pgphash_process() No Match for Group: " << pg.mMeta.mGroupId;
		std::cerr << std::endl;
#endif // DEBUG_IDS

		ssdata.pgp.lastCheckTs = time(NULL);
		ssdata.pgp.checkAttempts++;
		ssdata.pgp.pgpId = pgpId;	// read from the signature, but not verified
	}

    if(!error)
    {
        // update IdScore too.
        ssdata.score.rep.updateIdScore(true, ssdata.pgp.validatedSignature);
        ssdata.score.rep.update();

        /* set new Group ServiceString */
        uint32_t dummyToken = 0;
        std::string serviceString = ssdata.save();
        setGroupServiceString(dummyToken, pg.mMeta.mGroupId, serviceString);

        cache_update_if_cached(RsGxsId(pg.mMeta.mGroupId), serviceString);
    }

	// Schedule Next Processing.
	RsTickEvent::schedule_in(GXSID_EVENT_PGPHASH_PROC, PGPHASH_PROC_PERIOD);
	return false; // as there are more items on the queue to process.
}



bool p3IdService::checkId(const RsGxsIdGroup &grp, RsPgpId &pgpId,bool& error)
{
#ifdef DEBUG_IDS
	std::cerr << "p3IdService::checkId() Starting Match Check for RsGxsId: ";
	std::cerr << grp.mMeta.mGroupId;
	std::cerr << std::endl;
#endif // DEBUG_IDS

    error = false ;

	/* some sanity checking... make sure hash is the right size */

#ifdef DEBUG_IDS
	std::cerr << "p3IdService::checkId() PgpIdHash is: " << grp.mPgpIdHash;
	std::cerr << std::endl;
#endif // DEBUG_IDS

	/* iterate through and check hash */
	Sha1CheckSum ans = grp.mPgpIdHash;
    
#ifdef DEBUG_IDS
    std::string esign ;
    Radix64::encode((unsigned char *) grp.mPgpIdSign.c_str(), grp.mPgpIdSign.length(),esign) ;
    std::cerr << "Checking group signature " << esign << std::endl;
#endif
    RsPgpId issuer_id ;

    if(mPgpUtils->parseSignature((unsigned char *) grp.mPgpIdSign.c_str(), grp.mPgpIdSign.length(),issuer_id))
    {
#ifdef DEBUG_IDS
	    std::cerr << "Issuer found: " << issuer_id << std::endl;
#endif
	    pgpId = issuer_id ;
    }
    else
    {
	    std::cerr << "Signature parsing failed!!" << std::endl;
	    pgpId.clear() ;
    }

#ifdef DEBUG_IDS
	std::cerr << "\tExpected Answer: " << ans.toStdString();
	std::cerr << std::endl;
#endif // DEBUG_IDS

	RsStackMutex stack(mIdMtx); /********** STACK LOCKED MTX ******/

	std::map<RsPgpId, PGPFingerprintType>::iterator mit;
	for(mit = mPgpFingerprintMap.begin(); mit != mPgpFingerprintMap.end(); ++mit)
	{
		Sha1CheckSum hash;
        calcPGPHash(RsGxsId(grp.mMeta.mGroupId), mit->second, hash);

               
		if (ans == hash)
		{
#ifdef DEBUG_IDS
			std::cerr << "p3IdService::checkId() HASH MATCH!";
			std::cerr << std::endl;
			std::cerr << "p3IdService::checkId() Hash : " << hash.toStdString();
			std::cerr << std::endl;
#endif

			/* miracle match! */
			/* check signature too */
			if (mPgpUtils->VerifySignBin((void *) hash.toByteArray(), hash.SIZE_IN_BYTES, 
				(unsigned char *) grp.mPgpIdSign.c_str(), grp.mPgpIdSign.length(), 
				mit->second))
			{
#ifdef DEBUG_IDS
				std::cerr << "p3IdService::checkId() Signature Okay too!";
				std::cerr << std::endl;
#endif

				pgpId = mit->first;
				return true;
			}

			/* error */
			std::cerr << "p3IdService::checkId() ERROR Signature Failed";
			std::cerr << std::endl;

			std::cerr << "p3IdService::checkId() Matched PGPID: " << mit->first.toStdString();
			std::cerr << " Fingerprint: " << mit->second.toStdString();
			std::cerr << std::endl;

			std::cerr << "p3IdService::checkId() Signature: ";
			std::string strout;

			/* push binary into string -> really bad! */
			for(unsigned int i = 0; i < grp.mPgpIdSign.length(); i++)
			{
				rs_sprintf_append(strout, "%02x", (uint32_t) ((uint8_t) grp.mPgpIdSign[i]));
			}
			std::cerr << strout;
            std::cerr << std::endl;

            error = true ;
            return false ;
		}
	}

#ifdef DEBUG_IDS
	std::cerr << "p3IdService::checkId() Checked " << mPgpFingerprintMap.size() << " Hashes without Match";
	std::cerr << std::endl;
#endif // DEBUG_IDS

	return false;
}


/* worker functions */
void p3IdService::getPgpIdList()
{
#ifdef DEBUG_IDS
	std::cerr << "p3IdService::getPgpIdList() Starting....";
	std::cerr << std::endl;
#endif // DEBUG_IDS

 	std::list<RsPgpId> list;
	mPgpUtils->getGPGAllList(list);

	RsStackMutex stack(mIdMtx); /********** STACK LOCKED MTX ******/

	mPgpFingerprintMap.clear();

 	std::list<RsPgpId>::iterator it;
	for(it = list.begin(); it != list.end(); ++it)
	{
 		RsPgpId pgpId(*it);
		PGPFingerprintType fp;
		mPgpUtils->getKeyFingerprint(pgpId, fp);

#ifdef DEBUG_IDS
		std::cerr << "p3IdService::getPgpIdList() Id: " << pgpId.toStdString() << " => " << fp.toStdString();
		std::cerr << std::endl;
#endif // DEBUG_IDS

		mPgpFingerprintMap[pgpId] = fp;
	}

#ifdef DEBUG_IDS
	std::cerr << "p3IdService::getPgpIdList() Items: " << mPgpFingerprintMap.size();
	std::cerr << std::endl;
#endif // DEBUG_IDS
}


void calcPGPHash(const RsGxsId &id, const PGPFingerprintType &pgp, Sha1CheckSum &hash)
{
	unsigned char signature[SHA_DIGEST_LENGTH];
	/* hash id + pubkey => pgphash */
	SHA_CTX *sha_ctx = new SHA_CTX;
	SHA1_Init(sha_ctx);

	SHA1_Update(sha_ctx, id.toStdString().c_str(), id.toStdString().length()); // TO FIX ONE DAY.
	SHA1_Update(sha_ctx, pgp.toByteArray(), pgp.SIZE_IN_BYTES);
	SHA1_Final(signature, sha_ctx);
	hash = Sha1CheckSum(signature);

#ifdef DEBUG_IDS
	std::cerr << "calcPGPHash():";
	std::cerr << std::endl;
	std::cerr << "\tRsGxsId: " << id;
	std::cerr << std::endl;
	std::cerr << "\tFingerprint: " << pgp.toStdString();
	std::cerr << std::endl;
	std::cerr << "\tFinal Hash: " << hash.toStdString();
	std::cerr << std::endl;
#endif // DEBUG_IDS

	delete sha_ctx;
}

/************************************************************************************/
/************************************************************************************/
/************************************************************************************/
/************************************************************************************/

/* Task to validate Recogn Tags.
 *
 * Info to be stored in GroupServiceString + Cache.
 **/

bool p3IdService::recogn_schedule()
{
#ifdef DEBUG_RECOGN
	std::cerr << "p3IdService::recogn_schedule()";
	std::cerr << std::endl;
#endif

	int32_t age = 0;
	int32_t next_event = 0;

	if (RsTickEvent::event_count(GXSID_EVENT_RECOGN) > 0)
	{
#ifdef DEBUG_RECOGN
		std::cerr << "p3IdService::recogn_schedule() Skipping GXSIS_EVENT_RECOGN already scheduled";
		std::cerr << std::endl;
#endif
		return false;
	}

	if (RsTickEvent::prev_event_ago(GXSID_EVENT_RECOGN, age))
	{
#ifdef DEBUG_RECOGN
		std::cerr << "p3IdService::recogn_schedule() previous event " << age << " secs ago";
		std::cerr << std::endl;
#endif

		next_event = RECOGN_PERIOD - age;
		if (next_event < 0)
		{
			next_event = 0;
		}
	}

	RsTickEvent::schedule_in(GXSID_EVENT_RECOGN, next_event);
	return true;
}


bool p3IdService::recogn_start()
{
	if (!CacheArbitration(BG_RECOGN))
	{
#ifdef DEBUG_RECOGN
		std::cerr << "p3IdService::recogn_start() Other Events running... Rescheduling";
		std::cerr << std::endl;
#endif // DEBUG_RECOGN

		/* reschedule in a bit */
		RsTickEvent::schedule_in(GXSID_EVENT_RECOGN, RECOGN_RETRY_PERIOD);
		return false;
	}

	// NEXT EVENT is scheduled via recogn_schedule.

#ifdef DEBUG_RECOGN
	std::cerr << "p3IdService::recogn_start() making request";
	std::cerr << std::endl;
#endif // DEBUG_RECOGN

	std::list<RsGxsGroupId> recognList;
	{
		RsStackMutex stack(mIdMtx); /********** STACK LOCKED MTX ******/

		recognList = mRecognGroupIds;
		mRecognGroupIds.clear();
	}

	if (recognList.empty())
	{
#ifdef DEBUG_RECOGN
		std::cerr << "p3IdService::recogn_start() List is Empty, cancelling";
		std::cerr << std::endl;
#endif // DEBUG_RECOGN

		// FINISHED.
		CacheArbitrationDone(BG_RECOGN);
		return false;
	}

	uint32_t ansType = RS_TOKREQ_ANSTYPE_DATA;
	RsTokReqOptions opts;
	opts.mReqType = GXS_REQUEST_TYPE_GROUP_DATA;
	uint32_t token = 0;
	
	RsGenExchange::getTokenService()->requestGroupInfo(token, ansType, opts, recognList);
	GxsTokenQueue::queueRequest(token, GXSIDREQ_RECOGN);	
	return true;

}


bool p3IdService::recogn_handlerequest(uint32_t token)
{
#ifdef DEBUG_RECOGN
	std::cerr << "p3IdService::recogn_handlerequest(" << token << ")";
	std::cerr << std::endl;
#endif // DEBUG_RECOGN

	std::vector<RsGxsGrpItem*> grpData;
	bool ok = RsGenExchange::getGroupData(token, grpData);
	
	if(ok)
	{
#ifdef DEBUG_RECOGN
		std::cerr << "p3IdService::recogn_request() Have " << grpData.size() << " Groups";
		std::cerr << std::endl;
#endif // DEBUG_RECOGN
		
		std::vector<RsGxsGrpItem*>::iterator vit = grpData.begin();
		
		for(; vit != grpData.end(); ++vit)
		{
			RsGxsIdGroupItem* item = dynamic_cast<RsGxsIdGroupItem*>(*vit);
			if (item)
			{

#ifdef DEBUG_RECOGN
				std::cerr << "p3IdService::recogn_request() Group Id: " << item->meta.mGroupId;
				std::cerr << std::endl;
#endif // DEBUG_RECOGN

				RsStackMutex stack(mIdMtx); /********** STACK LOCKED MTX ******/
				mRecognGroupsToProcess.push_back(item);
			}
			else 
			{
				delete (*vit);
			}
		}
	}
	else
	{
		std::cerr << "p3IdService::recogn_request() getGroupData ERROR";
		std::cerr << std::endl;
	}

	// Schedule Processing.
	RsTickEvent::schedule_in(GXSID_EVENT_RECOGN_PROC, RECOGN_PROC_PERIOD);
	return true;
}


bool p3IdService::recogn_process()
{
	/* each time this is called - process one Id from mGroupsToProcess */
	RsGxsIdGroupItem *item;
	bool isDone = false;
	{
		RsStackMutex stack(mIdMtx); /********** STACK LOCKED MTX ******/
		if (!mRecognGroupsToProcess.empty())
		{
			item = mRecognGroupsToProcess.front();
			mRecognGroupsToProcess.pop_front();

#ifdef DEBUG_RECOGN
			std::cerr << "p3IdService::recogn_process() Popped Group: " << item->meta.mGroupId;
			std::cerr << std::endl;
#endif // DEBUG_RECOGN
		}
		else
		{
			isDone = true;
		}
	}

	if (isDone)
	{
#ifdef DEBUG_RECOGN
		std::cerr << "p3IdService::recogn_process() List Empty... Done";
		std::cerr << std::endl;
#endif // DEBUG_RECOGN
		// FINISHED.
		CacheArbitrationDone(BG_RECOGN);
		return true;
	}
	


	std::list<RsGxsRecognTagItem *> tagItems;
	std::list<RsGxsRecognTagItem *>::iterator it;

	recogn_extract_taginfo(item, tagItems);

	bool isPending = false;
	int i = 1;
	uint32_t tagValidFlags = 0;
	for(it = tagItems.begin(); it != tagItems.end(); ++it)
	{
		bool isTagPending = false;
		bool isTagOk = recogn_checktag(RsGxsId(item->meta.mGroupId.toStdString()), item->meta.mGroupName, *it, true, isPending);
		if (isTagOk)
		{
			tagValidFlags |= i;
		}
		else 
		{
			isPending |= isTagPending;
		}

		delete *it;
		i *= 2;		
	}

#ifdef DEBUG_RECOGN
	std::cerr << "p3IdService::recogn_process() Tags Checked, saving";
	std::cerr << std::endl;
#endif // DEBUG_RECOGN

	SSGxsIdGroup ssdata;
	ssdata.load(item->meta.mServiceString); // attempt load - okay if fails.

	ssdata.recogntags.setTags(true, isPending, tagValidFlags);
	ssdata.recogntags.lastCheckTs = time(NULL);
	ssdata.recogntags.publishTs = item->meta.mPublishTs;

	// update IdScore too.
	bool pgpId = (item->meta.mGroupFlags & RSGXSID_GROUPFLAG_REALID_kept_for_compatibility);
	ssdata.score.rep.updateIdScore(pgpId, ssdata.pgp.validatedSignature);
	ssdata.score.rep.update();

	/* set new Group ServiceString */
	uint32_t dummyToken = 0;
	std::string serviceString = ssdata.save();
	setGroupServiceString(dummyToken, item->meta.mGroupId, serviceString);

	cache_update_if_cached(RsGxsId(item->meta.mGroupId.toStdString()), serviceString);

	delete item;
	
	// Schedule Next Processing.
	RsTickEvent::schedule_in(GXSID_EVENT_RECOGN_PROC, RECOGN_PROC_PERIOD);
	return false; // as there are more items on the queue to process.
}


bool p3IdService::recogn_checktag(const RsGxsId &id, const std::string &nickname, RsGxsRecognTagItem *item, bool doSignCheck, bool &isPending)
{

#ifdef DEBUG_RECOGN
		std::cerr << "p3IdService::recogn_checktag() groupId: " << id;
		std::cerr << std::endl;
		std::cerr << "p3IdService::recogn_checktag() nickname: " << nickname;
		std::cerr << std::endl;
		std::cerr << "p3IdService::recogn_checktag() item: ";
		std::cerr << std::endl;
		((RsGxsRecognTagItem *) item)->print(std::cerr);
#endif // DEBUG_RECOGN

	// To check:
	// -------------------
	// date range.
	// id matches.
	// nickname matches.
	// signer is valid.
	// ------ 
	// signature is valid.  (only if doSignCheck == true)
	
	time_t now = time(NULL);
	isPending = false;
	
	// check date range.
	if ((item->valid_from > now) || (item->valid_to < now))
	{
#ifdef DEBUG_RECOGN
		std::cerr << "p3IdService::recogn_checktag() failed timestamp";
		std::cerr << std::endl;
#endif // DEBUG_RECOGN

		return false;
	}
	
	// id match.
	if (id != item->identity)
	{
#ifdef DEBUG_RECOGN
		std::cerr << "p3IdService::recogn_checktag() failed identity";
		std::cerr << std::endl;
#endif // DEBUG_RECOGN
		return false;
	}
	
	// nickname match.
	if (nickname != item->nickname)
	{
#ifdef DEBUG_RECOGN
		std::cerr << "p3IdService::recogn_checktag() failed nickname";
		std::cerr << std::endl;
#endif // DEBUG_RECOGN
		return false;
	}
	
	
	
	{
		/* check they validity of the Tag */
		RsStackMutex stack(mIdMtx); /********** STACK LOCKED MTX ******/

		
		std::map<RsGxsId, RsGxsRecognSignerItem *>::iterator it;
		it = mRecognSignKeys.find(item->sign.keyId);
		if (it == mRecognSignKeys.end())
		{
#ifdef DEBUG_RECOGN
			std::cerr << "p3IdService::recogn_checktag() failed to find signkey";
			std::cerr << std::endl;
#endif // DEBUG_RECOGN

			// If OldKey, then we don't want to reprocess.
			if (mRecognOldSignKeys.end() != 
				mRecognOldSignKeys.find(item->sign.keyId))
			{
				isPending = true; // need to reprocess later with new key
			}
			return false;
		}
		
		// Check tag_class is okay for signer.
		if (it->second->signing_classes.ids.end() == 
			std::find(it->second->signing_classes.ids.begin(), it->second->signing_classes.ids.end(), item->tag_class))
		{
#ifdef DEBUG_RECOGN
			std::cerr << "p3IdService::recogn_checktag() failed signing_class check";
			std::cerr << std::endl;
#endif // DEBUG_RECOGN
			return false;
		}
		
		// ALL Okay, just signature to check.
		if (!doSignCheck)
		{
			return true;
		}

		return RsRecogn::validateTagSignature(it->second, item);
	}
}


void p3IdService::loadRecognKeys()
{
	RsStackMutex stack(mIdMtx); /**** LOCKED MUTEX ****/

	RsRecogn::loadSigningKeys(mRecognSignKeys);
}



/************************************************************************************/
/************************************************************************************/
/************************************************************************************/
/************************************************************************************/
/************************************************************************************/

#define MAX_KNOWN_PGPIDS	20 
#define MAX_UNKNOWN_PGPIDS	20 
#define MAX_PSEUDOIDS		20

#define DUMMY_GXSID_DELAY	5

void p3IdService::generateDummyData()
{

	generateDummy_OwnIds();

	time_t age = 0;
	for(int i = 0; i < MAX_KNOWN_PGPIDS; i++)
	{
		age += DUMMY_GXSID_DELAY;
		RsTickEvent::schedule_in(GXSID_EVENT_DUMMY_PGPID, age);
	}

	for(int i = 0; i < MAX_PSEUDOIDS; i++)
	{
		age += DUMMY_GXSID_DELAY;
		RsTickEvent::schedule_in(GXSID_EVENT_DUMMY_PSEUDOID, age);
	}

	for(int i = 0; i < MAX_UNKNOWN_PGPIDS; i++)
	{
		age += DUMMY_GXSID_DELAY;
		RsTickEvent::schedule_in(GXSID_EVENT_DUMMY_UNKNOWN_PGPID, age);
	}
}





void p3IdService::generateDummy_OwnIds()
{
	RsStackMutex stack(mIdMtx); /********** STACK LOCKED MTX ******/

	/* grab all the gpg ids... and make some ids */

	RsPgpId ownId = mPgpUtils->getPGPOwnId();

#if 0
	// generate some ownIds.
    //int genCount = 0;
	int i;

	int nIds = 2 + (RSRandom::random_u32() % 2);
	for(i = 0; i < nIds; i++)
	{
		RsGxsIdGroup id;
	       	RsPeerDetails details;

		id.mMeta.mGroupFlags = RSGXSID_GROUPFLAG_REALID;

//		// HACK FOR DUMMY GENERATION.
//		id.mMeta.mAuthorId = ownId.toStdString();
//	       	if (rsPeers->getPeerDetails(ownId, details))
//		{
//			std::ostringstream out;
//			out << details.name << "_" << i + 1;
//
//			id.mMeta.mGroupName = out.str();
//		}

		uint32_t dummyToken = 0;
		createGroup(dummyToken, id);
	}
#endif
}


void p3IdService::generateDummy_FriendPGP()
{
	RsStackMutex stack(mIdMtx); /********** STACK LOCKED MTX ******/

	// Now Generate for friends.
	std::list<RsPgpId> gpgids;
	std::list<RsPgpId>::const_iterator it;
	mPgpUtils->getGPGAllList(gpgids);

	RsGxsIdGroup id;

	id.mMeta.mGroupFlags = RSGXSID_GROUPFLAG_REALID_kept_for_compatibility;

	int idx = RSRandom::random_f32() * (gpgids.size() - 1);
	it = gpgids.begin();
	for(int j = 0; j < idx; j++, ++it) ;

#if 0
	// HACK FOR DUMMY GENERATION.
	id.mMeta.mAuthorId = RsGxsId::random() ;

	RsPeerDetails details;
	if (/*rsPeers->getPeerDetails(*it, details)*/false)
	{
		std::ostringstream out;
		out << details.name << "_" << RSRandom::random_u32() % 1000;
		id.mMeta.mGroupName = out.str();
	}
	else
	{
		std::cerr << "p3IdService::generateDummy_FriendPGP() missing" << std::endl;
		std::cerr << std::endl;
		id.mMeta.mGroupName = RSRandom::random_alphaNumericString(10) ;
	}

	uint32_t dummyToken = 0;
	createGroup(dummyToken, id);
#endif
}


void p3IdService::generateDummy_UnknownPGP()
{
	RsStackMutex stack(mIdMtx); /********** STACK LOCKED MTX ******/

	RsGxsIdGroup id;

	// FAKE DATA.
	id.mMeta.mGroupFlags = RSGXSID_GROUPFLAG_REALID_kept_for_compatibility;
	id.mPgpIdHash = Sha1CheckSum::random() ;
	id.mPgpIdSign = RSRandom::random_alphaNumericString(20) ;
	id.mMeta.mGroupName = RSRandom::random_alphaNumericString(10) ;

	uint32_t dummyToken = 0;
	createGroup(dummyToken, id);
}


void p3IdService::generateDummy_UnknownPseudo()
{
	RsStackMutex stack(mIdMtx); /********** STACK LOCKED MTX ******/

	RsGxsIdGroup id;

	// FAKE DATA.
	id.mMeta.mGroupFlags = 0;
	id.mMeta.mGroupName = RSRandom::random_alphaNumericString(10) ;

	uint32_t dummyToken = 0;
	createGroup(dummyToken, id);
}


std::string rsIdTypeToString(uint32_t idtype)
{
	std::string str;
	if (idtype & RSID_TYPE_REALID)
	{
		str += "GPGID ";
	}
	if (idtype & RSID_TYPE_PSEUDONYM)
	{
		str += "PSEUDO ";
	}
	if (idtype & RSID_RELATION_YOURSELF)
	{
		str += "YOURSELF ";
	}
	if (idtype & RSID_RELATION_FRIEND)
	{
		str += "FRIEND ";
	}
	if (idtype & RSID_RELATION_FOF)
	{
		str += "FOF ";
	}
	if (idtype & RSID_RELATION_OTHER)
	{
		str += "OTHER ";
	}
	if (idtype & RSID_RELATION_UNKNOWN)
	{
		str += "UNKNOWN ";
	}
	return str;
}








/************************************************************************************/
/************************************************************************************/
/************************************************************************************/
/************************************************************************************/


/* here we are running a background process that calculates the reputation scores
 * for each of the IDs....
 * 
 * As this class will be extensively used by many other threads... it is best
 * that we don't block at all. This should be in a background thread.
 * Perhaps a generic method to handle this will be advisable.... but we do that later.
 *
 *
 * So Reputation....
 *   4 components:
 *     1) Your Opinion: Should override everything else.
 *     2) Implicit Factors: Know the associated GPG Key.
 *     3) Your Friends Opinions: 
 *     4) Your Friends Calculated Reputation Scores.
 *
 * Must make sure that there is no Feedback loop in the Reputation calculation.
 *
 * So: Our Score + Friends Scores => Local Reputation.
 *  Local Reputation + Friends Reputations => Final Reputation?
 *
 * Do we need to 'ignore' Non-scores?
 *   ---> This becomes like the "Best Comment" algorithm from Reddit...
 *   Use a statistical mechanism to work out a lower bound on Reputation.
 *
 * But what if your opinion is wrong?.... well likely your friends will
 * get their messages and reply... you'll see the missing message - request it - check reputation etc.
 *
 * So we are going to have three different scores (Own, Peers, (the neighbour) Hood)...
 *
 * So next question, when do we need to incrementally calculate the score?
 *  .... how often do we need to recalculate everything -> this could lead to a flux of messages. 
 *
 *
 * 
 * MORE NOTES:
 *
 *   The Opinion Messages will have to be signed by PGP or SSL Keys, to guarantee that we don't 
 * multiple votes per person... As the message system doesn't handle uniqueness in this respect, 
 * we might have to do FULL_CALC for everything - This bit TODO.
 *
 * This will make IdService quite different to the other GXS services.
 */

/************************************************************************************/
/*
 * Processing Algorithm:
 *  - Grab all Groups which have received messages. 
 *  (opt 1)-> grab latest msgs for each of these and process => score.
 *  (opt 2)-> try incremental system (people probably won't change opinions often -> just set them once)
 *      --> if not possible, fallback to full calculation.
 *
 * 
 */


/************************************************************************************/
/*
 * Scoring system.
 * -100 to 100 is expected range.
 * 
 *
 * Each Lobby has a publish threshold.
 *   - As part of Lobby definition. ???
 *   - Locally Set.
 *
 * Threshold:
 *   50 VIP List.
 *   20 Dress Code
 *   10 Limit Riffraff.
 *   0 Accept All.
 *
 * Implicit Scores:
 *   +50 for known PGP
 *   +10 for unknown PGP  (want to encourage usage).
 *   +5 for Anon ID.
 *
 * Own Scores:
 *   +1000 Accepted
 *   +50 Friend
 *   +10 Interesting
 *   0 Mostly Harmless
 *   -10 Annoying.
 *   -50 Troll
 *   -1000 Total Banned
 *
 *
 * 



Processing Algorithm:
 *  - Grab all Groups which have received messages. 
 *  (opt 1)-> grab latest msgs for each of these and process => score.
 *  (opt 2)-> try incremental system (people probably won't change opinions often -> just set them once)
 *      --> if not possible, fallback to full calculation.
 *
 * 
 */




std::ostream &operator<<(std::ostream &out, const RsGxsIdGroup &grp)
{
	out << "RsGxsIdGroup: Meta: " << grp.mMeta;
	out << " PgpIdHash: " << grp.mPgpIdHash;
	out << " PgpIdSign: [binary]"; // << grp.mPgpIdSign;
	out << std::endl;
	
	return out;
}


void p3IdService::checkPeerForIdentities()
{
	RsStackMutex stack(mIdMtx);

	// crud, i needed peers instead!
	mGroupNotPresent.clear();
}


// Overloaded from GxsTokenQueue for Request callbacks.
void p3IdService::handleResponse(uint32_t token, uint32_t req_type)
{
#ifdef DEBUG_IDS
	std::cerr << "p3IdService::handleResponse(" << token << "," << req_type << ")";
	std::cerr << std::endl;
#endif // DEBUG_IDS

	// stuff.
	switch(req_type)
	{
	case GXSIDREQ_CACHEOWNIDS:
		cache_load_ownids(token);
		break;
	case GXSIDREQ_CACHELOAD:
		cache_load_for_token(token);
		break;
	case GXSIDREQ_PGPHASH:
		pgphash_handlerequest(token);
		break;
	case GXSIDREQ_RECOGN:
		recogn_handlerequest(token);
		break;
	case GXSIDREQ_CACHETEST:
		cachetest_handlerequest(token);
		break;
	case GXSIDREQ_OPINION:
		opinion_handlerequest(token);
		break;
	case GXSIDREQ_SERIALIZE_TO_MEMORY:
		handle_get_serialized_grp(token);
		break;
	default:
		std::cerr << "p3IdService::handleResponse() Unknown Request Type: "
		          << req_type << std::endl;
		break;
	}
}


	// Overloaded from RsTickEvent for Event callbacks.
void p3IdService::handle_event(uint32_t event_type, const std::string &/*elabel*/)
{
#ifdef DEBUG_IDS
	std::cerr << "p3IdService::handle_event(" << event_type << ")";
	std::cerr << std::endl;
#endif // DEBUG_IDS

	// stuff.
	switch(event_type)
	{
		case GXSID_EVENT_CACHEOWNIDS:
			cache_request_ownids();
			break;

		case GXSID_EVENT_CACHELOAD:
			cache_start_load();
			break;

		case GXSID_EVENT_CACHETEST:
			cachetest_getlist();
			break;

		case GXSID_EVENT_PGPHASH:
			pgphash_start();
			break;

		case GXSID_EVENT_PGPHASH_PROC:
			pgphash_process();
			break;

		case GXSID_EVENT_RECOGN:
			recogn_start();
			break;

		case GXSID_EVENT_RECOGN_PROC:
			recogn_process();
			break;

		case GXSID_EVENT_DUMMYDATA:
			generateDummyData();
			break;

		case GXSID_EVENT_DUMMY_OWNIDS:
			generateDummy_OwnIds();
			break;

		case GXSID_EVENT_DUMMY_PGPID:
			generateDummy_FriendPGP();
			break;

		case GXSID_EVENT_DUMMY_UNKNOWN_PGPID:
			generateDummy_UnknownPGP();
			break;

		case GXSID_EVENT_DUMMY_PSEUDOID:
			generateDummy_UnknownPseudo();
			break;
		case GXSID_EVENT_REQUEST_IDS:
			requestIdsFromNet();
			break;


		default:
			/* error */
			std::cerr << "p3IdService::handle_event() Unknown Event Type: " << event_type;
			std::cerr << std::endl;
			break;
	}
}

RsIdentityUsage::RsIdentityUsage(uint16_t service,const RsIdentityUsage::UsageCode& code,const RsGxsGroupId& gid,const RsGxsMessageId& mid,uint64_t additional_id,const std::string& comment)
    	: mServiceId(service), mUsageCode(code), mGrpId(gid), mMsgId(mid),mAdditionalId(additional_id),mComment(comment)
{
#ifdef DEBUG_IDS
	// This is a hack, since it will hash also mHash, but because it is initialized to 0, and only computed in the constructor here, it should be ok.
    std::cerr << "New identity usage: " << std::endl;
    std::cerr << "  service=" << std::hex << service << std::endl;
    std::cerr << "  code   =" << std::hex << code << std::endl;
    std::cerr << "  grpId  =" << std::hex << gid << std::endl;
    std::cerr << "  msgId  =" << std::hex << mid << std::endl;
    std::cerr << "  add id =" << std::hex << additional_id << std::endl;
    std::cerr << "  commnt =\"" << std::hex << comment << "\"" << std::endl;
#endif

    librs::crypto::HashStream hs(librs::crypto::HashStream::SHA1) ;

    hs << (uint32_t)service ;
    hs << (uint8_t)code ;
    hs << gid ;
    hs << mid ;
    hs << (uint64_t)additional_id ;
    hs << comment ;

	mHash = hs.hash();

#ifdef DEBUG_IDS
    std::cerr << "  hash   =\"" << std::hex << mHash << "\"" << std::endl;
#endif
}



