├── .github ├── SECURITY.md └── workflows │ ├── build.yml │ ├── codeql-analysis.yml │ ├── container.yml │ ├── coverity.yml │ └── release.yml ├── .gitignore ├── ChangeLog.md ├── Dockerfile ├── LICENSE ├── Makefile.am ├── README.md ├── autogen.sh ├── build.sh ├── configure.ac ├── doc ├── README.thttpd ├── TODO └── cgi.txt ├── lib ├── .gitignore ├── lstat.c ├── malloc.c ├── realloc.c ├── strlcat.c ├── strlcpy.c └── tempfile.c ├── man.sh ├── man ├── Makefile.am ├── htpasswd.1 ├── merecat.8 ├── merecat.conf.5 └── ssi.8 ├── merecat.conf ├── merecat.service.in ├── run.sh ├── src ├── .gitignore ├── Makefile.am ├── base64.c ├── base64.h ├── conf.c ├── conf.h ├── fdwatch.c ├── fdwatch.h ├── file.c ├── file.h ├── htpasswd.c ├── libhttpd.c ├── libhttpd.h ├── make_mime.pl ├── match.c ├── match.h ├── md5.c ├── md5.h ├── merecat.c ├── merecat.h ├── mime_encodings.h ├── mime_encodings.txt ├── mime_types.h ├── mime_types.txt ├── mmc.c ├── mmc.h ├── pidfile.c ├── srv.c ├── srv.h ├── ssl.c ├── ssl.h ├── stack.c ├── tdate_parse.c ├── tdate_parse.h ├── timers.c └── timers.h ├── tests ├── .gitignore ├── Makefile.am ├── cgi.sh ├── gzip.sh ├── location.sh ├── merecat.conf ├── php.sh ├── redirect.sh ├── start.sh └── stop.sh ├── throttle.conf └── www ├── .gitignore ├── Makefile.am ├── cgi-bin ├── .gitignore ├── Makefile.am ├── onboard.py ├── printenv └── ssi.c ├── footer.html ├── header.html ├── htpasswd.1.html ├── icons ├── Makefile.am ├── back.gif ├── binary.gif ├── binhex.gif ├── blank.gif ├── comp.gray.gif ├── compressed.gif ├── favicon.ico ├── folder.gif ├── folder.open.gif ├── forward.gif ├── generic.gif ├── image.gif ├── index.gif ├── movie.gif ├── text.gif ├── transfer.gif ├── unknown.gif └── uuencoded.gif ├── img ├── Makefile.am └── merecat.jpg ├── index.html ├── main.css ├── merecat.8.html ├── merecat.conf.5.html ├── onboard ├── Makefile.am ├── file │ └── FYI.text └── form.html ├── phpinfo.php ├── ssi.8.html └── test.php /.github/SECURITY.md: -------------------------------------------------------------------------------- 1 | Security Policy 2 | =============== 3 | 4 | Supported Versions 5 | ------------------ 6 | 7 | Merecat httpd is a small project, as such we have no possibility to 8 | support older versions. The only supported version is the latest 9 | released on GitHub: 10 | 11 | 12 | 13 | 14 | Reporting a Vulnerability 15 | ------------------------- 16 | 17 | Contact the project's main author and owner to report and discuss 18 | vulnerabilities. See the [README][] in the projects top directory, 19 | also part of the distribution archive. 20 | 21 | [README]: https://github.com/troglobit/merecat/blob/master/README.md 22 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Bob the Builder 2 | 3 | # Run on all branches, including all pull requests, except the 'dev' 4 | # branch since that's where we run Coverity Scan (limited tokens/day) 5 | on: 6 | push: 7 | branches: 8 | - '**' 9 | - '!dev' 10 | pull_request: 11 | branches: 12 | - '**' 13 | 14 | jobs: 15 | build: 16 | # Verify we can build on latest Ubuntu with both gcc and clang 17 | name: ${{ matrix.compiler }} 18 | runs-on: ubuntu-latest 19 | strategy: 20 | matrix: 21 | compiler: [gcc, clang] 22 | fail-fast: false 23 | env: 24 | MAKEFLAGS: -j3 25 | CC: ${{ matrix.compiler }} 26 | steps: 27 | - name: Install dependencies 28 | run: | 29 | sudo apt-get -y update 30 | sudo apt-get -y install tree libconfuse-dev libssl-dev zlib1g-dev php-cgi 31 | - uses: actions/checkout@v2 32 | - name: Create configure script 33 | run: | 34 | ./autogen.sh 35 | - name: Build w/o HTTPS 36 | run: | 37 | ./configure --prefix=/ --without-ssl --enable-htaccess --enable-htpasswd 38 | make V=1 39 | - name: Build w/ HTTPS 40 | run: | 41 | make clean 42 | ./configure --prefix=/ --enable-htaccess --enable-htpasswd 43 | make V=1 44 | - name: Install to ~/tmp and Inspect 45 | run: | 46 | DESTDIR=~/tmp make install-strip 47 | tree ~/tmp 48 | ldd ~/tmp/sbin/merecat 49 | size ~/tmp/sbin/merecat 50 | ~/tmp/sbin/merecat -h 51 | - name: Run tests 52 | run: | 53 | # Tests must currently not run in parallel 54 | make -j1 check || (cat tests/test-suite.log; false) 55 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ master ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ master ] 20 | schedule: 21 | - cron: '32 0 * * 2' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | permissions: 28 | actions: read 29 | contents: read 30 | security-events: write 31 | 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | language: [ 'cpp' ] 36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] 37 | # Learn more about CodeQL language support at https://git.io/codeql-language-support 38 | 39 | steps: 40 | - name: Checkout repository 41 | uses: actions/checkout@v2 42 | 43 | - name: Install dependencies 44 | run: | 45 | sudo apt-get -y update 46 | sudo apt-get -y install tree libconfuse-dev libssl-dev zlib1g-dev php-cgi 47 | 48 | - name: Initialize CodeQL 49 | uses: github/codeql-action/init@v1 50 | with: 51 | languages: ${{ matrix.language }} 52 | # If you wish to specify custom queries, you can do so here or in a config file. 53 | # By default, queries listed here will override any specified in a config file. 54 | # Prefix the list here with "+" to use these queries and those in the config file. 55 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 56 | 57 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 58 | # If this step fails, then you should remove it and run the build manually (see below) 59 | - name: Autobuild 60 | uses: github/codeql-action/autobuild@v1 61 | 62 | # ℹ️ Command-line programs to run using the OS shell. 63 | # 📚 https://git.io/JvXDl 64 | 65 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 66 | # and modify them (or add more) to build your code if your project 67 | # uses a compiled language 68 | 69 | #- run: | 70 | # make bootstrap 71 | # make release 72 | 73 | - name: Perform CodeQL Analysis 74 | uses: github/codeql-action/analyze@v1 75 | -------------------------------------------------------------------------------- /.github/workflows/container.yml: -------------------------------------------------------------------------------- 1 | name: Container Claus 2 | 3 | on: 4 | push: 5 | branches: 6 | - 'master' 7 | tags: 8 | - 'v[0-9]+.[0-9]+*' 9 | 10 | jobs: 11 | docker: 12 | runs-on: ubuntu-latest 13 | permissions: 14 | packages: write 15 | contents: read 16 | env: 17 | MAKEFLAGS: -j3 18 | IMAGE_NAME: merecat 19 | steps: 20 | - uses: actions/checkout@v2 21 | - name: Build image 22 | run: docker build . --file Dockerfile --tag $IMAGE_NAME --label "runnumber=${GITHUB_RUN_ID}" 23 | - name: Log in to registry 24 | run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin 25 | - name: Push image 26 | run: | 27 | IMAGE_ID=ghcr.io/${{ github.repository_owner }}/$IMAGE_NAME 28 | # Change all uppercase to lowercase 29 | IMAGE_ID=$(echo $IMAGE_ID | tr '[A-Z]' '[a-z]') 30 | # Strip git ref prefix from version 31 | VERSION=$(echo "${{ github.ref }}" | sed -e 's,.*/\(.*\),\1,') 32 | # Use Docker `latest` tag convention 33 | [ "$VERSION" == "master" ] && VERSION=latest 34 | echo IMAGE_ID=$IMAGE_ID 35 | echo VERSION=$VERSION 36 | docker tag $IMAGE_NAME $IMAGE_ID:$VERSION 37 | docker push $IMAGE_ID:$VERSION 38 | -------------------------------------------------------------------------------- /.github/workflows/coverity.yml: -------------------------------------------------------------------------------- 1 | name: Coverity Scan 2 | 3 | on: 4 | push: 5 | branches: 6 | - 'dev' 7 | 8 | env: 9 | PROJECT_NAME: merecat 10 | CONTACT_EMAIL: troglobit@gmail.com 11 | COVERITY_NAME: troglobit-merecat 12 | COVERITY_PROJ: troglobit%2Fmerecat 13 | 14 | jobs: 15 | coverity: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/checkout@v2 19 | - name: Fetch latest Coverity Scan MD5 20 | id: var 21 | env: 22 | TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }} 23 | run: | 24 | wget -q https://scan.coverity.com/download/cxx/linux64 \ 25 | --post-data "token=$TOKEN&project=${COVERITY_PROJ}&md5=1" \ 26 | -O coverity-latest.tar.gz.md5 27 | export MD5=$(cat coverity-latest.tar.gz.md5) 28 | echo "Got MD5 $MD5" 29 | echo ::set-output name=md5::${MD5} 30 | - uses: actions/cache@v2 31 | id: cache 32 | with: 33 | path: coverity-latest.tar.gz 34 | key: ${{ runner.os }}-coverity-${{ steps.var.outputs.md5 }} 35 | restore-keys: | 36 | ${{ runner.os }}-coverity-${{ steps.var.outputs.md5 }} 37 | ${{ runner.os }}-coverity- 38 | ${{ runner.os }}-coverity 39 | - name: Download Coverity Scan 40 | env: 41 | TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }} 42 | run: | 43 | if [ ! -f coverity-latest.tar.gz ]; then 44 | wget -q https://scan.coverity.com/download/cxx/linux64 \ 45 | --post-data "token=$TOKEN&project=${COVERITY_PROJ}" \ 46 | -O coverity-latest.tar.gz 47 | else 48 | echo "Latest Coverity Scan available from cache :-)" 49 | md5sum coverity-latest.tar.gz 50 | fi 51 | mkdir coverity 52 | tar xzf coverity-latest.tar.gz --strip 1 -C coverity 53 | - name: Install dependencies 54 | run: | 55 | sudo apt-get -y update 56 | sudo apt-get -y install tree libconfuse-dev libssl-dev zlib1g-dev php-cgi 57 | - name: Configure 58 | run: | 59 | ./autogen.sh 60 | ./configure 61 | - name: Build 62 | run: | 63 | export PATH=`pwd`/coverity/bin:$PATH 64 | cov-build --dir cov-int make 65 | - name: Submit results to Coverity Scan 66 | env: 67 | TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }} 68 | run: | 69 | tar czvf ${PROJECT_NAME}.tgz cov-int 70 | curl \ 71 | --form project=${COVERITY_NAME} \ 72 | --form token=$TOKEN \ 73 | --form email=${CONTACT_EMAIL} \ 74 | --form file=@${PROJECT_NAME}.tgz \ 75 | --form version=trunk \ 76 | --form description="${PROJECT_NAME} $(git rev-parse HEAD)" \ 77 | https://scan.coverity.com/builds?project=${COVERITY_PROJ} 78 | - name: Upload build.log 79 | uses: actions/upload-artifact@v2 80 | with: 81 | name: coverity-build.log 82 | path: cov-int/build-log.txt 83 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release General 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v[0-9]+.[0-9]+*' 7 | 8 | jobs: 9 | release: 10 | name: Create GitHub release 11 | runs-on: ubuntu-latest 12 | if: startsWith(github.ref, 'refs/tags/') 13 | outputs: 14 | upload_url: ${{ steps.create_release.outputs.upload_url }} 15 | release_id: ${{ steps.create_release.outputs.id }} 16 | steps: 17 | - uses: actions/checkout@v2 18 | - name: Extract ChangeLog entry ... 19 | # Hack to extract latest entry for body_path below 20 | run: | 21 | awk '/-----*/{if (x == 1) exit; x=1;next}x' ChangeLog.md \ 22 | |head -n -1 > release.md 23 | cat release.md 24 | - name: Create release ... 25 | id: create_release 26 | uses: actions/create-release@v1 27 | env: 28 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 29 | with: 30 | tag_name: ${{ github.ref }} 31 | release_name: Merecat httpd ${{ github.ref }} 32 | body_path: release.md 33 | draft: false 34 | prerelease: false 35 | tarball: 36 | name: Build and upload release tarball 37 | needs: release 38 | if: startsWith(github.ref, 'refs/tags/') 39 | runs-on: ubuntu-latest 40 | steps: 41 | - uses: actions/checkout@v2 42 | - name: Install dependencies ... 43 | run: | 44 | sudo apt-get -y update 45 | sudo apt-get -y install tree libconfuse-dev libssl-dev zlib1g-dev php-cgi 46 | - name: Creating Makefiles ... 47 | run: | 48 | ./autogen.sh 49 | ./configure 50 | - name: Build release ... 51 | run: | 52 | make release 53 | mkdir -p artifacts/ 54 | mv ../*.tar.* artifacts/ 55 | - name: Upload release artifacts ... 56 | uses: skx/github-action-publish-binaries@release-0.15 57 | env: 58 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 59 | with: 60 | releaseId: ${{ needs.release.outputs.release_id }} 61 | args: artifacts/* 62 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.o 3 | .config 4 | .unpacked 5 | .gdb_history 6 | GPATH 7 | GRTAGS 8 | GSYMS 9 | GTAGS 10 | aclocal.m4 11 | autom4te.cache 12 | compile 13 | config.h 14 | config.h.in 15 | config.log 16 | config.status 17 | configure 18 | depcomp 19 | install-sh 20 | merecat.service 21 | missing 22 | stamp-h1 23 | test-driver 24 | Makefile 25 | Makefile.in 26 | -------------------------------------------------------------------------------- /ChangeLog.md: -------------------------------------------------------------------------------- 1 | Change Log 2 | ========== 3 | 4 | All relevant changes are documented in this file. 5 | 6 | 7 | [v2.32][UNRELEASED] 8 | ------------------- 9 | 10 | Notable new features: multiple server support from one process, HTTPS, 11 | HTTP/1.1 keep-alive, and built-in gzip deflate compression using zlib. 12 | 13 | ### Changes 14 | - Add support for HTTPS, works with certificates from Let's Encrypt 15 | - Add support for multiple servers, listen to different ports 16 | - Add support for built-in HTTP redirect, e.g. from HTTP to HTTPS 17 | - Add support for server location directive, similar to nginx but with 18 | security limitations and native vhost support native to thttpd 19 | - Add gzip deflate compression when built with zlib, also compress 20 | HEAD as well as GET requests 21 | - Add true `Connection: keep-alive` support 22 | - Add missing `Vary: Accept-Encoding` header 23 | - CGI: Allow handling other HTTP methods besides GET/HEAD/POST, from 24 | thttpd v2.29, change by Jef Poskanzer 25 | - CGI: Allow `:PORT` in `HTTP_POST`, like Apache 26 | - CGI: Allow trailing slash in `PATH_INFO`, like Apache 27 | - CGI: Change default `CGI_PATTERN` from disabled to `**.cgi|/cgi-bin/*` 28 | - CGI: Add support for looking for an `index.cgi` index file 29 | - CGI: Add several missing standard CGI/1.1 environment variables, see 30 | the file doc/cgi.txt for details 31 | - PHP: 32 | - Add support for `php-cgi` and `index.php` index file 33 | - Add support for PHP pattern matching, run php-cgi if `**.php` 34 | - Server-Side Includes (SSI): 35 | - Add support for SSI pattern matching, run cgi-bin/ssi if `**.shtml` 36 | - Add support for silencing default SSI `errmsg` 37 | - Add support for looking for `index.shtml` index file 38 | - Dot files are no longer shown in dir listings, use the `merecat.conf` 39 | setting `list-dotfiles = true` to enable 40 | - Server stats are no longer periodically sent to syslog, re-enable in 41 | `merecat.conf` if you need the `STATS_TIME` feature 42 | - Apply Debian thttpd `SIGBUS` patch for reading from NFS 43 | - Add `-I IDENT` command line option to override program identity. 44 | This change makes it possible to change syslog, PID file name, *and* 45 | `.conf` file name. Useful when running multiple instances of Merecat 46 | - Add `--enable-msie-padding` to `configure` script 47 | - Add `.htaccess` support, limited to IPv4. Feature by Felix J. Ogris 48 | - Allow `.htpasswd` file to be symlinked 49 | - DOC: How to use `.htpasswd` and virtual hosts 50 | - DOC: Added section on how to optimize performance 51 | - Update MIME types, e.g. Ogg video, 7zip, svg 52 | - Add Dockerfile for ease of deployment in limited setups 53 | - Add cute cat default favicon 54 | - Built-in icons for FTP dir listings; folder, file, etc. 55 | - Refactor, deprecated POSIX API's, e.g. `bzero() --> memset()` 56 | - Enable `SO_REUSEPORT` if available, useful for load balancing 57 | 58 | ### Fixes 59 | - Fix CVE-2017-17663, buffer overrun in htpasswd tool, from thttpd v2.28 60 | - Fixes for non GNU C libraries like musl: `__progname`, `%m`, etc. 61 | - Fix `X-Forwarded-For` when using IPv6, thanks to Steve Kemp! 62 | - Debian packaging fixes 63 | - Make sure both `.htpasswd` *and* `.htaccess` are declared forbidden 64 | files and not allowed to be downloaded or shown in directory listings 65 | - Use `memmove()` instead of `strcpy()` for possibly overlapping regions 66 | - Cleanup of default `merecat.conf`, default disabled options to their 67 | built-in default values 68 | - Spelling fixes and major documentation cleanup 69 | 70 | 71 | [v2.31][] - 2016-11-06 72 | ---------------------- 73 | 74 | The "it works now" release. 75 | 76 | ### Changes 77 | - Sort directories first in dir listings 78 | - Include systemd unit file 79 | - Add `debian/` packaging, easy to rebuild and replace for others 80 | - Add `--enable-public-html` to enable `~user/public_html` dirs 81 | - Support for shared `WEBROOT/cgi-bin` as fallback for vhosts 82 | - Update default landing page 83 | 84 | ### Fixes 85 | - Add missing CSS and jpeg files to install 86 | - Fix dependency tracking when reconfiguring 87 | - Fix `.conf` file parser bugs reported by Gaetan Bisson 88 | - Fix missing `HAVE_LIBCONFUSE` #define causing `.conf` file support to 89 | not be built, reported by Gaetan Bisson 90 | - Fix malplaced call to `cfg_free()` in .conf file parser, reported by 91 | Gaetan Bisson 92 | - Update man page and other documentation with missing quotes around CGI 93 | pattern, issue reported by Gaetan Bisson 94 | - Fix syslog warning: bind 0.0.0.0: Address already in use 95 | 96 | 97 | [v2.30][] - 2016-10-09 98 | ---------------------- 99 | 100 | Initial release. Based on [sthttpd][] master, 2015-07-22. 101 | 102 | [UNRELEASED]: https://github.com/troglobit/merecat/compare/v2.31...HEAD 103 | [v2.32]: https://github.com/troglobit/merecat/compare/v2.31...v2.32 104 | [v2.31]: https://github.com/troglobit/merecat/compare/v2.30...v2.31 105 | [v2.30]: https://github.com/troglobit/merecat/compare/v2.29...v2.30 106 | [sthttpd]: https://github.com/blueness/sthttpd/ 107 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:3.6 2 | 3 | # Build depends 4 | RUN apk add --no-cache gcc musl-dev make automake autoconf zlib-dev 5 | 6 | # Install from GIT 7 | WORKDIR . 8 | ADD . /merecat 9 | RUN cd merecat/; ./build.sh; make install-strip; cd ..; rm -rf merecat 10 | 11 | # Alternatively, install from released tarball 12 | #RUN wget https://ftp.troglobit.com/merecat/merecat-2.32.tar.xz; \ 13 | # tar xf merecat-2.32.tar.bz2; \ 14 | # cd merecat-2.32/; \ 15 | # ./build.sh; \ 16 | # make install-strip 17 | 18 | # Clean up container 19 | # m4 perl binutils binutils-libs bmp isl libgomp libatomic pkgconf 20 | RUN apk del --purge gcc musl-dev make automake autoconf zlib-dev 21 | 22 | EXPOSE 80 23 | VOLUME /var/www 24 | ENTRYPOINT merecat -p 80 -n /var/www 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 1995-2015 Jef Poskanzer 2 | Copyright (C) 2016-2021 Joachim Wiberg 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions 7 | are met: 8 | 1. Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 2. Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE 18 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 19 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 20 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 21 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 22 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 24 | THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = src www man tests 2 | doc_DATA = README.md LICENSE ChangeLog.md 3 | EXTRA_DIST = README.md LICENSE ChangeLog.md throttle.conf merecat.conf 4 | DISTCHECK_CONFIGURE_FLAGS = --with-systemd=$$dc_install_base/$(systemd) 5 | 6 | if HAVE_CONFUSE 7 | dist_sysconf_DATA = merecat.conf 8 | endif 9 | 10 | if HAVE_SYSTEMD 11 | systemd_DATA = merecat.service 12 | endif 13 | 14 | ## Update html versions of man pages 15 | doc: 16 | @for file in htpasswd.1 merecat.8 merecat.conf.5 ssi.8; do \ 17 | out=www/$$file.html; \ 18 | sed "s/%TITLE%/$$file/" < www/header.html > $$out; \ 19 | mandoc -T html -O fragment -O man=%N.%S.html \ 20 | man/$$file >> $$out; \ 21 | cat www/footer.html >> $$out; \ 22 | done 23 | 24 | ## Generate MD5 checksum file 25 | MD5 = md5sum 26 | md5-dist: 27 | @for file in $(DIST_ARCHIVES); do \ 28 | $(MD5) $$file > ../$$file.md5; \ 29 | mv $$file ../; \ 30 | done 31 | 32 | ## Check if tagged in git 33 | release-hook: 34 | if [ ! `git tag | grep $(PACKAGE_VERSION)` ]; then \ 35 | echo; \ 36 | printf "\e[1m\e[41mCannot find release tag $(PACKAGE_VERSION)\e[0m\n"; \ 37 | printf "\e[1m\e[5mDo release anyway?\e[0m "; read yorn; \ 38 | if [ "$$yorn" != "y" -a "$$yorn" != "Y" ]; then \ 39 | printf "OK, aborting release.\n"; \ 40 | exit 1; \ 41 | fi; \ 42 | echo; \ 43 | else \ 44 | echo; \ 45 | printf "\e[1m\e[42mFound GIT release tag $(PACKAGE_VERSION)\e[0m\n"; \ 46 | printf "\e[1m\e[44m>>Remember to push tags!\e[0m\n"; \ 47 | echo; \ 48 | fi 49 | 50 | # Target to run when building a release 51 | release: distcheck release-hook md5-dist 52 | @echo 53 | @echo "Resulting release files:" 54 | @echo "=================================================================" 55 | @for file in $(DIST_ARCHIVES); do \ 56 | printf "$$file \tDistribution tarball\n"; \ 57 | printf "$$file.md5\t"; cat ../$$file.md5 | cut -f1 -d' '; \ 58 | done 59 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | autoreconf -W portability -visfm 4 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | if [ ! -x configure ]; then 2 | ./autogen.sh 3 | fi 4 | 5 | ./configure --prefix=/usr --localstatedir=/var --sysconfdir=/etc --enable-builtin-icons \ 6 | --without-config --without-ssl --without-symlinks --enable-htaccess --enable-htpasswd 7 | 8 | make -j5 clean 9 | make -j5 10 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | AC_PREREQ([2.68]) 2 | AC_INIT([Merecat httpd], [2.32-rc4], [https://github.com/troglobit/merecat/issues], [merecat]) 3 | AC_CONFIG_AUX_DIR(aux) 4 | AM_INIT_AUTOMAKE([1.11 foreign dist-xz]) 5 | AM_SILENT_RULES([yes]) 6 | 7 | AC_CONFIG_SRCDIR([src/libhttpd.c]) 8 | AC_CONFIG_HEADERS([config.h]) 9 | AC_CONFIG_FILES([Makefile 10 | merecat.service 11 | src/Makefile 12 | man/Makefile 13 | tests/Makefile 14 | www/Makefile 15 | www/cgi-bin/Makefile 16 | www/icons/Makefile 17 | www/img/Makefile 18 | www/onboard/Makefile]) 19 | 20 | # Use pkg-config to check for some deps; libConfuse, libssl 21 | PKG_PROG_PKG_CONFIG 22 | 23 | # Checks for programs. 24 | AC_PROG_CC 25 | AC_PROG_LN_S 26 | AC_PROG_INSTALL 27 | AC_PROG_MAKE_SET 28 | AC_PROG_RANLIB 29 | m4_ifdef([AM_PROG_AR], [AM_PROG_AR]) 30 | 31 | # Checks for libraries. 32 | AC_CHECK_LIB(crypt, crypt) 33 | AC_CHECK_LIB(rt, clock_gettime) 34 | AC_CHECK_LIB(resolv, hstrerror) 35 | 36 | # Checks for header files. 37 | AC_CHECK_HEADERS([arpa/inet.h fcntl.h grp.h memory.h netdb.h netinet/in.h osreldate.h paths.h poll.h stddef.h stdlib.h string.h termios.h sys/devpoll.h sys/event.h sys/param.h sys/poll.h sys/socket.h sys/time.h syslog.h unistd.h]) 38 | AC_CHECK_HEADER_STDBOOL 39 | AC_HEADER_TIME 40 | AC_HEADER_DIRENT 41 | 42 | AC_C_INLINE 43 | 44 | # Checks for typedefs, structures, and compiler characteristics. 45 | AC_TYPE_UID_T 46 | AC_TYPE_UINT8_T 47 | AC_TYPE_UINT16_T 48 | AC_TYPE_INT64_T 49 | AC_TYPE_MODE_T 50 | AC_TYPE_OFF_T 51 | AC_TYPE_PID_T 52 | AC_TYPE_SIZE_T 53 | AC_TYPE_SSIZE_T 54 | 55 | # Check for missing/broken API's, which we can replace, e.g. lib/lstat.c 56 | AC_REPLACE_FUNCS([strlcpy strlcat tempfile]) 57 | AC_CONFIG_LIBOBJ_DIR([lib]) 58 | 59 | # Checks for library functions. 60 | AC_FUNC_CHOWN 61 | AC_FUNC_FORK 62 | AC_FUNC_MMAP 63 | AC_FUNC_MALLOC 64 | AC_FUNC_REALLOC 65 | AC_FUNC_LSTAT_FOLLOWS_SLASHED_SYMLINK 66 | AC_FUNC_WAIT3 67 | AC_CHECK_FUNCS([alarm atexit atoll backtrace clock_gettime daemon dup2 gai_strerror getcwd getaddrinfo gethostbyname gethostname getnameinfo getpass gettimeofday hstrerror inet_ntoa isascii kqueue malloc memmove memset mkdir mmap munmap poll realpath select setenv setlogin setsid sigaction socket strcasecmp strchr strcspn strdup strerror strncasecmp strpbrk strrchr strspn strstr strtoul snprintf tzset waitpid]) 68 | 69 | # Check for command line options 70 | AC_ARG_ENABLE(builtin-icons, 71 | AS_HELP_STRING([--enable-builtin-icons], [Enable built-in icons for dir listings])) 72 | 73 | AC_ARG_ENABLE(public-html, 74 | AS_HELP_STRING([--enable-public-html], [Enable ~user/public_html in non-chrooted setups])) 75 | 76 | AC_ARG_ENABLE(htaccess, 77 | AS_HELP_STRING([--enable-htaccess], [Enable .htaccess files for access control])) 78 | 79 | AC_ARG_ENABLE(htpasswd, 80 | AS_HELP_STRING([--enable-htpasswd], [Enable .htpasswd files for authentication])) 81 | 82 | AC_ARG_ENABLE(msie-padding, 83 | AS_HELP_STRING([--enable-msie-padding], [Add padding to error messages for Internet Explorer])) 84 | 85 | AC_ARG_ENABLE(dirlisting, 86 | AS_HELP_STRING([--disable-dirlisting], [Disable directory listings when no index file is found]),,[ 87 | enable_dirlisting=yes]) 88 | 89 | AC_ARG_WITH([systemd], 90 | [AS_HELP_STRING([--with-systemd=DIR], [Directory for systemd service files, default: auto])],, 91 | [with_systemd=auto]) 92 | 93 | AC_ARG_WITH([config], 94 | AS_HELP_STRING([--without-config], [Disable /etc/merecat.conf support using libConfuse]),, 95 | [with_config=yes]) 96 | 97 | AC_ARG_WITH(ssl, 98 | AS_HELP_STRING([--without-ssl], [Disable HTTPS support, default: enabled]),, 99 | [with_ssl=yes]) 100 | 101 | AC_ARG_WITH([symlinks], 102 | AS_HELP_STRING([--without-symlinks], [Disable httpd and in.httpd symlinks to merecat]),, 103 | [with_symlinks=yes]) 104 | 105 | AC_ARG_WITH([zlib], 106 | AS_HELP_STRING([--without-zlib], [Disable mod_deflate (gzip) using zlib]),, 107 | [with_zlib=auto]) 108 | 109 | AS_IF([test "x$enable_builtin_icons" = "xyes"], [ 110 | AC_DEFINE(BUILTIN_ICONS, [1], [Enables built-in icons for dir listings])]) 111 | AM_CONDITIONAL([HAVE_ICONS], [test "x$enable_builtin_icons" != "xyes"]) 112 | 113 | AS_IF([test "x$enable_public_html" = "xyes"], [ 114 | AC_DEFINE_UNQUOTED(TILDE_MAP_2, "public_html", [Enables ~user/public_html])]) 115 | 116 | AS_IF([test "x$enable_htaccess" = "xyes"], [ 117 | AC_DEFINE_UNQUOTED(ACCESS_FILE, ".htaccess", [Enables .htaccess access control files])]) 118 | 119 | AS_IF([test "x$enable_htpasswd" = "xyes"], [ 120 | AC_DEFINE_UNQUOTED(AUTH_FILE, ".htpasswd", [Enables .htpasswd auth. files])]) 121 | AM_CONDITIONAL([ENABLE_HTPASSWD], [test "x$enable_htpasswd" = "xyes"]) 122 | 123 | AS_IF([test "x$enable_msie_padding" = "xyes"], [ 124 | AC_DEFINE(MSIE_PADDING, [1], [Enable padding of error messages for IE])]) 125 | 126 | AS_IF([test "x$enable_dirlisting" != "xno"], [ 127 | AC_DEFINE(GENERATE_INDEXES, [1], [Automatically generate directory index when index file is missing])]) 128 | 129 | AC_ARG_VAR(WEBDIR, [Document root of your web server [LOCALSTATEDIR/www]]) 130 | AS_IF([test "x${WEBDIR}" = "x"], [ 131 | WEBDIR=$localstatedir/www]) 132 | 133 | AS_IF([test "x$with_config" != "xno"], [ 134 | AC_DEFINE([HAVE_LIBCONFUSE], [1], [Build with support for /etc/merecat.conf]) 135 | PKG_CHECK_MODULES([confuse], [libconfuse >= 2.7])]) 136 | AM_CONDITIONAL([HAVE_CONFUSE], [test "x$with_config" != "xno"]) 137 | 138 | AM_CONDITIONAL([CREATE_SYMLINKS], [test "x$with_symlinks" != "xno"]) 139 | 140 | AS_IF([test "x$with_ssl" != "xno"], [ 141 | PKG_CHECK_MODULES([OpenSSL], [openssl >= 1.1.1]) 142 | LDFLAGS="$LDFLAGS $OpenSSL_LIBS" 143 | CPPFLAGS="$CPPFLAGS $OpenSSL_CFLAGS" 144 | AC_CHECK_LIB([crypto], [EVP_EncryptInit], [], 145 | AC_MSG_ERROR([*** Crypto library (OpenSSL) not found!])) 146 | AC_CHECK_LIB([ssl], [SSL_library_init], [], 147 | AC_CHECK_LIB([ssl], [OPENSSL_init_ssl], [], 148 | AC_MSG_ERROR([*** SSL library (OpenSSL) not found!]))) 149 | AC_CHECK_HEADERS([openssl/crypto.h openssl/x509.h openssl/pem.h openssl/ssl.h openssl/tls1.h openssl/err.h], [], 150 | AC_MSG_ERROR([*** Cannot find required header files!]), [ 151 | #include 152 | ]) 153 | AC_CHECK_DECLS([SSL_COMP_free_compression_methods,SSL_CTX_set_ecdh_auto], [], [], [ 154 | #ifdef HAVE_OPENSSL_ERR_H 155 | #include 156 | #endif 157 | 158 | #ifdef HAVE_OPENSSL_RAND_H 159 | #include 160 | #endif 161 | 162 | #ifdef HAVE_OPENSSL_CONF_H 163 | #include 164 | #endif 165 | 166 | #ifdef HAVE_OPENSSL_ENGINE_H 167 | #include 168 | #endif 169 | #include 170 | #include 171 | ]) 172 | AC_DEFINE([ENABLE_SSL], [1], [Enable HTTPS support]) 173 | ]) 174 | AM_CONDITIONAL([ENABLE_SSL], test "x$with_ssl" = "xyes") 175 | 176 | AS_IF([test "x$with_zlib" != "xno"], [ 177 | AC_CHECK_HEADERS([zlib.h]) 178 | AS_IF([test "x$ac_cv_header_zlib_h" = "xyes" -o "x$with_zlib" = "xyes"], [ 179 | PKG_CHECK_MODULES([zlib], [zlib >= 1.2.3.4])]) 180 | ]) 181 | 182 | # Check where to install the systemd .service file 183 | AS_IF([test "x$with_systemd" = "xyes" -o "x$with_systemd" = "xauto"], [ 184 | def_systemd=$($PKG_CONFIG --variable=systemdsystemunitdir systemd) 185 | AS_IF([test "x$def_systemd" = "x"], 186 | [AS_IF([test "x$with_systemd" = "xyes"], 187 | [AC_MSG_ERROR([systemd support requested but pkg-config unable to query systemd package])]) 188 | with_systemd=no], [with_systemd="$def_systemd"])] 189 | ) 190 | AS_IF([test "x$with_systemd" != "xno"], 191 | [AC_SUBST([systemddir], [$with_systemd])]) 192 | AM_CONDITIONAL([HAVE_SYSTEMD], [test "x$with_systemd" != "xno"]) 193 | 194 | # Expand $sbindir early, into $SBINDIR, for systemd unit file 195 | # NOTE: This does *not* take prefix/exec_prefix override at "make 196 | # install" into account, unfortunately. 197 | test "x$prefix" = xNONE && prefix= 198 | test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' 199 | DOCDIR=`eval echo $docdir` 200 | DOCDIR=`eval echo $DOCDIR` 201 | AC_SUBST(DOCDIR) 202 | SBINDIR=`eval echo $sbindir` 203 | SBINDIR=`eval echo $SBINDIR` 204 | AC_SUBST(SBINDIR) 205 | WWWDIR=`eval echo $WEBDIR` 206 | AC_SUBST(WWWDIR) 207 | 208 | AC_OUTPUT 209 | -------------------------------------------------------------------------------- /doc/README.thttpd: -------------------------------------------------------------------------------- 1 | thttpd - tiny/turbo/throttling HTTP server 2 | version 2.25b of 29dec2003 3 | 4 | thttpd is a simple, small, portable, fast, and secure HTTP server. 5 | 6 | Simple: It handles only the minimum necessary to implement HTTP/1.1. 7 | 8 | Small: See the size comparison chart at 9 | http://www.acme.com/software/thttpd/notes.html#sizes. It also has a 10 | very small run-time size, since it does not fork and is very careful about 11 | memory allocation. 12 | 13 | Portable: It compiles cleanly on FreeBSD 2.x/3.x, SunOS 4.1.x, Solaris 2.x, 14 | BSD/OS 2.x, Linux 1.2.x, OSF/1 (on a 64-bit Alpha), and no doubt many others. 15 | 16 | Fast: In typical use it's about as fast as the best full-featured servers 17 | (Apache, NCSA, Netscape). Under extreme load it's much faster. 18 | 19 | Secure: It goes to great lengths to protect the web server machine 20 | against attacks and breakins from other sites. 21 | 22 | It also has one extremely useful feature (URL-traffic-based throttling) that 23 | no other server currently has. 24 | 25 | See the manual entry for more details. See the INSTALL file for 26 | configuration and installation instructions. Check the web page 27 | (http://www.acme.com/software/thttpd/) for updates, or add yourself to 28 | the mailing list by sending a "subscribe" to thttpd-announce-request@mail.acme.com. 29 | 30 | Comments to: 31 | Jef Poskanzer jef@mail.acme.com http://www.acme.com/jef/ 32 | -------------------------------------------------------------------------------- /doc/TODO: -------------------------------------------------------------------------------- 1 | - - - - - - - - - - before release - - - - - - - - - - 2 | 3 | - Add 'server' section to config file, 4 | 5 | server title { 6 | host = virual-host.name 7 | port = PORT 8 | ssl = BOOL 9 | redirect = site.tla:port 10 | } 11 | 12 | - Let 'server' directives slowly replace vhost matching 13 | 14 | When virtual-host=false we can use either the title of the server 15 | directive or a separate host/server-name setting for matching the 16 | incoming requests. 17 | 18 | - Add 'location' section to config file 19 | 20 | This to be able to set max-age per PATTERN rather than for all files, 21 | which may otherwise mess up reloading, e.g. a blog. 22 | 23 | location "PATTERN" { 24 | max-age = 2d 25 | add-header { 26 | key = "Cache-Control" 27 | value = "public, no-transform" 28 | } 29 | gzip = true 30 | } 31 | 32 | - Add proxy-pass to location directive 33 | 34 | - Let command line options override port/host etc. that 35 | are in the global section of the config file. 36 | 37 | - Verify cert at startup, abort if cert is too old or new 38 | add command line option to override (embedded systems) 39 | 40 | - - - - - - - - - - high priority - - - - - - - - - - 41 | 42 | Look into compressing CGI and ls() output using fmemopen() 43 | 44 | IPv6 not working right. 45 | 46 | Problem with ACME News downloads. PATH_INFO interferes with the authorization. 47 | 48 | Why is the client's IP address showing up in paths? 49 | 50 | Fetches with numeric IP addresses and no Host: header are screwing up the 51 | vhost code? 52 | 143.90.193.229 - - [06/Apr/2000:09:21:34 -0700] "GET /209.133.38.22/software/thttpd/ HTTP/1.0" 200 12093 "http://www.dbphotography.demon.co.uk/index.html" "Mozilla/1.22 (compatible; MSIE 2.0; Windows 95)" 53 | 143.90.193.229 - - [06/Apr/2000:09:21:37 -0700] "GET /143.90.193.229/software/thttpd/anvil_thttpd.gif HTTP/1.0" 403 - "http://www.acme.com/software/thttpd/" "Mozilla/1.22 (compatible; MSIE 2.0; Windows 95)" 54 | 55 | Add comment on INDEX_NAMES that it should be simple filenames only. 56 | 57 | The error page generated for non-local referers should include the 58 | original URL as an active link. 59 | 60 | Make open in mmc.c use O_NONBLOCK flag, to prevent DOS attack via 61 | a named pipe? 62 | 63 | - - - - - - - - - - later - - - - - - - - - - 64 | 65 | Document how symlinks interact with .htpasswd - authorization is checked 66 | on the result of the symlink, and not the origin. 67 | 68 | Change redirect to put the Refresh command in the HTTP headers, instead of 69 | a META tag. 70 | 71 | Add TCP_NODELAY, but after CGIs get spawned. 72 | 73 | Add stat cache? 1 minute expiry? 74 | 75 | Ifdef the un-close-on-exec CGI thing for Linux only. 76 | 77 | Add keep-alives, via a new state in thttpd.c. 78 | 79 | - - - - - - - - - - someday - - - - - - - - - - 80 | 81 | The special world-permissions checking is probably bogus. For one 82 | thing, it doesn't handle restrictive permissions on parent directories 83 | properly. It should probably just go away. 84 | 85 | redirect should interpret a path with a trailing / as /index.html 86 | 87 | ssi should change $cwd to the source document's location. 88 | 89 | Allow .throttle files in individual directories. 90 | 91 | Log-digesting scripts. 92 | 93 | Config web page. 94 | Common errors: 95 | Not realizing that -c overrides CGI_PATTERN instead of augmenting it. 96 | Using a directory name for the -c pattern. 97 | 98 | - - - - - - - - - - 3.x - - - - - - - - - - 99 | 100 | Tasklets re-write. 101 | 102 | - - - - - - - - - - general - - - - - - - - - - 103 | 104 | Release process: 105 | - update version number in version.h README INSTALL and 106 | contrib/redhat-rpm/thttpd.spec 107 | - do a tdiff and update the local installation 108 | - do an rcstreeinfo, and check in all files 109 | - make tar 110 | - mv it to .. 111 | - update version number in ../thttpd.html 112 | - update ~acmeweb/updates.html 113 | - mail announcement to thttpd-announce 114 | -------------------------------------------------------------------------------- /doc/cgi.txt: -------------------------------------------------------------------------------- 1 | -*-org-*- 2 | * Supported CGI environment variables 3 | 4 | | | Key | RFC | Value | 5 | |-----+----------------------+-----+--------------------------------------------------------------------------------| 6 | | [X] | AUTH_TYPE | YES | Basic supported | 7 | | [X] | CONTENT_LENGTH | YES | | 8 | | [X] | CONTENT_TYPE | YES | | 9 | | [ ] | DOCUMENT_ROOT | | The root directory of your server | 10 | | [X] | GATEWAY_INTERFACE | YES | "CGI/1.1" | 11 | | [ ] | HTTP_ACCEPT | | | 12 | | [ ] | HTTP_ACCEPT_CHARSET | | | 13 | | [ ] | HTTP_ACCEPT_ENCODING | | | 14 | | [ ] | HTTP_ACCEPT_LANGUAGE | | | 15 | | [X] | HTTP_AUTHORIZATION | | Workaround for Apache & php5-cgi, see AUTH_TYPE | 16 | | [ ] | HTTP_CONNECTION | | | 17 | | [ ] | HTTP_COOKIE | | The visitor's cookie, if one is set | 18 | | [X] | HTTP_HOST | | The hostname of the page being attempted | 19 | | [ ] | HTTP_KEEP_ALIVE | | | 20 | | [X] | HTTP_REFERER | | The URL of the page that called your program | 21 | | [X] | HTTP_USER_AGENT | | The browser type of the visitor | 22 | | [ ] | HTTPS | | "on" if the program is being called through a secure server | 23 | | [ ] | PATH | | The system path your server is running under | 24 | | [X] | PATH_INFO | YES | | 25 | | [X] | PATH_TRANSLATED | YES | | 26 | | [X] | QUERY_STRING | YES | The query string (see GET, below) | 27 | | [X] | REDIRECT_STATUS | | Non-standard environment varialbe used by PHP | 28 | | [X] | REMOTE_ADDR | YES | The IP address of the visitor | 29 | | [X] | REMOTE_HOST | YES | The hostname of the visitor[fn:1] | 30 | | [ ] | REMOTE_IDENT | MAY | May be used to provide RFC 1413 connection identity information | 31 | | [ ] | REMOTE_PORT | | The port from which the request was sent | 32 | | [X] | REMOTE_USER | YES | The visitor's username (for .htaccess-protected pages) | 33 | | [X] | REQUEST_METHOD | YES | GET or POST | 34 | | [X] | REQUEST_URI | | Interpreted pathname of requested document/CGI (relative to the document root) | 35 | | [X] | SCRIPT_FILENAME | | Full pathname of the current CGI, used by PHP | 36 | | [X] | SCRIPT_NAME | YES | Interpreted pathname of the current CGI (relative to the document root) | 37 | | [ ] | SERVER_ADDR | | Server IP address | 38 | | [ ] | SERVER_ADMIN | | The email address for your server's webmaster | 39 | | [X] | SERVER_NAME | YES | Your server's fully qualified domain name (e.g. www.cgi101.com) | 40 | | [X] | SERVER_PORT | YES | The port number your server is listening on | 41 | | [X] | SERVER_PROTOCOL | YES | "HTTP/1.1" | 42 | | [ ] | SERVER_SIGNATURE | | | 43 | | [X] | SERVER_SOFTWARE | YES | The server software you're using, e.g. "Apache/1.3 (Win32)" | 44 | 45 | The RFC column refers to mandatory CGI environment variables, as defined 46 | in . 47 | 48 | ---- 49 | [fn:1] If your server has reverse-name-lookups on; otherwise this is the IP address again. 50 | -------------------------------------------------------------------------------- /lib/.gitignore: -------------------------------------------------------------------------------- 1 | .deps 2 | .dirstamp 3 | -------------------------------------------------------------------------------- /lib/lstat.c: -------------------------------------------------------------------------------- 1 | /* lstat.c - lstat(2) replacement, requires POSIX fstatat(2) 2 | ** 3 | ** Copyright (C) 2019-2021 Joachim Wiberg 4 | ** All rights reserved. 5 | ** 6 | ** Redistribution and use in source and binary forms, with or without 7 | ** modification, are permitted provided that the following conditions 8 | ** are met: 9 | ** 1. Redistributions of source code must retain the above copyright 10 | ** notice, this list of conditions and the following disclaimer. 11 | ** 2. Redistributions in binary form must reproduce the above copyright 12 | ** notice, this list of conditions and the following disclaimer in the 13 | ** documentation and/or other materials provided with the distribution. 14 | ** 15 | ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | ** AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | ** ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE 19 | ** LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 20 | ** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 21 | ** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 | ** INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 | ** CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 24 | ** ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 25 | ** THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #include 29 | #include 30 | 31 | int lstat(const char *pathname, struct stat *statbuf) 32 | { 33 | return fstatat(AT_FDCWD, pathname, statbuf, AT_SYMLINK_NOFOLLOW); 34 | } 35 | -------------------------------------------------------------------------------- /lib/malloc.c: -------------------------------------------------------------------------------- 1 | #if HAVE_CONFIG_H 2 | # include 3 | #endif 4 | #undef malloc 5 | 6 | #include 7 | 8 | void *malloc (); 9 | 10 | /* 11 | * Allocate an N-byte block of memory from the heap. 12 | * If N is zero, allocate a 1-byte block. 13 | */ 14 | void *rpl_malloc (size_t n) 15 | { 16 | if (n == 0) 17 | n = 1; 18 | 19 | return malloc (n); 20 | } 21 | -------------------------------------------------------------------------------- /lib/realloc.c: -------------------------------------------------------------------------------- 1 | #if HAVE_CONFIG_H 2 | # include 3 | #endif 4 | #undef realloc 5 | #undef malloc 6 | 7 | #include 8 | 9 | #include 10 | 11 | void * rpl_realloc (void *p, size_t n) 12 | { 13 | void *result; 14 | 15 | if (n == 0) 16 | n = 1; 17 | 18 | if (p == NULL) 19 | result = malloc (n); 20 | else 21 | result = realloc (p, n); 22 | 23 | if (result == NULL) 24 | errno = ENOMEM; 25 | 26 | return result; 27 | } 28 | -------------------------------------------------------------------------------- /lib/strlcat.c: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: strlcat.c,v 1.15 2015/03/02 21:41:08 millert Exp $ */ 2 | 3 | /* 4 | * Copyright (c) 1998, 2015 Todd C. Miller 5 | * 6 | * Permission to use, copy, modify, and distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #include 20 | #include 21 | 22 | /* 23 | * Appends src to string dst of size dsize (unlike strncat, dsize is the 24 | * full size of dst, not space left). At most dsize-1 characters 25 | * will be copied. Always NUL terminates (unless dsize <= strlen(dst)). 26 | * Returns strlen(src) + MIN(dsize, strlen(initial dst)). 27 | * If retval >= dsize, truncation occurred. 28 | */ 29 | size_t 30 | strlcat(char *dst, const char *src, size_t dsize) 31 | { 32 | const char *odst = dst; 33 | const char *osrc = src; 34 | size_t n = dsize; 35 | size_t dlen; 36 | 37 | /* Find the end of dst and adjust bytes left but don't go past end. */ 38 | while (n-- != 0 && *dst != '\0') 39 | dst++; 40 | dlen = dst - odst; 41 | n = dsize - dlen; 42 | 43 | if (n-- == 0) 44 | return(dlen + strlen(src)); 45 | while (*src != '\0') { 46 | if (n != 0) { 47 | *dst++ = *src; 48 | n--; 49 | } 50 | src++; 51 | } 52 | *dst = '\0'; 53 | 54 | return(dlen + (src - osrc)); /* count does not include NUL */ 55 | } 56 | -------------------------------------------------------------------------------- /lib/strlcpy.c: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: strlcpy.c,v 1.12 2015/01/15 03:54:12 millert Exp $ */ 2 | 3 | /* 4 | * Copyright (c) 1998, 2015 Todd C. Miller 5 | * 6 | * Permission to use, copy, modify, and distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #include 20 | #include 21 | 22 | /* 23 | * Copy string src to buffer dst of size dsize. At most dsize-1 24 | * chars will be copied. Always NUL terminates (unless dsize == 0). 25 | * Returns strlen(src); if retval >= dsize, truncation occurred. 26 | */ 27 | size_t 28 | strlcpy(char *dst, const char *src, size_t dsize) 29 | { 30 | const char *osrc = src; 31 | size_t nleft = dsize; 32 | 33 | /* Copy as many bytes as will fit. */ 34 | if (nleft != 0) { 35 | while (--nleft != 0) { 36 | if ((*dst++ = *src++) == '\0') 37 | break; 38 | } 39 | } 40 | 41 | /* Not enough room in dst, add NUL and traverse rest of src. */ 42 | if (nleft == 0) { 43 | if (dsize != 0) 44 | *dst = '\0'; /* NUL-terminate dst */ 45 | while (*src++) 46 | ; 47 | } 48 | 49 | return(src - osrc - 1); /* count does not include NUL */ 50 | } 51 | -------------------------------------------------------------------------------- /lib/tempfile.c: -------------------------------------------------------------------------------- 1 | /* A secure tmpfile() replacement. 2 | * 3 | * Copyright (c) 2015-2021 Joachim Wiberg 4 | * 5 | * Permission to use, copy, modify, and/or distribute this software for any 6 | * purpose with or without fee is hereby granted, provided that the above 7 | * copyright notice and this permission notice appear in all copies. 8 | * 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | */ 17 | #define _GNU_SOURCE 18 | #include 19 | #include 20 | #include /* O_TMPFILE requires -D_GNU_SOURCE */ 21 | #include /* fdopen() */ 22 | #include /* mkostemp() */ 23 | #include /* umask() */ 24 | 25 | static FILE *fallback(void) 26 | { 27 | char nm[15] = _PATH_TMP "XXXXXXXX"; 28 | int fd; 29 | 30 | fd = mkostemp(nm, O_CLOEXEC); 31 | if (-1 == fd) 32 | return NULL; 33 | 34 | return fdopen(fd, "w+"); 35 | } 36 | 37 | /** 38 | * A secure tmpfile() replacement 39 | * 40 | * This is the secure replacement for tmpfile() that does not exist in 41 | * GLIBC. It uses the Linux specific @c O_TMPFILE and @c O_EXCL to hide 42 | * the filename. When the @c FILE is fclose()'ed, the file contents is 43 | * lost. The file is hidden in the @c _PATH_TMP ("/tmp") directory in 44 | * the system. 45 | * 46 | * This function requires Linux 3.11, or later, due to @c O_TMPFILE. 47 | * Not all file systems support hidden inodes, in which case this 48 | * function defaults to call tmpfile() as a fallback. 49 | * 50 | * @returns An open @c FILE pointer, or @c NULL on error. 51 | */ 52 | FILE *tempfile(void) 53 | { 54 | #ifdef O_TMPFILE /* Only on Linux, with fairly recent (G)LIBC */ 55 | int fd; 56 | mode_t oldmask; 57 | 58 | oldmask = umask(0077); 59 | fd = open(_PATH_TMP, O_TMPFILE | O_RDWR | O_EXCL | O_CLOEXEC, S_IRUSR | S_IWUSR); 60 | umask(oldmask); 61 | if (-1 == fd) { 62 | if (errno == EOPNOTSUPP) 63 | return fallback(); 64 | 65 | return NULL; 66 | } 67 | 68 | return fdopen(fd, "w+"); 69 | #else 70 | return fallback(); 71 | #endif 72 | } 73 | 74 | /** 75 | * Local Variables: 76 | * indent-tabs-mode: t 77 | * c-file-style: "linux" 78 | * End: 79 | */ 80 | -------------------------------------------------------------------------------- /man.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | # Generates HTML versions of man pages using mandoc 3 | 4 | GEN=`which mandoc` 5 | TOP=`git rev-parse --show-toplevel` 6 | HEAD=$TOP/www/header.html 7 | FOOT=$TOP/www/footer.html 8 | for file in `ls $TOP/man/*.[158]`; do 9 | name=`basename $file` 10 | web=$TOP/www/$name.html 11 | echo "Updating $web ..." 12 | cat $HEAD > $web 13 | mandoc -T html -O fragment $file >> $web 14 | cat $FOOT >> $web 15 | sed -i "s/%TITLE%/$name/" $web 16 | done 17 | -------------------------------------------------------------------------------- /man/Makefile.am: -------------------------------------------------------------------------------- 1 | if ENABLE_HTPASSWD 2 | dist_man1_MANS = htpasswd.1 3 | endif 4 | if HAVE_CONFUSE 5 | dist_man5_MANS = merecat.conf.5 6 | endif 7 | dist_man8_MANS = merecat.8 ssi.8 8 | # in.httpd.8 9 | SYMLINK = httpd.8 10 | 11 | # Hook in install merecat.8 --> in.httpd-8, httpd-8 symlinks 12 | if CREATE_SYMLINKS 13 | install-data-hook: 14 | @for file in $(SYMLINK); do \ 15 | link=$(DESTDIR)$(mandir)/man8/$$file; \ 16 | [ "`readlink $$link`" = "merecat.8" ] && continue; \ 17 | $(LN_S) merecat.8 $$link; \ 18 | done 19 | endif 20 | -------------------------------------------------------------------------------- /man/htpasswd.1: -------------------------------------------------------------------------------- 1 | .\" -*- nroff -*- 2 | .\" The Merecat web server stems from both sthttpd and thttpd, both of 3 | .\" which are free software under the 2-clause simplified BSD license. 4 | .\" 5 | .\" Copyright (c) 1995-2015 Jef Poskanzer 6 | .\" All rights reserved. 7 | .\" 8 | .\" Redistribution and use in source and binary forms, with or without 9 | .\" modification, are permitted provided that the following conditions 10 | .\" are met: 11 | .\" 1. Redistributions of source code must retain the above copyright 12 | .\" notice, this list of conditions and the following disclaimer. 13 | .\" 2. Redistributions in binary form must reproduce the above copyright 14 | .\" notice, this list of conditions and the following disclaimer in the 15 | .\" documentation and/or other materials provided with the distribution. 16 | .\" 17 | .\" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | .\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | .\" ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE 21 | .\" LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | .\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | .\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | .\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | .\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | .\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 27 | .\" THE POSSIBILITY OF SUCH DAMAGE. 28 | .Dd Aug 3, 2019 29 | .Dt htpasswd 1 30 | .Os "merecat (2.32)" 31 | .Sh NAME 32 | .Nm htpasswd 33 | .Nd Create and update user authentication files 34 | .Sh SYNOPSIS 35 | .Nm 36 | .Op Fl cm 37 | .Ar FILE 38 | .Ar USERNAME 39 | .Sh DESCRIPTION 40 | .Nm 41 | is used to create and update the flat-files used to store usernames and 42 | password for basic authentication of HTTP users. Resources available 43 | from the Merecat web server can be restricted to just the users listed 44 | in the files created by htpasswd. This program can only be used when 45 | the usernames are stored in a flat-file. 46 | .Pp 47 | This manual page only lists the command line arguments for 48 | .Nm . 49 | For details of the directives necessary to enable user authentication in 50 | the web server, see 51 | .Xr merecat 8 , 52 | the Merecat httpd README, or the GitHub project home page 53 | .Aq https://github.com/troglobit/merecat . 54 | .Sh OPTIONS 55 | .Bl -tag -width Ds 56 | .It Fl c 57 | Create, or recreate the .htpwassd file, 58 | .Ar FILE , 59 | if it already exists. 60 | .It Fl m 61 | Use MD5 encryption for passwords, this is tested at runtime. So this 62 | option is simply for compatibility with other similar tools. 63 | .It Ar FILE 64 | Name of the file to contain the user name and password, 65 | usually .htpasswd. If 66 | .Fl c 67 | is given, this file is created if it does not already exist, or deleted 68 | and recreated if it does exist. 69 | .It Ar USERNAME 70 | The username to create or update in passwdfile, 71 | .Ar FILE . 72 | If username does not exist is this file, an entry is added. If it does 73 | exist, the password is changed. 74 | .El 75 | .Sh SEE ALSO 76 | .Xr merecat 8 77 | .Sh AUTHORS 78 | .An Rob McCool Aq robm@stanford.edu 79 | originally wrote 80 | .Nm 81 | for NCSA httpd. It then (naturally) made its way to Apache and other 82 | web servers, like Roxen Challenger. 83 | .An Jef Poskanzer Aq jef@mail.acme.com 84 | modified it 29aug97 to accept new password on stdin, if stdin is a pipe 85 | or file. This is necessary for use from CGI. 86 | -------------------------------------------------------------------------------- /man/ssi.8: -------------------------------------------------------------------------------- 1 | .\" -*- nroff -*- 2 | .\" The Merecat web server stems from both sthttpd and thttpd, both of 3 | .\" which are free software under the 2-clause simplified BSD license. 4 | .\" 5 | .\" Copyright (c) 1995-2015 Jef Poskanzer 6 | .\" All rights reserved. 7 | .\" 8 | .\" Redistribution and use in source and binary forms, with or without 9 | .\" modification, are permitted provided that the following conditions 10 | .\" are met: 11 | .\" 1. Redistributions of source code must retain the above copyright 12 | .\" notice, this list of conditions and the following disclaimer. 13 | .\" 2. Redistributions in binary form must reproduce the above copyright 14 | .\" notice, this list of conditions and the following disclaimer in the 15 | .\" documentation and/or other materials provided with the distribution. 16 | .\" 17 | .\" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | .\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | .\" ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE 21 | .\" LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | .\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | .\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | .\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | .\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | .\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 27 | .\" THE POSSIBILITY OF SUCH DAMAGE. 28 | .Dd Aug 3, 2019 29 | .Dt SSI 8 SMM 30 | .Os "merecat (2.32)" 31 | .Sh NAME 32 | .Nm ssi 33 | .Nd Server-Side-Includes CGI program 34 | .Sh SYNOPSIS 35 | .Nm 36 | .Sh DESCRIPTION 37 | This is an external CGI program that provides the same functionality as 38 | the built-in server-side-includes feature in many HTTP daemons. It is 39 | written for use with 40 | .Xr thttpd 8 41 | and 42 | .Xr merecat 8 , 43 | but should be easy to adapt to other systems. 44 | .Pp 45 | There are two ways to use this; the modern way of using a 46 | .Cm .shtml 47 | pattern in 48 | .Xr merecat.conf 5 49 | to trigger the SSI script, which requires enabling the SSI module. Then 50 | there is the traditional 51 | .Xr thttpd 8 52 | approach. We start with the relevant settings needed in 53 | .Nm merecat.conf : 54 | .Bd -literal -offset indent 55 | ssi { 56 | enabled = true 57 | pattern = "**.shtml" # default 58 | cgi-path = "cgi-bin/ssi" # default, web server root is used 59 | } 60 | .Ed 61 | .Pp 62 | The traditional 63 | .Nm thttpd 64 | way runs ssi as a simple CGI script, which requires placing the ssi 65 | binary in the web server CGI area, and enabling CGI. Then set up URLs 66 | with the path to the document to parse as the "pathinfo". That's the 67 | part of the URL that comes after the CGI program name. For example, if 68 | the URL to this program is: 69 | .Bd -unfilled -offset left 70 | 71 | http://www.acme.com/cgi-bin/ssi 72 | 73 | .Ed 74 | and the url for the document is: 75 | .Bd -unfilled -offset left 76 | 77 | http://www.acme.com/users/wecoyote/doc.html 78 | 79 | .Ed 80 | then the compound URL would be: 81 | .Bd -unfilled -offset left 82 | 83 | http://www.acme.com/cgi-bin/ssi/users/wecoyote/doc.html 84 | 85 | .Ed 86 | .Sh INCLUDE FORMAT 87 | The format description below is adapted from 88 | .Aq http://hoohoo.ncsa.uiuc.edu/docs/tutorials/includes.html . 89 | .Pp 90 | All directives are formatted as SGML comments within the document. This 91 | is in case the document should ever find itself in the client's hands 92 | unparsed. Each directive has the following format: 93 | .Bd -unfilled -offset left 94 | 95 | 96 | 97 | .Ed 98 | .Em Note: 99 | the lack of space between the initial HTML comment start and the 100 | #command. This is explicitly stated in the standard and strictly 101 | enforced by all web servers implementing SSI. 102 | .Pp 103 | Each command takes different arguments, most only accept one tag at a 104 | time. Here is a breakdown of the commands and their associated tags: 105 | .Bl -tag -width Ds 106 | .It Cm config 107 | The config directive controls various aspects of the file parsing. 108 | There are two valid tags: 109 | .Bl -tag -width Ds 110 | .It Cm timefmt 111 | gives the server a new format to use when providing dates. This is a 112 | string compatible with the 113 | .Xr strftime 3 114 | library call. 115 | .It Cm sizefmt 116 | determines the formatting to be used when displaying the size of a file. 117 | Valid choices are bytes, for a formatted byte count (formatted as 118 | 1,234,567), or abbrev for an abbreviated version displaying the number 119 | of kilobytes or megabytes the file occupies. 120 | .It Cm errmsg 121 | overrides the default; 122 | .Qq [an error occurred while processing this directive] 123 | .El 124 | .It Cm include 125 | Inserts the text of another document into the parsed document. The 126 | inserted file is parsed recursively, so it can contain 127 | server-side-include directives too. This command accepts two tags: 128 | .Bl -tag -width Ds 129 | .It Cm virtual 130 | Gives a virtual path to a document on the server. 131 | .It Cm file 132 | Gives a pathname relative to the current directory. ../ cannot be used 133 | in this pathname, nor can absolute paths be used. 134 | .El 135 | .It Cm echo 136 | Prints the value of one of the include variables (defined below). Any 137 | dates are printed subject to the currently configured timefmt. The only 138 | valid tag to this command is var, whose value is the name of the 139 | variable you wish to echo. 140 | .It Cm fsize 141 | prints the size of the specified file, subject to the sizefmt parameter 142 | to the config command. Valid tags are the same as with the include 143 | command. 144 | .It Cm flastmod 145 | prints the last modification date of the specified file, subject to the 146 | formatting preference given by the timefmt parameter to config. Valid 147 | tags are the same as with the include command. 148 | .El 149 | .Sh VARIABLES 150 | A number of variables are made available to parsed documents. In 151 | addition to the CGI variable set, the following variables are made 152 | available: 153 | .Bl -tag -width Ds 154 | .It Cm DOCUMENT_NAME 155 | The current filename. 156 | .It Cm DOCUMENT_URI 157 | The virtual path to this document (such as /~robm/foo.shtml). 158 | .It Cm QUERY_STRING_UNESCAPED 159 | The unescaped version of any search query the client sent. 160 | .It Cm DATE_LOCAL 161 | The current date, local time zone. Subject to the timefmt parameter to 162 | the config command. 163 | .It Cm DATE_GMT 164 | Same as 165 | .Cm DATE_LOCAL 166 | but in Greenwich mean time (GMT). 167 | .It Cm LAST_MODIFIED 168 | The last modification date of the current document. Subject to timefmt 169 | like the others. 170 | .El 171 | .Sh SEE ALSO 172 | .Xr merecat 8 , 173 | .Xr merecat.conf 5 , 174 | .Xr strftime 3 175 | .Sh AUTHORS 176 | .An -split 177 | .An Jef Poskanzer Aq jef@mail.acme.com 178 | wrote the original for use with 179 | .Nm thttpd . 180 | .An Joachim Wiberg Aq troglobit@gmail.com 181 | added minor features and a trigger in 182 | .Nm merecat 183 | for 184 | .Cm .shtml 185 | pages. 186 | .Sh BUGS 187 | Does not implement all "modern" SSI directives are supported. E.g., 188 | .Cm exec cgi 189 | and 190 | .Cm exec cmd 191 | or any control directives like 192 | .Cm if, elif, else, endif, 193 | etc. Patches and pull-requests are welcome :) 194 | -------------------------------------------------------------------------------- /merecat.conf: -------------------------------------------------------------------------------- 1 | ## /etc/merecat.conf -*-conf-unix-*- 2 | ## This is a sample configuration file for Merecat httpd 3 | ## For more help and more settings, see merecat.conf(5). 4 | ## 5 | 6 | ## what interface to bind to? 7 | ## (default is binding to any interface) 8 | #hostname=www.example.org 9 | 10 | ## Port to listen to, overrides command line argument 11 | ## Defaults to 80, or 443 when enabling HTTPS 12 | #port = 80 13 | 14 | ## Unpriviliged user to run as, usually nobody or www-data 15 | #username = nobody 16 | 17 | ## Global .htpasswd (true) or local per-directory (false) 18 | #global-passwd = false 19 | 20 | ## Chrooting is a security measure which means isolating the webserver's 21 | ## access to files only available from a the given directory. To access 22 | ## files outside the chroot the administrator can either copy or bind 23 | ## mount files and directories into the chroot. 24 | #chroot = false 25 | 26 | ## Only useful if not chrooting 27 | #check-symlinks = false 28 | 29 | ## Alt. charset=iso-8859-1 30 | #charset = UTF-8 31 | 32 | ## Deflate (gzip) compression level: -1 .. 9 33 | ## -1: Default (zlib's reasonable default, currently 6) 34 | ## 0: Disabled 35 | ## 1: Best speed 36 | ## 9: Best compression 37 | #compression-level = -1 38 | 39 | ## Webserver document root, or chroot 40 | #directory = /var/www 41 | 42 | ## When chrooting, alt. document root inside chroot 43 | ## => /var/www/htdocs 44 | #data-directory = /htdocs 45 | 46 | ## Skip dotfiles in dirlistings 47 | #list-dotfiles = false 48 | 49 | ## Virtual hosting 50 | ## /var/www/cgi-bin/ <-- Shared CGI 51 | ## /var/www/git.example.com <-- git.example.com 52 | ## /var/www/ftp.example.com <-- ftp.example.com 53 | #virtual-host = false 54 | 55 | ## Control the caching, in seconds, by setting the following header for 56 | ## all transactions. Depends heavily on the content you provide, and 57 | ## this global setting is disabled by default. It is recommended to 58 | ## instead set it per server location, e.g. for all image files. 59 | ## 60 | ## Cache-Control: max-age=SEC 61 | ## 62 | ## Min max-age value 0 (browser caching disabled) 63 | ## Max max-age value 31536000 (1 year) 64 | ## 65 | #max-age = 0 66 | 67 | ## Some bots behave really badly and may overload your server. Often 68 | ## they cannot be blocked based on IP address, so the only means we are 69 | ## left with is User-Agent blocking. Use patterns like this: 70 | #user-agent-deny = "**SemrushBot**|**MJ12bot**|**DotBot**|**PetalBot**" 71 | 72 | ## Enable HTTPS support. The certificate (public) and key (private) are 73 | ## required when enabling HTTPS support. The (min) protocol and cipher 74 | ## settings are optional and have sane built-in defaults, e.g. 'protocol' 75 | ## defaults to TLSv1.1. See ciphers(1) man page for possible values. 76 | ## 77 | ## Note: You may want to enable this on a per-server basis instead. 78 | #ssl { 79 | # protocol = "TLSv1.1" 80 | # ciphers = "..." 81 | # certfile = certs/cert.pem 82 | # keyfile = private/key.pem 83 | # dhfile = certs/dhparam.pem 84 | #} 85 | 86 | ## The CGI module is a core part of Merecat httpd and is for security 87 | ## reasons disabled by default. Like other modules it uses pattern 88 | ## matching to trigger the CGI functionality: 89 | ## ? match a single char 90 | ## * matches any string excluding "/" 91 | ## ** matches any string including "/" 92 | ## separate multiple patterns with "|" 93 | ## Example: "**.sh|**.cgi" 94 | ## 95 | ## `limit` sets the max number of simultaneous CGI programs allowed. 96 | ## 97 | ## The below values are the default, so to enable CGI only `enabled` 98 | ## need to be set to 'true'. 99 | #cgi "**.cgi|/cgi-bin/*" { 100 | # enabled = false 101 | # limit = 50 102 | #} 103 | 104 | ## The PHP module is bolted on top of the CGI module, so the same limits 105 | ## apply also to PHP scripts. The below are the built-in defaults. 106 | ## Verify the path to the php-cgi binary for your system and expand on 107 | ## the pattern if you have, e.g. .php5 files. 108 | #php "**.php*" { 109 | # enabled = false 110 | # cgi-path = "/usr/bin/php-cgi" 111 | #} 112 | 113 | ## The SSI module, like PHP above, is built on top of the CGI module, 114 | ## and it also requires the Merecat SSI CGI script to be installed, the 115 | ## defaults are commented out below. The silent setting controls the 116 | ## default value. 117 | #ssi "**.shtml" { 118 | # enabled = false 119 | # silent = false 120 | # cgi-path = "cgi-bin/ssi" 121 | #} 122 | 123 | ## Server specific settings, overrides certain global settings 124 | ## Notice the HTTP redirect from the default server to HTTPS. 125 | #server default { 126 | # port = 80 127 | # redirect "/" { 128 | # code = 301 129 | # location = "https://$host$request_uri$args" 130 | # } 131 | #} 132 | #server secure { 133 | # port = 443 134 | # ssl = on 135 | # certfile = /etc/letsencrypt/live/example.com/fullchain.pem 136 | # keyfile = /etc/letsencrypt/live/example.com/privkey.pem 137 | # dhfile = certs/dhparam.pem 138 | #} 139 | -------------------------------------------------------------------------------- /merecat.service.in: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Merecat web server 3 | Documentation=man:merecat(8) man:merecat.conf(5) man:ssi(8) man:htpasswd(1) 4 | ConditionPathExists=@WWWDIR@ 5 | After=network-online.target 6 | Requires=network-online.target 7 | 8 | [Service] 9 | Type=simple 10 | Restart=always 11 | RestartSec=3 12 | ExecStart=@SBINDIR@/merecat -sn @WWWDIR@ 13 | 14 | [Install] 15 | WantedBy=multi-user.target 16 | -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Serve volume /srv/ftp with merecat httpd on host port 80 3 | docker pull troglobit/merecat:latest 4 | docker run -dit --restart unless-stopped -h `hostname` \ 5 | -v /srv/ftp:/var/www -p 80:80 troglobit/merecat:latest merecat -n /var/www 6 | 7 | -------------------------------------------------------------------------------- /src/.gitignore: -------------------------------------------------------------------------------- 1 | .deps 2 | htpasswd 3 | libmatch.a 4 | merecat 5 | -------------------------------------------------------------------------------- /src/Makefile.am: -------------------------------------------------------------------------------- 1 | AUTOMAKE_OPTIONS = subdir-objects 2 | 3 | sbin_PROGRAMS = merecat 4 | if ENABLE_HTPASSWD 5 | sbin_PROGRAMS += htpasswd 6 | endif 7 | 8 | # in.httpd 9 | SYMLINK = httpd 10 | merecat_CFLAGS = -fPIC -W -Wall -Wextra -std=gnu99 11 | merecat_CFLAGS += -Wno-unused-result -Wno-unused-parameter -Wno-unused-variable 12 | merecat_CFLAGS += $(zlib_CFLAGS) 13 | merecat_CPPFLAGS = -D_POSIX_SOURCE -D_BSD_SOURCE -D_GNU_SOURCE -D_DEFAULT_SOURCE 14 | merecat_CPPFLAGS += -DCONFDIR='"$(sysconfdir)"' -DLOCALSTATEDIR='"$(localstatedir)"' 15 | merecat_CPPFLAGS += -DRUNDIR='"$(runstatedir)"' 16 | merecat_LDADD = libmatch.a $(zlib_LIBS) 17 | merecat_LDADD += $(LIBS) $(LIBOBJS) 18 | merecat_SOURCES = base64.c base64.h \ 19 | fdwatch.c fdwatch.h \ 20 | file.c file.h \ 21 | libhttpd.c libhttpd.h \ 22 | md5.c md5.h \ 23 | merecat.c merecat.h \ 24 | mmc.c mmc.h \ 25 | pidfile.c stack.c \ 26 | srv.c srv.h \ 27 | timers.c timers.h \ 28 | tdate_parse.c tdate_parse.h \ 29 | mime_encodings.h mime_types.h 30 | if ENABLE_SSL 31 | merecat_SOURCES += ssl.c ssl.h 32 | merecat_CFLAGS += $(OpenSSL_CFLAGS) 33 | merecat_LDADD += $(OpenSSL_LIBS) 34 | endif 35 | 36 | if HAVE_CONFUSE 37 | merecat_SOURCES += conf.c conf.h 38 | merecat_CFLAGS += $(confuse_CFLAGS) 39 | merecat_LDADD += $(confuse_LIBS) 40 | endif 41 | 42 | if ENABLE_HTPASSWD 43 | htpasswd_CFLAGS = -W -Wall -Wextra -Wno-unused-result -Wno-unused-parameter 44 | htpasswd_CPPFLAGS = -DWEBDIR='"$(WEBDIR)"' 45 | htpasswd_SOURCES = htpasswd.c 46 | endif 47 | 48 | noinst_LIBRARIES = libmatch.a 49 | libmatch_a_SOURCES = match.c match.h 50 | 51 | # Hook in install merecat --> in.httpd, httpd symlinks 52 | if CREATE_SYMLINKS 53 | install-exec-hook: 54 | @for file in $(SYMLINK); do \ 55 | link=$(DESTDIR)$(sbindir)/$$file; \ 56 | [ "`readlink $$link`" = "merecat" ] && continue; \ 57 | $(LN_S) merecat $$link; \ 58 | done 59 | 60 | uninstall-hook: 61 | @for file in $(SYMLINK); do \ 62 | $(RM) $(DESTDIR)$(sbindir)/$$file; \ 63 | done 64 | endif 65 | -------------------------------------------------------------------------------- /src/base64.c: -------------------------------------------------------------------------------- 1 | /* Base-64 decoding. This represents binary data as printable ASCII 2 | ** characters. Three 8-bit binary bytes are turned into four 6-bit 3 | ** values, like so: 4 | ** 5 | ** [11111111] [22222222] [33333333] 6 | ** 7 | ** [111111] [112222] [222233] [333333] 8 | ** 9 | ** Then the 6-bit values are represented using the characters "A-Za-z0-9+/". 10 | */ 11 | 12 | static int b64_decode_table[256] = { 13 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 00-0F */ 14 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 10-1F */ 15 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /* 20-2F */ 16 | 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, /* 30-3F */ 17 | -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 40-4F */ 18 | 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 50-5F */ 19 | -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 60-6F */ 20 | 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, /* 70-7F */ 21 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 80-8F */ 22 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 90-9F */ 23 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* A0-AF */ 24 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* B0-BF */ 25 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* C0-CF */ 26 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* D0-DF */ 27 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* E0-EF */ 28 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 /* F0-FF */ 29 | }; 30 | 31 | /* Do base-64 decoding on a string. Ignore any non-base64 bytes. 32 | ** Return the actual number of bytes generated. The decoded size will 33 | ** be at most 3/4 the size of the encoded, and may be smaller if there 34 | ** are padding characters (blanks, newlines). 35 | */ 36 | int b64_decode(const char *str, unsigned char *space, int size) 37 | { 38 | const char *cp; 39 | int space_idx, phase; 40 | int prev_d = 0; 41 | unsigned char c; 42 | 43 | space_idx = 0; 44 | phase = 0; 45 | for (cp = str; *cp != '\0'; ++cp) { 46 | int d; 47 | 48 | d = b64_decode_table[(int)*cp]; 49 | if (d != -1) { 50 | switch (phase) { 51 | case 0: 52 | ++phase; 53 | break; 54 | 55 | case 1: 56 | c = ((prev_d << 2) | ((d & 0x30) >> 4)); 57 | if (space_idx < size) 58 | space[space_idx++] = c; 59 | ++phase; 60 | break; 61 | 62 | case 2: 63 | c = (((prev_d & 0xf) << 4) | ((d & 0x3c) >> 2)); 64 | if (space_idx < size) 65 | space[space_idx++] = c; 66 | ++phase; 67 | break; 68 | 69 | case 3: 70 | c = (((prev_d & 0x03) << 6) | d); 71 | if (space_idx < size) 72 | space[space_idx++] = c; 73 | phase = 0; 74 | break; 75 | } 76 | prev_d = d; 77 | } 78 | } 79 | 80 | return space_idx; 81 | } 82 | -------------------------------------------------------------------------------- /src/base64.h: -------------------------------------------------------------------------------- 1 | /* Base-64 decoding */ 2 | 3 | #ifndef BASE64_H_ 4 | #define BASE64_H_ 5 | 6 | int b64_decode(const char *str, unsigned char *space, int size); 7 | 8 | #endif /* BASE64_H_ */ 9 | 10 | -------------------------------------------------------------------------------- /src/conf.h: -------------------------------------------------------------------------------- 1 | /* libConfuse based merecat.conf parser 2 | ** 3 | ** Copyright (C) 2016-2021 Joachim Wiberg 4 | ** All rights reserved. 5 | ** 6 | ** Redistribution and use in source and binary forms, with or without 7 | ** modification, are permitted provided that the following conditions 8 | ** are met: 9 | ** 1. Redistributions of source code must retain the above copyright 10 | ** notice, this list of conditions and the following disclaimer. 11 | ** 2. Redistributions in binary form must reproduce the above copyright 12 | ** notice, this list of conditions and the following disclaimer in the 13 | ** documentation and/or other materials provided with the distribution. 14 | ** 15 | ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | ** AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | ** ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE 19 | ** LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 20 | ** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 21 | ** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 | ** INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 | ** CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 24 | ** ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 25 | ** THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #ifndef MERECAT_CONF_H_ 29 | #define MERECAT_CONF_H_ 30 | 31 | #include 32 | #include 33 | #include 34 | 35 | #include "merecat.h" 36 | #include "srv.h" 37 | 38 | /* From The Practice of Programming, by Kernighan and Pike */ 39 | #ifndef NELEMS 40 | #define NELEMS(array) (sizeof(array) / sizeof(array[0])) 41 | #endif 42 | 43 | /* Command line argument always wins */ 44 | struct conf { 45 | struct conf *prev, *next; 46 | 47 | char *hostname; 48 | char *user; /* DEFAULT_USER or command line */ 49 | uint16_t port; /* ... or command line */ 50 | 51 | bool vhost; 52 | bool check_referer; 53 | bool dotfiles; 54 | bool global_pwd; /* ... or command line */ 55 | 56 | bool chroot; /* ... or command line */ 57 | char *dir; /* SERVER_DIR_DEFUALT: /var/www or command line */ 58 | char *data_dir; 59 | 60 | char *cgi_pattern; /* CGI_PATTERN or command line */ 61 | int cgi_limit; /* CGI_LIMIT */ 62 | char *url_pattern; 63 | char *local_pattern; 64 | char *useragent_deny; 65 | 66 | char *charset; /* DEFAULT_CHARSET */ 67 | int max_age; /* DEFAULT_MAX_AGE */ 68 | int z_level; /* DEFAULT_COMPRESSION: For content-encoding: gzip */ 69 | 70 | bool ssl; 71 | char *certfile; 72 | char *keyfile; 73 | char *dhfile; 74 | }; 75 | 76 | #ifdef HAVE_LIBCONFUSE 77 | int conf_init(char *file); 78 | void conf_exit(void); 79 | 80 | int conf_srv(struct srv *arr, size_t len); 81 | #else 82 | #define conf_init(foo) 83 | #define conf_exit() 84 | 85 | static inline int conf_srv(struct srv *arr, size_t len) 86 | { 87 | arr[0].port = port; 88 | arr[0].ssl = 0; 89 | arr[0].host = hostname; 90 | arr[0].path = data_dir; 91 | 92 | return 1; 93 | } 94 | #endif 95 | 96 | #endif /* MERECAT_CONF_H_ */ 97 | -------------------------------------------------------------------------------- /src/fdwatch.h: -------------------------------------------------------------------------------- 1 | /* fdwatch.h - header file for fdwatch package 2 | ** 3 | ** This package abstracts the use of the select()/poll()/kqueue() 4 | ** system calls. The basic function of these calls is to watch a set 5 | ** of file descriptors for activity. select() originated in the BSD world, 6 | ** while poll() came from SysV land, and their interfaces are somewhat 7 | ** different. fdwatch lets you write your code to a single interface, 8 | ** with the portability differences hidden inside the package. 9 | ** 10 | ** Usage is fairly simple. Call fdwatch_get_nfiles() to initialize 11 | ** the package and find out how many fine descriptors are available. 12 | ** Then each time through your main loop, call fdwatch_clear(), then 13 | ** fdwatch_add_fd() for each of the descriptors you want to watch, 14 | ** then call fdwatch() to actually perform the watch. After it returns 15 | ** you can check which descriptors are ready via fdwatch_check_fd(). 16 | ** 17 | ** If your descriptor set hasn't changed from the last time through 18 | ** the loop, you can skip calling fdwatch_clear() and fdwatch_add_fd() 19 | ** to save a little CPU time. 20 | ** 21 | ** 22 | ** Copyright (C) 1995-2015 Jef Poskanzer 23 | ** All rights reserved. 24 | ** 25 | ** Redistribution and use in source and binary forms, with or without 26 | ** modification, are permitted provided that the following conditions 27 | ** are met: 28 | ** 1. Redistributions of source code must retain the above copyright 29 | ** notice, this list of conditions and the following disclaimer. 30 | ** 2. Redistributions in binary form must reproduce the above copyright 31 | ** notice, this list of conditions and the following disclaimer in the 32 | ** documentation and/or other materials provided with the distribution. 33 | ** 34 | ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 35 | ** AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 36 | ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 37 | ** ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE 38 | ** LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 39 | ** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 40 | ** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 41 | ** INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 42 | ** CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 43 | ** ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 44 | ** THE POSSIBILITY OF SUCH DAMAGE. 45 | */ 46 | 47 | #ifndef FDWATCH_H_ 48 | #define FDWATCH_H_ 49 | 50 | #define FDW_READ 0 51 | #define FDW_WRITE 1 52 | 53 | #ifndef INFTIM 54 | #define INFTIM -1 55 | #endif /* INFTIM */ 56 | 57 | /* Figure out how many file descriptors the system allows, and 58 | ** initialize the fdwatch data structures. Returns -1 on failure. 59 | */ 60 | extern int fdwatch_get_nfiles(void); 61 | 62 | /* Free initialized fdwatch data structues at exit */ 63 | extern void fdwatch_put_nfiles(void); 64 | 65 | /* Add a descriptor to the watch list. rw is either FDW_READ or FDW_WRITE. */ 66 | extern void fdwatch_add_fd(int fd, void *arg, int rw); 67 | 68 | /* Delete a descriptor from the watch list. */ 69 | extern void fdwatch_del_fd(int fd); 70 | 71 | /* Do the watch. Return value is the number of descriptors that are ready, 72 | ** or 0 if the timeout expired, or -1 on errors. A timeout of INFTIM means 73 | ** wait indefinitely. 74 | */ 75 | extern int fdwatch(long timeout_msecs); 76 | 77 | /* Check if a descriptor was ready. */ 78 | extern int fdwatch_check_fd(int fd); 79 | 80 | /* Get the client data for the next returned event. Returns -1 when there 81 | ** are no more events. 82 | */ 83 | extern void *fdwatch_get_next_arg(void); 84 | 85 | /* Generate debugging statistics syslog message. */ 86 | extern void fdwatch_logstats(long secs); 87 | 88 | #endif /* FDWATCH_H_ */ 89 | -------------------------------------------------------------------------------- /src/file.c: -------------------------------------------------------------------------------- 1 | /* Utility functions for file descriptor access 2 | ** 3 | ** Copyright (C) 1995-2015 Jef Poskanzer 4 | ** Copyright (C) 2016-2021 Joachim Wiberg 5 | ** All rights reserved. 6 | ** 7 | ** Redistribution and use in source and binary forms, with or without 8 | ** modification, are permitted provided that the following conditions 9 | ** are met: 10 | ** 1. Redistributions of source code must retain the above copyright 11 | ** notice, this list of conditions and the following disclaimer. 12 | ** 2. Redistributions in binary form must reproduce the above copyright 13 | ** notice, this list of conditions and the following disclaimer in the 14 | ** documentation and/or other materials provided with the distribution. 15 | ** 16 | ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | ** AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | ** ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE 20 | ** LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | ** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | ** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | ** INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | ** CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | ** ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 26 | ** THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #include 30 | #include 31 | 32 | /* Read the requested buffer completely, accounting for interruptions. */ 33 | ssize_t file_read(int fd, void *buf, size_t len) 34 | { 35 | ssize_t sz, retry = 3; 36 | 37 | sz = 0; 38 | while ((size_t)sz < len) { 39 | ssize_t r; 40 | 41 | r = read(fd, (char *)buf + sz, len - sz); 42 | if (r < 0) { 43 | if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) { 44 | if (retry-- > 0) { 45 | sleep(1); 46 | continue; 47 | } 48 | } 49 | 50 | return r; 51 | } 52 | 53 | if (r == 0) 54 | break; 55 | 56 | sz += r; 57 | } 58 | 59 | return sz; 60 | } 61 | 62 | /* Write the requested buffer completely, accounting for interruptions. */ 63 | ssize_t file_write(int fd, void *buf, size_t len) 64 | { 65 | ssize_t sz, retry = 3; 66 | 67 | sz = 0; 68 | while ((size_t)sz < len) { 69 | ssize_t r; 70 | 71 | r = write(fd, (char *)buf + sz, len - sz); 72 | if (r < 0) { 73 | if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) { 74 | if (retry-- > 0) { 75 | sleep(1); 76 | continue; 77 | } 78 | continue; 79 | } 80 | 81 | return r; 82 | } 83 | 84 | if (r == 0) 85 | break; 86 | 87 | sz += r; 88 | } 89 | 90 | return sz; 91 | } 92 | 93 | -------------------------------------------------------------------------------- /src/file.h: -------------------------------------------------------------------------------- 1 | /* Utility functions for file descriptor access 2 | ** 3 | ** Copyright (C) 1995-2015 Jef Poskanzer 4 | ** Copyright (C) 2016-2021 Joachim Wiberg 5 | ** All rights reserved. 6 | ** 7 | ** Redistribution and use in source and binary forms, with or without 8 | ** modification, are permitted provided that the following conditions 9 | ** are met: 10 | ** 1. Redistributions of source code must retain the above copyright 11 | ** notice, this list of conditions and the following disclaimer. 12 | ** 2. Redistributions in binary form must reproduce the above copyright 13 | ** notice, this list of conditions and the following disclaimer in the 14 | ** documentation and/or other materials provided with the distribution. 15 | ** 16 | ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | ** AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | ** ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE 20 | ** LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | ** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | ** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | ** INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | ** CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | ** ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 26 | ** THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #ifndef MERECAT_FILE_H_ 30 | #define MERECAT_FILE_H_ 31 | 32 | ssize_t file_read (int fd, void *buf, size_t len); 33 | ssize_t file_write (int fd, void *buf, size_t len); 34 | 35 | #endif /* MERECAT_FILE_H_ */ 36 | -------------------------------------------------------------------------------- /src/htpasswd.c: -------------------------------------------------------------------------------- 1 | /* 2 | * htpasswd.c: simple program for manipulating password file for NCSA httpd 3 | * 4 | * Rob McCool 5 | */ 6 | 7 | /* Modified 29aug97 by Jef Poskanzer to accept new password on stdin, 8 | ** if stdin is a pipe or file. This is necessary for use from CGI. 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | extern char *crypt(const char *key, const char *setting); 24 | 25 | #define LF 10 26 | #define CR 13 27 | 28 | #define MAX_STRING_LEN 256 29 | 30 | int tfd; 31 | char tmp[] = "/tmp/htp.XXXXXX"; 32 | struct termios saved; 33 | 34 | static void getword(char *word, char *line, char stop) 35 | { 36 | int x = 0, y; 37 | 38 | for (x = 0; ((line[x]) && (line[x] != stop)); x++) 39 | word[x] = line[x]; 40 | 41 | word[x] = '\0'; 42 | if (line[x]) 43 | ++x; 44 | y = 0; 45 | 46 | while ((line[y++] = line[x++])) 47 | ; 48 | } 49 | 50 | static int get_line(char *s, int n, FILE *f) 51 | { 52 | int i = 0; 53 | 54 | while (1) { 55 | s[i] = (char)fgetc(f); 56 | 57 | if (s[i] == CR) 58 | s[i] = fgetc(f); 59 | 60 | if ((s[i] == 0x4) || (s[i] == LF) || (i == (n - 1))) { 61 | s[i] = '\0'; 62 | return (feof(f) ? 1 : 0); 63 | } 64 | ++i; 65 | } 66 | } 67 | 68 | static void putline(FILE *f, char *l) 69 | { 70 | int x; 71 | 72 | for (x = 0; l[x]; x++) 73 | fputc(l[x], f); 74 | fputc('\n', f); 75 | } 76 | 77 | 78 | /* From local_passwd.c (C) Regents of Univ. of California blah blah */ 79 | static unsigned char itoa64[] = /* 0 ... 63 => ascii - 64 */ 80 | "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; 81 | 82 | static void to64(char *s, long v, size_t len) 83 | { 84 | size_t i; 85 | 86 | for (i = 0; i <= len; i++) { 87 | *s++ = itoa64[v & 0x3f]; 88 | v >>= 6; 89 | } 90 | } 91 | 92 | static char *get_password(const char *prompt, char *password, size_t len) 93 | { 94 | int c; 95 | char *pwd = NULL; 96 | size_t pos; 97 | struct termios term = saved; 98 | 99 | /* Disable XON/XOFF and ECHO while reading password string */ 100 | term.c_iflag &= ~(IXON|IXOFF); 101 | term.c_lflag &= ~ECHO; 102 | tcsetattr(STDIN_FILENO, TCSANOW, &term); 103 | 104 | fputs(prompt, stderr); 105 | 106 | pos = 0; 107 | do { 108 | c = fgetc(stdin); 109 | if (isascii(c) && '\r' != c && '\n' != c) 110 | password[pos++] = c; 111 | } while (c != '\n' && pos < len); 112 | fputs("\n", stderr); 113 | password[pos] = 0; 114 | pwd = password; 115 | 116 | /* Restore TTY */ 117 | tcsetattr(STDIN_FILENO, TCSANOW, &saved); 118 | return pwd; 119 | } 120 | 121 | static void add_password(char *user, FILE *fp) 122 | { 123 | char pass[100]; 124 | char *pw; 125 | char *cpw; 126 | char salt[12] = "$1$"; /* Long enough for MD5 Crypt */ 127 | size_t index = 3; 128 | size_t saltlen = 8; 129 | const char *md5 = "$1$JASka/..$pV3V31AdjgqQmjTbgTNVu/"; 130 | 131 | /* Test if the system supports MD5 passwords */ 132 | if (strcmp(crypt("123456", md5), md5)) { 133 | /* The system does not support MD5 crypt: reset to default crypt. */ 134 | saltlen = 2; 135 | index = 0; 136 | } 137 | 138 | if (!isatty(STDIN_FILENO)) { 139 | if (!fgets(pass, sizeof(pass), stdin)) { 140 | fprintf(stderr, "Failed reading password from stdin: %s\n", strerror(errno)); 141 | exit(1); 142 | } 143 | 144 | if (pass[strlen(pass) - 1] == '\n') 145 | pass[strlen(pass) - 1] = '\0'; 146 | pw = pass; 147 | } else { 148 | pw = get_password("New password: ", pass, sizeof(pass)); 149 | if (!pw) { 150 | fail: 151 | fprintf(stderr, "Failed reading password.\n"); 152 | error: 153 | if (tfd != -1) 154 | unlink(tmp); 155 | exit(1); 156 | } 157 | 158 | pw = strdup(pw); 159 | cpw = get_password("Re-type password: ", pass, sizeof(pass)); 160 | if (!pw || !cpw) 161 | goto fail; 162 | 163 | if (strcmp(pw, cpw) != 0) { 164 | fprintf(stderr, "Passwords do not match, aborting.\n"); 165 | goto error; 166 | } 167 | } 168 | 169 | srandom(time(NULL)); 170 | to64(&salt[index], random(), saltlen); 171 | 172 | cpw = crypt(pw, salt); 173 | if (cpw) 174 | fprintf(fp, "%s:%s\n", user, cpw); 175 | else 176 | fprintf(stderr, "crypt() returned NULL, sorry\n"); 177 | } 178 | 179 | static int activate_template(char *template, char *file) 180 | { 181 | char *buf; 182 | FILE *in, *out; 183 | 184 | in = fopen(template, "r"); 185 | if (!in) 186 | return 1; 187 | 188 | out = fopen(file, "w"); 189 | if (!out) { 190 | fclose(in); 191 | return 1; 192 | } 193 | 194 | buf = malloc(BUFSIZ); 195 | if (!buf) { 196 | fclose(in); 197 | fclose(out); 198 | return 1; 199 | } 200 | 201 | while (fgets(buf, BUFSIZ, in)) 202 | fputs(buf, out); 203 | 204 | free(buf); 205 | fclose(in); 206 | fclose(out); 207 | 208 | return 0; 209 | } 210 | 211 | static int usage(int code) 212 | { 213 | fprintf(stderr, "Usage: htpasswd [-c] passwordfile username\n"); 214 | fprintf(stderr, "The -c flag creates a new file.\n"); 215 | 216 | return code; 217 | } 218 | 219 | static void interrupted(int signo) 220 | { 221 | fprintf(stderr, "Interrupted.\n"); 222 | 223 | if (isatty(STDIN_FILENO)) 224 | tcsetattr(STDIN_FILENO, TCSANOW, &saved); 225 | 226 | if (tfd != -1) 227 | unlink(tmp); 228 | exit(1); 229 | } 230 | 231 | int main(int argc, char *argv[]) 232 | { 233 | int found; 234 | FILE *tfp, *f; 235 | char user[MAX_STRING_LEN]; 236 | char pwfilename[MAX_STRING_LEN]; 237 | char line[MAX_STRING_LEN]; 238 | char l[MAX_STRING_LEN]; 239 | char w[MAX_STRING_LEN]; 240 | 241 | if (isatty(STDIN_FILENO) && tcgetattr(STDIN_FILENO, &saved)) { 242 | fprintf(stderr, "Cannot query TTY status, sorry: %s\n", strerror(errno)); 243 | return 1; 244 | } 245 | 246 | tfd = -1; 247 | signal(SIGINT, interrupted); 248 | if (argc == 4) { 249 | if (strcmp(argv[1], "-c")) 250 | return usage(1); 251 | 252 | tfp = fopen(argv[2], "w"); 253 | if (!tfp) { 254 | fprintf(stderr, "Could not open passwd file %s for writing.\n", argv[2]); 255 | perror("fopen"); 256 | return 1; 257 | } 258 | 259 | if (strlen(argv[2]) > (sizeof(pwfilename) - 1)) { 260 | fprintf(stderr, "%s: filename is too long\n", argv[0]); 261 | fclose(tfp); 262 | return 1; 263 | } 264 | 265 | if (strchr(argv[2], ';') || strchr(argv[2], '>')) { 266 | fprintf(stderr, "%s: filename contains an illegal character\n", argv[0]); 267 | fclose(tfp); 268 | return 1; 269 | } 270 | 271 | if (strlen(argv[3]) > (sizeof(user) - 1)) { 272 | fprintf(stderr, "%s: username is too long\n", argv[0]); 273 | fclose(tfp); 274 | return 1; 275 | } 276 | 277 | if (strchr(argv[3], ':')) { 278 | fprintf(stderr, "%s: username contains an illegal character\n", argv[0]); 279 | fclose(tfp); 280 | return 1; 281 | } 282 | 283 | printf("Adding password for %s.\n", argv[3]); 284 | add_password(argv[3], tfp); 285 | fclose(tfp); 286 | return 0; 287 | } 288 | 289 | if (argc != 3) 290 | return usage(1); 291 | 292 | tfd = mkstemp(tmp); 293 | tfp = fdopen(tfd, "w"); 294 | if (!tfp) { 295 | fprintf(stderr, "Could not open temp file.\n"); 296 | close(tfd); 297 | return 1; 298 | } 299 | 300 | if (strlen(argv[1]) > (sizeof(pwfilename) - 1)) { 301 | fprintf(stderr, "%s: filename is too long\n", argv[0]); 302 | fclose(tfp); 303 | return 1; 304 | } 305 | 306 | if (((strchr(argv[1], ';')) != NULL) || ((strchr(argv[1], '>')) != NULL)) { 307 | fprintf(stderr, "%s: filename contains an illegal character\n", argv[0]); 308 | fclose(tfp); 309 | return 1; 310 | } 311 | 312 | if (strlen(argv[2]) > (sizeof(user) - 1)) { 313 | fprintf(stderr, "%s: username is too long\n", argv[0]); 314 | fclose(tfp); 315 | return 1; 316 | } 317 | 318 | if ((strchr(argv[2], ':')) != NULL) { 319 | fprintf(stderr, "%s: username contains an illegal character\n", argv[0]); 320 | fclose(tfp); 321 | return 1; 322 | } 323 | 324 | if (!(f = fopen(argv[1], "r"))) { 325 | fprintf(stderr, "Could not open passwd file %s for reading.\n", argv[1]); 326 | fprintf(stderr, "Use -c option to create new one.\n"); 327 | fclose(tfp); 328 | return 1; 329 | } 330 | strncpy(user, argv[2], sizeof(user) - 1); 331 | user[sizeof(user)-1] = '\0'; 332 | 333 | found = 0; 334 | while (!get_line(line, MAX_STRING_LEN, f)) { 335 | if (found || (line[0] == '#') || (!line[0])) { 336 | putline(tfp, line); 337 | continue; 338 | } 339 | 340 | strcpy(l, line); 341 | getword(w, l, ':'); 342 | if (strcmp(user, w)) { 343 | putline(tfp, line); 344 | continue; 345 | } 346 | 347 | printf("Changing password for user %s.\n", user); 348 | add_password(user, tfp); 349 | found = 1; 350 | } 351 | 352 | if (!found) { 353 | printf("Adding user %s.\n", user); 354 | add_password(user, tfp); 355 | } 356 | 357 | fclose(f); 358 | fclose(tfp); 359 | 360 | if (activate_template(tmp, argv[1])) 361 | fprintf(stderr, "Failed writing to %s: %s\n", argv[1], strerror(errno)); 362 | unlink(tmp); 363 | 364 | return 0; 365 | } 366 | -------------------------------------------------------------------------------- /src/make_mime.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | #Run this on developer side, whenever you update 4 | #your mime encodings, or mime types. 5 | 6 | open(ENCODINGS, '<', "mime_encodings.txt"); 7 | @encoding=; 8 | close(ENCODINGS); 9 | 10 | open(ENCHEADER, '>', "mime_encodings.h"); 11 | foreach (@encoding) 12 | { 13 | chomp($_); 14 | @element = split(/\t+/,$_); 15 | next if $element[0] =~ /#/ ; 16 | next if $element[1] =~ /#/ ; 17 | next if length($element[0]) == 0 || length($element[1]) == 0 ; 18 | print ENCHEADER '{ "', $element[0], '", 0, "', $element[1], '", 0 },', "\n"; 19 | } 20 | close(ENCHEADER); 21 | 22 | 23 | open(TYPES, '<', "mime_types.txt"); 24 | @type=; 25 | close(TYPES); 26 | 27 | open(TYPEHEADER, '>', "mime_types.h"); 28 | foreach (@type) 29 | { 30 | chomp($_); 31 | @element = split(/\t+/,$_); 32 | next if $element[0] =~ /#/ ; 33 | next if $element[1] =~ /#/ ; 34 | next if length($element[0]) == 0 || length($element[1]) == 0 ; 35 | print TYPEHEADER '{ "', $element[0], '", 0, "', $element[1], '", 0 },', "\n"; 36 | } 37 | close(TYPEHEADER); 38 | -------------------------------------------------------------------------------- /src/match.c: -------------------------------------------------------------------------------- 1 | /* match.c - simple shell-style filename matcher 2 | ** 3 | ** Only does ? * and **, and multiple patterns separated by |. Returns 1 or 0. 4 | ** 5 | ** Copyright (C) 1995-2015 Jef Poskanzer 6 | ** All rights reserved. 7 | ** 8 | ** Redistribution and use in source and binary forms, with or without 9 | ** modification, are permitted provided that the following conditions 10 | ** are met: 11 | ** 1. Redistributions of source code must retain the above copyright 12 | ** notice, this list of conditions and the following disclaimer. 13 | ** 2. Redistributions in binary form must reproduce the above copyright 14 | ** notice, this list of conditions and the following disclaimer in the 15 | ** documentation and/or other materials provided with the distribution. 16 | ** 17 | ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | ** AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | ** ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE 21 | ** LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | ** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | ** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | ** INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | ** CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | ** ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 27 | ** THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #include 31 | #include 32 | #include "match.h" 33 | 34 | static int match_one(const char *pattern, int patternlen, const char *string); 35 | 36 | int match(const char *pattern, const char *string) 37 | { 38 | if (!pattern) 39 | return 0; 40 | 41 | for (;;) { 42 | const char *or; 43 | int rc; 44 | 45 | or = strchr(pattern, '|'); 46 | if (!or) 47 | return match_one(pattern, strlen(pattern), string); 48 | 49 | rc = match_one(pattern, or - pattern, string); 50 | if (rc) 51 | return rc; 52 | 53 | pattern = or + 1; 54 | } 55 | } 56 | 57 | 58 | static int match_one(const char *pattern, int patternlen, const char *string) 59 | { 60 | const char *p, *s; 61 | 62 | for (p = pattern, s = string; p - pattern < patternlen; ++p, ++s) { 63 | if (*p == '?' && *s != '\0') 64 | continue; 65 | 66 | if (*p == '*') { 67 | int i, pl; 68 | 69 | ++p; 70 | if (*p == '*') { 71 | /* Double-wildcard matches anything. */ 72 | ++p; 73 | i = strlen(s); 74 | } else { 75 | /* Single-wildcard matches anything but slash. */ 76 | i = strcspn(s, "/"); 77 | } 78 | 79 | pl = patternlen - (p - pattern); 80 | for (; i >= 0; --i) { 81 | if (match_one(p, pl, &(s[i]))) { 82 | int len = s - string; 83 | 84 | return len > 1 ? len : 1; 85 | } 86 | } 87 | 88 | return 0; 89 | } 90 | 91 | if (*p != *s) 92 | return 0; 93 | } 94 | 95 | if (*s == '\0') 96 | return 1; 97 | 98 | return 0; 99 | } 100 | -------------------------------------------------------------------------------- /src/match.h: -------------------------------------------------------------------------------- 1 | /* match.h - simple shell-style filename patcher 2 | ** 3 | ** Copyright (C) 1995-2015 Jef Poskanzer 4 | ** All rights reserved. 5 | ** 6 | ** Redistribution and use in source and binary forms, with or without 7 | ** modification, are permitted provided that the following conditions 8 | ** are met: 9 | ** 1. Redistributions of source code must retain the above copyright 10 | ** notice, this list of conditions and the following disclaimer. 11 | ** 2. Redistributions in binary form must reproduce the above copyright 12 | ** notice, this list of conditions and the following disclaimer in the 13 | ** documentation and/or other materials provided with the distribution. 14 | ** 15 | ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | ** AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | ** ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE 19 | ** LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 20 | ** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 21 | ** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 | ** INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 | ** CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 24 | ** ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 25 | ** THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #ifndef MATCH_H_ 29 | #define MATCH_H_ 30 | 31 | /* Simple shell-style filename pattern matcher. Only does ? * and **, and 32 | ** multiple patterns separated by |. Returns 1 or 0. 33 | */ 34 | extern int match(const char *pattern, const char *string); 35 | 36 | #endif /* MATCH_H_ */ 37 | -------------------------------------------------------------------------------- /src/md5.c: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: md5.c,v 1.11 2015/09/11 09:18:27 guenther Exp $ */ 2 | 3 | /* 4 | * This code implements the MD5 message-digest algorithm. 5 | * The algorithm is due to Ron Rivest. This code was 6 | * written by Colin Plumb in 1993, no copyright is claimed. 7 | * This code is in the public domain; do with it what you wish. 8 | * 9 | * Equivalent code is available from RSA Data Security, Inc. 10 | * This code has been tested against that, and is equivalent, 11 | * except that you don't need to include two pages of legalese 12 | * with every copy. 13 | * 14 | * To compute the message digest of a chunk of bytes, declare an 15 | * MD5Context structure, pass it to MD5Init, call MD5Update as 16 | * needed on buffers full of bytes, and then call MD5Final, which 17 | * will fill a supplied 16-byte array with the digest. 18 | */ 19 | 20 | #include 21 | #include 22 | #include "md5.h" 23 | 24 | #define PUT_64BIT_LE(cp, value) do { \ 25 | (cp)[7] = (value) >> 56; \ 26 | (cp)[6] = (value) >> 48; \ 27 | (cp)[5] = (value) >> 40; \ 28 | (cp)[4] = (value) >> 32; \ 29 | (cp)[3] = (value) >> 24; \ 30 | (cp)[2] = (value) >> 16; \ 31 | (cp)[1] = (value) >> 8; \ 32 | (cp)[0] = (value); } while (0) 33 | 34 | #define PUT_32BIT_LE(cp, value) do { \ 35 | (cp)[3] = (value) >> 24; \ 36 | (cp)[2] = (value) >> 16; \ 37 | (cp)[1] = (value) >> 8; \ 38 | (cp)[0] = (value); } while (0) 39 | 40 | static u_int8_t PADDING[MD5_BLOCK_LENGTH] = { 41 | 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 43 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 44 | }; 45 | 46 | /* 47 | * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious 48 | * initialization constants. 49 | */ 50 | void 51 | MD5Init(MD5_CTX *ctx) 52 | { 53 | ctx->count = 0; 54 | ctx->state[0] = 0x67452301; 55 | ctx->state[1] = 0xefcdab89; 56 | ctx->state[2] = 0x98badcfe; 57 | ctx->state[3] = 0x10325476; 58 | } 59 | 60 | /* 61 | * Update context to reflect the concatenation of another buffer full 62 | * of bytes. 63 | */ 64 | void 65 | MD5Update(MD5_CTX *ctx, const unsigned char *input, size_t len) 66 | { 67 | size_t have, need; 68 | 69 | /* Check how many bytes we already have and how many more we need. */ 70 | have = (size_t)((ctx->count >> 3) & (MD5_BLOCK_LENGTH - 1)); 71 | need = MD5_BLOCK_LENGTH - have; 72 | 73 | /* Update bitcount */ 74 | ctx->count += (u_int64_t)len << 3; 75 | 76 | if (len >= need) { 77 | if (have != 0) { 78 | memcpy(ctx->buffer + have, input, need); 79 | MD5Transform(ctx->state, ctx->buffer); 80 | input += need; 81 | len -= need; 82 | have = 0; 83 | } 84 | 85 | /* Process data in MD5_BLOCK_LENGTH-byte chunks. */ 86 | while (len >= MD5_BLOCK_LENGTH) { 87 | MD5Transform(ctx->state, input); 88 | input += MD5_BLOCK_LENGTH; 89 | len -= MD5_BLOCK_LENGTH; 90 | } 91 | } 92 | 93 | /* Handle any remaining bytes of data. */ 94 | if (len != 0) 95 | memcpy(ctx->buffer + have, input, len); 96 | } 97 | 98 | /* 99 | * Pad pad to 64-byte boundary with the bit pattern 100 | * 1 0* (64-bit count of bits processed, MSB-first) 101 | */ 102 | void 103 | MD5Pad(MD5_CTX *ctx) 104 | { 105 | u_int8_t count[8]; 106 | size_t padlen; 107 | 108 | /* Convert count to 8 bytes in little endian order. */ 109 | PUT_64BIT_LE(count, ctx->count); 110 | 111 | /* Pad out to 56 mod 64. */ 112 | padlen = MD5_BLOCK_LENGTH - 113 | ((ctx->count >> 3) & (MD5_BLOCK_LENGTH - 1)); 114 | if (padlen < 1 + 8) 115 | padlen += MD5_BLOCK_LENGTH; 116 | MD5Update(ctx, PADDING, padlen - 8); /* padlen - 8 <= 64 */ 117 | MD5Update(ctx, count, 8); 118 | } 119 | 120 | /* 121 | * Final wrapup--call MD5Pad, fill in digest and zero out ctx. 122 | */ 123 | void 124 | MD5Final(unsigned char digest[MD5_DIGEST_LENGTH], MD5_CTX *ctx) 125 | { 126 | int i; 127 | 128 | MD5Pad(ctx); 129 | for (i = 0; i < 4; i++) 130 | PUT_32BIT_LE(digest + i * 4, ctx->state[i]); 131 | memset(ctx, 0, sizeof(*ctx)); 132 | } 133 | 134 | 135 | /* The four core functions - F1 is optimized somewhat */ 136 | 137 | /* #define F1(x, y, z) (x & y | ~x & z) */ 138 | #define F1(x, y, z) (z ^ (x & (y ^ z))) 139 | #define F2(x, y, z) F1(z, x, y) 140 | #define F3(x, y, z) (x ^ y ^ z) 141 | #define F4(x, y, z) (y ^ (x | ~z)) 142 | 143 | /* This is the central step in the MD5 algorithm. */ 144 | #define MD5STEP(f, w, x, y, z, data, s) \ 145 | ( w += f(x, y, z) + data, w = w<>(32-s), w += x ) 146 | 147 | /* 148 | * The core of the MD5 algorithm, this alters an existing MD5 hash to 149 | * reflect the addition of 16 longwords of new data. MD5Update blocks 150 | * the data and converts bytes into longwords for this routine. 151 | */ 152 | void 153 | MD5Transform(u_int32_t state[4], const u_int8_t block[MD5_BLOCK_LENGTH]) 154 | { 155 | u_int32_t a, b, c, d, in[MD5_BLOCK_LENGTH / 4]; 156 | 157 | #if BYTE_ORDER == LITTLE_ENDIAN 158 | memcpy(in, block, sizeof(in)); 159 | #else 160 | for (a = 0; a < MD5_BLOCK_LENGTH / 4; a++) { 161 | in[a] = (u_int32_t)( 162 | (u_int32_t)(block[a * 4 + 0]) | 163 | (u_int32_t)(block[a * 4 + 1]) << 8 | 164 | (u_int32_t)(block[a * 4 + 2]) << 16 | 165 | (u_int32_t)(block[a * 4 + 3]) << 24); 166 | } 167 | #endif 168 | 169 | a = state[0]; 170 | b = state[1]; 171 | c = state[2]; 172 | d = state[3]; 173 | 174 | MD5STEP(F1, a, b, c, d, in[ 0] + 0xd76aa478, 7); 175 | MD5STEP(F1, d, a, b, c, in[ 1] + 0xe8c7b756, 12); 176 | MD5STEP(F1, c, d, a, b, in[ 2] + 0x242070db, 17); 177 | MD5STEP(F1, b, c, d, a, in[ 3] + 0xc1bdceee, 22); 178 | MD5STEP(F1, a, b, c, d, in[ 4] + 0xf57c0faf, 7); 179 | MD5STEP(F1, d, a, b, c, in[ 5] + 0x4787c62a, 12); 180 | MD5STEP(F1, c, d, a, b, in[ 6] + 0xa8304613, 17); 181 | MD5STEP(F1, b, c, d, a, in[ 7] + 0xfd469501, 22); 182 | MD5STEP(F1, a, b, c, d, in[ 8] + 0x698098d8, 7); 183 | MD5STEP(F1, d, a, b, c, in[ 9] + 0x8b44f7af, 12); 184 | MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); 185 | MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); 186 | MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); 187 | MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); 188 | MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); 189 | MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); 190 | 191 | MD5STEP(F2, a, b, c, d, in[ 1] + 0xf61e2562, 5); 192 | MD5STEP(F2, d, a, b, c, in[ 6] + 0xc040b340, 9); 193 | MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); 194 | MD5STEP(F2, b, c, d, a, in[ 0] + 0xe9b6c7aa, 20); 195 | MD5STEP(F2, a, b, c, d, in[ 5] + 0xd62f105d, 5); 196 | MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); 197 | MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); 198 | MD5STEP(F2, b, c, d, a, in[ 4] + 0xe7d3fbc8, 20); 199 | MD5STEP(F2, a, b, c, d, in[ 9] + 0x21e1cde6, 5); 200 | MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); 201 | MD5STEP(F2, c, d, a, b, in[ 3] + 0xf4d50d87, 14); 202 | MD5STEP(F2, b, c, d, a, in[ 8] + 0x455a14ed, 20); 203 | MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); 204 | MD5STEP(F2, d, a, b, c, in[ 2] + 0xfcefa3f8, 9); 205 | MD5STEP(F2, c, d, a, b, in[ 7] + 0x676f02d9, 14); 206 | MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); 207 | 208 | MD5STEP(F3, a, b, c, d, in[ 5] + 0xfffa3942, 4); 209 | MD5STEP(F3, d, a, b, c, in[ 8] + 0x8771f681, 11); 210 | MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); 211 | MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); 212 | MD5STEP(F3, a, b, c, d, in[ 1] + 0xa4beea44, 4); 213 | MD5STEP(F3, d, a, b, c, in[ 4] + 0x4bdecfa9, 11); 214 | MD5STEP(F3, c, d, a, b, in[ 7] + 0xf6bb4b60, 16); 215 | MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); 216 | MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); 217 | MD5STEP(F3, d, a, b, c, in[ 0] + 0xeaa127fa, 11); 218 | MD5STEP(F3, c, d, a, b, in[ 3] + 0xd4ef3085, 16); 219 | MD5STEP(F3, b, c, d, a, in[ 6] + 0x04881d05, 23); 220 | MD5STEP(F3, a, b, c, d, in[ 9] + 0xd9d4d039, 4); 221 | MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); 222 | MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); 223 | MD5STEP(F3, b, c, d, a, in[2 ] + 0xc4ac5665, 23); 224 | 225 | MD5STEP(F4, a, b, c, d, in[ 0] + 0xf4292244, 6); 226 | MD5STEP(F4, d, a, b, c, in[7 ] + 0x432aff97, 10); 227 | MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); 228 | MD5STEP(F4, b, c, d, a, in[5 ] + 0xfc93a039, 21); 229 | MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); 230 | MD5STEP(F4, d, a, b, c, in[3 ] + 0x8f0ccc92, 10); 231 | MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); 232 | MD5STEP(F4, b, c, d, a, in[1 ] + 0x85845dd1, 21); 233 | MD5STEP(F4, a, b, c, d, in[8 ] + 0x6fa87e4f, 6); 234 | MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); 235 | MD5STEP(F4, c, d, a, b, in[6 ] + 0xa3014314, 15); 236 | MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); 237 | MD5STEP(F4, a, b, c, d, in[4 ] + 0xf7537e82, 6); 238 | MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); 239 | MD5STEP(F4, c, d, a, b, in[2 ] + 0x2ad7d2bb, 15); 240 | MD5STEP(F4, b, c, d, a, in[9 ] + 0xeb86d391, 21); 241 | 242 | state[0] += a; 243 | state[1] += b; 244 | state[2] += c; 245 | state[3] += d; 246 | } 247 | -------------------------------------------------------------------------------- /src/md5.h: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: md5.h,v 1.17 2012/12/05 23:19:57 deraadt Exp $ */ 2 | 3 | /* 4 | * This code implements the MD5 message-digest algorithm. 5 | * The algorithm is due to Ron Rivest. This code was 6 | * written by Colin Plumb in 1993, no copyright is claimed. 7 | * This code is in the public domain; do with it what you wish. 8 | * 9 | * Equivalent code is available from RSA Data Security, Inc. 10 | * This code has been tested against that, and is equivalent, 11 | * except that you don't need to include two pages of legalese 12 | * with every copy. 13 | */ 14 | 15 | #ifndef _MD5_H_ 16 | #define _MD5_H_ 17 | 18 | #define MD5_BLOCK_LENGTH 64 19 | #define MD5_DIGEST_LENGTH 16 20 | #define MD5_DIGEST_STRING_LENGTH (MD5_DIGEST_LENGTH * 2 + 1) 21 | 22 | typedef struct MD5Context { 23 | u_int32_t state[4]; /* state */ 24 | u_int64_t count; /* number of bits, mod 2^64 */ 25 | u_int8_t buffer[MD5_BLOCK_LENGTH]; /* input buffer */ 26 | } MD5_CTX; 27 | 28 | void MD5Init(MD5_CTX *); 29 | void MD5Update(MD5_CTX *, const u_int8_t *, size_t); 30 | void MD5Pad(MD5_CTX *); 31 | void MD5Final(u_int8_t [MD5_DIGEST_LENGTH], MD5_CTX *); 32 | void MD5Transform(u_int32_t [4], const u_int8_t [MD5_BLOCK_LENGTH]); 33 | char *MD5End(MD5_CTX *, char *); 34 | char *MD5File(const char *, char *); 35 | char *MD5FileChunk(const char *, char *, off_t, off_t); 36 | char *MD5Data(const u_int8_t *, size_t, char *); 37 | 38 | #endif /* _MD5_H_ */ 39 | -------------------------------------------------------------------------------- /src/mime_encodings.h: -------------------------------------------------------------------------------- 1 | { "gz", 0, "gzip", 0 }, 2 | { "xz", 0, "xz", 0 }, 3 | { "bz2", 0, "bzip2", 0 }, 4 | { "svgz", 0, "gzip", 0 }, 5 | { "Z", 0, "compress", 0 }, 6 | { "uu", 0, "x-uuencode", 0 }, 7 | -------------------------------------------------------------------------------- /src/mime_encodings.txt: -------------------------------------------------------------------------------- 1 | # mime_encodings.txt 2 | # 3 | # A list of file extensions followed by the corresponding MIME encoding. 4 | # Extensions not found in the table proceed to the mime_types table. 5 | 6 | gz gzip 7 | xz xz 8 | bz2 bzip2 9 | svgz gzip 10 | Z compress 11 | uu x-uuencode 12 | -------------------------------------------------------------------------------- /src/mime_types.h: -------------------------------------------------------------------------------- 1 | { "7z", 0, "application/x-7z-compressed", 0 }, 2 | { "a", 0, "application/octet-stream", 0 }, 3 | { "aab", 0, "application/x-authorware-bin", 0 }, 4 | { "aam", 0, "application/x-authorware-map", 0 }, 5 | { "aas", 0, "application/x-authorware-seg", 0 }, 6 | { "ai", 0, "application/postscript", 0 }, 7 | { "aif", 0, "audio/x-aiff", 0 }, 8 | { "aifc", 0, "audio/x-aiff", 0 }, 9 | { "aiff", 0, "audio/x-aiff", 0 }, 10 | { "arc", 0, "application/octet-stream", 0 }, 11 | { "arj", 0, "application/octet-stream", 0 }, 12 | { "asc", 0, "text/plain; charset=%s", 0 }, 13 | { "asf", 0, "video/x-ms-asf", 0 }, 14 | { "asx", 0, "video/x-ms-asf", 0 }, 15 | { "au", 0, "audio/basic", 0 }, 16 | { "avi", 0, "video/x-msvideo", 0 }, 17 | { "avif", 0, "image/avif", 0 }, 18 | { "bcpio", 0, "application/x-bcpio", 0 }, 19 | { "bin", 0, "application/octet-stream", 0 }, 20 | { "bmp", 0, "image/bmp", 0 }, 21 | { "bz2", 0, "application/x-bzip2", 0 }, 22 | { "cdf", 0, "application/x-netcdf", 0 }, 23 | { "class", 0, "application/x-java-vm", 0 }, 24 | { "cpio", 0, "application/x-cpio", 0 }, 25 | { "cpt", 0, "application/mac-compactpro", 0 }, 26 | { "crl", 0, "application/x-pkcs7-crl", 0 }, 27 | { "cer", 0, "application/x-x509-ca-cert", 0 }, 28 | { "crt", 0, "application/x-x509-ca-cert", 0 }, 29 | { "csh", 0, "application/x-csh", 0 }, 30 | { "css", 0, "text/css; charset=%s", 0 }, 31 | { "dcr", 0, "application/x-director", 0 }, 32 | { "der", 0, "application/x-x509-ca-cert", 0 }, 33 | { "dir", 0, "application/x-director", 0 }, 34 | { "disco", 0, "text/xml", 0 }, 35 | { "djv", 0, "image/vnd.djvu", 0 }, 36 | { "djvu", 0, "image/vnd.djvu", 0 }, 37 | { "dll", 0, "application/octet-stream", 0 }, 38 | { "dms", 0, "application/octet-stream", 0 }, 39 | { "doc", 0, "application/msword", 0 }, 40 | { "dtd", 0, "text/xml; charset=%s", 0 }, 41 | { "dump", 0, "application/octet-stream", 0 }, 42 | { "dvi", 0, "application/x-dvi", 0 }, 43 | { "dxr", 0, "application/x-director", 0 }, 44 | { "eps", 0, "application/postscript", 0 }, 45 | { "etx", 0, "text/x-setext", 0 }, 46 | { "exe", 0, "application/octet-stream", 0 }, 47 | { "ez", 0, "application/andrew-inset", 0 }, 48 | { "fgd", 0, "application/x-director", 0 }, 49 | { "fh", 0, "image/x-freehand", 0 }, 50 | { "fh4", 0, "image/x-freehand", 0 }, 51 | { "fh5", 0, "image/x-freehand", 0 }, 52 | { "fh7", 0, "image/x-freehand", 0 }, 53 | { "fhc", 0, "image/x-freehand", 0 }, 54 | { "gif", 0, "image/gif", 0 }, 55 | { "gtar", 0, "application/x-gtar", 0 }, 56 | { "gz", 0, "application/gzip", 0 }, 57 | { "hdf", 0, "application/x-hdf", 0 }, 58 | { "hqx", 0, "application/mac-binhex40", 0 }, 59 | { "htm", 0, "text/html; charset=%s", 0 }, 60 | { "html", 0, "text/html; charset=%s", 0 }, 61 | { "ice", 0, "x-conference/x-cooltalk", 0 }, 62 | { "ico", 0, "image/x-icon", 0 }, 63 | { "ief", 0, "image/ief", 0 }, 64 | { "iges", 0, "model/iges", 0 }, 65 | { "igs", 0, "model/iges", 0 }, 66 | { "iv", 0, "application/x-inventor", 0 }, 67 | { "jar", 0, "application/x-java-archive", 0 }, 68 | { "jfif", 0, "image/jpeg", 0 }, 69 | { "jpe", 0, "image/jpeg", 0 }, 70 | { "jpeg", 0, "image/jpeg", 0 }, 71 | { "jpg", 0, "image/jpeg", 0 }, 72 | { "js", 0, "application/javascript", 0 }, 73 | { "kar", 0, "audio/midi", 0 }, 74 | { "kml", 0, "application/vnd.google-earth.kml+xml", 0 }, 75 | { "kmz", 0, "application/vnd.google-earth.kmz", 0 }, 76 | { "latex", 0, "application/x-latex", 0 }, 77 | { "lha", 0, "application/octet-stream", 0 }, 78 | { "loc", 0, "application/xml-loc", 0 }, 79 | { "lzh", 0, "application/octet-stream", 0 }, 80 | { "m3u", 0, "audio/x-mpegurl", 0 }, 81 | { "man", 0, "application/x-troff-man", 0 }, 82 | { "mathml", 0, "application/mathml+xml", 0 }, 83 | { "me", 0, "application/x-troff-me", 0 }, 84 | { "mesh", 0, "model/mesh", 0 }, 85 | { "mid", 0, "audio/midi", 0 }, 86 | { "midi", 0, "audio/midi", 0 }, 87 | { "mif", 0, "application/vnd.mif", 0 }, 88 | { "mime", 0, "message/rfc822", 0 }, 89 | { "mml", 0, "application/mathml+xml", 0 }, 90 | { "mov", 0, "video/quicktime", 0 }, 91 | { "movie", 0, "video/x-sgi-movie", 0 }, 92 | { "mp2", 0, "audio/mpeg", 0 }, 93 | { "mp3", 0, "audio/mpeg", 0 }, 94 | { "mp4", 0, "video/mp4", 0 }, 95 | { "mpe", 0, "video/mpeg", 0 }, 96 | { "mpeg", 0, "video/mpeg", 0 }, 97 | { "mpg", 0, "video/mpeg", 0 }, 98 | { "mpga", 0, "audio/mpeg", 0 }, 99 | { "ms", 0, "application/x-troff-ms", 0 }, 100 | { "msh", 0, "model/mesh", 0 }, 101 | { "mv", 0, "video/x-sgi-movie", 0 }, 102 | { "mxu", 0, "video/vnd.mpegurl", 0 }, 103 | { "nc", 0, "application/x-netcdf", 0 }, 104 | { "o", 0, "application/octet-stream", 0 }, 105 | { "oda", 0, "application/oda", 0 }, 106 | { "ogg", 0, "application/ogg", 0 }, 107 | { "ogv", 0, "video/ogg", 0 }, 108 | { "ogx", 0, "application/ogg", 0 }, 109 | { "pac", 0, "application/x-ns-proxy-autoconfig", 0 }, 110 | { "pbm", 0, "image/x-portable-bitmap", 0 }, 111 | { "pdb", 0, "chemical/x-pdb", 0 }, 112 | { "pdf", 0, "application/pdf", 0 }, 113 | { "pgm", 0, "image/x-portable-graymap", 0 }, 114 | { "pgn", 0, "application/x-chess-pgn", 0 }, 115 | { "png", 0, "image/png", 0 }, 116 | { "pnm", 0, "image/x-portable-anymap", 0 }, 117 | { "ppm", 0, "image/x-portable-pixmap", 0 }, 118 | { "ppt", 0, "application/vnd.ms-powerpoint", 0 }, 119 | { "ps", 0, "application/postscript", 0 }, 120 | { "qt", 0, "video/quicktime", 0 }, 121 | { "ra", 0, "audio/x-realaudio", 0 }, 122 | { "ram", 0, "audio/x-pn-realaudio", 0 }, 123 | { "ras", 0, "image/x-cmu-raster", 0 }, 124 | { "rdf", 0, "application/rdf+xml", 0 }, 125 | { "rgb", 0, "image/x-rgb", 0 }, 126 | { "rm", 0, "audio/x-pn-realaudio", 0 }, 127 | { "roff", 0, "application/x-troff", 0 }, 128 | { "rpm", 0, "audio/x-pn-realaudio-plugin", 0 }, 129 | { "rss", 0, "application/rss+xml", 0 }, 130 | { "rtf", 0, "text/rtf; charset=%s", 0 }, 131 | { "rtx", 0, "text/richtext; charset=%s", 0 }, 132 | { "sfx", 0, "application/octet-stream", 0 }, 133 | { "sgm", 0, "text/sgml; charset=%s", 0 }, 134 | { "sgml", 0, "text/sgml; charset=%s", 0 }, 135 | { "sh", 0, "application/x-sh", 0 }, 136 | { "shar", 0, "application/x-shar", 0 }, 137 | { "sig", 0, "application/pgp-signature", 0 }, 138 | { "silo", 0, "model/mesh", 0 }, 139 | { "sit", 0, "application/x-stuffit", 0 }, 140 | { "skd", 0, "application/x-koan", 0 }, 141 | { "skm", 0, "application/x-koan", 0 }, 142 | { "skp", 0, "application/x-koan", 0 }, 143 | { "skt", 0, "application/x-koan", 0 }, 144 | { "smi", 0, "application/smil", 0 }, 145 | { "smil", 0, "application/smil", 0 }, 146 | { "snd", 0, "audio/basic", 0 }, 147 | { "so", 0, "application/octet-stream", 0 }, 148 | { "spl", 0, "application/x-futuresplash", 0 }, 149 | { "src", 0, "application/x-wais-source", 0 }, 150 | { "stc", 0, "application/vnd.sun.xml.calc.template", 0 }, 151 | { "std", 0, "application/vnd.sun.xml.draw.template", 0 }, 152 | { "sti", 0, "application/vnd.sun.xml.impress.template", 0 }, 153 | { "stw", 0, "application/vnd.sun.xml.writer.template", 0 }, 154 | { "sv4cpio", 0, "application/x-sv4cpio", 0 }, 155 | { "sv4crc", 0, "application/x-sv4crc", 0 }, 156 | { "svg", 0, "image/svg+xml", 0 }, 157 | { "svgx", 0, "image/svg+xml", 0 }, 158 | { "svgz", 0, "image/svg+xml", 0 }, 159 | { "swf", 0, "application/x-shockwave-flash", 0 }, 160 | { "sxc", 0, "application/vnd.sun.xml.calc", 0 }, 161 | { "sxd", 0, "application/vnd.sun.xml.draw", 0 }, 162 | { "sxg", 0, "application/vnd.sun.xml.writer.global", 0 }, 163 | { "sxi", 0, "application/vnd.sun.xml.impress", 0 }, 164 | { "sxm", 0, "application/vnd.sun.xml.math", 0 }, 165 | { "sxw", 0, "application/vnd.sun.xml.writer", 0 }, 166 | { "t", 0, "application/x-troff", 0 }, 167 | { "tar", 0, "application/x-tar", 0 }, 168 | { "taz", 0, "application/octet-stream", 0 }, 169 | { "tgz", 0, "application/gzip", 0 }, 170 | { "tbz2", 0, "application/x-bzip2", 0 }, 171 | { "txz", 0, "application/x-xz", 0 }, 172 | { "tcl", 0, "application/x-tcl", 0 }, 173 | { "tex", 0, "application/x-tex", 0 }, 174 | { "texi", 0, "application/x-texinfo", 0 }, 175 | { "texinfo", 0, "application/x-texinfo", 0 }, 176 | { "tif", 0, "image/tiff", 0 }, 177 | { "tiff", 0, "image/tiff", 0 }, 178 | { "torrent", 0, "application/x-bittorrent", 0 }, 179 | { "tr", 0, "application/x-troff", 0 }, 180 | { "tsp", 0, "application/dsptype", 0 }, 181 | { "tsv", 0, "text/tab-separated-values; charset=%s", 0 }, 182 | { "txt", 0, "text/plain; charset=%s", 0 }, 183 | { "tz", 0, "application/octet-stream", 0 }, 184 | { "ustar", 0, "application/x-ustar", 0 }, 185 | { "vcd", 0, "application/x-cdlink", 0 }, 186 | { "vrml", 0, "model/vrml", 0 }, 187 | { "vx", 0, "video/x-rad-screenplay", 0 }, 188 | { "wav", 0, "audio/x-wav", 0 }, 189 | { "wax", 0, "audio/x-ms-wax", 0 }, 190 | { "wbmp", 0, "image/vnd.wap.wbmp", 0 }, 191 | { "wbxml", 0, "application/vnd.wap.wbxml", 0 }, 192 | { "webm", 0, "video/webm", 0 }, 193 | { "webp", 0, "image/webp", 0 }, 194 | { "wm", 0, "video/x-ms-wm", 0 }, 195 | { "wma", 0, "audio/x-ms-wma", 0 }, 196 | { "wmd", 0, "application/x-ms-wmd", 0 }, 197 | { "wml", 0, "text/vnd.wap.wml", 0 }, 198 | { "wmlc", 0, "application/vnd.wap.wmlc", 0 }, 199 | { "wmls", 0, "text/vnd.wap.wmlscript", 0 }, 200 | { "wmlsc", 0, "application/vnd.wap.wmlscriptc", 0 }, 201 | { "wmv", 0, "video/x-ms-wmv", 0 }, 202 | { "wmx", 0, "video/x-ms-wmx", 0 }, 203 | { "wmz", 0, "application/x-ms-wmz", 0 }, 204 | { "wrl", 0, "model/vrml", 0 }, 205 | { "wsrc", 0, "application/x-wais-source", 0 }, 206 | { "wvx", 0, "video/x-ms-wvx", 0 }, 207 | { "xbm", 0, "image/x-xbitmap", 0 }, 208 | { "xht", 0, "application/xhtml+xml; charset=%s", 0 }, 209 | { "xhtml", 0, "application/xhtml+xml; charset=%s", 0 }, 210 | { "xls", 0, "application/vnd.ms-excel", 0 }, 211 | { "xml", 0, "text/xml; charset=%s", 0 }, 212 | { "xpi", 0, "application/x-xpinstall", 0 }, 213 | { "xpm", 0, "image/x-xpixmap", 0 }, 214 | { "xsd", 0, "text/xml; charset=%s", 0 }, 215 | { "xsl", 0, "text/xml; charset=%s", 0 }, 216 | { "xslt", 0, "text/xml; charset=%s", 0 }, 217 | { "xul", 0, "application/vnd.mozilla.xul+xml", 0 }, 218 | { "xwd", 0, "image/x-xwindowdump", 0 }, 219 | { "xz", 0, "application/x-xz", 0 }, 220 | { "xyz", 0, "chemical/x-xyz", 0 }, 221 | { "zip", 0, "application/zip", 0 }, 222 | { "zoo", 0, "application/octet-stream", 0 }, 223 | -------------------------------------------------------------------------------- /src/mime_types.txt: -------------------------------------------------------------------------------- 1 | # mime_types.txt 2 | # 3 | # A list of file extensions followed by the corresponding MIME type. 4 | # Extensions not found in the table are returned as text/plain. 5 | 6 | 7z application/x-7z-compressed 7 | a application/octet-stream 8 | aab application/x-authorware-bin 9 | aam application/x-authorware-map 10 | aas application/x-authorware-seg 11 | ai application/postscript 12 | aif audio/x-aiff 13 | aifc audio/x-aiff 14 | aiff audio/x-aiff 15 | arc application/octet-stream 16 | arj application/octet-stream 17 | asc text/plain; charset=%s 18 | asf video/x-ms-asf 19 | asx video/x-ms-asf 20 | au audio/basic 21 | avi video/x-msvideo 22 | avif image/avif 23 | bcpio application/x-bcpio 24 | bin application/octet-stream 25 | bmp image/bmp 26 | bz2 application/x-bzip2 27 | cdf application/x-netcdf 28 | class application/x-java-vm 29 | cpio application/x-cpio 30 | cpt application/mac-compactpro 31 | crl application/x-pkcs7-crl 32 | cer application/x-x509-ca-cert 33 | crt application/x-x509-ca-cert 34 | csh application/x-csh 35 | css text/css; charset=%s 36 | dcr application/x-director 37 | der application/x-x509-ca-cert 38 | dir application/x-director 39 | disco text/xml 40 | djv image/vnd.djvu 41 | djvu image/vnd.djvu 42 | dll application/octet-stream 43 | dms application/octet-stream 44 | doc application/msword 45 | dtd text/xml; charset=%s 46 | dump application/octet-stream 47 | dvi application/x-dvi 48 | dxr application/x-director 49 | eps application/postscript 50 | etx text/x-setext 51 | exe application/octet-stream 52 | ez application/andrew-inset 53 | fgd application/x-director 54 | fh image/x-freehand 55 | fh4 image/x-freehand 56 | fh5 image/x-freehand 57 | fh7 image/x-freehand 58 | fhc image/x-freehand 59 | gif image/gif 60 | gtar application/x-gtar 61 | gz application/gzip 62 | hdf application/x-hdf 63 | hqx application/mac-binhex40 64 | htm text/html; charset=%s 65 | html text/html; charset=%s 66 | ice x-conference/x-cooltalk 67 | ico image/x-icon 68 | ief image/ief 69 | iges model/iges 70 | igs model/iges 71 | iv application/x-inventor 72 | jar application/x-java-archive 73 | jfif image/jpeg 74 | jpe image/jpeg 75 | jpeg image/jpeg 76 | jpg image/jpeg 77 | js application/javascript 78 | kar audio/midi 79 | kml application/vnd.google-earth.kml+xml 80 | kmz application/vnd.google-earth.kmz 81 | latex application/x-latex 82 | lha application/octet-stream 83 | loc application/xml-loc 84 | lzh application/octet-stream 85 | m3u audio/x-mpegurl 86 | man application/x-troff-man 87 | mathml application/mathml+xml 88 | me application/x-troff-me 89 | mesh model/mesh 90 | mid audio/midi 91 | midi audio/midi 92 | mif application/vnd.mif 93 | mime message/rfc822 94 | mml application/mathml+xml 95 | mov video/quicktime 96 | movie video/x-sgi-movie 97 | mp2 audio/mpeg 98 | mp3 audio/mpeg 99 | mp4 video/mp4 100 | mpe video/mpeg 101 | mpeg video/mpeg 102 | mpg video/mpeg 103 | mpga audio/mpeg 104 | ms application/x-troff-ms 105 | msh model/mesh 106 | mv video/x-sgi-movie 107 | mxu video/vnd.mpegurl 108 | nc application/x-netcdf 109 | o application/octet-stream 110 | oda application/oda 111 | ogg application/ogg 112 | ogv video/ogg 113 | ogx application/ogg 114 | pac application/x-ns-proxy-autoconfig 115 | pbm image/x-portable-bitmap 116 | pdb chemical/x-pdb 117 | pdf application/pdf 118 | pgm image/x-portable-graymap 119 | pgn application/x-chess-pgn 120 | png image/png 121 | pnm image/x-portable-anymap 122 | ppm image/x-portable-pixmap 123 | ppt application/vnd.ms-powerpoint 124 | ps application/postscript 125 | qt video/quicktime 126 | ra audio/x-realaudio 127 | ram audio/x-pn-realaudio 128 | ras image/x-cmu-raster 129 | rdf application/rdf+xml 130 | rgb image/x-rgb 131 | rm audio/x-pn-realaudio 132 | roff application/x-troff 133 | rpm audio/x-pn-realaudio-plugin 134 | rss application/rss+xml 135 | rtf text/rtf; charset=%s 136 | rtx text/richtext; charset=%s 137 | sfx application/octet-stream 138 | sgm text/sgml; charset=%s 139 | sgml text/sgml; charset=%s 140 | sh application/x-sh 141 | shar application/x-shar 142 | sig application/pgp-signature 143 | silo model/mesh 144 | sit application/x-stuffit 145 | skd application/x-koan 146 | skm application/x-koan 147 | skp application/x-koan 148 | skt application/x-koan 149 | smi application/smil 150 | smil application/smil 151 | snd audio/basic 152 | so application/octet-stream 153 | spl application/x-futuresplash 154 | src application/x-wais-source 155 | stc application/vnd.sun.xml.calc.template 156 | std application/vnd.sun.xml.draw.template 157 | sti application/vnd.sun.xml.impress.template 158 | stw application/vnd.sun.xml.writer.template 159 | sv4cpio application/x-sv4cpio 160 | sv4crc application/x-sv4crc 161 | svg image/svg+xml 162 | svgx image/svg+xml 163 | svgz image/svg+xml 164 | swf application/x-shockwave-flash 165 | sxc application/vnd.sun.xml.calc 166 | sxd application/vnd.sun.xml.draw 167 | sxg application/vnd.sun.xml.writer.global 168 | sxi application/vnd.sun.xml.impress 169 | sxm application/vnd.sun.xml.math 170 | sxw application/vnd.sun.xml.writer 171 | t application/x-troff 172 | tar application/x-tar 173 | taz application/octet-stream 174 | tgz application/gzip 175 | tbz2 application/x-bzip2 176 | txz application/x-xz 177 | tcl application/x-tcl 178 | tex application/x-tex 179 | texi application/x-texinfo 180 | texinfo application/x-texinfo 181 | tif image/tiff 182 | tiff image/tiff 183 | torrent application/x-bittorrent 184 | tr application/x-troff 185 | tsp application/dsptype 186 | tsv text/tab-separated-values; charset=%s 187 | txt text/plain; charset=%s 188 | tz application/octet-stream 189 | ustar application/x-ustar 190 | vcd application/x-cdlink 191 | vrml model/vrml 192 | vx video/x-rad-screenplay 193 | wav audio/x-wav 194 | wax audio/x-ms-wax 195 | wbmp image/vnd.wap.wbmp 196 | wbxml application/vnd.wap.wbxml 197 | webm video/webm 198 | webp image/webp 199 | wm video/x-ms-wm 200 | wma audio/x-ms-wma 201 | wmd application/x-ms-wmd 202 | wml text/vnd.wap.wml 203 | wmlc application/vnd.wap.wmlc 204 | wmls text/vnd.wap.wmlscript 205 | wmlsc application/vnd.wap.wmlscriptc 206 | wmv video/x-ms-wmv 207 | wmx video/x-ms-wmx 208 | wmz application/x-ms-wmz 209 | wrl model/vrml 210 | wsrc application/x-wais-source 211 | wvx video/x-ms-wvx 212 | xbm image/x-xbitmap 213 | xht application/xhtml+xml; charset=%s 214 | xhtml application/xhtml+xml; charset=%s 215 | xls application/vnd.ms-excel 216 | xml text/xml; charset=%s 217 | xpi application/x-xpinstall 218 | xpm image/x-xpixmap 219 | xsd text/xml; charset=%s 220 | xsl text/xml; charset=%s 221 | xslt text/xml; charset=%s 222 | xul application/vnd.mozilla.xul+xml 223 | xwd image/x-xwindowdump 224 | xz application/x-xz 225 | xyz chemical/x-xyz 226 | zip application/zip 227 | zoo application/octet-stream 228 | -------------------------------------------------------------------------------- /src/mmc.h: -------------------------------------------------------------------------------- 1 | /* mmc.h - header file for mmap cache package 2 | ** 3 | ** Copyright (C) 1995-2015 Jef Poskanzer 4 | ** All rights reserved. 5 | ** 6 | ** Redistribution and use in source and binary forms, with or without 7 | ** modification, are permitted provided that the following conditions 8 | ** are met: 9 | ** 1. Redistributions of source code must retain the above copyright 10 | ** notice, this list of conditions and the following disclaimer. 11 | ** 2. Redistributions in binary form must reproduce the above copyright 12 | ** notice, this list of conditions and the following disclaimer in the 13 | ** documentation and/or other materials provided with the distribution. 14 | ** 15 | ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | ** AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | ** ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE 19 | ** LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 20 | ** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 21 | ** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 | ** INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 | ** CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 24 | ** ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 25 | ** THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #ifndef MMC_H_ 29 | #define MMC_H_ 30 | 31 | /* Checks if filename is a built-in icon */ 32 | extern int mmc_icon_check(char *filename, struct stat *st); 33 | 34 | /* Returns an mmap()ed area for the given file, or (void*) 0 on errors. 35 | ** If you have a stat buffer on the file, pass it in, otherwise pass 0. 36 | ** Same for the current time. 37 | */ 38 | extern void *mmc_map(char *filename, struct stat *sbP, struct timeval *nowP); 39 | 40 | /* Done with an mmap()ed area that was returned by mmc_map(). 41 | ** If you have a stat buffer on the file, pass it in, otherwise pass 0. 42 | ** Same for the current time. 43 | */ 44 | extern void mmc_unmap(void *addr, struct stat *sbP, struct timeval *nowP); 45 | 46 | /* Clean up the mmc package, freeing any unused storage. 47 | ** This should be called periodically, say every five minutes. 48 | ** If you have the current time, pass it in, otherwise pass 0. 49 | */ 50 | extern void mmc_cleanup(struct timeval *nowP); 51 | 52 | /* Free all storage, usually in preparation for exitting. */ 53 | extern void mmc_destroy(void); 54 | 55 | /* Generate debugging statistics syslog message. */ 56 | extern void mmc_logstats(long secs); 57 | 58 | #endif /* MMC_H_ */ 59 | -------------------------------------------------------------------------------- /src/pidfile.c: -------------------------------------------------------------------------------- 1 | /* Updated by troglobit for libite/finit/uftpd projects 2016/07/04 */ 2 | /* $OpenBSD: pidfile.c,v 1.11 2015/06/03 02:24:36 millert Exp $ */ 3 | /* $NetBSD: pidfile.c,v 1.4 2001/02/19 22:43:42 cgd Exp $ */ 4 | 5 | /*- 6 | * Copyright (c) 1999 The NetBSD Foundation, Inc. 7 | * All rights reserved. 8 | * 9 | * This code is derived from software contributed to The NetBSD Foundation 10 | * by Jason R. Thorpe. 11 | * 12 | * Redistribution and use in source and binary forms, with or without 13 | * modification, are permitted provided that the following conditions 14 | * are met: 15 | * 1. Redistributions of source code must retain the above copyright 16 | * notice, this list of conditions and the following disclaimer. 17 | * 2. Redistributions in binary form must reproduce the above copyright 18 | * notice, this list of conditions and the following disclaimer in the 19 | * documentation and/or other materials provided with the distribution. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 22 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 23 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 24 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 25 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | * POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | #include /* utimensat() */ 35 | #include /* utimensat() on *BSD */ 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include "merecat.h" 42 | 43 | static char *pidfile_path = NULL; 44 | static pid_t pidfile_pid = 0; 45 | 46 | static void pidfile_cleanup(void); 47 | 48 | const char *__pidfile_path = LOCALSTATEDIR "/run"; 49 | const char *__pidfile_name = NULL; 50 | 51 | int 52 | pidfile(const char *basename) 53 | { 54 | int save_errno; 55 | int atexit_already; 56 | pid_t pid; 57 | FILE *f; 58 | 59 | if (basename == NULL) 60 | basename = prognm; 61 | 62 | pid = getpid(); 63 | atexit_already = 0; 64 | 65 | if (pidfile_path != NULL) { 66 | if (!access(pidfile_path, R_OK) && pid == pidfile_pid) { 67 | utimensat(0, pidfile_path, NULL, 0); 68 | return (0); 69 | } 70 | free(pidfile_path); 71 | pidfile_path = NULL; 72 | __pidfile_name = NULL; 73 | atexit_already = 1; 74 | } 75 | 76 | if (basename[0] != '/') { 77 | if (asprintf(&pidfile_path, "%s/%s.pid", __pidfile_path, basename) == -1) 78 | return (-1); 79 | } else { 80 | if (asprintf(&pidfile_path, "%s", basename) == -1) 81 | return (-1); 82 | } 83 | 84 | if ((f = fopen(pidfile_path, "w")) == NULL) { 85 | save_errno = errno; 86 | free(pidfile_path); 87 | pidfile_path = NULL; 88 | errno = save_errno; 89 | return (-1); 90 | } 91 | 92 | if (fprintf(f, "%ld\n", (long)pid) <= 0 || fflush(f) != 0) { 93 | save_errno = errno; 94 | fclose(f); 95 | unlink(pidfile_path); 96 | free(pidfile_path); 97 | pidfile_path = NULL; 98 | errno = save_errno; 99 | return (-1); 100 | } 101 | fclose(f); 102 | __pidfile_name = pidfile_path; 103 | 104 | /* 105 | * LITE extension, no need to set up another atexit() handler 106 | * if user only called us to update the mtime of the PID file 107 | */ 108 | if (atexit_already) 109 | return (0); 110 | 111 | pidfile_pid = pid; 112 | if (atexit(pidfile_cleanup) < 0) { 113 | save_errno = errno; 114 | unlink(pidfile_path); 115 | free(pidfile_path); 116 | pidfile_path = NULL; 117 | pidfile_pid = 0; 118 | errno = save_errno; 119 | return (-1); 120 | } 121 | 122 | return (0); 123 | } 124 | 125 | static void 126 | pidfile_cleanup(void) 127 | { 128 | if (pidfile_path != NULL && pidfile_pid == getpid()) { 129 | unlink(pidfile_path); 130 | free(pidfile_path); 131 | pidfile_path = NULL; 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/srv.c: -------------------------------------------------------------------------------- 1 | /* Start, stop, and act on a single HTTP server 2 | ** 3 | ** Copyright (C) 1995-2015 Jef Poskanzer 4 | ** Copyright (C) 2016-2021 Joachim Wiberg 5 | ** All rights reserved. 6 | ** 7 | ** Redistribution and use in source and binary forms, with or without 8 | ** modification, are permitted provided that the following conditions 9 | ** are met: 10 | ** 1. Redistributions of source code must retain the above copyright 11 | ** notice, this list of conditions and the following disclaimer. 12 | ** 2. Redistributions in binary form must reproduce the above copyright 13 | ** notice, this list of conditions and the following disclaimer in the 14 | ** documentation and/or other materials provided with the distribution. 15 | ** 16 | ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | ** AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | ** ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE 20 | ** LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | ** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | ** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | ** INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | ** CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | ** ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 26 | ** THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | #include "fdwatch.h" 36 | #include "libhttpd.h" 37 | #include "merecat.h" 38 | #include "srv.h" 39 | #include "ssl.h" 40 | 41 | extern int handle_newconnect(struct httpd *hs, struct timeval *tv, int fd); 42 | 43 | static void lookup_hostname(char *hostname, uint16_t port, 44 | sockaddr_t *sa4, size_t sa4_len, int *gotv4, 45 | sockaddr_t *sa6, size_t sa6_len, int *gotv6) 46 | { 47 | #ifdef USE_IPV6 48 | struct addrinfo hints; 49 | char service[10]; 50 | int gaierr; 51 | struct addrinfo *ai; 52 | struct addrinfo *ptr; 53 | struct addrinfo *aiv6; 54 | struct addrinfo *aiv4; 55 | 56 | memset(&hints, 0, sizeof(hints)); 57 | hints.ai_family = PF_UNSPEC; 58 | hints.ai_flags = AI_PASSIVE; 59 | hints.ai_socktype = SOCK_STREAM; 60 | snprintf(service, sizeof(service), "%d", port); 61 | if ((gaierr = getaddrinfo(hostname, service, &hints, &ai)) != 0) { 62 | syslog(LOG_CRIT, "getaddrinfo %s: %s", hostname, gai_strerror(gaierr)); 63 | exit(1); 64 | } 65 | 66 | /* Find the first IPv6 and IPv4 entries. */ 67 | aiv6 = NULL; 68 | aiv4 = NULL; 69 | for (ptr = ai; ptr; ptr = ptr->ai_next) { 70 | switch (ptr->ai_family) { 71 | case AF_INET6: 72 | if (!aiv6) 73 | aiv6 = ptr; 74 | break; 75 | 76 | case AF_INET: 77 | if (!aiv4) 78 | aiv4 = ptr; 79 | break; 80 | } 81 | } 82 | 83 | if (!aiv6) { 84 | *gotv6 = 0; 85 | } else { 86 | if (sa6_len < aiv6->ai_addrlen) { 87 | syslog(LOG_CRIT, "%s - sockaddr too small (%lu < %lu)", 88 | hostname, (unsigned long)sa6_len, (unsigned long)aiv6->ai_addrlen); 89 | exit(1); 90 | } 91 | memset(sa6, 0, sa6_len); 92 | memmove(sa6, aiv6->ai_addr, aiv6->ai_addrlen); 93 | *gotv6 = 1; 94 | } 95 | 96 | #ifdef __linux__ 97 | /* 98 | * On Linux listening to IN6ADDR_ANY_INIT means also listening 99 | * to INADDR_ANY, so for this special case we do not need to 100 | * try to bind() to both. In fact, it will cause an error. 101 | */ 102 | if (!aiv4 || (aiv6 && !hostname)) 103 | #else 104 | if (!aiv4) 105 | #endif 106 | *gotv4 = 0; 107 | else { 108 | if (sa4_len < aiv4->ai_addrlen) { 109 | syslog(LOG_CRIT, "%s - sockaddr too small (%lu < %lu)", 110 | hostname, (unsigned long)sa4_len, (unsigned long)aiv4->ai_addrlen); 111 | exit(1); 112 | } 113 | memset(sa4, 0, sa4_len); 114 | memmove(sa4, aiv4->ai_addr, aiv4->ai_addrlen); 115 | *gotv4 = 1; 116 | } 117 | 118 | freeaddrinfo(ai); 119 | 120 | #else /* USE_IPV6 */ 121 | 122 | struct hostent *he; 123 | 124 | *gotv6 = 0; 125 | 126 | memset(sa4, 0, sa4_len); 127 | sa4->sa.sa_family = AF_INET; 128 | if (!hostname) { 129 | sa4->sa_in.sin_addr.s_addr = htonl(INADDR_ANY); 130 | } else { 131 | sa4->sa_in.sin_addr.s_addr = inet_addr(hostname); 132 | if ((int)sa4->sa_in.sin_addr.s_addr == -1) { 133 | he = gethostbyname(hostname); 134 | if (!he) { 135 | #ifdef HAVE_HSTRERROR 136 | syslog(LOG_CRIT, "gethostbyname %s: %s", hostname, hstrerror(h_errno)); 137 | #else 138 | syslog(LOG_CRIT, "gethostbyname %s failed", hostname); 139 | #endif 140 | exit(1); 141 | } 142 | if (he->h_addrtype != AF_INET) { 143 | syslog(LOG_CRIT, "%s - non-IP network address", hostname); 144 | exit(1); 145 | } 146 | memmove(&sa4->sa_in.sin_addr.s_addr, he->h_addr, he->h_length); 147 | } 148 | } 149 | sa4->sa_in.sin_port = htons(port); 150 | *gotv4 = 1; 151 | 152 | #endif /* USE_IPV6 */ 153 | } 154 | 155 | struct httpd *srv_init(struct srv *srv) 156 | { 157 | struct httpd *hs; 158 | sockaddr_t sa4; 159 | sockaddr_t sa6; 160 | size_t i; 161 | void *ctx = NULL; 162 | int gotv4, gotv6; 163 | 164 | syslog(LOG_DEBUG, "Initializing server %s: port %d, ssl %s, path %s", 165 | srv->title, srv->port, srv->ssl ? "on" : "off", srv->path); 166 | 167 | /* Resolve default port */ 168 | if (!srv->port) 169 | srv->port = srv->ssl ? DEFAULT_HTTPS_PORT : DEFAULT_HTTP_PORT; 170 | 171 | /* Look up hostname now, in case we chroot(). */ 172 | lookup_hostname(srv->host, srv->port, &sa4, sizeof(sa4), &gotv4, &sa6, sizeof(sa6), &gotv6); 173 | if (!(gotv4 || gotv6)) { 174 | syslog(LOG_ERR, "cannot find any valid address"); 175 | exit(1); 176 | } 177 | 178 | /* Initialize SSL library and load cert files before we chroot */ 179 | if (srv->ssl) { 180 | ctx = httpd_ssl_init(srv->certfile, srv->keyfile, srv->dhfile, srv->ssl_proto, srv->ciphers); 181 | if (!ctx) { 182 | httpd_ssl_log_errors(); 183 | exit(1); 184 | } 185 | } 186 | 187 | /* Initialize the HTTP layer. Got to do this before giving up root, 188 | ** so that we can bind to a privileged port. 189 | */ 190 | hs = httpd_init(hostname, srv->port, ctx, charset, max_age, srv->path, 191 | 0, no_symlink_check, do_vhost, do_global_passwd, 192 | url_pattern, local_pattern, 193 | no_empty_referers, do_list_dotfiles); 194 | if (!hs) 195 | goto err; 196 | 197 | if (httpd_cgi_init(hs, cgi_enabled, cgi_pattern, cgi_limit)) 198 | goto release; 199 | 200 | for (i = 0; i < NELEMS(srv->redirect); i++) 201 | httpd_redirect_add(hs, srv->redirect[i].code, 202 | srv->redirect[i].pattern, 203 | srv->redirect[i].location); 204 | 205 | for (i = 0; i < NELEMS(srv->location); i++) 206 | httpd_location_add(hs, srv->location[i].pattern, srv->location[i].path); 207 | 208 | if (httpd_listen(hs, gotv4 ? &sa4 : NULL, gotv6 ? &sa6 : NULL)) 209 | goto err; 210 | 211 | return hs; 212 | release: 213 | srv_exit(hs); 214 | err: 215 | syslog(LOG_CRIT, "Failed initializing server %s", srv->title); 216 | return NULL; 217 | } 218 | 219 | void srv_start(struct httpd *hs) 220 | { 221 | if (hs->listen4_fd != -1) 222 | fdwatch_add_fd(hs->listen4_fd, NULL, FDW_READ); 223 | if (hs->listen6_fd != -1) 224 | fdwatch_add_fd(hs->listen6_fd, NULL, FDW_READ); 225 | } 226 | 227 | void srv_stop(struct httpd *hs) 228 | { 229 | if (hs->listen4_fd != -1) 230 | fdwatch_del_fd(hs->listen4_fd); 231 | if (hs->listen6_fd != -1) 232 | fdwatch_del_fd(hs->listen6_fd); 233 | httpd_unlisten(hs); 234 | } 235 | 236 | int srv_connect(struct httpd *hs, struct timeval *tv) 237 | { 238 | if (!hs) 239 | return 0; 240 | 241 | /* Is it a new connection? */ 242 | if (hs->listen6_fd != -1 && fdwatch_check_fd(hs->listen6_fd)) { 243 | if (handle_newconnect(hs, tv, hs->listen6_fd)) 244 | return 1; 245 | } 246 | 247 | if (hs->listen4_fd != -1 && fdwatch_check_fd(hs->listen4_fd)) { 248 | if (handle_newconnect(hs, tv, hs->listen4_fd)) 249 | return 1; 250 | } 251 | 252 | return 0; 253 | } 254 | 255 | void srv_exit(struct httpd *hs) 256 | { 257 | srv_stop(hs); 258 | httpd_exit(hs); 259 | } 260 | -------------------------------------------------------------------------------- /src/srv.h: -------------------------------------------------------------------------------- 1 | /* Start, stop, and act on a single HTTP server 2 | ** 3 | ** Copyright (C) 1995-2015 Jef Poskanzer 4 | ** Copyright (C) 2016-2021 Joachim Wiberg 5 | ** All rights reserved. 6 | ** 7 | ** Redistribution and use in source and binary forms, with or without 8 | ** modification, are permitted provided that the following conditions 9 | ** are met: 10 | ** 1. Redistributions of source code must retain the above copyright 11 | ** notice, this list of conditions and the following disclaimer. 12 | ** 2. Redistributions in binary form must reproduce the above copyright 13 | ** notice, this list of conditions and the following disclaimer in the 14 | ** documentation and/or other materials provided with the distribution. 15 | ** 16 | ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | ** AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | ** ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE 20 | ** LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | ** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | ** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | ** INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | ** CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | ** ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 26 | ** THE POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #ifndef SRV_H_ 30 | #define SRV_H_ 31 | 32 | #define MAX_REDIRECTS 2 33 | #define MAX_LOCATIONS 2 34 | 35 | struct srv { 36 | char *title; 37 | char *host; /* specific virtual-host, unused for now */ 38 | uint16_t port; /* Server listening port */ 39 | char *path; /* path within chroot/server dir, unused for now */ 40 | 41 | int ssl; /* HTTPS or HTTP */ 42 | char *ssl_proto; 43 | char *ciphers; 44 | char *certfile; 45 | char *keyfile; 46 | char *dhfile; 47 | 48 | struct { 49 | char *pattern; /* Pattern to match() against */ 50 | 51 | int code; /* HTTP status code, default: 301 */ 52 | char *location; /* Location: to redirect to, supports format specifiers */ 53 | } redirect[MAX_REDIRECTS]; 54 | 55 | struct { 56 | char *pattern; /* Pattern to match() against */ 57 | 58 | char *path; /* Path to use for matching requests */ 59 | } location[MAX_LOCATIONS]; 60 | }; 61 | 62 | struct httpd *srv_init (struct srv *srv); 63 | void srv_exit (struct httpd *hs); 64 | 65 | void srv_start (struct httpd *hs); 66 | void srv_stop (struct httpd *hs); 67 | 68 | int srv_connect(struct httpd *hs, struct timeval *tv); 69 | 70 | #endif /* SRV_H_ */ 71 | -------------------------------------------------------------------------------- /src/ssl.h: -------------------------------------------------------------------------------- 1 | /* ssl.c - HTTPS support functions 2 | ** 3 | ** Copyright (C) 2017-2021 Joachim Wiberg 4 | ** All rights reserved. 5 | ** 6 | ** Redistribution and use in source and binary forms, with or without 7 | ** modification, are permitted provided that the following conditions 8 | ** are met: 9 | ** 1. Redistributions of source code must retain the above copyright 10 | ** notice, this list of conditions and the following disclaimer. 11 | ** 2. Redistributions in binary form must reproduce the above copyright 12 | ** notice, this list of conditions and the following disclaimer in the 13 | ** documentation and/or other materials provided with the distribution. 14 | ** 15 | ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | ** AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | ** ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE 19 | ** LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 20 | ** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 21 | ** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 | ** INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 | ** CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 24 | ** ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 25 | ** THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #ifndef MERECAT_SSL_H_ 29 | #define MERECAT_SSL_H_ 30 | 31 | #include 32 | #include 33 | #include "libhttpd.h" 34 | 35 | #ifdef ENABLE_SSL 36 | 37 | /* Initialize SSL and load certificate and key file */ 38 | void *httpd_ssl_init(char *cert, char *key, char *dhparm, char *proto, char *ciphers); 39 | 40 | /* Unload SSL, called automatically at httpd_exit() */ 41 | void httpd_ssl_exit(struct httpd *hs); 42 | 43 | /* Open a new HTTPS connection */ 44 | int httpd_ssl_open(struct http_conn *hc); 45 | 46 | /* Close a HTTP/HTTPS connection */ 47 | void httpd_ssl_close(struct http_conn *hc); 48 | 49 | /* Called before httpd_ssl_close() to signal connection shut down */ 50 | void httpd_ssl_shutdown(struct http_conn *hc); 51 | 52 | /* Reads SSL error log and sends to syslog */ 53 | void httpd_ssl_log_errors(void); 54 | 55 | /* Wrappers for read()/write() and writev() */ 56 | ssize_t httpd_ssl_read (struct http_conn *hc, void *buf, size_t len); 57 | ssize_t httpd_ssl_write (struct http_conn *hc, void *buf, size_t len); 58 | ssize_t httpd_ssl_writev (struct http_conn *hc, struct iovec *iov, int num); 59 | 60 | #else 61 | #define httpd_ssl_init(cert, key, dhparm, proto, ciphers) NULL 62 | #define httpd_ssl_exit(hs) 63 | 64 | #define httpd_ssl_open(hc) (hc->ssl = NULL) 65 | #define httpd_ssl_close(hc) 66 | #define httpd_ssl_shutdown(hc) 67 | 68 | #define httpd_ssl_log_errors() 69 | 70 | #define httpd_ssl_read(hc, buf, len) -1 71 | #define httpd_ssl_write(hc, buf, len) -1 72 | #define httpd_ssl_writev(hc, iov, num) -1 73 | #endif 74 | 75 | #endif /* MERECAT_SSL_H_ */ 76 | -------------------------------------------------------------------------------- /src/stack.c: -------------------------------------------------------------------------------- 1 | /* Simple, stupid and silly stack probe :P 2 | ** 3 | ** Copyright (C) 2017-2021 Joachim Wiberg 4 | ** All rights reserved. 5 | ** 6 | ** Redistribution and use in source and binary forms, with or without 7 | ** modification, are permitted provided that the following conditions 8 | ** are met: 9 | ** 1. Redistributions of source code must retain the above copyright 10 | ** notice, this list of conditions and the following disclaimer. 11 | ** 2. Redistributions in binary form must reproduce the above copyright 12 | ** notice, this list of conditions and the following disclaimer in the 13 | ** documentation and/or other materials provided with the distribution. 14 | ** 15 | ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | ** AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | ** ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE 19 | ** LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 20 | ** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 21 | ** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 | ** INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 | ** CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 24 | ** ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 25 | ** THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | #include 28 | #ifdef HAVE_BACKTRACE 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include /* readlink() */ 35 | #include /* backtrace() */ 36 | 37 | /* From The Practice of Programming, by Kernighan and Pike */ 38 | #ifndef NELEMS 39 | #define NELEMS(array) (sizeof(array) / sizeof(array[0])) 40 | #endif 41 | 42 | static char *addr2line(char *exec, char *addr) 43 | { 44 | static char buf[512]; 45 | FILE *fp; 46 | 47 | snprintf(buf, sizeof(buf), "addr2line -e %s %s", exec, addr); 48 | fp = popen(buf, "r"); 49 | if (!fp) 50 | return NULL; 51 | 52 | fgets(buf, sizeof(buf), fp); 53 | pclose(fp); 54 | 55 | return buf; 56 | } 57 | 58 | /* 59 | * Build with: ./configure CFLAGS="-g -Og -rdynamic" 60 | */ 61 | void stack_trace(void) 62 | { 63 | char **messages; 64 | void *trace[16]; 65 | char exec[256] = { 0 }; 66 | int i, rc, len; 67 | 68 | rc = readlink("/proc/self/exe", exec, sizeof(exec)); 69 | if (-1 == rc) 70 | return; 71 | 72 | len = backtrace(trace, NELEMS(trace)); 73 | messages = backtrace_symbols(trace, len); 74 | if (!messages) 75 | return; 76 | 77 | syslog(LOG_NOTICE, ">>> STACK TRACE"); 78 | for (i = 0; i < len; i++) { 79 | char *line; 80 | 81 | line = strstr(messages[i], " [0x"); 82 | if (line) 83 | line = addr2line(exec, line + 2); 84 | if (!line) 85 | line = ""; 86 | 87 | syslog(LOG_NOTICE, ">>> %s%s", messages[i], line); 88 | } 89 | 90 | free(messages); 91 | } 92 | 93 | #endif /* HAVE_BACKTRACE */ 94 | -------------------------------------------------------------------------------- /src/tdate_parse.c: -------------------------------------------------------------------------------- 1 | /* tdate_parse - parse string dates into internal form, stripped-down version 2 | ** 3 | ** Copyright (C) 1995 Jef Poskanzer 4 | ** All rights reserved. 5 | ** 6 | ** Redistribution and use in source and binary forms, with or without 7 | ** modification, are permitted provided that the following conditions 8 | ** are met: 9 | ** 1. Redistributions of source code must retain the above copyright 10 | ** notice, this list of conditions and the following disclaimer. 11 | ** 2. Redistributions in binary form must reproduce the above copyright 12 | ** notice, this list of conditions and the following disclaimer in the 13 | ** documentation and/or other materials provided with the distribution. 14 | ** 15 | ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | ** AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | ** ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE 19 | ** LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 20 | ** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 21 | ** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 | ** INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 | ** CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 24 | ** ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 25 | ** THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | /* This is a stripped-down version of date_parse.c, available at 29 | ** http://www.acme.com/software/date_parse/ 30 | */ 31 | 32 | #include 33 | 34 | #include 35 | #ifdef HAVE_MEMORY_H 36 | #include 37 | #endif 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | 44 | #include "tdate_parse.h" 45 | 46 | 47 | struct strlong { 48 | char *s; 49 | long l; 50 | }; 51 | 52 | 53 | static void pound_case(char *str) 54 | { 55 | for (; *str != '\0'; ++str) { 56 | if (isupper((int)*str)) 57 | *str = tolower((int)*str); 58 | } 59 | } 60 | 61 | static int strlong_compare(v1, v2) 62 | char *v1; 63 | char *v2; 64 | { 65 | return strcmp(((struct strlong *)v1)->s, ((struct strlong *)v2)->s); 66 | } 67 | 68 | 69 | static int strlong_search(char *str, struct strlong *tab, int n, long *lP) 70 | { 71 | int i, h, l, r; 72 | 73 | l = 0; 74 | h = n - 1; 75 | for (;;) { 76 | i = (h + l) / 2; 77 | r = strcmp(str, tab[i].s); 78 | if (r < 0) { 79 | h = i - 1; 80 | } else if (r > 0) { 81 | l = i + 1; 82 | } else { 83 | *lP = tab[i].l; 84 | return 1; 85 | } 86 | 87 | if (h < l) 88 | return 0; 89 | } 90 | } 91 | 92 | 93 | static int scan_wday(char *str_wday, long *tm_wdayP) 94 | { 95 | static struct strlong wday_tab[] = { 96 | {"sun", 0}, {"sunday", 0}, 97 | {"mon", 1}, {"monday", 1}, 98 | {"tue", 2}, {"tuesday", 2}, 99 | {"wed", 3}, {"wednesday", 3}, 100 | {"thu", 4}, {"thursday", 4}, 101 | {"fri", 5}, {"friday", 5}, 102 | {"sat", 6}, {"saturday", 6}, 103 | }; 104 | static int sorted = 0; 105 | 106 | if (!sorted) { 107 | qsort(wday_tab, sizeof(wday_tab) / sizeof(struct strlong), sizeof(struct strlong), strlong_compare); 108 | sorted = 1; 109 | } 110 | 111 | pound_case(str_wday); 112 | 113 | return strlong_search(str_wday, wday_tab, sizeof(wday_tab) / sizeof(struct strlong), tm_wdayP); 114 | } 115 | static int scan_mon(char *str_mon, long *tm_monP) 116 | { 117 | static struct strlong mon_tab[] = { 118 | {"jan", 0}, {"january", 0}, 119 | {"feb", 1}, {"february", 1}, 120 | {"mar", 2}, {"march", 2}, 121 | {"apr", 3}, {"april", 3}, 122 | {"may", 4}, 123 | {"jun", 5}, {"june", 5}, 124 | {"jul", 6}, {"july", 6}, 125 | {"aug", 7}, {"august", 7}, 126 | {"sep", 8}, {"september", 8}, 127 | {"oct", 9}, {"october", 9}, 128 | {"nov", 10}, {"november", 10}, 129 | {"dec", 11}, {"december", 11}, 130 | }; 131 | static int sorted = 0; 132 | 133 | if (!sorted) { 134 | qsort(mon_tab, sizeof(mon_tab) / sizeof(struct strlong), sizeof(struct strlong), strlong_compare); 135 | sorted = 1; 136 | } 137 | 138 | pound_case(str_mon); 139 | 140 | return strlong_search(str_mon, mon_tab, sizeof(mon_tab) / sizeof(struct strlong), tm_monP); 141 | } 142 | 143 | 144 | static int is_leap(int year) 145 | { 146 | return (year % 400) ? ((year % 100) ? ((year % 4) ? 0 : 1) : 0) : 1; 147 | } 148 | 149 | 150 | /* Basically the same as mktime(). */ 151 | static time_t tm_to_time(struct tm *tmP) 152 | { 153 | time_t t; 154 | 155 | static int monthtab[12] = { 156 | 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 157 | }; 158 | 159 | /* Years since epoch, converted to days. */ 160 | t = (tmP->tm_year - 70) * 365; 161 | /* Leap days for previous years. */ 162 | t += (tmP->tm_year - 69) / 4; 163 | /* Days for the beginning of this month. */ 164 | t += monthtab[tmP->tm_mon]; 165 | /* Leap day for this year. */ 166 | if (tmP->tm_mon >= 2 && is_leap(tmP->tm_year + 1900)) 167 | ++t; 168 | /* Days since the beginning of this month. */ 169 | t += tmP->tm_mday - 1; /* 1-based field */ 170 | /* Hours, minutes, and seconds. */ 171 | t = t * 24 + tmP->tm_hour; 172 | t = t * 60 + tmP->tm_min; 173 | t = t * 60 + tmP->tm_sec; 174 | 175 | return t; 176 | } 177 | 178 | 179 | time_t tdate_parse(char *str) 180 | { 181 | struct tm tm; 182 | char *cp; 183 | char str_mon[500], str_wday[500]; 184 | int tm_sec, tm_min, tm_hour, tm_mday, tm_year; 185 | long tm_mon, tm_wday; 186 | time_t t; 187 | 188 | /* Initialize. */ 189 | memset(&tm, 0, sizeof(struct tm)); 190 | 191 | /* Skip initial whitespace ourselves - sscanf is clumsy at this. */ 192 | for (cp = str; *cp == ' ' || *cp == '\t'; ++cp) 193 | continue; 194 | 195 | /* And do the sscanfs. WARNING: you can add more formats here, 196 | ** but be careful! You can easily screw up the parsing of existing 197 | ** formats when you add new ones. The order is important. 198 | */ 199 | 200 | /* DD-mth-YY HH:MM:SS GMT */ 201 | if (sscanf(cp, "%d-%400[a-zA-Z]-%d %d:%d:%d GMT", 202 | &tm_mday, str_mon, &tm_year, &tm_hour, &tm_min, &tm_sec) == 6 && scan_mon(str_mon, &tm_mon)) { 203 | tm.tm_mday = tm_mday; 204 | tm.tm_mon = tm_mon; 205 | tm.tm_year = tm_year; 206 | tm.tm_hour = tm_hour; 207 | tm.tm_min = tm_min; 208 | tm.tm_sec = tm_sec; 209 | } 210 | 211 | /* DD mth YY HH:MM:SS GMT */ 212 | else if (sscanf(cp, "%d %400[a-zA-Z] %d %d:%d:%d GMT", 213 | &tm_mday, str_mon, &tm_year, &tm_hour, &tm_min, &tm_sec) == 6 && scan_mon(str_mon, &tm_mon)) { 214 | tm.tm_mday = tm_mday; 215 | tm.tm_mon = tm_mon; 216 | tm.tm_year = tm_year; 217 | tm.tm_hour = tm_hour; 218 | tm.tm_min = tm_min; 219 | tm.tm_sec = tm_sec; 220 | } 221 | 222 | /* HH:MM:SS GMT DD-mth-YY */ 223 | else if (sscanf(cp, "%d:%d:%d GMT %d-%400[a-zA-Z]-%d", 224 | &tm_hour, &tm_min, &tm_sec, &tm_mday, str_mon, &tm_year) == 6 && scan_mon(str_mon, &tm_mon)) { 225 | tm.tm_hour = tm_hour; 226 | tm.tm_min = tm_min; 227 | tm.tm_sec = tm_sec; 228 | tm.tm_mday = tm_mday; 229 | tm.tm_mon = tm_mon; 230 | tm.tm_year = tm_year; 231 | } 232 | 233 | /* HH:MM:SS GMT DD mth YY */ 234 | else if (sscanf(cp, "%d:%d:%d GMT %d %400[a-zA-Z] %d", 235 | &tm_hour, &tm_min, &tm_sec, &tm_mday, str_mon, &tm_year) == 6 && scan_mon(str_mon, &tm_mon)) { 236 | tm.tm_hour = tm_hour; 237 | tm.tm_min = tm_min; 238 | tm.tm_sec = tm_sec; 239 | tm.tm_mday = tm_mday; 240 | tm.tm_mon = tm_mon; 241 | tm.tm_year = tm_year; 242 | } 243 | 244 | /* wdy, DD-mth-YY HH:MM:SS GMT */ 245 | else if (sscanf(cp, "%400[a-zA-Z], %d-%400[a-zA-Z]-%d %d:%d:%d GMT", 246 | str_wday, &tm_mday, str_mon, &tm_year, &tm_hour, &tm_min, 247 | &tm_sec) == 7 && scan_wday(str_wday, &tm_wday) && scan_mon(str_mon, &tm_mon)) { 248 | tm.tm_wday = tm_wday; 249 | tm.tm_mday = tm_mday; 250 | tm.tm_mon = tm_mon; 251 | tm.tm_year = tm_year; 252 | tm.tm_hour = tm_hour; 253 | tm.tm_min = tm_min; 254 | tm.tm_sec = tm_sec; 255 | } 256 | 257 | /* wdy, DD mth YY HH:MM:SS GMT */ 258 | else if (sscanf(cp, "%400[a-zA-Z], %d %400[a-zA-Z] %d %d:%d:%d GMT", 259 | str_wday, &tm_mday, str_mon, &tm_year, &tm_hour, &tm_min, 260 | &tm_sec) == 7 && scan_wday(str_wday, &tm_wday) && scan_mon(str_mon, &tm_mon)) { 261 | tm.tm_wday = tm_wday; 262 | tm.tm_mday = tm_mday; 263 | tm.tm_mon = tm_mon; 264 | tm.tm_year = tm_year; 265 | tm.tm_hour = tm_hour; 266 | tm.tm_min = tm_min; 267 | tm.tm_sec = tm_sec; 268 | } 269 | 270 | /* wdy mth DD HH:MM:SS GMT YY */ 271 | else if (sscanf(cp, "%400[a-zA-Z] %400[a-zA-Z] %d %d:%d:%d GMT %d", 272 | str_wday, str_mon, &tm_mday, &tm_hour, &tm_min, &tm_sec, 273 | &tm_year) == 7 && scan_wday(str_wday, &tm_wday) && scan_mon(str_mon, &tm_mon)) { 274 | tm.tm_wday = tm_wday; 275 | tm.tm_mon = tm_mon; 276 | tm.tm_mday = tm_mday; 277 | tm.tm_hour = tm_hour; 278 | tm.tm_min = tm_min; 279 | tm.tm_sec = tm_sec; 280 | tm.tm_year = tm_year; 281 | } else 282 | return (time_t)-1; 283 | 284 | if (tm.tm_year > 1900) 285 | tm.tm_year -= 1900; 286 | else if (tm.tm_year < 70) 287 | tm.tm_year += 100; 288 | 289 | t = tm_to_time(&tm); 290 | 291 | return t; 292 | } 293 | -------------------------------------------------------------------------------- /src/tdate_parse.h: -------------------------------------------------------------------------------- 1 | /* tdate_parse.h - parse string dates into internal form, stripped-down version 2 | ** 3 | ** Copyright (C) 1995 Jef Poskanzer 4 | ** All rights reserved. 5 | ** 6 | ** Redistribution and use in source and binary forms, with or without 7 | ** modification, are permitted provided that the following conditions 8 | ** are met: 9 | ** 1. Redistributions of source code must retain the above copyright 10 | ** notice, this list of conditions and the following disclaimer. 11 | ** 2. Redistributions in binary form must reproduce the above copyright 12 | ** notice, this list of conditions and the following disclaimer in the 13 | ** documentation and/or other materials provided with the distribution. 14 | ** 15 | ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | ** AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | ** ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE 19 | ** LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 20 | ** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 21 | ** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 | ** INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 | ** CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 24 | ** ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 25 | ** THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #ifndef _TDATE_PARSE_H_ 29 | #define _TDATE_PARSE_H_ 30 | 31 | extern time_t tdate_parse(char *str); 32 | 33 | #endif /* _TDATE_PARSE_H_ */ 34 | -------------------------------------------------------------------------------- /src/timers.c: -------------------------------------------------------------------------------- 1 | /* timers.c - simple timer routines 2 | ** 3 | ** Copyright (C) 1995-2015 Jef Poskanzer 4 | ** All rights reserved. 5 | ** 6 | ** Redistribution and use in source and binary forms, with or without 7 | ** modification, are permitted provided that the following conditions 8 | ** are met: 9 | ** 1. Redistributions of source code must retain the above copyright 10 | ** notice, this list of conditions and the following disclaimer. 11 | ** 2. Redistributions in binary form must reproduce the above copyright 12 | ** notice, this list of conditions and the following disclaimer in the 13 | ** documentation and/or other materials provided with the distribution. 14 | ** 15 | ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | ** AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | ** ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE 19 | ** LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 20 | ** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 21 | ** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 | ** INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 | ** CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 24 | ** ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 25 | ** THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #include 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | #include "timers.h" 36 | 37 | 38 | #define HASH_SIZE 67 39 | static struct timer *timers[HASH_SIZE]; 40 | static struct timer *free_timers; 41 | static int alloc_count, active_count, free_count; 42 | 43 | arg_t noarg; 44 | 45 | #undef HAVE_CLOCK_MONO 46 | #if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC) 47 | #define HAVE_CLOCK_MONO 48 | static int use_monotonic = 0; /* monotonic clock runtime availability flag */ 49 | static struct timeval tv_diff; /* system time - monotonic difference at start */ 50 | #endif 51 | 52 | static unsigned int hash(struct timer *t) 53 | { 54 | /* We can hash on the trigger time, even though it can change over 55 | ** the life of a timer via either the periodic bit or the tmr_reset() 56 | ** call. This is because both of those guys call l_resort(), which 57 | ** recomputes the hash and moves the timer to the appropriate list. 58 | */ 59 | return ((unsigned int)t->time.tv_sec ^ (unsigned int)t->time.tv_usec) % HASH_SIZE; 60 | } 61 | 62 | 63 | static void l_add(struct timer *t) 64 | { 65 | int h = t->hash; 66 | struct timer *t2; 67 | struct timer *t2prev; 68 | 69 | t2 = timers[h]; 70 | if (!t2) { 71 | /* The list is empty. */ 72 | timers[h] = t; 73 | t->prev = t->next = NULL; 74 | } else { 75 | if ( t->time.tv_sec < t2->time.tv_sec || 76 | (t->time.tv_sec == t2->time.tv_sec && 77 | t->time.tv_usec <= t2->time.tv_usec)) { 78 | /* The new timer goes at the head of the list. */ 79 | timers[h] = t; 80 | t->prev = NULL; 81 | t->next = t2; 82 | t2->prev = t; 83 | } else { 84 | /* Walk the list to find the insertion point. */ 85 | for (t2prev = t2, t2 = t2->next; t2; t2prev = t2, t2 = t2->next) { 86 | if (t->time.tv_sec < t2->time.tv_sec || 87 | (t->time.tv_sec == t2->time.tv_sec && 88 | t->time.tv_usec <= t2->time.tv_usec)) { 89 | /* Found it. */ 90 | t2prev->next = t; 91 | t->prev = t2prev; 92 | t->next = t2; 93 | t2->prev = t; 94 | return; 95 | } 96 | } 97 | 98 | /* Oops, got to the end of the list. Add to tail. */ 99 | t2prev->next = t; 100 | t->prev = t2prev; 101 | t->next = NULL; 102 | } 103 | } 104 | } 105 | 106 | 107 | static void l_remove(struct timer *t) 108 | { 109 | if (!t) 110 | return; 111 | 112 | if (!t->prev) 113 | timers[t->hash] = t->next; 114 | else 115 | t->prev->next = t->next; 116 | 117 | if (t->next) 118 | t->next->prev = t->prev; 119 | } 120 | 121 | 122 | static void l_resort(struct timer *t) 123 | { 124 | /* Remove the timer from its old list. */ 125 | l_remove(t); 126 | 127 | /* Recompute the hash. */ 128 | t->hash = hash(t); 129 | 130 | /* And add it back in to its new list, sorted correctly. */ 131 | l_add(t); 132 | } 133 | 134 | 135 | void tmr_init(void) 136 | { 137 | int h; 138 | 139 | for (h = 0; h < HASH_SIZE; ++h) 140 | timers[h] = NULL; 141 | free_timers = NULL; 142 | alloc_count = active_count = free_count = 0; 143 | 144 | /* Check for monotonic clock availability */ 145 | #ifdef HAVE_CLOCK_MONO 146 | struct timespec ts; 147 | struct timeval tv_start, tv; 148 | 149 | /* Try to get monotonic clock time */ 150 | if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) { 151 | use_monotonic = 1; 152 | 153 | /* Get current system time */ 154 | gettimeofday(&tv_start, NULL); 155 | 156 | tv.tv_sec = ts.tv_sec; 157 | tv.tv_usec = ts.tv_nsec / 1000L; 158 | /* Calculate and save the difference: tv_start is since 159 | ** the Epoch, so tv_start > ts tv_diff = tv_start - tv 160 | */ 161 | timersub(&tv_start, &tv, &tv_diff); 162 | } 163 | #endif 164 | } 165 | 166 | 167 | struct timer *tmr_create(struct timeval *now, 168 | void (*cb)(arg_t, struct timeval *), 169 | arg_t arg, long msecs, int periodic) 170 | { 171 | struct timer *t; 172 | 173 | if (free_timers) { 174 | t = free_timers; 175 | free_timers = t->next; 176 | --free_count; 177 | } else { 178 | t = malloc(sizeof(struct timer)); 179 | if (!t) 180 | return NULL; 181 | 182 | ++alloc_count; 183 | } 184 | 185 | t->cb = cb; 186 | t->arg = arg; 187 | t->msecs = msecs; 188 | t->periodic = periodic; 189 | if (now) 190 | t->time = *now; 191 | else 192 | tmr_prepare_timeval(&t->time); 193 | 194 | t->time.tv_sec += msecs / 1000L; 195 | t->time.tv_usec += (msecs % 1000L) * 1000L; 196 | if (t->time.tv_usec >= 1000000L) { 197 | t->time.tv_sec += t->time.tv_usec / 1000000L; 198 | t->time.tv_usec %= 1000000L; 199 | } 200 | 201 | t->hash = hash(t); 202 | 203 | /* Add the new timer to the proper active list. */ 204 | l_add(t); 205 | ++active_count; 206 | 207 | return t; 208 | } 209 | 210 | 211 | struct timeval *tmr_timeout(struct timeval *now) 212 | { 213 | long msecs; 214 | static struct timeval timeout; 215 | 216 | msecs = tmr_mstimeout(now); 217 | if (msecs == INFTIM) 218 | return NULL; 219 | 220 | timeout.tv_sec = msecs / 1000L; 221 | timeout.tv_usec = (msecs % 1000L) * 1000L; 222 | 223 | return &timeout; 224 | } 225 | 226 | 227 | long tmr_mstimeout(struct timeval *now) 228 | { 229 | long msecs, m; 230 | int gotone; 231 | int h; 232 | 233 | gotone = 0; 234 | msecs = 0; 235 | 236 | /* Since the lists are sorted, we only need to look at 237 | ** the first timer on each one. 238 | */ 239 | for (h = 0; h < HASH_SIZE; ++h) { 240 | struct timer *t; 241 | 242 | t = timers[h]; 243 | if (!t) 244 | continue; 245 | 246 | m = (t->time.tv_sec - now->tv_sec) * 1000L + (t->time.tv_usec - now->tv_usec) / 1000L; 247 | if (!gotone) { 248 | msecs = m; 249 | gotone = 1; 250 | } else if (m < msecs) { 251 | msecs = m; 252 | } 253 | } 254 | 255 | if (!gotone) 256 | return INFTIM; 257 | 258 | if (msecs <= 0) 259 | msecs = 500; /* Was 0, but we should never poll() < 500 msec */ 260 | 261 | return msecs; 262 | } 263 | 264 | 265 | void tmr_run(struct timeval *now) 266 | { 267 | int h; 268 | struct timer *t; 269 | struct timer *next; 270 | 271 | for (h = 0; h < HASH_SIZE; ++h) 272 | for (t = timers[h]; t; t = next) { 273 | next = t->next; 274 | /* Since the lists are sorted, as soon as we find a timer 275 | ** that isn't ready yet, we can go on to the next list. 276 | */ 277 | if (t->time.tv_sec > now->tv_sec || (t->time.tv_sec == now->tv_sec && 278 | t->time.tv_usec > now->tv_usec)) 279 | break; 280 | 281 | (t->cb) (t->arg, now); 282 | if (t->periodic) { 283 | /* Reschedule. */ 284 | t->time.tv_sec += t->msecs / 1000L; 285 | t->time.tv_usec += (t->msecs % 1000L) * 1000L; 286 | if (t->time.tv_usec >= 1000000L) { 287 | t->time.tv_sec += t->time.tv_usec / 1000000L; 288 | t->time.tv_usec %= 1000000L; 289 | } 290 | l_resort(t); 291 | } else 292 | tmr_cancel(t); 293 | } 294 | } 295 | 296 | 297 | void tmr_reset(struct timeval *now, struct timer *t) 298 | { 299 | if (!t) 300 | return; 301 | 302 | t->time = *now; 303 | t->time.tv_sec += t->msecs / 1000L; 304 | t->time.tv_usec += (t->msecs % 1000L) * 1000L; 305 | if (t->time.tv_usec >= 1000000L) { 306 | t->time.tv_sec += t->time.tv_usec / 1000000L; 307 | t->time.tv_usec %= 1000000L; 308 | } 309 | l_resort(t); 310 | } 311 | 312 | 313 | void tmr_cancel(struct timer *t) 314 | { 315 | if (!t) 316 | return; 317 | 318 | /* Remove it from its active list. */ 319 | l_remove(t); 320 | --active_count; 321 | 322 | /* And put it on the free list. */ 323 | t->prev = NULL; 324 | t->next = free_timers; 325 | free_timers = t; 326 | 327 | ++free_count; 328 | } 329 | 330 | 331 | void tmr_cleanup(void) 332 | { 333 | struct timer *t; 334 | 335 | while (free_timers) { 336 | t = free_timers; 337 | free_timers = t->next; 338 | 339 | --free_count; 340 | --alloc_count; 341 | 342 | free(t); 343 | } 344 | } 345 | 346 | 347 | void tmr_destroy(void) 348 | { 349 | int h; 350 | 351 | for (h = 0; h < HASH_SIZE; ++h) { 352 | while (timers[h]) 353 | tmr_cancel(timers[h]); 354 | } 355 | tmr_cleanup(); 356 | } 357 | 358 | 359 | /* Generate debugging statistics syslog message. */ 360 | void tmr_logstats(long secs) 361 | { 362 | syslog(LOG_INFO, " timers - %d allocated, %d active, %d free", alloc_count, active_count, free_count); 363 | if (active_count + free_count != alloc_count) 364 | syslog(LOG_ERR, "timer counts don't add up!"); 365 | } 366 | 367 | /* Fill timeval structure for further usage by the package. */ 368 | void tmr_prepare_timeval(struct timeval *tv) 369 | { 370 | #ifdef HAVE_CLOCK_MONO 371 | struct timespec ts; 372 | struct timeval tv0; 373 | 374 | if (use_monotonic) { /* use monotonic clock source ? */ 375 | if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0) { 376 | perror("clock_gettime"); 377 | return; 378 | } 379 | tv0.tv_sec = ts.tv_sec; 380 | tv0.tv_usec = ts.tv_nsec / 1000L; 381 | /* Return system time value like it was running accurately */ 382 | timeradd(&tv_diff, &tv0, tv); 383 | } else { 384 | #endif 385 | gettimeofday(tv, NULL); 386 | #ifdef HAVE_CLOCK_MONO 387 | } 388 | #endif 389 | } 390 | -------------------------------------------------------------------------------- /src/timers.h: -------------------------------------------------------------------------------- 1 | /* timers.h - header file for timers package 2 | ** 3 | ** Copyright (C) 1995-2015 Jef Poskanzer 4 | ** All rights reserved. 5 | ** 6 | ** Redistribution and use in source and binary forms, with or without 7 | ** modification, are permitted provided that the following conditions 8 | ** are met: 9 | ** 1. Redistributions of source code must retain the above copyright 10 | ** notice, this list of conditions and the following disclaimer. 11 | ** 2. Redistributions in binary form must reproduce the above copyright 12 | ** notice, this list of conditions and the following disclaimer in the 13 | ** documentation and/or other materials provided with the distribution. 14 | ** 15 | ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | ** AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | ** ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE 19 | ** LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 20 | ** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 21 | ** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 | ** INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 | ** CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 24 | ** ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 25 | ** THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #ifndef _TIMERS_H_ 29 | #define _TIMERS_H_ 30 | 31 | #include 32 | #include 33 | 34 | #ifndef INFTIM 35 | #define INFTIM -1 36 | #endif 37 | 38 | /* arg_t is a random value that tags along with a timer. The client 39 | ** can use it for whatever, and it gets passed to the callback when the 40 | ** timer triggers. 41 | */ 42 | typedef union { 43 | void *p; 44 | int i; 45 | long l; 46 | } arg_t; 47 | 48 | extern arg_t noarg; /* for use when you don't care */ 49 | 50 | struct timer { 51 | struct timer *prev; 52 | struct timer *next; 53 | 54 | int hash; 55 | struct timeval time; 56 | long msecs; 57 | int periodic; 58 | 59 | void (*cb)(arg_t, struct timeval *); 60 | arg_t arg; 61 | }; 62 | 63 | /* Initialize the timer package. */ 64 | extern void tmr_init(void); 65 | 66 | /* Set up a timer, either periodic or one-shot. Returns NULL on errors. */ 67 | extern struct timer *tmr_create(struct timeval *now, void (*cb)(arg_t, struct timeval *), 68 | arg_t arg, long msecs, int periodic); 69 | 70 | /* Returns a timeout indicating how long until the next timer triggers. You 71 | ** can just put the call to this routine right in your select(). Returns 72 | ** (struct timeval*) 0 if no timers are pending. 73 | */ 74 | extern struct timeval *tmr_timeout(struct timeval *now); 75 | 76 | /* Returns a timeout in milliseconds indicating how long until the next timer 77 | ** triggers. You can just put the call to this routine right in your poll(). 78 | ** Returns INFTIM (-1) if no timers are pending. 79 | */ 80 | extern long tmr_mstimeout(struct timeval *now); 81 | 82 | /* Run the list of timers. Your main program needs to call this every so often, 83 | ** or as indicated by tmr_timeout(). 84 | */ 85 | extern void tmr_run(struct timeval *now); 86 | 87 | /* Reset the clock on a timer, to current time plus the original timeout. */ 88 | extern void tmr_reset(struct timeval *now, struct timer *timer); 89 | 90 | /* Deschedule a timer. Note that non-periodic timers are automatically 91 | ** descheduled when they run, so you don't have to call this on them. 92 | */ 93 | extern void tmr_cancel(struct timer *timer); 94 | 95 | /* Clean up the timers package, freeing any unused storage. */ 96 | extern void tmr_cleanup(void); 97 | 98 | /* Cancel all timers and free storage, usually in preparation for exitting. */ 99 | extern void tmr_destroy(void); 100 | 101 | /* Generate debugging statistics syslog message. */ 102 | extern void tmr_logstats(long secs); 103 | 104 | /* Fill timeval structure for further usage by the package. */ 105 | extern void tmr_prepare_timeval(struct timeval *tv); 106 | 107 | #endif /* _TIMERS_H_ */ 108 | -------------------------------------------------------------------------------- /tests/.gitignore: -------------------------------------------------------------------------------- 1 | .deps/ 2 | *.log 3 | *.trs 4 | -------------------------------------------------------------------------------- /tests/Makefile.am: -------------------------------------------------------------------------------- 1 | EXTRA_DIST = merecat.conf start.sh stop.sh 2 | EXTRA_DIST += cgi.sh gzip.sh redirect.sh location.sh php.sh 3 | CLEANFILES = *~ *.trs *.log 4 | TEST_EXTENSIONS = .sh 5 | 6 | TESTS = start.sh 7 | TESTS += cgi.sh 8 | TESTS += php.sh 9 | TESTS += gzip.sh 10 | TESTS += redirect.sh 11 | TESTS += location.sh 12 | TESTS += stop.sh 13 | -------------------------------------------------------------------------------- /tests/cgi.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -ex 3 | 4 | curl http://localhost:8086/cgi-bin/printenv 2>/dev/null |grep 'SERVER_SOFTWARE=merecat/' 5 | -------------------------------------------------------------------------------- /tests/gzip.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # https://en.wikipedia.org/wiki/HTTP_compression 3 | set -ex 4 | 5 | curl -H "Accept-Encoding: gzip" -I http://localhost:8086/main.css 2>/dev/null |grep gzip 6 | 7 | -------------------------------------------------------------------------------- /tests/location.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Verify location directive 3 | set -ex 4 | 5 | # Fetch from 8080:/.secret/path 6 | echo "Pass 1/2" 7 | curl -s -I http://localhost:8080/.secret/path/merecat.jpg | tee foo |grep "200 OK" 8 | cat foo; rm foo 9 | 10 | # But not from :8086 11 | echo "Pass 2/2" 12 | curl -s -I http://localhost:8086/.secret/path/merecat.jpg |tee foo | grep "404 Not Found" 13 | cat foo; rm foo 14 | -------------------------------------------------------------------------------- /tests/merecat.conf: -------------------------------------------------------------------------------- 1 | cgi "**.cgi|/cgi-bin/*" { 2 | enabled = true 3 | } 4 | 5 | php "**.php*" { 6 | enabled = true 7 | } 8 | 9 | server hej { 10 | port = 8086 11 | } 12 | 13 | server nej { 14 | port = 8080 15 | # If location is requested, replace with path 16 | location "/.secret/path/**" { 17 | path = "/img" 18 | } 19 | redirect "/**" { 20 | code = 301 21 | location = "http://$host:8086$request_uri$args" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /tests/php.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -ex 3 | 4 | echo "" >srv/test.php 5 | ls srv 6 | cat srv/test.php 7 | 8 | curl http://localhost:8086/test.php?name=foobar 2>/dev/null |grep 'Hello foobar' 9 | -------------------------------------------------------------------------------- /tests/redirect.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # https://en.wikipedia.org/wiki/URL_redirection 3 | 4 | curl -Ls -w %{url_effective} http://127.0.0.1:8080 |grep http://127.0.0.1:8086/ 5 | -------------------------------------------------------------------------------- /tests/start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | srvfiles="main.css index.html img/merecat.jpg" 3 | if [ -z "$srcdir" ]; then 4 | srcdir=. 5 | fi 6 | 7 | mkdir -p srv/img srv/cgi-bin 8 | 9 | for file in $srvfiles; do 10 | cp ${srcdir}/../www/$file srv/$file 11 | gzip -c srv/$file > srv/$file.gz 12 | done 13 | cp ${srcdir}/../www/cgi-bin/printenv srv/cgi-bin/ 14 | 15 | echo "Starting merecat httpd, config file ${srcdir}/merecat.conf" 16 | ../src/merecat -f ${srcdir}/merecat.conf -n -l debug srv & 17 | echo $! >merecat.pid 18 | 19 | sleep 2 20 | -------------------------------------------------------------------------------- /tests/stop.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | kill `cat merecat.pid` 4 | sleep 1 5 | rm -rf srv 6 | rm merecat.pid 7 | -------------------------------------------------------------------------------- /throttle.conf: -------------------------------------------------------------------------------- 1 | # example throttle file 2 | # 3 | # pattern [M-]N 4 | # 5 | # maximum of N bytes per second 6 | # minimum of M bytes per second 7 | # 8 | # Definint a minimum means we don't start new connections 9 | # if we're doing less than M bytes/s 10 | 11 | ** 400 12 | 13 | **.jpg|**.gif 1000-50000 14 | jef/** 20000 15 | -------------------------------------------------------------------------------- /www/.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | -------------------------------------------------------------------------------- /www/Makefile.am: -------------------------------------------------------------------------------- 1 | wwwdir = $(WEBDIR) 2 | SUBDIRS = cgi-bin icons img onboard 3 | dist_www_DATA = index.html main.css phpinfo.php test.php 4 | dist_www_DATA += htpasswd.1.html merecat.conf.5.html merecat.8.html ssi.8.html 5 | 6 | man: 7 | ../man.sh 8 | -------------------------------------------------------------------------------- /www/cgi-bin/.gitignore: -------------------------------------------------------------------------------- 1 | .deps 2 | ssi 3 | -------------------------------------------------------------------------------- /www/cgi-bin/Makefile.am: -------------------------------------------------------------------------------- 1 | AUTOMAKE_OPTIONS = subdir-objects no-dependencies 2 | 3 | cgidir = $(WEBDIR)/cgi-bin 4 | cgi_PROGRAMS = ssi 5 | dist_cgi_SCRIPTS = printenv onboard.py 6 | 7 | ssi_SOURCES = ssi.c 8 | ssi_CPPFLAGS = -I$(top_srcdir)/src 9 | ssi_LDADD = ../../src/libmatch.a $(LIBOBJS) 10 | -------------------------------------------------------------------------------- /www/cgi-bin/onboard.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | import cgi 4 | import cgitb 5 | import datetime 6 | 7 | 8 | cgitb.enable(display=0, logdir="/var/log/onboard") 9 | 10 | # Create instance of FieldStorage 11 | form = cgi.FieldStorage() 12 | 13 | # Get data from fields 14 | if form.getvalue('username'): 15 | username = form.getvalue('username') 16 | else: 17 | username = "username" 18 | 19 | if form.getvalue('gecos'): 20 | gecos = form.getvalue('gecos') 21 | else: 22 | gecos = "gecos" 23 | 24 | if form.getvalue('sshkey'): 25 | sshkey = form.getvalue('sshkey') 26 | else: 27 | sshkey = "no_sshkey entered" 28 | 29 | mmss = datetime.datetime.now().strftime("%M%S") 30 | f = open("/var/www/onboard/file/%s.text" % mmss, "w") 31 | f.write("# k:v, k:v, separator, line(s)\n") 32 | f.write("username: %s\n"% username) 33 | f.write("gecos: %s\n"% gecos) 34 | f.write("# SSH public key(s)\n") 35 | f.write("%s\n"% sshkey) 36 | f.close() 37 | 38 | print("Content-type:text/html") 39 | print() # separator 40 | print("") 41 | print("") 42 | print("So far") 43 | print("") 44 | print("") 45 | print("

