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