Changeset 5494:53d9210aa4ee

Show
Ignore:
Timestamp:
2008-08-25 00:16:30 (3 months ago)
Author:
Brendan Cully <brendan@…>
Branch:
HEAD
Message:

Rework IMAP command queueing to allow pipelining to be disabled.
IDLE handling has been better abstracted, and there are fewer entry
points to the IMAP command issuing machinery. Any commands that
are simply queued may be executed whenever the pipeline fills,
instead of requiring explicit handling in the caller.

Tested on my Cyrus server, but I wouldn't be surprise if this causes
new problems.

Location:
imap
Files:
4 modified

Legend:

Unmodified
Added
Removed
  • imap/command.c

    r5405 r5494  
    22 * Copyright (C) 1996-8 Michael R. Elkins <me@mutt.org> 
    33 * Copyright (C) 1996-9 Brandon Long <blong@fiction.net> 
    4  * Copyright (C) 1999-2006 Brendan Cully <brendan@kublai.com> 
     4 * Copyright (C) 1999-2008 Brendan Cully <brendan@kublai.com> 
    55 *  
    66 *     This program is free software; you can redistribute it and/or modify 
     
    3838 
    3939/* forward declarations */ 
     40static int cmd_start (IMAP_DATA* idata, const char* cmdstr, int flags); 
     41static int cmd_queue_full (IMAP_DATA* idata); 
     42static int cmd_queue (IMAP_DATA* idata, const char* cmdstr); 
    4043static IMAP_COMMAND* cmd_new (IMAP_DATA* idata); 
    4144static int cmd_status (const char *s); 
     
    6871}; 
    6972 
    70 /* imap_cmd_queue: Add command to command queue. Fails if the queue is full. */ 
    71 int imap_cmd_queue (IMAP_DATA* idata, const char* cmdstr) 
    72 { 
    73   IMAP_COMMAND* cmd; 
    74  
    75   if (idata->status == IMAP_FATAL) 
    76   { 
    77     cmd_handle_fatal (idata); 
    78     return IMAP_CMD_BAD; 
    79   } 
    80  
    81   if (!(cmd = cmd_new (idata))) 
    82     return IMAP_CMD_BAD; 
    83  
    84   if (mutt_buffer_printf (idata->cmdbuf, "%s%s %s\r\n", 
    85       idata->state == IMAP_IDLE ? "DONE\r\n" : "", cmd->seq, cmdstr) < 0) 
    86     return IMAP_CMD_BAD; 
    87  
    88   if (idata->state == IMAP_IDLE) 
    89     idata->state = IMAP_SELECTED; 
    90  
    91   return 0; 
    92 } 
    93  
    9473/* imap_cmd_start: Given an IMAP command, send it to the server. 
    9574 *   If cmdstr is NULL, sends queued commands. */ 
    9675int imap_cmd_start (IMAP_DATA* idata, const char* cmdstr) 
    9776{ 
    98   int rc; 
    99  
    100   if (cmdstr && (rc = imap_cmd_queue (idata, cmdstr)) < 0) 
    101     return rc; 
    102  
    103   /* don't write old or empty commands */ 
    104   if (idata->cmdbuf->dptr == idata->cmdbuf->data) 
    105     return IMAP_CMD_BAD; 
    106  
    107   rc = mutt_socket_write (idata->conn, idata->cmdbuf->data); 
    108   idata->cmdbuf->dptr = idata->cmdbuf->data; 
    109  
    110   return (rc < 0) ? IMAP_CMD_BAD : 0; 
     77  return cmd_start (idata, cmdstr, 0); 
    11178} 
    11279 
     
    236203  int rc; 
    237204 
    238   if (idata->status == IMAP_FATAL) 
     205  if ((rc = cmd_start (idata, cmdstr, flags)) < 0) 
     206    return rc; 
     207 
     208  if (rc < 0) 
    239209  { 
    240210    cmd_handle_fatal (idata); 
     
    242212  } 
    243213 
    244   if (cmdstr && (rc = imap_cmd_queue (idata, cmdstr)) < 0) 
    245     return rc; 
    246    
    247   /* don't write old or empty commands */ 
    248   if (idata->cmdbuf->dptr == idata->cmdbuf->data) 
    249     return IMAP_CMD_BAD; 
    250    
    251   rc = mutt_socket_write_d (idata->conn, idata->cmdbuf->data, -1, 
    252     flags & IMAP_CMD_PASS ? IMAP_LOG_PASS : IMAP_LOG_CMD); 
    253   idata->cmdbuf->dptr = idata->cmdbuf->data; 
    254  
    255   if (rc < 0) 
    256   { 
    257     cmd_handle_fatal (idata); 
    258     return -1; 
    259   } 
     214  if (flags & IMAP_CMD_QUEUE) 
     215    return 0; 
    260216 
    261217  do 
     
    324280} 
    325281 
     282/* imap_cmd_idle: Enter the IDLE state. */ 
     283int imap_cmd_idle (IMAP_DATA* idata) 
     284{ 
     285  int rc; 
     286 
     287  imap_cmd_start (idata, "IDLE"); 
     288  do 
     289    rc = imap_cmd_step (idata); 
     290  while (rc == IMAP_CMD_CONTINUE); 
     291 
     292  if (rc == IMAP_CMD_RESPOND) 
     293  { 
     294    /* successfully entered IDLE state */ 
     295    idata->state = IMAP_IDLE; 
     296    /* queue automatic exit when next command is issued */ 
     297    mutt_buffer_printf (idata->cmdbuf, "DONE\r\n"); 
     298    rc = IMAP_CMD_OK; 
     299  } 
     300  if (rc != IMAP_CMD_OK) 
     301  { 
     302    dprint (1, (debugfile, "imap_cmd_idle: error starting IDLE\n")); 
     303    return -1; 
     304  } 
     305   
     306  return 0; 
     307} 
     308 
     309static int cmd_queue_full (IMAP_DATA* idata) 
     310{ 
     311  if ((idata->nextcmd + 1) % IMAP_PIPELINE_DEPTH == idata->lastcmd) 
     312    return 1; 
     313 
     314  return 0; 
     315} 
     316 
    326317/* sets up a new command control block and adds it to the queue. 
    327318 * Returns NULL if the pipeline is full. */ 
     
    330321  IMAP_COMMAND* cmd; 
    331322 
    332   if ((idata->nextcmd + 1) % IMAP_PIPELINE_DEPTH == idata->lastcmd) 
    333   { 
    334     dprint (2, (debugfile, "cmd_new: IMAP command queue full\n")); 
     323  if (cmd_queue_full (idata)) 
     324  { 
     325    dprint (3, (debugfile, "cmd_new: IMAP command queue full\n")); 
    335326    return NULL; 
    336327  } 
     
    346337 
    347338  return cmd; 
     339} 
     340 
     341/* queues command. If the queue is full, attempts to drain it. */ 
     342static int cmd_queue (IMAP_DATA* idata, const char* cmdstr) 
     343{ 
     344  IMAP_COMMAND* cmd; 
     345  int rc; 
     346 
     347  if (cmd_queue_full (idata)) 
     348  { 
     349    dprint (3, (debugfile, "Draining IMAP command pipeline\n")); 
     350 
     351    rc = imap_exec (idata, NULL, IMAP_CMD_FAIL_OK); 
     352 
     353    if (rc < 0 && rc != -2) 
     354      return rc; 
     355  } 
     356 
     357  if (!(cmd = cmd_new (idata))) 
     358    return IMAP_CMD_BAD; 
     359 
     360  if (mutt_buffer_printf (idata->cmdbuf, "%s %s\r\n", cmd->seq, cmdstr) < 0) 
     361    return IMAP_CMD_BAD; 
     362 
     363  return 0; 
     364} 
     365 
     366static int cmd_start (IMAP_DATA* idata, const char* cmdstr, int flags) 
     367{ 
     368  int rc; 
     369 
     370  if (idata->status == IMAP_FATAL) 
     371  { 
     372    cmd_handle_fatal (idata); 
     373    return -1; 
     374  } 
     375 
     376  if (cmdstr && ((rc = cmd_queue (idata, cmdstr)) < 0)) 
     377    return rc; 
     378 
     379  if (flags & IMAP_CMD_QUEUE) 
     380    return 0; 
     381 
     382  if (idata->cmdbuf->dptr == idata->cmdbuf->data) 
     383    return IMAP_CMD_BAD; 
     384 
     385  rc = mutt_socket_write_d (idata->conn, idata->cmdbuf->data, -1, 
     386                            flags & IMAP_CMD_PASS ? IMAP_LOG_PASS : IMAP_LOG_CMD); 
     387  idata->cmdbuf->dptr = idata->cmdbuf->data; 
     388 
     389  /* unidle when command queue is flushed */ 
     390  if (idata->state == IMAP_IDLE) 
     391    idata->state = IMAP_SELECTED; 
     392 
     393  return (rc < 0) ? IMAP_CMD_BAD : 0; 
    348394} 
    349395 
  • imap/imap.c

    r5443 r5494  
    22 * Copyright (C) 1996-8 Michael R. Elkins <me@mutt.org> 
    33 * Copyright (C) 1996-9 Brandon Long <blong@fiction.net> 
    4  * Copyright (C) 1999-2007 Brendan Cully <brendan@kublai.com> 
     4 * Copyright (C) 1999-2008 Brendan Cully <brendan@kublai.com> 
    55 *  
    66 *     This program is free software; you can redistribute it and/or modify 
     
    348348  } 
    349349  if (!conn) 
    350           return NULL; /* this happens when the initial connection fails */ 
     350    return NULL; /* this happens when the initial connection fails */ 
    351351 
    352352  if (!idata) 
     
    384384  { 
    385385    /* capabilities may have changed */ 
    386     imap_cmd_queue (idata, "CAPABILITY"); 
     386    imap_exec (idata, "CAPABILITY", IMAP_CMD_QUEUE); 
    387387    /* get root delimiter, '/' as default */ 
    388388    idata->delim = '/'; 
    389     imap_cmd_queue (idata, "LIST \"\" \"\""); 
     389    imap_exec (idata, "LIST \"\" \"\"", IMAP_CMD_QUEUE); 
    390390    if (option (OPTIMAPCHECKSUBSCRIBED)) 
    391       imap_cmd_queue (idata, "LSUB \"\" \"*\""); 
     391      imap_exec (idata, "LSUB \"\" \"*\"", IMAP_CMD_QUEUE); 
    392392    /* we may need the root delimiter before we open a mailbox */ 
    393393    imap_exec (idata, NULL, IMAP_CMD_FAIL_OK); 
     
    599599  { 
    600600    snprintf (bufout, sizeof (bufout), "MYRIGHTS %s", buf); 
    601     imap_cmd_queue (idata, bufout); 
     601    imap_exec (idata, bufout, IMAP_CMD_QUEUE); 
    602602  } 
    603603  /* assume we have all rights if ACL is unavailable */ 
     
    10731073    rc++; 
    10741074    mutt_buffer_printf (buf, " +FLAGS.SILENT (%s)", name); 
    1075     imap_cmd_queue (idata, buf->data); 
     1075    imap_exec (idata, buf->data, IMAP_CMD_QUEUE); 
    10761076  } 
    10771077  buf->dptr = buf->data; 
     
    10811081    rc++; 
    10821082    mutt_buffer_printf (buf, " -FLAGS.SILENT (%s)", name); 
    1083     imap_cmd_queue (idata, buf->data); 
     1083    imap_exec (idata, buf->data, IMAP_CMD_QUEUE); 
    10841084  } 
    10851085   
     
    12591259  if (expunge && ctx->closing) 
    12601260  { 
    1261     imap_cmd_queue (idata, "CLOSE"); 
     1261    imap_exec (idata, "CLOSE", IMAP_CMD_QUEUE); 
    12621262    idata->state = IMAP_AUTHENTICATED; 
    12631263  } 
     
    12971297       * and the mailbox is unchanged, so we may have to close here */ 
    12981298      if (!ctx->deleted) 
    1299         imap_cmd_queue (idata, "CLOSE"); 
     1299        imap_exec (idata, "CLOSE", IMAP_CMD_QUEUE); 
    13001300      if (idata->state == IMAP_IDLE) 
    13011301      { 
     
    13531353      && (idata->state != IMAP_IDLE || time(NULL) >= idata->lastread + ImapKeepalive)) 
    13541354  { 
    1355     imap_cmd_start (idata, "IDLE"); 
    1356     idata->state = IMAP_IDLE; 
    1357     do 
    1358       result = imap_cmd_step (idata); 
    1359     while (result == IMAP_CMD_CONTINUE); 
    1360     /* it's possible that we were notified and fetched mail before 
    1361      * getting to the +, in which case we've automatically unidled. */ 
    1362     if (result != IMAP_CMD_RESPOND && result != IMAP_CMD_OK) 
    1363     { 
    1364       dprint (1, (debugfile, "Error starting IDLE\n")); 
    1365       idata->state = IMAP_SELECTED; 
     1355    if (imap_cmd_idle (idata) < 0) 
    13661356      return -1; 
    1367     } 
    13681357  } 
    13691358  if (idata->state == IMAP_IDLE) 
     
    14871476              "STATUS %s (UIDNEXT UIDVALIDITY UNSEEN RECENT)", munged); 
    14881477 
    1489     if (imap_cmd_queue (idata, command) < 0) 
    1490     { 
    1491       /* pipeline must be full, drain it */ 
    1492       dprint (2, (debugfile, "IMAP command pipeline full, draining\n")); 
    1493  
    1494       if (imap_exec (idata, NULL, IMAP_CMD_FAIL_OK) == -1) 
    1495         dprint (1, (debugfile, "Error polling mailboxes\n")); 
    1496        
    1497       if (imap_cmd_queue (idata, command) < 0) { 
    1498         /* real trouble */ 
    1499         dprint (1, (debugfile, "Error queueing command\n")); 
    1500         return 0; 
    1501       } 
     1478    if (imap_exec (idata, command, IMAP_CMD_QUEUE) < 0) 
     1479    { 
     1480      dprint (1, (debugfile, "Error queueing command\n")); 
     1481      return 0; 
    15021482    } 
    15031483  } 
     
    15511531  if (queue) 
    15521532  { 
    1553     imap_cmd_queue (idata, buf); 
     1533    imap_exec (idata, buf, IMAP_CMD_QUEUE); 
    15541534    queued = 1; 
    15551535    return 0; 
  • imap/imap_private.h

    r5120 r5494  
    11/* 
    22 * Copyright (C) 1996-9 Brandon Long <blong@fiction.net> 
    3  * Copyright (C) 1999-2005 Brendan Cully <brendan@kublai.com> 
     3 * Copyright (C) 1999-2008 Brendan Cully <brendan@kublai.com> 
    44 *  
    55 *     This program is free software; you can redistribute it and/or modify 
     
    7070#define IMAP_CMD_FAIL_OK (1<<0) 
    7171#define IMAP_CMD_PASS    (1<<1) 
     72#define IMAP_CMD_QUEUE   (1<<2) 
    7273 
    7374enum 
     
    244245 
    245246/* command.c */ 
    246 int imap_cmd_queue (IMAP_DATA* idata, const char* cmdstr); 
    247247int imap_cmd_start (IMAP_DATA* idata, const char* cmd); 
    248248int imap_cmd_step (IMAP_DATA* idata); 
     
    250250int imap_code (const char* s); 
    251251int imap_exec (IMAP_DATA* idata, const char* cmd, int flags); 
     252int imap_cmd_idle (IMAP_DATA* idata); 
    252253 
    253254/* message.c */ 
  • imap/message.c

    r5283 r5494  
    11/* 
    22 * Copyright (C) 1996-9 Brandon Long <blong@fiction.net> 
    3  * Copyright (C) 1999-2007 Brendan Cully <brendan@kublai.com> 
     3 * Copyright (C) 1999-2008 Brendan Cully <brendan@kublai.com> 
    44 *  
    55 *     This program is free software; you can redistribute it and/or modify 
     
    144144    imap_cmd_start (idata, buf); 
    145145   
    146     for (msgno = msgbegin; msgno <= msgend ; msgno++) 
     146    rc = IMAP_CMD_CONTINUE; 
     147    for (msgno = msgbegin; rc == IMAP_CMD_CONTINUE; msgno++) 
    147148    { 
    148149      mutt_progress_update (&progress, msgno + 1, -1);