' AND ( (link_url LIKE %s) OR (link_name LIKE %s) OR (link_description LIKE %s) ) ', $like, $like, $like ); } $category_query = ''; $join = ''; if ( ! empty( $parsed_args['category'] ) ) { $incategories = wp_parse_id_list( $parsed_args['category'] ); if ( count( $incategories ) ) { foreach ( $incategories as $incat ) { if ( empty( $category_query ) ) { $category_query = ' AND ( tt.term_id = ' . $incat . ' '; } else { $category_query .= ' OR tt.term_id = ' . $incat . ' '; } } } } if ( ! empty( $category_query ) ) { $category_query .= ") AND taxonomy = 'link_category'"; $join = " INNER JOIN $wpdb->term_relationships AS tr ON ($wpdb->links.link_id = tr.object_id) INNER JOIN $wpdb->term_taxonomy as tt ON tt.term_taxonomy_id = tr.term_taxonomy_id"; } if ( $parsed_args['show_updated'] ) { $recently_updated_test = ', IF (DATE_ADD(link_updated, INTERVAL 120 MINUTE) >= NOW(), 1,0) as recently_updated '; } else { $recently_updated_test = ''; } $get_updated = ( $parsed_args['show_updated'] ) ? ', UNIX_TIMESTAMP(link_updated) AS link_updated_f ' : ''; $orderby = strtolower( $parsed_args['orderby'] ); $length = ''; switch ( $orderby ) { case 'length': $length = ', CHAR_LENGTH(link_name) AS length'; break; case 'rand': $orderby = 'rand()'; break; case 'link_id': $orderby = "$wpdb->links.link_id"; break; default: $orderparams = array(); $keys = array( 'link_id', 'link_name', 'link_url', 'link_visible', 'link_rating', 'link_owner', 'link_updated', 'link_notes', 'link_description' ); foreach ( explode( ',', $orderby ) as $ordparam ) { $ordparam = trim( $ordparam ); if ( in_array( 'link_' . $ordparam, $keys, true ) ) { $orderparams[] = 'link_' . $ordparam; } elseif ( in_array( $ordparam, $keys, true ) ) { $orderparams[] = $ordparam; } } $orderby = implode( ',', $orderparams ); } if ( empty( $orderby ) ) { $orderby = 'link_name'; } $order = strtoupper( $parsed_args['order'] ); if ( '' !== $order && ! in_array( $order, array( 'ASC', 'DESC' ), true ) ) { $order = 'ASC'; } $visible = ''; if ( $parsed_args['hide_invisible'] ) { $visible = "AND link_visible = 'Y'"; } $query = "SELECT * $length $recently_updated_test $get_updated FROM $wpdb->links $join WHERE 1=1 $visible $category_query"; $query .= " $exclusions $inclusions $search"; $query .= " ORDER BY $orderby $order"; if ( -1 != $parsed_args['limit'] ) { $query .= ' LIMIT ' . absint( $parsed_args['limit'] ); } $results = $wpdb->get_results( $query ); if ( 'rand()' !== $orderby ) { $cache[ $key ] = $results; wp_cache_set( 'get_bookmarks', $cache, 'bookmark' ); } /** This filter is documented in wp-includes/bookmark.php */ return apply_filters( 'get_bookmarks', $results, $parsed_args ); } /** * Sanitizes all bookmark fields. * * @since 2.3.0 * * @param stdClass|array $bookmark Bookmark row. * @param string $context Optional. How to filter the fields. Default 'display'. * @return stdClass|array Same type as $bookmark but with fields sanitized. */ function sanitize_bookmark( $bookmark, $context = 'display' ) { $fields = array( 'link_id', 'link_url', 'link_name', 'link_image', 'link_target', 'link_category', 'link_description', 'link_visible', 'link_owner', 'link_rating', 'link_updated', 'link_rel', 'link_notes', 'link_rss', ); if ( is_object( $bookmark ) ) { $do_object = true; $link_id = $bookmark->link_id; } else { $do_object = false; $link_id = $bookmark['link_id']; } foreach ( $fields as $field ) { if ( $do_object ) { if ( isset( $bookmark->$field ) ) { $bookmark->$field = sanitize_bookmark_field( $field, $bookmark->$field, $link_id, $context ); } } else { if ( isset( $bookmark[ $field ] ) ) { $bookmark[ $field ] = sanitize_bookmark_field( $field, $bookmark[ $field ], $link_id, $context ); } } } return $bookmark; } /** * Sanitizes a bookmark field. * * Sanitizes the bookmark fields based on what the field name is. If the field * has a strict value set, then it will be tested for that, else a more generic * filtering is applied. After the more strict filter is applied, if the `$context` * is 'raw' then the value is immediately return. * * Hooks exist for the more generic cases. With the 'edit' context, the {@see 'edit_$field'} * filter will be called and passed the `$value` and `$bookmark_id` respectively. * * With the 'db' context, the {@see 'pre_$field'} filter is called and passed the value. * The 'display' context is the final context and has the `$field` has the filter name * and is passed the `$value`, `$bookmark_id`, and `$context`, respectively. * * @since 2.3.0 * * @param string $field The bookmark field. * @param mixed $value The bookmark field value. * @param int $bookmark_id Bookmark ID. * @param string $context How to filter the field value. Accepts 'raw', 'edit', 'attribute', * 'js', 'db', or 'display' * @return mixed The filtered value. */ function sanitize_bookmark_field( $field, $value, $bookmark_id, $context ) { switch ( $field ) { case 'link_id': // ints case 'link_rating': $value = (int) $value; break; case 'link_category': // array( ints ) $value = array_map( 'absint', (array) $value ); // We return here so that the categories aren't filtered. // The 'link_category' filter is for the name of a link category, not an array of a link's link categories. return $value; case 'link_visible': // bool stored as Y|N $value = preg_replace( '/[^YNyn]/', '', $value ); break; case 'link_target': // "enum" $targets = array( '_top', '_blank' ); if ( ! in_array( $value, $targets, true ) ) { $value = ''; } break; } if ( 'raw' === $context ) { return $value; } if ( 'edit' === $context ) { /** This filter is documented in wp-includes/post.php */ $value = apply_filters( "edit_{$field}", $value, $bookmark_id ); if ( 'link_notes' === $field ) { $value = esc_html( $value ); // textarea_escaped } else { $value = esc_attr( $value ); } } elseif ( 'db' === $context ) { /** This filter is documented in wp-includes/post.php */ $value = apply_filters( "pre_{$field}", $value ); } else { /** This filter is documented in wp-includes/post.php */ $value = apply_filters( "{$field}", $value, $bookmark_id, $context ); if ( 'attribute' === $context ) { $value = esc_attr( $value ); } elseif ( 'js' === $context ) { $value = esc_js( $value ); } } return $value; } /** * Deletes the bookmark cache. * * @since 2.7.0 * * @param int $bookmark_id Bookmark ID. */ function clean_bookmark_cache( $bookmark_id ) { wp_cache_delete( $bookmark_id, 'bookmark' ); wp_cache_delete( 'get_bookmarks', 'bookmark' ); clean_object_term_cache( $bookmark_id, 'link' ); } do_action(self::HOOK_FIRST_LOGIN_AFTER_INSTALL, self::getMigrationData()); } } /** * @return void */ public static function wpAdminHook() { if (!ToolsPageController::isToolPage()) { wp_safe_redirect( ControllersManager::getMenuLink( ControllersManager::TOOLS_SUBMENU_SLUG, null, null, array(), false ) ); exit; } } /** * check if is first login after install option is set * * @return boolean */ public static function isFirstLoginAfterInstallOption() { return get_option(self::FIRST_LOGIN_OPTION, false); } /** * Check if is first login after install * * @return boolean */ private static function isFirstLoginAfterInstall() { if (is_user_logged_in() && self::isFirstLoginAfterInstallOption()) { if (self::getMigrationData()->installType === InstState::TYPE_STANDALONE) { CapMng::getInstance()->reset(); return true; } else { CapMng::getInstance()->migrationUpdate(); return CapMng::can(CapMng::CAP_BASIC, false); } } else { return false; } } /** * Purge all caches * * @return string[] // messages */ public static function purgeCaches() { if ( self::getMigrationData()->restoreBackupMode || in_array( self::getMigrationData()->installType, [ InstState::TYPE_SINGLE_ON_SUBDOMAIN, InstState::TYPE_SINGLE_ON_SUBFOLDER, InstState::TYPE_SUBSITE_ON_SUBDOMAIN, InstState::TYPE_SUBSITE_ON_SUBFOLDER, ] ) ) { return array(); } return CachesPurge::purgeAll(); } /** * Update new install info * * @param MigrateData $migrationData migration data * * @return void */ public static function updateNewInstallInfo(MigrateData $migrationData) { if ($migrationData->restoreBackupMode || $migrationData->recoveryMode) { return; } // After migration is considered a new install UpgradePlugin::setInstallInfo(); } /** * Clean after install * * @param MigrateData $migrationData migration data * * @return void */ public static function usageStatistics(MigrateData $migrationData) { PluginData::getInstance()->updateFromMigrateData($migrationData); CommStats::installerSend(); } /** * Clean after install * * @param MigrateData $migrationData migration data * * @return void */ public static function autoCleanFileAfterInstall(MigrateData $migrationData) { if ($migrationData->cleanInstallerFiles == false) { return; } wp_safe_redirect(ToolsPageController::getInstance()->getCleanFilesAcrtionUrl(false)); exit; } /** * Update settings after install * * @param MigrateData $migrationData migration data * * @return void */ public static function updateSettings(MigrateData $migrationData) { $isAddSiteOnMultisite = in_array( self::getMigrationData()->installType, [ InstState::TYPE_SINGLE_ON_SUBDOMAIN, InstState::TYPE_SINGLE_ON_SUBFOLDER, InstState::TYPE_SUBSITE_ON_SUBDOMAIN, InstState::TYPE_SUBSITE_ON_SUBFOLDER, ] ); if ( !$migrationData->restoreBackupMode && !$isAddSiteOnMultisite ) { DUP_PRO_Global_Entity::getInstance()->updateAftreInstall(); } if (!$isAddSiteOnMultisite) { // remove point in database but not files. RecoveryPackage::resetRecoverPackage(); } flush_rewrite_rules(true); } /** * Update capabilities after install * * @param MigrateData $migrationData migration data * * @return void */ public static function updateCapabilities(MigrateData $migrationData) { CapMng::getInstance()->migrationUpdate(); } /** * Return cleanup report * * @return array{removed: string[], stored: string[], instFile: string[]} */ public static function getCleanupReport() { $option = get_option(self::CLEAN_INSTALL_REPORT_OPTION); if (is_array($option)) { self::$migrationCleanupReport = array_merge(self::$migrationCleanupReport, $option); } return self::$migrationCleanupReport; } /** * Save clean up report in WordPress options * * @param MigrateData $migrationData migration data * * @return void */ public static function saveCleanupReport(MigrateData $migrationData) { add_option(self::CLEAN_INSTALL_REPORT_OPTION, self::$migrationCleanupReport, '', false); } /** * Remove first login after install option * * @param MigrateData $migrationData migration data * * @return void */ public static function removeFirstLoginOption(MigrateData $migrationData) { delete_option(self::FIRST_LOGIN_OPTION); } /** * Return migration data, * if the site isn't migrated $data->installType = InstState::TYPE_NOT_SET * * @return MigrateData */ public static function getMigrationData() { static $migrationData = null; if (is_null($migrationData)) { if (($migrationData = get_option(self::MIGRATION_DATA_OPTION, false)) === false) { $migrationData = new MigrateData(); } else { $migrationData = JsonSerialize::unserializeToObj($migrationData, MigrateData::class); } } return $migrationData; } /** * Safe mode warning * * @return string */ public static function getSaveModeWarning() { switch (self::getMigrationData()->safeMode) { case 1: //safe_mode basic return __('NOTICE: Safe mode (Basic) was enabled during install, be sure to re-enable all your plugins.', 'duplicator-pro'); case 2: //safe_mode advance return __('NOTICE: Safe mode (Advanced) was enabled during install, be sure to re-enable all your plugins.', 'duplicator-pro'); case 0: default: return ''; } } /** * Check the root path and in case there are installer files without hashes rename them. * * @param int $fileTimeDelay If the file is younger than $fileTimeDelay seconds then it is not renamed. * * @return void */ public static function renameInstallersPhpFiles($fileTimeDelay = 0) { $fileTimeDelay = is_numeric($fileTimeDelay) ? (int) $fileTimeDelay : 0; $pathsTocheck = array( SnapIO::safePathTrailingslashit(ABSPATH), SnapIO::safePathTrailingslashit(SnapWP::getHomePath()), SnapIO::safePathTrailingslashit(WP_CONTENT_DIR), ); $migrationData = self::getMigrationData(); if (strlen($migrationData->installerPath)) { $pathsTocheck[] = SnapIO::safePathTrailingslashit(dirname($migrationData->installerPath)); } if (strlen($migrationData->dupInstallerPath)) { $pathsTocheck[] = SnapIO::safePathTrailingslashit(dirname($migrationData->dupInstallerPath)); } $pathsTocheck = array_unique($pathsTocheck); $filesToCheck = array(); foreach ($pathsTocheck as $cFolder) { if ( !is_dir($cFolder) || !is_writable($cFolder) // rename permissions ) { continue; } $cFile = $cFolder . 'installer.php'; if ( !is_file($cFile) || !SnapIO::chmod($cFile, 'u+rw') || !is_readable($cFile) ) { continue; } $filesToCheck[] = $cFile; } $installerTplCheck = '/const\s+ARCHIVE_FILENAME\s*=\s*[\'"](.+?)[\'"]\s*;.*const\s+PACKAGE_HASH\s*=\s*[\'"](.+?)[\'"]\s*;/s'; foreach ($filesToCheck as $file) { $fileName = basename($file); if ($fileTimeDelay > 0 && (time() - filemtime($file)) < $fileTimeDelay) { continue; } if (($content = @file_get_contents($file, false, null)) === false) { continue; } $matches = null; if (preg_match($installerTplCheck, $content, $matches) !== 1) { continue; } $archiveName = $matches[1]; $hash = $matches[2]; $matches = null; if (preg_match(DUPLICATOR_PRO_ARCHIVE_REGEX_PATTERN, $archiveName, $matches) !== 1) { if (SnapIO::unlink($file)) { self::$migrationCleanupReport['instFile'][] = "
" . " " . sprintf(__('Installer file %s removed for security reasons', 'duplicator-pro'), esc_html($fileName)) . "
"; } else { self::$migrationCleanupReport['instFile'][] = "
" . ' ' . sprintf(__('Can\'t remove installer file %s, please remove it for security reasons', 'duplicator-pro'), esc_html($fileName)) . '
'; } continue; } $archiveHash = $matches[1]; if (strpos($file, $archiveHash) === false) { if (SnapIO::rename($file, dirname($file) . '/' . $archiveHash . '_installer.php', true)) { self::$migrationCleanupReport['instFile'][] = "
" . " " . sprintf(__('Installer file %s renamed with HASH', 'duplicator-pro'), esc_html($fileName)) . "
"; } else { self::$migrationCleanupReport['instFile'][] = "
" . ' ' . sprintf( __('Can\'t rename installer file %s with HASH, please remove it for security reasons', 'duplicator-pro'), esc_html($fileName) ) . '
'; } } } } /** * Store migratuion files * * @param MigrateData $migrationData Migration data * * @return void */ public static function storeMigrationFiles(MigrateData $migrationData) { wp_mkdir_p(DUPLICATOR_PRO_SSDIR_PATH_INSTALLER); SnapIO::emptyDir(DUPLICATOR_PRO_SSDIR_PATH_INSTALLER); SnapIO::createSilenceIndex(DUPLICATOR_PRO_SSDIR_PATH_INSTALLER); $filesToMove = array( $migrationData->installerLog, $migrationData->installerBootLog, $migrationData->origFileFolderPath, ); foreach ($filesToMove as $path) { if (file_exists($path)) { if (SnapIO::rcopy($path, DUPLICATOR_PRO_SSDIR_PATH_INSTALLER . '/' . basename($path))) { self::$migrationCleanupReport['stored'][] = "
" . " " . __('Original files folder moved in installer backup directory', 'duplicator-pro') . " - " . esc_html($path) . "
"; } else { self::$migrationCleanupReport['stored'][] = "
" . ' ' . sprintf(__('Can\'t move %1$s to %2$s', 'duplicator-pro'), esc_html($path), DUPLICATOR_PRO_SSDIR_PATH_INSTALLER) . '
'; } } } } /** * Get file list to store * * @return array */ public static function getStoredMigrationLists() { if (($migrationData = self::getMigrationData()) == false) { $filesToCheck = array(); } else { $filesToCheck = array( $migrationData->installerLog => __('Installer log', 'duplicator-pro'), $migrationData->installerBootLog => __('Installer boot log', 'duplicator-pro'), $migrationData->origFileFolderPath => __('Original files folder', 'duplicator-pro'), ); } $result = array(); foreach ($filesToCheck as $path => $label) { $storedPath = DUPLICATOR_PRO_SSDIR_PATH_INSTALLER . '/' . basename($path); if (!file_exists($storedPath)) { continue; } $result[$storedPath] = $label; } return $result; } /** * Check if exist file to remove * * @return bool */ public static function haveFileToClean() { return count(self::checkInstallerFilesList()) > 0; } /** * Gets a list of all the installer files and directory by name and full path * * @remarks * FILES: installer.php, installer-backup.php, dup-installer-bootlog__[HASH].txt * DIRS: dup-installer * Last set is for lazy developer cleanup files that a developer may have * accidentally left around lets be proactive for the user just in case. * * @return string[] file names */ public static function getGenericInstallerFiles() { return array( 'installer.php', '[HASH]installer-backup.php', 'dup-installer', 'dup-installer[HASH]', 'dup-installer-bootlog__[HASH].txt', '[HASH]_archive.zip|daf', ); } /** * Get installer files list * * @return string[] */ public static function checkInstallerFilesList() { $migrationData = self::getMigrationData(); $foldersToChkeck = array( SnapIO::safePathTrailingslashit(ABSPATH), SnapWP::getHomePath(), ); $result = array(); if (!empty($migrationData)) { if ( file_exists($migrationData->archivePath) && !DUP_PRO_Archive::isBackupPathChild($migrationData->archivePath) && !PackageImporter::isImportPath($migrationData->archivePath) && !RecoveryPackage::isRecoverPath($migrationData->archivePath) && !StoragesUtil::isLocalStorageChildPath($migrationData->archivePath) ) { $result[] = $migrationData->archivePath; } if ( file_exists($migrationData->installerPath) && self::isInstallerFile($migrationData->installerPath) && !DUP_PRO_Archive::isBackupPathChild($migrationData->installerPath) && !RecoveryPackage::isRecoverPath($migrationData->installerPath) && !StoragesUtil::isLocalStorageChildPath($migrationData->installerPath) ) { $result[] = $migrationData->installerPath; } if (file_exists($migrationData->installerBootLog)) { $result[] = $migrationData->installerBootLog; } if (file_exists($migrationData->dupInstallerPath)) { $result[] = $migrationData->dupInstallerPath; } } foreach ($foldersToChkeck as $folder) { $result = array_merge($result, SnapIO::regexGlob($folder, array( 'regexFile' => array( DUPLICATOR_PRO_ARCHIVE_REGEX_PATTERN, DUPLICATOR_PRO_INSTALLER_REGEX_PATTERN, DUPLICATOR_PRO_DUP_INSTALLER_BOOTLOG_REGEX_PATTERN, DUPLICATOR_PRO_DUP_INSTALLER_OWRPARAM_REGEX_PATTERN, ), 'regexFolder' => array(DUPLICATOR_PRO_DUP_INSTALLER_FOLDER_REGEX_PATTERN), ))); } $result = array_map(array('\\Duplicator\\Libs\\Snap\\SnapIO', 'safePathUntrailingslashit'), $result); return array_unique($result); } /** * Check if file is installer file * * @param string $path path to check * * @return bool true if the file at current path is the installer file */ public static function isInstallerFile($path) { if (!is_file($path) || !is_array($last5Lines = SnapIO::getLastLinesOfFile($path, 5)) || empty($last5Lines)) { return false; } return strpos(implode("", $last5Lines), "DUPLICATOR_PRO_INSTALLER_EOF") !== false; } /** * Clean installer files * * @param bool $deleteCleanInstallReportOption Delete clean install report option * @param int $fileTimeDelay File time delay * * @return array Clean result */ public static function cleanMigrationFiles($deleteCleanInstallReportOption = true, $fileTimeDelay = 0) { $cleanList = self::checkInstallerFilesList(); $result = array(); foreach ($cleanList as $path) { $success = false; try { if ($fileTimeDelay <= 0 || time() - filectime($path) > $fileTimeDelay) { $success = (SnapIO::rrmdir($path) !== false); } else { // The file does not even need to be removed yet $success = true; } } catch (Exception $ex) { $success = false; } catch (Error $ex) { $success = false; } $result[$path] = $success; } if ($deleteCleanInstallReportOption) { delete_option(self::CLEAN_INSTALL_REPORT_OPTION); } return $result; } /** * Recalculate active schedules next run time * * @return void */ public static function updateNextSchedules() { $schedules = DUP_PRO_Schedule_Entity::get_active(); foreach ($schedules as $schedule) { $schedule->updateNextRuntime(); } } }