<?php
/*
 * Plugin Name: Infinite Uploads
 * Description: Infinitely scalable cloud storage and delivery for your videos and uploads made easy! Upload directly to cloud storage and manage your files right from the WordPress Media Library.
 * Version: 3.2.5
 * Author: Infinite Uploads
 * Author URI: https://infiniteuploads.com/?utm_source=iup_plugin&utm_medium=plugin&utm_campaign=iup_plugin&utm_content=meta
 * Text Domain: infinite-uploads
 * Requires at least: 6.0
 * Tested up to: 7.0
 * Requires PHP: 8.0
 * License: GPLv2
 * License URI: https://www.gnu.org/licenses/gpl-2.0.html
 * Network: true
 *
 * Inspired by AWS PHP SDK stream wrapper code from the S3 Uploads plugin by Human Made https://github.com/humanmade/S3-Uploads.
 *
 * Copyright 2021-2025 ClikIT, LLC
*/

define( 'INFINITE_UPLOADS_VERSION', '3.2.5' );

if ( file_exists( __DIR__ . '/vendor/autoload.php' ) ) {
    require_once __DIR__ . '/vendor/autoload.php';
}

// Load action scheduler.
require_once dirname( __FILE__ ) . '/libs/action-scheduler/action-scheduler.php';

if ( defined( 'WP_CLI' ) && WP_CLI ) {
    \WP_CLI::add_command( 'infinite-uploads', '\ClikIT\InfiniteUploads\InfiniteUploadsWPCLICommand' );
}

//require_once 'inc/class-infinite-uploads-wp-mail.php';
register_activation_hook( __FILE__, 'infinite_uploads_install' );

add_action( 'plugins_loaded', 'infinite_uploads_init' );

function infinite_uploads_init() {
	//how much to try uploading/downloading per ajax loop (we want as much as possible without exceeding (php timeout - ajax_timeout) to avoid 504s
	if ( ! defined( 'INFINITE_UPLOADS_SYNC_MAX_BYTES' ) ) {
		define( 'INFINITE_UPLOADS_SYNC_MAX_BYTES', MB_IN_BYTES * 12 );
	}
	//This is the maximum to transfer in parallel within the INFINITE_UPLOADS_SYNC_MAX_BYTES size.
	if ( ! defined( 'INFINITE_UPLOADS_SYNC_CONCURRENCY' ) ) {
		define( 'INFINITE_UPLOADS_SYNC_CONCURRENCY', 15 );
	}
	if ( ! defined( 'INFINITE_UPLOADS_SYNC_MULTIPART_CONCURRENCY' ) ) {
		define( 'INFINITE_UPLOADS_SYNC_MULTIPART_CONCURRENCY', 5 );
	}
	if ( ! defined( 'INFINITE_UPLOADS_SYNC_PER_LOOP' ) ) {
		define( 'INFINITE_UPLOADS_SYNC_PER_LOOP', 1000 );
	}
	if ( ! defined( 'INFINITE_UPLOADS_HTTP_CACHE_CONTROL' ) ) {
		define( 'INFINITE_UPLOADS_HTTP_CACHE_CONTROL', YEAR_IN_SECONDS );
	}
	//we cache the last object uploaded to cloud in memory so it can be processed without downloading again.
	if ( ! defined( 'INFINITE_UPLOADS_STREAM_CACHE_MAX_BYTES' ) ) {
		define( 'INFINITE_UPLOADS_STREAM_CACHE_MAX_BYTES', MB_IN_BYTES * 32 );
	}

	// Require Our custom AWS Autoloader file.

	require_once dirname( __FILE__ ) . '/vendor/Aws3/aws-autoloader.php';

	if ( ! infinite_uploads_check_requirements() ) {
		return;
	}

	infinite_uploads_upgrade();

    $instance = \ClikIT\InfiniteUploads\InfiniteUploads::get_instance();

    require_once dirname( __FILE__ ) . '/functions.php';

	$instance->setup();
}

