package filters import ( "bufio" "fmt" "io" "io/ioutil" "net/http" "os" "path/filepath" "strings" "time" "github.com/AdguardTeam/AdGuardHome/util" "github.com/AdguardTeam/golibs/log" ) // Allows printable UTF-8 text with CR, LF, TAB characters func isPrintableText(data []byte) bool { for _, c := range data { if (c >= ' ' && c != 0x7f) || c == '\n' || c == '\r' || c == '\t' { continue } return false } return true } // Download filter data // Return nil on success. Set f.Path to a file path, or "" if the file was not modified func (fs *filterStg) downloadFilter(f *Filter) error { log.Debug("Filters: Downloading filter from %s", f.URL) // create temp file tmpFile, err := ioutil.TempFile(filepath.Join(fs.conf.FilterDir), "") if err != nil { return err } defer func() { if tmpFile != nil { _ = tmpFile.Close() _ = os.Remove(tmpFile.Name()) } }() // create data reader object var reader io.Reader if filepath.IsAbs(f.URL) { f, err := os.Open(f.URL) if err != nil { return fmt.Errorf("open file: %s", err) } defer f.Close() reader = f } else { req, err := http.NewRequest("GET", f.URL, nil) if err != nil { return err } if len(f.LastModified) != 0 { req.Header.Add("If-Modified-Since", f.LastModified) } resp, err := fs.conf.HTTPClient.Do(req) if resp != nil && resp.Body != nil { defer resp.Body.Close() } if err != nil { f.networkError = true return err } if resp.StatusCode == 304 { // "NOT_MODIFIED" log.Debug("Filters: filter %s isn't modified since %s", f.URL, f.LastModified) f.LastUpdated = time.Now() f.Path = "" return nil } else if resp.StatusCode != 200 { err := fmt.Errorf("Filters: Couldn't download filter from %s: status code: %d", f.URL, resp.StatusCode) return err } f.LastModified = resp.Header.Get("Last-Modified") reader = resp.Body } // parse and validate data, write to a file err = writeFile(f, reader, tmpFile) if err != nil { return err } // Closing the file before renaming it is necessary on Windows _ = tmpFile.Close() fname := fs.filePath(*f) err = os.Rename(tmpFile.Name(), fname) if err != nil { return err } tmpFile = nil // prevent from deleting this file in "defer" handler log.Debug("Filters: saved filter %s at %s", f.URL, fname) f.Path = fname f.LastUpdated = time.Now() return nil } func gatherUntil(dst []byte, dstLen int, src []byte, until int) int { num := util.MinInt(len(src), until-dstLen) return copy(dst[dstLen:], src[:num]) } func isHTML(buf []byte) bool { s := strings.ToLower(string(buf)) return strings.Contains(s, "