200 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			Factor
		
	
	
			
		
		
	
	
			200 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			Factor
		
	
	
! Copyright (C) 2009 Elie Chaftari.
 | 
						|
! See http://factorcode.org/license.txt for BSD license.
 | 
						|
USING: accessors annotations arrays assocs calendar combinators
 | 
						|
fry hashtables io io.crlf io.encodings.utf8 io.sockets
 | 
						|
io.streams.duplex io.timeouts kernel make math math.parser
 | 
						|
math.ranges namespaces prettyprint sequences splitting
 | 
						|
strings ;
 | 
						|
IN: pop3
 | 
						|
 | 
						|
TUPLE: pop3-account
 | 
						|
# host port timeout user pwd stream capa count list
 | 
						|
uidls messages ;
 | 
						|
 | 
						|
: <pop3-account> ( -- pop3-account )
 | 
						|
    pop3-account new
 | 
						|
        110 >>port
 | 
						|
        1 minutes >>timeout ;
 | 
						|
 | 
						|
: account ( -- pop3-account ) pop3-account get ;
 | 
						|
 | 
						|
TUPLE: message # uidl headers from to subject size ;
 | 
						|
 | 
						|
<PRIVATE
 | 
						|
 | 
						|
: stream ( -- duplex-stream ) account stream>> ;
 | 
						|
 | 
						|
: <message> ( -- message ) message new ; inline
 | 
						|
 | 
						|
TUPLE: raw-source top headers content ;
 | 
						|
 | 
						|
: <raw-source> ( -- raw-source ) raw-source new ; inline
 | 
						|
 | 
						|
: raw ( -- raw-source ) raw-source get ;
 | 
						|
 | 
						|
: set-read-timeout ( -- )
 | 
						|
    stream [
 | 
						|
        account timeout>> timeouts
 | 
						|
    ] with-stream* ;
 | 
						|
 | 
						|
: get-ok ( -- )
 | 
						|
    stream [
 | 
						|
        readln dup "+OK" head? [ drop ] [ throw ] if
 | 
						|
    ] with-stream* ;
 | 
						|
 | 
						|
: get-ok-and-total ( -- total )
 | 
						|
    stream [
 | 
						|
        readln dup "+OK" head? [
 | 
						|
            " " split second string>number dup account count<<
 | 
						|
        ] [ throw ] if
 | 
						|
    ] with-stream* ;
 | 
						|
 | 
						|
: get-ok-and-uidl ( -- uidl )
 | 
						|
    stream [
 | 
						|
        readln dup "+OK" head? [
 | 
						|
            " " split last
 | 
						|
        ] [ throw ] if
 | 
						|
    ] with-stream* ;
 | 
						|
 | 
						|
: command ( string -- ) write crlf flush get-ok ;
 | 
						|
 | 
						|
: command-and-total ( string -- total ) write crlf flush
 | 
						|
    get-ok-and-total ;
 | 
						|
 | 
						|
: command-and-uidl ( string -- uidl ) write crlf flush
 | 
						|
    get-ok-and-uidl ;
 | 
						|
 | 
						|
: associate-split ( seq -- assoc )
 | 
						|
    [ " " split1 ] H{ } map>assoc ;
 | 
						|
 | 
						|
: split-map ( seq -- assoc )
 | 
						|
    associate-split [ [ string>number ] dip ] assoc-map ;
 | 
						|
 | 
						|
: (readlns) ( -- )
 | 
						|
    readln dup "." = [ , ] dip [ (readlns) ] unless ;
 | 
						|
 | 
						|
: readlns ( -- seq ) [ (readlns) ] { } make but-last ;
 | 
						|
 | 
						|
: (list) ( -- )
 | 
						|
    stream [
 | 
						|
        "LIST" command
 | 
						|
        readlns account list<<
 | 
						|
    ] with-stream* ;
 | 
						|
 | 
						|
: (uidls) ( -- )
 | 
						|
    stream [
 | 
						|
        "UIDL" command
 | 
						|
        readlns account uidls<<
 | 
						|
    ] with-stream* ;
 | 
						|
 | 
						|
PRIVATE>
 | 
						|
 | 
						|
: >user ( name -- )
 | 
						|
    [ stream ] dip '[
 | 
						|
        "USER " _ append command
 | 
						|
    ] with-stream* ;
 | 
						|
 | 
						|
