Bercakap dengan POPerver Gmail dengan Python

PHPz
Lepaskan: 2024-09-08 06:31:43
asal
1242 orang telah melayarinya

Talking to a Gmail POPerver with Python

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())
Salin selepas log masuk

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>
Salin selepas log masuk

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)
Salin selepas log masuk

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'
Salin selepas log masuk

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())
Salin selepas log masuk

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'
Salin selepas log masuk

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'

Salin selepas log masuk

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())
Salin selepas log masuk

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 &#187;</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]: 
> 
Salin selepas log masuk

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()
Salin selepas log masuk

Atas ialah kandungan terperinci Bercakap dengan POPerver Gmail dengan Python. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

sumber:dev.to
Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Tutorial Popular
Lagi>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan