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.

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300
  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. "strconv"
  8. "strings"
  9. "time"
  10. "github.com/goshuirc/irc-go/ircfmt"
  11. "github.com/oragono/oragono/irc/passwd"
  12. "github.com/oragono/oragono/irc/sno"
  13. "github.com/oragono/oragono/irc/utils"
  14. )
  15. // "enabled" callbacks for specific nickserv commands
  16. func servCmdRequiresAccreg(config *Config) bool {
  17. return config.Accounts.Registration.Enabled
  18. }
  19. func servCmdRequiresAuthEnabled(config *Config) bool {
  20. return config.Accounts.AuthenticationEnabled
  21. }
  22. func servCmdRequiresNickRes(config *Config) bool {
  23. return config.Accounts.AuthenticationEnabled && config.Accounts.NickReservation.Enabled
  24. }
  25. func servCmdRequiresBouncerEnabled(config *Config) bool {
  26. return config.Accounts.Multiclient.Enabled
  27. }
  28. const (
  29. nsPrefix = "NickServ!NickServ@localhost"
  30. )
  31. const nickservHelp = `NickServ lets you register, log in to, and manage an account.`
  32. var (
  33. nickservCommands = map[string]*serviceCommand{
  34. "clients": {
  35. handler: nsClientsHandler,
  36. help: `Syntax: $bCLIENTS LIST [nickname]$b
  37. CLIENTS LIST shows information about the clients currently attached, via
  38. the server's multiclient functionality, to your nickname. An administrator
  39. can use this command to list another user's clients.
  40. Syntax: $bCLIENTS LOGOUT [nickname] [client_id/all]$b
  41. CLIENTS LOGOUT detaches a single client, or all other clients currently
  42. attached, via the server's multiclient functionality, to your nickname. An
  43. administrator can use this command to logout another user's clients.`,
  44. helpShort: `$bCLIENTS$b can list and logout the sessions attached to a nickname.`,
  45. enabled: servCmdRequiresBouncerEnabled,
  46. minParams: 1,
  47. },
  48. "drop": {
  49. handler: nsDropHandler,
  50. help: `Syntax: $bDROP [nickname]$b
  51. DROP de-links the given (or your current) nickname from your user account.`,
  52. helpShort: `$bDROP$b de-links your current (or the given) nickname from your user account.`,
  53. enabled: servCmdRequiresNickRes,
  54. authRequired: true,
  55. },
  56. "enforce": {
  57. hidden: true,
  58. handler: nsEnforceHandler,
  59. help: `Syntax: $bENFORCE [method]$b
  60. ENFORCE is an alias for $bGET enforce$b and $bSET enforce$b. See the help
  61. entry for $bSET$b for more information.`,
  62. authRequired: true,
  63. enabled: servCmdRequiresNickRes,
  64. },
  65. "ghost": {
  66. handler: nsGhostHandler,
  67. help: `Syntax: $bGHOST <nickname>$b
  68. GHOST disconnects the given user from the network if they're logged in with the
  69. same user account, letting you reclaim your nickname.`,
  70. helpShort: `$bGHOST$b reclaims your nickname.`,
  71. enabled: servCmdRequiresNickRes,
  72. authRequired: true,
  73. minParams: 1,
  74. },
  75. "group": {
  76. handler: nsGroupHandler,
  77. help: `Syntax: $bGROUP$b
  78. GROUP links your current nickname with your logged-in account, so other people
  79. will not be able to use it.`,
  80. helpShort: `$bGROUP$b links your current nickname to your user account.`,
  81. enabled: servCmdRequiresNickRes,
  82. authRequired: true,
  83. },
  84. "identify": {
  85. handler: nsIdentifyHandler,
  86. help: `Syntax: $bIDENTIFY <username> [password]$b
  87. IDENTIFY lets you login to the given username using either password auth, or
  88. certfp (your client certificate) if a password is not given.`,
  89. helpShort: `$bIDENTIFY$b lets you login to your account.`,
  90. enabled: servCmdRequiresAuthEnabled,
  91. minParams: 1,
  92. },
  93. "list": {
  94. handler: nsListHandler,
  95. help: `Syntax: $bLIST [regex]$b
  96. LIST returns the list of registered nicknames, which match the given regex.
  97. If no regex is provided, all registered nicknames are returned.`,
  98. helpShort: `$bLIST$b searches the list of registered nicknames.`,
  99. enabled: servCmdRequiresAuthEnabled,
  100. capabs: []string{"accreg"},
  101. minParams: 0,
  102. },
  103. "info": {
  104. handler: nsInfoHandler,
  105. help: `Syntax: $bINFO [username]$b
  106. INFO gives you information about the given (or your own) user account.`,
  107. helpShort: `$bINFO$b gives you information on a user account.`,
  108. },
  109. "register": {
  110. handler: nsRegisterHandler,
  111. // TODO: "email" is an oversimplification here; it's actually any callback, e.g.,
  112. // person@example.com, mailto:person@example.com, tel:16505551234.
  113. help: `Syntax: $bREGISTER <password> [email]$b
  114. REGISTER lets you register your current nickname as a user account. If the
  115. server allows anonymous registration, you can omit the e-mail address.
  116. If you are currently logged in with a TLS client certificate and wish to use
  117. it instead of a password to log in, send * as the password.`,
  118. helpShort: `$bREGISTER$b lets you register a user account.`,
  119. enabled: servCmdRequiresAccreg,
  120. minParams: 1,
  121. maxParams: 2,
  122. },
  123. "sadrop": {
  124. handler: nsDropHandler,
  125. help: `Syntax: $bSADROP <nickname>$b
  126. SADROP forcibly de-links the given nickname from the attached user account.`,
  127. helpShort: `$bSADROP$b forcibly de-links the given nickname from its user account.`,
  128. capabs: []string{"accreg"},
  129. enabled: servCmdRequiresNickRes,
  130. minParams: 1,
  131. },
  132. "saregister": {
  133. handler: nsSaregisterHandler,
  134. help: `Syntax: $bSAREGISTER <username> [password]$b
  135. SAREGISTER registers an account on someone else's behalf.
  136. This is for use in configurations that require SASL for all connections;
  137. an administrator can set use this command to set up user accounts.`,
  138. helpShort: `$bSAREGISTER$b registers an account on someone else's behalf.`,
  139. enabled: servCmdRequiresAuthEnabled,
  140. capabs: []string{"accreg"},
  141. minParams: 1,
  142. },
  143. "sessions": {
  144. hidden: true,
  145. handler: nsClientsHandler,
  146. help: `Syntax: $bSESSIONS [nickname]$b
  147. SESSIONS is an alias for $bCLIENTS LIST$b. See the help entry for $bCLIENTS$b
  148. for more information.`,
  149. enabled: servCmdRequiresBouncerEnabled,
  150. },
  151. "unregister": {
  152. handler: nsUnregisterHandler,
  153. help: `Syntax: $bUNREGISTER <username> [code]$b
  154. UNREGISTER lets you delete your user account (or someone else's, if you're an
  155. IRC operator with the correct permissions). To prevent accidental
  156. unregistrations, a verification code is required; invoking the command without
  157. a code will display the necessary code.`,
  158. helpShort: `$bUNREGISTER$b lets you delete your user account.`,
  159. enabled: servCmdRequiresAuthEnabled,
  160. minParams: 1,
  161. },
  162. "erase": {
  163. handler: nsUnregisterHandler,
  164. help: `Syntax: $bERASE <username> [code]$b
  165. ERASE deletes all records of an account, allowing it to be re-registered.
  166. This should be used with caution, because it violates an expectation that
  167. account names are permanent identifiers. Typically, UNREGISTER should be
  168. used instead. A confirmation code is required; invoking the command
  169. without a code will display the necessary code.`,
  170. helpShort: `$bERASE$b erases all records of an account, allowing reuse.`,
  171. enabled: servCmdRequiresAuthEnabled,
  172. capabs: []string{"accreg"},
  173. minParams: 1,
  174. },
  175. "verify": {
  176. handler: nsVerifyHandler,
  177. help: `Syntax: $bVERIFY <username> <code>$b
  178. VERIFY lets you complete an account registration, if the server requires email
  179. or other verification.`,
  180. helpShort: `$bVERIFY$b lets you complete account registration.`,
  181. enabled: servCmdRequiresAccreg,
  182. minParams: 2,
  183. },
  184. "passwd": {
  185. handler: nsPasswdHandler,
  186. help: `Syntax: $bPASSWD <current> <new> <new_again>$b
  187. Or: $bPASSWD <username> <new>$b
  188. PASSWD lets you change your account password. You must supply your current
  189. password and confirm the new one by typing it twice. If you're an IRC operator
  190. with the correct permissions, you can use PASSWD to reset someone else's
  191. password by supplying their username and then the desired password. To
  192. indicate an empty password, use * instead.`,
  193. helpShort: `$bPASSWD$b lets you change your password.`,
  194. enabled: servCmdRequiresAuthEnabled,
  195. minParams: 2,
  196. },
  197. "get": {
  198. handler: nsGetHandler,
  199. help: `Syntax: $bGET <setting>$b
  200. GET queries the current values of your account settings. For more information
  201. on the settings and their possible values, see HELP SET.`,
  202. helpShort: `$bGET$b queries the current values of your account settings`,
  203. authRequired: true,
  204. enabled: servCmdRequiresAuthEnabled,
  205. minParams: 1,
  206. },
  207. "saget": {
  208. handler: nsGetHandler,
  209. help: `Syntax: $bSAGET <account> <setting>$b
  210. SAGET queries the values of someone else's account settings. For more
  211. information on the settings and their possible values, see HELP SET.`,
  212. helpShort: `$bSAGET$b queries the current values of another user's account settings`,
  213. enabled: servCmdRequiresAuthEnabled,
  214. minParams: 2,
  215. capabs: []string{"accreg"},
  216. },
  217. "set": {
  218. handler: nsSetHandler,
  219. helpShort: `$bSET$b modifies your account settings`,
  220. // these are broken out as separate strings so they can be translated separately
  221. helpStrings: []string{
  222. `Syntax $bSET <setting> <value>$b
  223. SET modifies your account settings. The following settings are available:`,
  224. `$bENFORCE$b
  225. 'enforce' lets you specify a custom enforcement mechanism for your registered
  226. nicknames. Your options are:
  227. 1. 'none' [no enforcement, overriding the server default]
  228. 2. 'strict' [you must already be authenticated to use the nick]
  229. 3. 'default' [use the server default]`,
  230. `$bMULTICLIENT$b
  231. If 'multiclient' is enabled and you are already logged in and using a nick, a
  232. second client of yours that authenticates with SASL and requests the same nick
  233. is allowed to attach to the nick as well (this is comparable to the behavior
  234. of IRC "bouncers" like ZNC). Your options are 'on' (allow this behavior),
  235. 'off' (disallow it), and 'default' (use the server default value).`,
  236. `$bAUTOREPLAY-LINES$b
  237. 'autoreplay-lines' controls the number of lines of channel history that will
  238. be replayed to you automatically when joining a channel. Your options are any
  239. positive number, 0 to disable the feature, and 'default' to use the server
  240. default.`,
  241. `$bREPLAY-JOINS$b
  242. 'replay-joins' controls whether replayed channel history will include
  243. lines for join and part. This provides more information about the context of
  244. messages, but may be spammy. Your options are 'always', 'never', and the default
  245. of 'commands-only' (the messages will be replayed in /HISTORY output, but not
  246. during autoreplay).`,
  247. `$bALWAYS-ON$b
  248. 'always-on' controls whether your nickname/identity will remain active
  249. even while you are disconnected from the server. Your options are 'true',
  250. 'false', and 'default' (use the server default value).`,
  251. `$bAUTOREPLAY-MISSED$b
  252. 'autoreplay-missed' is only effective for always-on clients. If enabled,
  253. if you have at most one active session, the server will remember the time
  254. you disconnect and then replay missed messages to you when you reconnect.
  255. Your options are 'on' and 'off'.`,
  256. `$bDM-HISTORY$b
  257. 'dm-history' is only effective for always-on clients. It lets you control
  258. how the history of your direct messages is stored. Your options are:
  259. 1. 'off' [no history]
  260. 2. 'ephemeral' [a limited amount of temporary history, not stored on disk]
  261. 3. 'on' [history stored in a permanent database, if available]
  262. 4. 'default' [use the server default]`,
  263. `$bAUTO-AWAY$b
  264. 'auto-away' is only effective for always-on clients. If enabled, you will
  265. automatically be marked away when all your sessions are disconnected, and
  266. automatically return from away when you connect again.`,
  267. },
  268. authRequired: true,
  269. enabled: servCmdRequiresAuthEnabled,
  270. minParams: 2,
  271. },
  272. "saset": {
  273. handler: nsSetHandler,
  274. help: `Syntax: $bSASET <account> <setting> <value>$b
  275. SASET modifies the values of someone else's account settings. For more
  276. information on the settings and their possible values, see HELP SET.`,
  277. helpShort: `$bSASET$b modifies another user's account settings`,
  278. enabled: servCmdRequiresAuthEnabled,
  279. minParams: 3,
  280. capabs: []string{"accreg"},
  281. },
  282. "cert": {
  283. handler: nsCertHandler,
  284. help: `Syntax: $bCERT <LIST | ADD | DEL> [account] [certfp]$b
  285. CERT examines or modifies the TLS certificate fingerprints that can be used to
  286. log into an account. Specifically, $bCERT LIST$b lists the authorized
  287. fingerprints, $bCERT ADD <fingerprint>$b adds a new fingerprint, and
  288. $bCERT DEL <fingerprint>$b removes a fingerprint. If you're an IRC operator
  289. with the correct permissions, you can act on another user's account, for
  290. example with $bCERT ADD <account> <fingerprint>$b.`,
  291. helpShort: `$bCERT$b controls a user account's certificate fingerprints`,
  292. enabled: servCmdRequiresAuthEnabled,
  293. minParams: 1,
  294. },
  295. "suspend": {
  296. handler: nsSuspendHandler,
  297. help: `Syntax: $bSUSPEND <nickname>$b
  298. SUSPEND disables an account and disconnects the associated clients.`,
  299. helpShort: `$bSUSPEND$b disables an account and disconnects the clients`,
  300. minParams: 1,
  301. capabs: []string{"accreg"},
  302. },
  303. "unsuspend": {
  304. handler: nsUnsuspendHandler,
  305. help: `Syntax: $bUNSUSPEND <nickname>$b
  306. UNSUSPEND reverses a previous SUSPEND, restoring access to the account.`,
  307. helpShort: `$bUNSUSPEND$b restores access to a suspended account`,
  308. minParams: 1,
  309. capabs: []string{"accreg"},
  310. },
  311. }
  312. )
  313. // nsNotice sends the client a notice from NickServ
  314. func nsNotice(rb *ResponseBuffer, text string) {
  315. // XXX i can't figure out how to use OragonoServices[servicename].prefix here
  316. // without creating a compile-time initialization loop
  317. rb.Add(nil, nsPrefix, "NOTICE", rb.target.Nick(), text)
  318. }
  319. func nsGetHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  320. var account string
  321. if command == "saget" {
  322. account = params[0]
  323. params = params[1:]
  324. } else {
  325. account = client.Account()
  326. }
  327. accountData, err := server.accounts.LoadAccount(account)
  328. if err == errAccountDoesNotExist {
  329. nsNotice(rb, client.t("No such account"))
  330. return
  331. } else if err != nil {
  332. nsNotice(rb, client.t("Error loading account data"))
  333. return
  334. }
  335. displaySetting(params[0], accountData.Settings, client, rb)
  336. }
  337. func displaySetting(settingName string, settings AccountSettings, client *Client, rb *ResponseBuffer) {
  338. config := client.server.Config()
  339. switch strings.ToLower(settingName) {
  340. case "enforce":
  341. storedValue := settings.NickEnforcement
  342. serializedStoredValue := nickReservationToString(storedValue)
  343. nsNotice(rb, fmt.Sprintf(client.t("Your stored nickname enforcement setting is: %s"), serializedStoredValue))
  344. serializedActualValue := nickReservationToString(configuredEnforcementMethod(config, storedValue))
  345. nsNotice(rb, fmt.Sprintf(client.t("Given current server settings, your nickname is enforced with: %s"), serializedActualValue))
  346. case "autoreplay-lines":
  347. if settings.AutoreplayLines == nil {
  348. nsNotice(rb, fmt.Sprintf(client.t("You will receive the server default of %d lines of autoreplayed history"), config.History.AutoreplayOnJoin))
  349. } else {
  350. nsNotice(rb, fmt.Sprintf(client.t("You will receive %d lines of autoreplayed history"), *settings.AutoreplayLines))
  351. }
  352. case "replay-joins":
  353. switch settings.ReplayJoins {
  354. case ReplayJoinsCommandsOnly:
  355. nsNotice(rb, client.t("You will see JOINs and PARTs in /HISTORY output, but not in autoreplay"))
  356. case ReplayJoinsAlways:
  357. nsNotice(rb, client.t("You will see JOINs and PARTs in /HISTORY output and in autoreplay"))
  358. case ReplayJoinsNever:
  359. nsNotice(rb, client.t("You will not see JOINs and PARTs in /HISTORY output or in autoreplay"))
  360. }
  361. case "multiclient":
  362. if !config.Accounts.Multiclient.Enabled {
  363. nsNotice(rb, client.t("This feature has been disabled by the server administrators"))
  364. } else {
  365. switch settings.AllowBouncer {
  366. case MulticlientAllowedServerDefault:
  367. if config.Accounts.Multiclient.AllowedByDefault {
  368. nsNotice(rb, client.t("Multiclient functionality is currently enabled for your account, but you can opt out"))
  369. } else {
  370. nsNotice(rb, client.t("Multiclient functionality is currently disabled for your account, but you can opt in"))
  371. }
  372. case MulticlientDisallowedByUser:
  373. nsNotice(rb, client.t("Multiclient functionality is currently disabled for your account"))
  374. case MulticlientAllowedByUser:
  375. nsNotice(rb, client.t("Multiclient functionality is currently enabled for your account"))
  376. }
  377. }
  378. case "always-on":
  379. stored := settings.AlwaysOn
  380. actual := persistenceEnabled(config.Accounts.Multiclient.AlwaysOn, stored)
  381. nsNotice(rb, fmt.Sprintf(client.t("Your stored always-on setting is: %s"), persistentStatusToString(stored)))
  382. if actual {
  383. nsNotice(rb, client.t("Given current server settings, your client is always-on"))
  384. } else {
  385. nsNotice(rb, client.t("Given current server settings, your client is not always-on"))
  386. }
  387. case "autoreplay-missed":
  388. stored := settings.AutoreplayMissed
  389. if stored {
  390. alwaysOn := persistenceEnabled(config.Accounts.Multiclient.AlwaysOn, settings.AlwaysOn)
  391. if alwaysOn {
  392. nsNotice(rb, client.t("Autoreplay of missed messages is enabled"))
  393. } else {
  394. nsNotice(rb, client.t("You have enabled autoreplay of missed messages, but you can't receive them because your client isn't set to always-on"))
  395. }
  396. } else {
  397. nsNotice(rb, client.t("Your account is not configured to receive autoreplayed missed messages"))
  398. }
  399. case "auto-away":
  400. stored := settings.AutoAway
  401. alwaysOn := persistenceEnabled(config.Accounts.Multiclient.AlwaysOn, settings.AlwaysOn)
  402. actual := persistenceEnabled(config.Accounts.Multiclient.AutoAway, settings.AutoAway)
  403. nsNotice(rb, fmt.Sprintf(client.t("Your stored auto-away setting is: %s"), persistentStatusToString(stored)))
  404. if actual && alwaysOn {
  405. nsNotice(rb, client.t("Given current server settings, auto-away is enabled for your client"))
  406. } else if actual && !alwaysOn {
  407. nsNotice(rb, client.t("Because your client is not always-on, auto-away is disabled"))
  408. } else if !actual {
  409. nsNotice(rb, client.t("Given current server settings, auto-away is disabled for your client"))
  410. }
  411. case "dm-history":
  412. effectiveValue := historyEnabled(config.History.Persistent.DirectMessages, settings.DMHistory)
  413. nsNotice(rb, fmt.Sprintf(client.t("Your stored direct message history setting is: %s"), historyStatusToString(settings.DMHistory)))
  414. nsNotice(rb, fmt.Sprintf(client.t("Given current server settings, your direct message history setting is: %s"), historyStatusToString(effectiveValue)))
  415. default:
  416. nsNotice(rb, client.t("No such setting"))
  417. }
  418. }
  419. func nsSetHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  420. var account string
  421. if command == "saset" {
  422. account = params[0]
  423. params = params[1:]
  424. } else {
  425. account = client.Account()
  426. }
  427. var munger settingsMunger
  428. var finalSettings AccountSettings
  429. var err error
  430. switch strings.ToLower(params[0]) {
  431. case "pass", "password":
  432. nsNotice(rb, client.t("To change a password, use the PASSWD command. For details, /msg NickServ HELP PASSWD"))
  433. return
  434. case "enforce":
  435. var method NickEnforcementMethod
  436. method, err = nickReservationFromString(params[1])
  437. if err != nil {
  438. err = errInvalidParams
  439. break
  440. }
  441. // updating enforcement settings is special-cased, because it requires
  442. // an update to server.accounts.accountToMethod
  443. finalSettings, err = server.accounts.SetEnforcementStatus(account, method)
  444. if err == nil {
  445. finalSettings.NickEnforcement = method // success
  446. }
  447. case "autoreplay-lines":
  448. var newValue *int
  449. if strings.ToLower(params[1]) != "default" {
  450. val, err_ := strconv.Atoi(params[1])
  451. if err_ != nil || val < 0 {
  452. err = errInvalidParams
  453. break
  454. }
  455. newValue = new(int)
  456. *newValue = val
  457. }
  458. munger = func(in AccountSettings) (out AccountSettings, err error) {
  459. out = in
  460. out.AutoreplayLines = newValue
  461. return
  462. }
  463. case "multiclient":
  464. var newValue MulticlientAllowedSetting
  465. if strings.ToLower(params[1]) == "default" {
  466. newValue = MulticlientAllowedServerDefault
  467. } else {
  468. var enabled bool
  469. enabled, err = utils.StringToBool(params[1])
  470. if enabled {
  471. newValue = MulticlientAllowedByUser
  472. } else {
  473. newValue = MulticlientDisallowedByUser
  474. }
  475. }
  476. if err == nil {
  477. munger = func(in AccountSettings) (out AccountSettings, err error) {
  478. out = in
  479. out.AllowBouncer = newValue
  480. return
  481. }
  482. }
  483. case "replay-joins":
  484. var newValue ReplayJoinsSetting
  485. newValue, err = replayJoinsSettingFromString(params[1])
  486. if err == nil {
  487. munger = func(in AccountSettings) (out AccountSettings, err error) {
  488. out = in
  489. out.ReplayJoins = newValue
  490. return
  491. }
  492. }
  493. case "always-on":
  494. // #821: it's problematic to alter the value of always-on if you're not
  495. // the (actual or potential) always-on client yourself. make an exception
  496. // for `saset` to give operators an escape hatch (any consistency problems
  497. // can probably be fixed by restarting the server):
  498. if command != "saset" {
  499. details := client.Details()
  500. if details.nick != details.accountName {
  501. err = errNickAccountMismatch
  502. }
  503. }
  504. if err == nil {
  505. var newValue PersistentStatus
  506. newValue, err = persistentStatusFromString(params[1])
  507. // "opt-in" and "opt-out" don't make sense as user preferences
  508. if err == nil && newValue != PersistentOptIn && newValue != PersistentOptOut {
  509. munger = func(in AccountSettings) (out AccountSettings, err error) {
  510. out = in
  511. out.AlwaysOn = newValue
  512. return
  513. }
  514. }
  515. }
  516. case "autoreplay-missed":
  517. var newValue bool
  518. newValue, err = utils.StringToBool(params[1])
  519. if err == nil {
  520. munger = func(in AccountSettings) (out AccountSettings, err error) {
  521. out = in
  522. out.AutoreplayMissed = newValue
  523. return
  524. }
  525. }
  526. case "auto-away":
  527. var newValue PersistentStatus
  528. newValue, err = persistentStatusFromString(params[1])
  529. // "opt-in" and "opt-out" don't make sense as user preferences
  530. if err == nil && newValue != PersistentOptIn && newValue != PersistentOptOut {
  531. munger = func(in AccountSettings) (out AccountSettings, err error) {
  532. out = in
  533. out.AutoAway = newValue
  534. return
  535. }
  536. }
  537. case "dm-history":
  538. var newValue HistoryStatus
  539. newValue, err = historyStatusFromString(params[1])
  540. if err == nil {
  541. munger = func(in AccountSettings) (out AccountSettings, err error) {
  542. out = in
  543. out.DMHistory = newValue
  544. return
  545. }
  546. }
  547. default:
  548. err = errInvalidParams
  549. }
  550. if munger != nil {
  551. finalSettings, err = server.accounts.ModifyAccountSettings(account, munger)
  552. }
  553. switch err {
  554. case nil:
  555. nsNotice(rb, client.t("Successfully changed your account settings"))
  556. displaySetting(params[0], finalSettings, client, rb)
  557. case errInvalidParams, errAccountDoesNotExist, errFeatureDisabled, errAccountUnverified, errAccountUpdateFailed:
  558. nsNotice(rb, client.t(err.Error()))
  559. case errNickAccountMismatch:
  560. nsNotice(rb, fmt.Sprintf(client.t("Your nickname must match your account name %s exactly to modify this setting. Try changing it with /NICK, or logging out and back in with the correct nickname."), client.AccountName()))
  561. default:
  562. // unknown error
  563. nsNotice(rb, client.t("An error occurred"))
  564. }
  565. }
  566. func nsDropHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  567. sadrop := command == "sadrop"
  568. var nick string
  569. if len(params) > 0 {
  570. nick = params[0]
  571. } else {
  572. nick = client.NickCasefolded()
  573. }
  574. err := server.accounts.SetNickReserved(client, nick, sadrop, false)
  575. if err == nil {
  576. nsNotice(rb, fmt.Sprintf(client.t("Successfully ungrouped nick %s with your account"), nick))
  577. } else if err == errAccountNotLoggedIn {
  578. nsNotice(rb, client.t("You're not logged into an account"))
  579. } else if err == errAccountCantDropPrimaryNick {
  580. nsNotice(rb, client.t("You can't ungroup your primary nickname (try unregistering your account instead)"))
  581. } else {
  582. nsNotice(rb, client.t("Could not ungroup nick"))
  583. }
  584. }
  585. func nsGhostHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  586. nick := params[0]
  587. ghost := server.clients.Get(nick)
  588. if ghost == nil {
  589. nsNotice(rb, client.t("No such nick"))
  590. return
  591. } else if ghost == client {
  592. nsNotice(rb, client.t("You can't GHOST yourself (try /QUIT instead)"))
  593. return
  594. } else if ghost.AlwaysOn() {
  595. nsNotice(rb, client.t("You can't GHOST an always-on client"))
  596. return
  597. }
  598. authorized := false
  599. account := client.Account()
  600. if account != "" {
  601. // the user must either own the nick, or the target client
  602. authorized = (server.accounts.NickToAccount(nick) == account) || (ghost.Account() == account)
  603. }
  604. if !authorized {
  605. nsNotice(rb, client.t("You don't own that nick"))
  606. return
  607. }
  608. ghost.Quit(fmt.Sprintf(ghost.t("GHOSTed by %s"), client.Nick()), nil)
  609. ghost.destroy(nil)
  610. }
  611. func nsGroupHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  612. nick := client.Nick()
  613. err := server.accounts.SetNickReserved(client, nick, false, true)
  614. if err == nil {
  615. nsNotice(rb, fmt.Sprintf(client.t("Successfully grouped nick %s with your account"), nick))
  616. } else if err == errAccountTooManyNicks {
  617. nsNotice(rb, client.t("You have too many nicks reserved already (you can remove some with /NS DROP)"))
  618. } else if err == errNicknameReserved {
  619. nsNotice(rb, client.t("That nickname is already reserved by someone else"))
  620. } else {
  621. nsNotice(rb, client.t("Error reserving nickname"))
  622. }
  623. }
  624. func nsLoginThrottleCheck(client *Client, rb *ResponseBuffer) (success bool) {
  625. throttled, remainingTime := client.checkLoginThrottle()
  626. if throttled {
  627. nsNotice(rb, fmt.Sprintf(client.t("Please wait at least %v and try again"), remainingTime))
  628. }
  629. return !throttled
  630. }
  631. func nsIdentifyHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  632. if client.LoggedIntoAccount() {
  633. nsNotice(rb, client.t("You're already logged into an account"))
  634. return
  635. }
  636. var err error
  637. loginSuccessful := false
  638. var username, passphrase string
  639. if len(params) == 1 {
  640. if rb.session.certfp != "" {
  641. username = params[0]
  642. } else {
  643. // XXX undocumented compatibility mode with other nickservs, allowing
  644. // /msg NickServ identify passphrase
  645. username = client.NickCasefolded()
  646. passphrase = params[0]
  647. }
  648. } else {
  649. username = params[0]
  650. passphrase = params[1]
  651. }
  652. // try passphrase
  653. if passphrase != "" {
  654. err = server.accounts.AuthenticateByPassphrase(client, username, passphrase)
  655. loginSuccessful = (err == nil)
  656. }
  657. // try certfp
  658. if !loginSuccessful && rb.session.certfp != "" {
  659. err = server.accounts.AuthenticateByCertificate(client, rb.session.certfp, rb.session.peerCerts, "")
  660. loginSuccessful = (err == nil)
  661. }
  662. nickFixupFailed := false
  663. if loginSuccessful {
  664. if !fixupNickEqualsAccount(client, rb, server.Config()) {
  665. loginSuccessful = false
  666. // fixupNickEqualsAccount sends its own error message, don't send another
  667. nickFixupFailed = true
  668. }
  669. }
  670. if loginSuccessful {
  671. sendSuccessfulAccountAuth(client, rb, true, true)
  672. } else if !nickFixupFailed {
  673. nsNotice(rb, fmt.Sprintf(client.t("Authentication failed: %s"), authErrorToMessage(server, err)))
  674. }
  675. }
  676. func nsListHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  677. if !client.HasRoleCapabs("accreg") {
  678. nsNotice(rb, client.t("Insufficient privileges"))
  679. return
  680. }
  681. var searchRegex *regexp.Regexp
  682. if len(params) > 0 {
  683. var err error
  684. searchRegex, err = regexp.Compile(params[0])
  685. if err != nil {
  686. nsNotice(rb, client.t("Invalid regex"))
  687. return
  688. }
  689. }
  690. nsNotice(rb, ircfmt.Unescape(client.t("*** $bNickServ LIST$b ***")))
  691. nicks := server.accounts.AllNicks()
  692. for _, nick := range nicks {
  693. if searchRegex == nil || searchRegex.MatchString(nick) {
  694. nsNotice(rb, fmt.Sprintf(" %s", nick))
  695. }
  696. }
  697. nsNotice(rb, ircfmt.Unescape(client.t("*** $bEnd of NickServ LIST$b ***")))
  698. }
  699. func nsInfoHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  700. if !server.Config().Accounts.AuthenticationEnabled && !client.HasRoleCapabs("accreg") {
  701. nsNotice(rb, client.t("This command has been disabled by the server administrators"))
  702. return
  703. }
  704. var accountName string
  705. if len(params) > 0 {
  706. nick := params[0]
  707. if server.Config().Accounts.NickReservation.Enabled {
  708. accountName = server.accounts.NickToAccount(nick)
  709. if accountName == "" {
  710. nsNotice(rb, client.t("That nickname is not registered"))
  711. return
  712. }
  713. } else {
  714. accountName = nick
  715. }
  716. } else {
  717. accountName = client.Account()
  718. if accountName == "" {
  719. nsNotice(rb, client.t("You're not logged into an account"))
  720. return
  721. }
  722. }
  723. account, err := server.accounts.LoadAccount(accountName)
  724. if err != nil || !account.Verified {
  725. nsNotice(rb, client.t("Account does not exist"))
  726. return
  727. }
  728. nsNotice(rb, fmt.Sprintf(client.t("Account: %s"), account.Name))
  729. registeredAt := account.RegisteredAt.Format(time.RFC1123)
  730. nsNotice(rb, fmt.Sprintf(client.t("Registered at: %s"), registeredAt))
  731. // TODO nicer formatting for this
  732. for _, nick := range account.AdditionalNicks {
  733. nsNotice(rb, fmt.Sprintf(client.t("Additional grouped nick: %s"), nick))
  734. }
  735. for _, channel := range server.accounts.ChannelsForAccount(accountName) {
  736. nsNotice(rb, fmt.Sprintf(client.t("Registered channel: %s"), channel))
  737. }
  738. }
  739. func nsRegisterHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  740. details := client.Details()
  741. passphrase := params[0]
  742. var email string
  743. if 1 < len(params) {
  744. email = params[1]
  745. }
  746. certfp := rb.session.certfp
  747. if passphrase == "*" {
  748. if certfp == "" {
  749. nsNotice(rb, client.t("You must be connected with TLS and a client certificate to do this"))
  750. return
  751. } else {
  752. passphrase = ""
  753. }
  754. }
  755. if passphrase != "" {
  756. cfPassphrase, err := Casefold(passphrase)
  757. if err == nil && cfPassphrase == details.nickCasefolded {
  758. nsNotice(rb, client.t("Usage: REGISTER <passphrase> [email]")) // #1179
  759. return
  760. }
  761. }
  762. if !nsLoginThrottleCheck(client, rb) {
  763. return
  764. }
  765. config := server.Config()
  766. account := details.nick
  767. if config.Accounts.NickReservation.ForceGuestFormat {
  768. matches := config.Accounts.NickReservation.guestRegexp.FindStringSubmatch(account)
  769. if matches == nil || len(matches) < 2 {
  770. nsNotice(rb, client.t("Erroneous nickname"))
  771. return
  772. }
  773. account = matches[1]
  774. }
  775. callbackNamespace, callbackValue, validationErr := parseCallback(email, config)
  776. if validationErr != nil {
  777. nsNotice(rb, client.t("Registration requires a valid e-mail address"))
  778. return
  779. }
  780. err := server.accounts.Register(client, account, callbackNamespace, callbackValue, passphrase, rb.session.certfp)
  781. if err == nil {
  782. if callbackNamespace == "*" {
  783. err = server.accounts.Verify(client, account, "")
  784. if err == nil && fixupNickEqualsAccount(client, rb, config) {
  785. sendSuccessfulRegResponse(client, rb, true)
  786. }
  787. } else {
  788. messageTemplate := client.t("Account created, pending verification; verification code has been sent to %s")
  789. message := fmt.Sprintf(messageTemplate, callbackValue)
  790. nsNotice(rb, message)
  791. }
  792. } else {
  793. // details could not be stored and relevant numerics have been dispatched, abort
  794. message := registrationErrorToMessage(err)
  795. nsNotice(rb, client.t(message))
  796. }
  797. }
  798. func nsSaregisterHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  799. var account, passphrase string
  800. account = params[0]
  801. if 1 < len(params) && params[1] != "*" {
  802. passphrase = params[1]
  803. }
  804. err := server.accounts.SARegister(account, passphrase)
  805. if err != nil {
  806. var errMsg string
  807. if err == errAccountAlreadyRegistered || err == errAccountAlreadyVerified {
  808. errMsg = client.t("Account already exists")
  809. } else if err == errAccountBadPassphrase {
  810. errMsg = client.t("Passphrase contains forbidden characters or is otherwise invalid")
  811. } else {
  812. server.logger.Error("services", "unknown error from saregister", err.Error())
  813. errMsg = client.t("Could not register")
  814. }
  815. nsNotice(rb, errMsg)
  816. } else {
  817. nsNotice(rb, fmt.Sprintf(client.t("Successfully registered account %s"), account))
  818. 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))
  819. }
  820. }
  821. func nsUnregisterHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  822. erase := command == "erase"
  823. username := params[0]
  824. var verificationCode string
  825. if len(params) > 1 {
  826. verificationCode = params[1]
  827. }
  828. if username == "" {
  829. nsNotice(rb, client.t("You must specify an account"))
  830. return
  831. }
  832. var accountName string
  833. var registeredAt time.Time
  834. if erase {
  835. // account may not be in a loadable state, e.g., if it was unregistered
  836. accountName = username
  837. // make the confirmation code nondeterministic for ERASE
  838. registeredAt = server.ctime
  839. } else {
  840. account, err := server.accounts.LoadAccount(username)
  841. if err == errAccountDoesNotExist {
  842. nsNotice(rb, client.t("Invalid account name"))
  843. return
  844. } else if err != nil {
  845. nsNotice(rb, client.t("Internal error"))
  846. return
  847. }
  848. accountName = account.Name
  849. registeredAt = account.RegisteredAt
  850. }
  851. if !(accountName == client.AccountName() || client.HasRoleCapabs("accreg")) {
  852. nsNotice(rb, client.t("Insufficient oper privs"))
  853. return
  854. }
  855. expectedCode := utils.ConfirmationCode(accountName, registeredAt)
  856. if expectedCode != verificationCode {
  857. if erase {
  858. nsNotice(rb, ircfmt.Unescape(client.t("$bWarning: erasing this account will allow it to be re-registered; consider UNREGISTER instead.$b")))
  859. } else {
  860. nsNotice(rb, ircfmt.Unescape(client.t("$bWarning: unregistering this account will remove its stored privileges.$b")))
  861. }
  862. nsNotice(rb, fmt.Sprintf(client.t("To confirm, run this command: %s"), fmt.Sprintf("/NS %s %s %s", strings.ToUpper(command), accountName, expectedCode)))
  863. return
  864. }
  865. err := server.accounts.Unregister(accountName, erase)
  866. if err == errAccountDoesNotExist {
  867. nsNotice(rb, client.t(err.Error()))
  868. } else if err != nil {
  869. nsNotice(rb, client.t("Error while unregistering account"))
  870. } else {
  871. nsNotice(rb, fmt.Sprintf(client.t("Successfully unregistered account %s"), accountName))
  872. server.logger.Info("accounts", "client", client.Nick(), "unregistered account", accountName)
  873. 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))
  874. }
  875. }
  876. func nsVerifyHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  877. username, code := params[0], params[1]
  878. err := server.accounts.Verify(client, username, code)
  879. var errorMessage string
  880. if err != nil {
  881. switch err {
  882. case errAccountAlreadyLoggedIn, errAccountVerificationInvalidCode, errAccountAlreadyVerified:
  883. errorMessage = err.Error()
  884. default:
  885. errorMessage = errAccountVerificationFailed.Error()
  886. }
  887. }
  888. if errorMessage != "" {
  889. nsNotice(rb, client.t(errorMessage))
  890. return
  891. }
  892. if fixupNickEqualsAccount(client, rb, server.Config()) {
  893. sendSuccessfulRegResponse(client, rb, true)
  894. }
  895. }
  896. func nsPasswdHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  897. var target string
  898. var newPassword string
  899. var errorMessage string
  900. hasPrivs := client.HasRoleCapabs("accreg")
  901. switch len(params) {
  902. case 2:
  903. if !hasPrivs {
  904. errorMessage = `Insufficient privileges`
  905. } else {
  906. target, newPassword = params[0], params[1]
  907. if newPassword == "*" {
  908. newPassword = ""
  909. }
  910. }
  911. case 3:
  912. target = client.Account()
  913. if target == "" {
  914. errorMessage = `You're not logged into an account`
  915. } else if params[1] != params[2] {
  916. errorMessage = `Passwords do not match`
  917. } else {
  918. if !nsLoginThrottleCheck(client, rb) {
  919. return
  920. }
  921. accountData, err := server.accounts.LoadAccount(target)
  922. if err != nil {
  923. errorMessage = `You're not logged into an account`
  924. } else {
  925. hash := accountData.Credentials.PassphraseHash
  926. if hash != nil && passwd.CompareHashAndPassword(hash, []byte(params[0])) != nil {
  927. errorMessage = `Password incorrect`
  928. } else {
  929. newPassword = params[1]
  930. if newPassword == "*" {
  931. newPassword = ""
  932. }
  933. }
  934. }
  935. }
  936. default:
  937. errorMessage = `Invalid parameters`
  938. }
  939. if errorMessage != "" {
  940. nsNotice(rb, client.t(errorMessage))
  941. return
  942. }
  943. err := server.accounts.setPassword(target, newPassword, hasPrivs)
  944. switch err {
  945. case nil:
  946. nsNotice(rb, client.t("Password changed"))
  947. case errEmptyCredentials:
  948. nsNotice(rb, client.t("You can't delete your password unless you add a certificate fingerprint"))
  949. case errCredsExternallyManaged:
  950. nsNotice(rb, client.t("Your account credentials are managed externally and cannot be changed here"))
  951. case errCASFailed:
  952. nsNotice(rb, client.t("Try again later"))
  953. default:
  954. server.logger.Error("internal", "could not upgrade user password:", err.Error())
  955. nsNotice(rb, client.t("Password could not be changed due to server error"))
  956. }
  957. }
  958. func nsEnforceHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  959. newParams := []string{"enforce"}
  960. if len(params) == 0 {
  961. nsGetHandler(server, client, "get", newParams, rb)
  962. } else {
  963. newParams = append(newParams, params[0])
  964. nsSetHandler(server, client, "set", newParams, rb)
  965. }
  966. }
  967. func nsClientsHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  968. var verb string
  969. if command == "sessions" {
  970. // Legacy "SESSIONS" command is an alias for CLIENTS LIST.
  971. verb = "list"
  972. } else if len(params) > 0 {
  973. verb = strings.ToLower(params[0])
  974. params = params[1:]
  975. }
  976. switch verb {
  977. case "list":
  978. nsClientsListHandler(server, client, params, rb)
  979. case "logout":
  980. nsClientsLogoutHandler(server, client, params, rb)
  981. default:
  982. nsNotice(rb, client.t("Invalid parameters"))
  983. }
  984. }
  985. func nsClientsListHandler(server *Server, client *Client, params []string, rb *ResponseBuffer) {
  986. target := client
  987. hasPrivs := client.HasRoleCapabs("local_ban")
  988. if 0 < len(params) {
  989. target = server.clients.Get(params[0])
  990. if target == nil {
  991. nsNotice(rb, client.t("No such nick"))
  992. return
  993. }
  994. if target != client && !hasPrivs {
  995. nsNotice(rb, client.t("Command restricted"))
  996. return
  997. }
  998. }
  999. sessionData, currentIndex := target.AllSessionData(rb.session, hasPrivs)
  1000. nsNotice(rb, fmt.Sprintf(client.t("Nickname %[1]s has %[2]d attached clients(s)"), target.Nick(), len(sessionData)))
  1001. for i, session := range sessionData {
  1002. if currentIndex == i {
  1003. nsNotice(rb, fmt.Sprintf(client.t("Client %d (currently attached client):"), session.sessionID))
  1004. } else {
  1005. nsNotice(rb, fmt.Sprintf(client.t("Client %d:"), session.sessionID))
  1006. }
  1007. if session.deviceID != "" {
  1008. nsNotice(rb, fmt.Sprintf(client.t("Device ID: %s"), session.deviceID))
  1009. }
  1010. nsNotice(rb, fmt.Sprintf(client.t("IP address: %s"), session.ip.String()))
  1011. nsNotice(rb, fmt.Sprintf(client.t("Hostname: %s"), session.hostname))
  1012. if hasPrivs {
  1013. nsNotice(rb, fmt.Sprintf(client.t("Connection: %s"), session.connInfo))
  1014. }
  1015. nsNotice(rb, fmt.Sprintf(client.t("Created at: %s"), session.ctime.Format(time.RFC1123)))
  1016. nsNotice(rb, fmt.Sprintf(client.t("Last active: %s"), session.atime.Format(time.RFC1123)))
  1017. if session.certfp != "" {
  1018. nsNotice(rb, fmt.Sprintf(client.t("Certfp: %s"), session.certfp))
  1019. }
  1020. }
  1021. }
  1022. func nsClientsLogoutHandler(server *Server, client *Client, params []string, rb *ResponseBuffer) {
  1023. if len(params) < 1 {
  1024. nsNotice(rb, client.t("Missing client ID to logout (or \"all\")"))
  1025. return
  1026. }
  1027. target := client
  1028. if len(params) >= 2 {
  1029. // CLIENTS LOGOUT [nickname] [client ID]
  1030. target = server.clients.Get(params[0])
  1031. if target == nil {
  1032. nsNotice(rb, client.t("No such nick"))
  1033. return
  1034. }
  1035. // User must have "local_kill" privileges to logout other user sessions.
  1036. if target != client {
  1037. oper := client.Oper()
  1038. if oper == nil || !oper.Class.Capabilities.Has("local_kill") {
  1039. nsNotice(rb, client.t("Insufficient oper privs"))
  1040. return
  1041. }
  1042. }
  1043. params = params[1:]
  1044. }
  1045. var sessionToDestroy *Session // target.destroy(nil) will logout all sessions
  1046. if strings.ToLower(params[0]) != "all" {
  1047. sessionID, err := strconv.ParseInt(params[0], 10, 64)
  1048. if err != nil {
  1049. nsNotice(rb, client.t("Client ID to logout should be an integer (or \"all\")"))
  1050. return
  1051. }
  1052. // Find the client ID that the user requested to logout.
  1053. sessions := target.Sessions()
  1054. for _, session := range sessions {
  1055. if session.sessionID == sessionID {
  1056. sessionToDestroy = session
  1057. }
  1058. }
  1059. if sessionToDestroy == nil {
  1060. nsNotice(rb, client.t("Specified client ID does not exist"))
  1061. return
  1062. }
  1063. }
  1064. target.destroy(sessionToDestroy)
  1065. if (sessionToDestroy != nil && rb.session != sessionToDestroy) || client != target {
  1066. if sessionToDestroy != nil {
  1067. nsNotice(rb, client.t("Successfully logged out session"))
  1068. } else {
  1069. nsNotice(rb, client.t("Successfully logged out all sessions"))
  1070. }
  1071. }
  1072. }
  1073. func nsCertHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  1074. verb := strings.ToLower(params[0])
  1075. params = params[1:]
  1076. var target, certfp string
  1077. switch verb {
  1078. case "list":
  1079. if 1 <= len(params) {
  1080. target = params[0]
  1081. }
  1082. case "add", "del":
  1083. if 2 <= len(params) {
  1084. target, certfp = params[0], params[1]
  1085. } else if len(params) == 1 {
  1086. certfp = params[0]
  1087. } else if len(params) == 0 && verb == "add" && rb.session.certfp != "" {
  1088. certfp = rb.session.certfp // #1059
  1089. } else {
  1090. nsNotice(rb, client.t("Invalid parameters"))
  1091. return
  1092. }
  1093. default:
  1094. nsNotice(rb, client.t("Invalid parameters"))
  1095. return
  1096. }
  1097. hasPrivs := client.HasRoleCapabs("accreg")
  1098. if target != "" && !hasPrivs {
  1099. nsNotice(rb, client.t("Insufficient privileges"))
  1100. return
  1101. } else if target == "" {
  1102. target = client.Account()
  1103. if target == "" {
  1104. nsNotice(rb, client.t("You're not logged into an account"))
  1105. return
  1106. }
  1107. }
  1108. var err error
  1109. switch verb {
  1110. case "list":
  1111. accountData, err := server.accounts.LoadAccount(target)
  1112. if err == errAccountDoesNotExist {
  1113. nsNotice(rb, client.t("Account does not exist"))
  1114. return
  1115. } else if err != nil {
  1116. nsNotice(rb, client.t("An error occurred"))
  1117. return
  1118. }
  1119. certfps := accountData.Credentials.Certfps
  1120. nsNotice(rb, fmt.Sprintf(client.t("There are %[1]d certificate fingerprint(s) authorized for account %[2]s."), len(certfps), accountData.Name))
  1121. for i, certfp := range certfps {
  1122. nsNotice(rb, fmt.Sprintf("%d: %s", i+1, certfp))
  1123. }
  1124. return
  1125. case "add":
  1126. err = server.accounts.addRemoveCertfp(target, certfp, true, hasPrivs)
  1127. case "del":
  1128. err = server.accounts.addRemoveCertfp(target, certfp, false, hasPrivs)
  1129. }
  1130. switch err {
  1131. case nil:
  1132. if verb == "add" {
  1133. nsNotice(rb, client.t("Certificate fingerprint successfully added"))
  1134. } else {
  1135. nsNotice(rb, client.t("Certificate fingerprint successfully removed"))
  1136. }
  1137. case errNoop:
  1138. if verb == "add" {
  1139. nsNotice(rb, client.t("That certificate fingerprint was already authorized"))
  1140. } else {
  1141. nsNotice(rb, client.t("Certificate fingerprint not found"))
  1142. }
  1143. case errAccountDoesNotExist:
  1144. nsNotice(rb, client.t("Account does not exist"))
  1145. case errLimitExceeded:
  1146. nsNotice(rb, client.t("You already have too many certificate fingerprints"))
  1147. case utils.ErrInvalidCertfp:
  1148. nsNotice(rb, client.t("Invalid certificate fingerprint"))
  1149. case errCertfpAlreadyExists:
  1150. nsNotice(rb, client.t("That certificate fingerprint is already associated with another account"))
  1151. case errEmptyCredentials:
  1152. nsNotice(rb, client.t("You can't remove all your certificate fingerprints unless you add a password"))
  1153. case errCredsExternallyManaged:
  1154. nsNotice(rb, client.t("Your account credentials are managed externally and cannot be changed here"))
  1155. case errCASFailed:
  1156. nsNotice(rb, client.t("Try again later"))
  1157. default:
  1158. server.logger.Error("internal", "could not modify certificates:", err.Error())
  1159. nsNotice(rb, client.t("An error occurred"))
  1160. }
  1161. }
  1162. func nsSuspendHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  1163. err := server.accounts.Suspend(params[0])
  1164. switch err {
  1165. case nil:
  1166. nsNotice(rb, fmt.Sprintf(client.t("Successfully suspended account %s"), params[0]))
  1167. case errAccountDoesNotExist:
  1168. nsNotice(rb, client.t("No such account"))
  1169. default:
  1170. nsNotice(rb, client.t("An error occurred"))
  1171. }
  1172. }
  1173. func nsUnsuspendHandler(server *Server, client *Client, command string, params []string, rb *ResponseBuffer) {
  1174. err := server.accounts.Unsuspend(params[0])
  1175. switch err {
  1176. case nil:
  1177. nsNotice(rb, fmt.Sprintf(client.t("Successfully un-suspended account %s"), params[0]))
  1178. case errAccountDoesNotExist:
  1179. nsNotice(rb, client.t("No such account"))
  1180. default:
  1181. nsNotice(rb, client.t("An error occurred"))
  1182. }
  1183. }