imaplib - Libreria per client IMAP4

Scopo Libreria client per il protocollo di comunicazione IMAP4
Versione Python 1.5.2 e superiore

Il modulo imaplib implementa un client per la comunicazione con server attraverso Internet Message Access Protocol (IMAP), versione 4. Il protocollo IMAP definisce un insieme di comandi inviati al server e le risposte restituite dal client. La maggior parte dei comandi è disponibile sotto forma di metodi dell'oggetto IMAP4 usato per comunicare con il server.

Questi esempi trattano parti del protocollo IMAP, ma non sono in alcun modo completi. Si faccia riferimento a RFC 3501 per un completo dettaglio.

Varianti

Ci sono 3 classi client per comunicare con i server usando vari meccanismi. Il primo, IMAP4, usa socket con testo in chiaro; IMAP4_SSL usa una comunicazione criptata attraverso socket SSL, ed IMAP_stream usa gli standard input ed output di un comando esterno. Tutti gli esempi che seguono useranno IMAP4_SSL.

Connessione ad un Server

Ci sono due passi da compiere per stabilire una connessione con un server IMAP, Per prima cosa si imposta la connessione al socket, quindi si esegue l'autenticazione come utente che possiede un account sul server. Il codice di esempio che segue leggerà le informazioni circa il server e l'utente da un file di configurazione.

Non si dovrebbero conservare password di email in testo in chiaro, ma la gestione della crittografia avrebbe impedito di focalizzare l'attenzione sul resto degli esempi.
import imaplib
import ConfigParser
import os

def open_connection(verbose=False):
    # Lettura del file di configurazione
    config = ConfigParser.ConfigParser()
    config.read([os.path.expanduser('~/.pymotw')])

    # Connessione al server
    hostname = config.get('server', 'hostname')

    if verbose: print 'Connessione a', hostname
    connection = imaplib.IMAP4_SSL(hostname)

    # Connessione al proprio account
    username = config.get('account', 'username')
    password = config.get('account', 'password')
    if verbose: print 'Collegamento come', username
    connection.login(username, password)
    return connection

if __name__ == '__main__':
    c = open_connection(verbose=True)
    try:
        print c
    finally:
        c.logout()

Quando viene eseguito lo script, open_connection() legge le informazioni di configurazione da un file nella propria directory home, poi apre una connessione IMAP4_SSL ed esegue l'autenticazione

$ python imaplib_connect.py

Connessione a imap.gmail.com
Collegamento come pymotw.it@gmail.com

Autenticazione Fallita

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

import imaplib
import ConfigParser
import os

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

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

# Login al proprio account
username = config.get('account', 'username')
password = 'questa-è-la-password-sbagliata'
print 'Connesso come', username
connection.login(username, password)
$ python imaplib_connect_fail.py

Connecting to imap.gmail.com
Logging in as pymotw.it@gmail.com
Traceback (most recent call last):
  File "imaplib_connect_fail.py", line 25, in 
    connection.login(username, password)
  File "/usr/lib/python2.7/imaplib.py", line 507, in login
    raise self.error(dat[-1])
imaplib.error: [AUTHENTICATIONFAILED] Invalid credentials (Failure)

Configurazione di Esempio

L'account di esempio ha 4 mailbox, INBOX, Apple Mail To Do, Archive e 2008 (una sottocartella di Archive). La gerarchia delle mailbox è questa:

  • INBOX
  • Apple Mail To Do
  • Archive
    • 2008

C'è un messaggio non letto nella casella INBOX ed un messaggio letto in Archive/2008

Elencare le Mailbox

Per recuperare le mailbox disponibili per un account si usa il metodo list().

import imaplib
from pprint import pprint
from imaplib_connect import open_connection

c = open_connection()
try:
    typ, data = c.list()
    print 'Codice risposta:', typ
    print 'Risposta:'
    pprint(data)
finally:
    c.logout()

