tizen 2.3.1 release tizen_2.3.1 submit/tizen_2.3.1/20150915.075542 tizen_2.3.1_release
authorjk7744.park <jk7744.park@samsung.com>
Tue, 8 Sep 2015 13:07:27 +0000 (22:07 +0900)
committerjk7744.park <jk7744.park@samsung.com>
Tue, 8 Sep 2015 13:07:27 +0000 (22:07 +0900)
23 files changed:
.gitignore [new file with mode: 0755]
Makefile.am
imap-2007e/c-client/Makefile
imap-2007e/c-client/auth_pla.c
imap-2007e/c-client/auth_xoauth2.c [new file with mode: 0755]
imap-2007e/c-client/auths.c
imap-2007e/c-client/imap4r1.c
imap-2007e/c-client/imap4r1.h
imap-2007e/c-client/linkage.c
imap-2007e/c-client/linkage.h
imap-2007e/c-client/lnx_mail.c
imap-2007e/c-client/mail.c
imap-2007e/c-client/mail.h
imap-2007e/c-client/osdep.c
imap-2007e/c-client/osdepssl.c
imap-2007e/c-client/pop3.c
imap-2007e/c-client/rfc822.c
imap-2007e/c-client/rfc822.h
imap-2007e/c-client/smtp.c
imap-2007e/c-client/ssl_unix.c
imap-2007e/c-client/tcp_unix.c
libuw-imap-toolkit.manifest [new file with mode: 0644]
packaging/uw-imap-toolkit.spec

diff --git a/.gitignore b/.gitignore
new file mode 100755 (executable)
index 0000000..6c53821
--- /dev/null
@@ -0,0 +1,12 @@
+build_log
+*.log
+*.pyc
+usr
+opt
+*.o
+*.os
+*.exe
+packages
+binaries
+*.ipk
+*~
index 063cbb3..db8d2db 100755 (executable)
@@ -31,6 +31,7 @@ libuw_imap_toolkit_la_CPPFLAGS= -I. \
                            -D__FEATURE_HEADER_OPTIMIZATION__ \
                            -D__FEATURE_SEND_OPTMIZATION__ \
                            -D__FEATURE_XLIST_SUPPORT__ \
+                           -D__FEATURE_SUPPORT_IMAP_ID__ \
                            -DCREATEPROTO=unixproto -DEMPTYPROTO=unixproto \
                            -DMAILSPOOL=\"/var/spool/mail\" \
                            -DANONYMOUSHOME=\"/var/spool/mail/anonymous\" \
@@ -48,8 +49,9 @@ libuw_imap_toolkit_la_CPPFLAGS= -I. \
                            -DDATA_PATH=\"/opt/.email/res\" \
                            -DEXPORT_API="__attribute__((visibility(\"default\")))"
  
-#                              -DFEATURE_CORE_DEBUG 
-#               -D__NON_BLOCKING_SSL_WRITE__ 
+#-DFEATURE_CORE_DEBUG 
+#-D__NON_BLOCKING_SSL_WRITE__ 
+#-D__FEATURE_METADATA_SUPPORT__
                            
 libuw_imap_toolkit_la_LIBADD = -L$(prefix)/lib \
                               $(SSL_LIBS) 
index 090c8f2..3c043c0 100755 (executable)
@@ -903,7 +903,7 @@ osdep.o:mail.h misc.h env.h fs.h ftl.h nl.h tcp.h \
        gethstid.c getspnam.c \
        gr_wait.c gr_wait4.c gr_waitp.c \
        kerb_mit.c \
-       auth_ext.c auth_gss.c auth_log.c auth_md5.c auth_pla.c \
+       auth_ext.c auth_gss.c auth_log.c auth_md5.c auth_pla.c auth_xoauth2.c\
        pmatch.c scandir.c setpgrp.c strerror.c truncate.c write.c \
        memmove.c memmove2.c memset.c \
        tz_bsd.c tz_nul.c tz_sv4.c \
index 4813c4c..0327e91 100755 (executable)
@@ -65,61 +65,67 @@ long auth_plain_client (authchallenge_t challenger,authrespond_t responder,
                                /* get initial (empty) challenge */
   if (challenge = (*challenger) (stream,&clen)) {
     fs_give ((void **) &challenge);
-    if (clen) {                        /* abort if challenge non-empty */
-      mm_log ("auth_plain_client : Server bug: non-empty initial PLAIN challenge 1",WARN);
-      (*responder) (stream,NIL,0);
-      ret = LONGT;             /* will get a BAD response back */
-    }
     pwd[0] = NIL;              /* prompt user if empty challenge */
     mm_login (mb,user,pwd,*trial);
 
-    if (!pwd[0]) {             /* empty challenge or user requested abort */
-      (*responder) (stream,NIL,0);
-      *trial = 0;              /* cancel subsequent attempts */
-      ret = LONGT;             /* will get a BAD response back */
+    if (clen) {
+      mm_log ("Enter section for handling non-emtpy challenge",WARN);
+      if ((*responder) (stream,user,strlen (user)) &&
+           (challenge = (*challenger) (stream,&clen))) {
+        fs_give ((void **) &challenge);
+           /* send password */
+        if ((*responder) (stream,pwd,strlen (pwd))) {
+          if (challenge = (*challenger) (stream,&clen))
+            fs_give ((void **) &challenge);
+          else {
+            ++*trial;          /* can try again if necessary */
+            ret = LONGT;               /* check the authentication */
+          }
+        }
+      }
     }
     else {
-      unsigned long rlen = 
-       strlen (mb->authuser) + strlen (user) + strlen (pwd) + 2;
-      char *response = (char *) fs_get (rlen);
-      char *t = response;      /* copy authorization id */
+      if (!pwd[0]) {           /* empty challenge or user requested abort */
+        (*responder) (stream,NIL,0);
+        *trial = 0;            /* cancel subsequent attempts */
+        ret = LONGT;           /* will get a BAD response back */
+      }
+      else {
+        unsigned long rlen =
+        strlen (mb->authuser) + strlen (user) + strlen (pwd) + 2;
+        char *response = (char *) fs_get (rlen);
+        char *t = response;    /* copy authorization id */
 
-       memset(response, 0x00, rlen);
+        memset(response, 0x00, rlen);
 
-      if (mb->authuser[0]) for (u = user; *u; *t++ = *u++);
-      *t++ = '\0';             /* delimiting NUL */
+        if (mb->authuser[0]) for (u = user; *u; *t++ = *u++);
+        *t++ = '\0';           /* delimiting NUL */
 
                                /* copy authentication id */
-      for (u = mb->authuser[0] ? mb->authuser : user; *u; *t++ = *u++);
-      *t++ = '\0';             /* delimiting NUL */
+        for (u = mb->authuser[0] ? mb->authuser : user; *u; *t++ = *u++);
+        *t++ = '\0';           /* delimiting NUL */
 
                                /* copy password */
-      for (u = pwd; *u; *t++ = *u++);
+        for (u = pwd; *u; *t++ = *u++);
                                /* send credentials */
        
-      if ((*responder) (stream,response,rlen)) {
-       if (challenge = (*challenger) (stream,&clen))
-       {
-         fs_give ((void **) &challenge);
-       }
-       else {
-         ++*trial;             /* can try again if necessary */
-         ret = LONGT;          /* check the authentication */
-       }
+        if ((*responder) (stream,response,rlen)) {
+          if (challenge = (*challenger) (stream,&clen)) {
+            fs_give ((void **) &challenge);
+          }
+          else {
+            ++*trial;          /* can try again if necessary */
+            ret = LONGT;               /* check the authentication */
+          }
+        }
+        memset (response,0,rlen);      /* erase credentials */
+        fs_give ((void **) &response);
       }
-      memset (response,0,rlen);        /* erase credentials */
-      fs_give ((void **) &response);
     }
   }
-  else // [ Written by Kyuho Jo for AOL 2010/02/16
+  else
   {
        mm_log ("Enter section for handling emtpy challenge",WARN);
-    if (clen) 
-       {                       /* abort if challenge non-empty */
-      mm_log ("auth_plain_client : Server bug: non-empty initial PLAIN challenge 2",WARN);
-      (*responder) (stream,NIL,0);
-      ret = LONGT;             /* will get a BAD response back */
-    }
     pwd[0] = NIL;              /* prompt user if empty challenge */
 
        mm_login (mb,user,pwd,*trial);
@@ -155,7 +161,7 @@ long auth_plain_client (authchallenge_t challenger,authrespond_t responder,
              fs_give ((void **) &challenge);
            else 
            {
-             mm_log ("Second emtpy challege  ",WARN);
+             mm_log ("Second empty challege  ",WARN);
              ++*trial;         /* can try again if necessary */
              ret = LONGT;              /* check the authentication */
            }
@@ -164,7 +170,7 @@ long auth_plain_client (authchallenge_t challenger,authrespond_t responder,
       fs_give ((void **) &response);
     }
        
-  }    // ] Written by Kyuho Jo for AOL 2010/02/16
+  }
 
   memset (pwd,0,MAILTMPLEN);   /* erase password */
   if (!ret) *trial = 65535;    /* don't retry if bad protocol */
diff --git a/imap-2007e/c-client/auth_xoauth2.c b/imap-2007e/c-client/auth_xoauth2.c
new file mode 100755 (executable)
index 0000000..ff5f415
--- /dev/null
@@ -0,0 +1,111 @@
+/* ========================================================================
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 
+ * ========================================================================
+ */
+
+/*
+ * Program:    XOAUTH2 authenticator
+ *
+ * Author:     Kyuho Jo (kyuho.jo@samsung.com)
+ *
+ * Date:       18 May 2013
+ */
+\f
+long auth_xoauth2_client (authchallenge_t challenger,authrespond_t responder,
+                       char *service,NETMBX *mb,void *stream,
+                       unsigned long *trial,char *user);
+char *auth_xoauth2_server (authresponse_t responder,int argc,char *argv[]);
+
+AUTHENTICATOR auth_xoauth2 = {
+  AU_AUTHUSER | AU_HIDE,       /* allow authuser, hidden */
+  "XOAUTH2",                   /* authenticator name */
+  NIL,                         /* always valid */
+  auth_xoauth2_client,         /* client method */
+  auth_xoauth2_server,         /* server method */
+  NIL                          /* next authenticator */
+};
+\f
+/* Client authenticator
+ * Accepts: challenger function
+ *         responder function
+ *         SASL service name
+ *         parsed network mailbox structure
+ *         stream argument for functions
+ *         pointer to current trial count
+ *         returned user name
+ * Returns: T if success, NIL otherwise, number of trials incremented if retry
+ */
+
+#define MAX_RESPONSE_LENGTH 1024
+#define CHAR_SOH            0x01
+
+long auth_xoauth2_client (authchallenge_t challenger,authrespond_t responder,
+                       char *service,NETMBX *mb,void *stream,
+                       unsigned long *trial,char *user)
+{
+       char *u, token[MAILTMPLEN] = { 0, };
+       void *challenge;
+       unsigned long clen;
+       long ret = NIL;
+
+       if (challenge = (*challenger) (stream,&clen)) {
+               fs_give ((void **) &challenge);
+               if (clen) { /* abort if challenge non-empty */
+                       MM_LOG ("auth_xoauth2_client : non-empty initial XOAUTH2 challenge",WARN);
+                       (*responder) (stream,NIL,0);
+                       ret = LONGT; /* will get a BAD response back */
+               }
+               token[0] = NIL;         /* prompt user if empty challenge */
+               mm_login (mb, user, token, *trial);
+
+               if (!token[0]) {                /* empty challenge or user requested abort */
+                       (*responder) (stream, NIL, 0);
+                       *trial = 0;             /* cancel subsequent attempts */
+                       ret = LONGT;            /* will get a BAD response back */
+               }
+               else {
+                       unsigned long formed_response_length = 0;
+                       char formed_response[MAX_RESPONSE_LENGTH] = { 0, };
+
+                       snprintf(formed_response, MAX_RESPONSE_LENGTH, "user=%s%cauth=Bearer %s%c%c", user, CHAR_SOH, token, CHAR_SOH, CHAR_SOH);
+
+                       MM_LOG(formed_response, TCPDEBUG);
+
+                       if ((*responder) (stream,formed_response,strlen(formed_response))) {
+                               if (challenge = (*challenger) (stream,&clen)) {
+                                       /* print challenge with error code */
+                                       MM_LOG(challenge, ERROR);
+                                       /* and send CR */
+                                       (*responder) (stream, "", 0);
+                                       fs_give ((void **) &challenge);
+                                       ret = LONGT;
+                               }
+                               else {
+                                       ++*trial;
+                                       ret = LONGT;
+                               }
+                       }
+               }
+               memset(token,0,MAILTMPLEN);
+       }
+       return ret;
+}
+\f
+/* Server authenticator
+ * Accepts: responder function
+ *         argument count
+ *         argument vector
+ * Returns: authenticated user name or NIL
+ */
+char *auth_xoauth2_server (authresponse_t responder,int argc,char *argv[])
+{
+       char *ret = NIL;
+       return NIL;
+}
index 3e0e254..5836882 100755 (executable)
@@ -2,3 +2,4 @@
 #include "auth_md5.c"
 #include "auth_pla.c"
 #include "auth_log.c"
+#include "auth_xoauth2.c"
index d9ebdc4..f41f22e 100755 (executable)
@@ -39,6 +39,7 @@
 #include "c-client.h"
 #include "imap4r1.h"
 \f
+
 /* Parameters */
 
 #define IMAPLOOKAHEAD 20       /* envelope lookahead */
@@ -134,6 +135,9 @@ typedef struct imap_argument {
 #define MULTIAPPEND 13
 #define SNLIST 14
 #define MULTIAPPENDREDO 15
+#define LITERAL_CMD 16
+#define LITERAL_MSG 17
+#define LITERAL_END 18
 
 
 /* Append data */
@@ -165,6 +169,9 @@ long imap_status (MAILSTREAM *stream,char *mbx,long flags);
 MAILSTREAM *imap_open (MAILSTREAM *stream);
 IMAPPARSEDREPLY *imap_rimap (MAILSTREAM *stream,char *service,NETMBX *mb,
                             char *usr,char *tmp);
+#ifdef __FEATURE_SUPPORT_IMAP_ID__
+long imap_id (MAILSTREAM *stream);
+#endif /* __FEATURE_SUPPORT_IMAP_ID__ */
 long imap_anon (MAILSTREAM *stream,char *tmp);
 long imap_auth (MAILSTREAM *stream,NETMBX *mb,char *tmp,char *usr);
 long imap_login (MAILSTREAM *stream,NETMBX *mb,char *pwd,char *usr);
@@ -199,7 +206,10 @@ long imap_append_referral (char *mailbox,char *tmp,append_t af,void *data,
                           APPENDDATA *map,long options);
 IMAPPARSEDREPLY *imap_append_single (MAILSTREAM *stream,char *mailbox,
                                     char *flags,char *date,STRING *message);
-\f
+IMAPPARSEDREPLY *imap_append_single_cmd (MAILSTREAM *stream,char *mailbox,
+                                    char *flags,char *date,STRING *message);
+IMAPPARSEDREPLY *imap_append_single_msg (MAILSTREAM *stream,STRING *message);
+IMAPPARSEDREPLY *imap_append_single_end (MAILSTREAM *stream);
 void imap_gc (MAILSTREAM *stream,long gcflags);
 void imap_gc_body (BODY *body);
 void imap_capability (MAILSTREAM *stream);
@@ -212,6 +222,8 @@ IMAPPARSEDREPLY *imap_send_astring (MAILSTREAM *stream,char *tag,char **s,
                                    SIZEDTEXT *as,long wildok,char *limit);
 IMAPPARSEDREPLY *imap_send_literal (MAILSTREAM *stream,char *tag,char **s,
                                    STRING *st);
+IMAPPARSEDREPLY *imap_send_literal_cmd (MAILSTREAM *stream,char *tag,char **s,STRING *st);
+IMAPPARSEDREPLY *imap_send_literal_msg (MAILSTREAM *stream,char *tag,STRING *st);
 IMAPPARSEDREPLY *imap_send_spgm (MAILSTREAM *stream,char *tag,char *base,
                                 char **s,SEARCHPGM *pgm,char *limit);
 char *imap_send_spgm_trim (char *base,char *s,char *text);
@@ -244,9 +256,14 @@ void imap_parse_flags (MAILSTREAM *stream,MESSAGECACHE *elt,
 unsigned long imap_parse_user_flag (MAILSTREAM *stream,char *flag);
 unsigned char *imap_parse_astring (MAILSTREAM *stream,unsigned char **txtptr,
                          IMAPPARSEDREPLY *reply,unsigned long *len);
+typedef enum {
+       IMAP_PARSE_STRING_FLAGS_NONE,
+       IMAP_PARSE_STRING_FLAGS_FOR_NEW_LINE,
+       IMAP_PARSE_STRING_FLAGS_FOR_DOUBLE_QUOTE,
+} imap_parse_string_flags;
 unsigned char *imap_parse_string (MAILSTREAM *stream,unsigned char **txtptr,
                                  IMAPPARSEDREPLY *reply,GETS_DATA *md,
-                                 unsigned long *len,long flags);
+                                 unsigned long *len,imap_parse_string_flags flags);
 void imap_parse_body (GETS_DATA *md,char *seg,unsigned char **txtptr,
                      IMAPPARSEDREPLY *reply);
 void imap_parse_body_structure (MAILSTREAM *stream,BODY *body,
@@ -366,7 +383,8 @@ void *imap_parameters (long function,void *value)
       ((IMAPLOCAL *) ((MAILSTREAM *) value)->local)->cap.threader;
     break;
   case SET_FETCHLOOKAHEAD:     /* must use pointer from GET_FETCHLOOKAHEAD */
-    fatal ("SET_FETCHLOOKAHEAD not permitted");
+    MM_FATAL("SET_FETCHLOOKAHEAD not permitted");
+    return NIL;
   case GET_FETCHLOOKAHEAD:
     value = (void *) &((IMAPLOCAL *) ((MAILSTREAM *) value)->local)->lookahead;
     break;
@@ -445,7 +463,8 @@ void *imap_parameters (long function,void *value)
     break;
 \f
   case SET_IDLETIMEOUT:
-    fatal ("SET_IDLETIMEOUT not permitted");
+    MM_FATAL("SET_IDLETIMEOUT not permitted");
+    return NIL;
   case GET_IDLETIMEOUT:
     value = (void *) IDLETIMEOUT;
     break;
@@ -552,7 +571,7 @@ void imap_list_work (MAILSTREAM *stream,char *cmd,char *ref,char *pat,
       acont.type = ASTRING; acont.text = (void *) contents;
       imap_send (stream,cmd,args);
     }
-    else mm_log ("Scan not valid on this IMAP server",ERROR);
+    else MM_LOG ("Scan not valid on this IMAP server",ERROR);
   }
 \f
   else if (LEVELIMAP4 (stream)){/* easy if IMAP4 */
@@ -703,13 +722,15 @@ long imap_manage (MAILSTREAM *stream,char *mailbox,char *command,char *arg2)
       case 'D': code = REFDELETE; break;
       case 'R': code = REFRENAME; break;
       default:
-       fatal ("impossible referral command");
+         MM_FATAL("impossible referral command");
+         if (st != stream) mail_close (stream);
+         return NIL;
       }
       if ((code >= 0) && (mailbox = (*ir) (stream,LOCAL->referral,code)))
        ret = imap_manage (NIL,mailbox,command,(*command == 'R') ?
                           (mailbox + strlen (mailbox) + 1) : NIL);
     }
-    mm_log (reply->text,ret ? NIL : ERROR);
+    MM_LOG (reply->text,ret ? NIL : ERROR);
                                /* toss out temporary stream */
     if (st != stream) mail_close (stream);
   }
@@ -813,7 +834,7 @@ MAILSTREAM *imap_open (MAILSTREAM *stream)
        sprintf (tmp,"Reusing connection to %s",net_host (LOCAL->netstream));
        if (LOCAL->user) sprintf (tmp + strlen (tmp),"/user=\"%s\"",
                                  LOCAL->user);
-       if (!stream->silent) mm_log (tmp,(long) NIL);
+       if (!stream->silent) MM_LOG (tmp,(long) NIL);
                                /* unselect if now want halfopen */
        if (stream->halfopen) imap_send (stream,"UNSELECT",NIL);
       }
@@ -879,7 +900,7 @@ MAILSTREAM *imap_open (MAILSTREAM *stream)
                                /* make sure greeting is good */
     if (!reply || strcmp (reply->tag,"*") ||
        (strcmp (reply->key,"OK") && strcmp (reply->key,"PREAUTH"))) {
-      if (reply) mm_log (reply->text,ERROR);
+      if (reply) MM_LOG (reply->text,ERROR);
       return NIL;              /* lost during greeting */
     }
 \f
@@ -905,7 +926,7 @@ MAILSTREAM *imap_open (MAILSTREAM *stream)
        if (LOCAL->netstream) imap_capability (stream);
       }
       else if (mb.tlsflag) {   /* user specified /tls but can't do it */
-       mm_log ("Unable to negotiate TLS with this server",ERROR);
+       MM_LOG ("Unable to negotiate TLS with this server",ERROR);
        return NIL;
       }
       if (LOCAL->netstream) {  /* still in the land of the living? */
@@ -916,10 +937,20 @@ MAILSTREAM *imap_open (MAILSTREAM *stream)
                   net_host (LOCAL->netstream),NETMAXHOST-1);
          mb.host[NETMAXHOST-1] = '\0';
        }
+
+
+#ifdef __FEATURE_SUPPORT_IMAP_ID__
+       /* Process for IMAP ID */
+       if (LOCAL->cap.id) {
+               MM_LOG ("This server requires IMAP ID", NIL);
+               imap_id(stream);
+       }
+#endif /* __FEATURE_SUPPORT_IMAP_ID__ */
+
                                /* need new capabilities after login */
        LOCAL->gotcapability = NIL;
        if (!(stream->anonymous ? imap_anon (stream,tmp) :
-             (LOCAL->cap.auth ? imap_auth (stream,&mb,tmp,usr) :
+             ((LOCAL->cap.auth && (mb.auth_method > AUTH_METHOD_NONE))? imap_auth (stream,&mb,tmp,usr) :
               imap_login (stream,&mb,tmp,usr)))) {
                                /* failed, is there a referral? */
          if (ir && LOCAL->referral &&
@@ -990,7 +1021,7 @@ MAILSTREAM *imap_open (MAILSTREAM *stream)
                                             "EXAMINE": "SELECT",args))) {
        strcat (tmp,mb.mailbox);/* mailbox name */
        if (!stream->nmsgs && !stream->silent)
-         mm_log ("Mailbox is empty",(long) NIL);
+         MM_LOG ("Mailbox is empty",(long) NIL);
                                /* note if an INBOX or not */
        stream->inbox = !compare_cstring (mb.mailbox,"INBOX");
       }
@@ -1002,7 +1033,7 @@ MAILSTREAM *imap_open (MAILSTREAM *stream)
        return imap_open (stream);
       }
       else {
-       mm_log (reply->text,ERROR);
+       MM_LOG (reply->text,ERROR);
        if (imap_closeonerror) return NIL;
        stream->halfopen = T;   /* let him keep it half-open */
       }
@@ -1062,6 +1093,41 @@ IMAPPARSEDREPLY *imap_rimap (MAILSTREAM *stream,char *service,NETMBX *mb,
   }
   return NIL;
 }
+
+#ifdef __FEATURE_SUPPORT_IMAP_ID__
+long imap_id (MAILSTREAM *stream)
+{
+       IMAPPARSEDREPLY *reply;
+       int ret = NIL;
+       char *imap_id_tag_string = NULL;
+       IMAPARG *args[2];
+       IMAPARG id_tag;
+
+       if(stream == NULL)
+               return ret;
+
+       mm_imap_id (&imap_id_tag_string);
+
+       if(imap_id_tag_string != NULL) {
+               if(stream->debug)
+                       mm_dlog(imap_id_tag_string);
+
+               id_tag.type = ASTRING;
+               id_tag.text = (void *) imap_id_tag_string;
+               args[0] = &id_tag; args[1] = NIL;
+               /* send "ID tag" */
+               if (imap_OK (stream,reply = imap_send (stream,"ID",args)))
+                       ret = LONGT;            /* success */
+               else {
+                       MM_LOG ("ID failed",ERROR);
+               }
+               free(imap_id_tag_string);
+       }
+
+       return ret;
+}
+#endif /* __FEATURE_SUPPORT_IMAP_ID__ */
+
 \f
 /* IMAP log in as anonymous
  * Accepts: stream to authenticate
@@ -1081,7 +1147,7 @@ long imap_anon (MAILSTREAM *stream,char *tmp)
                                /* build command */
     sprintf (tmp,"%s AUTHENTICATE ANONYMOUS",tag);
     if (!imap_soutr (stream,tmp)) {
-      mm_log (broken,ERROR);
+      MM_LOG (broken,ERROR);
       return NIL;
     }
     if (imap_challenge (stream,&i)) imap_response (stream,s,strlen (s));
@@ -1105,7 +1171,7 @@ long imap_anon (MAILSTREAM *stream,char *tmp)
   }
                                /* success if reply OK */
   if (imap_OK (stream,reply)) return T;
-  mm_log (reply->text,ERROR);
+  MM_LOG (reply->text,ERROR);
   return NIL;
 }
 \f
@@ -1127,10 +1193,17 @@ long imap_auth (MAILSTREAM *stream,NETMBX *mb,char *tmp,char *usr)
   IMAPPARSEDREPLY *reply;
   for (ua = LOCAL->cap.auth, LOCAL->saslcancel = NIL; LOCAL->netstream && ua &&
        (at = mail_lookup_auth (find_rightmost_bit (&ua) + 1));) {
+
+    if (mb->auth_method != AUTH_METHOD_XOAUTH2 && strstr(at->name, auth_xoauth2.name) ) {
+         sprintf (tmp,"auth_method is not AUTH_METHOD_XOAUTH2. So skipped XOAUTH authenticator.");
+         MM_LOG (tmp,NIL);
+         continue;
+       }
+
     if (lsterr) {              /* previous authenticator failed? */
       sprintf (tmp,"Retrying using %s authentication after %.80s",
               at->name,lsterr);
-      mm_log (tmp,NIL);
+      MM_LOG (tmp,NIL);
       fs_give ((void **) &lsterr);
     }
     trial = 0;                 /* initial trial count */
@@ -1138,7 +1211,7 @@ long imap_auth (MAILSTREAM *stream,NETMBX *mb,char *tmp,char *usr)
     do {                       /* gensym a new tag */
       if (lsterr) {            /* previous attempt with this one failed? */
        sprintf (tmp,"Retrying %s authentication after %.80s",at->name,lsterr);
-       mm_log (tmp,WARN);
+       MM_LOG (tmp,WARN);
        fs_give ((void **) &lsterr);
       }
       LOCAL->saslcancel = NIL;
@@ -1161,7 +1234,7 @@ long imap_auth (MAILSTREAM *stream,NETMBX *mb,char *tmp,char *usr)
                                /* good if SASL ok and success response */
        if (ok && imap_OK (stream,reply)) return T;
        if (!trial) {           /* if main program requested cancellation */
-         mm_log ("IMAP Authentication cancelled",ERROR);
+         MM_LOG ("IMAP Authentication cancelled",ERROR);
          return NIL;
        }
                                /* no error if protocol-initiated cancel */
@@ -1174,7 +1247,7 @@ long imap_auth (MAILSTREAM *stream,NETMBX *mb,char *tmp,char *usr)
   if (lsterr) {                        /* previous authenticator failed? */
     if (!LOCAL->saslcancel) {  /* don't do this if a cancel */
       sprintf (tmp,"Can not authenticate to IMAP server: %.80s",lsterr);
-      mm_log (tmp,ERROR);
+      MM_LOG (tmp,ERROR);
     }
     fs_give ((void **) &lsterr);
   }
@@ -1197,12 +1270,12 @@ long imap_login (MAILSTREAM *stream,NETMBX *mb,char *pwd,char *usr)
   IMAPARG ausr,apwd;
   long ret = NIL;
   if (stream->secure)          /* never do LOGIN if want security */
-    mm_log ("Can't do secure authentication with this server",ERROR);
+    MM_LOG ("Can't do secure authentication with this server",ERROR);
                                /* never do LOGIN if server disabled it */
   else if (LOCAL->cap.logindisabled)
-    mm_log ("Server disables LOGIN, no recognized SASL authenticator",ERROR);
+    MM_LOG ("Server disables LOGIN, no recognized SASL authenticator",ERROR);
   else if (mb->authuser[0])    /* never do LOGIN with /authuser */
-    mm_log ("Can't do /authuser with this server",ERROR);
+    MM_LOG ("Can't do /authuser with this server",ERROR);
   else {                       /* OK to try login */
     ausr.type = apwd.type = ASTRING;
     ausr.text = (void *) usr;
@@ -1217,14 +1290,18 @@ long imap_login (MAILSTREAM *stream,NETMBX *mb,char *pwd,char *usr)
        if (imap_OK (stream,reply = imap_send (stream,"LOGIN",args)))
          ret = LONGT;          /* success */
        else {
-         mm_log (reply->text,WARN);
+         MM_LOG (reply->text,WARN);
+         if (reply->text && strstr(reply->text, "AUTHENTICATIONFAILED")) {
+                 MM_LOG ("Can not authenticate",ERROR);
+                 break;
+         }
          if (!LOCAL->referral && (trial == imap_maxlogintrials))
-           mm_log ("Too many login failures",ERROR);
+           MM_LOG ("Too many login failures",ERROR);
        }
        LOCAL->sensitive = NIL; /* unhide */
       }
                                /* user refused to give password */
-      else mm_log ("Login aborted",ERROR);
+      else MM_LOG ("Login aborted",ERROR);
     } while (!ret && pwd[0] && (trial < imap_maxlogintrials) &&
             LOCAL->netstream && !LOCAL->byeseen && !LOCAL->referral);
   }
