PluginProbe ʕ •ᴥ•ʔ
Transferito: WP Migration / 14.1.3
Transferito: WP Migration v14.1.3
trunk 11.4.0 12.0.0 13.1.0 14.0.0 14.0.11 14.0.7 14.1.0 14.1.1 14.1.2 14.1.3 14.1.4
transferito / src / Controllers / Transfer.php
transferito / src / Controllers Last commit date
Transfer.php 6 months ago
Transfer.php
3697 lines
1 <?php
2
3 namespace Transferito\Controllers;
4
5 use Transferito\Models\Core\Config;
6 use Transferito\Models\Transfer\CodeBase;
7 use Transferito\Models\Transfer\Database;
8 use Transferito\Models\Transfer\Upload;
9 use Transferito\Models\Core\Api as TransferitoAPI;
10 use Transferito\Models\Settings\Telemetry;
11
12 if (!defined('ABSPATH')) exit;
13
14 class Transfer {
15
16 private $codeBase;
17 private $dataBase;
18 private $upload;
19 private $api;
20 private $options;
21 private $telemetry;
22
23 public function __construct()
24 {
25 if (current_user_can('activate_plugins')) {
26 $this->api = new TransferitoAPI();
27 $this->codeBase = new CodeBase();
28 $this->dataBase = new Database();
29 $this->upload = new Upload();
30 $this->telemetry = new Telemetry();
31
32 $this->options = get_option( 'transferito_settings_option' );
33
34 add_action("wp_ajax_preparing_transfer", [ $this, "prepareDownload"]);
35 add_action("wp_ajax_start_migration", [ $this, "startMigration"]);
36 add_action("wp_ajax_clean_up_files", [ $this, "cleanUp"]);
37 add_action("wp_ajax_status_check", [ $this, "statusCheck"]);
38
39 /**
40 * @deprecated
41 */
42 add_action("wp_ajax_get_directories", [ $this, "getFTPDirectories"]);
43 add_action("wp_ajax_correct_directory_validation", [ $this, "directoryValidation"]);
44
45
46 add_action("wp_ajax_initiate_local_upload", [ $this, "initiateUpload"]);
47 add_action("wp_ajax_upload_chunk", [ $this, "uploadChunk"]);
48 add_action("wp_ajax_complete_upload", [ $this, "completeUpload"]);
49 add_action("wp_ajax_preparing_codebase", [ $this, "prepareCodebase"]);
50 add_action("wp_ajax_add_files_to_codebase_archive", [ $this, "addFilesToCodebase"]);
51 add_action("wp_ajax_codebase_completion", [ $this, "codebaseArchiveComplete"]);
52 add_action("wp_ajax_preparing_database", [ $this, "prepareDatabase"]);
53 add_action("wp_ajax_create_db_exports", [ $this, "chunkedDBExport"]);
54 add_action("wp_ajax_database_completion", [ $this, "databaseExportComplete"]);
55 add_action("wp_ajax_archive_db_exports", [ $this, "archiveDBExport"]);
56 add_action("wp_ajax_database_relocation", [ $this, "databaseRelocation"]);
57 add_action("wp_ajax_database_relocation_check", [ $this, "databaseRelocationCheck"]);
58 add_action("wp_ajax_check_archive_completion", [ $this, "checkArchiveCompletion"]);
59 add_action("wp_ajax_archive_creation", [ $this, "archiveCreation"]);
60 add_action("wp_ajax_archive_progress_check", [ $this, "archiveProgressCheck"]);
61 add_action("wp_ajax_cpanel_authentication", [ $this, "cpanelAuthentication"]);
62 add_action("wp_ajax_server_detail_validation", [ $this, "serverDetailValidation"]);
63 add_action("wp_ajax_transferito_hide_welcome_screen", [ $this, "hideWelcomeScreen"]);
64
65
66 add_action("wp_ajax_database_detail_validation", [ $this, "databaseValidation"]);
67 add_action("wp_ajax_hide_quickstart_popup", [ $this, "hideQuickStart"]);
68 add_action("wp_ajax_send_request_form", [ $this, "sendRequestForm"]);
69 add_action("wp_ajax_log_transferito_event", [ $this, "logEvent"]);
70 add_action("wp_ajax_start_directory_search", [ $this, "startDirectoryCheck"]);
71 add_action("wp_ajax_get_directory_check_update", [ $this, "getDirectoryCheckUpdate"]);
72 add_action('wp_ajax_check_premium_api_keys', [ $this, "checkPremiumApiKeys"]);
73 add_action('wp_ajax_download_transferito_verification_file', [ $this, "downloadVerificationFile"]);
74 add_action('wp_ajax_transferito_validate_destination_server_connection', [ $this, "destinationServerValidation"]);
75 add_action('wp_ajax_mark_backup_completed', [ $this, "markBackupCompleted"]);
76
77
78 /**
79 * Move to routing class
80 */
81 add_action("wp_ajax_check_current_site", [$this, "wpSiteCheck"]);
82 add_action("wp_ajax_check_cpanel_availability", [$this, "cPanelCheck"]);
83 add_action("wp_ajax_choose_migration_method", [$this, "chooseMigrationMethod"]);
84 add_action("wp_ajax_switch_mode", [$this, "switchMode"]);
85 add_action("wp_ajax_screen_route_redirection", [$this, "screenRouting"]);
86 add_action("wp_ajax_load_directory_template", [$this, "loadDirectoryTemplate"]);
87 }
88 }
89
90 public function logEvent()
91 {
92 /**
93 * Verify nonce with every event logged
94 */
95 check_ajax_referer('log_event', 'securityKey');
96
97 /**
98 * Get the ByPass exec usage Flag from the settings
99 */
100 $settingsOption = get_option('transferito_settings_option');
101
102 /**
103 * Check to see if the tracking has been opted in
104 */
105 $trackingEnabled = isset($settingsOption['transferito_enable_debug_tracking'])
106 ? $settingsOption['transferito_enable_debug_tracking']
107 : false;
108
109 /**
110 * Logs the event if the user has enabled debug tracking
111 */
112 if ($trackingEnabled) {
113 $event = isset($_POST['event'])
114 ? sanitize_text_field(wp_unslash($_POST['event']))
115 : '';
116 $eventProperties = isset($_POST['eventProperties'])
117 ? array_map('sanitize_text_field', wp_unslash($_POST['eventProperties']))
118 : '';
119
120 $this->telemetry->pushEvent($event, $eventProperties);
121 }
122
123 wp_send_json_success([ 'logged' => true ]);
124 }
125
126 /**
127 * @deprecated
128 * @return void
129 */
130 public function getFTPDirectories()
131 {
132 /**
133 * verify nonce with every FTP Directory request
134 */
135 check_ajax_referer('get_directory_list', 'securityKey');
136
137 /**
138 * Check that the ftpInfo element exists
139 */
140 $getDirectoriesPayload = get_transient('transferito_manual_server_detail');
141
142 /**
143 * Assign the correct path to the ftp details
144 */
145 if (count($getDirectoriesPayload) > 0) {
146 $path = isset($_POST['path']) ? sanitize_text_field(wp_unslash($_POST['path'])) : '';
147 $getDirectoriesPayload['path'] = $path;
148 }
149
150
151 try {
152 /**
153 * Hit the endpoint to return the results
154 */
155 $getDirectoriesRequest = $this->api->getDirectories($getDirectoriesPayload);
156
157 /**
158 * Listen to a successful response
159 * & then filter the array to just return the directories
160 */
161 if ($getDirectoriesRequest['code'] === 200) {
162 $directoryList = $getDirectoriesRequest['message']->filelist;
163 $filteredDirectoryList = array_filter($directoryList, function($value, $key) {
164 return $value->type === 'dir';
165 }, ARRAY_FILTER_USE_BOTH);
166 wp_send_json_success([
167 'folders' => $filteredDirectoryList,
168 'port' => $getDirectoriesPayload['ftpPort']
169 ]);
170 } else if ($getDirectoriesRequest['code'] !== 200) {
171 wp_send_json_error([
172 'err' => $getDirectoriesRequest['message']->result,
173 'payload' => $getDirectoriesPayload,
174 'message' => $getDirectoriesRequest
175 ], 400);
176 }
177 } catch(\Exception $exception) {
178 wp_send_json_error('There has been an issue - If this problem persists. Contact support', 500);
179 }
180 }
181
182 /**
183 * @deprecated
184 * @return void
185 */
186 public function startDirectoryCheck()
187 {
188 /**
189 * verify nonce with every FTP Directory request
190 */
191 check_ajax_referer('get_directory_list', 'securityKey');
192
193 /**
194 * Check that the ftpInfo element exists
195 */
196 $directoryCheckPayload = get_transient('transferito_manual_server_detail');
197
198 /**
199 * Get the destination server URL
200 */
201 $domain = get_transient('transferito_migration_domain');
202
203 /**
204 * Make initial Directory Check request
205 */
206 $directoryCheck = $this->api->startDirectoryCheck(array(
207 'ftpHost' => $directoryCheckPayload['ftpHost'],
208 'ftpUser' => $directoryCheckPayload['ftpUser'],
209 'ftpPass' => $directoryCheckPayload['ftpPass'],
210 'ftpPort' => $directoryCheckPayload['ftpPort'],
211 'useSFTP' => $directoryCheckPayload['useSFTP'],
212 'URL' => $domain,
213 ));
214
215 /**
216 * Check the status code
217 */
218 $httpStatusCode = $directoryCheck['code'];
219
220 /**
221 * Check we have a successful response
222 */
223 if ($httpStatusCode === 200) {
224 wp_send_json_success(array_merge((array)$directoryCheck['message'], $directoryCheckPayload));
225 }
226
227 /**
228 * If we have anything other than a 200
229 * Fail the endpoint
230 */
231 if ($httpStatusCode !== 200) {
232 wp_send_json_error($directoryCheck['message'], 400);
233 }
234
235 }
236
237 /**
238 * @deprecated
239 * @return void
240 */
241 public function getDirectoryCheckUpdate()
242 {
243 /**
244 * verify nonce with every FTP Directory request
245 */
246 check_ajax_referer('get_directory_list', 'securityKey');
247
248 $url = isset($_POST['url']) ? sanitize_text_field(wp_unslash($_POST['url'])) : '';
249 $directoryCheckId = isset($_POST['directoryCheckId']) ? sanitize_text_field(wp_unslash($_POST['directoryCheckId'])) : '';
250 $directoryCheckUpdate = $this->api->getDirectoryUpdate(array(
251 'URL' => $url,
252 'directoryCheckId' => $directoryCheckId
253 ));
254
255 /**
256 * Check the status code
257 */
258 $httpStatusCode = $directoryCheckUpdate['code'];
259
260 /**
261 * If we have anything other than a 200
262 * Fail the endpoint
263 */
264 if ($httpStatusCode !== 200) {
265 wp_send_json_error($directoryCheckUpdate['message'], 400);
266 }
267
268 /**
269 * If the path has been found
270 * Update the Server Detail
271 */
272 if ($directoryCheckUpdate['message']->found) {
273 $serverDetails = get_transient('transferito_manual_server_detail');
274
275 /**
276 * Fix to add the path if the document root is the ftp path
277 */
278 if ($directoryCheckUpdate['message']->path === '' && $serverDetails['useSFTP'] === '0') {
279 $directoryCheckUpdate['message']->path = './';
280 }
281
282 /**
283 * Add the path to the server details array
284 */
285 $serverDetails['path'] = $directoryCheckUpdate['message']->path;
286 $serverDetails['ftpPath'] = $directoryCheckUpdate['message']->path;
287 $serverDetails['URL'] = $url;
288
289 /**
290 * Update the server array
291 */
292 set_transient('transferito_manual_server_detail', $serverDetails);
293 }
294
295 wp_send_json_success($directoryCheckUpdate['message']);
296 }
297
298 public function markBackupCompleted()
299 {
300 try {
301
302 /**
303 * Check the correct action is being assigned
304 */
305 $correctAction = isset($_POST['areFilesBeingUploaded']) && filter_var(wp_unslash($_POST['areFilesBeingUploaded']), FILTER_VALIDATE_BOOLEAN)
306 ? 'start_upload'
307 : 'start_migration';
308
309 /**
310 * Check the action
311 */
312 check_ajax_referer($correctAction, 'security');
313
314 /**
315 * Update the status
316 */
317 $this->api->updateBackupStatus([
318 'token' => get_transient('transferito_migration_token'),
319 'timestamp' => get_transient('transferito_migration_timestamp')
320 ]);
321
322 } catch(\Exception $exception) {
323 // DO Nothing
324 }
325
326 wp_send_json_success();
327
328 }
329
330
331 public function hideWelcomeScreen()
332 {
333 /**
334 * verify nonce with every template change call
335 */
336 check_ajax_referer('transferito_welcome_screen', 'securityKey');
337
338 set_transient('transferito_hide_welcome_screen', true);
339
340 wp_send_json_success();
341 }
342
343 public function wpSiteCheck()
344 {
345 /**
346 * verify nonce with every template change call
347 */
348 check_ajax_referer('template_change', 'actionKey');
349
350 /**
351 * Clean up previous migration that may have failed
352 */
353 $this->freshStart();
354
355 $siteDetails = getDirectorySize(TRANSFERITO_ABSPATH);
356 $zipEnabled = class_exists('ZipArchive');
357 $getWPInstallationSizes = get_transient('transferito_installation_size');
358
359 /**
360 * Check if the user has exceeded the max size
361 */
362 $this->checkSiteWithinFreeTier($siteDetails);
363
364 /**
365 * Log any failures created during the getDirectorySize check
366 */
367 if (isset($getWPInstallationSizes['errors']) && count($getWPInstallationSizes['errors']) > 0) {
368 $errorMessage = implode('|', $getWPInstallationSizes['errors']);
369 $this->api->failedMigration($errorMessage);
370 }
371
372 /**
373 * Default the exec enabled flag to false
374 */
375 $execEnabled = false;
376
377 /**
378 * Get the ByPass exec usage Flag from the settings
379 */
380 $settingsOption = get_option('transferito_settings_option');
381 $bypassExecUsage = isset($settingsOption['transferito_bypass_exec_archive_creation'])
382 ? $settingsOption['transferito_bypass_exec_archive_creation']
383 : false;
384
385 /**
386 * If the flag is not set
387 * Check to see if the OS is windows
388 * If it is not - Check to see if exec can be used
389 */
390 if (!$bypassExecUsage) {
391 /**
392 * Windows HOT Fix
393 * @todo return to the original check when FULL FIX has been implemented
394 */
395 $isWindows = strtoupper(substr(PHP_OS, 0, 3)) === 'WIN';
396 $execEnabled = $isWindows
397 ? false
398 : function_exists('exec') && @exec('echo EXEC') == 'EXEC';
399 }
400
401
402 $metRequirements = !$zipEnabled && !$execEnabled ? false : true;
403 $useZip = !$execEnabled;
404
405 /**
406 * Save the requirements on load
407 */
408 set_transient('transferito_requirements', [
409 'metRequirements' => $metRequirements,
410 'useZip' => $useZip
411 ]);
412
413 /**
414 * Initially default the fallback to false
415 */
416 set_transient('transferito_request_fallback', false);
417
418 /**
419 * Get Welcome Screen option
420 */
421 $hideWelcomeScreen = get_transient('transferito_hide_welcome_screen');
422
423 /**
424 * Return the template for all users
425 */
426 $response = [
427 'hideWelcomeScreen' => $hideWelcomeScreen,
428 'htmlTemplate' => loadTemplate('parts/migration/cpanel-check', [
429 'secondaryMessage' => 'To start your migration, please choose a migration method',
430 'metRequirements' => $metRequirements,
431 'hideQuickStart' => true
432 ])
433 ];
434
435 /**
436 * Push loadScreen event to telemetry
437 */
438 $this->telemetry->pushEvent('loadScreen', [
439 'screenName' => 'DestinationURL'
440 ]);
441
442 wp_send_json_success($response);
443 }
444
445 public function switchMode()
446 {
447 /**
448 * Verify nonce with every screen switch
449 */
450 check_ajax_referer('template_change', 'securityKey');
451
452 $transferMethod = isset($_POST['method']) ? sanitize_text_field(wp_unslash($_POST['method'])) : '';
453 $domain = get_transient('transferito_migration_domain');
454 $cPanelAllowed = boolval(get_transient('transferito_cpanel_allowed'));
455
456 /**
457 * Has the cpanel domain
458 */
459 $hasCpanelDomain = get_transient('transferito_cpanel_domain');
460
461 /**
462 * Set / Remove the transient based on the transfer method
463 */
464 if ($transferMethod === 'cpanel' && !$hasCpanelDomain) {
465 /**
466 * Domain
467 */
468 $updatedDomain = rtrim(trim($domain), '/');
469
470 /**
471 * cPanel admin URL
472 */
473 $cpanelAdminURL = $updatedDomain . ':2083';
474
475 /**
476 * Result from the cPanel check
477 */
478 $response = $this->api->cPanelAvailabilityCheck($cpanelAdminURL);
479
480 /**
481 * Check that the string is in the response
482 */
483 $cPanelInHTMLSource = stripos($response['message'], 'cpanel') !== false;
484
485 /**
486 * Check if we should default to cPanel
487 */
488 $cPanelAllowed = $response['code'] === 200 && $cPanelInHTMLSource;
489
490 /**
491 * Correct cPanel URL
492 */
493 $cpanelFinalAdminURLSplit = explode(':2083', $response['url']);
494
495 if ($cPanelAllowed) {
496 set_transient('transferito_cpanel_domain', $cpanelFinalAdminURLSplit[0]);
497 }
498 }
499
500 /**
501 * Update transient - For the transfer method
502 */
503 $updatedTransferMethod = $transferMethod === 'cpanel' ? 'cpanel' : 'manual';
504 set_transient('transferito_transfer_method', $updatedTransferMethod);
505
506 /**
507 * Build the part dynamically
508 */
509 $templatePath = 'parts/migration/' . $transferMethod . '/main';
510
511 /**
512 * Load the correct template based on the site size
513 */
514 $htmlTemplate = loadTemplate($templatePath, [
515 'cpanelAllowed' => $cPanelAllowed,
516 'directories' => Config::getWPContentPaths(),
517 'cpanelDetail' => get_transient('transferito_cpanel_auth_details'),
518 'cpanelCompleted' => get_transient('transferito_cpanel_auth_details_completed'),
519 'detail' => get_transient('transferito_manual_server_detail')
520 ]);
521
522 /**
523 * Push loadScreen event to telemetry
524 */
525 $this->telemetry->pushEvent('loadScreen', [
526 'screenName' => $transferMethod === 'cpanel' ? 'cPanelAuthentication' : 'FTPAuthentication'
527 ]);
528
529 /**
530 * Return the object
531 */
532 wp_send_json_success([
533 'cPanelAllowed' => $cPanelAllowed,
534 'URL' => $domain,
535 'transferMethod' => $transferMethod,
536 'htmlTemplate' => $htmlTemplate,
537 ]);
538 }
539
540 public function screenRouting()
541 {
542 /**
543 * Verify nonce with every screen route change
544 */
545 check_ajax_referer('template_change', 'securityKey');
546
547 try {
548 $screenRoute = isset($_POST['route']) ? sanitize_text_field(wp_unslash($_POST['route'])) : '';
549 $serverDetail = get_transient('transferito_manual_server_detail');
550 $url = explode('://', get_transient('transferito_migration_domain'));
551 $domain = count($url) === 2 ? $url[1] : $url[0];
552 $cPanelDomainList = get_transient('transferito_cpanel_domain_selection');
553
554 $mappedScreenRoutes = [
555 'destinationURL' => loadTemplate('parts/migration/cpanel-check', [
556 'url' => $domain,
557 'mainMessage' => '',
558 'secondaryMessage' => 'To start your migration, please choose a migration method',
559 'metRequirements' => get_transient('transferito_requirements')['metRequirements'],
560 'hideQuickStart' => true
561 ]),
562 'databaseAuthentication' => loadTemplate('parts/migration/manual/database-detail', [
563 'detail' => $serverDetail,
564 'completed' => get_transient('transferito_database_detail_completed')
565 ])
566 ];
567
568 /**
569 * If the route does not exist in the mapped routes
570 *
571 * @todo throw error
572 */
573 if (!isset($mappedScreenRoutes[$screenRoute])) {
574 wp_send_json_error([
575 'message' => 'routeDoesNotExist'
576 ], 400);
577 }
578
579 /**
580 * Return the object
581 */
582 wp_send_json_success([
583 'htmlTemplate' => $mappedScreenRoutes[$screenRoute],
584 'detail' => $serverDetail
585 ]);
586 } catch (\Exception $exception) {
587 wp_send_json_error([
588 'message' => $exception->getMessage()
589 ], 400);
590 }
591 }
592
593 public function sendRequestForm()
594 {
595 check_ajax_referer('hosting_guide_request_detail', 'securityKey');
596
597 $this->api->hostingGuideRequest([
598 'email' => isset($_POST['data']['emailAddress']) ? sanitize_text_field(wp_unslash($_POST['data']['emailAddress'])) : '',
599 'hostingProvider' => isset($_POST['data']['hostingProvider']) ? sanitize_text_field(wp_unslash($_POST['data']['hostingProvider'])) : '',
600 'guideName' => isset($_POST['data']['guideName']) ? sanitize_text_field(wp_unslash($_POST['data']['guideName'])) : ''
601 ]);
602
603 /**
604 * Return the object
605 */
606 wp_send_json_success([ 'completed' => true ]);
607 }
608
609 public function checkPremiumApiKeys()
610 {
611 check_ajax_referer('in_plugin_premium_upgrade', 'securityKey');
612
613 try {
614
615 $publicKey = isset($_POST['data']['publicKey']) ? sanitize_text_field(wp_unslash($_POST['data']['publicKey'])) : '';
616 $secretKey = isset($_POST['data']['secretKey']) ? sanitize_text_field(wp_unslash($_POST['data']['secretKey'])) : '';
617
618 /**
619 * Check the user's API keys
620 */
621 $hasValidAPIKeys = $this->areAPIKeysValid($publicKey, $secretKey);
622
623 /**
624 * If the API Keys aren't valid
625 * Throw an error
626 */
627 if (!$hasValidAPIKeys) {
628 set_transient('transferito_user_status', 'FREE');
629 throw new \Exception('NOT_VALID');
630 }
631
632 /**
633 * Get Current Options
634 */
635 $options = get_option('transferito_settings_option');
636
637 /**
638 * Update the API Keys
639 */
640 $options['public_transferito_key'] = sanitize_text_field($publicKey);
641 $options['secret_transferito_key'] = sanitize_text_field($secretKey);
642
643 /**
644 * Update the options
645 */
646 update_option('transferito_settings_option', $options);
647
648 /**
649 * Set the USER status
650 */
651 set_transient('transferito_user_status', 'PREMIUM');
652
653 wp_send_json_success([ 'validated' => true ]);
654 } catch(\Exception $exception) {
655 wp_send_json_error([ 'validated' => true ], 403);
656 }
657 }
658
659 /**
660 * @remove all functionality for quickstart
661 */
662 public function hideQuickStart()
663 {
664 set_transient('transferito_hide_quick_start_guide', true);
665
666 /**
667 * Return the object
668 */
669 wp_send_json_success(['completed' => true ]);
670 }
671
672 public function loadDirectoryTemplate()
673 {
674 $serverDetail = get_transient('transferito_manual_server_detail');
675
676 /**
677 * Push loadScreen event to telemetry
678 */
679 $this->telemetry->pushEvent('loadScreen', [
680 'screenName' => 'directorySelector'
681 ]);
682
683 /**
684 * Return success object
685 * To update the nav and change the child template
686 */
687 wp_send_json_success([
688 'template' => loadTemplate('parts/migration/manual/directory-selection', []),
689 'path' => $serverDetail['path'],
690 'navigation' => [
691 'completed' => 'transferitoNav__manualFTPDetails',
692 'active' => 'transferitoNav__manualFTPDirectorySelect'
693 ],
694 ]);
695
696 }
697
698 public function downloadVerificationFile()
699 {
700 check_ajax_referer('connect_to_server', 'securityKey');
701
702 try {
703
704 $verification = $this->api->createVerificationRequest();
705 $created = $verification['code'] === 200;
706
707 /**
708 * Throw an error if the response isn't 200
709 */
710 if (!$created) {
711 throw new \Exception($verification['message']);
712 }
713
714 /**
715 * Assign verification details
716 */
717 $verificationInfo = $verification['message'];
718
719 /**
720 * Build the download URL
721 */
722 $url = Config::getBaseApiUrl() . '/verification/' . $verificationInfo->uuid . '/' . $verificationInfo->token;
723
724 /**
725 * Get the domain
726 */
727 $domain = get_transient('transferito_migration_unchanged_domain');
728
729 /**
730 * Create Verification Array
731 */
732 $verificationDetails = [
733 'domain' => $domain,
734 'token' => $verificationInfo->token,
735 'filename' => $verificationInfo->filename,
736 'connected' => false
737 ];
738
739 /**
740 * Get the list of connected sites
741 */
742 $connectedSites = get_transient('transferito_connected_sites');
743
744 /**
745 * Check to see if the sites are empty if so - Create an array
746 */
747 if (!$connectedSites) {
748 $connectedSites = array();
749 }
750
751 /**
752 * Add verification Details to the array
753 */
754 $connectedSites[] = $verificationDetails;
755
756 /**
757 * Save the verification details
758 */
759 set_transient('transferito_connected_sites', $connectedSites);
760
761 /**
762 * Return the response
763 */
764 wp_send_json_success([ 'url' => $url ]);
765
766 } catch (\Exception $exception) {
767 wp_send_json_error([
768 'message' => 'Unable to create Download Request',
769 'debug' => $exception->getMessage()
770 ], 400);
771 }
772 }
773
774 public function destinationServerValidation()
775 {
776 check_ajax_referer('connect_to_server', 'securityKey');
777
778 try {
779
780 $this->destinationServerValidationCheck(false);
781
782 /**
783 * Return the response
784 */
785 wp_send_json([ 'connected' => true ], 200);
786
787 } catch (\Exception $exception) {
788 wp_send_json([
789 'e' => get_transient('transferito_connected_sites'),
790 'connected' => false ,
791 'reason' => $exception->getMessage()
792 ], 400);
793 }
794 }
795
796 public function addCheckServerValidation($url, $filename, $token)
797 {
798 /**
799 * Check to see if connection can be verified
800 */
801 $siteAdded = false;
802
803 /**
804 * Set the migration domain
805 */
806 set_transient('transferito_migration_unchanged_domain', $url);
807
808 try {
809 /**
810 * Check to see of a site already exists and is connected
811 */
812 $this->destinationServerValidationCheck(true);
813
814 /**
815 * Assign Value to Site Added
816 */
817 $siteAdded = true;
818
819 } catch (\Exception $exception) {
820
821 /**
822 * Create Verification Array
823 */
824 $verificationDetails = [
825 'domain' => $url,
826 'token' => $token,
827 'filename' => $filename,
828 'connected' => false
829 ];
830
831 /**
832 * Get the list of connected sites
833 */
834 $connectedSites = get_transient('transferito_connected_sites');
835
836 /**
837 * Check to see if the sites are empty if so - Create an array
838 */
839 if (!$connectedSites) {
840 $connectedSites = array();
841 }
842
843 /**
844 * Add verification Details to the array
845 */
846 $connectedSites[] = $verificationDetails;
847
848 /**
849 * Save the verification details
850 */
851 set_transient('transferito_connected_sites', $connectedSites);
852
853 try {
854 /**
855 * Check to see of a site already exists and is connected
856 */
857 $this->destinationServerValidationCheck(true);
858
859 /**
860 * Assign Value to Site Added
861 */
862 $siteAdded = true;
863 } catch (\Exception $secondException) { }
864 }
865
866 return $siteAdded;
867 }
868
869 private function destinationServerValidationCheck($removeNonConnectedSite)
870 {
871 try {
872 $connectionExists = $this->destinationServerConnectionExists('array');
873
874 /**
875 * Check to see if the site exists
876 */
877 if (!$connectionExists) {
878 throw new \Exception('NO_CONNECTION_DETAILS_EXIST');
879 }
880
881 /**
882 * Build the Validation URL
883 */
884 $validationURL = $this->buildVerificationURL(
885 $connectionExists['domain'],
886 $connectionExists['filename']
887 );
888
889 /**
890 * Check to see if connection can be verified
891 */
892 $validationCheck = $this->api->validateServerConnection($validationURL, array(
893 'method' => 'connect',
894 'token' => $connectionExists['token']
895 ));
896
897 /**
898 * Get the Key
899 */
900 $connectionSiteKey = $this->destinationServerConnectionExists('key');
901
902 /**
903 * Check to see if we are connected
904 */
905 $connected = $validationCheck['code'] === 202;
906
907 /**
908 * Check to see if fails
909 */
910 if (!$connected) {
911 /**
912 * If the site no longer exists - Remove it
913 */
914 if ($removeNonConnectedSite) {
915 $this->updateTheDestinationServerConnection($connectionSiteKey, true, 'remove');
916 }
917
918 throw new \Exception('FILE_DOES_NOT_EXIST_YET');
919 }
920
921 /**
922 * Update the connected site array
923 */
924 $this->updateTheDestinationServerConnection($connectionSiteKey, true, 'update');
925 } catch (\Exception $exception) {
926 throw new \Exception(esc_html($exception->getMessage()));
927 }
928 }
929
930 private function buildVerificationURL($domain, $filename)
931 {
932 return $domain . '/' . $filename;
933 }
934
935 private function destinationServerConnectionExists($mode)
936 {
937 /**
938 * Get the domain
939 */
940 $domain = get_transient('transferito_migration_unchanged_domain');
941
942 /**
943 * Get the list of connected sites
944 */
945 $connectedSites = get_transient('transferito_connected_sites');
946
947 /**
948 * If no sites exist - Return false
949 */
950 if (!$connectedSites) {
951 return false;
952 }
953
954 /**
955 * Get the array
956 */
957 $selectedSite = array_filter($connectedSites, function($value) use ($domain) {
958 $domainParts = explode('://', $domain);
959 $siteDomainParts = explode('://', $value['domain']);
960 return $domainParts[1] === $siteDomainParts[1];
961 });
962
963 /**
964 * If the site can't be found - return falsey
965 */
966 if (empty($selectedSite)) {
967 return false;
968 }
969
970 $selectedArrayKey = array_keys($selectedSite)[0];
971
972 /**
973 * If the mode is key just return the key
974 */
975 if ($mode === 'key') {
976 return $selectedArrayKey;
977 }
978
979 return $selectedSite[$selectedArrayKey];
980 }
981
982 private function updateTheDestinationServerConnection($key, $connected, $mode)
983 {
984 /**
985 * Get the list of connected sites
986 */
987 $connectedSites = get_transient('transferito_connected_sites');
988
989 /**
990 * If there aren't any sites return false
991 */
992 if (!$connectedSites) {
993 return false;
994 }
995
996 if ($mode === 'update') {
997 /**
998 * Update the connection Status for the element
999 */
1000 $connectedSites[$key]['connected'] = $connected;
1001 }
1002
1003 if ($mode === 'remove') {
1004 unset($connectedSites[$key]);
1005 }
1006
1007 /**
1008 * Save the connection sites
1009 */
1010 set_transient('transferito_connected_sites', $connectedSites);
1011 }
1012
1013 public function cPanelCheck()
1014 {
1015 /**
1016 * verify nonce with every template change call
1017 */
1018 check_ajax_referer('cpanel_check', 'securityKey');
1019
1020 $localMigrationSanitized = isset($_POST['localMigration'])
1021 ? sanitize_text_field(wp_unslash($_POST['localMigration']))
1022 : '';
1023
1024 $domainSanitized = isset($_POST['domain'])
1025 ? sanitize_text_field(wp_unslash($_POST['domain']))
1026 : '';
1027
1028 $localMigration = filter_var($localMigrationSanitized, FILTER_VALIDATE_BOOL);
1029
1030 /**
1031 * If migrating to use the DesktopApp
1032 */
1033 if ($localMigration) {
1034
1035 /**
1036 * Domain
1037 */
1038 $domain = rtrim(trim($domainSanitized), '/');
1039
1040 /**
1041 * Default transfer method
1042 */
1043 $transferMethod = 'localSiteMigration';
1044
1045 set_transient('transferito_transfer_method', $transferMethod);
1046 set_transient('transferito_migration_domain', $domain);
1047
1048 /**
1049 *
1050 */
1051 wp_send_json_success([
1052 'transferMethod' => $transferMethod,
1053 'cpanelAllowed' => false,
1054 'securityToken' => wp_create_nonce("prepare_migration_files")
1055 ]);
1056
1057
1058 // $this->prepareLocalDownload();
1059
1060 die();
1061 }
1062
1063 /**
1064 *
1065 */
1066 if (!$localMigration) {
1067 $splitURL = explode('://', $domainSanitized);
1068
1069 /**
1070 * Check user hasn't used double protocol
1071 */
1072 if (count($splitURL) !== 2) {
1073 wp_send_json_error([
1074 'message' => 'Failed URL Check'
1075 ], 400);
1076 }
1077
1078 /**
1079 * Domain
1080 */
1081 $domain = rtrim(trim($domainSanitized), '/');
1082
1083 /**
1084 * cPanel admin URL
1085 */
1086 $cpanelAdminURL = $domain . ':2083';
1087
1088 /**
1089 * Result from the cPanel check
1090 */
1091 $response = $this->api->cPanelAvailabilityCheck($cpanelAdminURL);
1092
1093 /**
1094 * Check that the string is in the response
1095 */
1096 $cPanelInHTMLSource = stripos($response['message'], 'cpanel') !== false;
1097
1098 /**
1099 * Check if we should default to cPanel
1100 */
1101 $cPanelAllowed = $response['code'] === 200 && $cPanelInHTMLSource;
1102
1103 /**
1104 * Default transfer method
1105 */
1106 $transferMethod = $cPanelAllowed ? 'cpanel' : 'manual';
1107
1108 /**
1109 * Correct cPanel URL
1110 */
1111 $cpanelFinalAdminURLSplit = explode(':2083', $response['url']);
1112
1113 /**
1114 * Save the domain
1115 */
1116 set_transient('transferito_migration_domain', $domain);
1117 set_transient('transferito_migration_unchanged_domain', $domain);
1118 set_transient('transferito_cpanel_allowed', $cPanelAllowed);
1119 set_transient('transferito_transfer_method', $transferMethod);
1120
1121 /**
1122 * Only save if cPanel is allowed
1123 */
1124 if ($cPanelAllowed) {
1125 set_transient('transferito_cpanel_domain', $cpanelFinalAdminURLSplit[0]);
1126 } else {
1127 set_transient('transferito_cpanel_domain', $domain);
1128 }
1129
1130 $htmlTemplate = '';
1131 $screenName = '';
1132
1133 /**
1134 * Check to see which template to render based on the result of the site connection test
1135 */
1136 try {
1137
1138 $this->destinationServerValidationCheck(true);
1139
1140 /**
1141 * Set the Screen Name
1142 */
1143 $screenName = 'databaseAuthentication';
1144
1145 /**
1146 * Load the correct for the DB Details
1147 * As the user is connected
1148 */
1149 $htmlTemplate = loadTemplate('parts/migration/manual/database-detail', [
1150 'detail' => get_transient('transferito_manual_server_detail'),
1151 'completed' => get_transient('transferito_database_detail_completed'),
1152 ]);
1153
1154 } catch (\Exception $exception) {
1155
1156 /**
1157 * Set the Screen Name
1158 */
1159 $screenName = 'connectToServer';
1160
1161 /**
1162 * Load the correct for the user to connect
1163 */
1164 $htmlTemplate = loadTemplate('parts/migration/connect-to-server', [
1165 'cpanelAllowed' => $cPanelAllowed,
1166 'transferMethod' => $transferMethod,
1167 'url' => $domain
1168 ]);
1169 }
1170
1171 /**
1172 * Push loadScreen event to telemetry
1173 */
1174 $this->telemetry->pushEvent('loadScreen', [
1175 'screenName' => $screenName // @todo change screen name to ConnectToServer
1176 ]);
1177
1178 wp_send_json_success([
1179 't' => $cpanelFinalAdminURLSplit[0],
1180 'cPanelAllowed' => $cPanelAllowed,
1181 'URL' => $domain,
1182 'transferMethod' => $transferMethod,
1183 'htmlTemplate' => $htmlTemplate,
1184 ]);
1185 }
1186 }
1187
1188 /**
1189 * @deprecated
1190 * @return void
1191 */
1192 public function cpanelAuthentication()
1193 {
1194 try {
1195 check_ajax_referer('cpanel_migration', 'securityKey');
1196
1197 /**
1198 * Get the domain
1199 */
1200 $domain = get_transient('transferito_cpanel_domain');
1201
1202 /**
1203 * Get the destination URL
1204 */
1205 $destinationURL = get_transient('transferito_migration_domain');
1206
1207 /**
1208 * Split the destination URL into a domain via the protocol
1209 */
1210 $splitDestinationURL = explode('//', $destinationURL);
1211
1212 /**
1213 * Get the destination Domain
1214 */
1215 $destinationDomain = count($splitDestinationURL) === 2 ? $splitDestinationURL[1] : $destinationURL;
1216
1217 /**
1218 * Pull the auth details
1219 */
1220 $cPanelDetails = isset($_POST['auth']) ? array_map('sanitize_text_field', wp_unslash($_POST['auth'])) : [];
1221 $cPanelDetails['cpanelPass'] = sanitize_text_field(wp_unslash($cPanelDetails['cpanelPass']));
1222 $cPanelDetails['cPanelUseApiToken'] = isset($cPanelDetails['cPanelUseApiToken']) ? false : true;
1223
1224 /**
1225 * Updated the cPanel array
1226 */
1227 $updatedAuthArray = array_merge($cPanelDetails, [ 'cpanelHost' => $domain ]);
1228
1229 /**
1230 * Make a request to the endpoint to get a list of the available domains
1231 */
1232 $authResult = $this->api->cPanelAuth($updatedAuthArray);
1233
1234 /**
1235 * If there is an error authenticating
1236 */
1237 if ($authResult['code'] !== 200) {
1238 throw new \Exception(stripslashes($authResult['message']));
1239 }
1240
1241 /**
1242 * Get the list of selectable domains
1243 */
1244 $availableDomains = $authResult['message'];
1245
1246 /**
1247 * Save the cPanel details
1248 */
1249 set_transient('transferito_cpanel_auth_details', $cPanelDetails);
1250
1251 /**
1252 * Update the completed flag
1253 */
1254 set_transient('transferito_cpanel_auth_details_completed', true);
1255
1256 /**
1257 * Push loadScreen event to telemetry
1258 */
1259 $this->telemetry->pushEvent('loadScreen', [
1260 'screenName' => 'cPanelDomainSelection'
1261 ]);
1262
1263 /**
1264 * Assign the data needed for the template to a variable to save
1265 */
1266 $domainSelectionOptions = [
1267 'domain' => $destinationDomain,
1268 'domains' => $availableDomains,
1269 'username' => $cPanelDetails['cpanelUser'],
1270 'password' => $cPanelDetails['cpanelPass'],
1271 'apiToken' => $cPanelDetails['cPanelApiToken'],
1272 'useApiToken' => $cPanelDetails['cPanelUseApiToken'],
1273 'URL' => $domain,
1274 ];
1275
1276 /**
1277 * Save the domain selection data needed for the template
1278 */
1279 set_transient('transferito_cpanel_domain_selection', $domainSelectionOptions);
1280
1281 /**
1282 * Return success object
1283 * To update the nav and change the child template
1284 */
1285 wp_send_json_success([
1286 'template' => loadTemplate('parts/migration/cpanel/domain-selection', $domainSelectionOptions)
1287 ]);
1288
1289 } catch(\Exception $exception) {
1290 wp_send_json_error([
1291 'template' => loadTemplate('parts/migration/cpanel/main', [
1292 'showErrorPopup' => true
1293 ]),
1294 ], 400);
1295 }
1296 }
1297
1298 /**
1299 * @deprecated
1300 * @return void
1301 */
1302 public function serverDetailValidation()
1303 {
1304 try {
1305 check_ajax_referer('manual_migration_server_detail', 'securityKey');
1306
1307 /**
1308 * Pull the server details
1309 */
1310 $serverDetails = isset($_POST['serverDetails'])
1311 ? array_map('sanitize_text_field', wp_unslash($_POST['serverDetails']))
1312 : [];
1313
1314 /**
1315 * Updated the server details array
1316 */
1317 $updatedServerDetailArray = array_merge($serverDetails, [ 'ftpPath' => '.' ]);
1318
1319 /**
1320 * Set the server detail as a transient
1321 */
1322 set_transient('transferito_manual_server_detail', $updatedServerDetailArray);
1323
1324 /**
1325 * Make a request to the endpoint to get a list of the available domains
1326 */
1327 $serverDetailResult = $this->api->ftpValidation($updatedServerDetailArray);
1328
1329 /**
1330 * Fail when the FTP connection fails
1331 */
1332 if ($serverDetailResult['code'] !== 200) {
1333 throw new \Exception('API_REQUEST_FAILURE');
1334 }
1335
1336 /**
1337 * Fail when the FTP connection fails
1338 */
1339 if (!$serverDetailResult['message']) {
1340 throw new \Exception('FALSEY_RESPONSE');
1341 }
1342
1343 /**
1344 * Fail when the has connected property isn't present
1345 */
1346 if (!property_exists($serverDetailResult['message'], 'hasConnected')) {
1347 throw new \Exception('PROPERTY_MISSING');
1348 }
1349
1350 /**
1351 * Fail when the FTP connection fails
1352 */
1353 if (!$serverDetailResult['message']->hasConnected) {
1354 throw new \Exception('FTP_CONNECTION_FAILURE');
1355 }
1356
1357 /**
1358 * Push loadScreen event to telemetry
1359 */
1360 $this->telemetry->pushEvent('loadScreen', [
1361 'screenName' => 'directorySelector'
1362 ]);
1363
1364 /**
1365 * Set the server detail as a transient
1366 */
1367 $updatedServerDetailArray['ftpPort'] = (string) $serverDetailResult['message']->ftpPort;
1368 $updatedServerDetailArray['useSFTP'] = (string) $serverDetailResult['message']->useSFTP;
1369 set_transient('transferito_manual_server_detail', $updatedServerDetailArray);
1370
1371 /**
1372 * Return success object
1373 * To update the nav and change the child template
1374 */
1375 wp_send_json_success([
1376 'template' => loadTemplate('parts/migration/manual/directory-selection', []),
1377 'useSFTP' => $updatedServerDetailArray['useSFTP']
1378 ]);
1379
1380 } catch (\Exception $exception) {
1381 wp_send_json_error([
1382 'connected' => false,
1383 ], 400);
1384 }
1385 }
1386
1387 /**
1388 * @deprecated
1389 * @return void
1390 */
1391 public function directoryValidation()
1392 {
1393 try {
1394 check_ajax_referer('manual_migration_directory_selection', 'securityKey');
1395
1396 /**
1397 * Check that the ftpInfo element exists
1398 */
1399 $serverDetail = get_transient('transferito_manual_server_detail');
1400
1401 /**
1402 * Get the saved domain to use as the domain to check
1403 */
1404 $domain = get_transient('transferito_migration_domain');
1405
1406 /**
1407 * Sanitize the directory
1408 */
1409 $directory = isset($_POST['directory']) ? sanitize_text_field(wp_unslash($_POST['directory'])) : '';
1410
1411 /**
1412 * Create the updated server detail
1413 */
1414 $directoryCheckPayload = array_merge($serverDetail, [
1415 'path' => $directory,
1416 'URL' => $domain
1417 ]);
1418
1419 /**
1420 * update the ftpPath from the payload
1421 */
1422 $directoryCheckPayload['ftpPath'] = $directory;
1423
1424 /**
1425 * Save the updated server detail
1426 */
1427 set_transient('transferito_manual_server_detail', $directoryCheckPayload);
1428
1429 /**
1430 * Response from the directory check
1431 */
1432 $response = $this->api->directoryCheck($directoryCheckPayload);
1433
1434 /**
1435 *
1436 */
1437 // if ($response['code'] !== 200) {
1438 // throw new \Exception('FAILED_CORRECT_DIRECTORY_CHECK');
1439 // }
1440
1441 /**
1442 * Push loadScreen event to telemetry
1443 */
1444 // $this->telemetry->pushEvent('loadScreen', [
1445 // 'screenName' => 'databaseAuthentication'
1446 // ]);
1447
1448 /**
1449 * Return success object
1450 *
1451 * @todo Update the message -> check to see if the return message has the property correctDirectory
1452 */
1453 wp_send_json_success($response['message']);
1454
1455 } catch (\Exception $exception) {
1456 wp_send_json_error([
1457 'error' => $exception->getMessage(),
1458 'template' => loadTemplate('parts/migration/manual/directory-selection', []),
1459 ], 400);
1460 }
1461 }
1462
1463 public function databaseValidation()
1464 {
1465 try {
1466 check_ajax_referer('manual_migration_database_detail', 'securityKey');
1467
1468 /**
1469 * Merge the array
1470 */
1471 $databaseTestPayload = isset($_POST['databaseDetail'])
1472 ? array_map('sanitize_text_field', wp_unslash($_POST['databaseDetail']))
1473 : [];
1474
1475 /**
1476 * Get the saved domain to use as the domain to check
1477 */
1478 $domain = get_transient('transferito_migration_domain');
1479
1480 /**
1481 * Exclude database
1482 */
1483 $excludeDatabase = isset($databaseTestPayload['exclude_database_transfer']) && $databaseTestPayload['exclude_database_transfer'] === 'true';
1484
1485 /**
1486 * Use Existing Details
1487 */
1488 $useExistingDetails = isset($databaseTestPayload['use_existing_database']) && $databaseTestPayload['use_existing_database'] === 'true';
1489
1490 /**
1491 * Assign the domain flag to the new payload
1492 */
1493 $databaseTestPayload['domain'] = $domain;
1494 $databaseTestPayload['URL'] = $domain;
1495
1496 /**
1497 * Update the server detail transient
1498 */
1499 set_transient('transferito_manual_server_detail', $databaseTestPayload);
1500
1501 /**
1502 * Add check that db detail has been completed
1503 */
1504 set_transient('transferito_database_detail_completed', true);
1505
1506 /**
1507 * Only do the DB check if the database isn't excluded
1508 */
1509 if (!$excludeDatabase && !$useExistingDetails) {
1510 /**
1511 * Get connected Site
1512 */
1513 $connectedSite = $this->destinationServerConnectionExists('array');
1514
1515 /**
1516 * Merged Payload
1517 */
1518 $mergedPayload = array_merge($databaseTestPayload, [
1519 'validationToken' => $connectedSite['token'],
1520 'validationFile' => $connectedSite['filename'],
1521 'domain' => $connectedSite['domain']
1522 ]);
1523
1524 /**
1525 * Make a request to the database validation endpoint
1526 */
1527 $response = $this->api->databaseValidation($mergedPayload);
1528
1529 /**
1530 * Check to see whether the DB details have failed
1531 */
1532 if ($response['code'] !== 200) {
1533 throw new \Exception('FAILED_DATABASE_CHECK');
1534 }
1535 }
1536
1537 /**
1538 * Final additions to the payload
1539 */
1540 $migrationDetail = array_merge($databaseTestPayload, [
1541 'transferMethod' => 'ftp',
1542 'transferType' => 'manual'
1543 ]);
1544
1545 /**
1546 * Pass the full details and nonce to validate the call
1547 */
1548 wp_send_json_success([
1549 'migrationDetail' => array_map('stripslashes', $migrationDetail),
1550 'securityKey' => wp_create_nonce("prepare_migration_files")
1551 ]);
1552
1553 } catch (\Exception $exception) {
1554 /**
1555 * Get the server detail
1556 */
1557 $serverDetail = get_transient('transferito_manual_server_detail');
1558
1559 /**
1560 *
1561 */
1562 wp_send_json_error([
1563 'template' => loadTemplate('parts/migration/manual/database-detail', [
1564 'detail' => $serverDetail,
1565 'completed' => get_transient('transferito_database_detail_completed')
1566 ]),
1567 ], 400);
1568 }
1569 }
1570
1571 public function chooseMigrationMethod()
1572 {
1573 /**
1574 * verify nonce with every template change call
1575 */
1576 check_ajax_referer('migration_choice', 'actionKey');
1577
1578 $freeTierUser = (!$this->options['public_transferito_key'] || !$this->options['secret_transferito_key']);
1579 $mainMessage = stripslashes('We are creating a backup of your current WordPress installation');
1580 $secondaryMessage = stripslashes('Please wait.. This may take a few minutes, do not close this window or refresh the page');
1581
1582 $migrationType = isset($_POST['data']['migrationType'])
1583 ? sanitize_text_field(wp_unslash($_POST['data']['migrationType']))
1584 : '';
1585
1586 $htmlTemplate = ($migrationType === 'useCpanel')
1587 ? loadTemplate('parts/migration/cpanel-validation', [
1588 'mainMessage' => $mainMessage,
1589 'secondaryMessage' => $secondaryMessage
1590 ])
1591 : loadTemplate('parts/migration/transfer-detail-entry', [
1592 'mainMessage' => $mainMessage,
1593 'secondaryMessage' => $secondaryMessage,
1594 'freeMigration' => $freeTierUser,
1595 'directories' => Config::getWPContentPaths()
1596 ]);
1597
1598 $siteCheckResponse = [ 'htmlTemplate' => $htmlTemplate ];
1599
1600 wp_send_json_success($siteCheckResponse);
1601 }
1602
1603 public function statusCheck()
1604 {
1605
1606 /**
1607 * Verify nonce with every status check
1608 */
1609 check_ajax_referer('migration_status_check', 'securityKey');
1610
1611 $token = isset($_POST['token']) ? sanitize_text_field(wp_unslash($_POST['token'])) : '';
1612 $response = $this->api->getStatus($token);
1613 $responseMessage = $response['message'];
1614
1615 /**
1616 * Check the migration
1617 */
1618 if ($response["code"] === 200) {
1619 $startCleanUp = get_transient('transferito_cleanup_after_completion');
1620 $isCompleted = (isset($responseMessage->status))
1621 ? $responseMessage->status === 'completed' || $responseMessage->status === 'completed.with.errors'
1622 : false;
1623
1624 if ($responseMessage->status === 'completed' && $startCleanUp) {
1625 delete_transient('transferito_cleanup_after_completion');
1626 }
1627
1628 wp_send_json_success([
1629 'metadata' => $responseMessage->metaData,
1630 'statuses' => $responseMessage->all,
1631 'status' => $responseMessage->status,
1632 'completed' => $isCompleted
1633 ]);
1634
1635 } else {
1636 wp_send_json_error($responseMessage->error, $response['code']);
1637 }
1638 }
1639
1640 public function prepareDownload()
1641 {
1642 try {
1643 check_ajax_referer('prepare_migration_files', 'security');
1644
1645 $settings = get_option('transferito_settings_option');
1646
1647 /**
1648 * Site size info
1649 */
1650 $siteDetails = getDirectorySize(TRANSFERITO_ABSPATH);
1651 $siteSizeInfo = get_transient('transferito_installation_size');
1652 $siteSize = $siteSizeInfo ? $siteSizeInfo : [];
1653
1654 /**
1655 * Check the user's API keys
1656 */
1657 $hasValidAPIKeys = $this->areAPIKeysValid(
1658 $settings['public_transferito_key'],
1659 $settings['secret_transferito_key']
1660 );
1661
1662 /**
1663 * Get the Transfer Method
1664 */
1665 $transferMethod = isset($_POST['migrationDetails']['transferMethod'])
1666 ? sanitize_text_field(wp_unslash($_POST['migrationDetails']['transferMethod']))
1667 : '';
1668
1669 /**
1670 * Fire the required upgrade flow
1671 */
1672 if (!$hasValidAPIKeys && $siteDetails['maxSizeExceeded']) {
1673 wp_send_json_success(array_merge([
1674 'message' => '<strong>PLEASE DO NOT</strong> navigate away or reload this page while your migration is in process. Doing so will stop your migration.',
1675 'force' => true,
1676 'useZipFallback' => false,
1677 'created' => true,
1678 'excludeDatabase' => false,
1679 'upgradeRequired' => true,
1680 'htmlTemplate' => loadTemplate('parts/migration/progress/main',
1681 array_merge(
1682 [
1683 'backupPrepare' => true,
1684 'backupInstallation' => true,
1685 'uploadBackup' => false,
1686 'downloadBackup' => true,
1687 'extractBackup' => true,
1688 'installDatabase' => true,
1689 'finalizeInstallation' => true,
1690 'completed' => true
1691 ],
1692 [
1693 'method' => ''
1694 ]
1695 )
1696 )
1697 ], $siteSize));
1698
1699 die();
1700 }
1701
1702 /**
1703 * Set as a FREE User
1704 */
1705 if (!$hasValidAPIKeys) {
1706 $this->api->setFreeUser();
1707 }
1708
1709 /**
1710 * If a local migration forward to the migration method
1711 */
1712 if ($transferMethod === 'localSiteMigration') {
1713 $this->prepareLocalDownload($hasValidAPIKeys);
1714 die();
1715 }
1716
1717 /**
1718 * @DoNotRemove
1719 * Important as it creates the access file to check whether the migration will be an upload
1720 */
1721 Config::getCorrectPath();
1722
1723 $forceUpload = isset($settings['transferito_force_upload']) ? $settings['transferito_force_upload'] : false;
1724 $migrationDetails = isset($_POST['migrationDetails'])
1725 ? array_map('sanitize_text_field', wp_unslash($_POST['migrationDetails']))
1726 : [];
1727 $excludeDatabase = isset($migrationDetails['exclude_database_transfer'])
1728 ? $migrationDetails['exclude_database_transfer']
1729 : null;
1730 $folderPaths = isset($migrationDetails['folder_path'])
1731 ? array_map('sanitize_text_field', wp_unslash($migrationDetails['folder_path']))
1732 : null;
1733 $destinationURL = isset($migrationDetails['domain'])
1734 ? sanitize_text_field($migrationDetails['domain'])
1735 : null;
1736 $selectedFolderEnabled = ($folderPaths !== null);
1737 $backupDirectory = bin2hex(openssl_random_pseudo_bytes(8));
1738
1739 /**
1740 * Get the current domain & add it to the migrationDetails
1741 */
1742 $migrationDetails['currentDomain'] = site_url();
1743
1744 /**
1745 * Add the check to see if the options are selected
1746 */
1747 $disableWPObjectCache = isset($settings['transferito_disable_wordpress_cache'])
1748 ? $settings['transferito_disable_wordpress_cache']
1749 : false;
1750 $ignoreMalcareWAF = isset($settings['transferito_malcare_waf_plugin_fix'])
1751 ? $settings['transferito_malcare_waf_plugin_fix']
1752 : false;
1753
1754 /**
1755 * Get connected Site
1756 */
1757 $connectedSite = $this->destinationServerConnectionExists('array');
1758
1759 /**
1760 * Find the functions file - To Disable the WP Object Cache
1761 */
1762 if ($disableWPObjectCache) {
1763 $this->disableWPObjectCache();
1764 }
1765
1766 /**
1767 * Find the user.ini file - To Disable MalCare WAF
1768 */
1769 if ($ignoreMalcareWAF) {
1770 $this->disableAutoPrependOption();
1771 }
1772
1773
1774 /**
1775 * Set the destination URL transient
1776 */
1777 set_transient('transferito_migration_domain', $destinationURL);
1778
1779 /**
1780 * Check if we can download directly from the server, unless the force upload flag is checked
1781 */
1782 if ($forceUpload) {
1783 $siteAccessed = false;
1784 } else {
1785 $sampleArchiveFilename = bin2hex(random_bytes(16)) . '.zip';
1786
1787 /**
1788 * Create zip file
1789 * @todo create zip file
1790 */
1791
1792 try {
1793 $zip = new \ZipArchive;
1794
1795 if ($zip->open(TRANSFERITO_ABSPATH . $sampleArchiveFilename, \ZipArchive::CREATE) === TRUE) {
1796 $zip->addFromString('test_file.txt', 'Test zip file - to test migration');
1797 $zip->close();
1798 }
1799
1800 /**
1801 * Request to check the direct download
1802 */
1803 $directDownload = $this->api->directDownloadCheck([
1804 'validationToken' => $connectedSite['token'],
1805 'validationFile' => $connectedSite['filename'],
1806 "domain" => $destinationURL,
1807 'currentURL' => site_url(),
1808 'filename' => $sampleArchiveFilename,
1809 ]);
1810
1811 /**
1812 * Assign the result to
1813 */
1814 $siteAccessed = $directDownload['message']->canDownload;
1815
1816 /**
1817 * Remove the sample file
1818 */
1819 wp_delete_file(TRANSFERITO_ABSPATH . $sampleArchiveFilename);
1820
1821 } catch(\Exception $exception) {
1822 $siteAccessed = false;
1823 }
1824 }
1825
1826 /**
1827 * Add the local flag to the ftp details
1828 */
1829 $migrationDetails['isLocal'] = $siteAccessed;
1830
1831 /**
1832 * If the folder path is set and the transfer type is manual
1833 * Then validate the
1834 */
1835 $additionalData = [];
1836 if (isset($migrationDetails['folder_path']) && $migrationDetails['transferType'] === 'manual') {
1837 $additionalData['folder_path'] = array_map('sanitize_text_field', wp_unslash($migrationDetails['folder_path']));
1838 }
1839
1840 /**
1841 * Clean text fields before create migration request
1842 */
1843 $migrationPayload = array_merge($migrationDetails, $additionalData);
1844 $migrationPayload['isLocal'] = $siteAccessed;
1845 $migrationPayload['currentPath'] = ABSPATH;
1846
1847 /**
1848 * Create a migration and return a token
1849 */
1850 $createdMigration = $this->api->createMigration($migrationPayload);
1851
1852 /**
1853 * Fail gracefully if there is an issue creating the migration
1854 */
1855 if ($createdMigration['code'] !== 200) {
1856 $message = (property_exists($createdMigration['message'], 'result'))
1857 ? $createdMigration['message']->result
1858 : 'We are unable to create your migration. If this issue persists, please contact support.';
1859 throw new \Exception(stripslashes($message));
1860 }
1861
1862 /**
1863 * Set transients to use with the failure endpoint
1864 */
1865 set_transient('transferito_migration_token', $createdMigration['message']->token);
1866 set_transient('transferito_migration_timestamp', $createdMigration['message']->timestamp);
1867
1868 /**
1869 * Get the migration token
1870 */
1871 $migrationToken = $createdMigration['message']->token;
1872
1873 /**
1874 * Final Domain
1875 */
1876 $finalDomain = $createdMigration['message']->domain;
1877
1878 $serverCheck = null;
1879
1880 /**
1881 * If we can contact the site
1882 * Then check the server requirements
1883 */
1884 if ($siteAccessed) {
1885 /**
1886 * Create the test file & token
1887 */
1888 $testFile = Config::createTestFile();
1889
1890 /**
1891 * Check the destination server info
1892 */
1893 $serverRequirements = $this->api->checkDestinationServerRequirements([
1894 'validationToken' => $connectedSite['token'],
1895 'validationFile' => $connectedSite['filename'],
1896 'domain' => $destinationURL,
1897 'token' => $migrationToken,
1898 'timestamp' => $createdMigration['message']->timestamp,
1899 'fileURL' => $testFile['url'],
1900 'fileHash' => $testFile['hash']
1901 ]);
1902
1903 /**
1904 * Fallback
1905 * If the server req check fails
1906 */
1907 if ($serverRequirements['code'] !== 200) {
1908 $uploadToS3 = true;
1909 } else {
1910 $serverCheck = $serverRequirements;
1911 /**
1912 * Check the site access again
1913 * If it is a local environment - or if the module allowed is wget - Then default to S3
1914 */
1915 $uploadToS3 = !$serverRequirements['message']->pullDirect;
1916 }
1917 }
1918
1919 /**
1920 * If we can not reach the site
1921 * Upload directly to S3 bucket
1922 */
1923 if (!$siteAccessed) {
1924 $uploadToS3 = true;
1925 }
1926
1927 /**
1928 * Set transient for backup status
1929 */
1930 set_transient('transferito_backup_status', [
1931 'databaseBackupComplete' => $excludeDatabase ? true : false,
1932 'databaseExportComplete' => $excludeDatabase ? true : false,
1933 'codebaseBackupComplete' => false,
1934 'excludedDatabase' => $excludeDatabase
1935 ]);
1936
1937 /**
1938 * Set transient with transfer detail
1939 */
1940 set_transient('transferito_transfer_detail', [
1941 'isLocalEnv' => $uploadToS3,
1942 'selectedFolders' => $selectedFolderEnabled,
1943 'folders' => $folderPaths,
1944 'directory' => $backupDirectory,
1945 'token' => $migrationToken,
1946 'timestamp' => $createdMigration['message']->timestamp,
1947 'fromUrl' => site_url(),
1948 'newUrl' => $finalDomain
1949 ]);
1950
1951 /**
1952 * Set the transient for the destination site URL
1953 */
1954 set_transient('transferito_final_destination_url', $finalDomain);
1955
1956 /**
1957 * Available steps to pass to the template
1958 */
1959 $progressSteps = [
1960 'backupPrepare' => true,
1961 'backupInstallation' => true,
1962 'uploadBackup' => $uploadToS3,
1963 'downloadBackup' => true,
1964 'extractBackup' => true,
1965 'installDatabase' => !$excludeDatabase,
1966 'finalizeInstallation' => true,
1967 'completed' => true
1968 ];
1969
1970 /**
1971 * Get the requirement transient
1972 */
1973 $transferitoRequirements = get_transient('transferito_requirements');
1974
1975 /**
1976 * Push Migration detail event to telemetry
1977 */
1978 $this->telemetry->pushEvent('migrationDetails', [
1979 'uploadBackup' => $uploadToS3,
1980 'localMigration' => $uploadToS3,
1981 'databaseExcluded' => $excludeDatabase,
1982 'selectedFolders' => $selectedFolderEnabled,
1983 'migrationMethod' => $migrationDetails['transferMethod'],
1984 'siteSize' => isset($siteSize['totalSize']) ? $siteSize['totalSize'] : 0,
1985 'cPanelAPIToken' => isset($migrationDetails['cpanelApiToken']) ? $migrationDetails['cpanelApiToken'] : false
1986 ]);
1987
1988 /**
1989 * Push loadScreen event to telemetry
1990 */
1991 $this->telemetry->pushEvent('loadScreen', [
1992 'screenName' => 'migrationInProgress'
1993 ]);
1994
1995 wp_send_json_success(array_merge([
1996 'debug' => [
1997 'siteAccess' => $siteAccessed,
1998 'uploadS3' => $uploadToS3
1999 ],
2000 'message' => '<strong>PLEASE DO NOT</strong> navigate away or reload this page while your migration is in process. Doing so will stop your migration.',
2001 'force' => $forceUpload,
2002 'useZipFallback' => $transferitoRequirements['useZip'],
2003 'created' => true,
2004 'excludeDatabase' => $excludeDatabase,
2005 'upgradeRequired' => false,
2006 'htmlTemplate' => loadTemplate('parts/migration/progress/main',
2007 array_merge($progressSteps, [
2008 'method' => $migrationDetails['transferMethod'],
2009 'migrationToken' => $migrationToken
2010 ] )
2011 )
2012 ], $siteSize));
2013
2014 } catch (\Exception $exception) {
2015 /**
2016 * Push loadScreen event to telemetry
2017 */
2018 $this->telemetry->pushEvent('failedMigration', [
2019 'migrationStatus' => 'prepareDownload',
2020 'errorMessage' => $exception->getMessage()
2021 ]);
2022 $errorMessage = 'prepareDownload: ' . $exception->getMessage();
2023 $this->api->failedMigration($errorMessage);
2024 delete_transient('transferito_transfer_detail');
2025 wp_send_json_error([
2026 'message' => $exception->getMessage(),
2027 'error' => $exception->getTraceAsString(),
2028 'line' => $exception->getLine()
2029 ], 400);
2030 }
2031 }
2032
2033 private function prepareLocalDownload($premiumUser)
2034 {
2035 try {
2036
2037 /**
2038 * Fire the required upgrade flow
2039 */
2040 if (!$premiumUser) {
2041 wp_send_json_success([
2042 'message' => '<strong>PLEASE DO NOT</strong> navigate away or reload this page while your migration is in process. Doing so will stop your migration.',
2043 'force' => true,
2044 'useZipFallback' => false,
2045 'created' => true,
2046 'excludeDatabase' => false,
2047 'upgradeRequired' => true,
2048 'htmlTemplate' => loadTemplate('parts/migration/progress/main',
2049 array_merge(
2050 [
2051 'backupPrepare' => true,
2052 'backupInstallation' => true,
2053 'uploadBackup' => false,
2054 'downloadBackup' => true,
2055 'extractBackup' => true,
2056 'installDatabase' => true,
2057 'finalizeInstallation' => true,
2058 'completed' => true
2059 ],
2060 [
2061 'method' => ''
2062 ]
2063 )
2064 )
2065 ]);
2066
2067 die();
2068 }
2069
2070
2071 /**
2072 * @DoNotRemove
2073 * Important as it creates the access file to check whether the migration will be an upload
2074 */
2075 Config::getCorrectPath();
2076
2077 $transferMethod = 'localSiteMigration';
2078 $settings = get_option('transferito_settings_option');
2079 $destinationURL = get_transient('transferito_migration_domain');
2080 $backupDirectory = bin2hex(openssl_random_pseudo_bytes(8));
2081
2082 /**
2083 * Add the check to see if the options are selected
2084 */
2085 $disableWPObjectCache = isset($settings['transferito_disable_wordpress_cache'])
2086 ? $settings['transferito_disable_wordpress_cache']
2087 : false;
2088 $ignoreMalcareWAF = isset($settings['transferito_malcare_waf_plugin_fix'])
2089 ? $settings['transferito_malcare_waf_plugin_fix']
2090 : false;
2091
2092 /**
2093 * Find the functions file - To Disable the WP Object Cache
2094 */
2095 if ($disableWPObjectCache) {
2096 $this->disableWPObjectCache();
2097 }
2098
2099 /**
2100 * Find the user.ini file - To Disable MalCare WAF
2101 */
2102 if ($ignoreMalcareWAF) {
2103 $this->disableAutoPrependOption();
2104 }
2105
2106 /**
2107 * Set the destination URL transient
2108 */
2109 set_transient('transferito_migration_domain', $destinationURL);
2110
2111 /**
2112 * Create a migration and return a token
2113 */
2114 $createdMigration = $this->api->createMigration([
2115 'currentDomain' => site_url(),
2116 'transferMethod' => $transferMethod,
2117 'domain' => $destinationURL,
2118 'isLocal' => true,
2119 'currentPath' => ABSPATH
2120 ]);
2121
2122 /**
2123 * Fail gracefully if there is an issue creating the migration
2124 */
2125 if ($createdMigration['code'] !== 200) {
2126 $message = (property_exists($createdMigration['message'], 'result'))
2127 ? $createdMigration['message']->result
2128 : 'We are unable to create your migration. If this issue persists, please contact support.';
2129 throw new \Exception(stripslashes($message));
2130 }
2131
2132 /**
2133 * Set transients to use with the failure endpoint
2134 */
2135 set_transient('transferito_migration_token', $createdMigration['message']->token);
2136 set_transient('transferito_migration_timestamp', $createdMigration['message']->timestamp);
2137
2138 /**
2139 * Get the migration token
2140 */
2141 $migrationToken = $createdMigration['message']->token;
2142
2143 /**
2144 * Final Domain
2145 */
2146 $finalDomain = $createdMigration['message']->domain;
2147
2148 /**
2149 * Set transient for backup status
2150 */
2151 set_transient('transferito_backup_status', [
2152 'databaseBackupComplete' => false,
2153 'databaseExportComplete' => false,
2154 'codebaseBackupComplete' => false,
2155 'excludedDatabase' => false
2156 ]);
2157
2158 /**
2159 * Set transient with transfer detail
2160 */
2161 set_transient('transferito_transfer_detail', [
2162 'isLocalEnv' => true, // Defaulted to true as will always upload to S3 in this instance
2163 'selectedFolders' => false,
2164 'folders' => null, // No folders have been selected set directly to null
2165 'directory' => $backupDirectory,
2166 'token' => $migrationToken,
2167 'timestamp' => $createdMigration['message']->timestamp,
2168 'fromUrl' => site_url(),
2169 'newUrl' => $finalDomain
2170 ]);
2171
2172 /**
2173 * Set the transient for the destination site URL
2174 */
2175 set_transient('transferito_final_destination_url', $finalDomain);
2176
2177 /**
2178 * Available steps to pass to the template
2179 */
2180 $progressSteps = [
2181 'backupPrepare' => true,
2182 'backupInstallation' => true,
2183 'uploadBackup' => true,
2184 'downloadBackup' => true,
2185 'extractBackup' => true,
2186 'installDatabase' => true,
2187 'finalizeInstallation' => true,
2188 'completed' => true
2189 ];
2190
2191 /**
2192 * Get the requirement transient
2193 */
2194 $transferitoRequirements = get_transient('transferito_requirements');
2195
2196 /**
2197 * Site size info
2198 */
2199 $siteSizeInfo = get_transient('transferito_installation_size');
2200 $siteSize = $siteSizeInfo ? $siteSizeInfo : [];
2201
2202 /**
2203 * Push Migration detail event to telemetry
2204 */
2205 $this->telemetry->pushEvent('migrationDetails', [
2206 'uploadBackup' => true,
2207 'localMigration' => true,
2208 'databaseExcluded' => false,
2209 'selectedFolders' => false,
2210 'migrationMethod' => $transferMethod,
2211 'siteSize' => isset($siteSize['totalSize']) ? $siteSize['totalSize'] : 0,
2212 'cPanelAPIToken' => false
2213 ]);
2214
2215 /**
2216 * Push loadScreen event to telemetry
2217 */
2218 $this->telemetry->pushEvent('loadScreen', [
2219 'screenName' => 'migrationInProgress'
2220 ]);
2221
2222 wp_send_json_success(array_merge([
2223 'message' => '<strong>PLEASE DO NOT</strong> navigate away or reload this page while your migration is in process. Doing so will stop your migration.',
2224 'force' => true,
2225 'useZipFallback' => $transferitoRequirements['useZip'],
2226 'created' => true,
2227 'excludeDatabase' => false,
2228 'htmlTemplate' => loadTemplate('parts/migration/progress/main',
2229 array_merge(
2230 $progressSteps,
2231 [
2232 'method' => 'localSiteMigration'
2233 ]
2234 )
2235 )
2236 ], $siteSize));
2237
2238
2239 } catch (\Exception $exception) {
2240
2241 /**
2242 * @todo Implement a failure screen - If the migration creation fails
2243 */
2244
2245
2246 /**
2247 * Push loadScreen event to telemetry
2248 */
2249 $this->telemetry->pushEvent('failedMigration', [
2250 'migrationStatus' => 'prepareDownload',
2251 'errorMessage' => $exception->getMessage()
2252 ]);
2253 $errorMessage = 'prepareLocalDownload: ' . $exception->getMessage();
2254 $this->api->failedMigration($errorMessage);
2255 delete_transient('transferito_transfer_detail');
2256 wp_send_json_error($exception->getMessage(), 400);
2257 }
2258
2259 }
2260
2261 public function prepareCodebase()
2262 {
2263 try {
2264 check_ajax_referer('prepare_migration_files', 'security');
2265
2266 $transferDetail = get_transient('transferito_transfer_detail');
2267 $fileListCreated = $this->codeBase->createFileList($transferDetail['selectedFolders'], $transferDetail['folders']);
2268 $zipPaths = $this->codeBase->createArchivePath();
2269
2270 /**
2271 * Throw the error if the zip paths are not an array
2272 */
2273 if (is_string($zipPaths)) {
2274 throw new \Exception($zipPaths);
2275 }
2276
2277 /**
2278 * Set transient for zip creation paths
2279 */
2280 set_transient('transferito_codebase_archive', $zipPaths);
2281
2282 /**
2283 * Get the site size
2284 */
2285 $siteSizeInfo = get_transient('transferito_installation_size');
2286
2287 wp_send_json_success(array_merge($fileListCreated, $siteSizeInfo));
2288
2289 } catch(\Exception $exception) {
2290 /**
2291 * Push loadScreen event to telemetry
2292 */
2293 $this->telemetry->pushEvent('failedMigration', [
2294 'migrationStatus' => 'prepareCodebase',
2295 'errorMessage' => $exception->getMessage()
2296 ]);
2297 $errorMessage = 'prepareCodebase: ' . $exception->getMessage();
2298 $this->api->failedMigration($errorMessage);
2299 wp_send_json_error($exception->getMessage(), 400);
2300 }
2301 }
2302
2303 public function addFilesToCodebase()
2304 {
2305 try {
2306 check_ajax_referer('prepare_migration_files', 'security');
2307
2308 $currentIndex = isset($_POST['currentFileIndex']) ? sanitize_text_field(wp_unslash($_POST['currentFileIndex'])) : 0;
2309
2310 /**
2311 * Check to see if the client passed the add db flag flag is passed in to
2312 */
2313 $dbExportFileListPath = isset($_POST['addDatabaseExports']) ? DIRECTORY_SEPARATOR . 'db_import' : '';
2314 $zipDetail = get_transient('transferito_codebase_archive');
2315 $jsonFileName = TRANSFERITO_UPLOAD_PATH . $dbExportFileListPath . DIRECTORY_SEPARATOR . 'json' . DIRECTORY_SEPARATOR . 'file_list_' . $currentIndex . '.json';
2316 $jsonFile = file_get_contents($jsonFileName);
2317
2318 if (!file_exists($jsonFileName)) {
2319 throw new \Exception('Something has gone wrong. We can not find the requested back up file list.');
2320 }
2321
2322 if (!$jsonFile) {
2323 throw new \Exception('Something has gone wrong. We can not read requested back up file list.');
2324 }
2325
2326 $files = json_decode($jsonFile);
2327 $addedToArchive = $this->codeBase->addFileToArchive($zipDetail['path'], $files);
2328
2329 /**
2330 * If the response isn't truthy throw
2331 */
2332 if ($addedToArchive !== true) {
2333 throw new \Exception($addedToArchive);
2334 }
2335
2336 wp_send_json_success($zipDetail);
2337 } catch(\Exception $exception) {
2338 wp_send_json_error($exception->getMessage(), 400);
2339 }
2340 }
2341
2342 public function codebaseArchiveComplete()
2343 {
2344 try {
2345 check_ajax_referer('prepare_migration_files', 'security');
2346
2347 /**
2348 * Get the related transients
2349 */
2350 $backupStatus = get_transient('transferito_backup_status');
2351 $transferDetail = get_transient('transferito_transfer_detail');
2352 $zipDetail = get_transient('transferito_codebase_archive');
2353
2354 /**
2355 * Update the backup array
2356 * Notification of the codebase archive completed
2357 */
2358 $backupStatus['codebaseBackupComplete'] = true;
2359
2360 /**
2361 * Update the code path with the archive path or url
2362 */
2363 $uploadS3 = $transferDetail['isLocalEnv'];
2364 $transferDetail['archive'] = $uploadS3 ? $zipDetail['path'] : $zipDetail['url'];
2365
2366 /**
2367 * Update the transients with the updated values
2368 */
2369 set_transient('transferito_backup_status', $backupStatus);
2370 set_transient('transferito_transfer_detail', $transferDetail);
2371
2372 wp_send_json_success([
2373 'codebaseArchived' => true,
2374 'excludeDatabase' => $backupStatus['excludedDatabase']
2375 ]);
2376
2377 } catch (\Exception $exception) {
2378 wp_send_json_error($exception->getMessage(), 400);
2379 }
2380 }
2381
2382 public function prepareDatabase()
2383 {
2384 try {
2385 check_ajax_referer('prepare_migration_files', 'security');
2386
2387 /**
2388 * Prepare a table map that allows the chunked db database export
2389 */
2390 $tableMap = $this->dataBase->prepareTableMap();
2391
2392 /**
2393 * If the table map can not be created
2394 * Throw the error
2395 */
2396 if (!$tableMap) {
2397 throw new \Exception('We were in the process of preparing to back up your database but this has failed.');
2398 }
2399
2400 /**
2401 * Set the table map as a transient
2402 */
2403 set_transient('transferito_database_table_map', $tableMap);
2404
2405 /**
2406 * Return an array
2407 * Notifying the client of the table creation
2408 */
2409 wp_send_json_success([ 'tableMapCreated' => true ]);
2410
2411 } catch(\Exception $exception) {
2412 /**
2413 * Push loadScreen event to telemetry
2414 */
2415 $this->telemetry->pushEvent('failedMigration', [
2416 'migrationStatus' => 'prepareDatabase',
2417 'errorMessage' => $exception->getMessage()
2418 ]);
2419 $errorMessage = 'prepareDatabase: ' . $exception->getMessage();
2420 $this->api->failedMigration($errorMessage);
2421 wp_send_json_error($exception->getMessage(), 400);
2422 }
2423 }
2424
2425 public function chunkedDBExport()
2426 {
2427 try {
2428 check_ajax_referer('prepare_migration_files', 'security');
2429
2430 $firstRunSanitized = isset($_POST['firstRun'])
2431 ? sanitize_text_field(wp_unslash($_POST['firstRun']))
2432 : false;
2433
2434 /**
2435 * Validate the first run flag and convert it to a boolean
2436 */
2437 $firstRun = filter_var($firstRunSanitized, FILTER_VALIDATE_BOOLEAN);
2438
2439 /**
2440 * Pull the export progress data
2441 */
2442 $getDBProgress = get_transient('transferito_db_export_progress');
2443 $exportProgress = $getDBProgress ? $getDBProgress : [];
2444
2445 /**
2446 * On the first run - No arguments needed
2447 */
2448 if ($firstRun) {
2449 $exportResult = $this->dataBase->chunkedDBExport();
2450 }
2451
2452 /**
2453 * If it isn't the first run
2454 * Pass in the export progress
2455 */
2456 if (!$firstRun) {
2457 $exportResult = $this->dataBase->chunkedDBExport(
2458 $exportProgress['fileIndex'],
2459 $exportProgress['currentRowIndex'],
2460 $exportProgress['tableIndex']
2461 );
2462 }
2463
2464 /**
2465 * If the export fails - throw
2466 */
2467 if (!$exportResult) {
2468 throw new \Exception('We are unable to backup a part of your database.');
2469 }
2470
2471 /**
2472 * Return an array
2473 * Notifying the client of the status of the export
2474 */
2475 $mergedArray = array_merge($exportResult, $exportProgress, [ 'firstRun' => $firstRun ]);
2476 wp_send_json_success($mergedArray);
2477
2478 } catch(\Exception $exception) {
2479 wp_send_json_error($exception->getMessage(), 400);
2480 }
2481 }
2482
2483 public function databaseExportComplete()
2484 {
2485 try {
2486 check_ajax_referer('prepare_migration_files', 'security');
2487
2488 /**
2489 * Get the related transients
2490 */
2491 $backupStatus = get_transient('transferito_backup_status');
2492
2493 /**
2494 * Update the backup array
2495 * Notification of the database export completion
2496 */
2497 $backupStatus['databaseExportComplete'] = true;
2498
2499 /**
2500 * Get the requirement transient
2501 */
2502 $transferitoRequirements = get_transient('transferito_requirements');
2503
2504 /**
2505 * Update the transients with the updated values
2506 */
2507 set_transient('transferito_backup_status', $backupStatus);
2508
2509 wp_send_json_success([
2510 'databaseExported' => true ,
2511 'useZipFallback' => $transferitoRequirements['useZip'],
2512 ]);
2513
2514 } catch (\Exception $exception) {
2515 wp_send_json_error($exception->getMessage(), 400);
2516 }
2517 }
2518
2519 public function databaseRelocation()
2520 {
2521 try {
2522 check_ajax_referer('prepare_migration_files', 'security');
2523
2524 $this->dataBase->moveDatabaseFiles();
2525
2526 wp_send_json_success([ 'moved' => true ]);
2527
2528 } catch (\Exception $exception) {
2529 wp_send_json_error(stripslashes('We could not move your database files'), 400);
2530 }
2531 }
2532
2533 public function databaseRelocationCheck()
2534 {
2535 try {
2536 check_ajax_referer('prepare_migration_files', 'security');
2537
2538 /**
2539 * Pull the pid
2540 */
2541 $databaseMovePID = get_transient('transferito_database_relocation_pid');
2542
2543 /**
2544 * Get the site size
2545 */
2546 $siteSizeInfo = get_transient('transferito_installation_size');
2547
2548 wp_send_json_success([
2549 'completed' => checkJobHasCompleted($databaseMovePID),
2550 'siteInfo' => $siteSizeInfo
2551 ]);
2552
2553 } catch (\Exception $exception) {
2554 wp_send_json_error(stripslashes('We can not move your database files'), 400);
2555 }
2556 }
2557
2558 public function archiveCreation()
2559 {
2560 try {
2561 check_ajax_referer('prepare_migration_files', 'security');
2562
2563 /**
2564 * Pull the transfer detail to get the selected folder information
2565 */
2566 $transferDetail = get_transient('transferito_transfer_detail');
2567
2568 /**
2569 * Start the archive
2570 */
2571 $zipPaths = $this->codeBase->createExecArchive($transferDetail['selectedFolders'], $transferDetail['folders']);
2572
2573 /**
2574 * Set transient for zip creation paths
2575 */
2576 set_transient('transferito_codebase_archive', $zipPaths);
2577
2578 wp_send_json_success([ 'archiveCreationStarted' => true ]);
2579
2580 } catch (\Exception $exception) {
2581 /**
2582 * Push loadScreen event to telemetry
2583 */
2584 $this->telemetry->pushEvent('failedMigration', [
2585 'migrationStatus' => 'archiveCreation',
2586 'errorMessage' => $exception->getMessage()
2587 ]);
2588 $errorMessage = 'archiveCreation: ' . $exception->getMessage();
2589 $this->api->failedMigration($errorMessage);
2590 wp_send_json_error(stripslashes('We can not create a backup of your site'), 400);
2591 }
2592 }
2593
2594 public function archiveProgressCheck()
2595 {
2596 try {
2597
2598 check_ajax_referer('prepare_migration_files', 'security');
2599
2600 /**
2601 * Pull the PID for the progress file
2602 */
2603 $archiveCreationPID = get_transient('transferito_codebase_archive_pid');
2604
2605 /**
2606 * Get the file name
2607 */
2608 $progressFile = TRANSFERITO_UPLOAD_PATH . '/.archive-process';
2609
2610 /**
2611 * Get the archives
2612 */
2613 $archives = get_transient('transferito_codebase_archive');
2614
2615 /**
2616 * Get info for the archive path
2617 */
2618 $archiveInfo = pathinfo($archives['path']);
2619
2620 /**
2621 * Progress value
2622 */
2623 $progressValue = null;
2624
2625 /**
2626 * Set the archive result
2627 */
2628 $archiveResult = [];
2629
2630 /**
2631 * If the file is TAR
2632 * Read the amount of files
2633 */
2634 if ($archiveInfo['extension'] === 'tar') {
2635 /**
2636 * Pull the information about the installation
2637 */
2638 $installationInfo = get_transient('transferito_installation_size');
2639
2640 /**
2641 * Get the amount of lines in the progress file
2642 */
2643 $file = new \SplFileObject($progressFile, 'r');
2644 $file->seek(PHP_INT_MAX);
2645 $progressAmount = $file->key() + 1;
2646
2647 /**
2648 * Calculate the percentage of the archive creation
2649 */
2650 $progressValue = ($progressAmount / $installationInfo['amountOfFiles']) * 100;
2651 }
2652
2653 /**
2654 * If the archive is ZIP
2655 * Calculate the progress of the archive creation based on the zip verbose file structure
2656 */
2657 if ($archiveInfo['extension'] === 'zip') {
2658 /**
2659 * Get the last line of the progress file
2660 */
2661 $lastLine = transferitoGetLastLine($progressFile);
2662
2663 /**
2664 * Get everything within the brackets
2665 */
2666 preg_match("/\[(.*?)\]/", $lastLine, $match);
2667
2668 /**
2669 * If there is a match
2670 * Process the logic to calculate the progress
2671 */
2672 if (count($match) === 2) {
2673 /**
2674 * Remove all spaces from string
2675 */
2676 $cleanValue = str_replace(' ', '', $match[1]);
2677
2678 /**
2679 * Split the progress values
2680 */
2681 $values = explode('/', $cleanValue);
2682
2683 /**
2684 * Check that the 2 elements exist in the array
2685 */
2686 if (count($values) === 2) {
2687 /**
2688 * The amount completed via the zip
2689 */
2690 $completedAmount = preg_split('/(?<=[0-9])(?=[a-z]+)/i', $values[0]);
2691
2692 /**
2693 * The amount remaining
2694 */
2695 $remainingAmount = preg_split('/(?<=[0-9])(?=[a-z]+)/i', $values[1]);
2696
2697 /**
2698 * If the amount id 0 - dont count it yet
2699 */
2700 if ($completedAmount !== '0') {
2701
2702 $completedInBytes = transferitoConvertToBytes($completedAmount);
2703 $remainingInBytes = transferitoConvertToBytes($remainingAmount);
2704 $total = $completedInBytes + $remainingInBytes;
2705 $progressValue = ($total === 0) ? 0 : round(($completedInBytes / $total) * 100);
2706 }
2707 }
2708 }
2709 }
2710
2711 /**
2712 * Check if the process has completed
2713 */
2714 $completed = checkJobHasCompleted($archiveCreationPID);
2715
2716 /**
2717 * Remove the DB import directory
2718 */
2719 if ($completed) {
2720
2721 $transferDetail = get_transient('transferito_transfer_detail');
2722
2723 /**
2724 * Update the code path with the archive path or url
2725 */
2726 $uploadS3 = $transferDetail['isLocalEnv'];
2727 $transferDetail['archive'] = $uploadS3 ? $archives['path'] : $archives['url'];
2728
2729 /**
2730 * Save the archive
2731 */
2732 set_transient('transferito_transfer_detail', $transferDetail);
2733
2734 /**
2735 * Remove the import directory
2736 */
2737 $this->removeDBImportDirectory();
2738
2739 /**
2740 * Get the backup status
2741 */
2742 $backupStatus = get_transient('transferito_backup_status');
2743
2744 /**
2745 * Assign the flags to variables
2746 */
2747 $databaseExcluded = $backupStatus['excludedDatabase'];
2748
2749 /**
2750 * Result of the response when the DB is excluded
2751 */
2752 if ($databaseExcluded) {
2753 $archiveResult = $this->archiveCompletionResponse(true, false);
2754 }
2755
2756 /**
2757 * Result of the response when the DB is not excluded
2758 */
2759 if (!$databaseExcluded) {
2760 $archiveResult = $this->archiveCompletionResponse(true, true);
2761 }
2762 }
2763
2764 wp_send_json_success(array_merge([
2765 'completed' => $completed,
2766 'progress' => $progressValue
2767 ], $archiveResult));
2768
2769 } catch (\Exception $exception) {
2770 wp_send_json_error(stripslashes('We can not get the progress of your backup'), 400);
2771 }
2772 }
2773
2774 public function archiveDBExport()
2775 {
2776 try {
2777 check_ajax_referer('prepare_migration_files', 'security');
2778
2779 /**
2780 * Create the DB file list
2781 */
2782 $exportFileList = $this->dataBase->createFileList();
2783
2784 /**
2785 * Throw if any issues
2786 */
2787 if (!$exportFileList) {
2788 throw new \Exception('We are unable to create the list of database exports that wll be added to your backup.');
2789 }
2790
2791 wp_send_json_success($exportFileList);
2792
2793 } catch (\Exception $exception) {
2794 wp_send_json_error($exception->getMessage(), 400);
2795 }
2796 }
2797
2798 public function checkArchiveCompletion()
2799 {
2800 try {
2801 check_ajax_referer('prepare_migration_files', 'security');
2802
2803 /**
2804 * Get the backup status
2805 */
2806 $backupStatus = get_transient('transferito_backup_status');
2807
2808 /**
2809 * Check that there is a backup status available
2810 */
2811 if (!$backupStatus) {
2812 throw new \Exception('There was an error retrieving your backup information');
2813 }
2814
2815 /**
2816 * Assign the flags to variables
2817 */
2818 $databaseExcluded = $backupStatus['excludedDatabase'];
2819
2820 /**
2821 * Default the archive result
2822 */
2823 $archiveResult = $this->archiveCompletionResponse(false, false);
2824
2825 /**
2826 * Result of the response when the DB is excluded
2827 */
2828 if ($databaseExcluded && $backupStatus['codebaseBackupComplete']) {
2829 $archiveResult = $this->archiveCompletionResponse(true, false);
2830 }
2831
2832 /**
2833 * Result of the response when the DB is not excluded
2834 */
2835 if (!$databaseExcluded && $backupStatus['codebaseBackupComplete'] && $backupStatus['databaseExportComplete']) {
2836 $archiveResult = $this->archiveCompletionResponse(true, true);
2837 }
2838
2839 /**
2840 * Notify the client whether or not the migration should be started or the db exports need to be zipped
2841 */
2842 wp_send_json_success($archiveResult);
2843
2844 } catch (\Exception $exception) {
2845 wp_send_json_error($exception->getMessage(), 400);
2846 }
2847 }
2848
2849 public function initiateUpload()
2850 {
2851 check_ajax_referer('start_upload', 'security');
2852
2853 $transferDetail = get_transient('transferito_transfer_detail');
2854
2855 try {
2856 scandir(TRANSFERITO_UPLOAD_PATH);
2857
2858 /**
2859 * Create an array to hold the backups
2860 */
2861 $backup = [];
2862
2863 /**
2864 * Start the upload for the codebase archive
2865 */
2866 try {
2867 $codebaseUploadId = $this->upload->startUpload();
2868 } catch(\Exception $ex) {
2869 throw new \Exception($ex->getMessage());
2870 }
2871
2872 /**
2873 * Add all the relevant detail to the $backup array
2874 */
2875 $backup['archive'] = [
2876 'type' => 'codebase',
2877 'parts' => $this->getFileParts($transferDetail['archive']),
2878 'uploadId' => $codebaseUploadId
2879 ];
2880
2881 /**
2882 * Send response
2883 */
2884 wp_send_json_success([
2885 'backup' => $backup
2886 ]);
2887
2888 } catch (\Exception $exception) {
2889 /**
2890 * Push failedMigration event to telemetry
2891 */
2892 $this->telemetry->pushEvent('failedMigration', [
2893 'migrationStatus' => 'AWSInitiateUpload',
2894 'errorMessage' => $exception->getMessage()
2895 ]);
2896 $errorMessage = 'AWSInitiateUpload: ' . $exception->getMessage();
2897 $this->api->failedMigration($errorMessage);
2898 wp_send_json_error($errorMessage, 400);
2899 }
2900
2901 }
2902
2903 public function uploadChunk()
2904 {
2905 /**
2906 * Verify nonce with every chunk uploaded
2907 */
2908 check_ajax_referer('upload_chunk', 'securityKey');
2909
2910 try {
2911 $transferDetail = get_transient('transferito_transfer_detail');
2912
2913 /**
2914 * Get the correct file
2915 */
2916 $filePath = $transferDetail['archive'];
2917
2918 /**
2919 *
2920 */
2921 $partNumberSanitized = isset($_POST['partNumber'])
2922 ? sanitize_text_field(wp_unslash($_POST['partNumber']))
2923 : 0;
2924
2925 /**
2926 * Get the part number
2927 */
2928 $partNumber = filter_var($partNumberSanitized, FILTER_VALIDATE_INT);
2929
2930 /**
2931 * Get the chunk size
2932 */
2933 $chunkSize = Config::getChunkSize();
2934
2935 /**
2936 * Set FilePointer
2937 */
2938 $filePointer = ($partNumber - 1) * $chunkSize;
2939
2940 /**
2941 * Get the file
2942 */
2943 $file = fopen($filePath , 'rb');
2944
2945 /**
2946 * Set the file pointer
2947 */
2948 fseek($file, $filePointer);
2949
2950 /**
2951 * Get the chunk
2952 */
2953 $chunk = fread($file, $chunkSize);
2954
2955 /**
2956 * Close the file
2957 */
2958 fclose($file);
2959
2960 unset($file);
2961
2962 /**
2963 * Send the chunk to the API
2964 */
2965 try {
2966 $chunkUploaded = $this->upload->uploadChunk($partNumber, $chunk);
2967 } catch(\Exception $ex) {
2968 throw new \Exception($ex->getMessage());
2969 }
2970
2971 unset($chunk);
2972
2973
2974 /**
2975 * Send response
2976 */
2977 wp_send_json_success([
2978 'uploaded' => true,
2979 'partNumber' => $partNumber
2980 ]);
2981
2982 } catch (\Exception $exception) {
2983 /**
2984 * Push failedMigration event to telemetry
2985 */
2986 $this->telemetry->pushEvent('failedMigration', [
2987 'migrationStatus' => 'AWSUploadChunk',
2988 'errorMessage' => $exception->getMessage()
2989 ]);
2990 $errorMessage = 'AWSUploadChunk: ' . $exception->getMessage();
2991 $this->api->failedMigration($errorMessage);
2992 wp_send_json_error('There was an error uploading your chunk', 400);
2993 }
2994 }
2995
2996 public function completeUpload()
2997 {
2998 try {
2999 /**
3000 * Pull the transfer detail
3001 */
3002 $transferDetail = get_transient('transferito_transfer_detail');
3003
3004 /**
3005 * Call the upload complete end point
3006 */
3007 try {
3008 $uploadInfo = $this->upload->completeUpload();
3009 } catch(\Exception $ex) {
3010 throw new \Exception($ex->getMessage());
3011 }
3012
3013 /**
3014 * Assign the upload to the migration
3015 */
3016 $completeUpload = $this->api->completeUpload([
3017 'filename' => $uploadInfo['path'],
3018 'token' => $transferDetail['token'],
3019 'timestamp' => $transferDetail['timestamp']
3020 ]);
3021
3022 /**
3023 * If the upload completion failed
3024 */
3025 if ($completeUpload['code'] !== 200) {
3026 throw new \Exception(stripslashes('Your upload can not be assigned to your migration.'));
3027 }
3028
3029 /**
3030 * Set the publicly accessible path for the backup
3031 */
3032 $transferDetail['archive'] = $uploadInfo['URL'];
3033
3034 /**
3035 * Set transient with transfer detail
3036 */
3037 set_transient('transferito_transfer_detail', $transferDetail);
3038
3039 /**
3040 * Send response
3041 */
3042 wp_send_json_success([
3043 'htmlTemplate' => loadTemplate('parts/loading', [
3044 'showMigrationImage' => true,
3045 'mainMessage' => stripslashes('We have successfully backed up your WordPress installation'),
3046 'secondaryMessage' => 'We are currently migrating your site to your new destination',
3047 ]),
3048 'securityKey' => wp_create_nonce('start_migration')
3049 ]);
3050
3051 } catch (\Exception $exception) {
3052 /**
3053 * Push failedMigration event to telemetry
3054 */
3055 $this->telemetry->pushEvent('failedMigration', [
3056 'migrationStatus' => 'AWSCompleteUpload',
3057 'errorMessage' => $exception->getMessage()
3058 ]);
3059 $errorMessage = 'AWSCompleteUpload: ' . $exception->getMessage();
3060 $this->api->failedMigration($errorMessage);
3061
3062 wp_send_json_error('There was an error completing your upload', 400);
3063 }
3064 }
3065
3066 public function startMigration()
3067 {
3068 check_ajax_referer('start_migration', 'security');
3069
3070 $settings = get_option('transferito_settings_option');
3071 $chunkSize = isset($settings['transferito_chunk_size']) ? $settings['transferito_chunk_size'] : '10';
3072 $deleteVerificationFile = isset($settings['transferito_delete_verification_file']) ? $settings['transferito_delete_verification_file'] : false;
3073 $transferDetail = get_transient('transferito_transfer_detail');
3074 $installationInfo = get_transient('transferito_installation_size');
3075 $dbCharsetInfo = get_transient('transferito_database_charset_info');
3076
3077 /**
3078 * Check if the migration is a local migration or not
3079 */
3080 $localMigration = get_transient('transferito_transfer_method') === 'localSiteMigration';
3081
3082 delete_transient('transferito_transfer_detail');
3083 delete_transient('transferito_database_charset_info');
3084
3085 /**
3086 * Check the user's API keys
3087 */
3088 $hasValidAPIKeys = $this->areAPIKeysValid(
3089 $settings['public_transferito_key'],
3090 $settings['secret_transferito_key']
3091 );
3092
3093 /**
3094 * Set as a FREE User
3095 */
3096 if (!$hasValidAPIKeys) {
3097 $this->api->setFreeUser();
3098 }
3099
3100 /**
3101 * Get connected Site
3102 */
3103 $connectedSite = $this->destinationServerConnectionExists('array');
3104
3105 /**
3106 * Create the payload to send to the API
3107 */
3108 $startMigrationPayload = [
3109 'validationToken' => $connectedSite['token'],
3110 'validationFile' => $connectedSite['filename'],
3111 "domain" => $connectedSite['domain'],
3112 "deleteValidation" => $deleteVerificationFile,
3113 'chunkSize' => $chunkSize,
3114 'charset' => $dbCharsetInfo['actualCharset'],
3115 'configCharset' => $dbCharsetInfo['configCharset'],
3116 'archiveFileAmount' => $installationInfo['amountOfFiles'],
3117 'isLocal' => $transferDetail['isLocalEnv'],
3118 'archive' => $transferDetail['archive'],
3119 'backupDirectory' => $transferDetail['directory'],
3120 'token' => $transferDetail['token'],
3121 'timestamp' => $transferDetail['timestamp'],
3122 'fromUrl' => $transferDetail['fromUrl'],
3123 'currentPath' => ABSPATH
3124 ];
3125
3126 /**
3127 * Send files to API
3128 */
3129 $startMigrationRequest = $this->api->startMigration($startMigrationPayload);
3130 $statusCode = $startMigrationRequest['code'];
3131 $response = $startMigrationRequest['message'];
3132
3133 /**
3134 * If upload notification has been successful
3135 * Add flags stop backup removal
3136 */
3137 if ($statusCode === 200) {
3138 set_transient('transferito_ignore_cleanup', true, 12 * HOUR_IN_SECONDS);
3139 set_transient('transferito_cleanup_after_completion', true, 12 * HOUR_IN_SECONDS);
3140 }
3141
3142 /**
3143 * Return response based on status
3144 */
3145 if ($statusCode === 200) {
3146 $emptyKeys = get_transient('transferito_empty_keys');
3147 $availableTransfers = get_transient('transferito_has_available_transfers');
3148 $message = ($emptyKeys || !$availableTransfers)
3149 ? 'If you navigate away from the site, your migration will continue in the background. As you\'re not a premium member, we advise that you stay on this page until your migration has completed.'
3150 : 'If you navigate away from the site, your migration will continue in the background. You will be sent an email once your migration has completed.';
3151 wp_send_json_success([
3152 'localMigration' => $localMigration,
3153 'token' => $response->token,
3154 'message' => $message
3155 ]);
3156 } else {
3157 $errorMessage = is_array($response) ? 'There has been an error starting your migration' : stripslashes($response);
3158 wp_send_json_error($errorMessage, $statusCode);
3159 }
3160 }
3161
3162 public function cleanUp($url = null)
3163 {
3164 /**
3165 * Verify nonce on clean up
3166 */
3167 check_ajax_referer('run_cleanup', 'securityKey');
3168
3169 $url = get_transient('transferito_final_destination_url');
3170 $hasError = isset($_POST['hasError']) ? sanitize_text_field(wp_unslash($_POST['hasError'])) : false;
3171 $metadata = isset($_POST['metadata']) ? sanitize_text_field(wp_unslash($_POST['metadata'])) : '';
3172
3173 $localMigrationSanitized = isset($_POST['localMigration'])
3174 ? sanitize_text_field(wp_unslash($_POST['localMigration']))
3175 : false;
3176
3177 $localMigration = filter_var($localMigrationSanitized, FILTER_VALIDATE_BOOL);
3178
3179 $errors = isset($_POST['errors']) ? sanitize_text_field(wp_unslash($_POST['errors'])) : null;
3180 $failureList = [
3181 'CPANEL_CHECK_FAILED' => 'There has been an issue checking your URL',
3182 'SWITCH_METHOD_FAILED' => 'There has been an issue switching to the new migration mode',
3183 'FAILED_BACKUP_CHECK' => 'There has been an error processing a backup of your WordPress site',
3184 'FAILED_CREATE_BACKUP' => 'There has been an error creating a backup of your WordPress site',
3185 'FAILED_REMOVE_BACKUP' => 'There has been an error removing a backup of your WordPress site',
3186 'FAILED_STARTING_MIGRATION' => 'There has been an issue starting your migration',
3187 'ERROR_GETTING_STATUS' => 'There has been an issue getting the status of your migration.',
3188 'UPLOAD_START_FAILURE' => 'We have been unable to start uploading your backup to our servers',
3189 'UPLOAD_CHUNK_FAILURE' => 'An error has occurred whilst uploading your backup to our servers',
3190 'UPLOAD_COMPLETION_FAILURE' => 'We are unable to complete the upload of your backup to our servers',
3191 'USE_CUSTOM_ERROR_MESSAGE' => $errors && isset($errors['data']) ? $errors['data'] : 'There has been an issue processing your migration',
3192 ];
3193 $validError = in_array($hasError, array_keys($failureList));
3194
3195 $completionHTMLTemplate = ($localMigration)
3196 ? 'parts/migration/local-migration-completed'
3197 : 'parts/migration/completed';
3198
3199 try {
3200 clearstatcache();
3201
3202 if (file_exists(TRANSFERITO_UPLOAD_PATH)) {
3203 /**
3204 * Check if there has been an error archiving
3205 */
3206 $this->checkArchiveError();
3207
3208 /**
3209 * Remove everything in the transferito directory
3210 */
3211 $this->purgeDirectory(TRANSFERITO_UPLOAD_PATH);
3212 }
3213
3214 /**
3215 * Remove all instances of the db import directory
3216 */
3217 $this->removeDBImportDirectory();
3218
3219 /**
3220 * If the error exists
3221 */
3222 if ($validError) {
3223 $this->api->failedMigration($failureList[$hasError]);
3224 $htmlTemplate = [
3225 'error' => $failureList[$hasError],
3226 'failed' => true,
3227 ];
3228 } else {
3229 $htmlTemplate = loadTemplate($completionHTMLTemplate, [
3230 'url' => $url,
3231 'metadata' => $metadata
3232 ]);
3233 }
3234 } catch (\Exception $exception) {
3235 if ($validError) {
3236 $this->api->failedMigration($errors);
3237 $htmlTemplate = [
3238 'error' => $failureList[$hasError],
3239 'failed' => true,
3240 'secondary' => stripslashes('We have not been able to remove your backup. Please remove it manually.')
3241 ];
3242 } else {
3243 $this->api->failedMigration($failureList['FAILED_REMOVE_BACKUP']);
3244 $htmlTemplate = loadTemplate($completionHTMLTemplate, [ 'url' => $url, 'error' => 'FAILED_BACKUP_REMOVAL' ]);
3245 }
3246 }
3247
3248 $this->enableAutoPrependOption();
3249 $this->enableWPObjectCache();
3250 $this->removeTransferitoTransients();
3251
3252 wp_send_json_success([ 'htmlTemplate' => $htmlTemplate ]);
3253 }
3254
3255 private function areAPIKeysValid($publicKey, $secretKey) {
3256 $valid = null;
3257
3258 try {
3259 $planInfo = $this->api->planInformation([
3260 'publicTransferito_APIKey' => $publicKey,
3261 'secretTransferito_APIKey' => $secretKey
3262 ]);
3263 $code = $planInfo['code'];
3264
3265 /**
3266 * Success is a 200
3267 * If anything else throw an error
3268 */
3269 if ($code !== 200) {
3270 throw new \Exception('INVALID_API_KEYS');
3271 }
3272
3273 $availableTransfers = isset($planInfo['message']->availableTransfers)
3274 ? $planInfo['message']->availableTransfers
3275 : 0;
3276
3277 set_transient('transferito_has_available_transfers', ($availableTransfers !== 0));
3278
3279 $valid = ($code === 200 && $availableTransfers !== 0);
3280
3281 } catch(\Exception $exception) {
3282 $valid = false;
3283 }
3284
3285 return $valid;
3286 }
3287
3288 private function checkSiteWithinFreeTier($siteDetails)
3289 {
3290 try {
3291
3292 /**
3293 * Default the user status to FREE
3294 */
3295 $userStatus = 'FREE';
3296
3297 /**
3298 * Default if the user has valid API keys to false
3299 */
3300 $hasValidAPIKeys = false;
3301
3302 /**
3303 * Plan Check
3304 */
3305 if ($this->options && $this->options['public_transferito_key'] && $this->options['secret_transferito_key']) {
3306
3307 /**
3308 * Check the user's API keys
3309 */
3310 $hasValidAPIKeys = $this->areAPIKeysValid(
3311 $this->options['public_transferito_key'],
3312 $this->options['secret_transferito_key']
3313 );
3314
3315 /**
3316 * Only set the status if the API keys are valid
3317 */
3318 if ($hasValidAPIKeys) {
3319 $userStatus = 'PREMIUM';
3320 }
3321 }
3322
3323 /**
3324 * Save the user's status
3325 */
3326 set_transient('transferito_user_status', $userStatus);
3327
3328 /**
3329 * Only update the FREE User Options
3330 * If the user doesn't have Valid API Keys
3331 */
3332 if (!$hasValidAPIKeys) {
3333 /**
3334 * Inform the API Wrapper of the type of user
3335 */
3336 $this->api->setFreeUser();
3337 $this->api->setMaxSizeExceeded($siteDetails['maxSizeExceeded']);
3338
3339 /**
3340 * Update legend message
3341 */
3342 if ($siteDetails['maxSizeExceeded']) {
3343 /**
3344 * Push upgradeRequired event to telemetry
3345 */
3346 $this->telemetry->pushEvent('premiumUpgradeRequired', [
3347 'exceededFreeTierSize' => 'yes',
3348 ]);
3349 }
3350 }
3351
3352 } catch(\Exception $exception) {
3353 /**
3354 * Push upgradeRequired event to telemetry
3355 */
3356 $this->telemetry->pushEvent('premiumUpgradeRequired', [
3357 'exceededFreeTierSize' => 'yes',
3358 ]);
3359 }
3360 }
3361
3362 private function getFileParts($filePath)
3363 {
3364 sleep(5);
3365 getDirectorySize(TRANSFERITO_ABSPATH);
3366 clearstatcache();
3367 $fileInfo = stat($filePath);
3368 $archiveSize = $fileInfo['size'];
3369 $chunkSize = Config::getChunkSize();
3370 return ceil($archiveSize / $chunkSize);
3371 }
3372
3373 private function freshStart() {
3374 try {
3375 clearstatcache();
3376
3377 $this->enableAutoPrependOption();
3378 $this->enableWPObjectCache();
3379 $this->removeTransferitoTransients();
3380 $this->removeDBImportDirectory();
3381
3382 /**
3383 * Check if an unfinished migration still exists
3384 */
3385 if (file_exists(TRANSFERITO_UPLOAD_PATH)) {
3386 /**
3387 * Check if there has been an error archiving
3388 */
3389 $this->checkArchiveError();
3390
3391 /**
3392 * Remove everything in the transferito directory
3393 */
3394 $this->purgeDirectory(TRANSFERITO_UPLOAD_PATH);
3395 }
3396 } catch(\Exception $exception) {
3397 // do nada - for now
3398 }
3399 }
3400
3401 private function removeTransferitoTransients()
3402 {
3403 delete_transient('transferito_size_size_in_bytes');
3404 delete_transient('transferito_readable_size_size_in_bytes');
3405 delete_transient('transferito_request_fallback');
3406 delete_transient('transferito_destination_url');
3407 delete_transient('transferito_backup_status');
3408 delete_transient('transferito_transfer_detail');
3409 delete_transient('transferito_final_destination_url');
3410 delete_transient('transferito_codebase_archive');
3411 delete_transient('transferito_database_table_map');
3412 delete_transient('transferito_ignore_cleanup');
3413 delete_transient('transferito_cleanup_after_completion');
3414 delete_transient('transferito_db_export_progress');
3415 delete_transient('transferito_migration_token');
3416 delete_transient('transferito_migration_timestamp');
3417 delete_transient('transferito_migration_domain');
3418 delete_transient('transferito_migration_unchanged_domain');
3419 delete_transient('transferito_transfer_method');
3420 delete_transient('transferito_cpanel_allowed');
3421 delete_transient('transferito_cpanel_domain');
3422 delete_transient('transferito_manual_server_detail');
3423 delete_transient('transferito_use_backup_fallback');
3424 delete_transient('transferito_installation_size');
3425 delete_transient('transferito_requirements');
3426 delete_transient('transferito_database_relocation_pid');
3427 delete_transient('transferito_codebase_archive_pid');
3428 delete_transient('transferito_upload_information');
3429 delete_transient('transferito_database_charset_info');
3430 delete_transient('transferito_cpanel_auth_details');
3431 delete_transient('transferito_empty_keys');
3432 delete_transient('transferito_has_available_transfers');
3433 delete_transient('transferito_database_detail_completed');
3434 delete_transient('transferito_cpanel_auth_details_completed');
3435 delete_transient('transferito_archive_extension');
3436 delete_transient('transferito_cpanel_domain_selection');
3437 delete_transient('transferito_object_cache_filter_present');
3438 delete_transient('transferito_auto_prepend_option_disabled');
3439
3440 }
3441
3442 private function archiveCompletionResponse($backupsCompleted, $zipDBExport)
3443 {
3444 /**
3445 * Get the Transfer Detail
3446 */
3447 $transferDetail = get_transient('transferito_transfer_detail');
3448
3449 /**
3450 * Get the local Env flag
3451 */
3452 $localEnv = $transferDetail['isLocalEnv'];
3453
3454 /**
3455 * Information for the client
3456 */
3457 $information = null;
3458
3459 /**
3460 * If the zip DB Export is not needed
3461 */
3462 if ($backupsCompleted) {
3463 /**
3464 * If the site can not be reached - Then upload the archive
3465 */
3466 if ($localEnv) {
3467 $information = [
3468 'htmlTemplate' => loadTemplate('parts/migration/upload-progress', [
3469 'title' => 'Upload in progress. Please wait...',
3470 'mainMessage' => stripslashes('We are uploading a backup of your site to our secure servers.'),
3471 'secondaryMessage' => stripslashes('Please wait.. This may take a few minutes, do not close this window or refresh the page'),
3472 ]),
3473 'securityKey' => wp_create_nonce('start_upload'),
3474 'uploadFiles' => true,
3475 ];
3476 }
3477
3478 /**
3479 * If the site can be reached - Then pull direct
3480 */
3481 if (!$localEnv) {
3482 $information = [
3483 'htmlTemplate' => loadTemplate('parts/loading', [
3484 'showMigrationImage' => true,
3485 'mainMessage' => stripslashes('We have successfully backed up your WordPress installation'),
3486 'secondaryMessage' => 'We are currently migrating your site to your new destination',
3487 ]),
3488 'securityKey' => wp_create_nonce('start_migration'),
3489 'uploadFiles' => false,
3490 ];
3491 }
3492 }
3493
3494 return [
3495 'backupComplete' => $backupsCompleted,
3496 'zipDatabaseExport' => $zipDBExport,
3497 'information' => $information
3498 ];
3499 }
3500
3501 private function removeDBImportDirectory()
3502 {
3503 $importDirectory = TRANSFERITO_ABSPATH . 'transferito_import';
3504
3505 /**
3506 * If the Directory exists
3507 */
3508 if (file_exists($importDirectory)) {
3509 array_map('wp_delete_file', array_filter(glob($importDirectory . '/*')));
3510 rmdir($importDirectory);
3511 }
3512 }
3513
3514 private function purgeDirectory($directory)
3515 {
3516 if (!$directory) {
3517 return true;
3518 }
3519
3520 clearstatcache();
3521
3522 $it = new \RecursiveDirectoryIterator($directory, \RecursiveDirectoryIterator::SKIP_DOTS);
3523 $files = new \RecursiveIteratorIterator($it, \RecursiveIteratorIterator::CHILD_FIRST);
3524 foreach($files as $file) {
3525 if ($file->isDir()){
3526 rmdir($file->getRealPath());
3527 } else {
3528 wp_delete_file($file->getRealPath());
3529 }
3530 }
3531
3532 clearstatcache();
3533
3534 rmdir($directory);
3535
3536 return true;
3537 }
3538
3539 private function checkArchiveError()
3540 {
3541 $archiveErrorPath = TRANSFERITO_UPLOAD_PATH . DIRECTORY_SEPARATOR . '.archive-error';
3542 $archiveErrorRootPath = TRANSFERITO_ABSPATH . '.archive-error';
3543 $errorFileExists = file_exists($archiveErrorPath);
3544
3545 if ($errorFileExists) {
3546 $hasErrors = strlen(file_get_contents($archiveErrorPath)) > 0;
3547
3548 /**
3549 * If the file has errors
3550 * Then move the archive error file to the root directory
3551 */
3552 if ($hasErrors) {
3553 rename($archiveErrorPath, $archiveErrorRootPath);
3554 }
3555 }
3556 }
3557
3558 private function disableAutoPrependOption()
3559 {
3560 $autoPrependOptionData = getPrependOptionNameData();
3561 $userIniFilePath = $autoPrependOptionData['path'];
3562 $userIniFileExists = file_exists($userIniFilePath);
3563
3564 /**
3565 * Only proceed with the user.ini modification if the file exists
3566 */
3567 if ($userIniFileExists) {
3568 try {
3569
3570 $prependOptionName = $autoPrependOptionData['text'];
3571 $commentedPrependOptionName = $autoPrependOptionData['commented'];
3572 $userIniFile = file_get_contents($userIniFilePath);
3573 $optionExists = str_contains($userIniFile, $prependOptionName);
3574
3575 /**
3576 * If there isn't an auto_prepend option throw a graceful error
3577 */
3578 if (!$optionExists) {
3579 throw new \Exception('AUTO_PREPEND_MODIFICATION_NOT_REQUIRED');
3580 }
3581
3582 /**
3583 * Check to see if the option has been commented previously
3584 */
3585 $optionPreviouslyCommentedExists = str_contains($userIniFile, $commentedPrependOptionName);
3586
3587 /**
3588 * If a comment doesn't exist - modify the file
3589 */
3590 if (!$optionPreviouslyCommentedExists) {
3591 $modifiedUserIni = str_replace($prependOptionName, $commentedPrependOptionName, $userIniFile);
3592 file_put_contents($userIniFilePath, $modifiedUserIni);
3593 }
3594
3595 set_transient('transferito_auto_prepend_option_disabled', true);
3596
3597 } catch(\Exception $exception) {
3598 set_transient('transferito_auto_prepend_option_disabled', false);
3599 }
3600 }
3601 }
3602
3603 private function disableWPObjectCache()
3604 {
3605 $objectCacheFilterData = getObjectCacheFilterData();
3606
3607 /**
3608 * Only make this change if the function file exists
3609 */
3610 if (file_exists($objectCacheFilterData['path'])) {
3611 try {
3612 $cacheFilterExists = str_contains(file_get_contents($objectCacheFilterData['path']), $objectCacheFilterData['text']);
3613
3614 /**
3615 * If the filter isn't already in the PHP file
3616 */
3617 if (!$cacheFilterExists) {
3618 $filterModified = PHP_EOL . $objectCacheFilterData['text'] . PHP_EOL;
3619 file_put_contents($objectCacheFilterData['path'], $filterModified, FILE_APPEND | LOCK_EX);
3620 }
3621
3622 /**
3623 * Set the transient, so we can decide whether we need to revert the file
3624 */
3625 set_transient('transferito_object_cache_filter_present', true);
3626 } catch (\Exception $exception) {
3627 set_transient('transferito_object_cache_filter_present', false);
3628 }
3629 }
3630 }
3631
3632 private function enableAutoPrependOption()
3633 {
3634 $autoPrependOptionStatus = get_transient('transferito_auto_prepend_option_disabled');
3635
3636 /**
3637 * Only enable the option if the file transient status if truthy
3638 */
3639 if ($autoPrependOptionStatus) {
3640 $autoPrependOptionData = getPrependOptionNameData();
3641
3642 /**
3643 * Check the file exists
3644 */
3645 if (file_exists($autoPrependOptionData['path'])) {
3646 try {
3647 $userIniFile = file_get_contents($autoPrependOptionData['path']);
3648 $commentedOptionExists = str_contains($userIniFile, $autoPrependOptionData['commented']);
3649
3650 /**
3651 * Check to see that the commented option is present
3652 */
3653 if ($commentedOptionExists) {
3654 $modifiedUserIniFile = str_replace($autoPrependOptionData['commented'], $autoPrependOptionData['text'], $userIniFile);
3655 file_put_contents($autoPrependOptionData['path'], $modifiedUserIniFile);
3656 }
3657 } catch(\Exception $exception) {
3658 // handle gracefullu
3659 }
3660 }
3661 }
3662 }
3663
3664 private function enableWPObjectCache()
3665 {
3666 $wpObjectCacheStatus = get_transient('transferito_object_cache_filter_present');
3667
3668 /**
3669 * Only enable if the status is truthy
3670 */
3671 if ($wpObjectCacheStatus) {
3672 $objectCacheFilterData = getObjectCacheFilterData();
3673
3674 /**
3675 * Check the file exists
3676 */
3677 if (file_exists($objectCacheFilterData['path'])) {
3678 try {
3679 $functionsFile = file_get_contents($objectCacheFilterData['path']);
3680 $cacheFilterExists = str_contains($functionsFile, $objectCacheFilterData['text']);
3681
3682 /**
3683 * If the filter lives in the functions file, remove it
3684 */
3685 if ($cacheFilterExists) {
3686 $modifiedFunctionsFile = str_replace($objectCacheFilterData['text'], '', $functionsFile);
3687 file_put_contents($objectCacheFilterData['path'], $modifiedFunctionsFile);
3688 }
3689 } catch(\Exception $exception) {
3690 // Fail gracefully
3691 }
3692 }
3693 }
3694 }
3695
3696 }
3697