Browse Source

Merge pull request #325 from slingamn/services.1

services refactor
tags/v1.0.0-rc1
Shivaram Lingamneni 5 years ago
parent
commit
3db6c9472b
No account linked to committer's email address
9 changed files with 254 additions and 168 deletions
  1. 4
    0
      irc/accounts.go
  2. 28
    38
      irc/chanserv.go
  3. 1
    1
      irc/client_lookup_set.go
  4. 47
    44
      irc/hostserv.go
  5. 2
    1
      irc/nickname.go
  6. 71
    56
      irc/nickserv.go
  7. 49
    15
      irc/services.go
  8. 0
    13
      irc/utils/args.go
  9. 52
    0
      irc/utils/fieldsn.go

+ 4
- 0
irc/accounts.go View File

187
 	defer am.RUnlock()
187
 	defer am.RUnlock()
188
 
188
 
189
 	account = am.nickToAccount[cfnick]
189
 	account = am.nickToAccount[cfnick]
190
+	if account == "" {
191
+		method = NickReservationNone
192
+		return
193
+	}
190
 	method = am.accountToMethod[account]
194
 	method = am.accountToMethod[account]
191
 	// if they don't have a custom setting, or customization is disabled, use the default
195
 	// if they don't have a custom setting, or customization is disabled, use the default
192
 	if method == NickReservationOptional || !config.Accounts.NickReservation.AllowCustomEnforcement {
196
 	if method == NickReservationOptional || !config.Accounts.NickReservation.AllowCustomEnforcement {

+ 28
- 38
irc/chanserv.go View File

15
 	"github.com/goshuirc/irc-go/ircfmt"
15
 	"github.com/goshuirc/irc-go/ircfmt"
16
 	"github.com/oragono/oragono/irc/modes"
16
 	"github.com/oragono/oragono/irc/modes"
17
 	"github.com/oragono/oragono/irc/sno"
17
 	"github.com/oragono/oragono/irc/sno"
18
-	"github.com/oragono/oragono/irc/utils"
19
 )
18
 )
20
 
19
 
21
 const chanservHelp = `ChanServ lets you register and manage channels.
20
 const chanservHelp = `ChanServ lets you register and manage channels.
26
 Here are the commands you can use:
25
 Here are the commands you can use:
27
 %s`
26
 %s`
28
 
27
 
29
-func chanregEnabled(server *Server) bool {
30
-	return server.ChannelRegistrationEnabled()
28
+func chanregEnabled(config *Config) bool {
29
+	return config.Channels.Registration.Enabled
31
 }
30
 }
32
 
31
 
33
 var (
32
 var (
41
 			helpShort:    `$bOP$b makes the given user (or yourself) a channel admin.`,
40
 			helpShort:    `$bOP$b makes the given user (or yourself) a channel admin.`,
42
 			authRequired: true,
41
 			authRequired: true,
43
 			enabled:      chanregEnabled,
42
 			enabled:      chanregEnabled,
43
+			minParams:    1,
44
 		},
44
 		},
45
 		"register": {
45
 		"register": {
46
 			handler: csRegisterHandler,
46
 			handler: csRegisterHandler,
52
 			helpShort:    `$bREGISTER$b lets you own a given channel.`,
52
 			helpShort:    `$bREGISTER$b lets you own a given channel.`,
53
 			authRequired: true,
53
 			authRequired: true,
54
 			enabled:      chanregEnabled,
54
 			enabled:      chanregEnabled,
55
+			minParams:    1,
55
 		},
56
 		},
56
 		"unregister": {
57
 		"unregister": {
57
 			handler: csUnregisterHandler,
58
 			handler: csUnregisterHandler,
62
 invoking the command without a code will display the necessary code.`,
63
 invoking the command without a code will display the necessary code.`,
63
 			helpShort: `$bUNREGISTER$b deletes a channel registration.`,
64
 			helpShort: `$bUNREGISTER$b deletes a channel registration.`,
64
 			enabled:   chanregEnabled,
65
 			enabled:   chanregEnabled,
66
+			minParams: 1,
65
 		},
67
 		},
66
 		"drop": {
68
 		"drop": {
67
 			aliasOf: "unregister",
69
 			aliasOf: "unregister",
77
 referenced by their registered account names, not their nicknames.`,
79
 referenced by their registered account names, not their nicknames.`,
78
 			helpShort: `$bAMODE$b modifies persistent mode settings for channel members.`,
80
 			helpShort: `$bAMODE$b modifies persistent mode settings for channel members.`,
79
 			enabled:   chanregEnabled,
81
 			enabled:   chanregEnabled,
82
+			minParams: 1,
80
 		},
83
 		},
81
 	}
84
 	}
82
 )
85
 )
86
 	rb.Add(nil, "ChanServ", "NOTICE", rb.target.Nick(), text)
89
 	rb.Add(nil, "ChanServ", "NOTICE", rb.target.Nick(), text)
87
 }
90
 }
88
 
91
 
89
-func csAmodeHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
90
-	channelName, modeChange := utils.ExtractParam(params)
92
+func csAmodeHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
93
+	channelName := params[0]
91
 
94
 
92
 	channel := server.channels.Get(channelName)
95
 	channel := server.channels.Get(channelName)
93
 	if channel == nil {
96
 	if channel == nil {
98
 		return
101
 		return
99
 	}
102
 	}
100
 
103
 
101
-	modeChanges, unknown := modes.ParseChannelModeChanges(strings.Fields(modeChange)...)
104
+	modeChanges, unknown := modes.ParseChannelModeChanges(params[1:]...)
102
 	var change modes.ModeChange
105
 	var change modes.ModeChange
103
 	if len(modeChanges) > 1 || len(unknown) > 0 {
106
 	if len(modeChanges) > 1 || len(unknown) > 0 {
104
 		csNotice(rb, client.t("Invalid mode change"))
107
 		csNotice(rb, client.t("Invalid mode change"))
159
 	}
162
 	}
160
 }
163
 }
161
 
164
 
