1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; fill-column: 160 -*-
4 * Michael Zucchi <notzed@ximian.com>
6 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
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.
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.
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
33 #include "camel-nntp-stream.h"
35 #define dd(x) (camel_debug("nntp:stream")?(x):0)
38 #define ECONNRESET EIO
41 static CamelObjectClass *parent_class = NULL;
43 /* Returns the class for a CamelStream */
44 #define CS_CLASS(so) CAMEL_NNTP_STREAM_CLASS(CAMEL_OBJECT_GET_CLASS(so))
46 #define CAMEL_NNTP_STREAM_SIZE (4096)
47 #define CAMEL_NNTP_STREAM_LINE_SIZE (1024) /* maximum line size */
50 stream_fill(CamelNNTPStream *is)
55 left = is->end - is->ptr;
56 memcpy(is->buf, is->ptr, left);
57 is->end = is->buf + left;
59 left = camel_stream_read(is->source, (gchar *) is->end, CAMEL_NNTP_STREAM_SIZE - (is->end - is->buf));
63 return is->end - is->ptr;
67 dd(printf("NNTP_STREAM_FILL(ERROR): %d - '%s'\n", left, g_strerror(errno)));
76 stream_read(CamelStream *stream, gchar *buffer, gsize n)
78 CamelNNTPStream *is = (CamelNNTPStream *)stream;
83 if (is->mode != CAMEL_NNTP_STREAM_DATA || n == 0)
90 /* Need to copy/strip '.'s and whatnot */
96 case 0: /* start of line, always read at least 3 chars */
99 if (stream_fill(is) == -1)
105 if (p[1] == '\r' && p[2] == '\n') {
107 is->mode = CAMEL_NNTP_STREAM_EOD;
109 dd(printf("NNTP_STREAM_READ(%d):\n%.*s\n", (gint)(o-buffer), (gint)(o-buffer), buffer));
116 case 1: /* looking for next sol */
120 /* end of input sentinal check */
123 if (stream_fill(is) == -1)
132 } else if (c != '\r') {
142 dd(printf("NNTP_STREAM_READ(%d):\n%.*s\n", (gint)(o-buffer), (gint)(o-buffer), buffer));
148 stream_write(CamelStream *stream, const gchar *buffer, gsize n)
150 CamelNNTPStream *is = (CamelNNTPStream *)stream;
152 return camel_stream_write(is->source, buffer, n);
156 stream_close(CamelStream *stream)
163 stream_flush(CamelStream *stream)
170 stream_eos(CamelStream *stream)
172 CamelNNTPStream *is = (CamelNNTPStream *)stream;
174 return is->mode != CAMEL_NNTP_STREAM_DATA;
178 stream_reset(CamelStream *stream)
180 /* nop? reset literal mode? */
185 camel_nntp_stream_class_init (CamelStreamClass *camel_nntp_stream_class)
187 CamelStreamClass *camel_stream_class = (CamelStreamClass *)camel_nntp_stream_class;
189 parent_class = camel_type_get_global_classfuncs( CAMEL_TYPE_OBJECT );
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;
201 camel_nntp_stream_init(CamelNNTPStream *is, CamelNNTPStreamClass *isclass)
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;
212 is->mode = CAMEL_NNTP_STREAM_LINE;
216 camel_nntp_stream_finalize(CamelNNTPStream *is)
221 camel_object_unref((CamelObject *)is->source);
225 camel_nntp_stream_get_type (void)
227 static CamelType camel_nntp_stream_type = CAMEL_INVALID_TYPE;
229 if (camel_nntp_stream_type == CAMEL_INVALID_TYPE) {
230 camel_nntp_stream_type = camel_type_register( camel_stream_get_type(),
232 sizeof( CamelNNTPStream ),
233 sizeof( CamelNNTPStreamClass ),
234 (CamelObjectClassInitFunc) camel_nntp_stream_class_init,
236 (CamelObjectInitFunc) camel_nntp_stream_init,
237 (CamelObjectFinalizeFunc) camel_nntp_stream_finalize );
240 return camel_nntp_stream_type;
244 * camel_nntp_stream_new:
246 * Returns a NULL stream. A null stream is always at eof, and
247 * always returns success for all reads and writes.
249 * Returns: the stream
252 camel_nntp_stream_new(CamelStream *source)
256 is = (CamelNNTPStream *)camel_object_new(camel_nntp_stream_get_type ());
257 camel_object_ref((CamelObject *)source);
260 return (CamelStream *)is;
263 /* Get one line from the nntp stream */
265 camel_nntp_stream_line(CamelNNTPStream *is, guchar **data, guint *len)
267 register guchar c, *p, *o, *oe;
271 if (is->mode == CAMEL_NNTP_STREAM_EOD) {
278 oe = is->lineend - 1;
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 */
287 if (stream_fill(is) == -1)
293 /* check for isolated '.\r\n' or begging of line '.' */
295 if (p[1] == '\r' && p[2] == '\n') {
297 is->mode = CAMEL_NNTP_STREAM_EOD;
302 dd(printf("NNTP_STREAM_LINE(END)\n"));
317 if (stream_fill(is) == -1)
324 *len = o - is->linebuf;
327 dd(printf("NNTP_STREAM_LINE(%d): '%s'\n", *len, *data));
331 } else if (c != '\r') {
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;
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)
356 max = is->end - is->ptr;
358 max = stream_fill(is);
364 end = memchr(is->ptr, '\n', max);
366 max = (end - is->ptr) + 1;
371 dd(printf("NNTP_STREAM_GETS(%s,%d): '%.*s'\n", end==NULL?"more":"last", *len, (gint)*len, *start));
373 return end == NULL?1:0;
376 void camel_nntp_stream_set_mode(CamelNNTPStream *is, camel_nntp_stream_mode_t mode)
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)
389 if (is->mode == CAMEL_NNTP_STREAM_EOD)
392 if (is->mode == CAMEL_NNTP_STREAM_LINE) {
393 g_warning("nntp_stream reading data in line mode\n");
403 if (stream_fill(is) == -1)
414 /* check leading '.', ... */
416 if (p[1] == '\r' && p[2] == '\n') {
420 is->mode = CAMEL_NNTP_STREAM_EOD;
423 dd(printf("NNTP_STREAM_GETD(%s,%d): '%.*s'\n", "last", *len, (gint)*len, *start));
428 /* If at start, just skip '.', else return data upto '.' but skip it */
438 dd(printf("NNTP_STREAM_GETD(%s,%d): '%.*s'\n", "more", *len, (gint)*len, *start));
445 /* Scan for sentinal */
456 } while ((e-p) >= 3);
463 dd(printf("NNTP_STREAM_GETD(%s,%d): '%.*s'\n", "more", *len, (gint)*len, *start));