From: Stan Grishin Date: Tue, 24 Feb 2026 21:48:32 +0000 (+0000) Subject: luci-app-adblock-fast: update to 1.2.2-r6 X-Git-Url: http://git.99rst.org/?a=commitdiff_plain;h=0d3cab72c6fe90c3996959f468bf4536c50cd048;p=openwrt-luci.git luci-app-adblock-fast: update to 1.2.2-r6 Update luci-app-adblock-fast from 1.2.1-r3 to 1.2.2-r6. This rewrites the rpcd backend from a 519-line shell script to a 452-line native ucode module, adds two new RPC methods for cron management, reorganizes the web UI with a dedicated scheduling tab and a collapsible service details section, moves schedule persistence from UCI to crontab, and adds the AGPL-3.0-or-later LICENSE file. Signed-off-by: Stan Grishin --- - **8 files changed**, +1,765 / -851 lines (net +914) - **1 commit**: `a18c61a` — `luci-app-adblock-fast: update to 1.2.2-r6` --- - `+rpcd-mod-ucode` — Native ucode rpcd module loader - `+jsonfilter` — No longer needed; ucode handles JSON natively - Version bumped from `1.2.1-r3` to `1.2.2-r6` - URL updated from `github.com/stangri/...` to `github.com/mossdef-org/...` --- The rpcd script moves from: ``` /usr/libexec/rpcd/luci.adblock-fast (519 lines, shell) ``` to: ``` /usr/share/rpcd/ucode/luci.adblock-fast (452 lines, ucode) ``` | Aspect | Old (Shell) | New (ucode) | |-----------------|-------------------------|--------------------| | Language | POSIX `/bin/sh` | ucode bytecode | | JSON handling | `json_init/add/dump` | Native objects | | UCI access | `config_load/get` subpr | `cursor()` API | | ubus access | `jsonfilter` pipelines | `connect()` API | | Business logic | Duplicated in script | Imports shared lib | | Method decl | `case` switch block | Declarative schema | | Performance | Fork/exec per operation | In-process calls | The new script imports the shared business logic module directly: ```javascript import adb from '/lib/adblock-fast/adblock-fast.uc'; ``` Query methods (`getInitStatus`, `getPlatformSupport`, `getFileUrlFilesizes`, etc.) now delegate to the shared `adb` library rather than re-implementing the logic. **Retained (7 methods, same interface):** | Method | Type | Purpose | |-----------------------|--------|--------------------| | `getFileUrlFilesizes` | Query | URL sizes from cfg | | `getInitList` | Query | Enabled/running | | `getInitStatus` | Query | Full service state | | `getPlatformSupport` | Query | Resolver/tool info | | `setInitAction` | Action | Start/stop/enable | | `getCronStatus` | Query | Cron diagnostics | | `syncCron` | Action | Update cron sched | **Added (2 new methods):** | Method | Type | Purpose | |----------------|--------|----------------------| | `getCronEntry` | Query | Get raw cron line | | `setCronEntry` | Action | Set/replace cron line| The cron subsystem now tracks three entry states: - **`active`** — Enabled and scheduled - **`suspended`** — Disabled but recoverable - **`disabled`** — Fully off `getCronStatus` returns new diagnostic fields: - `cron_line_multi` — Multiple entries detected - `cron_line_parse_ok` — Expression validity - `cron_line_state` — One of: `active`, `suspended`, `disabled`, `multi`, `unsupported`, `missing` - `entry` — Raw matched cron line Bumped from **11** to **13**, reflecting the new methods and enhanced `getCronStatus` response shape. Two new methods added to the rpcd ACL file (`luci-app-adblock-fast.json`): - `getCronEntry` (read section) - `setCronEntry` (write section) --- **LuciCompat** bumped from `11` to `13`. **Data fetching refactored:** - Removed separate `getServiceInfo()` ubus call - `initStatus` response now provides all data (package compat, errors, warnings) in one call - `ubus` object built from `initData` properties instead of separate service instance query **New "Service Details" section:** - Blocking stats, DNS backend info, compressed cache status, force-DNS ports, and donation link moved from inline status text to a separate collapsible `detailsDiv` - Main status area now shows only the essential state label and cache info when stopped **Cron warning logic rewritten:** - Warnings only displayed when service is both enabled and running - New `warningCronEntryMismatch` warning for suspended or unparseable cron entries - Suggests "Resync Cron" action when `cronSyncNeeded` is detected - Pre-existing checks for missing/disabled cron daemon preserved but now gated behind `showCronWarnings` flag **Cron sync flow rewritten:** 1. Fetches current entry via `getCronEntry()` 2. Strips comment markers and suspended/disabled tags from the entry 3. Writes cleaned entry via `setCronEntry()` 4. Reloads page only on success **New exports:** `getCronEntry`, `setCronEntry` **New helper: `parseCronEntry(cronEntry)`** - Parses raw cron line into form field values - Detects schedule mode from cron pattern: - `every_n_hours` — `*/N * * * *` - `every_n_days` — `M H */N * *` - `monthly` — `M H D * *` - `weekly` — `M H * * D` - `daily` — `M H * * *` (default) - Returns config object with: `auto_update_enabled`, `auto_update_mode`, `auto_update_hour`, `auto_update_minute`, `auto_update_weekday`, `auto_update_monthday`, `auto_update_every_ndays`, `auto_update_every_nhours` - Falls back to defaults for unparseable entries **New helper: `generateCronEntry(config)`** - Inverse of `parseCronEntry` - Converts form config object back to cron syntax - Returns empty string when auto-update disabled - Output format: `M H DOM * DOW /etc/init.d/adblock-fast dl` `# adblock-fast-auto` **Data loading consolidated:** - Removed separate `getFileUrlFilesizes()` and `getPlatformSupport()` calls - Now fetches `getInitStatus()` + `getCronStatus()` - Sizes and platform data extracted from the unified `initStatus` response **New "List Updates Schedule" tab:** - All scheduling options moved from `tab_advanced` to dedicated `tab_schedule` - Options: `auto_update_enabled`, `auto_update_mode`, `auto_update_every_ndays`, `auto_update_every_nhours`, `auto_update_weekday`, `auto_update_monthday`, `auto_update_hour`, `auto_update_minute` - Each option's `cfgvalue()` overridden to read from parsed cron config instead of UCI - `config_update_enabled` remains on `tab_advanced` **Schedule persistence moved to crontab:** - Old: scheduling fields saved to UCI config, then `syncCron` called after `uci-applied` event to generate cron entry from config - New: `handleSave()` collects form values, calls `generateCronEntry()`, writes directly via `setCronEntry()`, then removes scheduling fields from UCI before saving remaining config - Result: schedule lives in crontab, survives config resets **`handleSaveApply` simplified:** - Old: chained `handleSave` → listener for `uci-applied` → `syncCron` → page reload - New: `handleSave()` → `ui.changes.apply()` - Cron already updated during save, no separate sync step needed **Instance handling fix:** - `dnsmasq_instance` and `smartdns_instance` write overrides now wrap values in arrays - Ensures instances stored as UCI lists, not scalar strings --- **2 new strings added:** - `"List Updates Schedule"` — New tab header - `"Service Details"` — New status section header **0 strings removed, 0 strings reworded.** All other changes are source line number updates from the JavaScript refactoring. Existing translations remain valid; translators only need to handle the 2 new entries. --- Adds the full AGPL-3.0-or-later license text (661 lines), matching the `PKG_LICENSE` field already declared in the Makefile. --- - `rpcdCompat` bumped from `11` to `13` - `LuciCompat` bumped from `11` to `13` - Requires `rpcd-mod-ucode` (replaces `jsonfilter`) - Requires companion `adblock-fast` package >=1.2.2 (for the shared `/lib/adblock-fast/adblock-fast.uc` library imported by the rpcd ucode module) - All existing RPC methods preserved; 2 new ones added - All existing UI functionality preserved; scheduling options reorganized into dedicated tab Signed-off-by: Stan Grishin --- diff --git a/applications/luci-app-adblock-fast/LICENSE b/applications/luci-app-adblock-fast/LICENSE new file mode 100644 index 0000000000..0ad25db4bd --- /dev/null +++ b/applications/luci-app-adblock-fast/LICENSE @@ -0,0 +1,661 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU Affero General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU AGPL, see +. diff --git a/applications/luci-app-adblock-fast/Makefile b/applications/luci-app-adblock-fast/Makefile index 04673e2f24..76239de4f5 100644 --- a/applications/luci-app-adblock-fast/Makefile +++ b/applications/luci-app-adblock-fast/Makefile @@ -6,13 +6,13 @@ include $(TOPDIR)/rules.mk PKG_NAME:=luci-app-adblock-fast PKG_LICENSE:=AGPL-3.0-or-later PKG_MAINTAINER:=Stan Grishin -PKG_VERSION:=1.2.1 -PKG_RELEASE:=3 +PKG_VERSION:=1.2.2 +PKG_RELEASE:=6 LUCI_TITLE:=AdBlock-Fast Web UI -LUCI_URL:=https://github.com/stangri/luci-app-adblock-fast/ +LUCI_URL:=https://github.com/mossdef-org/luci-app-adblock-fast/ LUCI_DESCRIPTION:=Provides Web UI for adblock-fast service. -LUCI_DEPENDS:=+luci-base +adblock-fast +jsonfilter +LUCI_DEPENDS:=+luci-base +adblock-fast +rpcd-mod-ucode define Package/$(PKG_NAME)/config # shown in make menuconfig diff --git a/applications/luci-app-adblock-fast/htdocs/luci-static/resources/adblock-fast/status.js b/applications/luci-app-adblock-fast/htdocs/luci-static/resources/adblock-fast/status.js index b5f22cc561..8047f169c4 100644 --- a/applications/luci-app-adblock-fast/htdocs/luci-static/resources/adblock-fast/status.js +++ b/applications/luci-app-adblock-fast/htdocs/luci-static/resources/adblock-fast/status.js @@ -12,7 +12,7 @@ var pkg = { return "adblock-fast"; }, get LuciCompat() { - return 11; + return 13; }, get ReadmeCompat() { return ""; @@ -199,6 +199,19 @@ var getCronStatus = rpc.declare({ params: ["name"], }); +var getCronEntry = rpc.declare({ + object: "luci." + pkg.Name, + method: "getCronEntry", + params: ["name"], +}); + +var setCronEntry = rpc.declare({ + object: "luci." + pkg.Name, + method: "setCronEntry", + params: ["name", "entry"], + expect: { result: false }, +}); + var getPlatformSupport = rpc.declare({ object: "luci." + pkg.Name, method: "getPlatformSupport", @@ -302,35 +315,38 @@ var status = baseclass.extend({ render: function () { return Promise.all([ L.resolveDefault(getInitStatus(pkg.Name), {}), - L.resolveDefault(getServiceInfo(pkg.Name, true), {}), L.resolveDefault(getCronStatus(pkg.Name), {}), - ]).then(function ([initStatus, ubusInfo, cronStatus]) { + ]).then(function ([initStatus, cronStatus]) { + var initData = initStatus?.[pkg.Name] || {}; var reply = { - status: initStatus?.[pkg.Name] || { - enabled: false, - status: null, - packageCompat: 0, - rpcdCompat: 0, - running: null, - version: null, - errors: [], - warnings: [], - force_dns_active: null, - force_dns_ports: [], - entries: null, - dns: null, - outputFile: null, - outputCache: null, - outputGzip: null, - outputFileExists: null, - outputCacheExists: null, - outputGzipExists: null, - leds: [], - }, - ubus: ubusInfo?.[pkg.Name]?.instances?.main?.data || { - packageCompat: 0, - errors: [], - warnings: [], + status: + initData.enabled !== undefined + ? initData + : { + enabled: false, + status: null, + packageCompat: 0, + rpcdCompat: 0, + running: null, + version: null, + errors: [], + warnings: [], + force_dns_active: null, + force_dns_ports: [], + entries: null, + dns: null, + outputFile: null, + outputCache: null, + outputGzip: null, + outputFileExists: null, + outputCacheExists: null, + outputGzipExists: null, + leds: [], + }, + ubus: { + packageCompat: initData.packageCompat || 0, + errors: initData.errors ? [...initData.errors] : [], + warnings: initData.warnings ? [...initData.warnings] : [], }, cron: cronStatus?.[pkg.Name] || { auto_update_enabled: false, @@ -340,6 +356,7 @@ var status = baseclass.extend({ cron_running: false, cron_line_present: false, cron_line_match: false, + cron_line_state: "none", }, }; @@ -364,22 +381,43 @@ var status = baseclass.extend({ }); } var cronSyncNeeded = false; - if (reply.cron.auto_update_enabled) { + var showCronWarnings = + reply.status.enabled && + reply.status.running && + (reply.cron.auto_update_enabled || + reply.cron.cron_line_state === "suspended"); + if (showCronWarnings) { var enableCronCmd = "/etc/init.d/cron enable && /etc/init.d/cron start"; var resyncLabel = "" + _("Resync Cron") + ""; - if (reply.status.enabled && reply.status.running) { - if (!reply.cron.cron_init || !reply.cron.cron_bin) { - reply.ubus.warnings.push({ - code: "warningCronMissing", - info: enableCronCmd, - }); - } else if (!reply.cron.cron_enabled || !reply.cron.cron_running) { - reply.ubus.warnings.push({ - code: "warningCronDisabled", - info: enableCronCmd, - }); - } + if (!reply.cron.cron_init || !reply.cron.cron_bin) { + reply.ubus.warnings.push({ + code: "warningCronMissing", + info: enableCronCmd, + }); + } else if (!reply.cron.cron_enabled || !reply.cron.cron_running) { + reply.ubus.warnings.push({ + code: "warningCronDisabled", + info: enableCronCmd, + }); + } + if (reply.cron.cron_line_state === "suspended") { + reply.ubus.warnings.push({ + code: "warningCronEntryMismatch", + info: resyncLabel, + }); + cronSyncNeeded = true; + } else if ( + reply.cron.auto_update_enabled && + (reply.cron.cron_line_state === "unsupported" || + reply.cron.cron_line_state === "multi") + ) { + reply.ubus.warnings.push({ + code: "warningCronEntryMismatch", + info: resyncLabel, + }); + cronSyncNeeded = true; + } else if (reply.cron.auto_update_enabled) { if (!reply.cron.cron_line_present) { reply.ubus.warnings.push({ code: "warningCronEntryMissing", @@ -410,31 +448,6 @@ var status = baseclass.extend({ switch (reply.status.status) { case "statusSuccess": text += pkg.statusTable[reply.status.status] + "."; - text += - "
" + - _("Blocking %s domains (with %s).").format( - reply.status.entries, - reply.status.dns, - ); - if (reply.status.outputGzipExists) { - text += "
" + _("Compressed cache file created."); - } - if (reply.status.force_dns_active) { - text += "
" + _("Force DNS ports:"); - reply.status.force_dns_ports.forEach((element) => { - text += " " + element; - }); - text += "."; - } - text += - "
" + - "
" + - _( - "Please %sdonate%s to support development of this project.", - ).format( - "", - "", - ); break; case "statusStopped": if (reply.status.enabled) { @@ -446,11 +459,6 @@ var status = baseclass.extend({ _("Disabled") + ")."; } - if (reply.status.outputCacheExists) { - text += "
" + _("Cache file found."); - } else if (reply.status.outputGzipExists) { - text += "
" + _("Compressed cache file found."); - } break; case "statusRestarting": case "statusForceReloading": @@ -465,13 +473,69 @@ var status = baseclass.extend({ } else { text = _("Not installed or not found"); } - var statusText = E("div", { class: "cbi-value-description" }, text); + var statusText = E("div", {}, text); var statusField = E("div", { class: "cbi-value-field" }, statusText); var statusDiv = E("div", { class: "cbi-value" }, [ statusTitle, statusField, ]); + var detailsDiv = []; + if (reply.status.version) { + var detailsText = ""; + if (reply.status.status === "statusSuccess") { + detailsText += _("Blocking %s domains (with %s).").format( + reply.status.entries, + reply.status.dns, + ); + if (reply.status.outputGzipExists) { + detailsText += "
" + _("Compressed cache file created."); + } + if (reply.status.force_dns_active) { + detailsText += "
" + _("Force DNS ports:"); + reply.status.force_dns_ports.forEach((element) => { + detailsText += " " + element; + }); + detailsText += "."; + } + } + if (reply.status.status === "statusStopped") { + if (reply.status.outputCacheExists) { + detailsText += _("Cache file found."); + } else if (reply.status.outputGzipExists) { + detailsText += _("Compressed cache file found."); + } + } + if (detailsText) { + var detailsTitle = E( + "label", + { class: "cbi-value-title" }, + _("Service Details"), + ); + var detailsDescr = E( + "div", + { class: "cbi-value-description" }, + _( + "Please %sdonate%s to support development of this project.", + ).format( + "", + "", + ), + ); + var detailsContent = E("div", {}, detailsText); + var detailsField = E("div", { class: "cbi-value-field" }, [ + detailsContent, + E("br"), + E("br"), + detailsDescr, + ]); + detailsDiv = E("div", { class: "cbi-value" }, [ + detailsTitle, + detailsField, + ]); + } + } + var warningsDiv = []; if (reply.ubus.warnings && reply.ubus.warnings.length) { var warningsTitle = E( @@ -479,7 +543,7 @@ var status = baseclass.extend({ { class: "cbi-value-title" }, _("Service Warnings"), ); - var text = ""; + text = ""; reply.ubus.warnings.forEach((element) => { if (element.code && pkg.warningTable[element.code]) { text += pkg.formatMessage( @@ -509,7 +573,7 @@ var status = baseclass.extend({ { class: "cbi-value-title" }, _("Service Errors"), ); - var text = ""; + text = ""; reply.ubus.errors.forEach((element) => { if (element.code && pkg.errorTable[element.code]) { text += pkg.formatMessage( @@ -586,19 +650,38 @@ var status = baseclass.extend({ ui.showModal(null, [ E("p", { class: "spinning" }, _("Syncing cron schedule")), ]); - return syncCron(pkg.Name, "apply").then( - function (result) { - ui.hideModal(); - location.reload(); - }, - function (error) { - ui.hideModal(); - ui.addNotification( - null, - E("p", {}, _("Failed to sync cron schedule")), + return L.resolveDefault(getCronEntry(pkg.Name), {}) + .then(function (response) { + var entry = + (response?.[pkg.Name] && response[pkg.Name].entry) || ""; + if (!entry) { + return Promise.reject(new Error("No cron entry")); + } + entry = entry.replace(/^\s*#\s*/, ""); + entry = entry.replace( + /adblock-fast-auto-(suspended|disabled)/g, + "adblock-fast-auto", ); - }, - ); + return L.resolveDefault(setCronEntry(pkg.Name, entry), { + result: false, + }); + }) + .then( + function (result) { + if (!result || result.result === false) { + throw new Error("Failed to update cron schedule"); + } + ui.hideModal(); + location.reload(); + }, + function (error) { + ui.hideModal(); + ui.addNotification( + null, + E("p", {}, _("Failed to sync cron schedule")), + ); + }, + ); }, }, _("Resync Cron"), @@ -749,6 +832,7 @@ var status = baseclass.extend({ return E("div", {}, [ header, statusDiv, + detailsDiv, warningsDiv, errorsDiv, buttonsDiv, @@ -773,6 +857,8 @@ return L.Class.extend({ getFileUrlFilesizes: getFileUrlFilesizes, syncCron: syncCron, getCronStatus: getCronStatus, + getCronEntry: getCronEntry, + setCronEntry: setCronEntry, getPlatformSupport: getPlatformSupport, getServiceInfo: getServiceInfo, }); diff --git a/applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js b/applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js index 56f6ac9732..0d84eaa391 100644 --- a/applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js +++ b/applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js @@ -8,14 +8,152 @@ "require view"; "require ui"; "require adblock-fast.status as adb"; +/* globals adb */ var pkg = adb.pkg; return view.extend({ + // Helper function to parse cron entry into config values + parseCronEntry: function (cronEntry) { + var defaults = { + auto_update_enabled: "0", + auto_update_mode: "daily", + auto_update_hour: "4", + auto_update_minute: "0", + auto_update_weekday: "0", + auto_update_monthday: "1", + auto_update_every_ndays: "3", + auto_update_every_nhours: "6", + }; + + if (!cronEntry || cronEntry.trim() === "") { + return defaults; + } + + var commented = cronEntry.trim().startsWith("#"); + var parts = cronEntry.replace(/^#\s*/, "").trim().split(/\s+/); + if (parts.length < 6) { + return defaults; + } + + var minute = parts[0]; + var hour = parts[1]; + var dom = parts[2]; + var month = parts[3]; + var dow = parts[4]; + + var isNumber = function (val) { + return /^[0-9]+$/.test(val); + }; + var isStep = function (val) { + return /^\*\/[0-9]+$/.test(val); + }; + + if (month !== "*" || !isNumber(minute)) { + return defaults; + } + + var config = Object.assign({}, defaults, { + auto_update_enabled: commented ? "0" : "1", + auto_update_minute: minute, + }); + + if (isStep(hour)) { + if (dom !== "*" || dow !== "*") { + return defaults; + } + config.auto_update_mode = "every_n_hours"; + config.auto_update_every_nhours = hour.split("/")[1]; + return config; + } + + if (!isNumber(hour)) { + return defaults; + } + + if (isStep(dom)) { + if (dow !== "*") { + return defaults; + } + config.auto_update_mode = "every_n_days"; + config.auto_update_hour = hour; + config.auto_update_every_ndays = dom.split("/")[1]; + return config; + } + + if (dom !== "*") { + if (!isNumber(dom) || dow !== "*") { + return defaults; + } + config.auto_update_mode = "monthly"; + config.auto_update_hour = hour; + config.auto_update_monthday = dom; + return config; + } + + if (dow !== "*") { + if (!isNumber(dow)) { + return defaults; + } + config.auto_update_mode = "weekly"; + config.auto_update_hour = hour; + config.auto_update_weekday = dow; + return config; + } + + config.auto_update_mode = "daily"; + config.auto_update_hour = hour; + return config; + }, + + // Helper function to generate cron entry from config values + generateCronEntry: function (config) { + if (config.auto_update_enabled !== "1") { + return ""; + } + + var minute = config.auto_update_minute || "0"; + var hour, + dom = "*", + dow = "*"; + + switch (config.auto_update_mode) { + case "every_n_hours": + hour = "*/" + (config.auto_update_every_nhours || "6"); + break; + case "every_n_days": + hour = config.auto_update_hour || "4"; + dom = "*/" + (config.auto_update_every_ndays || "3"); + break; + case "monthly": + hour = config.auto_update_hour || "4"; + dom = config.auto_update_monthday || "1"; + break; + case "weekly": + hour = config.auto_update_hour || "4"; + dow = config.auto_update_weekday || "0"; + break; + default: // daily + hour = config.auto_update_hour || "4"; + break; + } + + return ( + minute + + " " + + hour + + " " + + dom + + " * " + + dow + + " /etc/init.d/adblock-fast dl # adblock-fast-auto" + ); + }, + load: function () { return Promise.all([ - L.resolveDefault(adb.getFileUrlFilesizes(pkg.Name), {}), - L.resolveDefault(adb.getPlatformSupport(pkg.Name), {}), + L.resolveDefault(adb.getInitStatus(pkg.Name), {}), + L.resolveDefault(adb.getCronStatus(pkg.Name), {}), L.resolveDefault(L.uci.load(pkg.Name), {}), L.resolveDefault(L.uci.load("dhcp"), {}), L.resolveDefault(L.uci.load("smartdns"), {}), @@ -23,9 +161,10 @@ return view.extend({ }, render: function (data) { + var initData = (data[0] && data[0][pkg.Name]) || {}; var reply = { - sizes: (data[0] && data[0][pkg.Name] && data[0][pkg.Name]["sizes"]) || [], - platform: (data[1] && data[1][pkg.Name]) || { + sizes: initData.file_url || [], + platform: initData.platform || { ipset_installed: false, nft_installed: false, dnsmasq_installed: false, @@ -37,17 +176,26 @@ return view.extend({ unbound_installed: false, leds: [], }, + cronEntry: + (data[1] && data[1][pkg.Name] && data[1][pkg.Name]["entry"]) || "", + cronStatus: (data[1] && data[1][pkg.Name]) || {}, pkg: (!pkg.isObjEmpty(data[2]) && data[2]) || null, dhcp: (!pkg.isObjEmpty(data[3]) && data[3]) || null, smartdns: (!pkg.isObjEmpty(data[4]) && data[4]) || null, }; + + // Parse cron entry into virtual config values + var cronConfig = this.parseCronEntry(reply.cronEntry); + var status, m, s1, s2, s3, o; status = new adb.status(); m = new form.Map(pkg.Name, _("AdBlock-Fast - Configuration")); + this._map = m; s1 = m.section(form.NamedSection, "config", pkg.Name); s1.tab("tab_basic", _("Basic Configuration")); + s1.tab("tab_schedule", _("List Updates Schedule")); s1.tab("tab_advanced", _("Advanced Configuration")); var text = _( @@ -208,7 +356,9 @@ return view.extend({ } else return "*"; }; o.write = function (section_id, formvalue) { - L.uci.set(pkg.Name, section_id, "dnsmasq_instance", formvalue); + if (formvalue !== "+") { + L.uci.set(pkg.Name, section_id, "dnsmasq_instance", [formvalue]); + } }; o = s1.taboption( @@ -271,7 +421,9 @@ return view.extend({ } else return "*"; }; o.write = function (section_id, formvalue) { - L.uci.set(pkg.Name, section_id, "smartdns_instance", formvalue); + if (formvalue !== "+") { + L.uci.set(pkg.Name, section_id, "smartdns_instance", [formvalue]); + } }; o = s1.taboption( @@ -340,18 +492,7 @@ return view.extend({ } o = s1.taboption( - "tab_advanced", - form.ListValue, - "config_update_enabled", - _("Automatic Config Update"), - _("Perform config update before downloading the block/allow-lists."), - ); - o.value("0", _("Disable")); - o.value("1", _("Enable")); - o.default = "0"; - - o = s1.taboption( - "tab_advanced", + "tab_schedule", form.ListValue, "auto_update_enabled", _("Automatic List Update"), @@ -360,9 +501,13 @@ return view.extend({ o.value("0", _("Disable")); o.value("1", _("Enable")); o.default = "0"; + // Override to use cron data instead of UCI + o.cfgvalue = function (section_id) { + return cronConfig.auto_update_enabled; + }; o = s1.taboption( - "tab_advanced", + "tab_schedule", form.ListValue, "auto_update_mode", _("Schedule Type"), @@ -375,42 +520,47 @@ return view.extend({ o.value("every_n_hours", _("Every N hours")); o.default = "daily"; o.depends("auto_update_enabled", "1"); + // Override to use cron data instead of UCI + o.cfgvalue = function (section_id) { + return cronConfig.auto_update_mode; + }; o = s1.taboption( - "tab_advanced", + "tab_schedule", form.ListValue, - "auto_update_hour", - _("Update Hour"), - _("Hour of day to run the update (0-23)."), + "auto_update_every_ndays", + _("Every N days"), + _("Run once every N days."), ); - for (var i = 0; i < 24; i++) { - var hourLabel = i < 10 ? "0" + i : "" + i; - o.value(String(i), hourLabel); + for (let i = 1; i <= 31; i++) { + o.value(String(i), String(i)); } - o.default = "4"; - o.depends({ auto_update_enabled: "1", auto_update_mode: "daily" }); - o.depends({ auto_update_enabled: "1", auto_update_mode: "weekly" }); - o.depends({ auto_update_enabled: "1", auto_update_mode: "monthly" }); + o.default = "3"; o.depends({ auto_update_enabled: "1", auto_update_mode: "every_n_days" }); + // Override to use cron data instead of UCI + o.cfgvalue = function (section_id) { + return cronConfig.auto_update_every_ndays; + }; o = s1.taboption( - "tab_advanced", + "tab_schedule", form.ListValue, - "auto_update_minute", - _("Update Minute"), - _( - "Minute of hour to run the update (0-59). In 'Every N hours' mode, updates run at the selected minute within each interval.", - ), + "auto_update_every_nhours", + _("Every N hours"), + _("Run once every N hours."), ); - for (var i = 0; i < 60; i++) { - var minuteLabel = i < 10 ? "0" + i : "" + i; - o.value(String(i), minuteLabel); + for (let i = 1; i <= 23; i++) { + o.value(String(i), String(i)); } - o.default = "0"; - o.depends("auto_update_enabled", "1"); + o.default = "6"; + o.depends({ auto_update_enabled: "1", auto_update_mode: "every_n_hours" }); + // Override to use cron data instead of UCI + o.cfgvalue = function (section_id) { + return cronConfig.auto_update_every_nhours; + }; o = s1.taboption( - "tab_advanced", + "tab_schedule", form.ListValue, "auto_update_weekday", _("Day of Week"), @@ -425,45 +575,79 @@ return view.extend({ o.value("6", _("Saturday")); o.default = "0"; o.depends({ auto_update_enabled: "1", auto_update_mode: "weekly" }); + // Override to use cron data instead of UCI + o.cfgvalue = function (section_id) { + return cronConfig.auto_update_weekday; + }; o = s1.taboption( - "tab_advanced", + "tab_schedule", form.ListValue, "auto_update_monthday", _("Day of Month"), _("Run on the selected day of month."), ); - for (var i = 1; i <= 31; i++) { + for (let i = 1; i <= 31; i++) { o.value(String(i), String(i)); } o.default = "1"; o.depends({ auto_update_enabled: "1", auto_update_mode: "monthly" }); + // Override to use cron data instead of UCI + o.cfgvalue = function (section_id) { + return cronConfig.auto_update_monthday; + }; o = s1.taboption( - "tab_advanced", + "tab_schedule", form.ListValue, - "auto_update_every_ndays", - _("Every N days"), - _("Run once every N days."), + "auto_update_hour", + _("Update Hour"), + _("Hour of day to run the update (0-23)."), ); - for (var i = 1; i <= 31; i++) { - o.value(String(i), String(i)); + for (let i = 0; i < 24; i++) { + var hourLabel = i < 10 ? "0" + i : "" + i; + o.value(String(i), hourLabel); } - o.default = "3"; + o.default = "4"; + o.depends({ auto_update_enabled: "1", auto_update_mode: "daily" }); + o.depends({ auto_update_enabled: "1", auto_update_mode: "weekly" }); + o.depends({ auto_update_enabled: "1", auto_update_mode: "monthly" }); o.depends({ auto_update_enabled: "1", auto_update_mode: "every_n_days" }); + // Override to use cron data instead of UCI + o.cfgvalue = function (section_id) { + return cronConfig.auto_update_hour; + }; o = s1.taboption( - "tab_advanced", + "tab_schedule", form.ListValue, - "auto_update_every_nhours", - _("Every N hours"), - _("Run once every N hours."), + "auto_update_minute", + _("Update Minute"), + _( + "Minute of hour to run the update (0-59). In 'Every N hours' mode, updates run at the selected minute within each interval.", + ), ); - for (var i = 1; i <= 23; i++) { - o.value(String(i), String(i)); + for (let i = 0; i < 60; i++) { + var minuteLabel = i < 10 ? "0" + i : "" + i; + o.value(String(i), minuteLabel); } - o.default = "6"; - o.depends({ auto_update_enabled: "1", auto_update_mode: "every_n_hours" }); + o.default = "0"; + o.depends("auto_update_enabled", "1"); + // Override to use cron data instead of UCI + o.cfgvalue = function (section_id) { + return cronConfig.auto_update_minute; + }; + + o = s1.taboption( + "tab_advanced", + form.ListValue, + "config_update_enabled", + _("Automatic Config Update"), + _("Perform config update before downloading the block/allow-lists."), + ); + o.value("0", _("Disable")); + o.value("1", _("Enable")); + o.default = "0"; o = s1.taboption( "tab_advanced", @@ -676,25 +860,66 @@ return view.extend({ }, handleSave: function (ev) { - return this.super("handleSave", [ev]); + var map = this._map; + if (!map) { + return this.super("handleSave", [ev]); + } + + // Collect virtual scheduling values + var schedulingConfig = {}; + var schedulingFields = [ + "auto_update_enabled", + "auto_update_mode", + "auto_update_hour", + "auto_update_minute", + "auto_update_weekday", + "auto_update_monthday", + "auto_update_every_ndays", + "auto_update_every_nhours", + ]; + + schedulingFields.forEach(function (fieldName) { + var match = map.lookupOption(fieldName, "config"); + if (match && match[0].isValid("config")) { + schedulingConfig[fieldName] = match[0].formvalue("config"); + } + }); + + // Generate cron entry from config + var cronEntry = this.generateCronEntry(schedulingConfig); + + // Save cron entry directly + var savePromise = L.resolveDefault(adb.setCronEntry(pkg.Name, cronEntry), { + result: false, + }).then(function (result) { + if (!result || result.result === false) { + ui.addNotification( + null, + E("p", {}, _("Failed to update cron schedule.")), + ); + return Promise.reject(new Error("Failed to update cron schedule")); + } + + // Remove scheduling values from UCI before saving + schedulingFields.forEach(function (fieldName) { + var match = map.lookupOption(fieldName, "config"); + if (match) { + match[0].remove("config"); + } + }); + + // Save the rest of UCI config + return Promise.resolve(); + }); + + return savePromise.then(() => { + return this.super("handleSave", [ev]); + }); }, handleSaveApply: function (ev, mode) { - return this.super("handleSave", [ev]).then(function () { - var onApplied = function () { - L.resolveDefault(adb.syncCron(pkg.Name, "apply"), { - result: false, - }).then(function (result) { - if (!result || result.result === false) { - ui.addNotification( - null, - E("p", {}, _("Failed to update cron schedule.")), - ); - } - }); - }; - document.addEventListener("uci-applied", onApplied, { once: true }); - ui.changes.apply(mode == "0"); + return this.handleSave(ev).then(function () { + return ui.changes.apply(mode == "0"); }); }, }); diff --git a/applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/status/include/70_adblock-fast.js b/applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/status/include/70_adblock-fast.js index 19db4c4711..bec206e2a4 100644 --- a/applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/status/include/70_adblock-fast.js +++ b/applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/status/include/70_adblock-fast.js @@ -3,6 +3,7 @@ "require form"; "require baseclass"; "require adblock-fast.status as adb"; +/* globals adb */ const { pkg } = adb; @@ -14,70 +15,53 @@ return baseclass.extend({ }, render: function (data) { - var reply = { - status: (data[0] && data[0][pkg.Name]) || { - enabled: false, - status: null, - running: null, - version: null, - errors: [], - warnings: [], - force_dns_active: null, - force_dns_ports: [], - entries: null, - dns: null, - outputFile: null, - outputCache: null, - outputGzip: null, - outputFileExists: null, - outputCacheExists: null, - outputGzipExists: null, - leds: [], - }, - }; + try { + var status = (data[0] && data[0][pkg.Name]) || {}; - var cacheText; - if (reply.status.outputCacheExists) { - cacheText = _("Cache file"); - } else if (reply.status.outputGzipExists) { - cacheText = _("Compressed cache"); - } - var forceDnsText = ""; - if (reply.status.force_dns_active) { - reply.status.force_dns_ports.forEach((element) => { - forceDnsText += element + " "; - }); - } else { - forceDnsText = "-"; - } + var cacheText = "-"; + if (status.outputCacheExists) { + cacheText = _("Cache file"); + } else if (status.outputGzipExists) { + cacheText = _("Compressed cache"); + } - var table = E( - "table", - { class: "table", id: "adblock-fast_status_table" }, - [ - E("tr", { class: "tr table-titles" }, [ - E("th", { class: "th" }, _("Status")), - E("th", { class: "th" }, _("Version")), - E("th", { class: "th" }, _("DNS Service")), - E("th", { class: "th" }, _("Blocked Domains")), - E("th", { class: "th" }, _("Cache")), - E("th", { class: "th" }, _("Force DNS Ports")), - ]), - E("tr", { class: "tr" }, [ - E( - "td", - { class: "td" }, - pkg.statusTable[reply.status.status] || _("Unknown") - ), - E("td", { class: "td" }, reply.status.version || _("-")), - E("td", { class: "td" }, reply.status.dns || _("-")), - E("td", { class: "td" }, reply.status.entries || _("-")), - E("td", { class: "td" }, cacheText || _("-")), - E("td", { class: "td" }, forceDnsText || _("-")), - ]), - ] - ); + var forceDnsText = "-"; + if (status.force_dns_active && Array.isArray(status.force_dns_ports)) { + var ports = status.force_dns_ports.join(" "); + if (ports) forceDnsText = ports; + } - return table; + var table = E( + "table", + { class: "table", id: "adblock-fast_status_table" }, + [ + E("tr", { class: "tr table-titles" }, [ + E("th", { class: "th" }, _("Status")), + E("th", { class: "th" }, _("Version")), + E("th", { class: "th" }, _("DNS Service")), + E("th", { class: "th" }, _("Blocked Domains")), + E("th", { class: "th" }, _("Cache")), + E("th", { class: "th" }, _("Force DNS Ports")), + ]), + E("tr", { class: "tr" }, [ + E( + "td", + { class: "td" }, + (pkg.statusTable && pkg.statusTable[status.status]) || _("Unknown") + ), + E("td", { class: "td" }, status.version || _("-")), + E("td", { class: "td" }, status.dns || _("-")), + E("td", { class: "td" }, status.entries != null ? String(status.entries) : _("-")), + E("td", { class: "td" }, cacheText), + E("td", { class: "td" }, forceDnsText), + ]), + ] + ); + + return table; + } catch (e) { + return E("div", { class: "alert-message warning" }, + _("Unable to retrieve %s status").format("AdBlock-Fast")); + } }, }); diff --git a/applications/luci-app-adblock-fast/po/templates/adblock-fast.pot b/applications/luci-app-adblock-fast/po/templates/adblock-fast.pot index 22d1a7016f..3d66c46578 100644 --- a/applications/luci-app-adblock-fast/po/templates/adblock-fast.pot +++ b/applications/luci-app-adblock-fast/po/templates/adblock-fast.pot @@ -9,15 +9,13 @@ msgstr "" msgid "%s is not installed or not found" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/status/include/70_adblock-fast.js:72 -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/status/include/70_adblock-fast.js:73 -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/status/include/70_adblock-fast.js:74 -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/status/include/70_adblock-fast.js:75 -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/status/include/70_adblock-fast.js:76 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/status/include/70_adblock-fast.js:52 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/status/include/70_adblock-fast.js:53 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/status/include/70_adblock-fast.js:54 msgid "-" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:649 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:833 msgid "Action" msgstr "" @@ -25,13 +23,13 @@ msgstr "" msgid "Active" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:187 -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:251 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:335 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:401 msgid "Ad-blocking on all instances" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:188 -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:252 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:336 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:402 msgid "Ad-blocking on select instances" msgstr "" @@ -39,87 +37,87 @@ msgstr "" msgid "AdBlock Fast" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/status/include/70_adblock-fast.js:10 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/status/include/70_adblock-fast.js:11 msgid "AdBlock-Fast" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:600 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:784 msgid "AdBlock-Fast - Allowed and Blocked Domains" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:624 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:808 msgid "AdBlock-Fast - Allowed and Blocked Lists URLs" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:47 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:193 msgid "AdBlock-Fast - Configuration" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/adblock-fast/status.js:402 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/adblock-fast/status.js:440 msgid "AdBlock-Fast - Status" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:476 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:660 msgid "Add IPv6 entries" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:473 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:657 msgid "Add IPv6 entries to block-list." msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:51 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:199 msgid "Advanced Configuration" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:650 -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:655 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:834 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:839 msgid "Allow" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:608 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:792 msgid "Allowed Domains" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:537 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:721 msgid "" "Attempt to create a compressed cache of block-list in the persistent memory." msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:346 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:645 msgid "Automatic Config Update" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:357 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:498 msgid "Automatic List Update" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:50 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:197 msgid "Basic Configuration" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:651 -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:655 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:835 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:839 msgid "Block" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:616 -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/status/include/70_adblock-fast.js:62 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:800 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/status/include/70_adblock-fast.js:42 msgid "Blocked Domains" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/adblock-fast/status.js:415 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/adblock-fast/status.js:487 msgid "Blocking %s domains (with %s)." msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/status/include/70_adblock-fast.js:63 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/status/include/70_adblock-fast.js:43 msgid "Cache" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/status/include/70_adblock-fast.js:41 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/status/include/70_adblock-fast.js:23 msgid "Cache file" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/adblock-fast/status.js:450 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/adblock-fast/status.js:504 msgid "Cache file found." msgstr "" @@ -127,15 +125,15 @@ msgstr "" msgid "Can't detect free RAM" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/status/include/70_adblock-fast.js:43 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/status/include/70_adblock-fast.js:25 msgid "Compressed cache" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/adblock-fast/status.js:420 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/adblock-fast/status.js:492 msgid "Compressed cache file created." msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/adblock-fast/status.js:452 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/adblock-fast/status.js:506 msgid "Compressed cache file found." msgstr "" @@ -143,7 +141,7 @@ msgstr "" msgid "Config (%s) validation failure!" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:318 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:470 msgid "Controls system log and console output verbosity." msgstr "" @@ -165,81 +163,81 @@ msgstr "" msgid "Cron service is not enabled or running. Enable it with: %s." msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:510 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:694 msgid "Curl download retry" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:497 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:681 msgid "Curl maximum file size (in bytes)" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:132 -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/status/include/70_adblock-fast.js:61 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:280 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/status/include/70_adblock-fast.js:41 msgid "DNS Service" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:54 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:202 msgid "DNS resolution option, see the %sREADME%s for details." msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:371 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:516 msgid "Daily" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:433 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:587 msgid "Day of Month" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:416 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:566 msgid "Day of Week" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:548 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:732 msgid "Directory for compressed cache file" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:550 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:734 msgid "" "Directory for compressed cache file of block-list in the persistent memory." msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/adblock-fast/status.js:676 -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:349 -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:360 -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:568 -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:581 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/adblock-fast/status.js:759 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:501 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:648 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:752 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:765 msgid "Disable" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:592 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:776 msgid "Disable Debugging" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/adblock-fast/status.js:446 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/adblock-fast/status.js:459 msgid "Disabled" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/adblock-fast/status.js:670 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/adblock-fast/status.js:753 msgid "Disabling %s service" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:164 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:312 msgid "Dnsmasq Config File URL" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:475 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:659 msgid "Do not add IPv6 entries" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:540 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:724 msgid "Do not store compressed cache" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:527 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:711 msgid "Do not use simultaneous processing" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:487 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:671 msgid "Download time-out (in seconds)" msgstr "" @@ -247,63 +245,63 @@ msgstr "" msgid "Downloading lists" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/adblock-fast/status.js:657 -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:350 -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:361 -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:569 -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:582 -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:645 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/adblock-fast/status.js:740 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:502 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:649 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:753 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:766 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:829 msgid "Enable" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:589 -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:593 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:773 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:777 msgid "Enable Debugging" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:578 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:762 msgid "" "Enable RFC 1123 compliant domain validation for dnsmasq block-lists to " "remove invalid entries." msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:576 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:760 msgid "Enable dnsmasq domain validation" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:563 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:747 msgid "Enable dnsmasq sanity check" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:565 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:749 msgid "" "Enable sanity check for dnsmasq block-list processing to detect and report " "issues." msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:358 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:499 msgid "Enable scheduled list redownloads via /etc/init.d/adblock-fast dl." msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:590 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:774 msgid "Enables debug output to /tmp/adblock-fast.log." msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/adblock-fast/status.js:651 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/adblock-fast/status.js:734 msgid "Enabling %s service" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/adblock-fast/status.js:523 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/adblock-fast/status.js:587 msgid "Errors encountered, please check the %sREADME%s" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:374 -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:446 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:519 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:532 msgid "Every N days" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:375 -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:459 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:520 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:549 msgid "Every N hours" msgstr "" @@ -395,7 +393,7 @@ msgstr "" msgid "Failed to stop %s" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/adblock-fast/status.js:598 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/adblock-fast/status.js:681 msgid "Failed to sync cron schedule" msgstr "" @@ -403,15 +401,15 @@ msgstr "" msgid "Failed to unpack compressed cache" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:691 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:898 msgid "Failed to update cron schedule." msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/status/include/70_adblock-fast.js:64 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/status/include/70_adblock-fast.js:44 msgid "Force DNS Ports" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/adblock-fast/status.js:423 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/adblock-fast/status.js:495 msgid "Force DNS ports:" msgstr "" @@ -419,19 +417,19 @@ msgstr "" msgid "Force Reloading" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:306 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:458 msgid "Force Router DNS" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:310 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:462 msgid "Force Router DNS server to all local devices" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/adblock-fast/status.js:571 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/adblock-fast/status.js:635 msgid "Force redownloading %s block lists" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:307 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:459 msgid "Forces Router DNS use on local devices, also known as DNS Hijacking." msgstr "" @@ -439,7 +437,7 @@ msgstr "" msgid "Free ram (%s) is not enough to process all enabled block-lists" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:424 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:574 msgid "Friday" msgstr "" @@ -451,31 +449,31 @@ msgstr "" msgid "Heartbeat domain is not accessible after resolver restart" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:384 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:605 msgid "Hour of day to run the update (0-23)." msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:472 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:656 msgid "IPv6 Support" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:499 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:683 msgid "" "If curl is installed and detected, it would not download files bigger than " "this." msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:512 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:696 msgid "" "If curl is installed and detected, it would retry download this many times " "on timeout/fail." msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:609 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:793 msgid "Individual domains to be allowed." msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:617 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:801 msgid "Individual domains to be blocked." msgstr "" @@ -490,21 +488,25 @@ msgstr "" msgid "Invalid compressed cache directory '%s'" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:330 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:482 msgid "LED to indicate status" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:524 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:708 msgid "" "Launch all lists downloads and processing simultaneously, reducing service " "start time." msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:309 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:461 msgid "Let local devices use their own DNS servers if set" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:402 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:198 +msgid "List Updates Schedule" +msgstr "" + +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:627 msgid "" "Minute of hour to run the update (0-59). In 'Every N hours' mode, updates " "run at the selected minute within each interval." @@ -514,27 +516,27 @@ msgstr "" msgid "Missing recommended package: '%s'" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:420 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:570 msgid "Monday" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:373 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:518 msgid "Monthly" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:667 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:851 msgid "Name" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:658 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:842 msgid "Name/URL" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:253 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:403 msgid "No Ad-blocking on SmartDNS" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:189 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:337 msgid "No Ad-blocking on dnsmasq" msgstr "" @@ -546,52 +548,52 @@ msgstr "" msgid "No blocked list URLs nor blocked-domains enabled" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/adblock-fast/status.js:466 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/adblock-fast/status.js:474 msgid "Not installed or not found" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:317 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:469 msgid "Output Verbosity Setting" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/adblock-fast/status.js:619 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/adblock-fast/status.js:702 msgid "Pause" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/adblock-fast/status.js:614 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/adblock-fast/status.js:697 msgid "Pausing %s" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:347 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:646 msgid "Perform config update before downloading the block/allow-lists." msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:332 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:484 msgid "Pick the LED not already used in %sSystem LED Configuration%s." msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:281 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:433 msgid "Pick the SmartDNS instance(s) for ad-blocking" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:218 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:368 msgid "Pick the dnsmasq instance(s) for ad-blocking" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/adblock-fast/status.js:433 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/adblock-fast/status.js:519 msgid "Please %sdonate%s to support development of this project." msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:62 -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:67 -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:72 -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:77 -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:84 -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:91 -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:100 -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:107 -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:114 -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:123 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:210 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:215 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:220 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:225 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:232 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:239 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:248 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:255 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:262 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:271 msgid "Please note that %s is not supported on this system." msgstr "" @@ -599,7 +601,7 @@ msgstr "" msgid "Processing lists" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/adblock-fast/status.js:577 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/adblock-fast/status.js:641 msgid "Redownload" msgstr "" @@ -613,24 +615,24 @@ msgstr "" msgid "Restarting" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/adblock-fast/status.js:370 -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/adblock-fast/status.js:604 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/adblock-fast/status.js:392 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/adblock-fast/status.js:687 msgid "Resync Cron" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:434 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:588 msgid "Run on the selected day of month." msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:417 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:567 msgid "Run on the selected weekday." msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:447 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:533 msgid "Run once every N days." msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:460 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:550 msgid "Run once every N hours." msgstr "" @@ -642,51 +644,55 @@ msgstr "" msgid "Sanity check discovered leading dots in %s" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:425 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:575 msgid "Saturday" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:368 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:513 msgid "Schedule Type" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:370 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:515 msgid "Select how often the update should run." msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/adblock-fast/status.js:720 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/adblock-fast/status.js:803 msgid "Service Control" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/adblock-fast/status.js:510 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/adblock-fast/status.js:513 +msgid "Service Details" +msgstr "" + +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/adblock-fast/status.js:574 msgid "Service Errors" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/adblock-fast/status.js:406 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/adblock-fast/status.js:444 msgid "Service Status" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/adblock-fast/status.js:480 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/adblock-fast/status.js:544 msgid "Service Warnings" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:522 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:706 msgid "Simultaneous processing" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:632 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:816 msgid "Size" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:642 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:826 msgid "Size: %s" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:321 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:473 msgid "Some output" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/adblock-fast/status.js:558 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/adblock-fast/status.js:622 msgid "Start" msgstr "" @@ -694,19 +700,19 @@ msgstr "" msgid "Starting" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/adblock-fast/status.js:552 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/adblock-fast/status.js:616 msgid "Starting %s service" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/status/include/70_adblock-fast.js:59 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/status/include/70_adblock-fast.js:39 msgid "Status" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/adblock-fast/status.js:638 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/adblock-fast/status.js:721 msgid "Stop" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:488 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:672 msgid "Stop the download if it is stalled for set number of seconds." msgstr "" @@ -714,27 +720,27 @@ msgstr "" msgid "Stopped" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/adblock-fast/status.js:632 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/adblock-fast/status.js:715 msgid "Stopping %s service" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:541 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:725 msgid "Store compressed cache" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:535 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:719 msgid "Store compressed cache file on router" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:419 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:569 msgid "Sunday" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:320 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:472 msgid "Suppress output" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/adblock-fast/status.js:587 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/adblock-fast/status.js:651 msgid "Syncing cron schedule" msgstr "" @@ -773,34 +779,38 @@ msgstr "" msgid "The principal package (adblock-fast) is outdated, please update it" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:423 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:573 msgid "Thursday" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:421 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:571 msgid "Tuesday" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:671 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:855 msgid "URL" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:166 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:314 msgid "" "URL to the external dnsmasq config file, see the %sREADME%s for details." msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:625 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:809 msgid "URLs to file(s) containing lists to be allowed or blocked." msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:636 -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:663 -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/status/include/70_adblock-fast.js:70 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/status/include/70_adblock-fast.js:64 +msgid "Unable to retrieve %s status" +msgstr "" + +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:820 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:847 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/status/include/70_adblock-fast.js:50 msgid "Unknown" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/adblock-fast/status.js:520 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/adblock-fast/status.js:584 msgid "Unknown error" msgstr "" @@ -808,23 +818,23 @@ msgstr "" msgid "Unknown message" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/adblock-fast/status.js:490 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/adblock-fast/status.js:554 msgid "Unknown warning" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:383 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:604 msgid "Update Hour" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:400 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:625 msgid "Update Minute" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:243 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:393 msgid "Use ad-blocking on the SmartDNS instance(s)" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:179 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:327 msgid "Use ad-blocking on the dnsmasq instance(s)" msgstr "" @@ -833,19 +843,19 @@ msgid "" "Use of external dnsmasq config file detected, please set '%s' option to '%s'" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:528 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:712 msgid "Use simultaneous processing" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:322 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:474 msgid "Verbose output" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/status/include/70_adblock-fast.js:60 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/status/include/70_adblock-fast.js:40 msgid "Version" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/adblock-fast/status.js:409 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/adblock-fast/status.js:447 msgid "Version %s" msgstr "" @@ -857,43 +867,43 @@ msgstr "" msgid "Waiting for trigger (on_start)" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:422 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:572 msgid "Wednesday" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:372 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:517 msgid "Weekly" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:245 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:395 msgid "" "You can limit the ad-blocking to the specific SmartDNS instance(s) (%smore " "information%s)." msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:181 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:329 msgid "" "You can limit the ad-blocking to the specific dnsmasq instance(s) (%smore " "information%s)." msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:136 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:284 msgid "dnsmasq additional hosts" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:137 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:285 msgid "dnsmasq config" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:139 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:287 msgid "dnsmasq ipset" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:142 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:290 msgid "dnsmasq nft set" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:144 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:292 msgid "dnsmasq servers file" msgstr "" @@ -913,22 +923,22 @@ msgstr "" msgid "failed to restore backup file %s" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:335 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:487 msgid "none" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:147 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:295 msgid "smartdns domain set" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:149 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:297 msgid "smartdns ipset" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:152 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:300 msgid "smartdns nft set" msgstr "" -#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:156 +#: applications/luci-app-adblock-fast/htdocs/luci-static/resources/view/adblock-fast/overview.js:304 msgid "unbound adblock list" msgstr "" diff --git a/applications/luci-app-adblock-fast/root/usr/libexec/rpcd/luci.adblock-fast b/applications/luci-app-adblock-fast/root/usr/libexec/rpcd/luci.adblock-fast deleted file mode 100755 index 861cadb934..0000000000 --- a/applications/luci-app-adblock-fast/root/usr/libexec/rpcd/luci.adblock-fast +++ /dev/null @@ -1,519 +0,0 @@ -#!/bin/sh -# Copyright 2023 MOSSDeF, Stan Grishin (stangri@melmac.ca) -# shellcheck disable=SC2018,SC2019,SC3043,SC3060 - -# TechRef: https://openwrt.org/docs/techref/rpcd -# TESTS -# ubus -v list luci.adblock-fast -# ubus -S call luci.adblock-fast getFileUrlFilesizes '{"name": "adblock-fast" }' -# ubus -S call luci.adblock-fast getInitList '{"name": "adblock-fast" }' -# ubus -S call luci.adblock-fast getInitStatus '{"name": "adblock-fast" }' -# ubus -S call luci.adblock-fast getPlatformSupport '{"name": "adblock-fast" }' -# ubus -S call luci.adblock-fast getUbusInfo '{"name": "adblock-fast" }' -# ubus -S call luci.adblock-fast setInitAction '{"name": "adblock-fast", "action": "start" }' -# ubus -S call luci.adblock-fast setInitAction '{"name": "adblock-fast", "action": "dl" }' -# ubus -S call luci.adblock-fast setInitAction '{"name": "adblock-fast", "action": "pause" }' -# ubus -S call luci.adblock-fast setInitAction '{"name": "adblock-fast", "action": "stop" }' - -readonly rpcdCompat='11' -readonly adbFunctionsFile="${IPKG_INSTROOT}/etc/init.d/adblock-fast" -if [ -s "$adbFunctionsFile" ]; then -# shellcheck source=../../../../../adblock-fast/files/etc/init.d/adblock-fast - . "$adbFunctionsFile" -else - logger -t adblock-fast 'error' "adblock-fast init.d file ($adbFunctionsFile) not found!" - print_json_string 'error' "adblock-fast init.d file ($adbFunctionsFile) not found!" -fi - -get_file_url_filesizes() { - _get_file_url_size() { - local url size - config_get url "$1" 'url' - config_get size "$1" 'size' - if [ -z "$size" ]; then - size="$(get_url_filesize "$url")" - uci_set "$name" "$1" 'size' "$size" - fi - json_add_object - json_add_string 'url' "$url" - json_add_int 'size' "$size" - json_close_object - } - local name="$1" i - json_init - json_add_object "$name" - json_add_array 'sizes' - config_load "$name" - config_foreach _get_file_url_size 'file_url' - json_close_array - json_close_object - json_dump - json_cleanup - uci_changes "$name" && uci_commit "$name" -} - -update_cron() { - local action="$1" - local cron_file="/etc/crontabs/root" - local tmp_file - local auto_enabled add_line - - tmp_file="$(mktemp /tmp/adblock-fast.cron.XXXXXX)" || return 1 - - config_load "$packageName" - - if [ -f "$cron_file" ]; then - if [ -s "$cron_file" ]; then - grep -v -E "/etc/init.d/${packageName}[[:space:]]+dl" "$cron_file" > "$tmp_file" || true - else - : > "$tmp_file" - fi - else - : > "$tmp_file" && touch "$cron_file" || { rm -f "$tmp_file"; return 1; } - fi - - config_get auto_enabled 'config' 'auto_update_enabled' '0' - add_line=0 - case "$action" in - disable|stop) - add_line=0 - ;; - enable) - if [ "$auto_enabled" = "1" ] && is_running "$packageName"; then - add_line=1 - fi - ;; - start) - [ "$auto_enabled" = "1" ] && add_line=1 - ;; - *) - if [ "$auto_enabled" = "1" ] && [ "$(is_enabled "$packageName")" = "1" ] && is_running "$packageName"; then - add_line=1 - fi - ;; - esac - - if [ "$add_line" = "1" ]; then - local mode minute hour wday mday ndays nhours dom dow - config_get mode 'config' 'auto_update_mode' 'daily' - config_get minute 'config' 'auto_update_minute' '0' - config_get hour 'config' 'auto_update_hour' '4' - config_get wday 'config' 'auto_update_weekday' '0' - config_get mday 'config' 'auto_update_monthday' '1' - config_get ndays 'config' 'auto_update_every_ndays' '3' - config_get nhours 'config' 'auto_update_every_nhours' '6' - dom="*" - dow="*" - case "$mode" in - weekly) - dow="$wday" - ;; - monthly) - dom="$mday" - ;; - every_n_days) - dom="*/$ndays" - ;; - every_n_hours) - hour="*/$nhours" - ;; - esac - printf '%s %s %s * %s /etc/init.d/%s dl # adblock-fast-auto\n' "$minute" "$hour" "$dom" "$dow" "$packageName" >> "$tmp_file" - fi - - if mv "$tmp_file" "$cron_file"; then - chmod 600 "$cron_file" 2>/dev/null - else - rm -f "$tmp_file" - return 1 - fi - - [ -x /etc/init.d/cron ] && /etc/init.d/cron reload >/dev/null 2>&1 - return 0 -} - -sync_cron() { - local name action - name="$(basename "$1")" - action="$2" - [ "$name" = "$packageName" ] || { print_json_bool 'result' '0'; return 1; } - if update_cron "$action"; then - print_json_bool 'result' '1' - else - print_json_bool 'result' '0' - fi -} - -get_cron_status() { - local name auto_enabled cron_init cron_bin cron_enabled cron_running - local cron_line_present cron_line_match - local mode minute hour wday mday ndays nhours dom dow - local cron_file line line_count match_count - name="$(basename "$1")" - name="${name:-$packageName}" - - config_load "$packageName" - config_get auto_enabled 'config' 'auto_update_enabled' '0' - if [ "$auto_enabled" = "1" ]; then - auto_enabled=1 - else - auto_enabled=0 - fi - - cron_init=0 - cron_bin=0 - cron_enabled=0 - cron_running=0 - - [ -x /etc/init.d/cron ] && cron_init=1 - if command -v crond >/dev/null 2>&1 || [ -x /usr/sbin/crond ]; then - cron_bin=1 - fi - if [ "$cron_init" = "1" ] && /etc/init.d/cron enabled >/dev/null 2>&1; then - cron_enabled=1 - fi - if [ "$cron_init" = "1" ] && /etc/init.d/cron status >/dev/null 2>&1; then - cron_running=1 - elif pidof crond >/dev/null 2>&1; then - cron_running=1 - fi - - cron_line_present=0 - cron_line_match=0 - - config_get mode 'config' 'auto_update_mode' 'daily' - config_get minute 'config' 'auto_update_minute' '0' - config_get hour 'config' 'auto_update_hour' '4' - config_get wday 'config' 'auto_update_weekday' '0' - config_get mday 'config' 'auto_update_monthday' '1' - config_get ndays 'config' 'auto_update_every_ndays' '3' - config_get nhours 'config' 'auto_update_every_nhours' '6' - dom="*" - dow="*" - case "$mode" in - weekly) - dow="$wday" - ;; - monthly) - dom="$mday" - ;; - every_n_days) - dom="*/$ndays" - ;; - every_n_hours) - hour="*/$nhours" - ;; - esac - - cron_file="/etc/crontabs/root" - line_count=0 - match_count=0 - if [ -s "$cron_file" ]; then - set -f - while IFS= read -r line; do - case "$line" in - ''|[[:space:]]\#*|\#*) continue ;; - esac - line_count=$((line_count + 1)) - set -- $line - if [ "${1#@}" != "$1" ]; then - continue - fi - if [ "${6:-}" = "/etc/init.d/$packageName" ] && [ "${7:-}" = "dl" ] && \ - [ "$1" = "$minute" ] && [ "$2" = "$hour" ] && [ "$3" = "$dom" ] && \ - [ "$4" = "*" ] && [ "$5" = "$dow" ]; then - match_count=$((match_count + 1)) - fi - done <<-EOF - $(grep -v -E '^[[:space:]]*#' "$cron_file" | grep -E "/etc/init.d/${packageName}[[:space:]]+dl" 2>/dev/null) - EOF - set +f - fi - if [ "$line_count" -gt 0 ]; then - cron_line_present=1 - fi - if [ "$line_count" -eq 1 ] && [ "$match_count" -eq 1 ]; then - cron_line_match=1 - fi - - json_init - json_add_object "$name" - json_add_boolean 'auto_update_enabled' "$auto_enabled" - json_add_boolean 'cron_init' "$cron_init" - json_add_boolean 'cron_bin' "$cron_bin" - json_add_boolean 'cron_enabled' "$cron_enabled" - json_add_boolean 'cron_running' "$cron_running" - json_add_boolean 'cron_line_present' "$cron_line_present" - json_add_boolean 'cron_line_match' "$cron_line_match" - json_close_object - json_dump - json_cleanup -} - -get_init_list() { - local name - name="$(basename "$1")" - name="${name:-$packageName}" - json_init - json_add_object "$name" - json_add_boolean 'enabled' "$(is_enabled "$name")" - if is_running "$name"; then - json_add_boolean 'running' '1' - else - json_add_boolean 'running' '0' - fi - json_close_object - json_dump - json_cleanup -} - -set_init_action() { - local action="$2" cmd - [ "$(basename "$1")" = "$packageName" ] || { print_json_bool 'result' '0'; return 1; } - if [ ! -f "/etc/init.d/$packageName" ]; then - print_json_string 'error' 'Init script not found!' - return - fi - case $action in - enable) - cmd="/etc/init.d/${packageName} ${action}" - cmd="${cmd} && uci_set ${packageName} config enabled 1 && uci_commit $packageName" - ;; - disable) - cmd="/etc/init.d/${packageName} ${action}" - cmd="${cmd} && uci_set ${packageName} config enabled 0 && uci_commit $packageName" - ;; - start|stop|reload|restart|dl|pause) - cmd="/etc/init.d/${packageName} ${action}" - ;; - esac - local result=0 - if [ -n "$cmd" ] && eval "$cmd" >/dev/null 2>&1; then - result=1 - fi - case "$action" in - enable|start|disable|stop) - update_cron "$action" || logger -t adblock-fast 'error' 'failed to update cron' - ;; - esac - print_json_bool 'result' "$result" -} - -get_init_status() { - local name - name="$(basename "$1")" - name="${name:-$packageName}" - local ports dns outputFile outputCache outputGzip outputConfig compressed_cache_dir i - config_load "$name" - config_get compressed_cache_dir 'config' 'compressed_cache_dir' '/etc' - compressed_cache_dir="$(sanitize_dir "$compressed_cache_dir")" - compressed_cache_dir="${compressed_cache_dir:-/etc}" - if [ -n "$(uci_get "$packageName" 'config' 'dnsmasq_config_file_url')" ]; then - dns="dnsmasq.conf" - else - dns="$(uci_get "$packageName" 'config' 'dns' 'dnsmasq.servers')" - fi - dns_set_output_values "$dns" - - json_init - json_add_object "$name" - json_add_boolean 'enabled' "$(is_enabled "$name")" - json_add_string 'status' "$(json 'get' 'status')" - json_add_string 'version' "$PKG_VERSION" - json_add_int 'packageCompat' "${packageCompat:-0}" - json_add_int 'rpcdCompat' "${rpcdCompat:-0}" - if is_running "$name"; then - json_add_boolean 'running' '1' - else - json_add_boolean 'running' '0' - fi - ports="$(ubus_get_ports)" - if [ -n "$ports" ]; then - json_add_boolean 'force_dns_active' '1' - json_add_array 'force_dns_ports' - for i in $ports; do json_add_int '' "$i"; done - json_close_array - else - json_add_boolean 'force_dns_active' '0' - fi - json_add_int 'entries' "$(ubus_get_data entries)" - json_add_string 'dns' "$dns" - json_add_string 'outputFile' "$outputFile" - json_add_string 'outputCache' "$outputCache" - json_add_string 'outputGzip' "$outputGzip" - if [ -s "$outputFile" ]; then - json_add_boolean 'outputFileExists' '1' - else - json_add_boolean 'outputFileExists' '0' - fi - if [ -s "$outputCache" ]; then - json_add_boolean 'outputCacheExists' '1' - else - json_add_boolean 'outputCacheExists' '0' - fi - if [ -s "$outputGzip" ]; then - json_add_boolean 'outputGzipExists' '1' - else - json_add_boolean 'outputGzipExists' '0' - fi - json_add_array 'leds' - if ls /sys/class/leds/* >/dev/null 2>&1; then - for i in /sys/class/leds/*; do - [ -d "$i" ] && json_add_string '' "$(basename "$i")" - done - fi - json_close_array - json_close_object - json_dump - json_cleanup -} - -get_platform_support() { - local name - name="$(basename "$1")" - name="${name:-$packageName}" - json_init - json_add_object "$name" - if check_ipset; then - json_add_boolean 'ipset_installed' '1' - else - json_add_boolean 'ipset_installed' '0' - fi - if check_nft; then - json_add_boolean 'nft_installed' '1' - else - json_add_boolean 'nft_installed' '0' - fi - if check_dnsmasq; then - json_add_boolean 'dnsmasq_installed' '1' - else - json_add_boolean 'dnsmasq_installed' '0' - fi - if check_dnsmasq_ipset; then - json_add_boolean 'dnsmasq_ipset_support' '1' - else - json_add_boolean 'dnsmasq_ipset_support' '0' - fi - if check_dnsmasq_nftset; then - json_add_boolean 'dnsmasq_nftset_support' '1' - else - json_add_boolean 'dnsmasq_nftset_support' '0' - fi - if check_smartdns; then - json_add_boolean 'smartdns_installed' '1' - else - json_add_boolean 'smartdns_installed' '0' - fi - if check_smartdns_ipset; then - json_add_boolean 'smartdns_ipset_support' '1' - else - json_add_boolean 'smartdns_ipset_support' '0' - fi - if check_smartdns_nftset; then - json_add_boolean 'smartdns_nftset_support' '1' - else - json_add_boolean 'smartdns_nftset_support' '0' - fi - if check_unbound; then - json_add_boolean 'unbound_installed' '1' - else - json_add_boolean 'unbound_installed' '0' - fi - json_add_array 'leds' - if ls /sys/class/leds/* >/dev/null 2>&1; then - for i in /sys/class/leds/*; do - [ -d "$i" ] && json_add_string '' "$(basename "$i")" - done - fi - json_close_array - json_close_object - json_dump - json_cleanup -} - - -case "$1" in - list) - json_init - json_add_object "getFileUrlFilesizes" - json_add_string 'name' 'name' - json_close_object - json_add_object "syncCron" - json_add_string 'name' 'name' - json_add_string 'action' 'action' - json_close_object - json_add_object "getInitList" - json_add_string 'name' 'name' - json_close_object - json_add_object "getInitStatus" - json_add_string 'name' 'name' - json_close_object - json_add_object "getCronStatus" - json_add_string 'name' 'name' - json_close_object - json_add_object "getPlatformSupport" - json_add_string 'name' 'name' - json_close_object - json_add_object "setInitAction" - json_add_string 'name' 'name' - json_add_string 'action' 'action' - json_close_object - json_dump - json_cleanup - ;; - call) - case "$2" in - getFileUrlFilesizes) - read -r input - json_load "$input" - json_get_var name 'name' - json_cleanup - get_file_url_filesizes "$name" - ;; - syncCron) - read -r input - json_load "$input" - json_get_var name 'name' - json_get_var action 'action' - json_cleanup - sync_cron "$name" "$action" - ;; - getInitList) - read -r input - json_load "$input" - json_get_var name 'name' - json_cleanup - get_init_list "$name" - ;; - getInitStatus) - read -r input - json_load "$input" - json_get_var name 'name' - json_cleanup - get_init_status "$name" - ;; - getCronStatus) - read -r input - json_load "$input" - json_get_var name 'name' - json_cleanup - get_cron_status "$name" - ;; - getPlatformSupport) - read -r input - json_load "$input" - json_get_var name 'name' - json_cleanup - get_platform_support "$name" - ;; - setInitAction) - read -r input - json_load "$input" - json_get_var name 'name' - json_get_var action 'action' - json_cleanup - set_init_action "$name" "$action" - ;; - esac - ;; -esac diff --git a/applications/luci-app-adblock-fast/root/usr/share/rpcd/acl.d/luci-app-adblock-fast.json b/applications/luci-app-adblock-fast/root/usr/share/rpcd/acl.d/luci-app-adblock-fast.json index d9db228783..56f884b494 100644 --- a/applications/luci-app-adblock-fast/root/usr/share/rpcd/acl.d/luci-app-adblock-fast.json +++ b/applications/luci-app-adblock-fast/root/usr/share/rpcd/acl.d/luci-app-adblock-fast.json @@ -12,6 +12,7 @@ "getInitList", "getInitStatus", "getCronStatus", + "getCronEntry", "getPlatformSupport" ], "service": [ @@ -31,6 +32,7 @@ "ubus": { "luci.adblock-fast": [ "syncCron", + "setCronEntry", "setInitAction" ] } diff --git a/applications/luci-app-adblock-fast/root/usr/share/rpcd/ucode/luci.adblock-fast b/applications/luci-app-adblock-fast/root/usr/share/rpcd/ucode/luci.adblock-fast new file mode 100644 index 0000000000..5df0a92a8d --- /dev/null +++ b/applications/luci-app-adblock-fast/root/usr/share/rpcd/ucode/luci.adblock-fast @@ -0,0 +1,446 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later +// Copyright 2023-2026 MOSSDeF, Stan Grishin (stangri@melmac.ca). +// +// rpcd ucode plugin for adblock-fast. + +/* +ubus -v list luci.adblock-fast +ubus call luci.adblock-fast getInitList '{"name":"adblock-fast"}' +ubus call luci.adblock-fast getInitStatus '{"name":"adblock-fast"}' +ubus call luci.adblock-fast getPlatformSupport '{"name":"adblock-fast"}' +ubus call luci.adblock-fast getCronStatus '{"name":"adblock-fast"}' +ubus call luci.adblock-fast getCronEntry '{"name":"adblock-fast"}' +ubus call luci.adblock-fast getFileUrlFilesizes '{"name":"adblock-fast"}' +ubus call luci.adblock-fast setCronEntry '{"name":"adblock-fast","entry":"0 4 * * * /etc/init.d/adblock-fast dl"}' +ubus call luci.adblock-fast setInitAction '{"name":"adblock-fast","action":"start"}' +ubus call luci.adblock-fast syncCron '{"name":"adblock-fast","action":"start"}' +*/ + +import adb from '/lib/adblock-fast/adblock-fast.uc'; +import { readfile, writefile, stat, rename, unlink, chmod, mkdir, access } from 'fs'; +import { cursor } from 'uci'; + +const packageName = 'adblock-fast'; +const rpcdCompat = 13; // ucode-lsp disable + +// ── Helpers ───────────────────────────────────────────────────────── + +function uci_bool(val) { + if (val == null) return false; + switch ('' + val) { + case '1': case 'yes': case 'on': case 'true': case 'enabled': + return true; + default: + return false; + } +} + +// ── Cron Management ───────────────────────────────────────────────── + +function update_cron(action) { + let cron_file = '/etc/crontabs/root'; + let tmp_file = cron_file + '.tmp'; + + let uci_ctx = cursor(); + uci_ctx.load(packageName); + let cfg = uci_ctx.get_all(packageName, 'config') || {}; + + // Read existing cron, filter out our lines + let content = readfile(cron_file) || ''; + let pattern = sprintf('/etc/init.d/%s\\s+dl', packageName); + let re = regexp(pattern); + let lines = []; + for (let line in split(content, '\n')) { + if (!match(line, re)) + push(lines, line); + } + // Remove trailing empty element from split + while (length(lines) > 0 && lines[length(lines) - 1] === '') + pop(lines); + + let auto_enabled = uci_bool(cfg.auto_update_enabled ?? '0'); + let mode = cfg.auto_update_mode || 'daily'; + let minute = cfg.auto_update_minute || '0'; + let hour = cfg.auto_update_hour || '4'; + let wday = cfg.auto_update_weekday || '0'; + let mday = cfg.auto_update_monthday || '1'; + let ndays = cfg.auto_update_every_ndays || '3'; + let nhours = cfg.auto_update_every_nhours || '6'; + + let dom = '*', dow = '*'; + switch (mode) { + case 'weekly': dow = wday; break; + case 'monthly': dom = mday; break; + case 'every_n_days': dom = '*/' + ndays; break; + case 'every_n_hours': hour = '*/' + nhours; break; + } + + let base_line = sprintf('%s %s %s * %s /etc/init.d/%s dl', minute, hour, dom, dow, packageName); + let active_line = base_line + ' # adblock-fast-auto'; + let disabled_line = '# ' + base_line + ' # adblock-fast-auto-disabled'; + let suspended_line = '# ' + base_line + ' # adblock-fast-auto-suspended'; + + let add_line = 0; + if (auto_enabled) { + switch (action) { + case 'disable': case 'stop': + add_line = 2; break; + case 'enable': case 'start': + add_line = 1; break; + default: + let is_en = uci_bool(cfg.enabled ?? '0'); + add_line = is_en ? 1 : 2; + break; + } + } else { + add_line = 3; + } + + let line_to_add; + switch (add_line) { + case 1: line_to_add = active_line; break; + case 2: line_to_add = suspended_line; break; + case 3: line_to_add = disabled_line; break; + } + + if (line_to_add) + push(lines, line_to_add); + + writefile(tmp_file, join('\n', lines) + '\n'); + + if (!rename(tmp_file, cron_file)) { + unlink(tmp_file); + return false; + } + chmod(cron_file, 0600); + if (access('/etc/init.d/cron', 'x')) + system('/etc/init.d/cron reload >/dev/null 2>&1'); + return true; +} + +function get_cron_status(req) { + let name = req.args?.name || packageName; + let cron_file = '/etc/crontabs/root'; + + let cron_uci = cursor(); + cron_uci.load(packageName); + + let auto_enabled = uci_bool(cron_uci.get(packageName, 'config', 'auto_update_enabled') ?? '0'); + + let cron_init = !!access('/etc/init.d/cron', 'x'); + let cron_bin = system('command -v crond >/dev/null 2>&1') == 0 || !!access('/usr/sbin/crond', 'x'); + let cron_enabled = cron_init && system('/etc/init.d/cron enabled >/dev/null 2>&1') == 0; + let cron_running = (cron_init && system('/etc/init.d/cron status >/dev/null 2>&1') == 0) || + system('pidof crond >/dev/null 2>&1') == 0; + + let cron_line_present = false; + let cron_line_match = false; + let cron_multi = false; + let cron_parse_ok = false; + let cron_state = 'none'; + let parsed = {}; + + let line_count = 0, match_count = 0; + let active_seen = false, suspended_seen = false, disabled_seen = false; + let last_line_state = ''; + let matched_entry = ''; + + let content = readfile(cron_file) || ''; + let pattern = sprintf('/etc/init.d/%s\\s+dl', packageName); + let re = regexp(pattern); + + for (let line in split(content, '\n')) { + if (!length(trim(line))) continue; + + let commented = match(line, /^\s*#/) ? true : false; + let line_content = commented ? replace(line, /^\s*#\s*/, '') : line; + + if (!match(line_content, re)) continue; + + line_count++; + matched_entry = line; + let state; + if (index(line, 'adblock-fast-auto-disabled') >= 0) { + state = 'disabled'; + } else if (index(line, 'adblock-fast-auto-suspended') >= 0) { + state = 'suspended'; + } else if (index(line, 'adblock-fast-auto') >= 0) { + state = commented ? 'disabled' : 'active'; + } else { + state = commented ? 'disabled' : 'active'; + } + last_line_state = state; + + if (state == 'active') active_seen = true; + if (state == 'suspended') suspended_seen = true; + if (state == 'disabled') disabled_seen = true; + + let fields = split(trim(line_content), /\s+/); + let p_ok = true; + + // Validate basic structure + if (length(fields) < 7) p_ok = false; + if (p_ok && fields[3] != '*') p_ok = false; + if (p_ok && fields[5] != '/etc/init.d/' + packageName) p_ok = false; + if (p_ok && fields[6] != 'dl') p_ok = false; + if (p_ok && !match(fields[0], /^[0-9]+$/)) p_ok = false; + + let p_minute = fields[0], p_hour = fields[1], p_dom = fields[2], p_dow = fields[4]; + let p_mode = '', p_ndays = '', p_nhours = ''; + + if (p_ok) { + if (index(p_hour, '/') >= 0) { + p_nhours = split(p_hour, '/')[1]; + if (!match(p_nhours, /^[0-9]+$/)) p_ok = false; + if (p_dom != '*' || p_dow != '*') p_ok = false; + p_mode = 'every_n_hours'; + } else if (index(p_dom, '/') >= 0) { + p_ndays = split(p_dom, '/')[1]; + if (!match(p_ndays, /^[0-9]+$/)) p_ok = false; + if (!match(p_hour, /^[0-9]+$/)) p_ok = false; + if (p_dow != '*') p_ok = false; + p_mode = 'every_n_days'; + } else if (p_dom != '*') { + if (!match(p_dom, /^[0-9]+$/)) p_ok = false; + if (!match(p_hour, /^[0-9]+$/)) p_ok = false; + if (p_dow != '*') p_ok = false; + p_mode = 'monthly'; + } else if (p_dow != '*') { + if (!match(p_dow, /^[0-9]+$/)) p_ok = false; + if (!match(p_hour, /^[0-9]+$/)) p_ok = false; + p_mode = 'weekly'; + } else { + if (!match(p_hour, /^[0-9]+$/)) p_ok = false; + p_mode = 'daily'; + } + } + + if (p_ok) { + match_count++; + parsed = { + state: state, mode: p_mode, minute: p_minute, + hour: p_hour, dom: p_dom, dow: p_dow, + wday: p_dow, mday: p_dom, ndays: p_ndays, nhours: p_nhours, + }; + } + } + + if (line_count > 0) cron_line_present = true; + + if (line_count == 0) { + cron_state = 'missing'; + auto_enabled = false; + } else if (line_count > 1) { + cron_multi = true; + cron_state = 'multi'; + auto_enabled = active_seen || suspended_seen; + } else if (match_count == 1) { + cron_line_match = true; + cron_parse_ok = true; + cron_state = parsed.state; + auto_enabled = (parsed.state == 'active' || parsed.state == 'suspended'); + + if (parsed.mode) + cron_uci.set(packageName, 'config', 'auto_update_mode', parsed.mode); + if (parsed.minute) + cron_uci.set(packageName, 'config', 'auto_update_minute', parsed.minute); + if (parsed.hour && parsed.mode != 'every_n_hours') + cron_uci.set(packageName, 'config', 'auto_update_hour', parsed.hour); + if (parsed.mode == 'weekly' && parsed.wday) + cron_uci.set(packageName, 'config', 'auto_update_weekday', parsed.wday); + if (parsed.mode == 'monthly' && parsed.mday) + cron_uci.set(packageName, 'config', 'auto_update_monthday', parsed.mday); + if (parsed.mode == 'every_n_days' && parsed.ndays) + cron_uci.set(packageName, 'config', 'auto_update_every_ndays', parsed.ndays); + if (parsed.mode == 'every_n_hours' && parsed.nhours) + cron_uci.set(packageName, 'config', 'auto_update_every_nhours', parsed.nhours); + } else { + cron_state = 'unsupported'; + auto_enabled = (last_line_state == 'active' || last_line_state == 'suspended'); + } + + cron_uci.set(packageName, 'config', 'auto_update_enabled', auto_enabled ? '1' : '0'); + if (length(cron_uci.changes(packageName))) cron_uci.commit(packageName); + + let result = {}; + result[name] = { + auto_update_enabled: auto_enabled, + cron_init: cron_init, + cron_bin: cron_bin, + cron_enabled: cron_enabled, + cron_running: cron_running, + cron_line_present: cron_line_present, + cron_line_match: cron_line_match, + cron_line_multi: cron_multi, + cron_line_parse_ok: cron_parse_ok, + cron_line_state: cron_state, + entry: matched_entry, + }; + return result; +} + +function get_cron_entry(req) { + let name = req.args?.name || packageName; + let cron_file = '/etc/crontabs/root'; + let entry = ''; + + let content = readfile(cron_file) || ''; + let pattern = sprintf('/etc/init.d/%s\\s+dl', packageName); + let re = regexp(pattern); + + for (let line in split(content, '\n')) { + if (!length(trim(line))) continue; + if (match(line, re)) { + entry = line; + break; + } + } + + let result = {}; + result[name] = { entry: entry }; + return result; +} + +function set_cron_entry(req) { + let name = req.args?.name || packageName; + let cron_entry = req.args?.entry; + let cron_file = '/etc/crontabs/root'; + let temp_file = cron_file + '.tmp'; + let found = false, written = false; + + let dir = replace(cron_file, /\/[^\/]+$/, ''); + if (!stat(dir)) mkdir(dir); + + let content = readfile(cron_file) || ''; + let pattern = sprintf('/etc/init.d/%s\\s+dl', packageName); + let re = regexp(pattern); + let out_lines = []; + + for (let line in split(content, '\n')) { + if (match(line, re)) { + if (!written && cron_entry) { + push(out_lines, cron_entry); + written = true; + } + found = true; + } else { + push(out_lines, line); + } + } + + if (!found && cron_entry) + push(out_lines, cron_entry); + + writefile(temp_file, join('\n', out_lines) + '\n'); + + if (rename(temp_file, cron_file)) { + chmod(cron_file, 0600); + if (access('/etc/init.d/cron', 'x')) { + if (system('/etc/init.d/cron enabled >/dev/null 2>&1') == 0) + system('/etc/init.d/cron restart >/dev/null 2>&1'); + else if (system('pidof crond >/dev/null 2>&1') == 0) + system('killall -HUP crond 2>/dev/null'); + } else if (system('pidof crond >/dev/null 2>&1') == 0) { + system('killall -HUP crond 2>/dev/null'); + } + return { result: true }; + } + unlink(temp_file); + return { result: false }; +} + +function sync_cron(req) { + let name = req.args?.name || packageName; + let action = req.args?.action; + if (update_cron(action)) + return { result: true }; + return { result: false }; +} + +// ── rpcd Method Handlers ──────────────────────────────────────────── + +const methods = { + getFileUrlFilesizes: { + args: { name: 'name' }, + call: function(req) { + return adb.get_file_url_filesizes(req.args.name || packageName); + } + }, + getInitList: { + args: { name: 'name' }, + call: function(req) { + return adb.get_init_list(req.args.name || packageName); + } + }, + getInitStatus: { + args: { name: 'name' }, + call: function(req) { + let name = req.args.name || packageName; + let result = adb.get_init_status(name); + if (result[name]) + result[name].rpcdCompat = rpcdCompat; + return result; + } + }, + getPlatformSupport: { + args: { name: 'name' }, + call: function(req) { + return adb.get_platform_support(req.args.name || packageName); + } + }, + setInitAction: { + args: { name: 'name', action: 'action' }, + call: function(req) { + let name = req.args.name || packageName; + let action = req.args.action; + if (name != packageName) return { result: false }; + if (!access('/etc/init.d/' + packageName, 'x')) + return { error: 'Init script not found!' }; + + let result = false; + switch (action) { + case 'enable': + case 'disable': { + let val = (action === 'enable') ? '1' : '0'; + if (system(sprintf("/etc/init.d/%s %s >/dev/null 2>&1", packageName, action)) == 0) { + let uci_ctx = cursor(); + uci_ctx.load(packageName); + uci_ctx.set(packageName, 'config', 'enabled', val); + uci_ctx.commit(packageName); + result = true; + } + break; + } + case 'start': case 'stop': case 'reload': case 'restart': case 'dl': case 'pause': + if (system(sprintf("/etc/init.d/%s %s >/dev/null 2>&1", packageName, action)) == 0) + result = true; + break; + } + + switch (action) { + case 'enable': case 'start': case 'disable': case 'stop': + update_cron(action); + break; + } + return { result: result }; + } + }, + getCronStatus: { + args: { name: 'name' }, + call: get_cron_status, + }, + getCronEntry: { + args: { name: 'name' }, + call: get_cron_entry, + }, + setCronEntry: { + args: { name: 'name', entry: 'entry' }, + call: set_cron_entry, + }, + syncCron: { + args: { name: 'name', action: 'action' }, + call: sync_cron, + }, +}; + +return { 'luci.adblock-fast': methods };