162
-func csOpHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
163
-	channelName, clientToOp := utils.ExtractParam(params)
164
-
165
-	if channelName == "" {
166
-		csNotice(rb, ircfmt.Unescape(client.t("Syntax: $bOP #channel [nickname]$b")))
167
-		return
168
-	}
169
-
170
-	clientToOp = strings.TrimSpace(clientToOp)
171
-
172
-	channelKey, err := CasefoldChannel(channelName)
173
-	if err != nil {
174
-		csNotice(rb, client.t("Channel name is not valid"))
175
-		return
176
-	}
177
-
178
-	channelInfo := server.channels.Get(channelKey)
165
+func csOpHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
166
+	channelInfo := server.channels.Get(params[0])
179
 	if channelInfo == nil {
167
 	if channelInfo == nil {
180
 		csNotice(rb, client.t("Channel does not exist"))
168
 		csNotice(rb, client.t("Channel does not exist"))
181
 		return
169
 		return
182
 	}
170
 	}
171
+	channelName := channelInfo.Name()
183
 
172
 
184
 	clientAccount := client.Account()
173
 	clientAccount := client.Account()
185
 	if clientAccount == "" || clientAccount != channelInfo.Founder() {
174
 	if clientAccount == "" || clientAccount != channelInfo.Founder() {
188
 	}
177
 	}
189
 
178
 
190
 	var target *Client
179
 	var target *Client
191
-	if clientToOp != "" {
192
-		casefoldedNickname, err := CasefoldName(clientToOp)
193
-		target = server.clients.Get(casefoldedNickname)
194
-		if err != nil || target == nil {
180
+	if len(params) > 1 {
181
+		target = server.clients.Get(params[1])
182
+		if target == nil {
195
 			csNotice(rb, client.t("Could not find given client"))
183
 			csNotice(rb, client.t("Could not find given client"))
196
 			return
184
 			return
197
 		}
185
 		}
216
 
204
 
217
 	csNotice(rb, fmt.Sprintf(client.t("Successfully op'd in channel %s"), channelName))
205
 	csNotice(rb, fmt.Sprintf(client.t("Successfully op'd in channel %s"), channelName))
218
 
206
 
219
-	server.logger.Info("chanserv", fmt.Sprintf("Client %s op'd [%s] in channel %s", client.nick, clientToOp, channelName))
220
-	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, clientToOp, channelName))
207
+	tnick := target.Nick()
208
+	server.logger.Info("services", fmt.Sprintf("Client %s op'd [%s] in channel %s", client.Nick(), tnick, channelName))
209
+	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))
221
 }
210
 }
222
 
211
 
223
-func csRegisterHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
224
-	channelName := strings.TrimSpace(params)
225
-	if channelName == "" {
226
-		csNotice(rb, ircfmt.Unescape(client.t("Syntax: $bREGISTER #channel$b")))
227
-		return
228
-	}
212
+func csRegisterHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
213
+	channelName := params[0]
229
 
214
 
230
 	channelKey, err := CasefoldChannel(channelName)
215
 	channelKey, err := CasefoldChannel(channelName)
231
 	if err != nil {
216
 	if err != nil {
251
 
236
 
252
 	csNotice(rb, fmt.Sprintf(client.t("Channel %s successfully registered"), channelName))
237
 	csNotice(rb, fmt.Sprintf(client.t("Channel %s successfully registered"), channelName))
253
 
238
 
254
-	server.logger.Info("chanserv", fmt.Sprintf("Client %s registered channel %s", client.nick, channelName))
239
+	server.logger.Info("services", fmt.Sprintf("Client %s registered channel %s", client.nick, channelName))
255
 	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))
240
 	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))
256
 
241
 
257
 	// give them founder privs
242
 	// give them founder privs
266
 	}
251
 	}
267
 }
252
 }
268
 
253
 
269
-func csUnregisterHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
270
-	channelName, verificationCode := utils.ExtractParam(params)
254
+func csUnregisterHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
255
+	channelName := params[0]
256
+	var verificationCode string
257
+	if len(params) > 1 {
258
+		verificationCode = params[1]
259
+	}
260
+
271
 	channelKey, err := CasefoldChannel(channelName)
261
 	channelKey, err := CasefoldChannel(channelName)
272
 	if channelKey == "" || err != nil {
262
 	if channelKey == "" || err != nil {
273
 		csNotice(rb, client.t("Channel name is not valid"))
263
 		csNotice(rb, client.t("Channel name is not valid"))

+ 1
- 1
irc/client_lookup_set.go View File

129
 	if currentNewEntry != nil && currentNewEntry != client {
129
 	if currentNewEntry != nil && currentNewEntry != client {
130
 		return errNicknameInUse
130
 		return errNicknameInUse
131
 	}
131
 	}
132
-	if method == NickReservationStrict && reservedAccount != client.Account() {
132
+	if method == NickReservationStrict && reservedAccount != "" && reservedAccount != client.Account() {
133
 		return errNicknameReserved
133
 		return errNicknameReserved
134
 	}
134
 	}
135
 	clients.removeInternal(client)
135
 	clients.removeInternal(client)

+ 47
- 44
irc/hostserv.go View File

7
 	"errors"
7
 	"errors"
8
 	"fmt"
8
 	"fmt"
9
 	"regexp"
9
 	"regexp"
10
-	"strings"
11
 	"time"
10
 	"time"
12
-
13
-	"github.com/oragono/oragono/irc/utils"
14
 )
11
 )
15
 
12
 
16
 const hostservHelp = `HostServ lets you manage your vhost (i.e., the string displayed
13
 const hostservHelp = `HostServ lets you manage your vhost (i.e., the string displayed
29
 	defaultValidVhostRegex = regexp.MustCompile(`^[0-9A-Za-z.\-_/]+$`)
26
 	defaultValidVhostRegex = regexp.MustCompile(`^[0-9A-Za-z.\-_/]+$`)
30
 )
27
 )
31
 
28
 
32
-func hostservEnabled(server *Server) bool {
33
-	return server.AccountConfig().VHosts.Enabled
29
+func hostservEnabled(config *Config) bool {
30
+	return config.Accounts.VHosts.Enabled
34
 }
31
 }
35
 
32
 
36
-func hostservRequestsEnabled(server *Server) bool {
37
-	ac := server.AccountConfig()
38
-	return ac.VHosts.Enabled && ac.VHosts.UserRequests.Enabled
33
+func hostservRequestsEnabled(config *Config) bool {
34
+	return config.Accounts.VHosts.Enabled && config.Accounts.VHosts.UserRequests.Enabled
39
 }
35
 }
