Blame view

libmailutils/mailbox/body.c 8.73 KB
1
/* GNU Mailutils -- a suite of utilities for electronic mail
2 3
   Copyright (C) 1999, 2000, 2001, 2004, 2005, 2007, 2010 Free Software
   Foundation, Inc.
4

5 6 7
   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
8
   version 3 of the License, or (at your option) any later version.
9

10
   This library is distributed in the hope that it will be useful,
11
   but WITHOUT ANY WARRANTY; without even the implied warranty of
12 13
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.
14

15
   You should have received a copy of the GNU Lesser General
16 17
   Public License along with this library.  If not, see 
   <http://www.gnu.org/licenses/>. */
18

19 20 21
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
22 23 24 25

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
26
#include <string.h>
27
#include <unistd.h>
28 29
#include <sys/types.h>
#include <sys/stat.h>
30
#include <fcntl.h>
31

32
#include <mailutils/stream.h>
33
#include <mailutils/util.h>
34
#include <mailutils/errno.h>
35
#include <mailutils/sys/stream.h>
36
#include <mailutils/sys/body.h>
37

38 39
#define BODY_MODIFIED 0x10000

40
static int _body_flush    (mu_stream_t);
41
static int _body_read     (mu_stream_t, char *, size_t, size_t *);
42 43
static int _body_truncate (mu_stream_t, mu_off_t);
static int _body_size     (mu_stream_t, mu_off_t *);
44
static int _body_write    (mu_stream_t, const char *, size_t, size_t *);
45
static int _body_ioctl    (mu_stream_t, int, int, void *);
46
static int _body_seek     (mu_stream_t, mu_off_t, mu_off_t *);
47
static const char *_body_error_string (mu_stream_t, int);
48 49

/* Our own defaults for the body.  */
50 51 52 53
static int _body_get_size   (mu_body_t, size_t *);
static int _body_get_lines  (mu_body_t, size_t *);
static int _body_get_size0  (mu_stream_t, size_t *);
static int _body_get_lines0 (mu_stream_t, size_t *);
54 55

int
56
mu_body_create (mu_body_t *pbody, void *owner)
57
{
58
  mu_body_t body;
59

60 61 62
  if (pbody == NULL)
    return MU_ERR_OUT_PTR_NULL;
  if (owner == NULL)
63 64 65 66 67 68 69 70 71 72 73 74
    return EINVAL;

  body = calloc (1, sizeof (*body));
  if (body == NULL)
    return ENOMEM;

  body->owner = owner;
  *pbody = body;
  return 0;
}

void
75
mu_body_destroy (mu_body_t *pbody, void *owner)
76 77 78
{
  if (pbody && *pbody)
    {
79
      mu_body_t body = *pbody;
80 81
      if (body->owner == owner)
	{
82
	  if (body->stream)
83
	    mu_stream_destroy (&body->stream);
84 85 86

	  if (body->fstream)
	    {
87
	      mu_stream_close (body->fstream);
88
	      mu_stream_destroy (&body->fstream);
89 90
	    }

91
	  free (body);
92 93 94 95 96
	}
      *pbody = NULL;
    }
}

97
void *
98
mu_body_get_owner (mu_body_t body)
99 100
{
  return (body) ? body->owner : NULL;
101 102 103 104
}

/* FIXME: not implemented.  */
int
105
mu_body_is_modified (mu_body_t body)
106
{
107
  return (body) ? (body->flags & BODY_MODIFIED) : 0;
108 109 110 111
}

/* FIXME: not implemented.  */
int
112
mu_body_clear_modified (mu_body_t body)
113
{
114 115
  if (body)
    body->flags &= ~BODY_MODIFIED;
116
  return 0;
117 118
}

119 120 121 122 123 124 125 126 127

struct _mu_body_stream
{
  struct _mu_stream stream;
  mu_body_t body;
};

