public void DeleteAllEstablishedTrust(IUserOrGroup userOrGroup)
 {
     this.persistedUserManagerData.Read(userManagerData =>
     {
         var user = userManagerData.GetUser(userOrGroup.Id);
         user.receiveNotificationEndpointsBySenderToken.Clear();
         user.receiveNotificationSenderTokensByEndpoint.Clear();
     });
 }
        /// <summary>
        /// Establishes trust as part of GetRecipientInfos when trust isn't established
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="receiveNotificationEndpoint"></param>
        /// <param name="recipients"></param>
        /// <param name="callback"></param>
        private void GetRecipientInfos(
            IUserOrGroup sender,
            string receiveNotificationEndpoint,
            string establishTrustEndpoint,
            List<string> recipients,
            string requestedEndpoint,
            Action<EndpointInfo> callback,
            Action<IEnumerable<string>> errorCallback)
        {
            this.BeginEstablishTrust(sender, receiveNotificationEndpoint, establishTrustEndpoint, delegate(string senderToken)
            {
                var recipientInfo = new EndpointInfo()
                {
                    RecipientIdentities = recipients,
                    Endpoint = requestedEndpoint,
                    SenderToken = senderToken
                };

                callback(recipientInfo);
            },
            delegate(Exception e)
            {
                log.Error(
                    string.Format("Could not establish trust between {0} and {1}", sender.Name, StringGenerator.GenerateCommaSeperatedList(recipients)),
                    e);

                errorCallback(recipients);
            });
        }
        /// <summary>
        /// Establishes trust between a user and another server
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="receiveNotificationEndpoint"></param>
        /// <param name="callback"></param>
        private void BeginEstablishTrust(
            IUserOrGroup sender,
            string receiveNotificationEndpoint,
            string establishTrustEndpoint,
            Action<string> callback,
            Action<Exception> errorCallback)
        {
            // Make sure the timer is created
            if (null == EstablishTrustDataTimer)
            {
                Timer timer = new Timer(EstablishTrustDataCleanup, null, 600000, 600000);
                if (null != Interlocked.CompareExchange<Timer>(ref EstablishTrustDataTimer, timer, null))
                    timer.Dispose();
            }

            // Get the avatar
            byte[] avatar;
            ISession session = FileHandlerFactoryLocator.SessionManagerHandler.CreateSession();

            try
            {
                IWebConnection webConnection = new BlockingShellWebConnection(
                    FileHandlerFactoryLocator.WebServer,
                    session,
                    "/Users/" + sender.Name + ".user?Method=GetAvatar",
                    null,
                    null,
                    null,
                    CallingFrom.Web,
                    WebMethod.GET);

                IWebResults webResults = webConnection.ShellTo("/Users/" + sender.Name + ".user?Method=GetAvatar");

                using (Stream stream = webResults.ResultsAsStream)
                {
                    avatar = new byte[stream.Length];
                    stream.Read(avatar, 0, avatar.Length);
                }
            }
            finally
            {
                FileHandlerFactoryLocator.SessionManagerHandler.EndSession(session.SessionId);
            }

            // Hold on to callback information for use after trust is established
            string token;
            using (TimedLock.Lock(EstablishTrustDatasByToken))
            {
                do
                    token = Convert.ToBase64String(SRandom.NextBytes(100));
                while (EstablishTrustDatasByToken.ContainsKey(token));

                EstablishTrustDatasByToken[token] = new EstablishTrustData();
            }

            HttpWebClient httpWebClient = new HttpWebClient();
            httpWebClient.BeginPost(
                establishTrustEndpoint,
                delegate(HttpResponseHandler httpResponseHandler)
                {
                    if (httpResponseHandler.StatusCode == System.Net.HttpStatusCode.Created)
                    {
                        string senderToken;

                        using (TimedLock.Lock(EstablishTrustDatasByToken))
                        {
                            senderToken = EstablishTrustDatasByToken[token].SenderToken;
                            EstablishTrustDatasByToken.Remove(token);
                        }

                        this.persistedUserManagerData.Write(userManagerData =>
                        {
                            var user = userManagerData.GetUser(sender.Id);

                            string oldSenderToken;
                            if (user.receiveNotificationSenderTokensByEndpoint.TryGetValue(receiveNotificationEndpoint, out oldSenderToken))
                                user.receiveNotificationEndpointsBySenderToken.Remove(oldSenderToken);

                            user.receiveNotificationEndpointsBySenderToken[senderToken] = receiveNotificationEndpoint;
                            user.receiveNotificationSenderTokensByEndpoint[receiveNotificationEndpoint] = senderToken;
                        });

                        callback(senderToken);
                    }
                    else
                        errorCallback(new ParticleException.CouldNotEstablishTrust("Couldn't establish trust: " + httpResponseHandler.AsString()));
                },
                errorCallback,
                new KeyValuePair<string, string>("senderIdentity", sender.Identity),
                new KeyValuePair<string, string>("token", token),
                new KeyValuePair<string, string>("avatar", Convert.ToBase64String(avatar)),
                new KeyValuePair<string, string>("loginURL", string.Format("http://{0}/Users/UserDB?Method=OpenIDLogin", FileHandlerFactoryLocator.HostnameAndPort)),
                new KeyValuePair<string, string>("loginURLOpenID", "openid_url"),
                new KeyValuePair<string, string>("loginURLWebFinger", "openid_url"),
                new KeyValuePair<string, string>("loginURLRedirect", "redirect"),
                GenerateSecurityTimestamp());
        }
        /// <summary>
        /// Continues to get more information about recipients after all information about endpoints is loaded
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="recipientIdentities"></param>
        /// <param name="loadedEndpoints"></param>
        /// <param name="callback"></param>
        private void GetRecipientInfos(
            IUserOrGroup sender,
            bool forceRefresh,
            HashSet<string> recipientIdentities, 
            LockFreeQueue<Endpoints> loadedEndpoints,
            ParticleEndpoint particleEndpoint,
            Action<EndpointInfo> callback,
            Action<IEnumerable<string>> errorCallback,
            Action<Exception> exceptionCallback)
        {
            try
            {
                // All of the unique particle endpoints, with the recipients at each
                Dictionary<string, List<string>> recipientsAtEndpoints = new Dictionary<string, List<string>>();
                Dictionary<string, string> establishTrustEndpoints = new Dictionary<string, string>();
                Dictionary<string, string> requestedEndpoints = new Dictionary<string, string>();

                Endpoints particleEndpoints;
                while (loadedEndpoints.Dequeue(out particleEndpoints))
                {
                    string endpoint;
                    if (particleEndpoints.TryGetEndpoint(particleEndpoint, out endpoint))
                    {
                        List<string> users;
                        if (recipientsAtEndpoints.TryGetValue(particleEndpoints[ParticleEndpoint.ReceiveNotification], out users))
                            users.Add(particleEndpoints.OpenIdOrWebFinger);
                        else
                        {
                            users = new List<string>();
                            users.Add(particleEndpoints.OpenIdOrWebFinger);

                            recipientsAtEndpoints[particleEndpoints[ParticleEndpoint.ReceiveNotification]] = users;
                            establishTrustEndpoints[particleEndpoints[ParticleEndpoint.ReceiveNotification]] = particleEndpoints[ParticleEndpoint.EstablishTrust];
                            requestedEndpoints[particleEndpoints[ParticleEndpoint.ReceiveNotification]] = particleEndpoints[particleEndpoint];
                        }
                    }
                }

                if (!forceRefresh)
                {
                    // Load for situations where trust is already established
                    // copy is to avoid locked the database
                    this.persistedUserManagerData.Read(userManagerData =>
                    {
                        var recipientUser = userManagerData.GetUser(sender.Id);

                        foreach (var recipientAndToken in recipientUser.receiveNotificationEndpointsBySenderToken.Where(
                            r => recipientsAtEndpoints.ContainsKey(r.Value)))
                        {
                            var receiveNotificationEndpoint = recipientAndToken.Value;
                            var senderToken = recipientAndToken.Key;

                            string endpoint;
                            if (requestedEndpoints.TryGetValue(receiveNotificationEndpoint, out endpoint))
                            {
                                var recipientInfo = new EndpointInfo()
                                {
                                    RecipientIdentities = recipientsAtEndpoints[receiveNotificationEndpoint],
                                    Endpoint = endpoint,
                                    SenderToken = senderToken
                                };

                                recipientsAtEndpoints.Remove(receiveNotificationEndpoint);

                                callback(recipientInfo);
                            }
                        }
                    });
                }

                // For situations where trust isn't established, establish trust and then use the callback
                foreach (KeyValuePair<string, List<string>> endpointAndRecipients in recipientsAtEndpoints)
                    GetRecipientInfos(
                        sender,
                        endpointAndRecipients.Key,
                        establishTrustEndpoints[endpointAndRecipients.Key],
                        endpointAndRecipients.Value,
                        requestedEndpoints[endpointAndRecipients.Key],
                        callback,
                        errorCallback);
            }
            catch (Exception e)
            {
                exceptionCallback(e);
            }
        }
        // TODO
        // A lot of the logic in this file that's not tied to the DB should move someplace else so that independent implementations can use it
        /// <summary>
        /// Gets information about recipients for sending a notification
        /// </summary>
        /// <param name="openIdOrWebFinger"></param>
        /// <param name="forceRefresh"></param>
        /// <returns></returns>
        public void GetEndpointInfos(
            IUserOrGroup sender,
            bool forceRefresh, 
            IEnumerable<string> recipientIdentitiesArg,
            ParticleEndpoint particleEndpoint,
            Action<EndpointInfo> callback,
            Action<IEnumerable<string>> errorCallback,
            Action<Exception> exceptionCallback)
        {
            HashSet<string> recipientIdentities = new HashSet<string>(recipientIdentitiesArg);

            long outstandingRequests = recipientIdentities.Count;

            LockFreeQueue<Endpoints> loadedEndpoints = new LockFreeQueue<Endpoints>();

            Action<Endpoints> endpointLoaded = delegate(Endpoints endpoints)
            {
                loadedEndpoints.Enqueue(endpoints);

                if (0 == Interlocked.Decrement(ref outstandingRequests))
                    GetRecipientInfos(sender, forceRefresh, recipientIdentities, loadedEndpoints, particleEndpoint, callback, errorCallback, exceptionCallback);
            };

            Action<Exception> endpointException = delegate(Exception e)
            {
                if (0 == Interlocked.Decrement(ref outstandingRequests))
                    GetRecipientInfos(sender, forceRefresh, recipientIdentities, loadedEndpoints, particleEndpoint, callback, errorCallback, exceptionCallback);
            };

            foreach (string openIdOrWebFinger in recipientIdentities)
                Endpoints.GetEndpoints(openIdOrWebFinger, forceRefresh, endpointLoaded, endpointException);
        }