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