/* As a special-case, a value of NULL for header_desired will force * the entire header to be parsed if it is not parsed already. This is * used by the _notmuch_message_file_get_headers_end function. */ const char * notmuch_message_file_get_header (notmuch_message_file_t *message, const char *header_desired) { int contains; char *header, *decoded_value; const char *s, *colon; int match; static int initialized = 0; if (! initialized) { g_mime_init (0); initialized = 1; } message->parsing_started = 1; if (header_desired == NULL) contains = 0; else contains = g_hash_table_lookup_extended (message->headers, header_desired, NULL, (gpointer *) &decoded_value); if (contains && decoded_value) return decoded_value; if (message->parsing_finished) return ""; #define NEXT_HEADER_LINE(closure) \ while (1) { \ ssize_t bytes_read = getline (&message->line, \ &message->line_size, \ message->file); \ if (bytes_read == -1) { \ message->parsing_finished = 1; \ break; \ } \ if (*message->line == '\n') { \ message->parsing_finished = 1; \ break; \ } \ if (closure && \ (*message->line == ' ' || *message->line == '\t')) \ { \ copy_header_unfolding ((closure), message->line); \ } \ if (*message->line == ' ' || *message->line == '\t') \ message->header_size += strlen (message->line); \ else \ break; \ } if (message->line == NULL) NEXT_HEADER_LINE (NULL); while (1) { if (message->parsing_finished) break; colon = strchr (message->line, ':'); if (colon == NULL) { message->broken_headers++; /* A simple heuristic for giving up on things that just * don't look like mail messages. */ if (message->broken_headers >= 10 && message->good_headers < 5) { message->parsing_finished = 1; break; } NEXT_HEADER_LINE (NULL); continue; } message->header_size += strlen (message->line); message->good_headers++; header = xstrndup (message->line, colon - message->line); if (message->restrict_headers && ! g_hash_table_lookup_extended (message->headers, header, NULL, NULL)) { free (header); NEXT_HEADER_LINE (NULL); continue; } s = colon + 1; while (*s == ' ' || *s == '\t') s++; message->value.len = 0; copy_header_unfolding (&message->value, s); NEXT_HEADER_LINE (&message->value); if (header_desired == 0) match = 0; else match = (strcasecmp (header, header_desired) == 0); decoded_value = g_mime_utils_header_decode_text (message->value.str); g_hash_table_insert (message->headers, header, decoded_value); if (match) return decoded_value; } if (message->parsing_finished) { fclose (message->file); message->file = NULL; } if (message->line) free (message->line); message->line = NULL; if (message->value.size) { free (message->value.str); message->value.str = NULL; message->value.size = 0; message->value.len = 0; } /* We've parsed all headers and never found the one we're looking * for. It's probably just not there, but let's check that we * didn't make a mistake preventing us from seeing it. */ if (message->restrict_headers && header_desired && ! g_hash_table_lookup_extended (message->headers, header_desired, NULL, NULL)) { INTERNAL_ERROR ("Attempt to get header \"%s\" which was not\n" "included in call to notmuch_message_file_restrict_headers\n", header_desired); } return ""; }
/* As a special-case, a value of NULL for header_desired will force * the entire header to be parsed if it is not parsed already. This is * used by the _notmuch_message_file_get_headers_end function. * Another special case is the Received: header. For this header we * want to concatenate all instances of the header instead of just * hashing the first instance as we use this when analyzing the path * the mail has taken from sender to recipient. */ const char * notmuch_message_file_get_header (notmuch_message_file_t *message, const char *header_desired) { int contains; char *header, *decoded_value, *header_sofar, *combined_header; const char *s, *colon; int match, newhdr, hdrsofar, is_received; static int initialized = 0; is_received = (strcmp(header_desired,"received") == 0); if (! initialized) { g_mime_init (0); initialized = 1; } message->parsing_started = 1; if (header_desired == NULL) contains = 0; else contains = g_hash_table_lookup_extended (message->headers, header_desired, NULL, (gpointer *) &decoded_value); if (contains && decoded_value) return decoded_value; if (message->parsing_finished) return ""; #define NEXT_HEADER_LINE(closure) \ while (1) { \ ssize_t bytes_read = getline (&message->line, \ &message->line_size, \ message->file); \ if (bytes_read == -1) { \ message->parsing_finished = 1; \ break; \ } \ if (*message->line == '\n') { \ message->parsing_finished = 1; \ break; \ } \ if (closure && \ (*message->line == ' ' || *message->line == '\t')) \ { \ copy_header_unfolding ((closure), message->line); \ } \ if (*message->line == ' ' || *message->line == '\t') \ message->header_size += strlen (message->line); \ else \ break; \ } if (message->line == NULL) NEXT_HEADER_LINE (NULL); while (1) { if (message->parsing_finished) break; colon = strchr (message->line, ':'); if (colon == NULL) { message->broken_headers++; /* A simple heuristic for giving up on things that just * don't look like mail messages. */ if (message->broken_headers >= 10 && message->good_headers < 5) { message->parsing_finished = 1; break; } NEXT_HEADER_LINE (NULL); continue; } message->header_size += strlen (message->line); message->good_headers++; header = xstrndup (message->line, colon - message->line); if (message->restrict_headers && ! g_hash_table_lookup_extended (message->headers, header, NULL, NULL)) { free (header); NEXT_HEADER_LINE (NULL); continue; } s = colon + 1; while (*s == ' ' || *s == '\t') s++; message->value.len = 0; copy_header_unfolding (&message->value, s); NEXT_HEADER_LINE (&message->value); if (header_desired == NULL) match = 0; else match = (strcasecmp (header, header_desired) == 0); decoded_value = g_mime_utils_header_decode_text (message->value.str); header_sofar = (char *)g_hash_table_lookup (message->headers, header); /* we treat the Received: header special - we want to concat ALL of * the Received: headers we encounter. * for everything else we return the first instance of a header */ if (strcasecmp(header, "received") == 0) { if (header_sofar == NULL) { /* first Received: header we encountered; just add it */ g_hash_table_insert (message->headers, header, decoded_value); } else { /* we need to add the header to those we already collected */ newhdr = strlen(decoded_value); hdrsofar = strlen(header_sofar); combined_header = g_malloc(hdrsofar + newhdr + 2); strncpy(combined_header,header_sofar,hdrsofar); *(combined_header+hdrsofar) = ' '; strncpy(combined_header+hdrsofar+1,decoded_value,newhdr+1); g_free (decoded_value); g_hash_table_insert (message->headers, header, combined_header); } } else { if (header_sofar == NULL) { /* Only insert if we don't have a value for this header, yet. */ g_hash_table_insert (message->headers, header, decoded_value); } else { free (header); g_free (decoded_value); decoded_value = header_sofar; } } /* if we found a match we can bail - unless of course we are * collecting all the Received: headers */ if (match && !is_received) return decoded_value; } if (message->parsing_finished) { fclose (message->file); message->file = NULL; } if (message->line) free (message->line); message->line = NULL; if (message->value.size) { free (message->value.str); message->value.str = NULL; message->value.size = 0; message->value.len = 0; } /* For the Received: header we actually might end up here even * though we found the header (as we force continued parsing * in that case). So let's check if that's the header we were * looking for and return the value that we found (if any) */ if (is_received) return (char *)g_hash_table_lookup (message->headers, "received"); /* We've parsed all headers and never found the one we're looking * for. It's probably just not there, but let's check that we * didn't make a mistake preventing us from seeing it. */ if (message->restrict_headers && header_desired && ! g_hash_table_lookup_extended (message->headers, header_desired, NULL, NULL)) { INTERNAL_ERROR ("Attempt to get header \"%s\" which was not\n" "included in call to notmuch_message_file_restrict_headers\n", header_desired); } return ""; }