/* GNU Mailutils -- a suite of utilities for electronic mail Copyright (C) 1999, 2000, 2001, 2002, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc. 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 version 3, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with GNU Mailutils. If not, see . */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #if HAVE_TERMIOS_H # include #endif #include #include #include #include #include #include #include #include #include static int fd_read (struct _mu_stream *str, char *buf, size_t size, size_t *pret) { struct _mu_file_stream *fstr = (struct _mu_file_stream *) str; ssize_t n = read (fstr->fd, buf, size); if (n == -1) return errno; *pret = n; return 0; } static int fd_write (struct _mu_stream *str, const char *buf, size_t size, size_t *pret) { struct _mu_file_stream *fstr = (struct _mu_file_stream *) str; ssize_t n = write (fstr->fd, (char*) buf, size); if (n == -1) return errno; *pret = n; return 0; } static int fd_close (struct _mu_stream *str) { struct _mu_file_stream *fstr = (struct _mu_file_stream *) str; if (fstr->fd != -1) { if (!(fstr->flags & _MU_FILE_STREAM_FD_BORROWED) && close (fstr->fd)) return errno; fstr->fd = -1; } return 0; } static int fd_open (struct _mu_stream *str) { struct _mu_file_stream *fstr = (struct _mu_file_stream *) str; int oflg; int fd; if (!fstr->filename) return EINVAL; if (fstr->fd != -1) fd_close (str); /* Map the flags to the system equivalent. */ if ((fstr->stream.flags & MU_STREAM_RDWR) == MU_STREAM_RDWR) oflg = O_RDWR; else if (fstr->stream.flags & (MU_STREAM_WRITE|MU_STREAM_APPEND)) oflg = O_WRONLY; else /* default */ oflg = O_RDONLY; if (fstr->stream.flags & MU_STREAM_APPEND) oflg |= O_APPEND; /* Handle CREAT with care, not to follow symlinks. */ if (fstr->stream.flags & MU_STREAM_CREAT) { /* First see if the file already exists. */ fd = open (fstr->filename, oflg); if (fd == -1) { /* Oops bail out. */ if (errno != ENOENT) return errno; fd = open (fstr->filename, oflg|O_CREAT|O_EXCL, 0600 | mu_stream_flags_to_mode (fstr->stream.flags, 0)); } } else fd = open (fstr->filename, oflg); if (fd == -1) return errno; if (lseek (fd, 0, SEEK_SET) == -1) str->flags &= ~MU_STREAM_SEEK; /* Make sure it will be closed */ fstr->flags &= ~_MU_FILE_STREAM_FD_BORROWED; fstr->fd = fd; return 0; } int fd_seek (struct _mu_stream *str, mu_off_t off, mu_off_t *presult) { struct _mu_file_stream *fstr = (struct _mu_file_stream *) str; off = lseek (fstr->fd, off, SEEK_SET); if (off < 0) return errno; *presult = off; return 0; } int fd_size (struct _mu_stream *str, mu_off_t *psize) { struct _mu_file_stream *fstr = (struct _mu_file_stream *) str; struct stat st; if (fstat (fstr->fd, &st)) return errno; *psize = st.st_size; return 0; } void fd_done (struct _mu_stream *str) { struct _mu_file_stream *fstr = (struct _mu_file_stream *) str; if (fstr->fd != -1) fd_close (str); if (fstr->filename) free (fstr->filename); if (fstr->echo_state) free (fstr->echo_state); } #ifndef TCSASOFT # define TCSASOFT 0 #endif static int fd_ioctl (struct _mu_stream *str, int code, int opcode, void *ptr) { struct _mu_file_stream *fstr = (struct _mu_file_stream *) str; mu_transport_t *ptrans; switch (code) { case MU_IOCTL_TRANSPORT: if (!ptr) return EINVAL; switch (opcode) { case MU_IOCTL_OP_GET: ptrans = ptr; ptrans[0] = (mu_transport_t) fstr->fd; ptrans[1] = NULL; break; case MU_IOCTL_OP_SET: ptrans = ptr; fstr->fd = (int) ptrans[0]; break; } break; case MU_IOCTL_TRANSPORT_BUFFER: if (!ptr) return EINVAL; else { struct mu_buffer_query *qp = ptr; switch (opcode) { case MU_IOCTL_OP_GET: return mu_stream_get_buffer (str, qp); case MU_IOCTL_OP_SET: return mu_stream_set_buffer (str, qp->buftype, qp->bufsize); } } break; case MU_IOCTL_ECHO: if (!ptr) return EINVAL; switch (opcode) { case MU_IOCTL_OP_GET: *(int*)ptr = fstr->flags & _MU_FILE_STREAM_ECHO_OFF; break; case MU_IOCTL_OP_SET: { int status; struct termios t; int state = *(int*)ptr; #if HAVE_TCGETATTR if (state == 0) { if (fstr->flags & _MU_FILE_STREAM_ECHO_OFF) return 0; status = tcgetattr (fstr->fd, &t); if (status == 0) { fstr->echo_state = malloc (sizeof (t)); if (!fstr->echo_state) return ENOMEM; memcpy (fstr->echo_state, &t, sizeof (t)); t.c_lflag &= ~(ECHO | ISIG); status = tcsetattr (fstr->fd, TCSAFLUSH | TCSASOFT, &t); if (status == 0) fstr->flags |= _MU_FILE_STREAM_ECHO_OFF; } if (status) { status = errno; if (fstr->echo_state) { free (fstr->echo_state); fstr->echo_state = NULL; } } } else { if (!(fstr->flags & _MU_FILE_STREAM_ECHO_OFF)) return 0; if (tcsetattr (fstr->fd, TCSAFLUSH | TCSASOFT, fstr->echo_state)) status = errno; else { status = 0; free (fstr->echo_state); fstr->echo_state = NULL; fstr->flags &= ~_MU_FILE_STREAM_ECHO_OFF; } } return status; #else return ENOSYS; #endif } } break; case MU_IOCTL_FD: if (!ptr) return EINVAL; switch (opcode) { case MU_IOCTL_FD_GET_BORROW: *(int*) ptr = !!(fstr->flags & _MU_FILE_STREAM_FD_BORROWED); break; case MU_IOCTL_FD_SET_BORROW: if (*(int*)ptr) fstr->flags |= _MU_FILE_STREAM_FD_BORROWED; else fstr->flags &= ~_MU_FILE_STREAM_FD_BORROWED; break; } break; default: return ENOSYS; } return 0; } int fd_wait (mu_stream_t stream, int *pflags, struct timeval *tvp) { struct _mu_file_stream *fstr = (struct _mu_file_stream *) stream; if (fstr->fd == -1) return EINVAL; return mu_fd_wait (fstr->fd, pflags, tvp); } int fd_truncate (mu_stream_t stream, mu_off_t size) { struct _mu_file_stream *fstr = (struct _mu_file_stream *) stream; if (ftruncate (fstr->fd, size)) return errno; return 0; } int _mu_file_stream_create (struct _mu_file_stream **pstream, size_t size, const char *filename, int fd, int flags) { struct _mu_file_stream *str = (struct _mu_file_stream *) _mu_stream_create (size, flags); if (!str) return ENOMEM; str->stream.read = fd_read; str->stream.write = fd_write; str->stream.open = fd_open; str->stream.close = fd_close; str->stream.done = fd_done; str->stream.seek = fd_seek; str->stream.size = fd_size; str->stream.ctl = fd_ioctl; str->stream.wait = fd_wait; str->stream.truncate = fd_truncate; if (filename) str->filename = mu_strdup (filename); else str->filename = NULL; str->fd = fd; str->flags = 0; *pstream = str; return 0; } int mu_file_stream_create (mu_stream_t *pstream, const char *filename, int flags) { struct _mu_file_stream *fstr; int rc = _mu_file_stream_create (&fstr, sizeof (struct _mu_file_stream), filename, -1, flags | MU_STREAM_SEEK); if (rc == 0) { mu_stream_t stream = (mu_stream_t) fstr; mu_stream_set_buffer (stream, mu_buffer_full, 0); rc = mu_stream_open (stream); if (rc) mu_stream_unref (stream); else *pstream = stream; } return rc; } int mu_fd_stream_create (mu_stream_t *pstream, char *filename, int fd, int flags) { struct _mu_file_stream *fstr; int rc; if (flags & MU_STREAM_SEEK) { if (lseek (fd, 0, SEEK_SET)) return errno; } rc = _mu_file_stream_create (&fstr, sizeof (struct _mu_file_stream), filename, fd, flags|_MU_STR_OPEN); if (rc == 0) { mu_stream_t stream = (mu_stream_t) fstr; mu_stream_set_buffer (stream, mu_buffer_full, 0); *pstream = stream; } return rc; }