imaplib - Libreria Client IMAP4

Scopo: Libreria Client per comunicare con IMAP4

imaplib implementa un client per comunicare con i server che utilizzano il protocollo IMAP (Internet Message Access Protocol), versione 4. Il protocollo IMAP definisce un insieme di comandi da inviare al server e le risposte ritornate al client. La maggior parte dei comandi è disponibile come metodi dell'oggetto IMAP4 usato per comunicare con il server.

Questi esempi trattano parte del protocollo IMAP, ma non sono da ritenersi esaustivi. Si faccia riferimento a RFC 3501 per i dettagli completi.

Variazioni

Ci sono tre classi client per comunicare con i server usando vari meccanismi. La prima, IMAP4, usa socket con testo in chiaro; IMAP4_SSL usa una comunicazione criptata su socket SSL e IMAP4_stream usa l'input e l'output standard di un comando esterno. Tutti gli esempi qui sotto useranno IMAP4_SSL ma le API per le altre classi sono simili.

Connessione al Server

Ci sono due passi necessari per stabilire una connessione con un server IMAP. Prima si imposta la connessione stessa al socket, poi ci si autentica come utente con una utenza sul server. Il codice seguente leggerà le informazioni di server e utente da un file di configurazione.

# imaplib_connect.py

import imaplib
import configparser
import os


def open_connection(verbose=False):
    # Legge il file di configurazione
    config = configparser.ConfigParser()
    config.read([os.path.expanduser('~/.pymotw3')])

    # Connessione al server
    hostname = config.get('server', 'hostname')
    if verbose:
        print('Connessione a', hostname)
    connection = imaplib.IMAP4_SSL(hostname)

    # Accesso all'utenza
    username = config.get('account', 'username')
    password = config.get('account', 'password')
    print(username, password)
    if verbose:
        print('Autenticazione come', username)
    connection.login(username, password)
    return connection


if __name__ == '__main__':
    with open_connection(verbose=True) as c:
        print(c)

Quando in esecuzione, open_connection() legge le informazioni di configurazione da un file nella directory home dell'utente, quindi apre una connessione IMAP4_SSL e avviene l'autenticazione.

$ python3 imaplib_connect.py

Connessione a xo2.x10hosting.com

Autenticazione come test@robyp.x10host.com
<imaplib.IMAP4_SSL object at 0x7fcc25438ef0>

Gli altri esempi di questo articolo riutilizzano lo script di cui sopra, per evitare duplicazione di codice.

Autenticazione Fallita

Se la connessione viene stabilita, ma l'autenticazione fallisce, viene sollevata una eccezione.

# imaplib_connect_fail.py

import imaplib
import configparser
import os


# Legge il file di configurazione
config = configparser.ConfigParser()
config.read([os.path.expanduser('~/.pymotw3')])

# Connessione al server
hostname = config.get('server', 'hostname')
print('Connessione a', hostname)
connection = imaplib.IMAP4_SSL(hostname)

# Accesso all'utenza
username = config.get('account', 'username')
password = 'password_errata'
print('Autenticazione come', username)
try:
    connection.login(username, password)
except Exception as err:
    print('ERRORE:', err)

Questo esempio usa apposta una password sbagliata per far scattare una eccezione.

$ python3 imaplib_connect_fail.py

Autenticazione come pymotw-it@robyp.x10host.com
ERRORE: b'[AUTHENTICATIONFAILED] Authentication failed.'

Esempio di configurazione

Dal punto di vista di questa dimostrazione, l'utenza di esempio he le seguenti cartelle di posta significative in gerarchia. le altre (Junk, Trash, Drafts, Sent) sono irrilevanti e sono state stralciate dalla stampa dei risultati.

  • INBOX
  • INBOX.Messaggi Eliminati
  • INBOX.Archive
  • INBOX.Esempio
    • 2019

C'è un messaggio non letto nella cartella INBOX e un messaggio letto in Esempio/2019.

Elencare le caselle

Per recuperare le caselle disponibili per una utenza si usi il metodo list().

# imaplib_list.py

import imaplib
from pprint import pprint
from imaplib_connect import open_connection


with open_connection() as c:
    typ, data = c.list()
    print('Codice risposta:', typ)
    print('Risposta:')
    pprint(data)

Il valore restituito è una tuple contenente un codice di risposta e il dato ritornato dal server. Il codice di risposta è OK, a meno che non ci sia un errore. Il dato per list() è rappresentato da una sequenza di stringhe che contengono segnalatori, il delimitatore di gerarchia e il nome di casella per ciascuna casella di posta.

$ python3 imaplib_list.py

Codice risposta: OK
Risposta:
[b'(\\HasChildren) "." INBOX',
 b'(\\HasNoChildren) "." "INBOX.Messaggi Eliminati"',
 b'(\\HasChildren) "." INBOX.Esempio',
 b'(\\HasNoChildren) "." INBOX.Esempio.2019',
 b'(\\HasNoChildren \\Archive) "." INBOX.Archive']

Ogni stringa di risposta può essere divisa in tre parti usando re oppure csv (si veda IMAP Backup Script nei riferimenti di questo articolo per un esempio che usa csv).

# imaplib_list_parse.py

import imaplib
import re

from imaplib_connect import open_connection

list_response_pattern = re.compile(
    r'\((?P<flags>.*?)\) "(?P<delimiter>.*)" (?P<name>.*)'
)


def parse_list_response(line):
    match = list_response_pattern.match(line.decode('utf-8'))
    flags, delimiter, mailbox_name = match.groups()
    mailbox_name = mailbox_name.strip('"')
    return (flags, delimiter, mailbox_name)


with open_connection() as c:
    typ, data = c.list()
print('Codice di risposta:', typ)

for line in data:
    print('Risposta del server:', line)
    flags, delimiter, mailbox_name = parse_list_response(line)
    print('Risposta analizzata:', (flags, delimiter, mailbox_name))

Il server racchiude tra apici le caselle il cui nome contiene spazi, ma questi apici devono essere eliminati per usare il nome della casella in altre chiamate al server successivamente.

$ python3 imaplib_list_parse.py

Codice di risposta: OK
Risposta del server: b'(\\HasChildren) "." INBOX'
Risposta analizzata: ('\\HasChildren', '.', 'INBOX')
Risposta del server: b'(\\HasNoChildren) "." "INBOX.Messaggi Eliminati"'
Risposta analizzata: ('\\HasNoChildren', '.', 'INBOX.Messaggi Eliminati')
Risposta del server: b'(\\HasChildren) "." INBOX.Esempio'
Risposta analizzata: ('\\HasChildren', '.', 'INBOX.Esempio')
Risposta del server: b'(\\HasNoChildren) "." INBOX.Esempio.2019'
Risposta analizzata: ('\\HasNoChildren', '.', 'INBOX.Esempio.2019')
Risposta del server: b'(\\HasNoChildren \\Archive) "." INBOX.Archive'
Risposta analizzata: ('\\HasNoChildren \\Archive', '.', 'INBOX.Archive')

