Bläddra i källkod

refactor services prefixes and notice handlers

tags/v2.5.0-rc1
Shivaram Lingamneni 3 år sedan
förälder
incheckning
9214d978d0
8 ändrade filer med 386 tillägg och 401 borttagningar
  1. 8
    8
      irc/channel.go
  2. 107
    113
      irc/chanserv.go
  3. 18
    18
      irc/handlers.go
  4. 19
    25
      irc/histserv.go
  5. 21
    27
      irc/hostserv.go
  6. 5
    2
      irc/nickname.go
  7. 184
    195
      irc/nickserv.go
  8. 24
    13
      irc/services.go

+ 8
- 8
irc/channel.go Visa fil

@@ -1037,7 +1037,7 @@ func (channel *Channel) replayHistoryForResume(session *Session, after time.Time
1037 1037
 	}
1038 1038
 	if !complete && !session.resumeDetails.HistoryIncomplete {
1039 1039
 		// warn here if we didn't warn already
1040
-		rb.Add(nil, histServMask, "NOTICE", channel.Name(), session.client.t("Some additional message history may have been lost"))
1040
+		rb.Add(nil, histservService.prefix, "NOTICE", channel.Name(), session.client.t("Some additional message history may have been lost"))
1041 1041
 	}
1042 1042
 	rb.Send(true)
1043 1043
 }
@@ -1099,7 +1099,7 @@ func (channel *Channel) replayHistoryItems(rb *ResponseBuffer, items []history.I
1099 1099
 				} else {
1100 1100
 					message = fmt.Sprintf(client.t("%[1]s [account: %[2]s] joined the channel"), nick, item.AccountName)
1101 1101
 				}
1102
-				rb.AddFromClient(item.Message.Time, utils.MungeSecretToken(item.Message.Msgid), histServMask, "*", nil, "PRIVMSG", chname, message)
1102
+				rb.AddFromClient(item.Message.Time, utils.MungeSecretToken(item.Message.Msgid), histservService.prefix, "*", nil, "PRIVMSG", chname, message)
1103 1103
 			}
1104 1104
 		case history.Part:
1105 1105
 			if eventPlayback {
@@ -1109,14 +1109,14 @@ func (channel *Channel) replayHistoryItems(rb *ResponseBuffer, items []history.I
1109 1109
 					continue // #474
1110 1110
 				}
1111 1111
 				message := fmt.Sprintf(client.t("%[1]s left the channel (%[2]s)"), nick, item.Message.Message)
1112
-				rb.AddFromClient(item.Message.Time, utils.MungeSecretToken(item.Message.Msgid), histServMask, "*", nil, "PRIVMSG", chname, message)
1112
+				rb.AddFromClient(item.Message.Time, utils.MungeSecretToken(item.Message.Msgid), histservService.prefix, "*", nil, "PRIVMSG", chname, message)
1113 1113
 			}
1114 1114
 		case history.Kick:
1115 1115
 			if eventPlayback {
1116 1116
 				rb.AddFromClient(item.Message.Time, item.Message.Msgid, item.Nick, item.AccountName, nil, "KICK", chname, item.Params[0], item.Message.Message)
1117 1117
 			} else {
1118 1118
 				message := fmt.Sprintf(client.t("%[1]s kicked %[2]s (%[3]s)"), nick, item.Params[0], item.Message.Message)
1119
-				rb.AddFromClient(item.Message.Time, utils.MungeSecretToken(item.Message.Msgid), histServMask, "*", nil, "PRIVMSG", chname, message)
1119
+				rb.AddFromClient(item.Message.Time, utils.MungeSecretToken(item.Message.Msgid), histservService.prefix, "*", nil, "PRIVMSG", chname, message)
1120 1120
 			}
1121 1121
 		case history.Quit:
1122 1122
 			if eventPlayback {
@@ -1126,21 +1126,21 @@ func (channel *Channel) replayHistoryItems(rb *ResponseBuffer, items []history.I
1126 1126
 					continue // #474
1127 1127
 				}
1128 1128
 				message := fmt.Sprintf(client.t("%[1]s quit (%[2]s)"), nick, item.Message.Message)
1129
-				rb.AddFromClient(item.Message.Time, utils.MungeSecretToken(item.Message.Msgid), histServMask, "*", nil, "PRIVMSG", chname, message)
1129
+				rb.AddFromClient(item.Message.Time, utils.MungeSecretToken(item.Message.Msgid), histservService.prefix, "*", nil, "PRIVMSG", chname, message)
1130 1130
 			}
1131 1131
 		case history.Nick:
1132 1132
 			if eventPlayback {
1133 1133
 				rb.AddFromClient(item.Message.Time, item.Message.Msgid, item.Nick, item.AccountName, nil, "NICK", item.Params[0])
1134 1134
 			} else {
1135 1135
 				message := fmt.Sprintf(client.t("%[1]s changed nick to %[2]s"), nick, item.Params[0])
1136
-				rb.AddFromClient(item.Message.Time, utils.MungeSecretToken(item.Message.Msgid), histServMask, "*", nil, "PRIVMSG", chname, message)
1136
+				rb.AddFromClient(item.Message.Time, utils.MungeSecretToken(item.Message.Msgid), histservService.prefix, "*", nil, "PRIVMSG", chname, message)
1137 1137
 			}
1138 1138
 		case history.Topic:
1139 1139
 			if eventPlayback {
1140 1140
 				rb.AddFromClient(item.Message.Time, item.Message.Msgid, item.Nick, item.AccountName, nil, "TOPIC", chname, item.Message.Message)
1141 1141
 			} else {
1142 1142
 				message := fmt.Sprintf(client.t("%[1]s set the channel topic to: %[2]s"), nick, item.Message.Message)
1143
-				rb.AddFromClient(item.Message.Time, utils.MungeSecretToken(item.Message.Msgid), histServMask, "*", nil, "PRIVMSG", chname, message)
1143
+				rb.AddFromClient(item.Message.Time, utils.MungeSecretToken(item.Message.Msgid), histservService.prefix, "*", nil, "PRIVMSG", chname, message)
1144 1144
 			}
1145 1145
 		case history.Mode:
1146 1146
 			params := make([]string, len(item.Message.Split)+1)
@@ -1152,7 +1152,7 @@ func (channel *Channel) replayHistoryItems(rb *ResponseBuffer, items []history.I
1152 1152
 				rb.AddFromClient(item.Message.Time, item.Message.Msgid, item.Nick, item.AccountName, nil, "MODE", params...)
1153 1153
 			} else {
1154 1154
 				message := fmt.Sprintf(client.t("%[1]s set channel modes: %[2]s"), nick, strings.Join(params[1:], " "))
1155
-				rb.AddFromClient(item.Message.Time, utils.MungeSecretToken(item.Message.Msgid), histServMask, "*", nil, "PRIVMSG", chname, message)
1155
+				rb.AddFromClient(item.Message.Time, utils.MungeSecretToken(item.Message.Msgid), histservService.prefix, "*", nil, "PRIVMSG", chname, message)
1156 1156
 			}
1157 1157
 		}
1158 1158
 	}

+ 107
- 113
irc/chanserv.go Visa fil

@@ -17,7 +17,6 @@ import (
17 17
 )
18 18
 
19 19
 const chanservHelp = `ChanServ lets you register and manage channels.`
20
-const chanservMask = "ChanServ!ChanServ@localhost"
21 20
 
22 21
 func chanregEnabled(config *Config) bool {
23 22
 	return config.Channels.Registration.Enabled
@@ -188,27 +187,22 @@ SET modifies a channel's settings. The following settings are available:`,
188 187
 	}
189 188
 )
190 189
 
191
-// csNotice sends the client a notice from ChanServ
192
-func csNotice(rb *ResponseBuffer, text string) {
193
-	rb.Add(nil, chanservMask, "NOTICE", rb.target.Nick(), text)
194
-}
195
-
196
-func csAmodeHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
190
+func csAmodeHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
197 191
 	channelName := params[0]
198 192
 
199 193
 	channel := server.channels.Get(channelName)
200 194
 	if channel == nil {
201
-		csNotice(rb, client.t("Channel does not exist"))
195
+		service.Notice(rb, client.t("Channel does not exist"))
202 196
 		return
203 197
 	} else if channel.Founder() == "" {
204
-		csNotice(rb, client.t("Channel is not registered"))
198
+		service.Notice(rb, client.t("Channel is not registered"))
205 199
 		return
206 200
 	}
207 201
 
208 202
 	modeChanges, unknown := modes.ParseChannelModeChanges(params[1:]...)
209 203
 	var change modes.ModeChange
