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