Il valore restituito è una tupla con un codice di risposta ed i dati ritornati dal server. Il codice di risposta è OK, a meno che non vi sia un errore. I dati per list() sono una sequenza di stringhe che contengono flag, il delimitatore gerarchico, il nome della mailbox per ciascuna mailbox.

 $ python imaplib_list.py

Codice risposta: OK
Risposta:
['(\\HasNoChildren) "/" "Apple Mail To Do"',
 '(\\HasNoChildren) "/" "INBOX"',
 '(\\HasChildren) "/" "Archive"',
 '(\\HasNoChildren) "/" "Archive/2008"']

Ogni stringa di risposta può essere scomposta in 3 parti usando re oppure csv (si veda lo script IMAP Backup per un esempio che utilizza csv).

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):
    flags, delimiter, mailbox_name = list_response_pattern.match(line).groups()
    mailbox_name = mailbox_name.strip('"')
    return (flags, delimiter, mailbox_name)

if __name__ == '__main__':
    c = open_connection()
    try:
        typ, data = c.list()
    finally:
        c.logout()
    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)

Si noti che il server racchiude il nome delle mailbox tra apici se lo stesso contiene degli spazi, tuttavia detti apici devono essere eliminati per utilizzare il nome della mailbox in altre chiamate al server successivamente.

$ python imaplib_list_parse.py

Codice di risposta: OK
Risposta del server: (\HasNoChildren) "/" "Apple Mail To Do"
Risposta analizzata: ('\\HasNoChildren', '/', 'Apple Mail To Do')
Risposta del server: (\HasNoChildren) "/" "INBOX"
Risposta analizzata: ('\\HasNoChildren', '/', 'INBOX')
Risposta del server: (\HasChildren) "/" "Archive"
Risposta analizzata: ('\\HasChildren', '/', 'Archive')
Risposta del server: (\HasNoChildren) "/" "Archive/2008"
Risposta analizzata: ('\\HasNoChildren', '/', 'Archive/2008')

list() ottiene due parametri per consentire di richiedere solo le mailbox facenti parte di una certa gerarchia. Ad esempio per elencare tutte le sotto-cartelle di Archive, si passa un valore per il parametro directory:

import imaplib

from imaplib_connect import open_connection

if __name__ == '__main__':
    c = open_connection()
    try:
        typ, data = c.list(directory='Archive')
    finally:
        c.logout()
    print 'Codice di risposta:', typ

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

Viene restituito solo la singola sotto-cartella

$ python imaplib_list_subfolders.py

Codice di risposta: OK
Risposta del server: (\HasNoChildren) "/" "Archive/2008"

Oppure, per elencare cartelle che corrispondono ad un modello si può passare il parametro pattern:

import imaplib

from imaplib_connect import open_connection

if __name__ == '__main__':
    c = open_connection()
    try:
        typ, data = c.list(pattern='*Archive*')
    finally:
        c.logout()
    print 'Codice risposta:', typ

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

In questo caso sia Archive che Archive/2008 sono inclusi nella risposta.

$ python imaplib_list_pattern.py

Codice risposta: OK
Risposta del server: (\HasChildren) "/" "Archive"
Risposta del server: (\HasNoChildren) "/" "Archive/2008"

Stato della Mailbox

Si usa status() per richiedere informazioni aggregate circa il contenuto. Lo standard definisce le seguenti condizioni di stato

MESSAGES
Il numero di messaggi nella mailbox
RECENT
Il numero di messaggi con il flag Recent impostato
UIDNEXT
Il valore del prossimo identificatore univoco della mailbox
UIDVALIDITY
Il valore dell'identificativo univoco della mailbox
UNSEEN
Il numero di messaggi che non hanno impostato il flag Seen

Le condizioni di stato devono essere formattate come stringhe separate da spazio, racchiuse in parentesi; la codifica per "list" nella specifica IMAP4.

import imaplib
import re

from imaplib_connect import open_connection
from imaplib_list_parse import parse_list_response

