Re: [PATCH] emacs: wash: make word-wrap bound message width
[notmuch-archives.git] / f1 / c23c93edf2324f3f70f42ef632316d1c115c83
1 Return-Path: <BATV+95f6b7a4aaa7c13215e8+2420+infradead.org+hohndel@bombadil.srs.infradead.org>\r
2 X-Original-To: notmuch@notmuchmail.org\r
3 Delivered-To: notmuch@notmuchmail.org\r
4 Received: from localhost (localhost [127.0.0.1])\r
5         by olra.theworths.org (Postfix) with ESMTP id 96D014196F0\r
6         for <notmuch@notmuchmail.org>; Fri,  9 Apr 2010 15:53:08 -0700 (PDT)\r
7 X-Virus-Scanned: Debian amavisd-new at olra.theworths.org\r
8 X-Spam-Flag: NO\r
9 X-Spam-Score: -4.2\r
10 X-Spam-Level: \r
11 X-Spam-Status: No, score=-4.2 tagged_above=-999 required=5\r
12         tests=[BAYES_00=-1.9, RCVD_IN_DNSWL_MED=-2.3] autolearn=ham\r
13 Received: from olra.theworths.org ([127.0.0.1])\r
14         by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024)\r
15         with ESMTP id BzMjr7rpVKm1 for <notmuch@notmuchmail.org>;\r
16         Fri,  9 Apr 2010 15:53:06 -0700 (PDT)\r
17 Received: from bombadil.infradead.org (bombadil.infradead.org [18.85.46.34])\r
18         by olra.theworths.org (Postfix) with ESMTP id BF91F431FC1\r
19         for <notmuch@notmuchmail.org>; Fri,  9 Apr 2010 15:53:06 -0700 (PDT)\r
20 Received: from localhost ([::1] helo=localhost.localdomain)\r
21         by bombadil.infradead.org with esmtp (Exim 4.69 #1 (Red Hat Linux))\r
22         id 1O0N4T-0000k7-Pb\r
23         for notmuch@notmuchmail.org; Fri, 09 Apr 2010 22:53:06 +0000\r
24 Received: by localhost.localdomain (Postfix, from userid 500)\r
25         id 0584EC00E2; Fri,  9 Apr 2010 15:53:05 -0700 (PDT)\r
26 From: Dirk Hohndel <hohndel@infradead.org>\r
27 To: <notmuch@notmuchmail.org>\r
28 Subject: [PATCH] Next attempt to get guessing of From addresses correct in\r
29         replies\r
30 Date: Fri, 09 Apr 2010 15:53:04 -0700\r
31 Message-ID: <m37hogdyr3.fsf@x200.gr8dns.org>\r
32 MIME-Version: 1.0\r
33 Content-Type: text/plain; charset=us-ascii\r
34 X-SRS-Rewrite: SMTP reverse-path rewritten from <hohndel@infradead.org> by\r
35         bombadil.infradead.org See http://www.infradead.org/rpr.html\r
36 X-BeenThere: notmuch@notmuchmail.org\r
37 X-Mailman-Version: 2.1.13\r
38 Precedence: list\r
39 List-Id: "Use and development of the notmuch mail system."\r
40         <notmuch.notmuchmail.org>\r
41 List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,\r
42         <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>\r
43 List-Archive: <http://notmuchmail.org/pipermail/notmuch>\r
44 List-Post: <mailto:notmuch@notmuchmail.org>\r
45 List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>\r
46 List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,\r
47         <mailto:notmuch-request@notmuchmail.org?subject=subscribe>\r
48 X-List-Received-Date: Fri, 09 Apr 2010 22:53:08 -0000\r
49 \r
50 \r
51 We now go through the following searches in order:\r
52 0) one of the users addresses was in a To: or Cc: header\r
53 1) check for an Envelope-to: header that matches one of the addresses\r
54 2) check for an X-Original-To: header that matches one of the addresses\r
55 3) check for a (for <email@add.res>) clause in Received: headers\r
56 4) check for the domain part of known email addresses in the\r
57     'by' part of Received headers\r
58 Finally, if none of these work, we give up and use the primary email address.\r
59 \r
60 Signed-off-by: Dirk Hohndel <hohndel@infradead.org>\r
61 ---\r
62  lib/message-file.c |   35 ++++++++++-----\r
63  notmuch-reply.c    |  128 +++++++++++++++++++++++++++++++++++++++------------\r
64  2 files changed, 122 insertions(+), 41 deletions(-)\r
65 \r
66 diff --git a/lib/message-file.c b/lib/message-file.c\r
67 index 0c152a3..0114761 100644\r
68 --- a/lib/message-file.c\r
69 +++ b/lib/message-file.c\r
70 @@ -209,15 +209,21 @@ copy_header_unfolding (header_value_closure_t *value,\r
71  \r
72  /* As a special-case, a value of NULL for header_desired will force\r
73   * the entire header to be parsed if it is not parsed already. This is\r
74 - * used by the _notmuch_message_file_get_headers_end function. */\r
75 + * used by the _notmuch_message_file_get_headers_end function. \r
76 + * \r
77 + * WARNING - if the caller is asking for a header that could occur\r
78 + * multiple times than they MUST first call this function with a \r
79 + * a value of NULL for header_desired to ensure that all of the\r
80 + * headers are parsed and concatenated before a match is returned\r
81 + */\r
82  const char *\r
83  notmuch_message_file_get_header (notmuch_message_file_t *message,\r
84                                  const char *header_desired)\r
85  {\r
86      int contains;\r
87 -    char *header, *decoded_value;\r
88 +    char *header, *decoded_value, *header_sofar, *combined_header;\r
89      const char *s, *colon;\r
90 -    int match;\r
91 +    int match,newhdr,hdrsofar;\r
92      static int initialized = 0;\r
93  \r
94      if (! initialized) {\r
95 @@ -312,20 +318,27 @@ notmuch_message_file_get_header (notmuch_message_file_t *message,\r
96  \r
97         NEXT_HEADER_LINE (&message->value);\r
98  \r
99 -       if (header_desired == 0)\r
100 +       if (header_desired == NULL)\r
101             match = 0;\r
102         else\r
103             match = (strcasecmp (header, header_desired) == 0);\r
104  \r
105         decoded_value = g_mime_utils_header_decode_text (message->value.str);\r
106 -       if (g_hash_table_lookup (message->headers, header) == NULL) {\r
107 -           /* Only insert if we don't have a value for this header, yet.\r
108 -            * This way we always return the FIRST instance of any header\r
109 -            * we search for\r
110 -            * FIXME: we should be returning ALL instances of a header\r
111 -            *        or at least provide a way to iterate over them\r
112 -            */\r
113 +       header_sofar = (char *)g_hash_table_lookup (message->headers, header);\r
114 +       if (header_sofar == NULL) {\r
115 +           /* Only insert if we don't have a value for this header, yet. */\r
116             g_hash_table_insert (message->headers, header, decoded_value);\r
117 +       } else {\r
118 +           /* not sure this makes sense for all headers that can occur\r
119 +            * multiple times, but for now just concatenate headers\r
120 +            */\r
121 +           newhdr = strlen(decoded_value);\r
122 +           hdrsofar = strlen(header_sofar);\r
123 +           combined_header = xmalloc(hdrsofar + newhdr + 2);\r
124 +           strncpy(combined_header,header_sofar,hdrsofar);\r
125 +           *(combined_header+hdrsofar) = ' ';\r
126 +           strncpy(combined_header+hdrsofar+1,decoded_value,newhdr+1);\r
127 +           g_hash_table_insert (message->headers, header, combined_header);\r
128         }\r
129         if (match)\r
130             return decoded_value;\r
131 diff --git a/notmuch-reply.c b/notmuch-reply.c\r
132 index 39377e1..91a2094 100644\r
133 --- a/notmuch-reply.c\r
134 +++ b/notmuch-reply.c\r
135 @@ -285,33 +285,100 @@ add_recipients_from_message (GMimeMessage *reply,\r
136  static const char *\r
137  guess_from_received_header (notmuch_config_t *config, notmuch_message_t *message)\r
138  {\r
139 -    const char *received,*primary;\r
140 -    char **other;\r
141 -    char *by,*mta,*ptr,*token;\r
142 +    const char *received,*primary,*by;\r
143 +    char **other,*tohdr;\r
144 +    char *mta,*ptr,*token;\r
145      char *domain=NULL;\r
146      char *tld=NULL;\r
147      const char *delim=". \t";\r
148      size_t i,other_len;\r
149  \r
150 +    const char *to_headers[] = {"Envelope-to", "X-Original-To"};\r
151 +\r
152 +    primary = notmuch_config_get_user_primary_email (config);\r
153 +    other = notmuch_config_get_user_other_email (config, &other_len);\r
154 +\r
155 +    /* sadly, there is no standard way to find out to which email\r
156 +     * address a mail was delivered - what is in the headers depends\r
157 +     * on the MTAs used along the way. So we are trying a number of\r
158 +     * heuristics which hopefully will answer this question.\r
159 +\r
160 +     * We only got here if none of the users email addresses are in\r
161 +     * the To: or Cc: header. From here we try the following in order:\r
162 +     * 1) check for an Envelope-to: header\r
163 +     * 2) check for an X-Original-To: header\r
164 +     * 3) check for a (for <email@add.res>) clause in Received: headers\r
165 +     * 4) check for the domain part of known email addresses in the \r
166 +     *    'by' part of Received headers\r
167 +     * If none of these work, we give up and return NULL\r
168 +     */\r
169 +    for (i = 0; i < sizeof(to_headers)/sizeof(*to_headers); i++) {\r
170 +       tohdr = xstrdup(notmuch_message_get_header (message, to_headers[i]));\r
171 +       if (tohdr && *tohdr) {\r
172 +           /* tohdr is potentialy a list of email addresses, so here we\r
173 +            * check if one of the email addresses is a substring of tohdr\r
174 +            */\r
175 +           if (strcasestr(tohdr, primary)) {\r
176 +               free(tohdr);\r
177 +               return primary;\r
178 +           }\r
179 +           for (i = 0; i < other_len; i++)\r
180 +               if (strcasestr (tohdr, other[i])) {\r
181 +                   free(tohdr);\r
182 +                   return other[i];\r
183 +               }\r
184 +           free(tohdr);\r
185 +       }\r
186 +    }\r
187 +                  \r
188 +    /* We get the concatenated Received: headers and search from the\r
189 +     * front (last Received: header added) and try to extract from\r
190 +     * them indications to which email address this message was\r
191 +     * delivered.  One tricky side-effect of our lazy parsing of\r
192 +     * headers is that we need to make sure that all of the Received:\r
193 +     * headers have been read at this point and concatenated. So we\r
194 +     * first have to call notmuch_message_get_header with the magical\r
195 +     * header NULL (see comments in that function)\r
196 +     */\r
197 +    notmuch_message_get_header (message, NULL);\r
198      received = notmuch_message_get_header (message, "received");\r
199 -    by = strstr (received, " by ");\r
200 -    if (by && *(by+4)) {\r
201 -       /* sadly, the format of Received: headers is a bit inconsistent,\r
202 -        * depending on the MTA used. So we try to extract just the MTA\r
203 -        * here by removing leading whitespace and assuming that the MTA\r
204 -        * name ends at the next whitespace\r
205 -        * we test for *(by+4) to be non-'\0' to make sure there's something\r
206 -        * there at all - and then assume that the first whitespace delimited\r
207 -        * token that follows is the last receiving server\r
208 +    /* First we look for a " for <email@add.res>" in the received\r
209 +     * header\r
210 +     */\r
211 +    ptr = strstr (received, " for ");\r
212 +    if (ptr) {\r
213 +       /* the text following is potentialy a list of email addresses,\r
214 +        * so again we check if one of the email addresses is a\r
215 +        * substring of ptr\r
216          */\r
217 -       mta = strdup (by+4);\r
218 -       if (mta == NULL)\r
219 -           return NULL;\r
220 +       if (strcasestr(ptr, primary)) {\r
221 +           return primary;\r
222 +       }\r
223 +       for (i = 0; i < other_len; i++)\r
224 +           if (strcasestr (ptr, other[i])) {\r
225 +               return other[i];\r
226 +           }\r
227 +    }\r
228 +    /* Finally, we parse all the " by MTA ..." headers to guess the\r
229 +     * email address that this was originally delivered to.\r
230 +     * We extract just the MTA here by removing leading whitespace and\r
231 +     * assuming that the MTA name ends at the next whitespace.\r
232 +     * We test for *(by+4) to be non-'\0' to make sure there's\r
233 +     * something there at all - and then assume that the first\r
234 +     * whitespace delimited token that follows is the receiving\r
235 +     * system in this step of the receive chain\r
236 +     */\r
237 +    by = received;\r
238 +    while((by = strstr (by, " by ")) != NULL) {\r
239 +       by += 4;\r
240 +       if (*by == '\0')\r
241 +           break;\r
242 +       mta = xstrdup (by);\r
243         token = strtok(mta," \t");\r
244         if (token == NULL)\r
245 -           return NULL;\r
246 +           break;\r
247         /* Now extract the last two components of the MTA host name\r
248 -        * as domain and tld\r
249 +        * as domain and tld.\r
250          */\r
251         while ((ptr = strsep (&token, delim)) != NULL) {\r
252             if (*ptr == '\0')\r
253 @@ -321,23 +388,24 @@ guess_from_received_header (notmuch_config_t *config, notmuch_message_t *message\r
254         }\r
255  \r
256         if (domain) {\r
257 -           /* recombine domain and tld and look for it among the configured\r
258 -            * email addresses\r
259 +           /* Recombine domain and tld and look for it among the configured\r
260 +            * email addresses.\r
261 +            * This time we have a known domain name and nothing else - so\r
262 +            * the test is the other way around: we check if this is a \r
263 +            * substring of one of the email addresses.\r
264              */\r
265             *(tld-1) = '.';\r
266 -           primary = notmuch_config_get_user_primary_email (config);\r
267 -           if (strcasestr (primary, domain)) {\r
268 -               free (mta);\r
269 -               return primary;\r
270 +           \r
271 +           if (strcasestr(primary, domain)) {\r
272 +               free(mta);\r
273 +           return primary;\r
274 +       }\r
275 +       for (i = 0; i < other_len; i++)\r
276 +           if (strcasestr (other[i],domain)) {\r
277 +               free(mta);\r
278 +               return other[i];\r
279             }\r
280 -           other = notmuch_config_get_user_other_email (config, &other_len);\r
281 -           for (i = 0; i < other_len; i++)\r
282 -               if (strcasestr (other[i], domain)) {\r
283 -                   free (mta);\r
284 -                   return other[i];\r
285 -               }\r
286         }\r
287 -\r
288         free (mta);\r
289      }\r
290  \r
291 -- \r
292 1.6.6.1\r
293 \r
294 \r
295 -- \r
296 Dirk Hohndel\r
297 Intel Open Source Technology Center\r