@@ -1255,7 +1332,7 @@ void *imap_challenge (void *s,unsigned long *len)
                             strlen (reply->text),len))) {
     sprintf (tmp,"IMAP SERVER BUG (invalid challenge): %.80s",
             (char *) reply->text);
-    mm_log (tmp,ERROR);
+    MM_LOG (tmp,ERROR);
   }
   return ret;
 }
@@ -1309,7 +1386,7 @@ void imap_close (MAILSTREAM *stream,long options)
        imap_send (stream,LEVELIMAP4 (stream) ? "CLOSE" : "EXPUNGE",NIL);
       if (LOCAL->netstream &&
          !imap_OK (stream,reply = imap_send (stream,"LOGOUT",NIL)))
-       mm_log (reply->text,WARN);
+       MM_LOG (reply->text,WARN);
     }
                                /* close NET connection if still open */
     if (LOCAL->netstream) net_close (LOCAL->netstream);
@@ -1354,7 +1431,7 @@ void imap_fast (MAILSTREAM *stream,char *sequence,long flags)
 #else
   IMAPPARSEDREPLY *reply = imap_fetch (stream,sequence,flags & FT_UID);
 #endif
-  if (!imap_OK (stream,reply)) mm_log (reply->text,ERROR);
+  if (!imap_OK (stream,reply)) MM_LOG (reply->text,ERROR);
 }
 
 
@@ -1375,7 +1452,7 @@ void imap_flags (MAILSTREAM *stream,char *sequence,long flags)
   aatt.type = ATOM; aatt.text = (void *) "FLAGS";
   args[0] = &aseq; args[1] = &aatt; args[2] = NIL;
   if (!imap_OK (stream,reply = imap_send (stream,cmd,args)))
-    mm_log (reply->text,ERROR);
+    MM_LOG (reply->text,ERROR);
 }
 \f
 /* IMAP fetch overview
@@ -1484,7 +1561,7 @@ ENVELOPE *imap_structure (MAILSTREAM *stream,unsigned long msgno,BODY **body,
     if (!imap_OK (stream,reply = imap_fetch (stream,seq,FT_NEEDENV +
                                             (body ? FT_NEEDBODY : NIL) +
                                             (flags & (FT_UID + FT_NOHDRS)))))
-      mm_log (reply->text,ERROR);
+      MM_LOG (reply->text,ERROR);
                                /* now hunt for this UID */
     for (i = 1; i <= stream->nmsgs; i++)
       if ((elt = mail_elt (stream,i))->private.uid == msgno) {
@@ -1611,9 +1688,9 @@ ENVELOPE *imap_structure (MAILSTREAM *stream,unsigned long msgno,BODY **body,
          if (imap_OK (stream,reply = imap_send (stream,"FETCH",args)))
                                /* doesn't have body capabilities */
            LOCAL->cap.imap2bis = NIL;
-         else mm_log (reply->text,ERROR);
+         else MM_LOG (reply->text,ERROR);
        }
-       else mm_log (reply->text,ERROR);
+       else MM_LOG (reply->text,ERROR);
       }
     }
   }
@@ -1825,7 +1902,7 @@ long imap_msgdata (MAILSTREAM *stream,unsigned long msgno,char *section,
   if (noextend) {
     sprintf (tmp,"[NOTIMAP4REV1] IMAP%s server can't do extended body fetch",
             noextend);
-    mm_log (tmp,ERROR);
+    MM_LOG (tmp,ERROR);
     return NIL;                        /* can't do anything close either */
   }
   if (nopartial) {
@@ -1843,7 +1920,7 @@ long imap_msgdata (MAILSTREAM *stream,unsigned long msgno,char *section,
   if ((t = nopeek) || (t = nononpeek)) {
                                /* get most recent \Seen setting */
     if (!imap_OK (stream,reply = imap_send (stream,cmd,auxargs)))
-      mm_log (reply->text,WARN);
+      MM_LOG (reply->text,WARN);
                                /* note current setting of \Seen flag */
     if (!(i = mail_elt (stream,msgno)->seen)) {
       sprintf (tmp,nopeek ?    /* only babble if \Seen not set */
@@ -1853,7 +1930,7 @@ long imap_msgdata (MAILSTREAM *stream,unsigned long msgno,char *section,
     }
                                /* send the fetch command */
     if (!imap_OK (stream,reply = imap_send (stream,cmd,args))) {
-      mm_log (reply->text,ERROR);
+      MM_LOG (reply->text,ERROR);
       return NIL;              /* failure */
     }
                                /* send command if need to reset \Seen */
@@ -1862,11 +1939,11 @@ long imap_msgdata (MAILSTREAM *stream,unsigned long msgno,char *section,
         ((nononpeek && !mail_elt (stream,msgno)->seen) &&
          (aflg.text = "+FLAGS \\Seen"))) &&
        !imap_OK (stream,reply = imap_send (stream,"STORE",auxargs)))
-      mm_log (reply->text,WARN);
+      MM_LOG (reply->text,WARN);
   }
                                /* simple case if traditional behavior */
   else if (!imap_OK (stream,reply = imap_send (stream,cmd,args))) {
-    mm_log (reply->text,ERROR);
+    MM_LOG (reply->text,ERROR);
     return NIL;                        /* failure */
   }
                                /* simulate BODY[1] return for RFC 1064/1176 */
@@ -1919,7 +1996,7 @@ unsigned long imap_uid (MAILSTREAM *stream,unsigned long msgno)
     }
                                /* send "FETCH msgno UID" */
     if (!imap_OK (stream,reply = imap_send (stream,"FETCH",args)))
-      mm_log (reply->text,ERROR);
+      MM_LOG (reply->text,ERROR);
   }
   return elt->private.uid;     /* return our UID now */
 }
@@ -1955,7 +2032,7 @@ unsigned long imap_msgno (MAILSTREAM *stream,unsigned long uid)
     sprintf (seq,"%lu",uid);
                                /* send "UID FETCH uid UID" */
     if (!imap_OK (stream,reply = imap_send (stream,"UID FETCH",args)))
-      mm_log (reply->text,ERROR);
+      MM_LOG (reply->text,ERROR);
     if (LOCAL->lastuid.uid) {  /* got any results from FETCH? */
       if ((LOCAL->lastuid.uid == uid) &&
                                /* what, me paranoid? */
@@ -1996,7 +2073,7 @@ void imap_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags)
   args[0] = &aseq; args[1] = &ascm; args[2] = &aflg; args[3] = NIL;
                                /* send "STORE sequence +Flags flag" */
   if (!imap_OK (stream,reply = imap_send (stream,cmd,args)))
-    mm_log (reply->text,ERROR);
+    MM_LOG (reply->text,ERROR);
 }
 \f
 /* IMAP search for messages
@@ -2044,7 +2121,8 @@ long imap_search (MAILSTREAM *stream,char *charset,SEARCHPGM *pgm,long flags)
             pgm->reply_to || pgm->in_reply_to || pgm->message_id ||
             pgm->newsgroups || pgm->followup_to || pgm->references)) {
     if (!mail_search_default (stream,NIL,pgm,flags | SE_NOSERVER))
-      fatal ("impossible mail_search_default() failure");
+      MM_FATAL("impossible mail_search_default() failure");
+      return NIL;
   }
 \f
   else {                       /* do server-based SEARCH */
@@ -2089,7 +2167,7 @@ long imap_search (MAILSTREAM *stream,char *charset,SEARCHPGM *pgm,long flags)
        return NIL;
     }
     else if (!imap_OK (stream,reply)) {
-      mm_log (reply->text,ERROR);
+      MM_LOG (reply->text,ERROR);
       return NIL;
     }
   }
@@ -2121,11 +2199,11 @@ long imap_search (MAILSTREAM *stream,char *charset,SEARCHPGM *pgm,long flags)
       }
     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,s = cpystr (LOCAL->tmp),FT_NEEDENV +
+      if (!imap_OK (stream, reply =
+             imap_fetch (stream,s = cpystr (LOCAL->tmp),FT_NEEDENV +
                                ((flags & SE_NOHDRS) ? FT_NOHDRS : NIL) +
                                ((flags & SE_NEEDBODY) ? FT_NEEDBODY : NIL))))
-       mm_log (reply->text,ERROR);
+        MM_LOG (reply->text,ERROR);
       fs_give ((void **) &s);  /* flush copy of sequence */
     }
   }
@@ -2204,7 +2282,7 @@ unsigned long *imap_sort (MAILSTREAM *stream,char *charset,SEARCHPGM *spg,
       ret = LOCAL->sortdata;
       LOCAL->sortdata = NIL;   /* mail program is responsible for flushing */
     }
-    else mm_log (reply->text,ERROR);
+    else MM_LOG (reply->text,ERROR);
   }
 \f
                                /* not much can do if short caching */
@@ -2366,7 +2444,7 @@ THREADNODE *imap_thread_work (MAILSTREAM *stream,char *type,char *charset,
     ret = LOCAL->threaddata;
     LOCAL->threaddata = NIL;   /* mail program is responsible for flushing */
   }
-  else mm_log (reply->text,ERROR);
+  else MM_LOG (reply->text,ERROR);
   return ret;
 }
 \f
@@ -2390,7 +2468,7 @@ void imap_check (MAILSTREAM *stream)
 {
                                /* send "CHECK" */
   IMAPPARSEDREPLY *reply = imap_send (stream,"CHECK",NIL);
-  mm_log (reply->text,imap_OK (stream,reply) ? (long) NIL : ERROR);
+  MM_LOG (reply->text,imap_OK (stream,reply) ? (long) NIL : ERROR);
 }
 \f
 /* IMAP expunge mailbox
@@ -2412,7 +2490,7 @@ long imap_expunge (MAILSTREAM *stream,char *sequence,long options)
        args[0] = &aseq; args[1] = NIL;
        ret = imap_OK (stream,reply = imap_send (stream,"UID EXPUNGE",args));
       }
-      else mm_log ("[NOTUIDPLUS] Can't do UID EXPUNGE with this server",ERROR);
+      else MM_LOG ("[NOTUIDPLUS] Can't do UID EXPUNGE with this server",ERROR);
     }
                                /* otherwise try to make into UID EXPUNGE */
     else if (mail_sequence (stream,sequence)) {
@@ -2432,7 +2510,7 @@ long imap_expunge (MAILSTREAM *stream,char *sequence,long options)
            s += strlen (s);    /* point at end of string */
          }
          if ((s - t) > (IMAPTMPLEN - 50)) {
-           mm_log ("Excessively complex sequence",ERROR);
+           MM_LOG ("Excessively complex sequence",ERROR);
                fs_give ((void **) &s); //Prevent fix 10/05/2010 [santosh.br]
            return NIL;
          }
@@ -2444,7 +2522,7 @@ long imap_expunge (MAILSTREAM *stream,char *sequence,long options)
   }
                                /* ordinary EXPUNGE */
   else ret = imap_OK (stream,reply = imap_send (stream,"EXPUNGE",NIL));
-  if (reply) mm_log (reply->text,ret ? (long) NIL : ERROR);
+  if (reply) MM_LOG (reply->text,ret ? (long) NIL : ERROR);
   return ret;
 }
 \f
@@ -2486,7 +2564,7 @@ long imap_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long flags)
           (s = (*ir) (stream,LOCAL->referral,REFCOPY)))
     ret = (*pc) (stream,sequence,s,flags | (stream->debug ? CP_DEBUG : NIL));
                                /* otherwise issue error message */
-  else mm_log (reply->text,ERROR);
+  else MM_LOG (reply->text,ERROR);
   return ret;
 }
 \f
@@ -2500,57 +2578,85 @@ long imap_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long flags)
 
 long imap_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
 {
-  MAILSTREAM *st = stream;
-  IMAPARG *args[3],ambx,amap;
-  IMAPPARSEDREPLY *reply = NIL;
-  APPENDDATA map;
-  char tmp[MAILTMPLEN];
-  long debug = stream ? stream->debug : NIL;
-  long ret = NIL;
-  imapreferral_t ir =
-    (imapreferral_t) mail_parameters (stream,GET_IMAPREFERRAL,NIL);
-                               /* mailbox must be good */
-  if (mail_valid_net (mailbox,&imapdriver,NIL,tmp)) {
-                               /* create a stream if given one no good */
-    if ((stream && LOCAL && LOCAL->netstream) ||
-       (stream = mail_open (NIL,mailbox,OP_HALFOPEN|OP_SILENT |
-                            (debug ? OP_DEBUG : NIL)))) {
-                               /* note mailbox in case APPENDUID */
-      LOCAL->appendmailbox = mailbox;
-                               /* use multi-append? */
-      if (LEVELMULTIAPPEND (stream)) {
-       ambx.type = ASTRING; ambx.text = (void *) tmp;
-       amap.type = MULTIAPPEND; amap.text = (void *) &map;
-       map.af = af; map.data = data;
-       args[0] = &ambx; args[1] = &amap; args[2] = NIL;
+       MAILSTREAM *st = stream;
+       IMAPARG *args[3],ambx,amap;
+       IMAPPARSEDREPLY *reply = NIL;
+       APPENDDATA map;
+       char tmp[MAILTMPLEN];
+       long debug = stream ? stream->debug : NIL;
+       long ret = NIL;
+       imapreferral_t ir = (imapreferral_t) mail_parameters (stream,GET_IMAPREFERRAL,NIL);
+
+       /* mailbox must be good */
+       if (mail_valid_net (mailbox,&imapdriver,NIL,tmp)) {
+               /* create a stream if given one no good */
+               if ((stream && LOCAL && LOCAL->netstream) || (stream = mail_open (NIL,mailbox,OP_HALFOPEN|OP_SILENT | (debug ? OP_DEBUG : NIL)))) {
+                       /* note mailbox in case APPENDUID */
+                       LOCAL->appendmailbox = mailbox;
+
+                       /* use multi-append? */
+                       if (LEVELMULTIAPPEND (stream)) {
+                               ambx.type = ASTRING;
+                               ambx.text = (void *) tmp;
+                               amap.type = MULTIAPPEND;
+                               amap.text = (void *) &map;
+                               map.af = af;
+                               map.data = data;
+                               args[0] = &ambx;
+                               args[1] = &amap;
+                               args[2] = NIL;
+
                                /* success if OK */
-       ret = imap_OK (stream,reply = imap_send (stream,"APPEND",args));
-       LOCAL->appendmailbox = NIL;
-      }
-                               /* do succession of single appends */
-      else while ((*af) (stream,data,&map.flags,&map.date,&map.message) &&
-                 map.message &&
-                 (ret = imap_OK (stream,reply =
-                                 imap_append_single (stream,tmp,map.flags,
-                                                     map.date,map.message))));
-      LOCAL->appendmailbox = NIL;
-                               /* don't do referrals if success or no reply */
-      if (ret || !reply) mailbox = NIL;
-                               /* otherwise generate referral */
-      else if (!(mailbox = (ir && LOCAL->referral) ?
-                (*ir) (stream,LOCAL->referral,REFAPPEND) : NIL))
-       mm_log (reply->text,ERROR);
-                               /* close temporary stream */
-      if (st != stream) stream = mail_close (stream);
-      if (mailbox)             /* chase referral if any */
-       ret = imap_append_referral (mailbox,tmp,af,data,map.flags,map.date,
-                                   map.message,&map,debug);
-    }
-    else mm_log ("Can't access server for append",ERROR);
-  }
-  return ret;                  /* return */
+                               ret = imap_OK (stream,reply = imap_send (stream,"APPEND",args));
+                               LOCAL->appendmailbox = NIL;
+                       }
+
+                       /* do succession of single appends */
+                       else {
+                               int type = 0;
+                               if ((type = (*af) (stream,data,&map.flags,&map.date,&map.message))) {
+
+                                       if (type == 1) {
+                                               reply = imap_append_single (stream,tmp,map.flags,map.date,map.message);
+                                               ret = imap_OK (stream,reply);
+                                       } else if (type == 2) {
+                                               reply = imap_append_single_cmd (stream,tmp,map.flags,map.date,map.message);
+                                               if (strncmp (reply->tag,"+", 1) == 0) {
+                                                       ret = 1;
+                                               } else
+                                                       ret = 0;
+                                       } else if (type == 3) {
+                                               reply = imap_append_single_msg (stream,map.message);
+                                               if (reply == NIL) ret = 1;
+                                               else ret = imap_OK (stream,reply);
+                                       } else if (type == 4) {
+                                               reply = imap_append_single_end (stream);
+                                               ret = imap_OK (stream,reply);
+                                       }
+                               }
+                       }
+                       LOCAL->appendmailbox = NIL;
+
+                       /* don't do referrals if success or no reply */
+                       if (ret || !reply) mailbox = NIL;
+
+                       /* otherwise generate referral */
+                       else if (!(mailbox = (ir && LOCAL->referral) ? (*ir) (stream,LOCAL->referral,REFAPPEND) : NIL))
+                               MM_LOG (reply->text,ERROR);
+
+                       /* close temporary stream */
+                       if (st != stream) stream = mail_close (stream);
+
+                       if (mailbox)            /* chase referral if any */
+                               ret = imap_append_referral (mailbox,tmp,af,data,map.flags,map.date,map.message,&map,debug);
+               }
+               else
+                       MM_LOG ("Can't access server for append",ERROR);
+       }
+
+       return ret;                     /* return */
 }
-\f
+
 /* IMAP mail append message referral retry
  * Accepts: destination mailbox
  *         temporary buffer
@@ -2578,7 +2684,7 @@ long imap_append_referral (char *mailbox,char *tmp,append_t af,void *data,
     if (!(stream = mail_open (NIL,mailbox,OP_HALFOPEN|OP_SILENT |
                              (options ? OP_DEBUG : NIL)))) {
       sprintf (tmp,"Can't access referral server: %.80s",mailbox);
-      mm_log (tmp,ERROR);
+      MM_LOG (tmp,ERROR);
       return NIL;
     }
                                /* got referral server, use multi-append? */
@@ -2602,7 +2708,7 @@ long imap_append_referral (char *mailbox,char *tmp,append_t af,void *data,
                                /* generate error if no nested referral */
     if (!(mailbox = (ir && LOCAL->referral) ?
          (*ir) (stream,LOCAL->referral,REFAPPEND) : NIL))
-      mm_log (reply->text,ERROR);
+      MM_LOG (reply->text,ERROR);
     mail_close (stream);       /* close previous referral stream */
   }
   return NIL;                  /* bogus mailbox */
