Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

C/C++

C Programming


JUN95: C PROGRAMMING

The Check is in the IMail

This month's column describes the IMail application, a communications program that I designed specifically for sending and receiving electronic mail through a modem connected to an Internet site. I came up with the IMail application when DDJ established an Internet host with accounts for the editors. (I'm [email protected], for instance.) Before that, I used the CompuServe Information Service (CIS) for most of my online activities (71101,1262). My satisfaction with that arrangement was being strained. When I needed to discuss something with the powers at CIS, I tried several times to get through to their middle management, and failed--even though I frequently mention the service in this column and in my books. They don't return phone calls, even to important journalists like yours truly. No respect.

Then came the GIF patent debacle. By now you know that CIS and Unisys struck an agreement based on a 1985 patent that Unisys holds on the LZW compression algorithm, an integral part of CIS's GIF graphical file format. As a consequence of that agreement, CIS put the money squeeze on (mostly) small developers who use GIF in their applications. The industry is still reacting to that unseemly turn of events, and most developers are abandoning GIF in favor of other formats, such as the recently defined "Portable Network Graphic Format" (PNG). The way CIS has handled this affair sets my teeth on edge.

Sometimes you just have to grit those teeth and hang in there. Despite my disapproval of their treatment of programmers, I needed the services of CIS, mainly as a way to communicate with readers electronically. CIS's e-mail service is virtually universal; there are CIS gateways to most other mail services. The ddj.com connection offered me an acceptable alternative. Internet mail is even more universal.

The mail system at ddj.com uses the UNIX mail program, a primitive command-line interface that lets you write, read, reply to, and save mail messages in text files. The program works, but is not what you would call feature-laden. To use the mail services of ddj.com routinely, I needed a better mail-management program, and so I wrote the first version of IMail. You might recall from last month that I wrote that first version in C so that I could use D-Flat. That experience had me groaning at every turn. A little C++ angel sat on my shoulder and whispered into my ear every time I coded a C construct that would have been easier in C++. Once the program was working, I heeded that whisper and rewrote the program in C++, first building C++ wrapper classes around D-Flat. Last month I described the wrapper. This month I'll talk about the C++ application.

IMail: The Requirements

First let's consider the requirements for the program. It should store and remember configuration items such as login identification, password, telephone number, baud rate, communications port, data format, and modem-command strings. The program should be a multiple-window application with windows to display incoming and outgoing messages. It should remember how the user (me) positions and sizes those windows, and it should preserve those settings between executions. The program should maintain an address book of people to whom I send mail and let me select from that book as a function of addressing messages. The program should hold incoming and outgoing messages in their respective mailboxes until they have been read or sent. An option should permit me to automatically save copies of outgoing messages. The program should allow me to reply to and forward messages that I am reading. There should be user-defined file folders into which I can file mail messages. The mail-creation editor should support the usual text-editing functions. The program should be able to dial the host computer and upload and download all pending mail messages without my interaction or intervention. The program should support automatic message uploads and downloads, as well as interactive logins with the ability to upload and download binary files by using the XModem file-transfer protocol.

IMail: The Implementation

The IMail application uses the features of the D-Flat wrapper classes to define menus and dialog boxes and to associate member functions with those user-interface items. Listings One and Two are mailappl.h and mailappl.cpp, the source-code files that implement the MailAppl class, which is derived from the dfApplication class (listings begin on page 135). The class seems big, but it consists mostly of member functions associated with menu commands through the MSGMAP table. Many of these member functions do little more than open dialog boxes, which do most of the work of managing mail messages. Those dialog boxes are implemented in their own source-code files. I'm publishing only the application class this month, but I'll discuss generally how the whole program works.

Scripts

Interaction with the host--logging in, reading mail, sending mail, logging off--is managed by a set of scripts. IMail watches for prompting strings from the host and sends UNIX commands accordingly. The scripts are coded in a C++ source-code file named scripts.cpp. By isolating them there, I can change the scripts to work with other mail protocols if I want. They are not scripts in the truest sense of the word, however: They are not interpreted like the scripts of other communications programs. Since this program is for programmers who can be expected to have compilers, the scripts are written to be compiled into the program along with the rest of the code.

