func handleLog(ctx *cli.Context, client *daemon.Client) error { log, err := client.Log(nil, nil) if err != nil { return err } for _, pnode := range log.Nodes { commitMH, err := multihash.Cast(pnode.Hash) if err != nil { return err } pcmt := pnode.Commit if pcmt == nil { return fmt.Errorf("Empty commit in log-commit") } rootMH, err := multihash.Cast(pcmt.Root) if err != nil { return err } fmt.Printf( "%s/%s by %s, %s\n", colors.Colorize(commitMH.B58String()[:10], colors.Green), colors.Colorize(rootMH.B58String()[:10], colors.Magenta), pcmt.Author, pcmt.Message, ) } return nil }
func printCheckpoint(checkpoint *store.Checkpoint, idx, historylen int) { threeWayRune, twoWayRune := treeRuneTri, treeRunePipe if idx == historylen-1 { threeWayRune, twoWayRune = treeRuneCorner, " " } fmt.Printf( " %s%s %s #%d (%s by %s)\n", threeWayRune, treeRuneBar, colors.Colorize("Checkpoint", colors.Cyan), historylen-idx, colors.Colorize(checkpoint.ChangeType().String(), colors.Red), colors.Colorize(string(checkpoint.Author()), colors.Magenta), ) fmt.Printf( " %s ├─ % 9s: %v\n", twoWayRune, colors.Colorize("Hash", colors.Green), checkpoint.Hash().B58String(), ) fmt.Printf( " %s └─ % 9s: %v\n", twoWayRune, colors.Colorize("Date", colors.Yellow), time.Now(), // TODO: Use actual checkpoint mtime ) }
func createStrengthPrompt(password []rune, prefix string) string { symbol, color := "", colors.Red strength := zxcvbn.PasswordStrength(string(password), nil) switch { case strength.Score <= 1: symbol = "✗" color = colors.Red case strength.Score <= 2: symbol = "⚡" color = colors.Magenta case strength.Score <= 3: symbol = "⚠" color = colors.Yellow case strength.Score <= 4: symbol = "✔" color = colors.Green } prompt := colors.Colorize(symbol, color) if strength.Entropy > 0 { entropy := fmt.Sprintf(" %3.0f", strength.Entropy) prompt += colors.Colorize(entropy, colors.Cyan) } else { prompt += colors.Colorize(" ENT", colors.Cyan) } prompt += colors.Colorize(" "+prefix+"passphrase: ", color) return prompt }
func printRemoteEntry(re *daemon.RemoteEntry) { state := colors.Colorize("offline", colors.Red) if re.IsOnline { state = colors.Colorize("online", colors.Green) } fmt.Printf("%s %-7s %s\n", re.Hash, state, re.Ident) }
// PromptNewPassword asks the user to input a password. // // While typing, the user gets feedback by the prompt color, // which changes with the security of the password to green. // Additionally the entrtopy of the password is shown. // If minEntropy was not reached after hitting enter, // this function will log a message and ask the user again. func PromptNewPassword(minEntropy float64) ([]byte, error) { rl, err := readline.New("") if err != nil { return nil, err } defer util.Closer(rl) passwordCfg := rl.GenPasswordConfig() passwordCfg.SetListener(func(line []rune, pos int, key rune) (newLine []rune, newPos int, ok bool) { rl.SetPrompt(createStrengthPrompt(line, " New ")) rl.Refresh() return nil, 0, false }) pwd := []byte{} for { pwd, err = rl.ReadPasswordWithConfig(passwordCfg) if err != nil { return nil, err } strength := zxcvbn.PasswordStrength(string(pwd), nil) if strength.Entropy >= minEntropy { break } fmt.Printf(colors.Colorize(msgLowEntropy, colors.Yellow)+"\n", minEntropy) } passwordCfg.SetListener(func(line []rune, pos int, key rune) (newLine []rune, newPos int, ok bool) { rl.SetPrompt(createStrengthPrompt(line, "Retype ")) rl.Refresh() return nil, 0, false }) fmt.Println(colors.Colorize(msgReEnter, colors.Green)) for { newPwd, err := rl.ReadPasswordWithConfig(passwordCfg) if err != nil { return nil, err } if bytes.Equal(pwd, newPwd) { break } fmt.Println(colors.Colorize(msgBadPassword, colors.Yellow)) } return pwd, nil }
func printChangesByUser(changesByuser changeByType) error { for typ, checkpoints := range changesByuser { changeType := store.ChangeType(typ) fmt.Printf("\n %s:\n", strings.Title((&changeType).String())) for _, change := range checkpoints { msg := fmt.Sprintf( " %s (%s)", "TODO: resolve checkpoint to path", humanize.Bytes(uint64(change.Size())), ) color := 0 switch typ { case store.ChangeAdd: color = colors.Green case store.ChangeRemove: color = colors.Red case store.ChangeModify: color = colors.Yellow case store.ChangeMove: color = colors.Cyan default: return fmt.Errorf("Invalid change type") } fmt.Println(colors.Colorize(msg, color)) } } return nil }
func handleList(ctx *cli.Context, client *daemon.Client) error { path := "/" if ctx.NArg() > 0 { path = prefixSlash(ctx.Args().First()) } depth := ctx.Int("depth") if ctx.Bool("recursive") { depth = -1 } entries, err := client.List(path, depth) if err != nil { return ExitCode{ UnknownError, fmt.Sprintf("ls: %v", err), } } for _, entry := range entries { modTime := time.Time{} if err := modTime.UnmarshalText(entry.ModTime); err != nil { log.Warningf("Could not parse mtime (%s): %v", entry.ModTime, err) continue } fmt.Printf( "%s\t%s\t%s\n", colors.Colorize( humanize.Bytes(uint64(entry.NodeSize)), colors.Green, ), colors.Colorize( humanize.Time(modTime), colors.Cyan, ), colors.Colorize( entry.Path, colors.Magenta, ), ) } return nil }
func (n *treeNode) Print() { parents := make([]*treeNode, n.depth) curr := n sort.Sort(n) // You could do probably go upwards and print to // a string buffer for performance, but this is "fun" code... for i := 0; i < n.depth; i++ { parents[n.depth-i-1] = curr curr = curr.parent } for i := 0; i < n.depth; i++ { if i == n.depth-1 { if n.isLast { fmt.Printf("%s", treeRuneCorner) } else { fmt.Printf("%s", treeRuneTri) } } else { if parents[i].isLast { fmt.Printf("%s ", " ") } else { fmt.Printf("%s ", treeRunePipe) } } } name, prefix := n.name, treeRuneBar switch { case n.name == "/": name, prefix = colors.Colorize(n.name, colors.Magenta), "" case n.entry.Type == store.NodeTypeDirectory: name = colors.Colorize(name, colors.Green) } fmt.Printf("%s%s\n", prefix, name) for _, child := range n.order { child.Print() } }
func handleDaemonPing(ctx *cli.Context, client *daemon.Client) error { for i := 0; i < 100; i++ { before := time.Now() symbol := colors.Colorize("✔", colors.Green) if !client.Ping() { symbol = colors.Colorize("✘", colors.Red) } delay := time.Since(before) fmt.Printf("#%02d %s ➔ %s: %s (%v)\n", i+1, client.LocalAddr().String(), client.RemoteAddr().String(), symbol, delay) time.Sleep(1 * time.Second) } return nil }
func promptPasswordColored(color int) (string, error) { prompt := "Password: "******"", err } defer util.Closer(rl) return doPromptLine(rl, prompt, true) }
func handleHistory(ctx *cli.Context, client *daemon.Client) error { repoPath := prefixSlash(ctx.Args().First()) history, err := client.History(repoPath) if err != nil { return ExitCode{ UnknownError, fmt.Sprintf("Unable to retrieve history: %v", err), } } fmt.Println(colors.Colorize(repoPath, colors.Magenta)) for idx := range history { checkpoint := history[len(history)-idx-1] printCheckpoint(checkpoint, idx, len(history)) } return nil }
func handleStatus(ctx *cli.Context, client *daemon.Client) error { status, err := client.Status() if err != nil { return err } statusCmt := status.Commit if statusCmt == nil { return fmt.Errorf("Empty status commit in response.") } checkpoints := statusCmt.Checkpoints if len(checkpoints) == 0 { fmt.Println("Nothing to commit.") return nil } userToChanges := make(map[id.ID]changeByType) for _, pckp := range checkpoints { byAuthor, ok := userToChanges[id.ID(pckp.Author)] if !ok { byAuthor = make(changeByType) userToChanges[id.ID(pckp.Author)] = byAuthor } byAuthor[pckp.Change] = append(byAuthor[pckp.Change], pckp) } for user, changesByuser := range userToChanges { fmt.Printf( "Changes by %s:\n", colors.Colorize(string(user), colors.Magenta), ) printChangesByUser(changesByuser) } return nil }