/*
Auto:		sc <file>
Auto:		Protect <file> ADD p
*/


/* $Revision Header built automatically *************** (do not edit) ************
**
** © Copyright by GuntherSoft
**
** File             : SnakeSYS:CPrgs/MailQueue/MailQueue.c
** Created on       : Freitag, 13.01.95 17:12:27
** Created by       : Kai Iske
** Current revision : V37.2
**
**
** Purpose
** -------
**     Watches your SMTP spool directory for the number of
**     messages queued.
**
** Revision V37.2
** --------------
** created on Mittwoch, 15.02.95 13:32:37  by  Kai Iske.   LogMessage :
**   - Added SendMail switch, which acts as a SENDMAIL compatible
**     switch. SendMail creates two D.#? entries for one mail.
**     The SendMail switch causes MailQueue to halve the number
**     of files found, in order to show the number of mails
**     (Requested by : <phil@curve.demon.co.uk>)
**
** Revision V37.1
** --------------
** created on Sonntag, 22.01.95 03:25:27  by  Greg Patterson.   LogMessage :
**  -*-  changed on Montag, 23.01.95 20:38:31  by  Kai Iske.   LogMessage :
**   - Added new ONLYWB option, whichs causes the program
**     to only open it`s window, on Workbench
**  -*-  changed on Montag, 23.01.95 19:22:49  by  Kai Iske.   LogMessage :
**   - MailQueue wasn`t really pure, as it used to set up LibBases
**     as globals (well, technically wasn`t pure ;)). Changed.
**     (Reported by : WShell users)
**  -*-  changed on Sonntag, 22.01.95 23:37:04  by  Kai Iske.   LogMessage :
**   - Whoops, wrong version string contained in executable ;)
**  -*-  created on Sonntag, 22.01.95 03:25:27  by  Greg Patterson.   LogMessage :
**   - Added ZEROCLOSE option to hide the Queue window when no messages are
**     queued.
**
** Revision V37.0
** --------------
** created on Freitag, 13.01.95 17:12:27  by  Kai Iske.   LogMessage :
**     --- Initial release ---
**
*********************************************************************************/
#define REVISION "37.2"
#define REVDATE  "15.02.95"
#define REVTIME  "13:32:37"
#define AUTHOR   "Kai Iske"
#define VERNUM   37
#define REVNUM   2



#include	<stdlib.h>
#include	<stdio.h>
#include	<string.h>
#include	<stdarg.h>
#include	<exec/types.h>
#include	<exec/memory.h>
#include	<exec/execbase.h>
#include	<proto/exec.h>
#include	<proto/dos.h>
#include	<proto/intuition.h>
#include	<proto/netsupport.h>
#include	<dos/dos.h>
#include	<dos/dosextens.h>
#include	<dos/notify.h>
#include	<dos/exall.h>
#include	<intuition/intuition.h>



/**********************************************************************/
/*                           Version String                           */
/**********************************************************************/
static	const	char	Ver[]		= "$VER: MailQueue "REVISION" ("REVDATE")\0";




/**********************************************************************/
/*                              Template                              */
/**********************************************************************/
static	const	char	Template[]	= "WinX/N,WinY/N,OPENNOW/S,ZEROCLOSE/S,ONLYWB/S,SENDMAIL/S";
enum{WINX_ARG, WINY_ARG, OPENNOW_ARG, ZEROCLOSE_ARG, ONLYWB_ARG, SENDMAIL_ARG, LAST_ARG};



/**********************************************************************/
/*                        Our notify structure                        */
/**********************************************************************/
struct	NotifyNode
{
	struct	Node		Link;
	struct	NotifyRequest	Notify;
	char			Name[258];
};





/**********************************************************************/
/*                             Prototypes                             */
/**********************************************************************/
static	LONG	ScanDir(char *DirName, struct List *NotifyList, ULONG NotifySig, struct ExAllControl *EAC, struct ExAllData *EAB, struct DosLibrary *DOSBase);
static	void	__stdargs SPrintF(STRPTR buffer, STRPTR formatString,...);





