NeoMutt  2018-07-16 +952-a2da0a
Teaching an old dog new tricks
DOXYGEN
rfc3676.c File Reference

RFC3676 Format Flowed routines. More...

#include "config.h"
#include <limits.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "mutt/mutt.h"
#include "email/lib.h"
#include "curs_lib.h"
#include "globals.h"
#include "mutt_window.h"
#include "muttlib.h"
#include "state.h"
+ Include dependency graph for rfc3676.c:

Go to the source code of this file.

Data Structures

struct  FlowedState
 State of a Format-Flowed line of text. More...
 

Macros

#define FLOWED_MAX   72
 

Functions

static int get_quote_level (const char *line)
 Get the quote level of a line. More...
 
static int space_quotes (struct State *s)
 Should we add spaces between quote levels. More...
 
static bool add_quote_suffix (struct State *s, int ql)
 Should we add a trailing space to quotes. More...
 
static size_t print_indent (int ql, struct State *s, int add_suffix)
 Print indented text. More...
 
static void flush_par (struct State *s, struct FlowedState *fst)
 Write out the paragraph. More...
 
static int quote_width (struct State *s, int ql)
 Calculate the paragraph width based upon the quote level. More...
 
static void print_flowed_line (char *line, struct State *s, int ql, struct FlowedState *fst, bool term)
 Print a format-flowed line. More...
 
static void print_fixed_line (const char *line, struct State *s, int ql, struct FlowedState *fst)
 Print a fixed format line. More...
 
int rfc3676_handler (struct Body *a, struct State *s)
 Body handler implementing RFC3676 for format=flowed - Implements handler_t. More...
 
void rfc3676_space_stuff (struct Email *e)
 Perform required RFC3676 space stuffing. More...
 

Variables

bool ReflowSpaceQuotes
 Config: Insert spaces into reply quotes for 'format=flowed' messages. More...
 
short ReflowWrap
 Config: Maximum paragraph width for reformatting 'format=flowed' text. More...
 

Detailed Description

RFC3676 Format Flowed routines.

Authors
  • Andreas Krennmair
  • Peter J. Holzer
  • Rocco Rutte
  • Michael R. Elkins

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.

Definition in file rfc3676.c.

Macro Definition Documentation

#define FLOWED_MAX   72

Definition at line 50 of file rfc3676.c.

Function Documentation

static int get_quote_level ( const char *  line)
static

Get the quote level of a line.

Parameters
lineText to examine
Return values
numQuote level

Definition at line 67 of file rfc3676.c.

68 {
69  int quoted = 0;
70  char *p = (char *) line;
71 
72  while (p && *p == '>')
73  {
74  quoted++;
75  p++;
76  }
77 
78  return quoted;
79 }
const char * line
Definition: common.c:35

+ Here is the caller graph for this function:

static int space_quotes ( struct State s)
static

Should we add spaces between quote levels.

Parameters
sState to use
Return values
trueIf spaces should be added

Determines whether to add spacing between/after each quote level: >>>foo becomes > > > foo

Definition at line 91 of file rfc3676.c.

92 {
93  /* Allow quote spacing in the pager even for TextFlowed,
94  * but obviously not when replying.
95  */
96  if (TextFlowed && (s->flags & MUTT_REPLYING))
97  return 0;
98 
99  return ReflowSpaceQuotes;
100 }
WHERE bool TextFlowed
Config: Generate &#39;format=flowed&#39; messages.
Definition: globals.h:259
int flags
Definition: state.h:36
bool ReflowSpaceQuotes
Config: Insert spaces into reply quotes for &#39;format=flowed&#39; messages.
Definition: rfc3676.c:47
#define MUTT_REPLYING
are we replying?
Definition: state.h:46

+ Here is the caller graph for this function:

static bool add_quote_suffix ( struct State s,
int  ql 
)
static

Should we add a trailing space to quotes.

Parameters
sState to use
qlQuote level
Return values
trueIf spaces should be added

Determines whether to add a trailing space to quotes: >>> foo as opposed to >>>foo

Definition at line 113 of file rfc3676.c.

