/// <summary>
        /// Saves an aPK to file on the server.
        /// </summary>
        /// <param name="packageKey">Filename of the APK containing appname, version and whether it is a critical update or not.</param>
        /// <param name="file">Byte array containing APK data</param>
        /// <returns></returns>
        public static String SavePackage(String packageKey, byte[] file)
        {
            try
            {
                waitHandle.WaitOne();

                if (!Directory.Exists(PackageFolderLocation))
                {
                    Directory.CreateDirectory(PackageFolderLocation);
                }

                var fileName = Path.Combine(PackageFolderLocation, packageKey);

                File.WriteAllBytes(fileName, file);

                return(fileName);
            }
            catch (Exception e)
            {
                LoggingControl.Log("Error while saving package " + packageKey + ".", e);
            }
            finally
            {
                waitHandle.Set();
            }

            return(String.Empty);
        }
        /// <summary>
        /// Returns a list of hosted apks.
        /// APK details are parsed out of the filename.
        /// </summary>
        public static List <PackageDetails> GetPackageList()
        {
            var packages = new List <PackageDetails>();

            try
            {
                if (!Directory.Exists(PackageFolderLocation))
                {
                    Directory.CreateDirectory(PackageFolderLocation);
                }
                else
                {
                    var files = Directory.GetFiles(PackageFolderLocation).ToList();

                    foreach (var file in files)
                    {
                        packages.Add(new PackageDetails(file));
                    }
                }
            }
            catch (Exception e)
            {
                LoggingControl.Log("Error while retrieving package list. " + PackageFolderLocation, e);
            }

            return(packages);
        }
        /// <summary>
        /// Deletes an APK file from the server/service.
        /// </summary>
        /// <param name="packageDetails">Details of the APK to be deleted.</param>
        public static bool DeletePackage(PackageDetails packageDetails)
        {
            try
            {
                waitHandle.WaitOne();

                var packageKey = packageDetails.GetPackageKey();
                var path       = GetPackagePath(packageKey);

                if (File.Exists(path))
                {
                    File.Delete(path);
                }
                else
                {
                    LoggingControl.Log("Error while deleting package " + packageDetails.Name + " " + packageDetails.Version + ". Could not find file.");
                    return(false);
                }

                return(!File.Exists(path));
            }
            catch (Exception e)
            {
                LoggingControl.Log("Error while deleting package " + packageDetails.Name + " " + packageDetails.Version + ".", e);
                return(false);
            }
            finally
            {
                waitHandle.Set();
            }
        }
예제 #4
0
        /// <summary>
        /// Removes this download entry from the download dictionary if only one device is busy downloading or simply decremtents the count otherwise..
        /// </summary>
        /// <param name="packageDetails">Details of the package download that is being completed.</param>
        public static bool EndOrDecrementDownload(PackageDetails packageDetails, int fragmentSize)
        {
            try
            {
                var packageKey  = packageDetails.GetPackageKey();
                var downloadKey = GetDownloadKey(packageDetails, fragmentSize);

                Download download;

                if (activeDownloads.TryGetValue(downloadKey, out download))
                {
                    download.LastTimeStamp = DateTime.Now;
                    download.ActiveDownloads--;

                    if (download.ActiveDownloads <= 0)
                    {
                        return(activeDownloads.TryRemove(packageKey, out download));
                    }
                    else
                    {
                        return(true);
                    }
                }
                else
                {
                    LoggingControl.Log("Error while ending download for " + packageDetails.Name + " " + packageDetails.Version + ". Entry no longer exists in download list.");
                    return(true);
                }
            }
            catch (Exception e)
            {
                LoggingControl.Log("Error while ending download for  " + packageDetails.Name + " " + packageDetails.Version + ".", e);
                return(false);
            }
        }
