summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEduardo Chappa <chappa@washington.edu>2018-05-21 20:52:20 -0600
committerEduardo Chappa <chappa@washington.edu>2018-05-21 20:52:20 -0600
commit854ecc5e92a29f14983fc3c9c2edeb4980949568 (patch)
treef0c000b178c16689374441fc4da2a0371b9773c1
parent20e0e95b93f7f1eada65ac6b8e7729534537618c (diff)
downloadalpine-854ecc5e92a29f14983fc3c9c2edeb4980949568.tar.xz
* Add the "g" option to the select command that works in IMAP
servers that implement the X-GM-EXT-1 capability (such as the one offered by Gmail.) This allows users to do selection in Alpine as if they were doing a search in the web interface for Gmail.
-rw-r--r--alpine/mailcmd.c162
-rw-r--r--imap/src/c-client/imap4r1.c244
-rw-r--r--imap/src/c-client/imap4r1.h6
-rw-r--r--imap/src/c-client/mail.c1
-rw-r--r--imap/src/c-client/mail.h1
-rw-r--r--pith/mailcmd.c6
-rw-r--r--pith/pine.hlp7
7 files changed, 418 insertions, 9 deletions
diff --git a/alpine/mailcmd.c b/alpine/mailcmd.c
index b741261b..8f395a1b 100644
--- a/alpine/mailcmd.c
+++ b/alpine/mailcmd.c
@@ -105,6 +105,7 @@ int select_by_number(MAILSTREAM *, MSGNO_S *, SEARCHSET **);
int select_by_thrd_number(MAILSTREAM *, MSGNO_S *, SEARCHSET **);
int select_by_date(MAILSTREAM *, MSGNO_S *, long, SEARCHSET **);
int select_by_text(MAILSTREAM *, MSGNO_S *, long, SEARCHSET **);
+int select_by_gm_content(MAILSTREAM *, MSGNO_S *, long, SEARCHSET **);
int select_by_size(MAILSTREAM *, SEARCHSET **);
SEARCHSET *visible_searchset(MAILSTREAM *, MSGNO_S *);
int select_by_status(MAILSTREAM *, SEARCHSET **);
@@ -137,6 +138,8 @@ ESCKEY_S sel_opts1[] = {
#define SEL_OPTS_THREAD 9 /* index number of "tHread" */
#define SEL_OPTS_THREAD_CH 'h'
+#define SEL_OPTS_XGMEXT 10 /* index number of "boX" */
+#define SEL_OPTS_XGMEXT_CH 'g'
char *sel_pmt2 = "SELECT criteria : ";
static ESCKEY_S sel_opts2[] = {
@@ -199,6 +202,51 @@ static ESCKEY_S sel_opts4[] = {
{-1, 0, NULL, NULL}
};
+static ESCKEY_S sel_opts5[] = {
+ /* TRANSLATORS: very short descriptions of message selection criteria. Select Cur
+ means select the currently highlighted message; select by Number is by message
+ number; Status is by status of the message, for example the message might be
+ New or it might be Unseen or marked Important; Size has the Z upper case because
+ it is a Z command; Keyword is an alpine keyword that has been set by the user;
+ and Rule is an alpine rule */
+ {'a', 'a', "A", N_("select All")},
+ {'c', 'c', "C", N_("select Cur")},
+ {'n', 'n', "N", N_("Number")},
+ {'d', 'd', "D", N_("Date")},
+ {'t', 't', "T", N_("Text")},
+ {'s', 's', "S", N_("Status")},
+ {'z', 'z', "Z", N_("siZe")},
+ {'k', 'k', "K", N_("Keyword")},
+ {'r', 'r', "R", N_("Rule")},
+ {SEL_OPTS_THREAD_CH, 'h', "H", N_("tHread")},
+ {SEL_OPTS_XGMEXT_CH, 'g', "G", N_("GmSearch")},
+ {-1, 0, NULL, NULL},
+ {-1, 0, NULL, NULL},
+ {-1, 0, NULL, NULL},
+ {-1, 0, NULL, NULL},
+ {-1, 0, NULL, NULL}
+};
+
+static ESCKEY_S sel_opts6[] = {
+ {'a', 'a', "A", N_("select All")},
+ /* TRANSLATORS: select currrently highlighted message Thread */
+ {'c', 'c', "C", N_("select Curthrd")},
+ {'n', 'n', "N", N_("Number")},
+ {'d', 'd', "D", N_("Date")},
+ {'t', 't', "T", N_("Text")},
+ {'s', 's', "S", N_("Status")},
+ {'z', 'z', "Z", N_("siZe")},
+ {'k', 'k', "K", N_("Keyword")},
+ {'r', 'r', "R", N_("Rule")},
+ {SEL_OPTS_THREAD_CH, 'h', "H", N_("tHread")},
+ {SEL_OPTS_XGMEXT_CH, 'g', "G", N_("Gmail")},
+ {-1, 0, NULL, NULL},
+ {-1, 0, NULL, NULL},
+ {-1, 0, NULL, NULL},
+ {-1, 0, NULL, NULL},
+ {-1, 0, NULL, NULL}
+};
+
static char *sel_flag =
N_("Select New, Deleted, Answered, Forwarded, or Important messages ? ");
@@ -236,6 +284,8 @@ static ESCKEY_S sel_date_opt[] = {
};
+static char *sel_x_gm_ext =
+ N_("Search: ");
static char *sel_text =
N_("Select based on To, From, Cc, Recip, Partic, Subject fields or All msg text ? ");
static char *sel_text_not =
@@ -6915,12 +6965,24 @@ aggregate_select(struct pine *state, MSGNO_S *msgmap, int q_line, CmdWhere in_in
mm_search_stream = state->mail_stream;
mm_search_count = 0L;
- sel_opts = THRD_INDX() ? sel_opts4 : sel_opts2;
+ if(is_imap_stream(state->mail_stream) && XGMEXT1(state->mail_stream))
+ sel_opts = THRD_INDX() ? sel_opts6 : sel_opts5;
+ else
+ sel_opts = THRD_INDX() ? sel_opts4 : sel_opts2;
+
if(THREADING()){
sel_opts[SEL_OPTS_THREAD].ch = SEL_OPTS_THREAD_CH;
}
else{
- sel_opts[SEL_OPTS_THREAD].ch = -1;
+ if(is_imap_stream(state->mail_stream) && XGMEXT1(state->mail_stream)){
+ sel_opts[SEL_OPTS_THREAD].ch = SEL_OPTS_XGMEXT_CH;
+ sel_opts[SEL_OPTS_THREAD].rval = sel_opts[SEL_OPTS_XGMEXT].rval;
+ sel_opts[SEL_OPTS_THREAD].name = sel_opts[SEL_OPTS_XGMEXT].name;
+ sel_opts[SEL_OPTS_THREAD].label = sel_opts[SEL_OPTS_XGMEXT].label;
+ sel_opts[SEL_OPTS_XGMEXT].ch = -1;
+ }
+ else
+ sel_opts[SEL_OPTS_THREAD].ch = -1;
}
if((old_tot = any_lflagged(msgmap, MN_SLCT)) != 0){
@@ -6944,8 +7006,13 @@ aggregate_select(struct pine *state, MSGNO_S *msgmap, int q_line, CmdWhere in_in
}
sel_opts += 2; /* disable extra options */
- switch(q = radio_buttons(_(sel_pmt1), q_line, sel_opts1, 'c', 'x', NO_HELP,
- RB_NORM)){
+ if(is_imap_stream(state->mail_stream) && XGMEXT1(state->mail_stream))
+ q = double_radio_buttons(_(sel_pmt1), q_line, sel_opts1, 'c', 'x', NO_HELP,
+ RB_NORM);
+ else
+ q = radio_buttons(_(sel_pmt1), q_line, sel_opts1, 'c', 'x', NO_HELP,
+ RB_NORM);
+ switch(q){
case 'f' : /* flip selection */
msgno = 0L;
for(i = 1L; i <= mn_get_total(msgmap); i++){
@@ -6981,8 +7048,12 @@ aggregate_select(struct pine *state, MSGNO_S *msgmap, int q_line, CmdWhere in_in
if(!q){
while(1){
- q = radio_buttons(sel_pmt2, q_line, sel_opts, 'c', 'x',
- NO_HELP, RB_NORM|RB_RET_HELP);
+ if(is_imap_stream(state->mail_stream) && XGMEXT1(state->mail_stream))
+ q = double_radio_buttons(sel_pmt2, q_line, sel_opts, 'c', 'x', NO_HELP,
+ RB_NORM);
+ else
+ q = radio_buttons(sel_pmt2, q_line, sel_opts, 'c', 'x', NO_HELP,
+ RB_NORM|RB_RET_HELP);
if(q == 3){
helper(h_index_cmd_select, _("HELP FOR SELECT"), HLPD_SIMPLE);
@@ -7081,6 +7152,11 @@ aggregate_select(struct pine *state, MSGNO_S *msgmap, int q_line, CmdWhere in_in
rv = select_by_thread(state->mail_stream, msgmap, &limitsrch);
break;
+ case 'g' : /* X-GM-EXT-1 */
+ ret = 0;
+ rv = select_by_gm_content(state->mail_stream, msgmap, mn_get_cur(msgmap), &limitsrch);
+ break;
+
default :
q_status_message(SM_ORDER | SM_DING, 3, 3,
"Unsupported Select option");
@@ -8118,6 +8194,80 @@ select_by_date(MAILSTREAM *stream, MSGNO_S *msgmap, long int msgno, SEARCHSET **
return(0);
}
+/*
+ * Select by searching in message headers or body
+ * using the x-gm-ext-1 capaility. This function
+ * reads the input from the user and passes it to
+ * the server directly. We need a x-gm-ext-1 variable
+ * in the search pgm to carry this information to the
+ * server.
+ *
+ * Sets searched bits in mail_elts
+ *
+ * Args limitsrch -- limit search to this searchset
+ *
+ * Returns 0 on success.
+ */
+int
+select_by_gm_content(MAILSTREAM *stream, MSGNO_S *msgmap, long int msgno, SEARCHSET **limitsrch)
+{
+ int r, we_cancel = 0, rv;
+ char tmp[128];
+ char namehdr[MAILTMPLEN];
+ ESCKEY_S ekey[8];
+ HelpType help;
+
+ ps_global->mangled_footer = 1;
+ ekey[0].ch = ekey[1].ch = ekey[2].ch = ekey[3].ch = -1;
+
+ strncpy(tmp, sel_x_gm_ext, sizeof(tmp)-1);
+ tmp[sizeof(tmp)-1] = '\0';
+
+ namehdr[0] = '\0';
+
+ help = NO_HELP;
+ while (1){
+ int flags = OE_APPEND_CURRENT;
+
+ r = optionally_enter(namehdr, -FOOTER_ROWS(ps_global), 0,
+ sizeof(namehdr), tmp, ekey, help, &flags);
+
+ if(r == 4)
+ continue;
+
+ if(r == 3){
+ help = (help == NO_HELP) ? h_select_by_gm_content : NO_HELP;
+ continue;
+ }
+
+ if (r == 1){
+ cmd_cancelled("Selection by content");
+ return(1);
+ }
+
+ removing_leading_white_space(namehdr);
+
+ if ((namehdr[0] != '\0')
+ && isspace((unsigned char) namehdr[strlen(namehdr) - 1]))
+ removing_trailing_white_space(namehdr);
+
+ if (namehdr[0] != '\0')
+ break;
+ }
+
+ if(ps_global && ps_global->ttyo){
+ blank_keymenu(ps_global->ttyo->screen_rows - 2, 0);
+ ps_global->mangled_footer = 1;
+ }
+
+ we_cancel = busy_cue(_("Selecting"), NULL, 1);
+
+ rv = agg_text_select(stream, msgmap, 'g', namehdr, 0, 0, NULL, "utf-8", limitsrch);
+ if(we_cancel)
+ cancel_busy_cue(0);
+
+ return(rv);
+}
/*
* Select by searching in message headers or body.
diff --git a/imap/src/c-client/imap4r1.c b/imap/src/c-client/imap4r1.c
index 13aaadab..343e29bf 100644
--- a/imap/src/c-client/imap4r1.c
+++ b/imap/src/c-client/imap4r1.c
@@ -1,7 +1,7 @@
/*
- * Copyright 2016 Eduardo Chappa
+ * Copyright 2016-2018 Eduardo Chappa
*
- * Last Edited: February 1, 2015 Eduardo Chappa <chappa@gmx.com>
+ * Last Edited: May 5, 2018 Eduardo Chappa <alpine.chappa@gmx.com>
*
*/
/* ========================================================================
@@ -191,6 +191,7 @@ unsigned long imap_uid (MAILSTREAM *stream,unsigned long msgno);
unsigned long imap_msgno (MAILSTREAM *stream,unsigned long uid);
void imap_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags);
long imap_search (MAILSTREAM *stream,char *charset,SEARCHPGM *pgm,long flags);
+long imap_search_x_gm_ext1 (MAILSTREAM *stream,char *charset,SEARCHPGM *pgm,long flags);
unsigned long *imap_sort (MAILSTREAM *stream,char *charset,SEARCHPGM *spg,
SORTPGM *pgm,long flags);
THREADNODE *imap_thread (MAILSTREAM *stream,char *type,char *charset,
@@ -2023,6 +2024,10 @@ long imap_search (MAILSTREAM *stream,char *charset,SEARCHPGM *pgm,long flags)
char *s;
IMAPPARSEDREPLY *reply;
MESSAGECACHE *elt;
+
+ if(LOCAL->cap.x_gm_ext1 && pgm && pgm->x_gm_ext1)
+ return imap_search_x_gm_ext1(stream, charset, pgm, flags);
+
if ((flags & SE_NOSERVER) || /* if want to do local search */
LOCAL->loser || /* or loser */
(!LEVELIMAP4 (stream) && /* or old server but new functions... */
@@ -3339,7 +3344,239 @@ IMAPPARSEDREPLY *imap_send_literal (MAILSTREAM *stream,char *tag,char **s,
}
return NIL; /* success */
}
-
+
+#define ADD_STRING(X, Y) { \
+ if(remain > 0){ \
+ sprintf (u, (X), (Y)); \
+ len = strlen(u); \
+ if(len < remain){ \
+ remain -= len; \
+ strncpy(t, u, strlen(u)); \
+ t[strlen(u)] = '\0'; \
+ t += strlen (t); \
+ } \
+ } \
+ }
+
+
+long imap_search_x_gm_ext1 (MAILSTREAM *stream, char *charset, SEARCHPGM *pgm, long flags)
+{
+ char *cmd = (flags & SE_UID) ? "UID SEARCH X-GM-RAW" : "SEARCH X-GM-RAW";
+ char *p, *t, s[MAILTMPLEN+1], u[MAILTMPLEN], v[MAILTMPLEN];
+ IMAPARG *args[4],apgm,aatt,achs;;
+ IMAPPARSEDREPLY *reply;
+ unsigned long i,j,k;
+ MESSAGECACHE *elt;
+ size_t remain = sizeof(s) - 1, len;
+ NETMBX mb;
+
+ u[0] = s[0] = '\0';
+ t = s;
+ args[1] = args[2] = args[3] = NIL;
+
+ if(pgm->x_gm_ext1)
+ ADD_STRING(" %s", pgm->x_gm_ext1->text.data);
+
+#if 0 /* maybe later */
+ if (pgm->larger)
+ ADD_STRING(" larger:%lu", pgm->larger);
+
+ if (pgm->smaller)
+ ADD_STRING(" smaller:%lu", pgm->smaller);
+
+ if (pgm->deleted)
+ ADD_STRING(" %s", "in:trash");
+
+ if (pgm->undeleted)
+ ADD_STRING(" %s", "-in:trash");
+
+ if (pgm->draft)
+ ADD_STRING(" %s", "in:drafts");
+
+ if (pgm->undraft)
+ ADD_STRING(" %s", "-in:drafts");
+
+ if (pgm->flagged)
+ ADD_STRING(" %s", "is:starred");
+
+ if (pgm->unflagged)
+ ADD_STRING(" %s", "-is:starred");
+
+ if (pgm->seen)
+ ADD_STRING(" %s", "-is:unread");
+
+ if (pgm->unseen)
+ ADD_STRING(" %s", "is:unread");
+
+ if (pgm->keyword){
+ STRINGLIST *sl;
+ for(sl = pgm->keyword; remain > 0 && sl; sl = sl->next)
+ ADD_STRING(" label:%s", sl->text.data);
+ }
+
+ if (pgm->unkeyword){
+ STRINGLIST *sl;
+ for(sl = pgm->unkeyword; remain > 0 && sl; sl = sl->next)
+ ADD_STRING(" -label:%s", sl->text.data);
+ }
+
+ if (pgm->sentbefore){
+ unsigned short date = pgm->sentbefore;
+ sprintf(v, "%d/%d/%d", BASEYEAR + (date >> 9),
+ (date >> 5) & 0xf, date & 0x1f);
+ ADD_STRING(" before:%s", v);
+ }
+
+ if (pgm->sentsince){
+ unsigned short date = pgm->sentsince;
+ sprintf(v, "%d/%d/%d", BASEYEAR + (date >> 9),
+ (date >> 5) & 0xf, date & 0x1f);
+ ADD_STRING(" older:%s", v);
+ }
+
+ if (pgm->before){
+ unsigned short date = pgm->before;
+ sprintf(v, "%d/%d/%d", BASEYEAR + (date >> 9),
+ (date >> 5) & 0xf, date & 0x1f);
+ ADD_STRING(" before:%s", v);
+ }
+
+ if (pgm->since){
+ unsigned short date = pgm->since;
+ sprintf(v, "%d/%d/%d", BASEYEAR + (date >> 9),
+ (date >> 5) & 0xf, date & 0x1f);
+ ADD_STRING(" before:%s", v);
+ }
+
+ if (pgm->older){
+ sprintf(v, "%dd", pgm->older/86400);
+ ADD_STRING(" older_than:%s", v);
+ }
+
+ if (pgm->younger){
+ sprintf(v, "%dd", pgm->younger/86400);
+ ADD_STRING(" newer_than:%s", v);
+ }
+
+ if(pgm->bcc){
+ STRINGLIST *sl;
+ ADD_STRING("%s", pgm->bcc->next ? " {" : " ");
+ for(sl = pgm->bcc; remain > 0 && sl; sl = sl->next){
+ ADD_STRING("bcc:%s", sl->text.data);
+ ADD_STRING("%s", sl->next ? " " : "}");
+ }
+ }
+
+ if(pgm->cc){
+ STRINGLIST *sl;
+ ADD_STRING("%s", pgm->cc->next ? " {" : " ");
+ for(sl = pgm->cc; remain > 0 && sl; sl = sl->next){
+ ADD_STRING("cc:%s", sl->text.data);
+ ADD_STRING("%s", sl->next ? " " : "}");
+ }
+ }
+
+ if(pgm->from){
+ STRINGLIST *sl;
+ ADD_STRING("%s", pgm->from->next ? " {" : " ");
+ for(sl = pgm->from; remain > 0 && sl; sl = sl->next){
+ ADD_STRING("from:%s", sl->text.data);
+ ADD_STRING("%s", sl->next ? " " : (pgm->from->next ? "}" : ""));
+ }
+ }
+
+ if(pgm->to){
+ STRINGLIST *sl;
+ ADD_STRING("%s", pgm->to->next ? " {" : " ");
+ for(sl = pgm->to; remain > 0 && sl; sl = sl->next){
+ ADD_STRING("to:%s", sl->text.data);
+ ADD_STRING("%s", sl->next ? " " : (pgm->to->next ? "}" : ""));
+ }
+ }
+
+ if(pgm->subject){
+ STRINGLIST *sl;
+ ADD_STRING("%s", pgm->subject->next ? " {" : " ");
+ for(sl = pgm->subject; remain > 0 && sl; sl = sl->next){
+ ADD_STRING("subject:(%s)", sl->text.data);
+ ADD_STRING("%s", sl->next ? " " : (pgm->subject->next ? "}" : ""));
+ }
+ }
+
+ if(pgm->body){
+ STRINGLIST *sl;
+ ADD_STRING("%s", pgm->body->next ? " {" : " ");
+ for(sl = pgm->body; remain > 0 && sl; sl = sl->next){
+ ADD_STRING(" %s", sl->text.data);
+ ADD_STRING("%s", sl->next ? " " : (pgm->body->next ? "}" : ""));
+ }
+ }
+
+ if (mail_valid_net_parse (stream->mailbox,&mb)){
+ p = strchr(mb.mailbox, '/');
+ ADD_STRING(" in:%s", p ? p+1 : "inbox");
+ }
+
+#endif /* maybe later */
+
+ s[0] = '\"';
+ strcat(t, "\"");
+
+ apgm.type = ATOM; apgm.text = (void *) s;
+ args[0] = &apgm;
+ args[1] = NIL;
+ LOCAL->uidsearch = (flags & SE_UID) ? T : NIL;
+ reply = imap_send (stream,cmd,args);
+ LOCAL->uidsearch = NIL;
+ /* do locally if server won't grok */
+ if (!strcmp (reply->key,"BAD")) {
+ if ((flags & SE_NOLOCAL) ||
+ !mail_search_default (stream,charset,pgm,flags | SE_NOSERVER))
+ return NIL;
+ }
+ else if (!imap_OK (stream,reply)) {
+ mm_log (reply->text,ERROR);
+ return NIL;
+ }
+
+ /* can never pre-fetch with a short cache */
+ if ((k = imap_prefetch) && !(flags & (SE_NOPREFETCH | SE_UID)) &&
+ !stream->scache) { /* only if prefetching permitted */
+ t = LOCAL->tmp; /* build sequence in temporary buffer */
+ *t = '\0'; /* initially nothing */
+ /* search through mailbox */
+ for (i = 1; k && (i <= stream->nmsgs); ++i)
+ /* for searched messages with no envelope */
+ if ((elt = mail_elt (stream,i)) && elt->searched &&
+ !mail_elt (stream,i)->private.msg.env) {
+ /* prepend with comma if not first time */
+ if (LOCAL->tmp[0]) *t++ = ',';
+ sprintf (t,"%lu",j = i);/* output message number */
+ t += strlen (t); /* point at end of string */
+ k--; /* count one up */
+ /* search for possible end of range */
+ while (k && (i < stream->nmsgs) &&
+ (elt = mail_elt (stream,i+1))->searched &&
+ !elt->private.msg.env) i++,k--;
+ if (i != j) { /* if a range */
+ sprintf (t,":%lu",i); /* output delimiter and end of range */
+ t += strlen (t); /* point at end of string */
+ }
+ if ((t - LOCAL->tmp) > (IMAPTMPLEN - 50)) break;
+ }
+ if (LOCAL->tmp[0]) { /* anything to pre-fetch? */
+ /* pre-fetch envelopes for the first imap_prefetch number of messages */
+ if (!imap_OK (stream,reply =
+ imap_fetch (stream,t = cpystr (LOCAL->tmp),FT_NEEDENV +
+ ((flags & SE_NOHDRS) ? FT_NOHDRS : NIL) +
+ ((flags & SE_NEEDBODY) ? FT_NEEDBODY : NIL))))
+ mm_log (reply->text,ERROR);
+ fs_give ((void **) &t); /* flush copy of sequence */
+ }
+ }
+ return LONGT;
+}
+
/* IMAP send search program
* Accepts: MAIL stream
* reply tag
@@ -5663,6 +5900,7 @@ void imap_parse_capabilities (MAILSTREAM *stream,char *t)
else if (!compare_cstring (t,"CATENATE")) LOCAL->cap.catenate = T;
else if (!compare_cstring (t,"CONDSTORE")) LOCAL->cap.condstore = T;
else if (!compare_cstring (t,"ESEARCH")) LOCAL->cap.esearch = T;
+ else if (!compare_cstring (t,"X-GM-EXT-1")) LOCAL->cap.x_gm_ext1 = T;
else if (((t[0] == 'S') || (t[0] == 's')) &&
((t[1] == 'O') || (t[1] == 'o')) &&
((t[2] == 'R') || (t[2] == 'r')) &&
diff --git a/imap/src/c-client/imap4r1.h b/imap/src/c-client/imap4r1.h
index 8ee8a186..520b6562 100644
--- a/imap/src/c-client/imap4r1.h
+++ b/imap/src/c-client/imap4r1.h
@@ -87,6 +87,7 @@ typedef struct imap_cap {
unsigned int esearch : 1; /* server has ESEARCH (RFC 4731) */
unsigned int within : 1; /* server has WITHIN (RFC 5032) */
unsigned int extlevel; /* extension data level supported by server */
+ unsigned int x_gm_ext1:1; /* special extension for gmail server */
/* supported authenticators */
unsigned int auth : MAXAUTHENTICATORS;
THREADER *threader; /* list of threaders */
@@ -129,6 +130,11 @@ typedef struct imap_cap {
#define LEVELACL(stream) imap_cap (stream)->acl
+/* Has X-GM-EXT-1 extension */
+
+#define XGMEXT1(stream) imap_cap(stream)->x_gm_ext1
+
+
/* Has QUOTA extension */
#define LEVELQUOTA(stream) imap_cap (stream)->quota
diff --git a/imap/src/c-client/mail.c b/imap/src/c-client/mail.c
index 02519e5a..b18f7732 100644
--- a/imap/src/c-client/mail.c
+++ b/imap/src/c-client/mail.c
@@ -5990,6 +5990,7 @@ void mail_free_searchpgm (SEARCHPGM **pgm)
mail_free_stringlist (&(*pgm)->subject);
mail_free_stringlist (&(*pgm)->text);
mail_free_stringlist (&(*pgm)->to);
+ mail_free_stringlist (&(*pgm)->x_gm_ext1);
fs_give ((void **) pgm); /* return program to free storage */
}
}
diff --git a/imap/src/c-client/mail.h b/imap/src/c-client/mail.h
index 11ebdbc9..fc3f3862 100644
--- a/imap/src/c-client/mail.h
+++ b/imap/src/c-client/mail.h
@@ -996,6 +996,7 @@ SEARCHPGM { /* search program */
STRINGLIST *subject; /* text in subject */
STRINGLIST *text; /* text in headers and body */
STRINGLIST *to; /* to recipients */
+ STRINGLIST *x_gm_ext1; /* use X-GM-EXT-1 extension */
unsigned long larger; /* larger than this size */
unsigned long smaller; /* smaller than this size */
unsigned long older; /* older than this interval */
diff --git a/pith/mailcmd.c b/pith/mailcmd.c
index 609578fc..c5544f20 100644
--- a/pith/mailcmd.c
+++ b/pith/mailcmd.c
@@ -2342,6 +2342,12 @@ agg_text_select(MAILSTREAM *stream, MSGNO_S *msgmap, char type, char *namehdr,
if(!mepgm)
switch(type){
+ case 'g' : /* X-GM-EXT-1 */
+ pgm->x_gm_ext1 = mail_newstringlist();
+ pgm->x_gm_ext1->text.data = (unsigned char *) cpystr(namehdr);
+ pgm->x_gm_ext1->text.size = strlen(namehdr);
+ break;
+
case 'h' : /* Any header */
pgm->header = mail_newsearchheader (namehdr, sstring);
break;
diff --git a/pith/pine.hlp b/pith/pine.hlp
index a1d8ea1f..104ffa73 100644
--- a/pith/pine.hlp
+++ b/pith/pine.hlp
@@ -196,6 +196,10 @@ these contributed by Helmut Grohne. See
password file encryption key. This allows users to use their password file
without entering a master password.
+<LI> Add the &quot;g&quot; option to the select command that works in IMAP
+servers that implement the X-GM-EXT-1 capability (such as the one offered
+by Gmail.) This allows users to do selection in Alpine as if they were
+doing a search in the web interface for Gmail.
</UL>
<P>
@@ -36847,6 +36851,9 @@ attachment, or ^C to cancel.
========== h_select_by_num ==========
Enter a list of message numbers (or number ranges), or ^C to cancel. "end"
is the last message. "." is the current message. Example: 1-.,7-9,11,19,35-end
+========== h_select_by_gm_content ==========
+Enter your search key in the same way that you would enter a search
+key in the web interface for Gmail.
========== h_select_by_thrdnum ==========
Enter a list of message numbers (or number ranges), or ^C to cancel. "end"
is the last message. "." is the current message. Example: 1-.,7-9,11,19,35-end