Tcl Library Source Code

Changes On Branch smtp-tlsimport-tkt-83d674b2dd
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Changes In Branch smtp-tlsimport-tkt-83d674b2dd Excluding Merge-Ins

This is equivalent to a diff from e2d7b04944 to 657782a889

2018-06-19
04:12
mime / smtp <EF> Ticket [83d674b2dd] Extended TLS support to native TLS connections. Bumped to version 1.5. check-in: 9073bd414a user: aku tags: trunk
2018-05-18
17:47
oauth / oauth <B> Ticket [8fd2561785] Fixed missing escape-quoting of an opening brace in a quote-quoted regexp pattern in a proc. The outer {...} of the proc body governs. Fixed bogus variable name in oauth::QuoteValues. Added an empty test file with a no-op placeholder test. This will catch at least the file not loading properly. Version bumped to 1.0.2 check-in: aec286d43d user: aku tags: trunk
17:13
mime / smtp <EF> Ticket [83d674b2dd] New option -tlsimport to support native TLS for smtp. Updated documentation. Version bumped to 1.5 Closed-Leaf check-in: 657782a889 user: aku tags: smtp-tlsimport-tkt-83d674b2dd
04:42
json / json <D> Ticket [868b8ebe79] json / json::write <D> Extended the documentation of both packages to refer to the other. No version changes. Regenerated the online documentation. check-in: e2d7b04944 user: aku tags: trunk
04:15
log / log <EF> Ticket [19607f927b] Merged new `logsubst` command to prevent execution of expensive message construction until actually needed. Version bumped to 1.4. Thanks to Harald for idea and implementation. check-in: ea802e332b user: aku tags: trunk

Changes to modules/mime/pkgIndex.tcl.

1
2
3
4
if {![package vsatisfies [package provide Tcl] 8.3]} {return}
package ifneeded smtp 1.4.5 [list source [file join $dir smtp.tcl]]
if {![package vsatisfies [package provide Tcl] 8.5]} {return}
package ifneeded mime 1.6 [list source [file join $dir mime.tcl]]

|


1
2
3
4
if {![package vsatisfies [package provide Tcl] 8.3]} {return}
package ifneeded smtp 1.5 [list source [file join $dir smtp.tcl]]
if {![package vsatisfies [package provide Tcl] 8.5]} {return}
package ifneeded mime 1.6 [list source [file join $dir mime.tcl]]

Changes to modules/mime/smtp.man.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

[comment {-*- tcl -*- doctools manpage}]
[manpage_begin smtp n 1.4.5]
[see_also ftp]
[see_also http]
[see_also mime]
[see_also pop3]
[copyright {1999-2000 Marshall T. Rose and others}]
[moddesc   {smtp client}]
[titledesc {Client-side tcl implementation of the smtp protocol}]
[category  Networking]
[require Tcl]
[require mime [opt 1.5.4]]
[require smtp [opt 1.4.5]]
[description]
[para]

The [package smtp] library package provides the client side of the
Simple Mail Transfer Protocol (SMTP) (1) (2).

[list_begin definitions]
>

|










|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
[vset VERSION 1.5]
[comment {-*- tcl -*- doctools manpage}]
[manpage_begin smtp n [vset VERSION]]
[see_also ftp]
[see_also http]
[see_also mime]
[see_also pop3]
[copyright {1999-2000 Marshall T. Rose and others}]
[moddesc   {smtp client}]
[titledesc {Client-side tcl implementation of the smtp protocol}]
[category  Networking]
[require Tcl]
[require mime [opt 1.5.4]]
[require smtp [opt [vset VERSION]]]
[description]
[para]

The [package smtp] library package provides the client side of the
Simple Mail Transfer Protocol (SMTP) (1) (2).

[list_begin definitions]
69
70
71
72
73
74
75









76
77
78
79
80
81
82
value (the -header option may occur zero or more times).

[def [option -usetls]]

This package supports the RFC 3207 TLS extension (3) by default provided the
tls package is available. You can turn this off with this boolean option.










[def [option -tlspolicy]]

