/// <summary> /// Create applications and grant user permissions /// </summary> /// <param name="nameSpace"></param> /// <param name="name"></param> /// <param name="organization"></param> /// <param name="sessionId"></param> /// <returns></returns> private static App CreateApplication(PackageConfigFile config, Organization organization, string sessionId) { App app = new App(); app.ID = string.Format("{0:X8}", (organization.Name + config.Namespace).GetHashCode()); app.Namespace = config.Namespace; app.Name = config.DisplayName; app.UrlName = AssureUniqueUrlName(organization, app.Name); app.EntryPoint = "/" + config.AppName; app.Description = config.Description; // Connect software with an organization app.Organization = organization; // Apply permissions SystemUserSession userSession = Db.SQL<SystemUserSession>("SELECT o FROM Simplified.Ring5.SystemUserSession o WHERE o.SessionIdString = ?", sessionId).First; if (userSession != null && userSession.Token != null) { new SoftwarePermission() { User = userSession.Token.User, Software = app }; } else { throw new InvalidOperationException("Failed to retrive user session"); } return app; }
/// <summary> /// unpack image from package /// TODO: Assure random filename is unique /// </summary> /// <param name="packageZip"></param> /// <param name="config"></param> /// <param name="destination"></param> /// <param name="url"></param> private static void UnpackAppStoreMetadata(Software software, string packageZip, PackageConfigFile config, string destination) { string fileName = Path.GetRandomFileName(); if (!Directory.Exists(destination)) { Directory.CreateDirectory(destination); } //string destinationFileName = Path.Combine(destination, fileName); //destinationFileName = Path.ChangeExtension(destinationFileName, Path.GetExtension(config.ImageUri)); Dictionary<string, string> icons = new Dictionary<string, string>(); Dictionary<string, string> banners = new Dictionary<string, string>(); Dictionary<string, string> screenshots = new Dictionary<string, string>(); Dictionary<string, string> appstoreicons = new Dictionary<string, string>(); string metadataFolder = "metadata/"; string bannerMap = "banner"; string screenshotMap = "screenshot"; string appiconMap = "appicon"; using (ZipArchive archive = ZipFile.OpenRead(packageZip)) { #region Find Media foreach (ZipArchiveEntry entry in archive.Entries) { if (entry.FullName.Equals(config.ImageUri, StringComparison.InvariantCultureIgnoreCase)) { // Icon string uniqueFileName; string extention = Path.GetExtension(entry.FullName); Utils.GetUniqueFilename(destination, extention, out uniqueFileName); icons.Add(entry.FullName, uniqueFileName); } if (entry.FullName.StartsWith(metadataFolder + bannerMap, StringComparison.InvariantCultureIgnoreCase)) { // Banner string uniqueFileName; string extention = Path.GetExtension(entry.FullName); Utils.GetUniqueFilename(destination, extention, out uniqueFileName); banners.Add(entry.FullName, uniqueFileName); } if (entry.FullName.StartsWith(metadataFolder + screenshotMap, StringComparison.InvariantCultureIgnoreCase)) { // Screenshot string uniqueFileName; string extention = Path.GetExtension(entry.FullName); Utils.GetUniqueFilename(destination, extention, out uniqueFileName); screenshots.Add(entry.FullName, uniqueFileName); } if (entry.FullName.StartsWith(metadataFolder + appiconMap, StringComparison.InvariantCultureIgnoreCase)) { // Appstoreicon string uniqueFileName; string extention = Path.GetExtension(entry.FullName); Utils.GetUniqueFilename(destination, extention, out uniqueFileName); appstoreicons.Add(entry.FullName, uniqueFileName); } } #endregion #region Unpack foreach (var item in icons) { PackageManager.UnpackFile(archive, item.Key, item.Value); string hash = Utils.CalculateHash(item.Value); // Dup check SoftwareIcon softwareIcon = Db.SQL<SoftwareIcon>("SELECT o FROM Warehouse.SoftwareIcon o WHERE o.Software=? AND o.Hash=?", software, hash).First; if (softwareIcon != null) { // Delete new file File.Delete(item.Value); } else { SoftwareIcon img = new SoftwareIcon(); img.Software = software; img.File = item.Value; img.Position = 0; img.Hash = hash; } } foreach (var item in banners) { PackageManager.UnpackFile(archive, item.Key, item.Value); string hash = Utils.CalculateHash(item.Value); SoftwareBanner img = Db.SQL<SoftwareBanner>("SELECT o FROM Warehouse.SoftwareBanner o WHERE o.Software=? AND o.Hash=?", software, hash).First; if (img != null) { // Delete new file File.Delete(item.Value); } else { img = new SoftwareBanner(); img.Software = software; img.File = item.Value; img.Hash = hash; } try { string fName = Path.GetFileNameWithoutExtension(item.Key).Substring(bannerMap.Length); fName = fName.Trim(new Char[] { ' ', '-', '_' }); img.Position = int.Parse(fName); } catch (Exception) { } } foreach (var item in screenshots) { PackageManager.UnpackFile(archive, item.Key, item.Value); string hash = Utils.CalculateHash(item.Value); SoftwareScreenshot img = Db.SQL<SoftwareScreenshot>("SELECT o FROM Warehouse.SoftwareScreenshot o WHERE o.Software=? AND o.Hash=?", software, hash).First; if (img != null) { // Delete new file File.Delete(item.Value); } else { img = new SoftwareScreenshot(); img.Software = software; img.File = item.Value; img.Hash = hash; } try { string fName = Path.GetFileNameWithoutExtension(item.Key).Substring(screenshotMap.Length); fName = fName.Trim(new Char[] { ' ', '-', '_' }); img.Position = int.Parse(fName); } catch (Exception) { } } foreach (var item in appstoreicons) { PackageManager.UnpackFile(archive, item.Key, item.Value); string hash = Utils.CalculateHash(item.Value); SoftwareIcon img = Db.SQL<SoftwareIcon>("SELECT o FROM Warehouse.SoftwareIcon o WHERE o.Software=? AND o.Hash=?", software, hash).First; if (img != null) { // Delete new file File.Delete(item.Value); } else { img = new SoftwareIcon(); img.Software = software; img.File = item.Value; img.Hash = hash; } try { string fName = Path.GetFileNameWithoutExtension(item.Key).Substring(appiconMap.Length); fName = fName.Trim(new Char[] { ' ', '-', '_' }); img.Position = int.Parse(fName); } catch (Exception) { } } #endregion } }
/// <summary> /// Validate app configuration /// </summary> /// <param name="config"></param> private static void ValidateConfiguration(ZipArchive archive, PackageConfigFile config) { if (string.IsNullOrEmpty(config.Namespace)) { throw new InvalidOperationException("Invalid Namespace <tag> in package configuration"); } // TODO: Validate Namespace for invalid characters (this will be uses for folder naming) if (string.IsNullOrEmpty(config.DisplayName)) { throw new InvalidOperationException("Invalid DisplayName <tag> in package configuration"); } if (string.IsNullOrEmpty(config.Channel)) { throw new InvalidOperationException("Invalid Channel <tag> in package configuration"); } // TODO: Validate Channel for invalid characters (this will be uses for folder naming) if (string.IsNullOrEmpty(config.Version)) { throw new InvalidOperationException("Invalid Version <tag> in package configuration"); } //try { // new Version(config.Version); //} //catch (Exception) { // throw new InvalidOperationException("Invalid Version <tag> in package configuration"); //} if (string.IsNullOrEmpty(config.Executable)) { throw new InvalidOperationException("Invalid Executable <tag> in package configuration"); } try { VerifyPackageEntry(archive, config.Executable); } catch (Exception e) { throw new InvalidOperationException(e.Message + ", " + config.Executable); } // Resource folder // Note: Resource tag can be empty if (!string.IsNullOrEmpty(config.ResourceFolder)) { // Assure that the path is a folder path (ending with an "/" ) if (!config.ResourceFolder.EndsWith("/")) { config.ResourceFolder += "/"; } try { VerifyPackageEntry(archive, config.ResourceFolder); } catch (Exception e) { throw new InvalidOperationException(e.Message + ", " + config.ResourceFolder); } } if (!string.IsNullOrEmpty(config.ImageUri)) { try { VerifyPackageEntry(archive, config.ImageUri); } catch (Exception e) { throw new InvalidOperationException(e.Message + ", " + config.ImageUri); } } // TODO: Add more validations }
/// <summary> /// Get App configuration from zipped package /// </summary> /// <param name="packageZip"></param> /// <param name="config"></param> public static void ReadConfiguration(string packageZip, out PackageConfigFile config) { lock (PackageManager.locker) { using (ZipArchive archive = ZipFile.OpenRead(packageZip)) { ReadConfiguration(archive, out config); } } }
/// <summary> /// Get App configuration from zipped package /// </summary> /// <param name="packageZip"></param> /// <param name="config"></param> public static void ReadConfiguration(ZipArchive archive, out PackageConfigFile config) { lock (PackageManager.locker) { try { foreach (ZipArchiveEntry entry in archive.Entries) { if (entry.FullName.Equals(PackageManager.packageConfigurationFileName, StringComparison.OrdinalIgnoreCase)) { Stream s = entry.Open(); XmlSerializer ser = new XmlSerializer(typeof(PackageConfigFile)); config = ser.Deserialize(s) as PackageConfigFile; return; } } } catch (InvalidOperationException e) { throw new InvalidDataException("Invalid package format", e); } catch (Exception) { throw; } throw new FileNotFoundException(string.Format("Missing package configuration file ({0})", PackageManager.packageConfigurationFileName)); } }
/// <summary> /// Create App configuration from package configuration /// </summary> /// <param name="packageConfig"></param> /// <param name="config"></param> private static void CreateConfig(PackageConfigFile packageConfig, string sourceUrl, string storeUrl, string imageUri, out DeployedConfigFile config) { // Create app configuration file config = new DeployedConfigFile(); config.Namespace = packageConfig.Namespace; config.Channel = packageConfig.Channel; config.Version = packageConfig.Version; config.AppName = packageConfig.AppName; config.Executable = packageConfig.Executable; config.ResourceFolder = packageConfig.ResourceFolder; config.DisplayName = packageConfig.DisplayName; config.Company = packageConfig.Company; config.Description = packageConfig.Description; config.Heading = packageConfig.Heading; config.VersionDate = packageConfig.VersionDate; Uri u = new Uri(sourceUrl); if (u.IsFile) { config.SourceID = string.Format("{0:X8}", u.LocalPath.GetHashCode()); } else { config.SourceID = u.Segments[u.Segments.Length - 1]; } config.SourceUrl = sourceUrl; Uri su = new Uri(storeUrl); if (su.IsFile) { config.StoreID = string.Format("{0:X8}", su.LocalPath.GetHashCode()); } else { config.StoreID = su.Segments[su.Segments.Length - 1]; } config.StoreUrl = storeUrl; config.ImageUri = imageUri; config.CanBeUninstalled = true; }
/// <summary> /// /// </summary> /// <param name="archive"></param> /// <param name="config"></param> /// <param name="imageResourceFolder"></param> /// <param name="imageUri"></param> private static void UnpackAppImage(ZipArchive archive, PackageConfigFile config, string imageResourceFolder, out string imageUri) { string createdDestinationFolder = Utils.CreateDirectory(imageResourceFolder); try { // Unpack app image to starcounter admin folder ZipArchiveEntry entry = archive.GetEntry(config.ImageUri); if (entry != null) { string imageFileName = Path.ChangeExtension(Path.GetRandomFileName(), Path.GetExtension(config.ImageUri)); string imageFile = Path.Combine(imageResourceFolder, imageFileName); entry.ExtractToFile(imageFile, true); string imageFolder = Path.GetFileName(imageResourceFolder); imageUri = string.Format("{0}", imageFileName); } else { // TODO: Use default image? imageUri = config.ImageUri; } } catch (Exception) { if (createdDestinationFolder != null) { Directory.Delete(createdDestinationFolder, true); } throw; } }