@@ -2620,43 +2726,141 @@ long imap_append_referral (char *mailbox,char *tmp,append_t af,void *data,
 IMAPPARSEDREPLY *imap_append_single (MAILSTREAM *stream,char *mailbox,
                                     char *flags,char *date,STRING *message)
 {
-  MESSAGECACHE elt;
-  IMAPARG *args[5],ambx,aflg,adat,amsg;
-  IMAPPARSEDREPLY *reply;
-  char tmp[MAILTMPLEN];
-  int i;
-  ambx.type = ASTRING; ambx.text = (void *) mailbox;
-  args[i = 0] = &ambx;
-  if (flags) {
-    aflg.type = FLAGS; aflg.text = (void *) flags;
-    args[++i] = &aflg;
-  }
-  if (date) {                  /* ensure date in INTERNALDATE format */
-    if (!mail_parse_date (&elt,date)) {
-                               /* flush previous reply */
-      if (LOCAL->reply.line) fs_give ((void **) &LOCAL->reply.line);
-                               /* build new fake reply */
-      LOCAL->reply.tag = LOCAL->reply.line = cpystr ("*");
-      LOCAL->reply.key = "BAD";
-      LOCAL->reply.text = "Bad date in append";
-      return &LOCAL->reply;
-    }
-    adat.type = ASTRING;
-    adat.text = (void *) (date = mail_date (tmp,&elt));
-    args[++i] = &adat;
-  }
-  amsg.type = LITERAL; amsg.text = (void *) message;
-  args[++i] = &amsg;
-  args[++i] = NIL;
-                               /* easy if IMAP4[rev1] */
-  if (LEVELIMAP4 (stream)) reply = imap_send (stream,"APPEND",args);
-  else {                       /* try the IMAP2bis way */
-    args[1] = &amsg; args[2] = NIL;
-    reply = imap_send (stream,"APPEND",args);
-  }
-  return reply;
+       MESSAGECACHE elt;
+       IMAPARG *args[5],ambx,aflg,adat,amsg;
+       IMAPPARSEDREPLY *reply;
+       char tmp[MAILTMPLEN];
+       int i;
+       ambx.type = ASTRING; ambx.text = (void *) mailbox;
+       args[i = 0] = &ambx;
+
+       if (flags) {
+               aflg.type = FLAGS; aflg.text = (void *) flags;
+               args[++i] = &aflg;
+       }
+
+       if (date) {                     /* ensure date in INTERNALDATE format */
+               if (!mail_parse_date (&elt,date)) {
+                       /* flush previous reply */
+                       if (LOCAL->reply.line) fs_give ((void **) &LOCAL->reply.line);
+                       /* build new fake reply */
+                       LOCAL->reply.tag = LOCAL->reply.line = cpystr ("*");
+                       LOCAL->reply.key = "BAD";
+                       LOCAL->reply.text = "Bad date in append";
+                       return &LOCAL->reply;
+               }
+               adat.type = ASTRING;
+               adat.text = (void *) (date = mail_date (tmp,&elt));
+               args[++i] = &adat;
+       }
+
+       amsg.type = LITERAL; amsg.text = (void *) message;
+       args[++i] = &amsg;
+       args[++i] = NIL;
+
+       /* easy if IMAP4[rev1] */
+       if (LEVELIMAP4 (stream)) reply = imap_send (stream,"APPEND",args);
+       else {                  /* try the IMAP2bis way */
+               args[1] = &amsg; args[2] = NIL;
+               reply = imap_send (stream,"APPEND",args);
+       }
+
+       return reply;
 }
-\f
+
+IMAPPARSEDREPLY *imap_append_single_cmd (MAILSTREAM *stream,char *mailbox,
+                                    char *flags,char *date,STRING *message)
+{
+       MESSAGECACHE elt;
+       IMAPARG *args[5],ambx,aflg,adat,amsg;
+       IMAPPARSEDREPLY *reply;
+       char tmp[MAILTMPLEN];
+       int i;
+       ambx.type = ASTRING; ambx.text = (void *) mailbox;
+       args[i = 0] = &ambx;
+
+       if (flags) {
+               aflg.type = FLAGS; aflg.text = (void *) flags;
+               args[++i] = &aflg;
+       }
+
+       if (date) {                     /* ensure date in INTERNALDATE format */
+               if (!mail_parse_date (&elt,date)) {
+                       /* flush previous reply */
+                       if (LOCAL->reply.line) fs_give ((void **) &LOCAL->reply.line);
+                       /* build new fake reply */
+                       LOCAL->reply.tag = LOCAL->reply.line = cpystr ("*");
+                       LOCAL->reply.key = "BAD";
+                       LOCAL->reply.text = "Bad date in append";
+                       return &LOCAL->reply;
+               }
+               adat.type = ASTRING;
+               adat.text = (void *) (date = mail_date (tmp,&elt));
+               args[++i] = &adat;
+       }
+
+       amsg.type = LITERAL_CMD; amsg.text = (void *) message;
+       args[++i] = &amsg;
+       args[++i] = NIL;
+
+       /* easy if IMAP4[rev1] */
+       if (LEVELIMAP4 (stream)) reply = imap_send (stream,"APPEND",args);
+       else {                  /* try the IMAP2bis way */
+               args[1] = &amsg; args[2] = NIL;
+               reply = imap_send (stream,"APPEND",args);
+       }
+
+       return reply;
+}
+
+IMAPPARSEDREPLY *imap_append_single_msg (MAILSTREAM *stream,STRING *message)
+{
+       MESSAGECACHE elt;
+       IMAPARG *args[5],ambx,aflg,adat,amsg;
+       IMAPPARSEDREPLY *reply;
+       char tmp[MAILTMPLEN];
+       int i=0;
+
+       amsg.type = LITERAL_MSG; amsg.text = (void *) message;
+       args[i] = &amsg;
+       args[++i] = NIL;
+
+       /* easy if IMAP4[rev1] */
+       if (LEVELIMAP4 (stream)) {
+               reply = imap_send (stream,"APPEND",args);
+       }
+       else {                  /* try the IMAP2bis way */
+               args[1] = &amsg; args[2] = NIL;
+               reply = imap_send (stream,"APPEND",args);
+       }
+
+       return reply;
+}
+
+IMAPPARSEDREPLY *imap_append_single_end (MAILSTREAM *stream)
+{
+       MESSAGECACHE elt;
+       IMAPARG *args[5],ambx,aflg,adat,amsg;
+       IMAPPARSEDREPLY *reply;
+       char tmp[MAILTMPLEN];
+       int i=0;
+
+       amsg.type = LITERAL_END; amsg.text = NULL;
+       args[i] = &amsg;
+       args[++i] = NIL;
+
+       /* easy if IMAP4[rev1] */
+       if (LEVELIMAP4 (stream)) {
+               reply = imap_send (stream,"APPEND",args);
+       }
+       else {                  /* try the IMAP2bis way */
+               args[1] = &amsg; args[2] = NIL;
+               reply = imap_send (stream,"APPEND",args);
+       }
+
+       return reply;
+}
+
 /* IMAP garbage collect stream
  * Accepts: Mail stream
  *         garbage collection flags
@@ -2834,9 +3038,9 @@ long imap_acl_work (MAILSTREAM *stream,char *command,IMAPARG *args[])
     IMAPPARSEDREPLY *reply;
     if (imap_OK (stream,reply = imap_send (stream,command,args)))
       ret = LONGT;
-    else mm_log (reply->text,ERROR);
+    else MM_LOG (reply->text,ERROR);
   }
-  else mm_log ("ACL not available on this IMAP server",ERROR);
+  else MM_LOG ("ACL not available on this IMAP server",ERROR);
   return ret;
 }
 \f
@@ -2858,9 +3062,9 @@ long imap_setquota (MAILSTREAM *stream,char *qroot,STRINGLIST *limits)
     args[0] = &aqrt; args[1] = &alim; args[2] = NIL;
     if (imap_OK (stream,reply = imap_send (stream,"SETQUOTA",args)))
       ret = LONGT;
-    else mm_log (reply->text,ERROR);
+    else MM_LOG (reply->text,ERROR);
   }
-  else mm_log ("Quota not available on this IMAP server",ERROR);
+  else MM_LOG ("Quota not available on this IMAP server",ERROR);
   return ret;
 }
 \f
@@ -2880,9 +3084,9 @@ long imap_getquota (MAILSTREAM *stream,char *qroot)
     args[0] = &aqrt; args[1] = NIL;
     if (imap_OK (stream,reply = imap_send (stream,"GETQUOTA",args)))
       ret = LONGT;
-    else mm_log (reply->text,ERROR);
+    else MM_LOG (reply->text,ERROR);
   }
-  else mm_log ("Quota not available on this IMAP server",ERROR);
+  else MM_LOG ("Quota not available on this IMAP server",ERROR);
   return ret;
 }
 
@@ -2903,11 +3107,78 @@ long imap_getquotaroot (MAILSTREAM *stream,char *mailbox)
     args[0] = &ambx; args[1] = NIL;
     if (imap_OK (stream,reply = imap_send (stream,"GETQUOTAROOT",args)))
       ret = LONGT;
-    else mm_log (reply->text,ERROR);
+    else MM_LOG (reply->text,ERROR);
   }
-  else mm_log ("Quota not available on this IMAP server",ERROR);
+  else MM_LOG ("Quota not available on this IMAP server",ERROR);
   return ret;
 }
+
+#ifdef __FEATURE_METADATA_SUPPORT__
+/* RFC5464 : IMAP METADATA Extension */
+char *imap_getmetadata (MAILSTREAM *stream, char *mailbox, char *entry)
+{
+       char *ret = NIL;
+
+       if (LEVELMETADATA (stream)) {
+               IMAPPARSEDREPLY *reply = NULL;
+               IMAPARG *args[3] = { NIL, };
+               IMAPARG arg_mailbox, arg_entry;
+
+               arg_mailbox.type = ASTRING;
+               arg_mailbox.text = (void *) mailbox;
+
+               arg_entry.type   = ASTRING;
+               arg_entry.text   = (void *) entry;
+
+               args[0] = &arg_mailbox;
+               args[1] = &arg_entry;
+               args[2] = NIL;
+
+               if (imap_OK (stream,reply = imap_send (stream, "GETMETADATA", args)))
+                       ret = LONGT;
+               else
+                       MM_LOG (reply->text, ERROR);
+       }
+       else
+               MM_LOG ("METADATA not available on this IMAP server", ERROR);
+
+       return ret;
+}
+
+long imap_setmetadata (MAILSTREAM *stream, char *mailbox, char *entry, char *value)
+{
+       long ret = NIL;
+
+       if (LEVELMETADATA (stream)) {
+               IMAPPARSEDREPLY *reply = NULL;
+               IMAPARG *args[4] = { NIL, };
+               IMAPARG arg_mailbox, arg_entry, arg_value;
+
+               arg_mailbox.type = ASTRING;
+               arg_mailbox.text = (void *) mailbox;
+
+               arg_entry.type   = ASTRING;
+               arg_entry.text   = (void *) entry;
+
+               arg_value.type   = ASTRING;
+               arg_value.text   = (void *) value;
+
+               args[0] = &arg_mailbox;
+               args[1] = &arg_entry;
+               args[2] = &arg_value;
+               args[3] = NIL;
+
+               if (imap_OK (stream,reply = imap_send (stream, "SETMETADATA", args)))
+                       ret = LONGT;
+               else
+                       MM_LOG (reply->text, ERROR);
+       }
+       else
+               MM_LOG ("SETMETADATA not available on this IMAP server", ERROR);
+
+       return ret;
+}
+#endif /* __FEATURE_METADATA_SUPPORT__ */
 \f
 /* Internal routines */
 
@@ -2918,243 +3189,300 @@ long imap_getquotaroot (MAILSTREAM *stream,char *mailbox)
  *         argument list
  * Returns: parsed reply
  */
-
+static g_append_cmd_tag[10] = {0,};
 #define CMDBASE LOCAL->tmp     /* command base */
 
 IMAPPARSEDREPLY *imap_send (MAILSTREAM *stream,char *cmd,IMAPARG *args[])
 {
-  IMAPPARSEDREPLY *reply;
-  IMAPARG *arg,**arglst;
-  SORTPGM *spg;
-  STRINGLIST *list;
-  SIZEDTEXT st;
-  APPENDDATA *map;
-  sendcommand_t sc = (sendcommand_t) mail_parameters (NIL,GET_SENDCOMMAND,NIL);
-  size_t i;
-  void *a;
-  char c,*s,*t,tag[10];
-  stream->unhealthy = NIL;     /* make stream healthy again */
-                               /* gensym a new tag */
-  sprintf (tag,"%08lx",0xffffffff & (stream->gensym++));
-  if (!LOCAL->netstream)       /* make sure have a session */
-    return imap_fake (stream,tag,"[CLOSED] IMAP connection lost");
-  mail_lock (stream);          /* lock up the stream */
-  if (sc)                      /* tell client sending a command */
-    (*sc) (stream,cmd,((compare_cstring (cmd,"FETCH") &&
-                       compare_cstring (cmd,"STORE") &&
-                       compare_cstring (cmd,"SEARCH")) ? 
-                      NIL : SC_EXPUNGEDEFERRED));
-                               /* ignore referral from previous command */
-  if (LOCAL->referral) fs_give ((void **) &LOCAL->referral);
-  sprintf (CMDBASE,"%s %s",tag,cmd);
-  s = CMDBASE + strlen (CMDBASE);
-  if (arglst = args) while (arg = *arglst++) {
-    *s++ = ' ';                        /* delimit argument with space */
-    switch (arg->type) {
-    case ATOM:                 /* atom */
-      for (t = (char *) arg->text; *t; *s++ = *t++);
-      break;
-    case NUMBER:               /* number */
-      sprintf (s,"%lu",(unsigned long) arg->text);
-      s += strlen (s);
-      break;
-    case FLAGS:                        /* flag list as a single string */
-      if (*(t = (char *) arg->text) != '(') {
-       *s++ = '(';             /* wrap parens around string */
-       while (*t) *s++ = *t++;
-       *s++ = ')';             /* wrap parens around string */
-      }
-      else while (*t) *s++ = *t++;
-      break;
-    case ASTRING:              /* atom or string, must be literal? */
-      st.size = strlen ((char *) (st.data = (unsigned char *) arg->text));
-      if (reply = imap_send_astring (stream,tag,&s,&st,NIL,CMDBASE+MAXCOMMAND))
-       return reply;
-      break;
-    case LITERAL:              /* literal, as a stringstruct */
-      if (reply = imap_send_literal (stream,tag,&s,arg->text)) return reply;
-      break;
-\f
-    case LIST:                 /* list of strings */
-      list = (STRINGLIST *) arg->text;
-      c = '(';                 /* open paren */
-      do {                     /* for each list item */
-       *s++ = c;               /* write prefix character */
-       if (reply = imap_send_astring (stream,tag,&s,&list->text,NIL,
-                                      CMDBASE+MAXCOMMAND)) return reply;
-       c = ' ';                /* prefix character for subsequent strings */
-      }
-      while (list = list->next);
-      *s++ = ')';              /* close list */
-      break;
-    case SEARCHPROGRAM:                /* search program */
-      if (reply = imap_send_spgm (stream,tag,CMDBASE,&s,arg->text,
-                                 CMDBASE+MAXCOMMAND))
-       return reply;
-      break;
-    case SORTPROGRAM:          /* search program */
-      c = '(';                 /* open paren */
-      for (spg = (SORTPGM *) arg->text; spg; spg = spg->next) {
-       *s++ = c;               /* write prefix */
-       if (spg->reverse) for (t = "REVERSE "; *t; *s++ = *t++);
-       switch (spg->function) {
-       case SORTDATE:
-         for (t = "DATE"; *t; *s++ = *t++);
-         break;
-       case SORTARRIVAL:
-         for (t = "ARRIVAL"; *t; *s++ = *t++);
-         break;
-       case SORTFROM:
-         for (t = "FROM"; *t; *s++ = *t++);
-         break;
-       case SORTSUBJECT:
-         for (t = "SUBJECT"; *t; *s++ = *t++);
-         break;
-       case SORTTO:
-         for (t = "TO"; *t; *s++ = *t++);
-         break;
-       case SORTCC:
-         for (t = "CC"; *t; *s++ = *t++);
-         break;
-       case SORTSIZE:
-         for (t = "SIZE"; *t; *s++ = *t++);
-         break;
-       default:
-         fatal ("Unknown sort program function in imap_send()!");
-       }
-       c = ' ';                /* prefix character for subsequent items */
-      }
-      *s++ = ')';              /* close list */
-      break;
-\f
-    case BODYTEXT:             /* body section */
-      for (t = "BODY["; *t; *s++ = *t++);
-      for (t = (char *) arg->text; *t; *s++ = *t++);
-      break;
-    case BODYPEEK:             /* body section */
-      for (t = "BODY.PEEK["; *t; *s++ = *t++);
-      for (t = (char *) arg->text; *t; *s++ = *t++);
-      break;
-    case BODYCLOSE:            /* close bracket and possible length */
-      s[-1] = ']';             /* no leading space */
-      for (t = (char *) arg->text; *t; *s++ = *t++);
-      break;
-    case SEQUENCE:             /* sequence */
-      if ((i = strlen (t = (char *) arg->text)) <= (size_t) MAXCOMMAND)
-       while (*t) *s++ = *t++; /* easy case */
-      else {
-       mail_unlock (stream);   /* unlock stream */
-       a = arg->text;          /* save original sequence pointer */
-       arg->type = ATOM;       /* make recursive call be faster */
-       do {                    /* break up into multiple commands */
-         if (i <= MAXCOMMAND) {/* final part? */
-           reply = imap_send (stream,cmd,args);
-           i = 0;              /* and mark as done */
-         }
-         else {                /* still needs to be split further */
-           if (!(t = strchr (t + MAXCOMMAND - 30,',')) ||
-               ((t - (char *) arg->text) > MAXCOMMAND))
-             fatal ("impossible over-long sequence");
-           *t = '\0';          /* tie off sequence at point of split*/
-                               /* recurse to do this part */
-           reply = imap_send (stream,cmd,args);
-           *t++ = ',';         /* restore the comma in case something cares */
-                               /* punt if error */
-           if (!imap_OK (stream,reply)) break;
-                               /* calculate size of remaining sequence */
-           i -= (t - (char *) arg->text);
-                               /* point to new remaining sequence */
-           arg->text = (void *) t;
-         }
-       } while (i);
-       arg->type = SEQUENCE;   /* restore in case something cares */
-       arg->text = a;
-       return reply;           /* return result */
-      }
-      break;
-    case LISTMAILBOX:          /* astring with wildcards */
-      st.size = strlen ((char *) (st.data = (unsigned char *) arg->text));
-      if (reply = imap_send_astring (stream,tag,&s,&st,T,CMDBASE+MAXCOMMAND))
-       return reply;
-      break;
-\f
-    case MULTIAPPEND:          /* append multiple messages */
+       IMAPPARSEDREPLY *reply;
+       IMAPARG *arg,**arglst;
+       SORTPGM *spg;
+       STRINGLIST *list;
+       SIZEDTEXT st;
+       APPENDDATA *map;
+       sendcommand_t sc = (sendcommand_t) mail_parameters (NIL,GET_SENDCOMMAND,NIL);
+       size_t i;
+       void *a;
+       char c,*s,*t,tag[10];
+       int af_type = 0;
+       stream->unhealthy = NIL;        /* make stream healthy again */
+
+       /* gensym a new tag */
+       sprintf (tag,"%08lx",0xffffffff & (stream->gensym++));
+
+       if (!LOCAL->netstream)  /* make sure have a session */
+               return imap_fake (stream,tag,"[CLOSED] IMAP connection lost");
+
+       mail_lock (stream);             /* lock up the stream */
+
+       if (sc)                 /* tell client sending a command */
+               (*sc) (stream,cmd,((compare_cstring (cmd,"FETCH") && compare_cstring (cmd,"STORE") &&
+                       compare_cstring (cmd,"SEARCH")) ? NIL : SC_EXPUNGEDEFERRED));
+
+       /* ignore referral from previous command */
+       if (LOCAL->referral) fs_give ((void **) &LOCAL->referral);
+
+       sprintf (CMDBASE,"%s %s",tag,cmd);
+       s = CMDBASE + strlen (CMDBASE);
+
+       if (arglst = args)
+               while (arg = *arglst++) {
+                       *s++ = ' ';                     /* delimit argument with space */
+                       switch (arg->type) {
+
+                       case ATOM:                      /* atom */
+                               for (t = (char *) arg->text; *t; *s++ = *t++);
+                               break;
+
+                       case NUMBER:            /* number */
+                               sprintf (s,"%lu",(unsigned long) arg->text);
+                               s += strlen (s);
+                               break;
+
+                       case FLAGS:                     /* flag list as a single string */
+                               if (*(t = (char *) arg->text) != '(') {
+                                       *s++ = '(';             /* wrap parens around string */
+                                       while (*t) *s++ = *t++;
+                                       *s++ = ')';             /* wrap parens around string */
+                               } else
+                                       while (*t) *s++ = *t++;
+                               break;
+
+                       case ASTRING:           /* atom or string, must be literal? */
+                               st.size = strlen ((char *) (st.data = (unsigned char *) arg->text));
+                               if (reply = imap_send_astring (stream,tag,&s,&st,NIL,CMDBASE+MAXCOMMAND))
+                                       return reply;
+                               break;
+
+                       case LITERAL:           /* literal, as a stringstruct */
+                               if (reply = imap_send_literal (stream,tag,&s,arg->text))
+                                       return reply;
+                               break;
+
+                       case LITERAL_CMD:
+                               snprintf(g_append_cmd_tag, sizeof(g_append_cmd_tag), "%s", tag);
+                               if (reply = imap_send_literal_cmd (stream,tag,&s,arg->text))
+                                       return reply;
+                               break;
+
+                       case LITERAL_MSG:
+                               if (g_append_cmd_tag && strlen(g_append_cmd_tag) > 0) {
+                                       snprintf(tag, sizeof(tag), "%s", g_append_cmd_tag);
+                               }
+                               reply = imap_send_literal_msg (stream,tag,arg->text);
+                               return reply;
+
+                       case LITERAL_END:
+                               if (g_append_cmd_tag && strlen(g_append_cmd_tag) > 0) {
+                                       snprintf(tag, sizeof(tag), "%s", g_append_cmd_tag);
+                                       memset(g_append_cmd_tag, 0, sizeof(g_append_cmd_tag));
+                               }
+                               s = CMDBASE;
+                               break;
+
+                       case LIST:                      /* list of strings */
+                               list = (STRINGLIST *) arg->text;
+                               c = '(';                        /* open paren */
+                               do {                    /* for each list item */
+                                       *s++ = c;               /* write prefix character */
+                                       if (reply = imap_send_astring (stream,tag,&s,&list->text,NIL,CMDBASE+MAXCOMMAND))
+                                               return reply;
+                                       c = ' ';                /* prefix character for subsequent strings */
+                               } while (list = list->next);
+
+                               *s++ = ')';             /* close list */
+                               break;
+
+                       case SEARCHPROGRAM:             /* search program */
+                               if (reply = imap_send_spgm (stream,tag,CMDBASE,&s,arg->text,CMDBASE+MAXCOMMAND))
+                                       return reply;
+                               break;
+
+                       case SORTPROGRAM:               /* search program */
+                               c = '(';                        /* open paren */
+                               for (spg = (SORTPGM *) arg->text; spg; spg = spg->next) {
+                                       *s++ = c;               /* write prefix */
+                                       if (spg->reverse)
+                                               for (t = "REVERSE "; *t; *s++ = *t++);
+
+                                       switch (spg->function) {
+                                       case SORTDATE:
+                                               for (t = "DATE"; *t; *s++ = *t++);
+                                               break;
+
+                                       case SORTARRIVAL:
+                                               for (t = "ARRIVAL"; *t; *s++ = *t++);
+                                               break;
+
+                                       case SORTFROM:
+                                               for (t = "FROM"; *t; *s++ = *t++);
+                                               break;
+
+                                       case SORTSUBJECT:
+                                               for (t = "SUBJECT"; *t; *s++ = *t++);
+                                               break;
+
+                                       case SORTTO:
+                                               for (t = "TO"; *t; *s++ = *t++);
+                                               break;
+
+                                       case SORTCC:
+                                               for (t = "CC"; *t; *s++ = *t++);
+                                               break;
+
+                                       case SORTSIZE:
+                                               for (t = "SIZE"; *t; *s++ = *t++);
+                                               break;
+
+                                       default:
+                                               MM_LOG ("Unknown sort program function in imap_send()!", ERROR);
+                                               mail_unlock (stream);           /* unlock stream */
+                                               return imap_fake (stream,tag,"imap_send unknown sort program function error");
+                                       }
+
+                                       c = ' ';                /* prefix character for subsequent items */
+                               }
+                               *s++ = ')';             /* close list */
+                               break;
+
+                       case BODYTEXT:          /* body section */
+                               for (t = "BODY["; *t; *s++ = *t++);
+                               for (t = (char *) arg->text; *t; *s++ = *t++);
+                               break;
+
+                       case BODYPEEK:          /* body section */
+                               for (t = "BODY.PEEK["; *t; *s++ = *t++);
+                               for (t = (char *) arg->text; *t; *s++ = *t++);
+                               break;
+
+                       case BODYCLOSE:         /* close bracket and possible length */
+                               s[-1] = ']';            /* no leading space */
+                               for (t = (char *) arg->text; *t; *s++ = *t++);
+                               break;
+
+                       case SEQUENCE:          /* sequence */
+                               if ((i = strlen (t = (char *) arg->text)) <= (size_t) MAXCOMMAND)
+                                       while (*t) *s++ = *t++; /* easy case */
+                               else {
+                                       mail_unlock (stream);   /* unlock stream */
+                                       a = arg->text;          /* save original sequence pointer */
+                                       arg->type = ATOM;       /* make recursive call be faster */
+
+                                       do {                    /* break up into multiple commands */
+                                               if (i <= MAXCOMMAND) {/* final part? */
+                                                       reply = imap_send (stream,cmd,args);
+                                                       i = 0;          /* and mark as done */
+                                               } else {                /* still needs to be split further */
+                                                       if (!(t = strchr (t + MAXCOMMAND - 30,',')) || ((t - (char *) arg->text) > MAXCOMMAND)) {
+                                                               MM_LOG ("impossible over-long sequence", ERROR);
+                                                               reply = imap_fake (stream,tag,"impossible over-long sequence error");
+                                                               break;
+                                                       }
+                                                       *t = '\0';              /* tie off sequence at point of split*/
+                                                       /* recurse to do this part */
+                                                       reply = imap_send (stream,cmd,args);
+                                                       *t++ = ',';             /* restore the comma in case something cares */
+                                                       /* punt if error */
+                                                       if (!imap_OK (stream,reply)) break;
+                                                       /* calculate size of remaining sequence */
+                                                       i -= (t - (char *) arg->text);
+                                                       /* point to new remaining sequence */
+                                                       arg->text = (void *) t;
+                                               }
+                                       } while (i);
+
+                                       arg->type = SEQUENCE;   /* restore in case something cares */
+                                       arg->text = a;
+                                       return reply;           /* return result */
+                               }
+                               break;
+
+                       case LISTMAILBOX:               /* astring with wildcards */
+                               st.size = strlen ((char *) (st.data = (unsigned char *) arg->text));
+                               if (reply = imap_send_astring (stream,tag,&s,&st,T,CMDBASE+MAXCOMMAND))
+                                       return reply;
+                               break;
+
+                       case MULTIAPPEND:               /* append multiple messages */
                                /* get package pointer */
-      map = (APPENDDATA *) arg->text;
-      if (!(*map->af) (stream,map->data,&map->flags,&map->date,&map->message)||
-         !map->message) {
-       STRING es;
-       INIT (&es,mail_string,"",0);
-       return (reply = imap_send_literal (stream,tag,&s,&es)) ?
-         reply : imap_fake (stream,tag,"Server zero-length literal error");
-      }
-    case MULTIAPPENDREDO:      /* redo multiappend */
+                               map = (APPENDDATA *) arg->text;
+                               if (!(*map->af) (stream,map->data,&map->flags,&map->date,&map->message)||!map->message) {
+                                       STRING es;
+                                       INIT (&es,mail_string,"",0);
+                                       return (reply = imap_send_literal (stream,tag,&s,&es)) ? reply : imap_fake (stream,tag,"Server zero-length literal error");
+                               }
+
+                       case MULTIAPPENDREDO:   /* redo multiappend */
                                /* get package pointer */
-      map = (APPENDDATA *) arg->text;
-      do {                     /* make sure date valid if given */
-       char datetmp[MAILTMPLEN];
-       MESSAGECACHE elt;
-       STRING es;
-       if (!map->date || mail_parse_date (&elt,map->date)) {
-         if (t = map->flags) { /* flags given? */
-           if (*t != '(') {
-             *s++ = '(';       /* wrap parens around string */
-             while (*t) *s++ = *t++;
-             *s++ = ')';       /* wrap parens around string */
-           }
-           else while (*t) *s++ = *t++;
-           *s++ = ' ';         /* delimit with space */
-         }
-         if (map->date) {      /* date given? */
-           st.size = strlen ((char *) (st.data = (unsigned char *)
-                                       mail_date (datetmp,&elt)));
-           if (reply = imap_send_astring (stream,tag,&s,&st,NIL,
-                                          CMDBASE+MAXCOMMAND)) return reply;
-           *s++ = ' ';         /* delimit with space */
-         }
-         if (reply = imap_send_literal (stream,tag,&s,map->message))
-           return reply;
-                               /* get next message */
-         if ((*map->af) (stream,map->data,&map->flags,&map->date,
-                         &map->message)) {
-                               /* have a message, delete next in command */
-           if (map->message) *s++ = ' ';
-           continue;           /* loop back for next message */
-         }
-       }
-                               /* bad date or need to abort */
-       INIT (&es,mail_string,"",0);
-       return (reply = imap_send_literal (stream,tag,&s,&es)) ?
-         reply : imap_fake (stream,tag,"Server zero-length literal error");
-       break;                  /* exit the loop */
-      } while (map->message);
-      break;
-\f
-    case SNLIST:               /* list of string/number pairs */
-      list = (STRINGLIST *) arg->text;
-      c = '(';                 /* open paren */
-      do {                     /* for each list item */
-       *s++ = c;               /* write prefix character */
-       if (list) {             /* sigh, QUOTA has bizarre syntax! */
-         for (t = (char *) list->text.data; *t; *s++ = *t++);
-         sprintf (s," %lu",list->text.size);
-         s += strlen (s);
-         c = ' ';              /* prefix character for subsequent strings */
-       }
-      }
-      while (list = list->next);
-      *s++ = ')';              /* close list */
-      break;
-    default:
-      fatal ("Unknown argument type in imap_send()!");
-    }
-  }
-                               /* send the command */
-  reply = imap_sout (stream,tag,CMDBASE,&s);
-  mail_unlock (stream);                /* unlock stream */
-  return reply;
+                               map = (APPENDDATA *) arg->text;
+                               do {                    /* make sure date valid if given */
+                                       char datetmp[MAILTMPLEN];
+                                       MESSAGECACHE elt;
+                                       STRING es;
+                                       if (!map->date || mail_parse_date (&elt,map->date)) {
+                                               if (t = map->flags) {   /* flags given? */
+                                                       if (*t != '(') {
+                                                               *s++ = '(';     /* wrap parens around string */
+                                                               while (*t) *s++ = *t++;
+                                                               *s++ = ')';     /* wrap parens around string */
+                                                       }
+                                                       else while (*t) *s++ = *t++;
+                                                       *s++ = ' ';             /* delimit with space */
+                                               }
+
+                                               if (map->date) {        /* date given? */
+                                                       st.size = strlen ((char *) (st.data = (unsigned char *)mail_date (datetmp,&elt)));
+                                                       if (reply = imap_send_astring (stream,tag,&s,&st,NIL,CMDBASE+MAXCOMMAND)) return reply;
+                                                       *s++ = ' ';             /* delimit with space */
+                                               }
+
+                                               if (reply = imap_send_literal (stream,tag,&s,map->message))
+                                                       return reply;
+
+                                               /* get next message */
+                                               if ((*map->af) (stream,map->data,&map->flags,&map->date,&map->message)) {
+                                                       /* have a message, delete next in command */
+                                                       if (map->message) *s++ = ' ';
+                                                       continue;               /* loop back for next message */
+                                               }
+                                       }
+
+                                       /* bad date or need to abort */
+                                       INIT (&es,mail_string,"",0);
+                                       return (reply = imap_send_literal (stream,tag,&s,&es)) ? reply : imap_fake (stream,tag,"Server zero-length literal error");
+                                       break;                  /* exit the loop */
+                               } while (map->message);
+                               break;
+
+                       case SNLIST:            /* list of string/number pairs */
+                               list = (STRINGLIST *) arg->text;
+                               c = '(';                        /* open paren */
+                               do {                    /* for each list item */
+                                       *s++ = c;               /* write prefix character */
+                                       if (list) {             /* sigh, QUOTA has bizarre syntax! */
+                                               for (t = (char *) list->text.data; *t; *s++ = *t++);
+                                               sprintf (s," %lu",list->text.size);
+                                               s += strlen (s);
+                                               c = ' ';                /* prefix character for subsequent strings */
+                                       }
+                               } while (list = list->next);
+
+                               *s++ = ')';             /* close list */
+                               break;
+
+                       default:
+                               MM_LOG ("Unknown argument type in imap_send()!", ERROR);
+                               mail_unlock (stream);           /* unlock stream */
+                               return imap_fake (stream,tag,"[CLOSED] IMAP connection broken (data)");
+                       }
+               }
+
+       /* send the command */
+       reply = imap_sout (stream,tag,CMDBASE,&s);
+       mail_unlock (stream);           /* unlock stream */
+       return reply;
 }
-\f
+
 /* IMAP send atom-string
  * Accepts: MAIL stream
  *         reply tag
@@ -3214,44 +3542,104 @@ IMAPPARSEDREPLY *imap_send_astring (MAILSTREAM *stream,char *tag,char **s,
 IMAPPARSEDREPLY *imap_send_literal (MAILSTREAM *stream,char *tag,char **s,
                                    STRING *st)
 {
-  IMAPPARSEDREPLY *reply;
-  unsigned long i = SIZE (st);
-  unsigned long j;
-  sprintf (*s,"{%lu}",i);      /* write literal count */
-  *s += strlen (*s);           /* size of literal count */
-                               /* send the command */
-  reply = imap_sout (stream,tag,CMDBASE,s);
-  if (strcmp (reply->tag,"+")) {/* prompt for more data? */
-    mail_unlock (stream);      /* no, give up */
-    return reply;
-  }
-  while (i) {                  /* dump the text */
-    if (st->cursize) {         /* if text to do in this chunk */
-      /* RFC 3501 technically forbids NULs in literals.  Normally, the
-       * delivering MTA would take care of MIME converting the message text
-       * so that it is NUL-free.  If it doesn't, then we have the choice of
-       * either violating IMAP by sending NULs, corrupting the data, or going
-       * to lots of work to do MIME conversion in the IMAP server.
-       *
-       * No current stringstruct driver objects to having its buffer patched.
-       * If this ever changes, it will be necessary to change this kludge.
-       */
-                               /* patch NULs to C1 control */
-      for (j = 0; j < st->cursize; ++j)
-       if (!st->curpos[j]) st->curpos[j] = 0x80;
-      if (!net_sout (LOCAL->netstream,st->curpos,st->cursize)) {
-       mail_unlock (stream);
-       return imap_fake (stream,tag,"[CLOSED] IMAP connection broken (data)");
-      }
-      i -= st->cursize;                /* note that we wrote out this much */
-      st->curpos += (st->cursize - 1);
-      st->cursize = 0;
-    }
-    (*st->dtb->next) (st);     /* advance to next buffer's worth */
-  }
-  return NIL;                  /* success */
+       IMAPPARSEDREPLY *reply;
+       unsigned long i = SIZE (st);
+       unsigned long j;
+
+       sprintf (*s,"{%lu}",i); /* write literal count */
+       *s += strlen (*s);              /* size of literal count */
+       /* send the command */
+       reply = imap_sout (stream,tag,CMDBASE,s);
+
+       mm_notify (stream,reply->tag,WARN);
+       if (strncmp (reply->tag,"+", 1)) {/* prompt for more data? */
+               mail_unlock (stream);   /* no, give up */
+               return reply;
+       }
+
+       while (i) {                     /* dump the text */
+               if (st->cursize) {              /* if text to do in this chunk */
+                       /* RFC 3501 technically forbids NULs in literals.  Normally, the
+                       * delivering MTA would take care of MIME converting the message text
+                       * so that it is NUL-free.  If it doesn't, then we have the choice of
+                       * either violating IMAP by sending NULs, corrupting the data, or going
+                       * to lots of work to do MIME conversion in the IMAP server.
+                       *
+                       * No current stringstruct driver objects to having its buffer patched.
+                       * If this ever changes, it will be necessary to change this kludge.
+                       */
+                       /* patch NULs to C1 control */
+                       for (j = 0; j < st->cursize; ++j)
+                               if (!st->curpos[j]) st->curpos[j] = 0x80;
+
+                       if (!net_sout (LOCAL->netstream,st->curpos,st->cursize)) {
+                               mail_unlock (stream);
+                               return imap_fake (stream,tag,"[CLOSED] IMAP connection broken (data)");
+                       }
+                       i -= st->cursize;               /* note that we wrote out this much */
+                       st->curpos += (st->cursize - 1);
+                       st->cursize = 0;
+               }
+               (*st->dtb->next) (st);  /* advance to next buffer's worth */
+       }
+
+       return NIL;                     /* success */
 }
