[Mew-dist 1401] IMAP Scanning

若宮賢二 wkenji at example.com
1997年 7月 31日 (木) 12:35:07 JST


若宮@富士通研です。

しばらく Mew の IMAP 対応について考えていたのですが、im-48 では imls 
や imcat にも IMAP 用のコードが入っていますね。Mew 側の対応もすぐ行な
われるのでしょうか?

とりあえず imls を少し試してみて、個人的に考えていたものと照らし合わせ
て以下のようなことをやってみました。もし使える所があったら使ってやって
下さい。

  - メッセージ指定/表示を UID ベースに。
      UID のほうがローカルフォルダの場合に似ているのと、
      将来オフライン処理などをサポートし易い?かと思いまして (漠然)。

  - メッセージのレンジ指定を可能に。
      ローカルな imls と同じです。

  - ヘッダ情報の fetch を一回に。
      FETCH コマンドを一通ごとに発行しないようにしました。

多くの部分を Imap.pm に入れてしまったのですが、他との整合性が悪いし、
あまり綺麗じゃないですね…。
#Perl は型グロブがいまいち理解できません;-)。

// Kenji    | mailto:wkenji at example.com (office)
// Wakamiya | mailto:wkenji at example.com (private)
-------------- next part --------------
--- imls.in-	Sat Jul 26 00:08:43 1997
+++ imls.in	Thu Jul 31 12:12:02 1997
@@ -103,7 +103,7 @@
     } elsif (/^\%(.*)$/) {
 	require IM::Imap && import IM::Imap;
 	require IM::GetPass && import IM::GetPass;
-	&imap_messages($1);
+	&imap_messages($1, @ARGV);
     } else {
 	im_die("doesn't support $opt_src\n");
     }
@@ -430,8 +430,8 @@
 ## IMAP
 ##
 