static int
_body_get_stream (mu_body_t body, mu_stream_t *pstream, int ref)
128
{
129
  if (body == NULL)
130
    return EINVAL;
131 132
  if (pstream == NULL)
    return MU_ERR_OUT_PTR_NULL;
133 134 135

  if (body->stream == NULL)
    {
136 137 138 139 140 141 142 143 144 145 146 147
      if (body->_get_stream)
	{
	  int status = body->_get_stream (body, &body->stream);
	  if (status)
	    return status;
	}
      else
	{
	  int status;
	  struct _mu_body_stream *str =
	    (struct _mu_body_stream *)
	    _mu_stream_create (sizeof (*str),
148
			       MU_STREAM_RDWR|MU_STREAM_SEEK|_MU_STR_OPEN);
149 150 151 152
	  if (!str)
	    return ENOMEM;
	  
	  /* Create the temporary file.  */
153 154

	  status = mu_temp_file_stream_create (&body->fstream, NULL, 0);
155 156
	  if (status != 0)
	    return status;
157
	  mu_stream_set_buffer (body->fstream, mu_buffer_full, 0);
158 159 160 161 162 163 164 165 166 167 168 169 170
	  str->stream.ctl = _body_ioctl;
	  str->stream.read = _body_read;
	  str->stream.write = _body_write;
	  str->stream.truncate = _body_truncate;
	  str->stream.size = _body_size;
	  str->stream.seek = _body_seek;
	  str->stream.flush = _body_flush;
	  str->body = body;
	  body->stream = (mu_stream_t) str;
	  /* Override the defaults.  */
	  body->_lines = _body_get_lines;
	  body->_size = _body_get_size;
	}
171
    }
172
  
173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
  if (!ref)
    {
      *pstream = body->stream;
      return 0;
    }
  return mu_streamref_create (pstream, body->stream);
}

int
mu_body_get_stream (mu_body_t body, mu_stream_t *pstream)
{
  /* FIXME: Deprecation warning */
  return _body_get_stream (body, pstream, 0);
}

int
mu_body_get_streamref (mu_body_t body, mu_stream_t *pstream)
{
  return _body_get_stream (body, pstream, 1);
192 193 194
}

int
195
mu_body_set_stream (mu_body_t body, mu_stream_t stream, void *owner)
196 197 198 199 200
{
  if (body == NULL)
   return EINVAL;
  if (body->owner != owner)
    return EACCES;
201 202
  /* make sure we destroy the old one if it is owned by the body */
  mu_stream_destroy (&body->stream);
203
  body->stream = stream;
204
  body->flags |= BODY_MODIFIED;
205 206 207 208
  return 0;
}

int
209 210 211 212 213 214 215 216 217 218 219 220 221 222 223
mu_body_set_get_stream (mu_body_t body,
			int (*_getstr) (mu_body_t, mu_stream_t *),
			void *owner)
{
  if (body == NULL)
    return EINVAL;
  if (body->owner != owner)
    return EACCES;
  body->_get_stream = _getstr;
  return 0;
}

int
mu_body_set_lines (mu_body_t body, int (*_lines) (mu_body_t, size_t *),
		   void *owner)
224 225 226
{
  if (body == NULL)
    return EINVAL;
227
  if (body->owner != owner)
228 229 230 231 232 233
    return EACCES;
  body->_lines = _lines;
  return 0;
}

int
234
mu_body_lines (mu_body_t body, size_t *plines)
235 236 237 238 239
{
  if (body == NULL)
    return EINVAL;
  if (body->_lines)
    return body->_lines (body, plines);
240 241 242
  /* Fall on the stream.  */
  if (body->stream)
    return _body_get_lines0 (body->stream, plines);
243 244 245 246 247 248
  if (plines)
    *plines = 0;
  return 0;
}

int
249
mu_body_size (mu_body_t body, size_t *psize)
250 251 252 253 254
{
  if (body == NULL)
    return EINVAL;
  if (body->_size)
    return body->_size (body, psize);
255 256 257
  /* Fall on the stream.  */
  if (body->stream)
    return _body_get_size0 (body->stream, psize);
258
  if (psize)
259
    *psize = 0;
260
  return 0;
261 262 263
}