list() riceve argomenti per specificare caselle in parti della gerarchia, Ad esempio per elencare le sottocartelle di Esempio si passi "INBOX.Esempio" come argomento di directory.

# imaplib_list_subfolders.py

import imaplib

from imaplib_connect import open_connection

with open_connection() as c:
    typ, data = c.list(directory='INBOX.Esempio')

print('Codice di risposta:', typ)

for line in data:
    print('Risposta del server:', line)

Vengono ritornate le cartelle genitore e figlie.

$ python3 imaplib_list_subfolders.py

Codice di risposta: OK
Risposta del server: b'(\\HasChildren) "." INBOX.Esempio'
Risposta del server: b'(\\HasNoChildren) "." INBOX.Esempio.2019'

In alternativa, per elencare cartelle che corrispondono a un modello, si passi un modello all'argomento pattern.

# imaplib_list_pattern.py

import imaplib

from imaplib_connect import open_connection

with open_connection() as c:
    typ, data = c.list(pattern='*Esempio*')

print('Codice risposta:', typ)

for line in data:
    print('Risposta del server:', line)

In questo caso, sia INBOX.Esempio che INBOX.Esempio.2019 sono incluse nella risposta.

$ python3 imaplib_list_pattern.py

Codice di risposta: OK
Risposta del server: b'(\\HasChildren) "." INBOX.Esempio'
Risposta del server: b'(\\HasNoChildren) "." INBOX.Esempio.2019'

Stato della Casella di Posta

Si usi status() per richiedere informazioni aggregate circa i contenuti. La tabella di seguito elenca le condizioni di stato definite dallo standard.

CONDIZIONE SIGNIFICATO
MESSAGES Il numero di messaggi nella casella di posta
RECENT Il numero di messaggi con il segnalatore \Recent impostato
UIDNEXT Il valore del prossimo identificatore univoco della casella di posta
UIDVALIDITY Il valore di validità dell'identificatore univoco della casella di posta
UNSEEN Il numero di messaggi che non hanno il segnalatore \Seen impostato

Le condizioni di stato devono essere formattate come una stringa separata da spazio, racchiusa tra parentesi, la codifica per una "lista" nelle specifiche IMAP4. Il nome della casella di posta è racchiuso tra " qualora il nome includa spazi o altri caratteri che potrebbero disorientare l'elaboratore.

# imaplib_status.py

import imaplib
import re

from imaplib_connect import open_connection
from imaplib_list_parse import parse_list_response


with open_connection() as c:
    typ, data = c.list()
    for line in data:
        flags, delimiter, mailbox = parse_list_response(line)
        print('Casella di posta', mailbox)
        status = c.status(
            '"{}"'.format(mailbox),
            '(MESSAGES RECENT UIDNEXT UIDVALIDITY UNSEEN)'
        )
        print(status)

Il valore di ritorno è la solita tuple che contiene il codice di risposta e una lista di informazioni dal server. In questo caso contiene una singola stringa formattata con il nome della casella tra apici (se necessario), poi le condizioni di stato e i valori fra parentesi.

$ python3 imaplib_status.py

Codice di risposta: OK
Risposta del server: b'(\\HasChildren) "." INBOX'
Risposta analizzata: ('\\HasChildren', '.', 'INBOX')
Risposta del server: b'(\\HasNoChildren) "." "INBOX.Messaggi Eliminati"'
Risposta analizzata: ('\\HasNoChildren', '.', 'INBOX.Messaggi Eliminati')
Risposta del server: b'(\\HasChildren) "." INBOX.Esempio'
Risposta analizzata: ('\\HasChildren', '.', 'INBOX.Esempio')
Risposta del server: b'(\\HasNoChildren) "." INBOX.Esempio.2019'
Risposta analizzata: ('\\HasNoChildren', '.', 'INBOX.Esempio.2019')
Risposta del server: b'(\\HasNoChildren \\Archive) "." INBOX.Archive'
Risposta analizzata: ('\\HasNoChildren \\Archive', '.', 'INBOX.Archive')
Casella di posta INBOX
('OK', [b'INBOX (MESSAGES 1 RECENT 0 UIDNEXT 4 UIDVALIDITY 1556044852 UNSEEN 1)'])
Casella di posta INBOX.Drafts
('OK', [b'"INBOX.Messaggi Eliminati" (MESSAGES 0 RECENT 0 UIDNEXT 1 UIDVALIDITY 1556044859 UNSEEN 0)'])
Casella di posta INBOX.Esempio
('OK', [b'INBOX.Esempio (MESSAGES 0 RECENT 0 UIDNEXT 1 UIDVALIDITY 1556044855 UNSEEN 0)'])
Casella di posta INBOX.Esempio.2019
('OK', [b'INBOX.Esempio.2019 (MESSAGES 1 RECENT 0 UIDNEXT 2 UIDVALIDITY 1556044856 UNSEEN 0)'])
Casella di posta INBOX.Archive
('OK', [b'INBOX.Archive (MESSAGES 0 RECENT 0 UIDNEXT 1 UIDVALIDITY 1556044854 UNSEEN 0)'])

Selezionare una Casella di Posta

Il modo base per operare, una volta che il client è autenticato, è di selezionare una casella, quindi interrogare il server circa i messaggi nella casella. La connessione è stateful (per stateful si intende un programma che ricordi eventi o interazioni precedenti - note come stato del sistema - n.d.t.); quindi dopo che viene selezionata una casella, tutti i comandi opereranno su quella casella fino a quando non viene selezionata una nuova casella.

# imaplib_select.py

import imaplib
import imaplib_connect

with imaplib_connect.open_connection() as c:
    typ, data = c.select('INBOX')
    print(typ, data)
    num_msgs = int(data[0])
    print('Ci sono {} messaggi in INBOX'.format(num_msgs))

Il dato in risposta contiene il numero di messaggi nella casella.

$ python3 imaplib_select.py

OK [b'1']
Ci sono 1 messaggi in INBOX

Se viene specificata una casella non valida, il codice di risposta è NO.

# imaplib_select_invalid.py

import imaplib
import imaplib_connect

with imaplib_connect.open_connection() as c:
    typ, data = c.select('Non Esiste')
    print(typ, data)

Il dato ritornato contiene un messaggio di errore che descrive il problema.

$ python3 imaplib_select_invalid.py

NO [b'Client tried to access nonexistent namespace. (Mailbox name should probably be prefixed with: INBOX.) (0.000 + 0.000 secs).']

Cercare Messaggi

Dopo aver selezionato la casella di posta, si usi search() per recuperare gli identificativi dei messaggi nella casella.

# imaplib_search_all.py

import imaplib
import imaplib_connect
from imaplib_list_parse import parse_list_response

with imaplib_connect.open_connection() as c:
    typ, mailbox_data = c.list()
    for line in mailbox_data:
        flags, delimiter, mbox_name = parse_list_response(line)
        c.select('"{}"'.format(mbox_name), readonly=True)
        typ, msg_ids = c.search(None, 'ALL')
        print(mbox_name, typ, msg_ids)