114 {
115  if (s->flags & MUTT_REPLYING)
116  return false;
117 
118  if (space_quotes(s))
119  return false;
120 
121  if (!ql && !s->prefix)
122  return false;
123 
124  /* The prefix will add its own space */
125  if (!TextFlowed && !ql && s->prefix)
126  return false;
127 
128  return true;
129 }
static int space_quotes(struct State *s)
Should we add spaces between quote levels.
Definition: rfc3676.c:91
char * prefix
Definition: state.h:35
WHERE bool TextFlowed
Config: Generate &#39;format=flowed&#39; messages.
Definition: globals.h:259
int flags
Definition: state.h:36
#define MUTT_REPLYING
are we replying?
Definition: state.h:46

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

static size_t print_indent ( int  ql,
struct State s,
int  add_suffix 
)
static

Print indented text.

Parameters
qlQuote level
sState to work with
add_suffixIf true, write a trailing space character
Return values
numNumber of characters written

Definition at line 138 of file rfc3676.c.

139 {
140  size_t wid = 0;
141 
142  if (s->prefix)
143  {
144  /* use given prefix only for format=fixed replies to format=flowed,
145  * for format=flowed replies to format=flowed, use '>' indentation
146  */
147  if (TextFlowed)
148  ql++;
149  else
150  {
151  state_puts(s->prefix, s);
152  wid = mutt_strwidth(s->prefix);
153  }
154  }
155  for (int i = 0; i < ql; i++)
156  {
157  state_putc('>', s);
158  if (space_quotes(s))
159  state_putc(' ', s);
160  }
161  if (add_suffix)
162  state_putc(' ', s);
163 
164  if (space_quotes(s))
165  ql *= 2;
166 
167  return ql + add_suffix + wid;
168 }
static int space_quotes(struct State *s)
Should we add spaces between quote levels.
Definition: rfc3676.c:91
char * prefix
Definition: state.h:35
WHERE bool TextFlowed
Config: Generate &#39;format=flowed&#39; messages.
Definition: globals.h:259
#define state_putc(x, y)
Definition: state.h:52
int mutt_strwidth(const char *s)
Measure a string&#39;s width in screen cells.
Definition: curs_lib.c:1176
#define state_puts(x, y)
Definition: state.h:51

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

static void flush_par ( struct State s,
struct FlowedState fst 
)
static

Write out the paragraph.

Parameters
sState to work with
fstThe state of the flowed text

Definition at line 175 of file rfc3676.c.

176 {
177  if (fst->width > 0)
178  {
179  state_putc('\n', s);
180  fst->width = 0;
181  }
182  fst->spaces = 0;
183 }
size_t spaces
Definition: rfc3676.c:58
#define state_putc(x, y)
Definition: state.h:52
size_t width
Definition: rfc3676.c:57

+ Here is the caller graph for this function:

static int quote_width ( struct State s,
int  ql 
)
static

Calculate the paragraph width based upon the quote level.

Parameters
sState to use
qlQuote level
Return values
intParagraph width

The start of a quoted line will be ">>> ", so we need to subtract the space required for the prefix from the terminal width.

Definition at line 194 of file rfc3676.c.

195 {
197  if (TextFlowed && (s->flags & MUTT_REPLYING))
198  {
199  /* When replying, force a wrap at FLOWED_MAX to comply with RFC3676
200  * guidelines */
201  if (width > FLOWED_MAX)
202  width = FLOWED_MAX;
203  ql++; /* When replying, we will add an additional quote level */
204  }
205  /* adjust the paragraph width subtracting the number of prefix chars */
206  width -= space_quotes(s) ? ql * 2 : ql;
207  /* When displaying (not replying), there may be a space between the prefix
208  * string and the paragraph */
209  if (add_quote_suffix(s, ql))
210  width--;
211  /* failsafe for really long quotes */
212  if (width <= 0)
213  width = FLOWED_MAX; /* arbitrary, since the line will wrap */
214  return width;
215 }
static bool add_quote_suffix(struct State *s, int ql)
Should we add a trailing space to quotes.
Definition: rfc3676.c:113
static int space_quotes(struct State *s)
Should we add spaces between quote levels.
Definition: rfc3676.c:91
WHERE bool TextFlowed
Config: Generate &#39;format=flowed&#39; messages.
Definition: globals.h:259
int flags
Definition: state.h:36
short ReflowWrap
Config: Maximum paragraph width for reformatting &#39;format=flowed&#39; text.
Definition: rfc3676.c:48
struct MuttWindow * MuttIndexWindow
Index Window.
Definition: mutt_window.c:39
#define FLOWED_MAX
Definition: rfc3676.c:50
#define MUTT_REPLYING
are we replying?
Definition: state.h:46
int mutt_window_wrap_cols(struct MuttWindow *win, short wrap)
Calculate the wrap column for a Window.
Definition: mutt_window.c:309

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

