
Here is an excerpt from some code that I wrote that watches an AltaVista
mailbox and when new messages are delivered to it, it performs a POP3
connection to fetch the messages and deliver them to the user's NNMAIL
NEW folder.  This isn't the complete code; I post it mainly because
it shows how to fetch messages using POP3 protocol.

--PSW
=============================================

// pop3_monitor.cpp - POP3 monitor thread and utility routines
//
#include <windows.h>
#include <winsock.h>
#include <stdio.h>
#include <string.h>

#include "util.hxx"
#include "pop3_monitor.hxx"
#include "bsocket.hxx"


#define ISDIGIT(c) ((c) >= '0' && (c) <= '9')

// largest possible serial number for mail files in nnmail
//
#define MAXSERIAL 9999

// sleep time (milliseconds) between retries for locking the serial
// number file, and maximum total sleep time before we give up entirely
//
#define SLEEPTIME 250
#define MAXSLEEPTIME 20*1000


// suffix for temporary file
//
static const char *tfile_name = "mmonitor.tmp";

// forward routine declarations
//
static HANDLE open_tempfile(char *dirpath, FILE *logfile);
static int deliver(char *dirpath, FILE *logfile);
static int get_serial_number(char *dirpath, char *serialfile, FILE *logfile); 
static int find_highest_used(char *dirpath, FILE *logfile);

static unsigned long run_thread(void *m)
{
POP3_MONITOR *obj = (POP3_MONITOR *) m;
obj->tbody();
return 0;
}


// start a POP3 monitor thread.  Open a change notification event
// stream on the mailbox path, create the timer object and set the
// initial timer interval, then kick off the monitor thread itself.
//
int POP3_MONITOR::start()
{
// set the POP3 port number in the socket address structure
//
server.sin_port = htons(110);

sock = INVALID_SOCKET;
thread = 0;
if ((thread = CreateThread(NULL, 0, run_thread, this, 0, &thread_id)) == NULL)
	{
	squawk(logfile, "***CreateThread failed:");
	return 0;
	}

return 1;
}


