Browse Source

Centralise all command handlers in handlers.go

tags/v0.11.0-beta
Daniel Oaks 6 years ago
parent
commit
47d2ce351c
16 changed files with 2629 additions and 2661 deletions
  1. 0
    233
      irc/accountreg.go
  2. 0
    213
      irc/accounts.go
  3. 0
    63
      irc/capability.go
  4. 0
    7
      irc/chanserv.go
  5. 0
    76
      irc/debug.go
  6. 0
    271
      irc/dline.go
  7. 0
    74
      irc/gateways.go
  8. 2628
    0
      irc/handlers.go
  9. 0
    40
      irc/help.go
  10. 0
    235
      irc/kline.go
  11. 0
    129
      irc/modes.go
  12. 0
    138
      irc/monitor.go
  13. 0
    22
      irc/nickname.go
  14. 0
    7
      irc/nickserv.go
  15. 0
    49
      irc/roleplay.go
  16. 1
    1104
      irc/server.go

+ 0
- 233
irc/accountreg.go View File

@@ -4,18 +4,9 @@
4 4
 package irc
5 5
 
6 6
 import (
7
-	"encoding/json"
8 7
 	"errors"
9 8
 	"fmt"
10
-	"log"
11
-	"strconv"
12
-	"strings"
13
-	"time"
14 9
 
15
-	"github.com/goshuirc/irc-go/ircfmt"
16
-	"github.com/goshuirc/irc-go/ircmsg"
17
-	"github.com/oragono/oragono/irc/passwd"
18
-	"github.com/oragono/oragono/irc/sno"
19 10
 	"github.com/tidwall/buntdb"
20 11
 )
21 12
 
@@ -60,21 +51,6 @@ func NewAccountRegistration(config AccountRegistrationConfig) (accountReg Accoun
60 51
 	return accountReg
61 52
 }
62 53
 
63
-// accHandler parses the ACC command.
64
-func accHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
65
-	subcommand := strings.ToLower(msg.Params[0])
66
-
67
-	if subcommand == "register" {
68
-		return accRegisterHandler(server, client, msg)
69
-	} else if subcommand == "verify" {
70
-		client.Notice(client.t("VERIFY is not yet implemented"))
71
-	} else {
72
-		client.Send(nil, server.name, ERR_UNKNOWNERROR, client.nick, "ACC", msg.Params[0], client.t("Unknown subcommand"))
73
-	}
74
-
75
-	return false
76
-}
77
-
78 54
 // removeFailedAccRegisterData removes the data created by ACC REGISTER if the account creation fails early.
79 55
 func removeFailedAccRegisterData(store *buntdb.DB, account string) {
80 56
 	// error is ignored here, we can't do much about it anyways
@@ -86,212 +62,3 @@ func removeFailedAccRegisterData(store *buntdb.DB, account string) {
86 62
 		return nil
87 63
 	})
88 64
 }
89
-
90
-// accRegisterHandler parses the ACC REGISTER command.
91
-func accRegisterHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
92
-	// make sure reg is enabled
93
-	if !server.accountRegistration.Enabled {
94
-		client.Send(nil, server.name, ERR_REG_UNSPECIFIED_ERROR, client.nick, "*", client.t("Account registration is disabled"))
95
-		return false
96
-	}
97
-
98
-	// clients can't reg new accounts if they're already logged in
99
-	if client.LoggedIntoAccount() {
100
-		if server.accountRegistration.AllowMultiplePerConnection {
101
-			client.LogoutOfAccount()
102
-		} else {
103
-			client.Send(nil, server.name, ERR_REG_UNSPECIFIED_ERROR, client.nick, "*", client.t("You're already logged into an account"))
104
-			return false
105
-		}
106
-	}
107
-
108
-	// get and sanitise account name
109
-	account := strings.TrimSpace(msg.Params[1])
110
-	casefoldedAccount, err := CasefoldName(account)
111
-	// probably don't need explicit check for "*" here... but let's do it anyway just to make sure
112
-	if err != nil || msg.Params[1] == "*" {
113
-		client.Send(nil, server.name, ERR_REG_UNSPECIFIED_ERROR, client.nick, account, client.t("Account name is not valid"))
114
-		return false
115
-	}
116
-
117
-	// check whether account exists
118
-	// do it all in one write tx to prevent races
119
-	err = server.store.Update(func(tx *buntdb.Tx) error {
120
-		accountKey := fmt.Sprintf(keyAccountExists, casefoldedAccount)
121
-
122
-		_, err := tx.Get(accountKey)
123
-		if err != buntdb.ErrNotFound {
124
-			//TODO(dan): if account verified key doesn't exist account is not verified, calc the maximum time without verification and expire and continue if need be
125
-			client.Send(nil, server.name, ERR_ACCOUNT_ALREADY_EXISTS, client.nick, account, client.t("Account already exists"))
126
-			return errAccountCreation
127
-		}
128
-
129
-		registeredTimeKey := fmt.Sprintf(keyAccountRegTime, casefoldedAccount)
130
-
131
-		tx.Set(accountKey, "1", nil)
132
-		tx.Set(fmt.Sprintf(keyAccountName, casefoldedAccount), account, nil)
133
-		tx.Set(registeredTimeKey, strconv.FormatInt(time.Now().Unix(), 10), nil)
134
-		return nil
135
-	})
136
-
137
-	// account could not be created and relevant numerics have been dispatched, abort
138
-	if err != nil {
139
-		if err != errAccountCreation {
140
-			client.Send(nil, server.name, ERR_UNKNOWNERROR, client.nick, "ACC", "REGISTER", client.t("Could not register"))
141
-			log.Println("Could not save registration initial data:", err.Error())
142
-		}
143
-		return false
144
-	}
145
-
146
-	// account didn't already exist, continue with account creation and dispatching verification (if required)
147
-	callback := strings.ToLower(msg.Params[2])
148
-	var callbackNamespace, callbackValue string
149
-
150
-	if callback == "*" {
151
-		callbackNamespace = "*"
152
-	} else if strings.Contains(callback, ":") {
153
-		callbackValues := strings.SplitN(callback, ":", 2)
154
-		callbackNamespace, callbackValue = callbackValues[0], callbackValues[1]
155
-	} else {
156
-		callbackNamespace = server.accountRegistration.EnabledCallbacks[0]
157
-		callbackValue = callback
158
-	}
159
-
160
-	// ensure the callback namespace is valid
161
-	// need to search callback list, maybe look at using a map later?
162
-	var callbackValid bool
163
-	for _, name := range server.accountRegistration.EnabledCallbacks {
164
-		if callbackNamespace == name {
165
-			callbackValid = true
166
-		}
167
-	}
168
-
169
-	if !callbackValid {
170
-		client.Send(nil, server.name, ERR_REG_INVALID_CALLBACK, client.nick, account, callbackNamespace, client.t("Callback namespace is not supported"))
171
-		removeFailedAccRegisterData(server.store, casefoldedAccount)
172
-		return false
173
-	}
174
-
175
-	// get credential type/value
176
-	var credentialType, credentialValue string
177
-
178
-	if len(msg.Params) > 4 {
179
-		credentialType = strings.ToLower(msg.Params[3])
180
-		credentialValue = msg.Params[4]
181
-	} else if len(msg.Params) == 4 {
182
-		credentialType = "passphrase" // default from the spec
183
-		credentialValue = msg.Params[3]
184
-	} else {
185
-		client.Send(nil, server.name, ERR_NEEDMOREPARAMS, client.nick, msg.Command, client.t("Not enough parameters"))
186
-		removeFailedAccRegisterData(server.store, casefoldedAccount)
187
-		return false
188
-	}
189
-
190
-	// ensure the credential type is valid
191
-	var credentialValid bool
192
-	for _, name := range server.accountRegistration.EnabledCredentialTypes {
193
-		if credentialType == name {
194
-			credentialValid = true
195
-		}
196
-	}
197
-	if credentialType == "certfp" && client.certfp == "" {
198
-		client.Send(nil, server.name, ERR_REG_INVALID_CRED_TYPE, client.nick, credentialType, callbackNamespace, client.t("You are not using a TLS certificate"))
199
-		removeFailedAccRegisterData(server.store, casefoldedAccount)
200
-		return false
201
-	}
202
-
203
-	if !credentialValid {
204
-		client.Send(nil, server.name, ERR_REG_INVALID_CRED_TYPE, client.nick, credentialType, callbackNamespace, client.t("Credential type is not supported"))
205
-		removeFailedAccRegisterData(server.store, casefoldedAccount)
206
-		return false
207
-	}
208
-
209
-	// store details
210
-	err = server.store.Update(func(tx *buntdb.Tx) error {
211
-		// certfp special lookup key
212
-		if credentialType == "certfp" {
213
-			assembledKeyCertToAccount := fmt.Sprintf(keyCertToAccount, client.certfp)
214
-
215
-			// make sure certfp doesn't already exist because that'd be silly
216
-			_, err := tx.Get(assembledKeyCertToAccount)
217
-			if err != buntdb.ErrNotFound {
218
-				return errCertfpAlreadyExists
219
-			}
220
-
221
-			tx.Set(assembledKeyCertToAccount, casefoldedAccount, nil)
222
-		}
223
-
224
-		// make creds
225
-		var creds AccountCredentials
226
-
227
-		// always set passphrase salt
228
-		creds.PassphraseSalt, err = passwd.NewSalt()
229
-		if err != nil {
230
-			return fmt.Errorf("Could not create passphrase salt: %s", err.Error())
231
-		}
232
-
233
-		if credentialType == "certfp" {
234
-			creds.Certificate = client.certfp
235
-		} else if credentialType == "passphrase" {
236
-			creds.PassphraseHash, err = server.passwords.GenerateFromPassword(creds.PassphraseSalt, credentialValue)
237
-			if err != nil {
238
-				return fmt.Errorf("Could not hash password: %s", err)
239
-			}
240
-		}
241
-		credText, err := json.Marshal(creds)
242
-		if err != nil {
243
-			return fmt.Errorf("Could not marshal creds: %s", err)
244
-		}
245
-		tx.Set(fmt.Sprintf(keyAccountCredentials, account), string(credText), nil)
246
-
247
-		return nil
248
-	})
249
-
250
-	// details could not be stored and relevant numerics have been dispatched, abort
251
-	if err != nil {
252
-		errMsg := "Could not register"
253
-		if err == errCertfpAlreadyExists {
254
-			errMsg = "An account already exists for your certificate fingerprint"
255
-		}
256
-		client.Send(nil, server.name, ERR_UNKNOWNERROR, client.nick, "ACC", "REGISTER", errMsg)
257
-		log.Println("Could not save registration creds:", err.Error())
258
-		removeFailedAccRegisterData(server.store, casefoldedAccount)
259
-		return false
260
-	}
261
-
262
-	// automatically complete registration
263
-	if callbackNamespace == "*" {
264
-		err = server.store.Update(func(tx *buntdb.Tx) error {
265
-			tx.Set(fmt.Sprintf(keyAccountVerified, casefoldedAccount), "1", nil)
266
-
267
-			// load acct info inside store tx
268
-			account := ClientAccount{
269
-				Name:         strings.TrimSpace(msg.Params[1]),
270
-				RegisteredAt: time.Now(),
271
-				Clients:      []*Client{client},
272
-			}
273
-			//TODO(dan): Consider creating ircd-wide account adding/removing/affecting lock for protecting access to these sorts of variables
274
-			server.accounts[casefoldedAccount] = &account
275
-			client.account = &account
276
-
277
-			client.Send(nil, server.name, RPL_REGISTRATION_SUCCESS, client.nick, account.Name, client.t("Account created"))
278
-			client.Send(nil, server.name, RPL_LOGGEDIN, client.nick, client.nickMaskString, account.Name, fmt.Sprintf(client.t("You are now logged in as %s"), account.Name))
279
-			client.Send(nil, server.name, RPL_SASLSUCCESS, client.nick, client.t("Authentication successful"))
280
-			server.snomasks.Send(sno.LocalAccounts, fmt.Sprintf(ircfmt.Unescape("Account registered $c[grey][$r%s$c[grey]] by $c[grey][$r%s$c[grey]]"), account.Name, client.nickMaskString))
281
-			return nil
282
-		})
283
-		if err != nil {
284
-			client.Send(nil, server.name, ERR_UNKNOWNERROR, client.nick, "ACC", "REGISTER", client.t("Could not register"))
285
-			log.Println("Could not save verification confirmation (*):", err.Error())
286
-			removeFailedAccRegisterData(server.store, casefoldedAccount)
287
-			return false
288
-		}
289
-
290
-		return false
291
-	}
292
-
293
-	// dispatch callback
294
-	client.Notice(fmt.Sprintf("We should dispatch a real callback here to %s:%s", callbackNamespace, callbackValue))
295
-
296
-	return false
297
-}

