or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

exceptions.mdhigh-level-api.mdindex.mdsmtp-client.mdutilities.md

utilities.mddocs/

0

# Utility Functions

1

2

aiosmtplib provides utility functions for authentication, email message processing, and ESMTP extension handling. These functions are useful for custom implementations, debugging, and applications that need fine-grained control over SMTP operations.

3

4

## Capabilities

5

6

### Authentication Utilities

7

8

Functions for encoding authentication credentials in various SMTP authentication protocols.

9

10

```python

11

from aiosmtplib.auth import auth_plain_encode, auth_login_encode, auth_crammd5_verify

12

```

13

14

```python { .api }

15

def auth_plain_encode(username: str, password: str) -> bytes:

16

"""

17

Encode credentials for PLAIN authentication method.

18

19

Parameters:

20

- username: Authentication username

21

- password: Authentication password

22

23

Returns:

24

Encoded credentials as bytes suitable for PLAIN AUTH command

25

"""

26

27

def auth_login_encode(username: str, password: str) -> tuple[bytes, bytes]:

28

"""

29

Encode credentials for LOGIN authentication method.

30

31

Parameters:

32

- username: Authentication username

33

- password: Authentication password

34

35

Returns:

36

Tuple of (encoded_username, encoded_password) as bytes

37

"""

38

39

def auth_crammd5_verify(username: str, password: str, challenge: bytes) -> bytes:

40

"""

41

Generate CRAM-MD5 authentication response.

42

43

Parameters:

44

- username: Authentication username

45

- password: Authentication password

46

- challenge: Challenge bytes from server

47

48

Returns:

49

CRAM-MD5 response bytes

50

"""

51

```

52

53

### Email Message Utilities

54

55

Functions for processing email messages and extracting address information.

56

57

```python

58

from aiosmtplib.email import extract_recipients, extract_sender, flatten_message, parse_address, quote_address

59

```

60

61

```python { .api }

62

def extract_recipients(message: Union[EmailMessage, Message]) -> list[str]:

63

"""

64

Extract all recipient addresses from email message headers.

65

66

Processes To, Cc, and Bcc headers to build complete recipient list.

67

68

Parameters:

69

- message: EmailMessage or Message object

70

71

Returns:

72

List of recipient email addresses

73

"""

74

75

def extract_sender(message: Union[EmailMessage, Message]) -> Optional[str]:

76

"""

77

Extract sender address from email message headers.

78

79

Checks From header for sender address.

80

81

Parameters:

82

- message: EmailMessage or Message object

83

84

Returns:

85

Sender email address or None if not found

86

"""

87

88

def flatten_message(

89

message: Union[EmailMessage, Message],

90

*,

91

utf8: bool = False,

92

cte_type: str = "8bit"

93

) -> bytes:

94

"""

95

Convert email message to bytes for SMTP transmission.

96

97

Parameters:

98

- message: EmailMessage or Message object

99

- utf8: Use UTF-8 encoding

100

- cte_type: Content transfer encoding type

101

102

Returns:

103

Message as bytes ready for SMTP DATA command

104

"""

105

106

def parse_address(address: str) -> str:

107

"""

108

Parse and normalize email address string.

109

110

Handles various email address formats and extracts the actual

111

email address from display name formats.

112

113

Parameters:

114

- address: Email address string (may include display name)

115

116

Returns:

117

Normalized email address

118

"""

119

120

def quote_address(address: str) -> str:

121

"""

122

Quote email address according to RFC 821 rules.

123

124

Adds angle brackets around address if needed for SMTP commands.

125

126

Parameters:

127

- address: Email address string

128

129

Returns:

130

Properly quoted address for SMTP commands

131

"""

132

```

133

134

### ESMTP Extension Utilities

135

136

Functions for parsing and handling ESMTP server extensions.

137

138

```python

139

from aiosmtplib.esmtp import parse_esmtp_extensions

140

```

141

142

```python { .api }

143

def parse_esmtp_extensions(message: str) -> tuple[dict[str, str], list[str]]:

144

"""

145

Parse EHLO response into extensions dictionary and auth methods list.

146

147

Parameters:

148

- message: Raw EHLO response message from server

149

150

Returns:

151

Tuple of (extensions_dict, auth_methods_list) where:

152

- extensions_dict maps extension names to their parameters

153

- auth_methods_list contains supported authentication methods

154

"""

155

```

156

157

### Low-Level Protocol Access

158

159

Advanced protocol class for custom SMTP implementations.

160

161

```python { .api }

162

class SMTPProtocol:

163

"""

164

Low-level asyncio protocol for SMTP communication.

165

166

Advanced users can use this for custom SMTP implementations

167

that need direct protocol access.

168

"""

169

def __init__(

170

self,

171

loop: Optional[asyncio.AbstractEventLoop] = None,

172

connection_lost_callback: Optional[Callable[[], None]] = None,

173

) -> None: ...

174

```

