/core/src/main/php/peer/mail/store/MaildirStore.class.php

https://github.com/ghiata/xp-framework · PHP · 323 lines · 171 code · 41 blank · 111 comment · 26 complexity · 39c9ef461489176ddda73a2e6ed934a7 MD5 · raw file

  1. <?php
  2. /* This class is part of the XP framework
  3. *
  4. * $Id$
  5. */
  6. uses(
  7. 'peer.mail.store.MailStore',
  8. 'io.Folder',
  9. 'io.File'
  10. );
  11. /**
  12. * Mail store
  13. *
  14. * @see http://cr.yp.to/proto/maildir.html
  15. * @see http://www.courier-mta.org/maildir.html
  16. * @purpose Incarnation of abstract class MailStore for Maildir
  17. * @experimental
  18. */
  19. class MaildirStore extends MailStore {
  20. public
  21. $cache= NULL,
  22. $currentFolder= NULL;
  23. public
  24. $_folder= NULL,
  25. $_root= NULL;
  26. /**
  27. * Constructs a MaildirStore object.
  28. *
  29. */
  30. public function __construct($cache= NULL) {
  31. parent::__construct($cache);
  32. }
  33. /**
  34. * Opens a Maildir store. If no parameter is given, this opens
  35. * the users default mailbox located in $HOME/Maildir/.
  36. *
  37. * @param string folder default NULL
  38. * @return bool success
  39. */
  40. public function open($folder= NULL) {
  41. if (NULL === $folder)
  42. $folder= getenv ('HOME').DIRECTORY_SEPARATOR.'Maildir';
  43. try {
  44. $this->_folder= new Folder ($folder);
  45. $this->_folder->open();
  46. } catch (IOException $e) {
  47. $this->_folder= NULL;
  48. return $e;
  49. }
  50. $this->currentfolder= '';
  51. $this->_root= realpath ($folder);
  52. return TRUE;
  53. }
  54. /**
  55. * Returns the non-global foldername.
  56. *
  57. * @param string folder
  58. * @return string realfolder
  59. */
  60. protected function _getFolderName($folder) {
  61. return str_replace (
  62. $this->_root,
  63. '',
  64. realpath ($folder)
  65. );
  66. }
  67. /**
  68. * Closes currently open mailstore.
  69. *
  70. * @return bool
  71. */
  72. public function close() {
  73. return $this->_folder->close();
  74. }
  75. /**
  76. * Check whether a folder exists
  77. *
  78. * @param string name
  79. * @return bool
  80. */
  81. public function hasFolder($name) {
  82. $f= new Folder ($this->_folder->getURI().DIRECTORY_SEPARATOR.$name);
  83. return $f->exists();
  84. }
  85. /**
  86. * Opens a subfolder of the current folder and returns
  87. * an object of that mailbox.
  88. *
  89. * @param string foldername
  90. * @return peer.mail.MailFolder folder;
  91. */
  92. public function getFolder($name) {
  93. $f= new Folder ($this->_folder->getURI().DIRECTORY_SEPARATOR.$name);
  94. if (!$f->exists())
  95. throw (new MessagingException (
  96. 'Maildir does not exist: '.$f->getURI()
  97. ));
  98. $mf= new MailFolder ($this, $name);
  99. return $mf;
  100. }
  101. /**
  102. * Returns a list of all subfolders in current folder.
  103. *
  104. * @return array* folders array of peer.mail.MailFolder objects
  105. */
  106. public function getFolders() {
  107. $f= array();
  108. while ($entry= $this->_folder->getEntry()) {
  109. if (is_dir ($this->_folder->getURI().DIRECTORY_SEPARATOR.$entry)) {
  110. if ('.' != $entry{0} || '.' == $entry || '..' == $entry) {
  111. $f[]= new MailFolder (
  112. $this,
  113. $this->_getFolderName ($entry)
  114. );
  115. }
  116. }
  117. }
  118. return $f;
  119. }
  120. /**
  121. * Opens a folder.
  122. *
  123. * @param peer.mail.MailFolder folder
  124. * @param bool readonly default FALSE
  125. * @return bool success
  126. * @throws lang.IllegalAccessException if another folder is still open
  127. * @throws io.IOException if folder cannot be opened
  128. */
  129. public function openFolder($f, $readonly= FALSE) {
  130. // Is it already open?
  131. if ($this->currentfolder === $f->name)
  132. return TRUE;
  133. // Only one open folder at a time
  134. if (NULL !== $this->currentfolder) {
  135. trigger_error('Currently open Folder: '.$this->currentfolder, E_USER_NOTICE);
  136. throw new IllegalAccessException(
  137. 'There can only be one open folder at a time. Close the currently open folder first.',
  138. $f->name
  139. );
  140. }
  141. $nf= new Folder ($this->_root.DIRECTORY_SEPARATOR.$f->name);
  142. $nf->open();
  143. $this->_folder= $nf;
  144. $this->currentfolder= $f->name;
  145. return TRUE;
  146. }
  147. /**
  148. * Closes an open folder.
  149. *
  150. * @param peer.mail.MailFolder folder
  151. * @return bool success
  152. * @throws lang.IllegalArgumentException if folder is not opened folder
  153. */
  154. public function closeFolder($f) {
  155. // Is it already open?
  156. if ($this->currentfolder !== $f->name)
  157. throw (new IllegalArgumentException (
  158. 'Cannot close non-opened folder!',
  159. $f->name
  160. ));
  161. $this->_folder->close();
  162. $this->currentfolder= NULL;
  163. return TRUE;
  164. }
  165. /**
  166. * Gets the count of messages with speciefied attribute
  167. * or all messages when no attribute was specified
  168. *
  169. * @param peer.mail.Mailfolder f
  170. * @param int attr default 0xFFFF
  171. * @return int count
  172. */
  173. public function getMessageCount($f, $attr= 0xFFFF) {
  174. $this->openFolder ($f);
  175. $f= new Folder ($f->name.DIRECTORY_SEPARATOR.'cur');
  176. if (!$f->exists())
  177. return 0;
  178. $cnt= 0;
  179. $f->open();
  180. while ($e= $f->getEntry()) {
  181. if ($attr & $this->_getMailFlags ($e)) $cnt++;
  182. }
  183. $f->close();
  184. return $cnt;
  185. }
  186. /**
  187. * Returns the URI to a specific message in a Maildir. This is the
  188. * absolute path to that file.
  189. *
  190. * @param peer.mail.MailFolder folder
  191. * @param int number
  192. * @return string uri
  193. */
  194. protected function _getMessageURI($f, $nr) {
  195. $this->_folder->rewind();
  196. while (FALSE !== ($entry= $this->_folder->getEntry()) && $nr <= $i++) {
  197. if ($nr == $i)
  198. return $f->getURI().DIRECTORY_SEPARATOR.$entry;
  199. }
  200. return FALSE;
  201. }
  202. /**
  203. * Returns the flags of the specified message given in the filename.
  204. *
  205. * @param string filename
  206. * @return int flags
  207. */
  208. protected function _getMailFlags($filename) {
  209. static
  210. $maildirFlagMatrix= array (
  211. 'R' => MAIL_FLAG_ANSWERED,
  212. 'S' => MAIL_FLAG_SEEN,
  213. 'T' => MAIL_FLAG_DELETED,
  214. 'D' => MAIL_FLAG_DRAFT,
  215. 'F' => MAIL_FLAG_TAGGED
  216. );
  217. $flagString= substr ($e, strpos ('2,'));
  218. for ($i= 0; $i < count ($flagString); $i++)
  219. $flags|= $maildirFlagMatrix[$flagString{$i}];
  220. return $flags;
  221. }
  222. /**
  223. * Reads the whole message, applies the header information,
  224. * sets the body as a plain text (thus does not parse any
  225. * MIME-Information and returns the created Message object.
  226. *
  227. * @param string filename
  228. * @return peer.mail.Message
  229. * @throws io.IOException if file cannot be read
  230. */
  231. protected function _readMessageRaw($filename) {
  232. $header= '';
  233. $body= '';
  234. $f= new File ($filename);
  235. $f->open ();
  236. $d= $f->read ($f->size());
  237. $f->close();
  238. if (FALSE === ($hdrEnd= strpos ($d, "\n\r\n\r")))
  239. $hdrEnd= 0;
  240. $h= substr ($c, 0, $hdrEnd);
  241. $b= substr ($c, $hdrEnd);
  242. $msg= new Message();
  243. $msg->setHdrString ($h);
  244. $msg->setBody ($b);
  245. return $msg;
  246. }
  247. /**
  248. * Returns an array of messages specified by the numbers in the
  249. * argument
  250. *
  251. * @param peer.mail.MailFolder folder
  252. * @param var* msgnums
  253. * @return array messages
  254. */
  255. public function getMessages($f) {
  256. $this->openFolder ($f);
  257. if (1 == func_num_args()) {
  258. $count= $this->getMessageCount ();
  259. $msgnums= range (1, $count);
  260. } else {
  261. $msgnums= array();
  262. for ($i= 1, $s= func_num_args(); $i < $s; $i++) {
  263. $arg= func_get_arg($i);
  264. $msgnums= array_merge($msgnums, (array)$arg);
  265. }
  266. }
  267. $messages= array();
  268. foreach ($msgnums as $msg) {
  269. $filename= $this->_getMessageURI($f, $msg);
  270. $flags= $this->_getMailFlags($filename);
  271. try {
  272. $msg= $this->_readMessageRaw($filename);
  273. } catch (IOException $e) {
  274. // Ignore any errors
  275. continue;
  276. }
  277. $messages[]= $msg;
  278. }
  279. return $messages;
  280. }
  281. }
  282. ?>