-\f
+
+IMAPPARSEDREPLY *imap_send_literal_cmd (MAILSTREAM *stream,char *tag,char **s,STRING *st)
+{
+       IMAPPARSEDREPLY *reply;
+
+       sprintf (*s,"{%s}",st->chunk);  /* write literal count */
+       *s += strlen (*s);              /* size of literal count */
+
+       /* send the command */
+       reply = imap_sout (stream,tag,CMDBASE,s);
+
+       if (strncmp (reply->tag,"+", 1) == 0) {/* prompt for more data? */
+               mail_unlock (stream);   /* no, give up */
+               return reply;
+       }
+
+       return NIL;                     /* success */
+}
+
+IMAPPARSEDREPLY *imap_send_literal_msg (MAILSTREAM *stream,char *tag,STRING *st)
+{
+       IMAPPARSEDREPLY *reply;
+       unsigned long i = SIZE (st);
+       unsigned long j;
+
+       while (i) {                     /* dump the text */
+               if (st->cursize) {              /* if text to do in this chunk */
+                       /* RFC 3501 technically forbids NULs in literals.  Normally, the
+                       * delivering MTA would take care of MIME converting the message text
+                       * so that it is NUL-free.  If it doesn't, then we have the choice of
+                       * either violating IMAP by sending NULs, corrupting the data, or going
+                       * to lots of work to do MIME conversion in the IMAP server.
+                       *
+                       * No current stringstruct driver objects to having its buffer patched.
+                       * If this ever changes, it will be necessary to change this kludge.
+                       */
+                       /* patch NULs to C1 control */
+                       for (j = 0; j < st->cursize; ++j)
+                               if (!st->curpos[j]) st->curpos[j] = 0x80;
+
+                       if (!net_sout (LOCAL->netstream,st->curpos,st->cursize)) {
+                               mail_unlock (stream);
+                               return imap_fake (stream,tag,"[CLOSED] IMAP connection broken (data)");
+                       }
+                       i -= st->cursize;               /* note that we wrote out this much */
+                       st->curpos += (st->cursize - 1);
+                       st->cursize = 0;
+               }
+               (*st->dtb->next) (st);  /* advance to next buffer's worth */
+       }
+
+       mail_unlock (stream);   /* no, give up */
+       return NIL;                     /* success */
+}
+
 /* IMAP send search program
  * Accepts: MAIL stream
  *         reply tag
@@ -3689,6 +4077,8 @@ IMAPPARSEDREPLY *imap_fake (MAILSTREAM *stream,char *tag,char *text)
 long imap_OK (MAILSTREAM *stream,IMAPPARSEDREPLY *reply)
 {
   long ret = NIL;
+  if (reply == NULL) return ret;
+
                                /* OK - operation succeeded */
   if (!strcmp (reply->key,"OK")) {
     imap_parse_response (stream,reply->text,NIL,NIL);
@@ -3705,7 +4095,7 @@ long imap_OK (MAILSTREAM *stream,IMAPPARSEDREPLY *reply)
                                /* bad protocol received */
     else sprintf (LOCAL->tmp,"Unexpected IMAP response: %.80s %.80s",
                  (char *) reply->key,(char *) reply->text);
-    mm_log (LOCAL->tmp,ERROR); /* either way, this is not good */
+    MM_LOG (LOCAL->tmp,ERROR); /* either way, this is not good */
   }
   return ret;
 }
@@ -3772,7 +4162,7 @@ void imap_parse_unsolicited (MAILSTREAM *stream,IMAPPARSEDREPLY *reply)
                                /* canonicalize property, parse it */
        if (!strcmp (ucase (prop),"FLAGS")) imap_parse_flags (stream,elt,&t);
        else if (!strcmp (prop,"INTERNALDATE") &&
-                (s = imap_parse_string (stream,&t,reply,NIL,NIL,LONGT))) {
+                (s = imap_parse_string (stream,&t,reply,NIL,NIL,IMAP_PARSE_STRING_FLAGS_FOR_NEW_LINE))) {
          if (!mail_parse_date (elt,s)) {
            sprintf (LOCAL->tmp,"Bogus date: %.80s",(char *) s);
            mm_notify (stream,LOCAL->tmp,WARN);
@@ -3863,7 +4253,7 @@ void imap_parse_unsolicited (MAILSTREAM *stream,IMAPPARSEDREPLY *reply)
                imap_parse_string (stream,&t,reply,
                                   ((md.what[0] && (md.what[0] != 'H')) ||
                                    md.first || md.last) ? &md : NIL,
-                                  &text.size,NIL);
+                                  &text.size,IMAP_PARSE_STRING_FLAGS_NONE);
                                /* all done if partial */
              if (md.first || md.last) mail_free_stringlist (&stl);
                                /* otherwise register it in the cache */
@@ -3884,7 +4274,7 @@ void imap_parse_unsolicited (MAILSTREAM *stream,IMAPPARSEDREPLY *reply)
          if (!prop[6]) {       /* cache full message */
            md.what = "";
            text.data = (unsigned char *)
-             imap_parse_string (stream,&t,reply,&md,&text.size,NIL);
+             imap_parse_string (stream,&t,reply,&md,&text.size,IMAP_PARSE_STRING_FLAGS_NONE);
            imap_cache (stream,msgno,md.what,NIL,&text);
          }
          else if (!strcmp (prop+7,"SIZE"))
@@ -3892,14 +4282,14 @@ void imap_parse_unsolicited (MAILSTREAM *stream,IMAPPARSEDREPLY *reply)
                                /* legacy properties */
          else if (!strcmp (prop+7,"HEADER")) {
            text.data = (unsigned char *)
-             imap_parse_string (stream,&t,reply,NIL,&text.size,NIL);
+             imap_parse_string (stream,&t,reply,NIL,&text.size,IMAP_PARSE_STRING_FLAGS_NONE);
            imap_cache (stream,msgno,"HEADER",NIL,&text);
            e = stream->scache ? &stream->env : &elt->private.msg.env;
          }
          else if (!strcmp (prop+7,"TEXT")) {
            md.what = "TEXT";
            text.data = (unsigned char *)
-             imap_parse_string (stream,&t,reply,&md,&text.size,NIL);
+             imap_parse_string (stream,&t,reply,&md,&text.size,IMAP_PARSE_STRING_FLAGS_NONE);
            imap_cache (stream,msgno,md.what,NIL,&text);
          }
          else {
@@ -4097,7 +4487,6 @@ void imap_parse_unsolicited (MAILSTREAM *stream,IMAPPARSEDREPLY *reply)
         else if (!compare_cstring (t,"\\Starred")) i |= LATT_XLIST_FLAGGED; /* Gmail */
         else if (!compare_cstring (t,"\\Flagged")) i |= LATT_XLIST_FLAGGED; /* RFC 6154 */
         else if (!compare_cstring (t,"\\Trash"))   i |= LATT_XLIST_TRASH;   /* RFC 6154 and Gmail */
-               /* ignore extension flags */
       }
       while (t = strtok_r (NIL," ",&r));
       switch (*++s) {          /* process delimiter */
@@ -4206,7 +4595,7 @@ void imap_parse_unsolicited (MAILSTREAM *stream,IMAPPARSEDREPLY *reply)
       fs_give ((void **) &id); /* free identifier */
     }
     else {
-      sprintf (LOCAL->tmp,"Missing LISTRIGHTS identifer for %.80s",(char *) t);
+      sprintf (LOCAL->tmp,"Missing LISTRIGHTS identifier for %.80s",(char *) t);
       mm_notify (stream,LOCAL->tmp,WARN);
       stream->unhealthy = T;
     }
@@ -4301,6 +4690,16 @@ void imap_parse_unsolicited (MAILSTREAM *stream,IMAPPARSEDREPLY *reply)
     }
     fs_give ((void **) &t);
   }
+#ifdef __FEATURE_METADATA_SUPPORT__
+  else if (!strcmp (reply->key,"METADATA")) {
+    /* Example :
+     * C: a GETMETADATA "" /shared/comment
+     * S: * METADATA "" (/shared/comment "Shared comment")
+     * S: a OK GETMETADATA complete
+     */
+    t = imap_parse_astring (stream,&s,reply,NIL);
+  }
+#endif /* __FEATURE_METADATA_SUPPORT__ */
 \f
   else if (!strcmp (reply->key,"OK") || !strcmp (reply->key,"PREAUTH"))
     imap_parse_response (stream,reply->text,NIL,T);
@@ -4321,6 +4720,11 @@ void imap_parse_unsolicited (MAILSTREAM *stream,IMAPPARSEDREPLY *reply)
     else t = reply->text;
     mm_list (stream,NIL,t,NIL);
   }
+#ifdef __FEATURE_SUPPORT_IMAP_ID__
+  else if (!strcmp (reply->key,"ID")) {
+    imap_parse_id (stream,reply->text);
+  }
+#endif /* __FEATURE_SUPPORT_IMAP_ID__ */
   else {
     sprintf (LOCAL->tmp,"Unexpected untagged message: %.80s",
             (char *) reply->key);
@@ -4387,7 +4791,7 @@ void imap_parse_response (MAILSTREAM *stream,char *text,long errflg,long ntfy)
       }
 \f
       else if (!compare_cstring (t,"CAPABILITY"))
-       imap_parse_capabilities (stream,s);
+         imap_parse_capabilities (stream,s);
       else if ((j = LEVELUIDPLUS (stream) && LOCAL->appendmailbox) &&
               !compare_cstring (t,"COPYUID") &&
               (cu = (copyuid_t) mail_parameters (NIL,GET_COPYUID,NIL)) &&
@@ -4458,7 +4862,7 @@ NAMESPACE *imap_parse_namespace (MAILSTREAM *stream,unsigned char **txtptr,
        if (!ret) ret = nam;    /* if first time note first namespace */
                                /* if previous link new block to it */
        if (prev) prev->next = nam;
-       nam->name = imap_parse_string (stream,txtptr,reply,NIL,NIL,NIL);
+       nam->name = imap_parse_string (stream,txtptr,reply,NIL,NIL,IMAP_PARSE_STRING_FLAGS_NONE);
                                /* ignore whitespace */
        while (**txtptr == ' ') ++*txtptr;
        switch (**txtptr) {     /* parse delimiter */
@@ -4483,7 +4887,7 @@ NAMESPACE *imap_parse_namespace (MAILSTREAM *stream,unsigned char **txtptr,
        while (**txtptr == ' '){/* append new parameter to tail */
          if (nam->param) par = par->next = mail_newbody_parameter ();
          else nam->param = par = mail_newbody_parameter ();
-         if (!(par->attribute = imap_parse_string (stream,txtptr,reply,NIL,
+         if (!(par->attribute = imap_parse_string (stream,txtptr,reply,IMAP_PARSE_STRING_FLAGS_NONE,
                                                    NIL,NIL))) {
            mm_notify (stream,"Missing namespace extension attribute",WARN);
            stream->unhealthy = T;
@@ -4496,7 +4900,7 @@ NAMESPACE *imap_parse_namespace (MAILSTREAM *stream,unsigned char **txtptr,
            ++*txtptr;          /* yes */
            do {                /* parse each value */
              if (!(par->value = imap_parse_string (stream,txtptr,reply,NIL,
-                                                   NIL,LONGT))) {
+                                                   NIL,IMAP_PARSE_STRING_FLAGS_FOR_NEW_LINE))) {
                sprintf (LOCAL->tmp,
                         "Missing value for namespace attribute %.80s",att);
                mm_notify (stream,LOCAL->tmp,WARN);
@@ -4653,8 +5057,8 @@ void imap_parse_envelope (MAILSTREAM *stream,ENVELOPE **env,
   switch (c) {                 /* dispatch on first character */
   case '(':                    /* if envelope S-expression */
     *env = mail_newenvelope ();        /* parse the new envelope */
-    (*env)->date = imap_parse_string (stream,txtptr,reply,NIL,NIL,LONGT);
-    (*env)->subject = imap_parse_string (stream,txtptr,reply,NIL,NIL,LONGT);
+    (*env)->date = imap_parse_string (stream,txtptr,reply,NIL,NIL,IMAP_PARSE_STRING_FLAGS_FOR_NEW_LINE);
+    (*env)->subject = imap_parse_string (stream,txtptr,reply,NIL,NIL,IMAP_PARSE_STRING_FLAGS_FOR_NEW_LINE);
     (*env)->from = imap_parse_adrlist (stream,txtptr,reply);
     (*env)->sender = imap_parse_adrlist (stream,txtptr,reply);
     (*env)->reply_to = imap_parse_adrlist (stream,txtptr,reply);
@@ -4662,8 +5066,8 @@ void imap_parse_envelope (MAILSTREAM *stream,ENVELOPE **env,
     (*env)->cc = imap_parse_adrlist (stream,txtptr,reply);
     (*env)->bcc = imap_parse_adrlist (stream,txtptr,reply);
     (*env)->in_reply_to = imap_parse_string (stream,txtptr,reply,NIL,NIL,
-                                            LONGT);
-    (*env)->message_id = imap_parse_string (stream,txtptr,reply,NIL,NIL,LONGT);
+               IMAP_PARSE_STRING_FLAGS_FOR_NEW_LINE);
+    (*env)->message_id = imap_parse_string (stream,txtptr,reply,NIL,NIL,IMAP_PARSE_STRING_FLAGS_FOR_NEW_LINE);
     if (oenv) {                        /* need to merge old envelope? */
       (*env)->newsgroups = oenv->newsgroups;
       oenv->newsgroups = NIL;
@@ -4760,10 +5164,10 @@ ADDRESS *imap_parse_address (MAILSTREAM *stream,unsigned char **txtptr,
       ++*txtptr;               /* skip past open paren */
       if (adr) prev = adr;     /* note previous if any */
       adr = mail_newaddr ();   /* instantiate address and parse its fields */
-      adr->personal = imap_parse_string (stream,txtptr,reply,NIL,NIL,LONGT);
-      adr->adl = imap_parse_string (stream,txtptr,reply,NIL,NIL,LONGT);
-      adr->mailbox = imap_parse_string (stream,txtptr,reply,NIL,NIL,LONGT);
-      adr->host = imap_parse_string (stream,txtptr,reply,NIL,NIL,LONGT);
+      adr->personal = imap_parse_string (stream,txtptr,reply,NIL,NIL,IMAP_PARSE_STRING_FLAGS_FOR_DOUBLE_QUOTE);
+      adr->adl = imap_parse_string (stream,txtptr,reply,NIL,NIL,IMAP_PARSE_STRING_FLAGS_FOR_NEW_LINE);
+      adr->mailbox = imap_parse_string (stream,txtptr,reply,NIL,NIL,IMAP_PARSE_STRING_FLAGS_FOR_NEW_LINE);
+      adr->host = imap_parse_string (stream,txtptr,reply,NIL,NIL,IMAP_PARSE_STRING_FLAGS_FOR_NEW_LINE);
       if (**txtptr != ')') {   /* handle trailing paren */
        sprintf (LOCAL->tmp,"Junk at end of address: %.80s",(char *) *txtptr);
        mm_notify (stream,LOCAL->tmp,WARN);
@@ -4923,7 +5327,7 @@ unsigned char *imap_parse_astring (MAILSTREAM *stream,unsigned char **txtptr,
   switch (c) {
   case '"':                    /* quoted string? */
   case '{':                    /* literal? */
-    ret = imap_parse_string (stream,txtptr,reply,NIL,len,NIL);
+    ret = imap_parse_string (stream,txtptr,reply,NIL,len,IMAP_PARSE_STRING_FLAGS_NONE);
     break;
   default:                     /* must be atom */
     for (c = *(s = *txtptr);   /* find end of atom */
@@ -4953,7 +5357,7 @@ unsigned char *imap_parse_astring (MAILSTREAM *stream,unsigned char **txtptr,
  *         parsed reply
  *         mailgets data
  *         returned string length
- *         filter newline flag
+ *         filter newline/double quote handling flag
  * Returns: string
  *
  * Updates text pointer
@@ -4961,7 +5365,7 @@ unsigned char *imap_parse_astring (MAILSTREAM *stream,unsigned char **txtptr,
 
 unsigned char *imap_parse_string (MAILSTREAM *stream,unsigned char **txtptr,
                                  IMAPPARSEDREPLY *reply,GETS_DATA *md,
-                                 unsigned long *len,long flags)
+                                 unsigned long *len,imap_parse_string_flags flags)
 {
   char *st;
   char *string = NIL;
@@ -4980,25 +5384,48 @@ unsigned char *imap_parse_string (MAILSTREAM *stream,unsigned char **txtptr,
                                /* search for end of string */
     for (c = **txtptr; c != '"'; ++i,c = *++*txtptr) {
                                /* backslash quotes next character */
-      if (c == '\\') c = *++*txtptr;
+      if (c == '\\') {
+        if (flags == IMAP_PARSE_STRING_FLAGS_FOR_DOUBLE_QUOTE) {
+         if (*((*txtptr)+1) == '"') {
+                 i++;
+                 c = *++*txtptr;
+         }
+        }
+        else {
+          c = *++*txtptr;
+        }
+      }
                                /* CHAR8 not permitted in quoted string */
       if (!bogon && (bogon = (c & 0x80))) {
-       sprintf (LOCAL->tmp,"Invalid CHAR in quoted string: %x",
-                (unsigned int) c);
-       mm_notify (stream,LOCAL->tmp,WARN);
-       stream->unhealthy = T;
+        sprintf (LOCAL->tmp,"Invalid CHAR in quoted string: %x",
+                 (unsigned int) c);
+        mm_notify (stream,LOCAL->tmp,WARN);
+        stream->unhealthy = T;
       }
       else if (!c) {           /* NUL not permitted either */
-       mm_notify (stream,"Unterminated quoted string",WARN);
-       stream->unhealthy = T;
-       if (len) *len = 0;      /* punt, since may be at end of string */
-       return NIL;
+        mm_notify (stream,"Unterminated quoted string",WARN);
+        stream->unhealthy = T;
+        if (len) *len = 0;     /* punt, since may be at end of string */
+        return NIL;
       }
     }
+
     ++*txtptr;                 /* bump past delimiter */
-    string = (char *) fs_get ((size_t) i + 1);
+    string = (char *) fs_get ((size_t) i + 3);
     for (j = 0; j < i; j++) {  /* copy the string */
-      if (*st == '\\') ++st;   /* quoted character */
+
+      if (*st == '\\') /* quoted character */ {
+         if (flags == IMAP_PARSE_STRING_FLAGS_FOR_DOUBLE_QUOTE) {
+               if (*(st + 1) == '"') {
+                 string[j++] = *st++;
+               }
+               else
+                 ++st;
+        }
+        else
+          ++st;        /* quoted character */
+      }
+
       string[j] = *st++;
     }
     string[j] = '\0';          /* tie off string */
@@ -5061,7 +5488,7 @@ unsigned char *imap_parse_string (MAILSTREAM *stream,unsigned char **txtptr,
              else      net_getbuffer (LOCAL->netstream,i,string);
            }
            fs_give ((void **) &reply->line);
-           if (flags && string)        /* need to filter newlines? */
+           if (flags == IMAP_PARSE_STRING_FLAGS_FOR_NEW_LINE && string)        /* need to filter newlines? */
              for (st = string; st = strpbrk (st,"\015\012\011"); *st++ = ' ');
                                        /* get new reply text line */
            if (!(reply->line = net_getline (LOCAL->netstream)))
@@ -5119,7 +5546,8 @@ long imap_cache (MAILSTREAM *stream,unsigned long msgno,char *seg,
        imap_parse_header (stream,&stream->env,text,stl);
       }
                                /* regular caching */
-      else imap_parse_header (stream,&elt->private.msg.env,text,stl);
+      else
+         imap_parse_header (stream,&elt->private.msg.env,text,stl);
     }
   }
                                /* top level text */
@@ -5212,29 +5640,31 @@ void imap_parse_body_structure (MAILSTREAM *stream,BODY *body,
                                /* parse it */
        imap_parse_body_structure (stream,&part->body,txtptr,reply);
       } while (**txtptr == '(');/* for each body part */
-      if (body->subtype = imap_parse_string(stream,txtptr,reply,NIL,NIL,LONGT))
+      if (body->subtype = imap_parse_string(stream,txtptr,reply,NIL,NIL,IMAP_PARSE_STRING_FLAGS_FOR_NEW_LINE))
        ucase (body->subtype);
       else {
        mm_notify (stream,"Missing multipart subtype",WARN);
        stream->unhealthy = T;
        body->subtype = cpystr (rfc822_default_subtype (body->type));
       }
-      if (**txtptr == ' ')     /* multipart parameters */
+      if (**txtptr == ' ' && *((*txtptr)+ 1) != ')') { /* multipart parameters */
        body->parameter = imap_parse_body_parameter (stream,txtptr,reply);
-      if (**txtptr == ' ') {   /* disposition */
+      }
+      if (**txtptr == ' ' && *((*txtptr)+ 1) != ')') { /* disposition */
        imap_parse_disposition (stream,body,txtptr,reply);
        if (LOCAL->cap.extlevel < BODYEXTDSP) LOCAL->cap.extlevel = BODYEXTDSP;
       }
-      if (**txtptr == ' ') {   /* language */
+      if (**txtptr == ' ' && *((*txtptr)+ 1) != ')') { /* language */
        body->language = imap_parse_language (stream,txtptr,reply);
        if (LOCAL->cap.extlevel < BODYEXTLANG)
          LOCAL->cap.extlevel = BODYEXTLANG;
       }
-      if (**txtptr == ' ') {   /* location */
-       body->location = imap_parse_string (stream,txtptr,reply,NIL,NIL,LONGT);
+      if (**txtptr == ' ' && *((*txtptr)+ 1) != ')') { /* location */
+       body->location = imap_parse_string (stream,txtptr,reply,NIL,NIL,IMAP_PARSE_STRING_FLAGS_FOR_NEW_LINE);
        if (LOCAL->cap.extlevel < BODYEXTLOC) LOCAL->cap.extlevel = BODYEXTLOC;
       }
-      while (**txtptr == ' ') imap_parse_extension (stream,txtptr,reply);
+      while (**txtptr == ' ' && *((*txtptr)+ 1) != ')') imap_parse_extension (stream,txtptr,reply);
+      while ((c = **txtptr) == ' ') ++*(txtptr);
       if (**txtptr != ')') {   /* validate ending */
        sprintf (LOCAL->tmp,"Junk at end of multipart body: %.80s",
                 (char *) *txtptr);
@@ -5252,7 +5682,7 @@ void imap_parse_body_structure (MAILSTREAM *stream,BODY *body,
       body->type = TYPEOTHER;  /* assume unknown type */
       body->encoding = ENCOTHER;/* and unknown encoding */
                                /* parse type */
-      if (s = imap_parse_string (stream,txtptr,reply,NIL,NIL,LONGT)) {
+      if (s = imap_parse_string (stream,txtptr,reply,NIL,NIL,IMAP_PARSE_STRING_FLAGS_FOR_NEW_LINE)) {
        ucase (s);              /* application always gets uppercase form */
        for (i = 0;             /* look in existing table */
             (i <= TYPEMAX) && body_types[i] && strcmp (s,body_types[i]); i++);
@@ -5262,7 +5692,7 @@ void imap_parse_body_structure (MAILSTREAM *stream,BODY *body,
          else body_types[i]=s; /* assign empty slot */
        }
       }
-      if (body->subtype = imap_parse_string(stream,txtptr,reply,NIL,NIL,LONGT))
+      if (body->subtype = imap_parse_string(stream,txtptr,reply,NIL,NIL,IMAP_PARSE_STRING_FLAGS_FOR_NEW_LINE))
        ucase (body->subtype);  /* parse subtype */
       else {
        mm_notify (stream,"Missing body subtype",WARN);
@@ -5270,10 +5700,10 @@ void imap_parse_body_structure (MAILSTREAM *stream,BODY *body,
        body->subtype = cpystr (rfc822_default_subtype (body->type));
       }
       body->parameter = imap_parse_body_parameter (stream,txtptr,reply);
-      body->id = imap_parse_string (stream,txtptr,reply,NIL,NIL,LONGT);
+      body->id = imap_parse_string (stream,txtptr,reply,NIL,NIL,IMAP_PARSE_STRING_FLAGS_FOR_NEW_LINE);
       body->description = imap_parse_string (stream,txtptr,reply,NIL,NIL,
-                                            LONGT);
-      if (s = imap_parse_string (stream,txtptr,reply,NIL,NIL,LONGT)) {
+                 IMAP_PARSE_STRING_FLAGS_FOR_NEW_LINE);
+      if (s = imap_parse_string (stream,txtptr,reply,NIL,NIL,IMAP_PARSE_STRING_FLAGS_FOR_NEW_LINE)) {
        ucase (s);              /* application always gets uppercase form */
        for (i = 0;             /* search for body encoding */
             (i <= ENCMAX) && body_encodings[i] && strcmp(s,body_encodings[i]);
@@ -5313,21 +5743,21 @@ void imap_parse_body_structure (MAILSTREAM *stream,BODY *body,
        break;
       }
 \f
-      if (**txtptr == ' ') {   /* extension data - md5 */
-       body->md5 = imap_parse_string (stream,txtptr,reply,NIL,NIL,LONGT);
+      if (**txtptr == ' ' && *(*txtptr + 1) != ')') {  /* extension data - md5 */
+       body->md5 = imap_parse_string (stream,txtptr,reply,NIL,NIL,IMAP_PARSE_STRING_FLAGS_FOR_NEW_LINE);
        if (LOCAL->cap.extlevel < BODYEXTMD5) LOCAL->cap.extlevel = BODYEXTMD5;
       }
-      if (**txtptr == ' ') {   /* disposition */
+      if (**txtptr == ' ' && *(*txtptr + 1) != ')') {  /* disposition */
        imap_parse_disposition (stream,body,txtptr,reply);
        if (LOCAL->cap.extlevel < BODYEXTDSP) LOCAL->cap.extlevel = BODYEXTDSP;
       }
-      if (**txtptr == ' ') {   /* language */
+      if (**txtptr == ' ' && *(*txtptr + 1) != ')') {  /* language */
        body->language = imap_parse_language (stream,txtptr,reply);
        if (LOCAL->cap.extlevel < BODYEXTLANG)
          LOCAL->cap.extlevel = BODYEXTLANG;
       }
-      if (**txtptr == ' ') {   /* location */
-       body->location = imap_parse_string (stream,txtptr,reply,NIL,NIL,LONGT);
+      if (**txtptr == ' ' && *(*txtptr + 1) != ')') {  /* location */
+       body->location = imap_parse_string (stream,txtptr,reply,NIL,NIL,IMAP_PARSE_STRING_FLAGS_FOR_NEW_LINE);
        if (LOCAL->cap.extlevel < BODYEXTLOC) LOCAL->cap.extlevel = BODYEXTLOC;
       }
       while (**txtptr == ' ') imap_parse_extension (stream,txtptr,reply);
@@ -5376,16 +5806,21 @@ PARAMETER *imap_parse_body_parameter (MAILSTREAM *stream,
     if (ret) par = par->next = mail_newbody_parameter ();
     else ret = par = mail_newbody_parameter ();
     if(!(par->attribute=imap_parse_string (stream,txtptr,reply,NIL,NIL,
-                                          LONGT))) {
+               IMAP_PARSE_STRING_FLAGS_FOR_NEW_LINE))) {
       mm_notify (stream,"Missing parameter attribute",WARN);
       stream->unhealthy = T;
       par->attribute = cpystr ("UNKNOWN");
     }
-    if (!(par->value = imap_parse_string (stream,txtptr,reply,NIL,NIL,LONGT))){
+    if (!(par->value = imap_parse_string (stream,txtptr,reply,NIL,NIL,IMAP_PARSE_STRING_FLAGS_FOR_NEW_LINE))){
       sprintf (LOCAL->tmp,"Missing value for parameter %.80s",par->attribute);
       mm_notify (stream,LOCAL->tmp,WARN);
-      stream->unhealthy = T;
-      par->value = cpystr ("UNKNOWN");
+      if (strcmp(par->attribute, "boundary") == 0) {
+        par->value = cpystr ("NIL");
+      }
+      else {
+        stream->unhealthy = T;
+        par->value = cpystr ("UNKNOWN");
+      }
     }
     switch (c = **txtptr) {    /* see what comes after */
     case ' ':                  /* flush whitespace */
@@ -5428,7 +5863,7 @@ void imap_parse_disposition (MAILSTREAM *stream,BODY *body,
   case '(':
     ++*txtptr;                 /* skip open paren */
     body->disposition.type = imap_parse_string (stream,txtptr,reply,NIL,NIL,
-                                               LONGT);
+               IMAP_PARSE_STRING_FLAGS_FOR_NEW_LINE);
     body->disposition.parameter =
       imap_parse_body_parameter (stream,txtptr,reply);
     if (**txtptr != ')') {     /* validate ending */
@@ -5470,7 +5905,7 @@ STRINGLIST *imap_parse_language (MAILSTREAM *stream,unsigned char **txtptr,
   STRINGLIST *ret = NIL;
                                /* language is a list */
   if (*++*txtptr == '(') ret = imap_parse_stringlist (stream,txtptr,reply);
-  else if (s = imap_parse_string (stream,txtptr,reply,NIL,&i,LONGT)) {
+  else if (s = imap_parse_string (stream,txtptr,reply,NIL,&i,IMAP_PARSE_STRING_FLAGS_FOR_NEW_LINE)) {
     (ret = mail_newstringlist ())->text.data = (unsigned char *) s;
     ret->text.size = i;
   }
@@ -5635,6 +6070,9 @@ void imap_parse_capabilities (MAILSTREAM *stream,char *t)
 #ifdef __FEATURE_XLIST_SUPPORT__
     else if (!compare_cstring (t,"XLIST")) LOCAL->cap.xlist = T;
 #endif /* __FEATURE_XLIST_SUPPORT__ */
+#ifdef __FEATURE_METADATA_SUPPORT__
+    else if (!compare_cstring (t,"METADATA") || !compare_cstring (t,"METADATA-SERVER")) LOCAL->cap.metadata = T;
+#endif /* __FEATURE_METADATA_SUPPORT__ */
                                /* ignore other capabilities */
   }
                                /* disable LOGIN if PLAIN also advertised */
@@ -5643,6 +6081,16 @@ void imap_parse_capabilities (MAILSTREAM *stream,char *t)
       (i = mail_lookup_auth_name ("LOGIN",NIL)) && (--i < MAXAUTHENTICATORS))
     LOCAL->cap.auth &= ~(1 << i);
 }
+
+/* IMAP parse id
+ * Accepts: MAIL stream
+ *         reply
+ */
+
+void imap_parse_id (MAILSTREAM *stream,char *t)
+{
+  /* If ID information from host is needed, add parser here */
+}
 \f
 /* IMAP load cache
  * Accepts: MAIL stream
@@ -5767,8 +6215,10 @@ char *imap_reform_sequence (MAILSTREAM *stream,char *sequence,long flags)
 
 char *imap_host (MAILSTREAM *stream)
 {
-  if (stream->dtb != &imapdriver)
-    fatal ("imap_host called on non-IMAP stream!");
+  if (stream->dtb != &imapdriver) {
+    MM_FATAL("imap_host called on non-IMAP stream!");
+    return NULL;
+  }
                                /* return host name on stream if open */
   return (LOCAL && LOCAL->netstream) ? net_host (LOCAL->netstream) :
     ".NO-IMAP-CONNECTION.";
@@ -5782,7 +6232,9 @@ char *imap_host (MAILSTREAM *stream)
 
 IMAPCAP *imap_cap (MAILSTREAM *stream)
 {
-  if (stream->dtb != &imapdriver)
-    fatal ("imap_cap called on non-IMAP stream!");
+  if (stream->dtb != &imapdriver) {
+    MM_FATAL("imap_cap called on non-IMAP stream!");
+    return NULL;
+  }
   return &LOCAL->cap;          /* return capability structure */
 }
index 3f94c68..e8f5ffe 100755 (executable)
@@ -93,6 +93,9 @@ typedef struct imap_cap {
 #ifdef __FEATURE_XLIST_SUPPORT__
   unsigned int xlist : 1;
 #endif /* __FEATURE_XLIST_SUPPORT__ */
+#ifdef __FEATURE_METADATA_SUPPORT__
+  unsigned int metadata : 1;
+#endif /* __FEATURE_METADATA_SUPPORT__ */
 } IMAPCAP;
 \f
 /* IMAP4rev1 level or better */
@@ -249,6 +252,14 @@ typedef struct imap_cap {
 /* Has WITHIN extension */
 
 #define LEVELWITHIN(stream) imap_cap (stream)->within
+
+
+#ifdef __FEATURE_METADATA_SUPPORT__
+/* Has METADATA extension */
+
+#define LEVELMETADATA(stream) imap_cap (stream)->metadata
+#endif /* __FEATURE_METADATA_SUPPORT__ */
+
 \f
 /* Body structure extension levels */
 
@@ -282,3 +293,9 @@ long imap_myrights (MAILSTREAM *stream,char *mailbox);
 long imap_setquota (MAILSTREAM *stream,char *qroot,STRINGLIST *limits);
 long imap_getquota (MAILSTREAM *stream,char *qroot);
 long imap_getquotaroot (MAILSTREAM *stream,char *mailbox);
+
+#ifdef __FEATURE_METADATA_SUPPORT__
+/* RFC5464 - METADATA extension */
+char *imap_getmetadata (MAILSTREAM *stream, char *mailbox, char *entry);
+long imap_setmetadata (MAILSTREAM *stream, char *mailbox, char *entry, char *value);
+#endif /* __FEATURE_METADATA_SUPPORT__ */
index 66fb599..9de09a7 100755 (executable)
@@ -17,6 +17,7 @@
   auth_link (&auth_md5);               /* link in the md5 authenticator */
   auth_link (&auth_pla);               /* link in the pla authenticator */
   auth_link (&auth_log);               /* link in the log authenticator */
+  auth_link (&auth_xoauth2);   /* link in the xoauth2 authenticator */
   mail_versioncheck (CCLIENTVERSION);
   ssl_onceonlyinit ();
   mail_parameters (NIL,SET_DISABLEPLAINTEXT,(void *) 2);
index e7ed506..7fe61dd 100755 (executable)
@@ -17,3 +17,4 @@ extern AUTHENTICATOR auth_ext;
 extern AUTHENTICATOR auth_md5;
 extern AUTHENTICATOR auth_pla;
 extern AUTHENTICATOR auth_log;
+extern AUTHENTICATOR auth_xoauth2;
index 6ed835b..15ce4e1 100755 (executable)
@@ -22,7 +22,6 @@
 
 
 #include "mail.c"
-//#include "em-core-utils.h"
 #include <errno.h>
 #include <sys/time.h>
 #include <unistd.h>
 
 #include "tcp_unix.h"
 
+#define RETRY_COUNT_LIMITATION 3
 
-long 
-tcp_getdata_lnx(TCPSTREAM* stream)
+long tcp_getdata_lnx(TCPSTREAM* stream)
 {
        struct timeval tmout;
        fd_set readfds;
        int nread, sret, sockid, maxfd;
-       int max_timeout = 0;
+       int retry_count = 0;
+       long ttmo_read = (long) mail_parameters (NIL, GET_READTIMEOUT, NIL);
        
        sockid = stream->tcpsi;
        maxfd = sockid + 1;
@@ -59,8 +59,8 @@ tcp_getdata_lnx(TCPSTREAM* stream)
        
        while (stream->ictr < 1) 
        {
-               tmout.tv_usec = 0;//1000*10;
-               tmout.tv_sec = 1;
+               tmout.tv_usec = 0;
+               tmout.tv_sec  = ttmo_read ? ttmo_read : 5;
                
                FD_ZERO(&readfds);
                FD_SET(sockid, &readfds);
@@ -72,27 +72,25 @@ tcp_getdata_lnx(TCPSTREAM* stream)
                        return false;
                }
                else if (!sret) {
-                       if (max_timeout >= 50) {
-                               IMAP_DEBUG_EXCEPTION("max select timeout %d", max_timeout);
-                               
-                               //em_core_set_network_error(EMF_ERROR_NO_RESPONSE);
+                       if (retry_count >= RETRY_COUNT_LIMITATION) {
+                               IMAP_DEBUG_EXCEPTION("retry_count exceeded limitation", retry_count);
                                return false;
                        }
                        
-                       IMAP_DEBUG_EXCEPTION("%d select timeout", max_timeout);
+                       IMAP_DEBUG_EXCEPTION("retry_count [%d]", retry_count);
                        
-                       ++max_timeout;
+                       ++retry_count;
                        continue;
                }
                
                if ((nread = read(sockid, stream->ibuf, BUFLEN)) < 0) {
-                       IMAP_DEBUG_EXCEPTION("\t socket read failed...\n");
+                       IMAP_DEBUG_EXCEPTION("socket read failed...");
                        tcp_abort(stream);
                        return false;
                }
                
                if (!nread) {
-                       IMAP_DEBUG_EXCEPTION("\t socket read no data...\n");
+                       IMAP_DEBUG_EXCEPTION("socket read empty data...");
                        tcp_abort(stream);
                        return false;
                }
@@ -154,31 +152,11 @@ char *tcp_getline_lnx (void *vstream)
 }
 #endif
 
-
-int try_auth = 0;
 unsigned int mail_lookup_auth_name (char *mechanism,long flags)
 {
   int i;
   AUTHENTICATOR *auth;
 
-  if(!try_auth) return 0;
-
-  for (i = 1, auth = mailauthenticators; auth; i++, auth = auth->next)
-    if (auth->client && !(flags & ~auth->flags) &&
-       !compare_cstring (auth->name,mechanism))
-      return i;
-  return 0;
-}
-
-// 22-Mar-2010 added
-int try_auth_smtp = 0;
-unsigned int mail_lookup_auth_name_smtp (char *mechanism,long flags)
-{
-  int i;
-  AUTHENTICATOR *auth;
-
-  if(!try_auth_smtp) return 0;
-
   for (i = 1, auth = mailauthenticators; auth; i++, auth = auth->next)
     if (auth->client && !(flags & ~auth->flags) &&
        !compare_cstring (auth->name,mechanism))
index a4ffc43..5b2b737 100755 (executable)
@@ -217,7 +217,7 @@ void *mm_cache (MAILSTREAM *stream,unsigned long msgno,long op)
     stream->sc[i] = NIL;
     break;
   default:
-    fatal ("Bad mm_cache op");
+    MM_FATAL("Bad mm_cache op");
     break;
   }
   return ret;
@@ -328,24 +328,28 @@ void *mail_parameters (MAILSTREAM *stream,long function,void *value)
   AUTHENTICATOR *a;
   switch ((int) function) {
   case SET_INBOXPATH:
-    fatal ("SET_INBOXPATH not permitted");
+    MM_FATAL ("SET_INBOXPATH not permitted");
+    return NULL;
   case GET_INBOXPATH:
     if ((stream || (stream = mail_open (NIL,"INBOX",OP_PROTOTYPE))) &&
        stream->dtb) ret = (*stream->dtb->parameters) (function,value);
     break;
   case SET_THREADERS:
-    fatal ("SET_THREADERS not permitted");
+    MM_FATAL ("SET_THREADERS not permitted");
+    return NULL;
   case GET_THREADERS:          /* use stream dtb instead of global */
     ret = (stream && stream->dtb) ?
                                /* KLUDGE ALERT: note stream passed as value */
       (*stream->dtb->parameters) (function,stream) : (void *) &mailthreadlist;
     break;
   case SET_NAMESPACE:
-    fatal ("SET_NAMESPACE not permitted");
-    break;
+    MM_FATAL ("SET_NAMESPACE not permitted");
+    return NULL;
   case SET_NEWSRC:             /* too late on open stream */
-    if (stream && stream->dtb && (stream != ((*stream->dtb->open) (NIL))))
-      fatal ("SET_NEWSRC not permitted");
+    if (stream && stream->dtb && (stream != ((*stream->dtb->open) (NIL)))) {
+      MM_FATAL ("SET_NEWSRC not permitted");
+      return NULL;
+    }
     else ret = env_parameters (function,value);
     break;
   case GET_NAMESPACE:
@@ -356,24 +360,31 @@ void *mail_parameters (MAILSTREAM *stream,long function,void *value)
        env_parameters (function,value);
     break;
   case ENABLE_DEBUG:
-    fatal ("ENABLE_DEBUG not permitted");
+    MM_FATAL ("ENABLE_DEBUG not permitted");
+    return NULL;
   case DISABLE_DEBUG:
-    fatal ("DISABLE_DEBUG not permitted");
+    MM_FATAL ("DISABLE_DEBUG not permitted");
+    return NULL;
   case SET_DIRFMTTEST:
-    fatal ("SET_DIRFMTTEST not permitted");
+    MM_FATAL ("SET_DIRFMTTEST not permitted");
+    return NULL;
   case GET_DIRFMTTEST:
     if (!(stream && stream->dtb &&
-         (ret = (*stream->dtb->parameters) (function,NIL))))
-      fatal ("GET_DIRFMTTEST not permitted");
+         (ret = (*stream->dtb->parameters) (function,NIL)))) {
+      MM_FATAL ("GET_DIRFMTTEST not permitted");
+      return NULL;
+    }
     break;
 \f
   case SET_DRIVERS:
-    fatal ("SET_DRIVERS not permitted");
+    MM_FATAL ("SET_DRIVERS not permitted");
+    return NULL;
   case GET_DRIVERS:            /* always return global */
     ret = (void *) maildrivers;
     break;
   case SET_DRIVER:
-    fatal ("SET_DRIVER not permitted");
+    MM_FATAL ("SET_DRIVER not permitted");
+    return NULL;
   case GET_DRIVER:
     for (d = maildrivers; d && compare_cstring (d->name,(char *) value);
         d = d->next);
@@ -636,7 +647,10 @@ void *mail_parameters (MAILSTREAM *stream,long function,void *value)
       if (stream->snarf.name) fs_give ((void **) &stream->snarf.name);
       stream->snarf.name = cpystr ((char *) value);
     }
-    else fatal ("SET_SNARFMAILBOXNAME with no stream");
+    else {
+       MM_FATAL ("SET_SNARFMAILBOXNAME with no stream");
+       return NULL;
+    }
   case GET_SNARFMAILBOXNAME:
     if (stream) ret = (void *) stream->snarf.name;
     break;
@@ -806,9 +820,14 @@ long mail_valid_net_parse_work (char *name,NETMBX *mb,char *service)
        else if (!compare_cstring (s,"loser")) mb->loser = T;
        else if (!compare_cstring (s,"tls") && !mb->notlsflag)
          mb->tlsflag = T;
-       //APOP Authentication - shasikala.p@siso.com
        else if (!compare_cstring (s,"apop"))
          mb->apop = T;
+       else if (!compare_cstring (s,"force_tls_v1_0"))
+         mb->force_tls_v1_0 = T;
+       else if (!compare_cstring (s,"needauth"))
+         mb->auth_method = AUTH_METHOD_DEFAULT;
+       else if (!compare_cstring (s,"xoauth2"))
+         mb->auth_method = AUTH_METHOD_XOAUTH2;
        else if (!compare_cstring (s,"tls-sslv23") && !mb->notlsflag)
          mb->tlssslv23 = mb->tlsflag = T;
        else if (!compare_cstring (s,"notls") && !mb->tlsflag)
@@ -1260,7 +1279,10 @@ MAILSTREAM *mail_open (MAILSTREAM *stream,char *name,long options)
     d = mail_valid (NIL,name,(options & OP_SILENT) ?
                    (char *) NIL : "open mailbox");
   }
-  return d ? mail_open_work (d,stream,name,options) : stream;
+  if(d)
+         return mail_open_work (d,stream,name,options);
+
+  return stream;
 }
 \f
 /* Mail open worker routine
@@ -1276,16 +1298,17 @@ MAILSTREAM *mail_open_work (DRIVER *d,MAILSTREAM *stream,char *name,
 {
   int i;
   char tmp[MAILTMPLEN];
+  char *local_mailbox_name = NULL;
   NETMBX mb;
   if (options & OP_PROTOTYPE) return (*d->open) (NIL);
   /* name is copied here in case the caller does a re-open using
    * stream->mailbox or stream->original_mailbox as the argument.
    */
-  name = cpystr (name);                /* make copy of name */
+  local_mailbox_name = cpystr (name);          /* make copy of name */
   if (stream) {                        /* recycling requested? */
     if ((stream->dtb == d) && (d->flags & DR_RECYCLE) &&
        ((d->flags & DR_HALFOPEN) || !(options & OP_HALFOPEN)) &&
-       mail_usable_network_stream (stream,name)) {
+       mail_usable_network_stream (stream,local_mailbox_name)) {
                                /* yes, checkpoint if needed */
       if (d->flags & DR_XPOINT) mail_check (stream);
       mail_free_cache (stream);        /* clean up stream */
@@ -1308,7 +1331,7 @@ MAILSTREAM *mail_open_work (DRIVER *d,MAILSTREAM *stream,char *name,
   }
                                /* check if driver does not support halfopen */
   else if ((options & OP_HALFOPEN) && !(d->flags & DR_HALFOPEN)) {
-    fs_give ((void **) &name);
+    fs_give ((void **) &local_mailbox_name);
     return NIL;
   }
 \f
@@ -1318,7 +1341,7 @@ MAILSTREAM *mail_open_work (DRIVER *d,MAILSTREAM *stream,char *name,
                                     sizeof (MAILSTREAM)),(long) 0,CH_INIT);
   stream->dtb = d;             /* set dispatch */
                                /* set mailbox name */
-  stream->mailbox = cpystr (stream->original_mailbox = name);
+  stream->mailbox = cpystr (stream->original_mailbox = local_mailbox_name);
                                /* initialize stream flags */
   stream->inbox = stream->lock = NIL;
   stream->debug = (options & OP_DEBUG) ? T : NIL;
@@ -1350,9 +1373,15 @@ MAILSTREAM *mail_open_work (DRIVER *d,MAILSTREAM *stream,char *name,
 MAILSTREAM *mail_close_full (MAILSTREAM *stream,long options)
 {
   int i;
+  char tmp[MAILTMPLEN] = { 0, };
   if (stream) {                        /* make sure argument given */
-                               /* do the driver's close action */
-    if (stream->dtb) (*stream->dtb->close) (stream,options);
+    if(stream->unhealthy) {
+      snprintf (tmp, MAILTMPLEN, "Checking 'unhealthy' flag of MAILSTEAM.. [%d]", stream->unhealthy);
+      MM_LOG (tmp,(long)WARN);
+      return NIL;
+    }
+       /* do the driver's close action */
+    if (stream->dtb && stream->dtb->close) (*stream->dtb->close) (stream,options);
     stream->dtb = NIL;         /* resign driver */
     if (stream->mailbox) fs_give ((void **) &stream->mailbox);
     if (stream->original_mailbox)
@@ -1428,7 +1457,8 @@ MESSAGECACHE *mail_elt (MAILSTREAM *stream,unsigned long msgno)
     char tmp[MAILTMPLEN];
     sprintf (tmp,"Bad msgno %lu in mail_elt, nmsgs = %lu, mbx=%.80s",
             msgno,stream->nmsgs,stream->mailbox ? stream->mailbox : "???");
-    fatal (tmp);
+    MM_FATAL (tmp);
+    return NULL;
   }
   return (MESSAGECACHE *) (*mailcache) (stream,msgno,CH_MAKEELT);
 }
@@ -2071,7 +2101,10 @@ long mail_partial_text (MAILSTREAM *stream,unsigned long msgno,char *section,
   BODY *b;
   char tmp[MAILTMPLEN];
   unsigned long i;
-  if (!mailgets) fatal ("mail_partial_text() called without a mailgets!");
+  if (!mailgets) {
+         MM_FATAL ("mail_partial_text() called without a mailgets!");
+         return NIL;
+  }
   if (section && (strlen (section) > (MAILTMPLEN - 20))) return NIL;
   if (flags & FT_UID) {                /* UID form of call */
     if (msgno = mail_msgno (stream,msgno)) flags &= ~FT_UID;
@@ -2142,7 +2175,10 @@ long mail_partial_body (MAILSTREAM *stream,unsigned long msgno,char *section,
   unsigned long i;
   if (!(section && *section))  /* top-level text wanted? */
     return mail_partial_text (stream,msgno,NIL,first,last,flags);
-  if (!mailgets) fatal ("mail_partial_body() called without a mailgets!");
+  if (!mailgets) {
+         MM_FATAL ("mail_partial_body() called without a mailgets!");
+         return NIL;
+  }
   if (flags & FT_UID) {                /* UID form of call */
     if (msgno = mail_msgno (stream,msgno)) flags &= ~FT_UID;
     else return NIL;           /* must get UID/msgno map first */
@@ -2590,9 +2626,9 @@ long mail_copy_full (MAILSTREAM *stream,char *sequence,char *mailbox,
 /* Append data package to use for old single-message mail_append() interface */
 
 typedef struct mail_append_package {
-  char *flags;                 /* initial flags */
-  char *date;                  /* message internal date */
-  STRING *message;             /* stringstruct of message */
+       char *flags;                    /* initial flags */
+       char *date;                     /* message internal date */
+       STRING *message;                /* stringstruct of message */
 } APPENDPACKAGE;
 
 
@@ -2616,6 +2652,39 @@ static long mail_append_single (MAILSTREAM *stream,void *data,char **flags,
   return LONGT;                        /* always return success */
 }
 
+static long mail_append_single_command (MAILSTREAM *stream,void *data,char **flags,
+                               char **date,STRING **message)
+{
+  APPENDPACKAGE *ap = (APPENDPACKAGE *) data;
+  *flags = ap->flags;          /* get desired data from the package */
+  *date = ap->date;
+  *message = ap->message;
+  ap->message = NIL;           /* so next callback puts a stop to it */
+  return 2;                    /* always return success */
+}
+
+static long mail_append_single_message (MAILSTREAM *stream,void *data,char **flags,
+                               char **date,STRING **message)
+{
+  APPENDPACKAGE *ap = (APPENDPACKAGE *) data;
+  *flags = ap->flags;          /* get desired data from the package */
+  *date = ap->date;
+  *message = ap->message;
+  ap->message = NIL;           /* so next callback puts a stop to it */
+  return 3;                    /* always return success */
+}
+
+static long mail_append_single_end (MAILSTREAM *stream,void *data,char **flags,
+                               char **date,STRING **message)
+{
+  APPENDPACKAGE *ap = (APPENDPACKAGE *) data;
+  *flags = ap->flags;          /* get desired data from the package */
+  *date = ap->date;
+  *message = ap->message;
+  ap->message = NIL;           /* so next callback puts a stop to it */
+  return 4;                    /* always return success */
+}
+
 
 /* Mail append message string
  * Accepts: mail stream
@@ -2635,7 +2704,34 @@ long mail_append_full (MAILSTREAM *stream,char *mailbox,char *flags,char *date,
   ap.message = message;
   return mail_append_multiple (stream,mailbox,mail_append_single,(void *) &ap);
 }
-\f
+
+long mail_append_command (MAILSTREAM *stream,char *mailbox,char *flags,char *date, STRING *message)
+{
+  APPENDPACKAGE ap;
+  ap.flags = flags;            /* load append package */
+  ap.date = date;
+  ap.message = message;
+  return mail_append_multiple (stream,mailbox,mail_append_single_command,(void *) &ap);
+}
+
+long mail_append_message (MAILSTREAM *stream,char *mailbox,STRING *message)
+{
+  APPENDPACKAGE ap;
+  ap.flags = NULL;
+  ap.date = NULL;
+  ap.message = message;
+  return mail_append_multiple (stream,mailbox,mail_append_single_message,(void *) &ap);
+}
+
+long mail_append_end (MAILSTREAM *stream,char *mailbox)
+{
+  APPENDPACKAGE ap;
+  ap.flags = NULL;
+  ap.date = NULL;
+  ap.message = NULL;
+  return mail_append_multiple (stream,mailbox,mail_append_single_end,(void *) &ap);
+}
+
 /* Mail append message(s)
  * Accepts: mail stream
  *         destination mailbox
@@ -2644,52 +2740,53 @@ long mail_append_full (MAILSTREAM *stream,char *mailbox,char *flags,char *date,
  * Returns: T on success, NIL on failure
  */
 
-long mail_append_multiple (MAILSTREAM *stream,char *mailbox,append_t af,
-                          void *data)
+long mail_append_multiple (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
 {
-  char *s,tmp[MAILTMPLEN];
-  DRIVER *d = NIL;
-  long ret = NIL;
-                               /* never allow names with newlines */
-  if (strpbrk (mailbox,"\015\012"))
-    MM_LOG ("Can't append to mailbox with such a name",ERROR);
-  else if (strlen (mailbox) >=
-          (NETMAXHOST+(NETMAXUSER*2)+NETMAXMBX+NETMAXSRV+50)) {
-    sprintf (tmp,"Can't append %.80s: %s",mailbox,(*mailbox == '{') ?
-            "invalid remote specification" : "no such mailbox");
-    MM_LOG (tmp,ERROR);
-  }
-                               /* special driver hack? */
-  else if (!strncmp (lcase (strcpy (tmp,mailbox)),"#driver.",8)) {
-                               /* yes, tie off name at likely delimiter */
-    if (!(s = strpbrk (tmp+8,"/\\:"))) {
-      sprintf (tmp,"Can't append to mailbox %.80s: bad driver syntax",mailbox);
-      MM_LOG (tmp,ERROR);
-      return NIL;
-    }
-    *s++ = '\0';               /* tie off at delimiter */
-    if (!(d = (DRIVER *) mail_parameters (NIL,GET_DRIVER,tmp+8))) {
-      sprintf (tmp,"Can't append to mailbox %.80s: unknown driver",mailbox);
-      MM_LOG (tmp,ERROR);
-    }
-    else ret = SAFE_APPEND (d,stream,mailbox + (s - tmp),af,data);
-  }
-  else if (d = mail_valid (stream,mailbox,NIL))
-    ret = SAFE_APPEND (d,stream,mailbox,af,data);
-  /* No driver, try for TRYCREATE if no stream.  Note that we use the
-   * createProto here, not the appendProto, since the dummy driver already
-   * took care of the appendProto case.  Otherwise, if appendProto is set to
-   * NIL, we won't get a TRYCREATE.
-   */
-  else if (!stream && (stream = default_proto (NIL)) && stream->dtb &&
-          SAFE_APPEND (stream->dtb,stream,mailbox,af,data))
-                               /* timing race? */
-    MM_NOTIFY (stream,"Append validity confusion",WARN);
-                               /* generate error message */
-  else mail_valid (stream,mailbox,"append to mailbox");
-  return ret;
+       char *s,tmp[MAILTMPLEN];
+       DRIVER *d = NIL;
+       long ret = NIL;
+
+       /* never allow names with newlines */
+       if (strpbrk (mailbox,"\015\012"))
+               MM_LOG ("Can't append to mailbox with such a name",ERROR);
+       else if (strlen (mailbox) >= (NETMAXHOST+(NETMAXUSER*2)+NETMAXMBX+NETMAXSRV+50)) {
+               sprintf (tmp,"Can't append %.80s: %s",mailbox,(*mailbox == '{') ? "invalid remote specification" : "no such mailbox");
+               MM_LOG (tmp,ERROR);
+       }
+       /* special driver hack? */
+       else if (!strncmp (lcase (strcpy (tmp,mailbox)),"#driver.",8)) {
+               /* yes, tie off name at likely delimiter */
+               if (!(s = strpbrk (tmp+8,"/\\:"))) {
+                       sprintf (tmp,"Can't append to mailbox %.80s: bad driver syntax",mailbox);
+                       MM_LOG (tmp,ERROR);
+                       return NIL;
+               }
+               *s++ = '\0';            /* tie off at delimiter */
+
+               if (!(d = (DRIVER *) mail_parameters (NIL,GET_DRIVER,tmp+8))) {
+                       sprintf (tmp,"Can't append to mailbox %.80s: unknown driver",mailbox);
+                       MM_LOG (tmp,ERROR);
+               } else
+                       ret = SAFE_APPEND (d,stream,mailbox + (s - tmp),af,data);
+       }
+       else if (d = mail_valid (stream,mailbox,NIL))
+               ret = SAFE_APPEND (d,stream,mailbox,af,data);
+       /* No driver, try for TRYCREATE if no stream.  Note that we use the
+       * createProto here, not the appendProto, since the dummy driver already
+       * took care of the appendProto case.  Otherwise, if appendProto is set to
+       * NIL, we won't get a TRYCREATE.
+       */
+       else if (!stream && (stream = default_proto (NIL)) && stream->dtb && SAFE_APPEND (stream->dtb,stream,mailbox,af,data))
+               /* timing race? */
+               MM_NOTIFY (stream,"Append validity confusion",WARN);
+               /* generate error message */
+
+       else
+               mail_valid (stream,mailbox,"append to mailbox");
+
+       return ret;
 }
-\f
+
 /* Mail garbage collect stream
  * Accepts: mail stream
  *         garbage collection flags
@@ -3262,7 +3359,7 @@ void mail_lock (MAILSTREAM *stream)
     char tmp[MAILTMPLEN];
     sprintf (tmp,"Lock when already locked, mbx=%.80s",
             stream->mailbox ? stream->mailbox : "???");
-    fatal (tmp);
+    MM_FATAL (tmp);
   }
   else stream->lock = T;       /* lock stream */
 }
@@ -3274,7 +3371,7 @@ void mail_lock (MAILSTREAM *stream)
  
 void mail_unlock (MAILSTREAM *stream)
 {
-  if (!stream->lock) fatal ("Unlock when not locked");
+  if (!stream->lock) MM_FATAL ("Unlock when not locked");
   else stream->lock = NIL;     /* unlock stream */
 }
 
@@ -4005,7 +4102,8 @@ char *mail_search_gets (readfn_t f,void *stream,unsigned long size,
     sprintf (tmp,"Search botch, mbx = %.80s, %s = %lu[%.80s]",
             md->stream->mailbox,
             (md->flags & FT_UID) ? "UID" : "msg",md->msgno,md->what);
-    fatal (tmp);
+    MM_FATAL (tmp);
+    return NIL;
   }
                                /* initially no match for search */
   md->stream->private.search.result = NIL;
@@ -4076,6 +4174,10 @@ SEARCHPGM *mail_criteria (char *criteria)
        else if (!strcmp (criterion+1,"ROM"))
          f = mail_criteria_string (&pgm->from,&r);
        break;
+      case 'H':                 /* possible HEADER */
+       if (!strcmp (criterion+1,"EADER"))
+          f = mail_criteria_header_string (&pgm->header, &r);
+        break;
       case 'K':                        /* possible KEYWORD */
        if (!strcmp (criterion+1,"EYWORD"))
          f = mail_criteria_string (&pgm->keyword,&r);
@@ -4157,6 +4259,25 @@ int mail_criteria_date (unsigned short *date,char **r)
  * Returns: shortdate
  */
 
+int mail_criteria_header_string(SEARCHHEADER **hdr, char **r)
+{
+  unsigned long line_n, text_n;
+  char e,*d,*end = " ",*c = strtok_r (NIL,"",r);
+  char *line, *text;
+  if (!c) return NIL;
+  
+  if (line = strtok_r (c,end,r)) line_n = strlen (line);
+  else return NIL;
+
+  if (text = strtok_r (NIL,end,r)) text_n = strlen (text);
+  else return NIL;
+
+  while (*hdr) hdr = &(*hdr)->next;
+  *hdr = mail_newsearchheader(line, text);
+
+  return T;
+}
+
 unsigned short mail_shortdate (unsigned int year,unsigned int month,
                               unsigned int day)
 {
@@ -4555,7 +4676,8 @@ SORTCACHE **mail_sort_loadcache (MAILSTREAM *stream,SORTPGM *pgm)
        }
        break;
       default:
-       fatal ("Unknown sort function");
+       MM_FATAL ("Unknown sort function");
+       return NIL;
       }
     }
   return sc;
@@ -4882,7 +5004,10 @@ THREADNODE *mail_thread_orderedsubject (MAILSTREAM *stream,char *charset,
       tc = (THREADNODE **) fs_get (i * sizeof (THREADNODE *));
                                /* load threadnode cache */
       for (j = 0, cur = thr; cur; cur = cur->branch) tc[j++] = cur;
-      if (i != j) fatal ("Threadnode cache confusion");
+      if (i != j) {
+         MM_FATAL ("Threadnode cache confusion");
+         return NIL;
+      }
       qsort ((void *) tc,i,sizeof (THREADNODE *),mail_thread_compare_date);
       for (j = 0, --i; j < i; j++) tc[j]->branch = tc[j+1];
       tc[j]->branch = NIL;     /* end of root */
@@ -6226,6 +6351,9 @@ NETSTREAM *net_open (NETMBX *mb,NETDRIVER *dv,unsigned long port,
   NETSTREAM *stream = NIL;
   char tmp[MAILTMPLEN];
   unsigned long flags = mb->novalidate ? NET_NOVALIDATECERT : 0;
+
+  flags |= (mb->force_tls_v1_0) ? NET_FORCE_LOWER_TLS_VERSION : 0;
+
   if (strlen (mb->host) >= NETMAXHOST) {
     sprintf (tmp,"Invalid host name: %.80s",mb->host);
     MM_LOG (tmp,ERROR);
@@ -6339,6 +6467,7 @@ long net_getbuffer (void *st,unsigned long size,char *buffer)
 
 long net_soutr (NETSTREAM *stream,char *string)
 {
+  if (!stream) return NIL;
   return (*stream->dtb->soutr) (stream->stream,string);
 }
 
@@ -6352,6 +6481,7 @@ long net_soutr (NETSTREAM *stream,char *string)
 
 long net_sout (NETSTREAM *stream,char *string,unsigned long size)
 {
+  if (!stream) return NIL;
   return (*stream->dtb->sout) (stream->stream,string,size);
 }
 \f
@@ -6361,6 +6491,7 @@ long net_sout (NETSTREAM *stream,char *string,unsigned long size)
 
 void net_close (NETSTREAM *stream)
 {
+  if (!stream) return;
   if (stream->stream) (*stream->dtb->close) (stream->stream);
   fs_give ((void **) &stream);
 }
@@ -6373,6 +6504,8 @@ void net_close (NETSTREAM *stream)
 
 char *net_host (NETSTREAM *stream)
 {
+  if(stream == NULL || stream->stream == NULL)
+         return "";
   return (*stream->dtb->host) (stream->stream);
 }
 
@@ -6384,6 +6517,8 @@ char *net_host (NETSTREAM *stream)
 
 char *net_remotehost (NETSTREAM *stream)
 {
+  if(stream == NULL || stream->stream == NULL)
+    return "";
   return (*stream->dtb->remotehost) (stream->stream);
 }
 \f
@@ -6394,6 +6529,8 @@ char *net_remotehost (NETSTREAM *stream)
 
 unsigned long net_port (NETSTREAM *stream)
 {
+  if(stream == NULL || stream->stream == NULL)
+    return 0;
   return (*stream->dtb->port) (stream->stream);
 }
 
@@ -6405,5 +6542,7 @@ unsigned long net_port (NETSTREAM *stream)
 
 char *net_localhost (NETSTREAM *stream)
 {
+  if(stream == NULL || stream->stream == NULL)
+    return "";
   return (*stream->dtb->localhost) (stream->stream);
 }
index 138b162..10489b0 100755 (executable)
 #define OP_NOKOD (long) 0x800  /* suppress kiss-of-death */
 #define OP_SNIFF (long) 0x1000 /* metadata only open */
                                /* reserved for application use */
+
+#define OP_FORCE_LOWER_TLS_VERSION 0x2000 /* force lower TLS version */
 #define OP_RESERVED (unsigned long) 0xff000000
 
 
 #define NET_TLSCLIENT ((unsigned long) 0x10000000)
                                /* try SSL mode */
 #define NET_TRYSSL ((unsigned long) 0x8000000)
+                               /* force lower TLS version */
+#define NET_FORCE_LOWER_TLS_VERSION ((unsigned long) 0x4000000)
 \f
 /* Close options */
 
 
 #endif /* __FEATURE_XLIST_SUPPORT__ */
 
+#define AUTH_METHOD_NONE     0
+#define AUTH_METHOD_DEFAULT  1
+#define AUTH_METHOD_XOAUTH2  2
 
 /* Sort functions */
 
@@ -678,7 +685,9 @@ typedef struct net_mailbox {
   unsigned int norsh : 1;      /* don't use rsh/ssh */
   unsigned int loser : 1;      /* server is a loser */
   unsigned int tlssslv23 : 1;  /* force SSLv23 client method over TLS */
-  unsigned int apop; /*APOP Authentication - shasikala.p@siso.com*/
+  unsigned int apop; /*APOP Authentication */
+  unsigned int force_tls_v1_0 : 1; /* force TLS v1.0 */
+  unsigned int auth_method; /* Authentication method */
 } NETMBX;
 \f
 /* Item in an address list */
@@ -1612,7 +1621,15 @@ DRIVER {
 #define MM_FLAGS mm_flags
 #define MM_NOTIFY mm_notify
 #define MM_STATUS mm_status
-#define MM_LOG mm_log
+/* #define MM_LOG mm_log */
+#define MM_LOG(str, errflg)  \
+       do {\
+               char *path_log = g_strdup_printf("%s (%d)> %s", __FUNCTION__, __LINE__, str);\
+               mm_log(path_log, errflg);\
+               g_free(path_log);\
+               path_log = NULL;\
+       } while(0)
+
 #define MM_CRITICAL mm_critical
 #define MM_NOCRITICAL mm_nocritical
 #define MM_DISKERROR mm_diskerror
@@ -1637,6 +1654,9 @@ void mm_nocritical (MAILSTREAM *stream);
 long mm_diskerror (MAILSTREAM *stream,long errcode,long serious);
 void mm_fatal (char *string);
 void *mm_cache (MAILSTREAM *stream,unsigned long msgno,long op);
+#ifdef __FEATURE_IMAP_ID_SUPPORT__
+void mm_imap_id (char **id_string);
+#endif /* __FEATURE_IMAP_ID_SUPPORT__ */
 
 extern STRINGDRIVER mail_string;
 void mail_versioncheck (char *version);
@@ -1715,6 +1735,9 @@ long mail_copy_full (MAILSTREAM *stream,char *sequence,char *mailbox,
                     long options);
 long mail_append_full (MAILSTREAM *stream,char *mailbox,char *flags,char *date,
                       STRING *message);
+long mail_append_command (MAILSTREAM *stream,char *mailbox,char *flags,char *date, STRING *message);
+long mail_append_message (MAILSTREAM *stream,char *mailbox,STRING *message);
+long mail_append_end (MAILSTREAM *stream,char *mailbox);
 long mail_append_multiple (MAILSTREAM *stream,char *mailbox,append_t af,
                           void *data);
 void mail_gc (MAILSTREAM *stream,long gcflags);
@@ -1755,6 +1778,7 @@ char *mail_search_gets (readfn_t f,void *stream,unsigned long size,
 SEARCHPGM *mail_criteria (char *criteria);
 int mail_criteria_date (unsigned short *date,char **r);
 int mail_criteria_string (STRINGLIST **s,char **r);
+int mail_criteria_header_string (SEARCHHEADER **hdr, char **r);
 unsigned short mail_shortdate (unsigned int year,unsigned int month,
                               unsigned int day);
 SEARCHSET *mail_parse_set (char *s,char **ret);
index 61a5972..c38742b 100755 (executable)
@@ -257,6 +257,7 @@ void ssl_onceonlyinit (void)
     mail_parameters (NIL,SET_SSLDRIVER,(void *) &ssldriver);
     mail_parameters (NIL,SET_SSLSTART,(void *) ssl_start);
     SSL_library_init ();       /* add all algorithms */
+    SSL_load_error_strings();
   }
 }
 \f
@@ -327,11 +328,12 @@ static SSLSTREAM *ssl_start (TCPSTREAM *tstream,char *host,unsigned long flags)
                                /* pass to error callback */
       else if (sf) (*sf) (host,reason,flags);
       else {                   /* no error callback, build error message */
-       sprintf (tmp,"TLS/SSL failure for %.80s: %.512s",host,reason);
-       mm_log (tmp,ERROR);
+        sprintf (tmp,"TLS/SSL failure for %.80s: %.512s",host,reason);
+        mm_log (tmp,ERROR);
       }
       break;
     }
+    free (reason); /* OpenSSL error buf */
   }
   return stream;
 }
@@ -347,7 +349,7 @@ static SSLSTREAM *ssl_start (TCPSTREAM *tstream,char *host,unsigned long flags)
 static char *ssl_last_error = NIL;
 static char *ssl_last_host = NIL;
 
-/* #define __NON_BLOCKING_SSL_WRITE__ */
+#define __NON_BLOCKING_SSL_WRITE__ 
 
 #ifdef __NON_BLOCKING_SSL_WRITE__
 /*  g.shyamakshi@samsung.com 
@@ -399,9 +401,26 @@ static char *ssl_start_work (SSLSTREAM *stream,char *host,unsigned long flags)
   ssl_last_host = host;
   if (!(stream->context = SSL_CTX_new ((flags & NET_TLSCLIENT) ?
                                       TLSv1_client_method () :
-                                      SSLv23_client_method ())))
-    return "SSL context failed";
-  SSL_CTX_set_options (stream->context,0);
+                                      SSLv23_client_method ()))) {
+    /* bio to memory buf */
+    BIO *bio = BIO_new (BIO_s_mem ());
+    ERR_print_errors (bio);
+    char *buf = NULL;
+    size_t len = BIO_get_mem_data (bio, &buf);
+    char *ret = (char *) calloc (1, 1 + len + 40);
+    if (ret) {
+      memcpy (ret, buf , len);
+    }
+    sprintf (ret+len, ": SSL context failed [0x%x] ", flags & NET_TLSCLIENT);
+    BIO_free (bio);
+    return ret;
+//    return "SSL context failed";
+  }
+  if (flags & NET_FORCE_LOWER_TLS_VERSION)
+       SSL_CTX_set_options(stream->context, SSL_OP_NO_SSLv2|SSL_OP_NO_TLSv1_1|SSL_OP_NO_TLSv1_2);
+  else
+    SSL_CTX_set_options (stream->context,0);
+
                                /* disable certificate validation? */
   if (flags & NET_NOVALIDATECERT)
     SSL_CTX_set_verify (stream->context,SSL_VERIFY_NONE,NIL);
@@ -418,7 +437,7 @@ static char *ssl_start_work (SSLSTREAM *stream,char *host,unsigned long flags)
       X509_free (cert);
     }
     BIO_free (bio);
-    if (!cert) return "SSL client certificate failed";
+    if (!cert) return strdup("SSL client certificate failed");
                                /* want to supply private key? */
     if ((t = (sck ? (*sck) () : s)) && (tl = strlen (t))) {
       EVP_PKEY *key;
@@ -435,23 +454,33 @@ static char *ssl_start_work (SSLSTREAM *stream,char *host,unsigned long flags)
 \f
                                /* create connection */
   if (!(stream->con = (SSL *) SSL_new (stream->context)))
-    return "SSL connection failed";
+    return strdup("SSL connection failed");
   bio = BIO_new_socket (stream->tcpstream->tcpsi,BIO_NOCLOSE);
   SSL_set_bio (stream->con,bio,bio);
   SSL_set_connect_state (stream->con);
   if (SSL_in_init (stream->con)) SSL_total_renegotiations (stream->con);
                                /* now negotiate SSL */
   if (SSL_write (stream->con,"",0) < 0)
-    return ssl_last_error ? ssl_last_error : "SSL negotiation failed";
+    return strdup(ssl_last_error ? ssl_last_error : "SSL negotiation failed");
                                /* need to validate host names? */
-  if (!(flags & NET_NOVALIDATECERT) &&
-      (err = ssl_validate_cert (cert = SSL_get_peer_certificate (stream->con),
-                               host))) {
-                               /* application callback */
-    if (scq) return (*scq) (err,host,cert ? cert->name : "???") ? NIL : "";
-                               /* error message to return via mm_log() */
-    sprintf (tmp,"*%.128s: %.255s",err,cert ? cert->name : "???");
-    return ssl_last_error = cpystr (tmp);
+  if (!(flags & NET_NOVALIDATECERT)) {
+    cert = SSL_get_peer_certificate (stream->con);
+    if (err = ssl_validate_cert (cert, host)) {
+                               /* application callback */
+      if (scq) {
+        long err = (*scq) (err,host,cert ? cert->name : "???");
+        if (cert)
+          X509_free (cert);
+        return (err? NIL : strdup(""));
+      }        
+                       /* error message to return via mm_log() */
+      sprintf (tmp,"*%.128s: %.255s",err,cert ? cert->name : "???");
+      if (cert)
+        X509_free (cert);
+      return strdup(ssl_last_error = cpystr (tmp));
+    }
+    if (cert)
+      X509_free (cert);
   }
   return NIL;
 }
@@ -933,7 +962,10 @@ static long ssl_abort (SSLSTREAM *stream)
 
 char *ssl_host (SSLSTREAM *stream)
 {
-  return tcp_host (stream->tcpstream);
+       if(stream == NULL || stream->tcpstream == NULL)
+               return "";
+
+       return tcp_host (stream->tcpstream);
 }
 
 
index 3bfdff3..79884a2 100755 (executable)
@@ -115,6 +115,7 @@ void ssl_onceonlyinit (void)
     mail_parameters (NIL,SET_SSLDRIVER,(void *) &ssldriver);
     mail_parameters (NIL,SET_SSLSTART,(void *) ssl_start);
     SSL_library_init ();       /* add all algorithms */
+    SSL_load_error_strings();
   }
 }
 \f
@@ -185,8 +186,9 @@ static SSLSTREAM *ssl_start (TCPSTREAM *tstream,char *host,unsigned long flags)
                                /* pass to error callback */
       else if (sf) (*sf) (host,reason,flags);
       else {                   /* no error callback, build error message */
-       sprintf (tmp,"TLS/SSL failure for %.80s: %.512s",host,reason);
-       mm_log (tmp,ERROR);
+        sprintf (tmp,"TLS/SSL failure for %.80s: %.512s",host,reason);
+        mm_log (tmp,ERROR);
+        free (reason); /* OpenSSL error buf */
       }
       break;
     }
@@ -221,9 +223,26 @@ static char *ssl_start_work (SSLSTREAM *stream,char *host,unsigned long flags)
   ssl_last_host = host;
   if (!(stream->context = SSL_CTX_new ((flags & NET_TLSCLIENT) ?
                                       TLSv1_client_method () :
-                                      SSLv23_client_method ())))
-    return "SSL context failed";
-  SSL_CTX_set_options (stream->context,0);
+                                      SSLv23_client_method ()))) {
+    /* bio to memory buf */
+    BIO *bio = BIO_new (BIO_s_mem ());
+    ERR_print_errors (bio);
+    char *buf = NULL;
+    size_t len = BIO_get_mem_data (bio, &buf);
+    char *ret = (char *) calloc (1, 1 + len + 40);
+    if (ret) {
+      memcpy (ret, buf , len);
+    }
+    sprintf (ret+len, ": SSL context failed [0x%x] ", flags & NET_TLSCLIENT);
+    BIO_free (bio);
+    return ret;
+//    return "SSL context failed";
+  }
+  if (flags & NET_FORCE_LOWER_TLS_VERSION)
+       SSL_CTX_set_options(stream->context, SSL_OP_NO_SSLv2|SSL_OP_NO_TLSv1_1|SSL_OP_NO_TLSv1_2);
+  else
+    SSL_CTX_set_options (stream->context,0);
+
                                /* disable certificate validation? */
   if (flags & NET_NOVALIDATECERT)
     SSL_CTX_set_verify (stream->context,SSL_VERIFY_NONE,NIL);
index e7fb8f2..0cdc65e 100755 (executable)
@@ -249,7 +249,7 @@ void pop3_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
   if ((ref && *ref) ?          /* have a reference */
       (pop3_valid (ref) && pmatch ("INBOX",pat)) :
       (mail_valid_net (pat,&pop3driver,NIL,tmp) && pmatch ("INBOX",tmp)))
-    mm_log ("Scan not valid for POP3 mailboxes",ERROR);
+    MM_LOG ("Scan not valid for POP3 mailboxes",ERROR);
 }
 
 
@@ -413,12 +413,12 @@ MAILSTREAM *pop3_open (MAILSTREAM *stream)
   if (stream->local) fatal ("pop3 recycle stream");
                                /* /anonymous not supported */
   if (mb.anoflag || stream->anonymous) {
-    mm_log ("Anonymous POP3 login not available",ERROR);
+    MM_LOG ("Anonymous POP3 login not available",ERROR);
     return NIL;
   }
                                /* /readonly not supported either */
   if (mb.readonlyflag || stream->rdonly) {
-    mm_log ("Read-only POP3 access not available",ERROR);
+    MM_LOG ("Read-only POP3 access not available",ERROR);
     return NIL;
   }
                                /* copy other switches */
@@ -435,44 +435,44 @@ MAILSTREAM *pop3_open (MAILSTREAM *stream)
                 (NETDRIVER *) mail_parameters (NIL,GET_SSLDRIVER,NIL),
                 "*pop3s",pop3_sslport ? pop3_sslport : POP3SSLPORT)) &&
       pop3_reply (stream)) {
-    mm_log (LOCAL->reply,NIL); /* give greeting */
+    MM_LOG (LOCAL->reply,NIL); /* give greeting */
        #if 1 //APOP - shasikala.p@siso.com
        strcpy(tmp_str, LOCAL->reply);
-       mm_log(tmp_str, NIL);
+       MM_LOG(tmp_str, NIL);
        timestamp = strchr(tmp_str, '<');
 
        sprintf(temp, "Pop3_open - mb->apop - %d", mb.apop);
-       mm_log(temp, NIL);
+       MM_LOG(temp, NIL);
 
        if(timestamp && mb.apop != 0)
                apop = 1;
        else
        {
-               mm_log("server doesnt support apop", NIL);
+               MM_LOG("server doesnt support apop", NIL);
                if(mb.apop == 1)
-                       mm_log("Uncheck apop", NIL);
+                       MM_LOG("Uncheck apop", NIL);
                apop = 0;
        }
        
        if(apop)
        {
-               mm_log(timestamp, NIL);
+               MM_LOG(timestamp, NIL);
                mm_login(&mb, usr, tmp, 0);
-               mm_log(usr, NIL);
-               mm_log(tmp, NIL);
+               MM_LOG(usr, NIL);
+               MM_LOG(tmp, NIL);
                timestamp = strcat(timestamp, tmp);
-               mm_log(timestamp, NIL);
+               MM_LOG(timestamp, NIL);
                memset(tmp, 0, MAILTMPLEN);
                md5_init (&ctx);
                md5_update (&ctx,(unsigned char *) timestamp,strlen (timestamp));
                md5_final (digest,&ctx);
-               mm_log(digest, NIL);
+               MM_LOG(digest, NIL);
                    for (i = 0, s = tmp ; i < MD5DIGLEN; i++) {
                        *s++ = hex[(j = digest[i]) >> 4];
                        *s++ = hex[j & 0xf];
                        }
                *s = '\0';
-               mm_log(tmp, NIL);
+               MM_LOG(tmp, NIL);
        }
        
        #endif
@@ -515,7 +515,7 @@ MAILSTREAM *pop3_open (MAILSTREAM *stream)
                                /* flush final dot */
        if (s) fs_give ((void **) &s);
        else {                  /* lost connection */
-         mm_log ("POP3 connection broken while itemizing messages",ERROR);
+         MM_LOG ("POP3 connection broken while itemizing messages",ERROR);
          pop3_close (stream,NIL);
          return NIL;
        }
@@ -523,15 +523,15 @@ MAILSTREAM *pop3_open (MAILSTREAM *stream)
       stream->silent = silent; /* notify main program */
       mail_exists (stream,stream->nmsgs);
                                /* notify if empty */
-      if (!(stream->nmsgs || stream->silent)) mm_log ("Mailbox is empty",WARN);
+      if (!(stream->nmsgs || stream->silent)) MM_LOG ("Mailbox is empty",WARN);
     }
     else {                     /* error in STAT */
-      mm_log (LOCAL->reply,ERROR);
+      MM_LOG (LOCAL->reply,ERROR);
       pop3_close (stream,NIL); /* too bad */
     }
   }
   else {                       /* connection failed */
-    if (LOCAL->reply) mm_log (LOCAL->reply,ERROR);
+    if (LOCAL->reply) MM_LOG (LOCAL->reply,ERROR);
     pop3_close (stream,NIL);   /* failed, clean up */
   }
   return LOCAL ? stream : NIL; /* if stream is alive, return to caller */
@@ -622,17 +622,15 @@ long pop3_auth (MAILSTREAM *stream,NETMBX *mb,char *pwd,char *usr, int apop)
   long ret = NIL;
   long flags = (stream->secure ? AU_SECURE : NIL) |
     (mb->authuser[0] ? AU_AUTHUSER : NIL);
-  mm_log("in pop3_auth1", NIL);
-  if(apop)
-  {
+  MM_LOG("in pop3_auth1", NIL);
+  if(apop) {
        apop = 0;
-       mm_log("apop set", NIL);
+       MM_LOG("apop set", NIL);
        char* tmp = (char *)malloc(strlen(usr)+strlen(pwd) +1);
        sprintf (tmp,"%s %s",usr,pwd);
-       mm_log(tmp, NIL);
-       if(pop3_send(stream, "APOP", tmp))
-       {
-               mm_log("successfully logged in using APOP", NIL);
+       MM_LOG(tmp, NIL);
+       if(pop3_send(stream, "APOP", tmp)) {
+               MM_LOG("successfully logged in using APOP", NIL);
                pop3_capa (stream,flags);
                return LONGT;
        }
@@ -657,7 +655,7 @@ long pop3_auth (MAILSTREAM *stream,NETMBX *mb,char *pwd,char *usr, int apop)
     pop3_capa (stream,flags);  /* get capabilities now that TLS in effect */
   }
   else if (mb->tlsflag) {      /* user specified /tls but can't do it */
-    mm_log ("Unable to negotiate TLS with this server",ERROR);
+    MM_LOG ("Unable to negotiate TLS with this server",ERROR);
     return NIL;
   }
                                /* get authenticators from capabilities */
@@ -690,20 +688,20 @@ long pop3_auth (MAILSTREAM *stream,NETMBX *mb,char *pwd,char *usr, int apop)
       mb->host[NETMAXHOST-1] = '\0';
     }
     for (t = NIL, LOCAL->saslcancel = NIL; !ret && LOCAL->netstream && auths &&
-        (at = mail_lookup_auth (find_rightmost_bit (&auths)+1)); ) {
+             (at = mail_lookup_auth (find_rightmost_bit (&auths)+1)); ) {
       if (t) {                 /* previous authenticator failed? */
-       sprintf (pwd,"Retrying using %.80s authentication after %.80s",
-                at->name,t);
-       mm_log (pwd,NIL);
-       fs_give ((void **) &t);
+           sprintf (pwd,"Retrying using %.80s authentication after %.80s",
+                 at->name,t);
+         MM_LOG (pwd,NIL);
+        fs_give ((void **) &t);
       }
       trial = 0;               /* initial trial count */
       do {
-       if (t) {
-         sprintf (pwd,"Retrying %s authentication after %.80s",at->name,t);
-         mm_log (pwd,WARN);
-         fs_give ((void **) &t);
-       }
+        if (t) {
+          sprintf (pwd,"Retrying %s authentication after %.80s",at->name,t);
+          MM_LOG (pwd,WARN);
+          fs_give ((void **) &t);
+        }
        LOCAL->saslcancel = NIL;
        if (pop3_send (stream,"AUTH",at->name)) {
                                /* hide client authentication responses */
@@ -712,7 +710,7 @@ long pop3_auth (MAILSTREAM *stream,NETMBX *mb,char *pwd,char *usr, int apop)
                             &trial,usr) && LOCAL->response) {
            if (*LOCAL->response == '+') ret = LONGT;
                                /* if main program requested cancellation */
-           else if (!trial) mm_log ("POP3 Authentication cancelled",ERROR);
+           else if (!trial) MM_LOG ("POP3 Authentication cancelled",ERROR);
          }
          LOCAL->sensitive=NIL; /* unhide */
        }
@@ -723,18 +721,18 @@ long pop3_auth (MAILSTREAM *stream,NETMBX *mb,char *pwd,char *usr, int apop)
     }
     if (t) {                   /* previous authenticator failed? */
       if (!LOCAL->saslcancel) {        /* don't do this if a cancel */
      sprintf (pwd,"Can not authenticate to POP3 server: %.80s",t);
-       mm_log (pwd,ERROR);
+ sprintf (pwd,"Can not authenticate to POP3 server: %.80s",t);
+       MM_LOG (pwd,ERROR);
       }
       fs_give ((void **) &t);
     }
   }
 \f
   else if (stream->secure)
-    mm_log ("Can't do secure authentication with this server",ERROR);
+    MM_LOG ("Can't do secure authentication with this server",ERROR);
   else if (mb->authuser[0])
-    mm_log ("Can't do /authuser with this server",ERROR);
-  else if (!LOCAL->cap.user) mm_log ("Can't login to this server",ERROR);
+    MM_LOG ("Can't do /authuser with this server",ERROR);
+  else if (!LOCAL->cap.user) MM_LOG ("Can't login to this server",ERROR);
   else {                       /* traditional login */
     trial = 0;                 /* initial trial count */
     do {
@@ -747,13 +745,13 @@ long pop3_auth (MAILSTREAM *stream,NETMBX *mb,char *pwd,char *usr, int apop)
          LOCAL->sensitive=NIL; /* unhide */
        }
        if (!ret) {             /* failure */
-         mm_log (LOCAL->reply,WARN);
+         MM_LOG (LOCAL->reply,WARN);
          if (trial == pop3_maxlogintrials)
-           mm_log ("Too many login failures",ERROR);
+           MM_LOG ("Too many login failures",ERROR);
        }
       }
                                /* user refused to give password */
-      else mm_log ("Login aborted",ERROR);
+      else MM_LOG ("Login aborted",ERROR);
     } while (!ret && pwd[0] && (trial < pop3_maxlogintrials) &&
             LOCAL->netstream);
   }
@@ -779,7 +777,7 @@ void *pop3_challenge (void *s,unsigned long *len)
       !(ret = rfc822_base64 ((unsigned char *) LOCAL->reply,
                             strlen (LOCAL->reply),len))) {
     sprintf (tmp,"POP3 SERVER BUG (invalid challenge): %.80s",LOCAL->reply);
-    mm_log (tmp,ERROR);
+    MM_LOG (tmp,ERROR);
   }
   return ret;
 }
@@ -1007,7 +1005,7 @@ long pop3_ping (MAILSTREAM *stream)
 
 void pop3_check (MAILSTREAM *stream)
 {
-  if (pop3_ping (stream)) mm_log ("Check completed",NIL);
+  if (pop3_ping (stream)) MM_LOG ("Check completed",NIL);
 }
 
 
@@ -1047,9 +1045,9 @@ long pop3_expunge (MAILSTREAM *stream,char *sequence,long options)
     if (!stream->silent) {     /* only if not silent */
       if (n) {                 /* did we expunge anything? */
        sprintf (tmp,"Expunged %lu messages",n);
-       mm_log (tmp,(long) NIL);
+       MM_LOG (tmp,(long) NIL);
       }
-      else mm_log ("No messages deleted, so no update needed",(long) NIL);
+      else MM_LOG ("No messages deleted, so no update needed",(long) NIL);
     }
   }
   return ret;
@@ -1068,7 +1066,7 @@ long pop3_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
   mailproxycopy_t pc =
     (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
   if (pc) return (*pc) (stream,sequence,mailbox,options);
-  mm_log ("Copy not valid for POP3",ERROR);
+  MM_LOG ("Copy not valid for POP3",ERROR);
   return NIL;
 }
 
@@ -1083,7 +1081,7 @@ long pop3_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
 
 long pop3_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
 {
-  mm_log ("Append not valid for POP3",ERROR);
+  MM_LOG ("Append not valid for POP3",ERROR);
   return NIL;
 }
 \f
index 07a724a..1e6193e 100755 (executable)
@@ -198,7 +198,7 @@ void rfc822_parse_msg_full (ENVELOPE **en,BODY **bdy,char *s,unsigned long i,
        if (!env->date && !strcmp (tmp+1,"ATE")) env->date = cpystr (d);
        break;
       case 'F':                        /* possible From: */
-       if (!strcmp (tmp+1,"ROM")) rfc822_parse_adrlist (&env->from,d,host);
+       if (!strcmp (tmp+1,"ROM"))      rfc822_parse_adrlist (&env->from,d,host);
        else if (!strcmp (tmp+1,"OLLOWUP-TO")) {
          t = env->followup_to = (char *) fs_get (1 + strlen (d));
          while (c = *d++) if (c != ' ') *t++ = c;
@@ -738,6 +738,13 @@ void rfc822_parse_adrlist (ADDRESS **lst,char *string,char *host)
       last = adr;              /* new tail address */
       if (string) {            /* analyze what follows */
        rfc822_skipws (&string);
+
+       /* Recovery from failure on parsing */
+       if ( string != NULL) {
+               while (*string != ',' && *string != '\0')
+                       string++;
+    }
+
        switch (c = *(unsigned char *) string) {
        case ',':               /* comma? */
          ++string;             /* then another address follows */
@@ -900,7 +907,7 @@ ADDRESS *rfc822_parse_mailbox (char **string,char *defaulthost)
                                /* phrase is a personal name */
       if (adr->personal) fs_give ((void **) &adr->personal);
       *end = '\0';             /* tie off phrase */
-      adr->personal = rfc822_cpy (s);
+      adr->personal = rfc822_cpy_for_personal (s);
     }
                                /* call external phraseparser if phrase only */
     else if (pp && rfc822_phraseonly (end) &&
@@ -985,9 +992,11 @@ ADDRESS *rfc822_parse_routeaddr (char *string,char **ret,char *defaulthost)
     if (!**ret) *ret = NIL;    /* wipe pointer if at end of string */
     return adr;                        /* return the address */
   }
-  sprintf (tmp,"Unterminated mailbox: %.80s@%.80s",adr->mailbox,
-          *adr->host == '@' ? "<null>" : adr->host);
-  MM_LOG (tmp,PARSE);
+  if (adr) {
+         sprintf (tmp,"Unterminated mailbox: %.80s@%.80s", (adr->mailbox == NULL) ? "<null>" : adr->mailbox,
+                         (adr->host == NULL || *adr->host == '@') ? "<null>" : adr->host);
+         MM_LOG (tmp,PARSE);
+  }
   adr->next = mail_newaddr ();
   adr->next->mailbox = cpystr ("MISSING_MAILBOX_TERMINATOR");
   adr->next->host = cpystr (errhst);
@@ -1062,7 +1071,7 @@ ADDRESS *rfc822_parse_addrspec (char *string,char **ret,char *defaulthost)
   if (end && !(adr->personal && *adr->personal)) {
     while (*end == ' ') ++end; /* see if we can find a person name here */
     if ((*end == '(') && (s = rfc822_skip_comment (&end,LONGT)) && strlen (s))
-      adr->personal = rfc822_cpy (s);
+      adr->personal = rfc822_cpy_for_personal (s);
     rfc822_skipws (&end);      /* skip any other WS in the normal way */
   }
                                /* set return to end pointer */
@@ -1165,56 +1174,56 @@ char *rfc822_parse_word (char *s,const char *delimiters)
       str = ++st;              /* always skip past ESC */
       switch (*st) {           /* special hack for RFC 1468 (ISO-2022-JP) */
       case I2C_MULTI:          /* multi byte sequence */
-       switch (*++st) {
-       case I2CS_94x94_JIS_OLD:/* old JIS (1978) */
-       case I2CS_94x94_JIS_NEW:/* new JIS (1983) */
-         str = ++st;           /* skip past the shift to JIS */
-         while (st = strchr (st,I2C_ESC))
-           if ((*++st == I2C_G0_94) && ((st[1] == I2CS_94_ASCII) ||
-                                        (st[1] == I2CS_94_JIS_ROMAN) ||
-                                        (st[1] == I2CS_94_JIS_BUGROM))) {
-             str = st += 2;    /* skip past the shift back to ASCII */
-             break;
-           }
+        switch (*++st) {
+          case I2CS_94x94_JIS_OLD:/* old JIS (1978) */
+          case I2CS_94x94_JIS_NEW:/* new JIS (1983) */
+            str = ++st;                /* skip past the shift to JIS */
+            while (st = strchr (st,I2C_ESC))
+              if ((*++st == I2C_G0_94) && ((st[1] == I2CS_94_ASCII) ||
+                (st[1] == I2CS_94_JIS_ROMAN) ||
+                (st[1] == I2CS_94_JIS_BUGROM))) {
+                str = st += 2; /* skip past the shift back to ASCII */
+                break;
+              }
                                /* eats entire text if no shift back */
-         if (!st || !*st) return str + strlen (str);
-       }
-       break;
-      case I2C_G0_94:          /* single byte sequence */
-       switch (st[1]) {
-       case I2CS_94_ASCII:     /* shift to ASCII */
-       case I2CS_94_JIS_ROMAN: /* shift to JIS-Roman */
-       case I2CS_94_JIS_BUGROM:/* old buggy definition of JIS-Roman */
-         str = st + 2;         /* skip past the shift */
-         break;
-       }
+            if (!st || !*st) return str + strlen (str);
+          }
+           break;
+          case I2C_G0_94:              /* single byte sequence */
+            switch (st[1]) {
+              case I2CS_94_ASCII:      /* shift to ASCII */
+              case I2CS_94_JIS_ROMAN:  /* shift to JIS-Roman */
+              case I2CS_94_JIS_BUGROM:/* old buggy definition of JIS-Roman */
+                str = st + 2;          /* skip past the shift */
+                break;
+            }
       }
     }
 \f
     else switch (*st) {                /* dispatch based on delimiter */
-    case '"':                  /* quoted string */
+      case '"':                        /* quoted string */
                                /* look for close quote */
-      while (*++st != '"') switch (*st) {
-      case '\0':               /* unbalanced quoted string */
-       return NIL;             /* sick sick sick */
-      case '\\':               /* quoted character */
-       if (!*++st) return NIL; /* skip the next character */
-      default:                 /* ordinary character */
-       break;                  /* no special action */
-      }
-      str = ++st;              /* continue parse */
-      break;
-    case '\\':                 /* quoted character */
+        while (*++st != '"') switch (*st) {
+            case '\0':         /* unbalanced quoted string */
+              return NIL;              /* sick sick sick */
+            case '\\':         /* quoted character */
+              if (!*++st) return NIL;  /* skip the next character */
+            default:                   /* ordinary character */
+              break;                   /* no special action */
+        }
+        str = ++st;            /* continue parse */
+        break;
+      case '\\':                       /* quoted character */
       /* This is wrong; a quoted-pair can not be part of a word.  However,
        * domain-literal is parsed as a word and quoted-pairs can be used
        * *there*.  Either way, it's pretty pathological.
        */
-      if (st[1]) {             /* not on NUL though... */
-       str = st + 2;           /* skip quoted character and go on */
-       break;
-      }
-    default:                   /* found a word delimiter */
-      return (st == s) ? NIL : st;
+        if (st[1]) {           /* not on NUL though... */
+          str = st + 2;                /* skip quoted character and go on */
+          break;
+        }
+      default:                 /* found a word delimiter */
+        return (st == s) ? NIL : st;
     }
   }
 }
@@ -1230,6 +1239,14 @@ char *rfc822_cpy (char *src)
   return rfc822_quote (cpystr (src));
 }
 
+char *rfc822_cpy_for_personal (char *src)
+{
+                               /* copy and unquote */
+       return rfc822_quote (cpystr (src));
+  /* Calling rfc822_quote should be replaced with below line. */
+  /* return rfc822quote_for_personal (cpystr (src)); */
+}
+
 
 /* Unquote an RFC 2822 format string
  * Accepts: string
@@ -1253,6 +1270,27 @@ char *rfc822_quote (char *src)
   return ret;                  /* return our string */
 }
 
+char *rfc822_quote_for_personal (char *src)
+{
+       char *ret = src;
+       if (strpbrk (src,"\\\"")) {     /* any quoting in string? */
+               char *dst = ret;
+               while (*src) {          /* copy string */
+                       if (*src == '\\') {
+                               if (*(src + 1) == '\"')
+                                       *dst++ = *src++;        /* copy character */
+                               else
+                                       src++;/* skip over single quote, copy next always */
+                       }
+                       else if (*src == '\"')
+                               src++;  /* skip double quote entirely */
+                       *dst++ = *src++;        /* copy character */
+               }
+               *dst = '\0';            /* tie off string */
+       }
+       return ret;                     /* return our string */
+}
+
 
 /* Copy address list
  * Accepts: address list
@@ -1548,7 +1586,7 @@ long rfc822_output_address_list (RFC822BUFFER *buf,ADDRESS *adr,long pretty,
                                                    rspecials) : LONGT) &&
                rfc822_output_string (buf," <") &&
                rfc822_output_address (buf,adr) &&
-               rfc822_output_string (buf,">"))) return NIL;
+               rfc822_output_string (buf,">")))  return NIL;
        }
        else if (!rfc822_output_address (buf,adr)) return NIL;
        if (adr->next && adr->next->mailbox &&
@@ -1720,6 +1758,7 @@ void rfc822_encode_body_7bit (ENVELOPE *env,BODY *body)
   PARAMETER **param;
   if (body) switch (body->type) {
   case TYPEMULTIPART:          /* multi-part */
+    MM_LOG ("rfc822_encode_body_7bit-MULTIPART",PARSE);
     for (param = &body->parameter;
         *param && strcmp ((*param)->attribute,"BOUNDARY");
         param = &(*param)->next);
@@ -1737,6 +1776,7 @@ void rfc822_encode_body_7bit (ENVELOPE *env,BODY *body)
     while (part = part->next); /* until done */
     break;
   case TYPEMESSAGE:            /* encapsulated message */
+       MM_LOG ("rfc822_encode_body_7bit-MESSAGE",PARSE);
     switch (body->encoding) {
     case ENC7BIT:
       break;
@@ -1754,19 +1794,25 @@ void rfc822_encode_body_7bit (ENVELOPE *env,BODY *body)
     switch (body->encoding) {
     case ENC8BIT:              /* encode 8BIT into QUOTED-PRINTABLE */
                                /* remember old 8-bit contents */
+      MM_LOG ("rfc822_encode_body_7bit-encode-ENC8BIT",PARSE);
       f = (void *) body->contents.text.data;
+      MM_LOG ("rfc822_8bit-begin",PARSE);
       body->contents.text.data =
        rfc822_8bit (body->contents.text.data,
                     body->contents.text.size,&body->contents.text.size);
+      MM_LOG ("rfc822_8bit-end",PARSE);
       body->encoding = ENCQUOTEDPRINTABLE;
       fs_give (&f);            /* flush old binary contents */
       break;
     case ENCBINARY:            /* encode binary into BASE64 */
                                /* remember old binary contents */
+      MM_LOG ("rfc822_encode_body_7bit-encode-ENCBINARY",PARSE);
       f = (void *) body->contents.text.data;
+      MM_LOG ("rfc822_binary-begin",PARSE);
       body->contents.text.data =
        rfc822_binary ((void *) body->contents.text.data,
                       body->contents.text.size,&body->contents.text.size);
+      MM_LOG ("rfc822_binary-end",PARSE);
       body->encoding = ENCBASE64;
       fs_give (&f);            /* flush old binary contents */
     default:                   /* otherwise OK */
index 0621732..95d1761 100755 (executable)
@@ -76,7 +76,9 @@ char *rfc822_parse_domain (char *string,char **end);
 char *rfc822_parse_phrase (char *string);
 char *rfc822_parse_word (char *string,const char *delimiters);
 char *rfc822_cpy (char *src);
+char *rfc822_cpy_for_personal (char *src);
 char *rfc822_quote (char *src);
+char *rfc822_quote_for_personal (char *src);
 ADDRESS *rfc822_cpy_adr (ADDRESS *adr);
 void rfc822_skipws (char **s);
 char *rfc822_skip_comment (char **s,long trim);
index 023464a..47cc970 100755 (executable)
@@ -150,14 +150,14 @@ SENDSTREAM *smtp_open_full (NETDRIVER *dv,char **hostlist,char *service,
   char *s,tmp[MAILTMPLEN];
   NETSTREAM *netstream;
   NETMBX mb;
-  if (!(hostlist && *hostlist)) mm_log ("Missing SMTP service host",ERROR);
+  if (!(hostlist && *hostlist)) MM_LOG ("Missing SMTP service host",ERROR);
                                /* maximum domain name is 64 characters */
   else do if (strlen (*hostlist) < SMTPMAXDOMAIN) {
     sprintf (tmp,"{%.1000s}",*hostlist);
     if (!mail_valid_net_parse_work (tmp,&mb,service ? service : "smtp") ||
        mb.anoflag || mb.readonlyflag) {
       sprintf (tmp,"Invalid host specifier: %.80s",*hostlist);
-      mm_log (tmp,ERROR);
+      MM_LOG (tmp,ERROR);
     }
     else {                     /* light tryssl flag if requested */
       mb.trysslflag = (options & SOP_TRYSSL) ? T : NIL;
@@ -190,14 +190,14 @@ SENDSTREAM *smtp_open_full (NETDRIVER *dv,char **hostlist,char *service,
        while ((reply < 100) || (stream->reply[3] == '-'));
        if (reply != SMTPGREET){/* get SMTP greeting */
          sprintf (tmp,"SMTP greeting failure: %.80s",stream->reply);
-         mm_log (tmp,ERROR);
+         MM_LOG (tmp,ERROR);
          stream = smtp_close (stream);
        }
                                /* try EHLO first, then HELO */
        else if (((reply = smtp_ehlo (stream,s,&mb)) != SMTPOK) &&
                 ((reply = smtp_send (stream,"HELO",s)) != SMTPOK)) {
          sprintf (tmp,"SMTP hello failure: %.80s",stream->reply);
-         mm_log (tmp,ERROR);
+         MM_LOG (tmp,ERROR);
          stream = smtp_close (stream);
        }
        else {
@@ -217,7 +217,7 @@ SENDSTREAM *smtp_open_full (NETDRIVER *dv,char **hostlist,char *service,
                                /* TLS negotiation failed after STARTTLS */
              sprintf (tmp,"Unable to negotiate TLS with this server: %.80s",
                       mb.host);
-             mm_log (tmp,ERROR);
+             MM_LOG (tmp,ERROR);
                                /* close without doing QUIT */
              if (stream->netstream) net_close (stream->netstream);
              stream->netstream = NIL;
@@ -227,14 +227,14 @@ SENDSTREAM *smtp_open_full (NETDRIVER *dv,char **hostlist,char *service,
            else if ((reply = smtp_ehlo (stream,s,&mb)) != SMTPOK) {
              sprintf (tmp,"SMTP EHLO failure after STARTTLS: %.80s",
                       stream->reply);
-             mm_log (tmp,ERROR);
+             MM_LOG (tmp,ERROR);
              stream = smtp_close (stream);
            }
            else ESMTP.ok = T;  /* TLS OK and EHLO successful */
          }
          else if (mb.tlsflag) {/* user specified /tls but can't do it */
            sprintf (tmp,"TLS unavailable with this server: %.80s",mb.host);
-           mm_log (tmp,ERROR);
+           MM_LOG (tmp,ERROR);
            stream = smtp_close (stream);
          }
 \f
@@ -249,11 +249,13 @@ SENDSTREAM *smtp_open_full (NETDRIVER *dv,char **hostlist,char *service,
                         NETMAXHOST-1);
                mb.host[NETMAXHOST-1] = '\0';
              }
-             if (!smtp_auth (stream,&mb,tmp)) stream = smtp_close (stream);
+             if(mb.auth_method > 0) {
+                 if (!smtp_auth (stream,&mb,tmp)) stream = smtp_close (stream);
+             }
            }
            else {              /* no available authenticators? */
              sprintf (tmp,"%sSMTP authentication not available: %.80s", mb.secflag ? "Secure " : "",mb.host);
-             mm_log (tmp,ERROR);
+             MM_LOG (tmp,ERROR);
              stream = smtp_close (stream);
            }
          }
@@ -294,9 +296,18 @@ long smtp_auth (SENDSTREAM *stream,NETMBX *mb,char *tmp)
   for (auths = ESMTP.auth, stream->saslcancel = NIL;
        !ret && stream->netstream && auths &&
        (at = mail_lookup_auth (find_rightmost_bit (&auths) + 1)); ) {
+
+    if (mb->auth_method != AUTH_METHOD_XOAUTH2 && strstr(at->name, auth_xoauth2.name) ) {
+       sprintf (tmp,"auth_method is not AUTH_METHOD_XOAUTH2. So skipped XOAUTH authenticator.");
+       MM_LOG (tmp,NIL);
+       continue;
+    }
+
+    sprintf (tmp,"Trying using %s authentication. ", at->name);
+    MM_LOG (tmp,NIL);
     if (lsterr) {              /* previous authenticator failed? */
       sprintf (tmp,"Retrying using %s authentication after %.80s", at->name,lsterr);
-      mm_log (tmp,NIL);
+      MM_LOG (tmp,NIL);
       fs_give ((void **) &lsterr);
     }
     trial = 0;                 /* initial trial count */
@@ -304,7 +315,7 @@ long smtp_auth (SENDSTREAM *stream,NETMBX *mb,char *tmp)
     if (stream->netstream) do {
       if (lsterr) {
        sprintf (tmp,"Retrying %s authentication after %.80s",at->name,lsterr);
-       mm_log (tmp,WARN);
+       MM_LOG (tmp,WARN);
        fs_give ((void **) &lsterr);
       }
       stream->saslcancel = NIL;
@@ -318,56 +329,10 @@ long smtp_auth (SENDSTREAM *stream,NETMBX *mb,char *tmp)
            ret = LONGT;
          }
                                /* if main program requested cancellation */
-         else if (!trial) mm_log ("SMTP Authentication cancelled",ERROR);
+         else if (!trial) MM_LOG ("SMTP Authentication cancelled",ERROR);
        }
        stream->sensitive = NIL;/* unhide */
       }
-#if 1          // for smtp.web.de
-               else if (!strcmp(at->name, "PLAIN")) {
-                       char* user = usr;
-                       char pwd[MAILTMPLEN];
-                       
-                       pwd[0] = NIL;
-                       mm_login(mb, user, pwd, trial);
-                       
-                       unsigned long rlen = strlen(mb->authuser) + strlen(user) + strlen(pwd) + 2;
-                       char* response = (char*) fs_get(rlen);
-                       char* t = response;
-                       char* u;
-                       
-                       if (mb->authuser[0])
-                               for (u =user; *u; *t++ = *u++);
-                       
-                       *t++ = '\0';
-                       
-                       for (u = (mb->authuser[0] ? mb->authuser : user); *u; *t++ = *u++);
-                       
-                       *t++ = '\0';
-                       
-                       for (u = pwd; *u; *t++ = *u++);
-                       
-                       unsigned long i, j;
-                       
-                       for (t = (char*) rfc822_binary(response, rlen, &i), u = t, j = 0; j < i; j++) {
-                               if (t[j] > ' ') *u++ = t[j];
-                       }
-                       
-                       *u = '\0';
-                       
-                       i = smtp_send(stream, "AUTH PLAIN", t);
-                       
-                       fs_give((void**)&t);
-                       
-                       memset(response, 0, rlen);
-                       fs_give((void**)&response);
-                       
-                       if (i == SMTPAUTHED) {
-                               ESMTP.auth = NIL;
-                               ret = LONGT;
-                       }
-                       else if (!trial) mm_log("SMTP Authentication cancelled", ERROR);
-               }
-#endif
          /* remember response if error and no cancel */
       if (!ret && trial) lsterr = cpystr (stream->reply);
     } while (!ret && stream->netstream && trial &&
@@ -376,7 +341,7 @@ long smtp_auth (SENDSTREAM *stream,NETMBX *mb,char *tmp)
   if (lsterr) {                        /* previous authenticator failed? */
     if (!stream->saslcancel) { /* don't do this if a cancel */
       sprintf (tmp,"Can not authenticate to SMTP server: %.80s",lsterr);
-      mm_log (tmp,ERROR);
+      MM_LOG (tmp,ERROR);
     }
     fs_give ((void **) &lsterr);
   }
@@ -397,7 +362,7 @@ void *smtp_challenge (void *s,unsigned long *len)
   // for smtp.mail.yahoo.co.kr
        if (!strcmp(stream->reply+4, "ok, go on")) {
                sprintf (tmp,"smtp_challenge : Server bug: non-empty initial PLAIN challenge 3: %.80s",stream->reply+4);
-               mm_log (tmp,WARN);
+               MM_LOG (tmp,WARN);
                
                *len = 0;               // MUST BE
                return cpystr("ok, go on");
@@ -413,7 +378,7 @@ void *smtp_challenge (void *s,unsigned long *len)
       !(ret = rfc822_base64 ((unsigned char *) stream->reply + 4,
                             strlen (stream->reply + 4),len))) {
     sprintf (tmp,"SMTP SERVER BUG (invalid challenge): %.80s",stream->reply+4);
-    mm_log (tmp,ERROR);
+    MM_LOG (tmp,ERROR);
   }
   return ret;
 }
@@ -768,9 +733,8 @@ long smtp_ehlo (SENDSTREAM *stream,char *host,NETMBX *mb)
        ESMTP.atrn.ok = T;
       }
       else if (!compare_cstring (s,"AUTH"))
-       //do if ((j = mail_lookup_auth_name (t,flags)) &&
-       do if ((j = mail_lookup_auth_name_smtp (t,flags)) &&            // 22-Mar-2010 Fix for SMTP Authorization issue - avoid race condition. change from     mail_lookup_auth_name to mail_lookup_auth_name_smtp
-              (--j < MAXAUTHENTICATORS)) ESMTP.auth |= (1 << j);
+       do if ((j = mail_lookup_auth_name (t,flags)) &&
+       (--j < MAXAUTHENTICATORS)) ESMTP.auth |= (1 << j);
        while ((t = strtok_r (NIL," ",&r)) && *t);
     }
                                /* EHLO options which do not take arguments */
@@ -795,17 +759,12 @@ long smtp_ehlo (SENDSTREAM *stream,char *host,NETMBX *mb)
   }
   while ((i < 100) || (stream->reply[3] == '-'));
                                /* disable LOGIN if PLAIN also advertised */
-  // 22-Mar-2010 change from   mail_lookup_auth_name to mail_lookup_auth_name_smtp
   /*
   if ((j = mail_lookup_auth_name ("PLAIN",NIL)) && (--j < MAXAUTHENTICATORS) &&
       (ESMTP.auth & (1 << j)) &&
       (j = mail_lookup_auth_name ("LOGIN",NIL)) && (--j < MAXAUTHENTICATORS))
     ESMTP.auth &= ~(1 << j);
-    */
-  if ((j = mail_lookup_auth_name_smtp ("PLAIN",NIL)) && (--j < MAXAUTHENTICATORS) &&
-      (ESMTP.auth & (1 << j)) &&
-      (j = mail_lookup_auth_name_smtp ("LOGIN",NIL)) && (--j < MAXAUTHENTICATORS))
-    ESMTP.auth &= ~(1 << j);
+   */
   return i;                    /* return the response code */
 }
 \f
index 3bfdff3..84b392f 100755 (executable)
@@ -115,6 +115,7 @@ void ssl_onceonlyinit (void)
     mail_parameters (NIL,SET_SSLDRIVER,(void *) &ssldriver);
     mail_parameters (NIL,SET_SSLSTART,(void *) ssl_start);
     SSL_library_init ();       /* add all algorithms */
+    SSL_load_error_strings();
   }
 }
 \f
@@ -173,7 +174,7 @@ static SSLSTREAM *ssl_start (TCPSTREAM *tstream,char *host,unsigned long flags)
       if (sf) (*sf) (host,reason,flags);
       else {                   /* no error callback, build error message */
        sprintf (tmp,"Certificate failure for %.80s: %.512s",host,reason);
-       mm_log (tmp,ERROR);
+       MM_LOG (tmp,ERROR);
       }
     case '\0':                 /* user answered no to certificate callback */
       if (flags & NET_TRYSSL)  /* return dummy stream to stop tryssl */
@@ -185,8 +186,9 @@ static SSLSTREAM *ssl_start (TCPSTREAM *tstream,char *host,unsigned long flags)
                                /* pass to error callback */
       else if (sf) (*sf) (host,reason,flags);
       else {                   /* no error callback, build error message */
-       sprintf (tmp,"TLS/SSL failure for %.80s: %.512s",host,reason);
-       mm_log (tmp,ERROR);
+        sprintf (tmp,"TLS/SSL failure for %.80s: %.512s",host,reason);
+        MM_LOG (tmp,ERROR);
+        free (reason); /* OpenSSL error buf */
       }
       break;
     }
@@ -221,9 +223,25 @@ static char *ssl_start_work (SSLSTREAM *stream,char *host,unsigned long flags)
   ssl_last_host = host;
   if (!(stream->context = SSL_CTX_new ((flags & NET_TLSCLIENT) ?
                                       TLSv1_client_method () :
-                                      SSLv23_client_method ())))
-    return "SSL context failed";
-  SSL_CTX_set_options (stream->context,0);
+                                      SSLv23_client_method ()))) {
+    /* bio to memory buf */
+    BIO *bio = BIO_new (BIO_s_mem ());
+    ERR_print_errors (bio);
+    char *buf = NULL;
+    size_t len = BIO_get_mem_data (bio, &buf);
+    char *ret = (char *) calloc (1, 1 + len + 40);
+    if (ret) {
+      memcpy (ret, buf , len);
+    }
+    sprintf (ret+len, ": SSL context failed [0x%x] ", flags & NET_TLSCLIENT);
+    BIO_free (bio);
+    return ret;
+//    return "SSL context failed";
+  }
+  if (flags & NET_FORCE_LOWER_TLS_VERSION)
+       SSL_CTX_set_options(stream->context, SSL_OP_NO_SSLv2|SSL_OP_NO_TLSv1_1|SSL_OP_NO_TLSv1_2);
+  else
+    SSL_CTX_set_options (stream->context,0);
                                /* disable certificate validation? */
   if (flags & NET_NOVALIDATECERT)
     SSL_CTX_set_verify (stream->context,SSL_VERIFY_NONE,NIL);
@@ -271,7 +289,7 @@ static char *ssl_start_work (SSLSTREAM *stream,char *host,unsigned long flags)
                                host))) {
                                /* application callback */
     if (scq) return (*scq) (err,host,cert ? cert->name : "???") ? NIL : "";
-                               /* error message to return via mm_log() */
+                               /* error message to return via MM_LOG() */
     sprintf (tmp,"*%.128s: %.255s",err,cert ? cert->name : "???");
     return ssl_last_error = cpystr (tmp);
   }
@@ -294,7 +312,7 @@ static int ssl_open_verify (int ok,X509_STORE_CTX *ctx)
       (X509_STORE_CTX_get_error (ctx));
     X509_NAME_oneline (X509_get_subject_name
                       (X509_STORE_CTX_get_current_cert (ctx)),cert,255);
-    if (!scq) {                        /* mm_log() error message if no callback */
+    if (!scq) {                        /* MM_LOG() error message if no callback */
       sprintf (tmp,"*%.128s: %.255s",err,cert);
       ssl_last_error = cpystr (tmp);
     }
@@ -489,7 +507,7 @@ long ssl_getdata (SSLSTREAM *stream)
     int ti = ttmo_read ? now + ttmo_read : 0;
     if (SSL_pending (stream->con)) i = 1;
     else {
-      if (tcpdebug) mm_log ("Reading SSL data",TCPDEBUG);
+      if (tcpdebug) MM_LOG ("Reading SSL data",TCPDEBUG);
       tmo.tv_usec = 0;
       FD_ZERO (&fds);          /* initialize selection vector */
       FD_ZERO (&efds);         /* handle errors too */
@@ -515,17 +533,17 @@ long ssl_getdata (SSLSTREAM *stream)
          if (i) sprintf (s = tmp,"SSL data read I/O error %d SSL error %d",
                          errno,SSL_get_error (stream->con,i));
          else s = "SSL data read end of file";
-         mm_log (s,TCPDEBUG);
+         MM_LOG (s,TCPDEBUG);
        }
        return ssl_abort (stream);
       }
       stream->iptr = stream->ibuf;/* point at TCP buffer */
       stream->ictr = i;                /* set new byte count */
-      if (tcpdebug) mm_log ("Successfully read SSL data",TCPDEBUG);
+      if (tcpdebug) MM_LOG ("Successfully read SSL data",TCPDEBUG);
     }
                                /* timeout, punt unless told not to */
     else if (!tmoh || !(*tmoh) (now - t,now - tl)) {
-      if (tcpdebug) mm_log ("SSL data read timeout",TCPDEBUG);
+      if (tcpdebug) MM_LOG ("SSL data read timeout",TCPDEBUG);
       return ssl_abort (stream);
     }
   }
@@ -558,7 +576,7 @@ long ssl_sout (SSLSTREAM *stream,char *string,unsigned long size)
   blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
   if (!stream->con) return NIL;
   (*bn) (BLOCK_TCPWRITE,NIL);
-  if (tcpdebug) mm_log ("Writing to SSL",TCPDEBUG);
+  if (tcpdebug) MM_LOG ("Writing to SSL",TCPDEBUG);
                                /* until request satisfied */
   for (i = 0; size > 0; string += i,size -= i)
                                /* write as much as we can */
@@ -567,11 +585,11 @@ long ssl_sout (SSLSTREAM *stream,char *string,unsigned long size)
        char tmp[MAILTMPLEN];
        sprintf (tmp,"SSL data write I/O error %d SSL error %d",
                 errno,SSL_get_error (stream->con,i));
-       mm_log (tmp,TCPDEBUG);
+       MM_LOG (tmp,TCPDEBUG);
       }
       return ssl_abort (stream);/* write failed */
     }
-  if (tcpdebug) mm_log ("successfully wrote to TCP",TCPDEBUG);
+  if (tcpdebug) MM_LOG ("successfully wrote to TCP",TCPDEBUG);
   (*bn) (BLOCK_NONE,NIL);
   return LONGT;                        /* all done */
 }