if __name__ == '__main__':
    c = open_connection()
    try:
        typ, data = c.list()
        for line in data:
            flags, delimiter, mailbox_name = parse_list_response(line)
            print c.status(mailbox_name, '(MESSAGES RECENT UIDNEXT UIDVALIDITY UNSEEN)')
    finally:
        c.logout()

Il valore di ritorno è la solita tupla che contiene un codice di risposta ed una lista di informazioni dal server. In questo caso, la lista contiene una singola stringa formattata con il nome della mailbox racchiuso tra apici, poi le condizioni di stato ed i valori tra parentesi.

$ python imaplib_status.py

('OK', ['"Apple Mail To Do" (MESSAGES 0 RECENT 0 UIDNEXT 1 UIDVALIDITY 662496775 UNSEEN 0)'])
('OK', ['"INBOX" (MESSAGES 1 RECENT 0 UIDNEXT 7 UIDVALIDITY 662496756 UNSEEN 1)'])
('OK', ['"Archive" (MESSAGES 0 RECENT 0 UIDNEXT 1 UIDVALIDITY 662496776 UNSEEN 0)'])
('OK', ['"Archive/2008" (MESSAGES 1 RECENT 0 UIDNEXT 4 UIDVALIDITY 662496777 UNSEEN 0)'])

Selezionare una Mailbox

Il metodo operativo base, una volta che il client è autenticato, è quello di selezionare una mailbox quindi interrogare il server riguardo ai messaggi nella mailbox. La connessione è stateful, quindi, una volta che viene selezionata una mailbox tutti i comandi operano su messaggi in quella mailbox fino a quando non ne viene selezionata una nuova.

import imaplib
import imaplib_connect

c = imaplib_connect.open_connection()
try:
    typ, data = c.select('INBOX')
    print typ, data
    num_msgs = int(data[0])
    print 'Ci sono %d messagggi in INBOX' % num_msgs
finally:
    c.close()
    c.logout()

I dati in risposta contengono il numero totale di messaggi nella mailbox

$ python imaplib_select.py

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

Se viene indicata una mailbox non valida, il codice di risposta è NO.

import imaplib
import imaplib_connect

c = imaplib_connect.open_connection()
try:
    typ, data = c.select('Non Esiste')
    print typ, data
finally:
    c.logout()

I dati contengono un messaggio di errore che descrive il problema.

$ python imaplib_select_invalid.py

NO ['[NONEXISTENT] Unknown Mailbox: Non Esiste (Failure)']

Cercare Messaggi

Una volta che la mailbox è selezionata, si usa search() per recuperare gli identificativi dei messaggi nella mailbox

import imaplib
import imaplib_connect
from imaplib_list_parse import parse_list_response

c = imaplib_connect.open_connection()
try:
    typ, mailbox_data = c.list()
    for line in mailbox_data:
        flags, delimiter, mailbox_name = parse_list_response(line)
        c.select(mailbox_name, readonly=True)
        typ, msg_ids = c.search(None, 'ALL')
        print mailbox_name, typ, msg_ids
finally:
    try:
        c.close()
    except:
        pass
    c.logout()

Gli identificativi dei messaggi sono assegnati dal server, e dipendono dall'implementazione. Il protocollo IMAP4 distingue tra identificativi sequenziali per messaggi ad un determinato punto nel tempo durante una transazione ed identificativi UID per i messaggi, ma non tutti i server sembrano tenerne conto.

$ python imaplib_search_all.py

Apple Mail To Do OK ['']
INBOX OK ['1']
Archive OK ['']
Archive/2008 OK ['1']

In questo caso INBOX ed Archive/2008 hanno ciascuno un messaggio diverso con identificativo 1. Le altre mailbox sono vuote.

Criteri di Ricerca

Si possono usare vari criteri di ricerca, compreso la ricerca di date per i messaggi, flag ed altre intestazioni. Si faccia riferimento alla sezione 6.4.4 di RFC 3501 per i completi dettagli.

Come esempio, per trovare i messaggi con 'test message 2' come oggetto il criterio di ricerca potrebbe essere costruito in questo modo:

(SUBJECT "test message 2")