function infinite_uploads_upgrade() {

	// Network-wide table (infinite_uploads_files) — one option gates the whole network.
	$installed = get_site_option( 'iup_installed' );
	if ( INFINITE_UPLOADS_VERSION != $installed ) {
		infinite_uploads_install();
	}

	// Per-site folder tables — each subsite tracks its own version independently.
	$folders_installed = get_option( 'iup_folders_installed' );
	if ( INFINITE_UPLOADS_VERSION != $folders_installed ) {
		infinite_uploads_install_folders();
	}

	// One-time backfill: prior versions excluded /bb-plugin/cache/ entirely, so any
	// images BB had already cropped before this release were never added to
	// infinite_uploads_files. Schedule an async walk + queue so the existing
	// iu_bb_cache_push handler can offload them. Idempotent — flag is set when done.
	//
	// is_main_site() guard: the flag is network-wide (get_site_option), but
	// `infinite_uploads_upgrade()` runs on every plugins_loaded across every
	// subsite. Without this guard, multiple subsite requests can each pass the
	// `wp_next_scheduled` check before any of them actually schedule, racing to
	// queue duplicate iu_bb_carveout_backfill events. Restricting to the main
	// site collapses the race to a single scheduler.
	if ( is_main_site() && ! get_site_option( 'iup_bb_carveout_backfilled' ) && ! wp_next_scheduled( 'iu_bb_carveout_backfill' ) ) {
		wp_schedule_single_event( time() + 60, 'iu_bb_carveout_backfill' );
	}
}

function infinite_uploads_install() {
	global $wpdb;

	//prevent race condition during upgrade by setting option before running potentially long query
	if ( is_multisite() ) {
		update_site_option( 'iup_installed', INFINITE_UPLOADS_VERSION );
	} else {
		update_option( 'iup_installed', INFINITE_UPLOADS_VERSION, true );
	}

	$charset_collate = $wpdb->get_charset_collate();

	//191 is the maximum innodb default key length on utf8mb4
	$sql = "CREATE TABLE {$wpdb->base_prefix}infinite_uploads_files (
            `file` VARCHAR(255) NOT NULL,
            `size` BIGINT UNSIGNED NOT NULL DEFAULT '0',
            `modified` INT UNSIGNED NOT NULL,
            `type` VARCHAR(20) NOT NULL,
            `transferred` BIGINT UNSIGNED NOT NULL DEFAULT '0',
            `synced` BOOLEAN NOT NULL DEFAULT '0',
            `deleted` BOOLEAN NOT NULL DEFAULT '0',
            `errors` INT UNSIGNED NOT NULL DEFAULT '0',
            `transfer_status` TEXT NULL DEFAULT NULL,
            PRIMARY KEY  (`file`(191)),
            KEY `type` (`type`),
            KEY `synced` (`synced`),
            KEY `deleted` (`deleted`)
        ) {$charset_collate};";

	if ( ! function_exists( 'dbDelta' ) ) {
		require_once ABSPATH . 'wp-admin/includes/upgrade.php';
	}

	dbDelta( $sql );

	// Create per-site folder tables for the current site.
	infinite_uploads_install_folders();
}

/**
 * Create (or upgrade) the per-site media-folder tables for the current subsite.
 *
 * Uses $wpdb->prefix so each subsite gets its own tables, keeping folder
 * assignments isolated to the site that owns the attachment posts.
 * Gated by a per-site option so it runs independently on every subsite.
 */