int
264
mu_body_set_size (mu_body_t body, int (*_size)(mu_body_t, size_t*) , void *owner)
265 266 267 268 269 270 271 272 273
{
  if (body == NULL)
    return EINVAL;
  if (body->owner != owner)
    return EACCES;
  body->_size = _size;
  return 0;
}

274 275
/* Stub function for the body stream.  */

276
static int
277
_body_seek (mu_stream_t stream, mu_off_t off, mu_off_t *presult)
278 279 280
{
  struct _mu_body_stream *str = (struct _mu_body_stream*) stream;
  mu_body_t body = str->body;
281
  return mu_stream_seek (body->fstream, off, MU_SEEK_SET, presult);
282 283 284 285
}

static const char *
_body_error_string (mu_stream_t stream, int rc)
286
{
287 288
  /* FIXME: How to know if rc was returned by a body->stream? */
  return NULL;
289 290 291
}

static int
292
_body_ioctl (mu_stream_t stream, int code, int opcode, void *ptr)
293
{
294 295
  struct _mu_body_stream *str = (struct _mu_body_stream*) stream;
  mu_body_t body = str->body;
296
  return mu_stream_ioctl (body->fstream, code, opcode, ptr);
297 298 299
}

static int
300
_body_read (mu_stream_t stream, char *buf, size_t size, size_t *pret)
301
{
302 303 304
  struct _mu_body_stream *str = (struct _mu_body_stream*) stream;
  mu_body_t body = str->body;
  return mu_stream_read (body->fstream, buf, size, pret);
305 306 307
}

static int
308
_body_write (mu_stream_t stream, const char *buf, size_t size, size_t *pret)
309
{
310 311 312
  struct _mu_body_stream *str = (struct _mu_body_stream*) stream;
  mu_body_t body = str->body;
  return mu_stream_write (body->fstream, buf, size, pret);
313 314 315
}

static int
316
_body_truncate (mu_stream_t stream, mu_off_t n)
317
{
318 319
  struct _mu_body_stream *str = (struct _mu_body_stream*) stream;
  mu_body_t body = str->body;
320
  return mu_stream_truncate (body->fstream, n);
321 322 323
}

static int
324
_body_size (mu_stream_t stream, mu_off_t *size)
325
{
326 327
  struct _mu_body_stream *str = (struct _mu_body_stream*) stream;
  mu_body_t body = str->body;
328
  return mu_stream_size (body->fstream, size);
329 330 331
}

static int
332
_body_flush (mu_stream_t stream)
333
{
334 335
  struct _mu_body_stream *str = (struct _mu_body_stream*) stream;
  mu_body_t body = str->body;
336
  return mu_stream_flush (body->fstream);
337 338
}

339
/* Default function for the body.  */
340
static int
341
_body_get_lines (mu_body_t body, size_t *plines)
342 343 344 345 346
{
  return _body_get_lines0 (body->fstream, plines);
}

static int
347
_body_get_size (mu_body_t body, size_t *psize)
348 349 350 351 352
{
  return _body_get_size0 (body->fstream, psize);
}

static int
353
_body_get_size0 (mu_stream_t stream, size_t *psize)
354
{
355
  mu_off_t off = 0;
356
  int status = mu_stream_size (stream, &off);
357 358
  if (psize)
    *psize = off;
359 360 361 362
  return status;
}

static int
363
_body_get_lines0 (mu_stream_t stream, size_t *plines)
364
{
365
  int status =  mu_stream_flush (stream);
366
  size_t lines = 0;
367
  
368 369 370 371
  if (status == 0)
    {
      char buf[128];
      size_t n = 0;
372 373 374 375

      status = mu_stream_seek (stream, 0, MU_SEEK_SET, NULL);
      if (status)
	return status;
376
      while ((status = mu_stream_readline (stream, buf, sizeof buf,
377
					   &n)) == 0 && n > 0)
378 379 380 381 382 383 384 385 386 387
	{
	  if (buf[n - 1] == '\n')
	    lines++;
	}
    }
  if (plines)
    *plines = lines;
  return status;
}

388