예제 #5
0
        /// <summary>
        /// Initiates an download of a specified APK.
        /// Inserts an entry into the upload dictionary where download fragments are kept in memory while they are being sent to devices.
        /// Returns a count of fragments to be expected for this APK when using this fragment size.
        /// </summary>
        /// <param name="packageDetails">Details of the package being downloaded.</param>
        /// <param name="fragmentSize">The size of the fragments being downloaded as requested by the device.</param>
        public static int StartOrIncrementDownload(PackageDetails packageDetails, int fragmentSize)
        {
            try
            {
                CleanUpDownloads();

                Download packageDownload = null;

                var downloadKey = GetDownloadKey(packageDetails, fragmentSize);
                var packageKey  = packageDetails.GetPackageKey();
                var package     = new byte[0];

                if (FileControl.GetPackageList().Any(x => x.GetPackageKey().Equals(packageKey, StringComparison.OrdinalIgnoreCase)))
                {
                    var path = FileControl.GetPackagePath(packageKey);

                    if (File.Exists(path))
                    {
                        package = FileControl.GetPackage(path);

                        if (activeDownloads.TryGetValue(downloadKey, out packageDownload))
                        {
                            packageDownload.ActiveDownloads++;
                            packageDownload.LastTimeStamp = DateTime.Now;
                            return(packageDownload.Fragments.Count);
                        }
                        else
                        {
                            packageDownload = new Download(packageDetails.Name, packageDetails.Version, package, fragmentSize);

                            if (activeDownloads.TryAdd(downloadKey, packageDownload))
                            {
                                return(packageDownload.Fragments.Count);
                            }
                            else
                            {
                                LoggingControl.Log("Error while starting download for " + packageDetails.Name + " " + packageDetails.Version + ". Could not add instance to download list.");
                            }
                        }
                    }
                    else
                    {
                        LoggingControl.Log("Error while starting download for " + packageDetails.Name + " " + packageDetails.Version + ". Could not find file.");
                    }
                }
                else
                {
                    LoggingControl.Log("Error while cancelling downloads for " + packageDetails.Name + " " + packageDetails.Version + ". Could not find package in list.");
                }

                return(-1);
            }
            catch (Exception e)
            {
                LoggingControl.Log("Error while cancelling downloads for " + packageDetails.Name + " " + packageDetails.Version + ".", e);
                return(-1);
            }
        }
예제 #6
0
        /// <summary>
        /// Cancels a specific upload and removes its entry from the upload dictionary.
        /// </summary>
        /// <param name="packageDetails">Details of the package upload that is being cancelled</param>
        /// <returns></returns>
        public static bool CancelNewUpload(PackageDetails packageDetails)
        {
            try
            {
                Upload packageUpload = null;

                return(activeUploads.TryRemove(GetUploadKey(packageDetails), out packageUpload));
            }
            catch (Exception e)
            {
                LoggingControl.Log("Error while cancelling upload for " + packageDetails.Name + " " + packageDetails.Version + ".", e);
                return(false);
            }
        }
예제 #7
0
 /// <summary>
 /// Checks the service for any existing uploads of an APK.
 /// This helps to prevent the same APK from being uploaded at the same time.
 /// </summary>
 /// <param name="packageDetails">Details of the package being checked for active uploads.</param>
 public static bool CheckForActiveUpload(PackageDetails packageDetails)
 {
     try
     {
         CleanUpUploads();
         var uploadKey = GetUploadKey(packageDetails);
         return(activeUploads.Any(x => x.Key.Equals(uploadKey, StringComparison.OrdinalIgnoreCase)));
     }
     catch (Exception e)
     {
         LoggingControl.Log("Error while checking for uploads for " + packageDetails.Name + " " + packageDetails.Version + ".", e);
         return(false);
     }
 }
예제 #8
0
        /// <summary>
        /// Checks for active downloads of a specific APK. This allows the users altering or removing hosted APKs to be warned if an APK is currently in use.
        /// </summary>
        /// <param name="packageDetails">Details of the package being checked for active downloads.</param>
        public static int GetActiveDownloadCountForPackage(PackageDetails packageDetails)
        {
            try
            {
                CleanUpDownloads();

                var downloads = activeDownloads.Where(x => x.Value.Name == packageDetails.Name && x.Value.Version == packageDetails.Version).Sum(x => x.Value.ActiveDownloads);
                return(downloads);
            }
            catch (Exception e)
            {
                LoggingControl.Log("Error while checking for downloads for " + packageDetails.Name + " " + packageDetails.Version + ".", e);
                return(-1);
            }
        }
예제 #9
0
 /// <summary>
 /// Removes any entries in the download dictionary that have not been interacted with in the past 3 minutes.
 /// This ensures old entries that are incomplete for whatever reason will not remain on the server indefinately.
 /// </summary>
 public static void CleanUpDownloads()
 {
     try
     {
         foreach (var download in activeDownloads)
         {
             if ((DateTime.Now - download.Value.LastTimeStamp).TotalMinutes > 3)
             {
                 Download forgottenPackage;
                 activeDownloads.TryRemove(download.Key, out forgottenPackage);
             }
         }
     }
     catch (Exception e)
     {
         LoggingControl.Log("Error while cleaning up downloads.", e);
     }
 }
        /// <summary>
        /// Updates the filename of an APK to indicate whether it is a critical update or not.
        /// Apps updated through mobu canspecify whether they always get the latest version of an APK or only newer APKs marked as critical.
        /// This makes it possible allow testers to manually get new versions without automatically pushing it to all users.
        /// </summary>
        /// <param name="packageDetails">Details of the package that should be toggled.</param>
        /// <returns></returns>
        public static bool ToggleCriticalState(PackageDetails packageDetails)
        {
            try
            {
                waitHandle.WaitOne();

                var packageKey = packageDetails.GetPackageKey();
                var oldPath    = GetPackagePath(packageKey);
                var newPath    = oldPath.Remove(oldPath.Length - 1);

                if (File.Exists(oldPath))
                {
                    if (oldPath[oldPath.Length - 1] == '1')
                    {
                        newPath += '0';
                    }
                    else
                    {
                        newPath += '1';
                    }

                    File.Move(oldPath, newPath);
                }
                else
                {
                    LoggingControl.Log("Error while toggling critical state for " + packageDetails.Name + " " + packageDetails.Version + ". Could not find file.");;
                    return(false);
                }

                return(File.Exists(newPath) && !File.Exists(oldPath));
            }
            catch (Exception e)
            {
                LoggingControl.Log("Error while toggling critical state for " + packageDetails.Name + " " + packageDetails.Version + ".", e);
                return(false);
            }
            finally
            {
                waitHandle.Set();
            }
        }
        /// <summary>
        /// Returns a specific APK file.
        /// </summary>
        /// <param name="path">FIle path of the requested APK.</param>
        /// <returns></returns>
        public static byte[] GetPackage(String path)
        {
            try
            {
                waitHandle.WaitOne();
                if (File.Exists(path))
                {
                    return(File.ReadAllBytes(path));
                }
            }
            catch (Exception e)
            {
                LoggingControl.Log("Error while retrieving package", e);
            }
            finally
            {
                waitHandle.Set();
            }

            return(null);
        }
예제 #12
0
        /// <summary>
        /// Recombines uploaded fragments into an APK file which is saved on the server before removing this entry from the upload dictionary.
        /// </summary>
        /// <param name="packageDetails">Details of the package upload that is being completed.</param>
        public static bool FinishUpload(PackageDetails packageDetails)
        {
            try
            {
                Upload upload    = null;
                var    uploadKey = GetUploadKey(packageDetails);

                if (activeUploads.TryGetValue(uploadKey, out upload))
                {
                    var package = upload.CombineFragmentsIntoPackage();

                    if (package.Length != upload.PackageSize)
                    {
                        LoggingControl.Log("Error while finishing upload for " + packageDetails.Name + " " + packageDetails.Version + ". Unexpected file size.");
                        return(false);
                    }

                    var path = FileControl.SavePackage(packageDetails.GetPackageKey(), package);

                    if (String.IsNullOrEmpty(path))
                    {
                        LoggingControl.Log("Error while finishing upload for " + packageDetails.Name + " " + packageDetails.Version + ". Could not find key save file.");
                        return(false);
                    }

                    return(activeUploads.TryRemove(uploadKey, out upload));
                }
                else
                {
                    LoggingControl.Log("Error while finishing upload for " + packageDetails.Name + " " + packageDetails.Version + ". Could not find key " + uploadKey + ".");
                    return(false);
                }
            }
            catch (Exception e)
            {
                LoggingControl.Log("Error while finishing upload for " + packageDetails.Name + " " + packageDetails.Version + ".", e);
                return(false);
            }
        }
