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