-sub imap_messages {
-	my($folder) = @_;
+sub imap_messages ($@) {
+	my ($folder, @ranges) = (shift, @_);
 	my ($host, $user, $auth, $rest);
 
 	if ($folder =~ /\:/) {
@@ -470,38 +470,26 @@
 		im_warn("$ErrMsg\n");
 #		im_warn("IMAP connection was not established.\n")
 #		  if (&debug("imap") || &verbose);
+		$scan_count = -1;
 		return -1;
 	}
-	my $start = 1;
-	my $end = &imap_select($folder, 1);
-	if ($msgs < 0) {
+	my $exists = &imap_select($folder, 1);
+	if ($exists < 0) {
 		&imap_close;
-		im_warn("can't access to $folder");
+		im_warn("can't select $folder\@$host.\n");
+		$scan_count = -1;
 		return -1;
-	} elsif ($msgs < $msg) {
-		&imap_close;
-		im_info("message $msg not found in \%$folder");
-		return -1;
-	}
-
-#	if ($ARGV[0] ne 'all') {
-#		# XXX
-#	}
-
-	foreach $i ($start..$end) {
-		(my $rc, local *Head) = &imap_head($i);
-		next if ($rc < 0);
-		$Head{'number'} = $i;
-		$Head{'folder'} = "\%$folder";
-		parse_header(\%Head);
-		if ($opt_thread) {
-			&make_thread(%Head);
-		} else {
-			&disp_msg(\%Head);
-			$scan_count++;
+	} elsif ($exists > 0) {
+		if (&imap_scan_folder(@ranges) < 0) {
+			&imap_close;
+			im_warn("IMAP folder scanning error.\n")
+				if (&debug("imap") || &verbose);
+			$scan_count = -1;
+			return -1;
 		}
 	}
 	&imap_close;
+	return 0;
 }
 
 sub sort_uniq {
-------------- next part --------------
--- Imap.pm.in-	Fri Jul 25 15:47:34 1997
+++ Imap.pm.in	Thu Jul 31 12:12:10 1997
@@ -24,13 +24,14 @@
 use IM::GetPass;
 use IM::EncDec;
 use IM::MsgStore;
+use IM::Scan;
 
 @ISA = qw(Exporter);
 @EXPORT = qw(
     imap_open imap_close imap_select imap_head imap_from imap_get imap_put
     imap_delete imap_copy imap_flags
     imap_list_folder imap_create_folder imap_delete_folder imap_rename_folder
-    imap_get_msg imap_process imap_spec
+    imap_get_msg imap_process imap_spec imap_scan_folder
 );
 
 =head1 NAME
@@ -579,4 +580,127 @@
     }
 
     return ($folder, $auth, $user, $host);
+}
+
+############################################
+##
+## For imls
+##
+
+sub imap_scan_folder (@) {
+    my @ranges = @_;
+    my $msgset, $seq, $resp;
+    my $uid, $size, $len, $field, %Head;
+
+    $msgset  = &imap_range2set(@ranges);
+    return -1 if ($msgset < 0);
+    $seq = $ImapSeq++;
+    $resp = &send_command(\*IMAPd,
+	"im$seq UID FETCH $msgset (UID RFC822.SIZE RFC822.HEADER)", '');
+    while ($resp =~
+    /^\* \d+ FETCH \(UID (\d+) RFC822.SIZE (\d+) RFC822\.HEADER \{(\d+)\}/i) {
+	($uid, $size, $len) = ($1, $2, $3);
+	($field, %Head) = ('', ());
+	while (<IMAPd>) {
+	    $len -= length;
+	    s/\r?\n$//;
+	    im_warn("$_") if (&debug("imap") || &verbose);
+	    if (/^\s/) {
+		s/^\s+//;
+		$Head{$field} = $Head{$field} . $_;
+		last if ($len <= 0);
+		next;
+	    } elsif (/^([^:]+):\s*/) {
+		$field = lc($1);
+		$Head{$field} = $';
+	    } else {
+		$inheader = 0;
+		last if ($len <= 0);
+		next;
+	    }
+	    last if ($len <= 0);
+	}
+	$resp = &next_response(\*IMAPd);
+	return -1 if ($resp !~ /^\)/);
+
+#	$Head{'bytes:'} = $size;
+	$Head{'kbytes:'} = int(($size + 1023) / 1024);
+	$Head{'number'} = $uid;
+	$Head{'folder'} = "\%$folder";
+	parse_header(\%Head);
+
+	if ($opt_thread) {
+	    &make_thread(%Head);
+	} else {
+	    &disp_msg(\%Head);
+	    $main::scan_count++;
+	}
+	$resp = &next_response(\*IMAPd);
+    }
+    if ($resp !~ /^im$seq OK/) {
+	im_warn("FETCH command failed.\n") if (&debug("imap") || &verbose);
+	return -1;
+    }
+    return 0;
+}
+
+sub imap_range2set (@) {
+    my @ranges = @_;
+    my @alluids, $min, $max;
+    my @uids, $fromuid, $touid, $dir;
+
+    @alluids = &imap_all_uids;
+    return -1 if ($alluids[0] < 0);
+    ($min, $max) = ($alluids[0], $alluids[$#alluids]);
+
+    @ranges = ('first-last') if (!@ranges || grep(/^all$/, @ranges));
+    foreach (@ranges) {
+	if (/^(\d+|first|last)-(\d+|first|last)$/) {
+	    $fromuid = &imap_message_number($min, $max, $1);
+	    $touid   = &imap_message_number($min, $max, $2);
+	    $_ = "$fromuid:$touid";
+	} elsif (/^(\d+|last|first):([+-]?)(\d+)$/) {
+	    if ($1 eq 'last') {
+		$dir = (!$2 || $2 eq '-') ? -1 : +1;
+	    } else {
+		$dir = (!$2 || $2 eq '+') ? +1 : -1;
+	    }
+	    $fromuid = &imap_message_number($min, $max, $1);
+	    if ($dir > 0) {
+		@uids = grep($_ >= $fromuid, @alluids);
+		splice(@uids, $3) if ($3 < @uids);
+	    } else {
+		@uids = grep($_ <= $fromuid, @alluids);
+		splice(@uids, 0, @uids - $3) if ($3 < @uids);
+	    }
+	    $_ = join(',', @uids);
+	} elsif (/^(\d+|first|last)$/) {
+	    $_ = &imap_message_number($min, $max, $1);
+	}
+    }
+    return join(',', @ranges);
+}
+
+sub imap_all_uids () {
+    my $seq, $resp, @uids;
+
+    $seq = $ImapSeq++;
+    $resp = &send_command(\*IMAPd, "im$seq UID SEARCH 1:*", '');
+    if ($resp =~ /^\* SEARCH ((\d+[ ]?)*)/i) {
+	@uids = split(' ', $1);
+    } else {
+	im_warn("SEARCH command failed.\n") if (&debug("imap") || &verbose);
+    }
+    $resp = &next_response(\*IMAPd);
+    return (-1) if ($resp !~ /^im$seq OK/);
+    return @uids;
+}
+
+sub imap_message_number ($$$) {
+    my ($min, $max, $num) = @_;
+
+    return $num if $num =~ /^\d+$/;
+    return $min if $num =~ /^first$/;
+    return $max if $num =~ /^last$/;
+    return '';
 }


Mew-dist メーリングリストの案内