using Microsoft.VisualBasic.CompilerServices; using System.Linq.Expressions; using System; using System.Collections.Generic; using System.IO; using System.IO.Compression; using System.Linq; using System.Text; using System.Threading.Tasks; using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using NuGet.Packaging.Core; using NuGet.Versioning; using isnd.Data; using isnd.Helpers; using isnd.Entities; using Microsoft.AspNetCore.Http; using isn.abst; using isnd.Data.Packages; using Microsoft.EntityFrameworkCore; namespace isnd.Controllers { public partial class PackagesController { // TODO [Authorize(Policy = IsndConstants.RequireValidApiKey)] [HttpPut("~" + Constants.ApiVersionPrefix + ApiConfig.Package)] public async Task Put() { try { var clientVersionId = Request.Headers["X-NuGet-Client-Version"]; string apiKey = Request.Headers["X-NuGet-ApiKey"][0]; ViewData["versionId"] = typeof(PackagesController).Assembly.FullName; var files = new List(); ViewData["files"] = files; var clearkey = protector.Unprotect(apiKey); var apikey = dbContext.ApiKeys.SingleOrDefault(k => k.Id == clearkey); if (apikey == null) { logger.LogError("403 : no api-key"); return Unauthorized(); } Commit commit = new Commit { Action = PackageAction.PublishPackage, TimeStamp = DateTimeOffset.Now.ToUniversalTime() }; foreach (IFormFile file in Request.Form.Files) { string initpath = Path.Combine(Environment.GetEnvironmentVariable("TEMP") ?? Environment.GetEnvironmentVariable("TMP") ?? "/tmp", $"isn-{Guid.NewGuid()}."+Constants.PaquetFileEstension); using (FileStream fw = new FileStream(initpath, FileMode.Create)) { file.CopyTo(fw); } using (FileStream fw = new FileStream(initpath, FileMode.Open)) { var archive = new ZipArchive(fw); var spec = archive.Entries.FirstOrDefault(e => e.FullName.EndsWith("." + Constants.SpecFileEstension)); if (spec == null) return BadRequest(new { error = "no " + Constants.SpecFileEstension + " from archive" }); string pkgpath; NuGetVersion version; string pkgid; string fullpath; using (var specstr = spec.Open()) { NuspecCoreReader reader = new NuspecCoreReader(specstr); string pkgdesc = reader.GetDescription(); var types = reader.GetPackageTypes(); pkgid = reader.GetId(); version = reader.GetVersion(); string pkgidpath = Path.Combine(isndSettings.PackagesRootDir, pkgid); pkgpath = Path.Combine(pkgidpath, version.ToFullString()); string name = $"{pkgid}-{version}."+Constants.PaquetFileEstension; fullpath = Path.Combine(pkgpath, name); var destpkgiddir = new DirectoryInfo(pkgidpath); Package pkg = dbContext.Packages.SingleOrDefault(p => p.Id == pkgid); if (pkg != null) { if (pkg.OwnerId != apikey.UserId) { return new ForbidResult(); } pkg.Description = pkgdesc; } else { pkg = new Package { Id = pkgid, Description = pkgdesc, OwnerId = apikey.UserId, LatestCommit = commit }; dbContext.Packages.Add(pkg); } if (!destpkgiddir.Exists) destpkgiddir.Create(); var source = new FileInfo(initpath); var dest = new FileInfo(fullpath); var destdir = new DirectoryInfo(dest.DirectoryName); if (dest.Exists) { logger.LogWarning($"Existant package on disk : '{dest.FullName}'"); // La version existe sur le disque, // mais si elle ne l'est pas en base de donnéés, // on remplace la version sur disque. string exFullString = version.ToFullString(); var pkgv = dbContext.PackageVersions. Include(v=>v.LatestCommit) .SingleOrDefault( v => v.PackageId == pkg.Id && v.FullString == exFullString ); if (pkgv!=null && ! pkgv.IsDeleted) { string msg = $"existant : {pkg.Id}-{exFullString}"; logger.LogWarning("400 : {msg}", msg); ModelState.AddModelError("pkgversion", msg); return BadRequest(this.CreateAPIKO("existant")); } else dest.Delete(); } { if (!destdir.Exists) destdir.Create(); source.MoveTo(fullpath); files.Add(name); string fullstringversion = version.ToFullString(); var pkgvers = dbContext.PackageVersions.Where (v => v.PackageId == pkg.Id && v.FullString == fullstringversion); if (pkgvers.Count() > 0) { foreach (var v in pkgvers.ToArray()) dbContext.PackageVersions.Remove(v); } // FIXME default type or null if (types==null || types.Count==0) dbContext.PackageVersions.Add (new PackageVersion{ Package = pkg, Major = version.Major, Minor = version.Minor, Patch = version.Patch, Revision = version.Revision, IsPrerelease = version.IsPrerelease, FullString = version.ToFullString(), Type = null, LatestCommit = commit }); else foreach (var type in types) { var pkgver = new PackageVersion { Package = pkg, Major = version.Major, Minor = version.Minor, Patch = version.Patch, IsPrerelease = version.IsPrerelease, FullString = version.ToFullString(), Type = type.Name, LatestCommit = commit }; dbContext.PackageVersions.Add(pkgver); } dbContext.Commits.Add(commit); await dbContext.SaveChangesAsync(); await packageManager.ÛpdateCatalogForAsync(commit); logger.LogInformation($"new paquet : {spec.Name}"); } } using (var shacrypto = System.Security.Cryptography.SHA512.Create()) { using (var stream = System.IO.File.OpenRead(fullpath)) { var hash = shacrypto.ComputeHash(stream); var shafullname = fullpath + ".sha512"; var hashtext = Convert.ToBase64String(hash); var hashtextbytes = Encoding.ASCII.GetBytes(hashtext); using (var shafile = System.IO.File.OpenWrite(shafullname)) { shafile.Write(hashtextbytes, 0, hashtextbytes.Length); } } } string nuspecfullpath = Path.Combine(pkgpath, pkgid + "." + Constants.SpecFileEstension); FileInfo nfpi = new(nuspecfullpath); if (nfpi.Exists) nfpi.Delete(); spec.ExtractToFile(nuspecfullpath); } } return Ok(); } catch (Exception ex) { logger.LogError("PUT exception : " + ex.Message); logger.LogError("Stack Trace : " + ex.StackTrace); return new ObjectResult(new { ViewData, ex.Message }) { StatusCode = 500 }; } } } }