1 /* GNU Mailutils -- a suite of utilities for electronic mail
2    Copyright (C) 1999-2021 Free Software Foundation, Inc.
3 
4    This library is free software; you can redistribute it and/or modify
5    it under the terms of the GNU Lesser General Public License as published by
6    the Free Software Foundation; either version 3, or (at your option)
7    any later version.
8 
9    This library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU Lesser General Public License for more details.
13 
14    You should have received a copy of the GNU Lesser General Public License
15    along with GNU Mailutils.  If not, see <http://www.gnu.org/licenses/>. */
16 
17 #ifdef HAVE_CONFIG_H
18 # include <config.h>
19 #endif
20 #include <string.h>
21 #include <stdlib.h>
22 #include <unistd.h>
23 #include <fcntl.h>
24 #include <sys/stat.h>
25 #if HAVE_TERMIOS_H
26 # include <termios.h>
27 #endif
28 
29 #include <mailutils/types.h>
30 #include <mailutils/alloc.h>
31 #include <mailutils/error.h>
32 #include <mailutils/errno.h>
33 #include <mailutils/nls.h>
34 #include <mailutils/stream.h>
35 #include <mailutils/sys/stream.h>
36 #include <mailutils/sys/file_stream.h>
37 #include <mailutils/util.h>
38 
39 static int
fd_read(struct _mu_stream * str,char * buf,size_t size,size_t * pret)40 fd_read (struct _mu_stream *str, char *buf, size_t size, size_t *pret)
41 {
42   struct _mu_file_stream *fstr = (struct _mu_file_stream *) str;
43   ssize_t n = read (fstr->fd, buf, size);
44   if (n == -1)
45     return errno;
46   *pret = n;
47   return 0;
48 }
49 
50 static int
fd_write(struct _mu_stream * str,const char * buf,size_t size,size_t * pret)51 fd_write (struct _mu_stream *str, const char *buf, size_t size, size_t *pret)
52 {
53   struct _mu_file_stream *fstr = (struct _mu_file_stream *) str;
54   ssize_t n = write (fstr->fd, (char*) buf, size);
55   if (n == -1)
56     return errno;
57   *pret = n;
58   return 0;
59 }
60 
61 static int
fd_close(struct _mu_stream * str)62 fd_close (struct _mu_stream *str)
63 {
64   struct _mu_file_stream *fstr = (struct _mu_file_stream *) str;
65   if (fstr->fd != -1)
66     {
67       if (!(fstr->flags & _MU_FILE_STREAM_FD_BORROWED) && close (fstr->fd))
68 	return errno;
69       fstr->fd = -1;
70     }
71   return 0;
72 }
73 
74 static int
fd_open(struct _mu_stream * str)75 fd_open (struct _mu_stream *str)
76 {
77   struct _mu_file_stream *fstr = (struct _mu_file_stream *) str;
78   int oflg;
79   int fd;
80 
81   if (!fstr->filename)
82     return EINVAL;
83   if (fstr->fd != -1)
84     fd_close (str);
85 
86   /* Map the flags to the system equivalent.  */
87   if ((fstr->stream.flags & MU_STREAM_RDWR) == MU_STREAM_RDWR)
88     oflg = O_RDWR;
89   else if ((fstr->stream.flags & (MU_STREAM_READ|MU_STREAM_APPEND)) ==
90 	   (MU_STREAM_READ|MU_STREAM_APPEND))
91     oflg = O_RDWR;
92   else if (fstr->stream.flags & (MU_STREAM_WRITE|MU_STREAM_APPEND))
93     oflg = O_WRONLY;
94   else /* default */
95     oflg = O_RDONLY;
96 
97   if (fstr->stream.flags & MU_STREAM_APPEND)
98     oflg |= O_APPEND;
99 
100   /* Handle CREAT with care, not to follow symlinks.  */
101   if (fstr->stream.flags & MU_STREAM_CREAT)
102     {
103       /* First see if the file already exists.  */
104       fd = open (fstr->filename, oflg);
105       if (fd == -1)
106 	{
107 	  /* Oops bail out.  */
108 	  if (errno != ENOENT)
109 	    return errno;
110 	  fd = open (fstr->filename, oflg|O_CREAT|O_EXCL,
111 		     0600 | mu_stream_flags_to_mode (fstr->stream.flags, 0));
112 	}
113     }
114   else
115     fd = open (fstr->filename, oflg);
116 
117   if (fd == -1)
118     return errno;
119 
120   if (lseek (fd, 0, SEEK_SET) == -1)
121     str->flags &= ~MU_STREAM_SEEK;
122 
123   /* Make sure it will be closed */
124   fstr->flags &= ~_MU_FILE_STREAM_FD_BORROWED;
125 
126   fstr->fd = fd;
127   return 0;
128 }
129 
130 int
fd_seek(struct _mu_stream * str,mu_off_t off,mu_off_t * presult)131 fd_seek (struct _mu_stream *str, mu_off_t off, mu_off_t *presult)
132 {
133   struct _mu_file_stream *fstr = (struct _mu_file_stream *) str;
134   off = lseek (fstr->fd, off, SEEK_SET);
135   if (off < 0)
136     return errno;
137   *presult = off;
138   return 0;
139 }
140 
141 int
fd_size(struct _mu_stream * str,mu_off_t * psize)142 fd_size (struct _mu_stream *str, mu_off_t *psize)
143 {
144   struct _mu_file_stream *fstr = (struct _mu_file_stream *) str;
145   struct stat st;
146   if (fstat (fstr->fd, &st))
147     return errno;
148   *psize = st.st_size;
149   return 0;
150 }
151 
152 void
fd_done(struct _mu_stream * str)153 fd_done (struct _mu_stream *str)
154 {
155   struct _mu_file_stream *fstr = (struct _mu_file_stream *) str;
156   if (fstr->fd != -1)
157     fd_close (str);
158   if (fstr->filename && !(fstr->flags & _MU_FILE_STREAM_STATIC_FILENAME))
159     free (fstr->filename);
160   if (fstr->echo_state)
161     free (fstr->echo_state);
162 }
163 
164 #ifndef TCSASOFT
165 # define TCSASOFT 0
166 #endif
167 
168 static int
fd_ioctl(struct _mu_stream * str,int code,int opcode,void * ptr)169 fd_ioctl (struct _mu_stream *str, int code, int opcode, void *ptr)
170 {
171   struct _mu_file_stream *fstr = (struct _mu_file_stream *) str;
172   mu_transport_t *ptrans;
173 
174   switch (code)
175     {
176     case MU_IOCTL_TRANSPORT:
177       if (!ptr)
178 	return EINVAL;
179       switch (opcode)
180 	{
181 	case MU_IOCTL_OP_GET:
182 	  ptrans = ptr;
183 	  ptrans[0] = (mu_transport_t) (intptr_t) fstr->fd;
184 	  ptrans[1] = NULL;
185 	  break;
186 
187 	case MU_IOCTL_OP_SET:
188 	  ptrans = ptr;
189 	  fstr->fd = (int) (intptr_t) ptrans[0];
190 	  break;
191 	}
192       break;
193 
194     case MU_IOCTL_TRANSPORT_BUFFER:
195       if (!ptr)
196 	return EINVAL;
197       else
198 	{
199 	  struct mu_buffer_query *qp = ptr;
200 	  switch (opcode)
201 	    {
202 	    case MU_IOCTL_OP_GET:
203 	      return mu_stream_get_buffer (str, qp);
204 	    case MU_IOCTL_OP_SET:
205 	      return mu_stream_set_buffer (str, qp->buftype, qp->bufsize);
206 	    }
207 	}
208       break;
209 
210     case MU_IOCTL_ECHO:
211       if (!ptr)
212 	return EINVAL;
213       switch (opcode)
214 	{
215 	case MU_IOCTL_OP_GET:
216 	  *(int*)ptr = fstr->flags & _MU_FILE_STREAM_ECHO_OFF;
217 	  break;
218 	case MU_IOCTL_OP_SET:
219 	  {
220 	    int status;
221 	    struct termios t;
222 	    int state = *(int*)ptr;
223 #if HAVE_TCGETATTR
224 	    if (state == 0)
225 	      {
226 		if (fstr->flags & _MU_FILE_STREAM_ECHO_OFF)
227 		  return 0;
228 		status = tcgetattr (fstr->fd, &t);
229 		if (status == 0)
230 		  {
231 		    fstr->echo_state = malloc (sizeof (t));
232 		    if (!fstr->echo_state)
233 		      return ENOMEM;
234 		    memcpy (fstr->echo_state, &t, sizeof (t));
235 
236 		    t.c_lflag &= ~(ECHO | ISIG);
237 		    status = tcsetattr (fstr->fd, TCSAFLUSH | TCSASOFT, &t);
238 		    if (status == 0)
239 		      fstr->flags |= _MU_FILE_STREAM_ECHO_OFF;
240 		  }
241 		if (status)
242 		  {
243 		    status = errno;
244 		    if (fstr->echo_state)
245 		      {
246 			free (fstr->echo_state);
247 			fstr->echo_state = NULL;
248 		      }
249 		  }
250 	      }
251 	    else
252 	      {
253 		if (!(fstr->flags & _MU_FILE_STREAM_ECHO_OFF))
254 		  return 0;
255 		if (tcsetattr (fstr->fd, TCSAFLUSH | TCSASOFT,
256 			       fstr->echo_state))
257 		  status = errno;
258 		else
259 		  {
260 		    status = 0;
261 		    free (fstr->echo_state);
262 		    fstr->echo_state = NULL;
263 		    fstr->flags &= ~_MU_FILE_STREAM_ECHO_OFF;
264 		  }
265 	      }
266 	    return status;
267 #else
268 	    return ENOSYS;
269 #endif
270 	  }
271 	}
272       break;
273 
274     case MU_IOCTL_FD:
275       if (!ptr)
276 	return EINVAL;
277       switch (opcode)
278 	{
279 	case MU_IOCTL_FD_GET_BORROW:
280 	  *(int*) ptr = !!(fstr->flags & _MU_FILE_STREAM_FD_BORROWED);
281 	  break;
282 
283 	case MU_IOCTL_FD_SET_BORROW:
284 	  if (*(int*)ptr)
285 	    fstr->flags |= _MU_FILE_STREAM_FD_BORROWED;
286 	  else
287 	    fstr->flags &= ~_MU_FILE_STREAM_FD_BORROWED;
288 	  break;
289 	}
290       break;
291 
292     default:
293       return ENOSYS;
294     }
295   return 0;
296 }
297 
298 int
fd_wait(mu_stream_t stream,int * pflags,struct timeval * tvp)299 fd_wait (mu_stream_t stream, int *pflags, struct timeval *tvp)
300 {
301   struct _mu_file_stream *fstr = (struct _mu_file_stream *) stream;
302 
303   if (fstr->fd == -1)
304     return EINVAL;
305   return mu_fd_wait (fstr->fd, pflags, tvp);
306 }
307 
308 int
fd_truncate(mu_stream_t stream,mu_off_t size)309 fd_truncate (mu_stream_t stream, mu_off_t size)
310 {
311   struct _mu_file_stream *fstr = (struct _mu_file_stream *) stream;
312   if (ftruncate (fstr->fd, size))
313     return errno;
314   return 0;
315 }
316 
317 void
_mu_file_stream_setup(struct _mu_file_stream * str)318 _mu_file_stream_setup (struct _mu_file_stream *str)
319 {
320   str->stream.read = fd_read;
321   str->stream.write = fd_write;
322   str->stream.open = fd_open;
323   str->stream.close = fd_close;
324   str->stream.done = fd_done;
325   str->stream.seek = fd_seek;
326   str->stream.size = fd_size;
327   str->stream.ctl = fd_ioctl;
328   str->stream.wait = fd_wait;
329   str->stream.truncate = fd_truncate;
330 }
331 
332 int
_mu_file_stream_create(struct _mu_file_stream ** pstream,size_t size,const char * filename,int fd,int flags)333 _mu_file_stream_create (struct _mu_file_stream **pstream, size_t size,
334 			const char *filename, int fd, int flags)
335 {
336   struct _mu_file_stream *str =
337     (struct _mu_file_stream *) _mu_stream_create (size, flags);
338   if (!str)
339     return ENOMEM;
340 
341   _mu_file_stream_setup (str);
342 
343   if (filename)
344     str->filename = mu_strdup (filename);
345   else
346     str->filename = NULL;
347   str->fd = fd;
348   str->flags = 0;
349   *pstream = str;
350   return 0;
351 }
352 
353 int
mu_file_stream_create(mu_stream_t * pstream,const char * filename,int flags)354 mu_file_stream_create (mu_stream_t *pstream, const char *filename, int flags)
355 {
356   struct _mu_file_stream *fstr;
357   int rc = _mu_file_stream_create (&fstr,
358 				   sizeof (struct _mu_file_stream),
359 				   filename, -1,
360 				   flags | MU_STREAM_SEEK);
361   if (rc == 0)
362     {
363       mu_stream_t stream = (mu_stream_t) fstr;
364       mu_stream_set_buffer (stream, mu_buffer_full, 0);
365       rc = mu_stream_open (stream);
366       if (rc)
367 	mu_stream_unref (stream);
368       else
369 	*pstream = stream;
370     }
371   return rc;
372 }
373 
374 int
mu_fd_stream_create(mu_stream_t * pstream,char * filename,int fd,int flags)375 mu_fd_stream_create (mu_stream_t *pstream, char *filename, int fd, int flags)
376 {
377   struct _mu_file_stream *fstr;
378   int rc;
379 
380   if (flags & MU_STREAM_SEEK)
381     {
382       if (lseek (fd, 0, SEEK_SET))
383 	return errno;
384     }
385 
386   rc = _mu_file_stream_create (&fstr,
387 			       sizeof (struct _mu_file_stream),
388 			       filename, fd, flags|_MU_STR_OPEN);
389   if (rc == 0)
390     {
391       mu_stream_t stream = (mu_stream_t) fstr;
392       mu_stream_set_buffer (stream, mu_buffer_full, 0);
393       *pstream = stream;
394     }
395   return rc;
396 }
397 
398