Hello %s

" % username) 46 | print('

There is now file/%s.text.

' % (mmss,mmss)) 47 | print("

It has to be further processed, outside this web form stuff

") 48 | print('

Back to onboard begin

') 49 | print("") 50 | print("") 51 | -------------------------------------------------------------------------------- /www/cgi-bin/printenv: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | date=`date -u '+%a, %d %b %Y %H:%M:%S %Z'` 4 | 5 | cat << EOF 6 | Content-type: text/plain 7 | Expires: $date 8 | 9 | CGI printenv 10 | 11 | EOF 12 | 13 | echo 'Date:' 14 | date 15 | echo 16 | echo 'Id:' 17 | id 18 | echo 19 | echo 'Env:' 20 | printenv 21 | echo 22 | if [ "$CONTENT_LENGTH" != "" ] ; then 23 | if [ "$CONTENT_LENGTH" -ne 0 ] ; then 24 | echo 'Input:' 25 | echo 26 | dd bs=1 count=$CONTENT_LENGTH 27 | echo 28 | fi 29 | fi 30 | -------------------------------------------------------------------------------- /www/footer.html: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /www/header.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | %TITLE% 7 | 8 | 9 | 10 | 11 |
12 | -------------------------------------------------------------------------------- /www/htpasswd.1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | htpasswd.1 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
htpasswd(1)General Commands Manualhtpasswd(1)
19 |
20 |

