'not_configured']; $mbox = @imap_open($host, $user, $pass, 0, 1, ['DISABLE_AUTHENTICATOR' => 'GSSAPI']); if (!$mbox) return ['error' => imap_last_error() ?: 'connection_failed']; $total = imap_num_msg($mbox); $unseen = imap_search($mbox, 'UNSEEN') ?: []; $unread = count($unseen); // Fetch most recent messages (newest first) $start = max(1, $total - $maxMsgs + 1); $msgs = []; for ($i = $total; $i >= $start; $i--) { $hdr = @imap_headerinfo($mbox, $i); if (!$hdr) continue; $from = $hdr->from[0] ?? null; $fromName = $from ? (isset($from->personal) ? imap_utf8($from->personal) : '') : ''; $fromEmail = $from ? ($from->mailbox . '@' . ($from->host ?? '')) : ''; $subject = isset($hdr->subject) ? imap_utf8($hdr->subject) : '(no subject)'; $date = isset($hdr->date) ? date('M j g:ia', strtotime($hdr->date)) : ''; $isUnread = in_array($i, $unseen); // Fetch plain text preview (first 300 chars) $preview = ''; $struct = @imap_fetchstructure($mbox, $i); if ($struct) { if ($struct->type === 0) { // Single-part message $raw = @imap_fetchbody($mbox, $i, '1'); $enc = $struct->encoding ?? 0; if ($enc === 3) $raw = base64_decode($raw); elseif ($enc === 4) $raw = quoted_printable_decode($raw); $preview = mb_substr(strip_tags($raw), 0, 300); } else { // Multi-part — find first text/plain part foreach (($struct->parts ?? []) as $idx => $part) { if ($part->type === 0) { // text $raw = @imap_fetchbody($mbox, $i, (string)($idx + 1)); $enc = $part->encoding ?? 0; if ($enc === 3) $raw = base64_decode($raw); elseif ($enc === 4) $raw = quoted_printable_decode($raw); $preview = mb_substr(strip_tags($raw), 0, 300); break; } } } } $preview = trim(preg_replace('/\s+/', ' ', $preview)); $msgs[] = [ 'id' => $i, 'from_name' => $fromName ?: $fromEmail, 'from_email'=> $fromEmail, 'subject' => $subject, 'date' => $date, 'unread' => $isUnread, 'preview' => $preview, ]; } imap_close($mbox); return ['total' => $total, 'unread' => $unread, 'messages' => $msgs]; } $account = $data['account'] ?? $_GET['account'] ?? 'all'; $force = !empty($data['force']) || !empty($_GET['force']); $search = trim($data['search'] ?? $_GET['search'] ?? ''); $cacheKey = 'email_' . $account; $cacheTtl = 300; // 5 minutes if (!$force) { $cached = JarvisDB::single( "SELECT data, UNIX_TIMESTAMP(updated_at) ts FROM api_cache WHERE cache_key=?", [$cacheKey] ); if ($cached && (time() - (int)$cached['ts']) < $cacheTtl) { $out = json_decode($cached['data'], true); $out['cache_age_s'] = time() - (int)$cached['ts']; echo json_encode($out); exit; } } $result = ['accounts' => [], 'summary' => []]; if (in_array($account, ['all', 'gmail']) && defined('GMAIL_USER') && GMAIL_USER) { $r = imapFetch('{imap.gmail.com:993/imap/ssl}INBOX', GMAIL_USER, GMAIL_PASS); $result['accounts']['gmail'] = $r; } if (in_array($account, ['all', 'outlook']) && defined('OUTLOOK_USER') && OUTLOOK_USER) { $r = imapFetch('{outlook.office365.com:993/imap/ssl}INBOX', OUTLOOK_USER, OUTLOOK_PASS); $result['accounts']['outlook'] = $r; } if (in_array($account, ['all', 'icloud']) && defined('ICLOUD_USER') && ICLOUD_USER) { $r = imapFetch('{imap.mail.me.com:993/imap/ssl}INBOX', ICLOUD_USER, ICLOUD_PASS); $result['accounts']['icloud'] = $r; } // Build summary across all accounts $totalUnread = 0; $allMessages = []; foreach ($result['accounts'] as $acct => $r) { if (isset($r['unread'])) $totalUnread += (int)$r['unread']; if (!empty($r['messages'])) { foreach ($r['messages'] as $m) { $m['account'] = $acct; $allMessages[] = $m; } } } // Sort by date descending (approximate — already newest first per account) $result['summary'] = [ 'total_unread' => $totalUnread, 'recent' => array_slice($allMessages, 0, 10), 'fetched_at' => date('c'), ]; // Cache result JarvisDB::execute( "INSERT INTO api_cache (cache_key, data, updated_at) VALUES (?,?,NOW()) ON DUPLICATE KEY UPDATE data=VALUES(data), updated_at=NOW()", [$cacheKey, json_encode($result)] ); $result['cache_age_s'] = 0; echo json_encode($result);