1: <?php
2:
3: /**
4: * IMAP modules
5: * @package modules
6: * @subpackage imap
7: */
8:
9: if (!defined('DEBUG_MODE')) { die(); }
10:
11:
12: /**
13: * Check for attachments when forwarding a message
14: * @subpackage imap/handler
15: */
16: class Hm_Handler_imap_forward_attachments extends Hm_Handler_Module {
17: public function process() {
18: if (!array_key_exists('forward', $this->request->get)) {
19: return;
20: }
21: if (!array_key_exists('list_path', $this->request->get)) {
22: return;
23: }
24: if (!array_key_exists('uid', $this->request->get)) {
25: return;
26: }
27: $uid = $this->request->get['uid'];
28: $list_path = $this->request->get['list_path'];
29: $path = explode('_', $list_path);
30: if (count($path) != 3) {
31: return;
32: }
33: if ($path[0] != 'imap') {
34: return;
35: }
36: $filepath = $this->config->get('attachment_dir');
37: if (!$filepath) {
38: return;
39: }
40: $mailbox = Hm_IMAP_List::get_connected_mailbox($path[1], $this->cache);
41: if (! $mailbox) {
42: return;
43: }
44: $content = $mailbox->get_message_content(hex2bin($path[2]), $uid);
45: if (!$content) {
46: return;
47: }
48: $file = array(
49: 'name' => 'mail.mime',
50: 'type' => 'message/rfc822',
51: 'no_encoding' => true,
52: 'size' => mb_strlen($content)
53: );
54: $draft_id = next_draft_key($this->session);
55: // This needs to be replaced with something that works with the new attachment
56: // code.
57: //attach_file($content, $file, $filepath, $draft_id, $this);
58: $this->out('compose_draft_id', $draft_id);
59: }
60: }
61:
62: /**
63: * Get the status of an IMAP folder
64: * @subpackage imap/handler
65: */
66: class Hm_Handler_imap_folder_status extends Hm_Handler_Module {
67: public function process() {
68: list($success, $form) = $this->process_form(array('imap_server_id', 'folder'));
69: if ($success) {
70: $mailbox = Hm_IMAP_List::get_connected_mailbox($form['imap_server_id'], $this->cache);
71: if ($mailbox && $mailbox->authed()) {
72: $this->out('folder_status', array('imap_'.$form['imap_server_id'].'_'.$form['folder'] => $mailbox->get_folder_status(hex2bin($form['folder']))));
73: }
74: }
75: }
76: }
77:
78: /**
79: * Process input from the per page count setting
80: * @subpackage imap/handler
81: */
82: class Hm_Handler_process_imap_per_page_setting extends Hm_Handler_Module {
83: /**
84: * Allowed values are greater than zero and less than MAX_PER_SOURCE
85: */
86: public function process() {
87: process_site_setting('imap_per_page', $this, 'max_source_setting_callback', DEFAULT_IMAP_PER_PAGE);
88: }
89: }
90:
91: /**
92: * Process input from the max google contacts input in the settings page
93: * @subpackage imap/handler
94: */
95: class Hm_Handler_process_max_google_contacts_number extends Hm_Handler_Module {
96: /**
97: * Allowed values are greater than zero and less than MAX_PER_SOURCE
98: */
99: public function process() {
100: process_site_setting('max_google_contacts_number', $this, 'max_source_setting_callback', DEFAULT_MAX_GOOGLE_CONTACTS_NUMBER);
101: }
102: }
103:
104: /**
105: * Process input from the max per source setting for the Sent E-mail page in the settings page
106: * @subpackage imap/handler
107: */
108: class Hm_Handler_process_sent_source_max_setting extends Hm_Handler_Module {
109: /**
110: * Allowed values are greater than zero and less than MAX_PER_SOURCE
111: */
112: public function process() {
113: process_site_setting('sent_per_source', $this, 'max_source_setting_callback', DEFAULT_SENT_PER_SOURCE);
114: }
115: }
116:
117: /**
118: * Process input from archive to original folder setting archive page in the settings page
119: * @subpackage imap/handler
120: */
121: class Hm_Handler_process_original_folder_setting extends Hm_Handler_Module {
122: /**
123: * Allowed values are true and false
124: */
125: public function process() {
126: function original_folder_callback($val) {
127: return $val;
128: }
129: process_site_setting('original_folder', $this, 'original_folder_callback', false, true);
130: }
131: }
132:
133: /**
134: * Process "unread_on_open" setting for the message view page in the settings page
135: * @subpackage imap/handler
136: */
137: class Hm_Handler_process_unread_on_open extends Hm_Handler_Module {
138: /**
139: * valid values are true or false
140: */
141: public function process() {
142: function unread_on_open_callback($val) {
143: return $val;
144: }
145: process_site_setting('unread_on_open', $this, 'unread_on_open_callback', false, true);
146: }
147: }
148:
149: /**
150: * Process "simple message parts" setting for the message view page in the settings page
151: * @subpackage imap/handler
152: */
153: class Hm_Handler_process_simple_msg_parts extends Hm_Handler_Module {
154: /**
155: * valid values are true or false
156: */
157: public function process() {
158: function simple_msg_view_callback($val) {
159: return $val;
160: }
161: process_site_setting('simple_msg_parts', $this, 'simple_msg_view_callback', DEFAULT_SIMPLE_MSG_PARTS, true);
162: }
163: }
164:
165: /**
166: * Process "pagination links" setting for the message view page in the settings page
167: * @subpackage imap/handler
168: */
169: class Hm_Handler_process_pagination_links extends Hm_Handler_Module {
170: /**
171: * valid values are true or false
172: */
173: public function process() {
174: function pagination_links_callback($val) {
175: return $val;
176: }
177: process_site_setting('pagination_links', $this, 'pagination_links_callback', DEFAULT_PAGINATION_LINKS, true);
178: }
179: }
180:
181: /**
182: * Process "auto_advance_email" setting for loading the next email instead of returning to inbox in the settings page
183: * @subpackage imap/handler
184: */
185: class Hm_Handler_process_auto_advance_email_setting extends Hm_Handler_Module {
186: /**
187: * valid values are true or false
188: */
189: public function process() {
190: function auto_advance_email_callback($val) {
191: return $val;
192: }
193: process_site_setting('auto_advance_email', $this, 'auto_advance_email_callback', true, true);
194: }
195: }
196:
197: /**
198: * Process "message part icons" setting for the message view page in the settings page
199: * @subpackage imap/handler
200: */
201: class Hm_Handler_process_msg_part_icons extends Hm_Handler_Module {
202: /**
203: * valid values are true or false
204: */
205: public function process() {
206: function msg_part_icons_callback($val) {
207: return $val;
208: }
209: process_site_setting('msg_part_icons', $this, 'msg_part_icons_callback', DEFAULT_MSG_PART_ICONS, true);
210: }
211: }
212:
213: /**
214: * Process "text only" setting for the message view page in the settings page
215: * @subpackage imap/handler
216: */
217: class Hm_Handler_process_text_only_setting extends Hm_Handler_Module {
218: /**
219: * valid values are true or false
220: */
221: public function process() {
222: function text_only_callback($val) {
223: return $val;
224: }
225: process_site_setting('text_only', $this, 'text_only_callback', DEFAULT_TEXT_ONLY, true);
226: }
227: }
228:
229: /**
230: * Process "since" setting for the Sent page in the settings page
231: * @subpackage imap/handler
232: */
233: class Hm_Handler_process_sent_since_setting extends Hm_Handler_Module {
234: /**
235: * valid values are defined in the process_since_argument function
236: */
237: public function process() {
238: process_site_setting('sent_since', $this, 'since_setting_callback',DEFAULT_SENT_SINCE);
239: }
240: }
241:
242: /**
243: * Process an IMAP move/copy action
244: * @subpackage imap/handler
245: */
246: class Hm_Handler_imap_process_move extends Hm_Handler_Module {
247: public function process() {
248: list($success, $form) = $this->process_form(array('imap_move_to', 'imap_move_page', 'imap_move_action', 'imap_move_ids'));
249: if ($success) {
250: $screen = false;
251: $parts = explode("_", $this->request->get['list_path']);
252: $imap_server_id = $parts[1] ?? '';
253: $emails_to_block = [];
254: if ($form['imap_move_action'] == "screen_mail") {
255: $mailbox = Hm_IMAP_List::get_connected_mailbox($imap_server_id, $this->cache);
256: if ($mailbox && $mailbox->authed()) {
257: $form['imap_move_action'] = "move";
258: $screen = true;
259: $screen_folder = 'Screen emails';
260: if (! count($mailbox->get_folder_status($screen_folder))) {
261: $mailbox->create_folder($screen_folder);
262: }
263: $form['imap_move_to'] = $parts[0] ."_". $parts[1] ."_".bin2hex($screen_folder);
264: $imap_move_ids = explode(",", $form['imap_move_ids']);
265:
266: foreach ($imap_move_ids as $imap_msg_id) {
267: $array_imap_msg_id = explode("_", $imap_msg_id);
268: if (isset($array_imap_msg_id[2])) {
269: $msg_header = $mailbox->get_message_headers(hex2bin($array_imap_msg_id[3]), $array_imap_msg_id[2]);
270: $email_sender = process_address_fld($msg_header['From'])[0]['email'] ?? null;
271: if ($email_sender) {
272: $emails_to_block[] = $email_sender;
273: }
274: }
275: }
276: $emails_to_block = array_unique($emails_to_block);
277: }
278: }
279:
280: list($msg_ids, $dest_path, $same_server_ids, $other_server_ids) = process_move_to_arguments($form);
281: $moved = array();
282: if (count($same_server_ids) > 0) {
283: $action = imap_move_same_server($same_server_ids, $form['imap_move_action'], $this->cache, $dest_path, $screen);
284: $moved = array_merge($moved, $action['moved']);
285: }
286: if (count($other_server_ids) > 0) {
287: $action = imap_move_different_server($other_server_ids, $form['imap_move_action'], $dest_path, $this->cache);
288: $moved = array_merge($moved, $action['moved']);
289: }
290: if (count($moved) > 0) {
291: $this->out('move_responses', $action['responses']);
292: }
293: if (count($moved) > 0 && count($moved) == count($msg_ids)) {
294: if ($form['imap_move_action'] == 'move') {
295: if ($screen) {
296: Hm_Msgs::add('Emails moved to Screen email folder');
297: } else {
298: Hm_Msgs::add('Messages moved');
299: }
300: }
301: else {
302: Hm_Msgs::add('Messages copied');
303: }
304: }
305: elseif (count($moved) > 0) {
306: if ($form['imap_move_action'] == 'move') {
307: if ($screen) {
308: Hm_Msgs::add('Some Emails moved to Screen email folder', 'warning');
309: } else {
310: Hm_Msgs::add('Some messages moved (only IMAP message types can be moved)', 'warning');
311: }
312: }
313: else {
314: Hm_Msgs::add('Some messages copied (only IMAP message types can be copied)', 'warning');
315: }
316: }
317: elseif (count($moved) == 0) {
318: Hm_Msgs::add('Unable to move/copy selected messages', 'danger');
319: }
320: $this->out('move_count', $moved);
321: $this->out('emails_to_block', implode(",", $emails_to_block));
322: }
323: }
324: }
325:
326: /**
327: * Save a sent message
328: * @subpackage imap/handler
329: */
330: class Hm_Handler_imap_save_sent extends Hm_Handler_Module {
331: public function process() {
332: if (!$this->get('save_sent_msg')) {
333: return;
334: }
335: $imap_id = $this->get('save_sent_server');
336: $mime = $this->get('save_sent_msg');
337:
338: if ($imap_id === false) {
339: return;
340: }
341: $msg = $mime->get_mime_msg();
342: $msg = str_replace("\r\n", "\n", $msg);
343: $msg = str_replace("\n", "\r\n", $msg);
344: $msg = rtrim($msg)."\r\n";
345: $imap_details = Hm_IMAP_List::dump($imap_id);
346: $mailbox = Hm_IMAP_List::get_connected_mailbox($imap_id, $this->cache);
347: if ($mailbox && $mailbox->authed()) {
348: list($uid, $sent_folder) = save_sent_msg($this, $imap_id, $mailbox, $imap_details, $msg, $mime->get_headers()['Message-Id']);
349: if ($uid) {
350: $this->out('sent_msg_uid', $uid);
351: $this->out('sent_imap_id', $imap_id);
352:
353: if ($this->user_config->get('review_sent_email_setting', false)) {
354: $this->out('redirect_url', '?page=message&uid='.$uid.'&list_path=imap_'.$imap_id.'_'.bin2hex($sent_folder));
355: }
356: }
357: }
358: }
359: }
360:
361: /**
362: * Unflag a message after replying to it
363: * @subpackage imap/handler
364: */
365: class Hm_Handler_imap_unflag_on_send extends Hm_Handler_Module {
366: public function process() {
367: if ($this->get('msg_sent')) {
368: list($success, $form) = $this->process_form(array('compose_unflag_send', 'compose_msg_uid', 'compose_msg_path'));
369: if ($success) {
370: $path = explode('_', $form['compose_msg_path']);
371: if (count($path) == 3 && $path[0] == 'imap') {
372: $mailbox = Hm_IMAP_List::get_connected_mailbox($path[1], $this->cache);
373: if ($mailbox && $mailbox->authed()) {
374: $mailbox->message_action(hex2bin($path[2]), 'UNFLAG', array($form['compose_msg_uid']));
375: }
376: }
377: }
378: }
379: }
380: }
381:
382: /**
383: * Flag a message as answered
384: * @subpackage imap/handler
385: */
386: class Hm_Handler_imap_mark_as_answered extends Hm_Handler_Module {
387: public function process() {
388: if ($this->get('msg_sent')) {
389: list($success, $form) = $this->process_form(array('compose_msg_uid', 'compose_msg_path'));
390: if ($success) {
391: $path = explode('_', $form['compose_msg_path']);
392: if (count($path) == 3 && $path[0] == 'imap') {
393: $mailbox = Hm_IMAP_List::get_connected_mailbox($path[1], $this->cache);
394: if ($mailbox && $mailbox->authed()) {
395: $this->out('folder_status', array('imap_'.$path[1].'_'.$path[2] => $mailbox->get_folder_state()));
396: $mailbox->message_action(hex2bin($path[2]), 'ANSWERED', array($form['compose_msg_uid']));
397: }
398: }
399: }
400: }
401: if ($this->get('msg_next_link') && !$this->user_config->get('review_sent_email_setting', true)) {
402: $this->out('redirect_url', htmlspecialchars_decode($this->get('msg_next_link')));
403: }
404: }
405: }
406:
407: /**
408: * Flag a message as read
409: * @subpackage imap/handler
410: */
411: class Hm_Handler_imap_mark_as_read extends Hm_Handler_Module {
412: public function process() {
413: list($success, $form) = $this->process_form(array('imap_server_id', 'imap_msg_uid', 'folder'));
414: if ($success) {
415: $mailbox = Hm_IMAP_List::get_connected_mailbox($form['imap_server_id'], $this->cache);
416: if ($mailbox && $mailbox->authed()) {
417: $this->out('folder_status', array('imap_'.$form['imap_server_id'].'_'.$form['folder'] => $mailbox->get_folder_state()));
418: $mailbox->message_action(hex2bin($form['folder']), 'READ', array($form['imap_msg_uid']));
419: }
420: }
421: }
422: }
423:
424: /**
425: * Process a request to change a combined page source
426: * @subpackage imap/handler
427: */
428: class Hm_Handler_process_imap_source_update extends Hm_Handler_Module {
429: /**
430: * Add or remove an IMAP folder to the combined view
431: */
432: public function process() {
433: list($success, $form) = $this->process_form(array('combined_source_state', 'list_path'));
434: if ($success) {
435: $sources = $this->user_config->get('custom_imap_sources');
436: if ($form['combined_source_state'] == 1) {
437: $sources[$form['list_path']] = 'add';
438: Hm_Msgs::add('Folder added to combined pages', 'info');
439: $this->session->record_unsaved('Added folder to combined pages');
440: }
441: else {
442: $sources[$form['list_path']] = 'remove';
443: Hm_Msgs::add('Folder removed from combined pages', 'info');
444: $this->session->record_unsaved('Removed folder from combined pages');
445: }
446: $this->session->set('custom_imap_sources', $sources, true);
447: }
448: }
449: }
450:
451: /**
452: * Stream a message from IMAP to the browser and show it
453: * @subpackage imap/handler
454: */
455: class Hm_Handler_imap_show_message extends Hm_Handler_Module {
456: public function process() {
457: if (array_key_exists('imap_show_message', $this->request->get) && $this->request->get['imap_show_message']) {
458:
459: $server_id = NULL;
460: $uid = NULL;
461: $folder = NULL;
462: $msg_id = NULL;
463:
464: if (array_key_exists('uid', $this->request->get) && $this->request->get['uid']) {
465: $uid = $this->request->get['uid'];
466: }
467: if (array_key_exists('list_path', $this->request->get) && preg_match("/^imap_(\w+)_(.+)/", $this->request->get['list_path'], $matches)) {
468: $server_id = $matches[1];
469: $folder = hex2bin($matches[2]);
470: }
471: if (array_key_exists('imap_msg_part', $this->request->get) && preg_match("/^[0-9\.]+$/", $this->request->get['imap_msg_part'])) {
472: $msg_id = preg_replace("/^0.{1}/", '', $this->request->get['imap_msg_part']);
473: }
474: if ($server_id !== NULL && $uid !== NULL && $folder !== NULL && $msg_id !== NULL) {
475: $mailbox = Hm_IMAP_List::get_connected_mailbox($server_id, $this->cache);
476: if ($mailbox && $mailbox->authed()) {
477: $mailbox->stream_message_part($folder, $uid, $msg_id, function ($content_type) {
478: header('Content-Type: ' . $content_type);
479: header('Content-Transfer-Encoding: binary');
480: ob_end_clean();
481: });
482: Hm_Functions::cease();
483: }
484: }
485: Hm_Msgs::add('An Error occurred trying to download the message', 'danger');
486: }
487: }
488: }
489:
490: /**
491: * Stream a message from IMAP to the browser
492: * @subpackage imap/handler
493: */
494: class Hm_Handler_imap_download_message extends Hm_Handler_Module {
495: /**
496: * Download a message from the IMAP server
497: */
498: public function process() {
499: if (array_key_exists('imap_download_message', $this->request->get) && $this->request->get['imap_download_message']) {
500:
501: list($server_id, $uid, $folder, $msg_id) = get_request_params($this->request->get);
502: if ($server_id !== NULL && $uid !== NULL && $folder !== NULL && $msg_id !== NULL) {
503: $mailbox = Hm_IMAP_List::get_connected_mailbox($server_id, $this->cache);
504: if ($mailbox && $mailbox->authed()) {
505: $mailbox->stream_message_part($folder, $uid, $msg_id, function ($content_type, $part_name) {
506: header('Content-Disposition: attachment; filename="' . $part_name . '"');
507: header('Content-Type: ' . $content_type);
508: header('Content-Transfer-Encoding: binary');
509: ob_end_clean();
510: });
511: Hm_Functions::cease();
512: }
513: }
514: Hm_Msgs::add('An Error occurred trying to download the message', 'danger');
515: }
516: }
517: }
518:
519: /**
520: * Process the list_path input argument
521: * @subpackage imap/handler
522: */
523: class Hm_Handler_imap_message_list_type extends Hm_Handler_Module {
524: /**
525: * Output a list title
526: */
527: public function process() {
528: if (array_key_exists('list_path', $this->request->get)) {
529: $path = $this->request->get['list_path'];
530: if (preg_match("/^imap_\w+_.+$/", $path)) {
531: $this->out('list_meta', false, false);
532: $this->out('list_path', $path, false);
533: $this->out('move_copy_controls', true);
534: $parts = explode('_', $path, 3);
535: $details = Hm_IMAP_List::dump($parts[1]);
536: $custom_link = 'add';
537: foreach (imap_data_sources($this->user_config->get('custom_imap_sources', array())) as $vals) {
538: if ($vals['id'] == $parts[1] && $vals['folder'] == $parts[2]) {
539: $custom_link = 'remove';
540: break;
541: }
542: }
543: $this->out('custom_list_controls_type', $custom_link);
544: if (array_key_exists('keyword', $this->request->get)) {
545: $this->out('list_keyword', $this->request->get['keyword']);
546: }
547: if (array_key_exists('filter', $this->request->get)) {
548: if (in_array($this->request->get['filter'], array('all', 'unseen', 'seen',
549: 'answered', 'unanswered', 'flagged', 'unflagged'), true)) {
550: $this->out('list_filter', $this->request->get['filter']);
551: }
552: }
553: $folder = hex2bin($parts[2]);
554: $spcial_folders = get_special_folders($this, $parts[1]);
555: if (array_key_exists(strtolower($folder), $spcial_folders)) {
556: $this->out('core_msg_control_folder', $spcial_folders[strtolower($folder)]);
557: }
558: if (!empty($details)) {
559: if (array_key_exists('folder_label', $this->request->get)) {
560: $folder = $this->request->get['folder_label'];
561: $this->out('folder_label', $folder);
562: } else {
563: $folder = hex2bin($parts[2]);
564: }
565:
566: $mailbox = Hm_IMAP_List::get_mailbox_without_connection($details);
567: $label = $mailbox->get_folder_name($folder);
568: if(!$label) {
569: if ($this->config->get('allow_session_cache', false)) {
570: $paths = explode("_", $path);
571: $short_path = $paths[0] . "_" . $paths[1] . "_";
572: $cached_folders = $this->cache->get('imap_folders_'.$short_path, true);
573: $label = !empty($cached_folders[$folder]['name']) ? $cached_folders[$folder]['name'] : '';
574: } else {
575: Hm_Msgs::add('Folder name loaded directly from the server. This may be slower. Enable session caching for better performance.', 'warning');
576: if (isset($details['type']) && $details['type'] === 'ews') {
577: $connected_mailbox = Hm_IMAP_List::get_connected_mailbox($parts[1], $this->cache);
578: if ($connected_mailbox && $connected_mailbox->authed()) {
579: $folder_status = $connected_mailbox->get_folder_status($folder, false);
580: $label = $folder_status['name'] ?? null;
581: }
582: }
583: }
584: }
585: $title = array(strtoupper($details['type'] ?? 'IMAP'), $details['name'], $label);
586: if ($this->get('list_page', 0)) {
587: $title[] = sprintf('Page %d', $this->get('list_page', 0));
588: }
589: $this->out('mailbox_list_title', $title);
590: }
591:
592: if ($this->module_is_supported("contacts") && strtoupper($folder) == 'INBOX') {
593: $this->out('folder', $folder);
594: $this->out('screen_emails', isset($this->request->get['screen_emails']));
595: $this->out('first_time_screen_emails', $this->user_config->get('first_time_screen_emails_setting', DEFAULT_PER_SOURCE));
596: $this->out('move_messages_in_screen_email', $this->user_config->get('move_messages_in_screen_email_setting', DEFAULT_PER_SOURCE));
597: }
598: }
599: if (array_key_exists('sort', $this->request->get) || array_key_exists('sort', $this->request->post)) {
600: $sort = $this->request->get['sort'] ?? $this->request->post['sort'] ?? '';
601: if (in_array($sort, array('arrival', 'from', 'subject',
602: 'date', 'to', '-arrival', '-from', '-subject', '-date', '-to'), true)) {
603: $this->out('list_sort', $sort);
604: }
605: } elseif ($default_sort_order = $this->user_config->get('default_sort_order_setting', false)) {
606: $this->out('list_sort', $default_sort_order);
607: }
608: }
609: }
610: }
611:
612: /**
613: * Delete an attachment on the server
614: * @subpackage imap/handler
615: */
616: class Hm_Handler_imap_remove_attachment extends Hm_Handler_Module {
617: public function process() {
618: if (array_key_exists('imap_remove_attachment', $this->request->get) && $this->request->get['imap_remove_attachment']) {
619: list($server_id, $uid, $folder, $msg_id) = get_request_params($this->request->get);
620: if ($server_id !== NULL && $uid !== NULL && $folder !== NULL && $msg_id !== NULL) {
621: $mailbox = Hm_IMAP_List::get_connected_mailbox($server_id, $this->cache);
622: if ($mailbox && $mailbox->authed()) {
623: if ($mailbox->remove_attachment($folder, $uid, $this->request->get['imap_msg_part'])) {
624: Hm_Msgs::add('Attachment deleted');
625: $this->out('redirect_url', '?page=message_list&list_path=' . $this->request->get['list_path']);
626: return;
627: }
628: }
629: }
630: Hm_Msgs::add('An Error occurred trying to remove attachment to the message', 'danger');
631: }
632: }
633: }
634:
635: /**
636: * Expand an IMAP folder section
637: * @subpackage imap/handler
638: */
639: class Hm_Handler_imap_folder_expand extends Hm_Handler_Module {
640: /**
641: * Return cached subfolder contents or query the IMAP server for it
642: */
643: public function process() {
644:
645: list($success, $form) = $this->process_form(array('imap_server_id'));
646: if ($success) {
647: $folder = '';
648: if (isset($this->request->post['folder'])) {
649: $folder = $this->request->post['folder'];
650: }
651: $path = sprintf("imap_%s_%s", $form['imap_server_id'], $folder);
652: $page_cache = $this->cache->get('imap_folders_'.$path);
653: if (array_key_exists('imap_prefetch', $this->request->post)) {
654: $prefetched = $this->session->get('imap_prefetched_ids', array());
655: $prefetched[] = $form['imap_server_id'];
656: $this->session->set('imap_prefetched_ids', array_unique($prefetched, SORT_STRING));
657: }
658: $with_subscription = isset($this->request->post['subscription_state']) && $this->request->post['subscription_state'];
659: $mailbox = Hm_IMAP_List::get_connected_mailbox($form['imap_server_id'], $this->cache);
660: if ($mailbox && $mailbox->authed()) {
661: $this->out('can_share_folders', stripos($mailbox->get_capability(), 'ACL') !== false);
662: $quota_root = $mailbox->get_quota($folder ? $folder : 'INBOX', true);
663: if ($quota_root && isset($quota_root[0]['name'])) {
664: $quota = $mailbox->get_quota($quota_root[0]['name'], false);
665: if ($quota) {
666: $current = floatval($quota[0]['current']);
667: $max = floatval($quota[0]['max']);
668: if ($max > 0) {
669: $this->out('quota', ceil(($current / $max) * 100));
670: $this->out('quota_max', $max / 1024);
671: }
672: }
673: }
674: }
675: if ($page_cache) {
676: $this->out('imap_expanded_folder_data', $page_cache);
677: $this->out('imap_expanded_folder_id', $form['imap_server_id']);
678: $this->out('imap_expanded_folder_path', $path);
679: $this->out('with_input', $with_subscription);
680: $this->out('folder', $folder);
681: return;
682: }
683: if ($mailbox && $mailbox->authed()) {
684: $only_subscribed = $this->user_config->get('only_subscribed_folders_setting', false);
685: if ($with_subscription) {
686: $only_subscribed = false;
687: }
688: $count_children = false;
689: if (isset($this->request->post['count_children'])){
690: $count_children = $this->request->post['count_children'];
691: }
692: $msgs = $mailbox->get_subfolders(hex2bin($folder), $only_subscribed, $with_subscription, $count_children);
693: if (isset($msgs[$folder])) {
694: unset($msgs[$folder]);
695: }
696: $this->cache->set('imap_folders_'.$path, $msgs);
697: $this->out('imap_expanded_folder_data', $msgs);
698: $this->out('imap_expanded_folder_id', $form['imap_server_id']);
699: $this->out('imap_expanded_folder_path', $path);
700: $this->out('with_input', $with_subscription);
701: $this->out('folder', $folder);
702: }
703: else {
704: $details = Hm_IMAP_List::get($form['imap_server_id'], false);
705: if ($details) {
706: $type = $details['type'] ?? '';
707: } else {
708: $type = '';
709: }
710: Hm_Msgs::add(sprintf('Could not authenticate to the selected %s server (%s)', $type, $this->user_config->get('imap_servers')[$form['imap_server_id']]['user']), 'warning');
711: }
712: }
713: }
714: }
715:
716: /**
717: * Fetch the message headers for a an IMAP folder page
718: * @subpackage imap/handler
719: */
720: class Hm_Handler_imap_folder_page extends Hm_Handler_Module {
721:
722: /**
723: * Use IMAP FETCH to get a page of headers
724: */
725: public function process() {
726:
727: $filter = 'ALL';
728: if ($this->get('list_filter')) {
729: $filter = mb_strtoupper($this->get('list_filter'));
730: }
731: $keyword = $this->get('list_keyword', '');
732: list($sort, $rev) = process_sort_arg($this->get('list_sort'), $this->user_config->get('default_sort_order_setting', 'arrival'));
733: $limit = $this->user_config->get('imap_per_page_setting', DEFAULT_PER_SOURCE);
734: $offset = 0;
735: $msgs = array();
736: $list_page = 1;
737: $include_content_body = false;
738: $include_preview = $this->user_config->get('active_preview_message_setting', false);
739: $ceo_use_detect_ceo_fraud = $this->user_config->get('ceo_use_detect_ceo_fraud_setting', false);
740: if ($include_preview || $ceo_use_detect_ceo_fraud) {
741: $include_content_body = true;
742: }
743:
744: list($success, $form) = $this->process_form(array('imap_server_id', 'folder'));
745: if ($success) {
746: if (isset($this->request->get['list_page'])) {
747: $list_page = (int) $this->request->get['list_page'];
748: if ($list_page && $list_page > 1) {
749: $offset = ($list_page - 1)*$limit;
750: }
751: else {
752: $list_page = 1;
753: }
754: }
755: $path = sprintf("imap_%s_%s", $form['imap_server_id'], $form['folder']);
756: $details = Hm_IMAP_List::dump($form['imap_server_id']);
757: $mailbox = Hm_IMAP_List::get_connected_mailbox($form['imap_server_id'], $this->cache);
758: if ($mailbox && $mailbox->authed()) {
759: $this->out('imap_mailbox_page_path', $path);
760: if (isset($this->request->get['screen_emails']) && hex2bin($form['folder']) == 'INBOX' && $this->module_is_supported("contacts")) {
761: $contacts = $this->get('contact_store');
762: $contact_list = $contacts->getAll();
763:
764: $existingEmails = array_map(function($c){
765: return $c->value('email_address');
766: },$contact_list);
767: list($total, $results) = $mailbox->get_messages(hex2bin($form['folder']), $sort, $rev, $filter, $offset, $limit, $keyword, $existingEmails, $include_content_body);
768: } else {
769: list($total, $results) = $mailbox->get_messages(hex2bin($form['folder']), $sort, $rev, $filter, $offset, $limit, $keyword, null, $include_content_body);
770: }
771: foreach ($results as $msg) {
772: $msg['server_id'] = $form['imap_server_id'];
773: $msg['server_name'] = $details['name'];
774: $msg['folder'] = $form['folder'];
775: $uid = $msg['uid'];
776:
777: if ($ceo_use_detect_ceo_fraud && hex2bin($form['folder']) == 'INBOX') {
778: if ($this->isCeoFraud($msg['to'], $msg['subject'], $msg['preview_msg'])) {
779:
780: $folder = "Suspicious emails";
781: if (!count($mailbox->get_mailbox_status($folder))) {
782: $mailbox->create_folder($folder);
783: }
784: $dest_folder = bin2hex($folder);
785: $server_ids = array(
786: $form['imap_server_id'] => [
787: $form['folder'] => $uid
788: ]
789: );
790: imap_move_same_server($server_ids, "move", $this->cache, [null, null, $dest_folder]);
791: $msg = [];
792: $total--;
793: }
794: }
795:
796: if ($msg) {
797: if (! $include_preview && isset($msg['preview_msg'])) {
798: $msg['preview_msg'] = "";
799: }
800: $msgs[] = $msg;
801: }
802: }
803: if ($folder = $mailbox->get_selected_folder()) {
804: $folder['detail']['exists'] = $total;
805: $this->out('imap_folder_detail', array_merge($folder, array('offset' => $offset, 'limit' => $limit)));
806: }
807: $this->out('folder_status', array('imap_'.$form['imap_server_id'].'_'.$form['folder'] => $mailbox->get_folder_state()));
808: }
809: $this->out('imap_mailbox_page', $msgs);
810: $this->out('list_page', $list_page);
811: $this->out('imap_server_id', $form['imap_server_id']);
812: $this->out('do_not_flag_as_read_on_open', $this->user_config->get('unread_on_open_setting', false));
813: }
814: }
815: public function isCeoFraud($email, $subject, $msg) {
816: // 1. Check Suspicious Terms or Requests
817: $suspiciousTerms = explode(",", $this->user_config->get("ceo_suspicious_terms_setting"));
818: if ($this->detectSuspiciousTerms($msg, $suspiciousTerms) || $this->detectSuspiciousTerms($subject, $suspiciousTerms)) {
819:
820: // 2. check ceo_rate_limit
821: $amounts = $this->extractAmountFromEmail($msg);
822: $amountLimit = $this->user_config->get("ceo_amount_limit_setting");
823: $isUpperAmount = array_reduce($amounts, function ($carry, $value) use ($amountLimit) {
824: return $carry || $value > $amountLimit;
825: }, false);
826:
827: if ($isUpperAmount) {
828: if ($this->user_config->get("ceo_use_trusted_contact_setting")) {
829: $contacts = $this->get('contact_store');
830: $contact_list = $contacts->getAll();
831: $existingEmails = array_map(function($c){
832: return $c->value('email_address');
833: },$contact_list);
834: if (!$this->isEmailInTrustedDomainList(array_values($existingEmails), $email)) {
835: return true;
836: }
837: } else {
838: return true;
839: }
840: }
841: }
842: return false;
843: }
844: private function detectSuspiciousTerms($msg, $suspiciousTerms) {
845: foreach ($suspiciousTerms as $phrase) {
846: if (stripos($msg, trim($phrase)) !== false) {
847: return true;
848: }
849: }
850: return false;
851: }
852: private function isEmailInTrustedDomainList($trustedDomain, $email) {
853: if (in_array($email, $trustedDomain)) {
854: return true;
855: }
856: return false;
857: }
858: private function extractAmountFromEmail($emailBody) {
859: $pattern = '/\b\d+(?:,\d+)?\.?\d*\s*(?:USD|dollars?|US\$?|EUR|euros?|€|JPY|yen|¥|GBP|pounds?|£|CAD|CAD\$|AUD|AUD\$)/i';
860:
861: preg_match_all($pattern, $emailBody, $matches);
862:
863: if ($matches) {
864: return array_map(function($value) {
865: return floatval(preg_replace('/[^0-9]/', '', $value));
866: }, $matches[0]);
867: }
868: }
869:
870: }
871:
872: /**
873: * Build a list of IMAP servers as the top level folders
874: * @subpackage imap/handler
875: */
876: class Hm_Handler_load_imap_folders extends Hm_Handler_Module {
877: /**
878: * Used by the folder list
879: */
880: public function process() {
881: $servers = Hm_IMAP_List::dump();
882: $folders = array();
883: if (!empty($servers)) {
884: foreach ($servers as $id => $server) {
885: $folders[$id] = $server['name'];
886: }
887: }
888: $this->out('imap_folders', $folders);
889: }
890: }
891:
892: /**
893: * Delete a message
894: * @subpackage imap/handler
895: */
896: class Hm_Handler_imap_delete_message extends Hm_Handler_Module {
897: /**
898: * Use IMAP to delete the selected message uid
899: */
900: public function process() {
901: list($success, $form) = $this->process_form(array('imap_msg_uid', 'imap_server_id', 'folder'));
902: if ($success) {
903: $del_result = false;
904: $trash_folder = false;
905: $specials = get_special_folders($this, $form['imap_server_id']);
906: if (array_key_exists('trash', $specials) && $specials['trash']) {
907: $trash_folder = $specials['trash'];
908: }
909: $mailbox = Hm_IMAP_List::get_connected_mailbox($form['imap_server_id'], $this->cache);
910: if ($mailbox && $mailbox->authed()) {
911: if ($mailbox->delete_message(hex2bin($form['folder']), $form['imap_msg_uid'], $trash_folder)) {
912: $del_result = true;
913: }
914: $this->out('folder_status', array('imap_'.$form['imap_server_id'].'_'.$form['folder'] => $mailbox->get_folder_state()));
915: }
916: if (!$del_result) {
917: Hm_Msgs::add('An error occurred trying to delete this message', 'danger');
918: $this->out('imap_delete_error', true);
919: }
920: else {
921: Hm_Msgs::add('Message deleted');
922: $this->out('imap_delete_error', false);
923: }
924: }
925: }
926: }
927:
928:
929: /**
930: * Archive a message
931: * @subpackage imap/handler
932: */
933: class Hm_Handler_imap_archive_message extends Hm_Handler_Module {
934: /**
935: * Use IMAP to archive the selected message uid
936: */
937: public function process() {
938: list($success, $form) = $this->process_form(array('imap_msg_uid', 'imap_server_id', 'folder'));
939:
940: if (!$success) {
941: return;
942: }
943:
944: $archive_folder = false;
945: $form_folder = hex2bin($form['folder']);
946: $errors = 0;
947: $status = null;
948:
949: $specials = get_special_folders($this, $form['imap_server_id']);
950: if (array_key_exists('archive', $specials) && $specials['archive']) {
951: $archive_folder = $specials['archive'];
952: }
953:
954: $mailbox = Hm_IMAP_List::get_connected_mailbox($form['imap_server_id'], $this->cache);
955: if ($mailbox && ! $mailbox->is_imap()) {
956: // EWS supports archiving to user archive folders
957: $status = $mailbox->message_action($form_folder, 'ARCHIVE', array($form['imap_msg_uid']))['status'];
958: } else {
959: if (!$archive_folder) {
960: Hm_Msgs::add('No archive folder configured for this IMAP server', 'warning');
961: $errors++;
962: }
963:
964: if (! $errors && $mailbox && $mailbox->authed()) {
965: $archive_exists = count($mailbox->get_folder_status($archive_folder));
966: if (!$archive_exists) {
967: Hm_Msgs::add('Configured archive folder for this IMAP server does not exist', 'warning');
968: $errors++;
969: }
970:
971: /* path according to original option setting */
972: if ($this->user_config->get('original_folder_setting', false)) {
973: $archive_folder .= '/' . $form_folder;
974: if (!count($mailbox->get_folder_status($archive_folder))) {
975: if (! $mailbox->create_folder($archive_folder)) {
976: $debug = $mailbox->get_debug();
977: if (! empty($debug['debug'])) {
978: Hm_Msgs::add(array_pop($debug['debug']), 'danger');
979: } else {
980: Hm_Msgs::add('Could not create configured archive folder for the original folder of the message', 'danger');
981: }
982: $errors++;
983: }
984: }
985: }
986:
987: /* try to move the message */
988: if (! $errors) {
989: $status = $mailbox->message_action($form_folder, 'MOVE', array($form['imap_msg_uid']), $archive_folder)['status'];
990: }
991: }
992: }
993:
994: if ($status) {
995: Hm_Msgs::add("Message archived");
996: } else {
997: Hm_Msgs::add('An error occurred archiving the message', 'danger');
998: }
999:
1000: $this->save_hm_msgs();
1001: }
1002: }
1003:
1004: /**
1005: * Flag a message
1006: * @subpackage imap/handler
1007: */
1008: class Hm_Handler_flag_imap_message extends Hm_Handler_Module {
1009: /**
1010: * Use IMAP to flag the selected message uid
1011: */
1012: public function process() {
1013: list($success, $form) = $this->process_form(array('imap_flag_state', 'imap_msg_uid', 'imap_server_id', 'folder'));
1014: if ($success) {
1015: $flag_result = false;
1016: $mailbox = Hm_IMAP_List::get_connected_mailbox($form['imap_server_id'], $this->cache);
1017: if ($mailbox && $mailbox->authed()) {
1018: if ($form['imap_flag_state'] == 'flagged') {
1019: $cmd = 'UNFLAG';
1020: }
1021: else {
1022: $cmd = 'FLAG';
1023: }
1024: if ($mailbox->message_action(hex2bin($form['folder']), $cmd, array($form['imap_msg_uid']))['status']) {
1025: $flag_result = true;
1026: }
1027: $this->out('folder_status', array('imap_'.$form['imap_server_id'].'_'.$form['folder'] => $mailbox->get_folder_state()));
1028: }
1029: if (!$flag_result) {
1030: Hm_Msgs::add('An error occurred trying to flag this message', 'danger');
1031: }
1032: }
1033: }
1034: }
1035:
1036: /**
1037: * Snooze message
1038: * @subpackage imap/handler
1039: */
1040: class Hm_Handler_imap_snooze_message extends Hm_Handler_Module {
1041: /**
1042: * Use IMAP to snooze the selected message uid
1043: */
1044: public function process() {
1045: if ($this->should_skip_execution('enable_snooze_setting', DEFAULT_ENABLE_SNOOZE)) return;
1046:
1047: list($success, $form) = $this->process_form(array('imap_snooze_ids', 'imap_snooze_until'));
1048: if (!$success) {
1049: return;
1050: }
1051: $snoozed_messages = [];
1052: $snooze_tag = null;
1053: if ($form['imap_snooze_until'] != 'unsnooze') {
1054: $at = date('D, d M Y H:i:s O');
1055: $until = get_scheduled_date($form['imap_snooze_until']);
1056: $snooze_tag = "X-Snoozed: at $at; until $until";
1057: }
1058: $ids = explode(',', $form['imap_snooze_ids']);
1059: foreach ($ids as $msg_part) {
1060: list($imap_server_id, $msg_id, $folder) = explode('_', $msg_part);
1061: $mailbox = Hm_IMAP_List::get_connected_mailbox($imap_server_id, $this->cache);
1062: if ($mailbox && $mailbox->authed()) {
1063: $folder = hex2bin($folder);
1064: if (snooze_message($mailbox, $msg_id, $folder, $snooze_tag)) {
1065: $snoozed_messages[] = $msg_id;
1066: }
1067: }
1068: }
1069: $this->out('snoozed_messages', $snoozed_messages);
1070: $type = 'success';
1071: if (count($snoozed_messages) == count($ids)) {
1072: $msg = 'Messages snoozed';
1073: } elseif (count($snoozed_messages) > 0) {
1074: $msg = 'Some messages have been snoozed';
1075: $type = 'warning';
1076: } else {
1077: $msg = 'Failed to snooze selected messages';
1078: $type = 'danger';
1079: }
1080: Hm_Msgs::add($msg, $type);
1081: }
1082: }
1083:
1084: /**
1085: * Unsnooze messages
1086: * @subpackage imap/handler
1087: */
1088: class Hm_Handler_imap_unsnooze_message extends Hm_Handler_Module {
1089: /**
1090: * Use IMAP unsnooze messages in snoozed directory
1091: * This should use cron
1092: */
1093: public function process() {
1094: if ($this->should_skip_execution('enable_snooze_setting', DEFAULT_ENABLE_SNOOZE)) return;
1095:
1096: $servers = Hm_IMAP_List::dump();
1097: foreach (array_keys($servers) as $server_id) {
1098: $mailbox = Hm_IMAP_List::get_connected_mailbox($server_id, $this->cache);
1099: if ($mailbox && $mailbox->authed()) {
1100: $folder = 'Snoozed';
1101: $status = $mailbox->get_folder_status($folder);
1102: if (! count($status)) {
1103: continue;
1104: }
1105: $folder = $status['id'];
1106: $ret = $mailbox->get_messages($folder, 'DATE', false, 'ALL');
1107: foreach ($ret[1] as $msg) {
1108: $msg_headers = $mailbox->get_message_headers($folder, $msg['uid']);
1109: if (isset($msg_headers['X-Snoozed'])) {
1110: try {
1111: $snooze_headers = parse_delayed_header($msg_headers['X-Snoozed'], 'X-Snoozed');
1112: if (new DateTime($snooze_headers['until']) <= new DateTime()) {
1113: snooze_message($mailbox, $msg['uid'], $folder, null);
1114: }
1115: } catch (Exception $e) {
1116: Hm_Debug::add(sprintf('Cannot unsnooze message: %s', $msg_headers['subject']));
1117: }
1118: }
1119: }
1120: }
1121: }
1122: }
1123: }
1124:
1125: /**
1126: * Perform an IMAP message action
1127: * @subpackage imap/handler
1128: */
1129: class Hm_Handler_imap_message_action extends Hm_Handler_Module {
1130: /**
1131: * Read, unread, delete, flag, unflag, archive, or mark as junk a set of message uids
1132: */
1133: public function process() {
1134: list($success, $form) = $this->process_form(array('action_type', 'message_ids'));
1135: if ($success) {
1136: if (in_array($form['action_type'], array('delete', 'read', 'unread', 'flag', 'unflag', 'archive', 'junk'))) {
1137: $ids = process_imap_message_ids($form['message_ids']);
1138: $errs = 0;
1139: $msgs = 0;
1140: $moved = array();
1141: $status = array();
1142: foreach ($ids as $server => $folders) {
1143: $specials = get_special_folders($this, $server);
1144: $mailbox = Hm_IMAP_List::get_connected_mailbox($server, $this->cache);
1145: if ($mailbox && $mailbox->authed()) {
1146: $server_details = $this->user_config->get('imap_servers')[$server];
1147:
1148: foreach ($folders as $folder => $uids) {
1149: $status['imap_'.$server.'_'.$folder] = $mailbox->get_folder_state();
1150: $action_result = $this->perform_action($mailbox, $form['action_type'], $uids, $folder, $specials, $server_details);
1151: if ($action_result['error'] && ! $action_result['folder_not_found_error']) {
1152: $errs++;
1153: } else {
1154: $msgs += count($uids);
1155: $moved = array_merge($moved, $action_result['moved']);
1156: }
1157: }
1158: }
1159: }
1160: if ($errs > 0) {
1161: Hm_Msgs::add(sprintf('An error occurred trying to %s some messages!', $form['action_type'], $server), 'danger');
1162: }
1163: $this->out('move_count', $moved);
1164: if (count($status) > 0) {
1165: $this->out('folder_state', $status);
1166: }
1167: }
1168: }
1169: }
1170:
1171: /**
1172: * Perform a specified action on a set of messages in a mailbox.
1173: *
1174: * This function processes messages based on the provided action type (e.g., 'move', 'delete'),
1175: * moving them to a special folder if necessary, or performing an operation like expunging deleted messages.
1176: * It handles creating folders, moving messages to a special folder, and managing message status accordingly.
1177: *
1178: * @param object $mailbox The mailbox object used to perform actions.
1179: * @param string $action_type The type of action to perform (e.g., 'move', 'delete').
1180: * @param array $uids The unique identifiers (UIDs) of the messages to act upon.
1181: * @param string $folder The folder where the messages currently reside.
1182: * @param array $specials Special folder information for handling specific actions.
1183: * @param array $server_details Details of the server, including its unique ID and settings.
1184: *
1185: * @return array Returns an associative array with:
1186: * - 'error' => bool Indicates if an error occurred during the operation.
1187: * - 'moved' => array List of moved message identifiers in a specific format.
1188: */
1189: private function perform_action($mailbox, $action_type, $uids, $folder, $specials, $server_details) {
1190: $error = false;
1191: $moved = array();
1192: $folder_name = hex2bin($folder);
1193: $special_folder = $this->get_special_folder($action_type, $specials, $server_details);
1194:
1195: if ($special_folder && $special_folder != $folder_name) {
1196: if ($this->user_config->get('original_folder_setting', false)) {
1197: $special_folder .= '/' . $folder_name;
1198: if (!count($mailbox->get_folder_status($special_folder))) {
1199: $mailbox->create_folder($special_folder);
1200: }
1201: }
1202: if (!$mailbox->message_action($folder_name, 'MOVE', $uids, $special_folder)['status']) {
1203: $error = true;
1204: } else {
1205: foreach ($uids as $uid) {
1206: $moved[] = sprintf("imap_%s_%s_%s", $server_details['id'], $uid, $folder);
1207: }
1208: }
1209: } else {
1210: if (!$mailbox->message_action($folder_name, mb_strtoupper($action_type), $uids)['status']) {
1211: $error = true;
1212: } else {
1213: foreach ($uids as $uid) {
1214: $moved[] = sprintf("imap_%s_%s_%s", $server_details['id'], $uid, $folder);
1215: }
1216: if ($action_type == 'delete') {
1217: $mailbox->message_action($folder_name, 'EXPUNGE', $uids);
1218: }
1219: }
1220: }
1221:
1222: $folderNotFoundError = false;
1223: if (!$special_folder && $action_type != 'read' && $action_type != 'unread' && $action_type != 'flag' && $action_type != 'unflag') {
1224: Hm_Msgs::add(sprintf('No %s folder configured for %s. Please go to <a href="?page=folders&imap_server_id=%s">Folders seetting</a> and configure one', $action_type, $server_details['name'], $server_details['id']), empty($moved) ? 'danger' : 'warning');
1225: $folderNotFoundError = true;
1226: }
1227:
1228: return ['error' => $error, 'moved' => $moved, 'folder_not_found_error' => $folderNotFoundError];
1229: }
1230:
1231: /**
1232: * Retrieves the special folder associated with a specific action type.
1233: *
1234: * This function checks the given action type (e.g., 'delete', 'archive', 'junk') and looks for a corresponding
1235: * special folder from the provided special folders list. If the folder is not found for the action, it logs an
1236: * error message, unless the action type is one of 'read', 'unread', 'flag', or 'unflag'.
1237: *
1238: * @param string $action_type The action type that determines which special folder to retrieve (e.g., 'delete', 'archive').
1239: * @param array $specials An associative array of special folder names, like 'trash', 'archive', and 'junk'.
1240: * @param array $server_details Details of the server, including its name.
1241: *
1242: * @return string|false Returns the special folder name if found, or false if no corresponding folder is configured.
1243: */
1244: private function get_special_folder($action_type, $specials, $server_details) {
1245: $folder = false;
1246: if ($action_type == 'delete' && array_key_exists('trash', $specials)) {
1247: $folder = $specials['trash'];
1248: } elseif ($action_type == 'archive' && array_key_exists('archive', $specials)) {
1249: $folder = $specials['archive'];
1250: } elseif ($action_type == 'junk' && array_key_exists('junk', $specials)) {
1251: $folder = $specials['junk'];
1252: }
1253: return $folder;
1254: }
1255: }
1256:
1257: /**
1258: * Search for a message
1259: * @subpackage imap/handler
1260: */
1261: class Hm_Handler_imap_search extends Hm_Handler_Module {
1262: /**
1263: * Use IMAP SEARCH to find matching messages
1264: */
1265: public function process() {
1266: list($success, $form) = $this->process_form(array('imap_server_ids'));
1267: if ($success) {
1268: $terms = validate_search_terms($this->request->get['search_terms']);
1269: $since = isset($this->request->get['search_since']) ? process_since_argument($this->request->get['search_since'], true): DEFAULT_SEARCH_SINCE;
1270: $fld = isset($this->request->get['search_fld']) ? validate_search_fld($this->request->get['search_fld']): DEFAULT_SEARCH_FLD;
1271: $ids = explode(',', $form['imap_server_ids']);
1272: $date = process_since_argument($since);
1273: $folder = bin2hex('INBOX');
1274: if (array_key_exists('folder', $this->request->post)) {
1275: $folder = $this->request->post['folder'];
1276: }
1277: list($status, $msg_list) = merge_imap_search_results($ids, 'ALL', $this->session, $this->cache, array(hex2bin($folder)), MAX_PER_SOURCE, array(array(search_since_based_on_setting($this->user_config), $date), array($fld, $terms)));
1278: $this->out('imap_search_results', $msg_list);
1279: $this->out('folder_status', $status);
1280: $this->out('imap_server_ids', $form['imap_server_ids']);
1281: }
1282: }
1283: }
1284:
1285: /**
1286: * Get message headers for the Everthing page
1287: * @subpackage imap/handler
1288: */
1289: class Hm_Handler_imap_message_list extends Hm_Handler_Module {
1290: /**
1291: * Returns list of message data for the Everthing page
1292: */
1293: public function process() {
1294: $defaultGetParams = [
1295: 'list_page' => 1,
1296: 'sort' => 'arrival',
1297: ];
1298: $this->request->get = array_merge($defaultGetParams, $this->request->get);
1299:
1300: list($success, $form) = $this->process_form(array('imap_server_ids', 'imap_folder_ids'));
1301:
1302: if ($success) {
1303: $ids = explode(',', $form['imap_server_ids']);
1304: $folders = explode(',', $form['imap_folder_ids']);
1305: } else {
1306: $userCustomSources = $this->session->get('custom_imap_sources', user:true);
1307: if (! $userCustomSources) {
1308: $userCustomSources = [];
1309: }
1310: $data_sources = imap_data_sources($userCustomSources);
1311: $ids = array_map(function($ds) { return $ds['id']; }, $data_sources);
1312: $folders = array_map(function($ds) { return $ds['folder']; }, $data_sources);
1313: }
1314:
1315: list($sort, $reverse) = process_sort_arg($this->request->get['sort'], $this->user_config->get('default_sort_order_setting', 'arrival'));
1316:
1317: if (isset($this->request->post['list_path'])) {
1318: $list_path = $this->request->post['list_path'];
1319: } else {
1320: $list_path = $this->get('list_path');
1321: }
1322:
1323: switch ($list_path) {
1324: case 'email':
1325: $filter = 'ALL';
1326: $limit = $this->user_config->get('all_email_per_source_setting', DEFAULT_ALL_EMAIL_PER_SOURCE);
1327: $date = process_since_argument($this->user_config->get('all_email_since_setting', DEFAULT_SINCE));
1328: break;
1329: case 'combined_inbox':
1330: $filter = 'ALL';
1331: $limit = $this->user_config->get('all_per_source_setting', DEFAULT_ALL_EMAIL_PER_SOURCE);
1332: $date = process_since_argument($this->user_config->get('all_since_setting', DEFAULT_SINCE));
1333: break;
1334: case 'flagged':
1335: case 'unread':
1336: $filter = $list_path == 'unread' ? 'UNSEEN' : mb_strtoupper($list_path);
1337: default:
1338: if (empty($filter)) {
1339: $filter = 'ALL';
1340: }
1341: if ($list_path) {
1342: $limit = $this->user_config->get($list_path.'_per_source_setting', DEFAULT_PER_SOURCE);
1343: $date = process_since_argument($this->user_config->get($list_path.'_since_setting', DEFAULT_SINCE));
1344: } else {
1345: $limit = $this->user_config->get('all_per_source_setting', DEFAULT_ALL_PER_SOURCE);
1346: $date = process_since_argument($this->user_config->get('all_since_setting', DEFAULT_SINCE));
1347: }
1348: }
1349:
1350: if ($this->get('list_filter')) {
1351: $filter = mb_strtoupper($this->get('list_filter'));
1352: }
1353:
1354: $terms = [[search_since_based_on_setting($this->user_config), $date]];
1355:
1356: $messages = [];
1357: $status = [];
1358: foreach ($ids as $key => $id) {
1359: $details = Hm_IMAP_List::dump($id);
1360: $mailbox = Hm_IMAP_List::get_connected_mailbox($id, $this->cache);
1361: if($this->get('list_path') == 'snoozed' && !$mailbox->folder_exists('Snoozed')) {
1362: continue;
1363: }
1364: $uids = $mailbox->search(hex2bin($folders[$key]), $filter, $terms, $sort, $reverse);
1365:
1366: $total = count($uids);
1367: $uids = array_slice($uids, 0, $limit);
1368:
1369: $headers = $mailbox->get_message_list(hex2bin($folders[$key]), $uids);
1370: foreach ($uids as $uid) {
1371: if (isset($headers[$uid])) {
1372: $msg = $headers[$uid];
1373: } elseif (isset($headers[bin2hex($uid)])) {
1374: $msg = $headers[bin2hex($uid)];
1375: } else {
1376: continue;
1377: }
1378: $msg['server_id'] = $id;
1379: $msg['server_name'] = $details['name'];
1380: $msg['folder'] = $folders[$key];
1381: $messages[] = $msg;
1382: }
1383:
1384: $status['imap_'.$id.'_'.$folders[$key]] = $mailbox->get_folder_state(); // this is faster than get_folder_status as search call above already gets this folder's state
1385: }
1386:
1387: $this->out('folder_status', $status);
1388: $this->out('imap_message_list_data', $messages);
1389: $this->out('imap_server_ids', implode(',', $ids));
1390: }
1391: }
1392:
1393: /**
1394: * Check the status of an IMAP server connection
1395: * @subpackage imap/handler
1396: */
1397: class Hm_Handler_imap_status extends Hm_Handler_Module {
1398: /**
1399: * Output used on the info page to display the server status
1400: */
1401: public function process() {
1402: list($success, $form) = $this->process_form(array('imap_server_ids'));
1403: if ($success) {
1404: $ids = explode(',', $form['imap_server_ids']);
1405: foreach ($ids as $id) {
1406: $start_time = microtime(true);
1407: $mailbox = Hm_IMAP_List::get_connected_mailbox($id, $this->cache);
1408: $this->out('imap_connect_time', microtime(true) - $start_time);
1409: if ($mailbox && $mailbox->authed()) {
1410: $this->out('imap_capabilities_list', $mailbox->get_capability());
1411: $this->out('imap_connect_status', $mailbox->get_state());
1412: $this->out('imap_status_server_id', $id);
1413: }
1414: else {
1415: $this->out('imap_capabilities_list', "");
1416: $this->out('imap_connect_status', 'disconnected');
1417: $this->out('imap_status_server_id', $id);
1418: }
1419: }
1420: }
1421: }
1422: }
1423:
1424: /**
1425: * Add a new JMAP server
1426: * @subpackage imap/handler
1427: */
1428: class Hm_Handler_process_add_jmap_server extends Hm_Handler_Module {
1429: public function process() {
1430: /**
1431: * Used on the servers page to add a new JMAP server
1432: */
1433: if (isset($this->request->post['submit_jmap_server'])) {
1434: list($success, $form) = $this->process_form(array('new_jmap_name', 'new_jmap_address'));
1435: if (!$success) {
1436: $this->out('old_form', $form);
1437: Hm_Msgs::add('You must supply a name and a JMAP server URL', 'warning');
1438: return;
1439: }
1440: $hidden = false;
1441: if (isset($this->request->post['new_jmap_hidden'])) {
1442: $hidden = true;
1443: }
1444: $parsed = parse_url($form['new_jmap_address']);
1445: if (array_key_exists('host', $parsed) && @get_headers($form['new_jmap_address'])) {
1446:
1447: Hm_IMAP_List::add(array(
1448: 'name' => $form['new_jmap_name'],
1449: 'server' => $form['new_jmap_address'],
1450: 'hide' => $hidden,
1451: 'type' => 'jmap',
1452: 'port' => false,
1453: 'tls' => false));
1454: Hm_Msgs::add("Added server!. To preserve these settings after logout, please go to <a class='alert-link' href='/?page=save'>Save Settings</a>.");
1455: $this->session->record_unsaved('JMAP server added');
1456: }
1457: else {
1458: Hm_Msgs::add('Could not access supplied URL', 'warning');
1459: }
1460: }
1461: }
1462: }
1463:
1464: /**
1465: * Add a new IMAP server
1466: * @subpackage imap/handler
1467: */
1468: class Hm_Handler_process_add_imap_server extends Hm_Handler_Module {
1469: public function process() {
1470: /**
1471: * Used on the servers page to add a new IMAP server
1472: */
1473: if (isset($this->request->post['submit_imap_server'])) {
1474: list($success, $form) = $this->process_form(
1475: array('new_imap_name',
1476: 'new_imap_address',
1477: 'new_imap_port')
1478: );
1479: if (!$success) {
1480: $this->out('old_form', $form);
1481: Hm_Msgs::add('You must supply a name, a server and a port', 'warning');
1482: }
1483: else {
1484: $tls = false;
1485: if (array_key_exists('tls', $this->request->post) && $this->request->post['tls']) {
1486: $tls = true;
1487: }
1488: $hidden = false;
1489: if (isset($this->request->post['new_imap_hidden'])) {
1490: $hidden = true;
1491: }
1492: if ($con = fsockopen($form['new_imap_address'], $form['new_imap_port'], $errno, $errstr, 5)) {
1493: $imap_list = array(
1494: 'name' => $form['new_imap_name'],
1495: 'server' => $form['new_imap_address'],
1496: 'hide' => $hidden,
1497: 'port' => $form['new_imap_port'],
1498: 'tls' => $tls);
1499:
1500: if (isset($this->request->post['sieve_config_host']) && $this->request->post['sieve_config_host']) {
1501: $imap_list['sieve_config_host'] = $this->request->post['sieve_config_host'];
1502: }
1503: Hm_IMAP_List::add($imap_list);
1504: Hm_Msgs::add('Added server!');
1505: $this->session->record_unsaved('IMAP server added');
1506: }
1507: else {
1508: Hm_Msgs::add(sprintf('Could not add server: %s', $errstr), 'danger');
1509: }
1510: }
1511: }
1512: $this->out('is_jmap_supported', $this->module_is_supported('jmap'));
1513: }
1514: }
1515:
1516: /**
1517: * Save IMAP caches in the session
1518: * @subpackage imap/handler
1519: */
1520: class Hm_Handler_save_imap_cache extends Hm_Handler_Module {
1521: /**
1522: * Save IMAP cache data for re-use
1523: */
1524: public function process() {
1525: $servers = Hm_IMAP_List::dump(false, true);
1526: $cache = array();
1527: foreach ($servers as $index => $server) {
1528: if (isset($server['object']) && is_object($server['object'])) {
1529: if ($server['object']->use_cache()) {
1530: $cache[$index] = $server['object']->dump_cache('array');
1531: }
1532: }
1533: }
1534: if (count($cache) > 0) {
1535: foreach ($cache as $id => $data) {
1536: $this->cache->set('imap'.$id, $cache[$id]);
1537: }
1538: }
1539: }
1540: }
1541:
1542: /**
1543: * Save EWS server details
1544: * @subpackage imap/handler
1545: */
1546: class Hm_Handler_save_ews_server extends Hm_Handler_Module {
1547: public function process() {
1548: list($success, $form) = $this->process_form(array(
1549: 'ews_profile_name',
1550: 'ews_email',
1551: 'ews_password',
1552: 'ews_server',
1553: 'ews_server_id',
1554: 'ews_hide_from_c_page',
1555: 'ews_create_profile',
1556: 'ews_profile_signature',
1557: 'ews_profile_reply_to',
1558: 'ews_profile_is_default',
1559: ));
1560: if ($success) {
1561: $imap_server_id = connect_to_imap_server(
1562: $form['ews_server'],
1563: $form['ews_profile_name'],
1564: null,
1565: $form['ews_email'],
1566: $form['ews_password'],
1567: null,
1568: null,
1569: null,
1570: 'ews',
1571: $this,
1572: $form['ews_hide_from_c_page'],
1573: $form['ews_server_id'],
1574: );
1575: if(empty($imap_server_id)) {
1576: Hm_Msgs::add("Could not save EWS server", 'danger');
1577: return;
1578: }
1579: $smtp_server_id = connect_to_smtp_server(
1580: $form['ews_server'],
1581: $form['ews_profile_name'],
1582: null,
1583: $form['ews_email'],
1584: $form['ews_password'],
1585: null,
1586: 'ews',
1587: $form['ews_server_id'],
1588: );
1589: if ($form['ews_create_profile'] && $imap_server_id && $smtp_server_id) {
1590: if (! strstr($form['ews_email'], '@')) {
1591: $address = $form['ews_email'] . '@' . $form['ews_server'];
1592: } else {
1593: $address = $form['ews_email'];
1594: }
1595: add_profile($form['ews_profile_name'], $form['ews_profile_signature'], $form['ews_profile_reply_to'], $form['ews_profile_is_default'], $address, $form['ews_server'], $form['ews_email'], $smtp_server_id, $imap_server_id, $this);
1596: }
1597: // auto-assign special folders
1598: $mailbox = Hm_IMAP_List::get_connected_mailbox($imap_server_id, $this->cache);
1599: if (is_object($mailbox) && $mailbox->authed()) {
1600: $specials = $this->user_config->get('special_imap_folders', array());
1601: $exposed = $mailbox->get_special_use_mailboxes();
1602: $specials[$imap_server_id] = [
1603: 'sent' => $exposed['sent'] ?? '',
1604: 'draft' => $exposed['drafts'] ?? '',
1605: 'trash' => $exposed['trash'] ?? '',
1606: 'archive' => $exposed['archive'] ?? '',
1607: 'junk' => $exposed['junk'] ?? ''
1608: ];
1609: $this->user_config->set('special_imap_folders', $specials);
1610: }
1611: Hm_Msgs::add("EWS server saved. To preserve these settings after logout, please go to <a class='alert-link' href='/?page=save'>Save Settings</a>.");
1612: $this->session->record_unsaved('EWS server added');
1613: $this->session->secure_cookie($this->request, 'hm_reload_folders', '1');
1614: }
1615: }
1616: }
1617:
1618: /**
1619: * Save IMAP servers
1620: * @subpackage imap/handler
1621: */
1622: class Hm_Handler_save_imap_servers extends Hm_Handler_Module {
1623: /**
1624: * Save IMAP servers in the user config
1625: */
1626: public function process() {
1627: Hm_IMAP_List::save();
1628: Hm_IMAP_List::clean_up();
1629: }
1630: }
1631:
1632: /**
1633: * Load IMAP servers for message list pages
1634: * @subpackage imap/handler
1635: */
1636: class Hm_Handler_load_imap_servers_for_message_list extends Hm_Handler_Module {
1637: /**
1638: * Used by combined views excluding normal folder view and search pages
1639: */
1640: public function process() {
1641: if (array_key_exists('list_path', $this->request->get)) {
1642: $path = $this->request->get['list_path'];
1643: $this->out('move_copy_controls', true);
1644: }
1645: else {
1646: $path = '';
1647: }
1648: if (in_array($path, ['sent', 'junk', 'snoozed','trash', 'drafts'])) {
1649: foreach (imap_sources($this, $path) as $vals) {
1650: $this->append('data_sources', $vals);
1651: }
1652: }
1653: else {
1654: foreach (imap_data_sources($this->user_config->get('custom_imap_sources', array())) as $vals) {
1655: $this->append('data_sources', $vals);
1656: }
1657: }
1658: }
1659: }
1660: /**
1661: * Load IMAP servers permissions for shared folders
1662: * @subpackage imap/handler
1663: */
1664: class Hm_Handler_load_imap_folders_permissions extends Hm_Handler_Module {
1665: /**
1666: * Output IMAP server permissions array for shared folders
1667: */
1668: public function process() {
1669: list($success, $form) = $this->process_form(array('imap_server_id','imap_folder_uid','imap_folder'));
1670:
1671: if ($success && !empty($form['imap_server_id']) && !empty($form['imap_folder']) && !empty($form['imap_folder_uid'])) {
1672: Hm_IMAP_List::init($this->user_config, $this->session);
1673: $server = Hm_IMAP_List::dump($form['imap_server_id'], true);
1674: $cache = Hm_IMAP_List::get_cache($this->cache, $form['imap_server_id']);
1675:
1676: $imap = Hm_IMAP_List::connect($form['imap_server_id'], $cache, $server['user'], $server['pass']);
1677: $permissions = $imap->get_acl($form['imap_folder']);
1678: $this->out('imap_folders_permissions', $permissions);
1679: }
1680: }
1681: }
1682:
1683: /**
1684: * Load IMAP servers permissions for shared folders
1685: * @subpackage imap/handler
1686: */
1687: class Hm_Handler_set_acl_to_imap_folders extends Hm_Handler_Module {
1688: /**
1689: * Output IMAP server permissions array for shared folders
1690: */
1691: public function process() {
1692: list($success, $form) = $this->process_form(array('imap_server_id','imap_folder','identifier','permissions','action'));
1693:
1694: if ($success && !empty($form['imap_server_id']) && !empty($form['identifier']) && !empty($form['permissions']) && !empty($form['action'])) {
1695:
1696: Hm_IMAP_List::init($this->user_config, $this->session);
1697: $server = Hm_IMAP_List::dump($form['imap_server_id'], true);
1698: $cache = Hm_IMAP_List::get_cache($this->cache, $form['imap_server_id']);
1699:
1700: $imap = Hm_IMAP_List::connect($form['imap_server_id'], $cache, $server['user'], $server['pass']);
1701: if($form['action'] === 'add') {
1702: $response = $imap->set_acl($form['imap_folder'], $form['identifier'], $form['permissions']);
1703: } else {
1704: $response = $imap->delete_acl($form['imap_folder'], $form['identifier']);
1705: }
1706: if($response) {
1707: $permissions = $imap->get_acl($form['imap_folder']);
1708: $this->out('imap_folders_permissions', $permissions);
1709: }
1710: }
1711: }
1712: }
1713:
1714: /**
1715: * Load IMAP servers for the user config object
1716: * @subpackage imap/handler
1717: */
1718: class Hm_Handler_load_imap_servers_from_config extends Hm_Handler_Module {
1719: /**
1720: * This list is cached in the session between page loads by Hm_Handler_save_imap_servers
1721: */
1722: public function process() {
1723: Hm_IMAP_List::init($this->user_config, $this->session);
1724: $default_server_id = false;
1725: foreach (Hm_IMAP_List::getAll() as $id => $server) {
1726: if ($this->session->loaded) {
1727: if (array_key_exists('expiration', $server)) {
1728: $server['expiration'] = 1;
1729: Hm_IMAP_List::edit($id, $server);
1730: }
1731: }
1732: if (array_key_exists('default', $server) && $server['default']) {
1733: $default_server_id = $id;
1734: }
1735: }
1736: $auth_server = $this->session->get('imap_auth_server_settings', array());
1737: if (!empty($auth_server)) {
1738: if (array_key_exists('name', $auth_server)) {
1739: $name = $auth_server['name'];
1740: }
1741: else {
1742: $name = $this->config->get('imap_auth_name', 'Default');
1743: }
1744: $imap_details = array(
1745: 'name' => $name,
1746: 'default' => true,
1747: 'server' => $auth_server['server'],
1748: 'port' => $auth_server['port'],
1749: 'tls' => $auth_server['tls'],
1750: 'user' => $auth_server['username'],
1751: 'pass' => $auth_server['password'],
1752: 'type' => 'imap',
1753: );
1754: if (! empty($auth_server['sieve_config_host'])) {
1755: $imap_details['sieve_config_host'] = $auth_server['sieve_config_host'];
1756: $imap_details['sieve_tls'] = $auth_server['sieve_tls'];
1757: }
1758: if (!$default_server_id) {
1759: Hm_IMAP_List::add($imap_details);
1760: } else {
1761: // Perhaps something as changed
1762: Hm_IMAP_List::edit($default_server_id, $imap_details);
1763: }
1764: }
1765: }
1766: }
1767:
1768: /**
1769: * Check for IMAP server oauth2 token refresh
1770: * @subpackage imap/handler
1771: */
1772: class Hm_Handler_imap_oauth2_token_check extends Hm_Handler_Module {
1773: public function process() {
1774: $active = array();
1775: if (array_key_exists('imap_server_ids', $this->request->post)) {
1776: $active = explode(',', $this->request->post['imap_server_ids']);
1777: }
1778: if (array_key_exists('imap_server_id', $this->request->post)) {
1779: $active[] = $this->request->post['imap_server_id'];
1780: }
1781: if (count($active)===0) {
1782: $data_sources = imap_data_sources();
1783: $active = array_map(function($ds) { return $ds['id']; }, $data_sources);
1784: }
1785: $updated = 0;
1786: foreach ($active as $server_id) {
1787: $server = Hm_IMAP_List::dump($server_id, true);
1788: if ( $server && array_key_exists('auth', $server) && $server['auth'] == 'xoauth2') {
1789: $results = imap_refresh_oauth2_token($server, $this->config);
1790: if (!empty($results)) {
1791: if (Hm_IMAP_List::update_oauth2_token($server_id, $results[1], $results[0])) {
1792: Hm_Debug::add(sprintf('Oauth2 token refreshed for IMAP server id %s', $server_id), 'info');
1793: $updated++;
1794: }
1795: }
1796: }
1797: }
1798: if ($updated > 0) {
1799: Hm_IMAP_List::save();
1800: }
1801: }
1802: }
1803:
1804: /**
1805: * Set IMAP server ids to prefetch on login
1806: * @subpackage imap/handler
1807: */
1808: class Hm_Handler_prefetch_imap_folders extends Hm_Handler_Module {
1809: /**
1810: * Check for imap servers to prefetch
1811: */
1812: public function process() {
1813:
1814: $servers = array();
1815: foreach ($this->get('imap_servers', array()) as $index => $vals) {
1816: if (array_key_exists('user', $vals)) {
1817: $servers[$index] = $vals;
1818: }
1819: }
1820: if (count($servers) == 0) {
1821: return;
1822: }
1823: $fetched = $this->session->get('imap_prefetched_ids', array());
1824: $ids = array_keys($servers);
1825: if (count($fetched) > 0) {
1826: $ids = array_diff($ids, $fetched);
1827: }
1828: if (count($ids) > 0) {
1829: $this->out('prefetch_folder_ids', $ids);
1830: }
1831: }
1832: }
1833:
1834: /**
1835: * Output IMAP server data for other modules to use
1836: * @subpackage imap/handler
1837: */
1838: class Hm_Handler_add_imap_servers_to_page_data extends Hm_Handler_Module {
1839: /**
1840: * Creates folder source for the folder list and outputs IMAP server details
1841: */
1842: public function process() {
1843: $servers = Hm_IMAP_List::dump();
1844: if (!empty($servers)) {
1845: $this->out('imap_servers', $servers);
1846: }
1847: }
1848: }
1849:
1850: /**
1851: * Delete IMAP cache
1852: * @subpackage imap/handler
1853: */
1854: class Hm_Handler_imap_bust_cache extends Hm_Handler_Module {
1855: /**
1856: * Deletes all the saved IMAP cache data
1857: */
1858: public function process() {
1859: list($success, $form) = $this->process_form(array('imap_server_id'));
1860: if (!$success) {
1861: return;
1862: }
1863: $this->cache->del('imap'.$form['imap_server_id']);
1864: Hm_Debug::add(sprintf('Busted cache for IMAP server %s', $form['imap_server_id']), 'info');
1865: }
1866: }
1867:
1868: /**
1869: * Test a connection to an IMAP server
1870: * @subpackage imap/handler
1871: */
1872: class Hm_Handler_imap_connect extends Hm_Handler_Module {
1873: /**
1874: * Used by the servers page to test/authenticate with an IMAP server
1875: */
1876: public function process() {
1877: if (isset($this->request->post['imap_connect'])) {
1878: list($success, $form) = $this->process_form(array('imap_server_id'));
1879: $imap_details = Hm_IMAP_List::dump($form['imap_server_id'], true);
1880: if ($success && $imap_details) {
1881: $mailbox = false;
1882: $cache = Hm_IMAP_List::get_cache($this->cache, $form['imap_server_id']);
1883: $mailbox = Hm_IMAP_List::connect($form['imap_server_id'], $cache);
1884: if ($mailbox) {
1885: if ($mailbox->authed()) {
1886: Hm_Msgs::add(sprintf("Successfully authenticated to the %s server : %s", $mailbox->server_type(), $imap_details['user']));
1887: }
1888: else {
1889: Hm_Msgs::add(sprintf("Failed to authenticate to the %s server : %s", $mailbox->server_type(), $imap_details['user']), "danger");
1890: }
1891: }
1892: else {
1893: Hm_Msgs::add('Username and password are required', 'warning');
1894: $this->out('old_form', $form);
1895: }
1896: $this->out('imap_connect_details', $imap_details);
1897: }
1898: }
1899: }
1900: }
1901:
1902: /**
1903: * Get message content from an IMAP server
1904: * @subpackage imap/handler
1905: */
1906: class Hm_Handler_imap_message_content extends Hm_Handler_Module {
1907: /**
1908: * Fetch the content, message parts, and headers for the supplied message
1909: */
1910: public function process() {
1911: list($success, $form) = $this->process_form(array('imap_server_id', 'imap_msg_uid', 'folder'));
1912:
1913: if ($success) {
1914: $this->out('msg_text_uid', $form['imap_msg_uid']);
1915: $this->out('msg_server_id', $form['imap_server_id']);
1916: $this->out('msg_folder', $form['folder']);
1917: $this->out('msg_list_path', 'imap_'.$form['imap_server_id'].'_'.$form['folder']);
1918: $this->out('site_config', $this->config);
1919: $this->out('user_config', $this->user_config);
1920: $this->out('imap_accounts', $this->user_config->get('imap_servers'), array());
1921: $this->out('show_pagination_links', $this->user_config->get('pagination_links_setting', true));
1922: $this->out('auto_advance_email_enabled', $this->user_config->get('auto_advance_email_setting', true));
1923: $part = false;
1924: $prefetch = false;
1925: if (isset($this->request->post['imap_msg_part']) && preg_match("/[0-9\.]+/", $this->request->post['imap_msg_part'])) {
1926: $part = $this->request->post['imap_msg_part'];
1927: }
1928: elseif (isset($this->request->post['imap_prefetch']) && $this->request->post['imap_prefetch']) {
1929: $prefetch = true;
1930: }
1931:
1932: $this->out('header_allow_images', $this->config->get('allow_external_image_sources'));
1933: $this->out('images_whitelist', explode(',', $this->user_config->get('images_whitelist_setting')));
1934:
1935: $mailbox = Hm_IMAP_List::get_connected_mailbox($form['imap_server_id'], $this->cache);
1936: if ($mailbox && $mailbox->authed()) {
1937: if ($this->user_config->get('unread_on_open_setting', false)) {
1938: $mailbox->set_read_only(true);
1939: }
1940: else {
1941: $mailbox->set_read_only($prefetch);
1942: }
1943: list($msg_struct, $msg_struct_current, $msg_text, $part) = $mailbox->get_structured_message(hex2bin($form['folder']), $form['imap_msg_uid'], $part, $this->user_config->get('text_only_setting', false));
1944: $save_reply_text = false;
1945: if ($part == 0 || (isset($msg_struct_current['type']) && mb_strtolower($msg_struct_current['type'] == 'text'))) {
1946: $save_reply_text = true;
1947: }
1948: $msg_headers = $mailbox->get_message_headers(hex2bin($form['folder']), $form['imap_msg_uid']);
1949:
1950: $this->out('is_archive_folder', $mailbox->is_archive_folder($form['imap_server_id'], $this->user_config, $form['folder']));
1951: $this->out('folder_status', array('imap_'.$form['imap_server_id'].'_'.$form['folder'] => $mailbox->get_folder_state()));
1952: $this->out('msg_struct', $msg_struct);
1953: $this->out('list_headers', get_list_headers($msg_headers));
1954: $this->out('msg_headers', $msg_headers);
1955: $this->out('imap_prefetch', $prefetch);
1956: $this->out('imap_msg_part', "$part");
1957: $this->out('use_message_part_icons', $this->user_config->get('msg_part_icons_setting', false));
1958: $this->out('simple_msg_part_view', $this->user_config->get('simple_msg_parts_setting', DEFAULT_SIMPLE_MSG_PARTS));
1959: $this->out('allow_delete_attachment', $this->user_config->get('allow_delete_attachment_setting', false));
1960: if ($msg_struct_current) {
1961: $this->out('msg_struct_current', $msg_struct_current);
1962: }
1963: $this->out('msg_text', $msg_text);
1964: $download_args = sprintf("page=message&amp;uid=%s&amp;list_path=imap_%s_%s", $form['imap_msg_uid'], $form['imap_server_id'], $form['folder']);
1965: $this->out('msg_download_args', $download_args.'&amp;imap_download_message=1');
1966: $this->out('msg_attachment_remove_args', $download_args.'&amp;imap_remove_attachment=1');
1967: $this->out('msg_show_args', sprintf("page=message&amp;uid=%s&amp;list_path=imap_%s_%s&amp;imap_show_message=1", $form['imap_msg_uid'], $form['imap_server_id'], $form['folder']));
1968:
1969: if ($this->get('imap_allow_images', false)) {
1970: if ($this->module_is_supported('contacts') && $this->user_config->get('contact_auto_collect_setting', false)) {
1971: $this->out('collect_contacts', true);
1972: $this->out('collected_contact_email', $msg_headers["Return-Path"]);
1973: $this->out('collected_contact_name', $msg_headers["From"]);
1974: }
1975: }
1976:
1977: if (!$prefetch) {
1978: clear_existing_reply_details($this->session);
1979: if ($part == 0) {
1980: $msg_struct_current['type'] = 'text';
1981: $msg_struct_current['subtype'] = 'plain';
1982: }
1983: $this->session->set(sprintf('reply_details_imap_%s_%s_%s', $form['imap_server_id'], $form['folder'], $form['imap_msg_uid']),
1984: array('ts' => time(), 'msg_struct' => $msg_struct_current, 'msg_text' => ($save_reply_text ? $msg_text : ''), 'msg_headers' => $msg_headers));
1985: }
1986: }
1987: }
1988: }
1989: }
1990:
1991: /**
1992: * Store reply details for a message if not already in session
1993: * @subpackage imap/handler
1994: */
1995: class Hm_Handler_imap_store_reply_details extends Hm_Handler_Module {
1996: public function process() {
1997: if (! array_key_exists('list_path', $this->request->get) || ! array_key_exists('uid', $this->request->get)) {
1998: return;
1999: }
2000:
2001: $cache_name = sprintf('reply_details_%s_%s',
2002: $this->request->get['list_path'],
2003: $this->request->get['uid']
2004: );
2005: $reply_details = $this->session->get($cache_name, false);
2006:
2007: if ($reply_details) {
2008: return;
2009: }
2010:
2011: list($type, $server_id, $folder) = explode('_', $this->request->get['list_path']);
2012: $uid = $this->request->get['uid'];
2013:
2014: $mailbox = Hm_IMAP_List::get_connected_mailbox($server_id, $this->cache);
2015: if ($mailbox && $mailbox->authed()) {
2016: $prefetch = true;
2017: $mailbox->set_read_only($prefetch);
2018: $part = false;
2019: list($msg_struct, $msg_struct_current, $msg_text, $part) = $mailbox->get_structured_message(hex2bin($folder), $uid, $part, $this->user_config->get('text_only_setting', false));
2020: $msg_headers = $mailbox->get_message_headers(hex2bin($folder), $uid);
2021:
2022: clear_existing_reply_details($this->session);
2023: $msg_struct_current['type'] = 'text';
2024: $msg_struct_current['subtype'] = 'plain';
2025: $this->session->set(sprintf('reply_details_imap_%s_%s_%s', $server_id, $folder, $uid),
2026: array('ts' => time(), 'msg_struct' => $msg_struct_current, 'msg_text' => $msg_text, 'msg_headers' => $msg_headers));
2027: }
2028: }
2029: }
2030:
2031: /**
2032: * Get message source from an IMAP server
2033: */
2034: class Hm_Handler_imap_message_source extends Hm_Handler_Module {
2035: public function process() {
2036: $imap_server_id = $this->request->get['imap_server_id'];
2037: $imap_msg_uid = $this->request->get['imap_msg_uid'];
2038: $folder = $this->request->get['imap_folder'];
2039: if ($imap_server_id && $imap_msg_uid && $folder) {
2040: $mailbox = Hm_IMAP_List::get_connected_mailbox($imap_server_id, $this->cache);
2041: if ($mailbox && $mailbox->authed()) {
2042: $msg_source = $mailbox->get_message_content(hex2bin($folder), $imap_msg_uid);
2043: $this->out('msg_source', $msg_source);
2044: }
2045: }
2046: }
2047: }
2048:
2049: /**
2050: * Hide or unhide an IMAP server
2051: * @subpackage imap/handler
2052: */
2053: class Hm_Handler_imap_hide extends Hm_Handler_Module {
2054: /**
2055: * Hide or unhide an IMAP server from combined pages and searches
2056: */
2057: public function process() {
2058: if (isset($this->request->post['hide_imap_server'])) {
2059: list($success, $form) = $this->process_form(array('imap_server_id'));
2060: if ($success) {
2061: $action = (bool) $this->request->post['hide_imap_server'];
2062: $server_type = imap_server_type($form['imap_server_id']);
2063: Hm_IMAP_List::toggle_hidden($form['imap_server_id'], $action);
2064: if ($action) {
2065: Hm_Msgs::add(sprintf('%s server has been hidden', $server_type));
2066: } else {
2067: Hm_Msgs::add(sprintf('%s server is now visible', $server_type));
2068: }
2069: $this->session->record_unsaved(sprintf('%s server visibility updated', $server_type));
2070: }
2071: }
2072: }
2073: }
2074:
2075: /**
2076: * Delete an IMAP server
2077: * @subpackage imap/handler
2078: */
2079: class Hm_Handler_imap_delete extends Hm_Handler_Module {
2080: /**
2081: * Remove an IMAP server completely, used on the servers page
2082: */
2083: public function process() {
2084: if (isset($this->request->post['imap_delete'])) {
2085: list($success, $form) = $this->process_form(array('imap_server_id'));
2086: if ($success) {
2087: $type = imap_server_type($form['imap_server_id']);
2088: if (strtolower($type) == 'ews') {
2089: $details = Hm_IMAP_List::dump($form['imap_server_id']);
2090: foreach (Hm_Profiles::getAll() as $profile) {
2091: if ($details['user'] == $profile['user'] && $details['server'] == $profile['server']) {
2092: Hm_Profiles::del($profile['id']);
2093: Hm_SMTP_List::del($profile['smtp_id']);
2094: }
2095: }
2096: }
2097: $res = Hm_IMAP_List::del($form['imap_server_id']);
2098: if ($res) {
2099: $this->out('deleted_server_id', $form['imap_server_id']);
2100: Hm_Msgs::add('Server deleted');
2101: $this->session->record_unsaved(sprintf('%s server deleted', $type));
2102: }
2103: }
2104: else {
2105: $this->out('old_form', $form);
2106: }
2107: }
2108: }
2109: }
2110:
2111: /**
2112: * @subpackage imap/handler
2113: */
2114: class Hm_Handler_process_review_sent_email_setting extends Hm_Handler_Module {
2115: public function process() {
2116: function review_sent_email_callback($val) {
2117: return $val;
2118: }
2119: process_site_setting('review_sent_email', $this, 'review_sent_email_callback', DEFAULT_REVIEW_SENT_EMAIL, true);
2120: }
2121: }
2122:
2123: /**
2124: * Process first-time screen emails per page in the settings page
2125: * @subpackage core/handler
2126: */
2127: class Hm_Handler_process_first_time_screen_emails_per_page_setting extends Hm_Handler_Module {
2128: public function process() {
2129: function process_first_time_screen_emails_callback($val) {
2130: return $val;
2131: }
2132: process_site_setting('first_time_screen_emails', $this, 'process_first_time_screen_emails_callback');
2133: }
2134: }
2135:
2136: class Hm_Handler_process_setting_move_messages_in_screen_email extends Hm_Handler_Module {
2137: public function process() {
2138: function process_move_messages_in_screen_email_enabled_callback($val) { return $val; }
2139: process_site_setting('move_messages_in_screen_email', $this, 'process_move_messages_in_screen_email_enabled_callback', true, true);
2140: }
2141: }
2142:
2143: class Hm_Handler_process_setting_active_preview_message extends Hm_Handler_Module {
2144: public function process() {
2145: function process_active_preview_message_callback($val) { return $val; }
2146: process_site_setting('active_preview_message', $this, 'process_active_preview_message_callback', true, true);
2147: }
2148: }
2149:
2150: /**
2151: * Process setting_ceo_detection_fraud in the settings page
2152: * @subpackage core/handler
2153: */
2154: class Hm_Handler_process_setting_ceo_detection_fraud extends Hm_Handler_Module {
2155: public function process() {
2156: function process_ceo_use_detect_ceo_fraud_callback($val) { return $val; }
2157: function process_ceo_use_trusted_contact_callback($val) { return $val; }
2158: function process_ceo_suspicious_terms_callback($val) { return $val; }
2159: function process_ceo_amount_limit_callback($val) { return $val; }
2160:
2161: process_site_setting('ceo_use_detect_ceo_fraud', $this, 'process_ceo_use_detect_ceo_fraud_callback');
2162: process_site_setting('ceo_use_trusted_contact', $this, 'process_ceo_use_trusted_contact_callback');
2163: process_site_setting('ceo_suspicious_terms', $this, 'process_ceo_suspicious_terms_callback');
2164: process_site_setting('ceo_rate_limit', $this, 'process_ceo_amount_limit_callback');
2165: }
2166: }
2167: