fixed gui bits. Improved message handlign logic and notification

git-svn-id: http://svn.code.sf.net/p/retroshare/code/branches/v0.6-NewGRouterModel@7854 b45a01b8-16f6-495d-af2f-9b41ad6348cc
This commit is contained in:
csoler 2015-01-22 14:33:19 +00:00
parent 15fd4d787a
commit 1998ddd765
11 changed files with 266 additions and 161 deletions

View File

@ -36,6 +36,10 @@
class RsItem ;
static const uint32_t GROUTER_CLIENT_SERVICE_DATA_STATUS_UNKNOWN = 0x0000 ; // unused.
static const uint32_t GROUTER_CLIENT_SERVICE_DATA_STATUS_RECEIVED = 0x0001 ; // sent when data has been received and a receipt is available.
static const uint32_t GROUTER_CLIENT_SERVICE_DATA_STATUS_FAILED = 0x0002 ; // sent if the global router cannot send after a while
class GRouterClientService
{
public:
@ -55,12 +59,13 @@ class GRouterClientService
std::cerr << " destination key_id = " << destination_key.toStdString() << std::endl;
}
// This method is called by the global router when a message has been acknowledged, in order to notify the client.
// This method is called by the global router when a message has been received, or cannot be sent, etc.
//
virtual void acknowledgeDataReceived(const GRouterMsgPropagationId& received_id)
virtual void notifyDataStatus(const GRouterMsgPropagationId& received_id,uint32_t data_status)
{
std::cerr << "!!!!!! Received Data acknowledge from global router, but the client service is not handling it !!!!!!!!!!" << std::endl ;
std::cerr << "!!!!!! Received Data status from global router, but the client service is not handling it !!!!!!!!!!" << std::endl ;
std::cerr << " message ID = " << received_id << std::endl;
std::cerr << " data status = " << data_status << std::endl;
}
// This function is mandatory. It should do two things:

View File

@ -194,6 +194,8 @@ RsGRouterRoutingInfoItem *RsGRouterSerialiser::deserialise_RsGRouterRoutingInfoI
ok &= getRawUInt32(data, pktsize, &offset, &item->tunnel_status);
ok &= getRawTimeT(data, pktsize, &offset, item->received_time_TS);
ok &= getRawTimeT(data, pktsize, &offset, item->last_sent_TS);
ok &= getRawTimeT(data, pktsize, &offset, item->last_tunnel_request_TS);
ok &= getRawUInt32(data, pktsize, &offset, &item->sending_attempts);
ok &= getRawUInt32(data, pktsize, &offset, &item->client_id);
@ -205,11 +207,16 @@ RsGRouterRoutingInfoItem *RsGRouterSerialiser::deserialise_RsGRouterRoutingInfoI
else
ok = false ;
// receipt item is optional.
if(offset < pktsize)
{
item->receipt_item = deserialise_RsGRouterSignedReceiptItem(&((uint8_t*)data)[offset],pktsize - offset) ;
if(item->receipt_item != NULL)
offset += item->receipt_item->serial_size() ;
else
ok = false ;
}
if (offset != rssize || !ok)
@ -278,25 +285,29 @@ RsGRouterMatrixCluesItem *RsGRouterSerialiser::deserialise_RsGRouterMatrixCluesI
return item;
}
RsGRouterGenericDataItem *RsGRouterGenericDataItem::duplicate() const
{
RsGRouterGenericDataItem *item = new RsGRouterGenericDataItem ;
item->routing_id = routing_id ;
item->destination_key = destination_key ;
item->data_size = data_size ;
// copy all members
*item = *this ;
// then duplicate the memory chunk
item->data_bytes = (uint8_t*)malloc(data_size) ;
memcpy(item->data_bytes,data_bytes,data_size) ;
item->signature = signature ;
return item ;
}
item->randomized_distance = randomized_distance ;
item->flags = flags ;
RsGRouterSignedReceiptItem *RsGRouterSignedReceiptItem::duplicate() const
{
RsGRouterSignedReceiptItem *item = new RsGRouterSignedReceiptItem ;
// copy all members
*item = *this ;
return item ;
}
@ -525,10 +536,12 @@ uint32_t RsGRouterRoutingInfoItem::serial_size() const
{
uint32_t s = 8 ; // header
s += PeerId().serial_size() ;
s += 4 ; // data status_flags
s += 4 ; // tunnel status_flags
s += 8 ; // received_time
s += 8 ; // last_sent
s += 8 ; // last_TR_TS
s += 4 ; // sending attempts
@ -536,6 +549,8 @@ uint32_t RsGRouterRoutingInfoItem::serial_size() const
s += tunnel_hash.serial_size() ;
s += data_item->serial_size(); // data_item
if(receipt_item != NULL)
s += receipt_item->serial_size(); // receipt_item
return s ;

View File

@ -77,7 +77,7 @@ class RsGRouterNonCopyableObject
{
public:
RsGRouterNonCopyableObject() {}
private:
protected:
RsGRouterNonCopyableObject(const RsGRouterNonCopyableObject&) {}
RsGRouterNonCopyableObject operator=(const RsGRouterNonCopyableObject&) { return *this ;}
};
@ -145,6 +145,8 @@ class RsGRouterSignedReceiptItem: public RsGRouterAbstractMsgItem
virtual void clear() {}
virtual std::ostream& print(std::ostream &out, uint16_t indent = 0) ;
RsGRouterSignedReceiptItem *duplicate() const ;
// packet data
//
Sha1CheckSum data_hash ; // avoids an attacker to re-use a given signed receipt. This is the hash of the enceypted data.
@ -237,9 +239,11 @@ class RsGRouterRoutingInfoItem: public RsGRouterItem, public GRouterRoutingInfo,
virtual void clear()
{
if(data_item != NULL)
delete data_item ;
if(data_item != NULL) delete data_item ;
if(receipt_item != NULL) delete receipt_item ;
data_item = NULL ;
receipt_item = NULL ;
}
virtual std::ostream& print(std::ostream &out, uint16_t indent = 0) ;
};

View File

@ -37,9 +37,9 @@ class RsGRouterSignedReceiptItem ;
static const uint16_t GROUTER_CLIENT_ID_MESSAGES = 0x1001 ;
static const uint32_t RS_GROUTER_MATRIX_MAX_HIT_ENTRIES = 10; // max number of clues to store
static const uint32_t RS_GROUTER_MATRIX_MIN_TIME_BETWEEN_HITS = 60; // can be set to up to half the publish time interval. Prevents flooding routes.
static const uint32_t RS_GROUTER_MIN_CONFIG_SAVE_PERIOD = 5; // at most save config every 5 seconds
static const uint32_t RS_GROUTER_MATRIX_MAX_HIT_ENTRIES = 10 ; // max number of clues to store
static const uint32_t RS_GROUTER_MATRIX_MIN_TIME_BETWEEN_HITS = 60 ; // can be set to up to half the publish time interval. Prevents flooding routes.
static const uint32_t RS_GROUTER_MIN_CONFIG_SAVE_PERIOD = 5 ; // at most save config every 5 seconds
static const float RS_GROUTER_BASE_WEIGHT_ROUTED_MSG = 1.0f ; // base contribution of routed message clue to routing matrix
static const float RS_GROUTER_BASE_WEIGHT_GXS_PACKET = 0.1f ; // base contribution of GXS message to routing matrix
@ -80,7 +80,15 @@ class FriendTrialRecord
class GRouterRoutingInfo
{
// There's no destructor to this class, because the memory is managed elsewhere, which
// ovoids lots of duplications if the class is copied.
public:
GRouterRoutingInfo()
{
data_item = NULL ;
receipt_item = NULL ;
}
uint32_t data_status ; // pending, waiting, etc.
uint32_t tunnel_status ; // status of tunnel handling.
time_t received_time_TS ; // time at which the item was originally received

View File

@ -200,9 +200,9 @@
//#define GROUTER_DEBUG
/**********************/
#define GROUTER_DEBUG
#define NOT_IMPLEMENTED std::cerr << __PRETTY_FUNCTION__ << ": not implemented!" << std::endl;
static const uint32_t MAX_TUNNEL_WAIT_TIME = 60 ; // wait for 60 seconds at most for a tunnel response.
static const uint32_t MAX_TUNNEL_UNMANAGED_TIME = 600 ; // min time before retry tunnels for that msg.
static const uint32_t MAX_DELAY_BETWEEN_TWO_SEND = 120 ; // wait for 120 seconds before re-sending.
static const uint32_t TUNNEL_OK_WAIT_TIME = 10 ; // wait for 10 seconds after last tunnel ok, so that we have a complete set of tunnels.
static const uint32_t MAX_GROUTER_DATA_SIZE = 2*1024*1024 ; // 2MB size limit. This is of course arbitrary.
@ -306,6 +306,9 @@ RsSerialiser *p3GRouter::setupSerialiser()
void p3GRouter::autoWash()
{
std::map<GRouterMsgPropagationId,GRouterClientService *> cancelled_msgs ;
{
RsStackMutex mtx(grMtx) ;
#ifdef GROUTER_DEBUG
@ -322,6 +325,16 @@ void p3GRouter::autoWash()
#ifdef GROUTER_DEBUG
grouter_debug() << " Removing cached item " << std::hex << it->first << std::dec << std::endl;
#endif
GRouterClientService *client = NULL ;
GRouterServiceId service_id = 0;
if(!locked_getClientAndServiceId(it->second.tunnel_hash,it->second.data_item->destination_key,client,service_id))
{
std::cerr << " ERROR: cannot find client for cancelled message " << it->first << std::endl;
}
else
cancelled_msgs[it->first] = client ;
delete it->second.data_item ;
std::map<GRouterMsgPropagationId,GRouterRoutingInfo>::iterator tmp(it) ;
++tmp ;
@ -336,6 +349,10 @@ void p3GRouter::autoWash()
#ifdef GROUTER_DEBUG
grouter_debug() << " Pending messages to route : " << _pending_messages.size() << std::endl;
#endif
}
for(std::map<GRouterMsgPropagationId,GRouterClientService*>::const_iterator it(cancelled_msgs.begin());it!=cancelled_msgs.end();++it)
it->second->notifyDataStatus(it->first, GROUTER_CLIENT_SERVICE_DATA_STATUS_FAILED) ;
}
bool p3GRouter::registerKey(const RsGxsId& authentication_key,const GRouterServiceId& client_id,const std::string& description)
@ -402,15 +419,11 @@ bool p3GRouter::handleTunnelRequest(const RsFileHash& hash,const RsPeerId& /*pee
// - we know the destination and have a route (according to matrix) => accept with high probability
// - we don't know the destination => accept with very low probability
#ifdef GROUTER_DEBUG
std::cerr << "p3GRouter::handleTunnelRequest(). Got req for hash " << hash << ", responding OK" << std::endl;
#endif
if(_owned_key_ids.find(hash) == _owned_key_ids.end())
return false ;
#ifdef GROUTER_DEBUG
std::cerr << " responding ok." << std::endl;
std::cerr << "p3GRouter::handleTunnelRequest(). Got req for hash " << hash << ", responding OK" << std::endl;
#endif
return true ;
}
@ -750,12 +763,14 @@ if(!_pending_messages.empty())
for(std::map<GRouterMsgPropagationId, GRouterRoutingInfo>::iterator it=_pending_messages.begin();it!=_pending_messages.end();++it)
{
#ifdef GROUTER_DEBUG
grouter_debug() << " " << std::hex << it->first << std::dec << " data_status=" << it->second.data_status << ", tunnel_status=" << it->second.tunnel_status;
grouter_debug() << " " << std::hex << it->first << std::dec
<< " data_status=" << it->second.data_status << ", tunnel_status=" << it->second.tunnel_status
<< " last tried: "<< now - it->second.last_tunnel_request_TS << " (secs ago)" << ", last sent: " << now - it->second.last_sent_TS << " (secs ago) " ;
#endif
if(it->second.data_status == RS_GROUTER_DATA_STATUS_PENDING)
{
if(it->second.tunnel_status == RS_GROUTER_TUNNEL_STATUS_UNMANAGED)
if(it->second.tunnel_status == RS_GROUTER_TUNNEL_STATUS_UNMANAGED && it->second.last_tunnel_request_TS + MAX_TUNNEL_UNMANAGED_TIME < now)
{
uint32_t item_delay = now - it->second.last_tunnel_request_TS ;
int item_priority = item_delay - send_retry_time_delays[std::min(5u,it->second.sending_attempts)] ;
@ -786,6 +801,13 @@ if(!_pending_messages.empty())
grouter_debug() << std::endl;
#endif
}
else if(it->second.data_status == RS_GROUTER_DATA_STATUS_RECEIPT_OK)
{
#ifdef GROUTER_DEBUG
std::cerr << " closing pending tunnels." << std::endl;
#endif
mTurtle->stopMonitoringTunnels(it->second.tunnel_hash) ;
}
#ifdef GROUTER_DEBUG
else
std::cerr << " doing nothing." << std::endl;
@ -823,12 +845,12 @@ void p3GRouter::routePendingObjects()
time_t now = time(NULL) ;
#ifdef GROUTER_DEBUG
if(!_pending_messages.empty())
std::cerr << "p3GRouter::routePendingObjects()" << std::endl;
#endif
for(std::map<GRouterMsgPropagationId, GRouterRoutingInfo>::iterator it=_pending_messages.begin();it!=_pending_messages.end();++it)
if(it->second.data_status == RS_GROUTER_DATA_STATUS_PENDING && it->second.tunnel_status == RS_GROUTER_TUNNEL_STATUS_READY
&& now > it->second.last_sent_TS + MAX_DELAY_BETWEEN_TWO_SEND)
if(it->second.data_status == RS_GROUTER_DATA_STATUS_PENDING && it->second.tunnel_status == RS_GROUTER_TUNNEL_STATUS_READY && now > it->second.last_sent_TS + MAX_DELAY_BETWEEN_TWO_SEND)
{
#ifdef GROUTER_DEBUG
std::cerr << " routing id: " << std::hex << it->first << std::dec ;
@ -987,6 +1009,7 @@ void p3GRouter::handleIncoming(const TurtleFileHash& hash,RsGRouterAbstractMsgIt
void p3GRouter::handleIncomingReceiptItem(const TurtleFileHash& hash,RsGRouterSignedReceiptItem *receipt_item)
{
bool changed = false ;
#ifdef GROUTER_DEBUG
std::cerr << "Handling incoming signed receipt item." << std::endl;
std::cerr << "Item content:" << std::endl;
@ -1032,27 +1055,35 @@ void p3GRouter::handleIncomingReceiptItem(const TurtleFileHash& hash,RsGRouterSi
//delete it->second.receipt_item ;
_pending_messages.erase(it) ;
//it->second.data_status = RS_GROUTER_DATA_STATUS_RECEIPT_OK;
it->second.data_status = RS_GROUTER_DATA_STATUS_RECEIPT_OK;
changed = true ;
//it->second.receipt_item = signed_receipt_item ;
}
#ifdef GROUTER_DEBUG
std::cerr << " notifying client that the msg was received." << std::endl;
#endif
if(changed)
IndicateConfigChanged() ;
GRouterClientService *client = NULL ;
GRouterServiceId service_id = 0;
if(!getClientAndServiceId(hash,receipt_item->signature.keyId,client,service_id))
{
RS_STACK_MUTEX (grMtx) ;
if(!locked_getClientAndServiceId(hash,receipt_item->signature.keyId,client,service_id))
{
std::cerr << " ERROR: cannot find client service for this hash/key combination." << std::endl;
return ;
}
}
#ifdef GROUTER_DEBUG
std::cerr << " retrieved client " << (void*)client << ", service_id=" << std::hex << service_id << std::dec << std::endl;
std::cerr << " acknowledging client for data received" << std::endl;
#endif
client->acknowledgeDataReceived(receipt_item->routing_id) ;
client->notifyDataStatus(receipt_item->routing_id,GROUTER_CLIENT_SERVICE_DATA_STATUS_RECEIVED) ;
}
void p3GRouter::handleIncomingDataItem(const TurtleFileHash& hash,RsGRouterGenericDataItem *generic_item)
@ -1066,11 +1097,15 @@ void p3GRouter::handleIncomingDataItem(const TurtleFileHash& hash,RsGRouterGener
GRouterClientService *client = NULL ;
GRouterServiceId service_id = 0;
if(!getClientAndServiceId(hash,generic_item->destination_key,client,service_id))
{
RS_STACK_MUTEX(grMtx) ;
if(!locked_getClientAndServiceId(hash,generic_item->destination_key,client,service_id))
{
std::cerr << " ERROR: cannot find client service for this hash/key combination." << std::endl;
return ;
}
}
// We don't do proxy yet, so the item is necessarily for us.
// The item's signature must be checked, and the item needs to be decrypted.
@ -1143,7 +1178,7 @@ void p3GRouter::handleIncomingDataItem(const TurtleFileHash& hash,RsGRouterGener
#endif
}
bool p3GRouter::getClientAndServiceId(const TurtleFileHash& hash, const RsGxsId& destination_key, GRouterClientService *& client, GRouterServiceId& service_id)
bool p3GRouter::locked_getClientAndServiceId(const TurtleFileHash& hash, const RsGxsId& destination_key, GRouterClientService *& client, GRouterServiceId& service_id)
{
client = NULL ;
service_id = 0;
@ -1157,9 +1192,6 @@ bool p3GRouter::getClientAndServiceId(const TurtleFileHash& hash, const RsGxsId&
return false;
}
{
RS_STACK_MUTEX (grMtx) ;
// now find the client given its id.
std::map<GRouterServiceId,GRouterClientService*>::const_iterator its = _registered_services.find(service_id) ;
@ -1171,7 +1203,6 @@ bool p3GRouter::getClientAndServiceId(const TurtleFileHash& hash, const RsGxsId&
}
client = its->second ;
}
return true ;
}
@ -1352,7 +1383,7 @@ bool p3GRouter::verifySignedDataItem(RsGRouterAbstractMsgItem *item)
#ifdef GROUTER_DEBUG
std::cerr << " Validating signature for data hash: " << RsDirUtil::sha1sum(data,data_size) << " and key_id = " << item->signature.keyId << std::endl;
std::cerr << " First bytes of encrypted data: " << RsUtil::BinToHex((const char *)data,std::min(data_size,30u)) << "..."<< std::endl;
std::cerr << " First bytes of signed data: " << RsUtil::BinToHex((const char *)data,std::min(data_size,30u)) << "..."<< std::endl;
#endif
if(!GxsSecurity::validateSignature((char*)data,data_size,signature_key,item->signature))
@ -1370,6 +1401,35 @@ bool p3GRouter::verifySignedDataItem(RsGRouterAbstractMsgItem *item)
}
}
bool p3GRouter::cancel(GRouterMsgPropagationId mid)
{
{
RS_STACK_MUTEX(grMtx) ;
#ifdef GROUTER_DEBUG
std::cerr << "p3GRouter::cancel(). Canceling message ID " << mid << std::endl;
#endif
std::map<GRouterMsgPropagationId,GRouterRoutingInfo>::iterator it = _pending_messages.find(mid) ;
if(it == _pending_messages.end())
{
std::cerr << " ERROR: message ID is unknown." << std::endl;
return false ;
}
delete it->second.data_item ;
if(it->second.receipt_item)
delete it->second.receipt_item;
_pending_messages.erase(it) ;
}
IndicateConfigChanged() ;
return true ;
}
bool p3GRouter::sendData(const RsGxsId& destination,const GRouterServiceId& client_id,uint8_t *data, uint32_t data_size,const RsGxsId& signing_id, GRouterMsgPropagationId &propagation_id)
{
if(data_size > MAX_GROUTER_DATA_SIZE)
@ -1454,6 +1514,8 @@ bool p3GRouter::sendData(const RsGxsId& destination,const GRouterServiceId& clie
RS_STACK_MUTEX(grMtx) ;
_pending_messages[propagation_id] = info ;
}
IndicateConfigChanged() ;
return true ;
}
@ -1549,6 +1611,9 @@ bool p3GRouter::saveList(bool& cleanup,std::list<RsItem*>& items)
*(GRouterRoutingInfo*)item = it->second ; // copy all members
item->data_item = it->second.data_item->duplicate() ; // deep copy, because we call delete on the object, and the item might be removed before we handle it in the client.
if(it->second.receipt_item != NULL)
item->receipt_item = it->second.receipt_item->duplicate() ;
items.push_back(item) ;
}

View File

@ -123,12 +123,11 @@ public:
// remembered by the client, so that he knows when the data has been received.
// The client id is supplied so that the client can be notified when the data has been received.
//
virtual bool sendData( const RsGxsId& destination,
const GRouterServiceId& client_id,
uint8_t *data,
uint32_t data_size,
const RsGxsId& signing_id,
GRouterMsgPropagationId& id) ;
virtual bool sendData(const RsGxsId& destination, const GRouterServiceId& client_id, uint8_t *data, uint32_t data_size, const RsGxsId& signing_id, GRouterMsgPropagationId& id) ;
// Cancels a given sending order. If called too late, the message might already have left. But this will remove the item from the
// re-try list.
virtual bool cancel(GRouterMsgPropagationId mid) ;
//===================================================//
// Interface with RsGRouter //
@ -214,7 +213,7 @@ private:
void handleIncomingReceiptItem(const TurtleFileHash &hash, RsGRouterSignedReceiptItem *receipt_item) ;
void handleIncomingDataItem(const TurtleFileHash &hash, RsGRouterGenericDataItem *data_item) ;
bool getClientAndServiceId(const TurtleFileHash& hash, const RsGxsId& destination_key, GRouterClientService *& client, GRouterServiceId& service_id);
bool locked_getClientAndServiceId(const TurtleFileHash& hash, const RsGxsId& destination_key, GRouterClientService *& client, GRouterServiceId& service_id);
// utility functions

View File

@ -38,7 +38,7 @@ class RsGRouterGenericDataItem ;
class RsGRouter
{
public:
public:
// This is the interface file for the global router service.
//
struct GRouterRoutingCacheInfo
@ -87,12 +87,8 @@ class RsGRouter
// Communication to other services. //
//===================================================//
virtual bool sendData( const RsGxsId& destination,
const GRouterServiceId& client_id,
uint8_t *data,
uint32_t data_size,
const RsGxsId& signing_id,
GRouterMsgPropagationId& id) =0;
virtual bool sendData(const RsGxsId& destination, const GRouterServiceId& client_id, uint8_t *data, uint32_t data_size, const RsGxsId& signing_id, GRouterMsgPropagationId& id) =0;
virtual bool cancel(GRouterMsgPropagationId mid) =0;
virtual bool registerKey(const RsGxsId& authentication_id, const GRouterServiceId& client_id,const std::string& description_string)=0 ;

View File

@ -2155,8 +2155,19 @@ void p3MsgService::manageDistantPeers()
enableDistantMessaging(mDistantMessagingEnabled) ;
}
void p3MsgService::acknowledgeDataReceived(const GRouterMsgPropagationId& id)
void p3MsgService::notifyDataStatus(const GRouterMsgPropagationId& id,uint32_t data_status)
{
if(data_status == GROUTER_CLIENT_SERVICE_DATA_STATUS_FAILED)
{
std::cerr << __PRETTY_FUNCTION__ << ": Not fully implemented. The global router fails to send apacket, but we don't deal with it. Please remind the devs to do it" << std::endl;
return ;
}
if(data_status != GROUTER_CLIENT_SERVICE_DATA_STATUS_RECEIVED)
{
std::cerr << "p3MsgService: unhandled data status info from global router for msg ID " << id << ": this is a bug." << std::endl;
return ;
}
RsStackMutex stack(mMsgMtx); /********** STACK LOCKED MTX ******/
#ifdef DEBUG_DISTANT_MSG
std::cerr << "p3MsgService::acknowledgeDataReceived(): acknowledging data received for msg propagation id " << id << std::endl;

View File

@ -137,7 +137,7 @@ class p3MsgService: public p3Service, public p3Config, public pqiServiceMonitor,
// Overloaded from GRouterClientService
virtual void receiveGRouterData(const RsGxsId& destination_key,const RsGxsId& signing_key, GRouterServiceId &client_id, uint8_t *data, uint32_t data_size) ;
virtual void acknowledgeDataReceived(const GRouterMsgPropagationId& msg_id) ;
virtual void notifyDataStatus(const GRouterMsgPropagationId& msg_id,uint32_t data_status) ;
// Utility functions

View File

@ -59,6 +59,8 @@ public:
void setDefaultId(RsGxsId defId) {mDefaultId=defId;}
void setDefaultId(std::string defIdName) {mDefaultIdName=defIdName;}
bool hasAvailableIds() const { return !mDefaultId.isNull() ; }
bool setChosenId(RsGxsId &gxsId);
ChosenId_Ret getChosenId(RsGxsId &gxsId);

View File

@ -2352,7 +2352,7 @@ void MessageComposer::addContact(enumType type)
std::list<RsGxsId> gxsIds ;
ui.friendSelectionWidget->selectedIds<RsGxsId,FriendSelectionWidget::IDTYPE_GXS>(gxsIds, false);
if(!gxsIds.empty() && ui.respond_to_CB->count() == 0)
if(!gxsIds.empty() && ui.respond_to_CB->hasAvailableIds())
{
QMessageBox::warning(NULL,tr("Cannot send distant messages"),tr("In order to send distant messages, you need an identity to sign with. Please go to the Identities tab and create one first."));
return ;