<?xml version="1.0"?>

<st-source>
<!-- 
Name: Mail
Notice: Copyright © 1999, 2000, 2002, 2002, 2001 Cincom Systems, Inc.  All Rights Reserved.
Comment: This package builds on top of the more abstract MIME framework to provide practical components for dealing with email specifically. It provides MailMessage and MailFileReader allowing for efficient access of mail messages stored in external files (e.g. unix mbox files). It also provides Mailbox as a high-level interface that makes it simple to retrieve, deliver and delete messages from server side mailboxes. 

NetClients supports three standard email protocols: SMTP for outgoing mail, POP3 for incoming mail, and IMAP for full control over mailboxes on a server. Mail clients implement these specific protocols which carry mail messages between clients and servers. Messages themselves have to be created and formatted as described in the MIME package and below. In building a mail client, you typically need to provide for both sending and receiving mail. POP3 and IMAP are mail receiver clients, and SMTP is a sender client. The mail protocols you choose is determined by the support provided by the server you need to access. For internet ISPs, combining POP3 and SMTP is the most common.


	Mailboxes

The Mailbox class and its subclasses provide a high-level interface that makes it simple to check mailboxes. There are specific subclasses for IMAP and for POP3, in their protocol specific packages.

The following protocol is useful for checking a mailbox:

	#allHeaders - Returns a collection of message headers
	#anyNewMail - Returns a Boolean; true if there are new messages, false otherwise.
	#messageCount - Returns the number of messages in the mailbox.
	#getMessage: anInteger - Returns the message, in an instance of LetterInTransit, specified by anInteger.

You create either an IMAPMailbox or a Pop3Mailbox, and then you can send any of these messages. For example:

	mailbox := IMAPMailbox	"or Pop3Mailbox"
		user: (NetUser username: 'nicki' password: 'hox3')
		server: 'mail.northpole.net'.
	mailbox allHeaders.

If there's already an open connection, it is used for the command and left open. If there isn't already a connection, then one is created, and closed upon command completion. 


	Sending a Message

Normally, a message is constructed and then sent by an appropriate client (HTTP, SMTP, etc.) using the message protocol for that client (e.g., sendMessage: for SMTPClient). As a convenience, MailMessage defines a send message, which sends the message via the default SMTP server, if one is defined. The server is defined in the Net Settings. A default user may also be specified, but is not required if the message From: line has a legal mail address (name and domain). For example, if a default SMTP server is specified in Net Settings, this should succeed:

	message := MailMessage newTextPlain.
	message from: 'santa@northpole.net';
		to: 'jdoe@abc.com';
		subject: 'Start making your list now';
		text: 'What would you like for Christmas?';
		yourself.
	message send.

If a default user is also specified, then the from: 'santa@northpole.net' can also be omitted.


	Extract the best text representation from a message

Some clients can send a message in alternative formats as separate parts, such as plain text and HTML. The message itself has content type multipart/alternative. Alternates are, by convention, placed in the message in order of increasing complexity, so plain text comes first. Clients that can handle multiple formats, such as plain test and HTML, can specify their own preference order for the version to display, by sending textInPreferenceOrder: to the mail message with an ordered collection of preferences. So, if your client displays HTML, you may prefer that format over plain text, but still use plain text if there is no HTML alternate. Follow the list with asterisk (*) to accept any format in the absence of a preferred format. This example extracts html if available, plain text if not, and the first available if neither of those is available.

	message := MailMessage readFrom: message readStream.
	message textInPreferenceOrder: #('html' 'plain' '*').

For more sample how to build mail messages see the MailBuildHandler class comments.


DbIdentifier: bear73
DbTrace: 107160
DevelopmentPrerequisites: #(#(#any 'MIME' ''))
PackageName: Mail
Parcel: #('Mail')
ParcelName: Mail
PrerequisiteParcels: #(#('MIME' ''))
PrintStringCache: (751 28,mkobetic)
Version: 7.6
Date: 12:33:48 pm February 1, 2008
 -->
<time-stamp>From VisualWorks®, 7.6 of February 1, 2008 on February 1, 2008 at 12:33:48 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>UnknownEncoding</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>Mail</package>
</attributes>
</class>

<comment>
<class-id>Net.UnknownEncoding</class-id>
<body>This exception is raised while parsing a message from an external mail archive if any part of the message indicates an encoding that does not have a corresponding encoder present. This exception can be resumed, in which case a default encoding will be applied. Alternatively a resumption parameter, a Symbol, can be specified indicating different encoding to use.


	Examples

	input :=
'From: Fred Foobar &lt;foobar@Blurdybloop.COM&gt;
Subject: afternoon meeting
To: mooch@owatagu.siam.edu
Content-Type: text/plain; charset=klingon

Hello Joe, do you think we can meet at 3:30 tomorrow?
' readStream.

	"Default handling: if the exception is simply resumed, the message body source will use ISO8859-1"
	[	message := MailFileReader readFrom: input.
	]	on: UnknownEncoding 
		do: [ :ex | ex resume  ].
	message contents

	"Overriding the default encoding: to force the message body source to use US-ASCII encoding"
	input reset.
	[	message := MailFileReader readFrom: input
	]	on: UnknownEncoding 
		do: [ :ex | ex resume: #'US-ASCII' ].</body>
</comment>

<class>
<name>MailMessage</name>
<environment>Net</environment>
<super>Net.RFC822Message</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>Mail</package>
</attributes>
</class>

<comment>
<class-id>Net.MailMessage</class-id>
<body>This class further extends the RFC822Message to support the full range of aspects used in email messages today. A mail message has text and zero or more attachments. Mail message's text can have a number of different representations as follows:

	Simple message

This message has no attachments and its contents is text of the message. The message should have text content type ('text/*'); its subtype defines text formatting ('plain', 'rtf', 'html').

	Multipart message

This message uses multipart content type ('multipart/*'). Multipart message contains zero or more parts. One of the subparts is the text part. It may be simple or multipart/alternative part allowing to contain several alternative representations of the message text. This is done by enclosing all alternative representations into one multipart/alternative part.  Mail message itself may be multipart/alternative. In this case the message has no attachments, just alternative representations of its text. Otherwise, the top-level message uses content-type multipart/mixed. The text part is the first part of this message.

This class adds methods to access the message text (part) and to  perform common transformations (add/remove attachment, replace message text, mutate message from multipart into single part and vice versa) in such a way to preserve message structural consistency. This includes changing and/or mutating message body, fields and contents

Message text-related methods allow to specify a list of preferences. This is useful when client can display text in more than one format. Preferences can be specified as array or desired formats i.e. #('html' 'plain' '*'). The default is #('plain' '*') 

The implementation of the part manipulation methods is often not in this class itself, but are actually performed by an associated 'bot' -- a MailMessageBot</body>
</comment>

<class>
<name>MailBuildHandler</name>
<environment>Net</environment>
<super>Net.MimeBuildHandler</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>Mail</package>
</attributes>
</class>

<comment>
<class-id>Net.MailBuildHandler</class-id>
<body>This parser handler builds instances of MailMessage. In general the parser input is expected to be a transient stream (e.g. a socket stream), so the entire content of the message is copied into memory or external files (attachments). Once the message is read in the input stream can be discarded.

	Examples:

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

----11
Content-Type: multipart/alternative;
	boundary="--22"

----22
Content-Type: text/plain;
        charset="us-ascii"
Content-Transfer-Encoding: 7bit

TEXT

----22
Content-Type: text/html;
        charset="us-ascii"
Content-Transfer-Encoding: 7bit

&lt;HTML&gt;&lt;/HTML&gt;

----22--

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

QWxhZGRpbjpvcGVuIHNlc2FtZQ==

----11--
' readStream.

1. Creating an instance of MailMessage from a stream (yields the base64 encoded value)
	
	input reset.
	message := MailBuildHandler readFrom: input.
	message parts last contents

2. Creating a mail message and removing content transfer encoding (yields decoded content)

	input reset.
	message := MailBuildHandler new
		removeContentTransferEncoding: true;
		readFrom: input.
	message parts last contents

3. Parsing only headers first, then the body in a separate step.

	input reset.
	builder := MailBuildHandler new.
	message := builder readHeaderFrom: input.

	"to parse the rest of the message, the body"
	message := builder readBody.		

4. 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 ].


5. 	Handling unsupported characters in header fileds (Cincom™.txt)

When the parsing machinery encounters a character that is not supported by current header field encoding (ISO-8859-1 by default) an UnsupportedCharacterReplacement notification is signaled. A handler for this notification can resume it with arbitrary replacement character, otherwise the character will be replaced with ASCII NUL character (code 0) by default. 

	string :=
'Date: Mon, 19 May 2003 13:52:36 +0200
To: test &lt;test.dst.1@pb.here&gt;
Subject: Test Message
Content-Type: multipart/mixed; boundary="----------lNT1O53UKi8GkFrVZPze3O"

------------lNT1O53UKi8GkFrVZPze3O
Content-Disposition: attachment; filename="Cincom™.txt"
Content-Type: application/octet-stream; name="Cincom™.txt"
Content-Transfer-Encoding: Base64

UPjtbGmaIJ5sdZ1vdehr/SBr+fIg+nDsbCDv4WJlbHNr6SDzZHkuIA0KUNjN
TEmKII5MVY1PVchL3SBL2dIg2lDMTCDPwUJFTFNLySDTRFkuIA==
------------lNT1O53UKi8GkFrVZPze3O--
'.
	[	MimeBuildHandler new
			removeContentTransferEncoding: true;
			readFrom: string readStream. 
	]	on: UnsupportedCharacterReplacement 
		do: [ :ex | ex resume: $_  ].
</body>
</comment>

<class>
<name>MailFileReader</name>
<environment>Net</environment>
<super>Net.MailBuildHandler</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>Mail</package>
</attributes>
</class>

<comment>
<class-id>Net.MailFileReader</class-id>
<body>This is a special purpose parser handler aimed at reading messages from mail archives stored in external files, like the unix mbox files. The primary difference with the other handlers is that the original input stream is expected to be permanent, positionable stream so instead of copying the entire content of the message out of the input stream, the message is built in such a way that the source streams of its individual parts are just StreamSegments pointing back to the original input stream. The fundamental assumption is that the external archive will continue being available for the lifetime of the message objects and therefore there's no reason to waste space by copying its contents. It's quite common that external mail files contain a number of messages. Consequently there are potentially many message objects read from and therefore sharing the same input stream. The application is responsible for closing the input stream when it is done with the messages read from it. Note that accessing source streams of such message will usually reopen the input stream automatically if it is closed, so it is usually pointless to close the input stream right after a message is read. Generally the application will need close the input streams it used when exiting.

Due to the specialized nature of messages built by this class there are some differences that need to be considered. The byteSources of message bodies is always a binary stream on a segment of the original mail file. Since there is potentially a number of messages sharing the original input stream, the byteSources are strictly kept in binary mode to prevent conflicting expectations of different body parts or messages. Consequently the sources of message bodies are stacks of streams that completely decode the body from its stored form. There is no need to explicitly remove transfer encoding on messages built by this class. The message #contents automatically returns a completely decoded body value.


	Examples

1. Reading a mail message

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

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

QWxhZGRpbjpvcGVuIHNlc2FtZQ==

----11--' readStream.
	message := MailFileReader readFrom: input.
	message parts last contents

2. Creating a mail message with headers only.

	input reset.
	reader := MailFileReader new.
	message := reader readHeaderFrom: input.
	message isMultipart

	"Parsing the rest of the message (the body) must use the same reader instance"
	reader readBody.
	message contents.

3. Handling a message with unknown encoding

	input :=
