# imap4 search criteria

- Thread: https://externals.io/message/46907
- Original archive: https://news-web.php.net/php.internals/46907
- Started by Dominik Gehl on 2010-01-29T20:49:18+00:00
- Messages: 13
- Reply by email to internals@lists.php.net
- More for agents: search with https://externals.io/search.md?q={query} · JSON API at https://externals.io/api · documentation at https://externals.io/llms.txt

---

## #46907 — Dominik Gehl — 2010-01-29T20:49:18+00:00

URL: https://externals.io/message/46907#46907

Hi,

I noticed that the imap extension seems to support only IMAP2 search criteria.

This is caused by the fact that in ext/imap/php_imap.c, the imap_search function uses a call to mail_criteria. And
the University of Washington IMAP toolkit mentions in docs/internal.txt:

SEARCHPGM *mail_criteria (char *criteria);
       criteria IMAP2-format search criteria string

    This function accepts an IMAP2-format search criteria string and
parses it.  If the parse is successful, it returns a search program
suitable for use in mail_search_full().
       WARNING: This function does not accept IMAP4 search criteria.


Is there any specific reason why PHP uses this mail_criteria call ? It really would be nice to be able to use IMAP4 search criteria !

Thanks,
Dominik

---

## #46908 — Joey Smith — 2010-01-30T23:22:24+00:00

URL: https://externals.io/message/46907#46908 · In reply to: #46907

There's an open bug on this, #15238 (http://bugs.php.net/bug.php?id=15238&). I'm
sure patches would be welcomed.


> Hi,
> 
> I noticed that the imap extension seems to support only IMAP2 search criteria.
> 
> This is caused by the fact that in ext/imap/php_imap.c, the imap_search function uses a call to mail_criteria. And
> the University of Washington IMAP toolkit mentions in docs/internal.txt:
> 
> SEARCHPGM *mail_criteria (char *criteria);
>        criteria IMAP2-format search criteria string
> 
>     This function accepts an IMAP2-format search criteria string and
> parses it.  If the parse is successful, it returns a search program
> suitable for use in mail_search_full().
>        WARNING: This function does not accept IMAP4 search criteria.
> 
> 
> Is there any specific reason why PHP uses this mail_criteria call ? It really would be nice to be able to use IMAP4 search criteria !
> 
> Thanks,
> Dominik

---

## #46924 — Dominik Gehl — 2010-02-01T15:06:03+00:00

URL: https://externals.io/message/46907#46924 · In reply to: #46908

Thanks a lot for your reply. I also found a second bug report related to this: http://bugs.php.net/bug.php?id=21168

Now, how about adding an imap_newsearchpgm function to the PHP imap extension which would do a call to mail_nsewsearchpgm inside c-client and return a structure allowing to contruct IMAP4 search programs (SEARCHPGM inside c-client) ?

Anyone interested in this ?

Dominik



> There's an open bug on this, #15238 (http://bugs.php.net/bug.php?id=15238&). I'm
> sure patches would be welcomed.
> 
> 
> > […]

---

## #46934 — Dominik Gehl — 2010-02-02T21:14:12+00:00

URL: https://externals.io/message/46907#46934 · In reply to: #46924

I continued thinking about this and came up with the idea of adding a new resource 'imap_searchpgm' to the imap extension, which would then have quite a good number of new functions: imap_searchprogram_new, imap_searchprogram_sentsince, imap_searchprogram_since, imap_searchprogram_before, imap_searchprogram_on etc. etc.

The functions could then be defined along the following lines (untested code ...):

PHP_FUNCTION(imap_searchprogram_new)
{
	int myargc = ZEND_NUM_ARGS();

	if (myargc != 0) {
		ZEND_WRONG_PARAM_COUNT();
	}

	pgm = mail_newsearchpgm();
	searchpgm = emalloc(sizeof(php_imap_searchpgm));
	searchpgm->searchpgm = pgm;
	ZEND_REGISTER_RESOURCE(return_value, searchpgm, le_imap_searchpgm);
}

PHP_FUNCTION(imap_searchprogram_sentsince)
{
	zval *zpgm;
	php_imap_searchpgm *pgm;
	char *criterion = "";

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", &zpgm, &criterion) == FAILURE) {
        	RETURN_FALSE;
    	}

	ZEND_FETCH_RESOURCE(pgm, php_imap_search_pgm*, &zpgm, -1, "imapsearch", le_imap_searchpgm);

	mail_criteria_date(&pgm->searchpgm->sentsince, &criterion);

	RETURN_TRUE;
}

etc. etc.


And finally, the imap_search function could then accept either the criteria string OR an imap_searchpgm resource.

Ideas, comments ?

Thanks,
Dominik




> Thanks a lot for your reply. I also found a second bug report related to this: http://bugs.php.net/bug.php?id=21168
> 
> Now, how about adding an imap_newsearchpgm function to the PHP imap extension which would do a call to mail_nsewsearchpgm inside c-client and return a structure allowing to contruct IMAP4 search programs (SEARCHPGM inside c-client) ?
> 
> Anyone interested in this ?
> 
> Dominik
> 
> 
> 
> > […]

---

## #46939 — Dominik Gehl — 2010-02-03T19:37:11+00:00

URL: https://externals.io/message/46907#46939 · In reply to: #46934

Attached is a patch which would allow the usage of most of the IMAP4 search criteria.