40
 
36
 
41
 var (
37
 var (
67
 			helpShort:    `$bREQUEST$b requests a new vhost, pending operator approval.`,
63
 			helpShort:    `$bREQUEST$b requests a new vhost, pending operator approval.`,
68
 			authRequired: true,
64
 			authRequired: true,
69
 			enabled:      hostservRequestsEnabled,
65
 			enabled:      hostservRequestsEnabled,
66
+			minParams:    1,
70
 		},
67
 		},
71
 		"status": {
68
 		"status": {
72
 			handler: hsStatusHandler,
69
 			handler: hsStatusHandler,
73
-			help: `Syntax: $bSTATUS$b
70
+			help: `Syntax: $bSTATUS [user]$b
74
 
71
 
75
 STATUS displays your current vhost, if any, and the status of your most recent
72
 STATUS displays your current vhost, if any, and the status of your most recent
76
-request for a new one.`,
77
-			helpShort:    `$bSTATUS$b shows your vhost and request status.`,
78
-			authRequired: true,
79
-			enabled:      hostservEnabled,
73
+request for a new one. A server operator can view someone else's status.`,
74
+			helpShort: `$bSTATUS$b shows your vhost and request status.`,
75
+			enabled:   hostservEnabled,
80
 		},
76
 		},
81
 		"set": {
77
 		"set": {
82
 			handler: hsSetHandler,
78
 			handler: hsSetHandler,
86
 			helpShort: `$bSET$b sets a user's vhost.`,
82
 			helpShort: `$bSET$b sets a user's vhost.`,
87
 			capabs:    []string{"vhosts"},
83
 			capabs:    []string{"vhosts"},
88
 			enabled:   hostservEnabled,
84
 			enabled:   hostservEnabled,
85
+			minParams: 2,
89
 		},
86
 		},
90
 		"del": {
87
 		"del": {
91
 			handler: hsSetHandler,
88
 			handler: hsSetHandler,
95
 			helpShort: `$bDEL$b deletes a user's vhost.`,
92
 			helpShort: `$bDEL$b deletes a user's vhost.`,
96
 			capabs:    []string{"vhosts"},
93
 			capabs:    []string{"vhosts"},
97
 			enabled:   hostservEnabled,
94
 			enabled:   hostservEnabled,
95
+			minParams: 1,
98
 		},
96
 		},
99
 		"waiting": {
97
 		"waiting": {
100
 			handler: hsWaitingHandler,
98
 			handler: hsWaitingHandler,
114
 			helpShort: `$bAPPROVE$b approves a user's vhost request.`,
112
 			helpShort: `$bAPPROVE$b approves a user's vhost request.`,
115
 			capabs:    []string{"vhosts"},
113
 			capabs:    []string{"vhosts"},
116
 			enabled:   hostservEnabled,
114
 			enabled:   hostservEnabled,
115
+			minParams: 1,
117
 		},
116
 		},
118
 		"reject": {
117
 		"reject": {
119
 			handler: hsRejectHandler,
118
 			handler: hsRejectHandler,
124
 			helpShort: `$bREJECT$b rejects a user's vhost request.`,
123
 			helpShort: `$bREJECT$b rejects a user's vhost request.`,
125
 			capabs:    []string{"vhosts"},
124
 			capabs:    []string{"vhosts"},
126
 			enabled:   hostservEnabled,
125
 			enabled:   hostservEnabled,
126
+			minParams: 1,
127
+			maxParams: 2,
127
 		},
128
 		},
128
 	}
129
 	}
129
 )
130
 )
146
 	}
147
 	}
147
 }
148
 }
148
 
149
 
149
-func hsOnOffHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
150
+func hsOnOffHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
150
 	enable := false
151
 	enable := false
151
 	if command == "on" {
152
 	if command == "on" {
152
 		enable = true
153
 		enable = true
162
 	}
163
 	}
163
 }
164
 }
164
 
165
 
165
-func hsRequestHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
166
-	vhost, _ := utils.ExtractParam(params)
166
+func hsRequestHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
167
+	vhost := params[0]
167
 	if validateVhost(server, vhost, false) != nil {
168
 	if validateVhost(server, vhost, false) != nil {
168
 		hsNotice(rb, client.t("Invalid vhost"))
169
 		hsNotice(rb, client.t("Invalid vhost"))
169
 		return
170
 		return
195
 	}
196
 	}
196
 }
197
 }
197
 
198
 
198
-func hsStatusHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
199
-	accountName := client.Account()
199
+func hsStatusHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
200
+	var accountName string
201
+	if len(params) > 0 {
202
+		if !client.HasRoleCapabs("vhosts") {
203
+			hsNotice(rb, client.t("Command restricted"))
204
+			return
205
+		}
206
+		accountName = params[0]
207
+	} else {
208
+		accountName = client.Account()
209
+	}
210
+
200
 	account, err := server.accounts.LoadAccount(accountName)
211
 	account, err := server.accounts.LoadAccount(accountName)
201
 	if err != nil {
212
 	if err != nil {
202
-		server.logger.Warning("internal", "error loading account info", accountName, err.Error())
203
-		hsNotice(rb, client.t("An error occurred"))
213
+		if err != errAccountDoesNotExist {
214
+			server.logger.Warning("internal", "error loading account info", accountName, err.Error())
215
+		}
216
+		hsNotice(rb, client.t("No such account"))
204
 		return
217
 		return
205
 	}
218
 	}
206
 
219
 
232
 	return nil
245
 	return nil
233
 }
246
 }
234
 
247
 
235
-func hsSetHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
236
-	var user, vhost string
237
-	user, params = utils.ExtractParam(params)
238
-	if user == "" {
239
-		hsNotice(rb, client.t("A user is required"))
240
-		return
241
-	}
248
+func hsSetHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
249
+	user := params[0]
250
+	var vhost string
251
+
242
 	if command == "set" {
252
 	if command == "set" {
243
-		vhost, _ = utils.ExtractParam(params)
253
+		vhost = params[1]
244
 		if validateVhost(server, vhost, true) != nil {
254
 		if validateVhost(server, vhost, true) != nil {
245
 			hsNotice(rb, client.t("Invalid vhost"))
255
 			hsNotice(rb, client.t("Invalid vhost"))
246
 			return
256
 			return
247
 		}
257
 		}
248
-	} else if command != "del" {
249
-		server.logger.Warning("internal", "invalid hostserv set command", command)
250
-		return
251
 	}
258
 	}
