static void test_message_address(void) { static const char *input[] = { "user@domain", NULL, "<user@domain>", "user@domain", "foo bar <user@domain>", NULL, "\"foo bar\" <user@domain>", "foo bar <user@domain>", "<@route:user@domain>", NULL, "<@route@route2:user@domain>", "<@route,@route2:user@domain>", "hello <@route ,@route2:user@domain>", "hello <@route,@route2:user@domain>", "user (hello)", NULL, "hello <user>", NULL, "@domain", NULL }; static struct message_address group_prefix = { NULL, NULL, NULL, "group", NULL, FALSE }; static struct message_address group_suffix = { NULL, NULL, NULL, NULL, NULL, FALSE }; static struct message_address output[] = { { NULL, NULL, NULL, "user", "domain", FALSE }, { NULL, NULL, NULL, "user", "domain", FALSE }, { NULL, "foo bar", NULL, "user", "domain", FALSE }, { NULL, "foo bar", NULL, "user", "domain", FALSE }, { NULL, NULL, "@route", "user", "domain", FALSE }, { NULL, NULL, "@route,@route2", "user", "domain", FALSE }, { NULL, "hello", "@route,@route2", "user", "domain", FALSE }, { NULL, "hello", NULL, "user", "", TRUE }, { NULL, "hello", NULL, "user", "", TRUE }, { NULL, NULL, NULL, "", "domain", TRUE } }; struct message_address *addr; string_t *str, *group; const char *wanted_string; unsigned int i; i_assert(N_ELEMENTS(input) == N_ELEMENTS(output)*2); test_begin("message address parsing"); str = t_str_new(128); group = t_str_new(256); str_append(group, "group: "); for (i = 0; i < N_ELEMENTS(output); i++) { addr = message_address_parse(pool_datastack_create(), (const unsigned char *)input[i*2], strlen(input[i*2]), UINT_MAX, FALSE); test_assert(addr != NULL && addr->next == NULL && cmp_addr(addr, &output[i])); if (!output[i].invalid_syntax) { str_truncate(str, 0); message_address_write(str, addr); wanted_string = input[i*2+1] != NULL ? input[i*2+1] : input[i*2]; test_assert(strcmp(str_c(str), wanted_string) == 0); if (i != 0) { if ((i % 2) == 0) str_append(group, ","); else str_append(group, " , \n "); } str_append(group, input[i*2]); } } str_append_c(group, ';'); test_end(); test_begin("message address parsing with groups"); addr = message_address_parse(pool_datastack_create(), str_data(group), str_len(group), UINT_MAX, FALSE); test_assert(addr != NULL && cmp_addr(addr, &group_prefix)); addr = addr->next; for (i = 0; i < N_ELEMENTS(output) && addr != NULL; i++) { if (output[i].invalid_syntax) continue; test_assert(cmp_addr(addr, &output[i])); addr = addr->next; } test_assert(addr != NULL && addr->next == NULL && cmp_addr(addr, &group_suffix)); test_end(); test_begin("message address parsing with empty group"); str_truncate(group, 0); str_append(group, "group:;"); addr = message_address_parse(pool_datastack_create(), str_data(group), str_len(group), UINT_MAX, FALSE); test_assert(addr != NULL && cmp_addr(addr, &group_prefix)); addr = addr->next; test_assert(addr != NULL && addr->next == NULL && cmp_addr(addr, &group_suffix)); test_end(); }
static void search_header_arg(struct mail_search_arg *arg, struct search_header_context *ctx) { struct message_search_context *msg_search_ctx; struct message_block block; struct message_header_line hdr; int ret; /* first check that the field name matches to argument. */ switch (arg->type) { case SEARCH_BEFORE: case SEARCH_ON: case SEARCH_SINCE: if (arg->value.date_type != MAIL_SEARCH_DATE_TYPE_SENT) return; /* date is handled differently than others */ if (strcasecmp(ctx->hdr->name, "Date") == 0) { if (ctx->hdr->continues) { ctx->hdr->use_full_value = TRUE; return; } ret = search_sent(arg->type, arg->value.time, ctx->hdr->full_value, ctx->hdr->full_value_len); ARG_SET_RESULT(arg, ret); } return; case SEARCH_HEADER: case SEARCH_HEADER_ADDRESS: case SEARCH_HEADER_COMPRESS_LWSP: ctx->custom_header = TRUE; if (strcasecmp(ctx->hdr->name, arg->hdr_field_name) != 0) return; break; default: return; } if (arg->value.str[0] == '\0') { /* we're just testing existence of the field. always matches. */ ARG_SET_RESULT(arg, 1); return; } if (ctx->hdr->continues) { ctx->hdr->use_full_value = TRUE; return; } memset(&block, 0, sizeof(block)); /* We're searching only for values, so drop header name and middle parts. We use header searching so that MIME words will be decoded. */ hdr = *ctx->hdr; hdr.name = ""; hdr.name_len = 0; hdr.middle_len = 0; block.hdr = &hdr; msg_search_ctx = msg_search_arg_context(ctx->index_context, arg); if (msg_search_ctx == NULL) return; T_BEGIN { struct message_address *addr; string_t *str; switch (arg->type) { case SEARCH_HEADER: /* simple match */ break; case SEARCH_HEADER_ADDRESS: /* we have to match against normalized address */ addr = message_address_parse(pool_datastack_create(), ctx->hdr->full_value, ctx->hdr->full_value_len, (unsigned int)-1, TRUE); str = t_str_new(ctx->hdr->value_len); message_address_write(str, addr); hdr.value = hdr.full_value = str_data(str); hdr.value_len = hdr.full_value_len = str_len(str); break; case SEARCH_HEADER_COMPRESS_LWSP: /* convert LWSP to single spaces */ str = t_str_new(hdr.full_value_len); compress_lwsp(str, hdr.full_value, hdr.full_value_len); hdr.value = hdr.full_value = str_data(str); hdr.value_len = hdr.full_value_len = str_len(str); break; default: i_unreached(); } ret = message_search_more(msg_search_ctx, &block) ? 1 : 0; } T_END; /* there may be multiple headers. don't mark this failed yet. */ if (ret > 0) ARG_SET_RESULT(arg, 1); }