+ 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;
+}
+
+/* Does the given string contain an address configured as one of the
+ * user's "primary" or "other" addresses. If so, return the matching
+ * address, NULL otherwise. */
+static const char *
+user_address_in_string (const char *str, notmuch_config_t *config)
+{
+ return address_match (str, config, USER_ADDRESS_IN_STRING);
+}
+
+/* Do any of the addresses configured as one of the user's "primary"
+ * or "other" addresses contain the given string. If so, return the
+ * matching address, NULL otherwise. */
+static const char *
+string_in_user_address (const char *str, notmuch_config_t *config)
+{
+ return address_match (str, config, STRING_IN_USER_ADDRESS);
+}
+
+/* Is the given address configured as one of the user's "primary" or
+ * "other" addresses. */
+static notmuch_bool_t
+address_is_users (const char *address, notmuch_config_t *config)
+{
+ return address_match (address, config, STRING_IS_USER_ADDRESS) != NULL;
+}
+
+/* Scan addresses in 'list'.
+ *
+ * If 'message' is non-NULL, then for each address in 'list' that is
+ * not configured as one of the user's addresses in 'config', add that
+ * address to 'message' as an address of 'type'.
+ *
+ * If 'user_from' is non-NULL and *user_from is NULL, *user_from will
+ * be set to the first address encountered in 'list' that is the
+ * user's address.
+ *
+ * Return the number of addresses added to 'message'. (If 'message' is
+ * NULL, the function returns 0 by definition.)
+ */
+static unsigned int
+scan_address_list (InternetAddressList *list,
+ notmuch_config_t *config,
+ GMimeMessage *message,
+ GMimeRecipientType type,
+ const char **user_from)
+{
+ InternetAddress *address;
+ int i;
+ unsigned int n = 0;
+
+ if (list == NULL)
+ return 0;
+
+ for (i = 0; i < internet_address_list_length (list); i++) {
+ address = internet_address_list_get_address (list, i);
+ if (INTERNET_ADDRESS_IS_GROUP (address)) {
+ InternetAddressGroup *group;
+ InternetAddressList *group_list;
+
+ group = INTERNET_ADDRESS_GROUP (address);
+ group_list = internet_address_group_get_members (group);
+ n += scan_address_list (group_list, config, message, type, user_from);
+ } else {
+ InternetAddressMailbox *mailbox;
+ const char *name;
+ const char *addr;
+
+ mailbox = INTERNET_ADDRESS_MAILBOX (address);
+
+ name = internet_address_get_name (address);
+ addr = internet_address_mailbox_get_addr (mailbox);
+
+ if (address_is_users (addr, config)) {
+ if (user_from && *user_from == NULL)
+ *user_from = addr;
+ } else if (message) {
+ g_mime_message_add_recipient (message, type, name, addr);
+ n++;
+ }
+ }
+ }
+
+ return n;
+}
+
+/* Does the address in the Reply-To header of 'message' already appear
+ * in either the 'To' or 'Cc' header of the message?
+ */
+static notmuch_bool_t
+reply_to_header_is_redundant (GMimeMessage *message,
+ InternetAddressList *reply_to_list)
+{
+ const char *addr, *reply_to;
+ InternetAddress *address;
+ InternetAddressMailbox *mailbox;
+ InternetAddressList *recipients;
+ notmuch_bool_t ret = FALSE;
+ int i;
+
+ if (reply_to_list == NULL ||
+ internet_address_list_length (reply_to_list) != 1)
+ return 0;
+
+ address = internet_address_list_get_address (reply_to_list, 0);
+ if (INTERNET_ADDRESS_IS_GROUP (address))
+ return 0;
+
+ mailbox = INTERNET_ADDRESS_MAILBOX (address);
+ reply_to = internet_address_mailbox_get_addr (mailbox);
+
+ recipients = g_mime_message_get_all_recipients (message);
+
+ for (i = 0; i < internet_address_list_length (recipients); i++) {
+ address = internet_address_list_get_address (recipients, i);
+ if (INTERNET_ADDRESS_IS_GROUP (address))
+ continue;
+
+ mailbox = INTERNET_ADDRESS_MAILBOX (address);
+ addr = internet_address_mailbox_get_addr (mailbox);
+ if (strcmp (addr, reply_to) == 0) {
+ ret = TRUE;
+ break;
+ }
+ }
+
+ g_object_unref (G_OBJECT (recipients));
+
+ return ret;
+}
+
+static InternetAddressList *get_sender(GMimeMessage *message)
+{
+ const char *reply_to;
+
+ reply_to = g_mime_message_get_reply_to (message);
+ if (reply_to && *reply_to) {
+ InternetAddressList *reply_to_list;
+
+ /*
+ * Some mailing lists munge the Reply-To header despite it
+ * being A Bad Thing, see
+ * http://marc.merlins.org/netrants/reply-to-harmful.html
+ *
+ * The munging is easy to detect, because it results in a
+ * redundant reply-to header, (with an address that already
+ * exists in either To or Cc). So in this case, we ignore the
+ * Reply-To field and use the From header. This ensures the
+ * original sender will get the reply even if not subscribed
+ * to the list. Note that the address in the Reply-To header
+ * will always appear in the reply if reply_all is true.
+ */
+ reply_to_list = internet_address_list_parse_string (reply_to);
+ if (! reply_to_header_is_redundant (message, reply_to_list))
+ return reply_to_list;
+
+ g_object_unref (G_OBJECT (reply_to_list));
+ }
+
+ return internet_address_list_parse_string (
+ g_mime_message_get_sender (message));
+}
+
+static InternetAddressList *get_to(GMimeMessage *message)
+{
+ return g_mime_message_get_recipients (message, GMIME_RECIPIENT_TYPE_TO);
+}
+
+static InternetAddressList *get_cc(GMimeMessage *message)
+{
+ return g_mime_message_get_recipients (message, GMIME_RECIPIENT_TYPE_CC);
+}