Tcl Library Source Code

Changes On Branch contrib-nicola-hall-imap4
Login

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

Changes In Branch contrib-nicola-hall-imap4 Excluding Merge-Ins

This is equivalent to a diff from 8252e19883 to e00e057b9f

2013-01-28
23:14
New module for zipfile de- and encoding. Plus support new package in fileutil. check-in: ca44329bef user: andreask tags: trunk
2013-01-25
19:43
Merged branch "contrib-nicola-hall-imap4" into release. Updated release README. check-in: eb3625d042 user: andreask tags: tcllib-1-15-rc
19:29
Updated documentation with text contributed by Nicola Hall, explaining the new commands Closed-Leaf check-in: e00e057b9f user: andreask tags: contrib-nicola-hall-imap4
2013-01-24
18:30
New module, clock. rfc2822 and iso8601 parsing. taken from wiki. no docs yet check-in: 9a82504277 user: andreask tags: new-module-clock
2013-01-22
20:17
Applied contribution by Nicola Hall. Additional commands. Bumped version to 0.4. check-in: a35937592d user: andreask tags: contrib-nicola-hall-imap4
2013-01-21
22:47
Merge latest certs from trunk into the RC work. check-in: 107f16ba9c user: andreask tags: tcllib-1-15-rc
21:56
Regenerated the certificates here again, 10 years at 1024 bit. The issue with [7b80198969] seems to have been that I extended the Root cert validity to 100 years, and that screwed something up. With the root certs now back to 10 years (+10 days) validity, things are fine. I suspect that I may have run into the Y2038 problem in either Tcl and/or openssl. check-in: 8252e19883 user: andreask tags: trunk
19:37
[Bug 3433470] Regenerated the certificates, again. Expiry is Jan 2023 (10 years). While SimpleCA doesn't seem to allow me to specify a longer period in the GUI it was possible to get, update and run the Tcl code, unwrapped. Further changed to 4096-bit certs. --- Broken --- Investigation points to me setting the root cert validity period to 100 years as the cause, possibly invoking y2038 troubles in Tcl and/or openssl. check-in: 7b80198969 user: andreask

Changes to modules/imap4/ChangeLog.












1
2
3
4
5
6
7











2011-12-13  Andreas Kupries  <[email protected]>

	*
	* Released and tagged Tcllib 1.14 ========================
	* 

2011-01-24  Andreas Kupries  <[email protected]>
>
>
>
>
>
>
>
>
>
>
>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2013-01-25  Andreas Kupries  <[email protected]>

	* imap4.man: Updated documentation with text contributed by Nicola
	  Hall, explaining the new commands.

2013-01-22  Andreas Kupries  <[email protected]>

	* imap4.tcl: Applied contribution by Nicola Hall.
	* imap4.man: Additional commands. Bumped version
	* pkgIndex.tcl: to 0.4.

2011-12-13  Andreas Kupries  <[email protected]>

	*
	* Released and tagged Tcllib 1.14 ========================
	* 

2011-01-24  Andreas Kupries  <[email protected]>

Changes to modules/imap4/imap4.man.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[manpage_begin imap4 n 0.3]
[moddesc   {imap client}]
[titledesc {imap client-side tcl implementation of imap protocol}]

[require Tcl 8.5]
[require imap4 [opt 0.3]]

[description]

The [package imap4] library package provides the client side of the
[strong "Internet Message Access Protocol"] (IMAP) using standard
sockets or secure connection via TLS/SSL.
The package is fully implemented in Tcl.

[para]
This document describes the procedures and explains their usage.

[section "PROCEDURES"]
|




|




|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[manpage_begin imap4 n 0.4]
[moddesc   {imap client}]
[titledesc {imap client-side tcl implementation of imap protocol}]

[require Tcl 8.5]
[require imap4 [opt 0.4]]

[description]

The [package imap4] library package provides the client side of the
[emph "Internet Message Access Protocol"] (IMAP) using standard
sockets or secure connection via TLS/SSL.
The package is fully implemented in Tcl.

[para]
This document describes the procedures and explains their usage.

[section "PROCEDURES"]
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
returned instead of the result code. All flags are converted to
lowercase and leading special characters are removed.
[example {{{Arc08 noselect} {Arc08/Private {noinferiors unmarked}} {INBOX noinferiors}}}]

[call [cmd ::imap4::select] [arg chan] [opt [arg mailbox]]]
[para]Select a mailbox, 0 is returned on success.
[para][arg chan] - imap channel
[para][arg mailbox] - Path of the mailbox,  defaults to [strong INBOX]
[para]Prior to examine/select an open mailbox must be closed - see: [cmd ::imap4::close].

