Mail
[ ]
[ ]
[ ]
[ ]
[ ]
Source for file imap_set.php
Documentation is available at imap_set.php
1. <?php
2. /**
3. * File containing the ezcMailImapSet class.
4. *
5. * @package Mail
6. * @version 1.4
7. * @copyright Copyright (C) 2005-2007 eZ systems as. All rights reserved.
8. * @license http://ez.no/licenses/new_bsd New BSD License
9. */
10.
11. /**
12. * ezcMailImapSet is an internal class that fetches a series of mail
13. * from the IMAP server.
14. *
15. * The IMAP set works on an existing connection and a list of the messages that
16. * the user wants to fetch. The user must accept all the data for each mail for
17. * correct behaviour.
18. *
19. * @package Mail
20. * @version 1.4
21. */
22. class ezcMailImapSet implements ezcMailParserSet
23. {
24. /**
25. * Holds the list of messages that the user wants to retrieve from the server.
26. *
27. * @var array(int)
28. */
29. private $messages;
30.
31. /**
32. * Holds the current message the user is fetching.
33. *
34. * The variable is null before the first message and false after
35. * the last message has been fetched.
36. *
37. * @var int
38. */
39. private $currentMessage = null;
40.
41. /**
42. * Holds the line that will be read-ahead in order to determine the trailing paranthesis.
43. *
44. * @var string
45. */
46. private $nextData = null;
47.
48. /**
49. * This variable is true if there is more data in the mail that is being fetched.
50. *
51. * It is false if there is no mail being fetched currently or if all the data of the current mail
52. * has been fetched.
53. *
54. * @var bool
55. */
56. private $hasMoreMailData = false;
57.
58. /**
59. * Holds if mail should be deleted from the server after retrieval.
60. *
61. * @var bool
62. */
63. private $deleteFromServer = false;
64.
65. /**
66. * Used to generate a tag for sending commands to the IMAP server.
67. *
68. * @var string
69. */
70. private $currentTag = 'A0000';
71.
72. /**
73. * Holds the mode in which the IMAP commands operate.
74. *
75. * @var string
76. */
77. private $uid;
78.
79. /**
80. * Holds the options for an IMAP mail set.
81. *
82. * @var ezcMailImapSetOptions
83. */
84. private $options;
85.
86. /**
87. * Holds the number of bytes to read from the IMAP server.
88. *
89. * It is set before starting to read a message from the information
90. * returned by the IMAP server in this form:
91. *
92. * <code>
93. * * 2 FETCH (FLAGS (\Answered \Seen) RFC822 {377}
94. * </code>
95. *
96. * In this example, $this->bytesToRead will be set to 377.
97. *
98. * @var int
99. */
100. private $bytesToRead = false;
101.
102. /**
103. * Constructs a new IMAP parser set that will fetch the messages $messages.
104. *
105. * $connection must hold a valid connection to a IMAP server that is ready
106. * to retrieve the messages.
107. *
108. * If $deleteFromServer is set to true the messages will be deleted after retrieval.
109. *
110. * See {@link ezcMailImapSetOptions} for options you can set to IMAP sets.
111. *
112. * @throws ezcMailTransportException
113. * if the server sent a negative response
114. * @param ezcMailTransportConnection $connection
115. * @param array(int) $messages
116. * @param bool $deleteFromServer
117. * @param ezcMailImapSetOptions|array(string=>mixed)$options
118. */
119. public function __construct( ezcMailTransportConnection $connection, array $messages, $deleteFromServer = false, $options = array() )
120. {
121. if ( $options instanceof ezcMailImapSetOptions )
122. {
123. $this->options = $options;
124. }
125. else if ( is_array( $options ) )
126. {
127. $this->options = new ezcMailImapSetOptions( $options );
128. }
129. else
130. {
131. throw new ezcBaseValueException( "options", $options, "ezcMailImapSetOptions|array" );
132. }
133.
134. $this->connection = $connection;
135. $this->messages = $messages;
136. $this->deleteFromServer = $deleteFromServer;
137. $this->nextData = null;
138.
139. $this->uid = ( $this->options->uidReferencing ) ? ezcMailImapTransport::UID : ezcMailImapTransport::NO_UID;
140. }
141.
142. /**
143. * Returns true if all the data has been fetched from this set.
144. *
145. * @return bool
146. */
147. public function isFinished()
148. {
149. return $this->currentMessage === false ? true : false;
150. }
151.
152. /**
153. * Returns one line of data from the current mail in the set.
154. *
155. * Null is returned if there is no current mail in the set or
156. * the end of the mail is reached,
157. *
158. * @return string
159. */
160. public function getNextLine()
161. {
162. if ( $this->currentMessage === null )
163. {
164. // instead of calling $this->nextMail() in the constructor, it is called
165. // here, to avoid sending commands to the server when creating the set, and
166. // instead send the server commands when parsing the set (see ezcMailParser).
167. $this->nextMail();
168. }
169. if ( $this->hasMoreMailData )
170. {
171. $data = ( $this->nextData === null ) ? $this->connection->getLine() : $this->nextData;
172. if ( $this->bytesToRead !== false && $this->bytesToRead >= 0 )
173. {
174. $this->nextData = $this->connection->getLine();
175. $this->bytesToRead -= strlen( $this->nextData );
176. // the next code checks if the current line ends with ')'
177. // and the next line has the command tag (e.g. 'A0034').
178. if ( substr( trim( $data ), strlen( trim( $data ) ) - 1 ) === ')' && strpos( $this->nextData, $this->currentTag ) === 0 )
179. {
180. $this->hasMoreMailData = false;
181. // remove the mail if required by the user.
182. if ( $this->deleteFromServer === true )
183. {
184. $tag = $this->getNextTag();
185. $this->connection->sendData( "{$tag} {$this->uid}STORE {$this->currentMessage} +FLAGS (\\Deleted)" );
186. // skip OK response ("{$tag} OK Store completed.")
187. $response = $this->getResponse( $tag );
188. }
189. return null;
190. }
191. }
192. return $data;
193. }
194. return null;
195. }
196.
197. /**
198. * Moves the set to the next mail and returns true upon success.
199. *
200. * False is returned if there are no more mail in the set.
201. *
202. * @throws ezcMailTransportException
203. * if the server sent a negative response
204. * @return bool
205. */
206. public function nextMail()
207. {
208. if ( $this->currentMessage === null )
209. {
210. $this->currentMessage = reset( $this->messages );
211. }
212. else
213. {
214. $this->currentMessage = next( $this->messages );
215. }
216. $this->nextData = null;
217. $this->bytesToRead = false;
218. if ( $this->currentMessage !== false )
219. {
220. $tag = $this->getNextTag();
221. $this->connection->sendData( "{$tag} {$this->uid}FETCH {$this->currentMessage} RFC822" );
222. $response = $this->connection->getLine();
223. if ( strpos( $response, ' NO ' ) !== false ||
224. strpos( $response, ' BAD ') !== false )
225. {
226. throw new <a href="../Mail/ezcMailTransportException.html">ezcMailTransportException</a>( "The IMAP server sent a negative reply when requesting mail." );
227. }
228. else
229. {
230. $response = $this->getResponse( 'FETCH (', $response );
231. if ( strpos( $response, 'FETCH (' ) !== false )
232. {
233. $this->hasMoreMailData = true;
234. // retrieve the message size from $response, eg. if $response is:
235. // * 2 FETCH (FLAGS (\Answered \Seen) RFC822 {377}
236. // then $this->bytesToRead will be 377
237. preg_match( '/\{(.*)\}/', $response, $matches );
238. if ( count( $matches ) > 0 )
239. {
240. $this->bytesToRead = (int) $matches[1];
241. }
242. return true;
243. }
244. else
245. {
246. $response = $this->getResponse( $tag );
247. if ( strpos( $response, 'OK ' ) === false )
248. {
249. throw new <a href="../Mail/ezcMailTransportException.html">ezcMailTransportException</a>( "The IMAP server sent a negative reply when requesting mail." );
250. }
251. }
252. }
253. }
254. return false;
255. }
256.
257. /**
258. * Reads the responses from the server until encountering $tag.
259. *
260. * In IMAP, each command sent by the client is prepended with a
261. * alphanumeric tag like 'A1234'. The server sends the response
262. * to the client command as lines, and the last line in the response
263. * is prepended with the same tag, and it contains the status of
264. * the command completion ('OK', 'NO' or 'BAD').
265. *
266. * Sometimes the server sends alerts and response lines from other
267. * commands before sending the tagged line, so this method just
268. * reads all the responses until it encounters $tag.
269. *
270. * It returns the tagged line to be processed by the calling method.
271. *
272. * If $response is specified, then it will not read the response
273. * from the server before searching for $tag in $response.
274. *
275. * Before calling this method, a connection to the IMAP server must be
276. * established.
277. *
278. * @param string $tag
279. * @param string $response
280. * @return string
281. */
282. private function getResponse( $tag = null, $response = null )
283. {
284. if ( is_null( $response ) )
285. {
286. $response = $this->connection->getLine();
287. }
288. while ( strpos( $response, $tag ) === false )
289. {
290. if ( strpos( $response, ' BAD ' ) !== false ||
291. strpos( $response, ' NO ' ) !== false )
292. {
293. break;
294. }
295. $response = $this->connection->getLine();
296. }
297. return $response;
298. }
299.
300. /**
301. * Generates the next IMAP tag to prepend to client commands.
302. *
303. * The structure of the IMAP tag is Axxxx, where:
304. * - A is a letter (uppercase for conformity)
305. * - x is a digit from 0 to 9
306. *
307. * example of generated tag: T5439
308. *
309. * It uses the class variable $this->currentTag.
310. *
311. * Everytime it is called, the tag increases by 1.
312. *
313. * If it reaches the last tag, it wraps around to the first tag.
314. *
315. * By default, the first generated tag is A0001.
316. *
317. * @return string
318. */
319. private function getNextTag()
320. {
321. $tagLetter = substr( $this->currentTag, 0, 1 );
322. $tagNumber = intval( substr( $this->currentTag, 1 ) );
323. $tagNumber++;
324. if ( $tagLetter == 'Z' && $tagNumber == 10000 )
325. {
326. $tagLetter = 'A';
327. $tagNumber = 1;
328. }
329. if ( $tagNumber == 10000 )
330. {
331. $tagLetter++;
332. $tagNumber = 0;
333. }
334. $this->currentTag = $tagLetter . sprintf( "%04s", $tagNumber );
335. return $this->currentTag;
336. }
337.
338. /**
339. * Returns whether the set has mails.
340. *
341. * @return bool
342. */
343. public function hasData()
344. {
345. return count( $this->messages );
346. }
347.
348. /**
349. * Returns message numbers from the current set.
350. *
351. * @return array(int)
352. */
353. public function getMessageNumbers()
354. {
355. return $this->messages;
356. }
357. }
Last updated: Mon, 17 Dec 2007