You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

nickserv.go 27KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837
  1. // Copyright (c) 2017 Daniel Oaks <daniel@danieloaks.net>
  2. // released under the MIT license
  3. package irc
  4. import (
  5. "fmt"
  6. "strconv"
  7. "strings"
  8. "time"
  9. "github.com/goshuirc/irc-go/ircfmt"
  10. "github.com/oragono/oragono/irc/modes"
  11. "github.com/oragono/oragono/irc/utils"
  12. )
  13. // "enabled" callbacks for specific nickserv commands
  14. func servCmdRequiresAccreg(config *Config) bool {
  15. return config.Accounts.Registration.Enabled
  16. }
  17. func servCmdRequiresAuthEnabled(config *Config) bool {
  18. return config.Accounts.AuthenticationEnabled
  19. }
  20. func servCmdRequiresNickRes(config *Config) bool {
  21. return config.Accounts.AuthenticationEnabled && config.Accounts.NickReservation.Enabled
  22. }
  23. func servCmdRequiresBouncerEnabled(config *Config) bool {
  24. return config.Accounts.Bouncer.Enabled
  25. }
  26. const (
  27. nsPrefix = "NickServ!NickServ@localhost"
  28. // ZNC's nickserv module will not detect this unless it is:
  29. // 1. sent with prefix `nickserv`
  30. // 2. contains the string "identify"
  31. // 3. contains at least one of several other magic strings ("msg" works)
  32. nsTimeoutNotice = `This nickname is reserved. Please login within %v (using $b/msg NickServ IDENTIFY <password>$b or SASL), or switch to a different nickname.`
  33. )
  34. const nickservHelp = `NickServ lets you register and login to an account.
  35. To see in-depth help for a specific NickServ command, try:
  36. $b/NS HELP <command>$b
  37. Here are the commands you can use:
  38. %s`
  39. var (
  40. nickservCommands = map[string]*serviceCommand{
  41. "drop": {
  42. handler: nsDropHandler,
  43. help: `Syntax: $bDROP [nickname]$b
  44. DROP de-links the given (or your current) nickname from your user account.`,
  45. helpShort: `$bDROP$b de-links your current (or the given) nickname from your user account.`,
  46. enabled: servCmdRequiresNickRes,
  47. authRequired: true,
  48. },
  49. "enforce": {
  50. hidden: true,
  51. handler: nsEnforceHandler,
  52. help: `Syntax: $bENFORCE [method]$b
  53. ENFORCE is an alias for $bGET enforce$b and $bSET enforce$b. See the help
  54. entry for $bSET$b for more information.`,
  55. authRequired: true,
  56. enabled: servCmdRequiresAccreg,
  57. },
  58. "ghost": {
  59. handler: nsGhostHandler,
  60. help: `Syntax: $bGHOST <nickname>$b
  61. GHOST disconnects the given user from the network if they're logged in with the
  62. same user account, letting you reclaim your nickname.`,
  63. helpShort: `$bGHOST$b reclaims your nickname.`,
  64. authRequired: true,
  65. minParams: 1,
  66. },
  67. "group": {
  68. handler: nsGroupHandler,
  69. help: `Syntax: $bGROUP$b
  70. GROUP links your current nickname with your logged-in account, so other people
  71. will not be able to use it.`,
  72. helpShort: `$bGROUP$b links your current nickname to your user account.`,
  73. enabled: servCmdRequiresNickRes,
  74. authRequired: true,
  75. },
  76. "identify": {
  77. handler: nsIdentifyHandler,
  78. help: `Syntax: $bIDENTIFY <username> [password]$b
  79. IDENTIFY lets you login to the given username using either password auth, or
  80. certfp (your client certificate) if a password is not given.`,
  81. helpShort: `$bIDENTIFY$b lets you login to your account.`,
  82. minParams: 1,
  83. },
  84. "info": {
  85. handler: nsInfoHandler,
  86. help: `Syntax: $bINFO [username]$b
  87. INFO gives you information about the given (or your own) user account.`,
  88. helpShort: `$bINFO$b gives you information on a user account.`,
  89. },
  90. "register": {
  91. handler: nsRegisterHandler,
  92. // TODO: "email" is an oversimplification here; it's actually any callback, e.g.,
  93. // person@example.com, mailto:person@example.com, tel:16505551234.
  94. help: `Syntax: $bREGISTER <username> <email> [password]$b
  95. REGISTER lets you register a user account. If the server allows anonymous
  96. registration, you can send an asterisk (*) as the email address.
  97. If the password is left out, your account will be registered to your TLS client
  98. certificate (and you will need to use that certificate to login in future).`,
  99. helpShort: `$bREGISTER$b lets you register a user account.`,
  100. enabled: servCmdRequiresAccreg,
  101. minParams: 2,
  102. },
  103. "sadrop": {
  104. handler: nsDropHandler,
  105. help: `Syntax: $bSADROP <nickname>$b
  106. SADROP forcibly de-links the given nickname from the attached user account.`,
  107. helpShort: `$bSADROP$b forcibly de-links the given nickname from its user account.`,
  108. capabs: []string{"accreg"},
  109. enabled: servCmdRequiresNickRes,
  110. minParams: 1,
  111. },
  112. "saregister": {
  113. handler: nsSaregisterHandler,
  114. help: `Syntax: $bSAREGISTER <username> <password>$b
  115. SAREGISTER registers an account on someone else's behalf.
  116. This is for use in configurations that require SASL for all connections;
  117. an administrator can set use this command to set up user accounts.`,
  118. helpShort: `$bSAREGISTER$b registers an account on someone else's behalf.`,
  119. enabled: servCmdRequiresAuthEnabled,
  120. capabs: []string{"accreg"},
  121. minParams: 2,
  122. },
  123. "sessions": {
  124. handler: nsSessionsHandler,
  125. help: `Syntax: $bSESSIONS [nickname]$b
  126. SESSIONS lists information about the sessions currently attached, via
  127. the server's bouncer functionality, to your nickname. An administrator
  128. can use this command to list another user's sessions.`,
  129. helpShort: `$bSESSIONS$b lists the sessions attached to a nickname.`,
  130. enabled: servCmdRequiresBouncerEnabled,
  131. },
  132. "unregister": {
  133. handler: nsUnregisterHandler,
  134. help: `Syntax: $bUNREGISTER <username> [code]$b
  135. UNREGISTER lets you delete your user account (or someone else's, if you're an
  136. IRC operator with the correct permissions). To prevent accidental
  137. unregistrations, a verification code is required; invoking the command without
  138. a code will display the necessary code.`,
  139. helpShort: `$bUNREGISTER$b lets you delete your user account.`,
  140. enabled: servCmdRequiresAuthEnabled,
  141. minParams: 1,
  142. },
  143. "verify": {
  144. handler: nsVerifyHandler,
  145. help: `Syntax: $bVERIFY <username> <code>$b
  146. VERIFY lets you complete an account registration, if the server requires email
  147. or other verification.`,
  148. helpShort: `$bVERIFY$b lets you complete account registration.`,
  149. enabled: servCmdRequiresAccreg,
  150. minParams: 2,
  151. },
  152. "passwd": {
  153. handler: nsPasswdHandler,
  154. help: `Syntax: $bPASSWD <current> <new> <new_again>$b
  155. Or: $bPASSWD <username> <new>$b
  156. PASSWD lets you change your account password. You must supply your current
  157. password and confirm the new one by typing it twice. If you're an IRC operator
  158. with the correct permissions, you can use PASSWD to reset someone else's
  159. password by supplying their username and then the desired password.`,
  160. helpShort: `$bPASSWD$b lets you change your password.`,
  161. enabled: servCmdRequiresAuthEnabled,
  162. minParams: 2,
  163. },
  164. "get": {
  165. handler: nsGetHandler,
  166. help: `Syntax: $bGET <setting>$b
  167. GET queries the current values of your account settings. For more information
  168. on the settings and their possible values, see HELP SET.`,
  169. helpShort: `$bGET$b queries the current values of your account settings`,
  170. authRequired: true,
  171. enabled: servCmdRequiresAccreg,
  172. minParams: 1,
  173. },
  174. "saget": {
  175. handler: nsGetHandler,
  176. help: `Syntax: $bSAGET <account> <setting>$b
  177. SAGET queries the values of someone else's account settings. For more
  178. information on the settings and their possible values, see HELP SET.`,
  179. helpShort: `$bSAGET$b queries the current values of another user's account settings`,
  180. enabled: servCmdRequiresAccreg,
  181. minParams: 2,
  182. capabs: []string{"accreg"},
  183. },
  184. "set": {
  185. handler: nsSetHandler,
  186. helpShort: `$bSET$b modifies your account settings`,
  187. // these are broken out as separate strings so they can be translated separately
  188. helpStrings: []string{
  189. `Syntax $bSET <setting> <value>$b
  190. Set modifies your account settings. The following settings are available:`,
  191. `$bENFORCE$b
  192. 'enforce' lets you specify a custom enforcement mechanism for your registered
  193. nicknames. Your options are:
  194. 1. 'none' [no enforcement, overriding the server default]
  195. 2. 'timeout' [anyone using the nick must authenticate before a deadline,
  196. or else they will be renamed]
  197. 3. 'strict' [you must already be authenticated to use the nick]
  198. 4. 'default' [use the server default]`,
  199. `$bBOUNCER$b
  200. If 'bouncer' is enabled and you are already logged in and using a nick, a
  201. second client of yours that authenticates with SASL and requests the same nick
  202. is allowed to attach to the nick as well (this is comparable to the behavior
  203. of IRC "bouncers" like ZNC). Your options are 'on' (allow this behavior),
  204. 'off' (disallow it), and 'default' (use the server default value).`,
  205. `$bAUTOREPLAY-LINES$b
  206. 'autoreplay-lines' controls the number of lines of channel history that will
  207. be replayed to you automatically when joining a channel. Your options are any
  208. positive number, 0 to disable the feature, and 'default' to use the server
  209. default.`,
  210. `$bAUTOREPLAY-JOINS$b
  211. 'autoreplay-joins' controls whether autoreplayed channel history will include
  212. lines for join and part. This provides more information about the context of
  213. messages, but may be spammy. Your options are 'on' and 'off'.`,
  214. },
  215. authRequired: true,
  216. enabled: servCmdRequiresAccreg,
  217. minParams: 2,
  218. },
  219. "saset": {
  220. handler: nsSetHandler,
  221. help: `Syntax: $bSASET <account> <setting> <value>$b
  222. SASET modifies the values of someone else's account settings. For more
  223. information on the settings and their possible values, see HELP SET.`,
  224. helpShort: `$bSASET$b modifies another user's account settings`,
  225. enabled: servCmdRequiresAccreg,
  226. minParams: 3,
  227. capabs: []string{"accreg"},
  228. },
  229. }
  230. )
  231. // nsNotice sends the client a notice from NickServ
  232. func nsNotice(rb *ResponseBuffer, text string) {
  233. // XXX i can't figure out how to use OragonoServices[servicename].prefix here
  234. // without creating a compile-time initialization loop
  235. rb.Add(nil, nsPrefix, "NOTICE", rb.target.Nick(), text)
  236. }
  237. func nsGetHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  238. var account string
  239. if command == "saget" {
  240. account = params[0]
  241. params = params[1:]
  242. } else {
  243. account = client.Account()
  244. }
  245. accountData, err := server.accounts.LoadAccount(account)
  246. if err == errAccountDoesNotExist {
  247. nsNotice(rb, client.t("No such account"))
  248. return
  249. } else if err != nil {
  250. nsNotice(rb, client.t("Error loading account data"))
  251. return
  252. }
  253. displaySetting(params[0], accountData.Settings, client, rb)
  254. }
  255. func displaySetting(settingName string, settings AccountSettings, client *Client, rb *ResponseBuffer) {
  256. config := client.server.Config()
  257. switch strings.ToLower(settingName) {
  258. case "enforce":
  259. storedValue := settings.NickEnforcement
  260. serializedStoredValue := nickReservationToString(storedValue)
  261. nsNotice(rb, fmt.Sprintf(client.t("Your stored nickname enforcement setting is: %s"), serializedStoredValue))
  262. serializedActualValue := nickReservationToString(configuredEnforcementMethod(config, storedValue))
  263. nsNotice(rb, fmt.Sprintf(client.t("Given current server settings, your nickname is enforced with: %s"), serializedActualValue))
  264. case "autoreplay-lines":
  265. if settings.AutoreplayLines == nil {
  266. nsNotice(rb, fmt.Sprintf(client.t("You will receive the server default of %d lines of autoreplayed history"), config.History.AutoreplayOnJoin))
  267. } else {
  268. nsNotice(rb, fmt.Sprintf(client.t("You will receive %d lines of autoreplayed history"), *settings.AutoreplayLines))
  269. }
  270. case "autoreplay-joins":
  271. if settings.AutoreplayJoins {
  272. nsNotice(rb, client.t("You will see JOINs and PARTs in autoreplayed history lines"))
  273. } else {
  274. nsNotice(rb, client.t("You will not see JOINs and PARTs in autoreplayed history lines"))
  275. }
  276. case "bouncer":
  277. if !config.Accounts.Bouncer.Enabled {
  278. nsNotice(rb, fmt.Sprintf(client.t("This feature has been disabled by the server administrators")))
  279. } else {
  280. switch settings.AllowBouncer {
  281. case BouncerAllowedServerDefault:
  282. if config.Accounts.Bouncer.AllowedByDefault {
  283. nsNotice(rb, fmt.Sprintf(client.t("Bouncer functionality is currently enabled for your account, but you can opt out")))
  284. } else {
  285. nsNotice(rb, fmt.Sprintf(client.t("Bouncer functionality is currently disabled for your account, but you can opt in")))
  286. }
  287. case BouncerDisallowedByUser:
  288. nsNotice(rb, fmt.Sprintf(client.t("Bouncer functionality is currently disabled for your account")))
  289. case BouncerAllowedByUser:
  290. nsNotice(rb, fmt.Sprintf(client.t("Bouncer functionality is currently enabled for your account")))
  291. }
  292. }
  293. default:
  294. nsNotice(rb, client.t("No such setting"))
  295. }
  296. }
  297. func nsSetHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  298. var account string
  299. if command == "saset" {
  300. account = params[0]
  301. params = params[1:]
  302. } else {
  303. account = client.Account()
  304. }
  305. var munger settingsMunger
  306. var finalSettings AccountSettings
  307. var err error
  308. switch strings.ToLower(params[0]) {
  309. case "pass":
  310. nsNotice(rb, client.t("To change a password, use the PASSWD command. For details, /msg NickServ HELP PASSWD"))
  311. return
  312. case "enforce":
  313. var method NickEnforcementMethod
  314. method, err = nickReservationFromString(params[1])
  315. if err != nil {
  316. err = errInvalidParams
  317. break
  318. }
  319. // updating enforcement settings is special-cased, because it requires
  320. // an update to server.accounts.accountToMethod
  321. finalSettings, err = server.accounts.SetEnforcementStatus(account, method)
  322. if err == nil {
  323. finalSettings.NickEnforcement = method // success
  324. }
  325. case "autoreplay-lines":
  326. var newValue *int
  327. if strings.ToLower(params[1]) != "default" {
  328. val, err_ := strconv.Atoi(params[1])
  329. if err_ != nil || val < 0 {
  330. err = errInvalidParams
  331. break
  332. }
  333. newValue = new(int)
  334. *newValue = val
  335. }
  336. munger = func(in AccountSettings) (out AccountSettings, err error) {
  337. out = in
  338. out.AutoreplayLines = newValue
  339. return
  340. }
  341. case "bouncer":
  342. var newValue BouncerAllowedSetting
  343. if strings.ToLower(params[1]) == "default" {
  344. newValue = BouncerAllowedServerDefault
  345. } else {
  346. var enabled bool
  347. enabled, err = utils.StringToBool(params[1])
  348. if enabled {
  349. newValue = BouncerAllowedByUser
  350. } else {
  351. newValue = BouncerDisallowedByUser
  352. }
  353. }
  354. if err == nil {
  355. munger = func(in AccountSettings) (out AccountSettings, err error) {
  356. out = in
  357. out.AllowBouncer = newValue
  358. return
  359. }
  360. }
  361. case "autoreplay-joins":
  362. var newValue bool
  363. newValue, err = utils.StringToBool(params[1])
  364. if err == nil {
  365. munger = func(in AccountSettings) (out AccountSettings, err error) {
  366. out = in
  367. out.AutoreplayJoins = newValue
  368. return
  369. }
  370. }
  371. default:
  372. err = errInvalidParams
  373. }
  374. if munger != nil {
  375. finalSettings, err = server.accounts.ModifyAccountSettings(account, munger)
  376. }
  377. switch err {
  378. case nil:
  379. nsNotice(rb, client.t("Successfully changed your account settings"))
  380. displaySetting(params[0], finalSettings, client, rb)
  381. case errInvalidParams, errAccountDoesNotExist, errFeatureDisabled, errAccountUnverified, errAccountUpdateFailed:
  382. nsNotice(rb, client.t(err.Error()))
  383. default:
  384. // unknown error
  385. nsNotice(rb, client.t("An error occurred"))
  386. }
  387. }
  388. func nsDropHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  389. sadrop := command == "sadrop"
  390. var nick string
  391. if len(params) > 0 {
  392. nick = params[0]
  393. } else {
  394. nick = client.NickCasefolded()
  395. }
  396. err := server.accounts.SetNickReserved(client, nick, sadrop, false)
  397. if err == nil {
  398. nsNotice(rb, fmt.Sprintf(client.t("Successfully ungrouped nick %s with your account"), nick))
  399. } else if err == errAccountNotLoggedIn {
  400. nsNotice(rb, client.t("You're not logged into an account"))
  401. } else if err == errAccountCantDropPrimaryNick {
  402. nsNotice(rb, client.t("You can't ungroup your primary nickname (try unregistering your account instead)"))
  403. } else {
  404. nsNotice(rb, client.t("Could not ungroup nick"))
  405. }
  406. }
  407. func nsGhostHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  408. nick := params[0]
  409. ghost := server.clients.Get(nick)
  410. if ghost == nil {
  411. nsNotice(rb, client.t("No such nick"))
  412. return
  413. } else if ghost == client {
  414. nsNotice(rb, client.t("You can't GHOST yourself (try /QUIT instead)"))
  415. return
  416. }
  417. authorized := false
  418. account := client.Account()
  419. if account != "" {
  420. // the user must either own the nick, or the target client
  421. authorized = (server.accounts.NickToAccount(nick) == account) || (ghost.Account() == account)
  422. }
  423. if !authorized {
  424. nsNotice(rb, client.t("You don't own that nick"))
  425. return
  426. }
  427. ghost.Quit(fmt.Sprintf(ghost.t("GHOSTed by %s"), client.Nick()), nil)
  428. ghost.destroy(false, nil)
  429. }
  430. func nsGroupHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  431. nick := client.Nick()
  432. err := server.accounts.SetNickReserved(client, nick, false, true)
  433. if err == nil {
  434. nsNotice(rb, fmt.Sprintf(client.t("Successfully grouped nick %s with your account"), nick))
  435. } else if err == errAccountTooManyNicks {
  436. nsNotice(rb, client.t("You have too many nicks reserved already (you can remove some with /NS DROP)"))
  437. } else if err == errNicknameReserved {
  438. nsNotice(rb, client.t("That nickname is already reserved by someone else"))
  439. } else {
  440. nsNotice(rb, client.t("Error reserving nickname"))
  441. }
  442. }
  443. func nsLoginThrottleCheck(client *Client, rb *ResponseBuffer) (success bool) {
  444. throttled, remainingTime := client.loginThrottle.Touch()
  445. if throttled {
  446. nsNotice(rb, fmt.Sprintf(client.t("Please wait at least %v and try again"), remainingTime))
  447. return false
  448. }
  449. return true
  450. }
  451. func nsIdentifyHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  452. if client.LoggedIntoAccount() {
  453. nsNotice(rb, client.t("You're already logged into an account"))
  454. return
  455. }
  456. loginSuccessful := false
  457. var username, passphrase string
  458. if len(params) == 1 {
  459. if client.certfp != "" {
  460. username = params[0]
  461. } else {
  462. // XXX undocumented compatibility mode with other nickservs, allowing
  463. // /msg NickServ identify passphrase
  464. username = client.NickCasefolded()
  465. passphrase = params[0]
  466. }
  467. } else {
  468. username = params[0]
  469. passphrase = params[1]
  470. }
  471. // try passphrase
  472. if passphrase != "" {
  473. if !nsLoginThrottleCheck(client, rb) {
  474. return
  475. }
  476. err := server.accounts.AuthenticateByPassphrase(client, username, passphrase)
  477. loginSuccessful = (err == nil)
  478. }
  479. // try certfp
  480. if !loginSuccessful && client.certfp != "" {
  481. err := server.accounts.AuthenticateByCertFP(client)
  482. loginSuccessful = (err == nil)
  483. }
  484. if loginSuccessful {
  485. sendSuccessfulAccountAuth(client, rb, true, true)
  486. } else {
  487. nsNotice(rb, client.t("Could not login with your TLS certificate or supplied username/password"))
  488. }
  489. }
  490. func nsInfoHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  491. var accountName string
  492. if len(params) > 0 {
  493. nick := params[0]
  494. if server.AccountConfig().NickReservation.Enabled {
  495. accountName = server.accounts.NickToAccount(nick)
  496. if accountName == "" {
  497. nsNotice(rb, client.t("That nickname is not registered"))
  498. return
  499. }
  500. } else {
  501. accountName = nick
  502. }
  503. } else {
  504. accountName = client.Account()
  505. if accountName == "" {
  506. nsNotice(rb, client.t("You're not logged into an account"))
  507. return
  508. }
  509. }
  510. account, err := server.accounts.LoadAccount(accountName)
  511. if err != nil || !account.Verified {
  512. nsNotice(rb, client.t("Account does not exist"))
  513. return
  514. }
  515. nsNotice(rb, fmt.Sprintf(client.t("Account: %s"), account.Name))
  516. registeredAt := account.RegisteredAt.Format("Jan 02, 2006 15:04:05Z")
  517. nsNotice(rb, fmt.Sprintf(client.t("Registered at: %s"), registeredAt))
  518. // TODO nicer formatting for this
  519. for _, nick := range account.AdditionalNicks {
  520. nsNotice(rb, fmt.Sprintf(client.t("Additional grouped nick: %s"), nick))
  521. }
  522. for _, channel := range server.accounts.ChannelsForAccount(accountName) {
  523. nsNotice(rb, fmt.Sprintf(client.t("Registered channel: %s"), channel))
  524. }
  525. }
  526. func nsRegisterHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  527. // get params
  528. account, email := params[0], params[1]
  529. var passphrase string
  530. if len(params) > 2 {
  531. passphrase = params[2]
  532. }
  533. certfp := client.certfp
  534. if passphrase == "" && certfp == "" {
  535. nsNotice(rb, client.t("You need to either supply a passphrase or be connected via TLS with a client cert"))
  536. return
  537. }
  538. if client.LoggedIntoAccount() {
  539. nsNotice(rb, client.t("You're already logged into an account"))
  540. return
  541. }
  542. if !nsLoginThrottleCheck(client, rb) {
  543. return
  544. }
  545. // band-aid to let users know if they mix up the order of registration params
  546. if email == "*" {
  547. nsNotice(rb, client.t("Registering your account with no email address"))
  548. } else {
  549. nsNotice(rb, fmt.Sprintf(client.t("Registering your account with email address %s"), email))
  550. }
  551. config := server.AccountConfig()
  552. var callbackNamespace, callbackValue string
  553. noneCallbackAllowed := false
  554. for _, callback := range config.Registration.EnabledCallbacks {
  555. if callback == "*" {
  556. noneCallbackAllowed = true
  557. }
  558. }
  559. // XXX if ACC REGISTER allows registration with the `none` callback, then ignore
  560. // any callback that was passed here (to avoid confusion in the case where the ircd
  561. // has no mail server configured). otherwise, register using the provided callback:
  562. if noneCallbackAllowed {
  563. callbackNamespace = "*"
  564. } else {
  565. callbackNamespace, callbackValue = parseCallback(email, config)
  566. if callbackNamespace == "" {
  567. nsNotice(rb, client.t("Registration requires a valid e-mail address"))
  568. return
  569. }
  570. }
  571. err := server.accounts.Register(client, account, callbackNamespace, callbackValue, passphrase, client.certfp)
  572. if err == nil {
  573. if callbackNamespace == "*" {
  574. err = server.accounts.Verify(client, account, "")
  575. if err == nil {
  576. sendSuccessfulRegResponse(client, rb, true)
  577. }
  578. } else {
  579. messageTemplate := client.t("Account created, pending verification; verification code has been sent to %s")
  580. message := fmt.Sprintf(messageTemplate, fmt.Sprintf("%s:%s", callbackNamespace, callbackValue))
  581. nsNotice(rb, message)
  582. }
  583. }
  584. // details could not be stored and relevant numerics have been dispatched, abort
  585. message, _ := registrationErrorToMessageAndCode(err)
  586. if err != nil {
  587. nsNotice(rb, client.t(message))
  588. return
  589. }
  590. }
  591. func nsSaregisterHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  592. account, passphrase := params[0], params[1]
  593. err := server.accounts.Register(nil, account, "admin", "", passphrase, "")
  594. if err == nil {
  595. err = server.accounts.Verify(nil, account, "")
  596. }
  597. if err != nil {
  598. var errMsg string
  599. if err == errAccountAlreadyRegistered || err == errAccountAlreadyVerified {
  600. errMsg = client.t("Account already exists")
  601. } else if err == errAccountBadPassphrase {
  602. errMsg = client.t("Passphrase contains forbidden characters or is otherwise invalid")
  603. } else {
  604. server.logger.Error("services", "unknown error from saregister", err.Error())
  605. errMsg = client.t("Could not register")
  606. }
  607. nsNotice(rb, errMsg)
  608. } else {
  609. nsNotice(rb, fmt.Sprintf(client.t("Successfully registered account %s"), account))
  610. }
  611. }
  612. func nsUnregisterHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  613. username := params[0]
  614. var verificationCode string
  615. if len(params) > 1 {
  616. verificationCode = params[1]
  617. }
  618. if username == "" {
  619. nsNotice(rb, client.t("You must specify an account"))
  620. return
  621. }
  622. account, err := server.accounts.LoadAccount(username)
  623. if err == errAccountDoesNotExist {
  624. nsNotice(rb, client.t("Invalid account name"))
  625. return
  626. } else if err != nil {
  627. nsNotice(rb, client.t("Internal error"))
  628. return
  629. }
  630. cfname, _ := CasefoldName(username)
  631. if !(cfname == client.Account() || client.HasRoleCapabs("accreg")) {
  632. nsNotice(rb, client.t("Insufficient oper privs"))
  633. return
  634. }
  635. expectedCode := unregisterConfirmationCode(account.Name, account.RegisteredAt)
  636. if expectedCode != verificationCode {
  637. nsNotice(rb, ircfmt.Unescape(client.t("$bWarning: unregistering this account will remove its stored privileges.$b")))
  638. nsNotice(rb, fmt.Sprintf(client.t("To confirm account unregistration, type: /NS UNREGISTER %[1]s %[2]s"), cfname, expectedCode))
  639. return
  640. }
  641. err = server.accounts.Unregister(cfname)
  642. if err == errAccountDoesNotExist {
  643. nsNotice(rb, client.t(err.Error()))
  644. } else if err != nil {
  645. nsNotice(rb, client.t("Error while unregistering account"))
  646. } else {
  647. nsNotice(rb, fmt.Sprintf(client.t("Successfully unregistered account %s"), cfname))
  648. server.logger.Info("accounts", "client", client.Nick(), "unregistered account", cfname)
  649. }
  650. }
  651. func nsVerifyHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  652. username, code := params[0], params[1]
  653. err := server.accounts.Verify(client, username, code)
  654. var errorMessage string
  655. if err == errAccountVerificationInvalidCode || err == errAccountAlreadyVerified {
  656. errorMessage = err.Error()
  657. } else if err != nil {
  658. errorMessage = errAccountVerificationFailed.Error()
  659. }
  660. if errorMessage != "" {
  661. nsNotice(rb, client.t(errorMessage))
  662. return
  663. }
  664. sendSuccessfulRegResponse(client, rb, true)
  665. }
  666. func nsPasswdHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  667. var target string
  668. var newPassword string
  669. var errorMessage string
  670. hasPrivs := client.HasRoleCapabs("accreg")
  671. if !hasPrivs && !nsLoginThrottleCheck(client, rb) {
  672. return
  673. }
  674. switch len(params) {
  675. case 2:
  676. if !hasPrivs {
  677. errorMessage = "Insufficient privileges"
  678. } else {
  679. target, newPassword = params[0], params[1]
  680. }
  681. case 3:
  682. target = client.Account()
  683. if target == "" {
  684. errorMessage = "You're not logged into an account"
  685. } else if params[1] != params[2] {
  686. errorMessage = "Passwords do not match"
  687. } else {
  688. // check that they correctly supplied the preexisting password
  689. _, err := server.accounts.checkPassphrase(target, params[0])
  690. if err != nil {
  691. errorMessage = "Password incorrect"
  692. } else {
  693. newPassword = params[1]
  694. }
  695. }
  696. default:
  697. errorMessage = `Invalid parameters`
  698. }
  699. if errorMessage != "" {
  700. nsNotice(rb, client.t(errorMessage))
  701. return
  702. }
  703. err := server.accounts.setPassword(target, newPassword)
  704. if err == nil {
  705. nsNotice(rb, client.t("Password changed"))
  706. } else {
  707. server.logger.Error("internal", "could not upgrade user password:", err.Error())
  708. nsNotice(rb, client.t("Password could not be changed due to server error"))
  709. }
  710. }
  711. func nsEnforceHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  712. newParams := []string{"enforce"}
  713. if len(params) == 0 {
  714. nsGetHandler(server, client, "get", newParams, rb)
  715. } else {
  716. newParams = append(newParams, params[0])
  717. nsSetHandler(server, client, "set", newParams, rb)
  718. }
  719. }
  720. func nsSessionsHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  721. target := client
  722. if 0 < len(params) {
  723. // same permissions check as RPL_WHOISACTUALLY for now:
  724. if !client.HasMode(modes.Operator) {
  725. nsNotice(rb, client.t("Command restricted"))
  726. return
  727. }
  728. target = server.clients.Get(params[0])
  729. if target == nil {
  730. nsNotice(rb, client.t("No such nick"))
  731. return
  732. }
  733. }
  734. sessionData, currentIndex := target.AllSessionData(rb.session)
  735. nsNotice(rb, fmt.Sprintf(client.t("Nickname %[1]s has %[2]d attached session(s)"), target.Nick(), len(sessionData)))
  736. for i, session := range sessionData {
  737. if currentIndex == i {
  738. nsNotice(rb, fmt.Sprintf(client.t("Session %d (currently attached session):"), i+1))
  739. } else {
  740. nsNotice(rb, fmt.Sprintf(client.t("Session %d:"), i+1))
  741. }
  742. nsNotice(rb, fmt.Sprintf(client.t("IP address: %s"), session.ip.String()))
  743. nsNotice(rb, fmt.Sprintf(client.t("Hostname: %s"), session.hostname))
  744. nsNotice(rb, fmt.Sprintf(client.t("Created at: %s"), session.ctime.Format(time.RFC1123)))
  745. nsNotice(rb, fmt.Sprintf(client.t("Last active: %s"), session.atime.Format(time.RFC1123)))
  746. }
  747. }