2 * Copyright (C) 2005 Andreas Krennmair <ak@synflood.at>
3 * Copyright (C) 2005 Peter J. Holzer <hjp@hjp.net>
4 * Copyright (C) 2005-7 Rocco Rutte <pdmef@gmx.net>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 /* This file was originally part of mutt-ng */
36 #include "mutt_curses.h"
42 static int get_quote_level (const char *line)
45 char *p = (char *) line;
47 while (p && *p == '>')
56 static size_t print_indent (int ql, STATE *s, int sp)
62 for (i = 0; i < ql; i++)
69 static void flush_par (STATE *s, size_t *sofar)
78 static int quote_width (STATE *s, int ql)
80 size_t width = (Wrap ? mutt_term_width (Wrap) : FLOWED_MAX) - 1;
81 if (s->flags & M_REPLYING && width > FLOWED_MAX)
88 static void print_flowed_line (char *line, STATE *s, int ql, size_t *sofar, int term)
90 size_t width, w, words;
95 /* flush current paragraph (if any) first */
97 print_indent (ql, s, 0);
102 width = quote_width (s, ql);
104 dprint (4, (debugfile, "f-f: line [%s], width = %ld\n", NONULL(line), (long)width));
106 for (p = (char *)line, words = 0; (p = strsep (&line, " ")) != NULL ; )
108 w = mutt_strwidth (NONULL(p));
109 dprint (4, (debugfile, "f-f: word [%s], width = %ld, line = %ld\n", NONULL(p), (long)w, (long)*sofar));
110 if (w + 1 + (*sofar) > width)
112 /* line would be too long, flush */
113 dprint (4, (debugfile, "f-f: width: %ld\n", (long)*sofar));
114 state_puts (" \n", s);
119 /* indent empty lines */
120 *sofar = print_indent (ql, s, ql > 0 || s->prefix);
125 /* put space before current word if we have words already */
129 state_puts (NONULL(p), s);
135 flush_par (s, sofar);
138 static void print_fixed_line (const char *line, STATE *s, int ql, size_t *sofar)
140 int len = mutt_strlen (line);
144 print_indent (ql, s, 0);
145 state_putc ('\n', s);
149 print_indent (ql, s, ql > 0 || s->prefix);
150 state_puts (line, s);
151 state_putc ('\n', s);
156 int rfc3676_handler (BODY * a, STATE * s)
158 int bytes = a->length;
159 char buf[LONG_STRING];
161 unsigned int quotelevel = 0, newql = 0, sigsep = 0;
162 int buf_off = 0, buf_len;
163 int delsp = 0, fixed = 0;
166 /* respect DelSp of RfC3676 only with f=f parts */
167 if ((t = (char *) mutt_get_parameter ("delsp", a->parameter)))
169 delsp = mutt_strlen (t) == 3 && ascii_strncasecmp (t, "yes", 3) == 0;
173 dprint (4, (debugfile, "f=f: DelSp: %s\n", delsp ? "yes" : "no"));
175 while (bytes > 0 && fgets (buf, sizeof (buf), s->fpin))
178 buf_len = mutt_strlen (buf);
181 newql = get_quote_level (buf);
183 /* end flowed paragraph (if we're within one) if quoting level
184 * changes (should not but can happen, see RFC 3676, sec. 4.5.)
186 if (newql != quotelevel)
187 flush_par (s, &width);
191 /* XXX - If a line is longer than buf (shouldn't happen), it is split.
192 * This will almost always cause an unintended line break, and
193 * possibly a change in quoting level. But that's better than not
194 * displaying it at all.
196 if ((t = strrchr (buf, '\r')) || (t = strrchr (buf, '\n')))
204 /* respect sender's space-stuffing by removing one leading space */
205 if (buf[buf_off] == ' ')
208 /* test for signature separator */
209 sigsep = ascii_strcmp (buf + buf_off, "-- ") == 0;
211 /* a fixed line either has no trailing space or is the
212 * signature separator */
213 fixed = buf_len == buf_off || buf[buf_len - 1] != ' ' || sigsep;
215 /* print fixed-and-standalone, fixed-and-empty and sigsep lines as
217 if ((fixed && (!width || !buf_len)) || sigsep)
219 /* if we're within a flowed paragraph, terminate it */
220 flush_par (s, &width);
221 print_fixed_line (buf + buf_off, s, quotelevel, &width);
225 /* for DelSp=yes, we need to strip one SP prior to CRLF on flowed lines */
227 buf[--buf_len] = '\0';
229 print_flowed_line (buf + buf_off, s, quotelevel, &width, fixed);
232 flush_par (s, &width);
238 * This routine does RfC3676 space stuffing since it's a MUST.
239 * Space stuffing means that we have to add leading spaces to
241 * - lines starting with a space
242 * - lines starting with 'From '
243 * This routine is only called once right after editing the
244 * initial message so it's up to the user to take care of stuffing
245 * when editing the message several times before actually sending it
247 * This is more or less a hack as it replaces the message's content with
248 * a freshly created copy in a tempfile and modifies the file's mtime
249 * so we don't trigger code paths watching for mtime changes
251 void rfc3676_space_stuff (HEADER* hdr)
256 unsigned char c = '\0';
258 FILE *in = NULL, *out = NULL;
259 char buf[LONG_STRING];
260 char tmpfile[_POSIX_PATH_MAX];
262 if (!hdr || !hdr->content || !hdr->content->filename)
265 dprint (2, (debugfile, "f=f: postprocess %s\n", hdr->content->filename));
267 if ((in = safe_fopen (hdr->content->filename, "r")) == NULL)
270 mutt_mktemp (tmpfile);
271 if ((out = safe_fopen (tmpfile, "w+")) == NULL)
277 while (fgets (buf, sizeof (buf), in))
279 if (ascii_strncmp ("From ", buf, 5) == 0 || buf[0] == ' ') {
283 len = mutt_strlen (buf);
289 dprint (4, (debugfile, "f=f: line %d needs space-stuffing: '%s'\n",
299 mutt_set_mtime (hdr->content->filename, tmpfile);
300 unlink (hdr->content->filename);
301 mutt_str_replace (&hdr->content->filename, tmpfile);