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