|
@@ -52,17 +52,19 @@ type AccountManager struct {
|
52
|
52
|
|
53
|
53
|
server *Server
|
54
|
54
|
// track clients logged in to accounts
|
55
|
|
- accountToClients map[string][]*Client
|
56
|
|
- nickToAccount map[string]string
|
57
|
|
- accountToMethod map[string]NickReservationMethod
|
|
55
|
+ accountToClients map[string][]*Client
|
|
56
|
+ nickToAccount map[string]string
|
|
57
|
+ skeletonToAccount map[string]string
|
|
58
|
+ accountToMethod map[string]NickReservationMethod
|
58
|
59
|
}
|
59
|
60
|
|
60
|
61
|
func NewAccountManager(server *Server) *AccountManager {
|
61
|
62
|
am := AccountManager{
|
62
|
|
- accountToClients: make(map[string][]*Client),
|
63
|
|
- nickToAccount: make(map[string]string),
|
64
|
|
- accountToMethod: make(map[string]NickReservationMethod),
|
65
|
|
- server: server,
|
|
63
|
+ accountToClients: make(map[string][]*Client),
|
|
64
|
+ nickToAccount: make(map[string]string),
|
|
65
|
+ skeletonToAccount: make(map[string]string),
|
|
66
|
+ accountToMethod: make(map[string]NickReservationMethod),
|
|
67
|
+ server: server,
|
66
|
68
|
}
|
67
|
69
|
|
68
|
70
|
am.buildNickToAccountIndex()
|
|
@@ -76,6 +78,7 @@ func (am *AccountManager) buildNickToAccountIndex() {
|
76
|
78
|
}
|
77
|
79
|
|
78
|
80
|
nickToAccount := make(map[string]string)
|
|
81
|
+ skeletonToAccount := make(map[string]string)
|
79
|
82
|
accountToMethod := make(map[string]NickReservationMethod)
|
80
|
83
|
existsPrefix := fmt.Sprintf(keyAccountExists, "")
|
81
|
84
|
|
|
@@ -91,11 +94,21 @@ func (am *AccountManager) buildNickToAccountIndex() {
|
91
|
94
|
account := strings.TrimPrefix(key, existsPrefix)
|
92
|
95
|
if _, err := tx.Get(fmt.Sprintf(keyAccountVerified, account)); err == nil {
|
93
|
96
|
nickToAccount[account] = account
|
|
97
|
+ accountName, err := tx.Get(fmt.Sprintf(keyAccountName, account))
|
|
98
|
+ if err != nil {
|
|
99
|
+ am.server.logger.Error("internal", "missing account name for", account)
|
|
100
|
+ } else {
|
|
101
|
+ skeleton, _ := Skeleton(accountName)
|
|
102
|
+ skeletonToAccount[skeleton] = account
|
|
103
|
+ }
|
94
|
104
|
}
|
95
|
105
|
if rawNicks, err := tx.Get(fmt.Sprintf(keyAccountAdditionalNicks, account)); err == nil {
|
96
|
106
|
additionalNicks := unmarshalReservedNicks(rawNicks)
|
97
|
107
|
for _, nick := range additionalNicks {
|
98
|
|
- nickToAccount[nick] = account
|
|
108
|
+ cfnick, _ := CasefoldName(nick)
|
|
109
|
+ nickToAccount[cfnick] = account
|
|
110
|
+ skeleton, _ := Skeleton(nick)
|
|
111
|
+ skeletonToAccount[skeleton] = account
|
99
|
112
|
}
|
100
|
113
|
}
|
101
|
114
|
|
|
@@ -115,6 +128,7 @@ func (am *AccountManager) buildNickToAccountIndex() {
|
115
|
128
|
} else {
|
116
|
129
|
am.Lock()
|
117
|
130
|
am.nickToAccount = nickToAccount
|
|
131
|
+ am.skeletonToAccount = skeletonToAccount
|
118
|
132
|
am.accountToMethod = accountToMethod
|
119
|
133
|
am.Unlock()
|
120
|
134
|
}
|
|
@@ -171,36 +185,55 @@ func (am *AccountManager) NickToAccount(nick string) string {
|
171
|
185
|
|
172
|
186
|
// Given a nick, looks up the account that owns it and the method (none/timeout/strict)
|
173
|
187
|
// used to enforce ownership.
|
174
|
|
-func (am *AccountManager) EnforcementStatus(nick string) (account string, method NickReservationMethod) {
|
175
|
|
- cfnick, err := CasefoldName(nick)
|
176
|
|
- if err != nil {
|
177
|
|
- return
|
178
|
|
- }
|
179
|
|
-
|
|
188
|
+func (am *AccountManager) EnforcementStatus(cfnick, skeleton string) (account string, method NickReservationMethod) {
|
180
|
189
|
config := am.server.Config()
|
181
|
190
|
if !config.Accounts.NickReservation.Enabled {
|
182
|
|
- method = NickReservationNone
|
183
|
|
- return
|
|
191
|
+ return "", NickReservationNone
|
184
|
192
|
}
|
185
|
193
|
|
186
|
194
|
am.RLock()
|
187
|
195
|
defer am.RUnlock()
|
188
|
196
|
|
189
|
|
- account = am.nickToAccount[cfnick]
|
190
|
|
- if account == "" {
|
191
|
|
- method = NickReservationNone
|
|
197
|
+ // given an account, combine stored enforcement method with the config settings
|
|
198
|
+ // to compute the actual enforcement method
|
|
199
|
+ finalEnforcementMethod := func(account_ string) (result NickReservationMethod) {
|
|
200
|
+ result = am.accountToMethod[account_]
|
|
201
|
+ // if they don't have a custom setting, or customization is disabled, use the default
|
|
202
|
+ if result == NickReservationOptional || !config.Accounts.NickReservation.AllowCustomEnforcement {
|
|
203
|
+ result = config.Accounts.NickReservation.Method
|
|
204
|
+ }
|
|
205
|
+ if result == NickReservationOptional {
|
|
206
|
+ // enforcement was explicitly enabled neither in the config or by the user
|
|
207
|
+ result = NickReservationNone
|
|
208
|
+ }
|
192
|
209
|
return
|
193
|
210
|
}
|
194
|
|
- method = am.accountToMethod[account]
|
195
|
|
- // if they don't have a custom setting, or customization is disabled, use the default
|
196
|
|
- if method == NickReservationOptional || !config.Accounts.NickReservation.AllowCustomEnforcement {
|
197
|
|
- method = config.Accounts.NickReservation.Method
|
198
|
|
- }
|
199
|
|
- if method == NickReservationOptional {
|
200
|
|
- // enforcement was explicitly enabled neither in the config or by the user
|
201
|
|
- method = NickReservationNone
|
|
211
|
+
|
|
212
|
+ nickAccount := am.nickToAccount[cfnick]
|
|
213
|
+ skelAccount := am.skeletonToAccount[skeleton]
|
|
214
|
+ if nickAccount == "" && skelAccount == "" {
|
|
215
|
+ return "", NickReservationNone
|
|
216
|
+ } else if nickAccount != "" && (skelAccount == nickAccount || skelAccount == "") {
|
|
217
|
+ return nickAccount, finalEnforcementMethod(nickAccount)
|
|
218
|
+ } else if skelAccount != "" && nickAccount == "" {
|
|
219
|
+ return skelAccount, finalEnforcementMethod(skelAccount)
|
|
220
|
+ } else {
|
|
221
|
+ // nickAccount != skelAccount and both are nonempty:
|
|
222
|
+ // two people have competing claims on (this casefolding of) this nick!
|
|
223
|
+ nickMethod := finalEnforcementMethod(nickAccount)
|
|
224
|
+ skelMethod := finalEnforcementMethod(skelAccount)
|
|
225
|
+ switch {
|
|
226
|
+ case nickMethod == NickReservationNone && skelMethod == NickReservationNone:
|
|
227
|
+ return nickAccount, NickReservationNone
|
|
228
|
+ case skelMethod == NickReservationNone:
|
|
229
|
+ return nickAccount, nickMethod
|
|
230
|
+ case nickMethod == NickReservationNone:
|
|
231
|
+ return skelAccount, skelMethod
|
|
232
|
+ default:
|
|
233
|
+ // nobody can use this nick
|
|
234
|
+ return "!", NickReservationStrict
|
|
235
|
+ }
|
202
|
236
|
}
|
203
|
|
- return
|
204
|
237
|
}
|
205
|
238
|
|
206
|
239
|
// Looks up the enforcement method stored in the database for an account
|
|
@@ -264,10 +297,15 @@ func (am *AccountManager) AccountToClients(account string) (result []*Client) {
|
264
|
297
|
|
265
|
298
|
func (am *AccountManager) Register(client *Client, account string, callbackNamespace string, callbackValue string, passphrase string, certfp string) error {
|
266
|
299
|
casefoldedAccount, err := CasefoldName(account)
|
267
|
|
- if err != nil || account == "" || account == "*" {
|
|
300
|
+ skeleton, skerr := Skeleton(account)
|
|
301
|
+ if err != nil || skerr != nil || account == "" || account == "*" {
|
268
|
302
|
return errAccountCreation
|
269
|
303
|
}
|
270
|
304
|
|
|
305
|
+ if restrictedNicknames[casefoldedAccount] || restrictedNicknames[skeleton] {
|
|
306
|
+ return errAccountAlreadyRegistered
|
|
307
|
+ }
|
|
308
|
+
|
271
|
309
|
// can't register a guest nickname
|
272
|
310
|
config := am.server.AccountConfig()
|
273
|
311
|
renamePrefix := strings.ToLower(config.NickReservation.RenamePrefix)
|
|
@@ -535,8 +573,10 @@ func (am *AccountManager) Verify(client *Client, account string, code string) er
|
535
|
573
|
})
|
536
|
574
|
|
537
|
575
|
if err == nil {
|
|
576
|
+ skeleton, _ := Skeleton(raw.Name)
|
538
|
577
|
am.Lock()
|
539
|
578
|
am.nickToAccount[casefoldedAccount] = casefoldedAccount
|
|
579
|
+ am.skeletonToAccount[skeleton] = casefoldedAccount
|
540
|
580
|
am.Unlock()
|
541
|
581
|
}
|
542
|
582
|
}()
|
|
@@ -567,9 +607,10 @@ func unmarshalReservedNicks(nicks string) (result []string) {
|
567
|
607
|
|
568
|
608
|
func (am *AccountManager) SetNickReserved(client *Client, nick string, saUnreserve bool, reserve bool) error {
|
569
|
609
|
cfnick, err := CasefoldName(nick)
|
|
610
|
+ skeleton, skerr := Skeleton(nick)
|
570
|
611
|
// garbage nick, or garbage options, or disabled
|
571
|
612
|
nrconfig := am.server.AccountConfig().NickReservation
|
572
|
|
- if err != nil || cfnick == "" || (reserve && saUnreserve) || !nrconfig.Enabled {
|
|
613
|
+ if err != nil || skerr != nil || cfnick == "" || (reserve && saUnreserve) || !nrconfig.Enabled {
|
573
|
614
|
return errAccountNickReservationFailed
|
574
|
615
|
}
|
575
|
616
|
|
|
@@ -591,8 +632,15 @@ func (am *AccountManager) SetNickReserved(client *Client, nick string, saUnreser
|
591
|
632
|
return errAccountNotLoggedIn
|
592
|
633
|
}
|
593
|
634
|
|
594
|
|
- accountForNick := am.NickToAccount(cfnick)
|
595
|
|
- if reserve && accountForNick != "" {
|
|
635
|
+ am.Lock()
|
|
636
|
+ accountForNick := am.nickToAccount[cfnick]
|
|
637
|
+ var accountForSkeleton string
|
|
638
|
+ if reserve {
|
|
639
|
+ accountForSkeleton = am.skeletonToAccount[skeleton]
|
|
640
|
+ }
|
|
641
|
+ am.Unlock()
|
|
642
|
+
|
|
643
|
+ if reserve && (accountForNick != "" || accountForSkeleton != "") {
|
596
|
644
|
return errNicknameReserved
|
597
|
645
|
} else if !reserve && !saUnreserve && accountForNick != account {
|
598
|
646
|
return errNicknameReserved
|
|
@@ -623,12 +671,18 @@ func (am *AccountManager) SetNickReserved(client *Client, nick string, saUnreser
|
623
|
671
|
if len(nicks) >= nrconfig.AdditionalNickLimit {
|
624
|
672
|
return errAccountTooManyNicks
|
625
|
673
|
}
|
626
|
|
- nicks = append(nicks, cfnick)
|
|
674
|
+ nicks = append(nicks, nick)
|
627
|
675
|
} else {
|
|
676
|
+ // compute (original reserved nicks) minus cfnick
|
628
|
677
|
var newNicks []string
|
629
|
678
|
for _, reservedNick := range nicks {
|
630
|
|
- if reservedNick != cfnick {
|
|
679
|
+ cfreservednick, _ := CasefoldName(reservedNick)
|
|
680
|
+ if cfreservednick != cfnick {
|
631
|
681
|
newNicks = append(newNicks, reservedNick)
|
|
682
|
+ } else {
|
|
683
|
+ // found the original, unfolded version of the nick we're dropping;
|
|
684
|
+ // recompute the true skeleton from it
|
|
685
|
+ skeleton, _ = Skeleton(reservedNick)
|
632
|
686
|
}
|
633
|
687
|
}
|
634
|
688
|
nicks = newNicks
|
|
@@ -650,8 +704,10 @@ func (am *AccountManager) SetNickReserved(client *Client, nick string, saUnreser
|
650
|
704
|
defer am.Unlock()
|
651
|
705
|
if reserve {
|
652
|
706
|
am.nickToAccount[cfnick] = account
|
|
707
|
+ am.skeletonToAccount[skeleton] = account
|
653
|
708
|
} else {
|
654
|
709
|
delete(am.nickToAccount, cfnick)
|
|
710
|
+ delete(am.skeletonToAccount, skeleton)
|
655
|
711
|
}
|
656
|
712
|
return nil
|
657
|
713
|
}
|
|
@@ -787,8 +843,10 @@ func (am *AccountManager) Unregister(account string) error {
|
787
|
843
|
am.serialCacheUpdateMutex.Lock()
|
788
|
844
|
defer am.serialCacheUpdateMutex.Unlock()
|
789
|
845
|
|
|
846
|
+ var accountName string
|
790
|
847
|
am.server.store.Update(func(tx *buntdb.Tx) error {
|
791
|
848
|
tx.Delete(accountKey)
|
|
849
|
+ accountName, _ = tx.Get(accountNameKey)
|
792
|
850
|
tx.Delete(accountNameKey)
|
793
|
851
|
tx.Delete(verifiedKey)
|
794
|
852
|
tx.Delete(registeredTimeKey)
|
|
@@ -817,6 +875,7 @@ func (am *AccountManager) Unregister(account string) error {
|
817
|
875
|
}
|
818
|
876
|
}
|
819
|
877
|
|
|
878
|
+ skeleton, _ := Skeleton(accountName)
|
820
|
879
|
additionalNicks := unmarshalReservedNicks(rawNicks)
|
821
|
880
|
|
822
|
881
|
am.Lock()
|
|
@@ -825,8 +884,11 @@ func (am *AccountManager) Unregister(account string) error {
|
825
|
884
|
clients = am.accountToClients[casefoldedAccount]
|
826
|
885
|
delete(am.accountToClients, casefoldedAccount)
|
827
|
886
|
delete(am.nickToAccount, casefoldedAccount)
|
|
887
|
+ delete(am.skeletonToAccount, skeleton)
|
828
|
888
|
for _, nick := range additionalNicks {
|
829
|
889
|
delete(am.nickToAccount, nick)
|
|
890
|
+ additionalSkel, _ := Skeleton(nick)
|
|
891
|
+ delete(am.skeletonToAccount, additionalSkel)
|
830
|
892
|
}
|
831
|
893
|
for _, client := range clients {
|
832
|
894
|
am.logoutOfAccount(client)
|