This is NNTP.m in view mode; [Download] [Up]
#import "mapfile.h"
#import "NNTP.h"
#import "next2iso.tab"
#import "iso2next.tab"
#import "Alexandra.h"
#import <string.h>
#import <stdlib.h>
#import <sys/file.h>
#import <sys/uio.h>
#import <sys/types.h>
#import <sys/socket.h>
#import <netdb.h>
#import <netinet/in.h>
#import <time.h>
#import <sys/time.h>
#import <pwd.h>
#import "readline.h"
#import "response_codes.h"
#import "descriptors.h"
#import "headerfields.h"
#import <objc/Storage.h>
#import <misckit/MiscAppDefaults.h>
#include "instr.h"
#define NNTP_LIST_END(s) ((s)[0]=='.' && ((s)[1]=='\0' || (s)[1]=='\r' || (s)[1]==' '))
@implementation NNTP
int makeTimeTag(char *aTimetag)
{
struct timeval tp;
struct timezone tzp;
struct tm *tv;
gettimeofday(&tp, &tzp);
tv = gmtime(&tp.tv_sec);
sprintf(aTimetag,"%02d%02d%02d %02d%02d%02d GMT", tv->tm_year, tv->tm_mon+1, tv->tm_mday, tv->tm_hour,tv->tm_min,tv->tm_sec);
return 0;
}
- init
{
[super init];
nntpHostName =NULL;
handling_timeout=FALSE;
canPost=FALSE;
statusLine=NULL;
return self;
}
- writeTimeTagToDefaultDB
{
char timetag[20];
char *buf;
if(nntpHostName!=NULL){
makeTimeTag(timetag);
buf= (char *)malloc((strlen(nntpHostName)+15)*sizeof(char));
sprintf(buf,"NewGroups %s",nntpHostName);
[NXApp setDefault:buf to:timetag];
free(buf);
}
return self;
}
- (const char *)timeTag
{
char *buf1;
const char *buf2;
buf1=(char *)malloc((strlen(nntpHostName)+15)*sizeof(char));
sprintf(buf1,"NewGroups %s",nntpHostName);
buf2=[NXApp defaultValue:buf1];
free(buf1);
return buf2;
}
- openServerNamed:(const char *)serverName
{
struct servent *nntpEnt;
struct protoent *nntpProtoEnt;
struct hostent *nntpHost;
struct sockaddr_in nntpServer;
int statusCode;
char inCodeText[BUFFER_SIZE];
BOOL notUseNov;
int stype;
char *xuser,*xpasswd;
const char *buf;
if(nntpHostName!=NULL){
if(serverName!=nntpHostName){
free(nntpHostName);
nntpHostName = NXCopyStringBuffer(serverName);
}
}
else
nntpHostName = NXCopyStringBuffer(serverName);
// store nntp hostname for reconnecting
if ((nntpEnt = getservbyname("nntp", "tcp")) == NULL) {
NXRunAlertPanel("ALEXANDRA","Cannot find nntp service in 'services' database.",NULL,NULL,NULL);
return nil;
}
if ((nntpProtoEnt = getprotobyname(nntpEnt->s_proto)) == NULL) {
NXRunAlertPanel("ALEXANDRA","Cannot lookup protocol type.",NULL,NULL,NULL);
return nil;
}
if ((readSocket = socket(AF_INET, SOCK_STREAM,
nntpProtoEnt->p_proto))== -1) {
NXRunAlertPanel("ALEXANDRA","Cannot create socket to news server.",NULL,NULL,NULL);
return nil;
}
if ((nntpHost = gethostbyname((char *)serverName)) == NULL) {
NXRunAlertPanel("ALEXANDRA","Cannot find address of host %s.",NULL,NULL,NULL, serverName);
return nil;
}
nntpServer.sin_family = nntpHost->h_addrtype;
bcopy(nntpHost->h_addr, &nntpServer.sin_addr, nntpHost->h_length);
nntpServer.sin_port = nntpEnt->s_port;
if ((connect(readSocket, (struct sockaddr *) &nntpServer,
sizeof(nntpServer))) == -1) {
NXRunAlertPanel("ALEXANDRA","Cannot connect to news server on %s.",NULL,NULL,NULL, serverName);
return nil;
}
writeSocket=dup(readSocket);
nntpIn = fdopen(readSocket, "r");
nntpOut = fdopen(writeSocket,"w");
if(fgets(inCodeText,sizeof(inCodeText),nntpIn)==NULL){
NXRunAlertPanel("ALEXANDRA","Unable to open server socket.",NULL,NULL,NULL);
return nil;
}
statusCode=atoi(inCodeText);
switch (statusCode) {
case OK_CANPOST:
case OK_NOPOST:
canPost = (statusCode == OK_CANPOST);
if(!canPost)
NXLogError("You are not allowed to post");
break;
default:
NXRunAlertPanel("ALEXANDRA","News server on %s responded incorrectly.",NULL,NULL,NULL, serverName);
return nil;
break;
}
echoSocket=[NXApp defaultBoolValue:"EchoSocket"];
[self issueCommand:"mode reader"];
statusCode=[self issueCommand:"xover"];
if(statusCode==-1) return self;
novSupported=(statusCode==ERR_NCING);
sprintf(inCodeText,"DoNotUseNov %s",nntpHostName);
notUseNov=[NXApp defaultBoolValue:inCodeText];
if((notUseNov==YES)||(novSupported==FALSE))
novSupported=FALSE;
else
novSupported=TRUE;
sprintf(inCodeText,"DoNotPrefetchFrom %s",nntpHostName);
doNotPrefetchFROM=[NXApp defaultBoolValue:inCodeText];
sprintf(inCodeText,"DoNotPrefetchMsgid %s",nntpHostName);
doNotPrefetchMSGID=[NXApp defaultBoolValue:inCodeText];
sprintf(inCodeText,"DoNotPrefetchRefs %s",nntpHostName);
doNotPrefetchREFS=[NXApp defaultBoolValue:inCodeText];
sprintf(inCodeText,"DoNotPrefetchDate %s",nntpHostName);
doNotPrefetchDATE=[NXApp defaultBoolValue:inCodeText];
sprintf(inCodeText,"DoNotPrefetchLines %s",nntpHostName);
doNotPrefetchLINES=[NXApp defaultBoolValue:inCodeText];
if(!novSupported){
sprintf(inCodeText,"SortType %s",nntpHostName);
stype=[NXApp defaultIntValue:inCodeText];
if(((stype==SORT_BY_DATE)&&doNotPrefetchDATE)||
((stype==SORT_BY_REAL_NAME)&&doNotPrefetchFROM)){
sprintf(inCodeText,"SortType %s",nntpHostName);
[NXApp setDefault:inCodeText toInt:SORT_BY_NUMBER];
}
}
//AUTHENTICATION
sprintf(inCodeText,"Authuser %s",serverName);
buf=[NXApp defaultValue:inCodeText];
if(!buf)
buf="";
xuser=NXCopyStringBuffer(buf);
sprintf(inCodeText,"Authpasswd %s",serverName);
buf=[NXApp defaultValue:inCodeText];
if(!buf)
buf="";
xpasswd=NXCopyStringBuffer(buf);
if(*xuser && *xpasswd){
sprintf(inCodeText,"authinfo user %s",xuser);
statusCode=[self issueCommand: inCodeText];
if(statusCode==NEED_AUTHDATA){
sprintf(inCodeText,"authinfo pass %s",xpasswd);
statusCode=[self issueCommand:inCodeText];
if(statusCode!=OK_AUTH)
NXRunAlertPanel("ALEXANDRA","NNTP command failed (status %d). Authorization user %s/pass %s rejected. Access to host %s, if allowed, may be limited", NULL,
NULL,NULL, statusCode, xuser, xpasswd, nntpHostName);
}
else{
NXRunAlertPanel("ALEXANDRA","NNTP command failed (status %d). Auth for user %s rejected. Access to host %s, if allowed, may be limited",
NULL,NULL,NULL, statusCode, xuser, nntpHostName);
}
}
free(xuser);
free(xpasswd);
return self;
}
- (const char *)serverName
{
return nntpHostName;
}
- reconnectServer
{
if(nntpIn!=NULL) fclose(nntpIn);
if(nntpOut!=NULL) fclose(nntpOut);
if (nntpHostName == NULL) {
return nil;
}
return [self openServerNamed:nntpHostName];
}
- free
{
if(nntpOut!=NULL){
if(fprintf(nntpOut,"quit\r\n")==0)
fflush(nntpOut);
fclose(nntpOut);
}
if(nntpIn!=NULL) fclose(nntpIn);
free(nntpHostName);
if(statusLine!=NULL)
free(statusLine);
return [super free];
}
- (BOOL)canPost
{
return canPost;
}
- (BOOL)usesNov
{
return novSupported;
}
- closeServerDelayed
{
[mainWindowController perform:@selector(freeAndClose:) with:self afterDelay:0.0 cancelPrevious:FALSE];
fclose(nntpIn);
fclose(nntpOut);
return self;
}
- (char *)getNNTPLine
{
char *buffer;
int n;
buffer=readline(nntpIn);
if(buffer==NULL){
[self closeServerDelayed];
EM_ERROR(ENNTPUnexpectedSocketClose,NULL,NULL);
return NULL;
}
n = strlen(buffer);
if(echoSocket)
printf("%s",buffer);
if (n >= 2 && buffer[n-1] == '\n' && buffer[n-2] == '\r')
buffer[n-2]='\0';
return buffer;
}
- (int)getStatus
{
const char *buf=[self getNNTPLine];
if(statusLine!=NULL)
free(statusLine);
statusLine=NXCopyStringBuffer(buf);
return atoi(statusLine);
}
- (int)issueCommand:(char *)command
{
int status,newStatus;
// issue command
fprintf(nntpOut,"%s\r\n",command);
fflush(nntpOut);
if(echoSocket)
printf("%s\n",command);
// get answer
status=[self getStatus];
//recover if timeout
if((status==ERR_FAULT)&& instr(statusLine,"timeout",FALSE)){
if(handling_timeout==TRUE){
[self closeServerDelayed];
EM_ERROR(ENNTPCouldNotReconnect,NULL,NULL);
return -1; //fatal error: could not reconnect
}
handling_timeout = TRUE;
if([self reconnectServer]!=nil){
if(strncmp(command,"group",5)!=0)
if(currentGroup!=nil)
if([self requestGroup:currentGroup]!=OK_GROUP){
[self closeServerDelayed];
EM_ERROR(ENNTPCouldNotReconnect,NULL,NULL);
return -1; //fatal error: could not sync
}
}
else{
[self closeServerDelayed];
EM_ERROR(ENNTPCouldNotReconnect,NULL,NULL);
return -1; //fatal error: could not reconnect
}
newStatus=[self issueCommand:command];
handling_timeout = FALSE;
return newStatus;
}
return status;
}
- (int)requestGroup:(Newsgroup *)aGroup
{
char inCodeText[BUFFER_SIZE];
long first, last, numArticles;
int statusCode;
sprintf(inCodeText, "group %s", [aGroup stringValue]);
statusCode=[self issueCommand:inCodeText];
if(statusCode==OK_GROUP){
sscanf(statusLine, "%*d %ld %ld %ld", &numArticles, &first, &last);
[[aGroup setMin:first] setMax:last];
currentGroup=aGroup;
}
else if(statusCode==ERR_COMMAND)
NXLogError("NNTP server does not recognize GROUP command.");
return statusCode;
}
- unselectCurrentGroup
{
currentGroup=nil;
return self;
}
- loadStorageWithGroupList:(Storage *)array
{
char *inCodeText;
char group[BUFFER_SIZE];
newsgroupDesc ngDesc;
while(((inCodeText=[self getNNTPLine])!=NULL) &&
(NNTP_LIST_END(inCodeText)==FALSE)){
sscanf(inCodeText,"%s %ld %ld %c",
group, &(ngDesc.max), &(ngDesc.min), &(ngDesc.post));
ngDesc.groupname=NXCopyStringBuffer(group);
if((ngDesc.post!='y')&&(ngDesc.post!='m'))
ngDesc.post='n';
[array addElement:&ngDesc];
}
return self;
}
- scanActive:(Storage *)theArray
{
int statusCode;
[self writeTimeTagToDefaultDB];
statusCode=[self issueCommand:"list"];
if (statusCode != OK_GROUPS) {
EM_ERROR(ENNTPErrorPerformingCommand,"LIST",(void *)atoi(statusLine));
return self;
}
[self loadStorageWithGroupList:theArray];
return self;
}
- scanNewGroups:(Storage *)theArray
{
int statusCode;
char inCodeText[BUFFER_SIZE];
sprintf(inCodeText,"newgroups %s",[self timeTag]);
statusCode=[self issueCommand:inCodeText];
[self writeTimeTagToDefaultDB];
if (statusCode!= OK_NEWGROUPS){
[self closeServerDelayed];
EM_ERROR(ENNTPErrorPerformingCommand,"NEWGROUPS",(void *)atoi(statusLine));
return self;
}
[self loadStorageWithGroupList:theArray];
return self;
}
- fetchSubjectHeaders:(Storage *)array from:(long)first to:(long)last
{
if(novSupported==TRUE)
[self xover:array from:first to:last];
else
[self xhdr:array from:first to:last];
return self;
}
- xhdr:(Storage *)array from:(long)first to:(long)last
{
int statusCode,i;
char *inCodeText;
char *text,*p;
char buf[255];
subjectDesc *defaultDesc;
subjectDesc *desc;
BOOL firstHeader=TRUE;
long number;
int pos_in_array;
defaultDesc=(subjectDesc *)calloc(1,sizeof(subjectDesc));
for(i=0;i<XOVER_COUNT+1;i++){
if((i==FROM)&&(doNotPrefetchFROM==TRUE)) continue;
if((i==MSG_ID)&&(doNotPrefetchMSGID==TRUE)) continue;
if((i==REFS)&&((doNotPrefetchREFS==TRUE)||(doNotPrefetchMSGID==TRUE))) continue;
if((i==DATE)&&(doNotPrefetchDATE==TRUE)) continue;
if((i==XOVER_COUNT)&&(doNotPrefetchLINES==TRUE)) continue;
if(i<XOVER_COUNT)
sprintf(buf, "xhdr %s %ld-%ld",h_field_name[i], first, last);
else
sprintf(buf,"xhdr Lines %ld-%ld",first,last);
statusCode=[self issueCommand:buf];
if (statusCode != OK_HEAD){
if(statusCode == ERR_COMMAND)
EM_ERROR(ENNTPCommandNotRecognised,"XHDR",NULL);
else
EM_ERROR(ENNTPErrorPerformingCommand,"XHDR",(void *)atoi(statusLine));
return self;
}
pos_in_array=0;
while(((inCodeText=[self getNNTPLine])!=NULL) && (NNTP_LIST_END(inCodeText)==FALSE)){
sscanf(inCodeText,"%ld",&number);
text=NXCopyStringBuffer(strchr(inCodeText,' ')+1);
if(strcmp(text,"(none)")==0)
text[0]='\0';
// convert to iso
p=text;
while(*p){
*p=c_iso2next[(unsigned char)(*p)];
p++;
}
if(firstHeader==TRUE){
defaultDesc->number=number;
defaultDesc->fieldBody=(char **)calloc(XOVER_COUNT,sizeof(char *));
[array addElement:defaultDesc];
}
desc=(subjectDesc *)[array elementAt:(unsigned int)pos_in_array];
NX_ASSERT(desc!=NULL,"INTERNAL ERROR:XHDR confusion");
NX_ASSERT(desc->number==number,"INTERNAL ERROR:XHDR mismatch");
if(i<XOVER_COUNT)
desc->fieldBody[i]=text;
else{
desc->lines=atoi(text);
free(text);
}
pos_in_array++;
}
firstHeader=FALSE;
}
free(defaultDesc);
return self;
}
- xover:(Storage *)array from:(long)first to:(long)last
{
int statusCode;
char inCodeText[BUFFER_SIZE];
char *line_buffer;
int i,j,a;
subjectDesc subDesc;
sprintf(inCodeText, "xover %ld-%ld", first, last);
statusCode=[self issueCommand:inCodeText];
if (statusCode != OK_XOVER){
if(statusCode == ERR_COMMAND)
EM_ERROR(ENNTPCommandNotRecognised,"XOVER",NULL);
else
EM_ERROR(ENNTPErrorPerformingCommand,"XOVER",(void *)atoi(statusLine));
return self;
}
while(((line_buffer=[self getNNTPLine])!=NULL) &&(NNTP_LIST_END(line_buffer)==FALSE)){
subDesc.fieldBody=(char **)calloc(XOVER_COUNT,sizeof(char *));
sscanf(line_buffer,"%ld",&(subDesc.number));
i=0;j=0;a=0;
while(j<XOVER_COUNT+2){
a=i;
while((line_buffer[i]!='\0')&&(line_buffer[i]!='\t')){
char *c=line_buffer+i;
*c=c_iso2next[(unsigned char)(*c)];
i++;
}
if((j>0)&&(j<XOVER_COUNT+1)){
subDesc.fieldBody[j-1]=NULL;
if(i>a){
subDesc.fieldBody[j-1]=(char *)malloc((i-a+1)*sizeof(char));
strncpy(subDesc.fieldBody[j-1],line_buffer+a,i-a);
subDesc.fieldBody[j-1][i-a]='\0';
}
}
j++;
i++;
}
subDesc.artsize=0; subDesc.lines=0;
sscanf(line_buffer+a,"%d\t%d",&(subDesc.artsize),&(subDesc.lines));
[array addElement:&subDesc];
}
return self;
}
- (int)loadArticleHeader:(Article *)article toString:(char **)aString
{
int statusCode;
char inCodeText[BUFFER_SIZE];
long i;
char *buf,*buf2;
int len,maxlen;
NXStream *theStream;
//Read HEAD
sprintf(inCodeText,"head %ld", [article number]);
statusCode=[self issueCommand:inCodeText];
if(statusCode==ERR_NOARTIG)
return statusCode;
if(statusCode!=OK_HEAD){
EM_ERROR(ENNTPErrorPerformingCommand,"HEAD",NULL);
return statusCode;
}
theStream=NXOpenMemory(NULL,0,NX_WRITEONLY);
if(MapNntpToStream(nntpIn,theStream,echoSocket)==-1){
[self closeServerDelayed];
EM_ERROR(ENNTPUnexpectedSocketClose,NULL,NULL);
return -1;
}
[article parseHeader:theStream];
NXSeek(theStream,0,NX_FROMEND);
NXPutc(theStream,(int)'\0');
// convert to iso
NXGetMemoryBuffer(theStream,&buf,&len,&maxlen);
buf2=buf;
for(i=0;i<len;i++){
*buf2=(char)c_iso2next[(unsigned char)(*buf2)];
buf2++;
}
*aString=NXCopyStringBuffer(buf);
NXCloseMemory(theStream,NX_FREEBUFFER);
return statusCode;
}
- (int)loadArticleBody:(Article *)article toString:(char **)aString
{
int statusCode;
char inCodeText[BUFFER_SIZE];
long i;
char *buf,*buf2;
int len,maxlen;
NXStream *theStream;
//Read HEAD
sprintf(inCodeText,"body %ld", [article number]);
statusCode=[self issueCommand:inCodeText];
if(statusCode==ERR_NOARTIG)
return statusCode;
if(statusCode!=OK_BODY){
EM_ERROR(ENNTPErrorPerformingCommand,"HEAD",NULL);
return statusCode;
}
theStream=NXOpenMemory(NULL,0,NX_WRITEONLY);
if(MapNntpToStream(nntpIn,theStream,echoSocket)==-1){
[self closeServerDelayed];
EM_ERROR(ENNTPUnexpectedSocketClose,NULL,NULL);
return -1;
}
NXSeek(theStream,0,NX_FROMEND);
NXPutc(theStream,(int)'\0');
// convert to iso
NXGetMemoryBuffer(theStream,&buf,&len,&maxlen);
buf2=buf;
for(i=0;i<len-1;i++){
*buf2=(char)c_iso2next[(unsigned char)(*buf2)];
buf2++;
}
*aString=NXCopyStringBuffer(buf);
NXCloseMemory(theStream,NX_FREEBUFFER);
return statusCode;
}
- (int)postArticle:(NXStream *)theStream
{
int statusCode;
const char *streambuf;
char *buf,*copiedbuf;
int max, len,i;
int bytesSent;
NXGetMemoryBuffer(theStream, &streambuf, &len, &max);
buf=(char *)malloc((len+4)*sizeof(char));
strncpy(buf,streambuf,len);
statusCode=[self issueCommand:"post"];
switch (statusCode) {
case CONT_POST:
break;
case ERR_NOPOST:
NXRunAlertPanel("ALEXANDRA","NNTP POST command failed. %d posting not allowed.",
NULL,NULL,NULL, statusCode);
return statusCode;
default:
// This should never occur!
NXRunAlertPanel("ALEXANDRA",
"NNTP POST command failed. Status code %d.",NULL,NULL,NULL, statusCode);
return statusCode;
}
// Convert to iso
for(i=0;i<len;i++)
buf[i]=(char)c_next2iso[(unsigned char)buf[i]];
// append .
if(buf[len-1]!='\n'){
buf[len]='\n';
len++;
}
buf[len]='.';
buf[len+1]='\n';
len+=2;
buf[len]='\0';
// send
copiedbuf=buf;
while (len > 0) {
bytesSent = send(writeSocket, buf, len, 0);
buf += bytesSent;
len -= bytesSent;
}
if(echoSocket)
printf("%s",copiedbuf);
free(copiedbuf);
statusCode=[self getStatus];
switch (statusCode) {
case OK_POSTED:
return statusCode;
case ERR_POSTFAIL:
NXRunAlertPanel("ALEXANDRA","%d posting failed. %s.",
NULL,NULL,NULL, statusCode,statusLine);
return statusCode;
default:
// This should never occur!
NXRunAlertPanel("ALEXANDRA","NNTP POST command failed. Status Code %d.",NULL,NULL,NULL, statusCode);
return statusCode;
}
}
- (BOOL)slowLink
{
return isSlowLink;
}
- setSlowLink:(BOOL)v
{
isSlowLink=v;
return self;
}
- killFile
{
return killFile;
}
- (BOOL)doesPrefetchFrom
{
return (!doNotPrefetchFROM);
}
- (BOOL)doesPrefetchDate
{
return (!doNotPrefetchDATE);
}
- (BOOL)findArticle:(const char *)msgid inGroups:(char ***)groups
{
char buf[BUFFER_SIZE];
int numGroups,status;
char *buff,*aString;
sprintf(buf,"XHDR Newsgroups %s",msgid);
status=[self issueCommand:buf];
if(status==OK_HEAD){
char *gstring=strchr([self getNNTPLine],' ')+1;
// count the number of groups
numGroups=0;
buff=gstring;
while(*buff!='\r'){
if(*buff==',')
numGroups++;
buff++;
}
// make list
*groups=calloc(numGroups+3,sizeof(void *));
numGroups=0;
for(aString=strtok(gstring,",");aString;aString=strtok(NULL,",")){
(*groups)[numGroups]=NXCopyStringBuffer(aString);
numGroups++;
}
//remove the rest until .
[self getNNTPLine];
return TRUE;
}
return FALSE;
}
@end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.