#!/p/kd/fdc/mm-pop/wermit + # POP.KSC # Mon Dec 5 18:32:40 2005 # Operating parameters .verbose = 1 # 1 = Print progress messages; 0 = silent running .debug = 0 # 1 = Create log; don't delete temp files; print extra messages .clean = 1 # 1 = Delete old stale temp files as well as current ones # Columbia University POP3/SSL client. # Fetches all new mail just like old movemail did. # Does all byte stuffing and unstuffing required by POP. # Adds BSD-format envelope: "From user@host date-time". # Date-time is taken from Date: header and converted accurately to local time. # # This script is for use only on the Unix hosts. It assumes a Unix file # system and that the mailbox name is the same as the username ($USER). # # Call with: # [arg1] [arg2] [arg3] # arg1 = host:port of POP server. Default = pop.:995. # arg2 = Destination file. Default = pop_ndate_ntime_pid.txt in current dir. # arg3 = Password for POP server. If not given, password prompted for. # # Requires C-Kermit 8.0.212 Dev.07 20051204 or later because of: # . \flopx() - for building file and host names # . \femailaddress() - for building From line (source address) # . \fcvtdate() asctime format - for building From line (date-time) # . INPUT /CLEAR /NOWRAP - for speed (2x faster than line-by line) # . A bugfix to FOPEN /APPEND to allow it to open nonexistent files. # . A new SSL protocol option /SSL-RAW that treats incoming 0xff as data. # # Usage: Run this script "bare" and it prompts you for your password and # fetches your new mail into ~/.pop_yyyymmdd_ntime_pid.txt. # # To use within MM as a movemail replacement, tell mm to "set movemail-path" # to the path of this script and create a .mm-use-pop file in your home # directory. # # Works best with the new version of MM that has been modified to be aware # that it's fetching files from a network server rather than just copying # them from one directory to another on the local file system. # # F. da Cruz, Columbia University, Nov-Dec 2005. # # Updated Fri Jan 19 17:22:07 2007 to byte-stuff first line of message body. local ON_CTRLC def ON_CTRLC { # Ctrl-C trap echo \m(name) INTERRUPTED... if open connection { set input echo on pop3cmd QUIT # Or RSET? hangup } echo Bye. } .name := \flopx(\v(cmdfile),/) # Name of this script if kerbang .name := \flopx(\%0,/) # (for messages) .rc = 0 # Default return code if not def debug .debug = 0 # Default operating parameters if not def verbose .verbose = 0 if not def clean .clean = 0 if def debug { .clean = 0, .verbose = 2 } .columbia = 0 # Governs any CU-specific stuff .domain := \flopx(\v(host),.,2) # Domain, e.g. columbia.edu if eq "\m(domain)" "columbia.edu" .columbia = 1 .user := \$(USER) # User .host := pop.\m(domain) # Default POP3 host if \m(columbia) if exist ~/.cyrustt .host := mail.\m(domain) .port = 995 # Default TCP port of POP3 server .t1 := \v(ntime) # Current time for stats define FATAL { # Fatal error handler echo "\m(name) [\%1] \%2" set input echo on if open connection lineout RSET # Avoid recursion of FATAL .rc = 1 # (by not calling pop3cmd) goto end } if not available tls { # Need SSL for POP server connection echo \&@[0] fatal FATAL "Script interpreter lacks SSL/TLS" } if llt "\v(buildid)" "20051204" { # Minimum Kermit version echo \&@[0] fatal FATAL "C-Kermit 8.0.212 Dev.07 or later required" } if def \%1 { # First command-line arg if \findex(:,\%1) { # can be host:port .host := \fword(\%1,1,:) .port := \fword(\%1,2,:) } else if not eq "\%1" "-" .host := \%1 } if def \%2 .msgfile := \fcontents(\%2) # Arg 2 is destination file if def \%3 .pass := \fcontents(\%3) # Arg 3 is password .file := ~/.pop_\v(ndate)_\v(ntime)_\v(pid) # Unique prefix for filenames set auth tls certs-ok on # If client doesn't like server cert set exit warning off # No prompting on exit set quiet on # Suprress Kermit messages if \m(verbose) set input echo on # Verbosity of this script if \m(debug) { # Debugging log debug set telnet debug on set auth tls verbose on set auth tls debug on log session \m(file).log set quiet off } else { # Not debugging set auth tls verbose off set auth tls debug off set function diagnostics off } if def TESTDUMMY { # FOR TESTING ONLY .host = mail.columbia.edu .user = ct2217 } while not def pass { # Get password if necessary askq /echo:* pass \m(user)'s Password: } # Check POP server's response to most recent command # Argument #1 is the command name define ERRCHK { # Often have to wait a LONG time for response to PASS command. .\%9 = 4 undef \%8 if eq "\%1" "PASS" .\%9 = 12 clear input for i 1 \%9 1 { minput 15 +OK -OK +ERR -ERR if success break if not open connection fatal \%1 "Connection Lost" echo "No response in \feval(\m(i)*15) seconds - \%8waiting..." if = \m(i) 1 .\%8 = "still " } .\%9 := \v(minput) input /clear 10 \13\10 switch \%9 { :0, fatal \%1 "No response" :[12], end 0 :3, fatal \%1 "+ERR\fsubstitute(\v(input),\13\10,)" :4, fatal \%1 "-ERR\fsubstitute(\v(input),\13\10,)" :default, fatal \%1 "Unexpected error" } } # Send a POP3 command and check the result # Example: "pop3cmd DELE 14" define POP3CMD { if not open connection fatal POP3CMD "Connection closed" if \m(verbose) { if eq "\%1" "PASS" echo PASS XXXXXXXX } lineout \%* errchk \%1 } # GETMSG - Download the given message from the POP server. # Call with message number. # define GETMSG { local t1 t2 if not defined \%1 fatal GETMSG "Missing message number" if not numeric \%1 fatal GETMSG "Invalid message number: \%1" .eom := "\13\10.\13\10" # End of message pop3cmd RETR \%1 .hdrfile := \m(file).hdr # Put msg headers in temp file fopen /write \%h \m(hdrfile) if fail fatal FOPEN/WRITE "Failed: \m(hdrfile)" .t1 := \v(ntime) # Time started .\%n = 0 # Message counter # First read the message headers into a temporary file # extracting the sender's address and the date-time... while open connection { input /clear 20 \13\10 if fail fatal GETMSG "Timed out reading headers" .s := \v(input) if == \%n 0 { if == \findex(+OK,\m(s)) 1 continue if eq "\m(s)" "\13\10" continue } incr \%n if eq "\m(s)" "\13\10" break if eq "\s(s[1:6])" "From: " { .addr := \femailaddr(\m(s)) if not def addr .addr = "unknown@nowhere.com" } if eq "\s(s[1:6])" "Date: " { .time := \fcvtdate(\s(s[7]),4) if not def time .time := \fcvtdate(,4) } fwrite /string \%h \freplace(\m(s),\13,) if fail fatal "GETMSG FWRITE" Headers } fclose \%h # Write the BSD "From " line to the message file # followed by the headers and a blank line... fwrite /line \%o "From \m(addr) \m(time)" fopen /read \%h \m(hdrfile) while true { fread /line \%h line if fail break fwrite /line \%o \m(line) if fail fatal "GETMSG FWRITE" Body } fclose \%h # Delete the temporary file del /quiet \m(hdrfile) fwrite /line \%o # Read and copy the message itself doing all the byte stuffing and # unstuffing required by the POP RFCs and watching for the EOM... set flag off # FLAG ON = success .\%q = 0 # For identifying the first line while ( open connection && not flag ) { .oldinput := \fright(\v(input),8) input /clear /nowrap 4 \m(eom) if success { .s := {\freplace(\v(input),\m(eom),\13\10)} set flag on } if not flag .s := \v(input) if not \%q { # First line of message if ( eq "From " "\s(s[1:5])" ) .s := >\m(s) if ( eq ".." "\s(s[1:2])" ) .s := >\m(s[2]) } incr \%q .oldinput := \m(oldinput)\fsubstr(\v(input),1,8) if \findex(\m(eom),\m(oldinput)) set flag on if ( == \v(instatus) 6 || == \v(instatus) 1 || == \v(instatus) 0 ) { .s := {\freplace(\m(s),{\13\10From },{\13\10>From })} .s := {\freplace(\m(s),{\13\10..},{\13\10.})} fwrite /string \%o {\freplace(\m(s),\13\10,\10)} } else { break } .t2 ::= \v(ntime) - \m(t1) # Elapsed time if > \m(t2) 300 if fail fatal GETMSG "Timed out reading body" } if flag { # Success set input echo on fwrite /line \%o # Add blank line to end of message pop3cmd DELE \%1 # Delete message from server set input echo off } else { # Failure .rc = 1 # Set return code goto end # Quit } } # Action starts here set host \m(host) \m(port) /SSL-RAW # Connect to the POP server if fail fatal "SET HOST" "Connection failed" errchk if > \m(verbose) 1 { # Some messages echo CONNECT OK \v(timestamp) echo AUTHTYPE=\v(authtype) echo SECURE=\v(secure) } pop3cmd USER \m(user) # Log in over encrypted connection set input echo off pop3cmd PASS \m(pass) if \m(verbose) set input echo on .listfile := \m(file).tmp # Get message list into tmp file fopen /write \%o \m(listfile) if fail fatal FOPEN/WRITE "Failed: \m(listfile)" pop3cmd LIST .\%n = 0 # Message counter while true { input /clear 30 \13\10 .x := \v(input) if fail fatal LIST "Timed out" if \findex(+OK,\m(x)) continue if eq "\m(x)" ".\13\10" break fwrite /string \%o \v(input) increment \%n } fclose \%o if not \%n { if \m(verbose) echo "NO MESSAGES", forward end } # Loop through message list and fetch each message fopen /read \%i \m(listfile) if fail fatal FOPEN/READ "Failed: \m(listfile)" .\%9 := \ffiles(~/.pop_*_*_*.txt,&a) # A temp message file already exists? if > .\%9 0 { # This allows for repeated invocations .tmpmsgfile := \&a[\fdim(&a)] } else { .tmpmsgfile := \m(file).txt } fopen /append \%o \m(tmpmsgfile) if fail fatal FOPEN/WRITE "Failed: \m(tmpmsgfile)" set input echo off # Don't echo message contents while true { # Message-getting loop fread /line \%i line if fail break .n := \fword(\m(line),1) if \m(verbose) echo RETR MESSAGE \m(n)... getmsg \m(n) } fclose \%o # Finished - close message file if def msgfile { if exist \m(tmpmsgfile) if > \fsize(\m(tmpmsgfile)) 0 { if exist \m(msgfile) { rename \m(msgfile) \m(msgfile).tmp copy /append \m(tmpmsgfile) \m(msgfile).tmp rename \m(msgfile).tmp \m(msgfile) delete \m(tmpmsgfile) } else { rename \m(tmpmsgfile) \m(msgfile) } } } :END # Common exit point fclose all # Close all open files if open connection pop3cmd QUIT # Log out from POP server hangup # Ensure connection is really closed .t2 := \v(ntime) # Time for stats .t3 ::= \m(t2) - \m(t1) if < \m(t3) 0 increment t3 86400 if \m(verbose) echo "ELAPSED TIME \m(t3) sec" if \m(clean) { # Clean temp files. if \ffiles(~/.pop_*_*_*.tmp) del /quiet ~/.pop_*_*_*.tmp if \ffiles(~/.pop_*_*_*.hdr) del /quiet ~/.pop_*_*_*.hdr } else if not \m(debug) { if exist "\m(listfile)" del /quiet \m(listfile) if exist "\m(hdrfile)" del /quiet \m(hdrfile) } # If this script was invoked directly from the shell, exit. # If invoked from the Kermit prompt or from another Kermit script, return. if kerbang exit \m(rc) end \m(rc)