PluginProbe ʕ •ᴥ•ʔ
Kirki – Freeform Page Builder, Website Builder & Customizer / 6.2.3
Kirki – Freeform Page Builder, Website Builder & Customizer v6.2.3
6.2.4 6.2.3 6.2.2 6.2.1 6.2.0 6.1.1 6.1.0 6.0.14 6.0.13 6.0.12 6.0.11 6.0.10 6.0.9 6.0.8 6.0.7 6.0.6 6.0.5 6.0.4 6.0.3 6.0.2 6.0.1 3.1.3 3.1.4 3.1.5 3.1.6 3.1.7 3.1.8 3.1.9 4.0.19 4.0.20 4.0.21 4.0.22 4.0.23 4.0.24 4.1 4.2.0 5.0.0 5.1.0 5.1.1 5.2.0 5.2.1 5.2.2 5.2.3 6.0.0 trunk 3.0.40 3.0.41 3.0.42 3.0.43 3.0.44 3.0.45 3.1.0 3.1.1 3.1.2
kirki / app / Services / FontService.php
kirki / app / Services Last commit date
AppsService.php 1 month ago CollaborationCommentService.php 1 month ago CollaborationService.php 3 weeks ago CollectionItemService.php 1 week ago CollectionService.php 3 weeks ago ContentManagerTemplateBinder.php 3 weeks ago ContentManagerTemplateService.php 3 weeks ago EditorService.php 1 month ago FontService.php 1 week ago FormSubmissionService.php 1 week ago GlobalDataService.php 1 month ago MediaService.php 1 month ago PageService.php 3 weeks ago PageSettingsService.php 1 month ago PostService.php 3 weeks ago UtilityPageService.php 3 weeks ago
FontService.php
204 lines
1 <?php
2
3 namespace Kirki\App\Services;
4
5 defined('ABSPATH') || exit;
6
7 use Exception;
8 use Kirki\App\DTO\GoogleFontDTO;
9 use Kirki\App\Supports\Facades\GlobalData;
10 use Kirki\App\Supports\FileHandler;
11 use Kirki\Framework\Http\Response;
12 use Kirki\Framework\Supports\Facades\File;
13 use Kirki\Framework\Supports\Facades\Http;
14
15 use function Kirki\App\get_upload_directory;
16 use function Kirki\App\get_upload_directory_url;
17 use function Kirki\Framework\clean_path;
18
19 class FontService
20 {
21 public function download_google_font(GoogleFontDTO $payload)
22 {
23 // Extra security: keep only a-z, 0-9, and hyphens
24 $font_family_slug = preg_replace('/[^a-z0-9\-]/i', '', sanitize_title_with_dashes(basename($payload->family)));
25
26 if (empty($font_family_slug) || strpos($font_family_slug, '..') !== false) {
27 throw new Exception(esc_html__('Not valid font family.', 'kirki'), Response::FORBIDDEN);
28 }
29
30 $has_write_permission = File::is_writable(get_upload_directory());
31
32 if (!$has_write_permission) {
33 throw new Exception(esc_html__('Upload directory is not writable.', 'kirki'), Response::FORBIDDEN);
34 }
35
36 $font_dir = clean_path(get_upload_directory() . "/kirki-fonts/{$font_family_slug}", false);
37 $css_file_path = clean_path($font_dir . "/{$font_family_slug}.css", false);
38
39 FileHandler::verify_directory_traversal($css_file_path);
40
41 if (File::exists($css_file_path)) {
42 throw new Exception(esc_html__('Font already downloaded.', 'kirki'), Response::FORBIDDEN);
43 }
44
45 $font_url_parts = wp_parse_url($payload->fontUrl);
46
47 if (!$font_url_parts) {
48 throw new Exception(esc_html__('Invalid URL.', 'kirki'));
49 }
50
51 if (($font_url_parts['scheme'] ?? '') !== 'https') {
52 throw new Exception(esc_html__('Only HTTPS is allowed.', 'kirki'));
53 }
54
55 if (($font_url_parts['host'] ?? '') !== 'fonts.googleapis.com') {
56 throw new Exception(esc_html__('Only Google Fonts is supported.', 'kirki'));
57 }
58
59 try {
60 if (!File::is_directory($font_dir)) {
61 File::make_dir($font_dir);
62 }
63
64 $response = Http::with_options([
65 'redirection' => 0
66 ])->get($payload->fontUrl);
67
68 if ($response->failed()) {
69 throw new Exception(esc_html__('Failed to fetch Google Fonts CSS.', 'kirki'));
70 }
71
72 $formats = [
73 'woff2' => 'woff2',
74 'woff' => 'woff',
75 'ttf' => 'truetype',
76 'otf' => 'opentype',
77 ];
78
79 // Keep Google's CSS verbatim (all weights, styles and unicode-range)
80 // and only swap the remote font files for their local counterparts.
81 $css = $response->body();
82
83 if (!preg_match_all('/url\(\s*["\']?([^"\'()]+)["\']?\s*\)/i', $css, $matches)) {
84 throw new Exception(esc_html__('No usable font files were downloaded.', 'kirki'));
85 }
86
87 $local_files = [];
88
89 foreach (array_unique($matches[1]) as $url) {
90 $url_parts = wp_parse_url($url);
91
92 if (!$url_parts) {
93 throw new Exception(esc_html__('Invalid URL.', 'kirki'));
94 }
95
96 if (($url_parts['scheme'] ?? '') !== 'https') {
97 throw new Exception(esc_html__('Only HTTPS is allowed.', 'kirki'));
98 }
99
100 if (($url_parts['host'] ?? '') !== 'fonts.gstatic.com') {
101 throw new Exception(esc_html__('Only Google Fonts is supported.', 'kirki'));
102 }
103
104 $extension = strtolower(pathinfo(parse_url($url, PHP_URL_PATH), PATHINFO_EXTENSION));
105
106 if (!array_key_exists($extension, $formats)) {
107 /* translators: %s: file extension */
108 throw new Exception(sprintf(esc_html__('Invalid or unsafe file extension: %s.', 'kirki'), $extension));
109 }
110
111 $file_name = preg_replace('/[^a-zA-Z0-9._-]/', '', basename(parse_url($url, PHP_URL_PATH)));
112
113 if (empty($file_name)) {
114 throw new Exception(esc_html__('Invalid file name.', 'kirki'));
115 }
116
117 $file_path = clean_path($font_dir . '/' . $file_name, false);
118
119 FileHandler::verify_directory_traversal($file_path);
120
121 if (!File::exists($file_path)) {
122 $font_response = Http::with_options([
123 'redirection' => 0
124 ])->get($url);
125
126 if ($font_response->failed()) {
127 throw new Exception(esc_html__('Failed to fetch font file.', 'kirki'));
128 }
129
130 File::put($file_path, $font_response->__toString());
131 }
132
133 $local_files[$url] = $file_name;
134 }
135
136 $local_css = str_replace(array_keys($local_files), array_values($local_files), $css);
137
138 if (empty($local_css)) {
139 throw new Exception(esc_html__('No usable font files were downloaded.', 'kirki'));
140 }
141
142 File::put($css_file_path, $local_css);
143 $payload->localUrl = clean_path(get_upload_directory_url() . "/kirki-fonts/{$font_family_slug}/{$font_family_slug}.css", false);
144
145 $this->save_google_font_into_global_custom_fonts($payload);
146
147 return $payload;
148
149 } catch (Exception $exception) {
150 if (File::is_directory($font_dir)) {
151 File::delete($font_dir);
152 }
153
154 throw $exception;
155 }
156 }
157
158 public function remove_google_font(GoogleFontDTO $payload) {
159 $font_family_slug = sanitize_title_with_dashes(basename($payload->family));
160 $font_dir = clean_path(get_upload_directory() . "/kirki-fonts/{$font_family_slug}", false);
161
162 FileHandler::verify_directory_traversal($font_dir);
163
164 if (File::is_directory($font_dir)) {
165 File::delete($font_dir);
166 }
167
168 $payload->exclude(['localUrl']);
169
170 $this->save_google_font_into_global_custom_fonts($payload);
171
172 return true;
173 }
174
175 protected function save_google_font_into_global_custom_fonts(GoogleFontDTO $font) {
176 // first get the font data
177 $custom_fonts = GlobalData::get_global_custom_fonts();
178
179 if (empty($custom_fonts)) {
180 $custom_fonts = [];
181 }
182
183 if (!empty($font->family)) {
184 $custom_fonts[$font->family] = $font->to_array();
185
186 GlobalData::update_global_custom_fonts($custom_fonts);
187 }
188 }
189
190 public function remove_custom_fonts_permanently_from_directory(array $fonts)
191 {
192 foreach ($fonts as $font) {
193 // Remove font from local.
194 $font_family_slug = sanitize_title_with_dashes(basename($font['family']));
195 $font_local_dir = clean_path(get_upload_directory() . "/kirki-fonts/{$font_family_slug}", false);
196
197 FileHandler::verify_directory_traversal($font_local_dir);
198
199 if (File::is_directory($font_local_dir)) {
200 File::delete($font_local_dir);
201 }
202 }
203 }
204 }