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 }