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