This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | namespace CodexShaper\PWA\Http\Controllers; |
||
4 | |||
5 | use CodexShaper\PWA\Model\Setting; |
||
6 | use Exception; |
||
7 | use Illuminate\Http\Request; |
||
8 | use Illuminate\Support\Facades\File; |
||
9 | use Illuminate\Support\Facades\Response; |
||
10 | use Illuminate\Support\Facades\Storage; |
||
11 | |||
12 | class PwaController extends Controller |
||
13 | { |
||
14 | /** |
||
15 | * Display a listing of the resource. |
||
16 | * |
||
17 | * @return \Illuminate\Http\Response |
||
18 | */ |
||
19 | public function index() |
||
20 | { |
||
21 | $pwa = $this->getPwaInstance(); |
||
22 | |||
23 | return view('pwa::settings', compact('pwa')); |
||
24 | } |
||
25 | |||
26 | /** |
||
27 | * Store a newly created resource in storage. |
||
28 | * |
||
29 | * @param \Illuminate\Http\Request $request |
||
30 | * |
||
31 | * @return \Illuminate\Routing\Redirector|\Illuminate\Http\RedirectResponse |
||
32 | */ |
||
33 | public function store(Request $request) |
||
0 ignored issues
–
show
|
|||
34 | { |
||
35 | if (!File::isDirectory(storage_path('app/public/pwa/images/icons'))) { |
||
36 | Storage::makeDirectory('public/pwa/images/icons', 0777, true); |
||
0 ignored issues
–
show
The call to
Storage::makeDirectory() has too many arguments starting with 511 .
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. In this case you can add the ![]() |
|||
37 | } |
||
38 | |||
39 | File::copyDirectory( |
||
40 | config('pwa.icons_path', __DIR__.'/../../../resources/icons'), |
||
41 | storage_path('app/public/pwa/images/icons') |
||
42 | ); |
||
43 | |||
44 | $pwa = $this->getPwaInstance(); |
||
45 | |||
46 | if (!$pwa) { |
||
47 | $pwa = new Setting(); |
||
48 | } |
||
49 | |||
50 | $domain = request()->getHttpHost(); |
||
51 | $tenant_id = null; |
||
52 | if (function_exists('tenant') && isset(tenant()->id)) { |
||
53 | $tenant_id = tenant()->id; |
||
54 | } |
||
55 | |||
56 | $data = $this->getManifestData([ |
||
57 | 'name' => 'Demo App', |
||
58 | 'short_name' => 'DA', |
||
59 | 'start_url' => 'https://'.$domain.'/', |
||
60 | 'background_color' => '#ffffff', |
||
61 | 'theme_color' => '#000000', |
||
62 | 'display' => 'standalone', |
||
63 | ]); |
||
64 | |||
65 | $data['serviceworker'] = $this->generateServiceWorker(); |
||
66 | $data['register_serviceworker'] = $this->generateServiceWorkerRegister(); |
||
67 | |||
68 | $pwa->tenant_id = $tenant_id; |
||
69 | $pwa->domain = $domain; |
||
70 | $pwa->data = $data; |
||
71 | $pwa->status = 1; |
||
72 | $pwa->save(); |
||
73 | |||
74 | return redirect(route('pwa'))->with('success', 'Pwa created successfully.'); |
||
75 | } |
||
76 | |||
77 | /** |
||
78 | * Activate PWA for the current domain. |
||
79 | * |
||
80 | * @return \Illuminate\Routing\Redirector|\Illuminate\Http\RedirectResponse |
||
81 | */ |
||
82 | View Code Duplication | public function activate() |
|
0 ignored issues
–
show
This method seems to be duplicated in your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
83 | { |
||
84 | $pwa = $this->getPwaInstance(); |
||
85 | $pwa->status = 1; |
||
86 | $pwa->save(); |
||
87 | |||
88 | return redirect(route('pwa'))->with('success', 'Pwa activated successfully.'); |
||
89 | } |
||
90 | |||
91 | /** |
||
92 | * Deactivate PWA for the current domain. |
||
93 | * |
||
94 | * @return \Illuminate\Routing\Redirector|\Illuminate\Http\RedirectResponse |
||
95 | */ |
||
96 | View Code Duplication | public function deactivate() |
|
0 ignored issues
–
show
This method seems to be duplicated in your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
97 | { |
||
98 | $pwa = $this->getPwaInstance(); |
||
99 | $pwa->status = 0; |
||
100 | $pwa->save(); |
||
101 | |||
102 | return redirect(route('pwa'))->with('success', 'Pwa deactivated successfully.'); |
||
103 | } |
||
104 | |||
105 | /** |
||
106 | * Update the specified resource in storage. |
||
107 | * |
||
108 | * @param \Illuminate\Http\Request $request |
||
109 | * @param \CodexShaper\PWA\Model\Setting $Setting |
||
110 | * |
||
111 | * @return \Illuminate\Routing\Redirector|\Illuminate\Http\RedirectResponse |
||
112 | */ |
||
113 | public function update(Request $request, Setting $Setting) |
||
0 ignored issues
–
show
|
|||
114 | { |
||
115 | $request->validate([ |
||
0 ignored issues
–
show
|
|||
116 | 'name' => 'required', |
||
117 | 'short_name' => 'required', |
||
118 | 'start_url' => 'required', |
||
119 | 'background_color' => 'required', |
||
120 | 'theme_color' => 'required', |
||
121 | 'display' => 'required', |
||
122 | ]); |
||
123 | View Code Duplication | if (isset($request->icons) && count($request->icons) > 0) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
124 | foreach ($request->icons as $key => $icon) { |
||
125 | $destination_path = 'pwa/images/icons/icon-'.$key.'.png'; |
||
126 | Storage::disk('public')->putFileAs('', $icon, $destination_path); |
||
127 | } |
||
128 | } |
||
129 | |||
130 | View Code Duplication | if (isset($request->splashes) && count($request->splashes)) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
131 | foreach ($request->splashes as $key => $splash) { |
||
132 | $destination_path = 'pwa/images/icons/splash-'.$key.'.png'; |
||
133 | Storage::disk('public')->putFileAs('', $splash, $destination_path); |
||
134 | } |
||
135 | } |
||
136 | |||
137 | $pwa = $this->getPwaInstance(); |
||
138 | |||
139 | if (!$pwa) { |
||
140 | $pwa = new Setting(); |
||
141 | } |
||
142 | |||
143 | $domain = request()->getHttpHost(); |
||
144 | $tenant_id = null; |
||
145 | if (function_exists('tenant') && isset(tenant()->id)) { |
||
146 | $tenant_id = tenant()->id; |
||
147 | } |
||
148 | |||
149 | $data = $this->getManifestData([ |
||
150 | 'name' => $request->name, |
||
151 | 'short_name' => $request->short_name, |
||
152 | 'start_url' => $request->start_url, |
||
153 | 'background_color' => $request->background_color, |
||
154 | 'theme_color' => $request->theme_color, |
||
155 | 'display' => $request->display, |
||
156 | ]); |
||
157 | |||
158 | $data['serviceworker'] = $this->generateServiceWorker(); |
||
159 | $data['register_serviceworker'] = $this->generateServiceWorkerRegister(); |
||
160 | |||
161 | $pwa->tenant_id = $tenant_id; |
||
162 | $pwa->domain = $domain; |
||
163 | $pwa->data = $data; |
||
164 | $pwa->save(); |
||
165 | |||
166 | return redirect(route('pwa'))->with('success', 'Pwa settings updated successfully.'); |
||
167 | } |
||
168 | |||
169 | /** |
||
170 | * Remove the specified resource from storage. |
||
171 | * |
||
172 | * @param \CodexShaper\PWA\Model\Setting $Setting |
||
173 | * |
||
174 | * @throws \Exception |
||
175 | * |
||
176 | * @return \Illuminate\Routing\Redirector|\Illuminate\Http\RedirectResponse |
||
177 | */ |
||
178 | public function destroy(Setting $Setting) |
||
0 ignored issues
–
show
|
|||
179 | { |
||
180 | try { |
||
181 | if (File::isDirectory(storage_path('app/public/pwa'))) { |
||
182 | File::deleteDirectory(storage_path('app/public/pwa')); |
||
183 | } |
||
184 | |||
185 | $pwa = $this->getPwaInstance(); |
||
186 | |||
187 | if ($pwa) { |
||
188 | $pwa->delete(); |
||
189 | |||
190 | return redirect(route('pwa'))->with('success', 'Pwa deleted successfully.'); |
||
191 | } |
||
192 | } catch (Exception $ex) { |
||
193 | throw new Exception($ex->getMessage()); |
||
194 | } |
||
195 | } |
||
196 | |||
197 | /** |
||
198 | * Return manifest.josn content. |
||
199 | * |
||
200 | * @return \Illuminate\Http\Response |
||
201 | */ |
||
202 | public function manifest() |
||
203 | { |
||
204 | $pwa = $this->getPwaInstance(); |
||
205 | $manifest = $pwa->data['manifest']; |
||
206 | $manifestArr = static::prepareManifest($manifest); |
||
207 | |||
208 | return response()->json($manifestArr); |
||
0 ignored issues
–
show
The method
json does only exist in Illuminate\Contracts\Routing\ResponseFactory , but not in Illuminate\Http\Response .
It seems like the method you are trying to call exists only in some of the possible types. Let’s take a look at an example: class A
{
public function foo() { }
}
class B extends A
{
public function bar() { }
}
/**
* @param A|B $x
*/
function someFunction($x)
{
$x->foo(); // This call is fine as the method exists in A and B.
$x->bar(); // This method only exists in B and might cause an error.
}
Available Fixes
![]() |
|||
209 | } |
||
210 | |||
211 | /** |
||
212 | * Display pwa offline resources. |
||
213 | * |
||
214 | * @return \Illuminate\Http\Response |
||
215 | */ |
||
216 | public function offline() |
||
217 | { |
||
218 | return view('pwa::offline'); |
||
219 | } |
||
220 | |||
221 | /** |
||
222 | * Prepare manifest data. |
||
223 | * |
||
224 | * @param array $manifest |
||
225 | * |
||
226 | * @return array |
||
227 | */ |
||
228 | public static function prepareManifest($manifest) |
||
229 | { |
||
230 | $data = [ |
||
231 | 'name' => $manifest['name'], |
||
232 | 'short_name' => $manifest['short_name'], |
||
233 | 'start_url' => asset($manifest['start_url']), |
||
234 | 'display' => $manifest['display'], |
||
235 | 'theme_color' => $manifest['theme_color'], |
||
236 | 'background_color' => $manifest['background_color'], |
||
237 | 'orientation' => $manifest['orientation'], |
||
238 | 'status_bar' => $manifest['status_bar'], |
||
239 | 'splash' => $manifest['splash'], |
||
240 | ]; |
||
241 | |||
242 | foreach ($manifest['icons'] as $size => $file) { |
||
243 | $fileInfo = pathinfo($file['path']); |
||
244 | $data['icons'][] = [ |
||
245 | 'src' => $file['path'], |
||
246 | 'type' => 'image/'.$fileInfo['extension'], |
||
247 | 'sizes' => $size, |
||
248 | 'purpose' => $file['purpose'], |
||
249 | ]; |
||
250 | } |
||
251 | |||
252 | foreach ($manifest['shortcuts'] as $shortcut) { |
||
253 | if (array_key_exists('icons', $shortcut)) { |
||
254 | $fileInfo = pathinfo($shortcut['icons']['src']); |
||
255 | $icon = [ |
||
256 | 'src' => $shortcut['icons']['src'], |
||
257 | 'type' => 'image/'.$fileInfo['extension'], |
||
258 | 'purpose' => $shortcut['icons']['purpose'], |
||
259 | ]; |
||
260 | } else { |
||
261 | $icon = []; |
||
262 | } |
||
263 | |||
264 | $data['shortcuts'][] = [ |
||
265 | 'name' => trans($shortcut['name']), |
||
266 | 'description' => trans($shortcut['description']), |
||
267 | 'url' => $shortcut['url'], |
||
268 | 'icons' => [ |
||
269 | $icon, |
||
270 | ], |
||
271 | ]; |
||
272 | } |
||
273 | |||
274 | foreach ($manifest['custom'] as $tag => $value) { |
||
275 | $data[$tag] = $value; |
||
276 | } |
||
277 | |||
278 | return $data; |
||
279 | } |
||
280 | |||
281 | /** |
||
282 | * Prepare manifest data from request for database. |
||
283 | * |
||
284 | * @param array $data |
||
285 | * |
||
286 | * @return array |
||
287 | */ |
||
288 | protected function getManifestData($data) |
||
289 | { |
||
290 | return [ |
||
291 | 'name' => $data['name'], |
||
292 | 'manifest' => [ |
||
293 | 'name' => $data['name'], |
||
294 | 'short_name' => $data['short_name'], |
||
295 | 'start_url' => $data['start_url'], |
||
296 | 'background_color' => $data['background_color'], |
||
297 | 'theme_color' => $data['theme_color'], |
||
298 | 'display' => $data['display'], |
||
299 | 'orientation' => 'any', |
||
300 | 'status_bar' => 'black', |
||
301 | 'icons' => [ |
||
302 | '72x72' => [ |
||
303 | 'path' => pwa_asset('images/icons/icon-72x72.png'), |
||
304 | 'purpose' => 'any', |
||
305 | ], |
||
306 | '96x96' => [ |
||
307 | 'path' => pwa_asset('images/icons/icon-96x96.png'), |
||
308 | 'purpose' => 'any', |
||
309 | ], |
||
310 | '128x128' => [ |
||
311 | 'path' => pwa_asset('images/icons/icon-128x128.png'), |
||
312 | 'purpose' => 'any', |
||
313 | ], |
||
314 | '144x144' => [ |
||
315 | 'path' => pwa_asset('images/icons/icon-144x144.png'), |
||
316 | 'purpose' => 'any', |
||
317 | ], |
||
318 | '152x152' => [ |
||
319 | 'path' => pwa_asset('images/icons/icon-152x152.png'), |
||
320 | 'purpose' => 'any', |
||
321 | ], |
||
322 | '192x192' => [ |
||
323 | 'path' => pwa_asset('images/icons/icon-192x192.png'), |
||
324 | 'purpose' => 'any', |
||
325 | ], |
||
326 | '384x384' => [ |
||
327 | 'path' => pwa_asset('images/icons/icon-384x384.png'), |
||
328 | 'purpose' => 'any', |
||
329 | ], |
||
330 | '512x512' => [ |
||
331 | 'path' => pwa_asset('images/icons/icon-512x512.png'), |
||
332 | 'purpose' => 'any', |
||
333 | ], |
||
334 | ], |
||
335 | 'splash' => [ |
||
336 | '640x1136' => pwa_asset('images/icons/splash-640x1136.png'), |
||
337 | '750x1334' => pwa_asset('images/icons/splash-750x1334.png'), |
||
338 | '828x1792' => pwa_asset('images/icons/splash-828x1792.png'), |
||
339 | '1125x2436' => pwa_asset('images/icons/splash-1125x2436.png'), |
||
340 | '1242x2208' => pwa_asset('images/icons/splash-1242x2208.png'), |
||
341 | '1242x2688' => pwa_asset('images/icons/splash-1242x2688.png'), |
||
342 | '1536x2048' => pwa_asset('images/icons/splash-1536x2048.png'), |
||
343 | '1668x2224' => pwa_asset('images/icons/splash-1668x2224.png'), |
||
344 | '1668x2388' => pwa_asset('images/icons/splash-1668x2388.png'), |
||
345 | '2048x2732' => pwa_asset('images/icons/splash-2048x2732.png'), |
||
346 | ], |
||
347 | 'shortcuts' => [], |
||
348 | 'custom' => [], |
||
349 | ], |
||
350 | ]; |
||
351 | } |
||
352 | |||
353 | /** |
||
354 | * Return serviceworker.js content. |
||
355 | * |
||
356 | * @return \Illuminate\Http\Response |
||
357 | */ |
||
358 | View Code Duplication | public function serviceWorker() |
|
0 ignored issues
–
show
This method seems to be duplicated in your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
359 | { |
||
360 | $pwa = $this->getPwaInstance(); |
||
361 | |||
362 | if ($pwa) { |
||
363 | $response = Response::make($pwa->data['serviceworker'], 200); |
||
364 | $response->header('Content-Type', 'text/javascript'); |
||
365 | $response->setSharedMaxAge(31536000); |
||
366 | $response->setMaxAge(31536000); |
||
367 | $response->setExpires(new \DateTime('+1 year')); |
||
368 | |||
369 | return $response; |
||
370 | } |
||
371 | } |
||
372 | |||
373 | /** |
||
374 | * Return serviceworker register content. |
||
375 | * |
||
376 | * @return \Illuminate\Http\Response |
||
377 | */ |
||
378 | View Code Duplication | public function serviceWorkerRegisterContent() |
|
0 ignored issues
–
show
This method seems to be duplicated in your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
379 | { |
||
380 | $pwa = $this->getPwaInstance(); |
||
381 | |||
382 | if ($pwa) { |
||
383 | $response = Response::make($pwa->data['register_serviceworker'], 200); |
||
384 | $response->header('Content-Type', 'text/javascript'); |
||
385 | $response->setSharedMaxAge(31536000); |
||
386 | $response->setMaxAge(31536000); |
||
387 | $response->setExpires(new \DateTime('+1 year')); |
||
388 | |||
389 | return $response; |
||
390 | } |
||
391 | } |
||
392 | |||
393 | /** |
||
394 | * Generate service worker. |
||
395 | * |
||
396 | * @return string |
||
397 | */ |
||
398 | protected function generateServiceWorker() |
||
399 | { |
||
400 | $public_path = asset('/'); |
||
0 ignored issues
–
show
$public_path is not used, you could remove the assignment.
This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently. $myVar = 'Value';
$higher = false;
if (rand(1, 6) > 3) {
$higher = true;
} else {
$higher = false;
}
Both the ![]() |
|||
401 | $pwa_asset = pwa_asset(''); |
||
402 | $base_url = url('/'); |
||
403 | |||
404 | return <<<SERVICE_WORKER |
||
405 | var staticCacheName = "pwa-v" + new Date().getTime(); |
||
406 | var filesToCache = [ |
||
407 | '$base_url/offline', |
||
408 | '{$base_url}/css/app.css', |
||
409 | '{$base_url}/js/app.js', |
||
410 | '$pwa_asset/images/icons/icon-72x72.png', |
||
411 | '$pwa_asset/images/icons/icon-96x96.png', |
||
412 | '$pwa_asset/images/icons/icon-128x128.png', |
||
413 | '$pwa_asset/images/icons/icon-144x144.png', |
||
414 | '$pwa_asset/images/icons/icon-152x152.png', |
||
415 | '$pwa_asset/images/icons/icon-192x192.png', |
||
416 | '$pwa_asset/images/icons/icon-384x384.png', |
||
417 | '$pwa_asset/images/icons/icon-512x512.png', |
||
418 | ]; |
||
419 | |||
420 | // Cache on install |
||
421 | self.addEventListener("install", event => { |
||
422 | this.skipWaiting(); |
||
423 | event.waitUntil( |
||
424 | caches.open(staticCacheName) |
||
425 | .then(cache => { |
||
426 | return cache.addAll(filesToCache); |
||
427 | }) |
||
428 | ) |
||
429 | }); |
||
430 | |||
431 | // Clear cache on activate |
||
432 | self.addEventListener('activate', event => { |
||
433 | event.waitUntil( |
||
434 | caches.keys().then(cacheNames => { |
||
435 | return Promise.all( |
||
436 | cacheNames |
||
437 | .filter(cacheName => (cacheName.startsWith("pwa-"))) |
||
438 | .filter(cacheName => (cacheName !== staticCacheName)) |
||
439 | .map(cacheName => caches.delete(cacheName)) |
||
440 | ); |
||
441 | }) |
||
442 | ); |
||
443 | }); |
||
444 | |||
445 | // Serve from Cache |
||
446 | self.addEventListener("fetch", event => { |
||
447 | event.respondWith( |
||
448 | caches.match(event.request) |
||
449 | .then(response => { |
||
450 | return response || fetch(event.request); |
||
451 | }) |
||
452 | .catch(() => { |
||
453 | return caches.match('offline'); |
||
454 | }) |
||
455 | ) |
||
456 | }); |
||
457 | SERVICE_WORKER; |
||
458 | } |
||
459 | |||
460 | /** |
||
461 | * Register service worker. |
||
462 | * |
||
463 | * @return string |
||
464 | */ |
||
465 | protected function generateServiceWorkerRegister() |
||
466 | { |
||
467 | $serviceworker_route = route('pwa.serviceworker'); |
||
468 | $scope = config('pwa.scope', '.'); |
||
469 | |||
470 | return <<<REGISTER_SERVICE_WORKER |
||
471 | // Get serviceworker contents |
||
472 | var serviceworker = "$serviceworker_route"; |
||
473 | // Initialize the service worker |
||
474 | if ('serviceWorker' in navigator) { |
||
475 | navigator.serviceWorker.register(serviceworker, { |
||
476 | scope: '$scope' |
||
477 | }).then(function (registration) { |
||
478 | // Registration was successful |
||
479 | console.log('Laravel PWA enable successfully. Enjoy it!'); |
||
480 | }, function (err) { |
||
481 | // registration failed |
||
482 | console.log('Laravel PWA registration failed. Please check the error: ', err); |
||
483 | }); |
||
484 | } |
||
485 | REGISTER_SERVICE_WORKER; |
||
486 | } |
||
487 | |||
488 | /** |
||
489 | * Get Setting instance. |
||
490 | * |
||
491 | * @return \CodexShaper\PWA\Model\Setting |
||
492 | */ |
||
493 | public function getPwaInstance() |
||
494 | { |
||
495 | return Setting::where('domain', '=', request()->getHttpHost())->first(); |
||
496 | } |
||
497 | |||
498 | /** |
||
499 | * Return storage asset. |
||
500 | * |
||
501 | * @return \Illuminate\Http\Response |
||
502 | */ |
||
503 | public function asset($path) |
||
504 | { |
||
505 | try { |
||
506 | return response()->file(storage_path("app/public/pwa/$path")); |
||
0 ignored issues
–
show
The method
file does only exist in Illuminate\Contracts\Routing\ResponseFactory , but not in Illuminate\Http\Response .
It seems like the method you are trying to call exists only in some of the possible types. Let’s take a look at an example: class A
{
public function foo() { }
}
class B extends A
{
public function bar() { }
}
/**
* @param A|B $x
*/
function someFunction($x)
{
$x->foo(); // This call is fine as the method exists in A and B.
$x->bar(); // This method only exists in B and might cause an error.
}
Available Fixes
![]() |
|||
507 | } catch (\Throwable $th) { |
||
508 | abort(404); |
||
509 | } |
||
510 | } |
||
511 | } |
||
512 |
This check looks from parameters that have been defined for a function or method, but which are not used in the method body.