Please let me know if I should contact someone else (IMAP extension maintainer(s) ?) regarding this ...

Dominik

---

## #46940 — Pierre Joye — 2010-02-03T20:08:00+00:00

URL: https://externals.io/message/46907#46940 · In reply to: #46939

hi,

Can you send patches as attachment please?

Also I like to think again about a nicer approach. As your proposal
works, it sounds like multiple calls could be avoided.

Cheers,


> Attached is a patch which would allow the usage of most of the IMAP4 search criteria.
>
> Please let me know if I should contact someone else (IMAP extension maintainer(s) ?) regarding this ...
>
> Dominik
>
>
> 
>
> > […]
>
>
>
> --
> 
> 
>



-- 
Pierre

@pierrejoye | http://blog.thepimp.net | http://www.libgd.org

---

## #46941 — Dominik Gehl — 2010-02-03T20:19:43+00:00

URL: https://externals.io/message/46907#46941 · In reply to: #46940

Hi,

I just checked the mailinglist archive (http://news.php.net/php.internals/46939) and you're right, the attachment was missing, even though it's in my sent items ... is there some list policy which rejects attachments ?

Dominik



> hi,
> 
> Can you send patches as attachment please?
> 
> Also I like to think again about a nicer approach. As your proposal
> works, it sounds like multiple calls could be avoided.
> 
> Cheers,
> 
> 
> > […]
> 
> 
> 
> -- 
> Pierre
> 
> @pierrejoye | http://blog.thepimp.net | http://www.libgd.org

---

## #46942 — Dominik Gehl — 2010-02-04T11:06:30+00:00

URL: https://externals.io/message/46907#46942 · In reply to: #46941

Here's the patch inline:

diff -u --recursive ../orig/php-5.2.12/ext/imap/php_imap.c ./ext/imap/php_imap.c
--- ../orig/php-5.2.12/ext/imap/php_imap.c	2009-09-22 14:18:57.000000000 -0400
+++ ./ext/imap/php_imap.c	2010-02-03 14:16:41.000000000 -0500
@@ -152,6 +152,9 @@
 	PHP_FE(imap_timeout,								NULL)
 
 #if defined(HAVE_IMAP2000) || defined(HAVE_IMAP2001)
+	PHP_FE(imap_searchprogram_new,								NULL)
+	PHP_FE(imap_searchprogram_or,								NULL)
+	PHP_FE(imap_searchprogram_setcriterion,								NULL)
 	PHP_FE(imap_get_quota,							NULL)
 	PHP_FE(imap_get_quotaroot,						NULL)
 	PHP_FE(imap_set_quota,							NULL)
@@ -209,6 +212,7 @@
 
 /* True globals, no need for thread safety */
 static int le_imap;
+static int le_imap_searchpgm;
 
 #define PHP_IMAP_CHECK_MSGNO(msgindex)	\
 	if ((msgindex < 1) || ((unsigned) msgindex > imap_le_struct->imap_stream->nmsgs)) {	\
@@ -673,6 +677,7 @@
 	*/
 
 	le_imap = zend_register_list_destructors_ex(mail_close_it, NULL, "imap", module_number);
+	le_imap_searchpgm = zend_register_list_destructors_ex(NULL, NULL, "imapsearchpgm", module_number);
 	return SUCCESS;
 }
 /* }}} */
@@ -3693,7 +3698,9 @@
 	char *search_criteria;
 	MESSAGELIST *cur;
 	int argc = ZEND_NUM_ARGS();
+	int criteria_is_string;
 	SEARCHPGM *pgm = NIL;
+	pisearchpgms *php_searchpgm;
 
 	if (argc < 2 || argc > 4 || zend_get_parameters_ex(argc, &streamind, &criteria, &search_flags, &charset) == FAILURE) {
 		ZEND_WRONG_PARAM_COUNT();
@@ -3701,8 +3708,25 @@
 
 	ZEND_FETCH_RESOURCE(imap_le_struct, pils *, streamind, -1, "imap", le_imap);
 
-	convert_to_string_ex(criteria);
-	search_criteria = estrndup(Z_STRVAL_PP(criteria), Z_STRLEN_PP(criteria));
+	switch (Z_TYPE_PP(criteria)) {
+		case IS_STRING:
+			convert_to_string_ex(criteria);
+			search_criteria = estrndup(Z_STRVAL_PP(criteria), Z_STRLEN_PP(criteria));
+			pgm = mail_criteria(search_criteria);
+			criteria_is_string=1;
+			break;
+
+		case IS_RESOURCE:
+			ZEND_FETCH_RESOURCE(php_searchpgm, pisearchpgms *, criteria, -1, "imapsearchpgm", le_imap_searchpgm);
+			pgm = php_searchpgm->searchpgm;
+			criteria_is_string=0;
+			break;
+
+		default:
+			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Second argumented is expected to be either a string or a searchprogram resource, %s given", zend_zval_type_name(*criteria));
+                        RETURN_FALSE;
+	}
+			
 
 	if (argc == 2) {
 		flags = SE_FREE;
@@ -3714,17 +3738,18 @@
 		}
 	}
 
-	pgm = mail_criteria(search_criteria);
 	IMAPG(imap_messages) = IMAPG(imap_messages_tail) = NIL;
 
 	mail_search_full(imap_le_struct->imap_stream, (argc == 4 ? Z_STRVAL_PP(charset) : NIL), pgm, flags);
 
-	if (pgm && !(flags & SE_FREE)) {
+	if (pgm && criteria_is_string && !(flags & SE_FREE)) {
 		mail_free_searchpgm(&pgm);
 	}
 
 	if (IMAPG(imap_messages) == NIL) {
-		efree(search_criteria);
+		if (criteria_is_string) {
+			efree(search_criteria);
+		}
 		RETURN_FALSE;
 	}
 
@@ -3736,7 +3761,9 @@
 		cur = cur->next;
 	}
 	mail_free_messagelist(&IMAPG(imap_messages), &IMAPG(imap_messages_tail));
-	efree(search_criteria);
+	if (criteria_is_string) {
+		efree(search_criteria);
+	}
 }
 /* }}} */
 
