internal static void LoadAllPartiallyIfPossible(int bubblesPerGroup = 100)
        {
            var corruptedGroups = new List <string>();

            var bubbleGroupsLocations       = Directory.GetFiles(BubbleGroupDatabase.GetBaseLocation(), "*.*", SearchOption.AllDirectories);
            var bubbleGroupsLocationsSorted =
                bubbleGroupsLocations.OrderByDescending(x => Time.GetUnixTimestamp(File.GetLastWriteTime(x))).ToList();

            foreach (var bubbleGroupLocation in bubbleGroupsLocationsSorted)
            {
                String groupId = null;
                try
                {
                    var groupHeader = Path.GetFileNameWithoutExtension(bubbleGroupLocation);

                    var groupDelimeter = groupHeader.IndexOf("^", StringComparison.Ordinal);
                    var serviceName    = groupHeader.Substring(0, groupDelimeter);
                    groupId = groupHeader.Substring(groupDelimeter + 1);

                    var service = ServiceManager.GetByName(serviceName);

                    if (service == null)
                    {
                        Utils.DebugPrint("Service " + serviceName +
                                         " not found in AllServices!");
                        continue;
                    }

                    var deserializedBubbleGroup = LoadPartiallyIfPossible(bubbleGroupLocation, service, groupId, bubblesPerGroup);
                    if (deserializedBubbleGroup == null)
                    {
                        throw new Exception("DeserializedBubbleGroup is nothing.");
                    }
                    BubbleGroupManager.BubbleGroupsAdd(deserializedBubbleGroup);
                }
                catch (Exception ex)
                {
                    Utils.DebugPrint("Group " + bubbleGroupLocation + " is corrupt. Deleting. " + ex);
                    File.Delete(bubbleGroupLocation);
                    if (groupId != null)
                    {
                        corruptedGroups.Add(groupId);
                    }
                }
            }

            var migrationResaveNeeded  = false;
            var corruptedUnifiedGroups =
                new List <SimpleDatabase <UnifiedBubbleGroup, DisaUnifiedBubbleGroupEntry> .Container>();
            var removeFromRuntime =
                new List <SimpleDatabase <UnifiedBubbleGroup, DisaUnifiedBubbleGroupEntry> .Container>();

            foreach (var group in UnifiedBubbleGroupsDatabase)
            {
                var innerGroupCorrupt = false;
                var innerGroups       = new List <BubbleGroup>();
                foreach (var innerGroupId in @group.Serializable.GroupIds)
                {
                    var innerGroup = BubbleGroupManager.Find(innerGroupId);
                    if (innerGroup == null)
                    {
                        Utils.DebugPrint("Unified group, inner group " + innerGroupId +
                                         " could not be related.");
                        if (corruptedGroups.Contains(innerGroupId))
                        {
                            Utils.DebugPrint(
                                "It was detected that this inner group was corrupted and deleted. Will delete unified group.");
                            innerGroupCorrupt = true;
                            corruptedUnifiedGroups.Add(@group);
                        }
                    }
                    else
                    {
                        innerGroups.Add(innerGroup);
                    }
                }
                if (!innerGroups.Any())
                {
                    Utils.DebugPrint("Yuck. This unified group has no inner groups. Removing from runtime.");
                    removeFromRuntime.Add(@group);
                    continue;
                }
                if (innerGroupCorrupt)
                {
                    continue;
                }

                var primaryGroup = innerGroups.FirstOrDefault(x => x.ID == @group.Serializable.PrimaryGroupId);
                if (primaryGroup == null)
                {
                    Utils.DebugPrint("Unified group, primary group " + @group.Serializable.PrimaryGroupId +
                                     " could not be related. Removing from runtime.");
                    removeFromRuntime.Add(@group);
                    continue;
                }

                var id = @group.Serializable.Id;

                var unifiedGroup = CreateUnifiedInternal(innerGroups, primaryGroup, id);
                if (id == null)
                {
                    migrationResaveNeeded  = true;
                    @group.Serializable.Id = unifiedGroup.ID;
                }

                var sendingGroup = innerGroups.FirstOrDefault(x => x.ID == @group.Serializable.SendingGroupId);
                if (sendingGroup != null)
                {
                    unifiedGroup.SendingGroup = sendingGroup;
                }

                @group.Object = unifiedGroup;
                BubbleGroupManager.BubbleGroupsAdd(unifiedGroup);
            }
            if (removeFromRuntime.Any())
            {
                foreach (var group in removeFromRuntime)
                {
                    UnifiedBubbleGroupsDatabase.Remove(@group);
                }
            }
            if (corruptedUnifiedGroups.Any())
            {
                foreach (var group in corruptedUnifiedGroups)
                {
                    UnifiedBubbleGroupsDatabase.Remove(@group);
                }
            }
            if (migrationResaveNeeded)
            {
                Utils.DebugPrint("It was detected that we need to save migration changes for UnifiedBubbleGroups.");
                UnifiedBubbleGroupsDatabase.SaveChanges();
            }

            try
            {
                foreach (var groupCache in BubbleGroupCacheManager.Load())
                {
                    var associatedGroup = BubbleGroupManager.Find(groupCache.Guid);
                    if (associatedGroup == null)
                    {
                        continue;
                    }

                    var unifiedGroup = associatedGroup as UnifiedBubbleGroup;
                    if (unifiedGroup != null)
                    {
                        associatedGroup = unifiedGroup.PrimaryGroup;
                    }

                    associatedGroup.Title = groupCache.Name;
                    associatedGroup.Photo = groupCache.Photo;
                    associatedGroup.IsPhotoSetInitiallyFromCache = true;
                    if (groupCache.Participants != null)
                    {
                        associatedGroup.Participants = groupCache.Participants.ToSynchronizedCollection();
                        foreach (var participant in associatedGroup.Participants)
                        {
                            participant.IsPhotoSetInitiallyFromCache = true;
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Utils.DebugPrint("Failed to load bubble groups!: " + ex);
            }

            BubbleGroupSettingsManager.Load();
        }
        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();
                        }
                    }
                }
            }));
        }
Exemple #3
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();
                }
            }
        }