Gli identificativi dei messaggi sono assegnati dal server, e dipendono dall'implementazione. Il protocollo IMAP4 fa una distinzione tra identificativi sequenziali per i messaggi a un dato punto nel tempo durante una transazione e identificativi UID per i messaggi, ma non tutti i server li implementano entrambi.

$ python3 imaplib_search_all.py

Codice di risposta: OK
Risposta del server: b'(\\HasChildren) "." INBOX'
Risposta analizzata: ('\\HasChildren', '.', 'INBOX')
Risposta del server: b'(\\HasNoChildren) "." "INBOX.Messaggi Eliminati"'
Risposta analizzata: ('\\HasNoChildren', '.', 'INBOX.Messaggi Eliminati')
Risposta del server: b'(\\HasChildren) "." INBOX.Esempio'
Risposta analizzata: ('\\HasChildren', '.', 'INBOX.Esempio')
Risposta del server: b'(\\HasNoChildren) "." INBOX.Esempio.2019'
Risposta analizzata: ('\\HasNoChildren', '.', 'INBOX.Esempio.2019')
Risposta del server: b'(\\HasNoChildren \\Archive) "." INBOX.Archive'
Risposta analizzata: ('\\HasNoChildren \\Archive', '.', 'INBOX.Archive')

INBOX OK [b'1']
INBOX.Messaggi Eliminati OK [b'']
INBOX.Esempio OK [b'']
INBOX.Esempio.2019 OK [b'1']
INBOX.Archive OK [b'']

In questo caso INBOX e INBOX.Esempio.2019 hanno ciascuno un messaggio differente con identificativo 1. Le altre caselle sono vuote.

Criteri di Ricerca

E' possibile usare una varietà di criteri di ricerca, inclusi la data dei messaggi, segnalatori e altre intestazioni. Si faccia riferimento alla sezione 6.4.4 di RFC 3501 per i dettagli completi.

Per trovare messaggi che contengono Pymotw3-it imaplib nell'oggetto, il criterio di ricerca verrebbe così costruito:

(SUBJECT "Pymotw3-it imaplib")

Questo esempio cerca tutti i messaggi con la stringa sopra specificata su tutte le caselle.

# imaplib_search_subject.py

import imaplib
import imaplib_connect
from imaplib_list_parse import parse_list_response

with imaplib_connect.open_connection() as c:
    typ, mailbox_data = c.list()
    for line in mailbox_data:
        flags, delimiter, mbox_name = parse_list_response(line)
        c.select('"{}"'.format(mbox_name), readonly=True)
        typ, msg_ids = c.search(
            None,
            '(SUBJECT "Pymotw3-it imaplib")'
        )
        print(mbox_name, typ, msg_ids)

C'è un solo messaggio con questa stringa nell'utenza, e si trova in INBOX.

$ python3 imaplib_search_subject.py

Codice di risposta: OK
Risposta del server: b'(\\HasChildren) "." INBOX'
Risposta analizzata: ('\\HasChildren', '.', 'INBOX')
Risposta del server: b'(\\HasNoChildren) "." "INBOX.Messaggi Eliminati"'
Risposta analizzata: ('\\HasNoChildren', '.', 'INBOX.Messaggi Eliminati')
Risposta del server: b'(\\HasChildren) "." INBOX.Esempio'
Risposta analizzata: ('\\HasChildren', '.', 'INBOX.Esempio')
Risposta del server: b'(\\HasNoChildren) "." INBOX.Esempio.2019'
Risposta analizzata: ('\\HasNoChildren', '.', 'INBOX.Esempio.2019')
Risposta del server: b'(\\HasNoChildren \\Archive) "." INBOX.Archive'
Risposta analizzata: ('\\HasNoChildren \\Archive', '.', 'INBOX.Archive')

INBOX OK [b'1']
INBOX.Messaggi Eliminati OK [b'']
INBOX.Esempio OK [b'']
INBOX.Esempio.2019 OK [b'']
INBOX.Archive OK [b'']

I criteri di ricerca possono anche essere combinati.

# imaplib_search_from.py

import imaplib
import imaplib_connect
from imaplib_list_parse import parse_list_response

with imaplib_connect.open_connection() as c:
    typ, mailbox_data = c.list()
    for line in mailbox_data:
        flags, delimiter, mbox_name = parse_list_response(line)
        c.select('"{}"'.format(mbox_name), readonly=True)
        typ, msg_ids = c.search(
            None,
            '(FROM "Roberto" SUBJECT "Pymotw3-it imaplib")'
        )
        print(mbox_name, typ, msg_ids)

I criteri sono combinati con operatori logici e aritmetici.

$ python3 imaplib_search_from.py


Codice di risposta: OK
Risposta del server: b'(\\HasChildren) "." INBOX'
Risposta analizzata: ('\\HasChildren', '.', 'INBOX')
Risposta del server: b'(\\HasNoChildren) "." "INBOX.Messaggi Eliminati"'
Risposta analizzata: ('\\HasNoChildren', '.', 'INBOX.Messaggi Eliminati')
Risposta del server: b'(\\HasChildren) "." INBOX.Esempio'
Risposta analizzata: ('\\HasChildren', '.', 'INBOX.Esempio')
Risposta del server: b'(\\HasNoChildren) "." INBOX.Esempio.2019'
Risposta analizzata: ('\\HasNoChildren', '.', 'INBOX.Esempio.2019')
Risposta del server: b'(\\HasNoChildren \\Archive) "." INBOX.Archive'
Risposta analizzata: ('\\HasNoChildren \\Archive', '.', 'INBOX.Archive')

INBOX OK [b'1']
INBOX.Messaggi Eliminati OK [b'']
INBOX.Esempio OK [b'']
INBOX.Esempio.2019 OK [b'']
INBOX.Archive OK [b'']

Recuperare Messaggi

Gli identificativi ritornati da search() sono usati per recuperare il contenuto dei messaggi, intero o parziale, per successive elaborazioni tramite il metodo fetch(). Esso riceve due argomenti, l'identificativo del messaggio e la/le porzioni di messaggio da recuperare.

L'argomento message_ids è una lista di identificativi separati da virgola (es. "1", "1,2") o intervalli di identificativi (es. 1:2). L'argomento message_parts è una lista IMAP di nomi di segmenti di messaggio. Così come per i criteri di ricerca per search(), il protocollo IMAP specifica nomi di segmenti di messaggio in modo che i client possano recuperare con efficienza solo le parti delle quali necessitano. Ad esempio per recuperare le intestazioni dei messaggi in una casella, si usi fetch() con argomento BODY.PEEK[HEADER].

Un altro modo per recuperare le intestaioni è BODY[HEADERS], ma quella forma ha l'effetto collaterale di marcare implicitamente il messaggio come letto, il che nella maggior parte dei casi non è consigliabile.
# imaplib_fetch_raw.py

import imaplib
import pprint
import imaplib_connect

imaplib.Debug = 4
with imaplib_connect.open_connection() as c:
    c.select('INBOX', readonly=True)
    typ, msg_data = c.fetch('1', '(BODY.PEEK[HEADER] FLAGS)')
    pprint.pprint(msg_data)

