]> Pileus Git - ~andy/fetchmail/blob - imap.c
Drop back to using SockGets/SockWrite.
[~andy/fetchmail] / imap.c
1 /*
2  * imap.c -- IMAP2bis/IMAP4 protocol methods
3  *
4  * Copyright 1996 by Eric S. Raymond
5  * All rights reserved.
6  * For license terms, see the file COPYING in this directory.
7  */
8
9 #include  <config.h>
10 #include  <stdio.h>
11 #include  <string.h>
12 #include  <ctype.h>
13 #if defined(STDC_HEADERS)
14 #include  <stdlib.h>
15 #endif
16 #include  "fetchmail.h"
17 #include  "socket.h"
18
19 static int count, seen, recent, unseen, imap4;
20
21 int imap_ok (FILE *sockfp,  char *argbuf)
22 /* parse command response */
23 {
24     char buf [POPBUFSIZE+1];
25
26     seen = 0;
27     do {
28         if (!SockGets(buf, sizeof(buf), sockfp))
29             return(PS_SOCKET);
30         if (buf[strlen(buf)-1] == '\n')
31             buf[strlen(buf)-1] = '\0';
32         if (buf[strlen(buf)-1] == '\r')
33             buf[strlen(buf)-1] = '\r';
34
35         if (outlevel == O_VERBOSE)
36             error(0, 0, "IMAP< %s", buf);
37
38         /* interpret untagged status responses */
39         if (strstr(buf, "EXISTS"))
40             count = atoi(buf+2);
41         if (strstr(buf, "RECENT"))
42             recent = atoi(buf+2);
43         if (strstr(buf, "UNSEEN"))
44             unseen = atoi(buf+2);
45         if (strstr(buf, "FLAGS"))
46             seen = (strstr(buf, "Seen") != (char *)NULL);
47     } while
48         (tag[0] != '\0' && strncmp(buf, tag, strlen(tag)));
49
50     if (tag[0] == '\0')
51     {
52         strcpy(argbuf, buf);
53         return(0); 
54     }
55     else
56     {
57         char    *cp;
58
59         /* skip the tag */
60         for (cp = buf; !isspace(*cp); cp++)
61             continue;
62         while (isspace(*cp))
63             cp++;
64
65         if (strncmp(cp, "OK", 2) == 0)
66         {
67             strcpy(argbuf, cp);
68             return(0);
69         }
70         else if (strncmp(cp, "BAD", 2) == 0)
71             return(PS_ERROR);
72         else
73             return(PS_PROTOCOL);
74     }
75 }
76
77 int imap_getauth(FILE *sockfp, struct query *ctl, char *buf)
78 /* apply for connection authorization */
79 {
80     /* try to get authorized */
81     int ok = gen_transact(sockfp,
82                   "LOGIN %s \"%s\"",
83                   ctl->remotename, ctl->password);
84
85     if (ok)
86         return(ok);
87
88     /* probe to see if we're running IMAP4 and can use RFC822.PEEK */
89     imap4 = ((gen_transact(sockfp, "CAPABILITY")) == 0);
90
91     return(0);
92 }
93
94 static int imap_getrange(FILE *sockfp, struct query *ctl, int*countp, int*newp)
95 /* get range of messages to be fetched */
96 {
97     int ok;
98
99     /* find out how many messages are waiting */
100     recent = unseen = 0;
101     ok = gen_transact(sockfp,
102                   "SELECT %s",
103                   ctl->mailbox[0] ? ctl->mailbox : "INBOX");
104     if (ok != 0)
105         return(ok);
106
107     *countp = count;
108
109     if (unseen)         /* optional response, but better if we see it */
110         *newp = unseen;
111     else if (recent)    /* mandatory */
112         *newp = recent;
113     else
114         *newp = -1;     /* should never happen, RECENT is mandatory */ 
115
116     return(0);
117 }
118
119 static int imap_getsizes(FILE *sockfp, int count, int *sizes)
120 /* capture the sizes of all messages */
121 {
122     char buf [POPBUFSIZE+1];
123
124     gen_send(sockfp, "FETCH 1:%d RFC822.SIZE", count);
125     while (SockGets(buf, sizeof(buf), sockfp))
126     {
127         int num, size;
128
129         if (buf[strlen(buf)-1] == '\n')
130             buf[strlen(buf)-1] = '\0';
131         if (buf[strlen(buf)-1] == '\r')
132             buf[strlen(buf)-1] = '\r';
133         if (outlevel == O_VERBOSE)
134             error(0, 0, "IMAP< %s", buf);
135         if (strstr(buf, "OK"))
136             break;
137         else if (sscanf(buf, "* %d FETCH (RFC822.SIZE %d)", &num, &size) == 2)
138             sizes[num - 1] = size;
139         else
140             sizes[num - 1] = -1;
141     }
142
143     return(0);
144 }
145
146 static int imap_is_old(FILE *sockfp, struct query *ctl, int num)
147 /* is the given message old? */
148 {
149     int ok;
150
151     if ((ok = gen_transact(sockfp, "FETCH %d FLAGS", num)) != 0)
152         return(PS_ERROR);
153
154     return(seen);
155 }
156
157 static int imap_fetch(FILE *sockfp, int number, int *lenp)
158 /* request nth message */
159 {
160     char buf [POPBUFSIZE+1];
161     int num;
162
163     /*
164      * If we're using IMAP4, we can fetch the message without setting its
165      * seen flag.  This is good!  It means that if the protocol exchange
166      * craps out during the message, it will still be marked `unseen' on
167      * the server.
168      */
169     if (imap4)
170         gen_send(sockfp, "FETCH %d RFC822.PEEK", number);
171     else
172         gen_send(sockfp, "FETCH %d RFC822", number);
173
174     /* looking for FETCH response */
175     do {
176         if (!SockGets(buf, sizeof(buf), sockfp))
177             return(PS_SOCKET);
178     } while
179             (sscanf(buf+2, "%d FETCH (RFC822 {%d}", &num, lenp) != 2);
180
181     if (num != number)
182         return(PS_ERROR);
183     else
184         return(0);
185 }
186
187 static int imap_trail(FILE *sockfp, struct query *ctl, int number)
188 /* discard tail of FETCH response after reading message text */
189 {
190     char buf [POPBUFSIZE+1];
191
192     if (!SockGets(buf, sizeof(buf), sockfp))
193         return(PS_SOCKET);
194     else
195         return(0);
196 }
197
198 static int imap_delete(FILE *sockfp, struct query *ctl, int number)
199 /* set delete flag for given message */
200 {
201     /* use SILENT if possible as a minor throughput optimization */
202     return(gen_transact(sockfp,
203                         imap4 
204                                 ? "STORE %d +FLAGS.SILENT (\\Deleted)"
205                                 : "STORE %d +FLAGS (\\Deleted)", 
206                         number));
207 }
208
209 const static struct method imap =
210 {
211     "IMAP",             /* Internet Message Access Protocol */
212     143,                /* standard IMAP2bis/IMAP4 port */
213     1,                  /* this is a tagged protocol */
214     0,                  /* no message delimiter */
215     imap_ok,            /* parse command response */
216     imap_getauth,       /* get authorization */
217     imap_getrange,      /* query range of messages */
218     imap_getsizes,      /* grab message sizes */
219     imap_is_old,        /* no UID check */
220     imap_fetch,         /* request given message */
221     imap_trail,         /* eat message trailer */
222     imap_delete,        /* set IMAP delete flag */
223     "EXPUNGE",          /* the IMAP expunge command */
224     "LOGOUT",           /* the IMAP exit command */
225 };
226
227 int doIMAP(struct query *ctl)
228 /* retrieve messages using IMAP Version 2bis or Version 4 */
229 {
230     return(do_protocol(ctl, &imap));
231 }
232
233 /* imap.c ends here */