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