public bool removeMessage(Friend friend, byte[] msg_id)
 {
     lock (pendingRecipients)
     {
         PendingRecipient pending_recipient = pendingRecipients.Find(x => x.address.SequenceEqual(friend.walletAddress));
         if (pending_recipient != null)
         {
             PendingMessageHeader tmp_msg_header = pending_recipient.messageQueue.Find(x => x.id.SequenceEqual(msg_id));
             if (tmp_msg_header != null)
             {
                 pending_recipient.messageQueue.Remove(tmp_msg_header);
                 if (File.Exists(tmp_msg_header.filePath))
                 {
                     File.Delete(tmp_msg_header.filePath);
                     return(true);
                 }
             }
         }
         return(false);
     }
 }
        private void sendMessage(OffloadedMessage om)
        {
            PendingMessage       pm             = new PendingMessage(om.msg, om.sendToServer, om.sendPushNotification, om.removeAfterSending);
            StreamMessage        msg            = pm.streamMessage;
            PendingMessageHeader tmp_msg_header = tmp_msg_header = getPendingMessageHeader(om.friend, msg.id);

            if (tmp_msg_header != null)
            {
                pm.filePath = tmp_msg_header.filePath;
            }
            PendingRecipient tmp_recipient = null;

            lock (pendingRecipients)
            {
                tmp_recipient = pendingRecipients.Find(x => x.address.SequenceEqual(msg.recipient));
                if (tmp_recipient == null)
                {
                    tmp_recipient = new PendingRecipient(msg.recipient);
                    pendingRecipients.Add(tmp_recipient);
                }
            }
            if (om.addToPendingMessages)
            {
                pm.save(storagePath);
                if (tmp_msg_header == null)
                {
                    tmp_recipient.messageQueue.Add(new PendingMessageHeader()
                    {
                        filePath = pm.filePath, id = pm.streamMessage.id, sendToServer = pm.sendToServer
                    });
                }
            }
            if (tmp_recipient.messageQueue.Count == 1 || !om.addToPendingMessages)
            {
                sendMessage(om.friend, pm, om.addToPendingMessages);
            }
        }
        private bool sendMessage(Friend friend, PendingMessage pending_message, bool add_to_pending_messages = true)
        {
            StreamMessage msg                    = pending_message.streamMessage;
            bool          send_to_server         = pending_message.sendToServer;
            bool          send_push_notification = pending_message.sendPushNotification;

            // TODO this function has to be improved and node's wallet address has to be added
            if (friend.publicKey != null || (msg.encryptionType != StreamMessageEncryptionCode.rsa && friend.aesKey != null && friend.chachaKey != null))
            {
                if (msg.encryptionType == StreamMessageEncryptionCode.none)
                {
                    if (friend.aesKey != null && friend.chachaKey != null)
                    {
                        // upgrade encryption type
                        msg.encryptionType = StreamMessageEncryptionCode.spixi1;
                    }
                    else if (!friend.bot)
                    {
                        // upgrade encryption type
                        msg.encryptionType = StreamMessageEncryptionCode.rsa;
                    }
                }
                if (msg.encryptionType != StreamMessageEncryptionCode.none)
                {
                    if (msg.version == 0 && msg.encryptionType == StreamMessageEncryptionCode.rsa && !msg.encrypted)
                    {
                        msg.sign(IxianHandler.getWalletStorage().getPrimaryPrivateKey());
                    }
                    if (!msg.encrypt(friend.publicKey, friend.aesKey, friend.chachaKey))
                    {
                        Logging.warn("Could not encrypt message for {0}!", Base58Check.Base58CheckEncoding.EncodePlain(msg.recipient));
                        return(false);
                    }
                    if (msg.version > 0 && msg.encryptionType == StreamMessageEncryptionCode.rsa)
                    {
                        msg.sign(IxianHandler.getWalletStorage().getPrimaryPrivateKey());
                    }
                }
            }
            else if (msg.encryptionType != StreamMessageEncryptionCode.none)
            {
                if (friend.publicKey == null)
                {
                    byte[] pub_k = FriendList.findContactPubkey(friend.walletAddress);
                    friend.setPublicKey(pub_k);
                }
                if (!friend.bot)
                {
                    Logging.warn("Could not send message to {0}, due to missing encryption keys!", Base58Check.Base58CheckEncoding.EncodePlain(msg.recipient));
                    // Return true in case it has other messages in the queue that need to be processed and aren't encrypted
                    return(true);
                }
                else
                {
                    // TODO TODO TODO perhaps it would be better to discard such message and notify the user
                    Logging.warn("Tried sending encrypted message of type {0} without encryption keys to {1}, which is a bot, changing message encryption type to none!", msg.type, Base58Check.Base58CheckEncoding.EncodePlain(msg.recipient));
                    msg.encryptionType = StreamMessageEncryptionCode.none;
                }
            }

            bool   sent     = false;
            string hostname = friend.searchForRelay(); // TODO cuckoo filter should be used instead, need to check the performance when PL is big

            if (friend.online)
            {
                if (hostname != "" && hostname != null)
                {
                    StreamClientManager.connectTo(hostname, null); // TODO replace null with node address
                    sent = StreamClientManager.sendToClient(hostname, ProtocolMessageCode.s2data, msg.getBytes(), msg.id);
                    if (sent && pending_message.removeAfterSending)
                    {
                        removeMessage(friend, pending_message.streamMessage.id);
                    }
                }
            }

            if (friend.forcePush || !friend.online || !sent)
            {
                if (send_to_server)
                {
                    send_to_server = Config.enablePushNotifications;
                    if (friend.bot)
                    {
                        send_to_server         = false;
                        send_push_notification = false;
                    }
                }
                if (send_to_server)
                {
                    if (OfflinePushMessages.sendPushMessage(msg, send_push_notification))
                    {
                        pending_message.sendToServer = false;
                        if (add_to_pending_messages)
                        {
                            pending_message.save(storagePath);
                        }
                        PendingMessageHeader tmp_msg_header = getPendingMessageHeader(friend, pending_message.streamMessage.id);
                        if (tmp_msg_header != null)
                        {
                            tmp_msg_header.sendToServer = false;
                        }
                        if (pending_message.removeAfterSending)
                        {
                            removeMessage(friend, pending_message.streamMessage.id);
                        }
                        return(true);
                    }
                }
                return(false);
            }

            return(true);

            /*         string pub_k = FriendList.findContactPubkey(msg.recipientAddress);
             *       if (pub_k.Length < 1)
             *       {
             *           Console.WriteLine("Contact {0} not found, adding to offline queue!", msg.recipientAddress);
             *           addOfflineMessage(msg);
             *           return;
             *       }
             *
             *
             *       // Create a new IXIAN transaction
             *       //  byte[] checksum = Crypto.sha256(encrypted_message);
             *       Transaction transaction = new Transaction(0, msg.recipientAddress, Node.walletStorage.address);
             *       //  transaction.data = Encoding.UTF8.GetString(checksum);
             *       msg.transactionID = transaction.id;
             *       //ProtocolMessage.broadcastProtocolMessage(ProtocolMessageCode.newTransaction, transaction.getBytes());
             *
             *       // Add message to the queue
             *       messages.Add(msg);
             *
             *       // Request a new keypair from the S2 Node
             *       if(hostname == null)
             *           ProtocolMessage.broadcastProtocolMessage(ProtocolMessageCode.s2generateKeys, Encoding.UTF8.GetBytes(msg.getID()));
             *       else
             *       {
             *           NetworkClientManager.sendData(ProtocolMessageCode.s2generateKeys, Encoding.UTF8.GetBytes(msg.getID()), hostname);
             *       }*/
        }