github.com/decred/politeia@v1.4.0/util/file.go (about)

     1  // Copyright (c) 2017-2019 The Decred developers
     2  // Use of this source code is governed by an ISC
     3  // license that can be found in the LICENSE file.
     4  
     5  package util
     6  
     7  import (
     8  	"crypto/sha256"
     9  	"encoding/base64"
    10  	"encoding/hex"
    11  	"io"
    12  	"os"
    13  	"os/user"
    14  	"path/filepath"
    15  	"runtime"
    16  	"strings"
    17  
    18  	"github.com/decred/politeia/politeiad/api/v1/mime"
    19  )
    20  
    21  // MimeFile returns the MIME type of a file.
    22  func MimeFile(filename string) (string, error) {
    23  	f, err := os.Open(filename)
    24  	if err != nil {
    25  		return "", err
    26  	}
    27  	defer f.Close()
    28  
    29  	// We need up to 512 bytes
    30  	b := make([]byte, 512)
    31  	n, err := f.Read(b)
    32  	if err != nil {
    33  		return "", err
    34  	}
    35  
    36  	// Clip buffer to prevent detecting binary files.
    37  	return mime.DetectMimeType(b[:n]), nil
    38  }
    39  
    40  // DigestFile returns the SHA256 of a file.
    41  func DigestFile(filename string) (string, error) {
    42  	b, err := DigestFileBytes(filename)
    43  	if err != nil {
    44  		return "", err
    45  	}
    46  	return hex.EncodeToString(b), nil
    47  }
    48  
    49  // DigestFileBytes returns the SHA256 of a file.
    50  func DigestFileBytes(filename string) ([]byte, error) {
    51  	h := sha256.New()
    52  	f, err := os.Open(filename)
    53  	if err != nil {
    54  		return nil, err
    55  	}
    56  	defer f.Close()
    57  	if _, err = io.Copy(h, f); err != nil {
    58  		return nil, err
    59  	}
    60  
    61  	return h.Sum(nil), nil
    62  }
    63  
    64  // Base64File returns the base64 content of a file.
    65  func Base64File(filename string) (string, error) {
    66  	b, err := os.ReadFile(filename)
    67  	if err != nil {
    68  		return "", err
    69  	}
    70  
    71  	return base64.StdEncoding.EncodeToString(b), nil
    72  }
    73  
    74  // LoadFile loads a file of disk and returns the MIME type, the sha256 digest
    75  // and the payload encoded as base64.  If any of the intermediary operations
    76  // fail the function will return an error instead.
    77  func LoadFile(filename string) (mimeType string, digest string, payload string, err error) {
    78  	var b []byte // file payload
    79  	b, err = os.ReadFile(filename)
    80  	if err != nil {
    81  		return
    82  	}
    83  
    84  	// MIME
    85  	mimeType = mime.DetectMimeType(b)
    86  
    87  	// Digest
    88  	h := sha256.New()
    89  	h.Write(b)
    90  	digest = hex.EncodeToString(h.Sum(nil))
    91  
    92  	// Payload
    93  	payload = base64.StdEncoding.EncodeToString(b)
    94  
    95  	return
    96  }
    97  
    98  // LoadFile2 returns a file and its mime type.
    99  func LoadFile2(filename string) (string, []byte, error) {
   100  	var b []byte // file payload
   101  	b, err := os.ReadFile(filename)
   102  	if err != nil {
   103  		return "", nil, err
   104  	}
   105  
   106  	return mime.DetectMimeType(b), b, nil
   107  }
   108  
   109  // FilesExists reports whether the named file or directory exists.
   110  func FileExists(name string) bool {
   111  	if _, err := os.Stat(name); err != nil {
   112  		if os.IsNotExist(err) {
   113  			return false
   114  		}
   115  	}
   116  	return true
   117  }
   118  
   119  // CleanAndExpandPath expands environment variables and leading ~ in the
   120  // passed path, cleans the result, and returns it.
   121  func CleanAndExpandPath(path string) string {
   122  	// Nothing to do when no path is given.
   123  	if path == "" {
   124  		return path
   125  	}
   126  
   127  	// NOTE: The os.ExpandEnv doesn't work with Windows cmd.exe-style
   128  	// %VARIABLE%, but the variables can still be expanded via POSIX-style
   129  	// $VARIABLE.
   130  	path = os.ExpandEnv(path)
   131  
   132  	if !strings.HasPrefix(path, "~") {
   133  		return filepath.Clean(path)
   134  	}
   135  
   136  	// Expand initial ~ to the current user's home directory, or ~otheruser
   137  	// to otheruser's home directory.  On Windows, both forward and backward
   138  	// slashes can be used.
   139  	path = path[1:]
   140  
   141  	var pathSeparators string
   142  	if runtime.GOOS == "windows" {
   143  		pathSeparators = string(os.PathSeparator) + "/"
   144  	} else {
   145  		pathSeparators = string(os.PathSeparator)
   146  	}
   147  
   148  	userName := ""
   149  	if i := strings.IndexAny(path, pathSeparators); i != -1 {
   150  		userName = path[:i]
   151  		path = path[i:]
   152  	}
   153  
   154  	homeDir := ""
   155  	var u *user.User
   156  	var err error
   157  	if userName == "" {
   158  		u, err = user.Current()
   159  	} else {
   160  		u, err = user.Lookup(userName)
   161  	}
   162  	if err == nil {
   163  		homeDir = u.HomeDir
   164  	}
   165  	// Fallback to CWD if user lookup fails or user has no home directory.
   166  	if homeDir == "" {
   167  		homeDir = "."
   168  	}
   169  
   170  	return filepath.Join(homeDir, path)
   171  }