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.