This commit is contained in:
Oliver Drobnik
2025-03-05 10:12:24 +01:00
parent 5409de50f5
commit 09594acc42
9 changed files with 8 additions and 78 deletions
+1 -42
View File
@@ -30,17 +30,7 @@ LoggingSystem.bootstrap { label in
// Create a logger for the main application using Swift Logging
let logger = Logger(label: "com.cocoanetics.SwiftIMAP.Main")
// Helper function for debug prints - only prints when ENABLE_DEBUG_OUTPUT is set
func debugPrint(_ message: String) {
if ProcessInfo.processInfo.environment["ENABLE_DEBUG_OUTPUT"] == "1" {
print("DEBUG: \(message)")
}
}
print("📧 SwiftIMAPCLI - Email Reading Test")
print("Debug mode: OS_LOG_DISABLE=\(ProcessInfo.processInfo.environment["OS_LOG_DISABLE"] ?? "not set")")
print("Debug mode: OS_ACTIVITY_MODE=\(ProcessInfo.processInfo.environment["OS_ACTIVITY_MODE"] ?? "not set")")
print("Debug mode: SWIFT_LOG_LEVEL=\(ProcessInfo.processInfo.environment["SWIFT_LOG_LEVEL"] ?? "not set")")
do {
// Configure SwiftDotenv with the specified path
@@ -72,7 +62,6 @@ do {
logger.info("Host: \(host)")
logger.info("Port: \(port)")
logger.info("Username: \(username)")
debugPrint("IMAP configuration: \(host):\(port) with username \(username)")
// Create an IMAP server instance
let server = IMAPServer(host: host, port: port)
@@ -81,20 +70,13 @@ do {
await Task {
do {
// Connect to the server
debugPrint("Connecting to IMAP server \(host):\(port)...")
try await server.connect()
debugPrint("Connection established successfully")
// Login with credentials
debugPrint("Authenticating with username \(username)...")
try await server.login(username: username, password: password)
debugPrint("Authentication successful")
// Detect standard folders
logger.notice("Detecting standard folders...")
debugPrint("Detecting standard folders...")
let detectedConfig = try await server.detectStandardFolders()
debugPrint("Standard folders detected successfully")
// Print detected folder configuration
print("\n📁 Detected Standard Folders:")
@@ -106,26 +88,19 @@ do {
print("")
// Select the INBOX mailbox and get mailbox information
debugPrint("Selecting INBOX mailbox...")
let mailboxStatus = try await server.selectMailbox("INBOX")
debugPrint("INBOX selected. Message count: \(mailboxStatus.messageCount)")
// Print mailbox information
if mailboxStatus.messageCount > 0 {
// Fetch the 10 latest complete emails including attachments
logger.notice("Fetching latest emails...")
debugPrint("Fetching latest emails...")
let startMessage = SequenceNumber(max(1, mailboxStatus.messageCount - 9))
let endMessage = SequenceNumber(mailboxStatus.messageCount)
debugPrint("Fetching messages from sequence \(startMessage) to \(endMessage)")
do {
// Use the fetchEmails method with the sequence number set
let emails = try await server.fetchMessages(using: SequenceNumberSet(startMessage...endMessage))
debugPrint("\(emails.count) emails fetched successfully")
logger.notice("📧 Latest Complete Emails (\(emails.count)) 📧")
print("\n📧 Latest Complete Emails (\(emails.count)) 📧")
// Display emails using the improved debug description format
@@ -135,41 +110,25 @@ do {
}
} catch {
logger.error("Failed to fetch emails: \(error.localizedDescription)")
print("Failed to fetch emails: \(error.localizedDescription)")
debugPrint("ERROR: \(error)")
}
} else {
logger.notice("No messages in mailbox")
print("No messages in mailbox")
}
// Logout from the server
debugPrint("Logging out...")
try await server.logout()
debugPrint("Logout successful")
// Close the connection
debugPrint("Disconnecting...")
try await server.disconnect()
debugPrint("Disconnection complete")
}
catch let error as NIOIMAP.IMAPDecoderError {
let string = String(buffer: error.buffer)
print(string)
debugPrint("ERROR: \(error)")
}
catch {
logger.error("Error: \(error.localizedDescription)")
debugPrint("ERROR: \(error)")
exit(1)
}
}.value
} catch {
logger.error("Error: \(error.localizedDescription)")
debugPrint("ERROR: \(error)")
exit(1)
}
@@ -3,7 +3,7 @@ import NIOCore
import Logging
/// Base class for SMTP command handlers that provides common functionality
open class BaseSMTPHandler<T>: ChannelInboundHandler, RemovableChannelHandler, SMTPCommandHandler, LoggableHandler {
open class BaseSMTPHandler<T>: ChannelInboundHandler, RemovableChannelHandler, SMTPCommandHandler {
public typealias InboundIn = SMTPResponse
public typealias InboundOut = Never
public typealias ResultType = T
@@ -14,9 +14,6 @@ open class BaseSMTPHandler<T>: ChannelInboundHandler, RemovableChannelHandler, S
/// The promise that will be fulfilled when the command completes
public let promise: EventLoopPromise<ResultType>
/// Logger for handler operations
public var logger: Logger
/// Initialize a new handler
/// - Parameters:
/// - commandTag: Optional tag for the command (not commonly used in SMTP but included for consistency)
@@ -24,7 +21,6 @@ open class BaseSMTPHandler<T>: ChannelInboundHandler, RemovableChannelHandler, S
public required init(commandTag: String?, promise: EventLoopPromise<ResultType>) {
self.commandTag = commandTag
self.promise = promise
self.logger = Logger(label: "com.cocoanetics.SwiftSMTP.Handler.\(String(describing: type(of: self)))")
}
/// Process a response line from the server
@@ -75,7 +71,6 @@ open class BaseSMTPHandler<T>: ChannelInboundHandler, RemovableChannelHandler, S
/// - data: The data read from the channel
public func channelRead(context: ChannelHandlerContext, data: NIOAny) {
let response = self.unwrapInboundIn(data)
logger.debug("Received response: \(response)")
// Process the response
let isComplete = processResponse(response)
+1 -4
View File
@@ -13,18 +13,15 @@ public final class DataHandler: BaseSMTPHandler<Bool> {
- Returns: Whether the handler is complete
*/
override public func processResponse(_ response: SMTPResponse) -> Bool {
logger.debug("Received DATA response: \(response.code) \(response.message)")
// 3xx responses are considered successful for DATA command (server is ready for content)
if response.code >= 300 && response.code < 400 {
logger.info("Server ready for email content")
promise.succeed(true)
} else {
// Any other response is considered a failure
logger.warning("DATA command rejected: \(response.code) \(response.message)")
promise.succeed(false)
}
return true // Always complete after a single response
}
}
}
@@ -13,18 +13,15 @@ public final class MailFromHandler: BaseSMTPHandler<Bool> {
- Returns: Whether the handler is complete
*/
override public func processResponse(_ response: SMTPResponse) -> Bool {
logger.debug("Received MAIL FROM response: \(response.code) \(response.message)")
// 2xx responses are considered successful
if response.code >= 200 && response.code < 300 {
logger.info("Sender accepted")
promise.succeed(true)
} else {
// Any other response is considered a failure
logger.warning("Sender rejected: \(response.code) \(response.message)")
promise.succeed(false)
}
return true // Always complete after a single response
}
}
}
+1 -4
View File
@@ -15,19 +15,16 @@ public final class QuitHandler: BaseSMTPHandler<Bool> {
override public func processResponse(_ response: SMTPResponse) -> Bool {
// For QUIT command, any response is considered successful since we're going to close the connection anyway
// But we should log the response for debugging purposes
logger.debug("Server response to QUIT: \(response.code) \(response.message)")
// 2xx responses are considered successful
if response.code >= 200 && response.code < 300 {
logger.debug("QUIT command successful")
promise.succeed(true)
} else {
// Even non-2xx responses are logged but we still succeed the promise
// since we'll be closing the connection anyway
logger.warning("Unexpected response to QUIT: \(response.code) \(response.message)")
promise.succeed(false)
}
return true // Always complete after a single response
}
}
}
@@ -13,18 +13,15 @@ public final class RcptToHandler: BaseSMTPHandler<Bool> {
- Returns: Whether the handler is complete
*/
override public func processResponse(_ response: SMTPResponse) -> Bool {
logger.debug("Received RCPT TO response: \(response.code) \(response.message)")
// 2xx responses are considered successful
if response.code >= 200 && response.code < 300 {
logger.info("Recipient accepted")
promise.succeed(true)
} else {
// Any other response is considered a failure
logger.warning("Recipient rejected: \(response.code) \(response.message)")
promise.succeed(false)
}
return true // Always complete after a single response
}
}
}
@@ -24,9 +24,3 @@ public protocol SMTPCommandHandler {
/// - promise: The promise to fulfill when the command completes
init(commandTag: String?, promise: EventLoopPromise<ResultType>)
}
/// Protocol for handlers that can have a logger set
public protocol LoggableHandler {
/// Logger for handler operations
var logger: Logger { get set }
}
@@ -13,18 +13,15 @@ public final class SendContentHandler: BaseSMTPHandler<Bool> {
- Returns: Whether the handler is complete
*/
override public func processResponse(_ response: SMTPResponse) -> Bool {
logger.debug("Received email content response: \(response.code) \(response.message)")
// 2xx responses are considered successful
if response.code >= 200 && response.code < 300 {
logger.info("Email content accepted")
promise.succeed(true)
} else {
// Any other response is considered a failure
logger.warning("Email content rejected: \(response.code) \(response.message)")
promise.succeed(false)
}
return true // Always complete after a single response
}
}
}
@@ -13,18 +13,15 @@ public final class StartTLSHandler: BaseSMTPHandler<Bool> {
- Returns: Whether the handler is complete
*/
override public func processResponse(_ response: SMTPResponse) -> Bool {
logger.debug("Received STARTTLS response: \(response.code) \(response.message)")
// 2xx responses are considered successful
if response.code >= 200 && response.code < 300 {
logger.info("STARTTLS command accepted")
promise.succeed(true)
} else {
// Any other response is considered a failure
logger.warning("STARTTLS command rejected: \(response.code) \(response.message)")
promise.succeed(false)
}
return true // Always complete after a single response
}
}
}