+ 0
- 213
irc/accounts.go View File

@@ -4,17 +4,13 @@
4 4
 package irc
5 5
 
6 6
 import (
7
-	"bytes"
8
-	"encoding/base64"
9 7
 	"encoding/json"
10 8
 	"errors"
11 9
 	"fmt"
12 10
 	"strconv"
13
-	"strings"
14 11
 	"time"
15 12
 
16 13
 	"github.com/goshuirc/irc-go/ircfmt"
17
-	"github.com/goshuirc/irc-go/ircmsg"
18 14
 	"github.com/oragono/oragono/irc/caps"
19 15
 	"github.com/oragono/oragono/irc/sno"
20 16
 	"github.com/tidwall/buntdb"
@@ -87,163 +83,6 @@ func loadAccount(server *Server, tx *buntdb.Tx, accountKey string) *ClientAccoun
87 83
 	return &accountInfo
88 84
 }
89 85
 
90
-// authenticateHandler parses the AUTHENTICATE command (for SASL authentication).
91
-func authenticateHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
92
-	// sasl abort
93
-	if !server.accountAuthenticationEnabled || len(msg.Params) == 1 && msg.Params[0] == "*" {
94
-		client.Send(nil, server.name, ERR_SASLABORTED, client.nick, client.t("SASL authentication aborted"))
95
-		client.saslInProgress = false
96
-		client.saslMechanism = ""
97
-		client.saslValue = ""
98
-		return false
99
-	}
100
-
101
-	// start new sasl session
102
-	if !client.saslInProgress {
103
-		mechanism := strings.ToUpper(msg.Params[0])
104
-		_, mechanismIsEnabled := EnabledSaslMechanisms[mechanism]
105
-
106
-		if mechanismIsEnabled {
107
-			client.saslInProgress = true
108
-			client.saslMechanism = mechanism
109
-			client.Send(nil, server.name, "AUTHENTICATE", "+")
110
-		} else {
111
-			client.Send(nil, server.name, ERR_SASLFAIL, client.nick, client.t("SASL authentication failed"))
112
-		}
113
-
114
-		return false
115
-	}
116
-
117
-	// continue existing sasl session
118
-	rawData := msg.Params[0]
119
-
120
-	if len(rawData) > 400 {
121
-		client.Send(nil, server.name, ERR_SASLTOOLONG, client.nick, client.t("SASL message too long"))
122
-		client.saslInProgress = false
123
-		client.saslMechanism = ""
124
-		client.saslValue = ""
125
-		return false
126
-	} else if len(rawData) == 400 {
127
-		client.saslValue += rawData
128
-		// allow 4 'continuation' lines before rejecting for length
129
-		if len(client.saslValue) > 400*4 {
130
-			client.Send(nil, server.name, ERR_SASLFAIL, client.nick, client.t("SASL authentication failed: Passphrase too long"))
131
-			client.saslInProgress = false
132
-			client.saslMechanism = ""
133
-			client.saslValue = ""
134
-			return false
135
-		}
136
-		return false
137
-	}
138
-	if rawData != "+" {
139
-		client.saslValue += rawData
140
-	}
141
-
142
-	var data []byte
143
-	var err error
144
-	if client.saslValue != "+" {
145
-		data, err = base64.StdEncoding.DecodeString(client.saslValue)
146
-		if err != nil {
147
-			client.Send(nil, server.name, ERR_SASLFAIL, client.nick, client.t("SASL authentication failed: Invalid b64 encoding"))
148
-			client.saslInProgress = false
149
-			client.saslMechanism = ""
150
-			client.saslValue = ""
151
-			return false
152
-		}
153
-	}
154
-
155
-	// call actual handler
156
-	handler, handlerExists := EnabledSaslMechanisms[client.saslMechanism]
157
-
158
-	// like 100% not required, but it's good to be safe I guess
159
-	if !handlerExists {
160
-		client.Send(nil, server.name, ERR_SASLFAIL, client.nick, client.t("SASL authentication failed"))
161
-		client.saslInProgress = false
162
-		client.saslMechanism = ""
163
-		client.saslValue = ""
164
-		return false
165
-	}
166
-
167
-	// let the SASL handler do its thing
168
-	exiting := handler(server, client, client.saslMechanism, data)
169
-
170
-	// wait 'til SASL is done before emptying the sasl vars
171
-	client.saslInProgress = false
172
-	client.saslMechanism = ""
173
-	client.saslValue = ""
174
-
175
-	return exiting
176
-}
177
-
178
-// authPlainHandler parses the SASL PLAIN mechanism.
179
-func authPlainHandler(server *Server, client *Client, mechanism string, value []byte) bool {
180
-	splitValue := bytes.Split(value, []byte{'\000'})
181
-
182
-	var accountKey, authzid string
183
-
184
-	if len(splitValue) == 3 {
185
-		accountKey = string(splitValue[0])
186
-		authzid = string(splitValue[1])
187
-
188
-		if accountKey == "" {
189
-			accountKey = authzid
190
-		} else if accountKey != authzid {
191
-			client.Send(nil, server.name, ERR_SASLFAIL, client.nick, client.t("SASL authentication failed: authcid and authzid should be the same"))
192
-			return false
193
-		}
194
-	} else {
195
-		client.Send(nil, server.name, ERR_SASLFAIL, client.nick, client.t("SASL authentication failed: Invalid auth blob"))
196
-		return false
197
-	}
198
-
199
-	// keep it the same as in the REG CREATE stage
200
-	accountKey, err := CasefoldName(accountKey)
201
-	if err != nil {
202
-		client.Send(nil, server.name, ERR_SASLFAIL, client.nick, client.t("SASL authentication failed: Bad account name"))
203
-		return false
204
-	}
205
-
206
-	// load and check acct data all in one update to prevent races.
207
-	// as noted elsewhere, change to proper locking for Account type later probably
208
-	err = server.store.Update(func(tx *buntdb.Tx) error {
209
-		// confirm account is verified
210
-		_, err = tx.Get(fmt.Sprintf(keyAccountVerified, accountKey))
211
-		if err != nil {
212
-			return errSaslFail
213
-		}
214
-
215
-		creds, err := loadAccountCredentials(tx, accountKey)
216
-		if err != nil {
217
-			return err
218
-		}
219
-
220
-		// ensure creds are valid
221
-		password := string(splitValue[2])
222
-		if len(creds.PassphraseHash) < 1 || len(creds.PassphraseSalt) < 1 || len(password) < 1 {
223
-			return errSaslFail
224
-		}
225
-		err = server.passwords.CompareHashAndPassword(creds.PassphraseHash, creds.PassphraseSalt, password)
226
-
227
-		// succeeded, load account info if necessary
228
-		account, exists := server.accounts[accountKey]
229
-		if !exists {
230
-			account = loadAccount(server, tx, accountKey)
231
-		}
232
-
233
-		client.LoginToAccount(account)
234
-
235
-		return err
236
-	})
237
-
238
-	if err != nil {
239
-		client.Send(nil, server.name, ERR_SASLFAIL, client.nick, client.t("SASL authentication failed"))
240
-		return false
241
-	}
242
-
243
-	client.successfulSaslAuth()
244
-	return false
245
-}
246
-
247 86
 // LoginToAccount logs the client into the given account.