'From: Fred Foobar &lt;foobar@Blurdybloop.COM&gt;
Subject: afternoon meeting
To: mooch@owatagu.siam.edu
Content-Type: text/plain; charset=klingon

Hello Joe, do you think we can meet at 3:30 tomorrow?
' readStream.

	"Default handling: if the exception is simply resumed, the message body source will use ISO8859-1"
	[	message := MailFileReader readFrom: input.
	]	on: UnknownEncoding 
		do: [ :ex | ex resume  ].
	message contents

	"Overriding the default encoding: to force the message body source to use US-ASCII encoding"
	input reset.
	[	message := MailFileReader readFrom: input
	]	on: UnknownEncoding 
		do: [ :ex | ex resume: #'US-ASCII' ].</body>
</comment>

<class>
<name>MailMessageBot</name>
<environment>Net</environment>
<super>Net.MimeEntityBot</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>Mail</package>
</attributes>
</class>

<comment>
<class-id>Net.MailMessageBot</class-id>
<body>This class provides a 'robot' to perform common tasks on mail messages. This class provides methods to query and scan mail message for its text (part), attachments, etc. and to  perform common transformations (add/remove attachment, replace message text, mutate message from multipart into single part and vice versa) in such a way to preserve message structural consistency. This  includes changing and/or mutating message body, fields and contents

Text query methods allow to specify a list of preferences. This is useful when client can display text in more than one format. Preferences can be specified as array or desired formats i.e. #('html' 'plain' '*'). The default is #('plain' '*') 
</body>
</comment>

<class>
<name>LetterInTransit</name>
<environment>Net</environment>
<super>Core.Object</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>letterText deliveryAction uid server serverType </inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Net-Mail</category>
<attributes>
<package>Mail</package>
</attributes>
</class>

<comment>
<class-id>Net.LetterInTransit</class-id>
<body>LetterInTransit is used by Mailbox to hold raw data received from a mail server in a shared queue.

Instance Variables:

	letterText	&lt;String&gt;	mail message text
	deliveryAction	&lt;Block&gt;	some action performed when a message has being processed
	uid	&lt;String&gt;	message uid 
	server	&lt;String&gt;	server name
	serverType	&lt;String&gt;	IMAP/POP3</body>
</comment>

<class>
<name>Mailbox</name>
<environment>Net</environment>
<super>Core.Object</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>user server lastMessage keepMail deleteMail deliveryQueue pollingProcess delaySemaphore sync settingsSync connection </inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Net-Mail</category>
<attributes>
<package>Mail</package>
</attributes>
</class>

<comment>
<class-id>Net.Mailbox</class-id>
<body>The Mailbox class and its subclasses provide a high-level interface that makes it simple to check mailboxes. 

Instance Variables:

	user	&lt;NetUser&gt;	the mailbox user
	server	&lt;HostSpec&gt;	network server
	lastMessage	&lt;Integer&gt;	last message number
	keepMail	&lt;Boolean&gt;	keep mail indicatior
	deleteMail	&lt;Boolean&gt;	delete mail indicator
	deliveryQueue	&lt;SharedQueue&gt;  holds letter in transit
	pollingProcess	&lt;Process&gt;	retrieve new messages from the mail server
	delaySemaphore	&lt;Semaphore&gt;	provides synchronized communication between pollingProcess and main process
	sync	&lt;RecursionLock&gt;	protects access to mailbox
	settingsSync	&lt;RecursionLock&gt;	protects access to 'profile.smr' file (mailbox settings)
	connection	&lt;NetClient&gt;	 specific net client, currently supported IMAP and POP3</body>
</comment>

<class>
<name>MailFileParser</name>
<environment>Net</environment>
<super>Net.MimeParser</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>Mail</package>
</attributes>
</class>

<comment>
<class-id>Net.MailFileParser</class-id>
<body>This is a special purpose parser aimed at reading messages from mail archives stored in external files, like the unix mbox files. The primary difference with the other Mime parsers is that the parsing input is considered to be permanent, positionable stream so instead of copying the entire content of the message out of the input stream, the message is built in such a way that the source streams of its individual parts are just StreamSegments set up on top of the original input stream. The inherent assumption is that the external archive will continue being available for the lifetime of the message objects and therefore there's no reason to waste space by copying its contents. The corresponding handler class for this parser is the MailFileReader.
</body>
</comment>

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

<body package="Mail" selector="defaultResumeValue">defaultResumeValue
	"Answer the default value to resume with."

	^NetConnection defaultEncoding</body>
</methods>

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

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

	^true</body>
</methods>

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

<body package="Mail" selector="allAlternativeTextFormats">allAlternativeTextFormats
" Answer all alternative text representation formats available in this message " 
	^(self bot allTextPartsFor: self) collect: [ :each | each subtype ]</body>

<body package="Mail" selector="allTextParts">allTextParts
	^self bot allTextPartsFor: self</body>

<body package="Mail" selector="text">text
	^self bot textFor: self</body>

<body package="Mail" selector="text:">text: aString
	^self bot replaceTextFor: self with: aString</body>

<body package="Mail" selector="textInPreferenceOrder:">textInPreferenceOrder: preferences
	^self bot textFor: self preferences: preferences</body>

<body package="Mail" selector="textPart">textPart
	^self bot textPartFor: self</body>

<body package="Mail" selector="textPartInPreferenceOrder:">textPartInPreferenceOrder: preferences
	^self bot textPartFor: self preferences: preferences</body>
</methods>

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

<body package="Mail" selector="defaultBotClass">defaultBotClass
	^MailMessageBot</body>
</methods>

<methods>
<class-id>Net.MailMessage</class-id> <category>utility</category>

<body package="Mail" selector="applyTransferEncoding">applyTransferEncoding
	^self bot applyTransferEncodingTo: self</body>

<body package="Mail" selector="checkStructure">checkStructure
	^self bot checkStructure: self</body>

<body package="Mail" selector="prepareForTransport">prepareForTransport
	self checkStructure.
	self applyTransferEncoding.</body>

<body package="Mail" selector="replaceTextWith:">replaceTextWith: aString
	^self bot replaceTextFor: self with: aString</body>

<body package="Mail" selector="saveTextOn:">saveTextOn: aStream
	^self bot writeContentsOf: self textPart on: aStream</body>

<body package="Mail" selector="saveTextOn:inPreferenceOrder:">saveTextOn: aStream inPreferenceOrder: preferences
	^self bot writeContentsOf: (self textPartInPreferenceOrder: preferences) on: aStream</body>
</methods>

<methods>
<class-id>Net.MailMessage</class-id> <category>send</category>

<body package="Mail" selector="send">send

	NetClient defaultOutgoingMailClient send:  self</body>
</methods>

<methods>
<class-id>Net.MailMessage</class-id> <category>mime version</category>

<body package="Mail" selector="addMimeVersion">addMimeVersion

	^self mimeVersion: '1.0'</body>
</methods>

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

<body package="Mail" selector="defaultBuilderType">defaultBuilderType
	^MailBuildHandler</body>
</methods>

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

<body package="Mail" selector="defaultRootEntityType">defaultRootEntityType
	^MailMessage</body>
</methods>

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

<body package="Mail" selector="endBody:">endBody: aMessageBody 

	^aMessageBody parent</body>

<body package="Mail" selector="processBody:from:">processBody: aMessageBody from: aStreamSegment

	^ self prepareStream: aStreamSegment forSimpleBody: aMessageBody</body>
</methods>

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

<body package="Mail" selector="parserClass">parserClass
	^MailFileParser</body>
</methods>

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

<body package="Mail" 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: initialEntity body source reset.
	^initialEntity</body>
</methods>

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

<body package="Mail" selector="addDecodedStreamFor:transferEncoding:">addDecodedStreamFor: aStream transferEncoding: aString

	|   bodySource |
	('base64' ~= aString and: ['quoted-printable' ~= aString ]) ifTrue: [^aStream].
	bodySource := (
		EncodedStream
			on: aStream
			encodedBy: (StreamEncoder new: self defaultEncoding) initializeForMemory
		) lineEndAuto.
	'base64' = aString ifTrue: [
		bodySource := DecodedStream on: bodySource  encodedBy: B64StreamDecoder asEncoder ].
	'quoted-printable' = aString ifTrue: [
		bodySource := DecodedStream on: bodySource  encodedBy: QuotedPrintableDecoder asEncoder ].
	^bodySource</body>

<body package="Mail" selector="encoderForSimpleBody:">encoderForSimpleBody: aMessageBody

	| encoding encoder |
	encoding := aMessageBody parent isBinaryContentType
				ifTrue: [ self defaultEncoding ]
				ifFalse: [ aMessageBody parent charsetIfNone: [ self defaultEncoding ] ].
	encoder :=
		[	StreamEncoder new: encoding.
		]	on: KeyNotFoundError do: [ :ex |
				encoding :=
					UnknownEncoding 
						raiseRequestWith: encoding
						errorString: (#UnknownEncodingExc &lt;&lt; #netclients &gt;&gt; 'Missing encoder for encoding: &lt;1s&gt;'
									expandMacrosWith: encoding ).
				StreamEncoder new: encoding ].
	^encoder initializeForMemory; yourself</body>

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

	^stream := (aStream 
					asEncodedStreamWithEncoding: self defaultEncoding 
					lineEndConvention: LineEndTransparent ) binary</body>

<body package="Mail" selector="prepareStream:forSimpleBody:">prepareStream: aStreamSegment forSimpleBody: aMessageBody

	|   bodySource |
	aMessageBody byteSource: aStreamSegment.
	bodySource := aMessageBody source.
	aMessageBody parent contentTransferEncoding ifNotNil: [ :ce |
		bodySource := self addDecodedStreamFor: bodySource transferEncoding: ce ].

	aMessageBody source: (
		NonClosingReadStream on: (
			( EncodedStream
				on: bodySource
				encodedBy: (self encoderForSimpleBody: aMessageBody )
			) reset;
			lineEndAuto;
			yourself ) ).

	^aMessageBody</body>
</methods>

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

<body package="Mail" selector="allTextPartsFor:">allTextPartsFor: aMailMessage
" Answered sequencable collection of all text parts in the message "
	^self findTextPartCandidatesFor: aMailMessage.</body>

<body package="Mail" selector="applyTransferEncodingTo:">applyTransferEncodingTo: aMimeEntity

	(self attachmentsFor: aMimeEntity) 
		do: [ :attachment | self prepareForTransport: attachment encodeAs: 'base64' ]</body>

<body package="Mail" selector="attachmentsFor:">attachmentsFor: aMimeEntity 
	| textPart attachments |
	textPart := self textPartFor: aMimeEntity preferences: nil.

	textPart parent isNil ifTrue: [ ^OrderedCollection new ].		" Simple entity -- no attachments "

	(textPart parent isMultipartAlternative) ifTrue: [textPart := textPart parent].
	textPart == aMimeEntity ifTrue: [ ^OrderedCollection new ].
	
	attachments := OrderedCollection new: 4.
	self
		for: aMimeEntity
		maxDepth: 2
		partsDo: [:part | (part ~~ textPart and: [part ~~ aMimeEntity])
				ifTrue: [attachments add: part]].
	^attachments</body>

<body package="Mail" selector="findMultipartAlternativeTextPartsFor:">findMultipartAlternativeTextPartsFor: aMimeEntity 
	"Answer all text subparts of specified entity "

	^aMimeEntity parts select: [:subpart | subpart isTextContentType].</body>

<body package="Mail" selector="findMultipartMixedTextPartsFor:">findMultipartMixedTextPartsFor: aMimeEntity 
	"If my body is multipart/mixed, the first text part is message text. 
	Otherwise, the first multipart/alternative part contains alternative 
	representations of message text"

	| part |
	part := aMimeEntity parts detect: [:subpart | subpart isTextContentType]
				ifNone: [nil].

	^part notNil
		ifTrue: [Array with: part]
		ifFalse: 	" Try multipart/alternative now "
			[part := aMimeEntity parts detect: [:subpart | subpart isMultipartAlternative]
						ifNone: [^Array new].
			self findMultipartAlternativeTextPartsFor: part]</body>

<body package="Mail" selector="findTextPartCandidatesFor:">findTextPartCandidatesFor: aMimeEntity 
	aMimeEntity isSimple ifTrue: [^Array with: aMimeEntity].
	^aMimeEntity isMultipartMixed
		ifTrue: [self findMultipartMixedTextPartsFor: aMimeEntity]
		ifFalse: [self findMultipartAlternativeTextPartsFor: aMimeEntity]</body>

<body package="Mail" selector="highLevelTextPartFor:">highLevelTextPartFor: aMimeEntity
" Answer part that either text part itself, or its parent if the part is inside multipart/alternative part "

	| textPart |

	textPart := self textPartFor: aMimeEntity preferences: #('*').
	^(textPart isNil or: [textPart parent isNil or: [textPart parent isMultipart not]])
		ifTrue: [textPart]
		ifFalse: [textPart parent]</body>

<body package="Mail" selector="prepareForTransport:">prepareForTransport: aMimeEntity
	self checkStructure: aMimeEntity.
	self applyTransferEncodingTo: aMimeEntity.</body>

<body package="Mail" selector="prepareForTransport:encodeAs:">prepareForTransport: aMimeEntity encodeAs: encodingName

	self for: aMimeEntity partsDo: [ :part |
		part hasContentTransferEncoding
			ifFalse: [ self applyTransferEncodingTo: part encodeAs: encodingName]].
	^aMimeEntity</body>

<body package="Mail" selector="replaceTextFor:with:">replaceTextFor: aMimeEntity with: aString
" Replace text can result in one of three scenarios: 
	- For simple part text is this part's contents, so all three of aMimeEntity, textPart and containingPart
		are the same. Simply replace part's contents with new text.
	- For multipart/mixed entity containg simple text part aMimeEntity and containingPart are the same. 
		Simply replace contents of text part
	- For multipart/mixed entity containing multipart/alternative text part, containingTextPart is 
		multipart/alternative part containing textPart. The transformation replaces this multipart/alternative
		part with simple text part "

	| partWithText partToReplace |

	(partWithText := self textPartFor: aMimeEntity preferences: #('*')) isNil 
		ifTrue: [^self error: (#InvalidMessage &lt;&lt; #net &gt;&gt; 'Invalid message')].

	" Replace part's text "
	partWithText contents: aString.
	(partWithText ~~ aMimeEntity and: [partWithText parent isMultipartAlternative])
		ifTrue: 
			[" If this is first-level multipart message, replace its body with simple body, 
			otherwise replace it in its parent with (simple) text part "

			partToReplace := partWithText parent.
			(aMimeEntity == partToReplace)
				ifTrue: [aMimeEntity replaceFrom: partWithText]
				ifFalse: [partToReplace parent replacePart: partToReplace with: partWithText ]]</body>

<body package="Mail" selector="textFor:">textFor: aMimeEntity 
" Answer default text representation for specified message "

	^self textFor: aMimeEntity preferences: #('plain' '*')</body>

<body package="Mail" selector="textFor:preferences:">textFor: aMimeEntity preferences: anArray
" Answer mail message text according to the list of preferences "
	| entity |
	^(entity := self textPartFor: aMimeEntity preferences: anArray) isNil
		ifTrue: [ nil ]
		ifFalse: [ entity removeContentTransferEncoding; contents ]</body>

<body package="Mail" selector="textPartFor:">textPartFor: aMimeEntity 
" Answer default text part for specified message "

	^self textPartFor: aMimeEntity preferences: #('plain' '*')</body>

<body package="Mail" selector="textPartFor:preferences:">textPartFor: aMimeEntity preferences: anArray 
" Answer part containing message text according to the list of preferences. The list is
the form of #(bestPref nextBestPref ...). For example #('html' 'plain' '*') "

	| candidates |

	" First get all candidate text parts "
	candidates := self findTextPartCandidatesFor: aMimeEntity.
	^self bestMatchFrom: candidates preferences: anArray</body>
</methods>

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

<body package="Mail" selector="bestMatchFrom:preferences:">bestMatchFrom: coll preferences: prefArray 
	| sorted candidates |
	^coll isEmpty
		ifTrue: [nil]
		ifFalse: 
			[(prefArray isNil or: [prefArray isEmpty]) ifTrue: [ ^coll first ].
			" Then select only those that match at least one condition "
			candidates := coll select: [ :candidate | (self indexOf: candidate subtype in: prefArray) &gt; 0 ].
			candidates isEmpty ifTrue: [ ^nil ].
			sorted := (SortedCollection sortBlock: 
				[:x :y | (self indexOf: x subtype in: prefArray) &lt; (self indexOf: y subtype in: prefArray)])
				addAll: candidates; 
				yourself.
				sorted first]</body>

<body package="Mail" selector="indexOf:in:">indexOf: aString in: seqColl 

	1 to: seqColl size 
		do: [:idx | ((seqColl at: idx) match: aString) ifTrue: [^idx]].
	^0</body>
</methods>

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

<body package="Mail" selector="delivered">delivered
	deliveryAction value.
	letterText := nil.
	uid := nil</body>

<body package="Mail" selector="deliveryAction">deliveryAction
	^deliveryAction</body>

<body package="Mail" selector="deliveryAction:">deliveryAction: aBlock
	deliveryAction := aBlock</body>

<body package="Mail" selector="letterText">letterText
	^letterText</body>

<body package="Mail" selector="letterText:">letterText: aString
	letterText := aString</body>

<body package="Mail" selector="server">server
	^server</body>

<body package="Mail" selector="server:">server: aString
	server := aString</body>

<body package="Mail" selector="serverType">serverType

	^serverType</body>

<body package="Mail" selector="serverType:">serverType: aString

	serverType := aString</body>

<body package="Mail" selector="size">size

	^letterText size</body>

<body package="Mail" selector="uid">uid
	^uid</body>

<body package="Mail" selector="uid:">uid: aString

	uid := aString</body>
</methods>

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

<body package="Mail" selector="letterText:onDelivery:">letterText: aString onDelivery: aBlock
	letterText := aString.
	deliveryAction := aBlock</body>
</methods>

<methods>
<class-id>Net.LetterInTransit</class-id> <category>delivery</category>

<body package="Mail" selector="sendOn:">sendOn: aStream
	"Write the letter to aStream"
" Old code for POP3
	(letterText size &lt; 5
	or: [(letterText copyFrom: 1 to: 5) ~= 'From ']) ifTrue:
		[self halt: 'First line of header should be a From line.  It isn''t!'].
"
	aStream nextPutAll: letterText</body>
</methods>

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

<body package="Mail" selector="letterText:onDelivery:">letterText: aLetter onDelivery: aBlock
	^self new 
		letterText: aLetter onDelivery: aBlock;
		yourself</body>
</methods>

<methods>
<class-id>Net.Mailbox</class-id> <category>commands</category>

<body package="Mail" selector="allHeaders">allHeaders
	self subclassResponsibility</body>

<body package="Mail" selector="anyNewMail">anyNewMail
	self subclassResponsibility</body>

<body package="Mail" selector="deleteMessageNumbers:">deleteMessageNumbers: aNumber
	self subclassResponsibility</body>

<body package="Mail" selector="deliverNewMail">deliverNewMail
	self subclassResponsibility</body>

<body package="Mail" selector="getMessage:">getMessage: number 

	self subclassResponsibility</body>

<body package="Mail" selector="messageCount">messageCount
	"How many messages are on the server?"
	self subclassResponsibility</body>

<body package="Mail" selector="pollForNewMail">pollForNewMail
	delaySemaphore ~~ nil ifTrue: [delaySemaphore signal]</body>
</methods>

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

<body package="Mail" selector="clientSpecies">clientSpecies
	self subclassResponsibility</body>

<body package="Mail" selector="createConnection">createConnection

	(user isNil or: [self server isNil]) ifTrue: [^nil].
	connection := self clientSpecies connectToHost: self server name port: self server port. 
	[connection user: self user; login]
		on: NetClientError
		do: [:ex | | newUser |
			ex parameter == #connection ifTrue: [ ex pass].
			ex parameter == #login 
				ifTrue:
					[(newUser := self requestNewUser)  isNil ifTrue: [ex pass].
					self user: newUser.
					ex retry]
				ifFalse: [ex pass]].
	^connection</body>

<body package="Mail" selector="deleteUids:">deleteUids: aSequenceOfUids
	"Delete all the letters on the server.  Ignore non-existent ones"

	^sync critical:
		[(deliveryQueue isNil
		 or: [deliveryQueue isEmpty]) ifFalse:
			[ self error: (#TryingToDeleteLetters &lt;&lt; #net &gt;&gt; 'trying to delete letters when delivery queue is not empty.
This will mess up message numbers.')].
		 self connectedDo: [
			connection isNil ifTrue:
				[self class log: ((#CouldNotLoginToDeleteLetters &lt;&lt; #net &gt;&gt; 'could not login to delete letters on &lt;1s&gt;') expandMacrosWith: self serverName).
				 ^false].
			lastMessage := self deleteMessageNumbers: aSequenceOfUids.
			self saveSettings.
			true]]</body>

<body package="Mail" selector="deliver:">deliver: aLetterInTransit

	self deliveryQueue nextPut: aLetterInTransit.
	#{Net.MailReader} ifDefinedDo:
		[:mailReader| | activeInstance |
		(activeInstance := mailReader activeInstance) ~~ nil ifTrue:
			[activeInstance checkStatus]]</body>

<body package="Mail" selector="loadSettings">loadSettings
	| data filename lines |
	settingsSync critical:
		[(filename := self settingsFilename asFilename) definitelyExists ifFalse: [ lastMessage := 0. ^self].
		data := filename contentsOfEntireFile].
	lines := data tokensBasedOn: $, .
	lastMessage := (Number readFrom: (lines at: 2) readStream).</body>

<body package="Mail" selector="notifyServerMessageRead:">notifyServerMessageRead: uid
	^nil</body>

<body package="Mail" selector="requestNewUser">requestNewUser	
	| username password cancel |
	username := self user username asValue.
	password := '' asValue.
	cancel := (SimpleDialog initializedFor: nil)
				setInitialGap;
				addMessage: ((#YourLoginFailedFor1s &lt;&lt; #net &gt;&gt; 'Your login failed for &lt;1s&gt;') expandMacrosWith: self serverName) centered: true;
				addGap;
				addMessage: (#PleaseTryAgain &lt;&lt; #net &gt;&gt; 'Please try again.') centered: true;
				addGap;
				addMessage: (#Username &lt;&lt; #net &gt;&gt; 'Username') textLine: username boundary: 0.4;
				addGap;
				addMessage: (#Password &lt;&lt; #net &gt;&gt; 'Password') textLine: password type: #password boundary: 0.4;
				addGap;
				addOK: [true];
				openDialog;
				cancel.
	cancel value ifTrue: [^nil].
	^NetUser new 
		username: username value; 
		password: password value;
		yourself</body>

<body package="Mail" selector="safelyExecute:">safelyExecute: block
" Ensure there is a connection; execute a block and make sure response is OK.
Raise an appropriate exception if there is an error. Make sure connection is released
if we had to acquire it "

	^sync critical: [ self connectedDo: block.]</body>

<body package="Mail" selector="saveSettings">saveSettings
	self saveSettings: self lastMessage</body>

<body package="Mail" selector="saveSettings:">saveSettings: n
	settingsSync critical:
		[| output |output := self settingsFilename asFilename writeStream.
		[output 
			nextPutAll: self serverName;
			nextPut: $, ;
			print: n] ensure: [output close]]</body>

<body package="Mail" selector="settingsFilename">settingsFilename
	| basename |
	basename := self serverName, '.mailbox'.
	^#{MailFile}
		ifDefinedDo: [:mailFile| mailFile fileName: basename]
		elseDo: [basename asFilename]</body>
</methods>

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

<body package="Mail" selector="connectedDo:">connectedDo: aBlock 
	| shouldDisconnect |

	shouldDisconnect := connection isNil
		ifTrue: [connection := self createConnection. true]
		ifFalse: [false].

	^aBlock 
	ensure: [shouldDisconnect ifTrue: [self logout]]</body>

<body package="Mail" selector="connection">connection

	^connection</body>

<body package="Mail" selector="deleteMail">deleteMail
	^deleteMail isNil
		ifTrue: [deleteMail := true]
		ifFalse: [deleteMail]</body>

<body package="Mail" selector="deliveryQueue">deliveryQueue
	^deliveryQueue notNil
		ifTrue: [deliveryQueue]
		ifFalse: [ deliveryQueue := SharedQueue new]</body>

<body package="Mail" selector="deliveryQueue:">deliveryQueue: sharedQueue 
	deliveryQueue := sharedQueue</body>

<body package="Mail" selector="keepMail">keepMail
	^keepMail isNil
		ifTrue: [keepMail := true]
		ifFalse: [keepMail]</body>

<body package="Mail" selector="lastMessage">lastMessage
	^lastMessage notNil
		ifTrue: [ lastMessage]
		ifFalse: [ lastMessage := 0]</body>

<body package="Mail" selector="lastMessage:">lastMessage: aNumber
	lastMessage := aNumber</body>

<body package="Mail" selector="logout">logout

	^self subclassResponsibility</body>

<body package="Mail" selector="server">server

	^server</body>

<body package="Mail" selector="server:">server: aStringOrHostSpec
	server := (aStringOrHostSpec isString
				ifTrue: [ HostSpec new name: aStringOrHostSpec; yourself ]
				ifFalse: [ aStringOrHostSpec ])</body>

<body package="Mail" selector="serverName">serverName

	^self server name</body>

<body package="Mail" selector="serverType">serverType
	self subclassResponsibility</body>

<body package="Mail" selector="terminate">terminate
	pollingProcess isNil ifFalse: 
		[pollingProcess terminate.
		"We should find a way to clean up transactions in progress."]</body>

<body package="Mail" selector="user">user

	^user</body>

<body package="Mail" selector="user:">user: aValue

	user := aValue</body>
</methods>

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

<body package="Mail" selector="initialize">initialize
	sync := RecursionLock new.
	settingsSync := RecursionLock new.</body>
</methods>

<methods>
<class-id>Net.Mailbox</class-id> <category>debugging</category>

<body package="Mail" selector="debug:level:">debug: aStringOrBlock level: aLevel
	#{Tools.LogEnvironment} ifDefinedDo: [ :le | le  default debug: aStringOrBlock level: aLevel ]</body>

<body package="Mail" selector="log:level:">log: aStringOrBlock level: aLevel 
	self class log: aStringOrBlock level: aLevel</body>
</methods>

<methods>
<class-id>Net.Mailbox</class-id> <category>mail delivery</category>

<body package="Mail" selector="startPollingAt:every:">startPollingAt: priority every: seconds 
	pollingProcess isNil ifFalse: [pollingProcess terminate].
	pollingProcess := [
			[| delay |
			[self anyNewMail] whileTrue: [self deliverNewMail].
			delay := Delay forSeconds: seconds.
			delaySemaphore := delay delaySemaphore.
			delay wait] repeat]
				forkAt: priority
"
lastMessage := 0. self deliverAllMail.
"</body>
</methods>

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

<body package="Mail" selector="defaultPollingPriority">defaultPollingPriority
	^30</body>

<body package="Mail" selector="userClass">userClass
	^NetUser</body>
</methods>

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

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

<body package="Mail" selector="startFetchMailProcessWithPriority:from:for:password:to:pollingInterval:">startFetchMailProcessWithPriority: priority from: serverName for: user password: password to: deliveryQueue pollingInterval: pollTime

	| mailbox popUser |
	mailbox := self new.
	popUser := NetUser username: user password: password.
	mailbox 
		user: popUser;
		server: serverName;
		deliveryQueue: deliveryQueue;
		loadSettings.
	^mailbox 
		startPollingAt: (priority isNil ifTrue: [self defaultPollingPriority] ifFalse: [priority]) 
		every: pollTime</body>

<body package="Mail" selector="user:">user: popUser
	| mailbox |
	mailbox := self new.
	^mailbox 
		user: popUser;
		yourself</body>

<body package="Mail" selector="user:server:">user: popUser server: server
	"Pop3Mailbox 
		user: PopUser request
		server: (Dialog request: 'Pop Server:' initialAnswer: 'sulu.cincom.com')"

	| mailbox |
	mailbox := self new.
	^mailbox 
		user: popUser;
		server: server;
		yourself</body>
</methods>

<methods>
<class-id>Net.Mailbox class</class-id> <category>debugging</category>

<body package="Mail" selector="log:level:">log: aStringOrBlock level: aLevel
	#{Tools.LogEnvironment} ifDefinedDo: [ :le | le  default log: aStringOrBlock level: aLevel ]</body>

<body package="Mail" selector="registerToDebug">registerToDebug

	#{Tools.LogEnvironment} ifDefinedDo: [ :le | le  default addToDebug: self ]</body>
</methods>

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

<body package="Mail" selector="parse:">parse:  aStreamSegment
	| entity  |
	entity := self startMessageFrom: aStreamSegment.
	self parse:  entity from: aStreamSegment.
	self handler endMessage: entity.

	^entity</body>

<body package="Mail" selector="parse:from:">parse:  entity from: aStreamSegment
	| header   |

	aStreamSegment reset.
	entity source: aStreamSegment.
	header := self parseHeaderFrom: aStreamSegment.
	self handler addHeader: header toEntity: entity.
	entity body source: (aStreamSegment newSize: (aStreamSegment size - aStreamSegment position)).
	self parseMessageBody: entity from: entity body source.</body>

<body package="Mail" selector="parseHeaderFrom:">parseHeaderFrom: aStream 

	| headerx |
	headerx := self handler startHeader: nil.
	self privateParseHeader: headerx scanner: aStream.
	^self handler endHeader: headerx.</body>

<body package="Mail" selector="parseMessageBody:from:">parseMessageBody: aMessage from: aStream

	(self handler shouldStartMessageBody:  aMessage body) 
		ifTrue: [	self startMessageBody: aMessage body from: aStream.
				self parseBody: aMessage body from: aStream.
				self endMessageBody: aMessage body from: aStream].

	^aMessage</body>

<body package="Mail" selector="parseMultipartBody:from:">parseMultipartBody: aMultipartBody from: aStreamSegment 
	| parent part startPos pattern stream lineEndSize |
	stream :=  (EncodedStream 
					on: aStreamSegment 
					encodedBy: (StreamEncoder new: self handler defaultEncoding))  lineEndAuto.
	lineEndSize := stream segmentLineEndSize.
	pattern  := MultipartBody boundaryDashes , aMultipartBody boundary.
	stream skipThroughAll: pattern.
	stream next. "skipping line end "
	parent := aMultipartBody parent.
	[	part := self handler startPartFor: parent.
		startPos := aStreamSegment position.
		stream skipThroughAll: pattern. 
		part source: (aStreamSegment from: startPos to: aStreamSegment position - pattern size - lineEndSize ).
		self parse: part from: part source.
		(stream atEnd not and: [ stream next  ~= $- ])
	] whileTrue</body>

<body package="Mail" selector="parseSimpleBody:from:">parseSimpleBody: aBody from: aStreamSegment
	
	^self handler processBody: aBody from: aStreamSegment</body>
</methods>

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

<body package="Mail" selector="privateParseHeader:scanner:">privateParseHeader: header scanner: aStream
	| factory field startPos endPos string aMimeScanner stream |

	factory := self handler fieldFactory.
	stream := (EncodedStream 
				on:  aStream
				encodedBy: (StreamEncoder new: self handler defaultEncoding)) lineEndAuto.

	aMimeScanner := self scannerOn: stream.
	[aMimeScanner atEnd 
		or: [aMimeScanner peekForEndOfLine ]
	] whileFalse:
			[startPos := aMimeScanner position.
			string := aMimeScanner nextLineWithoutTerminator.
			endPos := aMimeScanner position.
			aMimeScanner peekForEndOfLine.
			(field := factory readFrom: string readStream) 
				ifNotNil: [field source: (EncodedStream 
										on: (aStream from: startPos to: endPos) 
										encodedBy: (StreamEncoder new: self handler defaultEncoding) ).
						header addField: field] ].</body>
</methods>

<methods>
<class-id>Net.Settings class</class-id> <category>accessing settings</category>

<body package="Mail" selector="removeContentTransferEncoding">removeContentTransferEncoding

	^MailBuildHandler removeContentTransferEncoding</body>

<body package="Mail" selector="removeContentTransferEncoding:">removeContentTransferEncoding: aBoolean

	MailBuildHandler removeContentTransferEncoding: aBoolean</body>
</methods>

<methods>
<class-id>Core.Stream</class-id> <category>accessing</category>

<body package="Mail" selector="segmentLineEndSize">segmentLineEndSize
"What is the byte size of the line end convention used by the underlying stream segment."

	^nil</body>
</methods>

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

<body package="Mail" selector="segmentLineEndSize">segmentLineEndSize
"What is the byte size of the line end convention used by the underlying stream segment."
	
	^source segmentLineEndSize</body>
</methods>

<methods>
<class-id>Core.InternalStream</class-id> <category>accessing</category>

<body package="Mail" selector="segmentLineEndSize">segmentLineEndSize
"What is the byte size of the line end convention used by the underlying stream segment."

	^self isCharacterStream ifTrue: [ 1 ] ifFalse: [ nil ]</body>
</methods>

<methods>
<class-id>Core.EncodedStream</class-id> <category>accessing</category>

<body package="Mail" selector="segmentLineEndSize">segmentLineEndSize
"What is the byte size of the line end convention used by the underlying stream segment."
	
	^stream segmentLineEndSize ifNil: [ self lineEndConvention == LineEndCRLF ifTrue: [2] ifFalse: [1]]</body>
</methods>

<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>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>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>

<class>
<name>StreamSegment</name>
<environment>Net</environment>
<super>Core.Object</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>source offset size position </inst-vars>
<class-inst-vars></class-inst-vars>
<imports>
			private OS.IOConstants.*
			</imports>
<category>Net-Clients</category>
<attributes>
<package>NetClientBase</package>
</attributes>
</class>

<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>

<class>
<name>InternalStream</name>
<environment>Core</environment>
<super>Core.PositionableStream</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars></inst-vars>
<class-inst-vars></class-inst-vars>
<imports></imports>
<category>Collections-Streams</category>
<attributes>
<package>Collections-Streams</package>
</attributes>
</class>

<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>

<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>

<class>
<name>EncodedStream</name>
<environment>Core</environment>
<super>Core.PeekableStream</super>
<private>false</private>
<indexed-type>none</indexed-type>
<inst-vars>binary stream encoder policy lineEndConvention lineEndCharacter skipSize </inst-vars>
<class-inst-vars></class-inst-vars>
<imports>
			private OS.IOConstants.*
			</imports>
<category>Collections-Streams</category>
<attributes>
<package>Collections-Streams</package>
</attributes>
</class>

</st-source>
