xref: /PHP-8.3/ext/imap/php_imap.c (revision 6c25257d)
1 /*
2    +----------------------------------------------------------------------+
3    | Copyright (c) The PHP Group                                          |
4    +----------------------------------------------------------------------+
5    | This source file is subject to version 3.01 of the PHP license,      |
6    | that is bundled with this package in the file LICENSE, and is        |
7    | available through the world-wide-web at the following url:           |
8    | https://www.php.net/license/3_01.txt                                 |
9    | If you did not receive a copy of the PHP license and are unable to   |
10    | obtain it through the world-wide-web, please send a note to          |
11    | license@php.net so we can mail you a copy immediately.               |
12    +----------------------------------------------------------------------+
13    | Authors: Rex Logan           <veebert@dimensional.com>               |
14    |          Mark Musone         <musone@afterfive.com>                  |
15    |          Brian Wang          <brian@vividnet.com>                    |
16    |          Kaj-Michael Lang    <milang@tal.org>                        |
17    |          Antoni Pamies Olive <toni@readysoft.net>                    |
18    |          Rasmus Lerdorf      <rasmus@php.net>                        |
19    |          Chuck Hagenbuch     <chuck@horde.org>                       |
20    |          Andrew Skalski      <askalski@chekinc.com>                  |
21    |          Hartmut Holzgraefe  <hholzgra@php.net>                      |
22    |          Jani Taskinen       <jani.taskinen@iki.fi>                  |
23    |          Daniel R. Kalowsky  <kalowsky@php.net>                      |
24    | PHP 4.0 updates:  Zeev Suraski <zeev@php.net>                        |
25    +----------------------------------------------------------------------+
26  */
27 
28 #define IMAP41
29 
30 #ifdef HAVE_CONFIG_H
31 #include "config.h"
32 #endif
33 
34 #include "php.h"
35 #include "php_ini.h"
36 #include "php_streams.h"
37 #include "Zend/zend_attributes.h"
38 #include "Zend/zend_exceptions.h"
39 #include "ext/standard/php_string.h"
40 #include "ext/standard/info.h"
41 #include "ext/standard/file.h"
42 #include "zend_smart_str.h"
43 #include "ext/pcre/php_pcre.h"
44 
45 #ifdef ERROR
46 #undef ERROR
47 #endif
48 #include "php_imap.h"
49 
50 #include <time.h>
51 #include <stdio.h>
52 #include <ctype.h>
53 #include <signal.h>
54 
55 #ifdef PHP_WIN32
56 #include <winsock2.h>
57 #include <stdlib.h>
58 #include "win32/sendmail.h"
59 MAILSTREAM DEFAULTPROTO;
60 #endif
61 
62 #define CRLF	"\015\012"
63 #define CRLF_LEN sizeof("\015\012") - 1
64 #define PHP_EXPUNGE 32768
65 #define PHP_IMAP_ADDRESS_SIZE_BUF 10
66 #ifndef SENDBUFLEN
67 #define SENDBUFLEN 16385
68 #endif
69 
70 #define PHP_IMAP_OPENTIMEOUT 1
71 #define PHP_IMAP_READTIMEOUT 2
72 #define PHP_IMAP_WRITETIMEOUT 3
73 #define PHP_IMAP_CLOSETIMEOUT 4
74 
75 #define SA_ALL SA_MESSAGES | SA_RECENT | SA_UNSEEN | SA_UIDNEXT | SA_UIDVALIDITY
76 
77 #include "php_imap_arginfo.h"
78 
79 #if defined(__GNUC__) && __GNUC__ >= 4
80 # define PHP_IMAP_EXPORT __attribute__ ((visibility("default")))
81 #else
82 # define PHP_IMAP_EXPORT
83 #endif
84 
85 static void _php_make_header_object(zval *myzvalue, ENVELOPE *en);
86 static void _php_imap_add_body(zval *arg, const BODY *body);
87 static zend_string* _php_imap_parse_address(ADDRESS *addresslist, zval *paddress);
88 static zend_string* _php_rfc822_write_address(ADDRESS *addresslist);
89 
90 /* the gets we use */
91 static char *php_mail_gets(readfn_t f, void *stream, unsigned long size, GETS_DATA *md);
92 
93 /* These function declarations are missing from the IMAP header files... */
94 void rfc822_date(char *date);
95 char *cpystr(const char *str);
96 char *cpytxt(SIZEDTEXT *dst, char *text, unsigned long size);
97 #ifndef HAVE_NEW_MIME2TEXT
98 long utf8_mime2text(SIZEDTEXT *src, SIZEDTEXT *dst);
99 #else
100 long utf8_mime2text (SIZEDTEXT *src, SIZEDTEXT *dst, long flags);
101 #endif
102 unsigned long find_rightmost_bit(unsigned long *valptr);
103 void fs_give(void **block);
104 void *fs_get(size_t size);
105 
106 ZEND_DECLARE_MODULE_GLOBALS(imap)
107 static PHP_GINIT_FUNCTION(imap);
108 
109 /* {{{ imap dependencies */
110 static const zend_module_dep imap_deps[] = {
111 	ZEND_MOD_REQUIRED("standard")
112 	ZEND_MOD_END
113 };
114 /* }}} */
115 
116 
117 /* {{{ PHP_INI */
118 PHP_INI_BEGIN()
119 STD_PHP_INI_BOOLEAN("imap.enable_insecure_rsh", "0", PHP_INI_SYSTEM, OnUpdateBool, enable_rsh, zend_imap_globals, imap_globals)
120 PHP_INI_END()
121 /* }}} */
122 
123 
124 /* {{{ imap_module_entry */
125 zend_module_entry imap_module_entry = {
126 	STANDARD_MODULE_HEADER_EX, NULL,
127 	imap_deps,
128 	"imap",
129 	ext_functions,
130 	PHP_MINIT(imap),
131 	NULL,
132 	PHP_RINIT(imap),
133 	PHP_RSHUTDOWN(imap),
134 	PHP_MINFO(imap),
135 	PHP_IMAP_VERSION,
136 	PHP_MODULE_GLOBALS(imap),
137 	PHP_GINIT(imap),
138 	NULL,
139 	NULL,
140 	STANDARD_MODULE_PROPERTIES_EX
141 };
142 /* }}} */
143 
144 #ifdef COMPILE_DL_IMAP
145 #ifdef ZTS
146 ZEND_TSRMLS_CACHE_DEFINE()
147 #endif
148 ZEND_GET_MODULE(imap)
149 #endif
150 
151 /* Imap class entry definition */
152 static zend_class_entry *php_imap_ce = NULL;
153 static zend_object_handlers imap_object_handlers;
154 
155 typedef struct _php_imap_object {
156 	MAILSTREAM *imap_stream;
157 	long flags;
158 	zend_object std;
159 } php_imap_object;
160 
imap_object_to_zend_object(php_imap_object * obj)161 static inline zend_object *imap_object_to_zend_object(php_imap_object *obj) {
162 	return ((zend_object*)(obj + 1)) - 1;
163 }
164 
imap_object_from_zend_object(zend_object * zobj)165 static inline php_imap_object *imap_object_from_zend_object(zend_object *zobj) {
166 	return ((php_imap_object*)(zobj + 1)) - 1;
167 }
168 
imap_object_create(zend_class_entry * ce)169 static zend_object* imap_object_create(zend_class_entry* ce) {
170 	php_imap_object *obj = zend_object_alloc(sizeof(php_imap_object), ce);
171 	zend_object *zobj = imap_object_to_zend_object(obj);
172 	obj->imap_stream = NULL;
173 	obj->flags = 0;
174 	zend_object_std_init(zobj, ce);
175 	object_properties_init(zobj, ce);
176 	zobj->handlers = &imap_object_handlers;
177 
178 	return zobj;
179 }
180 
imap_object_get_constructor(zend_object * zobj)181 static zend_function *imap_object_get_constructor(zend_object *zobj) {
182 	zend_throw_error(NULL, "Cannot directly construct IMAP\\Connection, use imap_open() instead");
183 	return NULL;
184 }
185 
imap_object_destroy(zend_object * zobj)186 static void imap_object_destroy(zend_object *zobj) {
187 	php_imap_object *obj = imap_object_from_zend_object(zobj);
188 
189 	if (obj->imap_stream) {
190 		/* Do not try to close prototype streams */
191 		if (!(obj->flags & OP_PROTOTYPE)) {
192 			mail_close_full(obj->imap_stream, obj->flags);
193 		}
194 	}
195 
196 	if (IMAPG(imap_user)) {
197 		efree(IMAPG(imap_user));
198 		IMAPG(imap_user) = 0;
199 	}
200 	if (IMAPG(imap_password)) {
201 		efree(IMAPG(imap_password));
202 		IMAPG(imap_password) = 0;
203 	}
204 
205 	zend_object_std_dtor(zobj);
206 }
207 
208 #define GET_IMAP_STREAM(imap_conn_struct, zval_imap_obj) \
209 	imap_conn_struct = imap_object_from_zend_object(Z_OBJ_P(zval_imap_obj)); \
210 	if (imap_conn_struct->imap_stream == NULL) { \
211 		zend_throw_exception(zend_ce_value_error, "IMAP\\Connection is already closed", 0); \
212 		RETURN_THROWS(); \
213 	}
214 
215 #define PHP_IMAP_CHECK_MSGNO_MAYBE_UID_PRE_FLAG_CHECKS(msgindex, arg_pos) \
216 	if (msgindex < 1) { \
217 		zend_argument_value_error(arg_pos, "must be greater than 0"); \
218 		RETURN_THROWS(); \
219 	} \
220 
221 #define PHP_IMAP_CHECK_MSGNO_MAYBE_UID_POST_FLAG_CHECKS(msgindex, arg_pos, func_flags, uid_flag) \
222 	if (func_flags & uid_flag) { \
223 		/* This should be cached; if it causes an extra RTT to the  IMAP server, */ \
224 		/* then that's the price we pay for making sure we don't crash. */ \
225 		unsigned int msg_no_from_uid = mail_msgno(imap_conn_struct->imap_stream, msgindex); \
226 		if (msg_no_from_uid == 0) { \
227 			php_error_docref(NULL, E_WARNING, "UID does not exist"); \
228 			RETURN_FALSE; \
229 		} \
230 	} else { \
231 		if (((unsigned) msgindex) > imap_conn_struct->imap_stream->nmsgs) { \
232 			php_error_docref(NULL, E_WARNING, "Bad message number"); \
233 			RETURN_FALSE; \
234 		} \
235 	} \
236 
237 // TODO Promote to ValueError?
238 #define PHP_IMAP_CHECK_MSGNO(msgindex, arg_pos)	\
239 	if (msgindex < 1) { \
240 		zend_argument_value_error(arg_pos, "must be greater than 0"); \
241 		RETURN_THROWS(); \
242 	} \
243 	if (((unsigned) msgindex) > imap_conn_struct->imap_stream->nmsgs) { \
244 		php_error_docref(NULL, E_WARNING, "Bad message number"); \
245 		RETURN_FALSE;	\
246 	}	\
247 
248 /* {{{ mail_newfolderobjectlist
249  *
250  * Mail instantiate FOBJECTLIST
251  * Returns: new FOBJECTLIST list
252  * Author: CJH
253  */
mail_newfolderobjectlist(void)254 FOBJECTLIST *mail_newfolderobjectlist(void)
255 {
256 	return (FOBJECTLIST *) memset(fs_get(sizeof(FOBJECTLIST)), 0, sizeof(FOBJECTLIST));
257 }
258 /* }}} */
259 
260 /* {{{ mail_free_foblist
261  *
262  * Mail garbage collect FOBJECTLIST
263  * Accepts: pointer to FOBJECTLIST pointer
264  * Author: CJH
265  */
mail_free_foblist(FOBJECTLIST ** foblist,FOBJECTLIST ** tail)266 void mail_free_foblist(FOBJECTLIST **foblist, FOBJECTLIST **tail)
267 {
268 	FOBJECTLIST *cur, *next;
269 
270 	for (cur=*foblist, next=cur->next; cur; cur=next) {
271 		next = cur->next;
272 
273 		if(cur->text.data)
274 			fs_give((void **)&(cur->text.data));
275 
276 		fs_give((void **)&cur);
277 	}
278 
279 	*tail = NIL;
280 	*foblist = NIL;
281 }
282 /* }}} */
283 
284 /* {{{ mail_newerrorlist
285  *
286  * Mail instantiate ERRORLIST
287  * Returns: new ERRORLIST list
288  * Author: CJH
289  */
mail_newerrorlist(void)290 ERRORLIST *mail_newerrorlist(void)
291 {
292 	return (ERRORLIST *) memset(fs_get(sizeof(ERRORLIST)), 0, sizeof(ERRORLIST));
293 }
294 /* }}} */
295 
296 /* {{{ mail_free_errorlist
297  *
298  * Mail garbage collect FOBJECTLIST
299  * Accepts: pointer to FOBJECTLIST pointer
300  * Author: CJH
301  */
mail_free_errorlist(ERRORLIST ** errlist)302 void mail_free_errorlist(ERRORLIST **errlist)
303 {
304 	if (*errlist) {		/* only free if exists */
305 		if ((*errlist)->text.data) {
306 			fs_give((void **) &(*errlist)->text.data);
307 		}
308 		mail_free_errorlist (&(*errlist)->next);
309 		fs_give((void **) errlist);	/* return string to free storage */
310 	}
311 }
312 /* }}} */
313 
314 /* {{{ mail_newmessagelist
315  *
316  * Mail instantiate MESSAGELIST
317  * Returns: new MESSAGELIST list
318  * Author: CJH
319  */
mail_newmessagelist(void)320 MESSAGELIST *mail_newmessagelist(void)
321 {
322 	return (MESSAGELIST *) memset(fs_get(sizeof(MESSAGELIST)), 0, sizeof(MESSAGELIST));
323 }
324 /* }}} */
325 
326 /* {{{ mail_free_messagelist
327  *
328  * Mail garbage collect MESSAGELIST
329  * Accepts: pointer to MESSAGELIST pointer
330  * Author: CJH
331  */
mail_free_messagelist(MESSAGELIST ** msglist,MESSAGELIST ** tail)332 void mail_free_messagelist(MESSAGELIST **msglist, MESSAGELIST **tail)
333 {
334 	MESSAGELIST *cur, *next;
335 
336 	for (cur = *msglist, next = cur->next; cur; cur = next) {
337 		next = cur->next;
338 		fs_give((void **)&cur);
339 	}
340 
341 	*tail = NIL;
342 	*msglist = NIL;
343 }
344 /* }}} */
345 
346 #if defined(HAVE_IMAP2000) || defined(HAVE_IMAP2001)
347 /* {{{ mail_getquota
348  *
349  * Mail GET_QUOTA callback
350  * Called via the mail_parameter function in c-client:src/c-client/mail.c
351  * Author DRK
352  */
353 
mail_getquota(MAILSTREAM * stream,char * qroot,QUOTALIST * qlist)354 void mail_getquota(MAILSTREAM *stream, char *qroot, QUOTALIST *qlist)
355 {
356 	zval t_map, *return_value;
357 
358 	return_value = *IMAPG(quota_return);
359 
360 /* put parsing code here */
361 	for(; qlist; qlist = qlist->next) {
362 		array_init(&t_map);
363 		if (strncmp(qlist->name, "STORAGE", 7) == 0)
364 		{
365 			/* this is to add backwards compatibility */
366 			add_assoc_long_ex(return_value, "usage", sizeof("usage") - 1, qlist->usage);
367 			add_assoc_long_ex(return_value, "limit", sizeof("limit") - 1, qlist->limit);
368 		}
369 
370 		add_assoc_long_ex(&t_map, "usage", sizeof("usage") - 1, qlist->usage);
371 		add_assoc_long_ex(&t_map, "limit", sizeof("limit") - 1, qlist->limit);
372 		add_assoc_zval_ex(return_value, qlist->name, strlen(qlist->name), &t_map);
373 	}
374 }
375 /* }}} */
376 
377 /* {{{ mail_getquota
378  *
379  * Mail GET_ACL callback
380  * Called via the mail_parameter function in c-client:src/c-client/mail.c
381  */
mail_getacl(MAILSTREAM * stream,char * mailbox,ACLLIST * alist)382 void mail_getacl(MAILSTREAM *stream, char *mailbox, ACLLIST *alist)
383 {
384 
385 	/* walk through the ACLLIST */
386 	for(; alist; alist = alist->next) {
387 		add_assoc_stringl(IMAPG(imap_acl_list), alist->identifier, alist->rights, strlen(alist->rights));
388 	}
389 }
390 /* }}} */
391 #endif
392 
393 /* {{{ PHP_GINIT_FUNCTION */
PHP_GINIT_FUNCTION(imap)394 static PHP_GINIT_FUNCTION(imap)
395 {
396 #if defined(COMPILE_DL_IMAP) && defined(ZTS)
397 	ZEND_TSRMLS_CACHE_UPDATE();
398 #endif
399 	imap_globals->imap_user = NIL;
400 	imap_globals->imap_password = NIL;
401 
402 	imap_globals->imap_alertstack = NIL;
403 	imap_globals->imap_errorstack = NIL;
404 
405 	imap_globals->imap_folders = NIL;
406 	imap_globals->imap_folders_tail = NIL;
407 	imap_globals->imap_sfolders = NIL;
408 	imap_globals->imap_sfolders_tail = NIL;
409 	imap_globals->imap_messages = NIL;
410 	imap_globals->imap_messages_tail = NIL;
411 	imap_globals->imap_folder_objects = NIL;
412 	imap_globals->imap_folder_objects_tail = NIL;
413 	imap_globals->imap_sfolder_objects = NIL;
414 	imap_globals->imap_sfolder_objects_tail = NIL;
415 
416 	imap_globals->folderlist_style = FLIST_ARRAY;
417 #if defined(HAVE_IMAP2000) || defined(HAVE_IMAP2001)
418 	imap_globals->quota_return = NIL;
419 	imap_globals->imap_acl_list = NIL;
420 #endif
421 	imap_globals->gets_stream = NIL;
422 }
423 /* }}} */
424 
425 /* {{{ PHP_MINIT_FUNCTION */
PHP_MINIT_FUNCTION(imap)426 PHP_MINIT_FUNCTION(imap)
427 {
428 	REGISTER_INI_ENTRIES();
429 
430 #ifndef PHP_WIN32
431 	mail_link(&unixdriver);		/* link in the unix driver */
432 	mail_link(&mhdriver);		/* link in the mh driver */
433 	/* mail_link(&mxdriver); */	/* According to c-client docs (internal.txt) this shouldn't be used. */
434 	mail_link(&mmdfdriver);		/* link in the mmdf driver */
435 	mail_link(&newsdriver);		/* link in the news driver */
436 	mail_link(&philedriver);	/* link in the phile driver */
437 #endif
438 	mail_link(&imapdriver);		/* link in the imap driver */
439 	mail_link(&nntpdriver);		/* link in the nntp driver */
440 	mail_link(&pop3driver);		/* link in the pop3 driver */
441 	mail_link(&mbxdriver);		/* link in the mbx driver */
442 	mail_link(&tenexdriver);	/* link in the tenex driver */
443 	mail_link(&mtxdriver);		/* link in the mtx driver */
444 	mail_link(&dummydriver);	/* link in the dummy driver */
445 
446 #ifndef PHP_WIN32
447 	auth_link(&auth_log);		/* link in the log authenticator */
448 	auth_link(&auth_md5);		/* link in the cram-md5 authenticator */
449 #if defined(HAVE_IMAP_KRB) && defined(HAVE_IMAP_AUTH_GSS)
450 	auth_link(&auth_gss);		/* link in the gss authenticator */
451 #endif
452 	auth_link(&auth_pla);		/* link in the plain authenticator */
453 #endif
454 
455 #ifdef HAVE_IMAP_SSL
456 	ssl_onceonlyinit ();
457 #endif
458 
459 	php_imap_ce = register_class_IMAP_Connection();
460 	php_imap_ce->create_object = imap_object_create;
461 
462 	memcpy(&imap_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
463 	imap_object_handlers.offset = XtOffsetOf(php_imap_object, std);
464 	imap_object_handlers.get_constructor = imap_object_get_constructor;
465 	imap_object_handlers.free_obj = imap_object_destroy;
466 	imap_object_handlers.clone_obj = NULL;
467 
468 	register_php_imap_symbols(module_number);
469 
470 	/* plug in our gets */
471 	mail_parameters(NIL, SET_GETS, (void *) NIL);
472 
473 	/* set default timeout values */
474 	mail_parameters(NIL, SET_OPENTIMEOUT, (void *) FG(default_socket_timeout));
475 	mail_parameters(NIL, SET_READTIMEOUT, (void *) FG(default_socket_timeout));
476 	mail_parameters(NIL, SET_WRITETIMEOUT, (void *) FG(default_socket_timeout));
477 	mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) FG(default_socket_timeout));
478 
479 	if (!IMAPG(enable_rsh)) {
480 		/* disable SSH and RSH, see https://bugs.php.net/bug.php?id=77153 */
481 		mail_parameters (NIL, SET_RSHTIMEOUT, 0);
482 		mail_parameters (NIL, SET_SSHTIMEOUT, 0);
483 	}
484 
485 	return SUCCESS;
486 }
487 /* }}} */
488 
489 /* {{{ PHP_RINIT_FUNCTION */
PHP_RINIT_FUNCTION(imap)490 PHP_RINIT_FUNCTION(imap)
491 {
492 	IMAPG(imap_errorstack) = NIL;
493 	IMAPG(imap_alertstack) = NIL;
494 	IMAPG(gets_stream) = NIL;
495 	return SUCCESS;
496 }
497 /* }}} */
498 
free_errorlist(void)499 static void free_errorlist(void)
500 {
501 	ERRORLIST *ecur = NIL;
502 
503 	if (IMAPG(imap_errorstack) != NIL) {
504 		/* output any remaining errors at their original error level */
505 		if (EG(error_reporting) & E_NOTICE) {
506 			zend_try {
507 				ecur = IMAPG(imap_errorstack);
508 				while (ecur != NIL) {
509 					php_error_docref(NULL, E_NOTICE, "%s (errflg=%ld)", ecur->LTEXT, ecur->errflg);
510 					ecur = ecur->next;
511 				}
512 			} zend_end_try();
513 		}
514 		mail_free_errorlist(&IMAPG(imap_errorstack));
515 		IMAPG(imap_errorstack) = NIL;
516 	}
517 }
518 
free_stringlist(void)519 static void free_stringlist(void)
520 {
521 	STRINGLIST *acur = NIL;
522 
523 	if (IMAPG(imap_alertstack) != NIL) {
524 		/* output any remaining alerts at E_NOTICE level */
525 		if (EG(error_reporting) & E_NOTICE) {
526 			zend_try {
527 				acur = IMAPG(imap_alertstack);
528 				while (acur != NIL) {
529 					php_error_docref(NULL, E_NOTICE, "%s", acur->LTEXT);
530 					acur = acur->next;
531 				}
532 			} zend_end_try();
533 		}
534 		mail_free_stringlist(&IMAPG(imap_alertstack));
535 		IMAPG(imap_alertstack) = NIL;
536 	}
537 }
538 
539 /* {{{ PHP_RSHUTDOWN_FUNCTION */
PHP_RSHUTDOWN_FUNCTION(imap)540 PHP_RSHUTDOWN_FUNCTION(imap)
541 {
542 	free_errorlist();
543 	free_stringlist();
544 	return SUCCESS;
545 }
546 /* }}} */
547 
548 #ifndef CCLIENTVERSION
549 # if defined(HAVE_IMAP2007e)
550 #  define CCLIENTVERSION "2007e"
551 # elif defined(HAVE_IMAP2007d)
552 #  define CCLIENTVERSION "2007d"
553 # elif defined(HAVE_IMAP2007b)
554 #  define CCLIENTVERSION "2007b"
555 # elif defined(HAVE_IMAP2007a)
556 #  define CCLIENTVERSION "2007a"
557 # elif defined(HAVE_IMAP2004)
558 #  define CCLIENTVERSION "2004"
559 # elif defined(HAVE_IMAP2001)
560 #  define CCLIENTVERSION "2001"
561 # elif defined(HAVE_IMAP2000)
562 #  define CCLIENTVERSION "2000"
563 # elif defined(IMAP41)
564 #  define CCLIENTVERSION "4.1"
565 # else
566 #  define CCLIENTVERSION "4.0"
567 # endif
568 #endif
569 
570 /* {{{ PHP_MINFO_FUNCTION */
PHP_MINFO_FUNCTION(imap)571 PHP_MINFO_FUNCTION(imap)
572 {
573 	php_info_print_table_start();
574 	php_info_print_table_row(2, "IMAP c-Client Version", CCLIENTVERSION);
575 #ifdef HAVE_IMAP_SSL
576 	php_info_print_table_row(2, "SSL Support", "enabled");
577 #endif
578 #if defined(HAVE_IMAP_KRB) && defined(HAVE_IMAP_AUTH_GSS)
579 	php_info_print_table_row(2, "Kerberos Support", "enabled");
580 #endif
581 	php_info_print_table_end();
582 
583 	DISPLAY_INI_ENTRIES();
584 }
585 /* }}} */
586 
587 /* {{{ Open an IMAP stream to a mailbox */
PHP_FUNCTION(imap_open)588 PHP_FUNCTION(imap_open)
589 {
590 	zend_string *mailbox, *user, *passwd;
591 	zend_long retries = 0, flags = 0, cl_flags = 0;
592 	MAILSTREAM *imap_stream;
593 	HashTable *params = NULL;
594 
595 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "PSS|llh", &mailbox, &user, &passwd, &flags, &retries, &params) == FAILURE) {
596 		RETURN_THROWS();
597 	}
598 
599 	/* Check for PHP_EXPUNGE and not CL_EXPUNGE as the user land facing CL_EXPUNGE constant is defined
600 	 * to something different to prevent clashes between CL_EXPUNGE and an OP_* constant allowing setting
601 	 * the CL_EXPUNGE flag which will expunge when the mailbox is closed (be that manually, or via the
602 	 * IMAP\Connection object being destroyed naturally at the end of the PHP script */
603 	if (flags && ((flags & ~(OP_READONLY | OP_ANONYMOUS | OP_HALFOPEN | PHP_EXPUNGE | OP_DEBUG | OP_SHORTCACHE
604 	 		| OP_SILENT | OP_PROTOTYPE | OP_SECURE)) != 0)) {
605 		zend_argument_value_error(4, "must be a bitmask of the OP_* constants, and CL_EXPUNGE");
606 		RETURN_THROWS();
607 	}
608 
609 	if (retries < 0) {
610 		zend_argument_value_error(5, "must be greater than or equal to 0");
611 		RETURN_THROWS();
612 	}
613 
614 	if (flags) {
615 		if (flags & PHP_EXPUNGE) {
616 			cl_flags = CL_EXPUNGE;
617 			flags ^= PHP_EXPUNGE;
618 		}
619 		if (flags & OP_PROTOTYPE) {
620 			cl_flags |= OP_PROTOTYPE;
621 		}
622 	}
623 
624 	if (params) {
625 		zval *disabled_auth_method;
626 
627 		if ((disabled_auth_method = zend_hash_str_find(params, "DISABLE_AUTHENTICATOR", sizeof("DISABLE_AUTHENTICATOR") - 1)) != NULL) {
628 			switch (Z_TYPE_P(disabled_auth_method)) {
629 				case IS_STRING:
630 					if (Z_STRLEN_P(disabled_auth_method) > 1) {
631 						mail_parameters(NIL, DISABLE_AUTHENTICATOR, (void *)Z_STRVAL_P(disabled_auth_method));
632 					}
633 					break;
634 				case IS_ARRAY:
635 					{
636 						zval *z_auth_method;
637 						int i;
638 						int nelems = zend_hash_num_elements(Z_ARRVAL_P(disabled_auth_method));
639 
640 						if (nelems == 0 ) {
641 							break;
642 						}
643 						for (i = 0; i < nelems; i++) {
644 							if ((z_auth_method = zend_hash_index_find(Z_ARRVAL_P(disabled_auth_method), i)) != NULL) {
645 								if (Z_TYPE_P(z_auth_method) == IS_STRING) {
646 									if (Z_STRLEN_P(z_auth_method) > 1) {
647 										mail_parameters(NIL, DISABLE_AUTHENTICATOR, (void *)Z_STRVAL_P(z_auth_method));
648 									}
649 								} else {
650 									zend_argument_type_error(6, "option \"DISABLE_AUTHENTICATOR\" must be a string or an array of strings");
651 									RETURN_THROWS();
652 								}
653 							}
654 						}
655 					}
656 					break;
657 				default:
658 					zend_argument_type_error(6, "option \"DISABLE_AUTHENTICATOR\" must be a string or an array of strings");
659 					RETURN_THROWS();
660 			}
661 		}
662 	}
663 
664 	if (IMAPG(imap_user)) {
665 		efree(IMAPG(imap_user));
666 		IMAPG(imap_user) = 0;
667 	}
668 
669 	if (IMAPG(imap_password)) {
670 		efree(IMAPG(imap_password));
671 		IMAPG(imap_password) = 0;
672 	}
673 
674 	/* local filename, need to perform open_basedir check */
675 	if (ZSTR_VAL(mailbox)[0] != '{' && php_check_open_basedir(ZSTR_VAL(mailbox))) {
676 		RETURN_FALSE;
677 	}
678 
679 	IMAPG(imap_user)     = estrndup(ZSTR_VAL(user), ZSTR_LEN(user));
680 	IMAPG(imap_password) = estrndup(ZSTR_VAL(passwd), ZSTR_LEN(passwd));
681 
682 #ifdef SET_MAXLOGINTRIALS
683 	if (retries) {
684 		mail_parameters(NIL, SET_MAXLOGINTRIALS, (void *) retries);
685 	}
686 #endif
687 
688 	ZEND_IGNORE_LEAKS_BEGIN();
689 	imap_stream = mail_open(NIL, ZSTR_VAL(mailbox), flags);
690 	ZEND_IGNORE_LEAKS_END();
691 
692 	if (imap_stream == NIL) {
693 		php_error_docref(NULL, E_WARNING, "Couldn't open stream %s", ZSTR_VAL(mailbox));
694 		efree(IMAPG(imap_user)); IMAPG(imap_user) = 0;
695 		efree(IMAPG(imap_password)); IMAPG(imap_password) = 0;
696 		RETURN_FALSE;
697 	}
698 
699 	object_init_ex(return_value, php_imap_ce);
700 	imap_object_from_zend_object(Z_OBJ_P(return_value))->imap_stream = imap_stream;
701 	imap_object_from_zend_object(Z_OBJ_P(return_value))->flags = cl_flags;
702 }
703 /* }}} */
704 
705 /* {{{ Reopen an IMAP stream to a new mailbox */
PHP_FUNCTION(imap_reopen)706 PHP_FUNCTION(imap_reopen)
707 {
708 	zval *imap_conn_obj;
709 	zend_string *mailbox;
710 	zend_long options = 0, retries = 0;
711 	php_imap_object *imap_conn_struct;
712 	long flags = 0;
713 	long cl_flags = 0;
714 
715 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "OS|ll", &imap_conn_obj, php_imap_ce, &mailbox, &options, &retries) == FAILURE) {
716 		RETURN_THROWS();
717 	}
718 
719 	GET_IMAP_STREAM(imap_conn_struct, imap_conn_obj);
720 
721 	/* TODO Verify these are the only options available as they are pulled from the php.net documentation */
722 	/* Check for PHP_EXPUNGE and not CL_EXPUNGE as the user land facing CL_EXPUNGE constant is defined
723      * to something different to prevent clashes between CL_EXPUNGE and an OP_* constant allowing setting
724      * the CL_EXPUNGE flag which will expunge when the mailbox is closed (be that manually, or via the
725      * IMAP\Connection object being destroyed naturally at the end of the PHP script */
726 	if (options && ((options & ~(OP_READONLY | OP_ANONYMOUS | OP_HALFOPEN | OP_EXPUNGE | PHP_EXPUNGE)) != 0)) {
727 		zend_argument_value_error(3, "must be a bitmask of OP_READONLY, OP_ANONYMOUS, OP_HALFOPEN, "
728 			"OP_EXPUNGE, and CL_EXPUNGE");
729 		RETURN_THROWS();
730 	}
731 
732 	if (retries < 0) {
733 		zend_argument_value_error(4, "must be greater than or equal to 0");
734 		RETURN_THROWS();
735 	}
736 
737 	if (options) {
738 		flags = options;
739 		if (flags & PHP_EXPUNGE) {
740 			cl_flags = CL_EXPUNGE;
741 			flags ^= PHP_EXPUNGE;
742 		}
743 		imap_conn_struct->flags = cl_flags;
744 	}
745 #ifdef SET_MAXLOGINTRIALS
746 	if (retries) {
747 		mail_parameters(NIL, SET_MAXLOGINTRIALS, (void *) retries);
748 	}
749 #endif
750 	/* local filename, need to perform open_basedir check */
751 	if (ZSTR_VAL(mailbox)[0] != '{' && php_check_open_basedir(ZSTR_VAL(mailbox))) {
752 		RETURN_FALSE;
753 	}
754 
755 	imap_conn_struct->imap_stream = mail_open(imap_conn_struct->imap_stream, ZSTR_VAL(mailbox), flags);
756 	if (imap_conn_struct->imap_stream == NIL) {
757 		/* IMAP\Connection object will release it self. */
758 		php_error_docref(NULL, E_WARNING, "Couldn't re-open stream");
759 		RETURN_FALSE;
760 	}
761 	RETURN_TRUE;
762 }
763 /* }}} */
764 
PHP_FUNCTION(imap_is_open)765 PHP_FUNCTION(imap_is_open)
766 {
767 	zval *imap_conn_obj;
768 	php_imap_object *imap_conn_struct;
769 
770 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &imap_conn_obj, php_imap_ce) == FAILURE) {
771 		RETURN_THROWS();
772 	}
773 
774 	/* Manual reimplementation of the GET_IMAP_STREAM() macro that doesn't throw */
775 	imap_conn_struct = imap_object_from_zend_object(Z_OBJ_P(imap_conn_obj));
776 	/* Stream was closed */
777 	if (imap_conn_struct->imap_stream == NULL) {
778 		RETURN_FALSE;
779 	}
780 	RETURN_TRUE;
781 }
782 
783 /* {{{ Append a new message to a specified mailbox */
PHP_FUNCTION(imap_append)784 PHP_FUNCTION(imap_append)
785 {
786 	zval *imap_conn_obj;
787 	zend_string *folder, *message, *internal_date = NULL, *flags = NULL;
788 	php_imap_object *imap_conn_struct;
789 	STRING st;
790 
791 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "OSS|S!S!", &imap_conn_obj, php_imap_ce, &folder, &message, &flags, &internal_date) == FAILURE) {
792 		RETURN_THROWS();
793 	}
794 
795 	if (internal_date) {
796 		zend_string *regex  = ZSTR_INIT_LITERAL("/[0-3][0-9]-((Jan)|(Feb)|(Mar)|(Apr)|(May)|(Jun)|(Jul)|(Aug)|(Sep)|(Oct)|(Nov)|(Dec))-[0-9]{4} [0-2][0-9]:[0-5][0-9]:[0-5][0-9] [+-][0-9]{4}/", 0);
797 		pcre_cache_entry *pce;				/* Compiled regex */
798 		zval *subpats = NULL;				/* Parts (not used) */
799 		int global = 0;
800 
801 		/* Make sure the given internal_date string matches the RFC specifiedformat */
802 		if ((pce = pcre_get_compiled_regex_cache(regex))== NULL) {
803 			zend_string_release(regex);
804 			RETURN_FALSE;
805 		}
806 
807 		zend_string_release(regex);
808 		php_pcre_match_impl(pce, internal_date, return_value, subpats, global,
809 			0, Z_L(0), Z_L(0));
810 
811 		if (!Z_LVAL_P(return_value)) {
812 			// TODO Promoto to error?
813 			php_error_docref(NULL, E_WARNING, "Internal date not correctly formatted");
814 			internal_date = NULL;
815 		}
816 	}
817 
818 	/* TODO Check if flags are valid (documentation is not present on php.net so need to check this first) */
819 
820 	GET_IMAP_STREAM(imap_conn_struct, imap_conn_obj);
821 
822 	INIT (&st, mail_string, (void *) ZSTR_VAL(message), ZSTR_LEN(message));
823 
824 	if (mail_append_full(imap_conn_struct->imap_stream, ZSTR_VAL(folder), (flags ? ZSTR_VAL(flags) : NIL), (internal_date ? ZSTR_VAL(internal_date) : NIL), &st)) {
825 		RETURN_TRUE;
826 	} else {
827 		RETURN_FALSE;
828 	}
829 }
830 /* }}} */
831 
832 /* {{{ Gives the number of messages in the current mailbox */
PHP_FUNCTION(imap_num_msg)833 PHP_FUNCTION(imap_num_msg)
834 {
835 	zval *imap_conn_obj;
836 	php_imap_object *imap_conn_struct;
837 
838 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &imap_conn_obj, php_imap_ce) == FAILURE) {
839 		RETURN_THROWS();
840 	}
841 
842 	GET_IMAP_STREAM(imap_conn_struct, imap_conn_obj);
843 
844 	RETURN_LONG(imap_conn_struct->imap_stream->nmsgs);
845 }
846 /* }}} */
847 
848 /* {{{ Check if the IMAP stream is still active */
PHP_FUNCTION(imap_ping)849 PHP_FUNCTION(imap_ping)
850 {
851 	zval *imap_conn_obj;
852 	php_imap_object *imap_conn_struct;
853 
854 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &imap_conn_obj, php_imap_ce) == FAILURE) {
855 		RETURN_THROWS();
856 	}
857 
858 	GET_IMAP_STREAM(imap_conn_struct, imap_conn_obj);
859 
860 	RETURN_BOOL(mail_ping(imap_conn_struct->imap_stream));
861 }
862 /* }}} */
863 
864 /* {{{ Gives the number of recent messages in current mailbox */
PHP_FUNCTION(imap_num_recent)865 PHP_FUNCTION(imap_num_recent)
866 {
867 	zval *imap_conn_obj;
868 	php_imap_object *imap_conn_struct;
869 
870 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &imap_conn_obj, php_imap_ce) == FAILURE) {
871 		RETURN_THROWS();
872 	}
873 
874 	GET_IMAP_STREAM(imap_conn_struct, imap_conn_obj);
875 
876 	RETURN_LONG(imap_conn_struct->imap_stream->recent);
877 }
878 /* }}} */
879 
880 #if defined(HAVE_IMAP2000) || defined(HAVE_IMAP2001)
881 /* {{{ Returns the quota set to the mailbox account qroot */
PHP_FUNCTION(imap_get_quota)882 PHP_FUNCTION(imap_get_quota)
883 {
884 	zval *imap_conn_obj;
885 	zend_string *qroot;
886 	php_imap_object *imap_conn_struct;
887 
888 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "OS", &imap_conn_obj, php_imap_ce, &qroot) == FAILURE) {
889 		RETURN_THROWS();
890 	}
891 
892 	GET_IMAP_STREAM(imap_conn_struct, imap_conn_obj);
893 
894 	array_init(return_value);
895 	IMAPG(quota_return) = &return_value;
896 
897 	/* set the callback for the GET_QUOTA function */
898 	mail_parameters(NIL, SET_QUOTA, (void *) mail_getquota);
899 	if (!imap_getquota(imap_conn_struct->imap_stream, ZSTR_VAL(qroot))) {
900 		php_error_docref(NULL, E_WARNING, "C-client imap_getquota failed");
901 		zend_array_destroy(Z_ARR_P(return_value));
902 		RETURN_FALSE;
903 	}
904 }
905 /* }}} */
906 
907 /* {{{ Returns the quota set to the mailbox account mbox */
PHP_FUNCTION(imap_get_quotaroot)908 PHP_FUNCTION(imap_get_quotaroot)
909 {
910 	zval *imap_conn_obj;
911 	zend_string *mbox;
912 	php_imap_object *imap_conn_struct;
913 
914 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "OS", &imap_conn_obj, php_imap_ce, &mbox) == FAILURE) {
915 		RETURN_THROWS();
916 	}
917 
918 	GET_IMAP_STREAM(imap_conn_struct, imap_conn_obj);
919 
920 	array_init(return_value);
921 	IMAPG(quota_return) = &return_value;
922 
923 	/* set the callback for the GET_QUOTAROOT function */
924 	mail_parameters(NIL, SET_QUOTA, (void *) mail_getquota);
925 	if (!imap_getquotaroot(imap_conn_struct->imap_stream, ZSTR_VAL(mbox))) {
926 		php_error_docref(NULL, E_WARNING, "C-client imap_getquotaroot failed");
927 		zend_array_destroy(Z_ARR_P(return_value));
928 		RETURN_FALSE;
929 	}
930 }
931 /* }}} */
932 
933 /* {{{ Will set the quota for qroot mailbox */
PHP_FUNCTION(imap_set_quota)934 PHP_FUNCTION(imap_set_quota)
935 {
936 	zval *imap_conn_obj;
937 	zend_string *qroot;
938 	zend_long mailbox_size;
939 	php_imap_object *imap_conn_struct;
940 	STRINGLIST	limits;
941 
942 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "OSl", &imap_conn_obj, php_imap_ce, &qroot, &mailbox_size) == FAILURE) {
943 		RETURN_THROWS();
944 	}
945 
946 	GET_IMAP_STREAM(imap_conn_struct, imap_conn_obj);
947 
948 	limits.text.data = (unsigned char*)"STORAGE";
949 	limits.text.size = mailbox_size;
950 	limits.next = NIL;
951 
952 	RETURN_BOOL(imap_setquota(imap_conn_struct->imap_stream, ZSTR_VAL(qroot), &limits));
953 }
954 /* }}} */
955 
956 /* {{{ Sets the ACL for a given mailbox */
PHP_FUNCTION(imap_setacl)957 PHP_FUNCTION(imap_setacl)
958 {
959 	zval *imap_conn_obj;
960 	zend_string *mailbox, *id, *rights;
961 	php_imap_object *imap_conn_struct;
962 
963 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "OSSS", &imap_conn_obj, php_imap_ce, &mailbox, &id, &rights) == FAILURE) {
964 		RETURN_THROWS();
965 	}
966 
967 	GET_IMAP_STREAM(imap_conn_struct, imap_conn_obj);
968 
969 	RETURN_BOOL(imap_setacl(imap_conn_struct->imap_stream, ZSTR_VAL(mailbox), ZSTR_VAL(id), ZSTR_VAL(rights)));
970 }
971 /* }}} */
972 
973 /* {{{ Gets the ACL for a given mailbox */
PHP_FUNCTION(imap_getacl)974 PHP_FUNCTION(imap_getacl)
975 {
976 	zval *imap_conn_obj;
977 	zend_string *mailbox;
978 	php_imap_object *imap_conn_struct;
979 
980 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "OS", &imap_conn_obj, php_imap_ce, &mailbox) == FAILURE) {
981 		RETURN_THROWS();
982 	}
983 
984 	GET_IMAP_STREAM(imap_conn_struct, imap_conn_obj);
985 
986 	/* initializing the special array for the return values */
987 	array_init(return_value);
988 
989 	IMAPG(imap_acl_list) = return_value;
990 
991 	/* set the callback for the GET_ACL function */
992 	mail_parameters(NIL, SET_ACL, (void *) mail_getacl);
993 	if (!imap_getacl(imap_conn_struct->imap_stream, ZSTR_VAL(mailbox))) {
994 		php_error(E_WARNING, "c-client imap_getacl failed");
995 		zend_array_destroy(Z_ARR_P(return_value));
996 		RETURN_FALSE;
997 	}
998 
999 	IMAPG(imap_acl_list) = NIL;
1000 }
1001 /* }}} */
1002 #endif /* HAVE_IMAP2000 || HAVE_IMAP2001 */
1003 
1004 /* {{{ Permanently delete all messages marked for deletion */
PHP_FUNCTION(imap_expunge)1005 PHP_FUNCTION(imap_expunge)
1006 {
1007 	zval *imap_conn_obj;
1008 	php_imap_object *imap_conn_struct;
1009 
1010 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &imap_conn_obj, php_imap_ce) == FAILURE) {
1011 		RETURN_THROWS();
1012 	}
1013 
1014 	GET_IMAP_STREAM(imap_conn_struct, imap_conn_obj);
1015 
1016 	mail_expunge (imap_conn_struct->imap_stream);
1017 
1018 	RETURN_TRUE;
1019 }
1020 /* }}} */
1021 
1022 /* {{{ This function garbage collects (purges) the cache of entries of a specific type. */
PHP_FUNCTION(imap_gc)1023 PHP_FUNCTION(imap_gc)
1024 {
1025 	zval *imap_conn_obj;
1026 	php_imap_object *imap_conn_struct;
1027 	zend_long flags;
1028 
1029 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ol", &imap_conn_obj, php_imap_ce, &flags) == FAILURE) {
1030 		RETURN_THROWS();
1031 	}
1032 
1033 	GET_IMAP_STREAM(imap_conn_struct, imap_conn_obj);
1034 
1035 	if (flags && ((flags & ~(GC_TEXTS | GC_ELT | GC_ENV)) != 0)) {
1036 		zend_argument_value_error(2, "must be a bitmask of IMAP_GC_TEXTS, IMAP_GC_ELT, and IMAP_GC_ENV");
1037 		RETURN_THROWS();
1038 	}
1039 
1040 	mail_gc(imap_conn_struct->imap_stream, flags);
1041 
1042 	// TODO Return void?
1043 	RETURN_TRUE;
1044 }
1045 /* }}} */
1046 
1047 /* {{{ Close an IMAP stream */
PHP_FUNCTION(imap_close)1048 PHP_FUNCTION(imap_close)
1049 {
1050 	zval *imap_conn_obj;
1051 	php_imap_object *imap_conn_struct = NULL;
1052 	zend_long options = 0;
1053 
1054 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|l", &imap_conn_obj, php_imap_ce, &options) == FAILURE) {
1055 		RETURN_THROWS();
1056 	}
1057 
1058 	GET_IMAP_STREAM(imap_conn_struct, imap_conn_obj);
1059 
1060 	if (options) {
1061 		/* Check that flags is exactly equal to PHP_EXPUNGE or zero */
1062 		if (options && ((options & ~PHP_EXPUNGE) != 0)) {
1063 			zend_argument_value_error(2, "must be CL_EXPUNGE or 0");
1064 			RETURN_THROWS();
1065 		}
1066 
1067 		/* Do the translation from PHP's internal PHP_EXPUNGE define to c-client's CL_EXPUNGE */
1068 		if (options & PHP_EXPUNGE) {
1069 			options ^= PHP_EXPUNGE;
1070 			options |= CL_EXPUNGE;
1071 		}
1072 		imap_conn_struct->flags = options;
1073 	}
1074 
1075 	/* Do not try to close prototype streams */
1076 	if (!(imap_conn_struct->flags & OP_PROTOTYPE)) {
1077 		mail_close_full(imap_conn_struct->imap_stream, imap_conn_struct->flags);
1078 		imap_conn_struct->imap_stream = NULL;
1079 	}
1080 
1081 	// TODO Return void?
1082 	RETURN_TRUE;
1083 }
1084 /* }}} */
1085 
1086 /* {{{ Returns headers for all messages in a mailbox */
PHP_FUNCTION(imap_headers)1087 PHP_FUNCTION(imap_headers)
1088 {
1089 	zval *imap_conn_obj;
1090 	php_imap_object *imap_conn_struct;
1091 	unsigned long i;
1092 	char *t;
1093 	unsigned int msgno;
1094 	char tmp[MAILTMPLEN];
1095 
1096 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &imap_conn_obj, php_imap_ce) == FAILURE) {
1097 		RETURN_THROWS();
1098 	}
1099 
1100 	GET_IMAP_STREAM(imap_conn_struct, imap_conn_obj);
1101 
1102 	/* Initialize return array */
1103 	array_init(return_value);
1104 
1105 	for (msgno = 1; msgno <= imap_conn_struct->imap_stream->nmsgs; msgno++) {
1106 		MESSAGECACHE * cache = mail_elt (imap_conn_struct->imap_stream, msgno);
1107 		mail_fetchstructure(imap_conn_struct->imap_stream, msgno, NIL);
1108 		tmp[0] = cache->recent ? (cache->seen ? 'R': 'N') : ' ';
1109 		tmp[1] = (cache->recent | cache->seen) ? ' ' : 'U';
1110 		tmp[2] = cache->flagged ? 'F' : ' ';
1111 		tmp[3] = cache->answered ? 'A' : ' ';
1112 		tmp[4] = cache->deleted ? 'D' : ' ';
1113 		tmp[5] = cache->draft ? 'X' : ' ';
1114 		snprintf(tmp + 6, sizeof(tmp) - 6, "%4ld) ", cache->msgno);
1115 		mail_date(tmp+11, cache);
1116 		tmp[22] = ' ';
1117 		tmp[23] = '\0';
1118 		mail_fetchfrom(tmp+23, imap_conn_struct->imap_stream, msgno, (long)20);
1119 		strcat(tmp, " ");
1120 		if ((i = cache->user_flags)) {
1121 			strcat(tmp, "{");
1122 			while (i) {
1123 				strlcat(tmp, imap_conn_struct->imap_stream->user_flags[find_rightmost_bit (&i)], sizeof(tmp));
1124 				if (i) strlcat(tmp, " ", sizeof(tmp));
1125 			}
1126 			strlcat(tmp, "} ", sizeof(tmp));
1127 		}
1128 		mail_fetchsubject(t = tmp + strlen(tmp), imap_conn_struct->imap_stream, msgno, (long)25);
1129 		snprintf(t += strlen(t), sizeof(tmp) - strlen(tmp), " (%ld chars)", cache->rfc822_size);
1130 		add_next_index_string(return_value, tmp);
1131 	}
1132 }
1133 /* }}} */
1134 
1135 /* {{{ Read the message body */
PHP_FUNCTION(imap_body)1136 PHP_FUNCTION(imap_body)
1137 {
1138 	zval *imap_conn_obj;
1139 	zend_long msgno, flags = 0;
1140 	php_imap_object *imap_conn_struct;
1141 	char *body;
1142 	unsigned long body_len = 0;
1143 
1144 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ol|l", &imap_conn_obj, php_imap_ce, &msgno, &flags) == FAILURE) {
1145 		RETURN_THROWS();
1146 	}
1147 
1148 	GET_IMAP_STREAM(imap_conn_struct, imap_conn_obj);
1149 
1150 	PHP_IMAP_CHECK_MSGNO_MAYBE_UID_PRE_FLAG_CHECKS(msgno, 2);
1151 
1152 	if (flags && ((flags & ~(FT_UID|FT_PEEK|FT_INTERNAL)) != 0)) {
1153 		zend_argument_value_error(3, "must be a bitmask of FT_UID, FT_PEEK, and FT_INTERNAL");
1154 		RETURN_THROWS();
1155 	}
1156 
1157 	PHP_IMAP_CHECK_MSGNO_MAYBE_UID_POST_FLAG_CHECKS(msgno, 2, flags, FT_UID);
1158 
1159 	body = mail_fetchtext_full (imap_conn_struct->imap_stream, msgno, &body_len, flags);
1160 	if (body_len == 0) {
1161 		RETVAL_EMPTY_STRING();
1162 	} else {
1163 		RETVAL_STRINGL(body, body_len);
1164 	}
1165 }
1166 /* }}} */
1167 
1168 /* TODO UID Tests */
1169 /* {{{ Copy specified message to a mailbox */
PHP_FUNCTION(imap_mail_copy)1170 PHP_FUNCTION(imap_mail_copy)
1171 {
1172 	zval *imap_conn_obj;
1173 	zend_long options = 0;
1174 	zend_string *seq, *folder;
1175 	php_imap_object *imap_conn_struct;
1176 
1177 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "OSS|l", &imap_conn_obj, php_imap_ce, &seq, &folder, &options) == FAILURE) {
1178 		RETURN_THROWS();
1179 	}
1180 
1181 	GET_IMAP_STREAM(imap_conn_struct, imap_conn_obj);
1182 
1183 	if (options && ((options & ~(CP_UID | CP_MOVE)) != 0)) {
1184 		zend_argument_value_error(4, "must be a bitmask of CP_UID, and CP_MOVE");
1185 		RETURN_THROWS();
1186 	}
1187 
1188 	if (mail_copy_full(imap_conn_struct->imap_stream, ZSTR_VAL(seq), ZSTR_VAL(folder), options) == T) {
1189 		RETURN_TRUE;
1190 	} else {
1191 		RETURN_FALSE;
1192 	}
1193 }
1194 /* }}} */
1195 
1196 /* TODO UID Tests */
1197 /* {{{ Move specified message to a mailbox */
PHP_FUNCTION(imap_mail_move)1198 PHP_FUNCTION(imap_mail_move)
1199 {
1200 	zval *imap_conn_obj;
1201 	zend_string *seq, *folder;
1202 	zend_long options = 0;
1203 	php_imap_object *imap_conn_struct;
1204 
1205 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "OSS|l", &imap_conn_obj, php_imap_ce, &seq, &folder, &options) == FAILURE) {
1206 		RETURN_THROWS();
1207 	}
1208 
1209 	GET_IMAP_STREAM(imap_conn_struct, imap_conn_obj);
1210 
1211 	if (options && ((options & ~CP_UID) != 0)) {
1212 		zend_argument_value_error(4, "must be CP_UID or 0");
1213 		RETURN_THROWS();
1214 	}
1215 
1216 	/* Add CP_MOVE flag */
1217 	options = (options | CP_MOVE);
1218 
1219 	if (mail_copy_full(imap_conn_struct->imap_stream, ZSTR_VAL(seq), ZSTR_VAL(folder), options) == T) {
1220 		RETURN_TRUE;
1221 	} else {
1222 		RETURN_FALSE;
1223 	}
1224 }
1225 /* }}} */
1226 
1227 /* {{{ Create a new mailbox */
PHP_FUNCTION(imap_createmailbox)1228 PHP_FUNCTION(imap_createmailbox)
1229 {
1230 	zval *imap_conn_obj;
1231 	zend_string *folder;
1232 	php_imap_object *imap_conn_struct;
1233 
1234 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "OS", &imap_conn_obj, php_imap_ce, &folder) == FAILURE) {
1235 		RETURN_THROWS();
1236 	}
1237 
1238 	GET_IMAP_STREAM(imap_conn_struct, imap_conn_obj);
1239 
1240 	if (mail_create(imap_conn_struct->imap_stream, ZSTR_VAL(folder)) == T) {
1241 		RETURN_TRUE;
1242 	} else {
1243 		RETURN_FALSE;
1244 	}
1245 }
1246 /* }}} */
1247 
1248 /* {{{ Rename a mailbox */
PHP_FUNCTION(imap_renamemailbox)1249 PHP_FUNCTION(imap_renamemailbox)
1250 {
1251 	zval *imap_conn_obj;
1252 	zend_string *old_mailbox, *new_mailbox;
1253 	php_imap_object *imap_conn_struct;
1254 
1255 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "OSS", &imap_conn_obj, php_imap_ce, &old_mailbox, &new_mailbox) == FAILURE) {
1256 		RETURN_THROWS();
1257 	}
1258 
1259 	GET_IMAP_STREAM(imap_conn_struct, imap_conn_obj);
1260 
1261 	if (mail_rename(imap_conn_struct->imap_stream, ZSTR_VAL(old_mailbox), ZSTR_VAL(new_mailbox)) == T) {
1262 		RETURN_TRUE;
1263 	} else {
1264 		RETURN_FALSE;
1265 	}
1266 }
1267 /* }}} */
1268 
1269 /* {{{ Delete a mailbox */
PHP_FUNCTION(imap_deletemailbox)1270 PHP_FUNCTION(imap_deletemailbox)
1271 {
1272 	zval *imap_conn_obj;
1273 	zend_string *folder;
1274 	php_imap_object *imap_conn_struct;
1275 
1276 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "OS", &imap_conn_obj, php_imap_ce, &folder) == FAILURE) {
1277 		RETURN_THROWS();
1278 	}
1279 
1280 	GET_IMAP_STREAM(imap_conn_struct, imap_conn_obj);
1281 
1282 	if (mail_delete(imap_conn_struct->imap_stream, ZSTR_VAL(folder)) == T) {
1283 		RETURN_TRUE;
1284 	} else {
1285 		RETURN_FALSE;
1286 	}
1287 }
1288 /* }}} */
1289 
1290 /* {{{ Read the list of mailboxes */
PHP_FUNCTION(imap_list)1291 PHP_FUNCTION(imap_list)
1292 {
1293 	zval *imap_conn_obj;
1294 	zend_string *ref, *pat;
1295 	php_imap_object *imap_conn_struct;
1296 	STRINGLIST *cur=NIL;
1297 
1298 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "OSS", &imap_conn_obj, php_imap_ce, &ref, &pat) == FAILURE) {
1299 		RETURN_THROWS();
1300 	}
1301 
1302 	GET_IMAP_STREAM(imap_conn_struct, imap_conn_obj);
1303 
1304 	/* set flag for normal, old mailbox list */
1305 	IMAPG(folderlist_style) = FLIST_ARRAY;
1306 
1307 	IMAPG(imap_folders) = IMAPG(imap_folders_tail) = NIL;
1308 	mail_list(imap_conn_struct->imap_stream, ZSTR_VAL(ref), ZSTR_VAL(pat));
1309 	if (IMAPG(imap_folders) == NIL) {
1310 		RETURN_FALSE;
1311 	}
1312 
1313 	array_init(return_value);
1314 	cur=IMAPG(imap_folders);
1315 	while (cur != NIL) {
1316 		add_next_index_string(return_value, (char*)cur->LTEXT);
1317 		cur=cur->next;
1318 	}
1319 	mail_free_stringlist (&IMAPG(imap_folders));
1320 	IMAPG(imap_folders) = IMAPG(imap_folders_tail) = NIL;
1321 }
1322 
1323 /* }}} */
1324 
php_imap_populate_mailbox_object(zval * z_object,const FOBJECTLIST * mailbox)1325 static void php_imap_populate_mailbox_object(zval *z_object, const FOBJECTLIST *mailbox)
1326 {
1327 	zend_update_property_string(
1328 		Z_OBJCE_P(z_object), Z_OBJ_P(z_object),
1329 		"name", strlen("name"),
1330 		(char*)mailbox->LTEXT
1331 	);
1332 	zend_update_property_long(
1333 		Z_OBJCE_P(z_object), Z_OBJ_P(z_object),
1334 		"attributes", strlen("attributes"),
1335 		mailbox->attributes
1336 	);
1337 #ifdef IMAP41
1338 	zend_update_property_str(
1339 		Z_OBJCE_P(z_object), Z_OBJ_P(z_object),
1340 		"delimiter", strlen("delimiter"),
1341 		ZSTR_CHAR((unsigned char)mailbox->delimiter)
1342 	);
1343 #else
1344 	zend_update_property_string(
1345 		Z_OBJCE_P(z_object), Z_OBJ_P(z_object),
1346 		"delimiter", strlen("delimiter"),
1347 		mailbox->delimiter
1348 	);
1349 #endif
1350 }
1351 
1352 /* {{{ Reads the list of mailboxes and returns a full array of objects containing name, attributes, and delimiter */
1353 /* Author: CJH */
PHP_FUNCTION(imap_getmailboxes)1354 PHP_FUNCTION(imap_getmailboxes)
1355 {
1356 	zval *imap_conn_obj;
1357 	zend_string *ref, *pat;
1358 	php_imap_object *imap_conn_struct;
1359 	FOBJECTLIST *cur=NIL;
1360 
1361 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "OSS", &imap_conn_obj, php_imap_ce, &ref, &pat) == FAILURE) {
1362 		RETURN_THROWS();
1363 	}
1364 
1365 	GET_IMAP_STREAM(imap_conn_struct, imap_conn_obj);
1366 
1367 	/* set flag for new, improved array of objects mailbox list */
1368 	IMAPG(folderlist_style) = FLIST_OBJECT;
1369 
1370 	IMAPG(imap_folder_objects) = IMAPG(imap_folder_objects_tail) = NIL;
1371 	mail_list(imap_conn_struct->imap_stream, ZSTR_VAL(ref), ZSTR_VAL(pat));
1372 	if (IMAPG(imap_folder_objects) == NIL) {
1373 		RETURN_FALSE;
1374 	}
1375 
1376 	array_init(return_value);
1377 	cur=IMAPG(imap_folder_objects);
1378 	while (cur != NIL) {
1379 		zval mboxob;
1380 		object_init(&mboxob);
1381 		php_imap_populate_mailbox_object(&mboxob, cur);
1382 		zend_hash_next_index_insert_new(Z_ARR_P(return_value), &mboxob);
1383 		cur=cur->next;
1384 	}
1385 	mail_free_foblist(&IMAPG(imap_folder_objects), &IMAPG(imap_folder_objects_tail));
1386 	IMAPG(folderlist_style) = FLIST_ARRAY;		/* reset to default */
1387 }
1388 /* }}} */
1389 
1390 /* {{{ Read list of mailboxes containing a certain string */
PHP_FUNCTION(imap_listscan)1391 PHP_FUNCTION(imap_listscan)
1392 {
1393 	zval *imap_conn_obj;
1394 	zend_string *ref, *pat, *content;
1395 	php_imap_object *imap_conn_struct;
1396 	STRINGLIST *cur=NIL;
1397 
1398 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "OSSS", &imap_conn_obj, php_imap_ce, &ref, &pat, &content) == FAILURE) {
1399 		RETURN_THROWS();
1400 	}
1401 
1402 	GET_IMAP_STREAM(imap_conn_struct, imap_conn_obj);
1403 
1404 	IMAPG(imap_folders) = NIL;
1405 	mail_scan(imap_conn_struct->imap_stream, ZSTR_VAL(ref), ZSTR_VAL(pat), ZSTR_VAL(content));
1406 	if (IMAPG(imap_folders) == NIL) {
1407 		RETURN_FALSE;
1408 	}
1409 
1410 	array_init(return_value);
1411 	cur=IMAPG(imap_folders);
1412 	while (cur != NIL) {
1413 		add_next_index_string(return_value, (char*)cur->LTEXT);
1414 		cur=cur->next;
1415 	}
1416 	mail_free_stringlist (&IMAPG(imap_folders));
1417 	IMAPG(imap_folders) = IMAPG(imap_folders_tail) = NIL;
1418 }
1419 
1420 /* }}} */
1421 
php_imap_populate_mailbox_properties_object(zval * z_object,const MAILSTREAM * imap_stream)1422 static void php_imap_populate_mailbox_properties_object(zval *z_object, const MAILSTREAM *imap_stream)
1423 {
1424 	char date[100];
1425 	rfc822_date(date);
1426 	zend_update_property_string(
1427 		Z_OBJCE_P(z_object), Z_OBJ_P(z_object),
1428 		"Date", strlen("Date"),
1429 		date
1430 	);
1431 	zend_update_property_string(
1432 		Z_OBJCE_P(z_object), Z_OBJ_P(z_object),
1433 		"Driver", strlen("Driver"),
1434 		imap_stream->dtb->name
1435 	);
1436 	zend_update_property_string(
1437 		Z_OBJCE_P(z_object), Z_OBJ_P(z_object),
1438 		"Mailbox", strlen("Mailbox"),
1439 		imap_stream->mailbox
1440 	);
1441 	zend_update_property_long(
1442 		Z_OBJCE_P(z_object), Z_OBJ_P(z_object),
1443 		"Nmsgs", strlen("Nmsgs"),
1444 		imap_stream->nmsgs
1445 	);
1446 	zend_update_property_long(
1447 		Z_OBJCE_P(z_object), Z_OBJ_P(z_object),
1448 		"Recent", strlen("Recent"),
1449 		imap_stream->recent
1450 	);
1451 }
1452 
1453 /* {{{ Get mailbox properties */
PHP_FUNCTION(imap_check)1454 PHP_FUNCTION(imap_check)
1455 {
1456 	zval *imap_conn_obj;
1457 	php_imap_object *imap_conn_struct;
1458 
1459 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &imap_conn_obj, php_imap_ce) == FAILURE) {
1460 		RETURN_THROWS();
1461 	}
1462 
1463 	GET_IMAP_STREAM(imap_conn_struct, imap_conn_obj);
1464 
1465 	if (mail_ping (imap_conn_struct->imap_stream) == NIL) {
1466 		RETURN_FALSE;
1467 	}
1468 
1469 	if (imap_conn_struct->imap_stream->mailbox) {
1470 		object_init(return_value);
1471 		php_imap_populate_mailbox_properties_object(return_value, imap_conn_struct->imap_stream);
1472 	} else {
1473 		RETURN_FALSE;
1474 	}
1475 }
1476 /* }}} */
1477 
1478 /* {{{ Mark a message for deletion */
PHP_FUNCTION(imap_delete)1479 PHP_FUNCTION(imap_delete)
1480 {
1481 	zval *imap_conn_obj;
1482 	php_imap_object *imap_conn_struct;
1483 	zend_string *sequence;
1484 	zend_long flags = 0;
1485 
1486 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "OS|l", &imap_conn_obj, php_imap_ce, &sequence, &flags) == FAILURE) {
1487 		RETURN_THROWS();
1488 	}
1489 
1490 	GET_IMAP_STREAM(imap_conn_struct, imap_conn_obj);
1491 
1492 	// TODO Check sequence validity?
1493 
1494 	if (flags && ((flags & ~FT_UID) != 0)) {
1495 		zend_argument_value_error(3, "must be FT_UID or 0");
1496 		RETURN_THROWS();
1497 	}
1498 
1499 	mail_setflag_full(imap_conn_struct->imap_stream, ZSTR_VAL(sequence), "\\DELETED", flags);
1500 
1501 	// TODO Return void?
1502 	RETURN_TRUE;
1503 }
1504 /* }}} */
1505 
1506 /* {{{ Remove the delete flag from a message */
PHP_FUNCTION(imap_undelete)1507 PHP_FUNCTION(imap_undelete)
1508 {
1509 	zval *imap_conn_obj;
1510 	zend_string *sequence;
1511 	zend_long flags = 0;
1512 	php_imap_object *imap_conn_struct;
1513 
1514 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "OS|l", &imap_conn_obj, php_imap_ce, &sequence, &flags) == FAILURE) {
1515 		RETURN_THROWS();
1516 	}
1517 
1518 	GET_IMAP_STREAM(imap_conn_struct, imap_conn_obj);
1519 
1520 	/* TODO Check if flags are valid (documentation is not present on php.net so need to check this first) */
1521 
1522 	mail_clearflag_full(imap_conn_struct->imap_stream, ZSTR_VAL(sequence), "\\DELETED", flags);
1523 
1524 	// TODO Return void?
1525 	RETURN_TRUE;
1526 }
1527 /* }}} */
1528 
1529 /* {{{ Read the headers of the message */
PHP_FUNCTION(imap_headerinfo)1530 PHP_FUNCTION(imap_headerinfo)
1531 {
1532 	zval *imap_conn_obj;
1533 	zend_long msgno, fromlength = 0, subjectlength = 0;
1534 	php_imap_object *imap_conn_struct;
1535 	MESSAGECACHE *cache;
1536 	ENVELOPE *en;
1537 	char dummy[2000], fulladdress[MAILTMPLEN + 1];
1538 
1539 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ol|ll", &imap_conn_obj, php_imap_ce, &msgno, &fromlength, &subjectlength) == FAILURE) {
1540 		RETURN_THROWS();
1541 	}
1542 
1543 	GET_IMAP_STREAM(imap_conn_struct, imap_conn_obj);
1544 
1545 	PHP_IMAP_CHECK_MSGNO(msgno, 2);
1546 
1547 	if (fromlength < 0 || fromlength > MAILTMPLEN) {
1548 		zend_argument_value_error(3, "must be between 0 and %d", MAILTMPLEN);
1549 		RETURN_THROWS();
1550 	}
1551 
1552 	if (subjectlength < 0 || subjectlength > MAILTMPLEN) {
1553 		zend_argument_value_error(4, "must be between 0 and %d", MAILTMPLEN);
1554 		RETURN_THROWS();
1555 	}
1556 
1557 	if (mail_fetchstructure(imap_conn_struct->imap_stream, msgno, NIL)) {
1558 		cache = mail_elt(imap_conn_struct->imap_stream, msgno);
1559 	} else {
1560 		RETURN_FALSE;
1561 	}
1562 
1563 	en = mail_fetchenvelope(imap_conn_struct->imap_stream, msgno);
1564 
1565 	/* call a function to parse all the text, so that we can use the
1566 	   same function to parse text from other sources */
1567 	_php_make_header_object(return_value, en);
1568 
1569 	/* now run through properties that are only going to be returned
1570 	   from a server, not text headers */
1571 	zend_update_property_str(
1572 		Z_OBJCE_P(return_value), Z_OBJ_P(return_value),
1573 		"Recent", strlen("Recent"),
1574 		cache->recent ? (
1575 			cache->seen ? ZSTR_CHAR('R') : ZSTR_CHAR('N')
1576 		) : ZSTR_CHAR(' ')
1577 	);
1578 	zend_update_property_str(
1579 		Z_OBJCE_P(return_value), Z_OBJ_P(return_value),
1580 		"Unseen", strlen("Unseen"),
1581 		(cache->recent | cache->seen) ? ZSTR_CHAR(' ') : ZSTR_CHAR('U')
1582 	);
1583 	zend_update_property_str(
1584 		Z_OBJCE_P(return_value), Z_OBJ_P(return_value),
1585 		"Flagged", strlen("Flagged"),
1586 		cache->flagged ? ZSTR_CHAR('F') : ZSTR_CHAR(' ')
1587 	);
1588 	zend_update_property_str(
1589 		Z_OBJCE_P(return_value), Z_OBJ_P(return_value),
1590 		"Answered", strlen("Answered"),
1591 		cache->answered ? ZSTR_CHAR('A') : ZSTR_CHAR(' ')
1592 	);
1593 	zend_update_property_str(
1594 		Z_OBJCE_P(return_value), Z_OBJ_P(return_value),
1595 		"Deleted", strlen("Deleted"),
1596 		cache->deleted ? ZSTR_CHAR('D') : ZSTR_CHAR(' ')
1597 	);
1598 	zend_update_property_str(
1599 		Z_OBJCE_P(return_value), Z_OBJ_P(return_value),
1600 		"Draft", strlen("Draft"),
1601 		cache->draft ? ZSTR_CHAR('X') : ZSTR_CHAR(' ')
1602 	);
1603 
1604 	snprintf(dummy, sizeof(dummy), "%4ld", cache->msgno);
1605 	zend_update_property_string(
1606 		Z_OBJCE_P(return_value), Z_OBJ_P(return_value),
1607 		"Msgno", strlen("Msgno"),
1608 		dummy
1609 	);
1610 
1611 	mail_date(dummy, cache);
1612 	zend_update_property_string(
1613 		Z_OBJCE_P(return_value), Z_OBJ_P(return_value),
1614 		"MailDate", strlen("MailDate"),
1615 		dummy
1616 	);
1617 
1618 	snprintf(dummy, sizeof(dummy), "%ld", cache->rfc822_size);
1619 	zend_update_property_string(
1620 		Z_OBJCE_P(return_value), Z_OBJ_P(return_value),
1621 		"Size", strlen("Size"),
1622 		dummy
1623 	);
1624 
1625 	zend_update_property_long(
1626 		Z_OBJCE_P(return_value), Z_OBJ_P(return_value),
1627 		"udate", strlen("udate"),
1628 		mail_longdate(cache)
1629 	);
1630 
1631 	if (en->from && fromlength) {
1632 		fulladdress[0] = 0x00;
1633 		mail_fetchfrom(fulladdress, imap_conn_struct->imap_stream, msgno, fromlength);
1634 		zend_update_property_string(
1635 			Z_OBJCE_P(return_value), Z_OBJ_P(return_value),
1636 			"fetchfrom", strlen("fetchfrom"),
1637 			fulladdress
1638 		);
1639 	}
1640 	if (en->subject && subjectlength) {
1641 		fulladdress[0] = 0x00;
1642 		mail_fetchsubject(fulladdress, imap_conn_struct->imap_stream, msgno, subjectlength);
1643 		zend_update_property_string(
1644 			Z_OBJCE_P(return_value), Z_OBJ_P(return_value),
1645 			"fetchsubject", strlen("fetchsubject"),
1646 			fulladdress
1647 		);
1648 	}
1649 }
1650 /* }}} */
1651 
1652 /* {{{ Parse a set of mail headers contained in a string, and return an object similar to imap_headerinfo() */
PHP_FUNCTION(imap_rfc822_parse_headers)1653 PHP_FUNCTION(imap_rfc822_parse_headers)
1654 {
1655 	zend_string *headers, *defaulthost = NULL;
1656 	ENVELOPE *en;
1657 
1658 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "S|S", &headers, &defaulthost) == FAILURE) {
1659 		RETURN_THROWS();
1660 	}
1661 
1662 	if (defaulthost) {
1663 		rfc822_parse_msg(&en, NULL, ZSTR_VAL(headers), ZSTR_LEN(headers), NULL, ZSTR_VAL(defaulthost), NIL);
1664 	} else {
1665 		rfc822_parse_msg(&en, NULL, ZSTR_VAL(headers), ZSTR_LEN(headers), NULL, "UNKNOWN", NIL);
1666 	}
1667 
1668 	/* call a function to parse all the text, so that we can use the
1669 	   same function no matter where the headers are from */
1670 	_php_make_header_object(return_value, en);
1671 	mail_free_envelope(&en);
1672 }
1673 /* }}} */
1674 
1675 /* KMLANG */
1676 /* {{{ Return a list of subscribed mailboxes */
PHP_FUNCTION(imap_lsub)1677 PHP_FUNCTION(imap_lsub)
1678 {
1679 	zval *imap_conn_obj;
1680 	zend_string *ref, *pat;
1681 	php_imap_object *imap_conn_struct;
1682 	STRINGLIST *cur=NIL;
1683 
1684 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "OSS", &imap_conn_obj, php_imap_ce, &ref, &pat) == FAILURE) {
1685 		RETURN_THROWS();
1686 	}
1687 
1688 	GET_IMAP_STREAM(imap_conn_struct, imap_conn_obj);
1689 
1690 	/* set flag for normal, old mailbox list */
1691 	IMAPG(folderlist_style) = FLIST_ARRAY;
1692 
1693 	IMAPG(imap_sfolders) = NIL;
1694 	ZEND_IGNORE_LEAKS_BEGIN();
1695 	mail_lsub(imap_conn_struct->imap_stream, ZSTR_VAL(ref), ZSTR_VAL(pat));
1696 	ZEND_IGNORE_LEAKS_END();
1697 	if (IMAPG(imap_sfolders) == NIL) {
1698 		RETURN_FALSE;
1699 	}
1700 
1701 	array_init(return_value);
1702 	cur=IMAPG(imap_sfolders);
1703 	while (cur != NIL) {
1704 		add_next_index_string(return_value, (char*)cur->LTEXT);
1705 		cur=cur->next;
1706 	}
1707 	mail_free_stringlist (&IMAPG(imap_sfolders));
1708 	IMAPG(imap_sfolders) = IMAPG(imap_sfolders_tail) = NIL;
1709 }
1710 /* }}} */
1711 
1712 /* {{{ Return a list of subscribed mailboxes, in the same format as imap_getmailboxes() */
1713 /* Author: CJH */
PHP_FUNCTION(imap_getsubscribed)1714 PHP_FUNCTION(imap_getsubscribed)
1715 {
1716 	zval *imap_conn_obj;
1717 	zend_string *ref, *pat;
1718 	php_imap_object *imap_conn_struct;
1719 	FOBJECTLIST *cur=NIL;
1720 
1721 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "OSS", &imap_conn_obj, php_imap_ce, &ref, &pat) == FAILURE) {
1722 		RETURN_THROWS();
1723 	}
1724 
1725 	GET_IMAP_STREAM(imap_conn_struct, imap_conn_obj);
1726 
1727 	/* set flag for new, improved array of objects list */
1728 	IMAPG(folderlist_style) = FLIST_OBJECT;
1729 
1730 	IMAPG(imap_sfolder_objects) = IMAPG(imap_sfolder_objects_tail) = NIL;
1731 	ZEND_IGNORE_LEAKS_BEGIN();
1732 	mail_lsub(imap_conn_struct->imap_stream, ZSTR_VAL(ref), ZSTR_VAL(pat));
1733 	ZEND_IGNORE_LEAKS_END();
1734 	if (IMAPG(imap_sfolder_objects) == NIL) {
1735 		RETURN_FALSE;
1736 	}
1737 
1738 	array_init(return_value);
1739 	cur = IMAPG(imap_sfolder_objects);
1740 	while (cur != NIL) {
1741 		zval mboxob;
1742 		object_init(&mboxob);
1743 		php_imap_populate_mailbox_object(&mboxob, cur);
1744 		zend_hash_next_index_insert_new(Z_ARR_P(return_value), &mboxob);
1745 
1746 		cur = cur->next;
1747 	}
1748 	mail_free_foblist (&IMAPG(imap_sfolder_objects), &IMAPG(imap_sfolder_objects_tail));
1749 	IMAPG(folderlist_style) = FLIST_ARRAY; /* reset to default */
1750 }
1751 /* }}} */
1752 
1753 /* {{{ Subscribe to a mailbox */
PHP_FUNCTION(imap_subscribe)1754 PHP_FUNCTION(imap_subscribe)
1755 {
1756 	zval *imap_conn_obj;
1757 	zend_string *folder;
1758 	php_imap_object *imap_conn_struct;
1759 
1760 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "OS", &imap_conn_obj, php_imap_ce, &folder) == FAILURE) {
1761 		RETURN_THROWS();
1762 	}
1763 
1764 	GET_IMAP_STREAM(imap_conn_struct, imap_conn_obj);
1765 
1766 	if (mail_subscribe(imap_conn_struct->imap_stream, ZSTR_VAL(folder)) == T) {
1767 		RETURN_TRUE;
1768 	} else {
1769 		RETURN_FALSE;
1770 	}
1771 }
1772 /* }}} */
1773 
1774 /* {{{ Unsubscribe from a mailbox */
PHP_FUNCTION(imap_unsubscribe)1775 PHP_FUNCTION(imap_unsubscribe)
1776 {
1777 	zval *imap_conn_obj;
1778 	zend_string *folder;
1779 	php_imap_object *imap_conn_struct;
1780 
1781 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "OS", &imap_conn_obj, php_imap_ce, &folder) == FAILURE) {
1782 		RETURN_THROWS();
1783 	}
1784 
1785 	GET_IMAP_STREAM(imap_conn_struct, imap_conn_obj);
1786 
1787 	if (mail_unsubscribe(imap_conn_struct->imap_stream, ZSTR_VAL(folder)) == T) {
1788 		RETURN_TRUE;
1789 	} else {
1790 		RETURN_FALSE;
1791 	}
1792 }
1793 /* }}} */
1794 
1795 /* {{{ Read the full structure of a message */
PHP_FUNCTION(imap_fetchstructure)1796 PHP_FUNCTION(imap_fetchstructure)
1797 {
1798 	zval *imap_conn_obj;
1799 	zend_long msgno, flags = 0;
1800 	php_imap_object *imap_conn_struct;
1801 	BODY *body;
1802 
1803 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ol|l", &imap_conn_obj, php_imap_ce, &msgno, &flags) == FAILURE) {
1804 		RETURN_THROWS();
1805 	}
1806 
1807 	GET_IMAP_STREAM(imap_conn_struct, imap_conn_obj);
1808 
1809 	PHP_IMAP_CHECK_MSGNO_MAYBE_UID_PRE_FLAG_CHECKS(msgno, 2);
1810 
1811 	if (flags && ((flags & ~FT_UID) != 0)) {
1812 		zend_argument_value_error(3, "must be FT_UID or 0");
1813 		RETURN_THROWS();
1814 	}
1815 
1816 	PHP_IMAP_CHECK_MSGNO_MAYBE_UID_POST_FLAG_CHECKS(msgno, 2, flags, FT_UID);
1817 
1818 	object_init(return_value);
1819 
1820 	mail_fetchstructure_full(imap_conn_struct->imap_stream, msgno, &body , flags);
1821 
1822 	if (!body) {
1823 		php_error_docref(NULL, E_WARNING, "No body information available");
1824 		RETURN_FALSE;
1825 	}
1826 
1827 	_php_imap_add_body(return_value, body);
1828 }
1829 /* }}} */
1830 
1831 /* {{{ Get a specific body section */
PHP_FUNCTION(imap_fetchbody)1832 PHP_FUNCTION(imap_fetchbody)
1833 {
1834 	zval *imap_conn_obj;
1835 	zend_long msgno, flags = 0;
1836 	php_imap_object *imap_conn_struct;
1837 	char *body;
1838 	zend_string *sec;
1839 	unsigned long len;
1840 
1841 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "OlS|l", &imap_conn_obj, php_imap_ce, &msgno, &sec, &flags) == FAILURE) {
1842 		RETURN_THROWS();
1843 	}
1844 
1845 	GET_IMAP_STREAM(imap_conn_struct, imap_conn_obj);
1846 
1847 	PHP_IMAP_CHECK_MSGNO_MAYBE_UID_PRE_FLAG_CHECKS(msgno, 2);
1848 
1849 	if (flags && ((flags & ~(FT_UID|FT_PEEK|FT_INTERNAL)) != 0)) {
1850 		zend_argument_value_error(4, "must be a bitmask of FT_UID, FT_PEEK, and FT_INTERNAL");
1851 		RETURN_THROWS();
1852 	}
1853 
1854 	PHP_IMAP_CHECK_MSGNO_MAYBE_UID_POST_FLAG_CHECKS(msgno, 2, flags, FT_UID);
1855 
1856 	body = mail_fetchbody_full(imap_conn_struct->imap_stream, msgno, ZSTR_VAL(sec), &len, flags);
1857 
1858 	if (!body) {
1859 		php_error_docref(NULL, E_WARNING, "No body information available");
1860 		RETURN_FALSE;
1861 	}
1862 	RETVAL_STRINGL(body, len);
1863 }
1864 
1865 /* }}} */
1866 
1867 
1868 /* {{{ Get a specific body section's MIME headers */
PHP_FUNCTION(imap_fetchmime)1869 PHP_FUNCTION(imap_fetchmime)
1870 {
1871 	zval *imap_conn_obj;
1872 	zend_long msgno, flags = 0;
1873 	php_imap_object *imap_conn_struct;
1874 	char *body;
1875 	zend_string *sec;
1876 	unsigned long len;
1877 
1878 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "OlS|l", &imap_conn_obj, php_imap_ce, &msgno, &sec, &flags) == FAILURE) {
1879 		RETURN_THROWS();
1880 	}
1881 
1882 	GET_IMAP_STREAM(imap_conn_struct, imap_conn_obj);
1883 
1884 	PHP_IMAP_CHECK_MSGNO_MAYBE_UID_PRE_FLAG_CHECKS(msgno, 2);
1885 
1886 	if (flags && ((flags & ~(FT_UID|FT_PEEK|FT_INTERNAL)) != 0)) {
1887 		zend_argument_value_error(4, "must be a bitmask of FT_UID, FT_PEEK, and FT_INTERNAL");
1888 		RETURN_THROWS();
1889 	}
1890 
1891 	PHP_IMAP_CHECK_MSGNO_MAYBE_UID_POST_FLAG_CHECKS(msgno, 2, flags, FT_UID);
1892 
1893 	body = mail_fetch_mime(imap_conn_struct->imap_stream, msgno, ZSTR_VAL(sec), &len, flags);
1894 
1895 	if (!body) {
1896 		php_error_docref(NULL, E_WARNING, "No body MIME information available");
1897 		RETURN_FALSE;
1898 	}
1899 	RETVAL_STRINGL(body, len);
1900 }
1901 
1902 /* }}} */
1903 
1904 /* {{{ Save a specific body section to a file */
PHP_FUNCTION(imap_savebody)1905 PHP_FUNCTION(imap_savebody)
1906 {
1907 	zval *imap_conn_obj;
1908 	zval *out;
1909 	php_imap_object *imap_conn_struct = NULL;
1910 	php_stream *writer = NULL;
1911 	zend_string *section = NULL;
1912 	int close_stream = 1;
1913 	zend_long msgno, flags = 0;
1914 
1915 	if (SUCCESS != zend_parse_parameters(ZEND_NUM_ARGS(), "Ozl|Sl", &imap_conn_obj, php_imap_ce, &out, &msgno, &section, &flags)) {
1916 		RETURN_THROWS();
1917 	}
1918 
1919 	GET_IMAP_STREAM(imap_conn_struct, imap_conn_obj);
1920 
1921 	PHP_IMAP_CHECK_MSGNO_MAYBE_UID_PRE_FLAG_CHECKS(msgno, 3)
1922 
1923 	if (flags && ((flags & ~(FT_UID|FT_PEEK|FT_INTERNAL)) != 0)) {
1924 		zend_argument_value_error(5, "must be a bitmask of FT_UID, FT_PEEK, and FT_INTERNAL");
1925 		RETURN_THROWS();
1926 	}
1927 
1928 	PHP_IMAP_CHECK_MSGNO_MAYBE_UID_POST_FLAG_CHECKS(msgno, 3, flags, FT_UID);
1929 
1930 	switch (Z_TYPE_P(out))
1931 	{
1932 		case IS_LONG:
1933 		case IS_RESOURCE:
1934 			close_stream = 0;
1935 			php_stream_from_zval(writer, out);
1936 		break;
1937 
1938 		default:
1939 			if (!try_convert_to_string(out)) {
1940 				RETURN_THROWS();
1941 			}
1942 			// TODO Need to check for null bytes?
1943 			writer = php_stream_open_wrapper(Z_STRVAL_P(out), "wb", REPORT_ERRORS, NULL);
1944 		break;
1945 	}
1946 
1947 	if (!writer) {
1948 		RETURN_FALSE;
1949 	}
1950 
1951 	IMAPG(gets_stream) = writer;
1952 	mail_parameters(NIL, SET_GETS, (void *) php_mail_gets);
1953 	mail_fetchbody_full(imap_conn_struct->imap_stream, msgno, section?ZSTR_VAL(section):"", NULL, flags);
1954 	mail_parameters(NIL, SET_GETS, (void *) NULL);
1955 	IMAPG(gets_stream) = NULL;
1956 
1957 	if (close_stream) {
1958 		php_stream_close(writer);
1959 	}
1960 
1961 	RETURN_TRUE;
1962 }
1963 /* }}} */
1964 
1965 /* {{{ Decode BASE64 encoded text */
PHP_FUNCTION(imap_base64)1966 PHP_FUNCTION(imap_base64)
1967 {
1968 	zend_string *text;
1969 	char *decode;
1970 	unsigned long newlength;
1971 
1972 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &text) == FAILURE) {
1973 		RETURN_THROWS();
1974 	}
1975 
1976 	decode = (char *) rfc822_base64((unsigned char *) ZSTR_VAL(text), ZSTR_LEN(text), &newlength);
1977 
1978 	if (decode == NULL) {
1979 		RETURN_FALSE;
1980 	}
1981 
1982 	RETVAL_STRINGL(decode, newlength);
1983 	fs_give((void**) &decode);
1984 }
1985 /* }}} */
1986 
1987 /* {{{ Convert a quoted-printable string to an 8-bit string */
PHP_FUNCTION(imap_qprint)1988 PHP_FUNCTION(imap_qprint)
1989 {
1990 	zend_string *text;
1991 	char *decode;
1992 	unsigned long newlength;
1993 
1994 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &text) == FAILURE) {
1995 		RETURN_THROWS();
1996 	}
1997 
1998 	decode = (char *) rfc822_qprint((unsigned char *) ZSTR_VAL(text), ZSTR_LEN(text), &newlength);
1999 
2000 	if (decode == NULL) {
2001 		RETURN_FALSE;
2002 	}
2003 
2004 	RETVAL_STRINGL(decode, newlength);
2005 	fs_give((void**) &decode);
2006 }
2007 /* }}} */
2008 
2009 /* {{{ Convert an 8-bit string to a quoted-printable string */
PHP_FUNCTION(imap_8bit)2010 PHP_FUNCTION(imap_8bit)
2011 {
2012 	zend_string *text;
2013 	char *decode;
2014 	unsigned long newlength;
2015 
2016 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &text) == FAILURE) {
2017 		RETURN_THROWS();
2018 	}
2019 
2020 	decode = (char *) rfc822_8bit((unsigned char *) ZSTR_VAL(text), ZSTR_LEN(text), &newlength);
2021 
2022 	if (decode == NULL) {
2023 		RETURN_FALSE;
2024 	}
2025 
2026 	RETVAL_STRINGL(decode, newlength);
2027 	fs_give((void**) &decode);
2028 }
2029 /* }}} */
2030 
2031 /* {{{ Convert an 8bit string to a base64 string */
PHP_FUNCTION(imap_binary)2032 PHP_FUNCTION(imap_binary)
2033 {
2034 	zend_string *text;
2035 	char *decode;
2036 	unsigned long newlength;
2037 
2038 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &text) == FAILURE) {
2039 		RETURN_THROWS();
2040 	}
2041 
2042 	decode = (char*)rfc822_binary(ZSTR_VAL(text), ZSTR_LEN(text), &newlength);
2043 
2044 	if (decode == NULL) {
2045 		RETURN_FALSE;
2046 	}
2047 
2048 	RETVAL_STRINGL(decode, newlength);
2049 	fs_give((void**) &decode);
2050 }
2051 /* }}} */
2052 
2053 /* {{{ Returns info about the current mailbox */
PHP_FUNCTION(imap_mailboxmsginfo)2054 PHP_FUNCTION(imap_mailboxmsginfo)
2055 {
2056 	zval *imap_conn_obj;
2057 	php_imap_object *imap_conn_struct;
2058 	unsigned long msgno;
2059 	zend_ulong unreadmsg = 0, deletedmsg = 0, msize = 0;
2060 
2061 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &imap_conn_obj, php_imap_ce) == FAILURE) {
2062 		RETURN_THROWS();
2063 	}
2064 
2065 	GET_IMAP_STREAM(imap_conn_struct, imap_conn_obj);
2066 
2067 	/* Initialize return object */
2068 	object_init(return_value);
2069 
2070 	for (msgno = 1; msgno <= imap_conn_struct->imap_stream->nmsgs; msgno++) {
2071 		MESSAGECACHE * cache = mail_elt (imap_conn_struct->imap_stream, msgno);
2072 		mail_fetchstructure (imap_conn_struct->imap_stream, msgno, NIL);
2073 
2074 		if (!cache->seen || cache->recent) {
2075 			unreadmsg++;
2076 		}
2077 
2078 		if (cache->deleted) {
2079 			deletedmsg++;
2080 		}
2081 		msize = msize + cache->rfc822_size;
2082 	}
2083 	zend_update_property_long(
2084 		Z_OBJCE_P(return_value), Z_OBJ_P(return_value),
2085 		"Unread", strlen("Unread"),
2086 		unreadmsg
2087 	);
2088 	zend_update_property_long(
2089 		Z_OBJCE_P(return_value), Z_OBJ_P(return_value),
2090 		"Deleted", strlen("Deleted"),
2091 		deletedmsg
2092 	);
2093 	zend_update_property_long(
2094 		Z_OBJCE_P(return_value), Z_OBJ_P(return_value),
2095 		"Size", strlen("Size"),
2096 		msize
2097 	);
2098 
2099 	php_imap_populate_mailbox_properties_object(return_value, imap_conn_struct->imap_stream);
2100 }
2101 /* }}} */
2102 
2103 /* {{{ Returns a properly formatted email address given the mailbox, host, and personal info */
PHP_FUNCTION(imap_rfc822_write_address)2104 PHP_FUNCTION(imap_rfc822_write_address)
2105 {
2106 	zend_string *mailbox, *host, *personal;
2107 	ADDRESS *addr;
2108 	zend_string *string;
2109 
2110 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "SSS", &mailbox, &host, &personal) == FAILURE) {
2111 		RETURN_THROWS();
2112 	}
2113 
2114 	addr=mail_newaddr();
2115 
2116 	if (mailbox) {
2117 		addr->mailbox = cpystr(ZSTR_VAL(mailbox));
2118 	}
2119 
2120 	if (host) {
2121 		addr->host = cpystr(ZSTR_VAL(host));
2122 	}
2123 
2124 	if (personal) {
2125 		addr->personal = cpystr(ZSTR_VAL(personal));
2126 	}
2127 
2128 	addr->next=NIL;
2129 	addr->error=NIL;
2130 	addr->adl=NIL;
2131 
2132 	string = _php_rfc822_write_address(addr);
2133 	if (string) {
2134 		RETVAL_STR(string);
2135 	} else {
2136 		RETVAL_FALSE;
2137 	}
2138 	mail_free_address(&addr);
2139 }
2140 /* }}} */
2141 
php_imap_construct_address_object(zval * z_object,const ADDRESS * address)2142 static void php_imap_construct_address_object(zval *z_object, const ADDRESS *address)
2143 {
2144 	if (address->mailbox) {
2145 		zend_update_property_string(
2146 			Z_OBJCE_P(z_object), Z_OBJ_P(z_object),
2147 			"mailbox", strlen("mailbox"),
2148 			address->mailbox
2149 		);
2150 	}
2151 	if (address->host) {
2152 		zend_update_property_string(
2153 			Z_OBJCE_P(z_object), Z_OBJ_P(z_object),
2154 			"host", strlen("host"),
2155 			address->host
2156 		);
2157 	}
2158 	if (address->personal) {
2159 		zend_update_property_string(
2160 			Z_OBJCE_P(z_object), Z_OBJ_P(z_object),
2161 			"personal", strlen("personal"),
2162 			address->personal
2163 		);
2164 	}
2165 	if (address->adl) {
2166 		zend_update_property_string(
2167 			Z_OBJCE_P(z_object), Z_OBJ_P(z_object),
2168 			"adl", strlen("adl"),
2169 			address->adl
2170 		);
2171 	}
2172 }
2173 
php_imap_construct_list_of_addresses(HashTable * list,const ADDRESS * const address_list)2174 static void php_imap_construct_list_of_addresses(HashTable *list, const ADDRESS *const address_list)
2175 {
2176 	const ADDRESS *current_address = address_list;
2177 	do {
2178 		zval tmp_object;
2179 		object_init(&tmp_object);
2180 		php_imap_construct_address_object(&tmp_object, current_address);
2181 		zend_hash_next_index_insert_new(list, &tmp_object);
2182 	} while ((current_address = current_address->next));
2183 }
2184 
2185 /* {{{ Parses an address string */
PHP_FUNCTION(imap_rfc822_parse_adrlist)2186 PHP_FUNCTION(imap_rfc822_parse_adrlist)
2187 {
2188 	zend_string *str, *defaulthost;
2189 	char *str_copy;
2190 	ADDRESS *address_list;
2191 	ENVELOPE *env;
2192 
2193 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "SS", &str, &defaulthost) == FAILURE) {
2194 		RETURN_THROWS();
2195 	}
2196 
2197 	env = mail_newenvelope();
2198 
2199 	/* rfc822_parse_adrlist() modifies passed string. Copy it. */
2200 	str_copy = estrndup(ZSTR_VAL(str), ZSTR_LEN(str));
2201 	rfc822_parse_adrlist(&env->to, str_copy, ZSTR_VAL(defaulthost));
2202 	efree(str_copy);
2203 
2204 	array_init(return_value);
2205 
2206 	address_list = env->to;
2207 
2208 	if (address_list) {
2209 		php_imap_construct_list_of_addresses(Z_ARR_P(return_value), address_list);
2210 	}
2211 
2212 	mail_free_envelope(&env);
2213 }
2214 
2215 /* {{{ Convert a mime-encoded text to UTF-8 */
PHP_FUNCTION(imap_utf8)2216 PHP_FUNCTION(imap_utf8)
2217 {
2218 	zend_string *str;
2219 	SIZEDTEXT src, dest;
2220 
2221 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &str) == FAILURE) {
2222 		RETURN_THROWS();
2223 	}
2224 
2225 	src.data  = NULL;
2226 	src.size  = 0;
2227 	dest.data = NULL;
2228 	dest.size = 0;
2229 
2230 	cpytxt(&src, ZSTR_VAL(str), ZSTR_LEN(str));
2231 
2232 #ifndef HAVE_NEW_MIME2TEXT
2233 	utf8_mime2text(&src, &dest);
2234 #else
2235 	utf8_mime2text(&src, &dest, U8T_DECOMPOSE);
2236 #endif
2237 	RETVAL_STRINGL((char*)dest.data, dest.size);
2238 	if (dest.data) {
2239 		free(dest.data);
2240 	}
2241 	if (src.data && src.data != dest.data) {
2242 		free(src.data);
2243 	}
2244 }
2245 /* }}} */
2246 
2247 /* {{{ macros for the modified utf7 conversion functions
2248  *
2249  * author: Andrew Skalski <askalski@chek.com>
2250  */
2251 
2252 /* tests `c' and returns true if it is a special character */
2253 #define SPECIAL(c) ((c) <= 0x1f || (c) >= 0x7f)
2254 
2255 /* validate a modified-base64 character */
2256 #define B64CHAR(c) (isalnum(c) || (c) == '+' || (c) == ',')
2257 
2258 /* map the low 64 bits of `n' to the modified-base64 characters */
2259 #define B64(n)	("ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
2260 				"abcdefghijklmnopqrstuvwxyz0123456789+,"[(n) & 0x3f])
2261 
2262 /* map the modified-base64 character `c' to its 64 bit value */
2263 #define UNB64(c)	((c) == '+' ? 62 : (c) == ',' ? 63 : (c) >= 'a' ? \
2264 					(c) - 71 : (c) >= 'A' ? (c) - 65 : (c) + 4)
2265 /* }}} */
2266 
2267 /* {{{ Decode a modified UTF-7 string */
PHP_FUNCTION(imap_utf7_decode)2268 PHP_FUNCTION(imap_utf7_decode)
2269 {
2270 	/* author: Andrew Skalski <askalski@chek.com> */
2271 	zend_string *arg;
2272 	const unsigned char *in, *inp, *endp;
2273 	unsigned char *out, *outp;
2274 	unsigned char c;
2275 	int inlen, outlen;
2276 	enum {
2277 		ST_NORMAL,	/* printable text */
2278 		ST_DECODE0,	/* encoded text rotation... */
2279 		ST_DECODE1,
2280 		ST_DECODE2,
2281 		ST_DECODE3
2282 	} state;
2283 
2284 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &arg) == FAILURE) {
2285 		RETURN_THROWS();
2286 	}
2287 
2288 	in = (const unsigned char *) ZSTR_VAL(arg);
2289 	inlen = ZSTR_LEN(arg);
2290 
2291 	/* validate and compute length of output string */
2292 	outlen = 0;
2293 	state = ST_NORMAL;
2294 	for (endp = (inp = in) + inlen; inp < endp; inp++) {
2295 		if (state == ST_NORMAL) {
2296 			/* process printable character */
2297 			if (SPECIAL(*inp)) {
2298 				php_error_docref(NULL, E_WARNING, "Invalid modified UTF-7 character: `%c'", *inp);
2299 				RETURN_FALSE;
2300 			} else if (*inp != '&') {
2301 				outlen++;
2302 			} else if (inp + 1 == endp) {
2303 				php_error_docref(NULL, E_WARNING, "Unexpected end of string");
2304 				RETURN_FALSE;
2305 			} else if (inp[1] != '-') {
2306 				state = ST_DECODE0;
2307 			} else {
2308 				outlen++;
2309 				inp++;
2310 			}
2311 		} else if (*inp == '-') {
2312 			/* return to NORMAL mode */
2313 			if (state == ST_DECODE1) {
2314 				php_error_docref(NULL, E_WARNING, "Stray modified base64 character: `%c'", *--inp);
2315 				RETURN_FALSE;
2316 			}
2317 			state = ST_NORMAL;
2318 		} else if (!B64CHAR(*inp)) {
2319 			php_error_docref(NULL, E_WARNING, "Invalid modified base64 character: `%c'", *inp);
2320 			RETURN_FALSE;
2321 		} else {
2322 			switch (state) {
2323 				case ST_DECODE3:
2324 					outlen++;
2325 					state = ST_DECODE0;
2326 					break;
2327 				case ST_DECODE2:
2328 				case ST_DECODE1:
2329 					outlen++;
2330 					ZEND_FALLTHROUGH;
2331 				case ST_DECODE0:
2332 					state++;
2333 				case ST_NORMAL:
2334 					break;
2335 			}
2336 		}
2337 	}
2338 
2339 	/* enforce end state */
2340 	if (state != ST_NORMAL) {
2341 		php_error_docref(NULL, E_WARNING, "Unexpected end of string");
2342 		RETURN_FALSE;
2343 	}
2344 
2345 	/* allocate output buffer */
2346 	out = emalloc(outlen + 1);
2347 
2348 	/* decode input string */
2349 	outp = out;
2350 	state = ST_NORMAL;
2351 	for (endp = (inp = in) + inlen; inp < endp; inp++) {
2352 		if (state == ST_NORMAL) {
2353 			if (*inp == '&' && inp[1] != '-') {
2354 				state = ST_DECODE0;
2355 			}
2356 			else if ((*outp++ = *inp) == '&') {
2357 				inp++;
2358 			}
2359 		}
2360 		else if (*inp == '-') {
2361 			state = ST_NORMAL;
2362 		}
2363 		else {
2364 			/* decode input character */
2365 			switch (state) {
2366 			case ST_DECODE0:
2367 				*outp = UNB64(*inp) << 2;
2368 				state = ST_DECODE1;
2369 				break;
2370 			case ST_DECODE1:
2371 				outp[1] = UNB64(*inp);
2372 				c = outp[1] >> 4;
2373 				*outp++ |= c;
2374 				*outp <<= 4;
2375 				state = ST_DECODE2;
2376 				break;
2377 			case ST_DECODE2:
2378 				outp[1] = UNB64(*inp);
2379 				c = outp[1] >> 2;
2380 				*outp++ |= c;
2381 				*outp <<= 6;
2382 				state = ST_DECODE3;
2383 				break;
2384 			case ST_DECODE3:
2385 				*outp++ |= UNB64(*inp);
2386 				state = ST_DECODE0;
2387 			case ST_NORMAL:
2388 				break;
2389 			}
2390 		}
2391 	}
2392 
2393 	*outp = 0;
2394 
2395 #if PHP_DEBUG
2396 	/* warn if we computed outlen incorrectly */
2397 	if (outp - out != outlen) {
2398 		php_error_docref(NULL, E_WARNING, "outp - out [%zd] != outlen [%d]", outp - out, outlen);
2399 	}
2400 #endif
2401 
2402 	RETURN_STRINGL((char*)out, outlen);
2403 }
2404 /* }}} */
2405 
2406 /* {{{ Encode a string in modified UTF-7 */
PHP_FUNCTION(imap_utf7_encode)2407 PHP_FUNCTION(imap_utf7_encode)
2408 {
2409 	/* author: Andrew Skalski <askalski@chek.com> */
2410 	zend_string *arg;
2411 	const unsigned char *in, *inp, *endp;
2412 	zend_string *out;
2413 	unsigned char *outp;
2414 	unsigned char c;
2415 	int inlen, outlen;
2416 	enum {
2417 		ST_NORMAL,	/* printable text */
2418 		ST_ENCODE0,	/* encoded text rotation... */
2419 		ST_ENCODE1,
2420 		ST_ENCODE2
2421 	} state;
2422 
2423 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &arg) == FAILURE) {
2424 		RETURN_THROWS();
2425 	}
2426 
2427 	in = (const unsigned char *) ZSTR_VAL(arg);
2428 	inlen = ZSTR_LEN(arg);
2429 
2430 	/* compute the length of the result string */
2431 	outlen = 0;
2432 	state = ST_NORMAL;
2433 	endp = (inp = in) + inlen;
2434 	while (inp < endp) {
2435 		if (state == ST_NORMAL) {
2436 			if (SPECIAL(*inp)) {
2437 				state = ST_ENCODE0;
2438 				outlen++;
2439 			} else if (*inp++ == '&') {
2440 				outlen++;
2441 			}
2442 			outlen++;
2443 		} else if (!SPECIAL(*inp)) {
2444 			state = ST_NORMAL;
2445 		} else {
2446 			/* ST_ENCODE0 -> ST_ENCODE1	- two chars
2447 			 * ST_ENCODE1 -> ST_ENCODE2	- one char
2448 			 * ST_ENCODE2 -> ST_ENCODE0	- one char
2449 			 */
2450 			if (state == ST_ENCODE2) {
2451 				state = ST_ENCODE0;
2452 			}
2453 			else if (state++ == ST_ENCODE0) {
2454 				outlen++;
2455 			}
2456 			outlen++;
2457 			inp++;
2458 		}
2459 	}
2460 
2461 	/* allocate output buffer */
2462 	out = zend_string_safe_alloc(1, outlen, 0, 0);
2463 
2464 	/* encode input string */
2465 	outp = (unsigned char*)ZSTR_VAL(out);
2466 	state = ST_NORMAL;
2467 	endp = (inp = in) + inlen;
2468 	while (inp < endp || state != ST_NORMAL) {
2469 		if (state == ST_NORMAL) {
2470 			if (SPECIAL(*inp)) {
2471 				/* begin encoding */
2472 				*outp++ = '&';
2473 				state = ST_ENCODE0;
2474 			} else if ((*outp++ = *inp++) == '&') {
2475 				*outp++ = '-';
2476 			}
2477 		} else if (inp == endp || !SPECIAL(*inp)) {
2478 			/* flush overflow and terminate region */
2479 			if (state != ST_ENCODE0) {
2480 				c = B64(*outp);
2481 				*outp++ = c;
2482 			}
2483 			*outp++ = '-';
2484 			state = ST_NORMAL;
2485 		} else {
2486 			/* encode input character */
2487 			switch (state) {
2488 				case ST_ENCODE0:
2489 					*outp++ = B64(*inp >> 2);
2490 					*outp = *inp++ << 4;
2491 					state = ST_ENCODE1;
2492 					break;
2493 				case ST_ENCODE1:
2494 					c = B64(*outp | *inp >> 4);
2495 					*outp++ = c;
2496 					*outp = *inp++ << 2;
2497 					state = ST_ENCODE2;
2498 					break;
2499 				case ST_ENCODE2:
2500 					c = B64(*outp | *inp >> 6);
2501 					*outp++ = c;
2502 					*outp++ = B64(*inp++);
2503 					state = ST_ENCODE0;
2504 				case ST_NORMAL:
2505 					break;
2506 			}
2507 		}
2508 	}
2509 
2510 	*outp = 0;
2511 
2512 	RETURN_STR(out);
2513 }
2514 /* }}} */
2515 
2516 #undef SPECIAL
2517 #undef B64CHAR
2518 #undef B64
2519 #undef UNB64
2520 
2521 #ifdef HAVE_IMAP_MUTF7
php_imap_mutf7(INTERNAL_FUNCTION_PARAMETERS,int mode)2522 static void php_imap_mutf7(INTERNAL_FUNCTION_PARAMETERS, int mode) /* {{{ */
2523 {
2524 	zend_string *in;
2525 	unsigned char *out;
2526 
2527 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &in) == FAILURE) {
2528 		RETURN_THROWS();
2529 	}
2530 
2531 	if (ZSTR_LEN(in) < 1) {
2532 		RETURN_EMPTY_STRING();
2533 	}
2534 
2535 	if (mode == 0) {
2536 		out = utf8_to_mutf7((unsigned char *) ZSTR_VAL(in));
2537 	} else {
2538 		out = utf8_from_mutf7((unsigned char *) ZSTR_VAL(in));
2539 	}
2540 
2541 	if (out == NIL) {
2542 		RETURN_FALSE;
2543 	} else {
2544 		RETVAL_STRING((char *)out);
2545 		fs_give((void**) &out);
2546 	}
2547 }
2548 /* }}} */
2549 
2550 /* {{{ Encode a UTF-8 string to modified UTF-7 */
PHP_FUNCTION(imap_utf8_to_mutf7)2551 PHP_FUNCTION(imap_utf8_to_mutf7)
2552 {
2553 	php_imap_mutf7(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
2554 }
2555 /* }}} */
2556 
2557 /* {{{ Decode a modified UTF-7 string to UTF-8 */
PHP_FUNCTION(imap_mutf7_to_utf8)2558 PHP_FUNCTION(imap_mutf7_to_utf8)
2559 {
2560 	php_imap_mutf7(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
2561 }
2562 /* }}} */
2563 #endif
2564 
2565 /* {{{ Sets flags on messages */
PHP_FUNCTION(imap_setflag_full)2566 PHP_FUNCTION(imap_setflag_full)
2567 {
2568 	zval *imap_conn_obj;
2569 	zend_string *sequence, *flag;
2570 	zend_long flags = 0;
2571 	php_imap_object *imap_conn_struct;
2572 
2573 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "OSS|l", &imap_conn_obj, php_imap_ce, &sequence, &flag, &flags) == FAILURE) {
2574 		RETURN_THROWS();
2575 	}
2576 
2577 	GET_IMAP_STREAM(imap_conn_struct, imap_conn_obj);
2578 
2579 	if (flags && ((flags & ~ST_UID) != 0)) {
2580 		zend_argument_value_error(4, "must be ST_UID or 0");
2581 		RETURN_THROWS();
2582 	}
2583 
2584 	mail_setflag_full(imap_conn_struct->imap_stream, ZSTR_VAL(sequence), ZSTR_VAL(flag), (flags ? flags : NIL));
2585 	RETURN_TRUE;
2586 }
2587 /* }}} */
2588 
2589 /* {{{ Clears flags on messages */
PHP_FUNCTION(imap_clearflag_full)2590 PHP_FUNCTION(imap_clearflag_full)
2591 {
2592 	zval *imap_conn_obj;
2593 	zend_string *sequence, *flag;
2594 	zend_long flags = 0;
2595 	php_imap_object *imap_conn_struct;
2596 
2597 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "OSS|l", &imap_conn_obj, php_imap_ce, &sequence, &flag, &flags) ==FAILURE) {
2598 		RETURN_THROWS();
2599 	}
2600 
2601 	GET_IMAP_STREAM(imap_conn_struct, imap_conn_obj);
2602 
2603 	if (flags && ((flags & ~ST_UID) != 0)) {
2604 		zend_argument_value_error(4, "must be ST_UID or 0");
2605 		RETURN_THROWS();
2606 	}
2607 
2608 	mail_clearflag_full(imap_conn_struct->imap_stream, ZSTR_VAL(sequence), ZSTR_VAL(flag), flags);
2609 	RETURN_TRUE;
2610 }
2611 /* }}} */
2612 
2613 /* {{{ Sort an array of message headers, optionally including only messages that meet specified criteria. */
PHP_FUNCTION(imap_sort)2614 PHP_FUNCTION(imap_sort)
2615 {
2616 	zval *imap_conn_obj;
2617 	zend_string *criteria = NULL, *charset = NULL;
2618 	zend_long sort, flags = 0;
2619 	bool rev;
2620 	php_imap_object *imap_conn_struct;
2621 	unsigned long *slst, *sl;
2622 	char *search_criteria;
2623 	SORTPGM *mypgm=NIL;
2624 	SEARCHPGM *spg=NIL;
2625 
2626 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "Olb|lS!S!", &imap_conn_obj, php_imap_ce, &sort, &rev, &flags, &criteria, &charset) == FAILURE) {
2627 		RETURN_THROWS();
2628 	}
2629 
2630 	GET_IMAP_STREAM(imap_conn_struct, imap_conn_obj);
2631 
2632 	if (!(sort == SORTDATE || sort == SORTARRIVAL || sort == SORTFROM || sort == SORTSUBJECT || sort == SORTTO ||
2633 			sort == SORTCC || sort == SORTSIZE) ) {
2634 		zend_argument_value_error(2, "must be one of the SORT* constants");
2635 		RETURN_THROWS();
2636 	}
2637 
2638 	if (flags && ((flags & ~(SE_UID|SE_NOPREFETCH )) != 0)) {
2639 		zend_argument_value_error(4, "must be a bitmask of SE_UID, and SE_NOPREFETCH");
2640 		RETURN_THROWS();
2641 	}
2642 
2643 	if (criteria) {
2644 		search_criteria = estrndup(ZSTR_VAL(criteria), ZSTR_LEN(criteria));
2645 		spg = mail_criteria(search_criteria);
2646 		efree(search_criteria);
2647 	} else {
2648 		spg = mail_newsearchpgm();
2649 	}
2650 	if (spg == NIL) {
2651 		RETURN_FALSE;
2652 	}
2653 
2654 	mypgm = mail_newsortpgm();
2655 	mypgm->reverse = rev;
2656 	mypgm->function = (short) sort;
2657 	mypgm->next = NIL;
2658 
2659 	slst = mail_sort(imap_conn_struct->imap_stream, (charset ? ZSTR_VAL(charset) : NIL), spg, mypgm, flags);
2660 
2661 	mail_free_sortpgm(&mypgm);
2662 	if (spg && !(flags & SE_FREE)) {
2663 		mail_free_searchpgm(&spg);
2664 	}
2665 
2666 	array_init(return_value);
2667 	if (slst != NULL) {
2668 		for (sl = slst; *sl; sl++) {
2669 			add_next_index_long(return_value, *sl);
2670 		}
2671 		fs_give ((void **) &slst);
2672 	}
2673 }
2674 /* }}} */
2675 
2676 /* {{{ Get the full unfiltered header for a message */
PHP_FUNCTION(imap_fetchheader)2677 PHP_FUNCTION(imap_fetchheader)
2678 {
2679 	zval *imap_conn_obj;
2680 	zend_long msgno, flags = 0;
2681 	php_imap_object *imap_conn_struct;
2682 
2683 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ol|l", &imap_conn_obj, php_imap_ce, &msgno, &flags) == FAILURE) {
2684 		RETURN_THROWS();
2685 	}
2686 
2687 	GET_IMAP_STREAM(imap_conn_struct, imap_conn_obj);
2688 
2689 	PHP_IMAP_CHECK_MSGNO_MAYBE_UID_PRE_FLAG_CHECKS(msgno, 2);
2690 
2691 	if (flags && ((flags & ~(FT_UID|FT_INTERNAL|FT_PREFETCHTEXT)) != 0)) {
2692 		zend_argument_value_error(3, "must be a bitmask of FT_UID, FT_PREFETCHTEXT, and FT_INTERNAL");
2693 		RETURN_THROWS();
2694 	}
2695 
2696 	PHP_IMAP_CHECK_MSGNO_MAYBE_UID_POST_FLAG_CHECKS(msgno, 2, flags, FT_UID);
2697 
2698 	RETVAL_STRING(mail_fetchheader_full(imap_conn_struct->imap_stream, msgno, NIL, NIL, flags));
2699 }
2700 /* }}} */
2701 
2702 /* {{{ Get the unique message id associated with a standard sequential message number */
PHP_FUNCTION(imap_uid)2703 PHP_FUNCTION(imap_uid)
2704 {
2705 	zval *imap_conn_obj;
2706 	zend_long msgno;
2707 	php_imap_object *imap_conn_struct;
2708 
2709 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ol", &imap_conn_obj, php_imap_ce, &msgno) == FAILURE) {
2710 		RETURN_THROWS();
2711 	}
2712 
2713 	GET_IMAP_STREAM(imap_conn_struct, imap_conn_obj);
2714 
2715 	PHP_IMAP_CHECK_MSGNO(msgno, 2);
2716 
2717 	RETURN_LONG(mail_uid(imap_conn_struct->imap_stream, msgno));
2718 }
2719 /* }}} */
2720 
2721 /* {{{ Get the sequence number associated with a UID */
PHP_FUNCTION(imap_msgno)2722 PHP_FUNCTION(imap_msgno)
2723 {
2724 	zval *imap_conn_obj;
2725 	zend_long msg_uid;
2726 	php_imap_object *imap_conn_struct;
2727 
2728 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ol", &imap_conn_obj, php_imap_ce, &msg_uid) == FAILURE) {
2729 		RETURN_THROWS();
2730 	}
2731 
2732 	GET_IMAP_STREAM(imap_conn_struct, imap_conn_obj);
2733 
2734 	/* Do NOT use the PHP_IMAP_CHECK_MSGNO() macro as UID cannot be checked for their upper bound. */
2735 	if (msg_uid < 1) {
2736 		zend_argument_value_error(2, "must be greater than 0");
2737 		RETURN_THROWS();
2738 	}
2739 
2740 	RETURN_LONG(mail_msgno(imap_conn_struct->imap_stream, msg_uid));
2741 }
2742 /* }}} */
2743 
2744 /* {{{ Get status info from a mailbox */
PHP_FUNCTION(imap_status)2745 PHP_FUNCTION(imap_status)
2746 {
2747 	zval *imap_conn_obj;
2748 	zend_string *mbx;
2749 	zend_long flags;
2750 	php_imap_object *imap_conn_struct;
2751 
2752 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "OSl", &imap_conn_obj, php_imap_ce, &mbx, &flags) == FAILURE) {
2753 		RETURN_THROWS();
2754 	}
2755 
2756 	GET_IMAP_STREAM(imap_conn_struct, imap_conn_obj);
2757 
2758 	if (flags && ((flags & ~(SA_ALL)) != 0)) {
2759 		zend_argument_value_error(3, "must be a bitmask of SA_* constants");
2760 		RETURN_THROWS();
2761 	}
2762 
2763 	if (mail_status(imap_conn_struct->imap_stream, ZSTR_VAL(mbx), flags)) {
2764 		object_init(return_value);
2765 
2766 		zend_update_property_long(
2767 			Z_OBJCE_P(return_value), Z_OBJ_P(return_value),
2768 			"flags", strlen("flags"),
2769 			IMAPG(status_flags)
2770 		);
2771 		if (IMAPG(status_flags) & SA_MESSAGES) {
2772 			zend_update_property_long(
2773 				Z_OBJCE_P(return_value), Z_OBJ_P(return_value),
2774 				"messages", strlen("messages"),
2775 				IMAPG(status_messages)
2776 			);
2777 		}
2778 		if (IMAPG(status_flags) & SA_RECENT) {
2779 			zend_update_property_long(
2780 				Z_OBJCE_P(return_value), Z_OBJ_P(return_value),
2781 				"recent", strlen("recent"),
2782 				IMAPG(status_recent)
2783 			);
2784 		}
2785 		if (IMAPG(status_flags) & SA_UNSEEN) {
2786 			zend_update_property_long(
2787 				Z_OBJCE_P(return_value), Z_OBJ_P(return_value),
2788 				"unseen", strlen("unseen"),
2789 				IMAPG(status_unseen)
2790 			);
2791 		}
2792 		if (IMAPG(status_flags) & SA_UIDNEXT) {
2793 			zend_update_property_long(
2794 				Z_OBJCE_P(return_value), Z_OBJ_P(return_value),
2795 				"uidnext", strlen("uidnext"),
2796 				IMAPG(status_uidnext)
2797 			);
2798 		}
2799 		if (IMAPG(status_flags) & SA_UIDVALIDITY) {
2800 			zend_update_property_long(
2801 				Z_OBJCE_P(return_value), Z_OBJ_P(return_value),
2802 				"uidvalidity", strlen("uidvalidity"),
2803 				IMAPG(status_uidvalidity)
2804 			);
2805 		}
2806 	} else {
2807 		RETURN_FALSE;
2808 	}
2809 }
2810 /* }}} */
2811 
php_imap_populate_body_struct_object(zval * z_object,const BODY * body)2812 static void php_imap_populate_body_struct_object(zval *z_object, const BODY *body)
2813 {
2814 	if (body->type <= TYPEMAX) {
2815 		zend_update_property_long(
2816 			Z_OBJCE_P(z_object), Z_OBJ_P(z_object),
2817 			"type", strlen("type"),
2818 			body->type
2819 		);
2820 	}
2821 
2822 	if (body->encoding <= ENCMAX) {
2823 		zend_update_property_long(
2824 			Z_OBJCE_P(z_object), Z_OBJ_P(z_object),
2825 			"encoding", strlen("encoding"),
2826 			body->encoding
2827 		);
2828 	}
2829 
2830 	if (body->subtype) {
2831 		zend_update_property_long(
2832 			Z_OBJCE_P(z_object), Z_OBJ_P(z_object),
2833 			"ifsubtype", strlen("ifsubtype"),
2834 			1
2835 		);
2836 		zend_update_property_string(
2837 			Z_OBJCE_P(z_object), Z_OBJ_P(z_object),
2838 			"subtype", strlen("subtype"),
2839 			body->subtype
2840 		);
2841 	} else {
2842 		zend_update_property_long(
2843 			Z_OBJCE_P(z_object), Z_OBJ_P(z_object),
2844 			"ifsubtype", strlen("ifsubtype"),
2845 			0
2846 		);
2847 	}
2848 
2849 	if (body->description) {
2850 		zend_update_property_long(
2851 			Z_OBJCE_P(z_object), Z_OBJ_P(z_object),
2852 			"ifdescription", strlen("ifdescription"),
2853 			1
2854 		);
2855 		zend_update_property_string(
2856 			Z_OBJCE_P(z_object), Z_OBJ_P(z_object),
2857 			"description", strlen("description"),
2858 			body->description
2859 		);
2860 	} else {
2861 		zend_update_property_long(
2862 			Z_OBJCE_P(z_object), Z_OBJ_P(z_object),
2863 			"ifdescription", strlen("ifdescription"),
2864 			0
2865 		);
2866 	}
2867 
2868 	if (body->id) {
2869 		zend_update_property_long(
2870 			Z_OBJCE_P(z_object), Z_OBJ_P(z_object),
2871 			"ifid", strlen("ifid"),
2872 			1
2873 		);
2874 		zend_update_property_string(
2875 			Z_OBJCE_P(z_object), Z_OBJ_P(z_object),
2876 			"id", strlen("id"),
2877 			body->id
2878 		);
2879 	} else {
2880 		zend_update_property_long(
2881 			Z_OBJCE_P(z_object), Z_OBJ_P(z_object),
2882 			"ifid", strlen("ifid"),
2883 			0
2884 		);
2885 	}
2886 
2887 	if (body->size.lines) {
2888 		zend_update_property_long(
2889 			Z_OBJCE_P(z_object), Z_OBJ_P(z_object),
2890 			"lines", strlen("lines"),
2891 			body->size.lines
2892 		);
2893 	}
2894 
2895 	if (body->size.bytes) {
2896 		zend_update_property_long(
2897 			Z_OBJCE_P(z_object), Z_OBJ_P(z_object),
2898 			"bytes", strlen("bytes"),
2899 			body->size.bytes
2900 		);
2901 	}
2902 
2903 #ifdef IMAP41
2904 	if (body->disposition.type) {
2905 		zend_update_property_long(
2906 			Z_OBJCE_P(z_object), Z_OBJ_P(z_object),
2907 			"ifdisposition", strlen("ifdisposition"),
2908 			1
2909 		);
2910 		zend_update_property_string(
2911 			Z_OBJCE_P(z_object), Z_OBJ_P(z_object),
2912 			"disposition", strlen("disposition"),
2913 			body->disposition.type
2914 		);
2915 	} else {
2916 		zend_update_property_long(
2917 			Z_OBJCE_P(z_object), Z_OBJ_P(z_object),
2918 			"ifdisposition", strlen("ifdisposition"),
2919 			0
2920 		);
2921 	}
2922 
2923 	if (body->disposition.parameter) {
2924 		PARAMETER *disposition_parameter = body->disposition.parameter;
2925 		zval z_disposition_parameter_list;
2926 
2927 		zend_update_property_long(
2928 			Z_OBJCE_P(z_object), Z_OBJ_P(z_object),
2929 			"ifdparameters", strlen("ifdparameters"),
2930 			1
2931 		);
2932 		array_init(&z_disposition_parameter_list);
2933 		do {
2934 			zval z_disposition_parameter;
2935 			object_init(&z_disposition_parameter);
2936 			zend_update_property_string(
2937 				Z_OBJCE_P(&z_disposition_parameter), Z_OBJ_P(&z_disposition_parameter),
2938 				"attribute", strlen("attribute"),
2939 				disposition_parameter->attribute
2940 			);
2941 			zend_update_property_string(
2942 				Z_OBJCE_P(&z_disposition_parameter), Z_OBJ_P(&z_disposition_parameter),
2943 				"value", strlen("value"),
2944 				disposition_parameter->value
2945 			);
2946 			zend_hash_next_index_insert_new(Z_ARR(z_disposition_parameter_list), &z_disposition_parameter);
2947 		} while ((disposition_parameter = disposition_parameter->next));
2948 
2949 		zend_update_property(
2950 			Z_OBJCE_P(z_object), Z_OBJ_P(z_object),
2951 			"dparameters", strlen("dparameters"),
2952 			&z_disposition_parameter_list
2953 		);
2954 		zval_ptr_dtor(&z_disposition_parameter_list);
2955 	} else {
2956 		zend_update_property_long(
2957 			Z_OBJCE_P(z_object), Z_OBJ_P(z_object),
2958 			"ifdparameters", strlen("ifdparameters"),
2959 			0
2960 		);
2961 	}
2962 #endif
2963 
2964 	PARAMETER *body_parameters = body->parameter;
2965 	zval z_body_parameter_list;
2966 
2967 	if (body_parameters) {
2968 		zend_update_property_long(
2969 			Z_OBJCE_P(z_object), Z_OBJ_P(z_object),
2970 			"ifparameters", strlen("ifparameters"),
2971 			1
2972 		);
2973 
2974 		array_init(&z_body_parameter_list);
2975 		do {
2976 			zval z_body_parameter;
2977 			object_init(&z_body_parameter);
2978 			zend_update_property_string(
2979 				Z_OBJCE_P(&z_body_parameter), Z_OBJ_P(&z_body_parameter),
2980 				"attribute", strlen("attribute"),
2981 				body_parameters->attribute
2982 			);
2983 			zend_update_property_string(
2984 				Z_OBJCE_P(&z_body_parameter), Z_OBJ_P(&z_body_parameter),
2985 				"value", strlen("value"),
2986 				body_parameters->value
2987 			);
2988 
2989 			zend_hash_next_index_insert_new(Z_ARR(z_body_parameter_list), &z_body_parameter);
2990 		} while ((body_parameters = body_parameters->next));
2991 	} else {
2992 		object_init(&z_body_parameter_list);
2993 		zend_update_property_long(
2994 			Z_OBJCE_P(z_object), Z_OBJ_P(z_object),
2995 			"ifparameters", strlen("ifparameters"),
2996 			0
2997 		);
2998 	}
2999 
3000 	zend_update_property(
3001 		Z_OBJCE_P(z_object), Z_OBJ_P(z_object),
3002 		"parameters", strlen("parameters"),
3003 		&z_body_parameter_list
3004 	);
3005 	zval_ptr_dtor(&z_body_parameter_list);
3006 }
3007 
3008 /* {{{ Read the structure of a specified body section of a specific message */
PHP_FUNCTION(imap_bodystruct)3009 PHP_FUNCTION(imap_bodystruct)
3010 {
3011 	zval *imap_conn_obj;
3012 	zend_long msgno;
3013 	zend_string *section;
3014 	php_imap_object *imap_conn_struct;
3015 
3016 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "OlS", &imap_conn_obj, php_imap_ce, &msgno, &section) == FAILURE) {
3017 		RETURN_THROWS();
3018 	}
3019 
3020 	GET_IMAP_STREAM(imap_conn_struct, imap_conn_obj);
3021 
3022 	PHP_IMAP_CHECK_MSGNO(msgno, 2);
3023 
3024 	const BODY *body = mail_body(imap_conn_struct->imap_stream, msgno, (unsigned char*)ZSTR_VAL(section));
3025 	if (body == NULL) {
3026 		RETURN_FALSE;
3027 	}
3028 
3029 	object_init(return_value);
3030 	php_imap_populate_body_struct_object(return_value, body);
3031 }
3032 
3033 /* }}} */
3034 
php_imap_update_property_with_full_address_str(zval * z_object,const char * property,size_t property_len,ADDRESS * address)3035 static void php_imap_update_property_with_full_address_str(
3036 	zval *z_object, const char *property, size_t property_len, ADDRESS *address)
3037 {
3038 	ADDRESS *current = address;
3039 	current->next = NULL;
3040 
3041 	zend_string *full_address = _php_rfc822_write_address(current);
3042 	if (full_address) {
3043 		zend_update_property_str(
3044 			Z_OBJCE_P(z_object), Z_OBJ_P(z_object),
3045 			property, property_len,
3046 			full_address
3047 		);
3048 		zend_string_release_ex(full_address, false);
3049 	}
3050 }
3051 
3052 #define UPDATE_PROPERTY_FULL_STR_ADDRESS(z_object, name, envelope) if ((envelope)->name) { php_imap_update_property_with_full_address_str((z_object), #name, strlen(#name), (envelope)->name); }
3053 
3054 /* {{{ Read an overview of the information in the headers of the given message sequence */
PHP_FUNCTION(imap_fetch_overview)3055 PHP_FUNCTION(imap_fetch_overview)
3056 {
3057 	zval *imap_conn_obj;
3058 	zend_string *sequence;
3059 	php_imap_object *imap_conn_struct;
3060 	zend_long status, flags = 0L;
3061 
3062 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "OS|l", &imap_conn_obj, php_imap_ce, &sequence, &flags) == FAILURE) {
3063 		RETURN_THROWS();
3064 	}
3065 
3066 	GET_IMAP_STREAM(imap_conn_struct, imap_conn_obj);
3067 
3068 	if (flags && ((flags & ~FT_UID) != 0)) {
3069 		zend_argument_value_error(3, "must be FT_UID or 0");
3070 		RETURN_THROWS();
3071 	}
3072 
3073 	status = (flags & FT_UID)
3074 		? mail_uid_sequence(imap_conn_struct->imap_stream, (unsigned char*)ZSTR_VAL(sequence))
3075 		: mail_sequence(imap_conn_struct->imap_stream, (unsigned char*)ZSTR_VAL(sequence));
3076 
3077 	if (!status) {
3078 		RETURN_EMPTY_ARRAY();
3079 	}
3080 
3081 	array_init(return_value);
3082 	for (unsigned long  i = 1; i <= imap_conn_struct->imap_stream->nmsgs; i++) {
3083 		MESSAGECACHE *elt;
3084 		ENVELOPE *env;
3085 
3086 		elt = mail_elt(imap_conn_struct->imap_stream, i);
3087 		if (!elt->sequence) {
3088 			continue;
3089 		}
3090 
3091 		env = mail_fetch_structure(imap_conn_struct->imap_stream, i, NIL, NIL);
3092 		if (!env) {
3093 			continue;
3094 		}
3095 
3096 		// TODO Use part _php_make_header_object function?
3097 		zval myoverview;
3098 		object_init(&myoverview);
3099 		if (env->subject) {
3100 			zend_update_property_string(
3101 				Z_OBJCE_P(&myoverview), Z_OBJ_P(&myoverview),
3102 				"subject", strlen("subject"),
3103 				env->subject
3104 			);
3105 		}
3106 		UPDATE_PROPERTY_FULL_STR_ADDRESS(&myoverview, from, env);
3107 		UPDATE_PROPERTY_FULL_STR_ADDRESS(&myoverview, to, env);
3108 		if (env->date) {
3109 			zend_update_property_string(
3110 				Z_OBJCE_P(&myoverview), Z_OBJ_P(&myoverview),
3111 				"date", strlen("date"),
3112 				(char*)env->date
3113 			);
3114 		}
3115 		if (env->message_id) {
3116 			zend_update_property_string(
3117 				Z_OBJCE_P(&myoverview), Z_OBJ_P(&myoverview),
3118 				"message_id", strlen("message_id"),
3119 				env->message_id
3120 			);
3121 		}
3122 		if (env->references) {
3123 			zend_update_property_string(
3124 				Z_OBJCE_P(&myoverview), Z_OBJ_P(&myoverview),
3125 				"references", strlen("references"),
3126 				env->references
3127 			);
3128 		}
3129 		if (env->in_reply_to) {
3130 			zend_update_property_string(
3131 				Z_OBJCE_P(&myoverview), Z_OBJ_P(&myoverview),
3132 				"in_reply_to", strlen("in_reply_to"),
3133 				env->in_reply_to
3134 			);
3135 		}
3136 		zend_update_property_long(
3137 			Z_OBJCE_P(&myoverview), Z_OBJ_P(&myoverview),
3138 			"size", strlen("size"),
3139 			elt->rfc822_size
3140 		);
3141 		zend_update_property_long(
3142 			Z_OBJCE_P(&myoverview), Z_OBJ_P(&myoverview),
3143 			"uid", strlen("uid"),
3144 			mail_uid(imap_conn_struct->imap_stream, i)
3145 		);
3146 		zend_update_property_long(
3147 			Z_OBJCE_P(&myoverview), Z_OBJ_P(&myoverview),
3148 			"msgno", strlen("msgno"),
3149 			i
3150 		);
3151 		zend_update_property_long(
3152 			Z_OBJCE_P(&myoverview), Z_OBJ_P(&myoverview),
3153 			"recent", strlen("recent"),
3154 			elt->recent
3155 		);
3156 		zend_update_property_long(
3157 			Z_OBJCE_P(&myoverview), Z_OBJ_P(&myoverview),
3158 			"flagged", strlen("flagged"),
3159 			elt->flagged
3160 		);
3161 		zend_update_property_long(
3162 			Z_OBJCE_P(&myoverview), Z_OBJ_P(&myoverview),
3163 			"answered", strlen("answered"),
3164 			elt->answered
3165 		);
3166 		zend_update_property_long(
3167 			Z_OBJCE_P(&myoverview), Z_OBJ_P(&myoverview),
3168 			"deleted", strlen("deleted"),
3169 			elt->deleted
3170 		);
3171 		zend_update_property_long(
3172 			Z_OBJCE_P(&myoverview), Z_OBJ_P(&myoverview),
3173 			"seen", strlen("seen"),
3174 			elt->seen
3175 		);
3176 		zend_update_property_long(
3177 			Z_OBJCE_P(&myoverview), Z_OBJ_P(&myoverview),
3178 			"draft", strlen("draft"),
3179 			elt->draft
3180 		);
3181 		zend_update_property_long(
3182 			Z_OBJCE_P(&myoverview), Z_OBJ_P(&myoverview),
3183 			"udate", strlen("udate"),
3184 			mail_longdate(elt)
3185 		);
3186 
3187 		zend_hash_next_index_insert_new(Z_ARR_P(return_value), &myoverview);
3188 	}
3189 }
3190 /* }}} */
3191 
header_injection(const zend_string * str,bool adrlist)3192 static bool header_injection(const zend_string *str, bool adrlist)
3193 {
3194 	const char *p = ZSTR_VAL(str);
3195 
3196 	while ((p = strpbrk(p, "\r\n")) != NULL) {
3197 		if (
3198 			!(p[0] == '\r' && p[1] == '\n')
3199 			/* adrlists do not support folding, but swallow trailing line breaks */
3200 			&& !(
3201 				(adrlist && p[1] == '\0')
3202 				/* other headers support folding */
3203 				|| (!adrlist && (p[1] == ' ' || p[1] == '\t'))
3204 			)
3205 		) {
3206 			return true;
3207 		}
3208 		p++;
3209 	}
3210 	return false;
3211 }
3212 
3213 /* {{{ Create a MIME message based on given envelope and body sections */
PHP_FUNCTION(imap_mail_compose)3214 PHP_FUNCTION(imap_mail_compose)
3215 {
3216 	HashTable *envelope, *body;
3217 	zend_string *key;
3218 	zval *data, *pvalue, *disp_data, *env_data;
3219 	char *cookie = NIL;
3220 	ENVELOPE *env;
3221 	BODY *bod=NULL, *topbod=NULL;
3222 	PART *mypart=NULL, *part;
3223 	PARAMETER *param, *disp_param = NULL, *custom_headers_param = NULL, *tmp_param = NULL;
3224 	char *tmp=NULL, *mystring=NULL, *t=NULL, *tempstring=NULL, *str_copy = NULL;
3225 	int toppart = 0;
3226 	int first;
3227 
3228 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "h/h/", &envelope, &body) == FAILURE) {
3229 		RETURN_THROWS();
3230 	}
3231 
3232 	if (zend_hash_num_elements(body) == 0) {
3233 		zend_argument_value_error(2, "cannot be empty");
3234 	}
3235 
3236 #define CHECK_HEADER_INJECTION(zstr, adrlist, header) \
3237 	if (header_injection(zstr, adrlist)) { \
3238 		php_error_docref(NULL, E_WARNING, "header injection attempt in " header); \
3239 		RETVAL_FALSE; \
3240 		goto done; \
3241 	}
3242 
3243 #define PHP_RFC822_PARSE_ADRLIST(target, value) \
3244 	str_copy = estrndup(Z_STRVAL_P(value), Z_STRLEN_P(value)); \
3245 	rfc822_parse_adrlist(target, str_copy, "NO HOST"); \
3246 	efree(str_copy);
3247 
3248 	env = mail_newenvelope();
3249 	if ((pvalue = zend_hash_str_find(envelope, "remail", sizeof("remail") - 1)) != NULL) {
3250 		convert_to_string(pvalue);
3251 		CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 0, "remail");
3252 		env->remail = cpystr(Z_STRVAL_P(pvalue));
3253 	}
3254 	if ((pvalue = zend_hash_str_find(envelope, "return_path", sizeof("return_path") - 1)) != NULL) {
3255 		convert_to_string(pvalue);
3256 		CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 1, "return_path");
3257 		PHP_RFC822_PARSE_ADRLIST(&env->return_path, pvalue);
3258 	}
3259 	if ((pvalue = zend_hash_str_find(envelope, "date", sizeof("date") - 1)) != NULL) {
3260 		convert_to_string(pvalue);
3261 		CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 0, "date");
3262 		env->date = (unsigned char*)cpystr(Z_STRVAL_P(pvalue));
3263 	}
3264 	if ((pvalue = zend_hash_str_find(envelope, "from", sizeof("from") - 1)) != NULL) {
3265 		convert_to_string(pvalue);
3266 		CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 1, "from");
3267 		PHP_RFC822_PARSE_ADRLIST(&env->from, pvalue);
3268 	}
3269 	if ((pvalue = zend_hash_str_find(envelope, "reply_to", sizeof("reply_to") - 1)) != NULL) {
3270 		convert_to_string(pvalue);
3271 		CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 1, "reply_to");
3272 		PHP_RFC822_PARSE_ADRLIST(&env->reply_to, pvalue);
3273 	}
3274 	if ((pvalue = zend_hash_str_find(envelope, "in_reply_to", sizeof("in_reply_to") - 1)) != NULL) {
3275 		convert_to_string(pvalue);
3276 		CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 0, "in_reply_to");
3277 		env->in_reply_to = cpystr(Z_STRVAL_P(pvalue));
3278 	}
3279 	if ((pvalue = zend_hash_str_find(envelope, "subject", sizeof("subject") - 1)) != NULL) {
3280 		convert_to_string(pvalue);
3281 		CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 0, "subject");
3282 		env->subject = cpystr(Z_STRVAL_P(pvalue));
3283 	}
3284 	if ((pvalue = zend_hash_str_find(envelope, "to", sizeof("to") - 1)) != NULL) {
3285 		convert_to_string(pvalue);
3286 		CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 1, "to");
3287 		PHP_RFC822_PARSE_ADRLIST(&env->to, pvalue);
3288 	}
3289 	if ((pvalue = zend_hash_str_find(envelope, "cc", sizeof("cc") - 1)) != NULL) {
3290 		convert_to_string(pvalue);
3291 		CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 1, "cc");
3292 		PHP_RFC822_PARSE_ADRLIST(&env->cc, pvalue);
3293 	}
3294 	if ((pvalue = zend_hash_str_find(envelope, "bcc", sizeof("bcc") - 1)) != NULL) {
3295 		convert_to_string(pvalue);
3296 		CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 1, "bcc");
3297 		PHP_RFC822_PARSE_ADRLIST(&env->bcc, pvalue);
3298 	}
3299 	if ((pvalue = zend_hash_str_find(envelope, "message_id", sizeof("message_id") - 1)) != NULL) {
3300 		convert_to_string(pvalue);
3301 		CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 0, "message_id");
3302 		env->message_id=cpystr(Z_STRVAL_P(pvalue));
3303 	}
3304 
3305 	if ((pvalue = zend_hash_str_find(envelope, "custom_headers", sizeof("custom_headers") - 1)) != NULL) {
3306 		if (Z_TYPE_P(pvalue) == IS_ARRAY) {
3307 			custom_headers_param = tmp_param = NULL;
3308 			SEPARATE_ARRAY(pvalue);
3309 			ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(pvalue), env_data) {
3310 				custom_headers_param = mail_newbody_parameter();
3311 				convert_to_string(env_data);
3312 				CHECK_HEADER_INJECTION(Z_STR_P(env_data), 0, "custom_headers");
3313 				custom_headers_param->value = (char *) fs_get(Z_STRLEN_P(env_data) + 1);
3314 				custom_headers_param->attribute = NULL;
3315 				memcpy(custom_headers_param->value, Z_STRVAL_P(env_data), Z_STRLEN_P(env_data) + 1);
3316 				custom_headers_param->next = tmp_param;
3317 				tmp_param = custom_headers_param;
3318 			} ZEND_HASH_FOREACH_END();
3319 		}
3320 	}
3321 
3322 	first = 1;
3323 	ZEND_HASH_FOREACH_VAL(body, data) {
3324 		if (first) {
3325 			first = 0;
3326 
3327 			if (Z_TYPE_P(data) != IS_ARRAY) {
3328 				zend_argument_type_error(2, "individual body must be of type array, %s given",
3329 					zend_zval_value_name(data));
3330 				goto done;
3331 			}
3332 			if (zend_hash_num_elements(Z_ARRVAL_P(data)) == 0) {
3333 				zend_argument_value_error(2, "individual body cannot be empty");
3334 				goto done;
3335 			}
3336 			SEPARATE_ARRAY(data);
3337 
3338 			bod = mail_newbody();
3339 			topbod = bod;
3340 
3341 			if ((pvalue = zend_hash_str_find(Z_ARRVAL_P(data), "type", sizeof("type") - 1)) != NULL) {
3342 				zend_long type = zval_get_long(pvalue);
3343 				if (type >= 0 && type <= TYPEMAX && body_types[type] != NULL) {
3344 					bod->type = (short) type;
3345 				}
3346 			}
3347 			if ((pvalue = zend_hash_str_find(Z_ARRVAL_P(data), "encoding", sizeof("encoding") - 1)) != NULL) {
3348 				zend_long encoding = zval_get_long(pvalue);
3349 				if (encoding >= 0 && encoding <= ENCMAX && body_encodings[encoding] != NULL) {
3350 					bod->encoding = (short) encoding;
3351 				}
3352 			}
3353 			if ((pvalue = zend_hash_str_find(Z_ARRVAL_P(data), "charset", sizeof("charset") - 1)) != NULL) {
3354 				convert_to_string(pvalue);
3355 				CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 0, "body charset");
3356 				tmp_param = mail_newbody_parameter();
3357 				tmp_param->value = cpystr(Z_STRVAL_P(pvalue));
3358 				tmp_param->attribute = cpystr("CHARSET");
3359 				tmp_param->next = bod->parameter;
3360 				bod->parameter = tmp_param;
3361 			}
3362 			if ((pvalue = zend_hash_str_find(Z_ARRVAL_P(data), "type.parameters", sizeof("type.parameters") - 1)) != NULL) {
3363 				if(Z_TYPE_P(pvalue) == IS_ARRAY && !HT_IS_PACKED(Z_ARRVAL_P(pvalue))) {
3364 					disp_param = tmp_param = NULL;
3365 					SEPARATE_ARRAY(pvalue);
3366 					ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(pvalue), key, disp_data) {
3367 						if (key == NULL) continue;
3368 						CHECK_HEADER_INJECTION(key, 0, "body disposition key");
3369 						disp_param = mail_newbody_parameter();
3370 						disp_param->attribute = cpystr(ZSTR_VAL(key));
3371 						convert_to_string(disp_data);
3372 						CHECK_HEADER_INJECTION(Z_STR_P(disp_data), 0, "body disposition value");
3373 						disp_param->value = (char *) fs_get(Z_STRLEN_P(disp_data) + 1);
3374 						memcpy(disp_param->value, Z_STRVAL_P(disp_data), Z_STRLEN_P(disp_data) + 1);
3375 						disp_param->next = tmp_param;
3376 						tmp_param = disp_param;
3377 					} ZEND_HASH_FOREACH_END();
3378 					bod->parameter = disp_param;
3379 				}
3380 			}
3381 			if ((pvalue = zend_hash_str_find(Z_ARRVAL_P(data), "subtype", sizeof("subtype") - 1)) != NULL) {
3382 				convert_to_string_ex(pvalue);
3383 				CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 0, "body subtype");
3384 				bod->subtype = cpystr(Z_STRVAL_P(pvalue));
3385 			}
3386 			if ((pvalue = zend_hash_str_find(Z_ARRVAL_P(data), "id", sizeof("id") - 1)) != NULL) {
3387 				convert_to_string(pvalue);
3388 				CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 0, "body id");
3389 				bod->id = cpystr(Z_STRVAL_P(pvalue));
3390 			}
3391 			if ((pvalue = zend_hash_str_find(Z_ARRVAL_P(data), "description", sizeof("description") - 1)) != NULL) {
3392 				convert_to_string(pvalue);
3393 				CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 0, "body description");
3394 				bod->description = cpystr(Z_STRVAL_P(pvalue));
3395 			}
3396 			if ((pvalue = zend_hash_str_find(Z_ARRVAL_P(data), "disposition.type", sizeof("disposition.type") - 1)) != NULL) {
3397 				convert_to_string(pvalue);
3398 				CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 0, "body disposition.type");
3399 				bod->disposition.type = (char *) fs_get(Z_STRLEN_P(pvalue) + 1);
3400 				memcpy(bod->disposition.type, Z_STRVAL_P(pvalue), Z_STRLEN_P(pvalue)+1);
3401 			}
3402 			if ((pvalue = zend_hash_str_find(Z_ARRVAL_P(data), "disposition", sizeof("disposition") - 1)) != NULL) {
3403 				if (Z_TYPE_P(pvalue) == IS_ARRAY && !HT_IS_PACKED(Z_ARRVAL_P(pvalue))) {
3404 					disp_param = tmp_param = NULL;
3405 					SEPARATE_ARRAY(pvalue);
3406 					ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(pvalue), key, disp_data) {
3407 						if (key == NULL) continue;
3408 						CHECK_HEADER_INJECTION(key, 0, "body type.parameters key");
3409 						disp_param = mail_newbody_parameter();
3410 						disp_param->attribute = cpystr(ZSTR_VAL(key));
3411 						convert_to_string(disp_data);
3412 						CHECK_HEADER_INJECTION(Z_STR_P(disp_data), 0, "body type.parameters value");
3413 						disp_param->value = (char *) fs_get(Z_STRLEN_P(disp_data) + 1);
3414 						memcpy(disp_param->value, Z_STRVAL_P(disp_data), Z_STRLEN_P(disp_data) + 1);
3415 						disp_param->next = tmp_param;
3416 						tmp_param = disp_param;
3417 					} ZEND_HASH_FOREACH_END();
3418 					bod->disposition.parameter = disp_param;
3419 				}
3420 			}
3421 			if (bod->type == TYPEMESSAGE && bod->subtype && !strcmp(bod->subtype, "RFC822")) {
3422 				bod->nested.msg = mail_newmsg();
3423 			} else {
3424 				if ((pvalue = zend_hash_str_find(Z_ARRVAL_P(data), "contents.data", sizeof("contents.data") - 1)) != NULL) {
3425 					convert_to_string(pvalue);
3426 					bod->contents.text.data = fs_get(Z_STRLEN_P(pvalue) + 1);
3427 					memcpy(bod->contents.text.data, Z_STRVAL_P(pvalue), Z_STRLEN_P(pvalue)+1);
3428 					bod->contents.text.size = Z_STRLEN_P(pvalue);
3429 				} else {
3430 					bod->contents.text.data = fs_get(1);
3431 					memcpy(bod->contents.text.data, "", 1);
3432 					bod->contents.text.size = 0;
3433 				}
3434 			}
3435 			if ((pvalue = zend_hash_str_find(Z_ARRVAL_P(data), "lines", sizeof("lines") - 1)) != NULL) {
3436 				bod->size.lines = zval_get_long(pvalue);
3437 			}
3438 			if ((pvalue = zend_hash_str_find(Z_ARRVAL_P(data), "bytes", sizeof("bytes") - 1)) != NULL) {
3439 				bod->size.bytes = zval_get_long(pvalue);
3440 			}
3441 			if ((pvalue = zend_hash_str_find(Z_ARRVAL_P(data), "md5", sizeof("md5") - 1)) != NULL) {
3442 				convert_to_string(pvalue);
3443 				CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 0, "body md5");
3444 				bod->md5 = cpystr(Z_STRVAL_P(pvalue));
3445 			}
3446 		} else if (Z_TYPE_P(data) == IS_ARRAY && topbod->type == TYPEMULTIPART) {
3447 			short type = 0;
3448 			SEPARATE_ARRAY(data);
3449 			if ((pvalue = zend_hash_str_find(Z_ARRVAL_P(data), "type", sizeof("type") - 1)) != NULL) {
3450 				zend_long tmp_type = zval_get_long(pvalue);
3451 				if (tmp_type >= 0 && tmp_type <= TYPEMAX && tmp_type != TYPEMULTIPART && body_types[tmp_type] != NULL) {
3452 					type = (short) tmp_type;
3453 				}
3454 			}
3455 
3456 			if (!toppart) {
3457 				bod->nested.part = mail_newbody_part();
3458 				mypart = bod->nested.part;
3459 				toppart = 1;
3460 			} else {
3461 				mypart->next = mail_newbody_part();
3462 				mypart = mypart->next;
3463 			}
3464 
3465 			bod = &mypart->body;
3466 			bod->type = type;
3467 
3468 			if ((pvalue = zend_hash_str_find(Z_ARRVAL_P(data), "encoding", sizeof("encoding") - 1)) != NULL) {
3469 				zend_long encoding = zval_get_long(pvalue);
3470 				if (encoding >= 0 && encoding <= ENCMAX && body_encodings[encoding] != NULL) {
3471 					bod->encoding = (short) encoding;
3472 				}
3473 			}
3474 			if ((pvalue = zend_hash_str_find(Z_ARRVAL_P(data), "charset", sizeof("charset") - 1)) != NULL) {
3475 				convert_to_string_ex(pvalue);
3476 				CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 0, "body charset");
3477 				tmp_param = mail_newbody_parameter();
3478 				tmp_param->value = (char *) fs_get(Z_STRLEN_P(pvalue) + 1);
3479 				memcpy(tmp_param->value, Z_STRVAL_P(pvalue), Z_STRLEN_P(pvalue) + 1);
3480 				tmp_param->attribute = cpystr("CHARSET");
3481 				tmp_param->next = bod->parameter;
3482 				bod->parameter = tmp_param;
3483 			}
3484 			if ((pvalue = zend_hash_str_find(Z_ARRVAL_P(data), "type.parameters", sizeof("type.parameters") - 1)) != NULL) {
3485 				if (Z_TYPE_P(pvalue) == IS_ARRAY && !HT_IS_PACKED(Z_ARRVAL_P(pvalue))) {
3486 					disp_param = tmp_param = NULL;
3487 					SEPARATE_ARRAY(pvalue);
3488 					ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(pvalue), key, disp_data) {
3489 						if (key == NULL) continue;
3490 						CHECK_HEADER_INJECTION(key, 0, "body type.parameters key");
3491 						disp_param = mail_newbody_parameter();
3492 						disp_param->attribute = cpystr(ZSTR_VAL(key));
3493 						convert_to_string_ex(disp_data);
3494 						CHECK_HEADER_INJECTION(Z_STR_P(disp_data), 0, "body type.parameters value");
3495 						disp_param->value = (char *)fs_get(Z_STRLEN_P(disp_data) + 1);
3496 						memcpy(disp_param->value, Z_STRVAL_P(disp_data), Z_STRLEN_P(disp_data) + 1);
3497 						disp_param->next = tmp_param;
3498 						tmp_param = disp_param;
3499 					} ZEND_HASH_FOREACH_END();
3500 					bod->parameter = disp_param;
3501 				}
3502 			}
3503 			if ((pvalue = zend_hash_str_find(Z_ARRVAL_P(data), "subtype", sizeof("subtype") - 1)) != NULL) {
3504 				convert_to_string_ex(pvalue);
3505 				CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 0, "body subtype");
3506 				bod->subtype = cpystr(Z_STRVAL_P(pvalue));
3507 			}
3508 			if ((pvalue = zend_hash_str_find(Z_ARRVAL_P(data), "id", sizeof("id") - 1)) != NULL) {
3509 				convert_to_string_ex(pvalue);
3510 				CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 0, "body id");
3511 				bod->id = cpystr(Z_STRVAL_P(pvalue));
3512 			}
3513 			if ((pvalue = zend_hash_str_find(Z_ARRVAL_P(data), "description", sizeof("description") - 1)) != NULL) {
3514 				convert_to_string_ex(pvalue);
3515 				CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 0, "body description");
3516 				bod->description = cpystr(Z_STRVAL_P(pvalue));
3517 			}
3518 			if ((pvalue = zend_hash_str_find(Z_ARRVAL_P(data), "disposition.type", sizeof("disposition.type") - 1)) != NULL) {
3519 				convert_to_string_ex(pvalue);
3520 				CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 0, "body disposition.type");
3521 				bod->disposition.type = (char *) fs_get(Z_STRLEN_P(pvalue) + 1);
3522 				memcpy(bod->disposition.type, Z_STRVAL_P(pvalue), Z_STRLEN_P(pvalue)+1);
3523 			}
3524 			if ((pvalue = zend_hash_str_find(Z_ARRVAL_P(data), "disposition", sizeof("disposition") - 1)) != NULL) {
3525 				if (Z_TYPE_P(pvalue) == IS_ARRAY && !HT_IS_PACKED(Z_ARRVAL_P(pvalue))) {
3526 					disp_param = tmp_param = NULL;
3527 					SEPARATE_ARRAY(pvalue);
3528 					ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(pvalue), key, disp_data) {
3529 						if (key == NULL) continue;
3530 						CHECK_HEADER_INJECTION(key, 0, "body disposition key");
3531 						disp_param = mail_newbody_parameter();
3532 						disp_param->attribute = cpystr(ZSTR_VAL(key));
3533 						convert_to_string_ex(disp_data);
3534 						CHECK_HEADER_INJECTION(Z_STR_P(disp_data), 0, "body disposition value");
3535 						disp_param->value = (char *) fs_get(Z_STRLEN_P(disp_data) + 1);
3536 						memcpy(disp_param->value, Z_STRVAL_P(disp_data), Z_STRLEN_P(disp_data) + 1);
3537 						disp_param->next = tmp_param;
3538 						tmp_param = disp_param;
3539 					} ZEND_HASH_FOREACH_END();
3540 					bod->disposition.parameter = disp_param;
3541 				}
3542 			}
3543 			if (bod->type == TYPEMESSAGE && bod->subtype && !strcmp(bod->subtype, "RFC822")) {
3544 				bod->nested.msg = mail_newmsg();
3545 			} else {
3546 				if ((pvalue = zend_hash_str_find(Z_ARRVAL_P(data), "contents.data", sizeof("contents.data") - 1)) != NULL) {
3547 					convert_to_string(pvalue);
3548 					bod->contents.text.data = fs_get(Z_STRLEN_P(pvalue) + 1);
3549 					memcpy(bod->contents.text.data, Z_STRVAL_P(pvalue), Z_STRLEN_P(pvalue) + 1);
3550 					bod->contents.text.size = Z_STRLEN_P(pvalue);
3551 				} else {
3552 					bod->contents.text.data = fs_get(1);
3553 					memcpy(bod->contents.text.data, "", 1);
3554 					bod->contents.text.size = 0;
3555 				}
3556 			}
3557 			if ((pvalue = zend_hash_str_find(Z_ARRVAL_P(data), "lines", sizeof("lines") - 1)) != NULL) {
3558 				bod->size.lines = zval_get_long(pvalue);
3559 			}
3560 			if ((pvalue = zend_hash_str_find(Z_ARRVAL_P(data), "bytes", sizeof("bytes") - 1)) != NULL) {
3561 				bod->size.bytes = zval_get_long(pvalue);
3562 			}
3563 			if ((pvalue = zend_hash_str_find(Z_ARRVAL_P(data), "md5", sizeof("md5") - 1)) != NULL) {
3564 				convert_to_string_ex(pvalue);
3565 				CHECK_HEADER_INJECTION(Z_STR_P(pvalue), 0, "body md5");
3566 				bod->md5 = cpystr(Z_STRVAL_P(pvalue));
3567 			}
3568 		}
3569 	} ZEND_HASH_FOREACH_END();
3570 
3571 	if (bod && bod->type == TYPEMULTIPART && (!bod->nested.part || !bod->nested.part->next)) {
3572 		php_error_docref(NULL, E_WARNING, "Cannot generate multipart e-mail without components.");
3573 		RETVAL_FALSE;
3574 		goto done;
3575 	}
3576 
3577 	rfc822_encode_body_7bit(env, topbod);
3578 
3579 	tmp = emalloc(SENDBUFLEN + 1);
3580 
3581 	rfc822_header(tmp, env, topbod);
3582 
3583 	/* add custom envelope headers */
3584 	if (custom_headers_param) {
3585 		int l = strlen(tmp) - 2, l2;
3586 		PARAMETER *tp = custom_headers_param;
3587 
3588 		/* remove last CRLF from tmp */
3589 		tmp[l] = '\0';
3590 		tempstring = emalloc(l);
3591 		memcpy(tempstring, tmp, l);
3592 
3593 		do {
3594 			l2 = strlen(custom_headers_param->value);
3595 			tempstring = erealloc(tempstring, l + l2 + CRLF_LEN + 1);
3596 			memcpy(tempstring + l, custom_headers_param->value, l2);
3597 			memcpy(tempstring + l + l2, CRLF, CRLF_LEN);
3598 			l += l2 + CRLF_LEN;
3599 		} while ((custom_headers_param = custom_headers_param->next));
3600 
3601 		mail_free_body_parameter(&tp);
3602 
3603 		mystring = emalloc(l + CRLF_LEN + 1);
3604 		memcpy(mystring, tempstring, l);
3605 		memcpy(mystring + l , CRLF, CRLF_LEN);
3606 		mystring[l + CRLF_LEN] = '\0';
3607 
3608 		efree(tempstring);
3609 	} else {
3610 		mystring = estrdup(tmp);
3611 	}
3612 
3613 	bod = topbod;
3614 
3615 	if (bod && bod->type == TYPEMULTIPART) {
3616 
3617 		/* first body part */
3618 			part = bod->nested.part;
3619 
3620 		/* find cookie */
3621 			for (param = bod->parameter; param && !cookie; param = param->next) {
3622 				if (!strcmp (param->attribute, "BOUNDARY")) {
3623 					cookie = param->value;
3624 				}
3625 			}
3626 
3627 		/* yucky default */
3628 			if (!cookie) {
3629 				cookie = "-";
3630 			} else if (strlen(cookie) > (SENDBUFLEN - 2 - 2 - 2)) { /* validate cookie length -- + CRLF * 2 */
3631 				php_error_docref(NULL, E_WARNING, "The boundary should be no longer than 4kb");
3632 				RETVAL_FALSE;
3633 				goto done;
3634 			}
3635 
3636 		/* for each part */
3637 			do {
3638 				t = tmp;
3639 
3640 			/* append mini-header */
3641 				*t = '\0';
3642 				rfc822_write_body_header(&t, &part->body);
3643 
3644 			/* output cookie, mini-header, and contents */
3645 				spprintf(&tempstring, 0, "%s--%s%s%s%s", mystring, cookie, CRLF, tmp, CRLF);
3646 				efree(mystring);
3647 				mystring=tempstring;
3648 
3649 				bod=&part->body;
3650 
3651 				spprintf(&tempstring, 0, "%s%s%s", mystring, bod->contents.text.data ? (char *) bod->contents.text.data : "", CRLF);
3652 				efree(mystring);
3653 				mystring=tempstring;
3654 			} while ((part = part->next)); /* until done */
3655 
3656 			/* output trailing cookie */
3657 			spprintf(&tempstring, 0, "%s--%s--%s", mystring, cookie, CRLF);
3658 			efree(mystring);
3659 			mystring=tempstring;
3660 	} else if (bod) {
3661 		spprintf(&tempstring, 0, "%s%s%s", mystring, bod->contents.text.data ? (char *) bod->contents.text.data : "", CRLF);
3662 		efree(mystring);
3663 		mystring=tempstring;
3664 	} else {
3665 		efree(mystring);
3666 		RETVAL_FALSE;
3667 		goto done;
3668 	}
3669 
3670 	RETVAL_STRING(tempstring);
3671 	efree(tempstring);
3672 done:
3673 	if (tmp) {
3674 		efree(tmp);
3675 	}
3676 	mail_free_body(&topbod);
3677 	mail_free_envelope(&env);
3678 }
3679 /* }}} */
3680 
3681 /* {{{ _php_imap_mail */
_php_imap_mail(zend_string * to,zend_string * subject,zend_string * message,zend_string * headers,zend_string * cc,zend_string * bcc,zend_string * rpath)3682 bool _php_imap_mail(zend_string *to, zend_string *subject, zend_string *message, zend_string *headers,
3683 	zend_string *cc, zend_string *bcc, zend_string* rpath)
3684 {
3685 #ifdef PHP_WIN32
3686 	int tsm_err;
3687 #else
3688 	FILE *sendmail;
3689 	int ret;
3690 #endif
3691 
3692 	ZEND_ASSERT(to && ZSTR_LEN(to) != 0);
3693 	ZEND_ASSERT(subject && ZSTR_LEN(subject) != 0);
3694 	ZEND_ASSERT(message);
3695 
3696 #ifdef PHP_WIN32
3697 	char *tempMailTo;
3698 	char *tsm_errmsg = NULL;
3699 	ADDRESS *addr;
3700 	char *bufferTo = NULL, *bufferCc = NULL, *bufferBcc = NULL, *bufferHeader = NULL;
3701 	size_t offset, bufferLen = 0;
3702 	size_t bt_len;
3703 
3704 	/* Add "To" field's necessary buffer length */
3705 	bufferLen += ZSTR_LEN(to) + 6;
3706 	if (headers) {
3707 		bufferLen += ZSTR_LEN(headers);
3708 	}
3709 	if (cc) {
3710 		bufferLen += ZSTR_LEN(cc) + 6;
3711 	}
3712 
3713 #define PHP_IMAP_CLEAN	if (bufferTo) efree(bufferTo); if (bufferCc) efree(bufferCc); if (bufferBcc) efree(bufferBcc); if (bufferHeader) efree(bufferHeader);
3714 #define PHP_IMAP_BAD_DEST PHP_IMAP_CLEAN; efree(tempMailTo); return (BAD_MSG_DESTINATION);
3715 
3716 	bufferHeader = (char *)safe_emalloc(bufferLen, 1, 1);
3717 	memset(bufferHeader, 0, bufferLen);
3718 
3719 	/* Handle "To" Field */
3720 	strlcat(bufferHeader, "To: ", bufferLen + 1);
3721 	strlcat(bufferHeader, ZSTR_VAL(to), bufferLen + 1);
3722 	strlcat(bufferHeader, "\r\n", bufferLen + 1);
3723 	tempMailTo = estrdup(ZSTR_VAL(to));
3724 	bt_len = ZSTR_LEN(to);
3725 	bufferTo = (char *)safe_emalloc(bt_len, 1, 1);
3726 	bt_len++;
3727 	offset = 0;
3728 	addr = NULL;
3729 	rfc822_parse_adrlist(&addr, tempMailTo, "NO HOST");
3730 	while (addr) {
3731 		if (addr->host == NULL || strcmp(addr->host, ERRHOST) == 0) {
3732 			PHP_IMAP_BAD_DEST;
3733 		} else {
3734 			bufferTo = safe_erealloc(bufferTo, bt_len, 1, strlen(addr->mailbox));
3735 			bt_len += strlen(addr->mailbox);
3736 			bufferTo = safe_erealloc(bufferTo, bt_len, 1, strlen(addr->host));
3737 			bt_len += strlen(addr->host);
3738 			offset += slprintf(bufferTo + offset, bt_len - offset, "%s@%s,", addr->mailbox, addr->host);
3739 		}
3740 		addr = addr->next;
3741 	}
3742 	efree(tempMailTo);
3743 	if (offset>0) {
3744 		bufferTo[offset-1] = 0;
3745 	}
3746 
3747 	if (cc && ZSTR_LEN(cc) != 0) {
3748 		strlcat(bufferHeader, "Cc: ", bufferLen + 1);
3749 		strlcat(bufferHeader, ZSTR_VAL(cc), bufferLen + 1);
3750 		strlcat(bufferHeader, "\r\n", bufferLen + 1);
3751 		tempMailTo = estrdup(ZSTR_VAL(cc));
3752 		bt_len = ZSTR_LEN(cc);
3753 		bufferCc = (char *)safe_emalloc(bt_len, 1, 1);
3754 		bt_len++;
3755 		offset = 0;
3756 		addr = NULL;
3757 		rfc822_parse_adrlist(&addr, tempMailTo, "NO HOST");
3758 		while (addr) {
3759 			if (addr->host == NULL || strcmp(addr->host, ERRHOST) == 0) {
3760 				PHP_IMAP_BAD_DEST;
3761 			} else {
3762 				bufferCc = safe_erealloc(bufferCc, bt_len, 1, strlen(addr->mailbox));
3763 				bt_len += strlen(addr->mailbox);
3764 				bufferCc = safe_erealloc(bufferCc, bt_len, 1, strlen(addr->host));
3765 				bt_len += strlen(addr->host);
3766 				offset += slprintf(bufferCc + offset, bt_len - offset, "%s@%s,", addr->mailbox, addr->host);
3767 			}
3768 			addr = addr->next;
3769 		}
3770 		efree(tempMailTo);
3771 		if (offset>0) {
3772 			bufferCc[offset-1] = 0;
3773 		}
3774 	}
3775 
3776 	if (bcc && ZSTR_LEN(bcc)) {
3777 		tempMailTo = estrdup(ZSTR_VAL(bcc));
3778 		bt_len = ZSTR_LEN(bcc);
3779 		bufferBcc = (char *)safe_emalloc(bt_len, 1, 1);
3780 		bt_len++;
3781 		offset = 0;
3782 		addr = NULL;
3783 		rfc822_parse_adrlist(&addr, tempMailTo, "NO HOST");
3784 		while (addr) {
3785 			if (addr->host == NULL || strcmp(addr->host, ERRHOST) == 0) {
3786 				PHP_IMAP_BAD_DEST;
3787 			} else {
3788 				bufferBcc = safe_erealloc(bufferBcc, bt_len, 1, strlen(addr->mailbox));
3789 				bt_len += strlen(addr->mailbox);
3790 				bufferBcc = safe_erealloc(bufferBcc, bt_len, 1, strlen(addr->host));
3791 				bt_len += strlen(addr->host);
3792 				offset += slprintf(bufferBcc + offset, bt_len - offset, "%s@%s,", addr->mailbox, addr->host);
3793 			}
3794 			addr = addr->next;
3795 		}
3796 		efree(tempMailTo);
3797 		if (offset>0) {
3798 			bufferBcc[offset-1] = 0;
3799 		}
3800 	}
3801 
3802 	if (headers && ZSTR_LEN(headers)) {
3803 		strlcat(bufferHeader, ZSTR_VAL(headers), bufferLen + 1);
3804 	}
3805 
3806 	if (TSendMail(INI_STR("SMTP"), &tsm_err, &tsm_errmsg, bufferHeader, ZSTR_VAL(subject),
3807 			bufferTo, ZSTR_VAL(message), bufferCc, bufferBcc, rpath ? ZSTR_VAL(rpath) : NULL) != SUCCESS) {
3808 		if (tsm_errmsg) {
3809 			php_error_docref(NULL, E_WARNING, "%s", tsm_errmsg);
3810 			efree(tsm_errmsg);
3811 		} else {
3812 			php_error_docref(NULL, E_WARNING, "%s", GetSMErrorText(tsm_err));
3813 		}
3814 		PHP_IMAP_CLEAN;
3815 		return 0;
3816 	}
3817 	PHP_IMAP_CLEAN;
3818 #else
3819 	if (!INI_STR("sendmail_path")) {
3820 		return 0;
3821 	}
3822 	sendmail = popen(INI_STR("sendmail_path"), "w");
3823 	if (sendmail) {
3824 		if (rpath && ZSTR_LEN(rpath) != 0) {
3825 			fprintf(sendmail, "From: %s\n", ZSTR_VAL(rpath));
3826 		}
3827 		/* to cannot be a null pointer, asserted earlier on */
3828 		fprintf(sendmail, "To: %s\n", ZSTR_VAL(to));
3829 		if (cc && ZSTR_LEN(cc) != 0) {
3830 			fprintf(sendmail, "Cc: %s\n", ZSTR_VAL(cc));
3831 		}
3832 		if (bcc && ZSTR_LEN(bcc) != 0) {
3833 			fprintf(sendmail, "Bcc: %s\n", ZSTR_VAL(bcc));
3834 		}
3835 		/* subject cannot be a null pointer, asserted earlier on */
3836 		fprintf(sendmail, "Subject: %s\n", ZSTR_VAL(subject));
3837 		if (headers && ZSTR_LEN(headers) != 0) {
3838 			fprintf(sendmail, "%s\n", ZSTR_VAL(headers));
3839 		}
3840 		/* message cannot be a null pointer, asserted earlier on */
3841 		fprintf(sendmail, "\n%s\n", ZSTR_VAL(message));
3842 		ret = pclose(sendmail);
3843 
3844 		return ret != -1;
3845 	} else {
3846 		php_error_docref(NULL, E_WARNING, "Could not execute mail delivery program");
3847 		return 0;
3848 	}
3849 #endif
3850 	return 1;
3851 }
3852 /* }}} */
3853 
3854 /* {{{ Send an email message */
PHP_FUNCTION(imap_mail)3855 PHP_FUNCTION(imap_mail)
3856 {
3857 	zend_string *to=NULL, *message=NULL, *headers=NULL, *subject=NULL, *cc=NULL, *bcc=NULL, *rpath=NULL;
3858 
3859 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "PPP|P!P!P!P!", &to, &subject, &message,
3860 		&headers, &cc, &bcc, &rpath) == FAILURE) {
3861 		RETURN_THROWS();
3862 	}
3863 
3864 	/* To: */
3865 	if (ZSTR_LEN(to) == 0) {
3866 		zend_argument_value_error(1, "cannot be empty");
3867 		RETURN_THROWS();
3868 	}
3869 
3870 	/* Subject: */
3871 	if (ZSTR_LEN(subject) == 0) {
3872 		zend_argument_value_error(2, "cannot be empty");
3873 		RETURN_THROWS();
3874 	}
3875 
3876 	/* message body */
3877 	if (ZSTR_LEN(message) == 0) {
3878 		/* this is not really an error, so it is allowed. */
3879 		// TODO Drop warning or emit ValueError?
3880 		php_error_docref(NULL, E_WARNING, "No message string in mail command");
3881 	}
3882 
3883 	if (_php_imap_mail(to, subject, message, headers, cc, bcc, rpath)) {
3884 		RETURN_TRUE;
3885 	} else {
3886 		RETURN_FALSE;
3887 	}
3888 }
3889 /* }}} */
3890 
3891 /* {{{ Return a list of messages matching the given criteria */
PHP_FUNCTION(imap_search)3892 PHP_FUNCTION(imap_search)
3893 {
3894 	zval *imap_conn_obj;
3895 	zend_string *criteria, *charset = NULL;
3896 	zend_long flags = SE_FREE;
3897 	php_imap_object *imap_conn_struct;
3898 	char *search_criteria;
3899 	MESSAGELIST *cur;
3900 	SEARCHPGM *pgm = NIL;
3901 
3902 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "OS|lS", &imap_conn_obj, php_imap_ce, &criteria, &flags, &charset) == FAILURE) {
3903 		RETURN_THROWS();
3904 	}
3905 
3906 	GET_IMAP_STREAM(imap_conn_struct, imap_conn_obj);
3907 
3908 	/* TODO Update docs to allow SE_FREE as an option */
3909 	if (flags && ((flags & ~(SE_FREE | SE_UID)) != 0)) {
3910 		zend_argument_value_error(3, "must be a bitmask of SE_FREE, and SE_UID");
3911 		RETURN_THROWS();
3912 	}
3913 
3914 	search_criteria = estrndup(ZSTR_VAL(criteria), ZSTR_LEN(criteria));
3915 
3916 	IMAPG(imap_messages) = IMAPG(imap_messages_tail) = NIL;
3917 	pgm = mail_criteria(search_criteria);
3918 
3919 	mail_search_full(imap_conn_struct->imap_stream, (charset ? ZSTR_VAL(charset) : NIL), pgm, flags);
3920 
3921 	if (pgm && !(flags & SE_FREE)) {
3922 		mail_free_searchpgm(&pgm);
3923 	}
3924 
3925 	if (IMAPG(imap_messages) == NIL) {
3926 		efree(search_criteria);
3927 		RETURN_FALSE;
3928 	}
3929 
3930 	array_init(return_value);
3931 
3932 	cur = IMAPG(imap_messages);
3933 	while (cur != NIL) {
3934 		add_next_index_long(return_value, cur->msgid);
3935 		cur = cur->next;
3936 	}
3937 	mail_free_messagelist(&IMAPG(imap_messages), &IMAPG(imap_messages_tail));
3938 	efree(search_criteria);
3939 }
3940 /* }}} */
3941 
3942 /* {{{ Returns an array of all IMAP alerts that have been generated since the last page load or since the last imap_alerts() call, whichever came last. The alert stack is cleared after imap_alerts() is called. */
3943 /* Author: CJH */
PHP_FUNCTION(imap_alerts)3944 PHP_FUNCTION(imap_alerts)
3945 {
3946 	STRINGLIST *cur=NIL;
3947 
3948 	if (zend_parse_parameters_none() == FAILURE) {
3949 		RETURN_THROWS();
3950 	}
3951 
3952 	if (IMAPG(imap_alertstack) == NIL) {
3953 		RETURN_FALSE;
3954 	}
3955 
3956 	array_init(return_value);
3957 
3958 	cur = IMAPG(imap_alertstack);
3959 	while (cur != NIL) {
3960 		add_next_index_string(return_value, (char*)cur->LTEXT);
3961 		cur = cur->next;
3962 	}
3963 	mail_free_stringlist(&IMAPG(imap_alertstack));
3964 	IMAPG(imap_alertstack) = NIL;
3965 }
3966 /* }}} */
3967 
3968 /* {{{ Returns an array of all IMAP errors generated since the last page load, or since the last imap_errors() call, whichever came last. The error stack is cleared after imap_errors() is called. */
3969 /* Author: CJH */
PHP_FUNCTION(imap_errors)3970 PHP_FUNCTION(imap_errors)
3971 {
3972 	ERRORLIST *cur=NIL;
3973 
3974 	if (zend_parse_parameters_none() == FAILURE) {
3975 		RETURN_THROWS();
3976 	}
3977 
3978 	if (IMAPG(imap_errorstack) == NIL) {
3979 		RETURN_FALSE;
3980 	}
3981 
3982 	array_init(return_value);
3983 
3984 	cur = IMAPG(imap_errorstack);
3985 	while (cur != NIL) {
3986 		add_next_index_string(return_value, (char*)cur->LTEXT);
3987 		cur = cur->next;
3988 	}
3989 	mail_free_errorlist(&IMAPG(imap_errorstack));
3990 	IMAPG(imap_errorstack) = NIL;
3991 }
3992 /* }}} */
3993 
3994 /* {{{ Returns the last error that was generated by an IMAP function. The error stack is NOT cleared after this call. */
3995 /* Author: CJH */
PHP_FUNCTION(imap_last_error)3996 PHP_FUNCTION(imap_last_error)
3997 {
3998 	ERRORLIST *cur=NIL;
3999 
4000 	if (zend_parse_parameters_none() == FAILURE) {
4001 		RETURN_THROWS();
4002 	}
4003 
4004 	if (IMAPG(imap_errorstack) == NIL) {
4005 		RETURN_FALSE;
4006 	}
4007 
4008 	cur = IMAPG(imap_errorstack);
4009 	while (cur != NIL) {
4010 		if (cur->next == NIL) {
4011 			RETURN_STRING((char*)cur->LTEXT);
4012 		}
4013 		cur = cur->next;
4014 	}
4015 }
4016 /* }}} */
4017 
php_imap_populate_mime_header_object(zval * z_object,const char * charset,const char * content)4018 static void php_imap_populate_mime_header_object(zval *z_object, const char *charset, const char *content)
4019 {
4020 	ZEND_ASSERT(z_object && "Object must be initialized");
4021 	zend_update_property_string(
4022 		Z_OBJCE_P(z_object), Z_OBJ_P(z_object),
4023 		"charset", strlen("charset"),
4024 		charset
4025 	);
4026 	zend_update_property_string(
4027 		Z_OBJCE_P(z_object), Z_OBJ_P(z_object),
4028 		"text", strlen("text"),
4029 		content
4030 	);
4031 }
4032 
4033 /* {{{ Decode mime header element in accordance with RFC 2047 and return array of objects containing 'charset' encoding and decoded 'text' */
PHP_FUNCTION(imap_mime_header_decode)4034 PHP_FUNCTION(imap_mime_header_decode)
4035 {
4036 	/* Author: Ted Parnefors <ted@mtv.se> */
4037 	zval myobject;
4038 	zend_string *str;
4039 	char *string, *charset, encoding, *text, *decode;
4040 	zend_long charset_token, encoding_token, end_token, end, offset=0, i;
4041 	unsigned long newlength;
4042 
4043 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &str) == FAILURE) {
4044 		RETURN_THROWS();
4045 	}
4046 
4047 	array_init(return_value);
4048 
4049 	string = ZSTR_VAL(str);
4050 	end = ZSTR_LEN(str);
4051 
4052 	charset = (char *) safe_emalloc((end + 1), 2, 0);
4053 	text = &charset[end + 1];
4054 	while (offset < end) {	/* Reached end of the string? */
4055 		if ((charset_token = (zend_long)php_memnstr(&string[offset], "=?", 2, string + end))) {	/* Is there anything encoded in the string? */
4056 			charset_token -= (zend_long)string;
4057 			if (offset != charset_token) {	/* Is there anything before the encoded data? */
4058 				/* Retrieve unencoded data that is found before encoded data */
4059 				memcpy(text, &string[offset], charset_token-offset);
4060 				text[charset_token - offset] = 0x00;
4061 				object_init(&myobject);
4062 				php_imap_populate_mime_header_object(&myobject, "default", text);
4063 				zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &myobject);
4064 			}
4065 			if ((encoding_token = (zend_long)php_memnstr(&string[charset_token+2], "?", 1, string+end))) {		/* Find token for encoding */
4066 				encoding_token -= (zend_long)string;
4067 				if ((end_token = (zend_long)php_memnstr(&string[encoding_token+3], "?=", 2, string+end))) {	/* Find token for end of encoded data */
4068 					end_token -= (zend_long)string;
4069 					memcpy(charset, &string[charset_token + 2], encoding_token - (charset_token + 2));	/* Extract charset encoding */
4070 					charset[encoding_token-(charset_token + 2)] = 0x00;
4071 					encoding=string[encoding_token + 1];	/* Extract encoding from string */
4072 					memcpy(text, &string[encoding_token + 3], end_token - (encoding_token + 3));	/* Extract text */
4073 					text[end_token - (encoding_token + 3)] = 0x00;
4074 					decode = text;
4075 					if (encoding == 'q' || encoding == 'Q') {	/* Decode 'q' encoded data */
4076 						for(i=0; text[i] != 0x00; i++) if (text[i] == '_') text[i] = ' ';	/* Replace all *_' with space. */
4077 						decode = (char *)rfc822_qprint((unsigned char *) text, strlen(text), &newlength);
4078 					} else if (encoding == 'b' || encoding == 'B') {
4079 						decode = (char *)rfc822_base64((unsigned char *) text, strlen(text), &newlength); /* Decode 'B' encoded data */
4080 					}
4081 					if (decode == NULL) {
4082 						efree(charset);
4083 						zend_array_destroy(Z_ARR_P(return_value));
4084 						RETURN_FALSE;
4085 					}
4086 					object_init(&myobject);
4087 					php_imap_populate_mime_header_object(&myobject, charset, decode);
4088 					zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &myobject);
4089 
4090 					/* only free decode if it was allocated by rfc822_qprint or rfc822_base64 */
4091 					if (decode != text) {
4092 						fs_give((void**)&decode);
4093 					}
4094 
4095 					offset = end_token+2;
4096 					for (i = 0; (string[offset + i] == ' ') || (string[offset + i] == 0x0a) || (string[offset + i] == 0x0d) || (string[offset + i] == '\t'); i++);
4097 					if ((string[offset + i] == '=') && (string[offset + i + 1] == '?') && (offset + i < end)) {
4098 						offset += i;
4099 					}
4100 					continue;	/*/ Iterate the loop again please. */
4101 				}
4102 			}
4103 		} else {
4104 			/* Just some tweaking to optimize the code, and get the end statements work in a general manner.
4105 			 * If we end up here we didn't find a position for "charset_token",
4106 			 * so we need to set it to the start of the yet unextracted data.
4107 			 */
4108 			charset_token = offset;
4109 		}
4110 		/* Return the rest of the data as unencoded, as it was either unencoded or was missing separators
4111 		   which rendered the remainder of the string impossible for us to decode. */
4112 		memcpy(text, &string[charset_token], end - charset_token);	/* Extract unencoded text from string */
4113 		text[end - charset_token] = 0x00;
4114 		object_init(&myobject);
4115 		php_imap_populate_mime_header_object(&myobject, "default", text);
4116 		zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &myobject);
4117 
4118 		offset = end;	/* We have reached the end of the string. */
4119 	}
4120 	efree(charset);
4121 }
4122 /* }}} */
4123 
4124 /* Support Functions */
4125 
4126 #ifdef HAVE_RFC822_OUTPUT_ADDRESS_LIST
4127 /* {{{ _php_rfc822_soutr */
_php_rfc822_soutr(void * stream,char * string)4128 static long _php_rfc822_soutr (void *stream, char *string)
4129 {
4130 	smart_str *ret = (smart_str*)stream;
4131 	size_t len = strlen(string);
4132 
4133 	smart_str_appendl(ret, string, len);
4134 	return LONGT;
4135 }
4136 /* }}} */
4137 
4138 /* {{{ _php_rfc822_write_address */
_php_rfc822_write_address(ADDRESS * addresslist)4139 static zend_string* _php_rfc822_write_address(ADDRESS *addresslist)
4140 {
4141 	char address[MAILTMPLEN];
4142 	smart_str ret = {0};
4143 	RFC822BUFFER buf;
4144 
4145 	buf.beg = address;
4146 	buf.cur = buf.beg;
4147 	buf.end = buf.beg + sizeof(address) - 1;
4148 	buf.s = &ret;
4149 	buf.f = _php_rfc822_soutr;
4150 	rfc822_output_address_list(&buf, addresslist, 0, NULL);
4151 	rfc822_output_flush(&buf);
4152 	smart_str_0(&ret);
4153 	return ret.s;
4154 }
4155 /* }}} */
4156 
4157 #else
4158 
4159 /* Calculate string length based on imap's rfc822_cat function. */
_php_rfc822_len(const char * const str)4160 static size_t _php_rfc822_len(const char *const str)
4161 {
4162 	/* Non existent or empty string */
4163 	if (!str || !*str) {
4164 		return 0;
4165 	}
4166 
4167 	/* strings with special characters will need to be quoted, as a safety measure we
4168 	 * add 2 bytes for the quotes just in case.
4169 	 */
4170 	size_t len = strlen(str) + 2;
4171 
4172 	/* rfc822_cat() will escape all " and \ characters, therefore we need to increase
4173 	 * our buffer length to account for these characters.
4174 	 */
4175 	const char *p = str;
4176 	while ((p = strpbrk(p, "\\\""))) {
4177 		p++;
4178 		len++;
4179 	}
4180 
4181 	return len;
4182 }
4183 
_php_imap_address_size(const ADDRESS * const address_list)4184 static size_t _php_imap_address_size(const ADDRESS *const address_list)
4185 {
4186 	size_t total_size = 0;
4187 	unsigned int nb_addresses = 0;
4188 	const ADDRESS *current_address = address_list;
4189 
4190 	if (current_address) do {
4191 		total_size += _php_rfc822_len(current_address->personal);
4192 		total_size += _php_rfc822_len(current_address->adl);
4193 		total_size += _php_rfc822_len(current_address->mailbox);
4194 		total_size += _php_rfc822_len(current_address->host);
4195 		nb_addresses++;
4196 	} while ((current_address = current_address->next));
4197 
4198 	/*
4199 	 * rfc822_write_address_full() needs some extra space for '<>,', etc.
4200 	 * for this purpose we allocate additional PHP_IMAP_ADDRESS_SIZE_BUF bytes
4201 	 * by default this buffer is 10 bytes long
4202 	 */
4203 	total_size += nb_addresses * PHP_IMAP_ADDRESS_SIZE_BUF;
4204 
4205 	return total_size;
4206 }
4207 
4208 /* {{{ _php_rfc822_write_address */
_php_rfc822_write_address(ADDRESS * addresslist)4209 static zend_string* _php_rfc822_write_address(ADDRESS *addresslist)
4210 {
4211 	char address[SENDBUFLEN];
4212 
4213 	if (_php_imap_address_size(addresslist) >= SENDBUFLEN) {
4214 		zend_throw_error(NULL, "Address buffer overflow");
4215 		return NULL;
4216 	}
4217 	address[0] = 0;
4218 	rfc822_write_address(address, addresslist);
4219 	return zend_string_init(address, strlen(address), 0);
4220 }
4221 /* }}} */
4222 #endif
4223 /* {{{ _php_imap_parse_address */
_php_imap_parse_address(ADDRESS * address_list,zval * paddress)4224 static zend_string* _php_imap_parse_address (ADDRESS *address_list, zval *paddress)
4225 {
4226 	zend_string *fulladdress;
4227 	ADDRESS *addresstmp;
4228 
4229 	addresstmp = address_list;
4230 
4231 	fulladdress = _php_rfc822_write_address(addresstmp);
4232 
4233 	php_imap_construct_list_of_addresses(Z_ARR_P(paddress), address_list);
4234 	return fulladdress;
4235 }
4236 /* }}} */
4237 
php_imap_update_property_with_parsed_full_address(zval * z_object,ADDRESS * address,const char * str_address_property,size_t str_address_property_len,const char * parts_address_property,size_t parts_address_property_len)4238 static void php_imap_update_property_with_parsed_full_address(
4239 	zval *z_object, ADDRESS *address,
4240 	const char *str_address_property, size_t str_address_property_len,
4241 	const char *parts_address_property, size_t parts_address_property_len
4242 )
4243 {
4244 	zval address_parts;
4245 	zend_string *full_address = NULL;
4246 
4247 	array_init(&address_parts);
4248 	full_address = _php_imap_parse_address(address, &address_parts);
4249 	if (full_address) {
4250 		zend_update_property_str(
4251 			Z_OBJCE_P(z_object), Z_OBJ_P(z_object),
4252 			str_address_property, str_address_property_len,
4253 			full_address
4254 		);
4255 		zend_string_release_ex(full_address, false);
4256 	}
4257 	zend_update_property(
4258 		Z_OBJCE_P(z_object), Z_OBJ_P(z_object),
4259 		parts_address_property, parts_address_property_len,
4260 		&address_parts
4261 	);
4262 	zval_ptr_dtor(&address_parts);
4263 }
4264 
4265 #define UPDATE_PROPERTY_PARSED_ADDRESS(z_object, name, envelope) if ((envelope)->name) { \
4266 	php_imap_update_property_with_parsed_full_address( \
4267 		(z_object), (envelope)->name, \
4268 		#name "address", strlen(#name "address"), \
4269 		#name, strlen(#name) \
4270 	); }
4271 
4272 /* {{{ _php_make_header_object */
_php_make_header_object(zval * myzvalue,ENVELOPE * en)4273 static void _php_make_header_object(zval *myzvalue, ENVELOPE *en)
4274 {
4275 	object_init(myzvalue);
4276 
4277 	if (en->remail) {
4278 		zend_update_property_string(
4279 			Z_OBJCE_P(myzvalue), Z_OBJ_P(myzvalue),
4280 			"remail", strlen("remail"),
4281 			en->remail
4282 		);
4283 	}
4284 	if (en->date) {
4285 		zend_update_property_string(
4286 			Z_OBJCE_P(myzvalue), Z_OBJ_P(myzvalue),
4287 			"date", strlen("date"),
4288 			(char*)en->date
4289 		);
4290 		zend_update_property_string(
4291 			Z_OBJCE_P(myzvalue), Z_OBJ_P(myzvalue),
4292 			"Date", strlen("Date"),
4293 			(char*)en->date
4294 		);
4295 	}
4296 	if (en->subject) {
4297 		zend_update_property_string(
4298 			Z_OBJCE_P(myzvalue), Z_OBJ_P(myzvalue),
4299 			"subject", strlen("subject"),
4300 			en->subject
4301 		);
4302 		zend_update_property_string(
4303 			Z_OBJCE_P(myzvalue), Z_OBJ_P(myzvalue),
4304 			"Subject", strlen("Subject"),
4305 			en->subject
4306 		);
4307 	}
4308 	if (en->in_reply_to) {
4309 		zend_update_property_string(
4310 			Z_OBJCE_P(myzvalue), Z_OBJ_P(myzvalue),
4311 			"in_reply_to", strlen("in_reply_to"),
4312 			en->in_reply_to
4313 		);
4314 	}
4315 	if (en->message_id) {
4316 		zend_update_property_string(
4317 			Z_OBJCE_P(myzvalue), Z_OBJ_P(myzvalue),
4318 			"message_id", strlen("message_id"),
4319 			en->message_id
4320 		);
4321 	}
4322 	if (en->newsgroups) {
4323 		zend_update_property_string(
4324 			Z_OBJCE_P(myzvalue), Z_OBJ_P(myzvalue),
4325 			"newsgroups", strlen("newsgroups"),
4326 			en->newsgroups
4327 		);
4328 	}
4329 	if (en->followup_to) {
4330 		zend_update_property_string(
4331 			Z_OBJCE_P(myzvalue), Z_OBJ_P(myzvalue),
4332 			"followup_to", strlen("followup_to"),
4333 			en->followup_to
4334 		);
4335 	}
4336 	if (en->references) {
4337 		zend_update_property_string(
4338 			Z_OBJCE_P(myzvalue), Z_OBJ_P(myzvalue),
4339 			"references", strlen("references"),
4340 			en->references
4341 		);
4342 	}
4343 
4344 	UPDATE_PROPERTY_PARSED_ADDRESS(myzvalue, to, en);
4345 	UPDATE_PROPERTY_PARSED_ADDRESS(myzvalue, from, en);
4346 	UPDATE_PROPERTY_PARSED_ADDRESS(myzvalue, cc, en);
4347 	UPDATE_PROPERTY_PARSED_ADDRESS(myzvalue, bcc, en);
4348 	UPDATE_PROPERTY_PARSED_ADDRESS(myzvalue, reply_to, en);
4349 	UPDATE_PROPERTY_PARSED_ADDRESS(myzvalue, sender, en);
4350 	UPDATE_PROPERTY_PARSED_ADDRESS(myzvalue, return_path, en);
4351 }
4352 /* }}} */
4353 
4354 /* {{{ _php_imap_add_body */
_php_imap_add_body(zval * arg,const BODY * body)4355 void _php_imap_add_body(zval *arg, const BODY *body)
4356 {
4357 	php_imap_populate_body_struct_object(arg, body);
4358 
4359 	/* multipart message ? */
4360 	if (body->type == TYPEMULTIPART) {
4361 		zval z_content_part_list;
4362 
4363 		array_init(&z_content_part_list);
4364 		for (const PART *content_part = body->CONTENT_PART; content_part; content_part = content_part->next) {
4365 			zval z_content_part;
4366 			object_init(&z_content_part);
4367 			_php_imap_add_body(&z_content_part, &content_part->body);
4368 			zend_hash_next_index_insert_new(Z_ARR(z_content_part_list), &z_content_part);
4369 		}
4370 
4371 		zend_update_property(
4372 			Z_OBJCE_P(arg), Z_OBJ_P(arg),
4373 			"parts", strlen("parts"),
4374 			&z_content_part_list
4375 		);
4376 		zval_ptr_dtor(&z_content_part_list);
4377 	}
4378 
4379 	/* encapsulated message ? */
4380 	if ((body->type == TYPEMESSAGE) && (!strcasecmp(body->subtype, "rfc822"))) {
4381 		zval message_list, message;
4382 
4383 		const BODY *message_body = body->CONTENT_MSG_BODY;
4384 		array_init(&message_list);
4385 		object_init(&message);
4386 		_php_imap_add_body(&message, message_body);
4387 		zend_hash_next_index_insert_new(Z_ARR(message_list), &message);
4388 
4389 		zend_update_property(
4390 			Z_OBJCE_P(arg), Z_OBJ_P(arg),
4391 			"parts", strlen("parts"),
4392 			&message_list
4393 		);
4394 		zval_ptr_dtor(&message_list);
4395 	}
4396 }
4397 /* }}} */
4398 
4399 /* imap_thread, stealing this from header cclient -rjs3 */
4400 /* {{{ build_thread_tree_helper */
build_thread_tree_helper(THREADNODE * cur,zval * tree,long * numNodes,char * buf)4401 static void build_thread_tree_helper(THREADNODE *cur, zval *tree, long *numNodes, char *buf)
4402 {
4403 	unsigned long thisNode = *numNodes;
4404 
4405 	/* define "#.num" */
4406 	snprintf(buf, 25, "%ld.num", thisNode);
4407 
4408 	add_assoc_long(tree, buf, cur->num);
4409 
4410 	snprintf(buf, 25, "%ld.next", thisNode);
4411 	if(cur->next) {
4412 		(*numNodes)++;
4413 		add_assoc_long(tree, buf, *numNodes);
4414 		build_thread_tree_helper(cur->next, tree, numNodes, buf);
4415 	} else { /* "null pointer" */
4416 		add_assoc_long(tree, buf, 0);
4417 	}
4418 
4419 	snprintf(buf, 25, "%ld.branch", thisNode);
4420 	if(cur->branch) {
4421 		(*numNodes)++;
4422 		add_assoc_long(tree, buf, *numNodes);
4423 		build_thread_tree_helper(cur->branch, tree, numNodes, buf);
4424 	} else { /* "null pointer" */
4425 		add_assoc_long(tree, buf, 0);
4426 	}
4427 }
4428 /* }}} */
4429 
4430 /* {{{ build_thread_tree */
build_thread_tree(THREADNODE * top,zval ** tree)4431 static int build_thread_tree(THREADNODE *top, zval **tree)
4432 {
4433 	long numNodes = 0;
4434 	char buf[25];
4435 
4436 	array_init(*tree);
4437 
4438 	build_thread_tree_helper(top, *tree, &numNodes, buf);
4439 
4440 	return SUCCESS;
4441 }
4442 /* }}} */
4443 
4444 /* {{{ Return threaded by REFERENCES tree */
PHP_FUNCTION(imap_thread)4445 PHP_FUNCTION(imap_thread)
4446 {
4447 	zval *imap_conn_obj;
4448 	php_imap_object *imap_conn_struct;
4449 	zend_long flags = SE_FREE;
4450 	char criteria[] = "ALL";
4451 	THREADNODE *top;
4452 	SEARCHPGM *pgm = NIL;
4453 
4454 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|l", &imap_conn_obj, php_imap_ce, &flags) == FAILURE) {
4455 		RETURN_THROWS();
4456 	}
4457 
4458 	GET_IMAP_STREAM(imap_conn_struct, imap_conn_obj);
4459 
4460 	/* TODO Check if flags are valid (documentation is not present on php.net so need to check this first) */
4461 
4462 	pgm = mail_criteria(criteria);
4463 	top = mail_thread(imap_conn_struct->imap_stream, "REFERENCES", NIL, pgm, flags);
4464 	if (pgm && !(flags & SE_FREE)) {
4465 		mail_free_searchpgm(&pgm);
4466 	}
4467 
4468 	if (top == NIL) {
4469 		php_error_docref(NULL, E_WARNING, "Function returned an empty tree");
4470 		RETURN_FALSE;
4471 	}
4472 
4473 	/* Populate our return value data structure here. */
4474 	if (build_thread_tree(top, &return_value) == FAILURE) {
4475 		mail_free_threadnode(&top);
4476 		RETURN_FALSE;
4477 	}
4478 	mail_free_threadnode(&top);
4479 }
4480 /* }}} */
4481 
4482 /* {{{ Set or fetch imap timeout */
PHP_FUNCTION(imap_timeout)4483 PHP_FUNCTION(imap_timeout)
4484 {
4485 	zend_long ttype, timeout=-1;
4486 	int timeout_type;
4487 
4488 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|l", &ttype, &timeout) == FAILURE) {
4489 		RETURN_THROWS();
4490 	}
4491 
4492 	if (timeout == -1) {
4493 		switch (ttype) {
4494 			case 1:
4495 				timeout_type = GET_OPENTIMEOUT;
4496 				break;
4497 			case 2:
4498 				timeout_type = GET_READTIMEOUT;
4499 				break;
4500 			case 3:
4501 				timeout_type = GET_WRITETIMEOUT;
4502 				break;
4503 			case 4:
4504 				timeout_type = GET_CLOSETIMEOUT;
4505 				break;
4506 			default:
4507 				RETURN_FALSE;
4508 				break;
4509 		}
4510 
4511 		timeout = (zend_long) mail_parameters(NIL, timeout_type, NIL);
4512 		RETURN_LONG(timeout);
4513 	} else if (timeout >= 0) {
4514 		switch (ttype) {
4515 			case 1:
4516 				timeout_type = SET_OPENTIMEOUT;
4517 				break;
4518 			case 2:
4519 				timeout_type = SET_READTIMEOUT;
4520 				break;
4521 			case 3:
4522 				timeout_type = SET_WRITETIMEOUT;
4523 				break;
4524 			case 4:
4525 				timeout_type = SET_CLOSETIMEOUT;
4526 				break;
4527 			default:
4528 				RETURN_FALSE;
4529 				break;
4530 		}
4531 
4532 		timeout = (zend_long) mail_parameters(NIL, timeout_type, (void *) timeout);
4533 		RETURN_TRUE;
4534 	} else {
4535 		RETURN_FALSE;
4536 	}
4537 }
4538 /* }}} */
4539 
4540 #define GETS_FETCH_SIZE 8196LU
php_mail_gets(readfn_t f,void * stream,unsigned long size,GETS_DATA * md)4541 static char *php_mail_gets(readfn_t f, void *stream, unsigned long size, GETS_DATA *md) /* {{{ */
4542 {
4543 
4544 	/*	write to the gets stream if it is set,
4545 		otherwise forward to c-clients gets */
4546 	if (IMAPG(gets_stream)) {
4547 		char buf[GETS_FETCH_SIZE];
4548 
4549 		while (size) {
4550 			unsigned long read;
4551 
4552 			if (size > GETS_FETCH_SIZE) {
4553 				read = GETS_FETCH_SIZE;
4554 				size -=GETS_FETCH_SIZE;
4555 			} else {
4556 				read = size;
4557 				size = 0;
4558 			}
4559 
4560 			if (!f(stream, read, buf)) {
4561 				php_error_docref(NULL, E_WARNING, "Failed to read from socket");
4562 				break;
4563 			} else if (read != php_stream_write(IMAPG(gets_stream), buf, read)) {
4564 				php_error_docref(NULL, E_WARNING, "Failed to write to stream");
4565 				break;
4566 			}
4567 		}
4568 		return NULL;
4569 	} else {
4570 		char *buf = pemalloc(size + 1, 1);
4571 
4572 		if (f(stream, size, buf)) {
4573 			buf[size] = '\0';
4574 		} else {
4575 			php_error_docref(NULL, E_WARNING, "Failed to read from socket");
4576 			free(buf);
4577 			buf = NULL;
4578 		}
4579 		return buf;
4580 	}
4581 }
4582 /* }}} */
4583 
4584 /* {{{ Interfaces to C-client */
mm_searched(MAILSTREAM * stream,unsigned long number)4585 PHP_IMAP_EXPORT void mm_searched(MAILSTREAM *stream, unsigned long number)
4586 {
4587 	MESSAGELIST *cur = NIL;
4588 
4589 	if (IMAPG(imap_messages) == NIL) {
4590 		IMAPG(imap_messages) = mail_newmessagelist();
4591 		IMAPG(imap_messages)->msgid = number;
4592 		IMAPG(imap_messages)->next = NIL;
4593 		IMAPG(imap_messages_tail) = IMAPG(imap_messages);
4594 	} else {
4595 		cur = IMAPG(imap_messages_tail);
4596 		cur->next = mail_newmessagelist();
4597 		cur = cur->next;
4598 		cur->msgid = number;
4599 		cur->next = NIL;
4600 		IMAPG(imap_messages_tail) = cur;
4601 	}
4602 }
4603 
mm_exists(MAILSTREAM * stream,unsigned long number)4604 PHP_IMAP_EXPORT void mm_exists(MAILSTREAM *stream, unsigned long number)
4605 {
4606 }
4607 
mm_expunged(MAILSTREAM * stream,unsigned long number)4608 PHP_IMAP_EXPORT void mm_expunged(MAILSTREAM *stream, unsigned long number)
4609 {
4610 }
4611 
mm_flags(MAILSTREAM * stream,unsigned long number)4612 PHP_IMAP_EXPORT void mm_flags(MAILSTREAM *stream, unsigned long number)
4613 {
4614 }
4615 
4616 /* Author: CJH */
mm_notify(MAILSTREAM * stream,char * str,long errflg)4617 PHP_IMAP_EXPORT void mm_notify(MAILSTREAM *stream, char *str, long errflg)
4618 {
4619 	STRINGLIST *cur = NIL;
4620 
4621 	if (strncmp(str, "[ALERT] ", 8) == 0) {
4622 		if (IMAPG(imap_alertstack) == NIL) {
4623 			IMAPG(imap_alertstack) = mail_newstringlist();
4624 			IMAPG(imap_alertstack)->LSIZE = strlen((char*)(IMAPG(imap_alertstack)->LTEXT = (unsigned char*)cpystr(str)));
4625 			IMAPG(imap_alertstack)->next = NIL;
4626 		} else {
4627 			cur = IMAPG(imap_alertstack);
4628 			while (cur->next != NIL) {
4629 				cur = cur->next;
4630 			}
4631 			cur->next = mail_newstringlist ();
4632 			cur = cur->next;
4633 			cur->LSIZE = strlen((char*)(cur->LTEXT = (unsigned char*)cpystr(str)));
4634 			cur->next = NIL;
4635 		}
4636 	}
4637 }
4638 
mm_list(MAILSTREAM * stream,DTYPE delimiter,char * mailbox,long attributes)4639 PHP_IMAP_EXPORT void mm_list(MAILSTREAM *stream, DTYPE delimiter, char *mailbox, long attributes)
4640 {
4641 	STRINGLIST *cur=NIL;
4642 	FOBJECTLIST *ocur=NIL;
4643 
4644 	if (IMAPG(folderlist_style) == FLIST_OBJECT) {
4645 		/* build up a the new array of objects */
4646 		/* Author: CJH */
4647 		if (IMAPG(imap_folder_objects) == NIL) {
4648 			IMAPG(imap_folder_objects) = mail_newfolderobjectlist();
4649 			IMAPG(imap_folder_objects)->LSIZE=strlen((char*)(IMAPG(imap_folder_objects)->LTEXT = (unsigned char*)cpystr(mailbox)));
4650 			IMAPG(imap_folder_objects)->delimiter = delimiter;
4651 			IMAPG(imap_folder_objects)->attributes = attributes;
4652 			IMAPG(imap_folder_objects)->next = NIL;
4653 			IMAPG(imap_folder_objects_tail) = IMAPG(imap_folder_objects);
4654 		} else {
4655 			ocur=IMAPG(imap_folder_objects_tail);
4656 			ocur->next=mail_newfolderobjectlist();
4657 			ocur=ocur->next;
4658 			ocur->LSIZE = strlen((char*)(ocur->LTEXT = (unsigned char*)cpystr(mailbox)));
4659 			ocur->delimiter = delimiter;
4660 			ocur->attributes = attributes;
4661 			ocur->next = NIL;
4662 			IMAPG(imap_folder_objects_tail) = ocur;
4663 		}
4664 
4665 	} else {
4666 		/* build the old IMAPG(imap_folders) variable to allow old imap_listmailbox() to work */
4667 		if (!(attributes & LATT_NOSELECT)) {
4668 			if (IMAPG(imap_folders) == NIL) {
4669 				IMAPG(imap_folders)=mail_newstringlist();
4670 				IMAPG(imap_folders)->LSIZE=strlen((char*)(IMAPG(imap_folders)->LTEXT = (unsigned char*)cpystr(mailbox)));
4671 				IMAPG(imap_folders)->next=NIL;
4672 				IMAPG(imap_folders_tail) = IMAPG(imap_folders);
4673 			} else {
4674 				cur=IMAPG(imap_folders_tail);
4675 				cur->next=mail_newstringlist ();
4676 				cur=cur->next;
4677 				cur->LSIZE = strlen((char*)(cur->LTEXT = (unsigned char*)cpystr(mailbox)));
4678 				cur->next = NIL;
4679 				IMAPG(imap_folders_tail) = cur;
4680 			}
4681 		}
4682 	}
4683 }
4684 
mm_lsub(MAILSTREAM * stream,DTYPE delimiter,char * mailbox,long attributes)4685 PHP_IMAP_EXPORT void mm_lsub(MAILSTREAM *stream, DTYPE delimiter, char *mailbox, long attributes)
4686 {
4687 	STRINGLIST *cur=NIL;
4688 	FOBJECTLIST *ocur=NIL;
4689 
4690 	if (IMAPG(folderlist_style) == FLIST_OBJECT) {
4691 		/* build the array of objects */
4692 		/* Author: CJH */
4693 		if (IMAPG(imap_sfolder_objects) == NIL) {
4694 			IMAPG(imap_sfolder_objects) = mail_newfolderobjectlist();
4695 			IMAPG(imap_sfolder_objects)->LSIZE = strlen((char*)(IMAPG(imap_sfolder_objects)->LTEXT = (unsigned char*)cpystr(mailbox)));
4696 			IMAPG(imap_sfolder_objects)->delimiter = delimiter;
4697 			IMAPG(imap_sfolder_objects)->attributes = attributes;
4698 			IMAPG(imap_sfolder_objects)->next = NIL;
4699 			IMAPG(imap_sfolder_objects_tail) = IMAPG(imap_sfolder_objects);
4700 		} else {
4701 			ocur=IMAPG(imap_sfolder_objects_tail);
4702 			ocur->next=mail_newfolderobjectlist();
4703 			ocur=ocur->next;
4704 			ocur->LSIZE=strlen((char*)(ocur->LTEXT = (unsigned char*)cpystr(mailbox)));
4705 			ocur->delimiter = delimiter;
4706 			ocur->attributes = attributes;
4707 			ocur->next = NIL;
4708 			IMAPG(imap_sfolder_objects_tail) = ocur;
4709 		}
4710 	} else {
4711 		/* build the old simple array for imap_listsubscribed() */
4712 		if (IMAPG(imap_sfolders) == NIL) {
4713 			IMAPG(imap_sfolders)=mail_newstringlist();
4714 			IMAPG(imap_sfolders)->LSIZE=strlen((char*)(IMAPG(imap_sfolders)->LTEXT = (unsigned char*)cpystr(mailbox)));
4715 			IMAPG(imap_sfolders)->next=NIL;
4716 			IMAPG(imap_sfolders_tail) = IMAPG(imap_sfolders);
4717 		} else {
4718 			cur=IMAPG(imap_sfolders_tail);
4719 			cur->next=mail_newstringlist ();
4720 			cur=cur->next;
4721 			cur->LSIZE = strlen((char*)(cur->LTEXT = (unsigned char*)cpystr(mailbox)));
4722 			cur->next = NIL;
4723 			IMAPG(imap_sfolders_tail) = cur;
4724 		}
4725 	}
4726 }
4727 
mm_status(MAILSTREAM * stream,char * mailbox,MAILSTATUS * status)4728 PHP_IMAP_EXPORT void mm_status(MAILSTREAM *stream, char *mailbox, MAILSTATUS *status)
4729 {
4730 
4731 	IMAPG(status_flags)=status->flags;
4732 	if (IMAPG(status_flags) & SA_MESSAGES) {
4733 		IMAPG(status_messages)=status->messages;
4734 	}
4735 	if (IMAPG(status_flags) & SA_RECENT) {
4736 		IMAPG(status_recent)=status->recent;
4737 	}
4738 	if (IMAPG(status_flags) & SA_UNSEEN) {
4739 		IMAPG(status_unseen)=status->unseen;
4740 	}
4741 	if (IMAPG(status_flags) & SA_UIDNEXT) {
4742 		IMAPG(status_uidnext)=status->uidnext;
4743 	}
4744 	if (IMAPG(status_flags) & SA_UIDVALIDITY) {
4745 		IMAPG(status_uidvalidity)=status->uidvalidity;
4746 	}
4747 }
4748 
mm_log(char * str,long errflg)4749 PHP_IMAP_EXPORT void mm_log(char *str, long errflg)
4750 {
4751 	ERRORLIST *cur = NIL;
4752 
4753 	/* Author: CJH */
4754 	if (errflg != NIL) { /* CJH: maybe put these into a more comprehensive log for debugging purposes? */
4755 		if (IMAPG(imap_errorstack) == NIL) {
4756 			IMAPG(imap_errorstack) = mail_newerrorlist();
4757 			IMAPG(imap_errorstack)->LSIZE = strlen((char*)(IMAPG(imap_errorstack)->LTEXT = (unsigned char*)cpystr(str)));
4758 			IMAPG(imap_errorstack)->errflg = errflg;
4759 			IMAPG(imap_errorstack)->next = NIL;
4760 		} else {
4761 			cur = IMAPG(imap_errorstack);
4762 			while (cur->next != NIL) {
4763 				cur = cur->next;
4764 			}
4765 			cur->next = mail_newerrorlist();
4766 			cur = cur->next;
4767 			cur->LSIZE = strlen((char*)(cur->LTEXT = (unsigned char*)cpystr(str)));
4768 			cur->errflg = errflg;
4769 			cur->next = NIL;
4770 		}
4771 	}
4772 }
4773 
mm_dlog(char * str)4774 PHP_IMAP_EXPORT void mm_dlog(char *str)
4775 {
4776 	/* CJH: this is for debugging; it might be useful to allow setting
4777 	   the stream to debug mode and capturing this somewhere - syslog?
4778 	   php debugger? */
4779 }
4780 
mm_login(NETMBX * mb,char * user,char * pwd,long trial)4781 PHP_IMAP_EXPORT void mm_login(NETMBX *mb, char *user, char *pwd, long trial)
4782 {
4783 
4784 	if (*mb->user) {
4785 		strlcpy (user, mb->user, MAILTMPLEN);
4786 	} else {
4787 		strlcpy (user, IMAPG(imap_user), MAILTMPLEN);
4788 	}
4789 	strlcpy (pwd, IMAPG(imap_password), MAILTMPLEN);
4790 }
4791 
mm_critical(MAILSTREAM * stream)4792 PHP_IMAP_EXPORT void mm_critical(MAILSTREAM *stream)
4793 {
4794 }
4795 
mm_nocritical(MAILSTREAM * stream)4796 PHP_IMAP_EXPORT void mm_nocritical(MAILSTREAM *stream)
4797 {
4798 }
4799 
mm_diskerror(MAILSTREAM * stream,long errcode,long serious)4800 PHP_IMAP_EXPORT long mm_diskerror(MAILSTREAM *stream, long errcode, long serious)
4801 {
4802 	return 1;
4803 }
4804 
mm_fatal(char * str)4805 PHP_IMAP_EXPORT void mm_fatal(char *str)
4806 {
4807 }
4808 /* }}} */
4809