This option lets you specify a command to be called if an error occurs
during TLS setup. The command is called with the SMTP code and diagnostic
message appended. The command should return 'secure' or 'insecure' where
insecure will cause the package to continue on the unencrypted channel.
Returning 'secure' will cause the socket to be closed and the next server







>
>
>
>
>
>
>
>
>







70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
value (the -header option may occur zero or more times).

[def [option -usetls]]

This package supports the RFC 3207 TLS extension (3) by default provided the
tls package is available. You can turn this off with this boolean option.

[def [option -tlsimport]]

This boolean flag is [const false] by default.

When this flag is set the package will import TLS on a sucessfully
opened channel. This is needed for connections using native TLS
negotiation instead of [const STARTTLS]. The [package tls] package
is automatically required when needed.

[def [option -tlspolicy]]

This option lets you specify a command to be called if an error occurs
during TLS setup. The command is called with the SMTP code and diagnostic
message appended. The command should return 'secure' or 'insecure' where
insecure will cause the package to continue on the unencrypted channel.
Returning 'secure' will cause the socket to be closed and the next server

Changes to modules/mime/smtp.tcl.

97
98
99
100
101
102
103


104
105
106
107
108
109
110
#                          is 120 seconds.
#             -usetls      A boolean flag. If the server supports it and we
#                          have the package, use TLS to secure the connection.
#             -tlspolicy   A command to call if the TLS negotiation fails for
#                          some reason. Return 'insecure' to continue with
#                          normal SMTP or 'secure' to close the connection and
#                          try another server.


#             -username    These are needed if your SMTP server requires
#             -password    authentication.
#
# Results:
#	Message is sent.  On success, return "".  On failure, throw an
#       exception with an error code and error message.








>
>







97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
#                          is 120 seconds.
#             -usetls      A boolean flag. If the server supports it and we
#                          have the package, use TLS to secure the connection.
#             -tlspolicy   A command to call if the TLS negotiation fails for
#                          some reason. Return 'insecure' to continue with
#                          normal SMTP or 'secure' to close the connection and
#                          try another server.
#             -tlsimport   after a succesfull socket command, import tls on
#                          channel - used for native smtps negotiation
#             -username    These are needed if your SMTP server requires
#             -password    authentication.
#
# Results:
#	Message is sent.  On success, return "".  On failure, throw an
#       exception with an error code and error message.

125
126
127
128
129
130
131

132
133
134
135
136
137
138
    set originator ""
    set recipients ""
    set servers [list localhost]
    set client "" ;# default is set after options processing
    set ports [list 25]
    set tlsP 1
    set tlspolicy {}

    set username {}
    set password {}

    array set header ""

    # lowerL will contain the list of header keys (converted to lower case) 
    # specified with various -header options.  mixedL is the mixed-case version







>







127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
    set originator ""
    set recipients ""
    set servers [list localhost]
    set client "" ;# default is set after options processing
    set ports [list 25]
    set tlsP 1
    set tlspolicy {}
    set tlsimport 0
    set username {}
    set password {}

    array set header ""

    # lowerL will contain the list of header keys (converted to lower case) 
    # specified with various -header options.  mixedL is the mixed-case version
150
151
152
153
154
155
156