[call [cmd ::imap4::examine] [arg chan] [opt [arg mailbox]]]
[para]"Examines" a mailbox, read-only equivalent of [cmd ::imap4::select].
[para][arg chan] - imap channel
[para][arg mailbox] - mailbox name or path to mailbox,
defaults to [strong INBOX]
[para]Prior to examine/select an open mailbox must be closed - see: [cmd ::imap4::close].

[call [cmd ::imap4::fetch] [arg chan] [arg range] [opt [arg -inline]] [opt [arg "attr ..."]]]
[para]Fetch attributes from messages.
[para]The attributes are fetched and stored in the internal state
which can be retrieved with command [cmd ::imap4::msginfo], 0 is returned
on success.







|






|







53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
returned instead of the result code. All flags are converted to
lowercase and leading special characters are removed.
[example {{{Arc08 noselect} {Arc08/Private {noinferiors unmarked}} {INBOX noinferiors}}}]

[call [cmd ::imap4::select] [arg chan] [opt [arg mailbox]]]
[para]Select a mailbox, 0 is returned on success.
[para][arg chan] - imap channel
[para][arg mailbox] - Path of the mailbox,  defaults to [emph INBOX]
[para]Prior to examine/select an open mailbox must be closed - see: [cmd ::imap4::close].

[call [cmd ::imap4::examine] [arg chan] [opt [arg mailbox]]]
[para]"Examines" a mailbox, read-only equivalent of [cmd ::imap4::select].
[para][arg chan] - imap channel
[para][arg mailbox] - mailbox name or path to mailbox,
defaults to [emph INBOX]
[para]Prior to examine/select an open mailbox must be closed - see: [cmd ::imap4::close].

[call [cmd ::imap4::fetch] [arg chan] [arg range] [opt [arg -inline]] [opt [arg "attr ..."]]]
[para]Fetch attributes from messages.
[para]The attributes are fetched and stored in the internal state
which can be retrieved with command [cmd ::imap4::msginfo], 0 is returned
on success.
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
[para]If the required information name is suffixed with a ? character,
the command returns true if the information is available, or
false if it is not.
[para][arg chan] - imap channel
[para][arg info] - folderlist options to retrieve
[para]
Currently supported options:
[strong delim] - hierarchy delimiter only,
[strong match] - ref and mbox search patterns (see [cmd ::imap4::folders]),
[strong names] - list of folder names only,
[strong flags] - list of folder names with flags in format
[emph "{ {name {flags}} ... }"] (see also compact format in function
[cmd ::imap4::folders]).
[example {
{{Arc08 {{\NoSelect}}} {Arc08/Private {{\NoInferiors} {\UnMarked}}} {INBOX {\NoInferiors}}}
}]
[call [cmd ::imap4::msginfo] [arg chan] [arg msgid] [opt [arg info]] [opt [arg defval]]]
[para]Get information (from previously collected using fetch) from a given







|
|
|
|







97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
[para]If the required information name is suffixed with a ? character,
the command returns true if the information is available, or
false if it is not.
[para][arg chan] - imap channel
[para][arg info] - folderlist options to retrieve
[para]
Currently supported options:
[emph delim] - hierarchy delimiter only,
[emph match] - ref and mbox search patterns (see [cmd ::imap4::folders]),
[emph names] - list of folder names only,
[emph flags] - list of folder names with flags in format
[emph "{ {name {flags}} ... }"] (see also compact format in function
[cmd ::imap4::folders]).
[example {
{{Arc08 {{\NoSelect}}} {Arc08/Private {{\NoInferiors} {\UnMarked}}} {INBOX {\NoInferiors}}}
}]
[call [cmd ::imap4::msginfo] [arg chan] [arg msgid] [opt [arg info]] [opt [arg defval]]]
[para]Get information (from previously collected using fetch) from a given
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
[para]If the required information name is suffixed with a ? character,
the command returns true if the information is available, or
false if it is not.
[para][arg chan] - imap channel
[para][arg opt] - mailbox option to retrieve
[para]
Currently supported options:
[strong EXISTS] (noof msgs), 
[strong RECENT] (noof 'recent' flagged msgs), 
[strong FLAGS]
[para]In conjunction with OK:
[strong PERMFLAGS], [strong UIDNEXT], [strong UIDVAL], [strong UNSEEN]
[para]Div. states:
[strong CURRENT], [strong FOUND], [strong PERM].