Mailboxes

IMail uses mailbox classes that store messages and display lists of the messages. Mail-reader classes display the contents of messages. The user can read messages, delete them, and file them in other mailboxes. The program uses two default mailboxes for incoming and outgoing messages: INBOX and OUTBOX. Other mailboxes are implemented as folders into which the user files messages to be saved. The user can establish and delete folders. The program maintains folders as subdirectories under a fixed subdirectory named CABINET. Any subdirectory under CABINET without a file extension is automatically treated as a folder. One such folder is named SENT and optionally holds copies of all the messages that the user sends. Another folder is named GENERAL, to be used as needed. The user may delete the GENERAL folder but not the SENT folder.

Messages in the mailboxes are text-file copies of the messages as written by the user or received from the user's correspondents. Each message is its own text file, and the program assigns a unique filename to each message file. The message filename has this format: MAILnnnn.MSG. The first message written to a folder is named MAIL0000.MSG, the next is MAIL0001.MSG, and so on. When messages are deleted or moved to other folders, their names can be reused for new messages in the folder.

I don't save a lot of mail. If I did, putting every message into its own text file would get expensive. Not only would each message consume a directory entry, but every message would also use disk space in multiples of the file system's cluster factor, which on my computer is 8K. If I were a heavy mail user and saver, I'd build an indexing system and concatenated message files, perhaps even with conversation-thread management.

Incoming Messages

IMail receives mail messages when it sees the text string "you have mail" in the input stream from the host. The UNIX system displays that message when you log in if mail is indeed waiting. The program waits for the $ prompt and then issues the following command as if you had typed it: cat $MAIL. The UNIX cat command displays a text file on the screen, and the $MAIL environment variable expands to the name of the logged-on user's incoming mail file in the UNIX environment. IMail captures the input stream into a disk file named IMAIL.TMP. When the messages are copied, IMail sends this command to delete the mail text file from the UNIX system: rm $MAIL.

After IMail logs off and hangs up, it converts the messages in IMAIL.TMP into message files in the incoming-mailbox folder. Each received message is stored in the incoming mailbox in this format: a line of text with the sender's mail address, a second line of text with the subject, and a third line with the date the message was written. Subsequent lines of text constitute the message body.

By reacting to the UNIX mail program's format for incoming messages, IMail is able to preserve only those three lines from the typically abundant, verbose message headers that are created by Internet mail transport programs and that are meaningless to most people. This one feature is worth the whole program. It removes one of my principal gripes about Internet mail--all that junk up front that nobody reads. The program parses the first lines of text, watching for lines with the strings "From:", "Subject:", and "Date:" in the first characters of the line. The program extracts the pertinent fields from those lines, ignoring all others until it sees a blank line, which marks the end of the message header and the start of the message text.

Outgoing Messages

For each outgoing message, the IMail script simulates an interactive session with the UNIX mail program. The script sends the "mail" command, a space character, the recipient's mail address, and a carriage return. Then the script waits for a "ject:" string that prompts for the subject, whereupon it sends the subject text and a carriage return. Next, it sends the message text, a final carriage return, and the Ctrl-D character to tell the mail program that the message is complete. When the $ prompt comes back, the script copies the message to the SENT folder if that option is selected and deletes the message from the outgoing message box.

Both scripts have a lot of one-second delays built in between interchanges with the host. These delays reflect what I have determined works best. Without them, some incoming data characters are lost, and the program goes into a timeout loop waiting for an expected string from the host.

The Communications Classes

To support the dial-up connection, I encapsulated the operations of the serial port and the modem in two classes. The CommPort class includes a lot of low-level stuff to connect to the input interrupt vector, read and write the serial device's data and status ports, and manage things like XON/XOFF protocols. The Modem class manages modem operations--dialing, hanging up, sending modem commands, and watching the carrier-detect signal. An XModem class handles uploads and downloads of binary files with the XModem file-transfer protocol. These operations allow me to use ddj.com for FTP downloads and for exchanging files with the staff at DDJ.

IMail: The Future

I developed IMail and the D-Flat wrapper classes with an objective: Like the rest of the world, I'm gradually becoming more of a Windows user and less of a DOS user. The architecture of this software leans toward that of a program written under the Microsoft Foundation Classes. Eventually IMail will be a Win32 application.

