Updated 2012-01-07 15:05:49 by dkf

Luciano ES, July 6, 2003. Many people use procmail to filter their mail and do other funny stuff with it. Also, many people TRY to use procmail to filter their mail and do other funny stuff with it, and succumb to frustration. Too many complex rules and settings to make the damn thing work and maybe you're not that interested in becoming a procmail expert. There are so many other interesting things in life!

If you know Tcl, your headaches are over. Tcl can do procmail's job, probably much better than procmail itself.

The idea is not mine. I took it from Rildo Pragana's Web site: [1]. I just rewrote it with better variable names and added sound notification. All I know is that it was posted at some very enthusiastic Tcl fan's Web page, with plenty of encouragement for anyone to reuse the code.
 # \
 exec tclsh "$0" "$@"

 package require sound
 proc   playwav {myWavFile}     { snack::sound s -file $myWavFile; s play -block 1 }

 # The two lines above are optional. If you don't want any sound notifications,
 # remove them and also all lines below that contain "playwav"
 package require pop3
 proc func_GetPop {argBoxFile argPopHost argLogin argPassword}  {
        set myPop [ pop3::open $argPopHost $argLogin $argPassword ]
        set myFP [ open $argBoxFile a+ ]
        set myStatus [ pop3::status $myPop ]
                playwav /path/to/sounds/accessing.wav
        set myMessageCount [ lindex $myStatus 0 ] 
        puts "\nmyMessageCount is $myMessageCount\n"
        puts "status: $myStatus"
        for     {set i 1} {$i <= $myMessageCount} {incr i}      {
                playwav /path/to/sounds/baqueta.wav
                puts "retrieving msg $i"
                set body [ lindex [ pop3::retrieve $myPop $i ] 0 ]
                set timestamp [ clock format [ clock seconds ] -gmt 1 ]
# -- 

 # now $body has the message's header and body, ready to be appended to your
 # mailbox file. If you want to filter it, filter it now.


# --  

                puts $myFP "From pop3-tclmail $timestamp\n$body"
 #      pop3::delete $myPop 1 end
 # uncomment line above to delete downloaded messages from the server
        pop3::close $myPop
        close $myFP
        puts "--------------"
        if      {$myMessageCount > 0}   {
                playwav /path/to/sounds/blimp2.wav
        } else  { playwav /path/to/sounds/beep5.wav }
 # WARNING - edit options below according to your own settings
 set myBoxFile {~/Mail/Mailbox.txt}
 set myPopHost {pop3.myhost.com}
 set myLogin {your login here}
 set myPassword {your password here}

 # finally, run the function
 func_GetPop $myBoxFile $myPopHost $myLogin $myPassword

# ----

Hmm - procmail isn't so bad, and handles things like locking and other stuff. I agree that it can be pretty complicated. If you do happen to want to use Tcl in your procmail handlers, here is some code I've used for a filter that reads a message, fiddles with it, then writes it back out. The procmail filter that invokes it is

  :0 wf :$PMDIR/filter$LOCKEXT
  | $PMDIR/filter1

And this is the Procmail/filter1 script (add suitable #!tclsh line)
  proc BailOut { message } {
    global fromline headers body
    puts stdout "${fromline}${headers}X-Filter-Error: $message\n\n$body"
    exit 0

  # Split the message into the initial from line,
  # the headers, and the body

  set fromline ""
  set headers ""
  set body ""

  while {![eof stdin]} {
    gets stdin fromline
    if {[regexp {^(from |From )} $fromline]} {
    if {[string length $fromline]} {
      set fromline ""
      set headers $fromline\n
  while {![eof stdin]} {
    gets stdin line
    if {[string length [string trim $line]] == 0} {
    append headers $line\n
  set body [read stdin]

  if {![regexp {From: *([^\n]+)\n} $headers _ from]} {
    BailOut "No From Header"

  # Now do various stuff here with regexp's over
  # the headers or body, then frob the headers

  append headers "X-Mumble: Kilroy was here\n"

  # Output the message again - this is a pure procmail filter

  puts stdout "${fromline}${headers}\n${body}"
  exit 0