3 # Copyright 2022 - 2023 Paul Hänsch
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.
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 ANY
12 # 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 OR
15 # IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 REV_ATTACHMENTS="${REV_ATTACHMENTS:-false}"
19 if [ "${PATH_INFO##*/\[attachment\]}" ]; then
20 # Skip any action not happening on attachment page
24 page="${PATH_INFO%\[attachment\]}"
25 action="$(POST action)"
27 tsid="$(POST session_key)"; tsid="${tsid%% *}"
30 if ! acl_write "${PATH_INFO%\[attachment\]}"; then
31 # Deny access to write protected pages
32 printf 'Refresh: %i\r\n' 4
34 [ "${CONTENT_TYPE%%;*}" = "multipart/form-data" ] \
35 && head -c $((CONTENT_LENGTH)) >/dev/null
38 elif [ "${CONTENT_TYPE%%;*}" = "multipart/form-data" ]; then
39 . "$_EXEC/multipart.sh"
42 # Use positional parameters for filename collection
43 # The positional array is the only array available
44 # in plain posix shells, see the documentation for
45 # your shells "set" builtin for a hint to this
49 # Validate session id from form to prevent CSRF
50 # Only validate if username is present, because no username means
51 # anonymous uploads are allowed via acl and cgilite/session.sh does not
52 # validate anonymous sessions from a multipart/formdata
53 if [ "$USER_NAME" -a "$(multipart session_id)" != "$SESSION_ID" ]; then
54 rm -- "$multipart_cachefile"
55 printf 'Refresh: %i\r\n' 4
60 mkdir -p "$_DATA/pages${page}#attachments/"
61 n=1; while filename=$(multipart_filename "file" "$n"); do
62 filename="$(printf %s "$filename" |tr /\\0 __)"
63 set -- "$@" "pages${page}#attachments/$filename"
64 multipart "file" "$n" >"$_DATA/pages${page}#attachments/$filename"
67 rm -- "$multipart_cachefile"
68 if [ "$REV_ATTACHMENTS" = true ]; then
69 git -C "$_DATA" add -- "$@"
70 git -C "$_DATA" commit -qm "Attachments to # $page # uploaded by @ $USER_NAME @" -- "$@"
72 REDIRECT "${_BASE}${PATH_INFO}"
74 elif [ "$SESSION_ID" != "$tsid" ]; then
75 # Match session key from POST-Data to prevent CSRF:
76 # For authenticated users the POST session_key must match
77 # the session key used for authentication (usually from a
78 # cookie). This should ensure that POST requests were not
79 # triggered by malicious 3rd party sites freeriding on an
80 # existing user authentication.
81 # For pages that are writable by anonymous users, this is
84 printf 'Refresh: %i\r\n' 4
89 if [ "$action" = delete -o "$action" = move ]; then
91 n="$(POST_COUNT select)"; while [ $n -gt 0 ]; do
92 select="$(POST select $n |PATH)"
93 set -- "$@" "pages${page}#attachments/${select##*/}"
98 if [ "$action" = delete ]; then
99 if [ "$REV_ATTACHMENTS" = true ]; then
100 git -C "$_DATA" rm -- "$@"
101 git -C "$_DATA" commit -qm \
102 "Attachment to # $page # deleted by @ $USER_NAME @" -- "$@"
104 ( cd "$_DATA" && rm -- "$@"; )
106 REDIRECT "${_BASE}${PATH_INFO}"
108 elif [ "$action" = move ]; then
109 moveto="$(POST moveto |PATH)"
111 if ! acl_write "$moveto"; then
112 printf 'Refresh: %i\r\n' 4
116 elif [ ! -d "${_DATA}/pages${moveto}" ]; then
117 printf 'Refresh: %i\r\n' 4
121 elif [ "$REV_ATTACHMENTS" = true ]; then
122 mkdir -p -- "${_DATA}/pages${moveto}/#attachments"
123 git -C "$_DATA" mv -f -- "$@" "pages${moveto}/#attachments/"
125 cnt=$#; while [ $cnt -gt 0 ]; do
126 set -- "$@" "$1" "pages/${moveto}/#attachments/${1##*/}"
127 cnt=$((cnt - 1)); shift 1
130 git -C "$_DATA" commit -qm \
131 "Attachment moved from # $page # to # $moveto # by @ $USER_NAME @" -- "$@"
133 mkdir -p -- "${_DATA}/pages${moveto}/#attachments"
134 ( cd "$_DATA" && mv -- "$@" "pages${moveto}/#attachments/"; )
136 REDIRECT "${_BASE}${PATH_INFO}"
138 elif [ "$action" = rename ]; then
142 for file in "${_DATA}/pages${page}#attachments"/*; do
143 rename="$(POST rename_"$(slopecode "${file##*/}" |sed 's;=;%3D;g')")"
145 if [ "$REV_ATTACHMENTS" = true -a \
148 "${rename%/*}" = "${rename}" -a \
149 ! -e "${_DATA}/pages${page}#attachments/${rename}" ] \
150 && git -C "$_DATA" mv -- "pages${page}#attachments/${file##*/}" "pages${page}#attachments/${rename}"; then
151 success="${success}$(HTML "${file##*/}/${rename}")${BR}"
152 set -- "$@" "pages${page}#attachments/${file##*/}" "pages${page}#attachments/${rename}"
154 elif [ "$REV_ATTACHMENTS" = true -a "${rename}" ]; then
155 fail="${fail}$(HTML "${file##*/}/${rename}")${BR}"
157 elif [ -f "${file}" -a \
159 "${rename%/*}" = "${rename}" -a \
160 ! -e "${_DATA}/pages${page}#attachments/${rename}" ] \
161 && mv -- "${file}" "${_DATA}/pages${page}#attachments/${rename}"; then
162 success="${success}$(HTML "${file##*/}/${rename}")${BR}"
164 elif [ "${rename}" ]; then
165 fail="${fail}$(HTML "${file##*/}/${rename}")${BR}"
170 if [ "$REV_ATTACHMENTS" = true -a $# -gt 2 ]; then
171 git -C "$_DATA" commit -qm \
172 "Attachment files renamed by @ $USER_NAME @" -- "$@"
173 elif [ "$REV_ATTACHMENTS" = true -a $# -eq 2 ]; then
174 git -C "$_DATA" commit -qm \
175 "Attachment file renamed by @ $USER_NAME @" -- "$@"
178 if [ "$success" -a "$fail" ]; then
179 printf "%s\r\n" "Status: 500 Internal Server Error"
180 theme_page - "$(_ "Attachment rename")" <<-EOF
181 <h1 class="rename partial">$(_ Some files could not be renamed)</h1>
182 <h2 class="rename success">$(_ Successfully renamed:)</h2>
183 <ul class="rename success">
184 $(printf %s "$success" |while read html; do
185 printf '<li><span class=from>%s</span> -> <span class=to>%s</span></li>' \
186 "${html%%/*}" "${html##*/}"
189 <h2 class="rename fail">$(_ Errors:)</h2>
190 <ul class="rename fail">
191 $(printf %s "$fail" |while read html; do
192 printf '<li><span class=from>%s</span> -> <span class=to>%s</span></li>' \
193 "${html%%/*}" "${html##*/}"
196 <a class="button rename fail" href="[attachment]">$(_ OK)</a>
200 elif [ "$fail" ]; then
201 printf "%s\r\n" "Status: 500 Internal Server Error"
202 theme_page - "$(_ "Attachment rename")" <<-EOF
203 <h1 class="rename fail">$(_ "Files could not be renamed")</h1>
204 <ul class="rename fail">
205 $(printf %s "$fail" |while read html; do
206 printf '<li><span class=from>%s</span> -> <span class=to>%s</span></li>' \
207 "${html%%/*}" "${html##*/}"
210 <a class="button rename fail" href="[attachment]">$(_ OK)</a>
214 elif [ "$success" ]; then
215 printf 'Refresh: %i\r\n' 4
216 theme_page - "Attachment rename" <<-EOF
217 <h1 class="rename success">$(_ Files were renamed)</h1>
218 <ul class="rename success">
219 $(printf %s "$success" |while read html; do
220 printf '<li><span class=from>%s</span> -> <span class=to>%s</span></li>' \
221 "${html%%/*}" "${html##*/}"
224 <a class="button rename success" href="[attachment]">$(_ OK)</a>
229 REDIRECT "${_BASE}${PATH_INFO}"