<?xml version="1.0"?>

<st-source>
<!-- 
Name: MIME
Notice: Copyright © 2008-1999 Cincom Systems, Inc.  All Rights Reserved.
Comment: This package provides the core framework allowing to manipulate (parse, decode) internet mail messages. Internet messaging and electronic mail standards were defined initially to consist exclusively of plain, US-ASCII text. Such text messaging standards are defined in RFC 822, which is the basis for almost all subsequent internet mail and messaging standards. A text message, as defined in RFC 822, consists of two parts: a header and a body. The header is a collection of fields (date, from, subject). The body, which is optional, consists simply of lines of text.

This representation is too limiting for many purposes, however, so internet messaging standards have been extended beyond the capabilities of plain text. MIME, defined in RFCs 2045-2048, provides a standard for embedding other kinds of message body content, such as non-US-ASCII text, binary data, such as executables and images, and multi-part messages. In short, MIME

	- defines the structure of multi-part messages
	- provides a mechanism for specifying the type of information in the message (content-type)
	- describes how non-ASCII information can be transported
	- describes how non-ASCII information can be encoded in message headers
	- defines 7-bit transport and content transfer encoding


	Creating Mime Entities

A MIME entity is either a header element or a body element in some enclosing message, either a mail message or an HTTP message. Accordingly, they are created for inclusion in some top-level container, the message. For example, you might start with a basic RFC 822 style message:

	message := RFC822Message new

This creates a very basic message. You can inspect the result to examine its structure. To this basic structure you add MIME entities as needed. MailMessage provides support for additional message entities that are typically used only by mail messages, such as attachments. Of particular interest is that it has a header instance variable, which holds a set of header entities (initially empty), and a value instance variable, which holds its body, if any.

A better way to create a new MIME entity is to send one of these instance creation messages to MimeEntity or a subclass:

	#source: aStream - Creates a simple MIME entity from the contents of the Stream.
	#fileName: fileNameOrString withEncoding: aSymbol - Creates a simple MIME entity containing the contents of the file, guessing at the MIME type from the aFileName extension. If aFileName does not have an extension, returns default 'application/octet-stream'.
	#newTextHTML - Creates a new entity with type text/html.
	#newTextPlain - Creates a new entity with type text/plain.
	#readFrom: aStream - Creates a new entity from aStream, parsing the information, such as message headers, based on its structure.
	#readHeaderFrom:  aStream -  Creates a new entity from aStream, parsing the header information only.

The result is a new MimeEntity with the appropriate MIME type specified in the entity header. These will be used in later examples.


	Adding Header fields

MIME defines a few specific header fields and permits others. Net Clients supports adding any of these to the header. Headers that are defined by MIME are supported by specific set/get accessors. A sampling of the setters is (browse the MimeEntity accessing method category for more, and for getters):

	#charset: aString - Set the 'charset=aString' to the Content-type header.
	#contentId: aString - Add or set the 'ContentId:' header to aString.
	#contentLength: anInteger - Add or set the 'Content-length:' header to anInteger.
	#contentTransferEncoding: encodingNameString - Add or set the 'Content-transfer-encoding:' header to encodingNameString.
	#contentType: aString - Add or set the 'Content-type:' header to aString, with the default charset.
	#mimeVersion: aString - Add or set the 'Mime-version:' header to aString.
	#addMimeVersion - Add or set the 'Mime-version:' header with the default version string.

Any of these can be sent to the message already created to set the header. For example:

	message contentType: 'text/xml'.

The messages create the appropriate instances of HeaderField subclasses and add them to the header.

Some header fields take optional parameters and other information. These are implemented as subclasses of ValueWithParametersField, and provide additional messages for supplying the additional information. For example, ContentTypeField represents the content-type field in MIME and HTTP protocols, which may specify a type, a subtype, may specify a character set for text types, and boundary for multipart subtypes, and so on. If you use these header fields, browse the class for supporting messages.

Additional fields can be added to the header for informational purposes. This is done by creating a HeaderField with a value and adding that to the message header. For example:

	header := HeaderField name: 'MyField'.
	header value: 'dummy'.
	message addField: header.

Another useful message is getFieldAt:, which takes a field name as a String for its argument. If the field already exists, the message returns the field and its value. If the field does not yet exist, it is created, and you can then provide a value for it. The following expression checks for a field's existence, creates it if necessary, and sets its value:

	(message getFieldAt: 'bogus' ) value isNil
		ifTrue: [ message fieldValueAt: 'bogus' put: 'stuff' ]

The Mail and HTTP packages provide protocol for adding headers that are meaningful for specific types of messages.


	Adding a body

Instances of MimeEntity and its subclasses include a value instance variable that contains the message body, which holds either a SimpleBody or a MultipartBody (which are subclasses of MimeMessageBody).


	Simple Body

Initially value holds an empty SimpleBody. To access the body, send a body message to the message:

	message body

To give the body a simple content, such as a String, send a value: message to it:

	message contents:: 'web text'

A better way to specify the body is when the message is created initially, by using the source: instance creation method:

	message := MimeEntity source: 'some text' readStream.

There are a couple other instance creation methods, defined in MimeEntity (such as readFrom: illustrated below), that do the same. This has the advantage of creating the message with the correct content type, and creating the SimpleBody with value 'some text'.

The following is a more complete example of creating a MIME mail message using this last approach:

	string :=
'Date: 27 Aug 76 09:32 PDT
From: Jon Doe <JDoe@This-Host.This-net>
Subject: Re: The Syntax in the RFC
Sender: KSecy@Other-Host
Reply-To: Sam.Irving@Reg.Organization
To: George Jones <Group@Some-Reg.An-Org>,
Al.Neuman@MAD.Publisher
Message-ID: <4231.629.XYzi-What@Other-Host>

A bunch of text.
'.
	message := MimeEntity readFrom: string readStream.

This specifies the entire message, including message headers (Date,From, Subject, etc.) as a String in the form specified for a MIME message. The last line uses the readFrom: instance creation method to create the MIME message from the String.


	Multipart Body

MIME provides the capability of multipart messages, consisting of multiple MIME body elements preceded, followed, and separated by a boundary marker. NetClients represents a multipart message as a message with a MultipartBody, which holds a collection of MimeEnties as body parts. To create a multipart body, simply send an addPart: message to the MIME message with a MimeEntity as the argument:

	message := MimeEntity source: 'some text' readStream.
	newEntity := MimeEntity source: 'some other text' readStream.
	message addPart: newEntity.

If the MIME message currently has a SimpleBody, addPart: mutates the current body to a MultipartBody, with the original body as the first body part, and adds the new element to the collection of body parts. If the body is already a MultipartBody, the new part is simply added to the collection. As explained above, using the source:, or other MimeEntity instance creation methods, invokes the MIME framework to provide the correct content type.

Body parts are separated by a 'boundary'. A default boundary is provided, but you probably want to specify your own. After the body has been converted to multipart, you can specify the boundary by sending a
boundary: message to the MIME message:

	message boundary: 'some_string_that_will_not_occur_naturally'

The boundary delimiter begins with -  (two hyphens), which are provided by VisualWorks. The String may be up to 70 characters, and must be such that it does not occur in any of the embedded elements. Any strategy to create such a String can be used.


	Creating a File Attachment

You can create a file attachment for an external file by sending the fileName: instance creation message to MimeEntity. For example:

	attachment := MimeEntity fileName: 'visual.im' withEncoding: #'Windows-1252'.

The entity is created with the proper content type. You can then add the entity to a message as a part, as described above.


	Reading and Writing a MIME Message.

If you have a message in MIME format, whether simple or multipart, in a file, you can create a MIME message from it directly. To do so, you pass the message through the parser by sending readFrom: with a
ReadStream on the file:

	stream := String new readWriteStream.
	message writeOn: stream.
	stream reset.
	message := MimeEntity readFrom: stream.

If your text file properly defines a message, the result is a message with all the parts defined in the file. This differs from:

	MimeEntity source: stream

which will create a new, single part, text/plain message with the stream being the source of its body.


	Accessing headers

Several messages are provided for accessing header fields. The following messages, and variations of them, are defined in the accessing field and body parts message category in MimeEntity. These
provide general access to fields in MIME entities.

	#fieldValueAt: aString - Returns the value of the header field named aString, or nil if it doesn't exist or is empty.
	#fieldsAt: aString - Returns a List of all header fields named aString. This is useful if there are multiple header fields with the same name, as is permitted by MIME.
	#getFieldAt: aString - Returns the value of header field aString if it exists; otherwise creates the field.

Notice that the header fields have initial lower-case names. There are specific messages for accessing particular fields as well, especially content related fields. These are implemented using the above methods, providing an easier to use interface.

	#contentId - Returns the value of the content-id field.
	#contentLength - Returns the value of the content-length field.
	#contentType - Returns the value of the content-type field.

RFC822Message defines additional messages for accessing headers of specific interest in mail messages. Browse the accessing method category for the complete set. The following is a small sample of the available messages.

	#to - Returns the contents of the to field.
	#from - Returns the contents of the from field.
	#bcc - Returns the contents of the bcc field.
	#cc - Returns the contents of the cc field.
	#date - Returns the value of the date field.
	#replyTo - Returns the value of the reply-to field.


	Accessing bodies

A MIME message may have a single-part (SimpleBody) or multipart (MultipartBody) body, or no body at all. The basic MimeEntity protocol for accessing bodies consists of these
messages:

	#contents - For a simple message, returns a ByteString containing the message body. For a multi-part message, returns an OrderedCollection of the parts, which are instances of MimeEntity.
	#parts - Returns an OrderedCollection of the message parts of a multi-part message (same as contents). The collection is empty for a simple message.
	#partAt: partIndex - Returns the MimeEntity at the specified partIndex of the collection returned by parts.

To test whether the body is simple or multi-part, send an isMultipart message to the message. The response is a Boolean which you can use to choose how to process the message. For example, this returns the
message text as a ByteString for either:

	message isMultipart ifTrue: [ ^(message partAt: 1) contents ].
	^message contents.

Given a multi-part message, which will be an instance of MailMessage, there are additional messages you can send it to operate on its text part or parts. Browse the accessing and utility method categories for these operations. For example, texts are often included in a variety of formats, such as text/plain or text/html, providing the mail reader a choice of text formats to use for display purposes. When there are alternatives, the text type itself is multipart/alternative, and each format is provided as a part. The following messages allow you access these formats and parts.

	#allAlternativeTextFormats - Returns a collection of all alternative text formats.
	#allTextParts - Returns a collection of all text parts.
	#prepareForTransport - Verifies that fields are in proper order, that required fields are present, that assigned boundaries are present, and that attachments are base64 encoded.
	#replaceTextWith: aString - Replaces the message body with aString, and resets the content type.
	#saveAttachment: aMimeEntity on: aStream - Writes aMimeEntity on aStream using appropriate encoding.
	#saveAttachmentAt: anIndex on: aStream - Writes the message attachment at anIndex on aStream with appropriate encoding.
	#saveTextOn: aStream - Writes the message text on aStream. If the text includes alternates, all are written in the order they occur.
	#saveTextOn: aString inPreferenceOrder: anArrary - Writes all alternate message texts on aStream in the order specified by anArray, e.g., #('html' 'plain' '*')
	#text - Returns the plain text part of a message text.
	#textInPreferenceOrder: - Returns text in all alternative formats, in the specified order.


	Getting content information of a message part

Message parts are MIME entities, and so you can get the usual MIME information from it. For example, the following code checks a multi-part message to verify that the first part is plain text, and if so, returns the text:

	(message isMultipart and: [ ( message partAt: 1 ) contentType = 'text/plain' ])
		ifTrue: [ ( message partAt: 1 ) contents ].


	Header fields transfer encoding

To send header fields in non-ASCII character sets you will need to specify a character set for the message header ( MimeEntity>>headerCharset:).
stream := String new writeStream.
(MailMessage new)
.....
		charset: 'iso-8859-2';
		date: Timestamp now; 
		headerCharset: 'iso-8859-2';
		writeOn: stream.
stream contents
The header fields will get encoded as
From: =?iso-8859-2?Q?=AElva=20?= =?iso-8859-2?Q?=E1belsk=E9=20?= =?iso-8859-2?Q?=F3dy?=<zzz@m.com>
Subject: =?iso-8859-2?B?UO1sabkgvmx1b3Vr/V9ubyBoZWFkZXJDaGFyc2V0IA==?=
To: =?iso-8859-2?Q?Bo=BEidar=20?= 




	Message transfer encoding

MIME entities can be encoded using the encoding scheme specified in the content-transfer-encoding field. Encoding is necessary to encode data for transfer over some transfer protocols, such as SNMP which restricts mail message data to 7-bit US-ASCII.

Net Clients supports the two standard encoding schemes, base64 and quoted-printable, which transform messages to conform to the 7-bit USASCII restriction. Other encodings can be specified, but Net Clients does not provide the encoding.

Quoted-printable is used for message bodies that are mostly 7-bit USASCII, but may contain a few 8-bit characters. Any 8-bit characters are transformed to be represented as character $= followed by the two-digit hexadecimal representation.

Base64 encoding is used for message bodies if quoted-printable is not appropriate. It represents arbitrary octets in a 65 character subset of USASCII. Base64 encoding can be used, for example, to include binary data in a mail message, such as in an attachment.

Net Clients protocol for encoding and decoding is very simple. These messages are sent to a MimeEntity, typically a SimpleBody:

	#addContentTranferEncoding: anEncoding - Modifies the receiver MimeEntity to include the content-transfer-encoding header for anEncoding, and encodes the body as appropriate. The String specifying anEncoding is not validated, but must be either 'base64' or 'quoted-printable' (case sensitive) for the body to be encoded. If you do not add the content transfer encoding for mail message attachments that are not plain text, the mail client adds default encoding 'base64' when the message is sent.
	#removeContentTransferEncoding - Removes the content-transfer-encoding header and decodes the body if it had been either base64 or quoted-printable encoded.

For example, if you added a part that should be encoded (and did not use a message like addFileAttachment: that handles the encoding), you can access the part and apply the encoding:

	( message parts at: 2 ) addContentTransferEncoding: 'base64'

Removing content transfer encoding is a configurable setting.

	MimeBuildHandler mailRemoveContentTransferEncoding: true.

The default setting is false. If set to true, content transfer encoding is removed while parsing the message. To remove the encoding from a part of a received message, send:

	( message parts at: 2 ) removeContentTransferEncoding


	Working with Attachments

Attachments are files included in a message, but typically in a format recognizable to an application other than the reader. The following messages for handling attachments is provided by MailMessage.

	#addFileAttachment: aFilename - Adds a part to the message containing the specified aFilename, and encodes it appropriately.
	#attachments - Returns an OrderedCollection of MimeEntity instances containing the attachments.
	#attachmentAt: index - Returns the MimeEntity containing the attachment at index.
	#attachmentNames - Returns a List of the file names of the attachments.
	#saveAttachment: aMimeEntity on: aStream - Writes the attachment aMimeEntity on aStream.
	#saveAttachmentAt: index on: aStream - Writes the attachment at index on aStream.

There is an configurable setting 

MimeParserHandler class>>saveAttachmentsAsFiles 

that allows saving attachements in to external files while parsing a mail messages. 

If the option is true the builder will save attachments in to an external file. The external file name will be create from 'Content-Disposition' field filename attribute if any. If there is no filename in the attachment the random name will be generated. The AttachmentFilename notification will be raised allowing to provide a new file name. If the #saveAttachmentAsFile option is false (default for mime entities) the attachment will be save in to internal stream. Attachment files are saved in a directory, which is by default named mail-temp-files located in the image directory. 

To change the default directory use the following expression:

MimeParserHandler defaultAttachmentDirectory:  'myDirectory'.

AttachmentFilename, allowing the user to override the file name on the fly.
Here is an example:
Saving attachments directly into external files while parsing a message (streams the attachment directly into a file 'my-budd.jpg' in the current directory; the corresponding message part's source becomes an external stream on that file)

	input := 
'From: zz@holcim.com
Content-Type: multipart/related;
	boundary="- 11"

- - 11
Content-Type: image/jpg; name="budd.jpg"
Content-Disposition: attachment; filename="budd.jpg"
Content-Transfer-Encoding: base64

QWxhZGRpbjpvcGVuIHNlc2FtZQ==

- - 11- 
' readStream.
	[	message := MailBuildHandler new
				saveAttachmentsAsFiles: true;
				removeContentTransferEncoding: true;
				readFrom: input. 
	] 
		on: AttachmentFilename
		do:  [ :ex | ex resume: 'my-', ex filename asFilename tail ].
DbIdentifier: bear73
DbTrace: 114081
DevelopmentPrerequisites: #(#(#any 'NetClientBase' ''))
PackageName: MIME
Parcel: #('MIME')
ParcelName: MIME
PrerequisiteParcels: #(#('NetClientBase' ''))
PrintStringCache: (751 101,mkobetic)
Version: 7.6
Date: 12:33:43 pm February 1, 2008
 -->
<time-stamp>From VisualWorks®, 7.6 of February 1, 2008 on February 1, 2008 at 12:33:43 pm</time-stamp>


<do-it>(Dialog confirm: 'You are filing-in a Parcel source file!\\While this is possible it will not have\the same effect as loading the parcel.\None of the Parcel''s prerequisites will\be loaded and none of its load actions\will be performed.\\Are you sure you want to file-in?' withCRs) ifFalse: [self error: 'Parcel file-in abandoned.  Choose terminate or close.']</do-it>

<class>
<name>HeaderFieldValue</name>
<environment>Net</environment>
<super>Core.Object</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>separators value </inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Net-MIME-Support</category>
<attributes>
<package>MIME</package>
</attributes>
</class>

<comment>
<class-id>Net.HeaderFieldValue</class-id>
<body>HeaderFieldValue parses, composes and holds values for header fields

Instance Variables:
	separators	&lt;SequenceableCollection&gt;	field separators
	value	&lt;String&gt;	parameter  value


</body>
</comment>

<class>
<name>ValueWithParams</name>
<environment>Net</environment>
<super>Net.HeaderFieldValue</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>params </inst-vars>
<class-inst-vars>defaultParameterValueEncoding </class-inst-vars>
<imports></imports>
<category>Net-MIME-Support</category>
<attributes>
<package>MIME</package>
</attributes>
</class>

<comment>
<class-id>Net.ValueWithParams</class-id>
<body>ValueWithParams parses, composes and holds values for header fields that have value and parameters.  

Instance Variables:
	params	&lt;Dictionary&gt;	filed parameters

Class Instance Variables:
	defaultParameterValueEncoding	&lt;Symbol&gt; if set, the specified encoding will be applied if we encounter non-ascii characters in parameter values (technically this is against the MIME specification, but seems to be a widespread practice, e.g. many web browsers do that with filenames of uploaded files)

For example, Content-Type field

  content := "Content-Type" ":" type "/" subtype
             *(";" parameter)
             ; Matching of media type and subtype
             ; is ALWAYS case-insensitive.

  parameter := attribute "=" value
  value := token / quoted-string

If while parsing parameter value non-ASCII characters would be detected the parser is going to raise NonASCIIFieldParamException. If there is no handler for this exception the parameter value will be set to stream bytes.
</body>
</comment>

<class>
<name>MessageScanner</name>
<environment>Net</environment>
<super>Net.SimpleScanner</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars></inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Net-MIME-Support</category>
<attributes>
<package>MIME</package>
</attributes>
</class>

<comment>
<class-id>Net.MessageScanner</class-id>
<body>This class is an abstract class for all mail-related scanners. It provides some common printing methods for lexical types that are used by all subclasses -- atom, text, quoted text


</body>
</comment>

<class>
<name>MimeDispatcherHandler</name>
<environment>Net</environment>
<super>Core.Object</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>dispatcher headerOnly </inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Net-MIME-Support</category>
<attributes>
<package>MIME</package>
</attributes>
</class>

<comment>
<class-id>Net.MimeDispatcherHandler</class-id>
<body>This class is strategy class for MimeDispatcher. When mime dispatcher traverses message, it calls back its message handler notifying it where it is in the message. All possible callbacks are declared in 'action callbacks' protocol. Subclasses override these methods to implement such tasks as printing and searching of messages.


Instance Variables:

	dispatcher	&lt;MimeDispatcher&gt;	my dispatcher. Handler typically initialtes work by invoking dispatcher's method visit:. Dispatcher then executes series of callbacks to the hahdler
	headerOnly	&lt;Boolean&gt;	if true, dispatcher will stop after processing message header


</body>
</comment>

<class>
<name>MimeOutputHandler</name>
<environment>Net</environment>
<super>Net.MimeDispatcherHandler</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>streams fieldStream </inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Net-MIME-Support</category>
<attributes>
<package>MIME</package>
</attributes>
</class>

<comment>
<class-id>Net.MimeOutputHandler</class-id>
<body>This is a common superclass for printing and writing of messages on a stream according to rfc822 syntax rules. Writing renders messages on the output stream in full conformance with rfc822 syntax, so it can be used to send message directly to SMTP server, for example. Printing is rendering messages for inspection during development. Therefore the logic is slightly different to enhance readability of the output, entities are indented and binary data is only sumarized.

Instance Variables:
	streams	&lt;OrderedCollection of: Stream&gt; stack of various stream compositions used at different levels of the entity being written
	fieldStream	&lt;WriteStream&gt; caches a stream used to convert field values into streams, it is reused for performance reasons

</body>
</comment>

<class>
<name>MimeWriteHandler</name>
<environment>Net</environment>
<super>Net.MimeOutputHandler</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>preferFieldSource originalStream binaryMode applyTransferEncoding </inst-vars>
<class-inst-vars>applyTransferEncoding </class-inst-vars>
<imports></imports>
<category>Net-MIME-Support</category>
<attributes>
<package>MIME</package>
</attributes>
</class>

<comment>
<class-id>Net.MimeWriteHandler</class-id>
<body>This class implements writing messages on output stream in full conformance of rfc822 syntax. It can be used to send message directly to SMTP server without creating intermediate streams. If some constituent entities require content transfer encoding, it is applied dynamically and transfer-encoded output is written directly on output stream with no intermediate buffers created. Header fields are arranged in the order imposed by rfc822.

Instance Variables:
	preferFieldSource	&lt;Boolean&gt; if true , a header field source will be used to print instead of parsed values. If application resends, routes of forwards messages, all mail protocols strongly advise against and changes in message header fields. These changes are unavoidable if all fields are to be parsed and later composed when a message is resent/forwarded. In this situation a wise approach is to prefer source, so fields are copied to the output exactly the way thay have been received
	originalStream	&lt;EncodedStream&gt; original stream or wrapper for internal streams to print a message
	binaryMode	&lt;Boolean&gt; preserves the output stream mode. It will be changed to binary while writing a message
	applyTransferEncoding	&lt;Boolean&gt; flag allowing to suppress transfer encoding; it is used for #writeContentsOn:

Class Instance Variables:
	applyTransferEncoding	&lt;Boolean&gt; default value for the applyTransferEncoding flag

</body>
</comment>

<class>
<name>MessageStream</name>
<environment>Net</environment>
<super>Core.Stream</super>
<private>true</private>
<indexed-type>none</indexed-type>
<inst-vars>stream position readLimit </inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>HTTP</category>
<attributes>
<package>MIME</package>
</attributes>
</class>

<comment>
<class-id>Net.MessageStream</class-id>
<body>MessageStream is the super class for message streams. The streams help to parse MIME and HTTP messages.

Instance Variables:
	position	&lt;Integer&gt;	current position
	readLimit	&lt;Integer&gt;	maximum number bytes to read
	stream	&lt;Stream&gt;	external, socket or internal stream to parse

</body>
</comment>

<class>
<name>BoundedStream</name>
<environment>Net</environment>
<super>Net.MessageStream</super>
<private>true</private>
<indexed-type>none</indexed-type>
<inst-vars>boundary boundaryDashes partNumber </inst-vars>
<class-inst-vars></class-inst-vars>
<imports>
			private OS.IOConstants.*
			</imports>
<category>HTTP</category>
<attributes>
<package>MIME</package>
</attributes>
</class>

<comment>
<class-id>Net.BoundedStream</class-id>
<body>BoundedStream is an abstract class for the bounded read and write streams. Bounded streams help parsing multipart MIME bodies, where individual parts are separated by boundary strings.

Instance Variables:
	boundary	&lt;ByteArray&gt;	bytes that defines pattern to search for
	boundaryDashes &lt;ByteArray&gt; bondary dashes
	partNumber  &lt;Number&gt; a part number





</body>
</comment>

<class>
<name>BoundedWriteStream</name>
<environment>Net</environment>
<super>Net.BoundedStream</super>
<private>true</private>
<indexed-type>none</indexed-type>
<inst-vars></inst-vars>
<class-inst-vars></class-inst-vars>
<imports>
			private OS.IOConstants.*
			</imports>
<category>HTTP</category>
<attributes>
<package>MIME</package>
</attributes>
</class>

<comment>
<class-id>Net.BoundedWriteStream</class-id>
<body>BoundedWriteStream writes MIME part boundaries for multipart messages. This version is used by the MimeWriteHandlers. To ensure proper specification compliance of the output it writes them as bytes, therefore the underlying stream has to be a binary stream.</body>
</comment>

<class>
<name>HeaderField</name>
<environment>Net</environment>
<super>Net.MessageElement</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>name </inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Net-MIME-Support</category>
<attributes>
<package>MIME</package>
</attributes>
</class>

<comment>
<class-id>Net.HeaderField</class-id>
<body>This class represents header fields in RFC822 and MIME messages. Related RFCs numbers are 822, 2045-2048 and 2183.

Each field has name and value. The general syntax of RFC822 fields is
	name: value&lt;CrLf&gt;
Fields can occupy more than one line using line folding techniques. Values can be structured or unstructured. Unstructured value is considered to be text (RFC822 defines text as any sequence terminated by CrLf, but MIME specifies how non-ASCII characters can be encoded into text, so there is some decoding involved when text lexems are parsed or composed. This class provides enough logic to process
unstructured fields. Its subclasses implement structure fields.

Structured fields have defined syntax. For each structured field a subclass of HeaderField is created to capture its syntax and semantics. Many fields share common syntax (i.e. address fields in RFC822, date and version fields across a number of protocols. All fields with the same syntax can be handled with signle class unless semantical differences justify introduction of a separate class.

Fields are responsible for parsing themselves from a stream and composing (writing) themselves on stream. Parsing is done with a help of a scanner which has interface of RFC822Scanner.  Fields specific to protocols derived from RFC822 use specialized scanners that support protocol-specific syntax. A field can parse itself from a string, stream or RFC822 scanner. 

	Examples:

	ContentTypeField fromLine: 'content-type: text/html'
	HeaderField readFrom: 'content-type: text/html' readStream
	HeaderField parse: (RFC822Scanner on: 'content-type: text/html' readStream)

	Handling non ASCII characters:
	string := 'Content-Disposition: attachment; filename="PÅ™ÃÅ™Ã"'. 
	HeaderField readFrom: string readStream signalNonAsciiCharacters: false. - will allow parsing non ASCII characters
	HeaderField readFrom: string readStream -  will raise the NonASCIICharacter exception
	


Internally, string is wrapped in a stream, stream is wrapped in RFC822 scanner. Parsing is invoked  by calling #parse:.

Parsing and composing is always done on the instance side; class methods create new instance and invoke instance side method to complete parsing. The only processing done on the class side is reading field name and finding proper header field class for specified field name (field factory).

Note: the current factory scheme is based on scanning subclasses and might need a little more work because each protocol may need its own field factory. Currently a subclass implements class method #fieldNames that answers an array of field types that this class recognizes.

Each field also stores its source. This is significant for mail protocol because it allows to reproduce fields verbatim if a message containing the field is to be resent, forwarded, etc. RFC822 and other standards strongly advise against any rewriting of message fields if it is not absolutely necessary. Source is stored in inherited instance variable source which is a stream; the value is the stream's contents. Subclasses may disable storing source. See MessageElement for more details.

Typically fields are built not by MimeEntity itself, but rather by MimeParserHandler. MimeParserHandler decides whether to store source in a field. One other application of storing source -- if a message is read from mail file containing multiple message, a MailFileReader stores StreamSegment as method source. This way each message element knows its exact location in the file and fields can be changed and written back to the file in place one-by-one.

Instance Variables:
	name	&lt;String&gt; the field name

Shared Class Variables:
	FieldsRegistry	&lt;Dictionary key: String value: Class&gt; maps header field names to corresponding classes used to represent those fields

Class Variables:

	FieldsRegistry	&lt;IdentityDictionary&gt;	 used to store field factory bindings;</body>
</comment>

<class>
<name>StructuredHeaderField</name>
<environment>Net</environment>
<super>Net.HeaderField</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>comment </inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Net-MIME-Support</category>
<attributes>
<package>MIME</package>
</attributes>
</class>

<comment>
<class-id>Net.StructuredHeaderField</class-id>
<body>This class is an abstract class for all structured fields. Each specific structured field type has to implement its own parsing by overriding #parse: method. This class only provides some utility method to help parse some common constructs such as parameters.

Instance Variables:
	comment	&lt;String&gt; stores the optional field comment

</body>
</comment>

<class>
<name>CollectionField</name>
<environment>Net</environment>
<super>Net.StructuredHeaderField</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>separator </inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Net-MIME-Support</category>
<attributes>
<package>MIME</package>
</attributes>
</class>

<comment>
<class-id>Net.CollectionField</class-id>
<body>CollectionField is super class for header fields which value is collection.

Instance Variables:
	separator	&lt;Character&gt; the character used to separate field value elements

</body>
</comment>

<class>
<name>AttachmentFileExists</name>
<environment>Net</environment>
<super>Net.NetError</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>filename </inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>MIME</category>
<attributes>
<package>MIME</package>
</attributes>
</class>

<comment>
<class-id>Net.AttachmentFileExists</class-id>
<body>This error will be raised if the file name provided by the AttachmentFilename notification already exists. This exception is resumable with a new file name to use for the attachment file. If it is resumed without parameter or the new filename also exists that file will then be deleted and reused for the attachment. Obviously if this exception is not resumed, the attachment will not be saved and parsing of the enclosing message containing this attachment will end here, unfinished.

Instance Variables:
	filename	&lt;Filename&gt; the existing file

	Example:

	input := 
'From: zz@holcim.com
Content-Type: multipart/related;
	boundary="--11"

----11
Content-Type: text/plain; name="budd.txt"
Content-Disposition: attachment; filename="budd.txt"
Content-Transfer-Encoding: base64

QWxhZGRpbjpvcGVuIHNlc2FtZQ==

----11--
' .

	[[	message := MailBuildHandler new
				saveAttachmentsAsFiles: true;
				readFrom: input readStream. 
	]	on: AttachmentFilename
		do:  [ :ex | ex resume: 'temp-', ex filename tail ].
	] on: AttachmentFileExists do: [ :ex | ex resume: 'anotherTemp.txt' ]

</body>
</comment>

<class>
<name>ScalarField</name>
<environment>Net</environment>
<super>Net.StructuredHeaderField</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars></inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Net-MIME-Support</category>
<attributes>
<package>MIME</package>
</attributes>
</class>

<comment>
<class-id>Net.ScalarField</class-id>
<body>This class is the superclass for all fields whose value represents a scalar value. Examples are date field, maibox field, version field, etc. So for the date field field's value will be parsed date object, not a string representation of date (field's source would still hold the string representation). Each specific field type should implement method #parse:</body>
</comment>

<class>
<name>SingleMailboxField</name>
<environment>Net</environment>
<super>Net.ScalarField</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>address </inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Net-MIME-Support</category>
<attributes>
<package>MIME</package>
</attributes>
</class>

<comment>
<class-id>Net.SingleMailboxField</class-id>
<body>This class represents field whose value is a mailbox-spec as defined by RFC822. Several field types in RFC822 messages are single mailbox fields. Mailbox allows to specify address-spec as well as an alias. MIME allows to encode non-ASCII characters in the alias part.

Mailbox examples:
	Alfred Neuman &lt;Neuman@BBN-TENEXA&gt;
	"George, Ted" &lt;Shared@Group.Arpanet&gt;

The formal grammar is as follows:

     address     =  mailbox                      ; one addressee
                 /  group                        ; named list

     group       =  phrase ":" [#mailbox] ";"

     mailbox     =  addr-spec                    ; simple address
                 /  phrase route-addr            ; name &amp; addr-spec

     route-addr  =  "&lt;" [route] addr-spec "&gt;"

     route       =  1#("@" domain) ":"           ; path-relative

     addr-spec   =  local-part "@" domain        ; global address

     local-part  =  word *("." word)             ; uninterpreted
                                                 ; case-preserved

     domain      =  sub-domain *("." sub-domain)

     sub-domain  =  domain-ref / domain-literal

     domain-ref  =  atom                         ; symbolic reference

</body>
</comment>

<class>
<name>NetworkEntityDescriptor</name>
<environment>Net</environment>
<super>Net.MessageElement</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>alias comment </inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Net-MIME-Support</category>
<attributes>
<package>MIME</package>
</attributes>
</class>

<comment>
<class-id>Net.NetworkEntityDescriptor</class-id>
<body>This class is an abstract class for mailbox address and mail group address descriptors as defined in RFC822. All addresses may have an address part and alias part. Address part defines the actual networ address while alias is a human-readable name associated with the address.
Main purpose of descriptors is to be able to parse and compose address string into indivudual components, check syntax, provide canonical value and decode non-ascii characters in alias.
While address syntax is defined in rfc822, the individual tokens (namely, phrase representing alias) are further developed in Mime (rfc2048) to provide for non-ascii characters. Therefore, message is parsed using rfc822 syntactic rules including rfc822 character table. Once parsed, individual lexical tokens are scanned using Mime rules.

Subclasses must implement the following messages:
	printing
	printCanonicalValueOn:

Instance Variables:
	alias	&lt;OrderedCollection&gt;  Collection of words comprising alias. Access alias using #aliasString
	comment	&lt;String&gt;  Comment string if present as part of the address
</body>
</comment>

<class>
<name>MimeMessageBody</name>
<environment>Net</environment>
<super>Net.MessageElement</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars></inst-vars>
<class-inst-vars></class-inst-vars>
<imports>
			private OS.IOConstants.*
			</imports>
<category>Net-MIME-Support</category>
<attributes>
<package>MIME</package>
</attributes>
</class>

<comment>
<class-id>Net.MimeMessageBody</class-id>
<body>This is an abstract superclass for Mime message bodies. Message bodies can be simple or multi-part.  Mail messages can be complex and deeply nested and their parts can be very large. In many cases we may not need contents of one or more message parts, so it makes sense not to parse messages too deep and only read data into memory when we actually need it. For this reason parsing and value initialization is done lazily. Source stream is remembered so that parsing can be done at later time.

Messages can also have external parts where data can be retrieved from external sources (e.g. from external file or FTP servers). Because streams are a universal mean of accessing external resources, storing source stream also encapsulates part access logic.

</body>
</comment>

<class>
<name>ValueWithParametersField</name>
<environment>Net</environment>
<super>Net.StructuredHeaderField</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>separators </inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Net-MIME-Support</category>
<attributes>
<package>MIME</package>
</attributes>
</class>

<comment>
<class-id>Net.ValueWithParametersField</class-id>
<body>This class is an  abstract class that provides infrastructure for Mime and HTTP fields that have value and optional parameters. The class defines logic for parsing and composition. Subclasses may perform more detailed parsing of field value. Subclasses also add accessor methods to access specific parameters (such as 'charset' or 'boundary' in content-type field or 'fileName' in content-disposition field

For example, the syntax of content-type field as defined in rfc2045 is as follows:

  content := "Content-Type" ":" type "/" subtype
             *(";" parameter)
             ; Matching of media type and subtype
             ; is ALWAYS case-insensitive.

  parameter := attribute "=" value
  value := token / quoted-string

Some of the most important fields, namely content-type and content-disposition types are value with parameters fields.


</body>
</comment>

<class>
<name>SingleValueWithParametersField</name>
<environment>Net</environment>
<super>Net.ValueWithParametersField</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars></inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Net-MIME-Support</category>
<attributes>
<package>MIME</package>
</attributes>
</class>

<comment>
<class-id>Net.SingleValueWithParametersField</class-id>
<body>SingleValueWithParametersField is an abstract class that provides infrastructure for Mime and HTTP fields that have value and an one parameter
</body>
</comment>

<class>
<name>VersionField</name>
<environment>Net</environment>
<super>Net.ScalarField</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars></inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Net-MIME-Support</category>
<attributes>
<package>MIME</package>
</attributes>
</class>

<comment>
<class-id>Net.VersionField</class-id>
<body>This class represents version fields. Version is used in MIME and in HTTP. 
Version field syntax for MIME messages is as follows:

     version := "MIME-Version" ":" 1*DIGIT "." 1*DIGIT

     MIME-Version: 1.0</body>
</comment>

<class>
<name>NumericField</name>
<environment>Net</environment>
<super>Net.ScalarField</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars></inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Net-MIME-Support</category>
<attributes>
<package>MIME</package>
</attributes>
</class>

<comment>
<class-id>Net.NumericField</class-id>
<body>I represent numeric fields such as content-length. My value is a number. String representation of value can be accessed using #asString

Example:
	Content-length: 1123

Instance Variables:
</body>
</comment>

<class>
<name>MimeEntity</name>
<environment>Net</environment>
<super>Net.MessageElement</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>header headerCharset </inst-vars>
<class-inst-vars>builderClass printerClass writerClass </class-inst-vars>
<imports></imports>
<category>Net-MIME-Support</category>
<attributes>
<package>MIME</package>
</attributes>
</class>

<comment>
<class-id>Net.MimeEntity</class-id>
<body>I represent Mime entity as defined by RFC2045. Mime entities form a basis for mail messages and HTTP. A Mime entity is basically and object that has header and a body. Header consists of fields each of which has a name and (potentially complex) value. A body can be either a simple or multipart body. Simple body has contents, which is either a string, or byte array. Multipart body consists of multiple parts, each of which is a Mime entity in itself.

Header fields describe entity contents or provide additional information. Header fields are represented by hierarchy of classes starting at HeaderFields. Some fields have known structure and syntax, the rest are considered to be just a text field. Mime defines a few standard fields, they all start with content-*. The most important field is content-type field. It defines type of information that entity represents (see rfc2045 for details). MimeEntity provides methods to access/set aspects of content-type field:
	type		(default 'text')
	subtype		('plain')
	contentType (=type/subtype	default 'text/plain')
	charset		(default charset from parent entity (RFC822Message/HttpEntity) or if the parent is nil - 'iso-8859-1')
	boundary	(default nil, should be specified for multipart entities)
	headerCharset (default nil, is used to encode header fields that have non ASCII characters)
Other notable fields include content-length (contentLength) and content-disposition (fileName).
Fields may occur more than once and sometimes have to occur in particular sequence. MimeEntity allows multiple fields with the same name and remembers field order. There are various methods that allow to manipulate fields.

Methods are provide to access and travers entity body and parts. Mime entity allows to add and remove parts. An entity is created as a simple entity by default. As parts are added, it will mutate itself into multipart entity transparently. It is also possible to convert multiupart entity back into a single-part one, merging fields in the process.

MimeEntity also supports Visitor patterns. Nodes can be traversed by an outside visitor. This allows to put domain- and application-specific logic into the visitor and create visitors specific to Mail, HTTP, etc. 

Instance Variables:
	header	&lt;MessageHeader&gt; Message header contains header fields
	headerCharset	&lt;Symbol&gt; is used to encode destination addresses

Class Instance Variables:
	builderClass	&lt;Class&gt; default class of parser handler to use for parsing entities of this class
	printerClass	&lt;Class&gt; default class of dispatcher handler to use for simplified (digest) printing of entities of this class
	writerClass	&lt;Class&gt; default class of dispatcher handler to use for writing entities of this class into files and sockets


</body>
</comment>

<class>
<name>RFC822Message</name>
<environment>Net</environment>
<super>Net.MimeEntity</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars></inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Net-MIME-Support</category>
<attributes>
<package>MIME</package>
</attributes>
</class>

<comment>
<class-id>Net.RFC822Message</class-id>
<body>This class represents top-level RFC822 messages. It adds accessor methods for all fields postulated in rfc822 (to, from, cc, bcc, data, etc.).

</body>
</comment>

<class>
<name>MailboxListField</name>
<environment>Net</environment>
<super>Net.ScalarField</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>addresses </inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Net-MIME-Support</category>
<attributes>
<package>MIME</package>
</attributes>
</class>

<comment>
<class-id>Net.MailboxListField</class-id>
<body>This class represents field whose value is a list of mailboxes as defined by RFC822. Several field types in RFC822 messages (from, cc, to, etc) are mailbox list fields. Mailbox specs allows to specify address-spec as well as an alias. MIME allows to encode non-ASCII characters in the alias part. Addresses can be mailbox or mail group addresses.

Value of this field is list of addresses (strings). Addresses can be read or set using messages:
	addresses
	addresses:

When setting addresses, caller specified a collection of strings. Each string can be a single address or a sequence of comma-separated addresses. Among other things, this handles non-ascii characters in the alias part of the address. Address strings are always parsed and canonicalized inside. This class uses NetworkAddressDescriptor class to parse and compose address strings.

Mailbox list examples:
	Gourmets:  Pompous Person &lt;WhoZiWhatZit@Cordon-Bleu&gt;,
                Childs@WGBH.Boston, Galloping Gourmet@
                ANT.Down-Under (Australian National Television),
                Cheapie@Discount-Liquors;,
       Cruisers:  Port@Portugal, Jones@SEA;,
         Another@Somewhere.SomeOrg

The formal grammar is as follows:

     address     =  mailbox                      ; one addressee
                 /  group                        ; named list

     group       =  phrase ":" [#mailbox] ";"

     mailbox     =  addr-spec                    ; simple address
                 /  phrase route-addr            ; name &amp; addr-spec

     route-addr  =  "&lt;" [route] addr-spec "&gt;"

     route       =  1#("@" domain) ":"           ; path-relative

     addr-spec   =  local-part "@" domain        ; global address

     local-part  =  word *("." word)             ; uninterpreted
                                                 ; case-preserved

     domain      =  sub-domain *("." sub-domain)

     sub-domain  =  domain-ref / domain-literal

     domain-ref  =  atom                         ; symbolic reference

</body>
</comment>

<class>
<name>MailGroupDescriptor</name>
<environment>Net</environment>
<super>Net.NetworkEntityDescriptor</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>addresses </inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Net-MIME-Support</category>
<attributes>
<package>MIME</package>
</attributes>
</class>

<comment>
<class-id>Net.MailGroupDescriptor</class-id>
<body>This class represents mail group specification as defined in rfc822. It is capable of parsing and composing group specs to/from a stream and provides access to group components. A group spec may be parsed from a source stream, or created by invoking mutator methods and then written on a stream. Mime-encoded fields are handled transparently

Examples:
    Reply-To: The Committee: Jones@Host.Net,
                                     Smith@Other.Org,
                                     Doe@Somewhere-Else;

    Reply-To :  Sam.Irving@Reg.Organization
       :  George Jones &lt;Group@Some-Reg.An-Org&gt;,
          Al.Neuman@MAD.Publisher
       :  Important folk:
            Tom Softwood &lt;Balsa@Tree.Root&gt;,
            "Sam Irving"@Other-Host;,
          Standard Distribution:
                   /main/davis/people/standard@Other-Host,
                   "&lt;Jones&gt;standard.dist.3"@Tops-20-Host&gt;;

Group syntax from rfc822 is given below

     group       =  phrase ":" [#mailbox] ";"

     mailbox     =  addr-spec                    ; simple address
                 /  phrase route-addr            ; name &amp; addr-spec

     route-addr  =  "&lt;" [route] addr-spec "&gt;"

     route       =  1#("@" domain) ":"           ; path-relative

     addr-spec   =  local-part "@" domain        ; global address

     local-part  =  word *("." word)             ; uninterpreted
                                                 ; case-preserved

     domain      =  sub-domain *("." sub-domain)

     sub-domain  =  domain-ref / domain-literal

     domain-ref  =  atom                         ; symbolic reference



Instance Variables:
	addresses	&lt;SequencableCollection&gt;  Collection of NetworkAddressDescriptors constituting group</body>
</comment>

<class>
<name>RFC822Scanner</name>
<environment>Net</environment>
<super>Net.MessageScanner</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars></inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Net-MIME-Support</category>
<attributes>
<package>MIME</package>
</attributes>
</class>

<comment>
<class-id>Net.RFC822Scanner</class-id>
<body>This class performs scanning/parsing of rfc822 messages. It implements scanning routines for rfc822 lexical types (see 'multi-character scans') and general tokenization logic. 

Shared Variables:
	AtomMask	&lt;Integer&gt;  Mask for rfc822 Atoms
	CommentMask	&lt;Integer&gt;  Mask for rfc822 Comments ()
	DomainTextMask	&lt;Integer&gt;  Mask for rfc822 Domain Text
	HeaderNameDelimiterChar	&lt;Character&gt;  $:
	HeaderNameMask	&lt;Integer&gt;  Mask for rfc822 header field names
	QuotedPairChar	&lt;Character&gt;  $\
	QuotedPairMask	&lt;Integer&gt;  Mask for rfc822 quoted-pair (\x)
	QuotedTextMask	&lt;Integer&gt;  Mask for rfc822 quoted-text
	SimpleTimeZones	&lt;Dictionary key: ?type? value: ?type?&gt;  comment
	TextMask	&lt;Integer&gt;  Mask for rfc822 text
	TokenMask	&lt;Integer&gt;  Mask for rfc822 tokens
</body>
</comment>

<class>
<name>MimeParser</name>
<environment>Net</environment>
<super>Core.Object</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>handler </inst-vars>
<class-inst-vars></class-inst-vars>
<imports>
			private OS.IOConstants.*
			</imports>
<category>Net-MIME-Support</category>
<attributes>
<package>MIME</package>
</attributes>
</class>

<comment>
<class-id>Net.MimeParser</class-id>
<body>This class parses Mime entities usually coming from some external sources. As it parses various elements from the input it keeps notifying its handler. Handler is the one that actually contructs the message elements in response to parser callbacks. This division of labor is designed so that different strategies can be employed at the handler level. For example, we may choose to process some parts of the message as they are decoded without waiting for the whole message to be fully parsed; we may choose to create different entities to represent the parsed entity (e.g. MimeEntity vs. MailMessage), etc. The parsing process can be controlled by various parameters coming from the handler. 

Instance Variables:
	handler &lt;MimeParserHandler&gt; constructs the message</body>
</comment>

<class>
<name>BoundedWriteCharacterStream</name>
<environment>Net</environment>
<super>Net.BoundedWriteStream</super>
<private>true</private>
<indexed-type>none</indexed-type>
<inst-vars></inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>HTTP</category>
<attributes>
<package>MIME</package>
</attributes>
</class>

<comment>
<class-id>Net.BoundedWriteCharacterStream</class-id>
<body>BoundedWriteCharacterStream writes MIME part boundaries for multipart messages. This version is used by the MimePrintHandlers so it writes them as strings to accomodate any kind of character stream setup. Consequently the underlying stream has to be a pure character stream or it has to be set to the text mode.
</body>
</comment>

<class>
<name>AttachmentFilename</name>
<environment>Net</environment>
<super>Net.NetNotification</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>originalFilename </inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Net-HTTP-Support</category>
<attributes>
<package>MIME</package>
</attributes>
</class>

<comment>
<class-id>Net.AttachmentFilename</class-id>
<body>This notification is raised whenever an external files for attachements is about to be created. The notification provides access to the suggested filename, and allows to override it by resuming the notication with a different file name. If the notification is not handled the original suggested filename will be used. 
For example:

	input := 
'From: zz@holcim.com
Content-Type: multipart/related;
	boundary="--11"

----11
Content-Type: image/jpg; name="budd.jpg"
Content-Disposition: attachment; filename="budd.jpg"
Content-Transfer-Encoding: base64

QWxhZGRpbjpvcGVuIHNlc2FtZQ==

----11--
' readStream.
	[	message := MimeParserHandler new
				saveAttachmentsAsFiles: true;
				readFrom: input. 
	] 
		on: AttachmentFilename
		do:  [ :ex | ex resume: 'my-', ex filename asFilename tail ].</body>
</comment>

<class>
<name>MessagePrintHandler</name>
<environment>Net</environment>
<super>Net.MimeWriteHandler</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars></inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Net-HTTP-Support</category>
<attributes>
<package>MIME</package>
</attributes>
</class>

<comment>
<class-id>Net.MessagePrintHandler</class-id>
<body>Obsolete class, use MimeWriteHandler instead.</body>
</comment>

<class>
<name>BoundedReadStream</name>
<environment>Net</environment>
<super>Net.BoundedStream</super>
<private>true</private>
<indexed-type>none</indexed-type>
<inst-vars>buffer atEnd endOfLastPart </inst-vars>
<class-inst-vars></class-inst-vars>
<imports>
			private OS.IOConstants.*
			</imports>
<category>HTTP</category>
<attributes>
<package>MIME</package>
</attributes>
</class>

<comment>
<class-id>Net.BoundedReadStream</class-id>
<body>BoundedStream reads bytes up to a specified boundary. The stream helps to parse multipart messages.

Instance Variables:
	atEnd	&lt;Boolean&gt;	is true when the stream is positioned at the boundary and means end of the part. 
	buffer	&lt;ByteArray&gt;	data buffer
	endOfLastPart	&lt;Boolean&gt;	is true when stream is positioned at the end of the last part. 
</body>
</comment>

<class>
<name>InvalidHeaderField</name>
<environment>Net</environment>
<super>Net.NetNotification</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars></inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>MIME</category>
<attributes>
<package>MIME</package>
</attributes>
</class>

<comment>
<class-id>Net.InvalidHeaderField</class-id>
<body>Raised when the a parsed header field doesn't conform to it's required structure.

</body>
</comment>

<class>
<name>UnparsedBody</name>
<environment>Net</environment>
<super>Net.MimeMessageBody</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars></inst-vars>
<class-inst-vars></class-inst-vars>
<imports>
			private OS.IOConstants.*
			</imports>
<category>Net-MIME-Support</category>
<attributes>
<package>MIME</package>
</attributes>
</class>

<comment>
<class-id>Net.UnparsedBody</class-id>
<body>UnparsedBody is used as a temporary object while parsing a mime entity for the brief period between the time when a MimeEntity is created and until its header is parsed (so that the right type of body is created). It is primarily meant for debugging purposes, because due to the lazy initialization, inspection of an entity while debugging the parsing process can cause creation of wrong body object.

</body>
</comment>

<class>
<name>MimeDispatcher</name>
<environment>Net</environment>
<super>Core.Object</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>handler </inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Net-MIME-Support</category>
<attributes>
<package>MIME</package>
</attributes>
</class>

<comment>
<class-id>Net.MimeDispatcher</class-id>
<body>This class is an abstract superclass for all classes that traverse structure of RFC822, Mime, HTTP, etc. messages. Subclasses perform tasks such as printing, searching, etc. Structure traversing is implemented via visitor pattern. Visitor pattern (GoF, p 331-344) allows to separate concerns and tasks from the hierarchical message structure. We can do some very sophisticated searches and data mining inside complex messages, develop nice printing mechanisms, message transformation schemes (answers, forwarding, etc).

Instance Variables:
	handler &lt;DispatcherHandler&gt; writes/prints the message
</body>
</comment>

<class>
<name>MimeScanner</name>
<environment>Net</environment>
<super>Net.RFC822Scanner</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars></inst-vars>
<class-inst-vars>base64Decoder codeChars </class-inst-vars>
<imports>
			private OS.IOConstants.*
			</imports>
<category>Net-MIME-Support</category>
<attributes>
<package>MIME</package>
</attributes>
</class>

<comment>
<class-id>Net.MimeScanner</class-id>
<body>MimeScanner implements Mime encoding rules specified in RFC2047 (Message Header Extensions for Non-Ascii Text). The notation extends RFC822 describing encoding lexical elements text, phrase and word. MimeScanner extends RFC822 scanner. It has the same set of lexems. When either text, phrase or word is scanned, it is scanned using rfc822 syntax but Mime character  table. Once lexem is scanned, then Mime encoding rules for this lexem are applied so that non-ASCII characters can be decoded.

Class Variables:

	base64Decoder &lt;Array&gt; the base64 encoding map
	codeChars &lt;String&gt; base64 characters. The only characters used are the upper- and lower-case Roman alphabet characters (A–Z, a–z), the numerals (0–9), and the "+" and "/" symbols, with the "=" symbol as a special suffix code.
</body>
</comment>

<class>
<name>ContentTypeField</name>
<environment>Net</environment>
<super>Net.SingleValueWithParametersField</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars></inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Net-MIME-Support</category>
<attributes>
<package>MIME</package>
</attributes>
</class>

<comment>
<class-id>Net.ContentTypeField</class-id>
<body>This class represents content-type field in MIME and HTTP protocols. 
RFC2045 implements content-type field syntax defined in RFC2045.

The field performs parsing/compisition for content-type field and exposes
accessor methods to access individual components:
	contetType ('type/subtype', example 'text/html')
	type
	subType
	charset
	boundary

Other parameters can be accessed using #parameterAt:, #parameterAt:put:

Syntax (abbreviated) from RFC2045 (ftp.uu.net/inet/rfcs/rfc2045.Z): 

     content := "Content-Type" ":" type "/" subtype
                *(";" parameter)
                ; Matching of media type and subtype
                ; is ALWAYS case-insensitive.

     type := discrete-type / composite-type

     discrete-type := "text" / "image" / "audio" / "video" /
                      "application" / extension-token

     composite-type := "message" / "multipart" / extension-token

     parameter := attribute "=" value
</body>
</comment>

<class>
<name>ContentDispositionField</name>
<environment>Net</environment>
<super>Net.SingleValueWithParametersField</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars></inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Net-MIME-Support</category>
<attributes>
<package>MIME</package>
</attributes>
</class>

<comment>
<class-id>Net.ContentDispositionField</class-id>
<body>This class represents content-disposition field as defined by RFC2183.

Content-disposition field designates Mime entity as 'inline' or ''attachment'. 'Inline' parts can be displayed automatically whereas 'attachments' are located externally and require some action from the user. These can be displayed as iconized images. Content-disposition field also provides fiel name and essential file attributes to help in saving file and determining its Mime type of its contents.

Default setting is 'attachment' and can be reset as
	ContentDispositionField defaultContentDisposition: 'inline'.

This class provides accessor methods for standard parameters of this field. These methods are also provided in MimeEntity class (category content-disposition)

Excerpts from RFC2183:

   MIME specifies a standard format for encapsulating multiple pieces of
   data into a single Internet message. That document does not address
   the issue of presentation styles; it provides a framework for the
   interchange of message content, but leaves presentation issues solely
   in the hands of mail user agent (MUA) implementors.

   Two common ways of presenting multipart electronic messages are as a
   main document with a list of separate attachments, and as a single
   document with the various parts expanded (displayed) inline. The
   display of an attachment is generally construed to require positive
   action on the part of the recipient, while inline message components
   are displayed automatically when the message is viewed. A mechanism
   is needed to allow the sender to transmit this sort of presentational
   information to the recipient; the Content-Disposition header provides
   this mechanism, allowing each component of a message to be tagged
   with an indication of its desired presentation semantics.

   Tagging messages in this manner will often be sufficient for basic
   message formatting. However, in many cases a more powerful and
   flexible approach will be necessary. The definition of such
   approaches is beyond the scope of this memo; however, such approaches
   can benefit from additional Content-Disposition values and
   parameters, to be defined at a later date.

   In addition to allowing the sender to specify the presentational
   disposition of a message component, it is desirable to allow her to
   indicate a default archival disposition; a filename. The optional
   "filename" parameter provides for this.  Further, the creation-date,
   modification-date, and read-date parameters allow preservation of
   those file attributes when the file is transmitted over MIME email.

2.  The Content-Disposition Header Field

   Content-Disposition is an optional header field. In its absence, the
   MUA may use whatever presentation method it deems suitable.

   It is desirable to keep the set of possible disposition types small
   and well defined, to avoid needless complexity. Even so, evolving
   usage will likely require the definition of additional disposition
   types or parameters, so the set of disposition values is extensible;
   see below.

   In the extended BNF notation of [RFC 822], the Content-Disposition
   header field is defined as follows:

     disposition := "Content-Disposition" ":"
                    disposition-type
                    *(";" disposition-parm)

     disposition-type := "inline"
                       / "attachment"
                       / extension-token
                       ; values are not case-sensitive

     disposition-parm := filename-parm
                       / creation-date-parm
                       / modification-date-parm
                       / read-date-parm
                       / size-parm
                       / parameter

     filename-parm := "filename" "=" value

     creation-date-parm := "creation-date" "=" quoted-date-time

     modification-date-parm := "modification-date" "=" quoted-date-time

     read-date-parm := "read-date" "=" quoted-date-time

     size-parm := "size" "=" 1*DIGIT

     quoted-date-time := quoted-string
                      ; contents MUST be an RFC 822 `date-time'
                      ; numeric timezones (+HHMM or -HHMM) MUST be used

Shared Class Variables:
	DefaultContentDisposition	&lt;String&gt; default contents disposition. The default value is 'attachment'

</body>
</comment>

<class>
<name>MimeParserHandler</name>
<environment>Net</environment>
<super>Core.Object</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>fieldFactory messageHeaderClass scannerType stream lineEndConvention initialEntity saveAttachmentsAsFiles parser headerOnly binaryMode acceptNonAsciiCharacters </inst-vars>
<class-inst-vars>saveAttachmentsAsFiles defaultAttachmentDirectory </class-inst-vars>
<imports></imports>
<category>Net-MIME-Support</category>
<attributes>
<package>MIME</package>
</attributes>
</class>

<comment>
<class-id>Net.MimeParserHandler</class-id>
<body>This class is responsible for putting message together from pieces parsed by the mime parser. Specifically, this class knows what type of entity to create based on the context (parent entity, content type, etc.) and what to do with message body.

Instance Variables:
	fieldFactory	&lt;HeaderField&gt; field factory or default field factory if none is specified. Different protocols may have different field factories (i.e. Mime vs. HTTP)
	messageHeaderClass	&lt;MessageHeader&gt; message header class
	scannerType	&lt;MimeScanner&gt; scanner to parse header fields
	stream	&lt;Stream&gt; a stream to parse an entity
	lineEndConvention	&lt;Number&gt; original stream line end convention
	initialEntity	&lt;MimeEntity&gt; initial entity to build
	saveAttachmentsAsFiles	&lt;Boolean&gt; if true the builder will save attachments in to an external file. The external file name will be create from 'Content-Disposition' field falename attribute if any. If there is no filename in the attachment the random name will be generated. The AttachmentFilename notification will be raised allowing to provide a new file name. If the #saveAttachmentAsFile option is false the attachment will be save in to internal stream
	parser	&lt;MimeParser&gt; my parser. Parser then executes series of callbacks to the hahdler
	headerOnly	&lt;Boolean&gt; if true, dispatcher will stop after processing message header
	binaryMode	&lt;Boolean&gt; indicates the original mode of the input stream

Class Instance Variables:
	saveAttachmentsAsFiles	&lt;Boolean&gt; if true, save attachments in to the upload directory.
	defaultAttachmentDirectory	&lt;String&gt; default directory to upload attachments

</body>
</comment>

<class>
<name>MimeBuildHandler</name>
<environment>Net</environment>
<super>Net.MimeParserHandler</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>removeContentTransferEncoding </inst-vars>
<class-inst-vars>removeContentTransferEncoding </class-inst-vars>
<imports></imports>
<category>Net-MIME-Support</category>
<attributes>
<package>MIME</package>
</attributes>
</class>

<comment>
<class-id>Net.MimeBuildHandler</class-id>
<body>This class adds handling of transfer encodings on body parts.

Instance Variables:
	removeContentTransferEncoding	&lt;Boolean&gt; should the builder automatically remove transfer encoding from parsed messages and their parts

Class Instance Variables:
	removeContentTransferEncoding	&lt;Boolean&gt; the default value for removeContentTransferEncoding

</body>
</comment>

<class>
<name>NonClosingReadStream</name>
<environment>Net</environment>
<super>Core.Stream</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>stream </inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>MIME</category>
<attributes>
<package>MIME</package>
</attributes>
</class>

<comment>
<class-id>Net.NonClosingReadStream</class-id>
<body>I protect the underlying stream from being written into or flushed (raise an error). I suppress closing too (no error). I'm a helper stream for mail archive use protecting the message bodies stored in the archive.

Instance Variables:
	stream	&lt;Stream&gt; protected stream

</body>
</comment>

<class>
<name>NetworkAddressParser</name>
<environment>Net</environment>
<super>Net.RFC822Scanner</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>descriptor </inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Net-MIME-Support</category>
<attributes>
<package>MIME</package>
</attributes>
</class>

<comment>
<class-id>Net.NetworkAddressParser</class-id>
<body>This class parses string representing mailbox and group addresses and answers a instances of address descriptors -- either NetworkAddressDescriptor or MailGroupDescriptor -- depending on the nature of the address.

Instance Variables:
	descriptor	&lt;NetwordAddressDescriptor or MailGroupDescriptor&gt;  descriptor to put parsed info in
</body>
</comment>

<class>
<name>EntityException</name>
<environment>Net</environment>
<super>Net.NetError</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars></inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>MIME</category>
<attributes>
<package>MIME</package>
</attributes>
</class>

<comment>
<class-id>Net.EntityException</class-id>
<body>EntityException will be raise while constructing a new mime entity from a file stream in case when the mime entity expects the stream in binary mode. The assumption is based on the file name extension

</body>
</comment>

<class>
<name>EncodingFailure</name>
<environment>Net</environment>
<super>Net.NetError</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars></inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>MIME</category>
<attributes>
<package>MIME</package>
</attributes>
</class>

<comment>
<class-id>Net.EncodingFailure</class-id>
<body>EncodingFailure will be raised in case of failure to encode body source </body>
</comment>

<class>
<name>MimeEncodedWordCoDec</name>
<environment>Net</environment>
<super>Net.SimpleScanner</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars></inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Net-MIME-Support</category>
<attributes>
<package>MIME</package>
</attributes>
</class>

<comment>
<class-id>Net.MimeEncodedWordCoDec</class-id>
<body>This class encodes and decodes Mime encoded words. These may occur in header fields of Mime entities. Mime (rfc2047) defines encoding for the following lexical elements: comments, text and phrase. So when each of them is read from a stream, it has to be decoded according to its lexical type by invoking an appropriate method on an instance of MimeEncodedWordCoDec.

Shared Variables:
	AvailableEncodings &lt;OrderedCollection&gt; available encodings is used to define encoding for strings</body>
</comment>

<class>
<name>MessageHeader</name>
<environment>Net</environment>
<super>Net.MessageElement</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>registry </inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Net-MIME-Support</category>
<attributes>
<package>MIME</package>
</attributes>
</class>

<comment>
<class-id>Net.MessageHeader</class-id>
<body>I represent header fields in rfc822 messages. RFC822 messages may have repeated fields, order of field appearance is significant, and there are manadatory rules that certain fields should precede all other fields. This class encapsulates all this logic. Fields are represented by an instance of Dictionary. Value at a key is always an ordered collection of values. Each occurrence of the field adds another element to this collection. When a field is removed and its value is an empty collection, the whole entry is removed. Usually, when fields are parsed from a message, their sequence is already correct. If fields are added dynamically, some fields may appear in the wrong order, so part of preparing a message for transport is re-arranging fields to ensure correct sequence

Instance Variables:
	registry 	&lt;Dictionary&gt; header fields


</body>
</comment>

<class>
<name>QuotedPrintableDecoder</name>
<environment>Net</environment>
<super>Core.StreamEncoder</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>digit octet offset lineLength </inst-vars>
<class-inst-vars>defaultLineLength </class-inst-vars>
<imports>
			OS.IOConstants.*
			</imports>
<category>MIMETests</category>
<attributes>
<package>MIME</package>
</attributes>
</class>

<comment>
<class-id>Net.QuotedPrintableDecoder</class-id>
<body>QuotedPrintableDecoder is a simplified encoder for the quoted-printable Content-Transfer-Encoding for MIME message bodies (RFC#2045). It is to be used with the DecodedStream and therefore expects to convert bytes into characters of quoted-printable encoding. It is simplified because it deviates from the RFC by mapping the line-breaks into CRs (as opposed to CRLFs). This is because this encoder operates at the level where the underlying stream is a character stream and once strings are brought into the image all line end conventions should have been converted to CRs only by then. If this encoder encounters an LF character it will pass it through as byte 10 when reading and vice versa when writing. Note that DecodedStream does perfom line-end conversion on the CR and LF characters according to its line end setting. Another simplification is that this encoder is only expected to be used with textual media types with regards to line-break processing (point 4 in the RFC), i.e it will always preserve line ends literally. As such it is unsuitable for binary media types where bytes need to be preserved exactly. Use base-64 encoding for binary media types instead.

Example - Writing:

	| encoded stream |
	encoded := String new writeStream.
	stream := DecodedStream on: encoded encodedBy: QuotedPrintableDecoder asEncoder.
	stream := EncodedStream on: stream encodedBy: (StreamEncoder new: #iso8859_2).
	stream nextPutAll: 'Příliš žluťoučký kúň úpěl ďábelské ódy.\PŘÍLIŠ ŽLUŤOUČKÝ KŮŇ ÚPĚL ĎÁBELSKÉ ÓDY.\' withCRs.
	stream flush.
	encoded contents

Example - Reading:

	| encoded stream |
	encoded := 'P=F8=EDli=B9 =BElu=BBou=E8k=FD k=FA=F2 =FAp=ECl =EF=E1belsk=E9 =F3dy.\'.
	encoded := encoded, 'P=D8=CDLI=A9 =AELU=ABOU=C8K=DD K=D9=D2 =DAP=CCL =CF=C1BELSK=C9 =D3DY.\'.
	encoded := encoded withCRs readStream.
	stream := DecodedStream on: encoded encodedBy: QuotedPrintableDecoder asEncoder.
	stream := EncodedStream on: stream encodedBy: (StreamEncoder new: #iso8859_2).
	stream contents.


RFC 2045: Internet Message Bodies; chapter 6.7  Quoted-Printable Content-Transfer-Encoding:

The Quoted-Printable encoding is intended to represent data that largely consists of octets that correspond to printable characters in the US-ASCII character set.  It encodes the data in such a way that the resulting octets are unlikely to be modified by mail transport. If the data being encoded are mostly US-ASCII text, the encoded form of the data remains largely recognizable by humans.  A body which is entirely US-ASCII may also be encoded in Quoted-Printable to ensure the integrity of the data should the message pass through a character-translating, and/or line-wrapping gateway.

In this encoding, octets are to be represented as determined by the following rules:

	(1)	(General 8bit representation) Any octet, except a CR or LF that is part of a CRLF line break of the canonical (standard) form of the data being encoded, may be represented by an "=" followed by a two digit hexadecimal representation of the octet's value.  The digits of the hexadecimal alphabet, for this purpose, are "0123456789ABCDEF".  Uppercase letters must be used; lowercase letters are not allowed.  Thus, for example, the decimal value 12 (US-ASCII form feed) can be represented by "=0C", and the decimal value 61 (US- ASCII EQUAL SIGN) can be represented by "=3D".  This rule must be followed except when the following rules allow an alternative encoding.

	(2)	(Literal representation) Octets with decimal values of 33 through 60 inclusive, and 62 through 126, inclusive, MAY be represented as the US-ASCII characters which correspond to those octets (EXCLAMATION POINT through LESS THAN, and GREATER THAN through TILDE, respectively).

	(3)	(White Space) Octets with values of 9 and 32 MAY be represented as US-ASCII TAB (HT) and SPACE characters, respectively, but MUST NOT be so represented at the end of an encoded line.  Any TAB (HT) or SPACE characters on an encoded line MUST thus be followed on that line by a printable character.  In particular, an "=" at the end of an encoded line, indicating a soft line break (see rule #5) may follow one or more TAB (HT) or SPACE characters.  It follows that an octet with decimal value 9 or 32 appearing at the end of an encoded line must be represented according to Rule #1.  This rule is necessary because some MTAs (Message Transport Agents, programs which transport messages from one user to another, or perform a portion of such transfers) are known to pad lines of text with SPACEs, and others are known to remove "white space" characters from the end of a line.  Therefore, when decoding a Quoted-Printable body, any trailing white space on a line must be deleted, as it will necessarily have been added by intermediate transport agents.

	(4)	(Line Breaks) A line break in a text body, represented as a CRLF sequence in the text canonical form, must be represented by a (RFC 822) line break, which is also a CRLF sequence, in the Quoted-Printable encoding.  Since the canonical representation of media types other than text do not generally include the representation of line breaks as CRLF sequences, no hard line breaks (i.e. line breaks that are intended to be meaningful and to be displayed to the user) can occur in the quoted-printable encoding of such types.  Sequences like "=0D", "=0A", "=0A=0D" and "=0D=0A" will routinely appear in non-text data represented in quoted- printable, of course.

	(5)	(Soft Line Breaks) The Quoted-Printable encoding REQUIRES that encoded lines be no more than 76 characters long.  If longer lines are to be encoded with the Quoted-Printable encoding, "soft" line breaks must be used.  An equal sign as the last character on a encoded line indicates such a non-significant ("soft") line break in the encoded 

This provides a mechanism with which long lines are encoded in such a way as to be restored by the user agent.  The 76 character limit does not count the trailing CRLF, but counts all other characters, including any equal signs.

Since the hyphen character ("-") may be represented as itself in the Quoted-Printable encoding, care must be taken, when encapsulating a quoted-printable encoded body inside one or more multipart entities, to ensure that the boundary delimiter does not appear anywhere in the encoded body.  (A good strategy is to choose a boundary that includes a character sequence such as "=_" which can never appear in a quoted-printable body.  See the definition of multipart messages in RFC 2046.)

NOTE: The quoted-printable encoding represents something of a compromise between readability and reliability in transport.  Bodies encoded with the quoted-printable encoding will work reliably over most mail gateways, but may not work perfectly over a few gateways, notably those involving translation into EBCDIC.  A higher level of confidence is offered by the base64 Content-Transfer-Encoding.  A way to get reasonably reliable transport through EBCDIC gateways is to also quote the US-ASCII characters !"#$@[\]^`{|}~ according to rule #1.

Because quoted-printable data is generally assumed to be line-oriented, it is to be expected that the representation of the breaks between the lines of quoted-printable data may be altered in transport, in the same manner that plain text mail has always been altered in Internet mail when passing between systems with differing newline conventions.  If such alterations are likely to constitute a corruption of the data, it is probably more sensible to use the base64 encoding rather than the quoted-printable encoding.

NOTE: Several kinds of substrings cannot be generated according to the encoding rules for the quoted-printable content-transfer- encoding, and hence are formally illegal if they appear in the output of a quoted-printable encoder. This note enumerates these cases and suggests ways to handle such illegal substrings if any are encountered in quoted-printable data that is to be decoded.

    (1)   An "=" followed by two hexadecimal digits, one or both of which are lowercase letters in "abcdef", is formally illegal. A robust implementation might choose to recognize them as the corresponding uppercase letters.

    (2)   An "=" followed by a character that is neither a hexadecimal digit (including "abcdef") nor the CR character of a CRLF pair is illegal.  This case can be the result of US-ASCII text having been included in a quoted-printable part of a message without itself having been subjected to quoted-printable encoding.  A reasonable approach by a robust implementation might be to include the "=" character and the following character in the decoded data without any transformation and, if possible, indicate to the user that proper decoding was not possible at this point in the data.

    (3)   An "=" cannot be the ultimate or penultimate character in an encoded object.  This could be handled as in case (2) above.

    (4)   Control characters other than TAB, or CR and LF as parts of CRLF pairs, must not appear. The same is true for octets with decimal values greater than 126.  If found in incoming quoted-printable data by a decoder, a robust implementation might exclude them from the decoded data and warn the user that illegal characters were discovered.

    (5)   Encoded lines must not be longer than 76 characters, not counting the trailing CRLF. If longer lines are found in incoming, encoded data, a robust implementation might nevertheless decode the lines, and might report the erroneous encoding to the user.

WARNING TO IMPLEMENTORS:  If binary data is encoded in quoted-printable, care must be taken to encode CR and LF characters as "=0D" and "=0A", respectively.  In particular, a CRLF sequence in binary data should be encoded as "=0D=0A".  Otherwise, if CRLF were represented as a hard line break, it might be incorrectly decoded on platforms with different line break conventions.


Instance Variables:
	digit	&lt;Integer&gt; indicates the index of the current hexadecimal digit of the =XX encoding; 0 means we're not encoding current octet; {0,1,2}
	octet	&lt;Integer&gt; currently processed octet
	offset	&lt;Integer&gt; zero based offset of the next character on current line
	lineLength	&lt;Integer&gt; specifies the line length where to emit CR and wrap to the next line when encoding

Class Instance Variables:
	defaultLineLength	&lt;Integer&gt; specifies the default line length where to emit CR and wrap to the next line when encoding

</body>
</comment>

<class>
<name>ParsingSimpleBodyError</name>
<environment>Net</environment>
<super>Net.NetError</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars></inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>MIME</category>
<attributes>
<package>MIME</package>
</attributes>
</class>

<comment>
<class-id>Net.ParsingSimpleBodyError</class-id>
<body>ParsingSimpleBodyError is raised when any error is detected while parsing the message body. The exception parameter holds the original error.
</body>
</comment>

<class>
<name>SimpleBody</name>
<environment>Net</environment>
<super>Net.MimeMessageBody</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>byteSource </inst-vars>
<class-inst-vars></class-inst-vars>
<imports>
			private OS.IOConstants.*
			</imports>
<category>Net-MIME-Support</category>
<attributes>
<package>MIME</package>
</attributes>
</class>

<comment>
<class-id>Net.SimpleBody</class-id>
<body>This class represents simple message bodies. Simple message body's value is its contents, which is a string. Simple message bodies make no attempt to decode or transform its contents, it is responsibility of higher-level code. Parsing and value initialization is done lazily. Source stream is remembered so that parsing can be done at any time.

Instance Variables:

	byteSource &lt;Stream of ByteArray&gt; it will always contain row bytes parsed by a parser handler and the #source stream will always contain decompressed or decoded ByteString. 
The MailBuildReader set the #byteSource to a StreamSegment pointing to an original mail file and the #source is set as stacks of streams that are built on top of the #byteSource to completely decode the body.</body>
</comment>

<class>
<name>MimeEntityBot</name>
<environment>Net</environment>
<super>Net.MimeDispatcherHandler</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>shouldCallback doCallback keepSearching entityStack </inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Net-MIME-Support</category>
<attributes>
<package>MIME</package>
</attributes>
</class>

<comment>
<class-id>Net.MimeEntityBot</class-id>
<body>This class is a 'robot' that can be used to search Mime entities for information and to perform some transformations of Mime entities.

Methods in 'enumeration' provide search facilities to find and collect parts matching certain criteria. Searches can be restricted to some level of nesting, which is convenient when searching for message text parts and attachments.
Methods in 'tasks' provides facilities to check message structure, add/remove transfer encoding and also to correctly mutate message. Examples include adding part to a simple message which mutates it froma simple message to multi-part message. Another example is dropping all representations of message text except plain text one, which may require to mutate message from multipart/alternative to simple, or replacing multipart/alternative part with a simple part. This class encapsulates all the logistics and complexity involved in these transformations providing easy to use high-level methods.

Instance Variables:
	shouldCallback	&lt;BlockContext&gt; invoked with a part to decide if it should be processed or not
	doCallback	&lt;BlockContext&gt; invoked with a part to process it
	keepSearching	&lt;Boolean&gt; internal - terminates message traversal when set
	entityStack	&lt;OrderedCollection of: MimeEntity&gt; internal - a stack of message parts representing the path to the currently processed part
</body>
</comment>

<class>
<name>NetworkAddressDescriptor</name>
<environment>Net</environment>
<super>Net.NetworkEntityDescriptor</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>domain localPart route </inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Net-MIME-Support</category>
<attributes>
<package>MIME</package>
</attributes>
</class>

<comment>
<class-id>Net.NetworkAddressDescriptor</class-id>
<body>This class represents RFC822 network addresses. An address may have address part, alias and comment. Each one is optional. 

Address syntax from rfc822 is as follows:

     address     =  mailbox                      ; one addressee
                 /  group                        ; named list

     group       =  phrase ":" [#mailbox] ";"

     mailbox     =  addr-spec                    ; simple address
                 /  phrase route-addr            ; name &amp; addr-spec

     route-addr  =  "&lt;" [route] addr-spec "&gt;"

     route       =  1#("@" domain) ":"           ; path-relative

     addr-spec   =  local-part "@" domain        ; global address

     local-part  =  word *("." word)             ; uninterpreted
                                                 ; case-preserved

     domain      =  sub-domain *("." sub-domain)

     sub-domain  =  domain-ref / domain-literal

     domain-ref  =  atom                         ; symbolic reference


Instance Variables:
	domain	&lt;SequencableCollection of Strings&gt;  Domain parts domainString answers domain string
	localPart	&lt;SequencableCollection of Strings&gt;  Parts of local part. localPartString answers string
	route	&lt;Collection | EncodedStream | PositionableStream&gt;  Route part of the address. routeString answers route string
</body>
</comment>

<class>
<name>MultipartBody</name>
<environment>Net</environment>
<super>Net.MimeMessageBody</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars></inst-vars>
<class-inst-vars></class-inst-vars>
<imports>
			private OS.IOConstants.*
			</imports>
<category>Net-MIME-Support</category>
<attributes>
<package>MIME</package>
</attributes>
</class>

<comment>
<class-id>Net.MultipartBody</class-id>
<body>This class represents multi-part MIME message bodies. Multi-part bodies contain one or more body parts, each preceded by a boundary line, and the last one followed by a closing boundary line. After the opening boundary line, each body part consists of a header area, a blank line, and a body area.  Thus a body part is similar to an RFC 822 message in syntax, but different in meaning.

This class is responsible for parsing body structure from source stream and composing body structure to output stream. It parses out individual entities and lets entities parse themselves. Value of multi-part body is an array of Mime entities. Value is initialized lazily to avoid unnecessary parsing.

Multi-part bodies are defined in section 5.1 of RFC2046 (http://www.ietf.org/rfc/rfc2046.txt). Here is an example of a multi-part message:

     From: Nathaniel Borenstein &lt;nsb@bellcore.com&gt;
     To: Ned Freed &lt;ned@innosoft.com&gt;
     Date: Sun, 21 Mar 1993 23:56:48 -0800 (PST)
     Subject: Sample message
     MIME-Version: 1.0
     Content-type: multipart/mixed; boundary="simple boundary"

     This is the preamble.  It is to be ignored, though it
     is a handy place for composition agents to include an
     explanatory note to non-MIME conformant readers.

     --simple boundary

     This is implicitly typed plain US-ASCII text.
     It does NOT end with a linebreak.
     --simple boundary
     Content-type: text/plain; charset=us-ascii

     This is explicitly typed plain US-ASCII text.
     It DOES end with a linebreak.

     --simple boundary--

     This is the epilogue.  It is also to be ignored.

</body>
</comment>

<class>
<name>MimePrintHandler</name>
<environment>Net</environment>
<super>Net.MimeOutputHandler</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>indentation indentLines </inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Net-MIME-Support</category>
<attributes>
<package>MIME</package>
</attributes>
</class>

<comment>
<class-id>Net.MimePrintHandler</class-id>
<body>This class implements printing of messages on a stream in a shortened (digest) mode to enhance readability of the output. To accomodate any kind of character stream for output, the stream is expected to be able to accept characters right away, i.e. be a pure character stream or be set to the text mode. First, nested entities are indented according to their nesting level. Secondly, binary data is not shown and in general only a digest of each entity's contents is displayed. The stream to print on expected to be a character stream or an error will be raised.

Instance Variables:
	indentation	&lt;Number&gt; the level of indention
	indentLines	&lt;Boolean&gt; if true, add tab while printing lines

</body>
</comment>

<class>
<name>MessageBuildHandler</name>
<environment>Net</environment>
<super>Net.MimeParserHandler</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars></inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Net-HTTP-Support</category>
<attributes>
<package>MIME</package>
</attributes>
</class>

<comment>
<class-id>Net.MessageBuildHandler</class-id>
<body>Obsolete class. </body>
</comment>

<class>
<name>BodyStream</name>
<environment>Net</environment>
<super>Net.MessageStream</super>
<private>true</private>
<indexed-type>none</indexed-type>
<inst-vars></inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>HTTP</category>
<attributes>
<package>MIME</package>
</attributes>
</class>

<comment>
<class-id>Net.BodyStream</class-id>
<body>BodyStream allows to read only up to a specified number of bytes (readLimit) from the underlying stream.

</body>
</comment>

<class>
<name>DateField</name>
<environment>Net</environment>
<super>Net.ScalarField</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars></inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Net-MIME-Support</category>
<attributes>
<package>MIME</package>
</attributes>
</class>

<comment>
<class-id>Net.DateField</class-id>
<body>This class represents date fields as defines in RFC822 and HTTP. Value of the field is an instance of date, not a string. See rfc822 for the exact syntax of dates

	Examples:
		HeaderField fromLine: 'Date : Tue, 18 Apr 89 23:29:47 +0900	' 
		HeaderField fromLine: 'Date : 26 Aug 76 14:30 EDT' 
		HeaderField fromLine: 'Expired : 0' -  the data filed value will be the number zero. HTTP/1.1 clients MUST treat other invalid date formats, especially including the value 0, as in the past (i.e., already expired). 

</body>
</comment>

<class>
<name>MimeMultipartEntity</name>
<environment>Net</environment>
<super>Net.MimeEntity</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars></inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Net-MIME-Support</category>
<attributes>
<package>MIME</package>
</attributes>
</class>

<comment>
<class-id>Net.MimeMultipartEntity</class-id>
<body>MimeMultipartEntity is obsolete and will be deprecated in the next release

</body>
</comment>

<shared-variable>
<name>DefaultCharsetEncoder</name>
<environment>Net.SimpleBody</environment>
<private>false</private>
<constant>false</constant>
<category>defaults</category>
<initializer></initializer>
<attributes>
<package>MIME</package>
</attributes>
</shared-variable>

<shared-variable>
<name>SimpleTimeZones</name>
<environment>Net.RFC822Scanner</environment>
<private>false</private>
<constant>false</constant>
<category>masks</category>
<attributes>
<package>MIME</package>
</attributes>
</shared-variable>

<shared-variable>
<name>TextMask</name>
<environment>Net.RFC822Scanner</environment>
<private>false</private>
<constant>false</constant>
<category>masks</category>
<initializer>8192</initializer>
<attributes>
<package>MIME</package>
</attributes>
</shared-variable>

<shared-variable>
<name>QuotedPairChar</name>
<environment>Net.RFC822Scanner</environment>
<private>false</private>
<constant>false</constant>
<category>masks</category>
<initializer>$\</initializer>
<attributes>
<package>MIME</package>
</attributes>
</shared-variable>

<shared-variable>
<name>TokenMask</name>
<environment>Net.RFC822Scanner</environment>
<private>false</private>
<constant>false</constant>
<category>masks</category>
<initializer>16384</initializer>
<attributes>
<package>MIME</package>
</attributes>
</shared-variable>

<shared-variable>
<name>CommentMask</name>
<environment>Net.RFC822Scanner</environment>
<private>false</private>
<constant>false</constant>
<category>masks</category>
<initializer>512</initializer>
<attributes>
<package>MIME</package>
</attributes>
</shared-variable>

<shared-variable>
<name>HeaderNameMask</name>
<environment>Net.RFC822Scanner</environment>
<private>false</private>
<constant>false</constant>
<category>masks</category>
<initializer>2048</initializer>
<attributes>
<package>MIME</package>
</attributes>
</shared-variable>

<shared-variable>
<name>HeaderNameDelimiterChar</name>
<environment>Net.RFC822Scanner</environment>
<private>false</private>
<constant>false</constant>
<category>masks</category>
<initializer>$:</initializer>
<attributes>
<package>MIME</package>
</attributes>
</shared-variable>

<shared-variable>
<name>DomainTextMask</name>
<environment>Net.RFC822Scanner</environment>
<private>false</private>
<constant>false</constant>
<category>masks</category>
<initializer>1024</initializer>
<attributes>
<package>MIME</package>
</attributes>
</shared-variable>

<shared-variable>
<name>QuotedTextMask</name>
<environment>Net.RFC822Scanner</environment>
<private>false</private>
<constant>false</constant>
<category>masks</category>
<initializer>4096</initializer>
<attributes>
<package>MIME</package>
</attributes>
</shared-variable>

<shared-variable>
<name>QuotedPairMask</name>
<environment>Net.RFC822Scanner</environment>
<private>false</private>
<constant>false</constant>
<category>masks</category>
<initializer>"(QuotedTextMask bitOr: CommentMask)  bitOr: DomainTextMask"</initializer>
<attributes>
<package>MIME</package>
</attributes>
</shared-variable>

<shared-variable>
<name>AtomMask</name>
<environment>Net.RFC822Scanner</environment>
<private>false</private>
<constant>false</constant>
<category>masks</category>
<initializer>256</initializer>
<attributes>
<package>MIME</package>
</attributes>
</shared-variable>

<shared-variable>
<name>MimeQuotedTextMask</name>
<environment>Net.MimeScanner</environment>
<private>false</private>
<constant>false</constant>
<category>masks</category>
<initializer>32768</initializer>
<attributes>
<package>MIME</package>
</attributes>
</shared-variable>

<shared-variable>
<name>AvailableEncodings</name>
<environment>Net.MimeEncodedWordCoDec</environment>
<private>false</private>
<constant>false</constant>
<category>encoding</category>
<initializer>nil</initializer>
<attributes>
<package>MIME</package>
</attributes>
</shared-variable>

<shared-variable>
<name>FieldsRegistry</name>
<environment>Net.HeaderField</environment>
<private>false</private>
<constant>false</constant>
<category>registry</category>
<initializer>Dictionary new: 50</initializer>
<attributes>
<package>MIME</package>
</attributes>
</shared-variable>

<shared-variable>
<name>DefaultContentDisposition</name>
<environment>Net.ContentDispositionField</environment>
<private>false</private>
<constant>false</constant>
<category>defaults</category>
<initializer></initializer>
<attributes>
<package>MIME</package>
</attributes>
</shared-variable>

<methods>
<class-id>Net.HeaderFieldValue</class-id> <category>accessing</category>

<body package="MIME" selector="groupSeparator">groupSeparator
	^self separators last</body>

<body package="MIME" selector="separators">separators
	^separators</body>

<body package="MIME" selector="separators:">separators: anArray
	separators := anArray</body>

<body package="MIME" selector="value">value
	^value</body>

<body package="MIME" selector="value:">value: aString
	value := aString</body>

<body package="MIME" selector="valueSeparator">valueSeparator
	^self separators first</body>
</methods>

<methods>
<class-id>Net.HeaderFieldValue</class-id> <category>parsing</category>

<body package="MIME" selector="doParse:">doParse: scanner
	" Canonical value removes all white space and comments from the source "

	| str |
	str := scanner scanUntil: 
			[ scanner hereChar == self valueSeparator 
				or: [ scanner hereChar == self groupSeparator] ].
	^self  tokenizedValueFrom: (self scannerOn: str readStream).</body>

<body package="MIME" selector="parse:">parse: scanner
	self parseValue: scanner.</body>

<body package="MIME" selector="parseValue:">parseValue: scanner
	scanner skipWhiteSpace. 
	value := self doParse: scanner.
	^value</body>

<body package="MIME" selector="readFrom:">readFrom: aStream
	^self parse: (self scannerOn: aStream)</body>

<body package="MIME" selector="scannerOn:">scannerOn: aStream
	^self class scannerOn: aStream</body>
</methods>

<methods>
<class-id>Net.HeaderFieldValue</class-id> <category>composing</category>

<body package="MIME" selector="writeOn:encoding:">writeOn: aStream encoding: aString

	self writeValueOn: aStream</body>

<body package="MIME" selector="writeValueOn:">writeValueOn: aStream

	self printValueOn: aStream</body>
</methods>

<methods>
<class-id>Net.HeaderFieldValue</class-id> <category>printing</category>

<body package="MIME" selector="printOn:">printOn: aStream

	self printValueOn: aStream.</body>

<body package="MIME" selector="printValueOn:">printValueOn: aStream

	value ~~ nil ifTrue: [aStream nextPutAll: self value]</body>
</methods>

<methods>
<class-id>Net.HeaderFieldValue</class-id> <category>private-utility</category>

<body package="MIME" selector="tokenize:">tokenize: rfc822Stream 
	"Scan field value token by token. Answer an array of tokens"

	| result token |
	result := (Array new: 2) writeStream.
	[rfc822Stream atEnd or: [rfc822Stream peek == Character cr or: [(token := rfc822Stream nextToken) isNil]]]
		whileFalse: [result nextPut: token].
	^result contents</body>

<body package="MIME" selector="tokenizedValueFrom:">tokenizedValueFrom: rfc822Stream 
	"Scan field value token by token. Answer a string that is a concatenation of all elements in the array. One can view this as a canonicalized field value because this operation eliminates all white space and comments "

	| result tokens |
	result := (String new: 20) writeStream.
	tokens := self tokenize: rfc822Stream.
	tokens
		do: [:token | token isString
				ifTrue: [result nextPutAll: token]
				ifFalse: [result nextPut: token]
].
	^result contents</body>
</methods>

<methods>
<class-id>Net.HeaderFieldValue</class-id> <category>initialize-release</category>

<body package="MIME" selector="initialize">initialize</body>
</methods>

<methods>
<class-id>Net.HeaderFieldValue class</class-id> <category>instance creation</category>

<body package="MIME" selector="new">new
	^super new initialize</body>

<body package="MIME" selector="newWithSeparators:">newWithSeparators: anArray
	^self new
		separators: anArray;
		yourself</body>
</methods>

<methods>
<class-id>Net.HeaderFieldValue class</class-id> <category>parsing</category>

<body package="MIME" selector="scannerOn:">scannerOn: aStream 
	^(aStream isKindOf: self scannerType)
		ifTrue: [aStream]
		ifFalse: [self scannerType on: aStream asStream]</body>

<body package="MIME" selector="scannerType">scannerType
	^MimeScanner</body>
</methods>

<methods>
<class-id>Net.ValueWithParams</class-id> <category>accessing</category>

<body package="MIME" selector="paramAt:">paramAt: aString
	^self paramAt: aString asLowercase ifAbsent: [ nil ]</body>

<body package="MIME" selector="paramAt:ifAbsent:">paramAt: aString ifAbsent: aBlock
	^self params at: aString ifAbsent: [ aBlock value ]</body>

<body package="MIME" selector="paramAt:put:">paramAt: aName put: aValue
	^self params at: aName put: aValue</body>

<body package="MIME" selector="parametersSeparator">parametersSeparator
	^self separators at: 2</body>

<body package="MIME" selector="params">params
	^params ifNil: [ self resetParams ].</body>

<body package="MIME" selector="removeParamAt:">removeParamAt: aName
	^self params removeKey: aName ifAbsent: [nil]</body>
</methods>

<methods>
<class-id>Net.ValueWithParams</class-id> <category>composing</category>

<body package="MIME" selector="writeOn:encoding:">writeOn: aStream encoding: aString
	super writeValueOn: aStream.
	self	writeParamsOn: aStream encoding: aString.</body>

<body package="MIME" selector="writeParameter:on:encoding:">writeParameter: assoc on: aStream encoding: aString

	| rfcString |
	self class scannerType
			printWord: assoc key on: aStream;
			print: $= on: aStream.
	assoc value isEmpty ifTrue: [^aStream nextPut: $"; nextPut: $"].
	assoc value ~~ nil
		ifTrue: 
			[(MimeEncodedWordCoDec shouldBeEncoded: assoc value)
				ifTrue: 
					[rfcString := MimeEncodedWordCoDec 
									encodeText: assoc value 
									asBase64Using: aString. 
					aStream nextPut: $"; nextPutAll:  rfcString; nextPut: $" ]
				ifFalse: [ self class scannerType printWord: assoc value on: aStream ]]</body>

<body package="MIME" selector="writeParamsOn:encoding:">writeParamsOn: aStream encoding: aString

	(params ~~ nil and: [ params notEmpty])
		ifTrue:
			[aStream nextPut: self valueSeparator.
			params associations
				do: [ :assoc | self writeParameter: assoc on: aStream encoding: aString ]
				separatedBy: [ aStream nextPut: self parametersSeparator ]]</body>
</methods>

<methods>
<class-id>Net.ValueWithParams</class-id> <category>printing</category>

<body package="MIME" selector="printOn:">printOn: aStream

	super printOn: aStream.
	self printParamsOn: aStream</body>

<body package="MIME" selector="printParameter:on:">printParameter: assoc on: aStream 
	self class scannerType printWord: assoc key on: aStream.
	self printParameter: assoc key value: assoc value on: aStream.</body>

<body package="MIME" selector="printParameter:value:on:">printParameter: name value: anObject on: aStream
	anObject isNil ifTrue: [^nil].
	self class scannerType print: $= on: aStream.
	[self class scannerType printWord: anObject on: aStream
	] 	on: NonASCIICharacter 
		do: [ :ex | aStream nextPut: $"; nextPutAll: anObject; nextPut: $"]</body>

<body package="MIME" selector="printParametersSeparatorOn:">printParametersSeparatorOn: aStream
	aStream nextPut: self parametersSeparator</body>

<body package="MIME" selector="printParamsOn:">printParamsOn: aStream
	self params notEmpty
		ifTrue:
			[aStream nextPut: self valueSeparator.
			params associations
				do: [ :assoc | self printParameter: assoc on: aStream ]
				separatedBy: [self printParametersSeparatorOn: aStream  ]]</body>
</methods>

<methods>
<class-id>Net.ValueWithParams</class-id> <category>parsing</category>

<body package="MIME" selector="addParameterNamed:value:">addParameterNamed: valString value: parString
	self params 
		at: valString asLowercase
		put: parString</body>

<body package="MIME" selector="finishParse">finishParse</body>

<body package="MIME" selector="parse:">parse: scanner
	self parseValue: scanner.
	scanner atEnd ifTrue: [ ^self]. 
	scanner hereChar ~~ self valueSeparator
		ifTrue: [ scanner stepBack. ^self ].
	self parseParameters: scanner.
	self finishParse.</body>

<body package="MIME" selector="parseParameters:">parseParameters: scanner
	| paramName |
	scanner atEnd ifFalse: [
		scanner
			tokenizeList: [
				(paramName := self parseParamNameFrom: scanner ) notNil ifTrue: [
					scanner skipWhiteSpace.
					(self validateNextToken: scanner for: paramName) ifFalse: [
							InvalidHeaderField signalWith: scanner contents.
							^self ].  
					self addParameterNamed: paramName  
						value: (self parseParamValueFrom: scanner) ] ]
			separatedBy: [scanner token == self parametersSeparator ] ]</body>

<body package="MIME" selector="parseParamNameFrom:">parseParamNameFrom: scanner 
	^scanner nextToken</body>

<body package="MIME" selector="parseParamValueFrom:">parseParamValueFrom: scanner 

	| paramValue encoding |
	encoding := self class defaultParameterValueEncoding.
	paramValue :=
		(encoding isNil or: [ encoding isEmpty ])
			ifTrue: [ scanner nextToken ]
			ifFalse: [ self parseParamValueFrom: scanner allowing: encoding ].
	paramValue ifNil: [^nil].
	^MimeEncodedWordCoDec decodeText: paramValue.</body>

<body package="MIME" selector="parseParamValueFrom:allowing:">parseParamValueFrom: scanner allowing: encoding

	| acceptNonASCIICharacter paramValue hasNonASCIICharacter |
	acceptNonASCIICharacter := scanner acceptNonAsciiCharacters.
	hasNonASCIICharacter := false.
	scanner acceptNonAsciiCharacters: false.
	[	[	(paramValue := scanner nextToken)  ifNil: [ ^nil].
		]	on: NonASCIICharacter 
			do: [ :ex | 
				hasNonASCIICharacter := true.
				acceptNonASCIICharacter 
					ifTrue: [ scanner acceptNonAsciiCharacters: acceptNonASCIICharacter. 
							ex resume: SimpleScanner  anyTextMask]
					ifFalse: [ ex pass ] ].
	] ensure: [ scanner acceptNonAsciiCharacters: acceptNonASCIICharacter].
	hasNonASCIICharacter ifTrue: [
		paramValue := (paramValue asByteArrayEncoding: #'iso8859-1') asStringEncoding: encoding].
	^paramValue.</body>

<body package="MIME" selector="validateNextToken:for:">validateNextToken: scanner for: parName
	^scanner next == $=</body>
</methods>

<methods>
<class-id>Net.ValueWithParams</class-id> <category>initialize-release</category>

<body package="MIME" selector="resetParams">resetParams
	^params := Struct new</body>
</methods>

<methods>
<class-id>Net.ValueWithParams class</class-id> <category>accessing</category>

<body package="MIME" selector="defaultParameterValueEncoding">defaultParameterValueEncoding

	^defaultParameterValueEncoding ifNil: [self defaultParameterValueEncodingValue ]</body>

<body package="MIME" selector="defaultParameterValueEncoding:">defaultParameterValueEncoding: aString
"If set, the specified encoding will be applied if we encounter non-ascii characters in parameter values (technically this is against the HTTP specification, but seems to be a widespread practice, e.g. many web browsers do that with filenames of uploaded files).
	self defaultParameterValueEncoding: nil
	self defaultParameterValueEncoding: #utf8
"
	defaultParameterValueEncoding := aString ifNotNil: [ aString asSymbol ]</body>

<body package="MIME" selector="defaultParameterValueEncodingValue">defaultParameterValueEncodingValue

	^nil</body>
</methods>

<methods>
<class-id>Net.MessageScanner</class-id> <category>printing</category>

<body package="MIME" selector="printAtom:on:">printAtom: atom on: stream
	self class printAtom: atom on: stream</body>

<body package="MIME" selector="printQuotedText:on:">printQuotedText: qtext on: stream
	self class printQuotedText: qtext on: stream</body>

<body package="MIME" selector="printText:on:">printText: qtext on: stream
	self class printText: qtext on: stream</body>
</methods>

<methods>
<class-id>Net.MessageScanner class</class-id> <category>printing</category>

<body package="MIME" selector="print:on:">print: char on: aStream
	aStream nextPut: char</body>

<body package="MIME" selector="printQuotedText:on:">printQuotedText: str on: stream 
	"Print word as quoted text"

	stream nextPut: $"; nextPutAll: str; nextPut: $"</body>

<body package="MIME" selector="printText:on:">printText: text on: stream
	stream nextPutAll: text</body>

<body package="MIME" selector="printTokenList:on:">printTokenList: list on: stream
	self printTokenList: list on: stream separatedBy: [ stream space ]</body>

<body package="MIME" selector="printTokenList:on:separatedBy:">printTokenList: list on: stream separatedBy: aBlock 
	list do: [:assoc | self printToken: assoc on: stream]
		separatedBy: aBlock</body>
</methods>

<methods>
<class-id>Net.MimeDispatcherHandler</class-id> <category>accessing</category>

<body package="MIME" selector="dispatcher">dispatcher
	^dispatcher isNil 
		ifTrue: [dispatcher := self defaultDispatcher]
		ifFalse: [dispatcher]</body>

<body package="MIME" selector="dispatcher:">dispatcher: aMessageDispatcher 
	aMessageDispatcher handler: self.
	dispatcher := aMessageDispatcher</body>

<body package="MIME" selector="headerOnly">headerOnly
	^headerOnly</body>

<body package="MIME" selector="headerOnly:">headerOnly: aBoolean 
	headerOnly := aBoolean</body>
</methods>

<methods>
<class-id>Net.MimeDispatcherHandler</class-id> <category>action callbacks</category>

<body package="MIME" selector="endEntity:">endEntity: aMimeEntityOrNil

	^aMimeEntityOrNil</body>

<body package="MIME" selector="endHeader:">endHeader: aMessageHeader

	^aMessageHeader parent</body>

<body package="MIME" selector="endMessage:">endMessage: aMessage

	^aMessage</body>

<body package="MIME" selector="endMultipartBody:">endMultipartBody: aMultipartBody

	^aMultipartBody</body>

<body package="MIME" selector="endPart:">endPart: aMimeEntity

	^aMimeEntity</body>

<body package="MIME" selector="endSimpleBody:">endSimpleBody: aMessageBody 

	^aMessageBody</body>

<body package="MIME" selector="processMessageBody:do:">processMessageBody:  aMessage do: aBlock

	aBlock value</body>

<body package="MIME" selector="shouldStartBody:">shouldStartBody: aMimeEntity 
	^self headerOnly not</body>

<body package="MIME" selector="shouldStartEntity:">shouldStartEntity: aMimeEntityOrNil

	^true</body>

<body package="MIME" selector="shouldStartMessage:">shouldStartMessage: aMimeEntityOrNil

	^true</body>

<body package="MIME" selector="startEntity:">startEntity: aMimeEntityOrNil

	^aMimeEntityOrNil</body>

<body package="MIME" selector="startHeader:">startHeader: aMessageHeader

	^aMessageHeader</body>

<body package="MIME" selector="startMessage:">startMessage: aMessage

	^aMessage</body>

<body package="MIME" selector="startMultipartBody:">startMultipartBody: aMultipartBody

	^aMultipartBody</body>

<body package="MIME" selector="startPart:">startPart: aMimeEntity

	^aMimeEntity</body>

<body package="MIME" selector="startSimpleBody:">startSimpleBody: aSimpleBody

	^aSimpleBody</body>
</methods>

<methods>
<class-id>Net.MimeDispatcherHandler</class-id> <category>defaults</category>

<body package="MIME" selector="defaultDispatcher">defaultDispatcher
	^self dispatcherClass handler: self</body>

<body package="MIME" selector="dispatcherClass">dispatcherClass
	^MimeDispatcher</body>
</methods>

<methods>
<class-id>Net.MimeDispatcherHandler</class-id> <category>initialization</category>

<body package="MIME" selector="initialize">initialize
	headerOnly := false</body>
</methods>

<methods>
<class-id>Net.MimeDispatcherHandler</class-id> <category>dispatcher interface</category>

<body package="MIME" selector="visit:">visit: anObject
	self dispatcher visit: anObject</body>
</methods>

<methods>
<class-id>Net.MimeDispatcherHandler</class-id> <category>private</category>

<body package="MIME" selector="defaultBoundaryFor:">defaultBoundaryFor: aMimeEntity
" Compute default boundary for specified Mime entity. Boundary is a string that
could not occur inside any of the parts. For now just use constant plus a random number "

	^'=_vw', Random new next printString, '_='</body>
</methods>

<methods>
<class-id>Net.MimeDispatcherHandler class</class-id> <category>instance creation</category>

<body package="MIME" selector="new">new
	^super new initialize</body>
</methods>

<methods>
<class-id>Net.MimeOutputHandler</class-id> <category>printing utilities</category>

<body package="MIME" selector="printHeader:">printHeader: aMessageHeader

	(self  selectFieldsFrom: aMessageHeader )
		do: [ :field | self printHeaderField: field ]</body>

<body package="MIME" selector="printHeaderField:">printHeaderField: aHeaderField
" Get string representation of the field, make sure all lines are withing max line length and write all this on output stream. Lines may be folded to fit into max length (cr followed by tab is inserted). Field itself may fold lines, so field's representation is first broken into lines and then each line is folded if necessary. a cr is written at the end even if the field's source did not have cr "

	(self fieldValueAsStreamFor: aHeaderField)
		linesDo: [ :line | self printLine: line]</body>

<body package="MIME" selector="printLine:">printLine: aString

	self stream nextPutAll: aString.
	self stream cr</body>

<body package="MIME" selector="printPreambule:">printPreambule: aMessageBody
" Print preamble if any "
	| aStream |
	(aStream := aMessageBody preamble) notNil
		ifTrue: [ aStream linesDo: [:line | self printLine: line ]].
	^true</body>
</methods>

<methods>
<class-id>Net.MimeOutputHandler</class-id> <category>private</category>

<body package="MIME" selector="fieldStream">fieldStream
" field stream is used to convert field values into streams. field stream is reused for performance reasons "

	^fieldStream ifNil: [fieldStream := (String new: 256) writeStream]</body>

<body package="MIME" selector="fieldValueAsStreamFor:">fieldValueAsStreamFor: aHeaderField

	aHeaderField printOn: self fieldStream reset.
	^self fieldStream contents readStream</body>

<body package="MIME" selector="finishEmptyBodyMessage:">finishEmptyBodyMessage: anEntity</body>

<body package="MIME" selector="prepareHeader:">prepareHeader: aMimeEntity

	self setBoundary: aMimeEntity</body>

<body package="MIME" selector="selectFieldsFrom:">selectFieldsFrom: aMessageHeader

	^aMessageHeader fieldsInOrder: aMessageHeader parent transferFieldOrder</body>

<body package="MIME" selector="setBoundary:">setBoundary: aMimeEntity 

	aMimeEntity needsBoundary
		ifTrue: [aMimeEntity boundary: (self defaultBoundaryFor: aMimeEntity)].</body>

<body package="MIME" selector="setTransferContext:">setTransferContext: aMimeEntity 

	^#()</body>

<body package="MIME" selector="writeFrom:to:">writeFrom: sourceStream to: destStream

	| buffer count textMode |
	textMode := sourceStream isBinary not.
	sourceStream binary.
	destStream binary.
	buffer := ByteArray new: 1024.
	[
		[sourceStream atEnd] 
			whileFalse: 
				[count := sourceStream nextAvailable: buffer size into: buffer startingAt: 1.
				destStream next: count putAll: buffer startingAt: 1 ]
	] ensure: 
		[textMode ifTrue: [sourceStream text].
		destStream text ]</body>
</methods>

<methods>
<class-id>Net.MimeOutputHandler</class-id> <category>accessing</category>

<body package="MIME" selector="popStream">popStream

	^self streams size &gt; 0 ifTrue: [ self streams removeFirst ]</body>

<body package="MIME" selector="pushStream:">pushStream: aStream

	^self streams addFirst: aStream</body>

<body package="MIME" selector="stream">stream
	^self streams first</body>

<body package="MIME" selector="streams">streams

	^streams ifNil: [ streams := OrderedCollection new]</body>
</methods>

<methods>
<class-id>Net.MimeOutputHandler</class-id> <category>action callbacks</category>

<body package="MIME" selector="endEntity:">endEntity: aMimeEntityOrNil

	^aMimeEntityOrNil</body>

<body package="MIME" selector="endHeader:">endHeader: aMessageHeader

	self stream cr.
	^aMessageHeader parent</body>

<body package="MIME" selector="endMessage:">endMessage: aMessage

	"finish stream output but do not close the stream" 
	self stream finish</body>

<body package="MIME" selector="endMultipartBody:">endMultipartBody: aMessageBody
" Prints end of boundary for a multipart message "

	self stream writeConclusion.
	self popStream.</body>

<body package="MIME" selector="endPart:">endPart: aMimeEntity

	self stream endPart.</body>

<body package="MIME" selector="processMessageBody:do:">processMessageBody:  aMessage do: aBlock

	aBlock value</body>

<body package="MIME" selector="shouldStartBody:">shouldStartBody: aMessageBody

	^self headerOnly not and: [aMessageBody notEmpty ]</body>

<body package="MIME" selector="startEntity:">startEntity: aMimeEntityOrNil

	^aMimeEntityOrNil</body>

<body package="MIME" selector="startHeader:">startHeader: aMessageHeader 
	"Print header fields"

	self 
		prepareHeader: aMessageHeader parent;
		printHeader: aMessageHeader.</body>

<body package="MIME" selector="startMessageBody:">startMessageBody: aMessage

	| streamx |
	streamx := self prepareStream: self stream forMessageBody: aMessage.
	self popStream.
	self pushStream: streamx.

	"print preambule if any "
	self printPreambule: aMessage body.</body>

<body package="MIME" selector="startMultipartBody:">startMultipartBody: aMultipartBody

	| bodyStream |
	bodyStream :=
		BoundedWriteStream
			on: self stream
			boundary: aMultipartBody parent boundary.
	self pushStream: bodyStream.</body>

<body package="MIME" selector="startPart:">startPart: aMimeEntity

	self stream startPart.</body>
</methods>

<methods>
<class-id>Net.MimeOutputHandler</class-id> <category>private prepare streams</category>

<body package="MIME" selector="prepareStream:">prepareStream: aStream

	self pushStream: ( aStream 
							asEncodedStreamWithEncoding: self defaultEncoding 
							lineEndConvention: LineEndCRLF ).
	self stream text; lineEndCRLF.</body>

<body package="MIME" selector="prepareStream:forMessageBody:">prepareStream: aStream forMessageBody: aMessage

	^aStream</body>
</methods>

<methods>
<class-id>Net.MimeOutputHandler</class-id> <category>defaults</category>

<body package="MIME" selector="defaultEncoding">defaultEncoding
	
	^NetConnection defaultEncoding</body>
</methods>

<methods>
<class-id>Net.MimeWriteHandler</class-id> <category>private</category>

<body package="MIME" selector="contentTransferEncodingFor:">contentTransferEncodingFor: aMimeEntity
	^'base64'</body>

<body package="MIME" selector="fieldValueAsStreamFor:">fieldValueAsStreamFor: aHeaderField
	^(self preferFieldSource and: [aHeaderField source notNil])
		ifTrue: [ aHeaderField source ]
		ifFalse: [
			aHeaderField writeOn: self fieldStream reset.
			self fieldStream contents readStream ]</body>

<body package="MIME" selector="prepareHeader:">prepareHeader: aMimeEntity
"  http://www.faqs.org/rfcs/rfc1521.html
 Therefore, this document defines a new header field, MIME-Version,  which is to be used to declare the version of the Internet message body format standard in use. Messages composed in accordance with this document MUST include such a header field, with the following verbatim text:  MIME-Version: 1.0
The version field will be added only for instances of the MailMessage class"

	self setBoundary: aMimeEntity.
	aMimeEntity addMimeVersion.</body>

<body package="MIME" selector="selectFieldsFrom:">selectFieldsFrom: aMessageHeader

	^(super selectFieldsFrom: aMessageHeader) 
		addAll: ( self setTransferContext: aMessageHeader parent);
		yourself.</body>

<body package="MIME" selector="setTransferContext:">setTransferContext: aMimeEntity 

	^aMimeEntity needsContentTransferEncoding
		ifTrue: [ Array with: (
				ScalarField contentTransferEncoding: (
					self contentTransferEncodingFor: aMimeEntity))]
		ifFalse: [ #() ]</body>
</methods>

<methods>
<class-id>Net.MimeWriteHandler</class-id> <category>printing utilities</category>

<body package="MIME" selector="encodeBodySource:as:">encodeBodySource: aSimpleBody as: encodingName
"the method is used by SimpleBody to add transfer encoding "
	| aStream outStream decStream | 
	(#('base64' 'quoted-printable') includes: encodingName) ifFalse: [^self].
	outStream := (ByteArray new 
					withEncoding: aSimpleBody parent charset )
					readWriteStream.
	aStream := aSimpleBody byteValueAsStream.
	[decStream := (encodingName = 'base64' asLowercase
		ifTrue: [	DecodedStream on: outStream  encodedBy: B64StreamDecoder asEncoder]
		ifFalse: [ DecodedStream on: outStream  encodedBy: QuotedPrintableDecoder asEncoder ]).

	self writeFrom: aStream to: decStream. 
	decStream finish.
	]  ensure: 
		[aStream ~~nil ifTrue: [ aStream close ]].
	outStream reset.
	aSimpleBody source: outStream</body>

<body package="MIME" selector="printHeader:">printHeader: aMessageHeader

	self stream text.
	super printHeader: aMessageHeader</body>

<body package="MIME" selector="printSimpleBodyStream:entity:">printSimpleBodyStream: aStream entity: aMimeEntity

	| streamx |
	streamx := self prepareStream: self stream forSimpleBody: aMimeEntity.
	self writeFrom: aStream to: streamx. 
	streamx finish.</body>

<body package="MIME" selector="writeBodyContents:on:">writeBodyContents: aBody on: aStream

	| sourceStream destStream |

	self pushStream: aStream. 
	sourceStream :=  aBody parent notNil
		ifTrue:  [aBody parent contentsStream]
		ifFalse: [ aBody valueAsStream]. 
	destStream := self stream.
	sourceStream isByteStream ifTrue: [ destStream binary ].
	sourceStream isCharacterStream ifTrue: [ destStream text ].

	[
		[sourceStream atEnd] 
			whileFalse: 
				[destStream nextPut: sourceStream next ]
	] ensure: 
		[sourceStream close ]</body>
</methods>

<methods>
<class-id>Net.MimeWriteHandler</class-id> <category>tasks</category>

<body package="MIME" selector="writeMessage:on:">writeMessage: aMimeEntity on: aStream 
	self writeMessageElement: aMimeEntity on: aStream</body>

<body package="MIME" selector="writeMessageElement:on:">writeMessageElement: aMessageElement on: aStream

	self prepareStream: aStream.
	aMessageElement acceptVisit: self dispatcher with: nil.</body>

<body package="MIME" selector="writeMessageHeaderFor:on:">writeMessageHeaderFor: aMimeEntity on: aStream
	self 
		headerOnly: true; 
		writeMessage: aMimeEntity on: aStream</body>
</methods>

<methods>
<class-id>Net.MimeWriteHandler</class-id> <category>accessing</category>

<body package="MIME" selector="applyTransferEncoding">applyTransferEncoding
	^applyTransferEncoding ifNil: [self class applyTransferEncoding ]</body>

<body package="MIME" selector="applyTransferEncoding:">applyTransferEncoding: aBoolean
	applyTransferEncoding := aBoolean</body>

<body package="MIME" selector="preferFieldSource">preferFieldSource
" Answers true if field's source is preferred to its parsed values, false otherwise. If application resends, routes of forwards messages, all mail protocols strongly advise against and changes in message header fields. These changes are unavoidable if all fields are to be parsed and later composed when a message is resent/forwarded. In this situation a wise approach is to prefer source, so fields are copied to the output exactly the way thay have been received "

	^preferFieldSource ifNil: [false]</body>

<body package="MIME" selector="preferFieldSource:">preferFieldSource: aBoolean

	preferFieldSource := aBoolean</body>
</methods>

<methods>
<class-id>Net.MimeWriteHandler</class-id> <category>action callbacks</category>

<body package="MIME" selector="endMessage:">endMessage: aMessage

	super endMessage: aMessage.
	"restore the print stream original mode"
	binaryMode ifNotNil: [ :mode |  mode ifFalse: [originalStream text]].</body>

<body package="MIME" selector="startSimpleBody:">startSimpleBody: aSimpleBody

	| aStream | 
	aStream := aSimpleBody byteValueAsStream.
	[	self printSimpleBodyStream: aStream entity: aSimpleBody parent
	]  ensure: 
		[aStream ~~nil ifTrue: [ aStream close ]]</body>
</methods>

<methods>
<class-id>Net.MimeWriteHandler</class-id> <category>private prepare streams</category>

<body package="MIME" selector="prepareStream:">prepareStream: aStream
	
	aStream isInternal ifFalse: [binaryMode := aStream isBinary].
	super prepareStream: aStream.
	originalStream := self stream</body>

<body package="MIME" selector="prepareStream:forSimpleBody:">prepareStream: aStream forSimpleBody: aMimeEntity

	^(self applyTransferEncoding and: [aMimeEntity needsContentTransferEncoding])
				ifTrue: [	DecodedStream on: aStream encodedBy: B64StreamDecoder asEncoder.]
				ifFalse: [aStream]</body>
</methods>

<methods>
<class-id>Net.MimeWriteHandler class</class-id> <category>instance creation</category>

<body package="MIME" selector="write:on:">write: message on: aStream
	^self writeMessage: message on: aStream</body>

<body package="MIME" selector="writeHeaderFor:on:">writeHeaderFor: message on: aStream
	^self writeMessageHeaderFor: message on: aStream</body>

<body package="MIME" selector="writeMessage:on:">writeMessage: message on: aStream
	^self new writeMessage: message on: aStream</body>

<body package="MIME" selector="writeMessageHeaderFor:on:">writeMessageHeaderFor: message on: aStream
	^self new writeMessageHeaderFor: message on: aStream</body>
</methods>

<methods>
<class-id>Net.MimeWriteHandler class</class-id> <category>accessing</category>

<body package="MIME" selector="applyTransferEncoding">applyTransferEncoding
	^applyTransferEncoding ifNil: [ self defaultApplyTransferEncoding ]</body>

<body package="MIME" selector="applyTransferEncoding:">applyTransferEncoding: aBoolean
	applyTransferEncoding := aBoolean</body>

<body package="MIME" selector="defaultApplyTransferEncoding">defaultApplyTransferEncoding
	^true</body>
</methods>

<methods>
<class-id>Net.MimeWriteHandler class</class-id> <category>deprecated</category>

<body package="MIME" selector="on:">on: aStream
" will be removed"
	^self new
		prepareStream: aStream</body>
</methods>

<methods>
<class-id>Net.MessageStream</class-id> <category>private</category>

<body package="MIME" selector="contentsSpecies">contentsSpecies

	^stream contentsSpecies</body>

<body package="MIME" selector="pastEnd">pastEnd
	"The receiver has attempted to read past the end, answer nil."

	^self class endOfStreamSignal raiseRequestFrom: self</body>
</methods>

<methods>
<class-id>Net.MessageStream</class-id> <category>accessing</category>

<body package="MIME" selector="binary">binary

	stream binary</body>

<body package="MIME" selector="contents">contents
	^self error: (#NotImplementedYet &lt;&lt; #net &gt;&gt; 'This message has not been implemented yet')</body>

<body package="MIME" selector="lineEnd">lineEnd

	^#[ 13 10]</body>

<body package="MIME" selector="text">text
	stream text.</body>
</methods>

<methods>
<class-id>Net.MessageStream class</class-id> <category>instance creation</category>

<body package="MIME" selector="new">new
	^super new initialize</body>

<body package="MIME" selector="on:">on: aStream
	^self basicNew initializeOn: aStream</body>
</methods>

<methods>
<class-id>Net.BoundedStream</class-id> <category>accessing</category>

<body package="MIME" selector="boundary">boundary

	^boundary</body>

<body package="MIME" selector="boundaryDashes">boundaryDashes

	^boundaryDashes</body>

<body package="MIME" selector="flush">flush

	stream flush</body>
</methods>

<methods>
<class-id>Net.BoundedStream</class-id> <category>initialize-release</category>

<body package="MIME" selector="initializeOn:boundary:">initializeOn: aStream boundary: aString

	stream := aStream. 
	stream binary.
	boundaryDashes := MultipartBody boundaryDashes asByteArrayEncoding: NetConnection defaultEncoding.
	self setBoundary:aString.
	partNumber := 0.</body>

<body package="MIME" selector="setBoundary:">setBoundary: aString

	boundary := (self lineEnd asString, MultipartBody boundaryDashes , aString) 
					asByteArrayEncoding: #'ISO8859_1'.</body>
</methods>

<methods>
<class-id>Net.BoundedStream class</class-id> <category>instance creation</category>

<body package="MIME" selector="on:boundary:">on: aStream boundary: aString

	^self basicNew initializeOn: aStream boundary: aString</body>
</methods>

<methods>
<class-id>Net.BoundedWriteStream</class-id> <category>accessing</category>

<body package="MIME" selector="endPart">endPart

	stream 
		binary;
		nextPutAll: self lineEnd.

	stream flush</body>

<body package="MIME" selector="nextPut:">nextPut: anObject 
	
	stream nextPut: anObject</body>

<body package="MIME" selector="startPart">startPart

	partNumber := partNumber + 1.
	stream 
		binary;
		nextPutAll: self boundary;
		nextPutAll: self lineEnd.

	stream flush</body>

<body package="MIME" selector="stream">stream

	^stream</body>

<body package="MIME" selector="writeConclusion">writeConclusion

	stream 
		binary;
		nextPutAll:  self boundary;
		nextPutAll: self boundaryDashes;
		nextPutAll: self lineEnd;
		flush</body>
</methods>

<methods>
<class-id>Net.BoundedWriteStream</class-id> <category>testing</category>

<body package="MIME" selector="isReadable">isReadable

	^false</body>

<body package="MIME" selector="isWritable">isWritable
	"Answer whether the stream implements #nextPut:."

	^true</body>
</methods>

<methods>
<class-id>Net.BoundedWriteStream</class-id> <category>status</category>

<body package="MIME" selector="finish">finish

	stream finish.</body>
</methods>

<methods>
<class-id>Net.BoundedWriteStream</class-id> <category>initialize-release</category>

<body package="MIME" selector="setBoundary:">setBoundary: aString

	boundary := (MultipartBody boundaryDashes , aString) 
					asByteArrayEncoding: NetConnection defaultEncoding.</body>
</methods>

<methods>
<class-id>Net.HeaderField</class-id> <category>accessing</category>

<body package="MIME" selector="canMerge">canMerge
	^false</body>

<body package="MIME" selector="canonicalFieldName">canonicalFieldName
	| s |

	s := name copy.
	(s isNil or: [ s isEmpty]) ifTrue: [ ^String new ].
	s at: 1 put: s first asUppercase.			" Capitalize first letter "
	^ s</body>

<body package="MIME" selector="canonicalValue">canonicalValue
" Override as necessary "
	^self value</body>

<body package="MIME" selector="name">name
	^name</body>

<body package="MIME" selector="name:">name: aString
	name := aString</body>

<body package="MIME" selector="values">values
" Multi-valued fields override this method "
	^Array with: self value</body>

<body package="MIME" selector="values:">values: anArray
" Multi-valued fields override this method "
	anArray size ~= 1 ifTrue: [ ^self error: (#CantSetMultipleValuesInField &lt;&lt; #net &gt;&gt; 'Can''t set multiple values in this field') ].
	self value: anArray first</body>
</methods>

<methods>
<class-id>Net.HeaderField</class-id> <category>private</category>

<body package="MIME" selector="doParse:">doParse: rfc822Stream
" Generic parser for unstructured fields. This method is typically overridden for fields with known syntax to do more detailed parsing "

	^rfc822Stream skipWhiteSpace; scanText</body>

<body package="MIME" selector="getValue">getValue

	^(value isNil and: [self source notNil])
		ifTrue: [[value := self doParse: (self scannerOn: self source)]
				ensure: [self source close]]
		ifFalse: [value]</body>
</methods>

<methods>
<class-id>Net.HeaderField</class-id> <category>printing</category>

<body package="MIME" selector="printNameOn:">printNameOn: aStream

	aStream
		nextPutAll: self canonicalFieldName;
		nextPut: $: ;
		space.</body>

<body package="MIME" selector="printOn:indent:">printOn: aStream indent: level

	aStream tab: level.
	self 
		printNameOn: aStream;
		printValueOn: aStream.</body>

<body package="MIME" selector="printValueOn:">printValueOn: aStream
	| val | 
	(val := self value) notNil
		ifTrue: 
			[ (val isKindOf: String) 
				ifTrue: [ aStream nextPutAll:  val]
				ifFalse: [ val printOn: aStream ] ]</body>
</methods>

<methods>
<class-id>Net.HeaderField</class-id> <category>private-initialize</category>

<body package="MIME" selector="valueFrom:">valueFrom: aString 
" Swazoo compatibility"

	^self readFrom: aString readStream</body>
</methods>

<methods>
<class-id>Net.HeaderField</class-id> <category>visiting</category>

<body package="MIME" selector="acceptVisit:with:">acceptVisit: aVisitor  with: argument
	^aVisitor acceptHeaderField: self with: argument</body>
</methods>

<methods>
<class-id>Net.HeaderField</class-id> <category>composing</category>

<body package="MIME" selector="writeOn:">writeOn: aStream
	self parent ~~ nil
		ifTrue: [ 	self writeOn: aStream encoding: self parent headerCharset ]
		ifFalse: [ self printOn: aStream ]</body>

<body package="MIME" selector="writeOn:encoding:">writeOn: aStream encoding: aString
"Encoding should be added to value if needed "
	self 
		printNameOn: aStream;
		writeValueOn: aStream encoding: aString.</body>

<body package="MIME" selector="writeValueOn:encoding:">writeValueOn: aStream encoding: aString
	| val rfcString |
	(val := self value) notNil
		ifTrue: 
			[ rfcString :=  (MimeEncodedWordCoDec shouldBeEncoded: val)
				ifTrue: [ (MimeEncodedWordCoDec encodeText: val asBase64Using: aString) ]
				ifFalse: [ val ].
			aStream nextPutAll: rfcString]</body>
</methods>

<methods>
<class-id>Net.HeaderField</class-id> <category>parsing</category>

<body package="MIME" selector="parse:">parse: rfc822Stream
" Generic parser for unstructured fields. Copy everything up to CRLF. Scanner handles end of line rules and answers cr when end of line is seen. Scanner also folds linear white space answering space character in place of &lt;CRLF space+&gt; "

	self source: rfc822Stream asStream.
	value := self doParse: rfc822Stream.</body>
</methods>

<methods>
<class-id>Net.HeaderField class</class-id> <category>parsing</category>

<body package="MIME" selector="defaultFieldClass">defaultFieldClass
	^HeaderField</body>

<body package="MIME" selector="fieldClassForName:">fieldClassForName: fieldName

	^self fieldsRegistry at:  fieldName asLowercase ifAbsent: [self defaultFieldClass].</body>

<body package="MIME" selector="fieldNames">fieldNames
	^#()</body>

<body package="MIME" selector="fromLine:">fromLine: aString 
" For compatibility with Swazoo "

	^self readFrom: aString readStream</body>

<body package="MIME" selector="readFrom:">readFrom: aStream 
	"Reads and parses message header contents from the message stream; answers an instance of header field"

	^self readFrom: aStream acceptNonAsciiCharacters: nil</body>

<body package="MIME" selector="readFrom:acceptNonAsciiCharacters:">readFrom: aStream acceptNonAsciiCharacters: aBoolen

	"Reads and parses message header contents from the message stream; answers an instance of header field"

	| fname scanner |
	scanner := self scannerOn: aStream.
	scanner acceptNonAsciiCharacters: aBoolen.
	^(fname := scanner readFieldName) isNil
		ifTrue: [nil]
		ifFalse: [(self name: fname)
				source: aStream;
				parse: scanner]</body>

<body package="MIME" selector="scannerType">scannerType
	^MimeScanner</body>
</methods>

<methods>
<class-id>Net.HeaderField class</class-id> <category>instance creation</category>

<body package="MIME" selector="default">default
	^self new</body>

<body package="MIME" selector="name:">name: aname
" Answer new instance of field corresponding to field's name. For now, treat all fields as unstructured "

	^(self fieldClassForName: aname) new name: aname; yourself</body>

<body package="MIME" selector="new">new
	^super new initialize</body>
</methods>

<methods>
<class-id>Net.HeaderField class</class-id> <category>class initialization</category>

<body package="MIME" selector="fieldsRegistry">fieldsRegistry
	"(self fieldsRegistry)"
	FieldsRegistry isEmpty
		ifTrue: [ self registerClassFields].

	^FieldsRegistry</body>

<body package="MIME" selector="initialize">initialize
	"(((self initialize)))"
	self resetRegistry.</body>

<body package="MIME" selector="registerClassFields">registerClassFields
	HeaderField allSubclasses 
		do: [ :cl | 
			cl fieldNames do: [ :fname | FieldsRegistry at: fname put: cl ]]</body>

<body package="MIME" selector="resetRegistry">resetRegistry
	FieldsRegistry := Dictionary new: 5.</body>
</methods>

<methods>
<class-id>Net.HeaderField class</class-id> <category>accessing</category>

<body package="MIME" selector="acceptNonAsciiCharacters">acceptNonAsciiCharacters

	^self scannerType acceptNonAsciiCharacters</body>

<body package="MIME" selector="acceptNonAsciiCharacters:">acceptNonAsciiCharacters: aBoolean

	self scannerType acceptNonAsciiCharacters: aBoolean</body>

<body package="MIME" selector="acceptNonAsciiCharactersValue">acceptNonAsciiCharactersValue

	^self scannerType acceptNonAsciiCharactersValue</body>
</methods>

<methods>
<class-id>Net.StructuredHeaderField</class-id> <category>accessing</category>

<body package="MIME" selector="canonicalValue">canonicalValue
" Canonical value removes all white space and comments from the source "
	^self tokenizedValueFrom: (self scannerOn: self source readStream)</body>

<body package="MIME" selector="comment">comment
	^comment</body>

<body package="MIME" selector="comment:">comment: aString
	comment := aString</body>
</methods>

<methods>
<class-id>Net.StructuredHeaderField</class-id> <category>printing</category>

<body package="MIME" selector="printCommentOn:">printCommentOn: aStream 
	self class scannerType printComment: self comment on: aStream</body>

<body package="MIME" selector="printStructureOn:">printStructureOn: aStream
" Default implementation is the same as inherited. Subclasses can override it "
	super printValueOn: aStream.
	self printCommentOn: aStream</body>

<body package="MIME" selector="printValueOn:">printValueOn: aStream
	self printStructureOn: aStream</body>
</methods>

<methods>
<class-id>Net.StructuredHeaderField</class-id> <category>private-utility</category>

<body package="MIME" selector="sanitizeAddress:">sanitizeAddress: addressOrString 
	"Make addresses is strings"

	^addressOrString isString
		ifTrue: [addressOrString ]
		ifFalse: [addressOrString printString]</body>

<body package="MIME" selector="sanitizeAddress:encoding:">sanitizeAddress: addressOrString encoding: aString
	"Make addresses as strings"
	^addressOrString isString
		ifTrue: [MimeEncodedWordCoDec encodeIfRequired: addressOrString using: aString]
		ifFalse: 	[addressOrString stringWithEncoding: aString]</body>

<body package="MIME" selector="tokenize:">tokenize: rfc822Stream 
	"Scan field value token by token. Answer an array of tokens"

	| result token |
	result := (Array new: 2) writeStream.
	[rfc822Stream atEnd or: [rfc822Stream peek == Character cr or: [(token := rfc822Stream nextToken) isNil]]]
		whileFalse: [result nextPut: token].
	^result contents</body>

<body package="MIME" selector="tokenizedValueFrom:">tokenizedValueFrom: rfc822Stream 
	"Scan field value token by token. Answer a string that is a concatenation of all elements in the array. One can view this as a canonicalized field value because this operation eliminates all white space and comments "

	| result tokens |
	result := (String new: 32) writeStream.
	tokens := self tokenize: rfc822Stream.
	tokens
		do: [:token | token isString
				ifTrue: [result nextPutAll: token]
				ifFalse: [result nextPut: token]
].
	^result contents</body>
</methods>

<methods>
<class-id>Net.StructuredHeaderField</class-id> <category>composing</category>

<body package="MIME" selector="writeCommentOn:encoding:">writeCommentOn: aStream encoding: aString
	self comment ~~ nil 
		ifTrue: 
			[aStream nextPutAll: 
				(MimeEncodedWordCoDec 
					encodeComment: self comment 
					asBase64Using: aString)]</body>

<body package="MIME" selector="writeStructureOn:encoding:">writeStructureOn: aStream encoding: aString
" Default implementation is the same as inherited. Subclasses can override it "
	self  printValueOn: aStream.
	self writeCommentOn: aStream encoding: aString</body>

<body package="MIME" selector="writeValueOn:encoding:">writeValueOn: aStream encoding: aString
	self writeStructureOn: aStream encoding: aString</body>
</methods>

<methods>
<class-id>Net.CollectionField</class-id> <category>accessing</category>

<body package="MIME" selector="separator">separator
	separator isNil 
		ifTrue: [ separator := self class defaultSeparator].
	^separator</body>

<body package="MIME" selector="separator:">separator: aSymbol
	separator := aSymbol</body>

<body package="MIME" selector="value">value
	^value isNil
		ifTrue: [ value := OrderedCollection new]
		ifFalse: [value]</body>

<body package="MIME" selector="value:">value: aStringOrCollection

	aStringOrCollection isString
		ifTrue: [ self value add: aStringOrCollection]
		ifFalse: [ self value addAll: aStringOrCollection]</body>
</methods>

<methods>
<class-id>Net.CollectionField</class-id> <category>printing</category>

<body package="MIME" selector="printValueOn:">printValueOn: aStream 
	self value  
		do: [ :param |  aStream nextPutAll: param ]
		separatedBy: [aStream nextPut: self separator].
	aStream cr.</body>
</methods>

<methods>
<class-id>Net.CollectionField</class-id> <category>parsing</category>

<body package="MIME" selector="doParse:">doParse: scanner
	| coll stream |
	coll := OrderedCollection new. 
	stream := (String new: 32) writeStream.
	[scanner skipWhiteSpace.
	scanner nextToken. 
	scanner tokenType = #doIt ]
		whileFalse: 
			[ scanner token ~~ self separator
				ifTrue: 
					[scanner 
						printToken: scanner token 
						tokenType: scanner tokenType 
						on: stream].
			(scanner atEnd or: [scanner token == self separator])
				ifTrue: 
					[ coll add: (self parseItem: stream contents).
					stream := (String new: 20) writeStream]]. 

	stream contents notEmpty ifTrue: [ coll add: (self parseItem: stream contents)].

	^coll</body>

<body package="MIME" selector="parseItem:">parseItem: aString
	^aString</body>
</methods>

<methods>
<class-id>Net.CollectionField class</class-id> <category>defaults</category>

<body package="MIME" selector="defaultSeparator">defaultSeparator
	^$,</body>
</methods>

<methods>
<class-id>Net.CollectionField class</class-id> <category>instant creation</category>

<body package="MIME" selector="name:value:">name: nameString value: stringOrCollection
	^( self name: nameString)
		value: stringOrCollection;
		yourself</body>
</methods>

<methods>
<class-id>Net.CollectionField class</class-id> <category>parsing</category>

<body package="MIME" selector="fieldNames">fieldNames
	^#('accept-encoding' 'allow' 'accept-ranges' 'if-match' 'if-none-match' 'public' 'connection' )</body>
</methods>

<methods>
<class-id>Net.AttachmentFileExists</class-id> <category>constants</category>

<body package="MIME" selector="defaultResumeValue">defaultResumeValue
	"delete the file."

	^self filename</body>
</methods>

<methods>
<class-id>Net.AttachmentFileExists</class-id> <category>printing</category>

<body package="MIME" selector="defaultMessageText">defaultMessageText
	^(#FilenameExist &lt;&lt; #net &gt;&gt; ('The file name: ', filename tail, ' already exists' )) asString</body>
</methods>

<methods>
<class-id>Net.AttachmentFileExists</class-id> <category>accessing</category>

<body package="MIME" selector="filename">filename
	^filename</body>

<body package="MIME" selector="filename:">filename: aFilename
	filename := aFilename</body>
</methods>

<methods>
<class-id>Net.AttachmentFileExists class</class-id> <category>Testing</category>

<body package="MIME" selector="mayResume">mayResume

	^true</body>
</methods>

<methods>
<class-id>Net.ScalarField</class-id> <category>parsing</category>

<body package="MIME" selector="doParse:">doParse: rfc822Stream 
	^self tokenizedValueFrom: rfc822Stream</body>
</methods>

<methods>
<class-id>Net.ScalarField class</class-id> <category>instant creation</category>

<body package="MIME" selector="base64">base64
" ScalarField base64 "

	^self contentTransferEncoding: 'base64'</body>

<body package="MIME" selector="contentTransferEncoding:">contentTransferEncoding: encodingName
" Answer an instance of this field representing desired content transfer encoding. encodingName is one of 'base64' or 'quoted-printable' "
" ScalarField contentTransferEncoding: 'base64' "

	^(self name: 'content-transfer-encoding') value: encodingName</body>

<body package="MIME" selector="quotedPrintable">quotedPrintable
" ScalarField quotedPrintable "

	^self contentTransferEncoding: 'quoted-printable'</body>
</methods>

<methods>
<class-id>Net.ScalarField class</class-id> <category>parsing</category>

<body package="MIME" selector="fieldNames">fieldNames
	^#('message-id' 'content-id' 'content-transfer-encoding' 'content-encoding')</body>
</methods>

<methods>
<class-id>Net.SingleMailboxField</class-id> <category>printing</category>

<body package="MIME" selector="addressString">addressString
	^self asString</body>

<body package="MIME" selector="asString">asString
	^self address printString</body>
</methods>

<methods>
<class-id>Net.SingleMailboxField</class-id> <category>accessing</category>

<body package="MIME" selector="address">address
	^address</body>

<body package="MIME" selector="address:">address: aNetworkAddressDescriptor
	address := aNetworkAddressDescriptor.
	self value: (self sanitizeAddress: aNetworkAddressDescriptor)</body>

<body package="MIME" selector="addresses">addresses
	^self address</body>
</methods>

<methods>
<class-id>Net.SingleMailboxField</class-id> <category>parsing</category>

<body package="MIME" selector="doParse:">doParse: rfc822Stream 
" HeaderField fromLine: 'Sender :		Phil Campbell (The great) &lt;philc@yahoo.com&gt;' "
	address := NetworkAddressDescriptor addressFrom: rfc822Stream asStream.
	^self sanitizeAddress: address</body>
</methods>

<methods>
<class-id>Net.SingleMailboxField class</class-id> <category>parsing</category>

<body package="MIME" selector="fieldNames">fieldNames
	^#('sender' 'resent-sender' 'return-path' )</body>
</methods>

<methods>
<class-id>Net.NetworkEntityDescriptor</class-id> <category>accessing</category>

<body package="MIME" selector="alias">alias

	^alias</body>

<body package="MIME" selector="alias:">alias: aValue

	alias := aValue</body>

<body package="MIME" selector="comment">comment

	^comment</body>

<body package="MIME" selector="comment:">comment: aValue

	comment := aValue</body>

<body package="MIME" selector="stringWithEncoding:">stringWithEncoding: aString
	| stream |
	stream := (String new: 256) writeStream.
	self writeOn: stream encoding: aString.
	^stream contents</body>
</methods>

<methods>
<class-id>Net.NetworkEntityDescriptor</class-id> <category>printing</category>

<body package="MIME" selector="printAliasOn:">printAliasOn: stream 
	alias notNil 
		ifTrue: 
			[(self scannerType shouldBeQuoted: alias)
				ifTrue: [ stream nextPut: $"; nextPutAll: alias; nextPut: $" ]
				ifFalse: [stream nextPutAll: alias]].</body>

<body package="MIME" selector="printCanonicalValueOn:">printCanonicalValueOn: stream
	self subclassResponsibility</body>

<body package="MIME" selector="printCommentOn:">printCommentOn: stream 
	comment notNil ifTrue: [
		stream nextPut: $(.
		comment
			do: 
				[:char | 
				(RFC822Scanner isCommentChar: char)
					ifFalse: [stream nextPut: $\].
				stream nextPut: char].
		stream nextPut: $).
	]</body>

<body package="MIME" selector="printOn:indent:">printOn: stream indent: level

	self printCanonicalValueOn: stream.
	comment notNil ifTrue: [self printCommentOn: stream]</body>
</methods>

<methods>
<class-id>Net.NetworkEntityDescriptor</class-id> <category>parsing</category>

<body package="MIME" selector="scannerType">scannerType
	^self class scannerType</body>
</methods>

<methods>
<class-id>Net.NetworkEntityDescriptor</class-id> <category>composing</category>

<body package="MIME" selector="writeAliasOn:encoding:">writeAliasOn: stream encoding: aString
	"Encoding is required if alias contains non-ASCII chars."

	alias notNil 
		ifTrue: 
			[(MimeEncodedWordCoDec shouldBeEncoded: alias)
				ifTrue: [ stream nextPutAll: (MimeEncodedWordCoDec 
									encodeText: alias 
									asQuotedPrintableUsing: aString)]
				ifFalse: 
					[(self scannerType shouldBeQuoted: alias) 
						ifTrue: [stream nextPut: $"; nextPutAll: alias; nextPut: $"]
						ifFalse: [stream nextPutAll: alias]]]</body>

<body package="MIME" selector="writeCanonicalValueOn:encoding:">writeCanonicalValueOn: stream encoding: aString
	self subclassResponsibility</body>

<body package="MIME" selector="writeCommentOn:encoding:">writeCommentOn: stream encoding: aString

	self comment ~~ nil 
		ifTrue: 
			[stream nextPutAll: 
				(MimeEncodedWordCoDec 
					encodeComment: self comment 
					asBase64Using: aString)]</body>

<body package="MIME" selector="writeOn:encoding:">writeOn: stream encoding: aString
	self writeCanonicalValueOn: stream encoding: aString.
"	comment notNil ifTrue: [self writeCommentOn: stream encoding: aString] "</body>
</methods>

<methods>
<class-id>Net.NetworkEntityDescriptor class</class-id> <category>parsing</category>

<body package="MIME" selector="scannerType">scannerType
	^NetworkAddressParser</body>
</methods>

<methods>
<class-id>Net.MimeMessageBody</class-id> <category>testing</category>

<body package="MIME" selector="isBinary">isBinary
	^self parent isBinary</body>

<body package="MIME" selector="isBinaryContentType">isBinaryContentType
	^self parent isBinaryContentType</body>

<body package="MIME" selector="isEmpty">isEmpty
	^self notEmpty not</body>

<body package="MIME" selector="isMultipart">isMultipart
	^false</body>

<body package="MIME" selector="isSimple">isSimple
	^true</body>

<body package="MIME" selector="notEmpty">notEmpty
	^self size &gt; 0</body>
</methods>

<methods>
<class-id>Net.MimeMessageBody</class-id> <category>accessing</category>

<body package="MIME" selector="decodedValue">decodedValue
	^self value</body>

<body package="MIME" selector="decodedValueWith:">decodedValueWith: aSymbol
	^self value</body>

<body package="MIME" selector="mimeType">mimeType
	^self parent mimeType</body>

<body package="MIME" selector="partAt:">partAt: anInteger
	self subclassResponsibility</body>

<body package="MIME" selector="removePart:">removePart: aMimeEntity
	self subclassResponsibility</body>

<body package="MIME" selector="removePartAt:">removePartAt: anInteger
	self subclassResponsibility</body>

<body package="MIME" selector="size">size
	^ self sourceSize</body>
</methods>

<methods>
<class-id>Net.MimeMessageBody</class-id> <category>writing</category>

<body package="MIME" selector="writeContentsOn:">writeContentsOn: aStream
" Write your contents on a stream. Used to save to a file. etc. "
	self subclassResponsibility</body>
</methods>

<methods>
<class-id>Net.MimeMessageBody</class-id> <category>printing</category>

<body package="MIME" selector="preamble">preamble
	^nil</body>

<body package="MIME" selector="printForDisplayOn:">printForDisplayOn: aStream
	self printOn: aStream</body>

<body package="MIME" selector="printOn:indent:">printOn: aStream indent: level
	(self parent ifNil: [ MimeEntity ]) newPrinter
		printMessageElement: self on: aStream indent: level</body>
</methods>

<methods>
<class-id>Net.MimeMessageBody</class-id> <category>parsing</category>

<body package="MIME" selector="parse:">parse: scanner</body>
</methods>

<methods>
<class-id>Net.MimeMessageBody</class-id> <category>mutating</category>

<body package="MIME" selector="newBody">newBody</body>
</methods>

<methods>
<class-id>Net.MimeMessageBody class</class-id> <category>instance creation</category>

<body package="MIME" selector="bodyTypeFor:">bodyTypeFor: aMimeEntity 

	^aMimeEntity isMultipart
		ifTrue: [MultipartBody]
		ifFalse: [SimpleBody]</body>

<body package="MIME" selector="newBodyFor:">newBodyFor: aMimeEntity 
	^(self bodyTypeFor: aMimeEntity) new
		parent: aMimeEntity;
		yourself</body>

<body package="MIME" selector="readFrom:parent:">readFrom: stream parent: aMimeEntity
	^(self newBodyFor: aMimeEntity)
		readFrom: stream;
		yourself</body>
</methods>

<methods>
<class-id>Net.MimeMessageBody class</class-id> <category>parsing</category>

<body package="MIME" selector="scannerType">scannerType
	^MimeScanner</body>
</methods>

<methods>
<class-id>Net.ValueWithParametersField</class-id> <category>accessing</category>

<body package="MIME" selector="canMerge">canMerge
	^true</body>

<body package="MIME" selector="defaultSeparators">defaultSeparators
	^self class defaultSeparators</body>

<body package="MIME" selector="element">element
	self first isNil
		ifTrue: [ self value add: self newValue ].
	^self first</body>

<body package="MIME" selector="elementParams">elementParams 
	^self element params</body>

<body package="MIME" selector="first">first
	^self value first</body>

<body package="MIME" selector="groupSeparator">groupSeparator
	^self separators at: 3</body>

<body package="MIME" selector="newValue">newValue
	^self valueWithParamsClass newWithSeparators: self separators</body>

<body package="MIME" selector="parametersSeparator">parametersSeparator
	^self separators at: 2</body>

<body package="MIME" selector="separators">separators
	separators isNil 
		ifTrue: [ separators := self defaultSeparators].
	^separators</body>

<body package="MIME" selector="separators:">separators: anArray
	separators := anArray</body>

<body package="MIME" selector="value">value
	^value isNil
		ifTrue: [ value := OrderedCollection new]
		ifFalse: [value]</body>

<body package="MIME" selector="valueSeparator">valueSeparator
	^self separators first</body>

<body package="MIME" selector="valueWithParamsClass">valueWithParamsClass
	^ValueWithParams</body>
</methods>

<methods>
<class-id>Net.ValueWithParametersField</class-id> <category>composing</category>

<body package="MIME" selector="writeValueOn:encoding:">writeValueOn: aStream encoding: aString
	self printValueOn: aStream</body>
</methods>

<methods>
<class-id>Net.ValueWithParametersField</class-id> <category>printing</category>

<body package="MIME" selector="printValueOn:">printValueOn: aStream 
	self value notNil
		ifTrue: 
			[self value  
				do: [ :param |  param printOn: aStream]
				separatedBy: [aStream nextPut: self groupSeparator ]]
		ifFalse: [ aStream nextPutAll: '' ].
	aStream cr.</body>
</methods>

<methods>
<class-id>Net.ValueWithParametersField</class-id> <category>adding</category>

<body package="MIME" selector="add:">add: aValueWithParametersFields
	self value addAll: aValueWithParametersFields value</body>
</methods>

<methods>
<class-id>Net.ValueWithParametersField</class-id> <category>parsing</category>

<body package="MIME" selector="doParse:">doParse: scanner
	| list newVal |
	list := OrderedCollection new: 5.
	[	scanner atEnd
	] whileFalse: [
		(newVal := self newValueFrom: scanner) readFrom: scanner.
		list add: newVal.
		(scanner atEnd not and: [self groupSeparator ~~ Character space]) ifTrue: [
			scanner skipWhiteSpace. 
			(scanner next == self groupSeparator) ifFalse: [
				InvalidHeaderField signalWith: scanner contents.
				scanner upTo: self groupSeparator]]].
	^list</body>

<body package="MIME" selector="newValueFrom:">newValueFrom: scanner
	^self valueWithParamsClass newWithSeparators: self separators</body>
</methods>

<methods>
<class-id>Net.ValueWithParametersField class</class-id> <category>defaults</category>

<body package="MIME" selector="defaultSeparators">defaultSeparators
	^self subclassResponsibility</body>
</methods>

<methods>
<class-id>Net.ValueWithParametersField class</class-id> <category>instance creation</category>

<body package="MIME" selector="newWithSeparators:">newWithSeparators: anArray
	^self new
		separators: anArray;
		yourself</body>
</methods>

<methods>
<class-id>Net.SingleValueWithParametersField</class-id> <category>accessing</category>

<body package="MIME" selector="canMerge">canMerge
	^false</body>

<body package="MIME" selector="localValue">localValue
	^self element value</body>

<body package="MIME" selector="localValue:">localValue: aString
	self element value: aString</body>

<body package="MIME" selector="parameterAt:">parameterAt: aName 

	^self element paramAt: aName</body>

<body package="MIME" selector="parameterAt:ifAbsent:">parameterAt: aName ifAbsent: aBlock

	^self element paramAt: aName ifAbsent: [aBlock value]</body>

<body package="MIME" selector="parameterAt:put:">parameterAt: aName put: aValue

	self element paramAt: aName put: aValue</body>

<body package="MIME" selector="value">value
	value isNil 
		ifTrue: [ value := OrderedCollection with: self newValue ].
	^value</body>
</methods>

<methods>
<class-id>Net.SingleValueWithParametersField</class-id> <category>composing</category>

<body package="MIME" selector="writeValueOn:encoding:">writeValueOn: aStream encoding: aString
" Default implementation is the same as inherited. Subclasses can override it "

	self value ~~ nil
		ifTrue: 
			[self value  
				do: [ :param |  param writeOn: aStream encoding: aString ]
				separatedBy: [aStream nextPut: self groupSeparator].
			aStream cr.].</body>
</methods>

<methods>
<class-id>Net.SingleValueWithParametersField</class-id> <category>parsing</category>

<body package="MIME" selector="parse:">parse: scanner

	value := self doParse: scanner.
	self validateValue: scanner.</body>

<body package="MIME" selector="validateValue:">validateValue: scanner
	value size &gt; 1 ifTrue: [ ^scanner notify: (#WrongParametersNumber &lt;&lt; #net &gt;&gt; 'Wrong parameters number').].</body>
</methods>

<methods>
<class-id>Net.VersionField</class-id> <category>accessing</category>

<body package="MIME" selector="majorVersion">majorVersion
	^(self parseVersion: self value) first</body>

<body package="MIME" selector="majorVersion:">majorVersion: numberOrString
	| arr |

	arr := self parseVersion: self value.
	arr at: 1 put: numberOrString.
	self versionStringFrom: arr.</body>

<body package="MIME" selector="minorVersion">minorVersion
	^(self parseVersion: self value) last</body>

<body package="MIME" selector="minorVersion:">minorVersion: numberOrString
	| arr |

	arr := self parseVersion: self value.
	arr at: 2 put: numberOrString.
	self versionStringFrom: arr.</body>

<body package="MIME" selector="version">version

	^self value</body>

<body package="MIME" selector="version:">version: string
	self value: string</body>

<body package="MIME" selector="versionStringFrom:">versionStringFrom: arr
	^(arr first printString), $., (arr last printString)</body>
</methods>

<methods>
<class-id>Net.VersionField</class-id> <category>parsing</category>

<body package="MIME" selector="parseVersion:">parseVersion: string
	| arr |

	(string isNil or: [ string isEmpty ]) ifTrue: [ ^Array new: 2 ].

	arr := string tokensBasedOn: $..
	arr size &lt; 2 
		ifTrue: [ ^self notify: (#VersionShouldBeSpecifiedAs &lt;&lt; #net &gt;&gt; 'Version should be specified as &lt;major version&gt;.&lt;minor version&gt;')].
	^arr</body>
</methods>

<methods>
<class-id>Net.VersionField class</class-id> <category>parsing</category>

<body package="MIME" selector="fieldNames">fieldNames
	^#('mime-version' 'http-version')</body>
</methods>

<methods>
<class-id>Net.NumericField</class-id> <category>accessing</category>

<body package="MIME" selector="asString">asString
	^self value printString</body>
</methods>

<methods>
<class-id>Net.NumericField</class-id> <category>parsing</category>

<body package="MIME" selector="doParse:">doParse: rfc822Stream 
" HeaderField fromLine: 'Content-length : 1024 (This is body length in bytes)' "
	rfc822Stream skipWhiteSpace.
	^Number readFrom: rfc822Stream asStream</body>
</methods>

<methods>
<class-id>Net.NumericField</class-id> <category>initialization</category>

<body package="MIME" selector="initialize">initialize
	super initialize.
	value := 0</body>
</methods>

<methods>
<class-id>Net.NumericField</class-id> <category>printing</category>

<body package="MIME" selector="printStructureOn:">printStructureOn: aStream
	aStream nextPutAll: self asString.
	self printCommentOn: aStream.
	aStream cr</body>
</methods>

<methods>
<class-id>Net.NumericField class</class-id> <category>parsing</category>

<body package="MIME" selector="fieldNames">fieldNames
	^#('content-length' )</body>
</methods>

<methods>
<class-id>Net.NumericField class</class-id> <category>instant creation</category>

<body package="MIME" selector="contentLength:">contentLength: aNumber
" Answer an instance of this field representing content length"

	^(self name: 'content-length') value: aNumber</body>
</methods>

<methods>
<class-id>Net.MimeEntity</class-id> <category>accessing</category>

<body package="MIME" selector="body">body

	^value isNil
		ifTrue: [ value := self newBody ]
		ifFalse: [ value ]</body>

<body package="MIME" selector="body:">body: aMessageBody
	value := aMessageBody.
	aMessageBody parent: self.</body>

<body package="MIME" selector="boundary">boundary
	^self contentTypeField boundary</body>

<body package="MIME" selector="boundary:">boundary: aString
	^self contentTypeField boundary: aString</body>

<body package="MIME" selector="charset">charset
	^self charsetIfNone: 
			[ self parent ~~ nil
				ifTrue: [	self parent defaultCharset]
				ifFalse: [ self defaultCharset] ]</body>

<body package="MIME" selector="charset:">charset: aName
	self isSimple
		ifTrue: [ (self getFieldAt: 'content-type') charset: aName ]</body>

<body package="MIME" selector="charsetIfNone:">charsetIfNone: aBlock
	^(self fieldAt: 'content-type' ifAbsent: [ ^aBlock value ]) charsetIfNone: aBlock</body>

<body package="MIME" selector="contentEncoding">contentEncoding
	"Http header. The message can be sent from HttpBuildHandler while building multipart message"
	^nil</body>

<body package="MIME" selector="contentId">contentId
	^self fieldValueAt: 'content-id'</body>

<body package="MIME" selector="contentId:">contentId: aString
	^self fieldValueAt: 'content-id' put: aString</body>

<body package="MIME" selector="contentLength">contentLength
	^self fieldValueAt: 'content-length'</body>

<body package="MIME" selector="contentLength:">contentLength: anInteger
	^self fieldValueAt: 'content-length' put: anInteger</body>

<body package="MIME" selector="contentsSpecies">contentsSpecies
" Answer ByteArray if I contain bytes, String if I containg character info. Note
that if content transfer encoding is present, it is always String. See also contentTypeSpecies "

	^self isBinary
		ifTrue: [ ByteArray ]
		ifFalse: [ String ]</body>

<body package="MIME" selector="contentTransferEncoding">contentTransferEncoding
	^self fieldValueAt: 'content-transfer-encoding'</body>

<body package="MIME" selector="contentTransferEncoding:">contentTransferEncoding: encodingNameString
	^self fieldValueAt: 'content-transfer-encoding'  put: encodingNameString</body>

<body package="MIME" selector="contentType">contentType
	^(self fieldAt: 'content-type' ifAbsent: [ self defaultContentTypeField  ]) contentType</body>

<body package="MIME" selector="contentType:">contentType: aString
	^('text/plain' match: aString)
		ifTrue: [self  contentType: aString charset: self defaultCharset ]
		ifFalse: [self fieldAt: 'content-type' put: (ContentTypeField contentType: aString)]</body>

<body package="MIME" selector="contentType:charset:">contentType: aString charset: cString
	self fieldAt: 'content-type' put: (ContentTypeField contentType: aString charset: cString)</body>

<body package="MIME" selector="contentTypeEncoding">contentTypeEncoding
	" Answer symbolic name of the character encoding for the decoded body of this entity "

	^self contentTypeEncodingType</body>

<body package="MIME" selector="contentTypeEncodingType">contentTypeEncodingType
" Answer symbol representing character encoding that we use to decode the body of this entity. This 
encoding is entirely determined by the current content type field as follows:
	If content type is text or multipart, then value of charset parameter is used
		content-type: text/plain; charset = iso-8859-1
	If no charset is present, default charset encoding is used
	If content type is binary, binary encoding is used
"

	^self isBinaryContentType
		ifTrue: [ #binary ]
		ifFalse: [self charset notNil
					ifTrue: [self charset]
					ifFalse: [#default]]</body>

<body package="MIME" selector="contentTypeField">contentTypeField
	^(self fieldAt: 'content-type' ifAbsent: [ self defaultContentTypeField ])</body>

<body package="MIME" selector="contentTypeSpecies">contentTypeSpecies
" Answer ByteArray if my Mime type is binary, String otherwise. See also contentsSpecies "

	^self isBinaryContentType
		ifTrue: [ ByteArray ]
		ifFalse: [ String ]</body>

<body package="MIME" selector="encoding">encoding
	" Answer symbolic name of the character encoding for the body of this entity "

	^self encodingType ~~ nil
		ifTrue: [ self encodingType ]
		ifFalse: [ MimeTypeDescriptor encodingForCharset: #default ]</body>

<body package="MIME" selector="encodingType">encodingType
" Answer symbol representing character encoding that we use to read the body of this entity. This 
encoding depends on the current content type and content transfer encoding.
If transfer encoding is not present, then encoding is determined by content-type
field as follows:
	If content type is text or multipart, then value of charset parameter is used
		content-type: text/plain; charset = iso-8859-1
	If no charset is present, default charset encoding is used
	If content type is binary, binary encoding is used
If transfer encoding is present, default charset encoding is used
"

	^self isBinary
		ifTrue: [ #binary ]
		ifFalse: [self charsetIfNone: [#default]]</body>

<body package="MIME" selector="fields">fields

	^self header fields</body>

<body package="MIME" selector="fields:">fields: aValue
	self header fields: aValue</body>

<body package="MIME" selector="headerCharset">headerCharset
"	headerCharset == nil
		ifTrue: 
			[headerCharset := 
				( self charset ~~ nil
					ifTrue: [ self charset ]
					ifFalse: 
						[self parent ~~ nil
							ifTrue: [ self parent headerCharset ]
							ifFalse: [ MimeTypeDescriptor defaultCharacterSet ]])].
"
	^headerCharset</body>

<body package="MIME" selector="headerCharset:">headerCharset: aString
	headerCharset := aString</body>

<body package="MIME" selector="majorType">majorType
	^self contentTypeField subtype</body>

<body package="MIME" selector="mimeType">mimeType
	^MimeTypeDescriptor guessFromContentType: self contentType</body>

<body package="MIME" selector="mimeVersion">mimeVersion
	^self fieldValueAt: 'mime-version'</body>

<body package="MIME" selector="mimeVersion:">mimeVersion: aString
	^self fieldValueAt: 'mime-version' put: aString</body>

<body package="MIME" selector="subtype">subtype
	^self contentTypeField subtype</body>

<body package="MIME" selector="type">type
	^self contentTypeField type</body>

<body package="MIME" selector="type:">type: aString
	^self contentTypeField type: aString</body>
</methods>

<methods>
<class-id>Net.MimeEntity</class-id> <category>content-disposition</category>

<body package="MIME" selector="contentDisposition">contentDisposition
	^self fieldAt: 'content-disposition' ifAbsent: [nil]</body>

<body package="MIME" selector="contentDisposition:">contentDisposition: aString
" aString should be 'inline' or 'attachment' "
	^(self getFieldAt: 'content-disposition') disposition: aString</body>

<body package="MIME" selector="contentDispositionSize">contentDispositionSize
	^(self fieldAt: 'content-disposition' ifAbsent: [^nil]) size</body>

<body package="MIME" selector="contentDispositionSize:">contentDispositionSize: anInteger
	^(self getFieldAt: 'content-disposition') size: anInteger</body>

<body package="MIME" selector="fileName">fileName
	| headerField f |
	headerField := self 
					fieldAt: 'content-disposition' 
					ifAbsent: [self fieldAt: 'content-type' ifAbsent:[^nil]].
	^((f := headerField fileName) isNil or: [ f isEmpty])
		ifTrue: [ nil ]
		ifFalse: [ f ]</body>

<body package="MIME" selector="fileName:">fileName: aString

	(self getFieldAt: 'content-type')  fileName: aString. "For eudora"
	^(self getFieldAt: 'content-disposition') fileName: aString</body>
</methods>

<methods>
<class-id>Net.MimeEntity</class-id> <category>testing</category>

<body package="MIME" selector="canBeDecoded">canBeDecoded
	^self contentTransferEncoding isNil</body>

<body package="MIME" selector="hasBoundary">hasBoundary
	^self boundary notNil</body>

<body package="MIME" selector="hasContentTransferEncoding">hasContentTransferEncoding
	^self contentTransferEncoding notNil</body>

<body package="MIME" selector="isApplication">isApplication

	^ self type match: 'application'</body>

<body package="MIME" selector="isAttachment">isAttachment
	| f |
	^( f := self contentDisposition) notNil and: [ f isAttachment ]</body>

<body package="MIME" selector="isBase64Type">isBase64Type
	| ce |
	^(ce := self contentTransferEncoding) notNil
		ifTrue: [ ce match: 'base64']
		ifFalse: [ false ]</body>

<body package="MIME" selector="isBinary">isBinary
" Answers true if my contents are represented as text or binary. If content
transfer encoding is present, my contents are text even though this text
encodes binary information "

	^self isBinaryContentType and: [self hasContentTransferEncoding not]</body>

<body package="MIME" selector="isBinaryContentType">isBinaryContentType
	^self mimeType isBinary</body>

<body package="MIME" selector="isChunked">isChunked

	^false</body>

<body package="MIME" selector="isContentEncodingGZip">isContentEncodingGZip
	| ce |
	^((ce := self contentEncoding) notNil and: ['gzip' match: ce ])</body>

<body package="MIME" selector="isFileAttachment">isFileAttachment
	^self fileName notNil</body>

<body package="MIME" selector="isFormData">isFormData
	| f |
	^( f := self contentDisposition) notNil and: [ f isFormData ]</body>

<body package="MIME" selector="isHtmlText">isHtmlText

	^ self isTextContentType and: [self subtype = 'html' ]</body>

<body package="MIME" selector="isHttpEntity">isHttpEntity
	^false</body>

<body package="MIME" selector="isInline">isInline
	| f |
	^(f := self contentDisposition) notNil and: [ f  isInline ]</body>

<body package="MIME" selector="isMimeEntity">isMimeEntity
	^true</body>

<body package="MIME" selector="isMultipart">isMultipart
	^self contentTypeField isMultipart</body>

<body package="MIME" selector="isMultipartAlternative">isMultipartAlternative

	^self contentType match: 'multipart/alternative'</body>

<body package="MIME" selector="isMultipartMixed">isMultipartMixed

	^self contentType match: 'multipart/mixed'</body>

<body package="MIME" selector="isPlainText">isPlainText

	^ self isTextContentType and: [ self subtype = 'plain' ]</body>

<body package="MIME" selector="isPrintedQuotableType">isPrintedQuotableType
	| ce |
	^( ce := self contentTransferEncoding) notNil
		ifTrue: [ ce match: 'quoted-printable' ]
		ifFalse: [ false ]</body>

<body package="MIME" selector="isSimple">isSimple
	^self isMultipart not</body>

<body package="MIME" selector="isText">isText
" Answers true is message body contains binary data. This method will
always answer true in the presence of content transfer encoding. See also
isTextContentType "

	^ self isBinary not</body>

<body package="MIME" selector="isTextContentType">isTextContentType
" Answers true is message content type corresponds to binary data. See also
isText "

	^ self mimeType isText</body>

<body package="MIME" selector="needsBoundary">needsBoundary
	^self isMultipart and: [self boundary isNil]</body>

<body package="MIME" selector="needsContentTransferEncoding">needsContentTransferEncoding

	^self hasContentTransferEncoding not and: [self body needsContentTransferEncoding]</body>
</methods>

<methods>
<class-id>Net.MimeEntity</class-id> <category>copying</category>

<body package="MIME" selector="postCopy">postCopy
	super postCopy.
	header := header copy parent: self.
	value := value copy parent: self.</body>
</methods>

<methods>
<class-id>Net.MimeEntity</class-id> <category>mutating</category>

<body package="MIME" selector="replaceFrom:">replaceFrom: replacementPart
" Replace your guts from another entity. Copy all the fields into yourself
replacing your own fields if same name. Replace your own body with
the replacement entity's body "

	" Merge fields from the replacement part into the message "
	replacementPart fields do: [ :field | self fieldAt: field name put: field ].
	self body: replacementPart body</body>

<body package="MIME" selector="replacePart:with:">replacePart: originalPart with: replacementPart
	| index |

	index := self parts indexOf: originalPart.
	index = 0 ifTrue: [ ^self error: (#OriginalPartNotFound &lt;&lt; #net &gt;&gt; 'Original part not found') ].
	self partAt: index put: replacementPart</body>
</methods>

<methods>
<class-id>Net.MimeEntity</class-id> <category>transfer encodings</category>

<body package="MIME" selector="addContentTransferEncoding:">addContentTransferEncoding: encodingNameString
" Transfer-encode message. encodingNameString is one of: 'base64', 'quoted-printable', ... "
	self isSimple ifFalse: [^self ].
	self contentTransferEncoding: encodingNameString.
	self newWriter encodeBodySource: self body as: encodingNameString.</body>

<body package="MIME" selector="removeContentTransferEncoding">removeContentTransferEncoding
	"Transfer-encode message. encodingNameString is one of: 'base64', 'quoted-printable', ..."

	self hasContentTransferEncoding
		ifTrue: [self body removeContentTransferEncoding]</body>

<body package="MIME" selector="transferFieldOrder">transferFieldOrder
" Answer fields that should appear in specific order during transfer "
	^OrderedCollection new</body>
</methods>

<methods>
<class-id>Net.MimeEntity</class-id> <category>defaults</category>

<body package="MIME" selector="defaultBot">defaultBot
	^self defaultBotClass new</body>

<body package="MIME" selector="defaultBotClass">defaultBotClass
	^MimeEntityBot</body>

<body package="MIME" selector="defaultBuilderType">defaultBuilderType

	^self class builderClass</body>

<body package="MIME" selector="defaultCharset">defaultCharset

	^self class defaultCharset</body>

<body package="MIME" selector="defaultContentDispositionField">defaultContentDispositionField
	^ContentDispositionField default</body>

<body package="MIME" selector="defaultContentType">defaultContentType
" Answer default content type string. Use Mime rules, subclasses may override "

	^self parent notNil
		ifTrue: [ self parent defaultContentTypeForParts ]
		ifFalse: [ MimeTypeDescriptor defaultContentTypeString ]</body>

<body package="MIME" selector="defaultContentTypeField">defaultContentTypeField
" Answer default content-type field "
	^ContentTypeField contentType: self defaultContentType charset: self defaultCharset</body>

<body package="MIME" selector="defaultContentTypeForParts">defaultContentTypeForParts
" Answer default content-type field for body parts in multipart message. This method uses Mime rules. It can be overridden, i.e. for HTTP. Mime specifies that the default content-type is text/plain; charset = 'us-ascii' except in multipart/digest entities where the default content-type is message/rfc822 "
	^self contentType ~= MimeTypeDescriptor contentTypeMultipartDigest
		ifTrue: [ MimeTypeDescriptor defaultContentTypeString ]
		ifFalse: [ MimeTypeDescriptor contentTypeMessageRfc822 ]</body>

<body package="MIME" selector="defaultPrinterType">defaultPrinterType

	^self class printerClass</body>

<body package="MIME" selector="defaultTransferEncodingName">defaultTransferEncodingName
" Answer default transfer encoding for this entity. Somewhat arbitrarily we use quoted-printable
for text entities (which assumes we have largely ASCII-based text, saving space), and base64
for everything else (because it is most likely binary data, so base64 is more appropriate "

	^self isTextContentType
		ifTrue: [ 'quoted-printable' ]
		ifFalse: [ 'base64' ]</body>

<body package="MIME" selector="defaultWriterType">defaultWriterType

	^self class writerClass</body>
</methods>

<methods>
<class-id>Net.MimeEntity</class-id> <category>visiting</category>

<body package="MIME" selector="acceptVisit:with:">acceptVisit: aVisitor with: argument
	^aVisitor acceptMimeEntity: self with: argument</body>
</methods>

<methods>
<class-id>Net.MimeEntity</class-id> <category>printing</category>

<body package="MIME" selector="displayString">displayString
"For presenter to display something useful in place of the entity contents.
For text entity this is simply entity contents, but for binary entities only a short
digest is shown"

	| stream |
	stream := (String new: 128) writeStream.
	self printForDisplayOn: stream.
	^stream contents</body>

<body package="MIME" selector="newPrinter">newPrinter

	^self class newPrinter</body>

<body package="MIME" selector="printForDisplayOn:">printForDisplayOn: stream
"For presenter to display something useful in place of the entity contents.
For text entity this is simply entity contents, but for binary entities only a short
digest is shown"

	self body printForDisplayOn: stream</body>

<body package="MIME" selector="printHeaderOn:">printHeaderOn: aStream

	self newPrinter printMessageElement: self header on: aStream</body>

<body package="MIME" selector="printOn:indent:">printOn: aStream indent: level

	self newPrinter printMessage: self on: aStream indent: level.</body>
</methods>

<methods>
<class-id>Net.MimeEntity</class-id> <category>accessing fields and body parts</category>

<body package="MIME" selector="fieldValueAt:ifAbsent:">fieldValueAt: fieldName ifAbsent: aBlock

	^(self fieldAt: fieldName ifAbsent: [ ^aBlock value ]) value</body>

<body package="MIME" selector="getFieldAt:">getFieldAt: aString
" Make sure we have either field or its stub so we can put real value in it. Default implementation
of #default in HeaderField just creates a new instance of the field. Some subclasses override
this method to provide more specific stub creation "

	^self header 
		at: aString 
		ifAbsentPut: [(HeaderField fieldClassForName: aString) default name: aString].</body>
</methods>

<methods>
<class-id>Net.MimeEntity</class-id> <category>accessing fields</category>

<body package="MIME" selector="addField:">addField: field
	" This method will check if the field exists already; if yes, if it can be merged into the existing field and, if yes, merge it. Otherwise, add as a new field "

	self header addField: field.</body>

<body package="MIME" selector="arrangeFieldsInOrder:">arrangeFieldsInOrder: anArrayOfFieldNames
	self header arrangeFieldsInOrder: anArrayOfFieldNames</body>

<body package="MIME" selector="fieldAt:">fieldAt: aString
	^self header at: aString</body>

<body package="MIME" selector="fieldAt:ifAbsent:">fieldAt: aString ifAbsent: aNiladicBlock
	^self header at: aString ifAbsent: aNiladicBlock</body>

<body package="MIME" selector="fieldAt:ifAbsentPut:">fieldAt: aString ifAbsentPut: aNiladicBlock 
	^self header at: aString ifAbsentPut: aNiladicBlock</body>

<body package="MIME" selector="fieldAt:put:">fieldAt: aString put: field
	^self header at: aString put: field</body>

<body package="MIME" selector="fieldsAt:">fieldsAt: aString
" Answer all fields with specified names in their order of appearance "
	^self header allAt: aString</body>

<body package="MIME" selector="fieldValueAt:">fieldValueAt: fieldName
" Answer value of specified field or nil "
	^self fieldValueAt: fieldName ifAbsent: [ nil ]</body>

<body package="MIME" selector="fieldValueAt:put:">fieldValueAt: fieldName put: aValue
" Ensures at least field's placeholder exists and sets field's value "
	^(self getFieldAt: fieldName ) value: aValue</body>

<body package="MIME" selector="fieldValuesAt:">fieldValuesAt: aString
" Answer all fields with specified names in their order of appearance "
	^(self fieldsAt: aString)
		inject: OrderedCollection new into: [ :list :field | list addAll: field values. list ]</body>

<body package="MIME" selector="getValueFor:">getValueFor: fieldName
	^self getValueFor: fieldName ifAbsent: [ nil ]</body>

<body package="MIME" selector="getValueFor:ifAbsent:">getValueFor: fieldName ifAbsent: aBlock
" For compatibility only "
	^self fieldValueAt: fieldName ifAbsent: [ ^aBlock value ]</body>

<body package="MIME" selector="header">header

	^header isNil
		ifTrue: [ header := self newHeader ]
		ifFalse: [ header ]</body>

<body package="MIME" selector="header:">header: aMessageHeader

	header := aMessageHeader.
	header parent: self</body>

<body package="MIME" selector="indexOfField:">indexOfField: aField
	^self header indexOf: aField</body>

<body package="MIME" selector="indexOfFieldNamed:">indexOfFieldNamed: aFieldName
	^self header indexOf: (self fieldAt: aFieldName ifAbsent: [^0])</body>

<body package="MIME" selector="newHeader">newHeader
	^MessageHeader newFor: self</body>

<body package="MIME" selector="removeField:">removeField: aHeaderField
" Remove header field "

	^self header remove: aHeaderField</body>

<body package="MIME" selector="removeFieldAt:">removeFieldAt: fieldName
" Remove field with specified name "

	^self header removeKey: fieldName</body>
</methods>

<methods>
<class-id>Net.MimeEntity</class-id> <category>accessing body parts</category>

<body package="MIME" selector="addPart:">addPart: part
	part parent: self.
	self body addPart: part.</body>

<body package="MIME" selector="contents">contents
	^self body value</body>

<body package="MIME" selector="contents:">contents: aString
	^self body value: aString</body>

<body package="MIME" selector="contentsStream">contentsStream

	^self body byteSource notNil 
		ifTrue:  [self decodedByteContentsStream]
		ifFalse: [ self body valueAsStream ]</body>

<body package="MIME" selector="decodedContents">decodedContents
	^self body decodedValue</body>

<body package="MIME" selector="decodedContentsWith:">decodedContentsWith: aSymbol
	^self body decodedValueWith: aSymbol</body>

<body package="MIME" selector="indexOfPart:">indexOfPart: aPart
	^self parts indexOf: aPart</body>

<body package="MIME" selector="newBody">newBody
	^MimeMessageBody newBodyFor: self</body>

<body package="MIME" selector="partAt:">partAt: index
	^self parts at: index</body>

<body package="MIME" selector="partAt:put:">partAt: index put: aPart
	self parts at: index put: aPart</body>

<body package="MIME" selector="parts">parts
	^ self body parts</body>

<body package="MIME" selector="removePart:">removePart: aMimeEntity
	^self body removePart: aMimeEntity</body>

<body package="MIME" selector="removePartAt:">removePartAt: index
	^self body removePartAt: index</body>
</methods>

<methods>
<class-id>Net.MimeEntity</class-id> <category>private</category>

<body package="MIME" selector="bot">bot
	^self defaultBot</body>

<body package="MIME" selector="decodedByteContentsStream">decodedByteContentsStream
	^self body getValueStreamFromByteSource</body>
</methods>

<methods>
<class-id>Net.MimeEntity</class-id> <category>deprecated</category>

<body package="MIME" selector="builder">builder

	^self newBuilder</body>

<body package="MIME" selector="parseBody:">parseBody: rfc822Scanner 
	^self newBuilder parseMessageBody: self from: rfc822Scanner</body>

<body package="MIME" selector="printer">printer

	^self newPrinter</body>

<body package="MIME" selector="readBodyFrom:">readBodyFrom: aStream
" Each message element has responsibility to read itself from input stream. Reading usually involves parsing, so implementations of this method typically create an instance of lexical scanner and invoke a parser (see explanation for parse: method) "

	self source: aStream.
	^self newBuilder readBodyFor: self  from: aStream</body>

<body package="MIME" selector="scannerOn:">scannerOn: aStream

	^self error: #ImplementedByEntityBuilder &lt;&lt; #net &gt;&gt; 'Implemented by  the entity builder'</body>
</methods>

<methods>
<class-id>Net.MimeEntity</class-id> <category>parsing</category>

<body package="MIME" selector="newBuilder">newBuilder

	^self class newBuilder</body>

<body package="MIME" selector="parse">parse

	^self newBuilder read: self  from: self source</body>

<body package="MIME" selector="readFrom:">readFrom: aStream

	self source: aStream.
	^self newBuilder read: self  from: aStream</body>
</methods>

<methods>
<class-id>Net.MimeEntity</class-id> <category>attachments</category>

<body package="MIME" selector="addFileAttachment:">addFileAttachment: fileNameOrString
	^self bot addFileAttachmentFor: self fileName: fileNameOrString</body>

<body package="MIME" selector="addFileAttachment:withEncoding:">addFileAttachment: fileNameOrString withEncoding: aSymbol
	^self bot addFileAttachmentFor: self fileName: fileNameOrString withEncoding: aSymbol</body>

<body package="MIME" selector="attachmentAt:">attachmentAt: index
	^(self bot attachmentsFor: self) at: index</body>

<body package="MIME" selector="attachmentFiles">attachmentFiles
	^self attachments select: [:each | each fileName ~~ nil ]</body>

<body package="MIME" selector="attachmentFilesAsDictionary">attachmentFilesAsDictionary
	| dict |
	dict := Dictionary new.
	self attachments 
		do: [:each | 
			each fileName ~~ nil
				ifTrue: [ dict at: each fileName put: each value value ]].
	^dict</body>

<body package="MIME" selector="attachmentNames">attachmentNames
	"Return a list of attachments file names  (if any)"
	| list fn  |
	list := OrderedCollection new.
	self attachments do: [:ea | (fn := ea fileName) notNil ifTrue: [ list add: fn]].
	^list</body>

<body package="MIME" selector="attachments">attachments
	^self bot attachmentsFor: self</body>
</methods>

<methods>
<class-id>Net.MimeEntity</class-id> <category>utility</category>

<body package="MIME" selector="saveAttachment:on:">saveAttachment: aMimeEntity on: aStream
	^self bot writeContentsOf: aMimeEntity  on: aStream</body>

<body package="MIME" selector="saveAttachmentAt:on:">saveAttachmentAt: anInteger on: aStream
	^self saveAttachment: (self attachmentAt: anInteger)  on: aStream</body>
</methods>

<methods>
<class-id>Net.MimeEntity</class-id> <category>writing</category>

<body package="MIME" selector="newWriter">newWriter

	^self class newWriter</body>

<body package="MIME" selector="writeContentsOn:">writeContentsOn: anEncodedStream
	^self body writeContentsOn: anEncodedStream</body>

<body package="MIME" selector="writeOn:">writeOn: aStream
	self newWriter writeMessage: self on: aStream</body>
</methods>

<methods>
<class-id>Net.MimeEntity</class-id> <category>mime version</category>

<body package="MIME" selector="addMimeVersion">addMimeVersion</body>
</methods>

<methods>
<class-id>Net.MimeEntity class</class-id> <category>instance creation</category>

<body package="MIME" selector="fileName:">fileName: fileNameOrString
	^self fileName: fileNameOrString withEncoding: SimpleBody defaultCharsetEncoder</body>

<body package="MIME" selector="fileName:withEncoding:">fileName: fileNameOrString withEncoding: aSymbol
	| stream entity filename |
	entity := MimeEntity new. 
	filename := fileNameOrString asFilename.
	entity 
		fileName: filename tail;
		contentType: (self mimeTypeForFile: filename).
	[entity isBinary
		ifTrue: 
			[stream := ( filename withEncoding: #binary) readStream.
			entity body: (SimpleBody new byteSource: stream).] 
		ifFalse: 
			[entity charset: aSymbol.
			stream := (filename withEncoding: aSymbol ) readStream. 
			entity body: (SimpleBody new source: stream)].

	] ensure: [ stream notNil ifTrue: [ stream close]].
	^entity</body>

<body package="MIME" selector="mimeTypeForFile:">mimeTypeForFile: fileName
	^MimeTypeDescriptor guessFromExtension: fileName asFilename extension</body>

<body package="MIME" selector="newTextHTML">newTextHTML
	" self newTextHTML "
	^self new
		contentType: MimeTypeDescriptor contentTypeHtml; 
		charset: self defaultCharset;
		yourself.</body>

<body package="MIME" selector="newTextPlain">newTextPlain
	" self newTextPlain "
	^self new
		contentType: MimeTypeDescriptor contentTypePlainText; 
		charset: self  defaultCharset;
		yourself.</body>

<body package="MIME" selector="source:">source: aStream
" Construct new simple Mime entity containing specified stream
Example:
	self stream: 'ftp.st' asFilename readStream lineEndTransparent "

	| entity |
	entity := self new. 
	(aStream respondsTo: #fileName) 
		ifTrue: 
			[entity fileName: aStream fileName tail;
					contentType: (self mimeTypeForFile: aStream fileName).
			(entity isBinary and: [aStream isCharacterStream])
				ifTrue: [ EntityException raiseRequestErrorString: (#MimeEntityExpectsBinaryStream &lt;&lt; #net &gt;&gt; 'The Mime entity expects the stream in binary mode. The assumption is based on the file name extension') ]].
	(aStream isKindOf: EncodedStream)
		ifTrue: [ entity charset: aStream encoding].

	^entity
		body: (SimpleBody new source: aStream);
		yourself</body>
</methods>

<methods>
<class-id>Net.MimeEntity class</class-id> <category>parsing</category>

<body package="MIME" selector="parse:">parse: rfc822Scanner 
	^self newBuilder parse: rfc822Scanner</body>

<body package="MIME" selector="readFrom:">readFrom: aStream
	^self newBuilder readFrom: aStream</body>

<body package="MIME" selector="readHeaderFrom:">readHeaderFrom: stream
	^self newBuilder readHeaderFrom: stream</body>

<body package="MIME" selector="scannerType">scannerType
	^MimeScanner</body>
</methods>

<methods>
<class-id>Net.MimeEntity class</class-id> <category>documentation</category>

<body package="MIME" selector="syntaxOfMultiPartMimeBodies">syntaxOfMultiPartMimeBodies
" From RFC 2046: Media Types                  November 1996

   The Content-Type field for multipart entities requires one parameter,
   'boundary'. The boundary delimiter line is then defined as a line
   consisting entirely of two hyphen characters ($-, decimal value 45)
   followed by the boundary parameter value from the Content-Type header
   field, optional linear whitespace, and a terminating CRLF.

   WARNING TO IMPLEMENTORS:  The grammar for parameters on the Content-
   type field is such that it is often necessary to enclose the boundary
   parameter values in quotes on the Content-type line.  This is not
   always necessary, but never hurts. Implementors should be sure to
   study the grammar carefully in order to avoid producing invalid
   Content-type fields.  Thus, a typical 'multipart' Content-Type header
   field might look like this:

     Content-Type: multipart/mixed; boundary=gc0p4Jq0M2Yt08j34c0p

   But the following is not valid:

     Content-Type: multipart/mixed; boundary=gc0pJq0M:08jU534c0p

   (because of the colon) and must instead be represented as

     Content-Type: multipart/mixed; boundary=""gc0pJq0M:08jU534c0p""

   This Content-Type value indicates that the content consists of one or
   more parts, each with a structure that is syntactically identical to
   an RFC 822 message, except that the header area is allowed to be
   completely empty, and that the parts are each preceded by the line

     --gc0pJq0M:08jU534c0p

   The boundary delimiter MUST occur at the beginning of a line, i.e.,
   following a CRLF, and the initial CRLF is considered to be attached
   to the boundary delimiter line rather than part of the preceding
   part.  The boundary may be followed by zero or more characters of
   linear whitespace. It is then terminated by either another CRLF and
   the header fields for the next part, or by two CRLFs, in which case
   there are no header fields for the next part.  If no Content-Type
   field is present it is assumed to be 'message/rfc822' in a
   'multipart/digest' and 'text/plain' otherwise.

   NOTE:  The CRLF preceding the boundary delimiter line is conceptually
   attached to the boundary so that it is possible to have a part that
   does not end with a CRLF (line  break).  Body parts that must be
   considered to end with line breaks, therefore, must have two CRLFs
   preceding the boundary delimiter line, the first of which is part of
   the preceding body part, and the second of which is part of the
   encapsulation boundary.
"</body>
</methods>

<methods>
<class-id>Net.MimeEntity class</class-id> <category>defaults</category>

<body package="MIME" selector="defaultBuilderType">defaultBuilderType
	^MimeBuildHandler</body>

<body package="MIME" selector="defaultCharset">defaultCharset

	^MimeTypeDescriptor iso8859x1</body>

<body package="MIME" selector="defaultPrinterType">defaultPrinterType
	^MimePrintHandler</body>

<body package="MIME" selector="defaultWriterType">defaultWriterType
	^MimeWriteHandler</body>
</methods>

<methods>
<class-id>Net.MimeEntity class</class-id> <category>accessing</category>

<body package="MIME" selector="builderClass">builderClass

	^builderClass ifNil: [self defaultBuilderType]</body>

<body package="MIME" selector="newBuilder">newBuilder

	^self builderClass new</body>

<body package="MIME" selector="newPrinter">newPrinter

	^self printerClass new</body>

<body package="MIME" selector="newWriter">newWriter

	^self writerClass new</body>

<body package="MIME" selector="printerClass">printerClass

	^printerClass ifNil: [self defaultPrinterType]</body>

<body package="MIME" selector="writerClass">writerClass

	^writerClass ifNil: [self defaultWriterType]</body>
</methods>

<methods>
<class-id>Net.MimeEntity class</class-id> <category>deprecated</category>

<body package="MIME" selector="builder">builder
"Use newBuilder instead."
	^self newBuilder</body>

<body package="MIME" selector="printer">printer
"Use newPrinter instead."
	^self newPrinter</body>
</methods>

<methods>
<class-id>Net.RFC822Message</class-id> <category>accessing</category>

<body package="MIME" selector="bcc">bcc
	^self getValueFor: 'bcc' ifAbsent: [ OrderedCollection new ]</body>

<body package="MIME" selector="bcc:">bcc: aStringOrList
	^self setAddressesAt: 'bcc' to: aStringOrList</body>

<body package="MIME" selector="bccSendStrings">bccSendStrings
	^self encodedStringAt: 'bcc'</body>

<body package="MIME" selector="cc">cc
	^self getValueFor: 'cc' ifAbsent: [ OrderedCollection new ]</body>

<body package="MIME" selector="cc:">cc: aStringOrList
	^self setAddressesAt: 'cc' to: aStringOrList</body>

<body package="MIME" selector="ccSendStrings">ccSendStrings
	^self encodedStringAt: 'cc'</body>

<body package="MIME" selector="comments">comments
	^self getValueFor: 'comments'</body>

<body package="MIME" selector="date">date
	^self fieldValueAt: 'date'</body>

<body package="MIME" selector="date:">date: aTimestampOrString
	^(self getFieldAt: 'date') value: aTimestampOrString</body>

<body package="MIME" selector="dateSendString">dateSendString
	^self encodedStringAt: 'cc'</body>

<body package="MIME" selector="destinationAddresses">destinationAddresses
" Answer collection of destination addresses. These addresses have been preprocessed
to make them good for SMTP transport. This means removing everything but address-spec and 
encoding alias based on the message charset "
	| coll f |
	coll := OrderedCollection new.
	self destinationFields 
		do: [ :fn |  
			(f := self fieldAt: fn ifAbsent: [nil]) ~~ nil
				ifTrue: [ coll addAll: (f addressesEncodedWith: self headerCharset)]].
	^coll</body>

<body package="MIME" selector="destinationFields">destinationFields
" Answer collection of fields that contain message destination addresses "

	^#('to' 'cc' 'bcc')</body>

<body package="MIME" selector="encrypted">encrypted
	^self getValueFor: 'encrypted'</body>

<body package="MIME" selector="from">from
	^self getValueFor: 'from' ifAbsent: [ OrderedCollection new ]</body>

<body package="MIME" selector="from:">from: aStringOrList
	^self setAddressesAt: 'from' to: aStringOrList</body>

<body package="MIME" selector="fromSendStrings">fromSendStrings
	^self encodedStringAt: 'from'</body>

<body package="MIME" selector="id">id
	^self fieldValueAt: 'message-id'</body>

<body package="MIME" selector="id:">id: aString
	^(self getFieldAt: 'message-id') value: aString</body>

<body package="MIME" selector="inReplyTo">inReplyTo
	^self getValueFor: 'in-reply-to' ifAbsent: [ OrderedCollection new ]</body>

<body package="MIME" selector="inReplyTo:">inReplyTo: aStringOrList
	^self setAddressesAt: 'in-reply-to' to: aStringOrList</body>

<body package="MIME" selector="inReplyToSendString">inReplyToSendString
	^self encodedStringAt: 'in-reply-to'</body>

<body package="MIME" selector="keywords">keywords
	^self getValueFor:  'keywords'</body>

<body package="MIME" selector="messageID">messageID
	^self getValueFor: 'message-id'</body>

<body package="MIME" selector="reference">reference
	^self getValueFor: 'reference'</body>

<body package="MIME" selector="replyTo">replyTo
	^self getValueFor: 'reply-to' ifAbsent: [ OrderedCollection new ]</body>

<body package="MIME" selector="replyTo:">replyTo: aStringOrList
	^self setAddressesAt: 'reply-to' to: aStringOrList</body>

<body package="MIME" selector="replyToSendStrings">replyToSendStrings
	^self encodedStringAt: 'reply-to'</body>

<body package="MIME" selector="resentBcc">resentBcc
	^self getValueFor: 'resent-bcc' ifAbsent: [ OrderedCollection new ]</body>

<body package="MIME" selector="resentBcc:">resentBcc: aStringOrList
	^self setAddressesAt: 'resent-bcc' to: aStringOrList</body>

<body package="MIME" selector="resentBccSendStrings">resentBccSendStrings
	^self encodedStringAt: 'resent-bcc'</body>

<body package="MIME" selector="resentCc">resentCc
	^self getValueFor: 'resent-cc' ifAbsent: [ OrderedCollection new ]</body>

<body package="MIME" selector="resentCc:">resentCc: aStringOrList
	^self setAddressesAt: 'resent-cc' to: aStringOrList</body>

<body package="MIME" selector="resentCcSendStrings">resentCcSendStrings
	^self encodedStringAt: 'resent-cc'</body>

<body package="MIME" selector="resentDate">resentDate
	^self getValueFor: 'resent-date'</body>

<body package="MIME" selector="resentFrom">resentFrom
	^self getValueFor: 'resent-from'</body>

<body package="MIME" selector="resentFrom:">resentFrom: aStringOrList
	^self setAddressesAt: 'resent-from' to: aStringOrList</body>

<body package="MIME" selector="resentFromSendStrings">resentFromSendStrings
	^self encodedStringAt: 'resent-from'</body>

<body package="MIME" selector="resentMessageID">resentMessageID
	^self getValueFor: 'resent-message-id'</body>

<body package="MIME" selector="resentReplyTo">resentReplyTo
	^self getValueFor: 'resent-reply-to'</body>

<body package="MIME" selector="resentReplyTo:">resentReplyTo: aStringOrList
	^self setAddressesAt: 'resent-reply-to' to: aStringOrList</body>

<body package="MIME" selector="resentReplyToSendStrings">resentReplyToSendStrings
	^self encodedStringAt: 'resent-reply-to'</body>

<body package="MIME" selector="resentSender">resentSender
	^self getValueFor: 'resent-sender'</body>

<body package="MIME" selector="resentSender:">resentSender: aStringOrList
	^self setAddressesAt: 'resent-sender' to: aStringOrList</body>

<body package="MIME" selector="resentTo">resentTo
	^self getValueFor: 'resent-to' ifAbsent: [ OrderedCollection new ]</body>

<body package="MIME" selector="resentTo:">resentTo: aStringOrList
	^self setAddressesAt: 'resent-to' to: aStringOrList</body>

<body package="MIME" selector="resentToSendStrings">resentToSendStrings
	^self encodedStringAt: 'resent-to'</body>

<body package="MIME" selector="returnPath">returnPath
	^self getValueFor: 'return-path'</body>

<body package="MIME" selector="sender">sender
	^self getValueFor: 'sender'</body>

<body package="MIME" selector="setAddressesAt:to:">setAddressesAt: fieldName to: aStringOrList
	| addresses |

	addresses := self addressesFrom: aStringOrList. 
	^(self getFieldAt: fieldName) addresses: addresses</body>

<body package="MIME" selector="status">status
	^self getValueFor: 'status'</body>

<body package="MIME" selector="subject">subject
	^self getValueFor: 'subject'</body>

<body package="MIME" selector="subject:">subject: aString
	^self fieldValueAt: 'subject' put: aString</body>

<body package="MIME" selector="subjectSendString">subjectSendString
	^self encodedStringAt: 'subject'</body>

<body package="MIME" selector="timestamp">timestamp
	^(self fieldAt: 'date' ifAbsent: [^nil]) timestamp</body>

<body package="MIME" selector="timestamp:">timestamp: aTimestamp
	^(self getFieldAt: 'date') timestamp: aTimestamp</body>

<body package="MIME" selector="to">to
	^self getValueFor: 'to' ifAbsent: [ OrderedCollection new ]</body>

<body package="MIME" selector="to:">to: aStringOrList
	^self setAddressesAt: 'to' to: aStringOrList</body>

<body package="MIME" selector="toSendStrings">toSendStrings
	^self encodedStringAt: 'to'</body>
</methods>

<methods>
<class-id>Net.RFC822Message</class-id> <category>transfer encodings</category>

<body package="MIME" selector="transferFieldOrder">transferFieldOrder
" rfc822 recommends field order (if present) to be as follows. Order for remaining fiedls does not matter "
	^#('return-path' 'received' 'date' 'from' 'subject' 'sender' 'to' 'cc' 'mime-version' 'content-type' 'content-length' 'content-transfer-encoding' )</body>
</methods>

<methods>
<class-id>Net.RFC822Message</class-id> <category>private</category>

<body package="MIME" selector="encodedStringAt:">encodedStringAt: fieldName
	^(self getFieldAt: fieldName) addressesEncodedWith: self headerCharset</body>
</methods>

<methods>
<class-id>Net.RFC822Message</class-id> <category>visiting</category>

<body package="MIME" selector="acceptVisit:with:">acceptVisit: aVisitor with: argument

	^aVisitor acceptMessage: self with: argument</body>
</methods>

<methods>
<class-id>Net.RFC822Message</class-id> <category>parsing</category>

<body package="MIME" selector="addressesFrom:">addressesFrom: aListOrString
" RFC822Message new addressesFrom: 'George Jones &lt;Group@Some-Reg.An-Org&gt;,
                 Al.Neuman@MAD.Publisher' "

	" Canonicalize addresses "
	| coll addresses scanner |
	coll := aListOrString isString
		ifTrue: [ OrderedCollection with: aListOrString]
		ifFalse: [ aListOrString ].
	addresses := OrderedCollection new.
	coll do: 
		[ :addr |
		addr isString
			ifTrue: [(scanner := NetworkAddressDescriptor scannerType on: addr readStream) 
						acceptNonAsciiCharacters: true.
					addresses addAll: (scanner parseAddressesSeparatedBy: $, )]
			ifFalse: [addresses add: addr]]. 
	^addresses</body>
</methods>

<methods>
<class-id>Net.MailboxListField</class-id> <category>accessing</category>

<body package="MIME" selector="addAddress:">addAddress: aString
	^self addAddresses: (Array with: aString)</body>

<body package="MIME" selector="addAddresses:">addAddresses: aCollection
	addresses isNil ifTrue: [ addresses := OrderedCollection new].
	addresses add: aCollection.
	self value addAll: (self sanitize: aCollection)</body>

<body package="MIME" selector="address">address
	^self addresses isEmpty
		ifTrue: [nil]
		ifFalse: [self addresses first]</body>

<body package="MIME" selector="address:">address: aString
" MailboxListField default address: 'Some person&lt;person@acme.com&gt;' "
	self addresses: (OrderedCollection with: aString)</body>

<body package="MIME" selector="addresses">addresses
	^addresses</body>

<body package="MIME" selector="addresses:">addresses: aCollection
	 addresses := aCollection.
	self value: (self sanitize: aCollection)</body>

<body package="MIME" selector="addressesEncodedWith:">addressesEncodedWith: aString
	^self addresses collect: [ :addressDesc | addressDesc stringWithEncoding: aString ]</body>

<body package="MIME" selector="values">values
" Multi-valued fields override this method "
	^self value</body>

<body package="MIME" selector="values:">values: anArray
" Multi-valued fields override this method "
	self value: anArray</body>
</methods>

<methods>
<class-id>Net.MailboxListField</class-id> <category>printing</category>

<body package="MIME" selector="printAddressesOn:">printAddressesOn: aStream
	self class printAddressList: (self sanitize: self addresses) on: aStream</body>

<body package="MIME" selector="printStructureOn:">printStructureOn: aStream
	self printAddressesOn: aStream.</body>
</methods>

<methods>
<class-id>Net.MailboxListField</class-id> <category>private</category>

<body package="MIME" selector="sanitize:">sanitize: aCollection
" Make sure collection of addresses is comprised of strings "
	^aCollection collect: [:address | self sanitizeAddress: address]</body>

<body package="MIME" selector="sanitize:encoding:">sanitize: aCollection encoding: aString
" Make sure collection of addresses is comprised of strings "
	^aCollection collect: [:address | self sanitizeAddress: address encoding: aString ]</body>
</methods>

<methods>
<class-id>Net.MailboxListField</class-id> <category>initialization</category>

<body package="MIME" selector="initialize">initialize
	super initialize.
	value := OrderedCollection new.</body>
</methods>

<methods>
<class-id>Net.MailboxListField</class-id> <category>parsing</category>

<body package="MIME" selector="doParse:">doParse: rfc822Stream 
" HeaderField fromLine: 'To       :  George Jones &lt;Group@Some-Reg.An-Org&gt;,
                 Al.Neuman@MAD.Publisher' "
	| pos |

	pos := rfc822Stream position.
	addresses :=  [NetworkAddressDescriptor addressesFrom: rfc822Stream]
					on: Error
					do: [ :ex | 
						Array with: 
							(NetworkAddressDescriptor new 	
								comment: (rfc822Stream position: pos; nextLine); 
								yourself) ]. 
	^(self sanitize: addresses) asOrderedCollection</body>
</methods>

<methods>
<class-id>Net.MailboxListField</class-id> <category>composing</category>

<body package="MIME" selector="writeAddressesOn:encoding:">writeAddressesOn: aStream encoding: aString
	self class printAddressList: (self addressesEncodedWith: aString ) on: aStream</body>

<body package="MIME" selector="writeStructureOn:encoding:">writeStructureOn: aStream encoding: aString
	self writeAddressesOn: aStream encoding: aString.</body>
</methods>

<methods>
<class-id>Net.MailboxListField class</class-id> <category>printing</category>

<body package="MIME" selector="addressSeparator">addressSeparator
	^$,</body>

<body package="MIME" selector="printAddressList:on:">printAddressList: collection on: aStream
	collection do: [:addr | aStream nextPutAll: addr]
		separatedBy: [aStream nextPut: self addressSeparator]</body>
</methods>

<methods>
<class-id>Net.MailboxListField class</class-id> <category>parsing</category>

<body package="MIME" selector="fieldNames">fieldNames
	^#('from' 'to' 'reply-to' 'cc' 'bcc' 'resent-reply-to' 'resent-from' 'resent-to' 'resent-cc' 'resent-bcc' )</body>
</methods>

<methods>
<class-id>Net.MailGroupDescriptor</class-id> <category>accessing</category>

<body package="MIME" selector="addresses">addresses
	^addresses</body>

<body package="MIME" selector="addresses:">addresses: anArray
	addresses := anArray</body>

<body package="MIME" selector="alias">alias
	^alias</body>

<body package="MIME" selector="alias:">alias: aString
	alias := aString</body>
</methods>

<methods>
<class-id>Net.MailGroupDescriptor</class-id> <category>initialization</category>

<body package="MIME" selector="initialize">initialize
	addresses := Array new.</body>
</methods>

<methods>
<class-id>Net.MailGroupDescriptor</class-id> <category>composing</category>

<body package="MIME" selector="writeCanonicalValueOn:encoding:">writeCanonicalValueOn: stream encoding: aString
	self writeAliasOn: stream encoding: aString.
	self printAddressesOn: stream</body>
</methods>

<methods>
<class-id>Net.MailGroupDescriptor</class-id> <category>printing</category>

<body package="MIME" selector="printAddressesOn:">printAddressesOn: stream

	stream nextPut: $:.
	self addresses do: [ :address | address printOn: stream ] separatedBy: [ stream nextPut: $, ].
	stream nextPut: $;</body>

<body package="MIME" selector="printCanonicalValueOn:">printCanonicalValueOn: stream
	self printAliasOn: stream.
	self printAddressesOn: stream</body>
</methods>

<methods>
<class-id>Net.RFC822Scanner</class-id> <category>tokenization</category>

<body package="MIME" selector="nextToken">nextToken
	^self nextRFC822Token</body>

<body package="MIME" selector="specials">specials
" This method is provided to encapsulate lexical differences between RFC822 on one side, and MIME, HTTP on the other side. MIME definiton of 'tspecials' is the same as the RFC 822 definition of ''specials' with the addition of the three characters &lt;/&gt;, &lt;?&gt;, and &lt;=&gt;, and the removal of &lt;.&gt;. To present uniform tokenization interface, this method is overridden in Mime scanner "

	^self class specials</body>
</methods>

<methods>
<class-id>Net.RFC822Scanner</class-id> <category>multi-character scans</category>

<body package="MIME" selector="scanAtom">scanAtom
" atom  =  1*&lt;any CHAR except specials, SPACE and CTLs&gt; "

	token := self scanTokenMask: AtomMask.
	tokenType := #atom.
	^token.</body>

<body package="MIME" selector="scanComment">scanComment
	"collect comment"

	| output decComment |

	output := saveComments
				ifTrue: [(String new: 64) writeStream]
				ifFalse: [nil].

	self scanCommentOn: output.

	output notNil 
		ifTrue: [
			decComment := MimeEncodedWordCoDec decodeComment: output contents.
			currentComment isNil
				ifTrue: [currentComment := OrderedCollection with: decComment]
				ifFalse: [currentComment add: decComment]]. 
	^token</body>

<body package="MIME" selector="scanDate">scanDate
	^self class readDateAndTimeFrom: self asStream</body>

<body package="MIME" selector="scanDomainText">scanDomainText
	"dtext = &lt;any CHAR excluding &lt;[&gt;, &lt;]&gt;, &lt;\&gt; &amp; CR, &amp; including linear-white-space&gt; ; =&gt; may be folded"

	token := self
				scanToken: [self scanQuotedChar; matchCharacterType: DomainTextMask]
				delimitedBy: '[]'
				notify: (#MalformedDomainLiteral &lt;&lt; #net &gt;&gt; 'Malformed domain literal').
	tokenType := #domainText.
	^token</body>

<body package="MIME" selector="scanEndOfLine">scanEndOfLine
	"Note: this will work only for RFC822 but not for HTTP. Needs more design work"

	(self matchCharacterType: CRLFMask)
		ifTrue: 
			[self shouldFoldLine
				ifTrue: [ 	self hereChar: Character space. ^self].	

			"Otherwise we have an end-of-line condition -- set appropriate masks"
			classificationMask := (classificationMask maskClear: WhiteSpaceMask)
						maskSet: EndOfLineMask]</body>

<body package="MIME" selector="scanFieldName">scanFieldName
	" RFC822, p.9: field-name = 1*&lt;any CHAR excluding CTLs, SPACE and ':'&gt; "
	^self scanTokenMask: HeaderNameMask</body>

<body package="MIME" selector="scanPhrase">scanPhrase
	"RFC822: phrase = 1*word ; Sequence of words. At the end of scan the scanner has read the first token after phrase "

	^self tokenizeWhile: [#(#quotedText #atom) includes: tokenType]</body>

<body package="MIME" selector="scanQuotedChar">scanQuotedChar
	"Scan possible quoted character. If the current char is $\, read in next character and make it a quoted 
	string character"

	^(hereChar == QuotedPairChar)
		ifTrue: 
			[self step.
			classificationMask := QuotedPairMask.
			true]
		ifFalse: [false]</body>

<body package="MIME" selector="scanQuotedText">scanQuotedText
" quoted-string = &lt;""&gt; *(qtext/quoted-pair) &lt;""&gt;; Regular qtext or quoted chars.
  qtext    =  &lt;any CHAR excepting &lt;""&gt;, &lt;\&gt; &amp; CR, and including linear-white-space&gt;  ; =&gt; may be folded"

	" We are positioned at the first double quote character "
	token := self 
				scanToken: [ self scanQuotedChar; matchCharacterType: QuotedTextMask ] 
				delimitedBy: '""' 
				notify: (#UnmatchedQuotedText &lt;&lt; #net &gt;&gt; 'Unmatched quoted text').
	tokenType := #quotedText.
	^token.</body>

<body package="MIME" selector="scanText">scanText
	"RFC822: text = &lt;Any CHAR, including bare CR &amp; bare LF, but not including CRLF. This is a 'catchall' category and cannot be tokenized. Text is used only to read values of unstructured fields"

	^MimeEncodedWordCoDec decodeText: (self scanUntil: [ self matchCharacterType: CRLFMask ])</body>

<body package="MIME" selector="scanWord">scanWord
	self nextToken.
	(#(#quotedText #atom) includes: tokenType)
			ifFalse: [self error: (#ExpectingWord &lt;&lt; #net &gt;&gt; 'Expecting word')].
	^token</body>

<body package="MIME" selector="skipWhiteSpace">skipWhiteSpace
	"It is inefficient because intermediate stream is created. Perhaps refactoring scanWhile: can help"

	self scanWhile: [hereChar == $(
			ifTrue: 
				[self stepBack; scanComment.
				true]
			ifFalse: [self matchCharacterType: WhiteSpaceMask]]</body>
</methods>

<methods>
<class-id>Net.RFC822Scanner</class-id> <category>private</category>

<body package="MIME" selector="isTokenChar:">isTokenChar: aMask

	^(self matchCharacterType:  aMask)
			or: [self acceptNonAsciiCharacters 
				and:[self matchCharacterType: AnyTextMask]]</body>

<body package="MIME" selector="nextRFC822Token">nextRFC822Token
	| char |
	self skipWhiteSpace.
	char := self peek.
	char isNil				"end of input"
		ifTrue: [tokenType := #doIt.
			^token := nil].
	char == $( ifTrue: [^self scanComment; nextToken].
	char == $" ifTrue: [^self scanQuotedText].
	(self specials includes: char)
		ifTrue: [tokenType := #special.		" Special character. Make it token value and set token type "
				^token := self next.].

	^self privateScanTokenMask: AtomMask</body>

<body package="MIME" selector="privateScanTokenMask:">privateScanTokenMask: aMask
	| stream specials  |

	(self isTokenChar: aMask)
		ifFalse: [tokenType := #doIt. ^token:= self next ].
	tokenType := aMask = AtomMask
			ifTrue: [#atom]
			ifFalse: [#token].
	stream := (String new: 64) writeStream.
	specials := self specials, ' '.

	[self atEnd
		ifTrue: 
			[self hereChar: nil.
			^token := stream contents].
	self step.
	((specials includes: hereChar) not and: [self isTokenChar: aMask]) ]
		whileTrue: [stream nextPut: hereChar ].

	self stepBack.
	token := stream contents.
	^token</body>

<body package="MIME" selector="scanCommentOn:">scanCommentOn: streamOrNil 
	"scan comment copying on specified stream"

	self step ~~ $( ifTrue: [self error: (#UnmatchedComment &lt;&lt; #net &gt;&gt; 'Unmatched comment') ].		" Should never be the case "
	token := self
				scanUntil: [
					((self scanQuotedChar; matchCharacterType: CommentMask)
						ifTrue: 
							[streamOrNil notNil ifTrue: [streamOrNil nextPut: hereChar].
							true]
						ifFalse: [hereChar == $(
								ifTrue: 
									[streamOrNil notNil ifTrue: [streamOrNil space].
									self stepBack; scanCommentOn: streamOrNil.
									streamOrNil notNil ifTrue: [streamOrNil space].
									true]
								ifFalse: [false]]) not].
	hereChar ~~ $) ifTrue: [ self error: (#UnmatchedComment &lt;&lt; #net &gt;&gt; 'Unmatched comment') ].
	^token</body>

<body package="MIME" selector="shouldFoldLine">shouldFoldLine
	"Answers true if next line is to be folded in, that is, if CRLF is followed by at least one white space"

	self atEnd ifTrue: [ ^false ].
	^((self classificationMaskFor: self peek) anyMask: WhiteSpaceMask)
		ifTrue: [self sourceTrailNextPut: source next. true]
		ifFalse: [false]</body>

<body package="MIME" selector="step">step
	super step.
	self scanEndOfLine.
	^hereChar</body>

<body package="MIME" selector="writeFoldLineOn:">writeFoldLineOn: stream

	[self atEnd ifTrue: [^hereChar].

	self next.

	((self matchCharacterType: CRLFMask) and: [self shouldFoldLine])
		ifTrue: ["Fold the line " self hereChar: Character space.].

	(self matchCharacterType: CRLFMask) ifTrue: [^self hereChar ]. 	
			
	stream nextPut: self hereChar	
	] repeat</body>
</methods>

<methods>
<class-id>Net.RFC822Scanner</class-id> <category>converting</category>

<body package="MIME" selector="phraseAsString:">phraseAsString: phrase
	| stream |
	stream := (String new: 64) writeStream.
	self class printPhrase: phrase on: stream. 
	^stream contents.</body>
</methods>

<methods>
<class-id>Net.RFC822Scanner</class-id> <category>utilities</category>

<body package="MIME" selector="peekForEndOfLine">peekForEndOfLine

	((self classificationMaskFor: self peek) anyMask: CRLFMask)
		ifTrue: [ source next. ^true].
	^false</body>

<body package="MIME" selector="readFieldName">readFieldName
	| fname |

	fname := self scanFieldName. 
	super skipWhiteSpace.
	^(self atEnd or: [(self peekFor: $:) not])
		ifTrue: [ nil ]
		ifFalse: [fname asLowercase]</body>
</methods>

<methods>
<class-id>Net.RFC822Scanner class</class-id> <category>from Network Clients</category>

<body package="MIME" selector="adjustTime:timezone:">adjustTime: aTimestamp timezone: timezone
	| localSeconds timezoneSeconds |

	localSeconds := TimeZone default secondsFromGMTAtLocal: aTimestamp.
	timezoneSeconds := ('#####' match: timezone)
			ifTrue: [ | sec |
				sec := ((timezone copyFrom: 2 to: 3) asNumber * 60 + (timezone copyFrom: 4 to: 5) asNumber) * 60.
				timezone first = $- ifTrue: [sec negated] ifFalse: [sec]]
			ifFalse: [(SimpleTimeZones at: timezone ifAbsent: [nil]) ifNotNil: [ :hrs | "60*60"3600 * hrs ]].
	^timezoneSeconds isNil
		ifTrue: [aTimestamp]
		ifFalse: [Timestamp fromSeconds: (aTimestamp asSeconds - timezoneSeconds + localSeconds)]</body>

<body package="MIME" selector="correctDateForY2K:">correctDateForY2K: date
" Arbitrarily decide that if year is &gt;2050, subtract one century "
"RFC 2822
Where a two or three digit year occurs in a date, the year is to be
   interpreted as follows: If a two digit year is encountered whose
   value is between 00 and 49, the year is interpreted by adding 2000,
   ending up with a value between 2000 and 2049.  If a two digit year is
   encountered with a value between 50 and 99, or any three digit year
   is encountered, the year is interpreted by adding 1900. "

	date year &gt; 2050 
		ifTrue: [date day: date day year: date year - 100].
	^date</body>

<body package="MIME" selector="currentGMTDateAndTime">currentGMTDateAndTime
"self  currentGMTDateAndTime"

	^TimeZone null
		convertGMT: Time secondClock
		do:
			[:date :seconds |
			Array with: date with: (Time fromSeconds: seconds)]</body>

<body package="MIME" selector="dateAndTimeFrom:">dateAndTimeFrom: aString
	"RFC822Scanner dateAndTimeFrom: '6 Dec 88 10:16:08 +0900 (Tuesday)'."
	"RFC822Scanner dateAndTimeFrom: '12 Dec 88 10:16:08 +0900 (Tuesday)'."
	"RFC822Scanner dateAndTimeFrom: 'Fri, 31 Mar 89 09:13:20 +0900'."
	"RFC822Scanner dateAndTimeFrom: 'Tue, 18 Apr 89 23:29:47 +0900'."
	"RFC822Scanner dateAndTimeFrom: 'Tue, 23 May 89 13:52:12 JST'."
	"RFC822Scanner dateAndTimeFrom: 'Thu, 1 Dec 88 17:13:27 jst'."
	"RFC822Scanner dateAndTimeFrom: 'Sat, 15 Jul 95 14:36:22 0900'."
	"RFC822Scanner dateAndTimeFrom: '2-Nov-86 10:43:42 PST'."
	"RFC822Scanner dateAndTimeFrom: 'Friday, 21-Jul-95 04:04:55 GMT'."
	"RFC822Scanner dateAndTimeFrom: 'Jul 10 11:06:40 1995'."
	"RFC822Scanner dateAndTimeFrom: 'Jul 10 11:06:40 JST 1995'."
	"RFC822Scanner dateAndTimeFrom: 'Mon Jul 10 11:06:40 1995'."
	"RFC822Scanner dateAndTimeFrom: 'Mon Jul 10 11:06:40 JST 1995'."
	"RFC822Scanner dateAndTimeFrom: '(6 December 1988 10:16:08 am )'."
	"RFC822Scanner dateAndTimeFrom: '(12 December 1988 10:16:08 am )'."
	"RFC822Scanner dateAndTimeFrom: ''."

	| rfcString |
	aString size &lt;= 10	"may be illegal format"
		ifTrue: [^self currentGMTDateAndTime].
	rfcString := self normalizeDateAndTimeString: aString.
	^self readDateAndTimeFrom: rfcString readStream</body>

<body package="MIME" selector="dateStringFrom:">dateStringFrom: aTimestamp
	"Return Date dateAndTimeNow in the Unix mail format"

	" self dateAndTimeFrom: (Array with: Date today with: Time now) "

	| stream temp indx d seconds hours minutes date |
	stream := WriteStream on: (String new: 40).
	date := aTimestamp asDate.
	indx := date weekdayIndex.
	d := #(#Mon #Tue #Wed #Thu #Fri #Sat #Sun) at: indx.
	stream nextPutAll: d.
	stream nextPutAll: ', '.
	date printOn: stream format: #(1 2 3 $  2 1).

	stream nextPutAll: ((temp := aTimestamp hour) &lt; 10 
				ifTrue: [' 0']
				ifFalse: [' ']).
	temp printOn: stream.
	stream nextPutAll: ((temp := aTimestamp minute) &lt; 10 
				ifTrue: [':0']
				ifFalse: [':']).
	temp printOn: stream.
	stream nextPutAll: ((temp := aTimestamp second) &lt; 10 
				ifTrue: [':0']
				ifFalse: [':']).
	temp printOn: stream.

	seconds := TimeZone default secondsFromGMTAtLocal: aTimestamp .
	seconds negative 
		ifTrue: [stream nextPutAll: ' -']
		ifFalse: [stream nextPutAll: ' +'].
	hours := seconds abs // 3600.
	hours abs &lt; 10 ifTrue: [stream nextPut: $0].
	stream print: hours abs.
	minutes := seconds abs \\ 3600 // 60.
	minutes abs &lt; 10 ifTrue: [stream nextPut: $0].
	stream print: minutes.  
	^stream contents</body>

<body package="MIME" selector="defaultTimeZoneDifference">defaultTimeZoneDifference
	^TimeZone default instVarAt: 1</body>

<body package="MIME" selector="initializeTimeZones">initializeTimeZones
	"RFC822Scanner initializeTimeZones."
	"Install TimeZone constants."

	SimpleTimeZones := Dictionary new.

	"Universal Time"
	SimpleTimeZones at: 'UT' put: 0.
	SimpleTimeZones at: 'GMT' put: 0.

	"For North America."
	SimpleTimeZones at: 'EST' put: -5.
	SimpleTimeZones at: 'EDT' put: -4.
	SimpleTimeZones at: 'CST' put: -6.
	SimpleTimeZones at: 'CDT' put: -5.
	SimpleTimeZones at: 'MST' put: -7.
	SimpleTimeZones at: 'MDT' put: -6.
	SimpleTimeZones at: 'PST' put: -8.
	SimpleTimeZones at: 'PDT' put: -7.

	"For Europe."
	SimpleTimeZones at: 'BST' put: 0.
	SimpleTimeZones at: 'WET' put: 0.
	SimpleTimeZones at: 'MET' put: 1.
	SimpleTimeZones at: 'EET' put: 2.

	"For Japan."
	SimpleTimeZones at: 'JST' put: 9</body>

<body package="MIME" selector="normalizeDateAndTimeString:">normalizeDateAndTimeString: aString
	"RFC822 formats"
	"RFC822Scanner normalizeDateAndTimeString: '6 Dec 88 10:16:08 +0900 (Tuesday)'."
	"RFC822Scanner normalizeDateAndTimeString: 'Tue, 18 Apr 89 23:29:47 +0900'."
	"RFC822Scanner normalizeDateAndTimeString: 'Tue, 18 Apr 89 23:29:47 0900'."
	"RFC822Scanner normalizeDateAndTimeString: 'Tue, 23 May 89 13:52:12 JST'."
	"RFC822Scanner normalizeDateAndTimeString: '2-Nov-86 10:43:42 PST'."
	"Other formats"
	"RFC822Scanner normalizeDateAndTimeString: 'Jul 10 11:06:40 1995'."
	"RFC822Scanner normalizeDateAndTimeString: 'Jul 10 11:06:40 JST 1995'."
	"RFC822Scanner normalizeDateAndTimeString: 'Mon Jul 10 11:06:40 1995'."
	"RFC822Scanner normalizeDateAndTimeString: 'Mon Jul 10 11:06:40 JST 1995'."

	| head tail read str1 str2 write |
	aString size &lt; 6 ifTrue: [^aString].
	head := aString copyFrom: 1 to: aString size - 5.
	(head indexOf: $,) &gt; 0 ifTrue: [^aString].
	tail := aString copyFrom: aString size - 4 to: aString size.
	read := tail readStream.
	(read next = Character space and:
	[read next isDigit and:
	[read next isDigit and:
	[read next isDigit and:
	[read next isDigit]]]])
		ifFalse: [^aString].
	read := head readStream.
	str1 := read through: Character space.
	str2 := read through: Character space.
	(str1 isEmpty or: [str2 isEmpty]) ifTrue: [^aString].
	str2 first isDigit
		ifFalse:
			[str1 := str2.
			str2 := read through: Character space.
			(str2 isEmpty or: [str2 first isDigit not]) ifTrue: [^aString]].
	read atEnd ifTrue: [^aString].
	write := WriteStream on: (String new: 32).
	write nextPutAll: str2;
		nextPutAll: str1;
		nextPutAll: (tail copyFrom: 4 to: 5);
		space;
		nextPutAll: read.
	^write contents</body>

<body package="MIME" selector="readDateAndTimeFrom:">readDateAndTimeFrom: aStream
	"date-time	=  [ day ',' ] date time
	day			=  'Mon'  / 'Tue' /  'Wed'  / 'Thu'
				/  'Fri'  / 'Sat' /  'Sun'"
	"RFC822Scanner readDateAndTimeFrom: '6 Dec 88 10:16:08 +0900 (Tuesday)' readStream."

	| date time timezone |
	"While reviewing AR#44558 we decided if the date field is empty to return nil instead of old implementation ^self currentGMTDateAndTime to be able determine if field is valid or not"
	aStream atEnd ifTrue: [^nil].
	date := self readDateFrom: aStream.

	"time	= hour zone 
	hour	= 2DIGIT ':' 2DIGIT [':' 2DIGIT] "
	aStream skipSeparators.
	time := Time readFrom: aStream.

	timezone := self readTimezoneFrom: aStream.
	^timezone isEmpty
		ifTrue: [Timestamp fromDate: date andTime: time]
		ifFalse: 
			[self 
				adjustTime: (Timestamp fromDate: date andTime: time) 
				timezone: timezone].</body>

<body package="MIME" selector="readDateFrom:">readDateFrom: aStream
	"date	=  1*2DIGIT month 2DIGIT
	month	=  'Jan'  /  'Feb' /  'Mar'  /  'Apr'
			/  'May'  /  'Jun' /  'Jul'  /  'Aug'
			/  'Sep'  /  'Oct' /  'Nov'  /  'Dec'"
	"RFC822Scanner readDateFrom: '01 Jan 95' readStream."
	"RFC822Scanner readDateFrom: '1 Jan 50' readStream."
	"RFC822Scanner readDateFrom: '23 Jan 02' readStream."
	"RFC822Scanner readDateFrom: '23-Jan-95' readStream."
	"RFC822Scanner readDateFrom: 'Jan 23 2095' readStream."
	"RFC822Scanner readDateFrom: 'Jan 23 1995' readStream."

	| reader position token date yearChunk |
	reader := (TimestampPrintPolicy newFor: #us) reader.
	position := aStream position.
	token := reader getLatinToken: aStream.
	(token isString 
		and: [ (#('mon' 'tue' 'wed' 'thu' 'thr' 'fri' 'sat' 'sun' 'monday' 'tuesday' 'wednesday' 'thursday' 'friday' 'saturday' 'sunday') includes: token asLowercase)])
		ifFalse: [ aStream position: position ].
	date := reader readDateFrom: aStream.
	 aStream skip: -4.
	 yearChunk := aStream next: 4.
	 (('####' match: yearChunk)
		 and: [ (yearChunk copyFrom: 1 to: 4) asNumber = date year ])
			  ifFalse: [ self correctDateForY2K: date ].
	^date</body>

<body package="MIME" selector="readTimezoneFrom:">readTimezoneFrom: aStream 
	"time	= hour zone 
	hour	= 2DIGIT ':' 2DIGIT [':' 2DIGIT] 
	zone	= 'UT' / 'GMT' 
	/ 'EST' / 'EDT' 
	/ 'CST' / 'CDT' 
	/ 'MST' / 'MDT' 
	/ 'PST' / 'PDT' 
	/ 1ALPHA 
	/ ( ('+' / '-') 4DIGIT )"
	| write char |
	aStream skipSeparators.
	write := WriteStream on: (String new: 8).
	[aStream atEnd
		or: 
			[char := aStream next.
			char isSeparator]]
		whileFalse: [write nextPut: char].
	^write contents asUppercase.</body>
</methods>

<methods>
<class-id>Net.RFC822Scanner class</class-id> <category>printing</category>

<body package="MIME" selector="defaultTokenType">defaultTokenType
	^#word</body>

<body package="MIME" selector="printComment:on:">printComment: comment on: stream 
	comment notNil ifTrue: [
		stream nextPut: $(.
		comment
			do: 
				[:char | 
				(self isCommentChar: char)
					ifFalse: [stream nextPut: $\].
				stream nextPut: char].
		stream nextPut: $).
	]</body>

<body package="MIME" selector="printDomain:on:">printDomain: domainx on: stream 
	"Domainx is an array of domain segments"

	domainx notNil ifTrue: [domainx do: [:word | self printWord: word on: stream]
			separatedBy: [stream nextPut: $.]]</body>

<body package="MIME" selector="printPhrase:on:">printPhrase: phrase on: stream
	phrase do: [ :word | stream nextPutAll: word] separatedBy: [ stream space ]</body>

<body package="MIME" selector="printWord:on:">printWord: str on: stream 
	"Print word as either atom or quoted text"

	(self shouldBeQuoted: str)
		ifTrue: [stream nextPut: $"; nextPutAll: str; nextPut: $"]
		ifFalse: [stream nextPutAll: str]</body>
</methods>

<methods>
<class-id>Net.RFC822Scanner class</class-id> <category>class initialization</category>

<body package="MIME" selector="initClassificationTable">initClassificationTable
	super initClassificationTable.
	self initClassificationTableWith: HeaderNameMask when:
		[:c | c &gt; Character space and: [c ~~ $:]].
	self initClassificationTableWith: TextMask when:
		[:c | c ~~ Character cr ].
	self initClassificationTableWith: AtomMask when:
		[:c | c &gt; Character space and: [ (self specials includes: c) not ] ].
	self initClassificationTableWith: TokenMask when:
		[:c | c &gt; Character space and: [ (self tspecials includes: c) not ] ].
	self initClassificationTableWith: QuotedTextMask when:
		[:c | c ~~ $" and: [ c ~~ $\ and: [ c ~~ Character cr ]]].
	self initClassificationTableWith: DomainTextMask when:
		[:c | ('[]\' includes: c) not and: [ c ~~ Character cr ]].
	self initClassificationTableWith: CommentMask when:
		[:c | c ~~ $( and: [ c ~~ $) and: [ c ~~ $\ and: [ c ~~ Character cr ]]]].</body>

<body package="MIME" selector="initialize">initialize
	" RFC822Scanner initialize "

	self initializeConstants; initClassificationTable</body>

<body package="MIME" selector="initializeConstants">initializeConstants
	AtomMask := 256.
	CommentMask := 512.
	DomainTextMask := 1024.
	HeaderNameMask := 2048.
	QuotedTextMask := 4096.
	TextMask := 8192.
	TokenMask := 16384.
	QuotedPairMask := (QuotedTextMask bitOr: CommentMask) bitOr: DomainTextMask.
	QuotedPairChar := $\.
	HeaderNameDelimiterChar := $:.

	"Install TimeZone constants."

	SimpleTimeZones := Dictionary new.

	"Universal Time"
	SimpleTimeZones at: 'UT' put: 0.
	SimpleTimeZones at: 'GMT' put: 0.

	"For North America."
	SimpleTimeZones at: 'EST' put: -5.
	SimpleTimeZones at: 'EDT' put: -4.
	SimpleTimeZones at: 'CST' put: -6.
	SimpleTimeZones at: 'CDT' put: -5.
	SimpleTimeZones at: 'MST' put: -7.
	SimpleTimeZones at: 'MDT' put: -6.
	SimpleTimeZones at: 'PST' put: -8.
	SimpleTimeZones at: 'PDT' put: -7.

	"For Europe."
	SimpleTimeZones at: 'BST' put: 0.
	SimpleTimeZones at: 'WET' put: 0.
	SimpleTimeZones at: 'MET' put: 1.
	SimpleTimeZones at: 'EET' put: 2.

	"For Japan."
	SimpleTimeZones at: 'JST' put: 9</body>
</methods>

<methods>
<class-id>Net.RFC822Scanner class</class-id> <category>character classification</category>

<body package="MIME" selector="specials">specials
	" Note that definition of this set varies from standard to standard, so this method needs to be overridden for specialized parsers " 
	^'()&lt;&gt;@,;:\".[]'</body>

<body package="MIME" selector="tspecials">tspecials
	" tspecials in MIME and HTTP. It is derived from RCC822 specials with addition of &lt;/&gt;, &lt;?&gt;, &lt;=&gt; and removal; of &lt;.&gt; " 
	^'()&lt;&gt;@,;:\"/[]?='</body>
</methods>

<methods>
<class-id>Net.RFC822Scanner class</class-id> <category>testing</category>

<body package="MIME" selector="isAtomChar:">isAtomChar: char
	^((self classificationMaskFor: char)  bitAnd: AtomMask) ~= 0</body>

<body package="MIME" selector="isCommentChar:">isCommentChar: char
	^[ ((self classificationMaskFor: char) bitAnd: CommentMask) ~= 0
	] on: NonASCIICharacter
	do: [ :ex | true ]</body>

<body package="MIME" selector="shouldBeQuoted:">shouldBeQuoted: string 
	
	^[(string detect: [:char | (self isAtomChar: char) not] ifNone: [nil]) 
		notNil] 
			on: NonASCIICharacter
			do: [:ex | true ]</body>
</methods>

<methods>
<class-id>Net.MimeParser</class-id> <category>parsing</category>

<body package="MIME" selector="endMessageBody:from:">endMessageBody: aBody from:  aStream

	self handler endMessageBody:  aBody</body>

<body package="MIME" selector="parse:">parse:  aStream
	| header entity  |
	entity := self startMessageFrom: aStream.
	header := self parseHeaderFrom: aStream.
	entity := self handler addHeader: header toEntity: entity.
	self parseMessageBody: entity from: aStream.
	self handler endMessage: entity.

	^entity</body>

<body package="MIME" selector="parseBody:from:">parseBody: unparsedBody from: aStream

	| aBody |
	aBody := unparsedBody newBody.
	self handler startBody: aBody.
	[aBody isSimple 
		ifTrue: [self parseSimpleBody: aBody from: aStream]
		ifFalse: [self parseMultipartBody: aBody from: aStream].
	] ensure: [self handler endBody:  aBody].

	^aBody</body>

<body package="MIME" selector="parseHeaderFrom:">parseHeaderFrom: aStream 

	| headerx |
	headerx := self handler startHeader: nil.
	aStream text.
	self privateParseHeader: headerx scanner: (self scannerOn: aStream).
	^self handler endHeader: headerx.</body>

<body package="MIME" selector="parseMessageBody:from:">parseMessageBody: aMessage from: aStream

	| stream |
	(self handler shouldStartMessageBody:  aMessage body) 
		ifTrue: [stream := self handler 
							prepareStream: aStream
							forMessageBody: aMessage.
				self startMessageBody: aMessage body from: stream.
				self parseBody: aMessage body from: stream.
				self endMessageBody: aMessage body from: aStream].

	^aMessage</body>

<body package="MIME" selector="parseMultipartBody:from:">parseMultipartBody: aMultipartBody from: aStream
	| stream parent header part boundedStream |
	boundedStream := self handler 
						prepareStream: aStream
						forMultipartBody: aMultipartBody.
	stream := self  handler asEncodedStream: boundedStream.
	parent := aMultipartBody parent. 

	[part := self handler startPartFor: parent.
	header := self parseHeaderFrom: stream.
	self handler addHeader: header toEntity: part. 
	self parseBody: part body from: stream.
	self handler endPart: part. 
	boundedStream isEndOfLastPart 
	] 
		whileFalse</body>

<body package="MIME" selector="parseSimpleBody:from:">parseSimpleBody: aBody from: aStream 

	| processed |
	(aBody parent parent notNil and: [ aBody parent  parent isMultipart ])
		ifFalse: [ ^self handler processBody: aBody from: aStream].

	processed := false.

	aBody parent isAttachment ifTrue: [self handler processAttachment: aBody from:  aStream. processed := true. ].
	aBody parent isInline ifTrue: [self handler processInline: aBody from:  aStream. processed := true. ].
	aBody parent isFormData 	ifTrue: [self handler processFormData: aBody from:  aStream. processed := true. ].
	processed ifFalse: [self handler processPartBody: aBody from:  aStream.].</body>

<body package="MIME" selector="startMessageBody:from:">startMessageBody: aBody from:  aStream

	self handler startMessageBody:  aBody</body>

<body package="MIME" selector="startMessageFrom:">startMessageFrom: aStream

	^self handler startMessage: nil</body>
</methods>

<methods>
<class-id>Net.MimeParser</class-id> <category>accessing</category>

<body package="MIME" selector="handler">handler
	^handler</body>

<body package="MIME" selector="handler:">handler: aMessageHandler

	handler := aMessageHandler</body>
</methods>

<methods>
<class-id>Net.MimeParser</class-id> <category>private</category>

<body package="MIME" selector="privateParseHeader:scanner:">privateParseHeader: header scanner: aMimeScanner 
	| factory field stream |
	factory := self handler fieldFactory.
	aMimeScanner acceptNonAsciiCharacters: self handler acceptNonAsciiCharacters.
	[aMimeScanner atEnd 
		or: [aMimeScanner peekForEndOfLine ]
	] whileFalse:
			[stream := ReadWriteStream on: String new.
			aMimeScanner writeFoldLineOn: stream.
			(field := factory readFrom: stream reset acceptNonAsciiCharacters: self handler acceptNonAsciiCharacters) 
				ifNotNil: [header addField: field] ]</body>

<body package="MIME" selector="scannerOn:">scannerOn: aStream
	^self handler scannerType on: aStream</body>
</methods>

<methods>
<class-id>Net.MimeParser</class-id> <category>deprecated</category>

<body package="MIME" selector="readFrom:">readFrom: aStream
	^self parse: aStream</body>
</methods>

<methods>
<class-id>Net.MimeParser</class-id> <category>initialize-release</category>

<body package="MIME" selector="initialize">initialize</body>
</methods>

<methods>
<class-id>Net.MimeParser</class-id> <category>defaults</category>

<body package="MIME" selector="encoding">encoding
self halt.
	^#UTF_8</body>
</methods>

<methods>
<class-id>Net.MimeParser class</class-id> <category>instance creation</category>

<body package="MIME" selector="handler:">handler: aMessageHandler
	^self new
		handler: aMessageHandler</body>

<body package="MIME" selector="new">new
	^super new initialize</body>

<body package="MIME" selector="readFrom:">readFrom: aStream
	^self readFrom: aStream builder: MimeEntity newBuilder</body>

<body package="MIME" selector="readFrom:builder:">readFrom: aStream builder: aMessageBuildHandler
	^self new 
		handler: aMessageBuildHandler;
		readFrom: aStream</body>
</methods>

<methods>
<class-id>Net.BoundedWriteCharacterStream</class-id> <category>accessing</category>

<body package="MIME" selector="endPart">endPart

	stream cr.</body>

<body package="MIME" selector="startPart">startPart

	partNumber := partNumber + 1.
	stream 
		nextPutAll: self boundary;
		cr</body>

<body package="MIME" selector="writeConclusion">writeConclusion

	stream 
		nextPutAll:  self boundary;
		nextPutAll: self boundaryDashes;
		cr</body>
</methods>

<methods>
<class-id>Net.BoundedWriteCharacterStream</class-id> <category>initialize-release</category>

<body package="MIME" selector="initializeOn:boundary:">initializeOn: aStream boundary: aString

	stream := aStream. 
	boundaryDashes := MultipartBody boundaryDashes.
	self setBoundary:aString.
	partNumber := 0.</body>

<body package="MIME" selector="setBoundary:">setBoundary: aString

	boundary := MultipartBody boundaryDashes , aString</body>
</methods>

<methods>
<class-id>Net.AttachmentFilename</class-id> <category>accessing</category>

<body package="MIME" selector="filename">filename
	^parameter</body>

<body package="MIME" selector="filename:">filename: aString
	parameter := aString</body>

<body package="MIME" selector="originalFilename">originalFilename

	^originalFilename</body>

<body package="MIME" selector="originalFilename:">originalFilename: aString

	originalFilename := aString</body>
</methods>

<methods>
<class-id>Net.AttachmentFilename</class-id> <category>private</category>

<body package="MIME" selector="defaultResumeValue">defaultResumeValue

	^self filename</body>
</methods>

<methods>
<class-id>Net.AttachmentFilename</class-id> <category>private - actions</category>

<body package="MIME" selector="defaultAction">defaultAction

	| s |
	self class logToTranscript 
		ifTrue: [s := (#TheAttachmentIsSavedInTo &lt;&lt; #net &gt;&gt; 'the attachment is saved in to ') asString.
				Transcript cr; show:'&lt;', self class fullName , '&gt; ', s, self parameter  asString ].

	^self defaultResumeValue</body>
</methods>

<methods>
<class-id>Net.BoundedReadStream</class-id> <category>testing</category>

<body package="MIME" selector="atEnd">atEnd

	^atEnd</body>

<body package="MIME" selector="endOfLastPart">endOfLastPart

	^endOfLastPart</body>

<body package="MIME" selector="isReadable">isReadable

	^true</body>

<body package="MIME" selector="isWritable">isWritable
	"Answer whether the stream implements #nextPut:."

	^false</body>
</methods>

<methods>
<class-id>Net.BoundedReadStream</class-id> <category>accessing</category>

<body package="MIME" selector="isEndOfLastPart">isEndOfLastPart

	stream atEnd ifTrue: [ ^true].
	self endOfLastPart ifFalse: [	self fillBuffer ].
	^self endOfLastPart</body>

<body package="MIME" selector="next">next

	| c | 
	stream atEnd ifTrue: [self pastEnd].
	c := buffer first. 
	1 to: buffer size - 1 do: [ :ind | buffer at: ind put: (buffer at: ind + 1) ].
	buffer at: buffer size put: stream next.
	self boundary = buffer ifTrue: [self skipToEnd ].

	^c</body>
</methods>

<methods>
<class-id>Net.BoundedReadStream</class-id> <category>private</category>

<body package="MIME" selector="fillBuffer">fillBuffer

	stream atEnd ifTrue: [self pastEnd].

	[ buffer :=  stream next: self boundary size ] 
		on: Stream incompleteNextCountSignal
		do: [:ex |  self pastEnd ].

	atEnd := (self boundary = buffer).
	atEnd ifTrue: [self skipToEnd].</body>

<body package="MIME" selector="skipToEnd">skipToEnd

	| bytes |
	stream atEnd ifTrue: [ ^self pastEnd ].

	[ bytes := stream next: 2 ]
		on: Stream incompleteNextCountSignal
		do: [:ex | ^self pastEnd ].

	bytes  =  self lineEnd ifTrue: [ partNumber := partNumber + 1. atEnd := true ].
	bytes = self boundaryDashes 
		ifTrue: [ stream atEnd ifFalse: [ stream next: 2.].
				atEnd := endOfLastPart := true ].</body>
</methods>

<methods>
<class-id>Net.BoundedReadStream</class-id> <category>initialize-release</category>

<body package="MIME" selector="initializeOn:boundary:">initializeOn: aStream boundary: aString

	super initializeOn: aStream boundary: aString.

	endOfLastPart := false.

	[buffer := stream next: self boundary size.
	]	on: EndOfStreamNotification
		do: [ :ex | "resignal the notification as hard error"
			ex resignalAs: (
				IncompleteNextCountError new
					messageText: ex messageText;
					parameter: self boundary size
					yourself ) ].
	atEnd := (boundary = buffer).

	atEnd 
		ifTrue: [self skipToEnd. self fillBuffer]
		ifFalse: 
			["First boundary right after the line end that separates the message header and body "
			(buffer copyFrom: 1 to: buffer size - 2) = (self boundary copyFrom: 3 to: self boundary size) 
				ifTrue: [partNumber := 1. self fillBuffer ]
				ifFalse: [ self skipUpToFirstBoundary]].

	atEnd := (boundary = buffer).</body>

<body package="MIME" selector="skipUpToFirstBoundary">skipUpToFirstBoundary

	"Skip everything before the first boundary "
	[self atEnd ] whileFalse: [self next].
	partNumber := 0.
	self fillBuffer.</body>
</methods>

<methods>
<class-id>Net.InvalidHeaderField</class-id> <category>private</category>

<body package="MIME" selector="defaultResumeValue">defaultResumeValue

	^self parameter asString</body>
</methods>

<methods>
<class-id>Net.UnparsedBody</class-id> <category>mutating</category>

<body package="MIME" selector="newBody">newBody

	| body |
	body := (self isMultipart
		ifTrue: [MultipartBody]
		ifFalse: [SimpleBody] ) new.

	source ifNotNil: [ body source: source].
	self parent body: body.
	^body</body>
</methods>

<methods>
<class-id>Net.UnparsedBody</class-id> <category>private</category>

<body package="MIME" selector="getValue">getValue

	^nil</body>
</methods>

<methods>
<class-id>Net.UnparsedBody</class-id> <category>printing</category>

<body package="MIME" selector="printForDisplayOn:">printForDisplayOn: aStream
	
	self printOn: aStream indent: nil</body>

<body package="MIME" selector="printOn:indent:">printOn: aStream indent: level
	
	| string |
	string := 'Unparsed body'.
	self isSimple ifTrue: [string := 'Unparsed simple body'].
	self isMultipart ifTrue: [string := 'Unparsed multipart body'].
	
	aStream nextPutAll: string</body>
</methods>

<methods>
<class-id>Net.UnparsedBody</class-id> <category>testing</category>

<body package="MIME" selector="isMultipart">isMultipart
	^self parent notNil
		ifTrue: [ self parent isMultipart ]
		ifFalse: [false]</body>

<body package="MIME" selector="isSimple">isSimple
	^self parent notNil
		ifTrue: [ self parent isSimple ]
		ifFalse: [false]</body>
</methods>

<methods>
<class-id>Net.UnparsedBody</class-id> <category>writing</category>

<body package="MIME" selector="writeContentsOn:">writeContentsOn: aStream
	^self shouldNotImplement</body>
</methods>

<methods>
<class-id>Net.MimeDispatcher</class-id> <category>visiting -- double dispatching</category>

<body package="MIME" selector="acceptHeaderField:with:">acceptHeaderField: aHeaderField with: argument

	^aHeaderField</body>

<body package="MIME" selector="acceptMessage:with:">acceptMessage: aMessage with: argument

	(self handler shouldStartMessage: aMessage)
		ifTrue: [ 	self handler startMessage: aMessage.

				[aMessage header acceptVisit: self  with: argument.
				(self handler shouldStartBody: aMessage body) 
					ifTrue: [self handler startMessageBody: aMessage.
							self handler 
								processMessageBody:  aMessage 
								do: [aMessage body acceptVisit: self  with: argument	]]
					ifFalse: [self handler finishEmptyBodyMessage: aMessage ]
				] ensure: [ self handler endMessage: aMessage ] ].

	^aMessage</body>

<body package="MIME" selector="acceptMessageHeader:with:">acceptMessageHeader: aMessageHeader with: argument 
	
	[self handler startHeader: aMessageHeader 
	]  ensure: [self handler endHeader: aMessageHeader].

	^aMessageHeader</body>

<body package="MIME" selector="acceptMimeEntity:with:">acceptMimeEntity: aMimeEntity  with: argument

	(self handler shouldStartEntity: aMimeEntity) ifTrue: [
		[	self handler startEntity: aMimeEntity.
			aMimeEntity header acceptVisit: self with: argument.
			aMimeEntity body acceptVisit: self with: argument.
		] ensure: [self handler endEntity:  aMimeEntity] ].
	^aMimeEntity</body>

<body package="MIME" selector="acceptMultipartBody:with:">acceptMultipartBody: aMultipartBody with: argument

	(self handler shouldStartBody: aMultipartBody) 
		ifTrue:
			[self handler startMultipartBody: aMultipartBody .
			[aMultipartBody parts do: 
				[ :part | 
				self handler startPart:part.
				part acceptVisit: self with: nil.
				self handler endPart: part ].
			] ensure: [self handler endMultipartBody: aMultipartBody]].
	
	^aMultipartBody</body>

<body package="MIME" selector="acceptSimpleBody:with:">acceptSimpleBody: aSimpleBody  with: argument

	(self handler shouldStartBody: aSimpleBody) 
		ifTrue: 
			[ [self handler startSimpleBody: aSimpleBody ]
				ensure: [self handler endSimpleBody: aSimpleBody]].
		
	^aSimpleBody</body>
</methods>

<methods>
<class-id>Net.MimeDispatcher</class-id> <category>visiting</category>

<body package="MIME" selector="visit:with:">visit: aMessageElement with: argument
	^aMessageElement notNil 
		ifTrue: [aMessageElement acceptVisit: self with: argument]
		ifFalse: [nil]</body>
</methods>

<methods>
<class-id>Net.MimeDispatcher</class-id> <category>initialization</category>

<body package="MIME" selector="initialize">initialize</body>
</methods>

<methods>
<class-id>Net.MimeDispatcher</class-id> <category>accessing</category>

<body package="MIME" selector="handler">handler
	^handler</body>

<body package="MIME" selector="handler:">handler: aMessageHandler
	handler := aMessageHandler</body>
</methods>

<methods>
<class-id>Net.MimeDispatcher class</class-id> <category>instance creation</category>

<body package="MIME" selector="handler:">handler: aMessageHandler
	^self new
		handler: aMessageHandler</body>

<body package="MIME" selector="new">new
	^super new initialize</body>
</methods>

<methods>
<class-id>Net.MimeScanner</class-id> <category>tokenization</category>

<body package="MIME" selector="nextToken">nextToken
	^self nextMimeToken</body>

<body package="MIME" selector="specials">specials
" This method is provided to encapsulate lexical differences between RFC822 on one side, and MIME, HTTP on the other side. MIME definiton of 'tspecials' is the same as the RFC 822 definition of ''specials' with the addition of the three characters &lt;/&gt;, &lt;?&gt;, and &lt;=&gt;, and the removal of &lt;.&gt;. To present uniform tokenization interface, this method is overridden in Mime scanner "

	^self class tspecials</body>
</methods>

<methods>
<class-id>Net.MimeScanner</class-id> <category>multi-character scans</category>

<body package="MIME" selector="scanQuotedText">scanQuotedText
"RFC 2183: `tspecials' and `value' are defined according to RFC 2045. 
RFC2045 defines
     parameter := attribute '=' value
     value := token / quoted-string
     token := 1*&lt;any (US-ASCII) CHAR except SPACE, CTLs, or tspecials&gt;
     tspecials :=  (  ) &lt;  &gt;  @  ,  ;  :  \   /  [ ] ? =     - Must be in quoted-string  to use within parameter values
"

	" We are positioned at the first double quote character "

	token := self 
				scanToken: [ self matchCharacterType: MimeQuotedTextMask ] 
				delimitedBy: '""' 
				notify: (#UnmatchedQuotedText &lt;&lt; #net &gt;&gt; 'Unmatched quoted text').

	tokenType := #quotedText. 
	^token.</body>

<body package="MIME" selector="scanToBoundary:">scanToBoundary: boundary
" Scan for specified boundary (RFC2046, p5.1). Answer scanned text from current position up to the beginning of the boundary. Stream is positioned after end of the boundary line
Second element is either #next or #last. #next means the boundary found is not the last one. #last means the boundary is the closing boundary for the multi-part body (that is, it looks like '--&lt;boundary&gt;--) "

	| string |

	string := self upToAll: (self boundaryDashes , boundary).
	self upTo: Character cr.					" Skip to the end of line "
	^string.</body>

<body package="MIME" selector="scanToken">scanToken
" MIME and HTTP: token  =  1*&lt;any CHAR except tspecials, SPACE and CTLs&gt;. That is, 'token' is analogous to RFC822 'atom' except set of Mime's set of tspecials characters includes three more characters as compared to set of 'specials' in RFC822"

	token := self scanTokenMask: TokenMask.
	tokenType := #token.
	^token</body>
</methods>

<methods>
<class-id>Net.MimeScanner</class-id> <category>printing</category>

<body package="MIME" selector="printPhrase:on:">printPhrase: phrase on: stream
	self class printPhrase: phrase on: stream</body>
</methods>

<methods>
<class-id>Net.MimeScanner</class-id> <category>constants</category>

<body package="MIME" selector="boundaryDashes">boundaryDashes
	^MultipartBody boundaryDashes</body>
</methods>

<methods>
<class-id>Net.MimeScanner</class-id> <category>private</category>

<body package="MIME" selector="nextMimeToken">nextMimeToken
" MIME and HTTP: token  =  1*&lt;any CHAR except tspecials, SPACE and CTLs&gt;. That is, 'token' is analogous to RFC822 'atom' except set of Mime's set of tspecials characters includes three more characters as compared to set of 'specials' in RFC822"
	| char |
	self skipWhiteSpace.
	char := self peek.
	char isNil				"end of input"
		ifTrue: [tokenType := #doIt.
			^token := nil].
	char == $( ifTrue: [^self scanComment; nextToken].
	char == $" ifTrue: [^self scanQuotedText].
	(self specials includes: char)
		ifTrue: [tokenType := #special.		" Special character. Make it token value and set token type "
				^token := self next.].
	^self privateScanTokenMask: TokenMask</body>
</methods>

<methods>
<class-id>Net.MimeScanner class</class-id> <category>testing</category>

<body package="MIME" selector="isTokenChar:">isTokenChar: char
	^((self classificationMaskFor: char ) bitAnd: TokenMask) ~= 0</body>

<body package="MIME" selector="needsTransferEncoding:mimeType:">needsTransferEncoding: aStream mimeType: aMimeTypeDescriptor
	"Answer true if the object needs to be transfer-encoded because it contains 
	8-bit values or lines are too long, etc. 
	For now on, answer true for non-binary streams. Scan character strings looking for 8-bit characters 
	and answer true if one is found. Otherwise answer false"
	| savePos |
	aMimeTypeDescriptor isBinary ifTrue: [ ^true ].
	((aStream respondsTo: #isBinary) and: [aStream isBinary])
		ifTrue: [^true].

	savePos := aStream position.
	[aStream reset.
	[aStream atEnd]
		whileFalse: [aStream next asInteger &gt; 127 ifTrue: [^true]]]
		ensure: [aStream position: savePos].
	^false</body>

<body package="MIME" selector="shouldBeQuoted:">shouldBeQuoted: string
	^(string detect: [ :char | (self isTokenChar: char) not ] ifNone: [ nil ]) notNil</body>
</methods>

<methods>
<class-id>Net.MimeScanner class</class-id> <category>printing</category>

<body package="MIME" selector="printPhrase:on:">printPhrase: phrase on: stream
	MimeEncodedWordCoDec decodePhrase: phrase printOn: stream</body>

<body package="MIME" selector="printToken:tokenType:on:">printToken: value tokenType: aSymbol on: stream
	aSymbol == #special 
		ifTrue: [stream nextPut: value]
		ifFalse: 
			[aSymbol == #token
				ifTrue: [stream nextPutAll: value]
				ifFalse: [stream nextPutAll: '"', value, '"' ]]</body>
</methods>

<methods>
<class-id>Net.MimeScanner class</class-id> <category>class initialization</category>

<body package="MIME" selector="initClassificationTable">initClassificationTable
	super initClassificationTable.
	self initClassificationTableWith: MimeQuotedTextMask when:
		[:c | c ~~ $" and: [ c ~~ Character cr ]].</body>

<body package="MIME" selector="initialize">initialize
	" MimeScanner initialize "

	self 
		initializeConstants; 
		initClassificationTable;
		initializeBase64Decoder</body>

<body package="MIME" selector="initializeBase64Decoder">initializeBase64Decoder
	" (MimeScanner initializeBase64Decoder )"
	codeChars := 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'.
	base64Decoder := (0 to: 255) collect: [:n| (codeChars indexOf: (n + 1) asCharacter) - 1].
	base64Decoder replaceAll: -1 with: 0.</body>

<body package="MIME" selector="initializeConstants">initializeConstants
	MimeQuotedTextMask := 32768.</body>
</methods>

<methods>
<class-id>Net.MimeScanner class</class-id> <category>deprecated</category>

<body package="MIME" selector="decodeBase64From:to:">decodeBase64From: inStream to: outStream
	"Decode input stream from base64 to output stream. outStream is an encoded stream "

	| string |
	[inStream atEnd]
		whileFalse: 
			[(string := inStream upTo: Character cr) notEmpty
				ifTrue: [	self decodeBase64From: 1 to: string size in: string on: outStream]].</body>

<body package="MIME" selector="decodeBase64From:to:in:">decodeBase64From: startIndex to: endIndex in: aString

	| output data |
	output := (data := ByteString new: endIndex - startIndex * 3 // 4) writeStream.
	self decodeBase64From: startIndex to: endIndex in: aString on: output.
	^data copyFrom: 1 to: output position.</body>

<body package="MIME" selector="decodeBase64From:to:in:on:">decodeBase64From: startIndex to: endIndex in: aString on: output
	"Decode aString from startIndex to endIndex in base64." 

	|   index cr end limit padding sz |
	index := startIndex.
	cr := Character cr.
	"There is padding at the end of a base64 message if the content is not a multiple of
	 3 bytes in length.  The padding is either two ='s to pad-out a trailing byte, 1 = to
	 pad out a trailing pair of bytes, or no padding.  Here we count the padding.  After
	 processing the message we cut-back by the amount of padding."

	end := endIndex min: (sz := aString size).
	[( (codeChars, (String with: $=) ) includes: (aString at: end))
		and: [end = endIndex or: [(aString at: end + 1) = cr]]] 
			whileFalse: [end := end - 1].

	limit := end.
	padding := 0.
	[(aString at: end) == $=] whileTrue: [padding := padding - 1. end := end - 1].
	[index &lt;= limit] whileTrue:
		[| triple |
		triple :=    ((base64Decoder at: (aString at: index     ) asInteger) bitShift: 18)
				+ ((base64Decoder at: (aString at: index + 1) asInteger) bitShift: 12)
				+ ((base64Decoder at: (aString at: index + 2) asInteger) bitShift:  6)
				+  (base64Decoder at: (aString at: index + 3) asInteger).
		output nextPut: (triple digitAt: 3).
		output nextPut: (triple digitAt: 2).
		output nextPut: (triple digitAt: 1).
		index := index + 4.
		[(index &gt; sz or: [(aString at: index) = cr])
		and: [index &lt;= limit]] whileTrue: [index := index + 1]].
	padding ~= 0 ifTrue: [output skip: padding].
	^output</body>

<body package="MIME" selector="decodeQuotedPrintableFrom:">decodeQuotedPrintableFrom: input
	"Decode aString from startIndex to endIndex in quoted-printable."
	^self decodeQuotedPrintableFrom: input on: (ByteString new: input size ) writeStream.</body>

<body package="MIME" selector="decodeQuotedPrintableFrom:on:">decodeQuotedPrintableFrom: inputStream on: outputStream
	"Decode aString from startIndex to endIndex in quoted-printable."

	|  char n1 n2 |
	[inputStream atEnd] whileFalse:
		[inputStream position = 498 ifTrue: [self halt].
		char := inputStream next.
		$= == char
			ifTrue:
				[('0123456789ABCDEF' includes: (n1 := inputStream next)) 
					ifTrue:
						[n2 := inputStream next.
						outputStream nextPut: ((n1 digitValue bitShift: 4) + n2 digitValue) asCharacter]]
					ifFalse: 	[outputStream nextPut: char]].
	^outputStream contents</body>

<body package="MIME" selector="decodeQuotedPrintableFrom:to:in:">decodeQuotedPrintableFrom: startIndex to: endIndex in: aString
	"Decode aString from startIndex to endIndex in quoted-printable."

	^self decodeQuotedPrintableFrom: (ReadStream on: aString from: startIndex to: endIndex)</body>

<body package="MIME" selector="decodeUUEncodedFrom:to:in:">decodeUUEncodedFrom: startIndex to: farEndIndex in: aString
	"decode aString from startIndex to farEndIndex as uuencode-encoded"
	| endIndex i cr space output data |
	endIndex := farEndIndex - 2.
	[endIndex &lt;= startIndex
	or: [(aString at: endIndex + 1) = $e
		and: [(aString at: endIndex + 2) = $n
		and: [(aString at: endIndex + 3) = $d]]]]
		whileFalse:
			[endIndex := endIndex - 1].
	i := (aString
			findString: 'begin'
			startingAt: startIndex
			ignoreCase: true
			useWildcards: false) first.
	i = 0 ifTrue: [i := startIndex].
	cr := Character cr.
	space := Character space asInteger.
	output := (data := ByteString new: endIndex - startIndex * 3 // 4) writeStream.
	[[i &lt; endIndex
	and: [(aString at: i) ~= cr]] whileTrue: [i := i + 1].
	i &lt; endIndex] whileTrue:
		[| count |
		count := (aString at: (i := i + 1)) asInteger - space bitAnd: 8r77.
		i := i + 1.
		count = 0
			ifTrue: [i := endIndex]
			ifFalse:
				[[count &gt; 0] whileTrue:
					[| m n o p |
					m := (aString at: i) asInteger - space bitAnd: 8r77.
					n := (aString at: i + 1) asInteger - space bitAnd: 8r77.
					o :=  (aString at: i + 2) asInteger - space bitAnd: 8r77.
					p :=  (aString at: i + 3) asInteger - space bitAnd: 8r77.
					count &gt;= 1 ifTrue:
						[output nextPut: (Character value: (m bitShift: 2) + (n bitShift: -4)).
						count &gt;= 2 ifTrue:
							[output nextPut: (Character value: ((n bitShift: 4) + (o bitShift: -2) bitAnd: 16rFF)).
							count &gt;= 3 ifTrue:
								[output nextPut: (Character value: ((o bitShift: 6) + p bitAnd: 16rFF))]]].
					i := i + 4.
					count := count - 3]]].
	^data copyFrom: 1 to: output position</body>

<body package="MIME" selector="encode:asBase64To:">encode: inStream asBase64To: outStream
	"Encode the input as base64 to outStream."
	| buffer lineLength count end column a b c in convertToByteArray chunkSize temp |
	column := 0.

	" Ugly code to ensure the the underlying array is bytes "
	in := inStream.
	[in binary] on: Error do: [ :ex | ex return].

	convertToByteArray := in contentsSpecies includesBehavior: String.
	lineLength := 76.
	chunkSize := (lineLength / 4 * 3) asInteger.
	[convertToByteArray
		ifTrue: 
			[temp := in nextAvailable: chunkSize.
			buffer := temp asByteArrayEncoding: temp stringEncoding streamEncodingType.
			count := buffer size]
		ifFalse:
			[buffer := ByteArray new: chunkSize.
			count := buffer size.
			[in next: buffer size into: buffer startingAt: 1]
				on: Stream incompleteNextCountSignal
				do: [:ex| count := ex parameter. ex return]].
	end := count // 3 * 3.
	1 to: end by: 3 do:
		[:i|
		(column := column + 4) &gt; lineLength ifTrue:
			[outStream cr. column := 0].
		a := buffer at: i.
		b := buffer at: i + 1.
		c := buffer at: i + 2.
		outStream
			nextPut: (codeChars at: (a bitShift: -2) + 1);
			nextPut: (codeChars at: (((a bitShift: 4) + (b bitShift: -4)) bitAnd: 16r3F) + 1);
			nextPut: (codeChars at: (((b bitShift: 2) + (c bitShift: -6)) bitAnd: 16r3F) + 1);
			nextPut: (codeChars at: (c bitAnd: 16r3F) + 1)].
	(column := column + 4) &gt; lineLength ifTrue:
		[outStream cr. column := 0].
	count - end = 1 ifTrue: "one byte left to write"
		[a := buffer at: end + 1.
		 outStream
			nextPut: (codeChars at: (a bitShift: -2) + 1);
			nextPut: (codeChars at: ((a bitShift: 4) bitAnd: 16r3F) + 1);
			nextPut: $=;
			nextPut: $=].
	count - end = 2 ifTrue: "two bytes left to write"
		[a := buffer at: end + 1.
		 b := buffer at: end + 2.
		 outStream
			nextPut: (codeChars at: (a bitShift: -2) + 1);
			nextPut: (codeChars at: (((a bitShift: 4) + (b bitShift: -4)) bitAnd: 16r3F) + 1);
			nextPut: (codeChars at: ((b bitShift: 2) bitAnd: 16r3F) + 1);
			nextPut: $=].
	count &gt; 0] whileTrue</body>

<body package="MIME" selector="encode:asQuotedPrintableTo:">encode: inStream asQuotedPrintableTo: outStream
	"Encode the input as printed-quotable to outStream.  Ensure that leading '.'
	 characters are encoded so that mailers which interpret '.' at the beginning
	 of line as an end-of-message indicator are not confused by '.'s in the body
	 of a message."
	| cr tab spaceCode encoder column ch code |
	cr := Character cr.
	tab := Character tab.
	spaceCode := Character space asInteger.
	encoder := '0123456789ABCDEF'.
	column := 0.
	[(ch := inStream next) == nil] whileFalse:
		[ch = cr
			ifTrue:
				[outStream nextPut: cr.
				 column := 0]
			ifFalse:
				[(column := column + 1) &gt;= 76 ifTrue:
					[outStream nextPut: $=; nextPut: Character cr. column := 1].
				((code := ch asInteger) &lt; 127
				  and: 
					[
						((code &gt;= spaceCode or: [ch == tab])
						  and: [ch ~~ $. or: [column &gt; 1]]) 
							and: [ ch ~~ $_ ]
					]
				)
					ifTrue:
						[outStream nextPut: ch]
					ifFalse:
						[outStream
							nextPut: $=;
							nextPut: (encoder at: (code bitShift: -4) + 1);
							nextPut: (encoder at: (code bitAnd: 16rF) + 1).
						column := column + 2]]]</body>
</methods>

<methods>
<class-id>Net.ContentTypeField</class-id> <category>accessing</category>

<body package="MIME" selector="boundary">boundary
	^self parameterAt: 'boundary'</body>

<body package="MIME" selector="boundary:">boundary: aString
	^self parameterAt: 'boundary' put: aString</body>

<body package="MIME" selector="charset">charset
	| charset |
	charset := self parameterAt: 'charset' ifAbsent: [nil]. 
	^(charset == nil
		ifTrue: 
			[ ('text*' match: self contentType)
				ifTrue: [ self class defaultCharset]
				ifFalse: [ nil ]]
		ifFalse: [ charset asLowercase ])</body>

<body package="MIME" selector="charset:">charset: aName

	^aName ~~ nil
		ifTrue: [self parameterAt: 'charset' put: aName ]
		ifFalse: [ nil]</body>

<body package="MIME" selector="charsetIfNone:">charsetIfNone: aBlock
	| v |
	v := self parameterAt: 'charset' ifAbsent: [aBlock value].
	^v notNil
		ifTrue: [ v asLowercase ]
		ifFalse: [ nil ]</body>

<body package="MIME" selector="contentType">contentType

	^self localValue</body>

<body package="MIME" selector="contentType:">contentType: aString
	self localValue:  aString</body>

<body package="MIME" selector="fileName">fileName
	^(self parameterAt: 'name' ifAbsent: [^String new ]) asLowercase</body>

<body package="MIME" selector="fileName:">fileName: aName
	^self parameterAt: 'name' put: aName</body>

<body package="MIME" selector="subtype">subtype
	^(self contentType tokensBasedOn: $/) last.</body>

<body package="MIME" selector="subtype:">subtype: aString
	self type: self type subtype: aString.</body>

<body package="MIME" selector="type">type
	^(self contentType tokensBasedOn: $/) first.</body>

<body package="MIME" selector="type:">type: aString
	self type: aString subtype: self subtype</body>

<body package="MIME" selector="type:subtype:">type: typeString subtype: subtypeString
	self  localValue: (typeString, '/', subtypeString)</body>
</methods>

<methods>
<class-id>Net.ContentTypeField</class-id> <category>testing</category>

<body package="MIME" selector="isMultipart">isMultipart
	^self type = 'multipart'</body>
</methods>

<methods>
<class-id>Net.ContentTypeField</class-id> <category>parsing</category>

<body package="MIME" selector="validateValue:">validateValue: scanner
"The charset value should be quoted string but the Microsoft-IIS/6.0 server produces:
Content-type: text/xml;charset='UTF-8' 
This hack is trying to set correct charset from the wrong parameter.

Another server with another bug:
Server: Yaws/1.56 Yet Another Web Server &gt;&gt;  'Content-Type: text/xml;charset=' 
Remove this empty charset!"
	| cs |
	super validateValue: scanner.
	cs := self  parameterAt: 'charset' ifAbsent: [^nil].

	(cs isNil or: [cs isEmpty]) ifTrue: [self element removeParamAt: 'charset'. ^nil].

	(cs first = $' and: [ cs last = $' ])
		ifTrue: [self charset: ( cs copyFrom: 2 to: cs size - 1) ]</body>
</methods>

<methods>
<class-id>Net.ContentTypeField class</class-id> <category>parsing</category>

<body package="MIME" selector="fieldNames">fieldNames
	^#('content-type')</body>
</methods>

<methods>
<class-id>Net.ContentTypeField class</class-id> <category>instance creation</category>

<body package="MIME" selector="applicationOctetStream">applicationOctetStream
	" self applicationOctetStream "
	^self contentType: MimeTypeDescriptor contentTypeApplicationOctetStream</body>

<body package="MIME" selector="contentType:">contentType: aString
	^self contentType: aString charset: nil</body>

<body package="MIME" selector="contentType:charset:">contentType: aString charset: cString
	^self new 
		name: 'content-type';
		contentType: aString;
		charset: cString;
		yourself</body>

<body package="MIME" selector="default">default
	^self iso8859x1Default</body>

<body package="MIME" selector="iso8859x1Default">iso8859x1Default
	^self new
		name: 'content-type';
		contentType: self defaultContentType;
		charset: self iso8859x1;
		yourself</body>

<body package="MIME" selector="messageRfc822">messageRfc822
	" self messageRfc822 "
	^self contentType: MimeTypeDescriptor contentTypeMessageRfc822 charset: self defaultCharset</body>

<body package="MIME" selector="textPlain">textPlain
	" self textPlain "
	^self contentType: MimeTypeDescriptor contentTypePlainText charset: self defaultCharset</body>

<body package="MIME" selector="usAsciiDefault">usAsciiDefault
	^self new
		name: 'content-type';
		contentType: self defaultContentType;
		charset: self usAscii;
		yourself</body>
</methods>

<methods>
<class-id>Net.ContentTypeField class</class-id> <category>defaults</category>

<body package="MIME" selector="defaultCharset">defaultCharset
	^self iso8859x1</body>

<body package="MIME" selector="defaultContentType">defaultContentType
	^MimeTypeDescriptor defaultContentTypeString</body>

<body package="MIME" selector="defaultSeparators">defaultSeparators
	^#($; $; nil  )</body>

<body package="MIME" selector="defaultUnsupportedContentType">defaultUnsupportedContentType
	^MimeTypeDescriptor defaultUnsupportedContentType</body>

<body package="MIME" selector="iso8859x1">iso8859x1
	^MimeTypeDescriptor iso8859x1</body>

<body package="MIME" selector="usAscii">usAscii
	^MimeTypeDescriptor usAscii</body>
</methods>

<methods>
<class-id>Net.ContentDispositionField</class-id> <category>accessing</category>

<body package="MIME" selector="disposition">disposition
	^self localValue</body>

<body package="MIME" selector="disposition:">disposition: aString
" Disposition should be 'inline' or 'attachment' "
	self localValue: aString</body>

<body package="MIME" selector="fileName">fileName
	^self parameterAt: 'filename'</body>

<body package="MIME" selector="fileName:">fileName: aString
	^self parameterAt: 'filename' put: aString</body>

<body package="MIME" selector="size">size
	Integer readFrom: (self parameterAt: 'size' ifAbsent: [^nil]) readStream</body>

<body package="MIME" selector="size:">size: anInteger
	self parameterAt: 'size' put: anInteger printString</body>
</methods>

<methods>
<class-id>Net.ContentDispositionField</class-id> <category>testing</category>

<body package="MIME" selector="isAttachment">isAttachment
" Answer true if content disposition is 'attachments' "
	^self disposition asLowercase = 'attachment'</body>

<body package="MIME" selector="isFormData">isFormData
	^self disposition asLowercase = 'form-data'</body>

<body package="MIME" selector="isInline">isInline
" Answer true if content disposition is 'inline' "
	^self disposition asLowercase = 'inline'</body>
</methods>

<methods>
<class-id>Net.ContentDispositionField class</class-id> <category>parsing</category>

<body package="MIME" selector="fieldNames">fieldNames
	^#('content-disposition')</body>
</methods>

<methods>
<class-id>Net.ContentDispositionField class</class-id> <category>defaults</category>

<body package="MIME" selector="defaultContentDispositionValue">defaultContentDispositionValue
	^#attachment</body>

<body package="MIME" selector="defaultSeparators">defaultSeparators
	^#($; $; nil  )</body>
</methods>

<methods>
<class-id>Net.ContentDispositionField class</class-id> <category>instance creation</category>

<body package="MIME" selector="default">default
	^self fromLine: 'Content-disposition: ', self defaultContentDisposition</body>

<body package="MIME" selector="formData">formData
	^self fromLine: 'Content-disposition: ', 'form-data'.</body>
</methods>

<methods>
<class-id>Net.ContentDispositionField class</class-id> <category>accessing</category>

<body package="MIME" selector="defaultContentDisposition">defaultContentDisposition
	^DefaultContentDisposition isNil
		ifTrue: [ DefaultContentDisposition := self defaultContentDispositionValue]
		ifFalse: [DefaultContentDisposition ]</body>

<body package="MIME" selector="defaultContentDisposition:">defaultContentDisposition: aString
	DefaultContentDisposition := aString</body>
</methods>

<methods>
<class-id>Net.MimeParserHandler</class-id> <category>tasks</category>

<body package="MIME" selector="on:">on: aStream

	stream := aStream</body>

<body package="MIME" selector="read:from:">read: aMimeEntity from: aStream

	^self 
		setEntity: aMimeEntity;
		parse:  aStream</body>

<body package="MIME" selector="readBody">readBody

	initialEntity ifNil: [ ^self error: #ThereIsNoEntity &lt;&lt; #net &gt;&gt; 'There is no entity created to parse the body. Use #readHeader to create an entity before parsing the body' ].
	self headerOnly: false.
	self parser parseMessageBody: initialEntity from: stream.
	^initialEntity</body>

<body package="MIME" selector="readFrom:">readFrom: aStream

	^self parse:  aStream</body>

<body package="MIME" selector="readHeader">readHeader

	stream ifNil: [ ^self error: #ThereIsNoStream &lt;&lt; #net &gt;&gt; 'There is no stream specified to parse the header. Use #on: to set the stream' ].
	^self readHeaderFrom: stream</body>

<body package="MIME" selector="readHeaderFor:from:">readHeaderFor: aMimeEntity from: aStream

	self setEntity: aMimeEntity.
	^self readHeaderFrom: aStream</body>

<body package="MIME" selector="readHeaderFrom:">readHeaderFrom: aStream

	self headerOnly: true.
	^self parse: aStream</body>
</methods>

<methods>
<class-id>Net.MimeParserHandler</class-id> <category>accessing</category>

<body package="MIME" selector="acceptNonAsciiCharacters">acceptNonAsciiCharacters

	^acceptNonAsciiCharacters ifNil: [self scannerType acceptNonAsciiCharacters]</body>

<body package="MIME" selector="acceptNonAsciiCharacters:">acceptNonAsciiCharacters: aBoolean

	acceptNonAsciiCharacters := aBoolean</body>

<body package="MIME" selector="fieldFactory">fieldFactory
" Answer field factory or default field factory if none is specified. Different protocols may have different field factories  "

	^fieldFactory ifNil: [fieldFactory := self defaultFieldFactory ]</body>

<body package="MIME" selector="fieldFactory:">fieldFactory: anObject
" Set field factory. Different protocols may have different field factories. Field factory should respond to messages:
	readFrom: 
	readNameFrom:
	scannerType
	fieldClassForName:
"
	fieldFactory := anObject</body>

<body package="MIME" selector="headerOnly">headerOnly
	^headerOnly</body>

<body package="MIME" selector="headerOnly:">headerOnly: aBoolean 
	headerOnly := aBoolean</body>

<body package="MIME" selector="messageHeaderClass">messageHeaderClass
" Answer message header class "

	^messageHeaderClass ifNil: [ messageHeaderClass := self defaultMessageHeaderClass ]</body>

<body package="MIME" selector="messageHeaderClass:">messageHeaderClass: aClass
" Set message header class "

	messageHeaderClass := aClass</body>

<body package="MIME" selector="parser">parser
	^parser isNil 
		ifTrue: [parser := self defaultParser]
		ifFalse: [parser]</body>

<body package="MIME" selector="parser:">parser: aMessageDispatcher 
	aMessageDispatcher handler: self.
	parser := aMessageDispatcher</body>

<body package="MIME" selector="saveAttachmentsAsFiles">saveAttachmentsAsFiles

	^saveAttachmentsAsFiles ifNil: [saveAttachmentsAsFiles := self class saveAttachmentsAsFiles]</body>

<body package="MIME" selector="saveAttachmentsAsFiles:">saveAttachmentsAsFiles: aBoolean

	saveAttachmentsAsFiles := aBoolean</body>

<body package="MIME" selector="scannerType">scannerType

	^scannerType ifNil: [ scannerType := self defaultScannerType ]</body>

<body package="MIME" selector="scannerType:">scannerType: aClass

	scannerType :=  aClass</body>
</methods>

<methods>
<class-id>Net.MimeParserHandler</class-id> <category>defaults</category>

<body package="MIME" selector="defaultEncoding">defaultEncoding
	^#ISO8859_1</body>

<body package="MIME" selector="defaultFieldFactory">defaultFieldFactory
	^HeaderField</body>

<body package="MIME" selector="defaultMessageHeaderClass">defaultMessageHeaderClass
	^MessageHeader</body>

<body package="MIME" selector="defaultParser">defaultParser
	^self parserClass handler: self</body>

<body package="MIME" selector="defaultPartEntityType">defaultPartEntityType
	^MimeEntity</body>

<body package="MIME" selector="defaultRootEntityType">defaultRootEntityType
	^MimeEntity</body>

<body package="MIME" selector="defaultScannerType">defaultScannerType
	^MimeScanner</body>

<body package="MIME" selector="parserClass">parserClass
	^MimeParser</body>
</methods>

<methods>
<class-id>Net.MimeParserHandler</class-id> <category>private prepare streams</category>

<body package="MIME" selector="asEncodedStream:">asEncodedStream: aStream

	"To provide skip  back we use  CachedReadStream"
	^(EncodedStream on: ( CachedReadStream wrap: aStream cache: 100 )
		encodedBy: (StreamEncoder new: self defaultEncoding))
		lineEndConvention: lineEndConvention.</body>

<body package="MIME" selector="openBodyStream:">openBodyStream: aMessageBody

	aMessageBody byteSource:  (ReadWriteStream on: (ByteArray new: 1024)).
	^aMessageBody byteSource</body>

<body package="MIME" selector="prepareStream:">prepareStream: aStream 

	^stream := (aStream 
					asEncodedStreamWithEncoding: self defaultEncoding 
					lineEndConvention: LineEndCRLF )
				lineEndCRLF;
				text.</body>

<body package="MIME" selector="prepareStream:forMessageBody:">prepareStream: aStream forMessageBody: anEntity

	^stream</body>

<body package="MIME" selector="prepareStream:forMultipartBody:">prepareStream: aStream forMultipartBody: aMultipartBody

	^BoundedReadStream on: aStream boundary: aMultipartBody boundary.</body>

<body package="MIME" selector="prepareStream:forSimpleBody:">prepareStream: aStream forSimpleBody: aBody

	^aStream</body>

<body package="MIME" selector="setAttachmentSource:to:">setAttachmentSource: aBody to: aStream

	"The attachment was not saved into an external file  "
	aBody byteSource ifNotNil: [^aBody].

	" Setting the body source to an external stream "
	aBody source: aStream.
	aBody source
		text;
		lineEndAuto;
		close.</body>
</methods>

<methods>
<class-id>Net.MimeParserHandler</class-id> <category>action callbacks</category>

<body package="MIME" selector="addHeader:toEntity:">addHeader: aMessageHeader toEntity: anEntity
	| ent |
	ent := anEntity isNil
		ifTrue: [ self defaultPartEntityType new ]
		ifFalse: [anEntity].
	self setEntity: ent.
	ent header: aMessageHeader.
	^ent</body>

<body package="MIME" selector="endBody:">endBody: aMessageBody 
	
	(aMessageBody isSimple and: [aMessageBody byteSource notNil ])
		ifTrue: [ aMessageBody byteSource close ].

	^aMessageBody parent</body>

<body package="MIME" selector="endHeader:">endHeader: aHeader

	^aHeader</body>

<body package="MIME" selector="endMessage:">endMessage: aMessage

	"restore the stream original mode"
	binaryMode ifTrue: [ stream binary] ifFalse: [stream text].
	^aMessage</body>

<body package="MIME" selector="endMessageBody:">endMessageBody:  aBody

	^aBody</body>

<body package="MIME" selector="endPart:">endPart: aMimeEntity

	^aMimeEntity</body>

<body package="MIME" selector="shouldStartBody:">shouldStartBody: aMimeEntity 
	^self headerOnly not</body>

<body package="MIME" selector="shouldStartMessageBody:">shouldStartMessageBody: aMessageBody 

	^self shouldStartBody: aMessageBody</body>

<body package="MIME" selector="startBody:">startBody:  aBody

	^aBody</body>

<body package="MIME" selector="startHeader:">startHeader: aMessageHeaderOrNil 

	^aMessageHeaderOrNil isNil
		ifTrue: [self messageHeaderClass new]
		ifFalse: [aMessageHeaderOrNil]</body>

<body package="MIME" selector="startMessage:">startMessage: anObjectOrNil

	^initialEntity ifNil: [ initialEntity := self defaultRootEntityType new.
						initialEntity body: UnparsedBody new.
						initialEntity ].</body>

<body package="MIME" selector="startMessageBody:">startMessageBody: aBody

	^aBody</body>

<body package="MIME" selector="startPartFor:">startPartFor: aParentEntity

	| part |
	part := self defaultPartEntityType new.
	part body: UnparsedBody new.
	aParentEntity addPart: part.
	^part</body>
</methods>

<methods>
<class-id>Net.MimeParserHandler</class-id> <category>private</category>

<body package="MIME" selector="fileNameFrom:">fileNameFrom: aString
	| fn |
	[fn := aString asFilename.]
		on: UnsupportedCharacterError do: [ :ex | ^self randomExtension asFilename].
	^(fn exists 
		ifTrue:[ self randomExtension, '_', fn tail ] 
		ifFalse: [fn tail]).</body>

<body package="MIME" selector="handleStartBody:exception:">handleStartBody: aBody exception: exception

	ParsingSimpleBodyError new
		messageText: exception class name asString, ': ',exception messageText;
		parameter: exception;
		raiseSignal</body>

<body package="MIME" selector="parse:">parse: aStream 

	self prepareStream: aStream.
	binaryMode := stream isBinary.
	lineEndConvention := stream lineEndConvention.
	^self parser parse: stream</body>

<body package="MIME" selector="randomExtension">randomExtension
	^(Random new next * 10000000000000000) asInteger printString</body>

<body package="MIME" selector="safelyRead:from:into:">safelyRead: aBody from: sourceStream into: resultStream

	sourceStream binary.
	[ [sourceStream atEnd ] 
		whileFalse: [resultStream nextPut: sourceStream next].
	 ]
		on: Error 
		do: [ :ex | self handleStartBody: aBody exception: ex ].

	sourceStream text.</body>

<body package="MIME" selector="setBodySource:">setBodySource: aBody
	| source |
	aBody byteSource notNil
		ifTrue: [	source := EncodedStream 
					on: aBody byteSource
					encodedBy: (StreamEncoder new: aBody parent charset) initializeForMemory.
				source
					lineEndAuto;
					close.
				aBody source: source.]</body>

<body package="MIME" selector="setEntity:">setEntity: anEntity

	initialEntity ifNil: [ initialEntity := anEntity ]</body>
</methods>

<methods>
<class-id>Net.MimeParserHandler</class-id> <category>initialization</category>

<body package="MIME" selector="initialize">initialize
	headerOnly := false</body>
</methods>

<methods>
<class-id>Net.MimeParserHandler</class-id> <category>deprecated</category>

<body package="MIME" selector="build:from:">build: aMimeEntity from: aStream

	^self read: aMimeEntity from: aStream</body>

<body package="MIME" selector="readBodyFor:from:">readBodyFor: aMimeEntity from: aStream

	self headerOnly: false.
	^self parser parseMessageBody: aMimeEntity from: aStream</body>
</methods>

<methods>
<class-id>Net.MimeParserHandler</class-id> <category>body callbacks</category>

<body package="MIME" selector="openAttachmentStream:">openAttachmentStream: aBody
	| loadDir fn name |
	self saveAttachmentsAsFiles ifFalse: [^self openBodyStream: aBody ].

	loadDir := self class defaultAttachmentDirectory.
	fn := aBody parent fileName.

	name :=
		loadDir asFilename construct: (
			fn notNil
				ifTrue: [self fileNameFrom: fn]
				ifFalse: [self randomExtension]).
	name := AttachmentFilename newException
				originalFilename: fn;
				filename: name;
				raiseRequest.
	name asFilename exists
		ifTrue: [ name := AttachmentFileExists new filename: name asFilename; raiseRequest].
	name asFilename exists ifTrue: [name asFilename delete].
	^self openAttachmentStream: aBody filename: name asFilename</body>

<body package="MIME" selector="openAttachmentStream:filename:">openAttachmentStream: aBody filename: aFilename

	| encoding |
	^((encoding := aBody parent charsetIfNone: [nil ]) isNil
		ifTrue: [ aFilename ]
		ifFalse: [aFilename withEncoding: encoding ] ) readWriteStream binary</body>

<body package="MIME" selector="processAttachment:from:">processAttachment: aBody from: aStream
	| resultStream |

	resultStream := self openAttachmentStream: aBody.
	[
		self safelyRead: aBody 
			from:  ( self prepareStream: aStream forSimpleBody: aBody)  
			into: resultStream.
	] ensure: [ resultStream close].

	self setAttachmentSource: aBody to: resultStream .</body>

<body package="MIME" selector="processBody:from:">processBody: aBody from: aStream

	self 
		safelyRead: aBody 
		from: ( self prepareStream: aStream forSimpleBody: aBody) 
		into: ( self openBodyStream: aBody) .</body>

<body package="MIME" selector="processFormData:from:">processFormData: aBody from: aStream

	^self processBody: aBody from: aStream</body>

<body package="MIME" selector="processInline:from:">processInline: aBody from: aStream

	^self processBody: aBody from: aStream</body>

<body package="MIME" selector="processPartBody:from:">processPartBody: aBody from: aStream 

	self processBody: aBody from: aStream</body>
</methods>

<methods>
<class-id>Net.MimeParserHandler class</class-id> <category>defaults</category>

<body package="MIME" selector="defaultAttachmentDirectory">defaultAttachmentDirectory
	defaultAttachmentDirectory ifNil: 
		[defaultAttachmentDirectory := self defaultAttachmentDirectoryValue.
		defaultAttachmentDirectory := defaultAttachmentDirectory , (String with: Filename getSeparator) ].

	defaultAttachmentDirectory asFilename ensureDirectoryExists.
	
	^defaultAttachmentDirectory</body>

<body package="MIME" selector="defaultAttachmentDirectory:">defaultAttachmentDirectory: aString
	defaultAttachmentDirectory := aString</body>

<body package="MIME" selector="saveAttachmentsAsFiles">saveAttachmentsAsFiles
	" (self  saveAttachmentsAsFiles) "
	^saveAttachmentsAsFiles ifNil: [saveAttachmentsAsFiles := self saveAttachmentsAsFilesValue]</body>

<body package="MIME" selector="saveAttachmentsAsFiles:">saveAttachmentsAsFiles: aBoolean
	" (self  saveAttachmentsAsFiles: nil) "
	saveAttachmentsAsFiles := aBoolean</body>
</methods>

<methods>
<class-id>Net.MimeParserHandler class</class-id> <category>instance creation</category>

<body package="MIME" selector="new">new
	^super new initialize</body>

<body package="MIME" selector="on:">on: aStream

	^self new on: aStream; yourself</body>

<body package="MIME" selector="read:from:">read: entity from: aStream
	"Builds specified entity from the stream  "
	^self new read: entity from: aStream</body>

<body package="MIME" selector="readFrom:">readFrom: aStream
	"Creates and builds the entity from the stream  " 
	^self new readFrom: aStream</body>

<body package="MIME" selector="readHeaderFor:from:">readHeaderFor: aMimeEntity from: aStream
	"Builds headers for the specified entity from  the stream "
	^self new readHeaderFor: aMimeEntity from: aStream</body>

<body package="MIME" selector="readHeaderFrom:">readHeaderFrom: aStream
	"Creates an entity and builds headers for the entity from the stream "
	^self new readHeaderFrom: aStream</body>
</methods>

<methods>
<class-id>Net.MimeParserHandler class</class-id> <category>default values</category>

<body package="MIME" selector="defaultAttachmentDirectoryValue">defaultAttachmentDirectoryValue

	^'temp-files'</body>

<body package="MIME" selector="saveAttachmentsAsFilesValue">saveAttachmentsAsFilesValue
	^false</body>
</methods>

<methods>
<class-id>Net.MimeBuildHandler</class-id> <category>testing</category>

<body package="MIME" selector="shouldRemoveBase64EncodingFor:">shouldRemoveBase64EncodingFor: aBody

	| ce |
	^(self removeContentTransferEncoding
		and: [ (ce := aBody parent contentTransferEncoding) ~~ nil 
				and: ['base64' = ce asLowercase]])</body>

<body package="MIME" selector="shouldRemoveContentTransferEncodingFor:">shouldRemoveContentTransferEncodingFor: aBody

	^(self shouldRemoveBase64EncodingFor: aBody)
		or: [self shouldRemoveQuotedPrintableEncodingFor: aBody]</body>

<body package="MIME" selector="shouldRemoveQuotedPrintableEncodingFor:">shouldRemoveQuotedPrintableEncodingFor: aBody

	^(self removeContentTransferEncoding
		and: [ aBody parent contentTransferEncoding ~~ nil 
				and: ['quoted-printable' = aBody parent contentTransferEncoding asLowercase]])</body>
</methods>

<methods>
<class-id>Net.MimeBuildHandler</class-id> <category>accessing</category>

<body package="MIME" selector="removeContentTransferEncoding">removeContentTransferEncoding

	removeContentTransferEncoding 
		ifNil: [removeContentTransferEncoding := self class removeContentTransferEncoding].

	^removeContentTransferEncoding</body>

<body package="MIME" selector="removeContentTransferEncoding:">removeContentTransferEncoding: aBoolean

	removeContentTransferEncoding := aBoolean</body>
</methods>

<methods>
<class-id>Net.MimeBuildHandler</class-id> <category>private prepare streams</category>

<body package="MIME" selector="prepareStream:forSimpleBody:">prepareStream: aStream forSimpleBody: aBody

	(self shouldRemoveBase64EncodingFor: aBody)
		ifTrue: [ ^DecodedStream on: aStream  encodedBy: B64StreamDecoder asEncoder.].

	(self shouldRemoveQuotedPrintableEncodingFor: aBody)
		ifTrue: [ ^DecodedStream on: aStream  encodedBy: QuotedPrintableDecoder asEncoder. ].

	^aStream</body>
</methods>

<methods>
<class-id>Net.MimeBuildHandler</class-id> <category>tasks</category>

<body package="MIME" selector="removeContentTransferEncodingFor:">removeContentTransferEncodingFor: aBody
	
	removeContentTransferEncoding := true.
	(self shouldRemoveContentTransferEncodingFor: aBody) ifFalse: [ ^aBody ].

	self processBody: aBody from: aBody anySource reset.
	self endBody: aBody.</body>
</methods>

<methods>
<class-id>Net.MimeBuildHandler</class-id> <category>action callbacks</category>

<body package="MIME" selector="endBody:">endBody: aBody

	(self shouldRemoveContentTransferEncodingFor: aBody)
		ifTrue: [aBody parent removeFieldAt: 'content-transfer-encoding'].

	aBody isSimple 	ifTrue: [self setBodySource: aBody ].

	^super endBody: aBody</body>
</methods>

<methods>
<class-id>Net.MimeBuildHandler class</class-id> <category>default values</category>

<body package="MIME" selector="defaultAttachmentDirectoryValue">defaultAttachmentDirectoryValue

	^'mail-temp-files'</body>

<body package="MIME" selector="removeContentTransferEncodingValue">removeContentTransferEncodingValue

	^false</body>
</methods>

<methods>
<class-id>Net.MimeBuildHandler class</class-id> <category>defaults</category>

<body package="MIME" selector="removeContentTransferEncoding">removeContentTransferEncoding

	^removeContentTransferEncoding ifNil: [ self removeContentTransferEncodingValue ]</body>

<body package="MIME" selector="removeContentTransferEncoding:">removeContentTransferEncoding: aBoolean

	removeContentTransferEncoding := aBoolean</body>
</methods>

<methods>
<class-id>Net.NonClosingReadStream</class-id> <category>accessing</category>

<body package="MIME" selector="binary">binary

	stream binary</body>

<body package="MIME" selector="contents">contents

	^stream contents</body>

<body package="MIME" selector="contentsSpecies">contentsSpecies

	^stream contentsSpecies</body>

<body package="MIME" selector="encoding">encoding

	^stream encoding</body>

<body package="MIME" selector="flush">flush

	self error: 'This stream is not writable!'</body>

<body package="MIME" selector="next">next

	^stream next</body>

<body package="MIME" selector="nextPut:">nextPut: anObject

	self error: 'This stream is not writable!'</body>

<body package="MIME" selector="position">position

	^stream position</body>

<body package="MIME" selector="position:">position: integer

	stream position: integer</body>

<body package="MIME" selector="reset">reset

	stream reset</body>

<body package="MIME" selector="size">size

	^stream size</body>

<body package="MIME" selector="text">text

	stream text</body>
</methods>

<methods>
<class-id>Net.NonClosingReadStream</class-id> <category>testing</category>

<body package="MIME" selector="atEnd">atEnd

	^stream atEnd</body>

<body package="MIME" selector="isBinary">isBinary
	^stream isBinary</body>

<body package="MIME" selector="isReadable">isReadable
	
	^true</body>

<body package="MIME" selector="isWritable">isWritable

	^false</body>

<body package="MIME" selector="lineEndConvention">lineEndConvention
	^stream lineEndConvention</body>
</methods>

<methods>
<class-id>Net.NonClosingReadStream</class-id> <category>initialize-release</category>

<body package="MIME" selector="on:">on: aStream

	stream := aStream</body>
</methods>

<methods>
<class-id>Net.NonClosingReadStream class</class-id> <category>instance creation</category>

<body package="MIME" selector="on:">on: aStream

	^self basicNew on: aStream</body>
</methods>

<methods>
<class-id>Net.NetworkAddressParser</class-id> <category>private</category>

<body package="MIME" selector="completeScanOfAddressSpecWith:">completeScanOfAddressSpecWith: partial
" addr-spec   =  local-part &lt;@&gt; domain        ; global address
  local-part = word *(&lt;.&gt; word) ; uninterpreted, case-preserved 
First local-part token was already scanned; we are now scanning *(&lt;.&gt; word) group and domain part.
Partial is an array of tokens already read "

	| stream pos |
	stream := partial readWriteStream.
	stream setToEnd.
	self descriptor localPart: (self scanLocalAddressPartTo: stream ).
	pos := self position.
	self nextRFC822Token == $@ 
		ifTrue: [self descriptor domain: self scanDomain]
		ifFalse:[self position: pos].</body>

<body package="MIME" selector="newAddressDescriptor">newAddressDescriptor
	^NetworkAddressDescriptor new</body>

<body package="MIME" selector="parseGroupSpecWith:">parseGroupSpecWith: phrase 
	"group = phrase &lt;:&gt; [#mailbox] &lt;;&gt;"

	| group mailboxes phrasex comment stream |

	mailboxes := self tokenizeList: [self parseAddress]
				separatedBy: [token == $,].
	self nextRFC822Token == $; ifFalse: [^self notify: (#GroupDescriptorShouldBeTerminated &lt;&lt; #net &gt;&gt; 'Group descriptor should be terminated by &lt;:&gt;')].
	group := MailGroupDescriptor new.

	" If phrase is non-empty, an alias was specified "
	phrasex := phrase isEmpty 
				ifTrue: [nil] 
				ifFalse: [self phraseAsString: phrase].

	comment := currentComment isNil
				ifTrue: [nil]
				ifFalse: 
					[stream := (String new: 64) writeStream.
					currentComment do: [:part | stream nextPutAll: part]
						separatedBy: [stream space].
					stream contents].

	group alias: phrasex; addresses: mailboxes; comment: comment.
	^group</body>

<body package="MIME" selector="parseMailboxSpecWith:">parseMailboxSpecWith: phrasex
"     address     =  mailbox                      ; one addressee
		                 /  group                        ; named list
     group       =  phrase &lt;:&gt; [#mailbox] &lt;;&gt;
     mailbox     =  addr-spec                    ; simple address
                 /  phrase route-addr            ; name &amp; addr-spec
     route-addr  =  &lt;&lt;&gt; [route] addr-spec &lt;&gt;&gt;
     route       =  1#(&lt;@&gt; domain) &lt;:&gt;           ; path-relative"

	| phrase tok local stream comment |
	phrase := phrasex. 
	tok := self nextRFC822Token.
	self descriptor: self newAddressDescriptor.

	"Variations of mailbox spec"
	tok = $&lt; ifTrue:								"Phil Campbell&lt;philc@acme.com&gt;"
		[self stepBack; scanRouteAndAddress]
	ifFalse: [
		('.@' includes: tok)
			ifTrue: 
				["These ones should have a non-empty local part to the left of delimiter"
				phrase isEmpty ifTrue: [self error: (#InvalidNetworkAddress &lt;&lt; #net &gt;&gt; 'Invalid network address')].
				local := Array with: phrase last.
				phrase := phrase copyFrom: 1 to: phrase size - 1.	"Extract the part we already scanned"
				tok = $. ifTrue:							"phil.campbell.wise@acme.com&gt;"
					[self stepBack; completeScanOfAddressSpecWith: local].
				tok = $@								"philc@acme.com&gt;"
					ifTrue: 
						[self descriptor localPart: local.
						self descriptor domain: self scanDomain]]
				ifFalse: [self stepBack]].
	" If phrase is non-empty, an alias was specified "
	phrase := phrase isEmpty 
				ifTrue: [phrase := nil] 
				ifFalse: [self phraseAsString: phrase].
	self descriptor alias: phrase.

	comment := currentComment == nil
				ifTrue: [nil]
				ifFalse: 
					[stream := (String new: 64) writeStream.
					currentComment do: [:part | stream nextPutAll: part]
						separatedBy: [stream space].
					stream contents].
	self descriptor comment: comment.
	^self descriptor</body>

<body package="MIME" selector="scanLocalAddressPartTo:">scanLocalAddressPartTo: stream 
	"local-part = word *(&lt;.&gt; word) ; uninterpreted, case-preserved 
	Part of local part may have been scanned already, it's in localPart of the descriptor"
	
	self 
		tokenizeWhile: [token == $.] 
		do: [stream nextPut: self scanWord].
	^stream contents</body>

<body package="MIME" selector="tryScanSubdomain">tryScanSubdomain
	self nextRFC822Token.
	tokenType = #atom ifTrue: [ ^true ].
	(token = $[) ifTrue: [ self stepBack; scanDomainText. ^true ].
	^false.</body>
</methods>

<methods>
<class-id>Net.NetworkAddressParser</class-id> <category>accessing</category>

<body package="MIME" selector="descriptor">descriptor
	^descriptor</body>

<body package="MIME" selector="descriptor:">descriptor: aValue

	descriptor := aValue</body>
</methods>

<methods>
<class-id>Net.NetworkAddressParser</class-id> <category>initialize-release</category>

<body package="MIME" selector="initialize">initialize
	super initialize.
	descriptor := self newAddressDescriptor</body>
</methods>

<methods>
<class-id>Net.NetworkAddressParser</class-id> <category>public</category>

<body package="MIME" selector="addressesFrom:">addressesFrom: stream
	^(self on: stream) parseAddressesSeparatedBy: $,</body>

<body package="MIME" selector="parse:">parse: aString 
	^self on: aString readStream; parseAddress</body>

<body package="MIME" selector="parseAddress">parseAddress
"     address     =  mailbox                      ; one addressee
		                 /  group                        ; named list
     group       =  phrase &lt;:&gt; [#mailbox] &lt;;&gt;
     mailbox     =  addr-spec                    ; simple address
                 /  phrase route-addr            ; name &amp; addr-spec
     route-addr  =  &lt;&lt;&gt; [route] addr-spec &lt;&gt;&gt;
     route       =  1#(&lt;@&gt; domain) &lt;:&gt;           ; path-relative"

	| phrase decodePhrase | 

	phrase := self scanPhrase.
	(phrase isEmpty and: [self atEnd]) ifTrue: [ ^nil ].  
	decodePhrase := Array with: (MimeEncodedWordCoDec decodePhrase: phrase).

	^self nextRFC822Token = $: 
		ifTrue: [self parseGroupSpecWith: decodePhrase]
		ifFalse: [self stepBack; parseMailboxSpecWith: decodePhrase].</body>

<body package="MIME" selector="parseAddressesSeparatedBy:">parseAddressesSeparatedBy: separatorChar

	^self tokenizeList: [self parseAddress]
				separatedBy: [token == separatorChar].</body>

<body package="MIME" selector="scanDomain">scanDomain
	"domain = sub-domain *(&lt;.&gt; sub-domain)"
	"Answers an array of domain seqments, from least significant to most significant"

	^self tokenizeList: [self nextRFC822Token.
		tokenType = #atom
			ifTrue: [token]
			ifFalse: [token = $[
					ifTrue: [self stepBack; scanDomainText]
					ifFalse: [^self notify: (#InvalidDomainSpecification &lt;&lt; #net &gt;&gt; 'Invalid domain specification')]]]
		separatedBy: [token == $.]</body>

<body package="MIME" selector="scanLocalAddress">scanLocalAddress
	"local-part = word *(&lt;.&gt; word) ; uninterpreted, case-preserved"

	^self
		tokenizeList: 
			[self nextRFC822Token.
			(#(#quotedText #atom) includes: tokenType)
				ifFalse: [^self notify: (#LocalPartcanOnlyIncludeWords &lt;&lt; #net &gt;&gt; 'Local part can only include words')].
			token]
		separatedBy: [token == $.]</body>

<body package="MIME" selector="scanRoute">scanRoute
	"route = 1#(&lt;@&gt; domain) &lt;:&gt; ; path-relative"

	| stream |
	stream := (Array new: 2) writeStream.
	[self nextRFC822Token == $@]
		whileTrue: 
			[stream nextPut: self scanDomain.
			self nextToken = $: ifFalse: [self error: (#InvalidRouteSpec &lt;&lt; #net &gt;&gt; 'Invalid route spec')]].
	stream size = 0 ifTrue: [self error: (#InvalidRouteSpec &lt;&lt; #net &gt;&gt; 'Invalid route spec')].
	^stream contents</body>

<body package="MIME" selector="scanRouteAndAddress">scanRouteAndAddress
"     route-addr  =  &lt;&lt;&gt; [route] addr-spec &lt;&gt;&gt;"

	self mustMatch: $&lt; notify: (#InvalidRouteAddressSpec &lt;&lt; #net &gt;&gt; 'Invalid route address spec').	
	self nextRFC822Token == $@ ifTrue: [ self stepBack. self descriptor route: self scanRoute ].
	self completeScanOfAddressSpecWith: (Array with: token).
	self mustMatch: $&gt; notify: (#InvalidRouteAddressSpec &lt;&lt; #net &gt;&gt; 'Invalid route address spec').</body>
</methods>

<methods>
<class-id>Net.NetworkAddressParser</class-id> <category>converting</category>

<body package="MIME" selector="phraseAsString:">phraseAsString: phrase
	| stream |
	stream := (String new: 64) writeStream.
	MimeScanner printPhrase: phrase on: stream. 
	^stream contents.</body>
</methods>

<methods>
<class-id>Net.NetworkAddressParser class</class-id> <category>instance creation</category>

<body package="MIME" selector="parse:">parse: string
	^self new parse: string</body>
</methods>

<methods>
<class-id>Net.NetworkAddressParser class</class-id> <category>utility</category>

<body package="MIME" selector="addressesFrom:">addressesFrom: aRFC822ScannerOrStream
	"self addressesFrom: 'kyasu@crl.fujixerox.co.jp' readStream."
	"self addressesFrom: 'Kazuki Yasumatsu &lt;kyasu@crl.fujixerox.co.jp&gt;' readStream."
	"self addressesFrom: 'kyasu@crl.fujixerox.co.jp (Kazuki Yasumatsu)' readStream."
	"self addressesFrom: ' kyasu1, kyasu2, Kazuki Yasumatsu &lt;kyasu3&gt;, kyasu4 (Kazuki Yasumatsu)' readStream."
	"self addressesFrom: ' foo bar, kyasu1, ,  Kazuki Yasumatsu &lt;kyasu2&gt; &lt;kyasu3&gt; (&lt;foo&gt; (foo bar), bar)' readStream."

	^(self newFrom: aRFC822ScannerOrStream ) 
			parseAddressesSeparatedBy: $,</body>

<body package="MIME" selector="addressFrom:">addressFrom: aRFC822ScannerOrStream
	"self addressFrom: 'kyasu@crl.fujixerox.co.jp'."
	"self addressFrom: 'Kazuki Yasumatsu &lt;kyasu@crl.fujixerox.co.jp&gt;'."
	"self addressFrom: 'kyasu@crl.fujixerox.co.jp (Kazuki Yasumatsu)'."

	^(self newFrom: aRFC822ScannerOrStream )  parseAddress</body>
</methods>

<methods>
<class-id>Net.NetworkAddressParser class</class-id> <category>private</category>

<body package="MIME" selector="newFrom:">newFrom: aRFC822ScannerOrStream

	| scanner |
	scanner := self new.
	(aRFC822ScannerOrStream isKindOf: SimpleScanner)
		ifTrue: [scanner acceptNonAsciiCharacters: aRFC822ScannerOrStream acceptNonAsciiCharacters ].

	^(scanner on: aRFC822ScannerOrStream )</body>
</methods>

<methods>
<class-id>Net.EntityException class</class-id> <category>testing</category>

<body package="MIME" selector="mayResume">mayResume

	^true</body>
</methods>

<methods>
<class-id>Net.MimeEncodedWordCoDec</class-id> <category>parsing</category>

<body package="MIME" selector="decode:">decode: word 
	| stream |
	stream := String new writeStream.
	self decode: word on: stream.
	^stream contents</body>

<body package="MIME" selector="decodePhrase:">decodePhrase: words
" decode phrase word by word; concatenate decoded words and answer concatenated string "
	| output |
	output := (String new: words size) writeStream.
	self decodePhrase: words printOn: output.
	^output contents</body>

<body package="MIME" selector="decodePhrase:printOn:">decodePhrase: words printOn: stream 
	| str |
	str := WriteStream on: String new.
	words 
		do: [ :word | str nextPutAll: word ]
		separatedBy: [str nextPut: Character space].
	stream nextPutAll: (self decodeText: str contents).</body>

<body package="MIME" selector="decodeText:">decodeText: text 
" Decoding of text is similar to decoding of comment, but RFC2047 requires that an encoded word that appears in in *text token MUST be separated from any adjacent encoded word or text by a linear-white-space
RFC 2047:  When displaying a particular header field that contains multiple 'encoded-word's, any 'linear-white-space' that separates a pair of adjacent 'encoded-word's is ignored.  (This is to allow the use of multiple 'encoded-word's to represent long strings of unencoded text,  without having to separate 'encoded-word's where spaces occur in the  unencoded text.)"
	| output word spaces |
	(text indexOfSubCollection: '=?' startingAt: 1) = 0 
		ifTrue: [^text].	
	self on: text readStream.
	output := (String new: text size) writeStream.
	[self atEnd]
		whileFalse: 
			[word := self scanWord. 
			self decode: word on: output.
			spaces := self scanSpaces.
			self shouldAddSpaces ifTrue: [output nextPutAll: spaces]]. 
	^output contents</body>
</methods>

<methods>
<class-id>Net.MimeEncodedWordCoDec</class-id> <category>private</category>

<body package="MIME" selector="decode:on:">decode: word on: output 
	| aStream char pos |
	aStream := word readStream. 
	[aStream atEnd] 
		whileFalse: 
			[pos := aStream position.
			char := aStream next. 
			(char = $= and: [aStream peekFor:  $?]) 
				ifTrue: 
					[self 
						decodeFrom: aStream 
						on: output 
						onError: [aStream position: pos. ^output nextPutAll: aStream upToEnd].
					tokenType := #encoded]
				ifFalse: 
					[char ~= CRLF 
						ifTrue: [output nextPut: char. tokenType := #notEncoded.]]]</body>

<body package="MIME" selector="decodeEncodedWord:charset:encoding:">decodeEncodedWord: contents charset: charset encoding: encodingString
	| encoding outStream string | 
	encoding := encodingString asLowercase.
	(#('b' base64) includes: encoding) 
		ifTrue: " Decode into bytes, then impose desired charset encoding on the result "
			[outStream := (ByteArray new: contents size * 2 // 3 + 1) writeStream.
			MimeScanner decodeBase64From: contents readStream to: outStream.
			^(outStream contents 
				withEncoding: charset) 
				readStream contents].
	(#('q' 'quoted-printable') includes: encoding) 
		ifTrue: 
			["RFC2047 (2) The 8-bit hexadecimal value 20 (e.g., ISO-8859-1 SPACE) may be
		       represented as '_' (underscore, ASCII 95.).  "
			string := contents copyReplaceAll: '_' with: (String with: (Character value: 16r20)). 
			^((MimeScanner decodeQuotedPrintableFrom: string readStream)
				asByteArray withEncoding: charset) readStream contents ].

	(#('uue' 'uuencode' 'x-uue' 'x-uuencode') includes: encoding)
		ifTrue: [^MimeScanner 
				decodeUUEncodedFrom: 1
				to: contents size
				in: contents].
	^nil.									" Failed to decode "</body>

<body package="MIME" selector="decodeFrom:on:onError:">decodeFrom: aStream on: output onError: aBlock
	|  charset encoding scanner word |

	scanner := SimpleScanner on: aStream.
	charset := scanner scanUntil: [ scanner hereChar = $? ].
	(charset isEmpty 
		or: [	[ StreamEncoder new: charset. false ] on: KeyNotFoundError do: [ :ex | true ] ]
	) ifTrue: [ ^aBlock value].
	encoding := scanner scanUntil: [ scanner hereChar = $? ].
	(encoding = 'Q' or: [encoding = 'B']) ifFalse: [ ^aBlock value]. 
	word := scanner upToAndSkipThroughAll:  '?='. 
	[word := self  
			decodeEncodedWord: word
			charset: charset
			encoding: encoding
	] on: Error do: [ :ex | word := nil. ex return].
	word ifNil: [^aBlock value].

	output nextPutAll: word</body>

<body package="MIME" selector="scanSpaces">scanSpaces
	| spaces |
	spaces := self scanWhile: [self matchCharacterType: WhiteSpaceMask].
	self scanWhile: [self matchCharacterType: CRLFMask].
	^spaces, (self scanWhile: [self matchCharacterType: WhiteSpaceMask]).</body>

<body package="MIME" selector="scanWord">scanWord
	^self scanWhile: [(self matchCharacterType: WhiteSpaceMask) not].</body>

<body package="MIME" selector="shouldAddSpaces">shouldAddSpaces
	| pos mask |
	pos := self position.
	self atEnd ifFalse: [mask := self next: 2].
	self position: pos.
	
	^(('=?' = mask) and: [tokenType = #encoded]) not</body>
</methods>

<methods>
<class-id>Net.MimeEncodedWordCoDec class</class-id> <category>encoding</category>

<body package="MIME" selector="encode:asBase64Using:">encode: string asBase64Using: wordEncoding
	|  streamx |

	^self 
		getEncodedWordDo: 
			[ :stream |
			streamx := DecodedStream 
				on: stream
				encodedBy: B64StreamDecoder asEncoder. 
			streamx nextPutAll: (string asByteArrayEncoding: wordEncoding).
			streamx close.]
		charset: wordEncoding 
		encoding: 'B'</body>

<body package="MIME" selector="encode:asQuotedPrintableUsing:">encode: string asQuotedPrintableUsing: wordEncoding
	| quoted streamx |

	^self 
		getEncodedWordDo: 
			[ :stream |
			streamx := DecodedStream 
				on: String new readWriteStream
				encodedBy: QuotedPrintableDecoder asEncoder. 
			streamx nextPutAll: (string asByteArrayEncoding: wordEncoding).
			streamx close.
			quoted := streamx encodedContents. 
			quoted first = $" ifTrue: [ quoted := quoted copyFrom: 2 to: quoted size].
			quoted do: [ :ch |
					ch = $_
						ifTrue: [stream  nextPutAll: '=5F' ]
						ifFalse: [stream nextPut: ch]]]

		charset: wordEncoding 
		encoding: 'Q'</body>

<body package="MIME" selector="encodeComment:asBase64Using:">encodeComment: aString asBase64Using: anEncodingString
	^'(' , (self encodeText: aString asBase64Using: anEncodingString) , ')'</body>

<body package="MIME" selector="encodeIfRequired:using:">encodeIfRequired: string using: anEncoding
	^self 
		encodeIfRequired: string 
		using: anEncoding 
		do: [ :encoding | self encode: string asQuotedPrintableUsing: encoding]</body>

<body package="MIME" selector="encodeIfRequired:using:do:">encodeIfRequired: string using: anEncoding do: aBlock
	| wordEncoding |
	wordEncoding := (anEncoding == nil
		ifTrue: [(self findEncodingFor: string) asString]
		ifFalse: [ anEncoding ]).
	('us-ascii' match: wordEncoding) ifTrue: [ ^string].
	^wordEncoding notNil 
		ifTrue: [aBlock value: wordEncoding  ]
		ifFalse: [string]</body>

<body package="MIME" selector="encodeText:asBase64Using:">encodeText: aString asBase64Using: anEncodingString
"Encoding *text token separated from any adjacent encoded word or text by a linear-white-space"
	| result tokens stream contents |
	^self 
		encodeIfRequired: aString 
		using: anEncodingString
		do: 
			[ :encoding | 
			result := (String new: 256) writeStream.
			tokens := OrderedCollection new.
			aString size &gt; 44
				ifTrue: 
					[stream := aString readStream.
					[stream atEnd ]			
						whileFalse: 
							[ tokens add: ( stream next: 44 ). 
							stream position + 44 &gt; aString size
								ifTrue: [ tokens add: stream upToEnd]]]
				ifFalse: [ tokens add: aString].
				tokens 
					do: [ :token | 
						result nextPutAll: (self encode: token asBase64Using: encoding).
						result cr; tab. ].
				contents := result contents. 
				contents copyFrom: 1 to: contents size - 2. ]</body>

<body package="MIME" selector="encodeText:asQuotedPrintableUsing:">encodeText: aString asQuotedPrintableUsing: anEncodingString
"Encoding *text token separated from any adjacent encoded word or text by a linear-white-space"
	| result contents |
	^self 
		encodeIfRequired: aString 
		using: anEncodingString
		do: 
			[ :encoding | 
			result := (String new: 256) writeStream.
			aString 
				piecesCutWhere: [:each :next | each = Character space ] 
				do: [ :each | 
					(self encode: each asQuotedPrintableUsing: encoding)
						do: [ :ch |
							ch ~= Character space
								ifTrue: [ result nextPut: ch]
								ifFalse: [result nextPutAll: '=20' ]].
					result nextPut: Character space]. 
			contents := result contents.
			contents copyFrom: 1 to: contents size -1  ]</body>

<body package="MIME" selector="getEncodedWordDo:charset:encoding:">getEncodedWordDo: aBlock charset: charset encoding: aString
	| result |

	result := (String new: 256)  writeStream. 
	result 
		nextPutAll: '=?';
		nextPutAll: charset;
		nextPutAll: '?';
		nextPutAll: aString;
		nextPutAll: '?'.
	aBlock value: result.
	result nextPutAll: '?='.
	^result contents</body>
</methods>

<methods>
<class-id>Net.MimeEncodedWordCoDec class</class-id> <category>testing</category>

<body package="MIME" selector="shouldBeEncoded:">shouldBeEncoded: aString
	^(aString detect: [:char | char asInteger &gt; 128] ifNone: [nil]) ~~ nil</body>
</methods>

<methods>
<class-id>Net.MimeEncodedWordCoDec class</class-id> <category>utility</category>

<body package="MIME" selector="findEncodingFor:">findEncodingFor: aString 

	(self shouldBeEncoded: aString) ifFalse: [^'us-ascii'].
	^AvailableEncodings 
		detect: 
			[:encoding | 
				[aString asByteArrayEncoding: encoding. 
				true] on: Error do: [:ex | false]]
		ifNone: [].</body>
</methods>

<methods>
<class-id>Net.MimeEncodedWordCoDec class</class-id> <category>decoding</category>

<body package="MIME" selector="decode:">decode: word 
	^self new decode: word</body>

<body package="MIME" selector="decodeComment:">decodeComment: commentString
	^self decodeText: commentString</body>

<body package="MIME" selector="decodePhrase:">decodePhrase: words
" decode phrase word by word; concatenate decoded words and answer concatenated string "
	^self new decodePhrase: words</body>

<body package="MIME" selector="decodePhrase:printOn:">decodePhrase: words printOn: stream 
	self new decodePhrase: words printOn: stream</body>

<body package="MIME" selector="decodeText:">decodeText: text 
	^self new decodeText: text</body>
</methods>

<methods>
<class-id>Net.MimeEncodedWordCoDec class</class-id> <category>initialize-release</category>

<body package="MIME" selector="initialize">initialize
	self initializeAvalibleEncoding</body>

<body package="MIME" selector="initializeAvalibleEncoding">initializeAvalibleEncoding
	"self initializeAvalibleEncoding"

	AvailableEncodings := ((1 to: 15) collect: [:i | ('iso8859-' , i printString) asSymbol]) asOrderedCollection.
	AvailableEncodings addAll: ((1 to: 15) collect: [:i | ('iso-8859-' , i printString) asSymbol]) asOrderedCollection.
	AvailableEncodings 
		addFirst: #'us-ascii';
		addLast: Locale current defaultEncoding;
		addLast: #'utf-8'.

	^AvailableEncodings</body>
</methods>

<methods>
<class-id>Net.MessageHeader</class-id> <category>adding</category>

<body package="MIME" selector="add:">add: field
	self register: field.
	self fields add: field</body>

<body package="MIME" selector="add:after:">add: field after: object
	self register: field.
	self fields add: field after: object</body>

<body package="MIME" selector="add:afterIndex:">add: field afterIndex: occurrenceIndex
	self register: field.
	self fields add: field afterIndex: occurrenceIndex</body>

<body package="MIME" selector="add:before:">add: field before: object
	self register: field.
	self fields add: field before: object</body>

<body package="MIME" selector="add:beforeIndex:">add: field beforeIndex: occurrenceIndex
	self register: field.
	self fields add: field beforeIndex: occurrenceIndex</body>

<body package="MIME" selector="addAll:">addAll: collectionOfFields 
	collectionOfFields do: [:field | self add: field]</body>

<body package="MIME" selector="addField:">addField: field
	" This method will check if the field exists already; if yes, if it can be merged into the existing field and, if yes, merge it. Otherwise, add as a new field "

	(field canMerge and: [(self at: field name) notNil])
		ifTrue: [ (self at: field name) add: field]
		ifFalse: [ self add: field]</body>
</methods>

<methods>
<class-id>Net.MessageHeader</class-id> <category>sorting</category>

<body package="MIME" selector="arrangeFieldsInOrder:">arrangeFieldsInOrder: anArrayOfFieldNames
" Make sure specified fields occur before all other fields in specified order "

	value := self fieldsInOrder: anArrayOfFieldNames</body>

<body package="MIME" selector="fieldsInOrder:">fieldsInOrder: anArrayOfFieldNames
" Answer fields sorted such that specified fields occur before all other fields"

	| definedFields |

	definedFields := OrderedCollection new: self fields size.
	anArrayOfFieldNames do: [ :fieldName | definedFields addAll: (self allAt: fieldName)].
	definedFields addAll: (self fields reject: [ :fieldx | definedFields includes: fieldx]).
	^definedFields</body>
</methods>

<methods>
<class-id>Net.MessageHeader</class-id> <category>accessing</category>

<body package="MIME" selector="allAt:">allAt: name
	^self registry at: name asLowercase ifAbsent: [OrderedCollection new]</body>

<body package="MIME" selector="at:">at: name
	^self at: name at: 1</body>

<body package="MIME" selector="at:at:">at: name at: occurrenceIndex
	^self at: name at: occurrenceIndex ifAbsent: [ nil ]</body>

<body package="MIME" selector="at:at:ifAbsent:">at: name at: occurrenceIndex ifAbsent: aNiladicBlock
	| namedFields |

	namedFields := self registry at: name asLowercase ifAbsent: [^aNiladicBlock value ].
	^namedFields size &gt;= occurrenceIndex
		ifTrue: [namedFields at: occurrenceIndex]
		ifFalse: [ aNiladicBlock value ]</body>

<body package="MIME" selector="at:ifAbsent:">at: name ifAbsent: aNiladicBlock
	^self at: name at: 1 ifAbsent: aNiladicBlock</body>

<body package="MIME" selector="at:ifAbsentPut:">at: name ifAbsentPut: aNiladicBlock 
	^self at: name ifAbsent: [self at: name put: aNiladicBlock value]</body>

<body package="MIME" selector="at:put:">at: name put: field
	field name: name.
	self 
		removeKey: name;
		add: field.
	^field</body>

<body package="MIME" selector="fields">fields
	^value</body>

<body package="MIME" selector="fields:">fields: aCollection
" Replace all fields with new set of fields "
	self initialize; addAll: aCollection</body>

<body package="MIME" selector="indexOf:">indexOf: field
	^self fields indexOf: field</body>

<body package="MIME" selector="parent:">parent: aMimeEntity
	super parent: aMimeEntity.
	self fieldsDo: [ :field | field parent: aMimeEntity ].</body>
</methods>

<methods>
<class-id>Net.MessageHeader</class-id> <category>enumerating</category>

<body package="MIME" selector="fieldsDo:">fieldsDo: aMonadicBlock
	self fields do: aMonadicBlock</body>
</methods>

<methods>
<class-id>Net.MessageHeader</class-id> <category>copying</category>

<body package="MIME" selector="postCopy">postCopy
	| oldFields |

	oldFields := value.
	self initialize.
	oldFields do: [ :field | self add: field copy ]</body>
</methods>

<methods>
<class-id>Net.MessageHeader</class-id> <category>private</category>

<body package="MIME" selector="deregister:">deregister: field 
	"Remove field from registry"

	| namedFields | 
	namedFields := self registry at: field name asLowercase ifAbsent: [^self].
	namedFields remove: field.
	namedFields isEmpty ifTrue: [self registry removeKey: field name asLowercase]</body>

<body package="MIME" selector="register:">register: field
" Register field in field registry "
	field parent: self parent.
	(self registry at: field name asLowercase ifAbsentPut: [ OrderedCollection new ]) add: field</body>

<body package="MIME" selector="registry">registry
	^registry</body>
</methods>

<methods>
<class-id>Net.MessageHeader</class-id> <category>removing</category>

<body package="MIME" selector="remove:">remove: field 
	field notNil
		ifTrue: 
			[self fields remove: field.
			self deregister: field]</body>

<body package="MIME" selector="removeAll:">removeAll: collectionOfFields
	collectionOfFields do: [ :field | self remove: field ]</body>

<body package="MIME" selector="removeAllAt:">removeAllAt: name
	self removeAll: (self allAt: name)</body>

<body package="MIME" selector="removeKey:">removeKey: fieldName
	self removeAll: (self allAt: fieldName)</body>
</methods>

<methods>
<class-id>Net.MessageHeader</class-id> <category>composing</category>

<body package="MIME" selector="writeOn:">writeOn: aStream 
	self fields do: [:field | field writeOn: aStream]</body>
</methods>

<methods>
<class-id>Net.MessageHeader</class-id> <category>visiting</category>

<body package="MIME" selector="acceptVisit:with:">acceptVisit: aVisitor with: argument
	^aVisitor acceptMessageHeader: self with: argument</body>
</methods>

<methods>
<class-id>Net.MessageHeader</class-id> <category>initialization</category>

<body package="MIME" selector="initialize">initialize
	registry := Dictionary new: 10.
	value := OrderedCollection new: 10.</body>
</methods>

<methods>
<class-id>Net.MessageHeader</class-id> <category>parsing</category>

<body package="MIME" selector="parse:">parse: scanner</body>
</methods>

<methods>
<class-id>Net.MessageHeader</class-id> <category>printing</category>

<body package="MIME" selector="printOn:indent:">printOn: aStream indent: level
	self fields 
		do: [ :each |
			each printOn: aStream.
			aStream cr]</body>
</methods>

<methods>
<class-id>Net.MessageHeader class</class-id> <category>instance creation</category>

<body package="MIME" selector="newFor:">newFor: aMimeEntity
	^self new parent: aMimeEntity</body>
</methods>

<methods>
<class-id>Net.QuotedPrintableDecoder</class-id> <category>private</category>

<body package="MIME" selector="invalidHexEncoding:">invalidHexEncoding: char

	EncodingFailure new 
		messageText: (
			(#InvalidQuotedPrintableEncoding1p &lt;&lt; #net &gt;&gt;
			'&lt;1p&gt; is not a valid hexadecimal digit in quoted-printable encoding!')
				expandMacrosWith: char);
		parameter: char;
		raise</body>

<body package="MIME" selector="shouldBeRepresentedLiterally:">shouldBeRepresentedLiterally: byte

	^((byte between: 33 and: 126) and: [ byte ~~ 61 "$=" ])
	or: [ byte ==  32 "SPC" or: [ byte == 9 "TAB" ]]</body>
</methods>

<methods>
<class-id>Net.QuotedPrintableDecoder</class-id> <category>accessing</category>

<body package="MIME" selector="lineLength">lineLength

	^lineLength ifNil: [ self class defaultLineLength ]</body>

<body package="MIME" selector="lineLength:">lineLength: length
"
	&lt;Integer&gt; specifies the line length where to emit CR and wrap to the next line when encoding
"
	lineLength := length</body>

<body package="MIME" selector="nextFrom:">nextFrom: aStream

	offset &lt; 0 ifTrue: [ "finish soft line break"
		offset := 0.
		^CR ].
	digit &gt; 0	ifTrue: [ | char | "finish encoding a non ASCII character"
		char := digit = 1
				ifTrue: [ octet bitShift: -4 ]
				ifFalse: [ octet bitAnd: 16rF ].
		char := '0123456789ABCDEF' at: (char + 1).
		digit := (digit + 1) \\ 3.
		offset := offset + 1.
		^char ].
	offset &lt; self lineLength ifFalse: [ "start a soft line break"
		offset := -1.
		^$= ].
	octet := aStream next.
	octet = 13 "CR" ifTrue: [ "preserve hard line break"
		offset := 0.
		^CR].
	octet = 10 "LF" ifTrue: [ "preserve hard line break"
		offset := 0.
		^LF].
	offset := offset + 1.
	^(self shouldBeRepresentedLiterally: octet)
		ifTrue: [ octet asCharacter ]
		ifFalse: [ digit := 1. $= ]</body>

<body package="MIME" selector="nextPut:on:">nextPut: char on: stream

	digit &gt; 0
		ifTrue: [
			(digit = 1 and: [ char = CR or: [ char = LF] ]) ifTrue: [ digit := 0. ^char ].
			('0123456789ABCDEFabcdef' includes: char)
				ifFalse: [ ^self invalidHexEncoding: char ].
			octet := (octet bitShift: 4) + char digitValue.
			digit := digit + 1.
			digit &gt; 2 ifTrue: [
				stream nextPut: octet.
				octet := digit := 0 ] ]
		ifFalse: [
			char = $=
				ifTrue: [ digit := 1 ]
				ifFalse: [ stream nextPut: char asInteger ] ]</body>

<body package="MIME" selector="reset">reset

	digit := 0.
	octet := 0.
	offset := 0.</body>
</methods>

<methods>
<class-id>Net.QuotedPrintableDecoder</class-id> <category>printing</category>

<body package="MIME" selector="encoding">encoding

	^#'quoted-printable'</body>
</methods>

<methods>
<class-id>Net.QuotedPrintableDecoder class</class-id> <category>accessing</category>

<body package="MIME" selector="defaultLineLength">defaultLineLength

	^defaultLineLength ifNil: [ self defaultLineLengthValue ]</body>

<body package="MIME" selector="defaultLineLength:">defaultLineLength: length
"
	length &lt;Integer&gt; specifies the default line length where to emit CR and wrap to the next line when encoding
"
	defaultLineLength := length</body>

<body package="MIME" selector="defaultLineLengthValue">defaultLineLengthValue

	^76</body>

<body package="MIME" selector="streamEncodingType">streamEncodingType

	^nil</body>
</methods>

<methods>
<class-id>Net.QuotedPrintableDecoder class</class-id> <category>instance creation</category>

<body package="MIME" selector="new:">new: symbol

	^symbol = #'quoted-printable'
		ifTrue: [ self asEncoder ]
		ifFalse: [ super new: symbol ]</body>
</methods>

<methods>
<class-id>Net.ParsingSimpleBodyError class</class-id> <category>testing</category>

<body package="MIME" selector="mayResume">mayResume

	^true</body>
</methods>

<methods>
<class-id>Net.SimpleBody</class-id> <category>defaults</category>

<body package="MIME" selector="defaultContentType">defaultContentType
	^'text/plain'</body>
</methods>

<methods>
<class-id>Net.SimpleBody</class-id> <category>private -- parsing</category>

<body package="MIME" selector="doParse:">doParse: rfc822Scanner
	^rfc822Scanner contents</body>
</methods>

<methods>
<class-id>Net.SimpleBody</class-id> <category>accessing</category>

<body package="MIME" selector="anySource">anySource
	(source isNil and: [byteSource isNil]) 
		ifTrue: [^String new readStream].
	^(source isNil
		ifTrue: [ byteSource ]
		ifFalse: [ source ])</body>

<body package="MIME" selector="byteSource">byteSource
	^byteSource</body>

<body package="MIME" selector="byteSource:">byteSource: aByteArrayStream
	byteSource := aByteArrayStream</body>

<body package="MIME" selector="byteSourceSize">byteSourceSize
	^byteSource isNil
		ifTrue: [ 0 ]
		ifFalse: [byteSource size]</body>

<body package="MIME" selector="byteValueAsStream">byteValueAsStream
	"The method is used for message writing.  Answer stream representing value. If value has been set, make stream out of it. Otherwise just use source stream. Reset the stream before returning. If the source contains non-ASCII chars convert it to an EncodedStream."
	| stream encoding |

	byteSource notNil ifTrue: [
		byteSource reset.
		^byteSource isSegmented "only mail messages parsed from archives use stream segments"
			ifTrue: [ NonClosingReadStream on: byteSource ]
			ifFalse: [ (EncodedStream
						on: byteSource reset
						encodedBy: ( StreamEncoder new: #ISO8859_1 )
					) binary]].
	^self source == nil
		ifTrue: [ByteArray new readStream]
		ifFalse: 
			[ self source isInternal 
				ifTrue: [encoding := self encodingFor:  self source reset contents.
					encoding ifNil: [encoding := #ISO8859_1  ].
					stream := EncodedStream
								on: ( ReadWriteStream on: (ByteArray new: self source size))
								encodedBy: (StreamEncoder new: encoding ) initializeForMemory.
					self source reset.
					[ self source atEnd]
						whileFalse: [ stream nextPut: self source next ]. 
					stream binary;reset ]
				ifFalse: 
					[" Attachment " 
					self source reset ]]</body>

<body package="MIME" selector="contentLength">contentLength
" Answer content length in octets, nil if not known "
	^self parent contentLength</body>

<body package="MIME" selector="contentsSpecies">contentsSpecies
	^self parent contentsSpecies</body>

<body package="MIME" selector="contentTypeEncoding">contentTypeEncoding
	" Answer symbolic name of the character encoding for this body "

	^self parent notNil
		ifTrue: [self parent contentTypeEncoding]
		ifFalse: [#default]</body>

<body package="MIME" selector="contentTypeSpecies">contentTypeSpecies
	^self parent contentTypeSpecies</body>

<body package="MIME" selector="decodedValue">decodedValue

	^self decodedValueWith:  self class defaultCharsetEncoder.</body>

<body package="MIME" selector="decodedValueWith:">decodedValueWith: aSymbol
	^self parent canBeDecoded 
		ifTrue: 
			[self byteSource notNil
				ifTrue: [(self byteSource contents withEncoding: aSymbol) readStream
							lineEndAuto;
							contents]
				ifFalse: 
					[(self source encodedContents asByteArray withEncoding: aSymbol ) 
						readStream  
						lineEndConvention: self source lineEndConvention; 
						contents]]
		ifFalse: 
			[ self value ]</body>

<body package="MIME" selector="decompressedContents">decompressedContents
"Returns decompressed body contents. The body contents won't be changed "
	| writeStream zippedStream |
	self byteSource isNil ifTrue: [ ^String new ].
	writeStream := (ByteArray new: 1024) writeStream.
	zippedStream := OS.ZLib.GZipReadStream on: self byteSource reset.
	[zippedStream atEnd] whileFalse: [ writeStream nextPut: zippedStream next].
	^writeStream contents</body>

<body package="MIME" selector="encoding">encoding
	" Answer symbolic name of the character encoding for this body "

	^self parent notNil
		ifTrue: [self parent encoding]
		ifFalse: [#default]</body>

<body package="MIME" selector="partAt:">partAt: anInteger
	self shouldNotImplement</body>

<body package="MIME" selector="parts">parts
	^OrderedCollection new</body>

<body package="MIME" selector="removePart:">removePart: aMimeEntity
	self shouldNotImplement</body>

<body package="MIME" selector="removePartAt:">removePartAt: anInteger
	self shouldNotImplement</body>

<body package="MIME" selector="size">size
	^source notNil
		ifTrue: [ self sourceSize ]
		ifFalse: [ self byteSourceSize ]</body>

<body package="MIME" selector="source:">source: aStream
	source := aStream.</body>

<body package="MIME" selector="sourceForDigest">sourceForDigest

	^self anySource reset</body>

<body package="MIME" selector="value:">value: anArray
	self source: anArray readStream.
	byteSource := nil.</body>

<body package="MIME" selector="valueAsStream">valueAsStream
	" Answer stream representing value. If value has been set, make stream out of it. Otherwise just use source stream. Reset the stream before returning. If the source contains non-ASCII chars convert it to an EncodedString."
	| encoding contents |
	byteSource ifNotNil: [^byteSource reset]. 
	^self source == nil
		ifTrue: [String new readStream]
		ifFalse: 
			[ self source isInternal
				ifTrue: 
					[contents := self source reset contents.
					encoding := self encodingFor:  contents.
					encoding ifNotNil:
						[contents := self 
										encodeDo: [ (contents asByteArrayEncoding: encoding) asString ] 
										with: encoding ].
					contents readStream]
				ifFalse: 
					[" Attachment " 
					self source reset ]]</body>
</methods>

<methods>
<class-id>Net.SimpleBody</class-id> <category>writing</category>

<body package="MIME" selector="writeContentsOn:">writeContentsOn: aStream

	self parent newWriter 
		writeBodyContents: self on: aStream</body>

<body package="MIME" selector="writeOn:">writeOn: aStream
" Writing can be different from printing. Printing may be indented, abbreviated
and otherwise formatted for readability. Writing is used to actually send message.
Default implementation is the same "

	self writeContentsOn: aStream</body>
</methods>

<methods>
<class-id>Net.SimpleBody</class-id> <category>visiting</category>

<body package="MIME" selector="acceptVisit:with:">acceptVisit: aVisitor with: argument
	^aVisitor acceptSimpleBody: self with: argument</body>
</methods>

<methods>
<class-id>Net.SimpleBody</class-id> <category>printing</category>

<body package="MIME" selector="printForDisplayOn:">printForDisplayOn: aStream
	self isBinary
		ifTrue: [self printOn: aStream]
		ifFalse: [self writeOn: aStream]</body>
</methods>

<methods>
<class-id>Net.SimpleBody</class-id> <category>private</category>

<body package="MIME" selector="encodeDo:with:">encodeDo: aBlock with: encoding
	^[ aBlock value ] 
		on: Error 
		do: [ :ex | 
			(EncodingFailure  new 
					messageText: ((#FailedToEncodeBodySource &lt;&lt; #net &gt;&gt;'Failed to encode the body source with: &lt;1s&gt; ') expandMacrosWith: encoding printString);
					parameter: self;
					yourself) raise ]</body>

<body package="MIME" selector="encodingFor:">encodingFor: aByteString
	^parent charsetIfNone: [MimeEncodedWordCoDec findEncodingFor: aByteString ].</body>

<body package="MIME" selector="getValue">getValue
	^self source isNil
		ifTrue: [self getValueFromByteSource]
		ifFalse: [[self source reset; contents] ensure: [self source close]]</body>

<body package="MIME" selector="getValueFromByteSource">getValueFromByteSource
	| s |
	^(s :=self getValueStreamFromByteSource) isNil
		ifTrue: [s]
		ifFalse: [ [s contents] ensure:[ s close ] ]</body>

<body package="MIME" selector="getValueStreamFromByteSource">getValueStreamFromByteSource
	| encoding |
	byteSource isNil ifTrue: [ ^nil].
"	byteSource isExternalStream ifTrue: [^byteSource reset]."
	self parent canBeDecoded
		ifTrue:
			[encoding := self parent charsetIfNone: [nil ].
			encoding isNil
				ifTrue: [ byteSource reset ]
				ifFalse: 
					[^self 
						encodeDo: [[ (byteSource contents asByteArray withEncoding: encoding) readStream] ensure: [byteSource close]]  
						with: encoding ]].
	^byteSource reset</body>
</methods>

<methods>
<class-id>Net.SimpleBody</class-id> <category>mutating</category>

<body package="MIME" selector="addPart:">addPart: part
	(self parent bot
		makeMultipart: self parent) 
		addPart: part;
		yourself</body>
</methods>

<methods>
<class-id>Net.SimpleBody</class-id> <category>transfer encoding</category>

<body package="MIME" selector="needsContentTransferEncoding">needsContentTransferEncoding

	self isBinary ifTrue: [^true].
	^self class scannerType 
			needsTransferEncoding: self valueAsStream mimeType: self mimeType</body>

<body package="MIME" selector="removeContentTransferEncoding">removeContentTransferEncoding

	(byteSource notNil and: [ byteSource isSegmented ]) ifTrue: [^self ].
	self parent newBuilder removeContentTransferEncodingFor: self</body>
</methods>

<methods>
<class-id>Net.SimpleBody</class-id> <category>deprecated</category>

<body package="MIME" selector="setByteSource:">setByteSource: aByteArrayStream
	self byteSource: aByteArrayStream</body>
</methods>

<methods>
<class-id>Net.SimpleBody class</class-id> <category>accessing settings</category>

<body package="MIME" selector="defaultCharsetEncoder">defaultCharsetEncoder
	^DefaultCharsetEncoder isNil
		ifTrue: [ DefaultCharsetEncoder := self defaultCharsetEncoderValue ]
		ifFalse: [ DefaultCharsetEncoder ]</body>

<body package="MIME" selector="defaultCharsetEncoder:">defaultCharsetEncoder: aString
	DefaultCharsetEncoder := aString</body>
</methods>

<methods>
<class-id>Net.SimpleBody class</class-id> <category>defaults</category>

<body package="MIME" selector="defaultCharsetEncoderValue">defaultCharsetEncoderValue
	^Locale current defaultEncoding</body>
</methods>

<methods>
<class-id>Net.MimeEntityBot</class-id> <category>tasks</category>

<body package="MIME" selector="addFileAttachmentFor:fileName:">addFileAttachmentFor: aMessage fileName: aFilename
	| part |
	aMessage addPart: (part := MimeEntity fileName: aFilename).
	^part</body>

<body package="MIME" selector="addFileAttachmentFor:fileName:withEncoding:">addFileAttachmentFor: aMessage fileName: aFilename withEncoding: aString
	| part |
	aMessage addPart: (part := MimeEntity fileName: aFilename withEncoding: aString).
	^part</body>

<body package="MIME" selector="applyTransferEncodingTo:">applyTransferEncodingTo: part
	self applyTransferEncodingTo: part encodeAs: self defaultEncodingName</body>

<body package="MIME" selector="applyTransferEncodingTo:encodeAs:">applyTransferEncodingTo: part encodeAs: encodingNameOrNil
	| encoding |

	encoding := encodingNameOrNil isNil 
					ifTrue: [ part defaultTransferEncodingName ]
					ifFalse: [encodingNameOrNil].
	part addContentTransferEncoding: encoding.</body>

<body package="MIME" selector="attachmentsFor:">attachmentsFor: aMimeEntity 
	^aMimeEntity isMultipart
		ifTrue: [ aMimeEntity parts ]
		ifFalse: [ OrderedCollection new ]</body>

<body package="MIME" selector="checkStructure:">checkStructure: aMimeEntity
	" Make sure all fields are in order mandated by RFC822 "

	self for: aMimeEntity partsDo: 
		[ :part | part arrangeFieldsInOrder: aMimeEntity transferFieldOrder ].

	aMimeEntity mimeVersion isNil
		ifTrue: [aMimeEntity addMimeVersion].

	" Make sure all parts have assigned boundaries "
	self for: aMimeEntity partsDo: [ :part |
		part needsBoundary
			ifTrue: [part boundary: (self defaultBoundaryFor: part)]].</body>

<body package="MIME" selector="defaultEncodingName">defaultEncodingName
	^'base64'</body>

<body package="MIME" selector="makeMultipart:">makeMultipart: rfc822Message
" Make single-part mail message a multipart one. Assumption that is made
here: aMimeEntity is a top-level RFC822 message. We create a new Mime
entity to contain message text and Mime fields (content-type, content-length, etc).
These fields are removed from the top-level message and new content-type is
set. This content type is somewhat arbitrarily set to miltipart/mixed. The message
is then given new miltipart body and new part is added to the body "

	| messagePart |
	messagePart := self makeSinglepartFrom: rfc822Message.
	" Remove non-mime fields from the top-level message "

	rfc822Message 
		fields: (rfc822Message fields reject: [ :field |  'content*' match: field name ]);
		contentType: 'multipart/mixed';
		body: MultipartBody new.
	messagePart ~~ nil
		ifTrue: [ rfc822Message addPart: messagePart].
	^rfc822Message</body>

<body package="MIME" selector="makeSinglepart:textPart:">makeSinglepart: rfc822Message textPart: textPart
" Mutate multipart mail message into a single-part one. Assumption that is made
here: rfc822Message is a top-level RFC822 message that has only one part. 
We create new simple part and copy the part's contents into message contents.
We then copy and all the part's fields into the top-level message fields. Simple body
becomes the top level message's body "

	" Merge fields from the part into the message "
	textPart fields do: [ :field | rfc822Message addField: field ].

	rfc822Message 
		body: SimpleBody new;
		contents: textPart contents.
	^rfc822Message</body>

<body package="MIME" selector="makeSinglepartFrom:">makeSinglepartFrom: aMimeEntity
" Make single-part from mime entity. Return nil if mime entity has empty body and  header fields "
	| messagePart chSet |
	messagePart := MimeEntity new.
	" Copy Mime fields from the message "
	aMimeEntity fields 
		do: [ :field | 
			('content*' match: field name)
				ifTrue: [ messagePart addField: field copy] ].
	aMimeEntity contents notNil 
		ifTrue: [ 	messagePart contents: aMimeEntity contents].

	^(messagePart fields isEmpty and: [ messagePart contents == nil ])
		ifTrue: [nil]
		ifFalse: 
			[(chSet := messagePart charset ) ~~ nil ifTrue: [ aMimeEntity charset: chSet ]. 
			messagePart fields isEmpty ifTrue: [ messagePart charset: aMimeEntity defaultCharset].
			messagePart]</body>

<body package="MIME" selector="validate:">validate: aMimeEntity 
" Run sanity checks on an entity "

	" 1. Validate structure "
	self
		for: aMimeEntity
		partsDo: [:part | 
			part body parent == part ifFalse: [ self error: (#BodyParentIsWrong &lt;&lt; #net &gt;&gt; 'Body parent is wrong') ].
			part fields do: [ :field | field parent == part ifFalse: [ self error: (#FieldParentIsWrong &lt;&lt; #net &gt;&gt; 'Field parent is wrong') ]].
			part parent notNil 
				ifTrue: [ (part parent isMultipart and: [ part parent parts includes: part ])
						ifFalse: [self error: (#InvalidMessageStructure &lt;&lt; #net &gt;&gt; 'Invalid message structure') ]].
			(part isMultipart 
				ifTrue: [ part body isMultipart ]
				ifFalse: [ part body isSimple ])
					ifFalse: [self error: (#BodyTypeMismatch &lt;&lt; #net &gt;&gt; 'Body type mismatch') ].
			part isMultipart 
				ifTrue: [part boundary notNil
					ifFalse: [self error: (#ShouldHaveBoundary &lt;&lt; #net &gt;&gt; 'Should have boundary') ]].
		].</body>

<body package="MIME" selector="writeContentsOf:on:">writeContentsOf: aMimeEntity on: anEncodedStream
	aMimeEntity removeContentTransferEncoding.
	aMimeEntity isBinary
		ifTrue: [anEncodedStream binary]
		ifFalse: [anEncodedStream text].
	aMimeEntity writeContentsOn: anEncodedStream.</body>
</methods>

<methods>
<class-id>Net.MimeEntityBot</class-id> <category>enumerating</category>

<body package="MIME" selector="for:maxDepth:partsDo:">for: aMimeEntity maxDepth: depthLimit partsDo: aBlock

	self 
		for: aMimeEntity 
		while: [ :part | self level &lt;= (depthLimit - 1) ]
		partsDo: aBlock</body>

<body package="MIME" selector="for:partsDetect:ifNone:">for: aMimeEntity partsDetect: aBlock ifNone: exceptionBlock 
	self
		for: aMimeEntity
		partsDo: [:part | (aBlock value: part) ifTrue: [^part]].
	^exceptionBlock value</body>

<body package="MIME" selector="for:partsDo:">for: aMimeEntity partsDo: aBlock
	self for: aMimeEntity while: [ :part | true ] partsDo: aBlock</body>

<body package="MIME" selector="for:while:partsDo:">for: aMimeEntity while: condBlock partsDo: aBlock 
	self shouldCallback: [ :part |
			(condBlock value: part)
				ifTrue: [ true ]
				ifFalse: [ self keepSearching: false. false] ];
		doCallback: aBlock;
		traverse: aMimeEntity.
	^aMimeEntity</body>

<body package="MIME" selector="traverse:">traverse: aMimeEntity

	self	initializeEntityStack;
		keepSearching: true.
	self dispatcher acceptMimeEntity: aMimeEntity with: nil.</body>
</methods>

<methods>
<class-id>Net.MimeEntityBot</class-id> <category>initialization</category>

<body package="MIME" selector="initialize">initialize
	super initialize.
	keepSearching := true.
	self initializeEntityStack</body>

<body package="MIME" selector="initializeEntityStack">initializeEntityStack

	entityStack := OrderedCollection new.</body>
</methods>

<methods>
<class-id>Net.MimeEntityBot</class-id> <category>accessing</category>

<body package="MIME" selector="doCallback">doCallback
	^doCallback</body>

<body package="MIME" selector="doCallback:">doCallback: aMonadicBlock
"	
	aMonadicBlock &lt;BlockContext&gt; invoked with a part to process it
"
	doCallback := aMonadicBlock</body>

<body package="MIME" selector="entity">entity
"Returns currently processed entity."
	^entityStack first</body>

<body package="MIME" selector="level">level
"Returns 'depth' of the currently processed part in the message structure."
	^entityStack size</body>

<body package="MIME" selector="shouldCallback">shouldCallback
	^shouldCallback</body>

<body package="MIME" selector="shouldCallback:">shouldCallback: aMonadicBlock
"	
	aMonadicBlock  &lt;BlockContext&gt; boolean block invoked with a part to decide if it should be processed or not
"
	shouldCallback := aMonadicBlock</body>
</methods>

<methods>
<class-id>Net.MimeEntityBot</class-id> <category>private</category>

<body package="MIME" selector="entityStack">entityStack

	^entityStack</body>

<body package="MIME" selector="keepSearching">keepSearching
	^keepSearching</body>

<body package="MIME" selector="keepSearching:">keepSearching: aBoolean
	keepSearching := aBoolean</body>

<body package="MIME" selector="popEntity">popEntity

	^entityStack removeFirst</body>

<body package="MIME" selector="pushEntity:">pushEntity: entity

	^entityStack addFirst: entity</body>
</methods>

<methods>
<class-id>Net.MimeEntityBot</class-id> <category>action callbacks</category>

<body package="MIME" selector="endEntity:">endEntity: aMimeEntityOrNil
	
	^self popEntity</body>

<body package="MIME" selector="shouldStartEntity:">shouldStartEntity: aMimeEntityOrNil

	^self shouldCallback isNil 
		ifTrue: [true]
		ifFalse: [self shouldCallback value: aMimeEntityOrNil]</body>

<body package="MIME" selector="startEntity:">startEntity: aMimeEntityOrNil

	self pushEntity: aMimeEntityOrNil.
	self doCallback value: aMimeEntityOrNil.
	^aMimeEntityOrNil</body>
</methods>

<methods>
<class-id>Net.MimeEntityBot</class-id> <category>deprecated</category>

<body package="MIME" selector="entityCallback">entityCallback
	^self doCallback</body>

<body package="MIME" selector="entityCallback:">entityCallback: aMonadicBlock
	self doCallback: aMonadicBlock</body>
</methods>

<methods>
<class-id>Net.NetworkAddressDescriptor</class-id> <category>accessing</category>

<body package="MIME" selector="addressSpecString">addressSpecString
	^self printStringSelector: #printAddressSpecOn:</body>

<body package="MIME" selector="aliasString">aliasString
	^self printStringSelector: #printAliasOn:</body>

<body package="MIME" selector="commentString">commentString
	^self printStringSelector: #printCommentOn:</body>

<body package="MIME" selector="domain">domain

	^domain</body>

<body package="MIME" selector="domain:">domain: aValue

	domain := aValue</body>

<body package="MIME" selector="domainString">domainString
	^self printStringSelector: #printDomainOn:</body>

<body package="MIME" selector="localPart">localPart

	^localPart</body>

<body package="MIME" selector="localPart:">localPart: aValue

	localPart := aValue</body>

<body package="MIME" selector="localPartString">localPartString
	^self printStringSelector: #printLocalPartOn:</body>

<body package="MIME" selector="route">route

	^route</body>

<body package="MIME" selector="route:">route: aValue

	route := aValue</body>

<body package="MIME" selector="routeString">routeString
	^self printStringSelector: #printRouteOn:</body>
</methods>

<methods>
<class-id>Net.NetworkAddressDescriptor</class-id> <category>printing</category>

<body package="MIME" selector="printAddressSpecOn:">printAddressSpecOn: stream 
	self hasAddressSpec
		ifTrue: 
			[self printLocalPartOn: stream.
			stream nextPut: $@.
			self printDomainOn: stream]
		ifFalse:
			[(alias notNil and: [ alias notEmpty ]) 
				ifTrue: [self scannerType printWord: alias on: stream ]]</body>

<body package="MIME" selector="printCanonicalValueOn:">printCanonicalValueOn: stream 
	alias notNil
		ifTrue: 
			[self printAliasOn: stream.
			self printRouteAddressOn: stream]
		ifFalse: [self printAddressSpecOn: stream].</body>

<body package="MIME" selector="printDomainOn:">printDomainOn: stream
	self scannerType printDomain: domain on: stream</body>

<body package="MIME" selector="printLocalPartOn:">printLocalPartOn: stream 
	localPart do: [ :token | self scannerType printWord: token on: stream ] separatedBy: [ stream nextPut: $. ]</body>

<body package="MIME" selector="printRouteAddressOn:">printRouteAddressOn: stream 
	(route notNil or: [self hasAddressSpec])
		ifTrue: 
			[stream nextPut: $&lt;.
			self printRouteOn: stream; printAddressSpecOn: stream.
			stream nextPut: $&gt;]</body>

<body package="MIME" selector="printRouteOn:">printRouteOn: stream 
	(route notNil and: [route isEmpty not])
		ifTrue: 
			[route
				do: 
					[:domainx | 
					stream space; nextPut: $@.
					self scannerType printDomain: domainx on: stream.
					stream nextPut: $:].
			stream space]</body>
</methods>

<methods>
<class-id>Net.NetworkAddressDescriptor</class-id> <category>testing</category>

<body package="MIME" selector="hasAddressSpec">hasAddressSpec
	^localPart notNil and: [localPart isEmpty not and: [domain notNil and: [domain isEmpty not]]]</body>
</methods>

<methods>
<class-id>Net.NetworkAddressDescriptor</class-id> <category>initialization</category>

<body package="MIME" selector="initialize">initialize
	localPart := Array new.</body>
</methods>

<methods>
<class-id>Net.NetworkAddressDescriptor</class-id> <category>private</category>

<body package="MIME" selector="printStringSelector:">printStringSelector: sel
	| stream |
	stream := (String new: 64) writeStream.
	self perform: sel with: stream.
	^stream contents</body>
</methods>

<methods>
<class-id>Net.NetworkAddressDescriptor</class-id> <category>composing</category>

<body package="MIME" selector="writeCanonicalValueOn:encoding:">writeCanonicalValueOn: stream encoding: aString
	alias notNil
		ifTrue: 
			[self writeAliasOn: stream encoding: aString.
			self writeCommentOn: stream encoding: aString.
			self printRouteAddressOn: stream]
		ifFalse: 	[ self printAddressSpecOn: stream].</body>
</methods>

<methods>
<class-id>Net.NetworkAddressDescriptor class</class-id> <category>utility</category>

<body package="MIME" selector="addressesFrom:">addressesFrom: aRFC822ScannerOrStream
	"self addressesFrom: 'kyasu@crl.fujixerox.co.jp' readStream."
	"self addressesFrom: 'Kazuki Yasumatsu &lt;kyasu@crl.fujixerox.co.jp&gt;' readStream."
	"self addressesFrom: 'kyasu@crl.fujixerox.co.jp (Kazuki Yasumatsu)' readStream."
	"self addressesFrom: ' kyasu1, kyasu2, Kazuki Yasumatsu &lt;kyasu3&gt;, kyasu4 (Kazuki Yasumatsu)' readStream."
	"self addressesFrom: ' foo bar, kyasu1, ,  Kazuki Yasumatsu &lt;kyasu2&gt; &lt;kyasu3&gt; (&lt;foo&gt; (foo bar), bar)' readStream."

	^self scannerType addressesFrom: aRFC822ScannerOrStream</body>

<body package="MIME" selector="addressFrom:">addressFrom: aRFC822ScannerOrStream
	"self addressesFrom: 'kyasu@crl.fujixerox.co.jp' readStream."
	"self addressesFrom: 'Kazuki Yasumatsu &lt;kyasu@crl.fujixerox.co.jp&gt;' readStream."
	"self addressesFrom: 'kyasu@crl.fujixerox.co.jp (Kazuki Yasumatsu)' readStream."
	"self addressesFrom: ' kyasu1, kyasu2, Kazuki Yasumatsu &lt;kyasu3&gt;, kyasu4 (Kazuki Yasumatsu)' readStream."
	"self addressesFrom: ' foo bar, kyasu1, ,  Kazuki Yasumatsu &lt;kyasu2&gt; &lt;kyasu3&gt; (&lt;foo&gt; (foo bar), bar)' readStream."

	^self scannerType addressFrom: aRFC822ScannerOrStream</body>
</methods>

<methods>
<class-id>Net.MultipartBody</class-id> <category>accessing</category>

<body package="MIME" selector="addPart:">addPart: part
	self parts add: part</body>

<body package="MIME" selector="boundary">boundary
	^self parent boundary</body>

<body package="MIME" selector="parent:">parent: aMimeEntity
	super parent: aMimeEntity.
	" Re-parent all my parts "
	self parts do: [ :part | part parent: aMimeEntity ]</body>

<body package="MIME" selector="partAt:">partAt: anInteger
	^self parts at: anInteger</body>

<body package="MIME" selector="parts">parts
	^value</body>

<body package="MIME" selector="parts:">parts: aCollection
	value := aCollection.
	value do: [ :part | part parent: self ].</body>

<body package="MIME" selector="removePart:">removePart: aMimeEntity
	self parts remove: aMimeEntity</body>

<body package="MIME" selector="removePartAt:">removePartAt: anInteger
	self parts remove: (self partAt: anInteger)</body>
</methods>

<methods>
<class-id>Net.MultipartBody</class-id> <category>initialization</category>

<body package="MIME" selector="initialize">initialize
	self parts: (OrderedCollection new: 4)</body>
</methods>

<methods>
<class-id>Net.MultipartBody</class-id> <category>testing</category>

<body package="MIME" selector="isMultipart">isMultipart
	^true</body>

<body package="MIME" selector="isSimple">isSimple
	^false</body>

<body package="MIME" selector="notEmpty">notEmpty
	^true</body>
</methods>

<methods>
<class-id>Net.MultipartBody</class-id> <category>transfer encoding</category>

<body package="MIME" selector="needsContentTransferEncoding">needsContentTransferEncoding
	^false</body>
</methods>

<methods>
<class-id>Net.MultipartBody</class-id> <category>copying</category>

<body package="MIME" selector="postCopy">postCopy
	super postCopy.
	value := value collect: [ :part | part copy ].</body>
</methods>

<methods>
<class-id>Net.MultipartBody</class-id> <category>writing</category>

<body package="MIME" selector="writeContentsOn:">writeContentsOn: aStream
	self shouldNotImplement</body>
</methods>

<methods>
<class-id>Net.MultipartBody</class-id> <category>visiting</category>

<body package="MIME" selector="acceptVisit:with:">acceptVisit: aVisitor with: argument
	^aVisitor acceptMultipartBody: self with: argument</body>
</methods>

<methods>
<class-id>Net.MultipartBody</class-id> <category>printing</category>

<body package="MIME" selector="preamble">preamble
	^'This message is in Mime format\\' withCRs readStream</body>

<body package="MIME" selector="printLine:on:indent:">printLine: aString on: aStream indent: level

	aStream 
		tab: level;
		nextPutAll: aString;
		cr</body>
</methods>

<methods>
<class-id>Net.MultipartBody</class-id> <category>constants</category>

<body package="MIME" selector="boundaryDashes">boundaryDashes
	^self class boundaryDashes</body>
</methods>

<methods>
<class-id>Net.MultipartBody class</class-id> <category>documentation</category>

<body package="MIME" selector="fromRFC2046">fromRFC2046

" From RFC2046: Multipurpose Internet Mail Extensions (MIME) Part Two:
                              Media Types

5.1.  Multipart Media Type

   In the case of multipart entities, in which one or more different
   sets of data are combined in a single body, a 'multipart' media type
   field must appear in the entity's header.  The body must then contain
   one or more body parts, each preceded by a boundary delimiter line,
   and the last one followed by a closing boundary delimiter line.
   After its boundary delimiter line, each body part then consists of a
   header area, a blank line, and a body area.  Thus a body part is
   similar to an RFC 822 message in syntax, but different in meaning.

   A body part is an entity and hence is NOT to be interpreted as
   actually being an RFC 822 message.  To begin with, NO header fields
   are actually required in body parts.  A body part that starts with a
   blank line, therefore, is allowed and is a body part for which all
   default values are to be assumed.  In such a case, the absence of a
   Content-Type header usually indicates that the corresponding body has
   a content-type of 'text/plain; charset=US-ASCII'.

   The only header fields that have defined meaning for body parts are
   those the names of which begin with 'Content-'.  All other header
   fields may be ignored in body parts.  Although they should generally
   be retained if at all possible, they may be discarded by gateways if
   necessary.  Such other fields are permitted to appear in body parts
   but must not be depended on.  'X-' fields may be created for
   experimental or private purposes, with the recognition that the
   information they contain may be lost at some gateways.

   NOTE:  The distinction between an RFC 822 message and a body part is
   subtle, but important.  A gateway between Internet and X.400 mail,
   for example, must be able to tell the difference between a body part
   that contains an image and a body part that contains an encapsulated
   message, the body of which is a JPEG image.  In order to represent
   the latter, the body part must have 'Content-Type: message/rfc822',
   and its body (after the blank line) must be the encapsulated message,
   with its own 'Content-Type: image/jpeg' header field.  The use of
   similar syntax facilitates the conversion of messages to body parts,
   and vice versa, but the distinction between the two must be
   understood by implementors.  (For the special case in which parts
   actually are messages, a 'digest' subtype is also defined.)

   As stated previously, each body part is preceded by a boundary
   delimiter line that contains the boundary delimiter.  The boundary
   delimiter MUST NOT appear inside any of the encapsulated parts, on a
   line by itself or as the prefix of any line.  This implies that it is
   crucial that the composing agent be able to choose and specify a
   unique boundary parameter value that does not contain the boundary
   parameter value of an enclosing multipart as a prefix.

   All present and future subtypes of the 'multipart' type must use an
   identical syntax.  Subtypes may differ in their semantics, and may
   impose additional restrictions on syntax, but must conform to the
   required syntax for the 'multipart' type.  This requirement ensures
   that all conformant user agents will at least be able to recognize
   and separate the parts of any multipart entity, even those of an
   unrecognized subtype.

   As stated in the definition of the Content-Transfer-Encoding field
   [RFC 2045], no encoding other than '7bit', '8bit', or 'binary' is
   permitted for entities of type 'multipart'.  The 'multipart' boundary
   delimiters and header fields are always represented as 7bit US-ASCII
   in any case (though the header fields may encode non-US-ASCII header
   text as per RFC 2047) and data within the body parts can be encoded
   on a part-by-part basis, with Content-Transfer-Encoding fields for
   each appropriate body part.

5.1.1.  Common Syntax

   This section defines a common syntax for subtypes of 'multipart'.
   All subtypes of 'multipart' must use this syntax.  A simple example
   of a multipart message also appears in this section.  An example of a
   more complex multipart message is given in RFC 2049.

   The Content-Type field for multipart entities requires one parameter,
   'boundary'. The boundary delimiter line is then defined as a line
   consisting entirely of two hyphen characters ('-', decimal value 45)
   followed by the boundary parameter value from the Content-Type header
   field, optional linear whitespace, and a terminating CRLF.

   NOTE:  The hyphens are for rough compatibility with the earlier RFC
   934 method of message encapsulation, and for ease of searching for
   the boundaries in some implementations.  However, it should be noted
   that multipart messages are NOT completely compatible with RFC 934
   encapsulations; in particular, they do not obey RFC 934 quoting
   conventions for embedded lines that begin with hyphens.  This
   mechanism was chosen over the RFC 934 mechanism because the latter
   causes lines to grow with each level of quoting.  The combination of
   this growth with the fact that SMTP implementations sometimes wrap
   long lines made the RFC 934 mechanism unsuitable for use in the event
   that deeply-nested multipart structuring is ever desired.

   WARNING TO IMPLEMENTORS:  The grammar for parameters on the Content-
   type field is such that it is often necessary to enclose the boundary
   parameter values in quotes on the Content-type line.  This is not
   always necessary, but never hurts. Implementors should be sure to
   study the grammar carefully in order to avoid producing invalid
   Content-type fields.  Thus, a typical 'multipart' Content-Type header
   field might look like this:

     Content-Type: multipart/mixed; boundary=gc0p4Jq0M2Yt08j34c0p

   But the following is not valid:

     Content-Type: multipart/mixed; boundary=gc0pJq0M:08jU534c0p

   (because of the colon) and must instead be represented as

     Content-Type: multipart/mixed; boundary='gc0pJq0M:08jU534c0p'

   This Content-Type value indicates that the content consists of one or
   more parts, each with a structure that is syntactically identical to
   an RFC 822 message, except that the header area is allowed to be
   completely empty, and that the parts are each preceded by the line

     --gc0pJq0M:08jU534c0p

   The boundary delimiter MUST occur at the beginning of a line, i.e.,
   following a CRLF, and the initial CRLF is considered to be attached
   to the boundary delimiter line rather than part of the preceding
   part.  The boundary may be followed by zero or more characters of
   linear whitespace. It is then terminated by either another CRLF and
   the header fields for the next part, or by two CRLFs, in which case
   there are no header fields for the next part.  If no Content-Type
   field is present it is assumed to be 'message/rfc822' in a
   'multipart/digest' and 'text/plain' otherwise.

   NOTE:  The CRLF preceding the boundary delimiter line is conceptually
   attached to the boundary so that it is possible to have a part that
   does not end with a CRLF (line  break).  Body parts that must be
   considered to end with line breaks, therefore, must have two CRLFs
   preceding the boundary delimiter line, the first of which is part of
   the preceding body part, and the second of which is part of the
   encapsulation boundary.

   Boundary delimiters must not appear within the encapsulated material,
   and must be no longer than 70 characters, not counting the two
   leading hyphens.

   The boundary delimiter line following the last body part is a
   distinguished delimiter that indicates that no further body parts
   will follow.  Such a delimiter line is identical to the previous
   delimiter lines, with the addition of two more hyphens after the
   boundary parameter value.

     --gc0pJq0M:08jU534c0p--

   NOTE TO IMPLEMENTORS:  Boundary string comparisons must compare the
   boundary value with the beginning of each candidate line.  An exact
   match of the entire candidate line is not required; it is sufficient
   that the boundary appear in its entirety following the CRLF.

   There appears to be room for additional information prior to the
   first boundary delimiter line and following the final boundary
   delimiter line.  These areas should generally be left blank, and
   implementations must ignore anything that appears before the first
   boundary delimiter line or after the last one.

   NOTE:  These 'preamble' and 'epilogue' areas are generally not used
   because of the lack of proper typing of these parts and the lack of
   clear semantics for handling these areas at gateways, particularly
   X.400 gateways.  However, rather than leaving the preamble area
   blank, many MIME implementations have found this to be a convenient
   place to insert an explanatory note for recipients who read the
   message with pre-MIME software, since such notes will be ignored by
   MIME-compliant software.

   NOTE:  Because boundary delimiters must not appear in the body parts
   being encapsulated, a user agent must exercise care to choose a
   unique boundary parameter value.  The boundary parameter value in the
   example above could have been the result of an algorithm designed to
   produce boundary delimiters with a very low probability of already
   existing in the data to be encapsulated without having to prescan the
   data.  Alternate algorithms might result in more 'readable' boundary
   delimiters for a recipient with an old user agent, but would require
   more attention to the possibility that the boundary delimiter might
   appear at the beginning of some line in the encapsulated part.  The
   simplest boundary delimiter line possible is something like '---',
   with a closing boundary delimiter line of '-----'.

   As a very simple example, the following multipart message has two
   parts, both of them plain text, one of them explicitly typed and one
   of them implicitly typed:

     From: Nathaniel Borenstein &lt;nsb@bellcore.com&gt;
     To: Ned Freed &lt;ned@innosoft.com&gt;
     Date: Sun, 21 Mar 1993 23:56:48 -0800 (PST)
     Subject: Sample message
     MIME-Version: 1.0
     Content-type: multipart/mixed; boundary='simple boundary'

     This is the preamble.  It is to be ignored, though it
     is a handy place for composition agents to include an
     explanatory note to non-MIME conformant readers.

     --simple boundary

     This is implicitly typed plain US-ASCII text.
     It does NOT end with a linebreak.
     --simple boundary
     Content-type: text/plain; charset=us-ascii

     This is explicitly typed plain US-ASCII text.
     It DOES end with a linebreak.

     --simple boundary--

     This is the epilogue.  It is also to be ignored.

   The use of a media type of 'multipart' in a body part within another
   'multipart' entity is explicitly allowed.  In such cases, for obvious
   reasons, care must be taken to ensure that each nested 'multipart'
   entity uses a different boundary delimiter.  See RFC 2049 for an
   example of nested 'multipart' entities.

   The use of the 'multipart' media type with only a single body part
   may be useful in certain contexts, and is explicitly permitted.

   NOTE: Experience has shown that a 'multipart' media type with a
   single body part is useful for sending non-text media types.  It has
   the advantage of providing the preamble as a place to include
   decoding instructions.  In addition, a number of SMTP gateways move
   or remove the MIME headers, and a clever MIME decoder can take a good
   guess at multipart boundaries even in the absence of the Content-Type
   header and thereby successfully decode the message.

   The only mandatory global parameter for the 'multipart' media type is
   the boundary parameter, which consists of 1 to 70 characters from a
   set of characters known to be very robust through mail gateways, and
   NOT ending with white space. (If a boundary delimiter line appears to
   end with white space, the white space must be presumed to have been
   added by a gateway, and must be deleted.)  It is formally specified
   by the following BNF:

     boundary := 0*69&lt;bchars&gt; bcharsnospace

     bchars := bcharsnospace / ' '

     bcharsnospace := DIGIT / ALPHA / ''' / '(' / ')' /
                      '+' / '_' / ',' / '-' / '.' /
                      '/' / ':' / '=' / '?'

   Overall, the body of a 'multipart' entity may be specified as
   follows:

     dash-boundary := '--' boundary
                      ; boundary taken from the value of
                      ; boundary parameter of the
                      ; Content-Type field.

     multipart-body := [preamble CRLF]
                       dash-boundary transport-padding CRLF
                       body-part *encapsulation
                       close-delimiter transport-padding
                       [CRLF epilogue]

     transport-padding := *LWSP-char
                          ; Composers MUST NOT generate
                          ; non-zero length transport
                          ; padding, but receivers MUST
                          ; be able to handle padding
                          ; added by message transports.

     encapsulation := delimiter transport-padding
                      CRLF body-part

     delimiter := CRLF dash-boundary

     close-delimiter := delimiter '--'

     preamble := discard-text

     epilogue := discard-text

     discard-text := *(*text CRLF) *text
                     ; May be ignored or discarded.

     body-part := MIME-part-headers [CRLF *OCTET]
                  ; Lines in a body-part must not start
                  ; with the specified dash-boundary and
                  ; the delimiter must not appear anywhere
                  ; in the body part.  Note that the
                  ; semantics of a body-part differ from
                  ; the semantics of a message, as
                  ; described in the text.

     OCTET := &lt;any 0-255 octet value&gt;

   IMPORTANT:  The free insertion of linear-white-space and RFC 822
   comments between the elements shown in this BNF is NOT allowed since
   this BNF does not specify a structured header field.

   NOTE:  In certain transport enclaves, RFC 822 restrictions such as
   the one that limits bodies to printable US-ASCII characters may not
   be in force. (That is, the transport domains may exist that resemble
   standard Internet mail transport as specified in RFC 821 and assumed
   by RFC 822, but without certain restrictions.) The relaxation of
   these restrictions should be construed as locally extending the
   definition of bodies, for example to include octets outside of the
   US-ASCII range, as long as these extensions are supported by the
   transport and adequately documented in the Content- Transfer-Encoding
   header field.  However, in no event are headers (either message
   headers or body part headers) allowed to contain anything other than
   US-ASCII characters.

   NOTE:  Conspicuously missing from the 'multipart' type is a notion of
   structured, related body parts. It is recommended that those wishing
   to provide more structured or integrated multipart messaging
   facilities should define subtypes of multipart that are syntactically
   identical but define relationships between the various parts. For
   example, subtypes of multipart could be defined that include a
   distinguished part which in turn is used to specify the relationships
   between the other parts, probably referring to them by their
   Content-ID field.  Old implementations will not recognize the new
   subtype if this approach is used, but will treat it as
   multipart/mixed and will thus be able to show the user the parts that
   are recognized.

5.1.2.  Handling Nested Messages and Multiparts

   The 'message/rfc822' subtype defined in a subsequent section of this
   document has no terminating condition other than running out of data.
   Similarly, an improperly truncated 'multipart' entity may not have
   any terminating boundary marker, and can turn up operationally due to
   mail system malfunctions.

   It is essential that such entities be handled correctly when they are
   themselves imbedded inside of another 'multipart' structure.  MIME
   implementations are therefore required to recognize outer level
   boundary markers at ANY level of inner nesting.  It is not sufficient
   to only check for the next expected marker or other terminating
   condition.

"</body>
</methods>

<methods>
<class-id>Net.MultipartBody class</class-id> <category>constants</category>

<body package="MIME" selector="boundaryDashes">boundaryDashes
	^'--'</body>
</methods>

<methods>
<class-id>Net.MimePrintHandler</class-id> <category>accessing</category>

<body package="MIME" selector="indentation">indentation

	^indentation ifNil: [indentation := 0]</body>

<body package="MIME" selector="indentation:">indentation: anInteger

	^indentation := anInteger</body>

<body package="MIME" selector="indentLines">indentLines

	^indentLines ifNil: [false]</body>

<body package="MIME" selector="indentLines:">indentLines: aBoolean

	indentLines := aBoolean</body>
</methods>

<methods>
<class-id>Net.MimePrintHandler</class-id> <category>tasks</category>

<body package="MIME" selector="printMessage:on:">printMessage: aMimeEntity on: aStream
	self printMessageElement: aMimeEntity on: aStream indent: 0</body>

<body package="MIME" selector="printMessage:on:indent:">printMessage: aMimeEntity on: aStream indent: level

	^self printMessageElement: aMimeEntity on: aStream indent: level</body>

<body package="MIME" selector="printMessageElement:on:">printMessageElement: aMessageElement on: aStream

	^self printMessageElement: aMessageElement on: aStream indent: 0</body>

<body package="MIME" selector="printMessageElement:on:indent:">printMessageElement: aMessageElement on: aStream indent: level

	self indentLines: true;
		indentation: level;
		prepareStream: aStream.
	aMessageElement acceptVisit: self dispatcher with: nil.</body>
</methods>

<methods>
<class-id>Net.MimePrintHandler</class-id> <category>action callbacks</category>

<body package="MIME" selector="endEntity:">endEntity: aMimeEntityOrNil

	self indentation &gt; 0 ifTrue: [self indentation: self indentation - 1].
	^super endEntity: aMimeEntityOrNil</body>

<body package="MIME" selector="startEntity:">startEntity: aMimeEntityOrNil

	(aMimeEntityOrNil notNil
		and: [ aMimeEntityOrNil parent notNil ])
		ifTrue: [ self indentation: self indentation + 1].

	^aMimeEntityOrNil</body>

<body package="MIME" selector="startMultipartBody:">startMultipartBody: aMultipartBody

	| bodyStream |
	bodyStream := BoundedWriteCharacterStream
						on: self stream
						boundary: aMultipartBody parent boundary.
	self pushStream: bodyStream.</body>

<body package="MIME" selector="startSimpleBody:">startSimpleBody: aSimpleBody

	| aStream |
	aStream := aSimpleBody sourceForDigest.
	[	[self printDigestOf: aStream entity: aSimpleBody parent
		] ensure: [ aStream ~~ nil ifTrue: [ aStream close ] ]

	] on: Error do: [ :ex |
				self printLine:  ( #errorPrintingBodyContents &lt;&lt; #netclients &gt;&gt; 'ERROR printing body contents: ' ) asString.
				self printLine: ex errorString].</body>
</methods>

<methods>
<class-id>Net.MimePrintHandler</class-id> <category>printing utilities</category>

<body package="MIME" selector="printDigestOf:entity:">printDigestOf: aStream entity: anEntity
	" Print short digest from source stream onto aStream "
	| contents binary fname size position |
	binary := self isBinaryContent: aStream entity: anEntity.
	fname := anEntity fileName.
	size := aStream size.
	fname notNil 
		ifTrue: [self printLine: '------ from ', fname, ' (', size printString, ' bytes) -----']
		ifFalse: [binary ifTrue: [self printLine: '------ (', size printString, ' bytes) -----']].
	binary not
		ifTrue: 
			[ position := aStream position. 
				[contents := (aStream nextAvailable: 512) asString. 
				aStream atEnd ifFalse: [contents := contents, '...']. 
				contents readStream linesDo: [ :line | self printLine: line ].
				] ensure: [ aStream position: position]
			].
	(fname notNil and: [binary not]) ifTrue: [self printLine: '---------------------------------------------------']</body>

<body package="MIME" selector="printLine:">printLine: aString

	self indentLines ifTrue: [ self stream tab: self indentation ].
	super printLine: aString</body>
</methods>

<methods>
<class-id>Net.MimePrintHandler</class-id> <category>private</category>

<body package="MIME" selector="isBinaryContent:entity:">isBinaryContent: aStream entity: anEntity

	^anEntity isBinaryContentType</body>
</methods>

<methods>
<class-id>Net.MimePrintHandler</class-id> <category>private prepare streams</category>

<body package="MIME" selector="prepareStream:">prepareStream: aStream

	self pushStream:  aStream.</body>
</methods>

<methods>
<class-id>Net.BodyStream</class-id> <category>testing</category>

<body package="MIME" selector="atEnd">atEnd
	^position &gt;= readLimit  or: [ stream atEnd ]</body>

<body package="MIME" selector="isReadable">isReadable

	^true</body>

<body package="MIME" selector="isWritable">isWritable
	"Answer whether the stream implements #nextPut:."

	^false</body>
</methods>

<methods>
<class-id>Net.BodyStream</class-id> <category>printing</category>

<body package="MIME" selector="printDetailsOn:">printDetailsOn: aStream

	aStream nextPutAll: ' position: ', position printString.
	16rFFFFFFFF = readLimit 
		ifFalse: [ aStream nextPutAll:  ' readLimit: ', readLimit printString ]</body>
</methods>

<methods>
<class-id>Net.BodyStream</class-id> <category>accessing</category>

<body package="MIME" selector="next">next

	(self atEnd or: [ stream atEnd ]) ifTrue: [self pastEnd].
	position := position + 1.
	^stream next</body>
</methods>

<methods>
<class-id>Net.BodyStream</class-id> <category>initialize-release</category>

<body package="MIME" selector="initializeOn:length:">initializeOn: aStream length: aNumber
	stream := aStream.
	stream binary.
	readLimit := (aNumber isNil
		ifTrue: [16rFFFFFFFF]
		ifFalse: [ aNumber ]).

	position := 0.</body>
</methods>

<methods>
<class-id>Net.BodyStream class</class-id> <category>instance creation</category>

<body package="MIME" selector="on:length:">on: aStream length: aNumber
	^self basicNew initializeOn: aStream length: aNumber</body>
</methods>

<methods>
<class-id>Net.DateField</class-id> <category>converting</category>

<body package="MIME" selector="asDateAndTime">asDateAndTime
	^self value</body>

<body package="MIME" selector="fromDateAndTime:">fromDateAndTime: dateAndTime
	^Timestamp fromDate: dateAndTime first andTime: dateAndTime last.</body>
</methods>

<methods>
<class-id>Net.DateField</class-id> <category>accessing</category>

<body package="MIME" selector="asString">asString
	^self dateString</body>

<body package="MIME" selector="dateString">dateString
	^self value
		ifNil: [ 'nil' ]
		ifNotNil: [ RFC822Scanner dateStringFrom: self value ]</body>

<body package="MIME" selector="timestamp">timestamp
	^self value</body>

<body package="MIME" selector="timestamp:">timestamp: aTimestamp
	self value: aTimestamp</body>

<body package="MIME" selector="value:">value: aTimestampOrStringOrArray 
	"We allow to set date from either string, timestamp or a two-element array (date, time) "

	(aTimestampOrStringOrArray isKindOf: Timestamp)
		ifTrue: [value := aTimestampOrStringOrArray]
		ifFalse: [aTimestampOrStringOrArray isString
				ifTrue: [value := self doParse: (self scannerOn: aTimestampOrStringOrArray readStream)]
				ifFalse: [value := self fromDateAndTime: aTimestampOrStringOrArray]]</body>
</methods>

<methods>
<class-id>Net.DateField</class-id> <category>printing</category>

<body package="MIME" selector="printStructureOn:">printStructureOn: aStream
	aStream nextPutAll: self printValue.
	self printCommentOn: aStream</body>

<body package="MIME" selector="printValue">printValue
	| v |
	v := self value.
	^ v respondsToArithmetic
		ifTrue: [  v printString ]
		ifFalse: [ self dateString].</body>
</methods>

<methods>
<class-id>Net.DateField</class-id> <category>parsing</category>

<body package="MIME" selector="doParse:">doParse: rfc822Scanner 
" HeaderField fromLine: 'Date : Tue, 18 Apr 89 23:29:47 +0900	' "
" HeaderField fromLine: 'Date : (Tue, 18 Apr 89 23:29:47 +0900)	' "
" HeaderField fromLine: 'Expires : 1' "
" HeaderField fromLine: 'Expires :   1' "
" HTTP/1.1 clients MUST treat other invalid date formats, especially including the value 0, as in the past (i.e., already expired). That is why the value can be a number :-("

	| pos string |
	pos := rfc822Scanner asStream position.
	^[rfc822Scanner class readDateAndTimeFrom: rfc822Scanner asStream
		] on: Error
		 do: [ :ex | 
			string := (rfc822Scanner asStream position: pos) upToEnd trimBlanks.
			ex return: (Number readFrom: string readStream ) ]</body>
</methods>

<methods>
<class-id>Net.DateField class</class-id> <category>parsing</category>

<body package="MIME" selector="fieldNames">fieldNames
	^#('date' 'expires' 'last-modified' 'retry-after')</body>
</methods>

<methods>
<class-id>Net.DateField class</class-id> <category>instance creation</category>

<body package="MIME" selector="default">default
	^self new value: Timestamp now</body>
</methods>

<methods>
<class-id>OS.Filename</class-id> <category>net clients</category>

<body package="MIME" selector="ensureDirectoryExists">ensureDirectoryExists
	(self exists and: [self isDirectory]) ifTrue: [^self].
	self directory ensureDirectoryExists.
	self makeDirectory</body>
</methods>

<methods>
<class-id>Core.TimestampReader</class-id> <category>initialize</category>

<body package="MIME" selector="rfc822">rfc822
"Obsolete"
	timeSelector := #readLatinTimeFrom:.
	dateSelector := #readLatinDateFrom:.
	timestampSelector := #readLatinTimestampFrom:.</body>
</methods>

<methods>
<class-id>Core.TimestampPrintPolicy</class-id> <category>initialize-release</category>

<body package="MIME" selector="rfc822">rfc822
	"Obsolete"

	self shortPolicyString: 'd mmm yyyy hh:mm:ss;d mmm yyyy;hh:mm:ss;'.
	self longPolicyString: 'd mmm yyyy hh:mm:ss;d mmm yyyy;hh:mm:ss;'.

	self policyNamed: #editing putString: 'mm/dd/yyyy h:mm:ss.fff;m/d/yyyy;h:mm:ss am/pm;'.
	dateMiniFormat := #dmy.
	timeSeparator := $:.
	shortWeekdays := #('Mon' 'Tue' 'Wed' 'Thu' 'Fri' 'Sat' 'Sun').
	longWeekdays := #('Monday' 'Tuesday' 'Wednesday' 'Thursday' 'Friday' 'Saturday' 'Sunday').
	shortMonths := #('Jan' 'Feb' 'Mar' 'Apr' 'May' 'Jun' 'Jul' 'Aug' 'Sep' 'Oct' 'Nov' 'Dec').
	longMonths := #('January' 'February' 'March' 'April' 'May' 'June'
				'July' 'August' 'September' 'October' 'November' 'December').
	shortAmPm := #('a' 'p').
	longAmPm := #('am' 'pm')</body>
</methods>

<initialize>
<class-id>Net.HeaderField</class-id>
</initialize>

<initialize>
<class-id>Net.RFC822Scanner</class-id>
</initialize>

<initialize>
<class-id>Net.MimeScanner</class-id>
</initialize>

<initialize>
<class-id>Net.MimeEncodedWordCoDec</class-id>
</initialize>

<do-it>"Imported Classes:"</do-it>

<do-it>self error: 'Attempting to file-in parcel imports.  Choose terminate or close'</do-it>

<class>
<name>Object</name>
<environment>Core</environment>
<super></super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars></inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Kernel-Objects</category>
<attributes>
<package>Kernel-Objects</package>
</attributes>
</class>

<class>
<name>NetError</name>
<environment>Net</environment>
<super>Core.Error</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars></inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Net-Clients</category>
<attributes>
<package>NetClientBase</package>
</attributes>
</class>

<class>
<name>NetNotification</name>
<environment>Net</environment>
<super>Core.Notification</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars></inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Net-Clients</category>
<attributes>
<package>NetClientBase</package>
</attributes>
</class>

<class>
<name>Filename</name>
<environment>OS</environment>
<super>Core.Object</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>osName publicName logicalName </inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>OS-Support</category>
<attributes>
<package>OS-Support</package>
</attributes>
</class>

<class>
<name>TimestampReader</name>
<environment>Core</environment>
<super>Core.LocaleSensitiveDataReader</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>timeSelector dateSelector timestampSelector </inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Internationalization</category>
<attributes>
<package>Internationalization</package>
</attributes>
</class>

<class>
<name>SimpleScanner</name>
<environment>Net</environment>
<super>Core.Object</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>source hereChar token tokenType saveComments currentComment classificationMask sourceTrailStream acceptNonAsciiCharacters </inst-vars>
<class-inst-vars>classificationTable acceptNonAsciiCharacters </class-inst-vars>
<imports></imports>
<category>Net-Clients</category>
<attributes>
<package>NetClientBase</package>
</attributes>
</class>

<class>
<name>StreamEncoder</name>
<environment>Core</environment>
<super>Core.Object</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>encoder errorPolicy skipRecord </inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Collections-Streams</category>
<attributes>
<package>Collections-Streams</package>
</attributes>
</class>

<class>
<name>Stream</name>
<environment>Core</environment>
<super>Core.Object</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars></inst-vars>
<class-inst-vars></class-inst-vars>
<imports>
			private IOConstants.*
			</imports>
<category>Collections-Streams</category>
<attributes>
<package>Collections-Streams</package>
</attributes>
</class>

<class>
<name>MessageElement</name>
<environment>Net</environment>
<super>Core.Object</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>source parent value </inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Net-Clients</category>
<attributes>
<package>NetClientBase</package>
</attributes>
</class>

<class>
<name>TimestampPrintPolicy</name>
<environment>Core</environment>
<super>Core.PrintPolicy</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>policies dateMiniFormat timeSeparator shortWeekdays longWeekdays shortMonths longMonths shortAmPm longAmPm </inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Internationalization</category>
<attributes>
<package>Internationalization</package>
</attributes>
</class>

</st-source>
