mbox.tcl

Go to the documentation of this file.
00001 /*  mbox.tcl - mailbox package*/
00002 /* */
00003 /*  (c) 1999 Marshall T. Rose*/
00004 /*  Hold harmless the author, and any lawful use is allowed.*/
00005 /* */
00006 
00007 /* */
00008 /*  TODO:*/
00009 /* */
00010 /*      mbox::initialize*/
00011 /*          add -pop server option*/
00012 /*          add -imap server option*/
00013 /*          along with -username, -password, and -passback*/
00014 /* */
00015 /*      mbox::getmsgproperty*/
00016 /*          add support for deleted messages*/
00017 /* */
00018 /*      mbox::deletemsg token msgNo*/
00019 /*          marks a message for deletion*/
00020 /* */
00021 /*      mbox::synchronize token ?-commit boolean?*/
00022 /*          commits or rollllbacks changes*/
00023 
00024 
00025 package provide mbox 1.0
00026 
00027 package require mime 1.1
00028 
00029 
00030 /* */
00031 /*  state variables:*/
00032 /* */
00033 /*      msgs: serialized array of messages, containing array of:*/
00034 /*            msgNo, mime*/
00035 /*      count: number of messages*/
00036 /*      first: number of initial message*/
00037 /*      last: number of final message*/
00038 /*      value: either "file", or "directory"*/
00039 /* */
00040 /*      file: file containing mailbox*/
00041 /*      fd: corresponding file descriptor*/
00042 /*      fileA: serialized array of messages, containing array of:*/
00043 /*             msgNo, offset, size*/
00044 /* */
00045 /*      directory: directory containing mailbox*/
00046 /*      dirA: serialized array of messages, containing array of:*/
00047 /*            msgNo, size*/
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 

Generated on 21 Sep 2010 for Gui by  doxygen 1.6.1