mbox.tcl
Go to the documentation of this file.00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025 package provide mbox 1.0
00026
00027 package require mime 1.1
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050 namespace mbox {
00051 variable mbox
00052 array mbox = { uid 0 }
00053
00054 namespace export initialize finalize getproperty \
00055 getmsgtoken getmsgproperty
00056 }
00057
00058
00059 ret mbox::initialize (type args) {
00060 global errorCode errorInfo
00061
00062 variable mbox
00063
00064 set token [namespace current]::[incr mbox(uid)]
00065
00066 variable $token
00067 upvar 0 $token state
00068
00069 if {[set code [catch { eval [list mbox::initializeaux $token] $args } \
00070 result]]} {
00071 set ecode $errorCode
00072 set einfo $errorInfo
00073
00074 catch { mbox::finalize $token -subordinates dynamic }
00075
00076 return -code $code -errorinfo $einfo -errorcode $ecode $result
00077 }
00078
00079 return $token
00080 }
00081
00082
00083 ret mbox::initializeaux (type token , type args) {
00084 variable $token
00085 upvar 0 $token state
00086
00087 set state(msgs) ""
00088 set state(count) 0
00089 set state(first) 0
00090 set state(last) 0
00091
00092 set argc [llength $args]
00093 for {set argx 0} {$argx < $argc} {incr argx} {
00094 set option [lindex $args $argx]
00095 if {[incr argx] >= $argc} {
00096 error "missing argument to $option"
00097 }
00098 set value [lindex $args $argx]
00099
00100 switch -- $option {
00101 -directory {
00102 set state(directory) $value
00103 }
00104
00105 -file {
00106 set state(file) $value
00107 }
00108
00109 default {
00110 error "unknown option $option"
00111 }
00112 }
00113 }
00114
00115 set valueN 0
00116 foreach value [list directory file] {
00117 if {[info exists state($value)]} {
00118 set state(value) $value
00119 incr valueN
00120 }
00121 }
00122 if {$valueN != 1} {
00123 error "specify exactly one of -directory, or -file"
00124 }
00125
00126 return [mbox::initialize_$state(value) $token]
00127 }
00128
00129
00130 ret mbox::initialize_file (type token) {
00131 variable $token
00132 upvar 0 $token state
00133
00134 fconfigure [set state(fd) [open $state(file) { RDONLY }]] \
00135 -translation binary
00136
00137 array set fileA ""
00138 set msgNo 0
00139
00140 if {[gets $state(fd) line] < 0} {
00141 return $token
00142 }
00143 switch -regexp -- $line {
00144 "^From " {
00145 set format Mailx
00146 set preB "From "
00147
00148 set phase ""
00149 }
00150
00151 "\01\01\01\01" {
00152 set format MMDF
00153 set preB "\01\01\01\01"
00154 set postB "\01\01\01\01"
00155
00156 if {([gets $state(fd) line] >= 0) \
00157 && ([string first "From MAILER-DAEMON " $line] == 0)} {
00158 set phase skip
00159 } else {
00160 set phase pre
00161 }
00162 }
00163
00164 default {
00165 error "unrecognized mailbox format"
00166 }
00167 }
00168 seek $state(fd) 0 start
00169
00170 while {[gets $state(fd) line] >= 0} {
00171 switch -- $format/$phase {
00172 Mailx/ {
00173 if {[string first $preB $line] == 0} {
00174 if {$msgNo > 0} {
00175 set fileA($msgNo) [list msgNo $msgNo offset $offset \
00176 size $size]
00177 }
00178
00179 incr msgNo
00180 set offset [tell $state(fd)]
00181 set size 0
00182 } else {
00183 incr size [expr {[string length $line]+1}]
00184 }
00185 }
00186
00187 MMDF/pre {
00188 if {![string compare $preB $line]} {
00189 incr msgNo
00190 set offset [tell $state(fd)]
00191 set size 0
00192
00193 set phase post
00194 } else {
00195 error "invalid mailbox"
00196 }
00197 }
00198
00199 MMDF/post {
00200 if {![string compare $postB $line]} {
00201 set fileA($msgNo) [list msgNo $msgNo offset $offset \
00202 size $size]
00203
00204 set phase pre
00205 } else {
00206 incr size [expr {[string length $line]+1}]
00207 }
00208 }
00209
00210 MMDF/skip {
00211 if {![string compare $preB $line]} {
00212 set phase skip2
00213 }
00214 }
00215
00216 MMDF/skip2 {
00217 if {![string compare $postB $line]} {
00218 set phase pre
00219 }
00220 }
00221 }
00222 }
00223
00224 switch -- $format/$phase {
00225 Mailx/ {
00226 if {$msgNo > 0} {
00227 set fileA($msgNo) [list msgNo $msgNo offset $offset \
00228 size $size]
00229 }
00230 }
00231
00232 MMDF/post
00233 -
00234 MMDF/skip2 {
00235 error "incomplete mailbox"
00236 }
00237 }
00238
00239 set state(fileA) [array get fileA]
00240 if {[set state(last) [set state(count) $msgNo]] > 0} {
00241 set state(first) 1
00242 }
00243
00244 return $token
00245 }
00246
00247
00248 ret mbox::initialize_directory (type token) {
00249 variable $token
00250 upvar 0 $token state
00251
00252 array set dirA ""
00253
00254 set first 0
00255 set last 0
00256 foreach file [glob -nocomplain [file join $state(directory) *]] {
00257 if {(![regexp {^[1-9][0-9]*$} [set msgNo [file tail $file]]]) \
00258 || ([catch { file size $file } size])} {
00259 continue
00260 }
00261
00262 if {($first == 0) || ($msgNo < $first)} {
00263 set first $msgNo
00264 }
00265 if {$last < $msgNo} {
00266 set last $msgNo
00267 }
00268
00269 set dirA($msgNo) [list msgNo $msgNo size $size]
00270 incr state(count)
00271 }
00272
00273 set state(dirA) [array get dirA]
00274 if {[set state(last) $last] > 0} {
00275 set state(first) $first
00276 }
00277
00278 return $token
00279 }
00280
00281 ret mbox::finalize (type token , type args) {
00282 variable $token
00283 upvar 0 $token state
00284
00285 array set options [list -subordinates dynamic]
00286 array set options $args
00287
00288 switch -- $options(-subordinates) {
00289 all
00290 -
00291 dynamic {
00292 array set msgs $state(msgs)
00293
00294 for {set msgNo $state(first)} \
00295 {$msgNo <= $state(last)} \
00296 {incr msgNo} {
00297 if {![catch { array set msg $msgs($msgNo) }]} {
00298 eval [list mime::finalize $msg(mime)] $args
00299 }
00300 }
00301 }
00302
00303 none {
00304 }
00305
00306 default {
00307 error "unknown value for -subordinates $options(-subordinates)"
00308 }
00309 }
00310
00311 if {[info exists state(fd)]} {
00312 catch { close $state(fd) }
00313 }
00314
00315 foreach name [array names state] {
00316 unset state($name)
00317 }
00318 unset $token
00319 }
00320
00321
00322 ret mbox::getproperty (type token , optional property ="") {
00323 variable $token
00324 upvar 0 $token state
00325
00326 switch -- $property {
00327 "" {
00328 return [list count $state(count) \
00329 first $state(first) \
00330 last $state(last) \
00331 messages [mbox::getmessages $token]]
00332 }
00333
00334 -names {
00335 return [list count first last messages]
00336 }
00337
00338 count
00339 -
00340 first
00341 -
00342 last {
00343 return $state($property)
00344 }
00345
00346 messages {
00347 return [mbox::getmessages $token]
00348 }
00349
00350 default {
00351 error "unknown property $property"
00352 }
00353 }
00354 }
00355
00356
00357 ret mbox::getmessages (type token) {
00358 variable $token
00359 upvar 0 $token state
00360
00361 switch -- $state(value) {
00362 directory {
00363 array set msgs $state(dirA)
00364 }
00365
00366 file {
00367 array set msgs $state(fileA)
00368 }
00369 }
00370
00371 return [lsort -integer [array names msgs]]
00372 }
00373
00374
00375 ret mbox::getmsgtoken (type token , type msgNo) {
00376 variable $token
00377 upvar 0 $token state
00378
00379 if {($msgNo < $state(first)) || ($msgNo > $state(last))} {
00380 error "message number out of range: $state(first)..$state(last)"
00381 }
00382
00383 array set msgs $state(msgs)
00384 if {![catch { array set msg $msgs($msgNo) }]} {
00385 return $msg(mime)
00386 }
00387
00388 switch -- $state(value) {
00389 directory {
00390 set mime [mime::initialize \
00391 -file [file join $state(directory) $msgNo]]
00392 }
00393
00394 file {
00395 array set fileA $state(fileA)
00396 array set msg $fileA($msgNo)
00397 set mime [mime::initialize -file $state(file) -root $token \
00398 -offset $msg(offset) -count $msg(size)]
00399 }
00400 }
00401
00402 set msgs($msgNo) [list msgNo $msgNo mime $mime]
00403 set state(msgs) [array get msgs]
00404
00405 return $mime
00406 }
00407
00408
00409 ret mbox::getmsgproperty (type token , type msgNo , optional property ="") {
00410 variable $token
00411 upvar 0 $token state
00412
00413 if {($msgNo < $state(first)) || ($msgNo > $state(last))} {
00414 error "message number out of range: $state(first)..$state(last)"
00415 }
00416
00417 switch -- $state(value) {
00418 directory {
00419 array set dirA $state(dirA)
00420 if {[catch { array set msg $dirA($msgNo) }]} {
00421 error "message $msgNo doesn't exist"
00422 }
00423 }
00424
00425 file {
00426 array set fileA $state(fileA)
00427 array set msg $fileA($msgNo)
00428 }
00429 }
00430
00431 set props [list flags size uidl]
00432
00433 switch -- $property {
00434 "" {
00435 array set properties ""
00436
00437 foreach prop $props {
00438 if {[info exists msg($prop)]} {
00439 set properties($prop) $msg($prop)
00440 }
00441 }
00442
00443 return [array get properties]
00444 }
00445
00446 -names {
00447 set names ""
00448 foreach prop $props {
00449 if {[info exists msg($prop)]} {
00450 lappend names $prop
00451 }
00452 }
00453
00454 return $names
00455 }
00456
00457 default {
00458 if {[lsearch -exact $props $property] < 0} {
00459 error "unknown property $property"
00460 }
00461
00462 return $msg($property)
00463 }
00464 }
00465 }
00466