Questo esempio trova tutti i messaggi con oggetto che contiene 'test message 2' in tutte le mailbox.

import imaplib
import imaplib_connect
from imaplib_list_parse import parse_list_response

c = imaplib_connect.open_connection()
try:
    typ, mailbox_data = c.list()
    for line in mailbox_data:
        flags, delimiter, mailbox_name = parse_list_response(line)
        c.select(mailbox_name, readonly=True)
        typ, msg_ids = c.search(None, '(SUBJECT "test message 2")')
        print mailbox_name, typ, msg_ids
finally:
    try:
        c.close()
    except:
        pass
    c.logout()

Si trova solo un messaggio che corrisponde nell'account, e si trova in INBOX

$ python imaplib_search_subject.py

Apple Mail To Do OK ['']
INBOX OK ['1']
Archive OK ['']
Archive/2008 OK ['']

I criteri di ricerca si possono anche combinare

import imaplib
import imaplib_connect
from imaplib_list_parse import parse_list_response

c = imaplib_connect.open_connection()
try:
    typ, mailbox_data = c.list()
    for line in mailbox_data:
        flags, delimiter, mailbox_name = parse_list_response(line)
        c.select(mailbox_name, readonly=True)
        typ, msg_ids = c.search(None, '(FROM "Doug" SUBJECT "test message 2")')
        print mailbox_name, typ, msg_ids
finally:
    try:
        c.close()
    except:
        pass
    c.logout()

La combinazione viene trattata come una operazione di and logico.

$ python imaplib_search_from.py

INBOX OK ['1']
Apple Mail To Do OK ['']
Archive OK ['']
Archive.2008 OK ['']

Recuperare Messaggi

Gli identificativi dei messaggi restituiti da search() vengono usati per recuperarne il contenuto, anche parziale, per una successiva elaborazione tramite fetch(), che riceve due parametri, l'identificativo del messaggio da prendere e la(e) porzione(i) da recuperare.

Il parametro message_ids è una lista di identificativi separati da virgola ("1", "1,2"), oppure da una gamma di identificativi (1:2). Il parametro message_parts è una lista di nomi di segmenti di messaggi IMAP. Assieme ai criteri di ricerca per search(), il protocollo IMAP attribuisce nomi ai segmenti di messaggi in modo che i client possano con efficacia recuperare solo le parti di messaggio delle quali necessitano. Ad esempio per stampare tutte le intestazioni dei messaggi in una mailbox, si potrebbe usare fetch() per recuperare le intestazioni usando BODY.PEEK[HEADER].

Un altro modo per recuperare le intestazioni sarebbe semplicemente BODY[HEADERS], ma quella forma contrassegna implicitamente i messaggi come letti, la qual cosa, in molti casi non è desiderabile.
import imaplib
import pprint
import imaplib_connect

imaplib.Debug = 4
c = imaplib_connect.open_connection()
try:
    c.select('INBOX', readonly=True)
    typ, msg_data = c.fetch('1', '(BODY.PEEK[HEADER] FLAGS)')
    pprint.pprint(msg_data)
finally:
    try:
        c.close()
    except:
        pass
    c.logout()

Il valore di ritorno di fetch() è stato parzialmente elaborato così da essere in qualche modo più difficoltoso da lavorare rispetto al valore restituito da list(). Se si attiva il debugger, si può vedere l'interazione completa tra il client ed il server per comprendere perchè succede questo.

