public ChangeGroupResponse ChangeGroup(ISession session, ChangeGroupRequest request)
        {
            var response  = request.CreateResponse <ChangeGroupResponse>();
            var groupChat = _groupChatsRepository.GetChat(request.GroupId);

            if (!HasAccess(groupChat, session.UserId))
            {
                response.Success = false;
                response.Error   = Errors.OperationIsNotPermitted;
                return(response);
            }

            if (groupChat.Avatar != request.NewGroupAvatar)
            {
                groupChat.Avatar = request.NewGroupAvatar;
                _groupChatsRepository.UpdateOrCreateGroup(groupChat);
                foreach (var participant in groupChat.Participants)
                {
                    _groupChangedEventManager.DeliverEventToDevices(participant.Devices,
                                                                    () => new GroupChangedEvent
                    {
                        ChangesAuthorId = session.UserId,
                        GroupId         = request.GroupId,
                        Avatar          = request.NewGroupAvatar,
                        Type            = GroupChangedEvent.ChangesType.NewAvatar
                    });
                }
            }

            if (groupChat.Name != request.NewGroupName)
            {
                groupChat.Name = request.NewGroupName;
                _groupChatsRepository.UpdateOrCreateGroup(groupChat);
                foreach (var participant in groupChat.Participants)
                {
                    _groupChangedEventManager.DeliverEventToDevices(participant.Devices,
                                                                    () => new GroupChangedEvent
                    {
                        ChangesAuthorId = session.UserId,
                        GroupId         = request.GroupId,
                        Name            = request.NewGroupName,
                        Type            = GroupChangedEvent.ChangesType.NewName
                    });
                }
            }
            return(response);
        }
        public SendMessageResponse SendMessage(ISession session, SendMessageRequest request)
        {
            var response = request.CreateResponse <SendMessageResponse>();

            Logger.Info("SendMessage from Id={0} (Device={1}) to Id={2} ({3} devices)",
                        session.UserId, session.DeviceId.Cut(), request.ReceiverUserId, request.Keys != null ? request.Keys.Count : -1);

            Dictionary <string, long>   actualMessageReceiversDevices = null;
            Dictionary <string, byte[]> keys = request.Keys;

            if (request.GroupId != Guid.Empty)// group conversation
            {
                var groupChat = _groupChatsRepository.GetChat(request.GroupId);

                //sender is in the group?
                if (groupChat.Participants.All(i => i.UserId != session.UserId))
                {
                    response.Success = false;
                    response.Error   = Errors.YouAreNotParticipantOfThatGroup;
                    return(response);
                }

                if (keys == null) //means not encrypted
                {
                    keys = groupChat.Participants
                           .SelectMany(p => p.Devices)
                           .Where(p => p != session.DeviceId)
                           .Distinct()
                           .ToDictionary <string, string, byte[]>(k => k, v => null); //null value means unencrypted
                }
                else
                {
                    //it means we will check all provided devices below (see if (actualMessageReceiversDevices != null)) -- just to avoid copy-paste

                    actualMessageReceiversDevices = new Dictionary <string, long>();
                    foreach (var groupChatParticipant in groupChat.Participants)
                    {
                        foreach (var device in groupChatParticipant.Devices)
                        {
                            actualMessageReceiversDevices[device] = groupChatParticipant.UserId;
                        }
                    }
                }
            }
            else // private conversation
            {
                if (request.ReceiverUserId == session.UserId)
                {
                    response.Success = false;
                    response.Error   = Errors.SendMessage_ReceiverAndSenderAreSame;
                    return(response);
                }

                if (keys == null)//means not encrypted
                {
                    keys = _devicesRepository.GetDevices(request.ReceiverUserId).ToDictionary <string, string, byte[]>(k => k, v => null);
                }
                else
                {
                    //it means we will check all provided devices below (see if (actualMessageReceiversDevices != null)) -- just to avoid copy-paste
                    actualMessageReceiversDevices = _devicesRepository.GetDevices(request.ReceiverUserId).ToDictionary(k => k, v => request.ReceiverUserId);
                }
            }

            if (actualMessageReceiversDevices != null) //TODO: uncoment later
            {
                var providedDevices = keys.Select(i => i.Key);

                List <string> union;
                List <string> notProvidedDevices; //conversation has more devices than user provided as targets
                List <string> wrongDevices;       //some of the devices user provided are not members of that conversation
                actualMessageReceiversDevices.Select(i => i.Key)
                .FindIntersectionAndDifference(providedDevices, out union, out notProvidedDevices, out wrongDevices);

                if (notProvidedDevices.Any())
                {
                    response.Success = false;
                    response.Error   = Errors.SendMessage_ProvideKeysForTheseDevices;
                    //we will help the user - provide public keys as well
                    response.MissedDevicesWithPublicKeysToReEncrypt = notProvidedDevices
                                                                      .Where(d => session.DeviceId != d)
                                                                      .Select(d => new PublicKeyInfo
                    {
                        DeviceId  = d,
                        PublicKey = _devicesRepository.GetPublicKeyForDevice(d),
                        UserId    = actualMessageReceiversDevices[d]
                    })
                                                                      .ToList();
                    return(response);
                }

                //cut receivers not in the conversation
                //TODO: uncomment it later

                /*foreach (var key in wrongDevices)
                 * {
                 *  keys.Remove(key);
                 * }*/
            }

            keys.Remove(session.DeviceId);

            if (keys.Count < 1)
            {
                response.Success = false;
                response.Error   = Errors.SendMessage_ReceiversNotFound;
                return(response);
            }

            foreach (var key in keys)
            {
                var innerMsg = new Message
                {
                    SenderAccessToken = session.AccessToken,
                    Text             = request.Message,
                    GroupId          = request.GroupId,
                    SenderId         = session.UserId,
                    SenderDeviceId   = session.DeviceId,
                    SenderName       = request.SenderName,
                    ReceiverId       = request.ReceiverUserId,
                    MessageTypeId    = (int)request.MessageType,
                    Thumbnail        = request.Thumbnail,
                    ReceiverDeviceId = key.Key,
                    EncryptionKey    = key.Value,
                    MessageToken     = request.MessageToken,
                };
                _messageEventManager.DeliverEventToDevice(innerMsg);
            }
            return(response);
        }