157
158
159
160
161
162
163
    foreach {option value} $args {
        switch -- $option {
            -atleastone {set aloP   [boolean $value]}
            -debug      {set debugP [boolean $value]}
            -queue      {set queueP [boolean $value]}
            -usetls     {set tlsP   [boolean $value]}
            -tlspolicy  {set tlspolicy $value}

	    -maxsecs    {set maxsecs [expr {$value < 0 ? 0 : $value}]}
            -header {
                if {[llength $value] != 2} {
                    error "-header expects a key and a value, not $value"
                }
                set mixed [lindex $value 0]
                set lower [string tolower $mixed]







>







153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
    foreach {option value} $args {
        switch -- $option {
            -atleastone {set aloP   [boolean $value]}
            -debug      {set debugP [boolean $value]}
            -queue      {set queueP [boolean $value]}
            -usetls     {set tlsP   [boolean $value]}
            -tlspolicy  {set tlspolicy $value}
            -tlsimport  {set tlsimport [boolean $value]}
	    -maxsecs    {set maxsecs [expr {$value < 0 ? 0 : $value}]}
            -header {
                if {[llength $value] != 2} {
                    error "-header expects a key and a value, not $value"
                }
                set mixed [lindex $value 0]
                set lower [string tolower $mixed]
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431

    # Create smtp token, which essentially means begin talking to the SMTP
    # server.
    set token [initialize -debug $debugP -client $client \
		                -maxsecs $maxsecs -usetls $tlsP \
                                -multiple $bccP -queue $queueP \
                                -servers $servers -ports $ports \
                                -tlspolicy $tlspolicy \
                                -username $username -password $password]

    if {![string match "::smtp::*" $token]} {
	# An error occurred and $token contains the error info
	array set respArr $token
	return -code error $respArr(diagnostic)
    }







|







421
422
423
424
425
426
427
428
429
430
431
432
433
434
435

    # Create smtp token, which essentially means begin talking to the SMTP
    # server.
    set token [initialize -debug $debugP -client $client \
		                -maxsecs $maxsecs -usetls $tlsP \
                                -multiple $bccP -queue $queueP \
                                -servers $servers -ports $ports \
                                -tlspolicy $tlspolicy -tlsimport $tlsimport \
                                -username $username -password $password]

    if {![string match "::smtp::*" $token]} {
	# An error occurred and $token contains the error info
	array set respArr $token
	return -code error $respArr(diagnostic)
    }
591
592
593
594
595
596
597


598
599
600
601
602
603
604
#                          being sent should be queued for later delivery.
#             -servers     A list of mail servers that could process the
#                          request.
#             -ports       A list of ports on mail servers that could process
#                          the request (one port per server-- defaults to 25).
#             -usetls      A boolean to indicate we will use TLS if possible.
#             -tlspolicy   Command called if TLS setup fails.


#             -username    These provide the authentication information 
#             -password    to be used if needed by the SMTP server.
#
# Results:
#	On success, return an smtp token.  On failure, throw
#       an exception with an error code and error message.








>
>







595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
#                          being sent should be queued for later delivery.
#             -servers     A list of mail servers that could process the
#                          request.
#             -ports       A list of ports on mail servers that could process
#                          the request (one port per server-- defaults to 25).
#             -usetls      A boolean to indicate we will use TLS if possible.
#             -tlspolicy   Command called if TLS setup fails.
#             -tlsimport   after a succesfull socket command, import tls on
#                          channel - used for native smtps negotiation
#             -username    These provide the authentication information 
#             -password    to be used if needed by the SMTP server.
#
# Results:
#	On success, return an smtp token.  On failure, throw
#       an exception with an error code and error message.

612
613
614
615
616
617
618

619
620
621
622
623
624
625
    variable $token
    upvar 0 $token state

    array set state [list afterID "" options "" readable 0]
    array set options [list -debug 0 -client localhost -multiple 1 \
                            -maxsecs 120 -queue 0 -servers localhost \
                            -ports 25 -usetls 1 -tlspolicy {} \

                            -username {} -password {}]
    array set options $args
    set state(options) [array get options]

    # Iterate through servers until one accepts a connection (and responds
    # nicely).
   







>







618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
    variable $token
    upvar 0 $token state

    array set state [list afterID "" options "" readable 0]
    array set options [list -debug 0 -client localhost -multiple 1 \
                            -maxsecs 120 -queue 0 -servers localhost \
                            -ports 25 -usetls 1 -tlspolicy {} \
                            -tlsimport 0 \
                            -username {} -password {}]
    array set options $args
    set state(options) [array get options]

    # Iterate through servers until one accepts a connection (and responds
    # nicely).
   
638
639
640
641
642
643
644




645
646
647
648
649
650
651

        if {[info exists state(sd)]} {
            unset state(sd)
        }

        if {[set code [catch {
            set state(sd) [socket -async $server $port]




            fconfigure $state(sd) -blocking off -translation binary
            fileevent $state(sd) readable [list ::smtp::readable $token]
        } result]]} {
            set ecode $errorCode
            set einfo $errorInfo

            catch { close $state(sd) }







>
>
>
>







645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662

        if {[info exists state(sd)]} {
            unset state(sd)
        }

        if {[set code [catch {
            set state(sd) [socket -async $server $port]
            if { $options(-tlsimport) } {
                package require tls
                tls::import $state(sd)
            }
            fconfigure $state(sd) -blocking off -translation binary
            fileevent $state(sd) readable [list ::smtp::readable $token]
        } result]]} {
            set ecode $errorCode
            set einfo $errorInfo

            catch { close $state(sd) }
681
682
683
684
685
686
687


688
689
690
691
692
693
694
    # return an error.
    finalize $token -close drop

    return -code $code -errorinfo $einfo -errorcode $ecode $result
}

# If we cannot load the tls package, ignore the error


proc ::smtp::load_tls {} {
    set r [catch {package require tls}]
    if {$r} {set ::errorInfo ""}
    return $r
}

proc ::smtp::initialize_ehlo {token} {







>
>







692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
    # return an error.
    finalize $token -close drop

    return -code $code -errorinfo $einfo -errorcode $ecode $result
}

# If we cannot load the tls package, ignore the error
# Result value is a Tcl return code, not a bool.
# 0 == OK
proc ::smtp::load_tls {} {
    set r [catch {package require tls}]
    if {$r} {set ::errorInfo ""}
    return $r
}

proc ::smtp::initialize_ehlo {token} {
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
        }
        
        # Support STARTTLS extension.
        # The state(tls) item is used to see if we have already tried this.
        if {($options(-usetls)) && ![info exists state(tls)] \
                && (([lsearch $response(args) STARTTLS] >= 0)
                    || ([lsearch $response(args) TLS] >= 0))} {
            if {![load_tls]} {
                set state(tls) 0
                if {![catch {smtp::talk $token 300 STARTTLS} resp]} {
                    array set starttls $resp
                    if {$starttls(code) == 220} {
                        fileevent $state(sd) readable {}
                        catch {
                            ::tls::import $state(sd)
                            catch {::tls::handshake $state(sd)} msg
                            set state(tls) 1
                        } 
                        fileevent $state(sd) readable \
                            [list ::smtp::readable $token]
                        return [initialize_ehlo $token]
                    } else {
                        # Call a TLS client policy proc here
                        #  returns secure close and try another server.
                        #  returns insecure continue on current socket
                        set policy insecure
                        if {$options(-tlspolicy) != {}} {
                            catch {
                                eval $options(-tlspolicy) \
                                    [list $starttls(code)] \
                                    [list $starttls(diagnostic)]
                            } policy







|















|
|







753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
        }
        
        # Support STARTTLS extension.
        # The state(tls) item is used to see if we have already tried this.
        if {($options(-usetls)) && ![info exists state(tls)] \
                && (([lsearch $response(args) STARTTLS] >= 0)
                    || ([lsearch $response(args) TLS] >= 0))} {
            if {[load_tls] == 0} {
                set state(tls) 0
                if {![catch {smtp::talk $token 300 STARTTLS} resp]} {
                    array set starttls $resp
                    if {$starttls(code) == 220} {
                        fileevent $state(sd) readable {}
                        catch {
                            ::tls::import $state(sd)
                            catch {::tls::handshake $state(sd)} msg
                            set state(tls) 1
                        } 
                        fileevent $state(sd) readable \
                            [list ::smtp::readable $token]
                        return [initialize_ehlo $token]
                    } else {
                        # Call a TLS client policy proc here
                        #  returns secure   - close and try another server.
                        #  returns insecure - continue on current socket
                        set policy insecure
                        if {$options(-tlspolicy) != {}} {
                            catch {
                                eval $options(-tlspolicy) \
                                    [list $starttls(code)] \
                                    [list $starttls(diagnostic)]
                            } policy
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
            error "unknown boolean value: $value"
        }
    }
}

# -------------------------------------------------------------------------

package provide smtp 1.4.5

# -------------------------------------------------------------------------
# Local variables:
# indent-tabs-mode: nil
# End:







|





1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
            error "unknown boolean value: $value"
        }
    }
}

# -------------------------------------------------------------------------

package provide smtp 1.5

# -------------------------------------------------------------------------
# Local variables:
# indent-tabs-mode: nil
# End: