POP ialah protokol yang agak lama. Versi pertama telah ditentukan pada tahun 1984. Versi yang masih digunakan hari ini, POP3, telah ditetapkan pada tahun 1996. Untuk mencubanya, saya menyambung ke pelayan POP3 Gmail.
Langkah pertama ialah mencari tetapan POP3 -- pelayan yang hendak disambungkan, pada port apa. Google membawa saya ke sini, di mana saya menemui maklumat berikut.
pop.gmail.com
Memerlukan SSL: Ya
Port: 995
Ia menyebut bahawa SSL diperlukan. Ini bukan perkara yang saya uruskan 25 tahun lalu, apabila kali terakhir saya bermain-main dengan POP. Saya takut ia akan menjadi sakit kepala, tetapi ia ternyata tidak mencabar apa-apa; dengan sedikit bantuan daripada dokumen Python, saya mencapai kod ini.
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())
Ia menghubungkan dan memberitahu saya versi SSL yang sedang digunakan... atau sesuatu. Kejayaan besar! Masa untuk memulakan perbualan dengan pelayan.
Meminjam daripada RFC rasmi untuk POP3, berikut ialah contoh perbualan POP3 antara pelanggan dan pelayan/
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>
Perkara pertama yang berlaku ialah pelayan menghantar ucapan kepada pelanggan. mesra. Jadi saya akan menambah kod untuk menerima mesej daripada pelayan.
Apabila anda meminta untuk menerima data daripada soket, anda perlu menentukan saiz penimbal. Dokumen mengesyorkan kuasa 2, seperti 4096. Banyak respons daripada pelayan akan datang serentak. Ada yang tidak; kadangkala mesej daripada pelayan akan dipecahkan merentasi bacaan pelayan, dan penimbal mungkin tidak diisi secara maksimum walaupun terdapat lebih banyak lagi yang akan datang.
Dalam kes POP3, cara untuk mengetahui sama ada mesej telah masuk sepenuhnya bergantung pada mesej yang akan masuk. Selalunya pelayan menghantar satu baris teks. (Seperti yang akan kita lihat semula kemudian, ini mempunyai watak pemulangan pengangkutan dan suapan baris pada penghujung setiap baris.) Mesej tertentu yang mungkin mempunyai respons yang lebih lama menggunakan cara lain untuk menunjukkan bahawa ia telah selesai: satu titik pada satu baris dengan sendirinya.
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)
Lari lagi dan, kita dapat salam. Satu lagi kejayaan besar! Perhatikan bahawa baris berakhir dengan "rn" -- carriage return dan aksara suapan baris.
Anda perlu menghantar saiz penimbal kepada kaedah baca. Ia kemudiannya akan mempunyai penimbal saiz yang tersedia untuk membaca data daripada pelayan -- tetapi tiada jaminan tentang jumlah data yang akan masuk ke dalam penimbal pada satu-satu masa. Ini bermakna bahawa protokol memerlukan beberapa cara untuk menentukan apabila mesej selesai. Terdapat banyak strategi yang mungkin. POP menggunakan dua: untuk semua mesej, baris diakhiri dengan rn. Untuk mesej pendek (satu baris), ini sahaja yang diperlukan. Untuk respons berbilang baris, noktah pada baris dengan sendirinya menunjukkan bahawa mesej telah selesai.
TLSv1.3 b'+OK Gpop ready for requests from 2601:1c0:8301:b590:f408:d66a:3029:16ad dq2mb54750689ivb\r\n'
Sekarang kita perlu mula bercakap kembali kepada pelayan. Masa untuk mencipta gelung I/O (atau O/I); dapatkan beberapa input pengguna dan hantar ke pelayan. Aduh! Saya tidak boleh menghantar rentetan secara langsung; yang memberi saya TypeError. Saya perlu menukar mesej kepada bait. Kaedah pengekodan rentetan() akan melakukannya (pengekodan lalai utf-8 berfungsi dengan baik).
Hanya, apabila saya menjalankannya -- op sekali lagi! Tiada apa-apa yang berlaku apabila mesej saya dihantar ke pelayan. Kerana saya terlupa bahawa mesej yang datang dari pelanggan juga perlu diakhiri dengan rn. Satu lagi tweak kecil memberi kami:
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())
Bagus, sekarang saya sebenarnya boleh cuba log masuk!
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, jadi mengikuti pautan itu membawa saya ke halaman di mana saya boleh menyediakan kata laluan khusus aplikasi. Satu halangan yang berpotensi yang saya temui: akaun anda perlu menghidupkan pengesahan dua faktor agar anda boleh membuat kata laluan khusus aplikasi, sejauh yang saya tahu. Mengapakah saya tidak menghidupkan pengesahan dua faktor pada tahun Lorde 2024 kita? Saya tidak boleh berkata. Saya lakukan sekarang.
Berbekalkan kata laluan khusus aplikasi (fikirkan anda keluarkan ruang), saya boleh log masuk! Kemudian saya akan mengeluarkan arahan STAT yang akan memberitahu saya berapa banyak mesej yang saya ada, dan saiz gabungannya. Selepas itu, saya akan mengeluarkan arahan LIST, yang akan mengembalikan senarai mesej dengan ID dan saiz untuk setiap satu.
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'
Saya telah terkena pepijat dalam kod. Respons untuk LIST merangkumi berbilang baris dan, dalam kes ini, memerlukan berbilang bacaan penimbal. Seluruh mesej akan berakhir dengan noktah pada baris dengan sendirinya. Di sini saya telah menerima satu mesej yang bernilai penimbal, dan kini saya perlu menekan pulangan dan menghantar mesej kosong kepada pelayan agar kod itu maju ke lelaran gelung seterusnya dan membaca dari penimbal semula.
Saya akan mengubah suai kod supaya pengguna sentiasa mempunyai pilihan untuk membaca daripada penimbal semula atau tidak. Saya juga akhirnya akan menyahkod bait masuk daripada pelayan, supaya teks menjadi lebih bagus.
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())
Dan berikut ialah sesi penuh termasuk mendapatkan semula e-mel dan menghantar mesej putus sambungan.
> 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()
Atas ialah kandungan terperinci Bercakap dengan POPerver Gmail dengan Python. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!