175

176

## Usage Examples

177

178

### Authentication Utilities

179

180

```python

181

from aiosmtplib.auth import auth_plain_encode, auth_login_encode, auth_crammd5_verify

182

183

# Encode credentials for different auth methods

184

username = "user@example.com"

185

password = "secret123"

186

187

# PLAIN authentication

188

plain_creds = auth_plain_encode(username, password)

189

print(f"PLAIN credentials: {plain_creds}")

190

191

# LOGIN authentication

192

login_user, login_pass = auth_login_encode(username, password)

193

print(f"LOGIN username: {login_user}")

194

print(f"LOGIN password: {login_pass}")

195

196

# CRAM-MD5 authentication (requires server challenge)

197

challenge = b"<12345.678@example.com>"

198

crammd5_response = auth_crammd5_verify(username, password, challenge)

199

print(f"CRAM-MD5 response: {crammd5_response}")

200

```

201

202

### Email Message Processing

203

204

```python

205

from aiosmtplib.email import extract_recipients, extract_sender, flatten_message, parse_address, quote_address

206

from email.message import EmailMessage

207

208

# Create test message

209

message = EmailMessage()

210

message["From"] = "sender@example.com"

211

message["To"] = "recipient1@example.com, recipient2@example.com"

212

message["Cc"] = "cc@example.com"

213

message["Bcc"] = "bcc@example.com"

214

message["Subject"] = "Test Message"

215

message.set_content("This is a test message.")

216

217

# Extract recipients

218

recipients = extract_recipients(message)

219

print(f"All recipients: {recipients}")

220

# Output: ['recipient1@example.com', 'recipient2@example.com', 'cc@example.com', 'bcc@example.com']

221

222

# Extract sender

223

sender = extract_sender(message)

224

print(f"Sender: {sender}")

225

# Output: sender@example.com

226

227

# Convert to bytes for transmission

228

message_bytes = flatten_message(message)

229

print(f"Message size: {len(message_bytes)} bytes")

230

231

# Parse and quote addresses

232

raw_address = "John Doe <john@example.com>"

233

parsed = parse_address(raw_address)

234

quoted = quote_address(parsed)

235

print(f"Raw: {raw_address}")

236

print(f"Parsed: {parsed}")

237

print(f"Quoted: {quoted}")

238

```

239

240

### ESMTP Extension Parsing

241

242

```python

243

from aiosmtplib.esmtp import parse_esmtp_extensions

244

245

# Example EHLO response from server

246

ehlo_response = """250-smtp.example.com Hello [192.168.1.100]

247

250-SIZE 35882577

248

250-8BITMIME

249

250-PIPELINING

250

250-CHUNKING

251

250-SMTPUTF8

252

250-AUTH PLAIN LOGIN CRAM-MD5

253

250-AUTH=PLAIN LOGIN CRAM-MD5

254

250 HELP"""

255

256

# Parse extensions and auth methods

257

extensions, auth_methods = parse_esmtp_extensions(ehlo_response)

258

259

print("Server extensions:")

260

for ext_name, ext_value in extensions.items():

261

print(f" {ext_name}: {ext_value}")

262

263

print(f"\nSupported auth methods: {auth_methods}")

264

265

# Check for specific extensions

266

if "PIPELINING" in extensions:

267

print("Server supports command pipelining")

268

269

if "SIZE" in extensions:

270

max_size = extensions["SIZE"]

271

print(f"Maximum message size: {max_size} bytes")

272

```

273

274

### Custom Authentication Implementation

275

276

```python

277

import asyncio

278

import aiosmtplib

279

import base64

280

281

async def custom_auth_example():

282

smtp = aiosmtplib.SMTP(hostname="localhost", port=1025)

283

284

try:

285

await smtp.connect()

286

287

# Check what auth methods server supports

288

auth_methods = smtp.supported_auth_methods

289

print(f"Server supports: {auth_methods}")

290

291

if "PLAIN" in auth_methods:

292

# Manual PLAIN authentication

293

credentials = aiosmtplib.auth_plain_encode("user", "pass")

294

auth_string = base64.b64encode(credentials).decode()

295

296

# Send AUTH command manually

297

response = await smtp.execute_command(f"AUTH PLAIN {auth_string}")

298

print(f"Manual PLAIN auth response: {response}")

299

300

elif "LOGIN" in auth_methods:

301

# Manual LOGIN authentication

302

username_b64, password_b64 = aiosmtplib.auth_login_encode("user", "pass")

303

304

# LOGIN authentication is interactive

305

response1 = await smtp.execute_command("AUTH LOGIN")

306

print(f"AUTH LOGIN response: {response1}")

307

308

response2 = await smtp.execute_command(username_b64.decode())

309

print(f"Username response: {response2}")

310

311

response3 = await smtp.execute_command(password_b64.decode())

312

print(f"Password response: {response3}")

313

314

finally:

315

smtp.close()

316

317

asyncio.run(custom_auth_example())

318

```