Source Code and Database

The source-code files for IMail and the D-Flat libraries are free. You can download them from the DDJ Forum on CompuServe and on the Internet by anonymous FTP; see "Availability," page 3.

If you cannot get to one of the online sources, send a 3.5-inch diskette and an addressed, stamped mailer to me at Dr. Dobb's Journal, 411 Borel Avenue, San Mateo, CA 94402, and I'll send you the source code. Make sure that you include a note that says which project you want. The code is free, but if you care to support my Careware charity, include a dollar for the Brevard County Food Bank.

Listing One


// ------- mailappl.h
#ifndef MAILAPPL_H
#define MAILAPPL_H

#include <fstream.h>

#define MAILPREFIX "MAIL"
#define MAILSUFFIX "MSG"
#define SENTFOLDER "SENT"
#define GENERALFOLDER "GENERAL"
#define INBOXSUBDIRECTORY "INBOX"
#define OUTBOXSUBDIRECTORY "OUTBOX"
#define CABINETSUBDIRECTORY "CABINET"
#define SENTSUBDIRECTORY CABINETSUBDIRECTORY "\\" SENTFOLDER
#define GENERALSUBDIRECTORY CABINETSUBDIRECTORY "\\" GENERALFOLDER
#define ADDRBOOK "ADDRBOOK.DAT"

#include <cstring.h>
#include "dfwrap.h"
#include "icmds.h"
#include "mailbox.h"
#include "contents.h"
#include "modem.h"

extern MBAR MainMenu;
extern DBOX ReadMailDB;
extern DBOX CreateMailDB;

class CreateMail;
class OnLineWnd;
class Modem;

// ------------ configuration items
struct IMailConfig  {
    // ----- login configuration
    char phoneno[25];    // phone number
    char loginid[35];    // user's remote login id
    char password[15];   // user's remote password (encrypted)
    bool viewlogin;      // true to view login scripts as they run
    bool saveoutgoing;   // true to save outgoing mail in SENT
    // ----- communications configuration
    CommParameters commparms;
    // ----- modem configuration
    ModemParameters modemparms;
};
class MailAppl : public dfApplication    {
    bool m_connected;
    bool m_closing;
    bool m_EscPressed;
    ofstream logfile;
    ofstream mailfile;
    CreateMail *mp_CreateMail;
    OnLineWnd *mp_OnLineWnd;
    InBox m_inbox;
    OutBox m_outbox;
    Folder m_folder;
    Modem *mp_Modem;
    bool LoadConfig();
    void SaveConfig();
    void EnDeCryptPassword();
    bool OnDownload();
    bool OnUploadXmodem();
    bool OnUploadASCII();
    bool OnViewLogin();
    bool OnDeliver();
    bool OnLogin();
    bool OnManual();
    bool OnHangup();
    bool OnLogTransmissions();
    bool OnCreateMail();
    bool OnAddrBook();
    bool OnKeyboard();
    bool OnAbout();
    bool OnInBox();
    bool OnOutBox();
    bool OnOpenFolder();
    bool OnFileCabinet();
    bool OnLoginOptions();
    bool OnCommOptions();
    bool OnModemOptions();
    bool OnClose();
    void LoginScript();
    void CollectMailScript();
    void DeliverMailScript();
    void ConvertMailScript();
    void GoInteractive(const CommParameters& cp,
                const char *phone, bool autolog);
    bool GetCommOptions(const string& ttl,
            CommParameters& commparms, char *phoneno);
    void DisableMenuCommands();
    void EnableMenuCommands();
    friend class ReadMail;
protected:
    MAPDEF(dfApplication)
public:
    static IMailConfig iCfg;
    // ----- document window configurations
    static DialogConfig CreateMailCfg;
    static DialogConfig ReadMailCfg;
    MailAppl();
    virtual ~MailAppl();
    bool isConnected()
        { return m_connected; }
    bool isModemDeclared()
        { return mp_Modem != 0; }
    const InBox& InBoxRef() const
        { return m_inbox; }
    const MailBox& OutBoxRef() const
        { return m_outbox; }
    Folder& FolderRef()
        { return m_folder; }
    void IMailPath(string& path) const;
    void AddrBookPath(string& path) const;
    void ConfigPath(string& path) const;
    void TempMailPath(string& path) const;
    void LogSerialInput(int ch);
};
extern MailAppl *mailappl;
#endif


