Fix FSF address (Tobias Mueller, #470445)
[platform/upstream/evolution-data-server.git] / camel / providers / pop3 / camel-pop3-stream.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; fill-column: 160 -*-
2  *
3  * Author:
4  *  Michael Zucchi <notzed@ximian.com>
5  *
6  * Copyright 2002 Ximian, Inc. (www.ximian.com)
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of version 2 of the GNU Lesser General Public
10  * License as published by the Free Software Foundation.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
20  * USA
21  */
22
23 /* This is *identical* to the camel-nntp-stream, so should probably
24    work out a way to merge them */
25
26
27 #ifdef HAVE_CONFIG_H
28 #include <config.h>
29 #endif
30
31 #include <errno.h>
32 #include <stdio.h>
33 #include <string.h>
34
35 #include <glib.h>
36
37 #include "camel-pop3-stream.h"
38
39 extern int camel_verbose_debug;
40 #define dd(x) (camel_verbose_debug?(x):0)
41
42 static CamelObjectClass *parent_class = NULL;
43
44 /* Returns the class for a CamelStream */
45 #define CS_CLASS(so) CAMEL_POP3_STREAM_CLASS(CAMEL_OBJECT_GET_CLASS(so))
46
47 #define CAMEL_POP3_STREAM_SIZE (4096)
48 #define CAMEL_POP3_STREAM_LINE (1024) /* maximum line size */
49
50 static int
51 stream_fill(CamelPOP3Stream *is)
52 {
53         int left = 0;
54
55         if (is->source) {
56                 left = is->end - is->ptr;
57                 memcpy(is->buf, is->ptr, left);
58                 is->end = is->buf + left;
59                 is->ptr = is->buf;
60                 left = camel_stream_read(is->source, is->end, CAMEL_POP3_STREAM_SIZE - (is->end - is->buf));
61                 if (left > 0) {
62                         is->end += left;
63                         is->end[0] = '\n';
64                         return is->end - is->ptr;
65                 } else {
66                         dd(printf("POP3_STREAM_FILL(ERROR): '%s'\n", strerror (errno)));
67                         return -1;
68                 }
69         }
70
71         return 0;
72 }
73
74 static ssize_t
75 stream_read(CamelStream *stream, char *buffer, size_t n)
76 {
77         CamelPOP3Stream *is = (CamelPOP3Stream *)stream;
78         char *o, *oe;
79         unsigned char *p, *e, c;
80         int state;
81
82         if (is->mode != CAMEL_POP3_STREAM_DATA || n == 0)
83                 return 0;
84
85         o = buffer;
86         oe = buffer + n;
87         state = is->state;
88
89         /* Need to copy/strip '.'s and whatnot */
90         p = is->ptr;
91         e = is->end;
92
93         switch(state) {
94         state_0:
95         case 0:         /* start of line, always read at least 3 chars */
96                 while (e - p < 3) {
97                         is->ptr = p;
98                         if (stream_fill(is) == -1)
99                                 return -1;
100                         p = is->ptr;
101                         e = is->end;
102                 }
103                 if (p[0] == '.') {
104                         if (p[1] == '\r' && p[2] == '\n') {
105                                 is->ptr = p+3;
106                                 is->mode = CAMEL_POP3_STREAM_EOD;
107                                 is->state = 0;
108                                 dd(printf("POP3_STREAM_READ(%d):\n%.*s\n", (int)(o-buffer), (int)(o-buffer), buffer));
109                                 return o-buffer;
110                         }
111                         p++;
112                 }
113                 state = 1;
114                 /* FALLS THROUGH */
115         case 1:         /* looking for next sol */
116                 while (o < oe) {
117                         c = *p++;
118                         if (c == '\n') {
119                                 /* end of input sentinal check */
120                                 if (p > e) {
121                                         is->ptr = e;
122                                         if (stream_fill(is) == -1)
123                                                 return -1;
124                                         p = is->ptr;
125                                         e = is->end;
126                                 } else {
127                                         *o++ = '\n';
128                                         state = 0;
129                                         goto state_0;
130                                 }
131                         } else if (c != '\r') {
132                                 *o++ = c;
133                         }
134                 }
135                 break;
136         }
137
138         is->ptr = p;
139         is->state = state;
140
141         dd(printf("POP3_STREAM_READ(%d):\n%.*s\n", (int)(o-buffer), (int)(o-buffer), buffer));
142
143         return o-buffer;
144 }
145
146 static ssize_t
147 stream_write(CamelStream *stream, const char *buffer, size_t n)
148 {
149         CamelPOP3Stream *is = (CamelPOP3Stream *)stream;
150         
151         if (strncmp (buffer, "PASS ", 5) != 0)
152                 dd(printf("POP3_STREAM_WRITE(%d):\n%.*s\n", (int)n, (int)n, buffer));
153         else
154                 dd(printf("POP3_STREAM_WRITE(%d):\nPASS xxxxxxxx\n", (int)n));
155         
156         return camel_stream_write(is->source, buffer, n);
157 }
158
159 static int
160 stream_close(CamelStream *stream)
161 {
162         /* nop? */
163         return 0;
164 }
165
166 static int
167 stream_flush(CamelStream *stream)
168 {
169         /* nop? */
170         return 0;
171 }
172
173 static gboolean
174 stream_eos(CamelStream *stream)
175 {
176         CamelPOP3Stream *is = (CamelPOP3Stream *)stream;
177
178         return is->mode != CAMEL_POP3_STREAM_DATA;
179 }
180
181 static int
182 stream_reset(CamelStream *stream)
183 {
184         /* nop?  reset literal mode? */
185         return 0;
186 }
187
188 static void
189 camel_pop3_stream_class_init (CamelStreamClass *camel_pop3_stream_class)
190 {
191         CamelStreamClass *camel_stream_class = (CamelStreamClass *)camel_pop3_stream_class;
192
193         parent_class = camel_type_get_global_classfuncs( CAMEL_OBJECT_TYPE );
194
195         /* virtual method definition */
196         camel_stream_class->read = stream_read;
197         camel_stream_class->write = stream_write;
198         camel_stream_class->close = stream_close;
199         camel_stream_class->flush = stream_flush;
200         camel_stream_class->eos = stream_eos;
201         camel_stream_class->reset = stream_reset;
202 }
203
204 static void
205 camel_pop3_stream_init(CamelPOP3Stream *is, CamelPOP3StreamClass *isclass)
206 {
207         /* +1 is room for appending a 0 if we need to for a line */
208         is->ptr = is->end = is->buf = g_malloc(CAMEL_POP3_STREAM_SIZE+1);
209         is->lineptr = is->linebuf = g_malloc(CAMEL_POP3_STREAM_LINE+1);
210         is->lineend = is->linebuf + CAMEL_POP3_STREAM_LINE;
211
212         /* init sentinal */
213         is->ptr[0] = '\n';
214
215         is->state = 0;
216         is->mode = CAMEL_POP3_STREAM_LINE;
217 }
218
219 static void
220 camel_pop3_stream_finalise(CamelPOP3Stream *is)
221 {
222         g_free(is->buf);
223         g_free(is->linebuf);
224         if (is->source)
225                 camel_object_unref((CamelObject *)is->source);
226 }
227
228 CamelType
229 camel_pop3_stream_get_type (void)
230 {
231         static CamelType camel_pop3_stream_type = CAMEL_INVALID_TYPE;
232
233         if (camel_pop3_stream_type == CAMEL_INVALID_TYPE) {
234                 camel_pop3_stream_type = camel_type_register( camel_stream_get_type(),
235                                                             "CamelPOP3Stream",
236                                                             sizeof( CamelPOP3Stream ),
237                                                             sizeof( CamelPOP3StreamClass ),
238                                                             (CamelObjectClassInitFunc) camel_pop3_stream_class_init,
239                                                             NULL,
240                                                             (CamelObjectInitFunc) camel_pop3_stream_init,
241                                                             (CamelObjectFinalizeFunc) camel_pop3_stream_finalise );
242         }
243
244         return camel_pop3_stream_type;
245 }
246
247 /**
248  * camel_pop3_stream_new:
249  *
250  * Returns a NULL stream.  A null stream is always at eof, and
251  * always returns success for all reads and writes.
252  *
253  * Return value: the stream
254  **/
255 CamelStream *
256 camel_pop3_stream_new(CamelStream *source)
257 {
258         CamelPOP3Stream *is;
259
260         is = (CamelPOP3Stream *)camel_object_new(camel_pop3_stream_get_type ());
261         camel_object_ref((CamelObject *)source);
262         is->source = source;
263
264         return (CamelStream *)is;
265 }
266
267 /* Get one line from the pop3 stream */
268 int
269 camel_pop3_stream_line(CamelPOP3Stream *is, unsigned char **data, unsigned int *len)
270 {
271         register unsigned char c, *p, *o, *oe;
272         int newlen, oldlen;
273         unsigned char *e;
274
275         if (is->mode == CAMEL_POP3_STREAM_EOD) {
276                 *data = is->linebuf;
277                 *len = 0;
278                 return 0;
279         }
280
281         o = is->linebuf;
282         oe = is->lineend - 1;
283         p = is->ptr;
284         e = is->end;
285
286         /* Data mode, convert leading '..' to '.', and stop when we reach a solitary '.' */
287         if (is->mode == CAMEL_POP3_STREAM_DATA) {
288                 /* need at least 3 chars in buffer */
289                 while (e-p < 3) {
290                         is->ptr = p;
291                         if (stream_fill(is) == -1)
292                                 return -1;
293                         p = is->ptr;
294                         e = is->end;
295                 }
296
297                 /* check for isolated '.\r\n' or begging of line '.' */
298                 if (p[0] == '.') {
299                         if (p[1] == '\r' && p[2] == '\n') {
300                                 is->ptr = p+3;
301                                 is->mode = CAMEL_POP3_STREAM_EOD;
302                                 *data = is->linebuf;
303                                 *len = 0;
304                                 is->linebuf[0] = 0;
305
306                                 dd(printf("POP3_STREAM_LINE(END)\n"));
307
308                                 return 0;
309                         }
310                         p++;
311                 }
312         }
313
314         while (1) {
315                 while (o < oe) {
316                         c = *p++;
317                         if (c == '\n') {
318                                 /* sentinal? */
319                                 if (p> e) {
320                                         is->ptr = e;
321                                         if (stream_fill(is) == -1)
322                                                 return -1;
323                                         p = is->ptr;
324                                         e = is->end;
325                                 } else {
326                                         is->ptr = p;
327                                         *data = is->linebuf;
328                                         *len = o - is->linebuf;
329                                         *o = 0;
330
331                                         dd(printf("POP3_STREAM_LINE(%d): '%s'\n", *len, *data));
332
333                                         return 1;
334                                 }
335                         } else if (c != '\r') {
336                                 *o++ = c;
337                         }
338                 }
339
340                 /* limit this for bad server data? */
341                 oldlen = o - is->linebuf;
342                 newlen = (is->lineend - is->linebuf) * 3 / 2;
343                 is->lineptr = is->linebuf = g_realloc(is->linebuf, newlen);
344                 is->lineend = is->linebuf + newlen;
345                 oe = is->lineend - 1;
346                 o = is->linebuf + oldlen;
347         }
348
349         return -1;
350 }
351
352 /* returns -1 on error, 0 if last lot of data, >0 if more remaining */
353 int camel_pop3_stream_gets(CamelPOP3Stream *is, unsigned char **start, unsigned int *len)
354 {
355         int max;
356         unsigned char *end;
357
358         *len = 0;
359
360         max = is->end - is->ptr;
361         if (max == 0) {
362                 max = stream_fill(is);
363                 if (max <= 0)
364                         return max;
365         }
366
367         *start = is->ptr;
368         end = memchr(is->ptr, '\n', max);
369         if (end)
370                 max = (end - is->ptr) + 1;
371         *start = is->ptr;
372         *len = max;
373         is->ptr += max;
374
375         dd(printf("POP3_STREAM_GETS(%s,%d): '%.*s'\n", end==NULL?"more":"last", *len, (int)*len, *start));
376
377         return end == NULL?1:0;
378 }
379
380 void camel_pop3_stream_set_mode(CamelPOP3Stream *is, camel_pop3_stream_mode_t mode)
381 {
382         is->mode = mode;
383 }
384
385 /* returns -1 on erorr, 0 if last data, >0 if more data left */
386 int camel_pop3_stream_getd(CamelPOP3Stream *is, unsigned char **start, unsigned int *len)
387 {
388         unsigned char *p, *e, *s;
389         int state;
390
391         *len = 0;
392
393         if (is->mode == CAMEL_POP3_STREAM_EOD)
394                 return 0;
395
396         if (is->mode == CAMEL_POP3_STREAM_LINE) {
397                 g_warning("pop3_stream reading data in line mode\n");
398                 return 0;
399         }
400
401         state = is->state;
402         p = is->ptr;
403         e = is->end;
404
405         while (e - p < 3) {
406                 is->ptr = p;
407                 if (stream_fill(is) == -1)
408                         return -1;
409                 p = is->ptr;
410                 e = is->end;
411         }
412
413         s = p;
414
415         do {
416                 switch(state) {
417                 case 0:
418                         /* check leading '.', ... */
419                         if (p[0] == '.') {
420                                 if (p[1] == '\r' && p[2] == '\n') {
421                                         is->ptr = p+3;
422                                         *len = p-s;
423                                         *start = s;
424                                         is->mode = CAMEL_POP3_STREAM_EOD;
425                                         is->state = 0;
426
427                                         dd(printf("POP3_STREAM_GETD(%s,%d): '%.*s'\n", "last", *len, (int)*len, *start));
428
429                                         return 0;
430                                 }
431
432                                 /* If at start, just skip '.', else return data upto '.' but skip it */
433                                 if (p == s) {
434                                         s++;
435                                         p++;
436                                 } else {
437                                         is->ptr = p+1;
438                                         *len = p-s;
439                                         *start = s;
440                                         is->state = 1;
441
442                                         dd(printf("POP3_STREAM_GETD(%s,%d): '%.*s'\n", "more", *len, (int)*len, *start));
443
444                                         return 1;
445                                 }
446                         }
447                         state = 1;
448                 case 1:
449                         /* Scan for sentinal */
450                         while ((*p++)!='\n')
451                                 ;
452
453                         if (p > e) {
454                                 p = e;
455                         } else {
456                                 state = 0;
457                         }
458                         break;
459                 }
460         } while ((e-p) >= 3);
461
462         is->state = state;
463         is->ptr = p;
464         *len = p-s;
465         *start = s;
466
467         dd(printf("POP3_STREAM_GETD(%s,%d): '%.*s'\n", "more", *len, (int)*len, *start));
468
469         return 1;
470 }