319

320

### Message Validation and Processing

321

322

```python

323

import aiosmtplib

324

from email.message import EmailMessage

325

import email.utils

326

327

def validate_and_process_message(message):

328

"""Validate and process email message before sending."""

329

330

# Extract and validate sender

331

sender = aiosmtplib.extract_sender(message)

332

if not sender:

333

raise ValueError("Message must have a From header")

334

335

# Validate sender format

336

try:

337

parsed_sender = aiosmtplib.parse_address(sender)

338

if "@" not in parsed_sender:

339

raise ValueError(f"Invalid sender address: {sender}")

340

except Exception as e:

341

raise ValueError(f"Cannot parse sender address: {e}")

342

343

# Extract and validate recipients

344

recipients = aiosmtplib.extract_recipients(message)

345

if not recipients:

346

raise ValueError("Message must have recipients (To, Cc, or Bcc)")

347

348

# Validate each recipient

349

valid_recipients = []

350

for recipient in recipients:

351

try:

352

parsed_recipient = aiosmtplib.parse_address(recipient)

353

if "@" not in parsed_recipient:

354

print(f"Warning: Skipping invalid recipient: {recipient}")

355

continue

356

valid_recipients.append(parsed_recipient)

357

except Exception as e:

358

print(f"Warning: Cannot parse recipient {recipient}: {e}")

359

360

if not valid_recipients:

361

raise ValueError("No valid recipients found")

362

363

# Add Message-ID if missing

364

if "Message-ID" not in message:

365

message["Message-ID"] = email.utils.make_msgid()

366

367

# Add Date if missing

368

if "Date" not in message:

369

message["Date"] = email.utils.formatdate(localtime=True)

370

371

return parsed_sender, valid_recipients

372

373

# Example usage

374

message = EmailMessage()

375

message["From"] = "John Doe <john@example.com>"

376

message["To"] = "recipient@example.com, invalid-email"

377

message["Subject"] = "Test Message"

378

message.set_content("This is a test message.")

379

380

try:

381

sender, recipients = validate_and_process_message(message)

382

print(f"Validated sender: {sender}")

383

print(f"Valid recipients: {recipients}")

384

print(f"Message-ID: {message['Message-ID']}")

385

print(f"Date: {message['Date']}")

386

except ValueError as e:

387

print(f"Message validation failed: {e}")

388

```

389

390

### Advanced Message Processing

391

392

```python

393

import aiosmtplib

394

from email.message import EmailMessage

395

from email.mime.multipart import MIMEMultipart

396

from email.mime.text import MIMEText

397

from email.mime.base import MIMEBase

398

import email.encoders

399

400

def create_complex_message():

401

"""Create a complex multipart message."""

402

403

# Create multipart message

404

msg = MIMEMultipart("alternative")

405

msg["From"] = "sender@example.com"

406

msg["To"] = "recipient@example.com"

407

msg["Subject"] = "Complex Message"

408

409

# Add text part

410

text_part = MIMEText("This is the plain text version.", "plain")

411

msg.attach(text_part)

412

413

# Add HTML part

414

html_part = MIMEText("<h1>This is the HTML version</h1>", "html")

415

msg.attach(html_part)

416

417

return msg

418

419

async def send_complex_message():

420

# Create complex message

421

message = create_complex_message()

422

423

# Process with utilities

424

sender = aiosmtplib.extract_sender(message)

425

recipients = aiosmtplib.extract_recipients(message)

426

message_bytes = aiosmtplib.flatten_message(message, utf8=True)

427

428

print(f"Sender: {sender}")

429

print(f"Recipients: {recipients}")

430

print(f"Message size: {len(message_bytes)} bytes")

431

432

# Send using SMTP client

433

smtp = aiosmtplib.SMTP(hostname="localhost", port=1025)

434

async with smtp:

435

response = await smtp.send_message(message)

436

print(f"Message sent: {response}")

437

438

asyncio.run(send_complex_message())

439

```

440

441

## Internal vs Public Utilities

442

443

### Public Utilities (Exported in __all__)

444

These functions are intended for public use:

445

- Authentication encoding functions

446

- Email message processing functions

447

- ESMTP extension parsing

448

- SMTPProtocol for advanced use cases

449

450

### Internal Utilities (Not Recommended for Direct Use)

451

Some utilities are for internal use but may be useful in specific scenarios:

452

- Low-level protocol functions

453

- Internal message parsing helpers

454

- Connection management utilities

455

456

When using any utility functions, prefer the high-level `send()` function or `SMTP` class for most use cases, and only use utilities when you need specific functionality not provided by the main API.