HEX
Server: Apache
System: Linux p3plzcpnl506847.prod.phx3.secureserver.net 4.18.0-553.54.1.lve.el8.x86_64 #1 SMP Wed Jun 4 13:01:13 UTC 2025 x86_64
User: slfopp7cb1df (5698090)
PHP: 8.1.34
Disabled: NONE
Upload Files
File: /home/slfopp7cb1df/www/sitepacket.com/system/app/Libraries/Gmail_imap.php
<?php

namespace App\Libraries;

use Google\Service\Gmail as Google_Service_Gmail;
use Google\Service\Gmail\ModifyMessageRequest as Google_Service_Gmail_ModifyMessageRequest;
use App\Libraries\Google_Trait;
use App\Libraries\Ticket;

class Gmail_imap {

    use Google_Trait;

    private $client;
    private $service;
    private $Settings_model;

    public function __construct() {
        $this->Settings_model = model("App\Models\Settings_model");
        $this->set_type("imap");

        // Load Google API client
        require_once(APPPATH . "ThirdParty/Google/2-18-3/autoload.php");
        require_once(APPPATH . "ThirdParty/Imap/mail-mime-parser/autoload.php"); //load mail-mime-parser resources
        require_once(APPPATH . "ThirdParty/Imap/EmailReplyParser/vendor/autoload.php"); //load EmailReplyParser resources
    }

    // Initialize Gmail service
    private function _init_service($client) {
        $this->client = $client;
        $this->service = new Google_Service_Gmail($this->client);
    }

    // Process emails from Gmail
    public function run_imap() {
        $client = $this->_get_client_credentials();
        $this->_check_access_token($client);
        $this->_init_service($client);

        try {
            // Get unread messages
            $pageToken = null;
            $messages = [];
            $opt_param = [
                'maxResults' => 50,
                'labelIds' => ['INBOX', 'UNREAD']
            ];

            do {
                if ($pageToken) {
                    $opt_param['pageToken'] = $pageToken;
                }

                $messagesResponse = $this->service->users_messages->listUsersMessages('me', $opt_param);
                $messages = array_merge($messages, $messagesResponse->getMessages());
                $pageToken = $messagesResponse->getNextPageToken();
            } while ($pageToken);

            $last_seen_settings_name = "last_seen_gmail_message_id";
            $saved_last_message = get_setting($last_seen_settings_name);

            foreach ($messages as $message) {
                $messageId = $message->getId();

                if ($saved_last_message && $messageId <= $saved_last_message) {
                    continue;
                }

                $this->_process_single_message($messageId);
                $this->Settings_model->save_setting($last_seen_settings_name, $messageId);
            }

            return true;
        } catch (\Exception $e) {
            log_message('error', 'Gmail IMAP Error: ' . $e->getMessage());
            return false;
        }
    }

    // Process a single email message
    private function _process_single_message($messageId) {
        try {
            // Get the full raw message
            $message = $this->service->users_messages->get('me', $messageId, ['format' => 'raw']);

            $Ticket = new Ticket();
            $data = $this->_prepare_data($message);
            $Ticket->create_ticket_from_imap($data);

            // Mark as read
            $mods = new Google_Service_Gmail_ModifyMessageRequest();
            $mods->setRemoveLabelIds(['UNREAD']);
            $this->service->users_messages->modify('me', $messageId, $mods);
        } catch (\Exception $e) {
            log_message('error', 'Error processing Gmail message ' . $messageId . ': ' . $e->getMessage());
        }
    }

    // Decode base64 URL safe string
    private function _decode_body($data) {
        $data = str_replace(['-', '_'], ['+', '/'], $data);
        $data = base64_decode($data);
        return $data;
    }