$ python imaplib_fetch_raw.py
  13:12.54 imaplib version 2.58
  13:12.54 new IMAP4 connection, tag=CFKH
  13:12.54 < * OK dovecot ready.
  13:12.54 > CFKH0 CAPABILITY
  13:12.54 < * CAPABILITY IMAP4rev1 SORT THREAD=REFERENCES MULTIAPPEND UNSELECT IDLE CHILDREN LISTEXT LIST-SUBSCRIBED NAMESPACE AUTH=PLAIN
  13:12.54 < CFKH0 OK Capability completed.
  13:12.54 CAPABILITIES: ('IMAP4REV1', 'SORT', 'THREAD=REFERENCES', 'MULTIAPPEND', 'UNSELECT', 'IDLE', 'CHILDREN', 'LISTEXT', 'LIST-SUBSCRIBED', 'NAMESPACE', 'AUTH=PLAIN')
  13:12.54 > CFKH1 LOGIN example "password"
  13:13.18 < CFKH1 OK Logged in.
  13:13.18 > CFKH2 EXAMINE INBOX
  13:13.20 < * FLAGS (\Answered \Flagged \Deleted \Seen \Draft  )
  13:13.20 < * OK [PERMANENTFLAGS ()] Read-only mailbox.
  13:13.20 < * 2 EXISTS
  13:13.20 < * 1 RECENT
  13:13.20 < * OK [UNSEEN 1] First unseen.
  13:13.20 < * OK [UIDVALIDITY 1222003700] UIDs valid
  13:13.20 < * OK [UIDNEXT 4] Predicted next UID
  13:13.20 < CFKH2 OK [READ-ONLY] Select completed.
  13:13.20 > CFKH3 FETCH 1 (BODY.PEEK[HEADER] FLAGS)
  13:13.20 < * 1 FETCH (FLAGS () BODY[HEADER] {595}
  13:13.20 read literal size 595
  13:13.20 < )
  13:13.20 < CFKH3 OK Fetch completed.
  13:13.20 > CFKH4 CLOSE
  13:13.21 < CFKH4 OK Close completed.
  13:13.21 > CFKH5 LOGOUT
  13:13.21 < * BYE Logging out
  13:13.21 BYE response: Logging out
  13:13.21 < CFKH5 OK Logout completed.
[('1 (FLAGS () BODY[HEADER] {595}',
  'Return-Path: \r\nReceived: from example.com (localhost [127.0.0.1])\r\n\tby example.com (8.13.4/8.13.4) with ESMTP id m8LDTGW4018260\r\n\tfor ; Sun, 21 Sep 2008 09:29:16 -0400\r\nReceived: (from dhellmann@localhost)\r\n\tby example.com (8.13.4/8.13.4/Submit) id m8LDTGZ5018259\r\n\tfor example@example.com; Sun, 21 Sep 2008 09:29:16 -0400\r\nDate: Sun, 21 Sep 2008 09:29:16 -0400\r\nFrom: Doug Hellmann \r\nMessage-Id: <200809211329.m8LDTGZ5018259@example.com>\r\nTo: example@example.com\r\nSubject: test message 2\r\n\r\n'),
 ')']

La rsposta del comando FETCH inizia con i flag, quindi indica che ci sono 595 byte 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 ) che il server invia alla fine della risposta a fetch. A causa di questa formattazione, potrebbe essere più facile recuperare diversi pezzi di informazione separatamente, oppure ricombinare la risposta ed elaborarla personalmente.

mport imaplib
import pprint
import imaplib_connect

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

    print 'HEADER:'
    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 'BODY TEXT:'
    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 '\nFLAGS:'
    typ, msg_data = c.fetch('1', '(FLAGS)')
    for response_part in msg_data:
        print response_part
        print imaplib.ParseFlags(response_part)
finally:
    try:
        c.close()
    except:
        pass
    c.logout()

Recuperando i valori separatamente si ha il vantaggio addizionale di rendere facile l'uso di ParseFlags per elaborare i flag dalla risposta.

$ python imaplib_fetch_separately.py
HEADER:
Return-Path: 
Received: from example.com (localhost [127.0.0.1])
    by example.com (8.13.4/8.13.4) with ESMTP id m8LDTGW4018260
    for ; Sun, 21 Sep 2008 09:29:16 -0400
Received: (from dhellmann@localhost)
    by example.com (8.13.4/8.13.4/Submit) id m8LDTGZ5018259
    for example@example.com; Sun, 21 Sep 2008 09:29:16 -0400
Date: Sun, 21 Sep 2008 09:29:16 -0400
From: Doug Hellmann 
Message-Id: <200809211329.m8LDTGZ5018259@example.com>
To: example@example.com
Subject: test message 2


