PyMailGUI — Email Without the Evil
|Summary:||A basic but useful and portable email client GUI, without the ads and snoops|
|Version:||4.0.1, August 2017 (see all version history)|
|Author:||© M. Lutz, 2000-2017, learning-python.com|
|License:||Provided freely, but with no warranties of any kind (see also README.txt)|
|Screenshots:||This program runs on Mac OS, Windows, and Linux|
|Usage:||Download and start source code, Mac app, and Windows and Linux executables|
|History:||This is a much-enhanced version of code that originally appeared in the book PP4E|
This is an online-only version of this document. It has the same content as the original desktop version shipped in the program's download package, but has been styled for viewing on both desktop and mobile devices. To use this instead of the original version, use your browser to save this page's HTML in the install folder.
Welcome to PyMailGUI—a basic but useful and private email client, that runs as a desktop GUI on your computer. It allows you to process your email both live at your account's POP/SMTP servers, and offline in locally saved files; is available in both self-contained executable and transparent source-code formats; and works the same on Mac OS, Windows, and Linux per the screenshots above.
Among its most valuable assets, PyMailGUI dedicated to the notion that your email is your business. With PyMailGUI, you can read, send, and save your email on your own computer—without logging in to an online session that someone else controls; without being bombarded by rude advertising; and without your activity being tracked by those who see your personal communications as assets to exploit. If this stuff matters to you, PyMailGUI may be the email client you've been looking for.
PyMailGUI is coded with Python 3.X and its tkinter library. It's available as a Mac app and Windows and Linux executables for ease of use, as well as complete and portable open-source code that ensures transparency. While apps and executables integrate best with your computer's GUI, source code means that PyMailGUI can never do something you cannot see.
As a desktop GUI, PyMailGUI runs locally on your computer, and fetches and sends emails only on request using your configured email account's POP and SMTP servers, respectively. It comes with features including those in the following table.
|Multitasking||Run multiple overlapping email transfers|
|MIME parts||Send and receive diverse bodies, headers, and attachments|
|Unicode text||Access and display Unicode text content throughout|
|Saved mail||Process email offline with local saved-mail files|
|HTML views||Open HTML-alternative/only emails in a browser|
|Multiple windows||Leverage a rich, multiple-window graphical interface|
|Customizations||Configure and customize with simple Python code|
|Multiple accounts||Access all your email accounts from a unified interface|
|Desktop GUI||Connect to POP/SMTP servers only on mail transfers|
|Wide portability||Use the same program on Mac OS, Windows, and Linux|
|Multiple formats||Install app, executable, and source-code packages|
|Open source||Inspect and change the code however you wish|
|Session privacy||Read email with a program that's ad-free and snoop-free|
Because this program was originally coded as an example for a book, it intentionally omits some more advanced features such as spell checkers, HTML email composition tools, inline HTML viewing (it spawns a browser for HTML content), address books, and email sorting and searching. Extensions remain suggested exercises.
Despite its design limits, though, the program has been sufficient to serve as its author's primary business and personal email tool for over a decade. It comes with everything you need to take control of your email experience.
Perhaps most importantly, PyMailGUI provides a degree of privacy that many popular email clients do not. PyMailGUI never connects to the Internet, except to momentarily fetch and send emails, and only on your request. This means that your email session is offline—and immune to prying eyes and marketers. Especially when using PyMailGUI to access a private email account, your email is your property, not something to be scanned and monetized by self-interested people. While PyMailGUI cannot guarantee complete email privacy (and servers are still open to abuse), it does deliver email sessions that are both ad-free and snoop-free.
True to its book-example heritage, PyMailGUI is also partly intended to serve in an educational role. Users interested in programming are invited to consider its amply documented code, which spans 15K source lines (at latest count) developed incrementally in the book and greatly enhanced later, as an example that demonstrates larger-scale Python programming techniques.
This standalone release uses code based on that in the book, but reused packages are nested within PyMailGUI's folder, and a new launcher and per-account configuration files reside at the top of the package. In the book, both PyMailGUI and packages it uses are part of a larger PP4E package tree, and the launcher is nested within PyMailGUI. This release has also evolved substantially since its book-example genesis, and is aimed at both book readers and others looking for a private email-client solution.
PyMailGUI's standalone-release code is available both in its zipfile and online, per the next section.
This section provides points on installing, launching, configuring, and using the program itself. The shortest version of this story is that to use PyMailGUI, you'll:
PyMailGUI runs the same on Mac OS, Windows, and Linux. It's distributed in both self-contained "frozen" app and executable formats, as well as complete source code, available from this site. If you consider yourself more user of software than developer of software you probably want one of the standalone formats for each platform you work on. In more detail, the tradeoffs are these:
The frozen packages run on just one platform each, but mesh best with your computer's GUI, require no additional installs, and are immune to changes in other installed software.
The source-code package requires a separate Python install on each platform you wish to run it on, but the code itself runs on all PyMailGUI platforms, and supports both inspection and changes.
In other words, unless you plan on spelunking though the program's code, one of the frozen packages are probably your best bet.
Once you've fetched your PyMailGUI, install and launch details vary per package. Frozen apps and executables do both with a click or two, but source-code requires extra steps. For the frozen packages:
After installing PyMailGUI per the above, your next step is to configure your email accounts—the topic of the next section.
Apart from installing PyMailGUI, some configuration is required if you wish to connect to your own email accounts. You can safely use the defaults or any example mail configuration file shipped with the program to experiment with the GUI or work offline, but must setup a configuration file with your own account parameters to send or receive email.
Mail configuration files are coded in Python for flexibility, and are located in the installed program's MailConfigs folder. This folder contains one file per account you wish to access; its files' names become entries in the account launcher GUI that opens when you first start the system.
The mapping from file to account is described in full in MailConfigs' README.txt, but in short:
Each account is represented by a MailConfigs file of Python assignments which set variables expected by the program.
Account files are named "mailconfig_XXX.py", where "XXX" is the name that shows up in the launcher's selections list.
When you open an account by name in the launcher, its MailConfigs file is run to setup the account's parameters.
More generally, mail configuration files are coded as a hierarchy. Settings in the base file provide defaults that apply globally to all accounts' sessions, but settings in an account-specific file you provide in MailConfigs pertain to just that account's sessions. Your mailconfig_XXX.py files' assignments implicitly override and replace the default settings in the base file, for a specific account's parameters and preferences. For instance, to run PyMailGUI on your Gmail account live, emulate or tailor mailconfig_Gmail.py, select "Gmail" in the launcher GUI and press its Open Account.
For more details on configuration, see the MailConfigs folder's README, and edit or emulate the shipped examples for your own accounts. Any example account file may be freely edited or replaced, and can be used to experiment with the system offline—per the next section.
While MailConfigs file changes allow you to connect to your account servers, they are not required for offline use. Offline mode allows you to process mails saved in files on your computer, using any account's configurations file—including the examples shipped. The program does not connect to any server unless and until mail is fetched or sent, and all the example account files shipped require a password on initial connections. Passwords are not provided for the example accounts (and you're unlikely to guess correctly!).
See PyMailGui-PP4E's SavedMail folder for example saved-mail files to view offline via the GUI's "Open" buttons. You can also view your sent mail offline, as described ahead. Now that you've learned about installing and configuring PyMailGUI, though, it's time to jump in to its GUI to do some email.
This section provides a quick rundown on the GUI's operation. Clicks its thumbnails and links along the way to view example windows. For space the linked shots here are all from the Mac app, but you'll find equivalents for your platform of choice in the complete screenshots collection.
PyMailGUI allows you to both send and receive emails from any of your accounts' servers, and work with saved-mail files completely offline. When started, it first displays a launcher window, from which you can open email accounts specified by your MailConfigs folder files that we met earlier.
For each account that you spawn with the launcher's Open Account button, PyMailGUI provides an independently running multiwindow GUI that consists of:
View and compose windows use the PyEdit program for display and edit of a message's main text (more on this ahead). Additional popup windows—including mail raw-text, file selection, password prompts, and transfer status—appear when needed. If a console window is supported by your install package (e.g., source code), it is also used for program trace messages that can be safely ignored by most users.
In general, most mail-processing operations can overlap in time—for instance, you can compose and send multiple emails while a load of received messages is in progress—though some transfers preclude starting others. The GUI will let you know if a new operation must wait for others to finish. The next two sections cover the two main types of windows you'll use in PyMailGUI.
Mail list windows, opened from the launcher's window, are your top-level interface to both:
These windows look and act almost entirely the same, but saved-mail lists have no load button because there are no incoming messages to fetch, and only the server window has a top-of-window help bar (this plus window title helps make them distinct). Both types of list windows display just enough header information to help you identify messages, with a leading "*" to indicate attachments. Both also provide two sets of action buttons and event triggers:
Both widow types also have a Quit button works the same as a window close, and varies by type: a Quit in a saved-mail file list window closes just that window, but a Quit in the server list window checks for unsaved changes in any mail composition window and exits the program on verification. The next two sections provide a more formal reference to list-window actions.
|Load||Load all or newly arrived email headers from POP server (server window only)|
|Open||Opens a selected saved-mail file in a new list window|
|Write||Compose, and possibly send, a new email in a new compose window|
|Quit||Close list window (saved-mail windows) or exit program (server window)|
|All||Toggle all-message selection on and off in this list window|
For speed, Load fetches just newly arrived email headers from your POP server after the initial load. The full content of server mails is fetched only when first accessed (e.g., by the first View, Reply, or Save in a session), and mails are never automatically retained on your computer or removed from the mail server (use Save and/or Delete as desired).
As usual for general GUIs, you can open multiple saved-mail files in independent list windows via Open buttons in any list window. Changes in saved-mail file content (e.g., via Save and Delete) are reflected in their open list windows immediately.
In the mail list window of the server or any saved-mail file, you can process one or more emails with the following buttons and operations:
|View||Display content of selected email(s), open individual parts by clicks|
|Reply and Fwd||Compose a reply or forward to selected email(s) in new window(s)|
|Save||Save the full content of selected email(s) to a saved-mail file|
|Delete||Verify and remove selected email(s) from the server or saved-mail file|
|Double-clicks||Display full raw-text of selected email(s) in a PyEdit popup|
|Single-clicks||Select email: add Ctrl or Shift to select many, or click All for all|
Per the last row above, list windows support multiple selections. When used, the above operations are automatically applied to all selected messages on either the server or in a saved-mail file, depending on the window's type. For example, you fetch and open multiple mails from your account server at once by selecting them in the server-list window and pressing View. Use a single click to select one mail; Ctrl/control-click to select others; Shift-click to select a range of mails; and the All button to select all (or none).
Multiple selections also make it easy to manage mails offline as a group. For instance, you can also both save multiple server mails to saved-mail files, and copy or move multiple saved mails to another new or existing saved-mail file:
Tip: for maximum privacy, save your new emails to local files, delete them from your account server, and use the actions listed above to manage them. Your local saved-mail files can be used to categorize your mail much like mail folders on some IMAP servers, but are completely offline.
Note that Open and Save in list windows process messages in saved-mail files, while Open and Save in the text area of view and compose windows process the main text part of individual messages; see ahead for more on the distinction.
List windows can be resized, minimized, and scrolled freely and independently, and show you which messages you can access on an account's server or in a local saved-mail file; to view and send actual email content, you'll use the next section's windows.
View and compose windows, opened by buttons in list windows, are your interface to individual messages displayed in server or save-file list windows. View and compose windows are similar, but their topmost actions and components vary for their different roles:
The upper portion of both window types includes the message's most-common header fields for display or entry (Ctrl or command + C/V work in these to cut/paste text). The lower portion of both window types automatically uses the PyEdit program's text-editor component for both display and composition of an email's main message text. Click the PyEdit Help button in this area of the window for more details on using its editing tools omitted here for space (they're mostly intuitive) PyMailGUI also uses PyEdit popup windows to display raw mail text and some HTML parts' text.
On window close (and the Cancel button, which runs the same code), both view and compose windows notify you of unsaved changes in just that window and close the window if verified. By contrast, a window close or Quit in the server list window notifies you of unsaved changes in any PyEdit component or popup window before shutting down if verified. The following sections summarize view and compose windows' actions.
|Cancel||Close this view window (only), verify if its text has changed|
|Parts||Display a list of the parts attached to this message|
|Split||Save all the message's parts in a selected folder, open each (optionally)|
|Part-view row||Open individual parts by single-clicks, '...' runs Split if too many to show|
View window Parts and Split buttons list and save all mail parts, respectively. To open individual email parts, click their names in the part-buttons row below the header. These open in programs appropriate for the part's filetype, subject to your MailConfigs settings. For example, text parts open in a PyEdit popup; the HTML version of a text/HTML alternative email opens in a webbrowser; and an attached image opens in your local viewer.
Compose windows—opened by list-window Write, Reply, and Fwd buttons—are similar to view windows, but there is no part-button row; the PyEdit component and header fields are used for entry instead of display (and possibly prefilled, depending on the composition type); and the Split button is replaced with Attach and Send controls, for attaching new parts and sending the mail via your SMTP server, respectively.
|Cancel||Close this view window (only), verify if its text has changed|
|Parts||Display a list of the parts already attached to this new message|
|Attach||Select and attach a new part (any file) to the new message|
|Send||Send the new message, per its headers, main text, and attachments, via SMTP|
In addition to PyMailGUI actions described in the two preceding sections, view and compose windows also inherit all the operations supported by the PyEdit component used to display a mail's main text in the lower part of the window, including the following.
|Save||Store the main text displayed for this message to a chosen file.|
|Open||Load the text from a chosen text file into this message's main text.|
|Etc.||Click PyEdit's Help button for more on its available text-editing tools.|
This concludes our tour of the GUI in PyMailGUI, but there's more to say about common operations you can perform with the program. For a brief tour of these topics, let's move on to the next section.
This section provides a collection additional task-oriented usage pointers. It's a bit random, and in some parts redundant with other sections of this document, but it may connect some PyMailGUI dots that you hadn't considered before.
A Save in a list window saves entire messages (all those selected), and always appends them to the saved-mail file you select in a GUI dialog. By contrast, a Save in the text area of a view/compose window saves just that mail's main text part displayed. The latter can be used as a manual drafts-saving tool (save to a file, open or copy-paste later). For text-part saves, you can generally use the default Unicode encoding when prompted (per PyEdit's customizable support; see its Help).
Like the prior note, an Open in a PyMailGUI list window opens a saved-mail file's messages in a new list window, but an Open in the PyEdit component of a View or Compose window opens a simple text file and inserts its content in the message's main-text display. The latter can be used to save and resume notes in progress, or save just the main text of a mail received.
All emails you send are automatically stored by default in a saved-mail file called sentmail.txt in the PyMailGui-PP4E folder located at the top level of your install package. You can change this file's name and location and disable sent-mail saves altogether in MailConfigs files, either per account or globally; see the mail configuration base file's "sentmailfile" setting for pointers. For example, you may wish to locate the sent-mail save file outside the install tree to avoid accidentally losing it on upgrades (more on upgrades ahead). Open the sent-mail save file with Open in any list window to see the mails you've sent in the GUI.
Both Reply and Fwd in list windows automatically include and quote the original main-text part. There currently is no automatic way to include attachments from the original message, but it's easy to save and reattach any part of a message. First, open the original message with a list's View and click on the buttons of its parts that you wish to include in the reply or forward. This saves the clicked parts in the TempParts subfolder of the install's PyMailGui-PP4E folder. Then, use the Attach button in the compose window to reattach the saved original parts from the TempParts subfolder. A mail's parts can also be saved in and reattached from a folder of your choosing with the Split button.
Mails composed in PyMailGUI always display and send a plain-text main part. There is no current support for creating text/HTML alternative or HTML-only emails, but you can include an HTML part attachment with a main text part that simply directs readers to open the HTML. As there are no HTML editing tools in PyMailGUI, an HTML part would be composed with other programs (e.g., MS-Word, LibreOffice, or PyEdit for the text-biased). In contrast, mails received or saved in PyMailGUI fully support both text/HTML alternative and HTML-only modes, and display their HTML parts in your web browser—on clicks for alternative mails, and along with extracted plain-text for HTML-only mails.
Saved-mail files are a local alternative to email folders on your account's server, and allow you to partition your saved mails by categories, years, and so on. They contain text—the raw text of mails, which includes their attachments often in encoded form—but can have any name, can be stored in any folder, and may have any extension (or none). Saved-mail files emulate some of the functionality of IMAP server-account folders, but reside on your local computer only. This makes them private, and easy to access and archive offline; it also limits their visibility, though they can be propagated to other devices normally (e.g., see mergeall). Use Open in the GUI to view the shipped examples.
As a POP client, this program can access only the "inbox" folder at your server account; per the prior note, its local saved-mail files can fill the same role as saved-mail folders at your server. You can, however, use this program to view preexisting server email folders by first downloading their content to saved-mail files with the separate imapfetch program. Because server connection is tried only on fetches and sends, you don't need to have or configure an account to view emails saved by either imapfetch or PyMailGUI's Save; simply press Open in a PyMailGUI list window. IMAP download from server folders is a one-time manual step, but local files are generally immune to email provider snooping, and ineffectual oversight.
This hint applied to the former text-based launcher, and has been made mostly obsolete by the new GUI-based launcher in version 4.0. One part of the prior note still applies, however: you can avoid password input on each initial server connection by setting the local POP/SMTP (fetch/send) password-file paths in your MailConfigs files. When set to valid paths, passwords are read automatically from the files you've configured, instead of GUI popups. Password files aren't recommended on machines used by others, but are not generally any worse than allowing a web browser to remember your password in a local cookie. For more on setting this up, see Configuration.
When entering recipient email addresses in To, Cc, and other fields, be sure to quote the optional name portion if it contains special characters (for instance: "name" <email@example.com>). When entering multiple recipients, separate email addresses with a comma (for example: address1, address2, address3). The standard control/Ctrl plus C and V key combinations work to copy and paste text in header fields; on some platforms a right- (or two-finger-) click may paste as well. For sends: if the initial "?" character in an address field is left unchanged, no address will be used for the field; by contrast, the initial "?" in Subject will be used as your email's subject line if unchanged—be sure to fill in a valid subject.
Because emojis—Unicode symbols whose codepoints lie outside the BMP range—are not supported by the Tk library used by PyMailGUI, they must be replaced with a � Unicode replacement character for display in the GUI (only). You also cannot enter them in the GUI's edit fields. For more details, see the "About emojis" note in PyEdit's User Guide, available in some packages here, and online here. Despite this limitation, you can still jazz-up your emails by using any of the very many Unicode symbols and language characters that fall in the BMP's U+0000..U+FFFF codepoint range, including these: ✓ ☓ ☑ ☒ ☀ ☼ ☉ ★ ☆ ☞ ☜ ☯ ⚐ ♡ ☮ 重 出 ж म ä ☺.
For both developers and upgraders, this section logs changes made in recent PyMailGUI releases, from latest to oldest. Optional reading to be sure, but most users would do well to scan the upgrading tips before installing a new PyMailGUI.
First off, to upgrade from prior releases, be sure to save and restore (or copy) the following items from your prior PyMailGUI install's folder if they have been changed since the prior install:
If you install a new version to a different folder, you can simply copy the items listed above from old install to new. If you install to the same folder, though, these items will be overwritten with new versions if you don't save and restore. Upgraders beware!
Per the archives, the last book's version of this program was 3.0, published in 2010 (prior book versions appeared in 2006 and 2001), and the first standalone (post-book) version was released late in 2015. Starting in October, 2016, version number 3.1 is used to denote the initial standalone release, and other 3.X releases have been numbered accordingly below. As of 2017's release 4.0, changes are marked in code by version number (e.g., "[4.0]") instead of abbreviated release dates, but this was not retroactively applied to 3.1+ changes.
This minor patch release applies to Mac app users only. Its sole change was a fix for a rare broken-pipe error that could occur only in the Mac app distribution of this program. For details on the issue and fix, please see this web page. Only the Mac app and source code packages were rereleased as 4.0.1, not the Windows or Linux executables. The source-code package was updated to include the app fix as an example for developers (see "[4.0.1]"), but the fix is irrelevant to both source code and other platforms. Users of the 4.0 Mac app are encouraged to upgrade to this new release.
This was a colossal release with too many features to document here. Among the most notable, with major items near the top:
And so on; again, search for "[4.0]" in the source-code files for full details.
Python 3.5 changed the way that timeouts are applied to mail send operations. Its smtplib module's server.sendmail() uses Python's socket.sendall() system call to send full mail content. Formerly, the latter applied a timeout value to each partial data transfer it performed during a call. As of 3.5, a timeout value is now applied instead to the full duration of the call, as documented here and here. This subtly changes the meaning of timeout values—they now always impose a maximum on the total time to send an email's full content.
Both for this Python change and general usage, PyMailGUI's default SMTP timeout was increased from 20 to 120 seconds to better accommodate large emails sent on slow SMTP servers, and was moved to the main (base) mailconfig.py file for easy adjustment. In contrast to smtplib's single-call scheme, Python's poplib module used for fetches reads mails line by line manually, and is thus less sensitive to timeout values, and immune to 3.5 changes. PyMailGUI's default POP timeout was nevertheless also increased for both symmetry and future-proofing.
Search for "May2016" in code here and here for changes applied. Timeouts are crucial in PyMailGUI; without them, the program can't be closed in the GUI if a server transaction hangs, as originally described here.
[This fix has been made fully moot by the new GUI launcher in 4.0; it's historical only.] Repackaged with a trivial fix for a minor launcher buglet: on invalid account name input, the error message giving valid account-name choices included bogus strings if > 1 "_" appeared in any account configuration filename. Fixed by splitting on just 1 (first) "_", via a second argument to str.split(). Also revised this document, added a console screenshot, and added top-level example go-one.py for minimizing keyboard input on launches (defunct).
This release adds revamped password input code which reinstates local POP (fetch) password files, described in the new usage note earlier. This feature wasn't formerly available in the GUI due to the special handling of password prompts for thread safety. Local SMTP (send) password files also work, as before. Also new: when passwords are not in files, the GUI now never asks for them after the first successful load or send, as they are valid thereafter (they were formerly reinput after server errors). Search for "Dec2015" to see code changes and new implementation notes.
This release adds new color configurations for the help bar and view action buttons, as captured here and here; expanded help content and actions, with three selectable displays; support for Linux app-bar icons; and more robust launcher code (now superseded by 4.0's GUI launcher code). See "Dec2015" in the code for changes made; all are only minor deviations from the original book code.
This release now has support for using SSL and TLS/SSL encrypted modes when communicating with your POP (fetch) and SMPT (send) email servers. SSL and its TLS extension make email transmissions more secure. To enable this, the program's configuration settings now include optional switches to turn on SSL modes, and accept port numbers at the end of server-name strings following a ":" (ports may vary per server, especially for SSL). This support is not present in the book's version of the code, but is a common expectation today. While some servers still don't implement SSL (e.g., one of the author's accounts on Earthlink), some require it (e.g., Gmail), and others generally recommend it (e.g., Godaddy).
You email provider will have the details on your server account's TLS/SSL story. For SSL configuration examples in the MailConfigs folder, see the self-contained Gmail file, as well as the other SSL file examples. For all changes made in this revision, see the announcement, and search for "Dec2015" in the code here and here. Hint: the PyEdit component's Search=>Grep in View windows can be used to search the full source-code tree.
For additional changes adopted by PyMailGUI between the hosting book's publication and the original release of this standalone version, see also this page for changes necessitated by recent Python releases, and this list for older PyMailGUI modifications.
This version had much of the current functionality of the latest, and was included with and documented by the book PP4E published in December 2010 (really, January 2011, but the publisher munged the date, and the program was finalized months before publication).
Prior to its 2015 general release, this program appeared in more-limited forms in books published in 2010, 2006, and 2001 (and copyrighted by the author of the latest release). In brief, the first version was text-messages only; the second added MIME attachments; and the third implemented Unicode and included much of the functionality available in the current standalone release.
Despite its broad usability, there is still ample room for further enhancements in PyMailGUI (e.g., minimizing the help bar, using sortable tables for list windows, popup placement, resolving some thread starvation slowdowns, address books, spelling checkers, and HTML editing). These ideas are open ended, but limited by the dual underlying goals of retaining an easy-to-use interface, and keeping this program similar to its original version in the book it came from. As usual, the code is yours to change as you wish.
Because much of this program is fully described in the book that serves as its host, we'll defer to other resources for additional details here. Please refer to the following for more information:
Depending on your configuration file settings, the top-of-window bar in the program's main server list window may also open the second of the above—in addition to the document you have just finished reading.
If you like this program, you may also be interested in these other productivity tools brought to you by the makers of PyMailGUI:
|frigcal||—||Personal Calendar GUI; No Login Required|
|mergeall||—||Backup and Mirror Your Stuff Your Way|
|PyEdit||—||Edit Text. Run Code. Have Fun.|
|PyGadgets||—||GUI Toys, Just for the Hack of It|
You can find these and other free software packages at the programs site.