home *** CD-ROM | disk | FTP | other *** search
/ Otherware / Otherware_1_SB_Development.iso / mac / util / comm / news102.sit / NewsWatcher / source / nntplow.c < prev    next >
Text File  |  1991-04-03  |  16KB  |  704 lines

  1. /*----------------------------------------------------------
  2. #
  3. #    NewsWatcher    - Macintosh NNTP Client Application
  4. #
  5. #    Written by Steven Falkenburg
  6. #    ⌐1990 Apple Computer, Inc.
  7. #
  8. #-----------------------------------------------------------
  9. #
  10. #    nntplow.c
  11. #
  12. #    The nntplow code module contains the network code which
  13. #    communicates with the remote NNTP server to get/post
  14. #    news articles or retrieve subject or group lists.
  15. #
  16. #-----------------------------------------------------------*/
  17.  
  18. #pragma segment lowlevel
  19.  
  20. #include "compat.h"
  21.  
  22. #ifdef PROTOS
  23.  
  24. #include <Types.h>
  25. #include <Memory.h>
  26. #include <QuickDraw.h>
  27. #include <OSUtils.h>
  28. #include <CursorCtl.h>
  29. #include <Packages.h>
  30. #include <ToolUtils.h>
  31. #include <Lists.h>
  32. #include <Strings.h>
  33.  
  34. #endif
  35.  
  36. #include <string.h>
  37.  
  38. #include "nntp.h"
  39. #include "MacTCPCommonTypes.h"
  40. #include "TCPPB.h"
  41. #include "netstuff.h"
  42. #include "TCPHi.h"
  43. #include "FTPLow.h"
  44. #include "NNTPLow.h"
  45. #include "miscstuff.h"
  46.  
  47. unsigned long    gConnID = 0;        /* connection id for nntp connection */
  48. short            gNumGroups;            /* number of newsgroups */
  49. TGroup            *gGroupList;        /* pointer to first group */
  50. static TGroup    **groupHandle;        /* handle to first group */
  51. unsigned long    gNNTPAddress;        /* address of NNTP server */
  52.  
  53. OSErr GrowDataStructure(void);
  54.  
  55.  
  56. /*    GrowDataStructure dynamically grows the group data structure, re-assigning
  57.     gGroupList after locking the handle.
  58. */
  59.  
  60. OSErr GrowDataStructure(void)
  61. {
  62.     Size prevSize;
  63.     OSErr err;
  64.     
  65.     HUnlock((Handle)groupHandle);
  66.     prevSize = GetHandleSize((Handle)groupHandle);
  67.     MySetHandleSize((Handle)groupHandle,prevSize+sizeof(TGroup));
  68.     if ((err = MyMemErr())!=noErr) {
  69.         MoveHHi((Handle)groupHandle);
  70.         HLock((Handle)groupHandle);
  71.         gGroupList = *groupHandle;
  72.         return err;
  73.     }
  74.     MoveHHi((Handle)groupHandle);
  75.     HLock((Handle)groupHandle);
  76.     gGroupList = *groupHandle;
  77. }
  78.  
  79.  
  80. /*    StartNNTP initializes MacTCP, opens the NNTP connection, and
  81.     gets the list of groups from the NNTP server.
  82. */
  83.  
  84. OSErr StartNNTP(void)
  85. {
  86.     OSErr err;
  87.     
  88.     StatusWindow("Getting NNTP Address...",-1);
  89.     GetConfiguration();
  90.     
  91.     StatusWindow("Opening TCP Driver...",-1);
  92.     GiveTime(0);    
  93.     if ((err = InitNNTP()) == noErr &&
  94.         (err = FTPInit((ProcPtr)StatusProc)) == noErr &&
  95.         (StatusWindow("Opening Connection...",-1)) &&
  96.         (err = OpenNewsConnection(gNNTPAddress)) == noErr &&
  97.         (StatusWindow("Getting Groups From Net...",0)) &&
  98.         (err = GetGroupList(&gNumGroups)) == noErr &&
  99.         (StatusWindow("Getting Groups From Net...",100)))
  100.             ;
  101.     GiveTime(0);
  102.     return err;
  103. }
  104.  
  105.  
  106. /*    InitNNTP is called to initialize all drivers/etc necessary for
  107.     operation and maintenence of the NNTP connection.
  108. */
  109.  
  110. OSErr InitNNTP(void)
  111. {
  112.     groupHandle = (TGroup **)MyNewHandle(sizeof(TGroup));
  113.     if (MyMemErr() != noErr)
  114.         return MyMemErr();
  115.     HLock((Handle)groupHandle);
  116.     gGroupList = *groupHandle;
  117.     return InitNetwork();
  118. }
  119.  
  120.  
  121. /*    ResetConnection closes and re-opens the NNTP connection.  This routine
  122.     is called when a network error occurs.
  123. */
  124.  
  125. OSErr ResetConnection(void)
  126. {
  127.     OSErr err;
  128.     Str255 tmpStr;
  129.     extern Boolean gCancel;
  130.     
  131.     gCancel = false;
  132.     AbortNewsConnection();
  133.     StatusWindow("Re-establishing Connection...",-1);
  134.     if ((err = InitNetwork()) == noErr &&
  135.         (err = FTPInit((ProcPtr)StatusProc)) == noErr &&
  136.         (err = OpenNewsConnection(gNNTPAddress)) == noErr)
  137.             ;
  138.     else {
  139.         NumToString(err,tmpStr);
  140.         ParamText("\pConnection could not established.",tmpStr,"\p","\p");
  141.         CautionAlert(kErrDlg,nil);
  142.     }        
  143.     return err;
  144. }
  145.  
  146.  
  147. /*    GetHello gets the initial status back from an open NNTP connection
  148.     to determine whether or not the connection was opened sucessfully.
  149. */
  150.  
  151. OSErr GetHello(void)
  152. {
  153.     OSErr err;
  154.     Ptr data;
  155.     unsigned short length;
  156.     
  157.     data = MyNewPtr(kBufLen);
  158.     if (MyMemErr() != noErr)
  159.         return MyMemErr();
  160.         
  161.     length = kBufLen;
  162.     
  163.     err = RecvData(gConnID,data,&length,true);
  164.     
  165.     if (*data != '2')
  166.         err = -1;
  167.     
  168.     MyDisposPtr(data);
  169.     
  170.     return err;
  171. }
  172.  
  173.  
  174. /*    OpenNewsConnection opens the conneciton to the remote NNTP server.
  175. */
  176.  
  177. OSErr OpenNewsConnection(unsigned long address)
  178. {
  179.     unsigned long recvLen;
  180.     OSErr err;
  181.     
  182.     recvLen = kBufLen;
  183.     if ((err = CreateStream(&gConnID,recvLen)) != noErr)
  184.         return err;
  185.     if ((err = OpenConnection(gConnID,address,kNNTPPort,10)) == noErr)
  186.         err = GetHello();
  187.  
  188.     return err;
  189. }
  190.  
  191.  
  192. /*    CloseConnection closes the connection to the NNTP server.  This is normally
  193.     called when the program is terminating.
  194. */
  195.  
  196. OSErr CloseNewsConnection(void)
  197. {
  198.     OSErr err;
  199.     
  200.     StatusWindow("Closing NNTP Connection...",-1);
  201.     err = CloseConnection(gConnID);
  202.     err = ReleaseStream(gConnID);
  203.     return err;
  204. }
  205.  
  206.  
  207. /*    AbortNewsConnection forcibly closes a connection to the NNTP server, usually
  208.     before resetting the connection.
  209. */
  210.  
  211. OSErr AbortNewsConnection(void)
  212. {
  213.     StatusWindow("Aborting NNTP Connection...",-1);
  214.     AbortConnection(gConnID);
  215.     return ReleaseStream(gConnID);
  216. }
  217.  
  218.  
  219. /*    GetGroupList gets the list of all known newsgroups from the NNTP server.
  220. */
  221.  
  222. OSErr GetGroupList(short *numGroups)
  223. {
  224.     OSErr err;
  225.     char *current;
  226.     char curGroup[256];
  227.     char *data;
  228.     short nameLen = 0;
  229.     unsigned short length = (unsigned short) kBufLen;
  230.     Boolean done = false,start = true;
  231.     short state = cNormal;
  232.     Str255 sendData[2];
  233.     
  234.     *numGroups = 0;
  235.     nameLen = 0;
  236.     
  237.     strcpy((char *)sendData[0],"LIST");
  238.     strcpy((char *)sendData[1],CRLF);
  239.     
  240.     if ((err = SendMultiData(gConnID,sendData,2,true)) != noErr) {
  241.         ResetConnection();
  242.         return err;
  243.     }
  244.     
  245.     data = MyNewPtr(kBufLen);
  246.     if (MyMemErr() != noErr)
  247.         return MyMemErr();
  248.         
  249.     while ( !done && ((err = RecvData(gConnID,data,&length,true)) == noErr)) {
  250.         for (current = data; (current - data) < length; current++) {
  251.             switch (*current) {
  252.                 case CR:
  253.                     if (state == cLastDot)
  254.                         state = cLastDotCR;
  255.                     else
  256.                         state = cLastCR;
  257.                     break;
  258.                 case LF:
  259.                     if (state == cLastCR) {
  260.                         state = cLastLF;
  261.                         
  262.                         curGroup[nameLen] = '\0';
  263.                         if ((err = GrowDataStructure()) != noErr)
  264.                             return err;
  265.                         if (ParseGroup(curGroup,nameLen,&gGroupList[*numGroups])) {
  266.                             start = false;
  267.                             (*numGroups)++;
  268.                             StatusWindow("Getting Groups From Net...",(short)1+(((float)(*numGroups)/1100.0)*100));
  269.                         }
  270.                         else if (start==true && curGroup[0]!='2') {
  271.                             ResetConnection();
  272.                             done=true;
  273.                         }
  274.                         nameLen = 0; 
  275.                         GiveTime(0);
  276.                     }
  277.                     else if (state == cLastDotCR) {
  278.                         done = true;
  279.                         state = cLastDotCRLF;
  280.                     }
  281.                     break;
  282.                 case '.':
  283.                     if (state == cLastLF)
  284.                         state = cLastDot;
  285.                     else {
  286.                         state = cNormal;
  287.                         curGroup[nameLen++] = *current;
  288.                     }
  289.                     break;
  290.                 default:
  291.                     state = cNormal;
  292.                     curGroup[nameLen++] = *current;
  293.                     break;
  294.             }
  295.         }
  296.         length = (unsigned short) kBufLen;
  297.     }
  298.     MyDisposPtr(data);
  299.     SetCursor(&QDARROW);
  300.     
  301.     if (err != noErr)
  302.         ResetConnection();
  303.         
  304.     return noErr;
  305. }
  306.  
  307.  
  308. /*    ParseGroup parses out the name of a specific group from the receive buffer.
  309.     This is called once per line for the buffer received after a 'LIST' command.
  310. */
  311.  
  312. Boolean ParseGroup(char *groupString,short groupLen,TGroup *groupStorage)
  313. {
  314.     char tmpStr[256],tmpStore[512];
  315.     char *current,*tmpCurrent;
  316.     
  317.     if (groupLen > 0 &&
  318.         !(*groupString >= '0' && *groupString <= '9') &&
  319.         *groupString != '.') {
  320.             if (MyMemErr() != noErr)
  321.                 return false;
  322.             for (current = groupString,tmpCurrent=tmpStore;
  323.                     *current != ' ' && *current;
  324.                     *tmpCurrent++ = *current++);
  325.             *tmpCurrent = '\0';
  326.             strncpy(groupStorage->name,tmpStore,kGNameLen);
  327.             groupStorage->name[kGNameLen-1] = '\0';
  328.             for (current++,tmpCurrent=tmpStr;
  329.                     *current != ' ' && *current;
  330.                     *tmpCurrent++ = *current++);
  331.             *tmpCurrent = '\0';
  332.             StringToNum((StringPtr)c2pstr(tmpStr),&groupStorage->lastMess);
  333.             if (groupStorage->lastMess == 0)
  334.                 groupStorage->lastMess++;
  335.                 
  336.             for (current++,tmpCurrent=tmpStr;
  337.                     *current != ' ' && *current;
  338.                     *tmpCurrent++ = *current++);
  339.             *tmpCurrent = '\0';
  340.             StringToNum((StringPtr)c2pstr(tmpStr),&groupStorage->firstMess);
  341.             if (groupStorage->firstMess == 0)
  342.                 groupStorage->firstMess++;
  343.  
  344.             groupStorage->status = *(current+1);
  345.             
  346.             if (groupStorage->status == 'n')
  347.                 return false;
  348.             
  349.             return true;
  350.     }
  351.     else
  352.         return false;
  353. }
  354.  
  355.  
  356. /*    GetMessages returns the list of subjects from a range of messages in a
  357.     specific newsgroup.  This routine issues the command 'XHDR <first>-<last>'
  358.     to the remote NNTP server.
  359. */
  360.  
  361. OSErr GetMessages(char *newsGroup,long first,long last,TSubject *subjects,long *numSubjects,char *hdrName)
  362. {
  363.     Ptr data;
  364.     Boolean done = false,start;
  365.     OSErr err;
  366.     unsigned short length;
  367.     char *current,*current2,*current3,curSubject[1024],numberStr[256];
  368.     short nameLen;
  369.     short state = cNormal;
  370.     char tmpStr[256];
  371.     long articleNumber;
  372.     Str255 sendData[7];
  373.             
  374.     start = true;
  375.     nameLen = 0;
  376.     *numSubjects = 0;
  377.     data = MyNewPtr(kBufLen);
  378.     if (MyMemErr() != noErr)
  379.         return MyMemErr();
  380.  
  381.     /* send GROUP <groupname> command */ 
  382.     
  383.     GiveTime(0);
  384.  
  385.     strcpy((char *)sendData[0],"GROUP ");
  386.     strcpy((char *)sendData[1],newsGroup);
  387.     strcpy((char *)sendData[2],CRLF);
  388.     
  389.     if ((err = SendMultiData(gConnID,sendData,3,true)) != noErr) {
  390.         ResetConnection();
  391.         return err;
  392.     }
  393.     
  394.     GiveTime(0);
  395.  
  396.     length = (unsigned short) kBufLen;
  397.     if ((err = RecvData(gConnID,data,&length,true)) != noErr) {
  398.         ResetConnection();
  399.         return err;
  400.     }
  401.     
  402.     if (*data != '2') {
  403.         ResetConnection();
  404.         return -1;
  405.     }
  406.     
  407.     /* send XHDR <hdrName> <first>-<last> command */
  408.     
  409.     GiveTime(0);
  410.  
  411.     strcpy((char *)sendData[0],"XHDR ");
  412.     strcpy((char *)sendData[1],hdrName);
  413.     strcpy((char *)sendData[2]," ");
  414.     NumToString(first,(StringPtr)tmpStr);
  415.     p2cstr(tmpStr);
  416.     strcpy((char *)sendData[3],tmpStr);
  417.     strcpy((char *)sendData[4],"-");
  418.     NumToString(last,(StringPtr)tmpStr);
  419.     p2cstr(tmpStr);
  420.     strcpy((char *)sendData[5],tmpStr);
  421.     strcpy((char *)sendData[6],CRLF);
  422.         
  423.     if ((err = SendMultiData(gConnID,sendData,7,true)) != noErr) {
  424.         ResetConnection();
  425.         return err;
  426.     }
  427.  
  428.     GiveTime(0);
  429.     
  430.     while ( !done && ((err = RecvData(gConnID,data,&length,true)) == noErr)) {
  431.         for (current = data; (current - data) < length; current++) {
  432.             switch (*current) {
  433.                 case CR:
  434.                     if (state == cLastDot)
  435.                         state = cLastDotCR;
  436.                     else
  437.                         state = cLastCR;
  438.                     break;
  439.                 case LF:
  440.                     if (state == cLastCR) {
  441.                         state = cLastLF;
  442.                         
  443.                         if (nameLen > 250)
  444.                             nameLen = 250;
  445.                         curSubject[nameLen] = '\0';
  446.                         
  447.                         if (!start && curSubject[0] >= '0' && curSubject[0] <= '9' && (*numSubjects <= (last-first))) {
  448.                                 subjects[*numSubjects].name = (char *) MyNewPtr(nameLen+2);
  449.                                 if (MyMemErr() != noErr)
  450.                                     return MyMemErr();
  451.                                 strncpy(subjects[*numSubjects].name+1,curSubject,nameLen+1);
  452.                                 *(subjects[*numSubjects].name) = ' ';
  453.                                 
  454.                                 for (current2 = numberStr,current3 = curSubject;
  455.                                         *current3 != ' ' && *current3;
  456.                                         *current2++ = *current3++)
  457.                                     ;
  458.                                 *current2 = '\0';
  459.                                 StringToNum((StringPtr)c2pstr(numberStr),&articleNumber);
  460.                                 
  461.                                 subjects[*numSubjects].number = articleNumber;
  462.                                 subjects[*numSubjects].read = false;
  463.                                 
  464.                                 (*numSubjects)++;
  465.                                 StatusWindow("Getting Subject List...",(short)1+(((float)(1+*numSubjects)/(float)(last-first))*100));
  466.                                 GiveTime(0);
  467.  
  468.                         }
  469.                         else if (start && curSubject[0]!='2') {
  470.                             SysBeep(1);
  471.                             done = true;
  472.                         }
  473.                         else
  474.                             start = false;
  475.                             
  476.                         nameLen = 0; 
  477.                     } else if (state == cLastDotCR) {
  478.                         state = cLastDotCRLF;
  479.                         done = true;
  480.                     }
  481.                     break;
  482.                 case '.':
  483.                     if (state == cLastLF)
  484.                         state = cLastDot;
  485.                     else {
  486.                         state = cNormal;
  487.                         curSubject[nameLen++] = *current;
  488.                     }
  489.                     break;
  490.                 default:
  491.                     state = cNormal;
  492.                     curSubject[nameLen++] = *current;
  493.                     break;
  494.             }
  495.         }
  496.         length = (unsigned short) kBufLen;
  497.     }
  498.     MyDisposPtr(data);
  499.     SetCursor(&QDARROW);
  500.     
  501.     if (err != noErr)
  502.         ResetConnection();
  503.         
  504.     return noErr;
  505. }
  506.  
  507.  
  508. /*    GetArticle gets the full text from one article from the NNTP server.
  509.     This text is returned in a buffer which the routine allocates.
  510. */
  511.  
  512. OSErr GetArticle(char *newsGroup,char *article,char **text,long *retLength,long maxLength)
  513. {
  514.     OSErr err;
  515.     Ptr data;
  516.     unsigned short length;
  517.     char *current;
  518.     Boolean done,start;
  519.     short state = cNormal;
  520.     long totalLength = 0;
  521.     Str255 sendData[3];
  522.     Handle txtHndl;
  523.     long curMaxLen;
  524.     
  525.     txtHndl = MyNewHandle(kMaxLength);
  526.     if (MyMemErr() != noErr)
  527.         return MyMemErr();
  528.         
  529.     curMaxLen = kMaxLength;
  530.     HLock(txtHndl);
  531.     *text = *txtHndl;
  532.     
  533.     start = true;
  534.     done = false;
  535.     
  536.     data = MyNewPtr(kBufLen);
  537.     if (MyMemErr() != noErr)
  538.         return MyMemErr();
  539.  
  540.     if (newsGroup) {
  541.         /* send GROUP <groupname> command */
  542.         
  543.         GiveTime(0);
  544.     
  545.         strcpy((char *)sendData[0],"GROUP ");
  546.         strcpy((char *)sendData[1],newsGroup);
  547.         strcpy((char *)sendData[2],CRLF);
  548.         
  549.         if ((err = SendMultiData(gConnID,sendData,3,true)) != noErr) {
  550.             ResetConnection();
  551.             return err;
  552.         }
  553.     
  554.         GiveTime(0);
  555.     
  556.         length = (unsigned short) kBufLen;
  557.         if ((err = RecvData(gConnID,data,&length,true)) != noErr) {
  558.             ResetConnection();
  559.             return err;
  560.         }
  561.         
  562.         if (*data != '2') {
  563.             ResetConnection();
  564.             return -1;
  565.         }
  566.     
  567.     }
  568.     
  569.     strcpy((char *)sendData[0],"ARTICLE ");
  570.     strcpy((char *)sendData[1],article);
  571.     strcpy((char *)sendData[2],CRLF);
  572.     
  573.     if ((err = SendMultiData(gConnID,sendData,3,true)) != noErr) {
  574.         ResetConnection();
  575.         return err;
  576.     }
  577.     
  578.     length = kBufLen;
  579.     while ( !done && ((err = RecvData(gConnID,data,&length,true)) == noErr)) {
  580.         for (current = data; (current - data) < length; current++) {
  581.             if (totalLength >= curMaxLen && totalLength < (maxLength-kMaxLength)) {
  582.                 curMaxLen += kMaxLength;
  583.                 HUnlock(txtHndl);
  584.                 MySetHandleSize(txtHndl,curMaxLen);
  585.                 if ((err = MyMemErr()) != noErr) {
  586.                     MyDisposHandle(txtHndl);
  587.                     MyDisposPtr(data);
  588.                     return err;
  589.                 }
  590.                     
  591.                 HLock(txtHndl);
  592.                 *text = *txtHndl;
  593.             }
  594.             switch (*current) {
  595.                 case CR:
  596.                     if (state == cLastDot)
  597.                         state = cLastDotCR;
  598.                     else
  599.                         state = cLastCR;
  600.                     if (!start) {
  601.                         if (totalLength < maxLength)
  602.                             (*text)[totalLength++] = *current;
  603.                     }
  604.                     break;
  605.                 case LF:
  606.                     if (state == cLastCR) {
  607.                         start = false;
  608.                         state = cLastLF;
  609.                     }
  610.                     else if (state == cLastDotCR) {
  611.                         state = cLastDotCRLF;
  612.                         done = true;
  613.                     }
  614.                     break;
  615.                 case '.':
  616.                     if (state == cLastLF)
  617.                         state = cLastDot;
  618.                     else if (!start) {
  619.                         state = cNormal;
  620.                         if (totalLength < maxLength)
  621.                             (*text)[totalLength++] = *current;
  622.                     }
  623.                     break;
  624.                 default:
  625.                     if (!start) {
  626.                         state = cNormal;
  627.                         if (totalLength < maxLength)
  628.                             (*text)[totalLength++] = *current;
  629.                     }
  630.                     else if (data[0]!='2')
  631.                         done = true;
  632.                     break;
  633.             }
  634.         }
  635.         length = (unsigned short) kBufLen;
  636.         GiveTime(0);
  637.     }
  638.     SetCursor(&QDARROW);
  639.     *retLength = totalLength;
  640.     
  641.     MyDisposPtr(data);
  642.     
  643.     HUnlock(txtHndl);
  644.     *text = MyNewPtr(totalLength);
  645.     if ((err = MyMemErr()) != noErr) {
  646.         MyDisposHandle(txtHndl);
  647.         return err;
  648.     }
  649.     HLock(txtHndl);
  650.     BlockMove(*txtHndl,*text,totalLength);
  651.     HUnlock(txtHndl);
  652.     MyDisposHandle(txtHndl);
  653.     
  654.     if (err != noErr)
  655.         ResetConnection();
  656.             
  657.     return noErr;
  658. }
  659.  
  660.  
  661. /*    SendNNTP posts a news article to a newsgroup using the 'POST' command
  662.     on the NNTP server.
  663. */
  664.  
  665. Boolean SendNNTP(char *text,unsigned short tLength)
  666. {
  667.     extern unsigned long gConnID;
  668.     Ptr data;
  669.     unsigned short length;
  670.     Boolean result;
  671.     char commStr[256];
  672.     
  673.     strcpy(commStr,"POST");
  674.     strcat(commStr,CRLF);
  675.     
  676.     if (SendData(gConnID,commStr,6,false) != noErr)
  677.         return false;
  678.     
  679.     data = MyNewPtr(256);
  680.     if (MyMemErr() != noErr)
  681.         return false;
  682.     
  683.     strcpy(commStr,CRLF);
  684.     strcat(commStr,".");
  685.     strcat(commStr,CRLF);
  686.     
  687.     length = 200;
  688.     if (RecvData(gConnID,data,&length,false) == noErr &&
  689.         strncmp(data,"3",1)==0 &&
  690.         SendData(gConnID,text,tLength,false) == noErr &&
  691.         SendData(gConnID,commStr,5,false) == noErr &&
  692.         (length = 200, true) &&
  693.         RecvData(gConnID,data,&length,false) == noErr &&
  694.         strncmp(data,"2",1)==0)
  695.             result = true;
  696.     else {
  697.         result = false;
  698.     }
  699.     MyDisposPtr(data);
  700.     return true;
  701. }
  702.  
  703.  
  704.