home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Otherware
/
Otherware_1_SB_Development.iso
/
mac
/
util
/
comm
/
news102.sit
/
NewsWatcher
/
source
/
netstuff.c
< prev
next >
Wrap
Text File
|
1991-04-03
|
14KB
|
623 lines
/*----------------------------------------------------------
#
# NewsWatcher - Macintosh NNTP Client Application
#
# Written by Steven Falkenburg
# ⌐1990 Apple Computer, Inc.
#
#-----------------------------------------------------------
#
# netstuff.c
#
# The netstuff code module contains several routines which
# handle network specific tasks of the program. Among
# these are user authentication, and high-level file xfer
# calls.
#
#-----------------------------------------------------------*/
#pragma segment netstuff
#include "compat.h"
#include <String.h>
#include <StdIO.h>
#ifdef PROTOS
#include <Types.h>
#include <TextEdit.h>
#include <Dialogs.h>
#include <Files.h>
#include <Memory.h>
#include <Errors.h>
#include <Events.h>
#include <Fonts.h>
#include <Packages.h>
#include <Lists.h>
#include <ToolUtils.h>
#include <Strings.h>
#endif
#include "MacTCPCommonTypes.h"
#include "AddressXLation.h"
#include "TCPPB.h"
#include "nntp.h"
#include "netstuff.h"
#include "miscstuff.h"
#include "userint.h"
#include "newsprocess.h"
#include "TCPHi.h"
#include "FTPLow.h"
#include "NNTPLow.h"
#include "SMTPLow.h"
static char gPwStr[256]; /* the user's password */
unsigned long gSMTPAddress; /* the ip address of the SMTP host */
/* local protos */
pascal Boolean PasswordFilter(DialogPtr theDialog,EventRecord *theEvent,short *itemHit);
pascal void DNRResultProc(struct hostInfo *hInfoPtr,char *userDataPtr);
/* ChangeServerAddress is called to get a new name/address for the news
or main server. The new address will be stored in the prefs file.
*/
void ChangeServerAddress(Boolean forNews)
{
DialogPtr theDlg;
short iType;
Handle iHndl;
Rect iBox;
Str255 serverName;
short item;
extern Boolean gDone;
extern TPrefRec gPrefs;
if (forNews) {
if (gPrefs.newsServerName[0] == '\0') {
SysBeep(1);
BlockMove(kNNTPName,serverName,kNNTPNameLen);
}
else
BlockMove(gPrefs.newsServerName,serverName,(gPrefs.newsServerName)[0]+1);
theDlg = GetNewDialog(kNewsServerAddrID,nil,(WindowPtr)-1);
}
else {
if (gPrefs.mailServerName[0] == '\0') {
SysBeep(1);
BlockMove(kSMTPName,serverName,kSMTPNameLen);
}
else
BlockMove(gPrefs.mailServerName,serverName,(gPrefs.mailServerName)[0]+1);
theDlg = GetNewDialog(kMailServerAddrID,nil,(WindowPtr)-1);
}
OutlineOK(theDlg);
GetDItem(theDlg,3,&iType,&iHndl,&iBox);
SetIText(iHndl,serverName);
SelIText(theDlg,3,0L,32767L);
do {
ModalDialog(CmdKeyFilter,&item);
} while (item!=okButton && item!=cancelButton);
GetIText(iHndl,serverName);
DisposDialog(theDlg);
if (forNews) {
if (item==okButton)
BlockMove(serverName,gPrefs.newsServerName,serverName[0]+1);
}
else {
if (item==okButton)
BlockMove(serverName,gPrefs.mailServerName,serverName[0]+1);
}
if (!gDone && item==okButton) {
ParamText("\pYou must quit and restart the program for this change to take effect.","\p","\p","\p");
NoteAlert(kErrDlg,nil);
}
}
/* GetConfiguration is called to get the news/mail server ip addresses,
given their names from the preferences file.
*/
void GetConfiguration(void)
{
extern unsigned long gNNTPAddress;
extern TPrefRec gPrefs;
Str255 name;
if (gPrefs.newsServerName[0] == '\0' || gPrefs.mailServerName[0] == '\0') {
ChangeServerAddress(true);
if (gPrefs.newsServerName[0] == '\0') {
gNNTPAddress = kNNTPAddress;
return;
}
ChangeServerAddress(false);
if (gPrefs.mailServerName[0] == '\0') {
gSMTPAddress = kSMTPAddress;
return;
}
}
BlockMove(gPrefs.newsServerName,name,(gPrefs.newsServerName)[0]+1);
p2cstr((char *)name);
if (ConvertStringToAddr((char *)name,&gNNTPAddress)!=noErr)
gNNTPAddress = kNNTPAddress;
BlockMove(gPrefs.mailServerName,name,(gPrefs.mailServerName)[0]+1);
p2cstr((char *)name);
if (ConvertStringToAddr((char *)name,&gSMTPAddress)!=noErr)
gSMTPAddress = kSMTPAddress;
}
/* This is the completion routine used for name-resolver calls.
It sets the userDataPtr flag to indicate the call has completed.
*/
pascal void DNRResultProc(struct hostInfo *hInfoPtr,char *userDataPtr)
{
#pragma unused (hInfoPtr)
*userDataPtr = 0xff;
}
/* ConvertStringToAddr is a simple call to get a host's IP number, given the name
of the host.
*/
OSErr ConvertStringToAddr(char *name,unsigned long *netNum)
{
struct hostInfo hInfo;
OSErr result;
char done = 0x00;
extern Boolean gCancel;
if ((result = OpenResolver(nil)) == noErr) {
result = StrToAddr(name,&hInfo,DNRResultProc,&done);
if (result == cacheFault)
while (!done)
; /* wait for cache fault resolver to be called by interrupt */
CloseResolver();
if ((hInfo.rtnCode == noErr) || (hInfo.rtnCode == cacheFault)) {
*netNum = hInfo.addr[0];
strcpy(name,hInfo.cname);
name[strlen(name)-1] = '\0';
return noErr;
}
}
*netNum = 0;
return result;
}
/* PasswordFilter is the dialog filter used to switch all keys pressed
into bullets (Ñ) to hide the user's password from view.
*/
pascal Boolean PasswordFilter(DialogPtr theDialog,EventRecord *theEvent,short *itemHit)
{
DialogPeek dPeek;
char theChar;
short selStart,selEnd;
dPeek = (DialogPeek) theDialog;
if ((theEvent->what == keyDown) || (theEvent->what == autoKey)) {
if ((theEvent->modifiers & cmdKey) != 0) {
return CmdKeyFilter(theDialog,theEvent,itemHit);
}
if (dPeek->editField == 2) {
theChar = theEvent->message & charCodeMask;
selStart = (**(dPeek->textH)).selStart;
selEnd = (**(dPeek->textH)).selEnd;
switch (theChar) {
case 0x08: /* backspace */
if (selStart == selEnd) {
if (selStart > 0)
BlockMove((Ptr)gPwStr + selStart,(Ptr)gPwStr + selStart-1,strlen(gPwStr)-selStart+1);
}
else
BlockMove((Ptr)gPwStr + selEnd,(Ptr)gPwStr + selStart,strlen(gPwStr)-selEnd+1);
break;
case CR: /* CR */
case 0x03: /* enter */
*itemHit = okButton;
return true;
break;
case 0x09: /* tab */
case 0x1c: /* left */
case 0x1d: /* right */
case 0x1e: /* up */
case 0x1f: /* down */
return false;
break;
default:
BlockMove((Ptr)gPwStr+selEnd,(Ptr)gPwStr+selEnd+1,strlen(gPwStr)-selEnd+1);
*(gPwStr+selStart) = theChar;
theEvent->message = 0xffffff00 + 'Ñ';
return false;
break;
}
}
if ((theChar == CR) || (theChar == 0x03)) {
*itemHit = okButton;
return true;
}
}
return false;
}
/* GetUInfo displays modal dialogs for the user to enter their
login id, host, and password for authentication.
*/
Boolean GetUInfo(Str255 login,Str255 pass,Str255 host)
{
DialogPtr theDlg;
short item,iType;
Handle iHndl;
Rect iRect;
theDlg = GetNewDialog(kLoginDlg,nil,(WindowPtr)-1);
OutlineOK(theDlg);
GetDItem(theDlg,3,&iType,&iHndl,&iRect);
SetIText(iHndl,(StringPtr)c2pstr((char *)host));
GetDItem(theDlg,4,&iType,&iHndl,&iRect);
SetIText(iHndl,(StringPtr)c2pstr((char *)login));
do
ModalDialog(CmdKeyFilter,&item);
while (item != okButton && item != cancelButton);
GetDItem(theDlg,3,&iType,&iHndl,&iRect);
GetIText(iHndl,host);
GetDItem(theDlg,4,&iType,&iHndl,&iRect);
GetIText(iHndl,login);
DisposDialog(theDlg);
ParamText(host,"\p","\p","\p");
p2cstr((char *)login);
p2cstr((char *)host);
if (item == cancelButton)
return false;
if (!host[0] || !login[0])
return false;
theDlg = GetNewDialog(kPassDlg,nil,(WindowPtr)-1);
OutlineOK(theDlg);
*gPwStr = '\0';
do
ModalDialog(PasswordFilter,&item);
while (item != okButton && item != cancelButton);
DisposDialog(theDlg);
if (item == cancelButton)
return false;
strcpy(pass,gPwStr);
return true;
}
/* Authenticate is called to check the accuracy of a user's id and
password. Eventually, this procedure could be replaced with a
more secure method. Currently, FTP is used for authentication.
*/
Boolean Authenticate(void)
{
extern Boolean gAuthenticated;
extern char gPass[];
extern TPrefRec gPrefs;
unsigned long connID;
if (gAuthenticated)
return true;
if (!GetUInfo((StringPtr)gPrefs.name,(StringPtr)gPass,(StringPtr)gPrefs.host))
return false;
StatusWindow("Authenticating user...",-1);
if (FTPConnect(&connID,(char *)gPrefs.host,(char *)gPrefs.name,gPass)!=noErr) {
FTPDisconnect(connID);
CloseStatusWindow();
ParamText("\pIncorrect user information.","\p","\p","\p");
NoteAlert(kErrDlg,nil);
return false;
}
FTPDisconnect(connID);
CloseStatusWindow();
gAuthenticated = true;
return true;
}
/* Logout removes the user's password from memory.
*/
void Logout(void)
{
extern Boolean gAuthenticated;
extern char gPass[];
strcpy(gPass,"%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%");
gAuthenticated = false;
}
/* GetFromNet gets a newsrc file from the user's remote account.
*/
void GetFromNet(void)
{
extern char gPass[];
extern TPrefRec gPrefs;
unsigned long connID;
Ptr newsData;
char *current;
TwindowInfo *info;
WindowPtr window;
OSErr err;
extern TPrefRec gPrefs;
if (!Authenticate())
return;
StatusWindow("Establishing FTP Connection...",-1);
if (FTPConnect(&connID,(char *)gPrefs.host,(char *)gPrefs.name,gPass)!=noErr) {
FTPDisconnect(connID);
CloseStatusWindow();
ParamText("\pFile not retrieved successfully.","\p","\p","\p");
NoteAlert(kErrDlg,nil);
return;
}
if ((err = FTPViewFile(connID,&newsData,".newsrc"))!=noErr) {
FTPDisconnect(connID);
CloseStatusWindow();
SetCursor(&QDARROW);
ParamText("\pFile not retrieved successfully.","\p","\p","\p");
NoteAlert(kErrDlg,nil);
return;
}
CloseStatusWindow();
FTPDisconnect(connID);
NewGroupWindow("\pnewsrc");
info = (TwindowInfo *)GetWRefCon(window = FrontWindow());
BlockMove("\pnewsrc",info->diskFile,8);
info->diskVRefNum = 0;
LDoDraw(false,(ListHandle)info->data);
current = newsData;
while (*current)
ProcessLine(¤t,window);
LDoDraw(true,(ListHandle)info->data);
info->changed = false;
info->saved = false;
if (gPrefs.openWindowsZoomed)
ToggleZoom(window);
ShowWindow(window);
SetPort(window);
InvalRect(&window->portRect);
MyDisposPtr(newsData);
}
/* SendToNet sends a user group list to a user's remote account for storage
as a newsrc file.
*/
void SendToNet(void)
{
extern char gPass[];
extern TPrefRec gPrefs;
char tmpStr[256];
char *newsData;
long newsDataLen;
long first;
short offset;
TWList *current;
TGroup *theGroup;
TReadRec *read;
TwindowInfo *info;
WindowPtr window;
unsigned long connID;
window = FrontWindow();
if (!window || !IsAppWindow(window))
return;
info = (TwindowInfo *)GetWRefCon(window);
if (info->kind != cUserGroup)
return;
if (!Authenticate())
return;
StatusWindow("Converting to newsrc format...",-1);
GiveTime(0);
newsData = (char *)MyNewPtr(kMaxGroupLen);
if (MyMemErr() != noErr)
return;
newsDataLen = 0;
newsData[newsDataLen] = '\0';
/* update read lists */
for (current = info->childList; current != nil; current = current->next)
MarkReadMsgs((TwindowInfo *) GetWRefCon(current->childWindow));
/* process groups */
for (theGroup = (TGroup *)info->data2; theGroup!=nil; theGroup=theGroup->next) {
strcat(newsData,theGroup->name);
strcat(newsData,": ");
first = 1;
for (read = theGroup->read; read!=nil; read = read->next) {
if ( (read->firstRead-1 - first) >= 0) {
sprintf(tmpStr,"%lu-%lu",first,(read->firstRead-1));
if (read->next)
strcat(tmpStr,",");
strcat(newsData,tmpStr);
}
first = 1 + read->lastRead;
}
/* mark read to last message of group */
if (theGroup->lastMess >= first) {
sprintf(tmpStr,", %lu-%lu",first,theGroup->lastMess);
if (first==1)
offset=1;
else
offset=0;
strcat(newsData,tmpStr+offset);
}
strcat(newsData,CRSTR);
}
StatusWindow("Establishing FTP Connection...",-1);
GiveTime(0);
if (FTPConnect(&connID,(char *)gPrefs.host,(char *)gPrefs.name,gPass)!=noErr) {
FTPDisconnect(connID);
CloseStatusWindow();
ParamText("\pFile not sent successfully.","\p","\p","\p");
NoteAlert(kErrDlg,nil);
return;
}
if (FTPPutFile(connID,".newsrc",newsData,strlen(newsData))!=noErr) {
CloseStatusWindow();
ParamText("\pFile not sent successfully.","\p","\p","\p");
NoteAlert(kErrDlg,nil);
}
FTPDisconnect(connID);
/* newsdata has already been disposed */
}
/* DoSendMsg either posts a message or sends the message through
electronic mail (SMTP) after converting the appropriate header
fields, etc...
*/
Boolean DoSendMsg(TwindowInfo *info)
{
Handle sendText;
unsigned short length;
Boolean result = false;
long offset;
char mungeText1[256];
char mungeText2[256];
char mungeText3[256];
strcpy(mungeText1,CRSTR);
strcat(mungeText1,CRSTR);
strcpy(mungeText2,CRSTR);
strcat(mungeText2,"From");
strcpy(mungeText3,CRSTR);
strcat(mungeText3,">From");
if (!CheckHeader(info)) {
ParamText("\pInvalid sender name! Can't send message.","\p","\p","\p");
StopAlert(kErrDlg,nil);
return false;
}
StatusWindow("Sending Message...",-1);
GiveTime(0);
sendText = (Handle) TEGetText((TEHandle)info->data);
length = (**((TEHandle)info->data)).teLength;
if (MyHandToHand(&sendText) != noErr)
return false;
MySetHandleSize(sendText,(long)length);
if (MyMemErr() != noErr)
return false;
/* remove all "From"s at start of lines */
for (offset = Munger(sendText,0L,mungeText1,2L,nil,0L);
offset>=0;
offset = Munger(sendText,offset,mungeText2,5L,mungeText3,6L))
length++;
length--;
/* convert all "." to "," at start of lines */
if (**sendText=='.')
**sendText = ',';
strcpy(mungeText1,CRSTR);
strcat(mungeText1,".");
strcat(mungeText1,CRSTR);
strcpy(mungeText2,CRSTR);
strcat(mungeText2,",");
strcat(mungeText2,CRSTR);
for (offset = 0; offset>=0; offset = Munger(sendText,offset,mungeText1,3L,mungeText2,3L))
;
/* convert CR to CRLF */
for (offset = 0; offset>=0; offset = Munger(sendText,offset,CRSTR,1L,CRLF,2L))
length++;
length--;
HLock(sendText);
/* send message */
switch (info->kind) {
case cSendMessage:
result = SendSMTP((char *)*sendText,length);
break;
case cPostMessage:
result = SendNNTP((char *)*sendText,length);
break;
}
MyDisposHandle(sendText);
CloseStatusWindow();
if (result == false) {
ParamText("\pMessage not sent successfully.","\p","\p","\p");
CautionAlert(kErrDlg,nil);
}
else
info->changed = false;
return result;
}