210 204
 	if len(modeChanges) > 1 || len(unknown) > 0 {
211
-		csNotice(rb, client.t("Invalid mode change"))
205
+		service.Notice(rb, client.t("Invalid mode change"))
212 206
 		return
213 207
 	} else if len(modeChanges) == 1 {
214 208
 		change = modeChanges[0]
@@ -233,17 +227,17 @@ func csAmodeHandler(server *Server, client *Client, command string, params []str
233 227
 		accountIsValid = (change.Arg != "")
234 228
 	}
235 229
 	if !accountIsValid {
236
-		csNotice(rb, client.t("Account does not exist"))
230
+		service.Notice(rb, client.t("Account does not exist"))
237 231
 		return
238 232
 	}
239 233
 
240 234
 	affectedModes, err := channel.ProcessAccountToUmodeChange(client, change)
241 235
 
242 236
 	if err == errInsufficientPrivs {
243
-		csNotice(rb, client.t("Insufficient privileges"))
237
+		service.Notice(rb, client.t("Insufficient privileges"))
244 238
 		return
245 239
 	} else if err != nil {
246
-		csNotice(rb, client.t("Internal error"))
240
+		service.Notice(rb, client.t("Internal error"))
247 241
 		return
248 242
 	}
249 243
 
@@ -253,13 +247,13 @@ func csAmodeHandler(server *Server, client *Client, command string, params []str
253 247
 		sort.Slice(affectedModes, func(i, j int) bool {
254 248
 			return umodeGreaterThan(affectedModes[i].Mode, affectedModes[j].Mode)
255 249
 		})
256
-		csNotice(rb, fmt.Sprintf(client.t("Channel %[1]s has %[2]d persistent modes set"), channelName, len(affectedModes)))
250
+		service.Notice(rb, fmt.Sprintf(client.t("Channel %[1]s has %[2]d persistent modes set"), channelName, len(affectedModes)))
257 251
 		for _, modeChange := range affectedModes {
258
-			csNotice(rb, fmt.Sprintf(client.t("Account %[1]s receives mode +%[2]s"), modeChange.Arg, string(modeChange.Mode)))
252
+			service.Notice(rb, fmt.Sprintf(client.t("Account %[1]s receives mode +%[2]s"), modeChange.Arg, string(modeChange.Mode)))
259 253
 		}
260 254
 	case modes.Add, modes.Remove:
261 255
 		if len(affectedModes) > 0 {
262
-			csNotice(rb, fmt.Sprintf(client.t("Successfully set persistent mode %[1]s on %[2]s"), strings.Join([]string{string(change.Op), string(change.Mode)}, ""), change.Arg))
256
+			service.Notice(rb, fmt.Sprintf(client.t("Successfully set persistent mode %[1]s on %[2]s"), strings.Join([]string{string(change.Op), string(change.Mode)}, ""), change.Arg))
263 257
 			// #729: apply change to current membership
264 258
 			for _, member := range channel.Members() {
265 259
 				if member.Account() == change.Arg {
@@ -270,22 +264,22 @@ func csAmodeHandler(server *Server, client *Client, command string, params []str
270 264
 				}
271 265
 			}
272 266
 		} else {
273
-			csNotice(rb, client.t("No changes were made"))
267
+			service.Notice(rb, client.t("No changes were made"))
274 268
 		}
275 269
 	}
276 270
 }
277 271
 
278
-func csOpHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
272
+func csOpHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
279 273
 	channelInfo := server.channels.Get(params[0])
280 274
 	if channelInfo == nil {
281
-		csNotice(rb, client.t("Channel does not exist"))
275
+		service.Notice(rb, client.t("Channel does not exist"))
282 276
 		return
283 277
 	}
284 278
 	channelName := channelInfo.Name()
285 279
 
286 280
 	clientAccount := client.Account()
287 281
 	if clientAccount == "" || clientAccount != channelInfo.Founder() {
288
-		csNotice(rb, client.t("Only the channel founder can do this"))
282
+		service.Notice(rb, client.t("Only the channel founder can do this"))
289 283
 		return
290 284
 	}
291 285
 
@@ -293,7 +287,7 @@ func csOpHandler(server *Server, client *Client, command string, params []string
293 287
 	if len(params) > 1 {
294 288
 		target = server.clients.Get(params[1])
295 289
 		if target == nil {
296
-			csNotice(rb, client.t("Could not find given client"))
290
+			service.Notice(rb, client.t("Could not find given client"))
297 291
 			return
298 292
 		}
299 293
 	} else {
@@ -315,21 +309,21 @@ func csOpHandler(server *Server, client *Client, command string, params []string
315 309
 		announceCmodeChanges(channelInfo, modes.ModeChanges{change}, server.name, "*", "", rb)
316 310
 	}
317 311
 
318
-	csNotice(rb, client.t("Successfully granted operator privileges"))
312
+	service.Notice(rb, client.t("Successfully granted operator privileges"))
319 313
 
320 314
 	tnick := target.Nick()
321 315
 	server.logger.Info("services", fmt.Sprintf("Client %s op'd [%s] in channel %s", client.Nick(), tnick, channelName))
322 316
 	server.snomasks.Send(sno.LocalChannels, fmt.Sprintf(ircfmt.Unescape("Client $c[grey][$r%s$c[grey]] CS OP'd $c[grey][$r%s$c[grey]] in channel $c[grey][$r%s$c[grey]]"), client.NickMaskString(), tnick, channelName))
323 317
 }
324 318
 
325
-func csDeopHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
319
+func csDeopHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
326 320
 	channel := server.channels.Get(params[0])
327 321
 	if channel == nil {
328
-		csNotice(rb, client.t("Channel does not exist"))
322
+		service.Notice(rb, client.t("Channel does not exist"))
329 323
 		return
330 324
 	}
331 325
 	if !channel.hasClient(client) {
332
-		csNotice(rb, client.t("You're not on that channel"))
326
+		service.Notice(rb, client.t("You're not on that channel"))
333 327
 		return
334 328
 	}
335 329
 
@@ -337,7 +331,7 @@ func csDeopHandler(server *Server, client *Client, command string, params []stri
337 331
 	if len(params) > 1 {
338 332
 		target = server.clients.Get(params[1])
339 333
 		if target == nil {
340
-			csNotice(rb, client.t("Could not find given client"))
334
+			service.Notice(rb, client.t("Could not find given client"))
341 335
 			return
342 336
 		}
343 337
 	} else {
@@ -346,7 +340,7 @@ func csDeopHandler(server *Server, client *Client, command string, params []stri
346 340
 
347 341
 	present, cumodes := channel.ClientStatus(target)
348 342
 	if !present || len(cumodes) == 0 {
349
-		csNotice(rb, client.t("Target has no privileges to remove"))
343
+		service.Notice(rb, client.t("Target has no privileges to remove"))
350 344
 		return
351 345
 	}
352 346
 
@@ -370,38 +364,38 @@ func csDeopHandler(server *Server, client *Client, command string, params []stri
370 364
 		return
371 365
 	}
372 366
 
373
-	csNotice(rb, client.t("Successfully removed operator privileges"))
367
+	service.Notice(rb, client.t("Successfully removed operator privileges"))
374 368
 }
375 369
 
376
-func csRegisterHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
370
+func csRegisterHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
377 371
 	if server.Config().Channels.Registration.OperatorOnly && !client.HasRoleCapabs("chanreg") {
378
-		csNotice(rb, client.t("Channel registration is restricted to server operators"))
372
+		service.Notice(rb, client.t("Channel registration is restricted to server operators"))
379 373
 		return
380 374
 	}
381 375
 	channelName := params[0]
382 376
 	channelInfo := server.channels.Get(channelName)
383 377
 	if channelInfo == nil {
384
-		csNotice(rb, client.t("No such channel"))
378
+		service.Notice(rb, client.t("No such channel"))
385 379
 		return
386 380
 	}
387 381
 	if !channelInfo.ClientIsAtLeast(client, modes.ChannelOperator) {
388
-		csNotice(rb, client.t("You must be an oper on the channel to register it"))
382
+		service.Notice(rb, client.t("You must be an oper on the channel to register it"))
389 383
 		return
390 384
 	}
391 385
 
392 386
 	account := client.Account()
393
-	if !checkChanLimit(client, rb) {
387
+	if !checkChanLimit(service, client, rb) {
394 388
 		return
395 389
 	}
396 390
 
397 391
 	// this provides the synchronization that allows exactly one registration of the channel:
398 392
 	err := server.channels.SetRegistered(channelName, account)
399 393
 	if err != nil {
400
-		csNotice(rb, err.Error())
394
+		service.Notice(rb, err.Error())
401 395
 		return
402 396
 	}
403 397
 
404
-	csNotice(rb, fmt.Sprintf(client.t("Channel %s successfully registered"), channelName))
398
+	service.Notice(rb, fmt.Sprintf(client.t("Channel %s successfully registered"), channelName))
405 399
 
406 400
 	server.logger.Info("services", fmt.Sprintf("Client %s registered channel %s", client.Nick(), channelName))
407 401
 	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))
@@ -415,38 +409,38 @@ func csRegisterHandler(server *Server, client *Client, command string, params []
415 409
 		},
416 410
 		rb)
417 411
 	if applied {
418
-		announceCmodeChanges(channelInfo, modes.ModeChanges{change}, chanservMask, "*", "", rb)
412
+		announceCmodeChanges(channelInfo, modes.ModeChanges{change}, service.prefix, "*", "", rb)
419 413
 	}
420 414
 }
421 415
 
422 416
 // check whether a client has already registered too many channels
423
-func checkChanLimit(client *Client, rb *ResponseBuffer) (ok bool) {
417
+func checkChanLimit(service *ircService, client *Client, rb *ResponseBuffer) (ok bool) {
424 418
 	account := client.Account()
425 419
 	channelsAlreadyRegistered := client.server.accounts.ChannelsForAccount(account)
426 420
 	ok = len(channelsAlreadyRegistered) < client.server.Config().Channels.Registration.MaxChannelsPerAccount || client.HasRoleCapabs("chanreg")
427 421
 	if !ok {
428
-		csNotice(rb, client.t("You have already registered the maximum number of channels; try dropping some with /CS UNREGISTER"))
422
+		service.Notice(rb, client.t("You have already registered the maximum number of channels; try dropping some with /CS UNREGISTER"))
429 423
 	}
430 424
 	return
431 425
 }
432 426
 
433
-func csPrivsCheck(channel RegisteredChannel, client *Client, rb *ResponseBuffer) (success bool) {
427
+func csPrivsCheck(service *ircService, channel RegisteredChannel, client *Client, rb *ResponseBuffer) (success bool) {
434 428
 	founder := channel.Founder
435 429
 	if founder == "" {
436
-		csNotice(rb, client.t("That channel is not registered"))
430
+		service.Notice(rb, client.t("That channel is not registered"))
437 431
 		return false
438 432
 	}
439 433
 	if client.HasRoleCapabs("chanreg") {
440 434
 		return true
441 435
 	}
442 436
 	if founder != client.Account() {
443
-		csNotice(rb, client.t("Insufficient privileges"))
437
+		service.Notice(rb, client.t("Insufficient privileges"))
444 438
 		return false
445 439
 	}
446 440
 	return true
447 441
 }
448 442
 
449
-func csUnregisterHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
443
+func csUnregisterHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
450 444
 	channelName := params[0]
451 445
 	var verificationCode string
452 446
 	if len(params) > 1 {
@@ -455,41 +449,41 @@ func csUnregisterHandler(server *Server, client *Client, command string, params
455 449
 
456 450
 	channel := server.channels.Get(channelName)
457 451
 	if channel == nil {
458
-		csNotice(rb, client.t("No such channel"))
452
+		service.Notice(rb, client.t("No such channel"))
459 453
 		return
460 454
 	}
461 455
 
462 456
 	info := channel.ExportRegistration(0)
463 457
 	channelKey := info.NameCasefolded
464
-	if !csPrivsCheck(info, client, rb) {
458
+	if !csPrivsCheck(service, info, client, rb) {
465 459
 		return
466 460
 	}
467 461
 
468 462
 	expectedCode := utils.ConfirmationCode(info.Name, info.RegisteredAt)
469 463
 	if expectedCode != verificationCode {
470
-		csNotice(rb, ircfmt.Unescape(client.t("$bWarning: unregistering this channel will remove all stored channel attributes.$b")))
471
-		csNotice(rb, fmt.Sprintf(client.t("To confirm, run this command: %s"), fmt.Sprintf("/CS UNREGISTER %s %s", channelKey, expectedCode)))
464
+		service.Notice(rb, ircfmt.Unescape(client.t("$bWarning: unregistering this channel will remove all stored channel attributes.$b")))
465
+		service.Notice(rb, fmt.Sprintf(client.t("To confirm, run this command: %s"), fmt.Sprintf("/CS UNREGISTER %s %s", channelKey, expectedCode)))
472 466
 		return
473 467
 	}
474 468
 
475 469
 	server.channels.SetUnregistered(channelKey, info.Founder)
476
-	csNotice(rb, fmt.Sprintf(client.t("Channel %s is now unregistered"), channelKey))
470
+	service.Notice(rb, fmt.Sprintf(client.t("Channel %s is now unregistered"), channelKey))
477 471
 }
478 472
 
479
-func csClearHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
473
+func csClearHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
480 474
 	channel := server.channels.Get(params[0])
481 475
 	if channel == nil {
482
-		csNotice(rb, client.t("Channel does not exist"))
476
+		service.Notice(rb, client.t("Channel does not exist"))
483 477
 		return
484 478
 	}
485
-	if !csPrivsCheck(channel.ExportRegistration(0), client, rb) {
479
+	if !csPrivsCheck(service, channel.ExportRegistration(0), client, rb) {
486 480
 		return
487 481
 	}
488 482
 
489 483
 	switch strings.ToLower(params[1]) {
490 484
 	case "access":
491 485
 		channel.resetAccess()
492
-		csNotice(rb, client.t("Successfully reset channel access"))
486
+		service.Notice(rb, client.t("Successfully reset channel access"))
493 487
 	case "users":
494 488
 		for _, target := range channel.Members() {
495 489
 			if target != client {
@@ -497,20 +491,20 @@ func csClearHandler(server *Server, client *Client, command string, params []str
497 491
 			}
498 492
 		}
499 493
 	default:
500
-		csNotice(rb, client.t("Invalid parameters"))
494
+		service.Notice(rb, client.t("Invalid parameters"))
501 495
 	}
502 496
 
503 497
 }
504 498
 
505
-func csTransferHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
499
+func csTransferHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
506 500
 	if strings.ToLower(params[0]) == "accept" {
507
-		processTransferAccept(client, params[1], rb)
501
+		processTransferAccept(service, client, params[1], rb)
508 502
 		return
509 503
 	}
510 504
 	chname := params[0]
511 505
 	channel := server.channels.Get(chname)
512 506
 	if channel == nil {
513
-		csNotice(rb, client.t("Channel does not exist"))
507
+		service.Notice(rb, client.t("Channel does not exist"))
514 508
 		return
515 509
 	}
516 510
 	regInfo := channel.ExportRegistration(0)
@@ -519,21 +513,21 @@ func csTransferHandler(server *Server, client *Client, command string, params []
519 513
 	isFounder := account != "" && account == regInfo.Founder
520 514
 	hasPrivs := client.HasRoleCapabs("chanreg")
521 515
 	if !(isFounder || hasPrivs) {
522
-		csNotice(rb, client.t("Insufficient privileges"))
516
+		service.Notice(rb, client.t("Insufficient privileges"))
523 517
 		return
524 518
 	}
525 519
 	target := params[1]
526 520
 	targetAccount, err := server.accounts.LoadAccount(params[1])
527 521
 	if err != nil {
528
-		csNotice(rb, client.t("Account does not exist"))
522
+		service.Notice(rb, client.t("Account does not exist"))
529 523
 		return
530 524
 	}
531 525
 	if targetAccount.NameCasefolded != account {
532 526
 		expectedCode := utils.ConfirmationCode(regInfo.Name, regInfo.RegisteredAt)
533 527
 		codeValidated := 2 < len(params) && params[2] == expectedCode
534 528
 		if !codeValidated {
535
-			csNotice(rb, ircfmt.Unescape(client.t("$bWarning: you are about to transfer control of your channel to another user.$b")))
536
-			csNotice(rb, fmt.Sprintf(client.t("To confirm your channel transfer, type: /CS TRANSFER %[1]s %[2]s %[3]s"), chname, target, expectedCode))
529
+			service.Notice(rb, ircfmt.Unescape(client.t("$bWarning: you are about to transfer control of your channel to another user.$b")))
530
+			service.Notice(rb, fmt.Sprintf(client.t("To confirm your channel transfer, type: /CS TRANSFER %[1]s %[2]s %[3]s"), chname, target, expectedCode))
537 531
 			return
538 532
 		}
539 533
 	}
@@ -541,19 +535,19 @@ func csTransferHandler(server *Server, client *Client, command string, params []
541 535
 	if err == nil {
542 536
 		switch status {
543 537
 		case channelTransferComplete:
544
-			csNotice(rb, fmt.Sprintf(client.t("Successfully transferred channel %[1]s to account %[2]s"), chname, target))
538
+			service.Notice(rb, fmt.Sprintf(client.t("Successfully transferred channel %[1]s to account %[2]s"), chname, target))
545 539
 		case channelTransferPending:
546
-			sendTransferPendingNotice(server, target, chname)
547
-			csNotice(rb, fmt.Sprintf(client.t("Transfer of channel %[1]s to account %[2]s succeeded, pending acceptance"), chname, target))
540
+			sendTransferPendingNotice(service, server, target, chname)
541
+			service.Notice(rb, fmt.Sprintf(client.t("Transfer of channel %[1]s to account %[2]s succeeded, pending acceptance"), chname, target))
548 542
 		case channelTransferCancelled:
549
-			csNotice(rb, fmt.Sprintf(client.t("Cancelled pending transfer of channel %s"), chname))
543
+			service.Notice(rb, fmt.Sprintf(client.t("Cancelled pending transfer of channel %s"), chname))
550 544
 		}
551 545
 	} else {
552
-		csNotice(rb, client.t("Could not transfer channel"))
546
+		service.Notice(rb, client.t("Could not transfer channel"))
553 547
 	}
554 548
 }
555 549
 
556
-func sendTransferPendingNotice(server *Server, account, chname string) {
550
+func sendTransferPendingNotice(service *ircService, server *Server, account, chname string) {
557 551
 	clients := server.accounts.AccountToClients(account)
558 552
 	if len(clients) == 0 {
559 553
 		return
@@ -565,29 +559,29 @@ func sendTransferPendingNotice(server *Server, account, chname string) {
565 559
 			break // prefer the login where the nick is the account
566 560
 		}
567 561
 	}
568
-	client.Send(nil, chanservMask, "NOTICE", client.Nick(), fmt.Sprintf(client.t("You have been offered ownership of channel %[1]s. To accept, /CS TRANSFER ACCEPT %[1]s"), chname))
562
+	client.Send(nil, service.prefix, "NOTICE", client.Nick(), fmt.Sprintf(client.t("You have been offered ownership of channel %[1]s. To accept, /CS TRANSFER ACCEPT %[1]s"), chname))
569 563
 }
570 564
 
571
-func processTransferAccept(client *Client, chname string, rb *ResponseBuffer) {
565
+func processTransferAccept(service *ircService, client *Client, chname string, rb *ResponseBuffer) {
572 566
 	channel := client.server.channels.Get(chname)
573 567
 	if channel == nil {
574
-		csNotice(rb, client.t("Channel does not exist"))
568
+		service.Notice(rb, client.t("Channel does not exist"))
575 569
 		return
576 570
 	}
577
-	if !checkChanLimit(client, rb) {
571
+	if !checkChanLimit(service, client, rb) {
578 572
 		return
579 573
 	}
580 574
 	switch channel.AcceptTransfer(client) {
581 575
 	case nil:
582
-		csNotice(rb, fmt.Sprintf(client.t("Successfully accepted ownership of channel %s"), channel.Name()))
576
+		service.Notice(rb, fmt.Sprintf(client.t("Successfully accepted ownership of channel %s"), channel.Name()))
583 577
 	case errChannelTransferNotOffered:
584
-		csNotice(rb, fmt.Sprintf(client.t("You weren't offered ownership of channel %s"), channel.Name()))
578
+		service.Notice(rb, fmt.Sprintf(client.t("You weren't offered ownership of channel %s"), channel.Name()))
585 579
 	default:
586
-		csNotice(rb, fmt.Sprintf(client.t("Could not accept ownership of channel %s"), channel.Name()))
580
+		service.Notice(rb, fmt.Sprintf(client.t("Could not accept ownership of channel %s"), channel.Name()))
587 581
 	}
588 582
 }
589 583
 
590
-func csPurgeHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
584
+func csPurgeHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
591 585
 	oper := client.Oper()
592 586
 	if oper == nil {
593 587
 		return // should be impossible because you need oper capabs for this
@@ -611,29 +605,29 @@ func csPurgeHandler(server *Server, client *Client, command string, params []str
611 605
 				channel.Kick(client, target, "Cleared by ChanServ", rb, true)
612 606
 			}
613 607
 		}
614
-		csNotice(rb, fmt.Sprintf(client.t("Successfully purged channel %s from the server"), chname))
608
+		service.Notice(rb, fmt.Sprintf(client.t("Successfully purged channel %s from the server"), chname))
615 609
 	case errInvalidChannelName:
616
-		csNotice(rb, fmt.Sprintf(client.t("Can't purge invalid channel %s"), chname))
610
+		service.Notice(rb, fmt.Sprintf(client.t("Can't purge invalid channel %s"), chname))
617 611
 	default:
618
-		csNotice(rb, client.t("An error occurred"))
612
+		service.Notice(rb, client.t("An error occurred"))
619 613
 	}
620 614
 }
621 615
 
622
-func csUnpurgeHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
616
+func csUnpurgeHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
623 617
 	chname := params[0]
624 618
 	switch server.channels.Unpurge(chname) {
625 619
 	case nil:
626
-		csNotice(rb, fmt.Sprintf(client.t("Successfully unpurged channel %s from the server"), chname))
620
+		service.Notice(rb, fmt.Sprintf(client.t("Successfully unpurged channel %s from the server"), chname))
627 621
 	case errNoSuchChannel:
628
-		csNotice(rb, fmt.Sprintf(client.t("Channel %s wasn't previously purged from the server"), chname))
622
+		service.Notice(rb, fmt.Sprintf(client.t("Channel %s wasn't previously purged from the server"), chname))
629 623
 	default:
630
-		csNotice(rb, client.t("An error occurred"))
624
+		service.Notice(rb, client.t("An error occurred"))
631 625
 	}
632 626
 }
633 627
 
634
-func csListHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
628
+func csListHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
635 629
 	if !client.HasRoleCapabs("chanreg") {
636
-		csNotice(rb, client.t("Insufficient privileges"))
630
+		service.Notice(rb, client.t("Insufficient privileges"))
637 631
 		return
638 632
 	}
639 633
 
@@ -642,27 +636,27 @@ func csListHandler(server *Server, client *Client, command string, params []stri
642 636
 		var err error
643 637
 		searchRegex, err = regexp.Compile(params[0])
644 638
 		if err != nil {
645
-			csNotice(rb, client.t("Invalid regex"))
639
+			service.Notice(rb, client.t("Invalid regex"))
646 640
 			return
647 641
 		}
648 642
 	}
649 643
 
650
-	csNotice(rb, ircfmt.Unescape(client.t("*** $bChanServ LIST$b ***")))
644
+	service.Notice(rb, ircfmt.Unescape(client.t("*** $bChanServ LIST$b ***")))
651 645
 
652 646
 	channels := server.channelRegistry.AllChannels()
653 647
 	for _, channel := range channels {
654 648
 		if searchRegex == nil || searchRegex.MatchString(channel) {
655
-			csNotice(rb, fmt.Sprintf("    %s", channel))
649
+			service.Notice(rb, fmt.Sprintf("    %s", channel))
656 650
 		}
657 651
 	}
658 652
 
659
-	csNotice(rb, ircfmt.Unescape(client.t("*** $bEnd of ChanServ LIST$b ***")))
653
+	service.Notice(rb, ircfmt.Unescape(client.t("*** $bEnd of ChanServ LIST$b ***")))
660 654
 }
661 655
 
662
-func csInfoHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
656
+func csInfoHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
663 657
 	chname, err := CasefoldChannel(params[0])
664 658
 	if err != nil {
665
-		csNotice(rb, client.t("Invalid channel name"))
659
+		service.Notice(rb, client.t("Invalid channel name"))
666 660
 		return
667 661
 	}
668 662
 
@@ -670,16 +664,16 @@ func csInfoHandler(server *Server, client *Client, command string, params []stri
670 664
 	if client.HasRoleCapabs("chanreg") {
671 665
 		purgeRecord, err := server.channelRegistry.LoadPurgeRecord(chname)
672 666
 		if err == nil {
673
-			csNotice(rb, fmt.Sprintf(client.t("Channel %s was purged by the server operators and cannot be used"), chname))
674
-			csNotice(rb, fmt.Sprintf(client.t("Purged by operator: %s"), purgeRecord.Oper))
675
-			csNotice(rb, fmt.Sprintf(client.t("Purged at: %s"), purgeRecord.PurgedAt.Format(time.RFC1123)))
667
+			service.Notice(rb, fmt.Sprintf(client.t("Channel %s was purged by the server operators and cannot be used"), chname))
668
+			service.Notice(rb, fmt.Sprintf(client.t("Purged by operator: %s"), purgeRecord.Oper))
669
+			service.Notice(rb, fmt.Sprintf(client.t("Purged at: %s"), purgeRecord.PurgedAt.Format(time.RFC1123)))
676 670
 			if purgeRecord.Reason != "" {
677
-				csNotice(rb, fmt.Sprintf(client.t("Purge reason: %s"), purgeRecord.Reason))
671
+				service.Notice(rb, fmt.Sprintf(client.t("Purge reason: %s"), purgeRecord.Reason))
678 672
 			}
679 673
 		}
680 674
 	} else {
681 675
 		if server.channels.IsPurged(chname) {
682
-			csNotice(rb, fmt.Sprintf(client.t("Channel %s was purged by the server operators and cannot be used"), chname))
676
+			service.Notice(rb, fmt.Sprintf(client.t("Channel %s was purged by the server operators and cannot be used"), chname))
683 677
 		}
684 678
 	}
685 679
 
@@ -690,59 +684,59 @@ func csInfoHandler(server *Server, client *Client, command string, params []stri
690 684
 	} else {
691 685
 		chinfo, err = server.channelRegistry.LoadChannel(chname)
692 686
 		if err != nil && !(err == errNoSuchChannel || err == errFeatureDisabled) {
693
-			csNotice(rb, client.t("An error occurred"))
687
+			service.Notice(rb, client.t("An error occurred"))
694 688
 			return
695 689
 		}
696 690
 	}
697 691
 
698 692
 	// channel exists but is unregistered, or doesn't exist:
699 693
 	if chinfo.Founder == "" {
700
-		csNotice(rb, fmt.Sprintf(client.t("Channel %s is not registered"), chname))
694
+		service.Notice(rb, fmt.Sprintf(client.t("Channel %s is not registered"), chname))
701 695
 		return
702 696
 	}
703
-	csNotice(rb, fmt.Sprintf(client.t("Channel %s is registered"), chinfo.Name))
704
-	csNotice(rb, fmt.Sprintf(client.t("Founder: %s"), chinfo.Founder))
705
-	csNotice(rb, fmt.Sprintf(client.t("Registered at: %s"), chinfo.RegisteredAt.Format(time.RFC1123)))
697
+	service.Notice(rb, fmt.Sprintf(client.t("Channel %s is registered"), chinfo.Name))
698
+	service.Notice(rb, fmt.Sprintf(client.t("Founder: %s"), chinfo.Founder))
699
+	service.Notice(rb, fmt.Sprintf(client.t("Registered at: %s"), chinfo.RegisteredAt.Format(time.RFC1123)))
706 700
 }
707 701
 
708
-func displayChannelSetting(settingName string, settings ChannelSettings, client *Client, rb *ResponseBuffer) {
702
+func displayChannelSetting(service *ircService, settingName string, settings ChannelSettings, client *Client, rb *ResponseBuffer) {
709 703
 	config := client.server.Config()
710 704
 
711 705
 	switch strings.ToLower(settingName) {
712 706
 	case "history":
713 707
 		effectiveValue := historyEnabled(config.History.Persistent.RegisteredChannels, settings.History)
714
-		csNotice(rb, fmt.Sprintf(client.t("The stored channel history setting is: %s"), historyStatusToString(settings.History)))
715
-		csNotice(rb, fmt.Sprintf(client.t("Given current server settings, the channel history setting is: %s"), historyStatusToString(effectiveValue)))
708
+		service.Notice(rb, fmt.Sprintf(client.t("The stored channel history setting is: %s"), historyStatusToString(settings.History)))
709
+		service.Notice(rb, fmt.Sprintf(client.t("Given current server settings, the channel history setting is: %s"), historyStatusToString(effectiveValue)))
716 710
 	default:
717
-		csNotice(rb, client.t("Invalid params"))
711
+		service.Notice(rb, client.t("Invalid params"))
718 712
 	}
719 713
 }
720 714
 
721
-func csGetHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
715
+func csGetHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
722 716
 	chname, setting := params[0], params[1]
723 717
 	channel := server.channels.Get(chname)
724 718
 	if channel == nil {
725
-		csNotice(rb, client.t("No such channel"))
719
+		service.Notice(rb, client.t("No such channel"))
726 720
 		return
727 721
 	}
728 722
 	info := channel.ExportRegistration(IncludeSettings)
729
-	if !csPrivsCheck(info, client, rb) {
723
+	if !csPrivsCheck(service, info, client, rb) {
730 724
 		return
731 725
 	}
732 726
 
733
-	displayChannelSetting(setting, info.Settings, client, rb)
727
+	displayChannelSetting(service, setting, info.Settings, client, rb)
734 728
 }
735 729
 
736
-func csSetHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
730
+func csSetHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
737 731
 	chname, setting, value := params[0], params[1], params[2]
738 732
 	channel := server.channels.Get(chname)
739 733
 	if channel == nil {
740
-		csNotice(rb, client.t("No such channel"))
734
+		service.Notice(rb, client.t("No such channel"))
741 735
 		return
742 736
 	}
743 737
 	info := channel.ExportRegistration(IncludeSettings)
744 738
 	settings := info.Settings
745
-	if !csPrivsCheck(info, client, rb) {
739
+	if !csPrivsCheck(service, info, client, rb) {
746 740
 		return
747 741
 	}
748 742
 
@@ -760,12 +754,12 @@ func csSetHandler(server *Server, client *Client, command string, params []strin
760 754
 
761 755
 	switch err {
762 756
 	case nil:
763
-		csNotice(rb, client.t("Successfully changed the channel settings"))
764
-		displayChannelSetting(setting, settings, client, rb)
757
+		service.Notice(rb, client.t("Successfully changed the channel settings"))
758
+		displayChannelSetting(service, setting, settings, client, rb)
765 759
 	case errInvalidParams:
766
-		csNotice(rb, client.t("Invalid parameters"))
760
+		service.Notice(rb, client.t("Invalid parameters"))
767 761
 	default:
768 762
 		server.logger.Error("internal", "CS SET error:", err.Error())
769
-		csNotice(rb, client.t("An error occurred"))
763
+		service.Notice(rb, client.t("An error occurred"))
770 764
 	}
771 765
 }

+ 18
- 18
irc/handlers.go Visa fil

@@ -77,23 +77,23 @@ func registrationErrorToMessage(err error) (message string) {
77 77
 }
78 78
 
79 79
 // helper function to dispatch messages when a client successfully registers
80
-func sendSuccessfulRegResponse(client *Client, rb *ResponseBuffer, forNS bool) {
80
+func sendSuccessfulRegResponse(service *ircService, client *Client, rb *ResponseBuffer) {
81 81
 	details := client.Details()
82
-	if forNS {
83
-		nsNotice(rb, client.t("Account created"))
82
+	if service != nil {
83
+		service.Notice(rb, client.t("Account created"))
84 84
 	} else {
85 85
 		rb.Add(nil, client.server.name, RPL_REG_SUCCESS, details.nick, details.accountName, client.t("Account created"))
86 86
 	}
87 87
 	client.server.snomasks.Send(sno.LocalAccounts, fmt.Sprintf(ircfmt.Unescape("Client $c[grey][$r%s$c[grey]] registered account $c[grey][$r%s$c[grey]] from IP %s"), details.nickMask, details.accountName, rb.session.IP().String()))
88
-	sendSuccessfulAccountAuth(client, rb, forNS, false)
88
+	sendSuccessfulAccountAuth(service, client, rb, false)
89 89
 }
90 90
 
91 91
 // sendSuccessfulAccountAuth means that an account auth attempt completed successfully, and is used to dispatch messages.
92
-func sendSuccessfulAccountAuth(client *Client, rb *ResponseBuffer, forNS, forSASL bool) {
92
+func sendSuccessfulAccountAuth(service *ircService, client *Client, rb *ResponseBuffer, forSASL bool) {
93 93
 	details := client.Details()
94 94
 
95
-	if forNS {
96
-		nsNotice(rb, fmt.Sprintf(client.t("You're now logged in as %s"), details.accountName))
95
+	if service != nil {
96
+		service.Notice(rb, fmt.Sprintf(client.t("You're now logged in as %s"), details.accountName))
97 97
 	} else {
98 98
 		//TODO(dan): some servers send this numeric even for NickServ logins iirc? to confirm and maybe do too
99 99
 		rb.Add(nil, client.server.name, RPL_LOGGEDIN, details.nick, details.nickMask, details.accountName, fmt.Sprintf(client.t("You are now logged in as %s"), details.accountName))
@@ -253,11 +253,11 @@ func authPlainHandler(server *Server, client *Client, mechanism string, value []
253 253
 		msg := authErrorToMessage(server, err)
254 254
 		rb.Add(nil, server.name, ERR_SASLFAIL, client.Nick(), fmt.Sprintf("%s: %s", client.t("SASL authentication failed"), client.t(msg)))
255 255
 		return false
256
-	} else if !fixupNickEqualsAccount(client, rb, server.Config()) {
256
+	} else if !fixupNickEqualsAccount(client, rb, server.Config(), "") {
257 257
 		return false
258 258
 	}
259 259
 
260
-	sendSuccessfulAccountAuth(client, rb, false, true)
260
+	sendSuccessfulAccountAuth(nil, client, rb, true)
261 261
 	return false
262 262
 }
263 263
 
@@ -311,11 +311,11 @@ func authExternalHandler(server *Server, client *Client, mechanism string, value
311 311
 		msg := authErrorToMessage(server, err)
312 312
 		rb.Add(nil, server.name, ERR_SASLFAIL, client.nick, fmt.Sprintf("%s: %s", client.t("SASL authentication failed"), client.t(msg)))
313 313
 		return false
314
-	} else if !fixupNickEqualsAccount(client, rb, server.Config()) {
314
+	} else if !fixupNickEqualsAccount(client, rb, server.Config(), "") {
315 315
 		return false
316 316
 	}
317 317
 
318
-	sendSuccessfulAccountAuth(client, rb, false, true)
318
+	sendSuccessfulAccountAuth(nil, client, rb, true)
319 319
 	return false
320 320
 }
321 321
 
@@ -1525,7 +1525,7 @@ func listHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp
1525 1525
 	config := server.Config()
1526 1526
 	if time.Since(client.ctime) < config.Channels.ListDelay && client.Account() == "" && !client.HasMode(modes.Operator) {
1527 1527
 		remaining := time.Until(client.ctime.Add(config.Channels.ListDelay))
1528
-		csNotice(rb, fmt.Sprintf(client.t("This server requires that you wait %v after connecting before you can use /LIST. You have %v left."), config.Channels.ListDelay, remaining))
1528
+		rb.Notice(fmt.Sprintf(client.t("This server requires that you wait %v after connecting before you can use /LIST. You have %v left."), config.Channels.ListDelay, remaining))
1529 1529
 		rb.Add(nil, server.name, RPL_LISTEND, client.Nick(), client.t("End of LIST"))
1530 1530
 		return false
1531 1531
 	}
@@ -2355,7 +2355,7 @@ func passHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp
2355 2355
 			}
2356 2356
 			err := server.accounts.AuthenticateByPassphrase(client, account, accountPass)
2357 2357
 			if err == nil {
2358
-				sendSuccessfulAccountAuth(client, rb, false, true)
2358
+				sendSuccessfulAccountAuth(nil, client, rb, true)
2359 2359
 				// login-via-pass-command entails that we do not need to check
2360 2360
 				// an actual server password (either no password or skip-server-password)
2361 2361
 				rb.session.passStatus = serverPassSuccessful
@@ -2446,13 +2446,13 @@ func registerHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *
2446 2446
 			err := server.accounts.Verify(client, accountName, "")
2447 2447
 			if err == nil {
2448 2448
 				if client.registered {
2449
-					if !fixupNickEqualsAccount(client, rb, config) {
2449
+					if !fixupNickEqualsAccount(client, rb, config, "") {
2450 2450
 						err = errNickAccountMismatch
2451 2451
 					}
2452 2452
 				}
2453 2453
 				if err == nil {
2454 2454
 					rb.Add(nil, server.name, "REGISTER", "SUCCESS", accountName, client.t("Account successfully registered"))
2455
-					sendSuccessfulRegResponse(client, rb, true)
2455
+					sendSuccessfulRegResponse(nil, client, rb)
2456 2456
 				}
2457 2457
 			}
2458 2458
 			if err != nil {
@@ -2494,14 +2494,14 @@ func verifyHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Re
2494 2494
 	accountName, verificationCode := msg.Params[0], msg.Params[1]
2495 2495
 	err := server.accounts.Verify(client, accountName, verificationCode)
2496 2496
 	if err == nil && client.registered {
2497
-		if !fixupNickEqualsAccount(client, rb, config) {
2497
+		if !fixupNickEqualsAccount(client, rb, config, "") {
2498 2498
 			err = errNickAccountMismatch
2499 2499
 		}
2500 2500
 	}
2501 2501
 	switch err {
2502 2502
 	case nil:
2503 2503
 		rb.Add(nil, server.name, "VERIFY", "SUCCESS", accountName, client.t("Account successfully registered"))
2504
-		sendSuccessfulRegResponse(client, rb, true)
2504
+		sendSuccessfulRegResponse(nil, client, rb)
2505 2505
 	case errAccountVerificationInvalidCode:
2506 2506
 		rb.Add(nil, server.name, "FAIL", "VERIFY", "INVALID_CODE", client.t("Invalid verification code"))
2507 2507
 	default:
@@ -2873,7 +2873,7 @@ func userHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp
2873 2873
 			username, password = username[:colonIndex], username[colonIndex+1:]
2874 2874
 			err := server.accounts.AuthenticateByPassphrase(client, username, password)
2875 2875
 			if err == nil {
2876
-				sendSuccessfulAccountAuth(client, rb, false, true)
2876
+				sendSuccessfulAccountAuth(nil, client, rb, true)
2877 2877
 			} else {
2878 2878
 				// this is wrong, but send something for debugging that will show up in a raw transcript
2879 2879
 				rb.Add(nil, server.name, ERR_SASLFAIL, client.Nick(), client.t("SASL authentication failed"))

+ 19
- 25
irc/histserv.go Visa fil

@@ -18,7 +18,6 @@ import (
18 18
 
19 19
 const (
20 20
 	histservHelp = `HistServ provides commands related to history.`
21
-	histServMask = "HistServ!HistServ@localhost"
22 21
 )
23 22
 
24 23
 func histservEnabled(config *Config) bool {
@@ -83,24 +82,19 @@ CHATHISTORY.`,
83 82
 	}
84 83
 )
85 84
 
86
-// histNotice sends the client a notice from HistServ
87
-func histNotice(rb *ResponseBuffer, text string) {
88
-	rb.Add(nil, histServMask, "NOTICE", rb.target.Nick(), text)
89
-}
90
-
91
-func histservForgetHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
85
+func histservForgetHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
92 86
 	accountName := server.accounts.AccountToAccountName(params[0])
93 87
 	if accountName == "" {
94
-		histNotice(rb, client.t("Could not look up account name, proceeding anyway"))
88
+		service.Notice(rb, client.t("Could not look up account name, proceeding anyway"))
95 89
 		accountName = params[0]
96 90
 	}
97 91
 
98 92
 	server.ForgetHistory(accountName)
99 93
 
100
-	histNotice(rb, fmt.Sprintf(client.t("Enqueued account %s for message deletion"), accountName))
94
+	service.Notice(rb, fmt.Sprintf(client.t("Enqueued account %s for message deletion"), accountName))
101 95
 }
102 96
 
103
-func histservDeleteHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
97
+func histservDeleteHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
104 98
 	var target, msgid string
105 99
 	if len(params) == 1 {
106 100
 		msgid = params[0]
@@ -113,27 +107,27 @@ func histservDeleteHandler(server *Server, client *Client, command string, param
113 107
 	if !hasPrivs {
114 108
 		accountName = client.AccountName()
115 109
 		if !(server.Config().History.Retention.AllowIndividualDelete && accountName != "*") {
116
-			histNotice(rb, client.t("Insufficient privileges"))
110
+			service.Notice(rb, client.t("Insufficient privileges"))
117 111
 			return
118 112
 		}
119 113
 	}
120 114
 
121 115
 	err := server.DeleteMessage(target, msgid, accountName)
122 116
 	if err == nil {
123
-		histNotice(rb, client.t("Successfully deleted message"))
117
+		service.Notice(rb, client.t("Successfully deleted message"))
124 118
 	} else {
125 119
 		if hasPrivs {
126
-			histNotice(rb, fmt.Sprintf(client.t("Error deleting message: %v"), err))
120
+			service.Notice(rb, fmt.Sprintf(client.t("Error deleting message: %v"), err))
127 121
 		} else {
128
-			histNotice(rb, client.t("Could not delete message"))
122
+			service.Notice(rb, client.t("Could not delete message"))
129 123
 		}
130 124
 	}
131 125
 }
132 126
 
133
-func histservExportHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
127
+func histservExportHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
134 128
 	cfAccount, err := CasefoldName(params[0])
135 129
 	if err != nil {
136
-		histNotice(rb, client.t("Invalid account name"))
130
+		service.Notice(rb, client.t("Invalid account name"))
137 131
 		return
138 132
 	}
139 133
 
@@ -143,15 +137,15 @@ func histservExportHandler(server *Server, client *Client, command string, param
143 137
 	pathname := config.getOutputPath(filename)
144 138
 	outfile, err := os.Create(pathname)
145 139
 	if err != nil {
146
-		histNotice(rb, fmt.Sprintf(client.t("Error opening export file: %v"), err))
140
+		service.Notice(rb, fmt.Sprintf(client.t("Error opening export file: %v"), err))
147 141
 	} else {
148
-		histNotice(rb, fmt.Sprintf(client.t("Started exporting data for account %[1]s to file %[2]s"), cfAccount, filename))
142
+		service.Notice(rb, fmt.Sprintf(client.t("Started exporting data for account %[1]s to file %[2]s"), cfAccount, filename))
149 143
 	}
150 144
 
151
-	go histservExportAndNotify(server, cfAccount, outfile, filename, client.Nick())
145
+	go histservExportAndNotify(service, server, cfAccount, outfile, filename, client.Nick())
152 146
 }
153 147
 
154
-func histservExportAndNotify(server *Server, cfAccount string, outfile *os.File, filename, alertNick string) {
148
+func histservExportAndNotify(service *ircService, server *Server, cfAccount string, outfile *os.File, filename, alertNick string) {
155 149
 	defer func() {
156 150
 		if r := recover(); r != nil {
157 151
 			server.logger.Error("history",
@@ -167,19 +161,19 @@ func histservExportAndNotify(server *Server, cfAccount string, outfile *os.File,
167 161
 
168 162
 	client := server.clients.Get(alertNick)
169 163
 	if client != nil && client.HasRoleCapabs("history") {
170
-		client.Send(nil, histServMask, "NOTICE", client.Nick(), fmt.Sprintf(client.t("Data export for %[1]s completed and written to %[2]s"), cfAccount, filename))
164
+		client.Send(nil, service.prefix, "NOTICE", client.Nick(), fmt.Sprintf(client.t("Data export for %[1]s completed and written to %[2]s"), cfAccount, filename))
171 165
 	}
172 166
 }
173 167
 
174
-func histservPlayHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
168
+func histservPlayHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
175 169
 	items, _, err := easySelectHistory(server, client, params)
176 170
 	if err != nil {
177
-		histNotice(rb, client.t("Could not retrieve history"))
171
+		service.Notice(rb, client.t("Could not retrieve history"))
178 172
 		return
179 173
 	}
180 174
 
181 175
 	playMessage := func(timestamp time.Time, nick, message string) {
182
-		histNotice(rb, fmt.Sprintf("%s <%s> %s", timestamp.Format("15:04:05"), stripMaskFromNick(nick), message))
176
+		service.Notice(rb, fmt.Sprintf("%s <%s> %s", timestamp.Format("15:04:05"), stripMaskFromNick(nick), message))
183 177
 	}
184 178
 
185 179
 	for _, item := range items {
@@ -196,7 +190,7 @@ func histservPlayHandler(server *Server, client *Client, command string, params
196 190
 		}
197 191
 	}
198 192
 
199
-	histNotice(rb, client.t("End of history playback"))
193
+	service.Notice(rb, client.t("End of history playback"))
200 194
 }
201 195
 
202 196
 // handles parameter parsing and history queries for /HISTORY and /HISTSERV PLAY

+ 21
- 27
irc/hostserv.go Visa fil

@@ -16,7 +16,6 @@ import (
16 16
 const (
17 17
 	hostservHelp = `HostServ lets you manage your vhost (i.e., the string displayed
18 18
 in place of your client's hostname/IP).`
19
-	hsNickMask = "HostServ!HostServ@localhost"
20 19
 )
21 20
 
22 21
 var (
@@ -95,12 +94,7 @@ display the necessary code.`,
95 94
 	}
96 95
 )
97 96
 
98
-// hsNotice sends the client a notice from HostServ
99
-func hsNotice(rb *ResponseBuffer, text string) {
100
-	rb.Add(nil, hsNickMask, "NOTICE", rb.target.Nick(), text)
101
-}
102
-
103
-func hsOnOffHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
97
+func hsOnOffHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
104 98
 	enable := false
105 99
 	if command == "on" {
106 100
 		enable = true
@@ -108,28 +102,28 @@ func hsOnOffHandler(server *Server, client *Client, command string, params []str
108 102
 
109 103
 	_, err := server.accounts.VHostSetEnabled(client, enable)
110 104
 	if err == errNoVhost {
111
-		hsNotice(rb, client.t(err.Error()))
105
+		service.Notice(rb, client.t(err.Error()))
112 106
 	} else if err != nil {
113
-		hsNotice(rb, client.t("An error occurred"))
107
+		service.Notice(rb, client.t("An error occurred"))
114 108
 	} else if enable {
115
-		hsNotice(rb, client.t("Successfully enabled your vhost"))
109
+		service.Notice(rb, client.t("Successfully enabled your vhost"))
116 110
 	} else {
117
-		hsNotice(rb, client.t("Successfully disabled your vhost"))
111
+		service.Notice(rb, client.t("Successfully disabled your vhost"))
118 112
 	}
119 113
 }
120 114
 
121
-func hsStatusHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
115
+func hsStatusHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
122 116
 	var accountName string
123 117
 	if len(params) > 0 {
124 118
 		if !client.HasRoleCapabs("vhosts") {
125
-			hsNotice(rb, client.t("Command restricted"))
119
+			service.Notice(rb, client.t("Command restricted"))
126 120
 			return
127 121
 		}
128 122
 		accountName = params[0]
129 123
 	} else {
130 124
 		accountName = client.Account()
131 125
 		if accountName == "" {
132
-			hsNotice(rb, client.t("You're not logged into an account"))
126
+			service.Notice(rb, client.t("You're not logged into an account"))
133 127
 			return
134 128
 		}
135 129
 	}
@@ -139,17 +133,17 @@ func hsStatusHandler(server *Server, client *Client, command string, params []st
139 133
 		if err != errAccountDoesNotExist {
140 134
 			server.logger.Warning("internal", "error loading account info", accountName, err.Error())
141 135
 		}
142
-		hsNotice(rb, client.t("No such account"))
136
+		service.Notice(rb, client.t("No such account"))
143 137
 		return
144 138
 	}
145 139
 
146 140
 	if account.VHost.ApprovedVHost != "" {
147
-		hsNotice(rb, fmt.Sprintf(client.t("Account %[1]s has vhost: %[2]s"), accountName, account.VHost.ApprovedVHost))
141
+		service.Notice(rb, fmt.Sprintf(client.t("Account %[1]s has vhost: %[2]s"), accountName, account.VHost.ApprovedVHost))
148 142
 		if !account.VHost.Enabled {
149
-			hsNotice(rb, client.t("This vhost is currently disabled, but can be enabled with /HS ON"))
143
+			service.Notice(rb, client.t("This vhost is currently disabled, but can be enabled with /HS ON"))
150 144
 		}
151 145
 	} else {
152
-		hsNotice(rb, fmt.Sprintf(client.t("Account %s has no vhost"), accountName))
146
+		service.Notice(rb, fmt.Sprintf(client.t("Account %s has no vhost"), accountName))
153 147
 	}
154 148
 }
155 149
 
@@ -164,14 +158,14 @@ func validateVhost(server *Server, vhost string, oper bool) error {
164 158
 	return nil
165 159
 }
166 160
 
167
-func hsSetHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
161
+func hsSetHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
168 162
 	user := params[0]
169 163
 	var vhost string
170 164
 
171 165
 	if command == "set" {
172 166
 		vhost = params[1]
173 167
 		if validateVhost(server, vhost, true) != nil {
174
-			hsNotice(rb, client.t("Invalid vhost"))
168
+			service.Notice(rb, client.t("Invalid vhost"))
175 169
 			return
176 170
 		}
177 171
 	}
@@ -179,22 +173,22 @@ func hsSetHandler(server *Server, client *Client, command string, params []strin
179 173
 
180 174
 	_, err := server.accounts.VHostSet(user, vhost)
181 175
 	if err != nil {
182
-		hsNotice(rb, client.t("An error occurred"))
176
+		service.Notice(rb, client.t("An error occurred"))
183 177
 	} else if vhost != "" {
184
-		hsNotice(rb, client.t("Successfully set vhost"))
178
+		service.Notice(rb, client.t("Successfully set vhost"))
185 179
 	} else {
186
-		hsNotice(rb, client.t("Successfully cleared vhost"))
180
+		service.Notice(rb, client.t("Successfully cleared vhost"))
187 181
 	}
188 182
 }
189 183
 
190
-func hsSetCloakSecretHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
184
+func hsSetCloakSecretHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
191 185
 	secret := params[0]
192 186
 	expectedCode := utils.ConfirmationCode(secret, server.ctime)
193 187
 	if len(params) == 1 || params[1] != expectedCode {
194
-		hsNotice(rb, ircfmt.Unescape(client.t("$bWarning: changing the cloak secret will invalidate stored ban/invite/exception lists.$b")))
195
-		hsNotice(rb, fmt.Sprintf(client.t("To confirm, run this command: %s"), fmt.Sprintf("/HS SETCLOAKSECRET %s %s", secret, expectedCode)))
188
+		service.Notice(rb, ircfmt.Unescape(client.t("$bWarning: changing the cloak secret will invalidate stored ban/invite/exception lists.$b")))
189
+		service.Notice(rb, fmt.Sprintf(client.t("To confirm, run this command: %s"), fmt.Sprintf("/HS SETCLOAKSECRET %s %s", secret, expectedCode)))
196 190
 		return
197 191
 	}
198 192
 	StoreCloakSecret(server.store, secret)
199
-	hsNotice(rb, client.t("Rotated the cloak secret; you must rehash or restart the server for it to take effect"))
193
+	service.Notice(rb, client.t("Rotated the cloak secret; you must rehash or restart the server for it to take effect"))
200 194
 }

+ 5
- 2
irc/nickname.go Visa fil

@@ -151,7 +151,7 @@ func (server *Server) RandomlyRename(client *Client) {
151 151
 // so we need to re-NICK automatically on every login event (IDENTIFY,
152 152
 // VERIFY, and a REGISTER that auto-verifies). if we can't get the nick
153 153
 // then we log them out (they will be able to reattach with SASL)
154
-func fixupNickEqualsAccount(client *Client, rb *ResponseBuffer, config *Config) (success bool) {
154
+func fixupNickEqualsAccount(client *Client, rb *ResponseBuffer, config *Config, source string) (success bool) {
155 155
 	if !config.Accounts.NickReservation.ForceNickEqualsAccount {
156 156
 		return true
157 157
 	}
@@ -161,7 +161,10 @@ func fixupNickEqualsAccount(client *Client, rb *ResponseBuffer, config *Config)
161 161
 	err := performNickChange(client.server, client, client, rb.session, client.AccountName(), rb)
162 162
 	if err != nil && err != errNoop {
163 163
 		client.server.accounts.Logout(client)
164
-		nsNotice(rb, client.t("A client is already using that account; try logging out and logging back in with SASL"))
164
+		if source == "" {
165
+			source = client.server.name
166
+		}
167
+		rb.Add(nil, source, "NOTICE", client.t("A client is already using that account; try logging out and logging back in with SASL"))
165 168
 		return false
166 169
 	}
167 170
 	return true

+ 184
- 195
irc/nickserv.go Visa fil

@@ -36,10 +36,6 @@ func servCmdRequiresBouncerEnabled(config *Config) bool {
36 36
 	return config.Accounts.Multiclient.Enabled
37 37
 }
38 38
 
39
-const (
40
-	nsPrefix = "NickServ!NickServ@localhost"
41
-)
42
-
43 39
 const nickservHelp = `NickServ lets you register, log in to, and manage an account.`
44 40
 
45 41
 var (
@@ -361,14 +357,7 @@ Currently, you can only change the canonical casefolding of an account
361 357
 	}
362 358
 )
363 359
 
364
-// nsNotice sends the client a notice from NickServ
365
-func nsNotice(rb *ResponseBuffer, text string) {
366
-	// XXX i can't figure out how to use OragonoServices[servicename].prefix here
367
-	// without creating a compile-time initialization loop
368
-	rb.Add(nil, nsPrefix, "NOTICE", rb.target.Nick(), text)
369
-}
370
-
371
-func nsGetHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
360
+func nsGetHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
372 361
 	var account string
373 362
 	if command == "saget" {
374 363
 		account = params[0]
@@ -379,101 +368,101 @@ func nsGetHandler(server *Server, client *Client, command string, params []strin
379 368
 
380 369
 	accountData, err := server.accounts.LoadAccount(account)
381 370
 	if err == errAccountDoesNotExist {
382
-		nsNotice(rb, client.t("No such account"))
371
+		service.Notice(rb, client.t("No such account"))
383 372
 		return
384 373
 	} else if err != nil {
385
-		nsNotice(rb, client.t("Error loading account data"))
374
+		service.Notice(rb, client.t("Error loading account data"))
386 375
 		return
387 376
 	}
388 377
 
389
-	displaySetting(params[0], accountData.Settings, client, rb)
378
+	displaySetting(service, params[0], accountData.Settings, client, rb)
390 379
 }
391 380
 
392
-func displaySetting(settingName string, settings AccountSettings, client *Client, rb *ResponseBuffer) {
381
+func displaySetting(service *ircService, settingName string, settings AccountSettings, client *Client, rb *ResponseBuffer) {
393 382
 	config := client.server.Config()
394 383
 	switch strings.ToLower(settingName) {
395 384
 	case "enforce":
396 385
 		storedValue := settings.NickEnforcement
397 386
 		serializedStoredValue := nickReservationToString(storedValue)
398
-		nsNotice(rb, fmt.Sprintf(client.t("Your stored nickname enforcement setting is: %s"), serializedStoredValue))
387
+		service.Notice(rb, fmt.Sprintf(client.t("Your stored nickname enforcement setting is: %s"), serializedStoredValue))
399 388
 		serializedActualValue := nickReservationToString(configuredEnforcementMethod(config, storedValue))
400
-		nsNotice(rb, fmt.Sprintf(client.t("Given current server settings, your nickname is enforced with: %s"), serializedActualValue))
389
+		service.Notice(rb, fmt.Sprintf(client.t("Given current server settings, your nickname is enforced with: %s"), serializedActualValue))
401 390
 	case "autoreplay-lines":
402 391
 		if settings.AutoreplayLines == nil {
403
-			nsNotice(rb, fmt.Sprintf(client.t("You will receive the server default of %d lines of autoreplayed history"), config.History.AutoreplayOnJoin))
392
+			service.Notice(rb, fmt.Sprintf(client.t("You will receive the server default of %d lines of autoreplayed history"), config.History.AutoreplayOnJoin))
404 393
 		} else {
405
-			nsNotice(rb, fmt.Sprintf(client.t("You will receive %d lines of autoreplayed history"), *settings.AutoreplayLines))
394
+			service.Notice(rb, fmt.Sprintf(client.t("You will receive %d lines of autoreplayed history"), *settings.AutoreplayLines))
406 395
 		}
407 396
 	case "replay-joins":
408 397
 		switch settings.ReplayJoins {
409 398
 		case ReplayJoinsCommandsOnly:
410
-			nsNotice(rb, client.t("You will see JOINs and PARTs in /HISTORY output, but not in autoreplay"))
399
+			service.Notice(rb, client.t("You will see JOINs and PARTs in /HISTORY output, but not in autoreplay"))
411 400
 		case ReplayJoinsAlways:
412
-			nsNotice(rb, client.t("You will see JOINs and PARTs in /HISTORY output and in autoreplay"))
401
+			service.Notice(rb, client.t("You will see JOINs and PARTs in /HISTORY output and in autoreplay"))
413 402
 		case ReplayJoinsNever:
414
-			nsNotice(rb, client.t("You will not see JOINs and PARTs in /HISTORY output or in autoreplay"))
403
+			service.Notice(rb, client.t("You will not see JOINs and PARTs in /HISTORY output or in autoreplay"))
415 404
 		}
416 405
 	case "multiclient":
417 406
 		if !config.Accounts.Multiclient.Enabled {
418
-			nsNotice(rb, client.t("This feature has been disabled by the server administrators"))
407
+			service.Notice(rb, client.t("This feature has been disabled by the server administrators"))
419 408
 		} else {
420 409
 			switch settings.AllowBouncer {
421 410
 			case MulticlientAllowedServerDefault:
422 411
 				if config.Accounts.Multiclient.AllowedByDefault {
423
-					nsNotice(rb, client.t("Multiclient functionality is currently enabled for your account, but you can opt out"))
412
+					service.Notice(rb, client.t("Multiclient functionality is currently enabled for your account, but you can opt out"))
424 413
 				} else {
425
-					nsNotice(rb, client.t("Multiclient functionality is currently disabled for your account, but you can opt in"))
414
+					service.Notice(rb, client.t("Multiclient functionality is currently disabled for your account, but you can opt in"))
426 415
 				}
427 416
 			case MulticlientDisallowedByUser:
428
-				nsNotice(rb, client.t("Multiclient functionality is currently disabled for your account"))
417
+				service.Notice(rb, client.t("Multiclient functionality is currently disabled for your account"))
429 418
 			case MulticlientAllowedByUser:
430
-				nsNotice(rb, client.t("Multiclient functionality is currently enabled for your account"))
419
+				service.Notice(rb, client.t("Multiclient functionality is currently enabled for your account"))
431 420
 			}
432 421
 		}
433 422
 	case "always-on":
434 423
 		stored := settings.AlwaysOn
435 424
 		actual := persistenceEnabled(config.Accounts.Multiclient.AlwaysOn, stored)
436
-		nsNotice(rb, fmt.Sprintf(client.t("Your stored always-on setting is: %s"), persistentStatusToString(stored)))
425
+		service.Notice(rb, fmt.Sprintf(client.t("Your stored always-on setting is: %s"), persistentStatusToString(stored)))
437 426
 		if actual {
438
-			nsNotice(rb, client.t("Given current server settings, your client is always-on"))
427
+			service.Notice(rb, client.t("Given current server settings, your client is always-on"))
439 428
 		} else {
440
-			nsNotice(rb, client.t("Given current server settings, your client is not always-on"))
429
+			service.Notice(rb, client.t("Given current server settings, your client is not always-on"))
441 430
 		}
442 431
 	case "autoreplay-missed":
443 432
 		stored := settings.AutoreplayMissed
444 433
 		if stored {
445 434
 			alwaysOn := persistenceEnabled(config.Accounts.Multiclient.AlwaysOn, settings.AlwaysOn)
446 435
 			if alwaysOn {
447
-				nsNotice(rb, client.t("Autoreplay of missed messages is enabled"))
436
+				service.Notice(rb, client.t("Autoreplay of missed messages is enabled"))
448 437
 			} else {
449
-				nsNotice(rb, client.t("You have enabled autoreplay of missed messages, but you can't receive them because your client isn't set to always-on"))
438
+				service.Notice(rb, client.t("You have enabled autoreplay of missed messages, but you can't receive them because your client isn't set to always-on"))
450 439
 			}
451 440
 		} else {
452
-			nsNotice(rb, client.t("Your account is not configured to receive autoreplayed missed messages"))
441
+			service.Notice(rb, client.t("Your account is not configured to receive autoreplayed missed messages"))
453 442
 		}
454 443
 	case "auto-away":
455 444
 		stored := settings.AutoAway
456 445
 		alwaysOn := persistenceEnabled(config.Accounts.Multiclient.AlwaysOn, settings.AlwaysOn)
457 446
 		actual := persistenceEnabled(config.Accounts.Multiclient.AutoAway, settings.AutoAway)
458
-		nsNotice(rb, fmt.Sprintf(client.t("Your stored auto-away setting is: %s"), persistentStatusToString(stored)))
447
+		service.Notice(rb, fmt.Sprintf(client.t("Your stored auto-away setting is: %s"), persistentStatusToString(stored)))
459 448
 		if actual && alwaysOn {
460
-			nsNotice(rb, client.t("Given current server settings, auto-away is enabled for your client"))
449
+			service.Notice(rb, client.t("Given current server settings, auto-away is enabled for your client"))
461 450
 		} else if actual && !alwaysOn {
462
-			nsNotice(rb, client.t("Because your client is not always-on, auto-away is disabled"))
451
+			service.Notice(rb, client.t("Because your client is not always-on, auto-away is disabled"))
463 452
 		} else if !actual {
464
-			nsNotice(rb, client.t("Given current server settings, auto-away is disabled for your client"))
453
+			service.Notice(rb, client.t("Given current server settings, auto-away is disabled for your client"))
465 454
 		}
466 455
 	case "dm-history":
467 456
 		effectiveValue := historyEnabled(config.History.Persistent.DirectMessages, settings.DMHistory)
468
-		nsNotice(rb, fmt.Sprintf(client.t("Your stored direct message history setting is: %s"), historyStatusToString(settings.DMHistory)))
469
-		nsNotice(rb, fmt.Sprintf(client.t("Given current server settings, your direct message history setting is: %s"), historyStatusToString(effectiveValue)))
457
+		service.Notice(rb, fmt.Sprintf(client.t("Your stored direct message history setting is: %s"), historyStatusToString(settings.DMHistory)))
458
+		service.Notice(rb, fmt.Sprintf(client.t("Given current server settings, your direct message history setting is: %s"), historyStatusToString(effectiveValue)))
470 459
 
471 460
 	default:
472
-		nsNotice(rb, client.t("No such setting"))
461
+		service.Notice(rb, client.t("No such setting"))
473 462
 	}
474 463
 }
475 464
 
476
-func nsSetHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
465
+func nsSetHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
477 466
 	var account string
478 467
 	if command == "saset" {
479 468
 		account = params[0]
@@ -487,7 +476,7 @@ func nsSetHandler(server *Server, client *Client, command string, params []strin
487 476
 	var err error
488 477
 	switch strings.ToLower(params[0]) {
489 478
 	case "pass", "password":
490
-		nsNotice(rb, client.t("To change a password, use the PASSWD command. For details, /msg NickServ HELP PASSWD"))
479
+		service.Notice(rb, client.t("To change a password, use the PASSWD command. For details, /msg NickServ HELP PASSWD"))
491 480
 		return
492 481
 	case "enforce":
493 482
 		var method NickEnforcementMethod
@@ -612,19 +601,19 @@ func nsSetHandler(server *Server, client *Client, command string, params []strin
612 601
 
613 602
 	switch err {
614 603
 	case nil:
615
-		nsNotice(rb, client.t("Successfully changed your account settings"))
616
-		displaySetting(params[0], finalSettings, client, rb)
604
+		service.Notice(rb, client.t("Successfully changed your account settings"))
605
+		displaySetting(service, params[0], finalSettings, client, rb)
617 606
 	case errInvalidParams, errAccountDoesNotExist, errFeatureDisabled, errAccountUnverified, errAccountUpdateFailed:
618
-		nsNotice(rb, client.t(err.Error()))
607
+		service.Notice(rb, client.t(err.Error()))
619 608
 	case errNickAccountMismatch:
620
-		nsNotice(rb, fmt.Sprintf(client.t("Your nickname must match your account name %s exactly to modify this setting. Try changing it with /NICK, or logging out and back in with the correct nickname."), client.AccountName()))
609
+		service.Notice(rb, fmt.Sprintf(client.t("Your nickname must match your account name %s exactly to modify this setting. Try changing it with /NICK, or logging out and back in with the correct nickname."), client.AccountName()))
621 610
 	default:
622 611
 		// unknown error
623
-		nsNotice(rb, client.t("An error occurred"))
612
+		service.Notice(rb, client.t("An error occurred"))
624 613
 	}
625 614
 }
626 615
 
627
-func nsDropHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
616
+func nsDropHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
628 617
 	sadrop := command == "sadrop"
629 618
 	var nick string
630 619
 	if len(params) > 0 {
@@ -635,28 +624,28 @@ func nsDropHandler(server *Server, client *Client, command string, params []stri
635 624
 
636 625
 	err := server.accounts.SetNickReserved(client, nick, sadrop, false)
637 626
 	if err == nil {
638
-		nsNotice(rb, fmt.Sprintf(client.t("Successfully ungrouped nick %s with your account"), nick))
627
+		service.Notice(rb, fmt.Sprintf(client.t("Successfully ungrouped nick %s with your account"), nick))
639 628
 	} else if err == errAccountNotLoggedIn {
640
-		nsNotice(rb, client.t("You're not logged into an account"))
629
+		service.Notice(rb, client.t("You're not logged into an account"))
641 630
 	} else if err == errAccountCantDropPrimaryNick {
642
-		nsNotice(rb, client.t("You can't ungroup your primary nickname (try unregistering your account instead)"))
631
+		service.Notice(rb, client.t("You can't ungroup your primary nickname (try unregistering your account instead)"))
643 632
 	} else {
644
-		nsNotice(rb, client.t("Could not ungroup nick"))
633
+		service.Notice(rb, client.t("Could not ungroup nick"))
645 634
 	}
646 635
 }
647 636
 
648
-func nsGhostHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
637
+func nsGhostHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
649 638
 	nick := params[0]
650 639
 
651 640
 	ghost := server.clients.Get(nick)
652 641
 	if ghost == nil {
653
-		nsNotice(rb, client.t("No such nick"))
642
+		service.Notice(rb, client.t("No such nick"))
654 643
 		return
655 644
 	} else if ghost == client {
656
-		nsNotice(rb, client.t("You can't GHOST yourself (try /QUIT instead)"))
645
+		service.Notice(rb, client.t("You can't GHOST yourself (try /QUIT instead)"))
657 646
 		return
658 647
 	} else if ghost.AlwaysOn() {
659
-		nsNotice(rb, client.t("You can't GHOST an always-on client"))
648
+		service.Notice(rb, client.t("You can't GHOST an always-on client"))
660 649
 		return
661 650
 	}
662 651
 
@@ -667,7 +656,7 @@ func nsGhostHandler(server *Server, client *Client, command string, params []str
667 656
 		authorized = (server.accounts.NickToAccount(nick) == account) || (ghost.Account() == account)
668 657
 	}
669 658
 	if !authorized {
670
-		nsNotice(rb, client.t("You don't own that nick"))
659
+		service.Notice(rb, client.t("You don't own that nick"))
671 660
 		return
672 661
 	}
673 662
 
@@ -675,31 +664,31 @@ func nsGhostHandler(server *Server, client *Client, command string, params []str
675 664
 	ghost.destroy(nil)
676 665
 }
677 666
 
678
-func nsGroupHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
667
+func nsGroupHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
679 668
 	nick := client.Nick()
680 669
 	err := server.accounts.SetNickReserved(client, nick, false, true)
681 670
 	if err == nil {
682
-		nsNotice(rb, fmt.Sprintf(client.t("Successfully grouped nick %s with your account"), nick))
671
+		service.Notice(rb, fmt.Sprintf(client.t("Successfully grouped nick %s with your account"), nick))
683 672
 	} else if err == errAccountTooManyNicks {
684
-		nsNotice(rb, client.t("You have too many nicks reserved already (you can remove some with /NS DROP)"))
673
+		service.Notice(rb, client.t("You have too many nicks reserved already (you can remove some with /NS DROP)"))
685 674
 	} else if err == errNicknameReserved {
686
-		nsNotice(rb, client.t("That nickname is already reserved by someone else"))
675
+		service.Notice(rb, client.t("That nickname is already reserved by someone else"))
687 676
 	} else {
688
-		nsNotice(rb, client.t("Error reserving nickname"))
677
+		service.Notice(rb, client.t("Error reserving nickname"))
689 678
 	}
690 679
 }
691 680
 
692
-func nsLoginThrottleCheck(client *Client, rb *ResponseBuffer) (success bool) {
681
+func nsLoginThrottleCheck(service *ircService, client *Client, rb *ResponseBuffer) (success bool) {
693 682
 	throttled, remainingTime := client.checkLoginThrottle()
694 683
 	if throttled {
695
-		nsNotice(rb, fmt.Sprintf(client.t("Please wait at least %v and try again"), remainingTime))
684
+		service.Notice(rb, fmt.Sprintf(client.t("Please wait at least %v and try again"), remainingTime))
696 685
 	}
697 686
 	return !throttled
698 687
 }
699 688
 
700
-func nsIdentifyHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
689
+func nsIdentifyHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
701 690
 	if client.LoggedIntoAccount() {
702
-		nsNotice(rb, client.t("You're already logged into an account"))
691
+		service.Notice(rb, client.t("You're already logged into an account"))
703 692
 		return
704 693
 	}
705 694
 
@@ -735,7 +724,7 @@ func nsIdentifyHandler(server *Server, client *Client, command string, params []
735 724
 
736 725
 	nickFixupFailed := false
737 726
 	if loginSuccessful {
738
-		if !fixupNickEqualsAccount(client, rb, server.Config()) {
727
+		if !fixupNickEqualsAccount(client, rb, server.Config(), service.prefix) {
739 728
 			loginSuccessful = false
740 729
 			// fixupNickEqualsAccount sends its own error message, don't send another
741 730
 			nickFixupFailed = true
@@ -743,15 +732,15 @@ func nsIdentifyHandler(server *Server, client *Client, command string, params []
743 732
 	}
744 733
 
745 734
 	if loginSuccessful {
746
-		sendSuccessfulAccountAuth(client, rb, true, true)
735
+		sendSuccessfulAccountAuth(service, client, rb, true)
747 736
 	} else if !nickFixupFailed {
748
-		nsNotice(rb, fmt.Sprintf(client.t("Authentication failed: %s"), authErrorToMessage(server, err)))
737
+		service.Notice(rb, fmt.Sprintf(client.t("Authentication failed: %s"), authErrorToMessage(server, err)))
749 738
 	}
750 739
 }
751 740
 
752
-func nsListHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
741
+func nsListHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
753 742
 	if !client.HasRoleCapabs("accreg") {
754
-		nsNotice(rb, client.t("Insufficient privileges"))
743
+		service.Notice(rb, client.t("Insufficient privileges"))
755 744
 		return
756 745
 	}
757 746
 
@@ -760,26 +749,26 @@ func nsListHandler(server *Server, client *Client, command string, params []stri
760 749
 		var err error
761 750
 		searchRegex, err = regexp.Compile(params[0])
762 751
 		if err != nil {
763
-			nsNotice(rb, client.t("Invalid regex"))
752
+			service.Notice(rb, client.t("Invalid regex"))
764 753
 			return
765 754
 		}
766 755
 	}
767 756
 
768
-	nsNotice(rb, ircfmt.Unescape(client.t("*** $bNickServ LIST$b ***")))
757
+	service.Notice(rb, ircfmt.Unescape(client.t("*** $bNickServ LIST$b ***")))
769 758
 
770 759
 	nicks := server.accounts.AllNicks()
771 760
 	for _, nick := range nicks {
772 761
 		if searchRegex == nil || searchRegex.MatchString(nick) {
773
-			nsNotice(rb, fmt.Sprintf("    %s", nick))
762
+			service.Notice(rb, fmt.Sprintf("    %s", nick))
774 763
 		}
775 764
 	}
776 765
 
777
-	nsNotice(rb, ircfmt.Unescape(client.t("*** $bEnd of NickServ LIST$b ***")))
766
+	service.Notice(rb, ircfmt.Unescape(client.t("*** $bEnd of NickServ LIST$b ***")))
778 767
 }
779 768
 
780
-func nsInfoHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
769
+func nsInfoHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
781 770
 	if !server.Config().Accounts.AuthenticationEnabled && !client.HasRoleCapabs("accreg") {
782
-		nsNotice(rb, client.t("This command has been disabled by the server administrators"))
771
+		service.Notice(rb, client.t("This command has been disabled by the server administrators"))
783 772
 		return
784 773
 	}
785 774
 
@@ -789,7 +778,7 @@ func nsInfoHandler(server *Server, client *Client, command string, params []stri
789 778
 		if server.Config().Accounts.NickReservation.Enabled {
790 779
 			accountName = server.accounts.NickToAccount(nick)
791 780
 			if accountName == "" {
792
-				nsNotice(rb, client.t("That nickname is not registered"))
781
+				service.Notice(rb, client.t("That nickname is not registered"))
793 782
 				return
794 783
 			}
795 784
 		} else {
@@ -798,33 +787,33 @@ func nsInfoHandler(server *Server, client *Client, command string, params []stri
798 787
 	} else {
799 788
 		accountName = client.Account()
800 789
 		if accountName == "" {
801
-			nsNotice(rb, client.t("You're not logged into an account"))
790
+			service.Notice(rb, client.t("You're not logged into an account"))
802 791
 			return
803 792
 		}
804 793
 	}
805 794
 
806 795
 	account, err := server.accounts.LoadAccount(accountName)
807 796
 	if err != nil || !account.Verified {
808
-		nsNotice(rb, client.t("Account does not exist"))
797
+		service.Notice(rb, client.t("Account does not exist"))
809 798
 		return
810 799
 	}
811 800
 
812
-	nsNotice(rb, fmt.Sprintf(client.t("Account: %s"), account.Name))
801
+	service.Notice(rb, fmt.Sprintf(client.t("Account: %s"), account.Name))
813 802
 	registeredAt := account.RegisteredAt.Format(time.RFC1123)
814
-	nsNotice(rb, fmt.Sprintf(client.t("Registered at: %s"), registeredAt))
803
+	service.Notice(rb, fmt.Sprintf(client.t("Registered at: %s"), registeredAt))
815 804
 	// TODO nicer formatting for this
816 805
 	for _, nick := range account.AdditionalNicks {
817
-		nsNotice(rb, fmt.Sprintf(client.t("Additional grouped nick: %s"), nick))
806
+		service.Notice(rb, fmt.Sprintf(client.t("Additional grouped nick: %s"), nick))
818 807
 	}
819 808
 	for _, channel := range server.accounts.ChannelsForAccount(accountName) {
820
-		nsNotice(rb, fmt.Sprintf(client.t("Registered channel: %s"), channel))
809
+		service.Notice(rb, fmt.Sprintf(client.t("Registered channel: %s"), channel))
821 810
 	}
822 811
 	if account.Suspended != nil {
823
-		nsNotice(rb, suspensionToString(client, *account.Suspended))
812
+		service.Notice(rb, suspensionToString(client, *account.Suspended))
824 813
 	}
825 814
 }
826 815
 
827
-func nsRegisterHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
816
+func nsRegisterHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
828 817
 	details := client.Details()
829 818
 	passphrase := params[0]
830 819
 	var email string
@@ -835,7 +824,7 @@ func nsRegisterHandler(server *Server, client *Client, command string, params []
835 824
 	certfp := rb.session.certfp
836 825
 	if passphrase == "*" {
837 826
 		if certfp == "" {
838
-			nsNotice(rb, client.t("You must be connected with TLS and a client certificate to do this"))
827
+			service.Notice(rb, client.t("You must be connected with TLS and a client certificate to do this"))
839 828
 			return
840 829
 		} else {
841 830
 			passphrase = ""
@@ -845,12 +834,12 @@ func nsRegisterHandler(server *Server, client *Client, command string, params []
845 834
 	if passphrase != "" {
846 835
 		cfPassphrase, err := Casefold(passphrase)
847 836
 		if err == nil && cfPassphrase == details.nickCasefolded {
848
-			nsNotice(rb, client.t("Usage: REGISTER <passphrase> [email]")) // #1179
837
+			service.Notice(rb, client.t("Usage: REGISTER <passphrase> [email]")) // #1179
849 838
 			return
850 839
 		}
851 840
 	}
852 841
 
853
-	if !nsLoginThrottleCheck(client, rb) {
842
+	if !nsLoginThrottleCheck(service, client, rb) {
854 843
 		return
855 844
 	}
856 845
 
@@ -859,7 +848,7 @@ func nsRegisterHandler(server *Server, client *Client, command string, params []
859 848
 	if config.Accounts.NickReservation.ForceGuestFormat {
860 849
 		matches := config.Accounts.NickReservation.guestRegexp.FindStringSubmatch(account)
861 850
 		if matches == nil || len(matches) < 2 {
862
-			nsNotice(rb, client.t("Erroneous nickname"))
851
+			service.Notice(rb, client.t("Erroneous nickname"))
863 852
 			return
864 853
 		}
865 854
 		account = matches[1]
@@ -867,7 +856,7 @@ func nsRegisterHandler(server *Server, client *Client, command string, params []
867 856
 
868 857
 	callbackNamespace, callbackValue, validationErr := parseCallback(email, config)
869 858
 	if validationErr != nil {
870
-		nsNotice(rb, client.t("Registration requires a valid e-mail address"))
859
+		service.Notice(rb, client.t("Registration requires a valid e-mail address"))
871 860
 		return
872 861
 	}
873 862
 
@@ -875,22 +864,22 @@ func nsRegisterHandler(server *Server, client *Client, command string, params []
875 864
 	if err == nil {
876 865
 		if callbackNamespace == "*" {
877 866
 			err = server.accounts.Verify(client, account, "")
878
-			if err == nil && fixupNickEqualsAccount(client, rb, config) {
879
-				sendSuccessfulRegResponse(client, rb, true)
867
+			if err == nil && fixupNickEqualsAccount(client, rb, config, service.prefix) {
868
+				sendSuccessfulRegResponse(service, client, rb)
880 869
 			}
881 870
 		} else {
882 871
 			messageTemplate := client.t("Account created, pending verification; verification code has been sent to %s")
883 872
 			message := fmt.Sprintf(messageTemplate, callbackValue)
884
-			nsNotice(rb, message)
873
+			service.Notice(rb, message)
885 874
 		}
886 875
 	} else {
887 876
 		// details could not be stored and relevant numerics have been dispatched, abort
888 877
 		message := registrationErrorToMessage(err)
889
-		nsNotice(rb, client.t(message))
878
+		service.Notice(rb, client.t(message))
890 879
 	}
891 880
 }
892 881
 
893
-func nsSaregisterHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
882
+func nsSaregisterHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
894 883
 	var account, passphrase string
895 884
 	account = params[0]
896 885
 	if 1 < len(params) && params[1] != "*" {
@@ -908,14 +897,14 @@ func nsSaregisterHandler(server *Server, client *Client, command string, params
908 897
 			server.logger.Error("services", "unknown error from saregister", err.Error())
909 898
 			errMsg = client.t("Could not register")
910 899
 		}
911
-		nsNotice(rb, errMsg)
900
+		service.Notice(rb, errMsg)
912 901
 	} else {
913
-		nsNotice(rb, fmt.Sprintf(client.t("Successfully registered account %s"), account))
902
+		service.Notice(rb, fmt.Sprintf(client.t("Successfully registered account %s"), account))
914 903
 		server.snomasks.Send(sno.LocalAccounts, fmt.Sprintf(ircfmt.Unescape("Operator $c[grey][$r%s$c[grey]] registered account $c[grey][$r%s$c[grey]] with SAREGISTER"), client.Oper().Name, account))
915 904
 	}
916 905
 }
917 906
 
918
-func nsUnregisterHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
907
+func nsUnregisterHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
919 908
 	erase := command == "erase"
920 909
 
921 910
 	username := params[0]
@@ -925,7 +914,7 @@ func nsUnregisterHandler(server *Server, client *Client, command string, params
925 914
 	}
926 915
 
927 916
 	if username == "" {
928
-		nsNotice(rb, client.t("You must specify an account"))
917
+		service.Notice(rb, client.t("You must specify an account"))
929 918
 		return
930 919
 	}
931 920
 
@@ -939,10 +928,10 @@ func nsUnregisterHandler(server *Server, client *Client, command string, params
939 928
 	} else {
940 929
 		account, err := server.accounts.LoadAccount(username)
941 930
 		if err == errAccountDoesNotExist {
942
-			nsNotice(rb, client.t("Invalid account name"))
931
+			service.Notice(rb, client.t("Invalid account name"))
943 932
 			return
944 933
 		} else if err != nil {
945
-			nsNotice(rb, client.t("Internal error"))
934
+			service.Notice(rb, client.t("Internal error"))
946 935
 			return
947 936
 		}
948 937
 		accountName = account.Name
@@ -950,34 +939,34 @@ func nsUnregisterHandler(server *Server, client *Client, command string, params
950 939
 	}
951 940
 
952 941
 	if !(accountName == client.AccountName() || client.HasRoleCapabs("accreg")) {
953
-		nsNotice(rb, client.t("Insufficient oper privs"))
942
+		service.Notice(rb, client.t("Insufficient oper privs"))
954 943
 		return
955 944
 	}
956 945
 
957 946
 	expectedCode := utils.ConfirmationCode(accountName, registeredAt)
958 947
 	if expectedCode != verificationCode {
959 948
 		if erase {
960
-			nsNotice(rb, ircfmt.Unescape(client.t("$bWarning: erasing this account will allow it to be re-registered; consider UNREGISTER instead.$b")))
949
+			service.Notice(rb, ircfmt.Unescape(client.t("$bWarning: erasing this account will allow it to be re-registered; consider UNREGISTER instead.$b")))
961 950
 		} else {
962
-			nsNotice(rb, ircfmt.Unescape(client.t("$bWarning: unregistering this account will remove its stored privileges.$b")))
951
+			service.Notice(rb, ircfmt.Unescape(client.t("$bWarning: unregistering this account will remove its stored privileges.$b")))
963 952
 		}
964
-		nsNotice(rb, fmt.Sprintf(client.t("To confirm, run this command: %s"), fmt.Sprintf("/NS %s %s %s", strings.ToUpper(command), accountName, expectedCode)))
953
+		service.Notice(rb, fmt.Sprintf(client.t("To confirm, run this command: %s"), fmt.Sprintf("/NS %s %s %s", strings.ToUpper(command), accountName, expectedCode)))
965 954
 		return
966 955
 	}
967 956
 
968 957
 	err := server.accounts.Unregister(accountName, erase)
969 958
 	if err == errAccountDoesNotExist {
970
-		nsNotice(rb, client.t(err.Error()))
959
+		service.Notice(rb, client.t(err.Error()))
971 960
 	} else if err != nil {
972
-		nsNotice(rb, client.t("Error while unregistering account"))
961
+		service.Notice(rb, client.t("Error while unregistering account"))
973 962
 	} else {
974
-		nsNotice(rb, fmt.Sprintf(client.t("Successfully unregistered account %s"), accountName))
963
+		service.Notice(rb, fmt.Sprintf(client.t("Successfully unregistered account %s"), accountName))
975 964
 		server.logger.Info("accounts", "client", client.Nick(), "unregistered account", accountName)
976 965
 		client.server.snomasks.Send(sno.LocalAccounts, fmt.Sprintf(ircfmt.Unescape("Client $c[grey][$r%s$c[grey]] unregistered account $c[grey][$r%s$c[grey]]"), client.NickMaskString(), accountName))
977 966
 	}
978 967
 }
979 968
 
980
-func nsVerifyHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
969
+func nsVerifyHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
981 970
 	username, code := params[0], params[1]
982 971
 	err := server.accounts.Verify(client, username, code)
983 972
 
@@ -992,16 +981,16 @@ func nsVerifyHandler(server *Server, client *Client, command string, params []st
992 981
 	}
993 982
 
994 983
 	if errorMessage != "" {
995
-		nsNotice(rb, client.t(errorMessage))
984
+		service.Notice(rb, client.t(errorMessage))
996 985
 		return
997 986
 	}
998 987
 
999
-	if fixupNickEqualsAccount(client, rb, server.Config()) {
1000
-		sendSuccessfulRegResponse(client, rb, true)
988
+	if fixupNickEqualsAccount(client, rb, server.Config(), service.prefix) {
989
+		sendSuccessfulRegResponse(service, client, rb)
1001 990
 	}
1002 991
 }
1003 992
 
1004
-func nsPasswdHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
993
+func nsPasswdHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
1005 994
 	var target string
1006 995
 	var newPassword string
1007 996
 	var errorMessage string
@@ -1025,7 +1014,7 @@ func nsPasswdHandler(server *Server, client *Client, command string, params []st
1025 1014
 		} else if params[1] != params[2] {
1026 1015
 			errorMessage = `Passwords do not match`
1027 1016
 		} else {
1028
-			if !nsLoginThrottleCheck(client, rb) {
1017
+			if !nsLoginThrottleCheck(service, client, rb) {
1029 1018
 				return
1030 1019
 			}
1031 1020
 			accountData, err := server.accounts.LoadAccount(target)
@@ -1048,37 +1037,37 @@ func nsPasswdHandler(server *Server, client *Client, command string, params []st
1048 1037
 	}
1049 1038
 
1050 1039
 	if errorMessage != "" {
1051
-		nsNotice(rb, client.t(errorMessage))
1040
+		service.Notice(rb, client.t(errorMessage))
1052 1041
 		return
1053 1042
 	}
1054 1043
 
1055 1044
 	err := server.accounts.setPassword(target, newPassword, hasPrivs)
1056 1045
 	switch err {
1057 1046
 	case nil:
1058
-		nsNotice(rb, client.t("Password changed"))
1047
+		service.Notice(rb, client.t("Password changed"))
1059 1048
 	case errEmptyCredentials:
1060
-		nsNotice(rb, client.t("You can't delete your password unless you add a certificate fingerprint"))
1049
+		service.Notice(rb, client.t("You can't delete your password unless you add a certificate fingerprint"))
1061 1050
 	case errCredsExternallyManaged:
1062
-		nsNotice(rb, client.t("Your account credentials are managed externally and cannot be changed here"))
1051
+		service.Notice(rb, client.t("Your account credentials are managed externally and cannot be changed here"))
1063 1052
 	case errCASFailed:
1064
-		nsNotice(rb, client.t("Try again later"))
1053
+		service.Notice(rb, client.t("Try again later"))
1065 1054
 	default:
1066 1055
 		server.logger.Error("internal", "could not upgrade user password:", err.Error())
1067
-		nsNotice(rb, client.t("Password could not be changed due to server error"))
1056
+		service.Notice(rb, client.t("Password could not be changed due to server error"))
1068 1057
 	}
1069 1058
 }
1070 1059
 
1071
-func nsEnforceHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
1060
+func nsEnforceHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
1072 1061
 	newParams := []string{"enforce"}
1073 1062
 	if len(params) == 0 {
1074
-		nsGetHandler(server, client, "get", newParams, rb)
1063
+		nsGetHandler(service, server, client, "get", newParams, rb)
1075 1064
 	} else {
1076 1065
 		newParams = append(newParams, params[0])
1077
-		nsSetHandler(server, client, "set", newParams, rb)
1066
+		nsSetHandler(service, server, client, "set", newParams, rb)
1078 1067
 	}
1079 1068
 }
1080 1069
 
1081
-func nsClientsHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
1070
+func nsClientsHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
1082 1071
 	var verb string
1083 1072
 
1084 1073
 	if command == "sessions" {
@@ -1091,56 +1080,56 @@ func nsClientsHandler(server *Server, client *Client, command string, params []s
1091 1080
 
1092 1081
 	switch verb {
1093 1082
 	case "list":
1094
-		nsClientsListHandler(server, client, params, rb)
1083
+		nsClientsListHandler(service, server, client, params, rb)
1095 1084
 	case "logout":
1096
-		nsClientsLogoutHandler(server, client, params, rb)
1085
+		nsClientsLogoutHandler(service, server, client, params, rb)
1097 1086
 	default:
1098
-		nsNotice(rb, client.t("Invalid parameters"))
1087
+		service.Notice(rb, client.t("Invalid parameters"))
1099 1088
 	}
1100 1089
 }
1101 1090
 
1102
-func nsClientsListHandler(server *Server, client *Client, params []string, rb *ResponseBuffer) {
1091
+func nsClientsListHandler(service *ircService, server *Server, client *Client, params []string, rb *ResponseBuffer) {
1103 1092
 	target := client
1104 1093
 	hasPrivs := client.HasRoleCapabs("local_ban")
1105 1094
 	if 0 < len(params) {
1106 1095
 		target = server.clients.Get(params[0])
1107 1096
 		if target == nil {
1108
-			nsNotice(rb, client.t("No such nick"))
1097
+			service.Notice(rb, client.t("No such nick"))
1109 1098
 			return
1110 1099
 		}
1111 1100
 		if target != client && !hasPrivs {
1112
-			nsNotice(rb, client.t("Command restricted"))
1101
+			service.Notice(rb, client.t("Command restricted"))
1113 1102
 			return
1114 1103
 		}
1115 1104
 	}
1116 1105
 
1117 1106
 	sessionData, currentIndex := target.AllSessionData(rb.session, hasPrivs)
1118
-	nsNotice(rb, fmt.Sprintf(client.t("Nickname %[1]s has %[2]d attached clients(s)"), target.Nick(), len(sessionData)))
1107
+	service.Notice(rb, fmt.Sprintf(client.t("Nickname %[1]s has %[2]d attached clients(s)"), target.Nick(), len(sessionData)))
1119 1108
 	for i, session := range sessionData {
1120 1109
 		if currentIndex == i {
1121
-			nsNotice(rb, fmt.Sprintf(client.t("Client %d (currently attached client):"), session.sessionID))
1110
+			service.Notice(rb, fmt.Sprintf(client.t("Client %d (currently attached client):"), session.sessionID))
1122 1111
 		} else {
1123
-			nsNotice(rb, fmt.Sprintf(client.t("Client %d:"), session.sessionID))
1112
+			service.Notice(rb, fmt.Sprintf(client.t("Client %d:"), session.sessionID))
1124 1113
 		}
1125 1114
 		if session.deviceID != "" {
1126
-			nsNotice(rb, fmt.Sprintf(client.t("Device ID:   %s"), session.deviceID))
1115
+			service.Notice(rb, fmt.Sprintf(client.t("Device ID:   %s"), session.deviceID))
1127 1116
 		}
1128
-		nsNotice(rb, fmt.Sprintf(client.t("IP address:  %s"), session.ip.String()))
1129
-		nsNotice(rb, fmt.Sprintf(client.t("Hostname:    %s"), session.hostname))
1117
+		service.Notice(rb, fmt.Sprintf(client.t("IP address:  %s"), session.ip.String()))
1118
+		service.Notice(rb, fmt.Sprintf(client.t("Hostname:    %s"), session.hostname))
1130 1119
 		if hasPrivs {
1131
-			nsNotice(rb, fmt.Sprintf(client.t("Connection:  %s"), session.connInfo))
1120
+			service.Notice(rb, fmt.Sprintf(client.t("Connection:  %s"), session.connInfo))
1132 1121
 		}
1133
-		nsNotice(rb, fmt.Sprintf(client.t("Created at:  %s"), session.ctime.Format(time.RFC1123)))
1134
-		nsNotice(rb, fmt.Sprintf(client.t("Last active: %s"), session.atime.Format(time.RFC1123)))
1122
+		service.Notice(rb, fmt.Sprintf(client.t("Created at:  %s"), session.ctime.Format(time.RFC1123)))
1123
+		service.Notice(rb, fmt.Sprintf(client.t("Last active: %s"), session.atime.Format(time.RFC1123)))
1135 1124
 		if session.certfp != "" {
1136
-			nsNotice(rb, fmt.Sprintf(client.t("Certfp:      %s"), session.certfp))
1125
+			service.Notice(rb, fmt.Sprintf(client.t("Certfp:      %s"), session.certfp))
1137 1126
 		}
1138 1127
 	}
1139 1128
 }
1140 1129
 
1141
-func nsClientsLogoutHandler(server *Server, client *Client, params []string, rb *ResponseBuffer) {
1130
+func nsClientsLogoutHandler(service *ircService, server *Server, client *Client, params []string, rb *ResponseBuffer) {
1142 1131
 	if len(params) < 1 {
1143
-		nsNotice(rb, client.t("Missing client ID to logout (or \"all\")"))
1132
+		service.Notice(rb, client.t("Missing client ID to logout (or \"all\")"))
1144 1133
 		return
1145 1134
 	}
1146 1135
 
@@ -1149,14 +1138,14 @@ func nsClientsLogoutHandler(server *Server, client *Client, params []string, rb
1149 1138
 		// CLIENTS LOGOUT [nickname] [client ID]
1150 1139
 		target = server.clients.Get(params[0])
1151 1140
 		if target == nil {
1152
-			nsNotice(rb, client.t("No such nick"))
1141
+			service.Notice(rb, client.t("No such nick"))
1153 1142
 			return
1154 1143
 		}
1155 1144
 		// User must have "local_kill" privileges to logout other user sessions.
1156 1145
 		if target != client {
1157 1146
 			oper := client.Oper()
1158 1147
 			if oper == nil || !oper.Class.Capabilities.Has("local_kill") {
1159
-				nsNotice(rb, client.t("Insufficient oper privs"))
1148
+				service.Notice(rb, client.t("Insufficient oper privs"))
1160 1149
 				return
1161 1150
 			}
1162 1151
 		}
@@ -1167,7 +1156,7 @@ func nsClientsLogoutHandler(server *Server, client *Client, params []string, rb
1167 1156
 	if strings.ToLower(params[0]) != "all" {
1168 1157
 		sessionID, err := strconv.ParseInt(params[0], 10, 64)
1169 1158
 		if err != nil {
1170
-			nsNotice(rb, client.t("Client ID to logout should be an integer (or \"all\")"))
1159
+			service.Notice(rb, client.t("Client ID to logout should be an integer (or \"all\")"))
1171 1160
 			return
1172 1161
 		}
1173 1162
 		// Find the client ID that the user requested to logout.
@@ -1178,7 +1167,7 @@ func nsClientsLogoutHandler(server *Server, client *Client, params []string, rb
1178 1167
 			}
1179 1168
 		}
1180 1169
 		if sessionToDestroy == nil {
1181
-			nsNotice(rb, client.t("Specified client ID does not exist"))
1170
+			service.Notice(rb, client.t("Specified client ID does not exist"))
1182 1171
 			return
1183 1172
 		}
1184 1173
 	}
@@ -1186,14 +1175,14 @@ func nsClientsLogoutHandler(server *Server, client *Client, params []string, rb
1186 1175
 	target.destroy(sessionToDestroy)
1187 1176
 	if (sessionToDestroy != nil && rb.session != sessionToDestroy) || client != target {
1188 1177
 		if sessionToDestroy != nil {
1189
-			nsNotice(rb, client.t("Successfully logged out session"))
1178
+			service.Notice(rb, client.t("Successfully logged out session"))
1190 1179
 		} else {
1191
-			nsNotice(rb, client.t("Successfully logged out all sessions"))
1180
+			service.Notice(rb, client.t("Successfully logged out all sessions"))
1192 1181
 		}
1193 1182
 	}
1194 1183
 }
1195 1184
 
1196
-func nsCertHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
1185
+func nsCertHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
1197 1186
 	verb := strings.ToLower(params[0])
1198 1187
 	params = params[1:]
1199 1188
 	var target, certfp string
@@ -1211,22 +1200,22 @@ func nsCertHandler(server *Server, client *Client, command string, params []stri
1211 1200
 		} else if len(params) == 0 && verb == "add" && rb.session.certfp != "" {
1212 1201
 			certfp = rb.session.certfp // #1059
1213 1202
 		} else {
1214
-			nsNotice(rb, client.t("Invalid parameters"))
1203
+			service.Notice(rb, client.t("Invalid parameters"))
1215 1204
 			return
1216 1205
 		}
1217 1206
 	default:
1218
-		nsNotice(rb, client.t("Invalid parameters"))
1207
+		service.Notice(rb, client.t("Invalid parameters"))
1219 1208
 		return
1220 1209
 	}
1221 1210
 
1222 1211
 	hasPrivs := client.HasRoleCapabs("accreg")
1223 1212
 	if target != "" && !hasPrivs {
1224
-		nsNotice(rb, client.t("Insufficient privileges"))
1213
+		service.Notice(rb, client.t("Insufficient privileges"))
1225 1214
 		return
1226 1215
 	} else if target == "" {
1227 1216
 		target = client.Account()
1228 1217
 		if target == "" {
1229
-			nsNotice(rb, client.t("You're not logged into an account"))
1218
+			service.Notice(rb, client.t("You're not logged into an account"))
1230 1219
 			return
1231 1220
 		}
1232 1221
 	}
@@ -1236,16 +1225,16 @@ func nsCertHandler(server *Server, client *Client, command string, params []stri
1236 1225
 	case "list":
1237 1226
 		accountData, err := server.accounts.LoadAccount(target)
1238 1227
 		if err == errAccountDoesNotExist {
1239
-			nsNotice(rb, client.t("Account does not exist"))
1228
+			service.Notice(rb, client.t("Account does not exist"))
1240 1229
 			return
1241 1230
 		} else if err != nil {
1242
-			nsNotice(rb, client.t("An error occurred"))
1231
+			service.Notice(rb, client.t("An error occurred"))
1243 1232
 			return
1244 1233
 		}
1245 1234
 		certfps := accountData.Credentials.Certfps
1246
-		nsNotice(rb, fmt.Sprintf(client.t("There are %[1]d certificate fingerprint(s) authorized for account %[2]s."), len(certfps), accountData.Name))
1235
+		service.Notice(rb, fmt.Sprintf(client.t("There are %[1]d certificate fingerprint(s) authorized for account %[2]s."), len(certfps), accountData.Name))
1247 1236
 		for i, certfp := range certfps {
1248
-			nsNotice(rb, fmt.Sprintf("%d: %s", i+1, certfp))
1237
+			service.Notice(rb, fmt.Sprintf("%d: %s", i+1, certfp))
1249 1238
 		}
1250 1239
 		return
1251 1240
 	case "add":
@@ -1257,54 +1246,54 @@ func nsCertHandler(server *Server, client *Client, command string, params []stri
1257 1246
 	switch err {
1258 1247
 	case nil:
1259 1248
 		if verb == "add" {
1260
-			nsNotice(rb, client.t("Certificate fingerprint successfully added"))
1249
+			service.Notice(rb, client.t("Certificate fingerprint successfully added"))
1261 1250
 		} else {
1262
-			nsNotice(rb, client.t("Certificate fingerprint successfully removed"))
1251
+			service.Notice(rb, client.t("Certificate fingerprint successfully removed"))
1263 1252
 		}
1264 1253
 	case errNoop:
1265 1254
 		if verb == "add" {
1266
-			nsNotice(rb, client.t("That certificate fingerprint was already authorized"))
1255
+			service.Notice(rb, client.t("That certificate fingerprint was already authorized"))
1267 1256
 		} else {
1268
-			nsNotice(rb, client.t("Certificate fingerprint not found"))
1257
+			service.Notice(rb, client.t("Certificate fingerprint not found"))
1269 1258
 		}
1270 1259
 	case errAccountDoesNotExist:
1271
-		nsNotice(rb, client.t("Account does not exist"))
1260
+		service.Notice(rb, client.t("Account does not exist"))
1272 1261
 	case errLimitExceeded:
1273
-		nsNotice(rb, client.t("You already have too many certificate fingerprints"))
1262
+		service.Notice(rb, client.t("You already have too many certificate fingerprints"))
1274 1263
 	case utils.ErrInvalidCertfp:
1275
-		nsNotice(rb, client.t("Invalid certificate fingerprint"))
1264
+		service.Notice(rb, client.t("Invalid certificate fingerprint"))
1276 1265
 	case errCertfpAlreadyExists:
1277
-		nsNotice(rb, client.t("That certificate fingerprint is already associated with another account"))
1266
+		service.Notice(rb, client.t("That certificate fingerprint is already associated with another account"))
1278 1267
 	case errEmptyCredentials:
1279
-		nsNotice(rb, client.t("You can't remove all your certificate fingerprints unless you add a password"))
1268
+		service.Notice(rb, client.t("You can't remove all your certificate fingerprints unless you add a password"))
1280 1269
 	case errCredsExternallyManaged:
1281
-		nsNotice(rb, client.t("Your account credentials are managed externally and cannot be changed here"))
1270
+		service.Notice(rb, client.t("Your account credentials are managed externally and cannot be changed here"))
1282 1271
 	case errCASFailed:
1283
-		nsNotice(rb, client.t("Try again later"))
1272
+		service.Notice(rb, client.t("Try again later"))
1284 1273
 	default:
1285 1274
 		server.logger.Error("internal", "could not modify certificates:", err.Error())
1286
-		nsNotice(rb, client.t("An error occurred"))
1275
+		service.Notice(rb, client.t("An error occurred"))
1287 1276
 	}
1288 1277
 }
1289 1278
 
1290
-func nsSuspendHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
1279
+func nsSuspendHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
1291 1280
 	subCmd := strings.ToLower(params[0])
1292 1281
 	params = params[1:]
1293 1282
 	switch subCmd {
1294 1283
 	case "add":
1295
-		nsSuspendAddHandler(server, client, command, params, rb)
1284
+		nsSuspendAddHandler(service, server, client, command, params, rb)
1296 1285
 	case "del", "delete", "remove":
1297
-		nsSuspendRemoveHandler(server, client, command, params, rb)
1286
+		nsSuspendRemoveHandler(service, server, client, command, params, rb)
1298 1287
 	case "list":
1299
-		nsSuspendListHandler(server, client, command, params, rb)
1288
+		nsSuspendListHandler(service, server, client, command, params, rb)
1300 1289
 	default:
1301
-		nsNotice(rb, client.t("Invalid parameters"))
1290
+		service.Notice(rb, client.t("Invalid parameters"))
1302 1291
 	}
1303 1292
 }
1304 1293
 
1305
-func nsSuspendAddHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
1294
+func nsSuspendAddHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
1306 1295
 	if len(params) == 0 {
1307
-		nsNotice(rb, client.t("Invalid parameters"))
1296
+		service.Notice(rb, client.t("Invalid parameters"))
1308 1297
 		return
1309 1298
 	}
1310 1299
 
@@ -1316,7 +1305,7 @@ func nsSuspendAddHandler(server *Server, client *Client, command string, params
1316 1305
 		var err error
1317 1306
 		cDuration, err := custime.ParseDuration(params[1])
1318 1307
 		if err != nil {
1319
-			nsNotice(rb, client.t("Invalid time duration for NS SUSPEND"))
1308
+			service.Notice(rb, client.t("Invalid time duration for NS SUSPEND"))
1320 1309
 			return
1321 1310
 		}
1322 1311
 		duration = time.Duration(cDuration)
@@ -1333,30 +1322,30 @@ func nsSuspendAddHandler(server *Server, client *Client, command string, params
1333 1322
 	err := server.accounts.Suspend(account, duration, name, reason)
1334 1323
 	switch err {
1335 1324
 	case nil:
1336
-		nsNotice(rb, fmt.Sprintf(client.t("Successfully suspended account %s"), account))
1325
+		service.Notice(rb, fmt.Sprintf(client.t("Successfully suspended account %s"), account))
1337 1326
 	case errAccountDoesNotExist:
1338
-		nsNotice(rb, client.t("No such account"))
1327
+		service.Notice(rb, client.t("No such account"))
1339 1328
 	default:
1340
-		nsNotice(rb, client.t("An error occurred"))
1329
+		service.Notice(rb, client.t("An error occurred"))
1341 1330
 	}
1342 1331
 }
1343 1332
 
1344
-func nsSuspendRemoveHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
1333
+func nsSuspendRemoveHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
1345 1334
 	if len(params) == 0 {
1346
-		nsNotice(rb, client.t("Invalid parameters"))
1335
+		service.Notice(rb, client.t("Invalid parameters"))
1347 1336
 		return
1348 1337
 	}
1349 1338
 
1350 1339
 	err := server.accounts.Unsuspend(params[0])
1351 1340
 	switch err {
1352 1341
 	case nil:
1353
-		nsNotice(rb, fmt.Sprintf(client.t("Successfully un-suspended account %s"), params[0]))
1342
+		service.Notice(rb, fmt.Sprintf(client.t("Successfully un-suspended account %s"), params[0]))
1354 1343
 	case errAccountDoesNotExist:
1355
-		nsNotice(rb, client.t("No such account"))
1344
+		service.Notice(rb, client.t("No such account"))
1356 1345
 	case errNoop:
1357
-		nsNotice(rb, client.t("Account was not suspended"))
1346
+		service.Notice(rb, client.t("Account was not suspended"))
1358 1347
 	default:
1359
-		nsNotice(rb, client.t("An error occurred"))
1348
+		service.Notice(rb, client.t("An error occurred"))
1360 1349
 	}
1361 1350
 }
1362 1351
 
@@ -1367,12 +1356,12 @@ func (a ByCreationTime) Len() int           { return len(a) }
1367 1356
 func (a ByCreationTime) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
1368 1357
 func (a ByCreationTime) Less(i, j int) bool { return a[i].TimeCreated.After(a[j].TimeCreated) }
1369 1358
 
1370
-func nsSuspendListHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
1359
+func nsSuspendListHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
1371 1360
 	suspensions := server.accounts.ListSuspended()
1372 1361
 	sort.Sort(ByCreationTime(suspensions))
1373
-	nsNotice(rb, fmt.Sprintf(client.t("There are %d active suspensions."), len(suspensions)))
1362
+	service.Notice(rb, fmt.Sprintf(client.t("There are %d active suspensions."), len(suspensions)))
1374 1363
 	for _, suspension := range suspensions {
1375
-		nsNotice(rb, suspensionToString(client, suspension))
1364
+		service.Notice(rb, suspensionToString(client, suspension))
1376 1365
 	}
1377 1366
 }
1378 1367
 
@@ -1389,21 +1378,21 @@ func suspensionToString(client *Client, suspension AccountSuspension) (result st
1389 1378
 	return fmt.Sprintf(client.t("Account %[1]s suspended at %[2]s. Duration: %[3]s. %[4]s"), suspension.AccountName, ts, duration, reason)
1390 1379
 }
1391 1380
 
1392
-func nsRenameHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
1381
+func nsRenameHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
1393 1382
 	oldName, newName := params[0], params[1]
1394 1383
 	err := server.accounts.Rename(oldName, newName)
1395 1384
 
1396 1385
 	if err != nil {
1397
-		nsNotice(rb, fmt.Sprintf(client.t("Couldn't rename account: %s"), client.t(err.Error())))
1386
+		service.Notice(rb, fmt.Sprintf(client.t("Couldn't rename account: %s"), client.t(err.Error())))
1398 1387
 		return
1399 1388
 	}
1400 1389
 
1401
-	nsNotice(rb, client.t("Successfully renamed account"))
1390
+	service.Notice(rb, client.t("Successfully renamed account"))
1402 1391
 	if server.Config().Accounts.NickReservation.ForceNickEqualsAccount {
1403 1392
 		if curClient := server.clients.Get(oldName); curClient != nil {
1404 1393
 			renameErr := performNickChange(client.server, client, curClient, nil, newName, rb)
1405 1394
 			if renameErr != nil && renameErr != errNoop {
1406
-				nsNotice(rb, fmt.Sprintf(client.t("Warning: could not rename affected client: %v"), err))
1395
+				service.Notice(rb, fmt.Sprintf(client.t("Warning: could not rename affected client: %v"), err))
1407 1396
 			}
1408 1397
 		}
1409 1398
 	}

+ 24
- 13
irc/services.go Visa fil

@@ -20,7 +20,7 @@ import (
20 20
 type ircService struct {
21 21
 	Name           string
22 22
 	ShortName      string
23
-	prefix         string
23
+	prefix         string // NUH source of messages from this service
24 24
 	CommandAliases []string
25 25
 	Commands       map[string]*serviceCommand
26 26
 	HelpBanner     string
@@ -30,7 +30,7 @@ type ircService struct {
30 30
 type serviceCommand struct {
31 31
 	aliasOf           string   // marks this command as an alias of another
32 32
 	capabs            []string // oper capabs the given user has to have to access this command
33
-	handler           func(server *Server, client *Client, command string, params []string, rb *ResponseBuffer)
33
+	handler           func(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer)
34 34
 	help              string
35 35
 	helpStrings       []string
36 36
 	helpShort         string
@@ -60,36 +60,47 @@ func lookupServiceCommand(commands map[string]*serviceCommand, command string) *
60 60
 	return nil
61 61
 }
62 62
 
63
-// all services, by lowercase name
64
-var OragonoServices = map[string]*ircService{
65
-	"nickserv": {
63
+var (
64
+	nickservService = &ircService{
66 65
 		Name:           "NickServ",
67 66
 		ShortName:      "NS",
68 67
 		CommandAliases: []string{"NICKSERV", "NS"},
69 68
 		Commands:       nickservCommands,
70 69
 		HelpBanner:     nickservHelp,
71
-	},
72
-	"chanserv": {
70
+	}
71
+	chanservService = &ircService{
73 72
 		Name:           "ChanServ",
74 73
 		ShortName:      "CS",
75 74
 		CommandAliases: []string{"CHANSERV", "CS"},
76 75
 		Commands:       chanservCommands,
77 76
 		HelpBanner:     chanservHelp,
78
-	},
79
-	"hostserv": {
77
+	}
78
+	hostservService = &ircService{
80 79
 		Name:           "HostServ",
81 80
 		ShortName:      "HS",
82 81
 		CommandAliases: []string{"HOSTSERV", "HS"},
83 82
 		Commands:       hostservCommands,
84 83
 		HelpBanner:     hostservHelp,
85
-	},
86
-	"histserv": {
84
+	}
85
+	histservService = &ircService{
87 86
 		Name:           "HistServ",
88 87
 		ShortName:      "HISTSERV",
89 88
 		CommandAliases: []string{"HISTSERV"},
90 89
 		Commands:       histservCommands,
91 90
 		HelpBanner:     histservHelp,
92
-	},
91
+	}
92
+)
93
+
94
+// all services, by lowercase name
95
+var OragonoServices = map[string]*ircService{
96
+	"nickserv": nickservService,
97
+	"chanserv": chanservService,
98
+	"hostserv": hostservService,
99
+	"histserv": histservService,
100
+}
101
+
102
+func (service *ircService) Notice(rb *ResponseBuffer, text string) {
103
+	rb.Add(nil, service.prefix, "NOTICE", rb.target.Nick(), text)
93 104
 }
94 105
 
95 106
 // all service commands at the protocol level, by uppercase command name
@@ -212,7 +223,7 @@ func serviceRunCommand(service *ircService, server *Server, client *Client, cmd
212 223
 	if commandName == "help" {
213 224
 		serviceHelpHandler(service, server, client, params, rb)
214 225
 	} else {
215
-		cmd.handler(server, client, commandName, params, rb)
226
+		cmd.handler(service, server, client, commandName, params, rb)
216 227
 	}
217 228
 }
218 229
 

Laddar…
Avbryt
Spara