403Webshell
Server IP : 158.247.231.215  /  Your IP : 216.73.217.84
Web Server : Apache/2.4.41 (Ubuntu)
System : Linux CTMS 5.4.0-216-generic #236-Ubuntu SMP Fri Apr 11 19:53:21 UTC 2025 x86_64
User : www-data ( 33)
PHP Version : 8.0.30
Disable Function : pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare,
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : OFF  |  Sudo : ON  |  Pkexec : ON
Directory :  /mnt/blockstorage/ctms/wp-content/mu-plugins/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /mnt/blockstorage/ctms/wp-content/mu-plugins/custom-course-stats-api.php
<?php
/**
 * Plugin Name: Custom Course Statistics API
 * Description: Adds REST API endpoint for user course statistics
 * Version: 1.0.0
 * Author: Custom Development
 */

if (!defined('ABSPATH')) {
    exit;
}

/**
 * Register REST API routes
 */
add_action('rest_api_init', function() {
    register_rest_route('custom/v1', '/user/(?P<user_id>\d+)/course-stats', array(
        'methods' => 'GET',
        'callback' => 'custom_get_user_course_stats',
        'permission_callback' => '__return_true',
        'args' => array(
            'user_id' => array(
                'required' => true,
                'validate_callback' => function($param, $request, $key) {
                    return is_numeric($param);
                }
            ),
        ),
    ));

    register_rest_route('custom/v1', '/register', array(
        'methods' => 'POST',
        'callback' => 'custom_register_user',
        'permission_callback' => '__return_true',
    ));

    register_rest_route('custom/v1', '/me', array(
        'methods' => 'GET',
        'callback' => 'custom_get_current_user',
        'permission_callback' => 'custom_verify_jwt_token',
    ));

    register_rest_route('custom/v1', '/password/change', array(
        'methods' => 'POST',
        'callback' => 'custom_change_password',
        'permission_callback' => 'custom_verify_jwt_token',
    ));

    register_rest_route('custom/v1', '/me/membership', array(
        'methods' => 'GET',
        'callback' => 'custom_get_user_membership',
        'permission_callback' => 'custom_verify_jwt_token',
    ));

    register_rest_route('custom/v1', '/me/invoices', array(
        'methods' => 'GET',
        'callback' => 'custom_get_user_invoices',
        'permission_callback' => 'custom_verify_jwt_token',
    ));

    register_rest_route('custom/v1', '/me/social-profiles', array(
        'methods' => 'GET',
        'callback' => 'custom_get_social_profiles',
        'permission_callback' => 'custom_verify_jwt_token',
    ));

    register_rest_route('custom/v1', '/me/social-profiles', array(
        'methods' => 'POST',
        'callback' => 'custom_update_social_profiles',
        'permission_callback' => 'custom_verify_jwt_token',
    ));

    register_rest_route('custom/v1', '/me/quiz-attempts', array(
        'methods' => 'GET',
        'callback' => 'custom_get_user_quiz_attempts',
        'permission_callback' => 'custom_verify_jwt_token',
    ));
});

/**
 * Get user course statistics
 */
function custom_get_user_course_stats($request) {
    $user_id = $request->get_param('user_id');

    $user = get_user_by('ID', $user_id);
    if (!$user) {
        return new WP_REST_Response(array(
            'success' => false,
            'message' => 'User not found',
        ), 404);
    }

    $enrolled_count = custom_get_enrolled_courses_count($user_id);
    $active_count = custom_get_active_courses_count($user_id);
    $completed_count = custom_get_completed_courses_count($user_id);

    return new WP_REST_Response(array(
        'success' => true,
        'user_id' => $user_id,
        'user_name' => $user->display_name,
        'statistics' => array(
            'enrolled_courses' => $enrolled_count,
            'active_courses' => $active_count,
            'completed_courses' => $completed_count,
        ),
    ), 200);
}

/**
 * Get enrolled courses count
 */
function custom_get_enrolled_courses_count($user_id) {
    global $wpdb;
    $count = $wpdb->get_var($wpdb->prepare(
        "SELECT COUNT(DISTINCT class_id) FROM {$wpdb->prefix}class_enrollment WHERE student_id = %d",
        $user_id
    ));
    return (int) $count;
}

