PluginProbe
Fluent Support – Helpdesk & Customer Support Ticket System / 2.4.5
Fluent Support – Helpdesk & Customer Support Ticket System v2.4.5
2.4.5 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 All 69 releases
fluent-support / app / Services / Tickets / Importer / Common.php

Common.php in Fluent Support – Helpdesk & Customer Support Ticket System 2.4.5, at app/Services/Tickets/Importer/Common.php

177 lines 6.6 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\Services\Tickets\Importer;
4
5 use FluentSupport\App\Models\Person;
6
7 class Common
8 {
9 public static function updateOrCreatePerson($personData)
10 {
11 $emailArray = [
12 'email' => $personData['email'],
13 'person_type' => $personData['person_type']
14 ];
15
16 $person = Person::updateOrCreate($emailArray, $personData);
17 return $person->toArray();
18 }
19
20 public static function formatPersonData($personData, $type)
21 {
22 if(!$personData) {
23 return [];
24 }
25
26 $name = explode(' ', $personData->name);
27
28 return [
29 'first_name' => $name[0] ?? '',
30 'last_name' => $name[1] ?? '',
31 'email' => $personData->email ?? $personData->address ?? null,
32 'person_type' => $type
33 ];
34 }
35
36 // Reduce a remote-supplied attachment name to a safe, traversal-free basename.
37 public static function sanitizeAttachmentFilename($fileName)
38 {
39 $fileName = wp_basename((string) $fileName);
40 $fileName = sanitize_file_name($fileName);
41 $fileName = str_replace("\0", '', $fileName);
42 $fileName = ltrim($fileName, '.');
43
44 if ($fileName === '') {
45 $fileName = 'attachment-' . wp_generate_password(8, false, false);
46 }
47
48 return $fileName;
49 }
50
51 // Reject anything that isn't a plain https:// hostname resolving to a public,
52 // non-reserved IP — blocks loopback, RFC1918, link-local/cloud-metadata
53 // (169.254.0.0/16), and raw IP-literal hosts (real vendor tenants are never IPs).
54 public static function isSafeRemoteUrl($url)
55 {
56 if (!is_string($url) || $url === '') {
57 return false;
58 }
59
60 $parts = wp_parse_url($url);
61 if (empty($parts['scheme']) || strtolower($parts['scheme']) !== 'https' || empty($parts['host'])) {
62 return false;
63 }
64
65 if (!empty($parts['user']) || !empty($parts['pass'])) {
66 return false;
67 }
68
69 $host = trim($parts['host'], '.');
70
71 if (filter_var($host, FILTER_VALIDATE_IP)) {
72 return false;
73 }
74
75 $ip = gethostbyname($host);
76 if ($ip === $host) {
77 return false;
78 }
79
80 return filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) !== false;
81 }
82
83 // Download a file from a remote URL and create a new directory for this if not exists
84 // Then save the file to the new directory and move this directory to a new given directory
85 public static function downloadFile($remoteUrl, $baseDir, $fileName)
86 {
87 if (!self::isSafeRemoteUrl($remoteUrl)) {
88 return new \WP_Error('import_unsafe_url', __('Attachment URL is not allowed.', 'fluent-support'));
89 }
90
91 $baseDir = trailingslashit($baseDir);
92
93 if (!wp_mkdir_p($baseDir)) {
94 return new \WP_Error('import_dir_error', __('Could not create the attachment import directory.', 'fluent-support'));
95 }
96
97 $realBaseDir = realpath($baseDir);
98 if ($realBaseDir === false) {
99 return new \WP_Error('import_dir_error', __('Could not resolve the attachment import directory.', 'fluent-support'));
100 }
101 $realBaseDir = rtrim($realBaseDir, '/\\') . DIRECTORY_SEPARATOR;
102
103 $safeFileName = self::sanitizeAttachmentFilename($fileName);
104 $existingPath = $baseDir . $safeFileName;
105
106 // Preserve the original dedupe behaviour: if this exact attachment was
107 // already imported for this ticket, reuse it instead of re-downloading
108 // (wp_unique_filename() would otherwise mint a "-1" copy every re-import).
109 if (file_exists($existingPath)) {
110 $filePath = $existingPath;
111 $uniqueFileName = $safeFileName;
112 } else {
113 $uniqueFileName = wp_unique_filename($baseDir, $safeFileName);
114 $filePath = $baseDir . $uniqueFileName;
115 }
116
117 // Belt-and-braces: the resolved parent directory must still be the base dir.
118 $resolvedParent = realpath(dirname($filePath));
119 if ($resolvedParent === false || strpos($resolvedParent . DIRECTORY_SEPARATOR, $realBaseDir) !== 0) {
120 return new \WP_Error('import_path_error', __('Resolved attachment path escapes the import directory.', 'fluent-support'));
121 }
122
123 if (file_exists($filePath)) {
124 return $filePath;
125 }
126
127 $maxBytes = 25 * MB_IN_BYTES;
128
129 // Cap the transfer itself (not just the assembled body) so an oversized
130 // remote response can't be fully pulled into memory before we reject it.
131 // Ask for one byte over the limit so a truncated response (real size > cap)
132 // can still be told apart from a response that legitimately ends at the cap.
133 $response = wp_safe_remote_get($remoteUrl, [
134 'timeout' => 60,
135 'stream' => false,
136 'limit_response_size' => $maxBytes + 1,
137 'redirection' => 0,
138 ]);
139
140 if (is_wp_error($response)) {
141 return $response;
142 }
143
144 $status = (int) wp_remote_retrieve_response_code($response);
145 if ($status < 200 || $status >= 300) {
146 return new \WP_Error('import_download_error', sprintf(__('Attachment download failed with HTTP status %d.', 'fluent-support'), $status));
147 }
148
149 $file_contents = wp_remote_retrieve_body($response);
150 if (empty($file_contents)) {
151 return new \WP_Error('import_download_error', __('Attachment download returned an empty response.', 'fluent-support'));
152 }
153
154 if (strlen($file_contents) > $maxBytes) {
155 return new \WP_Error('import_download_error', __('Attachment exceeds the maximum allowed download size.', 'fluent-support'));
156 }
157
158 $tmpPath = $baseDir . '.' . $uniqueFileName . '-' . uniqid('', true) . '.part';
159 if (file_put_contents($tmpPath, $file_contents) === false) {
160 return new \WP_Error('import_write_error', __('Could not write the downloaded attachment.', 'fluent-support'));
161 }
162
163 $fileType = wp_check_filetype_and_ext($tmpPath, $uniqueFileName);
164 if (empty($fileType['ext']) || empty($fileType['type'])) {
165 @unlink($tmpPath);
166 return new \WP_Error('import_filetype_error', __('Attachment file type is not allowed.', 'fluent-support'));
167 }
168
169 if (!@rename($tmpPath, $filePath)) {
170 @unlink($tmpPath);
171 return new \WP_Error('import_write_error', __('Could not save the downloaded attachment.', 'fluent-support'));
172 }
173
174 return $filePath;
175 }
176 }
177