diff --git a/.gitattributes b/.gitattributes index 5b221be79..45f37b485 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,3 +1,3 @@ *.nims linguist-language=Nim *.nim linguist-language=Nim -*.* linguist-language=Nim +# *.* linguist-language=Nim diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 94b77d10e..b79d13835 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -44,4 +44,4 @@ jobs: run: nimble build -d:release - name: Nimble install - run: nimble install \ No newline at end of file + run: nimble install diff --git a/CHANGELOG.md b/CHANGELOG.md index 1df33ac37..97d1cb91f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## v6.0.6 +## Changes +- Fixes and updates for Nim >= 1.2.4. +- Clean out and optimizations. + ## v6.0.3 ## Changes - Fix #121, #122 and #123 diff --git a/README.md b/README.md index fbf88f49d..f6d626ea7 100644 --- a/README.md +++ b/README.md @@ -4,13 +4,13 @@ A quick website tool. Run the nim file and access your webpage. Website: [https: <img src="private/screenshots/NimWC_logo_shadow.png" style="max-height: 250px; display: block;" /> - - + +  - - - - + + + + <img src="http://feeds.feedburner.com/RecentCommitsToNimWC.1.gif" alt="Recent Commits to NimWC" title="Recent Commits to NimWC" width="99%" height="75px"> @@ -30,7 +30,6 @@ A quick website tool. Run the nim file and access your webpage. Website: [https: ## Security - Self-Firejailing Web Framework (It Firejails itself) Best Linux Security integrated on the Core. - 2 Factor Athentication TOTP -- [Design by Contract, Contract Programming](https://dev.to/juancarlospaco/design-by-contract-immutability-side-effects-and-gulag-44fk). - Security Hardened by default (based from [Gentoo Hardened](https://wiki.gentoo.org/wiki/Hardened_Gentoo) and [Debian Hardened](https://wiki.debian.org/Hardening), checked with [`hardening-check`](https://bitbucket.org/Alexander-Shukaev/hardening-check)). - Coded following the [Power of 10: NASA Coding guidelines for safety-critical code](https://en.wikipedia.org/wiki/The_Power_of_10:_Rules_for_Developing_Safety-Critical_Code#Rules) (as much as possible). - ReCAPTCHA (Optional) @@ -79,7 +78,7 @@ As alternative, [try AndOTP](https://github.com/andOTP/andOTP) (5Mb size). - No Global Interpreter Lock, no single-Thread, no single-Core, no Interpreter. Use all your 32 CPU Cores. ## Responsive -- Uses responsive [Bulma CSS framework](https://bulma.io), supports [Bootstrap CSS framework](https://getbootstrap.com). +- Uses responsive [Bulma CSS framework](https://bulma.io). - JavaScript framework agnostic, use Nim, [Karax](https://github.com/pragmagic/karax), vanilla JS, you choose. ## Other @@ -99,15 +98,9 @@ To get started you only need: Development dependencies (automatically installed by Nimble): -- [bCrypt](https://github.com/runvnc/bcryptnim) >= `0.2.1` -- [Contra](https://github.com/juancarlospaco/nim-contra) >= `0.2.0` -- [datetime2human](https://github.com/juancarlospaco/nim-datetime2human) >= `0.2.2` - [Jester](https://github.com/dom96/jester/) >= `0.4.1` -- [Libravatar](https://github.com/juancarlospaco/nim-libravatar#nim-libravatar) >= `0.4.0` - [otp](https://github.com/OpenSystemsLab/otp.nim) >= `0.1.1` -- [Firejail](https://github.com/juancarlospaco/nim-firejail) >= `0.5.0` (optional) - [reCAPTCHA](https://github.com/euantorano/recaptcha.nim) >= `1.0.2` (optional) -- [WebP](https://github.com/juancarlospaco/nim-webp-tools) >= `0.2.0` (optional) - [PackedJSON](https://github.com/Araq/packedjson#packedjson) >= `0.1.0` (optional) Foreing optional dependencies: @@ -177,7 +170,7 @@ cp config/config_default.cfg config/config.cfg nano config/config.cfg # Install dependencies -nimble install jester recaptcha bcrypt datetime2human otp firejail webp, contra +nimble install jester recaptcha otp firejail # Compile nimwc nim c nimwc.nim @@ -225,7 +218,7 @@ These arguments should be prepended to executable file, e.g. `./nimwc cdata` * `--newdb` = Generates the database with standard tables (does **not** override or delete tables). `newdb` will be initialized automatic, if no database exists. * `--insertdata` = Insert standard data, e.g `--insertdata bulma` (this will override existing data) * `bulma` = Use Bulma CSS, No JS required - * `bootstrap` = Use Bootstrap and jQuery + * `official` = Use Official CSS Themes * `water` = Water CSS framework, No JS, HTML Classless (No classes on HTML required) @@ -240,8 +233,7 @@ These options are only available at compiletime: * `-d:demo` = Used on public test site [Nim Website Creator](https://nimwc.org). This option will override the database every 1 hour with the standard data. * `-d:gitupdate` = Updates directly from Git and force a hard reset. * `-d:postgres` = Use Postgres database instead of SQLite. -* `-d:contracts` = Checks pre- and post-conditions when compiled with `-d:release`. -* `-d:hardened` = Hardens security, requires `-d:contracts`. Performance cost ~20% max. +* `-d:hardened` = Hardens security. Performance cost ~20% max. * `-d:packedjson` = Use [PackedJSON](https://github.com/Araq/packedjson#packedjson) instead of [std lib JSON](https://nim-lang.github.io/Nim/json.html). Performance optimization. @@ -302,7 +294,7 @@ When editing a blogpage or a normal page press Ctrl+S to save. # GrapesJS -GrapesJS is a Web Builder Framework. To use GrapeJS with a CSS framework (Bulma or Bootstrap), you have to edit `public/js/grapejs_custom.js` and `public/js/grapejsbs4.min.js`. Bootstrap support in `public/js/grapejs_custom.js` is commented out. +GrapesJS is a Web Builder Framework. To use GrapeJS with a CSS framework (Bulma), you have to edit `public/js/grapejs_custom.js` and `public/js/grapejsbs4.min.js`. # DevOps diff --git a/devops/autoinstall.sh b/devops/autoinstall.sh index 5221a2a7d..8dd15a17c 100755 --- a/devops/autoinstall.sh +++ b/devops/autoinstall.sh @@ -313,7 +313,6 @@ setStandardData() { ToggleCommand=(whiptail --separate-output --radiolist "Insert standard data?" ${r} ${c} 6) ChooseOptions=("Bulma (Recommended)" "" on - Bootstrap "" off Water "" off Off "" off) Choices=$("${ToggleCommand[@]}" "${ChooseOptions[@]}" 2>&1 >/dev/tty) || (printf " %bCancel was selected, exiting installer%b\\n" "${COL_LIGHT_RED}" "${COL_NC}" && exit 1) @@ -322,10 +321,6 @@ setStandardData() { printf " %b Bulma standard data On\\n" "${INFO}" CFG_STANDARDDATA="--insertdata bulma" ;; - Bootstrap) - printf " %b Bootstrap standard data\\n" "${INFO}" - CFG_STANDARDDATA="--insertdata bootstrap" - ;; Water) printf " %b Water standard data (HTML Classless)\\n" "${INFO}" CFG_STANDARDDATA="--insertdata water" diff --git a/devops/sourcehut/alpine/edge.yml b/devops/sourcehut/alpine/edge.yml index 9b698972a..bf3596349 100644 --- a/devops/sourcehut/alpine/edge.yml +++ b/devops/sourcehut/alpine/edge.yml @@ -12,7 +12,7 @@ sources: - https://github.com/ThomasTJdev/nim_websitecreator environment: - CHOOSENIM_CHOOSE_VERSION: "0.19.6" + CHOOSENIM_CHOOSE_VERSION: "1.2.4" CHOOSENIM_NO_ANALYTICS: 1 PATH: /home/build/nim-$CHOOSENIM_CHOOSE_VERSION/bin:$PATH @@ -25,7 +25,7 @@ tasks: bin/nim c koch ./koch tools bin/nimble -y refresh - bin/nimble -y install jester recaptcha bcrypt datetime2human otp firejail webp libravatar + bin/nimble -y install jester recaptcha otp firejail - build: | cd diff --git a/devops/sourcehut/alpine/latest.yml b/devops/sourcehut/alpine/latest.yml index 551eb21e5..f67a797e1 100644 --- a/devops/sourcehut/alpine/latest.yml +++ b/devops/sourcehut/alpine/latest.yml @@ -12,7 +12,7 @@ sources: - https://github.com/ThomasTJdev/nim_websitecreator environment: - CHOOSENIM_CHOOSE_VERSION: "0.19.6" + CHOOSENIM_CHOOSE_VERSION: "1.2.4" CHOOSENIM_NO_ANALYTICS: 1 PATH: /home/build/nim-$CHOOSENIM_CHOOSE_VERSION/bin:$PATH @@ -25,7 +25,7 @@ tasks: bin/nim c koch ./koch tools bin/nimble -y refresh - bin/nimble -y install jester recaptcha bcrypt datetime2human otp firejail webp libravatar + bin/nimble -y install jester recaptcha otp firejail - build: | cd diff --git a/devops/sourcehut/arch/rolling_release.yml b/devops/sourcehut/arch/rolling_release.yml index 86eba653d..33d1f132f 100644 --- a/devops/sourcehut/arch/rolling_release.yml +++ b/devops/sourcehut/arch/rolling_release.yml @@ -9,7 +9,7 @@ sources: - https://github.com/ThomasTJdev/nim_websitecreator environment: - CHOOSENIM_CHOOSE_VERSION: "0.19.6" + CHOOSENIM_CHOOSE_VERSION: "1.2.4" CHOOSENIM_NO_ANALYTICS: 1 PATH: $HOME/.nimble/bin:$PATH @@ -18,7 +18,7 @@ tasks: curl https://nim-lang.org/choosenim/init.sh -sSf > init.sh sh init.sh -y /home/build/.nimble/bin/nimble -y refresh - /home/build/.nimble/bin/nimble -y install jester recaptcha bcrypt datetime2human otp firejail webp libravatar + /home/build/.nimble/bin/nimble -y install jester recaptcha otp firejail - build: | cd nim_websitecreator diff --git a/devops/sourcehut/debian/stable.yml b/devops/sourcehut/debian/stable.yml index 4f815e23d..634c92e78 100644 --- a/devops/sourcehut/debian/stable.yml +++ b/devops/sourcehut/debian/stable.yml @@ -9,7 +9,7 @@ sources: - https://github.com/ThomasTJdev/nim_websitecreator environment: - CHOOSENIM_CHOOSE_VERSION: "0.19.6" + CHOOSENIM_CHOOSE_VERSION: "1.2.4" CHOOSENIM_NO_ANALYTICS: 1 PATH: $HOME/.nimble/bin:$PATH @@ -18,7 +18,7 @@ tasks: curl https://nim-lang.org/choosenim/init.sh -sSf > init.sh sh init.sh -y /home/build/.nimble/bin/nimble -y refresh - /home/build/.nimble/bin/nimble -y install jester recaptcha bcrypt datetime2human otp firejail webp libravatar + /home/build/.nimble/bin/nimble -y install jester recaptcha otp firejail - build: | cd nim_websitecreator diff --git a/devops/sourcehut/debian/testing.yml b/devops/sourcehut/debian/testing.yml index b5d3207a5..e216be431 100644 --- a/devops/sourcehut/debian/testing.yml +++ b/devops/sourcehut/debian/testing.yml @@ -10,7 +10,7 @@ sources: - https://github.com/ThomasTJdev/nim_websitecreator environment: - CHOOSENIM_CHOOSE_VERSION: "0.19.6" + CHOOSENIM_CHOOSE_VERSION: "1.2.4" CHOOSENIM_NO_ANALYTICS: 1 PATH: $HOME/.nimble/bin:$PATH @@ -19,7 +19,7 @@ tasks: curl https://nim-lang.org/choosenim/init.sh -sSf > init.sh sh init.sh -y /home/build/.nimble/bin/nimble -y refresh - /home/build/.nimble/bin/nimble -y install jester recaptcha bcrypt datetime2human otp firejail webp libravatar + /home/build/.nimble/bin/nimble -y install jester recaptcha otp firejail - build: | cd nim_websitecreator diff --git a/devops/sourcehut/debian/unstable.yml b/devops/sourcehut/debian/unstable.yml index fbc6e5b94..3fd19f3c9 100644 --- a/devops/sourcehut/debian/unstable.yml +++ b/devops/sourcehut/debian/unstable.yml @@ -10,7 +10,7 @@ sources: - https://github.com/ThomasTJdev/nim_websitecreator environment: - CHOOSENIM_CHOOSE_VERSION: "0.19.6" + CHOOSENIM_CHOOSE_VERSION: "1.2.4" CHOOSENIM_NO_ANALYTICS: 1 PATH: $HOME/.nimble/bin:$PATH @@ -19,7 +19,7 @@ tasks: curl https://nim-lang.org/choosenim/init.sh -sSf > init.sh sh init.sh -y /home/build/.nimble/bin/nimble -y refresh - /home/build/.nimble/bin/nimble -y install jester recaptcha bcrypt datetime2human otp firejail webp libravatar + /home/build/.nimble/bin/nimble -y install jester recaptcha otp firejail - build: | cd nim_websitecreator diff --git a/devops/sourcehut/fedora/latest.yml b/devops/sourcehut/fedora/latest.yml index 941564ffc..74eaa5a52 100644 --- a/devops/sourcehut/fedora/latest.yml +++ b/devops/sourcehut/fedora/latest.yml @@ -10,7 +10,7 @@ sources: - https://github.com/ThomasTJdev/nim_websitecreator environment: - CHOOSENIM_CHOOSE_VERSION: "0.19.6" + CHOOSENIM_CHOOSE_VERSION: "1.2.4" CHOOSENIM_NO_ANALYTICS: 1 PATH: $HOME/.nimble/bin:$PATH @@ -19,7 +19,7 @@ tasks: curl https://nim-lang.org/choosenim/init.sh -sSf > init.sh sh init.sh -y /home/build/.nimble/bin/nimble -y refresh - /home/build/.nimble/bin/nimble -y install jester recaptcha bcrypt datetime2human otp firejail webp libravatar + /home/build/.nimble/bin/nimble -y install jester recaptcha otp firejail - build: | cd nim_websitecreator diff --git a/devops/sourcehut/fedora/rawhide.yml b/devops/sourcehut/fedora/rawhide.yml index 941564ffc..74eaa5a52 100644 --- a/devops/sourcehut/fedora/rawhide.yml +++ b/devops/sourcehut/fedora/rawhide.yml @@ -10,7 +10,7 @@ sources: - https://github.com/ThomasTJdev/nim_websitecreator environment: - CHOOSENIM_CHOOSE_VERSION: "0.19.6" + CHOOSENIM_CHOOSE_VERSION: "1.2.4" CHOOSENIM_NO_ANALYTICS: 1 PATH: $HOME/.nimble/bin:$PATH @@ -19,7 +19,7 @@ tasks: curl https://nim-lang.org/choosenim/init.sh -sSf > init.sh sh init.sh -y /home/build/.nimble/bin/nimble -y refresh - /home/build/.nimble/bin/nimble -y install jester recaptcha bcrypt datetime2human otp firejail webp libravatar + /home/build/.nimble/bin/nimble -y install jester recaptcha otp firejail - build: | cd nim_websitecreator diff --git a/devops/sourcehut/ubuntu/latest.yml b/devops/sourcehut/ubuntu/latest.yml index c3c2abd40..f5a769e19 100644 --- a/devops/sourcehut/ubuntu/latest.yml +++ b/devops/sourcehut/ubuntu/latest.yml @@ -10,7 +10,7 @@ sources: - https://github.com/ThomasTJdev/nim_websitecreator environment: - CHOOSENIM_CHOOSE_VERSION: "0.19.6" + CHOOSENIM_CHOOSE_VERSION: "1.2.4" CHOOSENIM_NO_ANALYTICS: 1 PATH: $HOME/.nimble/bin:$PATH @@ -19,7 +19,7 @@ tasks: curl https://nim-lang.org/choosenim/init.sh -sSf > init.sh sh init.sh -y /home/build/.nimble/bin/nimble -y refresh - /home/build/.nimble/bin/nimble -y install jester recaptcha bcrypt datetime2human otp firejail webp libravatar + /home/build/.nimble/bin/nimble -y install jester recaptcha otp firejail - build: | cd nim_websitecreator diff --git a/devops/sourcehut/ubuntu/lts.yml b/devops/sourcehut/ubuntu/lts.yml index bb9e28257..2c243782d 100644 --- a/devops/sourcehut/ubuntu/lts.yml +++ b/devops/sourcehut/ubuntu/lts.yml @@ -10,7 +10,7 @@ sources: - https://github.com/ThomasTJdev/nim_websitecreator environment: - CHOOSENIM_CHOOSE_VERSION: "0.19.6" + CHOOSENIM_CHOOSE_VERSION: "1.2.4" CHOOSENIM_NO_ANALYTICS: 1 PATH: $HOME/.nimble/bin:$PATH @@ -19,7 +19,7 @@ tasks: curl https://nim-lang.org/choosenim/init.sh -sSf > init.sh sh init.sh -y /home/build/.nimble/bin/nimble -y refresh - /home/build/.nimble/bin/nimble -y install jester recaptcha bcrypt datetime2human otp firejail webp libravatar + /home/build/.nimble/bin/nimble -y install jester recaptcha otp firejail - build: | cd nim_websitecreator diff --git a/devops/sourcehut/ubuntu/next.yml b/devops/sourcehut/ubuntu/next.yml index cce3f8352..e7e817d21 100644 --- a/devops/sourcehut/ubuntu/next.yml +++ b/devops/sourcehut/ubuntu/next.yml @@ -10,7 +10,7 @@ sources: - https://github.com/ThomasTJdev/nim_websitecreator environment: - CHOOSENIM_CHOOSE_VERSION: "0.19.6" + CHOOSENIM_CHOOSE_VERSION: "1.2.4" CHOOSENIM_NO_ANALYTICS: 1 PATH: $HOME/.nimble/bin:$PATH @@ -19,7 +19,7 @@ tasks: curl https://nim-lang.org/choosenim/init.sh -sSf > init.sh sh init.sh -y /home/build/.nimble/bin/nimble -y refresh - /home/build/.nimble/bin/nimble -y install jester recaptcha bcrypt datetime2human otp firejail webp libravatar + /home/build/.nimble/bin/nimble -y install jester recaptcha otp firejail - build: | cd nim_websitecreator diff --git a/docs/nimwc-presentation-es-presentation.html b/docs/nimwc-presentation-es-presentation.html index 9df5a1638..04356bfd6 100644 --- a/docs/nimwc-presentation-es-presentation.html +++ b/docs/nimwc-presentation-es-presentation.html @@ -32,7 +32,7 @@ </div> <!-- Ends presentation slide number: 0 --> - + <!-- Begins presentation slide number: 1 --> <div id=1 class="step grayscale" data-rel-x="4161" data-rel-y="8041" data-rotate="362" data-rotate-x="3" data-rotate-y="1" data-x="2888" data-y="5597" data-z="2" data-scale="6"> <h1>WAT?</h1> @@ -48,7 +48,7 @@ <h1>WAT?</h1> </div> <!-- Ends presentation slide number: 1 --> - + <!-- Begins presentation slide number: 2 --> <div id=2 class="step contrast" data-rel-x="2923" data-rel-y="8202" data-rotate="282" data-rotate-x="2" data-rotate-y="2" data-x="4279" data-y="8240" data-z="1" data-scale="7"> <h1>Whats in the box?</h1> @@ -68,14 +68,13 @@ <h1>Whats in the box?</h1> </div> <!-- Ends presentation slide number: 2 --> - + <!-- Begins presentation slide number: 3 --> <div id=3 class="step " data-rel-x="2079" data-rel-y="6817" data-rotate="600" data-rotate-x="4" data-rotate-y="3" data-x="2727" data-y="5109" data-z="4" data-scale="5"> <h1>Stack</h1> <ul> <li>SQLite o Postgres</li> <li>Bulma CSS framework por defecto (No usa JS)</li> -<li>Bootstrap CSS framework soportado</li> <li>Agnostico de JavaScript framework</li> <li>No JavaScript framework empaquetado</li> <li>HTTP-Beast & Jester</li> @@ -85,7 +84,7 @@ <h1>Stack</h1> </div> <!-- Ends presentation slide number: 3 --> - + <!-- Begins presentation slide number: 4 --> <div id=4 class="step huerotate" data-rel-x="1735" data-rel-y="8596" data-rotate="120" data-rotate-x="4" data-rotate-y="4" data-x="4997" data-y="5829" data-z="1" data-scale="2"> <h1>Nim Power</h1> @@ -110,7 +109,7 @@ <h1>Nim Power</h1> </div> <!-- Ends presentation slide number: 4 --> - + <!-- Begins presentation slide number: 5 --> <div id=5 class="step grayscale" data-rel-x="1239" data-rel-y="7764" data-rotate="503" data-rotate-x="4" data-rotate-y="2" data-x="4229" data-y="6015" data-z="3" data-scale="9"> <h1>Admin Power</h1> @@ -132,7 +131,7 @@ <h1>Admin Power</h1> </div> <!-- Ends presentation slide number: 5 --> - + <!-- Begins presentation slide number: 6 --> <div id=6 class="step huerotate" data-rel-x="3938" data-rel-y="7434" data-rotate="393" data-rotate-x="4" data-rotate-y="2" data-x="2773" data-y="6146" data-z="2" data-scale="3"> <h1>Screenshots</h1> @@ -141,42 +140,42 @@ <h1>Screenshots</h1> </div> <!-- Ends presentation slide number: 6 --> - + <!-- Begins presentation slide number: 7 --> <div id=7 class="step grayscale" data-rel-x="2692" data-rel-y="5306" data-rotate="535" data-rotate-x="5" data-rotate-y="3" data-x="4011" data-y="7982" data-z="4" data-scale="4"> <p><img src="https://raw.githubusercontent.com/ThomasTJdev/nim_websitecreator/master/docs/nimwc-editor-summer.png" alt="File Upload"></p> </div> <!-- Ends presentation slide number: 7 --> - + <!-- Begins presentation slide number: 8 --> <div id=8 class="step " data-rel-x="4552" data-rel-y="8050" data-rotate="336" data-rotate-x="2" data-rotate-y="5" data-x="2466" data-y="8396" data-z="2" data-scale="5"> <p><img src="https://raw.githubusercontent.com/ThomasTJdev/nim_websitecreator/master/docs/nimwc-editor-grape.png" alt="User Reset"></p> </div> <!-- Ends presentation slide number: 8 --> - + <!-- Begins presentation slide number: 9 --> <div id=9 class="step invert" data-rel-x="2445" data-rel-y="6052" data-rotate="-65" data-rotate-x="1" data-rotate-y="1" data-x="2651" data-y="7409" data-z="4" data-scale="5"> <p><img src="https://raw.githubusercontent.com/ThomasTJdev/nim_websitecreator/master/docs/nimwc-firejail0.png" alt=""></p> </div> <!-- Ends presentation slide number: 9 --> - + <!-- Begins presentation slide number: 10 --> <div id=10 class="step " data-rel-x="2134" data-rel-y="6839" data-rotate="257" data-rotate-x="3" data-rotate-y="3" data-x="2364" data-y="8124" data-z="4" data-scale="6"> <p><img src="https://raw.githubusercontent.com/ThomasTJdev/nim_websitecreator/master/docs/nimwc-profile.png" alt=""></p> </div> <!-- Ends presentation slide number: 10 --> - + <!-- Begins presentation slide number: 11 --> <div id=11 class="step huerotate" data-rel-x="4548" data-rel-y="8115" data-rotate="-98" data-rotate-x="2" data-rotate-y="5" data-x="1439" data-y="7493" data-z="5" data-scale="2"> <p><img src="https://raw.githubusercontent.com/ThomasTJdev/nim_websitecreator/master/docs/nimwc-files.png" alt=""></p> </div> <!-- Ends presentation slide number: 11 --> - + <!-- Begins presentation slide number: 12 --> <div id=12 class="step invert" data-rel-x="4759" data-rel-y="6605" data-rotate="-531" data-rotate-x="3" data-rotate-y="4" data-x="3558" data-y="6429" data-z="5" data-scale="1"> <h5>CPU, Cores, RAM, SubProcesos</h5> @@ -184,14 +183,14 @@ <h5>CPU, Cores, RAM, SubProcesos</h5> </div> <!-- Ends presentation slide number: 12 --> - + <!-- Begins presentation slide number: 13 --> <div id=13 class="step saturate" data-rel-x="4997" data-rel-y="5221" data-rotate="307" data-rotate-x="3" data-rotate-y="2" data-x="3289" data-y="8729" data-z="2" data-scale="8"> <p><img src="https://user-images.githubusercontent.com/1189414/53709326-72ef5180-3e16-11e9-944e-8120d6ab2959.png" alt="Libravatar"></p> </div> <!-- Ends presentation slide number: 13 --> - + <!-- Begins presentation slide number: 14 --> <div id=14 class="step grayscale" data-rel-x="4716" data-rel-y="8560" data-rotate="354" data-rotate-x="2" data-rotate-y="5" data-x="1796" data-y="8214" data-z="5" data-scale="4"> <h5>Plugin Store</h5> @@ -199,7 +198,7 @@ <h5>Plugin Store</h5> </div> <!-- Ends presentation slide number: 14 --> - + <!-- Begins presentation slide number: 15 --> <div id=15 class="step invert" data-rel-x="1537" data-rel-y="6296" data-rotate="-198" data-rotate-x="1" data-rotate-y="4" data-x="2916" data-y="8605" data-z="1" data-scale="8"> <h1>Futuro</h1> @@ -214,7 +213,7 @@ <h1>Futuro</h1> </div> <!-- Ends presentation slide number: 15 --> - + <!-- Begins presentation slide number: 16 --> <div id=16 class="step saturate" data-rel-x="3922" data-rel-y="6672" data-rotate="-100" data-rotate-x="2" data-rotate-y="5" data-x="3387" data-y="6593" data-z="5" data-scale="3"> <h1>Nim necesita mas Comunidad</h1> @@ -223,7 +222,7 @@ <h1>Nim necesita mas Comunidad</h1> </div> <!-- Ends presentation slide number: 16 --> - + <!-- Begins presentation slide number: 17 --> <div id=17 class="step invert" data-rel-x="3887" data-rel-y="8698" data-rotate="245" data-rotate-x="4" data-rotate-y="2" data-x="4552" data-y="8871" data-z="3" data-scale="7"> <h1>Gracias</h1> @@ -246,7 +245,7 @@ <h1>Gracias</h1> </div> <!-- Ends presentation slide number: 17 --> - + <!-- Begins presentation slide overview --> <div id=overview class=step data-x=3332 data-y=4894 data-scale=3 style='pointer-events:none' data-rotate=319 ></div> <!-- Ends presentation slide overview --> @@ -307,7 +306,6 @@ <h1>Gracias</h1> - SQLite o Postgres - Bulma CSS framework por defecto (No usa JS) -- Bootstrap CSS framework soportado - Agnostico de JavaScript framework - No JavaScript framework empaquetado - HTTP-Beast & Jester @@ -693,24 +691,24 @@ <h1>Gracias</h1> // Let me show you the cogs that make impress.js run... (function ( document, window ) { 'use strict'; - + // HELPER FUNCTIONS - + // `pfx` is a function that takes a standard CSS property name as a parameter // and returns it's prefixed version valid for current browser it runs in. // The code is heavily inspired by Modernizr http://www.modernizr.com/ var pfx = (function () { - + var style = document.createElement('dummy').style, prefixes = 'Webkit Moz O ms Khtml'.split(' '), memory = {}; - + return function ( prop ) { if ( typeof memory[ prop ] === "undefined" ) { - + var ucProp = prop.charAt(0).toUpperCase() + prop.substr(1), props = (prop + ' ' + prefixes.join(ucProp + ' ') + ucProp).split(' '); - + memory[ prop ] = null; for ( var i in props ) { if ( style[ props[i] ] !== undefined ) { @@ -718,20 +716,20 @@ <h1>Gracias</h1> break; } } - + } - + return memory[ prop ]; }; - + })(); - + // `arraify` takes an array-like object and turns it into real Array // to make all the Array.prototype goodness available. var arrayify = function ( a ) { return [].slice.call( a ); }; - + // `css` function applies the styles given in `props` object to the element // given as `el`. It runs all property names through `pfx` function to make // sure proper prefixed version of the property is used. @@ -747,14 +745,14 @@ <h1>Gracias</h1> } return el; }; - + // `toNumber` takes a value given as `numeric` parameter and tries to turn // it into a number. If it is not possible it returns 0 (or other value // given as `fallback`). var toNumber = function (numeric, fallback) { return isNaN(numeric) ? (fallback || 0) : Number(numeric); }; - + var validateOrder = function ( order, fallback ) { var validChars = "xyz"; var returnStr = ""; @@ -774,26 +772,26 @@ <h1>Gracias</h1> else return "xyz"; }; - + // `byId` returns element with given `id` - you probably have guessed that ;) var byId = function ( id ) { return document.getElementById(id); }; - + // `$` returns first element for given CSS `selector` in the `context` of // the given element or whole document. var $ = function ( selector, context ) { context = context || document; return context.querySelector(selector); }; - + // `$$` return an array of elements for given CSS `selector` in the `context` of // the given element or whole document. var $$ = function ( selector, context ) { context = context || document; return arrayify( context.querySelectorAll(selector) ); }; - + // `triggerEvent` builds a custom DOM event with given `eventName` and `detail` data // and triggers it on element given as `el`. var triggerEvent = function (el, eventName, detail) { @@ -801,12 +799,12 @@ <h1>Gracias</h1> event.initCustomEvent(eventName, true, true, detail); el.dispatchEvent(event); }; - + // `translate` builds a translate transform string for given data. var translate = function ( t ) { return " translate3d(" + t.x + "px," + t.y + "px," + t.z + "px) "; }; - + // `rotate` builds a rotate transform string for given data. // By default the rotations are in X Y Z order that can be reverted by passing `true` // as second parameter. @@ -823,17 +821,17 @@ <h1>Gracias</h1> } return css; }; - + // `scale` builds a scale transform string for given data. var scale = function ( s ) { return " scale(" + s + ") "; }; - + // `perspective` builds a perspective transform string for given data. var perspective = function ( p ) { return " perspective(" + p + "px) "; }; - + // `getElementFromHash` returns an element located by id from hash part of // window location. var getElementFromHash = function () { @@ -841,37 +839,37 @@ <h1>Gracias</h1> // so both "fallback" `#slide-id` and "enhanced" `#/slide-id` will work return byId( window.location.hash.replace(/^#\/?/,"") ); }; - + // `computeWindowScale` counts the scale factor between window size and size // defined for the presentation in the config. var computeWindowScale = function ( config ) { var hScale = window.innerHeight / config.height, wScale = window.innerWidth / config.width, scale = hScale > wScale ? wScale : hScale; - + if (config.maxScale && scale > config.maxScale) { scale = config.maxScale; } - + if (config.minScale && scale < config.minScale) { scale = config.minScale; } - + return scale; }; - + // CHECK SUPPORT var body = document.body; - + var ua = navigator.userAgent.toLowerCase(); - var impressSupported = - // browser should support CSS 3D transtorms + var impressSupported = + // browser should support CSS 3D transtorms ( pfx("perspective") !== null ) && - + // and `classList` and `dataset` APIs ( body.classList ) && ( body.dataset ); - + if (!impressSupported) { // we can't be sure that `classList` is supported body.className += " impress-not-supported "; @@ -879,40 +877,40 @@ <h1>Gracias</h1> body.classList.remove("impress-not-supported"); body.classList.add("impress-supported"); } - + // GLOBALS AND DEFAULTS - + // This is where the root elements of all impress.js instances will be kept. // Yes, this means you can have more than one instance on a page, but I'm not // sure if it makes any sense in practice ;) var roots = {}; - + var preInitPlugins = []; var preStepLeavePlugins = []; - + // some default config values. var defaults = { width: 1024, height: 768, maxScale: 1, minScale: 0, - + perspective: 1000, - + transitionDuration: 1000 }; - + // it's just an empty function ... and a useless comment. var empty = function () { return false; }; - + // IMPRESS.JS API - + // And that's where interesting things will start to happen. // It's the core `impress` function that returns the impress.js API // for a presentation based on the element with given id ('impress' // by default). var impress = window.impress = function ( rootId ) { - + // If impress.js is not supported by the browser return a dummy API // it may not be a perfect solution but we return early and avoid // running code that may use features not implemented in the browser. @@ -926,49 +924,49 @@ <h1>Gracias</h1> addPreStepLeavePlugin: empty }; } - + rootId = rootId || "impress"; - + // if given root is already initialized just return the API if (roots["impress-root-" + rootId]) { return roots["impress-root-" + rootId]; } - + // data of all presentation steps var stepsData = {}; - + // element of currently active step var activeStep = null; - + // current state (position, rotation and scale) of the presentation var currentState = null; - + // array of step elements var steps = null; - + // configuration options var config = null; - + // scale factor of the browser window - var windowScale = null; - + var windowScale = null; + // root presentation elements var root = byId( rootId ); var canvas = document.createElement("div"); - + var initialized = false; - + // STEP EVENTS // // There are currently two step events triggered by impress.js - // `impress:stepenter` is triggered when the step is shown on the + // `impress:stepenter` is triggered when the step is shown on the // screen (the transition from the previous one is finished) and // `impress:stepleave` is triggered when the step is left (the // transition to next step just starts). - + // reference to last entered step var lastEntered = null; - + // `onStepEnter` is called whenever the step element is entered // but the event is triggered only if the step is different than // last entered step. @@ -978,7 +976,7 @@ <h1>Gracias</h1> lastEntered = step; } }; - + // `onStepLeave` is called whenever the step element is left // but the event is triggered only if the step is the same as // last entered step. @@ -988,9 +986,9 @@ <h1>Gracias</h1> lastEntered = null; } }; - + // `addPreInitPlugin` allows plugins to register a function that should - // be run (synchronously) at the beginning of init, before + // be run (synchronously) at the beginning of init, before // impress().init() itself executes. var addPreInitPlugin = function( plugin, weight ) { weight = toNumber(weight,10); @@ -999,7 +997,7 @@ <h1>Gracias</h1> } preInitPlugins[weight].push( plugin ); }; - + // Called at beginning of init, to execute all pre-init plugins. var execPreInitPlugins = function() { for( var i = 0; i < preInitPlugins.length; i++ ) { @@ -1011,7 +1009,7 @@ <h1>Gracias</h1> } } }; - + // `addPreStepLeavePlugin` allows plugins to register a function that should // be run (synchronously) at the beginning of goto() var addPreStepLeavePlugin = function( plugin, weight ) { @@ -1021,7 +1019,7 @@ <h1>Gracias</h1> } preStepLeavePlugins[weight].push( plugin ); }; - + // Called at beginning of goto(), to execute all preStepLeave plugins. var execPreStepLeavePlugins = function(event) { for( var i = 0; i < preStepLeavePlugins.length; i++ ) { @@ -1054,13 +1052,13 @@ <h1>Gracias</h1> transitionDuration: toNumber(data.transitionDuration, config.transitionDuration), el: el }; - + if ( !el.id ) { el.id = "step-" + (idx + 1); } - + stepsData["impress-" + el.id] = step; - + css(el, { position: "absolute", transform: "translate(-50%,-50%)" + @@ -1070,12 +1068,12 @@ <h1>Gracias</h1> transformStyle: "preserve-3d" }); }; - + // `init` API function that initializes (and runs) the presentation. var init = function () { if (initialized) { return; } execPreInitPlugins(); - + // First we set up the viewport for mobile devices. // For some reason iPad goes nuts when it is not done properly. var meta = $("meta[name='viewport']") || document.createElement("meta"); @@ -1084,41 +1082,41 @@ <h1>Gracias</h1> meta.name = 'viewport'; document.head.appendChild(meta); } - + // initialize configuration object var rootData = root.dataset; config = { width: toNumber( rootData.width, defaults.width ), height: toNumber( rootData.height, defaults.height ), maxScale: toNumber( rootData.maxScale, defaults.maxScale ), - minScale: toNumber( rootData.minScale, defaults.minScale ), + minScale: toNumber( rootData.minScale, defaults.minScale ), perspective: toNumber( rootData.perspective, defaults.perspective ), transitionDuration: toNumber( rootData.transitionDuration, defaults.transitionDuration ) }; - + windowScale = computeWindowScale( config ); - + // wrap steps with "canvas" element arrayify( root.childNodes ).forEach(function ( el ) { canvas.appendChild( el ); }); root.appendChild(canvas); - + // set initial styles document.documentElement.style.height = "100%"; - + css(body, { height: "100%", overflow: "hidden" }); - + var rootStyles = { position: "absolute", transformOrigin: "top left", transition: "all 0s ease-in-out", transformStyle: "preserve-3d" }; - + css(root, rootStyles); css(root, { top: "50%", @@ -1126,26 +1124,26 @@ <h1>Gracias</h1> transform: perspective( config.perspective/windowScale ) + scale( windowScale ) }); css(canvas, rootStyles); - + body.classList.remove("impress-disabled"); body.classList.add("impress-enabled"); - + // get and init steps steps = $$(".step", root); steps.forEach( initStep ); - + // set a default initial state of the canvas currentState = { translate: { x: 0, y: 0, z: 0 }, rotate: { x: 0, y: 0, z: 0, order: "xyz" }, scale: 1 }; - + initialized = true; - + triggerEvent(root, "impress:init", { api: roots[ "impress-root-" + rootId ] }); }; - + // `getStep` is a helper function that returns a step element defined by parameter. // If a number is given, step with index given by the number is returned, if a string // is given step element with such id is returned, if DOM element is given it is returned @@ -1158,19 +1156,19 @@ <h1>Gracias</h1> } return (step && step.id && stepsData["impress-" + step.id]) ? step : null; }; - + // used to reset timeout for `impress:stepenter` event var stepEnterTimeout = null; - + // `goto` API function that moves to step given with `el` parameter (by index, id or element), // with a transition `duration` optionally given as second parameter. var goto = function ( el, duration, reason = "goto" ) { - + if ( !initialized || !(el = getStep(el)) ) { // presentation not initialized or given element is not a step return false; } - + // Sometimes it's possible to trigger focus on first link with some keyboard action. // Browser in such a case tries to scroll the page to make this element visible // (even that body overflow is set to hidden) and it breaks our careful positioning. @@ -1180,9 +1178,9 @@ <h1>Gracias</h1> // // If you are reading this and know any better way to handle it, I'll be glad to hear about it! window.scrollTo(0, 0); - + var step = stepsData["impress-" + el.id]; - + // If we are in fact moving to another step, start with executing the registered preStepLeave plugins. if (activeStep && activeStep !== el) { var event = { target: activeStep, detail : {} }; @@ -1195,15 +1193,15 @@ <h1>Gracias</h1> step = stepsData["impress-" + el.id]; duration = event.detail.transitionDuration; } - + if ( activeStep ) { activeStep.classList.remove("active"); body.classList.remove("impress-on-" + activeStep.id); } el.classList.add("active"); - + body.classList.add("impress-on-" + el.id); - + // compute target state of the canvas based on given step var target = { rotate: { @@ -1219,7 +1217,7 @@ <h1>Gracias</h1> }, scale: 1 / step.scale }; - + // Check if the transition is zooming in or not. // // This information is used to alter the transition style: @@ -1227,23 +1225,23 @@ <h1>Gracias</h1> // and the scaling is delayed, but when we are zooming out we start // with scaling down and move and rotation are delayed. var zoomin = target.scale >= currentState.scale; - + duration = toNumber(duration, config.transitionDuration); var delay = (duration / 2); - + // if the same step is re-selected, force computing window scaling, // because it is likely to be caused by window resize if (el === activeStep) { windowScale = computeWindowScale(config); } - + var targetScale = target.scale * windowScale; - + // trigger leave of currently active element (if it's not the same step again) if (activeStep && activeStep !== el) { onStepLeave(activeStep, el); } - + // Now we alter transforms of `root` and `canvas` to trigger transitions. // // And here is why there are two elements: `root` and `canvas` - they are @@ -1259,13 +1257,13 @@ <h1>Gracias</h1> transitionDuration: duration + "ms", transitionDelay: (zoomin ? delay : 0) + "ms" }); - + css(canvas, { transform: rotate(target.rotate, true) + translate(target.translate), transitionDuration: duration + "ms", transitionDelay: (zoomin ? 0 : delay) + "ms" }); - + // Here is a tricky part... // // If there is no change in scale or no change in rotation and translation, it means there was actually @@ -1281,17 +1279,17 @@ <h1>Gracias</h1> currentState.translate.y === target.translate.y && currentState.translate.z === target.translate.z) ) { delay = 0; } - + // store current state currentState = target; activeStep = el; - + // And here is where we trigger `impress:stepenter` event. // We simply set up a timeout to fire it taking transition duration (and possible delay) into account. // // I really wanted to make it in more elegant way. The `transitionend` event seemed to be the best way // to do it, but the fact that I'm using transitions on two separate elements and that the `transitionend` - // event is only triggered when there was a transition (change in the values) caused some bugs and + // event is only triggered when there was a transition (change in the values) caused some bugs and // made the code really complicated, cause I had to handle all the conditions separately. And it still // needed a `setTimeout` fallback for the situations when there is no transition at all. // So I decided that I'd rather make the code simpler than use shiny new `transitionend`. @@ -1302,46 +1300,46 @@ <h1>Gracias</h1> stepEnterTimeout = window.setTimeout(function() { onStepEnter(activeStep); }, duration + delay); - + return el; }; - + // `prev` API function goes to previous step (in document order) var prev = function () { var prev = steps.indexOf( activeStep ) - 1; prev = prev >= 0 ? steps[ prev ] : steps[ steps.length-1 ]; - + return goto(prev, undefined, "prev"); }; - + // `next` API function goes to next step (in document order) var next = function () { var next = steps.indexOf( activeStep ) + 1; next = next < steps.length ? steps[ next ] : steps[ 0 ]; - + return goto(next, undefined, "next"); }; - + // Swipe for touch devices by @and3rson. // Below we extend the api to control the animation between the currently // active step and a presumed next/prev step. See touch plugin for // an example of using this api. - + // Helper function var interpolate = function(a, b, k) { return a + (b - a) * k; }; - - // Animate a swipe. + + // Animate a swipe. // // pct is a value between -1.0 and +1.0, designating the current length // of the swipe. // - // If pct is negative, swipe towards the next() step, if positive, - // towards the prev() step. + // If pct is negative, swipe towards the next() step, if positive, + // towards the prev() step. // - // Note that pre-stepleave plugins such as goto can mess with what is a - // next() and prev() step, so we need to trigger the pre-stepleave event + // Note that pre-stepleave plugins such as goto can mess with what is a + // next() and prev() step, so we need to trigger the pre-stepleave event // here, even if a swipe doesn't guarantee that the transition will // actually happen. // @@ -1369,7 +1367,7 @@ <h1>Gracias</h1> } execPreStepLeavePlugins(event); var nextElement = event.detail.next; - + var nextStep = stepsData['impress-' + nextElement.id]; var zoomin = nextStep.scale >= currentState.scale; // if the same step is re-selected, force computing window scaling, @@ -1426,26 +1424,26 @@ <h1>Gracias</h1> steps.forEach(function (step) { step.classList.add("future"); }); - + root.addEventListener("impress:stepenter", function (event) { event.target.classList.remove("past"); event.target.classList.remove("future"); event.target.classList.add("present"); }, false); - + root.addEventListener("impress:stepleave", function (event) { event.target.classList.remove("present"); event.target.classList.add("past"); }, false); - + }, false); - + // Adding hash change support. root.addEventListener("impress:init", function(){ - + // last hash detected var lastHash = ""; - + // `#/step-id` is used instead of `#step-id` to prevent default browser // scrolling to element in hash. // @@ -1455,10 +1453,10 @@ <h1>Gracias</h1> root.addEventListener("impress:stepenter", function (event) { window.location.hash = lastHash = "#/" + event.target.id; }, false); - + window.addEventListener("hashchange", function () { // When the step is entered hash in the location is updated - // (just few lines above from here), so the hash change is + // (just few lines above from here), so the hash change is // triggered and we would call `goto` again on the same element. // // To avoid this we store last entered hash and compare. @@ -1466,14 +1464,14 @@ <h1>Gracias</h1> goto( getElementFromHash() ); } }, false); - - // START + + // START // by selecting step defined in url or first step of the presentation goto(getElementFromHash() || steps[0], 0); }, false); - + body.classList.add("impress-disabled"); - + // store and return API for given impress.js root element return (roots[ "impress-root-" + rootId ] = { init: init, @@ -1486,7 +1484,7 @@ <h1>Gracias</h1> }); }; - + // flag that can be used in JS to check if browser have passed the support test impress.supported = impressSupported; @@ -1521,19 +1519,19 @@ <h1>Gracias</h1> return isNaN(numeric) ? (fallback || 0) : Number(numeric); }; - // On impress:init, check whether there is a default setting, as well as + // On impress:init, check whether there is a default setting, as well as // handle step-1. document.addEventListener("impress:init", function (event) { // Getting API from event data instead of global impress().init(). // You don't even need to know what is the id of the root element - // or anything. `impress:init` event data gives you everything you + // or anything. `impress:init` event data gives you everything you // need to control the presentation that was just initialized. api = event.detail.api; root = event.target; // Element attributes starting with 'data-', become available under // element.dataset. In addition hyphenized words become camelCased. var data = root.dataset; - + if (data.autoplay) { autoplayDefault = toNumber(data.autoplay, 0); } @@ -1546,7 +1544,7 @@ <h1>Gracias</h1> // Note that right after impress:init event, also impress:stepenter is // triggered for the first slide, so that's where code flow continues. }, false); - + // If default autoplay time was defined in the presentation root, or // in this step, set timeout. document.addEventListener("impress:stepenter", function (event) { @@ -1567,13 +1565,13 @@ <h1>Gracias</h1> if ( timeoutHandle ) { clearTimeout(timeoutHandle); } - + if ( timeout > 0) { timeoutHandle = setTimeout( function() { api.next(); }, timeout*1000 ); } setButtonText(); }; - + /*** Toolbar plugin integration *******************************************/ var status = "not clicked"; @@ -1609,7 +1607,7 @@ <h1>Gracias</h1> return "▶"; // play } }; - + var setButtonText = function() { if (toolbarButton) { // Keep button size the same even if label content is changing @@ -1652,16 +1650,16 @@ <h1>Gracias</h1> * * Press Ctrl+b to hide all slides, and Ctrl+b again to show them. * Also navigating to a different slide will show them again (impress:stepleave). - * + * * Copyright 2014 @Strikeskids * Released under the MIT license. */ (function ( document, window ) { 'use strict'; - + var canvas = null; var blackedOut = false; - + // While waiting for a shared library of utilities, copying these 2 from main impress.js var css = function ( el, props ) { var key, pkey; @@ -1677,17 +1675,17 @@ <h1>Gracias</h1> }; var pfx = (function () { - + var style = document.createElement('dummy').style, prefixes = 'Webkit Moz O ms Khtml'.split(' '), memory = {}; - + return function ( prop ) { if ( typeof memory[ prop ] === "undefined" ) { - + var ucProp = prop.charAt(0).toUpperCase() + prop.substr(1), props = (prop + ' ' + prefixes.join(ucProp + ' ') + ucProp).split(' '); - + memory[ prop ] = null; for ( var i in props ) { if ( style[ props[i] ] !== undefined ) { @@ -1695,14 +1693,14 @@ <h1>Gracias</h1> break; } } - + } - + return memory[ prop ]; }; - + })(); - + var removeBlackout = function() { @@ -1731,7 +1729,7 @@ <h1>Gracias</h1> var api = event.detail.api; var root = event.target; canvas = root.firstElementChild; - + document.addEventListener("keydown", function ( event ) { if ( event.ctrlKey && event.keyCode === 66 ) { event.preventDefault(); @@ -1746,7 +1744,7 @@ <h1>Gracias</h1> } } }, false); - + document.addEventListener("keyup", function ( event ) { if ( event.ctrlKey && event.keyCode === 66 ) { event.preventDefault(); @@ -1754,7 +1752,7 @@ <h1>Gracias</h1> }, false); }, false); - + document.addEventListener("impress:stepleave", function (event) { removeBlackout(); }, false); @@ -1797,7 +1795,7 @@ <h1>Gracias</h1> var markdownDivs = document.querySelectorAll(".markdown"); for (var element of markdownDivs) { - // Note: unlike the previous two, markdown.js doesn't automatically find or convert anything in + // Note: unlike the previous two, markdown.js doesn't automatically find or convert anything in var slides = element.textContent.split(/^-----$/m); var i = slides.length - 1; element.innerHTML = markdown.toHTML(slides[i]); @@ -1817,11 +1815,11 @@ <h1>Gracias</h1> element.id = id; } } // markdown - + if(window.hljs){ - hljs.initHighlightingOnLoad(); + hljs.initHighlightingOnLoad(); } - + if(window.mermaid){ mermaid.initialize({startOnLoad:true}); } @@ -1830,7 +1828,7 @@ <h1>Gracias</h1> var postInit = function(event){ if(window.impressConsole){ // Init impressConsole.js. - // This does not yet show the window, just activates the plugin. + // This does not yet show the window, just activates the plugin. // Press 'P' to show the console. // Note that we must pass the correct path to css file as well. // See https://github.com/regebro/impress-console/issues/19 @@ -1840,7 +1838,7 @@ <h1>Gracias</h1> else{ impressConsole().init(); } - + // Add 'P' to the help popup triggerEvent(document, "impress:help:add", { command : "P", text : "Presenter console", row : 10} ); @@ -1886,11 +1884,11 @@ <h1>Gracias</h1> */ (function ( document, window ) { 'use strict'; - + document.addEventListener("impress:stepleave", function (event) { document.activeElement.blur() }, false); - + })(document, window); @@ -1901,14 +1899,14 @@ <h1>Gracias</h1> * and will alter the destination where to transition next. * * Example: - * + * * <!-- When leaving this step, go directly to "step-5" --> * <div class="step" data-goto="step-5"> - * + * * <!-- When leaving this step with next(), go directly to "step-5", instead of the next step. * If moving backwards to previous step - e.g. prev() instead of next() - then go to "step-1". --> * <div class="step" data-goto-next="step-5" data-goto-prev="step-1"> - * + * * Copyright 2016 Henrik Ingo (@henrikingo) * Released under the MIT license. */ @@ -1919,14 +1917,14 @@ <h1>Gracias</h1> return isNaN(numeric) ? (fallback || 0) : Number(numeric); }; - + var goto = function(event) { if ( (!event) || (!event.target) ) return; - + var data = event.target.dataset; var steps = document.querySelectorAll(".step"); - + // Handle event.target data-goto-next attribute if ( !isNaN(data.gotoNext) && event.detail.reason == "next" ) { event.detail.next = steps[data.gotoNext]; @@ -1982,10 +1980,10 @@ <h1>Gracias</h1> } } }; - + // Register the plugin to be called in pre-stepleave phase impress().addPreStepLeavePlugin( goto ); - + })(document, window); @@ -1993,7 +1991,7 @@ <h1>Gracias</h1> * Help popup plugin * * Example: - * + * * <!-- Show a help popup at start, or if user presses 'H' --> * <div id="impress-help"></div> * @@ -2030,7 +2028,7 @@ <h1>Gracias</h1> } } }; - + var toggleHelp = function() { var helpDiv = document.getElementById("impress-help"); if(helpDiv.style.display == 'block') { @@ -2086,10 +2084,10 @@ <h1>Gracias</h1> }, 7000); } }); - + // Use our own API to register the help text for 'h' triggerEvent(document, "impress:help:add", { command : "H", text : "Show this help", row : 0} ); - + })(document, window); @@ -2135,7 +2133,7 @@ <h1>Gracias</h1> // Detect mobile browsers & add CSS class as appropriate. document.addEventListener("impress:init", function (event) { var body = document.body; - if(/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)){ + if(/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)){ body.classList.add('impress-mobile'); } }); @@ -2164,7 +2162,7 @@ <h1>Gracias</h1> /** * Mouse timeout plugin * - * After 3 seconds of mouse inactivity, add the css class + * After 3 seconds of mouse inactivity, add the css class * `body.impress-mouse-timeout`. On `mousemove`, `click` or `touch`, remove the * class. * @@ -2241,7 +2239,7 @@ <h1>Gracias</h1> */ (function ( document, window ) { 'use strict'; - + var triggerEvent = function (el, eventName, detail) { var event = document.createEvent("CustomEvent"); event.initCustomEvent(eventName, true, true, detail); @@ -2252,7 +2250,7 @@ <h1>Gracias</h1> document.addEventListener("impress:init", function (event) { // Getting API from event data. // So you don't event need to know what is the id of the root element - // or anything. `impress:init` event data gives you everything you + // or anything. `impress:init` event data gives you everything you // need to control the presentation that was just initialized. var api = event.detail.api; var tab = 9; @@ -2275,13 +2273,13 @@ <h1>Gracias</h1> if ( event.altKey || event.ctrlKey || event.metaKey ){ return false; } - + // In the case of TAB, we force step navigation always, overriding the browser navigation between // input elements, buttons and links. if ( event.keyCode === 9 ) { return true; } - + // With the sole exception of TAB, we also ignore keys pressed if shift is down. if ( event.shiftKey ){ return false; @@ -2297,17 +2295,17 @@ <h1>Gracias</h1> return true; } }; - - + + // KEYBOARD NAVIGATION HANDLERS - + // Prevent default keydown action when one of supported key is pressed. document.addEventListener("keydown", function ( event ) { if ( isNavigationEvent(event) ) { event.preventDefault(); } }, false); - + // Trigger impress action (next or prev) on keyup. document.addEventListener("keyup", function ( event ) { if ( isNavigationEvent(event) ) { @@ -2337,7 +2335,7 @@ <h1>Gracias</h1> event.preventDefault(); } }, false); - + // delegated handler for clicking on the links to presentation steps document.addEventListener("click", function ( event ) { // event delegation with "bubbling" @@ -2347,22 +2345,22 @@ <h1>Gracias</h1> (target !== document.documentElement) ) { target = target.parentNode; } - + if ( target.tagName === "A" ) { var href = target.getAttribute("href"); - + // if it's a link to presentation step, target this step if ( href && href[0] === '#' ) { target = document.getElementById( href.slice(1) ); } } - + if ( api.goto(target) ) { event.stopImmediatePropagation(); event.preventDefault(); } }, false); - + // delegated handler for clicking on step elements document.addEventListener("click", function ( event ) { var target = event.target; @@ -2371,17 +2369,17 @@ <h1>Gracias</h1> (target !== document.documentElement) ) { target = target.parentNode; } - + if ( api.goto(target) ) { event.preventDefault(); } }, false); - + // Add a line to the help popup triggerEvent(document, "impress:help:add", { command : "Left & Right", text : "Previous & Next step", row : 1} ); - + }, false); - + })(document, window); @@ -2419,7 +2417,7 @@ <h1>Gracias</h1> tempDiv.innerHTML = html; return tempDiv.firstChild; }; - + var selectOptionsHtml = function(){ var options = ""; for ( var i = 0; i < steps.length; i++ ) { @@ -2428,7 +2426,7 @@ <h1>Gracias</h1> options = options + '<option value="' + steps[i].id + '">' + steps[i].id + '</option>' + "\n"; } } - return options; + return options; }; var addNavigationControls = function( event ) { @@ -2460,12 +2458,12 @@ <h1>Gracias</h1> function() { api.next(); }); - + triggerEvent(toolbar, "impress:toolbar:appendChild", { group : 0, element : prev } ); triggerEvent(toolbar, "impress:toolbar:appendChild", { group : 0, element : select } ); triggerEvent(toolbar, "impress:toolbar:appendChild", { group : 0, element : next } ); }; - + // API for not listing given step in the select widget. // For example, if you set class="skip" on some element, you may not want it to show up in the list either. // Otoh we cannot assume that, or anything else, so steps that user wants omitted must be specifically added with this API call. @@ -2475,7 +2473,7 @@ <h1>Gracias</h1> select.innerHTML = selectOptionsHtml(); } }, false); - + // wait for impress.js to be initialized document.addEventListener("impress:init", function (event) { toolbar = document.querySelector("#impress-toolbar"); @@ -2483,13 +2481,13 @@ <h1>Gracias</h1> addNavigationControls( event ); } }, false); - + })(document, window); (function ( document, window ) { 'use strict'; - + var stepids = []; // wait for impress.js to be initialized document.addEventListener("impress:init", function (event) { @@ -2502,12 +2500,12 @@ <h1>Gracias</h1> }); var progressbar = document.querySelector('div.impress-progressbar div'); var progress = document.querySelector('div.impress-progress'); - - if (null !== progressbar || null !== progress) { + + if (null !== progressbar || null !== progress) { document.addEventListener("impress:stepleave", function (event) { updateProgressbar(event.detail.next.id); }); - + document.addEventListener("impress:stepenter", function (event) { updateProgressbar(event.target.id); }); @@ -2532,26 +2530,26 @@ <h1>Gracias</h1> * since as you add, remove or move steps, you may not need to edit the positions * as much as is the case with the absolute coordinates supported by impress.js * core. - * + * * Example: - * + * * <!-- Position step 1000 px to the right and 500 px up from the previous step. --> * <div class="step" data-rel-x="1000" data-rel-y="500"> - * + * * Following html attributes are supported for step elements: - * + * * data-rel-x * data-rel-y * data-rel-z - * - * These values are also inherited from the previous step. This makes it easy to - * create a boring presentation where each slide shifts for example 1000px down + * + * These values are also inherited from the previous step. This makes it easy to + * create a boring presentation where each slide shifts for example 1000px down * from the previous. - * + * * In addition to plain numbers, which are pixel values, it is also possible to * define relative positions as a multiple of screen height and width, using * a unit of "h" and "w", respectively, appended to the number. - * + * * Example: * * <div class="step" data-rel-x="1.5w" data-rel-y="1.5h"> @@ -2560,7 +2558,7 @@ <h1>Gracias</h1> * core at the beginning of `impress().init()`. This allows it to process its own * data attributes first, and possibly alter the data-x, data-y and data-z attributes * that will then be processed by `impress().init()`. - * + * * (Another name for this kind of plugin might be called a *filter plugin*, but * *pre-init plugin* is more generic, as a plugin might do whatever it wants in * the pre-init stage.) @@ -2600,7 +2598,7 @@ <h1>Gracias</h1> var computeRelativePositions = function ( el, prev ) { var data = el.dataset; - + if( !prev ) { // For the first step, inherit these defaults var prev = { x:0, y:0, z:0, relative: {x:0, y:0, z:0} }; @@ -2621,16 +2619,16 @@ <h1>Gracias</h1> if(data.x !== undefined) step.relative.x = 0; if(data.y !== undefined) step.relative.y = 0; if(data.z !== undefined) step.relative.z = 0; - + // Apply relative position to absolute position, if non-zero // Note that at this point, the relative values contain a number value of pixels. step.x = step.x + step.relative.x; step.y = step.y + step.relative.y; step.z = step.z + step.relative.z; - - return step; + + return step; }; - + var rel = function() { var root = document.querySelector("#impress"); var steps = root.querySelectorAll(".step"); @@ -2645,10 +2643,10 @@ <h1>Gracias</h1> prev = step; } }; - + // Register the plugin to be called in pre-init phase impress().addPreInitPlugin( rel ); - + })(document, window); @@ -2668,7 +2666,7 @@ <h1>Gracias</h1> */ (function ( document, window ) { 'use strict'; - + // throttling function calls, by Remy Sharp // http://remysharp.com/2010/07/21/throttling-function-calls/ var throttle = function (fn, delay) { @@ -2681,7 +2679,7 @@ <h1>Gracias</h1> }, delay); }; }; - + // wait for impress.js to be initialized document.addEventListener("impress:init", function (event) { var api = event.detail.api; @@ -2691,7 +2689,7 @@ <h1>Gracias</h1> api.goto( document.querySelector(".step.active"), 500 ); }, 250), false); }, false); - + })(document, window); @@ -2699,12 +2697,12 @@ <h1>Gracias</h1> * Skip Plugin * * Example: - * + * * <!-- This slide is disabled in presentations, when moving with next() * and prev() commands, but you can still move directly to it, for * example with a url (anything using goto()). --> * <div class="step skip"> - * + * * Copyright 2016 Henrik Ingo (@henrikingo) * Released under the MIT license. */ @@ -2739,7 +2737,7 @@ <h1>Gracias</h1> var skip = function(event) { if ( (!event) || (!event.target) ) return; - + if ( event.detail.next.classList.contains("skip") ) { if ( event.detail.reason == "next" ) { // Go to the next next step instead @@ -2756,11 +2754,11 @@ <h1>Gracias</h1> event.detail.transitionDuration = toNumber( event.detail.next.dataset.transitionDuration, event.detail.transitionDuration); } }; - + // Register the plugin to be called in pre-stepleave phase // The weight makes this plugin run early. This is a good thing, because this plugin calls itself recursively. impress().addPreStepLeavePlugin( skip, 1 ); - + })(document, window); @@ -2768,12 +2766,12 @@ <h1>Gracias</h1> * Stop Plugin * * Example: - * + * * <!-- Stop at this slide. - * (For example, when used on the last slide, this prevents the + * (For example, when used on the last slide, this prevents the * presentation from wrapping back to the beginning.) --> * <div class="step" data-stop="true"> - * + * * Copyright 2016 Henrik Ingo (@henrikingo) * Released under the MIT license. */ @@ -2783,17 +2781,17 @@ <h1>Gracias</h1> var stop = function(event) { if ( (!event) || (!event.target) ) return; - + if ( event.target.classList.contains("stop") ) { if ( event.detail.reason == "next" ) event.detail.next = event.target; } }; - + // Register the plugin to be called in pre-stepleave phase // The weight makes this plugin run fairly late. impress().addPreStepLeavePlugin( stop, 20 ); - + })(document, window); @@ -2804,9 +2802,9 @@ <h1>Gracias</h1> * or tapping on the left/right edges of the screen. * * - * + * * Copyright 2015: Andrew Dunai (@and3rson) - * Modified to a plugin, 2016: Henrik Ingo (@henrikingo) + * Modified to a plugin, 2016: Henrik Ingo (@henrikingo) * * MIT License */ @@ -2820,7 +2818,7 @@ <h1>Gracias</h1> var lastX = 0; var lastDX = 0; var threshold = window.innerWidth / 20; - + document.addEventListener('touchstart', function (event) { lastX = startX = event.touches[0].clientX; }); @@ -2878,7 +2876,7 @@ <h1>Gracias</h1> * To add/activate the toolbar in your presentation, add this div: * * <div id="impress-toolbar"></div> - * + * * Styling the toolbar is left to presentation author. Here's an example CSS: * * .impress-enabled div#impress-toolbar { @@ -2910,7 +2908,7 @@ <h1>Gracias</h1> * callback : "mycallback", // Toolbar plugin will trigger event `impress:toolbar:added:mycallback` when done. * before: element } // The reference element for an insertBefore() call. * - * You should also listen to the `impress:toolbar:added:mycallback` event. At + * You should also listen to the `impress:toolbar:added:mycallback` event. At * this point you can find the new widget in the DOM, and for example add an * event listener to it. * @@ -2933,7 +2931,7 @@ <h1>Gracias</h1> event.initCustomEvent(eventName, true, true, detail); el.dispatchEvent(event); }; - + /** * Get the span element that is a child of toolbar, identified by index. * @@ -2959,7 +2957,7 @@ <h1>Gracias</h1> } return groups[index]; }; - + /** * Get the span element from groups[] that is immediately after given index. * diff --git a/docs/nimwc-presentation-es.md b/docs/nimwc-presentation-es.md index 4e2c9f971..56ac3d556 100644 --- a/docs/nimwc-presentation-es.md +++ b/docs/nimwc-presentation-es.md @@ -41,7 +41,6 @@ - SQLite o Postgres - Bulma CSS framework por defecto (No usa JS) -- Bootstrap CSS framework soportado - Agnostico de JavaScript framework - No JavaScript framework empaquetado - HTTP-Beast & Jester diff --git a/docs/nimwc-presentation-presentation.html b/docs/nimwc-presentation-presentation.html index ecd6d1a57..3919cf94d 100644 --- a/docs/nimwc-presentation-presentation.html +++ b/docs/nimwc-presentation-presentation.html @@ -32,7 +32,7 @@ </div> <!-- Ends presentation slide number: 0 --> - + <!-- Begins presentation slide number: 1 --> <div id=1 class="step contrast" data-rel-x="2883" data-rel-y="7845" data-rotate="-566" data-rotate-x="3" data-rotate-y="3" data-x="4573" data-y="7996" data-z="1" data-scale="8"> <h1>WAT?</h1> @@ -49,7 +49,7 @@ <h1>WAT?</h1> </div> <!-- Ends presentation slide number: 1 --> - + <!-- Begins presentation slide number: 2 --> <div id=2 class="step huerotate" data-rel-x="4401" data-rel-y="5339" data-rotate="386" data-rotate-x="5" data-rotate-y="5" data-x="3358" data-y="7874" data-z="1" data-scale="3"> <h1>Whats in the box?</h1> @@ -69,14 +69,13 @@ <h1>Whats in the box?</h1> </div> <!-- Ends presentation slide number: 2 --> - + <!-- Begins presentation slide number: 3 --> <div id=3 class="step saturate" data-rel-x="4474" data-rel-y="7501" data-rotate="163" data-rotate-x="2" data-rotate-y="5" data-x="1684" data-y="7677" data-z="5" data-scale="1"> <h1>Stack</h1> <ul> <li>SQLite or Postgres database</li> <li>Bulma CSS framework by default (wont use any JS)</li> -<li>Bootstrap CSS framework supported</li> <li>JavaScript framework agnostic</li> <li>No JavaScript framework shipped</li> <li>HTTP-Beast & Jester</li> @@ -86,7 +85,7 @@ <h1>Stack</h1> </div> <!-- Ends presentation slide number: 3 --> - + <!-- Begins presentation slide number: 4 --> <div id=4 class="step grayscale" data-rel-x="2368" data-rel-y="8773" data-rotate="-121" data-rotate-x="5" data-rotate-y="1" data-x="3864" data-y="7747" data-z="4" data-scale="7"> <h1>Nim Power</h1> @@ -112,7 +111,7 @@ <h1>Nim Power</h1> </div> <!-- Ends presentation slide number: 4 --> - + <!-- Begins presentation slide number: 5 --> <div id=5 class="step huerotate" data-rel-x="2113" data-rel-y="7559" data-rotate="-10" data-rotate-x="1" data-rotate-y="4" data-x="4002" data-y="8210" data-z="3" data-scale="4"> <h1>Admin Power</h1> @@ -134,7 +133,7 @@ <h1>Admin Power</h1> </div> <!-- Ends presentation slide number: 5 --> - + <!-- Begins presentation slide number: 6 --> <div id=6 class="step grayscale" data-rel-x="1633" data-rel-y="5788" data-rotate="-379" data-rotate-x="4" data-rotate-y="2" data-x="3050" data-y="7965" data-z="5" data-scale="5"> <h1>Some Screenshots</h1> @@ -143,42 +142,42 @@ <h1>Some Screenshots</h1> </div> <!-- Ends presentation slide number: 6 --> - + <!-- Begins presentation slide number: 7 --> <div id=7 class="step brightness" data-rel-x="4051" data-rel-y="8910" data-rotate="-63" data-rotate-x="1" data-rotate-y="4" data-x="3312" data-y="6300" data-z="1" data-scale="7"> <p><img src="https://raw.githubusercontent.com/ThomasTJdev/nim_websitecreator/master/docs/nimwc-editor-summer.png" alt="File Upload"></p> </div> <!-- Ends presentation slide number: 7 --> - + <!-- Begins presentation slide number: 8 --> <div id=8 class="step " data-rel-x="3233" data-rel-y="6842" data-rotate="521" data-rotate-x="4" data-rotate-y="4" data-x="3201" data-y="6865" data-z="2" data-scale="1"> <p><img src="https://raw.githubusercontent.com/ThomasTJdev/nim_websitecreator/master/docs/nimwc-editor-grape.png" alt="User Reset"></p> </div> <!-- Ends presentation slide number: 8 --> - + <!-- Begins presentation slide number: 9 --> <div id=9 class="step " data-rel-x="3922" data-rel-y="8322" data-rotate="405" data-rotate-x="3" data-rotate-y="5" data-x="2728" data-y="6009" data-z="5" data-scale="4"> <p><img src="https://raw.githubusercontent.com/ThomasTJdev/nim_websitecreator/master/docs/nimwc-firejail0.png" alt=""></p> </div> <!-- Ends presentation slide number: 9 --> - + <!-- Begins presentation slide number: 10 --> <div id=10 class="step brightness" data-rel-x="4741" data-rel-y="8967" data-rotate="-463" data-rotate-x="1" data-rotate-y="2" data-x="4838" data-y="7632" data-z="3" data-scale="9"> <p><img src="https://raw.githubusercontent.com/ThomasTJdev/nim_websitecreator/master/docs/nimwc-profile.png" alt=""></p> </div> <!-- Ends presentation slide number: 10 --> - + <!-- Begins presentation slide number: 11 --> <div id=11 class="step brightness" data-rel-x="3217" data-rel-y="8895" data-rotate="568" data-rotate-x="4" data-rotate-y="4" data-x="2781" data-y="7013" data-z="4" data-scale="2"> <p><img src="https://raw.githubusercontent.com/ThomasTJdev/nim_websitecreator/master/docs/nimwc-files.png" alt=""></p> </div> <!-- Ends presentation slide number: 11 --> - + <!-- Begins presentation slide number: 12 --> <div id=12 class="step saturate" data-rel-x="2720" data-rel-y="5549" data-rotate="319" data-rotate-x="4" data-rotate-y="2" data-x="4975" data-y="5548" data-z="3" data-scale="3"> <h5>CPU, Cores, RAM, SubProcesses</h5> @@ -186,14 +185,14 @@ <h5>CPU, Cores, RAM, SubProcesses</h5> </div> <!-- Ends presentation slide number: 12 --> - + <!-- Begins presentation slide number: 13 --> <div id=13 class="step sepia" data-rel-x="2943" data-rel-y="7804" data-rotate="-348" data-rotate-x="3" data-rotate-y="4" data-x="3951" data-y="6655" data-z="5" data-scale="1"> <p><img src="https://user-images.githubusercontent.com/1189414/53709326-72ef5180-3e16-11e9-944e-8120d6ab2959.png" alt="Libravatar"></p> </div> <!-- Ends presentation slide number: 13 --> - + <!-- Begins presentation slide number: 14 --> <div id=14 class="step " data-rel-x="2472" data-rel-y="8751" data-rotate="579" data-rotate-x="2" data-rotate-y="4" data-x="2726" data-y="5543" data-z="3" data-scale="7"> <h5>Plugin Store</h5> @@ -201,7 +200,7 @@ <h5>Plugin Store</h5> </div> <!-- Ends presentation slide number: 14 --> - + <!-- Begins presentation slide number: 15 --> <div id=15 class="step invert" data-rel-x="3501" data-rel-y="8316" data-rotate="-581" data-rotate-x="1" data-rotate-y="1" data-x="3407" data-y="8657" data-z="2" data-scale="2"> <h1>Future</h1> @@ -216,7 +215,7 @@ <h1>Future</h1> </div> <!-- Ends presentation slide number: 15 --> - + <!-- Begins presentation slide number: 16 --> <div id=16 class="step sepia" data-rel-x="2950" data-rel-y="6149" data-rotate="386" data-rotate-x="2" data-rotate-y="2" data-x="4707" data-y="7536" data-z="1" data-scale="1"> <h1>Thanks</h1> @@ -236,7 +235,7 @@ <h1>Thanks</h1> </div> <!-- Ends presentation slide number: 16 --> - + <!-- Begins presentation slide overview --> <div id=overview class=step data-x=3917 data-y=3063 data-scale=4 style='pointer-events:none' data-rotate=705 ></div> <!-- Ends presentation slide overview --> @@ -298,7 +297,6 @@ <h1>Thanks</h1> - SQLite or Postgres database - Bulma CSS framework by default (wont use any JS) -- Bootstrap CSS framework supported - JavaScript framework agnostic - No JavaScript framework shipped - HTTP-Beast & Jester @@ -676,24 +674,24 @@ <h1>Thanks</h1> // Let me show you the cogs that make impress.js run... (function ( document, window ) { 'use strict'; - + // HELPER FUNCTIONS - + // `pfx` is a function that takes a standard CSS property name as a parameter // and returns it's prefixed version valid for current browser it runs in. // The code is heavily inspired by Modernizr http://www.modernizr.com/ var pfx = (function () { - + var style = document.createElement('dummy').style, prefixes = 'Webkit Moz O ms Khtml'.split(' '), memory = {}; - + return function ( prop ) { if ( typeof memory[ prop ] === "undefined" ) { - + var ucProp = prop.charAt(0).toUpperCase() + prop.substr(1), props = (prop + ' ' + prefixes.join(ucProp + ' ') + ucProp).split(' '); - + memory[ prop ] = null; for ( var i in props ) { if ( style[ props[i] ] !== undefined ) { @@ -701,20 +699,20 @@ <h1>Thanks</h1> break; } } - + } - + return memory[ prop ]; }; - + })(); - + // `arraify` takes an array-like object and turns it into real Array // to make all the Array.prototype goodness available. var arrayify = function ( a ) { return [].slice.call( a ); }; - + // `css` function applies the styles given in `props` object to the element // given as `el`. It runs all property names through `pfx` function to make // sure proper prefixed version of the property is used. @@ -730,14 +728,14 @@ <h1>Thanks</h1> } return el; }; - + // `toNumber` takes a value given as `numeric` parameter and tries to turn // it into a number. If it is not possible it returns 0 (or other value // given as `fallback`). var toNumber = function (numeric, fallback) { return isNaN(numeric) ? (fallback || 0) : Number(numeric); }; - + var validateOrder = function ( order, fallback ) { var validChars = "xyz"; var returnStr = ""; @@ -757,26 +755,26 @@ <h1>Thanks</h1> else return "xyz"; }; - + // `byId` returns element with given `id` - you probably have guessed that ;) var byId = function ( id ) { return document.getElementById(id); }; - + // `$` returns first element for given CSS `selector` in the `context` of // the given element or whole document. var $ = function ( selector, context ) { context = context || document; return context.querySelector(selector); }; - + // `$$` return an array of elements for given CSS `selector` in the `context` of // the given element or whole document. var $$ = function ( selector, context ) { context = context || document; return arrayify( context.querySelectorAll(selector) ); }; - + // `triggerEvent` builds a custom DOM event with given `eventName` and `detail` data // and triggers it on element given as `el`. var triggerEvent = function (el, eventName, detail) { @@ -784,12 +782,12 @@ <h1>Thanks</h1> event.initCustomEvent(eventName, true, true, detail); el.dispatchEvent(event); }; - + // `translate` builds a translate transform string for given data. var translate = function ( t ) { return " translate3d(" + t.x + "px," + t.y + "px," + t.z + "px) "; }; - + // `rotate` builds a rotate transform string for given data. // By default the rotations are in X Y Z order that can be reverted by passing `true` // as second parameter. @@ -806,17 +804,17 @@ <h1>Thanks</h1> } return css; }; - + // `scale` builds a scale transform string for given data. var scale = function ( s ) { return " scale(" + s + ") "; }; - + // `perspective` builds a perspective transform string for given data. var perspective = function ( p ) { return " perspective(" + p + "px) "; }; - + // `getElementFromHash` returns an element located by id from hash part of // window location. var getElementFromHash = function () { @@ -824,37 +822,37 @@ <h1>Thanks</h1> // so both "fallback" `#slide-id` and "enhanced" `#/slide-id` will work return byId( window.location.hash.replace(/^#\/?/,"") ); }; - + // `computeWindowScale` counts the scale factor between window size and size // defined for the presentation in the config. var computeWindowScale = function ( config ) { var hScale = window.innerHeight / config.height, wScale = window.innerWidth / config.width, scale = hScale > wScale ? wScale : hScale; - + if (config.maxScale && scale > config.maxScale) { scale = config.maxScale; } - + if (config.minScale && scale < config.minScale) { scale = config.minScale; } - + return scale; }; - + // CHECK SUPPORT var body = document.body; - + var ua = navigator.userAgent.toLowerCase(); - var impressSupported = - // browser should support CSS 3D transtorms + var impressSupported = + // browser should support CSS 3D transtorms ( pfx("perspective") !== null ) && - + // and `classList` and `dataset` APIs ( body.classList ) && ( body.dataset ); - + if (!impressSupported) { // we can't be sure that `classList` is supported body.className += " impress-not-supported "; @@ -862,40 +860,40 @@ <h1>Thanks</h1> body.classList.remove("impress-not-supported"); body.classList.add("impress-supported"); } - + // GLOBALS AND DEFAULTS - + // This is where the root elements of all impress.js instances will be kept. // Yes, this means you can have more than one instance on a page, but I'm not // sure if it makes any sense in practice ;) var roots = {}; - + var preInitPlugins = []; var preStepLeavePlugins = []; - + // some default config values. var defaults = { width: 1024, height: 768, maxScale: 1, minScale: 0, - + perspective: 1000, - + transitionDuration: 1000 }; - + // it's just an empty function ... and a useless comment. var empty = function () { return false; }; - + // IMPRESS.JS API - + // And that's where interesting things will start to happen. // It's the core `impress` function that returns the impress.js API // for a presentation based on the element with given id ('impress' // by default). var impress = window.impress = function ( rootId ) { - + // If impress.js is not supported by the browser return a dummy API // it may not be a perfect solution but we return early and avoid // running code that may use features not implemented in the browser. @@ -909,49 +907,49 @@ <h1>Thanks</h1> addPreStepLeavePlugin: empty }; } - + rootId = rootId || "impress"; - + // if given root is already initialized just return the API if (roots["impress-root-" + rootId]) { return roots["impress-root-" + rootId]; } - + // data of all presentation steps var stepsData = {}; - + // element of currently active step var activeStep = null; - + // current state (position, rotation and scale) of the presentation var currentState = null; - + // array of step elements var steps = null; - + // configuration options var config = null; - + // scale factor of the browser window - var windowScale = null; - + var windowScale = null; + // root presentation elements var root = byId( rootId ); var canvas = document.createElement("div"); - + var initialized = false; - + // STEP EVENTS // // There are currently two step events triggered by impress.js - // `impress:stepenter` is triggered when the step is shown on the + // `impress:stepenter` is triggered when the step is shown on the // screen (the transition from the previous one is finished) and // `impress:stepleave` is triggered when the step is left (the // transition to next step just starts). - + // reference to last entered step var lastEntered = null; - + // `onStepEnter` is called whenever the step element is entered // but the event is triggered only if the step is different than // last entered step. @@ -961,7 +959,7 @@ <h1>Thanks</h1> lastEntered = step; } }; - + // `onStepLeave` is called whenever the step element is left // but the event is triggered only if the step is the same as // last entered step. @@ -971,9 +969,9 @@ <h1>Thanks</h1> lastEntered = null; } }; - + // `addPreInitPlugin` allows plugins to register a function that should - // be run (synchronously) at the beginning of init, before + // be run (synchronously) at the beginning of init, before // impress().init() itself executes. var addPreInitPlugin = function( plugin, weight ) { weight = toNumber(weight,10); @@ -982,7 +980,7 @@ <h1>Thanks</h1> } preInitPlugins[weight].push( plugin ); }; - + // Called at beginning of init, to execute all pre-init plugins. var execPreInitPlugins = function() { for( var i = 0; i < preInitPlugins.length; i++ ) { @@ -994,7 +992,7 @@ <h1>Thanks</h1> } } }; - + // `addPreStepLeavePlugin` allows plugins to register a function that should // be run (synchronously) at the beginning of goto() var addPreStepLeavePlugin = function( plugin, weight ) { @@ -1004,7 +1002,7 @@ <h1>Thanks</h1> } preStepLeavePlugins[weight].push( plugin ); }; - + // Called at beginning of goto(), to execute all preStepLeave plugins. var execPreStepLeavePlugins = function(event) { for( var i = 0; i < preStepLeavePlugins.length; i++ ) { @@ -1037,13 +1035,13 @@ <h1>Thanks</h1> transitionDuration: toNumber(data.transitionDuration, config.transitionDuration), el: el }; - + if ( !el.id ) { el.id = "step-" + (idx + 1); } - + stepsData["impress-" + el.id] = step; - + css(el, { position: "absolute", transform: "translate(-50%,-50%)" + @@ -1053,12 +1051,12 @@ <h1>Thanks</h1> transformStyle: "preserve-3d" }); }; - + // `init` API function that initializes (and runs) the presentation. var init = function () { if (initialized) { return; } execPreInitPlugins(); - + // First we set up the viewport for mobile devices. // For some reason iPad goes nuts when it is not done properly. var meta = $("meta[name='viewport']") || document.createElement("meta"); @@ -1067,41 +1065,41 @@ <h1>Thanks</h1> meta.name = 'viewport'; document.head.appendChild(meta); } - + // initialize configuration object var rootData = root.dataset; config = { width: toNumber( rootData.width, defaults.width ), height: toNumber( rootData.height, defaults.height ), maxScale: toNumber( rootData.maxScale, defaults.maxScale ), - minScale: toNumber( rootData.minScale, defaults.minScale ), + minScale: toNumber( rootData.minScale, defaults.minScale ), perspective: toNumber( rootData.perspective, defaults.perspective ), transitionDuration: toNumber( rootData.transitionDuration, defaults.transitionDuration ) }; - + windowScale = computeWindowScale( config ); - + // wrap steps with "canvas" element arrayify( root.childNodes ).forEach(function ( el ) { canvas.appendChild( el ); }); root.appendChild(canvas); - + // set initial styles document.documentElement.style.height = "100%"; - + css(body, { height: "100%", overflow: "hidden" }); - + var rootStyles = { position: "absolute", transformOrigin: "top left", transition: "all 0s ease-in-out", transformStyle: "preserve-3d" }; - + css(root, rootStyles); css(root, { top: "50%", @@ -1109,26 +1107,26 @@ <h1>Thanks</h1> transform: perspective( config.perspective/windowScale ) + scale( windowScale ) }); css(canvas, rootStyles); - + body.classList.remove("impress-disabled"); body.classList.add("impress-enabled"); - + // get and init steps steps = $$(".step", root); steps.forEach( initStep ); - + // set a default initial state of the canvas currentState = { translate: { x: 0, y: 0, z: 0 }, rotate: { x: 0, y: 0, z: 0, order: "xyz" }, scale: 1 }; - + initialized = true; - + triggerEvent(root, "impress:init", { api: roots[ "impress-root-" + rootId ] }); }; - + // `getStep` is a helper function that returns a step element defined by parameter. // If a number is given, step with index given by the number is returned, if a string // is given step element with such id is returned, if DOM element is given it is returned @@ -1141,19 +1139,19 @@ <h1>Thanks</h1> } return (step && step.id && stepsData["impress-" + step.id]) ? step : null; }; - + // used to reset timeout for `impress:stepenter` event var stepEnterTimeout = null; - + // `goto` API function that moves to step given with `el` parameter (by index, id or element), // with a transition `duration` optionally given as second parameter. var goto = function ( el, duration, reason = "goto" ) { - + if ( !initialized || !(el = getStep(el)) ) { // presentation not initialized or given element is not a step return false; } - + // Sometimes it's possible to trigger focus on first link with some keyboard action. // Browser in such a case tries to scroll the page to make this element visible // (even that body overflow is set to hidden) and it breaks our careful positioning. @@ -1163,9 +1161,9 @@ <h1>Thanks</h1> // // If you are reading this and know any better way to handle it, I'll be glad to hear about it! window.scrollTo(0, 0); - + var step = stepsData["impress-" + el.id]; - + // If we are in fact moving to another step, start with executing the registered preStepLeave plugins. if (activeStep && activeStep !== el) { var event = { target: activeStep, detail : {} }; @@ -1178,15 +1176,15 @@ <h1>Thanks</h1> step = stepsData["impress-" + el.id]; duration = event.detail.transitionDuration; } - + if ( activeStep ) { activeStep.classList.remove("active"); body.classList.remove("impress-on-" + activeStep.id); } el.classList.add("active"); - + body.classList.add("impress-on-" + el.id); - + // compute target state of the canvas based on given step var target = { rotate: { @@ -1202,7 +1200,7 @@ <h1>Thanks</h1> }, scale: 1 / step.scale }; - + // Check if the transition is zooming in or not. // // This information is used to alter the transition style: @@ -1210,23 +1208,23 @@ <h1>Thanks</h1> // and the scaling is delayed, but when we are zooming out we start // with scaling down and move and rotation are delayed. var zoomin = target.scale >= currentState.scale; - + duration = toNumber(duration, config.transitionDuration); var delay = (duration / 2); - + // if the same step is re-selected, force computing window scaling, // because it is likely to be caused by window resize if (el === activeStep) { windowScale = computeWindowScale(config); } - + var targetScale = target.scale * windowScale; - + // trigger leave of currently active element (if it's not the same step again) if (activeStep && activeStep !== el) { onStepLeave(activeStep, el); } - + // Now we alter transforms of `root` and `canvas` to trigger transitions. // // And here is why there are two elements: `root` and `canvas` - they are @@ -1242,13 +1240,13 @@ <h1>Thanks</h1> transitionDuration: duration + "ms", transitionDelay: (zoomin ? delay : 0) + "ms" }); - + css(canvas, { transform: rotate(target.rotate, true) + translate(target.translate), transitionDuration: duration + "ms", transitionDelay: (zoomin ? 0 : delay) + "ms" }); - + // Here is a tricky part... // // If there is no change in scale or no change in rotation and translation, it means there was actually @@ -1264,17 +1262,17 @@ <h1>Thanks</h1> currentState.translate.y === target.translate.y && currentState.translate.z === target.translate.z) ) { delay = 0; } - + // store current state currentState = target; activeStep = el; - + // And here is where we trigger `impress:stepenter` event. // We simply set up a timeout to fire it taking transition duration (and possible delay) into account. // // I really wanted to make it in more elegant way. The `transitionend` event seemed to be the best way // to do it, but the fact that I'm using transitions on two separate elements and that the `transitionend` - // event is only triggered when there was a transition (change in the values) caused some bugs and + // event is only triggered when there was a transition (change in the values) caused some bugs and // made the code really complicated, cause I had to handle all the conditions separately. And it still // needed a `setTimeout` fallback for the situations when there is no transition at all. // So I decided that I'd rather make the code simpler than use shiny new `transitionend`. @@ -1285,46 +1283,46 @@ <h1>Thanks</h1> stepEnterTimeout = window.setTimeout(function() { onStepEnter(activeStep); }, duration + delay); - + return el; }; - + // `prev` API function goes to previous step (in document order) var prev = function () { var prev = steps.indexOf( activeStep ) - 1; prev = prev >= 0 ? steps[ prev ] : steps[ steps.length-1 ]; - + return goto(prev, undefined, "prev"); }; - + // `next` API function goes to next step (in document order) var next = function () { var next = steps.indexOf( activeStep ) + 1; next = next < steps.length ? steps[ next ] : steps[ 0 ]; - + return goto(next, undefined, "next"); }; - + // Swipe for touch devices by @and3rson. // Below we extend the api to control the animation between the currently // active step and a presumed next/prev step. See touch plugin for // an example of using this api. - + // Helper function var interpolate = function(a, b, k) { return a + (b - a) * k; }; - - // Animate a swipe. + + // Animate a swipe. // // pct is a value between -1.0 and +1.0, designating the current length // of the swipe. // - // If pct is negative, swipe towards the next() step, if positive, - // towards the prev() step. + // If pct is negative, swipe towards the next() step, if positive, + // towards the prev() step. // - // Note that pre-stepleave plugins such as goto can mess with what is a - // next() and prev() step, so we need to trigger the pre-stepleave event + // Note that pre-stepleave plugins such as goto can mess with what is a + // next() and prev() step, so we need to trigger the pre-stepleave event // here, even if a swipe doesn't guarantee that the transition will // actually happen. // @@ -1352,7 +1350,7 @@ <h1>Thanks</h1> } execPreStepLeavePlugins(event); var nextElement = event.detail.next; - + var nextStep = stepsData['impress-' + nextElement.id]; var zoomin = nextStep.scale >= currentState.scale; // if the same step is re-selected, force computing window scaling, @@ -1409,26 +1407,26 @@ <h1>Thanks</h1> steps.forEach(function (step) { step.classList.add("future"); }); - + root.addEventListener("impress:stepenter", function (event) { event.target.classList.remove("past"); event.target.classList.remove("future"); event.target.classList.add("present"); }, false); - + root.addEventListener("impress:stepleave", function (event) { event.target.classList.remove("present"); event.target.classList.add("past"); }, false); - + }, false); - + // Adding hash change support. root.addEventListener("impress:init", function(){ - + // last hash detected var lastHash = ""; - + // `#/step-id` is used instead of `#step-id` to prevent default browser // scrolling to element in hash. // @@ -1438,10 +1436,10 @@ <h1>Thanks</h1> root.addEventListener("impress:stepenter", function (event) { window.location.hash = lastHash = "#/" + event.target.id; }, false); - + window.addEventListener("hashchange", function () { // When the step is entered hash in the location is updated - // (just few lines above from here), so the hash change is + // (just few lines above from here), so the hash change is // triggered and we would call `goto` again on the same element. // // To avoid this we store last entered hash and compare. @@ -1449,14 +1447,14 @@ <h1>Thanks</h1> goto( getElementFromHash() ); } }, false); - - // START + + // START // by selecting step defined in url or first step of the presentation goto(getElementFromHash() || steps[0], 0); }, false); - + body.classList.add("impress-disabled"); - + // store and return API for given impress.js root element return (roots[ "impress-root-" + rootId ] = { init: init, @@ -1469,7 +1467,7 @@ <h1>Thanks</h1> }); }; - + // flag that can be used in JS to check if browser have passed the support test impress.supported = impressSupported; @@ -1504,19 +1502,19 @@ <h1>Thanks</h1> return isNaN(numeric) ? (fallback || 0) : Number(numeric); }; - // On impress:init, check whether there is a default setting, as well as + // On impress:init, check whether there is a default setting, as well as // handle step-1. document.addEventListener("impress:init", function (event) { // Getting API from event data instead of global impress().init(). // You don't even need to know what is the id of the root element - // or anything. `impress:init` event data gives you everything you + // or anything. `impress:init` event data gives you everything you // need to control the presentation that was just initialized. api = event.detail.api; root = event.target; // Element attributes starting with 'data-', become available under // element.dataset. In addition hyphenized words become camelCased. var data = root.dataset; - + if (data.autoplay) { autoplayDefault = toNumber(data.autoplay, 0); } @@ -1529,7 +1527,7 @@ <h1>Thanks</h1> // Note that right after impress:init event, also impress:stepenter is // triggered for the first slide, so that's where code flow continues. }, false); - + // If default autoplay time was defined in the presentation root, or // in this step, set timeout. document.addEventListener("impress:stepenter", function (event) { @@ -1550,13 +1548,13 @@ <h1>Thanks</h1> if ( timeoutHandle ) { clearTimeout(timeoutHandle); } - + if ( timeout > 0) { timeoutHandle = setTimeout( function() { api.next(); }, timeout*1000 ); } setButtonText(); }; - + /*** Toolbar plugin integration *******************************************/ var status = "not clicked"; @@ -1592,7 +1590,7 @@ <h1>Thanks</h1> return "▶"; // play } }; - + var setButtonText = function() { if (toolbarButton) { // Keep button size the same even if label content is changing @@ -1635,16 +1633,16 @@ <h1>Thanks</h1> * * Press Ctrl+b to hide all slides, and Ctrl+b again to show them. * Also navigating to a different slide will show them again (impress:stepleave). - * + * * Copyright 2014 @Strikeskids * Released under the MIT license. */ (function ( document, window ) { 'use strict'; - + var canvas = null; var blackedOut = false; - + // While waiting for a shared library of utilities, copying these 2 from main impress.js var css = function ( el, props ) { var key, pkey; @@ -1660,17 +1658,17 @@ <h1>Thanks</h1> }; var pfx = (function () { - + var style = document.createElement('dummy').style, prefixes = 'Webkit Moz O ms Khtml'.split(' '), memory = {}; - + return function ( prop ) { if ( typeof memory[ prop ] === "undefined" ) { - + var ucProp = prop.charAt(0).toUpperCase() + prop.substr(1), props = (prop + ' ' + prefixes.join(ucProp + ' ') + ucProp).split(' '); - + memory[ prop ] = null; for ( var i in props ) { if ( style[ props[i] ] !== undefined ) { @@ -1678,14 +1676,14 @@ <h1>Thanks</h1> break; } } - + } - + return memory[ prop ]; }; - + })(); - + var removeBlackout = function() { @@ -1714,7 +1712,7 @@ <h1>Thanks</h1> var api = event.detail.api; var root = event.target; canvas = root.firstElementChild; - + document.addEventListener("keydown", function ( event ) { if ( event.ctrlKey && event.keyCode === 66 ) { event.preventDefault(); @@ -1729,7 +1727,7 @@ <h1>Thanks</h1> } } }, false); - + document.addEventListener("keyup", function ( event ) { if ( event.ctrlKey && event.keyCode === 66 ) { event.preventDefault(); @@ -1737,7 +1735,7 @@ <h1>Thanks</h1> }, false); }, false); - + document.addEventListener("impress:stepleave", function (event) { removeBlackout(); }, false); @@ -1780,7 +1778,7 @@ <h1>Thanks</h1> var markdownDivs = document.querySelectorAll(".markdown"); for (var element of markdownDivs) { - // Note: unlike the previous two, markdown.js doesn't automatically find or convert anything in + // Note: unlike the previous two, markdown.js doesn't automatically find or convert anything in var slides = element.textContent.split(/^-----$/m); var i = slides.length - 1; element.innerHTML = markdown.toHTML(slides[i]); @@ -1800,11 +1798,11 @@ <h1>Thanks</h1> element.id = id; } } // markdown - + if(window.hljs){ - hljs.initHighlightingOnLoad(); + hljs.initHighlightingOnLoad(); } - + if(window.mermaid){ mermaid.initialize({startOnLoad:true}); } @@ -1813,7 +1811,7 @@ <h1>Thanks</h1> var postInit = function(event){ if(window.impressConsole){ // Init impressConsole.js. - // This does not yet show the window, just activates the plugin. + // This does not yet show the window, just activates the plugin. // Press 'P' to show the console. // Note that we must pass the correct path to css file as well. // See https://github.com/regebro/impress-console/issues/19 @@ -1823,7 +1821,7 @@ <h1>Thanks</h1> else{ impressConsole().init(); } - + // Add 'P' to the help popup triggerEvent(document, "impress:help:add", { command : "P", text : "Presenter console", row : 10} ); @@ -1869,11 +1867,11 @@ <h1>Thanks</h1> */ (function ( document, window ) { 'use strict'; - + document.addEventListener("impress:stepleave", function (event) { document.activeElement.blur() }, false); - + })(document, window); @@ -1884,14 +1882,14 @@ <h1>Thanks</h1> * and will alter the destination where to transition next. * * Example: - * + * * <!-- When leaving this step, go directly to "step-5" --> * <div class="step" data-goto="step-5"> - * + * * <!-- When leaving this step with next(), go directly to "step-5", instead of the next step. * If moving backwards to previous step - e.g. prev() instead of next() - then go to "step-1". --> * <div class="step" data-goto-next="step-5" data-goto-prev="step-1"> - * + * * Copyright 2016 Henrik Ingo (@henrikingo) * Released under the MIT license. */ @@ -1902,14 +1900,14 @@ <h1>Thanks</h1> return isNaN(numeric) ? (fallback || 0) : Number(numeric); }; - + var goto = function(event) { if ( (!event) || (!event.target) ) return; - + var data = event.target.dataset; var steps = document.querySelectorAll(".step"); - + // Handle event.target data-goto-next attribute if ( !isNaN(data.gotoNext) && event.detail.reason == "next" ) { event.detail.next = steps[data.gotoNext]; @@ -1965,10 +1963,10 @@ <h1>Thanks</h1> } } }; - + // Register the plugin to be called in pre-stepleave phase impress().addPreStepLeavePlugin( goto ); - + })(document, window); @@ -1976,7 +1974,7 @@ <h1>Thanks</h1> * Help popup plugin * * Example: - * + * * <!-- Show a help popup at start, or if user presses 'H' --> * <div id="impress-help"></div> * @@ -2013,7 +2011,7 @@ <h1>Thanks</h1> } } }; - + var toggleHelp = function() { var helpDiv = document.getElementById("impress-help"); if(helpDiv.style.display == 'block') { @@ -2069,10 +2067,10 @@ <h1>Thanks</h1> }, 7000); } }); - + // Use our own API to register the help text for 'h' triggerEvent(document, "impress:help:add", { command : "H", text : "Show this help", row : 0} ); - + })(document, window); @@ -2118,7 +2116,7 @@ <h1>Thanks</h1> // Detect mobile browsers & add CSS class as appropriate. document.addEventListener("impress:init", function (event) { var body = document.body; - if(/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)){ + if(/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)){ body.classList.add('impress-mobile'); } }); @@ -2147,7 +2145,7 @@ <h1>Thanks</h1> /** * Mouse timeout plugin * - * After 3 seconds of mouse inactivity, add the css class + * After 3 seconds of mouse inactivity, add the css class * `body.impress-mouse-timeout`. On `mousemove`, `click` or `touch`, remove the * class. * @@ -2224,7 +2222,7 @@ <h1>Thanks</h1> */ (function ( document, window ) { 'use strict'; - + var triggerEvent = function (el, eventName, detail) { var event = document.createEvent("CustomEvent"); event.initCustomEvent(eventName, true, true, detail); @@ -2235,7 +2233,7 @@ <h1>Thanks</h1> document.addEventListener("impress:init", function (event) { // Getting API from event data. // So you don't event need to know what is the id of the root element - // or anything. `impress:init` event data gives you everything you + // or anything. `impress:init` event data gives you everything you // need to control the presentation that was just initialized. var api = event.detail.api; var tab = 9; @@ -2258,13 +2256,13 @@ <h1>Thanks</h1> if ( event.altKey || event.ctrlKey || event.metaKey ){ return false; } - + // In the case of TAB, we force step navigation always, overriding the browser navigation between // input elements, buttons and links. if ( event.keyCode === 9 ) { return true; } - + // With the sole exception of TAB, we also ignore keys pressed if shift is down. if ( event.shiftKey ){ return false; @@ -2280,17 +2278,17 @@ <h1>Thanks</h1> return true; } }; - - + + // KEYBOARD NAVIGATION HANDLERS - + // Prevent default keydown action when one of supported key is pressed. document.addEventListener("keydown", function ( event ) { if ( isNavigationEvent(event) ) { event.preventDefault(); } }, false); - + // Trigger impress action (next or prev) on keyup. document.addEventListener("keyup", function ( event ) { if ( isNavigationEvent(event) ) { @@ -2320,7 +2318,7 @@ <h1>Thanks</h1> event.preventDefault(); } }, false); - + // delegated handler for clicking on the links to presentation steps document.addEventListener("click", function ( event ) { // event delegation with "bubbling" @@ -2330,22 +2328,22 @@ <h1>Thanks</h1> (target !== document.documentElement) ) { target = target.parentNode; } - + if ( target.tagName === "A" ) { var href = target.getAttribute("href"); - + // if it's a link to presentation step, target this step if ( href && href[0] === '#' ) { target = document.getElementById( href.slice(1) ); } } - + if ( api.goto(target) ) { event.stopImmediatePropagation(); event.preventDefault(); } }, false); - + // delegated handler for clicking on step elements document.addEventListener("click", function ( event ) { var target = event.target; @@ -2354,17 +2352,17 @@ <h1>Thanks</h1> (target !== document.documentElement) ) { target = target.parentNode; } - + if ( api.goto(target) ) { event.preventDefault(); } }, false); - + // Add a line to the help popup triggerEvent(document, "impress:help:add", { command : "Left & Right", text : "Previous & Next step", row : 1} ); - + }, false); - + })(document, window); @@ -2402,7 +2400,7 @@ <h1>Thanks</h1> tempDiv.innerHTML = html; return tempDiv.firstChild; }; - + var selectOptionsHtml = function(){ var options = ""; for ( var i = 0; i < steps.length; i++ ) { @@ -2411,7 +2409,7 @@ <h1>Thanks</h1> options = options + '<option value="' + steps[i].id + '">' + steps[i].id + '</option>' + "\n"; } } - return options; + return options; }; var addNavigationControls = function( event ) { @@ -2443,12 +2441,12 @@ <h1>Thanks</h1> function() { api.next(); }); - + triggerEvent(toolbar, "impress:toolbar:appendChild", { group : 0, element : prev } ); triggerEvent(toolbar, "impress:toolbar:appendChild", { group : 0, element : select } ); triggerEvent(toolbar, "impress:toolbar:appendChild", { group : 0, element : next } ); }; - + // API for not listing given step in the select widget. // For example, if you set class="skip" on some element, you may not want it to show up in the list either. // Otoh we cannot assume that, or anything else, so steps that user wants omitted must be specifically added with this API call. @@ -2458,7 +2456,7 @@ <h1>Thanks</h1> select.innerHTML = selectOptionsHtml(); } }, false); - + // wait for impress.js to be initialized document.addEventListener("impress:init", function (event) { toolbar = document.querySelector("#impress-toolbar"); @@ -2466,13 +2464,13 @@ <h1>Thanks</h1> addNavigationControls( event ); } }, false); - + })(document, window); (function ( document, window ) { 'use strict'; - + var stepids = []; // wait for impress.js to be initialized document.addEventListener("impress:init", function (event) { @@ -2485,12 +2483,12 @@ <h1>Thanks</h1> }); var progressbar = document.querySelector('div.impress-progressbar div'); var progress = document.querySelector('div.impress-progress'); - - if (null !== progressbar || null !== progress) { + + if (null !== progressbar || null !== progress) { document.addEventListener("impress:stepleave", function (event) { updateProgressbar(event.detail.next.id); }); - + document.addEventListener("impress:stepenter", function (event) { updateProgressbar(event.target.id); }); @@ -2515,26 +2513,26 @@ <h1>Thanks</h1> * since as you add, remove or move steps, you may not need to edit the positions * as much as is the case with the absolute coordinates supported by impress.js * core. - * + * * Example: - * + * * <!-- Position step 1000 px to the right and 500 px up from the previous step. --> * <div class="step" data-rel-x="1000" data-rel-y="500"> - * + * * Following html attributes are supported for step elements: - * + * * data-rel-x * data-rel-y * data-rel-z - * - * These values are also inherited from the previous step. This makes it easy to - * create a boring presentation where each slide shifts for example 1000px down + * + * These values are also inherited from the previous step. This makes it easy to + * create a boring presentation where each slide shifts for example 1000px down * from the previous. - * + * * In addition to plain numbers, which are pixel values, it is also possible to * define relative positions as a multiple of screen height and width, using * a unit of "h" and "w", respectively, appended to the number. - * + * * Example: * * <div class="step" data-rel-x="1.5w" data-rel-y="1.5h"> @@ -2543,7 +2541,7 @@ <h1>Thanks</h1> * core at the beginning of `impress().init()`. This allows it to process its own * data attributes first, and possibly alter the data-x, data-y and data-z attributes * that will then be processed by `impress().init()`. - * + * * (Another name for this kind of plugin might be called a *filter plugin*, but * *pre-init plugin* is more generic, as a plugin might do whatever it wants in * the pre-init stage.) @@ -2583,7 +2581,7 @@ <h1>Thanks</h1> var computeRelativePositions = function ( el, prev ) { var data = el.dataset; - + if( !prev ) { // For the first step, inherit these defaults var prev = { x:0, y:0, z:0, relative: {x:0, y:0, z:0} }; @@ -2604,16 +2602,16 @@ <h1>Thanks</h1> if(data.x !== undefined) step.relative.x = 0; if(data.y !== undefined) step.relative.y = 0; if(data.z !== undefined) step.relative.z = 0; - + // Apply relative position to absolute position, if non-zero // Note that at this point, the relative values contain a number value of pixels. step.x = step.x + step.relative.x; step.y = step.y + step.relative.y; step.z = step.z + step.relative.z; - - return step; + + return step; }; - + var rel = function() { var root = document.querySelector("#impress"); var steps = root.querySelectorAll(".step"); @@ -2628,10 +2626,10 @@ <h1>Thanks</h1> prev = step; } }; - + // Register the plugin to be called in pre-init phase impress().addPreInitPlugin( rel ); - + })(document, window); @@ -2651,7 +2649,7 @@ <h1>Thanks</h1> */ (function ( document, window ) { 'use strict'; - + // throttling function calls, by Remy Sharp // http://remysharp.com/2010/07/21/throttling-function-calls/ var throttle = function (fn, delay) { @@ -2664,7 +2662,7 @@ <h1>Thanks</h1> }, delay); }; }; - + // wait for impress.js to be initialized document.addEventListener("impress:init", function (event) { var api = event.detail.api; @@ -2674,7 +2672,7 @@ <h1>Thanks</h1> api.goto( document.querySelector(".step.active"), 500 ); }, 250), false); }, false); - + })(document, window); @@ -2682,12 +2680,12 @@ <h1>Thanks</h1> * Skip Plugin * * Example: - * + * * <!-- This slide is disabled in presentations, when moving with next() * and prev() commands, but you can still move directly to it, for * example with a url (anything using goto()). --> * <div class="step skip"> - * + * * Copyright 2016 Henrik Ingo (@henrikingo) * Released under the MIT license. */ @@ -2722,7 +2720,7 @@ <h1>Thanks</h1> var skip = function(event) { if ( (!event) || (!event.target) ) return; - + if ( event.detail.next.classList.contains("skip") ) { if ( event.detail.reason == "next" ) { // Go to the next next step instead @@ -2739,11 +2737,11 @@ <h1>Thanks</h1> event.detail.transitionDuration = toNumber( event.detail.next.dataset.transitionDuration, event.detail.transitionDuration); } }; - + // Register the plugin to be called in pre-stepleave phase // The weight makes this plugin run early. This is a good thing, because this plugin calls itself recursively. impress().addPreStepLeavePlugin( skip, 1 ); - + })(document, window); @@ -2751,12 +2749,12 @@ <h1>Thanks</h1> * Stop Plugin * * Example: - * + * * <!-- Stop at this slide. - * (For example, when used on the last slide, this prevents the + * (For example, when used on the last slide, this prevents the * presentation from wrapping back to the beginning.) --> * <div class="step" data-stop="true"> - * + * * Copyright 2016 Henrik Ingo (@henrikingo) * Released under the MIT license. */ @@ -2766,17 +2764,17 @@ <h1>Thanks</h1> var stop = function(event) { if ( (!event) || (!event.target) ) return; - + if ( event.target.classList.contains("stop") ) { if ( event.detail.reason == "next" ) event.detail.next = event.target; } }; - + // Register the plugin to be called in pre-stepleave phase // The weight makes this plugin run fairly late. impress().addPreStepLeavePlugin( stop, 20 ); - + })(document, window); @@ -2787,9 +2785,9 @@ <h1>Thanks</h1> * or tapping on the left/right edges of the screen. * * - * + * * Copyright 2015: Andrew Dunai (@and3rson) - * Modified to a plugin, 2016: Henrik Ingo (@henrikingo) + * Modified to a plugin, 2016: Henrik Ingo (@henrikingo) * * MIT License */ @@ -2803,7 +2801,7 @@ <h1>Thanks</h1> var lastX = 0; var lastDX = 0; var threshold = window.innerWidth / 20; - + document.addEventListener('touchstart', function (event) { lastX = startX = event.touches[0].clientX; }); @@ -2861,7 +2859,7 @@ <h1>Thanks</h1> * To add/activate the toolbar in your presentation, add this div: * * <div id="impress-toolbar"></div> - * + * * Styling the toolbar is left to presentation author. Here's an example CSS: * * .impress-enabled div#impress-toolbar { @@ -2893,7 +2891,7 @@ <h1>Thanks</h1> * callback : "mycallback", // Toolbar plugin will trigger event `impress:toolbar:added:mycallback` when done. * before: element } // The reference element for an insertBefore() call. * - * You should also listen to the `impress:toolbar:added:mycallback` event. At + * You should also listen to the `impress:toolbar:added:mycallback` event. At * this point you can find the new widget in the DOM, and for example add an * event listener to it. * @@ -2916,7 +2914,7 @@ <h1>Thanks</h1> event.initCustomEvent(eventName, true, true, detail); el.dispatchEvent(event); }; - + /** * Get the span element that is a child of toolbar, identified by index. * @@ -2942,7 +2940,7 @@ <h1>Thanks</h1> } return groups[index]; }; - + /** * Get the span element from groups[] that is immediately after given index. * diff --git a/docs/nimwc-presentation.md b/docs/nimwc-presentation.md index 5db140d5b..eee723618 100644 --- a/docs/nimwc-presentation.md +++ b/docs/nimwc-presentation.md @@ -42,7 +42,6 @@ - SQLite or Postgres database - Bulma CSS framework by default (wont use any JS) -- Bootstrap CSS framework supported - JavaScript framework agnostic - No JavaScript framework shipped - HTTP-Beast & Jester diff --git a/nimwc.nim b/nimwc.nim index 8bc397e99..bfeed4ebe 100644 --- a/nimwc.nim +++ b/nimwc.nim @@ -1,115 +1,28 @@ -import os, osproc, parsecfg, parseopt, rdstdin, strutils, terminal, times - -import contra +import os, osproc, parsecfg, parseopt, rdstdin, strutils, terminal, times, json import - nimwcpkg/constants/constants, nimwcpkg/enums/enums, - nimwcpkg/databases/databases, nimwcpkg/files/files, nimwcpkg/utils/loggers + nimwcpkg/constants/constants, nimwcpkg/enums/enums, nimwcpkg/utils/utils, nimwcpkg/utils/projectgen, nimwcpkg/utils/jail, + nimwcpkg/databases/databases, nimwcpkg/files/files, nimwcpkg/utils/loggers, nimwcpkg/utils/sysinfo, nimwcpkg/utils/updaters when defined(postgres): import db_postgres else: import db_sqlite -when not defined(firejail): {.warning: "Firejail is disabled, running unsecure.".} -else: from firejail import firejailVersion, firejailFeatures - hardenedBuild() - -when not defined(contracts): {.warning: "Design by Contract is Disabled, running unassertive.".} -when not defined(ssl): {.warning: "SSL is Disabled, running unsecure.".} -when not defined(firejail): {.warning: "Firejail is Disabled, running unsecure.".} +when not defined(ssl): {.warning: "SSL is Disabled, running unsecure.".} +when not defined(firejail): {.warning: "Firejail is Disabled, running unsecure.".} +when not defined(firejail): {.warning: "Firejail is disabled, running unsecure.".} var runInLoop = true nimwcMain: Process - - -macro configExists(): untyped = - ## Macro to check if the config file is present - let dir = parentDir(currentSourcePath()) - if not fileExists(replace(dir, "/nimwcpkg", "") & "/config/config.cfg"): - echo config_not_found_msg - discard staticExec("cp " & dir & "/config/config_default.cfg " & dir & "/config/config.cfg") - -configExists() - - -proc updateNimwc() = - ## GIT hard update - preconditions(existsDir"plugins/", existsDir"public/css/", existsDir"public/js/", - existsFile"plugins/plugin_import.txt", existsFile"public/css/style_custom.css", - existsFile"public/js/js_custom.js", findExe"git".len > 0) - # No postconditions because we directly quit anyways. - const cmd = "git fetch --all ; git reset --hard origin/master" - let - pluginImport = readFile"plugins/plugin_import.txt" # Save contents - styleCustom = readFile"public/css/style_custom.css" - jsCustom = readFile"public/js/js_custom.js" - humansTxt = readFile"public/humans.txt" - when not defined(release): echo cmd - discard execCmd(cmd) - writeFile("plugins/plugin_import.txt", pluginImport) # Write contents back - writeFile("public/css/style_custom.css", styleCustom) - writeFile("public/js/js_custom.js", jsCustom) - writeFile("public/humans.txt", humansTxt) - echo "\n\n\nTo finish the update:\n - Compile NimWC\n - Run with the arg `--newdb`\n" - quit("Git fetch done\n", 0) - - -proc pluginSkeleton() = - ## Creates the skeleton (folders and files) for a plugin - styledEcho(fgCyan, bgBlack, skeletonMsg) - let pluginName = normalize(readLineFromStdin("Plugin name: ").string.strip.toLowerAscii) - assert pluginName.len > 1, "Plugin name needs to be longer: " & pluginName - assert " " notin pluginName, "Plugin name may not contain spaces: " & pluginName - - let - authorName = readLineFromStdin("Author name: ").string.strip - authorMail = readLineFromStdin("Author email: ").string.strip.toLowerAscii - authorWeb = readLineFromStdin("Author website HTTP URL: ").string.strip - - # Create dirs - discard existsOrCreateDir("plugins") - let folder = "plugins" / pluginName - discard existsOrCreateDir(folder) - discard existsOrCreateDir(folder / "public") - - # Create files - writeFile(folder / pluginName & ".nim", reqCode.format(pluginName)) - writeFile(folder / "routes.nim", reqRoutes.format(pluginName)) - writeFile(folder / "public" / "js.js", - "/* https://github.com/pragmagic/karax OR Vanilla JavaScript */\n") - writeFile(folder / "public" / "style.css", - "/* https://bulma.io/documentation OR https://getbootstrap.com OR clean CSS */\n") - - if readLineFromStdin("Generate optional compile-time config files? (y/N): ").normalize == "y": - if readLineFromStdin("Use NimScript instead of CFG? (y/N): ").normalize == "y": - writeFile(folder / pluginName & ".nims", "# https://nim-lang.org/docs/nims.html\n") - else: - writeFile(folder / pluginName & ".nim.cfg", "# https://nim-lang.org/docs/parsecfg.html\n") - - if readLineFromStdin("Generate optional NimWC files? (y/N): ").string.strip.toLowerAscii == "y": - writeFile(folder / "public/js_private.js", "") - writeFile(folder / "public/style_private.css", "") - writeFile(folder / "html.nimf", "<!-- https://nim-lang.org/docs/filters.html -->\n") - - if readLineFromStdin("Generate optional .gitignore file? (y/N): ").normalize == "y": - writeFile(folder / ".gitattributes", "*.* linguist-language=Nim\n") - writeFile(folder / ".gitignore", "*.c\n*.h\n*.o\n*.sql\n*.log\n*.s") - - if readLineFromStdin("Generate optional Documentation files? (y/N): ").normalize == "y": - let ext = if readLineFromStdin("Use Markdown(MD) instead of ReSTructuredText(RST)? (y/N): ").normalize == "y": "md" else: "rst" - writeFile(folder / "LICENSE." & ext, "See https://tldrlegal.com/licenses/browse\n") - writeFile(folder / "CODE_OF_CONDUCT." & ext, "") - writeFile(folder / "CONTRIBUTING." & ext, "") - writeFile(folder / "AUTHORS." & ext, "# Authors\n\n- " & authorName & "\n") - writeFile(folder / "README." & ext, "# " & pluginName & "\n") - writeFile(folder / "CHANGELOG." & ext, "# 0.0.1\n\n- First initial version of " & pluginName & " created at " & $now()) - - writeFile(folder / "plugin.json", pluginJson.format( - capitalizeAscii(pluginName), pluginName, authorName, authorMail, authorWeb, NimblePkgVersion.substr(0, 2))) - quit("New Plugin at /plugins/\n\nNimWC created a new Plugin skeleton, happy hacking, bye.", 0) +block: # block so "dir" is not defined after it. + static: # static because it must run at compile time. + let dir = parentDir(currentSourcePath()) + if not fileExists(replace(dir, "/nimwcpkg", "") & "/config/config.cfg"): + echo config_not_found_msg + discard staticExec("cp " & dir & "/config/config_default.cfg " & dir & "/config/config.cfg") proc handler() {.noconv.} = @@ -142,44 +55,45 @@ proc launcherActivated(cfg: Config) = dnsz = [cfg.getSectionValue("firejail", "dns0").strip, cfg.getSectionValue("firejail", "dns1").strip, cfg.getSectionValue("firejail", "dns2").strip, cfg.getSectionValue("firejail", "dns3").strip] assert countProcessors() > cpuCores, "Dedicated CPU Cores must be less or equal to the actual CPU Cores: " & $cpuCores - assert hostz.existsFile, "Hosts file not found: " & hostz - let myjail = Firejail( - noDvd: cfg.getSectionValue("firejail", "noDvd").parseBool, - noSound: cfg.getSectionValue("firejail", "noSound").parseBool, - noAutoPulse: cfg.getSectionValue("firejail", "noAutoPulse").parseBool, - no3d: cfg.getSectionValue("firejail", "no3d").parseBool, - noX: cfg.getSectionValue("firejail", "noX").parseBool, - noVideo: cfg.getSectionValue("firejail", "noVideo").parseBool, - noDbus: cfg.getSectionValue("firejail", "noDbus").parseBool, - noShell: cfg.getSectionValue("firejail", "noShell").parseBool, - noDebuggers: cfg.getSectionValue("firejail", "noDebuggers").parseBool, - noMachineId: cfg.getSectionValue("firejail", "noMachineId").parseBool, - noRoot: cfg.getSectionValue("firejail", "noRoot").parseBool, - noAllusers: cfg.getSectionValue("firejail", "noAllusers").parseBool, - noU2f: cfg.getSectionValue("firejail", "noU2f").parseBool, - privateTmp: cfg.getSectionValue("firejail", "privateTmp").parseBool, - privateCache: cfg.getSectionValue("firejail", "privateCache").parseBool, - privateDev: cfg.getSectionValue("firejail", "privateDev").parseBool, - forceEnUsUtf8: cfg.getSectionValue("firejail", "forceEnUsUtf8").parseBool, - caps: cfg.getSectionValue("firejail", "caps").parseBool, - seccomp: cfg.getSectionValue("firejail", "seccomp").parseBool, - noTv: cfg.getSectionValue("firejail", "noTv").parseBool, - writables: cfg.getSectionValue("firejail", "writables").parseBool, - noMnt: cfg.getSectionValue("firejail", "noMnt").parseBool, - ) - nimwcCommand = myjail.makeCommand( - command=appPath & userArgsRun, - name = cfg.getSectionValue("Server", "appname"), # whitelist= @[getAppDir(), getCurrentDir()], + assert hostz.fileExists, "Hosts file not found: " & hostz + + nimwcCommand: string = jail.makeCommand( + + command = appPath & userArgsRun, + name = cfg.getSectionValue("Server", "appname"), # whitelist= @[getAppDir(), getCurrentDir()], maxSubProcesses = cfg.getSectionValue("firejail", "maxSubProcesses").parseInt * 1_000_000, # 1 is Ok, 0 is Disabled, int.high max. - maxOpenFiles = cfg.getSectionValue("firejail", "maxOpenFiles").parseInt * 1_000, # Below 1000 NimWC may not start. - maxFileSize = cfg.getSectionValue("firejail", "maxFileSize").parseInt * 1_000_000_000, # Below 1Mb NimWC may not start. + maxOpenFiles = cfg.getSectionValue("firejail", "maxOpenFiles").parseInt * 1_000, # Below 1000 NimWC may not start. + maxFileSize = cfg.getSectionValue("firejail", "maxFileSize").parseInt * 1_000_000_000, # Below 1Mb NimWC may not start. maxPendingSignals = cfg.getSectionValue("firejail", "maxPendingSignals").parseInt * 10, # 1 is Ok, 0 is Disabled, int.high max. - timeout = cfg.getSectionValue("firejail", "timeout").parseInt, # 1 is Ok, 0 is Disabled, 255 max. It will actually Restart instead of Stopping. - maxRam = cfg.getSectionValue("firejail", "maxRam").parseInt * 1_000_000_000, # Below 1Gb NimWC may fail. - maxCpu = cfg.getSectionValue("firejail", "maxCpu").parseInt, # 1 is Ok, 0 is Disabled, 255 max. + timeout = cfg.getSectionValue("firejail", "timeout").parseInt, # 1 is Ok, 0 is Disabled, 255 max. It will actually Restart instead of Stopping. + maxRam = cfg.getSectionValue("firejail", "maxRam").parseInt * 1_000_000_000, # Below 1Gb NimWC may fail. + maxCpu = cfg.getSectionValue("firejail", "maxCpu").parseInt, # 1 is Ok, 0 is Disabled, 255 max. cpuCoresByNumber = corez, # 0 is Disabled, else toSeq(0..corez) - hostsFile = hostz, # Optional Alternative/Fake /etc/hosts - dnsServers = dnsz, # Optional Alternative/Fake DNS, 4 Servers must be provided + hostsFile = hostz, # Optional Alternative/Fake /etc/hosts + dnsServers = dnsz, # Optional Alternative/Fake DNS, 4 Servers must be provided + + noDvd = cfg.getSectionValue("firejail", "noDvd").parseBool, + noSound = cfg.getSectionValue("firejail", "noSound").parseBool, + noAutoPulse = cfg.getSectionValue("firejail", "noAutoPulse").parseBool, + no3d = cfg.getSectionValue("firejail", "no3d").parseBool, + noX = cfg.getSectionValue("firejail", "noX").parseBool, + noVideo = cfg.getSectionValue("firejail", "noVideo").parseBool, + noDbus = cfg.getSectionValue("firejail", "noDbus").parseBool, + noShell = cfg.getSectionValue("firejail", "noShell").parseBool, + noDebuggers = cfg.getSectionValue("firejail", "noDebuggers").parseBool, + noMachineId = cfg.getSectionValue("firejail", "noMachineId").parseBool, + noRoot = cfg.getSectionValue("firejail", "noRoot").parseBool, + noAllusers = cfg.getSectionValue("firejail", "noAllusers").parseBool, + noU2f = cfg.getSectionValue("firejail", "noU2f").parseBool, + privateTmp = cfg.getSectionValue("firejail", "privateTmp").parseBool, + privateCache = cfg.getSectionValue("firejail", "privateCache").parseBool, + privateDev = cfg.getSectionValue("firejail", "privateDev").parseBool, + forceEnUsUtf8 = cfg.getSectionValue("firejail", "forceEnUsUtf8").parseBool, + caps = cfg.getSectionValue("firejail", "caps").parseBool, + seccomp = cfg.getSectionValue("firejail", "seccomp").parseBool, + noTv = cfg.getSectionValue("firejail", "noTv").parseBool, + writables = cfg.getSectionValue("firejail", "writables").parseBool, + noMnt = cfg.getSectionValue("firejail", "noMnt").parseBool, ) const processOpts = @@ -217,14 +131,14 @@ proc startupCheck(cfg: Config) = ## Checking if the main-program file exists. If not it will ## be compiled with args and compiler options (compiler ## options should be specified in the *.nim.pkg) - preconditions compileOptions.len > 0, storageEFS.len > 0, existsFile(getAppDir() & "/nimwcpkg/nimwc_main.nim") + assert compileOptions.len > 0 and storageEFS.len > 0 and fileExists(getAppDir() & "/nimwcpkg/nimwc_main.nim") # Storage location. Folders are created in the module files.nim let args = replace(commandLineParams().join(" "), "-", "") userArgs = if args == "": "" else: " " & args appPath = getAppDir() / "nimwcpkg" / cfg.getSectionValue("Server", "appname") when not defined(ignoreefs): - if not existsDir(storageEFS): # Check access to EFS file system. + if not dirExists(storageEFS): # Check access to EFS file system. quit("No access to storage in release mode. Critical.") # Ensure that the tables are present in the DB @@ -234,14 +148,12 @@ proc startupCheck(cfg: Config) = if not fileExists(appPath) or defined(rc): # Ensure that the DB tables are created styledEcho(fgGreen, bgBlack, compile_start_msg & userArgs) - let (output, exitCode) = execCmdEx("nim c --out:" & appPath & " " & compileOptions & " " & getAppDir() & "/nimwcpkg/nimwc_main.nim") + let (output, exitCode) = execCmdEx("nim c -d:strip -d:lto --out:" & appPath & " " & compileOptions & " " & getAppDir() & "/nimwcpkg/nimwc_main.nim") if exitCode != 0: styledEcho(fgRed, bgBlack, compile_fail_msg & output) quit(exitCode) else: styledEcho(fgGreen, bgBlack, compile_ok_msg) - when defined(release): - if findExe"strip".len > 0: discard execCmd(cmdStrip & appPath) # @@ -259,6 +171,7 @@ when isMainModule: case keys of "version": quit(NimblePkgVersion, 0) of "version-hash": quit(commitHash, 0) + of "debug": quit(getSystemInfo().pretty, 0) of "help", "fullhelp": styledEcho(fgGreen, bgBlack, doc) of "initplugin": pluginSkeleton() # Interactive (Asks to user). of "gitupdate": updateNimwc() @@ -266,8 +179,7 @@ when isMainModule: of "newdb": generateDB(db) of "newadmin": createAdminUser(db) of "insertdata": - if "bootstrap" in commandLineParams(): createStandardData(db, cssBootstrap, on) - elif "water" in commandLineParams(): createStandardData(db, cssWater, on) + if "water" in commandLineParams(): createStandardData(db, cssWater, on) elif "official" in commandLineParams(): createStandardData(db, cssOfficial, on) else: createStandardData(db, cssBulma, on) of "vacuumdb": echo vacuumDb(db) diff --git a/nimwc.nim.cfg b/nimwc.nim.cfg index e4044b026..0b779c2de 100644 --- a/nimwc.nim.cfg +++ b/nimwc.nim.cfg @@ -5,6 +5,5 @@ #-d:packedjson #-d:demo # activate the demo mode #-d:adminnotify # notify admin email on errors -#-d:contracts # checks pre- and post-conditions when compiled with -d:release -#-d:hardened # hardens security, requires -d:contracts. Performance cost at ~20%. +#-d:hardened # hardens security. Performance cost at ~20%. #-d:postgres # use Postgres database instead of SQLite diff --git a/nimwc.nimble b/nimwc.nimble index 102ef7283..d07dc6988 100644 --- a/nimwc.nimble +++ b/nimwc.nimble @@ -1,5 +1,5 @@ # Package -version = "6.0.5" +version = "6.0.6" author = "Thomas T. Jarløv (https://github.com/ThomasTJdev) & Juan Carlos (https://github.com/juancarlospaco)" description = "Generate and host a website. Run the package and access your new webpage." license = "PPL" @@ -10,17 +10,12 @@ installDirs = @["config", "nimwcpkg", "plugins", "public"] # Dependencies -requires "nim >= 1.0.0" +requires "nim >= 1.2.4" requires "jester >= 0.4.3" -requires "bcrypt >= 0.2.1" -requires "contra >= 0.2.5" -requires "datetime2human >= 0.2.5" -requires "firejail >= 0.5.0" -requires "libravatar >= 0.5.0" requires "otp >= 0.1.1" requires "recaptcha >= 1.0.3" -requires "webp >= 0.2.5" requires "packedjson >= 0.1.0" +requires "gatabase >= 0.9.9" import distros diff --git a/nimwcpkg/constants/_commands.nim b/nimwcpkg/constants/_commands.nim index 292171596..5ec6a88ce 100644 --- a/nimwcpkg/constants/_commands.nim +++ b/nimwcpkg/constants/_commands.nim @@ -4,8 +4,6 @@ const - cmdStrip* = "strip --strip-all --remove-section=.note.gnu.gold-version --remove-section=.comment --remove-section=.note --remove-section=.note.gnu.build-id --remove-section=.note.ABI-tag" # Defined statically on nimwc.nim.cfg, - cmdSign* = "gpg --armor --detach-sign --yes --digest-algo sha512 " cmdChecksum* = "sha512sum --tag " diff --git a/nimwcpkg/constants/_htmls.nim b/nimwcpkg/constants/_htmls.nim index f2d39c1a3..b279db475 100644 --- a/nimwcpkg/constants/_htmls.nim +++ b/nimwcpkg/constants/_htmls.nim @@ -70,23 +70,6 @@ const <script src="/js/js_custom.js" crossorigin="anonymous" defer></script>""" - headBootstrap* = """<meta charset="utf-8" name="viewport" content="width=device-width, initial-scale=1.0" /> -<link rel="author" href="humans.txt"> -<link rel="shortcut icon" href="/images/logo/favicon.ico"> -<link rel="icon" type="image/png" href="/images/logo/favicon-16x16.png" sizes="16x16"> -<link rel="icon" type="image/png" href="/images/logo/favicon-32x32.png" sizes="32x32"> -<link rel="icon" type="image/png" href="/images/logo/favicon-192x192.png" sizes="192x192"> -<link rel="apple-touch-icon" sizes="180x180" href="/images/logo/favicon-180x180.png"> -<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha256-L/W5Wfqfa0sdBNIKN9cG6QA5F2qx4qICmU2VgLruv9Y=" crossorigin="anonymous" /> -<link rel="stylesheet" href="/css/style.css"> -<link rel="stylesheet" href="/css/style_custom.css"> -<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js" integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo=" crossorigin="anonymous" defer></script> -<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.min.js" integrity="sha256-x3YZWtRjM8bJqf48dFAv/qmgL68SI4jqNWeSLMZaMGA=" crossorigin="anonymous" defer></script> -<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha256-WqU1JavFxSAMcLP2WIOI+GB2zWmShMI82mTpLDcqFUg=" crossorigin="anonymous" defer></script> -<script src="/js/js.js" crossorigin="anonymous" defer></script> -<script src="/js/js_custom.js" crossorigin="anonymous" defer></script>""" - - navbar* = """<nav class="navbar is-transparent is-fixed-top" role="navigation" aria-label="main navigation"> <div class="navbar-brand"> <a class="navbar-item logo" href="/"> @@ -128,55 +111,6 @@ const </nav>""" - navbarBootstrap* = """<nav id="navbar" class="navbar navbar-expand-md navbar-light"> - <div id="navbarInside"> - <a class="navbar-brand" href="/"> - <img src="/images/logo/NimWC_logo_blue.png" /> - <div>Nim Website Creator</div> - </a> - <div class="navbar-toggler mainMenu"> - <div class="baricon bar1"></div> - <div class="baricon bar2"></div> - <div class="baricon bar3"></div> - </div> - <div class="menu" id="mainMenu"> - <ul class="navbar-nav"> - <li class="nav-item"> - <a class="nav-link" href="/">Frontpage</a> - </li> - <li class="nav-item"> - <a class="nav-link" href="/blog">Blog</a> - </li> - <li class="nav-item"> - <a class="nav-link" href="/p/about">About</a> - </li> - </ul> - </div> - <div class="menu hidden" id="mobileMenu"> - <div class="navbar-toggler"> - <div class="baricon bar1"></div> - <div class="baricon bar2"></div> - <div class="baricon bar3"></div> - </div> - <ul class="navbar-nav"> - <li class="nav-item"> - <a class="nav-link" href="/">Frontpage</a> - </li> - <li class="nav-item"> - <a class="nav-link" href="/blog">Blog</a> - </li> - <li class="nav-item"> - <a class="nav-link" href="/p/about">About</a> - </li> - <li class="nav-item"> - <a class="nav-link" href="/login">Login</a> - </li> - </ul> - </div> - </div> -</nav>""" - - navbarOfficial* = """<nav class="navbar is-transparent is-fixed-top is-hidden-mobile" role="navigation" aria-label="main navigation"> <div class="navbar-brand"> <a class="navbar-item logo" href="/"> @@ -1061,8 +995,66 @@ h1, h2, h3, h4 {color: black;} .installmethod h4 {margin-top: 15px; color: black; margin-bottom: 15px; font-size: 26px;} main {padding-top: 40px;}""" - officialJs* = """!function(){ -const $wel = document.querySelector("#welcome"); -if ($wel != null) { - const t=window,e=document.documentElement;if(e.classList.remove("no-js"),e.classList.add("js"),document.body.classList.contains("has-animations")){const t=window.sr=ScrollReveal();t.reveal(".feature",{duration:600,distance:"20px",easing:"cubic-bezier(0.5, -0.01, 0, 1.005)",origin:"right",interval:100}),t.reveal(".media-canvas",{duration:600,scale:".95",easing:"cubic-bezier(0.5, -0.01, 0, 1.005)",viewFactor:.5})}const n=document.querySelector(".device-mockup");function i(){n.classList.add("has-loaded")}n.complete?i():n.addEventListener("load",i);const s=document.querySelector(".features"),a=s.querySelector(".section-title"),o=document.querySelector(".feature-inner");function r(){let t=s.querySelector(".features-inner").getBoundingClientRect().left,e=o.getBoundingClientRect().left,n=parseInt(e-t);a.style.marginLeft=e>t?`${n}px`:0}r(),t.addEventListener("resize",r);const c=document.querySelectorAll(".is-moving-object");let l=0,d=0,u=0,g=0,f=0,m=e.clientWidth,p=e.clientHeight;c&&t.addEventListener("mousemove",function(t,e){let n=null,i=e;return(...e)=>{let s=Date.now();(!n||s-n>=i)&&(n=s,t.apply(this,e))}}(function(e){!function(e,n){l=e.pageX,d=e.pageY,u=t.scrollY,g=m/2-l,f=p/2-(d-u);for(let t=0;t<n.length;t++){const e=n[t].getAttribute("data-translating-factor")||20,i=n[t].getAttribute("data-rotating-factor")||20,s=n[t].getAttribute("data-perspective")||500;let a=[];n[t].classList.contains("is-translating")&&a.push("translate("+g/e+"px, "+f/e+"px)"),n[t].classList.contains("is-rotating")&&a.push("perspective("+s+"px) rotateY("+-g/i+"deg) rotateX("+f/i+"deg)"),(n[t].classList.contains("is-translating")||n[t].classList.contains("is-rotating"))&&(a=a.join(" "),n[t].style.transform=a,n[t].style.transition="transform 1s ease-out",n[t].style.transformStyle="preserve-3d",n[t].style.backfaceVisibility="hidden")}}(e,c)},150)) -}}();""" \ No newline at end of file + officialJs* = """ + + ! function() { + const $wel = document.querySelector("#welcome"); + if ($wel != null) { + const t = window, e = document.documentElement; + if (e.classList.remove("no-js"), e.classList.add("js"), document.body.classList.contains("has-animations")) { + const t = window.sr = ScrollReveal(); + t.reveal(".feature", { + duration: 600, + distance: "20px", + easing: "cubic-bezier(0.5, -0.01, 0, 1.005)", + origin: "right", + interval: 100 + }), t.reveal(".media-canvas", { + duration: 600, + scale: ".95", + easing: "cubic-bezier(0.5, -0.01, 0, 1.005)", + viewFactor: .5 + }) + } + const n = document.querySelector(".device-mockup"); + + function i() { + n.classList.add("has-loaded") + } + n.complete ? i() : n.addEventListener("load", i); + const s = document.querySelector(".features"), + a = s.querySelector(".section-title"), + o = document.querySelector(".feature-inner"); + + function r() { + let t = s.querySelector(".features-inner").getBoundingClientRect().left, + e = o.getBoundingClientRect().left, + n = parseInt(e - t); + a.style.marginLeft = e > t ? `${n}px` : 0 + } + r(), t.addEventListener("resize", r); + const c = document.querySelectorAll(".is-moving-object"); + let l = 0, d = 0, u = 0, g = 0, f = 0, m = e.clientWidth, p = e.clientHeight; + c && t.addEventListener("mousemove", function(t, e) { + let n = null, + i = e; + return (...e) => { + let s = Date.now(); + (!n || s - n >= i) && (n = s, t.apply(this, e)) + } + }(function(e) { + ! function(e, n) { + l = e.pageX, d = e.pageY, u = t.scrollY, g = m / 2 - l, f = p / 2 - (d - u); + for (let t = 0; t < n.length; t++) { + const e = n[t].getAttribute("data-translating-factor") || 20, + i = n[t].getAttribute("data-rotating-factor") || 20, + s = n[t].getAttribute("data-perspective") || 500; + let a = []; + n[t].classList.contains("is-translating") && a.push("translate(" + g / e + "px, " + f / e + "px)"), n[t].classList.contains("is-rotating") && a.push("perspective(" + s + "px) rotateY(" + -g / i + "deg) rotateX(" + f / i + "deg)"), (n[t].classList.contains("is-translating") || n[t].classList.contains("is-rotating")) && (a = a.join(" "), n[t].style.transform = a, n[t].style.transition = "transform 1s ease-out", n[t].style.transformStyle = "preserve-3d", n[t].style.backfaceVisibility = "hidden") + } + }(e, c) + }, 150)) + } + }(); + + """.strip.unindent diff --git a/nimwcpkg/constants/_sqls2.nim b/nimwcpkg/constants/_sqls2.nim new file mode 100644 index 000000000..9fb29e628 --- /dev/null +++ b/nimwcpkg/constants/_sqls2.nim @@ -0,0 +1,54 @@ +# Database Queries for run-time use. +import db_common +import gatabase + + +const adminusers_createAdminUser0*: SqlQuery = sqls: + select "id" + `from` "person" + where "status = 'Admin'" + +# echo adminusers_createAdminUser0.string + + +const adminusers_createAdminUser1*: SqlQuery = sqls: + insertinto "person(name, email, password, salt, status)" + values 5 # '?' * 5 + +# echo adminusers_createAdminUser1.string + + +const testusers_createTestUser0*: SqlQuery = sqls: + select "id" + `from` "person" + where "email = 'test@test.com'" + +# echo testusers_createTestUser0.string + + +const testusers_createTestUser1*: SqlQuery = sqls: + insertinto "person(name, email, password, salt, status)" + values 5 # '?' * 5 + +# echo testusers_createTestUser1.string + + +const standarddatas_standardDataSettings0*: SqlQuery = sqls: + insertinto "settings(title, head, navbar, footer)" + values 4 # '?' * 4 + +# echo standarddatas_standardDataSettings0.string + + +const standarddatas_standardDataFrontpage0*: SqlQuery = sqls: + insertinto "pages(author_id, status, url, name, description, standardhead, standardnavbar, standardfooter, title, metadescription, metakeywords)" + values 11 + +# echo standarddatas_standardDataFrontpage0.string + + +const standarddatas_standardDataBlogpost0*: SqlQuery = sqls: + insertinto "blog(author_id, status, url, name, description, standardhead, standardnavbar, standardfooter, title, metadescription, metakeywords)" + values 11 + +# echo standarddatas_standardDataBlogpost0.string diff --git a/nimwcpkg/constants/_strings.nim b/nimwcpkg/constants/_strings.nim index cc2d079d3..df5258ee0 100644 --- a/nimwcpkg/constants/_strings.nim +++ b/nimwcpkg/constants/_strings.nim @@ -4,7 +4,7 @@ const - NimblePkgVersion* {.strdefine.} = "6.0.3" ## NimWC Version (SemVer) + NimblePkgVersion* {.strdefine.} = "6.0.6" ## NimWC Version (SemVer) commitHash* = staticExec"git rev-parse --short HEAD" & "" ## NimWC Version (Git Hash) @@ -18,7 +18,6 @@ const (when defined(postgres): " -d:postgres" else: "") & (when defined(webp): " -d:webp" else: "") & (when defined(firejail): " -d:firejail" else: "") & - (when defined(contracts): " -d:contracts" else: "") & (when defined(recaptcha): " -d:recaptcha" else: "") & (when defined(packedjson): " -d:packedjson" else: "") & @@ -79,7 +78,7 @@ const Compile Error ⚠️ Compile-time or Configuration or Plugin error occurred. - ➡️ Check the configuration file of NimWC and enabled plugins. Recompile with -d:contracts. + ➡️ Check the configuration file of NimWC and enabled plugins. ➡️ Remove new plugins, restore previous configuration. ➡️ ️️Check that you have the latest NimWC version and check the documentation. ➡️ Check your source code: nim check YourFile.nim; nimpretty YourFile.nim @@ -191,24 +190,22 @@ Options: --backupdb Full backup of database (Raw SQL plain text file). --backupdb-gpg Compressed signed full backup of database (GPG+SHA512+TarGz). --backuplogs Compressed full backup of all old unused rotated Logs (TarGz). - --insertdata bulma Insert https://bulma.io data to database (No JS, Default, overrides data) - --insertdata bootstrap Insert https://getbootstrap.com data to database (jQuery, overrides data) - --insertdata water Insert Water CSS data to database (No JS, HTML Classless, overrides data) - --insertdata official Insert official nimwc.org data to database (overrides data, CSS, JS) + --insertdata bulma Insert https://bulma.io data to database (No JS, Default, overrides data) + --insertdata water Insert Water CSS data to database (No JS, HTML Classless, overrides data) + --insertdata official Insert official nimwc.org data to database (overrides data, CSS, JS) --newdb Generates a database with standard tables (Wont override data). If no database exists, new db will be initialized automatically. Compile options (features when compiling source code): - -d:postgres Postgres replaces SQLite (SQLite is default) https://u.nu/b1c8 - -d:firejail Firejail is enabled. Runs secure and isolated. https://u.nu/teb6 - -d:webp WebP is enabled. Optimize images and photos. https://u.nu/0o5t - -d:recaptcha Recaptcha AntiSpamm enabled (wont work on Tor) https://u.nu/hbc7 - -d:packedjson PackedJSON replaces JSON. Performance optimization. https://u.nu/byf- + -d:postgres Postgres replaces SQLite (SQLite is default) + -d:firejail Firejail is enabled. Runs secure and isolated. + -d:webp WebP is enabled. Optimize images and photos. + -d:recaptcha Recaptcha AntiSpamm enabled (wont work on Tor) + -d:packedjson PackedJSON replaces JSON. Performance optimization. -d:hardened Security Hardened mode. Runs Hardened. ~20% Performance cost max. -d:adminnotify Send error logs (ERROR) to the specified Admin email. -d:dev Development (ignore reCaptcha, no emails, more Verbose). -d:devemailon Send error logs email when -d:dev is activated. - -d:contracts Force Design by Contract enabled. Runs assertive. -d:demo Public demo mode. Enable Test user. 2FA ignored. Pages Reset. Force database reset every 1 hour. Some options Disabled. diff --git a/nimwcpkg/constants/constants.nim b/nimwcpkg/constants/constants.nim index f137e98e7..d73859efa 100644 --- a/nimwcpkg/constants/constants.nim +++ b/nimwcpkg/constants/constants.nim @@ -1,2 +1,2 @@ ## Just include all constants here, so we keep them separated by topic -include "_integers", "_commands", "_strings", "_sqls", "_htmls", "_urlencodeds" # Order is NOT important here +include "_integers", "_commands", "_strings", "_sqls", "_sqls2", "_htmls", "_urlencodeds" # Order is NOT important here diff --git a/nimwcpkg/databases/_adminusers.nim b/nimwcpkg/databases/_adminusers.nim index 251a8d086..18982c2b9 100644 --- a/nimwcpkg/databases/_adminusers.nim +++ b/nimwcpkg/databases/_adminusers.nim @@ -1,24 +1,22 @@ ## Do NOT import this file directly, instead import ``database.nim`` -func ask4UserPass*(): tuple[iName, iEmail, iPwd: string] {.inline.} = +proc ask4UserPass*(): tuple[iName, iEmail, iPwd: string] {.inline.} = ## Ask the user for user, mail, password, and return them. - postconditions(result.iName.len > nameMinLen, result.iEmail.len > emailMinLen, result.iPwd.len > passwordMinLen, - result.iName.len < nameMaxLen, result.iEmail.len < emailMaxLen, result.iPwd.len < passwordMaxLen) - var iName, iEmail, iPwd, iPwd2: string - while not(iName.len > nameMinLen and iName.len < nameMaxLen): # Max len from DB SQL - iName = readLineFromStdin("\nType Username: ").strip - while not(iEmail.len > emailMinLen and iEmail.len < emailMaxLen): - iEmail = readLineFromStdin("\nType Email (Lowercase): ").strip.toLowerAscii - while not(iPwd.len > passwordMinLen and iPwd.len < passwordMaxLen and iPwd == iPwd2): - iPwd = readLineFromStdin("\nType Password: ").strip # Type it Twice. - iPwd2 = readLineFromStdin("\nConfirm Password (Repeat it again): ").strip - result = (iName: iName, iEmail: iEmail, iPwd: iPwd) + creates "", iName, iEmail, iPwd, iPwd2 + while not(iName[].len > nameMinLen and iName[].len < nameMaxLen): # Max len from DB SQL + iName[] = readLineFromStdin("\nType Username: ").strip + while not(iEmail[].len > emailMinLen and iEmail[].len < emailMaxLen): + iEmail[] = readLineFromStdin("\nType Email (Lowercase): ").strip.toLowerAscii + while not(iPwd[].len > passwordMinLen and iPwd[].len < passwordMaxLen and iPwd[] == iPwd2[]): + iPwd[] = readLineFromStdin("\nType Password: ").strip # Type it Twice. + iPwd2[] = readLineFromStdin("\nConfirm Password (Repeat it again): ").strip + result = (iName: iName[], iEmail: iEmail[], iPwd: iPwd[]) + deallocs iName, iEmail, iPwd, iPwd2 proc createAdminUser*(db: DbConn) {.discardable.} = ## Create new admin user. - const sqlAnyAdmin = sql"SELECT id FROM person WHERE status = 'Admin'" - let anyAdmin = getAllRows(db, sqlAnyAdmin) + let anyAdmin = getAllRows(db, adminusers_createAdminUser0) info(createAdminUserMsg.format(anyAdmin.len)) let (iName, iEmail, iPwd) = ask4UserPass() # Ask for User/Password/Mail @@ -26,10 +24,7 @@ proc createAdminUser*(db: DbConn) {.discardable.} = let salt = makeSalt() password = makePassword(iPwd, salt) - const sqlAddAdmin = sql""" - INSERT INTO person (name, email, password, salt, status) - VALUES (?, ?, ?, ?, ?)""" - discard insertID(db, sqlAddAdmin, iName, iEmail, password, salt, + discard insertID(db, adminusers_createAdminUser1, iName, iEmail, password, salt, if readLineFromStdin("\nis Admin? (y/N): ").normalize == "y": "Admin" else: "Moderator") info("1 new user added: " & iName) diff --git a/nimwcpkg/databases/_backupdbs.nim b/nimwcpkg/databases/_backupdbs.nim index b44386a96..79b7cd5f1 100644 --- a/nimwcpkg/databases/_backupdbs.nim +++ b/nimwcpkg/databases/_backupdbs.nim @@ -5,8 +5,8 @@ proc backupDb*(dbname: string, host = "localhost", port = Port(5432), username = getEnv("USER", "root"), dataOnly = false, inserts = false, checksum = true, sign = true, targz = true): tuple[output: TaintedString, exitCode: int] = ## Backup the whole Database to a plain-text Raw SQL Query human-readable file. - preconditions(dbname.len > 0, host.len > 0, username.len > 0, - when defined(postgres): findExe("pg_dump").len > 0 else: findExe("sqlite3").len > 0) + assert dbname.len > 0 and host.len > 0 and username.len > 0 + const optionsx = {poStdErrToStdOut, poUsePath, poEchoCmd} # poEchoCmd does echo cmd setCurrentDir(nimwcpkgDir) once: discard existsOrCreateDir(nimwcpkgDir / "backup") @@ -15,25 +15,19 @@ proc backupDb*(dbname: string, (if dataOnly: " --data-only " else: "") & (if inserts: " --inserts " else: "")) else: # TODO: SQLite .dump is Not working, Docs says it should. var cmd = cmdBackup.format(dbname, filename) - - when not defined(release): info("Database backup: " & cmd) - result = execCmdEx(cmd) + result = execCmdEx(cmd, options = optionsx) if checksum and result.exitCode == 0 and findExe("sha512sum").len > 0: - cmd = cmdChecksum & filename & " > " & filename & ".sha512" - when not defined(release): info("Database backup (sha512sum): " & cmd) - result = execCmdEx(cmd) + result = execCmdEx(cmdChecksum & filename & " > " & filename & ".sha512", options = optionsx) if sign and result.exitCode == 0 and findExe("gpg").len > 0: - cmd = cmdSign & filename - when not defined(release): info("Database backup (gpg): " & cmd) - result = execCmdEx(cmd) + result = execCmdEx(cmdSign & filename, options = optionsx) if targz and result.exitCode == 0 and findExe("tar").len > 0: - cmd = cmdTar & filename & ".tar.gz " & filename & " " & filename & ".sha512 " & filename & ".asc" - - when not defined(release): info("Database backup (tar): " & cmd) - result = execCmdEx(cmd) + result = execCmdEx( + cmdTar & filename & ".tar.gz " & filename & " " & filename & ".sha512 " & filename & ".asc", + options = optionsx + ) if result.exitCode == 0: removeFile(filename) diff --git a/nimwcpkg/databases/_standarddatas.nim b/nimwcpkg/databases/_standarddatas.nim index 75ca8fed4..b09888962 100644 --- a/nimwcpkg/databases/_standarddatas.nim +++ b/nimwcpkg/databases/_standarddatas.nim @@ -3,81 +3,113 @@ proc standardDataSettings*(db: DbConn, dataStyle = cssBulma) = ## Settings info("Standard data: Inserting settings-data.") - exec(db, sql"DELETE FROM settings") - const sqlDataSettings = sql"INSERT INTO settings (title, head, navbar, footer) VALUES (?, ?, ?, ?)" + + exec []: + delete "settings" + case dataStyle - of cssBootstrap: - discard insertID(db, sqlDataSettings, title, headBootstrap, navbarBootstrap, footer) of cssWater: - discard insertID(db, sqlDataSettings, title, headClean, navbarClean, footerClean) + discard insertID(db, standarddatas_standardDataSettings0, title, headClean, navbarClean, footerClean) of cssOfficial: - discard insertID(db, sqlDataSettings, title, headOfficial, navbarOfficial, footerOfficial) + discard insertID(db, standarddatas_standardDataSettings0, title, headOfficial, navbarOfficial, footerOfficial) writeFile("public/js/js_custom.js", officialJs) writeFile("public/css/style_custom.css", officialCss) else: - discard insertID(db, sqlDataSettings, title, head, navbar, footer) + discard insertID(db, standarddatas_standardDataSettings0, title, head, navbar, footer) proc standardDataFrontpage*(db: DbConn, dataStyle = cssBulma) = ## Frontpage info("Standard data: Inserting frontpage-data.") - const sqlFrontpageExist = sql"SELECT id FROM pages WHERE url = 'frontpage'" - let frontpageExists = getValue(db, sqlFrontpageExist) + + let frontpageExists: string = getValue []: + select "id" + `from` "pages" + where "url = 'frontpage'" + if frontpageExists != "": - exec(db, sql"DELETE FROM pages WHERE id = ?", frontpageExists) - const sqlFrontpageData = sql""" - INSERT INTO pages (author_id, status, url, name, description, standardhead, standardnavbar, standardfooter, title, metadescription, metakeywords) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""" + exec @[frontpageExists]: + delete "pages" + where "id = ?" + if dataStyle == cssWater: - discard insertID(db, sqlFrontpageData, "1", "2", "frontpage", "Frontpage", frontpageClean, "1", "1", "1", "", "", "") + discard insertID(db, standarddatas_standardDataFrontpage0, "1", "2", "frontpage", "Frontpage", frontpageClean, "1", "1", "1", "", "", "") elif dataStyle == cssOfficial: - discard insertID(db, sqlFrontpageData, "1", "2", "frontpage", "Frontpage", frontpageOfficial, "1", "1", "1", "", "", "") + discard insertID(db, standarddatas_standardDataFrontpage0, "1", "2", "frontpage", "Frontpage", frontpageOfficial, "1", "1", "1", "", "", "") else: - discard insertID(db, sqlFrontpageData, "1", "2", "frontpage", "Frontpage", frontpage, "1", "1", "1", "NimWC Nim Website Creator", "NimWC is an online webpage editor for users with little HTML knowledge, but it also offers experienced users a freedom to customize everything.", "website,blog,nim,nimwc") + discard insertID(db, standarddatas_standardDataFrontpage0, "1", "2", "frontpage", "Frontpage", frontpage, "1", "1", "1", "NimWC Nim Website Creator", "NimWC is an online webpage editor for users with little HTML knowledge, but it also offers experienced users a freedom to customize everything.", "website,blog,nim,nimwc") proc standardDataAbout*(db: DbConn) = ## About info("Standard data: Inserting about-data.") - const sqlAboutExists = sql"SELECT id FROM pages WHERE url = 'about'" - let aboutExists = getValue(db, sqlAboutExists) + + let aboutExists: string = getValue []: + select "id" + `from` "pages" + where "url = 'about'" + if aboutExists != "": - exec(db, sql"DELETE FROM pages WHERE id = ?", aboutExists) + exec @[aboutExists]: + delete "pages" + where "id = ?" - discard insertID(db, sql"INSERT INTO pages (author_id, status, url, name, description, standardhead, standardnavbar, standardfooter, title, metadescription, metakeywords) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", "1", "2", "about", "About", about, "1", "1", "1", "About Nim Website Creator", "NimWC is an online webpage editor for users with little HTML knowledge, but it also offers experienced users a freedom to customize everything.", "website,blog,nim,nimwc") + discard insertID(db, standarddatas_standardDataFrontpage0, + "1", "2", "about", "About", about, "1", "1", "1", "About Nim Website Creator", "NimWC is an online webpage editor for users with little HTML knowledge, but it also offers experienced users a freedom to customize everything.", "website,blog,nim,nimwc") proc standardDataBlogpost1*(db: DbConn) = ## Blog post info("Standard data: Inserting blog post-data (1).") - const sqlBlogExists = sql"SELECT id FROM blog WHERE url = 'standardpost'" - let blogExists = getValue(db, sqlBlogExists) + + let blogExists: string = getValue []: + select "id" + `from` "blog" + where "url = 'standardpost'" + if blogExists != "": - exec(db, sql"DELETE FROM blog WHERE id = ?", blogExists) + exec @[blogExists]: + delete "blog" + where "id = ?" - discard insertID(db, sql"INSERT INTO blog (author_id, status, url, name, description, standardhead, standardnavbar, standardfooter, title, metadescription, metakeywords) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", "1", "2", "standardpost", "Standard blogpost", blogpost1, "1", "1", "1", "NimWC Example blogpost", "This is an example blogpost using the default styling.", "website,blog,nim,nimwc") + discard insertID(db, standarddatas_standardDataBlogpost0, + "1", "2", "standardpost", "Standard blogpost", blogpost1, "1", "1", "1", "NimWC Example blogpost", "This is an example blogpost using the default styling.", "website,blog,nim,nimwc") proc standardDataBlogpost2*(db: DbConn) = ## Blog post info("Standard data: Inserting blog post-data (2).") - const sqlBlog2Exists = sql"SELECT id FROM blog WHERE url = 'standardpostv2'" - let blogExists = getValue(db, sqlBlog2Exists) + + let blogExists: string = getValue []: + select "id" + `from` "blog" + where "url = 'standardpostv2'" + if blogExists != "": - exec(db, sql"DELETE FROM blog WHERE id = ?", blogExists) + exec @[blogExists]: + delete "blog" + where "id = ?" - discard insertID(db, sql"INSERT INTO blog (author_id, status, url, name, description, standardhead, standardnavbar, standardfooter, title, metadescription, metakeywords) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", "1", "2", "standardpostv2", "Parallax post v2", blogpost2, "1", "1", "1", "NimWC Example blogpost parallax", "This is an example blogpost using parallax created with NimWC.", "website,blog,nim,nimwc,parallax") + discard insertID(db, standarddatas_standardDataBlogpost0, + "1", "2", "standardpostv2", "Parallax post v2", blogpost2, "1", "1", "1", "NimWC Example blogpost parallax", "This is an example blogpost using parallax created with NimWC.", "website,blog,nim,nimwc,parallax") proc standardDataBlogpost3*(db: DbConn) = ## Blog post info("Standard data: Inserting blog post-data (3).") - const sqlBlog3Exists = sql"SELECT id FROM blog WHERE url = 'standardpostv3'" - let blogExists = getValue(db, sqlBlog3Exists) + + let blogExists: string = getValue []: + select "id" + `from` "blog" + where "url = 'standardpostv3'" + if blogExists != "": - exec(db, sql"DELETE FROM blog WHERE id = ?", blogExists) + exec @[blogExists]: + delete "blog" + where "id = ?" - discard insertID(db, sql"INSERT INTO blog (author_id, status, url, name, description, standardhead, standardnavbar, standardfooter, title, metadescription, metakeywords) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", "1", "2", "standardpostv3", "Custom styling", blogpost3, "1", "1", "1", "NimWC Example blogpost custom", "This is an example blogpost using custom styling.", "website,blog,nim,nimwc") + discard insertID(db, standarddatas_standardDataBlogpost0, + "1", "2", "standardpostv3", "Custom styling", blogpost3, "1", "1", "1", "NimWC Example blogpost custom", "This is an example blogpost using custom styling.", "website,blog,nim,nimwc") proc createStandardData*(db: DbConn, dataStyle = cssBulma, confirm = false) {.discardable.} = diff --git a/nimwcpkg/databases/_testusers.nim b/nimwcpkg/databases/_testusers.nim index 6d70aaef3..816585198 100644 --- a/nimwcpkg/databases/_testusers.nim +++ b/nimwcpkg/databases/_testusers.nim @@ -2,17 +2,13 @@ proc createTestUser*(db: DbConn) {.discardable.} = ## Create new admin user. - const sqlAnyAdmin = sql"SELECT id FROM person WHERE email = 'test@test.com'" - let anyAdmin = getAllRows(db, sqlAnyAdmin) + let anyAdmin = getAllRows(db, testusers_createTestUser0) info(createTestUserMsg.format(anyAdmin.len)) if anyAdmin.len < 1: const salt = "0".repeat(128) # Weak Salt for Test user only. - const sqlAddTestUser = sql(""" - INSERT INTO person (name, email, password, salt, status) - VALUES ('Testuser', 'test@test.com', ?, '$1', 'Moderator')""".format(salt)) - - discard insertID(db, sqlAddTestUser, makePassword("test", salt)) + discard insertID(db, testusers_createTestUser1, + "Testuser", "test@test.com", makePassword("test", salt), salt, "Moderator") info("Test user added!.") else: diff --git a/nimwcpkg/databases/databases.nim b/nimwcpkg/databases/databases.nim index 5ae3f552e..7c53b4265 100644 --- a/nimwcpkg/databases/databases.nim +++ b/nimwcpkg/databases/databases.nim @@ -1,16 +1,14 @@ import os, parsecfg, tables, osproc, logging, times, nativesockets, strutils, rdstdin -import contra - -import ../constants/constants, ../utils/configs, ../passwords/passwords, ../enums/enums +import ../constants/constants, ../utils/configs, ../utils/utils, ../passwords/passwords, ../enums/enums export head, navbar, footer, title # HTML template fragments -when defined(postgres): import db_postgres -else: import db_sqlite +import gatabase +when not defined(postgres): import db_sqlite let nimwcpkgDir = getAppDir().replace("/nimwcpkg", "") -assert existsDir(nimwcpkgDir), "nimwcpkg directory not found" +assert dirExists(nimwcpkgDir), "nimwcpkg directory not found" template vacuumDb*(db: DbConn): bool = db.tryExec(sqlVacuum) diff --git a/nimwcpkg/emails/_messages.nim b/nimwcpkg/emails/_messages.nim index 1b6aaf638..e9603df42 100644 --- a/nimwcpkg/emails/_messages.nim +++ b/nimwcpkg/emails/_messages.nim @@ -1,7 +1,6 @@ ## Do NOT import this file directly, instead import ``email.nim`` -proc genEmailMessage*(msgContent: string): string {.inline.} = +template genEmailMessage*(msgContent: string): string = ## Generate email content - preconditions msgContent.len > 0 - postconditions result.len > msgContent.len + assert msgContent.len > 0 mailStyleHeader & msgContent & mailStyleFrom & mailStyleFooter diff --git a/nimwcpkg/emails/_registrations.nim b/nimwcpkg/emails/_registrations.nim index e755611a5..9357d47fa 100644 --- a/nimwcpkg/emails/_registrations.nim +++ b/nimwcpkg/emails/_registrations.nim @@ -2,15 +2,16 @@ proc sendEmailActivationManual*(email, userName, password, activateUrl, invitorName: string) {.async.} = ## Send the activation email, when admin added a new user. - preconditions email.len > 5, userName.len > 0, password.len > 3, activateUrl.len > 0, invitorName.len > 0 - let message = activationMsg.format( - userName, invitorName, email, password, - (website & activateUrl), title, website, supportEmail) - await sendMailNow(title & " - Email Confirmation", genEmailMessage(message), email) + assert email.len > 5 and userName.len > 0 and password.len > 3 and activateUrl.len > 0 and invitorName.len > 0 + await sendMailNow(title & " - Email Confirmation", genEmailMessage( + activationMsg.format( + userName, invitorName, email, password, (website & activateUrl), title, website, supportEmail) + ), email, + ) proc sendEmailRegistrationFollowup*(email, userName: string) {.async.} = ## Send a follow up mail, if user has not used their activation link. - preconditions email.len > 5, userName.len > 0 + assert email.len > 5 and userName.len > 0 await sendMailNow(title & "- Reminder: Email Confirmation", genEmailMessage(registrationMsg.format(userName, title, website, supportEmail)), email) diff --git a/nimwcpkg/emails/_sendemails.nim b/nimwcpkg/emails/_sendemails.nim index 240f058b1..22b0e2073 100644 --- a/nimwcpkg/emails/_sendemails.nim +++ b/nimwcpkg/emails/_sendemails.nim @@ -2,25 +2,21 @@ proc sendMailNow*(subject, message, recipient: string) {.async.} = ## Send the email through smtp - preconditions subject.len > 0, message.len > 0, recipient.len > 0 + assert subject.len > 0 and message.len > 0 and recipient.len > 0 setCurrentDir(appDir) when defined(dev) and not defined(devemailon): info("Dev is true, email is not send") return - - let - from_addr = smtpFrom - toList = @[recipient] var client = newAsyncSmtp(useSsl = true, debug = false) headers = otherHeaders - headers.add(("From", from_addr)) - let encoded = createMessage(subject, message, toList, @[], headers) + headers.add(("From", smtpFrom)) + let encoded = createMessage(subject, message, @[recipient], @[], headers) try: await client.connect(smtpAddress, Port(parseInt(smtpPort))) await client.auth(smtpUser, smtpPassword) - await client.sendMail(from_addr, toList, $encoded) + await client.sendMail(smtpFrom, @[recipient], $encoded) except: error("Error in sending mail: " & recipient) when defined(dev): info("Email sent") diff --git a/nimwcpkg/emails/_sendemails_admins.nim b/nimwcpkg/emails/_sendemails_admins.nim index ac44d0520..40f9e2763 100644 --- a/nimwcpkg/emails/_sendemails_admins.nim +++ b/nimwcpkg/emails/_sendemails_admins.nim @@ -2,7 +2,7 @@ proc sendAdminMailNow*(subject, message: string) {.async.} = ## Send email only to Admin. - preconditions subject.len > 0, message.len > 0 + assert subject.len > 0 and message.len > 0 setCurrentDir(appDir) when defined(dev) and not defined(devemailon): info("Dev is true, email is not sent") @@ -10,21 +10,16 @@ proc sendAdminMailNow*(subject, message: string) {.async.} = if adminEmail == "": info("No admin email specified") return - - let from_addr = adminEmail var headers = otherHeaders client = newAsyncSmtp(useSsl = true, debug = false) - headers.add(("From", from_addr)) - let - recipient = adminEmail - toList = @[recipient] - encoded = createMessage("Admin - " & subject, message, toList, @[], headers) + headers.add(("From", adminEmail)) + let encoded = createMessage("Admin - " & subject, message, @[adminEmail], @[], headers) try: await client.connect(smtpAddress, Port(parseInt(smtpPort))) await client.auth(smtpUser, smtpPassword) - await client.sendMail(from_addr, toList, $encoded) + await client.sendMail(adminEmail, @[adminEmail], $encoded) except: - error("Error in sending mail: " & recipient) + error("Error in sending mail: " & adminEmail) when defined(dev): info("Admin email sent") diff --git a/nimwcpkg/emails/_senderrors_admins.nim b/nimwcpkg/emails/_senderrors_admins.nim index ad2c9ec43..d0a32a93d 100644 --- a/nimwcpkg/emails/_senderrors_admins.nim +++ b/nimwcpkg/emails/_senderrors_admins.nim @@ -2,6 +2,6 @@ proc sendEmailAdminError*(msg: string) {.async.} = ## Send email - user removed - preconditions msg.len > 0 + assert msg.len > 0 await sendAdminMailNow("Admin: Error occurred", genEmailMessage( adminErrorMsg.format(msg, msg.countLines, now()))) diff --git a/nimwcpkg/emails/emails.nim b/nimwcpkg/emails/emails.nim index b198086c8..907205df5 100644 --- a/nimwcpkg/emails/emails.nim +++ b/nimwcpkg/emails/emails.nim @@ -1,6 +1,6 @@ -import asyncdispatch, contra, strutils, times, os, parsecfg, smtp, logging +import asyncdispatch, strutils, times, os, parsecfg, smtp, logging -import ../constants/constants +import ../constants/constants, ../utils/utils ## Base Header for SMTP. diff --git a/nimwcpkg/enums/enums.nim b/nimwcpkg/enums/enums.nim index c6e203bcc..44f45e58f 100644 --- a/nimwcpkg/enums/enums.nim +++ b/nimwcpkg/enums/enums.nim @@ -8,7 +8,6 @@ type CSSFramework* = enum ## CSS Frameworks enumeration. cssBulma = "bulma" - cssBootstrap = "bootstrap" cssWater = "water" cssOfficial = "official" @@ -20,4 +19,4 @@ type cfgProxy = "Proxy" cfgRecaptcha = "reCAPTCHA" cfgSmtp = "SMTP" - cfgFirejail = "firejail" \ No newline at end of file + cfgFirejail = "firejail" diff --git a/nimwcpkg/files/_walkers.nim b/nimwcpkg/files/_walkers.nim index 814858ca3..52435c9d6 100644 --- a/nimwcpkg/files/_walkers.nim +++ b/nimwcpkg/files/_walkers.nim @@ -1,20 +1,20 @@ proc filesListPrivate*(): seq[string] {.inline.} = ## Get all filenames for project files - preconditions existsDir(storageEFS / "files/private/") + assert dirExists(storageEFS / "files/private/") for file in walkFiles(storageEFS / "files/private/*.*"): result.add(file) proc filesListPublic*(): seq[string] {.inline.} = ## Get all filenames for project files - preconditions existsDir(storageEFS / "files/public/") + assert dirExists(storageEFS / "files/public/") for file in walkFiles(storageEFS / "files/public/*.*"): result.add(file) proc filesListPublicFolderFiles*(): seq[string] {.inline.} = ## Get all filenames for project files - preconditions existsDir("public/images/") + assert dirExists("public/images/") for file in walkFiles("public/images/*.*"): result.add(file) diff --git a/nimwcpkg/files/files.nim b/nimwcpkg/files/files.nim index ea85c8d24..856f4b1b7 100644 --- a/nimwcpkg/files/files.nim +++ b/nimwcpkg/files/files.nim @@ -1,4 +1,4 @@ -import parsecfg, os, strutils, logging, contra +import parsecfg, os, strutils, logging const section = when defined(dev): "storagedev" else: "storage" diff --git a/nimwcpkg/nimfs/_firejails.nimf b/nimwcpkg/nimfs/_firejails.nimf index ea5fc70e8..ea6dfcebf 100644 --- a/nimwcpkg/nimfs/_firejails.nimf +++ b/nimwcpkg/nimfs/_firejails.nimf @@ -464,25 +464,8 @@ </form> <hr> <h5 class="has-text-centered">Firejail Status</h5> - <div class="tags"> - # - # for capability in firejailFeatures.pairs: - <span - # - #if capability.val.getBool: - style="color:black" title="OK" class="tag is-rounded is-success is-capitalized" - # - # else: - # - style="color:yellow" title="Fail (Not Installed)" class="tag is-danger is-uppercase" - # end if - # - > $capability.key </span> - # end for - # - </div> <div class="notification is-info"> - You are protected by Firejail version $firejailVersion, <i>stay safe and use <a href="https://nim-lang.org/learn.html">Nim</a></i>. + You are protected by Firejail, <i>stay safe and use <a href="https://nim-lang.org/learn.html">Nim</a></i>. </div> </div> #end proc diff --git a/nimwcpkg/nimfs/_logins.nimf b/nimwcpkg/nimfs/_logins.nimf index c8538ce8c..b84675937 100644 --- a/nimwcpkg/nimfs/_logins.nimf +++ b/nimwcpkg/nimfs/_logins.nimf @@ -85,7 +85,7 @@ ## DONT TOUCH, HoneyPot: https://github.com/ThomasTJdev/nim_websitecreator/issues/43#issue-403507393 <input type="password" class="input is-rounded" name="password2" autocomplete="false" tabindex="-1" placeholder="Password" dir="auto" minlength="10" value="" style=" - ${ ["display:none !important", "visibility:hidden !important", "opacity:0 !important"].rand } + ${ ["display:none !important", "visibility:hidden !important", "opacity:0 !important"].sample } " > ## DONT TOUCH, HoneyPot: https://stackoverflow.com/questions/36227376/better-honeypot-implementation-form-anti-spam/36227377 </form> diff --git a/nimwcpkg/nimfs/_statuspages.nimf b/nimwcpkg/nimfs/_statuspages.nimf index cbee4ed70..204369fd5 100644 --- a/nimwcpkg/nimfs/_statuspages.nimf +++ b/nimwcpkg/nimfs/_statuspages.nimf @@ -2,8 +2,7 @@ # # #proc genServerInfo(): string = - #const jailv = when defined(firejail): $firejailVersion else: "<i> Firejail is Disabled </i>" - #let jailf = when defined(firejail): $firejailFeatures.pretty.strip else: "<i> Firejail is Disabled </i>" + #const jailv = when defined(firejail): "<i> Firejail is Enabled </i>" else: "<i> Firejail is Disabled </i>" <div id="serverinfo"> <h1 class="has-text-centered">Server Info</h1> <table border=1 class="table is-bordered is-striped is-hoverable is-fullwidth"> @@ -53,9 +52,6 @@ <tr> <td> <b>Firejail Version</b> </td> <td> $jailv </td> </tr> - <tr> - <td> <b>Firejail Status</b> </td> <td> $jailf </td> - </tr> <tr> <td> <b>CPU</b> </td> <td> $hostCPU.toUpperAscii </td> </tr> diff --git a/nimwcpkg/nimwc_main.nim b/nimwcpkg/nimwc_main.nim index 620079872..4e15464dd 100644 --- a/nimwcpkg/nimwc_main.nim +++ b/nimwcpkg/nimwc_main.nim @@ -1,28 +1,19 @@ import asyncdispatch, base32, cgi, encodings, logging, md5, nativesockets, os, - osproc, oswalkdir, parsecfg, random, re, rdstdin, sequtils, streams, strtabs, + osproc, parsecfg, random, re, rdstdin, sequtils, streams, strtabs, strutils, tables, times, macros, mimetypes, packages/docutils/rstgen -import - bcrypt, - contra, - datetime2human, - jester, - libravatar, - otp +import jester, otp import - constants/constants, enums/enums, databases/databases, emails/emails, files/files, + constants/constants, enums/enums, databases/databases, emails/emails, files/files, utils/utils, passwords/passwords, sessions/sessions, utils/loggers, plugins/plugins, webs/html_utils when defined(postgres): import db_postgres else: import db_sqlite -when not defined(webp): {. warning: "WebP is disabled, no image optimizations possible." .} -else: from webp import cwebp - -when not defined(firejail): {. warning: "Firejail is disabled, running unsecure." .} -else: from firejail import firejailVersion, firejailFeatures +when not defined(webp): {.warning: "WebP is disabled, no image optimizations possible.".} +when not defined(firejail): {.warning: "Firejail is disabled, running unsecure.".} when defined(packedjson): import packedjson else: import json @@ -40,78 +31,46 @@ proc getPluginsPath*(): seq[string] {.compileTime.} = ## Get all plugins path ## ## Generates a seq[string] with the path to the plugins - postconditions result.allIt(it.len > 0) - let - dir = parentDir(currentSourcePath()) - realPath = replace(dir, "/nimwcpkg", "") - - var - plugins = (staticRead(realPath & "/plugins/plugin_import.txt").split("\n")) - extensions: seq[string] - + const + realPath = replace(parentDir(currentSourcePath()), "/nimwcpkg", "") + plugins = staticRead(realPath & "/plugins/plugin_import.txt").splitLines # Loop through all files and folders - for plugin in oswalkdir.walkDir("plugins/"): - let (pd, ppath) = plugin - discard pd - + for plugin in walkDir("plugins/"): + let (_, ppath) = plugin # If the path matches a name in the plugin_import.txt if replace(ppath, "plugins/", "") in plugins: - if extensions.len() == 0: - extensions = @[realPath & "/" & ppath] - else: - extensions.add(realPath & "/" & ppath) - - return extensions + result.add realPath & "/" & ppath const pluginsPath = getPluginsPath() -macro extensionImport(): untyped = +macro extensionImport() = ## Macro to import plugins ## ## Generate code for importing modules from extensions. ## The extensions main module needs to be in plugins/plugin_import.txt ## to be activated. Only 1 module will be imported. - # preconditions pluginsPath.allIt(it.len > 0) - var extensions = "" - for ppath in pluginsPath: - let splitted = split(ppath, "/") - extensions.add("import " & ppath & "/" & splitted[splitted.len-1] & "\n") - - when defined(dev): - echo "Plugins - imports:\n" & $extensions - - result = parseStmt(extensions) + when pluginsPath.len > 0: + result = newStmtList() + for it in pluginsPath: + let splitted = split(it, "/") + let module = it / splitted[splitted.len - 1] + result.add quote do: + import `module` + else: discard # Error: implementation of 'extensionImport' expected. extensionImport() -macro extensionUpdateDatabase(): untyped = - ## Macro to generate proc for plugins init proc - ## +macro extensionUpdateDatabase(procs: static[seq[string]]) = + ## Macro to generate proc for plugins init proc. ## Generate proc for updating the database with new tables etc. ## The extensions main module shall contain a proc named 'proc <extensionname>Start(db: DbConn) =' - ## The proc will be executed when the program is executed. - # preconditions pluginsPath.allIt(it.len > 0) - var extensions = "" - - extensions.add("proc extensionUpdateDB*(db: DbConn) =\n") - if pluginsPath.len == 0: - extensions.add(" discard # Plugin list is currently empty.") - - else: - for ppath in pluginsPath: - let splitted = split(ppath, "/") - extensions.add(" " & splitted[splitted.len-1] & "Start(db)\n") - - extensions.add(" echo \" \"") - - when defined(dev): - echo "Plugins - required proc:\n" & $extensions - - result = parseStmt(extensions) - -extensionUpdateDatabase() + result = newStmtList() + for it in procs: + let function = newIdentNode(it & "Start") # pluginStart + let database = newIdentNode("db") # db + result.add newCall(function, database) # pluginStart(db) proc extensionCss(): string {.compiletime.} = @@ -121,7 +80,6 @@ proc extensionCss(): string {.compiletime.} = ## renaming to <extensionname>.css ## ## 2) Insert <style>-link into HTML - # preconditions pluginsPath.allIt(it.len > 0) let dir = parentDir(currentSourcePath()) let mainDir = replace(dir, "/nimwcpkg", "") @@ -148,7 +106,6 @@ proc extensionJs*(): string {.compiletime.} = ## renaming to <extensionname>.js ## ## 2) Insert <js>-link into HTML - # preconditions pluginsPath.allIt(it.len > 0) let dir = parentDir(currentSourcePath()) let mainDir = replace(dir, "/nimwcpkg", "") @@ -221,13 +178,9 @@ func init(c: var TData) {.inline.} = proc recompile*(): int {.inline.} = ## Recompile nimwc_main - # preconditions compileOptions.len > 0 - postconditions result == 0 let appName = dict.getSectionValue("Server", "appname") let appPath = getAppDir() / appName - result = execCmd("nim c " & compileOptions & " -o:" & appPath & "_new_tmp " & getAppDir() & "/nimwc_main.nim") - when defined(release): - if result == 0 and findExe"strip".len > 0: discard execCmd(cmdStrip & appPath & "_new_tmp") + result = execCmd("nim c -d:strip -d:lto " & compileOptions & " -o:" & appPath & "_new_tmp " & getAppDir() & "/nimwc_main.nim") moveFile(getAppDir() & "/" & appName & "_new_tmp", getAppDir() & "/" & appName & "_new") @@ -274,7 +227,6 @@ proc checkLoggedIn(c: var TData) = proc login(c: var TData, email, pass, totpRaw: string): tuple[isLoginOk: bool, statusMessage: string] = ## User login - # preconditions email.len > 5, pass.len > 3, email.len < 255, pass.len < 301 when not defined(demo): if email == "test@test.com": return (false, "Email must not be test@test.com") @@ -387,13 +339,12 @@ when isMainModule: info("Demo Mode: Database reverted to default") # Update sql database from extensions - extensionUpdateDB(db) + extensionUpdateDatabase(pluginsPath) # Insert standard data if "insertdata" in commandLineParams() and readLineFromStdin( "Insert standard data?\nThis will override existing data! (y/n):").normalize == "y": - if "bootstrap" in commandLineParams(): createStandardData(db, cssBootstrap) - elif "water" in commandLineParams(): createStandardData(db, cssWater) + if "water" in commandLineParams(): createStandardData(db, cssWater) elif "official" in commandLineParams(): createStandardData(db, cssOfficial) else: createStandardData(db, cssBulma) diff --git a/nimwcpkg/passwords/_salts.nim b/nimwcpkg/passwords/_salts.nim deleted file mode 100644 index bbf59c4cd..000000000 --- a/nimwcpkg/passwords/_salts.nim +++ /dev/null @@ -1,14 +0,0 @@ -## Do NOT import this file directly, instead import ``password.nim`` - -proc makeSalt*(): string = - ## Generate random salt. Uses cryptographically secure /dev/urandom - ## on platforms where it is available, and Nim's random module in other cases. - if likely(useUrandom): - var randomBytes: array[0..127, char] - discard urandom.readBuffer(addr(randomBytes), 128) - for ch in randomBytes: - if ord(ch) in {32..126}: - result.add(ch) - else: # Fallback to Nim random when no /dev/urandom - for i in 0..127: - result.add(chr(rand(94) + 32)) # Generate numbers from 32 to 94 + 32 = 126 diff --git a/nimwcpkg/passwords/bcrypt/arc4random.c b/nimwcpkg/passwords/bcrypt/arc4random.c new file mode 100644 index 000000000..bd1854044 --- /dev/null +++ b/nimwcpkg/passwords/bcrypt/arc4random.c @@ -0,0 +1,236 @@ +/* + * Arc4 random number generator for OpenBSD. + * Copyright 1996 David Mazieres <dm@lcs.mit.edu>. + * + * Modification and redistribution in source and binary forms is + * permitted provided that due credit is given to the author and the + * OpenBSD project (for instance by leaving this copyright notice + * intact). + */ + +/* + * This code is derived from section 17.1 of Applied Cryptography, + * second edition, which describes a stream cipher allegedly + * compatible with RSA Labs "RC4" cipher (the actual description of + * which is a trade secret). The same algorithm is used as a stream + * cipher called "arcfour" in Tatu Ylonen's ssh package. + * + * Here the stream cipher has been modified always to include the time + * when initializing the state. That makes it impossible to + * regenerate the same random sequence twice, so this can't be used + * for encryption, but will generate good random numbers. + * + * RC4 is a registered trademark of RSA Laboratories. + */ + +#include <sys/cdefs.h> + +//#include "namespace.h" +#include <sys/types.h> +#include <sys/time.h> +#include <stdlib.h> +#include <stdio.h> +#include <pthread.h> +#include <fcntl.h> +#include <unistd.h> +#include <pthread.h> + +#define __isthreaded 1 + +#define _open open +#define _close close +#define _read read + +#define _pthread_mutex_lock pthread_mutex_lock +#define _pthread_mutex_unlock pthread_mutex_unlock + +struct arc4_stream { + u_int8_t i; + u_int8_t j; + u_int8_t s[256]; +}; + +static pthread_mutex_t arc4random_mtx = PTHREAD_MUTEX_INITIALIZER; + +#define RANDOMDEV "/dev/urandom" +#define THREAD_LOCK() \ + do { \ + if (__isthreaded) \ + _pthread_mutex_lock(&arc4random_mtx); \ + } while (0) + +#define THREAD_UNLOCK() \ + do { \ + if (__isthreaded) \ + _pthread_mutex_unlock(&arc4random_mtx); \ + } while (0) + +static struct arc4_stream rs; +static int rs_initialized; +static int rs_stired; + +static inline u_int8_t arc4_getbyte(struct arc4_stream *); +static void arc4_stir(struct arc4_stream *); + +static inline void +arc4_init(as) + struct arc4_stream *as; +{ + int n; + + for (n = 0; n < 256; n++) + as->s[n] = n; + as->i = 0; + as->j = 0; +} + +static inline void +arc4_addrandom(as, dat, datlen) + struct arc4_stream *as; + u_char *dat; + int datlen; +{ + int n; + u_int8_t si; + + as->i--; + for (n = 0; n < 256; n++) { + as->i = (as->i + 1); + si = as->s[as->i]; + as->j = (as->j + si + dat[n % datlen]); + as->s[as->i] = as->s[as->j]; + as->s[as->j] = si; + } +} + +static void +arc4_stir(as) + struct arc4_stream *as; +{ + int fd, n; + struct { + struct timeval tv; + pid_t pid; + u_int8_t rnd[128 - sizeof(struct timeval) - sizeof(pid_t)]; + } rdat; + + gettimeofday(&rdat.tv, NULL); + rdat.pid = getpid(); + fd = _open(RANDOMDEV, O_RDONLY, 0); + if (fd >= 0) { + (void) _read(fd, rdat.rnd, sizeof(rdat.rnd)); + _close(fd); + } + /* fd < 0? Ah, what the heck. We'll just take whatever was on the + * stack... */ + + arc4_addrandom(as, (void *) &rdat, sizeof(rdat)); + + /* + * Throw away the first N bytes of output, as suggested in the + * paper "Weaknesses in the Key Scheduling Algorithm of RC4" + * by Fluher, Mantin, and Shamir. N=1024 is based on + * suggestions in the paper "(Not So) Random Shuffles of RC4" + * by Ilya Mironov. + */ + for (n = 0; n < 1024; n++) + arc4_getbyte(as); +} + +static inline u_int8_t +arc4_getbyte(as) + struct arc4_stream *as; +{ + u_int8_t si, sj; + + as->i = (as->i + 1); + si = as->s[as->i]; + as->j = (as->j + si); + sj = as->s[as->j]; + as->s[as->i] = sj; + as->s[as->j] = si; + + return (as->s[(si + sj) & 0xff]); +} + +static inline u_int32_t +arc4_getword(as) + struct arc4_stream *as; +{ + u_int32_t val; + + val = arc4_getbyte(as) << 24; + val |= arc4_getbyte(as) << 16; + val |= arc4_getbyte(as) << 8; + val |= arc4_getbyte(as); + + return (val); +} + +static void +arc4_check_init(void) +{ + if (!rs_initialized) { + arc4_init(&rs); + rs_initialized = 1; + } +} + +static void +arc4_check_stir(void) +{ + if (!rs_stired) { + arc4_stir(&rs); + rs_stired = 1; + } +} + +void +arc4random_stir() +{ + THREAD_LOCK(); + arc4_check_init(); + arc4_stir(&rs); + THREAD_UNLOCK(); +} + +void +arc4random_addrandom(dat, datlen) + u_char *dat; + int datlen; +{ + THREAD_LOCK(); + arc4_check_init(); + arc4_check_stir(); + arc4_addrandom(&rs, dat, datlen); + THREAD_UNLOCK(); +} + +u_int32_t +arc4random() +{ + u_int32_t rnd; + + THREAD_LOCK(); + arc4_check_init(); + arc4_check_stir(); + rnd = arc4_getword(&rs); + THREAD_UNLOCK(); + + return (rnd); +} + +#if 0 +/*-------- Test code for i386 --------*/ +#include <stdio.h> +int +main(int argc, char **argv) +{ + const int iter = 1000000; + int i; + + arc4random(); + + printf("ok\n"); +} +#endif diff --git a/nimwcpkg/passwords/bcrypt/blowfish.c b/nimwcpkg/passwords/bcrypt/blowfish.c new file mode 100644 index 000000000..e4a4d5829 --- /dev/null +++ b/nimwcpkg/passwords/bcrypt/blowfish.c @@ -0,0 +1,686 @@ +/* + * Blowfish block cipher + * Copyright 1997 Niels Provos <provos@physnet.uni-hamburg.de> + * All rights reserved. + * + * Implementation advice by David Mazieres <dm@lcs.mit.edu>. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Niels Provos. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> + +/* + * This code is derived from section 14.3 and the given source + * in section V of Applied Cryptography, second edition. + * Blowfish is an unpatented fast block cipher designed by + * Bruce Schneier. + */ + +/* + * FreeBSD implementation by Paul Herman <pherman@frenchfries.net> + */ + +#if 0 +#include <stdio.h> /* used for debugging */ +#include <string.h> +#endif + +#include <sys/types.h> +#include "blowfish.h" + +/* Function for Feistel Networks */ + +#define _F(s, x) ((((s)[ (((x)>>24)&0xFF)] \ + + (s)[0x100 + (((x)>>16)&0xFF)]) \ + ^ (s)[0x200 + (((x)>> 8)&0xFF)]) \ + + (s)[0x300 + ( (x) &0xFF)]) + +#define BLFRND(s, p, i, j, n) (i ^= _F(s, j) ^ (p)[n]) + +void +Blowfish_encipher(blf_ctx *c, u_int32_t *xl, u_int32_t *xr) +{ + u_int32_t Xl; + u_int32_t Xr; + u_int32_t *s = c->S[0]; + u_int32_t *p = c->P; + + Xl = *xl; + Xr = *xr; + + Xl ^= p[0]; + BLFRND(s, p, Xr, Xl, 1); BLFRND(s, p, Xl, Xr, 2); + BLFRND(s, p, Xr, Xl, 3); BLFRND(s, p, Xl, Xr, 4); + BLFRND(s, p, Xr, Xl, 5); BLFRND(s, p, Xl, Xr, 6); + BLFRND(s, p, Xr, Xl, 7); BLFRND(s, p, Xl, Xr, 8); + BLFRND(s, p, Xr, Xl, 9); BLFRND(s, p, Xl, Xr, 10); + BLFRND(s, p, Xr, Xl, 11); BLFRND(s, p, Xl, Xr, 12); + BLFRND(s, p, Xr, Xl, 13); BLFRND(s, p, Xl, Xr, 14); + BLFRND(s, p, Xr, Xl, 15); BLFRND(s, p, Xl, Xr, 16); + + *xl = Xr ^ p[17]; + *xr = Xl; +} + +void +Blowfish_decipher(blf_ctx *c, u_int32_t *xl, u_int32_t *xr) +{ + u_int32_t Xl; + u_int32_t Xr; + u_int32_t *s = c->S[0]; + u_int32_t *p = c->P; + + Xl = *xl; + Xr = *xr; + + Xl ^= p[17]; + BLFRND(s, p, Xr, Xl, 16); BLFRND(s, p, Xl, Xr, 15); + BLFRND(s, p, Xr, Xl, 14); BLFRND(s, p, Xl, Xr, 13); + BLFRND(s, p, Xr, Xl, 12); BLFRND(s, p, Xl, Xr, 11); + BLFRND(s, p, Xr, Xl, 10); BLFRND(s, p, Xl, Xr, 9); + BLFRND(s, p, Xr, Xl, 8); BLFRND(s, p, Xl, Xr, 7); + BLFRND(s, p, Xr, Xl, 6); BLFRND(s, p, Xl, Xr, 5); + BLFRND(s, p, Xr, Xl, 4); BLFRND(s, p, Xl, Xr, 3); + BLFRND(s, p, Xr, Xl, 2); BLFRND(s, p, Xl, Xr, 1); + + *xl = Xr ^ p[0]; + *xr = Xl; +} + +void +Blowfish_initstate(blf_ctx *c) +{ + +/* P-box and S-box tables initialized with digits of Pi */ + + const blf_ctx initstate = + + { { + { + 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, + 0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99, + 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, + 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e, + 0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee, + 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013, + 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, + 0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e, + 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60, + 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, + 0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce, + 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a, + 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, + 0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677, + 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193, + 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, + 0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88, + 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239, + 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, + 0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0, + 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, + 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, + 0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88, + 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe, + 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, + 0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d, + 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b, + 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, + 0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba, + 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463, + 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, + 0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09, + 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3, + 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, + 0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279, + 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8, + 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, + 0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82, + 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db, + 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, + 0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0, + 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b, + 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, + 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8, + 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4, + 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, + 0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7, + 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c, + 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, + 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1, + 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299, + 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, + 0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477, + 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf, + 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, + 0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af, + 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa, + 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5, + 0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41, + 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915, + 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, + 0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915, + 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664, + 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a}, + { + 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, + 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266, + 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1, + 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, + 0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6, + 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1, + 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, + 0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1, + 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737, + 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8, + 0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff, + 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd, + 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, + 0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7, + 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41, + 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, + 0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf, + 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af, + 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, + 0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87, + 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c, + 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, + 0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16, + 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd, + 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, + 0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509, + 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, + 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, + 0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f, + 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a, + 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, + 0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960, + 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66, + 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28, + 0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802, + 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84, + 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, + 0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf, + 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14, + 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, + 0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50, + 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7, + 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, + 0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281, + 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99, + 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696, + 0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128, + 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73, + 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, + 0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0, + 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105, + 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, + 0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3, + 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285, + 0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, + 0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061, + 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, + 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, + 0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735, + 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc, + 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, + 0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340, + 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20, + 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7}, + { + 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, + 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068, + 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, + 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840, + 0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45, + 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504, + 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, + 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb, + 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee, + 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6, + 0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42, + 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b, + 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, + 0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb, + 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527, + 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b, + 0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33, + 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c, + 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, + 0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc, + 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17, + 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564, + 0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b, + 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115, + 0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, + 0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728, + 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0, + 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, + 0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37, + 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d, + 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, + 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b, + 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3, + 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb, + 0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d, + 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c, + 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, + 0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9, + 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a, + 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe, + 0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d, + 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc, + 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, + 0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61, + 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2, + 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9, + 0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2, + 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c, + 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, + 0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633, + 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10, + 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169, + 0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52, + 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027, + 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, + 0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62, + 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, + 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, + 0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24, + 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc, + 0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, + 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c, + 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837, + 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0}, + { + 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, + 0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe, + 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b, + 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, + 0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8, + 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6, + 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, + 0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22, + 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4, + 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6, + 0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9, + 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59, + 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, + 0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51, + 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, + 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, + 0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b, + 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28, + 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, + 0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd, + 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a, + 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, + 0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb, + 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f, + 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, + 0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32, + 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680, + 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166, + 0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae, + 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb, + 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, + 0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47, + 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370, + 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, + 0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84, + 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048, + 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, + 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd, + 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9, + 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, + 0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38, + 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f, + 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, + 0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525, + 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1, + 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442, + 0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964, + 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e, + 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, + 0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d, + 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f, + 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, + 0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02, + 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc, + 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, + 0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a, + 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6, + 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, + 0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0, + 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060, + 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, + 0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9, + 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f, + 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6} + }, + { + 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, + 0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89, + 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c, + 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, + 0x9216d5d9, 0x8979fb1b + } }; + + *c = initstate; + +} + +u_int32_t +Blowfish_stream2word(const u_int8_t *data, u_int16_t databytes, + u_int16_t *current) +{ + u_int8_t i; + u_int16_t j; + u_int32_t temp; + + temp = 0x00000000; + j = *current; + + for (i = 0; i < 4; i++, j++) { + if (j >= databytes) + j = 0; + temp = (temp << 8) | data[j]; + } + + *current = j; + return temp; +} + +void +Blowfish_expand0state(blf_ctx *c, const u_int8_t *key, u_int16_t keybytes) +{ + u_int16_t i; + u_int16_t j; + u_int16_t k; + u_int32_t temp; + u_int32_t datal; + u_int32_t datar; + + j = 0; + for (i = 0; i < BLF_N + 2; i++) { + /* Extract 4 int8 to 1 int32 from keystream */ + temp = Blowfish_stream2word(key, keybytes, &j); + c->P[i] = c->P[i] ^ temp; + } + + j = 0; + datal = 0x00000000; + datar = 0x00000000; + for (i = 0; i < BLF_N + 2; i += 2) { + Blowfish_encipher(c, &datal, &datar); + + c->P[i] = datal; + c->P[i + 1] = datar; + } + + for (i = 0; i < 4; i++) { + for (k = 0; k < 256; k += 2) { + Blowfish_encipher(c, &datal, &datar); + + c->S[i][k] = datal; + c->S[i][k + 1] = datar; + } + } +} + + +void +Blowfish_expandstate(blf_ctx *c, const u_int8_t *data, u_int16_t databytes, + const u_int8_t *key, u_int16_t keybytes) +{ + u_int16_t i; + u_int16_t j; + u_int16_t k; + u_int32_t temp; + u_int32_t datal; + u_int32_t datar; + + j = 0; + for (i = 0; i < BLF_N + 2; i++) { + /* Extract 4 int8 to 1 int32 from keystream */ + temp = Blowfish_stream2word(key, keybytes, &j); + c->P[i] = c->P[i] ^ temp; + } + + j = 0; + datal = 0x00000000; + datar = 0x00000000; + for (i = 0; i < BLF_N + 2; i += 2) { + datal ^= Blowfish_stream2word(data, databytes, &j); + datar ^= Blowfish_stream2word(data, databytes, &j); + Blowfish_encipher(c, &datal, &datar); + + c->P[i] = datal; + c->P[i + 1] = datar; + } + + for (i = 0; i < 4; i++) { + for (k = 0; k < 256; k += 2) { + datal ^= Blowfish_stream2word(data, databytes, &j); + datar ^= Blowfish_stream2word(data, databytes, &j); + Blowfish_encipher(c, &datal, &datar); + + c->S[i][k] = datal; + c->S[i][k + 1] = datar; + } + } + +} + +void +blf_key(blf_ctx *c, const u_int8_t *k, u_int16_t len) +{ + /* Initalize S-boxes and subkeys with Pi */ + Blowfish_initstate(c); + + /* Transform S-boxes and subkeys with key */ + Blowfish_expand0state(c, k, len); +} + +void +blf_enc(blf_ctx *c, u_int32_t *data, u_int16_t blocks) +{ + u_int32_t *d; + u_int16_t i; + + d = data; + for (i = 0; i < blocks; i++) { + Blowfish_encipher(c, d, d + 1); + d += 2; + } +} + +void +blf_dec(blf_ctx *c, u_int32_t *data, u_int16_t blocks) +{ + u_int32_t *d; + u_int16_t i; + + d = data; + for (i = 0; i < blocks; i++) { + Blowfish_decipher(c, d, d + 1); + d += 2; + } +} + +void +blf_ecb_encrypt(blf_ctx *c, u_int8_t *data, u_int32_t len) +{ + u_int32_t l, r; + u_int32_t i; + + for (i = 0; i < len; i += 8) { + l = data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3]; + r = data[4] << 24 | data[5] << 16 | data[6] << 8 | data[7]; + Blowfish_encipher(c, &l, &r); + data[0] = l >> 24 & 0xff; + data[1] = l >> 16 & 0xff; + data[2] = l >> 8 & 0xff; + data[3] = l & 0xff; + data[4] = r >> 24 & 0xff; + data[5] = r >> 16 & 0xff; + data[6] = r >> 8 & 0xff; + data[7] = r & 0xff; + data += 8; + } +} + +void +blf_ecb_decrypt(blf_ctx *c, u_int8_t *data, u_int32_t len) +{ + u_int32_t l, r; + u_int32_t i; + + for (i = 0; i < len; i += 8) { + l = data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3]; + r = data[4] << 24 | data[5] << 16 | data[6] << 8 | data[7]; + Blowfish_decipher(c, &l, &r); + data[0] = l >> 24 & 0xff; + data[1] = l >> 16 & 0xff; + data[2] = l >> 8 & 0xff; + data[3] = l & 0xff; + data[4] = r >> 24 & 0xff; + data[5] = r >> 16 & 0xff; + data[6] = r >> 8 & 0xff; + data[7] = r & 0xff; + data += 8; + } +} + +void +blf_cbc_encrypt(blf_ctx *c, u_int8_t *iv, u_int8_t *data, u_int32_t len) +{ + u_int32_t l, r; + u_int32_t i, j; + + for (i = 0; i < len; i += 8) { + for (j = 0; j < 8; j++) + data[j] ^= iv[j]; + l = data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3]; + r = data[4] << 24 | data[5] << 16 | data[6] << 8 | data[7]; + Blowfish_encipher(c, &l, &r); + data[0] = l >> 24 & 0xff; + data[1] = l >> 16 & 0xff; + data[2] = l >> 8 & 0xff; + data[3] = l & 0xff; + data[4] = r >> 24 & 0xff; + data[5] = r >> 16 & 0xff; + data[6] = r >> 8 & 0xff; + data[7] = r & 0xff; + iv = data; + data += 8; + } +} + +void +blf_cbc_decrypt(blf_ctx *c, u_int8_t *iva, u_int8_t *data, u_int32_t len) +{ + u_int32_t l, r; + u_int8_t *iv; + u_int32_t i, j; + + iv = data + len - 16; + data = data + len - 8; + for (i = len - 8; i >= 8; i -= 8) { + l = data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3]; + r = data[4] << 24 | data[5] << 16 | data[6] << 8 | data[7]; + Blowfish_decipher(c, &l, &r); + data[0] = l >> 24 & 0xff; + data[1] = l >> 16 & 0xff; + data[2] = l >> 8 & 0xff; + data[3] = l & 0xff; + data[4] = r >> 24 & 0xff; + data[5] = r >> 16 & 0xff; + data[6] = r >> 8 & 0xff; + data[7] = r & 0xff; + for (j = 0; j < 8; j++) + data[j] ^= iv[j]; + iv -= 8; + data -= 8; + } + l = data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3]; + r = data[4] << 24 | data[5] << 16 | data[6] << 8 | data[7]; + Blowfish_decipher(c, &l, &r); + data[0] = l >> 24 & 0xff; + data[1] = l >> 16 & 0xff; + data[2] = l >> 8 & 0xff; + data[3] = l & 0xff; + data[4] = r >> 24 & 0xff; + data[5] = r >> 16 & 0xff; + data[6] = r >> 8 & 0xff; + data[7] = r & 0xff; + for (j = 0; j < 8; j++) + data[j] ^= iva[j]; +} + +#if 0 +void +report(u_int32_t data[], u_int16_t len) +{ + u_int16_t i; + for (i = 0; i < len; i += 2) + printf("Block %0hd: %08lx %08lx.\n", + i / 2, data[i], data[i + 1]); +} +void +main(void) +{ + + blf_ctx c; + char key[] = "AAAAA"; + char key2[] = "abcdefghijklmnopqrstuvwxyz"; + + u_int32_t data[10]; + u_int32_t data2[] = + {0x424c4f57L, 0x46495348L}; + + u_int16_t i; + + /* First test */ + for (i = 0; i < 10; i++) + data[i] = i; + + blf_key(&c, (u_int8_t *) key, 5); + blf_enc(&c, data, 5); + blf_dec(&c, data, 1); + blf_dec(&c, data + 2, 4); + printf("Should read as 0 - 9.\n"); + report(data, 10); + + /* Second test */ + blf_key(&c, (u_int8_t *) key2, strlen(key2)); + blf_enc(&c, data2, 1); + printf("\nShould read as: 0x324ed0fe 0xf413a203.\n"); + report(data2, 2); + blf_dec(&c, data2, 1); + report(data2, 2); +} +#endif diff --git a/nimwcpkg/passwords/bcrypt/blowfish.h b/nimwcpkg/passwords/bcrypt/blowfish.h new file mode 100644 index 000000000..3c05145e3 --- /dev/null +++ b/nimwcpkg/passwords/bcrypt/blowfish.h @@ -0,0 +1,88 @@ +/* + * Blowfish - a fast block cipher designed by Bruce Schneier + * + * Copyright 1997 Niels Provos <provos@physnet.uni-hamburg.de> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Niels Provos. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/* + * FreeBSD implementation by Paul Herman <pherman@frenchfries.net> + */ + +#include <stdint.h> +#include <sys/types.h> + +#ifndef _BLF_H_ +#define _BLF_H_ + +/* Schneier states the maximum key length to be 56 bytes. + * The way how the subkeys are initalized by the key up + * to (N+2)*4 i.e. 72 bytes are utilized. + * Warning: For normal blowfish encryption only 56 bytes + * of the key affect all cipherbits. + */ + +#define BLF_N 16 /* Number of Subkeys */ +#define BLF_MAXKEYLEN ((BLF_N-2)*4) /* 448 bits */ + +/* Blowfish context */ +typedef struct BlowfishContext { + u_int32_t S[4][256]; /* S-Boxes */ + u_int32_t P[BLF_N + 2]; /* Subkeys */ +} blf_ctx; + +/* Raw access to customized Blowfish + * blf_key is just: + * Blowfish_initstate( state ) + * Blowfish_expand0state( state, key, keylen ) + */ + +void Blowfish_encipher(blf_ctx *, u_int32_t *, u_int32_t *); +void Blowfish_decipher(blf_ctx *, u_int32_t *, u_int32_t *); +void Blowfish_initstate(blf_ctx *); +void Blowfish_expand0state(blf_ctx *, const u_int8_t *, u_int16_t); +void Blowfish_expandstate + (blf_ctx *, const u_int8_t *, u_int16_t, const u_int8_t *, u_int16_t); + +/* Standard Blowfish */ + +void blf_key(blf_ctx *, const u_int8_t *, u_int16_t); +void blf_enc(blf_ctx *, u_int32_t *, u_int16_t); +void blf_dec(blf_ctx *, u_int32_t *, u_int16_t); + +void blf_ecb_encrypt(blf_ctx *, u_int8_t *, u_int32_t); +void blf_ecb_decrypt(blf_ctx *, u_int8_t *, u_int32_t); + +void blf_cbc_encrypt(blf_ctx *, u_int8_t *, u_int8_t *, u_int32_t); +void blf_cbc_decrypt(blf_ctx *, u_int8_t *, u_int8_t *, u_int32_t); + +/* Converts u_int8_t to u_int32_t */ +u_int32_t Blowfish_stream2word(const u_int8_t *, u_int16_t , u_int16_t *); + +#endif diff --git a/nimwcpkg/passwords/bcrypt/crypt-blowfish.c b/nimwcpkg/passwords/bcrypt/crypt-blowfish.c new file mode 100644 index 000000000..8f14b2088 --- /dev/null +++ b/nimwcpkg/passwords/bcrypt/crypt-blowfish.c @@ -0,0 +1,405 @@ +/* + * Copyright 1997 Niels Provos <provos@physnet.uni-hamburg.de> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Niels Provos. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#include <stdint.h> +#include <sys/types.h> + +/* This implementation is adaptable to current computing power. + * You can have up to 2^31 rounds which should be enough for some + * time to come. + */ + +#define _PASSWORD_LEN 128 + +#define BCRYPT_VERSION '2' +#define BCRYPT_MAXSALT 16 /* Precomputation is just so nice */ +#define BCRYPT_BLOCKS 6 /* Ciphertext blocks */ +#define BCRYPT_MINROUNDS 16 /* we have log2(rounds) in salt */ + +#define false 0 +#define true 1 +#define bool int + +/* This password hashing algorithm was designed by David Mazieres + * <dm@lcs.mit.edu> and works as follows: + * + * 1. state := InitState () + * 2. state := ExpandKey (state, salt, password) 3. + * REPEAT rounds: + * state := ExpandKey (state, 0, salt) + * state := ExpandKey(state, 0, password) + * 4. ctext := "OrpheanBeholderScryDoubt" + * 5. REPEAT 64: + * ctext := Encrypt_ECB (state, ctext); + * 6. RETURN Concatenate (salt, ctext); + * + */ + +/* + * FreeBSD implementation by Paul Herman <pherman@frenchfries.net> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <string.h> +#include <pwd.h> +#include "blowfish.h" +#ifdef __APPLE__ +#include <unistd.h> +#else +#include "crypt.h" +#endif + +/* This implementation is adaptable to current computing power. + * You can have up to 2^31 rounds which should be enough for some + * time to come. + */ + +#define BCRYPT_VERSION '2' +#define BCRYPT_MAXSALT 16 /* Precomputation is just so nice */ +#define BCRYPT_BLOCKS 6 /* Ciphertext blocks */ +#define BCRYPT_MINROUNDS 16 /* we have log2(rounds) in salt */ + +char *bcrypt_gensalt(u_int8_t); + +static void encode_salt(char *, u_int8_t *, u_int16_t, u_int8_t); +static void encode_base64(u_int8_t *, u_int8_t *, u_int16_t); +static void decode_base64(u_int8_t *, u_int16_t, const u_int8_t *); + +//static char encrypted[_PASSWORD_LEN]; +static char gsalt[BCRYPT_MAXSALT * 4 / 3 + 1]; +static int error = -1; + +static const u_int8_t Base64Code[] = +"./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + +static const u_int8_t index_64[128] = +{ + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 0, 1, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, 255, 255, + 255, 255, 255, 255, 255, 2, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, + 255, 255, 255, 255, 255, 255, 28, 29, 30, + 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 53, 255, 255, 255, 255, 255 +}; +#define CHAR64(c) ( (c) > 127 ? 255 : index_64[(c)]) + +static void +decode_base64(u_int8_t *buffer, u_int16_t len, const u_int8_t *data) +{ + u_int8_t *bp = buffer; + const u_int8_t *p = data; + u_int8_t c1, c2, c3, c4; + while (bp < buffer + len) { + c1 = CHAR64(*p); + c2 = CHAR64(*(p + 1)); + + /* Invalid data */ + if (c1 == 255 || c2 == 255) + break; + + *bp++ = (u_int8_t)((c1 << 2) | ((c2 & 0x30) >> 4)); + if (bp >= buffer + len) + break; + + c3 = CHAR64(*(p + 2)); + if (c3 == 255) + break; + + *bp++ = ((c2 & 0x0f) << 4) | ((c3 & 0x3c) >> 2); + if (bp >= buffer + len) + break; + + c4 = CHAR64(*(p + 3)); + if (c4 == 255) + break; + *bp++ = ((c3 & 0x03) << 6) | c4; + + p += 4; + } +} + +static void +encode_salt(char *salt, u_int8_t *csalt, u_int16_t clen, u_int8_t logr) +{ + salt[0] = '$'; + salt[1] = BCRYPT_VERSION; + salt[2] = 'a'; + salt[3] = '$'; + + snprintf(salt + 4, 4, "%2.2u$", logr); + + encode_base64((u_int8_t *) salt + 7, csalt, clen); +} +/* Generates a salt for this version of crypt. + Since versions may change. Keeping this here + seems sensible. + */ + +char * +bcrypt_gensalt(u_int8_t log_rounds) +{ + u_int8_t csalt[BCRYPT_MAXSALT]; + u_int16_t i; + u_int32_t seed = 0; + + for (i = 0; i < BCRYPT_MAXSALT; i++) { + if (i % 4 == 0) + seed = arc4random(); + csalt[i] = seed & 0xff; + seed = seed >> 8; + } + + if (log_rounds < 4) + log_rounds = 4; + + encode_salt(gsalt, csalt, BCRYPT_MAXSALT, log_rounds); + return gsalt; +} +/* We handle $Vers$log2(NumRounds)$salt+passwd$ + i.e. $2$04$iwouldntknowwhattosayetKdJ6iFtacBqJdKe6aW7ou */ + +int +crypt_blowfish(const char *key, const char *salt, char *encrypted) +{ + blf_ctx state; + u_int32_t rounds, i, k; + u_int16_t j; + u_int8_t key_len, salt_len, logr, minr; + u_int8_t ciphertext[4 * BCRYPT_BLOCKS] = "OrpheanBeholderScryDoubt"; + u_int8_t csalt[BCRYPT_MAXSALT]; + u_int32_t cdata[BCRYPT_BLOCKS]; + static const char *magic = "$2a$04$"; + + /* Defaults */ + minr = 'a'; + logr = 4; + rounds = 1 << logr; + + /* If it starts with the magic string, then skip that */ + if(!strncmp(salt, magic, strlen(magic))) { + salt += strlen(magic); + } + else if (*salt == '$') { + + /* Discard "$" identifier */ + salt++; + + if (*salt > BCRYPT_VERSION) { + /* How do I handle errors ? Return ':' */ + return error; + } + + /* Check for minor versions */ + if (salt[1] != '$') { + switch (salt[1]) { + case 'a': + /* 'ab' should not yield the same as 'abab' */ + minr = (u_int8_t)salt[1]; + salt++; + break; + default: + return error; + } + } else + minr = 0; + + /* Discard version + "$" identifier */ + salt += 2; + + if (salt[2] != '$') + /* Out of sync with passwd entry */ + return error; + + /* Computer power doesnt increase linear, 2^x should be fine */ + logr = (u_int8_t)atoi(salt); + rounds = 1 << logr; + if (rounds < BCRYPT_MINROUNDS) + return error; + + /* Discard num rounds + "$" identifier */ + salt += 3; + } + + + /* We dont want the base64 salt but the raw data */ + decode_base64(csalt, BCRYPT_MAXSALT, salt); + salt_len = BCRYPT_MAXSALT; + key_len = (u_int8_t)(strlen(key) + (minr >= 'a' ? 1 : 0)); + + /* Setting up S-Boxes and Subkeys */ + Blowfish_initstate(&state); + Blowfish_expandstate(&state, csalt, salt_len, + (const u_int8_t *) key, key_len); + for (k = 0; k < rounds; k++) { + Blowfish_expand0state(&state, (const u_int8_t *) key, key_len); + Blowfish_expand0state(&state, csalt, salt_len); + } + + /* This can be precomputed later */ + j = 0; + for (i = 0; i < BCRYPT_BLOCKS; i++) + cdata[i] = Blowfish_stream2word(ciphertext, 4 * BCRYPT_BLOCKS, &j); + + /* Now do the encryption */ + for (k = 0; k < 64; k++) + blf_enc(&state, cdata, BCRYPT_BLOCKS / 2); + + for (i = 0; i < BCRYPT_BLOCKS; i++) { + ciphertext[4 * i + 3] = cdata[i] & 0xff; + cdata[i] = cdata[i] >> 8; + ciphertext[4 * i + 2] = cdata[i] & 0xff; + cdata[i] = cdata[i] >> 8; + ciphertext[4 * i + 1] = cdata[i] & 0xff; + cdata[i] = cdata[i] >> 8; + ciphertext[4 * i + 0] = cdata[i] & 0xff; + } + + + i = 0; + encrypted[i++] = '$'; + encrypted[i++] = BCRYPT_VERSION; + if (minr) + encrypted[i++] = (int8_t)minr; + encrypted[i++] = '$'; + + snprintf(encrypted + i, 4, "%2.2u$", logr); + + encode_base64((u_int8_t *) encrypted + i + 3, csalt, BCRYPT_MAXSALT); + encode_base64((u_int8_t *) encrypted + strlen(encrypted), ciphertext, + 4 * BCRYPT_BLOCKS - 1); + return 0; +} + +static void +encode_base64(u_int8_t *buffer, u_int8_t *data, u_int16_t len) +{ + u_int8_t *bp = buffer; + u_int8_t *p = data; + u_int8_t c1, c2; + while (p < data + len) { + c1 = *p++; + *bp++ = Base64Code[(c1 >> 2)]; + c1 = (c1 & 0x03) << 4; + if (p >= data + len) { + *bp++ = Base64Code[c1]; + break; + } + c2 = *p++; + c1 |= (c2 >> 4) & 0x0f; + *bp++ = Base64Code[c1]; + c1 = (c2 & 0x0f) << 2; + if (p >= data + len) { + *bp++ = Base64Code[c1]; + break; + } + c2 = *p++; + c1 |= (c2 >> 6) & 0x03; + *bp++ = Base64Code[c1]; + *bp++ = Base64Code[c2 & 0x3f]; + } + *bp = '\0'; +} + +int compare_string(const char* s1, const char* s2) { + + int eq = 1; + int s1_len = strlen(s1); + int s2_len = strlen(s2); + + if (s1_len != s2_len) { + eq = 0; + } + + const int max_len = (s2_len < s1_len) ? s1_len : s2_len; + + // to prevent timing attacks, should check entire string + // don't exit after found to be false + int i; + for (i = 0; i < max_len; ++i) { + if (s1_len >= i && s2_len >= i && s1[i] != s2[i]) { + eq = 0; + } + } + + return eq; +} + +#if 0 +void +main() +{ + char blubber[73]; + char tocheck[73]; + char p[_PASSWORD_LEN]; + char check[_PASSWORD_LEN]; + char *s; + char salt[100]; + //char *p, *s, *check; + salt[0] = '$'; + salt[1] = BCRYPT_VERSION; + salt[2] = '$'; + + snprintf(salt + 3, 4, "%2.2u$", 5); + + salt[99] = 0; + printf("72 bytes of password: "); + fgets(blubber, 73, stdin); + blubber[72] = 0; + + s = bcrypt_gensalt(5); + printf("Generated salt: %s\n", s); + crypt_blowfish(blubber, s, p); + printf("Passwd entry: %s\n", p); + fflush(stdin); + + printf("Enter password for check:"); + fgets(tocheck, 73, stdin); + tocheck[72] = 0; + + crypt_blowfish(tocheck, s, check); + printf("blowfish generated (should match passwd entry): %s\n", check); + + if (compare_string(p, check)==1) { + printf("Password matches.\n"); + } else { + printf("INVALID PASSWORD.\n"); + } +} +#endif diff --git a/nimwcpkg/passwords/passwords.nim b/nimwcpkg/passwords/passwords.nim index f512f3cdb..a50aef772 100644 --- a/nimwcpkg/passwords/passwords.nim +++ b/nimwcpkg/passwords/passwords.nim @@ -1,18 +1,44 @@ -import md5, bcrypt, contra, random - +import md5, random var urandom: File let useUrandom = urandom.open("/dev/urandom") -template makeSessionKey*(): string = - ## Creates a random key to be used to authorize a session. - bcrypt.hash(makeSalt(), genSalt(8)) +{.compile: "bcrypt/arc4random.c", compile: "bcrypt/blowfish.c", compile: "bcrypt/crypt-blowfish.c", pragma: mydll.} -template makePassword*(password, salt: string, comparingTo = ""): string = - ## Creates an MD5 hash by combining password and salt. - preconditions password.len > 3, password.len < 301 - bcrypt.hash(getMD5(salt & getMD5(password)), if comparingTo != "": comparingTo else: genSalt(8)) +func bcrypt_gensalt(rounds: int8): cstring {.cdecl, mydll, importc: "bcrypt_gensalt".} +func blowfish(key, salt, encrypted: cstring): int {.cdecl, mydll, importc: "crypt_blowfish".} +func compare_string(s1, s2: cstring): int {.cdecl, mydll, importc: "compare_string".} + +func genSalt(rounds: int8): string {.inline.} = $(bcrypt_gensalt(rounds)) + +func compare(s1, s2: string): bool {.inline.} = compare_string(s1, s2) != 0 + +func hash(key, salt: string): string = + var encrypted = newString(60) + var ret {.used.} = blowfish(key, salt, encrypted.cstring) + result = $encrypted -include "_salts" +proc makeSalt*(): string = + ## Generate random salt. Uses cryptographically secure /dev/urandom + if likely(useUrandom): + var randomBytes: array[0..127, char] + discard urandom.readBuffer(addr(randomBytes), 128) + for ch in randomBytes: + if ord(ch) in {32..126}: + result.add(ch) + else: # Fallback to Nim random when no /dev/urandom + for i in 0..127: + result.add(chr(rand(94) + 32)) # Generate numbers from 32 to 94 + 32 = 126 + + +proc makeSessionKey*(): string = + ## Creates a random key to be used to authorize a session. + hash(makeSalt(), genSalt(8)) + + +proc makePassword*(password, salt: string, comparingTo = ""): string = + ## Creates an MD5 hash by combining password and salt. + assert password.len > 3 and password.len < 301 + hash(getMD5(salt & getMD5(password)), if comparingTo != "": comparingTo else: genSalt(8)) diff --git a/nimwcpkg/plugins/_deleters.nim b/nimwcpkg/plugins/_deleters.nim index d8ec64c20..3a07e3881 100644 --- a/nimwcpkg/plugins/_deleters.nim +++ b/nimwcpkg/plugins/_deleters.nim @@ -2,7 +2,7 @@ proc pluginDelete*(pluginFolder: string): bool = ## Delete a Plugin from the filesystem. - preconditions pluginFolder.len > 0, existsFile"plugins/plugin_import.txt" + assert pluginFolder.len > 0 and existsFile"plugins/plugin_import.txt" for line in lines("plugins/plugin_import.txt"): if line == pluginFolder: return false diff --git a/nimwcpkg/plugins/_detailers.nim b/nimwcpkg/plugins/_detailers.nim index 09fc71207..45a6dec81 100644 --- a/nimwcpkg/plugins/_detailers.nim +++ b/nimwcpkg/plugins/_detailers.nim @@ -2,14 +2,11 @@ proc pluginGetDetails*(pluginFolder: string): tuple[name, version, description, url: string] = ## Get plugin data from [pluginName]/plugin.json - preconditions pluginFolder.len > 0 - postconditions result[0].len > 0, result[1].len > 0, result[2].len > 0, result[3].len > 0 - let pluginJsonPath = "plugins" / pluginFolder / "plugin.json" - let pluginJson = parseFile(pluginJsonPath) - for plugin in items(pluginJson): - let - name = plugin["name"].getStr.strip - version = plugin["version"].getStr.strip - description = plugin["description"].getStr.strip - url = plugin["url"].getStr.strip - return (name, version, description, url) + assert pluginFolder.len > 0 + for plugin in items(parseFile("plugins" / pluginFolder / "plugin.json")): + return ( + plugin["name"].getStr, + plugin["version"].getStr, + plugin["description"].getStr, + plugin["url"].getStr + ) diff --git a/nimwcpkg/plugins/_downloaders.nim b/nimwcpkg/plugins/_downloaders.nim index 77f53099d..a28e78880 100644 --- a/nimwcpkg/plugins/_downloaders.nim +++ b/nimwcpkg/plugins/_downloaders.nim @@ -1,15 +1,16 @@ ## Do NOT import this file directly, instead import ``plugins.nim`` -proc pluginDownload*(pluginGit, pluginFolder: string): bool = +proc pluginDownload*(pluginGit, pluginFolder: string): bool {.inline.} = ## Downloads an external plugin with clone - preconditions pluginGit.len > 0, pluginFolder.len > 0 + assert pluginGit.len > 0 and pluginFolder.len > 0 execCmdEx("git clone --depth 1 " & pluginGit & " " & - replace(getAppDir(), "/nimwcpkg", "") / "plugins" / pluginFolder).exitCode == 0 + replace(getAppDir(), "/nimwcpkg", "") / "plugins" / pluginFolder, + options = {poStdErrToStdOut, poUsePath, poEchoCmd}).exitCode == 0 proc pluginUpdate*(pluginFolder: string): bool = ## Updates an external plugin with pull - preconditions pluginFolder.len > 0 + assert pluginFolder.len > 0 if execCmdEx("git -C plugins" / pluginFolder & " fetch --all").exitCode == 0: if execCmdEx("git -C plugins" / pluginFolder & " reset --hard origin/master").exitCode == 0: result = execCmdEx("git -C plugins" / pluginFolder & " pull origin master").exitCode == 0 diff --git a/nimwcpkg/plugins/_repos.nim b/nimwcpkg/plugins/_repos.nim index feb2357ef..6b88be604 100644 --- a/nimwcpkg/plugins/_repos.nim +++ b/nimwcpkg/plugins/_repos.nim @@ -1,17 +1,14 @@ ## Do NOT import this file directly, instead import ``plugins.nim`` -proc pluginRepoClone*(): bool = +proc pluginRepoClone*(): bool {.inline.} = ## Clones (updates) the plugin repo - preconditions pluginRepo.len > 0 - let cmd = "git clone " & pluginRepo & " " & replace(getAppDir(), "/nimwcpkg", "") / "plugins/nimwc_plugins" - when defined(dev): echo cmd - execCmdEx(cmd).exitCode == 0 + assert pluginRepo.len > 0 # poEchoCmd does echo cmd + execCmdEx("git clone " & pluginRepo & " " & replace(getAppDir(), "/nimwcpkg", "") / "plugins/nimwc_plugins", + options = {poStdErrToStdOut, poUsePath, poEchoCmd}).exitCode == 0 -proc pluginRepoUpdate*(): bool = +proc pluginRepoUpdate*(): bool {.inline.} = ## Clones (updates) the plugin repo - preconditions existsDir("plugins" / pluginRepoName) - postconditions fileExists("plugins/nimwc_plugins/plugins.json") - let cmd = "git -C plugins" / pluginRepoName & " pull" - when defined(dev): echo cmd - execCmdEx(cmd).exitCode == 0 + assert existsDir("plugins" / pluginRepoName) + execCmdEx("git -C plugins" / pluginRepoName & " pull", + options = {poStdErrToStdOut, poUsePath, poEchoCmd}).exitCode == 0 diff --git a/nimwcpkg/plugins/_settings.nim b/nimwcpkg/plugins/_settings.nim index f742dae6e..97f6e3279 100644 --- a/nimwcpkg/plugins/_settings.nim +++ b/nimwcpkg/plugins/_settings.nim @@ -4,15 +4,14 @@ proc extensionSettings(): seq[string] = ## Proc to check if plugins listed in plugins_imported.txt ## are enabled or disabled. The result will be "true:pluginname" ## or "false:pluginname". - preconditions existsDir"plugins/", existsFile"plugins/plugin_import.txt" - postconditions if readFile("plugins/plugin_import.txt").splitLines.len > 1: result.len > 0 else: true - let plugins = readFile("plugins/plugin_import.txt").splitLines + assert existsDir"plugins/" and existsFile"plugins/plugin_import.txt" + var extensions = create(seq[string], sizeOf seq[string]) + var plugins = create(seq[string], sizeOf seq[string]) + plugins[] = readFile("plugins/plugin_import.txt").splitLines # Walk through files and folders in the plugin directory - var extensions: seq[string] for plugin in walkDir("plugins/"): - let (pd, ppath) = plugin - discard pd + let (_, ppath) = plugin let ppathName = replace(ppath, "plugins/", "") # Skip these files/folders @@ -20,41 +19,43 @@ proc extensionSettings(): seq[string] = # If the plugins is present in plugin_import, set the # plugin status to true, else false - if ppathName in plugins: - if extensions.len == 0: - extensions = @["true:" & ppathName] + if ppathName in plugins[]: + if extensions[].len == 0: + extensions[] = @["true:" & ppathName] else: - extensions.add("true:" & ppathName) + extensions[].add("true:" & ppathName) else: - if extensions.len == 0: - extensions = @["false:" & ppathName] + if extensions[].len == 0: + extensions[] = @["false:" & ppathName] else: - extensions.add("false:" & ppathName) + extensions[].add("false:" & ppathName) - return extensions + result = extensions[] + deallocs plugins, extensions proc genExtensionSettings*(): string = ## Generate HTML list items with plugins + pluginName := "" for plugin in extensionSettings(): - let pluginName = (split(plugin, ":"))[1] + pluginName[] = (split(plugin, ":"))[1] result.add(pluginHtmlListItem.format( # $1 - pluginName, + pluginName[], # $2 (split(plugin, ":"))[0] == "true", # $3 if (split(plugin, ":"))[0] == "true": - pluginName & "/settings" + pluginName[] & "/settings" else: "#", # $4 - pluginName.capitalizeAscii, + pluginName[].capitalizeAscii, # $5 if (split(plugin, ":"))[0] == "true": @@ -68,3 +69,4 @@ proc genExtensionSettings*(): string = else: "disabled" )) + dealloc pluginName diff --git a/nimwcpkg/plugins/_switchers.nim b/nimwcpkg/plugins/_switchers.nim index ae0dd689d..f20835061 100644 --- a/nimwcpkg/plugins/_switchers.nim +++ b/nimwcpkg/plugins/_switchers.nim @@ -7,18 +7,18 @@ proc pluginEnableDisable*(pluginPath, pluginName, status: string) = ## this will enable the plugin (add a line) ## @"status" == true => Plugin is enabled, ## this will disable the plugin (remove the line) - preconditions pluginName.len > 0, status in ["false", "true"], existsFile"plugins/plugin_import.txt" - postconditions existsFile"plugins/plugin_import.txt" - var newFile = "" + assert pluginName.len > 0 and status in ["false", "true"] and existsFile"plugins/plugin_import.txt" + newFile := "" for line in lines("plugins/plugin_import.txt"): if line == "" or line == pluginPath: continue else: - newFile.add(line) + newFile[].add(line) - newFile.add("\n") + newFile[].add("\n") if status == "false": - newFile.add(pluginName) + newFile[].add(pluginName) - writeFile("plugins/plugin_import.txt", newFile) + writeFile("plugins/plugin_import.txt", newFile[]) + dealloc newFile diff --git a/nimwcpkg/plugins/plugins.nim b/nimwcpkg/plugins/plugins.nim index e6b5ab04a..c5737a674 100644 --- a/nimwcpkg/plugins/plugins.nim +++ b/nimwcpkg/plugins/plugins.nim @@ -1,9 +1,9 @@ -import os, strutils, contra, osproc +import os, strutils, osproc when defined(packedjson): import packedjson else: import json -import ../constants/constants +import ../constants/constants, ../utils/utils # Order is important here. diff --git a/nimwcpkg/utils/configs.nim b/nimwcpkg/utils/configs.nim index 40f114139..4ee1028f5 100644 --- a/nimwcpkg/utils/configs.nim +++ b/nimwcpkg/utils/configs.nim @@ -1,66 +1,70 @@ -import parsecfg, tables, os, ../enums/enums +import parsecfg, tables, os + +import ../enums/enums proc getConfig*(configPath: string, section: ConfigSections): Table[string, string] = ## Take file path & section, return Table of config, to access using cfg["key"] assert configPath.len > 0, "configPath must not be empty string" assert configPath[^4..^1] == ".cfg", "configPath must be .cfg file extension" - assert existsFile(configPath), "configPath file not found" - let dict = loadConfig(configPath) + assert fileExists(configPath), "configPath file not found" + let dict = create(Config, sizeOf Config) + dict[] = loadConfig(configPath) case section of cfgDatabase: result = { - "folder": dict.getSectionValue("Database", "folder"), - "host": dict.getSectionValue("Database", "host"), - "name": dict.getSectionValue("Database", "name"), - "user": dict.getSectionValue("Database", "user"), - "pass": dict.getSectionValue("Database", "pass"), - "port": dict.getSectionValue("Database", "port"), + "folder": dict[].getSectionValue("Database", "folder"), + "host": dict[].getSectionValue("Database", "host"), + "name": dict[].getSectionValue("Database", "name"), + "user": dict[].getSectionValue("Database", "user"), + "pass": dict[].getSectionValue("Database", "pass"), + "port": dict[].getSectionValue("Database", "port"), }.toTable of cfgStorage: result = { - "storagedev": dict.getSectionValue("Storage", "storagedev"), - "storage": dict.getSectionValue("Storage", "storage"), + "storagedev": dict[].getSectionValue("Storage", "storagedev"), + "storage": dict[].getSectionValue("Storage", "storage"), }.toTable of cfgLogging: result = { - "logfiledev": dict.getSectionValue("Logging", "logfiledev"), - "logfile": dict.getSectionValue("Logging", "logfile"), + "logfiledev": dict[].getSectionValue("Logging", "logfiledev"), + "logfile": dict[].getSectionValue("Logging", "logfile"), }.toTable of cfgServer: result = { - "website": dict.getSectionValue("Server", "website"), - "title": dict.getSectionValue("Server", "title"), - "url": dict.getSectionValue("Server", "url"), - "port": dict.getSectionValue("Server", "port"), - "appname": dict.getSectionValue("Server", "appname"), + "website": dict[].getSectionValue("Server", "website"), + "title": dict[].getSectionValue("Server", "title"), + "url": dict[].getSectionValue("Server", "url"), + "port": dict[].getSectionValue("Server", "port"), + "appname": dict[].getSectionValue("Server", "appname"), }.toTable of cfgProxy: result = { - "url": dict.getSectionValue("Proxy", "url"), - "path": dict.getSectionValue("Proxy", "path"), + "url": dict[].getSectionValue("Proxy", "url"), + "path": dict[].getSectionValue("Proxy", "path"), }.toTable of cfgRecaptcha: result = { - "Sitekey": dict.getSectionValue("reCAPTCHA", "Sitekey"), - "Secretkey": dict.getSectionValue("reCAPTCHA", "Secretkey"), + "Sitekey": dict[].getSectionValue("reCAPTCHA", "Sitekey"), + "Secretkey": dict[].getSectionValue("reCAPTCHA", "Secretkey"), }.toTable of cfgSmtp: result = { - "SMTPAddress": dict.getSectionValue("SMTP", "SMTPAddress"), - "SMTPPort": dict.getSectionValue("SMTP", "SMTPPort"), - "SMTPUser": dict.getSectionValue("SMTP", "SMTPUser"), - "SMTPPassword": dict.getSectionValue("SMTP", "SMTPPassword"), - "SMTPFrom": dict.getSectionValue("SMTP", "SMTPFrom"), - "SMTPEmailAdmin": dict.getSectionValue("SMTP", "SMTPEmailAdmin"), - "SMTPEmailSupport": dict.getSectionValue("SMTP", "SMTPEmailSupport"), + "SMTPAddress": dict[].getSectionValue("SMTP", "SMTPAddress"), + "SMTPPort": dict[].getSectionValue("SMTP", "SMTPPort"), + "SMTPUser": dict[].getSectionValue("SMTP", "SMTPUser"), + "SMTPPassword": dict[].getSectionValue("SMTP", "SMTPPassword"), + "SMTPFrom": dict[].getSectionValue("SMTP", "SMTPFrom"), + "SMTPEmailAdmin": dict[].getSectionValue("SMTP", "SMTPEmailAdmin"), + "SMTPEmailSupport": dict[].getSectionValue("SMTP", "SMTPEmailSupport"), }.toTable of cfgFirejail: result = { - "folder": dict.getSectionValue("firejail", "folder"), - "host": dict.getSectionValue("firejail", "host"), - "name": dict.getSectionValue("firejail", "name"), - "user": dict.getSectionValue("firejail", "user"), - "pass": dict.getSectionValue("firejail", "pass"), - "port": dict.getSectionValue("firejail", "port"), + "folder": dict[].getSectionValue("firejail", "folder"), + "host": dict[].getSectionValue("firejail", "host"), + "name": dict[].getSectionValue("firejail", "name"), + "user": dict[].getSectionValue("firejail", "user"), + "pass": dict[].getSectionValue("firejail", "pass"), + "port": dict[].getSectionValue("firejail", "port"), }.toTable + dealloc dict diff --git a/nimwcpkg/utils/jail.nim b/nimwcpkg/utils/jail.nim new file mode 100644 index 000000000..397e49fb2 --- /dev/null +++ b/nimwcpkg/utils/jail.nim @@ -0,0 +1,126 @@ + +func makeCommand*( + + command: string, timeout: range[0..99] = 0, name = "", + gateway = "", hostsFile = "", logFile = "", chroot = "", tmpfs = "", + whitelist: seq[string] = @[], blacklist: seq[string] = @[], + dnsServers: array[4, string] = ["", "", "", ""], maxSubProcesses = 0, + maxOpenFiles = 0, maxFileSize = 0, maxPendingSignals = 0, + maxRam = 0, maxCpu = 0, cpuCoresByNumber: seq[int] = @[], + + # All the Boolean options. + apparmor, caps, no3d, noDbus, noDvd, noGroups, noNewPrivs, noRoot, noSound, + noAutoPulse, noVideo, noU2f, noTv, privateTmp, private, privateCache, privateDev, + seccomp, noX, noNet, noIp, appimage, useNice20, useMtuJumbo9000, newIpcNamespace, + noMnt, noAllusers, noMachineId, noKeepDevShm, noDebuggers, noShell, noRamWriteExec, + writables, forceEnUsUtf8, useRandomMac: bool + + ): string {.noinline.} = + ## Generate a Firejail container command from arguments. + + var blancas: string + if whitelist != @[]: + for folder in whitelist: + if folder.len > 1: + assert folder notin ["~", "/dev", "/usr", "/etc", "/opt", "/var", "/bin", "/proc", "/media", "/mnt", "/srv", "/sys"] + blancas.add "--whitelist='" & folder & "' " + + var negras: string + if blacklist != @[]: + for folder in blacklist: + if folder.len > 1: negras.add "--blacklist='" & folder & "' " + + var denese: string + if dnsServers != ["", "", "", ""]: + for servo in dnsServers: + if servo.len > 6: denese.add "--dns='" & servo & "' " + + var cpus: string + if cpuCoresByNumber != @[]: + for it in cpuCoresByNumber: cpus.add $it & "," + + + result = ( + # The Firejail command itself. + "firejail --noprofile " & + + # Debug when not release. + (when defined(release): "--quiet " else: "--debug ") & + + # All the Boolean options. + (if apparmor: "--apparmor " else: "") & + (if caps: "--caps " else: "") & + (if no3d: "--no3d " else: "") & + (if noDbus: "--nodbus " else: "") & + (if noDvd: "--nodvd " else: "") & + (if noGroups: "--nogroups " else: "") & + (if noNewPrivs: "--nonewprivs " else: "") & + (if noRoot: "--noroot " else: "") & + (if noSound: "--nosound " else: "") & + (if noAutoPulse: "--noautopulse " else: "") & + (if noVideo: "--novideo " else: "") & + (if noU2f: "--nou2f " else: "") & + (if noTv: "--notv " else: "") & + (if privateTmp: "--private-tmp " else: "") & + (if private: "--private " else: "") & + (if privateCache: "--private-cache " else: "") & + (if privateDev: "--private-dev " else: "") & + (if seccomp: "--seccomp " else: "") & + (if noX: "--x11=xvfb " else: "") & # "none" complains about network. + (if noNet: "--net=none " else: "") & + (if noIp: "--ip=none " else: "") & + (if appimage: "--appimage " else: "") & + (if useNice20: "--nice=20 " else: "") & + (if useMtuJumbo9000: "--mtu=9000 " else: "") & + (if newIpcNamespace: "--ipc-namespace " else: "") & + (if noMnt: "--disable-mnt " else: "") & + (if noAllusers: "" else: "--allusers " ) & + (if noMachineId: "" else: "--machine-id " ) & + (if noKeepDevShm: "" else: "--keep-dev-shm " ) & + (if noDebuggers: "" else: "--allow-debuggers ") & + (if noShell: "--shell=none " else: "--shell=/bin/bash ") & #ZSH/Fish sometimes fail,force plain old Bash. + (if noRamWriteExec: "--memory-deny-write-execute " else: "" ) & + (if writables: "--writable-etc --writable-run-user --writable-var --writable-var-log " else: "") & + (if forceEnUsUtf8: "--env=LC_CTYPE='en_US.UTF-8' --env=LC_NUMERIC='en_US.UTF-8' --env=LC_TIME='en_US.UTF-8' --env=LC_COLLATE='en_US.UTF-8' --env=LC_MONETARY='en_US.UTF-8' --env=LC_MESSAGES='en_US.UTF-8' --env=LC_PAPER='en_US.UTF-8' --env=LC_NAME='en_US.UTF-8' --env=LC_ADDRESS='en_US.UTF-8' --env=LC_TELEPHONE='en_US.UTF-8' --env=LC_MEASUREMENT='en_US.UTF-8' --env=LC_IDENTIFICATION='en_US.UTF-8' --env=LC_ALL='en_US.UTF-8' --env=LANG='en_US.UTF-8' " else: "") & + + # All the options with arguments. + (if timeout != 0: "--timeout='" & $timeout & ":00:00' " else: "") & + (if gateway != "": "--defaultgw='" & gateway & "' " else: "") & + (if hostsFile != "": "--hosts-file='" & hostsFile & "' " else: "") & + (if chroot != "": "--chroot='" & chroot & "' " else: "") & + (if tmpfs != "": "--tmpfs='" & tmpfs & "' " else: "") & + (if maxRam != 0: "--rlimit-as='" & $maxRam & "' " else: "") & + (if maxCpu != 0: "--rlimit-cpu='" & $maxCpu & "' " else: "") & + (if maxFileSize != 0: "--rlimit-fsize='" & $maxFileSize & "' " else: "") & + (if maxOpenFiles != 0: "--rlimit-nofile='" & $maxOpenFiles & "' " else: "") & + (if maxSubProcesses != 0: "--rlimit-nproc='" & $maxSubProcesses & "' " else: "") & + (if cpuCoresByNumber != @[]: "--cpu='" & cpus[0..^2] & "' " else: "") & + (if maxPendingSignals != 0: "--rlimit-sigpending='" & $maxPendingSignals & "' " else: "") & + (if name != "": "--name='" & name & "' --hostname='" & name & "' " else: "") & + (if logfile != "": "--output='" & logFile & "' --output-stderr='" & logFile & "' " else: "") & + + # Variables and the command itself. + denese & blancas & negras & command + ) + + +# when isMainModule: +# echo makeCommand( +# command = "echo 42", timeout = 99, name = "myAppName", gateway = "10.0.0.1", +# hostsFile = "/etc/hosts", logfile = "/tmp/myApp.log", chroot = "/tmp/chroot/", +# tmpfs = "/tmp/tmpfs", dnsServers = ["8.8.8.8", "8.8.4.4", "1.1.1.1", "1.1.1.2"], +# whitelist = @["/tmp/one", "/tmp/two"], blacklist = @["/usr/bin", "/share/bin"], +# maxSubProcesses = int8.high, maxOpenFiles = int8.high, maxFileSize = int32.high, +# maxPendingSignals = int16.high, maxRam = int16.high, maxCpu = int32.high, +# cpuCoresByNumber = @[0, 2], +# +# noAllusers = false, apparmor = false, caps = true, noKeepDevShm = false, +# noMachineId = false, noRamWriteExec = true, no3d = true, noDbus = true, +# noDvd = true, noGroups = true, noNewPrivs = true, noRoot = true, noSound = true, +# noAutoPulse = true, noVideo = true, forceEnUsUtf8 = true, noU2f = true, +# privateTmp = true, private = true, privateCache = true, noMnt = true, +# privateDev = true, seccomp = true, noShell = true, noNet = true, noIp = true, +# noDebuggers = false, newIpcNamespace = true, appimage = true, noTv = true, +# useMtuJumbo9000 = true, useNice20 = true, noX = true, useRandomMac = true, +# writables = false +# ) diff --git a/nimwcpkg/utils/loggers.nim b/nimwcpkg/utils/loggers.nim index a5b88cbf1..d3710846f 100644 --- a/nimwcpkg/utils/loggers.nim +++ b/nimwcpkg/utils/loggers.nim @@ -1,19 +1,19 @@ import os, osproc, parsecfg, times, logging, strutils + import ../constants/constants, ../emails/emails let nimwcpkgDir = getAppDir().replace("/nimwcpkg", "") -let configFile = nimwcpkgDir / "config/config.cfg" -assert existsDir(nimwcpkgDir), "nimwcpkg directory not found: " & nimwcpkgDir -assert existsFile(configFile), "config/config.cfg file not found" +assert dirExists(nimwcpkgDir), "nimwcpkg directory not found: " & nimwcpkgDir +assert fileExists(nimwcpkgDir / "config/config.cfg"), "config/config.cfg file not found" setCurrentDir(nimwcpkgDir) once: discard existsOrCreateDir("log") addHandler(newConsoleLogger(fmtStr = verboseFmtStr)) addHandler(newRollingFileLogger( # Logs to rotating files. - when defined(release): loadConfig(configFile).getSectionValue("Logging", "logfile") - else: loadConfig(configFile).getSectionValue("Logging", "logfiledev"), + when defined(release): loadConfig(nimwcpkgDir / "config/config.cfg").getSectionValue("Logging", "logfile") + else: loadConfig(nimwcpkgDir / "config/config.cfg").getSectionValue("Logging", "logfiledev"), fmtStr = verboseFmtStr)) debug("Rolling File Logger logs at: " & defaultFilename()) @@ -27,12 +27,11 @@ template log2admin*(msg: string) = proc backupOldLogs*(logFilePath: string): tuple[output: TaintedString, exitCode: int] = ## Compress all old rotated Logs. - assert existsDir(logFilePath), "logFilePath File not found" + assert dirExists(logFilePath), "logFilePath File not found" assert findExe("tar").len > 0, "Tar not found" var files2tar: seq[string] for logfile in walkFiles(logFilePath / "*.log"): files2tar.add logfile if files2tar.len > 1: - let cmd = cmdTar & logFilePath / "logs-" & replace($now(), ":", "_") & ".tar.gz " & files2tar.join" " - result = execCmdEx(cmd) + result = execCmdEx(cmdTar & logFilePath / "logs-" & replace($now(), ":", "_") & ".tar.gz " & files2tar.join" ") if result.exitCode == 0: for filename in files2tar: discard tryRemoveFile(filename) diff --git a/nimwcpkg/utils/projectgen.nim b/nimwcpkg/utils/projectgen.nim new file mode 100644 index 000000000..25c73ceb8 --- /dev/null +++ b/nimwcpkg/utils/projectgen.nim @@ -0,0 +1,85 @@ +import os, times, rdstdin, strutils + +import ../utils/utils + + +template pluginSkeleton*() = + ## Creates the skeleton (folders and files) for a plugin + creates "", pluginName, authorName, authorMail, authorWeb, folder, ext + styledEcho(fgCyan, bgBlack, skeletonMsg) + pluginName[] = readLineFromStdin("Plugin name: ").strip.normalize + assert pluginName[].len > 1, "Plugin name needs to be longer: " & pluginName[] + + authorName[] = readLineFromStdin("Author name: ").strip + authorMail[] = readLineFromStdin("Author email: ").strip.toLowerAscii + authorWeb[] = readLineFromStdin("Author website HTTP URL: ").strip + + # Create dirs + discard existsOrCreateDir("plugins") + folder[] = "plugins" / pluginName[] + discard existsOrCreateDir("plugins") + discard existsOrCreateDir(folder[]) + discard existsOrCreateDir(folder[] / "public") + + # Create files + writeFile(folder[] / pluginName[] & ".nim", reqCode.format(pluginName[])) + writeFile(folder[] / "routes.nim", reqRoutes.format(pluginName[])) + writeFile(folder[] / "public" / "js.js", + "/* https://github.com/pragmagic/karax OR Vanilla JavaScript */\n") + writeFile(folder[] / "public" / "style.css", + "/* https://bulma.io/documentation OR clean CSS */\n") + + if readLineFromStdin("Generate optional compile-time config files? (y/N): ") == "y": + if readLineFromStdin("Use NimScript instead of CFG? (y/N): ") == "y": + writeFile(folder[] / pluginName[] & ".nims", "# https://nim-lang.org/docs/nims.html\n") + else: + writeFile(folder[] / pluginName[] & ".nim.cfg", "# https://nim-lang.org/docs/parsecfg.html\n") + + if readLineFromStdin("Generate Unitests files on ./tests/ ? (y/N): ") == "y": + discard existsOrCreateDir(folder[] / "tests") + writeFile(folder[] / "tests" / "test1.nim", "") + + if readLineFromStdin("Generate GitHub files including GitHub Actions on ./github/ ? (y/N): ") == "y": + discard existsOrCreateDir(folder[] / ".github") + discard existsOrCreateDir(folder[] / ".github" / "workflows") + discard existsOrCreateDir(folder[] / ".github/ISSUE_TEMPLATE") + discard existsOrCreateDir(folder[] / ".github/PULL_REQUEST_TEMPLATE") + writeFile(folder[] / ".github/ISSUE_TEMPLATE/ISSUE_TEMPLATE.md", "") + writeFile(folder[] / ".github/PULL_REQUEST_TEMPLATE/PULL_REQUEST_TEMPLATE.md", "") + writeFile(folder[] / ".github/FUNDING.yml", "") + writeFile(folder[] / ".github" / "workflows" / "build.yml", "") + + if readLineFromStdin("Generate NimWC files? (y/N): ") == "y": + writeFile(folder[] / "public/js_private.js", "") + writeFile(folder[] / "public/style_private.css", "") + writeFile(folder[] / "html.nimf", "<!-- https://nim-lang.org/docs/filters.html -->\n") + + if readLineFromStdin("Generate .gitignore file? (y/N): ") == "y": + writeFile(folder[] / ".gitattributes", "*.* linguist-language=Nim\n") + writeFile(folder[] / ".gitignore", "*.c\n*.h\n*.o\n*.sql\n*.log\n*.s") + + if readLineFromStdin("Generate README,LICENSE,CHANGELOG,etc ? (y/N): ") == "y": + ext[] = if readLineFromStdin("Use Markdown (MD) instead of ReSTructuredText (RST)? (y/N): ") == "y": ".md" else: ".rst" + writeFile(folder[] / "LICENSE" & ext[], "See https://tldrlegal.com/licenses/browse\n") + writeFile(folder[] / "CODE_OF_CONDUCT" & ext[], "") + writeFile(folder[] / "CONTRIBUTING" & ext[], "") + writeFile(folder[] / "AUTHORS" & ext[], "# Authors\n\n- " & getEnv"USER" & "\n") + writeFile(folder[] / "README" & ext[], "# " & folder[] & "\n") + writeFile(folder[] / "CHANGELOG" & ext[], "# 0.0.1\n\n- First initial version of " & folder[] & " created at " & $now()) + + writeFile(folder[] / "plugin.json", pluginJson.format( + capitalizeAscii(pluginName[]), pluginName[], authorName[], authorMail[], authorWeb[], NimblePkgVersion.substr(0, 2))) + + setCurrentDir folder[] + if findExe"git".len > 0 and readLineFromStdin("Run 'git init .' on the Plugin folder? (y/N): ") == "y": + if execShellCmd("git init .") == 0: + if readLineFromStdin("Run 'git add .' on the Plugin folder? (y/N): ") == "y": + if execShellCmd("git add .") == 0: + if readLineFromStdin("Run 'git commit -a' on the Plugin folder? (y/N): ") == "y": + if execShellCmd("git commit -am 'init'") == 0: + if readLineFromStdin("Run 'git remote add origin ...' to add 1 Remote URL? (y/N): ") == "y": + if execShellCmd("git remote add origin " & readLineFromStdin("Git Remote URL?: ").strip) == 0: + if readLineFromStdin("Run 'git fetch --all' on the Plugin folder? (y/N): ") == "y": + echo execShellCmd("git fetch --all") == 0 + deallocs pluginName, authorName, authorMail, authorWeb, folder, ext + quit("New Plugin at /plugins/\n\nNimWC created a new Plugin skeleton, happy hacking, bye...", 0) diff --git a/nimwcpkg/utils/sysinfo.nim b/nimwcpkg/utils/sysinfo.nim new file mode 100644 index 000000000..aa7398291 --- /dev/null +++ b/nimwcpkg/utils/sysinfo.nim @@ -0,0 +1,35 @@ +import os, osproc, strutils, posix, posix_utils + +when defined(packedjson): import packedjson +else: import json + +template isSsd(): bool = + when defined(linux): # Returns `true` if main disk is SSD (Solid). Linux only + try: readFile("/sys/block/sda/queue/rotational") == "0\n" except: false + +proc getSystemInfo*(): JsonNode = + result = %*{ + "compiled": CompileDate & "T" & CompileTime, + "NimVersion": NimVersion, + "hostCPU": hostCPU, + "hostOS": hostOS, + "cpuEndian": cpuEndian, + "getTempDir": getTempDir(), + "getFreeMem": getFreeMem(), + "getTotalMem": getTotalMem(), + "getOccupiedMem": getOccupiedMem(), + "countProcessors": countProcessors(), + "arch": uname().machine, + "FSCaseSensitive": FileSystemCaseSensitive, + "currentCompilerExe": getCurrentCompilerExe(), + "nimpretty": if findExe"choosenim".len > 0: execCmdEx("nimpretty --version").output.strip else: "", + "nimble": if findExe"choosenim".len > 0: execCmdEx("nimble --noColor --version").output.strip else: "", + "nimsuggest": if findExe"choosenim".len > 0: execCmdEx("nimsuggest --version").output.strip else: "", + "choosenim": if findExe"choosenim".len > 0: execCmdEx("choosenim --noColor --version").output.strip else: "", + "gcc": if findExe"gcc".len > 0: execCmdEx("gcc --version").output.splitLines()[0].strip else: "", + "clang": if findExe"clang".len > 0: execCmdEx("clang --version").output.splitLines()[0].strip else: "", + "git": if findExe"git".len > 0: execCmdEx("git --version").output.replace("git version", "").strip else: "", + "node": if findExe"node".len > 0: execCmdEx("node --version").output.strip else: "", + "python": if findExe"python".len > 0: execCmdEx("python --version").output.replace("Python", "").strip else: "", + "ssd": isSsd() + } diff --git a/nimwcpkg/utils/updaters.nim b/nimwcpkg/utils/updaters.nim new file mode 100644 index 000000000..1e39e58b9 --- /dev/null +++ b/nimwcpkg/utils/updaters.nim @@ -0,0 +1,27 @@ +import os, osproc + +import ../utils/utils + + +proc updateNimwc*() = + ## GIT hard update + assert findExe"git".len > 0 + assert dirExists"plugins/" + assert dirExists"public/js/" + assert dirExists"public/css/" + assert fileExists"public/js/js_custom.js" + assert fileExists"plugins/plugin_import.txt" + assert fileExists"public/css/style_custom.css" + creates "", pluginImport, styleCustom, jsCustom, humansTxt + pluginImport[] = readFile"plugins/plugin_import.txt" # Save contents + styleCustom[] = readFile"public/css/style_custom.css" + jsCustom[] = readFile"public/js/js_custom.js" + humansTxt[] = readFile"public/humans.txt" + discard execCmdEx("git fetch --all ; git reset --hard origin/master", + options = {poStdErrToStdOut, poUsePath, poEchoCmd}) + writeFile("plugins/plugin_import.txt", pluginImport[]) # Write contents back + writeFile("public/css/style_custom.css", styleCustom[]) + writeFile("public/js/js_custom.js", jsCustom[]) + writeFile("public/humans.txt", humansTxt[]) + deallocs pluginImport, styleCustom, jsCustom, humansTxt + quit("\n\nTo finish the update:\n - Compile NimWC\n - Run with the arg `--newdb`\nGit fetch and update done", 0) diff --git a/nimwcpkg/utils/utils.nim b/nimwcpkg/utils/utils.nim new file mode 100644 index 000000000..2640f23ff --- /dev/null +++ b/nimwcpkg/utils/utils.nim @@ -0,0 +1,106 @@ +import macros + + +template hardenedBuild*() = + ## See: http:wiki.debian.org/Hardening & http:wiki.gentoo.org/wiki/Hardened_Gentoo + ## http:security.stackexchange.com/questions/24444/what-is-the-most-hardened-set-of-options-for-gcc-compiling-c-c + when defined(hardened) and defined(gcc) and not defined(objc) and not defined(js): + when defined(danger): {.fatal: "-d:hardened is incompatible with -d:danger".} + {.hint: "Security Hardened mode is enabled.".} + const hf = "-fstack-protector-all -Wstack-protector --param ssp-buffer-size=4 -pie -fPIE -Wformat -Wformat-security -D_FORTIFY_SOURCE=2 -Wall -Wextra -Wconversion -Wsign-conversion -mindirect-branch=thunk -mfunction-return=thunk -fstack-clash-protection -Wl,-z,relro,-z,now -Wl,-z,noexecstack -fsanitize=signed-integer-overflow -fsanitize-undefined-trap-on-error -fno-common" + {.passC: hf, passL: hf, assertions: on, checks: on.} + + +template getLibravatarUrl*(email: string, size: range[1..512] = 100, default = "robohash", forceDefault = false, + baseUrl = (when defined(ssl): "https://seccdn.libravatar.org/avatar/" else: "http://cdn.libravatar.org/avatar/")): string = + ## https://wiki.libravatar.org/api & https://wiki.libravatar.org/features + assert email.len > 5, "email must not be empty string" + assert email.len < 255, "email must be <255 characters long string" + assert '@' in email, "email must be a valid standard email address string" + assert baseUrl.len > 5, "baseUrl must be a valid HTTP URL string" + (baseUrl & getMD5(email.strip.toLowerAscii) & "?s=" & $size & + (if unlikely(default != ""): "&d=" & default else: "") & + (when defined(release): "" else: (if unlikely(forceDefault): "&f=y" else: "")) + ) + + +template cwebp*(inputFilename: string, outputFilename = "", preset = "drawing", + verbose = false, threads = true, lossless = false, noalpha = false, + quality: range[0..100] = 75): tuple[output: TaintedString, exitCode: int] = + ## Compress an image file to a WebP file. Input format can be either PNG, JPEG, TIFF, WebP. + assert inputFilename.len > 0, "inputFilename must not be empty string" + assert preset in ["default", "photo", "picture", "drawing", "icon", "text"] + execCmdEx( + (if unlikely(verbose): "cwebp -v " else: "cwebp -quiet ") & + (if likely(threads): "-mt " else: "") & + (if unlikely(lossless): "-lossless " else: "") & + (if unlikely(noalpha): "-noalpha " else: "") & + "-preset " & preset & " -q " & $quality & " -o " & + (if outputFilename.len == 0: quoteShell(inputFilename & ".webp") else: quoteShell(outputFilename)) & + " " & quoteShell(inputFilename) + ) + + +template `:=`*(name: untyped; value: any) = + ## Assistent for variables at compile-time. Uses system.nim + ## + ## expandMacros: + ## foo := 42 + ## + ## var foo = create(int, sizeOf int) + ## foo[] = 42 + ## + ## expandMacros: + ## bar := "Hello" + ## + ## var bar = create(string, sizeOf string) + ## bar[] = "Hello" + ## + ## https://nim-lang.github.io/Nim/system.html#create%2Ctypedesc + var name {.inject.} = create(type(value), sizeOf type(value)) + name[] = value + + +macro deallocs*(variables: varargs[typed]) = + ## This unrolls destruction of several variables at compile-time. Uses system.nim + ## + ## expandMacros: + ## deallocs foo, bar, baz + ## + ## dealloc foo + ## dealloc bar + ## dealloc baz + ## + ## https://nim-lang.github.io/Nim/system.html#dealloc.t%2Cpointer + result = newStmtList() # Creates a list of lines of code to be run. + for it in variables: result.add newCall(bindSym"dealloc", it) # Fills the list. + + +macro creates*(value: any; variables: varargs[untyped]) = + ## This unrolls creation of several variables at compile-time. Uses system.nim + ## + ## expandMacros: + ## creates 42, foo, bar, baz + ## + ## var foo = create(int, sizeOf int) + ## foo[] = 42 + ## var bar = create(int, sizeOf int) + ## bar[] = 42 + ## var baz = create(int, sizeOf int) + ## baz[] = 42 + ## + ## expandMacros: + ## creates "", foo, bar, baz + ## + ## var foo = create(string, sizeOf string) + ## foo[] = "" + ## var bar = create(string, sizeOf string) + ## bar[] = "" + ## var baz = create(string, sizeOf string) + ## baz[] = "" + ## + ## https://nim-lang.github.io/Nim/system.html#create%2Ctypedesc + result = newStmtList() # Creates a list of lines of code to be run. + for it in variables: result.add quote do: # Fills the list with code. + var `it` = create(type(`value`), sizeOf type(`value`)) + `it`[] = `value` diff --git a/nimwcpkg/webs/html_utils.nim b/nimwcpkg/webs/html_utils.nim index d0bfd5e6a..f5cb350ab 100644 --- a/nimwcpkg/webs/html_utils.nim +++ b/nimwcpkg/webs/html_utils.nim @@ -1,4 +1,4 @@ -import strutils +import strutils, times from strtabs import newStringTable, modeStyleInsensitive from packages/docutils/rstgen import rstToHtml @@ -30,14 +30,70 @@ template statusIntToText*(status: string): string = template statusIntToCheckbox*(status, value: string): string = ## When parsing DB status convert to HTML selected on selects - if status == "0" and value == "0": - "selected" - elif status == "1" and value == "1": - "selected" - elif status == "2" and value == "2": - "selected" + if status == "0" and value == "0": "selected" + elif status == "1" and value == "1": "selected" + elif status == "2" and value == "2": "selected" + else: "" + + +template currentDatetime*(formatting: string): string = + ## Getting the current local time + case formatting + of "full": format(local(getTime()), "yyyy-MM-dd HH:mm:ss") + of "date": format(local(getTime()), "yyyy-MM-dd") + of "year": format(local(getTime()), "yyyy") + of "month": format(local(getTime()), "MM") + of "day": format(local(getTime()), "dd") + of "time": format(local(getTime()), "HH:mm:ss") + else: format(local(getTime()), "yyyyMMdd") + + +template getDaysInMonthU*(month: range[1..12], year: Positive): int = + ## Gets the number of days in the month and year + getDaysInMonth(Month(month), year) + + +template dateEpoch*(date, format: string): int = + ## Transform a date in user format to epoch. Does not utilize timezone. + assert date.len > 0, "date must not be empty string" + assert format.len > 0, "format must not be empty string" + assert format in ["YYYYMMDD", "YYYY-MM-DD", "YYYY-MM-DD HH:mm", "DD-MM-YYYY"] + case format + of "YYYYMMDD": toUnix(toTime(parse(date, "yyyyMMdd"))).int + of "YYYY-MM-DD": toUnix(toTime(parse(date, "yyyy-MM-dd"))).int + of "YYYY-MM-DD HH:mm": toUnix(toTime(parse(date, "yyyy-MM-dd HH:mm"))).int + of "DD-MM-YYYY": toUnix(toTime(parse(date, "dd-MM-yyyy"))).int + else: 0 + + +template epochDate*(epochTime, format: string, timeZone: Natural = 0): string = + ## Transform epoch to user formatted date + assert epochTime.len > 0, "epochTime must not be empty string" + const monthNames = [ + "", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", + ] + let t = $(utc(fromUnix(parseInt(epochTime))) + initTimeInterval(hours = timeZone)) + case format + of "YYYY": + t.substr(0, 3) + of "YYYY_MM_DD-HH_mm": + t.substr(0, 3) & "_" & t.substr(5, 6) & "_" & t.substr(8, 9) & "-" & t.substr(11, 12) & "_" & t.substr(14, 15) + of "YYYY MM DD": + t.substr(0, 3) & " " & t.substr(5, 6) & " " & t.substr(8, 9) + of "YYYY-MM-DD": + t.substr(0, 9) + of "YYYY-MM-DD HH:mm": + t.substr(0, 9) & " - " & t.substr(11, 15) + of "DD MM YYYY": + t.substr(8, 9) & " " & t.substr(5, 6) & " " & t.substr(0, 3) + of "DD": + t.substr(8, 9) + of "MMM DD": + monthNames[parseInt(t.substr(5, 6))] & " " & t.substr(8, 9) + of "MMM": + monthNames[parseInt(t.substr(5, 6))] else: - "" + t.substr(0, 9) template inputNumberHtml*(value="", name="", class="input", id="", placeholder="0", required=true, min:byte=0.byte, max:int=byte.high.int, maxlenght=3): string = diff --git a/nimwcpkg/webs/routes.nim b/nimwcpkg/webs/routes.nim index 7c882e469..5a9302587 100644 --- a/nimwcpkg/webs/routes.nim +++ b/nimwcpkg/webs/routes.nim @@ -635,7 +635,7 @@ routes: salt = makeSalt() passwordOriginal = $rand(10_00_00_00_00_01.int..89_99_99_99_99_98.int) # User Must change it anyways. password = makePassword(passwordOriginal, salt) - secretUrl = repeat($rand(10_00_00_00_00_00_00_00_00.int..int.high), 5).center(99, rand(toSeq('a'..'z'))) + secretUrl = repeat($rand(10_00_00_00_00_00_00_00_00.int..int.high), 5).center(99, rand(toSeq('a'..'z'))) # rand is NOT Deprecated, got un-deprecated on devel. let userID = insertID(db, sql"INSERT INTO person (name, email, status, password, salt, secretUrl) VALUES (?, ?, ?, ?, ?, ?)", @"name", emailReady, @"status", password, salt, secretUrl) asyncCheck sendEmailActivationManual(emailReady, @"name", passwordOriginal, "/users/activate?id=" & $userID & "&ident=" & secretUrl, c.username) redirect("/users")