/**
 * Get active courses count
 */
function custom_get_active_courses_count($user_id) {
    if (function_exists('tutor_utils')) {
        $active_courses = tutor_utils()->get_active_courses_by_user($user_id);
        if (is_object($active_courses) && $active_courses->have_posts()) {
            return $active_courses->post_count;
        }
    }

    global $wpdb;
    $enrolled_ids = $wpdb->get_col($wpdb->prepare(
        "SELECT DISTINCT class_id FROM {$wpdb->prefix}class_enrollment WHERE student_id = %d",
        $user_id
    ));

    if (empty($enrolled_ids)) {
        return 0;
    }

    $completed_ids = custom_get_completed_course_ids($user_id);
    $active_ids = array_diff($enrolled_ids, $completed_ids);
    return count($active_ids);
}

/**
 * Get completed courses count
 */
function custom_get_completed_courses_count($user_id) {
    if (function_exists('tutor_utils')) {
        $completed_courses = tutor_utils()->get_completed_courses_ids_by_user($user_id);
        return count($completed_courses);
    }
    $completed_ids = custom_get_completed_course_ids($user_id);
    return count($completed_ids);
}

/**
 * Get completed course IDs
 */
function custom_get_completed_course_ids($user_id) {
    global $wpdb;
    $completed_meta = $wpdb->get_results($wpdb->prepare(
        "SELECT meta_key, meta_value FROM {$wpdb->prefix}usermeta WHERE user_id = %d AND (meta_key LIKE '_tutor_course_completed_%%' OR meta_key LIKE '%%course_completed%%')",
        $user_id
    ));

    $completed_ids = array();
    foreach ($completed_meta as $meta) {
        if (strpos($meta->meta_key, '_tutor_course_completed_') === 0) {
            $course_id = str_replace('_tutor_course_completed_', '', $meta->meta_key);
            if (is_numeric($course_id)) {
                $completed_ids[] = (int) $course_id;
            }
        }
    }

    if (function_exists('tutor_utils')) {
        $enrolled_ids = $wpdb->get_col($wpdb->prepare(
            "SELECT DISTINCT class_id FROM {$wpdb->prefix}class_enrollment WHERE student_id = %d",
            $user_id
        ));

        foreach ($enrolled_ids as $course_id) {
            $course_progress = tutor_utils()->get_course_completed_percent($course_id, $user_id);
            if (isset($course_progress['completed_percent']) && $course_progress['completed_percent'] >= 100) {
                if (!in_array($course_id, $completed_ids)) {
                    $completed_ids[] = (int) $course_id;
                }
            }
        }
    }

    return array_unique($completed_ids);
}
/**
 * Register new user
 */
function custom_register_user($request) {
    $username = sanitize_user($request->get_param('username'));
    $email = sanitize_email($request->get_param('email'));
    $password = $request->get_param('password');

    if (empty($username) || empty($email) || empty($password)) {
        return new WP_REST_Response(array(
            'success' => false,
            'message' => 'Username, email, and password are required',
        ), 400);
    }

    if (!is_email($email)) {
        return new WP_REST_Response(array(
            'success' => false,
            'message' => 'Invalid email address',
        ), 400);
    }

    if (username_exists($username)) {
        return new WP_REST_Response(array(
            'success' => false,
            'message' => 'Username already exists',
        ), 400);
    }

    if (email_exists($email)) {
        return new WP_REST_Response(array(
            'success' => false,
            'message' => 'Email already exists',
        ), 400);
    }

    if (strlen($password) < 6) {
        return new WP_REST_Response(array(
            'success' => false,
            'message' => 'Password must be at least 6 characters',
        ), 400);
    }

    $user_id = wp_create_user($username, $password, $email);

    if (is_wp_error($user_id)) {
        return new WP_REST_Response(array(
            'success' => false,
            'message' => $user_id->get_error_message(),
        ), 500);
    }

    $user = get_user_by('id', $user_id);
    wp_set_current_user($user_id);

    $token_request = new WP_REST_Request('POST', '/jwt-auth/v1/token');
    $token_request->set_param('username', $username);
    $token_request->set_param('password', $password);
    $token_response = rest_do_request($token_request);

    if ($token_response->is_error()) {
        return new WP_REST_Response(array(
            'success' => true,
            'message' => 'User created successfully',
            'user_id' => $user_id,
            'username' => $username,
            'email' => $email,
        ), 201);
    }

    $token_data = $token_response->get_data();

    return new WP_REST_Response(array(
        'success' => true,
        'message' => 'User created and logged in successfully',
        'user_id' => $user_id,
        'username' => $username,
        'email' => $email,
        'token' => isset($token_data['token']) ? $token_data['token'] : null,
    ), 201);
}
/**
 * Verify JWT token for authentication
 */