248 87
 func (client *Client) LoginToAccount(account *ClientAccount) {
249 88
 	if client.account == account {
@@ -292,58 +131,6 @@ func (client *Client) LogoutOfAccount() {
292 131
 	}
293 132
 }
294 133
 
295
-// authExternalHandler parses the SASL EXTERNAL mechanism.
296
-func authExternalHandler(server *Server, client *Client, mechanism string, value []byte) bool {
297
-	if client.certfp == "" {
298
-		client.Send(nil, server.name, ERR_SASLFAIL, client.nick, client.t("SASL authentication failed, you are not connecting with a certificate"))
299
-		return false
300
-	}
301
-
302
-	err := server.store.Update(func(tx *buntdb.Tx) error {
303
-		// certfp lookup key
304
-		accountKey, err := tx.Get(fmt.Sprintf(keyCertToAccount, client.certfp))
305
-		if err != nil {
306
-			return errSaslFail
307
-		}
308
-
309
-		// confirm account exists
310
-		_, err = tx.Get(fmt.Sprintf(keyAccountExists, accountKey))
311
-		if err != nil {
312
-			return errSaslFail
313
-		}
314
-
315
-		// confirm account is verified
316
-		_, err = tx.Get(fmt.Sprintf(keyAccountVerified, accountKey))
317
-		if err != nil {
318
-			return errSaslFail
319
-		}
320
-
321
-		// confirm the certfp in that account's credentials
322
-		creds, err := loadAccountCredentials(tx, accountKey)
323
-		if err != nil || creds.Certificate != client.certfp {
324
-			return errSaslFail
325
-		}
326
-
327
-		// succeeded, load account info if necessary
328
-		account, exists := server.accounts[accountKey]
329
-		if !exists {
330
-			account = loadAccount(server, tx, accountKey)
331
-		}
332
-
333
-		client.LoginToAccount(account)
334
-
335
-		return nil
336
-	})
337
-
338
-	if err != nil {
339
-		client.Send(nil, server.name, ERR_SASLFAIL, client.nick, client.t("SASL authentication failed"))
340
-		return false
341
-	}
342
-
343
-	client.successfulSaslAuth()
344
-	return false
345
-}
346
-
347 134
 // successfulSaslAuth means that a SASL auth attempt completed successfully, and is used to dispatch messages.
348 135
 func (client *Client) successfulSaslAuth() {
349 136
 	client.Send(nil, client.server.name, RPL_LOGGEDIN, client.nick, client.nickMaskString, client.account.Name, fmt.Sprintf("You are now logged in as %s", client.account.Name))

+ 0
- 63
irc/capability.go View File

@@ -5,9 +5,6 @@
5 5
 package irc
6 6
 
7 7
 import (
8
-	"strings"
9
-
10
-	"github.com/goshuirc/irc-go/ircmsg"
11 8
 	"github.com/oragono/oragono/irc/caps"
12 9
 )
13 10
 
@@ -32,63 +29,3 @@ const (
32 29
 	// CapNegotiated means CAP negotiation has been successfully ended and reg should complete.
33 30
 	CapNegotiated CapState = iota
34 31
 )
35
-
36
-// CAP <subcmd> [<caps>]
37
-func capHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
38
-	subCommand := strings.ToUpper(msg.Params[0])
39
-	capabilities := caps.NewSet()
40
-	var capString string
41
-
42
-	if len(msg.Params) > 1 {
43
-		capString = msg.Params[1]
44
-		strs := strings.Split(capString, " ")
45
-		for _, str := range strs {
46
-			if len(str) > 0 {
47
-				capabilities.Enable(caps.Capability(str))
48
-			}
49
-		}
50
-	}
51
-
52
-	switch subCommand {
53
-	case "LS":
54
-		if !client.registered {
55
-			client.capState = CapNegotiating
56
-		}
57
-		if len(msg.Params) > 1 && msg.Params[1] == "302" {
58
-			client.capVersion = 302
59
-		}
60
-		// weechat 1.4 has a bug here where it won't accept the CAP reply unless it contains
61
-		// the server.name source... otherwise it doesn't respond to the CAP message with
62
-		// anything and just hangs on connection.
63
-		//TODO(dan): limit number of caps and send it multiline in 3.2 style as appropriate.
64
-		client.Send(nil, server.name, "CAP", client.nick, subCommand, SupportedCapabilities.String(client.capVersion, CapValues))
65
-
66
-	case "LIST":
67
-		client.Send(nil, server.name, "CAP", client.nick, subCommand, client.capabilities.String(caps.Cap301, CapValues)) // values not sent on LIST so force 3.1
68
-
69
-	case "REQ":
70
-		if !client.registered {
71
-			client.capState = CapNegotiating
72
-		}
73
-
74
-		// make sure all capabilities actually exist
75
-		for _, capability := range capabilities.List() {
76
-			if !SupportedCapabilities.Has(capability) {
77
-				client.Send(nil, server.name, "CAP", client.nick, "NAK", capString)
78
-				return false
79
-			}
80
-		}
81
-		client.capabilities.Enable(capabilities.List()...)
82
-		client.Send(nil, server.name, "CAP", client.nick, "ACK", capString)
83
-
84
-	case "END":
85
-		if !client.registered {
86
-			client.capState = CapNegotiated
87
-			server.tryRegister(client)
88
-		}
89
-
90
-	default:
91
-		client.Send(nil, server.name, ERR_INVALIDCAPCMD, client.nick, subCommand, client.t("Invalid CAP subcommand"))
92
-	}
93
-	return false
94
-}

+ 0
- 7
irc/chanserv.go View File

@@ -8,16 +8,9 @@ import (
8 8
 	"strings"
9 9
 
10 10
 	"github.com/goshuirc/irc-go/ircfmt"
11
-	"github.com/goshuirc/irc-go/ircmsg"
12 11
 	"github.com/oragono/oragono/irc/sno"
13 12
 )
14 13
 
15
-// csHandler handles the /CS and /CHANSERV commands
16
-func csHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
17
-	server.chanservReceivePrivmsg(client, strings.Join(msg.Params, " "))
18
-	return false
19
-}
20
-
21 14
 func (server *Server) chanservReceiveNotice(client *Client, message string) {
22 15
 	// do nothing
23 16
 }

+ 0
- 76
irc/debug.go View File

@@ -1,76 +0,0 @@
1
-// Copyright (c) 2012-2014 Jeremy Latt
2
-// Copyright (c) 2016 Daniel Oaks <daniel@danieloaks.net>
3
-// released under the MIT license
4
-
5
-package irc
6
-
7
-import (
8
-	"fmt"
9
-	"os"
10
-	"runtime"
11
-	"runtime/debug"
12
-	"runtime/pprof"
13
-	"time"
14
-
15
-	"github.com/goshuirc/irc-go/ircmsg"
16
-)
17
-
18
-// DEBUG GCSTATS/NUMGOROUTINE/etc
19
-func debugHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
20
-	if !client.flags[Operator] {
21
-		return false
22
-	}
23
-
24
-	switch msg.Params[0] {
25
-	case "GCSTATS":
26
-		stats := debug.GCStats{
27
-			Pause:          make([]time.Duration, 10),
28
-			PauseQuantiles: make([]time.Duration, 5),
29
-		}
30
-		debug.ReadGCStats(&stats)
31
-
32
-		client.Notice(fmt.Sprintf("last GC:     %s", stats.LastGC.Format(time.RFC1123)))
33
-		client.Notice(fmt.Sprintf("num GC:      %d", stats.NumGC))
34
-		client.Notice(fmt.Sprintf("pause total: %s", stats.PauseTotal))
35
-		client.Notice(fmt.Sprintf("pause quantiles min%%: %s", stats.PauseQuantiles[0]))
36
-		client.Notice(fmt.Sprintf("pause quantiles 25%%:  %s", stats.PauseQuantiles[1]))
37
-		client.Notice(fmt.Sprintf("pause quantiles 50%%:  %s", stats.PauseQuantiles[2]))
38
-		client.Notice(fmt.Sprintf("pause quantiles 75%%:  %s", stats.PauseQuantiles[3]))
39
-		client.Notice(fmt.Sprintf("pause quantiles max%%: %s", stats.PauseQuantiles[4]))
40
-
41
-	case "NUMGOROUTINE":
42
-		count := runtime.NumGoroutine()
43
-		client.Notice(fmt.Sprintf("num goroutines: %d", count))
44
-
45
-	case "PROFILEHEAP":
46
-		profFile := "oragono.mprof"
47
-		file, err := os.Create(profFile)
48
-		if err != nil {
49
-			client.Notice(fmt.Sprintf("error: %s", err))
50
-			break
51
-		}
52
-		defer file.Close()
53
-		pprof.Lookup("heap").WriteTo(file, 0)
54
-		client.Notice(fmt.Sprintf("written to %s", profFile))
55
-
56
-	case "STARTCPUPROFILE":
57
-		profFile := "oragono.prof"
58
-		file, err := os.Create(profFile)
59
-		if err != nil {
60
-			client.Notice(fmt.Sprintf("error: %s", err))
61
-			break
62
-		}
63
-		if err := pprof.StartCPUProfile(file); err != nil {
64
-			defer file.Close()
65
-			client.Notice(fmt.Sprintf("error: %s", err))
66
-			break
67
-		}
68
-
69
-		client.Notice(fmt.Sprintf("CPU profile writing to %s", profFile))
70
-
71
-	case "STOPCPUPROFILE":
72
-		pprof.StopCPUProfile()
73
-		client.Notice(fmt.Sprintf("CPU profiling stopped"))
74
-	}
75
-	return false
76
-}

+ 0
- 271
irc/dline.go View File

@@ -7,18 +7,11 @@ import (
7 7
 	"errors"
8 8
 	"fmt"
9 9
 	"net"
10
-	"sort"
11 10
 	"sync"
12 11
 	"time"
13 12
 
14
-	"strings"
15
-
16 13
 	"encoding/json"
17 14
 
18
-	"github.com/goshuirc/irc-go/ircfmt"
19
-	"github.com/goshuirc/irc-go/ircmsg"
20
-	"github.com/oragono/oragono/irc/custime"
21
-	"github.com/oragono/oragono/irc/sno"
22 15
 	"github.com/tidwall/buntdb"
23 16
 )
24 17
 
@@ -216,270 +209,6 @@ func (dm *DLineManager) CheckIP(addr net.IP) (isBanned bool, info *IPBanInfo) {
216 209
 	return false, nil
217 210
 }
218 211
 
219
-// DLINE [ANDKILL] [MYSELF] [duration] <ip>/<net> [ON <server>] [reason [| oper reason]]
220
-// DLINE LIST
221
-func dlineHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
222
-	// check oper permissions
223
-	if !client.class.Capabilities["oper:local_ban"] {
224
-		client.Send(nil, server.name, ERR_NOPRIVS, client.nick, msg.Command, client.t("Insufficient oper privs"))
225
-		return false
226
-	}
227
-
228
-	currentArg := 0
229
-
230
-	// if they say LIST, we just list the current dlines
231
-	if len(msg.Params) == currentArg+1 && strings.ToLower(msg.Params[currentArg]) == "list" {
232
-		bans := server.dlines.AllBans()
233
-
234
-		if len(bans) == 0 {
235
-			client.Notice(client.t("No DLINEs have been set!"))
236
-		}
237
-
238
-		for key, info := range bans {
239
-			client.Notice(fmt.Sprintf(client.t("Ban - %[1]s - added by %[2]s - %[3]s"), key, info.OperName, info.BanMessage("%s")))
240
-		}
241
-
242
-		return false
243
-	}
244
-
245
-	// when setting a ban, if they say "ANDKILL" we should also kill all users who match it
246
-	var andKill bool
247
-	if len(msg.Params) > currentArg+1 && strings.ToLower(msg.Params[currentArg]) == "andkill" {
248
-		andKill = true
249
-		currentArg++
250
-	}
251
-
252
-	// when setting a ban that covers the oper's current connection, we require them to say
253
-	// "DLINE MYSELF" so that we're sure they really mean it.
254
-	var dlineMyself bool
255
-	if len(msg.Params) > currentArg+1 && strings.ToLower(msg.Params[currentArg]) == "myself" {
256
-		dlineMyself = true
257
-		currentArg++
258
-	}
259
-
260
-	// duration
261
-	duration, err := custime.ParseDuration(msg.Params[currentArg])
262
-	durationIsUsed := err == nil
263
-	if durationIsUsed {
264
-		currentArg++
265
-	}
266
-
267
-	// get host
268
-	if len(msg.Params) < currentArg+1 {
269
-		client.Send(nil, server.name, ERR_NEEDMOREPARAMS, client.nick, msg.Command, client.t("Not enough parameters"))
270
-		return false
271
-	}
272
-	hostString := msg.Params[currentArg]
273
-	currentArg++
274
-
275
-	// check host
276
-	var hostAddr net.IP
277
-	var hostNet *net.IPNet
278
-
279
-	_, hostNet, err = net.ParseCIDR(hostString)
280
-	if err != nil {
281
-		hostAddr = net.ParseIP(hostString)
282
-	}
283
-
284
-	if hostAddr == nil && hostNet == nil {
285
-		client.Send(nil, server.name, ERR_UNKNOWNERROR, client.nick, msg.Command, client.t("Could not parse IP address or CIDR network"))
286
-		return false
287
-	}
288
-
289
-	if hostNet == nil {
290
-		hostString = hostAddr.String()
291
-		if !dlineMyself && hostAddr.Equal(client.IP()) {
292
-			client.Send(nil, server.name, ERR_UNKNOWNERROR, client.nick, msg.Command, client.t("This ban matches you. To DLINE yourself, you must use the command:  /DLINE MYSELF <arguments>"))
293
-			return false
294
-		}
295
-	} else {
296
-		hostString = hostNet.String()
297
-		if !dlineMyself && hostNet.Contains(client.IP()) {
298
-			client.Send(nil, server.name, ERR_UNKNOWNERROR, client.nick, msg.Command, client.t("This ban matches you. To DLINE yourself, you must use the command:  /DLINE MYSELF <arguments>"))
299
-			return false
300
-		}
301
-	}
302
-
303
-	// check remote
304
-	if len(msg.Params) > currentArg && msg.Params[currentArg] == "ON" {
305
-		client.Send(nil, server.name, ERR_UNKNOWNERROR, client.nick, msg.Command, client.t("Remote servers not yet supported"))
306
-		return false
307
-	}
308
-
309
-	// get comment(s)
310
-	reason := "No reason given"
311
-	operReason := "No reason given"
312
-	if len(msg.Params) > currentArg {
313
-		tempReason := strings.TrimSpace(msg.Params[currentArg])
314
-		if len(tempReason) > 0 && tempReason != "|" {
315
-			tempReasons := strings.SplitN(tempReason, "|", 2)
316
-			if tempReasons[0] != "" {
317
-				reason = tempReasons[0]
318
-			}
319
-			if len(tempReasons) > 1 && tempReasons[1] != "" {
320
-				operReason = tempReasons[1]
321
-			} else {
322
-				operReason = reason
323
-			}
324
-		}
325
-	}
326
-	operName := client.operName
327
-	if operName == "" {
328
-		operName = server.name
329
-	}
330
-
331
-	// assemble ban info
332
-	var banTime *IPRestrictTime
333
-	if durationIsUsed {
334
-		banTime = &IPRestrictTime{
335
-			Duration: duration,
336
-			Expires:  time.Now().Add(duration),
337
-		}
338
-	}
339
-
340
-	info := IPBanInfo{
341
-		Reason:     reason,
342
-		OperReason: operReason,
343
-		OperName:   operName,
344
-		Time:       banTime,
345
-	}
346
-
347
-	// save in datastore
348
-	err = server.store.Update(func(tx *buntdb.Tx) error {
349
-		dlineKey := fmt.Sprintf(keyDlineEntry, hostString)
350
-
351
-		// assemble json from ban info
352
-		b, err := json.Marshal(info)
353
-		if err != nil {
354
-			return err
355
-		}
356
-
357
-		tx.Set(dlineKey, string(b), nil)
358
-
359
-		return nil
360
-	})
361
-
362
-	if err != nil {
363
-		client.Notice(fmt.Sprintf(client.t("Could not successfully save new D-LINE: %s"), err.Error()))
364
-		return false
365
-	}
366
-
367
-	if hostNet == nil {
368
-		server.dlines.AddIP(hostAddr, banTime, reason, operReason, operName)
369
-	} else {
370
-		server.dlines.AddNetwork(*hostNet, banTime, reason, operReason, operName)
371
-	}
372
-
373
-	var snoDescription string
374
-	if durationIsUsed {
375
-		client.Notice(fmt.Sprintf(client.t("Added temporary (%[1]s) D-Line for %[2]s"), duration.String(), hostString))
376
-		snoDescription = fmt.Sprintf(ircfmt.Unescape("%s [%s]$r added temporary (%s) D-Line for %s"), client.nick, operName, duration.String(), hostString)
377
-	} else {
378
-		client.Notice(fmt.Sprintf(client.t("Added D-Line for %s"), hostString))
379
-		snoDescription = fmt.Sprintf(ircfmt.Unescape("%s [%s]$r added D-Line for %s"), client.nick, operName, hostString)
380
-	}
381
-	server.snomasks.Send(sno.LocalXline, snoDescription)
382
-
383
-	var killClient bool
384
-	if andKill {
385
-		var clientsToKill []*Client
386
-		var killedClientNicks []string
387
-		var toKill bool
388
-
389
-		for _, mcl := range server.clients.AllClients() {
390
-			if hostNet == nil {
391
-				toKill = hostAddr.Equal(mcl.IP())
392
-			} else {
393
-				toKill = hostNet.Contains(mcl.IP())
394
-			}
395
-
396
-			if toKill {
397
-				clientsToKill = append(clientsToKill, mcl)
398
-				killedClientNicks = append(killedClientNicks, mcl.nick)
399
-			}
400
-		}
401
-
402
-		for _, mcl := range clientsToKill {
403
-			mcl.exitedSnomaskSent = true
404
-			mcl.Quit(fmt.Sprintf(mcl.t("You have been banned from this server (%s)"), reason))
405
-			if mcl == client {
406
-				killClient = true
407
-			} else {
408
-				// if mcl == client, we kill them below
409
-				mcl.destroy(false)
410
-			}
411
-		}
412
-
413
-		// send snomask
414
-		sort.Strings(killedClientNicks)
415
-		server.snomasks.Send(sno.LocalKills, fmt.Sprintf(ircfmt.Unescape("%s [%s] killed %d clients with a DLINE $c[grey][$r%s$c[grey]]"), client.nick, operName, len(killedClientNicks), strings.Join(killedClientNicks, ", ")))
416
-	}
417
-
418
-	return killClient
419
-}
420
-
421
-func unDLineHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
422
-	// check oper permissions
423
-	if !client.class.Capabilities["oper:local_unban"] {
424
-		client.Send(nil, server.name, ERR_NOPRIVS, client.nick, msg.Command, client.t("Insufficient oper privs"))
425
-		return false
426
-	}
427
-
428
-	// get host
429
-	hostString := msg.Params[0]
430
-
431
-	// check host
432
-	var hostAddr net.IP
433
-	var hostNet *net.IPNet
434
-
435
-	_, hostNet, err := net.ParseCIDR(hostString)
436
-	if err != nil {
437
-		hostAddr = net.ParseIP(hostString)
438
-	}
439
-
440
-	if hostAddr == nil && hostNet == nil {
441
-		client.Send(nil, server.name, ERR_UNKNOWNERROR, client.nick, msg.Command, client.t("Could not parse IP address or CIDR network"))
442
-		return false
443
-	}
444
-
445
-	if hostNet == nil {
446
-		hostString = hostAddr.String()
447
-	} else {
448
-		hostString = hostNet.String()
449
-	}
450
-
451
-	// save in datastore
452
-	err = server.store.Update(func(tx *buntdb.Tx) error {
453
-		dlineKey := fmt.Sprintf(keyDlineEntry, hostString)
454
-
455
-		// check if it exists or not
456
-		val, err := tx.Get(dlineKey)
457
-		if val == "" {
458
-			return errNoExistingBan
459
-		} else if err != nil {
460
-			return err
461
-		}
462
-
463
-		tx.Delete(dlineKey)
464
-		return nil
465
-	})
466
-
467
-	if err != nil {
468
-		client.Send(nil, server.name, ERR_UNKNOWNERROR, client.nick, msg.Command, fmt.Sprintf(client.t("Could not remove ban [%s]"), err.Error()))
469
-		return false
470
-	}
471
-
472
-	if hostNet == nil {
473
-		server.dlines.RemoveIP(hostAddr)
474
-	} else {
475
-		server.dlines.RemoveNetwork(*hostNet)
476
-	}
477
-
478
-	client.Notice(fmt.Sprintf(client.t("Removed D-Line for %s"), hostString))
479
-	server.snomasks.Send(sno.LocalXline, fmt.Sprintf(ircfmt.Unescape("%s$r removed D-Line for %s"), client.nick, hostString))
480
-	return false
481
-}
482
-
483 212
 func (s *Server) loadDLines() {
484 213
 	s.dlines = NewDLineManager()
485 214
 

+ 0
- 74
irc/gateways.go View File

@@ -9,11 +9,9 @@ import (
9 9
 	"errors"
10 10
 	"fmt"
11 11
 	"net"
12
-	"strings"
13 12
 
14 13
 	"github.com/oragono/oragono/irc/passwd"
15 14
 
16
-	"github.com/goshuirc/irc-go/ircmsg"
17 15
 	"github.com/oragono/oragono/irc/utils"
18 16
 )
19 17
 
@@ -62,78 +60,6 @@ func isGatewayAllowed(addr net.Addr, gatewaySpec string) bool {
62 60
 	return gatewayNet.Contains(ip)
63 61
 }
64 62
 
65
-// WEBIRC <password> <gateway> <hostname> <ip> [:flag1 flag2=x flag3]
66
-func webircHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
67
-	// only allow unregistered clients to use this command
68
-	if client.registered || client.proxiedIP != nil {
69
-		return false
70
-	}
71
-
72
-	// process flags
73
-	var secure bool
74
-	if 4 < len(msg.Params) {
75
-		for _, x := range strings.Split(msg.Params[4], " ") {
76
-			// split into key=value
77
-			var key string
78
-			if strings.Contains(x, "=") {
79
-				y := strings.SplitN(x, "=", 2)
80
-				key, _ = y[0], y[1]
81
-			} else {
82
-				key = x
83
-			}
84
-
85
-			lkey := strings.ToLower(key)
86
-			if lkey == "tls" || lkey == "secure" {
87
-				// only accept "tls" flag if the gateway's connection to us is secure as well
88
-				if client.flags[TLS] || utils.AddrIsLocal(client.socket.conn.RemoteAddr()) {
89
-					secure = true
90
-				}
91
-			}
92
-		}
93
-	}
94
-
95
-	for _, info := range server.WebIRCConfig() {
96
-		for _, gateway := range info.Hosts {
97
-			if isGatewayAllowed(client.socket.conn.RemoteAddr(), gateway) {
98
-				// confirm password and/or fingerprint
99
-				givenPassword := msg.Params[0]
100
-				if 0 < len(info.Password) && passwd.ComparePasswordString(info.Password, givenPassword) != nil {
101
-					continue
102
-				}
103
-				if 0 < len(info.Fingerprint) && client.certfp != info.Fingerprint {
104
-					continue
105
-				}
106
-
107
-				proxiedIP := msg.Params[3]
108
-				return client.ApplyProxiedIP(proxiedIP, secure)
109
-			}
110
-		}
111
-	}
112
-
113
-	client.Quit(client.t("WEBIRC command is not usable from your address or incorrect password given"))
114
-	return true
115
-}
116
-
117
-// PROXY TCP4/6 SOURCEIP DESTIP SOURCEPORT DESTPORT
118
-// http://www.haproxy.org/download/1.8/doc/proxy-protocol.txt
119
-func proxyHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
120
-	// only allow unregistered clients to use this command
121
-	if client.registered || client.proxiedIP != nil {
122
-		return false
123
-	}
124
-
125
-	for _, gateway := range server.ProxyAllowedFrom() {
126
-		if isGatewayAllowed(client.socket.conn.RemoteAddr(), gateway) {
127
-			proxiedIP := msg.Params[1]
128
-
129
-			// assume PROXY connections are always secure
130
-			return client.ApplyProxiedIP(proxiedIP, true)
131
-		}
132
-	}
133
-	client.Quit(client.t("PROXY command is not usable from your address"))
134
-	return true
135
-}
136
-
137 63
 // ApplyProxiedIP applies the given IP to the client.
138 64
 func (client *Client) ApplyProxiedIP(proxiedIP string, tls bool) (exiting bool) {
139 65
 	// ensure IP is sane

+ 2628
- 0
irc/handlers.go
File diff suppressed because it is too large
View File


+ 0
- 40
irc/help.go View File

@@ -7,8 +7,6 @@ import (
7 7
 	"fmt"
8 8
 	"sort"
9 9
 	"strings"
10
-
11
-	"github.com/goshuirc/irc-go/ircmsg"
12 10
 )
13 11
 
14 12
 // HelpEntryType represents the different sorts of help entries that can exist.
@@ -687,41 +685,3 @@ func GetHelpIndex(languages []string, helpIndex map[string]string) string {
687 685
 	// 'en' always exists
688 686
 	return helpIndex["en"]
689 687
 }
690
-
691
-// helpHandler returns the appropriate help for the given query.
692
-func helpHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
693
-	argument := strings.ToLower(strings.TrimSpace(strings.Join(msg.Params, " ")))
694
-
695
-	if len(argument) < 1 {
696
-		client.sendHelp("HELPOP", client.t(`HELPOP <argument>
697
-
698
-Get an explanation of <argument>, or "index" for a list of help topics.`))
699
-		return false
700
-	}
701
-
702
-	// handle index
703
-	if argument == "index" {
704
-		if client.flags[Operator] {
705
-			client.sendHelp("HELP", GetHelpIndex(client.languages, HelpIndexOpers))
706
-		} else {
707
-			client.sendHelp("HELP", GetHelpIndex(client.languages, HelpIndex))
708
-		}
709
-		return false
710
-	}
711
-
712
-	helpHandler, exists := Help[argument]
713
-
714
-	if exists && (!helpHandler.oper || (helpHandler.oper && client.flags[Operator])) {
715
-		if helpHandler.textGenerator != nil {
716
-			client.sendHelp(strings.ToUpper(argument), client.t(helpHandler.textGenerator(client)))
717
-		} else {
718
-			client.sendHelp(strings.ToUpper(argument), client.t(helpHandler.text))
719
-		}
720
-	} else {
721
-		args := msg.Params
722
-		args = append(args, client.t("Help not found"))
723
-		client.Send(nil, server.name, ERR_HELPNOTFOUND, args...)
724
-	}
725
-
726
-	return false
727
-}

