Merge remote-tracking branch 'g1o/silent_initiate_distant_chat_api' into qml_app_chat_aesthetic

This commit is contained in:
Angela Mazzurco 2017-06-07 15:02:29 +02:00
commit cac5093f37
18 changed files with 319 additions and 40 deletions

View File

@ -1199,9 +1199,9 @@ void ChatHandler::handleInitiateDistantChatConnexion(Request& req, Response& res
DistantChatPeerId distant_chat_id;
uint32_t error_code;
if(mRsMsgs->initiateDistantChatConnexion(receiver_id, sender_id,
distant_chat_id, error_code))
resp.setOk();
if(mRsMsgs->initiateDistantChatConnexion( receiver_id, sender_id,
distant_chat_id, error_code,
false )) resp.setOk();
else resp.setFail("Failed to initiate distant chat");
ChatId chat_id(distant_chat_id);

View File

@ -1,3 +1,23 @@
/*
* libresapi
*
* Copyright (C) 2015 electron128 <electron128@yahoo.com>
* Copyright (C) 2017 Gioacchino Mazzurco <gio@eigenlab.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "PeersHandler.h"
#include <retroshare/rspeers.h>
@ -5,6 +25,7 @@
#include <util/radix64.h>
#include <retroshare/rsstatus.h>
#include <retroshare/rsiface.h>
#include <retroshare/rsconfig.h>
#include <algorithm>
@ -194,15 +215,19 @@ PeersHandler::PeersHandler(StateTokenServer* sts, RsNotify* notify, RsPeers *pee
mNotify->registerNotifyClient(this);
mStateTokenServer->registerTickClient(this);
addResourceHandler("*", this, &PeersHandler::handleWildcard);
addResourceHandler("attempt_connection", this, &PeersHandler::handleAttemptConnection);
addResourceHandler("get_state_string", this, &PeersHandler::handleGetStateString);
addResourceHandler("set_state_string", this, &PeersHandler::handleSetStateString);
addResourceHandler("get_custom_state_string", this, &PeersHandler::handleGetCustomStateString);
addResourceHandler("set_custom_state_string", this, &PeersHandler::handleSetCustomStateString);
addResourceHandler("get_network_options", this, &PeersHandler::handleGetNetworkOptions);
addResourceHandler("set_network_options", this, &PeersHandler::handleSetNetworkOptions);
addResourceHandler("get_pgp_options", this, &PeersHandler::handleGetPGPOptions);
addResourceHandler("set_pgp_options", this, &PeersHandler::handleSetPGPOptions);
addResourceHandler("get_node_options", this, &PeersHandler::handleGetNodeOptions);
addResourceHandler("set_node_options", this, &PeersHandler::handleSetNodeOptions);
addResourceHandler("examine_cert", this, &PeersHandler::handleExamineCert);
}
PeersHandler::~PeersHandler()
@ -601,6 +626,19 @@ void PeersHandler::handleWildcard(Request &req, Response &resp)
}
}
void PeersHandler::handleAttemptConnection(Request &req, Response &resp)
{
std::string ssl_peer_id;
req.mStream << makeKeyValueReference("peer_id", ssl_peer_id);
RsPeerId peerId(ssl_peer_id);
if(peerId.isNull()) resp.setFail("Invalid peer_id");
else
{
mRsPeers->connectAttempt(peerId);
resp.setOk();
}
}
void PeersHandler::handleExamineCert(Request &req, Response &resp)
{
std::string cert_string;
@ -618,6 +656,198 @@ void PeersHandler::handleExamineCert(Request &req, Response &resp)
}
}
void PeersHandler::handleGetNetworkOptions(Request& req, Response& resp)
{
RsPeerDetails detail;
if (!mRsPeers->getPeerDetails(mRsPeers->getOwnId(), detail))
return;
resp.mDataStream << makeKeyValue("local_address", detail.localAddr);
resp.mDataStream << makeKeyValue("local_port", (int)detail.localPort);
resp.mDataStream << makeKeyValue("external_address", detail.extAddr);
resp.mDataStream << makeKeyValue("external_port", (int)detail.extPort);
resp.mDataStream << makeKeyValue("dyn_dns", detail.dyndns);
int netIndex = 0;
switch(detail.netMode)
{
case RS_NETMODE_EXT:
netIndex = 2;
break;
case RS_NETMODE_UDP:
netIndex = 1;
break;
case RS_NETMODE_UPNP:
netIndex = 0;
break;
}
resp.mDataStream << makeKeyValue("nat_mode", netIndex);
int discoveryIndex = 3; // NONE.
if(detail.vs_dht != RS_VS_DHT_OFF)
{
if(detail.vs_disc != RS_VS_DISC_OFF)
discoveryIndex = 0; // PUBLIC
else
discoveryIndex = 2; // INVERTED
}
else
{
if(detail.vs_disc != RS_VS_DISC_OFF)
discoveryIndex = 1; // PRIVATE
else
discoveryIndex = 3; // NONE
}
resp.mDataStream << makeKeyValue("discovery_mode", discoveryIndex);
int dlrate = 0;
int ulrate = 0;
rsConfig->GetMaxDataRates(dlrate, ulrate);
resp.mDataStream << makeKeyValue("download_limit", dlrate);
resp.mDataStream << makeKeyValue("upload_limit", ulrate);
bool checkIP = mRsPeers->getAllowServerIPDetermination();
resp.mDataStream << makeKeyValue("check_ip", checkIP);
StreamBase& previousIPsStream = resp.mDataStream.getStreamToMember("previous_ips");
previousIPsStream.getStreamToMember();
for(std::list<std::string>::const_iterator it = detail.ipAddressList.begin(); it != detail.ipAddressList.end(); ++it)
previousIPsStream.getStreamToMember() << makeKeyValue("ip_address", *it);
std::list<std::string> ip_servers;
mRsPeers->getIPServersList(ip_servers);
StreamBase& websitesStream = resp.mDataStream.getStreamToMember("websites");
websitesStream.getStreamToMember();
for(std::list<std::string>::const_iterator it = ip_servers.begin(); it != ip_servers.end(); ++it)
websitesStream.getStreamToMember() << makeKeyValue("website", *it);
std::string proxyaddr;
uint16_t proxyport;
uint32_t status ;
// Tor
mRsPeers->getProxyServer(RS_HIDDEN_TYPE_TOR, proxyaddr, proxyport, status);
resp.mDataStream << makeKeyValue("tor_address", proxyaddr);
resp.mDataStream << makeKeyValue("tor_port", (int)proxyport);
// I2P
mRsPeers->getProxyServer(RS_HIDDEN_TYPE_I2P, proxyaddr, proxyport, status);
resp.mDataStream << makeKeyValue("i2p_address", proxyaddr);
resp.mDataStream << makeKeyValue("i2p_port", (int)proxyport);
resp.setOk();
}
void PeersHandler::handleSetNetworkOptions(Request& req, Response& resp)
{
RsPeerDetails detail;
if (!mRsPeers->getPeerDetails(mRsPeers->getOwnId(), detail))
return;
int netIndex = 0;
uint32_t natMode = 0;
req.mStream << makeKeyValueReference("nat_mode", netIndex);
switch(netIndex)
{
case 3:
natMode = RS_NETMODE_HIDDEN;
break;
case 2:
natMode = RS_NETMODE_EXT;
break;
case 1:
natMode = RS_NETMODE_UDP;
break;
default:
case 0:
natMode = RS_NETMODE_UPNP;
break;
}
if (detail.netMode != natMode)
mRsPeers->setNetworkMode(mRsPeers->getOwnId(), natMode);
int discoveryIndex;
uint16_t vs_disc = 0;
uint16_t vs_dht = 0;
req.mStream << makeKeyValueReference("discovery_mode", discoveryIndex);
switch(discoveryIndex)
{
case 0:
vs_disc = RS_VS_DISC_FULL;
vs_dht = RS_VS_DHT_FULL;
break;
case 1:
vs_disc = RS_VS_DISC_FULL;
vs_dht = RS_VS_DHT_OFF;
break;
case 2:
vs_disc = RS_VS_DISC_OFF;
vs_dht = RS_VS_DHT_FULL;
break;
case 3:
default:
vs_disc = RS_VS_DISC_OFF;
vs_dht = RS_VS_DHT_OFF;
break;
}
if ((vs_disc != detail.vs_disc) || (vs_dht != detail.vs_dht))
mRsPeers->setVisState(mRsPeers->getOwnId(), vs_disc, vs_dht);
if (0 != netIndex)
{
std::string localAddr;
int localPort;
std::string extAddr;
int extPort;
req.mStream << makeKeyValueReference("local_address", localAddr);
req.mStream << makeKeyValueReference("local_port", localPort);
req.mStream << makeKeyValueReference("external_address", extAddr);
req.mStream << makeKeyValueReference("external_port", extPort);
mRsPeers->setLocalAddress(mRsPeers->getOwnId(), localAddr, (uint16_t)localPort);
mRsPeers->setExtAddress(mRsPeers->getOwnId(), extAddr, (uint16_t)extPort);
}
std::string dynDNS;
req.mStream << makeKeyValueReference("dyn_dns", dynDNS);
mRsPeers->setDynDNS(mRsPeers->getOwnId(), dynDNS);
int dlrate = 0;
int ulrate = 0;
req.mStream << makeKeyValueReference("download_limit", dlrate);
req.mStream << makeKeyValueReference("upload_limit", ulrate);
rsConfig->SetMaxDataRates(dlrate, ulrate);
bool checkIP;
req.mStream << makeKeyValueReference("check_ip", checkIP);
rsPeers->allowServerIPDetermination(checkIP) ;
// Tor
std::string toraddr;
int torport;
req.mStream << makeKeyValueReference("tor_address", toraddr);
req.mStream << makeKeyValueReference("tor_port", torport);
mRsPeers->setProxyServer(RS_HIDDEN_TYPE_TOR, toraddr, (uint16_t)torport);
// I2P
std::string i2paddr;
int i2pport;
req.mStream << makeKeyValueReference("i2p_address", i2paddr);
req.mStream << makeKeyValueReference("i2p_port", i2pport);
mRsPeers->setProxyServer(RS_HIDDEN_TYPE_I2P, i2paddr, (uint16_t)i2pport);
resp.mStateToken = getCurrentStateToken();
resp.setOk();
}
void PeersHandler::handleGetPGPOptions(Request& req, Response& resp)
{
std::string pgp_id;

View File

@ -1,4 +1,23 @@
#pragma once
/*
* libresapi
*
* Copyright (C) 2015 electron128 <electron128@yahoo.com>
* Copyright (C) 2017 Gioacchino Mazzurco <gio@eigenlab.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ResourceRouter.h"
#include "StateTokenServer.h"
@ -34,6 +53,9 @@ public:
private:
void handleWildcard(Request& req, Response& resp);
void handleAttemptConnection(Request& req, Response& resp);
void handleExamineCert(Request& req, Response& resp);
void handleGetStateString(Request& req, Response& resp);
@ -42,6 +64,9 @@ private:
void handleGetCustomStateString(Request& req, Response& resp);
void handleSetCustomStateString(Request& req, Response& resp);
void handleGetNetworkOptions(Request& req, Response& resp);
void handleSetNetworkOptions(Request& req, Response& resp);
void handleGetPGPOptions(Request& req, Response& resp);
void handleSetPGPOptions(Request& req, Response& resp);

View File

@ -146,7 +146,7 @@ class p3ChatService::AvatarInfo
~AvatarInfo()
{
delete[] _image_data ;
free( _image_data );
_image_data = NULL ;
_image_size = 0 ;
}
@ -159,7 +159,7 @@ class p3ChatService::AvatarInfo
void init(const unsigned char *jpeg_data,int size)
{
_image_size = size ;
_image_data = new unsigned char[size] ;
_image_data = (unsigned char*)rs_malloc(size) ;
memcpy(_image_data,jpeg_data,size) ;
}
AvatarInfo(const unsigned char *jpeg_data,int size)
@ -169,7 +169,7 @@ class p3ChatService::AvatarInfo
void toUnsignedChar(unsigned char *& data,uint32_t& size) const
{
data = new unsigned char[_image_size] ;
data = (unsigned char *)rs_malloc(_image_size) ;
size = _image_size ;
memcpy(data,_image_data,size*sizeof(unsigned char)) ;
}

View File

@ -359,7 +359,8 @@ bool GxsSecurity::getSignature(const char *data, uint32_t data_len, const RsTlvP
ok &= EVP_SignUpdate(mdctx, data, data_len) == 1;
unsigned int siglen = EVP_PKEY_size(key_priv);
unsigned char sigbuf[siglen] = { 0 };
unsigned char sigbuf[siglen] ;
memset(sigbuf,0,siglen) ;
ok &= EVP_SignFinal(mdctx, sigbuf, &siglen, key_priv) == 1;
// clean up

View File

@ -255,6 +255,14 @@ void p3GxsTrans::handleResponse(uint32_t token, uint32_t req_type)
}
}
void p3GxsTrans::GxsTransIntegrityCleanupThread::getMessagesToDelete(GxsMsgReq& m)
{
RS_STACK_MUTEX(mMtx) ;
m = mMsgToDel ;
mMsgToDel.clear();
}
void p3GxsTrans::GxsTransIntegrityCleanupThread::run()
{
// first take out all the groups
@ -304,7 +312,7 @@ void p3GxsTrans::GxsTransIntegrityCleanupThread::run()
std::cerr << " Unrecocognised item type!" << std::endl;
else if(NULL != (mitem = dynamic_cast<RsGxsTransMailItem*>(item)))
{
std::cerr << " " << msg->metaData->mMsgId << ": Mail data with ID " << std::hex << mitem->mailId << std::dec << " from " << msg->metaData->mAuthorId << " size: " << msg->msg.bin_len << std::endl;
std::cerr << " " << msg->metaData->mMsgId << ": Mail data with ID " << std::hex << std::setfill('0') << std::setw(16) << mitem->mailId << std::dec << " from " << msg->metaData->mAuthorId << " size: " << msg->msg.bin_len << std::endl;
stored_msgs[mitem->mailId] = std::make_pair(msg->metaData->mGroupId,msg->metaData->mMsgId) ;
}
@ -337,7 +345,8 @@ void p3GxsTrans::GxsTransIntegrityCleanupThread::run()
}
}
mDs->removeMsgs(msgsToDel);
RS_STACK_MUTEX(mMtx) ;
mMsgToDel = msgsToDel ;
}
void p3GxsTrans::service_tick()
@ -362,6 +371,21 @@ void p3GxsTrans::service_tick()
}
}
// now grab collected messages to delete
if(mCleanupThread != NULL && !mCleanupThread->isRunning())
{
GxsMsgReq msgToDel ;
mCleanupThread->getMessagesToDelete(msgToDel) ;
if(!msgToDel.empty())
{
std::cerr << "p3GxsTrans::service_tick(): deleting messages." << std::endl;
getDataStore()->removeMsgs(msgToDel);
}
}
{
RS_STACK_MUTEX(mOutgoingMutex);
for ( auto it = mOutgoingQueue.begin(); it != mOutgoingQueue.end(); )

View File

@ -282,16 +282,20 @@ private:
enum CheckState { CheckStart, CheckChecking };
public:
GxsTransIntegrityCleanupThread(RsGeneralDataService *const dataService): mDs(dataService) {}
GxsTransIntegrityCleanupThread(RsGeneralDataService *const dataService): mDs(dataService),mMtx("GxsTransIntegrityCheck") {}
bool isDone();
void run();
void getDeletedIds(std::list<RsGxsGroupId>& grpIds, std::map<RsGxsGroupId, std::vector<RsGxsMessageId> >& msgIds);
private:
void getMessagesToDelete(GxsMsgReq& req) ;
private:
RsGeneralDataService* const mDs;
RsMutex mMtx ;
GxsMsgReq mMsgToDel ;
};
GxsTransIntegrityCleanupThread *mCleanupThread ;

View File

@ -211,7 +211,7 @@ void p3GxsTunnelService::flush()
if(it->second.last_contact+20+GXS_TUNNEL_KEEP_ALIVE_TIMEOUT < now && it->second.status == RS_GXS_TUNNEL_STATUS_CAN_TALK)
{
#ifdef DEBUG_GXS_TUNNEL
std::cerr << "(II) GxsTunnelService:: connexion interrupted with peer." << std::endl;
std::cerr << "(II) GxsTunnelService:: connection interrupted with peer." << std::endl;
#endif
it->second.status = RS_GXS_TUNNEL_STATUS_TUNNEL_DN ;

View File

@ -599,7 +599,8 @@ bool AuthSSLimpl::SignData(const void *data, const uint32_t len, std::string &si
EVP_MD_CTX *mdctx = EVP_MD_CTX_create();
unsigned int signlen = EVP_PKEY_size(mOwnPrivateKey);
unsigned char signature[signlen] = { 0 };
unsigned char signature[signlen] ;
memset(signature,0,signlen) ;
if (0 == EVP_SignInit(mdctx, EVP_sha1()))
{

View File

@ -1528,7 +1528,7 @@ bool p3PeerMgrIMPL::addCandidateForOwnExternalAddress(const RsPeerId &from, cons
if((!rsBanList->isAddressAccepted(addr_filtered,RSBANLIST_CHECKING_FLAGS_WHITELIST)) && (!sockaddr_storage_sameip(own_addr,addr_filtered)))
{
std::cerr << " Peer " << from << " reports a connexion address (" << sockaddr_storage_iptostring(addr_filtered) <<") that is not your current external address (" << sockaddr_storage_iptostring(own_addr) << "). This is weird." << std::endl;
std::cerr << " Peer " << from << " reports a connection address (" << sockaddr_storage_iptostring(addr_filtered) <<") that is not your current external address (" << sockaddr_storage_iptostring(own_addr) << "). This is weird." << std::endl;
RsServer::notify()->AddFeedItem(RS_FEED_ITEM_SEC_IP_WRONG_EXTERNAL_IP_REPORTED, from.toStdString(), sockaddr_storage_iptostring(own_addr), sockaddr_storage_iptostring(addr));
}

View File

@ -490,7 +490,10 @@ virtual ChatLobbyId createChatLobby(const std::string& lobby_name,const RsGxsId&
virtual uint32_t getDistantChatPermissionFlags()=0 ;
virtual bool setDistantChatPermissionFlags(uint32_t flags)=0 ;
virtual bool initiateDistantChatConnexion(const RsGxsId& to_pid,const RsGxsId& from_pid,DistantChatPeerId& pid,uint32_t& error_code) = 0;
virtual bool initiateDistantChatConnexion(
const RsGxsId& to_pid, const RsGxsId& from_pid,
DistantChatPeerId& pid, uint32_t& error_code,
bool notify = true ) = 0;
virtual bool getDistantChatStatus(const DistantChatPeerId& pid,DistantChatPeerInfo& info)=0;
virtual bool closeDistantChatConnexion(const DistantChatPeerId& pid)=0;

View File

@ -520,9 +520,12 @@ void p3Msgs::getPendingChatLobbyInvites(std::list<ChatLobbyInvite>& invites)
{
mChatSrv->getPendingChatLobbyInvites(invites) ;
}
bool p3Msgs::initiateDistantChatConnexion(const RsGxsId& to_gxs_id,const RsGxsId& from_gxs_id,DistantChatPeerId& pid,uint32_t& error_code)
bool p3Msgs::initiateDistantChatConnexion(
const RsGxsId& to_gxs_id, const RsGxsId& from_gxs_id,
DistantChatPeerId& pid, uint32_t& error_code, bool notify )
{
return mChatSrv->initiateDistantChatConnexion(to_gxs_id,from_gxs_id,pid,error_code) ;
return mChatSrv->initiateDistantChatConnexion( to_gxs_id, from_gxs_id,
pid, error_code, notify );
}
bool p3Msgs::getDistantChatStatus(const DistantChatPeerId& pid,DistantChatPeerInfo& info)
{

View File

@ -159,7 +159,11 @@ class p3Msgs: public RsMsgs
virtual bool getLobbyAutoSubscribe(const ChatLobbyId& lobby_id);
virtual ChatLobbyId createChatLobby(const std::string& lobby_name,const RsGxsId& lobby_identity,const std::string& lobby_topic,const std::set<RsPeerId>& invited_friends,ChatLobbyFlags privacy_type) ;
virtual bool initiateDistantChatConnexion(const RsGxsId& to_gxs_id, const RsGxsId& from_gxs_id, DistantChatPeerId &pid, uint32_t& error_code) ;
virtual bool initiateDistantChatConnexion(
const RsGxsId& to_gxs_id, const RsGxsId& from_gxs_id,
DistantChatPeerId &pid, uint32_t& error_code,
bool notify = true );
virtual bool getDistantChatStatus(const DistantChatPeerId& gxs_id,DistantChatPeerInfo& info);
virtual bool closeDistantChatConnexion(const DistantChatPeerId &pid) ;

View File

@ -51,7 +51,7 @@ together. The routers will talk to a fake link manager, which reports the peers
Required components:
===================
NetworkGraph: a set of friends, with connexions. Should be able to be saved to a file for debugging.
NetworkGraph: a set of friends, with connections. Should be able to be saved to a file for debugging.
GraphNode: a RS peer, represented by a random SSL id, a link manager, and possibly components such as file transfer, etc.

View File

@ -45,16 +45,11 @@ int main(int argc, char *argv[])
dynamic_cast<resource_api::ResourceRouter*>(&ctrl_mod),
&resource_api::RsControlModule::handleRequest);
#if defined(Q_OS_WIN) && defined(QT_DEBUG)
QString sockPath = "RS/";
#elif defined(Q_OS_WIN)
QString sockPath = QCoreApplication::applicationDirPath();
#else
QString sockPath = QDir::homePath() + "/.retroshare";
#endif
QString sockPath = QDir::homePath() + "/.retroshare";
sockPath.append("/libresapi.sock");
qDebug() << "Listening on:" << sockPath;
ApiServerLocal apiServerLocal(&api, sockPath); (void) apiServerLocal;
while (!ctrl_mod.processShouldExit())

View File

@ -330,8 +330,6 @@ void GxsTransportStatistics::requestGroupMeta()
std::cerr << std::endl;
#endif
mTransQueue->cancelActiveRequestTokens(GXSTRANS_GROUP_META);
RsTokReqOptions opts;
opts.mReqType = GXS_REQUEST_TYPE_GROUP_META;
@ -340,7 +338,6 @@ void GxsTransportStatistics::requestGroupMeta()
}
void GxsTransportStatistics::requestGroupStat(const RsGxsGroupId &groupId)
{
mTransQueue->cancelActiveRequestTokens(GXSTRANS_GROUP_STAT);
uint32_t token;
rsGxsTrans->getTokenService()->requestGroupStatistic(token, groupId);
mTransQueue->queueRequest(token, 0, RS_TOKREQ_ANSTYPE_ACK, GXSTRANS_GROUP_STAT);
@ -354,8 +351,6 @@ void GxsTransportStatistics::requestMsgMeta(const RsGxsGroupId& grpId)
std::cerr << std::endl;
#endif
mTransQueue->cancelActiveRequestTokens(GXSTRANS_MSG_META);
RsTokReqOptions opts;
opts.mReqType = GXS_REQUEST_TYPE_MSG_META;
@ -369,12 +364,10 @@ void GxsTransportStatistics::requestMsgMeta(const RsGxsGroupId& grpId)
void GxsTransportStatistics::loadGroupStat(const uint32_t &token)
{
#ifdef DEBUG_GXSTRANS_STATS
std::cerr << "GxsTransportStatistics::loadGroupStat." << std::endl;
#endif
GxsGroupStatistic stats;
rsGxsTrans->getGroupStatistic(token, stats);
std::cerr << "Loading group stats: " << stats.mGrpId << ", num msgs=" << stats.mNumMsgs << ", total size=" << stats.mTotalSizeOfMsgs << std::endl;
dynamic_cast<GxsGroupStatistic&>(mGroupStats[stats.mGrpId]) = stats ;
}

View File

@ -78,7 +78,6 @@ QtObject {
return lastMessageList[chatId].lastMessage
}
return ""
}
}

View File

@ -174,9 +174,6 @@ Item
}
Row
{
anchors.right: parent.right