static void print_flowed_line ( char *  line,
struct State s,
int  ql,
struct FlowedState fst,
bool  term 
)
static

Print a format-flowed line.

Parameters
lineText to print
sState to work with
qlQuote level
fstThe state of the flowed text
termIf true, terminate with a new line

Definition at line 225 of file rfc3676.c.

227 {
228  size_t width, w, words = 0;
229  char *p = NULL;
230  char last;
231 
232  if (!line || !*line)
233  {
234  /* flush current paragraph (if any) first */
235  flush_par(s, fst);
236  print_indent(ql, s, 0);
237  state_putc('\n', s);
238  return;
239  }
240 
241  width = quote_width(s, ql);
242  last = line[mutt_str_strlen(line) - 1];
243 
244  mutt_debug(4, "f=f: line [%s], width = %ld, spaces = %lu\n", line,
245  (long) width, fst->spaces);
246 
247  for (words = 0; (p = strsep(&line, " "));)
248  {
249  mutt_debug(4, "f=f: word [%s], width: %lu, remaining = [%s]\n", p, fst->width, line);
250 
251  /* remember number of spaces */
252  if (!*p)
253  {
254  mutt_debug(4, "f=f: additional space\n");
255  fst->spaces++;
256  continue;
257  }
258  /* there's exactly one space prior to every but the first word */
259  if (words)
260  fst->spaces++;
261 
262  w = mutt_strwidth(p);
263  /* see if we need to break the line but make sure the first
264  word is put on the line regardless;
265  if for DelSp=yes only one trailing space is used, we probably
266  have a long word that we should break within (we leave that
267  up to the pager or user) */
268  if (!(!fst->spaces && fst->delsp && last != ' ') && w < width &&
269  w + fst->width + fst->spaces > width)
270  {
271  mutt_debug(4, "f=f: break line at %lu, %lu spaces left\n", fst->width, fst->spaces);
272  /* only honor trailing spaces for format=flowed replies */
273  if (TextFlowed)
274  for (; fst->spaces; fst->spaces--)
275  state_putc(' ', s);
276  state_putc('\n', s);
277  fst->width = 0;
278  fst->spaces = 0;
279  words = 0;
280  }
281 
282  if (!words && !fst->width)
283  fst->width = print_indent(ql, s, add_quote_suffix(s, ql));
284  fst->width += w + fst->spaces;
285  for (; fst->spaces; fst->spaces--)
286  state_putc(' ', s);
287  state_puts(p, s);
288  words++;
289  }
290 
291  if (term)
292  flush_par(s, fst);
293 }
static bool add_quote_suffix(struct State *s, int ql)
Should we add a trailing space to quotes.
Definition: rfc3676.c:113
static int quote_width(struct State *s, int ql)
Calculate the paragraph width based upon the quote level.
Definition: rfc3676.c:194
size_t spaces
Definition: rfc3676.c:58
size_t mutt_str_strlen(const char *a)
Calculate the length of a string, safely.
Definition: string.c:663
WHERE bool TextFlowed
Config: Generate &#39;format=flowed&#39; messages.
Definition: globals.h:259
#define state_putc(x, y)
Definition: state.h:52
const char * line
Definition: common.c:35
static size_t print_indent(int ql, struct State *s, int add_suffix)
Print indented text.
Definition: rfc3676.c:138
int mutt_strwidth(const char *s)
Measure a string&#39;s width in screen cells.
Definition: curs_lib.c:1176
int delsp
Definition: rfc3676.c:59
#define state_puts(x, y)
Definition: state.h:51
size_t width
Definition: rfc3676.c:57
static void flush_par(struct State *s, struct FlowedState *fst)
Write out the paragraph.
Definition: rfc3676.c:175
#define mutt_debug(LEVEL,...)
Definition: logging.h:85

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

static void print_fixed_line ( const char *  line,
struct State s,
int  ql,
struct FlowedState fst 
)
static

Print a fixed format line.