21 | htpasswd — 22 |
Create and update user authentication files
23 |

24 | 25 | 26 | 27 | 30 | 31 |
htpasswd[
-cm
] 28 | FILE 29 | USERNAME
32 |

33 | htpasswd is used to create and update the 34 | flat-files used to store usernames and password for basic authentication of 35 | HTTP users. Resources available from the Merecat web server can be restricted 36 | to just the users listed in the files created by htpasswd. This program can 37 | only be used when the usernames are stored in a flat-file. 38 |
39 | This manual page only lists the command line arguments for 40 | htpasswd. For details of the directives 41 | necessary to enable user authentication in the web server, see 42 | merecat(8), the Merecat httpd README, or the 43 | GitHub project home page ⟨https://github.com/troglobit/merecat⟩. 44 |

45 |
46 |
47 |
Create, or recreate the .htpwassd file, 48 | FILE, if it already exists.
49 |
50 |
Use MD5 encryption for passwords, this is tested at runtime. So this 51 | option is simply for compatibility with other similar tools.
52 |
FILE
53 |
Name of the file to contain the user name and password, usually .htpasswd. 54 | If -c is given, this file is created if 55 | it does not already exist, or deleted and recreated if it does exist.
56 |
USERNAME
57 |
The username to create or update in passwdfile, 58 | FILE. If username does not exist is this 59 | file, an entry is added. If it does exist, the password is changed.
60 |
61 |