function custom_verify_jwt_token($request) {
    $auth_header = $request->get_header('authorization');
    
    if (!$auth_header) {
        return new WP_Error('jwt_auth_no_auth_header', 'Authorization header not found.', array('status' => 401));
    }

    list($token) = sscanf($auth_header, 'Bearer %s');

    if (!$token) {
        return new WP_Error('jwt_auth_bad_auth_header', 'Authorization header malformed.', array('status' => 401));
    }

    $secret_key = defined('JWT_AUTH_SECRET_KEY') ? JWT_AUTH_SECRET_KEY : false;

    if (!$secret_key) {
        return new WP_Error('jwt_auth_bad_config', 'JWT is not configured properly.', array('status' => 500));
    }

    try {
        $token_parts = explode('.', $token);
        if (count($token_parts) !== 3) {
            return new WP_Error('jwt_auth_invalid_token', 'Invalid token format.', array('status' => 401));
        }

        list($header, $payload, $signature) = $token_parts;

        $valid_signature = hash_hmac('sha256', "$header.$payload", $secret_key, true);
        $valid_signature = rtrim(strtr(base64_encode($valid_signature), '+/', '-_'), '=');

        if ($signature !== $valid_signature) {
            return new WP_Error('jwt_auth_invalid_token', 'Token signature verification failed.', array('status' => 401));
        }

        $payload_data = json_decode(base64_decode(strtr($payload, '-_', '+/')), true);

        if (!$payload_data || !isset($payload_data['data']['user']['id'])) {
            return new WP_Error('jwt_auth_invalid_token', 'Invalid token payload.', array('status' => 401));
        }

        if (isset($payload_data['exp']) && $payload_data['exp'] < time()) {
            return new WP_Error('jwt_auth_token_expired', 'Token has expired.', array('status' => 401));
        }

        $user_id = $payload_data['data']['user']['id'];
        $user = get_user_by('id', $user_id);

        if (!$user) {
            return new WP_Error('jwt_auth_user_not_found', 'User not found.', array('status' => 404));
        }

        wp_set_current_user($user_id);
        return true;

    } catch (Exception $e) {
        return new WP_Error('jwt_auth_error', $e->getMessage(), array('status' => 401));
    }
}

/**
 * Get current authenticated user info
 */
function custom_get_current_user($request) {
    $user_id = get_current_user_id();

    if (!$user_id) {
        return new WP_REST_Response(array(
            'success' => false,
            'message' => 'User not authenticated',
        ), 401);
    }

    $user = get_user_by('id', $user_id);

    if (!$user) {
        return new WP_REST_Response(array(
            'success' => false,
            'message' => 'User not found',
        ), 404);
    }

    $user_meta = get_user_meta($user_id);

    return new WP_REST_Response(array(
        'success' => true,
        'id' => $user_id,
        'username' => $user->user_login,
        'email' => $user->user_email,
        'name' => $user->display_name,
        'first_name' => isset($user_meta['first_name'][0]) ? $user_meta['first_name'][0] : '',
        'last_name' => isset($user_meta['last_name'][0]) ? $user_meta['last_name'][0] : '',
        'nickname' => isset($user_meta['nickname'][0]) ? $user_meta['nickname'][0] : '',
        'description' => isset($user_meta['description'][0]) ? $user_meta['description'][0] : '',
        'avatar_url' => get_avatar_url($user_id),
        'roles' => $user->roles,
    ), 200);
}
/**
 * Change password for authenticated user
 */
