PluginProbe
Bit Form – Contact Form, Payment Forms, Multi Step Forms, Calculator & Custom Form Builder / 3.3.1
Bit Form – Contact Form, Payment Forms, Multi Step Forms, Calculator & Custom Form Builder v3.3.1
3.3.1 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 All 138 releases
← All changes | includes/Core/Util/FileDownloadProvider.php +183 -126 1.93.3.1 View file →
@@ -1,149 +1,206 @@
1 1 <?php
2 2
3 3 namespace BitCode\BitForm\Core\Util;
4 4
5 +if (!defined('ABSPATH')) {
6 + exit;
7 +}
8 +
5 9 final class FileDownloadProvider
6 10 {
7 - public function register()
8 - {
9 - add_action('template_redirect', array($this, 'authCheckandFrceDownloadHelper'));
10 - add_shortcode('bitforms-frontend-file', array($this, 'handleFileDownload'));
11 + private function isAuthorizedFileRequest($formID, $entryID)
12 + {
13 + if (!is_user_logged_in()) {
14 + return false;
11 15 }
12 16
13 - public function handleFileDownload()
14 - {
15 - if (!isset($_GET['formID']) || !isset($_GET['entryID']) || !isset($_GET['fileID'])) {
16 - global $wp_query;
17 - $wp_query->set_404();
18 - status_header(404);
19 - get_template_part(404);
20 - exit();
21 - }
22 - $formID = intval(sanitize_text_field($_GET['formID']));
23 - $entryID = intval(sanitize_text_field($_GET['entryID']));
24 - $fileID = sanitize_file_name($_GET['fileID']);
25 - $filePath = BITFORMS_UPLOAD_DIR . DIRECTORY_SEPARATOR . $formID . DIRECTORY_SEPARATOR . $entryID . DIRECTORY_SEPARATOR . $fileID;
26 - if (is_readable($filePath)) {
27 - $this->fileDownloadORView($filePath, true);
28 - }
17 + $currentUserId = get_current_user_id();
18 + if (empty($currentUserId)) {
19 + return false;
29 20 }
30 21
31 - public static function getBaseDownloadURL()
32 - {
33 - $routes = get_option('bitforms_routes');
34 - if (isset($routes['file'])) {
35 - $file_page = get_post($routes['file']);
36 - if (empty($file_page)) {
37 - $file_route_id = wp_insert_post(
38 - array(
39 - 'post_name' => 'bitforms-file',
40 - 'comment_status' => 'closed',
41 - 'ping_status' => 'closed',
42 - 'post_content' => '<!-- wp:shortcode -->[bitforms-frontend-file /]<!-- /wp:shortcode -->',
43 - 'post_status' => 'publish',
44 - 'post_type' => 'bitforms'
45 - )
46 - );
47 - $routes['file'] = $file_route_id;
48 - update_option('bitforms_routes', $routes);
49 - $file_page_slug = get_post_permalink($file_route_id);
50 - } else {
51 - $file_page_slug = get_post_permalink($file_page->ID);
52 - }
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']));
53 135 } else {
54 - $file_route_id = wp_insert_post(
55 - array(
56 - 'post_name' => 'bitforms-file',
57 - 'comment_status' => 'closed',
58 - 'ping_status' => 'closed',
59 - 'post_content' => '<!-- wp:shortcode -->[bitforms-frontend-file /]<!-- /wp:shortcode -->',
60 - 'post_status' => 'publish',
61 - 'post_type' => 'bitforms'
62 - )
63 - );
64 - $route_value = array();
65 - $route_value['file'] = $file_route_id;
66 - update_option('bitforms_routes', $route_value);
67 - $file_page_slug = get_post_permalink($file_route_id);
136 + $this->show404();
68 137 }
138 + } else {
139 + auth_redirect();
140 + }
141 + }
142 + }
69 143
70 - return $file_page_slug;
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;
71 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']));
72 162
73 - public function authCheckandFrceDownloadHelper()
74 - {
75 - if (!is_singular('bitforms')) {
76 - return;
77 - }
78 - global $post;
79 - if (!empty($post->post_content)) {
80 - $shortCodeRegex = get_shortcode_regex();
81 - preg_match_all('/' . $shortCodeRegex . '/', $post->post_content, $regexMatchGroups);
82 - if (!empty($regexMatchGroups[2]) && in_array('bitforms-frontend-file', $regexMatchGroups[2]) && is_user_logged_in()) {
83 - $file = $this->isRequestedFileExists();
84 - if ($file) {
85 - $this->fileDownloadORView($file, isset($_GET['download']));
86 - } else {
87 - $this->show404();
88 - }
89 - } else {
90 - auth_redirect();
91 - }
92 - }
163 + if (!$this->isAuthorizedFileRequest($formID, $entryID)) {
164 + return false;
93 165 }
94 166
95 - private function show404()
96 - {
97 - global $wp_query;
98 - $wp_query->set_404();
99 - status_header(404);
100 - get_template_part(404);
101 - exit();
167 + $filePath = FileHandler::getEntriesFileUploadDir($formID, $entryID) . DIRECTORY_SEPARATOR . $fileID;
168 + if (is_readable($filePath)) {
169 + return $filePath;
102 170 }
103 - private function isRequestedFileExists()
104 - {
105 - if (!isset($_GET['formID']) || !isset($_GET['entryID']) || !isset($_GET['fileID'])) {
106 - return false;
107 - }
108 - $formID = intval(sanitize_text_field($_GET['formID']));
109 - $entryID = intval(sanitize_text_field($_GET['entryID']));
110 - $fileID = sanitize_file_name($_GET['fileID']);
111 - $filePath = BITFORMS_UPLOAD_DIR . DIRECTORY_SEPARATOR . $formID . DIRECTORY_SEPARATOR . $entryID . DIRECTORY_SEPARATOR . $fileID;
112 - if (is_readable($filePath)) {
113 - return $filePath;
114 - }
115 171
116 - return false;
117 - }
172 + return false;
173 + }
118 174
119 - private function fileDownloadORView($filePath, $forceDownload = false)
120 - {
121 - if ($forceDownload) {
122 - header("Content-Type: application/force-download");
123 - header("Content-Type: application/octet-stream");
124 - header("Content-Type: application/download");
125 - header('Content-Disposition: attachment; filename="' . basename($filePath) . '"');
126 - } else {
127 - $fileInfo = wp_check_filetype($filePath);
128 - $content_types='text/plain';
129 - if($fileInfo['type'] && $fileInfo['ext']){
130 - $content_types=$fileInfo['type'];
131 - $ext = $fileInfo['ext'];
132 - if (in_array($ext[1], ['txt', 'php', 'html', 'xhtml', 'json'])) {
133 - $content_types = 'text/plain';
134 - }
135 - }
136 - header('Content-Disposition:filename="' . basename($filePath) . '"');
137 - header("Content-Type: $content_types");
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';
138 190 }
139 - header('Content-Description: File Transfer');
140 - header('Expires: 0');
141 - header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
142 - header('Pragma: public');
143 - header('Content-Length: ' . filesize($filePath));
144 - header("Content-Transfer-Encoding: binary ");
145 - flush();
146 - readfile($filePath);
147 - die();
191 + }
192 + header('Content-Disposition:filename="' . basename($filePath) . '"');
193 + header("Content-Type: $content_types");
148 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 + }
149 206 }