259
+	// else: command == "del", vhost == ""
252
 
260
 
253
 	_, err := server.accounts.VHostSet(user, vhost)
261
 	_, err := server.accounts.VHostSet(user, vhost)
254
 	if err != nil {
262
 	if err != nil {
260
 	}
268
 	}
261
 }
269
 }
262
 
270
 
263
-func hsWaitingHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
271
+func hsWaitingHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
264
 	requests, total := server.accounts.VHostListRequests(10)
272
 	requests, total := server.accounts.VHostListRequests(10)
265
 	hsNotice(rb, fmt.Sprintf(client.t("There are %d pending requests for vhosts (%d displayed)"), total, len(requests)))
273
 	hsNotice(rb, fmt.Sprintf(client.t("There are %d pending requests for vhosts (%d displayed)"), total, len(requests)))
266
 	for i, request := range requests {
274
 	for i, request := range requests {
268
 	}
276
 	}
269
 }
277
 }
270
 
278
 
271
-func hsApproveHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
272
-	user, _ := utils.ExtractParam(params)
273
-	if user == "" {
274
-		hsNotice(rb, client.t("A user is required"))
275
-		return
276
-	}
279
+func hsApproveHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
280
+	user := params[0]
277
 
281
 
278
 	vhostInfo, err := server.accounts.VHostApprove(user)
282
 	vhostInfo, err := server.accounts.VHostApprove(user)
279
 	if err != nil {
283
 	if err != nil {
288
 	}
292
 	}
289
 }
293
 }
290
 
294
 
291
-func hsRejectHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
292
-	user, params := utils.ExtractParam(params)
293
-	if user == "" {
294
-		hsNotice(rb, client.t("A user is required"))
295
-		return
295
+func hsRejectHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
296
+	var reason string
297
+	user := params[0]
298
+	if len(params) > 1 {
299
+		reason = params[1]
296
 	}
300
 	}
297
-	reason := strings.TrimSpace(params)
298
 
301
 
299
 	vhostInfo, err := server.accounts.VHostReject(user, reason)
302
 	vhostInfo, err := server.accounts.VHostReject(user, reason)
300
 	if err != nil {
303
 	if err != nil {

+ 2
- 1
irc/nickname.go View File

15
 )
15
 )
16
 
16
 
17
 var (
17
 var (
18
+	// anything added here MUST be casefolded:
18
 	restrictedNicknames = map[string]bool{
19
 	restrictedNicknames = map[string]bool{
19
 		"=scene=":  true, // used for rp commands
20
 		"=scene=":  true, // used for rp commands
20
-		"HistServ": true, // TODO(slingamn) this should become a real service
21
+		"histserv": true, // TODO(slingamn) this should become a real service
21
 	}
22
 	}
22
 )
23
 )
23
 
24
 

+ 71
- 56
irc/nickserv.go View File

8
 	"strings"
8
 	"strings"
9
 
9
 
10
 	"github.com/goshuirc/irc-go/ircfmt"
10
 	"github.com/goshuirc/irc-go/ircfmt"
11
-	"github.com/oragono/oragono/irc/utils"
12
 )
11
 )
13
 
12
 
14
 // "enabled" callbacks for specific nickserv commands
13
 // "enabled" callbacks for specific nickserv commands
15
-func servCmdRequiresAccreg(server *Server) bool {
16
-	return server.AccountConfig().Registration.Enabled
14
+func servCmdRequiresAccreg(config *Config) bool {
15
+	return config.Accounts.Registration.Enabled
17
 }
16
 }
18
 
17
 
19
-func servCmdRequiresAuthEnabled(server *Server) bool {
20
-	return server.AccountConfig().AuthenticationEnabled
18
+func servCmdRequiresAuthEnabled(config *Config) bool {
19
+	return config.Accounts.AuthenticationEnabled
21
 }
20
 }
22
 
21
 
23
-func nsGroupEnabled(server *Server) bool {
24
-	conf := server.Config()
25
-	return conf.Accounts.AuthenticationEnabled && conf.Accounts.NickReservation.Enabled
22
+func nsGroupEnabled(config *Config) bool {
23
+	return config.Accounts.AuthenticationEnabled && config.Accounts.NickReservation.Enabled
26
 }
24
 }
27
 
25
 
28
-func nsEnforceEnabled(server *Server) bool {
29
-	config := server.Config()
26
+func nsEnforceEnabled(config *Config) bool {
30
 	return config.Accounts.NickReservation.Enabled && config.Accounts.NickReservation.AllowCustomEnforcement
27
 	return config.Accounts.NickReservation.Enabled && config.Accounts.NickReservation.AllowCustomEnforcement
31
 }
28
 }
32
 
29
 
73
 same user account, letting you reclaim your nickname.`,
70
 same user account, letting you reclaim your nickname.`,
74
 			helpShort:    `$bGHOST$b reclaims your nickname.`,
71
 			helpShort:    `$bGHOST$b reclaims your nickname.`,
75
 			authRequired: true,
72
 			authRequired: true,
73
+			minParams:    1,
76
 		},
74
 		},
77
 		"group": {
75
 		"group": {
78
 			handler: nsGroupHandler,
76
 			handler: nsGroupHandler,
84
 			enabled:      nsGroupEnabled,
82
 			enabled:      nsGroupEnabled,
85
 			authRequired: true,
83
 			authRequired: true,
86
 		},
84
 		},
87
-
88
 		"identify": {
85
 		"identify": {
89
 			handler: nsIdentifyHandler,
86
 			handler: nsIdentifyHandler,
90
 			help: `Syntax: $bIDENTIFY <username> [password]$b
87
 			help: `Syntax: $bIDENTIFY <username> [password]$b
92
 IDENTIFY lets you login to the given username using either password auth, or
89
 IDENTIFY lets you login to the given username using either password auth, or
93
 certfp (your client certificate) if a password is not given.`,
90
 certfp (your client certificate) if a password is not given.`,
94
 			helpShort: `$bIDENTIFY$b lets you login to your account.`,
91
 			helpShort: `$bIDENTIFY$b lets you login to your account.`,
92
+			minParams: 1,
95
 		},
93
 		},
