Advertisement
Guest User

Untitled

a guest
Jun 26th, 2017
90
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 65.54 KB | None | 0 0
  1. -- TODO
  2. -- Fix error types
  3.  
  4. local base = _G
  5.  
  6. module("access")
  7.  
  8. base.require("luadchpp")
  9.  
  10. local adchpp = base.luadchpp
  11. local string = base.require('string')
  12.  
  13. -- Where to read/write user database
  14. local users_file = adchpp.Util_getCfgPath() .. "users.txt"
  15.  
  16. -- Where to read/write settings
  17. local settings_file = adchpp.Util_getCfgPath() .. "settings.txt"
  18.  
  19. -- Where to read/write ban database
  20. local bans_file = adchpp.Util_getCfgPath() .. "bans.txt"
  21.  
  22. -- Users with a level equal to or above the one specified here are operators
  23. level_op = 2
  24.  
  25. -- ADC extensions this script adds support for
  26. local extensions = { "PING" }
  27.  
  28. -- Regexes for the various fields.
  29. local cid_regex = "^" .. string.rep("[A-Z2-7]", 39) .. "$" -- No way of expressing exactly 39 chars without being explicit it seems
  30. local pid_regex = cid_regex
  31. local sid_regex = "^" .. string.rep("[A-Z2-7]", 4) .. "$"
  32. local integer_regex = "^%d+$"
  33.  
  34. local inf_fields = {
  35. ["ID"] = cid_regex,
  36. ["PD"] = pid_regex,
  37. ["I4"] = "^%d+%.%d+%.%d+%.%d+$",
  38. ["I6"] = "^[%x:]+$", -- This could probably be made better...
  39. ["U4"] = integer_regex,
  40. ["U6"] = integer_regex,
  41. ["SS"] = integer_regex,
  42. ["SF"] = integer_regex,
  43. ["US"] = integer_regex,
  44. ["DS"] = integer_regex,
  45. ["SL"] = integer_regex,
  46. ["AS"] = integer_regex,
  47. ["AM"] = integer_regex,
  48. ["NI"] = "^[%S]+$", -- Wonder what this does to 8-bit codes...
  49. ["HN"] = integer_regex,
  50. ["HR"] = integer_regex,
  51. ["HO"] = integer_regex,
  52. ["CT"] = integer_regex,
  53. ["AW"] = "^[12]$",
  54. ["SU"] = "[A-Z,]+"
  55. }
  56.  
  57. local context_hub = "[H]"
  58. local context_bcast = "[BF]"
  59. local context_direct = "[DE]"
  60. local context_send = "[BDEFH]"
  61. local context_hubdirect = "[HDE]"
  62.  
  63. local command_contexts = {
  64. [adchpp.AdcCommand_CMD_STA] = context_hubdirect,
  65. [adchpp.AdcCommand_CMD_SUP] = context_hub,
  66. [adchpp.AdcCommand_CMD_SID] = context_hub,
  67. [adchpp.AdcCommand_CMD_INF] = context_bcast,
  68. [adchpp.AdcCommand_CMD_MSG] = context_send,
  69. [adchpp.AdcCommand_CMD_SCH] = context_send,
  70. [adchpp.AdcCommand_CMD_RES] = context_direct,
  71. [adchpp.AdcCommand_CMD_CTM] = context_direct,
  72. [adchpp.AdcCommand_CMD_RCM] = context_direct,
  73. [adchpp.AdcCommand_CMD_GPA] = context_hub,
  74. [adchpp.AdcCommand_CMD_PAS] = context_hub,
  75. [adchpp.AdcCommand_CMD_QUI] = context_hub,
  76. [adchpp.AdcCommand_CMD_GET] = context_hub,
  77. [adchpp.AdcCommand_CMD_GFI] = context_hub,
  78. [adchpp.AdcCommand_CMD_SND] = context_hub,
  79. }
  80.  
  81. local io = base.require('io')
  82. local os = base.require('os')
  83. local json = base.require('json')
  84. local autil = base.require('autil')
  85. local table = base.require('table')
  86. local math = base.require('math')
  87.  
  88. local start_time = os.time()
  89.  
  90. users = {}
  91. users.nicks = {}
  92. users.cids = {}
  93.  
  94. local bans = {}
  95. bans.cids = {}
  96. bans.ips = {}
  97. bans.nicks = {}
  98. bans.nicksre = {}
  99. bans.msgsre = {}
  100. bans.muted = {}
  101.  
  102. local stats = {}
  103. local dispatch_stats = false
  104.  
  105. -- cache for +cfg min*level
  106. local restricted_commands = {}
  107.  
  108. local cm = adchpp.getCM()
  109. local lm = adchpp.getLM()
  110. local pm = adchpp.getPM()
  111. local sm = adchpp.getSM()
  112.  
  113. local saltsHandle = pm:registerByteVectorData()
  114.  
  115. -- forward declarations.
  116. local format_seconds, cut_str,
  117. send_user_commands, remove_user_commands,
  118. verify_info
  119.  
  120. -- Settings loaded and saved by the main script. Possible fields each setting can contain:
  121. -- * alias: other names that can also be used to reach this setting.
  122. -- * change: function called when the value has changed.
  123. -- * help: information about this setting, displayed in +help cfg.
  124. -- * value: the value of this setting, either a number or a string. [compulsory]
  125. -- * validate: function(string) called before changing the value; may return an error string.
  126. settings = {}
  127.  
  128. -- List of +commands handled by the main script. Possible fields each command can contain:
  129. -- * alias: other names that can also trigger this command.
  130. -- * command: function(Client c, string parameters). [compulsory]
  131. -- * help: information about this command, displayed in +help.
  132. -- * helplong: detailed information about this command, displayed in +help command-name.
  133. -- * protected: function(Client c) returning whether the command is to be shown in +help.
  134. -- * user_command: table containing information about the user command which will refer to this
  135. -- command. Possible fields each user_command table can contain:
  136. -- ** hub_params: list of arguments to be passed to this command for hub menus.
  137. -- ** name: name of the user command (defaults to capitalized command name).
  138. -- ** params: list of arguments to be passed to this command for all menus.
  139. -- ** user_params: list of arguments to be passed to this command for user menus.
  140. commands = {}
  141.  
  142. local function log(message)
  143. lm:log(_NAME, message)
  144. end
  145.  
  146. local function description_change()
  147. local description = settings.topic.value
  148. if #settings.topic.value == 0 then
  149. description = settings.description.value
  150. end
  151. cm:getEntity(adchpp.AdcCommand_HUB_SID):setField("DE", description)
  152. cm:sendToAll(adchpp.AdcCommand(adchpp.AdcCommand_CMD_INF, adchpp.AdcCommand_TYPE_INFO, adchpp.AdcCommand_HUB_SID):addParam("DE", description):getBuffer())
  153. end
  154.  
  155. local function validate_ni(new)
  156. for _, char in base.ipairs({ string.byte(new, 1, #new) }) do
  157. if char <= 32 then
  158. return "the name can't contain any space nor new line"
  159. end
  160. end
  161. end
  162.  
  163. local function validate_de(new)
  164. for _, char in base.ipairs({ string.byte(new, 1, #new) }) do
  165. if char < 32 then
  166. return "the description can't contain any new line nor tabulation"
  167. end
  168. end
  169. end
  170.  
  171. local function recheck_info()
  172. local entities = cm:getEntities()
  173. local size = entities:size()
  174. if size > 0 then
  175. for i = 0, size - 1 do
  176. local c = entities[i]:asClient()
  177. if c then
  178. verify_info(c)
  179. end
  180. end
  181. end
  182. end
  183.  
  184. settings.address = {
  185. alias = { host = true, dns = true },
  186.  
  187. help = "host address (DNS or IP)",
  188.  
  189. value = adchpp.Util_getLocalIp()
  190. }
  191.  
  192. settings.allownickchange = {
  193. help = "authorize regged users to connect with a different nick, 1 = allow, 0 = disallow",
  194.  
  195. value = 1
  196. }
  197.  
  198. settings.allowreg = {
  199. alias = { allowregistration = true },
  200.  
  201. help = "authorize un-regged users to register themselves with +mypass (otherwise, they'll have to ask an operator), 1 = alllow, 0 = disallow",
  202.  
  203. value = 1
  204. }
  205.  
  206. settings.botcid = {
  207. alias = { botid = true },
  208.  
  209. help = "CID of the bot, restart the hub after the change",
  210.  
  211. value = adchpp.CID_generate():toBase32(),
  212.  
  213. validate = function(new)
  214. if adchpp.CID(new):isZero() then
  215. return "the CID must be a valid 39-byte base32 representation"
  216. end
  217. end
  218. }
  219.  
  220. settings.botname = {
  221. alias = { botnick = true, botni = true },
  222.  
  223. change = function()
  224. if bot then
  225. bot:setField("NI", settings.botname.value)
  226. cm:sendToAll(adchpp.AdcCommand(adchpp.AdcCommand_CMD_INF, adchpp.AdcCommand_TYPE_BROADCAST, bot:getSID()):addParam("NI", settings.botname.value):getBuffer())
  227. end
  228. end,
  229.  
  230. help = "name of the hub bot",
  231.  
  232. value = "Bot",
  233.  
  234. validate = validate_ni
  235. }
  236.  
  237. settings.botdescription = {
  238. alias = { botdescr = true, botde = true },
  239.  
  240. change = function()
  241. if bot then
  242. bot:setField("DE", settings.botdescription.value)
  243. cm:sendToAll(adchpp.AdcCommand(adchpp.AdcCommand_CMD_INF, adchpp.AdcCommand_TYPE_BROADCAST, bot:getSID()):addParam("DE", settings.botdescription.value):getBuffer())
  244. end
  245. end,
  246.  
  247. help = "description of the hub bot",
  248.  
  249. value = "",
  250.  
  251. validate = validate_de
  252. }
  253.  
  254. settings.botemail = {
  255. alias = { botmail = true, botem = true },
  256.  
  257. change = function()
  258. if bot then
  259. bot:setField("EM", settings.botemail.value)
  260. cm:sendToAll(adchpp.AdcCommand(adchpp.AdcCommand_CMD_INF, adchpp.AdcCommand_TYPE_BROADCAST, bot:getSID()):addParam("EM", settings.botemail.value):getBuffer())
  261. end
  262. end,
  263.  
  264. help = "e-mail of the hub bot",
  265.  
  266. value = ""
  267. }
  268.  
  269. settings.description = {
  270. alias = { hubdescription = true },
  271.  
  272. change = description_change,
  273.  
  274. help = "hub description",
  275.  
  276. value = cm:getEntity(adchpp.AdcCommand_HUB_SID):getField("DE"),
  277.  
  278. validate = validate_de
  279. }
  280.  
  281. settings.maxmsglength = {
  282. alias = { maxmessagelength = true },
  283.  
  284. help = "maximum number of characters allowed per chat message, 0 = no limit",
  285.  
  286. value = 0
  287. }
  288.  
  289. settings.maxnicklength = {
  290. change = recheck_info,
  291.  
  292. help = "maximum number of characters allowed per nick, 0 = no limit",
  293.  
  294. value = 50
  295. }
  296.  
  297. settings.maxusers = {
  298. alias = { max_users = true, user_max = true, users_max = true, usermax = true, usersmax = true },
  299.  
  300. help = "maximum number of non-registered users, -1 = no limit, 0 = no unregistered users allowed",
  301.  
  302. value = -1
  303. }
  304.  
  305. settings.menuname = {
  306. alias = { ucmdname = true },
  307.  
  308. help = "title of the main user command menu sent to clients",
  309.  
  310. value = "ADCH++"
  311. }
  312.  
  313. settings.minchatlevel = {
  314. change = function()
  315. restricted_commands[adchpp.AdcCommand_CMD_MSG] = { level = settings.minchatlevel.value, str = "chat" }
  316. end,
  317.  
  318. help = "minimum level to chat - hub restart recommended",
  319.  
  320. value = 0
  321. }
  322.  
  323. settings.mindownloadlevel = {
  324. alias = { mindllevel = true, mintransferlevel = true },
  325.  
  326. change = function()
  327. restricted_commands[adchpp.AdcCommand_CMD_CTM] = { level = settings.mindownloadlevel.value, str = "download" }
  328. restricted_commands[adchpp.AdcCommand_CMD_RCM] = { level = settings.mindownloadlevel.value, str = "download" }
  329. end,
  330.  
  331. help = "minimum level to download - hub restart recommended",
  332.  
  333. value = 0
  334. }
  335.  
  336. settings.minsearchlevel = {
  337. change = function()
  338. restricted_commands[adchpp.AdcCommand_CMD_SCH] = { level = settings.minsearchlevel.value, str = "search" }
  339. restricted_commands[adchpp.AdcCommand_CMD_RES] = { level = settings.minsearchlevel.value, str = "send search results" }
  340. end,
  341.  
  342. help = "minimum level to search - hub restart recommended",
  343.  
  344. value = 0
  345. }
  346.  
  347. settings.name = {
  348. alias = { hubname = true },
  349.  
  350. change = function()
  351. cm:getEntity(adchpp.AdcCommand_HUB_SID):setField("NI", settings.name.value)
  352. cm:sendToAll(adchpp.AdcCommand(adchpp.AdcCommand_CMD_INF, adchpp.AdcCommand_TYPE_INFO, adchpp.AdcCommand_HUB_SID):addParam("NI", settings.name.value):getBuffer())
  353. end,
  354.  
  355. help = "hub name",
  356.  
  357. value = cm:getEntity(adchpp.AdcCommand_HUB_SID):getField("NI"),
  358.  
  359. validate = validate_ni
  360. }
  361.  
  362. settings.network = {
  363. value = ""
  364. }
  365.  
  366. settings.owner = {
  367. alias = { ownername = true },
  368.  
  369. help = "owner name",
  370.  
  371. value = ""
  372. }
  373.  
  374. settings.passinlist = {
  375. help = "show passwords of users with a lower level in +listregs, 1 = show, 0 = don't show",
  376.  
  377. value = 1
  378. }
  379.  
  380. settings.minsharesize = {
  381. alias = { minss = true },
  382.  
  383. change = recheck_info,
  384.  
  385. help = "minimum share size allowed in bytes, 0 = disabled",
  386.  
  387. value = 0
  388. }
  389.  
  390. settings.minslotsize = {
  391. alias = { minsl = true },
  392.  
  393. change = recheck_info,
  394.  
  395. help = "minimum number of opened upload slots required, 0 = disabled",
  396.  
  397. value = 0
  398. }
  399.  
  400. settings.minhubslotratio = {
  401. alias = { minhsr = true },
  402.  
  403. change = recheck_info,
  404.  
  405. help = "minimum hub/slot ratio required, 0 = disabled",
  406.  
  407. value = 0
  408. }
  409.  
  410. settings.maxsharesize = {
  411. alias = { maxss = true },
  412.  
  413. change = recheck_info,
  414.  
  415. help = "maximum share size allowed in bytes, 0 = disabled",
  416.  
  417. value = 0
  418. }
  419.  
  420. settings.maxslotsize = {
  421. alias = { maxsl = true },
  422.  
  423. change = recheck_info,
  424.  
  425. help = "maximum number of opened upload slots allowed, 0 = disabled",
  426.  
  427. value = 0
  428. }
  429.  
  430. settings.maxhubslotratio = {
  431. alias = { maxhsr = true },
  432.  
  433. change = recheck_info,
  434.  
  435. help = "maximum hub/slot ratio allowed, 0 = disabled",
  436.  
  437. value = 0
  438. }
  439.  
  440. settings.maxhubscount = {
  441. alias = { maxhubs = true },
  442.  
  443. change = recheck_info,
  444.  
  445. help = "maximum number of connected hubs allowed, 0 = disabled",
  446.  
  447. value = 0
  448. }
  449.  
  450. settings.sendversion = {
  451. alias = { displayversion = true },
  452.  
  453. help = "send hub version information at login, 1 = alllow, 0 = disallow",
  454.  
  455. value = 1
  456. }
  457.  
  458. settings.topic = {
  459. alias = { hubtopic = true },
  460.  
  461. change = description_change,
  462.  
  463. help = "hub topic: if set, overrides the description for normal users; the description is then only for use by hub-lists",
  464.  
  465. value = "",
  466.  
  467. validate = validate_de
  468. }
  469.  
  470. settings.website = {
  471. alias = { url = true },
  472.  
  473. value = ""
  474. }
  475.  
  476. function registered_users()
  477. local ret = {}
  478. local nicksdone = {}
  479.  
  480. for _, user in base.pairs(users.cids) do
  481. table.insert(ret, user)
  482. if user.nick then
  483. nicksdone[user] = 1
  484. end
  485. end
  486.  
  487. for _, user in base.pairs(users.nicks) do
  488. if not nicksdone[user] then
  489. table.insert(ret, user)
  490. end
  491. end
  492.  
  493. return ret
  494. end
  495.  
  496. local function load_users()
  497. users.cids = {}
  498. users.nicks = {}
  499.  
  500. local file = io.open(users_file, "r")
  501. if not file then
  502. log("Unable to open " .. users_file .. ", users not loaded")
  503. return
  504. end
  505.  
  506. local str = file:read("*a")
  507. file:close()
  508.  
  509. if #str == 0 then
  510. return
  511. end
  512.  
  513. local userok, userlist = base.pcall(json.decode, str)
  514. if not userok then
  515. log("Unable to decode users file: " .. userlist)
  516. return
  517. end
  518.  
  519. for _, user in base.pairs(userlist) do
  520. if user.cid then
  521. users.cids[user.cid] = user
  522. end
  523. if user.nick then
  524. users.nicks[user.nick] = user
  525. end
  526. end
  527. end
  528.  
  529. local function save_users()
  530. local file = io.open(users_file, "w")
  531. if not file then
  532. log("Unable to open " .. users_file .. ", users not saved")
  533. return
  534. end
  535.  
  536. file:write(json.encode(registered_users()))
  537. file:close()
  538. end
  539.  
  540. local function load_settings()
  541. local file = io.open(settings_file, "r")
  542. if not file then
  543. log("Unable to open " .. settings_file .. ", settings not loaded")
  544. return false
  545. end
  546.  
  547. local str = file:read("*a")
  548. file:close()
  549.  
  550. if #str == 0 then
  551. return false
  552. end
  553.  
  554. local ok, list = base.pcall(json.decode, str)
  555. if not ok then
  556. log("Unable to decode settings file: " .. list)
  557. return false
  558. end
  559.  
  560. for k, v in base.pairs(list) do
  561. if settings[k] then
  562. local change = settings[k].value ~= v
  563. settings[k].value = v
  564. if change and settings[k].change then
  565. settings[k].change()
  566. end
  567. end
  568. end
  569.  
  570. return true
  571. end
  572.  
  573. local function save_settings()
  574. local file = io.open(settings_file, "w")
  575. if not file then
  576. log("Unable to open " .. settings_file .. ", settings not saved")
  577. return
  578. end
  579.  
  580. local list = {}
  581. for k, v in base.pairs(settings) do
  582. list[k] = v.value
  583. end
  584. file:write(json.encode(list))
  585. file:close()
  586. end
  587.  
  588. local function load_bans()
  589. bans = {}
  590. bans.cids = {}
  591. bans.ips = {}
  592. bans.nicks = {}
  593. bans.nicksre = {}
  594. bans.msgsre = {}
  595. bans.muted = {}
  596.  
  597. local file = io.open(bans_file, "r")
  598. if not file then
  599. log("Unable to open " .. bans_file .. ", bans not loaded")
  600. return
  601. end
  602.  
  603. local str = file:read("*a")
  604. file:close()
  605.  
  606. if #str == 0 then
  607. return
  608. end
  609.  
  610. local ok, list = base.pcall(json.decode, str)
  611. if not ok then
  612. log("Unable to decode bans file: " .. list)
  613. return
  614. end
  615.  
  616. bans = list
  617. if not bans.cids then
  618. bans.cids = {}
  619. end
  620. if not bans.ips then
  621. bans.ips = {}
  622. end
  623. if not bans.nicks then
  624. bans.nicks = {}
  625. end
  626. if not bans.nicksre then
  627. bans.nicksre = {}
  628. end
  629. if not bans.msgsre then
  630. bans.msgsre = {}
  631. end
  632. if not bans.muted then
  633. bans.muted = {}
  634. end
  635.  
  636. clear_expired_bans()
  637. end
  638.  
  639. local function save_bans()
  640. local file = io.open(bans_file, "w")
  641. if not file then
  642. log("Unable to open " .. bans_file .. ", bans not saved")
  643. return
  644. end
  645.  
  646. file:write(json.encode(bans))
  647. file:close()
  648. end
  649.  
  650. local function add_stats(stat)
  651. if stats[stat] then
  652. stats[stat] = stats[stat] + 1
  653. else
  654. stats[stat] = 1
  655. end
  656. end
  657.  
  658. local function make_user(cid, nick, password, level)
  659. local user = { cid = cid, nick = nick, password = password, level = level }
  660. return user
  661. end
  662.  
  663. local function check_max_users()
  664. if settings.maxusers.value == -1 then
  665. return
  666. end
  667.  
  668. if settings.maxusers.value == 0 then
  669. return adchpp.AdcCommand_ERROR_REGGED_ONLY, "Only registered users are allowed in here"
  670. end
  671.  
  672. local count = cm:getEntities():size()
  673. if count >= settings.maxusers.value then
  674. return adchpp.AdcCommand_ERROR_HUB_FULL, "Hub full, please try again later"
  675. end
  676. return
  677. end
  678.  
  679. local function get_user(cid, nick)
  680. local user
  681.  
  682. if cid then
  683. user = users.cids[cid]
  684. end
  685.  
  686. if not user and nick then
  687. user = users.nicks[nick]
  688. end
  689.  
  690. return user
  691. end
  692.  
  693. local function get_user_c(c)
  694. return get_user(c:getCID():toBase32(), c:getField("NI"))
  695. end
  696.  
  697. function get_level(c)
  698. local user = get_user_c(c)
  699. if not user then
  700. return 0
  701. end
  702.  
  703. return user.level
  704. end
  705.  
  706. function has_level(c, level)
  707. return get_level(c) >= level
  708. end
  709.  
  710. function is_op(c)
  711. return has_level(c, level_op)
  712. end
  713.  
  714. local function update_user(user, cid, nick)
  715. -- only one of nick and cid may be updated...
  716.  
  717. if user.nick ~= nick then
  718. if settings.allownickchange.value == 0 then
  719. return false, "This hub doesn't allow registered users to change their nick; ask an operator to delete your current registration data if you really want a new nick. Please connect again with your current registered nick: " .. user.nick
  720. end
  721.  
  722. if users.nicks[nick] then
  723. -- new nick taken...
  724. return false, "Nick taken by another registered user"
  725. end
  726.  
  727. if user.nick then
  728. users.nicks[user.nick] = nil
  729. end
  730.  
  731. user.nick = nick
  732. users.nicks[user.nick] = user
  733. base.pcall(save_users)
  734. return true, "Registration data updated (new nick)"
  735. end
  736.  
  737. if user.cid ~= cid then
  738. if users.cids[cid] then
  739. -- new cid taken...
  740. return false, "CID taken by another registered user"
  741. end
  742.  
  743. if user.cid then
  744. users.cids[user.cid] = nil
  745. end
  746.  
  747. user.cid = cid
  748. users.cids[user.cid] = user
  749. base.pcall(save_users)
  750. return true, "Registration data updated (new CID)"
  751. end
  752.  
  753. return true
  754. end
  755.  
  756. function register_user(cid, nick, password, level)
  757. local user = make_user(cid, nick, password, level)
  758. if nick then
  759. users.nicks[nick] = user
  760. end
  761. if cid then
  762. users.cids[cid] = user
  763. end
  764.  
  765. base.pcall(save_users)
  766. end
  767.  
  768. local function make_ban(level, reason, minutes)
  769. local ban = { level = level }
  770. if string.len(reason) > 0 then
  771. ban.reason = reason
  772. end
  773. if minutes and string.len(minutes) > 0 then
  774. ban.expires = os.time() + minutes * 60
  775. end
  776. return ban
  777. end
  778.  
  779. local function ban_expiration_diff(ban)
  780. return os.difftime(ban.expires, os.time())
  781. end
  782.  
  783. local function clear_expired_bans()
  784. local save = false
  785.  
  786. for _, ban_array in base.pairs(bans) do
  787. for k, ban in base.pairs(ban_array) do
  788. if ban.expires and ban_expiration_diff(ban) <= 0 then
  789. ban_array[k] = nil
  790. save = true
  791. end
  792. end
  793. end
  794.  
  795. if save then
  796. base.pcall(save_bans)
  797. end
  798. end
  799.  
  800. local function ban_expiration_string(ban)
  801. if ban.expires then
  802. local diff = ban_expiration_diff(ban)
  803. if diff > 0 then
  804. return "in " .. format_seconds(diff)
  805. else
  806. return "expired"
  807. end
  808. else
  809. return "never"
  810. end
  811. end
  812.  
  813. local function ban_info_string(ban)
  814. local str = "\tLevel: " .. ban.level
  815. if ban.reason then
  816. str = str .. "\tReason: " .. ban.reason
  817. end
  818. str = str .. "\tExpires: " .. ban_expiration_string(ban)
  819. return str
  820. end
  821.  
  822. local function ban_return_info(ban)
  823. local str = " (expires: " .. ban_expiration_string(ban) .. ")"
  824. if ban.reason then
  825. str = str .. " (reason: " .. ban.reason .. ")"
  826. end
  827. return str
  828. end
  829.  
  830. local function dump_banned(c, ban)
  831. local str = "You are banned" .. ban_return_info(ban)
  832.  
  833. autil.dump(c, adchpp.AdcCommand_ERROR_BANNED_GENERIC, function(cmd)
  834. cmd:addParam("MS" .. str)
  835.  
  836. local expires
  837. if ban.expires then
  838. expires = ban_expiration_diff(ban)
  839. else
  840. expires = -1
  841. end
  842. cmd:addParam("TL" .. base.tostring(expires))
  843. end)
  844. end
  845.  
  846. local function get_ucmd_name(k, v)
  847. if v.user_command and v.user_command.name then
  848. return v.user_command.name
  849. else
  850. return string.upper(string.sub(k, 1, 1)) .. string.sub(k, 2)
  851. end
  852. end
  853.  
  854. send_user_commands = function(c)
  855. local names = {}
  856. local list = {}
  857. for k, v in base.pairs(commands) do
  858. if (not v.protected) or (v.protected and v.protected(c)) then
  859. local name = get_ucmd_name(k, v)
  860. table.insert(list, name)
  861. names[name] = k
  862. end
  863. end
  864. table.sort(list)
  865.  
  866. local function send_ucmd(name, internal_name, command, context)
  867. local ucmd = adchpp.AdcCommand(adchpp.AdcCommand_CMD_CMD, adchpp.AdcCommand_TYPE_INFO, adchpp.AdcCommand_HUB_SID)
  868. ucmd:addParam(settings.menuname.value .. autil.ucmd_sep .. name)
  869.  
  870. local back_cmd = adchpp.AdcCommand(adchpp.AdcCommand_CMD_MSG, adchpp.AdcCommand_TYPE_HUB, c:getSID())
  871. local str = "+" .. internal_name
  872.  
  873. local params = nil
  874. if context == 1 and command.user_command and command.user_command.hub_params then
  875. params = command.user_command.hub_params
  876. elseif context == 2 and command.user_command and command.user_command.user_params then
  877. params = command.user_command.user_params
  878. elseif command.user_command and command.user_command.params then
  879. params = command.user_command.params
  880. end
  881. if params then
  882. for _, param in base.ipairs(params) do
  883. str = str .. " " .. param
  884. end
  885. end
  886.  
  887. back_cmd:addParam(str)
  888. ucmd:addParam("TT", back_cmd:toString())
  889.  
  890. ucmd:addParam("CT", base.tostring(context))
  891.  
  892. c:send(ucmd)
  893. end
  894.  
  895. for _, name in base.ipairs(list) do
  896. local internal_name = names[name]
  897. local command = commands[internal_name]
  898.  
  899. local hub_sent = false
  900. if command.user_command and command.user_command.hub_params then
  901. send_ucmd(name, internal_name, command, 1)
  902. hub_sent = true
  903. end
  904.  
  905. local user_sent = false
  906. if command.user_command and command.user_command.user_params then
  907. send_ucmd(name, internal_name, command, 2)
  908. user_sent = true
  909. end
  910.  
  911. if (not hub_sent) and (not user_sent) then
  912. send_ucmd(name, internal_name, command, 3)
  913. elseif not hub_sent then
  914. send_ucmd(name, internal_name, command, 1)
  915. elseif not user_sent then
  916. send_ucmd(name, internal_name, command, 2)
  917. end
  918. end
  919. end
  920.  
  921. remove_user_commands = function(c)
  922. local function send_ucmd(name, context)
  923. c:send(adchpp.AdcCommand(adchpp.AdcCommand_CMD_CMD, adchpp.AdcCommand_TYPE_INFO, adchpp.AdcCommand_HUB_SID)
  924. :addParam(settings.menuname.value .. autil.ucmd_sep .. name)
  925. :addParam("RM", "1")
  926. :addParam("CT", base.tostring(context)))
  927. end
  928.  
  929. for k, v in base.pairs(commands) do
  930. local name = get_ucmd_name(k, v)
  931. send_ucmd(name, 1)
  932. send_ucmd(name, 2)
  933. send_ucmd(name, 3)
  934. end
  935. end
  936.  
  937. verify_info = function(c, cid, nick)
  938. if not cid or #cid == 0 then
  939. cid = c:getCID():toBase32()
  940. end
  941. if not nick or #nick == 0 then
  942. nick = c:getField("NI")
  943. end
  944. if #nick == 0 or #cid == 0 then
  945. autil.dump(c, adchpp.AdcCommand_ERROR_PROTOCOL_GENERIC, "No valid nick/CID supplied")
  946. return false
  947. end
  948.  
  949. local level = 0
  950. local user = get_user(cid, nick) -- can't use get_user_c if checking the first INF
  951. if user then
  952. level = user.level
  953. end
  954.  
  955. clear_expired_bans()
  956. local ban = nil
  957. if bans.cids[cid] then
  958. ban = bans.cids[cid]
  959. elseif bans.ips[c:getIp()] then
  960. ban = bans.ips[c:getIp()]
  961. elseif bans.nicks[nick] then
  962. ban = bans.nicks[nick]
  963. else
  964. for re, reban in base.pairs(bans.nicksre) do
  965. if nick:match(re) then
  966. ban = reban
  967. break
  968. end
  969. end
  970. end
  971. if ban and ban.level > level then
  972. dump_banned(c, ban)
  973. return false
  974. end
  975.  
  976. if settings.maxnicklength.value > 0 and #nick > settings.maxnicklength.value then
  977. autil.dump(c, adchpp.AdcCommand_ERROR_PROTOCOL_GENERIC, "Your nick (" .. nick .. ") is too long, it must contain " .. base.tostring(settings.maxnicklength.value) .. " characters max")
  978. return false
  979. end
  980.  
  981. local ss = base.tonumber(c:getField("SS"))
  982. if ss then
  983. if settings.minsharesize.value > 0 and ss < settings.minsharesize.value then
  984. autil.dump(c, adchpp.AdcCommand_ERROR_PROTOCOL_GENERIC, "Your share size (" .. c:getField("SS") .. " B) is too low, the minimum required size is " .. base.tostring(settings.minsharesize.value) .. " bytes")
  985. return false
  986. end
  987.  
  988. if settings.maxsharesize.value > 0 and ss > settings.minsharesize.value then
  989. autil.dump(c, adchpp.AdcCommand_ERROR_PROTOCOL_GENERIC, "Your share size (" .. c:getField("SS") .. " B) is too high, the maximum allowed size is " .. base.tostring(settings.minsharesize.value) .. " bytes")
  990. return false
  991. end
  992. end
  993.  
  994. local sl = base.tonumber(c:getField("SL"))
  995. if sl then
  996. if settings.minslotsize.value > 0 and sl < settings.minslotsize.value then
  997. autil.dump(c, adchpp.AdcCommand_ERROR_PROTOCOL_GENERIC, "Your number of opened upload slots (" .. c:getField("SL") .. ") is too few, the minimum required number of slots is " .. base.tostring(settings.minslotsize.value))
  998. return false
  999. end
  1000.  
  1001. if settings.maxslotsize.value > 0 and sl > settings.maxslotsize.value then
  1002. autil.dump(c, adchpp.AdcCommand_ERROR_PROTOCOL_GENERIC, "Your number of opened upload slots (" .. c:getField("SL") .. ") is too high, the maximum allowed number of slots is " .. base.tostring(settings.maxslotsize.value))
  1003. return false
  1004. end
  1005. end
  1006.  
  1007. local h1 = base.tonumber(c:getField("HN"))
  1008. local h2 = base.tonumber(c:getField("HR"))
  1009. local h3 = base.tonumber(c:getField("HO"))
  1010. local h
  1011. if (h1 and h2 and h3) then
  1012. h = base.tonumber(h1) + base.tonumber(h2) + base.tonumber(h3)
  1013. if settings.maxhubscount.value > 0 and h > settings.maxhubscount.value then
  1014. autil.dump(c, adchpp.AdcCommand_ERROR_PROTOCOL_GENERIC, "The number of hubs you're connected to (" .. base.tostring(h) .. ") is too high, the maximum allowed hubs count is " .. base.tostring(settings.maxhubscount.value))
  1015. return false
  1016. end
  1017. end
  1018.  
  1019. if sl and h and sl > 0 and h > 0 then -- Correct hubcount may not arrive with the first info
  1020. local r = sl / h
  1021. if settings.minhubslotratio.value > 0 and r < settings.minhubslotratio.value then
  1022. autil.dump(c, adchpp.AdcCommand_ERROR_PROTOCOL_GENERIC, "Your hubs/slots ratio (" .. base.tostring(r) .. ") is too low, you must open up more upload slots or disconnect from some hubs to achive ratio " .. base.tostring(settings.minhubslotratio.value))
  1023. return false
  1024. end
  1025.  
  1026. if settings.maxhubslotratio.value > 0 and r > settings.minhubslotratio.value then
  1027. autil.dump(c, adchpp.AdcCommand_ERROR_PROTOCOL_GENERIC, "Your hubs/slots ratio (" .. base.tostring(r) .. ") is too high, you must lower your number of opened upload slots or connect to more hubs to achive ratio " .. base.tostring(settings.maxhubslotratio.value))
  1028. return false
  1029. end
  1030. end
  1031.  
  1032. return true
  1033. end
  1034.  
  1035. local function onSUP(c, cmd)
  1036. -- imitate ClientManager::handle(AdcCommand::SUP, ...)
  1037.  
  1038. if not cm:verifySUP(c, cmd) then
  1039. return false
  1040. end
  1041.  
  1042. if c:getState() ~= adchpp.Entity_STATE_PROTOCOL or not c:hasSupport(adchpp.AdcCommand_toFourCC("PING")) then
  1043. -- let ClientManager further process this SUP
  1044. return true
  1045. end
  1046.  
  1047. -- imitate ClientManager::enterIdentify
  1048.  
  1049. if string.match(adchpp.versionString, 'Debug$') then
  1050. base.print(adchpp.AdcCommand_fromSID(c:getSID()) .. " entering IDENTIFY (supports 'PING')")
  1051. end
  1052.  
  1053. local hub = cm:getEntity(adchpp.AdcCommand_HUB_SID)
  1054.  
  1055. c:send(hub:getSUP())
  1056. c:send(adchpp.AdcCommand(adchpp.AdcCommand_CMD_SID, adchpp.AdcCommand_TYPE_INFO, adchpp.AdcCommand_HUB_SID)
  1057. :addParam(adchpp.AdcCommand_fromSID(c:getSID())));
  1058.  
  1059. local entities = cm:getEntities()
  1060. local uc = entities:size()
  1061. local ss = 0
  1062. local sf = 0
  1063. if uc > 0 then
  1064. for i = 0, uc - 1 do
  1065. local entity = entities[i]
  1066. local ss_ = entity:getField("SS")
  1067. if #ss_ > 0 then
  1068. ss = ss + base.tonumber(ss_)
  1069. end
  1070. local sf_ = entity:getField("SF")
  1071. if #sf_ > 0 then
  1072. sf = sf + base.tonumber(sf_)
  1073. end
  1074. end
  1075. end
  1076.  
  1077. local inf = adchpp.AdcCommand(adchpp.AdcCommand_CMD_INF, adchpp.AdcCommand_TYPE_INFO, adchpp.AdcCommand_HUB_SID)
  1078. hub:getAllFields(inf)
  1079. inf:delParam("DE", 0)
  1080. inf:addParam("DE", settings.description.value)
  1081. -- add PING-specific information
  1082. :addParam("HH" .. settings.address.value)
  1083. :addParam("WS" .. settings.website.value)
  1084. :addParam("NE" .. settings.network.value)
  1085. :addParam("OW" .. settings.owner.value)
  1086. :addParam("UC" .. base.tostring(uc))
  1087. :addParam("SS" .. base.tostring(ss))
  1088. :addParam("SF" .. base.tostring(sf))
  1089. :addParam("UP" .. base.tostring(os.difftime(os.time(), adchpp.Stats_startTime)))
  1090. if settings.maxusers.value > 0 then
  1091. inf:addParam("MC" .. base.tostring(settings.maxusers.value))
  1092. end
  1093. c:send(inf)
  1094.  
  1095. c:setState(adchpp.Entity_STATE_IDENTIFY)
  1096.  
  1097. return false
  1098. end
  1099.  
  1100. local function onINF(c, cmd)
  1101. for field, regex in base.pairs(inf_fields) do
  1102. val = cmd:getParam(field, 0)
  1103. if #val > 0 and not val:match(regex) then
  1104. autil.reply(c, "INF parsing: field " .. field .. " has an invalid value, removed")
  1105. cmd:delParam(field, 0)
  1106. end
  1107. end
  1108.  
  1109. if #cmd:getParam("CT", 0) > 0 then
  1110. autil.dump(c, adchpp.AdcCommand_ERROR_PROTOCOL_GENERIC, "I decide what type you are")
  1111. return false
  1112. end
  1113.  
  1114. local cid = cmd:getParam("ID", 0)
  1115. local nick = cmd:getParam("NI", 0)
  1116. if c:getState() == adchpp.Entity_STATE_NORMAL then
  1117. return verify_info(c, cid, nick)
  1118. end
  1119. if not verify_info(c, cid, nick) then
  1120. return false
  1121. end
  1122.  
  1123. if settings.sendversion.value == 1 then
  1124. autil.reply(c, "This hub is running " .. adchpp.versionString)
  1125. end
  1126.  
  1127. local user = get_user(cid, nick)
  1128. if not user then
  1129. -- non-reg user
  1130. local code, err = check_max_users()
  1131. if code then
  1132. autil.dump(c, code, err)
  1133. return false
  1134. end
  1135.  
  1136. -- let ClientManager further verify this INF
  1137. return true
  1138. end
  1139.  
  1140. c:setFlag(adchpp.Entity_FLAG_REGISTERED)
  1141. if user.level >= level_op then
  1142. c:setFlag(adchpp.Entity_FLAG_OP)
  1143. end
  1144. cmd:addParam("CT", c:getField("CT"))
  1145.  
  1146. if not cm:verifyINF(c, cmd) then
  1147. return false
  1148. end
  1149.  
  1150. autil.reply(c, "You are registered, please provide a password")
  1151.  
  1152. c:setByteVectorData(saltsHandle, cm:enterVerify(c, true))
  1153. return false
  1154. end
  1155.  
  1156. local function onPAS(c, cmd)
  1157. if c:getState() ~= adchpp.Entity_STATE_VERIFY then
  1158. autil.dump(c, adchpp.AdcCommand_ERROR_PROTOCOL_GENERIC, "Not in VERIFY state")
  1159. return false
  1160. end
  1161.  
  1162. local salt = c:getByteVectorData(saltsHandle)
  1163.  
  1164. if not salt then
  1165. autil.dump(c, adchpp.AdcCommand_ERROR_PROTOCOL_GENERIC, "You didn't get any salt?")
  1166. return false
  1167. end
  1168.  
  1169. local cid = c:getCID()
  1170. local nick = c:getField("NI")
  1171.  
  1172. local user = get_user_c(c)
  1173. if not user then
  1174. autil.dump(c, adchpp.AdcCommand_ERROR_PROTOCOL_GENERIC, "Can't find you now")
  1175. return false
  1176. end
  1177.  
  1178. local password = ""
  1179. if user.password then
  1180. password = user.password
  1181. end
  1182.  
  1183. if not cm:verifyPassword(c, password, salt, cmd:getParam(0)) then
  1184. autil.dump(c, adchpp.AdcCommand_ERROR_BAD_PASSWORD, "Invalid password")
  1185. return false
  1186. end
  1187.  
  1188. if not cm:verifyOverflow(c) then
  1189. return false
  1190. end
  1191.  
  1192. local updateOk, message = update_user(user, cid:toBase32(), nick)
  1193. if not updateOk then
  1194. autil.dump(c, adchpp.AdcCommand_ERROR_PROTOCOL_GENERIC, message)
  1195. return false
  1196. end
  1197.  
  1198. if message then
  1199. autil.reply(c, message)
  1200. end
  1201.  
  1202. autil.reply(c, "Welcome back")
  1203. cm:enterNormal(c, true, true)
  1204. return false
  1205. end
  1206.  
  1207. format_seconds = function(t)
  1208. local t_d = math.floor(t / (60*60*24))
  1209. local t_h = math.floor(t / (60*60)) % 24
  1210. local t_m = math.floor(t / 60) % 60
  1211. local t_s = t % 60
  1212.  
  1213. return string.format("%d days, %d hours, %d minutes and %d seconds", t_d, t_h, t_m, t_s)
  1214. end
  1215.  
  1216. cut_str = function(str, max)
  1217. if #str > max - 3 then
  1218. return string.sub(str, 1, max - 3) .. "..."
  1219. end
  1220. return str
  1221. end
  1222.  
  1223. local cfg_list_done = false
  1224. local function gen_cfg_list()
  1225. if cfg_list_done then
  1226. return
  1227. end
  1228. local list = {}
  1229. for k, v in base.pairs(settings) do
  1230. table.insert(list, k .. ": <" .. cut_str(v.help or "no information", 30) .. ">")
  1231. end
  1232. table.sort(list)
  1233. commands.cfg.user_command.params[1] = autil.ucmd_list("Name of the setting to change", list)
  1234. cfg_list_done = true
  1235. end
  1236.  
  1237. commands.cfg = {
  1238. alias = { changecfg = true, changeconfig = true, config = true, var = true, changevar = true, setvar = true, setcfg = true, setconfig = true },
  1239.  
  1240. command = function(c, parameters)
  1241. if not commands.cfg.protected(c) then
  1242. return
  1243. end
  1244.  
  1245. local name, value = parameters:match("^(%S+) ?(.*)")
  1246. if not name then
  1247. autil.reply(c, "You need to supply a variable name")
  1248. return
  1249. end
  1250.  
  1251. if string.sub(name, #name) == ":" then
  1252. -- get rid of additional info for the UCMD list
  1253. local found, _, params = string.find(parameters, "^[^>]+> (.+)")
  1254. if not found then
  1255. autil.reply(c, "Invalid parameters")
  1256. return
  1257. end
  1258. name = string.sub(name, 1, #name - 1)
  1259. value = params
  1260. end
  1261.  
  1262. local setting = nil
  1263. for k, v in base.pairs(settings) do
  1264. if k == name or (v.alias and v.alias[name]) then
  1265. setting = v
  1266. break
  1267. end
  1268. end
  1269. if not setting then
  1270. autil.reply(c, "The name " .. name .. " doesn't correspond to any setting variable, use \"+help cfg\" to list all variables")
  1271. return
  1272. end
  1273.  
  1274. local old = setting.value
  1275. local type = base.type(old)
  1276.  
  1277. if not value or #value == 0 then
  1278. -- no value; make up a default one
  1279. if type == "number" then
  1280. value = "0"
  1281. else
  1282. value = ""
  1283. end
  1284. end
  1285.  
  1286. if type == "number" then
  1287. local num = base.tonumber(value)
  1288. if not num then
  1289. autil.reply(c, "Only numbers are accepted for the variable " .. name)
  1290. return
  1291. end
  1292. value = num
  1293. end
  1294.  
  1295. if value == old then
  1296. autil.reply(c, "The value is the same as before, no change done")
  1297. return
  1298. end
  1299.  
  1300. if setting.validate then
  1301. local err = setting.validate(value)
  1302. if err then
  1303. autil.reply(c, "The new value \"" .. value .. "\" is invalid, no change done (" .. err .. ")")
  1304. return
  1305. end
  1306. end
  1307.  
  1308. setting.value = value
  1309. if setting.change then
  1310. setting.change()
  1311. end
  1312. base.pcall(save_settings)
  1313. autil.reply(c, "Variable " .. name .. " changed from " .. base.tostring(old) .. " to " .. base.tostring(setting.value))
  1314. end,
  1315.  
  1316. help = "name value - change hub configuration, use \"+help cfg\" to list all variables",
  1317.  
  1318. helplong = function()
  1319. local list = {}
  1320. for k, v in base.pairs(settings) do
  1321. local str = k .. " - current value: " .. base.tostring(v.value)
  1322. if v.help then
  1323. str = str .. " - " .. v.help
  1324. end
  1325. if v.alias then
  1326. local list_alias = {}
  1327. for k_alias, v_alias in base.pairs(v.alias) do
  1328. table.insert(list_alias, k_alias)
  1329. end
  1330. table.sort(list_alias)
  1331. str = str .. " (aliases: " .. table.concat(list_alias, ", ") .. ")"
  1332. end
  1333. table.insert(list, str)
  1334. end
  1335. table.sort(list)
  1336. return "List of all settings variables:\n" .. table.concat(list, "\n")
  1337. end,
  1338.  
  1339. protected = is_op,
  1340.  
  1341. user_command = {
  1342. name = "Hub management" .. autil.ucmd_sep .. "Change a setting",
  1343. params = {
  1344. '', -- will be set by gen_cfg_list
  1345. autil.ucmd_line("New value for the setting")
  1346. }
  1347. }
  1348. }
  1349.  
  1350. commands.help = {
  1351. command = function(c, parameters)
  1352. local command_help = function(k, v)
  1353. local str = "+" .. k
  1354. if v.help then
  1355. str = str .. " " .. v.help
  1356. end
  1357. if v.alias then
  1358. local list_alias = {}
  1359. for k_alias, v_alias in base.pairs(v.alias) do
  1360. table.insert(list_alias, "+" .. k_alias)
  1361. end
  1362. table.sort(list_alias)
  1363. str = str .. " (aliases: " .. table.concat(list_alias, ", ") .. ")"
  1364. end
  1365. return str
  1366. end
  1367.  
  1368. if #parameters > 0 then
  1369. local command = nil
  1370. for k, v in base.pairs(commands) do
  1371. if k == parameters or (v.alias and v.alias[parameters]) then
  1372. command = { k = k, v = v }
  1373. break
  1374. end
  1375. end
  1376.  
  1377. if not command then
  1378. autil.reply(c, "The command +" .. parameters .. " doesn't exist")
  1379. return
  1380. end
  1381.  
  1382. if command.v.protected and not command.v.protected(c) then
  1383. autil.reply(c, "You don't have access to the +" .. parameters .. " command")
  1384. return
  1385. end
  1386.  
  1387. local str = "\n" .. command_help(command.k, command.v)
  1388. if command.v.helplong then
  1389. str = str .. "\n\n"
  1390. if base.type(command.v.helplong) == "function" then
  1391. str = str .. command.v.helplong()
  1392. else
  1393. str = str .. command.v.helplong
  1394. end
  1395. end
  1396. autil.reply(c, str)
  1397.  
  1398. else
  1399. local list = {}
  1400. for k, v in base.pairs(commands) do
  1401. if (not v.protected) or (v.protected and v.protected(c)) then
  1402. table.insert(list, command_help(k, v))
  1403. end
  1404. end
  1405. table.sort(list)
  1406. autil.reply(c, "Available commands:\n" .. table.concat(list, "\n"))
  1407. end
  1408. end,
  1409.  
  1410. help = "[command] - list all available commands, or display detailed information about one specific command",
  1411.  
  1412. user_command = { params = {
  1413. autil.ucmd_line("Command name (facultative)")
  1414. } }
  1415. }
  1416.  
  1417. commands.info = {
  1418. alias = { hubinfo = true, stats = true, userinfo = true },
  1419.  
  1420. command = function(c, parameters)
  1421. if dispatch_stats then
  1422. return
  1423. end
  1424.  
  1425. local str
  1426.  
  1427. if #parameters > 0 then
  1428. local user = cm:findByNick(parameters) -- by nick
  1429. if not user then
  1430. user = cm:findByCID(adchpp.CID(parameters)) -- by CID
  1431. end
  1432.  
  1433. if user then
  1434. local field_function = function(field, text)
  1435. if user:hasField(field) then
  1436. str = str .. text .. ": " .. user:getField(field) .. "\n"
  1437. end
  1438. end
  1439.  
  1440. str = "\n"
  1441. field_function("NI", "Nick")
  1442. field_function("ID", "CID")
  1443. str = str .. "IP: "
  1444. local user_c = user:asClient()
  1445. if user_c then
  1446. str = str .. user_c:getIp()
  1447. else
  1448. str = str .. "unknown"
  1449. end
  1450. str = str .. "\n"
  1451. str = str .. "Level: " .. get_level(user) .. "\n"
  1452. field_function("DE", "Description")
  1453. field_function("SS", "Share size (bytes)")
  1454. field_function("SF", "Number of shared files")
  1455. field_function("VE", "Client identification")
  1456. field_function("US", "Max upload speed (bytes/s)")
  1457. field_function("DS", "Max download speed (bytes/s)")
  1458. field_function("SL", "Max slots")
  1459. field_function("AS", "Speed limit for auto-slots (bytes/s)")
  1460. field_function("AM", "Minimum auto-slots")
  1461. field_function("EM", "E-mail")
  1462. field_function("HN", "Hubs where user is a normal user")
  1463. field_function("HR", "Hubs where user is registered")
  1464. field_function("HO", "Hubs where user is operator")
  1465. field_function("AW", "Away")
  1466. field_function("SU", "Protocol supports")
  1467.  
  1468. else
  1469. -- by IP
  1470. local users_ip = {}
  1471. local entities = cm:getEntities()
  1472. local size = entities:size()
  1473. if size > 0 then
  1474. for i = 0, size - 1 do
  1475. local user_c = entities[i]:asClient()
  1476. if user_c and user_c:getIp() == parameters then
  1477. table.insert(users_ip, entities[i])
  1478. end
  1479. end
  1480. end
  1481. if table.getn(users_ip) > 0 then
  1482. str = "Users with the IP " .. parameters .. ":\n"
  1483. for i, v in base.ipairs(users_ip) do
  1484. str = str .. v:getField("NI") .. "\n"
  1485. end
  1486.  
  1487. else
  1488. str = "No user found with a nick, CID or IP matching " .. parameters
  1489. end
  1490. end
  1491.  
  1492. else
  1493. dispatch_stats = true
  1494. c:inject(adchpp.AdcCommand(adchpp.AdcCommand_CMD_MSG, adchpp.AdcCommand_TYPE_HUB, c:getSID())
  1495. :addParam('+stats'))
  1496. dispatch_stats = false
  1497.  
  1498. local now = os.time()
  1499. local scripttime = os.difftime(now, start_time)
  1500. local hubtime = os.difftime(now, adchpp.Stats_startTime)
  1501.  
  1502. str = "\n"
  1503. str = str .. "Hub uptime: " .. format_seconds(hubtime) .. "\n"
  1504. str = str .. "Script uptime: " .. format_seconds(scripttime) .. "\n"
  1505.  
  1506. str = str .. "\nADC and script commands: \n"
  1507. for k, v in base.pairs(stats) do
  1508. str = str .. v .. "\t" .. k .. "\n"
  1509. end
  1510.  
  1511. str = str .. "\nDisconnect reasons: \n"
  1512. for k, v in base.pairs(adchpp) do
  1513. if k:sub(1, 12) == "Util_REASON_" and k ~= "Util_REASON_LAST" then
  1514. str = str .. adchpp.size_t_getitem(adchpp.Util_reasons, adchpp[k]) .. "\t" .. k:sub(6) .. "\n"
  1515. end
  1516. end
  1517.  
  1518. str = str .. "\nSocket errors: \n"
  1519.  
  1520. local keys = sm.errors:keys()
  1521. local size = keys:size()
  1522.  
  1523. for i = 0, size - 1 do
  1524. local k = keys[i]
  1525. str = str .. k .. "\t" .. sm.errors:get(k) .. "\n"
  1526. end
  1527.  
  1528.  
  1529. local queued = cm:getQueuedBytes()
  1530. local queueBytes = adchpp.Stats_queueBytes
  1531. local queueCalls = adchpp.Stats_queueCalls
  1532. local sendBytes = adchpp.Stats_sendBytes
  1533. local sendCalls = adchpp.Stats_sendCalls
  1534. local recvBytes = adchpp.Stats_recvBytes
  1535. local recvCalls = adchpp.Stats_recvCalls
  1536.  
  1537. str = str .. "\nBandwidth stats: \n"
  1538. str = str .. adchpp.Util_formatBytes(queued) .. "\tBytes queued (" .. adchpp.Util_formatBytes(queued / cm:getEntities():size()) .. "/user)\n"
  1539. str = str .. adchpp.Util_formatBytes(queueBytes) .. "\tTotal bytes queued (" .. adchpp.Util_formatBytes(queueBytes/hubtime) .. "/s)\n"
  1540. str = str .. queueCalls .. "\tQueue calls (" .. adchpp.Util_formatBytes(queueBytes/queueCalls) .. "/call)\n"
  1541. str = str .. adchpp.Util_formatBytes(sendBytes) .. "\tTotal bytes sent (" .. adchpp.Util_formatBytes(sendBytes/hubtime) .. "/s)\n"
  1542. str = str .. sendCalls .. "\tSend calls (" .. adchpp.Util_formatBytes(sendBytes/sendCalls) .. "/call)\n"
  1543. str = str .. adchpp.Util_formatBytes(recvBytes) .. "\tTotal bytes received (" .. adchpp.Util_formatBytes(recvBytes/hubtime) .. "/s)\n"
  1544. str = str .. recvCalls .. "\tReceive calls (" .. adchpp.Util_formatBytes(recvBytes/recvCalls) .. "/call)\n"
  1545. end
  1546.  
  1547. autil.reply(c, str)
  1548. end,
  1549.  
  1550. help = "[nick or CID or IP] - information about a user, or about the hub if no parameter given",
  1551.  
  1552. user_command = { user_params = { "%[userNI]" } }
  1553. }
  1554.  
  1555. commands.kick = {
  1556. alias = { drop = true, dropuser = true, kickuser = true },
  1557.  
  1558. command = function(c, parameters)
  1559. local level = get_level(c)
  1560. if level < level_op then
  1561. return
  1562. end
  1563.  
  1564. local nick, reason = parameters:match("^(%S+) ?(.*)")
  1565. if not nick then
  1566. autil.reply(c, "You need to supply a nick")
  1567. return
  1568. end
  1569.  
  1570. local victim = cm:findByNick(nick)
  1571. if victim then
  1572. victim = victim:asClient()
  1573. end
  1574. if not victim then
  1575. autil.reply(c, "No user nick-named \"" .. nick .. "\"")
  1576. return
  1577. end
  1578.  
  1579. local victim_cid = victim:getCID():toBase32()
  1580. local victim_user = get_user(victim_cid, 0)
  1581. if victim_user and level <= victim_user.level then
  1582. autil.reply(c, "You can't kick users whose level is higher or equal than yours")
  1583. return
  1584. end
  1585.  
  1586. local text = "You have been kicked"
  1587. if string.len(reason) > 0 then
  1588. text = text .. " (reason: " .. reason .. ")"
  1589. end
  1590. autil.dump(victim, adchpp.AdcCommand_ERROR_BANNED_GENERIC, function(cmd)
  1591. cmd:addParam("ID" .. adchpp.AdcCommand_fromSID(c:getSID()))
  1592. :addParam("MS" .. text)
  1593. end)
  1594. autil.reply(c, "\"" .. nick .. "\" (CID: " .. victim_cid .. ") has been kicked")
  1595. end,
  1596.  
  1597. help = "user [reason] - disconnect the user, she can reconnect whenever she wants to",
  1598.  
  1599. protected = is_op,
  1600.  
  1601. user_command = {
  1602. hub_params = {
  1603. autil.ucmd_line("User"),
  1604. autil.ucmd_line("Reason (facultative)")
  1605. },
  1606. name = "Hub management" .. autil.ucmd_sep .. "Punish" .. autil.ucmd_sep .. "Kick",
  1607. user_params = {
  1608. "%[userNI]",
  1609. autil.ucmd_line("Reason (facultative)")
  1610. }
  1611. }
  1612. }
  1613.  
  1614. commands.listregs = {
  1615. alias = { listreg = true, listregged = true, reggedusers = true, showreg = true, showregs = true, showregged = true },
  1616.  
  1617. command = function(c, parameters)
  1618. local user = get_user_c(c)
  1619. if not user then
  1620. autil.reply(c, "Only registered users can use this command")
  1621. return
  1622. end
  1623.  
  1624. local list = {}
  1625. for _, v in base.ipairs(registered_users()) do
  1626. if v.level <= user.level then
  1627. local fields = {}
  1628. if v.nick then
  1629. table.insert(fields, "Nick: " .. v.nick)
  1630. end
  1631. if v.cid then
  1632. table.insert(fields, "CID: " .. v.cid)
  1633. end
  1634. if settings.passinlist.value ~=0 and v.level < user.level and v.password then
  1635. table.insert(fields, "Pass: " .. v.password)
  1636. end
  1637. table.insert(list, table.concat(fields, "\t"))
  1638. end
  1639. end
  1640. table.sort(list)
  1641.  
  1642. autil.reply(c, "Registered users with a level <= " .. user.level .. " (your level):\n" .. table.concat(list, "\n"))
  1643. end,
  1644.  
  1645. protected = function(c) return get_user_c(c) end,
  1646.  
  1647. user_command = { name = "List regs" }
  1648. }
  1649.  
  1650. commands.mass = {
  1651. alias = { massmessage = true },
  1652.  
  1653. command = function(c, parameters)
  1654. if not commands.mass.protected(c) then
  1655. return
  1656. end
  1657.  
  1658. local level_pos, _, level = parameters:find(" ?(%d*)$")
  1659. local message = parameters:sub(0, level_pos - 1)
  1660. if #message <= 0 then
  1661. autil.reply(c, "You need to supply a message")
  1662. return
  1663. end
  1664. if string.len(level) > 0 then
  1665. level = base.tonumber(level)
  1666. end
  1667.  
  1668. local entities = cm:getEntities()
  1669. local size = entities:size()
  1670. if size == 0 then
  1671. return
  1672. end
  1673.  
  1674. local mass_cmd = autil.pm(message, bot:getSID(), 0)
  1675.  
  1676. local count = 0
  1677. for i = 0, size - 1 do
  1678. local other = entities[i]:asClient()
  1679. if other then
  1680. local ok = string.len(level) == 0 or level <= 0
  1681. if not ok then
  1682. local user = get_user_c(other)
  1683. ok = user and user.level >= level
  1684. end
  1685.  
  1686. if ok then
  1687. mass_cmd:setTo(other:getSID())
  1688. other:send(mass_cmd)
  1689. count = count + 1
  1690. end
  1691. end
  1692. end
  1693.  
  1694. autil.reply(c, "Message sent to " .. count .. " users")
  1695. end,
  1696.  
  1697. help = "message [min-level]",
  1698.  
  1699. protected = is_op,
  1700.  
  1701. user_command = {
  1702. name = "Hub management" .. autil.ucmd_sep .. "Mass message",
  1703. params = {
  1704. autil.ucmd_line("Message"),
  1705. autil.ucmd_line("Minimum level (facultative)")
  1706. }
  1707. }
  1708. }
  1709.  
  1710. commands.mute = {
  1711. alias = { stfu = true },
  1712.  
  1713. command = function(c, parameters)
  1714. local level = get_level(c)
  1715. if level < level_op then
  1716. return
  1717. end
  1718.  
  1719. local minutes_pos, _, minutes = parameters:find(" (%d*)$")
  1720. if minutes_pos then
  1721. parameters = parameters:sub(0, minutes_pos - 1)
  1722. if #parameters <= 0 then
  1723. autil.reply(c, "Bad arguments")
  1724. return
  1725. end
  1726. end
  1727. local nick, reason = parameters:match("^(%S+) ?(.*)")
  1728. if not nick then
  1729. autil.reply(c, "You need to supply a nick")
  1730. return
  1731. end
  1732.  
  1733. local victim = cm:findByNick(nick)
  1734. if victim then
  1735. victim = victim:asClient()
  1736. end
  1737. if not victim then
  1738. autil.reply(c, "No user nick-named \"" .. nick .. "\"")
  1739. return
  1740. end
  1741.  
  1742. local victim_cid = victim:getCID():toBase32()
  1743. local victim_user = get_user(victim_cid, 0)
  1744. if victim_user and level <= victim_user.level then
  1745. autil.reply(c, "You can't mute users whose level is higher or equal than yours")
  1746. return
  1747. end
  1748.  
  1749. local ban = make_ban(level, reason, minutes)
  1750. bans.muted[victim_cid] = ban
  1751. base.pcall(save_bans)
  1752.  
  1753. autil.reply(c, "\"" .. nick .. "\" (CID: " .. victim_cid .. ") is now muted")
  1754. end,
  1755.  
  1756. help = "nick [reason] [minutes] - mute an online user (set minutes to 0 to un-mute)",
  1757.  
  1758. protected = is_op,
  1759.  
  1760. user_command = {
  1761. hub_params = {
  1762. autil.ucmd_line("Nick"),
  1763. autil.ucmd_line("Reason (facultative)"),
  1764. autil.ucmd_line("Minutes (facultative)")
  1765. },
  1766. name = "Hub management" .. autil.ucmd_sep .. "Punish" .. autil.ucmd_sep .. "Mute",
  1767. user_params = {
  1768. "%[userNI]",
  1769. autil.ucmd_line("Reason (facultative)"),
  1770. autil.ucmd_line("Minutes (facultative)")
  1771. }
  1772. }
  1773. }
  1774.  
  1775. commands.myip = {
  1776. alias = { getip = true, getmyip = true, ip = true, showip = true, showmyip = true },
  1777.  
  1778. command = function(c)
  1779. autil.reply(c, "Your IP: " .. c:getIp())
  1780. end,
  1781.  
  1782. user_command = { name = "My IP" }
  1783. }
  1784.  
  1785. commands.mypass = {
  1786. alias = { regme = true, changepass = true, mypassword = true, changepassword = true, setpass = true, setpassword = true },
  1787.  
  1788. command = function(c, parameters)
  1789. if #parameters <= 0 then
  1790. autil.reply(c, "You must provide a password")
  1791. return
  1792. end
  1793.  
  1794. local user = get_user_c(c)
  1795. if user then
  1796. -- already regged
  1797. user.password = parameters
  1798. base.pcall(save_users)
  1799. autil.reply(c, "Your password has been changed to \"" .. parameters .. "\"")
  1800.  
  1801. elseif settings.allowreg.value ~= 0 then
  1802. register_user(c:getCID():toBase32(), c:getField("NI"), parameters, 1)
  1803. autil.reply(c, "You're now registered with the password \"" .. parameters .. "\"")
  1804.  
  1805. else
  1806. autil.reply(c, "You are not allowed to register by yourself; ask an operator to do it for you")
  1807. return
  1808. end
  1809. end,
  1810.  
  1811. help = "new_pass - change your password, make sure you change it in your client options too",
  1812.  
  1813. protected = function(c) return settings.allowreg.value ~=0 or has_level(c, 2) end,
  1814.  
  1815. user_command = {
  1816. name = "My pass",
  1817. params = { autil.ucmd_line("New password") }
  1818. }
  1819. }
  1820.  
  1821. commands.redirect = {
  1822. alias = { forward = true },
  1823.  
  1824. command = function(c, parameters)
  1825. local level = get_level(c)
  1826. if level < level_op then
  1827. return
  1828. end
  1829.  
  1830. local nick, address = parameters:match("^(%S+) (.+)")
  1831. if not nick or not address then
  1832. autil.reply(c, "You need to supply a nick and an address")
  1833. return
  1834. end
  1835.  
  1836. local victim = cm:findByNick(nick)
  1837. if victim then
  1838. victim = victim:asClient()
  1839. end
  1840. if not victim then
  1841. autil.reply(c, "No user nick-named \"" .. nick .. "\"")
  1842. return
  1843. end
  1844.  
  1845. local victim_cid = victim:getCID():toBase32()
  1846. local victim_user = get_user(victim_cid, 0)
  1847. if victim_user and level <= victim_user.level then
  1848. autil.reply(c, "You can't redirect users whose level is higher or equal than yours")
  1849. return
  1850. end
  1851.  
  1852. autil.dump(victim, adchpp.AdcCommand_ERROR_BANNED_GENERIC, function(cmd) cmd:addParam("RD" .. address) end)
  1853. autil.reply(c, "\"" .. nick .. "\" (CID: " .. victim_cid .. ") has been redirected to \"" .. address .. "\"")
  1854. end,
  1855.  
  1856. help = "nick address",
  1857.  
  1858. protected = is_op,
  1859.  
  1860. user_command = {
  1861. hub_params = {
  1862. autil.ucmd_line("Nick"),
  1863. autil.ucmd_line("Address")
  1864. },
  1865. name = "Hub management" .. autil.ucmd_sep .. "Punish" .. autil.ucmd_sep .. "Redirect",
  1866. user_params = {
  1867. "%[userNI]",
  1868. autil.ucmd_line("Address")
  1869. }
  1870. }
  1871. }
  1872.  
  1873. commands.reload = {
  1874. command = function() end, -- empty on purpose, this is handled via PluginManager::handleCommand
  1875.  
  1876. help = "- reload scripts",
  1877.  
  1878. protected = is_op,
  1879.  
  1880. user_command = { name = "Hub management" .. autil.ucmd_sep .. "Reload scripts" }
  1881. }
  1882.  
  1883. commands.regnick = {
  1884. alias = { reguser = true },
  1885.  
  1886. command = function(c, parameters)
  1887. local my_user = get_user_c(c)
  1888. if not my_user then
  1889. autil.reply(c, "Only registered users may register others")
  1890. return
  1891. end
  1892.  
  1893. local level_pos, _, level = parameters:find(" (%d*)$")
  1894. if level_pos then
  1895. parameters = parameters:sub(0, level_pos - 1)
  1896. if #parameters <= 0 then
  1897. autil.reply(c, "Bad arguments")
  1898. return
  1899. end
  1900. end
  1901. local nick, password = parameters:match("^(%S+) ?(.*)")
  1902. if not nick then
  1903. autil.reply(c, "You need to supply a nick")
  1904. return
  1905. end
  1906.  
  1907. local other = cm:findByNick(nick)
  1908.  
  1909. local cid
  1910. if other then
  1911. cid = other:getCID():toBase32()
  1912. end
  1913.  
  1914. local other_user = get_user(cid, nick)
  1915. if other_user and other_user.level >= my_user.level then
  1916. autil.reply(c, "There is already a registered user with a level higher or equal than yours with this nick")
  1917. return
  1918. end
  1919.  
  1920. if level and string.len(level) > 0 then
  1921. level = base.tonumber(level)
  1922. if level >= my_user.level then
  1923. autil.reply(c, "You may only register to a lower level than your own (" .. my_user.level .. ")")
  1924. return
  1925. end
  1926. else
  1927. level = my_user.level - 1
  1928. end
  1929. if level < 1 then
  1930. autil.reply(c, "Level too low")
  1931. return
  1932. end
  1933.  
  1934. if #password == 0 then
  1935. -- un-reg
  1936. if not other_user then
  1937. autil.reply(c, "\"" .. nick .. "\" is not registered")
  1938. return
  1939. end
  1940. if other_user.nick then
  1941. users.nicks[other_user.nick] = nil
  1942. end
  1943. if other_user.cid then
  1944. users.cids[other_user.cid] = nil
  1945. end
  1946. base.pcall(save_users)
  1947.  
  1948. autil.reply(c, "\"" .. nick .. "\" has been un-registered")
  1949.  
  1950. if other then
  1951. autil.reply(other, "You've been un-registered")
  1952. end
  1953. return
  1954. end
  1955.  
  1956. register_user(cid, nick, password, level)
  1957.  
  1958. autil.reply(c, "\"" .. nick .. "\" has been registered")
  1959.  
  1960. if other then
  1961. autil.reply(other, "You've been registered with password \"" .. password .. "\"")
  1962. end
  1963. end,
  1964.  
  1965. help = "nick [password] [level] - register a user; use no password to un-reg; level defaults to your own level minus one",
  1966.  
  1967. protected = function(c) return has_level(c, 2) end,
  1968.  
  1969. user_command = {
  1970. hub_params = {
  1971. autil.ucmd_line("Nick"),
  1972. autil.ucmd_line("Password (leave empty to un-reg)"),
  1973. autil.ucmd_line("Level (facultative; defaults to your own level minus one)")
  1974. },
  1975. name = "Hub management" .. autil.ucmd_sep .. "Register nick",
  1976. user_params = {
  1977. "%[userNI]",
  1978. autil.ucmd_line("Password (leave empty to un-reg)"),
  1979. autil.ucmd_line("Level (facultative; defaults to your own level minus one)")
  1980. }
  1981. }
  1982. }
  1983.  
  1984. commands.test = {
  1985. command = function(c)
  1986. autil.reply(c, "Test ok")
  1987. end,
  1988.  
  1989. help = "- make the hub reply \"Test ok\""
  1990. }
  1991.  
  1992. -- simply map to +cfg topic
  1993. commands.topic = {
  1994. alias = { changetopic = true, settopic = true, changehubtopic = true, sethubtopic = true },
  1995.  
  1996. command = function(c, parameters)
  1997. commands.cfg.command(c, "topic " .. parameters)
  1998. end,
  1999.  
  2000. help = "topic - change the hub topic (shortcut to +cfg topic)",
  2001.  
  2002. protected = commands.cfg.protected,
  2003.  
  2004. user_command = {
  2005. name = "Hub management" .. autil.ucmd_sep .. "Change the topic",
  2006. params = { autil.ucmd_line("New topic") }
  2007. }
  2008. }
  2009.  
  2010. commands.ban = {
  2011. alias = { banuser = true },
  2012.  
  2013. command = function(c, parameters)
  2014. local level = get_level(c)
  2015. if level < level_op then
  2016. return
  2017. end
  2018.  
  2019. local minutes_pos, _, minutes = parameters:find(" (%d*)$")
  2020. if minutes_pos then
  2021. parameters = parameters:sub(0, minutes_pos - 1)
  2022. if #parameters <= 0 then
  2023. autil.reply(c, "Bad arguments")
  2024. return
  2025. end
  2026. end
  2027. local nick, reason = parameters:match("^(%S+) ?(.*)")
  2028. if not nick then
  2029. autil.reply(c, "You need to supply a nick")
  2030. return
  2031. end
  2032.  
  2033. local victim = cm:findByNick(nick)
  2034. if victim then
  2035. victim = victim:asClient()
  2036. end
  2037. if not victim then
  2038. autil.reply(c, "No user nick-named \"" .. nick .. "\"")
  2039. return
  2040. end
  2041.  
  2042. local victim_cid = victim:getCID():toBase32()
  2043. local victim_user = get_user(victim_cid, 0)
  2044. if victim_user and level <= victim_user.level then
  2045. autil.reply(c, "You can't ban users whose level is higher or equal than yours")
  2046. return
  2047. end
  2048.  
  2049. local ban = make_ban(level, reason, minutes)
  2050. bans.cids[victim_cid] = ban
  2051. base.pcall(save_bans)
  2052.  
  2053. dump_banned(victim, ban)
  2054. autil.reply(c, "\"" .. nick .. "\" (CID: " .. victim_cid .. ") is now banned")
  2055. end,
  2056.  
  2057. help = "nick [reason] [minutes] - ban an online user (set minutes to 0 to un-ban)",
  2058.  
  2059. protected = is_op,
  2060.  
  2061. user_command = {
  2062. hub_params = {
  2063. autil.ucmd_line("Nick"),
  2064. autil.ucmd_line("Reason (facultative)"),
  2065. autil.ucmd_line("Minutes (facultative)")
  2066. },
  2067. name = "Hub management" .. autil.ucmd_sep .. "Punish" .. autil.ucmd_sep .. "Ban",
  2068. user_params = {
  2069. "%[userNI]",
  2070. autil.ucmd_line("Reason (facultative)"),
  2071. autil.ucmd_line("Minutes (facultative)")
  2072. }
  2073. }
  2074. }
  2075.  
  2076. commands.bancid = {
  2077. command = function(c, parameters)
  2078. local level = get_level(c)
  2079. if level < level_op then
  2080. return
  2081. end
  2082.  
  2083. local minutes_pos, _, minutes = parameters:find(" (%d*)$")
  2084. if minutes_pos then
  2085. parameters = parameters:sub(0, minutes_pos - 1)
  2086. if #parameters <= 0 then
  2087. autil.reply(c, "Bad arguments")
  2088. return
  2089. end
  2090. end
  2091. local cid, reason = parameters:match("^(%S+) ?(.*)")
  2092. if not cid then
  2093. autil.reply(c, "You need to supply a CID")
  2094. return
  2095. end
  2096.  
  2097. bans.cids[cid] = make_ban(level, reason, minutes)
  2098. base.pcall(save_bans)
  2099.  
  2100. autil.reply(c, "The CID \"" .. cid .. "\" is now banned")
  2101. end,
  2102.  
  2103. help = "CID [reason] [minutes] (set minutes to 0 to un-ban)",
  2104.  
  2105. protected = is_op,
  2106.  
  2107. user_command = {
  2108. hub_params = {
  2109. autil.ucmd_line("CID"),
  2110. autil.ucmd_line("Reason (facultative)"),
  2111. autil.ucmd_line("Minutes (facultative)")
  2112. },
  2113. name = "Hub management" .. autil.ucmd_sep .. "Punish" .. autil.ucmd_sep .. "Ban CID",
  2114. user_params = {
  2115. "%[userCID]",
  2116. autil.ucmd_line("Reason (facultative)"),
  2117. autil.ucmd_line("Minutes (facultative)")
  2118. }
  2119. }
  2120. }
  2121.  
  2122. commands.banip = {
  2123. command = function(c, parameters)
  2124. local level = get_level(c)
  2125. if level < level_op then
  2126. return
  2127. end
  2128.  
  2129. local minutes_pos, _, minutes = parameters:find(" (%d*)$")
  2130. if minutes_pos then
  2131. parameters = parameters:sub(0, minutes_pos - 1)
  2132. if #parameters <= 0 then
  2133. autil.reply(c, "Bad arguments")
  2134. return
  2135. end
  2136. end
  2137. local ip, reason = parameters:match("^(%S+) ?(.*)")
  2138. if not ip then
  2139. autil.reply(c, "You need to supply an IP address")
  2140. return
  2141. end
  2142.  
  2143. bans.ips[ip] = make_ban(level, reason, minutes)
  2144. base.pcall(save_bans)
  2145.  
  2146. autil.reply(c, "The IP address \"" .. ip .. "\" is now banned")
  2147. end,
  2148.  
  2149. help = "IP [reason] [minutes] (set minutes to 0 to un-ban)",
  2150.  
  2151. protected = is_op,
  2152.  
  2153. user_command = {
  2154. hub_params = {
  2155. autil.ucmd_line("IP"),
  2156. autil.ucmd_line("Reason (facultative)"),
  2157. autil.ucmd_line("Minutes (facultative)")
  2158. },
  2159. name = "Hub management" .. autil.ucmd_sep .. "Punish" .. autil.ucmd_sep .. "Ban IP",
  2160. user_params = {
  2161. "%[userI4]",
  2162. autil.ucmd_line("Reason (facultative)"),
  2163. autil.ucmd_line("Minutes (facultative)")
  2164. }
  2165. }
  2166. }
  2167.  
  2168. commands.bannick = {
  2169. command = function(c, parameters)
  2170. local level = get_level(c)
  2171. if level < level_op then
  2172. return
  2173. end
  2174.  
  2175. local minutes_pos, _, minutes = parameters:find(" (%d*)$")
  2176. if minutes_pos then
  2177. parameters = parameters:sub(0, minutes_pos - 1)
  2178. if #parameters <= 0 then
  2179. autil.reply(c, "Bad arguments")
  2180. return
  2181. end
  2182. end
  2183. local nick, reason = parameters:match("^(%S+) ?(.*)")
  2184. if not nick then
  2185. autil.reply(c, "You need to supply a nick")
  2186. return
  2187. end
  2188.  
  2189. bans.nicks[nick] = make_ban(level, reason, minutes)
  2190. base.pcall(save_bans)
  2191.  
  2192. autil.reply(c, "The nick \"" .. nick .. "\" is now banned")
  2193. end,
  2194.  
  2195. help = "nick [reason] [minutes] (set minutes to 0 to un-ban)",
  2196.  
  2197. protected = is_op,
  2198.  
  2199. user_command = {
  2200. hub_params = {
  2201. autil.ucmd_line("Nick"),
  2202. autil.ucmd_line("Reason (facultative)"),
  2203. autil.ucmd_line("Minutes (facultative)")
  2204. },
  2205. name = "Hub management" .. autil.ucmd_sep .. "Punish" .. autil.ucmd_sep .. "Ban nick",
  2206. user_params = {
  2207. "%[userNI]",
  2208. autil.ucmd_line("Reason (facultative)"),
  2209. autil.ucmd_line("Minutes (facultative)")
  2210. }
  2211. }
  2212. }
  2213.  
  2214. commands.bannickre = {
  2215. command = function(c, parameters)
  2216. local level = get_level(c)
  2217. if level < level_op then
  2218. return
  2219. end
  2220.  
  2221. local minutes_pos, _, minutes = parameters:find(" (%d*)$")
  2222. if minutes_pos then
  2223. parameters = parameters:sub(0, minutes_pos - 1)
  2224. if #parameters <= 0 then
  2225. autil.reply(c, "Bad arguments")
  2226. return
  2227. end
  2228. end
  2229. local re, reason = parameters:match("<([^>]+)> ?(.*)")
  2230. if not re then
  2231. autil.reply(c, "You need to supply a reg exp (within '<' and '>' brackets)")
  2232. return
  2233. end
  2234.  
  2235. bans.nicksre[re] = make_ban(level, reason, minutes)
  2236. base.pcall(save_bans)
  2237.  
  2238. autil.reply(c, "Nicks that match \"" .. re .. "\" are now banned")
  2239. end,
  2240.  
  2241. help = "<nick-reg-exp> [reason] [minutes] - ban nicks that match the given reg exp (must be within '<' and '>' brackets) (set minutes to 0 to un-ban)",
  2242.  
  2243. protected = is_op,
  2244.  
  2245. user_command = {
  2246. name = "Hub management" .. autil.ucmd_sep .. "Punish" .. autil.ucmd_sep .. "Ban nick (reg exp)",
  2247. params = {
  2248. "<" .. autil.ucmd_line("Reg exp of nicks to forbid") .. ">",
  2249. autil.ucmd_line("Reason (facultative)"),
  2250. autil.ucmd_line("Minutes (facultative)")
  2251. }
  2252. }
  2253. }
  2254.  
  2255. commands.banmsgre = {
  2256. command = function(c, parameters)
  2257. local level = get_level(c)
  2258. if level < level_op then
  2259. return
  2260. end
  2261.  
  2262. local minutes_pos, _, minutes = parameters:find(" (%d*)$")
  2263. if minutes_pos then
  2264. parameters = parameters:sub(0, minutes_pos - 1)
  2265. if #parameters <= 0 then
  2266. autil.reply(c, "Bad arguments")
  2267. return
  2268. end
  2269. end
  2270. local re, reason = parameters:match("<([^>]+)> ?(.*)")
  2271. if not re then
  2272. autil.reply(c, "You need to supply a reg exp (within '<' and '>' brackets)")
  2273. return
  2274. end
  2275.  
  2276. bans.msgsre[re] = make_ban(level, reason, minutes)
  2277. base.pcall(save_bans)
  2278.  
  2279. autil.reply(c, "Messages that match \"" .. re .. "\" will get the user banned")
  2280. end,
  2281.  
  2282. help = "msg-reg-exp [reason] [minutes] - ban originators of messages that match the given reg exp (must be within '<' and '>' brackets) (set minutes to 0 to un-ban)",
  2283.  
  2284. protected = is_op,
  2285.  
  2286. user_command = {
  2287. name = "Hub management" .. autil.ucmd_sep .. "Punish" .. autil.ucmd_sep .. "Ban chat (reg exp)",
  2288. params = {
  2289. "<" .. autil.ucmd_line("Reg exp of chat messages to forbid") .. ">",
  2290. autil.ucmd_line("Reason (facultative)"),
  2291. autil.ucmd_line("Minutes (facultative)")
  2292. }
  2293. }
  2294. }
  2295.  
  2296. commands.listbans = {
  2297. alias = { listban = true, listbanned = true, showban = true, showbans = true, showbanned = true },
  2298.  
  2299. command = function(c)
  2300. local level = get_level(c)
  2301. if level < level_op then
  2302. return
  2303. end
  2304.  
  2305. clear_expired_bans()
  2306.  
  2307. local str = "\nCID bans:"
  2308. for cid, ban in base.pairs(bans.cids) do
  2309. str = str .. "\n\tCID: " .. cid .. ban_info_string(ban)
  2310. end
  2311.  
  2312. str = str .. "\n\nIP bans:"
  2313. for ip, ban in base.pairs(bans.ips) do
  2314. str = str .. "\n\tIP: " .. ip .. ban_info_string(ban)
  2315. end
  2316.  
  2317. str = str .. "\n\nNick bans:"
  2318. for nick, ban in base.pairs(bans.nicks) do
  2319. str = str .. "\n\tNick: " .. nick .. ban_info_string(ban)
  2320. end
  2321.  
  2322. str = str .. "\n\nNick bans (reg exp):"
  2323. for nickre, ban in base.pairs(bans.nicksre) do
  2324. str = str .. "\n\tReg exp: " .. nickre .. ban_info_string(ban)
  2325. end
  2326.  
  2327. str = str .. "\n\nMessage bans (reg exp):"
  2328. for msgre, ban in base.pairs(bans.msgsre) do
  2329. str = str .. "\n\tReg exp: " .. msgre .. ban_info_string(ban)
  2330. end
  2331.  
  2332. str = str .. "\n\nMuted:"
  2333. for cid, ban in base.pairs(bans.muted) do
  2334. str = str .. "\n\tCID: " .. cid .. ban_info_string(ban)
  2335. end
  2336.  
  2337. autil.reply(c, str)
  2338. end,
  2339.  
  2340. protected = is_op,
  2341.  
  2342. user_command = { name = "Hub management" .. autil.ucmd_sep .. "List bans" }
  2343. }
  2344.  
  2345. commands.loadbans = {
  2346. alias = { reloadbans = true },
  2347.  
  2348. command = function(c)
  2349. local level = get_level(c)
  2350. if level < level_op then
  2351. return
  2352. end
  2353.  
  2354. base.pcall(load_bans)
  2355.  
  2356. autil.reply(c, "Ban list reloaded")
  2357. end,
  2358.  
  2359. help = "- reload the ban list",
  2360.  
  2361. protected = is_op,
  2362.  
  2363. user_command = { name = "Hub management" .. autil.ucmd_sep .. "Reload bans" }
  2364. }
  2365.  
  2366. local function onMSG(c, cmd)
  2367. clear_expired_bans()
  2368. local muted = bans.muted[c:getCID():toBase32()]
  2369. if muted then
  2370. autil.reply(c, "You are muted" .. ban_return_info(muted))
  2371. return false
  2372. end
  2373.  
  2374. local msg = cmd:getParam(0)
  2375.  
  2376. local bot = autil.reply_from and autil.reply_from:getSID() == bot:getSID()
  2377. if not autil.reply_from or bot then
  2378. local command, parameters = msg:match("^%+(%a+) ?(.*)")
  2379. if command then
  2380. for k, v in base.pairs(commands) do
  2381. if k == command or (v.alias and v.alias[command]) then
  2382. add_stats('+' .. command)
  2383. v.command(c, parameters)
  2384. return false
  2385. end
  2386. end
  2387. end
  2388. if bot then
  2389. autil.reply(c, 'Invalid command, send "+help" for a list of available commands')
  2390. return false
  2391. end
  2392. end
  2393.  
  2394. local level = get_level(c)
  2395. clear_expired_bans()
  2396. for re, reban in base.pairs(bans.msgsre) do
  2397. if reban.level >= level and msg:match(re) then
  2398. local ban = { level = reban.level, reason = reban.reason, expires = reban.expires }
  2399. bans.cids[c:getCID():toBase32()] = ban
  2400. base.pcall(save_bans)
  2401. dump_banned(c, ban)
  2402. return false
  2403. end
  2404. end
  2405.  
  2406. if settings.maxmsglength.value > 0 and string.len(msg) > settings.maxmsglength.value then
  2407. autil.reply(c, "Your message contained too many characters, max allowed is " .. settings.maxmsglength.value)
  2408. return false
  2409. end
  2410.  
  2411. return true
  2412. end
  2413.  
  2414. local function onReceive(entity, cmd, ok)
  2415. add_stats(cmd:getCommandString())
  2416.  
  2417. if not ok then
  2418. return ok
  2419. end
  2420.  
  2421. local c = entity:asClient()
  2422. if not c then
  2423. return false
  2424. end
  2425.  
  2426. local allowed_type = command_contexts[cmd:getCommand()]
  2427. if allowed_type then
  2428. if not cmd:getType():match(allowed_type) then
  2429. autil.reply(c, "Invalid context for " .. cmd:getCommandString())
  2430. return false
  2431. end
  2432. end
  2433.  
  2434. if c:getState() == adchpp.Entity_STATE_NORMAL then
  2435. local restricted = restricted_commands[cmd:getCommand()]
  2436. if restricted and get_level(c) < restricted.level then
  2437. c:send(adchpp.AdcCommand(adchpp.AdcCommand_CMD_STA, adchpp.AdcCommand_TYPE_INFO, adchpp.AdcCommand_HUB_SID)
  2438. :addParam(adchpp.AdcCommand_SEV_RECOVERABLE .. adchpp.AdcCommand_ERROR_COMMAND_ACCESS)
  2439. :addParam("You are not allowed to " .. restricted.str .. " in this hub")
  2440. :addParam("FC" .. cmd:getFourCC()))
  2441. return false
  2442. end
  2443. end
  2444.  
  2445. if cmd:getCommand() == adchpp.AdcCommand_CMD_SUP then
  2446. return onSUP(c, cmd)
  2447. end
  2448. if cmd:getCommand() == adchpp.AdcCommand_CMD_INF then
  2449. return onINF(c, cmd)
  2450. end
  2451. if cmd:getCommand() == adchpp.AdcCommand_CMD_PAS then
  2452. return onPAS(c, cmd)
  2453. end
  2454. if cmd:getCommand() == adchpp.AdcCommand_CMD_MSG then
  2455. if cmd:getTo() ~= adchpp.AdcCommand_HUB_SID then
  2456. autil.reply_from = cm:getEntity(cmd:getTo())
  2457. end
  2458. local ret = onMSG(c, cmd)
  2459. autil.reply_from = nil
  2460. return ret
  2461. end
  2462.  
  2463. return true
  2464. end
  2465.  
  2466. base.pcall(load_users)
  2467. if not base.pcall(load_settings) then
  2468. base.pcall(save_settings) -- save initial settings
  2469. end
  2470. base.pcall(load_bans)
  2471.  
  2472. bot = cm:createSimpleBot()
  2473. bot:setCID(adchpp.CID(settings.botcid.value))
  2474. bot:setField("ID", settings.botcid.value)
  2475. bot:setField("NI", settings.botname.value)
  2476. bot:setField("DE", settings.botdescription.value)
  2477. bot:setField("EM", settings.botemail.value)
  2478. bot:setFlag(adchpp.Entity_FLAG_OP)
  2479. bot:setFlag(adchpp.Entity_FLAG_SU)
  2480. bot:setFlag(adchpp.Entity_FLAG_OWNER)
  2481. cm:regBot(bot)
  2482.  
  2483. autil.on_unloaded(_NAME, function()
  2484. bot:disconnect(adchpp.Util_REASON_PLUGIN)
  2485. end)
  2486.  
  2487. table.foreach(extensions, function(_, extension)
  2488. cm:getEntity(adchpp.AdcCommand_HUB_SID):addSupports(adchpp.AdcCommand_toFourCC(extension))
  2489. end)
  2490.  
  2491. access_1 = cm:signalReceive():connect(function(entity, cmd, ok)
  2492. local res = onReceive(entity, cmd, ok)
  2493. if not res then
  2494. cmd:setPriority(adchpp.AdcCommand_PRIORITY_IGNORE)
  2495. end
  2496. return res
  2497. end)
  2498.  
  2499. access_2 = cm:signalState():connect(function(entity)
  2500. if entity:getState() == adchpp.Entity_STATE_NORMAL then
  2501. local c = entity:asClient()
  2502. if c and (
  2503. entity:hasSupport(adchpp.AdcCommand_toFourCC("UCMD")) or entity:hasSupport(adchpp.AdcCommand_toFourCC("UCM0"))
  2504. ) then
  2505. gen_cfg_list()
  2506. send_user_commands(c)
  2507. end
  2508. end
  2509. end)
  2510.  
  2511. access_3 = pm:getCommandSignal("reload"):connect(function(entity, list, ok)
  2512. return commands.reload.protected(entity)
  2513. end)
  2514.  
  2515. access_4 = pm:getCommandSignal("stats"):connect(function()
  2516. return dispatch_stats
  2517. end)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement