Example #1
0
        internal static async void UpdatePartyParticipants(BubbleGroup bubbleGroup, Action finished = null)
        {
            var service = bubbleGroup.Service;

            if (!ServiceManager.IsRunning(service))
            {
                return;
            }

            if (!bubbleGroup.IsParty)
            {
                if (finished != null)
                {
                    finished();
                }
                return;
            }

            try
            {
                await service.GetBubbleGroupPartyParticipants(bubbleGroup, participants =>
                {
                    // we need to propogate the old participant photos to the new participant list
                    var newParticipants = participants == null ? new List <DisaParticipant>() : participants.ToList();
                    foreach (var oldParticipant in bubbleGroup.Participants)
                    {
                        var newParticipant = newParticipants.FirstOrDefault(x => service.BubbleGroupComparer(x.Address, oldParticipant.Address));
                        if (newParticipant != null)
                        {
                            newParticipant.Photo = oldParticipant.Photo;
                            newParticipant.IsPhotoSetFromService = oldParticipant.IsPhotoSetFromService;
                        }
                    }
                    // move all unknown participants over
                    // if there are more than 100 unknown participants, we'll ignore and force unknown participants to be rebuilt
                    // to prevent an exceedingly large cache
                    if (bubbleGroup.Participants.Count(x => x.Unknown) < 100)
                    {
                        foreach (var unknownParticipant in bubbleGroup.Participants.Where(x => x.Unknown))
                        {
                            if (newParticipants.FirstOrDefault(x => service.BubbleGroupComparer(x.Address, unknownParticipant.Address)) == null)
                            {
                                newParticipants.Add(unknownParticipant);
                            }
                        }
                    }
                    bubbleGroup.Participants = new ThreadSafeList <DisaParticipant>(newParticipants);
                    bubbleGroup.IsParticipantsSetFromService = true;
                    if (finished == null)
                    {
                        BubbleGroupEvents.RaiseBubblesUpdated(bubbleGroup);
                    }
                    else
                    {
                        finished();
                    }
                });
            }
            catch (Exception ex)
            {
                Utils.DebugPrint("Error updating bubble group participants: " + service.Information.ServiceName +
                                 " - " + bubbleGroup.Address + ": " + ex);
                if (finished != null)
                {
                    finished();
                }
            }
        }
 public static bool InsertBubbleByTime(BubbleGroup group, VisualBubble bubble, int maxDepth = 1000)
 {
     return(InsertBubblesByTime(@group, new [] { bubble }, maxDepth));
 }
 public static bool UpdateBubble(BubbleGroup group, VisualBubble bubble, int searchDepth = 100)
 {
     return(UpdateBubble(@group, new[] { bubble }, searchDepth));
 }
Example #4
0
        private static List <VisualBubble> LoadBubblesIntoQueryAgent(Agent innerGroupAgent, BubbleGroup innerGroup,
                                                                     long currentTime, int maxLoadPerGroup, LoadBubblesIntoStateHolder innerGroupState)
        {
            try
            {
                var task = innerGroupAgent.LoadBubbles(innerGroup, currentTime, maxLoadPerGroup);
                task.Wait();
                var loadedBubbles = task.Result;

                if (loadedBubbles == null || loadedBubbles.Count == 0)
                {
                    innerGroupState.Dead = true;
                    return(null);
                }
                else
                {
                    return(loadedBubbles);
                }
            }
            catch (Exception ex)
            {
                Utils.DebugPrint("Loading bubbles for service " +
                                 innerGroup.Service.Information.ServiceName + " on group " + innerGroup.ID + " failed: " + ex);
                innerGroupState.Dead = true;
            }

            return(null);
        }
