コード例 #1
0
ファイル: editstate.go プロジェクト: nico/pond
func do() bool {
	if err := system.IsSafe(); err != nil {
		fmt.Fprintf(os.Stderr, "System checks failed: %s\n", err)
		return false
	}

	editor := os.Getenv("EDITOR")
	if len(editor) == 0 {
		fmt.Fprintf(os.Stderr, "$EDITOR is not set\n")
		return false
	}

	stateFile := &disk.StateFile{
		Path: *stateFileName,
		Rand: crypto_rand.Reader,
		Log: func(format string, args ...interface{}) {
			fmt.Fprintf(os.Stderr, format, args...)
		},
	}

	stateLock, err := stateFile.Lock(false /* don't create */)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Cannot open state file: %s\n", err)
		return false
	}
	if stateLock == nil {
		fmt.Fprintf(os.Stderr, "Cannot obtain lock on state file\n")
		return false
	}
	defer stateLock.Close()

	var state *disk.State
	var passphrase string
	for {
		state, err = stateFile.Read(passphrase)
		if err == nil {
			break
		}
		if err != disk.BadPasswordError {
			fmt.Fprintf(os.Stderr, "Failed to decrypt state file: %s\n", err)
			return false
		}

		fmt.Fprintf(os.Stderr, "Passphrase: ")
		passphraseBytes, err := terminal.ReadPassword(0)
		fmt.Fprintf(os.Stderr, "\n")
		if err != nil {
			fmt.Fprintf(os.Stderr, "Failed to read password\n")
			return false
		}
		passphrase = string(passphraseBytes)
	}

	tempDir, err := system.SafeTempDir()
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to get safe temp directory: %s\n", err)
		return false
	}

	tempFile, err := ioutil.TempFile(tempDir, "pond-editstate-")
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to create temp file: %s\n", err)
		return false
	}
	tempFileName := tempFile.Name()
	defer func() {
		os.Remove(tempFileName)
	}()

	signals := make(chan os.Signal, 8)
	signal.Notify(signals, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM)
	go func() {
		<-signals
		println("Caught signal: removing", tempFileName)
		os.Remove(tempFileName)
		os.Exit(1)
	}()

	entities := serialise(tempFile, state)

	var newStateSerialized []byte
	for {
		cmd := exec.Command(editor, tempFileName)
		cmd.Stdin = os.Stdin
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr

		if err := cmd.Run(); err != nil {
			fmt.Fprintf(os.Stderr, "Failed to run editor: %s\n", err)
			return false
		}
		tempFile.Close()
		tempFile, err := os.Open(tempFileName)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Failed to open temp file: %s\n", err)
			return false
		}

		newState := new(disk.State)
		err = parse(newState, tempFile, entities)
		if err == nil {
			newStateSerialized, err = proto.Marshal(newState)
		}
		if err == nil {
			break
		}

		fmt.Fprintf(os.Stderr, "Error parsing: %s\n", err)
		fmt.Fprintf(os.Stderr, "Hit enter to edit again, or Ctrl-C to abort\n")

		var buf [100]byte
		os.Stdin.Read(buf[:])
	}

	states := make(chan disk.NewState)
	done := make(chan struct{})
	go stateFile.StartWriter(states, done)
	states <- disk.NewState{newStateSerialized, false}
	close(states)
	<-done

	return true
}
コード例 #2
0
ファイル: editstate.go プロジェクト: radii/pond
func do() bool {
	if err := system.IsSafe(); err != nil {
		fmt.Fprintf(os.Stderr, "System checks failed: %s\n", err)
		return false
	}

	editor := os.Getenv("EDITOR")
	if len(editor) == 0 {
		fmt.Fprintf(os.Stderr, "$EDITOR is not set\n")
		return false
	}

	stateFile, err := os.Open(*stateFileName)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to open state file: %s\n", err)
		return false
	}
	defer stateFile.Close()

	stateLock, ok := disk.LockStateFile(stateFile)
	if !ok {
		fmt.Fprintf(os.Stderr, "Cannot obtain lock on state file\n")
		return false
	}
	defer stateLock.Close()

	encrypted, err := ioutil.ReadAll(stateFile)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to read state file: %s\n", err)
		return false
	}

	salt, ok := disk.GetSCryptSaltFromState(encrypted)
	if !ok {
		fmt.Fprintf(os.Stderr, "State file is too short to be valid\n")
		return false
	}

	var state *disk.State
	var key [32]byte

	for {
		state, err = disk.LoadState(encrypted, &key)
		if err == nil {
			break
		}
		if err != disk.BadPasswordError {
			fmt.Fprintf(os.Stderr, "Failed to decrypt state file: %s\n", err)
			return false
		}

		fmt.Fprintf(os.Stderr, "Passphrase: ")
		password, err := terminal.ReadPassword(0)
		fmt.Fprintf(os.Stderr, "\n")
		if err != nil {
			fmt.Fprintf(os.Stderr, "Failed to read password\n")
			return false
		}
		keySlice, err := disk.DeriveKey(string(password), &salt)
		copy(key[:], keySlice)
	}

	tempDir, err := system.SafeTempDir()
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to get safe temp directory: %s\n", err)
		return false
	}

	tempFile, err := ioutil.TempFile(tempDir, "pond-editstate-")
	if err != nil {
		fmt.Fprintf(os.Stderr, "Failed to create temp file: %s\n", err)
		return false
	}
	tempFileName := tempFile.Name()
	defer func() {
		os.Remove(tempFileName)
	}()

	signals := make(chan os.Signal, 8)
	signal.Notify(signals, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM)
	go func() {
		<-signals
		println("Caught signal: removing", tempFileName)
		os.Remove(tempFileName)
		os.Exit(1)
	}()

	entities := serialise(tempFile, state)

	var newStateSerialized []byte
	for {
		cmd := exec.Command(editor, tempFileName)
		cmd.Stdin = os.Stdin
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr

		if err := cmd.Run(); err != nil {
			fmt.Fprintf(os.Stderr, "Failed to run editor: %s\n", err)
			return false
		}
		tempFile.Close()
		tempFile, err := os.Open(tempFileName)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Failed to open temp file: %s\n", err)
			return false
		}

		newState := new(disk.State)
		err = parse(newState, tempFile, entities)
		if err == nil {
			newStateSerialized, err = proto.Marshal(newState)
		}
		if err == nil {
			break
		}

		fmt.Fprintf(os.Stderr, "Error parsing: %s\n", err)
		fmt.Fprintf(os.Stderr, "Hit enter to edit again, or Ctrl-C to abort\n")

		var buf [100]byte
		os.Stdin.Read(buf[:])
	}

	states := make(chan []byte)
	done := make(chan bool)
	go disk.StateWriter(*stateFileName, &key, &salt, states, done)
	states <- newStateSerialized
	close(states)
	<-done

	return true
}