63 | merecat(8) 64 |

65 | Rob McCool ⟨robm@stanford.edu⟩ 66 | originally wrote htpasswd for NCSA httpd. 67 | It then (naturally) made its way to Apache and other web servers, like Roxen 68 | Challenger. 69 |
70 | Jef Poskanzer 71 | ⟨jef@mail.acme.com⟩ modified it 29aug97 to accept new password 72 | on stdin, if stdin is a pipe or file. This is necessary for use from 73 | CGI.
74 | 75 | 76 | 77 | 78 | 79 |
August 3, 2019merecat (2.32)
80 | 87 |
88 | 89 | 90 | -------------------------------------------------------------------------------- /www/icons/Makefile.am: -------------------------------------------------------------------------------- 1 | wwwiconsdir = $(WEBDIR)/icons 2 | if HAVE_ICONS 3 | dist_wwwicons_DATA = back.gif blank.gif folder.gif generic.gif movie.gif \ 4 | unknown.gif binary.gif comp.gray.gif folder.open.gif \ 5 | image.gif text.gif uuencoded.gif binhex.gif compressed.gif \ 6 | forward.gif index.gif transfer.gif favicon.ico 7 | endif 8 | -------------------------------------------------------------------------------- /www/icons/back.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/troglobit/merecat/4c6e3e401b8ec2fc6b106097c70d8032b0700e96/www/icons/back.gif -------------------------------------------------------------------------------- /www/icons/binary.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/troglobit/merecat/4c6e3e401b8ec2fc6b106097c70d8032b0700e96/www/icons/binary.gif -------------------------------------------------------------------------------- /www/icons/binhex.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/troglobit/merecat/4c6e3e401b8ec2fc6b106097c70d8032b0700e96/www/icons/binhex.gif -------------------------------------------------------------------------------- /www/icons/blank.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/troglobit/merecat/4c6e3e401b8ec2fc6b106097c70d8032b0700e96/www/icons/blank.gif -------------------------------------------------------------------------------- /www/icons/comp.gray.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/troglobit/merecat/4c6e3e401b8ec2fc6b106097c70d8032b0700e96/www/icons/comp.gray.gif -------------------------------------------------------------------------------- /www/icons/compressed.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/troglobit/merecat/4c6e3e401b8ec2fc6b106097c70d8032b0700e96/www/icons/compressed.gif -------------------------------------------------------------------------------- /www/icons/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/troglobit/merecat/4c6e3e401b8ec2fc6b106097c70d8032b0700e96/www/icons/favicon.ico -------------------------------------------------------------------------------- /www/icons/folder.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/troglobit/merecat/4c6e3e401b8ec2fc6b106097c70d8032b0700e96/www/icons/folder.gif -------------------------------------------------------------------------------- /www/icons/folder.open.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/troglobit/merecat/4c6e3e401b8ec2fc6b106097c70d8032b0700e96/www/icons/folder.open.gif -------------------------------------------------------------------------------- /www/icons/forward.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/troglobit/merecat/4c6e3e401b8ec2fc6b106097c70d8032b0700e96/www/icons/forward.gif -------------------------------------------------------------------------------- /www/icons/generic.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/troglobit/merecat/4c6e3e401b8ec2fc6b106097c70d8032b0700e96/www/icons/generic.gif -------------------------------------------------------------------------------- /www/icons/image.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/troglobit/merecat/4c6e3e401b8ec2fc6b106097c70d8032b0700e96/www/icons/image.gif -------------------------------------------------------------------------------- /www/icons/index.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/troglobit/merecat/4c6e3e401b8ec2fc6b106097c70d8032b0700e96/www/icons/index.gif -------------------------------------------------------------------------------- /www/icons/movie.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/troglobit/merecat/4c6e3e401b8ec2fc6b106097c70d8032b0700e96/www/icons/movie.gif -------------------------------------------------------------------------------- /www/icons/text.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/troglobit/merecat/4c6e3e401b8ec2fc6b106097c70d8032b0700e96/www/icons/text.gif -------------------------------------------------------------------------------- /www/icons/transfer.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/troglobit/merecat/4c6e3e401b8ec2fc6b106097c70d8032b0700e96/www/icons/transfer.gif -------------------------------------------------------------------------------- /www/icons/unknown.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/troglobit/merecat/4c6e3e401b8ec2fc6b106097c70d8032b0700e96/www/icons/unknown.gif -------------------------------------------------------------------------------- /www/icons/uuencoded.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/troglobit/merecat/4c6e3e401b8ec2fc6b106097c70d8032b0700e96/www/icons/uuencoded.gif -------------------------------------------------------------------------------- /www/img/Makefile.am: -------------------------------------------------------------------------------- 1 | wwwimgdir = $(WEBDIR)/img 2 | dist_wwwimg_DATA = merecat.jpg 3 | -------------------------------------------------------------------------------- /www/img/merecat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/troglobit/merecat/4c6e3e401b8ec2fc6b106097c70d8032b0700e96/www/img/merecat.jpg -------------------------------------------------------------------------------- /www/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Welcome to the Merecat embedded web server 7 | 8 | 9 | 10 | 11 |
12 | 15 | 23 |
24 |

25 | Welcome to the Merecat web server. This is the default landing 26 | page for new installations. Seeing this means the web server is 27 | working and the owner has not set up any pages of their own yet. 28 |

29 |

30 | Merecat started out as a pun at 31 | Mongoose, but is 32 | now useful for actual web serving purposes. However, it is not a 33 | real Meerkat, 34 | merely another copycat, forked from 35 | thttpd. 36 |

37 |

38 | It expands on the features originally offered by thttpd, e.g. native 39 | HTTPS support, PHP CGI, gzip deflate using zlib, dual server support; 40 | both HTTP & HTTPS from one process, and built-in HTTP redirect. 41 | The resulting footprint (~140 kiB) makes it suitable for small and 42 | embedded systems. See these fine manual pages for more information: 43 |

49 |

50 |

Credits

51 |

52 | Jef Poskanzer for creating and 53 | maintaining thttpd for so long, and 54 | SunShot for the Mere Cat 55 | meme, huge props! 56 |

57 |
58 | 64 |
65 | 66 | 67 | -------------------------------------------------------------------------------- /www/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: var(--body-bg); 3 | color: var(--body-color); 4 | font-family: sans-serif; 5 | font-size: 110%; /* flexible baseline */ 6 | line-height: 1.2; 7 | text-align: center; /*For IE6 Shenanigans*/ 8 | } 9 | 10 | h1 { 11 | font-size: 1.7em; 12 | font-weight: normal; 13 | border-bottom: 1px solid var(--line-color); 14 | } 15 | 16 | h2 { 17 | border-bottom: 1px solid var(--line-color); 18 | font-weight: normal; 19 | } 20 | 21 | a { 22 | text-decoration: none; 23 | color: var(--link-color); 24 | } 25 | 26 | a:hover { 27 | text-decoration: underline; 28 | filter: brightness(80%); 29 | } 30 | 31 | p { 32 | text-align: justify; 33 | } 34 | 35 | pre { 36 | font-family: monospace; 37 | font-size: 0.9em; 38 | } 39 | 40 | address { 41 | border-top: 1px solid var(--line-color); 42 | margin-top: 1em; 43 | color: var(--desc-color); 44 | } 45 | 46 | img { 47 | display: block; 48 | margin-left: auto; 49 | margin-right: auto; 50 | border-radius: 10px; 51 | border: 1px solid var(--desc-color); 52 | } 53 | 54 | dl.config dt { 55 | font-family: monospace; 56 | } 57 | 58 | dl.config dd { 59 | text-align: justify; 60 | } 61 | 62 | #header { 63 | text-align: center; 64 | } 65 | 66 | #footer { 67 | text-align: center; 68 | clear: both; 69 | } 70 | 71 | /* Man pages */ 72 | table.head, table.foot { width: 100%; } 73 | table.foot { margin: 2ex 0ex; } 74 | td.head-rtitle, td.foot-os { text-align: right; } 75 | td.head-vol { text-align: center; } 76 | .Nm > tbody > tr > td { vertical-align: text-top; } 77 | div.Pp { margin: 1ex 0ex; } 78 | div.Nd, div.Bf, div.Op { display: inline; } 79 | span.Pa, span.Ad { font-style: italic; } 80 | span.Ms { font-weight: bold; } 81 | dl.Bl-diag > dt { font-weight: bold; } 82 | code.Nm, code.Fl, code.Cm, code.Ic, code.In, code.Fd, code.Fn, 83 | code.Cd { font-weight: bold; font-family: inherit; } 84 | 85 | @media screen and (max-width: 1023px) { 86 | #wrapper { 87 | width: 80%; 88 | text-align: left; 89 | 90 | padding: 0.5em; 91 | margin: 0.5em auto; 92 | 93 | border-radius: 10px; 94 | border: 1px solid var(--wrapper-border); 95 | 96 | background: var(--wrapper-bg); 97 | box-shadow: 1px 1px 10px var(--wrapper-shadow); 98 | 99 | } 100 | #content { 101 | } 102 | #right { 103 | float: right; 104 | width: 75%; 105 | } 106 | #left { 107 | float: left; 108 | width: 75%; 109 | } 110 | } 111 | 112 | @media screen and (min-width: 1024px) { 113 | #wrapper { 114 | width: 800px; 115 | text-align: left; 116 | 117 | position: absolute; 118 | top: 0; 119 | left: 0; 120 | right: 0; 121 | padding: 1.5em; 122 | 123 | margin-top: 20px; 124 | margin-bottom:30px; 125 | margin-right: auto; 126 | margin-left: auto; 127 | 128 | border-radius: 10px; 129 | border: 1px solid var(--wrapper-border); 130 | 131 | background: var(--wrapper-bg); 132 | box-shadow: 1px 1px 10px var(--wrapper-shadow); 133 | } 134 | 135 | #right { 136 | float: right; 137 | width: 63%; 138 | } 139 | 140 | #left { 141 | float: left; 142 | width: 63%; 143 | } 144 | } 145 | 146 | :root { 147 | --body-bg: #f2f1f0; 148 | --body-color: #555; 149 | --wrapper-bg: white; 150 | --wrapper-border: #c8c5c2; 151 | --wrapper-shadow: #BBB; 152 | --desc-color: #c8c5c2; 153 | --line-color: #f2f1f0; 154 | --link-color: darkslategray; 155 | --link-color-hover: #000; 156 | } 157 | 158 | @media (prefers-color-scheme: dark) { 159 | :root { 160 | --body-bg: #212529; 161 | --body-color: #9ca59d; 162 | --wrapper-bg: rgba(0, 0, 0, 0.7); 163 | --wrapper-border: #252525; 164 | --wrapper-shadow: #252525; 165 | --desc-color: #9ca59d; 166 | --line-color: #9ca59d; 167 | --link-color: #dee2e6; 168 | --link-color-hover: #fff; 169 | } 170 | } 171 | 172 | -------------------------------------------------------------------------------- /www/onboard/Makefile.am: -------------------------------------------------------------------------------- 1 | wwwonboarddir = $(WEBDIR)/onboard 2 | dist_wwwonboard_DATA = form.html file/FYI.text 3 | -------------------------------------------------------------------------------- /www/onboard/file/FYI.text: -------------------------------------------------------------------------------- 1 | For Your Information 2 | 3 | This text file helps 4 | 5 | * to tell that cgi-bin/onboard.py writes files in this directory 6 | * showing that created files are visible 7 | 8 | (And this file ensures that git is aware of the onboard/file/ directory.) 9 | -------------------------------------------------------------------------------- /www/onboard/form.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |

Welcome

5 |

6 | Please provide information for SSH access. 7 |

8 | 9 |
10 | Your preferred user name:
11 | (Example: ws)
12 |
13 |
14 | Gecos, the fifth field in a /etc/passwd record:
15 | (Example: Winston Smith)
16 | (Example: Winston Smith,room,phone,home phone,other)
17 |
18 |
19 | Public part of SSH key you gonna use:
20 |
24 | 25 | 26 |
27 | 28 | 29 | -------------------------------------------------------------------------------- /www/phpinfo.php: -------------------------------------------------------------------------------- 1 | 4 | 5 | -------------------------------------------------------------------------------- /www/ssi.8.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ssi.8 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
SSI(8)System Manager's Manual (smm)SSI(8)
19 |
20 |

21 | ssi — 22 |
Server-Side-Includes CGI program
23 |

24 | 25 | 26 | 27 | 28 | 29 |
ssi
30 |

31 | This is an external CGI program that provides the same functionality as the 32 | built-in server-side-includes feature in many HTTP daemons. It is written for 33 | use with thttpd(8) and 34 | merecat(8), but should be easy to adapt to other 35 | systems. 36 |
37 | There are two ways to use this; the modern way of using a 38 | .shtml pattern in 39 | merecat.conf(5) to trigger the SSI script, which 40 | requires enabling the SSI module. Then there is the traditional 41 | thttpd(8) approach. We start with the relevant 42 | settings needed in merecat.conf: 43 |
44 |
45 |
 46 | ssi { 
 47 |     enabled = true 
 48 |     pattern = "**.shtml"	# default 
 49 |     cgi-path = "cgi-bin/ssi"    # default, web server root is used 
 50 | }
 51 | 
52 |
53 |
54 | The traditional thttpd way runs ssi as a 55 | simple CGI script, which requires placing the ssi binary in the web server CGI 56 | area, and enabling CGI. Then set up URLs with the path to the document to 57 | parse as the "pathinfo". That's the part of the URL that comes after 58 | the CGI program name. For example, if the URL to this program is: 59 |
60 |
61 |
 62 | 
 63 |     http://www.acme.com/cgi-bin/ssi 
 64 | 
 65 | 
66 |
67 | and the url for the document is: 68 |
69 |
70 |
 71 | 
 72 |     http://www.acme.com/users/wecoyote/doc.html 
 73 | 
 74 | 
75 |
76 | then the compound URL would be: 77 |
78 |
79 |
 80 | 
 81 |     http://www.acme.com/cgi-bin/ssi/users/wecoyote/doc.html 
 82 | 
 83 | 
84 |
85 |

87 | The format description below is adapted from 88 | ⟨http://hoohoo.ncsa.uiuc.edu/docs/tutorials/includes.html⟩. 89 |
90 | All directives are formatted as SGML comments within the document. This is in 91 | case the document should ever find itself in the client's hands unparsed. Each 92 | directive has the following format: 93 |
94 |
95 |
 96 | 
 97 |     <!--#command tag1="value1" tag2="value2" --> 
 98 | 
 99 | 
