Begin rewriting pop3 mailbox support. Several bugfixes.
* examples/pop3client.c (com_capa): Call mu_pop3_capa_test. (com_stat): Count is size_t. * include/mailutils/opool.h (mu_opool_copy): New proto. * mailbox/opool.c (mu_opool_copy): New function. * mailbox/xscript-stream.c (_xscript_ctl) <MU_IOCTL_SWAP_STREAM>: Avoid coredumping if sp->transport is NULL. * include/mailutils/pop3.h (pop3_capa_test): Rename to mu_pop3_capa_test. (mu_pop3_stat): Third argument is a pointer to mu_off_t. * libproto/pop/pop3_capatst.c (pop3_capa_test): Rename to mu_pop3_capa_test. * libproto/pop/pop3_stat.c (mu_pop3_stat): Third argument is a pointer to mu_off_t. * libproto/pop/Makefile.am (libmu_pop_la_SOURCES): Put back folder.c, url.c and mbox.c. * libproto/pop/mbox.c: Begin rewriting.
Showing
9 changed files
with
498 additions
and
2106 deletions
... | @@ -502,7 +502,7 @@ com_capa (int argc, char **argv) | ... | @@ -502,7 +502,7 @@ com_capa (int argc, char **argv) |
502 | for (; i < argc; i++) | 502 | for (; i < argc; i++) |
503 | { | 503 | { |
504 | const char *elt; | 504 | const char *elt; |
505 | int rc = pop3_capa_test (pop3, argv[i], &elt); | 505 | int rc = mu_pop3_capa_test (pop3, argv[i], &elt); |
506 | switch (rc) | 506 | switch (rc) |
507 | { | 507 | { |
508 | case 0: | 508 | case 0: |
... | @@ -677,7 +677,7 @@ com_pass (int argc, char **argv) | ... | @@ -677,7 +677,7 @@ com_pass (int argc, char **argv) |
677 | int | 677 | int |
678 | com_stat (int argc MU_ARG_UNUSED, char **argv MU_ARG_UNUSED) | 678 | com_stat (int argc MU_ARG_UNUSED, char **argv MU_ARG_UNUSED) |
679 | { | 679 | { |
680 | unsigned count = 0; | 680 | size_t count = 0; |
681 | size_t size = 0; | 681 | size_t size = 0; |
682 | int status = 0; | 682 | int status = 0; |
683 | 683 | ... | ... |
... | @@ -62,6 +62,10 @@ size_t mu_opool_size (mu_opool_t opool); | ... | @@ -62,6 +62,10 @@ size_t mu_opool_size (mu_opool_t opool); |
62 | mu_opool_create, above). */ | 62 | mu_opool_create, above). */ |
63 | int mu_opool_coalesce (mu_opool_t opool, size_t *psize); | 63 | int mu_opool_coalesce (mu_opool_t opool, size_t *psize); |
64 | 64 | ||
65 | /* Copy at most SIZE bytes of collected data into BUF. Return the | ||
66 | actual number of bytes copied. */ | ||
67 | size_t mu_opool_copy (mu_opool_t opool, void *buf, size_t size); | ||
68 | |||
65 | /* Return the pointer to the current object head chunk. If mu_opool_coalesce | 69 | /* Return the pointer to the current object head chunk. If mu_opool_coalesce |
66 | was called before, the returned value points to the entire object. | 70 | was called before, the returned value points to the entire object. |
67 | If PSIZE is not NULL, store the size of the head chunk to *PSIZE. */ | 71 | If PSIZE is not NULL, store the size of the head chunk to *PSIZE. */ | ... | ... |
... | @@ -60,7 +60,7 @@ int mu_pop3_stls (mu_pop3_t pop3); | ... | @@ -60,7 +60,7 @@ int mu_pop3_stls (mu_pop3_t pop3); |
60 | "const char *", no processing is done on the item except the removal of | 60 | "const char *", no processing is done on the item except the removal of |
61 | the trailing newline. */ | 61 | the trailing newline. */ |
62 | int mu_pop3_capa (mu_pop3_t pop3, int reread, mu_iterator_t *piter); | 62 | int mu_pop3_capa (mu_pop3_t pop3, int reread, mu_iterator_t *piter); |
63 | int pop3_capa_test (mu_pop3_t pop3, const char *name, const char **pret); | 63 | int mu_pop3_capa_test (mu_pop3_t pop3, const char *name, const char **pret); |
64 | 64 | ||
65 | int mu_pop3_dele (mu_pop3_t pop3, unsigned int mesgno); | 65 | int mu_pop3_dele (mu_pop3_t pop3, unsigned int mesgno); |
66 | 66 | ||
... | @@ -89,7 +89,7 @@ int mu_pop3_retr (mu_pop3_t pop3, unsigned int mesgno, | ... | @@ -89,7 +89,7 @@ int mu_pop3_retr (mu_pop3_t pop3, unsigned int mesgno, |
89 | 89 | ||
90 | int mu_pop3_rset (mu_pop3_t pop3); | 90 | int mu_pop3_rset (mu_pop3_t pop3); |
91 | 91 | ||
92 | int mu_pop3_stat (mu_pop3_t pop3, unsigned int *count, size_t *octets); | 92 | int mu_pop3_stat (mu_pop3_t pop3, size_t *count, mu_off_t *octets); |
93 | 93 | ||
94 | /* A stream is returned with the multi-line answer. It is the responsability | 94 | /* A stream is returned with the multi-line answer. It is the responsability |
95 | of the caller to call mu_stream_destroy() to dipose of the stream. */ | 95 | of the caller to call mu_stream_destroy() to dipose of the stream. */ | ... | ... |
... | @@ -24,10 +24,10 @@ lib_LTLIBRARIES = libmu_pop.la | ... | @@ -24,10 +24,10 @@ lib_LTLIBRARIES = libmu_pop.la |
24 | libmu_pop_la_LDFLAGS=-version-info @VI_CURRENT@:@VI_REVISION@:@VI_AGE@ | 24 | libmu_pop_la_LDFLAGS=-version-info @VI_CURRENT@:@VI_REVISION@:@VI_AGE@ |
25 | libmu_pop_la_LIBADD = ${MU_LIB_AUTH} ${MU_LIB_MAILUTILS} @INTLLIBS@ | 25 | libmu_pop_la_LIBADD = ${MU_LIB_AUTH} ${MU_LIB_MAILUTILS} @INTLLIBS@ |
26 | 26 | ||
27 | # folder.c\ | ||
28 | # url.c\ | ||
29 | # mbox.c | ||
30 | libmu_pop_la_SOURCES = \ | 27 | libmu_pop_la_SOURCES = \ |
28 | mbox.c \ | ||
29 | folder.c\ | ||
30 | url.c\ | ||
31 | \ | 31 | \ |
32 | pop3_apop.c \ | 32 | pop3_apop.c \ |
33 | pop3_capa.c \ | 33 | pop3_capa.c \ | ... | ... |
... | @@ -23,21 +23,13 @@ | ... | @@ -23,21 +23,13 @@ |
23 | 23 | ||
24 | #ifdef ENABLE_POP | 24 | #ifdef ENABLE_POP |
25 | 25 | ||
26 | #include <termios.h> | ||
27 | #include <errno.h> | ||
28 | #include <stdlib.h> | 26 | #include <stdlib.h> |
29 | #include <stdio.h> | ||
30 | #include <string.h> | 27 | #include <string.h> |
31 | #include <sys/time.h> | 28 | #include <sys/time.h> |
32 | #include <sys/types.h> | 29 | #include <sys/types.h> |
33 | #include <unistd.h> | 30 | #include <unistd.h> |
34 | #include <fcntl.h> | ||
35 | #include <stdarg.h> | ||
36 | |||
37 | #ifdef HAVE_STRINGS_H | ||
38 | # include <strings.h> | ||
39 | #endif | ||
40 | 31 | ||
32 | #include <mailutils/pop3.h> | ||
41 | #include <mailutils/attribute.h> | 33 | #include <mailutils/attribute.h> |
42 | #include <mailutils/auth.h> | 34 | #include <mailutils/auth.h> |
43 | #include <mailutils/body.h> | 35 | #include <mailutils/body.h> |
... | @@ -57,1509 +49,389 @@ | ... | @@ -57,1509 +49,389 @@ |
57 | #include <mailutils/mutil.h> | 49 | #include <mailutils/mutil.h> |
58 | #include <mailutils/cstr.h> | 50 | #include <mailutils/cstr.h> |
59 | #include <mailutils/cctype.h> | 51 | #include <mailutils/cctype.h> |
52 | #include <mailutils/opool.h> | ||
60 | 53 | ||
61 | #include <mailutils/sys/folder.h> | 54 | #include <mailutils/sys/folder.h> |
62 | #include <mailutils/sys/mailbox.h> | 55 | #include <mailutils/sys/mailbox.h> |
63 | #include <mailutils/sys/registrar.h> | 56 | #include <mailutils/sys/registrar.h> |
64 | #include <mailutils/sys/url.h> | 57 | #include <mailutils/sys/url.h> |
65 | 58 | ||
66 | #define PROP_RFC822 1 | 59 | #define _POP3_MSG_INBODY 0x01 |
67 | 60 | #define _POP3_MSG_SKIPHDR 0x02 | |
68 | /* Advance declarations. */ | 61 | #define _POP3_MSG_SKIPBDY 0x04 |
69 | struct _pop_data; | ||
70 | struct _pop_message; | ||
71 | |||
72 | typedef struct _pop_data * pop_data_t; | ||
73 | typedef struct _pop_message * pop_message_t; | ||
74 | 62 | ||
75 | /* The different possible states of a Pop client, Note that POP3 is not | 63 | struct _pop3_message |
76 | reentrant i.e. it is only one channel, so it is not possible to start | ||
77 | Another operation while one is running. The only resort is to close the | ||
78 | connection and reopen it again. This is what we do, the downside is that | ||
79 | the client as to get the authentication again user/pass. */ | ||
80 | enum pop_state | ||
81 | { | 64 | { |
82 | POP_NO_STATE, POP_STATE_DONE, | 65 | int flags; |
83 | POP_OPEN_CONNECTION, | ||
84 | POP_GREETINGS, | ||
85 | POP_CAPA, POP_CAPA_ACK, | ||
86 | POP_APOP, POP_APOP_ACK, | ||
87 | POP_DELE, POP_DELE_ACK, | ||
88 | POP_LIST, POP_LIST_ACK, POP_LIST_RX, | ||
89 | POP_QUIT, POP_QUIT_ACK, | ||
90 | POP_NOOP, POP_NOOP_ACK, | ||
91 | POP_RETR, POP_RETR_ACK, POP_RETR_RX_HDR, POP_RETR_RX_BODY, | ||
92 | POP_RSET, POP_RSET_ACK, | ||
93 | POP_STAT, POP_STAT_ACK, | ||
94 | POP_STLS, POP_STLS_ACK, | ||
95 | POP_TOP, POP_TOP_ACK, POP_TOP_RX, | ||
96 | POP_UIDL, POP_UIDL_ACK, | ||
97 | POP_AUTH, POP_AUTH_DONE, | ||
98 | POP_AUTH_USER, POP_AUTH_USER_ACK, | ||
99 | POP_AUTH_PASS, POP_AUTH_PASS_ACK | ||
100 | }; | ||
101 | |||
102 | /* POP3 capabilities */ | ||
103 | #define CAPA_TOP 0x00000001 | ||
104 | #define CAPA_USER 0x00000002 | ||
105 | #define CAPA_UIDL 0x00000004 | ||
106 | #define CAPA_RESP_CODES 0x00000008 | ||
107 | #define CAPA_LOGIN_DELAY 0x00000010 | ||
108 | #define CAPA_PIPELINING 0x00000020 | ||
109 | #define CAPA_EXPIRE 0x00000040 | ||
110 | #define CAPA_SASL 0x00000080 | ||
111 | #define CAPA_STLS 0x00000100 | ||
112 | #define CAPA_IMPLEMENTATION 0x00000200 | ||
113 | |||
114 | static void pop_destroy (mu_mailbox_t); | ||
115 | static int pop_capa (mu_mailbox_t); | ||
116 | static int pop_stls (mu_mailbox_t); | ||
117 | |||
118 | /* Functions/Methods that implements the mu_mailbox_t API. */ | ||
119 | static int pop_open (mu_mailbox_t, int); | ||
120 | static int pop_close (mu_mailbox_t); | ||
121 | static int pop_get_message (mu_mailbox_t, size_t, mu_message_t *); | ||
122 | static int pop_messages_count (mu_mailbox_t, size_t *); | ||
123 | static int pop_messages_recent (mu_mailbox_t, size_t *); | ||
124 | static int pop_message_unseen (mu_mailbox_t, size_t *); | ||
125 | static int pop_expunge (mu_mailbox_t); | ||
126 | static int pop_scan (mu_mailbox_t, size_t, size_t *); | ||
127 | static int pop_is_updated (mu_mailbox_t); | ||
128 | |||
129 | /* The implementation of mu_message_t */ | ||
130 | int _pop_user (mu_authority_t); | ||
131 | int _pop_apop (mu_authority_t); | ||
132 | static int pop_get_size (mu_mailbox_t, mu_off_t *); | ||
133 | /* We use pop_top for retreiving headers. */ | ||
134 | /* static int pop_header_read (mu_header_t, char *, size_t, mu_off_t, size_t *); */ | ||
135 | static int pop_body_transport (mu_stream_t, mu_transport_t *, mu_transport_t *); | ||
136 | static int pop_body_size (mu_body_t, size_t *); | ||
137 | static int pop_body_lines (mu_body_t, size_t *); | ||
138 | static int pop_body_read (mu_stream_t, char *, size_t, mu_off_t, size_t *); | ||
139 | static int pop_message_read (mu_stream_t, char *, size_t, mu_off_t, size_t *); | ||
140 | static int pop_message_size (mu_message_t, size_t *); | ||
141 | static int pop_message_transport (mu_stream_t, mu_transport_t *, mu_transport_t *); | ||
142 | static int pop_top (mu_header_t, char *, size_t, mu_off_t, size_t *); | ||
143 | static int pop_retr (pop_message_t, char *, size_t, mu_off_t, size_t *); | ||
144 | static int pop_get_transport2 (pop_message_t, mu_transport_t *, mu_transport_t *); | ||
145 | static int pop_get_attribute (mu_attribute_t, int *); | ||
146 | static int pop_set_attribute (mu_attribute_t, int); | ||
147 | static int pop_unset_attribute (mu_attribute_t, int); | ||
148 | static int pop_uidl (mu_message_t, char *, size_t, size_t *); | ||
149 | static int pop_uid (mu_message_t, size_t *); | ||
150 | static int fill_buffer (pop_data_t, char *, size_t); | ||
151 | static int pop_sleep (int); | ||
152 | static int pop_readline (pop_data_t); | ||
153 | static int pop_read_ack (pop_data_t); | ||
154 | static int pop_writeline (pop_data_t, const char *, ...) | ||
155 | MU_PRINTFLIKE(2,3); | ||
156 | static int pop_write (pop_data_t); | ||
157 | static int pop_get_user (mu_authority_t); | ||
158 | static int pop_get_passwd (mu_authority_t); | ||
159 | static char *pop_get_timestamp (pop_data_t); | ||
160 | static int pop_get_md5 (pop_data_t); | ||
161 | |||
162 | /* This structure holds the info for a message. The pop_message_t | ||
163 | type, will serve as the owner of the mu_message_t and contains the command to | ||
164 | send to "RETR"eive the specify message. The problem comes from the header. | ||
165 | If the POP server supports TOP, we can cleanly fetch the header. | ||
166 | But otherwise we use the clumsy approach. .i.e for the header we read 'til | ||
167 | ^\n then discard the rest, for the body we read after ^\n and discard the | ||
168 | beginning. This is a waste, Pop was not conceive for this obviously. */ | ||
169 | struct _pop_message | ||
170 | { | ||
171 | int inbody; | ||
172 | int skip_header; | ||
173 | int skip_body; | ||
174 | size_t body_size; | 66 | size_t body_size; |
175 | size_t header_size; | 67 | size_t header_size; |
176 | size_t body_lines; | 68 | size_t body_lines; |
177 | size_t header_lines; | 69 | size_t header_lines; |
178 | size_t mu_message_size; | 70 | size_t message_size; |
179 | size_t num; | 71 | size_t num; |
180 | char *uidl; /* Cache the uidl string. */ | 72 | char *uidl; /* Cache the uidl string. */ |
181 | int attr_flags; | 73 | int attr_flags; |
182 | mu_message_t message; | 74 | mu_message_t message; |
183 | pop_data_t mpd; /* Back pointer. */ | 75 | struct _pop3_mailbox *mpd; /* Back pointer. */ |
184 | }; | 76 | }; |
185 | 77 | ||
186 | /* Structure to hold things general to the POP mailbox, like its state, how | 78 | struct _pop3_mailbox |
187 | many messages we have so far etc ... */ | ||
188 | struct _pop_data | ||
189 | { | 79 | { |
190 | void *func; /* Indicate a command is in operation, busy. */ | 80 | mu_pop3_t pop3; /* mu_pop3_t is the working horse */ |
191 | size_t id; /* A second level of distincion, we maybe in the same function | 81 | int pops; /* true if pop3 over SSL is being used */ |
192 | but working on a different message. */ | 82 | int is_updated; /* true if the mailbox info is up to date */ |
193 | int pops; /* POPS or POP? */ | 83 | |
194 | char *greeting_banner; /* A greeting banner */ | 84 | size_t msg_count; /* Number of messages in the mailbox */ |
195 | unsigned long capa; /* Server capabilities */ | 85 | mu_off_t total_size; /* Total mailbox size. */ |
196 | enum pop_state state; | 86 | struct _pop3_message **msg; /* Array of messages */ |
197 | pop_message_t *pmessages; | 87 | size_t msg_max; /* Actual size of the array */ |
198 | size_t pmessages_count; | 88 | mu_mailbox_t mbox; /* MU mailbox corresponding to this one. */ |
199 | size_t messages_count; | 89 | |
200 | size_t size; | ||
201 | |||
202 | /* Working I/O buffers. */ | ||
203 | char *buffer; | ||
204 | size_t buflen; /* Len of buffer. */ | ||
205 | char *ptr; /* Points to the end of the buffer i.e the non consume chars. */ | ||
206 | char *nl; /* Points to the '\n' char in te string. */ | ||
207 | mu_off_t offset; /* Dummy, this is use because of the stream buffering. | ||
208 | The mu_stream_t maintains and offset and the offset we use must | ||
209 | be in sync. */ | ||
210 | |||
211 | int is_updated; | ||
212 | char *user; /* Temporary holders for user and passwd. */ | 90 | char *user; /* Temporary holders for user and passwd. */ |
213 | mu_secret_t secret; | 91 | mu_secret_t secret; |
214 | char *digest; | 92 | }; |
215 | mu_mailbox_t mbox; /* Back pointer. */ | 93 | |
216 | } ; | ||
217 | |||
218 | /* Usefull little Macros, since these are very repetitive. */ | ||
219 | |||
220 | /* Check if we're busy ? */ | ||
221 | /* POP is a one channel download protocol, so if someone | ||
222 | is trying to execute a command while another is running | ||
223 | something is seriously incorrect, So the best course | ||
224 | of action is to close down the connection and start a new one. | ||
225 | For example mu_mime_t only reads part of the message. If a client | ||
226 | wants to read different part of the message via mime it should | ||
227 | download it first. POP does not have the features of IMAP for | ||
228 | multipart messages. | ||
229 | Let see a concrete example: | ||
230 | { | ||
231 | mu_mailbox_t mbox; mu_message_t msg; mu_stream_t stream; char buffer[105]; | ||
232 | mu_mailbox_create (&mbox, "pop://qnx.com"); | ||
233 | mu_mailbox_get_message (mbox, 1, &msg); | ||
234 | mu_message_get_stream (msg, &stream); | ||
235 | while (mu_stream_readline (stream, buffer, sizeof(buffer), NULL) != 0) { ..} | ||
236 | } | ||
237 | if in the while of the readline, one try to get another email. The pop | ||
238 | server will get seriously confused, and the second message will still | ||
239 | be the first one, There is no way to tell POP servers yo! stop/abort. | ||
240 | The approach is to close the stream and reopen again. So every time | ||
241 | we go in to a function our state is preserve by the triplets | ||
242 | mpd->{func,state,id}. The macro CHECK_BUSY checks if we are not | ||
243 | in another operation if not you get access if yes the stream is close | ||
244 | and pop_open() is recall again for a new connection. | ||
245 | */ | ||
246 | #define CHECK_BUSY(mbox, mpd, function, identity) \ | ||
247 | do \ | ||
248 | { \ | ||
249 | int err = mu_monitor_wrlock (mbox->monitor); \ | ||
250 | if (err != 0) \ | ||
251 | return err; \ | ||
252 | if ((mpd->func && mpd->func != function) \ | ||
253 | || (mpd->id && mpd->id != (size_t)identity)) \ | ||
254 | { \ | ||
255 | mpd->id = 0; \ | ||
256 | mpd->func = (void *)pop_open; \ | ||
257 | mpd->state = POP_NO_STATE; \ | ||
258 | mu_monitor_unlock (mbox->monitor); \ | ||
259 | err = pop_open (mbox, mbox->flags); \ | ||
260 | if (err != 0) \ | ||
261 | { \ | ||
262 | return err; \ | ||
263 | } \ | ||
264 | } \ | ||
265 | else \ | ||
266 | { \ | ||
267 | mpd->id = (size_t)identity; \ | ||
268 | mpd->func = func; \ | ||
269 | mu_monitor_unlock (mbox->monitor); \ | ||
270 | } \ | ||
271 | } \ | ||
272 | while (0) | ||
273 | |||
274 | /* Clear the state. */ | ||
275 | #define CLEAR_STATE(mpd) \ | ||
276 | mpd->id = 0, mpd->func = NULL, mpd->state = POP_NO_STATE | ||
277 | |||
278 | /* Clear the state and close the stream. */ | ||
279 | #define CHECK_ERROR_CLOSE(mbox, mpd, status) \ | ||
280 | do \ | ||
281 | { \ | ||
282 | if (status != 0) \ | ||
283 | { \ | ||
284 | mu_stream_close (mbox->stream); \ | ||
285 | CLEAR_STATE (mpd); \ | ||
286 | mpd->func = (void *)-1; \ | ||
287 | MU_DEBUG1 (mbox->debug, MU_DEBUG_PROT, \ | ||
288 | "CHECK_ERROR_CLOSE: %s\n", mu_strerror (status));\ | ||
289 | return status; \ | ||
290 | } \ | ||
291 | } \ | ||
292 | while (0) | ||
293 | |||
294 | /* If error, clear the state and return. */ | ||
295 | #define CHECK_ERROR(mpd, status) \ | ||
296 | do \ | ||
297 | { \ | ||
298 | if (status != 0) \ | ||
299 | { \ | ||
300 | CLEAR_STATE (mpd); \ | ||
301 | mpd->func = (void*)-1; \ | ||
302 | MU_DEBUG1(mpd->mbox->debug, MU_DEBUG_PROT, \ | ||
303 | "CHECK_ERROR: %s\n", mu_strerror (status));\ | ||
304 | return status; \ | ||
305 | } \ | ||
306 | } \ | ||
307 | while (0) | ||
308 | |||
309 | /* Clear the state for non recoverable error. */ | ||
310 | #define CHECK_EAGAIN(mpd, status) \ | ||
311 | do \ | ||
312 | { \ | ||
313 | if (status != 0) \ | ||
314 | { \ | ||
315 | if (status != EAGAIN && status != EINPROGRESS && status != EINTR) \ | ||
316 | { \ | ||
317 | CLEAR_STATE (mpd); \ | ||
318 | mpd->func = (void *)-1; \ | ||
319 | MU_DEBUG1(mpd->mbox->debug, MU_DEBUG_PROT, \ | ||
320 | "CHECK_EAGAIN: %s\n", mu_strerror (status));\ | ||
321 | } \ | ||
322 | return status; \ | ||
323 | } \ | ||
324 | } \ | ||
325 | while (0) | ||
326 | |||
327 | |||
328 | /* Allocate mu_mailbox_t, allocate pop internal structures. */ | ||
329 | static int | 94 | static int |
330 | _mailbox_pop_and_pops_init (mu_mailbox_t mbox, int pops) | 95 | pop_open (mu_mailbox_t mbox, int flags) |
331 | { | 96 | { |
332 | pop_data_t mpd; | 97 | struct _pop3_mailbox *mpd = mbox->data; |
333 | int status = 0; | 98 | int status; |
99 | const char *host; | ||
100 | long port = mpd->pops ? MU_POPS_PORT : MU_POP_PORT; | ||
101 | mu_stream_t stream; | ||
334 | 102 | ||
335 | /* Allocate specifics for pop data. */ | 103 | /* Sanity checks. */ |
336 | mpd = mbox->data = calloc (1, sizeof (*mpd)); | 104 | if (mpd == NULL) |
337 | if (mbox->data == NULL) | 105 | return EINVAL; |
338 | return ENOMEM; | ||
339 | 106 | ||
340 | mpd->mbox = mbox; /* Back pointer. */ | 107 | /* Fetch the pop server name and the port in the mu_url_t. */ |
341 | mpd->state = POP_NO_STATE; /* Init with no state. */ | 108 | status = mu_url_sget_host (mbox->url, &host); |
342 | mpd->pops = pops; | 109 | if (status != 0) |
110 | return status; | ||
111 | mu_url_get_port (mbox->url, &port); | ||
343 | 112 | ||
344 | /* Initialize the structure. */ | 113 | mbox->flags = flags; |
345 | mbox->_destroy = pop_destroy; | ||
346 | 114 | ||
347 | mbox->_open = pop_open; | 115 | status = mu_tcp_stream_create (&stream, host, port, mbox->flags); |
348 | mbox->_close = pop_close; | 116 | if (status) |
117 | return status; | ||
118 | #ifdef WITH_TLS | ||
119 | if (mpd->pops) | ||
120 | { | ||
121 | mu_stream_t newstr; | ||
349 | 122 | ||
350 | /* Messages. */ | 123 | status = mu_stream_open (stream); |
351 | mbox->_get_message = pop_get_message; | 124 | if (status) |
352 | mbox->_messages_count = pop_messages_count; | 125 | { |
353 | mbox->_messages_recent = pop_messages_recent; | 126 | mu_stream_destroy (&stream); |
354 | mbox->_message_unseen = pop_message_unseen; | 127 | return status; |
355 | mbox->_expunge = pop_expunge; | 128 | } |
356 | 129 | ||
357 | mbox->_scan = pop_scan; | 130 | status = mu_tls_client_stream_create (&newstr, stream, stream, 0); |
358 | mbox->_is_updated = pop_is_updated; | 131 | mu_stream_unref (stream); |
132 | if (status) | ||
133 | { | ||
134 | mu_error ("pop_open: mu_tls_client_stream_create: %s", | ||
135 | mu_strerror (status)); | ||
136 | return status; | ||
137 | } | ||
138 | stream = newstr; | ||
139 | } | ||
140 | #endif /* WITH_TLS */ | ||
359 | 141 | ||
360 | mbox->_get_size = pop_get_size; | 142 | /* FIXME: How to configure buffer size? */ |
143 | mu_stream_set_buffer (stream, mu_buffer_line, 1024); | ||
361 | 144 | ||
362 | /* Set our properties. */ | 145 | status = mu_pop3_create (&mpd->pop3); |
146 | if (status) | ||
363 | { | 147 | { |
364 | mu_property_t property = NULL; | 148 | mu_stream_destroy (&stream); |
365 | mu_mailbox_get_property (mbox, &property); | 149 | return status; |
366 | mu_property_set_value (property, "TYPE", "POP3", 1); | ||
367 | } | 150 | } |
151 | mu_pop3_set_carrier (mpd->pop3, stream); | ||
368 | 152 | ||
369 | /* Hack! POP does not really have a folder. */ | 153 | if (mu_debug_check_level (mbox->debug, MU_DEBUG_PROT)) |
370 | mbox->folder->data = mbox; | 154 | mu_pop3_trace (mpd->pop3, MU_POP3_TRACE_SET); |
155 | |||
156 | do | ||
157 | { | ||
158 | status = mu_pop3_connect (mpd->pop3); | ||
159 | if (status) | ||
160 | break; | ||
161 | |||
162 | status = mu_pop3_capa (mpd->pop3, 1, NULL); | ||
163 | if (status) | ||
164 | break; | ||
165 | |||
166 | if (WITH_TLS && !mpd->pops && | ||
167 | mu_pop3_capa_test (mpd->pop3, "STLS", NULL) == 0) | ||
168 | { | ||
169 | status = mu_pop3_stls (mpd->pop3); | ||
170 | if (status) | ||
171 | break; | ||
172 | } | ||
371 | 173 | ||
174 | status = mu_authority_authenticate (mbox->folder->authority); | ||
175 | } | ||
176 | while (0); | ||
177 | |||
178 | if (status) | ||
179 | mu_pop3_destroy (&mpd->pop3); | ||
372 | return status; | 180 | return status; |
373 | } | 181 | } |
374 | 182 | ||
375 | int | 183 | static int |
376 | _mailbox_pop_init (mu_mailbox_t mbox) | 184 | pop_close (mu_mailbox_t mbox) |
377 | { | 185 | { |
378 | return _mailbox_pop_and_pops_init (mbox, 0); | 186 | struct _pop3_mailbox *mpd = mbox->data; |
379 | } | 187 | int status; |
380 | 188 | ||
381 | int | 189 | status = mu_pop3_quit (mpd->pop3); |
382 | _mailbox_pops_init (mu_mailbox_t mbox) | 190 | if (status) |
383 | { | 191 | mu_error ("mu_pop3_quit failed: %s", mu_strerror (status)); |
384 | return _mailbox_pop_and_pops_init (mbox, 1); | 192 | status = mu_pop3_disconnect (mpd->pop3); |
193 | if (status) | ||
194 | mu_error ("mu_pop3_disconnect failed: %s", mu_strerror (status)); | ||
195 | mu_pop3_destroy (&mpd->pop3); | ||
196 | return 0; | ||
385 | } | 197 | } |
386 | 198 | ||
387 | /* Cleaning up all the ressources associate with a pop mailbox. */ | ||
388 | static void | 199 | static void |
389 | pop_destroy (mu_mailbox_t mbox) | 200 | pop_destroy (mu_mailbox_t mbox) |
390 | { | 201 | { |
391 | if (mbox->data) | 202 | struct _pop3_mailbox *mpd = mbox->data; |
203 | if (mpd) | ||
392 | { | 204 | { |
393 | pop_data_t mpd = mbox->data; | ||
394 | size_t i; | 205 | size_t i; |
395 | mu_monitor_wrlock (mbox->monitor); | 206 | mu_monitor_wrlock (mbox->monitor); |
396 | /* Destroy the pop messages and ressources associated to them. */ | 207 | /* Destroy the pop messages and ressources associated to them. */ |
397 | for (i = 0; i < mpd->pmessages_count; i++) | 208 | for (i = 0; i < mpd->msg_count; i++) |
398 | { | 209 | { |
399 | if (mpd->pmessages[i]) | 210 | if (mpd->msg[i]) |
400 | { | 211 | { |
401 | mu_message_destroy (&(mpd->pmessages[i]->message), | 212 | mu_message_destroy (&mpd->msg[i]->message, mpd->msg[i]); |
402 | mpd->pmessages[i]); | 213 | if (mpd->msg[i]->uidl) |
403 | if (mpd->pmessages[i]->uidl) | 214 | free (mpd->msg[i]->uidl); |
404 | free (mpd->pmessages[i]->uidl); | 215 | free (mpd->msg[i]); |
405 | free (mpd->pmessages[i]); | ||
406 | mpd->pmessages[i] = NULL; | ||
407 | } | 216 | } |
408 | } | 217 | } |
409 | if (mpd->greeting_banner) | 218 | mu_pop3_destroy (&mpd->pop3); |
410 | free (mpd->greeting_banner); | 219 | if (mpd->user) |
411 | if (mpd->buffer) | 220 | free (mpd->user); |
412 | free (mpd->buffer); | 221 | if (mpd->secret) |
413 | if (mpd->pmessages) | 222 | mu_secret_unref (mpd->secret); |
414 | free (mpd->pmessages); | ||
415 | free (mpd); | ||
416 | mbox->data = NULL; | ||
417 | mu_monitor_unlock (mbox->monitor); | ||
418 | } | 223 | } |
419 | } | 224 | } |
420 | 225 | ||
226 | /* Update and scanning. */ | ||
421 | static int | 227 | static int |
422 | pop_mbox_uidls (mu_mailbox_t mbox, mu_list_t list) | 228 | pop_is_updated (mu_mailbox_t mbox) |
423 | { | 229 | { |
424 | pop_data_t mpd = mbox->data; | 230 | struct _pop3_mailbox *mpd = mbox->data; |
425 | int status; | 231 | if (mpd == NULL) |
426 | 232 | return 0; | |
427 | status = pop_writeline (mpd, "UIDL\r\n"); | 233 | return mpd->is_updated; |
428 | CHECK_ERROR (mpd, status); | 234 | } |
429 | MU_DEBUG (mbox->debug, MU_DEBUG_PROT, mpd->buffer); | ||
430 | 235 | ||
431 | status = pop_write (mpd); | 236 | /* Return the number of messages in the mailbox */ |
432 | CHECK_EAGAIN (mpd, status); | 237 | static int |
238 | pop_messages_count (mu_mailbox_t mbox, size_t *pcount) | ||
239 | { | ||
240 | struct _pop3_mailbox *mpd = mbox->data; | ||
241 | int status; | ||
433 | 242 | ||
434 | status = pop_read_ack (mpd); | 243 | if (mpd == NULL) |
435 | CHECK_EAGAIN (mpd, status); | 244 | return EINVAL; |
436 | MU_DEBUG (mpd->mbox->debug, MU_DEBUG_PROT, mpd->buffer); | ||
437 | 245 | ||
438 | if (!mu_c_strncasecmp (mpd->buffer, "+OK", 3)) | 246 | if (pop_is_updated (mbox)) |
439 | { | ||
440 | do | ||
441 | { | 247 | { |
442 | char *p; | 248 | if (pcount) |
443 | size_t num; | 249 | *pcount = mpd->msg_count; |
444 | struct mu_uidl *uidl; | 250 | return 0; |
445 | 251 | } | |
446 | status = pop_read_ack (mpd); | ||
447 | MU_DEBUG (mpd->mbox->debug, MU_DEBUG_PROT, mpd->buffer); | ||
448 | |||
449 | num = strtoul (mpd->buffer, &p, 10); | ||
450 | if (*p == 0 || !mu_isblank (*p)) | ||
451 | continue; /* FIXME: or error? */ | ||
452 | p = mu_str_skip_class (p, MU_CTYPE_SPACE); | ||
453 | mu_rtrim_cset (p, "\r\n"); | ||
454 | 252 | ||
455 | uidl = malloc (sizeof (uidl[0])); | 253 | status = mu_pop3_stat (mpd->pop3, &mpd->msg_count, &mpd->total_size); |
456 | if (!uidl) | 254 | if (status == 0) |
457 | { | 255 | { |
458 | status = ENOMEM; | 256 | if (pcount) |
459 | break; | 257 | *pcount = mpd->msg_count; |
460 | } | 258 | mpd->is_updated = 1; |
461 | uidl->msgno = num; | ||
462 | strncpy (uidl->uidl, p, MU_UIDL_BUFFER_SIZE); | ||
463 | status = mu_list_append (list, uidl); | ||
464 | } | ||
465 | while (mpd->nl); | ||
466 | } | 259 | } |
467 | else | ||
468 | status = ENOSYS; | ||
469 | return status; | 260 | return status; |
470 | } | 261 | } |
471 | 262 | ||
472 | /* | ||
473 | POP3 CAPA support. | ||
474 | */ | ||
475 | |||
476 | static int | 263 | static int |
477 | pop_parse_capa (pop_data_t mpd) | 264 | pop_scan (mu_mailbox_t mbox, size_t msgno, size_t *pcount) |
478 | { | 265 | { |
479 | int status; | 266 | int status; |
480 | if (!mu_c_strncasecmp (mpd->buffer, "+OK", 3)) | 267 | size_t i; |
481 | { | 268 | size_t count = 0; |
482 | mpd->capa = 0; | ||
483 | do | ||
484 | { | ||
485 | status = pop_read_ack (mpd); | ||
486 | MU_DEBUG (mpd->mbox->debug, MU_DEBUG_PROT, mpd->buffer); | ||
487 | |||
488 | /* Here we check some common capabilities like TOP, USER, UIDL, | ||
489 | and STLS. The rest are ignored. Please note that some | ||
490 | capabilities might have an extra arguments. For instance, | ||
491 | SASL can have CRAM-MD5 and/or KERBEROS_V4, and etc. | ||
492 | This is why I suggest adding (in a future) an extra variable, | ||
493 | for example `capa_sasl'. It would hold the following flags: | ||
494 | SASL_CRAM_MD5, SASL_KERBEROS_V4, and so on. Also the EXPIRE | ||
495 | and LOGIN-DELAY capabilities have an extra arguments! | ||
496 | Note that there is no APOP capability, even though APOP | ||
497 | is an optional command in POP3. -- W.P. */ | ||
498 | |||
499 | if (!mu_c_strncasecmp (mpd->buffer, "TOP", 3)) | ||
500 | mpd->capa |= CAPA_TOP; | ||
501 | else if (!mu_c_strncasecmp (mpd->buffer, "USER", 4)) | ||
502 | mpd->capa |= CAPA_USER; | ||
503 | else if (!mu_c_strncasecmp (mpd->buffer, "UIDL", 4)) | ||
504 | mpd->capa |= CAPA_UIDL; | ||
505 | else if (!mu_c_strncasecmp (mpd->buffer, "STLS", 4)) | ||
506 | mpd->capa |= CAPA_STLS; | ||
507 | } | ||
508 | while (mpd->nl); | ||
509 | |||
510 | if (mpd->capa & CAPA_UIDL) | ||
511 | mpd->mbox->_get_uidls = pop_mbox_uidls; | ||
512 | 269 | ||
270 | status = pop_messages_count (mbox, &count); | ||
271 | if (status != 0) | ||
513 | return status; | 272 | return status; |
514 | } | 273 | if (pcount) |
515 | else | 274 | *pcount = count; |
275 | if (mbox->observable == NULL) | ||
276 | return 0; | ||
277 | for (i = msgno; i <= count; i++) | ||
516 | { | 278 | { |
517 | /* mu_error ("CAPA not implemented"); */ /* FIXME */ | 279 | size_t tmp = i; |
518 | return ENOSYS; | 280 | if (mu_observable_notify (mbox->observable, MU_EVT_MESSAGE_ADD, |
281 | &tmp) != 0) | ||
282 | break; | ||
283 | if (((i +1) % 10) == 0) | ||
284 | { | ||
285 | mu_observable_notify (mbox->observable, MU_EVT_MAILBOX_PROGRESS, | ||
286 | NULL); | ||
519 | } | 287 | } |
288 | } | ||
289 | return 0; | ||
520 | } | 290 | } |
521 | 291 | ||
292 | /* There's no way to retrieve this info via POP3 */ | ||
522 | static int | 293 | static int |
523 | pop_capa (mu_mailbox_t mbox) | 294 | pop_message_unseen (mu_mailbox_t mbox, size_t *punseen) |
524 | { | 295 | { |
525 | pop_data_t mpd = mbox->data; | 296 | size_t count = 0; |
526 | int status; | 297 | int status = pop_messages_count (mbox, &count); |
527 | 298 | if (status != 0) | |
528 | status = pop_writeline (mpd, "CAPA\r\n"); | 299 | return status; |
529 | CHECK_ERROR (mpd, status); | 300 | if (punseen) |
530 | MU_DEBUG (mbox->debug, MU_DEBUG_PROT, mpd->buffer); | 301 | *punseen = (count > 0) ? 1 : 0; |
302 | return 0; | ||
303 | } | ||
531 | 304 | ||
532 | status = pop_write (mpd); | 305 | static int |
533 | CHECK_EAGAIN (mpd, status); | 306 | pop_get_size (mu_mailbox_t mbox, mu_off_t *psize) |
534 | mpd->state = POP_CAPA_ACK; | 307 | { |
308 | struct _pop3_mailbox *mpd = mbox->data; | ||
309 | int status = 0; | ||
535 | 310 | ||
536 | /* POP_CAPA_ACK */ | 311 | if (mpd == NULL) |
537 | status = pop_read_ack (mpd); | 312 | return EINVAL; |
538 | CHECK_EAGAIN (mpd, status); | ||
539 | MU_DEBUG (mpd->mbox->debug, MU_DEBUG_PROT, mpd->buffer); | ||
540 | 313 | ||
541 | return pop_parse_capa (mpd); | 314 | if (!pop_is_updated (mbox)) |
315 | status = pop_messages_count (mbox, NULL); | ||
316 | if (psize) | ||
317 | *psize = mpd->total_size; | ||
318 | return status; | ||
542 | } | 319 | } |
543 | 320 | ||
544 | /* Simple User/pass authentication for pop. We ask for the info | 321 | |
545 | from the standard input. */ | 322 | static int |
546 | int | 323 | pop_create_message (struct _pop3_message *mpm, struct _pop3_mailbox *mpd) |
547 | _pop_user (mu_authority_t auth) | ||
548 | { | 324 | { |
549 | mu_folder_t folder = mu_authority_get_owner (auth); | ||
550 | mu_mailbox_t mbox = folder->data; | ||
551 | pop_data_t mpd = mbox->data; | ||
552 | int status; | 325 | int status; |
326 | mu_message_t msg; | ||
553 | 327 | ||
554 | switch (mpd->state) | 328 | status = mu_message_create (&msg, mpm); |
555 | { | 329 | if (status) |
556 | case POP_AUTH: | 330 | return status; |
557 | /* Fetch the user from them. */ | 331 | // FIXME... |
558 | status = pop_get_user (auth); | 332 | mpm->message = msg; |
559 | if (status != 0 || mpd->user == NULL || mpd->user[0] == '\0') | ||
560 | { | ||
561 | pop_writeline (mpd, "QUIT\r\n"); | ||
562 | MU_DEBUG (mbox->debug, MU_DEBUG_PROT, mpd->buffer); | ||
563 | pop_write (mpd); | ||
564 | CHECK_ERROR_CLOSE (mbox, mpd, MU_ERR_NOUSERNAME); | ||
565 | } | ||
566 | status = pop_writeline (mpd, "USER %s\r\n", mpd->user); | ||
567 | CHECK_ERROR_CLOSE(mbox, mpd, status); | ||
568 | MU_DEBUG (mbox->debug, MU_DEBUG_PROT, mpd->buffer); | ||
569 | free (mpd->user); | ||
570 | mpd->user = NULL; | ||
571 | mpd->state = POP_AUTH_USER; | ||
572 | |||
573 | case POP_AUTH_USER: | ||
574 | /* Send username. */ | ||
575 | status = pop_write (mpd); | ||
576 | CHECK_EAGAIN (mpd, status); | ||
577 | mpd->state = POP_AUTH_USER_ACK; | ||
578 | |||
579 | case POP_AUTH_USER_ACK: | ||
580 | /* Get the user ack. */ | ||
581 | status = pop_read_ack (mpd); | ||
582 | CHECK_EAGAIN (mpd, status); | ||
583 | MU_DEBUG (mbox->debug, MU_DEBUG_PROT, mpd->buffer); | ||
584 | if (mu_c_strncasecmp (mpd->buffer, "+OK", 3) != 0) | ||
585 | { | ||
586 | mu_observable_t observable = NULL; | ||
587 | mu_mailbox_get_observable (mbox, &observable); | ||
588 | CLEAR_STATE (mpd); | ||
589 | mu_observable_notify (observable, MU_EVT_AUTHORITY_FAILED, NULL); | ||
590 | CHECK_ERROR_CLOSE (mbox, mpd, EACCES); | ||
591 | } | ||
592 | status = pop_get_passwd (auth); | ||
593 | if (status != 0 || mpd->secret == NULL) | ||
594 | { | ||
595 | pop_writeline (mpd, "QUIT\r\n"); | ||
596 | MU_DEBUG (mbox->debug, MU_DEBUG_PROT, mpd->buffer); | ||
597 | pop_write (mpd); | ||
598 | CHECK_ERROR_CLOSE (mbox, mpd, MU_ERR_NOPASSWORD); | ||
599 | } | ||
600 | status = pop_writeline (mpd, "PASS %s\r\n", | ||
601 | mu_secret_password (mpd->secret)); | ||
602 | mu_secret_password_unref (mpd->secret); | ||
603 | mu_secret_unref (mpd->secret); | ||
604 | mpd->secret = NULL; | ||
605 | MU_DEBUG (mbox->debug, MU_DEBUG_PROT, "PASS ***\n"); | ||
606 | CHECK_ERROR_CLOSE (mbox, mpd, status); | ||
607 | mpd->state = POP_AUTH_PASS; | ||
608 | /* FIXME: Merge these two cases */ | ||
609 | |||
610 | case POP_AUTH_PASS: | ||
611 | /* Send passwd. */ | ||
612 | status = pop_write (mpd); | ||
613 | CHECK_EAGAIN (mpd, status); | ||
614 | /* Clear the buffer it contains the passwd. */ | ||
615 | memset (mpd->buffer, '\0', mpd->buflen); | ||
616 | mpd->state = POP_AUTH_PASS_ACK; | ||
617 | |||
618 | case POP_AUTH_PASS_ACK: | ||
619 | /* Get the ack from passwd. */ | ||
620 | status = pop_read_ack (mpd); | ||
621 | CHECK_EAGAIN (mpd, status); | ||
622 | MU_DEBUG (mbox->debug, MU_DEBUG_PROT, mpd->buffer); | ||
623 | if (mu_c_strncasecmp (mpd->buffer, "+OK", 3) != 0) | ||
624 | { | ||
625 | mu_observable_t observable = NULL; | ||
626 | mu_mailbox_get_observable (mbox, &observable); | ||
627 | CLEAR_STATE (mpd); | ||
628 | mu_observable_notify (observable, MU_EVT_AUTHORITY_FAILED, NULL); | ||
629 | return MU_ERR_AUTH_FAILURE; | ||
630 | } | ||
631 | mpd->state = POP_AUTH_DONE; | ||
632 | break; /* We're outta here. */ | ||
633 | |||
634 | default: | ||
635 | break; | ||
636 | } | ||
637 | CLEAR_STATE (mpd); | ||
638 | return 0; | 333 | return 0; |
639 | } | 334 | } |
640 | 335 | ||
641 | int | 336 | |
642 | _pop_apop (mu_authority_t auth) | 337 | /* ------------------------------------------------------------------------- */ |
338 | /* Header */ | ||
339 | |||
340 | static int | ||
341 | pop_header_fill (void *data, char **pbuf, size_t *plen) | ||
643 | { | 342 | { |
644 | mu_folder_t folder = mu_authority_get_owner (auth); | 343 | struct _pop3_message *mpm = data; |
645 | mu_mailbox_t mbox = folder->data; | 344 | struct _pop3_mailbox *mpd = mpm->mpd; |
646 | pop_data_t mpd = mbox->data; | 345 | mu_stream_t stream; |
346 | mu_opool_t opool; | ||
647 | int status; | 347 | int status; |
648 | 348 | ||
649 | switch (mpd->state) | 349 | status = mu_opool_create (&opool, 0); |
650 | { | 350 | if (status) |
651 | case POP_AUTH: | 351 | return status; |
652 | /* Fetch the user from them. */ | 352 | |
653 | status = pop_get_user (auth); | 353 | if (mu_pop3_capa_test (mpd->pop3, "TOP", NULL) == 0) |
654 | if (status != 0 || mpd->user == NULL || mpd->user[0] == '\0') | 354 | status = mu_pop3_top (mpd->pop3, mpm->num, 0, &stream); |
355 | else | ||
356 | status = mu_pop3_retr (mpd->pop3, mpm->num, &stream); | ||
357 | |||
358 | if (status == 0) | ||
655 | { | 359 | { |
656 | CHECK_ERROR_CLOSE (mbox, mpd, EINVAL); | 360 | size_t size = 0; |
657 | } | 361 | char *buf = NULL; |
362 | size_t n; | ||
658 | 363 | ||
659 | /* Fetch the secret from them. */ | 364 | while (mu_stream_getline (stream, &buf, &size, &n) == 0 |
660 | status = pop_get_passwd (auth); | 365 | && n > 0) |
661 | if (status != 0 || mpd->secret == NULL) | ||
662 | { | 366 | { |
663 | CHECK_ERROR_CLOSE (mbox, mpd, EINVAL); | 367 | size_t len = mu_rtrim_cset (buf, "\r\n"); |
368 | if (len == 0) | ||
369 | break; | ||
370 | mu_opool_append (opool, buf, len); | ||
371 | mu_opool_append_char (opool, '\n'); | ||
664 | } | 372 | } |
665 | 373 | ||
666 | /* Make the MD5 digest string. */ | 374 | if (!mu_stream_eof (stream)) |
667 | status = pop_get_md5 (mpd); | 375 | /* Drain the stream. */ |
668 | if (status != 0) | 376 | while (mu_stream_getline (stream, &buf, &size, &n) == 0 |
377 | && n > 0); | ||
378 | mu_stream_destroy (&stream); | ||
379 | |||
380 | n = mu_opool_size (opool); | ||
381 | if (n > size) | ||
669 | { | 382 | { |
670 | CHECK_ERROR_CLOSE (mbox, mpd, status); | 383 | char *p = realloc (buf, n); |
671 | } | 384 | if (!p) |
672 | status = pop_writeline (mpd, "APOP %s %s\r\n", mpd->user, mpd->digest); | ||
673 | MU_DEBUG (mbox->debug, MU_DEBUG_PROT, mpd->buffer); | ||
674 | /* We have to obscure the md5 string. */ | ||
675 | memset (mpd->digest, '\0', strlen (mpd->digest)); | ||
676 | free (mpd->user); | ||
677 | free (mpd->digest); | ||
678 | mpd->user = NULL; | ||
679 | mpd->digest = NULL; | ||
680 | CHECK_ERROR_CLOSE (mbox, mpd, status); | ||
681 | mpd->state = POP_APOP; | ||
682 | |||
683 | case POP_APOP: | ||
684 | /* Send apop. */ | ||
685 | status = pop_write (mpd); | ||
686 | CHECK_EAGAIN (mpd, status); | ||
687 | /* Clear the buffer it contains the md5. */ | ||
688 | memset (mpd->buffer, '\0', mpd->buflen); | ||
689 | mpd->state = POP_APOP_ACK; | ||
690 | |||
691 | case POP_APOP_ACK: | ||
692 | status = pop_read_ack (mpd); | ||
693 | CHECK_EAGAIN (mpd, status); | ||
694 | MU_DEBUG (mbox->debug, MU_DEBUG_PROT, mpd->buffer); | ||
695 | if (mu_c_strncasecmp (mpd->buffer, "+OK", 3) != 0) | ||
696 | { | 385 | { |
697 | mu_observable_t observable = NULL; | 386 | free (buf); |
698 | mu_mailbox_get_observable (mbox, &observable); | 387 | mu_opool_destroy (&opool); |
699 | CLEAR_STATE (mpd); | 388 | return ENOMEM; |
700 | mu_observable_notify (observable, MU_EVT_AUTHORITY_FAILED, NULL); | ||
701 | CHECK_ERROR_CLOSE (mbox, mpd, EACCES); | ||
702 | } | 389 | } |
703 | mpd->state = POP_AUTH_DONE; | 390 | buf = p; |
704 | break; /* We're outta here. */ | ||
705 | |||
706 | default: | ||
707 | break; | ||
708 | } | 391 | } |
709 | CLEAR_STATE (mpd); | ||
710 | return 0; | ||
711 | } | ||
712 | 392 | ||
713 | /* | 393 | mu_opool_copy (opool, buf, n); |
714 | Client side STLS support. | 394 | *pbuf = buf; |
715 | */ | 395 | *plen = n; |
396 | status = 0; | ||
397 | } | ||
398 | mu_opool_destroy (&opool); | ||
716 | 399 | ||
717 | static int | 400 | return status; |
718 | pop_reader (void *iodata) | ||
719 | { | ||
720 | int status = 0; | ||
721 | pop_data_t iop = iodata; | ||
722 | status = pop_read_ack (iop); | ||
723 | CHECK_EAGAIN (iop, status); | ||
724 | MU_DEBUG (iop->mbox->debug, MU_DEBUG_PROT, iop->buffer); | ||
725 | return status;/*mu_c_strncasecmp (iop->buffer, "+OK", 3) == 0;*/ | ||
726 | } | 401 | } |
727 | 402 | ||
728 | static int | 403 | static int |
729 | pop_writer (void *iodata, char *buf) | 404 | pop_create_header (struct _pop3_message *mpm) |
730 | { | 405 | { |
731 | pop_data_t iop = iodata; | ||
732 | int status; | 406 | int status; |
407 | mu_header_t header = NULL; | ||
733 | 408 | ||
734 | MU_DEBUG1 (iop->mbox->debug, MU_DEBUG_PROT, "%s\n", buf); | 409 | status = mu_header_create (&header, NULL, 0); |
735 | status = pop_writeline (iop, "%s\r\n", buf); | 410 | if (status) |
736 | CHECK_ERROR (iop, status); | ||
737 | status = pop_write (iop); | ||
738 | CHECK_ERROR (iop, status); | ||
739 | return status; | 411 | return status; |
412 | mu_header_set_fill (header, pop_header_fill, mpm); | ||
413 | mu_message_set_header (mpm->message, header, mpm); | ||
414 | return 0; | ||
740 | } | 415 | } |
741 | 416 | ||
742 | static void | ||
743 | pop_stream_ctl (void *iodata, mu_stream_t *pold, mu_stream_t new) | ||
744 | { | ||
745 | pop_data_t iop = iodata; | ||
746 | if (pold) | ||
747 | *pold = iop->mbox->stream; | ||
748 | if (new) | ||
749 | iop->mbox->stream = new; | ||
750 | } | ||
751 | 417 | ||
418 | /* ------------------------------------------------------------------------- */ | ||
419 | /* Attributes */ | ||
420 | |||
421 | /* There is no POP3 command to return message attributes, therefore we | ||
422 | have to recurse to reading the "Status:" header. Unfortunately, some | ||
423 | servers remove it when you dowload a message, and in this case a message | ||
424 | will always look like new even if you already read it. There is also | ||
425 | no way to set an attribute on remote mailbox via the POP server and | ||
426 | many server once you do a RETR (and in some cases a TOP) will mark the | ||
427 | message as read (Status: RO). Even worse, some servers may be configured | ||
428 | to delete after the RETR, and some go as much as deleting after the TOP, | ||
429 | since technicaly you can download a message via TOP without RETR'eiving | ||
430 | it. */ | ||
752 | static int | 431 | static int |
753 | pop_stls (mu_mailbox_t mbox) | 432 | pop_get_attribute (mu_attribute_t attr, int *pflags) |
754 | { | 433 | { |
755 | #ifdef WITH_TLS | 434 | struct _pop3_message *mpm = mu_attribute_get_owner (attr); |
756 | int status; | ||
757 | pop_data_t mpd = mbox->data; | ||
758 | char *keywords[] = { "STLS", "CAPA", NULL }; | ||
759 | |||
760 | if (!mu_tls_enable || !(mpd->capa & CAPA_STLS)) | ||
761 | return -1; | ||
762 | |||
763 | status = mu_tls_begin (mpd, pop_reader, pop_writer, | ||
764 | pop_stream_ctl, keywords); | ||
765 | |||
766 | MU_DEBUG1 (mbox->debug, MU_DEBUG_PROT, "TLS negotiation %s\n", | ||
767 | status == 0 ? "succeeded" : "failed"); | ||
768 | |||
769 | if (status == 0) | ||
770 | pop_parse_capa (mpd); | ||
771 | |||
772 | return status; | ||
773 | #else | ||
774 | return -1; | ||
775 | #endif /* WITH_TLS */ | ||
776 | } | ||
777 | |||
778 | /* Open the connection to the sever, and send the authentication. */ | ||
779 | static int | ||
780 | pop_open (mu_mailbox_t mbox, int flags) | ||
781 | { | ||
782 | pop_data_t mpd = mbox->data; | ||
783 | int status; | ||
784 | const char *host; | ||
785 | long port = mpd->pops ? MU_POPS_PORT : MU_POP_PORT; | ||
786 | |||
787 | /* Sanity checks. */ | ||
788 | if (mpd == NULL) | ||
789 | return EINVAL; | ||
790 | |||
791 | /* Fetch the pop server name and the port in the mu_url_t. */ | ||
792 | status = mu_url_sget_host (mbox->url, &host); | ||
793 | if (status != 0) | ||
794 | return status; | ||
795 | mu_url_get_port (mbox->url, &port); | ||
796 | |||
797 | mbox->flags = flags; | ||
798 | |||
799 | /* Do not check for reconnect here. */ | ||
800 | /* CHECK_BUSY (mbox, mpd, func, 0); */ | ||
801 | |||
802 | /* Enter the pop state machine, and boogy: AUTHORISATION State. */ | ||
803 | switch (mpd->state) | ||
804 | { | ||
805 | case POP_NO_STATE: | ||
806 | /* Allocate a working io buffer. */ | ||
807 | if (mpd->buffer == NULL) | ||
808 | { | ||
809 | /* 255 is the limit lenght of a POP3 command according to RFCs. */ | ||
810 | mpd->buflen = 255; | ||
811 | mpd->buffer = calloc (mpd->buflen + 1, sizeof (char)); | ||
812 | if (mpd->buffer == NULL) | ||
813 | { | ||
814 | CHECK_ERROR (mpd, ENOMEM); | ||
815 | } | ||
816 | } | ||
817 | else | ||
818 | { | ||
819 | /* Clear any residual from a previous connection. */ | ||
820 | memset (mpd->buffer, '\0', mpd->buflen); | ||
821 | } | ||
822 | mpd->ptr = mpd->buffer; | ||
823 | |||
824 | /* Create the networking stack. */ | ||
825 | if (mbox->stream == NULL) | ||
826 | { | ||
827 | status = mu_tcp_stream_create (&mbox->stream, host, port, | ||
828 | mbox->flags); | ||
829 | CHECK_ERROR (mpd, status); | ||
830 | /* FIXME: How to configure the buffer size? */ | ||
831 | mu_stream_set_buffer (mbox->stream, mu_buffer_line, 1024); | ||
832 | |||
833 | #ifdef WITH_TLS | ||
834 | if (mpd->pops) | ||
835 | { | ||
836 | mu_stream_t newstr; | ||
837 | |||
838 | status = mu_stream_open (mbox->stream); | ||
839 | CHECK_EAGAIN (mpd, status); | ||
840 | CHECK_ERROR_CLOSE (mbox, mpd, status); | ||
841 | |||
842 | status = mu_tls_client_stream_create (&newstr, | ||
843 | mbox->stream, | ||
844 | mbox->stream, 0); | ||
845 | if (status != 0) | ||
846 | { | ||
847 | mu_error ("pop_open: mu_tls_client_stream_create: %s", | ||
848 | mu_strerror (status)); | ||
849 | return status; | ||
850 | } | ||
851 | mbox->stream = newstr; | ||
852 | } | ||
853 | #endif /* WITH_TLS */ | ||
854 | } | ||
855 | else | ||
856 | { | ||
857 | /* This is sudden death: for many pop servers, it is important to | ||
858 | let them time to remove locks or move the .user.pop files. This | ||
859 | happen when we do BUSY_CHECK(). For example, the user does not | ||
860 | want to read the entire file, and wants start to read a new | ||
861 | message, closing the connection and immediately contact the | ||
862 | server again, and we'll end up having "-ERR Mail Lock busy" or | ||
863 | something similar. To prevent this race condition we sleep 2 | ||
864 | seconds. */ | ||
865 | mu_stream_close (mbox->stream); | ||
866 | pop_sleep (2); | ||
867 | } | ||
868 | mpd->state = POP_OPEN_CONNECTION; | ||
869 | |||
870 | case POP_OPEN_CONNECTION: | ||
871 | /* Establish the connection. */ | ||
872 | MU_DEBUG2 (mbox->debug, MU_DEBUG_PROT, "open (%s:%ld)\n", host, port); | ||
873 | status = mu_stream_open (mbox->stream); | ||
874 | CHECK_EAGAIN (mpd, status); | ||
875 | /* Can't recover bailout. */ | ||
876 | CHECK_ERROR_CLOSE (mbox, mpd, status); | ||
877 | mpd->state = POP_GREETINGS; | ||
878 | |||
879 | case POP_GREETINGS: | ||
880 | { | ||
881 | int gblen = 0; | ||
882 | status = pop_read_ack (mpd); | ||
883 | CHECK_EAGAIN (mpd, status); | ||
884 | MU_DEBUG (mbox->debug, MU_DEBUG_PROT, mpd->buffer); | ||
885 | if (mu_c_strncasecmp (mpd->buffer, "+OK", 3) != 0) | ||
886 | { | ||
887 | CHECK_ERROR_CLOSE (mbox, mpd, EACCES); | ||
888 | } | ||
889 | gblen = strlen (mpd->buffer); | ||
890 | mpd->greeting_banner = calloc (gblen, 1); | ||
891 | if (mpd->greeting_banner == NULL) | ||
892 | { | ||
893 | CHECK_ERROR (mpd, ENOMEM); | ||
894 | } | ||
895 | memcpy (mpd->greeting_banner, mpd->buffer, gblen); | ||
896 | mpd->state = POP_CAPA; | ||
897 | } | ||
898 | |||
899 | case POP_CAPA: | ||
900 | case POP_CAPA_ACK: | ||
901 | pop_capa (mbox); | ||
902 | mpd->state = POP_STLS; | ||
903 | |||
904 | case POP_STLS: | ||
905 | case POP_STLS_ACK: | ||
906 | if (!mpd->pops) | ||
907 | pop_stls (mbox); | ||
908 | mpd->state = POP_AUTH; | ||
909 | |||
910 | case POP_AUTH: | ||
911 | case POP_AUTH_USER: | ||
912 | case POP_AUTH_USER_ACK: | ||
913 | case POP_AUTH_PASS: | ||
914 | case POP_AUTH_PASS_ACK: | ||
915 | case POP_APOP: | ||
916 | case POP_APOP_ACK: | ||
917 | /* Authenticate. */ | ||
918 | status = mu_authority_authenticate (mbox->folder->authority); | ||
919 | CHECK_EAGAIN (mpd, status); | ||
920 | |||
921 | case POP_AUTH_DONE: | ||
922 | break; | ||
923 | |||
924 | default: | ||
925 | /* | ||
926 | mu_error ("pop_open: unknown state"); | ||
927 | */ | ||
928 | break; | ||
929 | }/* End AUTHORISATION state. */ | ||
930 | |||
931 | /* Clear any state. */ | ||
932 | CLEAR_STATE (mpd); | ||
933 | return 0; | ||
934 | } | ||
935 | |||
936 | /* Send the QUIT and close the socket. */ | ||
937 | static int | ||
938 | pop_close (mu_mailbox_t mbox) | ||
939 | { | ||
940 | pop_data_t mpd = mbox->data; | ||
941 | void *func = (void *)pop_close; | ||
942 | int status; | ||
943 | size_t i; | ||
944 | |||
945 | if (mpd == NULL) | ||
946 | return EINVAL; | ||
947 | |||
948 | /* Should not check for Busy, we're shuting down anyway. */ | ||
949 | /* CHECK_BUSY (mbox, mpd, func, 0); */ | ||
950 | mu_monitor_wrlock (mbox->monitor); | ||
951 | if (mpd->func && mpd->func != func) | ||
952 | mpd->state = POP_NO_STATE; | ||
953 | mpd->id = 0; | ||
954 | mpd->func = func; | ||
955 | mu_monitor_unlock (mbox->monitor); | ||
956 | |||
957 | /* Ok boys, it's a wrap: UPDATE State. */ | ||
958 | switch (mpd->state) | ||
959 | { | ||
960 | case POP_NO_STATE: | ||
961 | /* Initiate the close. */ | ||
962 | status = pop_writeline (mpd, "QUIT\r\n"); | ||
963 | CHECK_ERROR (mpd, status); | ||
964 | MU_DEBUG (mbox->debug, MU_DEBUG_PROT, mpd->buffer); | ||
965 | mpd->state = POP_QUIT; | ||
966 | |||
967 | case POP_QUIT: | ||
968 | /* Send the quit. */ | ||
969 | status = pop_write (mpd); | ||
970 | CHECK_EAGAIN (mpd, status); | ||
971 | mpd->state = POP_QUIT_ACK; | ||
972 | |||
973 | case POP_QUIT_ACK: | ||
974 | /* Glob the acknowledge. */ | ||
975 | status = pop_read_ack (mpd); | ||
976 | CHECK_EAGAIN (mpd, status); | ||
977 | MU_DEBUG (mbox->debug, MU_DEBUG_PROT, mpd->buffer); | ||
978 | /* Now what ! and how can we tell them about errors ? So far now | ||
979 | lets just be verbose about the error but close the connection | ||
980 | anyway. */ | ||
981 | if (mu_c_strncasecmp (mpd->buffer, "+OK", 3) != 0) | ||
982 | mu_error ("pop_close: %s", mpd->buffer); | ||
983 | mu_stream_close (mbox->stream); | ||
984 | break; | ||
985 | |||
986 | default: | ||
987 | /* | ||
988 | mu_error ("pop_close: unknown state"); | ||
989 | */ | ||
990 | break; | ||
991 | } /* UPDATE state. */ | ||
992 | |||
993 | /* Free the messages. */ | ||
994 | for (i = 0; i < mpd->pmessages_count; i++) | ||
995 | { | ||
996 | if (mpd->pmessages[i]) | ||
997 | { | ||
998 | mu_message_destroy (&(mpd->pmessages[i]->message), | ||
999 | mpd->pmessages[i]); | ||
1000 | if (mpd->pmessages[i]->uidl) | ||
1001 | free (mpd->pmessages[i]->uidl); | ||
1002 | free (mpd->pmessages[i]); | ||
1003 | mpd->pmessages[i] = NULL; | ||
1004 | } | ||
1005 | } | ||
1006 | /* And clear any residue. */ | ||
1007 | if (mpd->greeting_banner) | ||
1008 | free (mpd->greeting_banner); | ||
1009 | mpd->greeting_banner = NULL; | ||
1010 | if (mpd->pmessages) | ||
1011 | free (mpd->pmessages); | ||
1012 | mpd->pmessages = NULL; | ||
1013 | mpd->pmessages_count = 0; | ||
1014 | mpd->is_updated = 0; | ||
1015 | if (mpd->buffer) | ||
1016 | free (mpd->buffer); | ||
1017 | mpd->buffer = NULL; | ||
1018 | |||
1019 | CLEAR_STATE (mpd); | ||
1020 | return 0; | ||
1021 | } | ||
1022 | |||
1023 | /* Only build/setup the mu_message_t structure for a mesgno. pop_message_t, | ||
1024 | will act as the owner of messages. */ | ||
1025 | static int | ||
1026 | pop_get_message (mu_mailbox_t mbox, size_t msgno, mu_message_t *pmsg) | ||
1027 | { | ||
1028 | pop_data_t mpd = mbox->data; | ||
1029 | mu_message_t msg = NULL; | ||
1030 | pop_message_t mpm; | ||
1031 | int status; | ||
1032 | size_t i; | ||
1033 | |||
1034 | /* Sanity. */ | ||
1035 | if (pmsg == NULL || mpd == NULL) | ||
1036 | return EINVAL; | ||
1037 | |||
1038 | /* If we did not start a scanning yet do it now. */ | ||
1039 | if (!pop_is_updated (mbox)) | ||
1040 | pop_scan (mbox, 1, NULL); | ||
1041 | |||
1042 | if (msgno > mpd->messages_count) | ||
1043 | return EINVAL; | ||
1044 | |||
1045 | mu_monitor_rdlock (mbox->monitor); | ||
1046 | /* See if we have already this message. */ | ||
1047 | for (i = 0; i < mpd->pmessages_count; i++) | ||
1048 | { | ||
1049 | if (mpd->pmessages[i]) | ||
1050 | { | ||
1051 | if (mpd->pmessages[i]->num == msgno) | ||
1052 | { | ||
1053 | *pmsg = mpd->pmessages[i]->message; | ||
1054 | mu_monitor_unlock (mbox->monitor); | ||
1055 | return 0; | ||
1056 | } | ||
1057 | } | ||
1058 | } | ||
1059 | mu_monitor_unlock (mbox->monitor); | ||
1060 | |||
1061 | mpm = calloc (1, sizeof (*mpm)); | ||
1062 | if (mpm == NULL) | ||
1063 | return ENOMEM; | ||
1064 | |||
1065 | /* Back pointer. */ | ||
1066 | mpm->mpd = mpd; | ||
1067 | mpm->num = msgno; | ||
1068 | |||
1069 | /* Create the message. */ | ||
1070 | { | ||
1071 | mu_stream_t stream = NULL; | ||
1072 | if ((status = mu_message_create (&msg, mpm)) != 0 | ||
1073 | || (status = mu_stream_create (&stream, mbox->flags, msg)) != 0) | ||
1074 | { | ||
1075 | mu_stream_destroy (&stream, msg); | ||
1076 | mu_message_destroy (&msg, mpm); | ||
1077 | free (mpm); | ||
1078 | return status; | ||
1079 | } | ||
1080 | /* Help for the readline()s */ | ||
1081 | mu_stream_setbufsiz (stream, 128); | ||
1082 | mu_stream_set_read (stream, pop_message_read, msg); | ||
1083 | mu_stream_set_get_transport2 (stream, pop_message_transport, msg); | ||
1084 | mu_message_set_stream (msg, stream, mpm); | ||
1085 | mu_message_set_size (msg, pop_message_size, mpm); | ||
1086 | } | ||
1087 | |||
1088 | /* Create the header. */ | ||
1089 | { | ||
1090 | mu_header_t header = NULL; | ||
1091 | if ((status = mu_header_create (&header, NULL, 0, msg)) != 0) | ||
1092 | { | ||
1093 | mu_message_destroy (&msg, mpm); | ||
1094 | free (mpm); | ||
1095 | return status; | ||
1096 | } | ||
1097 | mu_header_set_fill (header, pop_top, msg); | ||
1098 | mu_message_set_header (msg, header, mpm); | ||
1099 | } | ||
1100 | |||
1101 | /* Create the attribute. */ | ||
1102 | { | ||
1103 | mu_attribute_t attribute; | ||
1104 | status = mu_attribute_create (&attribute, msg); | ||
1105 | if (status != 0) | ||
1106 | { | ||
1107 | mu_message_destroy (&msg, mpm); | ||
1108 | free (mpm); | ||
1109 | return status; | ||
1110 | } | ||
1111 | mu_attribute_set_get_flags (attribute, pop_get_attribute, msg); | ||
1112 | mu_attribute_set_set_flags (attribute, pop_set_attribute, msg); | ||
1113 | mu_attribute_set_unset_flags (attribute, pop_unset_attribute, msg); | ||
1114 | mu_message_set_attribute (msg, attribute, mpm); | ||
1115 | } | ||
1116 | |||
1117 | /* Create the body and its stream. */ | ||
1118 | { | ||
1119 | mu_body_t body = NULL; | ||
1120 | mu_stream_t stream = NULL; | ||
1121 | if ((status = mu_body_create (&body, msg)) != 0 | ||
1122 | || (status = mu_stream_create (&stream, mbox->flags, body)) != 0) | ||
1123 | { | ||
1124 | mu_body_destroy (&body, msg); | ||
1125 | mu_stream_destroy (&stream, body); | ||
1126 | mu_message_destroy (&msg, mpm); | ||
1127 | free (mpm); | ||
1128 | return status; | ||
1129 | } | ||
1130 | /* Helps for the readline()s */ | ||
1131 | mu_stream_setbufsiz (stream, 128); | ||
1132 | mu_stream_set_read (stream, pop_body_read, body); | ||
1133 | mu_stream_set_get_transport2 (stream, pop_body_transport, body); | ||
1134 | mu_body_set_size (body, pop_body_size, msg); | ||
1135 | mu_body_set_lines (body, pop_body_lines, msg); | ||
1136 | mu_body_set_stream (body, stream, msg); | ||
1137 | mu_message_set_body (msg, body, mpm); | ||
1138 | } | ||
1139 | |||
1140 | /* Set the UIDL call on the message. */ | ||
1141 | if (mpd->capa & CAPA_UIDL) | ||
1142 | mu_message_set_uidl (msg, pop_uidl, mpm); | ||
1143 | |||
1144 | /* Set the UID on the message. */ | ||
1145 | mu_message_set_uid (msg, pop_uid, mpm); | ||
1146 | |||
1147 | /* Add it to the list. */ | ||
1148 | mu_monitor_wrlock (mbox->monitor); | ||
1149 | { | ||
1150 | pop_message_t *m ; | ||
1151 | m = realloc (mpd->pmessages, (mpd->pmessages_count + 1)*sizeof (*m)); | ||
1152 | if (m == NULL) | ||
1153 | { | ||
1154 | mu_message_destroy (&msg, mpm); | ||
1155 | free (mpm); | ||
1156 | mu_monitor_unlock (mbox->monitor); | ||
1157 | return ENOMEM; | ||
1158 | } | ||
1159 | mpd->pmessages = m; | ||
1160 | mpd->pmessages[mpd->pmessages_count] = mpm; | ||
1161 | mpd->pmessages_count++; | ||
1162 | } | ||
1163 | mu_monitor_unlock (mbox->monitor); | ||
1164 | |||
1165 | /* Save The message pointer. */ | ||
1166 | mu_message_set_mailbox (msg, mbox, mpm); | ||
1167 | *pmsg = mpm->message = msg; | ||
1168 | |||
1169 | return 0; | ||
1170 | } | ||
1171 | |||
1172 | /* FIXME: Should use strtoumax ideally */ | ||
1173 | static int | ||
1174 | parse_answer0 (const char *buffer, size_t *n1, size_t *n2) | ||
1175 | { | ||
1176 | char *p; | ||
1177 | unsigned long m; | ||
1178 | if (strlen (buffer) < 3 || memcmp (buffer, "+OK", 3)) | ||
1179 | return 1; | ||
1180 | m = *n1 = strtoul (buffer + 3, &p, 10); | ||
1181 | if (!mu_isspace (*p) || m != *n1) | ||
1182 | return 1; | ||
1183 | m = *n2 = strtoul (p, &p, 10); | ||
1184 | if (!(*p == 0 || mu_isspace (*p)) || m != *n2) | ||
1185 | return 1; | ||
1186 | return 0; | ||
1187 | } | ||
1188 | |||
1189 | /* FIXME: Should use strtoumax ideally */ | ||
1190 | static int | ||
1191 | parse_answer1 (const char *buffer, size_t *n1, char *buf, size_t bufsize) | ||
1192 | { | ||
1193 | char *p; | ||
1194 | unsigned long m; | ||
1195 | if (strlen (buffer) < 3 || memcmp (buffer, "+OK", 3)) | ||
1196 | return 1; | ||
1197 | m = *n1 = strtoul (buffer + 3, &p, 0); | ||
1198 | if (!mu_isspace (*p) || m != *n1) | ||
1199 | return 1; | ||
1200 | while (*p && mu_isspace (*p)) | ||
1201 | p++; | ||
1202 | if (strlen (p) >= bufsize) | ||
1203 | return 1; | ||
1204 | strcpy (buf, p); | ||
1205 | return 0; | ||
1206 | } | ||
1207 | |||
1208 | /* There is no such thing in pop all messages should be consider recent. | ||
1209 | FIXME: We could cheat and peek at the status if it was not strip | ||
1210 | by the server ... */ | ||
1211 | static int | ||
1212 | pop_messages_recent (mu_mailbox_t mbox, size_t *precent) | ||
1213 | { | ||
1214 | return pop_messages_count (mbox, precent); | ||
1215 | } | ||
1216 | |||
1217 | /* There is no such thing in pop all messages should be consider unseen. | ||
1218 | FIXME: We could cheat and peek at the status if it was not strip | ||
1219 | by the server ... */ | ||
1220 | static int | ||
1221 | pop_message_unseen (mu_mailbox_t mbox, size_t *punseen) | ||
1222 | { | ||
1223 | size_t count = 0; | ||
1224 | int status = pop_messages_count (mbox, &count); | ||
1225 | if (status != 0) | ||
1226 | return status; | ||
1227 | if (punseen) | ||
1228 | *punseen = (count > 0) ? 1 : 0; | ||
1229 | return 0; | ||
1230 | } | ||
1231 | |||
1232 | /* How many messages we have. Done with STAT. */ | ||
1233 | static int | ||
1234 | pop_messages_count (mu_mailbox_t mbox, size_t *pcount) | ||
1235 | { | ||
1236 | pop_data_t mpd = mbox->data; | ||
1237 | int status; | ||
1238 | void *func = (void *)pop_messages_count; | ||
1239 | |||
1240 | if (mpd == NULL) | ||
1241 | return EINVAL; | ||
1242 | |||
1243 | /* Do not send a STAT if we know the answer. */ | ||
1244 | if (pop_is_updated (mbox)) | ||
1245 | { | ||
1246 | if (pcount) | ||
1247 | *pcount = mpd->messages_count; | ||
1248 | return 0; | ||
1249 | } | ||
1250 | |||
1251 | /* Flag busy. */ | ||
1252 | CHECK_BUSY (mbox, mpd, func, 0); | ||
1253 | |||
1254 | /* TRANSACTION state. */ | ||
1255 | switch (mpd->state) | ||
1256 | { | ||
1257 | case POP_NO_STATE: | ||
1258 | status = pop_writeline (mpd, "STAT\r\n"); | ||
1259 | CHECK_ERROR (mpd, status); | ||
1260 | MU_DEBUG (mbox->debug, MU_DEBUG_PROT, mpd->buffer); | ||
1261 | mpd->state = POP_STAT; | ||
1262 | |||
1263 | case POP_STAT: | ||
1264 | /* Send the STAT. */ | ||
1265 | status = pop_write (mpd); | ||
1266 | CHECK_EAGAIN (mpd, status); | ||
1267 | mpd->state = POP_STAT_ACK; | ||
1268 | |||
1269 | case POP_STAT_ACK: | ||
1270 | /* Get the ACK. */ | ||
1271 | status = pop_read_ack (mpd); | ||
1272 | CHECK_EAGAIN (mpd, status); | ||
1273 | MU_DEBUG (mbox->debug, MU_DEBUG_PROT, mpd->buffer); | ||
1274 | break; | ||
1275 | |||
1276 | default: | ||
1277 | /* | ||
1278 | mu_error ("pop_messages_count: unknown state"); | ||
1279 | */ | ||
1280 | break; | ||
1281 | } | ||
1282 | |||
1283 | |||
1284 | /* Parse the answer. */ | ||
1285 | |||
1286 | status = parse_answer0 (mpd->buffer, &mpd->messages_count, &mpd->size); | ||
1287 | /* Clear the state _after_ the scanf, since another thread could | ||
1288 | start writing over mpd->buffer. */ | ||
1289 | CLEAR_STATE (mpd); | ||
1290 | |||
1291 | if (status) | ||
1292 | return EIO; | ||
1293 | |||
1294 | if (pcount) | ||
1295 | *pcount = mpd->messages_count; | ||
1296 | mpd->is_updated = 1; | ||
1297 | return 0; | ||
1298 | } | ||
1299 | |||
1300 | /* Update and scanning. */ | ||
1301 | static int | ||
1302 | pop_is_updated (mu_mailbox_t mbox) | ||
1303 | { | ||
1304 | pop_data_t mpd = mbox->data; | ||
1305 | if (mpd == NULL) | ||
1306 | return 0; | ||
1307 | return mpd->is_updated; | ||
1308 | } | ||
1309 | |||
1310 | /* We just simulate by sending a notification for the total msgno. */ | ||
1311 | /* FIXME is message is set deleted should we sent a notif ? */ | ||
1312 | static int | ||
1313 | pop_scan (mu_mailbox_t mbox, size_t msgno, size_t *pcount) | ||
1314 | { | ||
1315 | int status; | ||
1316 | size_t i; | ||
1317 | size_t count = 0; | ||
1318 | |||
1319 | status = pop_messages_count (mbox, &count); | ||
1320 | if (pcount) | ||
1321 | *pcount = count; | ||
1322 | if (status != 0) | ||
1323 | return status; | ||
1324 | if (mbox->observable == NULL) | ||
1325 | return 0; | ||
1326 | for (i = msgno; i <= count; i++) | ||
1327 | { | ||
1328 | size_t tmp = i; | ||
1329 | if (mu_observable_notify (mbox->observable, MU_EVT_MESSAGE_ADD, | ||
1330 | &tmp) != 0) | ||
1331 | break; | ||
1332 | if (((i +1) % 10) == 0) | ||
1333 | { | ||
1334 | mu_observable_notify (mbox->observable, MU_EVT_MAILBOX_PROGRESS, | ||
1335 | NULL); | ||
1336 | } | ||
1337 | } | ||
1338 | return 0; | ||
1339 | } | ||
1340 | |||
1341 | /* This is where we actually send the DELE command. Meaning that when | ||
1342 | the attribute on the message is set deleted the comand DELE is not | ||
1343 | sent right away and if we did there is no way to mark a message undeleted | ||
1344 | beside closing down the connection without going to the update state via | ||
1345 | QUIT. So DELE is send only when in expunge. */ | ||
1346 | static int | ||
1347 | pop_expunge (mu_mailbox_t mbox) | ||
1348 | { | ||
1349 | pop_data_t mpd = mbox->data; | ||
1350 | size_t i; | ||
1351 | mu_attribute_t attr; | ||
1352 | int status; | ||
1353 | void *func = (void *)pop_expunge; | ||
1354 | |||
1355 | if (mpd == NULL) | ||
1356 | return EINVAL; | ||
1357 | |||
1358 | /* Busy ? */ | ||
1359 | CHECK_BUSY (mbox, mpd, func, 0); | ||
1360 | |||
1361 | for (i = (int)mpd->id; i < mpd->pmessages_count; mpd->id = ++i) | ||
1362 | { | ||
1363 | if (mu_message_get_attribute (mpd->pmessages[i]->message, &attr) == 0) | ||
1364 | { | ||
1365 | if (mu_attribute_is_deleted (attr)) | ||
1366 | { | ||
1367 | switch (mpd->state) | ||
1368 | { | ||
1369 | case POP_NO_STATE: | ||
1370 | status = pop_writeline (mpd, "DELE %lu\r\n", | ||
1371 | (unsigned long) | ||
1372 | mpd->pmessages[i]->num); | ||
1373 | CHECK_ERROR (mpd, status); | ||
1374 | MU_DEBUG (mbox->debug, MU_DEBUG_PROT, mpd->buffer); | ||
1375 | mpd->state = POP_DELE; | ||
1376 | |||
1377 | case POP_DELE: | ||
1378 | /* Send DELETE. */ | ||
1379 | status = pop_write (mpd); | ||
1380 | CHECK_EAGAIN (mpd, status); | ||
1381 | mpd->state = POP_DELE_ACK; | ||
1382 | |||
1383 | case POP_DELE_ACK: | ||
1384 | /* Ack Delete. */ | ||
1385 | status = pop_read_ack (mpd); | ||
1386 | CHECK_EAGAIN (mpd, status); | ||
1387 | MU_DEBUG (mbox->debug, MU_DEBUG_PROT, mpd->buffer); | ||
1388 | if (mu_c_strncasecmp (mpd->buffer, "+OK", 3) != 0) | ||
1389 | { | ||
1390 | CHECK_ERROR (mpd, ERANGE); | ||
1391 | } | ||
1392 | mpd->state = POP_NO_STATE; | ||
1393 | break; | ||
1394 | |||
1395 | default: | ||
1396 | /* mu_error ("pop_expunge: unknown state); */ | ||
1397 | break; | ||
1398 | } /* switch (state) */ | ||
1399 | } /* if mu_attribute_is_deleted() */ | ||
1400 | } /* mu_message_get_attribute() */ | ||
1401 | } /* for */ | ||
1402 | CLEAR_STATE (mpd); | ||
1403 | /* Invalidate. But Really they should shutdown the channel POP protocol | ||
1404 | is not meant for this like IMAP. */ | ||
1405 | mpd->is_updated = 0; | ||
1406 | return 0; | ||
1407 | } | ||
1408 | |||
1409 | /* Mailbox size ? It is part of the STAT command */ | ||
1410 | static int | ||
1411 | pop_get_size (mu_mailbox_t mbox, mu_off_t *psize) | ||
1412 | { | ||
1413 | pop_data_t mpd = mbox->data; | ||
1414 | int status = 0; | ||
1415 | |||
1416 | if (mpd == NULL) | ||
1417 | return EINVAL; | ||
1418 | |||
1419 | if (!pop_is_updated (mbox)) | ||
1420 | status = pop_messages_count (mbox, &mpd->size); | ||
1421 | if (psize) | ||
1422 | *psize = mpd->size; | ||
1423 | return status; | ||
1424 | } | ||
1425 | |||
1426 | /* Form the RFC: | ||
1427 | "It is important to note that the octet count for a message on the | ||
1428 | server host may differ from the octet count assigned to that message | ||
1429 | due to local conventions for designating end-of-line. Usually, | ||
1430 | during the AUTHORIZATION state of the POP3 session, the POP3 server | ||
1431 | can calculate the size of each message in octets when it opens the | ||
1432 | maildrop. For example, if the POP3 server host internally represents | ||
1433 | end-of-line as a single character, then the POP3 server simply counts | ||
1434 | each occurrence of this character in a message as two octets." | ||
1435 | |||
1436 | This is not perfect if we do not know the number of lines in the message | ||
1437 | then the octets returned will not be correct so we do our best. | ||
1438 | */ | ||
1439 | static int | ||
1440 | pop_message_size (mu_message_t msg, size_t *psize) | ||
1441 | { | ||
1442 | pop_message_t mpm = mu_message_get_owner (msg); | ||
1443 | pop_data_t mpd; | ||
1444 | int status = 0; | ||
1445 | void *func = (void *)pop_message_size; | ||
1446 | size_t num; | ||
1447 | |||
1448 | if (mpm == NULL) | ||
1449 | return EINVAL; | ||
1450 | |||
1451 | /* Did we have it already ? */ | ||
1452 | if (mpm->mu_message_size != 0) | ||
1453 | { | ||
1454 | *psize = mpm->mu_message_size; | ||
1455 | return 0; | ||
1456 | } | ||
1457 | |||
1458 | mpd = mpm->mpd; | ||
1459 | /* Busy ? */ | ||
1460 | CHECK_BUSY (mpd->mbox, mpd, func, msg); | ||
1461 | |||
1462 | /* Get the size. */ | ||
1463 | switch (mpd->state) | ||
1464 | { | ||
1465 | case POP_NO_STATE: | ||
1466 | status = pop_writeline (mpd, "LIST %lu\r\n", (unsigned long) mpm->num); | ||
1467 | CHECK_ERROR (mpd, status); | ||
1468 | MU_DEBUG (mpd->mbox->debug, MU_DEBUG_PROT, mpd->buffer); | ||
1469 | mpd->state = POP_LIST; | ||
1470 | |||
1471 | case POP_LIST: | ||
1472 | /* Send the LIST. */ | ||
1473 | status = pop_write (mpd); | ||
1474 | CHECK_EAGAIN (mpd, status); | ||
1475 | mpd->state = POP_LIST_ACK; | ||
1476 | |||
1477 | case POP_LIST_ACK: | ||
1478 | /* Resp from LIST. */ | ||
1479 | status = pop_read_ack (mpd); | ||
1480 | CHECK_EAGAIN (mpd, status); | ||
1481 | MU_DEBUG (mpd->mbox->debug, MU_DEBUG_PROT, mpd->buffer); | ||
1482 | break; | ||
1483 | |||
1484 | default: | ||
1485 | /* | ||
1486 | mu_error ("pop_message_size: unknown state"); | ||
1487 | */ | ||
1488 | break; | ||
1489 | } | ||
1490 | |||
1491 | /* FIXME */ | ||
1492 | status = parse_answer0 (mpd->buffer, &num, &mpm->mu_message_size); | ||
1493 | CLEAR_STATE (mpd); | ||
1494 | |||
1495 | if (status != 0) | ||
1496 | return MU_ERR_PARSE; | ||
1497 | |||
1498 | /* The size of the message is with the extra '\r' octet for everyline. | ||
1499 | Substract to get, hopefully, a good count. */ | ||
1500 | if (psize) | ||
1501 | *psize = mpm->mu_message_size - (mpm->header_lines + mpm->body_lines); | ||
1502 | return 0; | ||
1503 | } | ||
1504 | |||
1505 | /* Another source of trouble, POP only gives the size of the message | ||
1506 | not the size of subparts like headers, body etc .. Again we're doing | ||
1507 | our best with what we know but the only way to get a precise number | ||
1508 | is by dowloading the whole message. */ | ||
1509 | static int | ||
1510 | pop_body_size (mu_body_t body, size_t *psize) | ||
1511 | { | ||
1512 | mu_message_t msg = mu_body_get_owner (body); | ||
1513 | pop_message_t mpm = mu_message_get_owner (msg); | ||
1514 | |||
1515 | if (mpm == NULL) | ||
1516 | return EINVAL; | ||
1517 | |||
1518 | /* Did we have it already ? */ | ||
1519 | if (mpm->body_size != 0) | ||
1520 | { | ||
1521 | *psize = mpm->body_size; | ||
1522 | } | ||
1523 | else if (mpm->mu_message_size != 0) | ||
1524 | { | ||
1525 | /* Take a guest. */ | ||
1526 | *psize = mpm->mu_message_size - mpm->header_size - mpm->body_lines; | ||
1527 | } | ||
1528 | else | ||
1529 | *psize = 0; | ||
1530 | |||
1531 | return 0; | ||
1532 | } | ||
1533 | |||
1534 | /* Not know until the whole message get downloaded. */ | ||
1535 | static int | ||
1536 | pop_body_lines (mu_body_t body, size_t *plines) | ||
1537 | { | ||
1538 | mu_message_t msg = mu_body_get_owner (body); | ||
1539 | pop_message_t mpm = mu_message_get_owner (msg); | ||
1540 | if (mpm == NULL) | ||
1541 | return EINVAL; | ||
1542 | if (plines) | ||
1543 | *plines = mpm->body_lines; | ||
1544 | return 0; | ||
1545 | } | ||
1546 | |||
1547 | /* Pop does not have any command for this, We fake by reading the "Status: " | ||
1548 | header. But this is hackish some POP server(Qpopper) skip it. Also | ||
1549 | because we call mu_header_get_value the function may return EAGAIN... uncool. | ||
1550 | To put it another way, many servers simply remove the "Status:" header | ||
1551 | field, when you dowload a message, so a message will always look like | ||
1552 | new even if you already read it. There is also no way to set an attribute | ||
1553 | on remote mailbox via the POP server and many server once you do a RETR | ||
1554 | and in some cases a TOP will mark the message as read; "Status: RO" | ||
1555 | or maybe worst some ISP configure there servers to delete after | ||
1556 | the RETR some go as much as deleting after the TOP, since technicaly | ||
1557 | you can download a message via TOP without RET'reiving it. */ | ||
1558 | static int | ||
1559 | pop_get_attribute (mu_attribute_t attr, int *pflags) | ||
1560 | { | ||
1561 | mu_message_t msg = mu_attribute_get_owner (attr); | ||
1562 | pop_message_t mpm = mu_message_get_owner (msg); | ||
1563 | char hdr_status[64]; | 435 | char hdr_status[64]; |
1564 | mu_header_t header = NULL; | 436 | mu_header_t header = NULL; |
1565 | 437 | ||
... | @@ -1568,9 +440,11 @@ pop_get_attribute (mu_attribute_t attr, int *pflags) | ... | @@ -1568,9 +440,11 @@ pop_get_attribute (mu_attribute_t attr, int *pflags) |
1568 | if (mpm->attr_flags == 0) | 440 | if (mpm->attr_flags == 0) |
1569 | { | 441 | { |
1570 | hdr_status[0] = '\0'; | 442 | hdr_status[0] = '\0'; |
443 | |||
1571 | mu_message_get_header (mpm->message, &header); | 444 | mu_message_get_header (mpm->message, &header); |
1572 | mu_header_get_value (header, "Status", hdr_status, sizeof hdr_status, NULL); | 445 | mu_header_get_value (header, MU_HEADER_STATUS, |
1573 | mu_string_to_flags (hdr_status, &(mpm->attr_flags)); | 446 | hdr_status, sizeof hdr_status, NULL); |
447 | mu_string_to_flags (hdr_status, &mpm->attr_flags); | ||
1574 | } | 448 | } |
1575 | *pflags = mpm->attr_flags; | 449 | *pflags = mpm->attr_flags; |
1576 | return 0; | 450 | return 0; |
... | @@ -1579,8 +453,7 @@ pop_get_attribute (mu_attribute_t attr, int *pflags) | ... | @@ -1579,8 +453,7 @@ pop_get_attribute (mu_attribute_t attr, int *pflags) |
1579 | static int | 453 | static int |
1580 | pop_set_attribute (mu_attribute_t attr, int flags) | 454 | pop_set_attribute (mu_attribute_t attr, int flags) |
1581 | { | 455 | { |
1582 | mu_message_t msg = mu_attribute_get_owner (attr); | 456 | struct _pop3_message *mpm = mu_attribute_get_owner (attr); |
1583 | pop_message_t mpm = mu_message_get_owner (msg); | ||
1584 | 457 | ||
1585 | if (mpm == NULL) | 458 | if (mpm == NULL) |
1586 | return EINVAL; | 459 | return EINVAL; |
... | @@ -1591,8 +464,7 @@ pop_set_attribute (mu_attribute_t attr, int flags) | ... | @@ -1591,8 +464,7 @@ pop_set_attribute (mu_attribute_t attr, int flags) |
1591 | static int | 464 | static int |
1592 | pop_unset_attribute (mu_attribute_t attr, int flags) | 465 | pop_unset_attribute (mu_attribute_t attr, int flags) |
1593 | { | 466 | { |
1594 | mu_message_t msg = mu_attribute_get_owner (attr); | 467 | struct _pop3_message *mpm = mu_attribute_get_owner (attr); |
1595 | pop_message_t mpm = mu_message_get_owner (msg); | ||
1596 | 468 | ||
1597 | if (mpm == NULL) | 469 | if (mpm == NULL) |
1598 | return EINVAL; | 470 | return EINVAL; |
... | @@ -1600,575 +472,225 @@ pop_unset_attribute (mu_attribute_t attr, int flags) | ... | @@ -1600,575 +472,225 @@ pop_unset_attribute (mu_attribute_t attr, int flags) |
1600 | return 0; | 472 | return 0; |
1601 | } | 473 | } |
1602 | 474 | ||
1603 | /* Stub to call the fd from body object. */ | ||
1604 | static int | 475 | static int |
1605 | pop_body_transport (mu_stream_t stream, mu_transport_t *ptr, mu_transport_t *ptr2) | 476 | pop_create_attribute (struct _pop3_message *mpm) |
1606 | { | 477 | { |
1607 | mu_body_t body = mu_stream_get_owner (stream); | 478 | int status; |
1608 | mu_message_t msg = mu_body_get_owner (body); | 479 | mu_attribute_t attribute; |
1609 | pop_message_t mpm = mu_message_get_owner (msg); | ||
1610 | return pop_get_transport2 (mpm, ptr, ptr2); | ||
1611 | } | ||
1612 | 480 | ||
1613 | /* Stub to call the fd from message object. */ | 481 | status = mu_attribute_create (&attribute, mpm); |
1614 | static int | 482 | if (status) |
1615 | pop_message_transport (mu_stream_t stream, mu_transport_t *ptr, mu_transport_t *ptr2) | 483 | return status; |
1616 | { | ||
1617 | mu_message_t msg = mu_stream_get_owner (stream); | ||
1618 | pop_message_t mpm = mu_message_get_owner (msg); | ||
1619 | return pop_get_transport2 (mpm, ptr, ptr2); | ||
1620 | } | ||
1621 | 484 | ||
1622 | static int | 485 | mu_attribute_set_get_flags (attribute, pop_get_attribute, mpm); |
1623 | pop_get_transport2 (pop_message_t mpm, mu_transport_t *ptr, mu_transport_t *ptr2) | 486 | mu_attribute_set_set_flags (attribute, pop_set_attribute, mpm); |
1624 | { | 487 | mu_attribute_set_unset_flags (attribute, pop_unset_attribute, mpm); |
1625 | if (mpm && mpm->mpd && mpm->mpd->mbox) | 488 | mu_message_set_attribute (mpm->message, attribute, mpm); |
1626 | return mu_stream_get_transport2 (mpm->mpd->mbox->stream, ptr, ptr2); | 489 | return 0; |
1627 | return EINVAL; | ||
1628 | } | 490 | } |
1629 | 491 | ||
492 | /* ------------------------------------------------------------------------- */ | ||
493 | /* Body */ | ||
494 | |||
1630 | static int | 495 | static int |
1631 | pop_uid (mu_message_t msg, size_t *puid) | 496 | pop_create_body (struct _pop3_message *mpm) |
1632 | { | 497 | { |
1633 | pop_message_t mpm = mu_message_get_owner (msg); | 498 | /* FIXME */ |
1634 | if (puid) | 499 | return ENOSYS; |
1635 | *puid = mpm->num; | ||
1636 | return 0; | ||
1637 | } | 500 | } |
1638 | 501 | ||
1639 | /* Get the UIDL. The client should be prepared, since it may fail. UIDL is | 502 | |
1640 | optional on many POP servers. | 503 | /* FIXME: change prototype to avoid fixed size buffer */ |
1641 | FIXME: We should check the "mpd->capa & CAPA_UIDL" and fall back to | ||
1642 | a md5 scheme ? Or maybe check for "X-UIDL" a la Qpopper ? */ | ||
1643 | static int | 504 | static int |
1644 | pop_uidl (mu_message_t msg, char *buffer, size_t buflen, size_t *pnwriten) | 505 | pop_uidl (mu_message_t msg, char *buffer, size_t buflen, size_t *pnwriten) |
1645 | { | 506 | { |
1646 | pop_message_t mpm = mu_message_get_owner (msg); | 507 | struct _pop3_message *mpm = mu_message_get_owner (msg); |
1647 | pop_data_t mpd; | 508 | struct _pop3_mailbox *mpd = mpm->mpd; |
1648 | int status = 0; | 509 | size_t len; |
1649 | void *func = (void *)pop_uidl; | ||
1650 | size_t num; | ||
1651 | char uniq[MU_UIDL_BUFFER_SIZE]; | ||
1652 | |||
1653 | if (mpm == NULL) | ||
1654 | return EINVAL; | ||
1655 | 510 | ||
1656 | /* Is it cached ? */ | 511 | if (!mpm->uidl) |
1657 | if (mpm->uidl) | ||
1658 | { | 512 | { |
1659 | size_t len = strlen (mpm->uidl); | 513 | if (mu_pop3_capa_test (mpd->pop3, "UIDL", NULL) == 0) |
1660 | if (buffer) | ||
1661 | { | 514 | { |
1662 | buflen--; /* Leave space for the null. */ | 515 | int status = mu_pop3_uidl (mpd->pop3, mpm->num, &mpm->uidl); |
1663 | buflen = (len > buflen) ? buflen : len; | ||
1664 | memcpy (buffer, mpm->uidl, buflen); | ||
1665 | buffer[buflen] = '\0'; | ||
1666 | } | ||
1667 | else | ||
1668 | buflen = len; | ||
1669 | if (pnwriten) | ||
1670 | *pnwriten = buflen; | ||
1671 | return 0; | ||
1672 | } | ||
1673 | |||
1674 | mpd = mpm->mpd; | ||
1675 | |||
1676 | /* Busy ? */ | ||
1677 | CHECK_BUSY (mpd->mbox, mpd, func, 0); | ||
1678 | |||
1679 | /* Get the UIDL. */ | ||
1680 | switch (mpd->state) | ||
1681 | { | ||
1682 | case POP_NO_STATE: | ||
1683 | status = pop_writeline (mpd, "UIDL %lu\r\n", (unsigned long) mpm->num); | ||
1684 | CHECK_ERROR (mpd, status); | ||
1685 | MU_DEBUG (mpd->mbox->debug, MU_DEBUG_PROT, mpd->buffer); | ||
1686 | mpd->state = POP_UIDL; | ||
1687 | |||
1688 | case POP_UIDL: | ||
1689 | /* Send the UIDL. */ | ||
1690 | status = pop_write (mpd); | ||
1691 | CHECK_EAGAIN (mpd, status); | ||
1692 | mpd->state = POP_UIDL_ACK; | ||
1693 | |||
1694 | case POP_UIDL_ACK: | ||
1695 | /* Resp from UIDL. */ | ||
1696 | status = pop_read_ack (mpd); | ||
1697 | CHECK_EAGAIN (mpd, status); | ||
1698 | MU_DEBUG (mpd->mbox->debug, MU_DEBUG_PROT, mpd->buffer); | ||
1699 | break; | ||
1700 | |||
1701 | default: | ||
1702 | /* | ||
1703 | mu_error ("pop_uidl: unknown state"); | ||
1704 | */ | ||
1705 | break; | ||
1706 | } | ||
1707 | |||
1708 | /* FIXME: I should cache the result. */ | ||
1709 | *uniq = '\0'; | ||
1710 | status = parse_answer1 (mpd->buffer, &num, uniq, sizeof uniq); | ||
1711 | if (status) | 516 | if (status) |
1712 | { | ||
1713 | status = MU_ERR_PARSE; | ||
1714 | buflen = 0; | ||
1715 | } | ||
1716 | else | ||
1717 | { | ||
1718 | num = strlen (uniq); | ||
1719 | uniq[num - 1] = '\0'; /* Nuke newline. */ | ||
1720 | if (buffer) | ||
1721 | { | ||
1722 | buflen--; /* Leave space for the null. */ | ||
1723 | buflen = (buflen < num) ? buflen : num; | ||
1724 | memcpy (buffer, uniq, buflen); | ||
1725 | buffer [buflen] = '\0'; | ||
1726 | } | ||
1727 | else | ||
1728 | buflen = num - 1; /* Do not count newline. */ | ||
1729 | mpm->uidl = strdup (uniq); | ||
1730 | status = 0; | ||
1731 | } | ||
1732 | |||
1733 | CLEAR_STATE (mpd); | ||
1734 | |||
1735 | if (pnwriten) | ||
1736 | *pnwriten = buflen; | ||
1737 | return status; | 517 | return status; |
1738 | } | ||
1739 | |||
1740 | /* How we retrieve the headers. If it fails we jump to the pop_retr() | ||
1741 | code i.e. send a RETR and skip the body, ugly. | ||
1742 | NOTE: if the offset is different, flag an error, offset is meaningless | ||
1743 | on a socket but we better warn them, some stuff like mu_mime_t may try to | ||
1744 | read ahead, for example for the headers. */ | ||
1745 | static int | ||
1746 | pop_top (mu_header_t header, char *buffer, size_t buflen, | ||
1747 | mu_off_t offset, size_t *pnread) | ||
1748 | { | ||
1749 | mu_message_t msg = mu_header_get_owner (header); | ||
1750 | pop_message_t mpm = mu_message_get_owner (msg); | ||
1751 | pop_data_t mpd; | ||
1752 | size_t nread = 0; | ||
1753 | int status = 0; | ||
1754 | void *func = (void *)pop_top; | ||
1755 | |||
1756 | if (mpm == NULL) | ||
1757 | return EINVAL; | ||
1758 | |||
1759 | mpd = mpm->mpd; | ||
1760 | |||
1761 | /* Busy ? */ | ||
1762 | CHECK_BUSY (mpd->mbox, mpd, func, msg); | ||
1763 | |||
1764 | /* We start fresh then reset the sizes. */ | ||
1765 | if (mpd->state == POP_NO_STATE) | ||
1766 | mpm->header_size = 0; | ||
1767 | |||
1768 | /* Throw an error if trying to seek back. */ | ||
1769 | if ((size_t)offset < mpm->header_size) | ||
1770 | return ESPIPE; | ||
1771 | |||
1772 | /* Get the header. */ | ||
1773 | switch (mpd->state) | ||
1774 | { | ||
1775 | case POP_NO_STATE: | ||
1776 | if (mpd->capa & CAPA_TOP) | ||
1777 | { | ||
1778 | status = pop_writeline (mpd, "TOP %lu 0\r\n", | ||
1779 | (unsigned long) mpm->num); | ||
1780 | CHECK_ERROR (mpd, status); | ||
1781 | MU_DEBUG (mpd->mbox->debug, MU_DEBUG_PROT, mpd->buffer); | ||
1782 | mpd->state = POP_TOP; | ||
1783 | } | ||
1784 | else /* Fall back to RETR call. */ | ||
1785 | { | ||
1786 | mpd->state = POP_NO_STATE; | ||
1787 | mpm->skip_header = 0; | ||
1788 | mpm->skip_body = 1; | ||
1789 | return pop_retr (mpm, buffer, buflen, offset, pnread); | ||
1790 | } | ||
1791 | |||
1792 | case POP_TOP: | ||
1793 | /* Send the TOP. */ | ||
1794 | status = pop_write (mpd); | ||
1795 | CHECK_EAGAIN (mpd, status); | ||
1796 | mpd->state = POP_TOP_ACK; | ||
1797 | |||
1798 | case POP_TOP_ACK: | ||
1799 | /* Ack from TOP. */ | ||
1800 | status = pop_read_ack (mpd); | ||
1801 | CHECK_EAGAIN (mpd, status); | ||
1802 | MU_DEBUG (mpd->mbox->debug, MU_DEBUG_PROT, mpd->buffer); | ||
1803 | if (mu_c_strncasecmp (mpd->buffer, "+OK", 3) != 0) | ||
1804 | CHECK_ERROR (mpd, EINVAL); | ||
1805 | mpd->state = POP_TOP_RX; | ||
1806 | |||
1807 | case POP_TOP_RX: | ||
1808 | /* Get the header. */ | ||
1809 | do | ||
1810 | { | ||
1811 | /* Seek in position. */ | ||
1812 | ssize_t pos = offset - mpm->header_size; | ||
1813 | /* Do we need to fill up. */ | ||
1814 | if (mpd->nl == NULL || mpd->ptr == mpd->buffer) | ||
1815 | { | ||
1816 | status = pop_readline (mpd); | ||
1817 | CHECK_EAGAIN (mpd, status); | ||
1818 | mpm->header_lines++; | ||
1819 | } | 518 | } |
1820 | /* If we have to skip some data to get to the offset. */ | ||
1821 | if (pos > 0) | ||
1822 | nread = fill_buffer (mpd, NULL, pos); | ||
1823 | else | 519 | else |
1824 | nread = fill_buffer (mpd, buffer, buflen); | 520 | return ENOSYS; |
1825 | mpm->header_size += nread; | ||
1826 | } | 521 | } |
1827 | while (nread > 0 && (size_t)offset > mpm->header_size); | ||
1828 | break; | ||
1829 | |||
1830 | default: | ||
1831 | /* Probaly TOP was not supported so we have fall back to RETR. */ | ||
1832 | mpm->skip_header = 0; | ||
1833 | mpm->skip_body = 1; | ||
1834 | return pop_retr (mpm, buffer, buflen, offset, pnread); | ||
1835 | } /* switch (state) */ | ||
1836 | 522 | ||
1837 | if (nread == 0) | 523 | len = strlen (mpm->uidl); |
524 | if (buffer) | ||
1838 | { | 525 | { |
1839 | CLEAR_STATE (mpd); | 526 | buflen--; /* Leave space for the null. */ |
1840 | } | 527 | buflen = (len > buflen) ? buflen : len; |
1841 | if (pnread) | 528 | memcpy (buffer, mpm->uidl, buflen); |
1842 | *pnread = nread; | 529 | buffer[buflen] = 0; |
1843 | return 0; | 530 | } |
1844 | } | 531 | else |
1845 | 532 | buflen = len; | |
1846 | /* This is no longer use, see pop_top to retreive headers, we still | 533 | if (pnwriten) |
1847 | keep it around for debugging purposes. */ | 534 | *pnwriten = buflen; |
1848 | #if 0 | 535 | return 0; |
1849 | /* Stub to call pop_retr (). Call form the stream object of the header. */ | ||
1850 | static int | ||
1851 | pop_header_read (mu_header_t header, char *buffer, size_t buflen, mu_off_t offset, | ||
1852 | size_t *pnread) | ||
1853 | { | ||
1854 | mu_message_t msg = mu_header_get_owner (header); | ||
1855 | pop_message_t mpm = mu_message_get_owner (msg); | ||
1856 | pop_data_t mpd; | ||
1857 | void *func = (void *)pop_header_read; | ||
1858 | |||
1859 | if (mpm == NULL) | ||
1860 | return EINVAL; | ||
1861 | |||
1862 | mpd = mpm->mpd; | ||
1863 | |||
1864 | /* Busy ? */ | ||
1865 | CHECK_BUSY (mpd->mbox, mpd, func, msg); | ||
1866 | |||
1867 | /* We start fresh then reset the sizes. */ | ||
1868 | if (mpd->state == POP_NO_STATE) | ||
1869 | mpm->header_size = mpm->inbody = 0; | ||
1870 | |||
1871 | /* Throw an error if trying to seek back. */ | ||
1872 | if ((size_t)offset < mpm->header_size) | ||
1873 | return ESPIPE; | ||
1874 | |||
1875 | mpm->skip_header = 0; | ||
1876 | mpm->skip_body = 1; | ||
1877 | return pop_retr (mpm, buffer, buflen, offset, pnread); | ||
1878 | } | 536 | } |
1879 | #endif | ||
1880 | 537 | ||
1881 | /* Stub to call pop_retr (). Call from the stream object of the body. */ | ||
1882 | static int | 538 | static int |
1883 | pop_body_read (mu_stream_t is, char *buffer, size_t buflen, mu_off_t offset, | 539 | pop_uid (mu_message_t msg, size_t *puid) |
1884 | size_t *pnread) | ||
1885 | { | 540 | { |
1886 | mu_body_t body = mu_stream_get_owner (is); | 541 | struct _pop3_message *mpm = mu_message_get_owner (msg); |
1887 | mu_message_t msg = mu_body_get_owner (body); | 542 | if (puid) |
1888 | pop_message_t mpm = mu_message_get_owner (msg); | 543 | *puid = mpm->num; |
1889 | pop_data_t mpd; | 544 | return 0; |
1890 | void *func = (void *)pop_body_read; | ||
1891 | |||
1892 | if (mpm == NULL) | ||
1893 | return EINVAL; | ||
1894 | |||
1895 | mpd = mpm->mpd; | ||
1896 | |||
1897 | /* Busy ? */ | ||
1898 | CHECK_BUSY (mpd->mbox, mpd, func, msg); | ||
1899 | |||
1900 | /* We start fresh then reset the sizes. */ | ||
1901 | if (mpd->state == POP_NO_STATE) | ||
1902 | mpm->body_size = mpm->inbody = 0; | ||
1903 | |||
1904 | /* Can not seek back this a stream socket. */ | ||
1905 | if ((size_t)offset < mpm->body_size) | ||
1906 | return ESPIPE; | ||
1907 | |||
1908 | mpm->skip_header = 1; | ||
1909 | mpm->skip_body = 0; | ||
1910 | return pop_retr (mpm, buffer, buflen, offset, pnread); | ||
1911 | } | 545 | } |
1912 | 546 | ||
1913 | /* Stub to call pop_retr (), calling from the stream object of a message. */ | ||
1914 | static int | 547 | static int |
1915 | pop_message_read (mu_stream_t is, char *buffer, size_t buflen, mu_off_t offset, | 548 | pop_get_message (mu_mailbox_t mbox, size_t msgno, mu_message_t *pmsg) |
1916 | size_t *pnread) | ||
1917 | { | 549 | { |
1918 | mu_message_t msg = mu_stream_get_owner (is); | 550 | struct _pop3_mailbox *mpd = mbox->data; |
1919 | pop_message_t mpm = mu_message_get_owner (msg); | 551 | struct _pop3_message *mpm; |
1920 | pop_data_t mpd; | 552 | int status; |
1921 | void *func = (void *)pop_message_read; | ||
1922 | 553 | ||
1923 | if (mpm == NULL) | 554 | /* Sanity checks. */ |
555 | if (pmsg == NULL || mpd == NULL) | ||
1924 | return EINVAL; | 556 | return EINVAL; |
1925 | 557 | ||
1926 | mpd = mpm->mpd; | 558 | /* If we did not start a scanning yet do it now. */ |
1927 | 559 | if (!pop_is_updated (mbox)) | |
1928 | /* Busy ? */ | 560 | pop_scan (mbox, 1, NULL); |
1929 | CHECK_BUSY (mpd->mbox, mpd, func, msg); | ||
1930 | 561 | ||
1931 | /* We start fresh then reset the sizes. */ | 562 | if (msgno > mpd->msg_count) |
1932 | if (mpd->state == POP_NO_STATE) | 563 | return EINVAL; |
1933 | mpm->header_size = mpm->body_size = mpm->inbody = 0; | ||
1934 | 564 | ||
1935 | /* Can not seek back this is a stream socket. */ | 565 | if (!mpd->msg) |
1936 | if ((size_t)offset < (mpm->body_size + mpm->header_size)) | 566 | { |
1937 | return ESPIPE; | 567 | mpd->msg = calloc (mpd->msg_count, sizeof (mpd->msg[0])); |
568 | if (!mpd->msg) | ||
569 | return ENOMEM; | ||
570 | } | ||
571 | if (mpd->msg[msgno]) | ||
572 | { | ||
573 | *pmsg = mpd->msg[msgno]->message; | ||
574 | return 0; | ||
575 | } | ||
1938 | 576 | ||
1939 | mpm->skip_header = mpm->skip_body = 0; | 577 | mpm = calloc (1, sizeof (*mpm)); |
1940 | return pop_retr (mpm, buffer, buflen, offset, pnread); | 578 | if (mpm == NULL) |
1941 | } | 579 | return ENOMEM; |
1942 | 580 | ||
1943 | /* Little helper to fill the buffer without overflow. */ | 581 | /* Back pointer. */ |
1944 | static int | 582 | mpm->mpd = mpd; |
1945 | fill_buffer (pop_data_t mpd, char *buffer, size_t buflen) | 583 | mpm->num = msgno; |
1946 | { | ||
1947 | int nleft, n, nread = 0; | ||
1948 | 584 | ||
1949 | /* How much we can copy ? */ | 585 | status = pop_create_message (mpm, mpd); |
1950 | n = mpd->ptr - mpd->buffer; | 586 | if (status) |
1951 | nleft = buflen - n; | 587 | { |
588 | free (mpm); | ||
589 | return status; | ||
590 | } | ||
1952 | 591 | ||
1953 | /* We got more then requested. */ | 592 | do |
1954 | if (nleft < 0) | ||
1955 | { | 593 | { |
1956 | size_t sentinel; | 594 | status = pop_create_header (mpm); |
1957 | nread = buflen; | 595 | if (status) |
1958 | sentinel = mpd->ptr - (mpd->buffer + nread); | 596 | break; |
1959 | if (buffer) | 597 | status = pop_create_attribute (mpm); |
1960 | memcpy (buffer, mpd->buffer, nread); | 598 | if (status) |
1961 | memmove (mpd->buffer, mpd->buffer + nread, sentinel); | 599 | break; |
1962 | mpd->ptr = mpd->buffer + sentinel; | 600 | status = pop_create_body (mpm); |
1963 | } | 601 | } |
1964 | else | 602 | while (0); |
603 | |||
604 | if (status) | ||
1965 | { | 605 | { |
1966 | /* Drain our buffer. */; | 606 | mu_message_destroy (&mpm->message, mpm); |
1967 | nread = n; | 607 | free (mpm); |
1968 | if (buffer) | 608 | return status; |
1969 | memcpy (buffer, mpd->buffer, nread); | ||
1970 | mpd->ptr = mpd->buffer; | ||
1971 | } | 609 | } |
1972 | 610 | ||
1973 | return nread; | 611 | if (mu_pop3_capa_test (mpd->pop3, "UIDL", NULL) == 0) |
612 | mu_message_set_uidl (mpm->message, pop_uidl, mpm); | ||
613 | |||
614 | mu_message_set_uid (mpm->message, pop_uid, mpm); | ||
615 | |||
616 | mpd->msg[msgno] = mpm; | ||
617 | mu_message_set_mailbox (mpm->message, mbox, mpm); | ||
618 | *pmsg = mpm->message; | ||
619 | return 0; | ||
1974 | } | 620 | } |
1975 | 621 | ||
1976 | /* The heart of most funtions. Send the RETR and skip different parts. */ | ||
1977 | static int | 622 | static int |
1978 | pop_retr (pop_message_t mpm, char *buffer, size_t buflen, | 623 | _pop3_mailbox_init (mu_mailbox_t mbox, int pops) |
1979 | mu_off_t offset MU_ARG_UNUSED, size_t *pnread) | ||
1980 | { | 624 | { |
1981 | pop_data_t mpd; | 625 | struct _pop3_mailbox *mpd; |
1982 | size_t nread = 0; | ||
1983 | int status = 0; | 626 | int status = 0; |
1984 | size_t oldbuflen = buflen; | ||
1985 | 627 | ||
1986 | mpd = mpm->mpd; | 628 | /* Allocate specifics for pop data. */ |
629 | mpd = mbox->data = calloc (1, sizeof (*mpd)); | ||
630 | if (mbox->data == NULL) | ||
631 | return ENOMEM; | ||
1987 | 632 | ||
1988 | if (pnread) | 633 | mpd->pop3 = NULL; |
1989 | *pnread = nread; | ||
1990 | 634 | ||
1991 | /* Take care of the obvious. */ | 635 | mpd->mbox = mbox; /* Back pointer. */ |
1992 | if (buffer == NULL || buflen == 0) | 636 | mpd->pops = pops; |
1993 | { | ||
1994 | CLEAR_STATE (mpd); | ||
1995 | return 0; | ||
1996 | } | ||
1997 | 637 | ||
1998 | /* pop_retr() is not call directly so we assume that the locks were set. */ | 638 | /* Initialize the structure. */ |
639 | mbox->_destroy = pop_destroy; | ||
1999 | 640 | ||
2000 | switch (mpd->state) | 641 | mbox->_open = pop_open; |
2001 | { | 642 | mbox->_close = pop_close; |
2002 | case POP_NO_STATE: | 643 | mbox->_messages_count = pop_messages_count; |
2003 | mpm->body_lines = mpm->body_size = 0; | 644 | /* FIXME: There is no way to retrieve the number of recent messages via |
2004 | status = pop_writeline (mpd, "RETR %lu\r\n", | 645 | POP3 protocol, so we consider all messages recent. */ |
2005 | (unsigned long) mpm->num); | 646 | mbox->_messages_recent = pop_messages_count; |
2006 | MU_DEBUG (mpd->mbox->debug, MU_DEBUG_PROT, mpd->buffer); | 647 | mbox->_is_updated = pop_is_updated; |
2007 | CHECK_ERROR (mpd, status); | 648 | mbox->_scan = pop_scan; |
2008 | mpd->state = POP_RETR; | 649 | mbox->_message_unseen = pop_message_unseen; |
2009 | 650 | mbox->_get_size = pop_get_size; | |
2010 | case POP_RETR: | ||
2011 | /* Send the RETR command. */ | ||
2012 | status = pop_write (mpd); | ||
2013 | CHECK_EAGAIN (mpd, status); | ||
2014 | mpd->state = POP_RETR_ACK; | ||
2015 | |||
2016 | case POP_RETR_ACK: | ||
2017 | /* RETR ACK. */ | ||
2018 | status = pop_read_ack (mpd); | ||
2019 | CHECK_EAGAIN (mpd, status); | ||
2020 | MU_DEBUG (mpd->mbox->debug, MU_DEBUG_PROT, mpd->buffer); | ||
2021 | |||
2022 | if (mu_c_strncasecmp (mpd->buffer, "+OK", 3) != 0) | ||
2023 | { | ||
2024 | CHECK_ERROR (mpd, EACCES); | ||
2025 | } | ||
2026 | mpd->state = POP_RETR_RX_HDR; | ||
2027 | 651 | ||
2028 | case POP_RETR_RX_HDR: | 652 | #if 0 //FIXME |
2029 | /* Skip/Take the header. */ | 653 | /* Messages. */ |
2030 | while (!mpm->inbody) | 654 | mbox->_get_message = pop_get_message; |
2031 | { | 655 | mbox->_expunge = pop_expunge; |
2032 | /* Do we need to fill up. */ | ||
2033 | if (mpd->nl == NULL || mpd->ptr == mpd->buffer) | ||
2034 | { | ||
2035 | status = pop_readline (mpd); | ||
2036 | if (status != 0) | ||
2037 | { | ||
2038 | /* Do we have something in the buffer flush it first. */ | ||
2039 | if (buflen != oldbuflen) | ||
2040 | return 0; | ||
2041 | CHECK_EAGAIN (mpd, status); | ||
2042 | } | ||
2043 | mpm->header_lines++; | ||
2044 | } | ||
2045 | /* Oops !! Hello houston we have a major problem here. */ | ||
2046 | if (mpd->buffer[0] == '\0') | ||
2047 | { | ||
2048 | /* Still Do the right thing. */ | ||
2049 | if (buflen != oldbuflen) | ||
2050 | { | ||
2051 | CLEAR_STATE (mpd); | ||
2052 | } | ||
2053 | else | ||
2054 | mpd->state = POP_STATE_DONE; | ||
2055 | return 0; | ||
2056 | } | ||
2057 | /* The problem is that we are using RETR instead of TOP to retreive | ||
2058 | headers, i.e the server contacted does not support it. So we | ||
2059 | have to make sure that the next line really means end of the | ||
2060 | headers. Still some cases we may loose. But 99.9% of POPD | ||
2061 | encounter support TOP. In the 0.1% case get GNU pop3d, or the | ||
2062 | hack below will suffice. */ | ||
2063 | if (mpd->buffer[0] == '\n' && mpd->buffer[1] == '\0') | ||
2064 | mpm->inbody = 1; /* break out of the while. */ | ||
2065 | if (!mpm->skip_header) | ||
2066 | { | ||
2067 | ssize_t pos = offset - mpm->header_size; | ||
2068 | if (pos > 0) | ||
2069 | { | ||
2070 | nread = fill_buffer (mpd, NULL, pos); | ||
2071 | mpm->header_size += nread; | ||
2072 | } | ||
2073 | else | ||
2074 | { | ||
2075 | nread = fill_buffer (mpd, buffer, buflen); | ||
2076 | mpm->header_size += nread; | ||
2077 | if (pnread) | ||
2078 | *pnread += nread; | ||
2079 | buflen -= nread ; | ||
2080 | if (buflen > 0) | ||
2081 | buffer += nread; | ||
2082 | else | ||
2083 | return 0; | ||
2084 | } | ||
2085 | } | ||
2086 | else | ||
2087 | mpd->ptr = mpd->buffer; | ||
2088 | } | ||
2089 | mpd->state = POP_RETR_RX_BODY; | ||
2090 | 656 | ||
2091 | case POP_RETR_RX_BODY: | ||
2092 | /* Start/Take the body. */ | ||
2093 | while (mpm->inbody) | ||
2094 | { | ||
2095 | /* Do we need to fill up. */ | ||
2096 | if (mpd->nl == NULL || mpd->ptr == mpd->buffer) | ||
2097 | { | ||
2098 | status = pop_readline (mpd); | ||
2099 | if (status != 0) | ||
2100 | { | ||
2101 | /* Flush The Buffer ? */ | ||
2102 | if (buflen != oldbuflen) | ||
2103 | return 0; | ||
2104 | CHECK_EAGAIN (mpd, status); | ||
2105 | } | ||
2106 | mpm->body_lines++; | ||
2107 | } | ||
2108 | 657 | ||
2109 | if (mpd->buffer[0] == '\0') | ||
2110 | mpm->inbody = 0; /* Breakout of the while. */ | ||
2111 | 658 | ||
2112 | if (!mpm->skip_body) | 659 | /* Set our properties. */ |
2113 | { | ||
2114 | /* If we did not skip the header, it means that we are | ||
2115 | downloading the entire message and the mu_header_size should be | ||
2116 | part of the offset count. */ | ||
2117 | ssize_t pos = offset - (mpm->body_size + ((mpm->skip_header) ? | ||
2118 | 0 : mpm->header_size)); | ||
2119 | if (pos > 0) | ||
2120 | { | ||
2121 | nread = fill_buffer (mpd, NULL, pos); | ||
2122 | mpm->body_size += nread; | ||
2123 | } | ||
2124 | else | ||
2125 | { | ||
2126 | nread = fill_buffer (mpd, buffer, buflen); | ||
2127 | mpm->body_size += nread; | ||
2128 | if (pnread) | ||
2129 | *pnread += nread; | ||
2130 | buflen -= nread ; | ||
2131 | if (buflen > 0) | ||
2132 | buffer += nread; | ||
2133 | else | ||
2134 | return 0; | ||
2135 | } | ||
2136 | } | ||
2137 | else | ||
2138 | { | 660 | { |
2139 | mpm->body_size += (mpd->ptr - mpd->buffer); | 661 | mu_property_t property = NULL; |
2140 | mpd->ptr = mpd->buffer; | 662 | mu_mailbox_get_property (mbox, &property); |
2141 | } | 663 | mu_property_set_value (property, "TYPE", "POP3", 1); |
2142 | } | 664 | } |
2143 | mpm->mu_message_size = mpm->body_size + mpm->header_size; | ||
2144 | mpd->state = POP_STATE_DONE; | ||
2145 | /* Return here earlier, because we want to return nread = 0 to notify | ||
2146 | the callee that we've finish, since there is already data | ||
2147 | we have to return them first and _then_ tell them its finish. If we | ||
2148 | don't we will start over again by sending another RETR. */ | ||
2149 | if (buflen != oldbuflen) | ||
2150 | return 0; | ||
2151 | 665 | ||
2152 | case POP_STATE_DONE: | 666 | #endif |
2153 | /* A convenient break, this is here we can return 0, we're done. */ | 667 | /* Hack! POP does not really have a folder. */ |
668 | mbox->folder->data = mbox; | ||
669 | return status; | ||
670 | } | ||
2154 | 671 | ||
2155 | default: | 672 | int |
2156 | /* mu_error ("pop_retr: unknown state"); */ | 673 | _mailbox_pop_init (mu_mailbox_t mbox) |
2157 | break; | 674 | { |
2158 | } /* Switch state. */ | 675 | return _pop3_mailbox_init (mbox, 0); |
676 | } | ||
2159 | 677 | ||
2160 | CLEAR_STATE (mpd); | 678 | int |
2161 | mpm->skip_header = mpm->skip_body = 0; | 679 | _mailbox_pops_init (mu_mailbox_t mbox) |
2162 | return 0; | 680 | { |
681 | return _pop3_mailbox_init (mbox, 1); | ||
2163 | } | 682 | } |
2164 | 683 | ||
684 | |||
685 | /* Authentication */ | ||
686 | |||
2165 | /* Extract the User from the URL or the ticket. */ | 687 | /* Extract the User from the URL or the ticket. */ |
2166 | static int | 688 | static int |
2167 | pop_get_user (mu_authority_t auth) | 689 | pop_get_user (mu_authority_t auth) |
2168 | { | 690 | { |
2169 | mu_folder_t folder = mu_authority_get_owner (auth); | 691 | mu_folder_t folder = mu_authority_get_owner (auth); |
2170 | mu_mailbox_t mbox = folder->data; | 692 | mu_mailbox_t mbox = folder->data; |
2171 | pop_data_t mpd = mbox->data; | 693 | struct _pop3_mailbox *mpd = mbox->data; |
2172 | mu_ticket_t ticket = NULL; | 694 | mu_ticket_t ticket = NULL; |
2173 | int status; | 695 | int status; |
2174 | /* Fetch the user from them. */ | 696 | /* Fetch the user from them. */ |
... | @@ -2195,7 +717,7 @@ pop_get_passwd (mu_authority_t auth) | ... | @@ -2195,7 +717,7 @@ pop_get_passwd (mu_authority_t auth) |
2195 | { | 717 | { |
2196 | mu_folder_t folder = mu_authority_get_owner (auth); | 718 | mu_folder_t folder = mu_authority_get_owner (auth); |
2197 | mu_mailbox_t mbox = folder->data; | 719 | mu_mailbox_t mbox = folder->data; |
2198 | pop_data_t mpd = mbox->data; | 720 | struct _pop3_mailbox *mpd = mbox->data; |
2199 | mu_ticket_t ticket = NULL; | 721 | mu_ticket_t ticket = NULL; |
2200 | int status; | 722 | int status; |
2201 | 723 | ||
... | @@ -2212,217 +734,60 @@ pop_get_passwd (mu_authority_t auth) | ... | @@ -2212,217 +734,60 @@ pop_get_passwd (mu_authority_t auth) |
2212 | return 0; | 734 | return 0; |
2213 | } | 735 | } |
2214 | 736 | ||
2215 | 737 | int | |
2216 | static char * | 738 | _pop_apop (mu_authority_t auth) |
2217 | pop_get_timestamp (pop_data_t mpd) | ||
2218 | { | ||
2219 | char *right, *left; | ||
2220 | char *timestamp = NULL; | ||
2221 | size_t len; | ||
2222 | |||
2223 | len = strlen (mpd->greeting_banner); | ||
2224 | right = memchr (mpd->greeting_banner, '<', len); | ||
2225 | if (right) | ||
2226 | { | ||
2227 | len = len - (right - mpd->greeting_banner); | ||
2228 | left = memchr (right, '>', len); | ||
2229 | if (left) | ||
2230 | { | ||
2231 | len = left - right + 1; | ||
2232 | timestamp = calloc (len + 1, 1); | ||
2233 | if (timestamp != NULL) | ||
2234 | { | ||
2235 | memcpy (timestamp, right, len); | ||
2236 | } | ||
2237 | } | ||
2238 | } | ||
2239 | return timestamp; | ||
2240 | } | ||
2241 | |||
2242 | /* Make the MD5 string. */ | ||
2243 | static int | ||
2244 | pop_get_md5 (pop_data_t mpd) | ||
2245 | { | 739 | { |
2246 | struct mu_md5_ctx md5context; | 740 | mu_folder_t folder = mu_authority_get_owner (auth); |
2247 | unsigned char md5digest[16]; | 741 | mu_mailbox_t mbox = folder->data; |
2248 | char digest[64]; /* Really it just has to be 32 + 1(null). */ | 742 | struct _pop3_mailbox *mpd = mbox->data; |
2249 | char *tmp; | 743 | int status; |
2250 | size_t n; | ||
2251 | char *timestamp; | ||
2252 | 744 | ||
2253 | timestamp = pop_get_timestamp (mpd); | 745 | status = pop_get_user (auth); |
2254 | if (timestamp == NULL) | 746 | if (status) |
2255 | return EINVAL; | 747 | return status; |
2256 | 748 | ||
2257 | mu_md5_init_ctx (&md5context); | 749 | /* Fetch the secret from them. */ |
2258 | mu_md5_process_bytes (timestamp, strlen (timestamp), &md5context); | 750 | status = pop_get_passwd (auth); |
2259 | mu_md5_process_bytes (mu_secret_password (mpd->secret), | 751 | if (status) |
2260 | mu_secret_length (mpd->secret), | 752 | return status; |
2261 | &md5context); | 753 | status = mu_pop3_apop (mpd->pop3, mpd->user, |
754 | mu_secret_password (mpd->secret)); | ||
2262 | mu_secret_password_unref (mpd->secret); | 755 | mu_secret_password_unref (mpd->secret); |
2263 | mu_secret_unref (mpd->secret); | 756 | mu_secret_unref (mpd->secret); |
2264 | mpd->secret = NULL; | 757 | mpd->secret = NULL; |
2265 | mu_md5_finish_ctx (&md5context, md5digest); | 758 | free (mpd->user); |
2266 | 759 | mpd->user = NULL; | |
2267 | for (tmp = digest, n = 0; n < 16; n++, tmp += 2) | ||
2268 | sprintf (tmp, "%02x", md5digest[n]); | ||
2269 | *tmp = '\0'; | ||
2270 | free (timestamp); | ||
2271 | mpd->digest = strdup (digest); | ||
2272 | return 0; | ||
2273 | } | ||
2274 | |||
2275 | /* GRRRRR!! We can not use sleep in the library since this we'll | ||
2276 | muck up any alarm() done by the user. */ | ||
2277 | static int | ||
2278 | pop_sleep (int seconds) | ||
2279 | { | ||
2280 | struct timeval tval; | ||
2281 | tval.tv_sec = seconds; | ||
2282 | tval.tv_usec = 0; | ||
2283 | return select (1, NULL, NULL, NULL, &tval); | ||
2284 | } | ||
2285 | |||
2286 | /* C99 says that a conforming implementation of snprintf () should return the | ||
2287 | number of char that would have been call but many old GNU/Linux && BSD | ||
2288 | implementations return -1 on error. Worse QnX/Neutrino actually does not | ||
2289 | put the terminal null char. So let's try to cope. */ | ||
2290 | static int | ||
2291 | pop_writeline (pop_data_t mpd, const char *format, ...) | ||
2292 | { | ||
2293 | int len; | ||
2294 | va_list ap; | ||
2295 | int done = 1; | ||
2296 | |||
2297 | if (mpd->buffer == NULL) | ||
2298 | return EINVAL; | ||
2299 | va_start(ap, format); | ||
2300 | do | ||
2301 | { | ||
2302 | len = vsnprintf (mpd->buffer, mpd->buflen - 1, format, ap); | ||
2303 | if (len < 0 || len >= (int)mpd->buflen | ||
2304 | || !memchr (mpd->buffer, '\0', len + 1)) | ||
2305 | { | ||
2306 | mpd->buflen *= 2; | ||
2307 | mpd->buffer = realloc (mpd->buffer, mpd->buflen); | ||
2308 | if (mpd->buffer == NULL) | ||
2309 | return ENOMEM; | ||
2310 | done = 0; | ||
2311 | } | ||
2312 | else | ||
2313 | done = 1; | ||
2314 | } | ||
2315 | while (!done); | ||
2316 | va_end(ap); | ||
2317 | mpd->ptr = mpd->buffer + len; | ||
2318 | return 0; | ||
2319 | } | ||
2320 | |||
2321 | /* A socket may write less then expected and we have to cope with nonblocking. | ||
2322 | if the write failed we keep track and restart where left. */ | ||
2323 | static int | ||
2324 | pop_write (pop_data_t mpd) | ||
2325 | { | ||
2326 | int status = 0; | ||
2327 | if (mpd->ptr > mpd->buffer) | ||
2328 | { | ||
2329 | size_t len; | ||
2330 | size_t n = 0; | ||
2331 | len = mpd->ptr - mpd->buffer; | ||
2332 | status = mu_stream_write (mpd->mbox->stream, mpd->buffer, len, 0, &n); | ||
2333 | if (status == 0) | ||
2334 | { | ||
2335 | memmove (mpd->buffer, mpd->buffer + n, len - n); | ||
2336 | mpd->ptr -= n; | ||
2337 | } | ||
2338 | } | ||
2339 | else | ||
2340 | mpd->ptr = mpd->buffer; | ||
2341 | return status; | ||
2342 | } | ||
2343 | |||
2344 | /* Call readline and reset the mpd->ptr to the buffer, signalling that we have | ||
2345 | done the read to completion. */ | ||
2346 | static int | ||
2347 | pop_read_ack (pop_data_t mpd) | ||
2348 | { | ||
2349 | int status = pop_readline (mpd); | ||
2350 | if (status == 0) | ||
2351 | mpd->ptr = mpd->buffer; | ||
2352 | return status; | 760 | return status; |
2353 | } | 761 | } |
2354 | 762 | ||
2355 | /* Read a complete line form the pop server. Transform CRLF to LF, remove | 763 | int |
2356 | the stuff byte termination octet ".", put a null in the buffer | 764 | _pop_user (mu_authority_t auth) |
2357 | when done. */ | ||
2358 | static int | ||
2359 | pop_readline (pop_data_t mpd) | ||
2360 | { | 765 | { |
2361 | size_t n = 0; | 766 | mu_folder_t folder = mu_authority_get_owner (auth); |
2362 | size_t total = mpd->ptr - mpd->buffer; | 767 | mu_mailbox_t mbox = folder->data; |
768 | struct _pop3_mailbox *mpd = mbox->data; | ||
2363 | int status; | 769 | int status; |
2364 | 770 | ||
2365 | /* Must get a full line before bailing out. */ | 771 | status = pop_get_user (auth); |
2366 | do | 772 | if (status) |
2367 | { | ||
2368 | status = mu_stream_readline (mpd->mbox->stream, mpd->buffer + total, | ||
2369 | mpd->buflen - total, mpd->offset, &n); | ||
2370 | if (status != 0) | ||
2371 | return status; | 773 | return status; |
2372 | 774 | status = mu_pop3_user (mpd->pop3, mpd->user); | |
2373 | /* The server went away: It maybe a timeout and some pop server | 775 | if (status == 0) |
2374 | does not send the -ERR. Consider this like an error. */ | ||
2375 | if (n == 0) | ||
2376 | return EIO; | ||
2377 | |||
2378 | total += n; | ||
2379 | mpd->offset += n; | ||
2380 | mpd->nl = memchr (mpd->buffer, '\n', total); | ||
2381 | if (mpd->nl == NULL) /* Do we have a full line. */ | ||
2382 | { | ||
2383 | /* Allocate a bigger buffer ? */ | ||
2384 | if (total >= mpd->buflen -1) | ||
2385 | { | ||
2386 | mpd->buflen *= 2; | ||
2387 | mpd->buffer = realloc (mpd->buffer, mpd->buflen + 1); | ||
2388 | if (mpd->buffer == NULL) | ||
2389 | return ENOMEM; | ||
2390 | } | ||
2391 | } | ||
2392 | mpd->ptr = mpd->buffer + total; | ||
2393 | } | ||
2394 | while (mpd->nl == NULL); | ||
2395 | |||
2396 | /* When examining a multi-line response, the client checks to see if the | ||
2397 | line begins with the termination octet "."(DOT). If yes and if octets | ||
2398 | other than CRLF follow, the first octet of the line (the termination | ||
2399 | octet) is stripped away. */ | ||
2400 | if (total >= 3 && mpd->buffer[0] == '.') | ||
2401 | { | ||
2402 | if (mpd->buffer[1] != '\r' && mpd->buffer[2] != '\n') | ||
2403 | { | 776 | { |
2404 | memmove (mpd->buffer, mpd->buffer + 1, total - 1); | 777 | /* Fetch the secret from them. */ |
2405 | mpd->ptr--; | 778 | status = pop_get_passwd (auth); |
2406 | mpd->nl--; | 779 | if (status == 0) |
2407 | } | ||
2408 | /* And if CRLF immediately follows the termination character, then the | ||
2409 | response from the POP server is ended and the line containing | ||
2410 | ".CRLF" is not considered part of the multi-line response. */ | ||
2411 | else if (mpd->buffer[1] == '\r' && mpd->buffer[2] == '\n') | ||
2412 | { | 780 | { |
2413 | mpd->buffer[0] = '\0'; | 781 | status = mu_pop3_pass (mpd->pop3, mu_secret_password (mpd->secret)); |
2414 | mpd->ptr = mpd->buffer; | 782 | mu_secret_password_unref (mpd->secret); |
2415 | mpd->nl = NULL; | 783 | mu_secret_unref (mpd->secret); |
2416 | } | 784 | mpd->secret = NULL; |
2417 | } | 785 | } |
2418 | /* \r\n --> \n\0, conversion. */ | ||
2419 | if (mpd->nl > mpd->buffer) | ||
2420 | { | ||
2421 | *(mpd->nl - 1) = '\n'; | ||
2422 | *(mpd->nl) = '\0'; | ||
2423 | mpd->ptr = mpd->nl; | ||
2424 | } | 786 | } |
2425 | return 0; | 787 | free (mpd->user); |
788 | mpd->user = NULL; | ||
789 | return status; | ||
2426 | } | 790 | } |
2427 | 791 | ||
2428 | #endif | 792 | |
793 | #endif /* ENABLE_POP */ | ... | ... |
... | @@ -26,7 +26,7 @@ | ... | @@ -26,7 +26,7 @@ |
26 | #include <mailutils/sys/pop3.h> | 26 | #include <mailutils/sys/pop3.h> |
27 | 27 | ||
28 | int | 28 | int |
29 | pop3_capa_test (mu_pop3_t pop3, const char *name, const char **pret) | 29 | mu_pop3_capa_test (mu_pop3_t pop3, const char *name, const char **pret) |
30 | { | 30 | { |
31 | int rc; | 31 | int rc; |
32 | 32 | ... | ... |
... | @@ -27,10 +27,10 @@ | ... | @@ -27,10 +27,10 @@ |
27 | #include <mailutils/sys/pop3.h> | 27 | #include <mailutils/sys/pop3.h> |
28 | 28 | ||
29 | int | 29 | int |
30 | mu_pop3_stat (mu_pop3_t pop3, unsigned *msg_count, size_t *size) | 30 | mu_pop3_stat (mu_pop3_t pop3, size_t *msg_count, mu_off_t *size) |
31 | { | 31 | { |
32 | int status; | 32 | int status; |
33 | unsigned long lv; | 33 | unsigned long lv, count; |
34 | 34 | ||
35 | if (pop3 == NULL || msg_count == NULL) | 35 | if (pop3 == NULL || msg_count == NULL) |
36 | return EINVAL; | 36 | return EINVAL; |
... | @@ -55,7 +55,8 @@ mu_pop3_stat (mu_pop3_t pop3, unsigned *msg_count, size_t *size) | ... | @@ -55,7 +55,8 @@ mu_pop3_stat (mu_pop3_t pop3, unsigned *msg_count, size_t *size) |
55 | *msg_count = 0; | 55 | *msg_count = 0; |
56 | lv = 0; | 56 | lv = 0; |
57 | /* FIXME: Error checking */ | 57 | /* FIXME: Error checking */ |
58 | sscanf (pop3->ackbuf, "+OK %d %lu", msg_count, &lv); | 58 | sscanf (pop3->ackbuf, "+OK %lu %lu", &count, &lv); |
59 | *msg_count = count; | ||
59 | *size = lv; | 60 | *size = lv; |
60 | break; | 61 | break; |
61 | 62 | ... | ... |
... | @@ -205,6 +205,25 @@ mu_opool_size (mu_opool_t opool) | ... | @@ -205,6 +205,25 @@ mu_opool_size (mu_opool_t opool) |
205 | return size; | 205 | return size; |
206 | } | 206 | } |
207 | 207 | ||
208 | size_t | ||
209 | mu_opool_copy (mu_opool_t opool, void *buf, size_t size) | ||
210 | { | ||
211 | char *cp = buf; | ||
212 | size_t total = 0; | ||
213 | struct mu_opool_bucket *p; | ||
214 | |||
215 | for (p = opool->head; p && total < size; p = p->next) | ||
216 | { | ||
217 | size_t cpsize = size - total; | ||
218 | if (cpsize > p->level) | ||
219 | cpsize = p->level; | ||
220 | memcpy (cp, p->buf, cpsize); | ||
221 | cp += cpsize; | ||
222 | total += cpsize; | ||
223 | } | ||
224 | return total; | ||
225 | } | ||
226 | |||
208 | int | 227 | int |
209 | mu_opool_coalesce (mu_opool_t opool, size_t *psize) | 228 | mu_opool_coalesce (mu_opool_t opool, size_t *psize) |
210 | { | 229 | { | ... | ... |
... | @@ -201,6 +201,9 @@ _xscript_ctl (struct _mu_stream *str, int op, void *arg) | ... | @@ -201,6 +201,9 @@ _xscript_ctl (struct _mu_stream *str, int op, void *arg) |
201 | case MU_IOCTL_SWAP_STREAM: | 201 | case MU_IOCTL_SWAP_STREAM: |
202 | if (!arg) | 202 | if (!arg) |
203 | return EINVAL; | 203 | return EINVAL; |
204 | if (!sp->transport) | ||
205 | status = ENOSYS; | ||
206 | else | ||
204 | status = mu_stream_ioctl (sp->transport, op, arg); | 207 | status = mu_stream_ioctl (sp->transport, op, arg); |
205 | if (status == EINVAL || status == ENOSYS) | 208 | if (status == EINVAL || status == ENOSYS) |
206 | { | 209 | { | ... | ... |
-
Please register or sign in to post a comment