예제 #13
0
        /// <summary>
        /// Adds a upload fragment to the list of fragments being uploaded for this APK.
        /// </summary>
        /// <param name="fragment">Contains apk details as well as the data content of the fragment itself.</param>
        public static bool UploadNextFragment(Fragment fragment)
        {
            try
            {
                var uploadKey = GetUploadKey(fragment.PackageDetails);

                if (activeUploads.ContainsKey(uploadKey))
                {
                    activeUploads[uploadKey].LastTimeStamp = DateTime.Now;
                    activeUploads[uploadKey].Fragments.Add(fragment.Data);
                    return(true);
                }
                else
                {
                    return(false);;
                }
            }
            catch (Exception e)
            {
                LoggingControl.Log("Error while uploading next fragment for " + fragment.PackageDetails.Name + " " + fragment.PackageDetails.Version + ".", e);
                return(false);
            }
        }
예제 #14
0
        /// <summary>
        /// Cancels all downloads of a specific APK and removes its entry from the download dictionary.
        /// This cancels all downloads of this version of an app, regardless of the requested fragment size.
        /// </summary>
        /// <param name="packageDetails">Details of the package being cancelled.</param>
        public static bool CancelActiveDownloadsForApp(PackageDetails packageDetails)
        {
            try
            {
                var downloads = activeDownloads.Where(x => x.Value.Name == packageDetails.Name && x.Value.Version == packageDetails.Version);

                foreach (var x in downloads)
                {
                    if (!activeDownloads.TryRemove(x.Key, out Download cancelledApp))
                    {
                        LoggingControl.Log("Error while cancelling download for " + x + ".");
                        return(false);
                    }
                }

                return(true);
            }
            catch (Exception e)
            {
                LoggingControl.Log("Error while cancelling downloads for " + packageDetails.Name + " " + packageDetails.Version + ".", e);
                return(false);
            }
        }
예제 #15
0
        /// <summary>
        /// Initiates an opload of a specified APK.
        /// Inserts an entyry into the upload dictionary where uploaded fragments are kept in memory until they are recompiled into the complete APK and saved.
        /// </summary>
        /// <param name="packageDetails">Details of the package being uploaded.</param>
        /// <param name="packageSize">The full size of the APK being uploaded in bytes.</param>
        /// <param name="fragmentSize">The size of the fragments being uploaded.</param>
        /// <returns></returns>
        public static bool StartUpload(PackageDetails packageDetails, int packageSize, int fragmentSize)
        {
            try
            {
                CleanUpUploads();

                var packageUpload = new Upload(GetUploadKey(packageDetails), packageSize, fragmentSize);

                if (activeUploads.ContainsKey(packageUpload.Key))
                {
                    LoggingControl.Log("Error while starting upload for " + packageDetails.Name + " " + packageDetails.Version + ". FIle is already being uploaded.");
                    return(false);
                }
                else
                {
                    return(activeUploads.TryAdd(packageUpload.Key, packageUpload));
                }
            }
            catch (Exception e)
            {
                LoggingControl.Log("Error while starting upload for " + packageDetails.Name + " " + packageDetails.Version + ".", e);
                return(false);
            }
        }
예제 #16
0
        /// <summary>
        /// Retrieves a specific fragment to be sent to the device.
        /// </summary>
        /// <param name="packageDetails">Details of the package being downloaded.</param>
        /// <param name="index">Index of the specific fragment </param>
        /// <param name="fragmentSize">Fragment size requested by the device</param>
        public static Fragment GetFragment(PackageDetails packageDetails, int index, int fragmentSize)
        {
            try
            {
                Download package     = null;
                var      downloadKey = GetDownloadKey(packageDetails, fragmentSize);

                if (activeDownloads.TryGetValue(downloadKey, out package))
                {
                    package.LastTimeStamp = DateTime.Now;
                    return(new Fragment(packageDetails, index, package.Fragments[index]));
                }
                else
                {
                    LoggingControl.Log("Error while getting next download fragment for " + packageDetails.Name + " " + packageDetails.Version + ". Could not find entry in download list.");
                    return(null);
                }
            }
            catch (Exception e)
            {
                LoggingControl.Log("Error while getting next download fragment for " + packageDetails.Name + " " + packageDetails.Version + ".", e);
                return(null);
            }
        }