Arsip mwmag[Files]  [Up]© 2002 PT Masterweb Media

Tutorial Regex Bagian 5:

Bermain-main Dengan Email

  1. Modifier s dan m
  2. Regex di Ruby
  3. Memparse Pesan Email
  4. Mencocokkan Alamat Email
  5. Format-Format Mailbox
  6. MIME
  7. Modul Pemrosesan Email
  8. Program Filter Email untuk Mozilla Mail
  9. Penutup

Files

  1. listserv-sample.msg
  2. rfc2045.txt
  3. rfc2046.txt
  4. rfc2047.txt
  5. rfc2048.txt
  6. rfc2049.txt
  7. rfc821.txt
  8. rfc822.txt

Kode

  1. compose_email.php
  2. mozfilter.rb

 

Meneruskan tradisi bagian-bagian tutorial sebelumnya belajar regex sambil menggunakannya untuk tujuan praktis, serta menyesuaikan dengan tema edisi majalah, maka di bagian kelima kali ini kita akan bermain-main dengan email. Sebelumnya kita akan mengulas kembali apa perbedaan antara modifier s (single line) dengan m (multiline), lalu mengenal fasilitas regex di bahasa Ruby. Untuk praktiknya kita akan memparse pesan email, mencocokkan alamat email, mengenal apa itu MIME, memparse mailbox, dan terakhir membuat sebuah program sederhana untuk memfilter email.

[Catatan: Untuk mencoba skrip-skrip di dibutuhkan Ruby, PHP, dan Perl. Source code Ruby maupun Ruby for Windows disertakan di CD.]

Modifier s dan m

Mengapa saya membahas kembali kedua modifier ini? Pertama, karena banyak pemula yang masih bingung apa kegunaan modifier s dan m, serta apa perbedaannya. Kedua, regex di Ruby—yang akan kita bahas sesaat lagi—memiliki perbedaan sintaks dalam segi ini. Namun terlebih dahulu kita akan membahas penggunaan modifier s dan m di flavor regex Perl dan famili dekatnya (PCRE, fungsi preg_* di PHP, dan modul re di Python 2).

Terus terang, dulu saya praktis tidak pernah menggunakan modifier m. Ini karena saya kurang mengetahui jelas fungsinya, jadi cenderung menghindarinya. Saya amati nampaknya sebagian orang juga melakukan hal yang sama. Barangkali salah satu sebab kepusingan adalah pada keambiguan nama modifiernya itu sendiri. s singkatan dari single line, padahal seringkali digunakan untuk menangkap banyak baris dalam satu match group. Sementara m berarti multiline, tapi sebetulnya mempengaruhi kelakuan karakter meta ^ agar memperlakukan sebuah baris sama dengan baris lain. Penamaan istilah single line dan multiline juga mengisyaratkan seolah-olah kedua modifier ini bekerja saling berlawanan, padahal tidak. s dan m dapat dipakai bersama-sama dan bekerja sama membuat pola regex kita lebih ringkas.

Jadi yang pertama perlu Anda ingat, s dan m bukanlah lawan satu sama lain. Keduanya berbeda fungsi dan bekerja pada karakter meta yang tidak sama.

Modifier s mempengaruhi kelakuan . (titik) dan \s (whitespace).

Modifier m mempengaruhi kelakuan ^ (jangkar awal) dan $ (jangkar akhir).

Sekali lagi, keduanya bukan lawan dan tidak berhubungan. Bisa dipakai bersama-sama.

Modifier s

Dengan kehadiran s, maka . (titik) dan \s akan cocok dengan karakter newline. Tanpa s, meskipun karakter titik dijuluki si match all, tapi secara default karakter newline tidak akan dicocokkan dengan titik oleh mesin regex. Mari kita lihat contoh:

Received: from masterwebnet.com (ip54-142.cbn.net.id [202.158.54.142])
by corp2.cbn.net.id (Postfix) with ESMTP id 95C60D98A
for <milis-masterweb@yahoogroups.com>; Sun, 25 Aug 2002 13:43:40 +0700 (WIT)

Ini adalah sebuah field header email panjang, yang terdiri dari tiga baris. (Format email akan dijelaskan sesaat lagi). Kalau Anda ingin menangkap seluruh isi field Received—dari “from ” hingga “(WIT)”—maka pola yang betul adalah /Received: (.+)/s dan bukan hanya /Received: (.+)/. Tanpa s, .+ akan berhenti di akhir baris pertama karena begitu bertemu dengan newline, titik sudah tidak cocok lagi. Sementara dengan s, .+ akan terus melahap newline hingga akhir string.

Biasanya, modifier s hampir selalu kita inginkan saat mencocokkan HTML. HTML mengizinkan adanya extra whitespace termasuk extra newline. Sehingga sebuah tag bisa ditulis:

<img src="2kelinci.jpg" width=468 height=60 alt="klik di sini! beli! sekarang!">

atau bisa juga ditulis:

<img src="2kelinci.jpg" width=468 height=60 alt="klik di sini! beli! sekarang!"
>

atau bahkan:

<img 
src="2kelinci.jpg"
width=468
height=60
alt="klik di sini! beli! sekarang!"
>

Sehingga kalau Anda ingin menghilangkan semua tag <img> dari teks HTML dengan regex misalnya, jangan lupa pasang modifier s pada kode Perl Anda:

$html =~ s/<img\b.*?>//sgi;

atau:

$html =~ s/<img\b[>]*>//sgi;

Tanpa s, .*? dan [>]* hanya akan cocok hingga akhir baris, atau bahkan tidak cocok sama sekali. Sebab tanpa s, .*? akan berbunyi “nol hingga lebih karakter apa saja kecuali newline” dan [>]* akan berbunyi “nol hingga lebih karakter apa saja kecuali > dan newline.”

Contoh lain, kalau Anda ingin mengambil atau menghilangkan elemen komentar (<!-- ... -->), elemen Javascript (<script...>...</script>), dan elemen CSS (<style...>...</style>), sudah hampir pasti Anda menginginkan s, karena elemen ini biasanya memanjang hingga berbaris-baris. Hanya jika Anda yakin teks yang Anda olah sudah pasti terdiri dari satu baris saja—misalnya karena telah Anda potong-potong sebelumnya—baru Anda tidak butuh s.

Di lain pihak, kehadiran s kadang tidak diinginkan, terutama untuk teks-teks yang sensitif terhadap perbedaan baris. Lihat kode Perl berikut:

$text = <<'EOF';
Marlboro $18
Camel $17
Regal Blue $17
Salem, Winston and more ...
EOF
 
# nama rokok yang harganya $17
($rokok) = /(.+) \$17/s; # oops

Dengan regex di kode tersebut, yang kita tangkap bukannya hanya “Camel” atau “Regal Blue” yang kita mau, melainkan:

Marlboro $18
Camel $17
Regal Blue

