diff --git a/AGHTechDoc.md b/AGHTechDoc.md index a5ad69ad..41413046 100644 --- a/AGHTechDoc.md +++ b/AGHTechDoc.md @@ -1439,7 +1439,7 @@ Request: { "name": "..." - "url": "..." + "url": "..." // URL or an absolute file path "whitelist": true } diff --git a/home/control_filtering.go b/home/control_filtering.go index 837d2a45..4c820ec1 100644 --- a/home/control_filtering.go +++ b/home/control_filtering.go @@ -8,15 +8,22 @@ import ( "net/http" "net/url" "os" + "path/filepath" "strings" "time" + "github.com/AdguardTeam/AdGuardHome/util" "github.com/AdguardTeam/golibs/log" "github.com/miekg/dns" ) -// IsValidURL - return TRUE if URL is valid +// IsValidURL - return TRUE if URL or file path is valid func IsValidURL(rawurl string) bool { + if filepath.IsAbs(rawurl) { + // this is a file path + return util.FileExists(rawurl) + } + url, err := url.ParseRequestURI(rawurl) if err != nil { return false //Couldn't even parse the rawurl @@ -42,7 +49,7 @@ func (f *Filtering) handleFilteringAddURL(w http.ResponseWriter, r *http.Request } if !IsValidURL(fj.URL) { - http.Error(w, "Invalid URL", http.StatusBadRequest) + http.Error(w, "Invalid URL or file path", http.StatusBadRequest) return } @@ -100,11 +107,6 @@ func (f *Filtering) handleFilteringRemoveURL(w http.ResponseWriter, r *http.Requ return } - if !IsValidURL(req.URL) { - http.Error(w, "URL parameter is not valid request URL", http.StatusBadRequest) - return - } - // go through each element and delete if url matches config.Lock() newFilters := []filter{} @@ -154,7 +156,7 @@ func (f *Filtering) handleFilteringSetURL(w http.ResponseWriter, r *http.Request } if !IsValidURL(fj.URL) { - http.Error(w, "invalid URL", http.StatusBadRequest) + http.Error(w, "invalid URL or file path", http.StatusBadRequest) return } diff --git a/home/filter.go b/home/filter.go index 494d98e5..7b632747 100644 --- a/home/filter.go +++ b/home/filter.go @@ -69,7 +69,7 @@ func defaultFilters() []filter { // field ordering is important -- yaml fields will mirror ordering from here type filter struct { Enabled bool - URL string + URL string // URL or a file path Name string `yaml:"name"` RulesCount int `yaml:"-"` LastUpdated time.Time `yaml:"-"` @@ -500,6 +500,7 @@ func (f *Filtering) update(filter *filter) (bool, error) { return b, err } +// nolint(gocyclo) func (f *Filtering) updateIntl(filter *filter) (bool, error) { log.Tracef("Downloading update for filter %d from %s", filter.ID, filter.URL) @@ -514,18 +515,29 @@ func (f *Filtering) updateIntl(filter *filter) (bool, error) { } }() - resp, err := Context.client.Get(filter.URL) - if resp != nil && resp.Body != nil { - defer resp.Body.Close() - } - if err != nil { - log.Printf("Couldn't request filter from URL %s, skipping: %s", filter.URL, err) - return false, err - } + var reader io.Reader + if filepath.IsAbs(filter.URL) { + f, err := os.Open(filter.URL) + if err != nil { + return false, fmt.Errorf("open file: %s", err) + } + defer f.Close() + reader = f + } else { + resp, err := Context.client.Get(filter.URL) + if resp != nil && resp.Body != nil { + defer resp.Body.Close() + } + if err != nil { + log.Printf("Couldn't request filter from URL %s, skipping: %s", filter.URL, err) + return false, err + } - if resp.StatusCode != 200 { - log.Printf("Got status code %d from URL %s, skipping", resp.StatusCode, filter.URL) - return false, fmt.Errorf("got status code != 200: %d", resp.StatusCode) + if resp.StatusCode != 200 { + log.Printf("Got status code %d from URL %s, skipping", resp.StatusCode, filter.URL) + return false, fmt.Errorf("got status code != 200: %d", resp.StatusCode) + } + reader = resp.Body } htmlTest := true @@ -534,7 +546,7 @@ func (f *Filtering) updateIntl(filter *filter) (bool, error) { buf := make([]byte, 64*1024) total := 0 for { - n, err := resp.Body.Read(buf) + n, err := reader.Read(buf) total += n if htmlTest { diff --git a/openapi/openapi.yaml b/openapi/openapi.yaml index 64fb5e5d..9653af5a 100644 --- a/openapi/openapi.yaml +++ b/openapi/openapi.yaml @@ -507,7 +507,7 @@ paths: tags: - filtering operationId: filteringAddURL - summary: 'Add filter URL' + summary: 'Add filter URL or an absolute file path' consumes: - application/json parameters: @@ -1495,7 +1495,7 @@ definitions: name: type: "string" url: - description: "URL containing filtering rules" + description: "URL or an absolute path to the file containing filtering rules" type: "string" example: "https://filters.adtidy.org/windows/filters/15.txt" RemoveUrlRequest: