mime::parsepart (private)
mime::parsepart token
Defined in packages/acs-tcl/tcl/tcllib-doc-procs.tcl
Parse a MIME part from its token's string or file input and populate the internal part state. Parsing errors are reported as Tcl errors. This version-dependent helper is not a public API.
- Parameters:
- token (required)
- MIME token containing the part to parse.
- Partial Call Graph (max 5 caller/called nodes):
- Testcases:
- No testcase defined.
Source code: # FRINK: nocheck variable $token upvar 0 $token state if {[set fileP [info exists state(file)]]} { seek $state(fd) [set pos $state(offset)] start set last [expr {$state(offset) + $state(count) - 1}] } else { set string $state(string) } set vline {} while 1 { set blankP 0 if {$fileP} { if {($pos > $last) || ([set x [gets $state(fd) line]] <= 0)} { set blankP 1 } else { incr pos [expr {$x + 1}] } } else { if {$state(lines.current) >= $state(lines.count)} { set blankP 1 set line {} } else { set line [lindex $state(lines) $state(lines.current)] incr state(lines.current) set x [string length $line] if {$x == 0} {set blankP 1} } } if {(!$blankP) && ([string last \r $line] == ($x - 1))} { set line [string range $line 0 [expr {$x - 2}]] if {$x == 1} { set blankP 1 } } if {(!$blankP) && (([ string first { } $line] == 0) || ([ string first \t $line] == 0))} { append vline \n $line continue } if {$vline eq {}} { if {$blankP} { break } set vline $line continue } if {([set x [string first : $vline]] <= 0) || ([set mixed [ string trimright [ string range $vline 0 [expr {$x - 1}]] ]] eq {}) } { error "improper line in header: $vline" } set value [string trim [string range $vline [expr {$x + 1}] end]] switch -- [set lower [string tolower $mixed]] { content-type { if {[info exists state(content)]} { error "multiple Content-Type fields starting with $vline" } if {![catch {set x [parsetype $token $value]}]} { set state(content) [lindex $x 0] set state(params) [lindex $x 1] } } content-md5 { } content-transfer-encoding { if {($state(encoding) ne {}) && ($state(encoding) ne [ string tolower $value])} { error "multiple Content-Transfer-Encoding fields starting with $vline" } set state(encoding) [string tolower $value] } mime-version { set state(version) $value } default { if {[lsearch -exact $state(lowerL) $lower] < 0} { lappend state(lowerL) $lower lappend state(mixedL) $mixed } array set header $state(header) lappend header($lower) $value set state(header) [array get header] } } if {$blankP} { break } set vline $line } if {![info exists state(content)]} { set state(content) text/plain set state(params) [list charset us-ascii] } if {![string match multipart/* $state(content)]} { if {$fileP} { set x [tell $state(fd)] incr state(count) [expr {$state(offset) - $x}] set state(offset) $x } else { # rebuild string, this is cheap and needed by other functions set state(string) [join [ lrange $state(lines) $state(lines.current) end] \n] } if {[string match message/* $state(content)]} { # FRINK: nocheck variable [set child $token-[incr state(cid)]] set state(value) parts set state(parts) $child if {$fileP} { mime::initializeaux $child -file $state(file) -root $state(root) -offset $state(offset) -count $state(count) } else { if {[info exists state(encoding)]} { set strng [join [ lrange $state(lines) $state(lines.current) end] \n] switch -- $state(encoding) { base64 - quoted-printable { set strng [$state(encoding) -mode decode -- $strng] } default {} } mime::initializeaux $child -string $strng } else { mime::initializeaux $child -lineslist [ lrange $state(lines) $state(lines.current) end] } } } return } set state(value) parts set boundary {} foreach {k v} $state(params) { if {$k eq "boundary"} { set boundary $v break } } if {$boundary eq {}} { error "boundary parameter is missing in $state(content)" } if {[string trim $boundary] eq {}} { error "boundary parameter is empty in $state(content)" } if {$fileP} { set pos [tell $state(fd)] # This variable is like 'start', for the reasons laid out # below, in the other branch of this conditional. set initialpos $pos } else { # This variable is like 'start', a list of lines in the # part. This record is made even before we find a starting # boundary and used if we run into the terminating boundary # before a starting boundary was found. In that case the lines # before the terminator as recorded by tracelines are seen as # the part, or at least we attempt to parse them as a # part. See the forceoctet and nochild flags later. We cannot # use 'start' as that records lines only after the starting # boundary was found. set tracelines [list] } set inP 0 set moreP 1 set forceoctet 0 while {$moreP} { if {$fileP} { if {$pos > $last} { # We have run over the end of the part per the outer # information without finding a terminating boundary. # We now fake the boundary and force the parser to # give any new part coming of this a mime-type of # application/octet-stream regardless of header # information. set line "--$boundary--" set x [string length $line] set forceoctet 1 } else { if {[set x [gets $state(fd) line]] < 0} { error "end-of-file encountered while parsing $state(content)" } } incr pos [expr {$x + 1}] } else { if {$state(lines.current) >= $state(lines.count)} { error "end-of-string encountered while parsing $state(content)" } else { set line [lindex $state(lines) $state(lines.current)] incr state(lines.current) set x [string length $line] } set x [string length $line] } if {[string last \r $line] == $x - 1} { set line [string range $line 0 [expr {$x - 2}]] set crlf 2 } else { set crlf 1 } if {[string first --$boundary $line] != 0} { if {$inP && !$fileP} { lappend start $line } continue } else { lappend tracelines $line } if {!$inP} { # Haven't seen the starting boundary yet. Check if the # current line contains this starting boundary. if {$line eq "--$boundary"} { # Yes. Switch parser state to now search for the # terminating boundary of the part and record where # the part begins (or initialize the recorder for the # lines in the part). set inP 1 if {$fileP} { set start $pos } else { set start [list] } continue } elseif {$line eq "--$boundary--"} { # We just saw a terminating boundary before we ever # saw the starting boundary of a part. This forces us # to stop parsing, we do this by forcing the parser # into an accepting state. We will try to create a # child part based on faked start position or recorded # lines, or, if that fails, let the current part have # no children. # As an example note the test case mime-3.7 and the # referenced file "badmail1.txt". set inP 1 if {$fileP} { set start $initialpos } else { set start $tracelines } set forceoctet 1 # Fall through. This brings to the creation of the new # part instead of searching further and possible # running over the end. } else { continue } } # Looking for the end of the current part. We accept both a # terminating boundary and the starting boundary of the next # part as the end of the current part. if {[set moreP [string compare $line --$boundary--]] && $line ne "--$boundary"} { # The current part has not ended, so we record the line # if we are inside a part and doing string parsing. if {$inP && !$fileP} { lappend start $line } continue } # The current part has ended. We now determine the exact # boundaries, create a mime part object for it and recursively # parse it deeper as part of that action. # FRINK: nocheck variable [set child $token-[incr state(cid)]] lappend state(parts) $child set nochild 0 if {$fileP} { if {[set count [expr {$pos - ($start + $x + $crlf + 1)}]] < 0} { set count 0 } if {$forceoctet} { set ::errorInfo {} if {[catch { mime::initializeaux $child -file $state(file) -root $state(root) -offset $start -count $count }]} { set nochild 1 set state(parts) [lrange $state(parts) 0 end-1] } } else { mime::initializeaux $child -file $state(file) -root $state(root) -offset $start -count $count } seek $state(fd) [set start $pos] start } else { if {$forceoctet} { if {[catch { mime::initializeaux $child -lineslist $start }]} { set nochild 1 set state(parts) [lrange $state(parts) 0 end-1] } } else { mime::initializeaux $child -lineslist $start } set start {} } if {$forceoctet && !$nochild} { variable $child upvar 0 $child childstate set childstate(content) application/octet-stream } set forceoctet 0 }XQL Not present: Generic, PostgreSQL, Oracle