@@ -4425,6 +4452,148 @@
 }
 /* }}} */
 
+/* {{{ proto mixed imap_search_program_new()
+   Get new search program */
+PHP_FUNCTION(imap_searchprogram_new)
+{
+	pisearchpgms *php_searchpgm;
+
+	if (ZEND_NUM_ARGS() != 0) {
+		ZEND_WRONG_PARAM_COUNT();
+	}
+
+	php_searchpgm = emalloc(sizeof(pisearchpgms));
+	php_searchpgm->searchpgm = mail_newsearchpgm();
+	ZEND_REGISTER_RESOURCE(return_value, php_searchpgm, le_imap_searchpgm);
+}
+/* }}} */
+
+PHP_FUNCTION(imap_searchprogram_or)
+{
+	zval **zpgm1, **zpgm2;
+	pisearchpgms *php_searchpgm_or;
+	pisearchpgms *php_searchpgm_1;
+	pisearchpgms *php_searchpgm_2;
+
+	if (ZEND_NUM_ARGS() != 2 || zend_get_parameters_ex(2, &zpgm1, &zpgm2) == FAILURE) {
+		ZEND_WRONG_PARAM_COUNT();
+	}
+
+	ZEND_FETCH_RESOURCE(php_searchpgm_1, pisearchpgms *, zpgm1, -1, "imapsearchpgm", le_imap_searchpgm);
+	ZEND_FETCH_RESOURCE(php_searchpgm_2, pisearchpgms *, zpgm2, -1, "imapsearchpgm", le_imap_searchpgm);
+
+	php_searchpgm_or = emalloc(sizeof(pisearchpgms));
+	php_searchpgm_or->searchpgm = mail_newsearchpgm();
+	php_searchpgm_or->searchpgm->or = mail_newsearchor();
+	php_searchpgm_or->searchpgm->or->first = php_searchpgm_1->searchpgm;
+	php_searchpgm_or->searchpgm->or->second = php_searchpgm_2->searchpgm;
+	ZEND_REGISTER_RESOURCE(return_value, php_searchpgm_or, le_imap_searchpgm);
+}
+
+PHP_FUNCTION(imap_searchprogram_setcriterion)
+{
+	zval **zpgm, **criterion_name, **criterion_value;
+	pisearchpgms *php_searchpgm;
+	char *search_criterion_name;
+	char *search_criterion_value;
+	int argc = ZEND_NUM_ARGS();
+	int f=0;
+
+	if (argc < 2 || argc > 3 || zend_get_parameters_ex(argc, &zpgm, &criterion_name, &criterion_value) == FAILURE) {
+		ZEND_WRONG_PARAM_COUNT();
+	}
+
+	ZEND_FETCH_RESOURCE(php_searchpgm, pisearchpgms *, zpgm, -1, "imapsearchpgm", le_imap_searchpgm);
+
+	convert_to_string_ex(criterion_name);
+	search_criterion_name = estrndup(Z_STRVAL_PP(criterion_name), Z_STRLEN_PP(criterion_name));
+	if (argc == 3) {
+		convert_to_string_ex(criterion_value);
+		search_criterion_value = estrndup(Z_STRVAL_PP(criterion_value), Z_STRLEN_PP(criterion_value));
+	}
+
+	// this comes from mail.c and imapd.c
+	switch (*ucase (search_criterion_name)) {
+		case 'A':			/* possible ALL, ANSWERED */
+			if (!strcmp (search_criterion_name+1,"LL")) f = 1;
+			else if (!strcmp (search_criterion_name+1,"NSWERED")) f = php_searchpgm->searchpgm->answered = 1;
+			break;
+		case 'B':			/* possible BCC, BEFORE, BODY */
+			if (!strcmp (search_criterion_name+1,"CC"))
+				f = mail_criteria_string (&php_searchpgm->searchpgm->bcc,&search_criterion_value);
+			else if (!strcmp (search_criterion_name+1,"EFORE"))
+				f = mail_criteria_date (&php_searchpgm->searchpgm->before,&search_criterion_value);
+			else if (!strcmp (search_criterion_name+1,"ODY"))
+				f = mail_criteria_string (&php_searchpgm->searchpgm->body,&search_criterion_value);
+			break;
+		case 'C':			/* possible CC */
+			if (!strcmp (search_criterion_name+1,"C")) f = mail_criteria_string (&php_searchpgm->searchpgm->cc,&search_criterion_value);
+			break;
+		case 'D':			/* possible DELETED, DRAFT */
+			if (!strcmp (search_criterion_name+1,"ELETED")) f = php_searchpgm->searchpgm->deleted = 1;
+			else if (!strcmp (search_criterion_name+1,"RAFT")) f = php_searchpgm->searchpgm->draft = 1;
+			break;
+		case 'F':			/* possible FLAGGED, FROM */
+			if (!strcmp (search_criterion_name+1,"LAGGED")) f = php_searchpgm->searchpgm->flagged = 1;
+			else if (!strcmp (search_criterion_name+1,"ROM"))
+				f = mail_criteria_string (&php_searchpgm->searchpgm->from,&search_criterion_value);
+			break;
+		case 'K':			/* possible KEYWORD */
+			if (!strcmp (search_criterion_name+1,"EYWORD"))
+				f = mail_criteria_string (&php_searchpgm->searchpgm->keyword,&search_criterion_value);
+			break;
+		case 'N':			/* possible NEW */
+			if (!strcmp (search_criterion_name+1,"EW")) f = php_searchpgm->searchpgm->recent = php_searchpgm->searchpgm->unseen = 1;
+			break;
+		case 'O':			/* possible OLD, ON */
+			if (!strcmp (search_criterion_name+1,"LD")) f = php_searchpgm->searchpgm->old = 1;
+			else if (!strcmp (search_criterion_name+1,"N"))
+				f = mail_criteria_date (&php_searchpgm->searchpgm->on,&search_criterion_value);
+			break;
+		case 'R':			/* possible RECENT */
+			if (!strcmp (search_criterion_name+1,"ECENT")) f = php_searchpgm->searchpgm->recent = 1;
+			break;
+		case 'S':			/* possible SEEN, SENTBEFORE, SENTON, SENTSINCE, SINCE, SUBJECT */
+			if (!strcmp (search_criterion_name+1,"EEN")) f = php_searchpgm->searchpgm->seen = 1;
+			else if (!strcmp (search_criterion_name+1,"ENTBEFORE"))
+				f = mail_criteria_date (&php_searchpgm->searchpgm->sentbefore,&search_criterion_value);
+			else if (!strcmp (search_criterion_name+1,"ENTON"))
+				f = mail_criteria_date (&php_searchpgm->searchpgm->senton,&search_criterion_value);
+			else if (!strcmp (search_criterion_name+1,"ENTSINCE"))
+				f = mail_criteria_date (&php_searchpgm->searchpgm->sentsince,&search_criterion_value);
+			else if (!strcmp (search_criterion_name+1,"INCE"))
+				f = mail_criteria_date (&php_searchpgm->searchpgm->since,&search_criterion_value);
+			else if (!strcmp (search_criterion_name+1,"UBJECT"))
+				f = mail_criteria_string (&php_searchpgm->searchpgm->subject,&search_criterion_value);
+			break;
+		case 'T':			/* possible TEXT, TO */
+			if (!strcmp (search_criterion_name+1,"EXT"))
+				f = mail_criteria_string (&php_searchpgm->searchpgm->text,&search_criterion_value);
+			else if (!strcmp (search_criterion_name+1,"O"))
+				f = mail_criteria_string (&php_searchpgm->searchpgm->to,&search_criterion_value);
+			break;
+		case 'U':			/* possible UN* */
+			if (search_criterion_name[1] == 'N') {
+				if (!strcmp (search_criterion_name+2,"ANSWERED")) f = php_searchpgm->searchpgm->unanswered = 1;
+					else if (!strcmp (search_criterion_name+2,"DELETED")) f = php_searchpgm->searchpgm->undeleted = 1;
+					else if (!strcmp (search_criterion_name+2,"DRAFT")) f = php_searchpgm->searchpgm->undraft = 1;
+					else if (!strcmp (search_criterion_name+2,"FLAGGED")) f = php_searchpgm->searchpgm->unflagged = 1;
+					else if (!strcmp (search_criterion_name+2,"KEYWORD"))
+						f = mail_criteria_string (&php_searchpgm->searchpgm->unkeyword,&search_criterion_value);
+					else if (!strcmp (search_criterion_name+2,"SEEN")) f = php_searchpgm->searchpgm->unseen = 1;
+				}
+				break;
+		default:			/* we will barf below */
+				break;
+	}
+
+	if (! f) {
+		RETURN_FALSE;
+	}
+	RETURN_TRUE;
+}
+
+
 #define GETS_FETCH_SIZE 8196LU
 /* {{{ php_mail_gets */
 static char *php_mail_gets(readfn_t f, void *stream, unsigned long size, GETS_DATA *md)