96
 		"info": {
94
 		"info": {
97
 			handler: nsInfoHandler,
95
 			handler: nsInfoHandler,
113
 certificate (and you will need to use that certificate to login in future).`,
111
 certificate (and you will need to use that certificate to login in future).`,
114
 			helpShort: `$bREGISTER$b lets you register a user account.`,
112
 			helpShort: `$bREGISTER$b lets you register a user account.`,
115
 			enabled:   servCmdRequiresAccreg,
113
 			enabled:   servCmdRequiresAccreg,
114
+			minParams: 2,
116
 		},
115
 		},
117
 		"sadrop": {
116
 		"sadrop": {
118
 			handler: nsDropHandler,
117
 			handler: nsDropHandler,
122
 			helpShort: `$bSADROP$b forcibly de-links the given nickname from its user account.`,
121
 			helpShort: `$bSADROP$b forcibly de-links the given nickname from its user account.`,
123
 			capabs:    []string{"accreg"},
122
 			capabs:    []string{"accreg"},
124
 			enabled:   servCmdRequiresAccreg,
123
 			enabled:   servCmdRequiresAccreg,
124
+			minParams: 1,
125
 		},
125
 		},
126
 		"unregister": {
126
 		"unregister": {
127
 			handler: nsUnregisterHandler,
127
 			handler: nsUnregisterHandler,
132
 unregistrations, a verification code is required; invoking the command without
132
 unregistrations, a verification code is required; invoking the command without
133
 a code will display the necessary code.`,
133
 a code will display the necessary code.`,
134
 			helpShort: `$bUNREGISTER$b lets you delete your user account.`,
134
 			helpShort: `$bUNREGISTER$b lets you delete your user account.`,
135
+			enabled:   servCmdRequiresAccreg,
136
+			minParams: 1,
135
 		},
137
 		},
136
 		"verify": {
138
 		"verify": {
137
 			handler: nsVerifyHandler,
139
 			handler: nsVerifyHandler,
141
 or other verification.`,
143
 or other verification.`,
142
 			helpShort: `$bVERIFY$b lets you complete account registration.`,
144
 			helpShort: `$bVERIFY$b lets you complete account registration.`,
143
 			enabled:   servCmdRequiresAccreg,
145
 			enabled:   servCmdRequiresAccreg,
146
+			minParams: 2,
144
 		},
147
 		},
145
 		"passwd": {
148
 		"passwd": {
146
 			handler: nsPasswdHandler,
149
 			handler: nsPasswdHandler,
153
 password by supplying their username and then the desired password.`,
156
 password by supplying their username and then the desired password.`,
154
 			helpShort: `$bPASSWD$b lets you change your password.`,
157
 			helpShort: `$bPASSWD$b lets you change your password.`,
155
 			enabled:   servCmdRequiresAuthEnabled,
158
 			enabled:   servCmdRequiresAuthEnabled,
159
+			minParams: 2,
156
 		},
160
 		},
157
 	}
161
 	}
158
 )
162
 )
162
 	rb.Add(nil, "NickServ", "NOTICE", rb.target.Nick(), text)
166
 	rb.Add(nil, "NickServ", "NOTICE", rb.target.Nick(), text)
163
 }
167
 }
164
 
168
 
165
-func nsDropHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
169
+func nsDropHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
166
 	sadrop := command == "sadrop"
170
 	sadrop := command == "sadrop"
167
-	nick, _ := utils.ExtractParam(params)
171
+	var nick string
172
+	if len(params) > 0 {
173
+		nick = params[0]
174
+	} else {
175
+		nick = client.NickCasefolded()
176
+	}
168
 
177
 
169
 	err := server.accounts.SetNickReserved(client, nick, sadrop, false)
178
 	err := server.accounts.SetNickReserved(client, nick, sadrop, false)
170
 	if err == nil {
179
 	if err == nil {
173
 		nsNotice(rb, client.t("You're not logged into an account"))
182
 		nsNotice(rb, client.t("You're not logged into an account"))
174
 	} else if err == errAccountCantDropPrimaryNick {
183
 	} else if err == errAccountCantDropPrimaryNick {
175
 		nsNotice(rb, client.t("You can't ungroup your primary nickname (try unregistering your account instead)"))
184
 		nsNotice(rb, client.t("You can't ungroup your primary nickname (try unregistering your account instead)"))
176
-	} else if err == errNicknameReserved {
177
-		nsNotice(rb, client.t("That nickname is already reserved by someone else"))
178
 	} else {
185
 	} else {
179
 		nsNotice(rb, client.t("Could not ungroup nick"))
186
 		nsNotice(rb, client.t("Could not ungroup nick"))
180
 	}
187
 	}
181
 }
188
 }
182
 
189
 
183
-func nsGhostHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
184
-	nick, _ := utils.ExtractParam(params)
190
+func nsGhostHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
191
+	nick := params[0]
185
 
192
 
186
 	ghost := server.clients.Get(nick)
193
 	ghost := server.clients.Get(nick)
187
 	if ghost == nil {
194
 	if ghost == nil {
207
 	ghost.destroy(false)
214
 	ghost.destroy(false)
208
 }
215
 }
209
 
216
 
210
-func nsGroupHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
217
+func nsGroupHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
211
 	nick := client.NickCasefolded()
218
 	nick := client.NickCasefolded()
212
 	err := server.accounts.SetNickReserved(client, nick, false, true)
219
 	err := server.accounts.SetNickReserved(client, nick, false, true)
213
 	if err == nil {
220
 	if err == nil {
230
 	return true
237
 	return true
231
 }
238
 }
232
 
239
 
233
-func nsIdentifyHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
240
+func nsIdentifyHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
234
 	loginSuccessful := false
241
 	loginSuccessful := false
235
 
242
 
236
-	username, passphrase := utils.ExtractParam(params)
243
+	username := params[0]
244
+	var passphrase string
245
+	if len(params) > 1 {
246
+		passphrase = params[1]
247
+	}
237
 
248
 
238
 	// try passphrase
249
 	// try passphrase