function infinite_uploads_install_folders() {
	global $wpdb;

	// Prevent race conditions by marking done before the potentially slow dbDelta.
	update_option( 'iup_folders_installed', INFINITE_UPLOADS_VERSION, true );

	$charset_collate = $wpdb->get_charset_collate();

	// Media folders table.
	$sql = "CREATE TABLE {$wpdb->prefix}infinite_uploads_media_folders (
            id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
            name VARCHAR(255) NOT NULL,
            parent_id BIGINT UNSIGNED NOT NULL DEFAULT 0,
            sort_order INT UNSIGNED NOT NULL DEFAULT 0,
            created_by BIGINT UNSIGNED NOT NULL DEFAULT 0,
            created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
            updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
            color VARCHAR(20) NOT NULL DEFAULT '',
            PRIMARY KEY  (id),
            KEY parent_id (parent_id),
            KEY sort_order (sort_order)
        ) {$charset_collate};";

	// Media-to-folder relationship table.
	$sql .= "\nCREATE TABLE {$wpdb->prefix}infinite_uploads_media_folder_relationships (
            folder_id BIGINT UNSIGNED NOT NULL,
            attachment_id BIGINT UNSIGNED NOT NULL,
            PRIMARY KEY  (folder_id, attachment_id),
            KEY attachment_id (attachment_id)
        ) {$charset_collate};";

	if ( ! function_exists( 'dbDelta' ) ) {
		require_once ABSPATH . 'wp-admin/includes/upgrade.php';
	}

	dbDelta( $sql );
}

// Create folder tables for each new subsite when it is provisioned.
add_action( 'wp_initialize_site', function ( $new_site ) {
	switch_to_blog( $new_site->blog_id );
	infinite_uploads_install_folders();
	restore_current_blog();
} );

/**
 * Check whether the environment meets the plugin's requirements, like the minimum PHP version.
 *
 * @return bool True if the requirements are met, else false.
 */
function infinite_uploads_check_requirements() {
	global $wp_version;
	$hook = is_multisite() ? 'network_admin_notices' : 'admin_notices';

	if ( version_compare( PHP_VERSION, '5.5.0', '<' ) ) {
		add_action( $hook, 'infinite_uploads_outdated_php_version_notice' );

		return false;
	}

	if ( version_compare( $wp_version, '5.3.0', '<' ) ) {
		add_action( $hook, 'infinite_uploads_outdated_wp_version_notice' );

		return false;
	}

	return true;
}

/**
 * Print an admin notice when the PHP version is not high enough.
 *
 * This has to be a named function for compatibility with PHP 5.2.
 */
function infinite_uploads_outdated_php_version_notice() {
	?>
	<div class="notice notice-warning is-dismissible"><p>
			<?php printf( esc_html__( 'The Infinite Uploads plugin requires PHP version 5.5.0 or higher. Your server is running PHP version %s.', 'infinite-uploads' ), PHP_VERSION ); ?>
		</p></div>
	<?php
}

/**
 * Print an admin notice when the WP version is not high enough.
 *
 * This has to be a named function for compatibility with PHP 5.2.
 */
function infinite_uploads_outdated_wp_version_notice() {
	global $wp_version;
	?>
	<div class="notice notice-warning is-dismissible"><p>
			<?php printf( esc_html__( 'The Infinite Uploads plugin requires WordPress version 5.3 or higher. Your server is running WordPress version %s.', 'infinite-uploads' ), $wp_version ); ?>
		</p></div>
	<?php
}

/**
 * Check if URL rewriting is enabled.
 *
 * @return bool
 */
function infinite_uploads_enabled() {
	return get_site_option( 'iup_enabled' );
}

/**
 * Autoload callback.
 *
 * @param $class_name string Name of the class to load.
 */
function infinite_uploads_autoload( $class_name ) {
	/*
	 * Load plugin classes:
	 * - Class name: Infinite_Uploads_Image_Editor_Imagick.
	 * - File name: class-infinite-uploads-image-editor-imagick.php.
	 */
	$class_file = 'class-' . strtolower( str_replace( '_', '-', $class_name ) ) . '.php';
	$class_path = dirname( __FILE__ ) . '/inc/' . $class_file;

	if ( file_exists( $class_path ) ) {
		require $class_path;

		return;
	}
}

// spl_autoload_register( 'infinite_uploads_autoload' );
