PluginProbe
Fluent Support – Helpdesk & Customer Support Ticket System / 1.10.2
Fluent Support – Helpdesk & Customer Support Ticket System v1.10.2
2.4.0 2.3.2 2.3.1 2.3.0 2.2.1 2.2.0 trunk 1.10.0 1.10.1 1.10.2 1.10.3 1.10.4 1.10.5 1.4.0 1.4.1 1.4.2 1.4.5 1.4.6 1.4.7 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 All 68 releases
fluent-support / app / Hooks / Handlers / ExternalPages.php

ExternalPages.php in Fluent Support – Helpdesk & Customer Support Ticket System 1.10.2, at app/Hooks/Handlers/ExternalPages.php

213 lines 6.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentSupport\App\Hooks\Handlers;
4
5
6 use FluentSupport\App\Models\Attachment;
7 use FluentSupport\App\Models\Ticket;
8 use FluentSupport\App\Services\Helper;
9 use FluentSupport\Framework\Support\Arr;
10
11 class ExternalPages
12 {
13 public function route()
14 {
15 // First verify nonce for security
16 if (!isset($_REQUEST['_wpnonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_REQUEST['_wpnonce'])), 'fluent-support')) {
17 wp_die(esc_html__('Security check failed. Please try again.', 'fluent-support'), 403);
18 return;
19 }
20
21 // Then check permissions if user is logged in
22 if (!get_current_user_id()) {
23 wp_die(esc_html__('You do not have permission to access this page.', 'fluent-support'), 403);
24 return;
25 }
26
27 $route = sanitize_text_field($_REQUEST['fs_view']);
28
29 $methodMaps = [
30 'ticket' => 'handleTicketView'
31 ];
32
33 if (isset($methodMaps[$route])) {
34 $this->{$methodMaps[$route]}();
35 }
36
37 }
38
39 public function handleTicketView()
40 {
41 if (!Helper::isPublicSignedTicketEnabled()) {
42 $this->handleInvalidTicket();
43 } else {
44 $this->handleValidTicket();
45 }
46 }
47
48 /**
49 * Display the attachment.
50 *
51 * Uses the new rewrite endpoint to get an attachment ID
52 * and display the attachment if the currently logged in user
53 * has the authorization to.
54 *
55 * @return void
56 * @since 3.2.0
57 */
58 public function view_attachment()
59 {
60 // First verify nonce for security
61 if (!isset($_REQUEST['_wpnonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_REQUEST['_wpnonce'])), 'fluent-support')) {
62 wp_die(esc_html__('Security check failed. Please try again.', 'fluent-support'), 403);
63 return;
64 }
65
66 // Then check permissions if user is logged in
67 if (!get_current_user_id()) {
68 wp_die(esc_html__('You do not have permission to access this file.', 'fluent-support'), 403);
69 return;
70 }
71
72 $attachmentHash = sanitize_text_field($_REQUEST['fst_file']);
73
74 if (empty($attachmentHash)) {
75 die('Invalid Attachment Hash');
76 }
77
78 $attachment = $this->getAttachmentByHash($attachmentHash);
79
80 if (!$attachment) {
81 die('Invalid Attachment Hash');
82 }
83
84 // check signature hash
85 if (!$this->validateAttachmentSignature($attachment)) {
86 $dieMessage = esc_html__('Sorry, Your secure sign is invalid, Please reload the previous page and get new signed url', 'fluent-support');
87 die(esc_html($dieMessage)); // Escaping the die message again for safety
88 }
89
90 //If external file
91 if ('local' !== $attachment->driver) {
92 if(!empty($attachment->full_url)){
93 $this->redirectToExternalAttachment($attachment->full_url);
94 }else{
95 die('File could not be found');
96 }
97 }
98
99 //Handle Local file
100 if (!file_exists($attachment->file_path)) {
101 die('File could not be found');
102 }
103 $this->serveLocalAttachment($attachment);
104 }
105
106 private function getAttachmentByHash($attachmentHash)
107 {
108 return Attachment::where('file_hash', $attachmentHash)->first();
109 }
110
111 private function validateAttachmentSignature($attachment)
112 {
113 // Sanitize and validate secure_sign input - don't trust any input
114 if (!isset($_REQUEST['secure_sign'])) {
115 return false;
116 }
117
118 $secureSign = sanitize_text_field(wp_unslash($_REQUEST['secure_sign']));
119 $sign = md5($attachment->id . date('YmdH'));
120 return $sign === $_REQUEST['secure_sign'];
121 // return hash_equals($sign, $secureSign);
122 }
123
124 private function handleInvalidTicket()
125 {
126 // Nonce already verified in route() method
127 // Validate and sanitize input - don't trust any input
128 $ticketId = absint(Arr::get($_REQUEST, 'ticket_id', 0));
129
130 if (!$ticketId) {
131 $this->showInvalidPortalMessage();
132 return;
133 }
134
135 $ticket = Ticket::where('id', $ticketId)->first();
136
137 if (!$ticket) {
138 $this->showInvalidPortalMessage();
139 } else {
140 // Check permissions if user is logged in
141 if (!get_current_user_id()) {
142 wp_die(esc_html__('You do not have permission to access this ticket.', 'fluent-support'), 403);
143 return;
144 }
145 $this->redirectToTicketView($ticket);
146 }
147 }
148
149 private function handleValidTicket()
150 {
151 // Nonce already verified in route() method
152 // Validate and sanitize inputs - don't trust any input
153 $ticketHash = sanitize_text_field(Arr::get($_REQUEST, 'support_hash', ''));
154 $ticketId = absint(Arr::get($_REQUEST, 'ticket_id', 0));
155
156 if (empty($ticketHash) || !$ticketId) {
157 $this->showInvalidPortalMessage();
158 return;
159 }
160
161 $ticket = Ticket::where('hash', $ticketHash)->where('id', $ticketId)->first();
162
163 if (!$ticket) {
164 $this->showInvalidPortalMessage();
165 } elseif (get_current_user_id()) {
166 $this->redirectToTicketView($ticket);
167 }
168 }
169
170 private function showInvalidPortalMessage()
171 {
172 echo '<h3 style="text-align: center; margin: 50px 0;">' . esc_html__('Invalid Support Portal URL', 'fluent-support') . '</h3>';
173 die();
174 }
175
176 private function redirectToTicketView($ticket)
177 {
178 $redirectUrl = Helper::getTicketViewUrl($ticket);
179 $this->redirectToExternalAttachment($redirectUrl);
180 }
181
182 private function redirectToExternalAttachment($redirectUrl)
183 {
184 wp_redirect($redirectUrl, 307);
185 exit();
186 }
187
188 // Helper method to serve an attachment
189 private function serveLocalAttachment($attachment)
190 {
191 $file_path = realpath($attachment->file_path);
192 $uploads = wp_upload_dir();
193 $uploads_dir = realpath($uploads['basedir']); // Ensures both paths are absolute
194
195 if (!$file_path || !$uploads_dir || strpos($file_path, $uploads_dir) !== 0 || !file_exists($file_path)) {
196 wp_die(esc_html__('File not found or access denied', 'fluent-support'), 403);
197 return;
198 }
199
200 ob_get_clean();
201 $original_user_agent = ini_get('user_agent');
202 ini_set('user_agent', 'Fluent Support/' . FLUENT_SUPPORT_VERSION . '; ' . esc_url(get_bloginfo('url')));
203
204 header("Content-Type: " . esc_attr($attachment->file_type));
205 header("Content-Disposition: inline; filename=\"" . esc_attr($attachment->title) . "\"");
206 readfile($file_path);
207
208 ini_set('user_agent', $original_user_agent);
209 die();
210 }
211
212 }
213