+ 0
- 235
irc/kline.go View File

@@ -5,17 +5,9 @@ package irc
5 5
 
6 6
 import (
7 7
 	"encoding/json"
8
-	"fmt"
9
-	"sort"
10
-	"strings"
11 8
 	"sync"
12
-	"time"
13 9
 
14
-	"github.com/goshuirc/irc-go/ircfmt"
15 10
 	"github.com/goshuirc/irc-go/ircmatch"
16
-	"github.com/goshuirc/irc-go/ircmsg"
17
-	"github.com/oragono/oragono/irc/custime"
18
-	"github.com/oragono/oragono/irc/sno"
19 11
 	"github.com/tidwall/buntdb"
20 12
 )
21 13
 
@@ -127,233 +119,6 @@ func (km *KLineManager) CheckMasks(masks ...string) (isBanned bool, info *IPBanI
127 119
 	return false, nil
128 120
 }
129 121
 
130
-// KLINE [ANDKILL] [MYSELF] [duration] <mask> [ON <server>] [reason [| oper reason]]
131
-// KLINE LIST
132
-func klineHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
133
-	// check oper permissions
134
-	if !client.class.Capabilities["oper:local_ban"] {
135
-		client.Send(nil, server.name, ERR_NOPRIVS, client.nick, msg.Command, client.t("Insufficient oper privs"))
136
-		return false
137
-	}
138
-
139
-	currentArg := 0
140
-
141
-	// if they say LIST, we just list the current klines
142
-	if len(msg.Params) == currentArg+1 && strings.ToLower(msg.Params[currentArg]) == "list" {
143
-		bans := server.klines.AllBans()
144
-
145
-		if len(bans) == 0 {
146
-			client.Notice("No KLINEs have been set!")
147
-		}
148
-
149
-		for key, info := range bans {
150
-			client.Notice(fmt.Sprintf(client.t("Ban - %s - added by %s - %s"), key, info.OperName, info.BanMessage("%s")))
151
-		}
152
-
153
-		return false
154
-	}
155
-
156
-	// when setting a ban, if they say "ANDKILL" we should also kill all users who match it
157
-	var andKill bool
158
-	if len(msg.Params) > currentArg+1 && strings.ToLower(msg.Params[currentArg]) == "andkill" {
159
-		andKill = true
160
-		currentArg++
161
-	}
162
-
163
-	// when setting a ban that covers the oper's current connection, we require them to say
164
-	// "KLINE MYSELF" so that we're sure they really mean it.
165
-	var klineMyself bool
166
-	if len(msg.Params) > currentArg+1 && strings.ToLower(msg.Params[currentArg]) == "myself" {
167
-		klineMyself = true
168
-		currentArg++
169
-	}
170
-
171
-	// duration
172
-	duration, err := custime.ParseDuration(msg.Params[currentArg])
173
-	durationIsUsed := err == nil
174
-	if durationIsUsed {
175
-		currentArg++
176
-	}
177
-
178
-	// get mask
179
-	if len(msg.Params) < currentArg+1 {
180
-		client.Send(nil, server.name, ERR_NEEDMOREPARAMS, client.nick, msg.Command, client.t("Not enough parameters"))
181
-		return false
182
-	}
183
-	mask := strings.ToLower(msg.Params[currentArg])
184
-	currentArg++
185
-
186
-	// check mask
187
-	if !strings.Contains(mask, "!") && !strings.Contains(mask, "@") {
188
-		mask = mask + "!*@*"
189
-	} else if !strings.Contains(mask, "@") {
190
-		mask = mask + "@*"
191
-	}
192
-
193
-	matcher := ircmatch.MakeMatch(mask)
194
-
195
-	for _, clientMask := range client.AllNickmasks() {
196
-		if !klineMyself && matcher.Match(clientMask) {
197
-			client.Send(nil, server.name, ERR_UNKNOWNERROR, client.nick, msg.Command, client.t("This ban matches you. To KLINE yourself, you must use the command:  /KLINE MYSELF <arguments>"))
198
-			return false
199
-		}
200
-	}
201
-
202
-	// check remote
203
-	if len(msg.Params) > currentArg && msg.Params[currentArg] == "ON" {
204
-		client.Send(nil, server.name, ERR_UNKNOWNERROR, client.nick, msg.Command, client.t("Remote servers not yet supported"))
205
-		return false
206
-	}
207
-
208
-	// get oper name
209
-	operName := client.operName
210
-	if operName == "" {
211
-		operName = server.name
212
-	}
213
-
214
-	// get comment(s)
215
-	reason := "No reason given"
216
-	operReason := "No reason given"
217
-	if len(msg.Params) > currentArg {
218
-		tempReason := strings.TrimSpace(msg.Params[currentArg])
219
-		if len(tempReason) > 0 && tempReason != "|" {
220
-			tempReasons := strings.SplitN(tempReason, "|", 2)
221
-			if tempReasons[0] != "" {
222
-				reason = tempReasons[0]
223
-			}
224
-			if len(tempReasons) > 1 && tempReasons[1] != "" {
225
-				operReason = tempReasons[1]
226
-			} else {
227
-				operReason = reason
228
-			}
229
-		}
230
-	}
231
-
232
-	// assemble ban info
233
-	var banTime *IPRestrictTime
234
-	if durationIsUsed {
235
-		banTime = &IPRestrictTime{
236
-			Duration: duration,
237
-			Expires:  time.Now().Add(duration),
238
-		}
239
-	}
240
-
241
-	info := IPBanInfo{
242
-		Reason:     reason,
243
-		OperReason: operReason,
244
-		OperName:   operName,
245
-		Time:       banTime,
246
-	}
247
-
248
-	// save in datastore
249
-	err = server.store.Update(func(tx *buntdb.Tx) error {
250
-		klineKey := fmt.Sprintf(keyKlineEntry, mask)
251
-
252
-		// assemble json from ban info
253
-		b, err := json.Marshal(info)
254
-		if err != nil {
255
-			return err
256
-		}
257
-
258
-		tx.Set(klineKey, string(b), nil)
259
-
260
-		return nil
261
-	})
262
-
263
-	if err != nil {
264
-		client.Notice(fmt.Sprintf(client.t("Could not successfully save new K-LINE: %s"), err.Error()))
265
-		return false
266
-	}
267
-
268
-	server.klines.AddMask(mask, banTime, reason, operReason, operName)
269
-
270
-	var snoDescription string
271
-	if durationIsUsed {
272
-		client.Notice(fmt.Sprintf(client.t("Added temporary (%[1]s) K-Line for %[2]s"), duration.String(), mask))
273
-		snoDescription = fmt.Sprintf(ircfmt.Unescape("%s [%s]$r added temporary (%s) K-Line for %s"), client.nick, operName, duration.String(), mask)
274
-	} else {
275
-		client.Notice(fmt.Sprintf(client.t("Added K-Line for %s"), mask))
276
-		snoDescription = fmt.Sprintf(ircfmt.Unescape("%s [%s]$r added K-Line for %s"), client.nick, operName, mask)
277
-	}
278
-	server.snomasks.Send(sno.LocalXline, snoDescription)
279
-
280
-	var killClient bool
281
-	if andKill {
282
-		var clientsToKill []*Client
283
-		var killedClientNicks []string
284
-
285
-		for _, mcl := range server.clients.AllClients() {
286
-			for _, clientMask := range mcl.AllNickmasks() {
287
-				if matcher.Match(clientMask) {
288
-					clientsToKill = append(clientsToKill, mcl)
289
-					killedClientNicks = append(killedClientNicks, mcl.nick)
290
-				}
291
-			}
292
-		}
293
-
294
-		for _, mcl := range clientsToKill {
295
-			mcl.exitedSnomaskSent = true
296
-			mcl.Quit(fmt.Sprintf(mcl.t("You have been banned from this server (%s)"), reason))
297
-			if mcl == client {
298
-				killClient = true
299
-			} else {
300
-				// if mcl == client, we kill them below
301
-				mcl.destroy(false)
302
-			}
303
-		}
304
-
305
-		// send snomask
306
-		sort.Strings(killedClientNicks)
307
-		server.snomasks.Send(sno.LocalKills, fmt.Sprintf(ircfmt.Unescape("%s [%s] killed %d clients with a KLINE $c[grey][$r%s$c[grey]]"), client.nick, operName, len(killedClientNicks), strings.Join(killedClientNicks, ", ")))
308
-	}
309
-
310
-	return killClient
311
-}
312
-
313
-func unKLineHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
314
-	// check oper permissions
315
-	if !client.class.Capabilities["oper:local_unban"] {
316
-		client.Send(nil, server.name, ERR_NOPRIVS, client.nick, msg.Command, client.t("Insufficient oper privs"))
317
-		return false
318
-	}
319
-
320
-	// get host
321
-	mask := msg.Params[0]
322
-
323
-	if !strings.Contains(mask, "!") && !strings.Contains(mask, "@") {
324
-		mask = mask + "!*@*"
325
-	} else if !strings.Contains(mask, "@") {
326
-		mask = mask + "@*"
327
-	}
328
-
329
-	// save in datastore
330
-	err := server.store.Update(func(tx *buntdb.Tx) error {
331
-		klineKey := fmt.Sprintf(keyKlineEntry, mask)
332
-
333
-		// check if it exists or not
334
-		val, err := tx.Get(klineKey)
335
-		if val == "" {
336
-			return errNoExistingBan
337
-		} else if err != nil {
338
-			return err
339
-		}
340
-
341
-		tx.Delete(klineKey)
342
-		return nil
343
-	})
344
-
345
-	if err != nil {
346
-		client.Send(nil, server.name, ERR_UNKNOWNERROR, client.nick, msg.Command, fmt.Sprintf(client.t("Could not remove ban [%s]"), err.Error()))
347
-		return false
348
-	}
349
-
350
-	server.klines.RemoveMask(mask)
351
-
352
-	client.Notice(fmt.Sprintf(client.t("Removed K-Line for %s"), mask))
353
-	server.snomasks.Send(sno.LocalXline, fmt.Sprintf(ircfmt.Unescape("%s$r removed K-Line for %s"), client.nick, mask))
354
-	return false
355
-}
356
-
357 122
 func (s *Server) loadKLines() {
358 123
 	s.klines = NewKLineManager()
359 124
 

+ 0
- 129
irc/modes.go View File

@@ -9,7 +9,6 @@ import (
9 9
 	"strconv"
10 10
 	"strings"
11 11
 
12
-	"github.com/goshuirc/irc-go/ircmsg"
13 12
 	"github.com/oragono/oragono/irc/sno"
14 13
 )
15 14
 
@@ -209,16 +208,6 @@ func GetLowestChannelModePrefix(prefixes string) *Mode {
209 208
 // commands
210 209
 //
211 210
 
212
-// MODE <target> [<modestring> [<mode arguments>...]]
213
-func modeHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
214
-	_, errChan := CasefoldChannel(msg.Params[0])
215
-
216
-	if errChan == nil {
217
-		return cmodeHandler(server, client, msg)
218
-	}
219
-	return umodeHandler(server, client, msg)
220
-}
221
-
222 211
 // ParseUserModeChanges returns the valid changes, and the list of unknown chars.
223 212
 func ParseUserModeChanges(params ...string) (ModeChanges, map[rune]bool) {
224 213
 	changes := make(ModeChanges, 0)
@@ -324,63 +313,6 @@ func (client *Client) applyUserModeChanges(force bool, changes ModeChanges) Mode
324 313
 	return applied
325 314
 }
326 315
 
327
-// MODE <target> [<modestring> [<mode arguments>...]]
328
-func umodeHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
329
-	nickname, err := CasefoldName(msg.Params[0])
330
-	target := server.clients.Get(nickname)
331
-	if err != nil || target == nil {
332
-		if len(msg.Params[0]) > 0 {
333
-			client.Send(nil, server.name, ERR_NOSUCHNICK, client.nick, msg.Params[0], client.t("No such nick"))
334
-		}
335
-		return false
336
-	}
337
-
338
-	targetNick := target.Nick()
339
-	hasPrivs := client == target || msg.Command == "SAMODE"
340
-
341
-	if !hasPrivs {
342
-		if len(msg.Params) > 1 {
343
-			client.Send(nil, server.name, ERR_USERSDONTMATCH, client.nick, client.t("Can't change modes for other users"))
344
-		} else {
345
-			client.Send(nil, server.name, ERR_USERSDONTMATCH, client.nick, client.t("Can't view modes for other users"))
346
-		}
347
-		return false
348
-	}
349
-
350
-	// applied mode changes
351
-	applied := make(ModeChanges, 0)
352
-
353
-	if 1 < len(msg.Params) {
354
-		// parse out real mode changes
355
-		params := msg.Params[1:]
356
-		changes, unknown := ParseUserModeChanges(params...)
357
-
358
-		// alert for unknown mode changes
359
-		for char := range unknown {
360
-			client.Send(nil, server.name, ERR_UNKNOWNMODE, client.nick, string(char), client.t("is an unknown mode character to me"))
361
-		}
362
-		if len(unknown) == 1 && len(changes) == 0 {
363
-			return false
364
-		}
365
-
366
-		// apply mode changes
367
-		applied = target.applyUserModeChanges(msg.Command == "SAMODE", changes)
368
-	}
369
-
370
-	if len(applied) > 0 {
371
-		client.Send(nil, client.nickMaskString, "MODE", targetNick, applied.String())
372
-	} else if hasPrivs {
373
-		client.Send(nil, target.nickMaskString, RPL_UMODEIS, targetNick, target.ModeString())
374
-		if client.flags[LocalOperator] || client.flags[Operator] {
375
-			masks := server.snomasks.String(client)
376
-			if 0 < len(masks) {
377
-				client.Send(nil, target.nickMaskString, RPL_SNOMASKIS, targetNick, masks, client.t("Server notice masks"))
378
-			}
379
-		}
380
-	}
381
-	return false
382
-}
383
-
384 316
 // ParseDefaultChannelModes parses the `default-modes` line of the config