    // Parse raw email message to extract headers
    private function _parse_raw_message($raw_message) {
        $result = [
            'subject' => '',
            'from_email' => '',
            'from_name' => '',
            'date' => ''
        ];

        // Split headers and body
        $header_parts = preg_split("/\r?\n\r?\n/", $raw_message, 2);
        $headers = get_array_value($header_parts, 0, '');

        // Parse headers
        $header_lines = preg_split("/\r?\n/", $headers);

        foreach ($header_lines as $line) {
            if (preg_match('/^Subject:\s*(.*)/i', $line, $matches)) {
                $result['subject'] = iconv_mime_decode(trim(get_array_value($matches, 1, '')), 0, 'UTF-8');
            } elseif (preg_match('/^From:\s*(.*)/i', $line, $matches)) {
                $from = trim(get_array_value($matches, 1, ''));
                if (preg_match('/(.*)<(.*)>/', $from, $from_matches)) {
                    $result['from_name'] = trim(trim(get_array_value($from_matches, 1, '')), ' \"');
                    $result['from_email'] = trim(get_array_value($from_matches, 2, ''));
                } else {
                    $result['from_email'] = $from;
                }
            } elseif (preg_match('/^Date:\s*(.*)/i', $line, $matches)) {
                $result['date'] = trim(get_array_value($matches, 1, ''));
            }
        }

        return $result;
    }

    private function _prepare_data($message_info) {
        // Get the raw email content
        $raw_content = $message_info->getRaw();
        $decoded_raw = $this->_decode_body($raw_content);

        // Get message details from the raw content
        $parsed_message = $this->_parse_raw_message($decoded_raw);

        $mail_mime_parser = \ZBateson\MailMimeParser\Message::from($decoded_raw, false);
        $email_content = $mail_mime_parser->getHtmlContent();

        //get content inside body tag only if it exists
        if ($email_content) {
            preg_match("/<body[^>]*>(.*?)<\/body>/is", $email_content, $body_matches);
            $email_content = isset($body_matches[1]) ? $body_matches[1] : $email_content;
        }

        $data = array();
        $data["email_address"] = get_array_value($parsed_message, 'from_email');
        $data["email_subject"] = get_array_value($parsed_message, 'subject');
        $data["email_sender_name"] = get_array_value($parsed_message, 'from_name');
        $data["email_content"] = $email_content;
        $data["email_reply_content"] = $this->_prepare_replying_message($email_content);
        $data["email_attachments"] = $this->_prepare_attachment_data($message_info);

        return $data;
    }

    private function _prepare_replying_message($email_content = "") {
        try {
            $reply_parser = new \EmailReplyParser\EmailReplyParser();
            return $reply_parser->parseReply($email_content);
        } catch (\Exception $ex) {
            return "";
        }
    }

    // Prepare attachment data from Gmail message
    private function _prepare_attachment_data($message) {
        $files_data = [];

        try {
            // Get the full message with attachments
            $message = $this->service->users_messages->get('me', $message->getId(), ['format' => 'full']);
            $payload = $message->getPayload();

            if (!$payload || !method_exists($payload, 'getParts')) {
                return $files_data;
            }

            $parts = $payload->getParts();

            // Process each part of the message
            foreach ($parts as $part) {
                $filename = $part->getFilename();

                // Skip if not an attachment
                if (empty($filename) && !$this->_is_attachment($part)) {
                    continue;
                }


                // Get attachment content
                $body = $part->getBody();
                if ($body) {
                    $file_content = '';
                    if ($body->getAttachmentId()) {
                        $file_content = $this->_get_attachment_content($message->getId(), $body->getAttachmentId());
                    } else if ($body->getData()) {
                        $file_content = $this->_decode_body($body->getData());
                    }

                    if (!empty($file_content)) {
                        $filename = $filename ?: 'attachment_' . uniqid();
                        $filename = str_replace("/", "-", $filename);

                        // Save file to the same directory structure as IMAP
                        $file_data = move_temp_file($filename, get_setting("timeline_file_path"), "imap_ticket", NULL, "", $file_content);

                        if ($file_data) {
                            $files_data[] = $file_data;
                        }
                    }
                }
            }
        } catch (\Exception $e) {
            log_message('error', 'Error preparing attachments: ' . $e->getMessage());
        }

        return $files_data;
    }

    // Check if a message part is an attachment
    private function _is_attachment($part) {
        $headers = $part->getHeaders();
        if ($headers) {
            foreach ($headers as $header) {
                if (strtolower($header->getName()) === 'content-disposition') {
                    return strpos(strtolower($header->getValue()), 'attachment') !== false;
                }
            }
        }
        return false;
    }

    // Get attachment content
    private function _get_attachment_content($messageId, $attachmentId) {
        try {
            $attachment = $this->service->users_messages_attachments->get('me', $messageId, $attachmentId);
            return $this->_decode_body($attachment->getData());
        } catch (\Exception $e) {
            log_message('error', 'Error getting attachment: ' . $e->getMessage());
            return '';
        }
    }
}