2018-10-14 08:17:04 +00:00
|
|
|
package main
|
2017-06-11 06:14:13 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
2017-06-11 07:52:32 +00:00
|
|
|
"github.com/blang/semver"
|
2018-10-14 08:35:50 +00:00
|
|
|
"io"
|
|
|
|
"log"
|
|
|
|
"net/http"
|
|
|
|
"os"
|
2017-09-14 03:33:29 +00:00
|
|
|
"path"
|
2018-10-14 08:35:50 +00:00
|
|
|
"strings"
|
2017-06-11 06:14:13 +00:00
|
|
|
)
|
|
|
|
|
2017-06-12 00:41:49 +00:00
|
|
|
func rescanHandler(w http.ResponseWriter, r *http.Request) {
|
2017-09-14 03:33:29 +00:00
|
|
|
mutex.Lock()
|
|
|
|
defer mutex.Unlock()
|
|
|
|
|
2017-06-12 00:41:49 +00:00
|
|
|
distroName := r.URL.Query().Get("distro")
|
2017-06-11 07:18:59 +00:00
|
|
|
|
2017-06-12 00:41:49 +00:00
|
|
|
if distroName == "" {
|
|
|
|
distroName = "stable"
|
|
|
|
}
|
2017-06-11 07:18:59 +00:00
|
|
|
|
2017-06-12 00:41:49 +00:00
|
|
|
if _, exists := distros[distroName]; !exists {
|
|
|
|
httpErrorf(w, "Unable to find distro %s", distroName)
|
|
|
|
return
|
|
|
|
}
|
2017-06-11 07:18:59 +00:00
|
|
|
|
2017-06-12 00:41:49 +00:00
|
|
|
distro := &Distro{Name: distroName, Architectures: make(map[string]map[string]*PackageFile)}
|
2017-06-11 07:18:59 +00:00
|
|
|
|
2017-06-12 00:41:49 +00:00
|
|
|
scanInitialPackages(conf, distro)
|
2017-06-11 07:18:59 +00:00
|
|
|
|
2017-09-14 03:33:29 +00:00
|
|
|
saveCache(distro)
|
|
|
|
|
2017-06-12 00:41:49 +00:00
|
|
|
distros[distroName] = distro
|
2017-09-14 03:33:29 +00:00
|
|
|
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
|
|
w.Write([]byte("ok"))
|
2017-06-12 00:41:49 +00:00
|
|
|
}
|
2017-06-11 07:18:59 +00:00
|
|
|
|
2017-06-12 00:41:49 +00:00
|
|
|
func uploadHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
if r.Method != "POST" {
|
|
|
|
http.Error(w, "method not supported", http.StatusMethodNotAllowed)
|
|
|
|
return
|
|
|
|
}
|
2017-06-11 07:52:32 +00:00
|
|
|
|
2017-09-14 03:33:29 +00:00
|
|
|
queryArchType := r.URL.Query().Get("arch")
|
2017-06-11 07:52:32 +00:00
|
|
|
|
2017-09-14 03:33:29 +00:00
|
|
|
if queryArchType == "" {
|
|
|
|
queryArchType = "all"
|
2017-06-12 00:41:49 +00:00
|
|
|
}
|
2017-06-11 07:52:32 +00:00
|
|
|
|
2017-06-12 00:41:49 +00:00
|
|
|
distroName := r.URL.Query().Get("distro")
|
2017-06-11 07:18:59 +00:00
|
|
|
|
2017-06-12 00:41:49 +00:00
|
|
|
if distroName == "" {
|
|
|
|
distroName = "stable"
|
|
|
|
}
|
2017-06-11 07:18:59 +00:00
|
|
|
|
2017-06-12 00:41:49 +00:00
|
|
|
key := r.URL.Query().Get("key")
|
2017-06-11 07:18:59 +00:00
|
|
|
|
2017-06-12 00:41:49 +00:00
|
|
|
if key == "" || key != conf.Http.Key {
|
|
|
|
http.Error(w, "unauthorized", 403)
|
|
|
|
return
|
|
|
|
}
|
2017-06-11 07:18:59 +00:00
|
|
|
|
2017-06-12 00:41:49 +00:00
|
|
|
force := false
|
2017-06-11 07:18:59 +00:00
|
|
|
|
2017-06-12 00:41:49 +00:00
|
|
|
forceStr := r.URL.Query().Get("force")
|
2017-06-11 07:18:59 +00:00
|
|
|
|
2017-09-14 03:33:29 +00:00
|
|
|
if forceStr == "true" {
|
2017-06-12 00:41:49 +00:00
|
|
|
force = true
|
|
|
|
}
|
2017-06-11 07:18:59 +00:00
|
|
|
|
2017-06-12 00:41:49 +00:00
|
|
|
reader, err := r.MultipartReader()
|
2017-06-11 07:18:59 +00:00
|
|
|
|
2017-06-12 00:41:49 +00:00
|
|
|
if err != nil {
|
|
|
|
httpErrorf(w, "error creating multipart reader: %s", err)
|
|
|
|
return
|
|
|
|
}
|
2017-06-11 07:18:59 +00:00
|
|
|
|
2017-06-12 00:41:49 +00:00
|
|
|
mutex.RLock()
|
2017-06-11 07:18:59 +00:00
|
|
|
|
2017-06-12 00:41:49 +00:00
|
|
|
distro, exists := distros[distroName]
|
2017-06-11 07:18:59 +00:00
|
|
|
|
2017-06-12 00:41:49 +00:00
|
|
|
if !exists {
|
|
|
|
httpErrorf(w, "invalid distro: %s", distroName)
|
|
|
|
mutex.RUnlock()
|
|
|
|
return
|
|
|
|
}
|
2017-06-11 07:18:59 +00:00
|
|
|
|
2017-06-12 00:41:49 +00:00
|
|
|
mutex.RUnlock()
|
2017-06-11 07:18:59 +00:00
|
|
|
|
2017-06-12 00:41:49 +00:00
|
|
|
// Lock to prevent concurrent modification
|
|
|
|
mutex.Lock()
|
|
|
|
defer mutex.Unlock()
|
2017-06-11 07:18:59 +00:00
|
|
|
|
2017-09-14 03:33:29 +00:00
|
|
|
modifiedArches := make(map[string]bool)
|
|
|
|
|
2017-09-14 04:11:44 +00:00
|
|
|
baseDir := path.Join(os.TempDir(), "deb-simple")
|
|
|
|
|
|
|
|
if _, err := os.Stat(baseDir); err != nil {
|
|
|
|
if os.IsNotExist(err) {
|
|
|
|
if err := os.MkdirAll(baseDir, 0755); err != nil {
|
|
|
|
httpErrorf(w, "error creating path: %s", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-12 00:41:49 +00:00
|
|
|
for {
|
|
|
|
part, err := reader.NextPart()
|
2017-06-11 07:52:32 +00:00
|
|
|
|
2017-06-12 00:41:49 +00:00
|
|
|
if err == io.EOF {
|
|
|
|
break
|
|
|
|
}
|
2017-06-11 07:52:32 +00:00
|
|
|
|
2017-09-14 03:33:29 +00:00
|
|
|
if part.FileName() == "" || !strings.HasSuffix(part.FileName(), ".deb"){
|
2017-06-12 00:41:49 +00:00
|
|
|
continue
|
|
|
|
}
|
2017-06-11 07:52:32 +00:00
|
|
|
|
2017-09-14 04:11:44 +00:00
|
|
|
tempFile := path.Join(baseDir, part.FileName())
|
2017-06-11 06:14:13 +00:00
|
|
|
|
2017-09-14 03:33:29 +00:00
|
|
|
dst, err := os.Create(tempFile)
|
2017-06-11 07:18:59 +00:00
|
|
|
|
2017-06-12 00:41:49 +00:00
|
|
|
if err != nil {
|
|
|
|
httpErrorf(w, "error creating deb file: %s", err)
|
2017-06-11 06:14:13 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-06-12 00:41:49 +00:00
|
|
|
if _, err := io.Copy(dst, part); err != nil {
|
2017-09-14 03:33:29 +00:00
|
|
|
dst.Close()
|
|
|
|
|
2017-06-12 00:41:49 +00:00
|
|
|
httpErrorf(w, "error writing deb file: %s", err)
|
2017-06-11 06:14:13 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-09-14 03:33:29 +00:00
|
|
|
dst.Close()
|
|
|
|
|
2017-06-12 00:41:49 +00:00
|
|
|
// Get package name, if it already exists remove the old file.
|
2017-09-14 03:57:03 +00:00
|
|
|
f, err := newPackageFile(tempFile)
|
2017-06-11 06:14:13 +00:00
|
|
|
|
2017-06-12 00:41:49 +00:00
|
|
|
if err != nil {
|
|
|
|
httpErrorf(w, "error loading package info: %s", err)
|
2017-06-11 06:14:13 +00:00
|
|
|
}
|
2017-06-11 07:18:59 +00:00
|
|
|
|
2017-09-14 03:33:29 +00:00
|
|
|
archType := f.Info.Architecture
|
|
|
|
|
|
|
|
if archType == "" {
|
|
|
|
archType = queryArchType
|
|
|
|
}
|
|
|
|
|
2017-09-17 23:15:41 +00:00
|
|
|
modifiedArches[archType] = true
|
|
|
|
|
2017-09-14 03:33:29 +00:00
|
|
|
// Get current packages
|
|
|
|
packages, exists := distro.Architectures[archType]
|
|
|
|
|
|
|
|
if !exists {
|
|
|
|
httpErrorf(w, "invalid arch: %s", archType)
|
2017-09-14 04:45:18 +00:00
|
|
|
continue
|
2017-09-14 03:33:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// New path based off package data
|
|
|
|
newPath := conf.PoolPackagePath(distroName, archType, f.Info.Package)
|
|
|
|
|
|
|
|
if _, err := os.Stat(newPath); err != nil {
|
|
|
|
if os.IsNotExist(err) {
|
|
|
|
if err := os.MkdirAll(newPath, 0755); err != nil {
|
|
|
|
httpErrorf(w, "error creating path: %s", err)
|
2017-09-14 04:45:18 +00:00
|
|
|
continue
|
2017-09-14 03:33:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
f.Path = path.Join(conf.RelativePoolPackagePath(distroName, archType, f.Info.Package), part.FileName())
|
|
|
|
|
2017-09-14 04:23:39 +00:00
|
|
|
if err := copyFile(tempFile, path.Join(newPath, part.FileName())); err != nil {
|
|
|
|
httpErrorf(w, "error copying temporary file: %s", err)
|
2017-09-14 04:45:18 +00:00
|
|
|
continue
|
2017-09-14 04:23:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if err := os.Remove(tempFile); err != nil {
|
|
|
|
httpErrorf(w, "unable to remove temporary file: %s", err)
|
2017-09-14 04:45:18 +00:00
|
|
|
continue
|
2017-09-14 03:33:29 +00:00
|
|
|
}
|
|
|
|
|
2017-06-12 00:41:49 +00:00
|
|
|
if p, exists := packages[f.Info.Package]; exists {
|
|
|
|
v1, err1 := semver.Parse(p.Info.Version)
|
|
|
|
v2, err2 := semver.Parse(f.Info.Version)
|
2017-06-11 07:18:59 +00:00
|
|
|
|
2017-06-12 00:41:49 +00:00
|
|
|
if err1 == nil && err2 == nil && v1.Compare(v2) > 0 && !force {
|
|
|
|
// Don't replace newer package
|
|
|
|
httpErrorf(w, "version in old package is greater than new: %s, %s - override with \"force\"", p.Info.Version, f.Info.Version)
|
2017-09-14 04:45:18 +00:00
|
|
|
continue
|
2017-06-12 00:41:49 +00:00
|
|
|
}
|
2017-06-11 07:18:59 +00:00
|
|
|
|
2017-06-12 00:41:49 +00:00
|
|
|
// Archive old file
|
|
|
|
log.Println("Replacing", p.Name, "with", f.Name)
|
2017-06-11 07:18:59 +00:00
|
|
|
|
2018-10-14 08:14:17 +00:00
|
|
|
oldPath := path.Join(conf.Repo.Root, p.Path)
|
|
|
|
|
2018-10-14 08:35:50 +00:00
|
|
|
// If oldPath == newPath then we already overwrote it
|
|
|
|
if oldPath != newPath {
|
|
|
|
if err := os.Remove(oldPath); err != nil && !os.IsNotExist(err) {
|
|
|
|
httpErrorf(w, "Unable to remove old package: %s", err)
|
|
|
|
continue
|
|
|
|
}
|
2017-06-12 00:41:49 +00:00
|
|
|
}
|
2017-06-11 07:18:59 +00:00
|
|
|
}
|
|
|
|
|
2017-06-12 00:41:49 +00:00
|
|
|
packages[f.Info.Package] = f
|
|
|
|
}
|
2017-06-11 07:18:59 +00:00
|
|
|
|
2017-06-12 00:41:49 +00:00
|
|
|
log.Println("got lock, updating package list...")
|
|
|
|
|
|
|
|
// Recreate the package index and release file.
|
|
|
|
|
2017-09-14 03:33:29 +00:00
|
|
|
for archType, _ := range modifiedArches {
|
|
|
|
if err := createPackagesCached(conf, distroName, archType, distro.Architectures[archType]); err != nil {
|
|
|
|
httpErrorf(w, "error creating package: %s", err)
|
|
|
|
return
|
|
|
|
}
|
2017-06-12 00:41:49 +00:00
|
|
|
}
|
|
|
|
|
2017-09-11 02:58:50 +00:00
|
|
|
err = createRelease(conf, distroName)
|
2017-06-12 00:41:49 +00:00
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
httpErrorf(w, "error creating package: %s", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-09-14 03:33:29 +00:00
|
|
|
err = saveCache(distro)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
httpErrorf(w, "error updating cache: %s", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-06-12 00:41:49 +00:00
|
|
|
w.WriteHeader(http.StatusCreated)
|
|
|
|
}
|
2017-06-11 07:18:59 +00:00
|
|
|
|
2017-06-12 00:41:49 +00:00
|
|
|
func deleteHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
if r.Method != "DELETE" {
|
|
|
|
http.Error(w, "method not supported", http.StatusMethodNotAllowed)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
var req DeleteObj
|
|
|
|
|
|
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
|
|
httpErrorf(w, "failed to decode json: %s", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
mutex.RLock()
|
|
|
|
|
|
|
|
distro, exists := distros[req.DistroName]
|
|
|
|
|
|
|
|
if !exists {
|
|
|
|
httpErrorf(w, "invalid distro: %s", req.DistroName)
|
2017-06-11 07:18:59 +00:00
|
|
|
mutex.RUnlock()
|
2017-06-12 00:41:49 +00:00
|
|
|
return
|
|
|
|
}
|
2017-06-11 07:18:59 +00:00
|
|
|
|
2017-06-12 00:41:49 +00:00
|
|
|
packages, exists := distro.Architectures[req.Arch]
|
2017-06-11 07:18:59 +00:00
|
|
|
|
2017-06-12 00:41:49 +00:00
|
|
|
if !exists {
|
|
|
|
httpErrorf(w, "invalid arch: %s", req.Arch)
|
|
|
|
mutex.RUnlock()
|
|
|
|
return
|
|
|
|
}
|
2017-06-11 07:18:59 +00:00
|
|
|
|
2017-06-12 00:41:49 +00:00
|
|
|
mutex.RUnlock()
|
2017-06-11 07:18:59 +00:00
|
|
|
|
2017-06-12 00:41:49 +00:00
|
|
|
key := r.URL.Query().Get("key")
|
2017-06-11 06:14:13 +00:00
|
|
|
|
2017-06-12 00:41:49 +00:00
|
|
|
if key == "" || key != conf.Http.Key {
|
|
|
|
http.Error(w, "unauthorized", http.StatusForbidden)
|
|
|
|
return
|
|
|
|
}
|
2017-06-11 06:14:13 +00:00
|
|
|
|
2017-09-14 03:33:29 +00:00
|
|
|
debPath := path.Join(conf.ArchPath(req.DistroName, req.Arch), req.Filename)
|
2017-06-12 00:41:49 +00:00
|
|
|
|
|
|
|
if err := os.Remove(debPath); err != nil {
|
|
|
|
httpErrorf(w, "failed to delete: %s", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
mutex.Lock()
|
|
|
|
defer mutex.Unlock()
|
|
|
|
|
|
|
|
if err := createPackagesCached(conf, req.DistroName, req.Arch, packages); err != nil {
|
|
|
|
httpErrorf(w, "failed to delete package: %s", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-09-11 02:58:50 +00:00
|
|
|
if err := createRelease(conf, req.DistroName); err != nil {
|
2017-06-12 00:41:49 +00:00
|
|
|
httpErrorf(w, "failed to delete package: %s", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
w.WriteHeader(http.StatusOK)
|
2017-06-11 06:14:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func httpErrorf(w http.ResponseWriter, format string, a ...interface{}) {
|
|
|
|
err := fmt.Errorf(format, a...)
|
|
|
|
log.Println(err)
|
|
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
|
|
}
|