385 317
 func ParseDefaultChannelModes(config *Config) Modes {
386 318
 	if config.Channels.DefaultModes == nil {
@@ -605,64 +537,3 @@ func (channel *Channel) ApplyChannelModeChanges(client *Client, isSamode bool, c
605 537
 
606 538
 	return applied
607 539
 }
608
-
609
-// MODE <target> [<modestring> [<mode arguments>...]]
610
-func cmodeHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
611
-	channelName, err := CasefoldChannel(msg.Params[0])
612
-	channel := server.channels.Get(channelName)
613
-
614
-	if err != nil || channel == nil {
615
-		client.Send(nil, server.name, ERR_NOSUCHCHANNEL, client.nick, msg.Params[0], client.t("No such channel"))
616
-		return false
617
-	}
618
-
619
-	// applied mode changes
620
-	applied := make(ModeChanges, 0)
621
-
622
-	if 1 < len(msg.Params) {
623
-		// parse out real mode changes
624
-		params := msg.Params[1:]
625
-		changes, unknown := ParseChannelModeChanges(params...)
626
-
627
-		// alert for unknown mode changes
628
-		for char := range unknown {
629
-			client.Send(nil, server.name, ERR_UNKNOWNMODE, client.nick, string(char), client.t("is an unknown mode character to me"))
630
-		}
631
-		if len(unknown) == 1 && len(changes) == 0 {
632
-			return false
633
-		}
634
-
635
-		// apply mode changes
636
-		applied = channel.ApplyChannelModeChanges(client, msg.Command == "SAMODE", changes)
637
-	}
638
-
639
-	// save changes to banlist/exceptlist/invexlist
640
-	var banlistUpdated, exceptlistUpdated, invexlistUpdated bool
641
-	for _, change := range applied {
642
-		if change.mode == BanMask {
643
-			banlistUpdated = true
644
-		} else if change.mode == ExceptMask {
645
-			exceptlistUpdated = true
646
-		} else if change.mode == InviteMask {
647
-			invexlistUpdated = true
648
-		}
649
-	}
650
-
651
-	if (banlistUpdated || exceptlistUpdated || invexlistUpdated) && channel.IsRegistered() {
652
-		go server.channelRegistry.StoreChannel(channel, true)
653
-	}
654
-
655
-	// send out changes
656
-	if len(applied) > 0 {
657
-		//TODO(dan): we should change the name of String and make it return a slice here
658
-		args := append([]string{channel.name}, strings.Split(applied.String(), " ")...)
659
-		for _, member := range channel.Members() {
660
-			member.Send(nil, client.nickMaskString, "MODE", args...)
661
-		}
662
-	} else {
663
-		args := append([]string{client.nick, channel.name}, channel.modeStrings(client)...)
664
-		client.Send(nil, client.nickMaskString, RPL_CHANNELMODEIS, args...)
665
-		client.Send(nil, client.nickMaskString, RPL_CHANNELCREATED, client.nick, channel.name, strconv.FormatInt(channel.createdTime.Unix(), 10))
666
-	}
667
-	return false
668
-}

+ 0
- 138
irc/monitor.go View File

@@ -5,12 +5,9 @@ package irc
5 5
 
6 6
 import (
7 7
 	"errors"
8
-	"strconv"
9
-	"strings"
10 8
 	"sync"
11 9
 
12 10
 	"github.com/goshuirc/irc-go/ircmsg"
13
-	"github.com/oragono/oragono/irc/utils"
14 11
 )
15 12
 
16 13
 // MonitorManager keeps track of who's monitoring which nicks.
@@ -118,138 +115,3 @@ var (
118 115
 		"s": monitorStatusHandler,
119 116
 	}
120 117
 )
