1 /* GNU Mailutils -- a suite of utilities for electronic mail
2    Copyright (C) 2010-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 <stdlib.h>
21 #include <errno.h>
22 #include <string.h>
23 #include <mailutils/types.h>
24 #include <mailutils/stream.h>
25 #include <mailutils/errno.h>
26 #include <mailutils/sys/nullstream.h>
27 #include <mailutils/stream.h>
28 #include <mailutils/cctype.h>
29 
30 static int
_nullstream_read(struct _mu_stream * str,char * buf,size_t bufsize,size_t * pnread)31 _nullstream_read (struct _mu_stream *str, char *buf, size_t bufsize,
32 		  size_t *pnread)
33 {
34   struct _mu_nullstream *np = (struct _mu_nullstream *)str;
35   size_t i;
36   mu_off_t off;
37 
38   if (np->pattern == NULL)
39     {
40       *pnread = 0;
41       return 0;
42     }
43 
44   off = np->base.offset + np->base.pos;
45   for (i = 0; i < bufsize; i++, off++)
46     {
47       if ((np->mode & MU_NULLSTREAM_SIZE) && off >= np->size)
48 	break;
49       *buf++ = np->pattern[off % np->patsize];
50     }
51   *pnread = i;
52   return 0;
53 }
54 
55 static int
_nullstream_write(struct _mu_stream * str,const char * buf,size_t bufsize,size_t * pnwrite)56 _nullstream_write (struct _mu_stream *str, const char *buf, size_t bufsize,
57 		   size_t *pnwrite)
58 {
59   *pnwrite = bufsize;
60   return 0;
61 }
62 
63 static void
_nullstream_free_pattern(struct _mu_nullstream * np)64 _nullstream_free_pattern (struct _mu_nullstream *np)
65 {
66   if (!(np->mode & MU_NULLSTREAM_PATSTAT))
67     {
68       free (np->pattern);
69       np->mode &= ~MU_NULLSTREAM_PATSTAT;
70     }
71   np->pattern = NULL;
72   np->patsize = 0;
73 }
74 
75 static void
_nullstream_done(struct _mu_stream * str)76 _nullstream_done (struct _mu_stream *str)
77 {
78   struct _mu_nullstream *np = (struct _mu_nullstream *)str;
79   _nullstream_free_pattern (np);
80 }
81 
82 static int
_nullstream_seek(struct _mu_stream * str,mu_off_t off,mu_off_t * ppos)83 _nullstream_seek (struct _mu_stream *str, mu_off_t off, mu_off_t *ppos)
84 {
85   struct _mu_nullstream *np = (struct _mu_nullstream *)str;
86   if ((np->mode & MU_NULLSTREAM_SIZE) && off >= np->size)
87     return ESPIPE;
88   *ppos = off;
89   return 0;
90 }
91 
92 static int
_nullstream_size(struct _mu_stream * str,mu_off_t * psize)93 _nullstream_size (struct _mu_stream *str, mu_off_t *psize)
94 {
95   struct _mu_nullstream *np = (struct _mu_nullstream *)str;
96   *psize = (np->mode & MU_NULLSTREAM_SIZE) ? np->size : 0;
97   return 0;
98 }
99 
100 static int
_nullstream_truncate(struct _mu_stream * str,mu_off_t size)101 _nullstream_truncate (struct _mu_stream *str, mu_off_t size)
102 {
103   struct _mu_nullstream *np = (struct _mu_nullstream *)str;
104   np->base.size = _nullstream_size;
105   np->size = size;
106   np->mode |= MU_NULLSTREAM_SIZE;
107   return 0;
108 }
109 
110 static int
_nullstream_ctl(struct _mu_stream * str,int code,int opcode,void * arg)111 _nullstream_ctl (struct _mu_stream *str, int code, int opcode, void *arg)
112 {
113   struct _mu_nullstream *np = (struct _mu_nullstream *)str;
114 
115   if (code != MU_IOCTL_NULLSTREAM)
116     /* Only this code is supported */
117     return ENOSYS;
118   switch (opcode)
119     {
120     case MU_IOCTL_NULLSTREAM_SET_PATTERN:
121       if (!arg)
122 	_nullstream_free_pattern (np);
123       else
124 	{
125 	  struct mu_nullstream_pattern *pat = arg;
126 	  char *p;
127 
128 	  p = malloc (pat->size);
129 	  if (!p)
130 	    return ENOMEM;
131 	  memcpy (p, pat->pattern, pat->size);
132 	  _nullstream_free_pattern (np);
133 	  np->pattern = p;
134 	  np->patsize = pat->size;
135 	}
136       break;
137 
138     case MU_IOCTL_NULLSTREAM_SET_PATCLASS:
139       if (!arg)
140 	return EINVAL;
141       else
142 	{
143 	  char buf[256];
144 	  int cnt = 0, i;
145 	  int class = *(int*)arg;
146 	  char *p;
147 
148 	  for (i = 0; i < 256; i++)
149 	    {
150 	      if (mu_c_is_class (i, class))
151 		buf[cnt++] = i;
152 	    }
153 
154 	  p = malloc (cnt);
155 	  if (!p)
156 	    return ENOMEM;
157 	  memcpy (p, buf, cnt);
158 	  _nullstream_free_pattern (np);
159 	  np->pattern = p;
160 	  np->patsize = cnt;
161 	}
162       break;
163 
164     case MU_IOCTL_NULLSTREAM_SETSIZE:
165       if (!arg)
166 	return EINVAL;
167       else
168 	return _nullstream_truncate (str, *(mu_off_t*)arg);
169       break;
170 
171     case MU_IOCTL_NULLSTREAM_CLRSIZE:
172       np->mode &= ~MU_NULLSTREAM_SIZE;
173       np->base.size = NULL;
174       break;
175 
176     default:
177       return ENOSYS;
178     }
179   return 0;
180 }
181 
182 int
mu_nullstream_create(mu_stream_t * pref,int flags)183 mu_nullstream_create (mu_stream_t *pref, int flags)
184 {
185   struct _mu_nullstream *np;
186 
187   np = (struct _mu_nullstream *)
188          _mu_stream_create (sizeof (*np),
189 			    flags | MU_STREAM_SEEK | _MU_STR_OPEN);
190   if (!np)
191     return ENOMEM;
192   np->base.read = _nullstream_read;
193   np->base.write = _nullstream_write;
194   np->base.seek = _nullstream_seek;
195   np->base.ctl = _nullstream_ctl;
196   np->base.truncate = _nullstream_truncate;
197   np->base.done = _nullstream_done;
198 
199   np->pattern = "\0";
200   np->patsize = 1;
201   np->mode = MU_NULLSTREAM_PATSTAT;
202 
203   *pref = (mu_stream_t) np;
204 
205   mu_stream_set_buffer (*pref, mu_buffer_full, 0);
206   return 0;
207 }
208