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