PluginProbe ʕ •ᴥ•ʔ
Transferito: WP Migration / 14.1.4
Transferito: WP Migration v14.1.4
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 2 months ago
Transfer.php
3704 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 = null;
1794
1795 if (class_exists('ZipArchive')) {
1796 $zip = new \ZipArchive;
1797 } else {
1798 throw new \Exception('ZipArchive not installed');
1799 }
1800
1801
1802 if ($zip->open(TRANSFERITO_ABSPATH . $sampleArchiveFilename, \ZipArchive::CREATE) === TRUE) {
1803 $zip->addFromString('test_file.txt', 'Test zip file - to test migration');
1804 $zip->close();
1805 }
1806
1807 /**
1808 * Request to check the direct download
1809 */
1810 $directDownload = $this->api->directDownloadCheck([
1811 'validationToken' => $connectedSite['token'],
1812 'validationFile' => $connectedSite['filename'],
1813 "domain" => $destinationURL,
1814 'currentURL' => site_url(),
1815 'filename' => $sampleArchiveFilename,
1816 ]);
1817
1818 /**
1819 * Assign the result to
1820 */
1821 $siteAccessed = $directDownload['message']->canDownload;
1822
1823 /**
1824 * Remove the sample file
1825 */
1826 wp_delete_file(TRANSFERITO_ABSPATH . $sampleArchiveFilename);
1827
1828 } catch(\Exception $exception) {
1829 $siteAccessed = false;
1830 }
1831 }
1832
1833 /**
1834 * Add the local flag to the ftp details
1835 */
1836 $migrationDetails['isLocal'] = $siteAccessed;
1837
1838 /**
1839 * If the folder path is set and the transfer type is manual
1840 * Then validate the
1841 */
1842 $additionalData = [];
1843 if (isset($migrationDetails['folder_path']) && $migrationDetails['transferType'] === 'manual') {
1844 $additionalData['folder_path'] = array_map('sanitize_text_field', wp_unslash($migrationDetails['folder_path']));
1845 }
1846
1847 /**
1848 * Clean text fields before create migration request
1849 */
1850 $migrationPayload = array_merge($migrationDetails, $additionalData);
1851 $migrationPayload['isLocal'] = $siteAccessed;
1852 $migrationPayload['currentPath'] = ABSPATH;
1853
1854 /**
1855 * Create a migration and return a token
1856 */
1857 $createdMigration = $this->api->createMigration($migrationPayload);
1858
1859 /**
1860 * Fail gracefully if there is an issue creating the migration
1861 */
1862 if ($createdMigration['code'] !== 200) {
1863 $message = (property_exists($createdMigration['message'], 'result'))
1864 ? $createdMigration['message']->result
1865 : 'We are unable to create your migration. If this issue persists, please contact support.';
1866 throw new \Exception(stripslashes($message));
1867 }
1868
1869 /**
1870 * Set transients to use with the failure endpoint
1871 */
1872 set_transient('transferito_migration_token', $createdMigration['message']->token);
1873 set_transient('transferito_migration_timestamp', $createdMigration['message']->timestamp);
1874
1875 /**
1876 * Get the migration token
1877 */
1878 $migrationToken = $createdMigration['message']->token;
1879
1880 /**
1881 * Final Domain
1882 */
1883 $finalDomain = $createdMigration['message']->domain;
1884
1885 $serverCheck = null;
1886
1887 /**
1888 * If we can contact the site
1889 * Then check the server requirements
1890 */
1891 if ($siteAccessed) {
1892 /**
1893 * Create the test file & token
1894 */
1895 $testFile = Config::createTestFile();
1896
1897 /**
1898 * Check the destination server info
1899 */
1900 $serverRequirements = $this->api->checkDestinationServerRequirements([
1901 'validationToken' => $connectedSite['token'],
1902 'validationFile' => $connectedSite['filename'],
1903 'domain' => $destinationURL,
1904 'token' => $migrationToken,
1905 'timestamp' => $createdMigration['message']->timestamp,
1906 'fileURL' => $testFile['url'],
1907 'fileHash' => $testFile['hash']
1908 ]);
1909
1910 /**
1911 * Fallback
1912 * If the server req check fails
1913 */
1914 if ($serverRequirements['code'] !== 200) {
1915 $uploadToS3 = true;
1916 } else {
1917 $serverCheck = $serverRequirements;
1918 /**
1919 * Check the site access again
1920 * If it is a local environment - or if the module allowed is wget - Then default to S3
1921 */
1922 $uploadToS3 = !$serverRequirements['message']->pullDirect;
1923 }
1924 }
1925
1926 /**
1927 * If we can not reach the site
1928 * Upload directly to S3 bucket
1929 */
1930 if (!$siteAccessed) {
1931 $uploadToS3 = true;
1932 }
1933
1934 /**
1935 * Set transient for backup status
1936 */
1937 set_transient('transferito_backup_status', [
1938 'databaseBackupComplete' => $excludeDatabase ? true : false,
1939 'databaseExportComplete' => $excludeDatabase ? true : false,
1940 'codebaseBackupComplete' => false,
1941 'excludedDatabase' => $excludeDatabase
1942 ]);
1943
1944 /**
1945 * Set transient with transfer detail
1946 */
1947 set_transient('transferito_transfer_detail', [
1948 'isLocalEnv' => $uploadToS3,
1949 'selectedFolders' => $selectedFolderEnabled,
1950 'folders' => $folderPaths,
1951 'directory' => $backupDirectory,
1952 'token' => $migrationToken,
1953 'timestamp' => $createdMigration['message']->timestamp,
1954 'fromUrl' => site_url(),
1955 'newUrl' => $finalDomain
1956 ]);
1957
1958 /**
1959 * Set the transient for the destination site URL
1960 */
1961 set_transient('transferito_final_destination_url', $finalDomain);
1962
1963 /**
1964 * Available steps to pass to the template
1965 */
1966 $progressSteps = [
1967 'backupPrepare' => true,
1968 'backupInstallation' => true,
1969 'uploadBackup' => $uploadToS3,
1970 'downloadBackup' => true,
1971 'extractBackup' => true,
1972 'installDatabase' => !$excludeDatabase,
1973 'finalizeInstallation' => true,
1974 'completed' => true
1975 ];
1976
1977 /**
1978 * Get the requirement transient
1979 */
1980 $transferitoRequirements = get_transient('transferito_requirements');
1981
1982 /**
1983 * Push Migration detail event to telemetry
1984 */
1985 $this->telemetry->pushEvent('migrationDetails', [
1986 'uploadBackup' => $uploadToS3,
1987 'localMigration' => $uploadToS3,
1988 'databaseExcluded' => $excludeDatabase,
1989 'selectedFolders' => $selectedFolderEnabled,
1990 'migrationMethod' => $migrationDetails['transferMethod'],
1991 'siteSize' => isset($siteSize['totalSize']) ? $siteSize['totalSize'] : 0,
1992 'cPanelAPIToken' => isset($migrationDetails['cpanelApiToken']) ? $migrationDetails['cpanelApiToken'] : false
1993 ]);
1994
1995 /**
1996 * Push loadScreen event to telemetry
1997 */
1998 $this->telemetry->pushEvent('loadScreen', [
1999 'screenName' => 'migrationInProgress'
2000 ]);
2001
2002 wp_send_json_success(array_merge([
2003 'debug' => [
2004 'siteAccess' => $siteAccessed,
2005 'uploadS3' => $uploadToS3
2006 ],
2007 'message' => '<strong>PLEASE DO NOT</strong> navigate away or reload this page while your migration is in process. Doing so will stop your migration.',
2008 'force' => $forceUpload,
2009 'useZipFallback' => $transferitoRequirements['useZip'],
2010 'created' => true,
2011 'excludeDatabase' => $excludeDatabase,
2012 'upgradeRequired' => false,
2013 'htmlTemplate' => loadTemplate('parts/migration/progress/main',
2014 array_merge($progressSteps, [
2015 'method' => $migrationDetails['transferMethod'],
2016 'migrationToken' => $migrationToken
2017 ] )
2018 )
2019 ], $siteSize));
2020
2021 } catch (\Exception $exception) {
2022 /**
2023 * Push loadScreen event to telemetry
2024 */
2025 $this->telemetry->pushEvent('failedMigration', [
2026 'migrationStatus' => 'prepareDownload',
2027 'errorMessage' => $exception->getMessage()
2028 ]);
2029 $errorMessage = 'prepareDownload: ' . $exception->getMessage();
2030 $this->api->failedMigration($errorMessage);
2031 delete_transient('transferito_transfer_detail');
2032 wp_send_json_error([
2033 'message' => $exception->getMessage(),
2034 'error' => $exception->getTraceAsString(),
2035 'line' => $exception->getLine()
2036 ], 400);
2037 }
2038 }
2039
2040 private function prepareLocalDownload($premiumUser)
2041 {
2042 try {
2043
2044 /**
2045 * Fire the required upgrade flow
2046 */
2047 if (!$premiumUser) {
2048 wp_send_json_success([
2049 'message' => '<strong>PLEASE DO NOT</strong> navigate away or reload this page while your migration is in process. Doing so will stop your migration.',
2050 'force' => true,
2051 'useZipFallback' => false,
2052 'created' => true,
2053 'excludeDatabase' => false,
2054 'upgradeRequired' => true,
2055 'htmlTemplate' => loadTemplate('parts/migration/progress/main',
2056 array_merge(
2057 [
2058 'backupPrepare' => true,
2059 'backupInstallation' => true,
2060 'uploadBackup' => false,
2061 'downloadBackup' => true,
2062 'extractBackup' => true,
2063 'installDatabase' => true,
2064 'finalizeInstallation' => true,
2065 'completed' => true
2066 ],
2067 [
2068 'method' => ''
2069 ]
2070 )
2071 )
2072 ]);
2073
2074 die();
2075 }
2076
2077
2078 /**
2079 * @DoNotRemove
2080 * Important as it creates the access file to check whether the migration will be an upload
2081 */
2082 Config::getCorrectPath();
2083
2084 $transferMethod = 'localSiteMigration';
2085 $settings = get_option('transferito_settings_option');
2086 $destinationURL = get_transient('transferito_migration_domain');
2087 $backupDirectory = bin2hex(openssl_random_pseudo_bytes(8));
2088
2089 /**
2090 * Add the check to see if the options are selected
2091 */
2092 $disableWPObjectCache = isset($settings['transferito_disable_wordpress_cache'])
2093 ? $settings['transferito_disable_wordpress_cache']
2094 : false;
2095 $ignoreMalcareWAF = isset($settings['transferito_malcare_waf_plugin_fix'])
2096 ? $settings['transferito_malcare_waf_plugin_fix']
2097 : false;
2098
2099 /**
2100 * Find the functions file - To Disable the WP Object Cache
2101 */
2102 if ($disableWPObjectCache) {
2103 $this->disableWPObjectCache();
2104 }
2105
2106 /**
2107 * Find the user.ini file - To Disable MalCare WAF
2108 */
2109 if ($ignoreMalcareWAF) {
2110 $this->disableAutoPrependOption();
2111 }
2112
2113 /**
2114 * Set the destination URL transient
2115 */
2116 set_transient('transferito_migration_domain', $destinationURL);
2117
2118 /**
2119 * Create a migration and return a token
2120 */
2121 $createdMigration = $this->api->createMigration([
2122 'currentDomain' => site_url(),
2123 'transferMethod' => $transferMethod,
2124 'domain' => $destinationURL,
2125 'isLocal' => true,
2126 'currentPath' => ABSPATH
2127 ]);
2128
2129 /**
2130 * Fail gracefully if there is an issue creating the migration
2131 */
2132 if ($createdMigration['code'] !== 200) {
2133 $message = (property_exists($createdMigration['message'], 'result'))
2134 ? $createdMigration['message']->result
2135 : 'We are unable to create your migration. If this issue persists, please contact support.';
2136 throw new \Exception(stripslashes($message));
2137 }
2138
2139 /**
2140 * Set transients to use with the failure endpoint
2141 */
2142 set_transient('transferito_migration_token', $createdMigration['message']->token);
2143 set_transient('transferito_migration_timestamp', $createdMigration['message']->timestamp);
2144
2145 /**
2146 * Get the migration token
2147 */
2148 $migrationToken = $createdMigration['message']->token;
2149
2150 /**
2151 * Final Domain
2152 */
2153 $finalDomain = $createdMigration['message']->domain;
2154
2155 /**
2156 * Set transient for backup status
2157 */
2158 set_transient('transferito_backup_status', [
2159 'databaseBackupComplete' => false,
2160 'databaseExportComplete' => false,
2161 'codebaseBackupComplete' => false,
2162 'excludedDatabase' => false
2163 ]);
2164
2165 /**
2166 * Set transient with transfer detail
2167 */
2168 set_transient('transferito_transfer_detail', [
2169 'isLocalEnv' => true, // Defaulted to true as will always upload to S3 in this instance
2170 'selectedFolders' => false,
2171 'folders' => null, // No folders have been selected set directly to null
2172 'directory' => $backupDirectory,
2173 'token' => $migrationToken,
2174 'timestamp' => $createdMigration['message']->timestamp,
2175 'fromUrl' => site_url(),
2176 'newUrl' => $finalDomain
2177 ]);
2178
2179 /**
2180 * Set the transient for the destination site URL
2181 */
2182 set_transient('transferito_final_destination_url', $finalDomain);
2183
2184 /**
2185 * Available steps to pass to the template
2186 */
2187 $progressSteps = [
2188 'backupPrepare' => true,
2189 'backupInstallation' => true,
2190 'uploadBackup' => true,
2191 'downloadBackup' => true,
2192 'extractBackup' => true,
2193 'installDatabase' => true,
2194 'finalizeInstallation' => true,
2195 'completed' => true
2196 ];
2197
2198 /**
2199 * Get the requirement transient
2200 */
2201 $transferitoRequirements = get_transient('transferito_requirements');
2202
2203 /**
2204 * Site size info
2205 */
2206 $siteSizeInfo = get_transient('transferito_installation_size');
2207 $siteSize = $siteSizeInfo ? $siteSizeInfo : [];
2208
2209 /**
2210 * Push Migration detail event to telemetry
2211 */
2212 $this->telemetry->pushEvent('migrationDetails', [
2213 'uploadBackup' => true,
2214 'localMigration' => true,
2215 'databaseExcluded' => false,
2216 'selectedFolders' => false,
2217 'migrationMethod' => $transferMethod,
2218 'siteSize' => isset($siteSize['totalSize']) ? $siteSize['totalSize'] : 0,
2219 'cPanelAPIToken' => false
2220 ]);
2221
2222 /**
2223 * Push loadScreen event to telemetry
2224 */
2225 $this->telemetry->pushEvent('loadScreen', [
2226 'screenName' => 'migrationInProgress'
2227 ]);
2228
2229 wp_send_json_success(array_merge([
2230 'message' => '<strong>PLEASE DO NOT</strong> navigate away or reload this page while your migration is in process. Doing so will stop your migration.',
2231 'force' => true,
2232 'useZipFallback' => $transferitoRequirements['useZip'],
2233 'created' => true,
2234 'excludeDatabase' => false,
2235 'htmlTemplate' => loadTemplate('parts/migration/progress/main',
2236 array_merge(
2237 $progressSteps,
2238 [
2239 'method' => 'localSiteMigration'
2240 ]
2241 )
2242 )
2243 ], $siteSize));
2244
2245
2246 } catch (\Exception $exception) {
2247
2248 /**
2249 * @todo Implement a failure screen - If the migration creation fails
2250 */
2251
2252
2253 /**
2254 * Push loadScreen event to telemetry
2255 */
2256 $this->telemetry->pushEvent('failedMigration', [
2257 'migrationStatus' => 'prepareDownload',
2258 'errorMessage' => $exception->getMessage()
2259 ]);
2260 $errorMessage = 'prepareLocalDownload: ' . $exception->getMessage();
2261 $this->api->failedMigration($errorMessage);
2262 delete_transient('transferito_transfer_detail');
2263 wp_send_json_error($exception->getMessage(), 400);
2264 }
2265
2266 }
2267
2268 public function prepareCodebase()
2269 {
2270 try {
2271 check_ajax_referer('prepare_migration_files', 'security');
2272
2273 $transferDetail = get_transient('transferito_transfer_detail');
2274 $fileListCreated = $this->codeBase->createFileList($transferDetail['selectedFolders'], $transferDetail['folders']);
2275 $zipPaths = $this->codeBase->createArchivePath();
2276
2277 /**
2278 * Throw the error if the zip paths are not an array
2279 */
2280 if (is_string($zipPaths)) {
2281 throw new \Exception($zipPaths);
2282 }
2283
2284 /**
2285 * Set transient for zip creation paths
2286 */
2287 set_transient('transferito_codebase_archive', $zipPaths);
2288
2289 /**
2290 * Get the site size
2291 */
2292 $siteSizeInfo = get_transient('transferito_installation_size');
2293
2294 wp_send_json_success(array_merge($fileListCreated, $siteSizeInfo));
2295
2296 } catch(\Exception $exception) {
2297 /**
2298 * Push loadScreen event to telemetry
2299 */
2300 $this->telemetry->pushEvent('failedMigration', [
2301 'migrationStatus' => 'prepareCodebase',
2302 'errorMessage' => $exception->getMessage()
2303 ]);
2304 $errorMessage = 'prepareCodebase: ' . $exception->getMessage();
2305 $this->api->failedMigration($errorMessage);
2306 wp_send_json_error($exception->getMessage(), 400);
2307 }
2308 }
2309
2310 public function addFilesToCodebase()
2311 {
2312 try {
2313 check_ajax_referer('prepare_migration_files', 'security');
2314
2315 $currentIndex = isset($_POST['currentFileIndex']) ? sanitize_text_field(wp_unslash($_POST['currentFileIndex'])) : 0;
2316
2317 /**
2318 * Check to see if the client passed the add db flag flag is passed in to
2319 */
2320 $dbExportFileListPath = isset($_POST['addDatabaseExports']) ? DIRECTORY_SEPARATOR . 'db_import' : '';
2321 $zipDetail = get_transient('transferito_codebase_archive');
2322 $jsonFileName = TRANSFERITO_UPLOAD_PATH . $dbExportFileListPath . DIRECTORY_SEPARATOR . 'json' . DIRECTORY_SEPARATOR . 'file_list_' . $currentIndex . '.json';
2323 $jsonFile = file_get_contents($jsonFileName);
2324
2325 if (!file_exists($jsonFileName)) {
2326 throw new \Exception('Something has gone wrong. We can not find the requested back up file list.');
2327 }
2328
2329 if (!$jsonFile) {
2330 throw new \Exception('Something has gone wrong. We can not read requested back up file list.');
2331 }
2332
2333 $files = json_decode($jsonFile);
2334 $addedToArchive = $this->codeBase->addFileToArchive($zipDetail['path'], $files);
2335
2336 /**
2337 * If the response isn't truthy throw
2338 */
2339 if ($addedToArchive !== true) {
2340 throw new \Exception($addedToArchive);
2341 }
2342
2343 wp_send_json_success($zipDetail);
2344 } catch(\Exception $exception) {
2345 wp_send_json_error($exception->getMessage(), 400);
2346 }
2347 }
2348
2349 public function codebaseArchiveComplete()
2350 {
2351 try {
2352 check_ajax_referer('prepare_migration_files', 'security');
2353
2354 /**
2355 * Get the related transients
2356 */
2357 $backupStatus = get_transient('transferito_backup_status');
2358 $transferDetail = get_transient('transferito_transfer_detail');
2359 $zipDetail = get_transient('transferito_codebase_archive');
2360
2361 /**
2362 * Update the backup array
2363 * Notification of the codebase archive completed
2364 */
2365 $backupStatus['codebaseBackupComplete'] = true;
2366
2367 /**
2368 * Update the code path with the archive path or url
2369 */
2370 $uploadS3 = $transferDetail['isLocalEnv'];
2371 $transferDetail['archive'] = $uploadS3 ? $zipDetail['path'] : $zipDetail['url'];
2372
2373 /**
2374 * Update the transients with the updated values
2375 */
2376 set_transient('transferito_backup_status', $backupStatus);
2377 set_transient('transferito_transfer_detail', $transferDetail);
2378
2379 wp_send_json_success([
2380 'codebaseArchived' => true,
2381 'excludeDatabase' => $backupStatus['excludedDatabase']
2382 ]);
2383
2384 } catch (\Exception $exception) {
2385 wp_send_json_error($exception->getMessage(), 400);
2386 }
2387 }
2388
2389 public function prepareDatabase()
2390 {
2391 try {
2392 check_ajax_referer('prepare_migration_files', 'security');
2393
2394 /**
2395 * Prepare a table map that allows the chunked db database export
2396 */
2397 $tableMap = $this->dataBase->prepareTableMap();
2398
2399 /**
2400 * If the table map can not be created
2401 * Throw the error
2402 */
2403 if (!$tableMap) {
2404 throw new \Exception('We were in the process of preparing to back up your database but this has failed.');
2405 }
2406
2407 /**
2408 * Set the table map as a transient
2409 */
2410 set_transient('transferito_database_table_map', $tableMap);
2411
2412 /**
2413 * Return an array
2414 * Notifying the client of the table creation
2415 */
2416 wp_send_json_success([ 'tableMapCreated' => true ]);
2417
2418 } catch(\Exception $exception) {
2419 /**
2420 * Push loadScreen event to telemetry
2421 */
2422 $this->telemetry->pushEvent('failedMigration', [
2423 'migrationStatus' => 'prepareDatabase',
2424 'errorMessage' => $exception->getMessage()
2425 ]);
2426 $errorMessage = 'prepareDatabase: ' . $exception->getMessage();
2427 $this->api->failedMigration($errorMessage);
2428 wp_send_json_error($exception->getMessage(), 400);
2429 }
2430 }
2431
2432 public function chunkedDBExport()
2433 {
2434 try {
2435 check_ajax_referer('prepare_migration_files', 'security');
2436
2437 $firstRunSanitized = isset($_POST['firstRun'])
2438 ? sanitize_text_field(wp_unslash($_POST['firstRun']))
2439 : false;
2440
2441 /**
2442 * Validate the first run flag and convert it to a boolean
2443 */
2444 $firstRun = filter_var($firstRunSanitized, FILTER_VALIDATE_BOOLEAN);
2445
2446 /**
2447 * Pull the export progress data
2448 */
2449 $getDBProgress = get_transient('transferito_db_export_progress');
2450 $exportProgress = $getDBProgress ? $getDBProgress : [];
2451
2452 /**
2453 * On the first run - No arguments needed
2454 */
2455 if ($firstRun) {
2456 $exportResult = $this->dataBase->chunkedDBExport();
2457 }
2458
2459 /**
2460 * If it isn't the first run
2461 * Pass in the export progress
2462 */
2463 if (!$firstRun) {
2464 $exportResult = $this->dataBase->chunkedDBExport(
2465 $exportProgress['fileIndex'],
2466 $exportProgress['currentRowIndex'],
2467 $exportProgress['tableIndex']
2468 );
2469 }
2470
2471 /**
2472 * If the export fails - throw
2473 */
2474 if (!$exportResult) {
2475 throw new \Exception('We are unable to backup a part of your database.');
2476 }
2477
2478 /**
2479 * Return an array
2480 * Notifying the client of the status of the export
2481 */
2482 $mergedArray = array_merge($exportResult, $exportProgress, [ 'firstRun' => $firstRun ]);
2483 wp_send_json_success($mergedArray);
2484
2485 } catch(\Exception $exception) {
2486 wp_send_json_error($exception->getMessage(), 400);
2487 }
2488 }
2489
2490 public function databaseExportComplete()
2491 {
2492 try {
2493 check_ajax_referer('prepare_migration_files', 'security');
2494
2495 /**
2496 * Get the related transients
2497 */
2498 $backupStatus = get_transient('transferito_backup_status');
2499
2500 /**
2501 * Update the backup array
2502 * Notification of the database export completion
2503 */
2504 $backupStatus['databaseExportComplete'] = true;
2505
2506 /**
2507 * Get the requirement transient
2508 */
2509 $transferitoRequirements = get_transient('transferito_requirements');
2510
2511 /**
2512 * Update the transients with the updated values
2513 */
2514 set_transient('transferito_backup_status', $backupStatus);
2515
2516 wp_send_json_success([
2517 'databaseExported' => true ,
2518 'useZipFallback' => $transferitoRequirements['useZip'],
2519 ]);
2520
2521 } catch (\Exception $exception) {
2522 wp_send_json_error($exception->getMessage(), 400);
2523 }
2524 }
2525
2526 public function databaseRelocation()
2527 {
2528 try {
2529 check_ajax_referer('prepare_migration_files', 'security');
2530
2531 $this->dataBase->moveDatabaseFiles();
2532
2533 wp_send_json_success([ 'moved' => true ]);
2534
2535 } catch (\Exception $exception) {
2536 wp_send_json_error(stripslashes('We could not move your database files'), 400);
2537 }
2538 }
2539
2540 public function databaseRelocationCheck()
2541 {
2542 try {
2543 check_ajax_referer('prepare_migration_files', 'security');
2544
2545 /**
2546 * Pull the pid
2547 */
2548 $databaseMovePID = get_transient('transferito_database_relocation_pid');
2549
2550 /**
2551 * Get the site size
2552 */
2553 $siteSizeInfo = get_transient('transferito_installation_size');
2554
2555 wp_send_json_success([
2556 'completed' => checkJobHasCompleted($databaseMovePID),
2557 'siteInfo' => $siteSizeInfo
2558 ]);
2559
2560 } catch (\Exception $exception) {
2561 wp_send_json_error(stripslashes('We can not move your database files'), 400);
2562 }
2563 }
2564
2565 public function archiveCreation()
2566 {
2567 try {
2568 check_ajax_referer('prepare_migration_files', 'security');
2569
2570 /**
2571 * Pull the transfer detail to get the selected folder information
2572 */
2573 $transferDetail = get_transient('transferito_transfer_detail');
2574
2575 /**
2576 * Start the archive
2577 */
2578 $zipPaths = $this->codeBase->createExecArchive($transferDetail['selectedFolders'], $transferDetail['folders']);
2579
2580 /**
2581 * Set transient for zip creation paths
2582 */
2583 set_transient('transferito_codebase_archive', $zipPaths);
2584
2585 wp_send_json_success([ 'archiveCreationStarted' => true ]);
2586
2587 } catch (\Exception $exception) {
2588 /**
2589 * Push loadScreen event to telemetry
2590 */
2591 $this->telemetry->pushEvent('failedMigration', [
2592 'migrationStatus' => 'archiveCreation',
2593 'errorMessage' => $exception->getMessage()
2594 ]);
2595 $errorMessage = 'archiveCreation: ' . $exception->getMessage();
2596 $this->api->failedMigration($errorMessage);
2597 wp_send_json_error(stripslashes('We can not create a backup of your site'), 400);
2598 }
2599 }
2600
2601 public function archiveProgressCheck()
2602 {
2603 try {
2604
2605 check_ajax_referer('prepare_migration_files', 'security');
2606
2607 /**
2608 * Pull the PID for the progress file
2609 */
2610 $archiveCreationPID = get_transient('transferito_codebase_archive_pid');
2611
2612 /**
2613 * Get the file name
2614 */
2615 $progressFile = TRANSFERITO_UPLOAD_PATH . '/.archive-process';
2616
2617 /**
2618 * Get the archives
2619 */
2620 $archives = get_transient('transferito_codebase_archive');
2621
2622 /**
2623 * Get info for the archive path
2624 */
2625 $archiveInfo = pathinfo($archives['path']);
2626
2627 /**
2628 * Progress value
2629 */
2630 $progressValue = null;
2631
2632 /**
2633 * Set the archive result
2634 */
2635 $archiveResult = [];
2636
2637 /**
2638 * If the file is TAR
2639 * Read the amount of files
2640 */
2641 if ($archiveInfo['extension'] === 'tar') {
2642 /**
2643 * Pull the information about the installation
2644 */
2645 $installationInfo = get_transient('transferito_installation_size');
2646
2647 /**
2648 * Get the amount of lines in the progress file
2649 */
2650 $file = new \SplFileObject($progressFile, 'r');
2651 $file->seek(PHP_INT_MAX);
2652 $progressAmount = $file->key() + 1;
2653
2654 /**
2655 * Calculate the percentage of the archive creation
2656 */
2657 $progressValue = ($progressAmount / $installationInfo['amountOfFiles']) * 100;
2658 }
2659
2660 /**
2661 * If the archive is ZIP
2662 * Calculate the progress of the archive creation based on the zip verbose file structure
2663 */
2664 if ($archiveInfo['extension'] === 'zip') {
2665 /**
2666 * Get the last line of the progress file
2667 */
2668 $lastLine = transferitoGetLastLine($progressFile);
2669
2670 /**
2671 * Get everything within the brackets
2672 */
2673 preg_match("/\[(.*?)\]/", $lastLine, $match);
2674
2675 /**
2676 * If there is a match
2677 * Process the logic to calculate the progress
2678 */
2679 if (count($match) === 2) {
2680 /**
2681 * Remove all spaces from string
2682 */
2683 $cleanValue = str_replace(' ', '', $match[1]);
2684
2685 /**
2686 * Split the progress values
2687 */
2688 $values = explode('/', $cleanValue);
2689
2690 /**
2691 * Check that the 2 elements exist in the array
2692 */
2693 if (count($values) === 2) {
2694 /**
2695 * The amount completed via the zip
2696 */
2697 $completedAmount = preg_split('/(?<=[0-9])(?=[a-z]+)/i', $values[0]);
2698
2699 /**
2700 * The amount remaining
2701 */
2702 $remainingAmount = preg_split('/(?<=[0-9])(?=[a-z]+)/i', $values[1]);
2703
2704 /**
2705 * If the amount id 0 - dont count it yet
2706 */
2707 if ($completedAmount !== '0') {
2708
2709 $completedInBytes = transferitoConvertToBytes($completedAmount);
2710 $remainingInBytes = transferitoConvertToBytes($remainingAmount);
2711 $total = $completedInBytes + $remainingInBytes;
2712 $progressValue = ($total === 0) ? 0 : round(($completedInBytes / $total) * 100);
2713 }
2714 }
2715 }
2716 }
2717
2718 /**
2719 * Check if the process has completed
2720 */
2721 $completed = checkJobHasCompleted($archiveCreationPID);
2722
2723 /**
2724 * Remove the DB import directory
2725 */
2726 if ($completed) {
2727
2728 $transferDetail = get_transient('transferito_transfer_detail');
2729
2730 /**
2731 * Update the code path with the archive path or url
2732 */
2733 $uploadS3 = $transferDetail['isLocalEnv'];
2734 $transferDetail['archive'] = $uploadS3 ? $archives['path'] : $archives['url'];
2735
2736 /**
2737 * Save the archive
2738 */
2739 set_transient('transferito_transfer_detail', $transferDetail);
2740
2741 /**
2742 * Remove the import directory
2743 */
2744 $this->removeDBImportDirectory();
2745
2746 /**
2747 * Get the backup status
2748 */
2749 $backupStatus = get_transient('transferito_backup_status');
2750
2751 /**
2752 * Assign the flags to variables
2753 */
2754 $databaseExcluded = $backupStatus['excludedDatabase'];
2755
2756 /**
2757 * Result of the response when the DB is excluded
2758 */
2759 if ($databaseExcluded) {
2760 $archiveResult = $this->archiveCompletionResponse(true, false);
2761 }
2762
2763 /**
2764 * Result of the response when the DB is not excluded
2765 */
2766 if (!$databaseExcluded) {
2767 $archiveResult = $this->archiveCompletionResponse(true, true);
2768 }
2769 }
2770
2771 wp_send_json_success(array_merge([
2772 'completed' => $completed,
2773 'progress' => $progressValue
2774 ], $archiveResult));
2775
2776 } catch (\Exception $exception) {
2777 wp_send_json_error(stripslashes('We can not get the progress of your backup'), 400);
2778 }
2779 }
2780
2781 public function archiveDBExport()
2782 {
2783 try {
2784 check_ajax_referer('prepare_migration_files', 'security');
2785
2786 /**
2787 * Create the DB file list
2788 */
2789 $exportFileList = $this->dataBase->createFileList();
2790
2791 /**
2792 * Throw if any issues
2793 */
2794 if (!$exportFileList) {
2795 throw new \Exception('We are unable to create the list of database exports that wll be added to your backup.');
2796 }
2797
2798 wp_send_json_success($exportFileList);
2799
2800 } catch (\Exception $exception) {
2801 wp_send_json_error($exception->getMessage(), 400);
2802 }
2803 }
2804
2805 public function checkArchiveCompletion()
2806 {
2807 try {
2808 check_ajax_referer('prepare_migration_files', 'security');
2809
2810 /**
2811 * Get the backup status
2812 */
2813 $backupStatus = get_transient('transferito_backup_status');
2814
2815 /**
2816 * Check that there is a backup status available
2817 */
2818 if (!$backupStatus) {
2819 throw new \Exception('There was an error retrieving your backup information');
2820 }
2821
2822 /**
2823 * Assign the flags to variables
2824 */
2825 $databaseExcluded = $backupStatus['excludedDatabase'];
2826
2827 /**
2828 * Default the archive result
2829 */
2830 $archiveResult = $this->archiveCompletionResponse(false, false);
2831
2832 /**
2833 * Result of the response when the DB is excluded
2834 */
2835 if ($databaseExcluded && $backupStatus['codebaseBackupComplete']) {
2836 $archiveResult = $this->archiveCompletionResponse(true, false);
2837 }
2838
2839 /**
2840 * Result of the response when the DB is not excluded
2841 */
2842 if (!$databaseExcluded && $backupStatus['codebaseBackupComplete'] && $backupStatus['databaseExportComplete']) {
2843 $archiveResult = $this->archiveCompletionResponse(true, true);
2844 }
2845
2846 /**
2847 * Notify the client whether or not the migration should be started or the db exports need to be zipped
2848 */
2849 wp_send_json_success($archiveResult);
2850
2851 } catch (\Exception $exception) {
2852 wp_send_json_error($exception->getMessage(), 400);
2853 }
2854 }
2855
2856 public function initiateUpload()
2857 {
2858 check_ajax_referer('start_upload', 'security');
2859
2860 $transferDetail = get_transient('transferito_transfer_detail');
2861
2862 try {
2863 scandir(TRANSFERITO_UPLOAD_PATH);
2864
2865 /**
2866 * Create an array to hold the backups
2867 */
2868 $backup = [];
2869
2870 /**
2871 * Start the upload for the codebase archive
2872 */
2873 try {
2874 $codebaseUploadId = $this->upload->startUpload();
2875 } catch(\Exception $ex) {
2876 throw new \Exception($ex->getMessage());
2877 }
2878
2879 /**
2880 * Add all the relevant detail to the $backup array
2881 */
2882 $backup['archive'] = [
2883 'type' => 'codebase',
2884 'parts' => $this->getFileParts($transferDetail['archive']),
2885 'uploadId' => $codebaseUploadId
2886 ];
2887
2888 /**
2889 * Send response
2890 */
2891 wp_send_json_success([
2892 'backup' => $backup
2893 ]);
2894
2895 } catch (\Exception $exception) {
2896 /**
2897 * Push failedMigration event to telemetry
2898 */
2899 $this->telemetry->pushEvent('failedMigration', [
2900 'migrationStatus' => 'AWSInitiateUpload',
2901 'errorMessage' => $exception->getMessage()
2902 ]);
2903 $errorMessage = 'AWSInitiateUpload: ' . $exception->getMessage();
2904 $this->api->failedMigration($errorMessage);
2905 wp_send_json_error($errorMessage, 400);
2906 }
2907
2908 }
2909
2910 public function uploadChunk()
2911 {
2912 /**
2913 * Verify nonce with every chunk uploaded
2914 */
2915 check_ajax_referer('upload_chunk', 'securityKey');
2916
2917 try {
2918 $transferDetail = get_transient('transferito_transfer_detail');
2919
2920 /**
2921 * Get the correct file
2922 */
2923 $filePath = $transferDetail['archive'];
2924
2925 /**
2926 *
2927 */
2928 $partNumberSanitized = isset($_POST['partNumber'])
2929 ? sanitize_text_field(wp_unslash($_POST['partNumber']))
2930 : 0;
2931
2932 /**
2933 * Get the part number
2934 */
2935 $partNumber = filter_var($partNumberSanitized, FILTER_VALIDATE_INT);
2936
2937 /**
2938 * Get the chunk size
2939 */
2940 $chunkSize = Config::getChunkSize();
2941
2942 /**
2943 * Set FilePointer
2944 */
2945 $filePointer = ($partNumber - 1) * $chunkSize;
2946
2947 /**
2948 * Get the file
2949 */
2950 $file = fopen($filePath , 'rb');
2951
2952 /**
2953 * Set the file pointer
2954 */
2955 fseek($file, $filePointer);
2956
2957 /**
2958 * Get the chunk
2959 */
2960 $chunk = fread($file, $chunkSize);
2961
2962 /**
2963 * Close the file
2964 */
2965 fclose($file);
2966
2967 unset($file);
2968
2969 /**
2970 * Send the chunk to the API
2971 */
2972 try {
2973 $chunkUploaded = $this->upload->uploadChunk($partNumber, $chunk);
2974 } catch(\Exception $ex) {
2975 throw new \Exception($ex->getMessage());
2976 }
2977
2978 unset($chunk);
2979
2980
2981 /**
2982 * Send response
2983 */
2984 wp_send_json_success([
2985 'uploaded' => true,
2986 'partNumber' => $partNumber
2987 ]);
2988
2989 } catch (\Exception $exception) {
2990 /**
2991 * Push failedMigration event to telemetry
2992 */
2993 $this->telemetry->pushEvent('failedMigration', [
2994 'migrationStatus' => 'AWSUploadChunk',
2995 'errorMessage' => $exception->getMessage()
2996 ]);
2997 $errorMessage = 'AWSUploadChunk: ' . $exception->getMessage();
2998 $this->api->failedMigration($errorMessage);
2999 wp_send_json_error('There was an error uploading your chunk', 400);
3000 }
3001 }
3002
3003 public function completeUpload()
3004 {
3005 try {
3006 /**
3007 * Pull the transfer detail
3008 */
3009 $transferDetail = get_transient('transferito_transfer_detail');
3010
3011 /**
3012 * Call the upload complete end point
3013 */
3014 try {
3015 $uploadInfo = $this->upload->completeUpload();
3016 } catch(\Exception $ex) {
3017 throw new \Exception($ex->getMessage());
3018 }
3019
3020 /**
3021 * Assign the upload to the migration
3022 */
3023 $completeUpload = $this->api->completeUpload([
3024 'filename' => $uploadInfo['path'],
3025 'token' => $transferDetail['token'],
3026 'timestamp' => $transferDetail['timestamp']
3027 ]);
3028
3029 /**
3030 * If the upload completion failed
3031 */
3032 if ($completeUpload['code'] !== 200) {
3033 throw new \Exception(stripslashes('Your upload can not be assigned to your migration.'));
3034 }
3035
3036 /**
3037 * Set the publicly accessible path for the backup
3038 */
3039 $transferDetail['archive'] = $uploadInfo['URL'];
3040
3041 /**
3042 * Set transient with transfer detail
3043 */
3044 set_transient('transferito_transfer_detail', $transferDetail);
3045
3046 /**
3047 * Send response
3048 */
3049 wp_send_json_success([
3050 'htmlTemplate' => loadTemplate('parts/loading', [
3051 'showMigrationImage' => true,
3052 'mainMessage' => stripslashes('We have successfully backed up your WordPress installation'),
3053 'secondaryMessage' => 'We are currently migrating your site to your new destination',
3054 ]),
3055 'securityKey' => wp_create_nonce('start_migration')
3056 ]);
3057
3058 } catch (\Exception $exception) {
3059 /**
3060 * Push failedMigration event to telemetry
3061 */
3062 $this->telemetry->pushEvent('failedMigration', [
3063 'migrationStatus' => 'AWSCompleteUpload',
3064 'errorMessage' => $exception->getMessage()
3065 ]);
3066 $errorMessage = 'AWSCompleteUpload: ' . $exception->getMessage();
3067 $this->api->failedMigration($errorMessage);
3068
3069 wp_send_json_error('There was an error completing your upload', 400);
3070 }
3071 }
3072
3073 public function startMigration()
3074 {
3075 check_ajax_referer('start_migration', 'security');
3076
3077 $settings = get_option('transferito_settings_option');
3078 $chunkSize = isset($settings['transferito_chunk_size']) ? $settings['transferito_chunk_size'] : '10';
3079 $deleteVerificationFile = isset($settings['transferito_delete_verification_file']) ? $settings['transferito_delete_verification_file'] : false;
3080 $transferDetail = get_transient('transferito_transfer_detail');
3081 $installationInfo = get_transient('transferito_installation_size');
3082 $dbCharsetInfo = get_transient('transferito_database_charset_info');
3083
3084 /**
3085 * Check if the migration is a local migration or not
3086 */
3087 $localMigration = get_transient('transferito_transfer_method') === 'localSiteMigration';
3088
3089 delete_transient('transferito_transfer_detail');
3090 delete_transient('transferito_database_charset_info');
3091
3092 /**
3093 * Check the user's API keys
3094 */
3095 $hasValidAPIKeys = $this->areAPIKeysValid(
3096 $settings['public_transferito_key'],
3097 $settings['secret_transferito_key']
3098 );
3099
3100 /**
3101 * Set as a FREE User
3102 */
3103 if (!$hasValidAPIKeys) {
3104 $this->api->setFreeUser();
3105 }
3106
3107 /**
3108 * Get connected Site
3109 */
3110 $connectedSite = $this->destinationServerConnectionExists('array');
3111
3112 /**
3113 * Create the payload to send to the API
3114 */
3115 $startMigrationPayload = [
3116 'validationToken' => $connectedSite['token'],
3117 'validationFile' => $connectedSite['filename'],
3118 "domain" => $connectedSite['domain'],
3119 "deleteValidation" => $deleteVerificationFile,
3120 'chunkSize' => $chunkSize,
3121 'charset' => $dbCharsetInfo['actualCharset'],
3122 'configCharset' => $dbCharsetInfo['configCharset'],
3123 'archiveFileAmount' => $installationInfo['amountOfFiles'],
3124 'isLocal' => $transferDetail['isLocalEnv'],
3125 'archive' => $transferDetail['archive'],
3126 'backupDirectory' => $transferDetail['directory'],
3127 'token' => $transferDetail['token'],
3128 'timestamp' => $transferDetail['timestamp'],
3129 'fromUrl' => $transferDetail['fromUrl'],
3130 'currentPath' => ABSPATH
3131 ];
3132
3133 /**
3134 * Send files to API
3135 */
3136 $startMigrationRequest = $this->api->startMigration($startMigrationPayload);
3137 $statusCode = $startMigrationRequest['code'];
3138 $response = $startMigrationRequest['message'];
3139
3140 /**
3141 * If upload notification has been successful
3142 * Add flags stop backup removal
3143 */
3144 if ($statusCode === 200) {
3145 set_transient('transferito_ignore_cleanup', true, 12 * HOUR_IN_SECONDS);
3146 set_transient('transferito_cleanup_after_completion', true, 12 * HOUR_IN_SECONDS);
3147 }
3148
3149 /**
3150 * Return response based on status
3151 */
3152 if ($statusCode === 200) {
3153 $emptyKeys = get_transient('transferito_empty_keys');
3154 $availableTransfers = get_transient('transferito_has_available_transfers');
3155 $message = ($emptyKeys || !$availableTransfers)
3156 ? '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.'
3157 : '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.';
3158 wp_send_json_success([
3159 'localMigration' => $localMigration,
3160 'token' => $response->token,
3161 'message' => $message
3162 ]);
3163 } else {
3164 $errorMessage = is_array($response) ? 'There has been an error starting your migration' : stripslashes($response);
3165 wp_send_json_error($errorMessage, $statusCode);
3166 }
3167 }
3168
3169 public function cleanUp($url = null)
3170 {
3171 /**
3172 * Verify nonce on clean up
3173 */
3174 check_ajax_referer('run_cleanup', 'securityKey');
3175
3176 $url = get_transient('transferito_final_destination_url');
3177 $hasError = isset($_POST['hasError']) ? sanitize_text_field(wp_unslash($_POST['hasError'])) : false;
3178 $metadata = isset($_POST['metadata']) ? sanitize_text_field(wp_unslash($_POST['metadata'])) : '';
3179
3180 $localMigrationSanitized = isset($_POST['localMigration'])
3181 ? sanitize_text_field(wp_unslash($_POST['localMigration']))
3182 : false;
3183
3184 $localMigration = filter_var($localMigrationSanitized, FILTER_VALIDATE_BOOL);
3185
3186 $errors = isset($_POST['errors']) ? sanitize_text_field(wp_unslash($_POST['errors'])) : null;
3187 $failureList = [
3188 'CPANEL_CHECK_FAILED' => 'There has been an issue checking your URL',
3189 'SWITCH_METHOD_FAILED' => 'There has been an issue switching to the new migration mode',
3190 'FAILED_BACKUP_CHECK' => 'There has been an error processing a backup of your WordPress site',
3191 'FAILED_CREATE_BACKUP' => 'There has been an error creating a backup of your WordPress site',
3192 'FAILED_REMOVE_BACKUP' => 'There has been an error removing a backup of your WordPress site',
3193 'FAILED_STARTING_MIGRATION' => 'There has been an issue starting your migration',
3194 'ERROR_GETTING_STATUS' => 'There has been an issue getting the status of your migration.',
3195 'UPLOAD_START_FAILURE' => 'We have been unable to start uploading your backup to our servers',
3196 'UPLOAD_CHUNK_FAILURE' => 'An error has occurred whilst uploading your backup to our servers',
3197 'UPLOAD_COMPLETION_FAILURE' => 'We are unable to complete the upload of your backup to our servers',
3198 'USE_CUSTOM_ERROR_MESSAGE' => $errors && isset($errors['data']) ? $errors['data'] : 'There has been an issue processing your migration',
3199 ];
3200 $validError = in_array($hasError, array_keys($failureList));
3201
3202 $completionHTMLTemplate = ($localMigration)
3203 ? 'parts/migration/local-migration-completed'
3204 : 'parts/migration/completed';
3205
3206 try {
3207 clearstatcache();
3208
3209 if (file_exists(TRANSFERITO_UPLOAD_PATH)) {
3210 /**
3211 * Check if there has been an error archiving
3212 */
3213 $this->checkArchiveError();
3214
3215 /**
3216 * Remove everything in the transferito directory
3217 */
3218 $this->purgeDirectory(TRANSFERITO_UPLOAD_PATH);
3219 }
3220
3221 /**
3222 * Remove all instances of the db import directory
3223 */
3224 $this->removeDBImportDirectory();
3225
3226 /**
3227 * If the error exists
3228 */
3229 if ($validError) {
3230 $this->api->failedMigration($failureList[$hasError]);
3231 $htmlTemplate = [
3232 'error' => $failureList[$hasError],
3233 'failed' => true,
3234 ];
3235 } else {
3236 $htmlTemplate = loadTemplate($completionHTMLTemplate, [
3237 'url' => $url,
3238 'metadata' => $metadata
3239 ]);
3240 }
3241 } catch (\Exception $exception) {
3242 if ($validError) {
3243 $this->api->failedMigration($errors);
3244 $htmlTemplate = [
3245 'error' => $failureList[$hasError],
3246 'failed' => true,
3247 'secondary' => stripslashes('We have not been able to remove your backup. Please remove it manually.')
3248 ];
3249 } else {
3250 $this->api->failedMigration($failureList['FAILED_REMOVE_BACKUP']);
3251 $htmlTemplate = loadTemplate($completionHTMLTemplate, [ 'url' => $url, 'error' => 'FAILED_BACKUP_REMOVAL' ]);
3252 }
3253 }
3254
3255 $this->enableAutoPrependOption();
3256 $this->enableWPObjectCache();
3257 $this->removeTransferitoTransients();
3258
3259 wp_send_json_success([ 'htmlTemplate' => $htmlTemplate ]);
3260 }
3261
3262 private function areAPIKeysValid($publicKey, $secretKey) {
3263 $valid = null;
3264
3265 try {
3266 $planInfo = $this->api->planInformation([
3267 'publicTransferito_APIKey' => $publicKey,
3268 'secretTransferito_APIKey' => $secretKey
3269 ]);
3270 $code = $planInfo['code'];
3271
3272 /**
3273 * Success is a 200
3274 * If anything else throw an error
3275 */
3276 if ($code !== 200) {
3277 throw new \Exception('INVALID_API_KEYS');
3278 }
3279
3280 $availableTransfers = isset($planInfo['message']->availableTransfers)
3281 ? $planInfo['message']->availableTransfers
3282 : 0;
3283
3284 set_transient('transferito_has_available_transfers', ($availableTransfers !== 0));
3285
3286 $valid = ($code === 200 && $availableTransfers !== 0);
3287
3288 } catch(\Exception $exception) {
3289 $valid = false;
3290 }
3291
3292 return $valid;
3293 }
3294
3295 private function checkSiteWithinFreeTier($siteDetails)
3296 {
3297 try {
3298
3299 /**
3300 * Default the user status to FREE
3301 */
3302 $userStatus = 'FREE';
3303
3304 /**
3305 * Default if the user has valid API keys to false
3306 */
3307 $hasValidAPIKeys = false;
3308
3309 /**
3310 * Plan Check
3311 */
3312 if ($this->options && $this->options['public_transferito_key'] && $this->options['secret_transferito_key']) {
3313
3314 /**
3315 * Check the user's API keys
3316 */
3317 $hasValidAPIKeys = $this->areAPIKeysValid(
3318 $this->options['public_transferito_key'],
3319 $this->options['secret_transferito_key']
3320 );
3321
3322 /**
3323 * Only set the status if the API keys are valid
3324 */
3325 if ($hasValidAPIKeys) {
3326 $userStatus = 'PREMIUM';
3327 }
3328 }
3329
3330 /**
3331 * Save the user's status
3332 */
3333 set_transient('transferito_user_status', $userStatus);
3334
3335 /**
3336 * Only update the FREE User Options
3337 * If the user doesn't have Valid API Keys
3338 */
3339 if (!$hasValidAPIKeys) {
3340 /**
3341 * Inform the API Wrapper of the type of user
3342 */
3343 $this->api->setFreeUser();
3344 $this->api->setMaxSizeExceeded($siteDetails['maxSizeExceeded']);
3345
3346 /**
3347 * Update legend message
3348 */
3349 if ($siteDetails['maxSizeExceeded']) {
3350 /**
3351 * Push upgradeRequired event to telemetry
3352 */
3353 $this->telemetry->pushEvent('premiumUpgradeRequired', [
3354 'exceededFreeTierSize' => 'yes',
3355 ]);
3356 }
3357 }
3358
3359 } catch(\Exception $exception) {
3360 /**
3361 * Push upgradeRequired event to telemetry
3362 */
3363 $this->telemetry->pushEvent('premiumUpgradeRequired', [
3364 'exceededFreeTierSize' => 'yes',
3365 ]);
3366 }
3367 }
3368
3369 private function getFileParts($filePath)
3370 {
3371 sleep(5);
3372 getDirectorySize(TRANSFERITO_ABSPATH);
3373 clearstatcache();
3374 $fileInfo = stat($filePath);
3375 $archiveSize = $fileInfo['size'];
3376 $chunkSize = Config::getChunkSize();
3377 return ceil($archiveSize / $chunkSize);
3378 }
3379
3380 private function freshStart() {
3381 try {
3382 clearstatcache();
3383
3384 $this->enableAutoPrependOption();
3385 $this->enableWPObjectCache();
3386 $this->removeTransferitoTransients();
3387 $this->removeDBImportDirectory();
3388
3389 /**
3390 * Check if an unfinished migration still exists
3391 */
3392 if (file_exists(TRANSFERITO_UPLOAD_PATH)) {
3393 /**
3394 * Check if there has been an error archiving
3395 */
3396 $this->checkArchiveError();
3397
3398 /**
3399 * Remove everything in the transferito directory
3400 */
3401 $this->purgeDirectory(TRANSFERITO_UPLOAD_PATH);
3402 }
3403 } catch(\Exception $exception) {
3404 // do nada - for now
3405 }
3406 }
3407
3408 private function removeTransferitoTransients()
3409 {
3410 delete_transient('transferito_size_size_in_bytes');
3411 delete_transient('transferito_readable_size_size_in_bytes');
3412 delete_transient('transferito_request_fallback');
3413 delete_transient('transferito_destination_url');
3414 delete_transient('transferito_backup_status');
3415 delete_transient('transferito_transfer_detail');
3416 delete_transient('transferito_final_destination_url');
3417 delete_transient('transferito_codebase_archive');
3418 delete_transient('transferito_database_table_map');
3419 delete_transient('transferito_ignore_cleanup');
3420 delete_transient('transferito_cleanup_after_completion');
3421 delete_transient('transferito_db_export_progress');
3422 delete_transient('transferito_migration_token');
3423 delete_transient('transferito_migration_timestamp');
3424 delete_transient('transferito_migration_domain');
3425 delete_transient('transferito_migration_unchanged_domain');
3426 delete_transient('transferito_transfer_method');
3427 delete_transient('transferito_cpanel_allowed');
3428 delete_transient('transferito_cpanel_domain');
3429 delete_transient('transferito_manual_server_detail');
3430 delete_transient('transferito_use_backup_fallback');
3431 delete_transient('transferito_installation_size');
3432 delete_transient('transferito_requirements');
3433 delete_transient('transferito_database_relocation_pid');
3434 delete_transient('transferito_codebase_archive_pid');
3435 delete_transient('transferito_upload_information');
3436 delete_transient('transferito_database_charset_info');
3437 delete_transient('transferito_cpanel_auth_details');
3438 delete_transient('transferito_empty_keys');
3439 delete_transient('transferito_has_available_transfers');
3440 delete_transient('transferito_database_detail_completed');
3441 delete_transient('transferito_cpanel_auth_details_completed');
3442 delete_transient('transferito_archive_extension');
3443 delete_transient('transferito_cpanel_domain_selection');
3444 delete_transient('transferito_object_cache_filter_present');
3445 delete_transient('transferito_auto_prepend_option_disabled');
3446
3447 }
3448
3449 private function archiveCompletionResponse($backupsCompleted, $zipDBExport)
3450 {
3451 /**
3452 * Get the Transfer Detail
3453 */
3454 $transferDetail = get_transient('transferito_transfer_detail');
3455
3456 /**
3457 * Get the local Env flag
3458 */
3459 $localEnv = $transferDetail['isLocalEnv'];
3460
3461 /**
3462 * Information for the client
3463 */
3464 $information = null;
3465
3466 /**
3467 * If the zip DB Export is not needed
3468 */
3469 if ($backupsCompleted) {
3470 /**
3471 * If the site can not be reached - Then upload the archive
3472 */
3473 if ($localEnv) {
3474 $information = [
3475 'htmlTemplate' => loadTemplate('parts/migration/upload-progress', [
3476 'title' => 'Upload in progress. Please wait...',
3477 'mainMessage' => stripslashes('We are uploading a backup of your site to our secure servers.'),
3478 'secondaryMessage' => stripslashes('Please wait.. This may take a few minutes, do not close this window or refresh the page'),
3479 ]),
3480 'securityKey' => wp_create_nonce('start_upload'),
3481 'uploadFiles' => true,
3482 ];
3483 }
3484
3485 /**
3486 * If the site can be reached - Then pull direct
3487 */
3488 if (!$localEnv) {
3489 $information = [
3490 'htmlTemplate' => loadTemplate('parts/loading', [
3491 'showMigrationImage' => true,
3492 'mainMessage' => stripslashes('We have successfully backed up your WordPress installation'),
3493 'secondaryMessage' => 'We are currently migrating your site to your new destination',
3494 ]),
3495 'securityKey' => wp_create_nonce('start_migration'),
3496 'uploadFiles' => false,
3497 ];
3498 }
3499 }
3500
3501 return [
3502 'backupComplete' => $backupsCompleted,
3503 'zipDatabaseExport' => $zipDBExport,
3504 'information' => $information
3505 ];
3506 }
3507
3508 private function removeDBImportDirectory()
3509 {
3510 $importDirectory = TRANSFERITO_ABSPATH . 'transferito_import';
3511
3512 /**
3513 * If the Directory exists
3514 */
3515 if (file_exists($importDirectory)) {
3516 array_map('wp_delete_file', array_filter(glob($importDirectory . '/*')));
3517 rmdir($importDirectory);
3518 }
3519 }
3520
3521 private function purgeDirectory($directory)
3522 {
3523 if (!$directory) {
3524 return true;
3525 }
3526
3527 clearstatcache();
3528
3529 $it = new \RecursiveDirectoryIterator($directory, \RecursiveDirectoryIterator::SKIP_DOTS);
3530 $files = new \RecursiveIteratorIterator($it, \RecursiveIteratorIterator::CHILD_FIRST);
3531 foreach($files as $file) {
3532 if ($file->isDir()){
3533 rmdir($file->getRealPath());
3534 } else {
3535 wp_delete_file($file->getRealPath());
3536 }
3537 }
3538
3539 clearstatcache();
3540
3541 rmdir($directory);
3542
3543 return true;
3544 }
3545
3546 private function checkArchiveError()
3547 {
3548 $archiveErrorPath = TRANSFERITO_UPLOAD_PATH . DIRECTORY_SEPARATOR . '.archive-error';
3549 $archiveErrorRootPath = TRANSFERITO_ABSPATH . '.archive-error';
3550 $errorFileExists = file_exists($archiveErrorPath);
3551
3552 if ($errorFileExists) {
3553 $hasErrors = strlen(file_get_contents($archiveErrorPath)) > 0;
3554
3555 /**
3556 * If the file has errors
3557 * Then move the archive error file to the root directory
3558 */
3559 if ($hasErrors) {
3560 rename($archiveErrorPath, $archiveErrorRootPath);
3561 }
3562 }
3563 }
3564
3565 private function disableAutoPrependOption()
3566 {
3567 $autoPrependOptionData = getPrependOptionNameData();
3568 $userIniFilePath = $autoPrependOptionData['path'];
3569 $userIniFileExists = file_exists($userIniFilePath);
3570
3571 /**
3572 * Only proceed with the user.ini modification if the file exists
3573 */
3574 if ($userIniFileExists) {
3575 try {
3576
3577 $prependOptionName = $autoPrependOptionData['text'];
3578 $commentedPrependOptionName = $autoPrependOptionData['commented'];
3579 $userIniFile = file_get_contents($userIniFilePath);
3580 $optionExists = str_contains($userIniFile, $prependOptionName);
3581
3582 /**
3583 * If there isn't an auto_prepend option throw a graceful error
3584 */
3585 if (!$optionExists) {
3586 throw new \Exception('AUTO_PREPEND_MODIFICATION_NOT_REQUIRED');
3587 }
3588
3589 /**
3590 * Check to see if the option has been commented previously
3591 */
3592 $optionPreviouslyCommentedExists = str_contains($userIniFile, $commentedPrependOptionName);
3593
3594 /**
3595 * If a comment doesn't exist - modify the file
3596 */
3597 if (!$optionPreviouslyCommentedExists) {
3598 $modifiedUserIni = str_replace($prependOptionName, $commentedPrependOptionName, $userIniFile);
3599 file_put_contents($userIniFilePath, $modifiedUserIni);
3600 }
3601
3602 set_transient('transferito_auto_prepend_option_disabled', true);
3603
3604 } catch(\Exception $exception) {
3605 set_transient('transferito_auto_prepend_option_disabled', false);
3606 }
3607 }
3608 }
3609
3610 private function disableWPObjectCache()
3611 {
3612 $objectCacheFilterData = getObjectCacheFilterData();
3613
3614 /**
3615 * Only make this change if the function file exists
3616 */
3617 if (file_exists($objectCacheFilterData['path'])) {
3618 try {
3619 $cacheFilterExists = str_contains(file_get_contents($objectCacheFilterData['path']), $objectCacheFilterData['text']);
3620
3621 /**
3622 * If the filter isn't already in the PHP file
3623 */
3624 if (!$cacheFilterExists) {
3625 $filterModified = PHP_EOL . $objectCacheFilterData['text'] . PHP_EOL;
3626 file_put_contents($objectCacheFilterData['path'], $filterModified, FILE_APPEND | LOCK_EX);
3627 }
3628
3629 /**
3630 * Set the transient, so we can decide whether we need to revert the file
3631 */
3632 set_transient('transferito_object_cache_filter_present', true);
3633 } catch (\Exception $exception) {
3634 set_transient('transferito_object_cache_filter_present', false);
3635 }
3636 }
3637 }
3638
3639 private function enableAutoPrependOption()
3640 {
3641 $autoPrependOptionStatus = get_transient('transferito_auto_prepend_option_disabled');
3642
3643 /**
3644 * Only enable the option if the file transient status if truthy
3645 */
3646 if ($autoPrependOptionStatus) {
3647 $autoPrependOptionData = getPrependOptionNameData();
3648
3649 /**
3650 * Check the file exists
3651 */
3652 if (file_exists($autoPrependOptionData['path'])) {
3653 try {
3654 $userIniFile = file_get_contents($autoPrependOptionData['path']);
3655 $commentedOptionExists = str_contains($userIniFile, $autoPrependOptionData['commented']);
3656
3657 /**
3658 * Check to see that the commented option is present
3659 */
3660 if ($commentedOptionExists) {
3661 $modifiedUserIniFile = str_replace($autoPrependOptionData['commented'], $autoPrependOptionData['text'], $userIniFile);
3662 file_put_contents($autoPrependOptionData['path'], $modifiedUserIniFile);
3663 }
3664 } catch(\Exception $exception) {
3665 // handle gracefullu
3666 }
3667 }
3668 }
3669 }
3670
3671 private function enableWPObjectCache()
3672 {
3673 $wpObjectCacheStatus = get_transient('transferito_object_cache_filter_present');
3674
3675 /**
3676 * Only enable if the status is truthy
3677 */
3678 if ($wpObjectCacheStatus) {
3679 $objectCacheFilterData = getObjectCacheFilterData();
3680
3681 /**
3682 * Check the file exists
3683 */
3684 if (file_exists($objectCacheFilterData['path'])) {
3685 try {
3686 $functionsFile = file_get_contents($objectCacheFilterData['path']);
3687 $cacheFilterExists = str_contains($functionsFile, $objectCacheFilterData['text']);
3688
3689 /**
3690 * If the filter lives in the functions file, remove it
3691 */
3692 if ($cacheFilterExists) {
3693 $modifiedFunctionsFile = str_replace($objectCacheFilterData['text'], '', $functionsFile);
3694 file_put_contents($objectCacheFilterData['path'], $modifiedFunctionsFile);
3695 }
3696 } catch(\Exception $exception) {
3697 // Fail gracefully
3698 }
3699 }
3700 }
3701 }
3702
3703 }
3704