home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 / Nebula One.iso / Internet / News / Alexandra.0.82 / Source / NNTP.m < prev    next >
Encoding:
Text File  |  1996-01-30  |  19.0 KB  |  794 lines

  1.  
  2. #import "mapfile.h"
  3. #import "NNTP.h"
  4. #import "next2iso.tab"
  5. #import "iso2next.tab"
  6. #import "Alexandra.h"
  7.  
  8. #import <string.h>
  9. #import <stdlib.h>
  10. #import <sys/file.h>
  11. #import <sys/uio.h>
  12. #import <sys/types.h>
  13. #import <sys/socket.h>
  14. #import <netdb.h>
  15. #import <netinet/in.h>
  16. #import <time.h>
  17. #import <sys/time.h>
  18. #import <pwd.h>
  19.  
  20. #import "readline.h"
  21. #import "response_codes.h"
  22. #import "descriptors.h"
  23. #import "headerfields.h"
  24.  
  25. #import <objc/Storage.h>
  26. #import <misckit/MiscAppDefaults.h>
  27. #include "instr.h"
  28.  
  29. #define NNTP_LIST_END(s)  ((s)[0]=='.' && ((s)[1]=='\0' || (s)[1]=='\r' || (s)[1]==' '))
  30.  
  31. @implementation NNTP
  32.  
  33. int makeTimeTag(char *aTimetag)
  34. {
  35.    struct timeval tp;
  36.    struct timezone tzp;
  37.    struct tm *tv;
  38.  
  39.    gettimeofday(&tp, &tzp);
  40.    tv = gmtime(&tp.tv_sec);
  41.     
  42.    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); 
  43.  
  44.    return 0;
  45. }
  46.  
  47.  
  48. - init
  49. {
  50.     [super init];
  51.     nntpHostName =NULL;
  52.     handling_timeout=FALSE;
  53.     canPost=FALSE;
  54.     statusLine=NULL;
  55.  
  56.     return self;
  57. }
  58.  
  59. - writeTimeTagToDefaultDB
  60. {
  61.    char timetag[20];
  62.    char *buf;
  63.  
  64.    if(nntpHostName!=NULL){
  65.       makeTimeTag(timetag);
  66.       buf= (char *)malloc((strlen(nntpHostName)+15)*sizeof(char));
  67.       sprintf(buf,"NewGroups %s",nntpHostName);
  68.       [NXApp setDefault:buf to:timetag];
  69.       free(buf);
  70.    }
  71.    
  72.    return self;
  73. }
  74.  
  75. - (const char *)timeTag
  76. {
  77.    char *buf1;
  78.    const char *buf2; 
  79.  
  80.    buf1=(char *)malloc((strlen(nntpHostName)+15)*sizeof(char));
  81.    sprintf(buf1,"NewGroups %s",nntpHostName);
  82.    buf2=[NXApp defaultValue:buf1];
  83.    free(buf1);
  84.    
  85.    return buf2;
  86. }
  87.  
  88. - openServerNamed:(const char *)serverName
  89. {
  90.     struct servent    *nntpEnt;
  91.     struct protoent    *nntpProtoEnt;
  92.     struct hostent    *nntpHost;
  93.     struct sockaddr_in    nntpServer;
  94.     int         statusCode;
  95.     char         inCodeText[BUFFER_SIZE];
  96.     BOOL notUseNov;
  97.     int stype;
  98.      char *xuser,*xpasswd;
  99.      const char *buf;
  100.      
  101.     if(nntpHostName!=NULL){
  102.         if(serverName!=nntpHostName){
  103.             free(nntpHostName);
  104.               nntpHostName = NXCopyStringBuffer(serverName);
  105.          }
  106.      }
  107.     else
  108.           nntpHostName = NXCopyStringBuffer(serverName);
  109.  
  110.     // store nntp hostname for reconnecting    
  111.     
  112.     if ((nntpEnt = getservbyname("nntp", "tcp")) == NULL) {
  113.      NXRunAlertPanel("ALEXANDRA","Cannot find nntp service in 'services' database.",NULL,NULL,NULL);
  114.       return nil;
  115.     }
  116.  
  117.     if ((nntpProtoEnt = getprotobyname(nntpEnt->s_proto)) == NULL) {
  118.     NXRunAlertPanel("ALEXANDRA","Cannot lookup protocol type.",NULL,NULL,NULL);
  119.     return nil;
  120.     }
  121.  
  122.     if ((readSocket = socket(AF_INET, SOCK_STREAM, 
  123.                              nntpProtoEnt->p_proto))== -1) {
  124.     NXRunAlertPanel("ALEXANDRA","Cannot create socket to news server.",NULL,NULL,NULL);
  125.     return nil;
  126.     }
  127.  
  128.     if ((nntpHost = gethostbyname((char *)serverName)) == NULL) {
  129.     NXRunAlertPanel("ALEXANDRA","Cannot find address of host %s.",NULL,NULL,NULL, serverName);
  130.     return nil;
  131.     }
  132.  
  133.   nntpServer.sin_family = nntpHost->h_addrtype;
  134.   bcopy(nntpHost->h_addr, &nntpServer.sin_addr, nntpHost->h_length);
  135.   nntpServer.sin_port = nntpEnt->s_port;
  136.   if ((connect(readSocket, (struct sockaddr *) &nntpServer,
  137.     sizeof(nntpServer))) == -1) {
  138.     NXRunAlertPanel("ALEXANDRA","Cannot connect to news server on %s.",NULL,NULL,NULL, serverName);
  139.     return nil;
  140.   }
  141.   
  142.   writeSocket=dup(readSocket);
  143.   
  144.   nntpIn = fdopen(readSocket, "r");
  145.   nntpOut = fdopen(writeSocket,"w");
  146.  
  147.   if(fgets(inCodeText,sizeof(inCodeText),nntpIn)==NULL){
  148.      NXRunAlertPanel("ALEXANDRA","Unable to open server socket.",NULL,NULL,NULL);
  149.      return nil;
  150.   }
  151.  
  152.   statusCode=atoi(inCodeText);
  153.   switch (statusCode) {
  154.     case OK_CANPOST:
  155.     case OK_NOPOST:
  156.       canPost = (statusCode == OK_CANPOST);
  157.         if(!canPost)
  158.             NXLogError("You are not allowed to post");
  159.       break;
  160.     default:
  161.       NXRunAlertPanel("ALEXANDRA","News server on %s responded incorrectly.",NULL,NULL,NULL, serverName);
  162.       return nil;
  163.       break;
  164.   }
  165.   
  166.   echoSocket=[NXApp defaultBoolValue:"EchoSocket"];
  167.  
  168.   [self issueCommand:"mode reader"];
  169.   
  170.   statusCode=[self issueCommand:"xover"];
  171.   if(statusCode==-1) return self;
  172.   novSupported=(statusCode==ERR_NCING);
  173.   sprintf(inCodeText,"DoNotUseNov %s",nntpHostName);
  174.   notUseNov=[NXApp defaultBoolValue:inCodeText];
  175.   if((notUseNov==YES)||(novSupported==FALSE))
  176.       novSupported=FALSE;
  177.    else
  178.       novSupported=TRUE;
  179.  
  180.   sprintf(inCodeText,"DoNotPrefetchFrom %s",nntpHostName);
  181.   doNotPrefetchFROM=[NXApp defaultBoolValue:inCodeText];
  182.   sprintf(inCodeText,"DoNotPrefetchMsgid %s",nntpHostName);
  183.   doNotPrefetchMSGID=[NXApp defaultBoolValue:inCodeText];
  184.   sprintf(inCodeText,"DoNotPrefetchRefs %s",nntpHostName);
  185.   doNotPrefetchREFS=[NXApp defaultBoolValue:inCodeText];
  186.   sprintf(inCodeText,"DoNotPrefetchDate %s",nntpHostName);
  187.   doNotPrefetchDATE=[NXApp defaultBoolValue:inCodeText];
  188.   sprintf(inCodeText,"DoNotPrefetchLines %s",nntpHostName);
  189.   doNotPrefetchLINES=[NXApp defaultBoolValue:inCodeText];
  190.  
  191.   if(!novSupported){ 
  192.      sprintf(inCodeText,"SortType %s",nntpHostName);
  193.      stype=[NXApp defaultIntValue:inCodeText];
  194.      if(((stype==SORT_BY_DATE)&&doNotPrefetchDATE)||
  195.         ((stype==SORT_BY_REAL_NAME)&&doNotPrefetchFROM)){
  196.            sprintf(inCodeText,"SortType %s",nntpHostName);
  197.            [NXApp setDefault:inCodeText toInt:SORT_BY_NUMBER];
  198.      }
  199.   }
  200.  
  201.     //AUTHENTICATION
  202.     sprintf(inCodeText,"Authuser %s",serverName);
  203.     buf=[NXApp defaultValue:inCodeText];
  204.     if(!buf)
  205.         buf="";
  206.     xuser=NXCopyStringBuffer(buf);
  207.     
  208.     sprintf(inCodeText,"Authpasswd %s",serverName);
  209.     buf=[NXApp defaultValue:inCodeText];
  210.     if(!buf)
  211.         buf="";
  212.     xpasswd=NXCopyStringBuffer(buf);
  213.     
  214.     if(*xuser && *xpasswd){
  215.         sprintf(inCodeText,"authinfo user %s",xuser);    
  216.         statusCode=[self issueCommand: inCodeText];    
  217.         if(statusCode==NEED_AUTHDATA){
  218.             sprintf(inCodeText,"authinfo pass %s",xpasswd);
  219.             statusCode=[self issueCommand:inCodeText];
  220.             if(statusCode!=OK_AUTH) 
  221.                 NXRunAlertPanel("ALEXANDRA","NNTP command failed (status %d). Authorization user %s/pass %s rejected. Access to host %s, if allowed, may be limited", NULL,
  222.                         NULL,NULL, statusCode, xuser, xpasswd, nntpHostName);
  223.         } 
  224.         else{
  225.             NXRunAlertPanel("ALEXANDRA","NNTP command failed (status %d). Auth for user %s rejected. Access to host %s, if allowed, may be limited", 
  226.                                         NULL,NULL,NULL, statusCode, xuser, nntpHostName);
  227.  
  228.         }
  229.     }
  230.   
  231.   free(xuser);
  232.   free(xpasswd);
  233.   
  234.   return self;
  235. }
  236.  
  237. - (const char *)serverName
  238. {
  239.    return nntpHostName;
  240. }
  241.  
  242. - reconnectServer
  243. {     
  244.     if(nntpIn!=NULL) fclose(nntpIn);
  245.     if(nntpOut!=NULL) fclose(nntpOut);
  246.  
  247.     if (nntpHostName == NULL) {
  248.     return nil;
  249.     }
  250.     return [self openServerNamed:nntpHostName];
  251. }
  252.  
  253.  
  254. - free
  255. {
  256.     if(nntpOut!=NULL){
  257.        if(fprintf(nntpOut,"quit\r\n")==0)
  258.           fflush(nntpOut);
  259.        fclose(nntpOut);
  260.     }
  261.     if(nntpIn!=NULL) fclose(nntpIn);
  262.     free(nntpHostName);
  263.     if(statusLine!=NULL)
  264.        free(statusLine);
  265.     return [super free];
  266. }
  267.  
  268. - (BOOL)canPost
  269. {
  270.    return canPost;
  271. }
  272.  
  273. - (BOOL)usesNov
  274. {
  275.    return novSupported;
  276. }
  277.  
  278. - closeServerDelayed
  279. {
  280.    [mainWindowController perform:@selector(freeAndClose:) with:self afterDelay:0.0 cancelPrevious:FALSE];
  281.    fclose(nntpIn);
  282.    fclose(nntpOut);
  283.    return self;
  284. }
  285.  
  286. - (char *)getNNTPLine
  287. {
  288.    char *buffer;
  289.    int n;
  290.  
  291.    buffer=readline(nntpIn);
  292.    if(buffer==NULL){         
  293.        [self closeServerDelayed];
  294.          EM_ERROR(ENNTPUnexpectedSocketClose,NULL,NULL);
  295.        return NULL;
  296.     }
  297.     n = strlen(buffer);
  298.      if(echoSocket)
  299.          printf("%s",buffer);
  300.     if (n >= 2 && buffer[n-1] == '\n' && buffer[n-2] == '\r')
  301.     buffer[n-2]='\0';
  302.  
  303.     return buffer;
  304. }
  305.  
  306. - (int)getStatus
  307. {
  308.     const char *buf=[self getNNTPLine];
  309.      
  310.     if(statusLine!=NULL)
  311.        free(statusLine);
  312.      
  313.     statusLine=NXCopyStringBuffer(buf);
  314.     return atoi(statusLine);
  315. }
  316.  
  317. - (int)issueCommand:(char *)command
  318. {  
  319.    int status,newStatus;
  320.  
  321.    // issue command
  322.    fprintf(nntpOut,"%s\r\n",command);
  323.    fflush(nntpOut);
  324.    if(echoSocket)
  325.         printf("%s\n",command);
  326.         
  327.    // get answer
  328.    status=[self getStatus];
  329.  
  330.    //recover if timeout
  331.    if((status==ERR_FAULT)&& instr(statusLine,"timeout",FALSE)){
  332.        if(handling_timeout==TRUE){
  333.           [self closeServerDelayed];
  334.              EM_ERROR(ENNTPCouldNotReconnect,NULL,NULL);
  335.           return -1;  //fatal error: could not reconnect
  336.        }
  337.        handling_timeout = TRUE;
  338.  
  339.        if([self reconnectServer]!=nil){
  340.           if(strncmp(command,"group",5)!=0)
  341.              if(currentGroup!=nil)
  342.                 if([self requestGroup:currentGroup]!=OK_GROUP){
  343.                    [self closeServerDelayed];
  344.                          EM_ERROR(ENNTPCouldNotReconnect,NULL,NULL);
  345.                    return -1; //fatal error: could not sync
  346.                 } 
  347.        }
  348.        else{
  349.           [self closeServerDelayed];
  350.              EM_ERROR(ENNTPCouldNotReconnect,NULL,NULL);
  351.           return -1; //fatal error: could not reconnect
  352.        }
  353.  
  354.        newStatus=[self issueCommand:command];
  355.        handling_timeout = FALSE;
  356.        
  357.        return newStatus;
  358.    }
  359.  
  360.    return status;
  361. }
  362.  
  363. - (int)requestGroup:(Newsgroup *)aGroup
  364. {
  365.   char    inCodeText[BUFFER_SIZE];
  366.   long    first, last, numArticles;
  367.   int statusCode;
  368.  
  369.   sprintf(inCodeText, "group %s", [aGroup stringValue]);
  370.   statusCode=[self issueCommand:inCodeText];
  371.  
  372.   if(statusCode==OK_GROUP){
  373.       sscanf(statusLine, "%*d %ld %ld %ld", &numArticles, &first, &last);
  374.       [[aGroup setMin:first] setMax:last];
  375.       currentGroup=aGroup;
  376.   }
  377.   else if(statusCode==ERR_COMMAND)
  378.      NXLogError("NNTP server does not recognize GROUP command.");
  379.       
  380.   return statusCode;
  381. }
  382.  
  383. - unselectCurrentGroup
  384. {
  385.    currentGroup=nil;
  386.  
  387.    return self;
  388. }
  389.  
  390. - loadStorageWithGroupList:(Storage *)array
  391. {
  392.   
  393.   char    *inCodeText;
  394.   char group[BUFFER_SIZE];
  395.   newsgroupDesc ngDesc;
  396.  
  397.   while(((inCodeText=[self getNNTPLine])!=NULL) &&
  398.         (NNTP_LIST_END(inCodeText)==FALSE)){
  399.      sscanf(inCodeText,"%s %ld %ld %c",
  400.             group, &(ngDesc.max), &(ngDesc.min), &(ngDesc.post));
  401.      ngDesc.groupname=NXCopyStringBuffer(group);
  402.      if((ngDesc.post!='y')&&(ngDesc.post!='m')) 
  403.         ngDesc.post='n';
  404.      [array addElement:&ngDesc];
  405.   }
  406.  
  407.   return self;
  408. }
  409.  
  410. - scanActive:(Storage *)theArray
  411. {
  412.   int        statusCode;
  413.  
  414.   [self writeTimeTagToDefaultDB];
  415.  
  416.   statusCode=[self issueCommand:"list"];
  417.  
  418.   if (statusCode != OK_GROUPS) {
  419.     EM_ERROR(ENNTPErrorPerformingCommand,"LIST",(void *)atoi(statusLine));
  420.     return self;
  421.   }
  422.   [self loadStorageWithGroupList:theArray];
  423.   return self;
  424. }
  425.  
  426. - scanNewGroups:(Storage *)theArray
  427. {
  428.   int statusCode;
  429.   char inCodeText[BUFFER_SIZE];
  430.  
  431.   sprintf(inCodeText,"newgroups %s",[self timeTag]);
  432.   statusCode=[self issueCommand:inCodeText];
  433.   [self writeTimeTagToDefaultDB];
  434.  
  435.   if (statusCode!= OK_NEWGROUPS){
  436.      [self closeServerDelayed];
  437.       EM_ERROR(ENNTPErrorPerformingCommand,"NEWGROUPS",(void *)atoi(statusLine));
  438.      return self;
  439.   }
  440.   [self loadStorageWithGroupList:theArray];
  441.   return self;
  442. }
  443.  
  444. - fetchSubjectHeaders:(Storage *)array from:(long)first to:(long)last
  445. {
  446.  
  447.    if(novSupported==TRUE)
  448.       [self xover:array from:first to:last];
  449.    else
  450.        [self xhdr:array from:first to:last];
  451.         
  452.     return self;
  453. }
  454.  
  455. - xhdr:(Storage *)array from:(long)first to:(long)last
  456. {
  457.    int    statusCode,i;
  458.    char    *inCodeText;
  459.    char    *text,*p;
  460.    char buf[255];
  461.    subjectDesc *defaultDesc;
  462.    subjectDesc *desc;
  463.    BOOL firstHeader=TRUE;
  464.    long number;
  465.    int pos_in_array;
  466.  
  467.    defaultDesc=(subjectDesc *)calloc(1,sizeof(subjectDesc));
  468.    for(i=0;i<XOVER_COUNT+1;i++){
  469.       if((i==FROM)&&(doNotPrefetchFROM==TRUE)) continue;
  470.       if((i==MSG_ID)&&(doNotPrefetchMSGID==TRUE)) continue;
  471.       if((i==REFS)&&((doNotPrefetchREFS==TRUE)||(doNotPrefetchMSGID==TRUE))) continue;
  472.       if((i==DATE)&&(doNotPrefetchDATE==TRUE)) continue;
  473.       if((i==XOVER_COUNT)&&(doNotPrefetchLINES==TRUE)) continue;
  474.  
  475.       if(i<XOVER_COUNT)
  476.          sprintf(buf, "xhdr %s %ld-%ld",h_field_name[i], first, last);
  477.       else
  478.          sprintf(buf,"xhdr Lines %ld-%ld",first,last);
  479.  
  480.       statusCode=[self issueCommand:buf];
  481.       if (statusCode != OK_HEAD){
  482.          if(statusCode == ERR_COMMAND)
  483.                 EM_ERROR(ENNTPCommandNotRecognised,"XHDR",NULL);
  484.          else
  485.                 EM_ERROR(ENNTPErrorPerformingCommand,"XHDR",(void *)atoi(statusLine));
  486.          return self;
  487.       }
  488.       pos_in_array=0;
  489.       while(((inCodeText=[self getNNTPLine])!=NULL) && (NNTP_LIST_END(inCodeText)==FALSE)){
  490.          sscanf(inCodeText,"%ld",&number);
  491.          text=NXCopyStringBuffer(strchr(inCodeText,' ')+1);
  492.          if(strcmp(text,"(none)")==0)
  493.             text[0]='\0';
  494.          // convert to iso
  495.             p=text;
  496.             while(*p){
  497.                 *p=c_iso2next[(unsigned char)(*p)];
  498.                 p++;
  499.             }
  500.             
  501.          if(firstHeader==TRUE){
  502.             defaultDesc->number=number;
  503.             defaultDesc->fieldBody=(char **)calloc(XOVER_COUNT,sizeof(char *));
  504.             [array addElement:defaultDesc];
  505.          }
  506.          desc=(subjectDesc *)[array elementAt:(unsigned int)pos_in_array];
  507.          NX_ASSERT(desc!=NULL,"INTERNAL ERROR:XHDR confusion");
  508.          NX_ASSERT(desc->number==number,"INTERNAL ERROR:XHDR mismatch");
  509.          if(i<XOVER_COUNT)
  510.             desc->fieldBody[i]=text;
  511.          else{
  512.             desc->lines=atoi(text);
  513.             free(text);
  514.          }
  515.          pos_in_array++;     
  516.       }
  517.       firstHeader=FALSE;
  518.    }
  519.   free(defaultDesc);
  520.   return self;
  521. }
  522.  
  523.  
  524.  
  525. - xover:(Storage *)array from:(long)first to:(long)last
  526. {
  527.   int    statusCode;
  528.   char    inCodeText[BUFFER_SIZE];
  529.   char  *line_buffer;
  530.   int i,j,a;
  531.   subjectDesc subDesc;
  532.  
  533.   sprintf(inCodeText, "xover %ld-%ld", first, last);
  534.   statusCode=[self issueCommand:inCodeText];
  535.   if (statusCode != OK_XOVER){
  536.      if(statusCode == ERR_COMMAND)
  537.          EM_ERROR(ENNTPCommandNotRecognised,"XOVER",NULL);
  538.     else
  539.           EM_ERROR(ENNTPErrorPerformingCommand,"XOVER",(void *)atoi(statusLine));
  540.     return self;
  541.   }
  542.   
  543.   while(((line_buffer=[self getNNTPLine])!=NULL) &&(NNTP_LIST_END(line_buffer)==FALSE)){
  544.      subDesc.fieldBody=(char **)calloc(XOVER_COUNT,sizeof(char *));
  545.      sscanf(line_buffer,"%ld",&(subDesc.number));
  546.      i=0;j=0;a=0;
  547.      while(j<XOVER_COUNT+2){
  548.         a=i;
  549.         while((line_buffer[i]!='\0')&&(line_buffer[i]!='\t')){
  550.              char *c=line_buffer+i;
  551.               *c=c_iso2next[(unsigned char)(*c)];
  552.            i++;
  553.           }
  554.         if((j>0)&&(j<XOVER_COUNT+1)){
  555.            subDesc.fieldBody[j-1]=NULL;
  556.            if(i>a){
  557.               subDesc.fieldBody[j-1]=(char *)malloc((i-a+1)*sizeof(char));
  558.               strncpy(subDesc.fieldBody[j-1],line_buffer+a,i-a);
  559.               subDesc.fieldBody[j-1][i-a]='\0';
  560.            }
  561.         }
  562.         j++;
  563.         i++;
  564.      }
  565.       subDesc.artsize=0; subDesc.lines=0;
  566.      sscanf(line_buffer+a,"%d\t%d",&(subDesc.artsize),&(subDesc.lines));
  567.      [array addElement:&subDesc];
  568.   }
  569.  
  570.   return self;
  571. }
  572.  
  573. - (int)loadArticleHeader:(Article *)article toString:(char **)aString
  574. {
  575.     int    statusCode;
  576.     char    inCodeText[BUFFER_SIZE];
  577.     long i;
  578.     char *buf,*buf2;
  579.     int len,maxlen;
  580.       NXStream *theStream;
  581.  
  582.   //Read HEAD
  583.   sprintf(inCodeText,"head %ld", [article number]);
  584.   statusCode=[self issueCommand:inCodeText];
  585.   if(statusCode==ERR_NOARTIG)
  586.      return statusCode;
  587.   if(statusCode!=OK_HEAD){
  588.       EM_ERROR(ENNTPErrorPerformingCommand,"HEAD",NULL);
  589.      return statusCode;
  590.   }
  591.  
  592.   theStream=NXOpenMemory(NULL,0,NX_WRITEONLY);
  593.   if(MapNntpToStream(nntpIn,theStream,echoSocket)==-1){
  594.      [self closeServerDelayed];
  595.      EM_ERROR(ENNTPUnexpectedSocketClose,NULL,NULL);
  596.      return -1;
  597.   }
  598.   
  599.   [article parseHeader:theStream];
  600.   
  601.   NXSeek(theStream,0,NX_FROMEND);
  602.   NXPutc(theStream,(int)'\0');
  603.   // convert to iso
  604.   NXGetMemoryBuffer(theStream,&buf,&len,&maxlen);
  605.   buf2=buf;
  606.   for(i=0;i<len;i++){
  607.      *buf2=(char)c_iso2next[(unsigned char)(*buf2)];
  608.      buf2++;
  609.   }
  610.   
  611.   *aString=NXCopyStringBuffer(buf);
  612.   NXCloseMemory(theStream,NX_FREEBUFFER);
  613.  
  614.   return statusCode;
  615. }
  616.  
  617. - (int)loadArticleBody:(Article *)article toString:(char **)aString
  618. {
  619.     int    statusCode;
  620.     char    inCodeText[BUFFER_SIZE];
  621.     long i;
  622.     char *buf,*buf2;
  623.     int len,maxlen;
  624.       NXStream *theStream;
  625.  
  626.   //Read HEAD
  627.   sprintf(inCodeText,"body %ld", [article number]);
  628.   statusCode=[self issueCommand:inCodeText];
  629.   if(statusCode==ERR_NOARTIG)
  630.      return statusCode;
  631.   if(statusCode!=OK_BODY){
  632.       EM_ERROR(ENNTPErrorPerformingCommand,"HEAD",NULL);
  633.      return statusCode;
  634.   }
  635.  
  636.   theStream=NXOpenMemory(NULL,0,NX_WRITEONLY);
  637.   if(MapNntpToStream(nntpIn,theStream,echoSocket)==-1){
  638.      [self closeServerDelayed];
  639.      EM_ERROR(ENNTPUnexpectedSocketClose,NULL,NULL);
  640.      return -1;
  641.   }
  642.   NXSeek(theStream,0,NX_FROMEND);
  643.   NXPutc(theStream,(int)'\0');
  644.   
  645.   // convert to iso
  646.   NXGetMemoryBuffer(theStream,&buf,&len,&maxlen);
  647.   buf2=buf;
  648.   for(i=0;i<len-1;i++){
  649.      *buf2=(char)c_iso2next[(unsigned char)(*buf2)];
  650.      buf2++;
  651.   }
  652.   
  653.   *aString=NXCopyStringBuffer(buf);
  654.   NXCloseMemory(theStream,NX_FREEBUFFER);
  655.   
  656.   return statusCode;
  657. }
  658.  
  659. - (int)postArticle:(NXStream *)theStream
  660. {
  661.     int statusCode;
  662.  
  663.     const char    *streambuf;
  664.     char *buf,*copiedbuf;
  665.     int        max, len,i;
  666.     int        bytesSent;
  667.  
  668.     NXGetMemoryBuffer(theStream, &streambuf, &len, &max);
  669.     buf=(char *)malloc((len+4)*sizeof(char));
  670.     strncpy(buf,streambuf,len);
  671.  
  672.     statusCode=[self issueCommand:"post"];
  673.     switch (statusCode) {
  674.     case CONT_POST:
  675.         break; 
  676.     case ERR_NOPOST:
  677.         NXRunAlertPanel("ALEXANDRA","NNTP POST command failed.  %d  posting not allowed.",
  678.     NULL,NULL,NULL, statusCode);
  679.         return statusCode;
  680.     default:
  681.         // This should never occur!
  682.         NXRunAlertPanel("ALEXANDRA",
  683.     "NNTP POST command failed. Status code %d.",NULL,NULL,NULL, statusCode);
  684.         return statusCode;
  685.     }
  686.  
  687.     // Convert to iso
  688.     for(i=0;i<len;i++)
  689.       buf[i]=(char)c_next2iso[(unsigned char)buf[i]];
  690.  
  691.     // append .
  692.     if(buf[len-1]!='\n'){
  693.        buf[len]='\n';
  694.        len++;
  695.     }
  696.     buf[len]='.';
  697.     buf[len+1]='\n';
  698.     len+=2;
  699.     buf[len]='\0';
  700.      
  701.     // send
  702.     copiedbuf=buf;
  703.     while (len > 0) {
  704.         bytesSent = send(writeSocket, buf, len, 0);
  705.         buf += bytesSent;
  706.         len -= bytesSent;
  707.     }
  708.      
  709.     if(echoSocket)
  710.          printf("%s",copiedbuf);
  711.         
  712.     free(copiedbuf);
  713.  
  714.     statusCode=[self getStatus];
  715.     switch (statusCode) {
  716.     case OK_POSTED:
  717.        return statusCode;
  718.     case ERR_POSTFAIL:
  719.        NXRunAlertPanel("ALEXANDRA","%d  posting failed. %s.",
  720.         NULL,NULL,NULL, statusCode,statusLine);
  721.         return statusCode;
  722.     default:
  723.         // This should never occur!
  724.         NXRunAlertPanel("ALEXANDRA","NNTP POST command failed. Status Code %d.",NULL,NULL,NULL, statusCode);
  725.         return statusCode;
  726.     }
  727. }
  728.  
  729. - (BOOL)slowLink
  730. {
  731.    return isSlowLink;
  732. }
  733.  
  734. - setSlowLink:(BOOL)v
  735. {
  736.    isSlowLink=v;
  737.    return self;
  738. }
  739.  
  740. - killFile
  741. {
  742.    return killFile;
  743. }
  744.  
  745. - (BOOL)doesPrefetchFrom
  746. {
  747.    return (!doNotPrefetchFROM);
  748. }
  749.  
  750. - (BOOL)doesPrefetchDate
  751. {
  752.    return (!doNotPrefetchDATE);
  753. }
  754.  
  755. - (BOOL)findArticle:(const char *)msgid inGroups:(char ***)groups
  756. {
  757.     char buf[BUFFER_SIZE];
  758.     int numGroups,status;
  759.     char *buff,*aString;
  760.     
  761.     sprintf(buf,"XHDR Newsgroups %s",msgid);
  762.     status=[self issueCommand:buf];
  763.  
  764.     if(status==OK_HEAD){
  765.         char *gstring=strchr([self getNNTPLine],' ')+1;
  766.         
  767.         // count the number of groups
  768.         numGroups=0;
  769.         buff=gstring;
  770.         while(*buff!='\r'){
  771.             if(*buff==',')
  772.                 numGroups++;
  773.             buff++;
  774.         }
  775.         
  776.         // make list
  777.         *groups=calloc(numGroups+3,sizeof(void *));
  778.         numGroups=0;
  779.         for(aString=strtok(gstring,",");aString;aString=strtok(NULL,",")){
  780.             (*groups)[numGroups]=NXCopyStringBuffer(aString);
  781.             numGroups++;
  782.         }
  783.         
  784.         //remove the rest until .
  785.         [self getNNTPLine];
  786.         
  787.         return TRUE;
  788.     }
  789.     
  790.     return FALSE;
  791. }
  792.  
  793. @end
  794.