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 Microsoft.AspNetCore.Http; using isnd.Data.Catalog; namespace isnd.Controllers { public partial class PackagesController { [HttpPut(_pkgRootPrefix + ApiConfig.Publish)] 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 = DateTime.Now }; _dbContext.Commits.Add(commit); foreach (IFormFile file in Request.Form.Files) { string initpath = Path.Combine(Environment.GetEnvironmentVariable("TEMP") ?? Environment.GetEnvironmentVariable("TMP") ?? "/tmp", $"isn-{Guid.NewGuid()}.nupkg"); 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 nuspec = archive.Entries.FirstOrDefault(e => e.FullName.EndsWith(".nuspec")); if (nuspec == null) return BadRequest(new { error = "no nuspec from archive" }); string pkgpath; NuGetVersion version; string pkgid; string fullpath; using (var specstr = nuspec.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}.nupkg"; fullpath = Path.Combine(pkgpath, name); var destpkgiddir = new DirectoryInfo(pkgidpath); Package package = _dbContext.Packages.SingleOrDefault(p => p.Id == pkgid); if (package != null) { if (package.OwnerId != apikey.UserId) { return new ForbidResult(); } package.Description = pkgdesc; } else { package = new Package { Id = pkgid, Description = pkgdesc, OwnerId = apikey.UserId, LatestVersion = commit }; _dbContext.Packages.Add(package); } if (!destpkgiddir.Exists) destpkgiddir.Create(); var source = new FileInfo(initpath); var dest = new FileInfo(fullpath); var destdir = new DirectoryInfo(dest.DirectoryName); if (dest.Exists) { // La version existe sur le disque, // mais si elle ne l'est pas en base de donnéés, // on remplace la version sur disque. var pkgv = _dbContext.PackageVersions.Where( v => v.PackageId == package.Id ); if (pkgv !=null && pkgv.Count()==0) { dest.Delete(); } else { ViewData["msg"] = "existant"; ViewData["ecode"] = 1; _logger.LogWarning("400 : existant"); return base.BadRequest(new { error = ModelState }); } } { if (!destdir.Exists) destdir.Create(); source.MoveTo(fullpath); files.Add(name); string fullstringversion = version.ToFullString(); var pkgvers = _dbContext.PackageVersions.Where (v => v.PackageId == package.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 = package, Major = version.Major, Minor = version.Minor, Patch = version.Patch, IsPrerelease = version.IsPrerelease, FullString = version.ToFullString(), Type = null, LatestCommit = commit }); else foreach (var type in types) { var pkgver = new PackageVersion { Package = package, Major = version.Major, Minor = version.Minor, Patch = version.Patch, IsPrerelease = version.IsPrerelease, FullString = version.ToFullString(), Type = type.Name, LatestCommit = commit }; _dbContext.PackageVersions.Add(pkgver); } await _dbContext.SaveChangesAsync(); _packageManager.ÛpdateCatalogFor(commit); _logger.LogInformation($"new package : {nuspec.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 + ".nuspec"); FileInfo nfpi = new FileInfo(nuspecfullpath); if (nfpi.Exists) nfpi.Delete(); nuspec.ExtractToFile(nuspecfullpath); } } return Ok(ViewData); } catch (Exception ex) { _logger.LogError(ex.Message); _logger.LogError("Stack Trace: " + ex.StackTrace); return new ObjectResult(new { ViewData, ex.Message }) { StatusCode = 500 }; } } } }