function custom_change_password($request) {
    $user_id = get_current_user_id();

    if (!$user_id) {
        return new WP_REST_Response(array(
            'success' => false,
            'message' => 'User not authenticated',
        ), 401);
    }

    $current_password = $request->get_param('current_password');
    $new_password = $request->get_param('new_password');
    $confirm_password = $request->get_param('confirm_password');

    if (empty($current_password) || empty($new_password) || empty($confirm_password)) {
        return new WP_REST_Response(array(
            'success' => false,
            'message' => 'All password fields are required',
        ), 400);
    }

    if ($new_password !== $confirm_password) {
        return new WP_REST_Response(array(
            'success' => false,
            'message' => 'New password and confirm password do not match',
        ), 400);
    }

    if (strlen($new_password) < 6) {
        return new WP_REST_Response(array(
            'success' => false,
            'message' => 'New password must be at least 6 characters',
        ), 400);
    }

    $user = get_user_by('id', $user_id);

    if (!$user) {
        return new WP_REST_Response(array(
            'success' => false,
            'message' => 'User not found',
        ), 404);
    }

    if (!wp_check_password($current_password, $user->data->user_pass, $user_id)) {
        return new WP_REST_Response(array(
            'success' => false,
            'message' => 'Current password is incorrect',
        ), 400);
    }

    wp_set_password($new_password, $user_id);

    return new WP_REST_Response(array(
        'success' => true,
        'message' => 'Password changed successfully',
    ), 200);
}
/**
 * Get user membership information
 */
function custom_get_user_membership($request) {
    $user_id = get_current_user_id();

    if (!$user_id) {
        return new WP_REST_Response(array(
            'success' => false,
            'message' => 'User not authenticated',
        ), 401);
    }

    global $wpdb;

    $current_membership = $wpdb->get_row($wpdb->prepare(
        "SELECT 
            mu.membership_id,
            ml.name,
            ml.description,
            mu.initial_payment,
            mu.billing_amount,
            mu.cycle_number,
            mu.cycle_period,
            mu.billing_limit,
            mu.trial_amount,
            mu.trial_limit,
            mu.startdate,
            mu.enddate
        FROM {$wpdb->prefix}pmpro_memberships_users mu
        JOIN {$wpdb->prefix}pmpro_membership_levels ml ON mu.membership_id = ml.id
        WHERE mu.user_id = %d
        AND mu.status = 'active'
        ORDER BY mu.id DESC
        LIMIT 1",
        $user_id
    ));

    $membership_history = $wpdb->get_results($wpdb->prepare(
        "SELECT 
            mu.membership_id,
            ml.name,
            mu.startdate,
            mu.enddate,
            mu.status
        FROM {$wpdb->prefix}pmpro_memberships_users mu
        JOIN {$wpdb->prefix}pmpro_membership_levels ml ON mu.membership_id = ml.id
        WHERE mu.user_id = %d
        ORDER BY mu.startdate DESC",
        $user_id
    ));

    $currency = get_option('pmpro_currency', 'USD');

    return new WP_REST_Response(array(
        'success' => true,
        'current_membership' => $current_membership ? array(
            'id' => (int) $current_membership->membership_id,
            'name' => $current_membership->name,
            'description' => $current_membership->description,
            'start_date' => $current_membership->startdate,
            'end_date' => $current_membership->enddate === '0000-00-00 00:00:00' ? null : $current_membership->enddate,
            'initial_payment' => (float) $current_membership->initial_payment,
            'billing_amount' => (float) $current_membership->billing_amount,
            'cycle_number' => (int) $current_membership->cycle_number,
            'cycle_period' => $current_membership->cycle_period,
            'billing_limit' => (int) $current_membership->billing_limit,
            'trial_amount' => (float) $current_membership->trial_amount,
            'trial_limit' => (int) $current_membership->trial_limit,
            'status' => 'active',
        ) : null,
        'membership_history' => array_map(function($item) {
            return array(
                'id' => (int) $item->membership_id,
                'name' => $item->name,
                'start_date' => $item->startdate,
                'end_date' => $item->enddate === '0000-00-00 00:00:00' ? null : $item->enddate,
                'status' => $item->status,
            );
        }, $membership_history),
        'currency' => $currency,
    ), 200);
}

