1// Copyright 2015 Keybase, Inc. All rights reserved. Use of
2// this source code is governed by the included BSD license.
3
4package libkb
5
6import (
7	"fmt"
8	"regexp"
9	"strings"
10	"unicode"
11
12	"github.com/keybase/client/go/kbun"
13)
14
15var emailRE = regexp.MustCompile(`^\S+@\S+\.\S+$`)
16
17// Also used in shared/signup/device-name/index.tsx
18var deviceRE = regexp.MustCompile(`^[a-zA-Z0-9][ _'a-zA-Z0-9+‘’—–-]*$`)
19var badDeviceRE = regexp.MustCompile(`  |[ '_-]$|['_-][ ]?['_-]`)
20var normalizeDeviceRE = regexp.MustCompile(`[^a-zA-Z0-9]`)
21
22var CheckEmail = Checker{
23	F: func(s string) bool {
24		return len(s) > 3 && emailRE.MatchString(s)
25	},
26	Hint: "must be a valid email address",
27}
28
29var CheckUsername = Checker{
30	F:    kbun.CheckUsername,
31	Hint: "between 2 and 16 characters long",
32}
33
34var CheckEmailOrUsername = Checker{
35	F: func(s string) bool {
36		return CheckEmail.F(s) || CheckUsername.F(s)
37	},
38	Hint: "valid usernames are 2-16 letters long",
39}
40
41var CheckPassphraseSimple = Checker{
42	F: func(s string) bool {
43		return len(s) > 0
44	},
45	Hint: "passphrase cannot be empty",
46}
47
48var CheckPassphraseNew = Checker{
49	F: func(s string) bool {
50		r := []rune(s)
51		if len(r) > 0 && unicode.IsSpace(r[0]) {
52			return false
53		}
54		return len(s) >= MinPassphraseLength
55	},
56	Hint:          fmt.Sprintf("passphrase must be %d or more characters", MinPassphraseLength),
57	PreserveSpace: true,
58}
59
60var CheckInviteCode = Checker{
61	F: func(s string) bool {
62		return len(s) > 4
63	},
64	Hint: "Invite codes are 4 or more characters",
65}
66
67func normalizeDeviceName(s string) string {
68	return strings.ToLower(normalizeDeviceRE.ReplaceAllString(s, ""))
69}
70
71var CheckDeviceName = Checker{
72	F: func(s string) bool {
73		normalized := normalizeDeviceName(s)
74
75		return len(normalized) >= 3 &&
76			len(normalized) <= 64 &&
77			deviceRE.MatchString(s) &&
78			!badDeviceRE.MatchString(s)
79	},
80	Transform: func(s string) string {
81		s = strings.Replace(s, "—", "-", -1) // em dash
82		s = strings.Replace(s, "–", "-", -1) // en dash
83		s = strings.Replace(s, "‘", "'", -1) // curly quote #1
84		s = strings.Replace(s, "", "'", -1) // curly quote #2
85		return s
86	},
87	Normalize: normalizeDeviceName,
88	Hint:      "between 3 and 64 characters long; use a-Z, 0-9, space, plus, underscore, dash and apostrophe",
89}
90
91func MakeCheckKex2SecretPhrase(g *GlobalContext) Checker {
92	return Checker{
93		F: func(s string) bool {
94			if err := validPhrase(s, []int{Kex2PhraseEntropy, Kex2PhraseEntropy2}); err != nil {
95				g.Log.Debug("invalid kex2 phrase: %s", err)
96				return false
97			}
98			return true
99		},
100		Hint: "Wrong secret phrase. Please try again.",
101	}
102}
103
104func IsYes(s string) bool {
105	s = strings.ToLower(strings.TrimSpace(s))
106	return s == "y" || s == "yes"
107}
108
109func IsEmpty(s string) bool {
110	return len(strings.TrimSpace(s)) == 0
111}
112
113func IsNo(s string) bool {
114	s = strings.ToLower(strings.TrimSpace(s))
115	return s == "n" || s == "no"
116}
117
118var CheckYesNo = Checker{
119	F: func(s string) bool {
120		return IsYes(s) || IsNo(s)
121	},
122	Hint: "'yes' or 'no'",
123}
124
125var CheckNotEmpty = Checker{
126	F: func(s string) bool {
127		return len(s) > 0
128	},
129	Hint: "cannot be empty",
130}
131
132type CheckMember struct {
133	Set []string
134}
135
136func (c CheckMember) Checker() Checker {
137	return Checker{
138		F: func(s string) bool {
139			for _, v := range c.Set {
140				if Cicmp(v, s) {
141					return true
142				}
143			}
144			return false
145		},
146		Hint: strings.Join(c.Set, ", "),
147	}
148}
149