PluginProbe ʕ •ᴥ•ʔ
Kirki – Freeform Page Builder, Website Builder & Customizer / 6.1.1
Kirki – Freeform Page Builder, Website Builder & Customizer v6.1.1
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 3 days ago CollaborationCommentService.php 3 weeks ago CollaborationService.php 3 weeks ago CollectionItemService.php 3 weeks ago CollectionService.php 3 weeks ago EditorService.php 3 weeks ago FontService.php 3 days ago GlobalDataService.php 3 days ago MediaService.php 3 weeks ago PageService.php 3 days ago PageSettingsService.php 3 weeks ago UtilityPageService.php 3 days ago
FontService.php
199 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 if (isset($payload->files['regular'])) {
73 $payload->files['400'] = $payload->files['regular'];
74 unset($payload->files['regular']);
75 }
76
77 $formats = [
78 'woff2' => 'woff2',
79 'woff' => 'woff',
80 'ttf' => 'truetype',
81 'otf' => 'opentype',
82 ];
83 $local_css = '';
84
85 foreach ($payload->files as $weight => $url) {
86 $allowed_ext = array_keys($formats);
87 $extension = strtolower(pathinfo($url, PATHINFO_EXTENSION));
88
89 if (!in_array($extension, $allowed_ext, true)) {
90 /* translators: %s: file extension */
91 throw new Exception(sprintf(esc_html__('Invalid or unsafe file extension: %s.', 'kirki'), $extension));
92 }
93
94 $url_parts = wp_parse_url($url);
95
96 if (!$url_parts) {
97 throw new Exception(esc_html__('Invalid URL.', 'kirki'));
98 }
99
100 if (($url_parts['scheme'] ?? '') !== 'https') {
101 throw new Exception(esc_html__('Only HTTPS is allowed.', 'kirki'));
102 }
103
104 if (($url_parts['host'] ?? '') !== 'fonts.gstatic.com') {
105 throw new Exception(esc_html__('Only Google Fonts is supported.', 'kirki'));
106 }
107
108 $file_name = "{$weight}.{$extension}";
109 $file_path = clean_path($font_dir . '/' . $file_name, false);
110
111 FileHandler::verify_directory_traversal($file_path);
112
113 $font_response = Http::with_options([
114 'redirection' => 0
115 ])->get($url);
116
117 if ($font_response->failed()) {
118 throw new Exception(esc_html__('Failed to fetch font file.', 'kirki'));
119 }
120
121 File::put($file_path, $font_response->__toString());
122
123 $format = $formats[$extension] ?? 'truetype';
124 $local_css .= "@font-face {
125 font-family: '{$payload->family}';
126 font-style: normal;
127 font-weight: {$weight};
128 font-display: swap;
129 src: url('{$file_name}') format('{$format}');
130 }\n";
131 }
132
133 if (empty($local_css)) {
134 throw new Exception(esc_html__('No usable font files were downloaded.', 'kirki'));
135 }
136
137 File::put($css_file_path, $local_css);
138 $payload->localUrl = clean_path(get_upload_directory_url() . "/kirki-fonts/{$font_family_slug}/{$font_family_slug}.css", false);
139
140 $this->save_google_font_into_global_custom_fonts($payload);
141
142 return $payload;
143
144 } catch (Exception $exception) {
145 if (File::is_directory($font_dir)) {
146 File::delete($font_dir);
147 }
148
149 throw $exception;
150 }
151 }
152
153 public function remove_google_font(GoogleFontDTO $payload) {
154 $font_family_slug = sanitize_title_with_dashes(basename($payload->family));
155 $font_dir = clean_path(get_upload_directory() . "/kirki-fonts/{$font_family_slug}", false);
156
157 FileHandler::verify_directory_traversal($font_dir);
158
159 if (File::is_directory($font_dir)) {
160 File::delete($font_dir);
161 }
162
163 $payload->exclude(['localUrl']);
164
165 $this->save_google_font_into_global_custom_fonts($payload);
166
167 return true;
168 }
169
170 protected function save_google_font_into_global_custom_fonts(GoogleFontDTO $font) {
171 // first get the font data
172 $custom_fonts = GlobalData::get_global_custom_fonts();
173
174 if (empty($custom_fonts)) {
175 $custom_fonts = [];
176 }
177
178 if (!empty($font->family)) {
179 $custom_fonts[$font->family] = $font->to_array();
180
181 GlobalData::update_global_custom_fonts($custom_fonts);
182 }
183 }
184
185 public function remove_custom_fonts_permanently_from_directory(array $fonts)
186 {
187 foreach ($fonts as $font) {
188 // Remove font from local.
189 $font_family_slug = sanitize_title_with_dashes(basename($font['family']));
190 $font_local_dir = clean_path(get_upload_directory() . "/kirki-fonts/{$font_family_slug}", false);
191
192 FileHandler::verify_directory_traversal($font_local_dir);
193
194 if (File::is_directory($font_local_dir)) {
195 File::delete($font_local_dir);
196 }
197 }
198 }
199 }