diff -u --recursive ../orig/php-5.2.12/ext/imap/php_imap.h ./ext/imap/php_imap.h
--- ../orig/php-5.2.12/ext/imap/php_imap.h	2008-12-31 06:17:49.000000000 -0500
+++ ./ext/imap/php_imap.h	2010-02-03 13:56:51.000000000 -0500
@@ -94,6 +94,10 @@
 	unsigned long msgid;
 	struct _php_imap_message_struct *next;
 } MESSAGELIST;
+
+typedef struct _php_imap_searchpgm_struct {
+	SEARCHPGM *searchpgm;
+} pisearchpgms;
  
 
 /* Functions */
@@ -169,6 +173,9 @@
 PHP_FUNCTION(imap_timeout);
 
 #if defined(HAVE_IMAP2000) || defined(HAVE_IMAP2001)
+PHP_FUNCTION(imap_searchprogram_new);
+PHP_FUNCTION(imap_searchprogram_or);
+PHP_FUNCTION(imap_searchprogram_setcriterion);
 PHP_FUNCTION(imap_get_quota);
 PHP_FUNCTION(imap_get_quotaroot);
 PHP_FUNCTION(imap_set_quota);





> Hi,
> 
> I just checked the mailinglist archive (http://news.php.net/php.internals/46939) and you're right, the attachment was missing, even though it's in my sent items ... is there some list policy which rejects attachments ?
> 
> Dominik
> 
> 
> 
> > […]

---

## #46945 — Dominik Gehl — 2010-02-04T21:23:55+00:00

URL: https://externals.io/message/46907#46945 · In reply to: #46942

Here's a new version of the patch. It allows the usage of the following style of code:

$pgm = imap_searchprogram_new();
imap_searchprogram_criteria($pgm1,"DRAFT SMALLER 34567 HEADER Message-Id <D76C58CD-C94D-4DE4-B621-83D66437EFE3@dokdok.com> FROM dominik@dokdok.com");
imap_search($mbox,$pgm));


