github.com/jfrog/jfrog-cli-core/v2@v2.52.0/utils/ioutils/ioutils.go (about)

     1  package ioutils
     2  
     3  import (
     4  	"bufio"
     5  	"errors"
     6  	"fmt"
     7  	"github.com/jfrog/jfrog-cli-core/v2/utils/coreutils"
     8  	"github.com/jfrog/jfrog-client-go/utils/errorutils"
     9  	"github.com/jfrog/jfrog-client-go/utils/io/fileutils"
    10  	"github.com/jfrog/jfrog-client-go/utils/log"
    11  	"golang.org/x/term"
    12  	"io"
    13  	"os"
    14  	"path/filepath"
    15  	"strings"
    16  	"syscall"
    17  )
    18  
    19  // disallowUsingSavedPassword - Prevent changing username or url without changing the password.
    20  // False if the user changed the username or the url.
    21  func ReadCredentialsFromConsole(details, savedDetails coreutils.Credentials, disallowUsingSavedPassword bool) error {
    22  	if details.GetUser() == "" {
    23  		tempUser := ""
    24  		ScanFromConsole("JFrog username", &tempUser, savedDetails.GetUser())
    25  		details.SetUser(tempUser)
    26  		disallowUsingSavedPassword = true
    27  	}
    28  	if details.GetPassword() == "" {
    29  		password, err := ScanJFrogPasswordFromConsole()
    30  		if err != nil {
    31  			return err
    32  		}
    33  		details.SetPassword(password)
    34  		if details.GetPassword() == "" && !disallowUsingSavedPassword {
    35  			details.SetPassword(savedDetails.GetPassword())
    36  		}
    37  	}
    38  
    39  	return nil
    40  }
    41  
    42  func ScanJFrogPasswordFromConsole() (string, error) {
    43  	return ScanPasswordFromConsole("JFrog password or API key: ")
    44  }
    45  
    46  func ScanPasswordFromConsole(message string) (string, error) {
    47  	fmt.Print(coreutils.PrintLink(message))
    48  	bytePassword, err := term.ReadPassword(int(syscall.Stdin)) //nolint:unconvert
    49  	if err != nil {
    50  		return "", errorutils.CheckError(err)
    51  	}
    52  	// New-line required after the password input:
    53  	log.Output()
    54  	return string(bytePassword), nil
    55  }
    56  
    57  func ScanFromConsole(caption string, scanInto *string, defaultValue string) {
    58  	caption = coreutils.PrintLink(caption)
    59  	if defaultValue != "" {
    60  		caption = caption + " [" + defaultValue + "]"
    61  	}
    62  	fmt.Print(caption + ": ")
    63  	scanner := bufio.NewScanner(os.Stdin)
    64  	scanner.Scan()
    65  	*scanInto = scanner.Text()
    66  	if *scanInto == "" {
    67  		*scanInto = defaultValue
    68  	}
    69  	*scanInto = strings.TrimSpace(*scanInto)
    70  }
    71  
    72  func DoubleWinPathSeparator(filePath string) string {
    73  	return strings.ReplaceAll(filePath, "\\", "\\\\")
    74  }
    75  
    76  func UnixToWinPathSeparator(filePath string) string {
    77  	return strings.ReplaceAll(filePath, "/", "\\\\")
    78  }
    79  
    80  func WinToUnixPathSeparator(filePath string) string {
    81  	return strings.ReplaceAll(filePath, "\\", "/")
    82  }
    83  
    84  // BackupFile creates a backup of the file in filePath. The backup will be found at backupPath.
    85  // The returned restore function can be called to restore the file's state - the file in filePath will be replaced by the backup in backupPath.
    86  // If there is no file at filePath, a backup file won't be created, and the restore function will delete the file at filePath.
    87  func BackupFile(filePath, backupFileName string) (restore func() error, err error) {
    88  	fileInfo, err := os.Stat(filePath)
    89  	if errorutils.CheckError(err) != nil {
    90  		if os.IsNotExist(err) {
    91  			restore = createRestoreFileFunc(filePath, backupFileName)
    92  			err = nil
    93  		}
    94  		return
    95  	}
    96  
    97  	if err = cloneFile(filePath, backupFileName, fileInfo.Mode()); err != nil {
    98  		return
    99  	}
   100  	log.Debug("The file", filePath, "was backed up successfully to", backupFileName)
   101  	restore = createRestoreFileFunc(filePath, backupFileName)
   102  	return
   103  }
   104  
   105  func cloneFile(origFile, newName string, fileMode os.FileMode) (err error) {
   106  	from, err := os.Open(origFile)
   107  	if errorutils.CheckError(err) != nil {
   108  		return
   109  	}
   110  	defer func() {
   111  		err = errors.Join(err, from.Close())
   112  	}()
   113  
   114  	to, err := os.OpenFile(filepath.Join(filepath.Dir(origFile), newName), os.O_RDWR|os.O_CREATE, fileMode)
   115  	if errorutils.CheckError(err) != nil {
   116  		return
   117  	}
   118  	defer func() {
   119  		err = errors.Join(err, to.Close())
   120  	}()
   121  
   122  	if _, err = io.Copy(to, from); err != nil {
   123  		err = errorutils.CheckError(err)
   124  	}
   125  	return
   126  }
   127  
   128  // createRestoreFileFunc creates a function for restoring a file from its backup.
   129  // The returned function replaces the file in filePath with the backup in backupPath.
   130  // If there is no file at backupPath (which means there was no file at filePath when BackupFile() was called), then the function deletes the file at filePath.
   131  func createRestoreFileFunc(filePath, backupFileName string) func() error {
   132  	return func() error {
   133  		backupPath := filepath.Join(filepath.Dir(filePath), backupFileName)
   134  		if _, err := os.Stat(backupPath); err != nil {
   135  			if os.IsNotExist(err) {
   136  				// We verify the existence of the file in the specified filePath before initiating its deletion in order to prevent errors that might occur when attempting to remove a non-existent file
   137  				var fileExists bool
   138  				fileExists, err = fileutils.IsFileExists(filePath, false)
   139  				if err != nil {
   140  					err = fmt.Errorf("failed to check for the existence of '%s' before deleting the file: %s", filePath, err.Error())
   141  					return errorutils.CheckError(err)
   142  				}
   143  				if fileExists {
   144  					err = os.Remove(filePath)
   145  				}
   146  				return errorutils.CheckError(err)
   147  			}
   148  			return errorutils.CheckErrorf(createRestoreErrorPrefix(filePath, backupPath) + err.Error())
   149  		}
   150  
   151  		if err := fileutils.MoveFile(backupPath, filePath); err != nil {
   152  			return errorutils.CheckError(err)
   153  		}
   154  		log.Debug("Restored the file", filePath, "successfully")
   155  		return nil
   156  	}
   157  }
   158  
   159  func createRestoreErrorPrefix(filePath, backupPath string) string {
   160  	return fmt.Sprintf("An error occurred while restoring the file: %s\n"+
   161  		"To restore the file manually: delete %s and rename the backup file at %s (if exists) to '%s'.\n"+
   162  		"Failure cause: ",
   163  		filePath, filePath, backupPath, filePath)
   164  }