mirror of
https://github.com/RetroShare/RetroShare.git
synced 2024-12-29 09:26:18 -05:00
614 lines
15 KiB
C++
614 lines
15 KiB
C++
|
|
// from libretroshare
|
|
//#include "serialiser/rsstatusitems.h"
|
|
#include "services/p3idservice.h"
|
|
#include "services/p3gxscircles.h"
|
|
#include "gxs/rsgixs.h"
|
|
#include "gxs/rsdataservice.h"
|
|
#include "gxs/rsgxsnetservice.h"
|
|
#include "util/rsdir.h"
|
|
|
|
// local
|
|
#include "GxsPeerNode.h"
|
|
#include "FakePgpAuxUtils.h"
|
|
#include "gxstestservice.h"
|
|
|
|
|
|
#ifdef USER_NETSERVICE_WRAPPER
|
|
#include "RsGxsNetServiceTester.h"
|
|
#endif
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
GxsPeerNode::GxsPeerNode(const RsPeerId &ownId, const std::list<RsPeerId> &friends, int testMode, bool useIdentityService)
|
|
:PeerNode(ownId, friends, false),
|
|
mUseIdentityService(useIdentityService),
|
|
mTestMode(testMode),
|
|
mGxsDir("./gxs_unittest/" + ownId.toStdString() + "/"),
|
|
mGxsIdService(NULL),
|
|
mGxsCircles(NULL),
|
|
mTestService(NULL),
|
|
mTestDs(NULL),
|
|
mTestNs(NULL)
|
|
{
|
|
// extract bits we need.
|
|
//p3PeerMgr *peerMgr = getPeerMgr();
|
|
//p3LinkMgr *linkMgr = getLinkMgr();
|
|
//p3NetMgr *netMgr = getNetMgr();
|
|
RsNxsNetMgr *nxsMgr = getNxsNetMgr();
|
|
//p3ServiceControl *serviceCtrl = getServiceControl();
|
|
|
|
// Create Service for Testing.
|
|
// Specific Testing service here.
|
|
RsDirUtil::checkCreateDirectory(mGxsDir);
|
|
|
|
std::set<std::string> filesToKeep;
|
|
RsDirUtil::cleanupDirectory(mGxsDir, filesToKeep);
|
|
|
|
std::string gxs_passwd = "testpassword";
|
|
|
|
mPgpAuxUtils = new FakePgpAuxUtils(ownId);
|
|
mPgpAuxUtils->addPeerListToPgpList(friends);
|
|
|
|
if (mUseIdentityService)
|
|
{
|
|
/**** Identity service ****/
|
|
mGxsIdDs = new RsDataService(mGxsDir, "gxsid_db",
|
|
RS_SERVICE_GXS_TYPE_GXSID, NULL, gxs_passwd);
|
|
mGxsIdService = new p3IdService(mGxsIdDs, NULL, mPgpAuxUtils);
|
|
|
|
// circles created here, as needed by Ids.
|
|
mGxsCirclesDs = new RsDataService(mGxsDir, "gxscircles_db",
|
|
RS_SERVICE_GXS_TYPE_GXSCIRCLE, NULL, gxs_passwd);
|
|
mGxsCircles = new p3GxsCircles(mGxsCirclesDs, NULL, mGxsIdService, mPgpAuxUtils);
|
|
|
|
// create GXS ID service
|
|
mGxsIdNs = new RsGxsNetService(
|
|
RS_SERVICE_GXS_TYPE_GXSID, mGxsIdDs, nxsMgr,
|
|
mGxsIdService, mGxsIdService->getServiceInfo(),
|
|
NULL, mGxsCircles,mGxsIdService,
|
|
mPgpAuxUtils,
|
|
false); // don't synchronise group automatic (need explicit group request)
|
|
|
|
mGxsIdService->setNes(mGxsIdNs);
|
|
/**** GxsCircle service ****/
|
|
|
|
#ifdef USER_NETSERVICE_WRAPPER
|
|
mGxsCirclesNs = new RsGxsNetServiceTester
|
|
#else
|
|
mGxsCirclesNs = new RsGxsNetService
|
|
#endif
|
|
(RS_SERVICE_GXS_TYPE_GXSCIRCLE, mGxsCirclesDs, nxsMgr,
|
|
mGxsCircles, mGxsCircles->getServiceInfo(),
|
|
NULL, mGxsCircles,NULL,
|
|
mPgpAuxUtils);
|
|
}
|
|
else
|
|
{
|
|
mGxsIdDs = NULL;
|
|
mGxsIdService = NULL;
|
|
mGxsCirclesDs = NULL;
|
|
mGxsCircles = NULL;
|
|
mGxsIdNs = NULL;
|
|
mGxsCirclesNs = NULL;
|
|
}
|
|
|
|
mTestDs = new RsDataService(mGxsDir, "test_db",
|
|
RS_SERVICE_GXS_TYPE_TEST,
|
|
NULL, "testPasswd");
|
|
|
|
mTestService = new GxsTestService(mTestDs, NULL, mGxsIdService, testMode);
|
|
|
|
#ifdef USER_NETSERVICE_WRAPPER
|
|
mTestNs = new RsGxsNetServiceTester
|
|
#else
|
|
mTestNs = new RsGxsNetService
|
|
#endif
|
|
(RS_SERVICE_GXS_TYPE_TEST, mTestDs, nxsMgr,
|
|
mTestService, mTestService->getServiceInfo(),
|
|
NULL, mGxsCircles,mGxsIdService,
|
|
mPgpAuxUtils);
|
|
|
|
if (mUseIdentityService)
|
|
{
|
|
AddService(mGxsIdNs);
|
|
AddService(mGxsCirclesNs);
|
|
}
|
|
AddService(mTestNs);
|
|
|
|
//mConfigMgr->addConfiguration("testservice.cfg", mTestService);
|
|
|
|
if (mUseIdentityService)
|
|
{
|
|
//mConfigMgr->addConfiguration("gxsid.cfg", mGxsIdNs);
|
|
//mConfigMgr->addConfiguration("gxscircles.cfg", mGxsCircleNs);
|
|
|
|
mGxsIdService->start();
|
|
mGxsIdNs->start();
|
|
mGxsCircles->start();
|
|
mGxsCirclesNs->start();
|
|
}
|
|
|
|
mTestService->start();
|
|
mTestNs->start();
|
|
|
|
//node->AddPqiServiceMonitor(status);
|
|
}
|
|
|
|
|
|
GxsPeerNode::~GxsPeerNode()
|
|
{
|
|
mTestService->join();
|
|
mTestNs->join();
|
|
|
|
if (mUseIdentityService)
|
|
{
|
|
mGxsIdService->join();
|
|
mGxsIdNs->join();
|
|
mGxsCircles->join();
|
|
mGxsCirclesNs->join();
|
|
|
|
delete mGxsIdNs;
|
|
delete mGxsIdService;
|
|
|
|
delete mGxsCirclesNs;
|
|
delete mGxsCircles;
|
|
}
|
|
|
|
delete mTestNs;
|
|
delete mTestService;
|
|
// this is deleted somewhere else?
|
|
//delete mTestDs;
|
|
|
|
std::set<std::string> filesToKeep;
|
|
RsDirUtil::cleanupDirectory(mGxsDir, filesToKeep);
|
|
}
|
|
|
|
bool GxsPeerNode::checkTestServiceAllowedGroups(const RsPeerId &/*peerId*/)
|
|
{
|
|
#ifdef USER_NETSERVICE_WRAPPER
|
|
std::vector<RsGxsGroupId> groups;
|
|
return ((RsGxsNetServiceTester *) mTestNs)->fetchAllowedGroups(peerId, groups);
|
|
#else
|
|
std::cerr << "GxsPeerNode::checkTestServiceAllowedGroups() not available";
|
|
std::cerr << std::endl;
|
|
return true;
|
|
#endif
|
|
}
|
|
|
|
|
|
bool GxsPeerNode::checkCircleServiceAllowedGroups(const RsPeerId &/*peerId*/)
|
|
{
|
|
#ifdef USER_NETSERVICE_WRAPPER
|
|
std::vector<RsGxsGroupId> groups;
|
|
return ((RsGxsNetServiceTester *) mGxsCirclesNs)->fetchAllowedGroups(peerId, groups);
|
|
#else
|
|
std::cerr << "GxsPeerNode::checkCircleServiceAllowedGroups() not available";
|
|
std::cerr << std::endl;
|
|
return true;
|
|
#endif
|
|
}
|
|
|
|
bool GxsPeerNode::createIdentity(const std::string &name,
|
|
bool pgpLinked,
|
|
uint32_t circleType,
|
|
const RsGxsCircleId &circleId,
|
|
RsGxsId &gxsId)
|
|
{
|
|
/* create a couple of groups */
|
|
RsTokenService *tokenService = mGxsIdService->RsGenExchange::getTokenService();
|
|
|
|
|
|
RsGxsIdGroup id;
|
|
id.mMeta.mGroupName = name;
|
|
if (pgpLinked)
|
|
{
|
|
id.mMeta.mGroupFlags = RSGXSID_GROUPFLAG_REALID_kept_for_compatibility;
|
|
}
|
|
else
|
|
{
|
|
id.mMeta.mGroupFlags = 0;
|
|
}
|
|
id.mMeta.mCircleType = circleType;
|
|
|
|
switch(circleType)
|
|
{
|
|
case GXS_CIRCLE_TYPE_YOUR_FRIENDS_ONLY:
|
|
id.mMeta.mInternalCircle = circleId;
|
|
break;
|
|
case GXS_CIRCLE_TYPE_LOCAL:
|
|
// no CircleId,
|
|
break;
|
|
case GXS_CIRCLE_TYPE_PUBLIC:
|
|
// no CircleId,
|
|
break;
|
|
case GXS_CIRCLE_TYPE_EXTERNAL:
|
|
id.mMeta.mCircleId = circleId;
|
|
break;
|
|
default:
|
|
case GXS_CIRCLE_TYPE_EXT_SELF:
|
|
std::cerr << "GxsPeerNode::createIdentity() Invalid circleType";
|
|
std::cerr << std::endl;
|
|
return false;
|
|
break;
|
|
}
|
|
|
|
uint32_t token;
|
|
if (!mGxsIdService->createGroup(token, id))
|
|
{
|
|
std::cerr << "GxsPeerNode::createIdentity() failed";
|
|
std::cerr << std::endl;
|
|
return false;
|
|
}
|
|
|
|
while(tokenService->requestStatus(token) != RsTokenService::GXS_REQUEST_V2_STATUS_COMPLETE)
|
|
{
|
|
tick();
|
|
sleep(1);
|
|
}
|
|
|
|
RsGxsGroupId groupId;
|
|
if (mGxsIdService->acknowledgeTokenGrp(token, groupId))
|
|
{
|
|
RsGxsId tmpGxsId(groupId.toStdString());
|
|
gxsId = tmpGxsId;
|
|
return true;
|
|
}
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
bool GxsPeerNode::createCircle(const std::string &name,
|
|
uint32_t circleType,
|
|
const RsGxsCircleId &circleId,
|
|
const RsGxsId &authorId,
|
|
std::set<RsPgpId> localMembers,
|
|
std::set<RsGxsId> externalMembers,
|
|
RsGxsGroupId &groupId)
|
|
{
|
|
/* create a couple of groups */
|
|
RsTokenService *tokenService = mGxsCircles->RsGenExchange::getTokenService();
|
|
|
|
RsGxsCircleGroup grp1;
|
|
grp1.mMeta.mGroupName = name;
|
|
grp1.mMeta.mAuthorId = authorId;
|
|
grp1.mMeta.mCircleType = circleType;
|
|
|
|
switch(circleType)
|
|
{
|
|
case GXS_CIRCLE_TYPE_LOCAL:
|
|
// no CircleId,
|
|
// THIS is for LOCAL Storage....
|
|
grp1.mLocalFriends = localMembers;
|
|
break;
|
|
case GXS_CIRCLE_TYPE_YOUR_FRIENDS_ONLY:
|
|
// Circle shouldn't use this.
|
|
// but could potentially.
|
|
grp1.mMeta.mInternalCircle = circleId;
|
|
grp1.mInvitedMembers = externalMembers;
|
|
break;
|
|
case GXS_CIRCLE_TYPE_PUBLIC:
|
|
// no CircleId,
|
|
grp1.mInvitedMembers = externalMembers;
|
|
break;
|
|
case GXS_CIRCLE_TYPE_EXTERNAL:
|
|
grp1.mMeta.mCircleId = circleId;
|
|
grp1.mInvitedMembers = externalMembers;
|
|
break;
|
|
case GXS_CIRCLE_TYPE_EXT_SELF:
|
|
// no CircleId.
|
|
grp1.mInvitedMembers = externalMembers;
|
|
break;
|
|
default:
|
|
std::cerr << "GxsPeerNode::createCircle() Invalid circleType";
|
|
std::cerr << std::endl;
|
|
return false;
|
|
break;
|
|
}
|
|
|
|
std::cerr << "GxsPeerNode::createCircle()";
|
|
std::cerr << " CircleType: " << (uint32_t) grp1.mMeta.mCircleType;
|
|
std::cerr << " CircleId: " << grp1.mMeta.mCircleId.toStdString();
|
|
std::cerr << std::endl;
|
|
|
|
uint32_t token;
|
|
mGxsCircles->createGroup(token, grp1) ;
|
|
|
|
while(tokenService->requestStatus(token) != RsTokenService::GXS_REQUEST_V2_STATUS_COMPLETE)
|
|
{
|
|
tick();
|
|
sleep(1);
|
|
}
|
|
return mGxsCircles->acknowledgeTokenGrp(token, groupId);
|
|
}
|
|
|
|
|
|
|
|
bool GxsPeerNode::createGroup(const std::string &name,
|
|
uint32_t circleType,
|
|
const RsGxsCircleId &circleId,
|
|
const RsGxsId &authorId,
|
|
RsGxsGroupId &groupId)
|
|
{
|
|
/* create a couple of groups */
|
|
RsTokenService *tokenService = mTestService->RsGenExchange::getTokenService();
|
|
|
|
RsTestGroup grp1;
|
|
grp1.mMeta.mGroupName = name;
|
|
grp1.mMeta.mAuthorId = authorId;
|
|
grp1.mMeta.mCircleType = circleType;
|
|
grp1.mTestString = "testString";
|
|
|
|
switch(circleType)
|
|
{
|
|
case GXS_CIRCLE_TYPE_YOUR_FRIENDS_ONLY:
|
|
grp1.mMeta.mInternalCircle = circleId;
|
|
break;
|
|
case GXS_CIRCLE_TYPE_LOCAL:
|
|
// no CircleId,
|
|
break;
|
|
case GXS_CIRCLE_TYPE_PUBLIC:
|
|
// no CircleId,
|
|
break;
|
|
case GXS_CIRCLE_TYPE_EXTERNAL:
|
|
grp1.mMeta.mCircleId = circleId;
|
|
break;
|
|
default:
|
|
case GXS_CIRCLE_TYPE_EXT_SELF:
|
|
std::cerr << "GxsPeerNode::createGroup() Invalid circleType";
|
|
std::cerr << std::endl;
|
|
return false;
|
|
break;
|
|
}
|
|
|
|
uint32_t token;
|
|
if (!mTestService->submitTestGroup(token, grp1))
|
|
{
|
|
std::cerr << "GxsPeerNode::createGroup() failed";
|
|
std::cerr << std::endl;
|
|
return false;
|
|
}
|
|
|
|
while(tokenService->requestStatus(token) != RsTokenService::GXS_REQUEST_V2_STATUS_COMPLETE)
|
|
{
|
|
tick();
|
|
sleep(1);
|
|
}
|
|
return mTestService->acknowledgeTokenGrp(token, groupId);
|
|
}
|
|
|
|
|
|
bool GxsPeerNode::createMsg(const std::string &msgstr,
|
|
const RsGxsGroupId &groupId,
|
|
const RsGxsId &authorId,
|
|
RsGxsMessageId &msgId)
|
|
{
|
|
/* create a couple of groups */
|
|
RsTokenService *tokenService = mTestService->RsGenExchange::getTokenService();
|
|
|
|
RsTestMsg msg;
|
|
msg.mMeta.mGroupId = groupId;
|
|
msg.mMeta.mAuthorId = authorId;
|
|
msg.mTestString = msgstr;
|
|
|
|
|
|
uint32_t token;
|
|
if (!mTestService->submitTestMsg(token, msg))
|
|
{
|
|
std::cerr << "GxsPeerNode::createMsg() failed";
|
|
std::cerr << std::endl;
|
|
return false;
|
|
}
|
|
|
|
while(tokenService->requestStatus(token) != RsTokenService::GXS_REQUEST_V2_STATUS_COMPLETE)
|
|
{
|
|
tick();
|
|
sleep(1);
|
|
}
|
|
RsGxsGrpMsgIdPair pairId;
|
|
bool retval = mTestService->acknowledgeTokenMsg(token, pairId);
|
|
if (retval)
|
|
{
|
|
msgId = pairId.second;
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
|
|
bool GxsPeerNode::subscribeToGroup(const RsGxsGroupId &groupId, bool subscribe)
|
|
{
|
|
/* create a couple of groups */
|
|
RsTokenService *tokenService = mTestService->RsGenExchange::getTokenService();
|
|
|
|
uint32_t token;
|
|
if (!mTestService->RsGenExchange::subscribeToGroup(token, groupId, subscribe))
|
|
{
|
|
std::cerr << "GxsPeerNode::subscribeToGroup() failed";
|
|
std::cerr << std::endl;
|
|
return false;
|
|
}
|
|
|
|
while(tokenService->requestStatus(token) != RsTokenService::GXS_REQUEST_V2_STATUS_COMPLETE)
|
|
{
|
|
tick();
|
|
sleep(1);
|
|
}
|
|
RsGxsGroupId ackGroupId;
|
|
return mTestService->acknowledgeTokenGrp(token, ackGroupId);
|
|
}
|
|
|
|
|
|
|
|
bool GxsPeerNode::getGroups(std::vector<RsTestGroup> &groups)
|
|
{
|
|
std::cerr << "GxsPeerNode::getGroups()";
|
|
std::cerr << std::endl;
|
|
|
|
RsTokenService *tokenService = mTestService->RsGenExchange::getTokenService();
|
|
|
|
RsTokReqOptions opts;
|
|
opts.mReqType = GXS_REQUEST_TYPE_GROUP_DATA;
|
|
|
|
uint32_t token;
|
|
tokenService->requestGroupInfo(token, RS_TOKREQ_ANSTYPE_DATA, opts);
|
|
while(tokenService->requestStatus(token) != RsTokenService::GXS_REQUEST_V2_STATUS_COMPLETE)
|
|
{
|
|
tick();
|
|
sleep(1);
|
|
}
|
|
return mTestService->getTestGroups(token, groups);
|
|
}
|
|
|
|
|
|
|
|
bool GxsPeerNode::getGroupList(std::list<RsGxsGroupId> &groups)
|
|
{
|
|
std::cerr << "GxsPeerNode::getGroupList()";
|
|
std::cerr << std::endl;
|
|
|
|
RsTokenService *tokenService = mTestService->RsGenExchange::getTokenService();
|
|
|
|
RsTokReqOptions opts;
|
|
opts.mReqType = GXS_REQUEST_TYPE_GROUP_IDS;
|
|
|
|
uint32_t token;
|
|
tokenService->requestGroupInfo(token, RS_TOKREQ_ANSTYPE_LIST, opts);
|
|
while(tokenService->requestStatus(token) != RsTokenService::GXS_REQUEST_V2_STATUS_COMPLETE)
|
|
{
|
|
tick();
|
|
sleep(1);
|
|
}
|
|
|
|
return mTestService->RsGenExchange::getGroupList(token, groups);
|
|
}
|
|
|
|
|
|
bool GxsPeerNode::getMsgList(const RsGxsGroupId &id, std::list<RsGxsMessageId> &msgIds)
|
|
{
|
|
std::cerr << "GxsPeerNode::getMsgList()";
|
|
std::cerr << std::endl;
|
|
|
|
RsTokenService *tokenService = mTestService->RsGenExchange::getTokenService();
|
|
|
|
RsTokReqOptions opts;
|
|
opts.mReqType = GXS_REQUEST_TYPE_MSG_IDS;
|
|
|
|
uint32_t token;
|
|
std::list<RsGxsGroupId> grpIds;
|
|
grpIds.push_back(id);
|
|
|
|
tokenService->requestMsgInfo(token, RS_TOKREQ_ANSTYPE_LIST, opts, grpIds);
|
|
while(tokenService->requestStatus(token) != RsTokenService::GXS_REQUEST_V2_STATUS_COMPLETE)
|
|
{
|
|
tick();
|
|
sleep(1);
|
|
}
|
|
|
|
std::map<RsGxsGroupId, std::vector<RsGxsMessageId> > msgResult;
|
|
|
|
if (mTestService->RsGenExchange::getMsgList(token, msgResult))
|
|
{
|
|
std::vector<RsGxsMessageId>::iterator vit;
|
|
for(vit = msgResult[id].begin(); vit != msgResult[id].end(); vit++)
|
|
{
|
|
msgIds.push_back(*vit);
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
bool GxsPeerNode::getIdentities(std::vector<RsGxsIdGroup> &groups)
|
|
{
|
|
std::cerr << "GxsPeerNode::getIdentities()";
|
|
std::cerr << std::endl;
|
|
|
|
RsTokenService *tokenService = mGxsIdService->RsGenExchange::getTokenService();
|
|
|
|
RsTokReqOptions opts;
|
|
opts.mReqType = GXS_REQUEST_TYPE_GROUP_DATA;
|
|
|
|
uint32_t token;
|
|
tokenService->requestGroupInfo(token, RS_TOKREQ_ANSTYPE_DATA, opts);
|
|
while(tokenService->requestStatus(token) != RsTokenService::GXS_REQUEST_V2_STATUS_COMPLETE)
|
|
{
|
|
tick();
|
|
sleep(1);
|
|
}
|
|
return mGxsIdService->getGroupData(token, groups);
|
|
}
|
|
|
|
bool GxsPeerNode::getIdentitiesList(std::list<RsGxsGroupId> &groups)
|
|
{
|
|
std::cerr << "GxsPeerNode::getIdentitiesList()";
|
|
std::cerr << std::endl;
|
|
|
|
RsTokenService *tokenService = mGxsIdService->RsGenExchange::getTokenService();
|
|
|
|
RsTokReqOptions opts;
|
|
opts.mReqType = GXS_REQUEST_TYPE_GROUP_IDS;
|
|
|
|
uint32_t token;
|
|
tokenService->requestGroupInfo(token, RS_TOKREQ_ANSTYPE_LIST, opts);
|
|
while(tokenService->requestStatus(token) != RsTokenService::GXS_REQUEST_V2_STATUS_COMPLETE)
|
|
{
|
|
tick();
|
|
sleep(1);
|
|
}
|
|
|
|
return mGxsIdService->RsGenExchange::getGroupList(token, groups);
|
|
}
|
|
|
|
|
|
bool GxsPeerNode::getCircles(std::vector<RsGxsCircleGroup> &groups)
|
|
{
|
|
std::cerr << "GxsPeerNode::getCircles()";
|
|
std::cerr << std::endl;
|
|
|
|
RsTokenService *tokenService = mGxsCircles->RsGenExchange::getTokenService();
|
|
|
|
RsTokReqOptions opts;
|
|
opts.mReqType = GXS_REQUEST_TYPE_GROUP_DATA;
|
|
|
|
uint32_t token;
|
|
tokenService->requestGroupInfo(token, RS_TOKREQ_ANSTYPE_DATA, opts);
|
|
while(tokenService->requestStatus(token) != RsTokenService::GXS_REQUEST_V2_STATUS_COMPLETE)
|
|
{
|
|
tick();
|
|
sleep(1);
|
|
}
|
|
return mGxsCircles->getGroupData(token, groups);
|
|
}
|
|
|
|
bool GxsPeerNode::getCirclesList(std::list<RsGxsGroupId> &groups)
|
|
{
|
|
std::cerr << "GxsPeerNode::getCirclesList()";
|
|
std::cerr << std::endl;
|
|
|
|
RsTokenService *tokenService = mGxsCircles->RsGenExchange::getTokenService();
|
|
|
|
RsTokReqOptions opts;
|
|
opts.mReqType = GXS_REQUEST_TYPE_GROUP_IDS;
|
|
|
|
uint32_t token;
|
|
tokenService->requestGroupInfo(token, RS_TOKREQ_ANSTYPE_LIST, opts);
|
|
while(tokenService->requestStatus(token) != RsTokenService::GXS_REQUEST_V2_STATUS_COMPLETE)
|
|
{
|
|
tick();
|
|
sleep(1);
|
|
}
|
|
|
|
return mGxsCircles->RsGenExchange::getGroupList(token, groups);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|