@@ -388,10 +388,10 @@ const SequenceIterator = struct {
388388
389389 pub fn next (self : * SequenceIterator ) ? u32 {
390390 // Continue yielding from current range
391- if (self .range_current <= self .range_end ) {
391+ while (self .range_current <= self .range_end ) {
392392 const val = self .range_current ;
393393 self .range_current += 1 ;
394- return val ;
394+ if ( val >= 1 and val <= self . max_seq ) return val ;
395395 }
396396
397397 // Parse next element from sequence set
@@ -417,12 +417,15 @@ const SequenceIterator = struct {
417417 // Reverse range
418418 self .range_current = end_num + 1 ;
419419 self .range_end = start_num ;
420- return end_num ;
420+ if (end_num >= 1 and end_num <= self .max_seq ) return end_num ;
421+ return self .next ();
421422 }
422- return start_num ;
423+ if (start_num >= 1 and start_num <= self .max_seq ) return start_num ;
424+ return self .next ();
423425 }
424426
425- return start_num ;
427+ if (start_num >= 1 and start_num <= self .max_seq ) return start_num ;
428+ return self .next ();
426429 }
427430
428431 fn parseNum (self : * SequenceIterator ) ? u32 {
@@ -1545,10 +1548,41 @@ pub const ImapSession = struct {
15451548 }
15461549 }
15471550
1551+ // Detect if we only need metadata (FLAGS, UID, RFC822.SIZE) — no file content read needed
1552+ const want_rfc822_size = std .mem .indexOf (u8 , items_raw , "RFC822.SIZE" ) != null ;
1553+ const metadata_only = ! want_full_body and ! want_header_only and ! want_bodystructure and ! want_body_text_only ;
1554+
15481555 var seq = start ;
15491556 while (seq <= end ) : (seq += 1 ) {
15501557 const filename = files [seq - 1 ];
15511558 const uid = self .getUidForSeq (seq );
1559+
1560+ // For metadata-only requests (FLAGS, UID, RFC822.SIZE), skip reading file content
1561+ if (metadata_only ) {
1562+ const msg_flags = MaildirFlags .fromFilename (filename );
1563+ var flag_str_buf : [256 ]u8 = undefined ;
1564+ const flag_str = msg_flags .toImapString (& flag_str_buf );
1565+
1566+ if (want_rfc822_size ) {
1567+ const filepath = try std .fmt .allocPrint (self .allocator , "{s}/{s}" , .{ dir , filename });
1568+ defer self .allocator .free (filepath );
1569+ const file_size = fs_compat .getFileSize (filepath ) catch 0 ;
1570+ const resp = try std .fmt .allocPrint (self .allocator , "* {d} FETCH (UID {d} FLAGS ({s}) RFC822.SIZE {d})" , .{
1571+ seq , uid , flag_str , file_size ,
1572+ });
1573+ defer self .allocator .free (resp );
1574+ try self .writeData (resp );
1575+ } else {
1576+ const resp = try std .fmt .allocPrint (self .allocator , "* {d} FETCH (UID {d} FLAGS ({s}))" , .{
1577+ seq , uid , flag_str ,
1578+ });
1579+ defer self .allocator .free (resp );
1580+ try self .writeData (resp );
1581+ }
1582+ try self .writeData ("\r \n " );
1583+ continue ;
1584+ }
1585+
15521586 const filepath = try std .fmt .allocPrint (self .allocator , "{s}/{s}" , .{ dir , filename });
15531587 defer self .allocator .free (filepath );
15541588
@@ -1773,15 +1807,55 @@ pub const ImapSession = struct {
17731807 std .ascii .indexOfIgnoreCase (criteria , "UNANSWERED" ) == null ;
17741808 const want_unanswered = std .ascii .indexOfIgnoreCase (criteria , "UNANSWERED" ) != null ;
17751809
1810+ // Parse UID range criteria (e.g. "UID 4:*", "UID 1,3,5")
1811+ var uid_range_start : i64 = 0 ;
1812+ var uid_range_end : i64 = std .math .maxInt (i64 );
1813+ var has_uid_range = false ;
1814+ if (std .ascii .indexOfIgnoreCase (criteria , "UID" )) | uid_pos | {
1815+ // Make sure this is the UID keyword, not part of another word
1816+ const after_uid = uid_pos + 3 ;
1817+ if (after_uid < criteria .len ) {
1818+ const rest = std .mem .trim (u8 , criteria [after_uid .. ], " \t " );
1819+ if (rest .len > 0 ) {
1820+ has_uid_range = true ;
1821+ if (std .mem .indexOf (u8 , rest , ":" )) | colon | {
1822+ uid_range_start = std .fmt .parseInt (i64 , rest [0.. colon ], 10 ) catch 1 ;
1823+ const end_part = rest [colon + 1 .. ];
1824+ // End part might have trailing spaces or other criteria
1825+ const end_token = if (std .mem .indexOfAny (u8 , end_part , " \t " )) | sp | end_part [0.. sp ] else end_part ;
1826+ if (std .mem .eql (u8 , end_token , "*" )) {
1827+ uid_range_end = std .math .maxInt (i64 );
1828+ } else {
1829+ uid_range_end = std .fmt .parseInt (i64 , end_token , 10 ) catch std .math .maxInt (i64 );
1830+ }
1831+ } else {
1832+ // Single UID value
1833+ const uid_token = if (std .mem .indexOfAny (u8 , rest , " \t " )) | sp | rest [0.. sp ] else rest ;
1834+ const single_uid = std .fmt .parseInt (i64 , uid_token , 10 ) catch 0 ;
1835+ if (single_uid > 0 ) {
1836+ uid_range_start = single_uid ;
1837+ uid_range_end = single_uid ;
1838+ }
1839+ }
1840+ }
1841+ }
1842+ }
1843+
17761844 var buf : [8192 ]u8 = undefined ;
17771845 var fbs = io_compat .fixedBufferStream (& buf );
17781846 const writer = fbs .writer ();
17791847 writer .writeAll ("SEARCH" ) catch {};
17801848
17811849 for (files , 0.. ) | filename , i | {
17821850 const seq = i + 1 ;
1851+ const uid = self .getUidForSeq (seq );
17831852 const flags = MaildirFlags .fromFilename (filename );
17841853
1854+ // Filter by UID range if specified
1855+ if (has_uid_range ) {
1856+ if (uid < uid_range_start or uid > uid_range_end ) continue ;
1857+ }
1858+
17851859 // Evaluate flag-based criteria
17861860 if (! is_all ) {
17871861 if (want_seen and ! flags .seen ) continue ;
@@ -1813,7 +1887,6 @@ pub const ImapSession = struct {
18131887 const after = criteria [from_pos + 4 .. ];
18141888 const term = extractQuotedArg (after );
18151889 if (term .len > 0 ) {
1816- // Check if From: header contains the term
18171890 if (std .ascii .indexOfIgnoreCase (hdr , "From:" )) | fp | {
18181891 const from_line_end = std .mem .indexOfScalarPos (u8 , hdr , fp , '\n ' ) orelse hdr .len ;
18191892 const from_line = hdr [fp .. from_line_end ];
@@ -1841,7 +1914,7 @@ pub const ImapSession = struct {
18411914 }
18421915 }
18431916
1844- const result_id : i64 = if (is_uid ) self . getUidForSeq ( seq ) else @intCast (seq );
1917+ const result_id : i64 = if (is_uid ) uid else @intCast (seq );
18451918 writer .print (" {d}" , .{result_id }) catch break ;
18461919 }
18471920
0 commit comments