Listing Two


// -------- mailappl.cpp
#include <fstream.h>
#include <strstrea.h>
#include "mailappl.h"
#include "addrbook.h"
#include "crmail.h"
#include "modem.h"
#include "loginopt.h"
#include "commopt.h"
#include "modemopt.h"
#include "fcabinet.h"
#include "onlinewn.h"

extern "C" {
char DFlatApplication[] = "IMail";
}
IMailConfig MailAppl::iCfg = {
    // ----- login configuration
    "",          // phone number
    "",          // user's remote login id
    "",          // user's remote password (encrypted)
    true,        // true to view login scripts as they run
    true,        // true to save outgoing mail in SENT
    // ----- communications configuration
    {
        1,       // serial port (1,2)
        0,       // parity       (0=N, 2=E)
        1,       // stop bits   (1)
        8,       // data bits   (7,8)
        9600     // baud rate
    },
    // ----- modem configuration
    {
        RESETMODEM, // reset string
        INITMODEM,  // initialize string
        DIAL,       // dial command
        HANGUP      // hangup string
    }
};
// --- dialog box configurations to be saved and restored
DialogConfig MailAppl::CreateMailCfg;
DialogConfig MailAppl::ReadMailCfg;

MSGMAP(MailAppl)
    MSG(KEYBOARD,          OnKeyboard)
    MSG(ID_DOWNLOAD,       OnDownload)
    MSG(ID_UPLOADASCII,    OnUploadASCII)
    MSG(ID_UPLOADXMODEM,   OnUploadXmodem)
    MSG(ID_VIEWLOGIN,      OnViewLogin)
    MSG(ID_DELIVER,        OnDeliver)
    MSG(ID_LOGIN,          OnLogin)
    MSG(ID_MANUAL,         OnManual)
    MSG(ID_HANGUP,         OnHangup)
    MSG(ID_LOGTRX,         OnLogTransmissions)
    MSG(ID_CREATE,         OnCreateMail)
    MSG(ID_ADDRBOOK,       OnAddrBook)
    MSG(ID_LOGINOPTIONS,   OnLoginOptions)
    MSG(ID_COMMUNICATIONS, OnCommOptions)
    MSG(ID_MODEMOPTIONS,   OnModemOptions)
    MSG(ID_INBOX,          OnInBox)
    MSG(ID_OUTBOX,         OnOutBox)
    MSG(ID_FILECABINET,    OnFileCabinet)
    MSG(ID_ABOUT,          OnAbout)
    MSG(CLOSE_WINDOW,      OnClose)