Il valore di ritorno di FETCH è stato parzialmente analizzato, quindi è talvolta più complicato lavorarci rispetto al valore di ritorno da list(). Attivando il debug, si ottengono le interazioni complete tra il client e il server per ben comprenderne il meccanismo.

$ python3 imaplib_fetch_raw.py

 07:12.39 imaplib version 2.58
  07:12.39 new IMAP4 connection, tag=b'BIFJ'
  07:12.55 < b'* OK [CAPABILITY IMAP4rev1 LITERAL+ SASL-IR LOGIN-REFERRALS ID ENABLE IDLE NAMESPACE AUTH=PLAIN AUTH=LOGIN] Dovecot ready.'
  07:12.55 > b'BIFJ0 CAPABILITY'
  07:12.68 < b'* CAPABILITY IMAP4rev1 LITERAL+ SASL-IR LOGIN-REFERRALS ID ENABLE IDLE NAMESPACE AUTH=PLAIN AUTH=LOGIN'
  07:12.68 < b'BIFJ0 OK Pre-login capabilities listed, post-login capabilities have more.'
  07:12.68 CAPABILITIES: ('IMAP4REV1', 'LITERAL+', 'SASL-IR', 'LOGIN-REFERRALS', 'ID', 'ENABLE', 'IDLE', 'NAMESPACE', 'AUTH=PLAIN', 'AUTH=LOGIN')

  07:12.68 > b'BIFJ1 LOGIN test@robyp.x10host.com "C60cP26vS8tl"'
  07:13.22 < b'* CAPABILITY IMAP4rev1 LITERAL+ SASL-IR LOGIN-REFERRALS ID ENABLE IDLE SORT SORT=DISPLAY THREAD=REFERENCES THREAD=REFS THREAD=ORDEREDSUBJECT MULTIAPPEND URL-PARTIAL CATENATE UNSELECT CHILDREN NAMESPACE UIDPLUS LIST-EXTENDED I18NLEVEL=1 CONDSTORE QRESYNC ESEARCH ESORT SEARCHRES WITHIN CONTEXT=SEARCH LIST-STATUS BINARY MOVE NAMESPACE SPECIAL-USE COMPRESS=DEFLATE QUOTA'
  07:13.22 < b'BIFJ1 OK Logged in'
  07:13.22 > b'BIFJ2 EXAMINE INBOX'
  07:13.35 < b'* FLAGS (\\Answered \\Flagged \\Deleted \\Seen \\Draft)'
  07:13.35 < b'* OK [PERMANENTFLAGS ()] Read-only mailbox.'
  07:13.35 < b'* 1 EXISTS'
  07:13.35 < b'* 0 RECENT'
  07:13.35 < b'* OK [UNSEEN 1] First unseen.'
  07:13.35 < b'* OK [UIDVALIDITY 1556044852] UIDs valid'
  07:13.35 < b'* OK [UIDNEXT 4] Predicted next UID'
  07:13.35 < b'* OK [HIGHESTMODSEQ 8] Highest'
  07:13.35 < b'BIFJ2 OK [READ-ONLY] Examine completed (0.000 + 0.000 secs).'
  07:13.35 > b'BIFJ3 FETCH 1 (BODY.PEEK[HEADER] FLAGS)'
  07:13.81 < b'* 1 FETCH (FLAGS () BODY[HEADER] {3410}'
  07:13.81 read literal size 3410
  07:14.44 < b')'
  07:14.44 < b'BIFJ3 OK Fetch completed (0.003 + 0.000 secs).'
