deb-simple/http.go

330 lines
7.2 KiB
Go
Raw Normal View History

2018-10-14 08:17:04 +00:00
package main
2017-06-11 06:14:13 +00:00
import (
"encoding/json"
"fmt"
"github.com/blang/semver"
log "github.com/sirupsen/logrus"
"io"
"net/http"
"os"
"path"
"strings"
2017-06-11 06:14:13 +00:00
)
2020-06-14 08:29:16 +00:00
func requireAuth(fn http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
key := extractAuthorization(r)
if key == "" || key != conf.Http.Key {
http.Error(w, "unauthorized", http.StatusForbidden)
return
}
fn(w, r)
}
}
func extractAuthorization(r *http.Request) string {
auth := r.Header.Get("Authorization")
if auth != "" {
idx := strings.Index(auth, " ")
if idx == -1 || auth[0:idx] != "Token" {
return ""
}
return auth[idx+1:]
}
return r.URL.Query().Get("key")
}
2017-06-12 00:41:49 +00:00
func rescanHandler(w http.ResponseWriter, r *http.Request) {
mutex.Lock()
defer mutex.Unlock()
2017-06-12 00:41:49 +00:00
distroName := r.URL.Query().Get("distro")
2017-06-12 00:41:49 +00:00
if distroName == "" {
distroName = "stable"
}
2017-06-12 00:41:49 +00:00
if _, exists := distros[distroName]; !exists {
2020-06-14 08:29:16 +00:00
httpErrorf(w, http.StatusBadRequest, "invalid distro %s", distroName)
2017-06-12 00:41:49 +00:00
return
}
2017-06-12 00:41:49 +00:00
distro := &Distro{Name: distroName, Architectures: make(map[string]map[string]*PackageFile)}
2017-06-12 00:41:49 +00:00
scanInitialPackages(conf, distro)
saveCache(distro)
2017-06-12 00:41:49 +00:00
distros[distroName] = distro
w.WriteHeader(http.StatusOK)
w.Write([]byte("ok"))
2017-06-12 00:41:49 +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
}
queryArchType := r.URL.Query().Get("arch")
if queryArchType == "" {
queryArchType = "all"
2017-06-12 00:41:49 +00:00
}
2017-06-12 00:41:49 +00:00
distroName := r.URL.Query().Get("distro")
2017-06-12 00:41:49 +00:00
if distroName == "" {
distroName = "stable"
}
2017-06-12 00:41:49 +00:00
force := false
2017-06-12 00:41:49 +00:00
forceStr := r.URL.Query().Get("force")
if forceStr == "true" {
2017-06-12 00:41:49 +00:00
force = true
}
2017-06-12 00:41:49 +00:00
reader, err := r.MultipartReader()
2017-06-12 00:41:49 +00:00
if err != nil {
2020-06-14 08:29:16 +00:00
httpErrorf(w, http.StatusInternalServerError, "error creating multipart reader: %s", err)
2017-06-12 00:41:49 +00:00
return
}
2017-06-12 00:41:49 +00:00
mutex.RLock()
2017-06-12 00:41:49 +00:00
distro, exists := distros[distroName]
2017-06-12 00:41:49 +00:00
if !exists {
2020-06-14 08:29:16 +00:00
httpErrorf(w, http.StatusBadRequest, "invalid distro: %s", distroName)
2017-06-12 00:41:49 +00:00
mutex.RUnlock()
return
}
2017-06-12 00:41:49 +00:00
mutex.RUnlock()
2017-06-12 00:41:49 +00:00
// Lock to prevent concurrent modification
mutex.Lock()
defer mutex.Unlock()
modifiedArches := make(map[string]bool)
2017-06-12 00:41:49 +00:00
for {
part, err := reader.NextPart()
2020-06-14 08:29:16 +00:00
if err != nil {
2017-06-12 00:41:49 +00:00
break
}
if part.FileName() == "" || !strings.HasSuffix(part.FileName(), ".deb"){
2017-06-12 00:41:49 +00:00
continue
}
2020-06-14 08:29:16 +00:00
archType, err := loadAndCheckPackage(distroName, distro, part.FileName(), part, force)
2017-06-12 00:41:49 +00:00
if err != nil {
2020-06-14 08:29:16 +00:00
httpErrorf(w, http.StatusInternalServerError, err.Error())
2017-06-11 06:14:13 +00:00
return
}
2020-06-14 08:29:16 +00:00
modifiedArches[archType] = true
}
2020-06-14 08:29:16 +00:00
// Recreate the package index and release file.
for archType, _ := range modifiedArches {
log.WithFields(log.Fields{
"arch": archType,
}).Debug("Updating package list...")
2020-06-14 08:29:16 +00:00
if err := createPackagesCached(conf, distroName, archType, distro.Architectures[archType]); err != nil {
httpErrorf(w, http.StatusInternalServerError, "error creating package: %s", err)
2017-06-11 06:14:13 +00:00
return
}
2020-06-14 08:29:16 +00:00
}
2017-06-11 06:14:13 +00:00
log.WithFields(log.Fields{
"distroName": distroName,
}).Debug("Updating release files...")
2020-06-14 08:29:16 +00:00
err = createRelease(conf, distroName)
2020-06-14 08:29:16 +00:00
if err != nil {
httpErrorf(w, http.StatusInternalServerError, "error creating package: %s", err)
return
}
2017-06-11 06:14:13 +00:00
log.Debug("Dumping cache...")
2020-06-14 08:29:16 +00:00
err = saveCache(distro)
2020-06-14 08:29:16 +00:00
if err != nil {
httpErrorf(w, http.StatusInternalServerError, "error updating cache: %s", err)
return
}
log.Debug("Done.")
2020-06-14 08:29:16 +00:00
w.WriteHeader(http.StatusCreated)
}
2020-06-14 08:29:16 +00:00
func copyToTemp(fileName string, r io.Reader) (string, error) {
dst, err := tmpFs.Create(fileName)
2020-06-14 08:29:16 +00:00
if err != nil {
return "", fmt.Errorf("error creating deb file: %s", err)
}
2020-06-14 08:29:16 +00:00
defer dst.Close()
2020-06-14 08:29:16 +00:00
if _, err := io.Copy(dst, r); err != nil {
return "", fmt.Errorf("error creating deb file: %s", err)
}
2020-06-14 08:29:16 +00:00
return fileName, nil
}
2020-06-14 08:29:16 +00:00
func loadAndCheckPackage(distroName string, distro *Distro, fileName string, r io.Reader, force bool) (string, error) {
tempFile, err := copyToTemp(fileName, r)
2017-09-14 04:23:39 +00:00
2020-06-14 08:29:16 +00:00
if err != nil {
return "", err
}
2020-06-14 08:29:16 +00:00
// Get package name, if it already exists remove the old file.
f, err := newPackageFile(tmpFs, tempFile)
2020-06-14 08:29:16 +00:00
if err != nil {
return "", fmt.Errorf("error loading package info: %s", err)
}
archType := f.Info.Architecture
2020-06-14 08:29:16 +00:00
// Get current packages
packages, exists := distro.Architectures[archType]
2020-06-14 08:29:16 +00:00
if !exists {
return "", fmt.Errorf("invalid arch: %s", archType)
}
// New path based off package data
newPath := conf.PoolPackagePath(distroName, archType, f.Info.Package)
2018-10-14 08:14:17 +00:00
2020-06-14 08:29:16 +00:00
if _, err := fs.Stat(newPath); err != nil {
if os.IsNotExist(err) {
if err := fs.MkdirAll(newPath, 0755); err != nil {
return "", fmt.Errorf("error creating path: %s", err)
2017-06-12 00:41:49 +00:00
}
}
2017-06-12 00:41:49 +00:00
}
2020-06-14 08:29:16 +00:00
f.Path = path.Join(conf.RelativePoolPackagePath(distroName, archType, f.Info.Package), fileName)
2017-06-12 00:41:49 +00:00
if err := copyFile(tmpFs, tempFile, fs, path.Join(newPath, fileName)); err != nil {
2020-06-14 08:29:16 +00:00
return "", fmt.Errorf("error copying temporary file: %s", err)
}
2017-06-12 00:41:49 +00:00
if err := tmpFs.Remove(tempFile); err != nil {
2020-06-14 08:29:16 +00:00
return "", fmt.Errorf("unable to remove temporary file: %s", err)
2017-06-12 00:41:49 +00:00
}
2020-06-14 08:29:16 +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-12 00:41:49 +00:00
2020-06-14 08:29:16 +00:00
if err1 == nil && err2 == nil {
if v1.Compare(v2) > 0 && !force {
// Don't replace newer package
return "", fmt.Errorf("version in old package is greater than new: %s, %s - override with \"force\"", p.Info.Version, f.Info.Version)
}
}
2017-06-12 00:41:49 +00:00
2020-06-14 08:29:16 +00:00
// Archive old file
log.WithFields(log.Fields{
"existing": p.Path,
"new": f.Path,
}).Debug("Replacing existing file")
2020-06-14 08:29:16 +00:00
// If oldPath == newPath then we already overwrote it
if path.Base(p.Path) != path.Base(f.Path) {
2020-06-14 08:29:16 +00:00
if err := fs.Remove(p.Path); err != nil && !os.IsNotExist(err) {
return "", fmt.Errorf("unable to remove old package: %s", err)
}
}
}
2020-06-14 08:29:16 +00:00
packages[f.Info.Package] = f
return archType, nil
2017-06-12 00:41:49 +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 {
2020-06-14 08:29:16 +00:00
httpErrorf(w, http.StatusBadRequest, "failed to decode json: %s", err)
2017-06-12 00:41:49 +00:00
return
}
mutex.RLock()
distro, exists := distros[req.DistroName]
if !exists {
2020-06-14 08:29:16 +00:00
httpErrorf(w, http.StatusBadRequest, "invalid distro: %s", req.DistroName)
mutex.RUnlock()
2017-06-12 00:41:49 +00:00
return
}
2017-06-12 00:41:49 +00:00
packages, exists := distro.Architectures[req.Arch]
2017-06-12 00:41:49 +00:00
if !exists {
2020-06-14 08:29:16 +00:00
httpErrorf(w, http.StatusBadRequest, "invalid arch: %s", req.Arch)
2017-06-12 00:41:49 +00:00
mutex.RUnlock()
return
}
2017-06-12 00:41:49 +00:00
mutex.RUnlock()
debPath := path.Join(conf.ArchPath(req.DistroName, req.Arch), req.Filename)
2017-06-12 00:41:49 +00:00
2020-06-14 08:29:16 +00:00
if err := fs.Remove(debPath); err != nil {
httpErrorf(w, http.StatusInternalServerError, "failed to delete: %s", err)
2017-06-12 00:41:49 +00:00
return
}
mutex.Lock()
defer mutex.Unlock()
if err := createPackagesCached(conf, req.DistroName, req.Arch, packages); err != nil {
2020-06-14 08:29:16 +00:00
httpErrorf(w, http.StatusInternalServerError, "failed to delete package: %s", err)
2017-06-12 00:41:49 +00:00
return
}
2017-09-11 02:58:50 +00:00
if err := createRelease(conf, req.DistroName); err != nil {
2020-06-14 08:29:16 +00:00
httpErrorf(w, http.StatusInternalServerError, "failed to delete package: %s", err)
2017-06-12 00:41:49 +00:00
return
}
w.WriteHeader(http.StatusOK)
2017-06-11 06:14:13 +00:00
}
2020-06-14 08:29:16 +00:00
func httpErrorf(w http.ResponseWriter, code int, format string, a ...interface{}) {
2017-06-11 06:14:13 +00:00
err := fmt.Errorf(format, a...)
log.Println(err)
2020-06-14 08:29:16 +00:00
http.Error(w, err.Error(), code)
2017-06-11 06:14:13 +00:00
}