github.com/yaling888/clash@v1.53.0/config/initial.go (about)

     1  package config
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"net/http"
     7  	"os"
     8  
     9  	"github.com/phuslu/log"
    10  
    11  	"github.com/yaling888/clash/common/convert"
    12  	"github.com/yaling888/clash/component/mmdb"
    13  	C "github.com/yaling888/clash/constant"
    14  )
    15  
    16  func downloadMMDB(path string) (err error) {
    17  	resp, err := doGet("geoip", "Country.mmdb")
    18  	if err != nil {
    19  		return
    20  	}
    21  	defer resp.Body.Close()
    22  
    23  	f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0o644)
    24  	if err != nil {
    25  		return err
    26  	}
    27  	defer f.Close()
    28  	_, err = io.Copy(f, resp.Body)
    29  
    30  	return err
    31  }
    32  
    33  func initMMDB() error {
    34  	if _, err := os.Stat(C.Path.MMDB()); os.IsNotExist(err) {
    35  		log.Info().Msg("[Config] can't find MMDB, start download")
    36  		if err := downloadMMDB(C.Path.MMDB()); err != nil {
    37  			return fmt.Errorf("can't download MMDB: %w", err)
    38  		}
    39  		log.Info().Msg("[Config] download MMDB finish")
    40  	}
    41  
    42  	if !mmdb.Verify() {
    43  		log.Info().Msg("[Config] invalid MMDB, remove and download")
    44  		if err := os.Remove(C.Path.MMDB()); err != nil {
    45  			return fmt.Errorf("can't remove invalid MMDB: %w", err)
    46  		}
    47  
    48  		if err := downloadMMDB(C.Path.MMDB()); err != nil {
    49  			return fmt.Errorf("can't download MMDB: %w", err)
    50  		}
    51  		log.Info().Msg("[Config] download MMDB finish")
    52  	}
    53  
    54  	return nil
    55  }
    56  
    57  func downloadGeoSite(path string) (err error) {
    58  	resp, err := doGet("geosite", "geosite.dat")
    59  	if err != nil {
    60  		return
    61  	}
    62  	defer resp.Body.Close()
    63  
    64  	f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0o644)
    65  	if err != nil {
    66  		return err
    67  	}
    68  	defer f.Close()
    69  	_, err = io.Copy(f, resp.Body)
    70  
    71  	return err
    72  }
    73  
    74  func initGeoSite() error {
    75  	if _, err := os.Stat(C.Path.GeoSite()); os.IsNotExist(err) {
    76  		log.Info().Msg("[Config] can't find GeoSite.dat, start download")
    77  		if err := downloadGeoSite(C.Path.GeoSite()); err != nil {
    78  			return fmt.Errorf("can't download GeoSite.dat: %w", err)
    79  		}
    80  		log.Info().Msg("[Config] download GeoSite.dat finish")
    81  	}
    82  
    83  	if err := verifyGeoSite(C.Path.GeoSite()); err != nil {
    84  		log.Info().Msg("[Config] invalid GeoSite.dat, remove and download")
    85  		if err := os.Remove(C.Path.GeoSite()); err != nil {
    86  			return fmt.Errorf("can't remove invalid GeoSite.dat: %w", err)
    87  		}
    88  
    89  		if err := downloadGeoSite(C.Path.GeoSite()); err != nil {
    90  			return fmt.Errorf("can't download GeoSite.dat: %w", err)
    91  		}
    92  		log.Info().Msg("[Config] download GeoSite.dat finish")
    93  	}
    94  
    95  	return nil
    96  }
    97  
    98  // Init prepare necessary files
    99  func Init(dir string) error {
   100  	// initial homedir
   101  	if _, err := os.Stat(dir); os.IsNotExist(err) {
   102  		if err := os.MkdirAll(dir, 0o777); err != nil {
   103  			return fmt.Errorf("can't create config directory %s: %w", dir, err)
   104  		}
   105  	}
   106  
   107  	// initial config.yaml
   108  	if _, err := os.Stat(C.Path.Config()); os.IsNotExist(err) {
   109  		log.Info().Msg("[Config] can't find config, create a initial config file")
   110  		f, err := os.OpenFile(C.Path.Config(), os.O_CREATE|os.O_WRONLY, 0o644)
   111  		if err != nil {
   112  			return fmt.Errorf("can't create file %s: %w", C.Path.Config(), err)
   113  		}
   114  		_, _ = f.Write([]byte(`mixed-port: 7890`))
   115  		_ = f.Close()
   116  	}
   117  
   118  	// initial mmdb
   119  	if err := initMMDB(); err != nil {
   120  		return fmt.Errorf("can't initial MMDB: %w", err)
   121  	}
   122  
   123  	// initial GeoSite
   124  	if err := initGeoSite(); err != nil {
   125  		return fmt.Errorf("can't initial GeoSite: %w", err)
   126  	}
   127  	return nil
   128  }
   129  
   130  func doGet(name, file string) (resp *http.Response, err error) {
   131  	var (
   132  		req     *http.Request
   133  		mirrors = []string{
   134  			"https://raw.githubusercontent.com/yaling888/%s/release/%s",
   135  			"https://cdn.jsdelivr.net/gh/yaling888/%s@release/%s",
   136  			"https://gcore.jsdelivr.net/gh/yaling888/%s@release/%s",
   137  			"https://testingcf.jsdelivr.net/gh/yaling888/%s@release/%s",
   138  			"https://fastly.jsdelivr.net/gh/yaling888/%s@release/%s",
   139  		}
   140  	)
   141  	for _, m := range mirrors {
   142  		req, err = http.NewRequest(http.MethodGet, fmt.Sprintf(m, name, file), nil)
   143  		if err != nil {
   144  			continue
   145  		}
   146  
   147  		log.Info().Msgf("[Config] try to download %s from %s", file, req.Host)
   148  
   149  		convert.SetUserAgent(req.Header)
   150  
   151  		resp, err = http.DefaultClient.Do(req)
   152  		if err == nil {
   153  			if resp.StatusCode != http.StatusOK {
   154  				_ = resp.Body.Close()
   155  				continue
   156  			}
   157  			return
   158  		}
   159  	}
   160  	return
   161  }