BODY TEXT:
second message


FLAGS:
1 (FLAGS ())
('',)

Interi Messaggi

Come qui sopra illustrato, il client può chiedere al server parti individuali del messaggio separatamente. E' anche possibile recuperare l'intero messaggio come messaggio di posta formattato secondo le specifiche di RFC 2822 ed elaborarlo con le classi del modulo email.

import imaplib
import email
import imaplib_connect

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

    typ, msg_data = c.fetch('1', '(RFC822)')
    for response_part in msg_data:
        if isinstance(response_part, tuple):
            msg = email.message_from_string(response_part[1])
            for header in [ 'subject', 'to', 'from' ]:
                print '%-8s: %s' % (header.upper(), msg[header])

finally:
    try:
        c.close()
    except:
        pass
    c.logout()

Il parser nel modulo email rende molto facile l'accesso e la manipolazione dei messaggi. Questo esempio stampa solo qualcuna delle intestazioni per ogni messaggio.

$ python imaplib_fetch_rfc822.py
SUBJECT : test message 2
TO      : example@example.com
FROM    : Doug Hellmann 

Caricare Messaggi

Per aggiungere un nuovo messaggio alla mailbox passarlo al metodo append().

import imaplib
import time
import email.message
import imaplib_connect

new_message = email.message.Message()
new_message.set_unixfrom('pymotw')
new_message['Subject'] = "L'oggetto va qui"
new_message['From'] = 'pymotw@example.com'
new_message['To'] = 'example@example.com'
new_message.set_payload('Questo è il corpo del messaggio.\n')

print new_message

c = imaplib_connect.open_connection()
try:
    c.append('INBOX', '', imaplib.Time2Internaldate(time.time()), str(new_message))

    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%s:' % num
                print response_part[1]

finally:
    try:
        c.close()
    except:
        pass
    c.logout()

pymotw
Subject: L'oggetto va qui
From: pymotw@example.com
To: example@example.com

Questo è il corpo del messaggio.


1:
Return-Path: 
Received: from example.com (localhost [127.0.0.1])
    by example.com (8.13.4/8.13.4) with ESMTP id m8LDTGW4018260
    for ; Sun, 21 Sep 2008 09:29:16 -0400
Received: (from dhellmann@localhost)
    by example.com (8.13.4/8.13.4/Submit) id m8LDTGZ5018259
    for example@example.com; Sun, 21 Sep 2008 09:29:16 -0400
Date: Sun, 21 Sep 2008 09:29:16 -0400
From: Doug Hellmann 
Message-Id: <200809211329.m8LDTGZ5018259@example.com>
To: example@example.com
Subject: test message 2



2:
Return-Path: 
Message-Id: <0D9C3C50-462A-4FD7-9E5A-11EE222D721D@example.com>
From: Doug Hellmann 
To: example@example.com
Content-Type: text/plain; charset=US-ASCII; format=flowed; delsp=yes
Content-Transfer-Encoding: 7bit
Mime-Version: 1.0 (Apple Message framework v929.2)
Subject: lorem ipsum
Date: Sun, 21 Sep 2008 12:53:16 -0400
X-Mailer: Apple Mail (2.929.2)



3:
pymotw
Subject: L'oggetto va qui
From: pymotw@example.com
To: example@example.com

Spostare e Copiare Messaggi

Una volta che il messaggio è sul server, può essere spostato o copiato senza scaricarlo usando move() oppure copy. Questi metodi operano su gamme di identificativi di messaggio, proprio come fetch().

Lo script di esempio crea una nuova mailbox sotto Archive e copia in essa i messaggi letti in INBOX.

import imaplib
import imaplib_connect