[(b'1 (FLAGS () BODY[HEADER] {3410}',
  b'Return-path: <mariorossi@mail.com>\r\nEnvelope-to: test@robyp.x10'
  b'host.com\r\nDelivery-date: Wed, 24 Apr 2019 14:21:12 -0400\r\nReceived: '
  b'from out2-smtp.messagingengine.com ([66.111.4.26]:38999)\r\n\tby xo2.x10hos'
  b'ting.com with esmtps (TLSv1.2:ECDHE-RSA-AES256-GCM-SHA384:256)\r\n\t(Exim 4'
  b'.87)\r\n\t(envelope-from <mariorossi@mail.com>)\r\n\tid 1hJMW8-00'
  b'5k3G-3K\r\n\tfor test@robyp.x10host.com; Wed, 24 Apr 2019 14:21:12 -040'
  b'0\r\nReceived: from compute5.internal (compute5.nyi.internal [10.202.2.45]'
  b')\r\n\tby mailout.nyi.internal (Postfix) with ESMTP id BAC7322237\r\n\tfor'
  b' <test@robyp.x10host.com>; Wed, 24 Apr 2019 14:20:31 -0400 (EDT)\r\nReceiv'
  b'ed: from imap22 ([10.202.2.72])\r\n  by compute5.internal (MEProxy); Wed, '
  b'24 Apr 2019 14:20:31 -0400\r\nDKIM-Signature: v=1; a=rsa-sha256; c=relaxed'
  b'/relaxed; d=fastmail.com; h=\r\n\tmime-version:message-id:date:from:to:subj'
  b'ect:content-type; s=\r\n\tfm2; bh=pa7QRViXlYKtXmb3+UUJsRWyS+BSAva6XV12N1KHx'
  b'V0=; b=WrQCZqyi\r\n\t0NseMjoERiLA09jt+N+J+gEQoNcECsTcrldGrxEk/006XhA9J1f3Mh'
  b'VqYOqsPeyE\r\n\tTbS6tqQ5yn2leezBM+CTk8MorSIb2QysUHUYMYFX7g9SybP7L/RzF5e+3U+'
  b'NZlUP\r\n\tbOobjIGZJoDFD3xWxxOc8F+BX0cxEn8OHAmnaoSYGTmRkP4woewF9fTYT31hY7fd'
  b'\r\n\t8pES7DWlMMxW3NT/5FYXIpukIbeRJucUyBEFAt6Q3elJgr+C6jMdc7EMKB0MdRvs\r'
  b'\n\t/dis9aGulbzxgJfM72tovDxXu9fnr8i4eUKmg2XCgFL+UVm3n6CFaKkjpaGvtg8z\r\n'
  b'\tC6SJvxzYg4JgLw==\r\nDKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;'
  b' d=\r\n\tmessagingengine.com; h=content-type:date:from:message-id\r\n\t:mi'
  b'me-version:subject:to:x-me-proxy:x-me-proxy:x-me-sender\r\n\t:x-me-sender:x'
  b'-sasl-enc; s=fm2; bh=pa7QRViXlYKtXmb3+UUJsRWyS+BSA\r\n\tva6XV12N1KHxV0=; b='
  b'iPO92nsu+2d0Jz2jHET+68xSaTLKi8WPklRNEi4iuwuJ3\r\n\tiGlc7v33TyxDl4zmz6quFw1K'
  b'W5QqfSooW5jSlskKkc+JeW11H2S2lw7OqK44CId0\r\n\tZidN0eJOf70jNovyypiwXRA4564jS'
  b'meyLARV4mlx42gc6GNTXqd5zrJTf3X1c6A8\r\n\tfXUdosbMKxmGEIK9HK/79zxOcOD7l2/cNn'
  b'3RBsOMF7lzWLDmbTMKwAhmXrVBkNdf\r\n\t2RkKqU8jc46wxaFFfrN3/ORhxYvetoQHR9B6eD6'
  b'azOb5gwYfwRQ7o5B5NK4JEyA5\r\n\tZx1KuQQnvxC6mplPs6Pg4+C3yiwS8OEi1sWiusFI'
  b'w==\r\nX-ME-Sender: <xms:76jAXHI03CAT8nZWkpTPUITivXhym1H0GufCd4PfgQpCgKqEH'
  b'RYL_g>\r\nX-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeduuddrhedvgddule'
  b'cutefuodetggdotefrodftvf\r\n    curfhrohhfihhlvgemucfhrghsthforghilhdpqfgf'
  b'vfdpuffrtefokffrpgfnqfghnecu\r\n    uegrihhlohhuthemuceftddtnecunecujfgurh'
  b'epofgfggfkfffhvffutgesrgdtreerre\r\n    ertdenucfhrhhomhepfdftohgsvghrthho'
  b'ucfrrghulhgvthhtohdfuceorhhosggvrhht\r\n    ohhprghulhgvthhtohesfhgrshhtmh'
  b'grihhlrdgtohhmqeenucfrrghrrghmpehmrghilh\r\n    hfrhhomheprhhosggvrhhtohhp'
  b'rghulhgvthhtohesfhgrshhtmhgrihhlrdgtohhmnecu\r\n    vehluhhsthgvrhfuihiivg'
  b'epud\r\nX-ME-Proxy: <xmx:76jAXLK864WUuZGpCbgBySK0Z43ydkjA2jwIPUP4Wc_hW2LVv'
  b'QR_eQ>\r\n    <xmx:76jAXF3iKiqIXOtr6e2argbf1CXf17uC03aAi9L_iDOpXNqJjLTWqg>'
  b'\r\n    <xmx:76jAXPHKddJtLlc_TeabTAiLsIKsclqGzlc_2wvZEVtcrv91LEv2Ng>\r\n'
  b'    <xmx:76jAXA8GlJ27VmxEk4vJFkW1JruET4Q0wex5DnclTB_WzObYjcxfGQ>\r\nReceiv'
  b'ed: by mailuser.nyi.internal (Postfix, from userid 501)\r\n\tid 5A6EAE80F; '
  b'Wed, 24 Apr 2019 14:20:31 -0400 (EDT)\r\nX-Mailer: MessagingEngine.com Web'
  b'mail Interface\r\nUser-Agent: Cyrus-JMAP/3.1.6-444-g755619f-fmstable-20190'
  b'423v1\r\nMime-Version: 1.0\r\nMessage-Id: <8a8a24db-06b8-49a5-9974-40d1b'
  b'd0f5b1b@www.fastmail.com>\r\nDate: Wed, 24 Apr 2019 14:20:31 -0400\r\nFr'
  b'om: "Roberto Pauletto" <mariorossi@mail.com>\r\nTo: test@robyp.x1'
  b'0host.com\r\nSubject: Pymotw3-it Imaplib\r\nContent-Type: multipart/alte'
  b'rnative;\r\n boundary=14c7d7383ef34ccd976a3caefb445a41\r\nX-Spam-Status:'
  b' No, score=\r\nX-Spam-Score: \r\nX-Spam-Bar: \r\nX-Ham-Report: \r\nX-Spa'
  b'm-Flag: NO\r\n\r\n'),
 b')']
  07:14.44 > b'BIFJ4 LOGOUT'
  07:14.56 < b'* BYE Logging out'
  07:14.56 BYE response: b'Logging out'

La risposta per il comando FETCH inizia con i segnalatori, quindi indica la dimensione di dati di intestazione. Il client costruisce una tupla con la risposta per il messaggio, quindi chiude la sequenza con una singola stringa che contiene la parentesi chiusa "')'" che il server invia alla fine della risposta recuperata. A causa della formattazione, potrebbe essere più semplice recuperare le diverse parti di informazione separatamente, oppure ricombinare la risposta ed elaborarla verso il client.

# imaplib_fetch_separately.py

import imaplib
import pprint
import imaplib_connect

with imaplib_connect.open_connection() as c:
    c.select('INBOX', readonly=True)

    print('INTESTAZIONE:')
    typ, msg_data = c.fetch('1', '(BODY.PEEK[HEADER])')
    for response_part in msg_data:
        if isinstance(response_part, tuple):
            print(response_part[1])

    print('\nTESTO NEL CORPO:')
    typ, msg_data = c.fetch('1', '(BODY.PEEK[TEXT])')
    for response_part in msg_data:
        if isinstance(response_part, tuple):
            print(response_part[1])

    print('\nSEGNALATORI:')
    typ, msg_data = c.fetch('1', '(FLAGS)')
    for response_part in msg_data:
        print(response_part)
        print(imaplib.ParseFlags(response_part))

Con il recupero dei valori separati si ottiene il beneficio di facilitare l'uso di ParseFlags() per elaborare i segnalatori dalla risposta.

$ python3 imaplib_fetch_separately.py