ENDMAP
// -------- construct the mail application
MailAppl::MailAppl() : dfApplication(string(), MainMenu),
                m_connected(false),
                m_closing(false),
                m_EscPressed(false),
                mp_CreateMail(0),
                mp_OnLineWnd(0),
                mp_Modem(0)
{
    string path;
    m_inbox.MailBoxPath(path);
    mkdir(path.c_str());
    m_outbox.MailBoxPath(path);
    mkdir(path.c_str());
    MailBox::CabinetPath(path);
    mkdir(path.c_str());
    mkdir((path+GENERALSUBDIRECTORY).c_str());
    mkdir((path+SENTSUBDIRECTORY).c_str());
}
// ---- destroy the mail application
MailAppl::~MailAppl()
{
    if (logfile.rdbuf()->is_open())
        logfile.close();
    delete mp_CreateMail;
    delete mp_OnLineWnd;
    delete mp_Modem;
}
// ---- encrypt and decrypt the password for the the configuration
void MailAppl::EnDeCryptPassword(void)
{
    char *pwd = iCfg.password;
    srand(62490U);
    while (*pwd && pwd < iCfg.password + sizeof iCfg.password)
        *pwd++ ^= (rand() % 256);
}
// ---- load the program's configuration from the last run
bool MailAppl::LoadConfig()
{
    dfApplication::LoadConfig();
    string path;
    ConfigPath(path);
    ifstream cfgfile(path.c_str(), ios::binary);
    if (!cfgfile.fail())    {
        cfgfile.seekg(sizeof(CONFIG));
        cfgfile.read(reinterpret_cast<char*>(&iCfg), sizeof(iCfg));
        cfgfile.read(reinterpret_cast<char*>(&ReadMailCfg),
            sizeof(ReadMailCfg));
        cfgfile.read(reinterpret_cast<char*>(&CreateMailCfg),
            sizeof(CreateMailCfg));
        EnDeCryptPassword();
    }
    if (iCfg.viewlogin)
        SetCommandToggle((commands)ID_VIEWLOGIN);
    return true;
}
// ---- save the program's configuration for the next time
void MailAppl::SaveConfig()
{
    dfApplication::SaveConfig();
    string path;
    ConfigPath(path);
    ofstream cfgfile(path.c_str(), ios::ate | ios::binary);
    if (!cfgfile.fail())    {
        EnDeCryptPassword();
        cfgfile.write(reinterpret_cast<char*>(&iCfg), sizeof(iCfg));
        cfgfile.write(reinterpret_cast<char*>(&ReadMailCfg),
            sizeof(ReadMailCfg));
        cfgfile.write(reinterpret_cast<char*>(&CreateMailCfg),
            sizeof(CreateMailCfg));
        EnDeCryptPassword();
    }
}
// ---- build a path to the IMAIL application
void MailAppl::IMailPath(string& path) const
{
    path = _argv[0];
    int wh = path.find_last_of("\\");
    path.resize(wh+1);
}
// ---- build a path and filename for the configuration file
void MailAppl::ConfigPath(string& path) const
{
    IMailPath(path);
    path += DFlatApplication;
    path += ".cfg";
}
// ---- build a path and filename for the address book
void MailAppl::AddrBookPath(string& path) const
{
    IMailPath(path);
    path += ADDRBOOK;
// ---- build a path and filename for the temporary mail file
void MailAppl::TempMailPath(string& path) const
{
    IMailPath(path);
    path += DFlatApplication;
    path += ".tmp";
}
// ---- File/Download... command
bool MailAppl::OnDownload()
{
    if (mp_Modem != 0)    {
        char FileName[128] = "*.*";
        while (SaveAsDialogBox(FileName, "*.*", FileName))    {
            if (access(FileName, 0) == 0)    {
                strstream ermsg;
                ermsg << FileName
                      << " exists. Replace it?"
                      << ends;
                bool rtn = YesNoBox(ermsg.str());
                delete ermsg.str();
                if (!rtn)
                    continue;
            }
            ofstream downfile(FileName, ios::binary);
            mp_Modem->DownloadXmodem(downfile);
            break;
        }
    }
    return false;
}
// ---- get a file name to upload from the user
bool MailAppl::GetUploadFile(ifstream& upfile, int mode)
{
    char FileName[128] = "*.*";
    while (OpenFileDialogBox(FileName, FileName))    {
        upfile.open(FileName, mode);
        if (upfile.fail())    {
            strstream ermsg;
            ermsg << "No such file as " << FileName << ends;
            ErrorMessage(ermsg.str());
            delete ermsg.str();
            continue;
        }
        return true;
    }
    return false;
}
// ---- File/Upload/Xmodem... command
bool MailAppl::OnUploadXmodem()
{
    if (mp_Modem != 0)    {
        ifstream upfile;
        if (GetUploadFile(upfile, ios::binary))
            mp_Modem->UploadXmodem(upfile);
    }
    return false;
}
// ---- File/Upload/ASCII... command
bool MailAppl::OnUploadASCII()
{
    if (mp_Modem != 0)    {
        ifstream upfile;
        if (GetUploadFile(upfile))
            mp_Modem->UploadASCII(upfile);
    }
    return false;
}
// ---- View/View login script command
bool MailAppl::OnViewLogin()
{
    iCfg.viewlogin = GetCommandToggle((commands)ID_VIEWLOGIN);
    return false;
}
// ---- CLOSE_WINDOW message
bool MailAppl::OnClose()
{
    if (m_connected)    {
        if (YesNoBox("Log off and exit?"))    {
            m_connected = false;
            m_closing = true;
        }
        return false;
    }
    return true;
}
// ---- command codes to be disabled during on-line session
static int dcmds[] = {
    ID_ADDRBOOK,
    ID_VIEWLOGIN,
    ID_DELIVER,
    ID_DOS,
    ID_CUT,
    ID_COPY,
    ID_PASTE,
    ID_DELETETEXT,
    ID_PARAGRAPH,
    ID_REPLACE,
    ID_SEARCHNEXT,
    ID_INTERACTIVE,
    ID_CREATE,
    ID_INBOX,
    ID_OUTBOX,
    ID_FILECABINET,
    ID_LOGINOPTIONS,
    ID_COMMUNICATIONS,
    ID_MODEMOPTIONS,
    ID_DISPLAY,
    ID_CLOSEALL,
    ID_WINDOW,
    0
};
// ----- disable menu commands during on-line session
void MailAppl::DisableMenuCommands()
{
    for (int i = 0; i < *(dcmds+i) != 0; i++)
        DeactivateCommand(&MainMenu, *(dcmds+i));
}
// ----- enable menu commands following on-line session
void MailAppl::EnableMenuCommands()
{
    for (int i = 0; i < *(dcmds+i) != 0; i++)
        ActivateCommand(&MainMenu, *(dcmds+i));
}
// ---- log onto the host for an interactive session
void MailAppl::GoInteractive(const CommParameters& cp,
            const char *phone, bool autolog)
{
    DisableMenuCommands();
    mp_Modem = new Modem(cp, iCfg.modemparms);
    if (iCfg.viewlogin || !autolog)
        mp_OnLineWnd = new OnLineWnd(mp_Modem);
    m_connected = mp_Modem->Dial(phone);
    if (m_connected && autolog)
        LoginScript();
    if (mp_OnLineWnd == 0)
        mp_OnLineWnd = new OnLineWnd(mp_Modem);
    while (m_connected)    {
        dispatch_message();
        if (mp_Modem->InputCharReady())
            LogSerialInput(mp_Modem->ReadChar());
        else if (!m_closing)
            m_connected = mp_Modem->TestCarrier();
    }
    delete mp_OnLineWnd;
    mp_OnLineWnd = 0;
    delete mp_Modem;
    mp_Modem = 0;
    WriteStatus("Off line");
    EnableMenuCommands();
    if (m_closing)
        CloseWindow();
}
// ---- Connect/Send-receive Mail command
bool MailAppl::OnDeliver()
{
    DeactivateCommand(&MainMenu, ID_EXIT);
    DisableMenuCommands();
    mp_Modem = new Modem(iCfg.commparms, iCfg.modemparms);
    if (iCfg.viewlogin)
        mp_OnLineWnd = new OnLineWnd(mp_Modem);
    m_connected = mp_Modem->Dial(iCfg.phoneno);
    if (m_connected)    {
        LoginScript();
        CollectMailScript();
        DeliverMailScript();
    }
    mp_Modem->HangUp();
    m_connected = false;
    delete mp_Modem;
    mp_Modem = 0;
    delete mp_OnLineWnd;
    mp_OnLineWnd = 0;
    WriteStatus("Off line");
    EnableMenuCommands();
    ConvertMailScript();
    ActivateCommand(&MainMenu, ID_EXIT);
    if (m_inbox.filecount)    {
        WriteStatus("You have mail in your Inbox");
        beep();
    }
    return false;
}
// ---- Connect/Interactive Session/Automatic Login... command
bool MailAppl::OnLogin()
{
    GoInteractive(iCfg.commparms, iCfg.phoneno, true);
    return false;
}
// ---- get communications options from user
bool MailAppl::GetCommOptions(const string& ttl,
            CommParameters& commparms, char *phoneno)
{
    bool rtn = p_commoptions->doModal();
    delete p_commoptions;
    return rtn;
}
// ---- Connect/Interactive Session/Manual Login... command
bool MailAppl::OnManual()
{
    static CommParameters commparms;
    static char phone[25] = "";
    static bool doneit = false;

    if (!doneit)    {
        commparms = iCfg.commparms;
        doneit = true;
    }
    if (GetCommOptions("Manual Dial", commparms, phone))
        GoInteractive(commparms, phone, false);
    return false;
}
// ---- Log serial input stream from host
void MailAppl::LogSerialInput(int ch)
{
    if (mp_OnLineWnd != 0)    {
        mp_OnLineWnd->WriteChar(ch);
        if (logfile.rdbuf()->is_open())
            logfile.put(static_cast<char>(ch));
    }
    if (mailfile.rdbuf()->is_open())
        mailfile.put(static_cast<char>(ch));
}
// ---- Connect/Hang up command
bool MailAppl::OnHangup()
{
    if (m_connected && mp_Modem != 0)    {
        mp_Modem->HangUp();
        m_connected = false;
    }
    return false;
}
// ---- File/Log Transmissions... command
bool MailAppl::OnLogTransmissions()
{
    if (logfile.rdbuf()->is_open())
        logfile.close();
    else    {
        char *FileName = new char[128];
        strcpy(FileName, "*.log");
        if (OpenFileDialogBox(FileName, FileName))
            logfile.open(FileName, ios::app);
        else
            ClearCommandToggle((commands) ID_LOGTRX);
        delete[] FileName;
    }
    return false;
}
// ---- Messages/Create New... command
bool MailAppl::OnCreateMail()
{
    if (mp_CreateMail && mp_CreateMail->isRunning())
        mp_CreateMail->SetControlFocus(ID_MESSAGE);
    else    {
        delete mp_CreateMail;
        mp_CreateMail = new CreateMail(m_outbox, "New Mail", true);
        mp_CreateMail->doModeless();
    }
    return false;
}
// ---- KEYBOARD message
bool MailAppl::OnKeyboard()
{
    if (p1 == ESC)
        m_EscPressed = true;
    return true;
}
// ---- File/Address Book... command
bool MailAppl::OnAddrBook()
{
    if (mp_CreateMail && mp_CreateMail->isRunning())
        mp_CreateMail->OnAddrBook();
    else    {
        AddrBook *p_addrbook = new AddrBook;
        p_addrbook->doModal();
        delete p_addrbook;
    }
    return false;
}
// ---- Messages/Out Box... command
bool MailAppl::OnOutBox()
{
    m_outbox.OpenMailBox("OutBox");
    return false;
}
// ---- Messages/In Box... command
bool MailAppl::OnInBox()
{
    m_inbox.OpenMailBox("InBox");
}
// ---- open a mail folder
bool MailAppl::OnOpenFolder()
{
    if (!m_folder.FolderName().is_null())    {
        static string title;
        title = "Folder: " + m_folder.FolderName();
        m_folder.OpenMailBox(title);
    }
    return false;
}
// ---- Messages/File Cabinet... command
bool MailAppl::OnFileCabinet()
{
    FolderReader* p_fr = new FolderReader();
    if (p_fr->doModal())
        OnOpenFolder();
    delete p_fr;
    return false;
}
// ---- Options/Login... command
bool MailAppl::OnLoginOptions()
{
    LoginOptions *p_loginoptions = new LoginOptions;
    p_loginoptions->doModal();
    delete p_loginoptions;
    return false;
}
// ---- Options/Communications... command
bool MailAppl::OnCommOptions()
{
    GetCommOptions("Communications Options", iCfg.commparms, iCfg.phoneno);
    return false;
}
// ---- Options/Modem... command
bool MailAppl::OnModemOptions()
{
    ModemOptions *p_modemoptions = new ModemOptions;
    p_modemoptions->doModal();
    delete p_modemoptions;
    return false;
}
// ---- Help/About command    
bool MailAppl::OnAbout()
{
    MessageBox(
            "About IMail",
        "   |------------------------------------------------| \n"
        "   |     zzz   zzz      z    | \n"
        "   |     z  z  z  z     z    | \n"
        "   |     z  z  z  z     z    | \n"
        "   |     z  z  z  z  z  z    | \n"
        "   |     zzz   zzz    zzz     | \n"
        "   |------------------------------------------------| \n"
        "  IMail Manages ddj.com Mail   ");
    return false;
}



Copyright © 1995, Dr. Dobb's Journal


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.