config.go 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. // +build !confonly
  2. package core
  3. import (
  4. "io"
  5. "io/ioutil"
  6. "os"
  7. "strings"
  8. "github.com/golang/protobuf/proto"
  9. "v2ray.com/core/common"
  10. "v2ray.com/core/common/buf"
  11. "v2ray.com/core/common/cmdarg"
  12. )
  13. // ConfigFormat is a configurable format of V2Ray config file.
  14. type ConfigFormat struct {
  15. Name string
  16. Extension []string
  17. Loader ConfigLoader
  18. }
  19. // ConfigLoader is a utility to load V2Ray config from external source.
  20. type ConfigLoader func(input interface{}) (*Config, error)
  21. var (
  22. configLoaderByName = make(map[string]*ConfigFormat)
  23. configLoaderByExt = make(map[string]*ConfigFormat)
  24. )
  25. // RegisterConfigLoader add a new ConfigLoader.
  26. func RegisterConfigLoader(format *ConfigFormat) error {
  27. name := strings.ToLower(format.Name)
  28. if _, found := configLoaderByName[name]; found {
  29. return newError(format.Name, " already registered.")
  30. }
  31. configLoaderByName[name] = format
  32. for _, ext := range format.Extension {
  33. lext := strings.ToLower(ext)
  34. if f, found := configLoaderByExt[lext]; found {
  35. return newError(ext, " already registered to ", f.Name)
  36. }
  37. configLoaderByExt[lext] = format
  38. }
  39. return nil
  40. }
  41. func getExtension(filename string) string {
  42. idx := strings.LastIndexByte(filename, '.')
  43. if idx == -1 {
  44. return ""
  45. }
  46. return filename[idx+1:]
  47. }
  48. // LoadConfig loads config with given format from given source.
  49. // input accepts 2 different types:
  50. // * []string slice of multiple filename/url(s) to open to read
  51. // * io.Reader that reads a config content (the original way)
  52. func LoadConfig(formatName string, filename string, input interface{}) (*Config, error) {
  53. ext := getExtension(filename)
  54. if len(ext) > 0 {
  55. if f, found := configLoaderByExt[ext]; found {
  56. return f.Loader(input)
  57. }
  58. }
  59. if f, found := configLoaderByName[formatName]; found {
  60. return f.Loader(input)
  61. }
  62. return nil, newError("Unable to load config in ", formatName).AtWarning()
  63. }
  64. func loadProtobufConfig(data []byte) (*Config, error) {
  65. config := new(Config)
  66. if err := proto.Unmarshal(data, config); err != nil {
  67. return nil, err
  68. }
  69. return config, nil
  70. }
  71. func init() {
  72. common.Must(RegisterConfigLoader(&ConfigFormat{
  73. Name: "Protobuf",
  74. Extension: []string{"pb"},
  75. Loader: func(input interface{}) (*Config, error) {
  76. switch v := input.(type) {
  77. case cmdarg.Arg:
  78. if len(v) == 0 {
  79. return nil, newError("input has no element")
  80. }
  81. var data []byte
  82. var rerr error
  83. // pb type can only handle the first config
  84. if v[0] == "stdin:" {
  85. data, rerr = buf.ReadAllToBytes(os.Stdin)
  86. } else {
  87. data, rerr = ioutil.ReadFile(v[0])
  88. }
  89. common.Must(rerr)
  90. return loadProtobufConfig(data)
  91. case io.Reader:
  92. data, err := buf.ReadAllToBytes(v)
  93. common.Must(err)
  94. return loadProtobufConfig(data)
  95. default:
  96. return nil, newError("unknow type")
  97. }
  98. },
  99. }))
  100. }