Documentation can be found at https://core.tcl-lang.org/tcllib/doc/trunk/embedded/md/tcllib/files/modules/pop3/pop3.md .

     package require pop3  ;# http://tcllib.sourceforge.net/

     set popsock             [pop3::open $_theHost $_theUser $_thePassword]
     foreach {msgcount size} [pop3::status   $popsock] {}
     set message             [pop3::retrieve $popsock $_index]
     set messages            [pop3::retrieve $popsock $_first $_last]
                              pop3::delete   $popsock $_index
                              pop3::delete   $popsock $_first $_last
                              pop3::close    $popsock

[Add commentary to above. Explain foreach idiom. Mention exceptions.]

VL 21 may 2003: -ERR autorization first, if you receive this error during pop3::open a likely explanation is that the email-server does not accept connections through telnet, only through SSH. VL 19 aug 2003: I think that there are instructions included with most SSH-clients about how to set up port forward/tunneling. At least there is in the Putty documentation [L1 ]

escargo 21 May 2003 - I put if 0 { around the code so that this page becomes reapable; the check mail code should then just work if collected by wish-reaper and friends.

[Explain performance considerations, version compatibility, head use, exception-handling, ... Warn about deletes. Point to pertinent RFC for index base and other definitions.]

A little pop3 client demo, using the tcllib: Steve Offutt, 5/25/01 mailto:[email protected] Thanks to Cameron for help getting started.

escargo 21 May 2003 - If you push the "Disconnect" button to disconnect, and then push it again, an error results. The close_it proc does not check to see if the popsock value is empty before trying to close it.

 # Check Mail - a lightweight pop3 client - Thursday May 31, 2001

 package require pop3

 #create a user interface
 proc make_gui { } {

        label .lhost -text "Hostname"
        label .luser -text "Username"
        label .lpass -text "Password"   

        entry .eh -textvariable "_theHost"
        entry .eu -textvariable "_theUser"
        entry .ep -textvariable "_thePassword"

        bind .ep <Return> {open_channel}

        button .b -text "Connect" -command {open_channel}
        button .b2 -text "Disconnect" -command {close_it}

        set popsock ""
        label .lsock -text "Socket:"
        label .lsock2 -relief groove -bd 2

        grid .lhost .eh -sticky ew
        grid .luser .eu -sticky ew
        grid .lpass .ep -sticky ew
        grid .lsock .lsock2 -row 3  -sticky ew
        grid .b -row 4 -columnspan 2 -sticky ew
        grid .b2 -row 5 -columnspan 2 -sticky ew

        .ep configure -show *

 global new_messages
 set new_messages 0

 global popsock
 set popsock ""

 #procedures follow
 proc open_channel { } {
        global _theHost _theUser _thePassword popsock
        set popsock [pop3::open $_theHost $_theUser $_thePassword]
        .lsock2 configure -text $popsock
        return $popsock

 proc close_it { } {
        global popsock
        close $popsock
        set popsock ""
        .lsock2 configure -text ""
        return $popsock

 proc get_status { } {
        global popsock 
        set my_stat [pop3::status $popsock]
        set new_messages [lindex $my_stat 0]
        show_it $new_messages
        return $new_messages

 proc show_it { var } {
        tk_messageBox -icon info -title "Messages" \
        -parent . -type ok -message "You have $var new messages."       

         #xception-handling, ...  Warn about deletes.  Point to pertinent RFC for index base and other definitions.]

 proc tkerror { errmsg } {
        global errorCode
        set msg [format "Error: %s\nResult: %s." $errmsg $errorCode]
        tk_messageBox -parent . -title Error -type ok\
                -icon error -message $msg

 wm protocol . WM_DELETE_WINDOW "clean_up"

 proc clean_up { } {
        global popsock
        if {$popsock != ""} {

 wm title . "Check Mail"
 wm deiconify .
 focus .eh

NOTE: Most pop3 servers only allow one client to connect at a time. That's what RFC 1939 [L2 ] requires.

AK: Actually the note above is too restrictive. The RFC says that a server should allow only one client/connection per maildrop. In other words, a pop3 server managing N different maildrops can have N connections from N clients, if these connections all talk to a different maildrop.

NOTE: RFC 1939 [L3 ] (which is a standard) is updated by RFC 1957 [L4 ] (informational), and RFC 2449 [L5 ] (standards track). -EE

(My information comes from http://www.rfc-editor.org/ , which is pretty much the canonical source of RFC information.)

Only marginally on topic, we also have RFC 2384 [L6 ].

Remember, one doesn't do a package require of tcllib itself, but of the modules INSIDE tcllib...

If you had only an old, socket-less Tcl at hand, but also Expect, you could exploit the Unix telnet binary, as Glenn Jackman's checkpopmail [L7 ] exemplifies.

Or maybe, you just copy Glenn Jackman's script once you realize that you want to check your IMAP mail account -- see Checking your IMAP Mail with Expect.

A small mail check utility. Checks a mailbox every 10 minutes. Displays number of messages in the mailbox and time of the last check. Number of messages is also displayed on title.

 package require pop3
 # timer indentifier
 set tid {}

 label .l -text "connecting...                          "
 button .chnow -text {Check now} -command {if {$tid!={}} {after cancel $tid} ;checkMailBox}
 pack .l .chnow

 proc checkMailBox {} {
        global tid
         wm title . "checking..."
         if {[catch {set mch [::pop3::open -retr-mode list your-pop-server your-user-name your-password]} loginErr]} {
                 tk_messageBox -message $loginErr
                 .l configure -text "[clock format [clock seconds] -format %H:%M:%S]: - ERROR                "
                 wm title . "m - ERROR"
         } else {
                 set mBoxStat [::pop3::status $mch]
                 set systemTime [clock seconds]
                 .l configure -text "[clock format $systemTime -format %H:%M:%S]: [lindex $mBoxStat 0] messages on the server..."
                 ::pop3::close $mch 
                 wm title . "m - [lindex $mBoxStat 0]"
                 set tid [after [expr 1000 * 60 * 10] checkMailBox]


Luciano ES Also check out similar mail box polling code at procmail sux

escargo 19 Aug 2003 - Has anyone build any anti-spam tools using pop3 as a base? The spam filters provided by my ISP are not ones I can control, so I can't tune them myself. Also, their pattern matching leaves a lot to be desired (things that are obvious to the human eye are defeating the scanners). Unlike SpamMap, I would like a program I could run from my client machine that would postprocess e-mail from my ISP, and have it ask me about killing things that it sees. This would save me a lot of manual clicking and so forth to create filters on my existing mail client.

LES - I started something just like that, but haven't found the time to finish it. Basically, it's what I suggest in procmail sux. Look for that line that says "filtering code here" and off you go.

[BR] 2003-09-24 - See A Primitive POP3-based Spam Killer.

[DH] 2005-10-28 - Added a $ sign to first "tid" reference to fix syntax error in line six of the "small mail check utility". The "Check now" button was broken without it.

See POP3 with TLS for a simple take at using TLS with the POP3 package in tcllib.

See pop3d for a simple POP3 server.