239
-	if username != "" && passphrase != "" {
250
+	if passphrase != "" {
240
 		if !nsLoginThrottleCheck(client, rb) {
251
 		if !nsLoginThrottleCheck(client, rb) {
241
 			return
252
 			return
242
 		}
253
 		}
257
 	}
268
 	}
258
 }
269
 }
259
 
270
 
260
-func nsInfoHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
261
-	nick, _ := utils.ExtractParam(params)
262
-
263
-	if nick == "" {
264
-		nick = client.Nick()
265
-	}
266
-
267
-	accountName := nick
268
-	if server.AccountConfig().NickReservation.Enabled {
269
-		accountName = server.accounts.NickToAccount(nick)
271
+func nsInfoHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
272
+	var accountName string
273
+	if len(params) > 0 {
274
+		nick := params[0]
275
+		if server.AccountConfig().NickReservation.Enabled {
276
+			accountName = server.accounts.NickToAccount(nick)
277
+			if accountName == "" {
278
+				nsNotice(rb, client.t("That nickname is not registered"))
279
+				return
280
+			}
281
+		} else {
282
+			accountName = nick
283
+		}
284
+	} else {
285
+		accountName = client.Account()
270
 		if accountName == "" {
286
 		if accountName == "" {
271
-			nsNotice(rb, client.t("That nickname is not registered"))
287
+			nsNotice(rb, client.t("You're not logged into an account"))
272
 			return
288
 			return
273
 		}
289
 		}
274
 	}
290
 	}
276
 	account, err := server.accounts.LoadAccount(accountName)
292
 	account, err := server.accounts.LoadAccount(accountName)
277
 	if err != nil || !account.Verified {
293
 	if err != nil || !account.Verified {
278
 		nsNotice(rb, client.t("Account does not exist"))
294
 		nsNotice(rb, client.t("Account does not exist"))
295
+		return
279
 	}
296
 	}
280
 
297
 
281
 	nsNotice(rb, fmt.Sprintf(client.t("Account: %s"), account.Name))
298
 	nsNotice(rb, fmt.Sprintf(client.t("Account: %s"), account.Name))
287
 	}
304
 	}
288
 }
305
 }
289
 
306
 
290
-func nsRegisterHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
307
+func nsRegisterHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
291
 	// get params
308
 	// get params
292
-	username, afterUsername := utils.ExtractParam(params)
293
-	email, passphrase := utils.ExtractParam(afterUsername)
309
+	username, email := params[0], params[1]
310
+	var passphrase string
311
+	if len(params) > 0 {
312
+		passphrase = params[2]
313
+	}
294
 
314
 
295
 	if !server.AccountConfig().Registration.Enabled {
315
 	if !server.AccountConfig().Registration.Enabled {
296
 		nsNotice(rb, client.t("Account registration has been disabled"))
316
 		nsNotice(rb, client.t("Account registration has been disabled"))
366
 	}
386
 	}
367
 }
387
 }
368
 
388
 
369
-func nsUnregisterHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
370
-	username, verificationCode := utils.ExtractParam(params)
371
-
372
-	if !server.AccountConfig().Registration.Enabled {
373
-		nsNotice(rb, client.t("Account registration has been disabled"))
374
-		return
389
+func nsUnregisterHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
390
+	username := params[0]
391
+	var verificationCode string
392
+	if len(params) > 1 {
393
+		verificationCode = params[1]
375
 	}
394
 	}
376
 
395
 
377
 	if username == "" {
396
 	if username == "" {
415
 	}
434
 	}
416
 }
435
 }
417
 
436
 
418
-func nsVerifyHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
419
-	username, code := utils.ExtractParam(params)
420
-
437
+func nsVerifyHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
438
+	username, code := params[0], params[1]
421
 	err := server.accounts.Verify(client, username, code)
439
 	err := server.accounts.Verify(client, username, code)
422
 
440
 
423
 	var errorMessage string
441
 	var errorMessage string
435
 	sendSuccessfulRegResponse(client, rb, true)
453
 	sendSuccessfulRegResponse(client, rb, true)
436
 }
454
 }
437
 
455
 
438
-func nsPasswdHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
456
+func nsPasswdHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
439
 	var target string
457
 	var target string
440
 	var newPassword string
458
 	var newPassword string
441
 	var errorMessage string
459
 	var errorMessage string
445
 		return
463
 		return
446
 	}
464
 	}
447
 
465
 
448
-	fields := strings.Fields(params)
449
-	switch len(fields) {
466
+	switch len(params) {
450
 	case 2:
467
 	case 2:
451
 		if !hasPrivs {
468
 		if !hasPrivs {
452
 			errorMessage = "Insufficient privileges"
469
 			errorMessage = "Insufficient privileges"
453
 		} else {
470
 		} else {
454
-			target, newPassword = fields[0], fields[1]
471
+			target, newPassword = params[0], params[1]
455
 		}
472
 		}
456
 	case 3:
473
 	case 3:
457
 		target = client.Account()
474
 		target = client.Account()
458
 		if target == "" {
475
 		if target == "" {
459
 			errorMessage = "You're not logged into an account"
476
 			errorMessage = "You're not logged into an account"
460
-		} else if fields[1] != fields[2] {
477
+		} else if params[1] != params[2] {
461
 			errorMessage = "Passwords do not match"
478
 			errorMessage = "Passwords do not match"
462
 		} else {
479
 		} else {
463
 			// check that they correctly supplied the preexisting password
480
 			// check that they correctly supplied the preexisting password
464
-			_, err := server.accounts.checkPassphrase(target, fields[0])
481
+			_, err := server.accounts.checkPassphrase(target, params[0])
465
 			if err != nil {
482
 			if err != nil {
466
 				errorMessage = "Password incorrect"
483
 				errorMessage = "Password incorrect"
467
 			} else {
484
 			} else {
468
-				newPassword = fields[1]
485
+				newPassword = params[1]
469
 			}
486
 			}
470
 		}
487
 		}
471
 	default:
488
 	default:
486
 	}
503
 	}
487
 }
504
 }
488
 
505
 
