+ is(scalar(@$res), 1, "got a match for quoted text");
+ is($res->[0]->mid, 'quote@a',
+ "quoted result returned if nothing else") if scalar(@$res);
+});
+
+# circular references
+$ibx->with_umask(sub {
+ my $s = 'foo://'. ('Circle' x 15).'/foo';
+ my $doc_id = $rw->add_message(Email::MIME->create(
+ header => [ Subject => $s ],
+ header_str => [
+ Date => 'Sat, 02 Oct 2010 00:00:01 +0000',
+ 'Message-ID' => '<circle@a>',
+ 'References' => '<circle@a>',
+ 'In-Reply-To' => '<circle@a>',
+ From => 'Circle <circle@example.com>',
+ To => 'list@example.com',
+ ],
+ body => "LOOP!\n"));
+ ok($doc_id > 0, "doc_id defined with circular reference");
+ my $smsg = $rw->query('m:circle@a', {limit=>1})->[0];
+ is(defined($smsg), 1, 'found m:circl@a');
+ is($smsg->references, '', "no references created") if defined($smsg);
+ is($smsg->subject, $s, 'long subject not rewritten') if defined($smsg);
+});
+
+$ibx->with_umask(sub {
+ my $str = eval {
+ my $mbox = 't/utf8.mbox';
+ open(my $fh, '<', $mbox) or die "failed to open mbox: $mbox\n";
+ local $/;
+ <$fh>
+ };
+ $str =~ s/\AFrom [^\n]+\n//s;
+ my $mime = Email::MIME->new($str);
+ my $doc_id = $rw->add_message($mime);
+ ok($doc_id > 0, 'message indexed doc_id with UTF-8');
+ my $msg = $rw->query('m:testmessage@example.com', {limit => 1})->[0];
+ is(defined($msg), 1, 'found testmessage@example.com');
+ is($mime->header('Subject'), $msg->subject, 'UTF-8 subject preserved') if defined($msg);
+});
+
+{
+ my $msgs = $ro->query('d:19931002..20101002');
+ ok(scalar(@$msgs) > 0, 'got results within range');
+ $msgs = $ro->query('d:20101003..');
+ is(scalar(@$msgs), 0, 'nothing after 20101003');
+ $msgs = $ro->query('d:..19931001');
+ is(scalar(@$msgs), 0, 'nothing before 19931001');
+}
+
+# names and addresses
+{
+ my $mset = $ro->query('t:list@example.com', {mset => 1});
+ is($mset->size, 6, 'searched To: successfully');
+ foreach my $m ($mset->items) {
+ my $smsg = $ro->{over_ro}->get_art($m->get_docid);
+ like($smsg->to, qr/\blist\@example\.com\b/, 'to appears');
+ }
+
+ $mset = $ro->query('tc:list@example.com', {mset => 1});
+ is($mset->size, 6, 'searched To+Cc: successfully');
+ foreach my $m ($mset->items) {
+ my $smsg = $ro->{over_ro}->get_art($m->get_docid);
+ my $tocc = join("\n", $smsg->to, $smsg->cc);
+ like($tocc, qr/\blist\@example\.com\b/, 'tocc appears');
+ }
+
+ foreach my $pfx ('tcf:', 'c:') {
+ my $mset = $ro->query($pfx . 'foo@example.com', { mset => 1 });
+ is($mset->items, 1, "searched $pfx successfully for Cc:");
+ foreach my $m ($mset->items) {
+ my $smsg = $ro->{over_ro}->get_art($m->get_docid);
+ like($smsg->cc, qr/\bfoo\@example\.com\b/,
+ 'cc appears');
+ }
+ }
+
+ foreach my $pfx ('', 'tcf:', 'f:') {
+ my $res = $ro->query($pfx . 'Laggy');
+ is(scalar(@$res), 1,
+ "searched $pfx successfully for From:");
+ foreach my $smsg (@$res) {
+ like($smsg->from_name, qr/Laggy Sender/,
+ "From appears with $pfx");
+ }
+ }
+}
+
+{
+ $rw_commit->();
+ $ro->reopen;
+ my $res = $ro->query('b:hello');
+ is(scalar(@$res), 0, 'no match on body search only');
+ $res = $ro->query('bs:smith');
+ is(scalar(@$res), 0,
+ 'no match on body+subject search for From');
+
+ $res = $ro->query('q:theatre');
+ is(scalar(@$res), 1, 'only one quoted body');
+ like($res->[0]->from_name, qr/\AQuoter/,
+ 'got quoted body') if (scalar(@$res));
+
+ $res = $ro->query('nq:theatre');
+ is(scalar @$res, 1, 'only one non-quoted body');
+ like($res->[0]->from_name, qr/\ANon-Quoter/,
+ 'got non-quoted body') if (scalar(@$res));
+
+ foreach my $pfx (qw(b: bs:)) {
+ $res = $ro->query($pfx . 'theatre');
+ is(scalar @$res, 2, "searched both bodies for $pfx");
+ like($res->[0]->from_name, qr/\ANon-Quoter/,
+ "non-quoter first for $pfx") if scalar(@$res);
+ }
+}
+
+$ibx->with_umask(sub {
+ my $part1 = Email::MIME->create(
+ attributes => {
+ content_type => 'text/plain',
+ disposition => 'attachment',
+ charset => 'US-ASCII',
+ encoding => 'quoted-printable',
+ filename => 'attached_fart.txt',
+ },
+ body_str => 'inside the attachment',
+ );
+ my $part2 = Email::MIME->create(
+ attributes => {
+ content_type => 'text/plain',
+ disposition => 'attachment',
+ charset => 'US-ASCII',
+ encoding => 'quoted-printable',
+ filename => 'part_deux.txt',
+ },
+ body_str => 'inside another',
+ );
+ my $amsg = Email::MIME->create(
+ header_str => [
+ Subject => 'see attachment',
+ 'Message-ID' => '<file@attached>',
+ From => 'John Smith <js@example.com>',
+ To => 'list@example.com',
+ ],
+ parts => [ $part1, $part2 ],
+ );
+ ok($rw->add_message($amsg), 'added attachment');
+ $rw_commit->();
+ $ro->reopen;
+ my $n = $ro->query('n:attached_fart.txt');
+ is(scalar @$n, 1, 'got result for n:');
+ my $res = $ro->query('part_deux.txt');
+ is(scalar @$res, 1, 'got result without n:');
+ is($n->[0]->mid, $res->[0]->mid,
+ 'same result with and without') if scalar(@$res);
+ my $txt = $ro->query('"inside another"');
+ is(scalar @$txt, 1, 'found inside another');
+ is($txt->[0]->mid, $res->[0]->mid,
+ 'search inside text attachments works') if scalar(@$txt);
+
+ my $art;
+ if (scalar(@$n) >= 1) {
+ my $mid = $n->[0]->mid;
+ my ($id, $prev);
+ $art = $ro->{over_ro}->next_by_mid($mid, \$id, \$prev);
+ ok($art, 'article exists in OVER DB');
+ }
+ $rw->unindex_blob($amsg);
+ $rw->commit_txn_lazy;
+ SKIP: {
+ skip('$art not defined', 1) unless defined $art;
+ is($ro->{over_ro}->get_art($art->{num}), undef,
+ 'gone from OVER DB');
+ };
+});
+
+my $all_mask = 07777;
+my $dir_mask = 02770;
+
+# FreeBSD and apparently OpenBSD does not allow non-root users to set S_ISGID,
+# so git doesn't set it, either (see DIR_HAS_BSD_GROUP_SEMANTICS in git.git)
+if ($^O =~ /(?:free|open)bsd/i) {
+ $all_mask = 0777;
+ $dir_mask = 0770;