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 // /usr/share/dict/words contains derogatory words
50 // which we don't think should be used in public usernames.
51 func derogatory(s string) bool {
52 if s == "bitch" {
53 return true
54 }
55 if strings.HasPrefix(s, "nigger") {
56 return true
57 }
58 return false
59 }
61 // RandomUsername generates a random username from the dictionary file at dictpath.
62 // The dictionary file should be a newline delimited text file, one word per line.
63 // On Unix systems, the file /usr/share/dic/words is a suitable file.
64 func RandomUsername(dictpath string) (string, error) {
65 f, err := os.Open(dictpath)
66 if err != nil {
67 return "", fmt.Errorf("open dictionary: %w", err)
68 }
69 defer f.Close()
71 var first, second string
72 for {
73 first, err = randomLine(f)
74 if err != nil {
75 return "", fmt.Errorf("first random word: %w", err)
76 }
77 first = strings.ToLower(first)
78 if !derogatory(first) {
79 break
80 }
81 }
82 for {
83 second, err = randomLine(f)
84 if err != nil {
85 return "", fmt.Errorf("second random word: %w", err)
86 }
87 second = strings.ToLower(second)
88 if !derogatory(second) {
89 break
90 }
91 }
92 return fmt.Sprintf("%s_%s%02d", first, second, rand.Intn(99)), nil
93 }