1<?php
2
3/**
4 * IMAP modules
5 * @package modules
6 * @subpackage imap
7 */
8
9if (!defined('DEBUG_MODE')) { die(); }
10
11/**
12 * Check for attachments when forwarding a message
13 * @subpackage imap/handler
14 */
15class Hm_Handler_imap_forward_attachments extends Hm_Handler_Module {
16    public function process() {
17        if (!array_key_exists('forward', $this->request->get)) {
18            return;
19        }
20        if (!array_key_exists('list_path', $this->request->get)) {
21            return;
22        }
23        if (!array_key_exists('uid', $this->request->get)) {
24            return;
25        }
26        $uid = $this->request->get['uid'];
27        $list_path = $this->request->get['list_path'];
28        $path = explode('_', $list_path);
29        if (count($path) != 3) {
30            return;
31        }
32        if ($path[0] != 'imap') {
33            return;
34        }
35        $filepath = $this->config->get('attachment_dir');
36        if (!$filepath) {
37            return;
38        }
39        $cache = Hm_IMAP_List::get_cache($this->cache, $path[1]);
40        $imap = Hm_IMAP_List::connect($path[1], $cache);
41        if (!imap_authed($imap)) {
42            return;
43        }
44        if (!$imap->select_mailbox(hex2bin($path[2]))) {
45            return;
46        }
47        $content = $imap->get_message_content($uid, 0);
48        if (!$content) {
49            return;
50        }
51        $file = array(
52            'name' => 'mail.mime',
53            'type' => 'message/rfc822',
54            'no_encoding' => true,
55            'size' => strlen($content)
56        );
57        $draft_id = count($this->session->get('compose_drafts', array()));
58        attach_file($content, $file, $filepath, $draft_id, $this);
59    }
60}
61
62/**
63 * Get the status of an IMAP folder
64 * @subpackage imap/handler
65 */
66class 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            $cache = Hm_IMAP_List::get_cache($this->cache, $form['imap_server_id']);
71            $imap = Hm_IMAP_List::connect($form['imap_server_id'], $cache);
72            if (imap_authed($imap)) {
73                $this->out('folder_status', array('imap_'.$form['imap_server_id'].'_'.$form['folder'] => $imap->get_mailbox_status(hex2bin($form['folder']))));
74            }
75        }
76    }
77}
78
79/**
80 * Process input from the per page count setting
81 * @subpackage imap/handler
82 */
83class Hm_Handler_process_imap_per_page_setting extends Hm_Handler_Module {
84    /**
85     * Allowed values are greater than zero and less than MAX_PER_SOURCE
86     */
87    public function process() {
88        process_site_setting('imap_per_page', $this, 'max_source_setting_callback', DEFAULT_PER_SOURCE);
89    }
90}
91
92/**
93 * Process input from the max per source setting for the Sent E-mail page in the settings page
94 * @subpackage imap/handler
95 */
96class Hm_Handler_process_sent_source_max_setting extends Hm_Handler_Module {
97    /**
98     * Allowed values are greater than zero and less than MAX_PER_SOURCE
99     */
100    public function process() {
101        process_site_setting('sent_per_source', $this, 'max_source_setting_callback', DEFAULT_PER_SOURCE);
102    }
103}
104
105/**
106 * Process "simple message parts" setting for the message view page in the settings page
107 * @subpackage imap/handler
108 */
109class Hm_Handler_process_simple_msg_parts extends Hm_Handler_Module {
110    /**
111     * valid values are true or false
112     */
113    public function process() {
114        function simple_msg_view_callback($val) {
115            return $val;
116        }
117        process_site_setting('simple_msg_parts', $this, 'simple_msg_view_callback', false, true);
118    }
119}
120
121/**
122 * Process "message part icons" setting for the message view page in the settings page
123 * @subpackage imap/handler
124 */
125class Hm_Handler_process_msg_part_icons extends Hm_Handler_Module {
126    /**
127     * valid values are true or false
128     */
129    public function process() {
130        function msg_part_icons_callback($val) {
131            return $val;
132        }
133        process_site_setting('msg_part_icons', $this, 'msg_part_icons_callback', false, true);
134    }
135}
136
137/**
138 * Process "text only" setting for the message view page in the settings page
139 * @subpackage imap/handler
140 */
141class Hm_Handler_process_text_only_setting extends Hm_Handler_Module {
142    /**
143     * valid values are true or false
144     */
145    public function process() {
146        function text_only_callback($val) {
147            return $val;
148        }
149        process_site_setting('text_only', $this, 'text_only_callback', false, true);
150    }
151}
152
153/**
154 * Process "since" setting for the Sent page in the settings page
155 * @subpackage imap/handler
156 */
157class Hm_Handler_process_sent_since_setting extends Hm_Handler_Module {
158    /**
159     * valid values are defined in the process_since_argument function
160     */
161    public function process() {
162        process_site_setting('sent_since', $this, 'since_setting_callback');
163    }
164}
165
166 /**
167 * Process an IMAP move/copy action
168 * @subpackage imap/handler
169 */
170class Hm_Handler_imap_process_move extends Hm_Handler_Module {
171    public function process() {
172        list($success, $form) = $this->process_form(array('imap_move_to', 'imap_move_page', 'imap_move_action', 'imap_move_ids'));
173        if ($success) {
174            list($msg_ids, $dest_path, $same_server_ids, $other_server_ids) = process_move_to_arguments($form);
175            $moved = array();
176            if (count($same_server_ids) > 0) {
177                $moved = array_merge($moved, imap_move_same_server($same_server_ids, $form['imap_move_action'], $this->cache, $dest_path));
178            }
179            if (count($other_server_ids) > 0) {
180                $moved = array_merge($moved, imap_move_different_server($other_server_ids, $form['imap_move_action'], $dest_path, $this->cache));
181
182            }
183            if (count($moved) > 0 && count($moved) == count($msg_ids)) {
184                if ($form['imap_move_action'] == 'move') {
185                    Hm_Msgs::add('Messages moved');
186                }
187                else {
188                    Hm_Msgs::add('Messages copied');
189                }
190                $this->session->set('imap_cache', array());
191            }
192            elseif (count($moved) > 0) {
193                if ($form['imap_move_action'] == 'move') {
194                    Hm_Msgs::add('Some messages moved (only IMAP message types can be moved)');
195                }
196                else {
197                    Hm_Msgs::add('Some messages copied (only IMAP message types can be copied)');
198                }
199                $this->session->set('imap_cache', array());
200            }
201            elseif (count($moved) == 0) {
202                Hm_Msgs::add('ERRUnable to move/copy selected messages');
203            }
204            if ($form['imap_move_action'] == 'move' && $form['imap_move_page'] == 'message') {
205                $msgs = Hm_Msgs::get();
206                Hm_Msgs::flush();
207                $this->session->secure_cookie($this->request, 'hm_msgs', base64_encode(json_encode($msgs)));
208            }
209            $this->out('move_count', $moved);
210        }
211    }
212}
213
214 /**
215 * Save a sent message
216 * @subpackage imap/handler
217 */
218class Hm_Handler_imap_save_sent extends Hm_Handler_Module {
219    public function process() {
220        if (!$this->get('save_sent_msg')) {
221            return;
222        }
223        $imap_id = $this->get('save_sent_server');
224        $mime = $this->get('save_sent_msg');
225
226        if ($imap_id === false) {
227            return;
228        }
229        $msg = $mime->get_mime_msg();
230        $msg = str_replace("\r\n", "\n", $msg);
231        $msg = str_replace("\n", "\r\n", $msg);
232        $msg = rtrim($msg)."\r\n";
233        $cache = Hm_IMAP_List::get_cache($this->cache, $imap_id);
234        $imap = Hm_IMAP_List::connect($imap_id, $cache);
235        $imap_details = Hm_IMAP_List::dump($imap_id);
236        $sent_folder = false;
237        if (imap_authed($imap)) {
238            $specials = $this->user_config->get('special_imap_folders', array());
239            if (array_key_exists($imap_id, $specials)) {
240                if (array_key_exists('sent', $specials[$imap_id])) {
241                    if ($specials[$imap_id]['sent']) {
242                        $sent_folder = $specials[$imap_id]['sent'];
243                    }
244                }
245            }
246
247            if (!$sent_folder) {
248                $auto_sent = $imap->get_special_use_mailboxes('sent');
249                if (!array_key_exists('sent', $auto_sent)) {
250                    return;
251                }
252                $sent_folder = $auto_sent['sent'];
253            }
254            if (!$sent_folder) {
255                Hm_Debug::add(sprintf("Unable to save sent message, no sent folder for IMAP %s", $imap_details['server']));
256            }
257            if ($sent_folder) {
258                Hm_Debug::add(sprintf("Attempting to save sent message for IMAP server %s in folder %s", $imap_details['server'], $sent_folder));
259                if ($imap->append_start($sent_folder, strlen($msg), true)) {
260                    $imap->append_feed($msg."\r\n");
261                    if (!$imap->append_end()) {
262                        Hm_Msgs::add('ERRAn error occurred saving the sent message');
263                    }
264                }
265            }
266        }
267    }
268}
269
270 /**
271 * Unflag a message after replying to it
272 * @subpackage imap/handler
273 */
274class Hm_Handler_imap_unflag_on_send extends Hm_Handler_Module {
275    public function process() {
276        if ($this->get('msg_sent')) {
277            list($success, $form) = $this->process_form(array('compose_unflag_send', 'compose_msg_uid', 'compose_msg_path'));
278            if ($success) {
279                $path = explode('_', $form['compose_msg_path']);
280                if (count($path) == 3 && $path[0] == 'imap') {
281                    $cache = Hm_IMAP_List::get_cache($this->cache, $path[1]);
282                    $imap = Hm_IMAP_List::connect($path[1], $cache);
283                    if (imap_authed($imap) && $imap->select_mailbox(hex2bin($path[2]))) {
284                        $imap->message_action('UNFLAG', array($form['compose_msg_uid']));
285                    }
286                }
287            }
288        }
289    }
290}
291
292 /**
293 * Flag a message as answered
294 * @subpackage imap/handler
295 */
296class Hm_Handler_imap_mark_as_answered extends Hm_Handler_Module {
297    public function process() {
298        if ($this->get('msg_sent')) {
299            list($success, $form) = $this->process_form(array('compose_msg_uid', 'compose_msg_path'));
300            if ($success) {
301                $path = explode('_', $form['compose_msg_path']);
302                if (count($path) == 3 && $path[0] == 'imap') {
303                    $cache = Hm_IMAP_List::get_cache($this->cache, $path[1]);
304                    $imap = Hm_IMAP_List::connect($path[1], $cache);
305                    if (imap_authed($imap) && $imap->select_mailbox(hex2bin($path[2]))) {
306                        $this->out('folder_status', array('imap_'.$path[1].'_'.$path[2] => $imap->folder_state));
307                        $imap->message_action('ANSWERED', array($form['compose_msg_uid']));
308                    }
309                }
310            }
311        }
312    }
313}
314
315 /**
316 * Flag a message as read
317 * @subpackage imap/handler
318 */
319class Hm_Handler_imap_mark_as_read extends Hm_Handler_Module {
320    public function process() {
321        list($success, $form) = $this->process_form(array('imap_server_id', 'imap_msg_uid', 'folder'));
322        if ($success) {
323            $cache = Hm_IMAP_List::get_cache($this->cache, $form['imap_server_id']);
324            $imap = Hm_IMAP_List::connect($form['imap_server_id'], $cache);
325            if (imap_authed($imap) && $imap->select_mailbox(hex2bin($form['folder']))) {
326                $this->out('folder_status', array('imap_'.$form['imap_server_id'].'_'.$form['folder'] => $imap->folder_state));
327                $imap->message_action('READ', array($form['imap_msg_uid']));
328            }
329        }
330    }
331}
332
333/**
334 * Process a request to change a combined page source
335 * @subpackage imap/handler
336 */
337class Hm_Handler_process_imap_source_update extends Hm_Handler_Module {
338    /**
339     * Add or remove an IMAP folder to the combined view
340     */
341    public function process() {
342        list($success, $form) = $this->process_form(array('combined_source_state', 'list_path'));
343        if ($success) {
344            $sources = $this->user_config->get('custom_imap_sources');
345            if ($form['combined_source_state'] == 1) {
346                $sources[$form['list_path']] = 'add';
347                Hm_Msgs::add('Folder added to combined pages');
348                $this->session->record_unsaved('Added folder to combined pages');
349            }
350            else {
351                if (is_array($sources) && array_key_exists($form['list_path'], $sources)) {
352                    unset($sources[$form['list_path']]);
353                }
354                else {
355                    $sources[$form['list_path']] = 'remove';
356                }
357                Hm_Msgs::add('Folder removed from combined pages');
358                $this->session->record_unsaved('Removed folder from combined pages');
359            }
360            $this->session->set('custom_imap_sources', $sources, true);
361        }
362    }
363}
364
365/**
366 * Stream a message from IMAP to the browser
367 * @subpackage imap/handler
368 */
369class Hm_Handler_imap_download_message extends Hm_Handler_Module {
370    /**
371     * Download a message from the IMAP server
372     */
373    public function process() {
374        if (array_key_exists('imap_download_message', $this->request->get) && $this->request->get['imap_download_message']) {
375
376            $server_id = NULL;
377            $uid = NULL;
378            $folder = NULL;
379            $msg_id = NULL;
380
381            if (array_key_exists('uid', $this->request->get) && $this->request->get['uid']) {
382                $uid = $this->request->get['uid'];
383            }
384            if (array_key_exists('list_path', $this->request->get) && preg_match("/^imap_(\d+)_(.+)/", $this->request->get['list_path'], $matches)) {
385                $server_id = $matches[1];
386                $folder = hex2bin($matches[2]);
387            }
388            if (array_key_exists('imap_msg_part', $this->request->get) && preg_match("/^[0-9\.]+$/", $this->request->get['imap_msg_part'])) {
389                $msg_id = preg_replace("/^0.{1}/", '', $this->request->get['imap_msg_part']);
390            }
391            if ($server_id !== NULL && $uid !== NULL && $folder !== NULL && $msg_id !== NULL) {
392                $cache = Hm_IMAP_List::get_cache($this->cache, $server_id);
393                $imap = Hm_IMAP_List::connect($server_id, $cache);
394                if (imap_authed($imap)) {
395                    if ($imap->select_mailbox($folder)) {
396                        $msg_struct = $imap->get_message_structure($uid);
397                        $struct = $imap->search_bodystructure($msg_struct, array('imap_part_number' => $msg_id));
398                        if (!empty($struct)) {
399                            $part_struct = array_shift($struct);
400                            $encoding = false;
401                            if (array_key_exists('encoding', $part_struct)) {
402                                $encoding = trim(strtolower($part_struct['encoding']));
403                            }
404                            $stream_size = $imap->start_message_stream($uid, $msg_id);
405                            if ($stream_size > 0) {
406                                $name = get_imap_part_name($part_struct, $uid, $msg_id);
407                                header('Content-Disposition: attachment; filename="'.$name.'"');
408                                $charset = '';
409                                if (array_key_exists('attributes', $part_struct)) {
410                                    if (is_array($part_struct['attributes']) && array_key_exists('charset', $part_struct['attributes'])) {
411                                        $charset = '; charset='.$part_struct['attributes']['charset'];
412                                    }
413                                }
414                                header('Content-Type: '.$part_struct['type'].'/'.$part_struct['subtype'].$charset);
415                                header('Content-Transfer-Encoding: binary');
416                                ob_end_clean();
417                                $output_line = '';
418                                while($line = $imap->read_stream_line()) {
419                                    if ($encoding == 'quoted-printable') {
420                                        $line = quoted_printable_decode($line);
421                                    }
422                                    elseif ($encoding == 'base64') {
423                                        $line = base64_decode($line);
424                                    }
425                                    echo $output_line;
426                                    $output_line = $line;
427                                }
428                                if ($part_struct['type'] == 'text') {
429                                    $output_line = preg_replace("/\)(\r\n)$/m", '$1', $output_line);
430                                }
431                                echo $output_line;
432                                Hm_Functions::cease();
433                            }
434                        }
435                    }
436                }
437            }
438            Hm_Msgs::add('ERRAn Error occurred trying to download the message');
439        }
440    }
441}
442
443/**
444 * Process the list_path input argument
445 * @subpackage imap/handler
446 */
447class Hm_Handler_imap_message_list_type extends Hm_Handler_Module {
448    /**
449     * Output a list title
450     */
451    public function process() {
452        if (array_key_exists('list_path', $this->request->get)) {
453            $path = $this->request->get['list_path'];
454            if (preg_match("/^imap_\d+_.+$/", $path)) {
455                $this->out('list_meta', false, false);
456                $this->out('list_path', $path, false);
457                $this->out('move_copy_controls', true);
458                $parts = explode('_', $path, 3);
459                $details = Hm_IMAP_List::dump(intval($parts[1]));
460                $custom_link = 'add';
461                foreach (imap_data_sources(false, $this->user_config->get('custom_imap_sources', array())) as $vals) {
462                    if ($vals['id'] == $parts[1] && $vals['folder'] == $parts[2]) {
463                        $custom_link = 'remove';
464                        break;
465                    }
466                }
467                $this->out('custom_list_controls_type', $custom_link);
468                if (array_key_exists('keyword', $this->request->get)) {
469                    $this->out('list_keyword', $this->request->get['keyword']);
470                }
471                if (array_key_exists('filter', $this->request->get)) {
472                    if (in_array($this->request->get['filter'], array('all', 'unseen', 'seen',
473                        'answered', 'unanswered', 'flagged', 'unflagged'), true)) {
474                        $this->out('list_filter', $this->request->get['filter']);
475                    }
476                }
477                if (array_key_exists('sort', $this->request->get)) {
478                    if (in_array($this->request->get['sort'], array('arrival', 'from', 'subject',
479                        'date', 'to', '-arrival', '-from', '-subject', '-date', '-to'), true)) {
480                        $this->out('list_sort', $this->request->get['sort']);
481                    }
482                }
483                if (!empty($details)) {
484                    if (array_key_exists('folder_label', $this->request->get)) {
485                        $folder = $this->request->get['folder_label'];
486                        $this->out('folder_label', $folder);
487                    }
488                    else {
489                        $folder = hex2bin($parts[2]);
490                    }
491                    $title = array('IMAP', $details['name'], $folder);
492                    if ($this->get('list_page', 0)) {
493                        $title[] = sprintf('Page %d', $this->get('list_page', 0));
494                    }
495                    $this->out('mailbox_list_title', $title);
496                }
497            }
498            elseif ($path == 'sent') {
499                $this->out('mailbox_list_title', array('Sent'));
500                $this->out('per_source_limit', $this->user_config->get('sent_per_source_setting', DEFAULT_PER_SOURCE));
501                $this->out('message_list_since', $this->user_config->get('sent_since_setting', DEFAULT_SINCE));
502            }
503        }
504    }
505}
506
507/**
508 * Expand an IMAP folder section
509 * @subpackage imap/handler
510 */
511class Hm_Handler_imap_folder_expand extends Hm_Handler_Module {
512    /**
513     * Return cached subfolder contents or query the IMAP server for it
514     */
515    public function process() {
516
517        list($success, $form) = $this->process_form(array('imap_server_id'));
518        if ($success) {
519            $folder = '';
520            if (isset($this->request->post['folder'])) {
521                $folder = $this->request->post['folder'];
522            }
523            $path = sprintf("imap_%d_%s", $form['imap_server_id'], $folder);
524            $page_cache = $this->cache->get('imap_folders_'.$path);
525            if (array_key_exists('imap_prefetch', $this->request->post)) {
526                $prefetched = $this->session->get('imap_prefetched_ids', array());
527                $prefetched[] = $form['imap_server_id'];
528                $this->session->set('imap_prefetched_ids', array_unique($prefetched, SORT_NUMERIC));
529            }
530            if ($page_cache) {
531                $this->out('imap_expanded_folder_data', $page_cache);
532                $this->out('imap_expanded_folder_id', $form['imap_server_id']);
533                $this->out('imap_expanded_folder_path', $path);
534                return;
535            }
536            $cache = Hm_IMAP_List::get_cache($this->cache, $form['imap_server_id']);
537            $imap = Hm_IMAP_List::connect($form['imap_server_id'], $cache);
538            if (imap_authed($imap)) {
539                $msgs = $imap->get_folder_list_by_level(hex2bin($folder));
540                if (isset($msgs[$folder])) {
541                    unset($msgs[$folder]);
542                }
543                $this->cache->set('imap_folders_'.$path, $msgs);
544                $this->out('imap_expanded_folder_data', $msgs);
545                $this->out('imap_expanded_folder_id', $form['imap_server_id']);
546                $this->out('imap_expanded_folder_path', $path);
547            }
548            else {
549                Hm_Msgs::add(sprintf('ERRCould not authenticate to the selected %s server', $imap->server_type));
550            }
551        }
552    }
553}
554
555/**
556 * Fetch the message headers for a an IMAP folder page
557 * @subpackage imap/handler
558 */
559class Hm_Handler_imap_folder_page extends Hm_Handler_Module {
560
561    /**
562     * Use IMAP FETCH to get a page of headers
563     */
564    public function process() {
565
566        $filter = 'ALL';
567        if ($this->get('list_filter')) {
568            $filter = strtoupper($this->get('list_filter'));
569        }
570        $keyword = $this->get('list_keyword', '');
571        list($sort, $rev) = process_sort_arg($this->get('list_sort'));
572        $limit = $this->user_config->get('imap_per_page_setting', DEFAULT_PER_SOURCE);
573        $offset = 0;
574        $msgs = array();
575        $list_page = 1;
576
577        list($success, $form) = $this->process_form(array('imap_server_id', 'folder'));
578        if ($success) {
579            if (isset($this->request->get['list_page'])) {
580                $list_page = (int) $this->request->get['list_page'];
581                if ($list_page && $list_page > 1) {
582                    $offset = ($list_page - 1)*$limit;
583                }
584                else {
585                    $list_page = 1;
586                }
587            }
588            $path = sprintf("imap_%d_%s", $form['imap_server_id'], $form['folder']);
589            $details = Hm_IMAP_List::dump($form['imap_server_id']);
590            $cache = Hm_IMAP_List::get_cache($this->cache, $form['imap_server_id']);
591            $imap = Hm_IMAP_List::connect($form['imap_server_id'], $cache);
592            if (imap_authed($imap)) {
593                $this->out('imap_mailbox_page_path', $path);
594                list($total, $results) = $imap->get_mailbox_page(hex2bin($form['folder']), $sort, $rev, $filter, $offset, $limit, $keyword);
595                foreach ($results as $msg) {
596                    $msg['server_id'] = $form['imap_server_id'];
597                    $msg['server_name'] = $details['name'];
598                    $msg['folder'] = $form['folder'];
599                    $msgs[] = $msg;
600                }
601                if ($imap->selected_mailbox) {
602                    $imap->selected_mailbox['detail']['exists'] = $total;
603                    $this->out('imap_folder_detail', array_merge($imap->selected_mailbox, array('offset' => $offset, 'limit' => $limit)));
604                }
605                $this->out('folder_status', array('imap_'.$form['imap_server_id'].'_'.$form['folder'] => $imap->folder_state));
606            }
607            $this->out('imap_mailbox_page', $msgs);
608            $this->out('list_page', $list_page);
609            $this->out('imap_server_id', $form['imap_server_id']);
610        }
611    }
612}
613
614/**
615 * Build a list of IMAP servers as the top level folders
616 * @subpackage imap/handler
617 */
618class Hm_Handler_load_imap_folders extends Hm_Handler_Module {
619    /**
620     * Used by the folder list
621     */
622    public function process() {
623        $servers = Hm_IMAP_List::dump();
624        $folders = array();
625        if (!empty($servers)) {
626            foreach ($servers as $id => $server) {
627                $folders[$id] = $server['name'];
628            }
629        }
630        $this->out('imap_folders', $folders);
631    }
632}
633
634/**
635 * Delete a message
636 * @subpackage imap/handler
637 */
638class Hm_Handler_imap_delete_message extends Hm_Handler_Module {
639    /**
640     * Use IMAP to delete the selected message uid
641     */
642    public function process() {
643        list($success, $form) = $this->process_form(array('imap_msg_uid', 'imap_server_id', 'folder'));
644        if ($success) {
645            $del_result = false;
646            $cache = Hm_IMAP_List::get_cache($this->cache, $form['imap_server_id']);
647            $imap = Hm_IMAP_List::connect($form['imap_server_id'], $cache);
648            $trash_folder = false;
649            $specials = $this->user_config->get('special_imap_folders', array());
650            if (array_key_exists($form['imap_server_id'], $specials)) {
651                if (array_key_exists('trash', $specials[$form['imap_server_id']])) {
652                    if ($specials[$form['imap_server_id']]['trash']) {
653                        $trash_folder = $specials[$form['imap_server_id']]['trash'];
654                    }
655                }
656            }
657            if (imap_authed($imap)) {
658                if ($imap->select_mailbox(hex2bin($form['folder']))) {
659                    $this->out('folder_status', array('imap_'.$form['imap_server_id'].'_'.$form['folder'] => $imap->folder_state));
660                    if ($trash_folder && $trash_folder != hex2bin($form['folder'])) {
661                        if ($imap->message_action('MOVE', array($form['imap_msg_uid']), $trash_folder)) {
662                            $del_result = true;
663                        }
664                    }
665                    else {
666                        if ($imap->message_action('DELETE', array($form['imap_msg_uid']))) {
667                            $del_result = true;
668                            $imap->message_action('EXPUNGE', array($form['imap_msg_uid']));
669                        }
670                    }
671                }
672            }
673            if (!$del_result) {
674                Hm_Msgs::add('ERRAn error occurred trying to delete this message');
675                $this->out('imap_delete_error', true);
676            }
677            else {
678                Hm_Msgs::add('Message deleted');
679                $this->out('imap_delete_error', false);
680            }
681            $msgs = Hm_Msgs::get();
682            Hm_Msgs::flush();
683            $this->session->secure_cookie($this->request, 'hm_msgs', base64_encode(json_encode($msgs)));
684        }
685    }
686}
687
688
689/**
690 * Archive a message
691 * @subpackage imap/handler
692 */
693class Hm_Handler_imap_archive_message extends Hm_Handler_Module {
694    /**
695     * Use IMAP to archive the selected message uid
696     */
697    public function process() {
698        list($success, $form) = $this->process_form(array('imap_msg_uid', 'imap_server_id', 'folder'));
699
700        if (!$success) {
701            return;
702        }
703        $cache = Hm_IMAP_List::get_cache($this->cache, $form['imap_server_id']);
704        $imap = Hm_IMAP_List::connect($form['imap_server_id'], $cache);
705        $archive_folder = false;
706
707        $specials = $this->user_config->get('special_imap_folders', array());
708        if (array_key_exists($form['imap_server_id'], $specials)) {
709            if (array_key_exists('archive', $specials[$form['imap_server_id']])) {
710                if ($specials[$form['imap_server_id']]['archive']) {
711                    $archive_folder = $specials[$form['imap_server_id']]['archive'];
712                }
713            }
714        }
715
716        if(!$archive_folder) {
717            $archive_folder = "Archive";
718        }
719
720        if (imap_authed($imap)) {
721            $new_folder = prep_folder_name($imap, hex2bin($form['folder']), false, $archive_folder);
722            $archive_exists = count($imap->get_mailbox_status($archive_folder));
723            $folder_exists = count($imap->get_mailbox_status($new_folder));
724            $error = false;
725
726            /* if archive folders don't exist try to create them */
727            if (!$archive_exists && !$imap->create_mailbox($archive_folder)) {
728                $error = true;
729            }
730            if (!$error && !$folder_exists && !$imap->create_mailbox($new_folder)) {
731                $error = true;
732            }
733
734            /* select source folder */
735            if (!$error && !$imap->select_mailbox(hex2bin($form['folder']))) {
736                $error = true;
737            }
738
739            /* try to move the message */
740            if (!$error && $imap->message_action('MOVE', array($form['imap_msg_uid']), $new_folder)) {
741                Hm_Msgs::add("Message archived");
742
743                /* bust imap cache if we created a new folder */
744                if (!$archive_exists || !$folder_exists) {
745                    $this->session->secure_cookie($this->request, 'hm_reload_folders', '1');
746                    $this->cache->del('imap'.$form['imap_server_id']);
747                    $this->cache->del('imap_folders_imap_'.$form['imap_server_id'].'_'.bin2hex($archive_folder));
748                }
749            }
750            else {
751                Hm_Msgs::add('ERRAn error occurred archiving the message');
752            }
753        }
754
755        $msgs = Hm_Msgs::get();
756        Hm_Msgs::flush();
757        $this->session->secure_cookie($this->request, 'hm_msgs', base64_encode(json_encode($msgs)));
758    }
759}
760
761/**
762 * Flag a message
763 * @subpackage imap/handler
764 */
765class Hm_Handler_flag_imap_message extends Hm_Handler_Module {
766    /**
767     * Use IMAP to flag the selected message uid
768     */
769    public function process() {
770        list($success, $form) = $this->process_form(array('imap_flag_state', 'imap_msg_uid', 'imap_server_id', 'folder'));
771        if ($success) {
772            $flag_result = false;
773            $cache = Hm_IMAP_List::get_cache($this->cache, $form['imap_server_id']);
774            $imap = Hm_IMAP_List::connect($form['imap_server_id'], $cache);
775            if (imap_authed($imap)) {
776                if ($imap->select_mailbox(hex2bin($form['folder']))) {
777                    $this->out('folder_status', array('imap_'.$form['imap_server_id'].'_'.$form['folder'] => $imap->folder_state));
778                    if ($form['imap_flag_state'] == 'flagged') {
779                        $cmd = 'UNFLAG';
780                    }
781                    else {
782                        $cmd = 'FLAG';
783                    }
784                    if ($imap->message_action($cmd, array($form['imap_msg_uid']))) {
785                        $flag_result = true;
786                    }
787                }
788            }
789            if (!$flag_result) {
790                Hm_Msgs::add('ERRAn error occurred trying to flag this message');
791            }
792        }
793    }
794}
795
796/**
797 * Perform an IMAP message action
798 * @subpackage imap/handler
799 */
800class Hm_Handler_imap_message_action extends Hm_Handler_Module {
801    /**
802     * Read, unread, delete, flag, or unflag a set of message uids
803     */
804    public function process() {
805        list($success, $form) = $this->process_form(array('action_type', 'message_ids'));
806        if ($success) {
807            if (in_array($form['action_type'], array('delete', 'read', 'unread', 'flag', 'unflag'))) {
808                $ids = process_imap_message_ids($form['message_ids']);
809                $errs = 0;
810                $msgs = 0;
811                $status = array();
812                $specials = $this->user_config->get('special_imap_folders', array());
813                foreach ($ids as $server => $folders) {
814                    $trash_folder = false;
815                    if ($form['action_type'] == 'delete') {
816                        if (array_key_exists($server, $specials)) {
817                            if (array_key_exists('trash', $specials[$server])) {
818                                if ($specials[$server]['trash']) {
819                                    $trash_folder = $specials[$server]['trash'];
820                                }
821                            }
822                        }
823                    }
824                    $cache = Hm_IMAP_List::get_cache($this->cache, $server);
825                    $imap = Hm_IMAP_List::connect($server, $cache);
826                    if (imap_authed($imap)) {
827                        foreach ($folders as $folder => $uids) {
828                            if ($imap->select_mailbox(hex2bin($folder))) {
829                                $status['imap_'.$server.'_'.$folder] = $imap->folder_state;
830
831                                if ($form['action_type'] == 'delete' && $trash_folder && $trash_folder != hex2bin($folder)) {
832                                    if (!$imap->message_action('MOVE', $uids, $trash_folder)) {
833                                        $errs++;
834                                    }
835                                }
836                                else {
837                                    if (!$imap->message_action(strtoupper($form['action_type']), $uids)) {
838                                        $errs++;
839                                    }
840                                    else {
841                                        $msgs += count($uids);
842                                        if ($form['action_type'] == 'delete') {
843                                            $imap->message_action('EXPUNGE', $uids);
844                                        }
845                                    }
846                                }
847                            }
848                        }
849                    }
850                }
851                if ($errs > 0) {
852                    Hm_Msgs::add(sprintf('ERRAn error occurred trying to %s some messages!', $form['action_type'], $server));
853                }
854                if (count($status) > 0) {
855                    $this->out('folder_state', $status);
856                }
857            }
858        }
859    }
860}
861
862/**
863 * Search for a message
864 * @subpackage imap/handler
865 */
866class Hm_Handler_imap_search extends Hm_Handler_Module {
867    /**
868     * Use IMAP SEARCH to find matching messages
869     */
870    public function process() {
871        list($success, $form) = $this->process_form(array('imap_server_ids'));
872        if ($success) {
873            $terms = $this->session->get('search_terms', false);
874            $since = $this->session->get('search_since', DEFAULT_SINCE);
875            $fld = $this->session->get('search_fld', 'TEXT');
876            $ids = explode(',', $form['imap_server_ids']);
877            $date = process_since_argument($since);
878            $folder = bin2hex('INBOX');
879            if (array_key_exists('folder', $this->request->post)) {
880                $folder = $this->request->post['folder'];
881            }
882            list($status, $msg_list) = merge_imap_search_results($ids, 'ALL', $this->session, $this->cache, array(hex2bin($folder)), MAX_PER_SOURCE, array(array('SINCE', $date), array($fld, $terms)));
883            $this->out('imap_search_results', $msg_list);
884            $this->out('folder_status', $status);
885            $this->out('imap_server_ids', $form['imap_server_ids']);
886        }
887    }
888}
889
890/**
891 * Get message headers for the Sent page
892 * @subpackage imap/handler
893 */
894class Hm_Handler_imap_sent extends Hm_Handler_Module {
895    /**
896     * Returns list of message data for the Everthing page
897     */
898    public function process() {
899        list($success, $form) = $this->process_form(array('imap_server_ids'));
900        if ($success) {
901            $limit = $this->user_config->get('sent_per_source_setting', DEFAULT_PER_SOURCE);
902            $date = process_since_argument($this->user_config->get('sent_since_setting', DEFAULT_SINCE));
903            $ids = explode(',', $form['imap_server_ids']);
904            $folder = bin2hex('INBOX');
905            if (array_key_exists('folder', $this->request->post)) {
906                $folder = $this->request->post['folder'];
907            }
908            if (hex2bin($folder) == 'SPECIAL_USE_CHECK' || hex2bin($folder) == 'INBOX') {
909                list($status, $msg_list) = merge_imap_search_results($ids, 'ALL', $this->session, $this->cache, array(hex2bin($folder)), $limit, array(array('SINCE', $date)), true);
910            }
911            else {
912                list($status, $msg_list) = merge_imap_search_results($ids, 'ALL', $this->session, $this->cache, array(hex2bin($folder)), $limit, array(array('SINCE', $date)), false);
913            }
914            $folders = array();
915            foreach ($msg_list as $msg) {
916                if (hex2bin($msg['folder']) != hex2bin($folder)) {
917                    $folders[] = hex2bin($msg['folder']);
918                }
919            }
920            if (count($folders) > 0) {
921                $auto_folder = $folders[0];
922                $this->out('auto_sent_folder', $msg_list[0]['server_name'].' '.$auto_folder);
923            }
924            $this->out('folder_status', $status);
925            $this->out('imap_sent_data', $msg_list);
926            $this->out('imap_server_ids', $form['imap_server_ids']);
927        }
928    }
929}
930
931/**
932 * Get message headers for the Everthing page
933 * @subpackage imap/handler
934 */
935class Hm_Handler_imap_combined_inbox extends Hm_Handler_Module {
936    /**
937     * Returns list of message data for the Everthing page
938     */
939    public function process() {
940        list($success, $form) = $this->process_form(array('imap_server_ids'));
941        if ($success) {
942            if (array_key_exists('list_path', $this->request->get) && $this->request->get['list_path'] == 'email') {
943                $limit = $this->user_config->get('all_email_per_source_setting', DEFAULT_PER_SOURCE);
944                $date = process_since_argument($this->user_config->get('all_email_since_setting', DEFAULT_SINCE));
945            }
946            else {
947                $limit = $this->user_config->get('all_per_source_setting', DEFAULT_PER_SOURCE);
948                $date = process_since_argument($this->user_config->get('all_since_setting', DEFAULT_SINCE));
949            }
950            $ids = explode(',', $form['imap_server_ids']);
951            $folder = bin2hex('INBOX');
952            if (array_key_exists('folder', $this->request->post)) {
953                $folder = $this->request->post['folder'];
954            }
955            list($status, $msg_list) = merge_imap_search_results($ids, 'ALL', $this->session, $this->cache, array(hex2bin($folder)), $limit, array(array('SINCE', $date)));
956            $this->out('folder_status', $status);
957            $this->out('imap_combined_inbox_data', $msg_list);
958            $this->out('imap_server_ids', $form['imap_server_ids']);
959        }
960    }
961}
962
963/**
964 * Get message headers for the Flagged page
965 * @subpackage imap/handler
966 */
967class Hm_Handler_imap_flagged extends Hm_Handler_Module {
968    /**
969     * Fetch flagged messages from an IMAP server
970     */
971    public function process() {
972        list($success, $form) = $this->process_form(array('imap_server_ids'));
973        if ($success) {
974            $limit = $this->user_config->get('flagged_per_source_setting', DEFAULT_PER_SOURCE);
975            $ids = explode(',', $form['imap_server_ids']);
976            $date = process_since_argument($this->user_config->get('flagged_since_setting', DEFAULT_SINCE));
977            $folder = bin2hex('INBOX');
978            if (array_key_exists('folder', $this->request->post)) {
979                $folder = $this->request->post['folder'];
980            }
981            list($status, $msg_list) = merge_imap_search_results($ids, 'FLAGGED', $this->session, $this->cache, array(hex2bin($folder)), $limit, array(array('SINCE', $date)));
982            $this->out('folder_status', $status);
983            $this->out('imap_flagged_data', $msg_list);
984            $this->out('imap_server_ids', $form['imap_server_ids']);
985        }
986    }
987}
988
989/**
990 * Check the status of an IMAP server connection
991 * @subpackage imap/handler
992 */
993class Hm_Handler_imap_status extends Hm_Handler_Module {
994    /**
995     * Output used on the info page to display the server status
996     */
997    public function process() {
998        list($success, $form) = $this->process_form(array('imap_server_ids'));
999        if ($success) {
1000            $ids = explode(',', $form['imap_server_ids']);
1001            foreach ($ids as $id) {
1002                $cache = Hm_IMAP_List::get_cache($this->cache, $id);
1003                $start_time = microtime(true);
1004                $imap = Hm_IMAP_List::connect($id, $cache);
1005                $this->out('imap_connect_time', microtime(true) - $start_time);
1006                if (imap_authed($imap)) {
1007                    $this->out('imap_connect_status', $imap->get_state());
1008                    $this->out('imap_status_server_id', $id);
1009                }
1010                else {
1011                    $this->out('imap_connect_status', 'disconnected');
1012                    $this->out('imap_status_server_id', $id);
1013                }
1014            }
1015        }
1016    }
1017}
1018
1019/**
1020 * Fetch messages for the Unread page
1021 * @subpackage imap/handler
1022 */
1023class Hm_Handler_imap_unread extends Hm_Handler_Module {
1024    /**
1025     * Returns UNSEEN messages for an IMAP server
1026     */
1027    public function process() {
1028        list($success, $form) = $this->process_form(array('imap_server_ids'));
1029        if ($success) {
1030            $limit = $this->user_config->get('unread_per_source_setting', DEFAULT_PER_SOURCE);
1031            $date = process_since_argument($this->user_config->get('unread_since_setting', DEFAULT_SINCE));
1032            $ids = explode(',', $form['imap_server_ids']);
1033            $msg_list = array();
1034            $folder = bin2hex('INBOX');
1035            if (array_key_exists('folder', $this->request->post)) {
1036                $folder = $this->request->post['folder'];
1037            }
1038            list($status, $msg_list) = merge_imap_search_results($ids, 'UNSEEN', $this->session, $this->cache, array(hex2bin($folder)), $limit, array(array('SINCE', $date)));
1039            $this->out('folder_status', $status);
1040            $this->out('imap_unread_data', $msg_list);
1041            $this->out('imap_server_ids', $form['imap_server_ids']);
1042        }
1043    }
1044}
1045
1046/**
1047 * Add a new JMAP server
1048 * @subpackage imap/handler
1049 */
1050class Hm_Handler_process_add_jmap_server extends Hm_Handler_Module {
1051    public function process() {
1052        /**
1053         * Used on the servers page to add a new JMAP server
1054         */
1055        if (isset($this->request->post['submit_jmap_server'])) {
1056            list($success, $form) = $this->process_form(array('new_jmap_name', 'new_jmap_address'));
1057            if (!$success) {
1058                $this->out('old_form', $form);
1059                Hm_Msgs::add('ERRYou must supply a name and a JMAP server URL');
1060                return;
1061            }
1062            $hidden = false;
1063            if (isset($this->request->post['new_jmap_hidden'])) {
1064                $hidden = true;
1065            }
1066            $parsed = parse_url($form['new_jmap_address']);
1067            if (array_key_exists('host', $parsed) && @get_headers($form['new_jmap_address'])) {
1068
1069                Hm_IMAP_List::add(array(
1070                    'name' => $form['new_jmap_name'],
1071                    'server' => $form['new_jmap_address'],
1072                    'hide' => $hidden,
1073                    'type' => 'jmap',
1074                    'port' => false,
1075                    'tls' => false));
1076                Hm_Msgs::add('Added server!');
1077                $this->session->record_unsaved('JMAP server added');
1078            }
1079            else {
1080                Hm_Msgs::add('ERRCound not access supplied URL');
1081            }
1082        }
1083    }
1084}
1085
1086/**
1087 * Add a new IMAP server
1088 * @subpackage imap/handler
1089 */
1090class Hm_Handler_process_add_imap_server extends Hm_Handler_Module {
1091    public function process() {
1092        /**
1093         * Used on the servers page to add a new IMAP server
1094         */
1095        if (isset($this->request->post['submit_imap_server'])) {
1096            list($success, $form) = $this->process_form(array('new_imap_name', 'new_imap_address', 'new_imap_port'));
1097            if (!$success) {
1098                $this->out('old_form', $form);
1099                Hm_Msgs::add('ERRYou must supply a name, a server and a port');
1100            }
1101            else {
1102                $tls = false;
1103                if (array_key_exists('tls', $this->request->post) && $this->request->post['tls']) {
1104                    $tls = true;
1105                }
1106                $hidden = false;
1107                if (isset($this->request->post['new_imap_hidden'])) {
1108                    $hidden = true;
1109                }
1110                if ($con = fsockopen($form['new_imap_address'], $form['new_imap_port'], $errno, $errstr, 2)) {
1111                    Hm_IMAP_List::add(array(
1112                        'name' => $form['new_imap_name'],
1113                        'server' => $form['new_imap_address'],
1114                        'hide' => $hidden,
1115                        'port' => $form['new_imap_port'],
1116                        'tls' => $tls));
1117                    Hm_Msgs::add('Added server!');
1118                    $this->session->record_unsaved('IMAP server added');
1119                }
1120                else {
1121                    Hm_Msgs::add(sprintf('ERRCound not add server: %s', $errstr));
1122                }
1123            }
1124        }
1125    }
1126}
1127
1128/**
1129 * Save IMAP caches in the session
1130 * @subpackage imap/handler
1131 */
1132class Hm_Handler_save_imap_cache extends Hm_Handler_Module {
1133    /**
1134     * Save IMAP cache data for re-use
1135     */
1136    public function process() {
1137        $servers = Hm_IMAP_List::dump(false, true);
1138        $cache = array();
1139        foreach ($servers as $index => $server) {
1140            if (isset($server['object']) && is_object($server['object'])) {
1141                if ($server['object']->use_cache) {
1142                    $cache[$index] = $server['object']->dump_cache('array');
1143                }
1144            }
1145        }
1146        if (count($cache) > 0) {
1147            foreach ($cache as $id => $data) {
1148                $this->cache->set('imap'.$id, $cache[$id]);
1149            }
1150        }
1151    }
1152}
1153
1154/**
1155 * Save IMAP servers
1156 * @subpackage imap/handler
1157 */
1158class Hm_Handler_save_imap_servers extends Hm_Handler_Module {
1159    /**
1160     * Save IMAP servers in the user config
1161     */
1162    public function process() {
1163        $servers = Hm_IMAP_List::dump(false, true);
1164        $this->user_config->set('imap_servers', $servers);
1165        Hm_IMAP_List::clean_up();
1166    }
1167}
1168
1169/**
1170 * Load IMAP servers for the search page
1171 * @subpackage imap/handler
1172 */
1173class Hm_Handler_load_imap_servers_for_search extends Hm_Handler_Module {
1174    /**
1175     * Output IMAP server array used on the search page
1176     */
1177    public function process() {
1178        foreach(imap_data_sources('imap_search_page_content', $this->user_config->get('custom_imap_sources', array())) as $vals) {
1179            $this->append('data_sources', $vals);
1180        }
1181    }
1182}
1183
1184/**
1185 * Load IMAP servers for message list pages
1186 * @subpackage imap/handler
1187 */
1188class Hm_Handler_load_imap_servers_for_message_list extends Hm_Handler_Module {
1189    /**
1190     * Used by combined views excluding normal folder view and search pages
1191     */
1192    public function process() {
1193        $callback = false;
1194        if (array_key_exists('list_path', $this->request->get)) {
1195            $path = $this->request->get['list_path'];
1196        }
1197        else {
1198            $path = '';
1199        }
1200        switch ($path) {
1201            case 'unread':
1202                $callback = 'imap_combined_unread_content';
1203                break;
1204            case 'flagged':
1205                $callback = 'imap_combined_flagged_content';
1206                break;
1207            case 'combined_inbox':
1208                $callback = 'imap_combined_inbox_content';
1209                break;
1210            case 'email':
1211                $callback = 'imap_all_mail_content';
1212                break;
1213            case 'sent':
1214                $callback = 'imap_sent_content';
1215                break;
1216            default:
1217                $callback = 'imap_background_unread_content';
1218                break;
1219        }
1220        if ($callback) {
1221            if ($callback != 'imap_background_unread_content') {
1222                $this->out('move_copy_controls', true);
1223            }
1224            if ($path == 'sent') {
1225                foreach (imap_sent_sources($callback, $this->user_config->get('special_imap_folders', array()),
1226                    $this->user_config->get('smtp_auto_bcc_setting', false)) as $vals) {
1227                    $this->append('data_sources', $vals);
1228                }
1229            }
1230            else {
1231                foreach (imap_data_sources($callback, $this->user_config->get('custom_imap_sources', array())) as $vals) {
1232                    if ($callback == 'imap_background_unread_content') {
1233                        $vals['group'] = 'background';
1234                    }
1235                    $this->append('data_sources', $vals);
1236                }
1237            }
1238        }
1239    }
1240}
1241
1242/**
1243 * Load IMAP servers for the user config object
1244 * @subpackage imap/handler
1245 */
1246class Hm_Handler_load_imap_servers_from_config extends Hm_Handler_Module {
1247    /**
1248     * This list is cached in the session between page loads by Hm_Handler_save_imap_servers
1249     */
1250    public function process() {
1251        $servers = $this->user_config->get('imap_servers', array());
1252        $added = false;
1253        $updated = false;
1254        $new_servers = array();
1255        $max = 0;
1256        foreach ($servers as $index => $server) {
1257            if ($this->session->loaded) {
1258                if (array_key_exists('expiration', $server)) {
1259                    $updated = true;
1260                    $server['expiration'] = 1;
1261                }
1262            }
1263            $new_servers[] = $server;
1264            $max = $index;
1265            Hm_IMAP_List::add($server, $index);
1266            if (array_key_exists('default', $server) && $server['default']) {
1267                $added = true;
1268            }
1269        }
1270        $max++;
1271        if ($updated) {
1272            $this->user_config->set('imap_servers', $new_servers);
1273        }
1274        if (!$added) {
1275            $auth_server = $this->session->get('imap_auth_server_settings', array());
1276            if (!empty($auth_server)) {
1277                if (array_key_exists('name', $auth_server)) {
1278                    $name = $auth_server['name'];
1279                }
1280                else {
1281                    $name = $this->config->get('imap_auth_name', 'Default');
1282                }
1283                Hm_IMAP_List::add(array(
1284                    'name' => $name,
1285                    'default' => true,
1286                    'server' => $auth_server['server'],
1287                    'port' => $auth_server['port'],
1288                    'tls' => $auth_server['tls'],
1289                    'user' => $auth_server['username'],
1290                    'pass' => $auth_server['password']),
1291                $max);
1292            }
1293        }
1294    }
1295}
1296
1297/**
1298 * Check for IMAP server oauth2 token refresh
1299 * @subpackage imap/handler
1300 */
1301class Hm_Handler_imap_oauth2_token_check extends Hm_Handler_Module {
1302    public function process() {
1303        $active = array();
1304        if (array_key_exists('imap_server_ids', $this->request->post)) {
1305            $active = explode(',', $this->request->post['imap_server_ids']);
1306        }
1307        if (array_key_exists('imap_server_id', $this->request->post)) {
1308            $active[] = $this->request->post['imap_server_id'];
1309        }
1310        $updated = 0;
1311        foreach ($active as $server_id) {
1312            $server = Hm_IMAP_List::dump($server_id, true);
1313            if (array_key_exists('auth', $server) && $server['auth'] == 'xoauth2') {
1314                $results = imap_refresh_oauth2_token($server, $this->config);
1315                if (!empty($results)) {
1316                    if (Hm_IMAP_List::update_oauth2_token($server_id, $results[1], $results[0])) {
1317                        Hm_Debug::add(sprintf('Oauth2 token refreshed for IMAP server id %d', $server_id));
1318                        $updated++;
1319                    }
1320                }
1321            }
1322        }
1323        if ($updated > 0) {
1324            $servers = Hm_IMAP_List::dump(false, true);
1325            $this->user_config->set('imap_servers', $servers);
1326            $this->session->set('user_data', $this->user_config->dump());
1327        }
1328    }
1329}
1330
1331/**
1332 * Set IMAP server ids to prefetch on login
1333 * @subpackage imap/handler
1334 */
1335class Hm_Handler_prefetch_imap_folders extends Hm_Handler_Module {
1336    /**
1337     * Check for imap servers to prefetch
1338     */
1339    public function process() {
1340
1341        $servers = array();
1342        foreach ($this->get('imap_servers', array()) as $index => $vals) {
1343            if (array_key_exists('user', $vals)) {
1344                $servers[$index] = $vals;
1345            }
1346        }
1347        if (count($servers) == 0) {
1348            return;
1349        }
1350        $fetched = $this->session->get('imap_prefetched_ids', array());
1351        $ids = array_keys($servers);
1352        if (count($fetched) > 0) {
1353            $ids = array_diff($ids, $fetched);
1354        }
1355        if (count($ids) > 0) {
1356            $this->out('prefetch_folder_ids', $ids);
1357        }
1358    }
1359}
1360
1361/**
1362 * Output IMAP server data for other modules to use
1363 * @subpackage imap/handler
1364 */
1365class Hm_Handler_add_imap_servers_to_page_data extends Hm_Handler_Module {
1366    /**
1367     * Creates folder source for the folder list and outputs IMAP server details
1368     */
1369    public function process() {
1370        $servers = Hm_IMAP_List::dump();
1371        if (!empty($servers)) {
1372            $this->out('imap_servers', $servers);
1373        }
1374    }
1375}
1376
1377/**
1378 * Delete IMAP cache
1379 * @subpackage imap/handler
1380 */
1381class Hm_Handler_imap_bust_cache extends Hm_Handler_Module {
1382    /**
1383     * Deletes all the saved IMAP cache data
1384     */
1385    public function process() {
1386        list($success, $form) = $this->process_form(array('imap_server_id'));
1387        if (!$success) {
1388            return;
1389        }
1390        $this->cache->del('imap'.$form['imap_server_id']);
1391        Hm_Debug::add(sprintf('Busted cache for IMAP server %s', $form['imap_server_id']));
1392    }
1393}
1394
1395/**
1396 * Test a connection to an IMAP server
1397 * @subpackage imap/handler
1398 */
1399class Hm_Handler_imap_connect extends Hm_Handler_Module {
1400    /**
1401     * Used by the servers page to test/authenticate with an IMAP server
1402     */
1403    public function process() {
1404        if (isset($this->request->post['imap_connect'])) {
1405            list($success, $form) = $this->process_form(array('imap_user', 'imap_pass', 'imap_server_id'));
1406            $imap = false;
1407            $cache = Hm_IMAP_List::get_cache($this->cache, $form['imap_server_id']);
1408            if ($success) {
1409                $imap = Hm_IMAP_List::connect($form['imap_server_id'], $cache, $form['imap_user'], $form['imap_pass']);
1410            }
1411            elseif (isset($form['imap_server_id'])) {
1412                $imap = Hm_IMAP_List::connect($form['imap_server_id'], $cache);
1413            }
1414            if ($imap) {
1415                if ($imap->get_state() == 'authenticated') {
1416                    Hm_Msgs::add(sprintf("Successfully authenticated to the %s server", $imap->server_type));
1417                }
1418                else {
1419                    Hm_Msgs::add(sprintf("ERRFailed to authenticate to the %s server", $imap->server_type));
1420                }
1421            }
1422            else {
1423                Hm_Msgs::add('ERRUsername and password are required');
1424                $this->out('old_form', $form);
1425            }
1426        }
1427    }
1428}
1429
1430/**
1431 * Forget IMAP server credentials
1432 * @subpackage imap/handler
1433 */
1434class Hm_Handler_imap_forget extends Hm_Handler_Module {
1435    /**
1436     * Used on the servers page to forget login information for an IMAP server
1437     */
1438    public function process() {
1439        $just_forgot_credentials = false;
1440        if (isset($this->request->post['imap_forget'])) {
1441            list($success, $form) = $this->process_form(array('imap_server_id'));
1442            if ($success) {
1443                Hm_IMAP_List::forget_credentials($form['imap_server_id']);
1444                $just_forgot_credentials = true;
1445                Hm_Msgs::add('Server credentials forgotten');
1446                $this->session->record_unsaved('IMAP server credentials forgotten');
1447            }
1448            else {
1449                $this->out('old_form', $form);
1450            }
1451        }
1452        $this->out('just_forgot_credentials', $just_forgot_credentials);
1453    }
1454}
1455
1456/**
1457 * Save a user/pass combination for an IMAP server
1458 * @subpackage imap/handler
1459 */
1460class Hm_Handler_imap_save extends Hm_Handler_Module {
1461    /**
1462     * Authenticate then save the username and password for an IMAP server
1463     */
1464    public function process() {
1465        $just_saved_credentials = false;
1466        if (isset($this->request->post['imap_save'])) {
1467            list($success, $form) = $this->process_form(array('imap_user', 'imap_pass', 'imap_server_id'));
1468            if (!$success) {
1469                Hm_Msgs::add('ERRUsername and Password are required to save a connection');
1470            }
1471            else {
1472                if (in_server_list('Hm_IMAP_List', $form['imap_server_id'], $form['imap_user'])) {
1473                    Hm_Msgs::add('ERRThis server and username are already configured');
1474                    return;
1475                }
1476                $cache = Hm_IMAP_List::get_cache($this->cache, $form['imap_server_id']);
1477                $imap = Hm_IMAP_List::connect($form['imap_server_id'], $cache, $form['imap_user'], $form['imap_pass'], true);
1478                if (imap_authed($imap)) {
1479                    $just_saved_credentials = true;
1480                    Hm_Msgs::add("Server saved");
1481                    $this->session->record_unsaved(sprintf('%s server saved', $imap->server_type));
1482                }
1483                else {
1484                    Hm_Msgs::add("ERRUnable to save this server, are the username and password correct?");
1485                    Hm_IMAP_List::forget_credentials($form['imap_server_id']);
1486                }
1487            }
1488        }
1489        $this->out('just_saved_credentials', $just_saved_credentials);
1490    }
1491}
1492
1493/**
1494 * Get message content from an IMAP server
1495 * @subpackage imap/handler
1496 */
1497class Hm_Handler_imap_message_content extends Hm_Handler_Module {
1498    /**
1499     * Fetch the content, message parts, and headers for the supplied message
1500     */
1501    public function process() {
1502        list($success, $form) = $this->process_form(array('imap_server_id', 'imap_msg_uid', 'folder'));
1503        if ($success) {
1504            $this->out('msg_text_uid', $form['imap_msg_uid']);
1505            $this->out('msg_server_id', $form['imap_server_id']);
1506            $this->out('msg_folder', $form['folder']);
1507            $part = false;
1508            $prefetch = false;
1509            if (isset($this->request->post['imap_msg_part']) && preg_match("/[0-9\.]+/", $this->request->post['imap_msg_part'])) {
1510                $part = $this->request->post['imap_msg_part'];
1511            }
1512            elseif (isset($this->request->post['imap_prefetch']) && $this->request->post['imap_prefetch']) {
1513                $prefetch = true;
1514            }
1515            if (array_key_exists('imap_allow_images', $this->request->post) && $this->request->post['imap_allow_images']) {
1516                $this->out('imap_allow_images', true);
1517            }
1518            $this->out('header_allow_images', $this->config->get('allow_external_image_sources'));
1519
1520            $cache = Hm_IMAP_List::get_cache($this->cache, $form['imap_server_id']);
1521            $imap = Hm_IMAP_List::connect($form['imap_server_id'], $cache);
1522            if (imap_authed($imap)) {
1523                $imap->read_only = $prefetch;
1524                if ($imap->select_mailbox(hex2bin($form['folder']))) {
1525                    $this->out('folder_status', array('imap_'.$form['imap_server_id'].'_'.$form['folder'] => $imap->folder_state));
1526                    $msg_struct = $imap->get_message_structure($form['imap_msg_uid']);
1527                    $this->out('msg_struct', $msg_struct);
1528                    if ($part !== false) {
1529                        if ($part == 0) {
1530                            $max = 500000;
1531                        }
1532                        else {
1533                            $max = false;
1534                        }
1535                        $struct = $imap->search_bodystructure($msg_struct, array('imap_part_number' => $part));
1536                        $msg_struct_current = array_shift($struct);
1537                        $msg_text = $imap->get_message_content($form['imap_msg_uid'], $part, $max, $msg_struct_current);
1538                    }
1539                    else {
1540                        if (!$this->user_config->get('text_only_setting', false)) {
1541                            list($part, $msg_text) = $imap->get_first_message_part($form['imap_msg_uid'], 'text', 'html', $msg_struct);
1542                            if (!$part) {
1543                                list($part, $msg_text) = $imap->get_first_message_part($form['imap_msg_uid'], 'text', false, $msg_struct);
1544                            }
1545                        }
1546                        else {
1547                            list($part, $msg_text) = $imap->get_first_message_part($form['imap_msg_uid'], 'text', false, $msg_struct);
1548                        }
1549                        $struct = $imap->search_bodystructure( $msg_struct, array('imap_part_number' => $part));
1550                        $msg_struct_current = array_shift($struct);
1551                        if (!trim($msg_text)) {
1552                            if (is_array($msg_struct_current) && array_key_exists('subtype', $msg_struct_current)) {
1553                                if ($msg_struct_current['subtype'] == 'plain') {
1554                                    $subtype = 'html';
1555                                }
1556                                else {
1557                                    $subtype = 'plain';
1558                                }
1559                                list($part, $msg_text) = $imap->get_first_message_part($form['imap_msg_uid'], 'text', $subtype, $msg_struct);
1560                                $struct = $imap->search_bodystructure($msg_struct, array('imap_part_number' => $part));
1561                                $msg_struct_current = array_shift($struct);
1562                            }
1563                        }
1564                    }
1565                    if (isset($msg_struct_current['subtype']) && strtolower($msg_struct_current['subtype'] == 'html')) {
1566                        $msg_text = add_attached_images($msg_text, $form['imap_msg_uid'], $msg_struct, $imap);
1567                    }
1568                    $save_reply_text = false;
1569                    if ($part == 0 || (isset($msg_struct_current['type']) && strtolower($msg_struct_current['type'] == 'text'))) {
1570                        $save_reply_text = true;
1571                    }
1572                    $msg_headers = $imap->get_message_headers($form['imap_msg_uid']);
1573                    $this->out('list_headers', get_list_headers($msg_headers));
1574                    $this->out('msg_headers', $msg_headers);
1575                    $this->out('imap_prefecth', $prefetch);
1576                    $this->out('imap_msg_part', "$part");
1577                    $this->out('use_message_part_icons', $this->user_config->get('msg_part_icons_setting', false));
1578                    $this->out('simple_msg_part_view', $this->user_config->get('simple_msg_parts_setting', false));
1579                    if ($msg_struct_current) {
1580                        $this->out('msg_struct_current', $msg_struct_current);
1581                    }
1582                    $this->out('msg_text', $msg_text);
1583                    $this->out('msg_download_args', sprintf("page=message&amp;uid=%s&amp;list_path=imap_%d_%s&amp;imap_download_message=1", $form['imap_msg_uid'], $form['imap_server_id'], $form['folder']));
1584                    if (!$prefetch) {
1585                        clear_existing_reply_details($this->session);
1586                        if ($part == 0) {
1587                            $msg_struct_current['type'] = 'text';
1588                            $msg_struct_current['subtype'] = 'plain';
1589                        }
1590                        $this->session->set(sprintf('reply_details_imap_%d_%s_%s', $form['imap_server_id'], $form['folder'], $form['imap_msg_uid']),
1591                            array('msg_struct' => $msg_struct_current, 'msg_text' => ($save_reply_text ? $msg_text : ''), 'msg_headers' => $msg_headers));
1592                    }
1593                }
1594            }
1595        }
1596    }
1597}
1598
1599/**
1600 * Hide or unhide an IMAP server
1601 * @subpackage imap/handler
1602 */
1603class Hm_Handler_imap_hide extends Hm_Handler_Module {
1604    /**
1605     * Hide or unhide an IMAP server from combined pages and searches
1606     */
1607    public function process() {
1608        if (isset($this->request->post['hide_imap_server'])) {
1609            list($success, $form) = $this->process_form(array('imap_server_id'));
1610            if ($success) {
1611                Hm_IMAP_List::toggle_hidden($form['imap_server_id'], (bool) $this->request->post['hide_imap_server']);
1612                Hm_Msgs::add('Hidden status updated');
1613                $this->session->record_unsaved(sprintf('%s server hidden status updated', imap_server_type($form['imap_server_id'])));
1614            }
1615        }
1616    }
1617}
1618
1619/**
1620 * Delete an IMAP server
1621 * @subpackage imap/handler
1622 */
1623class Hm_Handler_imap_delete extends Hm_Handler_Module {
1624    /**
1625     * Remove an IMAP server completely, used on the servers page
1626     */
1627    public function process() {
1628        if (isset($this->request->post['imap_delete'])) {
1629            list($success, $form) = $this->process_form(array('imap_server_id'));
1630            if ($success) {
1631                $res = Hm_IMAP_List::del($form['imap_server_id']);
1632                if ($res) {
1633                    $this->out('deleted_server_id', $form['imap_server_id']);
1634                    Hm_Msgs::add('Server deleted');
1635                    $this->session->record_unsaved(sprintf('%s server deleted', imap_server_type($form['imap_server_id'])));
1636                }
1637            }
1638            else {
1639                $this->out('old_form', $form);
1640            }
1641        }
1642    }
1643}
1644