INTESTAZIONE:
b'Return-path: <mariorossi@mail.com>\r\nEnvelope-to: test@robyp.x10host.com\r\nDelivery-date: Wed, 24 Apr 2019 14:21:12 -0400\r\nReceived: from out2-smtp.messagingengine.com ([66.111.4.26]:38999)\r\n\tby xo2.x10hosting.com with esmtps (TLSv1.2:ECDHE-RSA-AES256-GCM-SHA384:256)\r\n\t(Exim 4.87)\r\n\t(envelope-from <mariorossi@mail.com>)\r\n\tid 1hJMW8-005k3G-3K\r\n\tfor test@robyp.x10host.com; Wed, 24 Apr 2019 14:21:12 -0400\r\nReceived: from compute5.internal (compute5.nyi.internal [10.202.2.45])\r\n\tby mailout.nyi.internal (Postfix) with ESMTP id BAC7322237\r\n\tfor <test@robyp.x10host.com>; Wed, 24 Apr 2019 14:20:31 -0400 (EDT)\r\nReceived: from imap22 ([10.202.2.72])\r\n  by compute5.internal (MEProxy); Wed, 24 Apr 2019 14:20:31 -0400\r\nDKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=fastmail.com; h=\r\n\tmime-version:message-id:date:from:to:subject:content-type; s=\r\n\tfm2; bh=pa7QRViXlYKtXmb3+UUJsRWyS+BSAva6XV12N1KHxV0=; b=WrQCZqyi\r\n\t0NseMjoERiLA09jt+N+J+gEQoNcECsTcrldGrxEk/006XhA9J1f3MhVqYOqsPeyE\r\n\tTbS6tqQ5yn2leezBM+CTk8MorSIb2QysUHUYMYFX7g9SybP7L/RzF5e+3U+NZlUP\r\n\tbOobjIGZJoDFD3xWxxOc8F+BX0cxEn8OHAmnaoSYGTmRkP4woewF9fTYT31hY7fd\r\n\t8pES7DWlMMxW3NT/5FYXIpukIbeRJucUyBEFAt6Q3elJgr+C6jMdc7EMKB0MdRvs\r\n\t/dis9aGulbzxgJfM72tovDxXu9fnr8i4eUKmg2XCgFL+UVm3n6CFaKkjpaGvtg8z\r\n\tC6SJvxzYg4JgLw==\r\nDKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=\r\n\tmessagingengine.com; h=content-type:date:from:message-id\r\n\t:mime-version:subject:to:x-me-proxy:x-me-proxy:x-me-sender\r\n\t:x-me-sender:x-sasl-enc; s=fm2; bh=pa7QRViXlYKtXmb3+UUJsRWyS+BSA\r\n\tva6XV12N1KHxV0=; b=iPO92nsu+2d0Jz2jHET+68xSaTLKi8WPklRNEi4iuwuJ3\r\n\tiGlc7v33TyxDl4zmz6quFw1KW5QqfSooW5jSlskKkc+JeW11H2S2lw7OqK44CId0\r\n\tZidN0eJOf70jNovyypiwXRA4564jSmeyLARV4mlx42gc6GNTXqd5zrJTf3X1c6A8\r\n\tfXUdosbMKxmGEIK9HK/79zxOcOD7l2/cNn3RBsOMF7lzWLDmbTMKwAhmXrVBkNdf\r\n\t2RkKqU8jc46wxaFFfrN3/ORhxYvetoQHR9B6eD6azOb5gwYfwRQ7o5B5NK4JEyA5\r\n\tZx1KuQQnvxC6mplPs6Pg4+C3yiwS8OEi1sWiusFIw==\r\nX-ME-Sender: <xms:76jAXHI03CAT8nZWkpTPUITivXhym1H0GufCd4PfgQpCgKqEHRYL_g>\r\nX-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeduuddrhedvgddulecutefuodetggdotefrodftvf\r\n    curfhrohhfihhlvgemucfhrghsthforghilhdpqfgfvfdpuffrtefokffrpgfnqfghnecu\r\n    uegrihhlohhuthemuceftddtnecunecujfgurhepofgfggfkfffhvffutgesrgdtreerre\r\n    ertdenucfhrhhomhepfdftohgsvghrthhoucfrrghulhgvthhtohdfuceorhhosggvrhht\r\n    ohhprghulhgvthhtohesfhgrshhtmhgrihhlrdgtohhmqeenucfrrghrrghmpehmrghilh\r\n    hfrhhomheprhhosggvrhhtohhprghulhgvthhtohesfhgrshhtmhgrihhlrdgtohhmnecu\r\n    vehluhhsthgvrhfuihiivgepud\r\nX-ME-Proxy: <xmx:76jAXLK864WUuZGpCbgBySK0Z43ydkjA2jwIPUP4Wc_hW2LVvQR_eQ>\r\n    <xmx:76jAXF3iKiqIXOtr6e2argbf1CXf17uC03aAi9L_iDOpXNqJjLTWqg>\r\n    <xmx:76jAXPHKddJtLlc_TeabTAiLsIKsclqGzlc_2wvZEVtcrv91LEv2Ng>\r\n    <xmx:76jAXA8GlJ27VmxEk4vJFkW1JruET4Q0wex5DnclTB_WzObYjcxfGQ>\r\nReceived: by mailuser.nyi.internal (Postfix, from userid 501)\r\n\tid 5A6EAE80F; Wed, 24 Apr 2019 14:20:31 -0400 (EDT)\r\nX-Mailer: MessagingEngine.com Webmail Interface\r\nUser-Agent: Cyrus-JMAP/3.1.6-444-g755619f-fmstable-20190423v1\r\nMime-Version: 1.0\r\nMessage-Id: <8a8a24db-06b8-49a5-9974-40d1bd0f5b1b@www.fastmail.com>\r\nDate: Wed, 24 Apr 2019 14:20:31 -0400\r\nFrom: "Roberto Pauletto" <mariorossi@mail.com>\r\nTo: test@robyp.x10host.com\r\nSubject: Pymotw3-it Imaplib\r\nContent-Type: multipart/alternative;\r\n boundary=14c7d7383ef34ccd976a3caefb445a41\r\nX-Spam-Status: No, score=\r\nX-Spam-Score: \r\nX-Spam-Bar: \r\nX-Ham-Report: \r\nX-Spam-Flag: NO\r\n\r\n'

TESTO NEL CORPO:
b'--14c7d7383ef34ccd976a3caefb445a41\r\nContent-Type: text/plain\r\n\r\nUn messaggio dimostrativo per l\'articolo sul modulo imaplib\r\n\r\n\r\n\r\n--14c7d7383ef34ccd976a3caefb445a41\r\nContent-Type: text/html\r\n\r\n<!DOCTYPE html><html><head><title></title><style type="text/css">p.MsoNormal,p.MsoNoSpacing{margin:0}</style></head><body><div>Un messaggio dimostrativo per l\'articolo sul modulo imaplib</div><div><br></div><div id="sig58218369"><div class="signature"><br></div></div><div><br></div></body></html>\r\n--14c7d7383ef34ccd976a3caefb445a41--\r\n'

SEGNALATORI:
b'1 (FLAGS ())'
()

Messaggi Completi

Come illustrato in precedenza, il client può chiedere al server singole parti di messaggio separatamente. E' anche possibile recuperare l'intero messaggio formattato secondo le specifiche RFC 822 ed elaborarlo con le classi del modulo email.

# imaplib_fetch_rfc822.py

import imaplib
import email
import email.parser

import imaplib_connect

with imaplib_connect.open_connection() as c:
    c.select('INBOX', readonly=True)

    typ, msg_data = c.fetch('1', '(RFC822)')
    for response_part in msg_data:
        if isinstance(response_part, tuple):
            email_parser = email.parser.BytesFeedParser()
            email_parser.feed(response_part[1])
            msg = email_parser.close()
            for header in [ 'subject', 'to', 'from']:
                print('{:^8}: {}'.format(header.upper(), msg[header]))

Nel modulo email l'elaboratore rende molto facile l'accesso e la manipolazione dei messaggi. Questo esempio stampa solo alcune intestazioni per ogni messaggio.

$ python3 imaplib_fetch_rfc822.py

SUBJECT : Pymotw3-it Imaplib
   TO   : test@robyp.x10host.com
  FROM  : "Xxxxxx Yxxxxx" <mariorossi@mail.com>

Caricare Messaggi

Per aggiungere un nuovo messaggio a una casella di posta si costruisca una istanza di Message e la si passi al metodo append(), assieme alla marca temporale per il messaggio.

# imaplib_append.py

import imaplib
import time
import email.message
import imaplib_connect