Example #5
0
        private static IEnumerable <bool> LoadBubblesInto(BubbleGroup group, List <VisualBubble> listToLoadInto,
                                                          LoadBubbles instance)
        {
            if (!listToLoadInto.Any())
            {
                yield break;
            }

            Utils.DebugPrint("Loading into convo " + group.ID);

            lock (SyncLock)
            {
                Utils.DebugPrint("Loading into convo " + group.ID + " (after lock)");

                var unifiedGroup = group as UnifiedBubbleGroup;
                var groups       = unifiedGroup != null ? unifiedGroup.Groups : new List <BubbleGroup>()
                {
                    group
                };

                const int MaxLoadPerGroup = 100;
                var       currentTime     = listToLoadInto.Min(x => x.Time);

                var bubbleGroupStates = new Dictionary <BubbleGroup, LoadBubblesIntoStateHolder>();
                foreach (var innerGroup in groups)
                {
                    bubbleGroupStates[innerGroup] = new LoadBubblesIntoStateHolder();
                }

                EventHandler <Service> serviceStarted = (sender, e) =>
                {
                    foreach (var state in bubbleGroupStates)
                    {
                        if (state.Key.Service == e)
                        {
                            state.Value.Dead = false;
                        }
                    }
                };

                ServiceEvents.Started  += serviceStarted;
                instance.ServiceStarted = serviceStarted;

                while (true)
                {
                    var listToLoadIntoCount = listToLoadInto.Count;
                    var bubbleLoads         = new Dictionary <BubbleGroup, List <Tuple <VisualBubble, bool> > >();

                    foreach (var innerGroup in groups)
                    {
                        var innerGroupState = bubbleGroupStates[innerGroup];

                        if (innerGroupState.Dead)
                        {
                            bubbleLoads[innerGroup] = null;
                            continue;
                        }

                        if (innerGroupState.LocalFinished)
                        {
                            var agentBubbles = LoadBubblesIntoDoAgent(innerGroup, currentTime, MaxLoadPerGroup, innerGroupState);
                            if (agentBubbles != null)
                            {
                                bubbleLoads[innerGroup] = agentBubbles.Select(x =>
                                                                              new Tuple <VisualBubble, bool>(x, true)).ToList();
                            }
                            else
                            {
                                bubbleLoads[innerGroup] = null;
                            }
                        }
                        else
                        {
                            var localBubbles = new List <VisualBubble>();
                            innerGroupState.LocalCursor = BubbleGroupDatabase.FetchBubblesAt(innerGroup,
                                                                                             currentTime, MaxLoadPerGroup, ref localBubbles, innerGroupState.LocalCursor);
                            if (innerGroupState.LocalCursor == 0 || localBubbles.Count == 0)
                            {
                                innerGroupState.LocalFinished = true;
                            }

                            if (localBubbles.Count == MaxLoadPerGroup)
                            {
                                bubbleLoads[innerGroup] = localBubbles.Select(x =>
                                                                              new Tuple <VisualBubble, bool>(x, false)).ToList();
                            }
                            else
                            {
                                var innerGroupComparer = innerGroup.Service as Comparer;

                                if (innerGroupComparer != null)
                                {
                                    var agentBubbles = LoadBubblesIntoDoAgent(innerGroup, currentTime, MaxLoadPerGroup, innerGroupState);

                                    if (agentBubbles == null)
                                    {
                                        bubbleLoads[innerGroup] = localBubbles.Select(x =>
                                                                                      new Tuple <VisualBubble, bool>(x, false)).ToList();
                                    }
                                    else
                                    {
                                        var innerGroupAgent = innerGroup.Service as Agent;
                                        var combined        = new List <Tuple <VisualBubble, bool> >();

                                        // combine them: take all agent bubbles, and then try to replace the clouds with the locals.
                                        // what can't be replaced becomes the inserts.

                                        for (int i = 0; i < agentBubbles.Count; i++)
                                        {
                                            var agentBubble = agentBubbles[i];
                                            var localBubble =
                                                localBubbles.FirstOrDefault(x => innerGroupComparer.LoadBubblesComparer(x, agentBubble));
                                            if (localBubble != null)
                                            {
                                                combined.Add(new Tuple <VisualBubble, bool>(localBubble, false));
                                            }
                                            else
                                            {
                                                combined.Add(new Tuple <VisualBubble, bool>(agentBubble, true));
                                            }
                                        }

                                        bubbleLoads[innerGroup] = combined;
                                    }
                                }
                                else
                                {
                                    bubbleLoads[innerGroup] = localBubbles.Select(x =>
                                                                                  new Tuple <VisualBubble, bool>(x, false)).ToList();
                                }
                            }
                        }
                    }

                    // if all the sync agents failed, then obviously loading more bubbles in trivially failed
                    if (bubbleGroupStates.Count(x => x.Value.Dead) == groups.Count)
                    {
                        yield break;
                    }

                    // insert the bubbles into the disa bubble group with two conditions
                    // a) must not be bubble retreived from disa bubble group
                    // b) must not be a duplicate already in the list to load into
                    var listToLoadIntoBubblesOnTime = listToLoadInto.Where(x => x.Time == currentTime).ToList();
                    foreach (var bubbleLoad in bubbleLoads)
                    {
                        if (bubbleLoad.Value == null)
                        {
                            continue;
                        }

                        var bubbleGroup     = bubbleLoad.Key;
                        var bubblesToInsert = LoadBubblesIntoRemoveDuplicates(bubbleLoad.Value.Where(x => x.Item2)
                                                                              .Select(x => x.Item1).ToList(), listToLoadIntoBubblesOnTime);
                        if (bubblesToInsert.Any())
                        {
                            BubbleGroupDatabase.InsertBubblesByTime(bubbleGroup,
                                                                    bubblesToInsert.ToArray(), int.MaxValue, true, true);
                        }
                    }

                    // find the greatest minimum time of all the bubble loads
                    // and merge the bubble loads against that

                    var greatestMin = 0L;
                    foreach (var bubbleLoad in bubbleLoads)
                    {
                        if (bubbleLoad.Value == null || !bubbleLoad.Value.Any())
                        {
                            continue;
                        }

                        var min = bubbleLoad.Value.Min(x => x.Item1.Time);
                        if (min > greatestMin)
                        {
                            greatestMin = min;
                        }
                    }

                    var mergedBubbles = new List <VisualBubble>();
                    foreach (var bubbleLoad in bubbleLoads)
                    {
                        if (bubbleLoad.Value != null)
                        {
                            var bubblesToMerge = bubbleLoad.Value.Where(x =>
                                                                        x.Item1.Time >= greatestMin).Select(x => x.Item1).ToList();
                            foreach (var bubbleToMerge in bubblesToMerge)
                            {
                                bubbleToMerge.BubbleGroupReference = bubbleLoad.Key;
                            }
                            mergedBubbles.AddRange(bubblesToMerge);
                        }
                    }
                    mergedBubbles.TimSort((x, y) => x.Time.CompareTo(y.Time));

                    // insert the merged bubbles into the list to load into, making sure to
                    // remove and duplicates encountered.
                    listToLoadInto.InsertRange(0, mergedBubbles);
                    LoadBubblesIntoRemoveDuplicates(listToLoadInto, currentTime);

                    currentTime = mergedBubbles.First().Time;

                    // if the count wasn't altered, we've hit the end
                    if (listToLoadIntoCount == listToLoadInto.Count)
                    {
                        yield break;
                    }

                    yield return(true);
                }
            }
        }