Parameters
lineText to print
sState to work with
qlQuote level
fstThe state of the flowed text

Definition at line 302 of file rfc3676.c.

303 {
304  print_indent(ql, s, add_quote_suffix(s, ql));
305  if (line && *line)
306  state_puts(line, s);
307  state_putc('\n', s);
308 
309  fst->width = 0;
310  fst->spaces = 0;
311 }
static bool add_quote_suffix(struct State *s, int ql)
Should we add a trailing space to quotes.
Definition: rfc3676.c:113
size_t spaces
Definition: rfc3676.c:58
#define state_putc(x, y)
Definition: state.h:52
const char * line
Definition: common.c:35
static size_t print_indent(int ql, struct State *s, int add_suffix)
Print indented text.
Definition: rfc3676.c:138
#define state_puts(x, y)
Definition: state.h:51
size_t width
Definition: rfc3676.c:57

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

int rfc3676_handler ( struct Body a,
struct State s 
)

Body handler implementing RFC3676 for format=flowed - Implements handler_t.

Return values
0Always

Definition at line 317 of file rfc3676.c.

318 {
319  char *buf = NULL;
320  unsigned int quotelevel = 0;
321  int delsp = 0;
322  size_t sz = 0;
323  struct FlowedState fst = { 0 };
324 
325  /* respect DelSp of RFC3676 only with f=f parts */
326  char *t = mutt_param_get(&a->parameter, "delsp");
327  if (t)
328  {
329  delsp = mutt_str_strcasecmp(t, "yes") == 0;
330  t = NULL;
331  fst.delsp = 1;
332  }
333 
334  mutt_debug(4, "f=f: DelSp: %s\n", delsp ? "yes" : "no");
335 
336  while ((buf = mutt_file_read_line(buf, &sz, s->fpin, NULL, 0)))
337  {
338  const size_t buf_len = mutt_str_strlen(buf);
339  const unsigned int newql = get_quote_level(buf);
340 
341  /* end flowed paragraph (if we're within one) if quoting level
342  * changes (should not but can happen, see RFC3676, sec. 4.5.)
343  */
344  if (newql != quotelevel)
345  flush_par(s, &fst);
346 
347  quotelevel = newql;
348  int buf_off = newql;
349 
350  /* respect sender's space-stuffing by removing one leading space */
351  if (buf[buf_off] == ' ')
352  buf_off++;
353 
354  /* test for signature separator */
355  const unsigned int sigsep = (mutt_str_strcmp(buf + buf_off, "-- ") == 0);
356 
357  /* a fixed line either has no trailing space or is the
358  * signature separator */
359  const bool fixed = (buf_len == buf_off) || (buf[buf_len - 1] != ' ') || sigsep;
360 
361  /* print fixed-and-standalone, fixed-and-empty and sigsep lines as
362  * fixed lines */
363  if ((fixed && ((fst.width == 0) || (buf_len == 0))) || sigsep)
364  {
365  /* if we're within a flowed paragraph, terminate it */
366  flush_par(s, &fst);
367  print_fixed_line(buf + buf_off, s, quotelevel, &fst);
368  continue;
369  }
370 
371  /* for DelSp=yes, we need to strip one SP prior to CRLF on flowed lines */
372  if (delsp && !fixed)
373  buf[buf_len - 1] = '\0';
374 
375  print_flowed_line(buf + buf_off, s, quotelevel, &fst, fixed);
376  }
377 
378  flush_par(s, &fst);
379 
380  FREE(&buf);
381  return 0;
382 }
char * mutt_file_read_line(char *line, size_t *size, FILE *fp, int *line_num, int flags)
Read a line from a file.
Definition: file.c:632
size_t mutt_str_strlen(const char *a)
Calculate the length of a string, safely.
Definition: string.c:663
int delsp
Definition: rfc3676.c:59
static int get_quote_level(const char *line)
Get the quote level of a line.
Definition: rfc3676.c:67
static void print_fixed_line(const char *line, struct State *s, int ql, struct FlowedState *fst)
Print a fixed format line.
Definition: rfc3676.c:302
FILE * fpin
Definition: state.h:33
size_t width
Definition: rfc3676.c:57
char * mutt_param_get(const struct ParameterList *p, const char *s)
Find a matching Parameter.
Definition: parameter.c:81
State of a Format-Flowed line of text.
Definition: rfc3676.c:55
int mutt_str_strcasecmp(const char *a, const char *b)
Compare two strings ignoring case, safely.
Definition: string.c:625
#define FREE(x)
Definition: memory.h:46
static void flush_par(struct State *s, struct FlowedState *fst)
Write out the paragraph.
Definition: rfc3676.c:175
#define mutt_debug(LEVEL,...)
Definition: logging.h:85
static void print_flowed_line(char *line, struct State *s, int ql, struct FlowedState *fst, bool term)
Print a format-flowed line.
Definition: rfc3676.c:225
struct ParameterList parameter
parameters of the content-type
Definition: body.h:38
int mutt_str_strcmp(const char *a, const char *b)
Compare two strings, safely.
Definition: string.c:612

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