Dominik


diff --git a/external/php-5.2.12/ext/imap/php_imap.c b/external/php-5.2.12/ext/imap/php_imap.c
index 16714ae..771c788 100644
--- a/external/php-5.2.12/ext/imap/php_imap.c
+++ b/external/php-5.2.12/ext/imap/php_imap.c
@@ -152,6 +152,9 @@ zend_function_entry imap_functions[] = {
 	PHP_FE(imap_timeout,								NULL)
 
 #if defined(HAVE_IMAP2000) || defined(HAVE_IMAP2001)
+	PHP_FE(imap_searchprogram_new,								NULL)
+	PHP_FE(imap_searchprogram_or,								NULL)
+	PHP_FE(imap_searchprogram_criteria,								NULL)
 	PHP_FE(imap_get_quota,							NULL)
 	PHP_FE(imap_get_quotaroot,						NULL)
 	PHP_FE(imap_set_quota,							NULL)
@@ -209,6 +212,7 @@ ZEND_GET_MODULE(imap)
 
 /* True globals, no need for thread safety */
 static int le_imap;
+static int le_imap_searchpgm;
 
 #define PHP_IMAP_CHECK_MSGNO(msgindex)	\
 	if ((msgindex < 1) || ((unsigned) msgindex > imap_le_struct->imap_stream->nmsgs)) {	\
@@ -240,6 +244,14 @@ static void mail_close_it(zend_rsrc_list_entry *rsrc TSRMLS_DC)
 }
 /* }}} */
 
+static void mailsearchpgm_close_it(zend_rsrc_list_entry *rsrc TSRMLS_DC)
+{
+	pisearchpgms *php_searchpgm = (pisearchpgms *)rsrc->ptr;
+
+	//mail_free_searchpgm(&php_searchpgm->searchpgm);
+	efree(php_searchpgm);
+}
+
 /* {{{ add_assoc_object
  */
 static int add_assoc_object(zval *arg, char *key, zval *tmp TSRMLS_DC)
@@ -673,6 +685,7 @@ PHP_MINIT_FUNCTION(imap)
 	*/
 
 	le_imap = zend_register_list_destructors_ex(mail_close_it, NULL, "imap", module_number);
+	le_imap_searchpgm = zend_register_list_destructors_ex(mailsearchpgm_close_it, NULL, "imapsearchpgm", module_number);
 	return SUCCESS;
 }
 /* }}} */
@@ -3693,7 +3706,9 @@ PHP_FUNCTION(imap_search)
 	char *search_criteria;
 	MESSAGELIST *cur;
 	int argc = ZEND_NUM_ARGS();
+	int criteria_is_string;
 	SEARCHPGM *pgm = NIL;
+	pisearchpgms *php_searchpgm;
 
 	if (argc < 2 || argc > 4 || zend_get_parameters_ex(argc, &streamind, &criteria, &search_flags, &charset) == FAILURE) {
 		ZEND_WRONG_PARAM_COUNT();
@@ -3701,8 +3716,25 @@ PHP_FUNCTION(imap_search)
 
 	ZEND_FETCH_RESOURCE(imap_le_struct, pils *, streamind, -1, "imap", le_imap);
 
-	convert_to_string_ex(criteria);
-	search_criteria = estrndup(Z_STRVAL_PP(criteria), Z_STRLEN_PP(criteria));
+	switch (Z_TYPE_PP(criteria)) {
+		case IS_STRING:
+			convert_to_string_ex(criteria);
+			search_criteria = estrndup(Z_STRVAL_PP(criteria), Z_STRLEN_PP(criteria));
+			pgm = mail_criteria(search_criteria);
+			criteria_is_string=1;
+			break;
+
+		case IS_RESOURCE:
+			ZEND_FETCH_RESOURCE(php_searchpgm, pisearchpgms *, criteria, -1, "imapsearchpgm", le_imap_searchpgm);
+			pgm = php_searchpgm->searchpgm;
+			criteria_is_string=0;
+			break;
+
+		default:
+			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Second argumented is expected to be either a string or a searchprogram resource, %s given", zend_zval_type_name(*criteria));
+                        RETURN_FALSE;
+	}
+			
 
 	if (argc == 2) {
 		flags = SE_FREE;
@@ -3714,17 +3746,18 @@ PHP_FUNCTION(imap_search)
 		}
 	}
 