121
-
122
-func monitorHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
123
-	handler, exists := metadataSubcommands[strings.ToLower(msg.Params[0])]
124
-
125
-	if !exists {
126
-		client.Send(nil, server.name, ERR_UNKNOWNERROR, client.Nick(), "MONITOR", msg.Params[0], client.t("Unknown subcommand"))
127
-		return false
128
-	}
129
-
130
-	return handler(server, client, msg)
131
-}
132
-
133
-func monitorRemoveHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
134
-	if len(msg.Params) < 2 {
135
-		client.Send(nil, server.name, ERR_NEEDMOREPARAMS, client.Nick(), msg.Command, client.t("Not enough parameters"))
136
-		return false
137
-	}
138
-
139
-	targets := strings.Split(msg.Params[1], ",")
140
-	for _, target := range targets {
141
-		cfnick, err := CasefoldName(target)
142
-		if err != nil {
143
-			continue
144
-		}
145
-		server.monitorManager.Remove(client, cfnick)
146
-	}
147
-
148
-	return false
149
-}
150
-
151
-func monitorAddHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
152
-	if len(msg.Params) < 2 {
153
-		client.Send(nil, server.name, ERR_NEEDMOREPARAMS, client.Nick(), msg.Command, client.t("Not enough parameters"))
154
-		return false
155
-	}
156
-
157
-	var online []string
158
-	var offline []string
159
-
160
-	limit := server.Limits().MonitorEntries
161
-
162
-	targets := strings.Split(msg.Params[1], ",")
163
-	for _, target := range targets {
164
-		// check name length
165
-		if len(target) < 1 || len(targets) > server.limits.NickLen {
166
-			continue
167
-		}
168
-
169
-		// add target
170
-		casefoldedTarget, err := CasefoldName(target)
171
-		if err != nil {
172
-			continue
173
-		}
174
-
175
-		err = server.monitorManager.Add(client, casefoldedTarget, limit)
176
-		if err == ErrMonitorLimitExceeded {
177
-			client.Send(nil, server.name, ERR_MONLISTFULL, client.Nick(), strconv.Itoa(server.limits.MonitorEntries), strings.Join(targets, ","))
178
-			break
179
-		} else if err != nil {
180
-			continue
181
-		}
182
-
183
-		// add to online / offline lists
184
-		if targetClient := server.clients.Get(casefoldedTarget); targetClient == nil {
185
-			offline = append(offline, target)
186
-		} else {
187
-			online = append(online, targetClient.Nick())
188
-		}
189
-	}
190
-
191
-	if len(online) > 0 {
192
-		client.Send(nil, server.name, RPL_MONONLINE, client.Nick(), strings.Join(online, ","))
193
-	}
194
-	if len(offline) > 0 {
195
-		client.Send(nil, server.name, RPL_MONOFFLINE, client.Nick(), strings.Join(offline, ","))
196
-	}
197
-
198
-	return false
199
-}
200
-
201
-func monitorClearHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
202
-	server.monitorManager.RemoveAll(client)
203
-	return false
204
-}
205
-
206
-func monitorListHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
207
-	monitorList := server.monitorManager.List(client)
208
-
209
-	var nickList []string
210
-	for _, cfnick := range monitorList {
211
-		replynick := cfnick
212
-		// report the uncasefolded nick if it's available, i.e., the client is online
213
-		if mclient := server.clients.Get(cfnick); mclient != nil {
214
-			replynick = mclient.Nick()
215
-		}
216
-		nickList = append(nickList, replynick)
217
-	}
218
-
219
-	for _, line := range utils.ArgsToStrings(maxLastArgLength, nickList, ",") {
220
-		client.Send(nil, server.name, RPL_MONLIST, client.Nick(), line)
221
-	}
222
-
223
-	client.Send(nil, server.name, RPL_ENDOFMONLIST, "End of MONITOR list")
224
-
225
-	return false
226
-}
227
-
228
-func monitorStatusHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
229
-	var online []string
230
-	var offline []string
231
-
232
-	monitorList := server.monitorManager.List(client)
233
-
234
-	for _, name := range monitorList {
235
-		target := server.clients.Get(name)
236
-		if target == nil {
237
-			offline = append(offline, name)
238
-		} else {
239
-			online = append(online, target.Nick())
240
-		}
241
-	}
242
-
243
-	if len(online) > 0 {
244
-		for _, line := range utils.ArgsToStrings(maxLastArgLength, online, ",") {
245
-			client.Send(nil, server.name, RPL_MONONLINE, client.Nick(), line)
246
-		}
247
-	}
248
-	if len(offline) > 0 {
249
-		for _, line := range utils.ArgsToStrings(maxLastArgLength, offline, ",") {
250
-			client.Send(nil, server.name, RPL_MONOFFLINE, client.Nick(), line)
251
-		}
252
-	}
253
-
254
-	return false
255
-}

+ 0
- 22
irc/nickname.go View File

@@ -9,7 +9,6 @@ import (
9 9
 	"strings"
10 10
 
11 11
 	"github.com/goshuirc/irc-go/ircfmt"
12
-	"github.com/goshuirc/irc-go/ircmsg"
13 12
 	"github.com/oragono/oragono/irc/sno"
14 13
 )
15 14
 
@@ -21,16 +20,6 @@ var (
21 20
 	}
22 21
 )
23 22
 
24
-// NICK <nickname>
25
-func nickHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
26
-	if !client.authorized {
27
-		client.Quit("Bad password")
28
-		return true
29
-	}
30
-
31
-	return performNickChange(server, client, client, msg.Params[0])
32
-}
33
-
34 23
 func performNickChange(server *Server, client *Client, target *Client, newnick string) bool {
35 24
 	nickname := strings.TrimSpace(newnick)
36 25
 	cfnick, err := CasefoldName(nickname)
@@ -77,14 +66,3 @@ func performNickChange(server *Server, client *Client, target *Client, newnick s
77 66
 	}
78 67
 	return false
79 68
 }
80
-
81
-// SANICK <oldnick> <nickname>
82
-func sanickHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
83
-	targetNick := strings.TrimSpace(msg.Params[0])
84
-	target := server.clients.Get(targetNick)
85
-	if target == nil {
86
-		client.Send(nil, server.name, ERR_NOSUCHNICK, client.nick, msg.Params[0], client.t("No such nick"))
87
-		return false
88
-	}
89
-	return performNickChange(server, client, target, msg.Params[1])
90
-}

+ 0
- 7
irc/nickserv.go View File

@@ -11,7 +11,6 @@ import (
11 11
 	"time"
12 12
 
13 13
 	"github.com/goshuirc/irc-go/ircfmt"
14
-	"github.com/goshuirc/irc-go/ircmsg"
15 14
 	"github.com/oragono/oragono/irc/passwd"
16 15
 	"github.com/oragono/oragono/irc/sno"
17 16
 	"github.com/tidwall/buntdb"
@@ -28,12 +27,6 @@ To login to an account:
28 27
 Leave out [username password] to use your client certificate fingerprint. Otherwise,
29 28
 the given username and password will be used.`
30 29
 
31
-// nsHandler handles the /NS and /NICKSERV commands
32
-func nsHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
33
-	server.nickservReceivePrivmsg(client, strings.Join(msg.Params, " "))
34
-	return false
35
-}
36
-
37 30
 func (server *Server) nickservReceiveNotice(client *Client, message string) {
38 31
 	// do nothing
39 32
 }

+ 0
- 49
irc/roleplay.go View File

@@ -6,7 +6,6 @@ package irc
6 6
 import (
7 7
 	"fmt"
8 8
 
9
-	"github.com/goshuirc/irc-go/ircmsg"
10 9
 	"github.com/oragono/oragono/irc/caps"
11 10
 )
12 11
 
@@ -15,54 +14,6 @@ const (
15 14
 	sceneNickMask = "=Scene=!%s@npc.fakeuser.invalid"
16 15
 )
17 16
 
18
-// SCENE <target> <text to be sent>
19
-func sceneHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
20
-	target := msg.Params[0]
21
-	message := msg.Params[1]
22
-	sourceString := fmt.Sprintf(sceneNickMask, client.nick)
23
-
24
-	sendRoleplayMessage(server, client, sourceString, target, false, message)
25
-
26
-	return false
27
-}
28
-
29
-// NPC <target> <sourcenick> <text to be sent>
30
-func npcHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
31
-	target := msg.Params[0]
32
-	fakeSource := msg.Params[1]
33
-	message := msg.Params[2]
34
-
35
-	_, err := CasefoldName(fakeSource)
36
-	if err != nil {
37
-		client.Send(nil, client.server.name, ERR_CANNOTSENDRP, target, client.t("Fake source must be a valid nickname"))
38
-		return false
39
-	}
40
-
41
-	sourceString := fmt.Sprintf(npcNickMask, fakeSource, client.nick)
42
-
43
-	sendRoleplayMessage(server, client, sourceString, target, false, message)
44
-
45
-	return false
46
-}
47
-
48
-// NPCA <target> <sourcenick> <text to be sent>
49
-func npcaHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
50
-	target := msg.Params[0]
51
-	fakeSource := msg.Params[1]
52
-	message := msg.Params[2]
53
-	sourceString := fmt.Sprintf(npcNickMask, fakeSource, client.nick)
54
-
55
-	_, err := CasefoldName(fakeSource)
56
-	if err != nil {
57
-		client.Send(nil, client.server.name, ERR_CANNOTSENDRP, target, client.t("Fake source must be a valid nickname"))
58
-		return false
59
-	}
60
-
61
-	sendRoleplayMessage(server, client, sourceString, target, true, message)
62
-
63
-	return false
64
-}
65
-
66 17
 func sendRoleplayMessage(server *Server, client *Client, source string, targetString string, isAction bool, message string) {
67 18
 	if isAction {
68 19
 		message = fmt.Sprintf("\x01ACTION %s (%s)\x01", message, client.nick)

+ 1
- 1104
irc/server.go
File diff suppressed because it is too large
View File


Loading…
Cancel
Save