/**********************************************************************/
/*                       This is the main stuff                       */
/**********************************************************************/
ULONG __saveds MailQueue(void)
{
	struct	ExecBase		*SysBase	= *((struct ExecBase **)0x4L);
	struct	DosLibrary		*DOSBase;
	struct	IntuitionBase		*IntuitionBase;
	struct	NetSupportLibrary	*NetSupportBase;
	struct	Process			*MyProc		= (struct Process *)SysBase->ThisTask;
	struct	RDArgs			*RDArgs;
	struct	ExAllControl		*EAC;
	struct	ExAllData		*EAB;
	LONG				NotifySig;
	APTR				*Args;
	ULONG				NotifyMask,
					MySig;
	BOOL				Error		= TRUE;


		// Ignore startup from WB

	if(!(MyProc->pr_CLI))
	{
		struct	Message *MyMsg;

		WaitPort(&MyProc->pr_MsgPort);
		MyMsg = GetMsg(&MyProc->pr_MsgPort);
		Disable();
		ReplyMsg(MyMsg);
		return(10);
	}

		// Do the wild thing

	if((DOSBase = (struct DosLibrary *)OpenLibrary("dos.library", 37)))
	{
		if((IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library", 37)))
		{
			if((NetSupportBase = (struct NetSupportLibrary *)OpenLibrary("netsupport.library", 1)))
			{
				if((EAC = AllocDosObject(DOS_EXALLCONTROL, NULL)))
				{
					if((EAB = AllocVec(sizeof(struct ExAllData) * 16, MEMF_CLEAR)))
					{
						if((Args = AllocVec(LAST_ARG * sizeof(ULONG), MEMF_CLEAR)))
						{
							char	*Entry;

							Entry	= GetConfig(NULL, "SMTPSPOOLDIR", "SMTPSPOOLDIR", "");

							if(Entry && strlen(Entry))
							{
								if((RDArgs = ReadArgs((char *)Template, (LONG *)Args, NULL)))
								{
									if((NotifySig = AllocSignal(-1)) != -1)
									{
										struct	List		NotifyList;
										struct	NotifyRequest	DirNotify;
										struct	NotifyNode	*Notify;
										struct	Window		*NotifyWindow	= NULL;
										ULONG			NotifyWinSig	= 0,
													WinLeft		= 0,
													WinTop		= 0;
										char			WinTitle[80];
										BOOL			Initial		= TRUE,
													ZeroClose	= TRUE,
													OnlyWB		= FALSE,
													SendMail	= FALSE;

											// Get arguments

										if(Args[WINX_ARG])
											WinLeft		= *((ULONG *)Args[WINX_ARG]);

										if(Args[WINY_ARG])
											WinTop		= *((ULONG *)Args[WINY_ARG]);

										if(!Args[OPENNOW_ARG])
											Initial		= FALSE;

										if(!Args[ZEROCLOSE_ARG])
											ZeroClose	= FALSE;

										if(Args[ONLYWB_ARG])
											OnlyWB		= TRUE;

										if(Args[SENDMAIL_ARG])
											SendMail	= TRUE;


											// Set up list of notification requests

										NewList(&NotifyList);

											// Init notification for Directory
											// and cause initial notification

										DirNotify.nr_Name				= Entry;
										DirNotify.nr_Flags				= NRF_SEND_SIGNAL|NRF_NOTIFY_INITIAL;
										DirNotify.nr_stuff.nr_Signal.nr_Task		= MyProc;
										DirNotify.nr_stuff.nr_Signal.nr_SignalNum	= NotifySig;

											// Do it

										if(StartNotify(&DirNotify))
										{
											Error		= FALSE;

												// Get Mask for Notification Signal

											NotifyMask	= 1 << NotifySig;

											for(;;)
											{
												MySig	= Wait(SIGBREAKF_CTRL_C|NotifyMask|NotifyWinSig);

													// Quit now

												if(MySig & SIGBREAKF_CTRL_C)
													break;

													// Anything has changed within the dir

												if(MySig & NotifyMask)
												{
													LONG			Number;

														// Rescan directory and set up new
														// notifications for files within the dir
														// Create appropriate Window title

													if((Number = ScanDir(Entry, &NotifyList, NotifySig, EAC, EAB, DOSBase)) != -1)
													{
														if(SendMail)
															Number	= Number >> 1;

														if(ZeroClose && !Number && NotifyWindow)
														{
															CloseWindow(NotifyWindow);
															NotifyWindow	= NULL;
															NotifyWinSig	= 0;
														}
														else
															SPrintF(WinTitle, "%ld Message(s) queued", Number);
													}
													else
														strcpy(WinTitle, "Couldn`t get information");

														// Update window?

													if(NotifyWindow || Number > 0 || Initial)
													{
															// Window not opened yet?

														if(!NotifyWindow)
														{
															struct	Screen	*WB	= LockPubScreen((OnlyWB ? "Workbench" : NULL));

																// Open window

															if(WB)
															{
																if((NotifyWindow = OpenWindowTags(NULL,
																	WA_Left,	WinLeft,
																	WA_Top,		WinTop,
																	WA_Width,	(WB->RastPort.TxWidth * 40),
																	WA_Height,	(WB->RastPort.TxHeight + 3),
																	WA_Flags,	WFLG_DRAGBAR|WFLG_CLOSEGADGET|WFLG_DEPTHGADGET,
																	WA_IDCMP,	IDCMP_CHANGEWINDOW|IDCMP_CLOSEWINDOW,
																	WA_PubScreen,	WB,
																	WA_Title,	WinTitle,
																TAG_DONE)))
																	NotifyWinSig	= 1 << NotifyWindow->UserPort->mp_SigBit;

																UnlockPubScreen(NULL, WB);
															}
														}
														else
															SetWindowTitles(NotifyWindow, WinTitle, (char *)~0);

														Initial = FALSE;
													}
												}

													// Signal from window?

												if(MySig & NotifyWinSig)
												{
													struct	IntuiMessage	*Msg;
													BOOL			Close	= FALSE;

													while((Msg = (struct IntuiMessage *)GetMsg(NotifyWindow->UserPort)))
													{
														if(Msg->Class == IDCMP_CLOSEWINDOW)
															Close	= TRUE;
														else if(Msg->Class == IDCMP_CHANGEWINDOW)
														{
															WinLeft = NotifyWindow->LeftEdge;
															WinTop	= NotifyWindow->TopEdge;
														}

														ReplyMsg((struct Message *)Msg);
													}

													if(Close)
													{
														CloseWindow(NotifyWindow);
														NotifyWindow	= NULL;
														NotifyWinSig	= 0;
													}
												}
											}

												// Close window

											if(NotifyWindow)
												CloseWindow(NotifyWindow);

												// Kill Notifications

											while((Notify = (struct NotifyNode *)RemHead(&NotifyList)))
											{
												EndNotify(&Notify->Notify);
												FreeVec(Notify);
											}

												// End Dir Notification

											EndNotify(&DirNotify);
										}
										else
											FPuts(Output(), "MailQueue : Couldn`t launch Notification\n");

										FreeSignal(NotifySig);
									}
									else
										FPuts(Output(), "MailQueue : Couldn`t allocate Signal\n");

									FreeArgs(RDArgs);
								}
								else
									PrintFault(IoErr(), "MailQueue ");
							}
							else
								FPuts(Output(), "MailQueue : SMTPSPOOLDIR settings not found\n");

							FreeVec(Args);
						}
						else
							PrintFault(ERROR_NO_FREE_STORE, "MailQueue ");

						FreeVec(EAB);
					}
					else
						PrintFault(ERROR_NO_FREE_STORE, "MailQueue ");

					FreeDosObject(DOS_EXALLCONTROL, EAC);
				}
				else
					PrintFault(ERROR_NO_FREE_STORE, "MailQueue ");

				CloseLibrary((struct Library *)NetSupportBase);
			}
			else
				FPuts(Output(), "MailQueue : Couldn`t open netsupport.library\n");

			CloseLibrary((struct Library *)IntuitionBase);
		}
		else
			FPuts(Output(), "MailQueue : Couldn`t open intuition.library\n");

		CloseLibrary((struct Library *)DOSBase);
	}

	if(Error)
		return(10);
	else
		return(0);
}








/**********************************************************************/
/*            Scan the directory for the "D.#?" files                 */
/**********************************************************************/
static LONG ScanDir(char *DirName, struct List *NotifyList, ULONG NotifySig, struct ExAllControl *EAC, struct ExAllData *EAB, struct DosLibrary *DOSBase)
{
	struct	NotifyNode	*Notify;
	struct	ExAllData	*EAD;
	BPTR			DirLock;
	char			Pattern[32];
	LONG			Number		= -1;
	BOOL			Scanning;


		// First stop all pending notifications

	while((Notify = (struct NotifyNode *)RemHead(NotifyList)))
	{
		EndNotify(&Notify->Notify);
		FreeVec(Notify);
	}

		// Create pattern for files (Message files start with "D.")

	if(ParsePatternNoCase("D.#?", Pattern, 32) != -1)
	{
			// Scan directory

		if((DirLock = Lock(DirName, ACCESS_READ)))
		{
			EAC->eac_LastKey	= 0;
			EAC->eac_MatchString	= Pattern;
			EAC->eac_MatchFunc	= 0;

			Number		= 0;

			do
			{
				Scanning	= ExAll(DirLock, EAB, (16 * sizeof(struct ExAllControl)), ED_NAME, EAC);

				if(!Scanning && (IoErr() != ERROR_NO_MORE_ENTRIES))
					Number	= -1;

				if(EAC->eac_Entries == 0)
					Scanning	= FALSE;
				else if(Number != -1)
				{
					EAD	= EAB;

					do
					{
							// Create new notification for file found

						if((Notify = AllocVec(sizeof(struct NotifyNode), MEMF_CLEAR)))
						{
							Number++;

								// Create filename with path

							NameFromLock(DirLock, Notify->Name, 256);
							AddPart(Notify->Name, EAD->ed_Name, 256);

								// Set up notification on file

							Notify->Notify.nr_Name				= Notify->Name;
							Notify->Notify.nr_Flags				= NRF_SEND_SIGNAL;
							Notify->Notify.nr_stuff.nr_Signal.nr_Task	= FindTask(NULL);
							Notify->Notify.nr_stuff.nr_Signal.nr_SignalNum	= NotifySig;

								// Start notification

							if(!StartNotify(&Notify->Notify))
							{
									// Error -> Abort and free memory of node

								Number	= -1;
								FreeVec(Notify);
								break;
							}
							else
							{
									// Add node to list

								AddTail(NotifyList, (struct Node *)Notify);
							}
						}
						else
							Number	= -1;

						EAD	= EAD->ed_Next;
					} while(EAD && Number != -1);
				}
			} while(Scanning && Number != -1);

			UnLock(DirLock);
		}
	}

		// Error?!?! Kill requests

	if(Number == -1)
	{
		while((Notify = (struct NotifyNode *)RemHead(NotifyList)))
		{
			EndNotify(&Notify->Notify);
			FreeVec(Notify);
		}
	}

	return(Number);
}




/**********************************************************************/
/*			   My special sprintf			      */
/**********************************************************************/
static void __stdargs SPrintF(STRPTR buffer, STRPTR formatString,...)
{
	va_list varArgs;
	va_start(varArgs,formatString);
	RawDoFmt(formatString,varArgs,(void (*)())(void (*))"\x16\xC0\x4E\x75",buffer);
	va_end(varArgs);
}