void rfc3676_space_stuff ( struct Email e)

Perform required RFC3676 space stuffing.

Parameters
eEmail

Space stuffing means that we have to add leading spaces to certain lines:

  • lines starting with a space
  • lines starting with 'From ' This routine is only called once right after editing the initial message so it's up to the user to take care of stuffing when editing the message several times before actually sending it

This is more or less a hack as it replaces the message's content with a freshly created copy in a tempfile and modifies the file's mtime so we don't trigger code paths watching for mtime changes

Definition at line 400 of file rfc3676.c.

401 {
402  int lc = 0;
403  size_t len = 0;
404  unsigned char c = '\0';
405  FILE *in = NULL, *out = NULL;
406  char buf[LONG_STRING];
407  char tmpfile[PATH_MAX];
408 
409  if (!e || !e->content || !e->content->filename)
410  return;
411 
412  mutt_debug(2, "f=f: postprocess %s\n", e->content->filename);
413 
414  in = mutt_file_fopen(e->content->filename, "r");
415  if (!in)
416  return;
417 
418  mutt_mktemp(tmpfile, sizeof(tmpfile));
419  out = mutt_file_fopen(tmpfile, "w+");
420  if (!out)
421  {
422  mutt_file_fclose(&in);
423  return;
424  }
425 
426  while (fgets(buf, sizeof(buf), in))
427  {
428  if (buf[0] == ' ' || mutt_str_startswith(buf, "From ", CASE_MATCH))
429  {
430  fputc(' ', out);
431  lc++;
432  len = mutt_str_strlen(buf);
433  if (len > 0)
434  {
435  c = buf[len - 1];
436  buf[len - 1] = '\0';
437  }
438  mutt_debug(4, "f=f: line %d needs space-stuffing: '%s'\n", lc, buf);
439  if (len > 0)
440  buf[len - 1] = c;
441  }
442  fputs(buf, out);
443  }
444  mutt_file_fclose(&in);
445  mutt_file_fclose(&out);
446  mutt_file_set_mtime(e->content->filename, tmpfile);
447  unlink(e->content->filename);
448  mutt_str_replace(&e->content->filename, tmpfile);
449 }
char * filename
when sending a message, this is the file to which this structure refers
Definition: body.h:47
struct Body * content
list of MIME parts
Definition: email.h:92
Match case when comparing strings.
Definition: string2.h:69
size_t mutt_str_strlen(const char *a)
Calculate the length of a string, safely.
Definition: string.c:663
#define LONG_STRING
Definition: string2.h:36
void mutt_file_set_mtime(const char *from, const char *to)
Set the modification time of one file from another.
Definition: file.c:914
#define mutt_mktemp(a, b)
Definition: muttlib.h:71
#define PATH_MAX
Definition: mutt.h:46
void mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:460
size_t mutt_str_startswith(const char *str, const char *prefix, enum CaseSensitivity cs)
Check whether a string starts with a prefix.
Definition: string.c:166
FILE * mutt_file_fopen(const char *path, const char *mode)
Call fopen() safely.
Definition: file.c:548
int mutt_file_fclose(FILE **f)
Close a FILE handle (and NULL the pointer)
Definition: file.c:149
#define mutt_debug(LEVEL,...)
Definition: logging.h:85

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

Variable Documentation

bool ReflowSpaceQuotes

Config: Insert spaces into reply quotes for 'format=flowed' messages.

Definition at line 47 of file rfc3676.c.

short ReflowWrap

Config: Maximum paragraph width for reformatting 'format=flowed' text.

Definition at line 48 of file rfc3676.c.