add fetchFullMessage, fetchSection to IMAPClient for MIME attachment retrieval
This commit is contained in:
@@ -93,6 +93,65 @@ public actor IMAPClient: IMAPClientProtocol {
|
||||
return parseFlagResponses(responses)
|
||||
}
|
||||
|
||||
public func fetchFullMessage(uid: Int) async throws -> String {
|
||||
guard var runner else { throw IMAPError.notConnected }
|
||||
let uidValue = UID(rawValue: UInt32(uid))
|
||||
let range = MessageIdentifierRange<UID>(uidValue...uidValue)
|
||||
let set = MessageIdentifierSetNonEmpty<UID>(range: range)
|
||||
let responses = try await runner.run(.uidFetch(
|
||||
.set(set),
|
||||
[.bodySection(peek: true, SectionSpecifier(kind: .complete), nil)],
|
||||
[]
|
||||
))
|
||||
self.runner = runner
|
||||
return parseFullMessageResponse(responses)
|
||||
}
|
||||
|
||||
public func fetchSection(uid: Int, mailbox: String, section: String) async throws -> Data {
|
||||
guard var runner else { throw IMAPError.notConnected }
|
||||
|
||||
// Select the mailbox first
|
||||
let selectResponses = try await runner.run(.select(MailboxName(ByteBuffer(string: mailbox))))
|
||||
self.runner = runner
|
||||
guard selectResponses.contains(where: { isOKTagged($0) }) else {
|
||||
throw IMAPError.unexpectedResponse("SELECT \(mailbox) failed")
|
||||
}
|
||||
|
||||
let uidValue = UID(rawValue: UInt32(uid))
|
||||
let range = MessageIdentifierRange<UID>(uidValue...uidValue)
|
||||
let set = MessageIdentifierSetNonEmpty<UID>(range: range)
|
||||
|
||||
// Parse section path into SectionSpecifier.Part
|
||||
let sectionParts = section.split(separator: ".").compactMap { Int($0) }
|
||||
let part = SectionSpecifier.Part(sectionParts)
|
||||
let spec = SectionSpecifier(part: part)
|
||||
|
||||
let responses = try await runner.run(.uidFetch(
|
||||
.set(set),
|
||||
[.bodySection(peek: true, spec, nil)],
|
||||
[]
|
||||
))
|
||||
self.runner = runner
|
||||
|
||||
var bodyBuffer = ByteBuffer()
|
||||
for response in responses {
|
||||
if case .fetch(let fetchResponse) = response {
|
||||
if case .streamingBytes(let bytes) = fetchResponse {
|
||||
var mutableBytes = bytes
|
||||
bodyBuffer.writeBuffer(&mutableBytes)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The section content may be base64 encoded — decode it
|
||||
let raw = String(buffer: bodyBuffer)
|
||||
let cleaned = raw.filter { !$0.isWhitespace }
|
||||
if let decoded = Data(base64Encoded: cleaned) {
|
||||
return decoded
|
||||
}
|
||||
return Data(bodyBuffer.readableBytesView)
|
||||
}
|
||||
|
||||
public func fetchBody(uid: Int) async throws -> (text: String?, html: String?) {
|
||||
guard var runner else { throw IMAPError.notConnected }
|
||||
let uidValue = UID(rawValue: UInt32(uid))
|
||||
@@ -456,6 +515,22 @@ public actor IMAPClient: IMAPClientProtocol {
|
||||
return results
|
||||
}
|
||||
|
||||
private func parseFullMessageResponse(_ responses: [Response]) -> String {
|
||||
var bodyBuffer = ByteBuffer()
|
||||
for response in responses {
|
||||
if case .fetch(let fetchResponse) = response {
|
||||
switch fetchResponse {
|
||||
case .streamingBytes(let bytes):
|
||||
var mutableBytes = bytes
|
||||
bodyBuffer.writeBuffer(&mutableBytes)
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return String(buffer: bodyBuffer)
|
||||
}
|
||||
|
||||
private func parseBodyResponse(_ responses: [Response]) -> (text: String?, html: String?) {
|
||||
var bodyBuffer = ByteBuffer()
|
||||
|
||||
|
||||
@@ -9,6 +9,10 @@ public protocol IMAPClientProtocol: Sendable {
|
||||
func fetchFlags(uids: ClosedRange<Int>) async throws -> [UIDFlagsPair]
|
||||
func fetchBody(uid: Int) async throws -> (text: String?, html: String?)
|
||||
|
||||
// v0.5 attachment/MIME operations
|
||||
func fetchFullMessage(uid: Int) async throws -> String
|
||||
func fetchSection(uid: Int, mailbox: String, section: String) async throws -> Data
|
||||
|
||||
// v0.3 write operations
|
||||
func storeFlags(uid: Int, mailbox: String, add: [String], remove: [String]) async throws
|
||||
func moveMessage(uid: Int, from: String, to: String) async throws
|
||||
|
||||
Reference in New Issue
Block a user