NeoMutt  2018-07-16 +1783-b00bd9
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 C_ReflowSpaceQuotes
 Config: Insert spaces into reply quotes for 'format=flowed' messages. More...
 
short C_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 C_TextFlowed,
94  * but obviously not when replying. */
95  if (C_TextFlowed && (s->flags & MUTT_REPLYING))
96  return 0;
97 
98  return C_ReflowSpaceQuotes;
99 }
StateFlags flags
Flags, e.g. MUTT_DISPLAY.
Definition: state.h:49
WHERE bool C_TextFlowed
Config: Generate &#39;format=flowed&#39; messages.
Definition: globals.h:255
#define MUTT_REPLYING
Are we replying?
Definition: state.h:38
bool C_ReflowSpaceQuotes
Config: Insert spaces into reply quotes for &#39;format=flowed&#39; messages.
Definition: rfc3676.c:47

+ 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 112 of file rfc3676.c.

113 {
114  if (s->flags & MUTT_REPLYING)
115  return false;
116 
117  if (space_quotes(s))
118  return false;
119 
120  if (!ql && !s->prefix)
121  return false;
122 
123  /* The prefix will add its own space */
124  if (!C_TextFlowed && !ql && s->prefix)
125  return false;
126 
127  return true;
128 }
static int space_quotes(struct State *s)
Should we add spaces between quote levels.
Definition: rfc3676.c:91
char * prefix
String to add to the beginning of each output line.
Definition: state.h:48
StateFlags flags
Flags, e.g. MUTT_DISPLAY.
Definition: state.h:49
WHERE bool C_TextFlowed
Config: Generate &#39;format=flowed&#39; messages.
Definition: globals.h:255
#define MUTT_REPLYING
Are we replying?
Definition: state.h:38

+ 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 137 of file rfc3676.c.

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

+ 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 173 of file rfc3676.c.

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

+ 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
numParagraph 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 192 of file rfc3676.c.

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

+ 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 223 of file rfc3676.c.

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

+ 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 300 of file rfc3676.c.

301 {
302  print_indent(ql, s, add_quote_suffix(s, ql));
303  if (line && *line)
304  state_puts(line, s);
305  state_putc('\n', s);
306 
307  fst->width = 0;
308  fst->spaces = 0;
309 }
static bool add_quote_suffix(struct State *s, int ql)
Should we add a trailing space to quotes.
Definition: rfc3676.c:112
size_t spaces
Definition: rfc3676.c:58
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:137
size_t width
Definition: rfc3676.c:57
#define state_puts(str, state)
Definition: state.h:54
#define state_putc(str, state)
Definition: state.h:55

+ 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 315 of file rfc3676.c.

316 {
317  char *buf = NULL;
318  unsigned int quotelevel = 0;
319  int delsp = 0;
320  size_t sz = 0;
321  struct FlowedState fst = { 0 };
322 
323  /* respect DelSp of RFC3676 only with f=f parts */
324  char *t = mutt_param_get(&a->parameter, "delsp");
325  if (t)
326  {
327  delsp = mutt_str_strcasecmp(t, "yes") == 0;
328  t = NULL;
329  fst.delsp = 1;
330  }
331 
332  mutt_debug(LL_DEBUG3, "f=f: DelSp: %s\n", delsp ? "yes" : "no");
333 
334  while ((buf = mutt_file_read_line(buf, &sz, s->fp_in, NULL, 0)))
335  {
336  const size_t buf_len = mutt_str_strlen(buf);
337  const unsigned int newql = get_quote_level(buf);
338 
339  /* end flowed paragraph (if we're within one) if quoting level
340  * changes (should not but can happen, see RFC3676, sec. 4.5.) */
341  if (newql != quotelevel)
342  flush_par(s, &fst);
343 
344  quotelevel = newql;
345  int buf_off = newql;
346 
347  /* respect sender's space-stuffing by removing one leading space */
348  if (buf[buf_off] == ' ')
349  buf_off++;
350 
351  /* test for signature separator */
352  const unsigned int sigsep = (mutt_str_strcmp(buf + buf_off, "-- ") == 0);
353 
354  /* a fixed line either has no trailing space or is the
355  * signature separator */
356  const bool fixed = (buf_len == buf_off) || (buf[buf_len - 1] != ' ') || sigsep;
357 
358  /* print fixed-and-standalone, fixed-and-empty and sigsep lines as
359  * fixed lines */
360  if ((fixed && ((fst.width == 0) || (buf_len == 0))) || sigsep)
361  {
362  /* if we're within a flowed paragraph, terminate it */
363  flush_par(s, &fst);
364  print_fixed_line(buf + buf_off, s, quotelevel, &fst);
365  continue;
366  }
367 
368  /* for DelSp=yes, we need to strip one SP prior to CRLF on flowed lines */
369  if (delsp && !fixed)
370  buf[buf_len - 1] = '\0';
371 
372  print_flowed_line(buf + buf_off, s, quotelevel, &fst, fixed);
373  }
374 
375  flush_par(s, &fst);
376 
377  FREE(&buf);
378  return 0;
379 }
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:657
size_t mutt_str_strlen(const char *a)
Calculate the length of a string, safely.
Definition: string.c:669
FILE * fp_in
File to read from.
Definition: state.h:46
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:300
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:83
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:631
#define FREE(x)
Definition: memory.h:40
static void flush_par(struct State *s, struct FlowedState *fst)
Write out the paragraph.
Definition: rfc3676.c:173
#define mutt_debug(LEVEL,...)
Definition: logging.h:80
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:223
struct ParameterList parameter
parameters of the content-type
Definition: body.h:39
int mutt_str_strcmp(const char *a, const char *b)
Compare two strings, safely.
Definition: string.c:618
Log at debug level 3.
Definition: logging.h:58

+ 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 397 of file rfc3676.c.

398 {
399  int lc = 0;
400  size_t len = 0;
401  unsigned char c = '\0';
402  char buf[1024];
403  char tmpfile[PATH_MAX];
404 
405  if (!e || !e->content || !e->content->filename)
406  return;
407 
408  mutt_debug(LL_DEBUG2, "f=f: postprocess %s\n", e->content->filename);
409 
410  FILE *fp_in = mutt_file_fopen(e->content->filename, "r");
411  if (!fp_in)
412  return;
413 
414  mutt_mktemp(tmpfile, sizeof(tmpfile));
415  FILE *fp_out = mutt_file_fopen(tmpfile, "w+");
416  if (!fp_out)
417  {
418  mutt_file_fclose(&fp_in);
419  return;
420  }
421 
422  while (fgets(buf, sizeof(buf), fp_in))
423  {
424  if ((buf[0] == ' ') || mutt_str_startswith(buf, "From ", CASE_MATCH))
425  {
426  fputc(' ', fp_out);
427  lc++;
428  len = mutt_str_strlen(buf);
429  if (len > 0)
430  {
431  c = buf[len - 1];
432  buf[len - 1] = '\0';
433  }
434  mutt_debug(5, "f=f: line %d needs space-stuffing: '%s'\n", lc, buf);
435  if (len > 0)
436  buf[len - 1] = c;
437  }
438  fputs(buf, fp_out);
439  }
440  mutt_file_fclose(&fp_in);
441  mutt_file_fclose(&fp_out);
442  mutt_file_set_mtime(e->content->filename, tmpfile);
443  unlink(e->content->filename);
444  mutt_str_replace(&e->content->filename, tmpfile);
445 }
char * filename
when sending a message, this is the file to which this structure refers
Definition: body.h:48
void mutt_file_set_mtime(const char *from, const char *to)
Set the modification time of one file from another.
Definition: file.c:987
struct Body * content
list of MIME parts
Definition: email.h:93
Match case when comparing strings.
Definition: string2.h:66
size_t mutt_str_strlen(const char *a)
Calculate the length of a string, safely.
Definition: string.c:669
Log at debug level 2.
Definition: logging.h:57
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:149
#define mutt_mktemp(buf, buflen)
Definition: muttlib.h:74
#define PATH_MAX
Definition: mutt.h:49
void mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:456
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:168
#define mutt_debug(LEVEL,...)
Definition: logging.h:80
FILE * mutt_file_fopen(const char *path, const char *mode)
Call fopen() safely.
Definition: file.c:578

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

Variable Documentation

bool C_ReflowSpaceQuotes

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

Definition at line 47 of file rfc3676.c.

short C_ReflowWrap

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

Definition at line 48 of file rfc3676.c.