home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 35 Internet / 35-Internet.zip / vsoup128.zip / nntpcl.cc < prev    next >
C/C++ Source or Header  |  1997-04-20  |  18KB  |  800 lines

  1. //  $Id: nntpcl.cc 1.28 1997/04/20 19:18:48 hardy Exp $
  2. //
  3. //  This progam/module was written by Hardy Griech based on ideas and
  4. //  pieces of code from Chin Huang (cthuang@io.org).  Bug reports should
  5. //  be submitted to rgriech@swol.de.
  6. //
  7. //  This file is part of VSoup for OS/2.  VSoup including this file
  8. //  is freeware.  There is no warranty of any kind implied.  The terms
  9. //  of the GNU Gernal Public Licence are valid for this piece of software.
  10. //
  11. //  NNTP client routines
  12. //
  13.  
  14.  
  15. #include <assert.h>
  16. #include <ctype.h>
  17. #include <fcntl.h>
  18. #include <stdlib.h>
  19. #include <string.h>
  20. #include <time.h>
  21. #include <unistd.h>
  22.  
  23. #include "mts.hh"
  24. #include "nntp.hh"
  25. #include "nntpcl.hh"
  26. #include "socket.hh"
  27.  
  28.  
  29.  
  30. //
  31. //  is this a misfeature of GCC, is there a better way to do it??
  32. //
  33. #define STR2(x) #x
  34. #define STR(x)  STR2(x)
  35.  
  36.  
  37.  
  38. static TSemaphor cntSema;     // static in class tut nicht (gcc2.7.0)
  39.  
  40.  
  41.  
  42. //--------------------------------------------------------------------------------
  43.  
  44.  
  45.  
  46. TNntp::TNntp( void )
  47. {
  48. #ifdef TRACE_ALL
  49.     printfT( "TNntp::TNntp()\n" );
  50. #endif
  51.     xrefHook  = NULL;
  52.     killQHook = NULL;
  53.     actGroup = xstrdup("");
  54.     selGroup = xstrdup("");
  55.     user     = xstrdup("");
  56.     passwd   = xstrdup("");
  57.     strcpy( lastErrMsg, "unknown error condition" );
  58.     artNotAvail = 0;
  59. }   // TNntp::TNntp
  60.  
  61.  
  62.  
  63. TNntp::~TNntp()
  64. {
  65. #ifdef TRACE_ALL
  66.     printfT( "TNntp::~TNntp()\n" );
  67. #endif
  68.     close( 0 );
  69. ////    delete actGroup;
  70. ////    delete selGroup;
  71.     //// delete user;
  72.     //// delete passwd;
  73. }   // TNntp::~TNntp
  74.  
  75.  
  76.  
  77. void TNntp::setHelper( void (*xref)(const char *xrefLine),
  78.                long (*killQ)(const char *groupName, const char *headerLine ) )
  79. {
  80.     xrefHook  = xref;
  81.     killQHook = killQ;
  82. }   // TNntp::setHelper
  83.  
  84.  
  85.  
  86. TNntp::Res TNntp::request( const char *cmd, char *reply, size_t replySize,
  87.                int expReply )
  88. //
  89. //  Send a request to NNTP server and check the result (cmd must not end with \n)
  90. //  If the server request authentication, the AUTHINFO procedure according to
  91. //  RFC977-extension will be executed.
  92. //  If the coonection has been broken, then *reply == '\0'
  93. //
  94. {
  95.     int retcode;
  96.     int loopCnt;
  97.  
  98. #ifdef TRACE_ALL
  99.     printfT("TNntp::request(%s,,,%d)\n",cmd,expReply );
  100. #endif
  101.  
  102.     *reply = '\0';
  103.     loopCnt = 0;
  104.     for (;;) {
  105.     //
  106.     //  three retries for the command
  107.     //
  108.     if (loopCnt++ >= 3) {
  109.         strcpy( lastErrMsg,"nntp server is in a loop requesting AUTHINFO..." );
  110.         return nok;
  111.     }
  112.  
  113.     //
  114.     //  transmit the command & fetch the result
  115.     //
  116.     if (printf( "%s\n",cmd ) < 0) {
  117.         sprintfT( lastErrMsg,"%s:  cannot transmit", cmd );
  118.         *reply = '\0';
  119.         return nok;
  120.     }
  121.     if (gets(reply,replySize) == NULL) {
  122.         sprintfT( lastErrMsg,"%s:  no reply", cmd );
  123.         *reply = '\0';
  124.         return nok;
  125.     }
  126.     if (reply[0] == CHAR_FATAL) {
  127.         sprintfT( lastErrMsg,"%s:  fatal (%s)", cmd,reply );
  128.         return nok;
  129.     }
  130.  
  131.     //
  132.     //  if return code != ERR_NOAUTH, we are done (-> check the result)
  133.     //
  134.     retcode = atoi(reply);
  135.     if (retcode != ERR_NOAUTH)
  136.         break;
  137.  
  138. #ifdef DEBUG_ALL
  139.     hprintfT( STDERR_FILENO,"authentication requested\n" );
  140.     printfT( "authentication requested\n" );
  141. #endif
  142.     //
  143.     //  otherwise do the authentication
  144.     //
  145.     printf( "AUTHINFO USER %s\n",user );
  146.     if (gets(reply,replySize) == NULL) {
  147.         strcpy( lastErrMsg,"AUTHINFO USER:  no reply" );
  148.         *reply = '\0';
  149.         return nok;
  150.     }
  151.     retcode = atoi(reply);
  152.     if (retcode == OK_AUTH)
  153.         continue;
  154.     if (retcode != NEED_AUTHDATA) {
  155.         sprintfT( lastErrMsg,"AUTHINFO USER:  %s",reply );
  156.         return nok;
  157.     }
  158.     
  159.     printf( "AUTHINFO PASS %s\n",passwd );
  160.     if (gets(reply,replySize) == NULL) {
  161.         strcpy( lastErrMsg,"AUTHINFO PASS:  no reply" );
  162.         *reply = '\0';
  163.         return nok;
  164.     }
  165.     retcode = atoi(reply);
  166.     if (retcode != OK_AUTH) {
  167.         sprintfT( lastErrMsg,"AUTHINFO PASS:  %s",reply );
  168.         return nok;
  169.     }
  170. #ifdef DEBUG_ALL
  171.     hprintfT( STDERR_FILENO,"authentication ok\n" );
  172.     printfT( "authentication ok\n" );
  173. #endif
  174.     }
  175.  
  176.     if (retcode != expReply) {
  177.     sprintfT( lastErrMsg,"%s:  %s", cmd,reply );
  178.     return nok;
  179.     }
  180.     return ok;
  181. }   // TNntp::request
  182.  
  183.  
  184.  
  185. TNntp::Res TNntp::open( const char *nntpServer, const char *nntpUser,
  186.             const char *nntpPasswd, int nntpPort )
  187. {
  188.     char buf[500];
  189.     char buf2[500];
  190.     int  response;
  191.  
  192. #ifdef TRACE_ALL
  193.     printfT( "TNntp::open(%s,,%d)\n",nntpServer,nntpPort );
  194. #endif
  195.     readOnly = 0;
  196.     xstrdup(   &user,nntpUser );
  197.     xstrdup( &passwd,nntpPasswd );
  198.  
  199.     if (nntpServer == NULL  ||  *nntpServer == '\0') {
  200.     strcpy( lastErrMsg,"no news server defined" );
  201.     return nok;
  202.     }
  203.  
  204.     if (TSocket::open( nntpServer,"nntp","tcp",nntpPort ) < 0) {
  205.     strcpy( lastErrMsg,"cannot open socket" );
  206.     return nok;
  207.     }
  208.  
  209.     if (gets(buf, sizeof(buf)) == NULL) {
  210.     strcpy( lastErrMsg,"connect:  no reply" );
  211. #ifdef DEBUG
  212.     printfT( "TNntp::open():  socket: %s\n",buf );
  213. #endif
  214.     return nok;
  215.     }
  216.     else {
  217.     response = atoi(buf);
  218.     switch (response) {
  219.  
  220.     case OK_NOPOST:
  221.         readOnly = 1;
  222.         break;
  223.         
  224.     case OK_CANPOST:
  225.         break;
  226.         
  227.     case ERR_ACCESS:
  228.         sprintfT( lastErrMsg,"connect:  no permission, %s",buf );
  229.         return nok;
  230.         
  231.     default:
  232.         sprintfT( lastErrMsg,"connect:  ill response, %s",buf );
  233. #ifdef DEBUG
  234.         printfT( "TNntp::open():  illresp: %s\n",buf );
  235. #endif
  236.         return nok;
  237.     }
  238.     }
  239.  
  240.     //
  241.     // This is for INN (result is ignored)
  242.     //
  243.     request( "MODE READER",buf2,sizeof(buf2),OK_CANPOST );
  244. #ifdef DEBUG_ALL
  245.     printfT( "TNntp::open():  antwort auf mode reader: %s\n",buf2 );
  246. #endif
  247.  
  248.     sprintfT( lastErrMsg,"%s",buf );
  249.  
  250.     //
  251.     //  create temporary file
  252.     //
  253.     if ( !tmpF.isOpen()) {
  254.     if ( !tmpF.open("soup")) {
  255.         strcpy( lastErrMsg,"create of temporary file failed" );
  256.         return nok;
  257.     }
  258.     }
  259. #ifdef DEBUG_ALL
  260.     printfT( "TNntp::open(): connected\n" );
  261. #endif
  262.     return ok;
  263. }   // TNntnp::open
  264.  
  265.  
  266.  
  267. void TNntp::close( int sendQuit )
  268. {
  269. #ifdef TRACE_ALL
  270.     printfT( "TNntp::close(%d)\n",sendQuit );
  271. #endif
  272.     if (sendQuit) {
  273.     char buf[100];
  274.  
  275. #ifdef TRACE_ALL
  276.     printfT( "TNntp::close(): QUIT\n" );
  277. #endif
  278.     request( "QUIT",buf,sizeof(buf), OK_GOODBYE );
  279.     }
  280.     TSocket::close();
  281.     tmpF.close();
  282. }   // TNntp::close
  283.  
  284.  
  285.  
  286. const char *TNntp::getLastErrMsg( void )
  287. {
  288.     return lastErrMsg;
  289. }   // TNntp::getLastErrMsg
  290.  
  291.  
  292.  
  293. TNntp::Res TNntp::getXhdr( const char *headerField, long first, long last,
  294.                int (*callback)(int operation, const char *line) )
  295. //
  296. //  Get the XHDR 'lines' (shortest)
  297. //  callback()-fct is called for each received line (example in news.cc)
  298. //  Correct group must be selected for this thread
  299. //
  300. {
  301.     char buf[200];
  302.     char cmd[100];
  303.     Res  res;
  304.  
  305. #ifdef TRACE_ALL
  306.     printfT( "getXhdr(%s,%ld,%ld)\n",headerField,first,last );
  307. #endif
  308.  
  309.     sprintfT( cmd,"XHDR %s %ld-%ld",headerField,first,last );
  310.     if (request(cmd,buf,sizeof(buf),OK_HEAD) != ok) {
  311. #ifdef DEBUG_ALL
  312.     printfT( "getXhdr-error: %s\n",lastErrMsg );
  313. #endif
  314.     return ok;     // no problem!
  315.     }
  316.  
  317.     sprintfT( buf,"%ld",first );    // init callback
  318.     callback( 1,buf );
  319.     callback( 2,actGroup );
  320.  
  321.     res = nok;
  322.     while (gets(buf, sizeof(buf)) != NULL) {
  323. #ifdef TRACE_ALL
  324.     printfT( "xhdr: %s\n",buf );
  325. #endif
  326.     if (buf[0] == '.') {
  327.         res = ok;
  328.         break;
  329.     }
  330.     if ( !callback(0,buf))
  331.         break;
  332.     }
  333.     return res;
  334. }   // TNntp::getXhdr
  335.  
  336.  
  337.  
  338. TNntp::Res TNntp::getNewGroups( const char *nntpTimeFile, int changeFile )
  339. //
  340. //  fetch new groups to file
  341. //
  342. {
  343.     char oldTime[80], nntpTime[80], buf[NNTP_STRLEN];
  344.     TFile dateF;
  345.     int  getall;
  346.     char *p;
  347.     Res  res;
  348.  
  349. #ifdef TRACE
  350.     printfT( "getNewGroups()\n" );
  351. #endif
  352.  
  353.     //
  354.     //  get current date/time from NNTP server
  355.     //
  356.     if (request("DATE",buf,sizeof(buf),INF_DATE) == ok)
  357.     sscanfT( buf+4, "%s", nntpTime );
  358.     else {
  359.     time_t now = time(NULL);
  360.     strftime( nntpTime, sizeof(nntpTime), "%Y%m%d%H%M%S", gmtime(&now) );
  361.     }
  362.     
  363.     //
  364.     //  Get last date/time we checked for new newsgroups.
  365.     //
  366.     getall = 0;
  367.     *oldTime = '\0';
  368.     if (dateF.open(nntpTimeFile,TFile::mread,TFile::otext)) {
  369.     dateF.fgets( oldTime, sizeof(oldTime), 1 );
  370.     dateF.close();
  371.     }
  372.     //
  373.     //  check time stamp (no file is also caught)
  374.     //
  375.     {
  376.     int i;
  377.     for (i = 0;  i < 14;  ++i)
  378.         getall = getall  ||  !isdigit(oldTime[i]);
  379.     }
  380.  
  381.     //
  382.     //  Request new newsgroups.
  383.     //
  384.     {
  385.     char cmd[100];
  386.  
  387.     if (getall)
  388.         strcpy( cmd,"LIST" );
  389.     else
  390.         sprintfT( cmd,"NEWGROUPS %-6.6s %-6.6s GMT", oldTime+2, oldTime+8);
  391.     if (request(cmd,buf,sizeof(buf),getall ? OK_GROUPS : OK_NEWGROUPS) != ok)
  392.         return nok;
  393.     }
  394.  
  395.     tmpF.truncate( 0L );
  396.     res = nok;
  397.     while (gets(buf, sizeof(buf)) != NULL) {
  398. #ifdef DEBUG_ALL
  399.     printfT( "rcv: %s\n",buf );
  400. #endif
  401.     if (buf[0] == '.') {
  402.         res = ok;
  403.         break;
  404.     }
  405.     if ((p = strchr(buf, ' ')) != NULL)
  406.         *p = '\0';
  407.  
  408.     tmpF.printf( "%s\n",buf );
  409.     }
  410.  
  411.     //
  412.     //  Save current date/time.
  413.     //
  414.     if (changeFile) {
  415.     if (dateF.open(nntpTimeFile,TFile::mwrite,TFile::otext,1)) {
  416.         dateF.printf( "%s\n",nntpTime );
  417.         dateF.close();
  418.     }
  419.     }
  420.     if ( !ok)
  421.     strcpy( lastErrMsg,"LIST/NEWGROUPS aborted" );
  422.     return res;
  423. }   // TNntp::getNewGroups
  424.  
  425.  
  426.  
  427. TNntp::Res TNntp::getOverview( long first, long last )
  428. //
  429. //  Attention:  those overview lines are sometimes VERY long (references...)
  430. //
  431. {
  432.     char buf[NNTP_STRLEN];
  433.     char cmd[100];
  434.     Res  res;
  435.  
  436. #ifdef TRACE_ALL
  437.     printfT( "TNntp::getOverview(%ld,%ld,%s)\n",first,last,selGroup );
  438. #endif
  439.     if (first < last)
  440.     sprintfT( cmd,"XOVER %ld-%ld", first, last );
  441.     else
  442.     sprintfT( cmd,"XOVER %ld-", first );
  443.     if (request(cmd,buf,sizeof(buf),OK_XOVER) != ok)
  444.     return nok;
  445.  
  446.     tmpF.truncate( 0L );
  447.     res = nok;
  448.     while (gets(buf, sizeof(buf)) != NULL) {
  449.     if (buf[0] == '.') {
  450.         res = ok;
  451.         break;
  452.     }
  453.     tmpF.printf( "%s\n",buf );
  454. #ifdef TRACE_ALL
  455. //    printfT( "%s\n",buf );
  456. #endif
  457.     }
  458.     if ( !ok)
  459.     strcpy( lastErrMsg,"XOVER aborted" );
  460.     return res;
  461. }   // TNntp::getOverview
  462.  
  463.  
  464.  
  465. TNntp::Res TNntp::setActGroup( const char *group, long &cnt, long &lo, long &hi )
  466. //
  467. //  activate nntp group
  468. //  returns:  ok,nok,notavail
  469. //
  470. {
  471.     char buf[NNTP_STRLEN];
  472.     char cmd[100];
  473.     long l1,l2,l3;
  474.  
  475. #ifdef TRACE_ALL
  476.     printfT( "TNntp::setActGroup(%s,..)\n",group );
  477. #endif
  478.  
  479.     xstrdup( &actGroup,group );
  480.     selNntpArticle = -1;
  481.  
  482.     sprintfT( cmd,"GROUP %s",group );
  483.     if (request(cmd,buf,sizeof(buf),OK_GROUP) != ok) {
  484.     xstrdup( &actGroup,"" );
  485.     return (buf[0] == '\0') ? nok : notavail;
  486.     }
  487.  
  488.     sscanfT(buf+4, "%ld %ld %ld", &l1, &l2, &l3);
  489.     cnt = l1;  lo = l2;  hi = l3;
  490. ////    selNntpArticle = l2;  wäre korrekt, bringt es aber nicht so für den NEXT
  491.     nntpArtHi    = l3;
  492.     nntpArtFirst = l2;
  493.     
  494. #ifdef TRACE_ALL
  495.     printfT( "TNntp::setActGroup(%s,%ld,%ld,%ld)\n",group,cnt,lo,hi );
  496. #endif
  497.     return ok;
  498. }   // TNntp::setActGroup
  499.  
  500.  
  501.  
  502. TNntp::Res TNntp::nextArticle( long *next )
  503. //
  504. //  Get next article in group.
  505. //  Return ok if successful.
  506. //
  507. {
  508.     char buf[NNTP_STRLEN];
  509.  
  510.     if (request("NEXT",buf,sizeof(buf),OK_NOTEXT) != ok) {
  511.     *next = selNntpArticle = nntpArtHi;
  512.     return nok;                            // no next article
  513.     }
  514.     *next = selNntpArticle = atol(buf+4);
  515.  
  516. #ifdef DEBUG_ALL
  517.     printfT( "nntpNext() -> %ld\n",*next );
  518. #endif
  519.     return ok;
  520. }   // TNntp::nextArticle
  521.  
  522.  
  523.  
  524. void TNntp::selectArticle( const char *grpname, long artNum, int doKill,
  525.                long artFirst, long artHi )
  526. {
  527. #ifdef TRACE_ALL
  528.     printfT( "selectArticle(%s,%ld,%d,%ld,%ld)\n",
  529.          grpname,artNum,doKill,artFirst,artHi );
  530. #endif
  531.  
  532.     if (grpname != NULL) {
  533.     if (strcmp(selGroup,grpname) != 0)
  534.         xstrdup( &selGroup,grpname );
  535.     }
  536.  
  537.     selArticle  = artNum;
  538.     killEnabled = doKill;
  539.     if (artFirst > 0)
  540.     nntpArtFirst = artFirst;
  541.     if (artHi > 0)
  542.     nntpArtHi = artHi;
  543. }   // TNntp::selectArticle
  544.  
  545.  
  546.  
  547. TNntp::Res TNntp::_getHead( void )
  548. //
  549. //  Get the articles header and write it to a temporary file (tmpF)
  550. //  killing & cross referencing is handled here
  551. //  return:  ok,nok,killed,notavail
  552. //
  553. {
  554.     char buf[NNTP_STRLEN];
  555.     char cmd[100];
  556.     char gotXref;
  557.     Res  res;
  558.     long articleScore;
  559.     long killThreshold;
  560.     int  killReasonFound;
  561.  
  562. #ifdef TRACE_ALL
  563.     printfT( "_getHead(): %ld\n",selArticle );
  564. #endif
  565.  
  566.     //
  567.     //  request article (head)
  568.     //
  569.     sprintfT( cmd,"%s %ld", killEnabled ? "HEAD" : "ARTICLE",selArticle );
  570.     if (request(cmd,buf,sizeof(buf),killEnabled ? OK_HEAD : OK_ARTICLE) != ok)
  571.     return (buf[0] == CHAR_ERR) ? notavail : nok;
  572.     selNntpArticle = selArticle;
  573.  
  574.     articleScore = 0;
  575.     gotXref = 0;
  576.     killThreshold = 0;
  577.     if (killEnabled  &&  killQHook != NULL)
  578.     killThreshold = killQHook( NULL, NULL );    // hack: killQHook return killthreshold
  579.     killReasonFound = 0;
  580.  
  581.     //
  582.     //  Get lines of article head.
  583.     //
  584.     res = nok;
  585.     while (gets(buf, sizeof(buf)) != NULL) {
  586.     char *bufp = buf;
  587.  
  588. #ifdef DEBUG_ALL
  589.     printfT( "--1: %ld '%s'\n",selArticle,bufp );
  590. #endif
  591.  
  592.     if (killEnabled) {
  593.         if (buf[0] == '.')
  594.         if (*(++bufp) == '\0') {
  595.             res = ok;
  596.             break;
  597.         }
  598.     }
  599.     else if (*bufp == '\0') {
  600.         res = ok;
  601.         break;
  602.     }
  603.     
  604.     tmpF.printf( "%s\n",bufp);
  605.  
  606.     if (killEnabled  &&  killQHook != NULL) {
  607.         long oldScore = articleScore;
  608.         articleScore += killQHook(selGroup,bufp);
  609.         if ((oldScore >= killThreshold  &&  articleScore < killThreshold)  ||
  610.         (!killReasonFound  &&  strnicmp(bufp,"subject:",8) == 0)) {
  611.         sprintfT( lastErrMsg,"%.100s", bufp );
  612.         killReasonFound = 1;
  613.         }
  614.     }
  615.  
  616.     if (xrefHook != NULL  &&  !gotXref  &&  strnicmp(bufp, "xref: ", 6) == 0) {
  617.         xrefHook(bufp+6);
  618.         gotXref = 1;           // why is only one Xref allowed ?
  619.     }
  620.     }
  621.  
  622.     //
  623.     //  Don't process anymore if article was killed.
  624.     //
  625.     if (articleScore < killThreshold) {
  626.     assert( killEnabled );
  627.     return killed;
  628.     }
  629.  
  630.     //
  631.     //  Put empty line separating head from body.
  632.     //
  633.     tmpF.putcc('\n');
  634.     if ( !ok)
  635.     strcpy( lastErrMsg,"HEAD/ARTICLE aborted" );
  636.     return res;
  637. }   // TNntp::_getHead
  638.  
  639.  
  640.  
  641. TNntp::Res TNntp::_getBody( void )
  642. //
  643. //  Get the articles body and write it to a temporary file (tmpF)
  644. //  should not be called, if article is going to be killed
  645. //  return:  nok, ok
  646. //
  647. {
  648.     char buf[NNTP_STRLEN];
  649.     Res  res;
  650.  
  651. #ifdef TRACE_ALL
  652.     printfT( "_getBody(): %ld\n",selArticle );
  653. #endif
  654.     
  655.     if (killEnabled) {
  656.     char cmd[100];
  657.  
  658.     sprintfT( cmd,"BODY %ld", selArticle );
  659.     if (request(cmd,buf,sizeof(buf),OK_BODY) != ok)
  660.         return (buf[0] == CHAR_ERR) ? notavail : nok;
  661.     selNntpArticle = selArticle;
  662.     }
  663.  
  664.     //
  665.     //  Retrieve article body.
  666.     //
  667.     res = nok;
  668.     while (gets(buf, sizeof(buf)) != NULL) {
  669.     char *bufp = buf;
  670.  
  671.     if (buf[0] == '.') {
  672.         if (*(++bufp) == '\0') {
  673.         res = ok;               // -> end of article !
  674.         break;
  675.         }
  676.     }
  677.     tmpF.printf( "%s\n",bufp );
  678. #ifdef DEBUG_ALL
  679.     printfT( "--2: %ld '%s'\n",selArticle,bufp );
  680. #endif
  681.     }
  682.     if ( !ok)
  683.     strcpy( lastErrMsg,"BODY/ARTICLE aborted" );
  684.     return res;
  685. }   // _getBody
  686.  
  687.  
  688.  
  689. TNntp::Res TNntp::getArticle( void )
  690. //
  691. //  Get the article and write it to a temporary file (tmpF)
  692. //  killing & cross referencing is handled here
  693. //  return:  ok,nok,notvail,killed
  694. //  calls: _getHead, _getBody
  695. //
  696. {
  697.     Res res;
  698.  
  699. #ifdef TRACE_ALL
  700.     printfT( "getArticle(): %ld\n",selArticle );
  701. #endif
  702.  
  703. #ifdef TRACE_ALL
  704.     printfT( "--0: %ld\n",selArticle );
  705. #endif
  706.  
  707.     //
  708.     //  select the group, if required
  709.     //
  710.     if (strcmp(actGroup,selGroup) != 0) {
  711.     long d0,d1,d2;
  712.     res = setActGroup( selGroup, d0,d1,d2 );
  713.     if (res != ok)
  714.         return nok;   // ignore notavail in this case!
  715.     }
  716.  
  717. #ifdef TRACE_ALL
  718.     printfT( "--1: %ld\n",selArticle );
  719. #endif
  720.  
  721.     //
  722.     //  Get article to temporary file.
  723.     //
  724.     tmpF.seek(0L, SEEK_SET);
  725.  
  726.     res = _getHead();
  727.     if (res != ok)
  728.     return res;
  729.  
  730. #ifdef TRACE_ALL
  731.     printfT( "--2: %ld\n",selArticle );
  732. #endif
  733.  
  734.     res = _getBody();
  735.  
  736. #ifdef TRACE_ALL
  737.     printfT( "--3: %ld\n",selArticle );
  738. #endif
  739.     return res;
  740. }   // TNntp::getArticle
  741.  
  742.  
  743.  
  744. TNntp::Res TNntp::postArticle( TFile &file, size_t bytes )
  745. //
  746. //  Post article to NNTP server.
  747. //  on entry:  filehandle points to beginning of message
  748. //             'bytes' contains message size
  749. //  on exit:   filehandle points to end of message
  750. //  Return ok if successful.
  751. //
  752. {
  753.     char buf[NNTP_STRLEN];
  754.     size_t count;
  755.     long offset;
  756.     int  sol;                 // start of line
  757.     int  ll;                  // line length
  758.  
  759. #ifdef TRACE
  760.     printfT( "TNntp::postArticle(.,%ld)\n",bytes );
  761. #endif
  762.     if (request("POST",buf,sizeof(buf),CONT_POST) != ok)
  763.     return nok;
  764.  
  765.     offset = file.tell();
  766.     count = bytes;
  767.     sol = 1;
  768.     while (file.fgets(buf,sizeof(buf)) != NULL  &&  count > 0) {
  769.     //
  770.     //  - replace trailing "\r\n" with "\n"
  771.     //  - send the string to the socket
  772.     //  - set sol, countdown artlength
  773.     //
  774.     ll  = strlen(buf);
  775.     if (strcmp( buf+ll-2,"\r\n" ) == 0)
  776.         strcpy( buf+ll-2,"\n" );
  777.     printf( "%s%s", (sol && buf[0] == '.') ? "." : "", buf );
  778.     sol = (buf[strlen(buf)-1] == '\n');
  779.     count -= ll;
  780.     }
  781.     file.seek(offset+bytes, SEEK_SET);
  782.  
  783.     if (request(".",buf,sizeof(buf),OK_POSTED) != ok) {
  784.     if (atoi(buf) == ERR_POSTFAIL)
  785.         sprintfT( lastErrMsg, "POST:  article not accepted by server; not posted\n\t(%s)",buf );
  786.     else
  787.         sprintfT( lastErrMsg, "POST:  %s",buf );
  788.  
  789.     //
  790.     //  if the server replied with a 'dont resend' or so,
  791.     //  an ok-condition is faked
  792.     //
  793.     if (strstr(buf,STR(ERR_GOTIT))    != NULL  ||
  794.         strstr(buf,STR(ERR_XFERRJCT)) != NULL)
  795.         return ok;
  796.     return nok;
  797.     }
  798.     return ok;
  799. }   // TNntp::postArticle
  800.