Example #6
0
        public static IEnumerable <bool> LoadBubblesIntoOffline(BubbleGroup group, List <VisualBubble> listToLoadInto)
        {
            if (!listToLoadInto.Any())
            {
                yield break;
            }

            Utils.DebugPrint("Loading into convo (offline): " + group.ID);

            var unifiedGroup = group as UnifiedBubbleGroup;
            var groups       = unifiedGroup != null ? unifiedGroup.Groups : new List <BubbleGroup>()
            {
                group
            };

            const int MaxLoadPerGroup = 100;
            var       currentTime     = listToLoadInto.Min(x => x.Time);

            var bubbleGroupStates = new Dictionary <BubbleGroup, LoadBubblesIntoStateHolder>();

            foreach (var innerGroup in groups)
            {
                bubbleGroupStates[innerGroup] = new LoadBubblesIntoStateHolder();
            }

            while (true)
            {
                var listToLoadIntoCount = listToLoadInto.Count;
                var bubbleLoads         = new Dictionary <BubbleGroup, List <Tuple <VisualBubble, bool> > >();

                foreach (var innerGroup in groups)
                {
                    var innerGroupState = bubbleGroupStates[innerGroup];

                    if (innerGroupState.LocalFinished)
                    {
                        bubbleLoads[innerGroup] = null;
                    }
                    else
                    {
                        var localBubbles = new List <VisualBubble>();
                        innerGroupState.LocalCursor = BubbleGroupDatabase.FetchBubblesAt(innerGroup,
                                                                                         currentTime, MaxLoadPerGroup, ref localBubbles, innerGroupState.LocalCursor);
                        if (innerGroupState.LocalCursor == 0 || localBubbles.Count == 0)
                        {
                            innerGroupState.LocalFinished = true;
                        }

                        bubbleLoads[innerGroup] = localBubbles.Select(x =>
                                                                      new Tuple <VisualBubble, bool>(x, false)).ToList();
                    }
                }

                // find the greatest minimum time of all the bubble loads
                // and merge the bubble loads against that

                var greatestMin = 0L;
                foreach (var bubbleLoad in bubbleLoads)
                {
                    if (bubbleLoad.Value == null || !bubbleLoad.Value.Any())
                    {
                        continue;
                    }

                    var min = bubbleLoad.Value.Min(x => x.Item1.Time);
                    if (min > greatestMin)
                    {
                        greatestMin = min;
                    }
                }

                var mergedBubbles = new List <VisualBubble>();
                foreach (var bubbleLoad in bubbleLoads)
                {
                    if (bubbleLoad.Value != null)
                    {
                        var bubblesToMerge = bubbleLoad.Value.Where(x =>
                                                                    x.Item1.Time >= greatestMin).Select(x => x.Item1).ToList();
                        foreach (var bubbleToMerge in bubblesToMerge)
                        {
                            bubbleToMerge.BubbleGroupReference = bubbleLoad.Key;
                        }
                        mergedBubbles.AddRange(bubblesToMerge);
                    }
                }
                mergedBubbles.TimSort((x, y) => x.Time.CompareTo(y.Time));

                // insert the merged bubbles into the list to load into, making sure to
                // remove and duplicates encountered.
                listToLoadInto.InsertRange(0, mergedBubbles);
                LoadBubblesIntoRemoveDuplicates(listToLoadInto, currentTime);

                if (mergedBubbles.Any())
                {
                    currentTime = mergedBubbles.First().Time;
                }

                // if the count wasn't altered, we've hit the end
                if (listToLoadIntoCount == listToLoadInto.Count)
                {
                    yield break;
                }

                yield return(true);
            }
        }
 public abstract bool SwitchCurrentBubbleGroupOnUI(BubbleGroup group);
Example #8
0
 public abstract Task GetBubbleGroupLegibleId(BubbleGroup group, Action <string> result);
Example #9
0
 public abstract Task GetBubbleGroupName(BubbleGroup group, Action <string> result);
Example #10
0
 public override Task GetBubbleGroupUnknownPartyParticipant(BubbleGroup group, string unknownPartyParticipant, Action <DisaParticipant> result)
 {
     throw new NotImplementedException();
 }
Example #11
0
 public override Task GetBubbleGroupLastOnline(BubbleGroup @group, Action <long> result)
 {
     return(Task.Factory.StartNew(() => result(0)));
 }
Example #12
0
 public override Task GetBubbleGroupPartyParticipants(BubbleGroup @group, Action <DisaParticipant[]> result)
 {
     throw new NotImplementedException();
 }
Example #13
0
 public override Task GetBubbleGroupPhoto(BubbleGroup @group, Action <DisaThumbnail> result)
 {
     return(Task.Factory.StartNew(() => result(null)));
 }
Example #14
0
 public override Task GetBubbleGroupName(BubbleGroup @group, Action <string> result)
 {
     return(Task.Factory.StartNew(() => result(Name)));
 }
 public static void RemoveUnread(BubbleGroup group, bool updateUi = true)
 {
     SetUnread(@group, false, updateUi);
 }
Example #16
0
 public abstract Task GetBubbleGroupPhoto(BubbleGroup group, Action <DisaThumbnail> result);
 public static void SetReadTimes(BubbleGroup group, DisaReadTime[] readTimes)
 {
     @group.ReadTimes = readTimes == null ? null : (!readTimes.Any() ? null : readTimes);
 }
Example #18
0
 public abstract Task GetBubbleGroupPartyParticipants(BubbleGroup group, Action <DisaParticipant[]> result);