489
-func nsEnforceHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
490
-	arg := strings.TrimSpace(params)
491
-
492
-	if arg == "" {
506
+func nsEnforceHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
507
+	if len(params) == 0 {
493
 		status := server.accounts.getStoredEnforcementStatus(client.Account())
508
 		status := server.accounts.getStoredEnforcementStatus(client.Account())
494
 		nsNotice(rb, fmt.Sprintf(client.t("Your current nickname enforcement is: %s"), status))
509
 		nsNotice(rb, fmt.Sprintf(client.t("Your current nickname enforcement is: %s"), status))
495
 	} else {
510
 	} else {
496
-		method, err := nickReservationFromString(arg)
511
+		method, err := nickReservationFromString(params[0])
497
 		if err != nil {
512
 		if err != nil {
498
 			nsNotice(rb, client.t("Invalid parameters"))
513
 			nsNotice(rb, client.t("Invalid parameters"))
499
 			return
514
 			return

+ 49
- 15
irc/services.go View File

11
 
11
 
12
 	"github.com/goshuirc/irc-go/ircfmt"
12
 	"github.com/goshuirc/irc-go/ircfmt"
13
 	"github.com/goshuirc/irc-go/ircmsg"
13
 	"github.com/goshuirc/irc-go/ircmsg"
14
-
15
 	"github.com/oragono/oragono/irc/utils"
14
 	"github.com/oragono/oragono/irc/utils"
16
 )
15
 )
17
 
16
 
28
 type serviceCommand struct {
27
 type serviceCommand struct {
29
 	aliasOf      string   // marks this command as an alias of another
28
 	aliasOf      string   // marks this command as an alias of another
30
 	capabs       []string // oper capabs the given user has to have to access this command
29
 	capabs       []string // oper capabs the given user has to have to access this command
31
-	handler      func(server *Server, client *Client, command, params string, rb *ResponseBuffer)
30
+	handler      func(server *Server, client *Client, command string, params []string, rb *ResponseBuffer)
32
 	help         string
31
 	help         string
33
 	helpShort    string
32
 	helpShort    string
34
 	authRequired bool
33
 	authRequired bool
35
-	enabled      func(*Server) bool // is this command enabled in the server config?
34
+	enabled      func(*Config) bool // is this command enabled in the server config?
35
+	minParams    int
36
+	maxParams    int // split into at most n params, with last param containing remaining unsplit text
36
 }
37
 }
37
 
38
 
38
 // looks up a command in the table of command definitions for a service, resolving aliases
39
 // looks up a command in the table of command definitions for a service, resolving aliases
90
 	helpShort: `$bHELP$b shows in-depth information about commands.`,
91
 	helpShort: `$bHELP$b shows in-depth information about commands.`,
91
 }
92
 }
92
 
93
 
93
-// this handles IRC commands like `/NICKSERV INFO`, translating into `/MSG NICKSERV INFO`
94
+// generic handler for IRC commands like `/NICKSERV INFO`
94
 func serviceCmdHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool {
95
 func serviceCmdHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool {
95
 	service, ok := oragonoServicesByCommandAlias[msg.Command]
96
 	service, ok := oragonoServicesByCommandAlias[msg.Command]
96
 	if !ok {
97
 	if !ok {
98
 		return false
99
 		return false
99
 	}
100
 	}
100
 
101
 
101
-	fakePrivmsg := strings.Join(msg.Params, " ")
102
-	servicePrivmsgHandler(service, server, client, fakePrivmsg, rb)
102
+	if len(msg.Params) == 0 {
103
+		return false
104
+	}
105
+	commandName := strings.ToLower(msg.Params[0])
106
+	params := msg.Params[1:]
107
+	cmd := lookupServiceCommand(service.Commands, commandName)
108
+	// for a maxParams command, join all final parameters together if necessary
109
+	if cmd != nil && cmd.maxParams != 0 && cmd.maxParams < len(params) {
110
+		newParams := make([]string, cmd.maxParams)
111
+		copy(newParams, params[:cmd.maxParams-1])
112
+		newParams[cmd.maxParams-1] = strings.Join(params[cmd.maxParams-1:], " ")
113
+		params = newParams
114
+	}
115
+	serviceRunCommand(service, server, client, cmd, commandName, params, rb)
103
 	return false
116
 	return false
104
 }
117
 }
105
 
118
 
106
-// generic handler for service PRIVMSG
119
+// generic handler for service PRIVMSG, like `/msg NickServ INFO`
107
 func servicePrivmsgHandler(service *ircService, server *Server, client *Client, message string, rb *ResponseBuffer) {
120
 func servicePrivmsgHandler(service *ircService, server *Server, client *Client, message string, rb *ResponseBuffer) {
108
-	commandName, params := utils.ExtractParam(message)
109
-	commandName = strings.ToLower(commandName)
121
+	params := strings.Fields(message)
122
+	if len(params) == 0 {
123
+		return
124
+	}
125
+
126
+	// look up the service command to see how to parse it
127
+	commandName := strings.ToLower(params[0])
128
+	cmd := lookupServiceCommand(service.Commands, commandName)
129
+	// reparse if needed
130
+	if cmd != nil && cmd.maxParams != 0 {
131
+		params = utils.FieldsN(message, cmd.maxParams+1)[1:]
132
+	} else {
133
+		params = params[1:]
134
+	}
135
+	serviceRunCommand(service, server, client, cmd, commandName, params, rb)
136
+}
110
 
137
 
138
+// actually execute a service command
139
+func serviceRunCommand(service *ircService, server *Server, client *Client, cmd *serviceCommand, commandName string, params []string, rb *ResponseBuffer) {
111
 	nick := rb.target.Nick()
140
 	nick := rb.target.Nick()
112
 	sendNotice := func(notice string) {
141
 	sendNotice := func(notice string) {
113
 		rb.Add(nil, service.Name, "NOTICE", nick, notice)
142
 		rb.Add(nil, service.Name, "NOTICE", nick, notice)
114
 	}
143
 	}
115
 
144
 
116
-	cmd := lookupServiceCommand(service.Commands, commandName)
117
 	if cmd == nil {
145
 	if cmd == nil {
118
 		sendNotice(fmt.Sprintf("%s /%s HELP", client.t("Unknown command. To see available commands, run"), service.ShortName))
146
 		sendNotice(fmt.Sprintf("%s /%s HELP", client.t("Unknown command. To see available commands, run"), service.ShortName))
119
 		return
147
 		return
120
 	}
148
 	}
121
 
149
 
122
-	if cmd.enabled != nil && !cmd.enabled(server) {
150
+	if len(params) < cmd.minParams {
151
+		sendNotice(fmt.Sprintf(client.t("Invalid parameters. For usage, do /msg %s HELP %s"), service.Name, strings.ToUpper(commandName)))
152
+		return
153
+	}
154
+
155
+	if cmd.enabled != nil && !cmd.enabled(server.Config()) {
123
 		sendNotice(client.t("This command has been disabled by the server administrators"))
156
 		sendNotice(client.t("This command has been disabled by the server administrators"))
124
 		return
157
 		return
125
 	}
158
 	}
143
 }
176
 }
