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