// Function to perform the POP3 message monitoring
//
void POP3_MONITOR::tbody()
{
int ok = 1;
int line_len;
char searchname[MAX_PATH];
BSOCKET bs;
char line[4096];

// set up the wildcard for FindFirstFile
//
sprintf(searchname, "%s\\*.821", mbox_path);

// Monitor file creation and deletion in the mailbox directory.
//
if ((notification = FindFirstChangeNotification(mbox_path, FALSE,
							FILE_NOTIFY_CHANGE_FILE_NAME)) 
== INVALID_HANDLE_VALUE)
	{
	squawk(logfile, "***FindFirstChangeNotification failed:");
	ExitThread(0);
	}

// Loop forever, unless an error occurs
//
while (ok)
	{
	HANDLE find;
	int msgno;
	WIN32_FIND_DATA found;

	// Look for pending messages in the mailbox.  We can't depend
	// simply on the change notification because the POP3 server
	// creates lock files in there as well.
	//
	if ((find = FindFirstFile(searchname, &found)) == INVALID_HANDLE_VALUE)
		{
		DWORD error = GetLastError();

		if ((error != ERROR_FILE_NOT_FOUND) && (error != ERROR_NO_MORE_FILES))
			{ 
			squawk(logfile, "*** FindFirstFile on mailbox failed");
			ok = 0;
			break;
			}
		}
	else
		{
		// There are messages waiting!  Close the find file context, then
		// retrieve the messages.
		//
		FindClose(find);

		// Connect to the POP3 server and retrieve any messages
		//
		sprintf(line, "connecting to POP3 server, mailbox %s", mbox_name);
		tsay(logfile, line);
		if ((sock = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
			{
			ssquawk(logfile, "*** socket call failed:");
			ok = 0;
			break;
			}

		if (connect(sock, (struct sockaddr *) &server, sizeof server))
			{
			ssquawk(logfile, "*** connect call failed:");
			ok = 0;
			break;
			}

		bs.init(sock, logfile);
    
		// server hello message
		//
		if (!bs.getline(sizeof line, line, &line_len))
			{
			ok = 0;
			break;
			}

		// perform login
		//
		sprintf(line, "USER %s\r\n", mbox_name);
		if (!putline(sock, line, logfile))
			{
			ok = 0;
			break;
			}

		if (!bs.getline(sizeof line, line, &line_len))
			{
			ok = 0;
			break;
			}

		if (line[0] == '-')
			{
			say(logfile, "*** POP3 USER command failed:");
			say(logfile, line);
			ok = 0;
			break;
			}

		sprintf(line, "PASS %s\r\n", mbox_pass);
		if (!putline(sock, line, logfile))
			{
			ok = 0;
			break;
			}

		if (!bs.getline(sizeof line, line, &line_len))
			{
			ok = 0;
			break;
			}

		if (line[0] == '-')
			{
			say(logfile, "*** POP3 PASS command failed:");
			say(logfile, line);
			ok = 0;
			break;
			}
		
		// Retrieve all messages currently in the mailbox.
		//
		for (msgno = 1; ok; msgno++)
			{
			HANDLE tempfile;

			sprintf(line, "RETR %d\r\n", msgno);
			if (!putline(sock, line, logfile))
				{
				ok = 0;
				break;
				}

			if (!bs.getline(sizeof line, line, &line_len))
				{
				ok = 0;
				break;
				}

			// check for no more messages
			//
			if (line[0] == '-')
				break;
		
			// open temporary file
			//
			if ((tempfile = open_tempfile(dest_path, logfile)) == 
INVALID_HANDLE_VALUE)
				{
				ok = 0;
				break;
				}

			// copy text to temporary file
			//
			while (ok)
				{
				DWORD n_written;
				char *outline = &line[0];

				if (!bs.getline(sizeof line, line, &line_len))
					{
					ok = 0;
					break;
					}

				// check for termination and byte-unstuffing
				//
				if (line[0] == '.')
					if ((line[1] == '\r') && (line[2] == '\n'))
						break; // termination
					else
						{
						// unstuff
						//
						outline++;
						line_len--;
						}

				// write line to temp file
				//
				if (!WriteFile(tempfile, outline, line_len, &n_written, 
NULL))
					{
					squawk(logfile, "*** write to temporary file 
failed");
					ok = 0;
					break;
					}
				}

			// close temporary file
			//
			if (!CloseHandle(tempfile))
				{
				squawk(logfile, "*** close of temporary file failed");
				ok = 0;
				break;
				}

			// break out of this loop if there was an error during the
			// message fetch
			//
			if (!ok)
				break;

			// rename the temporary file to complete delivery of the 
message
			//
			if (!deliver(dest_path, logfile))
				{
				ok = 0;
				break;
				}

			// delete the message from the POP3 mailbox
			//
			sprintf(line, "DELE %d\r\n", msgno);
			if (!putline(sock, line, logfile))
				{
				ok = 0;
				break;
				}

			if (!bs.getline(sizeof line, line, &line_len))
				{
				ok = 0;
				break;
				}
			}

		// if all went OK, give the POP3 server the QUIT command to commit
		// the deletion of the messages from the mailbox.
		//
		if (ok)
			{
			if (!putline(sock, "QUIT\r\n", logfile))
				{
				ok = 0;
				break;
				}

			if (!bs.getline(sizeof line, line, &line_len))
				{
				ok = 0;
				break;
				}
			}

		// Close the socket
		//
		closesocket(sock);
		sock = INVALID_SOCKET;
	
		sprintf(line, "fetched %d message(s) from mailbox %s", msgno - 1, 
mbox_name);
		tsay(logfile, line);
		}

    // Give the POP3 server a few seconds to settle down
	//
	Sleep(5*1000);

	// Wait until the next time the directory's poked or the timeout
	// expires.
	//
	switch (WaitForSingleObject(notification, timeout))
		{
		case WAIT_OBJECT_0:
			//
			// Directory has changed.  We disable the change notification
			// because the POP3 server creates temporary files in the 
directory
			// we're watching during the fetch process.  If we don't stop 
watching
			// while we're fetching, we get an infinite loop of change 
notifications.
			//
			if (!FindNextChangeNotification(notification))
				{
				squawk(logfile, "*** FindNextChangeNotification 
failed");
				ok = 0;
				}
			break;

		case WAIT_TIMEOUT:
			//
			// Timeout expired.
			//
			break;

		default:
			//
			// Something strange happened.
			//
			squawk(logfile, "*** WaitForSingleObject failed:");
			ok = 0;
			break;
	    }
	}

// Clean up and terminate the thread
//
if (sock != INVALID_SOCKET)
	closesocket(sock);

ExitThread(0);
}


// open a temporary file in the destination mailbox directory
//
static HANDLE open_tempfile(char *dirpath, FILE *logfile)
{
HANDLE retval;
char tname[MAX_PATH];


sprintf(tname, "%s\\%s", dirpath, tfile_name);
if ((retval = CreateFile(tname, GENERIC_WRITE, 0, 0, CREATE_ALWAYS,
				FILE_ATTRIBUTE_NORMAL, 0)) == INVALID_HANDLE_VALUE)
	squawk(logfile, "*** temporary file create failed");

return retval;
}


// deliver the mail message by renaming the temporary file to
// a name of the form MAILnnnn.MAI.  The trick is determining "nnnn".
// There may be other threads of control besides us trying to deliver
// messages concurrantly, so we may have to retry a bunch of times
// with different serial numbers.
//
static int deliver(char *dirpath, FILE *logfile)
{
int retries;
char tempfile[MAX_PATH];
char serialfile[MAX_PATH];
char destfile[MAX_PATH];

sprintf(tempfile, "%s\\%s", dirpath, tfile_name);
sprintf(serialfile, "%s\\#serial.dat", dirpath);

// keep trying until we succeed in getting it properly renamed
//
for (retries = 0; retries < MAXSERIAL; retries++)
	{
	int serialnum;

	// Get the next candidate serial number.  If we get back a
	// negative number, it means we couldn't allocate a serial
	// number for some reason.
	//
	if ((serialnum = get_serial_number(dirpath, serialfile, logfile)) < 0)
		return 0;

	// try to rename the temp file to that name
	//
	sprintf(destfile, "%s\\msg%04d.mai", dirpath, serialnum);
	if (MoveFile(tempfile, destfile))
		return 1;

	// punt if the move failed for any reason other than "file
	// already exists"
	//
	if (GetLastError() != ERROR_FILE_EXISTS)
		{
		squawk(logfile, "*** error renaming temp file");
		return 0;
		}
	}

// if we get here, we tried innumerable times and failed.
//
say(logfile, "*** temp file rename retry count exhausted");
return 0;
}


// Obtain the next candidate serial number for file renaming.
// File #serial.dat contains the last-used serial number.
// This routine always updates the value, wrapping at the max.
// serial number if necessary.  Note that we must have exclusive
// access to #serial.dat when doing the update.  If we find the file
// locked, we sleep for a while.
//
static int get_serial_number(char *dirpath, char *serialfile, FILE *logfile) 
{
HANDLE snfile;
int wait_time;
int serial = -1;
DWORD error;
DWORD n_read;
char snbuff[16];

// open the serial file for exclusive access, creating it if it
// doesn't exist and sleeping and retrying if necessary.
//
for (wait_time = 0; wait_time < MAXSLEEPTIME; wait_time += SLEEPTIME)
	{
	snfile = CreateFile(serialfile, GENERIC_READ | GENERIC_WRITE,
				0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
	if ((snfile == INVALID_HANDLE_VALUE) &&
			((error = GetLastError()) == ERROR_SHARING_VIOLATION))
		Sleep(SLEEPTIME);  // file was locked--wait and retry
	else
		break;  // either we opened it or something bad happened
	}

// At this point, we either have opened the serial file for exclusive
// access or we've determined we can't open it.
//
if (snfile == INVALID_HANDLE_VALUE)
	{
	squawk(logfile, "*** unable to obtain a serial number");
	return -1;
	}

// Read the serial number from the file and update it.
//
do  {
	if (!ReadFile(snfile, snbuff, sizeof snbuff - 1, &n_read, NULL))
		{
		squawk(logfile, "*** error reading serial number file");
		break;
		}

	snbuff[n_read] = '\0';
	if ((n_read == 0) || (sscanf(snbuff, "%d", &serial) != 1) ||
			(serial < 0))
		{
		// Serial number file was empty or corrupted.  Set the serial
		// number based on the highest one currently in use.
		//
		if ((serial = find_highest_used(dirpath, logfile)) < 0)
			break;
		}

	// Increment the serial number.  Wrap if necessary.
	//
	serial++;
	if (serial > MAXSERIAL)
		serial = 1;

	// Update the serial number file
	//
	sprintf(snbuff, "%d\r\n", serial);
	if (SetFilePointer(snfile, 0, NULL, FILE_BEGIN) == 0xffffffff)
		{
		squawk(logfile, "*** SetFilePointer on serial file failed");
		break;
		}

	if (!WriteFile(snfile, snbuff, strlen(snbuff), &n_read, NULL))
		{
		squawk(logfile, "*** WriteFile on serial file failed");
		break;
		}

	if (!SetEndOfFile(snfile))
		squawk(logfile, "*** SetEndOfFile on serial file failed");

	} while (0);

CloseHandle(snfile);
return serial;
}


// Scan the directory to find the highest message serial number in
// use.  We don't really have to do this, but monotonically-increasing
// serial numbers are more user-friendly.
//
static int find_highest_used(char *dirpath, FILE *logfile)
{
BOOL more;
HANDLE find;
int i;
int serial = 0;
DWORD error;
WIN32_FIND_DATA found;
char filename[MAX_PATH];

sprintf(filename, "%s\\msg????.mai", dirpath);
more = ((find = FindFirstFile(filename, &found)) != INVALID_HANDLE_VALUE);
while (more)
	{
	int num = 0;
	int mult = 1;

	// got a candidate file.  determine its serial number and
	// remember it if it's bigger than what we've already got.
	//
	i = strlen(found.cFileName) - 1;
	while ((i >= 0) && !ISDIGIT(found.cFileName[i]))
		i--;

	while ((i >= 0) && ISDIGIT(found.cFileName[i]))
		{
		num += (found.cFileName[i] - '0') * mult;
		mult *= 10;
		i--;
		}

	if (num > serial)
		serial = num;

	more = FindNextFile(find, &found);
	}

error = GetLastError();
if ((error != ERROR_FILE_NOT_FOUND) && (error != ERROR_NO_MORE_FILES))
	{
	squawk(logfile, "*** error during serial directory search");
    serial = -1;
	}

if (find != INVALID_HANDLE_VALUE)
    FindClose(find);

return serial;
}

