Fix FSF address (Tobias Mueller, #470445)
[platform/upstream/evolution-data-server.git] / camel / providers / nntp / camel-nntp-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 1999, 2000 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 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <errno.h>
28 #include <stdio.h>
29 #include <string.h>
30
31 #include <glib.h>
32
33 #include "camel-debug.h"
34
35 #include "camel-nntp-stream.h"
36
37 #define dd(x) (camel_debug("nntp:stream")?(x):0)
38
39 #ifndef ECONNRESET
40 #define ECONNRESET EIO
41 #endif
42
43 static CamelObjectClass *parent_class = NULL;
44
45 /* Returns the class for a CamelStream */
46 #define CS_CLASS(so) CAMEL_NNTP_STREAM_CLASS(CAMEL_OBJECT_GET_CLASS(so))
47
48 #define CAMEL_NNTP_STREAM_SIZE (4096)
49 #define CAMEL_NNTP_STREAM_LINE_SIZE (1024) /* maximum line size */
50
51 static int
52 stream_fill(CamelNNTPStream *is)
53 {
54         int left = 0;
55
56         if (is->source) {
57                 left = is->end - is->ptr;
58                 memcpy(is->buf, is->ptr, left);
59                 is->end = is->buf + left;
60                 is->ptr = is->buf;
61                 left = camel_stream_read(is->source, is->end, CAMEL_NNTP_STREAM_SIZE - (is->end - is->buf));
62                 if (left > 0) {
63                         is->end += left;
64                         is->end[0] = '\n';
65                         return is->end - is->ptr;
66                 } else {
67                         if (left == 0)
68                                 errno = ECONNRESET;
69                         dd(printf("NNTP_STREAM_FILL(ERROR): %d - '%s'\n", left, strerror(errno)));
70                         return -1;
71                 }
72         }
73
74         return 0;
75 }
76
77 static ssize_t
78 stream_read(CamelStream *stream, char *buffer, size_t n)
79 {
80         CamelNNTPStream *is = (CamelNNTPStream *)stream;
81         char *o, *oe;
82         unsigned char *p, *e, c;
83         int state;
84
85         if (is->mode != CAMEL_NNTP_STREAM_DATA || n == 0)
86                 return 0;
87
88         o = buffer;
89         oe = buffer + n;
90         state = is->state;
91
92         /* Need to copy/strip '.'s and whatnot */
93         p = is->ptr;
94         e = is->end;
95
96         switch(state) {
97         state_0:
98         case 0:         /* start of line, always read at least 3 chars */
99                 while (e - p < 3) {
100                         is->ptr = p;
101                         if (stream_fill(is) == -1)
102                                 return -1;
103                         p = is->ptr;
104                         e = is->end;
105                 }
106                 if (p[0] == '.') {
107                         if (p[1] == '\r' && p[2] == '\n') {
108                                 is->ptr = p+3;
109                                 is->mode = CAMEL_NNTP_STREAM_EOD;
110                                 is->state = 0;
111                                 dd(printf("NNTP_STREAM_READ(%d):\n%.*s\n", (int)(o-buffer), (int)(o-buffer), buffer));
112                                 return o-buffer;
113                         }
114                         p++;
115                 }
116                 state = 1;
117                 /* FALLS THROUGH */
118         case 1:         /* looking for next sol */
119                 while (o < oe) {
120                         c = *p++;
121                         if (c == '\n') {
122                                 /* end of input sentinal check */
123                                 if (p > e) {
124                                         is->ptr = e;
125                                         if (stream_fill(is) == -1)
126                                                 return -1;
127                                         p = is->ptr;
128                                         e = is->end;
129                                 } else {
130                                         *o++ = '\n';
131                                         state = 0;
132                                         goto state_0;
133                                 }
134                         } else if (c != '\r') {
135                                 *o++ = c;
136                         }
137                 }
138                 break;
139         }
140
141         is->ptr = p;
142         is->state = state;
143
144         dd(printf("NNTP_STREAM_READ(%d):\n%.*s\n", (int)(o-buffer), (int)(o-buffer), buffer));
145
146         return o-buffer;
147 }
148
149 static ssize_t
150 stream_write(CamelStream *stream, const char *buffer, size_t n)
151 {
152         CamelNNTPStream *is = (CamelNNTPStream *)stream;
153
154         return camel_stream_write(is->source, buffer, n);
155 }
156
157 static int
158 stream_close(CamelStream *stream)
159 {
160         /* nop? */
161         return 0;
162 }
163
164 static int
165 stream_flush(CamelStream *stream)
166 {
167         /* nop? */
168         return 0;
169 }
170
171 static gboolean
172 stream_eos(CamelStream *stream)
173 {
174         CamelNNTPStream *is = (CamelNNTPStream *)stream;
175
176         return is->mode != CAMEL_NNTP_STREAM_DATA;
177 }
178
179 static int
180 stream_reset(CamelStream *stream)
181 {
182         /* nop?  reset literal mode? */
183         return 0;
184 }
185
186 static void
187 camel_nntp_stream_class_init (CamelStreamClass *camel_nntp_stream_class)
188 {
189         CamelStreamClass *camel_stream_class = (CamelStreamClass *)camel_nntp_stream_class;
190
191         parent_class = camel_type_get_global_classfuncs( CAMEL_OBJECT_TYPE );
192
193         /* virtual method definition */
194         camel_stream_class->read = stream_read;
195         camel_stream_class->write = stream_write;
196         camel_stream_class->close = stream_close;
197         camel_stream_class->flush = stream_flush;
198         camel_stream_class->eos = stream_eos;
199         camel_stream_class->reset = stream_reset;
200 }
201
202 static void
203 camel_nntp_stream_init(CamelNNTPStream *is, CamelNNTPStreamClass *isclass)
204 {
205         /* +1 is room for appending a 0 if we need to for a line */
206         is->ptr = is->end = is->buf = g_malloc(CAMEL_NNTP_STREAM_SIZE+1);
207         is->lineptr = is->linebuf = g_malloc(CAMEL_NNTP_STREAM_LINE_SIZE+1);
208         is->lineend = is->linebuf + CAMEL_NNTP_STREAM_LINE_SIZE;
209
210         /* init sentinal */
211         is->ptr[0] = '\n';
212
213         is->state = 0;
214         is->mode = CAMEL_NNTP_STREAM_LINE;
215 }
216
217 static void
218 camel_nntp_stream_finalise(CamelNNTPStream *is)
219 {
220         g_free(is->buf);
221         g_free(is->linebuf);
222         if (is->source)
223                 camel_object_unref((CamelObject *)is->source);
224 }
225
226 CamelType
227 camel_nntp_stream_get_type (void)
228 {
229         static CamelType camel_nntp_stream_type = CAMEL_INVALID_TYPE;
230
231         if (camel_nntp_stream_type == CAMEL_INVALID_TYPE) {
232                 camel_nntp_stream_type = camel_type_register( camel_stream_get_type(),
233                                                             "CamelNNTPStream",
234                                                             sizeof( CamelNNTPStream ),
235                                                             sizeof( CamelNNTPStreamClass ),
236                                                             (CamelObjectClassInitFunc) camel_nntp_stream_class_init,
237                                                             NULL,
238                                                             (CamelObjectInitFunc) camel_nntp_stream_init,
239                                                             (CamelObjectFinalizeFunc) camel_nntp_stream_finalise );
240         }
241
242         return camel_nntp_stream_type;
243 }
244
245 /**
246  * camel_nntp_stream_new:
247  *
248  * Returns a NULL stream.  A null stream is always at eof, and
249  * always returns success for all reads and writes.
250  *
251  * Return value: the stream
252  **/
253 CamelStream *
254 camel_nntp_stream_new(CamelStream *source)
255 {
256         CamelNNTPStream *is;
257
258         is = (CamelNNTPStream *)camel_object_new(camel_nntp_stream_get_type ());
259         camel_object_ref((CamelObject *)source);
260         is->source = source;
261
262         return (CamelStream *)is;
263 }
264
265 /* Get one line from the nntp stream */
266 int
267 camel_nntp_stream_line(CamelNNTPStream *is, unsigned char **data, unsigned int *len)
268 {
269         register unsigned char c, *p, *o, *oe;
270         int newlen, oldlen;
271         unsigned char *e;
272
273         if (is->mode == CAMEL_NNTP_STREAM_EOD) {
274                 *data = is->linebuf;
275                 *len = 0;
276                 return 0;
277         }
278
279         o = is->linebuf;
280         oe = is->lineend - 1;
281         p = is->ptr;
282         e = is->end;
283
284         /* Data mode, convert leading '..' to '.', and stop when we reach a solitary '.' */
285         if (is->mode == CAMEL_NNTP_STREAM_DATA) {
286                 /* need at least 3 chars in buffer */
287                 while (e-p < 3) {
288                         is->ptr = p;
289                         if (stream_fill(is) == -1)
290                                 return -1;
291                         p = is->ptr;
292                         e = is->end;
293                 }
294
295                 /* check for isolated '.\r\n' or begging of line '.' */
296                 if (p[0] == '.') {
297                         if (p[1] == '\r' && p[2] == '\n') {
298                                 is->ptr = p+3;
299                                 is->mode = CAMEL_NNTP_STREAM_EOD;
300                                 *data = is->linebuf;
301                                 *len = 0;
302                                 is->linebuf[0] = 0;
303
304                                 dd(printf("NNTP_STREAM_LINE(END)\n"));
305
306                                 return 0;
307                         }
308                         p++;
309                 }
310         }
311
312         while (1) {
313                 while (o < oe) {
314                         c = *p++;
315                         if (c == '\n') {
316                                 /* sentinal? */
317                                 if (p> e) {
318                                         is->ptr = e;
319                                         if (stream_fill(is) == -1)
320                                                 return -1;
321                                         p = is->ptr;
322                                         e = is->end;
323                                 } else {
324                                         is->ptr = p;
325                                         *data = is->linebuf;
326                                         *len = o - is->linebuf;
327                                         *o = 0;
328
329                                         dd(printf("NNTP_STREAM_LINE(%d): '%s'\n", *len, *data));
330
331                                         return 1;
332                                 }
333                         } else if (c != '\r') {
334                                 *o++ = c;
335                         }
336                 }
337
338                 /* limit this for bad server data? */
339                 oldlen = o - is->linebuf;
340                 newlen = (is->lineend - is->linebuf) * 3 / 2;
341                 is->lineptr = is->linebuf = g_realloc(is->linebuf, newlen);
342                 is->lineend = is->linebuf + newlen;
343                 oe = is->lineend - 1;
344                 o = is->linebuf + oldlen;
345         }
346
347         return -1;
348 }
349
350 /* returns -1 on error, 0 if last lot of data, >0 if more remaining */
351 int camel_nntp_stream_gets(CamelNNTPStream *is, unsigned char **start, unsigned int *len)
352 {
353         int max;
354         unsigned char *end;
355
356         *len = 0;
357
358         max = is->end - is->ptr;
359         if (max == 0) {
360                 max = stream_fill(is);
361                 if (max <= 0)
362                         return max;
363         }
364
365         *start = is->ptr;
366         end = memchr(is->ptr, '\n', max);
367         if (end)
368                 max = (end - is->ptr) + 1;
369         *start = is->ptr;
370         *len = max;
371         is->ptr += max;
372
373         dd(printf("NNTP_STREAM_GETS(%s,%d): '%.*s'\n", end==NULL?"more":"last", *len, (int)*len, *start));
374
375         return end == NULL?1:0;
376 }
377
378 void camel_nntp_stream_set_mode(CamelNNTPStream *is, camel_nntp_stream_mode_t mode)
379 {
380         is->mode = mode;
381 }
382
383 /* returns -1 on erorr, 0 if last data, >0 if more data left */
384 int camel_nntp_stream_getd(CamelNNTPStream *is, unsigned char **start, unsigned int *len)
385 {
386         unsigned char *p, *e, *s;
387         int state;
388
389         *len = 0;
390
391         if (is->mode == CAMEL_NNTP_STREAM_EOD)
392                 return 0;
393
394         if (is->mode == CAMEL_NNTP_STREAM_LINE) {
395                 g_warning("nntp_stream reading data in line mode\n");
396                 return 0;
397         }
398
399         state = is->state;
400         p = is->ptr;
401         e = is->end;
402
403         while (e - p < 3) {
404                 is->ptr = p;
405                 if (stream_fill(is) == -1)
406                         return -1;
407                 p = is->ptr;
408                 e = is->end;
409         }
410
411         s = p;
412
413         do {
414                 switch(state) {
415                 case 0:
416                         /* check leading '.', ... */
417                         if (p[0] == '.') {
418                                 if (p[1] == '\r' && p[2] == '\n') {
419                                         is->ptr = p+3;
420                                         *len = p-s;
421                                         *start = s;
422                                         is->mode = CAMEL_NNTP_STREAM_EOD;
423                                         is->state = 0;
424
425                                         dd(printf("NNTP_STREAM_GETD(%s,%d): '%.*s'\n", "last", *len, (int)*len, *start));
426
427                                         return 0;
428                                 }
429
430                                 /* If at start, just skip '.', else return data upto '.' but skip it */
431                                 if (p == s) {
432                                         s++;
433                                         p++;
434                                 } else {
435                                         is->ptr = p+1;
436                                         *len = p-s;
437                                         *start = s;
438                                         is->state = 1;
439
440                                         dd(printf("NNTP_STREAM_GETD(%s,%d): '%.*s'\n", "more", *len, (int)*len, *start));
441
442                                         return 1;
443                                 }
444                         }
445                         state = 1;
446                 case 1:
447                         /* Scan for sentinal */
448                         while ((*p++)!='\n')
449                                 ;
450
451                         if (p > e) {
452                                 p = e;
453                         } else {
454                                 state = 0;
455                         }
456                         break;
457                 }
458         } while ((e-p) >= 3);
459
460         is->state = state;
461         is->ptr = p;
462         *len = p-s;
463         *start = s;
464
465         dd(printf("NNTP_STREAM_GETD(%s,%d): '%.*s'\n", "more", *len, (int)*len, *start));
466         return 1;
467 }
468