c = imaplib_connect.open_connection()
try:
    # Cerca i messaggi  "SEEN"  in INBOX
    c.select('INBOX')
    typ, [response] = c.search(None, 'SEEN')
    if typ != 'OK':
        raise RuntimeError(response)

    # Crea una nuova mailbox, "Archive.Today"
    msg_ids = ','.join(response.split(' '))
    typ, create_response = c.create('Archive.Today')
    print 'CREATA Archive.Today:', create_response

    # Copia i messaggi
    print 'IN COPIA:', msg_ids
    c.copy(msg_ids, 'Archive.Today')

    # Cerca quello che ne risulta
    c.select('Archive.Today')
    typ, [response] = c.search(None, 'ALL')
    print 'COPIATI:', response

finally:
    c.close()
    c.logout()
$ python imaplib_archive_read.py
CREATA Archive.Today: ['Create completed.']
IN COPIA: 1,2
COPIATI: 1 2

Se si esegue nuovamente lo stesso script si dimostra l'importanza di verificare i codici di ritorno. Invece di sollevare una eccezione, la chiamata a create() per creare una nuova cartella rivela che la mailbox esiste già

$ python imaplib_archive_read.py
CREATA Archive.Today: ['Mailbox exists.']
IN COPIA: 1,2
COPIATI: 1 2 3 4

Cancellare Messaggi

Sebbene la maggior parte dei client mail moderni usino un modello "Cartella Trash" (Cestino) per lavorare con i messaggi cancellati, i messaggi in genere non sono spostati in una effettiva cartella. Invece i loro flag sono aggiornati per aggiungere \Deleted. Lo svuotamento del cestino viene implementato attraverso un comando EXPUNGE. Qusto script di esempio trova i messaggi archiviati che contengono "Lorem ipsum" nell'oggetto, imposta il flag di cancellazione, quindi dimostra che i messaggi sono ancora presenti nella cartella interrogando nuovamente il server.

import imaplib
import imaplib_connect
from imaplib_list_parse import parse_list_response

c = imaplib_connect.open_connection()
try:
    c.select('Archive.Today')

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

    # Trova i messaggi
    typ, [msg_ids] = c.search(None, '(SUBJECT "Lorem ipsum")')
    msg_ids = ','.join(msg_ids.split(' '))
    print 'Messaggi corrispondenti:', msg_ids

    # Quali sono i flag attuali?
    typ, response = c.fetch(msg_ids, '(FLAGS)')
    print 'Flags prima:', response

    # Cambio il flag Deleted
    typ, response = c.store(msg_ids, '+FLAGS', r'(\Deleted)')

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

    # Cancello veramente i messaggi
    typ, response = c.expunge()
    print 'Cancellati:', response

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

finally:
    try:
        c.close()
    except:
        pass
    c.logout()

Questo esempio chiama esplicitamente expunge() per rimuovere i messaggi, ma la chiamata a close() ottiene lo stesso effetto. La differenza è che il client non è notificato circa le cancellazioni quando si chiama close()

$ python imaplib_delete_messages.py
Messaggi di partenza: 1 2 3 4
Messaggi corrispondenti: 1,3
Flags prima: ['1 (FLAGS (\\Seen ))', '3 (FLAGS (\\Seen \\Recent ))']
Flags dopo: ['1 (FLAGS (\\Deleted \\Seen ))',
'3 (FLAGS (\\Deleted \\Seen \\Recent ))']
Cancellati: ['1', '2']
Messaggi rimasti: 1 2

Vedere anche:

imaplib
La documentazione della libreria standard per questo modulo.
What is IMAP?
Descrizione del protocollo IMAP in imap.org.
University of Washington IMAP Information Center
Buona risorsa per informazioni su IMAP, con anche del codice sorgente.
RFC 3501
Protocollo Internet di Accesso ai Messaggi
RFC 2822
Formato Internet dei Messaggi
IMAP Backup Script
Uno script per la copia di email da un server IMAP
rfc822
Il modulo rfc822 include un parser per RFC 822 / RFC 2822.
email
Il modulo email per elaborare messaggi email.
mailbox
Parser per mailbox locale.
ConfigParser
Legge e scrive file di configurazione
IMAPClient
Un client ad alto livello per comunicare con server IMAP, scritto da Menno Smits.