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 58KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658
  1. // Copyright (c) 2017 Daniel Oaks <daniel@danieloaks.net>
  2. // released under the MIT license
  3. package irc
  4. import (
  5. "fmt"
  6. "regexp"
  7. "sort"
  8. "strconv"
  9. "strings"
  10. "time"
  11. "github.com/ergochat/irc-go/ircfmt"
  12. "github.com/ergochat/ergo/irc/caps"
  13. "github.com/ergochat/ergo/irc/custime"
  14. "github.com/ergochat/ergo/irc/passwd"
  15. "github.com/ergochat/ergo/irc/sno"
  16. "github.com/ergochat/ergo/irc/utils"
  17. )
  18. // "enabled" callbacks for specific nickserv commands
  19. func servCmdRequiresAccreg(config *Config) bool {
  20. return config.Accounts.Registration.Enabled
  21. }
  22. func servCmdRequiresAuthEnabled(config *Config) bool {
  23. return config.Accounts.AuthenticationEnabled
  24. }
  25. func servCmdRequiresNickRes(config *Config) bool {
  26. return config.Accounts.AuthenticationEnabled && config.Accounts.NickReservation.Enabled
  27. }
  28. func servCmdRequiresBouncerEnabled(config *Config) bool {
  29. return config.Accounts.Multiclient.Enabled
  30. }
  31. func servCmdRequiresEmailReset(config *Config) bool {
  32. return config.Accounts.Registration.EmailVerification.Enabled &&
  33. config.Accounts.Registration.EmailVerification.PasswordReset.Enabled
  34. }
  35. const nickservHelp = `NickServ lets you register, log in to, and manage an account.`
  36. var (
  37. nickservCommands = map[string]*serviceCommand{
  38. "clients": {
  39. handler: nsClientsHandler,
  40. help: `Syntax: $bCLIENTS LIST [nickname]$b
  41. CLIENTS LIST shows information about the clients currently attached, via
  42. the server's multiclient functionality, to your nickname. An administrator
  43. can use this command to list another user's clients.
  44. Syntax: $bCLIENTS LOGOUT [nickname] [client_id/all]$b
  45. CLIENTS LOGOUT detaches a single client, or all clients currently attached
  46. to your nickname. An administrator can use this command to logout another
  47. user's clients.`,
  48. helpShort: `$bCLIENTS$b can list and logout the sessions attached to a nickname.`,
  49. enabled: servCmdRequiresBouncerEnabled,
  50. minParams: 1,
  51. },
  52. "drop": {
  53. handler: nsDropHandler,
  54. help: `Syntax: $bDROP [nickname]$b
  55. DROP de-links the given (or your current) nickname from your user account.`,
  56. helpShort: `$bDROP$b de-links your current (or the given) nickname from your user account.`,
  57. enabled: servCmdRequiresNickRes,
  58. authRequired: true,
  59. },
  60. "enforce": {
  61. hidden: true,
  62. handler: nsEnforceHandler,
  63. help: `Syntax: $bENFORCE [method]$b
  64. ENFORCE is an alias for $bGET enforce$b and $bSET enforce$b. See the help
  65. entry for $bSET$b for more information.`,
  66. authRequired: true,
  67. enabled: servCmdRequiresNickRes,
  68. },
  69. "ghost": {
  70. handler: nsGhostHandler,
  71. help: `Syntax: $bGHOST <nickname>$b
  72. GHOST disconnects the given user from the network if they're logged in with the
  73. same user account, letting you reclaim your nickname.`,
  74. helpShort: `$bGHOST$b reclaims your nickname.`,
  75. enabled: servCmdRequiresNickRes,
  76. authRequired: true,
  77. minParams: 1,
  78. },
  79. "group": {
  80. handler: nsGroupHandler,
  81. help: `Syntax: $bGROUP$b
  82. GROUP links your current nickname with your logged-in account, so other people
  83. will not be able to use it.`,
  84. helpShort: `$bGROUP$b links your current nickname to your user account.`,
  85. enabled: servCmdRequiresNickRes,
  86. authRequired: true,
  87. },
  88. "identify": {
  89. handler: nsIdentifyHandler,
  90. help: `Syntax: $bIDENTIFY <username> [password]$b
  91. IDENTIFY lets you login to the given username using either password auth, or
  92. certfp (your client certificate) if a password is not given.`,
  93. helpShort: `$bIDENTIFY$b lets you login to your account.`,
  94. enabled: servCmdRequiresAuthEnabled,
  95. minParams: 1,
  96. },
  97. "list": {
  98. handler: nsListHandler,
  99. help: `Syntax: $bLIST [regex]$b
  100. LIST returns the list of registered nicknames, which match the given regex.
  101. If no regex is provided, all registered nicknames are returned.`,
  102. helpShort: `$bLIST$b searches the list of registered nicknames.`,
  103. enabled: servCmdRequiresAuthEnabled,
  104. capabs: []string{"accreg"},
  105. minParams: 0,
  106. },
  107. "info": {
  108. handler: nsInfoHandler,
  109. help: `Syntax: $bINFO [username]$b
  110. INFO gives you information about the given (or your own) user account.`,
  111. helpShort: `$bINFO$b gives you information on a user account.`,
  112. },
  113. "register": {
  114. handler: nsRegisterHandler,
  115. // TODO: "email" is an oversimplification here; it's actually any callback, e.g.,
  116. // person@example.com, mailto:person@example.com, tel:16505551234.
  117. help: `Syntax: $bREGISTER <password> [email]$b
  118. REGISTER lets you register your current nickname as a user account. If the
  119. server allows anonymous registration, you can omit the e-mail address.
  120. If you are currently logged in with a TLS client certificate and wish to use
  121. it instead of a password to log in, send * as the password.`,
  122. helpShort: `$bREGISTER$b lets you register a user account.`,
  123. enabled: servCmdRequiresAccreg,
  124. minParams: 1,
  125. maxParams: 2,
  126. },
  127. "sadrop": {
  128. handler: nsDropHandler,
  129. help: `Syntax: $bSADROP <nickname>$b
  130. SADROP forcibly de-links the given nickname from the attached user account.`,
  131. helpShort: `$bSADROP$b forcibly de-links the given nickname from its user account.`,
  132. capabs: []string{"accreg"},
  133. enabled: servCmdRequiresNickRes,
  134. minParams: 1,
  135. },
  136. "saregister": {
  137. handler: nsSaregisterHandler,
  138. help: `Syntax: $bSAREGISTER <username> [password]$b
  139. SAREGISTER registers an account on someone else's behalf.
  140. This is for use in configurations that require SASL for all connections;
  141. an administrator can set use this command to set up user accounts.`,
  142. helpShort: `$bSAREGISTER$b registers an account on someone else's behalf.`,
  143. enabled: servCmdRequiresAuthEnabled, // deliberate
  144. capabs: []string{"accreg"},
  145. minParams: 1,
  146. },
  147. "saverify": {
  148. handler: nsSaverifyHandler,
  149. help: `Syntax: $bSAVERIFY <username>$b
  150. SAVERIFY manually verifies an account that is pending verification.`,
  151. helpShort: `$bSAVERIFY$b manually verifies an account that is pending verification.`,
  152. enabled: servCmdRequiresAuthEnabled, // deliberate
  153. capabs: []string{"accreg"},
  154. minParams: 1,
  155. },
  156. "sessions": {
  157. hidden: true,
  158. handler: nsClientsHandler,
  159. help: `Syntax: $bSESSIONS [nickname]$b
  160. SESSIONS is an alias for $bCLIENTS LIST$b. See the help entry for $bCLIENTS$b
  161. for more information.`,
  162. enabled: servCmdRequiresBouncerEnabled,
  163. },
  164. "unregister": {
  165. handler: nsUnregisterHandler,
  166. help: `Syntax: $bUNREGISTER <username> [code]$b
  167. UNREGISTER lets you delete your user account (or someone else's, if you're an
  168. IRC operator with the correct permissions). To prevent accidental
  169. unregistrations, a verification code is required; invoking the command without
  170. a code will display the necessary code.`,
  171. helpShort: `$bUNREGISTER$b lets you delete your user account.`,
  172. enabled: servCmdRequiresAuthEnabled,
  173. minParams: 1,
  174. },
  175. "erase": {
  176. handler: nsUnregisterHandler,
  177. help: `Syntax: $bERASE <username> [code]$b
  178. ERASE deletes all records of an account, allowing it to be re-registered.
  179. This should be used with caution, because it violates an expectation that
  180. account names are permanent identifiers. Typically, UNREGISTER should be
  181. used instead. A confirmation code is required; invoking the command
  182. without a code will display the necessary code.`,
  183. helpShort: `$bERASE$b erases all records of an account, allowing reuse.`,
  184. enabled: servCmdRequiresAuthEnabled,
  185. capabs: []string{"accreg"},
  186. minParams: 1,
  187. },
  188. "verify": {
  189. handler: nsVerifyHandler,
  190. help: `Syntax: $bVERIFY <username> <code>$b
  191. VERIFY lets you complete an account registration, if the server requires email
  192. or other verification.`,
  193. helpShort: `$bVERIFY$b lets you complete account registration.`,
  194. enabled: servCmdRequiresAccreg,
  195. minParams: 2,
  196. },
  197. "passwd": {
  198. handler: nsPasswdHandler,
  199. help: `Syntax: $bPASSWD <current> <new> <new_again>$b
  200. Or: $bPASSWD <username> <new>$b
  201. PASSWD lets you change your account password. You must supply your current
  202. password and confirm the new one by typing it twice. If you're an IRC operator
  203. with the correct permissions, you can use PASSWD to reset someone else's
  204. password by supplying their username and then the desired password. To
  205. indicate an empty password, use * instead.`,
  206. helpShort: `$bPASSWD$b lets you change your password.`,
  207. enabled: servCmdRequiresAuthEnabled,
  208. minParams: 2,
  209. },
  210. "password": {
  211. aliasOf: "passwd",
  212. },
  213. "get": {
  214. handler: nsGetHandler,
  215. help: `Syntax: $bGET <setting>$b
  216. GET queries the current values of your account settings. For more information
  217. on the settings and their possible values, see HELP SET.`,
  218. helpShort: `$bGET$b queries the current values of your account settings`,
  219. authRequired: true,
  220. enabled: servCmdRequiresAuthEnabled,
  221. minParams: 1,
  222. },
  223. "saget": {
  224. handler: nsGetHandler,
  225. help: `Syntax: $bSAGET <account> <setting>$b
  226. SAGET queries the values of someone else's account settings. For more
  227. information on the settings and their possible values, see HELP SET.`,
  228. helpShort: `$bSAGET$b queries the current values of another user's account settings`,
  229. enabled: servCmdRequiresAuthEnabled,
  230. minParams: 2,
  231. capabs: []string{"accreg"},
  232. },
  233. "set": {
  234. handler: nsSetHandler,
  235. helpShort: `$bSET$b modifies your account settings`,
  236. // these are broken out as separate strings so they can be translated separately
  237. helpStrings: []string{
  238. `Syntax $bSET <setting> <value>$b
  239. SET modifies your account settings. The following settings are available:`,
  240. `$bENFORCE$b
  241. 'enforce' lets you specify a custom enforcement mechanism for your registered
  242. nicknames. Your options are:
  243. 1. 'none' [no enforcement, overriding the server default]
  244. 2. 'strict' [you must already be authenticated to use the nick]
  245. 3. 'default' [use the server default]`,
  246. `$bMULTICLIENT$b
  247. If 'multiclient' is enabled and you are already logged in and using a nick, a
  248. second client of yours that authenticates with SASL and requests the same nick
  249. is allowed to attach to the nick as well (this is comparable to the behavior
  250. of IRC "bouncers" like ZNC). Your options are 'on' (allow this behavior),
  251. 'off' (disallow it), and 'default' (use the server default value).`,
  252. `$bAUTOREPLAY-LINES$b
  253. 'autoreplay-lines' controls the number of lines of channel history that will
  254. be replayed to you automatically when joining a channel. Your options are any
  255. positive number, 0 to disable the feature, and 'default' to use the server
  256. default.`,
  257. `$bREPLAY-JOINS$b
  258. 'replay-joins' controls whether replayed channel history will include
  259. lines for join and part. This provides more information about the context of
  260. messages, but may be spammy. Your options are 'always' and the default of
  261. 'commands-only' (the messages will be replayed in CHATHISTORY output, but not
  262. during autoreplay).`,
  263. `$bALWAYS-ON$b
  264. 'always-on' controls whether your nickname/identity will remain active
  265. even while you are disconnected from the server. Your options are 'true',
  266. 'false', and 'default' (use the server default value).`,
  267. `$bAUTOREPLAY-MISSED$b
  268. 'autoreplay-missed' is only effective for always-on clients. If enabled,
  269. if you have at most one active session, the server will remember the time
  270. you disconnect and then replay missed messages to you when you reconnect.
  271. Your options are 'on' and 'off'.`,
  272. `$bDM-HISTORY$b
  273. 'dm-history' is only effective for always-on clients. It lets you control
  274. how the history of your direct messages is stored. Your options are:
  275. 1. 'off' [no history]
  276. 2. 'ephemeral' [a limited amount of temporary history, not stored on disk]
  277. 3. 'on' [history stored in a permanent database, if available]
  278. 4. 'default' [use the server default]`,
  279. `$bAUTO-AWAY$b
  280. 'auto-away' is only effective for always-on clients. If enabled, you will
  281. automatically be marked away when all your sessions are disconnected, and
  282. automatically return from away when you connect again.`,
  283. `$bEMAIL$b
  284. 'email' controls the e-mail address associated with your account (if the
  285. server operator allows it, this address can be used for password resets).
  286. As an additional security measure, if you have a password set, you must
  287. provide it as an additional argument to $bSET$b, for example,
  288. SET EMAIL test@example.com hunter2`,
  289. },
  290. authRequired: true,
  291. enabled: servCmdRequiresAuthEnabled,
  292. minParams: 2,
  293. },
  294. "saset": {
  295. handler: nsSetHandler,
  296. help: `Syntax: $bSASET <account> <setting> <value>$b
  297. SASET modifies the values of someone else's account settings. For more
  298. information on the settings and their possible values, see HELP SET.`,
  299. helpShort: `$bSASET$b modifies another user's account settings`,
  300. enabled: servCmdRequiresAuthEnabled,
  301. minParams: 3,
  302. capabs: []string{"accreg"},
  303. },
  304. "sendpass": {
  305. handler: nsSendpassHandler,
  306. help: `Syntax: $bSENDPASS <account>$b
  307. SENDPASS sends a password reset email to the email address associated with
  308. the target account. The reset code in the email can then be used with the
  309. $bRESETPASS$b command.`,
  310. helpShort: `$bSENDPASS$b initiates an email-based password reset`,
  311. enabled: servCmdRequiresEmailReset,
  312. minParams: 1,
  313. },
  314. "resetpass": {
  315. handler: nsResetpassHandler,
  316. help: `Syntax: $bRESETPASS <account> <code> <password>$b
  317. RESETPASS resets an account password, using a reset code that was emailed as
  318. the result of a previous $bSENDPASS$b command.`,
  319. helpShort: `$bRESETPASS$b completes an email-based password reset`,
  320. enabled: servCmdRequiresEmailReset,
  321. minParams: 3,
  322. },
  323. "cert": {
  324. handler: nsCertHandler,
  325. help: `Syntax: $bCERT <LIST | ADD | DEL> [account] [certfp]$b
  326. CERT examines or modifies the SHA-256 TLS certificate fingerprints that can
  327. be used to log into an account. Specifically, $bCERT LIST$b lists the
  328. authorized fingerprints, $bCERT ADD <fingerprint>$b adds a new fingerprint, and
  329. $bCERT DEL <fingerprint>$b removes a fingerprint. If you're an IRC operator
  330. with the correct permissions, you can act on another user's account, for
  331. example with $bCERT ADD <account> <fingerprint>$b. See the operator manual
  332. for instructions on how to compute the fingerprint.`,
  333. helpShort: `$bCERT$b controls a user account's certificate fingerprints`,
  334. enabled: servCmdRequiresAuthEnabled,
  335. minParams: 1,
  336. },
  337. "suspend": {
  338. handler: nsSuspendHandler,
  339. help: `Syntax: $bSUSPEND ADD <nickname> [DURATION duration] [reason]$b
  340. $bSUSPEND DEL <nickname>$b
  341. $bSUSPEND LIST$b
  342. Suspending an account disables it (preventing new logins) and disconnects
  343. all associated clients. You can specify a time limit or a reason for
  344. the suspension. The $bDEL$b subcommand reverses a suspension, and the $bLIST$b
  345. command lists all current suspensions.`,
  346. helpShort: `$bSUSPEND$b manages account suspensions`,
  347. minParams: 1,
  348. capabs: []string{"ban"},
  349. },
  350. "rename": {
  351. handler: nsRenameHandler,
  352. help: `Syntax: $bRENAME <account> <newname>$b
  353. RENAME allows a server administrator to change the name of an account.
  354. Currently, you can only change the canonical casefolding of an account
  355. (e.g., you can change "Alice" to "alice", but not "Alice" to "Amanda").`,
  356. helpShort: `$bRENAME$b renames an account`,
  357. minParams: 2,
  358. capabs: []string{"accreg"},
  359. },
  360. "verifyemail": {
  361. handler: nsVerifyEmailHandler,
  362. authRequired: true,
  363. minParams: 1,
  364. hidden: true,
  365. },
  366. }
  367. )
  368. func nsGetHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  369. var account string
  370. if command == "saget" {
  371. account = params[0]
  372. params = params[1:]
  373. } else {
  374. account = client.Account()
  375. }
  376. accountData, err := server.accounts.LoadAccount(account)
  377. if err == errAccountDoesNotExist {
  378. service.Notice(rb, client.t("No such account"))
  379. return
  380. } else if err != nil {
  381. service.Notice(rb, client.t("Error loading account data"))
  382. return
  383. }
  384. displaySetting(service, params[0], accountData.Settings, client, rb)
  385. }
  386. func displaySetting(service *ircService, settingName string, settings AccountSettings, client *Client, rb *ResponseBuffer) {
  387. config := client.server.Config()
  388. switch strings.ToLower(settingName) {
  389. case "enforce":
  390. storedValue := settings.NickEnforcement
  391. serializedStoredValue := nickReservationToString(storedValue)
  392. service.Notice(rb, fmt.Sprintf(client.t("Your stored nickname enforcement setting is: %s"), serializedStoredValue))
  393. serializedActualValue := nickReservationToString(configuredEnforcementMethod(config, storedValue))
  394. service.Notice(rb, fmt.Sprintf(client.t("Given current server settings, your nickname is enforced with: %s"), serializedActualValue))
  395. case "autoreplay-lines":
  396. if settings.AutoreplayLines == nil {
  397. service.Notice(rb, fmt.Sprintf(client.t("You will receive the server default of %d lines of autoreplayed history"), config.History.AutoreplayOnJoin))
  398. } else {
  399. service.Notice(rb, fmt.Sprintf(client.t("You will receive %d lines of autoreplayed history"), *settings.AutoreplayLines))
  400. }
  401. case "replay-joins":
  402. switch settings.ReplayJoins {
  403. case ReplayJoinsCommandsOnly:
  404. service.Notice(rb, client.t("You will see JOINs and PARTs in /HISTORY output, but not in autoreplay"))
  405. case ReplayJoinsAlways:
  406. service.Notice(rb, client.t("You will see JOINs and PARTs in /HISTORY output and in autoreplay"))
  407. }
  408. case "multiclient":
  409. if !config.Accounts.Multiclient.Enabled {
  410. service.Notice(rb, client.t("This feature has been disabled by the server administrators"))
  411. } else {
  412. switch settings.AllowBouncer {
  413. case MulticlientAllowedServerDefault:
  414. if config.Accounts.Multiclient.AllowedByDefault {
  415. service.Notice(rb, client.t("Multiclient functionality is currently enabled for your account, but you can opt out"))
  416. } else {
  417. service.Notice(rb, client.t("Multiclient functionality is currently disabled for your account, but you can opt in"))
  418. }
  419. case MulticlientDisallowedByUser:
  420. service.Notice(rb, client.t("Multiclient functionality is currently disabled for your account"))
  421. case MulticlientAllowedByUser:
  422. service.Notice(rb, client.t("Multiclient functionality is currently enabled for your account"))
  423. }
  424. }
  425. case "always-on":
  426. stored := settings.AlwaysOn
  427. actual := persistenceEnabled(config.Accounts.Multiclient.AlwaysOn, stored)
  428. service.Notice(rb, fmt.Sprintf(client.t("Your stored always-on setting is: %s"), userPersistentStatusToString(stored)))
  429. if actual {
  430. service.Notice(rb, client.t("Given current server settings, your client is always-on"))
  431. } else {
  432. service.Notice(rb, client.t("Given current server settings, your client is not always-on"))
  433. }
  434. case "autoreplay-missed":
  435. stored := settings.AutoreplayMissed
  436. if stored {
  437. alwaysOn := persistenceEnabled(config.Accounts.Multiclient.AlwaysOn, settings.AlwaysOn)
  438. if alwaysOn {
  439. service.Notice(rb, client.t("Autoreplay of missed messages is enabled"))
  440. } else {
  441. service.Notice(rb, client.t("You have enabled autoreplay of missed messages, but you can't receive them because your client isn't set to always-on"))
  442. }
  443. } else {
  444. service.Notice(rb, client.t("Your account is not configured to receive autoreplayed missed messages"))
  445. }
  446. case "auto-away":
  447. stored := settings.AutoAway
  448. alwaysOn := persistenceEnabled(config.Accounts.Multiclient.AlwaysOn, settings.AlwaysOn)
  449. actual := persistenceEnabled(config.Accounts.Multiclient.AutoAway, settings.AutoAway)
  450. service.Notice(rb, fmt.Sprintf(client.t("Your stored auto-away setting is: %s"), userPersistentStatusToString(stored)))
  451. if actual && alwaysOn {
  452. service.Notice(rb, client.t("Given current server settings, auto-away is enabled for your client"))
  453. } else if actual && !alwaysOn {
  454. service.Notice(rb, client.t("Because your client is not always-on, auto-away is disabled"))
  455. } else if !actual {
  456. service.Notice(rb, client.t("Given current server settings, auto-away is disabled for your client"))
  457. }
  458. case "dm-history":
  459. effectiveValue := historyEnabled(config.History.Persistent.DirectMessages, settings.DMHistory)
  460. service.Notice(rb, fmt.Sprintf(client.t("Your stored direct message history setting is: %s"), historyStatusToString(settings.DMHistory)))
  461. service.Notice(rb, fmt.Sprintf(client.t("Given current server settings, your direct message history setting is: %s"), historyStatusToString(effectiveValue)))
  462. case "email":
  463. if settings.Email != "" {
  464. service.Notice(rb, fmt.Sprintf(client.t("Your stored e-mail address is: %s"), settings.Email))
  465. } else {
  466. service.Notice(rb, client.t("You have no stored e-mail address"))
  467. }
  468. default:
  469. service.Notice(rb, client.t("No such setting"))
  470. }
  471. }
  472. func userPersistentStatusToString(status PersistentStatus) string {
  473. // #1544: "mandatory" as a user setting should display as "enabled"
  474. result := persistentStatusToString(status)
  475. if result == "mandatory" {
  476. result = "enabled"
  477. }
  478. return result
  479. }
  480. func nsSetHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  481. var privileged bool
  482. var account string
  483. if command == "saset" {
  484. privileged = true
  485. account = params[0]
  486. params = params[1:]
  487. } else {
  488. account = client.Account()
  489. }
  490. key := strings.ToLower(params[0])
  491. // unprivileged NS SET EMAIL is different because it requires a confirmation
  492. if !privileged && key == "email" {
  493. nsSetEmailHandler(service, client, params, rb)
  494. return
  495. }
  496. var munger settingsMunger
  497. var finalSettings AccountSettings
  498. var err error
  499. switch key {
  500. case "pass", "password":
  501. service.Notice(rb, client.t("To change a password, use the PASSWD command. For details, /msg NickServ HELP PASSWD"))
  502. return
  503. case "enforce":
  504. var method NickEnforcementMethod
  505. method, err = nickReservationFromString(params[1])
  506. if err != nil {
  507. err = errInvalidParams
  508. break
  509. }
  510. // updating enforcement settings is special-cased, because it requires
  511. // an update to server.accounts.accountToMethod
  512. finalSettings, err = server.accounts.SetEnforcementStatus(account, method)
  513. if err == nil {
  514. finalSettings.NickEnforcement = method // success
  515. }
  516. case "autoreplay-lines":
  517. var newValue *int
  518. if strings.ToLower(params[1]) != "default" {
  519. val, err_ := strconv.Atoi(params[1])
  520. if err_ != nil || val < 0 {
  521. err = errInvalidParams
  522. break
  523. }
  524. newValue = new(int)
  525. *newValue = val
  526. }
  527. munger = func(in AccountSettings) (out AccountSettings, err error) {
  528. out = in
  529. out.AutoreplayLines = newValue
  530. return
  531. }
  532. case "multiclient":
  533. var newValue MulticlientAllowedSetting
  534. if strings.ToLower(params[1]) == "default" {
  535. newValue = MulticlientAllowedServerDefault
  536. } else {
  537. var enabled bool
  538. enabled, err = utils.StringToBool(params[1])
  539. if enabled {
  540. newValue = MulticlientAllowedByUser
  541. } else {
  542. newValue = MulticlientDisallowedByUser
  543. }
  544. }
  545. if err == nil {
  546. munger = func(in AccountSettings) (out AccountSettings, err error) {
  547. out = in
  548. out.AllowBouncer = newValue
  549. return
  550. }
  551. }
  552. case "replay-joins":
  553. var newValue ReplayJoinsSetting
  554. newValue, err = replayJoinsSettingFromString(params[1])
  555. if err == nil {
  556. munger = func(in AccountSettings) (out AccountSettings, err error) {
  557. out = in
  558. out.ReplayJoins = newValue
  559. return
  560. }
  561. }
  562. case "always-on":
  563. // #821: it's problematic to alter the value of always-on if you're not
  564. // the (actual or potential) always-on client yourself. make an exception
  565. // for `saset` to give operators an escape hatch (any consistency problems
  566. // can probably be fixed by restarting the server):
  567. if command != "saset" {
  568. details := client.Details()
  569. if details.nick != details.accountName {
  570. err = errNickAccountMismatch
  571. }
  572. }
  573. if err == nil {
  574. var newValue PersistentStatus
  575. newValue, err = persistentStatusFromString(params[1])
  576. // "opt-in" and "opt-out" don't make sense as user preferences
  577. if err == nil && newValue != PersistentOptIn && newValue != PersistentOptOut {
  578. munger = func(in AccountSettings) (out AccountSettings, err error) {
  579. out = in
  580. out.AlwaysOn = newValue
  581. return
  582. }
  583. }
  584. }
  585. case "autoreplay-missed":
  586. var newValue bool
  587. newValue, err = utils.StringToBool(params[1])
  588. if err == nil {
  589. munger = func(in AccountSettings) (out AccountSettings, err error) {
  590. out = in
  591. out.AutoreplayMissed = newValue
  592. return
  593. }
  594. }
  595. case "auto-away":
  596. var newValue PersistentStatus
  597. newValue, err = persistentStatusFromString(params[1])
  598. // "opt-in" and "opt-out" don't make sense as user preferences
  599. if err == nil && newValue != PersistentOptIn && newValue != PersistentOptOut {
  600. munger = func(in AccountSettings) (out AccountSettings, err error) {
  601. out = in
  602. out.AutoAway = newValue
  603. return
  604. }
  605. }
  606. case "dm-history":
  607. var newValue HistoryStatus
  608. newValue, err = historyStatusFromString(params[1])
  609. if err == nil {
  610. munger = func(in AccountSettings) (out AccountSettings, err error) {
  611. out = in
  612. out.DMHistory = newValue
  613. return
  614. }
  615. }
  616. case "email":
  617. newValue := params[1]
  618. munger = func(in AccountSettings) (out AccountSettings, err error) {
  619. out = in
  620. out.Email = newValue
  621. return
  622. }
  623. default:
  624. err = errInvalidParams
  625. }
  626. if munger != nil {
  627. finalSettings, err = server.accounts.ModifyAccountSettings(account, munger)
  628. }
  629. switch err {
  630. case nil:
  631. service.Notice(rb, client.t("Successfully changed your account settings"))
  632. displaySetting(service, key, finalSettings, client, rb)
  633. case errInvalidParams, errAccountDoesNotExist, errFeatureDisabled, errAccountUnverified, errAccountUpdateFailed:
  634. service.Notice(rb, client.t(err.Error()))
  635. case errNickAccountMismatch:
  636. service.Notice(rb, fmt.Sprintf(client.t("Your nickname must match your account name %s exactly to modify this setting. Try changing it with /NICK, or logging out and back in with the correct nickname."), client.AccountName()))
  637. default:
  638. // unknown error
  639. service.Notice(rb, client.t("An error occurred"))
  640. }
  641. }
  642. // handle unprivileged NS SET EMAIL, which sends a confirmation code
  643. func nsSetEmailHandler(service *ircService, client *Client, params []string, rb *ResponseBuffer) {
  644. config := client.server.Config()
  645. if !config.Accounts.Registration.EmailVerification.Enabled {
  646. rb.Notice(client.t("E-mail verification is disabled"))
  647. return
  648. }
  649. if !nsLoginThrottleCheck(service, client, rb) {
  650. return
  651. }
  652. var password string
  653. if len(params) > 2 {
  654. password = params[2]
  655. }
  656. account := client.Account()
  657. errorMessage := nsConfirmPassword(client.server, account, password)
  658. if errorMessage != "" {
  659. service.Notice(rb, client.t(errorMessage))
  660. return
  661. }
  662. err := client.server.accounts.NsSetEmail(client, params[1])
  663. switch err {
  664. case nil:
  665. service.Notice(rb, client.t("Check your e-mail for instructions on how to confirm your change of address"))
  666. case errLimitExceeded:
  667. service.Notice(rb, client.t("Try again later"))
  668. default:
  669. // if appropriate, show the client the error from the attempted email sending
  670. if rErr := registrationCallbackErrorText(config, client, err); rErr != "" {
  671. service.Notice(rb, rErr)
  672. } else {
  673. service.Notice(rb, client.t("An error occurred"))
  674. }
  675. }
  676. }
  677. func nsVerifyEmailHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  678. err := server.accounts.NsVerifyEmail(client, params[0])
  679. switch err {
  680. case nil:
  681. service.Notice(rb, client.t("Successfully changed your account settings"))
  682. displaySetting(service, "email", client.AccountSettings(), client, rb)
  683. case errAccountVerificationInvalidCode:
  684. service.Notice(rb, client.t(err.Error()))
  685. default:
  686. service.Notice(rb, client.t("An error occurred"))
  687. }
  688. }
  689. func nsDropHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  690. sadrop := command == "sadrop"
  691. var nick string
  692. if len(params) > 0 {
  693. nick = params[0]
  694. } else {
  695. nick = client.NickCasefolded()
  696. }
  697. err := server.accounts.SetNickReserved(client, nick, sadrop, false)
  698. if err == nil {
  699. service.Notice(rb, fmt.Sprintf(client.t("Successfully ungrouped nick %s with your account"), nick))
  700. } else if err == errAccountNotLoggedIn {
  701. service.Notice(rb, client.t("You're not logged into an account"))
  702. } else if err == errAccountCantDropPrimaryNick {
  703. service.Notice(rb, client.t("You can't ungroup your primary nickname (try unregistering your account instead)"))
  704. } else {
  705. service.Notice(rb, client.t("Could not ungroup nick"))
  706. }
  707. }
  708. func nsGhostHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  709. nick := params[0]
  710. ghost := server.clients.Get(nick)
  711. if ghost == nil {
  712. service.Notice(rb, client.t("No such nick"))
  713. return
  714. } else if ghost == client {
  715. service.Notice(rb, client.t("You can't GHOST yourself (try /QUIT instead)"))
  716. return
  717. } else if ghost.AlwaysOn() {
  718. service.Notice(rb, client.t("You can't GHOST an always-on client"))
  719. return
  720. }
  721. authorized := false
  722. account := client.Account()
  723. if account != "" {
  724. // the user must either own the nick, or the target client
  725. authorized = (server.accounts.NickToAccount(nick) == account) || (ghost.Account() == account)
  726. }
  727. if !authorized {
  728. service.Notice(rb, client.t("You don't own that nick"))
  729. return
  730. }
  731. ghost.Quit(fmt.Sprintf(ghost.t("GHOSTed by %s"), client.Nick()), nil)
  732. ghost.destroy(nil)
  733. }
  734. func nsGroupHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  735. nick := client.Nick()
  736. err := server.accounts.SetNickReserved(client, nick, false, true)
  737. if err == nil {
  738. service.Notice(rb, fmt.Sprintf(client.t("Successfully grouped nick %s with your account"), nick))
  739. } else if err == errAccountTooManyNicks {
  740. service.Notice(rb, client.t("You have too many nicks reserved already (you can remove some with /NS DROP)"))
  741. } else if err == errNicknameReserved {
  742. service.Notice(rb, client.t("That nickname is already reserved by someone else"))
  743. } else {
  744. service.Notice(rb, client.t("Error reserving nickname"))
  745. }
  746. }
  747. func nsLoginThrottleCheck(service *ircService, client *Client, rb *ResponseBuffer) (success bool) {
  748. throttled, remainingTime := client.checkLoginThrottle()
  749. if throttled {
  750. service.Notice(rb, fmt.Sprintf(client.t("Please wait at least %v and try again"), remainingTime.Round(time.Millisecond)))
  751. }
  752. return !throttled
  753. }
  754. func nsIdentifyHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  755. if client.LoggedIntoAccount() {
  756. service.Notice(rb, client.t("You're already logged into an account"))
  757. return
  758. }
  759. var err error
  760. loginSuccessful := false
  761. var username, passphrase string
  762. if len(params) == 1 {
  763. if rb.session.certfp != "" {
  764. username = params[0]
  765. } else {
  766. // XXX undocumented compatibility mode with other nickservs, allowing
  767. // /msg NickServ identify passphrase
  768. username = client.NickCasefolded()
  769. passphrase = params[0]
  770. }
  771. } else {
  772. username = params[0]
  773. passphrase = params[1]
  774. }
  775. // try passphrase
  776. if passphrase != "" {
  777. err = server.accounts.AuthenticateByPassphrase(client, username, passphrase)
  778. loginSuccessful = (err == nil)
  779. }
  780. // try certfp
  781. if !loginSuccessful && rb.session.certfp != "" {
  782. err = server.accounts.AuthenticateByCertificate(client, rb.session.certfp, rb.session.peerCerts, "")
  783. loginSuccessful = (err == nil)
  784. }
  785. nickFixupFailed := false
  786. if loginSuccessful {
  787. if !fixupNickEqualsAccount(client, rb, server.Config(), service.prefix) {
  788. loginSuccessful = false
  789. // fixupNickEqualsAccount sends its own error message, don't send another
  790. nickFixupFailed = true
  791. }
  792. }
  793. if loginSuccessful {
  794. sendSuccessfulAccountAuth(service, client, rb, true)
  795. } else if !nickFixupFailed {
  796. service.Notice(rb, fmt.Sprintf(client.t("Authentication failed: %s"), authErrorToMessage(server, err)))
  797. }
  798. }
  799. func nsListHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  800. if !client.HasRoleCapabs("accreg") {
  801. service.Notice(rb, client.t("Insufficient privileges"))
  802. return
  803. }
  804. var searchRegex *regexp.Regexp
  805. if len(params) > 0 {
  806. var err error
  807. searchRegex, err = regexp.Compile(params[0])
  808. if err != nil {
  809. service.Notice(rb, client.t("Invalid regex"))
  810. return
  811. }
  812. }
  813. service.Notice(rb, ircfmt.Unescape(client.t("*** $bNickServ LIST$b ***")))
  814. nicks := server.accounts.AllNicks()
  815. for _, nick := range nicks {
  816. if searchRegex == nil || searchRegex.MatchString(nick) {
  817. service.Notice(rb, fmt.Sprintf(" %s", nick))
  818. }
  819. }
  820. service.Notice(rb, ircfmt.Unescape(client.t("*** $bEnd of NickServ LIST$b ***")))
  821. }
  822. func nsInfoHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  823. if !server.Config().Accounts.AuthenticationEnabled && !client.HasRoleCapabs("accreg") {
  824. service.Notice(rb, client.t("This command has been disabled by the server administrators"))
  825. return
  826. }
  827. var accountName string
  828. if len(params) > 0 {
  829. nick := params[0]
  830. if server.Config().Accounts.NickReservation.Enabled {
  831. accountName = server.accounts.NickToAccount(nick)
  832. if accountName == "" {
  833. service.Notice(rb, client.t("That nickname is not registered"))
  834. return
  835. }
  836. } else {
  837. accountName = nick
  838. }
  839. } else {
  840. accountName = client.Account()
  841. if accountName == "" {
  842. service.Notice(rb, client.t("You're not logged into an account"))
  843. return
  844. }
  845. }
  846. account, err := server.accounts.LoadAccount(accountName)
  847. if err != nil || !account.Verified {
  848. if server.accounts.accountWasUnregistered(accountName) {
  849. service.Notice(rb, client.t("Name reserved due to a prior registration"))
  850. } else {
  851. service.Notice(rb, client.t("Account does not exist"))
  852. }
  853. return
  854. }
  855. service.Notice(rb, fmt.Sprintf(client.t("Account: %s"), account.Name))
  856. registeredAt := account.RegisteredAt.Format(time.RFC1123)
  857. service.Notice(rb, fmt.Sprintf(client.t("Registered at: %s"), registeredAt))
  858. if account.Name == client.AccountName() || client.HasRoleCapabs("accreg") {
  859. if account.Settings.Email != "" {
  860. service.Notice(rb, fmt.Sprintf(client.t("Email address: %s"), account.Settings.Email))
  861. }
  862. }
  863. // TODO nicer formatting for this
  864. for _, nick := range account.AdditionalNicks {
  865. service.Notice(rb, fmt.Sprintf(client.t("Additional grouped nick: %s"), nick))
  866. }
  867. listRegisteredChannels(service, accountName, rb)
  868. if account.Suspended != nil {
  869. service.Notice(rb, suspensionToString(client, *account.Suspended))
  870. }
  871. }
  872. func listRegisteredChannels(service *ircService, accountName string, rb *ResponseBuffer) {
  873. client := rb.session.client
  874. channels := client.server.channels.ChannelsForAccount(accountName)
  875. service.Notice(rb, fmt.Sprintf(client.t("Account %s has %d registered channel(s)."), accountName, len(channels)))
  876. for _, channel := range channels {
  877. service.Notice(rb, fmt.Sprintf(client.t("Registered channel: %s"), channel))
  878. }
  879. }
  880. func nsRegisterHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  881. details := client.Details()
  882. passphrase := params[0]
  883. var email string
  884. if 1 < len(params) {
  885. email = params[1]
  886. }
  887. certfp := rb.session.certfp
  888. if passphrase == "*" {
  889. if certfp == "" {
  890. service.Notice(rb, client.t("You must be connected with TLS and a client certificate to do this"))
  891. return
  892. } else {
  893. passphrase = ""
  894. }
  895. }
  896. if passphrase != "" {
  897. cfPassphrase, err := Casefold(passphrase)
  898. if err == nil && cfPassphrase == details.nickCasefolded {
  899. service.Notice(rb, client.t("Usage: REGISTER <passphrase> [email]")) // #1179
  900. return
  901. }
  902. }
  903. if !nsLoginThrottleCheck(service, client, rb) {
  904. return
  905. }
  906. config := server.Config()
  907. account := details.nick
  908. if config.Accounts.NickReservation.ForceGuestFormat {
  909. matches := config.Accounts.NickReservation.guestRegexp.FindStringSubmatch(account)
  910. if matches == nil || len(matches) < 2 {
  911. service.Notice(rb, client.t("Erroneous nickname"))
  912. return
  913. }
  914. account = matches[1]
  915. }
  916. callbackNamespace, callbackValue, validationErr := parseCallback(email, config)
  917. if validationErr != nil {
  918. service.Notice(rb, client.t("Registration requires a valid e-mail address"))
  919. return
  920. }
  921. err := server.accounts.Register(client, account, callbackNamespace, callbackValue, passphrase, rb.session.certfp)
  922. if err == nil {
  923. if callbackNamespace == "*" {
  924. err = server.accounts.Verify(client, account, "", true)
  925. if err == nil && fixupNickEqualsAccount(client, rb, config, service.prefix) {
  926. sendSuccessfulRegResponse(service, client, rb)
  927. }
  928. } else {
  929. messageTemplate := client.t("Account created, pending verification; verification code has been sent to %s")
  930. message := fmt.Sprintf(messageTemplate, callbackValue)
  931. service.Notice(rb, message)
  932. announcePendingReg(client, rb, account)
  933. }
  934. } else {
  935. // details could not be stored and relevant numerics have been dispatched, abort
  936. message := registrationErrorToMessage(config, client, err)
  937. service.Notice(rb, client.t(message))
  938. }
  939. }
  940. func nsSaregisterHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  941. registerCap := rb.session.capabilities.Has(caps.AccountRegistration)
  942. var account, passphrase string
  943. account = params[0]
  944. if 1 < len(params) && params[1] != "*" {
  945. passphrase = params[1]
  946. }
  947. err := server.accounts.SARegister(account, passphrase)
  948. if err != nil {
  949. var errMsg string
  950. var failCode string
  951. if err == errAccountAlreadyRegistered || err == errAccountAlreadyVerified {
  952. errMsg = client.t("Account already exists")
  953. failCode = "USERNAME_EXISTS"
  954. } else if err == errNameReserved {
  955. errMsg = client.t(err.Error())
  956. failCode = "USERNAME_EXISTS"
  957. } else if err == errAccountBadPassphrase {
  958. errMsg = client.t("Passphrase contains forbidden characters or is otherwise invalid")
  959. failCode = "INVALID_PASSWORD"
  960. } else {
  961. server.logger.Error("services", "unknown error from saregister", err.Error())
  962. errMsg = fmt.Sprintf(client.t("Could not register: %v"), err)
  963. failCode = "UNKNOWN_ERROR"
  964. }
  965. service.Notice(rb, errMsg)
  966. if registerCap {
  967. rb.Add(nil, server.name, "FAIL", "REGISTER", failCode, utils.SafeErrorParam(account), err.Error())
  968. }
  969. } else {
  970. service.Notice(rb, fmt.Sprintf(client.t("Successfully registered account %s"), account))
  971. if registerCap {
  972. rb.Add(nil, server.name, "REGISTER", "SUCCESS", account, client.t("Account successfully registered"))
  973. }
  974. server.snomasks.Send(sno.LocalAccounts, fmt.Sprintf(ircfmt.Unescape("Operator $c[grey][$r%s$c[grey]] registered account $c[grey][$r%s$c[grey]] with SAREGISTER"), client.Oper().Name, account))
  975. }
  976. }
  977. func nsSaverifyHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  978. account := params[0]
  979. err := server.accounts.Verify(nil, account, "", true)
  980. if err == nil {
  981. service.Notice(rb, fmt.Sprintf(client.t("Successfully verified account %s"), account))
  982. } else {
  983. service.Notice(rb, fmt.Sprintf(client.t("Failed to verify account %s: %v"), account, err.Error()))
  984. }
  985. }
  986. func nsUnregisterHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  987. erase := command == "erase"
  988. username := params[0]
  989. var verificationCode string
  990. if len(params) > 1 {
  991. verificationCode = params[1]
  992. }
  993. if username == "" {
  994. service.Notice(rb, client.t("You must specify an account"))
  995. return
  996. }
  997. var accountName string
  998. var registeredAt time.Time
  999. if erase {
  1000. // account may not be in a loadable state, e.g., if it was unregistered
  1001. accountName = username
  1002. // make the confirmation code nondeterministic for ERASE
  1003. registeredAt = server.ctime
  1004. } else {
  1005. account, err := server.accounts.LoadAccount(username)
  1006. if err == errAccountDoesNotExist {
  1007. service.Notice(rb, client.t("Invalid account name"))
  1008. return
  1009. } else if err != nil {
  1010. service.Notice(rb, client.t("Internal error"))
  1011. return
  1012. }
  1013. accountName = account.Name
  1014. registeredAt = account.RegisteredAt
  1015. }
  1016. if !(accountName == client.AccountName() || client.HasRoleCapabs("accreg")) {
  1017. service.Notice(rb, client.t("Insufficient oper privs"))
  1018. return
  1019. }
  1020. expectedCode := utils.ConfirmationCode(accountName, registeredAt)
  1021. if expectedCode != verificationCode {
  1022. if erase {
  1023. service.Notice(rb, ircfmt.Unescape(client.t("$bWarning: erasing this account will allow it to be re-registered; consider UNREGISTER instead.$b")))
  1024. } else {
  1025. service.Notice(rb, ircfmt.Unescape(client.t("$bWarning: unregistering this account will remove its stored privileges.$b")))
  1026. service.Notice(rb, ircfmt.Unescape(client.t("$bNote that an unregistered account name remains reserved and cannot be re-registered.$b")))
  1027. service.Notice(rb, ircfmt.Unescape(client.t("$bIf you are having problems with your account, contact an administrator.$b")))
  1028. service.Notice(rb, ircfmt.Unescape(client.t("$bUnregistering your account will unregister all channels you founded.$b")))
  1029. service.Notice(rb, ircfmt.Unescape(client.t("$bTo prevent this, transfer your channels first with CS TRANSFER.$b")))
  1030. }
  1031. service.Notice(rb, fmt.Sprintf(client.t("To confirm, run this command: %s"), fmt.Sprintf("/NS %s %s %s", strings.ToUpper(command), accountName, expectedCode)))
  1032. return
  1033. }
  1034. err := server.accounts.Unregister(accountName, erase)
  1035. if err == errAccountDoesNotExist {
  1036. service.Notice(rb, client.t(err.Error()))
  1037. } else if err != nil {
  1038. service.Notice(rb, client.t("Error while unregistering account"))
  1039. } else {
  1040. service.Notice(rb, fmt.Sprintf(client.t("Successfully unregistered account %s"), accountName))
  1041. server.logger.Info("accounts", "client", client.Nick(), "unregistered account", accountName)
  1042. client.server.snomasks.Send(sno.LocalAccounts, fmt.Sprintf(ircfmt.Unescape("Client $c[grey][$r%s$c[grey]] unregistered account $c[grey][$r%s$c[grey]]"), client.NickMaskString(), accountName))
  1043. }
  1044. }
  1045. func nsVerifyHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  1046. username, code := params[0], params[1]
  1047. err := server.accounts.Verify(client, username, code, false)
  1048. var errorMessage string
  1049. if err != nil {
  1050. switch err {
  1051. case errAccountAlreadyLoggedIn, errAccountVerificationInvalidCode, errAccountAlreadyVerified:
  1052. errorMessage = err.Error()
  1053. default:
  1054. errorMessage = errAccountVerificationFailed.Error()
  1055. }
  1056. }
  1057. if errorMessage != "" {
  1058. service.Notice(rb, client.t(errorMessage))
  1059. return
  1060. }
  1061. if fixupNickEqualsAccount(client, rb, server.Config(), service.prefix) {
  1062. sendSuccessfulRegResponse(service, client, rb)
  1063. }
  1064. }
  1065. func nsConfirmPassword(server *Server, account, passphrase string) (errorMessage string) {
  1066. accountData, err := server.accounts.LoadAccount(account)
  1067. if err != nil {
  1068. errorMessage = `You're not logged into an account`
  1069. } else {
  1070. hash := accountData.Credentials.PassphraseHash
  1071. if hash != nil {
  1072. if passphrase == "" {
  1073. errorMessage = `You must supply a password`
  1074. } else if passwd.CompareHashAndPassword(hash, []byte(passphrase)) != nil {
  1075. errorMessage = `Password incorrect`
  1076. }
  1077. }
  1078. }
  1079. return
  1080. }
  1081. func nsPasswdHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  1082. var target string
  1083. var newPassword string
  1084. var errorMessage string
  1085. var oper *Oper
  1086. switch len(params) {
  1087. case 2:
  1088. oper = client.Oper()
  1089. if !oper.HasRoleCapab("accreg") {
  1090. errorMessage = `Insufficient privileges`
  1091. } else {
  1092. target, newPassword = params[0], params[1]
  1093. if newPassword == "*" {
  1094. newPassword = ""
  1095. }
  1096. message := fmt.Sprintf("Operator %s ran NS PASSWD for account %s", oper.Name, target)
  1097. server.snomasks.Send(sno.LocalOpers, message)
  1098. server.logger.Info("opers", message)
  1099. }
  1100. case 3:
  1101. target = client.Account()
  1102. newPassword = params[1]
  1103. if newPassword == "*" {
  1104. newPassword = ""
  1105. }
  1106. checkPassword := params[2]
  1107. if checkPassword == "*" {
  1108. checkPassword = "" // #1883
  1109. }
  1110. if target == "" {
  1111. errorMessage = `You're not logged into an account`
  1112. } else if newPassword != checkPassword {
  1113. errorMessage = `Passwords do not match`
  1114. } else {
  1115. if !nsLoginThrottleCheck(service, client, rb) {
  1116. return
  1117. }
  1118. errorMessage = nsConfirmPassword(server, target, params[0])
  1119. }
  1120. default:
  1121. errorMessage = `Invalid parameters`
  1122. }
  1123. if errorMessage != "" {
  1124. service.Notice(rb, client.t(errorMessage))
  1125. return
  1126. }
  1127. err := server.accounts.setPassword(target, newPassword, oper != nil)
  1128. switch err {
  1129. case nil:
  1130. service.Notice(rb, client.t("Password changed"))
  1131. case errEmptyCredentials:
  1132. service.Notice(rb, client.t("You can't delete your password unless you add a certificate fingerprint"))
  1133. case errCredsExternallyManaged:
  1134. service.Notice(rb, client.t("Your account credentials are managed externally and cannot be changed here"))
  1135. case errCASFailed:
  1136. service.Notice(rb, client.t("Try again later"))
  1137. case errAccountDoesNotExist:
  1138. service.Notice(rb, client.t("Account does not exist"))
  1139. default:
  1140. server.logger.Error("internal", "could not upgrade user password:", err.Error())
  1141. service.Notice(rb, client.t("Password could not be changed due to server error"))
  1142. }
  1143. }
  1144. func nsEnforceHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  1145. newParams := []string{"enforce"}
  1146. if len(params) == 0 {
  1147. nsGetHandler(service, server, client, "get", newParams, rb)
  1148. } else {
  1149. newParams = append(newParams, params[0])
  1150. nsSetHandler(service, server, client, "set", newParams, rb)
  1151. }
  1152. }
  1153. func nsClientsHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  1154. var verb string
  1155. if command == "sessions" {
  1156. // Legacy "SESSIONS" command is an alias for CLIENTS LIST.
  1157. verb = "list"
  1158. } else if len(params) > 0 {
  1159. verb = strings.ToLower(params[0])
  1160. params = params[1:]
  1161. }
  1162. switch verb {
  1163. case "list":
  1164. nsClientsListHandler(service, server, client, params, rb)
  1165. case "logout":
  1166. nsClientsLogoutHandler(service, server, client, params, rb)
  1167. default:
  1168. service.Notice(rb, client.t("Invalid parameters"))
  1169. }
  1170. }
  1171. func nsClientsListHandler(service *ircService, server *Server, client *Client, params []string, rb *ResponseBuffer) {
  1172. target := client
  1173. hasPrivs := client.HasRoleCapabs("ban")
  1174. if 0 < len(params) {
  1175. target = server.clients.Get(params[0])
  1176. if target == nil {
  1177. service.Notice(rb, client.t("No such nick"))
  1178. return
  1179. }
  1180. if target != client && !hasPrivs {
  1181. service.Notice(rb, client.t("Command restricted"))
  1182. return
  1183. }
  1184. }
  1185. sessionData, currentIndex := target.AllSessionData(rb.session, hasPrivs)
  1186. service.Notice(rb, fmt.Sprintf(client.t("Nickname %[1]s has %[2]d attached clients(s)"), target.Nick(), len(sessionData)))
  1187. for i, session := range sessionData {
  1188. if currentIndex == i {
  1189. service.Notice(rb, fmt.Sprintf(client.t("Client %d (currently attached client):"), session.sessionID))
  1190. } else {
  1191. service.Notice(rb, fmt.Sprintf(client.t("Client %d:"), session.sessionID))
  1192. }
  1193. if session.deviceID != "" {
  1194. service.Notice(rb, fmt.Sprintf(client.t("Device ID: %s"), session.deviceID))
  1195. }
  1196. service.Notice(rb, fmt.Sprintf(client.t("IP address: %s"), session.ip.String()))
  1197. service.Notice(rb, fmt.Sprintf(client.t("Hostname: %s"), session.hostname))
  1198. if hasPrivs {
  1199. service.Notice(rb, fmt.Sprintf(client.t("Connection: %s"), session.connInfo))
  1200. }
  1201. service.Notice(rb, fmt.Sprintf(client.t("Created at: %s"), session.ctime.Format(time.RFC1123)))
  1202. service.Notice(rb, fmt.Sprintf(client.t("Last active: %s"), session.atime.Format(time.RFC1123)))
  1203. if session.certfp != "" {
  1204. service.Notice(rb, fmt.Sprintf(client.t("Certfp: %s"), session.certfp))
  1205. }
  1206. for _, capStr := range session.caps {
  1207. if capStr != "" {
  1208. service.Notice(rb, fmt.Sprintf(client.t("IRCv3 CAPs: %s"), capStr))
  1209. }
  1210. }
  1211. }
  1212. }
  1213. func nsClientsLogoutHandler(service *ircService, server *Server, client *Client, params []string, rb *ResponseBuffer) {
  1214. if len(params) < 1 {
  1215. service.Notice(rb, client.t("Missing client ID to logout (or \"all\")"))
  1216. return
  1217. }
  1218. target := client
  1219. if len(params) >= 2 {
  1220. // CLIENTS LOGOUT [nickname] [client ID]
  1221. target = server.clients.Get(params[0])
  1222. if target == nil {
  1223. service.Notice(rb, client.t("No such nick"))
  1224. return
  1225. }
  1226. // User must have "kill" privileges to logout other user sessions.
  1227. if target != client {
  1228. oper := client.Oper()
  1229. if !oper.HasRoleCapab("kill") {
  1230. service.Notice(rb, client.t("Insufficient oper privs"))
  1231. return
  1232. }
  1233. }
  1234. params = params[1:]
  1235. }
  1236. var sessionToDestroy *Session // target.destroy(nil) will logout all sessions
  1237. if strings.ToLower(params[0]) != "all" {
  1238. sessionID, err := strconv.ParseInt(params[0], 10, 64)
  1239. if err != nil {
  1240. service.Notice(rb, client.t("Client ID to logout should be an integer (or \"all\")"))
  1241. return
  1242. }
  1243. // Find the client ID that the user requested to logout.
  1244. sessions := target.Sessions()
  1245. for _, session := range sessions {
  1246. if session.sessionID == sessionID {
  1247. sessionToDestroy = session
  1248. }
  1249. }
  1250. if sessionToDestroy == nil {
  1251. service.Notice(rb, client.t("Specified client ID does not exist"))
  1252. return
  1253. }
  1254. }
  1255. target.destroy(sessionToDestroy)
  1256. if (sessionToDestroy != nil && rb.session != sessionToDestroy) || client != target {
  1257. if sessionToDestroy != nil {
  1258. service.Notice(rb, client.t("Successfully logged out session"))
  1259. } else {
  1260. service.Notice(rb, client.t("Successfully logged out all sessions"))
  1261. }
  1262. }
  1263. }
  1264. func nsCertHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  1265. verb := strings.ToLower(params[0])
  1266. params = params[1:]
  1267. var target, certfp string
  1268. switch verb {
  1269. case "list":
  1270. if 1 <= len(params) {
  1271. target = params[0]
  1272. }
  1273. case "add", "del":
  1274. if 2 <= len(params) {
  1275. target, certfp = params[0], params[1]
  1276. if cftarget, err := CasefoldName(target); err == nil && client.Account() == cftarget {
  1277. // If the target is equal to the account, then the user accidentally invoked operator
  1278. // syntax (cert add mynick <fp>) instead of self syntax (cert add <fp>).
  1279. target = ""
  1280. }
  1281. } else if len(params) == 1 {
  1282. certfp = params[0]
  1283. } else if len(params) == 0 && verb == "add" && rb.session.certfp != "" {
  1284. certfp = rb.session.certfp // #1059
  1285. } else {
  1286. service.Notice(rb, client.t("Invalid parameters"))
  1287. return
  1288. }
  1289. default:
  1290. service.Notice(rb, client.t("Invalid parameters"))
  1291. return
  1292. }
  1293. hasPrivs := client.HasRoleCapabs("accreg")
  1294. if target != "" && !hasPrivs {
  1295. service.Notice(rb, client.t("Insufficient privileges"))
  1296. return
  1297. } else if target == "" {
  1298. target = client.Account()
  1299. if target == "" {
  1300. service.Notice(rb, client.t("You're not logged into an account"))
  1301. return
  1302. }
  1303. }
  1304. var err error
  1305. switch verb {
  1306. case "list":
  1307. accountData, err := server.accounts.LoadAccount(target)
  1308. if err == errAccountDoesNotExist {
  1309. service.Notice(rb, client.t("Account does not exist"))
  1310. return
  1311. } else if err != nil {
  1312. service.Notice(rb, client.t("An error occurred"))
  1313. return
  1314. }
  1315. certfps := accountData.Credentials.Certfps
  1316. service.Notice(rb, fmt.Sprintf(client.t("There are %[1]d certificate fingerprint(s) authorized for account %[2]s."), len(certfps), accountData.Name))
  1317. for i, certfp := range certfps {
  1318. service.Notice(rb, fmt.Sprintf("%d: %s", i+1, certfp))
  1319. }
  1320. return
  1321. case "add":
  1322. err = server.accounts.addRemoveCertfp(target, certfp, true, hasPrivs)
  1323. case "del":
  1324. err = server.accounts.addRemoveCertfp(target, certfp, false, hasPrivs)
  1325. }
  1326. switch err {
  1327. case nil:
  1328. if verb == "add" {
  1329. service.Notice(rb, client.t("Certificate fingerprint successfully added"))
  1330. } else {
  1331. service.Notice(rb, client.t("Certificate fingerprint successfully removed"))
  1332. }
  1333. case errNoop:
  1334. if verb == "add" {
  1335. service.Notice(rb, client.t("That certificate fingerprint was already authorized"))
  1336. } else {
  1337. service.Notice(rb, client.t("Certificate fingerprint not found"))
  1338. }
  1339. case errAccountDoesNotExist:
  1340. service.Notice(rb, client.t("Account does not exist"))
  1341. case errLimitExceeded:
  1342. service.Notice(rb, client.t("You already have too many certificate fingerprints"))
  1343. case utils.ErrInvalidCertfp:
  1344. service.Notice(rb, client.t("Invalid certificate fingerprint"))
  1345. case errCertfpAlreadyExists:
  1346. service.Notice(rb, client.t("That certificate fingerprint is already associated with another account"))
  1347. case errEmptyCredentials:
  1348. service.Notice(rb, client.t("You can't remove all your certificate fingerprints unless you add a password"))
  1349. case errCredsExternallyManaged:
  1350. service.Notice(rb, client.t("Your account credentials are managed externally and cannot be changed here"))
  1351. case errCASFailed:
  1352. service.Notice(rb, client.t("Try again later"))
  1353. default:
  1354. server.logger.Error("internal", "could not modify certificates:", err.Error())
  1355. service.Notice(rb, client.t("An error occurred"))
  1356. }
  1357. }
  1358. func nsSuspendHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  1359. subCmd := strings.ToLower(params[0])
  1360. params = params[1:]
  1361. switch subCmd {
  1362. case "add":
  1363. nsSuspendAddHandler(service, server, client, command, params, rb)
  1364. case "del", "delete", "remove":
  1365. nsSuspendRemoveHandler(service, server, client, command, params, rb)
  1366. case "list":
  1367. nsSuspendListHandler(service, server, client, command, params, rb)
  1368. default:
  1369. service.Notice(rb, client.t("Invalid parameters"))
  1370. }
  1371. }
  1372. func nsSuspendAddHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  1373. if len(params) == 0 {
  1374. service.Notice(rb, client.t("Invalid parameters"))
  1375. return
  1376. }
  1377. account := params[0]
  1378. params = params[1:]
  1379. var duration time.Duration
  1380. if 2 <= len(params) && strings.ToLower(params[0]) == "duration" {
  1381. var err error
  1382. cDuration, err := custime.ParseDuration(params[1])
  1383. if err != nil {
  1384. service.Notice(rb, client.t("Invalid time duration for NS SUSPEND"))
  1385. return
  1386. }
  1387. duration = time.Duration(cDuration)
  1388. params = params[2:]
  1389. }
  1390. var reason string
  1391. if len(params) != 0 {
  1392. reason = strings.Join(params, " ")
  1393. }
  1394. name := client.Oper().Name
  1395. err := server.accounts.Suspend(account, duration, name, reason)
  1396. switch err {
  1397. case nil:
  1398. service.Notice(rb, fmt.Sprintf(client.t("Successfully suspended account %s"), account))
  1399. case errAccountDoesNotExist:
  1400. service.Notice(rb, client.t("No such account"))
  1401. default:
  1402. service.Notice(rb, client.t("An error occurred"))
  1403. }
  1404. }
  1405. func nsSuspendRemoveHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  1406. if len(params) == 0 {
  1407. service.Notice(rb, client.t("Invalid parameters"))
  1408. return
  1409. }
  1410. err := server.accounts.Unsuspend(params[0])
  1411. switch err {
  1412. case nil:
  1413. service.Notice(rb, fmt.Sprintf(client.t("Successfully un-suspended account %s"), params[0]))
  1414. case errAccountDoesNotExist:
  1415. service.Notice(rb, client.t("No such account"))
  1416. case errNoop:
  1417. service.Notice(rb, client.t("Account was not suspended"))
  1418. default:
  1419. service.Notice(rb, client.t("An error occurred"))
  1420. }
  1421. }
  1422. // sort in reverse order of creation time
  1423. type ByCreationTime []AccountSuspension
  1424. func (a ByCreationTime) Len() int { return len(a) }
  1425. func (a ByCreationTime) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
  1426. func (a ByCreationTime) Less(i, j int) bool { return a[i].TimeCreated.After(a[j].TimeCreated) }
  1427. func nsSuspendListHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  1428. listAccountSuspensions(client, rb, service.prefix)
  1429. }
  1430. func listAccountSuspensions(client *Client, rb *ResponseBuffer, source string) {
  1431. suspensions := client.server.accounts.ListSuspended()
  1432. sort.Sort(ByCreationTime(suspensions))
  1433. nick := client.Nick()
  1434. rb.Add(nil, source, "NOTICE", nick, fmt.Sprintf(client.t("There are %d active account suspensions."), len(suspensions)))
  1435. for _, suspension := range suspensions {
  1436. rb.Add(nil, source, "NOTICE", nick, suspensionToString(client, suspension))
  1437. }
  1438. }
  1439. func suspensionToString(client *Client, suspension AccountSuspension) (result string) {
  1440. duration := client.t("indefinite")
  1441. if suspension.Duration != time.Duration(0) {
  1442. duration = suspension.Duration.String()
  1443. }
  1444. ts := suspension.TimeCreated.Format(time.RFC1123)
  1445. reason := client.t("No reason given.")
  1446. if suspension.Reason != "" {
  1447. reason = fmt.Sprintf(client.t("Reason: %s"), suspension.Reason)
  1448. }
  1449. return fmt.Sprintf(client.t("Account %[1]s suspended at %[2]s. Duration: %[3]s. %[4]s"), suspension.AccountName, ts, duration, reason)
  1450. }
  1451. func nsSendpassHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  1452. if !nsLoginThrottleCheck(service, client, rb) {
  1453. return
  1454. }
  1455. account := params[0]
  1456. var message string
  1457. err := server.accounts.NsSendpass(client, account)
  1458. switch err {
  1459. case nil:
  1460. server.snomasks.Send(sno.LocalAccounts, fmt.Sprintf("Client %s sent a password reset for account %s", client.Nick(), account))
  1461. message = `Successfully sent password reset email`
  1462. case errAccountDoesNotExist, errAccountUnverified, errAccountSuspended:
  1463. message = err.Error()
  1464. case errValidEmailRequired:
  1465. message = `That account is not associated with an email address`
  1466. case errLimitExceeded:
  1467. message = `Try again later`
  1468. default:
  1469. server.logger.Error("services", "error in NS SENDPASS", err.Error())
  1470. message = `An error occurred`
  1471. }
  1472. rb.Notice(client.t(message))
  1473. }
  1474. func nsResetpassHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  1475. if !nsLoginThrottleCheck(service, client, rb) {
  1476. return
  1477. }
  1478. var message string
  1479. err := server.accounts.NsResetpass(client, params[0], params[1], params[2])
  1480. switch err {
  1481. case nil:
  1482. message = `Successfully reset account password`
  1483. case errAccountDoesNotExist, errAccountUnverified, errAccountSuspended, errAccountBadPassphrase:
  1484. message = err.Error()
  1485. case errAccountInvalidCredentials:
  1486. message = `Code did not match`
  1487. default:
  1488. server.logger.Error("services", "error in NS RESETPASS", err.Error())
  1489. message = `An error occurred`
  1490. }
  1491. rb.Notice(client.t(message))
  1492. }
  1493. func nsRenameHandler(service *ircService, server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  1494. oldName, newName := params[0], params[1]
  1495. err := server.accounts.Rename(oldName, newName)
  1496. if err != nil {
  1497. service.Notice(rb, fmt.Sprintf(client.t("Couldn't rename account: %s"), client.t(err.Error())))
  1498. return
  1499. }
  1500. service.Notice(rb, client.t("Successfully renamed account"))
  1501. if server.Config().Accounts.NickReservation.ForceNickEqualsAccount {
  1502. if curClient := server.clients.Get(oldName); curClient != nil {
  1503. renameErr := performNickChange(client.server, client, curClient, nil, newName, rb)
  1504. if renameErr != nil && renameErr != errNoop {
  1505. service.Notice(rb, fmt.Sprintf(client.t("Warning: could not rename affected client: %v"), err))
  1506. }
  1507. }
  1508. }
  1509. }