mutt stable branch with some hacks
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at jcs 431 lines 11 kB view raw
1/* 2 * Copyright (C) 2005 Andreas Krennmair <ak@synflood.at> 3 * Copyright (C) 2005 Peter J. Holzer <hjp@hjp.net> 4 * Copyright (C) 2005-2009 Rocco Rutte <pdmef@gmx.net> 5 * Copyright (C) 2010 Michael R. Elkins <me@mutt.org> 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation; either version 2 of the License, or 10 * (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 20 * 21 */ 22 23/* This file was originally part of mutt-ng */ 24 25#if HAVE_CONFIG_H 26# include "config.h" 27#endif 28 29#include <stdlib.h> 30#include <string.h> 31#include <unistd.h> 32#include <ctype.h> 33#include <sys/wait.h> 34#include <sys/stat.h> 35 36#include "mutt.h" 37#include "mutt_curses.h" 38#include "ascii.h" 39#include "lib.h" 40#include "mime.h" 41 42#define FLOWED_MAX 72 43 44typedef struct flowed_state 45{ 46 size_t width; 47 size_t spaces; 48 int delsp; 49} flowed_state_t; 50 51static int get_quote_level (const char *line) 52{ 53 int quoted = 0; 54 char *p = (char *) line; 55 56 while (p && *p == '>') 57 { 58 quoted++; 59 p++; 60 } 61 62 return quoted; 63} 64 65/* Determines whether to add spacing between/after each quote level: 66 * >>>foo 67 * becomes 68 * > > > foo 69 */ 70static int space_quotes (STATE *s) 71{ 72 /* Allow quote spacing in the pager even for OPTTEXTFLOWED, 73 * but obviously not when replying. 74 */ 75 if (option (OPTTEXTFLOWED) && (s->flags & MUTT_REPLYING)) 76 return 0; 77 78 return option (OPTREFLOWSPACEQUOTES); 79} 80 81/* Determines whether to add a trailing space to quotes: 82 * >>> foo 83 * as opposed to 84 * >>>foo 85 */ 86static int add_quote_suffix (STATE *s, int ql) 87{ 88 if (s->flags & MUTT_REPLYING) 89 return 0; 90 91 if (space_quotes (s)) 92 return 0; 93 94 if (!ql && !s->prefix) 95 return 0; 96 97 /* The prefix will add its own space */ 98 if (!option (OPTTEXTFLOWED) && !ql && s->prefix) 99 return 0; 100 101 return 1; 102} 103 104static size_t print_indent (int ql, STATE *s, int add_suffix) 105{ 106 int i; 107 size_t wid = 0; 108 109 if (s->prefix) 110 { 111 /* use given prefix only for format=fixed replies to format=flowed, 112 * for format=flowed replies to format=flowed, use '>' indentation 113 */ 114 if (option (OPTTEXTFLOWED)) 115 ql++; 116 else 117 { 118 state_puts (s->prefix, s); 119 wid = mutt_strwidth (s->prefix); 120 } 121 } 122 for (i = 0; i < ql; i++) 123 { 124 state_putc ('>', s); 125 if (space_quotes (s) ) 126 state_putc (' ', s); 127 } 128 if (add_suffix) 129 state_putc (' ', s); 130 131 if (space_quotes (s)) 132 ql *= 2; 133 134 return ql + add_suffix + wid; 135} 136 137static void flush_par (STATE *s, flowed_state_t *fst) 138{ 139 if (fst->width > 0) 140 { 141 state_putc ('\n', s); 142 fst->width = 0; 143 } 144 fst->spaces = 0; 145} 146 147/* Calculate the paragraph width based upon the current quote level. The start 148 * of a quoted line will be ">>> ", so we need to subtract the space required 149 * for the prefix from the terminal width. */ 150static int quote_width (STATE *s, int ql) 151{ 152 int width = mutt_window_wrap_cols (MuttIndexWindow, ReflowWrap); 153 if (option(OPTTEXTFLOWED) && (s->flags & MUTT_REPLYING)) 154 { 155 /* When replying, force a wrap at FLOWED_MAX to comply with RFC3676 156 * guidelines */ 157 if (width > FLOWED_MAX) 158 width = FLOWED_MAX; 159 ++ql; /* When replying, we will add an additional quote level */ 160 } 161 /* adjust the paragraph width subtracting the number of prefix chars */ 162 width -= space_quotes (s) ? ql*2 : ql; 163 /* When displaying (not replying), there may be a space between the prefix 164 * string and the paragraph */ 165 if (add_quote_suffix (s, ql)) 166 --width; 167 /* failsafe for really long quotes */ 168 if (width <= 0) 169 width = FLOWED_MAX; /* arbitrary, since the line will wrap */ 170 return width; 171} 172 173static void print_flowed_line (char *line, STATE *s, int ql, 174 flowed_state_t *fst, int term) 175{ 176 size_t width, w, words = 0; 177 char *p; 178 char last; 179 180 if (!line || !*line) 181 { 182 /* flush current paragraph (if any) first */ 183 flush_par (s, fst); 184 print_indent (ql, s, 0); 185 state_putc ('\n', s); 186 return; 187 } 188 189 width = quote_width (s, ql); 190 last = line[mutt_strlen (line) - 1]; 191 192 dprint (4, (debugfile, "f=f: line [%s], width = %ld, spaces = %d\n", 193 NONULL(line), (long)width, fst->spaces)); 194 195 for (p = (char *)line, words = 0; (p = strsep (&line, " ")) != NULL ; ) 196 { 197 dprint(4,(debugfile,"f=f: word [%s], width: %d, remaining = [%s]\n", 198 p, fst->width, line)); 199 200 /* remember number of spaces */ 201 if (!*p) 202 { 203 dprint(4,(debugfile,"f=f: additional space\n")); 204 fst->spaces++; 205 continue; 206 } 207 /* there's exactly one space prior to every but the first word */ 208 if (words) 209 fst->spaces++; 210 211 w = mutt_strwidth (p); 212 /* see if we need to break the line but make sure the first 213 word is put on the line regardless; 214 if for DelSp=yes only one trailing space is used, we probably 215 have a long word that we should break within (we leave that 216 up to the pager or user) */ 217 if (!(!fst->spaces && fst->delsp && last != ' ') && 218 w < width && w + fst->width + fst->spaces > width) 219 { 220 dprint(4,(debugfile,"f=f: break line at %d, %d spaces left\n", 221 fst->width, fst->spaces)); 222 /* only honor trailing spaces for format=flowed replies */ 223 if (option(OPTTEXTFLOWED)) 224 for ( ; fst->spaces; fst->spaces--) 225 state_putc (' ', s); 226 state_putc ('\n', s); 227 fst->width = 0; 228 fst->spaces = 0; 229 words = 0; 230 } 231 232 if (!words && !fst->width) 233 fst->width = print_indent (ql, s, add_quote_suffix (s, ql)); 234 fst->width += w + fst->spaces; 235 for ( ; fst->spaces; fst->spaces--) 236 state_putc (' ', s); 237 state_puts (p, s); 238 words++; 239 } 240 241 if (term) 242 flush_par (s, fst); 243} 244 245static void print_fixed_line (const char *line, STATE *s, int ql, 246 flowed_state_t *fst) 247{ 248 print_indent (ql, s, add_quote_suffix (s, ql)); 249 if (line && *line) 250 state_puts (line, s); 251 state_putc ('\n', s); 252 253 fst->width = 0; 254 fst->spaces = 0; 255} 256 257int rfc3676_handler (BODY * a, STATE * s) 258{ 259 char *buf = NULL, *t = NULL; 260 unsigned int quotelevel = 0, newql = 0, sigsep = 0; 261 int buf_off = 0, delsp = 0, fixed = 0; 262 size_t buf_len = 0, sz = 0; 263 flowed_state_t fst; 264 265 memset (&fst, 0, sizeof (fst)); 266 267 /* respect DelSp of RfC3676 only with f=f parts */ 268 if ((t = (char *) mutt_get_parameter ("delsp", a->parameter))) 269 { 270 delsp = mutt_strlen (t) == 3 && ascii_strncasecmp (t, "yes", 3) == 0; 271 t = NULL; 272 fst.delsp = 1; 273 } 274 275 dprint (4, (debugfile, "f=f: DelSp: %s\n", delsp ? "yes" : "no")); 276 277 while ((buf = mutt_read_line (buf, &sz, s->fpin, NULL, 0))) 278 { 279 buf_len = mutt_strlen (buf); 280 newql = get_quote_level (buf); 281 282 /* end flowed paragraph (if we're within one) if quoting level 283 * changes (should not but can happen, see RFC 3676, sec. 4.5.) 284 */ 285 if (newql != quotelevel) 286 flush_par (s, &fst); 287 288 quotelevel = newql; 289 buf_off = newql; 290 291 /* respect sender's space-stuffing by removing one leading space */ 292 if (buf[buf_off] == ' ') 293 buf_off++; 294 295 /* test for signature separator */ 296 sigsep = ascii_strcmp (buf + buf_off, "-- ") == 0; 297 298 /* a fixed line either has no trailing space or is the 299 * signature separator */ 300 fixed = buf_len == buf_off || buf[buf_len - 1] != ' ' || sigsep; 301 302 /* print fixed-and-standalone, fixed-and-empty and sigsep lines as 303 * fixed lines */ 304 if ((fixed && (!fst.width || !buf_len)) || sigsep) 305 { 306 /* if we're within a flowed paragraph, terminate it */ 307 flush_par (s, &fst); 308 print_fixed_line (buf + buf_off, s, quotelevel, &fst); 309 continue; 310 } 311 312 /* for DelSp=yes, we need to strip one SP prior to CRLF on flowed lines */ 313 if (delsp && !fixed) 314 buf[--buf_len] = '\0'; 315 316 print_flowed_line (buf + buf_off, s, quotelevel, &fst, fixed); 317 } 318 319 flush_par (s, &fst); 320 321 FREE (&buf); 322 return (0); 323} 324 325/* 326 * This routine does RfC3676 space stuffing since it's a MUST. 327 * Space stuffing means that we have to add leading spaces to 328 * certain lines: 329 * - lines starting with a space 330 * - lines starting with 'From ' 331 * 332 * Care is taken to preserve the hdr->content->filename, as 333 * mutt -i -E can directly edit a passed in filename. 334 */ 335static void rfc3676_space_stuff (HEADER* hdr, int unstuff) 336{ 337 FILE *in = NULL, *out = NULL; 338 char *buf = NULL; 339 size_t blen = 0; 340 BUFFER *tmpfile = NULL; 341 342 tmpfile = mutt_buffer_pool_get (); 343 344 if ((in = safe_fopen (hdr->content->filename, "r")) == NULL) 345 goto bail; 346 347 mutt_buffer_mktemp (tmpfile); 348 if ((out = safe_fopen (mutt_b2s (tmpfile), "w+")) == NULL) 349 goto bail; 350 351 while ((buf = mutt_read_line (buf, &blen, in, NULL, 0)) != NULL) 352 { 353 if (unstuff) 354 { 355 if (buf[0] == ' ') 356 fputs (buf + 1, out); 357 else 358 fputs (buf, out); 359 } 360 else 361 { 362 if (ascii_strncmp ("From ", buf, 5) == 0 || buf[0] == ' ') 363 fputc (' ', out); 364 fputs (buf, out); 365 } 366 fputc ('\n', out); 367 } 368 FREE (&buf); 369 safe_fclose (&in); 370 safe_fclose (&out); 371 mutt_set_mtime (hdr->content->filename, mutt_b2s (tmpfile)); 372 373 if ((in = safe_fopen (mutt_b2s (tmpfile), "r")) == NULL) 374 goto bail; 375 376 if ((truncate (hdr->content->filename, 0) == -1) || 377 ((out = safe_fopen (hdr->content->filename, "a")) == NULL)) 378 { 379 mutt_perror (hdr->content->filename); 380 goto bail; 381 } 382 383 mutt_copy_stream (in, out); 384 safe_fclose (&in); 385 safe_fclose (&out); 386 mutt_set_mtime (mutt_b2s (tmpfile), hdr->content->filename); 387 unlink (mutt_b2s (tmpfile)); 388 mutt_buffer_pool_release (&tmpfile); 389 return; 390 391bail: 392 safe_fclose (&in); 393 safe_fclose (&out); 394 mutt_buffer_pool_release (&tmpfile); 395} 396 397/* Note: we don't check the option OPTTEXTFLOWED because we want to 398 * stuff based the actual content type. The option only decides 399 * whether to *set* format=flowed on new messages. 400 */ 401void mutt_rfc3676_space_stuff (HEADER *hdr) 402{ 403 const char *format; 404 405 if (!hdr || !hdr->content || !hdr->content->filename) 406 return; 407 408 if (hdr->content->type == TYPETEXT && 409 !ascii_strcasecmp ("plain", hdr->content->subtype)) 410 { 411 format = mutt_get_parameter ("format", hdr->content->parameter); 412 if (!ascii_strcasecmp ("flowed", format)) 413 rfc3676_space_stuff (hdr, 0); 414 } 415} 416 417void mutt_rfc3676_space_unstuff (HEADER *hdr) 418{ 419 const char *format; 420 421 if (!hdr || !hdr->content || !hdr->content->filename) 422 return; 423 424 if (hdr->content->type == TYPETEXT && 425 !ascii_strcasecmp ("plain", hdr->content->subtype)) 426 { 427 format = mutt_get_parameter ("format", hdr->content->parameter); 428 if (!ascii_strcasecmp ("flowed", format)) 429 rfc3676_space_stuff (hdr, 1); 430 } 431}