-	pgm = mail_criteria(search_criteria);
 	IMAPG(imap_messages) = IMAPG(imap_messages_tail) = NIL;
 
 	mail_search_full(imap_le_struct->imap_stream, (argc == 4 ? Z_STRVAL_PP(charset) : NIL), pgm, flags);
 
-	if (pgm && !(flags & SE_FREE)) {
+	if (pgm && criteria_is_string && !(flags & SE_FREE)) {
 		mail_free_searchpgm(&pgm);
 	}
 
 	if (IMAPG(imap_messages) == NIL) {
-		efree(search_criteria);
+		if (criteria_is_string) {
+			efree(search_criteria);
+		}
 		RETURN_FALSE;
 	}
 
@@ -3736,7 +3769,9 @@ PHP_FUNCTION(imap_search)
 		cur = cur->next;
 	}
 	mail_free_messagelist(&IMAPG(imap_messages), &IMAPG(imap_messages_tail));
-	efree(search_criteria);
+	if (criteria_is_string) {
+		efree(search_criteria);
+	}
 }
 /* }}} */
 
@@ -4425,6 +4460,178 @@ PHP_FUNCTION(imap_timeout)
 }
 /* }}} */
 
+/* {{{ proto mixed imap_search_program_new()
+   Get new search program */
+PHP_FUNCTION(imap_searchprogram_new)
+{
+	pisearchpgms *php_searchpgm;
+
+	if (ZEND_NUM_ARGS() != 0) {
+		ZEND_WRONG_PARAM_COUNT();
+	}
+
+	php_searchpgm = emalloc(sizeof(pisearchpgms));
+	php_searchpgm->searchpgm = mail_newsearchpgm();
+	ZEND_REGISTER_RESOURCE(return_value, php_searchpgm, le_imap_searchpgm);
+}
+/* }}} */
+
+PHP_FUNCTION(imap_searchprogram_or)
+{
+	zval **zpgm1, **zpgm2;
+	pisearchpgms *php_searchpgm_or;
+	pisearchpgms *php_searchpgm_1;
+	pisearchpgms *php_searchpgm_2;
+
+	if (ZEND_NUM_ARGS() != 2 || zend_get_parameters_ex(2, &zpgm1, &zpgm2) == FAILURE) {
+		ZEND_WRONG_PARAM_COUNT();
+	}
+
+	ZEND_FETCH_RESOURCE(php_searchpgm_1, pisearchpgms *, zpgm1, -1, "imapsearchpgm", le_imap_searchpgm);
+	ZEND_FETCH_RESOURCE(php_searchpgm_2, pisearchpgms *, zpgm2, -1, "imapsearchpgm", le_imap_searchpgm);
+
+	php_searchpgm_or = emalloc(sizeof(pisearchpgms));
+	php_searchpgm_or->searchpgm = mail_newsearchpgm();
+	php_searchpgm_or->searchpgm->or = mail_newsearchor();
+	php_searchpgm_or->searchpgm->or->first = php_searchpgm_1->searchpgm;
+	php_searchpgm_or->searchpgm->or->second = php_searchpgm_2->searchpgm;
+	ZEND_REGISTER_RESOURCE(return_value, php_searchpgm_or, le_imap_searchpgm);
+}
+
+PHP_FUNCTION(imap_searchprogram_criteria)
+{
+	zval **zpgm, **zcriteria;
+	pisearchpgms *php_searchpgm;
+	char *criteria;
+	SEARCHHEADER **hdr;
+	char *header_name, *header_value;
+	char *search_criterion_name,*r;
+	int f;
+
+	if (ZEND_NUM_ARGS() != 2 || zend_get_parameters_ex(2, &zpgm, &zcriteria) == FAILURE) {
+		ZEND_WRONG_PARAM_COUNT();
+	}
+
+	ZEND_FETCH_RESOURCE(php_searchpgm, pisearchpgms *, zpgm, -1, "imapsearchpgm", le_imap_searchpgm);
+
+	convert_to_string_ex(zcriteria);
+	criteria = estrndup(Z_STRVAL_PP(zcriteria), Z_STRLEN_PP(zcriteria));
+
+	// this comes from mail.c and imapd.c
+	/* for each criterion */
+	for (search_criterion_name = strtok_r (criteria," ",&r); search_criterion_name; (search_criterion_name = strtok_r (NIL," ",&r))) {
+		f = NIL;			/* init then scan the criterion */
+
+		switch (*ucase (search_criterion_name)) {
+			case 'A':			/* possible ALL, ANSWERED */
+				if (!strcmp (search_criterion_name+1,"LL")) f = T;
+				else if (!strcmp (search_criterion_name+1,"NSWERED")) f = php_searchpgm->searchpgm->answered = T;
+				break;
+			case 'B':			/* possible BCC, BEFORE, BODY */
+				if (!strcmp (search_criterion_name+1,"CC"))
+					f = mail_criteria_string (&php_searchpgm->searchpgm->bcc,&r);
+				else if (!strcmp (search_criterion_name+1,"EFORE"))
+					f = mail_criteria_date (&php_searchpgm->searchpgm->before,&r);
+				else if (!strcmp (search_criterion_name+1,"ODY"))
+					f = mail_criteria_string (&php_searchpgm->searchpgm->body,&r);
+				break;
+			case 'C':			/* possible CC */
+				if (!strcmp (search_criterion_name+1,"C")) f = mail_criteria_string (&php_searchpgm->searchpgm->cc,&r);
+				break;
+			case 'D':			/* possible DELETED, DRAFT */
+				if (!strcmp (search_criterion_name+1,"ELETED")) f = php_searchpgm->searchpgm->deleted = T;
+				else if (!strcmp (search_criterion_name+1,"RAFT")) f = php_searchpgm->searchpgm->draft = T;
+				break;
+			case 'F':			/* possible FLAGGED, FROM */
+				if (!strcmp (search_criterion_name+1,"LAGGED")) f = php_searchpgm->searchpgm->flagged = T;
+				else if (!strcmp (search_criterion_name+1,"ROM"))
+					f = mail_criteria_string (&php_searchpgm->searchpgm->from,&r);
+				break;
+			case 'H':			/* possible HEADER */
+				if (!strcmp (search_criterion_name+1,"EADER")) {
+					header_name = strtok_r (NIL," ",&r);
+					header_value = strtok_r (NIL," ",&r);
+					for (hdr = &php_searchpgm->searchpgm->header; *hdr; hdr = &(*hdr)->next);
+					*hdr = mail_newsearchheader (header_name,header_value);
+					f = T;		/* success */
+				}
+				break;
+			case 'K':			/* possible KEYWORD */
+				if (!strcmp (search_criterion_name+1,"EYWORD"))
+					f = mail_criteria_string (&php_searchpgm->searchpgm->keyword,&r);
+				break;
+			case 'L':			/* possible LARGER */
+				if (!strcmp (search_criterion_name+1,"ARGER"))
+					f = mail_criteria_number (&php_searchpgm->searchpgm->larger,&r);
+				break;
+			case 'N':			/* possible NEW */
+				if (!strcmp (search_criterion_name+1,"EW")) f = php_searchpgm->searchpgm->recent = php_searchpgm->searchpgm->unseen = T;
+				break;
+			case 'O':			/* possible OLD, ON */
+				if (!strcmp (search_criterion_name+1,"LD")) f = php_searchpgm->searchpgm->old = T;
+				else if (!strcmp (search_criterion_name+1,"N"))
+					f = mail_criteria_date (&php_searchpgm->searchpgm->on,&r);
+				break;
+			case 'R':			/* possible RECENT */
+				if (!strcmp (search_criterion_name+1,"ECENT")) f = php_searchpgm->searchpgm->recent = T;
+				break;
+			case 'S':			/* possible SEEN, SENTBEFORE, SENTON, SENTSINCE, SINCE, SUBJECT */
+				if (!strcmp (search_criterion_name+1,"EEN")) f = php_searchpgm->searchpgm->seen = T;
+				else if (!strcmp (search_criterion_name+1,"ENTBEFORE"))
+					f = mail_criteria_date (&php_searchpgm->searchpgm->sentbefore,&r);
+				else if (!strcmp (search_criterion_name+1,"ENTON"))
+					f = mail_criteria_date (&php_searchpgm->searchpgm->senton,&r);
+				else if (!strcmp (search_criterion_name+1,"ENTSINCE"))
+					f = mail_criteria_date (&php_searchpgm->searchpgm->sentsince,&r);
+				else if (!strcmp (search_criterion_name+1,"INCE"))
+					f = mail_criteria_date (&php_searchpgm->searchpgm->since,&r);
+				else if (!strcmp (search_criterion_name+1,"MALLER"))
+					f = mail_criteria_number (&php_searchpgm->searchpgm->smaller,&r);
+				else if (!strcmp (search_criterion_name+1,"UBJECT"))
+					f = mail_criteria_string (&php_searchpgm->searchpgm->subject,&r);
+				break;
+			case 'T':			/* possible TEXT, TO */
+				if (!strcmp (search_criterion_name+1,"EXT"))
+					f = mail_criteria_string (&php_searchpgm->searchpgm->text,&r);
+				else if (!strcmp (search_criterion_name+1,"O"))
+					f = mail_criteria_string (&php_searchpgm->searchpgm->to,&r);
+				break;
+			case 'U':			/* possible UN* */
+				if (search_criterion_name[1] == 'N') {
+					if (!strcmp (search_criterion_name+2,"ANSWERED")) f = php_searchpgm->searchpgm->unanswered = T;
+					else if (!strcmp (search_criterion_name+2,"DELETED")) f = php_searchpgm->searchpgm->undeleted = T;
+					else if (!strcmp (search_criterion_name+2,"DRAFT")) f = php_searchpgm->searchpgm->undraft = T;
+					else if (!strcmp (search_criterion_name+2,"FLAGGED")) f = php_searchpgm->searchpgm->unflagged = T;
+					else if (!strcmp (search_criterion_name+2,"KEYWORD"))
+						f = mail_criteria_string (&php_searchpgm->searchpgm->unkeyword,&r);
+					else if (!strcmp (search_criterion_name+2,"SEEN")) f = php_searchpgm->searchpgm->unseen = T;
+				}
+				break;
+			default:			/* we will barf below */
+				break;
+		}
+		if (! f) {
+			RETURN_FALSE;
+		}
+	}
+
+	efree(criteria);
+
+	RETURN_TRUE;
+}
+
+int mail_criteria_number (unsigned long *number,unsigned char **arg)
+{
+				/* can't double this value */
+  if (*number || !isdigit (**arg)) return NIL;
+  *number = 0;
+  while (isdigit (**arg)) {	/* found a digit? */
+    *number *= 10;		/* add a decade */
+    *number += *(*arg)++ - '0';	/* add number */
+  }
+  return T;
+}
+
 #define GETS_FETCH_SIZE 8196LU
 /* {{{ php_mail_gets */
 static char *php_mail_gets(readfn_t f, void *stream, unsigned long size, GETS_DATA *md)
