1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14:
15:
16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30:
31: class IMP_Message
32: {
33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49:
50: public function copy($targetMbox, $action, IMP_Indices $indices,
51: array $opts = array())
52: {
53: global $conf, $notification;
54:
55: if (!count($indices)) {
56: return false;
57: }
58:
59: $targetMbox = IMP_Mailbox::get($targetMbox);
60:
61:
62: if ($conf['tasklist']['use_tasklist'] &&
63: (strpos($targetMbox, IMP::TASKLIST_EDIT) === 0)) {
64: $this->_createTasksOrNotes(str_replace(IMP::TASKLIST_EDIT, '', $targetMbox), $action, $indices, 'task');
65: return true;
66: }
67:
68:
69: if ($conf['notepad']['use_notepad'] &&
70: (strpos($targetMbox, IMP::NOTEPAD_EDIT) === 0)) {
71: $this->_createTasksOrNotes(str_replace(IMP::NOTEPAD_EDIT, '', $targetMbox), $action, $indices, 'note');
72: return true;
73: }
74:
75: if (!empty($opts['create']) && !$targetMbox->create()) {
76: return false;
77: }
78:
79: $imap_move = false;
80: $return_value = true;
81:
82: switch ($action) {
83: case 'move':
84: $imap_move = true;
85: $message = _("There was an error moving messages from \"%s\" to \"%s\". This is what the server said");
86: break;
87:
88: case 'copy':
89: $message = _("There was an error copying messages from \"%s\" to \"%s\". This is what the server said");
90: break;
91: }
92:
93: foreach ($indices as $ob) {
94: try {
95: if ($targetMbox->readonly) {
96: throw new IMP_Exception(_("The target directory is read-only."));
97: }
98:
99: if (($action == 'move') && $ob->mbox->readonly) {
100: throw new IMP_Exception(_("The source directory is read-only."));
101: }
102:
103: $ob->mbox->uidvalid;
104:
105:
106: $imp_imap = $ob->mbox->imp_imap;
107: $imp_imap->copy($ob->mbox, $targetMbox, array(
108: 'ids' => $imp_imap->getIdsOb($ob->uids),
109: 'move' => $imap_move
110: ));
111:
112: if (($action == 'move') &&
113: !empty($opts['mailboxob']) &&
114: $opts['mailboxob']->isBuilt()) {
115: $opts['mailboxob']->removeMsgs($ob->mbox->getIndicesOb($ob->uids));
116: }
117: } catch (Exception $e) {
118: $error_msg = sprintf($message, $ob->mbox->display, $targetMbox->display) . ': ' . $e->getMessage();
119: if ($e instanceof IMP_Imap_Exception) {
120: $e->notify($error_msg);
121: } else {
122: $notification->push($error_msg, 'horde.error');
123: }
124: $return_value = false;
125: }
126: }
127:
128: return $return_value;
129: }
130:
131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146:
147: public function delete(IMP_Indices $indices, array $opts = array())
148: {
149: global $injector, $notification, $prefs;
150:
151: if (!count($indices)) {
152: return false;
153: }
154:
155: $trash = IMP_Mailbox::getPref(IMP_Mailbox::MBOX_TRASH);
156: $use_trash = $prefs->getValue('use_trash');
157: if ($use_trash && !$trash) {
158: $notification->push(_("Cannot move messages to Trash - no Trash mailbox set in preferences."), 'horde.error');
159: return false;
160: }
161:
162: $ajax_queue = $injector->getInstance('IMP_Ajax_Queue');
163: $maillog = empty($opts['keeplog'])
164: ? $injector->getInstance('IMP_Maillog')
165: : null;
166: $return_value = 0;
167:
168:
169: $no_expunge = $use_trash_mbox = $use_vtrash = false;
170: if ($use_trash &&
171: empty($opts['nuke']) &&
172: $injector->getInstance('IMP_Factory_Imap')->create()->access(IMP_Imap::ACCESS_TRASH)) {
173: $use_vtrash = $trash->vtrash;
174: $use_trash_mbox = !$use_vtrash;
175: }
176:
177: 178: 179: 180:
181: $mark_seen = empty($opts['nuke']) &&
182: ($use_vtrash || $prefs->getValue('delete_mark_seen'));
183:
184: if ($use_trash_mbox && !$trash->create()) {
185: 186:
187: $no_expunge = true;
188: $return_value = $use_trash_mbox = false;
189: }
190:
191: foreach ($indices as $ob) {
192: try {
193: if (!$ob->mbox->access_deletemsgs) {
194: throw new IMP_Exception(_("This mailbox is read-only."));
195: }
196:
197: $ob->mbox->uidvalid;
198: } catch (IMP_Exception $e) {
199: $notification->push(sprintf(_("There was an error deleting messages from the mailbox \"%s\"."), $ob->mbox->display) . ' ' . $e->getMessage(), 'horde.error');
200: $return_value = false;
201: continue;
202: }
203:
204: $imp_indices = $ob->mbox->getIndicesOb($ob->uids);
205: if ($return_value !== false) {
206: $return_value += count($ob->uids);
207: }
208:
209: $imp_imap = $ob->mbox->imp_imap;
210: $ids_ob = $imp_imap->getIdsOb($ob->uids);
211:
212:
213: if ($use_trash_mbox &&
214: ($ob->mbox != $trash) &&
215:
216: !$ob->mbox->remote_mbox) {
217: if ($ob->mbox->access_expunge) {
218: try {
219: if ($mark_seen) {
220: $imp_imap->store($ob->mbox, array(
221: 'add' => array(
222: Horde_Imap_Client::FLAG_SEEN
223: ),
224: 'ids' => $ids_ob
225: ));
226: }
227:
228: $imp_imap->copy($ob->mbox, $trash, array(
229: 'ids' => $ids_ob,
230: 'move' => true
231: ));
232:
233: if (!empty($opts['mailboxob']) &&
234: $opts['mailboxob']->isBuilt()) {
235: $opts['mailboxob']->removeMsgs($imp_indices);
236: }
237: } catch (IMP_Imap_Exception $e) {
238: if ($e->getCode() == $e::OVERQUOTA) {
239: $notification->push(_("You are over your quota, so your messages will be permanently deleted instead of moved to the Trash mailbox."), 'horde.warning');
240: $opts['nuke'] = true;
241: return $this->delete(new IMP_Indices($ob->mbox, $ob->uids), $opts);
242: }
243:
244: return false;
245: }
246: }
247: } else {
248: 249: 250: 251: 252:
253: if ($maillog) {
254: $delete_ids = array();
255: foreach ($ids_ob as $val) {
256: $delete_ids[] = new IMP_Maillog_Message(
257: new IMP_Indices($ob->mbox, $val)
258: );
259: }
260: $maillog->deleteLog($delete_ids);
261: }
262:
263:
264: $expunge_now = false;
265: $del_flags = array(Horde_Imap_Client::FLAG_DELETED);
266:
267: if (!$use_vtrash &&
268: (!$imp_imap->access(IMP_Imap::ACCESS_TRASH) ||
269: !empty($opts['nuke']) ||
270: ($use_trash &&
271: ($ob->mbox == $trash) || $ob->mbox->remote_mbox))) {
272:
273: $expunge_now = !$no_expunge;
274: } elseif ($mark_seen) {
275: $del_flags[] = Horde_Imap_Client::FLAG_SEEN;
276: }
277:
278: try {
279: $imp_imap->store($ob->mbox, array(
280: 'add' => $del_flags,
281: 'ids' => $ids_ob
282: ));
283: if ($expunge_now) {
284: $this->expungeMailbox(
285: $imp_indices->indices(),
286: array(
287: 'mailboxob' => empty($opts['mailboxob']) ? null : $opts['mailboxob']
288: )
289: );
290: } elseif (!empty($opts['mailboxob']) &&
291: $opts['mailboxob']->isBuilt() &&
292: $ob->mbox->hideDeletedMsgs()) {
293: $opts['mailboxob']->removeMsgs($imp_indices);
294: } else {
295: $ajax_queue->flag($del_flags, true, new IMP_Indices($ob->mbox, $ids_ob));
296: }
297: } catch (IMP_Imap_Exception $e) {}
298: }
299: }
300:
301: return $return_value;
302: }
303:
304: 305: 306: 307: 308: 309: 310: 311: 312:
313: public function undelete(IMP_Indices $indices)
314: {
315: return $this->flag(array(
316: 'remove' => array(Horde_Imap_Client::FLAG_DELETED)
317: ), $indices);
318: }
319:
320: 321: 322: 323: 324: 325: 326: 327: 328: 329: 330:
331: protected function _createTasksOrNotes($list, $action,
332: IMP_Indices $indices, $type)
333: {
334: global $injector, $registry, $notification;
335:
336: foreach ($indices as $ob) {
337: foreach ($ob->uids as $uid) {
338:
339: $imp_contents = $injector->getInstance('IMP_Factory_Contents')->create($ob->mbox->getIndicesOb($uid));
340:
341:
342: $imp_headers = $imp_contents->getHeader();
343: $subject = $imp_headers->getValue('subject');
344:
345:
346: $body_part = $imp_contents->getMIMEPart($imp_contents->findBody());
347: $flowed = new Horde_Text_Flowed($body_part->getContents());
348: if ($body_part->getContentTypeParameter('delsp') == 'yes') {
349: $flowed->setDelSp(true);
350: }
351: $body = $flowed->toFlowed(false);
352:
353:
354: 355: 356:
357: $body = Horde_String::convertCharset($body, $body_part->getCharset(), 'UTF-8');
358:
359:
360: $vCal = new Horde_Icalendar();
361: $vCal->setAttribute('PRODID', '-//The Horde Project//IMP ' . $registry->getVersion() . '//EN');
362: $vCal->setAttribute('METHOD', 'PUBLISH');
363:
364: switch ($type) {
365: case 'task':
366: 367:
368: $vTodo = Horde_Icalendar::newComponent('vtodo', $vCal);
369: $vTodo->setAttribute('SUMMARY', $subject);
370: $vTodo->setAttribute('DESCRIPTION', $body);
371: $vTodo->setAttribute('PRIORITY', '3');
372:
373:
374: try {
375: $lists = $registry->call('tasks/listTasklists', array(false, Horde_Perms::EDIT));
376: } catch (Horde_Exception $e) {
377: $lists = null;
378: $notification->push($e);
379: }
380:
381: 382:
383: try {
384: $res = $registry->call('tasks/import', array($vTodo, 'text/calendar', $list));
385: } catch (Horde_Exception $e) {
386: $res = null;
387: $notification->push($e);
388: }
389: break;
390:
391: case 'note':
392: 393:
394: $vNote = Horde_Icalendar::newComponent('vnote', $vCal);
395: $vNote->setAttribute('BODY', $subject . "\n". $body);
396:
397:
398: try {
399: $lists = $registry->call('notes/listNotepads', array(false, Horde_Perms::EDIT));
400: } catch (Horde_Exception $e) {
401: $lists = null;
402: $notification->push($e);
403: }
404:
405: 406:
407: try {
408: $res = $registry->call('notes/import', array($vNote, 'text/x-vnote', $list));
409: } catch (Horde_Exception $e) {
410: $res = null;
411: $notification->push($e);
412: }
413: break;
414: }
415:
416: if (!is_null($res)) {
417: if (!$res) {
418: switch ($type) {
419: case 'task':
420: $notification->push(_("An unknown error occured while creating the new task."), 'horde.error');
421: break;
422:
423: case 'note':
424: $notification->push(_("An unknown error occured while creating the new note."), 'horde.error');
425: break;
426: }
427: } elseif (!is_null($lists)) {
428: $name = '"' . htmlspecialchars($subject) . '"';
429:
430: 431:
432: try {
433: switch ($type) {
434: case 'task':
435: $link = $registry->link('tasks/show', array('uid' => $res));
436: break;
437:
438: case 'note':
439: $link = $registry->hasMethod('notes/show')
440: ? $registry->link('notes/show', array('uid' => $res))
441: : false;
442: break;
443: }
444:
445: if ($link) {
446: $name = sprintf('<a href="%s">%s</a>', Horde::url($link), $name);
447: }
448:
449: $notification->push(sprintf(_("%s was successfully added to \"%s\"."), $name, htmlspecialchars($lists[$list]->get('name'))), 'horde.success', array('content.raw'));
450: } catch (Horde_Exception $e) {}
451: }
452: }
453: }
454: }
455:
456:
457: if ($action == 'move') {
458: $this->delete($indices);
459: }
460: }
461:
462: 463: 464: 465: 466: 467: 468: 469: 470: 471: 472: 473: 474: 475:
476: public function stripPart(IMP_Indices $indices, $partid = null,
477: array $opts = array())
478: {
479: global $injector;
480:
481: list($mbox, $uid) = $indices->getSingle();
482: if (!$uid) {
483: return;
484: }
485:
486: if ($mbox->readonly) {
487: throw new IMP_Exception(_("Cannot strip the MIME part as the mailbox is read-only."));
488: }
489:
490: $uidvalidity = $mbox->uidvalid;
491:
492: $contents = $injector->getInstance('IMP_Factory_Contents')->create($indices);
493: $message = $contents->getMIMEMessage();
494: $boundary = trim($message->getContentTypeParameter('boundary'), '"');
495:
496: $url = new Horde_Imap_Client_Url();
497: $url->mailbox = $mbox;
498: $url->uid = $uid;
499: $url->uidvalidity = $uidvalidity;
500:
501: $imp_imap = $mbox->imp_imap;
502:
503:
504: $url->section = 'HEADER';
505: $parts = array(
506: array(
507: 't' => 'url',
508: 'v' => strval($url)
509: )
510: );
511:
512: for ($id = 1; ; ++$id) {
513: $part = $message->getPart($id);
514: if (!$part) {
515: break;
516: }
517:
518: $parts[] = array(
519: 't' => 'text',
520: 'v' => "\r\n--" . $boundary . "\r\n"
521: );
522:
523: if (($id != 1) && is_null($partid) || ($id == $partid)) {
524: $newPart = new Horde_Mime_Part();
525: $newPart->setType('text/plain');
526:
527:
528: $newPart->setCharset('UTF-8');
529: $newPart->setContents(sprintf(_("[Attachment stripped: Original attachment type: %s, name: %s]"), $part->getType(), $contents->getPartName($part)));
530: $newPart->setDisposition('attachment');
531:
532: $parts[] = array(
533: 't' => 'text',
534: 'v' => $newPart->toString(array(
535: 'canonical' => true,
536: 'headers' => true,
537: 'stream' => true
538: ))
539: );
540: } else {
541: $url->section = $id . '.MIME';
542: $parts[] = array(
543: 't' => 'url',
544: 'v' => strval($url)
545: );
546:
547: $url->section = $id;
548: $parts[] = array(
549: 't' => 'url',
550: 'v' => strval($url)
551: );
552: }
553: }
554:
555: $parts[] = array(
556: 't' => 'text',
557: 'v' => "\r\n--" . $boundary . "--\r\n"
558: );
559:
560:
561: $query = new Horde_Imap_Client_Fetch_Query();
562: $query->imapDate();
563: $query->flags();
564:
565: try {
566: $res = $imp_imap->fetch($mbox, $query, array(
567: 'ids' => $imp_imap->getIdsOb($uid)
568: ))->first();
569: if (is_null($res)) {
570: throw new IMP_Imap_Exception();
571: }
572: $flags = $res->getFlags();
573:
574: 575:
576: if ($mbox->vinbox) {
577: $flags = array_values(array_diff($flags, array(Horde_Imap_Client::FLAG_SEEN)));
578: }
579:
580: $new_uid = $imp_imap->append($mbox, array(
581: array(
582: 'data' => $parts,
583: 'flags' => $flags,
584: 'internaldate' => $res->getImapDate()
585: )
586: ))->ids;
587: $new_uid = reset($new_uid);
588: } catch (IMP_Imap_Exception $e) {
589: throw new IMP_Exception(_("An error occured while attempting to strip the attachment."));
590: }
591:
592: $this->delete($indices, array(
593: 'keeplog' => true,
594: 'mailboxob' => empty($opts['mailboxob']) ? null : $opts['mailboxob'],
595: 'nuke' => true
596: ));
597:
598: $indices_ob = $mbox->getIndicesOb($new_uid);
599:
600: if (!empty($opts['mailboxob'])) {
601: $opts['mailboxob']->setIndex($indices_ob);
602: }
603:
604:
605: $vars = $injector->getInstance('Horde_Variables');
606: if (isset($vars->buid)) {
607: list(,$vars->buid) = $mbox->toBuids($indices_ob)->getSingle();
608: }
609: if (isset($vars->uid)) {
610: $vars->uid = $new_uid;
611: }
612:
613: return $indices_ob;
614: }
615:
616: 617: 618: 619: 620: 621: 622: 623: 624: 625: 626: 627: 628: 629: 630: 631: 632:
633: public function flag(array $action, IMP_Indices $indices,
634: array $opts = array())
635: {
636: global $injector, $notification;
637:
638: if (!count($indices)) {
639: return false;
640: }
641:
642: $opts = array_merge(array(
643: 'unchangedsince' => array()
644: ), $opts);
645:
646: $ajax_queue = $injector->getInstance('IMP_Ajax_Queue');
647: $ret = true;
648:
649: foreach ($indices as $ob) {
650: try {
651: if ($ob->mbox->readonly) {
652: throw new IMP_Exception(_("This mailbox is read-only."));
653: }
654:
655: $ob->mbox->uidvalid;
656:
657: $unchangedsince = isset($opts['unchangedsince'][strval($ob->mbox)])
658: ? $opts['unchangedsince'][strval($ob->mbox)]
659: : null;
660:
661:
662: $imp_imap = $ob->mbox->imp_imap;
663: $res = $imp_imap->store($ob->mbox, array_merge($action, array_filter(array(
664: 'ids' => $imp_imap->getIdsOb($ob->uids),
665: 'unchangedsince' => $unchangedsince
666: ))));
667:
668: $flag_change = $ob->mbox->getIndicesOb($ob->uids);
669:
670: if ($unchangedsince && count($res)) {
671: foreach ($res as $val) {
672: unset($flag_change[$val]);
673: }
674: if (empty($opts['silent'])) {
675: $notification->push(sprintf(_("Flags were not changed for at least one message in the mailbox \"%s\" because the flags were altered by another connection to the mailbox prior to this request. You may redo the flag action if desired; this warning is precautionary to ensure you don't overwrite flag changes."), $ob->mbox->display), 'horde.warning');
676: $ret = false;
677: }
678: }
679:
680: foreach ($action as $key => $val) {
681: $ajax_queue->flag($val, ($key == 'add'), $flag_change);
682: if ($indices instanceof IMP_Indices_Mailbox) {
683: $ajax_queue->flag($val, ($key == 'add'), $indices->mailbox->toBuids($flag_change));
684: }
685: }
686: } catch (Exception $e) {
687: if (empty($opts['silent'])) {
688: $notification->push(sprintf(_("There was an error flagging messages in the mailbox \"%s\": %s."), $ob->mbox->display, $e->getMessage()), 'horde.error');
689: }
690: $ret = false;
691: }
692: }
693:
694: return $ret;
695: }
696:
697: 698: 699: 700: 701: 702: 703: 704: 705: 706: 707:
708: public function flagAllInMailbox($flags, $mboxes, $action = true)
709: {
710: if (empty($mboxes) || !is_array($mboxes)) {
711: return false;
712: }
713:
714: $action_array = $action
715: ? array('add' => $flags)
716: : array('remove' => $flags);
717: $ajax_queue = $GLOBALS['injector']->getInstance('IMP_Ajax_Queue');
718:
719: $ajax_queue->poll($mboxes);
720:
721: foreach (IMP_Mailbox::get($mboxes) as $val) {
722: try {
723: 724:
725: $mailbox_list = $val->list_ob->getIndicesOb();
726: $val->imp_imap->store($val, $action_array);
727: $ajax_queue->flag(reset($action_array), $action, $mailbox_list);
728: } catch (IMP_Imap_Exception $e) {
729: return false;
730: }
731: }
732:
733: return true;
734: }
735:
736: 737: 738: 739: 740: 741: 742: 743: 744: 745: 746: 747: 748: 749: 750: 751: 752:
753: public function expungeMailbox($mbox_list, array $opts = array())
754: {
755: $msg_list = !empty($opts['list']);
756:
757: if (empty($mbox_list)) {
758: return $msg_list ? new IMP_Indices() : null;
759: }
760:
761: $process_list = $update_list = array();
762:
763: foreach ($mbox_list as $key => $val) {
764: $key = IMP_Mailbox::get($key);
765:
766: if ($key->access_expunge) {
767: $ids = $key->imp_imap->getIdsOb(is_array($val) ? $val : Horde_Imap_Client_Ids::ALL);
768:
769: if ($key->search) {
770: foreach ($key->getSearchOb()->mboxes as $skey) {
771: $process_list[] = array($skey, $ids);
772: }
773: } else {
774: $process_list[] = array($key, $ids);
775: }
776: }
777: }
778:
779:
780: foreach ($process_list as $val) {
781: 782:
783: if (!$val[1]->all) {
784: try {
785: $val[0]->uidvalid;
786: } catch (IMP_Exception $e) {
787: continue;
788: }
789: }
790:
791: try {
792: $update_list[strval($val[0])] = $val[0]->imp_imap->expunge($val[0], array(
793: 'ids' => $val[1],
794: 'list' => $msg_list
795: ));
796:
797: if (!empty($opts['mailboxob']) &&
798: $opts['mailboxob']->isBuilt()) {
799: $opts['mailboxob']->removeMsgs($val[1]->all ? true : $val[0]->getIndicesOb($val[1]));
800: }
801: } catch (IMP_Imap_Exception $e) {}
802: }
803:
804: if ($msg_list) {
805: return new IMP_Indices($update_list);
806: }
807: }
808:
809: 810: 811: 812: 813:
814: public function emptyMailbox($mbox_list)
815: {
816: global $notification, $prefs;
817:
818: $trash = ($prefs->getValue('use_trash'))
819: ? IMP_Mailbox::getPref(IMP_Mailbox::MBOX_TRASH)
820: : null;
821:
822: foreach (IMP_Mailbox::get($mbox_list) as $mbox) {
823: if (!$mbox->access_empty) {
824: $notification->push(sprintf(_("Could not delete messages from %s. This mailbox is read-only."), $mbox->display), 'horde.error');
825: continue;
826: }
827:
828: if ($mbox->vtrash) {
829: $this->expungeMailbox(array_flip($mbox->getSearchOb()->mboxes));
830: $notification->push(_("Emptied all messages from Virtual Trash Folder."), 'horde.success');
831: continue;
832: }
833:
834: 835:
836: try {
837: $imp_imap = $mbox->imp_imap;
838: $status = $imp_imap->status($mbox, Horde_Imap_Client::STATUS_MESSAGES);
839: if (empty($status['messages'])) {
840: $notification->push(sprintf(_("The mailbox %s is already empty."), $mbox->display), 'horde.message');
841: continue;
842: }
843:
844: if (!$trash || ($trash == $mbox)) {
845: $imp_imap->store($mbox, array(
846: 'add' => array(Horde_Imap_Client::FLAG_DELETED)
847: ));
848: $this->expungeMailbox(array(strval($mbox) => 1));
849: } else {
850: $ret = $imp_imap->search($mbox);
851: $this->delete($mbox->getIndicesOb($ret['match']));
852: }
853:
854: $notification->push(sprintf(_("Emptied all messages from %s."), $mbox->display), 'horde.success');
855: } catch (IMP_Imap_Exception $e) {}
856: }
857: }
858:
859: }
860: