/* Handle the case of "notmuch" being invoked with no command * argument. For now we just call notmuch_setup_command, but we plan * to be more clever about this in the future. */ static int notmuch (void *ctx) { notmuch_config_t *config; notmuch_bool_t is_new; char *db_path; struct stat st; config = notmuch_config_open (ctx, NULL, &is_new); /* If the user has never configured notmuch, then run * notmuch_setup_command which will give a nice welcome message, * and interactively guide the user through the configuration. */ if (is_new) { notmuch_config_close (config); return notmuch_setup_command (ctx, 0, NULL); } /* Notmuch is already configured, but is there a database? */ db_path = talloc_asprintf (ctx, "%s/%s", notmuch_config_get_database_path (config), ".notmuch"); if (stat (db_path, &st)) { notmuch_config_close (config); if (errno != ENOENT) { fprintf (stderr, "Error looking for notmuch database at %s: %s\n", db_path, strerror (errno)); return 1; } printf ("Notmuch is configured, but there's not yet a database at\n\n\t%s\n\n", db_path); printf ("You probably want to run \"notmuch new\" now to create that database.\n\n" "Note that the first run of \"notmuch new\" can take a very long time\n" "and that the resulting database will use roughly the same amount of\n" "storage space as the email being indexed.\n\n"); return 0; } printf ("Notmuch is configured and appears to have a database. Excellent!\n\n" "At this point you can start exploring the functionality of notmuch by\n" "using commands such as:\n\n" "\tnotmuch search tag:inbox\n\n" "\tnotmuch search to:\"%s\"\n\n" "\tnotmuch search from:\"%s\"\n\n" "\tnotmuch search subject:\"my favorite things\"\n\n" "See \"notmuch help search\" for more details.\n\n" "You can also use \"notmuch show\" with any of the thread IDs resulting\n" "from a search. Finally, you may want to explore using a more sophisticated\n" "interface to notmuch such as the emacs interface implemented in notmuch.el\n" "or any other interface described at http://notmuchmail.org\n\n" "And don't forget to run \"notmuch new\" whenever new mail arrives.\n\n" "Have fun, and may your inbox never have much mail.\n\n", notmuch_config_get_user_name (config), notmuch_config_get_user_primary_email (config)); notmuch_config_close (config); return 0; }
static GMimeMessage * create_reply_message(void *ctx, notmuch_config_t *config, notmuch_message_t *message, notmuch_bool_t reply_all) { const char *subject, *from_addr = NULL; const char *in_reply_to, *orig_references, *references; /* The 1 means we want headers in a "pretty" order. */ GMimeMessage *reply = g_mime_message_new (1); if (reply == NULL) { fprintf (stderr, "Out of memory\n"); return NULL; } subject = notmuch_message_get_header (message, "subject"); if (subject) { if (strncasecmp (subject, "Re:", 3)) subject = talloc_asprintf (ctx, "Re: %s", subject); g_mime_message_set_subject (reply, subject); } from_addr = add_recipients_from_message (reply, config, message, reply_all); if (from_addr == NULL) from_addr = guess_from_received_header (config, message); if (from_addr == NULL) from_addr = notmuch_config_get_user_primary_email (config); from_addr = talloc_asprintf (ctx, "%s <%s>", notmuch_config_get_user_name (config), from_addr); g_mime_object_set_header (GMIME_OBJECT (reply), "From", from_addr); in_reply_to = talloc_asprintf (ctx, "<%s>", notmuch_message_get_message_id (message)); g_mime_object_set_header (GMIME_OBJECT (reply), "In-Reply-To", in_reply_to); orig_references = notmuch_message_get_header (message, "references"); if (!orig_references) /* Treat errors like missing References headers. */ orig_references = ""; references = talloc_asprintf (ctx, "%s%s%s", *orig_references ? orig_references : "", *orig_references ? " " : "", in_reply_to); g_mime_object_set_header (GMIME_OBJECT (reply), "References", references); return reply; }
static int notmuch_config_command_get (void *ctx, char *item) { notmuch_config_t *config; config = notmuch_config_open (ctx, NULL, NULL); if (config == NULL) return 1; if (strcmp(item, "database.path") == 0) { printf ("%s\n", notmuch_config_get_database_path (config)); } else if (strcmp(item, "user.name") == 0) { printf ("%s\n", notmuch_config_get_user_name (config)); } else if (strcmp(item, "user.primary_email") == 0) { printf ("%s\n", notmuch_config_get_user_primary_email (config)); } else if (strcmp(item, "user.other_email") == 0) { const char **other_email; size_t i, length; other_email = notmuch_config_get_user_other_email (config, &length); for (i = 0; i < length; i++) printf ("%s\n", other_email[i]); } else if (strcmp(item, "new.tags") == 0) { const char **tags; size_t i, length; tags = notmuch_config_get_new_tags (config, &length); for (i = 0; i < length; i++) printf ("%s\n", tags[i]); } else { char **value; size_t i, length; char *group, *key; if (_item_split (item, &group, &key)) return 1; value = g_key_file_get_string_list (config->key_file, group, key, &length, NULL); if (value == NULL) { fprintf (stderr, "Unknown configuration item: %s.%s\n", group, key); return 1; } for (i = 0; i < length; i++) printf ("%s\n", value[i]); g_strfreev (value); } notmuch_config_close (config); return 0; }
/* Is the given address configured as one of the user's "personal" or * "other" addresses. */ static int address_is_users (const char *address, notmuch_config_t *config) { const char *primary; const char **other; size_t i, other_len; primary = notmuch_config_get_user_primary_email (config); if (strcasecmp (primary, address) == 0) return 1; other = notmuch_config_get_user_other_email (config, &other_len); for (i = 0; i < other_len; i++) if (strcasecmp (other[i], address) == 0) return 1; return 0; }
/* Match given string against user's configured "primary" and "other" * addresses according to mode. */ static const char * address_match (const char *str, notmuch_config_t *config, address_match_t mode) { const char *primary; const char **other; size_t i, other_len; if (!str || *str == '\0') return NULL; primary = notmuch_config_get_user_primary_email (config); if (match_address (str, primary, mode)) return primary; other = notmuch_config_get_user_other_email (config, &other_len); for (i = 0; i < other_len; i++) { if (match_address (str, other[i], mode)) return other[i]; } return NULL; }
static GMimeMessage * create_reply_message(void *ctx, notmuch_config_t *config, notmuch_message_t *message, notmuch_bool_t reply_all) { const char *subject, *from_addr = NULL; const char *in_reply_to, *orig_references, *references; /* The 1 means we want headers in a "pretty" order. */ GMimeMessage *reply = g_mime_message_new (1); if (reply == NULL) { fprintf (stderr, "Out of memory\n"); return NULL; } subject = notmuch_message_get_header (message, "subject"); if (subject) { if (strncasecmp (subject, "Re:", 3)) subject = talloc_asprintf (ctx, "Re: %s", subject); g_mime_message_set_subject (reply, subject); } from_addr = add_recipients_from_message (reply, config, message, reply_all); /* * Sadly, there is no standard way to find out to which email * address a mail was delivered - what is in the headers depends * on the MTAs used along the way. * * If none of the user's email addresses are in the To: or Cc: * headers, we try a number of heuristics which hopefully will * answer this question. * * First, check for Envelope-To:, X-Original-To:, and * Delivered-To: headers. */ if (from_addr == NULL) from_addr = get_from_in_to_headers (config, message); /* * Check for a (for <*****@*****.**>) clause in Received: headers, * and the domain part of known email addresses in the 'by' part * of Received: headers */ if (from_addr == NULL) from_addr = guess_from_in_received_headers (config, message); /* Default to user's primary address. */ if (from_addr == NULL) from_addr = notmuch_config_get_user_primary_email (config); from_addr = talloc_asprintf (ctx, "%s <%s>", notmuch_config_get_user_name (config), from_addr); g_mime_object_set_header (GMIME_OBJECT (reply), "From", from_addr); in_reply_to = talloc_asprintf (ctx, "<%s>", notmuch_message_get_message_id (message)); g_mime_object_set_header (GMIME_OBJECT (reply), "In-Reply-To", in_reply_to); orig_references = notmuch_message_get_header (message, "references"); if (!orig_references) /* Treat errors like missing References headers. */ orig_references = ""; references = talloc_asprintf (ctx, "%s%s%s", *orig_references ? orig_references : "", *orig_references ? " " : "", in_reply_to); g_mime_object_set_header (GMIME_OBJECT (reply), "References", references); return reply; }
int notmuch_setup_command (notmuch_config_t *config, unused (int argc), unused (char *argv[])) { char *response = NULL; size_t response_size = 0; const char **old_other_emails; size_t old_other_emails_len; GPtrArray *other_emails; unsigned int i; const char **new_tags; size_t new_tags_len; const char **search_exclude_tags; size_t search_exclude_tags_len; #define prompt(format, ...) \ do { \ printf (format, ##__VA_ARGS__); \ fflush (stdout); \ if (getline (&response, &response_size, stdin) < 0) { \ printf ("Exiting.\n"); \ exit (EXIT_FAILURE); \ } \ chomp_newline (response); \ } while (0) if (notmuch_config_is_new (config)) welcome_message_pre_setup (); prompt ("Your full name [%s]: ", notmuch_config_get_user_name (config)); if (strlen (response)) notmuch_config_set_user_name (config, response); prompt ("Your primary email address [%s]: ", notmuch_config_get_user_primary_email (config)); if (strlen (response)) notmuch_config_set_user_primary_email (config, response); other_emails = g_ptr_array_new (); old_other_emails = notmuch_config_get_user_other_email (config, &old_other_emails_len); for (i = 0; i < old_other_emails_len; i++) { prompt ("Additional email address [%s]: ", old_other_emails[i]); if (strlen (response)) g_ptr_array_add (other_emails, talloc_strdup (config, response)); else g_ptr_array_add (other_emails, talloc_strdup (config, old_other_emails[i])); } do { prompt ("Additional email address [Press 'Enter' if none]: "); if (strlen (response)) g_ptr_array_add (other_emails, talloc_strdup (config, response)); } while (strlen (response)); if (other_emails->len) notmuch_config_set_user_other_email (config, (const char **) other_emails->pdata, other_emails->len); g_ptr_array_free (other_emails, TRUE); prompt ("Top-level directory of your email archive [%s]: ", notmuch_config_get_database_path (config)); if (strlen (response)) { const char *absolute_path; absolute_path = make_path_absolute (config, response); notmuch_config_set_database_path (config, absolute_path); } new_tags = notmuch_config_get_new_tags (config, &new_tags_len); printf ("Tags to apply to all new messages (separated by spaces) ["); print_tag_list (new_tags, new_tags_len); prompt ("]: "); if (strlen (response)) { GPtrArray *tags = parse_tag_list (config, response); notmuch_config_set_new_tags (config, (const char **) tags->pdata, tags->len); g_ptr_array_free (tags, TRUE); } search_exclude_tags = notmuch_config_get_search_exclude_tags (config, &search_exclude_tags_len); printf ("Tags to exclude when searching messages (separated by spaces) ["); print_tag_list (search_exclude_tags, search_exclude_tags_len); prompt ("]: "); if (strlen (response)) { GPtrArray *tags = parse_tag_list (config, response); notmuch_config_set_search_exclude_tags (config, (const char **) tags->pdata, tags->len); g_ptr_array_free (tags, TRUE); } if (notmuch_config_save (config)) return EXIT_FAILURE; if (notmuch_config_is_new (config)) welcome_message_post_setup (); return EXIT_SUCCESS; }
int notmuch_setup_command (unused (void *ctx), unused (int argc), unused (char *argv[])) { char *response = NULL; size_t response_size = 0; notmuch_config_t *config; const char **old_other_emails; size_t old_other_emails_len; GPtrArray *other_emails; unsigned int i; int is_new; const char **new_tags; size_t new_tags_len; #define prompt(format, ...) \ do { \ printf (format, ##__VA_ARGS__); \ fflush (stdout); \ if (getline (&response, &response_size, stdin) < 0) { \ printf ("Exiting.\n"); \ exit (1); \ } \ chomp_newline (response); \ } while (0) config = notmuch_config_open (ctx, NULL, &is_new); if (is_new) welcome_message_pre_setup (); prompt ("Your full name [%s]: ", notmuch_config_get_user_name (config)); if (strlen (response)) notmuch_config_set_user_name (config, response); prompt ("Your primary email address [%s]: ", notmuch_config_get_user_primary_email (config)); if (strlen (response)) notmuch_config_set_user_primary_email (config, response); other_emails = g_ptr_array_new (); old_other_emails = notmuch_config_get_user_other_email (config, &old_other_emails_len); for (i = 0; i < old_other_emails_len; i++) { prompt ("Additional email address [%s]: ", old_other_emails[i]); if (strlen (response)) g_ptr_array_add (other_emails, talloc_strdup (ctx, response)); else g_ptr_array_add (other_emails, talloc_strdup (ctx, old_other_emails[i])); } do { prompt ("Additional email address [Press 'Enter' if none]: "); if (strlen (response)) g_ptr_array_add (other_emails, talloc_strdup (ctx, response)); } while (strlen (response)); if (other_emails->len) notmuch_config_set_user_other_email (config, (const char **) other_emails->pdata, other_emails->len); g_ptr_array_free (other_emails, TRUE); prompt ("Top-level directory of your email archive [%s]: ", notmuch_config_get_database_path (config)); if (strlen (response)) { const char *absolute_path; absolute_path = make_path_absolute (ctx, response); notmuch_config_set_database_path (config, absolute_path); } new_tags = notmuch_config_get_new_tags (config, &new_tags_len); printf ("Tags to apply to all new messages (separated by spaces) ["); for (i = 0; i < new_tags_len; i++) { if (i != 0) printf (" "); printf ("%s", new_tags[i]); } prompt ("]: "); if (strlen (response)) { GPtrArray *tags = g_ptr_array_new (); char *tag = response; char *space; while (tag && *tag) { space = strchr (tag, ' '); if (space) g_ptr_array_add (tags, talloc_strndup (ctx, tag, space - tag)); else g_ptr_array_add (tags, talloc_strdup (ctx, tag)); tag = space; while (tag && *tag == ' ') tag++; } notmuch_config_set_new_tags (config, (const char **) tags->pdata, tags->len); g_ptr_array_free (tags, TRUE); } if (! notmuch_config_save (config)) { if (is_new) welcome_message_post_setup (); return 0; } else { return 1; } }
/* Open the named notmuch configuration file. If the filename is NULL, * the value of the environment variable $NOTMUCH_CONFIG will be used. * If $NOTMUCH_CONFIG is unset, the default configuration file * ($HOME/.notmuch-config) will be used. * * If any error occurs, (out of memory, or a permission-denied error, * etc.), this function will print a message to stderr and return * NULL. * * FILE NOT FOUND: When the specified configuration file (whether from * 'filename' or the $NOTMUCH_CONFIG environment variable) does not * exist, the behavior of this function depends on the 'is_new_ret' * variable. * * If is_new_ret is NULL, then a "file not found" message will be * printed to stderr and NULL will be returned. * If is_new_ret is non-NULL then a default configuration will be * returned and *is_new_ret will be set to 1 on return so that * the caller can recognize this case. * * These default configuration settings are determined as * follows: * * database_path: $HOME/mail * * user_name: From /etc/passwd * * user_primary_mail: $EMAIL variable if set, otherwise * constructed from the username and * hostname of the current machine. * * user_other_email: Not set. * * The default configuration also contains comments to guide the * user in editing the file directly. */ notmuch_config_t * notmuch_config_open (void *ctx, const char *filename, notmuch_bool_t *is_new_ret) { GError *error = NULL; int is_new = 0; size_t tmp; char *notmuch_config_env = NULL; int file_had_database_group; int file_had_new_group; int file_had_user_group; int file_had_maildir_group; int file_had_search_group; if (is_new_ret) *is_new_ret = 0; notmuch_config_t *config = talloc (ctx, notmuch_config_t); if (config == NULL) { fprintf (stderr, "Out of memory.\n"); return NULL; } talloc_set_destructor (config, notmuch_config_destructor); if (filename) { config->filename = talloc_strdup (config, filename); } else if ((notmuch_config_env = getenv ("NOTMUCH_CONFIG"))) { config->filename = talloc_strdup (config, notmuch_config_env); } else { config->filename = talloc_asprintf (config, "%s/.notmuch-config", getenv ("HOME")); } config->key_file = g_key_file_new (); config->database_path = NULL; config->user_name = NULL; config->user_primary_email = NULL; config->user_other_email = NULL; config->user_other_email_length = 0; config->new_tags = NULL; config->new_tags_length = 0; config->new_ignore = NULL; config->new_ignore_length = 0; config->maildir_synchronize_flags = TRUE; config->search_exclude_tags = NULL; config->search_exclude_tags_length = 0; if (! g_key_file_load_from_file (config->key_file, config->filename, G_KEY_FILE_KEEP_COMMENTS, &error)) { /* If the caller passed a non-NULL value for is_new_ret, then * the caller is prepared for a default configuration file in * the case of FILE NOT FOUND. Otherwise, any read failure is * an error. */ if (is_new_ret && error->domain == G_FILE_ERROR && error->code == G_FILE_ERROR_NOENT) { g_error_free (error); is_new = 1; } else { fprintf (stderr, "Error reading configuration file %s: %s\n", config->filename, error->message); talloc_free (config); g_error_free (error); return NULL; } } /* Whenever we know of configuration sections that don't appear in * the configuration file, we add some comments to help the user * understand what can be done. * * It would be convenient to just add those comments now, but * apparently g_key_file will clear any comments when keys are * added later that create the groups. So we have to check for the * groups now, but add the comments only after setting all of our * values. */ file_had_database_group = g_key_file_has_group (config->key_file, "database"); file_had_new_group = g_key_file_has_group (config->key_file, "new"); file_had_user_group = g_key_file_has_group (config->key_file, "user"); file_had_maildir_group = g_key_file_has_group (config->key_file, "maildir"); file_had_search_group = g_key_file_has_group (config->key_file, "search"); if (notmuch_config_get_database_path (config) == NULL) { char *path = talloc_asprintf (config, "%s/mail", getenv ("HOME")); notmuch_config_set_database_path (config, path); talloc_free (path); } if (notmuch_config_get_user_name (config) == NULL) { char *name = get_name_from_passwd_file (config); notmuch_config_set_user_name (config, name); talloc_free (name); } if (notmuch_config_get_user_primary_email (config) == NULL) { char *email = getenv ("EMAIL"); if (email) { notmuch_config_set_user_primary_email (config, email); } else { char hostname[256]; struct hostent *hostent; const char *domainname; char *username = get_username_from_passwd_file (config); gethostname (hostname, 256); hostname[255] = '\0'; hostent = gethostbyname (hostname); if (hostent && (domainname = strchr (hostent->h_name, '.'))) domainname += 1; else domainname = "(none)"; email = talloc_asprintf (config, "%s@%s.%s", username, hostname, domainname); notmuch_config_set_user_primary_email (config, email); talloc_free (username); talloc_free (email); } } if (notmuch_config_get_new_tags (config, &tmp) == NULL) { const char *tags[] = { "unread", "inbox" }; notmuch_config_set_new_tags (config, tags, 2); } if (notmuch_config_get_new_ignore (config, &tmp) == NULL) { notmuch_config_set_new_ignore (config, NULL, 0); } if (notmuch_config_get_search_exclude_tags (config, &tmp) == NULL) { if (is_new) { const char *tags[] = { "deleted", "spam" }; notmuch_config_set_search_exclude_tags (config, tags, 2); } else { notmuch_config_set_search_exclude_tags (config, NULL, 0); } } error = NULL; config->maildir_synchronize_flags = g_key_file_get_boolean (config->key_file, "maildir", "synchronize_flags", &error); if (error) { notmuch_config_set_maildir_synchronize_flags (config, TRUE); g_error_free (error); } /* Whenever we know of configuration sections that don't appear in * the configuration file, we add some comments to help the user * understand what can be done. */ if (is_new) { g_key_file_set_comment (config->key_file, NULL, NULL, toplevel_config_comment, NULL); } if (! file_had_database_group) { g_key_file_set_comment (config->key_file, "database", NULL, database_config_comment, NULL); } if (! file_had_new_group) { g_key_file_set_comment (config->key_file, "new", NULL, new_config_comment, NULL); } if (! file_had_user_group) { g_key_file_set_comment (config->key_file, "user", NULL, user_config_comment, NULL); } if (! file_had_maildir_group) { g_key_file_set_comment (config->key_file, "maildir", NULL, maildir_config_comment, NULL); } if (! file_had_search_group) { g_key_file_set_comment (config->key_file, "search", NULL, search_config_comment, NULL); } if (is_new_ret) *is_new_ret = is_new; return config; }
static int notmuch_reply_format_default(void *ctx, notmuch_config_t *config, notmuch_query_t *query, notmuch_show_params_t *params) { GMimeMessage *reply; notmuch_messages_t *messages; notmuch_message_t *message; const char *subject, *from_addr = NULL; const char *in_reply_to, *orig_references, *references; const notmuch_show_format_t *format = &format_reply; for (messages = notmuch_query_search_messages (query); notmuch_messages_valid (messages); notmuch_messages_move_to_next (messages)) { message = notmuch_messages_get (messages); /* The 1 means we want headers in a "pretty" order. */ reply = g_mime_message_new (1); if (reply == NULL) { fprintf (stderr, "Out of memory\n"); return 1; } subject = notmuch_message_get_header (message, "subject"); if (subject) { if (strncasecmp (subject, "Re:", 3)) subject = talloc_asprintf (ctx, "Re: %s", subject); g_mime_message_set_subject (reply, subject); } from_addr = add_recipients_from_message (reply, config, message); if (from_addr == NULL) from_addr = guess_from_received_header (config, message); if (from_addr == NULL) from_addr = notmuch_config_get_user_primary_email (config); from_addr = talloc_asprintf (ctx, "%s <%s>", notmuch_config_get_user_name (config), from_addr); g_mime_object_set_header (GMIME_OBJECT (reply), "From", from_addr); in_reply_to = talloc_asprintf (ctx, "<%s>", notmuch_message_get_message_id (message)); g_mime_object_set_header (GMIME_OBJECT (reply), "In-Reply-To", in_reply_to); orig_references = notmuch_message_get_header (message, "references"); references = talloc_asprintf (ctx, "%s%s%s", orig_references ? orig_references : "", orig_references ? " " : "", in_reply_to); g_mime_object_set_header (GMIME_OBJECT (reply), "References", references); show_reply_headers (reply); g_object_unref (G_OBJECT (reply)); reply = NULL; printf ("On %s, %s wrote:\n", notmuch_message_get_header (message, "date"), notmuch_message_get_header (message, "from")); show_message_body (notmuch_message_get_filename (message), format, params); notmuch_message_destroy (message); } return 0; }
static const char * guess_from_received_header (notmuch_config_t *config, notmuch_message_t *message) { const char *received,*primary,*by; const char **other; char *tohdr; char *mta,*ptr,*token; char *domain=NULL; char *tld=NULL; const char *delim=". \t"; size_t i,j,other_len; const char *to_headers[] = {"Envelope-to", "X-Original-To"}; primary = notmuch_config_get_user_primary_email (config); other = notmuch_config_get_user_other_email (config, &other_len); /* sadly, there is no standard way to find out to which email * address a mail was delivered - what is in the headers depends * on the MTAs used along the way. So we are trying a number of * heuristics which hopefully will answer this question. * We only got here if none of the users email addresses are in * the To: or Cc: header. From here we try the following in order: * 1) check for an Envelope-to: header * 2) check for an X-Original-To: header * 3) check for a (for <*****@*****.**>) clause in Received: headers * 4) check for the domain part of known email addresses in the * 'by' part of Received headers * If none of these work, we give up and return NULL */ for (i = 0; i < sizeof(to_headers)/sizeof(*to_headers); i++) { tohdr = xstrdup(notmuch_message_get_header (message, to_headers[i])); if (tohdr && *tohdr) { /* tohdr is potentialy a list of email addresses, so here we * check if one of the email addresses is a substring of tohdr */ if (strcasestr(tohdr, primary)) { free(tohdr); return primary; } for (j = 0; j < other_len; j++) if (strcasestr (tohdr, other[j])) { free(tohdr); return other[j]; } free(tohdr); } } /* We get the concatenated Received: headers and search from the * front (last Received: header added) and try to extract from * them indications to which email address this message was * delivered. * The Received: header is special in our get_header function * and is always concatenated. */ received = notmuch_message_get_header (message, "received"); if (received == NULL) return NULL; /* First we look for a " for <*****@*****.**>" in the received * header */ ptr = strstr (received, " for "); if (ptr) { /* the text following is potentialy a list of email addresses, * so again we check if one of the email addresses is a * substring of ptr */ if (strcasestr(ptr, primary)) { return primary; } for (i = 0; i < other_len; i++) if (strcasestr (ptr, other[i])) { return other[i]; } } /* Finally, we parse all the " by MTA ..." headers to guess the * email address that this was originally delivered to. * We extract just the MTA here by removing leading whitespace and * assuming that the MTA name ends at the next whitespace. * We test for *(by+4) to be non-'\0' to make sure there's * something there at all - and then assume that the first * whitespace delimited token that follows is the receiving * system in this step of the receive chain */ by = received; while((by = strstr (by, " by ")) != NULL) { by += 4; if (*by == '\0') break; mta = xstrdup (by); token = strtok(mta," \t"); if (token == NULL) { free (mta); break; } /* Now extract the last two components of the MTA host name * as domain and tld. */ domain = tld = NULL; while ((ptr = strsep (&token, delim)) != NULL) { if (*ptr == '\0') continue; domain = tld; tld = ptr; } if (domain) { /* Recombine domain and tld and look for it among the configured * email addresses. * This time we have a known domain name and nothing else - so * the test is the other way around: we check if this is a * substring of one of the email addresses. */ *(tld-1) = '.'; if (strcasestr(primary, domain)) { free(mta); return primary; } for (i = 0; i < other_len; i++) if (strcasestr (other[i],domain)) { free(mta); return other[i]; } } free (mta); } return NULL; }