new_message = email.message.Message()
new_message.set_unixfrom('pymotw3-ot')
new_message['Subject'] = 'Oggetto va qui'
new_message['From'] = 'pymotw3@esempio.com'
new_message['To'] = 'esempio@esempio.com'
new_message.set_payload('Il corpo del messaggio.\n')

print(new_message)

with imaplib_connect.open_connection() as c:
    c.append('INBOX', '',
             imaplib.Time2Internaldate(time.time()),
             str(new_message).encode('utf-8'))

    # Mostra le intestazioni di tutti i messaggi della casella di osta
    c.select('INBOX')
    typ, [msg_ids] = c.search(None, 'ALL')
    for num in msg_ids.split():
        typ, msg_data = c.fetch(num, '(BODY.PEEK[HEADER])')
        for response_part in msg_data:
            if isinstance(response_part, tuple):
                print('\n{}:'.format(num))
                print(response_part[1])

I dati usati per questo esempio sono un semplice corpo di email in testo semplice. Message supporta anche messaggi multi parte codificati MIME.

$ python3 imaplib_append.py

Subject: Oggetto va qui
From: pymotw3@esempio.com
To: esempio@esempio.com

Il corpo del messaggio.


b'1':
b'Return-path: <mariorossi@gmail.com>\r\nEnvelope-to: test@robyp.x10host.com\r\nDelivery-date: Wed, 24 Apr 2019 14:21:12 -0400\r\nReceived: from out2-smtp.messagingengine.com ([66.111.4.26]:38999)\r\n\tby xo2.x10hosting.com with esmtps (TLSv1.2:ECDHE-RSA-AES256-GCM-SHA384:256)\r\n\t(Exim 4.87)\r\n\t(envelope-from <mariorossi@gmail.com>)\r\n\tid 1hJMW8-005k3G-3K\r\n\tfor test@robyp.x10host.com; Wed, 24 Apr 2019 14:21:12 -0400\r\nReceived: from compute5.internal (compute5.nyi.internal [10.202.2.45])\r\n\tby mailout.nyi.internal (Postfix) with ESMTP id BAC7322237\r\n\tfor <test@robyp.x10host.com>; Wed, 24 Apr 2019 14:20:31 -0400 (EDT)\r\nReceived: from imap22 ([10.202.2.72])\r\n  by compute5.internal (MEProxy); Wed, 24 Apr 2019 14:20:31 -0400\r\nDKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=fastmail.com; h=\r\n\tmime-version:message-id:date:from:to:subject:content-type; s=\r\n\tfm2; bh=pa7QRViXlYKtXmb3+UUJsRWyS+BSAva6XV12N1KHxV0=; b=WrQCZqyi\r\n\t0NseMjoERiLA09jt+N+J+gEQoNcECsTcrldGrxEk/006XhA9J1f3MhVqYOqsPeyE\r\n\tTbS6tqQ5yn2leezBM+CTk8MorSIb2QysUHUYMYFX7g9SybP7L/RzF5e+3U+NZlUP\r\n\tbOobjIGZJoDFD3xWxxOc8F+BX0cxEn8OHAmnaoSYGTmRkP4woewF9fTYT31hY7fd\r\n\t8pES7DWlMMxW3NT/5FYXIpukIbeRJucUyBEFAt6Q3elJgr+C6jMdc7EMKB0MdRvs\r\n\t/dis9aGulbzxgJfM72tovDxXu9fnr8i4eUKmg2XCgFL+UVm3n6CFaKkjpaGvtg8z\r\n\tC6SJvxzYg4JgLw==\r\nDKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=\r\n\tmessagingengine.com; h=content-type:date:from:message-id\r\n\t:mime-version:subject:to:x-me-proxy:x-me-proxy:x-me-sender\r\n\t:x-me-sender:x-sasl-enc; s=fm2; bh=pa7QRViXlYKtXmb3+UUJsRWyS+BSA\r\n\tva6XV12N1KHxV0=; b=iPO92nsu+2d0Jz2jHET+68xSaTLKi8WPklRNEi4iuwuJ3\r\n\tiGlc7v33TyxDl4zmz6quFw1KW5QqfSooW5jSlskKkc+JeW11H2S2lw7OqK44CId0\r\n\tZidN0eJOf70jNovyypiwXRA4564jSmeyLARV4mlx42gc6GNTXqd5zrJTf3X1c6A8\r\n\tfXUdosbMKxmGEIK9HK/79zxOcOD7l2/cNn3RBsOMF7lzWLDmbTMKwAhmXrVBkNdf\r\n\t2RkKqU8jc46wxaFFfrN3/ORhxYvetoQHR9B6eD6azOb5gwYfwRQ7o5B5NK4JEyA5\r\n\tZx1KuQQnvxC6mplPs6Pg4+C3yiwS8OEi1sWiusFIw==\r\nX-ME-Sender: <xms:76jAXHI03CAT8nZWkpTPUITivXhym1H0GufCd4PfgQpCgKqEHRYL_g>\r\nX-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgeduuddrhedvgddulecutefuodetggdotefrodftvf\r\n    curfhrohhfihhlvgemucfhrghsthforghilhdpqfgfvfdpuffrtefokffrpgfnqfghnecu\r\n    uegrihhlohhuthemuceftddtnecunecujfgurhepofgfggfkfffhvffutgesrgdtreerre\r\n    ertdenucfhrhhomhepfdftohgsvghrthhoucfrrghulhgvthhtohdfuceorhhosggvrhht\r\n    ohhprghulhgvthhtohesfhgrshhtmhgrihhlrdgtohhmqeenucfrrghrrghmpehmrghilh\r\n    hfrhhomheprhhosggvrhhtohhprghulhgvthhtohesfhgrshhtmhgrihhlrdgtohhmnecu\r\n    vehluhhsthgvrhfuihiivgepud\r\nX-ME-Proxy: <xmx:76jAXLK864WUuZGpCbgBySK0Z43ydkjA2jwIPUP4Wc_hW2LVvQR_eQ>\r\n    <xmx:76jAXF3iKiqIXOtr6e2argbf1CXf17uC03aAi9L_iDOpXNqJjLTWqg>\r\n    <xmx:76jAXPHKddJtLlc_TeabTAiLsIKsclqGzlc_2wvZEVtcrv91LEv2Ng>\r\n    <xmx:76jAXA8GlJ27VmxEk4vJFkW1JruET4Q0wex5DnclTB_WzObYjcxfGQ>\r\nReceived: by mailuser.nyi.internal (Postfix, from userid 501)\r\n\tid 5A6EAE80F; Wed, 24 Apr 2019 14:20:31 -0400 (EDT)\r\nX-Mailer: MessagingEngine.com Webmail Interface\r\nUser-Agent: Cyrus-JMAP/3.1.6-444-g755619f-fmstable-20190423v1\r\nMime-Version: 1.0\r\nMessage-Id: <8a8a24db-06b8-49a5-9974-40d1bd0f5b1b@www.fastmail.com>\r\nDate: Wed, 24 Apr 2019 14:20:31 -0400\r\nFrom: "Roberto Pauletto" <mariorossi@gmail.com>\r\nTo: test@robyp.x10host.com\r\nSubject: Pymotw3-it Imaplib\r\nContent-Type: multipart/alternative;\r\n boundary=14c7d7383ef34ccd976a3caefb445a41\r\nX-Spam-Status: No, score=\r\nX-Spam-Score: \r\nX-Spam-Bar: \r\nX-Ham-Report: \r\nX-Spam-Flag: NO\r\n\r\n'

