home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Otherware
/
Otherware_1_SB_Development.iso
/
mac
/
util
/
comm
/
news102.sit
/
NewsWatcher
/
source
/
nntplow.c
< prev
next >
Wrap
Text File
|
1991-04-03
|
16KB
|
704 lines
/*----------------------------------------------------------
#
# NewsWatcher - Macintosh NNTP Client Application
#
# Written by Steven Falkenburg
# ⌐1990 Apple Computer, Inc.
#
#-----------------------------------------------------------
#
# nntplow.c
#
# The nntplow code module contains the network code which
# communicates with the remote NNTP server to get/post
# news articles or retrieve subject or group lists.
#
#-----------------------------------------------------------*/
#pragma segment lowlevel
#include "compat.h"
#ifdef PROTOS
#include <Types.h>
#include <Memory.h>
#include <QuickDraw.h>
#include <OSUtils.h>
#include <CursorCtl.h>
#include <Packages.h>
#include <ToolUtils.h>
#include <Lists.h>
#include <Strings.h>
#endif
#include <string.h>
#include "nntp.h"
#include "MacTCPCommonTypes.h"
#include "TCPPB.h"
#include "netstuff.h"
#include "TCPHi.h"
#include "FTPLow.h"
#include "NNTPLow.h"
#include "miscstuff.h"
unsigned long gConnID = 0; /* connection id for nntp connection */
short gNumGroups; /* number of newsgroups */
TGroup *gGroupList; /* pointer to first group */
static TGroup **groupHandle; /* handle to first group */
unsigned long gNNTPAddress; /* address of NNTP server */
OSErr GrowDataStructure(void);
/* GrowDataStructure dynamically grows the group data structure, re-assigning
gGroupList after locking the handle.
*/
OSErr GrowDataStructure(void)
{
Size prevSize;
OSErr err;
HUnlock((Handle)groupHandle);
prevSize = GetHandleSize((Handle)groupHandle);
MySetHandleSize((Handle)groupHandle,prevSize+sizeof(TGroup));
if ((err = MyMemErr())!=noErr) {
MoveHHi((Handle)groupHandle);
HLock((Handle)groupHandle);
gGroupList = *groupHandle;
return err;
}
MoveHHi((Handle)groupHandle);
HLock((Handle)groupHandle);
gGroupList = *groupHandle;
}
/* StartNNTP initializes MacTCP, opens the NNTP connection, and
gets the list of groups from the NNTP server.
*/
OSErr StartNNTP(void)
{
OSErr err;
StatusWindow("Getting NNTP Address...",-1);
GetConfiguration();
StatusWindow("Opening TCP Driver...",-1);
GiveTime(0);
if ((err = InitNNTP()) == noErr &&
(err = FTPInit((ProcPtr)StatusProc)) == noErr &&
(StatusWindow("Opening Connection...",-1)) &&
(err = OpenNewsConnection(gNNTPAddress)) == noErr &&
(StatusWindow("Getting Groups From Net...",0)) &&
(err = GetGroupList(&gNumGroups)) == noErr &&
(StatusWindow("Getting Groups From Net...",100)))
;
GiveTime(0);
return err;
}
/* InitNNTP is called to initialize all drivers/etc necessary for
operation and maintenence of the NNTP connection.
*/
OSErr InitNNTP(void)
{
groupHandle = (TGroup **)MyNewHandle(sizeof(TGroup));
if (MyMemErr() != noErr)
return MyMemErr();
HLock((Handle)groupHandle);
gGroupList = *groupHandle;
return InitNetwork();
}
/* ResetConnection closes and re-opens the NNTP connection. This routine
is called when a network error occurs.
*/
OSErr ResetConnection(void)
{
OSErr err;
Str255 tmpStr;
extern Boolean gCancel;
gCancel = false;
AbortNewsConnection();
StatusWindow("Re-establishing Connection...",-1);
if ((err = InitNetwork()) == noErr &&
(err = FTPInit((ProcPtr)StatusProc)) == noErr &&
(err = OpenNewsConnection(gNNTPAddress)) == noErr)
;
else {
NumToString(err,tmpStr);
ParamText("\pConnection could not established.",tmpStr,"\p","\p");
CautionAlert(kErrDlg,nil);
}
return err;
}
/* GetHello gets the initial status back from an open NNTP connection
to determine whether or not the connection was opened sucessfully.
*/
OSErr GetHello(void)
{
OSErr err;
Ptr data;
unsigned short length;
data = MyNewPtr(kBufLen);
if (MyMemErr() != noErr)
return MyMemErr();
length = kBufLen;
err = RecvData(gConnID,data,&length,true);
if (*data != '2')
err = -1;
MyDisposPtr(data);
return err;
}
/* OpenNewsConnection opens the conneciton to the remote NNTP server.
*/
OSErr OpenNewsConnection(unsigned long address)
{
unsigned long recvLen;
OSErr err;
recvLen = kBufLen;
if ((err = CreateStream(&gConnID,recvLen)) != noErr)
return err;
if ((err = OpenConnection(gConnID,address,kNNTPPort,10)) == noErr)
err = GetHello();
return err;
}
/* CloseConnection closes the connection to the NNTP server. This is normally
called when the program is terminating.
*/
OSErr CloseNewsConnection(void)
{
OSErr err;
StatusWindow("Closing NNTP Connection...",-1);
err = CloseConnection(gConnID);
err = ReleaseStream(gConnID);
return err;
}
/* AbortNewsConnection forcibly closes a connection to the NNTP server, usually
before resetting the connection.
*/
OSErr AbortNewsConnection(void)
{
StatusWindow("Aborting NNTP Connection...",-1);
AbortConnection(gConnID);
return ReleaseStream(gConnID);
}
/* GetGroupList gets the list of all known newsgroups from the NNTP server.
*/
OSErr GetGroupList(short *numGroups)
{
OSErr err;
char *current;
char curGroup[256];
char *data;
short nameLen = 0;
unsigned short length = (unsigned short) kBufLen;
Boolean done = false,start = true;
short state = cNormal;
Str255 sendData[2];
*numGroups = 0;
nameLen = 0;
strcpy((char *)sendData[0],"LIST");
strcpy((char *)sendData[1],CRLF);
if ((err = SendMultiData(gConnID,sendData,2,true)) != noErr) {
ResetConnection();
return err;
}
data = MyNewPtr(kBufLen);
if (MyMemErr() != noErr)
return MyMemErr();
while ( !done && ((err = RecvData(gConnID,data,&length,true)) == noErr)) {
for (current = data; (current - data) < length; current++) {
switch (*current) {
case CR:
if (state == cLastDot)
state = cLastDotCR;
else
state = cLastCR;
break;
case LF:
if (state == cLastCR) {
state = cLastLF;
curGroup[nameLen] = '\0';
if ((err = GrowDataStructure()) != noErr)
return err;
if (ParseGroup(curGroup,nameLen,&gGroupList[*numGroups])) {
start = false;
(*numGroups)++;
StatusWindow("Getting Groups From Net...",(short)1+(((float)(*numGroups)/1100.0)*100));
}
else if (start==true && curGroup[0]!='2') {
ResetConnection();
done=true;
}
nameLen = 0;
GiveTime(0);
}
else if (state == cLastDotCR) {
done = true;
state = cLastDotCRLF;
}
break;
case '.':
if (state == cLastLF)
state = cLastDot;
else {
state = cNormal;
curGroup[nameLen++] = *current;
}
break;
default:
state = cNormal;
curGroup[nameLen++] = *current;
break;
}
}
length = (unsigned short) kBufLen;
}
MyDisposPtr(data);
SetCursor(&QDARROW);
if (err != noErr)
ResetConnection();
return noErr;
}
/* ParseGroup parses out the name of a specific group from the receive buffer.
This is called once per line for the buffer received after a 'LIST' command.
*/
Boolean ParseGroup(char *groupString,short groupLen,TGroup *groupStorage)
{
char tmpStr[256],tmpStore[512];
char *current,*tmpCurrent;
if (groupLen > 0 &&
!(*groupString >= '0' && *groupString <= '9') &&
*groupString != '.') {
if (MyMemErr() != noErr)
return false;
for (current = groupString,tmpCurrent=tmpStore;
*current != ' ' && *current;
*tmpCurrent++ = *current++);
*tmpCurrent = '\0';
strncpy(groupStorage->name,tmpStore,kGNameLen);
groupStorage->name[kGNameLen-1] = '\0';
for (current++,tmpCurrent=tmpStr;
*current != ' ' && *current;
*tmpCurrent++ = *current++);
*tmpCurrent = '\0';
StringToNum((StringPtr)c2pstr(tmpStr),&groupStorage->lastMess);
if (groupStorage->lastMess == 0)
groupStorage->lastMess++;
for (current++,tmpCurrent=tmpStr;
*current != ' ' && *current;
*tmpCurrent++ = *current++);
*tmpCurrent = '\0';
StringToNum((StringPtr)c2pstr(tmpStr),&groupStorage->firstMess);
if (groupStorage->firstMess == 0)
groupStorage->firstMess++;
groupStorage->status = *(current+1);
if (groupStorage->status == 'n')
return false;
return true;
}
else
return false;
}
/* GetMessages returns the list of subjects from a range of messages in a
specific newsgroup. This routine issues the command 'XHDR <first>-<last>'
to the remote NNTP server.
*/
OSErr GetMessages(char *newsGroup,long first,long last,TSubject *subjects,long *numSubjects,char *hdrName)
{
Ptr data;
Boolean done = false,start;
OSErr err;
unsigned short length;
char *current,*current2,*current3,curSubject[1024],numberStr[256];
short nameLen;
short state = cNormal;
char tmpStr[256];
long articleNumber;
Str255 sendData[7];
start = true;
nameLen = 0;
*numSubjects = 0;
data = MyNewPtr(kBufLen);
if (MyMemErr() != noErr)
return MyMemErr();
/* send GROUP <groupname> command */
GiveTime(0);
strcpy((char *)sendData[0],"GROUP ");
strcpy((char *)sendData[1],newsGroup);
strcpy((char *)sendData[2],CRLF);
if ((err = SendMultiData(gConnID,sendData,3,true)) != noErr) {
ResetConnection();
return err;
}
GiveTime(0);
length = (unsigned short) kBufLen;
if ((err = RecvData(gConnID,data,&length,true)) != noErr) {
ResetConnection();
return err;
}
if (*data != '2') {
ResetConnection();
return -1;
}
/* send XHDR <hdrName> <first>-<last> command */
GiveTime(0);
strcpy((char *)sendData[0],"XHDR ");
strcpy((char *)sendData[1],hdrName);
strcpy((char *)sendData[2]," ");
NumToString(first,(StringPtr)tmpStr);
p2cstr(tmpStr);
strcpy((char *)sendData[3],tmpStr);
strcpy((char *)sendData[4],"-");
NumToString(last,(StringPtr)tmpStr);
p2cstr(tmpStr);
strcpy((char *)sendData[5],tmpStr);
strcpy((char *)sendData[6],CRLF);
if ((err = SendMultiData(gConnID,sendData,7,true)) != noErr) {
ResetConnection();
return err;
}
GiveTime(0);
while ( !done && ((err = RecvData(gConnID,data,&length,true)) == noErr)) {
for (current = data; (current - data) < length; current++) {
switch (*current) {
case CR:
if (state == cLastDot)
state = cLastDotCR;
else
state = cLastCR;
break;
case LF:
if (state == cLastCR) {
state = cLastLF;
if (nameLen > 250)
nameLen = 250;
curSubject[nameLen] = '\0';
if (!start && curSubject[0] >= '0' && curSubject[0] <= '9' && (*numSubjects <= (last-first))) {
subjects[*numSubjects].name = (char *) MyNewPtr(nameLen+2);
if (MyMemErr() != noErr)
return MyMemErr();
strncpy(subjects[*numSubjects].name+1,curSubject,nameLen+1);
*(subjects[*numSubjects].name) = ' ';
for (current2 = numberStr,current3 = curSubject;
*current3 != ' ' && *current3;
*current2++ = *current3++)
;
*current2 = '\0';
StringToNum((StringPtr)c2pstr(numberStr),&articleNumber);
subjects[*numSubjects].number = articleNumber;
subjects[*numSubjects].read = false;
(*numSubjects)++;
StatusWindow("Getting Subject List...",(short)1+(((float)(1+*numSubjects)/(float)(last-first))*100));
GiveTime(0);
}
else if (start && curSubject[0]!='2') {
SysBeep(1);
done = true;
}
else
start = false;
nameLen = 0;
} else if (state == cLastDotCR) {
state = cLastDotCRLF;
done = true;
}
break;
case '.':
if (state == cLastLF)
state = cLastDot;
else {
state = cNormal;
curSubject[nameLen++] = *current;
}
break;
default:
state = cNormal;
curSubject[nameLen++] = *current;
break;
}
}
length = (unsigned short) kBufLen;
}
MyDisposPtr(data);
SetCursor(&QDARROW);
if (err != noErr)
ResetConnection();
return noErr;
}
/* GetArticle gets the full text from one article from the NNTP server.
This text is returned in a buffer which the routine allocates.
*/
OSErr GetArticle(char *newsGroup,char *article,char **text,long *retLength,long maxLength)
{
OSErr err;
Ptr data;
unsigned short length;
char *current;
Boolean done,start;
short state = cNormal;
long totalLength = 0;
Str255 sendData[3];
Handle txtHndl;
long curMaxLen;
txtHndl = MyNewHandle(kMaxLength);
if (MyMemErr() != noErr)
return MyMemErr();
curMaxLen = kMaxLength;
HLock(txtHndl);
*text = *txtHndl;
start = true;
done = false;
data = MyNewPtr(kBufLen);
if (MyMemErr() != noErr)
return MyMemErr();
if (newsGroup) {
/* send GROUP <groupname> command */
GiveTime(0);
strcpy((char *)sendData[0],"GROUP ");
strcpy((char *)sendData[1],newsGroup);
strcpy((char *)sendData[2],CRLF);
if ((err = SendMultiData(gConnID,sendData,3,true)) != noErr) {
ResetConnection();
return err;
}
GiveTime(0);
length = (unsigned short) kBufLen;
if ((err = RecvData(gConnID,data,&length,true)) != noErr) {
ResetConnection();
return err;
}
if (*data != '2') {
ResetConnection();
return -1;
}
}
strcpy((char *)sendData[0],"ARTICLE ");
strcpy((char *)sendData[1],article);
strcpy((char *)sendData[2],CRLF);
if ((err = SendMultiData(gConnID,sendData,3,true)) != noErr) {
ResetConnection();
return err;
}
length = kBufLen;
while ( !done && ((err = RecvData(gConnID,data,&length,true)) == noErr)) {
for (current = data; (current - data) < length; current++) {
if (totalLength >= curMaxLen && totalLength < (maxLength-kMaxLength)) {
curMaxLen += kMaxLength;
HUnlock(txtHndl);
MySetHandleSize(txtHndl,curMaxLen);
if ((err = MyMemErr()) != noErr) {
MyDisposHandle(txtHndl);
MyDisposPtr(data);
return err;
}
HLock(txtHndl);
*text = *txtHndl;
}
switch (*current) {
case CR:
if (state == cLastDot)
state = cLastDotCR;
else
state = cLastCR;
if (!start) {
if (totalLength < maxLength)
(*text)[totalLength++] = *current;
}
break;
case LF:
if (state == cLastCR) {
start = false;
state = cLastLF;
}
else if (state == cLastDotCR) {
state = cLastDotCRLF;
done = true;
}
break;
case '.':
if (state == cLastLF)
state = cLastDot;
else if (!start) {
state = cNormal;
if (totalLength < maxLength)
(*text)[totalLength++] = *current;
}
break;
default:
if (!start) {
state = cNormal;
if (totalLength < maxLength)
(*text)[totalLength++] = *current;
}
else if (data[0]!='2')
done = true;
break;
}
}
length = (unsigned short) kBufLen;
GiveTime(0);
}
SetCursor(&QDARROW);
*retLength = totalLength;
MyDisposPtr(data);
HUnlock(txtHndl);
*text = MyNewPtr(totalLength);
if ((err = MyMemErr()) != noErr) {
MyDisposHandle(txtHndl);
return err;
}
HLock(txtHndl);
BlockMove(*txtHndl,*text,totalLength);
HUnlock(txtHndl);
MyDisposHandle(txtHndl);
if (err != noErr)
ResetConnection();
return noErr;
}
/* SendNNTP posts a news article to a newsgroup using the 'POST' command
on the NNTP server.
*/
Boolean SendNNTP(char *text,unsigned short tLength)
{
extern unsigned long gConnID;
Ptr data;
unsigned short length;
Boolean result;
char commStr[256];
strcpy(commStr,"POST");
strcat(commStr,CRLF);
if (SendData(gConnID,commStr,6,false) != noErr)
return false;
data = MyNewPtr(256);
if (MyMemErr() != noErr)
return false;
strcpy(commStr,CRLF);
strcat(commStr,".");
strcat(commStr,CRLF);
length = 200;
if (RecvData(gConnID,data,&length,false) == noErr &&
strncmp(data,"3",1)==0 &&
SendData(gConnID,text,tLength,false) == noErr &&
SendData(gConnID,commStr,5,false) == noErr &&
(length = 200, true) &&
RecvData(gConnID,data,&length,false) == noErr &&
strncmp(data,"2",1)==0)
result = true;
else {
result = false;
}
MyDisposPtr(data);
return true;
}