Example #19
0
        public static Task Sync(BubbleGroup group, bool force = false)
        {
            return(Task.Factory.StartNew(() =>
            {
                Utils.DebugPrint("Syncing convo " + group.ID);

                lock (SyncLock)
                {
                    Utils.DebugPrint("Syncing convo " + group.ID + " (after lock)");

                    var somethingSynced = false;

                    var groupsToSync = new List <BubbleGroup>();

                    var unifiedGroup = group as UnifiedBubbleGroup;
                    if (unifiedGroup != null)
                    {
                        groupsToSync.AddRange(unifiedGroup.Groups);
                    }
                    else
                    {
                        groupsToSync.Add(group);
                    }

                    foreach (var groupToSync in groupsToSync)
                    {
                        if (!groupToSync.NeedsSync && !force)
                        {
                            continue;
                        }

                        var groupToSyncAgent = groupToSync.Service as Agent;

                        if (groupToSyncAgent == null)
                        {
                            continue;
                        }

                        if (!ServiceManager.IsRunning(groupToSync.Service))
                        {
                            continue;
                        }

                        somethingSynced = true;

                        try
                        {
                            ReSync:
                            var syncTime = Time.GetNowUnixTimestamp();
                            var syncTask = groupToSyncAgent.Sync(groupToSync, Database.GetActionId(groupToSync));
                            syncTask.Wait();
                            var syncResult = syncTask.Result;
                            if (!syncResult.EmptyResult && !syncResult.NullActionId && !syncResult.JustRefresh)
                            {
                                lock (BubbleGroupDatabase.OperationLock)
                                {
                                    if (syncResult.ResultType == Result.Type.Purge)
                                    {
                                        if (BubbleGroupManager.LastBubbleSentTimestamps.ContainsKey(groupToSync.ID))
                                        {
                                            lock (BubbleGroupManager.LastBubbleSentTimestamps)
                                            {
                                                var lastBubbleSentTime = BubbleGroupManager.LastBubbleSentTimestamps[groupToSync.ID];
                                                if (lastBubbleSentTime >= syncTime)
                                                {
                                                    Utils.DebugPrint("Sync has detected that a bubble was sent during the time the sync was fetched. Redoing sync...");
                                                    goto ReSync;
                                                }
                                            }
                                        }
                                        Utils.DebugPrint("Sync is purging the database for bubble group " + groupToSync.ID
                                                         + " on service " + groupToSync.Service.Information.ServiceName);
                                        BubbleGroupDatabase.Kill(groupToSync);
                                    }
                                    if (!syncResult.EmptyInserts)
                                    {
                                        Utils.DebugPrint("Sync is inserting " + syncResult.Inserts.Length
                                                         + " bubbles into " + groupToSync.ID + " on service " + groupToSync.Service.Information.ServiceName);
                                        if (syncResult.ResultType == Result.Type.Purge)
                                        {
                                            syncResult.Inserts.TimSort((x, y) => x.Time.CompareTo(y.Time));
                                            BubbleGroupDatabase.AddBubbles(groupToSync, syncResult.Inserts);
                                            // If a purge is issued, then we need to carry over all sending bubbles to the new BubbleGroup being created.
                                            // Additionally, we need to remove any sending bubbles that are already on the insert list to avoid duplicates.
                                            var sendingBubbles = BubbleManager.FetchAllSending(groupToSync).ToList();
                                            var sendingBubblesFiltered = new List <VisualBubble>();
                                            foreach (var sendingBubble in sendingBubbles)
                                            {
                                                var hasInsertBubble = syncResult.Inserts.FirstOrDefault(x => x.ID == sendingBubble.ID) != null;
                                                if (!hasInsertBubble)
                                                {
                                                    sendingBubblesFiltered.Add(sendingBubble);
                                                }
                                            }
                                            if (sendingBubblesFiltered.Any())
                                            {
                                                BubbleGroupDatabase.InsertBubblesByTime(groupToSync, sendingBubblesFiltered.ToArray(), SearchDepth);
                                            }
                                        }
                                        else
                                        {
                                            BubbleGroupDatabase.InsertBubblesByTime(groupToSync, syncResult.Inserts, SearchDepth);
                                        }
                                    }
                                    if (!syncResult.EmptyUpdates)
                                    {
                                        Utils.DebugPrint("Sync is updating " + syncResult.Updates.Length
                                                         + " bubbles into " + groupToSync.ID + " on service " + groupToSync.Service.Information.ServiceName);
                                        BubbleManager.Update(groupToSync, syncResult.Updates, SearchDepth);
                                    }
                                }
                            }
                            else
                            {
                                Utils.DebugPrint("Sync for bubble group " +
                                                 groupToSync.ID + " on service " + groupToSync.Service.Information.ServiceName +
                                                 " returned an empty result (" + syncResult.ResultType.ToString() + ").");
                            }
                            if (!syncResult.NullActionId)
                            {
                                Database.SetActionId(groupToSync, syncResult.NewActionId);
                            }
                            groupToSync.NeedsSync = false;
                        }
                        catch (Exception ex)
                        {
                            Utils.DebugPrint("Failed to sync bubble group " + groupToSync.ID
                                             + " on service " + groupToSync.Service.Information.ServiceName + ": " + ex);
                        }
                    }

                    if (somethingSynced)
                    {
                        lock (BubbleGroupDatabase.OperationLock)
                        {
                            // Here we need to replace all the sending or downloading bubbles with the 'real' ones.
                            // If this is never done, then on the UI, a 'sending' will never be set to 'sent', and any download
                            // progress will never be updated. Why? Different memory objects.
                            var sendingBubbles = BubbleManager.FetchAllSendingAndDownloading(group).ToList();
                            BubbleGroupFactory.UnloadFullLoad(group);
                            BubbleGroupFactory.LoadFullyIfNeeded(group, true);
                            if (sendingBubbles.Any())
                            {
                                BubbleManager.Replace(group, sendingBubbles);
                            }
                            group.RaiseBubblesSynced();
                        }
                    }
                }
            }));
        }