b'2':
b'Subject: Oggetto va qui\r\nFrom: pymotw3@esempio.com\r\nTo: esempio@esempio.com\r\n\r\n'

Spostare e Copiare Messaggi

Una volta che il messaggio è su di un server, può essere spostato o copiato senza doverlo scaricare usando move() o copy(). Questi metodi operano su intervalli di codici identificativi, proprio come fa fetch().

# imaplib_archive_read.py

import imaplib
import imaplib_connect


with imaplib_connect.open_connection() as c:
    # Trova i messaggi visti (SEEN) in INBOX
    c.select('INBOX')
    typ, [response] = c.search(None, 'SEEN')
    if typ != 'OK':
        raise RuntimeError(response)
    msg_ids = ','.join(response.decode('utf-8').split(' '))

    # Crea una nuova casella, "INBOX.Esempio.Oggi"
    typ, create_response = c.create('INBOX.Esempio.Oggi')
    print('Creato INBOX.Esempio.Oggi:', create_response)

    # Copia i messaggi
    print('IN COPIA:', msg_ids)
    c.copy(msg_ids, 'INBOX.Esempio.Oggi')

    # Legge il risultato
    c.select('INBOX.Esempio.Oggi')
    typ, [response] = c.search(None, 'ALL')
    print('COPIATI:', response)

Questo script di esempio crea una nuova casella sotto INBOX.Esempio e copia in essa i messaggi letti da INBOX.

$ python3 imaplib_archive_read.py

Creato INBOX.Esempio.Oggi: [b'Create completed (0.000 + 0.000 secs).']
IN COPIA: 1,2
COPIATI: b'1 2'

L'esecuzione dello stesso script una seconda volta mostra l'importanza della verifica dei codici di ritorno. Invece che sollevare una eccezione, la chiamata a create() per creare una nuova casella riporta che la cartella esiste già.

$ python3 imaplib_archive_read.py

Creato INBOX.Esempio.Oggi: [b'[ALREADYEXISTS] Mailbox already exists (0.000 + 0.000 secs).']
IN COPIA: 1,2
COPIATI: b'1 2'

Cancellare Messaggi

Anche se molti client mail moderni usano un modello che prevede una cartella "Cestino" per lavorare con i messaggi eliminati, i messaggi in genere non sono spostati in una effettiva cartella. Invece i loro segnalatori sono aggiornati aggiungendo \Deleted. L'operazione dello "svuotamento" del cestino viene implementata tramite il comando EXPUNGE. Questo script di esempio trova i messaggi archiviati con oggetto "Lorem ipsum", imposta il segnalatore di cancellazione, poi dimostra che quei messaggi sono comunque presenti nella cartella interrogando nuovamente il server.

# imaplib_delete_messages.py

import imaplib
import imaplib_connect
from imaplib_list_parse import parse_list_response

with imaplib_connect.open_connection() as c:
    c.select('INBOX.Esempio.Oggi')

    # Che identificativi ci sono nella casella?
    typ, [msg_ids] = c.search(None, 'ALL')
    print('Messaggi di partenza:', msg_ids)

    # Trova i(l) messaggi(o)
    typ, [msg_ids] = c.search(
        None,
        '(SUBJECT "oggetto va qui")',
    )
    msg_ids = ','.join(msg_ids.decode('utf-8').split(' '))
    print('Messaggi corrispondenti:', msg_ids)

    # Quali sono i segnalatori correnti?
    typ, response = c.fetch(msg_ids, '(FLAGS)')
    print('Segnalatori prima:', response)

    # Modifica il segnalatore Delete
    typ, response = c.store(msg_ids, '+FLAGS', r'(\Deleted)')

    # Quali sono i segnalatori adesso?
    typ, response = c.fetch(msg_ids, '(FLAGS)')
    print('Segnalatori dopo:', response)

    # Really delete the message.
    typ, response = c.expunge()
    print('Cancellati definitivamente:', response)

    # Che identificativi sono rimasti  nella casella?
    typ, [msg_ids] = c.search(None, 'ALL')
    print('Messaggi rimasti:', msg_ids)

La chiamata esplicita di Expunge rimuove i messaggi, ma chiamando close() si ottiene lo stesso effetto. La differenza è che il client non viene notificato delle cancellazioni quando viene chiamato close().

$ python3 imaplib_delete_messages.py

Codice di risposta: OK
Risposta del server: b'(\\HasChildren) "." INBOX'
Risposta analizzata: ('\\HasChildren', '.', 'INBOX')
Risposta del server: b'(\\HasChildren) "." INBOX.Esempio'
Risposta analizzata: ('\\HasChildren', '.', 'INBOX.Esempio')
Risposta del server: b'(\\HasNoChildren) "." INBOX.Esempio.Oggi'
Risposta analizzata: ('\\HasNoChildren', '.', 'INBOX.Esempio.Oggi')
Risposta del server: b'(\\HasNoChildren) "." INBOX.Esempio.2019'
Risposta analizzata: ('\\HasNoChildren', '.', 'INBOX.Esempio.2019')
Risposta del server: b'(\\HasNoChildren) "." "INBOX.Messaggi Eliminati"'
Risposta analizzata: ('\\HasNoChildren', '.', 'INBOX.Messaggi Eliminati')
Risposta del server: b'(\\HasNoChildren \\Archive) "." INBOX.Archive'
Risposta analizzata: ('\\HasNoChildren \\Archive', '.', 'INBOX.Archive')
test@robyp.x10host.com C60cP26vS8tl
Messaggi di partenza: b'1 2'
Messaggi corrispondenti: 2
Segnalatori prima: [b'2 (FLAGS (\\Seen))']
Segnalatori dopo: [b'2 (FLAGS (\\Deleted \\Seen))']
Cancellati definitivamente: [b'2']
Messaggi rimasti: b'1'

Vedere anche:

imaplib
La documentazione della libreria standard per questo modulo.
rfc822
Il modulo rfc822 include un elaboratore per RFC 822 / RFC 5322
email
Il modulo email per elaborare messaggi email
mailbox
Elaboratore di caselle di posta locali
ConfigParser
Legge e scrive file di configurazione
RFC 3501
Protocollo di Accesso Messaggio Internet
RFC 5322
Formato Messaggio Internet
IMAP Backup Script
Uno script per effettuare copie di email da un server IMAP
Client IMAP
Un client ad alto livello per parlare con i server IMAP, scritto da Menno Smits
offlineimap
Una applicazione Python per mantenere un insieme locale di caselle di posta sincronizzate con un server IMAP
Note di portabilità da Python2 a 3 per imaplib