[WIP] New updater first working prototype

This commit is contained in:
Vladimir Stoilov
2024-08-16 16:05:01 +03:00
parent abf444630b
commit 9bae1afd73
46 changed files with 4107 additions and 4149 deletions

View File

@@ -1,161 +1,161 @@
package updates
import (
"bytes"
"io"
"net/http"
"os"
"path/filepath"
"strings"
// "bytes"
// "io"
// "net/http"
// "os"
// "path/filepath"
// "strings"
"github.com/ghodss/yaml"
// "github.com/ghodss/yaml"
"github.com/safing/portmaster/base/api"
"github.com/safing/portmaster/base/log"
"github.com/safing/portmaster/base/utils"
// "github.com/safing/portmaster/base/api"
// "github.com/safing/portmaster/base/log"
// "github.com/safing/portmaster/base/utils"
)
const (
apiPathCheckForUpdates = "updates/check"
)
func registerAPIEndpoints() error {
if err := api.RegisterEndpoint(api.Endpoint{
Name: "Check for Updates",
Description: "Checks if new versions are available. If automatic updates are enabled, they are also downloaded and applied.",
Parameters: []api.Parameter{{
Method: http.MethodPost,
Field: "download",
Value: "",
Description: "Force downloading and applying of all updates, regardless of auto-update settings.",
}},
Path: apiPathCheckForUpdates,
Write: api.PermitUser,
ActionFunc: func(r *api.Request) (msg string, err error) {
// Check if we should also download regardless of settings.
downloadAll := r.URL.Query().Has("download")
// func registerAPIEndpoints() error {
// if err := api.RegisterEndpoint(api.Endpoint{
// Name: "Check for Updates",
// Description: "Checks if new versions are available. If automatic updates are enabled, they are also downloaded and applied.",
// Parameters: []api.Parameter{{
// Method: http.MethodPost,
// Field: "download",
// Value: "",
// Description: "Force downloading and applying of all updates, regardless of auto-update settings.",
// }},
// Path: apiPathCheckForUpdates,
// Write: api.PermitUser,
// ActionFunc: func(r *api.Request) (msg string, err error) {
// // Check if we should also download regardless of settings.
// downloadAll := r.URL.Query().Has("download")
// Trigger update task.
err = TriggerUpdate(true, downloadAll)
if err != nil {
return "", err
}
// // Trigger update task.
// err = TriggerUpdate(true, downloadAll)
// if err != nil {
// return "", err
// }
// Report how we triggered.
if downloadAll {
return "downloading all updates...", nil
}
return "checking for updates...", nil
},
}); err != nil {
return err
}
// // Report how we triggered.
// if downloadAll {
// return "downloading all updates...", nil
// }
// return "checking for updates...", nil
// },
// }); err != nil {
// return err
// }
if err := api.RegisterEndpoint(api.Endpoint{
Name: "Get Resource",
Description: "Returns the requested resource from the udpate system",
Path: `updates/get/{identifier:[A-Za-z0-9/\.\-_]{1,255}}`,
Read: api.PermitUser,
ReadMethod: http.MethodGet,
HandlerFunc: func(w http.ResponseWriter, r *http.Request) {
// Get identifier from URL.
var identifier string
if ar := api.GetAPIRequest(r); ar != nil {
identifier = ar.URLVars["identifier"]
}
if identifier == "" {
http.Error(w, "no resource speicified", http.StatusBadRequest)
return
}
// if err := api.RegisterEndpoint(api.Endpoint{
// Name: "Get Resource",
// Description: "Returns the requested resource from the udpate system",
// Path: `updates/get/{identifier:[A-Za-z0-9/\.\-_]{1,255}}`,
// Read: api.PermitUser,
// ReadMethod: http.MethodGet,
// HandlerFunc: func(w http.ResponseWriter, r *http.Request) {
// // Get identifier from URL.
// var identifier string
// if ar := api.GetAPIRequest(r); ar != nil {
// identifier = ar.URLVars["identifier"]
// }
// if identifier == "" {
// http.Error(w, "no resource speicified", http.StatusBadRequest)
// return
// }
// Get resource.
resource, err := registry.GetFile(identifier)
if err != nil {
http.Error(w, err.Error(), http.StatusNotFound)
return
}
// // Get resource.
// resource, err := registry.GetFile(identifier)
// if err != nil {
// http.Error(w, err.Error(), http.StatusNotFound)
// return
// }
// Open file for reading.
file, err := os.Open(resource.Path())
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
defer file.Close() //nolint:errcheck,gosec
// // Open file for reading.
// file, err := os.Open(resource.Path())
// if err != nil {
// http.Error(w, err.Error(), http.StatusInternalServerError)
// return
// }
// defer file.Close() //nolint:errcheck,gosec
// Assign file to reader
var reader io.Reader = file
// // Assign file to reader
// var reader io.Reader = file
// Add version to header.
w.Header().Set("Resource-Version", resource.Version())
// // Add version to header.
// w.Header().Set("Resource-Version", resource.Version())
// Set Content-Type.
contentType, _ := utils.MimeTypeByExtension(filepath.Ext(resource.Path()))
w.Header().Set("Content-Type", contentType)
// // Set Content-Type.
// contentType, _ := utils.MimeTypeByExtension(filepath.Ext(resource.Path()))
// w.Header().Set("Content-Type", contentType)
// Check if the content type may be returned.
accept := r.Header.Get("Accept")
if accept != "" {
mimeTypes := strings.Split(accept, ",")
// First, clean mime types.
for i, mimeType := range mimeTypes {
mimeType = strings.TrimSpace(mimeType)
mimeType, _, _ = strings.Cut(mimeType, ";")
mimeTypes[i] = mimeType
}
// Second, check if we may return anything.
var acceptsAny bool
for _, mimeType := range mimeTypes {
switch mimeType {
case "*", "*/*":
acceptsAny = true
}
}
// Third, check if we can convert.
if !acceptsAny {
var converted bool
sourceType, _, _ := strings.Cut(contentType, ";")
findConvertiblePair:
for _, mimeType := range mimeTypes {
switch {
case sourceType == "application/yaml" && mimeType == "application/json":
yamlData, err := io.ReadAll(reader)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
jsonData, err := yaml.YAMLToJSON(yamlData)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
reader = bytes.NewReader(jsonData)
converted = true
break findConvertiblePair
}
}
// // Check if the content type may be returned.
// accept := r.Header.Get("Accept")
// if accept != "" {
// mimeTypes := strings.Split(accept, ",")
// // First, clean mime types.
// for i, mimeType := range mimeTypes {
// mimeType = strings.TrimSpace(mimeType)
// mimeType, _, _ = strings.Cut(mimeType, ";")
// mimeTypes[i] = mimeType
// }
// // Second, check if we may return anything.
// var acceptsAny bool
// for _, mimeType := range mimeTypes {
// switch mimeType {
// case "*", "*/*":
// acceptsAny = true
// }
// }
// // Third, check if we can convert.
// if !acceptsAny {
// var converted bool
// sourceType, _, _ := strings.Cut(contentType, ";")
// findConvertiblePair:
// for _, mimeType := range mimeTypes {
// switch {
// case sourceType == "application/yaml" && mimeType == "application/json":
// yamlData, err := io.ReadAll(reader)
// if err != nil {
// http.Error(w, err.Error(), http.StatusInternalServerError)
// return
// }
// jsonData, err := yaml.YAMLToJSON(yamlData)
// if err != nil {
// http.Error(w, err.Error(), http.StatusInternalServerError)
// return
// }
// reader = bytes.NewReader(jsonData)
// converted = true
// break findConvertiblePair
// }
// }
// If we could not convert to acceptable format, return an error.
if !converted {
http.Error(w, "conversion to requested format not supported", http.StatusNotAcceptable)
return
}
}
}
// // If we could not convert to acceptable format, return an error.
// if !converted {
// http.Error(w, "conversion to requested format not supported", http.StatusNotAcceptable)
// return
// }
// }
// }
// Write file.
w.WriteHeader(http.StatusOK)
if r.Method != http.MethodHead {
_, err = io.Copy(w, reader)
if err != nil {
log.Errorf("updates: failed to serve resource file: %s", err)
return
}
}
},
}); err != nil {
return err
}
// // Write file.
// w.WriteHeader(http.StatusOK)
// if r.Method != http.MethodHead {
// _, err = io.Copy(w, reader)
// if err != nil {
// log.Errorf("updates: failed to serve resource file: %s", err)
// return
// }
// }
// },
// }); err != nil {
// return err
// }
return nil
}
// return nil
// }