Example #20
0
 public abstract Task GetBubbleGroupUnknownPartyParticipant(BubbleGroup group, string unknownPartyParticipant, Action <DisaParticipant> result);
Example #21
0
 public static IEnumerable <VisualBubble> ReadBubblesFromDatabase(BubbleGroup group)
 {
     return(BubbleGroupDatabase.FetchBubbles(group, SearchDepth, false));
 }
Example #22
0
 public abstract Task GetBubbleGroupLastOnline(BubbleGroup group, Action <long> result);
Example #23
0
 public LoadBubbles(BubbleGroup group, List <VisualBubble> listToLoadInto)
 {
     _enumerator = LoadBubblesInto(group, listToLoadInto, this).GetEnumerator();
 }
Example #24
0
 public virtual Task OpenedBubbleGroup(BubbleGroup group)
 {
     return(Task.FromResult(0));
 }
Example #25
0
        public static Task Sync(BubbleGroup group, bool force = false)
        {
            return(Task.Factory.StartNew(() =>
            {
                Utils.DebugPrint("Syncing convo " + group.ID);

                lock (SyncLock)
                {
                    Utils.DebugPrint("Syncing convo " + group.ID + " (after lock)");

                    var somethingSynced = true;

                    var groupsToSync = new List <BubbleGroup>();

                    var unifiedGroup = group as UnifiedBubbleGroup;
                    if (unifiedGroup != null)
                    {
                        groupsToSync.AddRange(unifiedGroup.Groups);
                    }
                    else
                    {
                        groupsToSync.Add(group);
                    }

                    foreach (var groupToSync in groupsToSync)
                    {
                        if (!groupToSync.NeedsSync && !force)
                        {
                            continue;
                        }

                        var groupToSyncAgent = groupToSync.Service as Agent;

                        if (groupToSyncAgent == null)
                        {
                            continue;
                        }

                        if (!ServiceManager.IsRunning(groupToSync.Service))
                        {
                            continue;
                        }

                        try
                        {
                            var syncTask = groupToSyncAgent.Sync(groupToSync, Database.GetActionId(groupToSync));
                            syncTask.Wait();
                            var syncResult = syncTask.Result;
                            if (!syncResult.EmptyResult && !syncResult.NullActionId && !syncResult.JustRefresh)
                            {
                                lock (BubbleGroupDatabase.OperationLock)
                                {
                                    if (syncResult.ResultType == Result.Type.Purge)
                                    {
                                        Utils.DebugPrint("Sync is purging the database for bubble group " + groupToSync.ID
                                                         + " on service " + groupToSync.Service.Information.ServiceName);
                                        BubbleGroupDatabase.Kill(groupToSync);
                                    }
                                    if (!syncResult.EmptyInserts)
                                    {
                                        Utils.DebugPrint("Sync is inserting " + syncResult.Inserts.Length
                                                         + " bubbles into " + groupToSync.ID + " on service " + groupToSync.Service.Information.ServiceName);
                                        if (syncResult.ResultType == Result.Type.Purge)
                                        {
                                            syncResult.Inserts.TimSort((x, y) => x.Time.CompareTo(y.Time));
                                            BubbleGroupDatabase.AddBubbles(groupToSync, syncResult.Inserts);
                                        }
                                        else
                                        {
                                            BubbleGroupDatabase.InsertBubblesByTime(groupToSync, syncResult.Inserts, SearchDepth);
                                        }
                                    }
                                    if (!syncResult.EmptyUpdates)
                                    {
                                        Utils.DebugPrint("Sync is updating " + syncResult.Updates.Length
                                                         + " bubbles into " + groupToSync.ID + " on service " + groupToSync.Service.Information.ServiceName);
                                        BubbleManager.Update(groupToSync, syncResult.Updates, SearchDepth);
                                    }
                                    Database.SetActionId(groupToSync, syncResult.NewActionId);
                                }
                            }
                            else
                            {
                                Utils.DebugPrint("Sync for bubble group " +
                                                 groupToSync.ID + " on service " + groupToSync.Service.Information.ServiceName +
                                                 " returned an empty result (" + syncResult.ResultType.ToString() + ").");
                                somethingSynced = syncResult.JustRefresh;
                            }
                            groupToSync.NeedsSync = false;
                        }
                        catch (Exception ex)
                        {
                            Utils.DebugPrint("Failed to sync bubble group " + groupToSync.ID
                                             + " on service " + groupToSync.Service.Information.ServiceName + ": " + ex);
                            somethingSynced = false;
                        }
                    }

                    if (somethingSynced)
                    {
                        lock (BubbleGroupDatabase.OperationLock)
                        {
                            var sendingBubbles = BubbleManager.FetchAllSendingAndDownloading(group).ToList();
                            BubbleGroupFactory.UnloadFullLoad(group);
                            BubbleGroupFactory.LoadFullyIfNeeded(group, true);
                            if (sendingBubbles.Any())
                            {
                                BubbleManager.Replace(group, sendingBubbles);
                            }
                            group.RaiseBubblesSynced();
                        }
                    }
                }
            }));
        }
        public static BubbleGroup GetSending(BubbleGroup group)
        {
            var unifiedGroup = @group as UnifiedBubbleGroup;

            return(unifiedGroup == null ? @group : unifiedGroup.SendingGroup);
        }
        public static bool InsertBubblesByTime(BubbleGroup group, VisualBubble[] bubbles, int maxDepth = 1000, bool guidCheck = false, bool insertAtTop = false)
        {
            //we can't operate if a thread/worker is concurrently processing a new bubble
            lock (OperationLock)
            {
                var groupDatabaseLocation = GetLocation(@group);
                var bubbleTuples          = bubbles.Select(x => new Tuple <long, VisualBubble>(x.Time, x)).ToList();

                VisualBubble lastBubble     = null;
                long         insertPosition = -1;

                using (var stream = File.Open(groupDatabaseLocation, FileMode.Open, FileAccess.ReadWrite))
                {
                    stream.Seek(stream.Length, SeekOrigin.Begin);

                    for (var i = 0; i < (maxDepth != -1 ? maxDepth : Int32.MaxValue); i++)
                    {
                        if (stream.Position == 0)
                        {
                            if (insertAtTop)
                            {
                                insertPosition = 0;

                                var cut = new byte[(int)stream.Length];
                                stream.Read(cut, 0, (int)stream.Length);
                                stream.Position = 0;

                                var bubblesToInsert = bubbleTuples.Select(x => x.Item2).ToList();
                                bubblesToInsert.TimSort((x, y) => x.Time.CompareTo(y.Time));
                                foreach (var bubbleToInsert in bubblesToInsert)
                                {
                                    using (var ms = new MemoryStream())
                                    {
                                        Serializer.Serialize(ms, bubbleToInsert);

                                        var bubbleToInsertData   = ms.ToArray();
                                        var bubbleToInsertHeader = bubbleToInsert.GetType().Name + ":" + bubbleToInsert.ID
                                                                   + ":" + bubbleToInsert.Time;

                                        BubbleGroupDatabasePrimitives.WriteBubbleData(stream, bubbleToInsertData);
                                        BubbleGroupDatabasePrimitives.WriteBubbleHeader(stream, bubbleToInsertHeader);
                                    }
                                }

                                stream.Write(cut, 0, cut.Length);
                                stream.SetLength(stream.Position);
                            }
                            else
                            {
                                break;
                            }
                        }

                        byte[] headerBytes;
                        int    headerBytesLength;
                        int    endPosition;

                        BubbleGroupDatabasePrimitives.ReadBubbleHeader(stream, out headerBytes, out headerBytesLength);
                        BubbleGroupDatabasePrimitives.FindBubbleHeaderDelimiter(headerBytes, headerBytesLength, 0, out endPosition);
                        var bubbleDataLength = BubbleGroupDatabasePrimitives.JumpBubbleData(stream); //we need to seek over the data.

                        var guid = BubbleGroupDatabasePrimitives.ReadBubbleHeaderStruct(headerBytes, headerBytesLength,
                                                                                        endPosition + 1, out endPosition);
                        var time = BubbleGroupDatabasePrimitives.AsciiBytesToString(headerBytes, endPosition + 1,
                                                                                    headerBytesLength);
                        long longTime;
                        Int64.TryParse(time, out longTime);

                        {
                            var bubblesToInsert = bubbleTuples.Where(x =>
                            {
                                if (guidCheck && guid == x.Item2.ID)
                                {
                                    return(false);
                                }

                                if (longTime <= x.Item1)
                                {
                                    return(true);
                                }

                                return(false);
                            }).ToList();
                            if (!bubblesToInsert.Any())
                            {
                                continue;
                            }

                            var bubbleSize     = headerBytesLength + bubbleDataLength + 8;
                            var insertLocation = stream.Position + bubbleSize;
                            stream.Seek(insertLocation, SeekOrigin.Begin);

                            var cutLength = stream.Length - insertLocation;
                            var cut       = new byte[cutLength];
                            stream.Read(cut, 0, (int)cutLength); //should always work as long as the count ain't crazy high

                            stream.Seek(insertLocation, SeekOrigin.Begin);

                            insertPosition = stream.Position;

                            foreach (var bubbleToInsert in bubblesToInsert.Select(x => x.Item2))
                            {
                                using (var ms = new MemoryStream())
                                {
                                    Serializer.Serialize(ms, bubbleToInsert);

                                    var bubbleToInsertData   = ms.ToArray();
                                    var bubbleToInsertHeader = bubbleToInsert.GetType().Name + ":" + bubbleToInsert.ID
                                                               + ":" + bubbleToInsert.Time;

                                    BubbleGroupDatabasePrimitives.WriteBubbleData(stream, bubbleToInsertData);
                                    BubbleGroupDatabasePrimitives.WriteBubbleHeader(stream, bubbleToInsertHeader);
                                }
                            }

                            stream.Write(cut, 0, cut.Length);
                            stream.SetLength(stream.Position);

                            // adding to end
                            if (cut.Length == 0)
                            {
                                lastBubble = bubblesToInsert.Last().Item2;
                            }

                            foreach (var bubbleToInsert in bubblesToInsert)
                            {
                                bubbleTuples.Remove(bubbleToInsert);
                            }

                            if (!bubbleTuples.Any())
                            {
                                break;
                            }
                        }
                    }

                    BubbleGroupIndex.UpdateLastBubbleOrAndLastModifiedIndex(group.ID, lastBubble, insertPosition);
                }

                if (!bubbleTuples.Any())
                {
                    return(true);
                }

                return(false);
            }
        }
 public static bool IsUnified(BubbleGroup group)
 {
     return(@group as UnifiedBubbleGroup != null);
 }
        public static bool UpdateBubble(BubbleGroup group, VisualBubble[] bubbles, int searchDepth = 100)
        {
            //we can't operate if a thread/worker is concurrently processing a new bubble
            lock (OperationLock)
            {
                var groupDatabaseLocation = GetLocation(@group);
                var bubbleTuples          = bubbles.Select(x => new Tuple <string, VisualBubble>(x.ID, x)).ToList();

                VisualBubble lastBubble     = null;
                long         insertPosition = -1;

                using (var stream = File.Open(groupDatabaseLocation, FileMode.Open, FileAccess.ReadWrite))
                {
                    stream.Seek(stream.Length, SeekOrigin.Begin);

                    for (var i = 0; i < (searchDepth != -1 ? searchDepth : Int32.MaxValue); i++)
                    {
                        if (stream.Position == 0)
                        {
                            break;
                        }

                        byte[] headerBytes;
                        int    headerBytesLength;
                        int    endPosition;

                        BubbleGroupDatabasePrimitives.ReadBubbleHeader(stream, out headerBytes, out headerBytesLength);
                        BubbleGroupDatabasePrimitives.FindBubbleHeaderDelimiter(headerBytes, headerBytesLength, 0, out endPosition);
                        var bubbleDataLength = BubbleGroupDatabasePrimitives.JumpBubbleData(stream); //we need to seek over the data.

                        var guid = BubbleGroupDatabasePrimitives.ReadBubbleHeaderStruct(headerBytes, headerBytesLength,
                                                                                        endPosition + 1, out endPosition);

                        Tuple <string, VisualBubble> bubbleTuple;
                        if ((bubbleTuple = bubbleTuples.FirstOrDefault(x => x.Item1 == guid)) == null)
                        {
                            continue;
                        }

                        var b = bubbleTuple.Item2;

                        var bubbleSize = headerBytesLength + bubbleDataLength + 8;
                        var cutStart   = stream.Position + bubbleSize;
                        var cutLength  = stream.Length - cutStart;

                        insertPosition = stream.Position;

                        using (var ms = new MemoryStream())
                        {
                            Serializer.Serialize(ms, b);

                            var updatedBubbleData   = ms.ToArray();
                            var updatedBubbleHeader = b.GetType().Name + ":" + b.ID + ":" + b.Time;
                            var updatedBubbleSize   = updatedBubbleHeader.Length + updatedBubbleData.Length + 8;

                            var bubbleInjectDelta = bubbleSize - updatedBubbleSize;
                            //enough room
                            if (bubbleInjectDelta != 0)
                            {
                                var injectPosition = stream.Position;

                                stream.Position = cutStart;          //higher
                                var cut = new byte[cutLength];
                                stream.Read(cut, 0, (int)cutLength); //should always work as long as the count ain't crazy high

                                //var bw = new BinaryWriter(stream, Encoding.ASCII);
                                stream.Position = injectPosition;
                                BubbleGroupDatabasePrimitives.WriteBubbleData(stream, updatedBubbleData);
                                BubbleGroupDatabasePrimitives.WriteBubbleHeader(stream, updatedBubbleHeader);

                                stream.Write(cut, 0, cut.Length);
                                stream.SetLength(stream.Position);
                            }
                            //they're the same!
                            else if (bubbleInjectDelta == 0)
                            {
                                //var bw = new BinaryWriter(stream, Encoding.ASCII);
                                BubbleGroupDatabasePrimitives.WriteBubbleData(stream, updatedBubbleData);
                                BubbleGroupDatabasePrimitives.WriteBubbleHeader(stream, updatedBubbleHeader);
                            }
                        }

                        if (cutLength == 0)
                        {
                            lastBubble = b;
                        }

                        bubbleTuples.Remove(bubbleTuple);
                        if (!bubbleTuples.Any())
                        {
                            break;
                        }
                    }

                    BubbleGroupIndex.UpdateLastBubbleOrAndLastModifiedIndex(group.ID, lastBubble, insertPosition);
                }

                if (!bubbleTuples.Any())
                {
                    return(true);
                }

                return(false);
            }
        }
