Add API Framework Revel Source Files
[iec.git] / src / foundation / api / revel / util.go
diff --git a/src/foundation/api/revel/util.go b/src/foundation/api/revel/util.go
new file mode 100644 (file)
index 0000000..340c599
--- /dev/null
@@ -0,0 +1,280 @@
+// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.
+// Revel Framework source code and usage is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+package revel
+
+import (
+       "bytes"
+       "fmt"
+       "io"
+       "io/ioutil"
+       "net"
+       "net/http"
+       "net/url"
+       "os"
+       "path/filepath"
+       "reflect"
+       "regexp"
+       "strings"
+
+       "github.com/revel/config"
+)
+
+const (
+       // DefaultFileContentType Revel's default response content type
+       DefaultFileContentType = "application/octet-stream"
+)
+
+var (
+       cookieKeyValueParser = regexp.MustCompile("\x00([^:]*):([^\x00]*)\x00")
+       HdrForwardedFor      = http.CanonicalHeaderKey("X-Forwarded-For")
+       HdrRealIP            = http.CanonicalHeaderKey("X-Real-Ip")
+       utilLog              = RevelLog.New("section", "util")
+       mimeConfig           *config.Context
+)
+
+// ExecutableTemplate adds some more methods to the default Template.
+type ExecutableTemplate interface {
+       Execute(io.Writer, interface{}) error
+}
+
+// ExecuteTemplate execute a template and returns the result as a string.
+func ExecuteTemplate(tmpl ExecutableTemplate, data interface{}) string {
+       var b bytes.Buffer
+       if err := tmpl.Execute(&b, data); err != nil {
+               utilLog.Error("ExecuteTemplate: Execute failed", "error", err)
+       }
+       return b.String()
+}
+
+// MustReadLines reads the lines of the given file.  Panics in the case of error.
+func MustReadLines(filename string) []string {
+       r, err := ReadLines(filename)
+       if err != nil {
+               panic(err)
+       }
+       return r
+}
+
+// ReadLines reads the lines of the given file.  Panics in the case of error.
+func ReadLines(filename string) ([]string, error) {
+       dataBytes, err := ioutil.ReadFile(filename)
+       if err != nil {
+               return nil, err
+       }
+       return strings.Split(string(dataBytes), "\n"), nil
+}
+
+func ContainsString(list []string, target string) bool {
+       for _, el := range list {
+               if el == target {
+                       return true
+               }
+       }
+       return false
+}
+
+// FindMethod returns the reflect.Method, given a Receiver type and Func value.
+func FindMethod(recvType reflect.Type, funcVal reflect.Value) *reflect.Method {
+       // It is not possible to get the name of the method from the Func.
+       // Instead, compare it to each method of the Controller.
+       for i := 0; i < recvType.NumMethod(); i++ {
+               method := recvType.Method(i)
+               if method.Func.Pointer() == funcVal.Pointer() {
+                       return &method
+               }
+       }
+       return nil
+}
+
+// ParseKeyValueCookie takes the raw (escaped) cookie value and parses out key values.
+func ParseKeyValueCookie(val string, cb func(key, val string)) {
+       val, _ = url.QueryUnescape(val)
+       if matches := cookieKeyValueParser.FindAllStringSubmatch(val, -1); matches != nil {
+               for _, match := range matches {
+                       cb(match[1], match[2])
+               }
+       }
+}
+
+// LoadMimeConfig load mime-types.conf on init.
+func LoadMimeConfig() {
+       var err error
+       mimeConfig, err = config.LoadContext("mime-types.conf", ConfPaths)
+       if err != nil {
+               utilLog.Fatal("Failed to load mime type config:", "error", err)
+       }
+}
+
+// ContentTypeByFilename returns a MIME content type based on the filename's extension.
+// If no appropriate one is found, returns "application/octet-stream" by default.
+// Additionally, specifies the charset as UTF-8 for text/* types.
+func ContentTypeByFilename(filename string) string {
+       dot := strings.LastIndex(filename, ".")
+       if dot == -1 || dot+1 >= len(filename) {
+               return DefaultFileContentType
+       }
+
+       extension := filename[dot+1:]
+       contentType := mimeConfig.StringDefault(extension, "")
+       if contentType == "" {
+               return DefaultFileContentType
+       }
+
+       if strings.HasPrefix(contentType, "text/") {
+               return contentType + "; charset=utf-8"
+       }
+
+       return contentType
+}
+
+// DirExists returns true if the given path exists and is a directory.
+func DirExists(filename string) bool {
+       fileInfo, err := os.Stat(filename)
+       return err == nil && fileInfo.IsDir()
+}
+
+func FirstNonEmpty(strs ...string) string {
+       for _, str := range strs {
+               if len(str) > 0 {
+                       return str
+               }
+       }
+       return ""
+}
+
+// Equal is a helper for comparing value equality, following these rules:
+//  - Values with equivalent types are compared with reflect.DeepEqual
+//  - int, uint, and float values are compared without regard to the type width.
+//    for example, Equal(int32(5), int64(5)) == true
+//  - strings and byte slices are converted to strings before comparison.
+//  - else, return false.
+func Equal(a, b interface{}) bool {
+       if reflect.TypeOf(a) == reflect.TypeOf(b) {
+               return reflect.DeepEqual(a, b)
+       }
+       switch a.(type) {
+       case int, int8, int16, int32, int64:
+               switch b.(type) {
+               case int, int8, int16, int32, int64:
+                       return reflect.ValueOf(a).Int() == reflect.ValueOf(b).Int()
+               }
+       case uint, uint8, uint16, uint32, uint64:
+               switch b.(type) {
+               case uint, uint8, uint16, uint32, uint64:
+                       return reflect.ValueOf(a).Uint() == reflect.ValueOf(b).Uint()
+               }
+       case float32, float64:
+               switch b.(type) {
+               case float32, float64:
+                       return reflect.ValueOf(a).Float() == reflect.ValueOf(b).Float()
+               }
+       case string:
+               switch b.(type) {
+               case []byte:
+                       return a.(string) == string(b.([]byte))
+               }
+       case []byte:
+               switch b.(type) {
+               case string:
+                       return b.(string) == string(a.([]byte))
+               }
+       }
+       return false
+}
+
+// ClientIP method returns client IP address from HTTP request.
+//
+// Note: Set property "app.behind.proxy" to true only if Revel is running
+// behind proxy like nginx, haproxy, apache, etc. Otherwise
+// you may get inaccurate Client IP address. Revel parses the
+// IP address in the order of X-Forwarded-For, X-Real-IP.
+//
+// By default revel will get http.Request's RemoteAddr
+func ClientIP(r *Request) string {
+       if Config.BoolDefault("app.behind.proxy", false) {
+               // Header X-Forwarded-For
+               if fwdFor := strings.TrimSpace(r.GetHttpHeader(HdrForwardedFor)); fwdFor != "" {
+                       index := strings.Index(fwdFor, ",")
+                       if index == -1 {
+                               return fwdFor
+                       }
+                       return fwdFor[:index]
+               }
+
+               // Header X-Real-Ip
+               if realIP := strings.TrimSpace(r.GetHttpHeader(HdrRealIP)); realIP != "" {
+                       return realIP
+               }
+       }
+
+       if remoteAddr, _, err := net.SplitHostPort(r.RemoteAddr); err == nil {
+               return remoteAddr
+       }
+
+       return ""
+}
+
+// Walk method extends filepath.Walk to also follow symlinks.
+// Always returns the path of the file or directory.
+func Walk(root string, walkFn filepath.WalkFunc) error {
+       return fsWalk(root, root, walkFn)
+}
+
+// createDir method creates nested directories if not exists
+func createDir(path string) error {
+       if _, err := os.Stat(path); err != nil {
+               if os.IsNotExist(err) {
+                       if err = os.MkdirAll(path, 0755); err != nil {
+                               return fmt.Errorf("Failed to create directory '%v': %v", path, err)
+                       }
+               } else {
+                       return fmt.Errorf("Failed to create directory '%v': %v", path, err)
+               }
+       }
+       return nil
+}
+
+func fsWalk(fname string, linkName string, walkFn filepath.WalkFunc) error {
+       fsWalkFunc := func(path string, info os.FileInfo, err error) error {
+               if err != nil {
+                       return err
+               }
+
+               var name string
+               name, err = filepath.Rel(fname, path)
+               if err != nil {
+                       return err
+               }
+
+               path = filepath.Join(linkName, name)
+
+               if err == nil && info.Mode()&os.ModeSymlink == os.ModeSymlink {
+                       var symlinkPath string
+                       symlinkPath, err = filepath.EvalSymlinks(path)
+                       if err != nil {
+                               return err
+                       }
+
+                       // https://github.com/golang/go/blob/master/src/path/filepath/path.go#L392
+                       info, err = os.Lstat(symlinkPath)
+
+                       if err != nil {
+                               return walkFn(path, info, err)
+                       }
+
+                       if info.IsDir() {
+                               return fsWalk(symlinkPath, path, walkFn)
+                       }
+               }
+
+               return walkFn(path, info, err)
+       }
+       err := filepath.Walk(fname, fsWalkFunc)
+       return err
+}
+
+func init() {
+       OnAppStart(LoadMimeConfig)
+}