[example {
    ::imap4::select $chan INBOX
    puts "[::imap4::mboxinfo $chan exists] mails in INBOX"}]

[call [cmd ::imap4::isableto] [arg chan] [opt [arg capability]]]
[para]Test for capability.







|
|
|

|

|







134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
[para]If the required information name is suffixed with a ? character,
the command returns true if the information is available, or
false if it is not.
[para][arg chan] - imap channel
[para][arg opt] - mailbox option to retrieve
[para]
Currently supported options:
[emph EXISTS] (noof msgs), 
[emph RECENT] (noof 'recent' flagged msgs), 
[emph FLAGS]
[para]In conjunction with OK:
[emph PERMFLAGS], [emph UIDNEXT], [emph UIDVAL], [emph UNSEEN]
[para]Div. states:
[emph CURRENT], [emph FOUND], [emph PERM].

[example {
    ::imap4::select $chan INBOX
    puts "[::imap4::mboxinfo $chan exists] mails in INBOX"}]

[call [cmd ::imap4::isableto] [arg chan] [opt [arg capability]]]
[para]Test for capability.
232
233
234
235
236
237
238

















































239
240
241
242
243
244
245
programmer. Every line entered is sent verbatim to the
server (after the addition of the request identifier).
The ::imap4::debug variable is automatically set to '1' on enter.
[para]It's possible to execute Tcl commands starting the line
with a slash.
[para][arg chan] - imap channel
[para][arg errormsg] - optional error message to display

















































[list_end]

[section EXAMPLES]

[example_begin]
    set user myusername
    set pass xtremescrt







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
programmer. Every line entered is sent verbatim to the
server (after the addition of the request identifier).
The ::imap4::debug variable is automatically set to '1' on enter.
[para]It's possible to execute Tcl commands starting the line
with a slash.
[para][arg chan] - imap channel
[para][arg errormsg] - optional error message to display

[call [cmd ::imap4::store] [arg chan] [arg range] [arg data] [arg flaglist]]

[para] Alters data associated with a message in the selected
mailbox.

[para][arg chan] - imap channel
[para][arg range] - message index in format [emph FROM]:[emph TO]
[para][arg flaglist] - Flags the [arg data] operates on.
[para][arg data] - The currently defined [arg data] items that can be
stored are shown below. [emph Note] that all of these data types may
also be suffixed with ".SILENT" to suppress the untagged FETCH
response.

[list_begin definitions]
[def FLAGS]
Replace the flags for the message (other than \Recent) with the
[arg flaglist].
[def "+FLAGS"]
Add the flags in [arg flaglist] to the existing flags for the message.
[def "-FLAGS"]
Remove the flags in [arg flaglist] to the existing flags for the
message.
[list_end]

For example:
[example {
	::imap4::store $chan $start_msgid:$end_msgid +FLAGS "Deleted"
}]

[call [cmd ::imap4::expunge] [arg chan]]

[para] Permanently removes all messages that have the \Deleted flag
set from the currently selected mailbox, without the need to close the
connection.

[para][arg chan] - imap channel

[call [cmd ::imap4::logout] [arg chan]]

[para] Informs the server that the client is done with the connection
and closes the network connection. Permanently removes \Deleted
messages.

[para] A new connection will need to be established to login once
more.

[para][arg chan] - imap channel

[list_end]

[section EXAMPLES]

[example_begin]
    set user myusername
    set pass xtremescrt

Changes to modules/imap4/imap4.tcl.

1
2
3
4
5

6
7
8
9
10
11
12
# IMAP4 protocol pure Tcl implementation.
#
# COPYRIGHT AND PERMISSION NOTICE
#
# Copyright (C) 2004 Salvatore Sanfilippo <[email protected]>.

#
# All rights reserved.
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,





>







1
2
3
4
5
6
7
8
9
10
11
12
13
# IMAP4 protocol pure Tcl implementation.
#
# COPYRIGHT AND PERMISSION NOTICE
#
# Copyright (C) 2004 Salvatore Sanfilippo <[email protected]>.
# Copyright (C) 2013 Nicola Hall <[email protected]>
#
# All rights reserved.
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#             option -inline for ::imap4::fetch, in order to return data as a Tcl list
#             isableto without arguments returns the capability list
#             implementation of LIST command
#   20100709: Adding suppport for SSL connections, namespace variable
#             use_ssl must be set to 1 and package TLS must be loaded
#   20100716: Bug in parsing special leading FLAGS characters in FETCH
#             command repaired, documentation cleanup.
#