Karena, ingat, modifier s memungkinkan kita menembus batas baris, dan karena pencocokan itu defaultnya rakus maka mesin regex akan terus mencari dari awal sampai menemukan $17 yang terakhir. Dalam hal kasus ini, seharusnya modifier s tidak dipakai.

Tanpa modifier s pun Anda tentu saja tetap bisa menangkap banyak baris sekaligus, yaitu dengan mengganti setiap titik dengan (.|\n) atau (.|\015|\012) dan mengganti setiap \s dengan (\s|\n) atau (\s|\012\015). Jadi s membantu Anda dengan membuat regex lebih ringkas—dan berarti, lebih mudah dibaca.

Modifier m

Modifier m membuat kita agar tetap bisa menggunakan ^ dan $ untuk mencocokkan baris yang ada di tengah-tengah sebuah teks multibaris. Tanpa m, ^ dan $ berbunyi “cocokkan dengan awal string” dan “cocokkan dengan akhir string.” Dengan m, “cocokkan dengan awal baris” dan “cocokkan dengan akhir baris.” Perhatikan kode Perl berikut:

$text = <<_;
Subject: Re: yuk!
From: A. Monica
To: S. Haryanto
_
while ($text =~ /^(\S+): (.+)/g) {
print "nama field=$1, nilai field=$2\n";
}

Kalau Anda mengharapkan ada tiga kali pencocokan header yang terjadi, Anda akan kecewa. Karena ^ hanya cocok dengan awal string (“Subject ...”) maka hanya header itu saja yang tercocokkan. Kalau Anda pasang modifier s, maka tetap Subject yang tercocokkan sebagai nama field, tapi nilai fieldnya sisa seluruh string!

Yang tepat adalah dengan menghadirkan modifier m:

while ($text =~ /^(\S+): (.+)/mg) {
print "nama field=$1, nilai field=$2\n";
}

Maka Anda akan memperoleh hasil yang dikehendaki.

Tentu saja, kalau teks Anda memang hanya satu baris—misalnya, karena Anda mengambil dari file memang per baris—pakai ^ juga boleh.

Di bawah pengaruh m, jika Anda ingin menyatakan “cocok hanya dengan awal string” Anda bisa memakai \A. Sementara untuk akhir string, \Z. Dan di luar pengaruh m, kalau Anda ingin menyatakan kecocokan dengan awal baris, gunakan pola extended (?:\A|(?<=\n)) dan untuk akhir baris, gunakan (?:(?=\n)|\Z). (?:...) telah Anda kenal sebelumnya, yaitu pola extended yang berguna untuk pengelompokan. (?<=...) disebut zero-width positive look-behind. (?=...) disebut zero-width positive look-ahead. Kita akan membahas positive/negative look-behind dan positive/negative look-ahead pada kesempatan lain. Bila penasaran, Anda bisa melihat halaman manual Perl perlre. Semua pola ini disebut zero-width karena tidak mewakili karakter nyata apapun, melainkan hanya untuk mewakili posisi atau melakukan fungsi lain.

Saya harap penjelasan bagian ini membantu Anda mengerti kegunaan modifier s dan m.

Regex di Ruby

Ruby adalah sebuah bahasa yang Made- & Big in Japan, bahkan kabarnya lebih popular daripada Python di negeri Sakura tersebut. Ruby adalah bahasa skripting OO yang banyak menyerap fitur-fiturnya dari bahasa lain seperti Smalltalk, Python, dan Perl. Ruby juga bahkan memiliki operator // dan =~ untuk melakukan pencocokan regex seperti di Perl, dan memiliki variabel global $1, $2, dst. untuk menyimpan match group.

Namun ada beberapa perbedaan. Pertama, semua regex adalah objek (instans dari kelas builtin Regexp). Ini karena Ruby memandang segala sesuatu sebagai objek (istilah kerennya, Ruby itu pure OO language). Bahkan sebetulnya // adalah sinonim untuk Regexp.new):

/[A-Z_][A-Z0-9_]*/i
# sama dengan Regexp.new('[A-Z_][A-Z0-9_]*', Regexp::IGNORECASE)

Selain //, %r() juga dapat digunakan untuk membuat objek Regexp. %r() fungsinya sama seperti qr() di Perl.

%r(.+/.+)i  # sama dengan /.+\/.+/i

Setelah objek Regexp dibuat, pola regex akan dikompile dan disimpan oleh Ruby, dan si objek siap melakukan pencocokan.

re = /[A-Z_][A-Z0-9_]*/i 
text = "luas = panjang * lebar"
text =~ /[A-Z_][A-Z0-9_]*/i # 0, true
text =~ re # 0, true
m = re.match(text) # objek MatchData
m = /[A-Z_][A-Z0-9_]*/i.match(text) # objek MatchData

Pencocokan dengan =~ akan menghasilkan posisi di mana pertama terjadi kecocokan. Dalam kasus ini di karakter ke-0 (“luas”). Perhatikan bahwa numerik 0 di Ruby bernilai logika true (Di Ruby, hanya nil dan false yang bernilai logika false. Sisanya, seperti string kosong, array kosong, maupun angka nol, adalah bernilai true.) Jika tidak ada kecocokan, =~ akan mengembalikan nil, yang bernilai logika false. Di lain pihak, pencocokan dengan metode match() akan menghasilkan objek MatchData jika terjadi kecocokan. Anda dapat mengambil match group dari objek MatchData ini dengan mengenakan operasi subscript, []. m[0] artinya seluruh string yang cocok, m[1] artinya match group pertama ($1), m[2] match group kedua, dst.

Perbedaan kedua yang penting antara Ruby vs Perl adalah cara substitusi dan pencocokan global. Di Ruby tidak ada operator s/// maupun modifier g. Yang ada adalah metode objek string sub, sub!, gsub, gsub!, dan scan. Metode-metode ini menerima objek regex sebagai argumen pertamanya. Metode-metode sub menerima string atau blok sebagai argumen kedua. Metode scan dapat menerima blok. Berikut beberapa contoh:

# ganti 'n' pertama dengan 'nt'
"kenalkan, saya dini.".sub(/n/, 'nt') # "kentalkan, saya dini."
 
# buang semua huruf hidup
"kenalkan, saya dini.".gsub(/[aeiou]/, '') # "knlkn, sy dn."
 
# bolak-balik huruf hidupnya
"kenalkan, saya dini.".gsub(/([aeiou])/) {|v|
case v;when'a';'e';when'e';'i';when'i';'o';when'o';'u';when'u';'a';end
} # "kinelken, seye dono."
 
# ganti 'in place'
a = "kenalkan, saya dini."
a.gsub!(Regexp.new('dini'),'gun')
print a # "kenalkan, saya gun."
 
# padanan 'while (/.../g) {...}' Perl
"468x60, 120x60, 88x31".scan(/(\d+)x(\d+)/) {|x, y|
puts "banner #{x} x #{y}"
}