100 |
101 | Note: the lack of space between the initial HTML 102 | comment start and the #command. This is explicitly stated in the standard and 103 | strictly enforced by all web servers implementing SSI. 104 |
105 | Each command takes different arguments, most only accept one tag at a time. Here 106 | is a breakdown of the commands and their associated tags: 107 |
108 |
109 |
The config directive controls various aspects of the file parsing. There 110 | are two valid tags: 111 |
112 |
113 |
gives the server a new format to use when providing dates. This is a 114 | string compatible with the strftime(3) 115 | library call.
116 |
117 |
determines the formatting to be used when displaying the size of a 118 | file. Valid choices are bytes, for a formatted byte count (formatted 119 | as 1,234,567), or abbrev for an abbreviated version displaying the 120 | number of kilobytes or megabytes the file occupies.
121 |
122 |
overrides the default; “[an error occurred while processing 123 | this directive]”
124 |
125 |
126 |
127 |
Inserts the text of another document into the parsed document. The 128 | inserted file is parsed recursively, so it can contain server-side-include 129 | directives too. This command accepts two tags: 130 |
131 |
132 |
Gives a virtual path to a document on the server.
133 |
134 |
Gives a pathname relative to the current directory. ../ cannot be used 135 | in this pathname, nor can absolute paths be used.
136 |
137 |
138 |
139 |
Prints the value of one of the include variables (defined below). Any 140 | dates are printed subject to the currently configured timefmt. The only 141 | valid tag to this command is var, whose value is the name of the variable 142 | you wish to echo.
143 |
144 |
prints the size of the specified file, subject to the sizefmt parameter to 145 | the config command. Valid tags are the same as with the include 146 | command.
147 |
148 |
prints the last modification date of the specified file, subject to the 149 | formatting preference given by the timefmt parameter to config. Valid tags 150 | are the same as with the include command.
151 |
152 |

153 | A number of variables are made available to parsed documents. In addition to the 154 | CGI variable set, the following variables are made available: 155 |
156 |
157 |
The current filename.
158 |
159 |
The virtual path to this document (such as /~robm/foo.shtml).
160 |
161 |
The unescaped version of any search query the client sent.
162 |
163 |
The current date, local time zone. Subject to the timefmt parameter to the 164 | config command.
165 |
166 |
Same as DATE_LOCAL but in Greenwich 167 | mean time (GMT).
168 |
169 |
The last modification date of the current document. Subject to timefmt 170 | like the others.
171 |
172 |

174 | merecat(8), 175 | merecat.conf(5), 176 | strftime(3) 177 |

178 |
179 | Jef Poskanzer 180 | ⟨jef@mail.acme.com⟩ wrote the original for use with 181 | thttpd. 182 |
183 | Joachim Wiberg 184 | ⟨troglobit@gmail.com⟩ added minor features and a trigger in 185 | merecat for 186 | .shtml pages. 187 |

188 | Does not implement all "modern" SSI directives are supported. E.g., 189 | exec cgi and 190 | exec cmd or any control directives like 191 | if, elif, else, endif, etc. Patches and 192 | pull-requests are welcome :)
193 | 194 | 195 | 196 | 197 | 198 |
August 3, 2019merecat (2.32)
199 | 206 |
207 | 208 | 209 | -------------------------------------------------------------------------------- /www/test.php: -------------------------------------------------------------------------------- 1 | 4 | --------------------------------------------------------------------------------