: >pwd ( password -- )
 | 
						|
    [ stream ] dip '[
 | 
						|
        "PASS " _ append command
 | 
						|
    ] with-stream* ;
 | 
						|
 | 
						|
: connect ( pop3-account -- )
 | 
						|
    [
 | 
						|
        [ host>> ] [ port>> ] bi
 | 
						|
        <inet> utf8 <client> drop
 | 
						|
    ] keep swap >>stream
 | 
						|
    {
 | 
						|
        [ pop3-account set ]
 | 
						|
        [ user>> [ >user ] when* ]
 | 
						|
        [ pwd>> [ >pwd ] when* ]
 | 
						|
    } cleave
 | 
						|
    set-read-timeout
 | 
						|
    get-ok ;
 | 
						|
 | 
						|
: capa ( -- array )
 | 
						|
    stream [
 | 
						|
        "CAPA" command
 | 
						|
        readlns dup account capa<<
 | 
						|
    ] with-stream* ;
 | 
						|
 | 
						|
: count ( -- n )
 | 
						|
    stream [
 | 
						|
        "STAT" command-and-total
 | 
						|
    ] with-stream* ;
 | 
						|
 | 
						|
: list ( -- assoc )
 | 
						|
    (list) account list>> split-map ;
 | 
						|
 | 
						|
: uidl ( message# -- uidl )
 | 
						|
    [ stream ] dip '[
 | 
						|
        "UIDL " _ number>string append command-and-uidl
 | 
						|
    ] with-stream* ;
 | 
						|
 | 
						|
: uidls ( -- assoc )
 | 
						|
    (uidls) account uidls>> split-map ;
 | 
						|
 | 
						|
: top ( message# #lines -- seq )
 | 
						|
    <raw-source> raw-source set
 | 
						|
    [ stream ] 2dip '[
 | 
						|
        "TOP " _ number>string append " "
 | 
						|
        append _ number>string append
 | 
						|
        command
 | 
						|
        readlns dup raw top<<
 | 
						|
    ] with-stream* ;
 | 
						|
 | 
						|
: headers ( -- assoc )
 | 
						|
    raw top>> {
 | 
						|
        [
 | 
						|
            [ dup "From:" head?
 | 
						|
                [ raw [ swap suffix ] change-headers drop ]
 | 
						|
                [ drop ] if
 | 
						|
            ] each
 | 
						|
        ]
 | 
						|
        [
 | 
						|
            [ dup "To:" head?
 | 
						|
                [ raw [ swap suffix ] change-headers drop ]
 | 
						|
                [ drop ] if
 | 
						|
            ] each
 | 
						|
        ]
 | 
						|
        [
 | 
						|
            [ dup "Subject:" head?
 | 
						|
                [ raw [ swap suffix ] change-headers drop ]
 | 
						|
                [ drop ] if
 | 
						|
            ] each
 | 
						|
        ]
 | 
						|
    } cleave raw headers>> associate-split ;
 | 
						|
 | 
						|
: retrieve ( message# -- seq )
 | 
						|
    [ stream ] dip '[
 | 
						|
        "RETR " _ number>string append command
 | 
						|
        readlns dup raw content<<
 | 
						|
    ] with-stream* ;
 | 
						|
 | 
						|
: delete ( message# -- )
 | 
						|
    [ stream ] dip '[
 | 
						|
        "DELE " _ number>string append command
 | 
						|
    ] with-stream* ;
 | 
						|
 | 
						|
: reset ( -- )
 | 
						|
    stream [ "RSET" command ] with-stream* ;
 | 
						|
 | 
						|
: consolidate ( -- seq )
 | 
						|
    count zero? [ "No mail for account." ] [
 | 
						|
        1 account count>> [a,b] [
 | 
						|
            {
 | 
						|
                [ 0 top drop ]
 | 
						|
                [ <message> swap >># ]
 | 
						|
                [ uidls at >>uidl ]
 | 
						|
                [ list at >>size ]
 | 
						|
            } cleave
 | 
						|
            "From:" headers at >>from
 | 
						|
            "To:" headers at >>to
 | 
						|
            "Subject:" headers at >>subject
 | 
						|
            account [ swap suffix ] change-messages drop
 | 
						|
        ] each account messages>>
 | 
						|
    ] if ;
 | 
						|
 | 
						|
: close ( -- )
 | 
						|
    stream [ "QUIT" command ] with-stream ;
 |