Commit b9355608 b93556087280eab726c507486415385f996ebf54 by Sergey Poznyakoff

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.
1 parent d22b2199
...@@ -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 {
......