"Fossies" - the Fresh Open Source Software Archive

Member "geoipupdate-4.3.0/pkg/geoipupdate/config.go" (16 Apr 2020, 5396 Bytes) of package /linux/misc/geoipupdate-4.3.0.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Go source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. See also the last Fossies "Diffs" side-by-side code changes report for "config.go": 4.1.5_vs_4.2.0.

    1 package geoipupdate
    2 
    3 import (
    4     "bufio"
    5     "log"
    6     "net/url"
    7     "os"
    8     "path/filepath"
    9     "regexp"
   10     "strconv"
   11     "strings"
   12 
   13     "github.com/pkg/errors"
   14 )
   15 
   16 // Config is a parsed configuration file.
   17 type Config struct {
   18     AccountID         int
   19     DatabaseDirectory string
   20     LicenseKey        string
   21     LockFile          string
   22     URL               string
   23     EditionIDs        []string
   24     Proxy             *url.URL
   25     PreserveFileTimes bool
   26     Verbose           bool
   27 }
   28 
   29 // NewConfig parses the configuration file.
   30 func NewConfig( // nolint: gocyclo
   31     file,
   32     defaultDatabaseDirectory,
   33     databaseDirectory string,
   34     verbose bool,
   35 ) (*Config, error) {
   36     fh, err := os.Open(filepath.Clean(file))
   37     if err != nil {
   38         return nil, errors.Wrap(err, "error opening file")
   39     }
   40     defer func() {
   41         if err := fh.Close(); err != nil {
   42             log.Fatalf("Error closing config file: %+v", errors.Wrap(err, "closing file"))
   43         }
   44     }()
   45 
   46     config := &Config{}
   47     scanner := bufio.NewScanner(fh)
   48     lineNumber := 0
   49     keysSeen := map[string]struct{}{}
   50     var host, proxy, proxyUserPassword string
   51     for scanner.Scan() {
   52         lineNumber++
   53         line := strings.TrimSpace(scanner.Text())
   54         if line == "" || line[0] == '#' {
   55             continue
   56         }
   57 
   58         fields := strings.Fields(line)
   59         if len(fields) < 2 {
   60             return nil, errors.Errorf("invalid format on line %d", lineNumber)
   61         }
   62         key := fields[0]
   63         value := strings.Join(fields[1:], " ")
   64 
   65         if _, ok := keysSeen[key]; ok {
   66             return nil, errors.Errorf("`%s' is in the config multiple times", key)
   67         }
   68         keysSeen[key] = struct{}{}
   69 
   70         switch key {
   71         case "AccountID", "UserId":
   72             accountID, err := strconv.Atoi(value)
   73             if err != nil {
   74                 return nil, errors.Wrap(err, "invalid account ID format")
   75             }
   76             config.AccountID = accountID
   77             keysSeen["AccountID"] = struct{}{}
   78             keysSeen["UserId"] = struct{}{}
   79         case "DatabaseDirectory":
   80             config.DatabaseDirectory = filepath.Clean(value)
   81         case "EditionIDs", "ProductIds":
   82             config.EditionIDs = strings.Fields(value)
   83             keysSeen["EditionIDs"] = struct{}{}
   84             keysSeen["ProductIds"] = struct{}{}
   85         case "Host":
   86             host = value
   87         case "LicenseKey":
   88             config.LicenseKey = value
   89         case "LockFile":
   90             config.LockFile = filepath.Clean(value)
   91         case "PreserveFileTimes":
   92             if value != "0" && value != "1" {
   93                 return nil, errors.New("`PreserveFileTimes' must be 0 or 1")
   94             }
   95             if value == "1" {
   96                 config.PreserveFileTimes = true
   97             }
   98         case "Proxy":
   99             proxy = value
  100         case "ProxyUserPassword":
  101             proxyUserPassword = value
  102         case "Protocol", "SkipHostnameVerification", "SkipPeerVerification":
  103             // Deprecated.
  104         default:
  105             return nil, errors.Errorf("unknown option on line %d", lineNumber)
  106         }
  107     }
  108 
  109     if err := scanner.Err(); err != nil {
  110         return nil, errors.Wrap(err, "error reading file")
  111     }
  112 
  113     if _, ok := keysSeen["EditionIDs"]; !ok {
  114         return nil, errors.Errorf("the `EditionIDs` option is required")
  115     }
  116 
  117     if _, ok := keysSeen["AccountID"]; !ok {
  118         return nil, errors.Errorf("the `AccountID` option is required")
  119     }
  120 
  121     if _, ok := keysSeen["LicenseKey"]; !ok {
  122         return nil, errors.Errorf("the `LicenseKey` option is required")
  123     }
  124 
  125     // Set defaults & post-process.
  126 
  127     // Argument takes precedence.
  128     if databaseDirectory != "" {
  129         config.DatabaseDirectory = filepath.Clean(databaseDirectory)
  130     }
  131 
  132     if config.DatabaseDirectory == "" {
  133         config.DatabaseDirectory = filepath.Clean(defaultDatabaseDirectory)
  134     }
  135 
  136     config.Verbose = verbose
  137 
  138     if host == "" {
  139         host = "updates.maxmind.com"
  140     }
  141 
  142     if config.LockFile == "" {
  143         config.LockFile = filepath.Join(config.DatabaseDirectory, ".geoipupdate.lock")
  144     }
  145 
  146     config.URL = "https://" + host
  147 
  148     config.Proxy, err = parseProxy(proxy, proxyUserPassword)
  149     if err != nil {
  150         return nil, err
  151     }
  152 
  153     // We used to recommend using 999999 / 000000000000 for free downloads
  154     // and many people still use this combination. With a real account id
  155     // and license key now being required, we want to give those people a
  156     // sensible error message.
  157     if (config.AccountID == 0 || config.AccountID == 999999) && config.LicenseKey == "000000000000" {
  158         return nil, errors.New("geoipupdate requires a valid AccountID and LicenseKey combination")
  159     }
  160 
  161     return config, nil
  162 }
  163 
  164 var schemeRE = regexp.MustCompile(`(?i)\A([a-z][a-z0-9+\-.]*)://`)
  165 
  166 func parseProxy(
  167     proxy,
  168     proxyUserPassword string,
  169 ) (*url.URL, error) {
  170     if proxy == "" {
  171         return nil, nil
  172     }
  173 
  174     // If no scheme is provided, use http.
  175     matches := schemeRE.FindStringSubmatch(proxy)
  176     if matches == nil {
  177         proxy = "http://" + proxy
  178     } else {
  179         scheme := strings.ToLower(matches[1])
  180         // The http package only supports http and socks5.
  181         if scheme != "http" && scheme != "socks5" {
  182             return nil, errors.Errorf("unsupported proxy type: %s", scheme)
  183         }
  184     }
  185 
  186     // Now that we have a scheme, we should be able to parse.
  187     u, err := url.Parse(proxy)
  188     if err != nil {
  189         return nil, errors.Wrap(err, "error parsing proxy URL")
  190     }
  191 
  192     if !strings.Contains(u.Host, ":") {
  193         u.Host += ":1080" // The 1080 default historically came from cURL.
  194     }
  195 
  196     // Historically if the Proxy option had a username and password they would
  197     // override any specified in the ProxyUserPassword option. Continue that.
  198     if u.User != nil {
  199         return u, nil
  200     }
  201 
  202     if proxyUserPassword == "" {
  203         return u, nil
  204     }
  205 
  206     userPassword := strings.SplitN(proxyUserPassword, ":", 2)
  207     if len(userPassword) != 2 {
  208         return nil, errors.New("proxy user/password is malformed")
  209     }
  210     u.User = url.UserPassword(userPassword[0], userPassword[1])
  211 
  212     return u, nil
  213 }