]> git.plutz.net Git - serve0/blob - cgilite/file.sh
include storage functions
[serve0] / cgilite / file.sh
1 #!/bin/sh
2
3 # Copyright 2016 - 2019 Paul Hänsch
4 #
5 # This file is part of cgilite.
6
7 # cgilite is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU Affero General Public License as published by
9 # the Free Software Foundation, either version 3 of the License, or
10 # (at your option) any later version.
11
12 # cgilite is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 # GNU Affero General Public License for more details.
16
17 # You should have received a copy of the GNU Affero General Public License
18 # along with cgilite.  If not, see <http://www.gnu.org/licenses/>. 
19
20 [ -n "$include_fileserve" ] && return 0
21 include_fileserve="$0"
22
23 file_type(){
24   case ${1##*.} in
25     html|html) printf 'text/html';;
26     css)       printf 'text/css';;
27     js)        printf 'text/javascript';;
28     txt)       printf 'text/plain';;
29     sh)        printf 'text/shellscript';;
30     jpg|jpeg)  printf 'image/jpeg';;
31     png)       printf 'image/png';;
32     svg)       printf 'image/svg+xml';;
33     gif)       printf 'image/gif';;
34     webm)      printf 'video/webm';;
35     mp4|m4v)   printf 'video/mp4';;
36     m4a)       printf 'audio/mp4';;
37     ogg)       printf 'audio/ogg';;
38     xml)       printf 'application/xml';;
39     m3u8)      printf 'application/x-mpegURL';;
40     ts)        printf 'video/MP2T';;
41     mpd)       printf 'application/dash+xml';;
42     m4s)       printf 'video/iso.segment';;
43     *)         printf 'application/octet-stream';;
44   esac
45 }
46
47 FILE(){
48   local file file_size file_date http_date cachedate range mime
49   file="$1" mime="$2"
50
51   if ! [ -f "$file" ]; then
52     printf 'Content-Length: 0\r\nStatus: 404 Not Found\r\n\r\n'
53     exit 0
54   elif ! [ -r "$file" ]; then
55     printf 'Content-Length: 0\r\nStatus: 403 Forbidden\r\n\r\n'
56     exit 0
57   fi
58
59   file_size="$(stat -Lc %s "$file")"
60   file_date="$(stat -Lc %Y "$file")"
61   http_date="$(date -ud "@$file_date" +"%a, %d %b %Y %T GMT")"
62   cachedate="$(
63     # Parse the allowable date formats from Section 3.3.1 of
64     # https://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html
65     HEADER If-Modified-Since \
66     | sed -E 's;^[^ ]+, ([0-9]{2}) (...) ([0-9]{4}) (..:..:..) GMT$;\3-\2-\1 \4;;
67               s;^[^ ]+, ([0-9]{2})-(...)-([789][0-9]) (..:..:..) GMT$;19\3-\2-\1 \4;;
68               s;^[^ ]+, ([0-9]{2})-(...)-([0-6][0-9]) (..:..:..) GMT$;20\3-\2-\1 \4;;
69               s;^[^ ]+ (...) ([0-9]{2}) (..:..:..) ([0-9]{4})$;\4-\1-\2 \3;;
70               s;^[^ ]+ (...)  ([0-9]) (..:..:..) ([0-9]{4})$;\4-\1-\2 \3;;
71               s;Jan;01;; s;Feb;02;; s;Mar;03;; s;Apr;04;; s;May;05;; s;Jun;06;;
72               s;Jul;07;; s;Aug;08;; s;Sep;09;; s;Oct;10;; s;Nov;11;; s;Dec;12;;' \
73     | xargs -r0 date +%s -ud 2>&-
74   )"
75
76   range="$(HEADER Range |sed -nE 's;^bytes=([0-9]+-[0-9]*|-[0-9]+)$;\1;p;q;')"
77   case "$range" in
78     *-) range="${range}$((file_size - 1))";;
79     -*) [ ${range#-} -le $file_size ] \
80         && range="$((file_size - ${range#-}))-$((file_size - 1))" \
81         || range="0-$((file_size - 1))";;
82     *-*) [ ${range#*-} -ge $file_size ] \
83          && range="${range%-*}-$((file_size - 1))";;
84   esac
85
86   if [ "$file_date" -lt "$cachedate" ] 2>&-; then
87     printf '%s: %s\r\n' \
88       Status '304 Not Modified' \
89       Content-Length 0 \
90       Last-Modified "$http_date"
91     printf '\r\n'
92   
93   elif [ -z "$range" ]; then
94     printf '%s: %s\r\n' \
95       Status "200 OK" \
96       Accept-Ranges bytes \
97       Last-Modified "$http_date" \
98       Content-Type "${mime:-$(file_type "$file")}" \
99       Content-Length $file_size
100     printf '\r\n'
101   
102     [ "$REQUEST_METHOD" != HEAD ] && cat "$file"
103
104   elif [ "${range%-*}" -le "${range#*-}" ]; then
105     printf '%s: %s\r\n' \
106       Status "206 Partial Content" \
107       Accept-Ranges bytes \
108       Last-Modified "$http_date" \
109       Content-Type "${mime:-$(file_type "$file")}" \
110       Content-Range "bytes ${range}/${file_size}" \
111       Content-Length "$((${range#*-} - ${range%-*} + 1))"
112     printf '\r\n'
113   
114     [ "$REQUEST_METHOD" != HEAD ] \
115     && tail -c+$((${range%-*} + 1)) "$file" \
116      | head -c "$((${range#*-} - ${range%-*} + 1))"
117
118   elif [ "${range%-*}" -gt "${range#*-}" ]; then
119     printf '%s: %s\r\n' \
120       Status "216 Range Not Satisfiable" \
121       Content-Length 0 \
122       Content-Range \*/${file_size}
123     printf '\r\n'
124   fi
125 }