PluginProbe
Bit Form – Contact Form, Payment Forms, Multi Step Forms, Calculator & Custom Form Builder / V3.0.3
Bit Form – Contact Form, Payment Forms, Multi Step Forms, Calculator & Custom Form Builder vV3.0.3
V-3.3.0 3.2.2 3.2.1 3.2.0 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 V3.0.3 V3.0.2 -3.0.1 V_3.0.0 1.1.1 1.1.8 1.2 1.3 1.4 1.4.18 1.5.2 1.9 2.0 2.10.0 2.10.1 2.10.2 All 137 releases
bit-form / includes / Core / Util / FileDownloadProvider.php

FileDownloadProvider.php in Bit Form – Contact Form, Payment Forms, Multi Step Forms, Calculator & Custom Form Builder V3.0.3, at includes/Core/Util/FileDownloadProvider.php

207 lines 6.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace BitCode\BitForm\Core\Util;
4
5 if (!defined('ABSPATH')) {
6 exit;
7 }
8
9 final class FileDownloadProvider
10 {
11 private function isAuthorizedFileRequest($formID, $entryID)
12 {
13 if (!is_user_logged_in()) {
14 return false;
15 }
16
17 $currentUserId = get_current_user_id();
18 if (empty($currentUserId)) {
19 return false;
20 }
21
22 // If a nonce is provided, verify it. If it's missing/invalid, fall back to an ownership/capability check.
23 $nonce = isset($_GET['nonce']) && is_scalar($_GET['nonce']) ? sanitize_text_field(wp_unslash((string) $_GET['nonce'])) : '';
24 $nonceAction = 'bitforms_file_download_' . $formID . '_' . $entryID;
25 if (!empty($nonce) && wp_verify_nonce($nonce, $nonceAction)) {
26 return true;
27 }
28
29 // Capability bypass.
30 if (current_user_can('manage_bitform') || current_user_can('manage_options')) {
31 return true;
32 }
33
34 // Ownership check: non-admin users may only download their own entry files.
35 $entryModel = new \BitCode\BitForm\Core\Database\FormEntryModel();
36 $entry = $entryModel->get(
37 'id',
38 [
39 'id' => $entryID,
40 'form_id' => $formID,
41 'user_id' => $currentUserId,
42 ]
43 );
44
45 return !is_wp_error($entry) && !empty($entry);
46 }
47
48 public function register()
49 {
50 add_action('template_redirect', [$this, 'authCheckandFrceDownloadHelper']);
51 add_shortcode('bitforms-frontend-file', [$this, 'handleFileDownload']);
52 }
53
54 public function handleFileDownload()
55 {
56 // File download: form/entry/file IDs read from query string; authorization enforced via isAuthorizedFileRequest() below.
57 if (!isset($_GET['formID']) || !isset($_GET['entryID']) || !isset($_GET['fileID'])) {
58 global $wp_query;
59 $wp_query->set_404();
60 status_header(404);
61 get_template_part(404);
62 exit();
63 }
64 $formID = intval(sanitize_text_field(wp_unslash($_GET['formID'])));
65 $entryID = intval(sanitize_text_field(wp_unslash($_GET['entryID'])));
66 $fileID = sanitize_file_name(wp_unslash($_GET['fileID']));
67 if (!$this->isAuthorizedFileRequest($formID, $entryID)) {
68 $this->show404();
69 }
70
71 $filePath = FileHandler::getEntriesFileUploadDir($formID, $entryID) . DIRECTORY_SEPARATOR . $fileID;
72
73 if (is_readable($filePath)) {
74 $this->fileDownloadORView($filePath, true);
75 } else {
76 $this->show404();
77 }
78 }
79
80 public static function getBaseDownloadURL()
81 {
82 $routes = get_option('bitforms_routes');
83 if (isset($routes['file'])) {
84 $file_page = get_post($routes['file']);
85 if (empty($file_page)) {
86 $file_route_id = wp_insert_post(
87 [
88 'post_name' => 'bitforms-file',
89 'comment_status' => 'closed',
90 'ping_status' => 'closed',
91 'post_content' => '<!-- wp:shortcode -->[bitforms-frontend-file /]<!-- /wp:shortcode -->',
92 'post_status' => 'publish',
93 'post_type' => 'bitforms'
94 ]
95 );
96 $routes['file'] = $file_route_id;
97 update_option('bitforms_routes', $routes);
98 $file_page_slug = get_post_permalink($file_route_id);
99 } else {
100 $file_page_slug = get_post_permalink($file_page->ID);
101 }
102 } else {
103 $file_route_id = wp_insert_post(
104 [
105 'post_name' => 'bitforms-file',
106 'comment_status' => 'closed',
107 'ping_status' => 'closed',
108 'post_content' => '<!-- wp:shortcode -->[bitforms-frontend-file /]<!-- /wp:shortcode -->',
109 'post_status' => 'publish',
110 'post_type' => 'bitforms'
111 ]
112 );
113 $route_value = [];
114 $route_value['file'] = $file_route_id;
115 update_option('bitforms_routes', $route_value);
116 $file_page_slug = get_post_permalink($file_route_id);
117 }
118
119 return $file_page_slug;
120 }
121
122 public function authCheckandFrceDownloadHelper()
123 {
124 if (!is_singular('bitforms')) {
125 return;
126 }
127 global $post;
128 if (!empty($post->post_content)) {
129 $shortCodeRegex = get_shortcode_regex();
130 preg_match_all('/' . $shortCodeRegex . '/', $post->post_content, $regexMatchGroups);
131 if (!empty($regexMatchGroups[2]) && in_array('bitforms-frontend-file', $regexMatchGroups[2]) && is_user_logged_in()) {
132 $file = $this->isRequestedFileExists();
133 if ($file) {
134 $this->fileDownloadORView($file, isset($_GET['download']));
135 } else {
136 $this->show404();
137 }
138 } else {
139 auth_redirect();
140 }
141 }
142 }
143
144 private function show404()
145 {
146 global $wp_query;
147 $wp_query->set_404();
148 status_header(404);
149 get_template_part(404);
150 exit();
151 }
152
153 private function isRequestedFileExists()
154 {
155 // File download: IDs read from query string; authorization enforced via isAuthorizedFileRequest() below.
156 if (!isset($_GET['formID']) || !isset($_GET['entryID']) || !isset($_GET['fileID'])) {
157 return false;
158 }
159 $formID = intval(sanitize_text_field(wp_unslash($_GET['formID'])));
160 $entryID = intval(sanitize_text_field(wp_unslash($_GET['entryID'])));
161 $fileID = sanitize_file_name(wp_unslash($_GET['fileID']));
162
163 if (!$this->isAuthorizedFileRequest($formID, $entryID)) {
164 return false;
165 }
166
167 $filePath = FileHandler::getEntriesFileUploadDir($formID, $entryID) . DIRECTORY_SEPARATOR . $fileID;
168 if (is_readable($filePath)) {
169 return $filePath;
170 }
171
172 return false;
173 }
174
175 private function fileDownloadORView($filePath, $forceDownload = false)
176 {
177 if ($forceDownload) {
178 header('Content-Type: application/force-download');
179 header('Content-Type: application/octet-stream');
180 header('Content-Type: application/download');
181 header('Content-Disposition: attachment; filename="' . basename($filePath) . '"');
182 } else {
183 $fileInfo = wp_check_filetype($filePath);
184 $content_types = 'text/plain';
185 if ($fileInfo['type'] && $fileInfo['ext']) {
186 $content_types = $fileInfo['type'];
187 $ext = $fileInfo['ext'];
188 if (in_array($ext, ['txt', 'php', 'html', 'xhtml', 'json'], true)) {
189 $content_types = 'text/plain';
190 }
191 }
192 header('Content-Disposition:filename="' . basename($filePath) . '"');
193 header("Content-Type: $content_types");
194 }
195 header('Content-Description: File Transfer');
196 header('Expires: 0');
197 header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
198 header('Pragma: public');
199 header('Content-Length: ' . filesize($filePath));
200 header('Content-Transfer-Encoding: binary ');
201 flush();
202 // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_readfile -- Streaming binary download; WP_Filesystem has no streaming equivalent and get_contents() would load entire file into memory.
203 readfile($filePath);
204 die();
205 }
206 }
207