144
 
177
 
145
 // generic handler that displays help for service commands
178
 // generic handler that displays help for service commands
146
-func serviceHelpHandler(service *ircService, server *Server, client *Client, params string, rb *ResponseBuffer) {
179
+func serviceHelpHandler(service *ircService, server *Server, client *Client, params []string, rb *ResponseBuffer) {
147
 	nick := rb.target.Nick()
180
 	nick := rb.target.Nick()
181
+	config := server.Config()
148
 	sendNotice := func(notice string) {
182
 	sendNotice := func(notice string) {
149
 		rb.Add(nil, service.Name, "NOTICE", nick, notice)
183
 		rb.Add(nil, service.Name, "NOTICE", nick, notice)
150
 	}
184
 	}
151
 
185
 
152
 	sendNotice(ircfmt.Unescape(fmt.Sprintf("*** $b%s HELP$b ***", service.Name)))
186
 	sendNotice(ircfmt.Unescape(fmt.Sprintf("*** $b%s HELP$b ***", service.Name)))
153
 
187
 
154
-	if params == "" {
188
+	if len(params) == 0 {
155
 		// show general help
189
 		// show general help
156
 		var shownHelpLines sort.StringSlice
190
 		var shownHelpLines sort.StringSlice
157
 		var disabledCommands bool
191
 		var disabledCommands bool
163
 			if commandInfo.aliasOf != "" {
197
 			if commandInfo.aliasOf != "" {
164
 				continue // don't show help lines for aliases
198
 				continue // don't show help lines for aliases
165
 			}
199
 			}
166
-			if commandInfo.enabled != nil && !commandInfo.enabled(server) {
200
+			if commandInfo.enabled != nil && !commandInfo.enabled(config) {
167
 				disabledCommands = true
201
 				disabledCommands = true
168
 				continue
202
 				continue
169
 			}
203
 			}
187
 			sendNotice(line)
221
 			sendNotice(line)
188
 		}
222
 		}
189
 	} else {
223
 	} else {
190
-		commandName := strings.ToLower(strings.TrimSpace(params))
224
+		commandName := strings.ToLower(params[0])
191
 		commandInfo := lookupServiceCommand(service.Commands, commandName)
225
 		commandInfo := lookupServiceCommand(service.Commands, commandName)
192
 		if commandInfo == nil {
226
 		if commandInfo == nil {
193
 			sendNotice(client.t(fmt.Sprintf("Unknown command. To see available commands, run /%s HELP", service.ShortName)))
227
 			sendNotice(client.t(fmt.Sprintf("Unknown command. To see available commands, run /%s HELP", service.ShortName)))

+ 0
- 13
irc/utils/args.go View File

3
 
3
 
4
 package utils
4
 package utils
5
 
5
 
6
-import "strings"
7
-
8
-// ExtractParam extracts a parameter from the given string, returning the param and the rest of the string.
9
-func ExtractParam(line string) (string, string) {
10
-	rawParams := strings.SplitN(strings.TrimSpace(line), " ", 2)
11
-	param0 := rawParams[0]
12
-	var param1 string
13
-	if 1 < len(rawParams) {
14
-		param1 = strings.TrimSpace(rawParams[1])
15
-	}
16
-	return param0, param1
17
-}
18
-
19
 // ArgsToStrings takes the arguments and splits them into a series of strings,
6
 // ArgsToStrings takes the arguments and splits them into a series of strings,
20
 // each argument separated by delim and each string bounded by maxLength.
7
 // each argument separated by delim and each string bounded by maxLength.
21
 func ArgsToStrings(maxLength int, arguments []string, delim string) []string {
8
 func ArgsToStrings(maxLength int, arguments []string, delim string) []string {

+ 52
- 0
irc/utils/fieldsn.go View File

1
+package utils
2
+
3
+// Copyright (c) 2014 Kevin Wallace <kevin@pentabarf.net>
4
+// Found here: https://github.com/kevinwallace/fieldsn
5
+// Released under the MIT license
6
+// XXX this implementation treats negative n as "return nil",
7
+// unlike stdlib SplitN and friends, which treat it as "no limit"
8
+
9
+// Original source code below:
10
+
11
+// Package fieldsn implements FieldsN and FieldsFuncN,
12
+// which are conspicuously missing from the strings package.
13
+
14
+import (
15
+	"unicode"
16
+)
17
+
18
+// FieldsN is like strings.Fields, but returns at most n fields,
19
+// and the nth field includes any whitespace at the end of the string.
20
+func FieldsN(s string, n int) []string {
21
+	return FieldsFuncN(s, unicode.IsSpace, n)
22
+}
23
+
24
+// FieldsFuncN is like strings.FieldsFunc, but returns at most n fields,
25
+// and the nth field includes any runes at the end of the string normally excluded by f.
26
+func FieldsFuncN(s string, f func(rune) bool, n int) []string {
27
+	if n <= 0 {
28
+		return nil
29
+	}
30
+
31
+	a := make([]string, 0, n)
32
+	na := 0
33
+	fieldStart := -1
34
+	for i, rune := range s {
35
+		if f(rune) {
36
+			if fieldStart >= 0 {
37
+				a = append(a, s[fieldStart:i])
38
+				na++
39
+				fieldStart = -1
40
+			}
41
+		} else if fieldStart == -1 {
42
+			fieldStart = i
43
+			if na+1 == n {
44
+				break
45
+			}
46
+		}
47
+	}
48
+	if fieldStart >= 0 {
49
+		a = append(a, s[fieldStart:])
50
+	}
51
+	return a
52
+}

Loading…
Cancel
Save