Changeset 5498:e68f79fef249

Show
Ignore:
Timestamp:
2008-08-28 02:31:14 (3 months ago)
Author:
Brendan Cully <brendan@…>
Branch:
HEAD
Message:

Split long IMAP commands for the benefit of lazy servers (closes #3000).
Also touches lots of old, hairy code. Likely to wake sleeping dogs.

Location:
imap
Files:
4 modified

Legend:

Unmodified
Added
Removed
  • imap/command.c

    r5495 r5498  
    197197 *     for checking for a mailbox on append and login 
    198198 *   IMAP_CMD_PASS: command contains a password. Suppress logging. 
     199 *   IMAP_CMD_QUEUE: only queue command, do not execute. 
    199200 * Return 0 on success, -1 on Failure, -2 on OK Failure 
    200201 */ 
  • imap/imap.c

    r5496 r5498  
    857857} 
    858858 
    859 /* imap_make_msg_set: make an IMAP4rev1 UID message set out of a set of 
    860  *   headers, given a flag enum to filter on. 
    861  * Params: idata: IMAP_DATA containing context containing header set 
    862  *         buf: to write message set into 
    863  *         flag: enum of flag type on which to filter 
    864  *         changed: include only changed messages in message set 
    865  *         invert: invert sense of flag, eg M_READ matches unread messages 
    866  * Returns: number of messages in message set (0 if no matches) */ 
    867 int imap_make_msg_set (IMAP_DATA* idata, BUFFER* buf, int flag, int changed, 
    868                        int invert) 
    869 { 
    870   HEADER** hdrs;        /* sorted local copy */ 
     859/* Note: headers must be in SORT_ORDER. See imap_exec_msgset for args. 
     860 * Pos is an opaque pointer a la strtok. It should be 0 at first call. */ 
     861static int imap_make_msg_set (IMAP_DATA* idata, BUFFER* buf, int flag, 
     862                              int changed, int invert, int* pos) 
     863{ 
     864  HEADER** hdrs = idata->ctx->hdrs; 
    871865  int count = 0;        /* number of messages in message set */ 
    872866  int match = 0;        /* whether current message matches flag condition */ 
    873867  unsigned int setstart = 0;    /* start of current message range */ 
    874868  int n; 
    875   short oldsort;        /* we clobber reverse, must restore it */ 
    876869  int started = 0; 
    877870 
    878   if (Sort != SORT_ORDER) 
    879   { 
    880     hdrs = safe_calloc (idata->ctx->msgcount, sizeof (HEADER*)); 
    881     memcpy (hdrs, idata->ctx->hdrs, idata->ctx->msgcount * sizeof (HEADER*)); 
    882      
    883     oldsort = Sort; 
    884     Sort = SORT_ORDER; 
    885     qsort ((void*) hdrs, idata->ctx->msgcount, sizeof (HEADER*), 
    886       mutt_get_sort_func (SORT_ORDER)); 
    887     Sort = oldsort; 
    888   } 
    889   else 
    890     hdrs = idata->ctx->hdrs; 
     871  hdrs = idata->ctx->hdrs; 
    891872   
    892   for (n = 0; n < idata->ctx->msgcount; n++) 
     873  for (n = *pos; 
     874       n < idata->ctx->msgcount && buf->dptr - buf->data < IMAP_MAX_CMDLEN; 
     875       n++) 
    893876  { 
    894877    match = 0; 
     
    952935  } 
    953936 
     937  *pos = n; 
     938 
     939  return count; 
     940} 
     941 
     942/* Prepares commands for all messages matching conditions (must be flushed 
     943 * with imap_exec) 
     944 * Params: 
     945 *   idata: IMAP_DATA containing context containing header set 
     946 *   pre, post: commands are of the form "%s %s %s %s", tag, 
     947 *     pre, message set, post 
     948 *   flag: enum of flag type on which to filter 
     949 *   changed: include only changed messages in message set 
     950 *   invert: invert sense of flag, eg M_READ matches unread messages 
     951 * Returns: number of matched messages, or -1 on failure */ 
     952int imap_exec_msgset (IMAP_DATA* idata, const char* pre, const char* post, 
     953                      int flag, int changed, int invert) 
     954{ 
     955  HEADER** hdrs;        /* sorted local copy */ 
     956  short oldsort;        /* we clobber reverse, must restore it */ 
     957  BUFFER* cmd; 
     958  int pos; 
     959  int rc; 
     960  int count = 0; 
     961 
     962  if (! (cmd = mutt_buffer_init (NULL))) 
     963  { 
     964    dprint (1, (debugfile, "imap_exec_msgset: unable to allocate buffer\n")); 
     965    return -1; 
     966  } 
     967 
     968  /* We make a copy of the headers just in case resorting doesn't give 
     969   exactly the original order (duplicate messages?), because other parts of 
     970   the ctx are tied to the header order. This may be overkill. */ 
     971  if (Sort != SORT_ORDER) 
     972  { 
     973    hdrs = safe_calloc (idata->ctx->msgcount, sizeof (HEADER*)); 
     974    memcpy (hdrs, idata->ctx->hdrs, idata->ctx->msgcount * sizeof (HEADER*)); 
     975     
     976    oldsort = Sort; 
     977    Sort = SORT_ORDER; 
     978    qsort ((void*) hdrs, idata->ctx->msgcount, sizeof (HEADER*), 
     979           mutt_get_sort_func (Sort)); 
     980    Sort = oldsort; 
     981  } 
     982  else 
     983    hdrs = idata->ctx->hdrs; 
     984 
     985  pos = 0; 
     986 
     987  do 
     988  { 
     989    cmd->dptr = cmd->data; 
     990    mutt_buffer_printf (cmd, "%s ", pre); 
     991    rc = imap_make_msg_set (idata, cmd, flag, changed, invert, &pos); 
     992    if (rc > 0) 
     993    { 
     994      mutt_buffer_printf (cmd, " %s", post); 
     995      if (imap_exec (idata, cmd->data, IMAP_CMD_QUEUE)) 
     996      { 
     997        rc = -1; 
     998        goto out; 
     999      } 
     1000      count += rc; 
     1001    } 
     1002  } 
     1003  while (rc > 0); 
     1004   
     1005  rc = count; 
     1006 
     1007out: 
     1008  mutt_buffer_free (&cmd); 
    9541009  if (Sort != SORT_ORDER) 
    9551010    FREE (&hdrs); 
    9561011 
    957   return count; 
     1012  return rc; 
    9581013} 
    9591014 
     
    9781033 
    9791034/* Update the IMAP server to reflect the flags a single message.  */ 
    980  
    9811035int imap_sync_message (IMAP_DATA *idata, HEADER *hdr, BUFFER *cmd, 
    9821036                       int *err_continue) 
     
    10561110} 
    10571111 
    1058 static int sync_helper (IMAP_DATA* idata, BUFFER* buf, int right, int flag, 
    1059                         const char* name) 
    1060 { 
    1061   int rc = 0; 
     1112static int sync_helper (IMAP_DATA* idata, int right, int flag, const char* name) 
     1113{ 
     1114  int count = 0; 
     1115  int rc; 
     1116 
     1117  char buf[LONG_STRING]; 
    10621118 
    10631119  if (!mutt_bit_isset (idata->ctx->rights, right)) 
    10641120    return 0; 
    1065    
     1121 
    10661122  if (right == M_ACL_WRITE && !imap_has_flag (idata->flags, name)) 
    10671123    return 0; 
    10681124 
    1069   buf->dptr = buf->data; 
    1070   mutt_buffer_addstr (buf, "UID STORE "); 
    1071   if (imap_make_msg_set (idata, buf, flag, 1, 0)) 
    1072   { 
    1073     rc++; 
    1074     mutt_buffer_printf (buf, " +FLAGS.SILENT (%s)", name); 
    1075     imap_exec (idata, buf->data, IMAP_CMD_QUEUE); 
    1076   } 
    1077   buf->dptr = buf->data; 
    1078   mutt_buffer_addstr (buf, "UID STORE "); 
    1079   if (imap_make_msg_set (idata, buf, flag, 1, 1)) 
    1080   { 
    1081     rc++; 
    1082     mutt_buffer_printf (buf, " -FLAGS.SILENT (%s)", name); 
    1083     imap_exec (idata, buf->data, IMAP_CMD_QUEUE); 
    1084   } 
    1085    
    1086   return rc; 
     1125  snprintf (buf, sizeof(buf), "+FLAGS.SILENT (%s)", name); 
     1126  if ((rc = imap_exec_msgset (idata, "UID STORE", buf, flag, 1, 0)) < 0) 
     1127    return rc; 
     1128  count += rc; 
     1129 
     1130  buf[0] = '-'; 
     1131  if ((rc = imap_exec_msgset (idata, "UID STORE", buf, flag, 1, 1)) < 0) 
     1132    return rc; 
     1133  count += rc; 
     1134 
     1135  return count; 
    10871136} 
    10881137 
     
    10961145  IMAP_DATA* idata; 
    10971146  CONTEXT* appendctx = NULL; 
    1098   BUFFER cmd; 
    10991147  HEADER* h; 
    11001148  HEADER** hdrs = NULL; 
    11011149  int oldsort; 
    1102   int deleted; 
    11031150  int n; 
    11041151  int rc; 
     
    11191166    return rc; 
    11201167 
    1121   memset (&cmd, 0, sizeof (cmd)); 
    1122  
    11231168  /* if we are expunging anyway, we can do deleted messages very quickly... */ 
    11241169  if (expunge && mutt_bit_isset (idata->ctx->rights, M_ACL_DELETE)) 
    11251170  { 
    1126     mutt_buffer_addstr (&cmd, "UID STORE "); 
    1127     deleted = imap_make_msg_set (idata, &cmd, M_DELETED, 1, 0); 
    1128  
    1129     /* if we have a message set, then let's delete */ 
    1130     if (deleted) 
    1131     { 
    1132       mutt_message (_("Marking %d messages deleted..."), deleted); 
    1133       mutt_buffer_addstr (&cmd, " +FLAGS.SILENT (\\Deleted)"); 
     1171    if ((rc = imap_exec_msgset (idata, "UID STORE", "+FLAGS.SILENT (\\Deleted)", 
     1172                                M_DELETED, 1, 0)) < 0) 
     1173    { 
     1174      mutt_error (_("Expunge failed")); 
     1175      mutt_sleep (1); 
     1176      goto out; 
     1177    } 
     1178 
     1179    if (rc > 0) 
     1180    { 
    11341181      /* mark these messages as unchanged so second pass ignores them. Done 
    11351182       * here so BOGUS UW-IMAP 4.7 SILENT FLAGS updates are ignored. */ 
    11361183      for (n = 0; n < ctx->msgcount; n++) 
    1137         if (ctx->hdrs[n]->deleted && ctx->hdrs[n]->changed) 
    1138           ctx->hdrs[n]->active = 0; 
    1139       if (imap_exec (idata, cmd.data, 0) != 0) 
    1140       { 
    1141         mutt_error (_("Expunge failed")); 
    1142         mutt_sleep (1); 
    1143         rc = -1; 
    1144         goto out; 
    1145       } 
     1184        if (ctx->hdrs[n]->deleted && ctx->hdrs[n]->changed) 
     1185          ctx->hdrs[n]->active = 0; 
     1186      mutt_message (_("Marking %d messages deleted..."), rc); 
    11461187    } 
    11471188  } 
     
    11771218          appendctx = mx_open_mailbox (ctx->path, M_APPEND | M_QUIET, NULL); 
    11781219        if (!appendctx) 
    1179         { 
    11801220          dprint (1, (debugfile, "imap_sync_mailbox: Error opening mailbox in append mode\n")); 
    1181         } 
    11821221        else 
    11831222          _mutt_save_message (h, appendctx, 1, 0, 0); 
     
    11931232  rc = 0; 
    11941233 
    1195   /* presort here to avoid doing 10 resorts in imap_make_msg_set */ 
     1234  /* presort here to avoid doing 10 resorts in imap_exec_msgset */ 
    11961235  oldsort = Sort; 
    11971236  if (Sort != SORT_ORDER) 
     
    12071246  } 
    12081247 
    1209   rc += sync_helper (idata, &cmd, M_ACL_DELETE, M_DELETED, "\\Deleted"); 
    1210   rc += sync_helper (idata, &cmd, M_ACL_WRITE, M_FLAG, "\\Flagged"); 
    1211   rc += sync_helper (idata, &cmd, M_ACL_WRITE, M_OLD, "Old"); 
    1212   rc += sync_helper (idata, &cmd, M_ACL_SEEN, M_READ, "\\Seen"); 
    1213   rc += sync_helper (idata, &cmd, M_ACL_WRITE, M_REPLIED, "\\Answered"); 
     1248  rc += sync_helper (idata, M_ACL_DELETE, M_DELETED, "\\Deleted"); 
     1249  rc += sync_helper (idata, M_ACL_WRITE, M_FLAG, "\\Flagged"); 
     1250  rc += sync_helper (idata, M_ACL_WRITE, M_OLD, "Old"); 
     1251  rc += sync_helper (idata, M_ACL_SEEN, M_READ, "\\Seen"); 
     1252  rc += sync_helper (idata, M_ACL_WRITE, M_REPLIED, "\\Answered"); 
    12141253 
    12151254  if (oldsort != Sort) 
     
    12201259  } 
    12211260 
    1222   if (rc) 
    1223   { 
    1224     if ((rc = imap_exec (idata, NULL, 0)) != IMAP_CMD_OK) 
    1225     { 
    1226       if (ctx->closing) 
     1261  if (rc && (imap_exec (idata, NULL, 0) != IMAP_CMD_OK)) 
     1262  { 
     1263    if (ctx->closing) 
     1264    { 
     1265      if (mutt_yesorno (_("Error saving flags. Close anyway?"), 0) == M_YES) 
    12271266      { 
    1228         if (mutt_yesorno (_("Error saving flags. Close anyway?"), 0) == M_YES) 
    1229         { 
    1230           rc = 0; 
    1231           idata->state = IMAP_AUTHENTICATED; 
    1232           goto out; 
    1233         } 
     1267        rc = 0; 
     1268        idata->state = IMAP_AUTHENTICATED; 
     1269        goto out; 
    12341270      } 
    1235       else 
    1236         mutt_error _("Error saving flags"); 
    1237       goto out; 
    1238     } 
    1239   } 
     1271    } 
     1272    else 
     1273      mutt_error _("Error saving flags"); 
     1274    goto out; 
     1275  } 
     1276 
    12401277  for (n = 0; n < ctx->msgcount; n++) 
    12411278    ctx->hdrs[n]->changed = 0; 
     
    12691306 
    12701307 out: 
    1271   if (cmd.data) 
    1272     FREE (&cmd.data); 
    12731308  if (appendctx) 
    12741309  { 
  • imap/imap_private.h

    r5495 r5498  
    5656 
    5757#define SEQLEN 5 
     58/* maximum length of command lines before they must be split (for 
     59 * lazy servers) */ 
     60#define IMAP_MAX_CMDLEN 1024 
    5861 
    5962#define IMAP_REOPEN_ALLOW     (1<<0) 
     
    226229                                 int create); 
    227230void imap_mboxcache_free (IMAP_DATA* idata); 
    228 int imap_make_msg_set (IMAP_DATA* idata, BUFFER* buf, int flag, int changed, 
    229                       int invert); 
     231int imap_exec_msgset (IMAP_DATA* idata, const char* pre, const char* post, 
     232                      int flag, int changed, int invert); 
    230233int imap_open_connection (IMAP_DATA* idata); 
    231234void imap_close_connection (IMAP_DATA* idata); 
  • imap/message.c

    r5494 r5498  
    689689  IMAP_DATA* idata; 
    690690  BUFFER cmd, sync_cmd; 
    691   char uid[11]; 
    692691  char mbox[LONG_STRING]; 
    693692  char mmbox[LONG_STRING]; 
     693  char prompt[LONG_STRING]; 
    694694  int rc; 
    695695  int n; 
    696696  IMAP_MBOX mx; 
    697697  int err_continue = M_NO; 
     698  int triedcreate = 0; 
    698699 
    699700  idata = (IMAP_DATA*) ctx->data; 
     
    720721   
    721722  imap_fix_path (idata, mx.mbox, mbox, sizeof (mbox)); 
    722  
    723   memset (&sync_cmd, 0, sizeof (sync_cmd)); 
    724   memset (&cmd, 0, sizeof (cmd)); 
    725   mutt_buffer_addstr (&cmd, "UID COPY "); 
    726  
    727   /* Null HEADER* means copy tagged messages */ 
    728   if (!h) 
    729   { 
    730     /* if any messages have attachments to delete, fall through to FETCH 
    731      * and APPEND. TODO: Copy what we can with COPY, fall through for the 
    732      * remainder. */ 
    733     for (n = 0; n < ctx->msgcount; n++) 
    734     { 
    735       if (ctx->hdrs[n]->tagged && ctx->hdrs[n]->attach_del) 
     723  imap_munge_mbox_name (mmbox, sizeof (mmbox), mbox); 
     724 
     725  /* loop in case of TRYCREATE */ 
     726  do 
     727  { 
     728    memset (&sync_cmd, 0, sizeof (sync_cmd)); 
     729    memset (&cmd, 0, sizeof (cmd)); 
     730 
     731    /* Null HEADER* means copy tagged messages */ 
     732    if (!h) 
     733    { 
     734      /* if any messages have attachments to delete, fall through to FETCH 
     735       * and APPEND. TODO: Copy what we can with COPY, fall through for the 
     736       * remainder. */ 
     737      for (n = 0; n < ctx->msgcount; n++) 
    736738      { 
    737         dprint (3, (debugfile, "imap_copy_messages: Message contains attachments to be deleted\n")); 
    738         return 1; 
     739        if (ctx->hdrs[n]->tagged && ctx->hdrs[n]->attach_del) 
     740        { 
     741          dprint (3, (debugfile, "imap_copy_messages: Message contains attachments to be deleted\n")); 
     742          return 1; 
     743        } 
     744 
     745        if (ctx->hdrs[n]->tagged && ctx->hdrs[n]->active && 
     746            ctx->hdrs[n]->changed) 
     747        { 
     748          rc = imap_sync_message (idata, ctx->hdrs[n], &sync_cmd, &err_continue); 
     749          if (rc < 0) 
     750          { 
     751            dprint (1, (debugfile, "imap_copy_messages: could not sync\n")); 
     752            goto fail; 
     753          } 
     754        } 
    739755      } 
    740756 
    741       if (ctx->hdrs[n]->tagged && ctx->hdrs[n]->active && 
    742           ctx->hdrs[n]->changed) 
     757      rc = imap_exec_msgset (idata, "UID COPY", mmbox, M_TAG, 0, 0); 
     758      if (!rc) 
    743759      { 
    744         rc = imap_sync_message (idata, ctx->hdrs[n], &sync_cmd, &err_continue); 
    745         if (rc < 0) 
    746         { 
    747           dprint (1, (debugfile, "imap_copy_messages: could not sync\n")); 
    748           goto fail; 
    749         } 
     760        dprint (1, (debugfile, "imap_copy_messages: No messages tagged\n")); 
     761        goto fail; 
    750762      } 
    751     } 
    752  
    753     rc = imap_make_msg_set (idata, &cmd, M_TAG, 0, 0); 
    754     if (!rc) 
    755     { 
    756       dprint (1, (debugfile, "imap_copy_messages: No messages tagged\n")); 
    757       goto fail; 
    758     } 
    759     mutt_message (_("Copying %d messages to %s..."), rc, mbox); 
    760   } 
    761   else 
    762   { 
    763     mutt_message (_("Copying message %d to %s..."), h->index+1, mbox); 
    764     snprintf (uid, sizeof (uid), "%u", HEADER_DATA (h)->uid); 
    765     mutt_buffer_addstr (&cmd, uid); 
    766  
    767     if (h->active && h->changed) 
    768     { 
    769       rc = imap_sync_message (idata, h, &sync_cmd, &err_continue); 
    770       if (rc < 0) 
     763      else if (rc < 0) 
    771764      { 
    772         dprint (1, (debugfile, "imap_copy_messages: could not sync\n")); 
    773         goto fail; 
     765        dprint (1, (debugfile, "could not queue copy\n")); 
     766        goto fail; 
    774767      } 
    775     } 
    776   } 
    777  
    778   /* let's get it on */ 
    779   mutt_buffer_addstr (&cmd, " "); 
    780   imap_munge_mbox_name (mmbox, sizeof (mmbox), mbox); 
    781   mutt_buffer_addstr (&cmd, mmbox); 
    782  
    783   rc = imap_exec (idata, cmd.data, IMAP_CMD_FAIL_OK); 
    784   if (rc == -2) 
    785   { 
    786     /* bail out if command failed for reasons other than nonexistent target */ 
    787     if (ascii_strncasecmp (imap_get_qualifier (idata->buf), "[TRYCREATE]", 11)) 
    788     { 
    789       imap_error ("imap_copy_messages", idata->buf); 
    790       goto fail; 
    791     } 
    792     dprint (2, (debugfile, "imap_copy_messages: server suggests TRYCREATE\n")); 
    793     snprintf (mmbox, sizeof (mmbox), _("Create %s?"), mbox); 
    794     if (option (OPTCONFIRMCREATE) && mutt_yesorno (mmbox, 1) < 1) 
    795     { 
    796       mutt_clear_error (); 
    797       goto fail; 
    798     } 
    799     if (imap_create_mailbox (idata, mbox) < 0) 
    800       goto fail; 
    801  
    802     /* try again */ 
    803     rc = imap_exec (idata, cmd.data, 0); 
    804   } 
     768      else 
     769        mutt_message (_("Copying %d messages to %s..."), rc, mbox); 
     770    } 
     771    else 
     772    { 
     773      mutt_message (_("Copying message %d to %s..."), h->index+1, mbox); 
     774      mutt_buffer_printf (&cmd, "UID COPY %u %s", HEADER_DATA (h)->uid, mmbox); 
     775 
     776      if (h->active && h->changed) 
     777      { 
     778        rc = imap_sync_message (idata, h, &sync_cmd, &err_continue); 
     779        if (rc < 0) 
     780        { 
     781          dprint (1, (debugfile, "imap_copy_messages: could not sync\n")); 
     782          goto fail; 
     783        } 
     784      }     
     785      if ((rc = imap_exec (idata, cmd.data, IMAP_CMD_QUEUE)) < 0) 
     786      { 
     787        dprint (1, (debugfile, "could not queue copy\n")); 
     788        goto fail; 
     789      } 
     790    } 
     791 
     792    /* let's get it on */ 
     793    rc = imap_exec (idata, NULL, IMAP_CMD_FAIL_OK); 
     794    if (rc == -2) 
     795    { 
     796      if (triedcreate) 
     797      { 
     798        dprint (1, (debugfile, "Already tried to create mailbox %s\n", mbox)); 
     799        break; 
     800      } 
     801      /* bail out if command failed for reasons other than nonexistent target */ 
     802      if (ascii_strncasecmp (imap_get_qualifier (idata->buf), "[TRYCREATE]", 11)) 
     803        break; 
     804      dprint (3, (debugfile, "imap_copy_messages: server suggests TRYCREATE\n")); 
     805      snprintf (prompt, sizeof (prompt), _("Create %s?"), mbox); 
     806      if (option (OPTCONFIRMCREATE) && mutt_yesorno (prompt, 1) < 1) 
     807      { 
     808        mutt_clear_error (); 
     809        break; 
     810      } 
     811      if (imap_create_mailbox (idata, mbox) < 0) 
     812        break; 
     813      triedcreate = 1; 
     814    } 
     815  } 
     816  while (rc == -2); 
     817 
    805818  if (rc != 0) 
    806819  {