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 +81 -17 2.03.3.1 View file →
@@ -1,15 +1,60 @@
1 1 <?php
2 2
3 3 namespace BitCode\BitForm\Core\Util;
4 4
5 -final class FileDownloadProvider {
6 - public function register() {
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 + {
7 50 add_action('template_redirect', [$this, 'authCheckandFrceDownloadHelper']);
8 51 add_shortcode('bitforms-frontend-file', [$this, 'handleFileDownload']);
9 52 }
10 53
11 - public function handleFileDownload() {
54 + public function handleFileDownload()
55 + {
56 + // File download: form/entry/file IDs read from query string; authorization enforced via isAuthorizedFileRequest() below.
12 57 if (!isset($_GET['formID']) || !isset($_GET['entryID']) || !isset($_GET['fileID'])) {
13 58 global $wp_query;
14 59 $wp_query->set_404();
15 60 status_header(404);
@@ -15,18 +60,26 @@
15 60 status_header(404);
16 61 get_template_part(404);
17 62 exit();
18 63 }
19 - $formID = intval(sanitize_text_field($_GET['formID']));
20 - $entryID = intval(sanitize_text_field($_GET['entryID']));
21 - $fileID = sanitize_file_name($_GET['fileID']);
22 - $filePath = BITFORMS_UPLOAD_DIR . DIRECTORY_SEPARATOR . $formID . DIRECTORY_SEPARATOR . $entryID . DIRECTORY_SEPARATOR . $fileID;
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 +
23 73 if (is_readable($filePath)) {
24 74 $this->fileDownloadORView($filePath, true);
75 + } else {
76 + $this->show404();
25 77 }
26 78 }
27 79
28 - public static function getBaseDownloadURL() {
80 + public static function getBaseDownloadURL()
81 + {
29 82 $routes = get_option('bitforms_routes');
30 83 if (isset($routes['file'])) {
31 84 $file_page = get_post($routes['file']);
32 85 if (empty($file_page)) {
@@ -65,9 +118,10 @@
65 118
66 119 return $file_page_slug;
67 120 }
68 121
69 - public function authCheckandFrceDownloadHelper() {
122 + public function authCheckandFrceDownloadHelper()
123 + {
70 124 if (!is_singular('bitforms')) {
71 125 return;
72 126 }
73 127 global $post;
@@ -86,9 +140,10 @@
86 140 }
87 141 }
88 142 }
89 143
90 - private function show404() {
144 + private function show404()
145 + {
91 146 global $wp_query;
92 147 $wp_query->set_404();
93 148 status_header(404);
94 149 get_template_part(404);
@@ -94,16 +149,23 @@
94 149 get_template_part(404);
95 150 exit();
96 151 }
97 152
98 - private function isRequestedFileExists() {
153 + private function isRequestedFileExists()
154 + {
155 + // File download: IDs read from query string; authorization enforced via isAuthorizedFileRequest() below.
99 156 if (!isset($_GET['formID']) || !isset($_GET['entryID']) || !isset($_GET['fileID'])) {
100 157 return false;
101 158 }
102 - $formID = intval(sanitize_text_field($_GET['formID']));
103 - $entryID = intval(sanitize_text_field($_GET['entryID']));
104 - $fileID = sanitize_file_name($_GET['fileID']);
105 - $filePath = BITFORMS_UPLOAD_DIR . DIRECTORY_SEPARATOR . $formID . DIRECTORY_SEPARATOR . $entryID . DIRECTORY_SEPARATOR . $fileID;
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;
106 168 if (is_readable($filePath)) {
107 169 return $filePath;
108 170 }
109 171
@@ -109,9 +171,10 @@
109 171
110 172 return false;
111 173 }
112 174
113 - private function fileDownloadORView($filePath, $forceDownload = false) {
175 + private function fileDownloadORView($filePath, $forceDownload = false)
176 + {
114 177 if ($forceDownload) {
115 178 header('Content-Type: application/force-download');
116 179 header('Content-Type: application/octet-stream');
117 180 header('Content-Type: application/download');
@@ -121,9 +184,9 @@
121 184 $content_types = 'text/plain';
122 185 if ($fileInfo['type'] && $fileInfo['ext']) {
123 186 $content_types = $fileInfo['type'];
124 187 $ext = $fileInfo['ext'];
125 - if (in_array($ext[1], ['txt', 'php', 'html', 'xhtml', 'json'])) {
188 + if (in_array($ext, ['txt', 'php', 'html', 'xhtml', 'json'], true)) {
126 189 $content_types = 'text/plain';
127 190 }
128 191 }
129 192 header('Content-Disposition:filename="' . basename($filePath) . '"');
@@ -135,8 +198,9 @@
135 198 header('Pragma: public');
136 199 header('Content-Length: ' . filesize($filePath));
137 200 header('Content-Transfer-Encoding: binary ');
138 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.
139 203 readfile($filePath);
140 204 die();
141 205 }
142 206 }