package require Tcl 8.5
package provide imap4 0.3

namespace eval imap4 {
    variable debugmode 0     ;# inside debug mode? usually not.
    variable folderinfo
    variable mboxinfo
    variable msginfo
    variable info







|


|







45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#             option -inline for ::imap4::fetch, in order to return data as a Tcl list
#             isableto without arguments returns the capability list
#             implementation of LIST command
#   20100709: Adding suppport for SSL connections, namespace variable
#             use_ssl must be set to 1 and package TLS must be loaded
#   20100716: Bug in parsing special leading FLAGS characters in FETCH
#             command repaired, documentation cleanup.
#   20121221: Added basic scope, expunge and logout function

package require Tcl 8.5
package provide imap4 0.4

namespace eval imap4 {
    variable debugmode 0     ;# inside debug mode? usually not.
    variable folderinfo
    variable mboxinfo
    variable msginfo
    variable info
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235

        set tag [string range $line 0 [expr {$idx-1}]]
        set line [string range $line [expr {$idx+1}] end]
        # If it's just a command continuation response, return.
        if {$tag eq {+}} {return +}

        # Extract the error code, if it's a tagged line
        if {$tag ne {*}} {
            set idx [string first { } $line]
            if {$idx <= 0} {
                protoerror $chan "IMAP: malformed response '$line'"
            }
            set code [string range $line 0 [expr {$idx-1}]]
            set line [string trim [string range $line [expr {$idx+1}] end]]
            set info($chan,lastcode) $code







|







222
223
224
225
226
227
228
229
230
231
232
233
234
235
236

        set tag [string range $line 0 [expr {$idx-1}]]
        set line [string range $line [expr {$idx+1}] end]
        # If it's just a command continuation response, return.
        if {$tag eq {+}} {return +}

        # Extract the error code, if it's a tagged line
        if {$tag ne "*"} {
            set idx [string first { } $line]
            if {$idx <= 0} {
                protoerror $chan "IMAP: malformed response '$line'"
            }
            set code [string range $line 0 [expr {$idx-1}]]
            set line [string trim [string range $line [expr {$idx+1}] end]]
            set info($chan,lastcode) $code
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
                if {$p1<0||$p2<0||$p3<0} {
                    protoerror $chan "IMAP: Not a valid RFC822 LIST format in '$line'"
                }
                set flags [string range $line [expr {$p1+1}] [expr {$p2-1}]]
                set delim [string range $line [expr {$p2+2}] [expr {$p3-1}]]
                set fname [string range $line [expr {$p3+1}] end]
                if {$fname eq ""} {
                    set folderinfo($chan,delim) [string trim $delim {"}]
                } else {
                    set fflag {}
                    foreach f [split $flags] {
                        lappend fflag $f
                    }
                    lappend folderinfo($chan,names) $fname
                    lappend folderinfo($chan,flags) [list $fname $fflag]
                    if {$delim ne "NIL"} {
                        set folderinfo($chan,delim) [string trim $delim {"}]
                    }
                }
                incr dirty
            }
            {FLAGS *(*)*} {
                regexp {.*\((.*)\).*} $line => flags
                set mboxinfo($chan,flags) $flags







|








|







253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
                if {$p1<0||$p2<0||$p3<0} {
                    protoerror $chan "IMAP: Not a valid RFC822 LIST format in '$line'"
                }
                set flags [string range $line [expr {$p1+1}] [expr {$p2-1}]]
                set delim [string range $line [expr {$p2+2}] [expr {$p3-1}]]
                set fname [string range $line [expr {$p3+1}] end]
                if {$fname eq ""} {
                    set folderinfo($chan,delim) [string trim $delim "\""]
                } else {
                    set fflag {}
                    foreach f [split $flags] {
                        lappend fflag $f
                    }
                    lappend folderinfo($chan,names) $fname
                    lappend folderinfo($chan,flags) [list $fname $fflag]
                    if {$delim ne "NIL"} {
                        set folderinfo($chan,delim) [string trim $delim "\""]
                    }
                }
                incr dirty
            }
            {FLAGS *(*)*} {
                regexp {.*\((.*)\).*} $line => flags
                set mboxinfo($chan,flags) $flags
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
    }

    # Write a request.
    proc request {chan request} {
        variable debug
        variable info

        set t "[tag $chan] $request"
        if {$debug} {
            puts "C: $t"
        }
        set info($chan,lastrequest) $t
        puts -nonewline $chan "$t\r\n"
        flush $chan
    }







|







522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
    }

    # Write a request.
    proc request {chan request} {
        variable debug
        variable info

        set t "[tag $chan] [string trim $request]"
        if {$debug} {
            puts "C: $t"
        }
        set info($chan,lastrequest) $t
        puts -nonewline $chan "$t\r\n"
        flush $chan
    }
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
        set folderinfo($chan,match) [list $ref $mbox]
        # parray folderinfo
        set rv [simplecmd $chan LIST {SELECT AUTH} \"$ref\" \"$mbox\"]
        if {$inline} {
            set rv {}
            foreach f [folderinfo $chan flags] {
                set lflags {}
                foreach {fl} [lindex $f 1] {
                    if {[string is alnum [string index $fl 0]]} {
                        lappend lflags [string tolower $fl]]
                    } else {
                        lappend lflags [string tolower [string range $fl 1 end]]
                    }
                }
                lappend rv [list [lindex $f 0] $lflags]
            }
        }







|

|







939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
        set folderinfo($chan,match) [list $ref $mbox]
        # parray folderinfo
        set rv [simplecmd $chan LIST {SELECT AUTH} \"$ref\" \"$mbox\"]
        if {$inline} {
            set rv {}
            foreach f [folderinfo $chan flags] {
                set lflags {}
                foreach fl [lindex $f 1] {
                    if {[string is alnum [string index $fl 0]]} {
                        lappend lflags [string tolower $fl]
                    } else {
                        lappend lflags [string tolower [string range $fl 1 end]]
                    }
                }
                lappend rv [list [lindex $f 0] $lflags]
            }
        }
1214
1215
1216
1217
1218
1219
1220

























































1221
1222
1223
1224
1225
1226
1227
    # Other stuff to do in random order...
    #
    # proc ::imap4::idle notify-command
    # proc ::imap4::auth plain ...
    # proc ::imap4::securestauth user pass
    # proc ::imap4::store
    # proc ::imap4::logout (need to clean both msg and mailbox info arrays)

























































}

################################################################################
# Example and test
################################################################################
if {[info script] eq $argv0} {
    # set imap4::debug 0







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
    # Other stuff to do in random order...
    #
    # proc ::imap4::idle notify-command
    # proc ::imap4::auth plain ...
    # proc ::imap4::securestauth user pass
    # proc ::imap4::store
    # proc ::imap4::logout (need to clean both msg and mailbox info arrays)

    # Amend the flags of a message to be updated once CLOSE/EXPUNGE is initiated
    proc store {chan range key values} {
	set valid_keys {
	    FLAGS
	    FLAGS.SILENT
	    +FLAGS
	    +FLAGS.SILENT
	    -FLAGS
	    -FLAGS.SILENT
	}
	if {$key ni $valid_keys} {
	    error "Invalid data item: $key. Must be one of [join $valid_keys ,]"
	}
        parserange $chan $range start end
	set newflags {}
	foreach val $values {
	    if {[regexp {^\\+(.*?)$} $val]} {
		lappend newflags $values
	    } else {
		lappend newflags "\\$val"
	    }
	}
        request $chan "STORE $start:$end $key ([join $newflags])"
	if {[getresponse $chan]} {
	    return 1
	}
	return 0
    }

    # Logout
    proc logout {chan} {
	if {[simplecmd $chan LOGOUT SELECT {}]} {
	    # clean out info arrays
	    variable info
	    variable folderinfo
	    variable mboxinfo
	    variable msginfo

	    array unset folderinfo $chan,*
	    array unset mboxinfo $chan,*
	    array unset msginfo $chan,*
	    array unset info $chan,*

	    return 1
	}
        return 0
    }

    # Expunge : force removal of any messages with the 
    # flag \Deleted
    proc expunge {chan} {
        if {[simplecmd $chan EXPUNGE SELECT {}]} {
            return 1
        }
        return 0
    }
}

################################################################################
# Example and test
################################################################################
if {[info script] eq $argv0} {
    # set imap4::debug 0

Changes to modules/imap4/pkgIndex.tcl.

1
2
if {![package vsatisfies [package provide Tcl] 8.5]} {return}
package ifneeded imap4 0.3 [list source [file join $dir imap4.tcl]]

|
1
2
if {![package vsatisfies [package provide Tcl] 8.5]} {return}
package ifneeded imap4 0.4 [list source [file join $dir imap4.tcl]]