index 7129092..288e085 100755 (executable)
@@ -150,7 +150,7 @@ void *tcp_parameters (long function,void *value)
 
 TCPSTREAM *tcp_open (char *host,char *service,unsigned long port)
 {
-       mm_log("tcp_open - begin", NIL);
+       MM_LOG("tcp_open - begin", NIL);
   TCPSTREAM *stream = NIL;
   int family;
   int sock = -1;
@@ -177,7 +177,7 @@ TCPSTREAM *tcp_open (char *host,char *service,unsigned long port)
     if (adr = ip_stringtoaddr (tmp,&adrlen,&family)) {
       (*bn) (BLOCK_TCPOPEN,NIL);
                                /* get an open socket for this system */
-      mm_log("tcp_open - Before calling tcp_socket_open", NIL);
+      MM_LOG("tcp_open - Before calling tcp_socket_open", NIL);
       sock = tcp_socket_open (family,adr,adrlen,port,tmp,ctrp,hostname = host);
       (*bn) (BLOCK_NONE,NIL);
       fs_give ((void **) &adr);
@@ -187,7 +187,7 @@ TCPSTREAM *tcp_open (char *host,char *service,unsigned long port)
   else {                       /* lookup host name */
     if (tcpdebug) {
       sprintf (tmp,"DNS resolution %.80s",host);
-      mm_log (tmp,TCPDEBUG);
+      MM_LOG (tmp,TCPDEBUG);
     }
     (*bn) (BLOCK_DNSLOOKUP,NIL);/* quell alarms */
     data = (*bn) (BLOCK_SENSITIVE,NIL);
@@ -196,15 +196,16 @@ TCPSTREAM *tcp_open (char *host,char *service,unsigned long port)
     (*bn) (BLOCK_NONSENSITIVE,data);
     (*bn) (BLOCK_NONE,NIL);
     if (s) {                   /* DNS resolution won? */
-      if (tcpdebug) mm_log ("DNS resolution done",TCPDEBUG);
+      if (tcpdebug) MM_LOG ("DNS resolution done",TCPDEBUG);
+      int retry_all_addr = 0; /* if you want to try all ip addresses, set it */
       do {
         (*bn) (BLOCK_TCPOPEN,NIL);
         if (((sock = tcp_socket_open (family,s,adrlen,port,tmp,ctrp,
           hostname)) < 0) &&
           (s = ip_nametoaddr (NIL,&adrlen,&family,&hostname,&next)) &&
-          !silent) mm_log (tmp,WARN);
+          !silent) MM_LOG (tmp,WARN);
           (*bn) (BLOCK_NONE,NIL);
-      } while ((sock < 0) && s);/* repeat until success or no more addreses */
+      } while ((sock < 0) && s && retry_all_addr);/* repeat until success or no more addreses */
     }
   }
   if (sock >= 0)  {            /* won */
@@ -217,9 +218,9 @@ TCPSTREAM *tcp_open (char *host,char *service,unsigned long port)
     if (stream->ictr = ctr) *(stream->iptr = stream->ibuf) = tmp[0];
                                /* copy official host name */
     stream->host = cpystr (hostname);
-    if (tcpdebug) mm_log ("Stream open and ready for read",TCPDEBUG);
+    if (tcpdebug) MM_LOG ("Stream open and ready for read",TCPDEBUG);
   }
-  else if (!silent) mm_log (tmp,ERROR);
+  else if (!silent) MM_LOG (tmp,ERROR);
   return stream;               /* return success */
 }
 \f
@@ -237,7 +238,7 @@ TCPSTREAM *tcp_open (char *host,char *service,unsigned long port)
 int tcp_socket_open (int family,void *adr,size_t adrlen,unsigned short port,
                     char *tmp,int *ctr,char *hst)
 {
-       mm_log("tcp_socket_open - begin", NIL);
+       MM_LOG("tcp_socket_open - begin", NIL);
   int i,ti,sock,flgs;
   size_t len;
   time_t now;
@@ -250,9 +251,9 @@ int tcp_socket_open (int family,void *adr,size_t adrlen,unsigned short port,
                                /* fetid Solaris */
   void *data = (*bn) (BLOCK_SENSITIVE,NIL);
   sprintf (tmp,"Trying IP address [%s]",ip_sockaddrtostring (sadr));
-  mm_log (tmp,NIL);
+  MM_LOG (tmp,NIL);
                                /* make a socket */
-       mm_log("tcp_socket_open - make a socket", NIL);
+       MM_LOG("tcp_socket_open - make a socket", NIL);
   if ((sock = socket (sadr->sa_family,SOCK_STREAM,pt ? pt->p_proto : 0)) < 0) {
     sprintf (tmp,"Unable to create TCP socket: %s",strerror (errno));
     (*bn) (BLOCK_NONSENSITIVE,data);
@@ -288,14 +289,14 @@ int tcp_socket_open (int family,void *adr,size_t adrlen,unsigned short port,
     }
 
     snprintf (debug_meesage, DEBUG_MESSAGE_LENGTH, "tcp_socket_open - socket id [%d]", sock);
-    mm_log(debug_meesage, NIL);
+    MM_LOG(debug_meesage, NIL);
 
     if ((sock >= 0) && ctr) {  /* want open timeout? */
       now = time (0);          /* open timeout */
       ti = ttmo_open ? now + ttmo_open : 0;
 
       snprintf (debug_meesage, DEBUG_MESSAGE_LENGTH, "ti [%d]", ti);
-      mm_log(debug_meesage, NIL);
+      MM_LOG(debug_meesage, NIL);
 
       tmo.tv_usec = 0;
       FD_ZERO (&rfds);         /* initialize selection vector */
@@ -306,9 +307,9 @@ int tcp_socket_open (int family,void *adr,size_t adrlen,unsigned short port,
       FD_SET (sock,&efds);
       do {                     /* block under timeout */
         tmo.tv_sec = ti ? ti - now : 0;
-        mm_log("tcp_socket_open - Before calling select", NIL);
+        MM_LOG("tcp_socket_open - Before calling select", NIL);
         i = select (sock+1,&rfds,&wfds,&efds,ti ? &tmo : NIL);
-        mm_log("tcp_socket_open - After calling select", NIL);
+        MM_LOG("tcp_socket_open - After calling select", NIL);
         now = time (0);                /* fake timeout if interrupt & time expired */
         if ((i < 0) && (errno == EINTR) && ti && (ti <= now)) i = 0;
       } while ((i < 0) && (errno == EINTR));
@@ -321,7 +322,7 @@ int tcp_socket_open (int family,void *adr,size_t adrlen,unsigned short port,
       }        
       if (i <= 0) {            /* timeout or error? */
         snprintf (debug_meesage, DEBUG_MESSAGE_LENGTH, "timed out or error. i [%d]", i);
-        mm_log(debug_meesage, NIL);
+        MM_LOG(debug_meesage, NIL);
         i = i ? errno : ETIMEDOUT;/* determine error code */
         close (sock);          /* flush socket */
         sock = -1;
@@ -381,7 +382,7 @@ TCPSTREAM *tcp_aopen (NETMBX *mb,char *service,char *usrbuf)
     if (adr = ip_stringtoaddr (host,&len,&i)) fs_give ((void **) &adr);
     else {
       sprintf (tmp,"Bad format domain-literal: %.80s",host);
-      mm_log (tmp,ERROR);
+      MM_LOG (tmp,ERROR);
       return NIL;
     }
   }
@@ -395,7 +396,7 @@ TCPSTREAM *tcp_aopen (NETMBX *mb,char *service,char *usrbuf)
   if (tcpdebug) {
     char msg[MAILTMPLEN];
     sprintf (msg,"Trying %.100s",tmp);
-    mm_log (msg,TCPDEBUG);
+    MM_LOG (msg,TCPDEBUG);
   }
                                /* parse command into argv */
   for (i = 1,path = argv[0] = strtok_r (tmp," ",&r);
@@ -460,7 +461,7 @@ TCPSTREAM *tcp_aopen (NETMBX *mb,char *service,char *usrbuf)
   if (i <= 0) {                        /* timeout or error? */
     sprintf (tmp,i ? "error in %s to IMAP server" :
             "%s to IMAP server timed out",(*service == '*') ? "ssh" : "rsh");
-    mm_log (tmp,WARN);
+    MM_LOG (tmp,WARN);
     tcp_close (stream);                /* punt stream */
     stream = NIL;
   }
@@ -552,7 +553,10 @@ long tcp_getbuffer (TCPSTREAM *stream,unsigned long size,char *s)
 {
   unsigned long n;
                                /* make sure socket still alive */
-  if (stream->tcpsi < 0) return NIL;
+  if (stream->tcpsi < 0) {
+         MM_LOG ("tcpsi < 0",TCPDEBUG);
+         return NIL;
+  }
                                /* can transfer bytes from buffer? */
   if (n = min (size,stream->ictr)) {
     memcpy (s,stream->iptr,n); /* yes, slurp as much as we can from it */
@@ -572,7 +576,7 @@ long tcp_getbuffer (TCPSTREAM *stream,unsigned long size,char *s)
       time_t tl = time (0);
       time_t now = tl;
       time_t ti = ttmo_read ? now + ttmo_read : 0;
-      if (tcpdebug) mm_log ("Reading TCP buffer",TCPDEBUG);
+      if (tcpdebug) MM_LOG ("Reading TCP buffer",TCPDEBUG);
       tmo.tv_usec = 0;
       FD_ZERO (&fds);          /* initialize selection vector */
       FD_ZERO (&efds);         /* handle errors too */
@@ -591,21 +595,20 @@ long tcp_getbuffer (TCPSTREAM *stream,unsigned long size,char *s)
          while (((i = read (stream->tcpsi,s,(int) min (maxposint,size))) < 0)
                 && (errno == EINTR));
        if (i <= 0) {           /* error seen? */
-         if (tcpdebug) {
            char tmp[MAILTMPLEN];
            if (i) sprintf (s = tmp,"TCP buffer read I/O error %d",errno);
            else s = "TCP buffer read end of file";
-           mm_log (s,TCPDEBUG);
-         }
+           MM_LOG (s,TCPDEBUG);
+
          return tcp_abort (stream);
        }
        s += i;                 /* success, point at new place to write */
        size -= i;              /* reduce byte count */
-       if (tcpdebug) mm_log ("Successfully read TCP buffer",TCPDEBUG);
+       if (tcpdebug) MM_LOG ("Successfully read TCP buffer",TCPDEBUG);
       }
                                /* timeout, punt unless told not to */
       else if (!tmoh || !(*tmoh) (now - t,now - tl)) {
-       if (tcpdebug) mm_log ("TCP buffer read timeout",TCPDEBUG);
+       MM_LOG ("TCP buffer read timeout",TCPDEBUG);
        return tcp_abort (stream);
       }
     }
@@ -633,7 +636,7 @@ long tcp_getdata (TCPSTREAM *stream)
     time_t tl = time (0);      /* start of request */
     time_t now = tl;
     time_t ti = ttmo_read ? now + ttmo_read : 0;
-    if (tcpdebug) mm_log ("Reading TCP data",TCPDEBUG);
+    if (tcpdebug) MM_LOG ("Reading TCP data",TCPDEBUG);
     tmo.tv_usec = 0;
     FD_ZERO (&fds);            /* initialize selection vector */
     FD_ZERO (&efds);           /* handle errors too */
@@ -655,17 +658,17 @@ long tcp_getdata (TCPSTREAM *stream)
          char *s,tmp[MAILTMPLEN];
          if (i) sprintf (s = tmp,"TCP data read I/O error %d",errno);
          else s = "TCP data read end of file";
-         mm_log (s,TCPDEBUG);
+         MM_LOG (s,TCPDEBUG);
        }
        return tcp_abort (stream);
       }
       stream->ictr = i;                /* success, set new count and pointer */
       stream->iptr = stream->ibuf;
-      if (tcpdebug) mm_log ("Successfully read TCP data",TCPDEBUG);
+      if (tcpdebug) MM_LOG ("Successfully read TCP data",TCPDEBUG);
     }
                                /* timeout, punt unless told not to */
     else if (!tmoh || !(*tmoh) (now - t,now - tl)) {
-      if (tcpdebug) mm_log ("TCP data read timeout",TCPDEBUG);
+      if (tcpdebug) MM_LOG ("TCP data read timeout",TCPDEBUG);
       return tcp_abort (stream);/* error or timeout no-continue */
     }
   }
@@ -705,7 +708,7 @@ long tcp_sout (TCPSTREAM *stream,char *string,unsigned long size)
     time_t tl = time (0);      /* start of request */
     time_t now = tl;
     time_t ti = ttmo_write ? now + ttmo_write : 0;
-    if (tcpdebug) mm_log ("Writing to TCP",TCPDEBUG);
+    if (tcpdebug) MM_LOG ("Writing to TCP",TCPDEBUG);
     tmo.tv_usec = 0;
     FD_ZERO (&fds);            /* initialize selection vector */
     FD_ZERO (&efds);           /* handle errors too */
@@ -726,17 +729,17 @@ long tcp_sout (TCPSTREAM *stream,char *string,unsigned long size)
        if (tcpdebug) {
          char tmp[MAILTMPLEN];
          sprintf (tmp,"TCP write I/O error %d",errno);
-         mm_log (tmp,TCPDEBUG);
+         MM_LOG (tmp,TCPDEBUG);
        }
        return tcp_abort (stream);
       }
       string += i;             /* how much we sent */
       size -= i;               /* count this size */
-      if (tcpdebug) mm_log ("successfully wrote to TCP",TCPDEBUG);
+      if (tcpdebug) MM_LOG ("successfully wrote to TCP",TCPDEBUG);
     }
                                /* timeout, punt unless told not to */
     else if (!tmoh || !(*tmoh) (now - t,now - tl)) {
-      if (tcpdebug) mm_log ("TCP write timeout",TCPDEBUG);
+      if (tcpdebug) MM_LOG ("TCP write timeout",TCPDEBUG);
       return tcp_abort (stream);
     }
   }
@@ -969,13 +972,13 @@ char *tcp_canonical (char *name)
   data = (*bn) (BLOCK_SENSITIVE,NIL);
   if (tcpdebug) {
     sprintf (host,"DNS canonicalization %.80s",name);
-    mm_log (host,TCPDEBUG);
+    MM_LOG (host,TCPDEBUG);
   }
                                /* get canonical name */
   if (!ip_nametoaddr (name,NIL,NIL,&ret,NIL)) ret = name;
   (*bn) (BLOCK_NONSENSITIVE,data);
   (*bn) (BLOCK_NONE,NIL);      /* alarms OK now */
-  if (tcpdebug) mm_log ("DNS canonicalization done",TCPDEBUG);
+  if (tcpdebug) MM_LOG ("DNS canonicalization done",TCPDEBUG);
   return ret;
 }
 \f
@@ -994,7 +997,7 @@ char *tcp_name (struct sockaddr *sadr,long flag)
     void *data;
     if (tcpdebug) {
       sprintf (tmp,"Reverse DNS resolution %s",adr);
-      mm_log (tmp,TCPDEBUG);
+      MM_LOG (tmp,TCPDEBUG);
     }
     (*bn) (BLOCK_DNSLOOKUP,NIL);/* quell alarms */
     data = (*bn) (BLOCK_SENSITIVE,NIL);
@@ -1006,7 +1009,7 @@ char *tcp_name (struct sockaddr *sadr,long flag)
     }
     (*bn) (BLOCK_NONSENSITIVE,data);
     (*bn) (BLOCK_NONE,NIL);    /* alarms OK now */
-    if (tcpdebug) mm_log ("Reverse DNS resolution done",TCPDEBUG);
+    if (tcpdebug) MM_LOG ("Reverse DNS resolution done",TCPDEBUG);
   }
   return cpystr (ret);
 }
diff --git a/libuw-imap-toolkit.manifest b/libuw-imap-toolkit.manifest
new file mode 100644 (file)
index 0000000..75b0fa5
--- /dev/null
@@ -0,0 +1,5 @@
+<manifest>
+    <request>
+        <domain name="_"/>
+    </request>
+</manifest>
index 71fbbea..8067e66 100644 (file)
@@ -1,14 +1,11 @@
-#sbs-git:slp/pkgs/u/uw-imap-toolkit uw-imap-toolkit 0.1.1 a675e5c581b6726dcb93c63d826d6827bf29d671
 %define _optdir        /opt
 %define _appdir        %{_optdir}/apps
-
-
 Name:       uw-imap-toolkit
 Summary:    IMAP-2007e developed by University of Washington
-Version: 0.1.1
+Version:    0.1.35
 Release:    0
 Group:      TO_BE/FILLED_IN
-License:    TO BE FILLED IN
+License:    Apache-2.0
 Source0:    %{name}-%{version}.tar.gz
 Requires(post): /sbin/ldconfig
 Requires(postun): /sbin/ldconfig
@@ -51,6 +48,8 @@ make %{?jobs:-j%jobs}
 rm -rf %{buildroot}
 %make_install
 
+mkdir -p %{buildroot}/usr/share/license/%{name}
+cp imap-2007e/LICENSE.txt %{buildroot}/usr/share/license/%{name}/LICENSE
 
 %post -n libuw-imap-toolkit -p /sbin/ldconfig
 
@@ -59,9 +58,10 @@ rm -rf %{buildroot}
 
 
 %files -n libuw-imap-toolkit
+%manifest libuw-imap-toolkit.manifest
 %defattr(-,root,root,-) 
 %{_libdir}/libuw-imap-toolkit.so.*
-
+/usr/share/license/%{name}/LICENSE
 
 %files -n libuw-imap-toolkit-devel
 %defattr(-,root,root,-)