Perbedaan ketiga yang perlu diperhatikan adalah, di Ruby tidak dikenal s. Modifier m pun memiliki arti berbeda, tepatnya, memiliki arti seperti s di regex Perl. Sebetulnya perbedaan ini cukup masuk akal. Pencipta Ruby, matz, nampaknya menyadari bahwa s vs m berpotensi membingungkan. Karena itu di Ruby, ^ dan $ selalu berarti awal dan akhir baris. Artinya, cocok untuk baris pertama, kedua, ketiga, dst. Untuk awal dan akhir string, digunakan \A dan \Z. Agar newline cocok dengan pola \s atau titik (.), digunakan modifier m (MULTILINE). Jadi, di Ruby multiline artinya sama dengan SINGLELINE (s) di regex Perl. Mudah-mudahan Anda tidak bingung.

Memparse Pesan Email

Setiap harinya di Internet barangkali berseliweran miliaran pesan email. Dari satu komputer ke komputer lain, dari satu program ke program lain, dan dari mailbox ke mailbox. Sebagian besar perpindahan pesan email dilakukan melalui protokol SMTP (Simple Mail Transport Protocol), dan sebagian besar email disimpan dalam mailbox berformat Unix mbox atau Maildir. Pesan-pesan demikian ditulis dalam format teks sederhana yang diatur dalam konvensi RFC 822 berjudul “Format Standar Pesan Teks Internet ARPA”. Saya sertakan rfc822.txt di CD dan situs majalah mwmag.com/issue/08/tutorial-regex-5/file/ sebagai referensi. Sudah dua puluh tahun RFC ini diterbitkan, dan rata-rata orang pasti sudah akrab dengan formatnya. Selain di dunia email Internet, format RFC 822 pun dipakai di Usenet.

Subject: [mwmag] jadwal edisi 10/11/12
Date: Fri, 23 Aug 2002 12:33:55 +0700
From: Steven Haryanto <steven@mwmag.com>
Reply-To: supporter-discuss@mwmag.com
To: supporter-discuss@mwmag.com
 
edisi 10 - xml
jadwal terbit: awal des 2002
deadline naskah: 31 okt 2002
 
edisi 11 - java
jadwal terbit: awal jan 2003/pertengahan jan 2002 (?)
deadline naskah: 20 nov 2002
 
edisi 12 - dotnet
jadwal terbit: awal feb 2003
deadline naskah: 20 des 2002
 
--
Salam,
mwmag
 
----------------------------------------------------------------------
Komunitas penulis dan editor lepas mwmag.
Unsubscribe? supporter-discuss-unsubscribe@mwmag.com
Arsip? http://mwmag.com/archive/supporter-discuss@mwmag.com/
----------------------------------------------------------------------

Header dan body. Pesan RFC 822 ditulis dalam set karakter US-ASCII (7 bit). Sebuah pesan selalu terdiri dari sebuah paragraf yang disebut header dan teks sisanya adalah body. Paragraf header adalah sekumpulan baris-baris takkosong pertama yang berisi informasi seperti nama pengirim, penerima, tanggal, dan metadata lainnya. Header dan body dipisahkan dengan satu baris kosong. Dalam regex Perl, kita bisa menyatakan sbb:

($header, $body) = $msg =~ /(.+?)\n\n(.*)/s;

\n\n berarti baris kosong. Kalau Anda ingat tutorial bagian kedua, Anda akan ingat bahwa modifier s dibutuhkan agar karakter meta titik di match group pertama maupun kedua dapat mencocokkan newline. Tapi kita menambahkan quantifier ? agar match pertama ini tidak rakus. Begitu ditemukan baris kosong (\n\n), maka sisanya akan ditangkap dalam match group kedua yaitu body.

Format header. Paragraf header terdiri dari satu atau lebih field header.

@header_fields = split /\n/, $header;

Pada contoh di atas kita menggunakan fasilitas operator split dari Perl. split berfungsi untuk memecah-mecah sebuah string berdasarkan pembatas tertentu. Contoh di atas memecah sebuah string paragraf menjadi array masing-masing berisi satu baris. Pembatasnya, yaitu “\n” (newline) tidak akan diikutsertakan. Contoh lain misalnya split /,/, "satu,dua,tiga" akan menghasilkan array ("satu", "dua", "tiga"). split sebetulnya amat ampuh. Dia bisa menerima pola regex sebagai pembatas serta dapat membelah hingga sejumlah potongan tertentu saja, dan dapat pula memasukkan kembali pola pembatasnya ke dalam potongan-potongan. Di PHP, padanan split() Perl adalah fungsi preg_split(). Di Python, padanannya adalah fungsi split() di modul re. Di Ruby, metode split() juga tersedia bagi objek builtin String. Silakan lihat dokumentasi masing-masing bahasa untuk melihat apa saja yang bisa dilakukan dengan split. Yang jelas, kekuatan utamanya adalah masih pada regex.

Field multiline. Sebetulnya contoh terakhir memecah-mecah baris header tidaklah akurat. Sebuah field header diperbolehkan memakan lebih dari satu baris (wrapped) kalau panjang. Asalkan, di baris-baris sambungannya teks diisi menjorok. Contohnya field-field header Received: yang rata-rata panjang:

Received: from masterwebnet.com (ip54-142.cbn.net.id [202.158.54.142])
by corp2.cbn.net.id (Postfix) with ESMTP id 95C60D98A
for <milis-masterweb@yahoogroups.com>; Sun, 25 Aug 2002 13:43:40 +0700 (WIT)

Field lain pun boleh saja dilipat, misalnya kalau judul email terlalu panjang.

Subject: Re: [MasterWeb] Re: ASP dan PHP, 7 Reasons Why PHP is Better than
ASP

Jadi split /\n/, $header sebetulnya salah, karena akan membelah field-field yang panjang ini ke dalam beberapa buah “field”. Bagaimana regex yang seharusnya? Salah satu cara adalah seperti di bawah:

@header_fields = $header =~ /(^\S.*(?:\n\s+.*)*)/mg;

Bagaimana bunyi regex di atas? “Tangkap baris yang tidak diawali spasi/tab (\S) yang diikuti oleh nol atau lebih baris yang diawali spasi/tab (\s).” Modifier m memegang peranan di sini, yaitu untuk membuat karakter meta jangkar (^) cocok dengan semua awal baris, baik itu baris pertama, kedua, dst. Tanpa kehadiran m maka si jangkar hanya bisa cocok dengan awal string, yaitu awal baris pertama.

Format field. Setiap field header disusun atas nama field yang diikuti tanda titik dua, boleh diikuti spasi, lalu sisanya isi field. Dalam bahasa regex:

($field_name, $field_value) = $header_field =~ /(\S+):\s*(.*)/s;

Lagi-lagi kita menggunakan modifier s. Gunanya? Untuk menangkap nilai field di baris kedua dst (kalau memang ada).