/**
 * Get user invoices/orders
 */
function custom_get_user_invoices($request) {
    $user_id = get_current_user_id();

    if (!$user_id) {
        return new WP_REST_Response(array(
            'success' => false,
            'message' => 'User not authenticated',
        ), 401);
    }

    global $wpdb;

    $orders = $wpdb->get_results($wpdb->prepare(
        "SELECT 
            o.id,
            o.code,
            o.membership_id,
            ml.name as membership_name,
            o.total,
            o.subtotal,
            o.tax,
            o.couponamount,
            o.payment_type,
            o.cardtype,
            o.status,
            o.gateway,
            o.gateway_environment,
            o.timestamp
        FROM {$wpdb->prefix}pmpro_membership_orders o
        LEFT JOIN {$wpdb->prefix}pmpro_membership_levels ml ON o.membership_id = ml.id
        WHERE o.user_id = %d
        ORDER BY o.timestamp DESC",
        $user_id
    ));

    $currency = get_option('pmpro_currency', 'USD');

    return new WP_REST_Response(array(
        'success' => true,
        'invoices' => array_map(function($order) use ($currency) {
            return array(
                'id' => (int) $order->id,
                'code' => $order->code,
                'membership_id' => (int) $order->membership_id,
                'membership_name' => $order->membership_name,
                'amount' => (float) $order->total,
                'subtotal' => (float) $order->subtotal,
                'tax' => (float) $order->tax,
                'discount' => (float) $order->couponamount,
                'currency' => $currency,
                'payment_type' => $order->payment_type,
                'card_type' => $order->cardtype,
                'status' => $order->status,
                'gateway' => $order->gateway,
                'date' => $order->timestamp,
            );
        }, $orders),
        'total_count' => count($orders),
    ), 200);
}
/**
 * Get user social profiles
 */
function custom_get_social_profiles($request) {
    $user_id = get_current_user_id();

    if (!$user_id) {
        return new WP_REST_Response(array(
            'success' => false,
            'message' => 'User not authenticated',
        ), 401);
    }

    $facebook = get_user_meta($user_id, '_tutor_profile_facebook', true);
    $twitter = get_user_meta($user_id, '_tutor_profile_twitter', true);
    $linkedin = get_user_meta($user_id, '_tutor_profile_linkedin', true);
    $website = get_user_meta($user_id, '_tutor_profile_website', true);
    $github = get_user_meta($user_id, '_tutor_profile_github', true);

    return new WP_REST_Response(array(
        'success' => true,
        'social_profiles' => array(
            'facebook' => $facebook ?: '',
            'twitter' => $twitter ?: '',
            'linkedin' => $linkedin ?: '',
            'website' => $website ?: '',
            'github' => $github ?: '',
        ),
    ), 200);
}

/**
 * Update user social profiles
 */
function custom_update_social_profiles($request) {
    $user_id = get_current_user_id();

    if (!$user_id) {
        return new WP_REST_Response(array(
            'success' => false,
            'message' => 'User not authenticated',
        ), 401);
    }

    $facebook = sanitize_text_field($request->get_param('facebook'));
    $twitter = sanitize_text_field($request->get_param('twitter'));
    $linkedin = sanitize_text_field($request->get_param('linkedin'));
    $website = esc_url_raw($request->get_param('website'));
    $github = sanitize_text_field($request->get_param('github'));

    if ($facebook !== null) {
        update_user_meta($user_id, '_tutor_profile_facebook', $facebook);
    }
    
    if ($twitter !== null) {
        update_user_meta($user_id, '_tutor_profile_twitter', $twitter);
    }
    
    if ($linkedin !== null) {
        update_user_meta($user_id, '_tutor_profile_linkedin', $linkedin);
    }
    
    if ($website !== null) {
        update_user_meta($user_id, '_tutor_profile_website', $website);
    }
    
    if ($github !== null) {
        update_user_meta($user_id, '_tutor_profile_github', $github);
    }

    return new WP_REST_Response(array(
        'success' => true,
        'message' => 'Social profiles updated successfully',
        'social_profiles' => array(
            'facebook' => get_user_meta($user_id, '_tutor_profile_facebook', true) ?: '',
            'twitter' => get_user_meta($user_id, '_tutor_profile_twitter', true) ?: '',
            'linkedin' => get_user_meta($user_id, '_tutor_profile_linkedin', true) ?: '',
            'website' => get_user_meta($user_id, '_tutor_profile_website', true) ?: '',
            'github' => get_user_meta($user_id, '_tutor_profile_github', true) ?: '',
        ),
    ), 200);
}

