]>
Commit | Line | Data |
---|---|---|
9de0e450 | 1 | diff -pruN2 mutt-1.3.27.orig/thread.c mutt-1.3.27/thread.c |
2 | --- mutt-1.3.27.orig/thread.c Wed Jan 16 21:43:59 2002 | |
3 | +++ mutt-1.3.27/thread.c Wed Jan 23 22:26:56 2002 | |
4 | @@ -1285,2 +1285,78 @@ HASH *mutt_make_subj_hash (CONTEXT *ctx) | |
5 | return hash; | |
6 | } | |
7 | + | |
8 | +static void clean_references (THREAD *brk, THREAD *cur) | |
9 | +{ | |
10 | + THREAD *p; | |
11 | + LIST *ref = NULL; | |
12 | + int done = 0; | |
13 | + | |
14 | + for (; cur; cur = cur->next, done = 0) | |
15 | + { | |
16 | + /* parse subthread recursively */ | |
17 | + clean_references (brk, cur->child); | |
18 | + | |
19 | + if (!cur->message) | |
20 | + break; /* skip pseudo-message */ | |
21 | + | |
22 | + /* Looking for the first bad reference according to the new threading. | |
23 | + * Optimal since Mutt stores the references in reverse order, and the | |
24 | + * first loop should match immediatly for mails respecting RFC2822. */ | |
25 | + for (p = brk; !done && p; p = p->parent) | |
26 | + for (ref = cur->message->env->references; p->message && ref; ref = ref->next) | |
27 | + if (!mutt_strcasecmp (ref->data, p->message->env->message_id)) | |
28 | + { | |
29 | + done = 1; | |
30 | + break; | |
31 | + } | |
32 | + | |
33 | + if (done) | |
34 | + { | |
35 | + /* clearing the References: header from obsolete Message-Id(s) */ | |
36 | + mutt_free_list (&ref->next); | |
37 | + | |
38 | + cur->message->refs_changed = cur->message->changed = 1; | |
39 | + } | |
40 | + } | |
41 | +} | |
42 | + | |
43 | +void mutt_break_thread (HEADER *hdr) | |
44 | +{ | |
45 | + mutt_free_list (&hdr->env->in_reply_to); | |
46 | + mutt_free_list (&hdr->env->references); | |
47 | + hdr->irt_changed = hdr->refs_changed = hdr->changed = 1; | |
48 | + | |
49 | + clean_references (hdr->thread, hdr->thread->child); | |
50 | +} | |
51 | + | |
52 | +static int link_threads (HEADER *parent, HEADER *child, CONTEXT *ctx) | |
53 | +{ | |
54 | + if (child == parent) | |
55 | + return 0; | |
56 | + | |
57 | + mutt_break_thread (child); | |
58 | + | |
59 | + child->env->in_reply_to = mutt_new_list (); | |
60 | + child->env->in_reply_to->data = safe_strdup (parent->env->message_id); | |
61 | + | |
62 | + mutt_set_flag (ctx, child, M_TAG, 0); | |
63 | + | |
64 | + child->irt_changed = child->changed = 1; | |
65 | + return 1; | |
66 | +} | |
67 | + | |
68 | +int mutt_link_threads (HEADER *cur, HEADER *last, CONTEXT *ctx) | |
69 | +{ | |
70 | + int i, changed = 0; | |
71 | + | |
72 | + if (!last) | |
73 | + { | |
74 | + for (i = 0; i < ctx->vcount; i++) | |
75 | + if (ctx->hdrs[Context->v2r[i]]->tagged) | |
76 | + changed |= link_threads (cur, ctx->hdrs[Context->v2r[i]], ctx); | |
77 | + } | |
78 | + else | |
79 | + changed = link_threads (cur, last, ctx); | |
80 | + | |
81 | + return changed; | |
82 | +} | |
83 | diff -pruN2 mutt-1.3.27.orig/OPS mutt-1.3.27/OPS | |
84 | --- mutt-1.3.27.orig/OPS Tue Sep 11 12:50:50 2001 | |
85 | +++ mutt-1.3.27/OPS Wed Jan 23 22:26:56 2002 | |
86 | @@ -95,4 +95,5 @@ OP_LIST_REPLY "reply to specified mailin | |
87 | OP_MACRO "execute a macro" | |
88 | OP_MAIL "compose a new mail message" | |
89 | +OP_MAIN_BREAK_THREAD "break the thread in two" | |
90 | OP_MAIN_CHANGE_FOLDER "open a different folder" | |
91 | OP_MAIN_CHANGE_FOLDER_READONLY "open a different folder in read only mode" | |
92 | @@ -104,4 +105,5 @@ OP_MAIN_FIRST_MESSAGE "move to the first | |
93 | OP_MAIN_LAST_MESSAGE "move to the last message" | |
94 | OP_MAIN_LIMIT "show only messages matching a pattern" | |
95 | +OP_MAIN_LINK_THREADS "link tagged message to the current one" | |
96 | OP_MAIN_NEXT_NEW "jump to the next new message" | |
97 | OP_MAIN_NEXT_SUBTHREAD "jump to the next subthread" | |
98 | diff -pruN2 mutt-1.3.27.orig/copy.c mutt-1.3.27/copy.c | |
99 | --- mutt-1.3.27.orig/copy.c Mon Dec 3 11:17:57 2001 | |
100 | +++ mutt-1.3.27/copy.c Wed Jan 23 22:26:56 2002 | |
101 | @@ -92,4 +92,10 @@ mutt_copy_hdr (FILE *in, FILE *out, long | |
102 | ascii_strncasecmp ("Lines:", buf, 6) == 0)) | |
103 | continue; | |
104 | + if ((flags & CH_UPDATE_REFS) && | |
105 | + ascii_strncasecmp ("References:", buf, 11) == 0) | |
106 | + continue; | |
107 | + if ((flags & CH_UPDATE_IRT) && | |
108 | + ascii_strncasecmp ("In-Reply-To:", buf, 12) == 0) | |
109 | + continue; | |
110 | ignore = 0; | |
111 | } | |
112 | @@ -168,4 +174,10 @@ mutt_copy_hdr (FILE *in, FILE *out, long | |
113 | ascii_strncasecmp ("mime-version:", buf, 13) == 0)) | |
114 | continue; | |
115 | + if ((flags & CH_UPDATE_REFS) && | |
116 | + ascii_strncasecmp ("References:", buf, 11) == 0) | |
117 | + continue; | |
118 | + if ((flags & CH_UPDATE_IRT) && | |
119 | + ascii_strncasecmp ("In-Reply-To:", buf, 12) == 0) | |
120 | + continue; | |
121 | ||
122 | /* Find x -- the array entry where this header is to be saved */ | |
123 | @@ -277,4 +289,6 @@ mutt_copy_hdr (FILE *in, FILE *out, long | |
124 | CH_XMIT ignore Lines: and Content-Length: | |
125 | CH_WEED do header weeding | |
126 | + CH_UPDATE_IRT update the In-Reply-To: header | |
127 | + CH_UPDATE_REFS update the References: header | |
128 | ||
129 | prefix | |
130 | @@ -286,4 +300,7 @@ mutt_copy_header (FILE *in, HEADER *h, F | |
131 | { | |
132 | char buffer[SHORT_STRING]; | |
133 | + | |
134 | + flags |= (h->irt_changed ? CH_UPDATE_IRT : 0) | |
135 | + | (h->refs_changed ? CH_UPDATE_REFS : 0); | |
136 | ||
137 | if (mutt_copy_hdr (in, out, h->offset, h->content->offset, flags, prefix) == -1) | |
138 | @@ -310,4 +327,47 @@ mutt_copy_header (FILE *in, HEADER *h, F | |
139 | if ((flags & CH_NOSTATUS) == 0) | |
140 | { | |
141 | + if (h->irt_changed && h->env->in_reply_to) | |
142 | + { | |
143 | + LIST *listp = h->env->in_reply_to; | |
144 | + | |
145 | + if (fputs ("In-Reply-To: ", out) == EOF) | |
146 | + return (-1); | |
147 | + | |
148 | + for (; listp; listp = listp->next) | |
149 | + if ((fputs (listp->data, out) == EOF) || (fputc (' ', out) == EOF)) | |
150 | + return (-1); | |
151 | + | |
152 | + if (fputc ('\n', out) == EOF) | |
153 | + return (-1); | |
154 | + } | |
155 | + | |
156 | + if (h->refs_changed && h->env->references) | |
157 | + { | |
158 | + LIST *listp = h->env->references, *refs = NULL, *t; | |
159 | + | |
160 | + if (fputs ("References: ", out) == EOF) | |
161 | + return (-1); | |
162 | + | |
163 | + /* Mutt stores references in reverse order, thus we create | |
164 | + * a reordered refs list that we can put in the headers */ | |
165 | + for (; listp; listp = listp->next, refs = t) | |
166 | + { | |
167 | + t = (LIST *)safe_malloc (sizeof (LIST)); | |
168 | + t->data = listp->data; | |
169 | + t->next = refs; | |
170 | + } | |
171 | + | |
172 | + for (; refs; refs = refs->next) | |
173 | + if ((fputs (refs->data, out) == EOF) || (fputc (' ', out) == EOF)) | |
174 | + return (-1); | |
175 | + | |
176 | + /* clearing refs from memory */ | |
177 | + for (t = refs; refs; refs = t->next, t = refs) | |
178 | + safe_free ((void **)&refs); | |
179 | + | |
180 | + if (fputc ('\n', out) == EOF) | |
181 | + return (-1); | |
182 | + } | |
183 | + | |
184 | if (h->old || h->read) | |
185 | { | |
186 | diff -pruN2 mutt-1.3.27.orig/curs_main.c mutt-1.3.27/curs_main.c | |
187 | --- mutt-1.3.27.orig/curs_main.c Wed Jan 16 21:43:58 2002 | |
188 | +++ mutt-1.3.27/curs_main.c Wed Jan 23 22:26:56 2002 | |
189 | @@ -878,4 +878,9 @@ int mutt_index_menu (void) | |
190 | { | |
191 | mutt_set_flag (Context, CURHDR, M_TAG, !CURHDR->tagged); | |
192 | + | |
193 | + Context->last_tag = CURHDR->tagged ? CURHDR : | |
194 | + ((Context->last_tag == CURHDR && !CURHDR->tagged) | |
195 | + ? NULL : Context->last_tag); | |
196 | + | |
197 | menu->redraw = REDRAW_STATUS; | |
198 | if (option (OPTRESOLVE) && menu->current < Context->vcount - 1) | |
199 | @@ -1098,4 +1103,83 @@ int mutt_index_menu (void) | |
200 | done = 1; | |
201 | } | |
202 | + break; | |
203 | + | |
204 | + case OP_MAIN_BREAK_THREAD: | |
205 | + | |
206 | + CHECK_MSGCOUNT; | |
207 | + CHECK_VISIBLE; | |
208 | + CHECK_READONLY; | |
209 | + | |
210 | + if ((Sort & SORT_MASK) != SORT_THREADS) | |
211 | + { | |
212 | + mutt_error _("Threading is not enabled."); | |
213 | + break; | |
214 | + } | |
215 | + if (Context->magic != M_MBOX && Context->magic != M_MMDF) | |
216 | + { | |
217 | + mutt_error _("break-thread: only for mbox and mmdf mailbox formats"); | |
218 | + break; | |
219 | + } | |
220 | + | |
221 | + { | |
222 | + HEADER *oldcur = CURHDR; | |
223 | + | |
224 | + mutt_break_thread (CURHDR); | |
225 | + mutt_sort_headers (Context, 1); | |
226 | + menu->current = oldcur->virtual; | |
227 | + } | |
228 | + | |
229 | + Context->changed = 1; | |
230 | + mutt_message _("Thread broken"); | |
231 | + | |
232 | + if (menu->menu == MENU_PAGER) | |
233 | + { | |
234 | + op = OP_DISPLAY_MESSAGE; | |
235 | + continue; | |
236 | + } | |
237 | + else | |
238 | + menu->redraw |= REDRAW_INDEX; | |
239 | + | |
240 | + break; | |
241 | + | |
242 | + case OP_MAIN_LINK_THREADS: | |
243 | + | |
244 | + CHECK_MSGCOUNT; | |
245 | + CHECK_VISIBLE; | |
246 | + CHECK_READONLY; | |
247 | + | |
248 | + if ((Sort & SORT_MASK) != SORT_THREADS) | |
249 | + mutt_error _("Threading is not enabled."); | |
250 | + else if (Context->magic != M_MBOX && Context->magic != M_MMDF) | |
251 | + mutt_error _("link-threads: only for mbox and mmdf mailbox formats"); | |
252 | + else if (!CURHDR->env->message_id) | |
253 | + mutt_error _("No Message-ID: header available to link thread"); | |
254 | + else if (!tag && (!Context->last_tag || !Context->last_tag->tagged)) | |
255 | + mutt_error _("First, please tag a message to be linked here"); | |
256 | + else | |
257 | + { | |
258 | + HEADER *oldcur = CURHDR; | |
259 | + | |
260 | + if (mutt_link_threads (CURHDR, tag ? NULL : Context->last_tag, | |
261 | + Context)) | |
262 | + { | |
263 | + mutt_sort_headers (Context, 1); | |
264 | + menu->current = oldcur->virtual; | |
265 | + | |
266 | + Context->changed = 1; | |
267 | + mutt_message _("Threads linked"); | |
268 | + } | |
269 | + else | |
270 | + mutt_error _("No thread linked"); | |
271 | + } | |
272 | + | |
273 | + if (menu->menu == MENU_PAGER) | |
274 | + { | |
275 | + op = OP_DISPLAY_MESSAGE; | |
276 | + continue; | |
277 | + } | |
278 | + else | |
279 | + menu->redraw |= REDRAW_STATUS | REDRAW_INDEX; | |
280 | + | |
281 | break; | |
282 | ||
283 | diff -pruN2 mutt-1.3.27.orig/doc/manual.sgml.head mutt-1.3.27/doc/manual.sgml.head | |
284 | --- mutt-1.3.27.orig/doc/manual.sgml.head Sat Jan 12 12:35:43 2002 | |
285 | +++ mutt-1.3.27/doc/manual.sgml.head Wed Jan 23 22:26:56 2002 | |
286 | @@ -2116,6 +2116,38 @@ with large volume mailing lists easier b | |
287 | uninteresting threads and quickly find topics of value. | |
288 | ||
289 | +<sect1>Editing threads | |
290 | +<p> | |
291 | +Mutt has the ability to dynamically restructure threads that are broken | |
292 | +either by misconfigured software or bad behaviour from some | |
293 | +correspondents. This allows to clean your mailboxes (<em/mbox/ and <em/mmdf/ | |
294 | +formats) from these annoyances which make it hard to follow a discussion. | |
295 | + | |
296 | +<sect2>Linking threads | |
297 | +<p> | |
298 | + | |
299 | +Some mailers tend to "forget" to correctly set the "In-Reply-To:" and | |
300 | +"References:" headers when replying to a message. This results in broken | |
301 | +discussions because Mutt has not enough information to guess the correct | |
302 | +threading. | |
303 | +You can fix this by tagging the reply to a mail, then go onto this mail | |
304 | +and use the ``link-threads'' function (bound to & by default). The | |
305 | +reply will then be connected to its "parent" message. | |
306 | + | |
307 | +You can also connect multiple childs at once, tagging them and using the | |
308 | +tag-prefix command (';') or the auto_tag option. | |
309 | + | |
310 | +<sect2>Breaking threads | |
311 | +<p> | |
312 | + | |
313 | +On mailing lists, some people are in the bad habit of starting a new | |
314 | +discussion by hitting "reply" to any message from the list and changing | |
315 | +the subject to a totally unrelated one. | |
316 | +You can fix such threads by using the ``break-thread'' function (bound | |
317 | +by default to #), which will turn the subthread starting from the | |
318 | +current message into a whole different thread. | |
319 | + | |
320 | <sect1>Delivery Status Notification (DSN) Support | |
321 | <p> | |
322 | + | |
323 | RFC1894 defines a set of MIME content types for relaying information | |
324 | about the status of electronic mail messages. These can be thought of as | |
325 | diff -pruN2 mutt-1.3.27.orig/functions.h mutt-1.3.27/functions.h | |
326 | --- mutt-1.3.27.orig/functions.h Tue Sep 11 12:51:39 2001 | |
327 | +++ mutt-1.3.27/functions.h Wed Jan 23 22:26:56 2002 | |
328 | @@ -67,4 +67,5 @@ struct binding_t OpMain[] = { | |
329 | { "create-alias", OP_CREATE_ALIAS, "a" }, | |
330 | { "bounce-message", OP_BOUNCE_MESSAGE, "b" }, | |
331 | + { "break-thread", OP_MAIN_BREAK_THREAD, "#" }, | |
332 | { "change-folder", OP_MAIN_CHANGE_FOLDER, "c" }, | |
333 | { "change-folder-readonly", OP_MAIN_CHANGE_FOLDER_READONLY, "\033c" }, | |
334 | @@ -93,4 +94,5 @@ struct binding_t OpMain[] = { | |
335 | { "previous-undeleted", OP_MAIN_PREV_UNDELETED, "k" }, | |
336 | { "limit", OP_MAIN_LIMIT, "l" }, | |
337 | + { "link-threads", OP_MAIN_LINK_THREADS, "&" }, | |
338 | { "list-reply", OP_LIST_REPLY, "L" }, | |
339 | { "mail", OP_MAIL, "m" }, | |
340 | @@ -151,4 +153,5 @@ struct binding_t OpMain[] = { | |
341 | ||
342 | struct binding_t OpPager[] = { | |
343 | + { "break-thread", OP_MAIN_BREAK_THREAD, "#" }, | |
344 | { "create-alias", OP_CREATE_ALIAS, "a" }, | |
345 | { "bounce-message", OP_BOUNCE_MESSAGE, "b" }, | |
346 | @@ -173,4 +176,5 @@ struct binding_t OpPager[] = { | |
347 | { "previous-undeleted",OP_MAIN_PREV_UNDELETED, "k" }, | |
348 | { "previous-entry", OP_PREV_ENTRY, "K" }, | |
349 | + { "link-threads", OP_MAIN_LINK_THREADS, "&" }, | |
350 | { "list-reply", OP_LIST_REPLY, "L" }, | |
351 | { "redraw-screen", OP_REDRAW, "\014" }, | |
352 | diff -pruN2 mutt-1.3.27.orig/mutt.h mutt-1.3.27/mutt.h | |
353 | --- mutt-1.3.27.orig/mutt.h Tue Jan 15 22:00:32 2002 | |
354 | +++ mutt-1.3.27/mutt.h Wed Jan 23 22:26:56 2002 | |
355 | @@ -82,4 +82,6 @@ | |
356 | #define CH_WEED_DELIVERED (1<<13) /* weed eventual Delivered-To headers */ | |
357 | #define CH_FORCE_FROM (1<<14) /* give CH_FROM precedence over CH_WEED? */ | |
358 | +#define CH_UPDATE_IRT (1<<15) /* update In-Reply-To: */ | |
359 | +#define CH_UPDATE_REFS (1<<16) /* update References: */ | |
360 | ||
361 | /* flags for mutt_enter_string() */ | |
362 | @@ -629,4 +631,6 @@ typedef struct header | |
363 | unsigned int threaded : 1; /* used for threading */ | |
364 | unsigned int display_subject : 1; /* used for threading */ | |
365 | + unsigned int irt_changed : 1; /* In-Reply-To changed to link/break threads */ | |
366 | + unsigned int refs_changed : 1; /* References changed to break thread */ | |
367 | unsigned int recip_valid : 1; /* is_recipient is valid */ | |
368 | unsigned int active : 1; /* message is not to be removed */ | |
369 | @@ -728,4 +732,5 @@ typedef struct | |
370 | pattern_t *limit_pattern; /* compiled limit pattern */ | |
371 | HEADER **hdrs; | |
372 | + HEADER *last_tag; /* last tagged msg. used to link threads */ | |
373 | THREAD *tree; /* top of thread tree */ | |
374 | HASH *id_hash; /* hash table by msg id */ | |
375 | diff -pruN2 mutt-1.3.27.orig/mx.c mutt-1.3.27/mx.c | |
376 | --- mutt-1.3.27.orig/mx.c Tue Jan 15 22:09:53 2002 | |
377 | +++ mutt-1.3.27/mx.c Wed Jan 23 22:26:56 2002 | |
378 | @@ -1163,4 +1163,6 @@ int mx_sync_mailbox (CONTEXT *ctx, int * | |
379 | } | |
380 | } | |
381 | + else if (ctx->last_tag && ctx->last_tag->deleted) | |
382 | + ctx->last_tag = NULL; /* reset last tagged msg now useless */ | |
383 | } | |
384 | ||
385 | diff -pruN2 mutt-1.3.27.orig/pager.c mutt-1.3.27/pager.c | |
386 | --- mutt-1.3.27.orig/pager.c Sun Jan 13 09:49:39 2002 | |
387 | +++ mutt-1.3.27/pager.c Wed Jan 23 22:26:56 2002 | |
388 | @@ -2436,4 +2436,9 @@ mutt_pager (const char *banner, const ch | |
389 | CHECK_MODE(IsHeader (extra)); | |
390 | mutt_set_flag (Context, extra->hdr, M_TAG, !extra->hdr->tagged); | |
391 | + | |
392 | + Context->last_tag = extra->hdr->tagged ? extra->hdr : | |
393 | + ((Context->last_tag == extra->hdr && !extra->hdr->tagged) | |
394 | + ? NULL : Context->last_tag); | |
395 | + | |
396 | redraw = REDRAW_STATUS | REDRAW_INDEX; | |
397 | if (option (OPTRESOLVE)) | |
398 | diff -pruN2 mutt-1.3.27.orig/protos.h mutt-1.3.27/protos.h | |
399 | --- mutt-1.3.27.orig/protos.h Wed Jan 16 21:43:58 2002 | |
400 | +++ mutt-1.3.27/protos.h Wed Jan 23 22:26:56 2002 | |
401 | @@ -154,4 +154,5 @@ void mutt_block_signals_system (void); | |
402 | void mutt_body_handler (BODY *, STATE *); | |
403 | void mutt_bounce_message (FILE *fp, HEADER *, ADDRESS *); | |
404 | +void mutt_break_thread (HEADER *); | |
405 | void mutt_buffy (char *, size_t); | |
406 | void mutt_canonical_charset (char *, size_t, const char *); | |
407 | @@ -289,4 +290,5 @@ int mutt_is_subscribed_list (ADDRESS *); | |
408 | int mutt_is_text_type (int, char *); | |
409 | int mutt_is_valid_mailbox (const char *); | |
410 | +int mutt_link_threads (HEADER *, HEADER *, CONTEXT *); | |
411 | int mutt_messages_in_thread (CONTEXT *, HEADER *, int); | |
412 | int mutt_multi_choice (char *prompt, char *letters); | |
413 | diff -pruN mutt-1.3.27.orig/PATCHES mutt-1.3.27/PATCHES | |
414 | --- mutt-1.3.27.orig/PATCHES Mon Nov 26 20:16:52 2001 | |
415 | +++ mutt-1.3.27/PATCHES Thu Dec 6 16:27:55 2001 | |
416 | @@ -1,0 +1 @@ | |
417 | +patch-1.3.27.cd.edit_threads.9.1 |