Browse Source

Split NS/CS commands into separate functions

tags/v0.11.0-beta
Daniel Oaks 6 years ago
parent
commit
3ef4c5f799
2 changed files with 248 additions and 235 deletions
  1. 45
    41
      irc/chanserv.go
  2. 203
    194
      irc/nickserv.go

+ 45
- 41
irc/chanserv.go View File

@@ -45,55 +45,59 @@ func (server *Server) chanservPrivmsgHandler(client *Client, message string) {
45 45
 			return
46 46
 		}
47 47
 
48
-		if !server.channelRegistrationEnabled {
49
-			client.ChanServNotice(client.t("Channel registration is not enabled"))
50
-			return
51
-		}
48
+		server.chanservRegisterHandler(client, params[1])
49
+	} else {
50
+		client.ChanServNotice(client.t("Sorry, I don't know that command"))
51
+	}
52
+}
52 53
 
53
-		channelName := params[1]
54
-		channelKey, err := CasefoldChannel(channelName)
55
-		if err != nil {
56
-			client.ChanServNotice(client.t("Channel name is not valid"))
57
-			return
58
-		}
54
+// chanservRegisterHandler handles the ChanServ REGISTER subcommand.
55
+func (server *Server) chanservRegisterHandler(client *Client, channelName string) {
56
+	if !server.channelRegistrationEnabled {
57
+		client.ChanServNotice(client.t("Channel registration is not enabled"))
58
+		return
59
+	}
59 60
 
60
-		channelInfo := server.channels.Get(channelKey)
61
-		if channelInfo == nil || !channelInfo.ClientIsAtLeast(client, modes.ChannelOperator) {
62
-			client.ChanServNotice(client.t("You must be an oper on the channel to register it"))
63
-			return
64
-		}
61
+	channelKey, err := CasefoldChannel(channelName)
62
+	if err != nil {
63
+		client.ChanServNotice(client.t("Channel name is not valid"))
64
+		return
65
+	}
65 66
 
66
-		if client.account == &NoAccount {
67
-			client.ChanServNotice(client.t("You must be logged in to register a channel"))
68
-			return
69
-		}
67
+	channelInfo := server.channels.Get(channelKey)
68
+	if channelInfo == nil || !channelInfo.ClientIsAtLeast(client, modes.ChannelOperator) {
69
+		client.ChanServNotice(client.t("You must be an oper on the channel to register it"))
70
+		return
71
+	}
70 72
 
71
-		// this provides the synchronization that allows exactly one registration of the channel:
72
-		err = channelInfo.SetRegistered(client.AccountName())
73
-		if err != nil {
74
-			client.ChanServNotice(err.Error())
75
-			return
76
-		}
73
+	if client.account == &NoAccount {
74
+		client.ChanServNotice(client.t("You must be logged in to register a channel"))
75
+		return
76
+	}
77
+
78
+	// this provides the synchronization that allows exactly one registration of the channel:
79
+	err = channelInfo.SetRegistered(client.AccountName())
80
+	if err != nil {
81
+		client.ChanServNotice(err.Error())
82
+		return
83
+	}
77 84
 
78
-		// registration was successful: make the database reflect it
79
-		go server.channelRegistry.StoreChannel(channelInfo, true)
85
+	// registration was successful: make the database reflect it
86
+	go server.channelRegistry.StoreChannel(channelInfo, true)
80 87
 
81
-		client.ChanServNotice(fmt.Sprintf(client.t("Channel %s successfully registered"), channelName))
88
+	client.ChanServNotice(fmt.Sprintf(client.t("Channel %s successfully registered"), channelName))
82 89
 
83
-		server.logger.Info("chanserv", fmt.Sprintf("Client %s registered channel %s", client.nick, channelName))
84
-		server.snomasks.Send(sno.LocalChannels, fmt.Sprintf(ircfmt.Unescape("Channel registered $c[grey][$r%s$c[grey]] by $c[grey][$r%s$c[grey]]"), channelName, client.nickMaskString))
90
+	server.logger.Info("chanserv", fmt.Sprintf("Client %s registered channel %s", client.nick, channelName))
91
+	server.snomasks.Send(sno.LocalChannels, fmt.Sprintf(ircfmt.Unescape("Channel registered $c[grey][$r%s$c[grey]] by $c[grey][$r%s$c[grey]]"), channelName, client.nickMaskString))
85 92
 
86
-		// give them founder privs
87
-		change := channelInfo.applyModeMemberNoMutex(client, modes.ChannelFounder, modes.Add, client.NickCasefolded())
88
-		if change != nil {
89
-			//TODO(dan): we should change the name of String and make it return a slice here
90
-			//TODO(dan): unify this code with code in modes.go
91
-			args := append([]string{channelName}, strings.Split(change.String(), " ")...)
92
-			for _, member := range channelInfo.Members() {
93
-				member.Send(nil, fmt.Sprintf("ChanServ!services@%s", client.server.name), "MODE", args...)
94
-			}
93
+	// give them founder privs
94
+	change := channelInfo.applyModeMemberNoMutex(client, modes.ChannelFounder, modes.Add, client.NickCasefolded())
95
+	if change != nil {
96
+		//TODO(dan): we should change the name of String and make it return a slice here
97
+		//TODO(dan): unify this code with code in modes.go
98
+		args := append([]string{channelName}, strings.Split(change.String(), " ")...)
99
+		for _, member := range channelInfo.Members() {
100
+			member.Send(nil, fmt.Sprintf("ChanServ!services@%s", client.server.name), "MODE", args...)
95 101
 		}
96
-	} else {
97
-		client.ChanServNotice(client.t("Sorry, I don't know that command"))
98 102
 	}
99 103
 }

+ 203
- 194
irc/nickserv.go View File

@@ -62,245 +62,254 @@ func (server *Server) nickservPrivmsgHandler(client *Client, message string) {
62 62
 			return
63 63
 		}
64 64
 
65
-		certfp := client.certfp
66
-		if passphrase == "" && certfp == "" {
67
-			client.Notice(client.t("You need to either supply a passphrase or be connected via TLS with a client cert"))
68
-			return
69
-		}
65
+		server.nickservRegisterHandler(client, username, passphrase)
66
+	} else if command == "identify" {
67
+		// get params
68
+		username, passphrase := extractParam(params)
70 69
 
71
-		if !server.accountRegistration.Enabled {
72
-			client.Notice(client.t("Account registration has been disabled"))
70
+		server.nickservIdentifyHandler(client, username, passphrase)
71
+	} else {
72
+		client.Notice(client.t("Command not recognised. To see the available commands, run /NS HELP"))
73
+	}
74
+}
75
+
76
+func (server *Server) nickservRegisterHandler(client *Client, username, passphrase string) {
77
+	certfp := client.certfp
78
+	if passphrase == "" && certfp == "" {
79
+		client.Notice(client.t("You need to either supply a passphrase or be connected via TLS with a client cert"))
80
+		return
81
+	}
82
+
83
+	if !server.accountRegistration.Enabled {
84
+		client.Notice(client.t("Account registration has been disabled"))
85
+		return
86
+	}
87
+
88
+	if client.LoggedIntoAccount() {
89
+		if server.accountRegistration.AllowMultiplePerConnection {
90
+			client.LogoutOfAccount()
91
+		} else {
92
+			client.Notice(client.t("You're already logged into an account"))
73 93
 			return
74 94
 		}
95
+	}
75 96
 
76
-		if client.LoggedIntoAccount() {
77
-			if server.accountRegistration.AllowMultiplePerConnection {
78
-				client.LogoutOfAccount()
79
-			} else {
80
-				client.Notice(client.t("You're already logged into an account"))
81
-				return
82
-			}
97
+	// get and sanitise account name
98
+	account := strings.TrimSpace(username)
99
+	casefoldedAccount, err := CasefoldName(account)
100
+	// probably don't need explicit check for "*" here... but let's do it anyway just to make sure
101
+	if err != nil || username == "*" {
102
+		client.Notice(client.t("Account name is not valid"))
103
+		return
104
+	}
105
+
106
+	// check whether account exists
107
+	// do it all in one write tx to prevent races
108
+	err = server.store.Update(func(tx *buntdb.Tx) error {
109
+		accountKey := fmt.Sprintf(keyAccountExists, casefoldedAccount)
110
+
111
+		_, err := tx.Get(accountKey)
112
+		if err != buntdb.ErrNotFound {
113
+			//TODO(dan): if account verified key doesn't exist account is not verified, calc the maximum time without verification and expire and continue if need be
114
+			client.Notice(client.t("Account already exists"))
115
+			return errAccountCreation
83 116
 		}
84 117
 
85
-		// get and sanitise account name
86
-		account := strings.TrimSpace(username)
87
-		casefoldedAccount, err := CasefoldName(account)
88
-		// probably don't need explicit check for "*" here... but let's do it anyway just to make sure
89
-		if err != nil || username == "*" {
90
-			client.Notice(client.t("Account name is not valid"))
91
-			return
118
+		registeredTimeKey := fmt.Sprintf(keyAccountRegTime, casefoldedAccount)
119
+
120
+		tx.Set(accountKey, "1", nil)
121
+		tx.Set(fmt.Sprintf(keyAccountName, casefoldedAccount), account, nil)
122
+		tx.Set(registeredTimeKey, strconv.FormatInt(time.Now().Unix(), 10), nil)
123
+		return nil
124
+	})
125
+
126
+	// account could not be created and relevant numerics have been dispatched, abort
127
+	if err != nil {
128
+		if err != errAccountCreation {
129
+			client.Notice(client.t("Account registration failed"))
92 130
 		}
131
+		return
132
+	}
93 133
 
94
-		// check whether account exists
95
-		// do it all in one write tx to prevent races
96
-		err = server.store.Update(func(tx *buntdb.Tx) error {
97
-			accountKey := fmt.Sprintf(keyAccountExists, casefoldedAccount)
134
+	// store details
135
+	err = server.store.Update(func(tx *buntdb.Tx) error {
136
+		// certfp special lookup key
137
+		if passphrase == "" {
138
+			assembledKeyCertToAccount := fmt.Sprintf(keyCertToAccount, client.certfp)
98 139
 
99
-			_, err := tx.Get(accountKey)
140
+			// make sure certfp doesn't already exist because that'd be silly
141
+			_, err := tx.Get(assembledKeyCertToAccount)
100 142
 			if err != buntdb.ErrNotFound {
101
-				//TODO(dan): if account verified key doesn't exist account is not verified, calc the maximum time without verification and expire and continue if need be
102
-				client.Notice(client.t("Account already exists"))
103
-				return errAccountCreation
143
+				return errCertfpAlreadyExists
104 144
 			}
105 145
 
106
-			registeredTimeKey := fmt.Sprintf(keyAccountRegTime, casefoldedAccount)
146
+			tx.Set(assembledKeyCertToAccount, casefoldedAccount, nil)
147
+		}
107 148
 
108
-			tx.Set(accountKey, "1", nil)
109
-			tx.Set(fmt.Sprintf(keyAccountName, casefoldedAccount), account, nil)
110
-			tx.Set(registeredTimeKey, strconv.FormatInt(time.Now().Unix(), 10), nil)
111
-			return nil
112
-		})
149
+		// make creds
150
+		var creds AccountCredentials
113 151
 
114
-		// account could not be created and relevant numerics have been dispatched, abort
152
+		// always set passphrase salt
153
+		creds.PassphraseSalt, err = passwd.NewSalt()
115 154
 		if err != nil {
116
-			if err != errAccountCreation {
117
-				client.Notice(client.t("Account registration failed"))
155
+			return fmt.Errorf("Could not create passphrase salt: %s", err.Error())
156
+		}
157
+
158
+		if passphrase == "" {
159
+			creds.Certificate = client.certfp
160
+		} else {
161
+			creds.PassphraseHash, err = server.passwords.GenerateFromPassword(creds.PassphraseSalt, passphrase)
162
+			if err != nil {
163
+				return fmt.Errorf("Could not hash password: %s", err)
118 164
 			}
119
-			return
120 165
 		}
166
+		credText, err := json.Marshal(creds)
167
+		if err != nil {
168
+			return fmt.Errorf("Could not marshal creds: %s", err)
169
+		}
170
+		tx.Set(fmt.Sprintf(keyAccountCredentials, account), string(credText), nil)
121 171
 
122
-		// store details
123
-		err = server.store.Update(func(tx *buntdb.Tx) error {
124
-			// certfp special lookup key
125
-			if passphrase == "" {
126
-				assembledKeyCertToAccount := fmt.Sprintf(keyCertToAccount, client.certfp)
172
+		return nil
173
+	})
127 174
 
128
-				// make sure certfp doesn't already exist because that'd be silly
129
-				_, err := tx.Get(assembledKeyCertToAccount)
130
-				if err != buntdb.ErrNotFound {
131
-					return errCertfpAlreadyExists
132
-				}
175
+	// details could not be stored and relevant numerics have been dispatched, abort
176
+	if err != nil {
177
+		errMsg := "Could not register"
178
+		if err == errCertfpAlreadyExists {
179
+			errMsg = "An account already exists for your certificate fingerprint"
180
+		}
181
+		client.Notice(errMsg)
182
+		removeFailedAccRegisterData(server.store, casefoldedAccount)
183
+		return
184
+	}
133 185
 
134
-				tx.Set(assembledKeyCertToAccount, casefoldedAccount, nil)
135
-			}
186
+	err = server.store.Update(func(tx *buntdb.Tx) error {
187
+		tx.Set(fmt.Sprintf(keyAccountVerified, casefoldedAccount), "1", nil)
136 188
 
137
-			// make creds
138
-			var creds AccountCredentials
189
+		// load acct info inside store tx
190
+		account := ClientAccount{
191
+			Name:         username,
192
+			RegisteredAt: time.Now(),
193
+			Clients:      []*Client{client},
194
+		}
195
+		//TODO(dan): Consider creating ircd-wide account adding/removing/affecting lock for protecting access to these sorts of variables
196
+		server.accounts[casefoldedAccount] = &account
197
+		client.account = &account
198
+
199
+		client.Notice(client.t("Account created"))
200
+		client.Send(nil, server.name, RPL_LOGGEDIN, client.nick, client.nickMaskString, account.Name, fmt.Sprintf(client.t("You are now logged in as %s"), account.Name))
201
+		client.Send(nil, server.name, RPL_SASLSUCCESS, client.nick, client.t("Authentication successful"))
202
+		server.snomasks.Send(sno.LocalAccounts, fmt.Sprintf(ircfmt.Unescape("Account registered $c[grey][$r%s$c[grey]] by $c[grey][$r%s$c[grey]]"), account.Name, client.nickMaskString))
203
+		return nil
204
+	})
205
+	if err != nil {
206
+		client.Notice(client.t("Account registration failed"))
207
+		removeFailedAccRegisterData(server.store, casefoldedAccount)
208
+		return
209
+	}
210
+}
139 211
 
140
-			// always set passphrase salt
141
-			creds.PassphraseSalt, err = passwd.NewSalt()
212
+func (server *Server) nickservIdentifyHandler(client *Client, username, passphrase string) {
213
+	// fail out if we need to
214
+	if !server.accountAuthenticationEnabled {
215
+		client.Notice(client.t("Login has been disabled"))
216
+		return
217
+	}
218
+
219
+	// try passphrase
220
+	if username != "" && passphrase != "" {
221
+		// keep it the same as in the ACC CREATE stage
222
+		accountKey, err := CasefoldName(username)
223
+		if err != nil {
224
+			client.Notice(client.t("Could not login with your username/password"))
225
+			return
226
+		}
227
+
228
+		// load and check acct data all in one update to prevent races.
229
+		// as noted elsewhere, change to proper locking for Account type later probably
230
+		var accountName string
231
+		err = server.store.Update(func(tx *buntdb.Tx) error {
232
+			// confirm account is verified
233
+			_, err = tx.Get(fmt.Sprintf(keyAccountVerified, accountKey))
142 234
 			if err != nil {
143
-				return fmt.Errorf("Could not create passphrase salt: %s", err.Error())
235
+				return errSaslFail
144 236
 			}
145 237
 
146
-			if passphrase == "" {
147
-				creds.Certificate = client.certfp
148
-			} else {
149
-				creds.PassphraseHash, err = server.passwords.GenerateFromPassword(creds.PassphraseSalt, passphrase)
150
-				if err != nil {
151
-					return fmt.Errorf("Could not hash password: %s", err)
152
-				}
153
-			}
154
-			credText, err := json.Marshal(creds)
238
+			creds, err := loadAccountCredentials(tx, accountKey)
155 239
 			if err != nil {
156
-				return fmt.Errorf("Could not marshal creds: %s", err)
240
+				return err
157 241
 			}
158
-			tx.Set(fmt.Sprintf(keyAccountCredentials, account), string(credText), nil)
159 242
 
160
-			return nil
161
-		})
243
+			// ensure creds are valid
244
+			if len(creds.PassphraseHash) < 1 || len(creds.PassphraseSalt) < 1 || len(passphrase) < 1 {
245
+				return errSaslFail
246
+			}
247
+			err = server.passwords.CompareHashAndPassword(creds.PassphraseHash, creds.PassphraseSalt, passphrase)
162 248
 
163
-		// details could not be stored and relevant numerics have been dispatched, abort
164
-		if err != nil {
165
-			errMsg := "Could not register"
166
-			if err == errCertfpAlreadyExists {
167
-				errMsg = "An account already exists for your certificate fingerprint"
249
+			// succeeded, load account info if necessary
250
+			account, exists := server.accounts[accountKey]
251
+			if !exists {
252
+				account = loadAccount(server, tx, accountKey)
168 253
 			}
169
-			client.Notice(errMsg)
170
-			removeFailedAccRegisterData(server.store, casefoldedAccount)
171
-			return
172
-		}
173 254
 
174
-		err = server.store.Update(func(tx *buntdb.Tx) error {
175
-			tx.Set(fmt.Sprintf(keyAccountVerified, casefoldedAccount), "1", nil)
255
+			client.LoginToAccount(account)
256
+			accountName = account.Name
176 257
 
177
-			// load acct info inside store tx
178
-			account := ClientAccount{
179
-				Name:         username,
180
-				RegisteredAt: time.Now(),
181
-				Clients:      []*Client{client},
182
-			}
183
-			//TODO(dan): Consider creating ircd-wide account adding/removing/affecting lock for protecting access to these sorts of variables
184
-			server.accounts[casefoldedAccount] = &account
185
-			client.account = &account
186
-
187
-			client.Notice(client.t("Account created"))
188
-			client.Send(nil, server.name, RPL_LOGGEDIN, client.nick, client.nickMaskString, account.Name, fmt.Sprintf(client.t("You are now logged in as %s"), account.Name))
189
-			client.Send(nil, server.name, RPL_SASLSUCCESS, client.nick, client.t("Authentication successful"))
190
-			server.snomasks.Send(sno.LocalAccounts, fmt.Sprintf(ircfmt.Unescape("Account registered $c[grey][$r%s$c[grey]] by $c[grey][$r%s$c[grey]]"), account.Name, client.nickMaskString))
191
-			return nil
258
+			return err
192 259
 		})
193
-		if err != nil {
194
-			client.Notice(client.t("Account registration failed"))
195
-			removeFailedAccRegisterData(server.store, casefoldedAccount)
196
-			return
197
-		}
198 260
 
199
-	} else if command == "identify" {
200
-		// fail out if we need to
201
-		if !server.accountAuthenticationEnabled {
202
-			client.Notice(client.t("Login has been disabled"))
261
+		if err == nil {
262
+			client.Notice(fmt.Sprintf(client.t("You're now logged in as %s"), accountName))
203 263
 			return
204 264
 		}
265
+	}
205 266
 
206
-		// try passphrase
207
-		username, passphrase := extractParam(params)
208
-		if username != "" && passphrase != "" {
209
-			// keep it the same as in the ACC CREATE stage
210
-			accountKey, err := CasefoldName(username)
267
+	// try certfp
268
+	certfp := client.certfp
269
+	if certfp != "" {
270
+		var accountName string
271
+		err := server.store.Update(func(tx *buntdb.Tx) error {
272
+			// certfp lookup key
273
+			accountKey, err := tx.Get(fmt.Sprintf(keyCertToAccount, certfp))
211 274
 			if err != nil {
212
-				client.Notice(client.t("Could not login with your username/password"))
213
-				return
275
+				return errSaslFail
214 276
 			}
215 277
 
216
-			// load and check acct data all in one update to prevent races.
217
-			// as noted elsewhere, change to proper locking for Account type later probably
218
-			var accountName string
219
-			err = server.store.Update(func(tx *buntdb.Tx) error {
220
-				// confirm account is verified
221
-				_, err = tx.Get(fmt.Sprintf(keyAccountVerified, accountKey))
222
-				if err != nil {
223
-					return errSaslFail
224
-				}
225
-
226
-				creds, err := loadAccountCredentials(tx, accountKey)
227
-				if err != nil {
228
-					return err
229
-				}
230
-
231
-				// ensure creds are valid
232
-				if len(creds.PassphraseHash) < 1 || len(creds.PassphraseSalt) < 1 || len(passphrase) < 1 {
233
-					return errSaslFail
234
-				}
235
-				err = server.passwords.CompareHashAndPassword(creds.PassphraseHash, creds.PassphraseSalt, passphrase)
236
-
237
-				// succeeded, load account info if necessary
238
-				account, exists := server.accounts[accountKey]
239
-				if !exists {
240
-					account = loadAccount(server, tx, accountKey)
241
-				}
242
-
243
-				client.LoginToAccount(account)
244
-				accountName = account.Name
278
+			// confirm account exists
279
+			_, err = tx.Get(fmt.Sprintf(keyAccountExists, accountKey))
280
+			if err != nil {
281
+				return errSaslFail
282
+			}
245 283
 
246
-				return err
247
-			})
284
+			// confirm account is verified
285
+			_, err = tx.Get(fmt.Sprintf(keyAccountVerified, accountKey))
286
+			if err != nil {
287
+				return errSaslFail
288
+			}
248 289
 
249
-			if err == nil {
250
-				client.Notice(fmt.Sprintf(client.t("You're now logged in as %s"), accountName))
251
-				return
290
+			// confirm the certfp in that account's credentials
291
+			creds, err := loadAccountCredentials(tx, accountKey)
292
+			if err != nil || creds.Certificate != client.certfp {
293
+				return errSaslFail
252 294
 			}
253
-		}
254 295
 
255
-		// try certfp
256
-		certfp := client.certfp
257
-		if certfp != "" {
258
-			var accountName string
259
-			err := server.store.Update(func(tx *buntdb.Tx) error {
260
-				// certfp lookup key
261
-				accountKey, err := tx.Get(fmt.Sprintf(keyCertToAccount, certfp))
262
-				if err != nil {
263
-					return errSaslFail
264
-				}
265
-
266
-				// confirm account exists
267
-				_, err = tx.Get(fmt.Sprintf(keyAccountExists, accountKey))
268
-				if err != nil {
269
-					return errSaslFail
270
-				}
271
-
272
-				// confirm account is verified
273
-				_, err = tx.Get(fmt.Sprintf(keyAccountVerified, accountKey))
274
-				if err != nil {
275
-					return errSaslFail
276
-				}
277
-
278
-				// confirm the certfp in that account's credentials
279
-				creds, err := loadAccountCredentials(tx, accountKey)
280
-				if err != nil || creds.Certificate != client.certfp {
281
-					return errSaslFail
282
-				}
283
-
284
-				// succeeded, load account info if necessary
285
-				account, exists := server.accounts[accountKey]
286
-				if !exists {
287
-					account = loadAccount(server, tx, accountKey)
288
-				}
289
-
290
-				client.LoginToAccount(account)
291
-				accountName = account.Name
292
-
293
-				return nil
294
-			})
295
-
296
-			if err == nil {
297
-				client.Notice(fmt.Sprintf(client.t("You're now logged in as %s"), accountName))
298
-				return
296
+			// succeeded, load account info if necessary
297
+			account, exists := server.accounts[accountKey]
298
+			if !exists {
299
+				account = loadAccount(server, tx, accountKey)
299 300
 			}
300
-		}
301 301
 
302
-		client.Notice(client.t("Could not login with your TLS certificate or supplied username/password"))
303
-	} else {
304
-		client.Notice(client.t("Command not recognised. To see the available commands, run /NS HELP"))
302
+			client.LoginToAccount(account)
303
+			accountName = account.Name
304
+
305
+			return nil
306
+		})
307
+
308
+		if err == nil {
309
+			client.Notice(fmt.Sprintf(client.t("You're now logged in as %s"), accountName))
310
+			return
311
+		}
305 312
 	}
313
+
314
+	client.Notice(client.t("Could not login with your TLS certificate or supplied username/password"))
306 315
 }

Loading…
Cancel
Save