diff --git a/external/php-5.2.12/ext/imap/php_imap.h b/external/php-5.2.12/ext/imap/php_imap.h
index 8dd60f5..390999f 100644
--- a/external/php-5.2.12/ext/imap/php_imap.h
+++ b/external/php-5.2.12/ext/imap/php_imap.h
@@ -94,6 +94,10 @@ typedef struct _php_imap_message_struct {
 	unsigned long msgid;
 	struct _php_imap_message_struct *next;
 } MESSAGELIST;
+
+typedef struct _php_imap_searchpgm_struct {
+	SEARCHPGM *searchpgm;
+} pisearchpgms;
  
 
 /* Functions */
@@ -169,6 +173,9 @@ PHP_FUNCTION(imap_thread);
 PHP_FUNCTION(imap_timeout);
 
 #if defined(HAVE_IMAP2000) || defined(HAVE_IMAP2001)
+PHP_FUNCTION(imap_searchprogram_new);
+PHP_FUNCTION(imap_searchprogram_or);
+PHP_FUNCTION(imap_searchprogram_criteria);
 PHP_FUNCTION(imap_get_quota);
 PHP_FUNCTION(imap_get_quotaroot);
 PHP_FUNCTION(imap_set_quota);

---

## #46946 — Dominik Gehl — 2010-02-05T00:01:12+00:00

