imap.c
上传用户:xxcykj
上传日期:2007-01-04
资源大小:727k
文件大小:38k
源码类别:

Email客户端

开发平台:

Unix_Linux

  1. /*
  2.  * imap.c -- IMAP2bis/IMAP4 protocol methods
  3.  *
  4.  * Copyright 1997 by Eric S. Raymond
  5.  * For license terms, see the file COPYING in this directory.
  6.  */
  7. #include  "config.h"
  8. #include  <stdio.h>
  9. #include  <string.h>
  10. #include  <ctype.h>
  11. #if defined(STDC_HEADERS)
  12. #include  <stdlib.h>
  13. #endif
  14. #include  "fetchmail.h"
  15. #include  "socket.h"
  16. #ifdef KERBEROS_V4
  17. #ifdef KERBEROS_V5
  18. #include <kerberosIV/des.h>
  19. #include <kerberosIV/krb.h>
  20. #else
  21. #if defined (__bsdi__)
  22. #include <des.h>
  23. #define krb_get_err_text(e) (krb_err_txt[e])
  24. #endif
  25. #if defined(__NetBSD__) || (__FreeBSD__) || defined(__linux__)
  26. #define krb_get_err_text(e) (krb_err_txt[e])
  27. #endif
  28. #include <krb.h>
  29. #endif
  30. #endif /* KERBEROS_V4 */
  31. #include  "i18n.h"
  32. #ifdef GSSAPI
  33. #ifdef HAVE_GSSAPI_H
  34. #include <gssapi.h>
  35. #endif
  36. #ifdef HAVE_GSSAPI_GSSAPI_H
  37. #include <gssapi/gssapi.h>
  38. #endif
  39. #ifdef HAVE_GSSAPI_GSSAPI_GENERIC_H
  40. #include <gssapi/gssapi_generic.h>
  41. #endif
  42. #ifndef HAVE_GSS_C_NT_HOSTBASED_SERVICE
  43. #define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name
  44. #endif
  45. #endif
  46. #include "md5.h"
  47. #if OPIE_ENABLE
  48. #include <opie.h>
  49. #endif /* OPIE_ENABLE */
  50. #ifndef strstr /* glibc-2.1 declares this as a macro */
  51. extern char *strstr(); /* needed on sysV68 R3V7.1. */
  52. #endif /* strstr */
  53. /* imap_version values */
  54. #define IMAP2 -1 /* IMAP2 or IMAP2BIS, RFC1176 */
  55. #define IMAP4 0 /* IMAP4 rev 0, RFC1730 */
  56. #define IMAP4rev1 1 /* IMAP4 rev 1, RFC2060 */
  57. static int count, seen, recent, unseen, deletions, imap_version, preauth; 
  58. static int expunged, expunge_period;
  59. static char capabilities[MSGBUFSIZE+1];
  60. int imap_ok(int sock, char *argbuf)
  61. /* parse command response */
  62. {
  63.     char buf [MSGBUFSIZE+1];
  64.     seen = 0;
  65.     do {
  66. int ok;
  67. char *cp;
  68. if ((ok = gen_recv(sock, buf, sizeof(buf))))
  69.     return(ok);
  70. /* all tokens in responses are caseblind */
  71. for (cp = buf; *cp; cp++)
  72.     if (islower(*cp))
  73. *cp = toupper(*cp);
  74. /* interpret untagged status responses */
  75. if (strstr(buf, "* CAPABILITY"))
  76.     strncpy(capabilities, buf + 12, sizeof(capabilities));
  77. if (strstr(buf, "EXISTS"))
  78.     count = atoi(buf+2);
  79. if (strstr(buf, "RECENT"))
  80.     recent = atoi(buf+2);
  81. if (strstr(buf, "UNSEEN"))
  82. {
  83.     char *cp;
  84.     /*
  85.      * Handle both "* 42 UNSEEN" (if tha ever happens) and 
  86.      * "* OK [UNSEEN 42] 42". Note that what this gets us is
  87.      * a minimum index, not a count.
  88.      */
  89.     unseen = 0;
  90.     for (cp = buf; *cp && !isdigit(*cp); cp++)
  91. continue;
  92.     unseen = atoi(cp);
  93. }
  94. if (strstr(buf, "FLAGS"))
  95.     seen = (strstr(buf, "SEEN") != (char *)NULL);
  96. if (strstr(buf, "PREAUTH"))
  97.     preauth = TRUE;
  98.     } while
  99. (tag[0] != '' && strncmp(buf, tag, strlen(tag)));
  100.     if (tag[0] == '')
  101.     {
  102. if (argbuf)
  103.     strcpy(argbuf, buf);
  104. return(PS_SUCCESS); 
  105.     }
  106.     else
  107.     {
  108. char *cp;
  109. /* skip the tag */
  110. for (cp = buf; !isspace(*cp); cp++)
  111.     continue;
  112. while (isspace(*cp))
  113.     cp++;
  114. if (strncmp(cp, "PREAUTH", 2) == 0)
  115. {
  116.     if (argbuf)
  117. strcpy(argbuf, cp);
  118.     preauth = TRUE;
  119.     return(PS_SUCCESS);
  120. }
  121. else if (strncmp(cp, "OK", 2) == 0)
  122. {
  123.     if (argbuf)
  124. strcpy(argbuf, cp);
  125.     return(PS_SUCCESS);
  126. }
  127. else if (strncmp(cp, "BAD", 3) == 0)
  128.     return(PS_ERROR);
  129. else if (strncmp(cp, "NO", 2) == 0)
  130. {
  131.     if (stage == STAGE_GETAUTH) 
  132. return(PS_AUTHFAIL); /* RFC2060, 6.2.2 */
  133.     else
  134. return(PS_ERROR);
  135. }
  136. else
  137.     return(PS_PROTOCOL);
  138.     }
  139. }
  140. #if OPIE_ENABLE
  141. static int do_otp(int sock, struct query *ctl)
  142. {
  143.     int i, rval;
  144.     char buffer[128];
  145.     char challenge[OPIE_CHALLENGE_MAX+1];
  146.     char response[OPIE_RESPONSE_MAX+1];
  147.     gen_send(sock, "AUTHENTICATE X-OTP");
  148.     if (rval = gen_recv(sock, buffer, sizeof(buffer)))
  149. return rval;
  150.     if ((i = from64tobits(challenge, buffer)) < 0) {
  151. report(stderr, _("Could not decode initial BASE64 challengen"));
  152. return PS_AUTHFAIL;
  153.     };
  154.     to64frombits(buffer, ctl->remotename, strlen(ctl->remotename));
  155.     if (outlevel >= O_MONITOR)
  156. report(stdout, "IMAP> %sn", buffer);
  157.     /* best not to count on the challenge code handling multiple writes */
  158.     strcat(buffer, "rn");
  159.     SockWrite(sock, buffer, strlen(buffer));
  160.     if (rval = gen_recv(sock, buffer, sizeof(buffer)))
  161. return rval;
  162.     if ((i = from64tobits(challenge, buffer)) < 0) {
  163. report(stderr, _("Could not decode OTP challengen"));
  164. return PS_AUTHFAIL;
  165.     };
  166.     rval = opiegenerator(challenge, !strcmp(ctl->password, "opie") ? "" : ctl->password, response);
  167.     if ((rval == -2) && !run.poll_interval) {
  168. char secret[OPIE_SECRET_MAX+1];
  169. fprintf(stderr, _("Secret pass phrase: "));
  170. if (opiereadpass(secret, sizeof(secret), 0))
  171.     rval = opiegenerator(challenge, secret, response);
  172. memset(secret, 0, sizeof(secret));
  173.     };
  174.     if (rval)
  175. return(PS_AUTHFAIL);
  176.     to64frombits(buffer, response, strlen(response));
  177.     if (outlevel >= O_MONITOR)
  178. report(stdout, "IMAP> %sn", buffer);
  179.     strcat(buffer, "rn");
  180.     SockWrite(sock, buffer, strlen(buffer));
  181.     if (rval = gen_recv(sock, buffer, sizeof(buffer)))
  182. return rval;
  183.     if (strstr(buffer, "OK"))
  184. return PS_SUCCESS;
  185.     else
  186. return PS_AUTHFAIL;
  187. };
  188. #endif /* OPIE_ENABLE */
  189. #ifdef KERBEROS_V4
  190. #if SIZEOF_INT == 4
  191. typedef int int32;
  192. #elif SIZEOF_SHORT == 4
  193. typedef short int32;
  194. #elif SIZEOF_LONG == 4
  195. typedef long int32;
  196. #else
  197. #error Cannot deduce a 32-bit-type
  198. #endif
  199. static int do_rfc1731(int sock, char *truename)
  200. /* authenticate as per RFC1731 -- note 32-bit integer requirement here */
  201. {
  202.     int result = 0, len;
  203.     char buf1[4096], buf2[4096];
  204.     union {
  205.       int32 cint;
  206.       char cstr[4];
  207.     } challenge1, challenge2;
  208.     char srvinst[INST_SZ];
  209.     char *p;
  210.     char srvrealm[REALM_SZ];
  211.     KTEXT_ST authenticator;
  212.     CREDENTIALS credentials;
  213.     char tktuser[MAX_K_NAME_SZ+1+INST_SZ+1+REALM_SZ+1];
  214.     char tktinst[INST_SZ];
  215.     char tktrealm[REALM_SZ];
  216.     des_cblock session;
  217.     des_key_schedule schedule;
  218.     gen_send(sock, "AUTHENTICATE KERBEROS_V4");
  219.     /* The data encoded in the first ready response contains a random
  220.      * 32-bit number in network byte order.  The client should respond
  221.      * with a Kerberos ticket and an authenticator for the principal
  222.      * "imap.hostname@realm", where "hostname" is the first component
  223.      * of the host name of the server with all letters in lower case
  224.      * and where "realm" is the Kerberos realm of the server.  The
  225.      * encrypted checksum field included within the Kerberos
  226.      * authenticator should contain the server provided 32-bit number
  227.      * in network byte order.
  228.      */
  229.     if (result = gen_recv(sock, buf1, sizeof buf1)) {
  230. return result;
  231.     }
  232.     len = from64tobits(challenge1.cstr, buf1);
  233.     if (len < 0) {
  234. report(stderr, _("could not decode initial BASE64 challengen"));
  235. return PS_AUTHFAIL;
  236.     }
  237.     /* this patch by Dan Root <dar@thekeep.org> solves an endianess
  238.      * problem. */
  239.     {
  240. char tmp[4];
  241. *(int *)tmp = ntohl(*(int *) challenge1.cstr);
  242. memcpy(challenge1.cstr, tmp, sizeof(tmp));
  243.     }
  244.     /* Client responds with a Kerberos ticket and an authenticator for
  245.      * the principal "imap.hostname@realm" where "hostname" is the
  246.      * first component of the host name of the server with all letters
  247.      * in lower case and where "realm" is the Kerberos realm of the
  248.      * server.  The encrypted checksum field included within the
  249.      * Kerberos authenticator should contain the server-provided
  250.      * 32-bit number in network byte order.
  251.      */
  252.     strncpy(srvinst, truename, (sizeof srvinst)-1);
  253.     srvinst[(sizeof srvinst)-1] = '';
  254.     for (p = srvinst; *p; p++) {
  255.       if (isupper(*p)) {
  256. *p = tolower(*p);
  257.       }
  258.     }
  259.     strncpy(srvrealm, (char *)krb_realmofhost(srvinst), (sizeof srvrealm)-1);
  260.     srvrealm[(sizeof srvrealm)-1] = '';
  261.     if (p = strchr(srvinst, '.')) {
  262.       *p = '';
  263.     }
  264.     result = krb_mk_req(&authenticator, "imap", srvinst, srvrealm, 0);
  265.     if (result) {
  266. report(stderr, "krb_mq_req: %sn", krb_get_err_text(result));
  267. return PS_AUTHFAIL;
  268.     }
  269.     result = krb_get_cred("imap", srvinst, srvrealm, &credentials);
  270.     if (result) {
  271. report(stderr, "krb_get_cred: %sn", krb_get_err_text(result));
  272. return PS_AUTHFAIL;
  273.     }
  274.     memcpy(session, credentials.session, sizeof session);
  275.     memset(&credentials, 0, sizeof credentials);
  276.     des_key_sched(&session, schedule);
  277.     result = krb_get_tf_fullname(TKT_FILE, tktuser, tktinst, tktrealm);
  278.     if (result) {
  279. report(stderr, "krb_get_tf_fullname: %sn", krb_get_err_text(result));
  280. return PS_AUTHFAIL;
  281.     }
  282.     if (strcmp(tktuser, user) != 0) {
  283. report(stderr, 
  284.        _("principal %s in ticket does not match -u %sn"), tktuser,
  285. user);
  286. return PS_AUTHFAIL;
  287.     }
  288.     if (tktinst[0]) {
  289. report(stderr, 
  290.        _("non-null instance (%s) might cause strange behaviorn"),
  291. tktinst);
  292. strcat(tktuser, ".");
  293. strcat(tktuser, tktinst);
  294.     }
  295.     if (strcmp(tktrealm, srvrealm) != 0) {
  296. strcat(tktuser, "@");
  297. strcat(tktuser, tktrealm);
  298.     }
  299.     result = krb_mk_req(&authenticator, "imap", srvinst, srvrealm,
  300.     challenge1.cint);
  301.     if (result) {
  302. report(stderr, "krb_mq_req: %sn", krb_get_err_text(result));
  303. return PS_AUTHFAIL;
  304.     }
  305.     to64frombits(buf1, authenticator.dat, authenticator.length);
  306.     if (outlevel >= O_MONITOR) {
  307. report(stdout, "IMAP> %sn", buf1);
  308.     }
  309.     strcat(buf1, "rn");
  310.     SockWrite(sock, buf1, strlen(buf1));
  311.     /* Upon decrypting and verifying the ticket and authenticator, the
  312.      * server should verify that the contained checksum field equals
  313.      * the original server provided random 32-bit number.  Should the
  314.      * verification be successful, the server must add one to the
  315.      * checksum and construct 8 octets of data, with the first four
  316.      * octets containing the incremented checksum in network byte
  317.      * order, the fifth octet containing a bit-mask specifying the
  318.      * protection mechanisms supported by the server, and the sixth
  319.      * through eighth octets containing, in network byte order, the
  320.      * maximum cipher-text buffer size the server is able to receive.
  321.      * The server must encrypt the 8 octets of data in the session key
  322.      * and issue that encrypted data in a second ready response.  The
  323.      * client should consider the server authenticated if the first
  324.      * four octets the un-encrypted data is equal to one plus the
  325.      * checksum it previously sent.
  326.      */
  327.     
  328.     if (result = gen_recv(sock, buf1, sizeof buf1))
  329. return result;
  330.     /* The client must construct data with the first four octets
  331.      * containing the original server-issued checksum in network byte
  332.      * order, the fifth octet containing the bit-mask specifying the
  333.      * selected protection mechanism, the sixth through eighth octets
  334.      * containing in network byte order the maximum cipher-text buffer
  335.      * size the client is able to receive, and the following octets
  336.      * containing a user name string.  The client must then append
  337.      * from one to eight octets so that the length of the data is a
  338.      * multiple of eight octets. The client must then PCBC encrypt the
  339.      * data with the session key and respond to the second ready
  340.      * response with the encrypted data.  The server decrypts the data
  341.      * and verifies the contained checksum.  The username field
  342.      * identifies the user for whom subsequent IMAP operations are to
  343.      * be performed; the server must verify that the principal
  344.      * identified in the Kerberos ticket is authorized to connect as
  345.      * that user.  After these verifications, the authentication
  346.      * process is complete.
  347.      */
  348.     len = from64tobits(buf2, buf1);
  349.     if (len < 0) {
  350. report(stderr, _("could not decode BASE64 ready responsen"));
  351. return PS_AUTHFAIL;
  352.     }
  353.     des_ecb_encrypt((des_cblock *)buf2, (des_cblock *)buf2, schedule, 0);
  354.     memcpy(challenge2.cstr, buf2, 4);
  355.     if (ntohl(challenge2.cint) != challenge1.cint + 1) {
  356. report(stderr, _("challenge mismatchn"));
  357. return PS_AUTHFAIL;
  358.     }     
  359.     memset(authenticator.dat, 0, sizeof authenticator.dat);
  360.     result = htonl(challenge1.cint);
  361.     memcpy(authenticator.dat, &result, sizeof result);
  362.     /* The protection mechanisms and their corresponding bit-masks are as
  363.      * follows:
  364.      *
  365.      * 1 No protection mechanism
  366.      * 2 Integrity (krb_mk_safe) protection
  367.      * 4 Privacy (krb_mk_priv) protection
  368.      */
  369.     authenticator.dat[4] = 1;
  370.     len = strlen(tktuser);
  371.     strncpy(authenticator.dat+8, tktuser, len);
  372.     authenticator.length = len + 8 + 1;
  373.     while (authenticator.length & 7) {
  374. authenticator.length++;
  375.     }
  376.     des_pcbc_encrypt((des_cblock *)authenticator.dat,
  377.     (des_cblock *)authenticator.dat, authenticator.length, schedule,
  378.     &session, 1);
  379.     to64frombits(buf1, authenticator.dat, authenticator.length);
  380.     if (outlevel >= O_MONITOR) {
  381. report(stdout, "IMAP> %sn", buf1);
  382.     }
  383.     strcat(buf1, "rn");
  384.     SockWrite(sock, buf1, strlen(buf1));
  385.     if (result = gen_recv(sock, buf1, sizeof buf1))
  386. return result;
  387.     if (strstr(buf1, "OK")) {
  388.         return PS_SUCCESS;
  389.     }
  390.     else {
  391. return PS_AUTHFAIL;
  392.     }
  393. }
  394. #endif /* KERBEROS_V4 */
  395. #ifdef GSSAPI
  396. #define GSSAUTH_P_NONE      1
  397. #define GSSAUTH_P_INTEGRITY 2
  398. #define GSSAUTH_P_PRIVACY   4
  399. static int do_gssauth(int sock, char *hostname, char *username)
  400. {
  401.     gss_buffer_desc request_buf, send_token;
  402.     gss_buffer_t sec_token;
  403.     gss_name_t target_name;
  404.     gss_ctx_id_t context;
  405.     gss_OID mech_name;
  406.     gss_qop_t quality;
  407.     int cflags;
  408.     OM_uint32 maj_stat, min_stat;
  409.     char buf1[8192], buf2[8192], server_conf_flags;
  410.     unsigned long buf_size;
  411.     int result;
  412.     /* first things first: get an imap ticket for host */
  413.     sprintf(buf1, "imap@%s", hostname);
  414.     request_buf.value = buf1;
  415.     request_buf.length = strlen(buf1) + 1;
  416.     maj_stat = gss_import_name(&min_stat, &request_buf, GSS_C_NT_HOSTBASED_SERVICE,
  417.         &target_name);
  418.     if (maj_stat != GSS_S_COMPLETE) {
  419.         report(stderr, _("Couldn't get service name for [%s]n"), buf1);
  420.         return PS_AUTHFAIL;
  421.     }
  422.     else if (outlevel >= O_DEBUG) {
  423.         maj_stat = gss_display_name(&min_stat, target_name, &request_buf,
  424.             &mech_name);
  425.         report(stderr, _("Using service name [%s]n"),request_buf.value);
  426.         maj_stat = gss_release_buffer(&min_stat, &request_buf);
  427.     }
  428.     gen_send(sock, "AUTHENTICATE GSSAPI");
  429.     /* upon receipt of the GSSAPI authentication request, server returns
  430.      * null data ready response. */
  431.     if (result = gen_recv(sock, buf1, sizeof buf1)) {
  432.         return result;
  433.     }
  434.     /* now start the security context initialisation loop... */
  435.     sec_token = GSS_C_NO_BUFFER;
  436.     context = GSS_C_NO_CONTEXT;
  437.     if (outlevel >= O_VERBOSE)
  438.         report(stdout, _("Sending credentialsn"));
  439.     do {
  440.         send_token.length = 0;
  441. send_token.value = NULL;
  442.         maj_stat = gss_init_sec_context(&min_stat, 
  443.         GSS_C_NO_CREDENTIAL,
  444.              &context, 
  445. target_name, 
  446. GSS_C_NO_OID, 
  447. GSS_C_MUTUAL_FLAG | GSS_C_SEQUENCE_FLAG, 
  448. 0, 
  449. GSS_C_NO_CHANNEL_BINDINGS, 
  450. sec_token, 
  451. NULL, 
  452. &send_token, 
  453. NULL, 
  454. NULL);
  455.         if (maj_stat!=GSS_S_COMPLETE && maj_stat!=GSS_S_CONTINUE_NEEDED) {
  456.             report(stderr, _("Error exchanging credentialsn"));
  457.             gss_release_name(&min_stat, &target_name);
  458.             /* wake up server and await NO response */
  459.             SockWrite(sock, "rn", 2);
  460.             if (result = gen_recv(sock, buf1, sizeof buf1))
  461.                 return result;
  462.             return PS_AUTHFAIL;
  463.         }
  464.         to64frombits(buf1, send_token.value, send_token.length);
  465.         gss_release_buffer(&min_stat, &send_token);
  466. strcat(buf1, "rn");
  467.         SockWrite(sock, buf1, strlen(buf1));
  468.         if (outlevel >= O_MONITOR)
  469.             report(stdout, "IMAP> %sn", buf1);
  470.         if (maj_stat == GSS_S_CONTINUE_NEEDED) {
  471.     if (result = gen_recv(sock, buf1, sizeof buf1)) {
  472.         gss_release_name(&min_stat, &target_name);
  473.         return result;
  474.     }
  475.     request_buf.length = from64tobits(buf2, buf1 + 2);
  476.     request_buf.value = buf2;
  477.     sec_token = &request_buf;
  478.         }
  479.     } while (maj_stat == GSS_S_CONTINUE_NEEDED);
  480.     gss_release_name(&min_stat, &target_name);
  481.     /* get security flags and buffer size */
  482.     if (result = gen_recv(sock, buf1, sizeof buf1)) {
  483.         return result;
  484.     }
  485.     request_buf.length = from64tobits(buf2, buf1 + 2);
  486.     request_buf.value = buf2;
  487.     maj_stat = gss_unwrap(&min_stat, context, &request_buf, &send_token,
  488.         &cflags, &quality);
  489.     if (maj_stat != GSS_S_COMPLETE) {
  490.         report(stderr, _("Couldn't unwrap security level datan"));
  491.         gss_release_buffer(&min_stat, &send_token);
  492.         return PS_AUTHFAIL;
  493.     }
  494.     if (outlevel >= O_DEBUG)
  495.         report(stdout, _("Credential exchange completen"));
  496.     /* first octet is security levels supported. We want none, for now */
  497.     server_conf_flags = ((char *)send_token.value)[0];
  498.     if ( !(((char *)send_token.value)[0] & GSSAUTH_P_NONE) ) {
  499.         report(stderr, _("Server requires integrity and/or privacyn"));
  500.         gss_release_buffer(&min_stat, &send_token);
  501.         return PS_AUTHFAIL;
  502.     }
  503.     ((char *)send_token.value)[0] = 0;
  504.     buf_size = ntohl(*((long *)send_token.value));
  505.     /* we don't care about buffer size if we don't wrap data */
  506.     gss_release_buffer(&min_stat, &send_token);
  507.     if (outlevel >= O_DEBUG) {
  508.         report(stdout, _("Unwrapped security level flags: %s%s%sn"),
  509.             server_conf_flags & GSSAUTH_P_NONE ? "N" : "-",
  510.             server_conf_flags & GSSAUTH_P_INTEGRITY ? "I" : "-",
  511.             server_conf_flags & GSSAUTH_P_PRIVACY ? "C" : "-");
  512.         report(stdout, _("Maximum GSS token size is %ldn"),buf_size);
  513.     }
  514.     /* now respond in kind (hack!!!) */
  515.     buf_size = htonl(buf_size); /* do as they do... only matters if we do enc */
  516.     memcpy(buf1, &buf_size, 4);
  517.     buf1[0] = GSSAUTH_P_NONE;
  518.     strcpy(buf1+4, username); /* server decides if princ is user */
  519.     request_buf.length = 4 + strlen(username) + 1;
  520.     request_buf.value = buf1;
  521.     maj_stat = gss_wrap(&min_stat, context, 0, GSS_C_QOP_DEFAULT, &request_buf,
  522.         &cflags, &send_token);
  523.     if (maj_stat != GSS_S_COMPLETE) {
  524.         report(stderr, _("Error creating security level requestn"));
  525.         return PS_AUTHFAIL;
  526.     }
  527.     to64frombits(buf1, send_token.value, send_token.length);
  528.     if (outlevel >= O_DEBUG) {
  529.         report(stdout, _("Requesting authorization as %sn"), username);
  530.         report(stdout, "IMAP> %sn",buf1);
  531.     }
  532.     strcat(buf1, "rn");
  533.     SockWrite(sock, buf1, strlen(buf1));
  534.     /* we should be done. Get status and finish up */
  535.     if (result = gen_recv(sock, buf1, sizeof buf1))
  536.         return result;
  537.     if (strstr(buf1, "OK")) {
  538.         /* flush security context */
  539.         if (outlevel >= O_DEBUG)
  540.             report(stdout, _("Releasing GSS credentialsn"));
  541.         maj_stat = gss_delete_sec_context(&min_stat, &context, &send_token);
  542.         if (maj_stat != GSS_S_COMPLETE) {
  543.             report(stderr, _("Error releasing credentialsn"));
  544.             return PS_AUTHFAIL;
  545.         }
  546.         /* send_token may contain a notification to the server to flush
  547.          * credentials. RFC 1731 doesn't specify what to do, and since this
  548.          * support is only for authentication, we'll assume the server
  549.          * knows enough to flush its own credentials */
  550.         gss_release_buffer(&min_stat, &send_token);
  551.         return PS_SUCCESS;
  552.     }
  553.     return PS_AUTHFAIL;
  554. }
  555. #endif /* GSSAPI */
  556. static void hmac_md5 (unsigned char *password,  size_t pass_len,
  557.                       unsigned char *challenge, size_t chal_len,
  558.                       unsigned char *response,  size_t resp_len)
  559. {
  560.     int i;
  561.     unsigned char ipad[64];
  562.     unsigned char opad[64];
  563.     unsigned char hash_passwd[16];
  564.     MD5_CTX ctx;
  565.     
  566.     if (resp_len != 16)
  567.         return;
  568.     if (pass_len > sizeof (ipad))
  569.     {
  570.         MD5Init (&ctx);
  571.         MD5Update (&ctx, password, pass_len);
  572.         MD5Final (hash_passwd, &ctx);
  573.         password = hash_passwd; pass_len = sizeof (hash_passwd);
  574.     }
  575.     memset (ipad, 0, sizeof (ipad));
  576.     memset (opad, 0, sizeof (opad));
  577.     memcpy (ipad, password, pass_len);
  578.     memcpy (opad, password, pass_len);
  579.     for (i=0; i<64; i++) {
  580.         ipad[i] ^= 0x36;
  581.         opad[i] ^= 0x5c;
  582.     }
  583.     MD5Init (&ctx);
  584.     MD5Update (&ctx, ipad, sizeof (ipad));
  585.     MD5Update (&ctx, challenge, chal_len);
  586.     MD5Final (response, &ctx);
  587.     MD5Init (&ctx);
  588.     MD5Update (&ctx, opad, sizeof (opad));
  589.     MD5Update (&ctx, response, resp_len);
  590.     MD5Final (response, &ctx);
  591. }
  592. #if NTLM_ENABLE
  593. #include "ntlm.h"
  594. static tSmbNtlmAuthRequest   request;    
  595. static tSmbNtlmAuthChallenge challenge;
  596. static tSmbNtlmAuthResponse  response;
  597. /*
  598.  * NTLM support by Grant Edwards.
  599.  *
  600.  * Handle MS-Exchange NTLM authentication method.  This is the same
  601.  * as the NTLM auth used by Samba for SMB related services. We just
  602.  * encode the packets in base64 instead of sending them out via a
  603.  * network interface.
  604.  * 
  605.  * Much source (ntlm.h, smb*.c smb*.h) was borrowed from Samba.
  606.  */
  607. static int do_imap_ntlm(int sock, struct query *ctl)
  608. {
  609.     char msgbuf[2048];
  610.     int result,len;
  611.   
  612.     gen_send(sock, "AUTHENTICATE NTLM");
  613.     if ((result = gen_recv(sock, msgbuf, sizeof msgbuf)))
  614. return result;
  615.   
  616.     if (msgbuf[0] != '+')
  617. return PS_AUTHFAIL;
  618.   
  619.     buildSmbNtlmAuthRequest(&request,ctl->remotename,NULL);
  620.     if (outlevel >= O_DEBUG)
  621. dumpSmbNtlmAuthRequest(stdout, &request);
  622.     memset(msgbuf,0,sizeof msgbuf);
  623.     to64frombits (msgbuf, (unsigned char*)&request, SmbLength(&request));
  624.   
  625.     if (outlevel >= O_MONITOR)
  626. report(stdout, "IMAP> %sn", msgbuf);
  627.   
  628.     strcat(msgbuf,"rn");
  629.     SockWrite (sock, msgbuf, strlen (msgbuf));
  630.     if ((gen_recv(sock, msgbuf, sizeof msgbuf)))
  631. return result;
  632.   
  633.     len = from64tobits ((unsigned char*)&challenge, msgbuf);
  634.     
  635.     if (outlevel >= O_DEBUG)
  636. dumpSmbNtlmAuthChallenge(stdout, &challenge);
  637.     
  638.     buildSmbNtlmAuthResponse(&challenge, &response,ctl->remotename,ctl->password);
  639.   
  640.     if (outlevel >= O_DEBUG)
  641. dumpSmbNtlmAuthResponse(stdout, &response);
  642.   
  643.     memset(msgbuf,0,sizeof msgbuf);
  644.     to64frombits (msgbuf, (unsigned char*)&response, SmbLength(&response));
  645.     if (outlevel >= O_MONITOR)
  646. report(stdout, "IMAP> %sn", msgbuf);
  647.       
  648.     strcat(msgbuf,"rn");
  649.     SockWrite (sock, msgbuf, strlen (msgbuf));
  650.   
  651.     if ((result = gen_recv (sock, msgbuf, sizeof msgbuf)))
  652. return result;
  653.   
  654.     if (strstr (msgbuf, "OK"))
  655. return PS_SUCCESS;
  656.     else
  657. return PS_AUTHFAIL;
  658. }
  659. #endif /* NTLM */
  660. static int do_cram_md5 (int sock, struct query *ctl)
  661. /* authenticate as per RFC2195 */
  662. {
  663.     int result;
  664.     int len;
  665.     unsigned char buf1[1024];
  666.     unsigned char msg_id[768];
  667.     unsigned char response[16];
  668.     unsigned char reply[1024];
  669.     gen_send (sock, "AUTHENTICATE CRAM-MD5");
  670.     /* From RFC2195:
  671.      * The data encoded in the first ready response contains an
  672.      * presumptively arbitrary string of random digits, a timestamp, and the
  673.      * fully-qualified primary host name of the server.  The syntax of the
  674.      * unencoded form must correspond to that of an RFC 822 'msg-id'
  675.      * [RFC822] as described in [POP3].
  676.      */
  677.     if ((result = gen_recv (sock, buf1, sizeof (buf1)))) {
  678. return result;
  679.     }
  680.     len = from64tobits (msg_id, buf1);
  681.     if (len < 0) {
  682. report (stderr, _("could not decode BASE64 challengen"));
  683. return PS_AUTHFAIL;
  684.     } else if (len < sizeof (msg_id)) {
  685.         msg_id[len] = 0;
  686.     } else {
  687.         msg_id[sizeof (msg_id)-1] = 0;
  688.     }
  689.     if (outlevel >= O_DEBUG) {
  690.         report (stdout, "decoded as %sn", msg_id);
  691.     }
  692.     /* The client makes note of the data and then responds with a string
  693.      * consisting of the user name, a space, and a 'digest'.  The latter is
  694.      * computed by applying the keyed MD5 algorithm from [KEYED-MD5] where
  695.      * the key is a shared secret and the digested text is the timestamp
  696.      * (including angle-brackets).
  697.      */
  698.     hmac_md5 (ctl->password, strlen (ctl->password),
  699.               msg_id, strlen (msg_id),
  700.               response, sizeof (response));
  701. #ifdef HAVE_SNPRINTF
  702.     snprintf (reply, sizeof (reply),
  703. #else
  704.     sprintf(reply,
  705. #endif
  706.               "%s %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", 
  707.               ctl->remotename,
  708.               response[0], response[1], response[2], response[3],
  709.               response[4], response[5], response[6], response[7],
  710.               response[8], response[9], response[10], response[11],
  711.               response[12], response[13], response[14], response[15]);
  712.     if (outlevel >= O_DEBUG) {
  713.         report (stdout, "replying with %sn", reply);
  714.     }
  715.     to64frombits (buf1, reply, strlen (reply));
  716.     if (outlevel >= O_MONITOR) {
  717. report (stdout, "IMAP> %sn", buf1);
  718.     }
  719.     /* PMDF5.2 IMAP has a bug that requires this to be a single write */
  720.     strcat (buf1, "rn");
  721.     SockWrite (sock, buf1, strlen (buf1));
  722.     if ((result = gen_recv (sock, buf1, sizeof (buf1))))
  723. return result;
  724.     if (strstr (buf1, "OK")) {
  725.         return PS_SUCCESS;
  726.     } else {
  727. return PS_AUTHFAIL;
  728.     }
  729. }
  730. int imap_canonicalize(char *result, char *raw, int maxlen)
  731. /* encode an IMAP password as per RFC1730's quoting conventions */
  732. {
  733.     int i, j;
  734.     j = 0;
  735.     for (i = 0; i < strlen(raw) && i < maxlen; i++)
  736.     {
  737. if ((raw[i] == '\') || (raw[i] == '"'))
  738.     result[j++] = '\';
  739. result[j++] = raw[i];
  740.     }
  741.     result[j] = '';
  742.     return(i);
  743. }
  744. int imap_getauth(int sock, struct query *ctl, char *greeting)
  745. /* apply for connection authorization */
  746. {
  747.     int ok = 0;
  748.     /* probe to see if we're running IMAP4 and can use RFC822.PEEK */
  749.     capabilities[0] = '';
  750.     if ((ok = gen_transact(sock, "CAPABILITY")) == PS_SUCCESS)
  751.     {
  752. /* UW-IMAP server 10.173 notifies in all caps */
  753. if (strstr(capabilities, "IMAP4REV1"))
  754. {
  755.     imap_version = IMAP4rev1;
  756.     if (outlevel >= O_DEBUG)
  757. report(stdout, _("Protocol identified as IMAP4 rev 1n"));
  758. }
  759. else
  760. {
  761.     imap_version = IMAP4;
  762.     if (outlevel >= O_DEBUG)
  763. report(stdout, _("Protocol identified as IMAP4 rev 0n"));
  764. }
  765.     }
  766.     else if (ok == PS_ERROR)
  767.     {
  768. imap_version = IMAP2;
  769. if (outlevel >= O_DEBUG)
  770.     report(stdout, _("Protocol identified as IMAP2 or IMAP2BISn"));
  771.     }
  772.     else
  773. return(ok);
  774.     peek_capable = (imap_version >= IMAP4);
  775.     /* 
  776.      * Assumption: expunges are cheap, so we want to do them
  777.      * after every message unless user said otherwise.
  778.      */
  779.     if (NUM_SPECIFIED(ctl->expunge))
  780. expunge_period = NUM_VALUE_OUT(ctl->expunge);
  781.     else
  782. expunge_period = 1;
  783.     /* 
  784.      * If either (a) we saw a PREAUTH token in the capability response, or
  785.      * (b) the user specified ssh preauthentication, then we're done.
  786.      */
  787.     if (preauth || ctl->server.preauthenticate == A_SSH)
  788. return(PS_SUCCESS);
  789. #if OPIE_ENABLE
  790.     if ((ctl->server.protocol == P_IMAP) && strstr(capabilities, "AUTH=X-OTP"))
  791.     {
  792. if (outlevel >= O_DEBUG)
  793.     report(stdout, _("OTP authentication is supportedn"));
  794. if (do_otp(sock, ctl) == PS_SUCCESS)
  795.     return(PS_SUCCESS);
  796.     };
  797. #endif /* OPIE_ENABLE */
  798. #ifdef GSSAPI
  799.     if (strstr(capabilities, "AUTH=GSSAPI"))
  800.     {
  801.         if (ctl->server.protocol == P_IMAP_GSS)
  802.         {
  803.             if (outlevel >= O_DEBUG)
  804.                 report(stdout, _("GSS authentication is supportedn"));
  805.             return do_gssauth(sock, ctl->server.truename, ctl->remotename);
  806.         }
  807.     }
  808.     else if (ctl->server.protocol == P_IMAP_GSS)
  809.     {
  810.         report(stderr, 
  811.        _("Required GSS capability not supported by servern"));
  812.         return(PS_AUTHFAIL);
  813.     }
  814. #endif /* GSSAPI */
  815. #ifdef KERBEROS_V4
  816.     if (strstr(capabilities, "AUTH=KERBEROS_V4"))
  817.     {
  818. if (outlevel >= O_DEBUG)
  819.     report(stdout, _("KERBEROS_V4 authentication is supportedn"));
  820. if (ctl->server.protocol == P_IMAP_K4)
  821. {
  822.     if ((ok = do_rfc1731(sock, ctl->server.truename)))
  823.     {
  824. if (outlevel >= O_MONITOR)
  825.     report(stdout, "IMAP> *n");
  826. SockWrite(sock, "*rn", 3);
  827.     }
  828.     
  829.     return(ok);
  830. }
  831. /* else fall through to ordinary AUTH=LOGIN case */
  832.     }
  833.     else if (ctl->server.protocol == P_IMAP_K4)
  834.     {
  835. report(stderr, 
  836.        _("Required KERBEROS_V4 capability not supported by servern"));
  837. return(PS_AUTHFAIL);
  838.     }
  839. #endif /* KERBEROS_V4 */
  840.     if (strstr(capabilities, "AUTH=CRAM-MD5"))
  841.     {
  842.         if (outlevel >= O_DEBUG)
  843.             report (stdout, _("CRAM-MD5 authentication is supportedn"));
  844.         if (ctl->server.protocol != P_IMAP_LOGIN)
  845.         {
  846.             if ((ok = do_cram_md5 (sock, ctl)))
  847.             {
  848.                 if (outlevel >= O_MONITOR)
  849.                     report (stdout, "IMAP> *n");
  850.                 SockWrite (sock, "*rn", 3);
  851.             }
  852.             return ok;
  853.         }
  854.     }
  855.     else if (ctl->server.protocol == P_IMAP_CRAM_MD5)
  856.     {
  857.         report(stderr,
  858.                _("Required CRAM-MD5 capability not supported by servern"));
  859.         return(PS_AUTHFAIL);
  860.     }
  861. #ifdef NTLM_ENABLE
  862.     if (strstr (capabilities, "AUTH=NTLM"))
  863.     {
  864.         if (outlevel >= O_DEBUG)
  865.             report (stdout, _("NTLM authentication is supportedn"));
  866.         return do_imap_ntlm (sock, ctl);
  867.     }
  868. #endif /* NTLM_ENABLE */
  869. #ifdef __UNUSED__ /* The Cyrus IMAP4rev1 server chokes on this */
  870.     /* this handles either AUTH=LOGIN or AUTH-LOGIN */
  871.     if ((imap_version >= IMAP4rev1) && (!strstr(capabilities, "LOGIN"))) {
  872.       report(stderr, 
  873.      _("Required LOGIN capability not supported by servern"));
  874.       return PS_AUTHFAIL;
  875.     };
  876. #endif /* __UNUSED__ */
  877.     {
  878. /* these sizes guarantee no buffer overflow */
  879. char remotename[NAMELEN*2+1], password[PASSWORDLEN*2+1];
  880. imap_canonicalize(remotename, ctl->remotename, NAMELEN);
  881. imap_canonicalize(password, ctl->password, PASSWORDLEN);
  882. ok = gen_transact(sock, "LOGIN "%s" "%s"", remotename, password);
  883.     }
  884.     if (ok)
  885. return(ok);
  886.     
  887.     return(PS_SUCCESS);
  888. }
  889. static int internal_expunge(int sock)
  890. /* ship an expunge, resetting associated counters */
  891. {
  892.     int ok;
  893.     if ((ok = gen_transact(sock, "EXPUNGE")))
  894. return(ok);
  895.     expunged += deletions;
  896.     deletions = 0;
  897. #ifdef IMAP_UID /* not used */
  898.     expunge_uids(ctl);
  899. #endif /* IMAP_UID */
  900.     return(PS_SUCCESS);
  901. }
  902. static int imap_getrange(int sock, 
  903.  struct query *ctl, 
  904.  const char *folder, 
  905.  int *countp, int *newp, int *bytes)
  906. /* get range of messages to be fetched */
  907. {
  908.     int ok;
  909.     /* find out how many messages are waiting */
  910.     *bytes = recent = unseen = -1;
  911.     if (pass > 1)
  912.     {
  913. /* 
  914.  * We have to have an expunge here, otherwise the re-poll will
  915.  * infinite-loop picking up un-expunged messages -- unless the
  916.  * expunge period is one and we've been nuking each message 
  917.  * just after deletion.
  918.  */
  919. ok = 0;
  920. if (deletions && expunge_period != 1)
  921.     internal_expunge(sock);
  922. count = -1;
  923. if (ok || gen_transact(sock, "NOOP"))
  924. {
  925.     report(stderr, _("re-poll failedn"));
  926.     return(ok);
  927. }
  928. else if (count == -1) /* no EXISTS response to NOOP */
  929. {
  930.     count = recent = 0;
  931.     unseen = -1;
  932. }
  933.     }
  934.     else
  935.     {
  936. if (!check_only)
  937.     ok = gen_transact(sock, "SELECT %s", folder ? folder : "INBOX");
  938. else
  939.     ok = gen_transact(sock, "EXAMINE %s", folder ? folder : "INBOX");
  940. if (ok != 0)
  941. {
  942.     report(stderr, _("mailbox selection failedn"));
  943.     return(ok);
  944. }
  945.     }
  946.     *countp = count;
  947.     /*
  948.      * Note: because IMAP has an is_old method, this number is used
  949.      * only for the "X messages (Y unseen)" notification.  Accordingly
  950.      * it doesn't matter much that it can be wrong (e.g. if we see an
  951.      * UNSEEN response but not all messages above the first UNSEEN one
  952.      * are likewise).
  953.      */
  954.     if (unseen >= 0) /* optional, but better if we see it */
  955. *newp = count - unseen + 1;
  956.     else if (recent >= 0) /* mandatory */
  957. *newp = recent;
  958.     else
  959. *newp = -1; /* should never happen, RECENT is mandatory */ 
  960.     expunged = 0;
  961.     return(PS_SUCCESS);
  962. }
  963. static int imap_getsizes(int sock, int count, int *sizes)
  964. /* capture the sizes of all messages */
  965. {
  966.     char buf [MSGBUFSIZE+1];
  967.     /*
  968.      * Some servers (as in, PMDF5.1-9.1 under OpenVMS 6.1)
  969.      * won't accept 1:1 as valid set syntax.  Some implementors
  970.      * should be taken out and shot for excessive anality.
  971.      */
  972.     if (count == 1)
  973. gen_send(sock, "FETCH 1 RFC822.SIZE", count);
  974.     else
  975. gen_send(sock, "FETCH 1:%d RFC822.SIZE", count);
  976.     for (;;)
  977.     {
  978. int num, size, ok;
  979. if ((ok = gen_recv(sock, buf, sizeof(buf))))
  980.     return(ok);
  981. if (strstr(buf, "OK"))
  982.     break;
  983. else if (sscanf(buf, "* %d FETCH (RFC822.SIZE %d)", &num, &size) == 2)
  984.     sizes[num - 1] = size;
  985.     }
  986.     return(PS_SUCCESS);
  987. }
  988. static int imap_is_old(int sock, struct query *ctl, int number)
  989. /* is the given message old? */
  990. {
  991.     int ok;
  992.     /* expunges change the fetch numbers */
  993.     number -= expunged;
  994.     if ((ok = gen_transact(sock, "FETCH %d FLAGS", number)) != 0)
  995. return(PS_ERROR);
  996.     return(seen);
  997. }
  998. static int imap_fetch_headers(int sock, struct query *ctl,int number,int *lenp)
  999. /* request headers of nth message */
  1000. {
  1001.     char buf [MSGBUFSIZE+1];
  1002.     int num;
  1003.     /* expunges change the fetch numbers */
  1004.     number -= expunged;
  1005.     /*
  1006.      * This is blessed by RFC1176, RFC1730, RFC2060.
  1007.      * According to the RFCs, it should *not* set the Seen flag.
  1008.      */
  1009.     gen_send(sock, "FETCH %d RFC822.HEADER", number);
  1010.     /* looking for FETCH response */
  1011.     do {
  1012. int ok;
  1013. if ((ok = gen_recv(sock, buf, sizeof(buf))))
  1014.     return(ok);
  1015.     } while
  1016. (sscanf(buf+2, "%d FETCH (%*s {%d}", &num, lenp) != 2);
  1017.     if (num != number)
  1018. return(PS_ERROR);
  1019.     else
  1020. return(PS_SUCCESS);
  1021. }
  1022. static int imap_fetch_body(int sock, struct query *ctl, int number, int *lenp)
  1023. /* request body of nth message */
  1024. {
  1025.     char buf [MSGBUFSIZE+1], *cp;
  1026.     int num;
  1027.     /* expunges change the fetch numbers */
  1028.     number -= expunged;
  1029.     /*
  1030.      * If we're using IMAP4, we can fetch the message without setting its
  1031.      * seen flag.  This is good!  It means that if the protocol exchange
  1032.      * craps out during the message, it will still be marked `unseen' on
  1033.      * the server.
  1034.      *
  1035.      * However...*don't* do this if we're using keep to suppress deletion!
  1036.      * In that case, marking the seen flag is the only way to prevent the
  1037.      * message from being re-fetched on subsequent runs (and according
  1038.      * to RFC2060 p.43 this fetch should set Seen as a side effect).
  1039.      */
  1040.     switch (imap_version)
  1041.     {
  1042.     case IMAP4rev1: /* RFC 2060 */
  1043. if (!ctl->keep)
  1044.     gen_send(sock, "FETCH %d BODY.PEEK[TEXT]", number);
  1045. else
  1046.     gen_send(sock, "FETCH %d BODY[TEXT]", number);
  1047. break;
  1048.     case IMAP4: /* RFC 1730 */
  1049. if (!ctl->keep)
  1050.     gen_send(sock, "FETCH %d RFC822.TEXT.PEEK", number);
  1051. else
  1052.     gen_send(sock, "FETCH %d RFC822.TEXT", number);
  1053. break;
  1054.     default: /* RFC 1176 */
  1055. gen_send(sock, "FETCH %d RFC822.TEXT", number);
  1056. break;
  1057.     }
  1058.     /* looking for FETCH response */
  1059.     do {
  1060. int ok;
  1061. if ((ok = gen_recv(sock, buf, sizeof(buf))))
  1062.     return(ok);
  1063.     } while
  1064. (!strstr(buf+4, "FETCH") || sscanf(buf+2, "%d", &num) != 1);
  1065.     if (num != number)
  1066. return(PS_ERROR);
  1067.     /*
  1068.      * Try to extract a length from the FETCH response.  RFC2060 requires
  1069.      * it to be present, but at least one IMAP server (Novell GroupWise)
  1070.      * botches this.
  1071.      */
  1072.     if ((cp = strchr(buf, '{')))
  1073. *lenp = atoi(cp + 1);
  1074.     else
  1075. *lenp = -1; /* missing length part in FETCH reponse */
  1076.     return(PS_SUCCESS);
  1077. }
  1078. static int imap_trail(int sock, struct query *ctl, int number)
  1079. /* discard tail of FETCH response after reading message text */
  1080. {
  1081.     /* expunges change the fetch numbers */
  1082.     /* number -= expunged; */
  1083.     for (;;)
  1084.     {
  1085. char buf[MSGBUFSIZE+1];
  1086. int ok;
  1087. if ((ok = gen_recv(sock, buf, sizeof(buf))))
  1088.     return(ok);
  1089. /* UW IMAP returns "OK FETCH", Cyrus returns "OK Completed" */
  1090. if (strstr(buf, "OK"))
  1091.     break;
  1092. #ifdef __UNUSED__
  1093. /*
  1094.  * Any IMAP server that fails to set Seen on a BODY[TEXT]
  1095.  * fetch violates RFC2060 p.43 (top).  This becomes an issue
  1096.  * when keep is on, because seen messages aren't deleted and
  1097.  * get refetched on each poll.  As a workaround, if keep is on
  1098.  * we can set the Seen flag explicitly.
  1099.  *
  1100.  * This code isn't used yet because we don't know of any IMAP
  1101.  * servers broken in this way.
  1102.  */
  1103. if (ctl->keep)
  1104.     if ((ok = gen_transact(sock,
  1105. imap_version == IMAP4 
  1106. ? "STORE %d +FLAGS.SILENT (\Seen)"
  1107. : "STORE %d +FLAGS (\Seen)", 
  1108. number)))
  1109. return(ok);
  1110. #endif /* __UNUSED__ */
  1111.     }
  1112.     return(PS_SUCCESS);
  1113. }
  1114. static int imap_delete(int sock, struct query *ctl, int number)
  1115. /* set delete flag for given message */
  1116. {
  1117.     int ok;
  1118.     /* expunges change the fetch numbers */
  1119.     number -= expunged;
  1120.     /*
  1121.      * Use SILENT if possible as a minor throughput optimization.
  1122.      * Note: this has been dropped from IMAP4rev1.
  1123.      *
  1124.      * We set Seen because there are some IMAP servers (notably HP
  1125.      * OpenMail) that do message-receipt DSNs, but only when the seen
  1126.      * bit is set.  This is the appropriate time -- we get here right
  1127.      * after the local SMTP response that says delivery was
  1128.      * successful.
  1129.      */
  1130.     if ((ok = gen_transact(sock,
  1131. imap_version == IMAP4 
  1132. ? "STORE %d +FLAGS.SILENT (\Seen \Deleted)"
  1133. : "STORE %d +FLAGS (\Seen \Deleted)", 
  1134. number)))
  1135. return(ok);
  1136.     else
  1137. deletions++;
  1138.     /*
  1139.      * We do an expunge after expunge_period messages, rather than
  1140.      * just before quit, so that a line hit during a long session
  1141.      * won't result in lots of messages being fetched again during
  1142.      * the next session.
  1143.      */
  1144.     if (NUM_NONZERO(expunge_period) && (deletions % expunge_period) == 0)
  1145. internal_expunge(sock);
  1146.     return(PS_SUCCESS);
  1147. }
  1148. static int imap_logout(int sock, struct query *ctl)
  1149. /* send logout command */
  1150. {
  1151.     /* if any un-expunged deletions remain, ship an expunge now */
  1152.     if (deletions)
  1153. internal_expunge(sock);
  1154.     return(gen_transact(sock, "LOGOUT"));
  1155. }
  1156. const static struct method imap =
  1157. {
  1158.     "IMAP", /* Internet Message Access Protocol */
  1159. #if INET6_ENABLE
  1160.     "imap",
  1161.     "imaps",
  1162. #else /* INET6_ENABLE */
  1163.     143,                /* standard IMAP2bis/IMAP4 port */
  1164.     993,                /* ssl IMAP2bis/IMAP4 port */
  1165. #endif /* INET6_ENABLE */
  1166.     TRUE, /* this is a tagged protocol */
  1167.     FALSE, /* no message delimiter */
  1168.     imap_ok, /* parse command response */
  1169.     imap_canonicalize, /* deal with embedded slashes and spaces */
  1170.     imap_getauth, /* get authorization */
  1171.     imap_getrange, /* query range of messages */
  1172.     imap_getsizes, /* get sizes of messages (used for ESMTP SIZE option) */
  1173.     imap_is_old, /* no UID check */
  1174.     imap_fetch_headers, /* request given message headers */
  1175.     imap_fetch_body, /* request given message body */
  1176.     imap_trail, /* eat message trailer */
  1177.     imap_delete, /* delete the message */
  1178.     imap_logout, /* expunge and exit */
  1179.     TRUE, /* yes, we can re-poll */
  1180. };
  1181. int doIMAP(struct query *ctl)
  1182. /* retrieve messages using IMAP Version 2bis or Version 4 */
  1183. {
  1184.     return(do_protocol(ctl, &imap));
  1185. }
  1186. /* imap.c ends here */