Skrip pengirim email. Mari sekarang kita gabungkan potongan-potongan kode sebelumnya menjadi sebuah program contoh yang lengkap. Kali ini kita gunakan PHP saja. Lihat Listing 1. Skrip PHP ini menerima email yang kita tulis, memeriksa header-headernya, lalu mengirimkannya. Sedikit catatan: jika Anda memasang skrip ini di tempat hosting/server yang terhubung ke Internet, maka berhati-hatilah dan jangan biarkan sembarang orang dapat mengaksesnya. Skrip ini memungkinkan siapa saja mengirim email ke mana saja, sehingga dapat disalahgunakan untuk mengirim spam atau bom.

Mencocokkan Alamat Email

Di Listing 1 tadi, bagaimana seandainya kita ingin mengecek dulu apakah $from dan $to yang dimasukkan pengguna skrip itu benar? Ya, masalah mencocokkan atau menguji keabsahan sebuah alamat email memang sering sekali dijumpai dalam pemrograman web. Tapi, bagaimana sebetulnya aturan sintaks sebuah alamat email itu? Ternyata, menurut standar (yakni, RFC 822 juga) aturannya cukup kompleks. Saking kompleksnya, sampai-sampai pengarang buku Mastering Regular Expressions menghabiskan sebelas halaman membahasnya. Tentu saja, ruang tutorial yang terbatas ini tidak memungkinkan kita membahas hal ini secara mendalam. Melainkan, saya hanya akan menghadirkan beberapa regex dari yang paling sederhana hingga yang cukup kompleks dan mendekati. Ingin bisa mencocokkan secara lebih “benar” lagi? Tersedia modul-modul yang bisa Anda pakai lalu intip untuk melihat bagaimana modul tersebut mencocokkan alamat email. Lihat bagian Modul-Modul Pemrosesan Email di akhir tutorial ini.

Sebuah alamat email Internet biasanya memiliki sintaks “user@host”. Saya akan mengambil regex yang sama yang mula-mula dihadirkan Jeffrey Friedl dalam Mastering Regular Expressions:

\w+\@\w+(\.\w+)+

Perhatikan bahwa @ saya escape. Meskipun bukan karakter meta, tapi bahasa Perl mengartikan @ sebagai prefiks untuk variabel array, jadi perlu diescape. Untuk bahasa lain, tidak ada salahnya juga @ ini dikutip. Untuk alamat email yang paling sederhana seperti budi@hotmail.com memang regex ini sudah mencukupi. Tapi sebetulnya, kalau Anda ingat tutorial bagian lalu, \w hanyalah cocok dengan [A-Za-z0-9_]. Jangankan user, sebuah hostname pun boleh mengandung spasi (mis: hot-mail.com). Jadi penggunaan \w terlalu ketat (artinya, tidak mencakup semua kasus yang ada). Tapi di lain sisi, hostname pun sebetulnya memiliki restriksi lain, seperti panjang maksimum nama domain, setiap bagian subdomain tidak boleh diawali/ hanya terdiri dari tanda “–” (tanda minus), tidak boleh mengandung “_” (underscore). Jadi, di lain sisi tersebut regex kita pun terlalu longgar.

Mari kita perbaiki regex ini. Bagian host kita bagi dulu menjadi 2 kasus, yaitu IP dan hostname. Untuk IP, regexnya cukup mudah (kita tidak akan mengacuhkan Ipv6):

\[\d{1,3}(?:\.\d{1,3}){3}\]

Contoh alamat emailnya misalnya: steven@[202.43.10.11]. (Perhatikan tanda kurung siku). Apakah regex ini telah sempurna? Tentu saja tidak, karena alamat IP tak valid seperti 999.999.999.999 pun akan diloloskan regex ini. Anda bisa menyusun regex yang mengecek tiap angka tak boleh lebih dari 255, atau menambahkan kode pengecek di luar regex. Tapi ini tidak akan kita lakukan di sini, biarlah jadi PR atau tantangan untuk Anda.

Kasus kedua adalah hostname:

[A-Za-z0-9][A-Za-z0-9-]*(?:\.[A-Za-z0-9][A-Za-z0-9-]*)*

Hostname ini lebih ketat daripada regex sebelumnya yang lebih sederhana, yakni tidak memperbolehkan garis bawah, tapi memperbolehkan hostname terdiri dari satu bagian saja (mis: “localhost” atau “server1”) karena memang dalam lingkungan intranet boleh-boleh saja hostname itu tidak fully qualified. Apakah regex ini sudah sempurna? Tentu saja belum. Pertama-tama, tidak ada pembatasan panjang domain. Ini agak sulit karena sebetulnya standar tidaklah membatasi ini, melainkan domain-domain seperti .com/net/org-lah yang memiliki keterbatasan panjang tertentu. Domain juga sebetulnya bisa dinyatakan dalam set karakter Unicode, tapi tentu saja itu di luar cakupan tutorial ini.

Bagaimana dengan bagian user (atau tepatnya, local part) pada alamat email? Regex berikut saya ambil dari source code modul Perl Mail::Sendmail. Tidak sempurna memang, tapi lumayanlah:

[\x21\x23-\x27\x2A-\x2B\x2D\w\x3D\x3F]+(?:\.[\x21\x23-\x27\x2A-\x2B\x2D\w\x3D\x3F]+)*

Local part sebetulnya juga didefinisikan mirip bagian host, yaitu: word(.word)* (satu atau lebih “kata” yang dipisahkan karakter titik). Yang menjadi masalah di sini adalah spesifikasi dari word. Menurut standar (silakan dilihat di bagian 3.3 RFC 822), word boleh mengandung karakter “biasa” apa saja, termasuk “@”! word juga dapat terdiri dari sebuah string yang dikutip. Jadi sebetulnya "Steven Haryanto"@mwmag.com adalah sah menurut standar.

Kompleksitas tidak berhenti di situ. Alamat email sebetulnya boleh juga diawali dengan sebuah frase. Jika ada frase, maka bagian user@host harus dikutip dengan tanda “<...>”. Apakah frase itu? frase adalah sekumpulan word. Jadi alamat-alamat di bawah ini semuanya sah:

Steven Haryanto <steven@mwmag.com>
"Steven Haryanto" <steven@mwmag.com>
"Steven" "Haryanto" <steven@mwmag.com>

Untuk kasus sederhana, kita bisa cukup berpuas diri dengan regex yang telah kita bentuk di atas. Mari kita gabungkan sekarang. Tapi, yang perlu dimengerti adalah, bahwa regex ini tidaklah sempurna. Meskipun demikian, sudah cukup untuk kasus-kasus yang umum:

[\x21\x23-\x27\x2A-\x2B\x2D\w\x3D\x3F]+(?:\.[\x21\x23-\x27\x2A-+
\x2B\x2D\w\x3D\x3F]+)*\@(?:\[\d{1,3}(?:\.\d{1,3}){3}\]|[A-Za-z0+
-9][A-Za-z0-9-]*(?:\.[A-Za-z0-9][A-Za-z0-9-]*)*

Perhatikan bahwa sebetulnya regex di atas terdiri dari satu baris.

Format-Format Mailbox

Biasanya, email dihasilkan oleh program lalu masuk ke queue, atau dikirim user melalui SMTP ISP-nya. Setelah dikirim ke sana kemari, mayoritas email akan berakhir di tempat penyimpanan tertentu yang disebut mailbox. Sebuah mailbox—bergantung pada formatnya—dapat berupa file tunggal, sebuah direktori khusus, atau bahkan tabel database. Berbagai program email menggunakan dan menciptakan beragam format mailbox. Malah sebagian produk sengaja melakukan ini bukan demi optimasi efisiensi/kecepatan, melainkan demi tujuan komersial. Menyimpan data dalam format biner dan proprietary merupakan salah satu trik untuk mengikat user agar terus bergantung menggunakan si produk.

mbox

Format mbox adalah format tradisional yang paling umum digunakan di Unix. Bahkan, mbox juga dipakai oleh Netscape Mail dan Mozilla Mail, Eudora, pine, dan banyak lagi. Format ini tetap popular karena sederhana dan murni teks 7bit, sehingga praktis semua pengolah teks bisa memanipulasinya dengan baik. Dengan format ini, pesan-pesan email ditaruh ke dalam satu file. Untuk memisahkan satu pesan dengan pesan lain, digunakan “baris From_” yaitu baris yang diawali oleh lima huruf: F, r, o, m, dan spasi. Contohnya, lihat mailbox berikut ini yang berisi 2 buah mail. Satu bersubjek “satu” dan lainnya “dua”. Baris yang dicetak tebal adalah baris From_ yang dipakai untuk menandai satu email dengan email lainnya.

From - Sun Aug 25 23:45:02 2002
X-Mozilla-Status: 0001
X-Mozilla-Status2: 00000000
FCC: mailbox://steven%40masterwebnet.com@masterwebnet.com/Sent
Message-ID: <3D69098E.2000404@masterwebnet.com>
Date: Sun, 25 Aug 2002 23:45:02 +0700
From: Steven Haryanto <steven@masterwebnet.com>
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US; rv:1.0.0) Gecko/20020530
X-Accept-Language: en-us, en
MIME-Version: 1.0
To: steven@masterwebnet.com
Subject: satu
Content-Type: text/plain; charset=us-ascii; format=flowed
Content-Transfer-Encoding: 7bit
 
 
From - Sun Aug 25 23:45:28 2002
X-Mozilla-Status: 0001
X-Mozilla-Status2: 00000000
FCC: mailbox://steven%40masterwebnet.com@masterwebnet.com/Sent
Message-ID: <3D6909A8.6080300@masterwebnet.com>
Date: Sun, 25 Aug 2002 23:45:28 +0700
From: Steven Haryanto <steven@masterwebnet.com>
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US; rv:1.0.0) Gecko/20020530
X-Accept-Language: en-us, en
MIME-Version: 1.0
To: alex@masterwebnet.com
Subject: dua
Content-Type: text/plain; charset=us-ascii; format=flowed
Content-Transfer-Encoding: 7bit
 
Ini email kedua, tidak kosong seperti yang pertama.

Hm, tunggu dulu, Anda berkata. Bukankah sebuah email pun mungkin saja mengandung lima karakter ini? Katakanlah ada yang menulis “From Russia with love!” di awal paragraf pesan email. Betul sekali. Format mbox, saking sederhananya, memiliki masalah yang disebut inband communication. Karakter kontrol yang digunakan bisa dikandung oleh data yang hendak dikontrol. Karena itu solusinya adalah sebuah email dilarang mengandung “From ” di awal baris. Kalau Anda menulis email dengan Mozilla Mail misalnya, maka ketika email Anda disimpan di folder Sent, “From ” di awal baris akan diubah menjadi “ From ” (ditambah awalan spasi). Sebagian program lain, seperti Sendmail, menambahkan “>” (tanda lebih besar) di depan kata From. Pokoknya, lima huruf “From ” tidak boleh ada di awal baris. Jika tidak, program-program akan bingung.

Memanipulasi mailbox berformat mbox amat mudah. Misalnya, untuk menghitung jumlah pesan dalam sebuah mailbox, kita hanya perlu menghitung jumlah “From ” yang terdapat di awal baris. Dengan perintah grep di Unix misalnya:

$ grep -c '^From ' MAILBOX

Atau untuk membelah-belah mailbox menjadi pesan-pesan email, bisa digunakan kode Perl berikut:

$/ = undef; # artinya: sekali baca pakai <>, akan telan seluruh file
open MBOX, "MAILBOX" or die $!;
$i = 0;
foreach $msg (split /^From .+\n/m, <MBOX>) {
next unless $i++;
# proses msg
}

Perhatikan regex yang dipakai untuk split(). Kita ingin sekalian menghilangkan baris “From_”-nya juga, jadi sebagai pembatas kita tambahkan .+ dan karakter newlinenya kalian, \n. Kita pakai modifier m karena ingin memanfaatkan karakter meta jangkar ^. Sederhana bukan? Sekarang tiap $msg telah berisi pesan email yang bisa Anda parse lebih lanjut. Perhatikan pula saya menambahkan sebuah baris next untuk mencuekkan $msg pertama. Kenapa? Karena isinya kosong. Mailbox berisi dua buah baris From_ akan menghasilkan tiga potongan ketika displit. Bandingkan dengan string ",1,2,3". Ketika displit, hasilnya adalah array ("", "1", "2", "3").

Kalau mailbox besar, misalnya beberapa megabyte ke atas dan berisi seratus pesan ke atas, maka akan lebih cepat dan irit memori jika kita tidak membelahnya langsung (sehingga menghasilkan ratusan string pesan baru). Melainkan, kita tinggal mengingat posisi-posisi “From ” di mailbox untuk tiap pesan lalu menggunakan substr() nanti untuk mengambil pesan yang diinginkan dari string mailbox:

$/ = undef;
open MBOX, "MAILBOX" or die $!;
$mbox = <MBOX>;
 
@posisi = (); @panjang = (); $n = -1;
while ($mbox =~ /^From /mg) {
$n++;
$posisi[$n] = pos($mbox)-5;
$panjang[$n-1] = $posisi[$n]-$posisi[$n-1] if $n;
}
$panjang[$n] = length($mbox)-$posisi[$n];

Selama masih ada baris From_, maka kondisi while akan terus benar. Kita mencatat posisi awal baris From_ ke array @posisi. Untuk mendapatkan posisi pencocokan terakhir, digunakan operator pos() di Perl. Kita juga mencatat panjang pesan di array @panjang ybs agar nanti bisa kita substr() dengan mudah. Panjang pesan dihitung berdasarkan selisih posisi pesan ybs dengan pesan berikutnya.

Setelah semua selesai dicatat, untuk mengambil pesan kelima misalnya kita tinggal melakukan:

$msg = substr($mbox, $posisi[4], $panjang[4]);

Perhatikan bahwa, berbeda dengan cara split() yang pertama, dengan cara ini kita belum memotong baris From_. Bagaimana cara membuangnya? Gampang saja:

$msg =~ s/^.+\n//;

Perhatikan bahwa modifier s maupun m tidak dipakai. m tidak berguna di sini, karena kita hanya menghapus satu baris. Sementara kalau kita pakai s, maka seluruh pesan kita bakal lenyap!

Maildir

Selain inband communication, format mbox juga memiliki masalah lain yaitu seringnya terjadi kerusakan data. Karena semua pesan ditumpuk di satu file maka jika ada dua atau lebih proses yang ingin menambah pesan ke mailbox, keduanya harus kompakan menggunakan file locking (berhubung locking di Unix hanyalah bersifat advisory). Jika salah satu proses main bablas saja tanpa memperdulikan locking, maka salah satu pesan bisa hilang. Atau bahkan kedua pesan bisa rusak karena bercampur misalnya. Jika mailbox ditaruh di network filesystem (NFS), maka locking pun tidak bisa dilakukan sempurna.

Untuk menghindari masalah ini, pencipta qmail, D. J. Bernstein, membuat format mailbox baru bernama Maildir. Dengan Maildir, sebuah pesan email disimpan dalam file terpisah dengan nama unik. Artinya, sebuah proses tidak perlu melakukan locking, cukup membuat sebuah file baru di direktori Maildir milik user dan menulisi file tersebut dengan pesan email. Bukan itu saja, parsing mailbox pun jadi lebih mudah lagi (baca: tidak perlu parsing). Untuk menghapus/mengedit sebuah mail pun, tak harus mensave ulang seluruh mailbox. Kerugiannya, terjadi pemborosan ruang disk karena setiap file menempati minimal 1 blok di filesystem. Pemborosan dapat ditekan dengan menggunakan filesystem yang memiliki ukuran blok kecil, tak berblok, atau mendukung reuse blok (seperti ReiserFS).

Sebetulnya sebuah direktori Maildir berisi tiga subdirektori yaitu cur/, new/, dan tmp/. Pesan-pesan email, bergantung pada statenya, disimpan di salah satu subdirektori ini. Namun pembahasan mendetil mengenai hal ini di luar cakupan tutorial kita. Kita juga tidak akan menyinggung Maildir lebih lanjut karena kita kurang dapat bermain-main regex dengannya. Yang jelas, untuk server multiuser, format mailbox satu-email-satu-file memang lebih disarankan daripada satu-mailbox-satu-file karena lebih tidak rentan terhadap kerusakan data. Untuk keperluan pribadi, misalnya mailbox email hasil download POP3 oleh program-program MUA seperti Eudora dan Mozilla Mail, format mbox umumnya masih sah-sah saja karena yang menulisi sebuah mailbox hanyalah satu proses saja.

MH

Maildir bukan satu-satunya format mailbox berbasiskan direktori. Sebelum Maildir ada, telah dikenal format MH (message handler). Nama format mailbox ini mengikuti nama program klien email yang memakainya, yaitu mh (dan anggota keluarga lainnya: nmh, xmh, dsb). MH juga didukung oleh program mutt.

MMDF

MMDF setipe dengan mbox, menyimpan seluruh pesan dalam satu file. Didukung oleh program MMDF (multi-channel memo distribution), elm, mutt, pine. Bedanya, sebagai pengganti baris From_ ia menggunakan empat buah karakter kontrol ^A (ASCII 1). Masalah inband communication dihilangkan, tapi mailbox berformat MMDF mungkin tidak bisa diedit oleh pengolah teks tertentu. Untuk membelah mailbox menjadi pesan-pesan menggunakan Perl misalnya, bisa digunakan:

split /\1{4}/, $mbox

Format-Format Lain

Program-program klien email seperti Outlook Express, The Bat!, atau Calypso, juga menyimpan email dalam formatnya masing-masing, kebanyakan biner. Kita tidak akan membahas format-format ini karena tidak terdokumentasi dan toh tidak bisa diproses hanya dengan regex saja.

MIME

Kita sampai pada topik Multipurpose Internet Mail Extensions, atau MIME. MIME adalah perluasan terhadap RFC 822 dan resmi dirumuskan tahun 1992 (lalu diupdate pada 1996 melalui serangkai RFC dari nomor 2045 hingga 2049). Karena topik MIME cukup kompleks—bayangkan saja, lima RFC bung!—maka yang akan disinggung di sini hanyalah sepintas latar belakang MIME.

Mengapa RFC 822 perlu diperluas? Pertama-tama, format RFC 822 amat berorientasi 7-bit dan US-ASCII. Ini dikarenakan pada zamannya dahulu, rata-rata jaringan Internet bersifat 7-bit. Seiring email popular digunakan secara internasional di jaringan Internet dan juga dipakai mentransfer file, maka format ini tidaklah efisien. Data 8-bit harus dienkode dulu menggunakan uuencode sehingga ukurannya menjadi lebih besar.

Kedua, RFC 822 kurang banyak mengatur soal bodi. Bagi RFC 822, bodi hanyalah segumpal data yang terdiri dari nol atau lebih karakter (.*). Padahal, di zaman sekarang sebuah pesan email seringkali “kaya”, misalnya mengandung HTML plus gambar, atau attachment suara dan file lainnya. Tanpa adanya standar yang mengatur bagaimana seharusnya file-file ini disimpan di dalam bodi, maka program-program email akan kesulitan dan harus menciptakan sendiri standarnya.

Maka hadirlah MIME. Sebuah email MIME pada dasarnya adalah email RFC 822 namun memiliki beberapa perbedaan. Pertama, bodi email dapat 8 bit. Tentu saja, agar dapat dibedakan dari email RFC 822 biasa, maka ditaruhlah beberapa header khusus MIME dalam email MIME a.l. Content-Type, MIME-Version, dan Content-Transfer-Encoding. Jika nilai header Content-Transfer-Encoding “8bit”, berarti bodi email adalah data 8-bit. Selain enkoding 8-bit, email MIME juga dapat dienkode menggunakan “base64” dan “quoted-printable”. Enkoding ini untuk membungkus bodi 8-bit agar aman ditransfer di jaringan 7-bit.

Kedua, lewat Content-Type bertipe “multipart”, maka sebuah bodi email dapat dinyatakan merupakan kumpulan dari dokumen-dokumen (atau “bodi-bodi”) lainnya. Artinya, sebuah program yang menerima email MIME dapat mengetahui bahwa email tersebut berisi beberapa buah bodi, masing-masing misalnya sebuah file .zip, file .HTML, dsb.

Ketiga, email MIME juga dapat mengandung informasi header yang bukan US ASCII 7-bit (namun harus diencode dan tidak boleh dibiarkan “telanjang” begitu saja, untuk kompatibilitas dengan program-program lain yang tidak mengerti MIME). Ini berarti email MIME dapat ditulis oleh orang Chinese misalnya, dan subjeknya pun ditulis menggunakan set karakter mereka. Artinya, email MIME menjadi lebih usable secara internasional.

Untuk memroses email MIME memang jauh lebih sulit daripada memroses email RFC 822. Pertama-tama, Anda perlu membaca MIME-Version dan Content-Transfer-Encoding. Setelah data bodi—dan juga header—didekode Anda lalu harus bisa “memilah-milah” bodinya (kalau memang Content-Type email ini multipart).

Modul Pemrosesan Email

Pembahasan pada tutorial kali ini tidak akan lengkap kalau saya tidak menyebutkan modul-modul yang telah tersedia di bahasa pemrograman untuk mengolah pesan email, mencocokkan alamat email, memanipulasi mailbox, maupun memparse/membentuk pesan MIME. Karena email begitu penting dan sering harus diurus, tentu saja bahasa seperti Perl, Python, maupun Ruby telah memiliki librarynya. Jadi Anda tidak perlu mengerutkan kepala karena harus bermain-main regex setiap kali ingin menyentuh yang namanya email. Saya hanya akan menyebutkan masing-masing secara sekilas, sisanya bisa Anda lihat pada dokumentasi modul yang bersangkutan.

Perl. Karena usianya yang sudah matang, telah banyak dibuat modul-modul untuk segala macam keperluan, antara lain seputar pengolahan email. Beberapa di antaranya sebagai berikut. Modul untuk mengirim email ada beberapa, a.l. Mail::Sendmail dan Mail::Sender. Di Mail::Sendmail Anda dapat menemukan regex untuk mencocokkan alamat email, sementara dengan Mail::Sender mengirimkan email MIME multipart (email dengan attachment) amat mudah. Untuk memparse alamat dan pesan email, tersedia modul-modul berinterface OO seperti Mail::Internet, Mail::Header, Mail::Address karya Graham Barr. Untuk memparse mailbox mbox, ada Mail::MboxParser. Untuk memparse Maildir atau format-format mailbox lainnya, ada keluarga modul Mail::Box atau Mail::Folder. Untuk memparse MIME, ada paket MIME-Tools yang berisi modul seperti MIME::Decoder. Sementara jika ingin menghasilkan MIME saja (lebih mudah) tersedia modul yang lebih mini yaitu MIME::Lite.

Python. Python hadir dengan beberapa package standar yang berhubungan dengan email. Yang pertama adalah package terpadu email karya Barry Warsaw. Package ini memiliki kemampuan memparse pesan email RFC 822 maupun MIME. Untuk mengirim email, tersedia package smtplib. Sementara untuk membaca berbagai format mailbox pun telah disediakan, yaitu package mailbox. Di package tersebut tersedia beberapa kelas yang dapat memparse format mbox, MMDF, MH, maupun Maildir. Lengkap kap.

Ruby. Pemain yang relatif baru ini pun tidak mau kalah. Beberapa library protokol Internet, termasuk SMTP (kelas Net::SMTP), sudah disediakan dalam distribusinya. Untuk parsing email memang nampaknya belum ada modul standar, namun di RAA, www.ruby-lang.org/en/raa.html, tersedia beberapa pilihan seperti TMail (RFC 822 + MIME) atau RubyMail (RFC 822, MIME, dan juga kelas untuk filtering). Untuk memparse mbox atau MH pun di RAA ada librarynya.

Program Filter Email untuk Mozilla Mail

Berbekal pengetahuan akan format pesan email dan mbox, sudah banyak kemungkinan yang dapat kita lakukan. Memfilter email misalnya, seperti yang akan saya sajikan contohnya sekarang.

Mozilla Mail adalah program klien email/newsgroup yang mendukung POP dan IMAP. User interfacenya diambil dari Netscape Communicator. (Agak kuno memang, tapi bagi saya cukuplah.) Fitur-fitur lain yang tersedia di antaranya, multilevel folder, multiple personality, multiple SMTP, multiple undo, thread view. Dan yang terpenting buat saya adalah, cross platform (Linux dan Windows) serta menggunakan format mailbox… mbox! Dengan format mbox kita dapat mengolah email kita dengan banyak tool lain, mulai dari program email lain, Perl, grep, skrip shell, editor teks, dsb. Kita tidak bergantung pada Mozilla Mail untuk bisa membaca data kita.

Satu hal yang kurang di Mozilla Mail, selain kecepatannya, adalah kemampuan filteringnya yang terbatas. Pertama, Mozilla Mail hanya dapat memfilter incoming mail, sehingga bila email yang sudah masuk ingin kita kategorikan lagi maka silakan gigit jari. Saya bahkan sampai pernah mencoba memindahkan email ke spool server lokal, lalu mengambilnya ulang lewat POP agar bisa difilter ulang. Kedua, tidak ada laporan filter (berapa jumlah email yang cocok dengan rule anu, berapa total mail yang berhasil difilter, dsb). Ketiga, kriterianya terbatas. Tidak ada regex di sana. Padahal program sebelumnya yang saya pakai, Eudora, mendukung regex.

Karena keterbatasan-keterbatasan itulah, saya membuat skrip kecil untuk melakukan filtering secara eksternal—artinya, saya lakukan dari luar Mozilla Mail. Pertama email saya download dulu semua dari Mozilla. Lalu Mozilla saya tutup, dan skrip filter saya jalankan. Skrip ini membaca mailbox Inbox semua account email saya, dan memindah-mindahkan email ke mailbox lain sesuai rule yang saya tentukan sendiri. Baru setelah filtering selesai, Mozilla saya buka kembali. Agak bertele-tele jadinya memang, tapi ada beberapa keuntungan. Pertama, kriteria bisa sefleksibel yang saya inginkan. Kedua, skrip dan rule bisa saya reuse untuk program email lain, selama program tersebut menggunakan mbox. Dan saya memang berusaha agar program email yang saya pakai menggunakan mbox agar email bisa saya proses dengan mudah.

Silakan perhatikan Listing 2 untuk melihat program yang biasanya saya pakai untuk memfilter Inbox. Untuk mencoba program ini, taruhlah di direktori folder-folder email Mozilla Mail Anda lalu buatlah *.dat dan *.rules (akan dijelaskan nanti). Perhatian: backup dulu folder email Anda sebelum mencoba program ini. Saya tidak bertanggung jawab atas segala kerusakan/kehilangan data yang mungkin terjadi.

Skrip mozfilter.db dirancang dengan pertimbangan data-data account, filtering rules, termasuk daftar milis yang saya ikuti, semua disimpan dalam file teks berformat sederhana. Tujuannya adalah agar saya mudah mengedit data ini dengan program pengolah teks apa pun, dan dengan mudah bisa me-reuse-nya jika dibutuhkan.