URL: https://externals.io/message/46907#46946

> > […]
> 
> I assume you meant '$pgm' above?

You are absolutely right !

In fact, you can also set every parameter in its own imap_searchprogram_criteria call, so the above (without the typo) would be equivalent to the four calls below:

imap_searchprogram_criteria($pgm,"DRAFT");
imap_searchprogram_criteria($pgm,"SMALLER 34567");
imap_searchprogram_criteria($pgm,"HEADER  Message-Id <D76C58CD-C94D-4DE4-B621-83D66437EFE3@dokdok.com>");
imap_searchprogram_criteria($pgm,"FROM dominik@dokdok.com");

If you need a logical OR of several criteria, you would use the following calls:

$pgm1 = imap_searchprogram_new();
imap_searchprogram_criteria($pgm1,"DRAFT");
$pgm2 = imap_searchprogram_new();
imap_searchprogram_criteria($pgm2,"ANSWERED");
$pgm = imap_searchprogram_or($pgm1, $pgm2);
imap_search($mbox,$pgm);


Dominik

---

## #46980 — Dominik Gehl — 2010-02-12T14:02:39+00:00

URL: https://externals.io/message/46907#46980 · In reply to: #46946

Hi,

I was just wondering if there was any interest to get this enhancement into the PHP IMAP extension. Should I re-sent the patch as an attachment ?

Thanks
Dominik



> > […]
> 
> You are absolutely right !
> 
> In fact, you can also set every parameter in its own imap_searchprogram_criteria call, so the above (without the typo) would be equivalent to the four calls below:
> 
> imap_searchprogram_criteria($pgm,"DRAFT");
> imap_searchprogram_criteria($pgm,"SMALLER 34567");
> imap_searchprogram_criteria($pgm,"HEADER  Message-Id <D76C58CD-C94D-4DE4-B621-83D66437EFE3@dokdok.com>");
> imap_searchprogram_criteria($pgm,"FROM dominik@dokdok.com");
> 
> If you need a logical OR of several criteria, you would use the following calls:
> 
> $pgm1 = imap_searchprogram_new();
> imap_searchprogram_criteria($pgm1,"DRAFT");
> $pgm2 = imap_searchprogram_new();
> imap_searchprogram_criteria($pgm2,"ANSWERED");
> $pgm = imap_searchprogram_or($pgm1, $pgm2);
> imap_search($mbox,$pgm);
> 
> 
> Dominik

---

## #46981 — Pierre Joye — 2010-02-12T15:47:38+00:00

URL: https://externals.io/message/46907#46981 · In reply to: #46980

hi,

There is but I'm not sure these new functions are the way to go. Also
we have been busy with releases lately, just in case you don't follow
the lists too closely, be patient :)

Cheers,


> Hi,
>
> I was just wondering if there was any interest to get this enhancement into the PHP IMAP extension. Should I re-sent the patch as an attachment ?
>
> Thanks
> Dominik
>
> 
>
> > […]
>
>
> --
> 
> 
>
>



-- 
Pierre

@pierrejoye | http://blog.thepimp.net | http://www.libgd.org

---

## #46982 — Dominik Gehl — 2010-02-12T15:55:48+00:00

URL: https://externals.io/message/46907#46982 · In reply to: #46981

Hi,

thanks for the feedback ! Let me know if you have other ideas ... I'm really open to make changes to the patch !

Thanks again,
Dominik



> hi,
> 
> There is but I'm not sure these new functions are the way to go. Also
> we have been busy with releases lately, just in case you don't follow
> the lists too closely, be patient :)
> 
> Cheers,
> 
> 
> > […]
> 
> 
> 
> -- 
> Pierre
> 
> @pierrejoye | http://blog.thepimp.net | http://www.libgd.org
