POP est un protocole relativement ancien. La première version a été spécifiée en 1984. La version encore utilisée aujourd'hui, POP3, a été spécifiée en 1996. Pour l'essayer, j'ai procédé à la connexion à un serveur Gmail POP3.
La première étape consistait à rechercher les paramètres POP3 : à quel serveur se connecter, sur quel port. Google m'a conduit ici, où j'ai trouvé les informations suivantes.
pop.gmail.com
Nécessite SSL : Oui
Port : 995
Il mentionne que SSL est requis. Ce n'était pas un problème auquel j'avais affaire il y a 25 ans, la dernière fois que je jouais avec POP. J'avais peur que ce soit un mal de tête, mais cela ne s'est avéré être aucun défi ; avec un peu d'aide de la documentation Python, je suis arrivé à ce code.
import socket import ssl hostname = 'pop.gmail.com' context = ssl.create_default_context() with socket.create_connection((hostname, 995)) as sock: with context.wrap_socket(sock, server_hostname=hostname) as s: print(s.version())
Il se connecte et me dit quelle version de SSL est utilisée... ou quelque chose du genre. Belle réussite ! Il est temps de démarrer une conversation avec le serveur.
En empruntant à la RFC officielle pour POP3, voici un exemple de conversation POP3 entre un client et un serveur/
C: <open connection> S: +OK POP3 server ready <1896.697170952@dbc.mtview.ca.us> C: USER mrose S: +OK mrose is a real hoopy frood C: PASS secret S: +OK mrose's maildrop has 2 messages (320 octets) C: STAT S: +OK 2 320 C: LIST S: +OK 2 messages (320 octets) S: 1 120 S: 2 200 S: . C: RETR 1 S: +OK 120 octets S: <the POP3 server sends message 1> S: . C: QUIT S: +OK dewey POP3 server signing off (maildrop empty) C: <close connection>
La première chose qui se produit est que le serveur envoie un message d'accueil au client. Amical. Je vais donc ajouter du code pour recevoir un message du serveur.
Lorsque vous demandez à recevoir des données d'un socket, vous devez spécifier une taille de tampon. La documentation recommande une puissance de 2, telle que 4096. De nombreuses réponses du serveur seront transmises en même temps. Certains ne le feront pas ; Parfois, un message du serveur sera divisé en plusieurs lectures et le tampon peut ne pas être rempli au maximum même s'il y a plus à venir.
Dans le cas de POP3, la façon de savoir si un message est arrivé dépend entièrement du message entrant. La plupart du temps, le serveur envoie une seule ligne de texte. (Comme nous le reverrons plus tard, ceux-ci comportent un retour chariot et des caractères de saut de ligne à la fin de chaque ligne.) Certains messages qui pourraient avoir une réponse beaucoup plus longue utilisent une autre façon de montrer qu'ils ont terminé : un point sur une seule ligne. par lui-même.
import socket import ssl hostname = 'pop.gmail.com' context = ssl.create_default_context() with socket.create_connection((hostname, 995)) as sock: with context.wrap_socket(sock, server_hostname=hostname) as s: print(s.version()) data = s.read(4096) print(data)
Courez à nouveau et nous recevons un salut. Encore une belle réussite ! Notez que la ligne se termine par "rn" - caractères de retour chariot et de saut de ligne.
Vous devez transmettre une taille de tampon à la méthode read. Il disposera alors d'un tampon de cette taille disponible pour lire les données du serveur - mais il n'y a aucune garantie quant à la quantité de données qui entreront dans le tampon à la fois. Cela signifie qu'un protocole a besoin d'un moyen de spécifier quand un message est terminé. De nombreuses stratégies sont possibles. POP en utilise deux : pour tous les messages, les lignes se terminent par rn. Pour un message court (une ligne), c'est tout ce qui est requis. Pour les réponses sur plusieurs lignes, un point sur une ligne indique à lui seul que le message est complet.
TLSv1.3 b'+OK Gpop ready for requests from 2601:1c0:8301:b590:f408:d66a:3029:16ad dq2mb54750689ivb\r\n'
Maintenant, nous devons commencer à répondre au serveur. Il est temps de créer une boucle E/S (ou O/I) ; obtenez une entrée de l'utilisateur et envoyez-la au serveur. Oups ! Je ne peux pas envoyer une chaîne directement ; cela me donne une TypeError. Je dois convertir le message en octets. La méthode string encode() fera cela (l'encodage par défaut de utf-8 fonctionne très bien).
Seulement, quand je l'exécute -- oups encore ! Rien ne se passe lorsque mon message est envoyé au serveur. Parce que j'ai oublié que les messages provenant du client doivent également se terminer par rn. Un autre petit ajustement nous donne :
import socket import ssl hostname = 'pop.gmail.com' context = ssl.create_default_context() with socket.create_connection((hostname, 995)) as sock: with context.wrap_socket(sock, server_hostname=hostname) as s: print(s.version()) while True: data = s.read(4096) print(data) msg = input() + "\r\n" s.send(msg.encode())
Super, je peux maintenant essayer de me connecter !
TLSv1.3 b'+OK Gpop ready for requests from 2601:1c0:8301:b590:f408:d66a:3029:16ad g4mb5147337iow\r\n' USER grokprogramming b'+OK send PASS\r\n' PASS trustno1 b'-ERR [AUTH] Application-specific password required: https://support.google.com/accounts/answer/185833\r\n'
OK, donc suivre ce lien m'amène à une page où je peux configurer un mot de passe spécifique à l'application. Une pierre d'achoppement potentielle que j'ai rencontrée : votre compte doit avoir une authentification à deux facteurs activée pour que vous puissiez créer un mot de passe spécifique à une application, pour autant que je sache. Pourquoi n'aurais-je pas activé l'authentification à deux facteurs l'année de notre Seigneur 2024 ? Je ne peux pas le dire. Je le fais maintenant.
Muni d'un mot de passe spécifique à l'application (attention à bien retirer les espaces), je peux me connecter ! Ensuite, j'émettrai la commande STAT qui me dira combien de messages j'ai et leur taille combinée. Après cela, j'émettrai la commande LIST, qui renverra une liste de messages avec un identifiant et une taille pour chacun.
TLSv1.3 b'+OK Gpop ready for requests from 2601:1c0:8301:b590:f408:d66a:3029:16ad e18mb76868856iow\r\n' USER grokprogramming b'+OK send PASS\r\n' PASS baygdsgkmihkckrb b'+OK Welcome.\r\n' STAT b'+OK 263 14191565\r\n' LIST b'+OK 263 messages (14191565 bytes)\r\n1 2778\r\n2 2947\r\n3 6558\r\n4 9864\r\n5 35997\r\n6 45462\r\n7 45462\r\n8 63894\r\n9 11487\r\n10 74936\r\n11 74925\r\n12 11632\r\n13 32392\r\n14 74997\r\n15 51961\r\n16 15375\r\n17 46513\r\n18 21519\r\n19 15966\r\n20 27258\r\n21 28503\r\n22 35615\r\n23 86353\r\n24 280'
J'ai rencontré un bug dans le code. La réponse pour LIST s'étend sur plusieurs lignes et, dans ce cas, nécessitera plusieurs lectures de tampon. L'ensemble du message se terminera par un point sur une ligne distincte. Ici, j'ai reçu un tampon du message, et maintenant je dois appuyer sur Entrée et envoyer un message vide au serveur pour que le code passe à l'itération suivante de la boucle et soit lu à nouveau dans le tampon.
Je vais modifier le code pour que l'utilisateur ait toujours la possibilité de relire ou non le tampon. Je vais aussi enfin décoder les octets entrants du serveur, pour que le texte soit plus joli.
import socket import ssl hostname = 'pop.gmail.com' context = ssl.create_default_context() with socket.create_connection((hostname, 995)) as sock: with context.wrap_socket(sock, server_hostname=hostname) as s: print(s.version()) while True: data = s.read(4096) print(data.decode()) while input("more? y/[n]: ") == "y": data = s.read(4096) print(data.decode()) msg = input("> ") + "\r\n" s.send(msg.encode())
Et voici une session complète comprenant la récupération d'un email et l'envoi du message de déconnexion.
> USER grokprogramming +OK send PASS more? y/[n]: > PASS trustno1 +OK Welcome. more? y/[n]: > STAT +OK 263 14191565 more? y/[n]: > LIST +OK 263 messages (14191565 bytes) 1 2778 2 2947 3 6558 <...> 260 41300 261 114059 262 174321 263 39206 . more? y/[n]: > RETR 1 +OK message follows MIME-Version: 1.0 Received: by 10.76.81.230; Thu, 28 Jun 2012 20:21:50 -0700 (PDT) Date: Thu, 28 Jun 2012 20:21:50 -0700 Message-ID: <CADBp03TWFOKcTOaK_0P7VV2GB+TZsoSd_W4G5nZKKs7pdk6cWQ@mail.gmail.com> Subject: Customize Gmail with colors and themes From: Gmail Team <mail-noreply@google.com> To: Grok Programming <grokprogramming@gmail.com> Content-Type: multipart/alternative; boundary=e0cb4e385592f8025004c393f2b4 --e0cb4e385592f8025004c393f2b4 Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: quoted-printable To spice up your inbox with colors and themes, check out the Themes tab under Settings. Customize Gmail =BB <https://mail.google.com/mail/#settings/themes> Enjoy! - The Gmail Team [image: Themes thumbnails] Please note that Themes are not available if you're using Internet Explorer 6.0. To take advantage of the latest Gmail features, please upgrade to a fully supported browser<http://support.google.com/mail/bin/answer.py?answer=3D6557&hl=3Den&= utm_source=3Dwel-eml&utm_medium=3Deml&utm_campaign=3Den> .. --e0cb4e385592f8025004c393f2b4 Content-Type: text/html; charset=ISO-8859-1 more? y/[n]: y <html> <font face="Arial, Helvetica, sans-serif"> <p>To spice up your inbox with colors and themes, check out the Themes tab under Settings.</p> <table cellpadding="0" cellspacing="0"> <col style="width: 1px;"/> <col/> <col style="width: 1px;"/> <tr> <td></td> <td height="1px" style="background-color: #ddd"></td> <td></td> </tr> <tr> <td style="background-color: #ddd"></td> <td background="https://mail.google.com/mail/images/welcome-button-background.png" style="background-color: #ddd; background-repeat: repeat-x; padding: 10px; font-size: larger"> <a href="https://mail.google.com/mail/#settings/themes" style="font-weight: bold; color: #000; text-decoration: none; display: block;"> Customize Gmail »</a> </td> <td style="ba more? y/[n]: y ckground-color: #ddd"></td> </tr> <tr> <td></td> <td height="1px" style="background-color: #ddd"></td> <td></td> </tr> </table> <p>Enjoy!</p> <p>- The Gmail Team</p> <img width="398" height="256" src="https://mail.google.com/mail/images/gmail_themes_2.png" alt="Themes thumbnails" /> <p><font size="-2" color="#999">Please note that Themes are not available if you're using Internet Explorer 6.0. To take advantage of the latest Gmail features, please <a href="http://support.google.com/mail/bin/answer.py?answer=6557&hl=en&utm_source=wel-eml&utm_medium=eml&utm_campaign=en"><font color="#999"> upgrade to a fully supported browser</font></a>.</font></p> </font> </html> --e0cb4e385592f8025004c393f2b4-- . more? y/[n]: > QUIT +OK Farewell. more? y/[n]: >
Yet another great success! I was able to log in to the POP3 server and retrieve a message. The script in its current state is pretty flexible, but it requires a lot of work from the user. I'll make a few final tweaks to make interacting with the POP3 server a little easier: if the user starts a message to the server with a "!" it will be stripped out, but the script will read in data from the server until it gets to a period on a line by itself -- in other words, for commands with long responses. No "!" and the script will read in a single line, looking for the \r\n characters.
import socket import ssl hostname = 'pop.gmail.com' context = ssl.create_default_context() def read_until(s, eom): # read into the buffer at least once data = s.read(4096) # continue reading until end of message while data[-len(eom):] != eom: data += s.read(4096) # return incoming bytes decoded to a string return data.decode() def read_single_line(s): return read_until(s, b"\r\n") def read_muli_line(s): return read_until(s, b"\r\n.\r\n") with socket.create_connection((hostname, 995)) as sock: with context.wrap_socket(sock, server_hostname=hostname) as s: print(s.version()) print(read_single_line(s)) msg = input("> ") # empty msg will close connection while msg != "": if msg[0] == "!": msg = msg[1:] long = True else: long = False msg += "\r\n" s.send(msg.encode()) if long: print(read_muli_line(s)) else: print(read_single_line(s)) msg = input("> ") s.close()
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!