Ada beberapa file teks yang dibaca program. Semua file data ini berisi record-record per baris. Tiap baris data terdiri dari kolom-kolom yang dipisahkan garis vertikal (“|”). File juga boleh mengandung baris kosong/baris komentar yang diawali tanda pagar (“#”).

File data pertama adalah accounts.dat, yaitu data account. Format baris data: nama account|direktori. Contohnya:

steven@mwmag.com|mwmag.com
steven-webmaster@mwmag.com|mwmag-1.com
steven@masterwebnet.com|masterwebnet.com

Pada contoh di atas saya memiliki tiga buah account. Account-account ini tentunya harus sudah disetup dulu di Mozilla Mail. Baris 174–188 membaca file accounts.dat dan memparsenya.

File data kedua adalah *.rules yang berisi rule-rule filtering. Saya mengelompokkan filtering ke dalam beberapa file terpisah. Misalnya, 00-spam-yahoo.rules untuk memfilter account-account Yahoo! yang dikenal spammer. 10-mwmag.rules untuk memfilter email-email mwmag saya. Setiap baris dalam file .rules saya buat sintaksnya sendiri sbb.: tanggal|nama rule|spec||nama account|folder. Field tanggal hanyalah untuk dokumentasi (kapan memasukkan rule ybs). Nama rule haruslah unik untuk semua file .rules yang ada. Tujuannya agar nanti saya bisa membuat statistik rule mana yang paling banyak cocok dengan email saya. spec formatnya sbb.: AND (subj,pred,obj) AND (...) atau OR (subj,pred,obj) OR (...). subj menyatakan apa yang ingin dicocokkan. Dapat bernilai “subject”, “from”, “to or cc”, “body” atau “header X” (untuk menyatakan sembarang header). pred menyatakan jenis pencocokan. Dapat bernilai “matches”, “doesn’t match”, “contains”, “doesn’t contain”, “exists”, “doesn’t exist”. Sementara obj menyatakan nilai yang harus dicocokkan. Untuk pred “contains” obj bernilai sebuah string. Untuk pred “exists” dan “doesn’t exist” tidak dibutuhkan obj. Sementara untuk pred “matches” nilainya adalah sebuah regex dalam format “/.../” atau “/.../i” (untuk pencocokan case-insensitive).

Beberapa contoh baris rule:

20020429|spam trafficmagnet|OR (from,contains,@trafficmagnet.net)||*|/spam

Artinya, jika ada email di Inbox yang header From-nya mengandung string “@trafficmagnet.net” maka akan dipindahkan ke folder /spam di account ybs. Folder /spam akan dibuat otomatis. Contoh lain:

20020429|spam yahoo rezeki20xx|OR (from,matches,/rezeki20\d\d@yahoo.com/i)||*|/spam

Artinya, kalau ada email yang header From-nya cocok dengan pola tersebut, akan dianggap spam.

Satu contoh lain (semua satu baris):

20020429|spam unadmitting|OR (body,contains,this is not a spam) OR +
(body,contains,"remove" in the subject)||*|/spam

Berbunyi: jika body mengandung ‘this is not a spam’ atau ‘“remove” in the subject’. Banyak spam memang sering mengaku-aku bukan spam dan malah meminta kita membalas email tersebut.

Baris 191–246 pada skrip akan membaca semua rule dan memasukkannya dalam daftar rule. Jika ada pred “matches” pada rule maka regex akan dikompile dan ditaruh di dalam variabel agar nanti pencocokan bisa lebih cepat.

File data ketiga adalah file lists.dat. File ini berisi daftar milis yang saya ikuti. Karena milis yang diikuti banyak, maka filtering rule untuk milis akan digenerate oleh skrip, tidak dimasukkan secara manual. Baris data pada lists.dat berformat: tanggal|alamat milis[,alamat alias,...]|kode mlm|alamat email terdaftar|nama account. Field tanggal dan kode mlm hanya sebagai dokumentasi, tapi kode mlm mungkin bisa digunakan nanti jika ingin sub/unsub secara otomatis dari semua list. Agar standar, setiap email dari milis akan dimasukkan ke dalam folder /lists/huruf pertama/alamat milis/periode.mbox. Misalnya (semua satu baris):

20020429|milis-masterweb@yahoogroups.com|EZMLM|+
steven@masterwebnet.com|steven@masterwebnet.com

Email-email ke milis milis-masterweb@yahoogroups.com yang ada di Inbox nanti akan ditaruh ke folder /lists/m/milis-masterweb@yahoogroups.com/200210.mbox. Pengaturan ini memudahkan saya dalam mengarsip milis-milis tanpa perlu memindah-mindahkan email per bulan atau membuat-buat folder milis secara manual.

Setelah semua file data dibaca, skrip mozfilter.rb akan mulai membaca file Inbox di semua account. Misalnya, mwmag.com/Inbox, mwmag-1.com/Inbox, dan masterwebnet.com/Inbox. Karena format filenya .mbox, kita bisa memparse menggunakan regex-regex yang telah dibahas sebelumnya. Mulai dari memecah-mecah tiap pesan email dengan mencari baris From_. Lalu, untuk setiap pesan email, memecah-mecah header dan bodinya.

Ada satu hal yang perlu diperhatikan. Email-email yang sudah dihapus dari Mozilla Mail namun belum di-compact masih akan berada di folder. Namun email ini ditandai yaitu pada nilai field X-Mozilla-Status-nya (yang berupa sebuah bilangan) bit ketiganya akan diset. Kita menguji nilai field ini dan jika memang sudah ditandai akan dihapus maka tidak perlu kita proses.

Sambil dibaca dan diparse, setiap pesan email akan dicocokkan dengan semua rule yang ada. Jika cocok dengan sebuah rule, maka email akan dikopikan ke folder tujuan. Mengkopi ke folder mbox pun mudah, cukup tambahkan sebuah baris From_ baru lalu tambahkan pesan email ke file mbox ybs.

Setelah memindah-mindahkan semua email, kita merewrite ulang file Inbox. Selesai. Laporan pun dicetak.

Penutup

Dari pembahasan di tutorial ini, saya berharap Anda dapat melihat kemungkinan berbagai aplikasi lain yang bisa Anda buat, misalnya searching, sorting, categorizing. Atau konversi satu format mailbox ke format lain. Atau penyensoran. Atau deteksi worm. Atau penyisipan iklan di body email. Dan lain sebagainya. Sekali Anda mengetahui format email/mailbox, maka dengan diperlengkapi kemampuan pemrosesan teks seperti regex Anda bisa memanipulasinya sesuka hati.

Sebagai satu bahan latihan, cobalah mengambil arsip milis yang dikelola program MLM LISTSERV (misalnya: milis SPAM-L@PEACH.EASE.LSOFT.COM) lalu konversikan menjadi format mbox. Ingat, tanpa latihan kemampuan regex Anda tidak akan terasah. Selamat mencoba. (sh)

mw

[Last-Modified: Fri Nov 29 14:47:21 2002]

Arsip mwmag[Files]  [Up]www.master.web.id/mwmag