Browse Source

refactor services prefixes and notice handlers

tags/v2.5.0-rc1
Shivaram Lingamneni 3 years ago
parent
commit
9214d978d0
8 changed files with 386 additions and 401 deletions
  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 View File

1037
 	}
1037
 	}
1038
 	if !complete && !session.resumeDetails.HistoryIncomplete {
1038
 	if !complete && !session.resumeDetails.HistoryIncomplete {
1039
 		// warn here if we didn't warn already
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
 	rb.Send(true)
1042
 	rb.Send(true)
1043
 }
1043
 }
1099
 				} else {
1099
 				} else {
1100
 					message = fmt.Sprintf(client.t("%[1]s [account: %[2]s] joined the channel"), nick, item.AccountName)
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
 		case history.Part:
1104
 		case history.Part:
1105
 			if eventPlayback {
1105
 			if eventPlayback {
1109
 					continue // #474
1109
 					continue // #474
1110
 				}
1110
 				}
1111
 				message := fmt.Sprintf(client.t("%[1]s left the channel (%[2]s)"), nick, item.Message.Message)
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
 		case history.Kick:
1114
 		case history.Kick:
1115
 			if eventPlayback {
1115
 			if eventPlayback {
1116
 				rb.AddFromClient(item.Message.Time, item.Message.Msgid, item.Nick, item.AccountName, nil, "KICK", chname, item.Params[0], item.Message.Message)
1116
 				rb.AddFromClient(item.Message.Time, item.Message.Msgid, item.Nick, item.AccountName, nil, "KICK", chname, item.Params[0], item.Message.Message)
1117
 			} else {
1117
 			} else {
1118
 				message := fmt.Sprintf(client.t("%[1]s kicked %[2]s (%[3]s)"), nick, item.Params[0], item.Message.Message)
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
 		case history.Quit:
1121
 		case history.Quit:
1122
 			if eventPlayback {
1122
 			if eventPlayback {
1126
 					continue // #474
1126
 					continue // #474
1127
 				}
1127
 				}
1128
 				message := fmt.Sprintf(client.t("%[1]s quit (%[2]s)"), nick, item.Message.Message)
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
 		case history.Nick:
1131
 		case history.Nick:
1132
 			if eventPlayback {
1132
 			if eventPlayback {
1133
 				rb.AddFromClient(item.Message.Time, item.Message.Msgid, item.Nick, item.AccountName, nil, "NICK", item.Params[0])
1133
 				rb.AddFromClient(item.Message.Time, item.Message.Msgid, item.Nick, item.AccountName, nil, "NICK", item.Params[0])
1134
 			} else {
1134
 			} else {
1135
 				message := fmt.Sprintf(client.t("%[1]s changed nick to %[2]s"), nick, item.Params[0])
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
 		case history.Topic:
1138
 		case history.Topic:
1139
 			if eventPlayback {
1139
 			if eventPlayback {
1140
 				rb.AddFromClient(item.Message.Time, item.Message.Msgid, item.Nick, item.AccountName, nil, "TOPIC", chname, item.Message.Message)
1140
 				rb.AddFromClient(item.Message.Time, item.Message.Msgid, item.Nick, item.AccountName, nil, "TOPIC", chname, item.Message.Message)
1141
 			} else {
1141
 			} else {
1142
 				message := fmt.Sprintf(client.t("%[1]s set the channel topic to: %[2]s"), nick, item.Message.Message)
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
 		case history.Mode:
1145
 		case history.Mode:
1146
 			params := make([]string, len(item.Message.Split)+1)
1146
 			params := make([]string, len(item.Message.Split)+1)
1152
 				rb.AddFromClient(item.Message.Time, item.Message.Msgid, item.Nick, item.AccountName, nil, "MODE", params...)
1152
 				rb.AddFromClient(item.Message.Time, item.Message.Msgid, item.Nick, item.AccountName, nil, "MODE", params...)
1153
 			} else {
1153
 			} else {
1154
 				message := fmt.Sprintf(client.t("%[1]s set channel modes: %[2]s"), nick, strings.Join(params[1:], " "))
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 View File

17
 )
17
 )
18
 
18
 
19
 const chanservHelp = `ChanServ lets you register and manage channels.`
19
 const chanservHelp = `ChanServ lets you register and manage channels.`
20
-const chanservMask = "ChanServ!ChanServ@localhost"
21
 
20
 
22
 func chanregEnabled(config *Config) bool {
21
 func chanregEnabled(config *Config) bool {
23
 	return config.Channels.Registration.Enabled
22
 	return config.Channels.Registration.Enabled
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
 	channelName := params[0]
191
 	channelName := params[0]
198
 
192
 
199
 	channel := server.channels.Get(channelName)
193
 	channel := server.channels.Get(channelName)
200
 	if channel == nil {
194
 	if channel == nil {
201
-		csNotice(rb, client.t("Channel does not exist"))
195
+		service.Notice(rb, client.t("Channel does not exist"))
202
 		return
196
 		return
203
 	} else if channel.Founder() == "" {
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
 		return
199
 		return
206
 	}
200
 	}
207
 
201
 
208
 	modeChanges, unknown := modes.ParseChannelModeChanges(params[1:]...)
202
 	modeChanges, unknown := modes.ParseChannelModeChanges(params[1:]...)
209
 	var change modes.ModeChange
203
 	var change modes.ModeChange
210
 	if len(modeChanges) > 1 || len(unknown) > 0 {
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
 		return
206
 		return
213
 	} else if len(modeChanges) == 1 {
207
 	} else if len(modeChanges) == 1 {
214
 		change = modeChanges[0]
208
 		change = modeChanges[0]
233
 		accountIsValid = (change.Arg != "")
227
 		accountIsValid = (change.Arg != "")
234
 	}
228
 	}
235
 	if !accountIsValid {
229
 	if !accountIsValid {
236
-		csNotice(rb, client.t("Account does not exist"))
230
+		service.Notice(rb, client.t("Account does not exist"))
237
 		return
231
 		return
238
 	}
232
 	}
239
 
233
 
240
 	affectedModes, err := channel.ProcessAccountToUmodeChange(client, change)
234
 	affectedModes, err := channel.ProcessAccountToUmodeChange(client, change)
241
 
235
 
242
 	if err == errInsufficientPrivs {
236
 	if err == errInsufficientPrivs {
243
-		csNotice(rb, client.t("Insufficient privileges"))
237
+		service.Notice(rb, client.t("Insufficient privileges"))
244
 		return
238
 		return
245
 	} else if err != nil {
239
 	} else if err != nil {
246
-		csNotice(rb, client.t("Internal error"))
240
+		service.Notice(rb, client.t("Internal error"))
247
 		return
241
 		return
248
 	}
242
 	}
249
 
243
 
253
 		sort.Slice(affectedModes, func(i, j int) bool {
247
 		sort.Slice(affectedModes, func(i, j int) bool {
254
 			return umodeGreaterThan(affectedModes[i].Mode, affectedModes[j].Mode)
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
 		for _, modeChange := range affectedModes {
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
 	case modes.Add, modes.Remove:
254
 	case modes.Add, modes.Remove:
261
 		if len(affectedModes) > 0 {
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
 			// #729: apply change to current membership
257
 			// #729: apply change to current membership
264
 			for _, member := range channel.Members() {
258
 			for _, member := range channel.Members() {
265
 				if member.Account() == change.Arg {
259
 				if member.Account() == change.Arg {
270
 				}
264
 				}
271
 			}
265
 			}
272
 		} else {
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
 	channelInfo := server.channels.Get(params[0])
273
 	channelInfo := server.channels.Get(params[0])
280
 	if channelInfo == nil {
274
 	if channelInfo == nil {
281
-		csNotice(rb, client.t("Channel does not exist"))
275
+		service.Notice(rb, client.t("Channel does not exist"))
282
 		return
276
 		return
283
 	}
277
 	}
284
 	channelName := channelInfo.Name()
278
 	channelName := channelInfo.Name()
285
 
279
 
286
 	clientAccount := client.Account()
280
 	clientAccount := client.Account()
287
 	if clientAccount == "" || clientAccount != channelInfo.Founder() {
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
 		return
283
 		return
290
 	}
284
 	}
291
 
285
 
293
 	if len(params) > 1 {
287
 	if len(params) > 1 {
294
 		target = server.clients.Get(params[1])
288
 		target = server.clients.Get(params[1])
295
 		if target == nil {
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
 			return
291
 			return
298
 		}
292
 		}
299
 	} else {
293
 	} else {
315
 		announceCmodeChanges(channelInfo, modes.ModeChanges{change}, server.name, "*", "", rb)
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
 	tnick := target.Nick()
314
 	tnick := target.Nick()
321
 	server.logger.Info("services", fmt.Sprintf("Client %s op'd [%s] in channel %s", client.Nick(), tnick, channelName))
315
 	server.logger.Info("services", fmt.Sprintf("Client %s op'd [%s] in channel %s", client.Nick(), tnick, channelName))
322
 	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))
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
 	channel := server.channels.Get(params[0])
320
 	channel := server.channels.Get(params[0])
327
 	if channel == nil {
321
 	if channel == nil {
328
-		csNotice(rb, client.t("Channel does not exist"))
322
+		service.Notice(rb, client.t("Channel does not exist"))
329
 		return
323
 		return
330
 	}
324
 	}
331
 	if !channel.hasClient(client) {
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
 		return
327
 		return
334
 	}
328
 	}
335
 
329
 
337
 	if len(params) > 1 {
331
 	if len(params) > 1 {
338
 		target = server.clients.Get(params[1])
332
 		target = server.clients.Get(params[1])
339
 		if target == nil {
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
 			return
335
 			return
342
 		}
336
 		}
343
 	} else {
337
 	} else {
346
 
340
 
347
 	present, cumodes := channel.ClientStatus(target)
341
 	present, cumodes := channel.ClientStatus(target)
348
 	if !present || len(cumodes) == 0 {
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
 		return
344
 		return
351
 	}
345
 	}
352
 
346
 
370
 		return
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
 	if server.Config().Channels.Registration.OperatorOnly && !client.HasRoleCapabs("chanreg") {
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
 		return
373
 		return
380
 	}
374
 	}
381
 	channelName := params[0]
375
 	channelName := params[0]
382
 	channelInfo := server.channels.Get(channelName)
376
 	channelInfo := server.channels.Get(channelName)
383
 	if channelInfo == nil {
377
 	if channelInfo == nil {
384
-		csNotice(rb, client.t("No such channel"))
378
+		service.Notice(rb, client.t("No such channel"))
385
 		return
379
 		return
386
 	}
380
 	}
387
 	if !channelInfo.ClientIsAtLeast(client, modes.ChannelOperator) {
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
 		return
383
 		return
390
 	}
384
 	}
391
 
385
 
392
 	account := client.Account()
386
 	account := client.Account()
393
-	if !checkChanLimit(client, rb) {
387
+	if !checkChanLimit(service, client, rb) {
394
 		return
388
 		return
395
 	}
389
 	}
396
 
390
 
397
 	// this provides the synchronization that allows exactly one registration of the channel:
391
 	// this provides the synchronization that allows exactly one registration of the channel:
398
 	err := server.channels.SetRegistered(channelName, account)
392
 	err := server.channels.SetRegistered(channelName, account)
399
 	if err != nil {
393
 	if err != nil {
400
-		csNotice(rb, err.Error())
394
+		service.Notice(rb, err.Error())
401
 		return
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
 	server.logger.Info("services", fmt.Sprintf("Client %s registered channel %s", client.Nick(), channelName))
400
 	server.logger.Info("services", fmt.Sprintf("Client %s registered channel %s", client.Nick(), channelName))
407
 	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))
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
 		},
409
 		},
416
 		rb)
410
 		rb)
417
 	if applied {
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
 // check whether a client has already registered too many channels
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
 	account := client.Account()
418
 	account := client.Account()
425
 	channelsAlreadyRegistered := client.server.accounts.ChannelsForAccount(account)
419
 	channelsAlreadyRegistered := client.server.accounts.ChannelsForAccount(account)
426
 	ok = len(channelsAlreadyRegistered) < client.server.Config().Channels.Registration.MaxChannelsPerAccount || client.HasRoleCapabs("chanreg")
420
 	ok = len(channelsAlreadyRegistered) < client.server.Config().Channels.Registration.MaxChannelsPerAccount || client.HasRoleCapabs("chanreg")
427
 	if !ok {
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
 	return
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
 	founder := channel.Founder
428
 	founder := channel.Founder
435
 	if founder == "" {
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
 		return false
431
 		return false
438
 	}
432
 	}
439
 	if client.HasRoleCapabs("chanreg") {
433
 	if client.HasRoleCapabs("chanreg") {
440
 		return true
434
 		return true
441
 	}
435
 	}
442
 	if founder != client.Account() {
436
 	if founder != client.Account() {
443
-		csNotice(rb, client.t("Insufficient privileges"))
437
+		service.Notice(rb, client.t("Insufficient privileges"))
444
 		return false
438
 		return false
445
 	}
439
 	}
446
 	return true
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
 	channelName := params[0]
444
 	channelName := params[0]
451
 	var verificationCode string
445
 	var verificationCode string
452
 	if len(params) > 1 {
446
 	if len(params) > 1 {
455
 
449
 
456
 	channel := server.channels.Get(channelName)
450
 	channel := server.channels.Get(channelName)
457
 	if channel == nil {
451
 	if channel == nil {
458
-		csNotice(rb, client.t("No such channel"))
452
+		service.Notice(rb, client.t("No such channel"))
459
 		return
453
 		return
460
 	}
454
 	}
461
 
455
 
462
 	info := channel.ExportRegistration(0)
456
 	info := channel.ExportRegistration(0)
463
 	channelKey := info.NameCasefolded
457
 	channelKey := info.NameCasefolded
464
-	if !csPrivsCheck(info, client, rb) {
458
+	if !csPrivsCheck(service, info, client, rb) {
465
 		return
459
 		return
466
 	}
460
 	}
467
 
461
 
468
 	expectedCode := utils.ConfirmationCode(info.Name, info.RegisteredAt)
462
 	expectedCode := utils.ConfirmationCode(info.Name, info.RegisteredAt)
469
 	if expectedCode != verificationCode {
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
 		return
466
 		return
473
 	}
467
 	}
474
 
468
 
475
 	server.channels.SetUnregistered(channelKey, info.Founder)
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
 	channel := server.channels.Get(params[0])
474
 	channel := server.channels.Get(params[0])
481
 	if channel == nil {
475
 	if channel == nil {
482
-		csNotice(rb, client.t("Channel does not exist"))
476
+		service.Notice(rb, client.t("Channel does not exist"))
483
 		return
477
 		return
484
 	}
478
 	}
485
-	if !csPrivsCheck(channel.ExportRegistration(0), client, rb) {
479
+	if !csPrivsCheck(service, channel.ExportRegistration(0), client, rb) {
486
 		return
480
 		return
487
 	}
481
 	}
488
 
482
 
489
 	switch strings.ToLower(params[1]) {
483
 	switch strings.ToLower(params[1]) {
490
 	case "access":
484
 	case "access":
491
 		channel.resetAccess()
485
 		channel.resetAccess()
492
-		csNotice(rb, client.t("Successfully reset channel access"))
486
+		service.Notice(rb, client.t("Successfully reset channel access"))
493
 	case "users":
487
 	case "users":
494
 		for _, target := range channel.Members() {
488
 		for _, target := range channel.Members() {
495
 			if target != client {
489
 			if target != client {
497
 			}
491
 			}
498
 		}
492
 		}
499
 	default:
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
 	if strings.ToLower(params[0]) == "accept" {
500
 	if strings.ToLower(params[0]) == "accept" {
507
-		processTransferAccept(client, params[1], rb)
501
+		processTransferAccept(service, client, params[1], rb)
508
 		return
502
 		return
509
 	}
503
 	}
510
 	chname := params[0]
504
 	chname := params[0]
511
 	channel := server.channels.Get(chname)
505
 	channel := server.channels.Get(chname)
512
 	if channel == nil {
506
 	if channel == nil {
513
-		csNotice(rb, client.t("Channel does not exist"))
507
+		service.Notice(rb, client.t("Channel does not exist"))
514
 		return
508
 		return
515
 	}
509
 	}
516
 	regInfo := channel.ExportRegistration(0)
510
 	regInfo := channel.ExportRegistration(0)
519
 	isFounder := account != "" && account == regInfo.Founder
513
 	isFounder := account != "" && account == regInfo.Founder
520
 	hasPrivs := client.HasRoleCapabs("chanreg")
514
 	hasPrivs := client.HasRoleCapabs("chanreg")
521
 	if !(isFounder || hasPrivs) {
515
 	if !(isFounder || hasPrivs) {
522
-		csNotice(rb, client.t("Insufficient privileges"))
516
+		service.Notice(rb, client.t("Insufficient privileges"))
523
 		return
517
 		return
524
 	}
518
 	}
525
 	target := params[1]
519
 	target := params[1]
526
 	targetAccount, err := server.accounts.LoadAccount(params[1])
520
 	targetAccount, err := server.accounts.LoadAccount(params[1])
527
 	if err != nil {
521
 	if err != nil {
528
-		csNotice(rb, client.t("Account does not exist"))
522
+		service.Notice(rb, client.t("Account does not exist"))
529
 		return
523
 		return
530
 	}
524
 	}
531
 	if targetAccount.NameCasefolded != account {
525
 	if targetAccount.NameCasefolded != account {
532
 		expectedCode := utils.ConfirmationCode(regInfo.Name, regInfo.RegisteredAt)
526
 		expectedCode := utils.ConfirmationCode(regInfo.Name, regInfo.RegisteredAt)
533
 		codeValidated := 2 < len(params) && params[2] == expectedCode
527
 		codeValidated := 2 < len(params) && params[2] == expectedCode
534
 		if !codeValidated {
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
 			return
531
 			return
538
 		}
532
 		}
539
 	}
533
 	}
541
 	if err == nil {
535
 	if err == nil {
542
 		switch status {
536
 		switch status {
543
 		case channelTransferComplete:
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
 		case channelTransferPending:
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
 		case channelTransferCancelled:
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
 	} else {
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
 	clients := server.accounts.AccountToClients(account)
551
 	clients := server.accounts.AccountToClients(account)
558
 	if len(clients) == 0 {
552
 	if len(clients) == 0 {
559
 		return
553
 		return
565
 			break // prefer the login where the nick is the account
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
 	channel := client.server.channels.Get(chname)
566
 	channel := client.server.channels.Get(chname)
573
 	if channel == nil {
567
 	if channel == nil {
574
-		csNotice(rb, client.t("Channel does not exist"))
568
+		service.Notice(rb, client.t("Channel does not exist"))
575
 		return
569
 		return
576
 	}
570
 	}
577
-	if !checkChanLimit(client, rb) {
571
+	if !checkChanLimit(service, client, rb) {
578
 		return
572
 		return
579
 	}
573
 	}
580
 	switch channel.AcceptTransfer(client) {
574
 	switch channel.AcceptTransfer(client) {
581
 	case nil:
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
 	case errChannelTransferNotOffered:
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
 	default:
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
 	oper := client.Oper()
585
 	oper := client.Oper()
592
 	if oper == nil {
586
 	if oper == nil {
593
 		return // should be impossible because you need oper capabs for this
587
 		return // should be impossible because you need oper capabs for this
611
 				channel.Kick(client, target, "Cleared by ChanServ", rb, true)
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
 	case errInvalidChannelName:
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
 	default:
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
 	chname := params[0]
617
 	chname := params[0]
624
 	switch server.channels.Unpurge(chname) {
618
 	switch server.channels.Unpurge(chname) {
625
 	case nil:
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
 	case errNoSuchChannel:
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
 	default:
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
 	if !client.HasRoleCapabs("chanreg") {
629
 	if !client.HasRoleCapabs("chanreg") {
636
-		csNotice(rb, client.t("Insufficient privileges"))
630
+		service.Notice(rb, client.t("Insufficient privileges"))
637
 		return
631
 		return
638
 	}
632
 	}
639
 
633
 
642
 		var err error
636
 		var err error
643
 		searchRegex, err = regexp.Compile(params[0])
637
 		searchRegex, err = regexp.Compile(params[0])
644
 		if err != nil {
638
 		if err != nil {
645
-			csNotice(rb, client.t("Invalid regex"))
639
+			service.Notice(rb, client.t("Invalid regex"))
646
 			return
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
 	channels := server.channelRegistry.AllChannels()
646
 	channels := server.channelRegistry.AllChannels()
653
 	for _, channel := range channels {
647
 	for _, channel := range channels {
654
 		if searchRegex == nil || searchRegex.MatchString(channel) {
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
 	chname, err := CasefoldChannel(params[0])
657
 	chname, err := CasefoldChannel(params[0])
664
 	if err != nil {
658
 	if err != nil {
665
-		csNotice(rb, client.t("Invalid channel name"))
659
+		service.Notice(rb, client.t("Invalid channel name"))
666
 		return
660
 		return
667
 	}
661
 	}
668
 
662
 
670
 	if client.HasRoleCapabs("chanreg") {
664
 	if client.HasRoleCapabs("chanreg") {
671
 		purgeRecord, err := server.channelRegistry.LoadPurgeRecord(chname)
665
 		purgeRecord, err := server.channelRegistry.LoadPurgeRecord(chname)
672
 		if err == nil {
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
 			if purgeRecord.Reason != "" {
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
 	} else {
674
 	} else {
681
 		if server.channels.IsPurged(chname) {
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
 	} else {
684
 	} else {
691
 		chinfo, err = server.channelRegistry.LoadChannel(chname)
685
 		chinfo, err = server.channelRegistry.LoadChannel(chname)
692
 		if err != nil && !(err == errNoSuchChannel || err == errFeatureDisabled) {
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
 			return
688
 			return
695
 		}
689
 		}
696
 	}
690
 	}
697
 
691
 
698
 	// channel exists but is unregistered, or doesn't exist:
692
 	// channel exists but is unregistered, or doesn't exist:
699
 	if chinfo.Founder == "" {
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
 		return
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
 	config := client.server.Config()
703
 	config := client.server.Config()
710
 
704
 
711
 	switch strings.ToLower(settingName) {
705
 	switch strings.ToLower(settingName) {
712
 	case "history":
706
 	case "history":
713
 		effectiveValue := historyEnabled(config.History.Persistent.RegisteredChannels, settings.History)
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
 	default:
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
 	chname, setting := params[0], params[1]
716
 	chname, setting := params[0], params[1]
723
 	channel := server.channels.Get(chname)
717
 	channel := server.channels.Get(chname)
724
 	if channel == nil {
718
 	if channel == nil {
725
-		csNotice(rb, client.t("No such channel"))
719
+		service.Notice(rb, client.t("No such channel"))
726
 		return
720
 		return
727
 	}
721
 	}
728
 	info := channel.ExportRegistration(IncludeSettings)
722
 	info := channel.ExportRegistration(IncludeSettings)
729
-	if !csPrivsCheck(info, client, rb) {
723
+	if !csPrivsCheck(service, info, client, rb) {
730
 		return
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
 	chname, setting, value := params[0], params[1], params[2]
731
 	chname, setting, value := params[0], params[1], params[2]
738
 	channel := server.channels.Get(chname)
732
 	channel := server.channels.Get(chname)
739
 	if channel == nil {
733
 	if channel == nil {
740
-		csNotice(rb, client.t("No such channel"))
734
+		service.Notice(rb, client.t("No such channel"))
741
 		return
735
 		return
742
 	}
736
 	}
743
 	info := channel.ExportRegistration(IncludeSettings)
737
 	info := channel.ExportRegistration(IncludeSettings)
744
 	settings := info.Settings
738
 	settings := info.Settings
745
-	if !csPrivsCheck(info, client, rb) {
739
+	if !csPrivsCheck(service, info, client, rb) {
746
 		return
740
 		return
747
 	}
741
 	}
748
 
742
 
760
 
754
 
761
 	switch err {
755
 	switch err {
762
 	case nil:
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
 	case errInvalidParams:
759
 	case errInvalidParams:
766
-		csNotice(rb, client.t("Invalid parameters"))
760
+		service.Notice(rb, client.t("Invalid parameters"))
767
 	default:
761
 	default:
768
 		server.logger.Error("internal", "CS SET error:", err.Error())
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 View File

77
 }
77
 }
78
 
78
 
79
 // helper function to dispatch messages when a client successfully registers
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
 	details := client.Details()
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
 	} else {
84
 	} else {
85
 		rb.Add(nil, client.server.name, RPL_REG_SUCCESS, details.nick, details.accountName, client.t("Account created"))
85
 		rb.Add(nil, client.server.name, RPL_REG_SUCCESS, details.nick, details.accountName, client.t("Account created"))
86
 	}
86
 	}
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()))
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
 // sendSuccessfulAccountAuth means that an account auth attempt completed successfully, and is used to dispatch messages.
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
 	details := client.Details()
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
 	} else {
97
 	} else {
98
 		//TODO(dan): some servers send this numeric even for NickServ logins iirc? to confirm and maybe do too
98
 		//TODO(dan): some servers send this numeric even for NickServ logins iirc? to confirm and maybe do too
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))
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
 		msg := authErrorToMessage(server, err)
253
 		msg := authErrorToMessage(server, err)
254
 		rb.Add(nil, server.name, ERR_SASLFAIL, client.Nick(), fmt.Sprintf("%s: %s", client.t("SASL authentication failed"), client.t(msg)))
254
 		rb.Add(nil, server.name, ERR_SASLFAIL, client.Nick(), fmt.Sprintf("%s: %s", client.t("SASL authentication failed"), client.t(msg)))
255
 		return false
255
 		return false
256
-	} else if !fixupNickEqualsAccount(client, rb, server.Config()) {
256
+	} else if !fixupNickEqualsAccount(client, rb, server.Config(), "") {
257
 		return false
257
 		return false
258
 	}
258
 	}
259
 
259
 
260
-	sendSuccessfulAccountAuth(client, rb, false, true)
260
+	sendSuccessfulAccountAuth(nil, client, rb, true)
261
 	return false
261
 	return false
262
 }
262
 }
263
 
263
 
311
 		msg := authErrorToMessage(server, err)
311
 		msg := authErrorToMessage(server, err)
312
 		rb.Add(nil, server.name, ERR_SASLFAIL, client.nick, fmt.Sprintf("%s: %s", client.t("SASL authentication failed"), client.t(msg)))
312
 		rb.Add(nil, server.name, ERR_SASLFAIL, client.nick, fmt.Sprintf("%s: %s", client.t("SASL authentication failed"), client.t(msg)))
313
 		return false
313
 		return false
314
-	} else if !fixupNickEqualsAccount(client, rb, server.Config()) {
314
+	} else if !fixupNickEqualsAccount(client, rb, server.Config(), "") {
315
 		return false
315
 		return false
316
 	}
316
 	}
317
 
317
 
318
-	sendSuccessfulAccountAuth(client, rb, false, true)
318
+	sendSuccessfulAccountAuth(nil, client, rb, true)
319
 	return false
319
 	return false
320
 }
320
 }
321
 
321
 
1525
 	config := server.Config()
1525
 	config := server.Config()
1526
 	if time.Since(client.ctime) < config.Channels.ListDelay && client.Account() == "" && !client.HasMode(modes.Operator) {
1526
 	if time.Since(client.ctime) < config.Channels.ListDelay && client.Account() == "" && !client.HasMode(modes.Operator) {
1527
 		remaining := time.Until(client.ctime.Add(config.Channels.ListDelay))
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
 		rb.Add(nil, server.name, RPL_LISTEND, client.Nick(), client.t("End of LIST"))
1529
 		rb.Add(nil, server.name, RPL_LISTEND, client.Nick(), client.t("End of LIST"))
1530
 		return false
1530
 		return false
1531
 	}
1531
 	}
2355
 			}
2355
 			}
2356
 			err := server.accounts.AuthenticateByPassphrase(client, account, accountPass)
2356
 			err := server.accounts.AuthenticateByPassphrase(client, account, accountPass)
2357
 			if err == nil {
2357
 			if err == nil {
2358
-				sendSuccessfulAccountAuth(client, rb, false, true)
2358
+				sendSuccessfulAccountAuth(nil, client, rb, true)
2359
 				// login-via-pass-command entails that we do not need to check
2359
 				// login-via-pass-command entails that we do not need to check
2360
 				// an actual server password (either no password or skip-server-password)
2360
 				// an actual server password (either no password or skip-server-password)
2361
 				rb.session.passStatus = serverPassSuccessful
2361
 				rb.session.passStatus = serverPassSuccessful
2446
 			err := server.accounts.Verify(client, accountName, "")
2446
 			err := server.accounts.Verify(client, accountName, "")
2447
 			if err == nil {
2447
 			if err == nil {
2448
 				if client.registered {
2448
 				if client.registered {
2449
-					if !fixupNickEqualsAccount(client, rb, config) {
2449
+					if !fixupNickEqualsAccount(client, rb, config, "") {
2450
 						err = errNickAccountMismatch
2450
 						err = errNickAccountMismatch
2451
 					}
2451
 					}
2452
 				}
2452
 				}
2453
 				if err == nil {
2453
 				if err == nil {
2454
 					rb.Add(nil, server.name, "REGISTER", "SUCCESS", accountName, client.t("Account successfully registered"))
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
 			if err != nil {
2458
 			if err != nil {
2494
 	accountName, verificationCode := msg.Params[0], msg.Params[1]
2494
 	accountName, verificationCode := msg.Params[0], msg.Params[1]
2495
 	err := server.accounts.Verify(client, accountName, verificationCode)
2495
 	err := server.accounts.Verify(client, accountName, verificationCode)
2496
 	if err == nil && client.registered {
2496
 	if err == nil && client.registered {
2497
-		if !fixupNickEqualsAccount(client, rb, config) {
2497
+		if !fixupNickEqualsAccount(client, rb, config, "") {
2498
 			err = errNickAccountMismatch
2498
 			err = errNickAccountMismatch
2499
 		}
2499
 		}
2500
 	}
2500
 	}
2501
 	switch err {
2501
 	switch err {
2502
 	case nil:
2502
 	case nil:
2503
 		rb.Add(nil, server.name, "VERIFY", "SUCCESS", accountName, client.t("Account successfully registered"))
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
 	case errAccountVerificationInvalidCode:
2505
 	case errAccountVerificationInvalidCode:
2506
 		rb.Add(nil, server.name, "FAIL", "VERIFY", "INVALID_CODE", client.t("Invalid verification code"))
2506
 		rb.Add(nil, server.name, "FAIL", "VERIFY", "INVALID_CODE", client.t("Invalid verification code"))
2507
 	default:
2507
 	default:
2873
 			username, password = username[:colonIndex], username[colonIndex+1:]
2873
 			username, password = username[:colonIndex], username[colonIndex+1:]
2874
 			err := server.accounts.AuthenticateByPassphrase(client, username, password)
2874
 			err := server.accounts.AuthenticateByPassphrase(client, username, password)
2875
 			if err == nil {
2875
 			if err == nil {
2876
-				sendSuccessfulAccountAuth(client, rb, false, true)
2876
+				sendSuccessfulAccountAuth(nil, client, rb, true)
2877
 			} else {
2877
 			} else {
2878
 				// this is wrong, but send something for debugging that will show up in a raw transcript
2878
 				// this is wrong, but send something for debugging that will show up in a raw transcript
2879
 				rb.Add(nil, server.name, ERR_SASLFAIL, client.Nick(), client.t("SASL authentication failed"))
2879
 				rb.Add(nil, server.name, ERR_SASLFAIL, client.Nick(), client.t("SASL authentication failed"))

+ 19
- 25
irc/histserv.go View File

18
 
18
 
19
 const (
19
 const (
20
 	histservHelp = `HistServ provides commands related to history.`
20
 	histservHelp = `HistServ provides commands related to history.`
21
-	histServMask = "HistServ!HistServ@localhost"
22
 )
21
 )
23
 
22
 
24
 func histservEnabled(config *Config) bool {
23
 func histservEnabled(config *Config) bool {
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
 	accountName := server.accounts.AccountToAccountName(params[0])
86
 	accountName := server.accounts.AccountToAccountName(params[0])
93
 	if accountName == "" {
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
 		accountName = params[0]
89
 		accountName = params[0]
96
 	}
90
 	}
97
 
91
 
98
 	server.ForgetHistory(accountName)
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
 	var target, msgid string
98
 	var target, msgid string
105
 	if len(params) == 1 {
99
 	if len(params) == 1 {
106
 		msgid = params[0]
100
 		msgid = params[0]
113
 	if !hasPrivs {
107
 	if !hasPrivs {
114
 		accountName = client.AccountName()
108
 		accountName = client.AccountName()
115
 		if !(server.Config().History.Retention.AllowIndividualDelete && accountName != "*") {
109
 		if !(server.Config().History.Retention.AllowIndividualDelete && accountName != "*") {
116
-			histNotice(rb, client.t("Insufficient privileges"))
110
+			service.Notice(rb, client.t("Insufficient privileges"))
117
 			return
111
 			return
118
 		}
112
 		}
119
 	}
113
 	}
120
 
114
 
121
 	err := server.DeleteMessage(target, msgid, accountName)
115
 	err := server.DeleteMessage(target, msgid, accountName)
122
 	if err == nil {
116
 	if err == nil {
123
-		histNotice(rb, client.t("Successfully deleted message"))
117
+		service.Notice(rb, client.t("Successfully deleted message"))
124
 	} else {
118
 	} else {
125
 		if hasPrivs {
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
 		} else {
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
 	cfAccount, err := CasefoldName(params[0])
128
 	cfAccount, err := CasefoldName(params[0])
135
 	if err != nil {
129
 	if err != nil {
136
-		histNotice(rb, client.t("Invalid account name"))
130
+		service.Notice(rb, client.t("Invalid account name"))
137
 		return
131
 		return
138
 	}
132
 	}
139
 
133
 
143
 	pathname := config.getOutputPath(filename)
137
 	pathname := config.getOutputPath(filename)
144
 	outfile, err := os.Create(pathname)
138
 	outfile, err := os.Create(pathname)
145
 	if err != nil {
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
 	} else {
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
 	defer func() {
149
 	defer func() {
156
 		if r := recover(); r != nil {
150
 		if r := recover(); r != nil {
157
 			server.logger.Error("history",
151
 			server.logger.Error("history",
167
 
161
 
168
 	client := server.clients.Get(alertNick)
162
 	client := server.clients.Get(alertNick)
169
 	if client != nil && client.HasRoleCapabs("history") {
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
 	items, _, err := easySelectHistory(server, client, params)
169
 	items, _, err := easySelectHistory(server, client, params)
176
 	if err != nil {
170
 	if err != nil {
177
-		histNotice(rb, client.t("Could not retrieve history"))
171
+		service.Notice(rb, client.t("Could not retrieve history"))
178
 		return
172
 		return
179
 	}
173
 	}
180
 
174
 
181
 	playMessage := func(timestamp time.Time, nick, message string) {
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
 	for _, item := range items {
179
 	for _, item := range items {
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
 // handles parameter parsing and history queries for /HISTORY and /HISTSERV PLAY
196
 // handles parameter parsing and history queries for /HISTORY and /HISTSERV PLAY

+ 21
- 27
irc/hostserv.go View File

16
 const (
16
 const (
17
 	hostservHelp = `HostServ lets you manage your vhost (i.e., the string displayed
17
 	hostservHelp = `HostServ lets you manage your vhost (i.e., the string displayed
18
 in place of your client's hostname/IP).`
18
 in place of your client's hostname/IP).`
19
-	hsNickMask = "HostServ!HostServ@localhost"
20
 )
19
 )
21
 
20
 
22
 var (
21
 var (
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
 	enable := false
98
 	enable := false
105
 	if command == "on" {
99
 	if command == "on" {
106
 		enable = true
100
 		enable = true
108
 
102
 
109
 	_, err := server.accounts.VHostSetEnabled(client, enable)
103
 	_, err := server.accounts.VHostSetEnabled(client, enable)
110
 	if err == errNoVhost {
104
 	if err == errNoVhost {
111
-		hsNotice(rb, client.t(err.Error()))
105
+		service.Notice(rb, client.t(err.Error()))
112
 	} else if err != nil {
106
 	} else if err != nil {
113
-		hsNotice(rb, client.t("An error occurred"))
107
+		service.Notice(rb, client.t("An error occurred"))
114
 	} else if enable {
108
 	} else if enable {
115
-		hsNotice(rb, client.t("Successfully enabled your vhost"))
109
+		service.Notice(rb, client.t("Successfully enabled your vhost"))
116
 	} else {
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
 	var accountName string
116
 	var accountName string
123
 	if len(params) > 0 {
117
 	if len(params) > 0 {
124
 		if !client.HasRoleCapabs("vhosts") {
118
 		if !client.HasRoleCapabs("vhosts") {
125
-			hsNotice(rb, client.t("Command restricted"))
119
+			service.Notice(rb, client.t("Command restricted"))
126
 			return
120
 			return
127
 		}
121
 		}
128
 		accountName = params[0]
122
 		accountName = params[0]
129
 	} else {
123
 	} else {
130
 		accountName = client.Account()
124
 		accountName = client.Account()
131
 		if accountName == "" {
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
 			return
127
 			return
134
 		}
128
 		}
135
 	}
129
 	}
139
 		if err != errAccountDoesNotExist {
133
 		if err != errAccountDoesNotExist {
140
 			server.logger.Warning("internal", "error loading account info", accountName, err.Error())
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
 		return
137
 		return
144
 	}
138
 	}
145
 
139
 
146
 	if account.VHost.ApprovedVHost != "" {
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
 		if !account.VHost.Enabled {
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
 	} else {
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
 	return nil
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
 	user := params[0]
162
 	user := params[0]
169
 	var vhost string
163
 	var vhost string
170
 
164
 
171
 	if command == "set" {
165
 	if command == "set" {
172
 		vhost = params[1]
166
 		vhost = params[1]
173
 		if validateVhost(server, vhost, true) != nil {
167
 		if validateVhost(server, vhost, true) != nil {
174
-			hsNotice(rb, client.t("Invalid vhost"))
168
+			service.Notice(rb, client.t("Invalid vhost"))
175
 			return
169
 			return
176
 		}
170
 		}
177
 	}
171
 	}
179
 
173
 
180
 	_, err := server.accounts.VHostSet(user, vhost)
174
 	_, err := server.accounts.VHostSet(user, vhost)
181
 	if err != nil {
175
 	if err != nil {
182
-		hsNotice(rb, client.t("An error occurred"))
176
+		service.Notice(rb, client.t("An error occurred"))
183
 	} else if vhost != "" {
177
 	} else if vhost != "" {
184
-		hsNotice(rb, client.t("Successfully set vhost"))
178
+		service.Notice(rb, client.t("Successfully set vhost"))
185
 	} else {
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
 	secret := params[0]
185
 	secret := params[0]
192
 	expectedCode := utils.ConfirmationCode(secret, server.ctime)
186
 	expectedCode := utils.ConfirmationCode(secret, server.ctime)
193
 	if len(params) == 1 || params[1] != expectedCode {
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
 		return
190
 		return
197
 	}
191
 	}
198
 	StoreCloakSecret(server.store, secret)
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 View File

151
 // so we need to re-NICK automatically on every login event (IDENTIFY,
151
 // so we need to re-NICK automatically on every login event (IDENTIFY,
152
 // VERIFY, and a REGISTER that auto-verifies). if we can't get the nick
152
 // VERIFY, and a REGISTER that auto-verifies). if we can't get the nick
153
 // then we log them out (they will be able to reattach with SASL)
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
 	if !config.Accounts.NickReservation.ForceNickEqualsAccount {
155
 	if !config.Accounts.NickReservation.ForceNickEqualsAccount {
156
 		return true
156
 		return true
157
 	}
157
 	}
161
 	err := performNickChange(client.server, client, client, rb.session, client.AccountName(), rb)
161
 	err := performNickChange(client.server, client, client, rb.session, client.AccountName(), rb)
162
 	if err != nil && err != errNoop {
162
 	if err != nil && err != errNoop {
163
 		client.server.accounts.Logout(client)
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
 		return false
168
 		return false
166
 	}
169
 	}
167
 	return true
170
 	return true

+ 184
- 195
irc/nickserv.go View File

36
 	return config.Accounts.Multiclient.Enabled
36
 	return config.Accounts.Multiclient.Enabled
37
 }
37
 }
38
 
38
 
39
-const (
40
-	nsPrefix = "NickServ!NickServ@localhost"
41
-)
42
-
43
 const nickservHelp = `NickServ lets you register, log in to, and manage an account.`
39
 const nickservHelp = `NickServ lets you register, log in to, and manage an account.`
44
 
40
 
45
 var (
41
 var (
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
 	var account string
361
 	var account string
373
 	if command == "saget" {
362
 	if command == "saget" {
374
 		account = params[0]
363
 		account = params[0]
379
 
368
 
380
 	accountData, err := server.accounts.LoadAccount(account)
369
 	accountData, err := server.accounts.LoadAccount(account)
381
 	if err == errAccountDoesNotExist {
370
 	if err == errAccountDoesNotExist {
382
-		nsNotice(rb, client.t("No such account"))
371
+		service.Notice(rb, client.t("No such account"))
383
 		return
372
 		return
384
 	} else if err != nil {
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
 		return
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
 	config := client.server.Config()
382
 	config := client.server.Config()
394
 	switch strings.ToLower(settingName) {
383
 	switch strings.ToLower(settingName) {
395
 	case "enforce":
384
 	case "enforce":
396
 		storedValue := settings.NickEnforcement
385
 		storedValue := settings.NickEnforcement
397
 		serializedStoredValue := nickReservationToString(storedValue)
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
 		serializedActualValue := nickReservationToString(configuredEnforcementMethod(config, storedValue))
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
 	case "autoreplay-lines":
390
 	case "autoreplay-lines":
402
 		if settings.AutoreplayLines == nil {
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
 		} else {
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
 	case "replay-joins":
396
 	case "replay-joins":
408
 		switch settings.ReplayJoins {
397
 		switch settings.ReplayJoins {
409
 		case ReplayJoinsCommandsOnly:
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
 		case ReplayJoinsAlways:
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
 		case ReplayJoinsNever:
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
 	case "multiclient":
405
 	case "multiclient":
417
 		if !config.Accounts.Multiclient.Enabled {
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
 		} else {
408
 		} else {
420
 			switch settings.AllowBouncer {
409
 			switch settings.AllowBouncer {
421
 			case MulticlientAllowedServerDefault:
410
 			case MulticlientAllowedServerDefault:
422
 				if config.Accounts.Multiclient.AllowedByDefault {
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
 				} else {
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
 			case MulticlientDisallowedByUser:
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
 			case MulticlientAllowedByUser:
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
 	case "always-on":
422
 	case "always-on":
434
 		stored := settings.AlwaysOn
423
 		stored := settings.AlwaysOn
435
 		actual := persistenceEnabled(config.Accounts.Multiclient.AlwaysOn, stored)
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
 		if actual {
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
 		} else {
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
 	case "autoreplay-missed":
431
 	case "autoreplay-missed":
443
 		stored := settings.AutoreplayMissed
432
 		stored := settings.AutoreplayMissed
444
 		if stored {
433
 		if stored {
445
 			alwaysOn := persistenceEnabled(config.Accounts.Multiclient.AlwaysOn, settings.AlwaysOn)
434
 			alwaysOn := persistenceEnabled(config.Accounts.Multiclient.AlwaysOn, settings.AlwaysOn)
446
 			if alwaysOn {
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
 			} else {
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
 		} else {
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
 	case "auto-away":
443
 	case "auto-away":
455
 		stored := settings.AutoAway
444
 		stored := settings.AutoAway
456
 		alwaysOn := persistenceEnabled(config.Accounts.Multiclient.AlwaysOn, settings.AlwaysOn)
445
 		alwaysOn := persistenceEnabled(config.Accounts.Multiclient.AlwaysOn, settings.AlwaysOn)
457
 		actual := persistenceEnabled(config.Accounts.Multiclient.AutoAway, settings.AutoAway)
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
 		if actual && alwaysOn {
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
 		} else if actual && !alwaysOn {
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
 		} else if !actual {
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
 	case "dm-history":
455
 	case "dm-history":
467
 		effectiveValue := historyEnabled(config.History.Persistent.DirectMessages, settings.DMHistory)
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
 	default:
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
 	var account string
466
 	var account string
478
 	if command == "saset" {
467
 	if command == "saset" {
479
 		account = params[0]
468
 		account = params[0]
487
 	var err error
476
 	var err error
488
 	switch strings.ToLower(params[0]) {
477
 	switch strings.ToLower(params[0]) {
489
 	case "pass", "password":
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
 		return
480
 		return
492
 	case "enforce":
481
 	case "enforce":
493
 		var method NickEnforcementMethod
482
 		var method NickEnforcementMethod
612
 
601
 
613
 	switch err {
602
 	switch err {
614
 	case nil:
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
 	case errInvalidParams, errAccountDoesNotExist, errFeatureDisabled, errAccountUnverified, errAccountUpdateFailed:
606
 	case errInvalidParams, errAccountDoesNotExist, errFeatureDisabled, errAccountUnverified, errAccountUpdateFailed:
618
-		nsNotice(rb, client.t(err.Error()))
607
+		service.Notice(rb, client.t(err.Error()))
619
 	case errNickAccountMismatch:
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
 	default:
610
 	default:
622
 		// unknown error
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
 	sadrop := command == "sadrop"
617
 	sadrop := command == "sadrop"
629
 	var nick string
618
 	var nick string
630
 	if len(params) > 0 {
619
 	if len(params) > 0 {
635
 
624
 
636
 	err := server.accounts.SetNickReserved(client, nick, sadrop, false)
625
 	err := server.accounts.SetNickReserved(client, nick, sadrop, false)
637
 	if err == nil {
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
 	} else if err == errAccountNotLoggedIn {
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
 	} else if err == errAccountCantDropPrimaryNick {
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
 	} else {
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
 	nick := params[0]
638
 	nick := params[0]
650
 
639
 
651
 	ghost := server.clients.Get(nick)
640
 	ghost := server.clients.Get(nick)
652
 	if ghost == nil {
641
 	if ghost == nil {
653
-		nsNotice(rb, client.t("No such nick"))
642
+		service.Notice(rb, client.t("No such nick"))
654
 		return
643
 		return
655
 	} else if ghost == client {
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
 		return
646
 		return
658
 	} else if ghost.AlwaysOn() {
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
 		return
649
 		return
661
 	}
650
 	}
662
 
651
 
667
 		authorized = (server.accounts.NickToAccount(nick) == account) || (ghost.Account() == account)
656
 		authorized = (server.accounts.NickToAccount(nick) == account) || (ghost.Account() == account)
668
 	}
657
 	}
669
 	if !authorized {
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
 		return
660
 		return
672
 	}
661
 	}
673
 
662
 
675
 	ghost.destroy(nil)
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
 	nick := client.Nick()
668
 	nick := client.Nick()
680
 	err := server.accounts.SetNickReserved(client, nick, false, true)
669
 	err := server.accounts.SetNickReserved(client, nick, false, true)
681
 	if err == nil {
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
 	} else if err == errAccountTooManyNicks {
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
 	} else if err == errNicknameReserved {
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
 	} else {
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
 	throttled, remainingTime := client.checkLoginThrottle()
682
 	throttled, remainingTime := client.checkLoginThrottle()
694
 	if throttled {
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
 	return !throttled
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
 	if client.LoggedIntoAccount() {
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
 		return
692
 		return
704
 	}
693
 	}
705
 
694
 
735
 
724
 
736
 	nickFixupFailed := false
725
 	nickFixupFailed := false
737
 	if loginSuccessful {
726
 	if loginSuccessful {
738
-		if !fixupNickEqualsAccount(client, rb, server.Config()) {
727
+		if !fixupNickEqualsAccount(client, rb, server.Config(), service.prefix) {
739
 			loginSuccessful = false
728
 			loginSuccessful = false
740
 			// fixupNickEqualsAccount sends its own error message, don't send another
729
 			// fixupNickEqualsAccount sends its own error message, don't send another
741
 			nickFixupFailed = true
730
 			nickFixupFailed = true
743
 	}
732
 	}
744
 
733
 
745
 	if loginSuccessful {
734
 	if loginSuccessful {
746
-		sendSuccessfulAccountAuth(client, rb, true, true)
735
+		sendSuccessfulAccountAuth(service, client, rb, true)
747
 	} else if !nickFixupFailed {
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
 	if !client.HasRoleCapabs("accreg") {
742
 	if !client.HasRoleCapabs("accreg") {
754
-		nsNotice(rb, client.t("Insufficient privileges"))
743
+		service.Notice(rb, client.t("Insufficient privileges"))
755
 		return
744
 		return
756
 	}
745
 	}
757
 
746
 
760
 		var err error
749
 		var err error
761
 		searchRegex, err = regexp.Compile(params[0])
750
 		searchRegex, err = regexp.Compile(params[0])
762
 		if err != nil {
751
 		if err != nil {
763
-			nsNotice(rb, client.t("Invalid regex"))
752
+			service.Notice(rb, client.t("Invalid regex"))
764
 			return
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
 	nicks := server.accounts.AllNicks()
759
 	nicks := server.accounts.AllNicks()
771
 	for _, nick := range nicks {
760
 	for _, nick := range nicks {
772
 		if searchRegex == nil || searchRegex.MatchString(nick) {
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
 	if !server.Config().Accounts.AuthenticationEnabled && !client.HasRoleCapabs("accreg") {
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
 		return
772
 		return
784
 	}
773
 	}
785
 
774
 
789
 		if server.Config().Accounts.NickReservation.Enabled {
778
 		if server.Config().Accounts.NickReservation.Enabled {
790
 			accountName = server.accounts.NickToAccount(nick)
779
 			accountName = server.accounts.NickToAccount(nick)
791
 			if accountName == "" {
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
 				return
782
 				return
794
 			}
783
 			}
795
 		} else {
784
 		} else {
798
 	} else {
787
 	} else {
799
 		accountName = client.Account()
788
 		accountName = client.Account()
800
 		if accountName == "" {
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
 			return
791
 			return
803
 		}
792
 		}
804
 	}
793
 	}
805
 
794
 
806
 	account, err := server.accounts.LoadAccount(accountName)
795
 	account, err := server.accounts.LoadAccount(accountName)
807
 	if err != nil || !account.Verified {
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
 		return
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
 	registeredAt := account.RegisteredAt.Format(time.RFC1123)
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
 	// TODO nicer formatting for this
804
 	// TODO nicer formatting for this
816
 	for _, nick := range account.AdditionalNicks {
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
 	for _, channel := range server.accounts.ChannelsForAccount(accountName) {
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
 	if account.Suspended != nil {
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
 	details := client.Details()
817
 	details := client.Details()
829
 	passphrase := params[0]
818
 	passphrase := params[0]
830
 	var email string
819
 	var email string
835
 	certfp := rb.session.certfp
824
 	certfp := rb.session.certfp
836
 	if passphrase == "*" {
825
 	if passphrase == "*" {
837
 		if certfp == "" {
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
 			return
828
 			return
840
 		} else {
829
 		} else {
841
 			passphrase = ""
830
 			passphrase = ""
845
 	if passphrase != "" {
834
 	if passphrase != "" {
846
 		cfPassphrase, err := Casefold(passphrase)
835
 		cfPassphrase, err := Casefold(passphrase)
847
 		if err == nil && cfPassphrase == details.nickCasefolded {
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
 			return
838
 			return
850
 		}
839
 		}
851
 	}
840
 	}
852
 
841
 
853
-	if !nsLoginThrottleCheck(client, rb) {
842
+	if !nsLoginThrottleCheck(service, client, rb) {
854
 		return
843
 		return
855
 	}
844
 	}
856
 
845
 
859
 	if config.Accounts.NickReservation.ForceGuestFormat {
848
 	if config.Accounts.NickReservation.ForceGuestFormat {
860
 		matches := config.Accounts.NickReservation.guestRegexp.FindStringSubmatch(account)
849
 		matches := config.Accounts.NickReservation.guestRegexp.FindStringSubmatch(account)
861
 		if matches == nil || len(matches) < 2 {
850
 		if matches == nil || len(matches) < 2 {
862
-			nsNotice(rb, client.t("Erroneous nickname"))
851
+			service.Notice(rb, client.t("Erroneous nickname"))
863
 			return
852
 			return
864
 		}
853
 		}
865
 		account = matches[1]
854
 		account = matches[1]
867
 
856
 
868
 	callbackNamespace, callbackValue, validationErr := parseCallback(email, config)
857
 	callbackNamespace, callbackValue, validationErr := parseCallback(email, config)
869
 	if validationErr != nil {
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
 		return
860
 		return
872
 	}
861
 	}
873
 
862
 
875
 	if err == nil {
864
 	if err == nil {
876
 		if callbackNamespace == "*" {
865
 		if callbackNamespace == "*" {
877
 			err = server.accounts.Verify(client, account, "")
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
 		} else {
870
 		} else {
882
 			messageTemplate := client.t("Account created, pending verification; verification code has been sent to %s")
871
 			messageTemplate := client.t("Account created, pending verification; verification code has been sent to %s")
883
 			message := fmt.Sprintf(messageTemplate, callbackValue)
872
 			message := fmt.Sprintf(messageTemplate, callbackValue)
884
-			nsNotice(rb, message)
873
+			service.Notice(rb, message)
885
 		}
874
 		}
886
 	} else {
875
 	} else {
887
 		// details could not be stored and relevant numerics have been dispatched, abort
876
 		// details could not be stored and relevant numerics have been dispatched, abort
888
 		message := registrationErrorToMessage(err)
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
 	var account, passphrase string
883
 	var account, passphrase string
895
 	account = params[0]
884
 	account = params[0]
896
 	if 1 < len(params) && params[1] != "*" {
885
 	if 1 < len(params) && params[1] != "*" {
908
 			server.logger.Error("services", "unknown error from saregister", err.Error())
897
 			server.logger.Error("services", "unknown error from saregister", err.Error())
909
 			errMsg = client.t("Could not register")
898
 			errMsg = client.t("Could not register")
910
 		}
899
 		}
911
-		nsNotice(rb, errMsg)
900
+		service.Notice(rb, errMsg)
912
 	} else {
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
 		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))
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
 	erase := command == "erase"
908
 	erase := command == "erase"
920
 
909
 
921
 	username := params[0]
910
 	username := params[0]
925
 	}
914
 	}
926
 
915
 
927
 	if username == "" {
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
 		return
918
 		return
930
 	}
919
 	}
931
 
920
 
939
 	} else {
928
 	} else {
940
 		account, err := server.accounts.LoadAccount(username)
929
 		account, err := server.accounts.LoadAccount(username)
941
 		if err == errAccountDoesNotExist {
930
 		if err == errAccountDoesNotExist {
942
-			nsNotice(rb, client.t("Invalid account name"))
931
+			service.Notice(rb, client.t("Invalid account name"))
943
 			return
932
 			return
944
 		} else if err != nil {
933
 		} else if err != nil {
945
-			nsNotice(rb, client.t("Internal error"))
934
+			service.Notice(rb, client.t("Internal error"))
946
 			return
935
 			return
947
 		}
936
 		}
948
 		accountName = account.Name
937
 		accountName = account.Name
950
 	}
939
 	}
951
 
940
 
952
 	if !(accountName == client.AccountName() || client.HasRoleCapabs("accreg")) {
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
 		return
943
 		return
955
 	}
944
 	}
956
 
945
 
957
 	expectedCode := utils.ConfirmationCode(accountName, registeredAt)
946
 	expectedCode := utils.ConfirmationCode(accountName, registeredAt)
958
 	if expectedCode != verificationCode {
947
 	if expectedCode != verificationCode {
959
 		if erase {
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
 		} else {
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
 		return
954
 		return
966
 	}
955
 	}
967
 
956
 
968
 	err := server.accounts.Unregister(accountName, erase)
957
 	err := server.accounts.Unregister(accountName, erase)
969
 	if err == errAccountDoesNotExist {
958
 	if err == errAccountDoesNotExist {
970
-		nsNotice(rb, client.t(err.Error()))
959
+		service.Notice(rb, client.t(err.Error()))
971
 	} else if err != nil {
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
 	} else {
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
 		server.logger.Info("accounts", "client", client.Nick(), "unregistered account", accountName)
964
 		server.logger.Info("accounts", "client", client.Nick(), "unregistered account", accountName)
976
 		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))
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
 	username, code := params[0], params[1]
970
 	username, code := params[0], params[1]
982
 	err := server.accounts.Verify(client, username, code)
971
 	err := server.accounts.Verify(client, username, code)
983
 
972
 
992
 	}
981
 	}
993
 
982
 
994
 	if errorMessage != "" {
983
 	if errorMessage != "" {
995
-		nsNotice(rb, client.t(errorMessage))
984
+		service.Notice(rb, client.t(errorMessage))
996
 		return
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
 	var target string
994
 	var target string
1006
 	var newPassword string
995
 	var newPassword string
1007
 	var errorMessage string
996
 	var errorMessage string
1025
 		} else if params[1] != params[2] {
1014
 		} else if params[1] != params[2] {
1026
 			errorMessage = `Passwords do not match`
1015
 			errorMessage = `Passwords do not match`
1027
 		} else {
1016
 		} else {
1028
-			if !nsLoginThrottleCheck(client, rb) {
1017
+			if !nsLoginThrottleCheck(service, client, rb) {
1029
 				return
1018
 				return
1030
 			}
1019
 			}
1031
 			accountData, err := server.accounts.LoadAccount(target)
1020
 			accountData, err := server.accounts.LoadAccount(target)
1048
 	}
1037
 	}
1049
 
1038
 
1050
 	if errorMessage != "" {
1039
 	if errorMessage != "" {
1051
-		nsNotice(rb, client.t(errorMessage))
1040
+		service.Notice(rb, client.t(errorMessage))
1052
 		return
1041
 		return
1053
 	}
1042
 	}
1054
 
1043
 
1055
 	err := server.accounts.setPassword(target, newPassword, hasPrivs)
1044
 	err := server.accounts.setPassword(target, newPassword, hasPrivs)
1056
 	switch err {
1045
 	switch err {
1057
 	case nil:
1046
 	case nil:
1058
-		nsNotice(rb, client.t("Password changed"))
1047
+		service.Notice(rb, client.t("Password changed"))
1059
 	case errEmptyCredentials:
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
 	case errCredsExternallyManaged:
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
 	case errCASFailed:
1052
 	case errCASFailed:
1064
-		nsNotice(rb, client.t("Try again later"))
1053
+		service.Notice(rb, client.t("Try again later"))
1065
 	default:
1054
 	default:
1066
 		server.logger.Error("internal", "could not upgrade user password:", err.Error())
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
 	newParams := []string{"enforce"}
1061
 	newParams := []string{"enforce"}
1073
 	if len(params) == 0 {
1062
 	if len(params) == 0 {
1074
-		nsGetHandler(server, client, "get", newParams, rb)
1063
+		nsGetHandler(service, server, client, "get", newParams, rb)
1075
 	} else {
1064
 	} else {
1076
 		newParams = append(newParams, params[0])
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
 	var verb string
1071
 	var verb string
1083
 
1072
 
1084
 	if command == "sessions" {
1073
 	if command == "sessions" {
1091
 
1080
 
1092
 	switch verb {
1081
 	switch verb {
1093
 	case "list":
1082
 	case "list":
1094
-		nsClientsListHandler(server, client, params, rb)
1083
+		nsClientsListHandler(service, server, client, params, rb)
1095
 	case "logout":
1084
 	case "logout":
1096
-		nsClientsLogoutHandler(server, client, params, rb)
1085
+		nsClientsLogoutHandler(service, server, client, params, rb)
1097
 	default:
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
 	target := client
1092
 	target := client
1104
 	hasPrivs := client.HasRoleCapabs("local_ban")
1093
 	hasPrivs := client.HasRoleCapabs("local_ban")
1105
 	if 0 < len(params) {
1094
 	if 0 < len(params) {
1106
 		target = server.clients.Get(params[0])
1095
 		target = server.clients.Get(params[0])
1107
 		if target == nil {
1096
 		if target == nil {
1108
-			nsNotice(rb, client.t("No such nick"))
1097
+			service.Notice(rb, client.t("No such nick"))
1109
 			return
1098
 			return
1110
 		}
1099
 		}
1111
 		if target != client && !hasPrivs {
1100
 		if target != client && !hasPrivs {
1112
-			nsNotice(rb, client.t("Command restricted"))
1101
+			service.Notice(rb, client.t("Command restricted"))
1113
 			return
1102
 			return
1114
 		}
1103
 		}
1115
 	}
1104
 	}
1116
 
1105
 
1117
 	sessionData, currentIndex := target.AllSessionData(rb.session, hasPrivs)
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
 	for i, session := range sessionData {
1108
 	for i, session := range sessionData {
1120
 		if currentIndex == i {
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
 		} else {
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
 		if session.deviceID != "" {
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
 		if hasPrivs {
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
 		if session.certfp != "" {
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
 	if len(params) < 1 {
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
 		return
1133
 		return
1145
 	}
1134
 	}
1146
 
1135
 
1149
 		// CLIENTS LOGOUT [nickname] [client ID]
1138
 		// CLIENTS LOGOUT [nickname] [client ID]
1150
 		target = server.clients.Get(params[0])
1139
 		target = server.clients.Get(params[0])
1151
 		if target == nil {
1140
 		if target == nil {
1152
-			nsNotice(rb, client.t("No such nick"))
1141
+			service.Notice(rb, client.t("No such nick"))
1153
 			return
1142
 			return
1154
 		}
1143
 		}
1155
 		// User must have "local_kill" privileges to logout other user sessions.
1144
 		// User must have "local_kill" privileges to logout other user sessions.
1156
 		if target != client {
1145
 		if target != client {
1157
 			oper := client.Oper()
1146
 			oper := client.Oper()
1158
 			if oper == nil || !oper.Class.Capabilities.Has("local_kill") {
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
 				return
1149
 				return
1161
 			}
1150
 			}
1162
 		}
1151
 		}
1167
 	if strings.ToLower(params[0]) != "all" {
1156
 	if strings.ToLower(params[0]) != "all" {
1168
 		sessionID, err := strconv.ParseInt(params[0], 10, 64)
1157
 		sessionID, err := strconv.ParseInt(params[0], 10, 64)
1169
 		if err != nil {
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
 			return
1160
 			return
1172
 		}
1161
 		}
1173
 		// Find the client ID that the user requested to logout.
1162
 		// Find the client ID that the user requested to logout.
1178
 			}
1167
 			}
1179
 		}
1168
 		}
1180
 		if sessionToDestroy == nil {
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
 			return
1171
 			return
1183
 		}
1172
 		}
1184
 	}
1173
 	}
1186
 	target.destroy(sessionToDestroy)
1175
 	target.destroy(sessionToDestroy)
1187
 	if (sessionToDestroy != nil && rb.session != sessionToDestroy) || client != target {
1176
 	if (sessionToDestroy != nil && rb.session != sessionToDestroy) || client != target {
1188
 		if sessionToDestroy != nil {
1177
 		if sessionToDestroy != nil {
1189
-			nsNotice(rb, client.t("Successfully logged out session"))
1178
+			service.Notice(rb, client.t("Successfully logged out session"))
1190
 		} else {
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
 	verb := strings.ToLower(params[0])
1186
 	verb := strings.ToLower(params[0])
1198
 	params = params[1:]
1187
 	params = params[1:]
1199
 	var target, certfp string
1188
 	var target, certfp string
1211
 		} else if len(params) == 0 && verb == "add" && rb.session.certfp != "" {
1200
 		} else if len(params) == 0 && verb == "add" && rb.session.certfp != "" {
1212
 			certfp = rb.session.certfp // #1059
1201
 			certfp = rb.session.certfp // #1059
1213
 		} else {
1202
 		} else {
1214
-			nsNotice(rb, client.t("Invalid parameters"))
1203
+			service.Notice(rb, client.t("Invalid parameters"))
1215
 			return
1204
 			return
1216
 		}
1205
 		}
1217
 	default:
1206
 	default:
1218
-		nsNotice(rb, client.t("Invalid parameters"))
1207
+		service.Notice(rb, client.t("Invalid parameters"))
1219
 		return
1208
 		return
1220
 	}
1209
 	}
1221
 
1210
 
1222
 	hasPrivs := client.HasRoleCapabs("accreg")
1211
 	hasPrivs := client.HasRoleCapabs("accreg")
1223
 	if target != "" && !hasPrivs {
1212
 	if target != "" && !hasPrivs {
1224
-		nsNotice(rb, client.t("Insufficient privileges"))
1213
+		service.Notice(rb, client.t("Insufficient privileges"))
1225
 		return
1214
 		return
1226
 	} else if target == "" {
1215
 	} else if target == "" {
1227
 		target = client.Account()
1216
 		target = client.Account()
1228
 		if target == "" {
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
 			return
1219
 			return
1231
 		}
1220
 		}
1232
 	}
1221
 	}
1236
 	case "list":
1225
 	case "list":
1237
 		accountData, err := server.accounts.LoadAccount(target)
1226
 		accountData, err := server.accounts.LoadAccount(target)
1238
 		if err == errAccountDoesNotExist {
1227
 		if err == errAccountDoesNotExist {
1239
-			nsNotice(rb, client.t("Account does not exist"))
1228
+			service.Notice(rb, client.t("Account does not exist"))
1240
 			return
1229
 			return
1241
 		} else if err != nil {
1230
 		} else if err != nil {
1242
-			nsNotice(rb, client.t("An error occurred"))
1231
+			service.Notice(rb, client.t("An error occurred"))
1243
 			return
1232
 			return
1244
 		}
1233
 		}
1245
 		certfps := accountData.Credentials.Certfps
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
 		for i, certfp := range certfps {
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
 		return
1239
 		return
1251
 	case "add":
1240
 	case "add":
1257
 	switch err {
1246
 	switch err {
1258
 	case nil:
1247
 	case nil:
1259
 		if verb == "add" {
1248
 		if verb == "add" {
1260
-			nsNotice(rb, client.t("Certificate fingerprint successfully added"))
1249
+			service.Notice(rb, client.t("Certificate fingerprint successfully added"))
1261
 		} else {
1250
 		} else {
1262
-			nsNotice(rb, client.t("Certificate fingerprint successfully removed"))
1251
+			service.Notice(rb, client.t("Certificate fingerprint successfully removed"))
1263
 		}
1252
 		}
1264
 	case errNoop:
1253
 	case errNoop:
1265
 		if verb == "add" {
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
 		} else {
1256
 		} else {
1268
-			nsNotice(rb, client.t("Certificate fingerprint not found"))
1257
+			service.Notice(rb, client.t("Certificate fingerprint not found"))
1269
 		}
1258
 		}
1270
 	case errAccountDoesNotExist:
1259
 	case errAccountDoesNotExist:
1271
-		nsNotice(rb, client.t("Account does not exist"))
1260
+		service.Notice(rb, client.t("Account does not exist"))
1272
 	case errLimitExceeded:
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
 	case utils.ErrInvalidCertfp:
1263
 	case utils.ErrInvalidCertfp:
1275
-		nsNotice(rb, client.t("Invalid certificate fingerprint"))
1264
+		service.Notice(rb, client.t("Invalid certificate fingerprint"))
1276
 	case errCertfpAlreadyExists:
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
 	case errEmptyCredentials:
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
 	case errCredsExternallyManaged:
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
 	case errCASFailed:
1271
 	case errCASFailed:
1283
-		nsNotice(rb, client.t("Try again later"))
1272
+		service.Notice(rb, client.t("Try again later"))
1284
 	default:
1273
 	default:
1285
 		server.logger.Error("internal", "could not modify certificates:", err.Error())
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
 	subCmd := strings.ToLower(params[0])
1280
 	subCmd := strings.ToLower(params[0])
1292
 	params = params[1:]
1281
 	params = params[1:]
1293
 	switch subCmd {
1282
 	switch subCmd {
1294
 	case "add":
1283
 	case "add":
1295
-		nsSuspendAddHandler(server, client, command, params, rb)
1284
+		nsSuspendAddHandler(service, server, client, command, params, rb)
1296
 	case "del", "delete", "remove":
1285
 	case "del", "delete", "remove":
1297
-		nsSuspendRemoveHandler(server, client, command, params, rb)
1286
+		nsSuspendRemoveHandler(service, server, client, command, params, rb)
1298
 	case "list":
1287
 	case "list":
1299
-		nsSuspendListHandler(server, client, command, params, rb)
1288
+		nsSuspendListHandler(service, server, client, command, params, rb)
1300
 	default:
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
 	if len(params) == 0 {
1295
 	if len(params) == 0 {
1307
-		nsNotice(rb, client.t("Invalid parameters"))
1296
+		service.Notice(rb, client.t("Invalid parameters"))
1308
 		return
1297
 		return
1309
 	}
1298
 	}
1310
 
1299
 
1316
 		var err error
1305
 		var err error
1317
 		cDuration, err := custime.ParseDuration(params[1])
1306
 		cDuration, err := custime.ParseDuration(params[1])
1318
 		if err != nil {
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
 			return
1309
 			return
1321
 		}
1310
 		}
1322
 		duration = time.Duration(cDuration)
1311
 		duration = time.Duration(cDuration)
1333
 	err := server.accounts.Suspend(account, duration, name, reason)
1322
 	err := server.accounts.Suspend(account, duration, name, reason)
1334
 	switch err {
1323
 	switch err {
1335
 	case nil:
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
 	case errAccountDoesNotExist:
1326
 	case errAccountDoesNotExist:
1338
-		nsNotice(rb, client.t("No such account"))
1327
+		service.Notice(rb, client.t("No such account"))
1339
 	default:
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
 	if len(params) == 0 {
1334
 	if len(params) == 0 {
1346
-		nsNotice(rb, client.t("Invalid parameters"))
1335
+		service.Notice(rb, client.t("Invalid parameters"))
1347
 		return
1336
 		return
1348
 	}
1337
 	}
1349
 
1338
 
1350
 	err := server.accounts.Unsuspend(params[0])
1339
 	err := server.accounts.Unsuspend(params[0])
1351
 	switch err {
1340
 	switch err {
1352
 	case nil:
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
 	case errAccountDoesNotExist:
1343
 	case errAccountDoesNotExist:
1355
-		nsNotice(rb, client.t("No such account"))
1344
+		service.Notice(rb, client.t("No such account"))
1356
 	case errNoop:
1345
 	case errNoop:
1357
-		nsNotice(rb, client.t("Account was not suspended"))
1346
+		service.Notice(rb, client.t("Account was not suspended"))
1358
 	default:
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
 func (a ByCreationTime) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
1356
 func (a ByCreationTime) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
1368
 func (a ByCreationTime) Less(i, j int) bool { return a[i].TimeCreated.After(a[j].TimeCreated) }
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
 	suspensions := server.accounts.ListSuspended()
1360
 	suspensions := server.accounts.ListSuspended()
1372
 	sort.Sort(ByCreationTime(suspensions))
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
 	for _, suspension := range suspensions {
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
 	return fmt.Sprintf(client.t("Account %[1]s suspended at %[2]s. Duration: %[3]s. %[4]s"), suspension.AccountName, ts, duration, reason)
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
 	oldName, newName := params[0], params[1]
1382
 	oldName, newName := params[0], params[1]
1394
 	err := server.accounts.Rename(oldName, newName)
1383
 	err := server.accounts.Rename(oldName, newName)
1395
 
1384
 
1396
 	if err != nil {
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
 		return
1387
 		return
1399
 	}
1388
 	}
1400
 
1389
 
1401
-	nsNotice(rb, client.t("Successfully renamed account"))
1390
+	service.Notice(rb, client.t("Successfully renamed account"))
1402
 	if server.Config().Accounts.NickReservation.ForceNickEqualsAccount {
1391
 	if server.Config().Accounts.NickReservation.ForceNickEqualsAccount {
1403
 		if curClient := server.clients.Get(oldName); curClient != nil {
1392
 		if curClient := server.clients.Get(oldName); curClient != nil {
1404
 			renameErr := performNickChange(client.server, client, curClient, nil, newName, rb)
1393
 			renameErr := performNickChange(client.server, client, curClient, nil, newName, rb)
1405
 			if renameErr != nil && renameErr != errNoop {
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 View File

20
 type ircService struct {
20
 type ircService struct {
21
 	Name           string
21
 	Name           string
22
 	ShortName      string
22
 	ShortName      string
23
-	prefix         string
23
+	prefix         string // NUH source of messages from this service
24
 	CommandAliases []string
24
 	CommandAliases []string
25
 	Commands       map[string]*serviceCommand
25
 	Commands       map[string]*serviceCommand
26
 	HelpBanner     string
26
 	HelpBanner     string
30
 type serviceCommand struct {
30
 type serviceCommand struct {
31
 	aliasOf           string   // marks this command as an alias of another
31
 	aliasOf           string   // marks this command as an alias of another
32
 	capabs            []string // oper capabs the given user has to have to access this command
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
 	help              string
34
 	help              string
35
 	helpStrings       []string
35
 	helpStrings       []string
36
 	helpShort         string
36
 	helpShort         string
60
 	return nil
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
 		Name:           "NickServ",
65
 		Name:           "NickServ",
67
 		ShortName:      "NS",
66
 		ShortName:      "NS",
68
 		CommandAliases: []string{"NICKSERV", "NS"},
67
 		CommandAliases: []string{"NICKSERV", "NS"},
69
 		Commands:       nickservCommands,
68
 		Commands:       nickservCommands,
70
 		HelpBanner:     nickservHelp,
69
 		HelpBanner:     nickservHelp,
71
-	},
72
-	"chanserv": {
70
+	}
71
+	chanservService = &ircService{
73
 		Name:           "ChanServ",
72
 		Name:           "ChanServ",
74
 		ShortName:      "CS",
73
 		ShortName:      "CS",
75
 		CommandAliases: []string{"CHANSERV", "CS"},
74
 		CommandAliases: []string{"CHANSERV", "CS"},
76
 		Commands:       chanservCommands,
75
 		Commands:       chanservCommands,
77
 		HelpBanner:     chanservHelp,
76
 		HelpBanner:     chanservHelp,
78
-	},
79
-	"hostserv": {
77
+	}
78
+	hostservService = &ircService{
80
 		Name:           "HostServ",
79
 		Name:           "HostServ",
81
 		ShortName:      "HS",
80
 		ShortName:      "HS",
82
 		CommandAliases: []string{"HOSTSERV", "HS"},
81
 		CommandAliases: []string{"HOSTSERV", "HS"},
83
 		Commands:       hostservCommands,
82
 		Commands:       hostservCommands,
84
 		HelpBanner:     hostservHelp,
83
 		HelpBanner:     hostservHelp,
85
-	},
86
-	"histserv": {
84
+	}
85
+	histservService = &ircService{
87
 		Name:           "HistServ",
86
 		Name:           "HistServ",
88
 		ShortName:      "HISTSERV",
87
 		ShortName:      "HISTSERV",
89
 		CommandAliases: []string{"HISTSERV"},
88
 		CommandAliases: []string{"HISTSERV"},
90
 		Commands:       histservCommands,
89
 		Commands:       histservCommands,
91
 		HelpBanner:     histservHelp,
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
 // all service commands at the protocol level, by uppercase command name
106
 // all service commands at the protocol level, by uppercase command name
212
 	if commandName == "help" {
223
 	if commandName == "help" {
213
 		serviceHelpHandler(service, server, client, params, rb)
224
 		serviceHelpHandler(service, server, client, params, rb)
214
 	} else {
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
 

Loading…
Cancel
Save