Blob


1 package mailmux
3 import (
4 "bufio"
5 "errors"
6 "fmt"
7 "io"
8 "math/rand"
9 "os"
10 "strings"
11 )
13 func randomLine(f *os.File) (string, error) {
14 fi, err := f.Stat()
15 if err != nil {
16 return "", err
17 }
18 offset := rand.Int63n(fi.Size())
19 offset, err = f.Seek(offset, io.SeekStart)
20 if err != nil {
21 return "", err
22 }
24 br := bufio.NewReader(f)
25 for {
26 b, err := br.ReadByte()
27 if b == '\n' {
28 break
29 }
30 if err != nil {
31 return "", err
32 }
33 }
34 line, err := br.ReadString('\n')
35 if errors.Is(err, io.EOF) {
36 // the file ends without a newline - no problem
37 } else if err != nil {
38 return "", err
39 }
41 line = strings.TrimSpace(line)
42 if line == "" {
43 // empty line. we're either at the end or hit a blank line. try again
44 return randomLine(f)
45 }
46 return line, nil
47 }
49 // RandomUsername generates a random username from the dictionary file at dictpath.
50 // The dictionary file should be a newline delimited text file, one word per line.
51 // On Unix systems, the file /usr/share/dic/words is a suitable file.
52 func RandomUsername(dictpath string) (string, error) {
53 f, err := os.Open(dictpath)
54 if err != nil {
55 return "", fmt.Errorf("open dictionary: %w", err)
56 }
57 defer f.Close()
59 first, err := randomLine(f)
60 if err != nil {
61 return "", fmt.Errorf("first random word: %w", err)
62 }
63 first = strings.ToLower(first)
64 second, err := randomLine(f)
65 if err != nil {
66 return "", fmt.Errorf("second random word: %w", err)
67 }
68 second = strings.ToLower(second)
70 return fmt.Sprintf("%s_%s%02d", first, second, rand.Intn(99)), nil
71 }