#include #include #include #include #include #include #include bool NNTPClient::open(const String &hostName,const String &user,const String &password) { Block responseLines; String strLastResponse; HostEnt hostEntry; ServEnt serverEntry; if(isConnected())cancel(); message(String("trying ")+hostName+String("...")); if(!mWSASystem.isInitialized()){message("WINSOCK initialization failure.");return false;} InternetAddress internetAddress(hostName); if(!internetAddress.isZero()){if(!hostEntry.hostByAddress(internetAddress)){message(String("no DNS entry for ")+hostName);return false;}} else if(!hostEntry.hostByName(hostName)){message(String("no DNS entry for ")+hostName);return false;} message(String("connect...")+String("'")+hostEntry.hostName()+String("' (")+(String)(hostEntry.addresses())[0]+String(")")); INETSocketAddress::internetAddress((hostEntry.addresses())[0]); if(!serverEntry.serviceByName("nntp","tcp")){message("cannot determine port number for nntp daemon.");return FALSE;} if(!mNNTPControl.create()){message("unable to create socket.");return FALSE;} INETSocketAddress::family(PF_INET); INETSocketAddress::port(serverEntry.port()); if(!mNNTPControl.connect((INETSocketAddress&)*this)){message("unable to connect to nntp server");return FALSE;} if(!isConnected())return false; receiveStrings(responseLines); message(responseLines); if(responseLines.size()) { strLastResponse=responseLines[responseLines.size()-1]; if(isInResponse(strLastResponse.betweenString(0,' '),mNakConnectStrings))mNNTPControl.destroy(); else if(!authenticate(user,password))mNNTPControl.destroy(); } return mNNTPControl.isConnected(); } bool NNTPClient::open(const String &hostName) { Block responseLines; String strLastResponse; HostEnt hostEntry; ServEnt serverEntry; if(isConnected())cancel(); message(String("trying ")+hostName+String("...")); if(!mWSASystem.isInitialized()){message("WINSOCK initialization failure.");return false;} InternetAddress internetAddress(hostName); if(!internetAddress.isZero()){if(!hostEntry.hostByAddress(internetAddress)){message(String("no DNS entry for ")+hostName);return false;}} else if(!hostEntry.hostByName(hostName)){message(String("no DNS entry for ")+hostName);return false;} message(String("connect...")+String("'")+hostEntry.hostName()+String("' (")+(String)(hostEntry.addresses())[0]+String(")")); INETSocketAddress::internetAddress((hostEntry.addresses())[0]); if(!serverEntry.serviceByName("nntp","tcp")){message("cannot determine port number for nntp daemon.");return false;} if(!mNNTPControl.create()){message("unable to create socket.");return false;} INETSocketAddress::family(PF_INET); INETSocketAddress::port(serverEntry.port()); if(!mNNTPControl.connect((INETSocketAddress&)*this)){message("unable to connect to nntp server");return false;} if(!isConnected())return false; receiveStrings(responseLines); return mNNTPControl.isConnected(); } bool NNTPClient::quit(void) { String controlData; String responseLine; if(!isConnected())return false; controlData=mNNTPCmds[Quit]; if(!putControlData(controlData,false))return false; if(!mNNTPControl.receive(responseLine))return false; message(responseLine); if(!isInResponse(responseLine.betweenString(0,' '),mAckQuitResponseStrings))return false; return true; } bool NNTPClient::slave(void) { String controlData; String responseLine; if(!isConnected())return false; controlData=mNNTPCmds[Slave]; if(!putControlData(controlData,false))return false; if(!mNNTPControl.receive(responseLine))return false; message(responseLine); if(!isInResponse(responseLine.betweenString(0,' '),mAckSlaveResponseStrings))return false; return true; } bool NNTPClient::group(GroupItem &groupItem) { String controlData; String responseLine; if(!isConnected())return false; if(groupItem.groupName().isNull())return false; controlData.reserve(String::MaxString); ::sprintf(controlData,"%s %s",(LPSTR)mNNTPCmds[Group],(LPSTR)groupItem.groupName()); if(!putControlData(controlData,false))return false; if(!mNNTPControl.receive(responseLine))return false; if(!isInResponse(responseLine.betweenString(0,' '),mAckGroupResponseStrings))return false; groupItem< &articleText,String &messageID) { String controlData; bool returnCode; ::sprintf(controlData,"%s %ld",(LPSTR)mNNTPCmds[Article],articleNumber); returnCode=retrieveBlock(controlData,articleText,messageID,mAckArticleResponseStrings,mNackArticleResponseStrings); messageID=messageID.betweenString('<','>'); return returnCode; } bool NNTPClient::article(const MsgID &msgID,Block &articleText,String &messageID) { String controlData; controlData=mNNTPCmds[Article]+mSpace+msgID; return retrieveBlock(controlData,articleText,messageID,mAckArticleResponseStrings,mNackArticleResponseStrings); } bool NNTPClient::body(DWORD articleNumber,Block &bodyText,String &messageID) { String controlData; ::sprintf(controlData,"%s %d",(LPSTR)mNNTPCmds[Body],articleNumber); return retrieveBlock(controlData,bodyText,messageID,mAckArticleResponseStrings,mNackArticleResponseStrings); } bool NNTPClient::body(const MsgID &msgID,Block &bodyText,String &messageID) { String controlData; controlData=mNNTPCmds[Body]; controlData+=mSpace+msgID; return retrieveBlock(controlData,bodyText,messageID,mAckArticleResponseStrings,mNackArticleResponseStrings); } bool NNTPClient::head(DWORD articleNumber,Block &headText,String &messageID) { String controlData; ::sprintf(controlData,"%s %ld",(LPSTR)mNNTPCmds[Head],articleNumber); return retrieveBlock(controlData,headText,mAckArticleResponseStrings,mNackArticleResponseStrings); } bool NNTPClient::head(const MsgID &msgID,Block &headText,String &messageID) { String controlData; controlData=mNNTPCmds[Head]+mSpace+msgID; return retrieveBlock(controlData,headText,mAckArticleResponseStrings,mNackArticleResponseStrings); } bool NNTPClient::stat(DWORD articleNumber,MsgID &msgID) { String controlData; String responseLine; if(!isConnected())return false; ::sprintf(controlData,"%s %ld",(LPSTR)mNNTPCmds[Stat],articleNumber); if(!putControlData(controlData,false))return false; if(!mNNTPControl.receive(responseLine))return false; if(isInResponse(responseLine.betweenString(0,' '),mNackArticleResponseStrings))return false; responseLine=responseLine.betweenString(' ',0); msgID=responseLine.betweenString(' ',0); return (!msgID.isNull()); } bool NNTPClient::stat(const MsgID &msgID,MsgID &msgFound) { String controlData; String responseLine; if(!isConnected())return false; controlData=mNNTPCmds[Stat]+mSpace+msgID; if(!putControlData(controlData,false))return false; if(!mNNTPControl.receive(responseLine))return false; if(isInResponse(responseLine.betweenString(0,' '),mNackArticleResponseStrings))return false; responseLine=responseLine.betweenString(' ',0); msgFound=responseLine.betweenString(' ',0); return (!msgFound.isNull()); } bool NNTPClient::listGroup(const String &newsGroup,Block &messageIDStrings) { DWORD responseLines(0L); String controlData; String responseLine; messageIDStrings.remove(); if(!isConnected())return false; controlData=mNNTPCmds[ListGroup]; controlData+=mSpace; controlData+=newsGroup; if(!putControlData(controlData,false))return false; if(!mNNTPControl.receive(responseLine))return false; if(isInResponse(responseLine.betweenString(0,' '),mNackGroupResponseStrings))return false; while(mNNTPControl.receive(responseLine)) { if(mPeriod==responseLine&&1==responseLine.length())break; messageIDStrings.insert(&responseLine); } return (messageIDStrings.size()?true:false); } bool NNTPClient::newNews(Block &newsGroups,Block &distributionGroups,Block &messageIDStrings,const SystemTime &systemTime,WORD isGMT) { DWORD responseLines(0L); String controlData; String timeDateString; String responseLine; messageIDStrings.remove(); if(!isConnected())return false; controlData=mNNTPCmds[NewNews]; controlData+=mSpace; for(int itemIndex=0;itemIndex &messageIDStrings,short pastDays) { String messageID; SystemTime systemTime; GroupItem groupItem; int badArticles(0); int goodArticles(0); int maxBadArticles(100); int maxGoodArticlesToOffsetBad(10); messageIDStrings.remove(); if(!isConnected())return false; daysPast(systemTime,pastDays); groupItem.groupName(newsGroup); group(groupItem); for(int articleIndex=groupItem.lastArticle();articleIndex>=groupItem.firstArticle();articleIndex--) { Block headerText; head(articleIndex,headerText,messageID); if(!headerText.size()) { String msgString; ::sprintf(msgString,"article #:%d is not available, %d of %d",articleIndex,++badArticles,maxBadArticles); message(msgString); if(badArticles>=maxBadArticles)break; continue; } else goodArticles++; if(goodArticles>maxGoodArticlesToOffsetBad){badArticles=0;goodArticles=0;} Header articleHeader(headerText); message(String("examining ")+articleHeader.messageID()+String(", ")+articleHeader.date()); if(articleHeader.systemTime() &messageIDStrings,short pastDays) { DWORD responseLines(0L); String controlData; String responseLine; String timeDateString; SystemTime systemTime; messageIDStrings.remove(); if(!isConnected())return false; daysPast(systemTime,pastDays); controlData=mNNTPCmds[NewNews]; controlData+=mSpace; controlData+=newsGroup; makeTimeDateString(timeDateString,systemTime); controlData+=mSpace; controlData+=timeDateString; if(!putControlData(controlData))return false; while(mNNTPControl.receive(responseLine)) { if(mPeriod==responseLine&&1==responseLine.length())break; if(!responseLines++) { String responseString(responseLine.betweenString(0,' ')); if(isInResponse(responseString,mAckNewNewsResponseStrings))continue; } messageIDStrings.insert(&responseLine); } return (messageIDStrings.size()?true:false); } bool NNTPClient::newGroups(ListItems &listItems,short pastDays) { SystemTime systemTime; Block distributionGroups; listItems.remove(); daysPast(systemTime,pastDays); return newGroups(listItems,distributionGroups,systemTime,false); } bool NNTPClient::newGroups(ListItems &listItems,Block &distributionGroups,const SystemTime &systemTime,bool isGMT) { String controlData; String responseLine; String timeDateString; DWORD responseLines(0); listItems.remove(); if(!isConnected())return false; controlData=mNNTPCmds[NewGroups]; controlData+=mSpace; makeTimeDateString(timeDateString,systemTime); controlData+=timeDateString; if(isGMT)controlData+=" [GMT]"; if(distributionGroups.size()) { controlData+=mSpace+mLeftAngle; for(int itemIndex=0;itemIndex &overview) { DWORD responseLines(0); String controlData; String responseLine; overview.remove(); if(!isConnected())return false; controlData=mNNTPCmds[XOver]+String(" ")+String().fromInt(first)+String("-")+String().fromInt(last); if(!putControlData(controlData))return false; while(mNNTPControl.receive(responseLine)) { if(mPeriod==responseLine&&1==responseLine.length())break; if(!responseLines++) { String responseString(responseLine.betweenString(0,' ')); if(isInResponse(responseString,mAckXOverResponseStrings))continue; } overview.insert(&responseLine); } return true; } bool NNTPClient::help(Block &cmdLines) { DWORD responseLines(0); String controlData; String responseLine; cmdLines.remove(); if(!isConnected())return false; controlData=mNNTPCmds[Help]; if(!putControlData(controlData))return false; while(mNNTPControl.receive(responseLine)) { if(mPeriod==responseLine&&1==responseLine.length())break; if(!responseLines++) { String responseString(responseLine.betweenString(0,' ')); if(isInResponse(responseString,mAckHelpResponseStrings))continue; } cmdLines.insert(&responseLine); } return true; } bool NNTPClient::post(Block &articleLines) { String responseLine; String controlData; String postString; int lineCount; if(!isConnected())return false; controlData=mNNTPCmds[Post]; if(!putControlData(controlData))return false; if(!mNNTPControl.receive(responseLine))return false; responseLine=responseLine.betweenString(0,' '); if(!isInResponse(responseLine,mAckPostResponseStrings))return false; lineCount=articleLines.size(); for(int lineIndex=0;lineIndex &stringBlock,Block &ackResponse,Block &nackResponse) { String extraInfo; return retrieveBlock(controlData,stringBlock,extraInfo,ackResponse,nackResponse); } bool NNTPClient::retrieveBlock(const String &controlData,Block &stringBlock,String &extraInfo,Block &ackResponse,Block &nackResponse) { String responseLine; DWORD responseLines(0); stringBlock.remove(); if(!isConnected())return false; if(!putControlData(controlData,false))return false; while(mNNTPControl.receive(responseLine)) { if(mPeriod==responseLine&&1==responseLine.length())break; if(!responseLines++) { String responseString(responseLine.betweenString(0,' ')); extraInfo=responseLine.betweenString(' ','\0'); if(nackResponse.size()&&isInResponse(responseString,nackResponse))break; if(ackResponse.size()&&isInResponse(responseString,ackResponse))continue; } stringBlock.insert(&responseLine); } return (stringBlock.size()?true:false); } bool NNTPClient::putControlData(const String &stringData,bool waitForResponse) { if(!mNNTPControl.isConnected())return false; if(!mNNTPControl.send(stringData)) { mNNTPControl.destroy(); String errorString(String("error sending '")+stringData+String("' to NNTP server.")); message(errorString); return false; } if(waitForResponse&&!getControlData()) { mNNTPControl.destroy(); String errorString(String("error reading result of '")+stringData+String("' command from NNTP server.")); message(errorString); return false; } return true; } bool NNTPClient::getControlData(void) { Block responseStrings; mNNTPControl.receive(responseStrings); return responseStrings.size()?true:false; } void NNTPClient::receiveStrings(WORD displayStrings) { Block receiveStrings; if(!mNNTPControl.isConnected())return; if(!mNNTPControl.receive(receiveStrings))return; if(!displayStrings||!receiveStrings.size())return; message(receiveStrings); } void NNTPClient::receiveStrings(Block &receiveStrings) { receiveStrings.remove(); if(!mNNTPControl.isConnected())return; if(!mNNTPControl.receive(receiveStrings))return; if(!receiveStrings.size())return; message(receiveStrings); } void NNTPClient::createCmds(void) { mNNTPCmds.insert(&String("ARTICLE")); mNNTPCmds.insert(&String("BODY")); mNNTPCmds.insert(&String("GROUP")); mNNTPCmds.insert(&String("HEAD")); mNNTPCmds.insert(&String("HELP")); mNNTPCmds.insert(&String("IHAVE")); mNNTPCmds.insert(&String("LAST")); mNNTPCmds.insert(&String("LIST")); mNNTPCmds.insert(&String("NEWGROUPS")); mNNTPCmds.insert(&String("NEWNEWS")); mNNTPCmds.insert(&String("NEXT")); mNNTPCmds.insert(&String("POST")); mNNTPCmds.insert(&String("QUIT")); mNNTPCmds.insert(&String("SLAVE")); mNNTPCmds.insert(&String("STAT")); mNNTPCmds.insert(&String("LISTGROUP")); mNNTPCmds.insert(&String("AUTHINFO USER")); mNNTPCmds.insert(&String("AUTHINFO PASS")); mNNTPCmds.insert(&String("XOVER")); } bool NNTPClient::isInResponse(const String &responseString,Block &responseStrings) { if(responseString.isNull())return false; for(int itemIndex=0;itemIndex2000?2000-systemTime.year():systemTime.year()-1900, systemTime.month(), systemTime.day(), systemTime.hour(), systemTime.minute(), systemTime.second()); } void NNTPClient::daysPast(SystemTime &systemTime,short pastDays) { systemTime.daysAdd360(-pastDays); systemTime.hour(0); systemTime.minute(0); systemTime.second(0); } // virtual overloads void NNTPClient::message(String messageString) { } void NNTPClient::message(Block &messageStrings) { }