Example #30
0
        internal static BubbleGroup Group(VisualBubble vb, bool resend = false, bool insertAtBottom = false)
        {
            lock (BubbleGroupDatabase.OperationLock)
            {
                Utils.DebugPrint("Grouping an " + vb.Direction + " bubble on service " + vb.Service.Information.ServiceName);

                AddUrlMarkupIfNeeded(vb);

                var theGroup =
                    BubbleGroupManager.FindWithAddress(vb.Service, vb.Address);

                BubbleGroupFactory.LoadFullyIfNeeded(theGroup);

                var duplicate = false;
                var newGroup  = false;
                if (theGroup == null)
                {
                    Utils.DebugPrint(vb.Service.Information.ServiceName + " unable to find suitable group. Creating a new one.");

                    theGroup = new BubbleGroup(vb, null, false);

                    newGroup = true;

                    Utils.DebugPrint("GUID of new group: " + theGroup.ID);

                    BubbleGroupSettingsManager.SetUnreadIndicatorGuid(theGroup, theGroup.LastBubbleSafe().ID, true);

                    vb.Service.NewBubbleGroupCreated(theGroup).ContinueWith(x =>
                    {
                        // force the UI to refetch the photo
                        theGroup.IsPhotoSetFromService = false;
                        SendSubscribe(theGroup, true);
                        BubbleGroupUpdater.Update(theGroup);
                    });

                    BubbleGroupManager.BubbleGroupsAdd(theGroup);
                }
                else
                {
                    if (resend)
                    {
                        if (vb.Status == Bubble.BubbleStatus.Failed)
                        {
                            UpdateStatus(vb, Bubble.BubbleStatus.Waiting, theGroup);
                        }
                        return(theGroup);
                    }

                    // Does the Service for this VisualBubble require that the VisualBubble's IdService and IdService2
                    // be distinct?
                    var visualBubbleServiceId = vb.Service as IVisualBubbleServiceId;
                    if (visualBubbleServiceId != null &&
                        visualBubbleServiceId.DisctinctIncomingVisualBubbleIdServices())
                    {
                        // Ok, we need to be distinct, BUT do the VisualBubble Type's have to be distinct as well?
                        var checkType = true;
                        if (!DisaFrameworkMethods.Missing(vb.Service, DisaFrameworkMethods.IVisualBubbleServiceIdCheckType))
                        {
                            checkType = visualBubbleServiceId.CheckType();
                        }

                        // Ok, now does the Service have special additional logic it wants to use for the distinction comparison?
                        // Example: For Telegram, we allow an ImageBubble immediately followed by a TextBubble to have the
                        //          same VisualBubble.IdService - as this represents an image with a caption in Telegram.
                        if (DisaFrameworkMethods.Missing(vb.Service, DisaFrameworkMethods.IVisualBubbleServiceIdVisualBubbleIdComparer))
                        {
                            // Normal distinction checks
                            if (vb.IdService != null)
                            {
                                duplicate = theGroup.Bubbles.FirstOrDefault(x =>
                                                                            (!checkType || x.GetType() == vb.GetType()) && x.IdService == vb.IdService) != null;
                            }
                            if (!duplicate && vb.IdService2 != null)
                            {
                                duplicate = theGroup.Bubbles.FirstOrDefault(x =>
                                                                            (!checkType || x.GetType() == vb.GetType()) && x.IdService2 == vb.IdService2) != null;
                            }
                        }
                        else
                        {
                            // Special additional Service defined distinction checks
                            if (vb.IdService != null)
                            {
                                var duplicateBubble = theGroup.Bubbles.FirstOrDefault(x =>
                                                                                      (!checkType || x.GetType() == vb.GetType()) && x.IdService == vb.IdService);

                                duplicate = duplicateBubble == null ? false :
                                            visualBubbleServiceId.VisualBubbleIdComparer(left: duplicateBubble, right: vb);
                            }
                            if (!duplicate && vb.IdService2 != null)
                            {
                                var duplicateBubble = theGroup.Bubbles.FirstOrDefault(x =>
                                                                                      (!checkType || x.GetType() == vb.GetType()) && x.IdService2 == vb.IdService2);

                                duplicate = duplicateBubble == null ? false :
                                            visualBubbleServiceId.VisualBubbleIdComparer(duplicateBubble, vb);
                            }
                        }
                    }

                    if (!duplicate)
                    {
                        Utils.DebugPrint(vb.Service.Information.ServiceName + " found a group. Adding.");

                        if (insertAtBottom)
                        {
                            var lastBubble = theGroup.LastBubbleSafe();
                            if (lastBubble.Time > vb.Time)
                            {
                                vb.Time = lastBubble.Time;
                            }
                        }

                        theGroup.InsertByTime(vb);
                    }
                    else
                    {
                        Utils.DebugPrint("Yuck. It's a duplicate bubble. No need to readd: " + vb.IdService + ", " + vb.IdService2);
                    }
                }

                try
                {
                    if (theGroup.IsParty && !string.IsNullOrWhiteSpace(vb.ParticipantAddressNickname))
                    {
                        var participantAddressNicknamesArray = theGroup.ParticipantNicknames;
                        if (participantAddressNicknamesArray == null)
                        {
                            participantAddressNicknamesArray = new DisaParticipantNickname[0];
                        }
                        var participantAddressNicknames = participantAddressNicknamesArray.ToList();
                        var changed = false;
                        var adding  = true;
                        foreach (var participantAddressNickname in participantAddressNicknames)
                        {
                            if (theGroup.Service.BubbleGroupComparer(participantAddressNickname.Address, vb.ParticipantAddress))
                            {
                                if (participantAddressNickname.Nickname != vb.ParticipantAddressNickname)
                                {
                                    participantAddressNickname.Nickname = vb.ParticipantAddressNickname;
                                    changed = true;
                                }
                                adding = false;
                                break;
                            }
                        }
                        if (adding)
                        {
                            participantAddressNicknames.Add(new DisaParticipantNickname
                            {
                                Address  = vb.ParticipantAddress,
                                Nickname = vb.ParticipantAddressNickname,
                            });
                        }
                        if (changed || adding)
                        {
                            theGroup.ParticipantNicknames = participantAddressNicknames.ToArray();
                        }
                    }
                }
                catch (Exception ex)
                {
                    Utils.DebugPrint("Failed to insert/update participant nickname into cache: " + ex);
                }

                if (!duplicate)
                {
                    Utils.DebugPrint("Inserting bubble into database group!");

                    try
                    {
                        if (newGroup)
                        {
                            BubbleGroupDatabase.AddBubble(theGroup, vb);
                        }
                        else
                        {
                            BubbleGroupDatabase.InsertBubbleByTime(theGroup, vb);
                        }
                    }
                    catch (Exception ex)
                    {
                        Utils.DebugPrint("Bubble failed to be inserting/added into the group " + theGroup.ID + ": " + ex);
                    }

                    try
                    {
                        BubbleGroupEvents.RaiseBubbleInserted(vb, theGroup);
                    }
                    catch (Exception ex)
                    {
                        Utils.DebugPrint(
                            "Error in notifying the interface that the bubble group has been updated (" +
                            vb.Service.Information.ServiceName + "): " + ex.Message);
                    }
                }

                return(theGroup);
            }
        }