/**
 * Get user quiz attempts
 */
function custom_get_user_quiz_attempts($request) {
    $user_id = get_current_user_id();

    if (!$user_id) {
        return new WP_REST_Response(array(
            'success' => false,
            'message' => 'User not authenticated',
        ), 401);
    }

    global $wpdb;

    $quiz_attempts_table = $wpdb->prefix . 'quiz_attempts';
    $quiz_attempt_data_table = $wpdb->prefix . 'quiz_attempt_data';
    $users_table = $wpdb->prefix . 'users';
    $quizzes_table = $wpdb->prefix . 'quizzes';
    $questions_table = $wpdb->prefix . 'quiz_questions';

    // Check if tables exist
    $table_exists = $wpdb->get_var("SHOW TABLES LIKE '$quiz_attempts_table'");

    if (!$table_exists) {
        return new WP_REST_Response(array(
            'success' => false,
            'message' => 'Quiz attempts table not found',
        ), 404);
    }

    // Retrieve all quiz attempts for the user in reverse chronological order
    $quiz_attempts = $wpdb->get_results($wpdb->prepare("
        SELECT
            ua.id AS attempt_id,
            u.display_name AS student_name,
            ua.score,
            ua.datetime,
            c.name AS class_name,
            q.title AS quiz_title
        FROM $quiz_attempts_table ua
        INNER JOIN $quizzes_table q ON ua.quiz_id = q.id
        INNER JOIN $users_table u ON ua.student_id = u.ID
        INNER JOIN {$wpdb->prefix}class_enrollment ce ON ua.student_id = ce.student_id
        INNER JOIN {$wpdb->prefix}classes c ON ce.class_id = c.id
        WHERE ua.student_id = %d
        ORDER BY ua.datetime DESC
    ", $user_id));

    if (empty($quiz_attempts)) {
        return new WP_REST_Response(array(
            'success' => true,
            'quiz_attempts' => array(),
            'total_count' => 0,
        ), 200);
    }

    // Format the results
    $formatted_attempts = array();

    foreach ($quiz_attempts as $attempt) {
        $attempt_id = $attempt->attempt_id;

        // Get detailed results for this attempt
        $details_results = $wpdb->get_results($wpdb->prepare("
            SELECT qq.title AS question_title, qad.answer, qad.is_correct
            FROM $quiz_attempt_data_table qad
            INNER JOIN $questions_table qq ON qad.question_id = qq.id
            WHERE qad.quiz_attempt_id = %d
        ", $attempt_id));

        $correct_answers = 0;
        $total_questions = count($details_results);
        $question_details = array();

        foreach ($details_results as $detail) {
            if ($detail->is_correct) {
                $correct_answers++;
            }

            $question_details[] = array(
                'question' => $detail->question_title,
                'answer' => $detail->answer,
                'is_correct' => (bool) $detail->is_correct,
            );
        }

        $percentage = $total_questions > 0 ? round(($correct_answers / $total_questions) * 100, 2) : 0;

        $formatted_attempts[] = array(
            'attempt_id' => (int) $attempt->attempt_id,
            'name' => $attempt->student_name,
            'class' => $attempt->class_name,
            'quiz_title' => $attempt->quiz_title,
            'score' => $attempt->score,
            'date' => $attempt->datetime,
            'percentage' => $percentage,
            'correct_answers' => $correct_answers,
            'total_questions' => $total_questions,
            'details' => $question_details,
        );
    }

    return new WP_REST_Response(array(
        'success' => true,
        'quiz_attempts' => $formatted_attempts,
        'total_count' => count($formatted_attempts),
    ), 200);
}

Youez - 2016 - github.com/yon3zu
LinuXploit