rework includes a bit
[apps/madmutt.git] / rfc3676.c
1 /*
2  * Parts were written/modified by:
3  * Andreas Krennmair <ak@synflood.at>
4  * Peter J. Holzer <hjp@hjp.net>
5  * Rocco Rutte <pdmef@cs.tu-berlin.de>
6  *
7  * This file is part of mutt-ng, see http://www.muttng.org/.
8  * It's licensed under the GNU General Public License,
9  * please see the file GPL in the top level source directory.
10  */
11
12 #if HAVE_CONFIG_H
13 # include "config.h"
14 #endif
15
16 #include <stdlib.h>
17 #include <string.h>
18 #include <unistd.h>
19 #include <ctype.h>
20 #include <sys/wait.h>
21 #include <sys/stat.h>
22
23 #include <lib-lib/mem.h>
24 #include <lib-lib/str.h>
25 #include <lib-lib/ascii.h>
26 #include <lib-lib/macros.h>
27 #include <lib-lib/file.h>
28 #include <lib-lib/debug.h>
29
30 #include <lib-ui/curses.h>
31
32 #include "mutt.h"
33 #include "handler.h"
34 #include "state.h"
35 #include "lib.h"
36
37 #define FLOWED_MAX 77
38
39 static int get_quote_level (char *line)
40 {
41   int quoted;
42
43   for (quoted = 0; line[quoted] == '>'; quoted++);
44   return quoted;
45 }
46
47 static void print_flowed_line (char *line, STATE * s, int ql) {
48   int width;
49   char *pos, *oldpos;
50   int len = m_strlen(line);
51   int i;
52
53   if (MaxLineLength > 0) {
54     width = MaxLineLength - WrapMargin - ql - 1;
55     if (!(s->flags & M_REPLYING) && option (OPTSTUFFQUOTED))
56       --width;
57     if (width < 0)
58       width = MaxLineLength;
59   }
60   else {
61     if (option (OPTMBOXPANE))
62       width = COLS - SidebarWidth - WrapMargin - ql - 1;
63     else
64       width = COLS - WrapMargin - ql - 1;
65
66     if (!(s->flags & M_REPLYING) && option (OPTSTUFFQUOTED))
67       --width;
68     if (width < 0)
69       width = COLS;
70   }
71
72   if (m_strlen(line) == 0) {
73     if (!(s->flags & M_REPLYING) || option (OPTQUOTEEMPTY)) {
74       if (s->prefix)
75         state_puts(s->prefix,s);
76       for (i=0;i<ql;++i) state_putc('>',s);
77       if (!(s->flags & M_REPLYING) && option(OPTSTUFFQUOTED))
78         state_putc(' ',s);
79     }
80     state_putc('\n',s);
81     return;
82   }
83
84   pos = line + width;
85   oldpos = line;
86
87   for (; oldpos < line + len; pos += width) {
88     /* only search a new position when we're not over
89      * the end of the string w/ pos */
90     if (pos < line + len) {
91       if (*pos == ' ') {
92         debug_print (4, ("f=f: found space directly at width\n"));
93         *pos = '\0';
94         ++pos;
95       }
96       else {
97         char *save = pos;
98         debug_print (4, ("f=f: need to search for space\n"));
99
100         while (pos >= oldpos && *pos != ' ') {
101           --pos;
102         }
103         if (pos < oldpos) {
104           debug_print (4, ("f=f: no space found while searching "
105                            "to left; going right\n"));
106           pos = save;
107           while (pos < line + len && *pos && *pos != ' ') {
108             ++pos;
109           }
110           debug_print (4, ("f=f: found space at pos %d\n", pos-line));
111         } else {
112           debug_print (4, ("f=f: found space while searching to left\n"));
113         }
114         *pos = '\0';
115         ++pos;
116       }
117     }
118     else {
119       debug_print (4, ("f=f: line completely fits on screen\n"));
120     }
121     if (s->prefix)
122       state_puts (s->prefix, s);
123
124     for (i = 0; i < ql; ++i)
125       state_putc ('>', s);
126     if (!(s->flags & M_REPLYING) && option (OPTSTUFFQUOTED) &&
127         (ql > 0 || s->prefix))
128       state_putc (' ', s);
129     state_puts (oldpos, s);
130     /* fprintf(stderr,"print_flowed_line: `%s'\n",oldpos); */
131     if (pos < line + len)
132       state_putc (' ', s);
133     state_putc ('\n', s);
134     oldpos = pos;
135   }
136 }
137
138 int rfc3676_handler (BODY * a, STATE * s) {
139   int bytes = a->length;
140   char buf[LONG_STRING];
141   char *curline = p_new(char, 1);
142   char *t = NULL;
143   unsigned int curline_len = 1,
144                quotelevel = 0, newql = 0;
145   int buf_off, buf_len;
146   int delsp = 0, fixed = 0;
147
148   *curline='\0';
149
150   /* respect DelSP of RfC3676 only with f=f parts */
151   if ((t = (char*) mutt_get_parameter ("delsp", a->parameter))) {
152     delsp = m_strlen(t) == 3 && ascii_strncasecmp (t, "yes", 3) == 0;
153     t = NULL;
154   }
155
156   debug_print (2, ("f=f: DelSp: %s\n", delsp ? "yes" : "no"));
157
158   while (bytes > 0 && fgets (buf, sizeof (buf), s->fpin)) {
159
160     buf_len = m_strlen(buf);
161     bytes -= buf_len;
162
163     newql = get_quote_level (buf);
164
165     /* a change of quoting level in a paragraph - shouldn't happen, 
166      * but has to be handled - see RFC 3676, sec. 4.5.
167      */
168     if (newql != quotelevel && curline && *curline) {
169       print_flowed_line (curline, s, quotelevel);
170       *curline = '\0';
171       curline_len = 1;
172     }
173     quotelevel = newql;
174
175     /* XXX - If a line is longer than buf (shouldn't happen), it is split.
176      * This will almost always cause an unintended line break, and 
177      * possibly a change in quoting level. But that's better than not
178      * displaying it at all.
179      */
180     if ((t = strrchr (buf, '\r')) || (t = strrchr (buf, '\n'))) {
181       *t = '\0';
182       buf_len = t - buf;
183     }
184
185     buf_off = newql;
186     /* respect space-stuffing */
187     if (buf[buf_off] == ' ')
188       buf_off++;
189
190     /* for DelSp=yes, we need to strip one SP prior to CRLF
191      * which may make the line look like fixed although it wasn't
192      * so keep this in mind for later processing */
193     fixed = buf_len == 0 || buf[buf_len - 1] != ' ' ||
194             (strcmp(buf + buf_off, "-- ") == 0);
195
196     if (delsp && buf_len >= 1 && buf[buf_len-1] == ' ')
197       buf[--buf_len] = '\0';
198
199     /* we're here when last space removed 'cause of DelSp was
200      * the last space and there isn't more -> done */
201     if ((buf_len - buf_off) < 0) {
202       print_flowed_line (curline, s, quotelevel);
203       *curline = '\0';
204       curline_len = 1;
205       continue;
206     }
207
208     /* signature separator also flushes the previous paragraph */
209     if (strcmp(buf + buf_off, "-- ") == 0 && curline && *curline) {
210       print_flowed_line (curline, s, quotelevel);
211       *curline = '\0';
212       curline_len = 1;
213     }
214
215     p_realloc(&curline, curline_len + buf_len - buf_off);
216     strcpy (curline + curline_len - 1, buf + buf_off);
217     curline_len += buf_len - buf_off;
218
219     /* if this was a fixed line the paragraph is finished */
220     if (fixed) {
221       print_flowed_line (curline, s, quotelevel);
222       *curline = '\0';
223       curline_len = 1;
224     }
225
226   }
227   p_delete(&curline);
228   return (0);
229 }
230
231 void rfc3676_space_stuff (HEADER* hdr) {
232 #if DEBUG
233   int lc = 0;
234   size_t len = 0;
235   unsigned char c = '\0';
236 #endif
237   FILE* in = NULL, *out = NULL;
238   char buf[LONG_STRING];
239   char tmpfile[_POSIX_PATH_MAX];
240
241   if (!hdr || !hdr->content || !hdr->content->filename)
242     return;
243
244   debug_print (2, ("f=f: postprocess %s\n", hdr->content->filename));
245   if ((in = safe_fopen (hdr->content->filename, "r")) == NULL)
246     return;
247   mutt_mktemp (tmpfile);
248   if ((out = safe_fopen (tmpfile, "w+")) == NULL) {
249     fclose (in);
250     return;
251   }
252
253   while (fgets (buf, sizeof (buf), in)) {
254     if (ascii_strncmp ("From ", buf, 4) == 0 || buf[0] == ' ') {
255       fputc (' ', out);
256 #if DEBUG
257       lc++;
258       len = m_strlen(buf);
259       if (len > 0) {
260         c = buf[len-1];
261         buf[len-1] = '\0';
262       }
263       debug_print (4, ("f=f: line %d needs space-stuffing: '%s'\n",
264                        lc, buf));
265       if (len > 0)
266         buf[len-1] = c;
267 #endif
268     }
269     fputs (buf, out);
270   }
271   fclose (in);
272   fclose (out);
273   mutt_set_mtime (hdr->content->filename, tmpfile);
274   unlink (hdr->content->filename);
275   m_strreplace(&hdr->content->filename, tmpfile);
276 }