Internet Clients
Introduction
The NetClients parcels contain frameworks and class libraries for VisualWorks that provide general access to common internet communications facilities. Specifically, it provides the following:
Uniform Resource Identification (URI) framework , to represent and access resources.
FTP framework , to access remote files and directories.
RFC822 and MIME framework , to work with e-mail/HTTP messages.
POP3 client engine framework , to receive e-mail messages and maintain mail folders.
IMAP4rev1 client engine framework , to receive e-mail messages and maintain mail folders.
SMTP client engine framework , to send e-mail messages.
HTTP client engine framework , to send and receive HTTP messages.
Loading Internet Connectivity
NetClients support is contained in a set of parcels in the net/ subdirectory. Support for specific protocols can be loaded, or support for all protocols in one action.
Using the Parcel Manager, select the Networking suggestion group. For individual protocols, load the parcel named for that protocol. To load the whole collection of NetClients supported protocols, load the NetClients parcel. In either case, all necessary prerequisites are also loaded.
FTP
File Transfer Protocol (FTP) is a popular protocol for transferring (copying) files from any host implementing TCP/IP to any other host implementing TCP/IP.
An FTP session requires a client and server: the FTP client initiates a file transfer and the FTP server serves the FTP client requests. Most hosts that have a TCP/IP protocol suite have FTP.
In VisualWorks, there are three "levels" of FTP that you can use. They are listed here, from easiest to most complicated to use. Each is explained further in the associated section of this chapter.
FtpURL. See Using FtpURL .
FTPClient. See Using FTPClient (FTP Protocol Level) .
FTP Commands. See Using FTP commands and responses .
To use FTP, you need an account (user name) and password on the remote server. Some sites have a set of files available for public access. To use these files, you do not need to have an account or password. You can, instead, access them using the anonymous user name.
Using FtpURL
An URL is a Uniform Resource Locator, a standard for specifying the location of a resource that is available electronically. URLs are defined by RFC 1738 and RFC 2396, to which you can look for more definitive technical information.
FtpURL
within VisualWorks mimics `Filename` . You can access remote files just like local files. Although this makes accessing remote files easy, you will need the following:
You must know exact names of directories and files on the remote server.
You must know the user name and password for access to a secured FTP server.
Identify a remote FTP server
Following is the standard syntax for accessing a remote FTP server:
ftp://<username>:<password>@<host>:<port>/<path>
Defaults
The <port> , <username> , and <password> parameters are all optional; specifically:
If you do not specify a port, the default (21) will be used.
If you do not specify a user, the default (anonymous) will be used.
If you do not specify a password, the default (your e-mail address) will be used.
Caution: It can be dangerous to use the <username>:<password> combination in the URL, because the password is not encrypted. If you are sending a password, it will be visible to anyone monitoring your network traffic. Therefore, it is preferable to use "anonymous" access (the default) whenever possible.
The "minimum" URL will look like this:
ftp://<host>/<path>
The host is the fully qualified domain name of the system, and the path is the hierarchical directory path of the form directory/directory/filename . The host can be left as an empty string or "localhost" to refer to local files on the client on which the URL is being interpreted.
Using special symbols
If your password includes certain symbols (for example, an "@" in an e-mail address), you must replace the symbol with a string representing its hex value (for example, replace "@" with "%40"). Other characters (especially in the password) should also be encoded so they are not confused with the URL syntax.
Some of the more common symbols are:
Symbol Replacement String
% (percent) %25
, (comma) %2C
/ (forward slash) %2F
: (colon) %3A
; (semi-colon) %3A
@ (at sign) %40
(space) %20
Download a file from a remote server (Binary mode)
The following code copies the file hello.txt from the visualworks directory into the "home" directory of VisualWorks on the local machine. The user zzuser with password zzpassword is used to logon to the yourftpserver FTP server. The file will be transferred in Binary (image) mode, the default transfer mode for URI.
'ftp://zzuser:zzpassword@yourftpserver/visualworks/hello.txt' asURI
copyTo: 'helloworld.txt' asFilename asURI.
Download a file from a remote server (Text mode)
The following code copies the file hello.txt from the visualworks directory into the "home" directory of VisualWorks on the local machine. However, on the local system, the file will be called helloworld.txt . The user zzuser with password zzpassword is used to logon to the yourftpserver FTP server. Because the default transfer mode is Binary, you must use streams in order to transfer in text (ASCII) mode.
| output |
output := 'helloworld.txt' asFilename writeStream.
'ftp://zzuser:zzpassword@yourftpserver/visualworks/hello.txt' asURI
readStreamDo: [ :stream :params | stream text.
[stream atEnd] whileFalse: [output nextPut: stream next] ].
output close.
Note: For uploading/downloading files in Text (ASCII) mode, it is best to use either the Stream methods for FtpURL (see Stream operations ) or, better yet, FTPClient commands (see Using FTPClient (FTP Protocol Level) ).
Upload a file to a remote server (Binary mode)
The following code copies the file vwlogo.jpg (found in the "home" directory of VisualWorks on the local machine) to the visualworks directory. The user zzuser with password zzpassword is used to logon to the yourftpserver FTP server.
'vwlogo.jpg' asFilename asURI copyTo:
'ftp://zzuser:zzpassword@yourftpserver/visualworks/vwlogo.jpg' asURI.
Upload a file to a remote server (Text mode)
The following code copies the file hello.txt (found in the "home" directory of VisualWorks on the local machine) to the visualworks directory. The user zzuser with password zzpassword is used to logon to the yourftpserver FTP server. It is assumed that hello.txt is a simple text file, since streams are being used.
| input |
input := 'hello.txt' asFilename readStream.
'ftp://zzuser:zzpassword@yourftpserver/visualworks/hello.txt' asURI
writeStreamDo: [ :stream | stream nextPutAll: input text; cr].
input close.
Create a new directory
The following code creates the directory testDir2 under the existing visualworks directory. The user zzuser with password zzpassword is used to logon to the yourftpserver FTP server.
'ftp://zzuser:zzpassword@yourftpserver/visualworks/testDir2' asURI makeDirectory.
Delete a directory
The following code deletes the directory testDir2 under the existing visualworks directory. The user zzuser with password zzpassword is used to logon to the yourftpserver FTP server. If the directory is not empty, an error will result: "550 : The directory is not empty". An error will also result if the directory cannot be found: "550 : The system cannot find the file specified".
'ftp://zzuser:zzpassword@yourftpserver/visualworks/testDir2' asURI removeDirectory.
List files in a directory
The following code collects the contents of the visualworks directory. The user zzuser with password zzpassword is used to logon to the yourftpserver FTP server. This will return an Ordered Collection of file details.
'ftp://zzuser:zzpassword@yourftpserver/visualworks' asURI directoryContents.
Delete a file
The following code deletes the file file1.txt from the visualworks directory. The user zzuser with password zzpassword is used to logon to the yourftpserver FTP server. An error will result if the file cannot be found: "550 : The system cannot find the file specified".
'ftp://zzuser:zzpassword@yourftpserver/visualworks/file1.txt' asURI delete.
Determine the size of a file
The following code returns the size (in bytes) of the file file1.txt found in the visualworks directory. The user zzuser with password zzpassword is used to logon to the yourftpserver FTP server.
'ftp://zzuser:zzpassword@yourftpserver/visualworks/file1.txt' asURI fileSize.
Determine if a file or directory exists
The following code returns either true or false, depending on the existence of the file file1.txt in the visualworks directory. The user zzuser with password zzpassword is used to logon to the yourftpserver FTP server.
'ftp://zzuser:zzpassword@yourftpserver/visualworks/file1.txt' asURI exists.
Determine if the URI is a directory
The following code returns either true or false depending on whether or not the file file1.txt in the visualworks directory is a directory. The user zzuser with password zzpassword is used to logon to the yourftpserver FTP server.
'ftp://zzuser:zzpassword@yourftpserver/visualworks/file1.txt' asURI isDirectory.
Stream operations
There are two stream operations. Both of these methods use guarded transfer, which ensures that streams are closed after use:
readStreamDo:
Read the resource that the URI represents. The argument to the message is a block that takes a stream as its first argument and a Dictionary of properties as its second argument. If the URI allows you to know or guess the MIME type of the data, that will appear in the dictionary under the key `#MIME` . The dictionary may have other keys, depending on the type of URI and the server which has the resource. The stream must be used within the block only. It will be closed when the block finishes executing. The return value of `#readStreamDo:` will be the value returned by the block.
writeStreamDo:
Write a new resource at its address. The argument to the message is a block that takes a stream as its only argument. The stream must be used within the block only. It will be closed when the block finishes executing. The return value of `#writeStreamDo:` will be the value returned by the block.
For more information about stream operations, refer to the VisualWorks Application Developer's Guide .
Create a file on a remote server
The following code creates a file called file1.txt in the visualworks directory. The contents of the file are created "on the fly" using a stream method. The user zzuser with password zzpassword is used to logon to the yourftpserver FTP server.
'ftp://zzuser:zzpassword@yourftpserver/visualworks/file1.txt' asURI
writeStreamDo: [ :ftpStream | ftpStream text;
nextPutAll: 'blah blah'; cr].
Upload a file to a remote server
The following code copies a file called test.text (found in the "home" directory of VisualWorks on the local machine) to the visualworks directory. The file in the visualworks directory will be called hello4.txt . It is assumed that test.text is a simple text file since streams are being used. The user zzuser with password zzpassword is used to logon to the yourftpserver FTP server.
| input output |
input := 'test.text' asFilename readStream.
output := 'ftp://zzuser:zzpassword@yourftpserver/visualworks/hello4.txt' asURI.
[input atEnd] whileFalse: [output writeStreamDo:
[ :ftpStream | ftpStream text; nextPutAll: input text.]].
input close.
Read a file from a remote server
The following code returns a stream containing the contents of a file called test.text found in the visualworks directory. The user zzuser with password zzpassword is used to logon to the yourftpserver FTP server.
'ftp://zzuser:zzpassword@yourftpserver/visualworks/file1.txt' asURI
readStreamDo: [:stream :params | stream contents inspect].
Download a file from a remote server (Text mode)
The following code copies a file called hello.txt from the visualworks directory to a file called helloworld.txt , which will be found in the "home" directory of VisualWorks on the local machine. It is assumed that hello.txt is a simple text file since streams are being used. The user zzuser with password zzpassword is used to logon to the yourftpserver FTP server.
| output |
output := 'helloworld.txt' asFilename writeStream.
'ftp://zzuser:zzpassword@yourftpserver/visualworks/hello.txt' asURI
readStreamDo: [ :stream :params | stream text.[stream atEnd]
whileFalse: [output nextPut: stream next] ].
output close.
Using FTPClient (FTP Protocol Level)
FtpURL
uses methods of the `FTPClient` class. As such, you have much more FTP functionality within your application using `FTPClient` than with `FtpURL` . For example, when using `FtpURL` , it is not possible to directly transfer a file from remote server A to remote server B, nor is it possible to rename a file on a remote server.
Although you have more control than `FtpURL` , there are still some restrictions:
You cannot access subdirectories directly, as you can with `FtpURL` .
You still must know the name and password for access to a secured FTP server.
The same rules apply for user and password under `FTPClient` as under `FtpURL` .
Note: It is good practice to explicitly close the connection with the server, using `FTPClient` , once you have successfully logged into the server.
Using `FTPClient` is very similar to using FTP from a command-line interface. The sequence of methods used for `FTPClient` nearly mimics the typical sequence of commands used during an FTP session with a command-line interface.
Download a file from a remote server
The following code copies the file hello.txt from the visualworks directory into the "home" directory of VisualWorks on the local machine. On the local system, however, the file will be called helloworld.txt . The user zzuser with password zzpassword is used to logon to the yourftpserver FTP server. The file will be transferred in text (ASCII) mode, since the default transfer of Binary (Image) is being overridden with the `textTransfer` method.
| ftpClient |
ftpClient:=FTPClient loginToHost: 'yourftpserver'
asUser: 'zzuser' withPassword: 'zzpassword'.
ftpClient beCurrentDirectory: 'visualworks'.
ftpClient textTransfer.
ftpClient retrieveFileNamed: 'hello.txt' as: 'helloworld.txt'.
ftpClient close.
Upload a file to a remote server (Text mode)
The following code copies the file helloworld.txt from the "home" directory of VisualWorks on the local machine to the visualworks directory on the server. On the remote server, however, the file will be called hello.txt . The user zzuser with password zzpassword is used to logon to the yourftpserver FTP server. The file will be transferred in text (ASCII) mode, since the default transfer of Binary (Image) is being overridden with the `textTransfer` method.
| ftpClient |
ftpClient:=FTPClient loginToHost: 'yourftpserver'
asUser: 'zzuser' withPassword: 'zzpassword'.
ftpClient beCurrentDirectory: 'visualworks'.
ftpClient textTransfer.
ftpClient storeFileNamed: 'helloworld.txt' to: 'hello.txt'.
ftpClient close.
Upload a file to a remote server (Binary mode)
The following code copies the file vwlogo.jpg from the "home" directory of VisualWorks on the local machine to the visualworks directory on the server. On the remote server, however, the file will be called vwlogo2.jpg . The user zzuser with password zzpassword is used to logon to the yourftpserver FTP server. Although not necessary, the `binaryTransfer` method is being used here to ensure that the file will be transferred in Binary (Image) mode.
| ftpClient |
ftpClient:=FTPClient loginToHost: 'yourftpserver'
asUser: 'zzuser' withPassword: 'zzpassword'.
ftpClient beCurrentDirectory: 'visualworks'.
ftpClient binaryTransfer.
ftpClient storeFileNamed: 'vwlogo.jpg' to: 'vwlogo2.jpg'.
ftpClient close.
Create a new directory
The following code creates the directory visualworks under the existing "root" directory. The user zzuser with password zzpassword is used to logon to the yourftpserver FTP server.
| ftpClient |
ftpClient:=FTPClient loginToHost: 'yourftpserver'
asUser: 'zzuser' withPassword: 'zzpassword'.
ftpClient makeDirectory: 'visualworks'.
ftpClient close.
Delete a directory
The following code deletes the directory testDir2 under the existing visualworks directory. The user zzuser with password zzpassword is used to logon to the yourftpserver FTP server. If the directory is not empty, an error will result: "550 : The directory is not empty". An error will also result if the directory cannot be found: "550 : The system cannot find the file specified".
| ftpClient |
ftpClient:=FTPClient loginToHost: 'yourftpserver'
asUser: 'zzuser' withPassword: 'zzpassword'.
ftpClient removeDirectory: 'visualworks'.
ftpClient close.
List all files in a directory (with details)
The following code collects the contents of the visualworks directory. The user zzuser with password zzpassword is used to logon to the yourftpserver FTP server. This code will return an `OrderedCollection` of file details.
| ftpClient |
ftpClient:=FTPClient loginToHost: 'yourftpserver'
asUser: 'zzuser' withPassword: 'zzpassword'.
ftpClient beCurrentDirectory: 'visualworks'.
ftpClient directoryContents: #() do:
[ :entry | Transcript show: entry; cr ].
ftpClient close.
List files by wildcard (with details)
The following code collects the contents of the visualworks directory but only those files and directories that begin with the letter "T". The user zzuser with password zzpassword is used to logon to the yourftpserver FTP server. This code will return an `OrderedCollection` of file details.
| ftpClient |
ftpClient:=FTPClient loginToHost: 'yourftpserver'
asUser: 'zzuser' withPassword: 'zzpassword'.
ftpClient beCurrentDirectory: 'visualworks'.
ftpClient directoryContents: #('t*') do:
[ :entry | Transcript show: entry; cr ].
ftpClient close.
List all files in a directory (file names only)
The following code will display only the names of the files in the visualworks directory in the Transcript. The user zzuser with password zzpassword is used to logon to the yourftpserver FTP server. This code will return an `OrderedCollection` of file names only.
| ftpClient |
ftpClient:=FTPClient loginToHost: 'yourftpserver'
asUser: 'zzuser' withPassword: 'zzpassword'.
ftpClient beCurrentDirectory: 'visualworks'.
ftpClient filesInDirectory: #() do:
[ :entry | Transcript show: entry; cr ].
ftpClient close.
List files by wildcard (file names only)
The following code will display, in the Transcript, only the names of the files in the visualworks directory that begin with the letter "T". If a directory begins with the letter "T", however, then all files contained in that directory will also be listed. The user zzuser with password zzpassword is used to logon to the yourftpserver FTP server. This code will return an `OrderedCollection` of file names only.
| ftpClient |
ftpClient:=FTPClient loginToHost: 'yourftpserver'
asUser: 'zzuser' withPassword: 'zzpassword'.
ftpClient beCurrentDirectory: 'visualworks'.
ftpClient filesInDirectory: #('t*') do: [ :entry | Transcript show: entry; cr ].
ftpClient close.
Delete a file
The following code deletes the file hello.txt from the visualworks directory. The user zzuser with password zzpassword is used to logon to the yourftpserver FTP server. An error will result if the file cannot be found: "550 : The system cannot find the file specified".
| ftpClient |
ftpClient:=FTPClient loginToHost: 'yourftpserver'
asUser: 'zzuser' withPassword: 'zzpassword'.
ftpClient beCurrentDirectory: 'visualworks'.
ftpClient deleteFile: 'hello.txt'.
ftpClient close.
Determine the size of a file
The following code returns the size (in bytes) of the file hello.txt found in the visualworks directory. The user zzuser with password zzpassword is used to logon to the yourftpserver FTP server.
| ftpClient |
ftpClient:=FTPClient loginToHost: 'yourftpserver'
asUser: 'zzuser' withPassword: 'zzpassword'.
ftpClient beCurrentDirectory: 'visualworks'.
ftpClient fileSize: 'hello.txt'.
ftpClient close.
Direct transfer between two FTP servers
The following code copies the file rfc-index found in the inet/rfc directory of the UUNet FTP server ( ftp.uu.net ) into the root directory of the XXX_ZZZ FTP server ( xxx_zzz.site.com ). The user username with password password is used to logon to the xxx_zzz.site.com FTP server, and the user anonymous with password user@zzz.com is used to logon to the ftp.uu.net FTP server. The file will be called rfc-index on the xxx_zzz.site.com server.
| ftpClient uunetClient |
[ftpClient := FTPClient loginToHost: 'xxx_zzz.site.com'
asUser: 'username' withPassword: 'password.
uunetClient := FTPClient loginToHost: 'ftp.uu.net'
asUser: 'anonymous' withPassword: 'user@zzz.com' withAcct: nil.
ftpClient copyFromFTP: uunetClient file: 'inet/rfc/rfc-index'
toFile: 'rfc-index' passive: false.
] ensure: [uunetClient disconnect.ftpClient disconnect.].
Display the current directory
The following code will inspect the current working directory of the yourftpserver FTP server after having just logged on with user zzuser and password zzpassword .
| ftpClient |
ftpClient:=FTPClient loginToHost: 'yourftpserver'
asUser: 'zzuser' withPassword: 'zzpassword'.
(ftpClient help: 'PWD') inspect.
ftpClient close.
Change mode to stream
The following code will change the mode to stream. Although there are codes for block mode and compressed mode, only stream mode is supported at this time.
| ftpClient |
"S=Stream B=Block C=Compressed"
" Only Stream is supported at this time "
ftpClient:=FTPClient loginToHost: 'yourftpserver'
asUser: 'zzuser' withPassword: 'zzpassword'.
ftpClient mode: $S.
ftpClient close.
Display the remote server type
The following code will inspect the details of the remote server. For Windows-based servers, the response will be "215 Windows_NT version 4.0...". It is especially useful for determining the version of Windows (4.0, 5.0, etc.) on the FTP server. For UNIX-based servers, an error will result: "500 'SYST ': command not understood".
| ftpClient |
ftpClient:=FTPClient loginToHost: 'yourftpserver'
asUser: 'zzuser' withPassword: 'zzpassword'.
(ftpClient remoteSystemType) inspect.
ftpClient close.
Display the remote server status
The following code will inspect the details of the remote server. All FTP servers will recognize this command, although the returned response is not very descriptive.
| ftpClient |
ftpClient:=FTPClient loginToHost: 'yourftpserver'
asUser: 'zzuser' withPassword: 'zzpassword'.
(ftpClient stat) inspect.
ftpClient close.
Change structure to file
The following code will change the structure to file. Although there are codes for record structure and page structure, only file structure is supported at this time.
| ftpClient |
"F=File R=Record P=Page "
" Only File is supported at this time "
ftpClient:=FTPClient loginToHost: 'yourftpserver'
asUser: 'zzuser' withPassword: 'zzpassword'.
ftpClient structure: $F.
ftpClient close.
Rename a file on the remote server
The following code will rename the file hello4.txt found in the visualworks directory to hellofour.txt . The user zzuser with password zzpassword is used to logon to the yourftpserver FTP server.
| ftpClient |
ftpClient:=FTPClient loginToHost: 'yourftpserver'
asUser: 'zzuser' withPassword: 'zzpassword'.
ftpClient beCurrentDirectory: 'visualworks'.
ftpClient rename: 'hello4.txt' to: 'hellofour.txt'.
ftpClient close.
Using FTP commands and responses
At this level, `FTPClient` commands can be issued almost as if you were at an FTP client command prompt. Most of the commands that can be issued against the `FTPClient` are fairly intuitive, and the list itself is typical of FTP commands that could be obtained from most FTP client prompts.
In some cases, the programming is simple. The following sections contain some simple examples. However, programming at this level is much more complex when the transfer or renaming of files is involved. In most cases an intimate knowledge of streams and the FTP handshaking protocol is required. This information is beyond the scope of this document. We recommend using the `FtpURL` and `FTPClient` classes.
Delete a file
The following code deletes the file helloX.txt from the visualworks directory. The user zzuser with password zzpassword is used to logon to the yourftpserver FTP server. An error will result if the file cannot be found: "550 : The system cannot find the file specified".
| ftpClient |
ftpClient:=FTPClient loginToHost: 'yourftpserver'
asUser: 'zzuser' withPassword: 'zzpassword'.
ftpClient executeCommand: 'CWD visualworks'.
ftpClient executeCommand: 'DELE helloX.txt'.
ftpClient executeCommand: 'QUIT'.
ftpClient disconnect.
Create a new directory
The following code creates the directory misc under the visualworks directory. The user zzuser with password zzpassword is used to logon to the yourftpserver FTP server.
| ftpClient |
ftpClient:=FTPClient loginToHost: 'yourftpserver'
asUser: 'zzuser' withPassword: 'zzpassword'.
ftpClient executeCommand: 'CWD visualworks'.
ftpClient executeCommand: 'MKD misc'.
ftpClient executeCommand: 'QUIT'.
ftpClient disconnect.
SMTP
Simple Mail Transfer Protocol (SMTP) is perhaps the simplest of all Internet protocols. It is designed to send an e-mail message to another SMTP server by simply writing a stream of data to port 25.
Send a text message to a single recipient
This example sends a simple text message from santa@northpole.net to jdoe@abc.com . The subject of the message is "Start making your list now", and the text of the message, which will be sent as plain text, is "What would you like for Christmas?".
The SMTP server is called r udolph.northpole.net . The SMTP server name could also be replaced with just the machine name (for example, RUDOLPH, where your network will resolve its full name) or an IP address.
| message smtpClient |
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.
smtpClient := SimpleSMTPClient host: 'rudolph.northpole.net'.
smtpClient sendMessage: message.
An SMTP server requires a mail address for the submitter, which the client supplies from the message from: line. You can leave the sender incomplete, or omit it entirely, and specify a NetUser instance before sending the mail message:
smtpClient := SimpleSMTPClient host: 'smtp.northpole.net'.
smtpClient user: (NetUser new username: 'nicki' ).
smtpClient sendMessage: message
Copy recipients on a simple text message
The following code expands on the example in " Send a text message to a single recipient ", by specifying that mrsclaus@northpole.net is to be carbon-copied and parentsofjdoe@aoltimewarner.com is to be blind carbon-copied on the message.
| message smtpClient |
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?';
cc: 'mrsclaus@northpole.net';
bcc: 'parentsofjdoe@aoltimewarner.com';
yourself.
smtpClient := SimpleSMTPClient host: 'rudolph.northpole.net'.
smtpClient sendMessage: message.
Send the contents of a file as a message
The following code sends the contents of an entire file ( thelist.txt ) as the text of the message. Note that this example does not send the file as an attachment (see Send a message with one attachment for this option) but places the contents of the file in the text of the message.
| message smtpClient |
message := MailMessage newTextPlain.
message from: 'santa@northpole.net';
to: 'jdoe@abc.com';
subject: 'The List';
text: ('thelist.txt' asFilename) contentsOfEntireFile;
yourself.
smtpClient := SimpleSMTPClient host: 'rudolph.northpole.net'.
smtpClient sendMessage: message.
Send the contents of a file as an HTML message
The following code sends the contents of an entire file ( thelist.htm ) as the text of a message, similar to the previous example, " Send the contents of a file as a message ." But since the contents of this file are assumed to be that of a typical Web page (with embedded HTML tags in it), and the sender wishes the recipient to see the contents of the file as a Web page, the `newTextHTML` method is used instead of `newTextPlain` .
| message smtpClient |
message := MailMessage newTextHTML.
message from: 'Santa Claus';
to: 'jdoe@abc.com';
subject: 'The List';
text: ('thelist.htm' asFilename) contentsOfEntireFile;
yourself.
smtpClient := SimpleSMTPClient host: 'rudolph.northpole.net'.
smtpClient sendMessage: message.
Send a message to multiple recipients
The following code will send a message to more than one recipient. Note that the email addresses for multiple recipients must be delimited with angle brackets and separated by commas.
| message smtpClient |
message := MailMessage newTextPlain.
message from: 'Santa Claus';
to: '<jdoe@abc.com>,<mdoe@abc.com>';
subject: 'The List';
text: ('thelist.htm' asFilename) contentsOfEntireFile;
yourself.
smtpClient := SimpleSMTPClient host: 'rudolph.northpole.net'.
smtpClient sendMessage: message.
Send a message with one attachment
The following code is similar to that in the previous example, " Send a message to multiple recipients ," except the message file ( thelist.htm ) is sent as an attachment, rather than having the contents of the file sent as the text of the message.
| message smtpClient |
message := MailMessage newTextPlain.
message from: 'Santa Claus';
to: '<jdoe@abc.com>,<mdoe@abc.com>';
addFileAttachment: 'thelist.htm';
subject: 'The List';
text: 'Here is the List';
yourself.
smtpClient := SimpleSMTPClient host: 'rudolph.northpole.net'.
smtpClient sendMessage: message.
Send a message with multiple attachments
The following code will send a message containing multiple file attachments: thelist.htm and thelist.txt . This example differs from " Send a message with one attachment " in that you simply need to add multiple `addFileAttachment:` methods to the original message object.
| message smtpClient |
message := MailMessage newTextPlain.
message from: 'Santa Claus';
to: '<jdoe@abc.com>,<mdoe@abc.com>';
addFileAttachment: 'thelist.htm';
addFileAttachment: 'thelist.txt';
subject: 'The List';
text: 'Here is the List';
yourself.
smtpClient := SimpleSMTPClient host: 'rudolph.northpole.net'.
smtpClient sendMessage: message.
Send a message to a list of recipients
The following code will send a message to more than one recipient. In this case, the list of multiple recipients is contained in a List object called `listOfStrings` . The list can be populated from any source, such as a database or text file. In this example, it was simply created on the fly.
| message smtpClient listOfStrings |
message := MailMessage newTextPlain.
message from: 'Santa Claus';
to: 'You know who you are';
subject: 'Naughty or Nice?';
text: ('thelist.htm' asFilename) contentsOfEntireFile;
yourself.
listOfStrings := List new.
listOfStrings add: '<jdoe@abc.com>'.
listOfStrings add: '<mdoe@abc.com>'.
smtpClient := SimpleSMTPClient host: 'rudolph.northpole.net'.
smtpClient sendMessage: message toAll: listOfStrings.
Send a message using an alias
The following code will send a message to jdoe@abc.com but will use an alias name instead of an e-mail address. When the recipient reads the message, the to field will read John Doe instead of jdoe@abc.com .
| message smtpClient |
message := MailMessage newTextPlain.
message from: 'Santa Claus';
to: '"John Doe"<jdoe@abc.com>';
subject: 'Naughty or Nice?';
text: ('thelist.htm' asFilename) contentsOfEntireFile;
yourself.
smtpClient := SimpleSMTPClient host: 'rudolph.northpole.net'.
smtpClient sendMessage: message toAll: #('jdoe@abc.com')
POP3
Post Office Protocol 3 (POP3) is a simple mail-reading protocol that can run over TCP/IP. POP3 enables reading and deleting of messages in a mailbox. (Sending mail is done using SMTP .)
All of the following code examples, which receive a message via POP3, are variations on a common base set of instructions. These instructions are fully documented in the first example. Subsequent examples will introduce variations on this first example, but only the differences will be explained.
Determine the number of messages in the inbox
The following code determines the number of messages in the inbox mailbox of user jdoe@abc.com . The password for jdoe@abc.com is passwordforjoe .
The (SMTP) server is called mail.yourserver.com . The SMTP server name could also be replaced with just the machine name (for example, POSTMAN, where your network will resolve its full name) or an IP address.
The POP3 protocol does not account for multiple mailboxes. All messages are assumed to be located in a mailbox named inbox .
When all processing on the mailbox is concluded, it is required that you `disconnect` from the connection.
| client user |
client := Pop3Client host: 'mail.yourserver.com'.
user := ( NetUser username: 'jdoe@abc.com'
password: 'passwordforjoe' ).
client user: user; connect; login.
Dialog warn: 'Your inbox contains ',
client status messages printString , ' message(s)'.
client disconnect.
Read message #1 from the inbox
The following code reads message #1 from the inbox . The `retrieveMessage:` method returns a `ByteString` (which is inspected). The `ByteString` is then read in as a stream to the `MailMessage` object (which is also inspected). This practice will also be used in later examples.
| client user message msg |
client := Pop3Connection host: 'mail.yourserver.com'.
user := ( NetUser username: 'jdoe@abc.com'
password: 'passwordforjoe' ) .
client user: user; connect; login.
Dialog warn: 'You have ', conn status messages printString , ' messages'.
message := client retrieveMessage: 1.
message inspect.
msg := MailMessage readFrom: message readStream.
msg inspect.
client disconnect.
Determine if there are any attachments to a message
The following code determines if any attachments were included as part of a message in the inbox:
| client user message msg list |
client := Pop3Connection host: 'mail.yourserver.com'.
user := ( NetUser username: 'jdoe@abc.com'
password: 'passwordforjoe' ) .
client user: user; connect; login.
msg := client retrieveMessage: 29.
message := MailMessage readFrom: msg readStream.
list := message attachmentNames.
list inspect.
client disconnect.
Save an attachment
The following code assumes that at least one attachment was included as part of message #29. The first attachment (in case there is more than one) is then saved to an external file.
| client user message msg attachment |
client := Pop3Connection host: 'mail.yourserver.com'.
user := ( NetUser username: 'jdoe@abc.com'
password: 'passwordforjoe' ) .
client user: user; connect; login.
msg := client retrieveMessage: 29.
message := MailMessage readFrom: msg readStream.
attachment := message attachmentAt: 1.
attachment removeContentTransferEncoding.
attachment writeContentsOn: ('filename.whatever' asFilename writeStream).
client disconnect.
Extract the best text representation
Some SMTP servers, such as Microsoft's Exchange server, will take an existing (simple) message and send it out in multiple parts: plain text and HTML. The following code assumes that message #42 is in multi-part format and demonstrates how the message can be extracted in those two formats:
| client user message msg text1 text2 |
client := Pop3Connection host: 'mail.yourserver.com'.
user := ( NetUser username: 'jdoe@abc.com'
password: 'passwordforjoe' ) .
client user: user; connect; login.
msg := client retrieveMessage: 42.
message := MailMessage readFrom: msg readStream.
text1 := message textInPreferenceOrder: #('html' 'plain' '*').
text1 inspect.
text2 := message textInPreferenceOrder: #('plain' 'html' '*').
text2 inspect.
client disconnect.
Extract other message information
The following code displays parts of the message header in the System Transcript:
| client user message msg text1 text2 |
client := Pop3Connection host: 'mail.yourserver.com'.
user := ( NetUser username: 'jdoe@abc.com'
password: 'passwordforjoe' ) .
client user: user; connect; login.
msg := client retrieveMessage: 29.
message := MailMessage readFrom: msg readStream.
Transcript clear;
show: 'Charset: ', message charset; cr;
show: 'Date of message: ', message date printString; cr;
show: 'From: ', message from printString; cr;
show: 'Content type: ', message contentType; cr;
show: 'Type: ', message type; cr;
show: 'Subtype: ', message subtype; cr;
show: 'Contents: ', message contents printString; cr.
client disconnect.
Parse a multi-part message
The following code assumes that message #42 is in multi-part format and inspects the various "parts" of the message:
| client user message msg |
client := Pop3Connection host: 'mail.yourserver.com'.
user := ( NetUser username: 'jdoe@abc.com' password: 'passwordforjoe' ) .
client user: user; connect; login.
msg := client retrieveMessage: 42.
message := RFC822Message readFrom: msg readStream.
message parts inspect.
(message parts size) inspect.
((message partAt: 1) contentType) inspect.
((message parts first contents)) inspect.
client disconnect.
Fetch header information into an Ordered Collection
The following code retrieves header information from message #42 into an `OrderedCollection` :
| client user message msg headerInfo |
client := Pop3Connection host: 'mail.yourserver.com'.
user := ( NetUser username: 'jdoe@abc.com'
password: 'passwordforjoe' ) .
client user: user; connect; login.
headerInfo := client retrieveMessageLines: 42.
headerInfo inspect.
client disconnect.
Fetch header information into a ByteString
The following code retrieves header information from message #42 into a `ByteString` :
| client user message msg headerInfo |
client := Pop3Connection host: 'mail.yourserver.com'.
user := ( NetUser username: 'jdoe@abc.com'
password: 'passwordforjoe' ) .
client user: user; connect; login.
headerInfo := client top: 1 of: 42.
headerInfo inspect.
client disconnect.
Delete a message
The following code deletes message #5 from the inbox :
| client user |
client := Pop3Connection host: 'mail.yourserver.com'.
user := ( NetUser username: 'jdoe@abc.com'
password: 'passwordforjoe' ) .
client user: user; connect; login.
client delete: 5.
client disconnect.
IMAP
Internet Message Access Protocol (IMAP) is a protocol that enables mail clients to access electronic mail or bulletin board messages that are kept on a mail server. As a result of this, e-mail stored on an IMAP server can be manipulated from a desktop computer at home, a workstation at the office, and a notebook computer while traveling, without the need to transfer messages or files back and forth between these computers.
All of the following code examples, which receive a message via IMAP, are variations on a common base set of instructions. These instructions are fully documented in the first example. Subsequent examples will introduce variations on this first example, but only the differences will be explained.
Determine the number of messages in the inbox
The following code determines the number of messages in the inbox mailbox of user jdoe@abc.com . The password for jdoe@abc.com is passwordforjoe .
The IMAP host name is mail.yourserver.com . The name could also be just machine name (for example, POSTMAN) if the network resolves its full name, or its IP address.
Even though the `IMAPClient` object uses inbox as the default mailbox location, you are required to select one.
When all processing on the mailbox is concluded, you are required to `logout` from the connection.
| client |
client := IMAPClient loginToHost: 'mail.yourserver.com'
asUser: 'jdoe@abc.com' withPassword: 'passwordforjoe'.
client select: 'inbox'.
Dialog warn: 'Your inbox contains ', client messageCount printString ,
' message(s)'.
client logout.
Examine the inbox folder
The following inspects what the `examineMailbox` method of the `IMAPClient` object returns:
| client |
client := IMAPClient loginToHost: 'mail.yourserver.com'
asUser: 'jdoe@abc.com' withPassword: 'passwordforjoe'.
client select: 'inbox'.
client examineMailbox inspect.
client logout.
Determine the number of messages in the "xxxxxx" folder (1)
The following code determines the number of messages in the xxxxxx mailbox. The name of the mailbox may contain spaces.
| client |
client := IMAPClient loginToHost: 'mail.yourserver.com'
asUser: 'jdoe@abc.com' withPassword: 'passwordforjoe'.
client select: 'inbox'.
Dialog warn: 'You have ',
(client messageCount: 'xxxxxx') printString , ' messages'.
client logout.
Determine the number of messages in the "xxxxxx" folder (2)
The following code determines the number of messages in the xxxxxx mailbox:
| client |
client := IMAPClient loginToHost: 'mail.yourserver.com'
asUser: 'jdoe@abc.com' withPassword: 'passwordforjoe'.
client select: 'xxxxxx'.
Dialog warn: 'Your inbox contains ', client messageCount printString , ' messages'.
client logout.
Determine the number of messages in the "xxxxxx" folder (3)
The following code determines the number of messages in the xxxxxx mailbox:
| client |
client := IMAPClient loginToHost: 'mail.yourserver.com'
asUser: 'jdoe@abc.com' withPassword: 'passwordforjoe'.
(client status: 'xxxxxx' criteria: 'messages') inspect.
client logout.
Determine the number of unseen messages in the "xxxxxx" folder
The following code determines the number of unseen messages in the xxxxxx mailbox:
| client |
client := IMAPClient loginToHost: 'mail.yourserver.com'
asUser: 'jdoe@abc.com' withPassword: 'passwordforjoe'.
client status: 'xxxxxx' criteria: #('messages' 'unseen').
client logout.
Create a new mailbox
The following code creates a new mailbox called WOW :
| client |
client := IMAPClient loginToHost: 'mail.yourserver.com'
asUser: 'jdoe@abc.com' withPassword: 'passwordforjoe'.
client create: 'WOW'.
client logout.
Rename a mailbox
The following code renames the WOW mailbox as WOW2 :
| client |
client := IMAPClient loginToHost: 'mail.yourserver.com'
asUser: 'jdoe@abc.com' withPassword: 'passwordforjoe'.
client rename: 'WOW' newName: 'WOW2'.
client logout.
Copy message #1 from one mailbox to another
The following code copies message #1 from inbox to the WOW2 mailbox. The message will remain in the inbox (this is not a "move" command). To implement a "move", copy the message, then delete it from its original mailbox (see Delete a message for an example of how to do this.).
| client |
client := IMAPClient loginToHost: 'mail.yourserver.com'
asUser: 'jdoe@abc.com' withPassword: 'passwordforjoe'.
client select: 'inbox'.
client copy: #(1) to: 'WOW2'.
client logout.
Delete a mailbox
The following code deletes the mailbox called WOW2 . The folder need not be empty. Any messages in the folder will be deleted as well.
| client |
client := IMAPClient loginToHost: 'mail.yourserver.com'
asUser: 'jdoe@abc.com' withPassword: 'passwordforjoe'.
client delete: 'WOW2'.
client logout.
Read message #1 from the "inbox"
The following code reads message #1 from the inbox. The `fetchMessage:` method returns a `Dictionary` containing a key and a ByteString (which is inspected). The `ByteString` is then read in as a stream to the MailMessage object (which is also inspected). This practice will also be used in later examples. The text of the message is then inspected.
| client message messageString |
client := IMAPClient loginToHost: 'mail.yourserver.com'
asUser: 'jdoe@abc.com' withPassword: 'passwordforjoe'.
client select: 'inbox'.
(client fetchMessage: 1) inspect.
messageString := ((client fetchMessage: 1) value first).
message := MailMessage readFrom: messageString readStream.
message inspect.
" just the text "
message text inspect.
client logout.
Determine if there are any attachments to a message
The following code determines if any attachments were included as part of message #1:
| client message messageString list |
client := IMAPClient loginToHost: 'mail.yourserver.com'
asUser: 'jdoe@abc.com' withPassword: 'passwordforjoe'.
client select: 'inbox'.
messageString := ((client fetchMessage: 1) value first).
message := MailMessage readFrom: messageString readStream.
list := message attachmentNames.
list inspect.
client logout.
Save an attachment
The following code assumes that at least one attachment was included as part of the entire message. The first attachment (in case there is more than one) is then saved to an external file.
| client message messageString attachment |
client := IMAPClient loginToHost: 'mail.yourserver.com'
asUser: 'jdoe@abc.com' withPassword: 'passwordforjoe'.
client select: 'inbox'.
messageString := ((client fetchMessage: 1) value first).
message := MailMessage readFrom: messageString readStream.
attachment := message attachmentAt: 1.
attachment removeContentTransferEncoding.
attachment writeContentsOn: ('filename.whatever' asFilename writeStream).
client logout.
Extract best text representation
Some SMTP servers, such as Microsoft's Exchange server, will take an existing (simple) message and send it out in multiple parts: plain text and HTML. The following code assumes that message #42 is in multi-part format and demonstrates how the message can be extracted in those two formats.
| client message messageString text1 text2 |
client := IMAPClient loginToHost: 'mail.yourserver.com'
asUser: 'jdoe@abc.com' withPassword: 'passwordforjoe'.
client select: 'inbox'.
messageString := ((client fetchMessage: 42) value first).
message := MailMessage readFrom: messageString readStream.
text1 := message textInPreferenceOrder: #('html' 'plain' '*').
text1 inspect.
text2 := message textInPreferenceOrder: #('plain' 'html' '*').
text2 inspect.
client logout.
Extract other message information
The following code displays parts of the message header in the System Transcript:
| client message messageString |
client := IMAPClient loginToHost: 'mail.yourserver.com'
asUser: 'jdoe@abc.com' withPassword: 'passwordforjoe'.
client select: 'inbox'.
messageString := ((client fetchMessage: 42) value first).
message := MailMessage readFrom: messageString readStream.
Transcript clear;
show: 'Charset: ', message charset; cr;
show: 'Date of message: ', message date printString; cr;
show: 'From: ', message from printString; cr;
show: 'Content type: ', message contentType; cr;
show: 'Type: ', message type; cr;
show: 'Subtype: ', message subtype; cr;
show: 'Contents: ', message contents printString; cr.
client logout.
Parse a multi-part message
The following code assumes that message #42 is in multi-part format and inspects the various "parts" of the message:
| client message messageString |
client := IMAPClient loginToHost: 'mail.yourserver.com'
asUser: 'jdoe@abc.com' withPassword: 'passwordforjoe'.
client select: 'inbox'.
messageString := ((client fetchMessage: 42) value first).
message := RFC822Message readFrom: messageString readStream.
message parts inspect.
(message parts size) inspect.
((message partAt: 1) contentType) inspect.
((message parts first contents)) inspect.
client logout.
Fetch messages and their headers
The following code shows the various ways in which multiple messages can be fetched:
| client oc1 oc2 oc3 oc4 oc5 |
client := IMAPClient loginToHost: 'mail.yourserver.com'
asUser: 'jdoe@abc.com' withPassword: 'passwordforjoe'.
client select: 'inbox'.
oc1 := client fetch: #( 3 8 ) with: #('RFC822').
oc2 := client fetchMessages: #( 1 3 ).
oc3 := client fetchMessageHeaders: #( 1 3 ).
oc4 := client fetch: #(1 4 5 6 ) with: #(RFC822.SIZE).
oc5 := client fetchMessage: 1.
oc1 inspect.
oc2 inspect.
oc3 inspect.
oc4 inspect.
oc5 inspect.
client logout.
A summary of the ways to fetch multiple messages is given here:
fetchMessage: messageNumber
Returns an association in which "key" is the message number and "value" is the message
fetchMessageHeaders: anArrayOfNumbers
Returns an OrderedCollection of associations in which "key" is the message number and "value" is the message header.
fetchMessages: anArrayOfNumbers
Returns an OrderedCollection of associations in which "key" is the message number and "value" is the message.
fetchRFC822MessageHeaders: anArrayOfNumbers
Returns an OrderedCollection of associations in which "key" is the message number and "value" is the message header.
fetchRFC822Messages: anArrayOfNumbers
Returns an OrderedCollection of associations in which "key" is the message number and "value" is the message.
fetch: anArrayOfNumbers with: qualifierOrListOfQualifiers
Returns an OrderedCollection of associations in which "key" is the message number and "value" is the message.
Delete a message
The following code shows how to mark a message for deletion as well as how to actually delete a message:
client := IMAPClient loginToHost: 'mail.yourserver.com'
asUser: 'jdoe@abc.com' withPassword: 'passwordforjoe'.
client select: 'inbox'.
client markForDelete: #( 2 ).
(client searchMessages: 'deleted') inspect.
client expunge.
(client searchMessages: 'deleted') inspect.
client logout.
Search for messages
The following code shows the various ways in which messages can be searched based upon certain criteria:
| client oc1 oc2 oc3 mNumbers |
client := IMAPClient loginToHost: 'mail.yourserver.com'
asUser: 'jdoe@abc.com' withPassword: 'passwordforjoe'.
client select: 'inbox'.
mNumbers := client searchMessages: 'UNSEEN FROM "Fred"'.
oc1 := client fetchMessages: mNumbers.
oc1 inspect.
mNumbers := client searchMessages: 'BEFORE 1-Feb-1999'.
oc2 := client fetchMessages: mNumbers.
oc2 inspect.
mNumbers := client searchMessages: 'FLAGGED SINCE 1-Feb-1999 NOT FROM "Smith"'
oc3 := client fetchMessages: mNumbers.
oc3 inspect.
client logout.
The complete list of criteria is given in RFC 2060 (IMAP4rev1 December 1996), which is available at ftp://ftp.uu.net/.vol/2/inet/rfc .
HTTP
HTTP (Hypertext Transfer Protocol) in it's most simple and basic sense, is a protocol for raw data transfer across the Internet. The HTTP protocol is a request/response protocol in that a client sends a request to the server in the form of a request method, URI, and the server responds with a status line followed by a MIME-like message.
HTTP within VisualWorks mimics `Filename` . You can access web pages (URLs) just like local files. This provides a level of transparency in accessing HTTP-based documents. For example, you could access a financial web site and retrieve the latest stock quote for a particular ticker by parsing the stream of HTML that is returned.
Retrieve the contents of a web page
The following code will retrieve the contents of Yahoo's home page and return it into a `ByteString` :
| url str |
url := 'http://www.yahoo.com' asURI.
url readStreamDo: [:rs :meta | str := rs contents].
str inspect.
If, in your environment, all Internet (HTTP) traffic is routed through a proxy server, this code will not work and you should see topic Retrieve the contents of a web page behind a Proxy .
Retrieve the contents of a web page behind a Proxy
The following code will return the contents of Yahoo's home page and return it into a `ByteString` . This code is necessary if, in your environment, all Internet (HTTP) traffic is routed through a proxy server. A dialog box will appear asking you to authenticate yourself with a username and password.
| url str |
[ HttpClient proxyHost: 'PROXYSERVER' port: 80.
url := 'http://finance.yahoo.com' asURI.
url readStreamDo: [:rs :meta | str := rs contents].
] ensure: [HttpClient disableProxy].
str inspect.