home *** CD-ROM | disk | FTP | other *** search
/ Borland Programmer's Resource / Borland_Programmers_Resource_CD_1995.iso / winsock / wvnsc926 / wvutil.c < prev   
Encoding:
C/C++ Source or Header  |  1995-05-19  |  63.4 KB  |  2,238 lines

  1. /*
  2.  * $Id: wvutil.c 1.69 1994/09/18 22:35:49 jcooper Exp $
  3.  *
  4.  */
  5.  
  6. /*-- WVUTIL.C -- File containing utility routines.
  7.  */
  8.  
  9. #include <windows.h>
  10. #include <windowsx.h>      // for GlobalFreePtr (JSC)
  11. #include "wvglob.h"
  12. #include "winvn.h"
  13. #pragma hdrstop
  14. #include <commdlg.h>    // for GetOpenFileName dialog (JSC)
  15. #include <ctype.h>
  16. #include <stdlib.h>
  17. #include <time.h>
  18.  
  19. char far *mylstrcpy (char_p ptr1, char far * ptr2);
  20. char *get_xhdr_line (char *line);
  21. time_t parse_usenet_date (char *date);
  22. void finish_header_retrieval ();
  23. void GenerateFileFilters (HWND hParentWnd, char *filters);
  24.  
  25. // please update this if you modify XHDR retrieval
  26. // This will now be either 4 or 6, depending on whether threading
  27. // is enabled via 'threadp'.
  28. unsigned int total_xhdrs = 4;
  29.  
  30. /*--- function GetNum --------------------------------------------
  31.  *
  32.  *  Cracks off a positive integer number from a string.
  33.  *
  34.  *  Entry    *ptr  is the character position to start scanning
  35.  *                 for an integer
  36.  *
  37.  *  Exit     *ptr  is the character position at which we stopped
  38.  *                 scanning (because of a non-digit).
  39.  *           *num  is the cracked off number.
  40.  *           Returns TRUE iff we got a number.
  41.  */
  42. BOOL
  43. GetNum (ptr, num)
  44.      char **ptr;
  45.      long int *num;
  46. {
  47.   BOOL gotit = FALSE;
  48.  
  49.   /* Skip initial spaces                                            */
  50.  
  51.   while ((**ptr) && **ptr == ' ')
  52.     (*ptr)++;
  53.  
  54.   *num = 0;
  55.   while (**ptr && isdigit (**ptr)) {
  56.     *num = 10 * (*num) + (**ptr - '0');
  57.     gotit = TRUE;
  58.     (*ptr)++;
  59.   }
  60.   return (gotit);
  61. }
  62.  
  63. char *
  64. get_xhdr_line (char *line)
  65. {
  66.   char *cptr;
  67. /* skip past the art # and space */
  68.   for (cptr = line; isdigit (*cptr); cptr++);
  69.   for (; *cptr == ' '; cptr++);
  70.   return (cptr);
  71. }
  72.  
  73. #if 0
  74. MRB already did this
  75. void
  76. make_neat_from (char far * in, char far * out)
  77. {
  78.   char far *left, far * right;
  79.  
  80.   /* this is controlled from .ini */
  81.   if (FullNameFrom) {
  82.     left = strchr (in, '(');
  83.     right = strrchr (in, ')');
  84.  
  85.     if ((left && right) && (left < right)) {
  86.       strncpy (out, left + 1, (size_t) (right - left - 1));
  87.       out[(right - left - 1)] = (char) 0;
  88.     }
  89.     else       /* No name in parens */
  90.       strcpy (out, in);
  91.   }
  92.   else            /* !FullNameFrom */
  93.     strcpy (out, in);
  94. }
  95. #endif
  96.  
  97. /*-- function StrToRGB -------------------------------------------------
  98.  *
  99.  *  Takes an ASCII string of the form "r,g,b" where r, g, and b are
  100.  *  decimal ASCII numbers, and converts it to an RGB color number.
  101.  */
  102. COLORREF
  103. StrToRGB (cstring)
  104.      char *cstring;
  105. {
  106.   BYTE red, green, blue;
  107.   long int lred, lgreen, lblue;
  108.  
  109.   GetNum (&cstring, &lred);
  110.   cstring++;
  111.   GetNum (&cstring, &lgreen);
  112.   cstring++;
  113.   GetNum (&cstring, &lblue);
  114.   red = (BYTE) lred;
  115.   green = (BYTE) lgreen;
  116.   blue = (BYTE) lblue;
  117.  
  118.   return (RGB (red, green, blue));
  119. }
  120.  
  121. /*-- function RGBToStr -------------------------------------------------
  122.  *
  123.  *  Takes an RGB color ref and converts to a string of the form "r,g,b"
  124.  *  result is placed in buf
  125.  *  (JSC)
  126.  */
  127. char *
  128. RGBToStr (char *buf, DWORD rgbVal)
  129. {
  130.   sprintf (buf, "%u,%u,%u", GetRValue (rgbVal),
  131.       GetGValue (rgbVal),
  132.       GetBValue (rgbVal));
  133.   return (buf);
  134. }
  135.  
  136. /* This was lifted from ANU news. */
  137.  
  138. time_t
  139. parse_usenet_date (char *s)
  140. {
  141.   struct tm datetime;
  142.   char *cp, mon[80];
  143.   int dom = 0, yr = 0, hr = 0, mn = 0, sc = 0, mth = 0;
  144.   static char fmtMonthTable[37] = "janfebmaraprmayjunjulaugsepoctnovdec";
  145.  
  146.   if (!s || !*s)
  147.     return (0);
  148.   if (cp = strchr (s, ','))
  149.     s = ++cp;
  150.   while (isspace (*s))
  151.     s++;
  152.   *mon = '\0';
  153.   if (isdigit (*s)) {
  154.     sscanf (s, "%d %s %d %d:%d:%d", &dom, mon, &yr, &hr, &mn, &sc);
  155.     if (yr < 100)
  156.       yr += 1900;
  157.   }
  158.   else
  159.     sscanf (s, "%*s %s %d %d:%d:%d %d", mon, &dom, &hr, &mn, &sc, &yr);
  160.  
  161.   if (!dom || !yr || !*(cp = mon))
  162.     return (0);
  163.   if ((dom <= 0) || (dom >= 32))
  164.     return (0);
  165.   if ((yr < 1980) || (yr > 2020))
  166.     return (0);
  167.   if (strlen (mon) > 10)
  168.     return (0);
  169.   if ((hr < 0) || (hr > 23))
  170.     return (0);
  171.   if ((mn < 0) || (mn > 59))
  172.     return (0);
  173.   if ((sc < 0) || (sc > 59))
  174.     return (0);
  175.  
  176.   for (cp = mon; *cp; cp++)
  177.     *cp = tolower (*cp);
  178.  
  179.   if (cp = strstr (fmtMonthTable, mon))
  180.     mth = (cp - fmtMonthTable) / 3;
  181.  
  182. /*  Setup a Posix time structure and calculate time in absolute
  183.    time (seconds since midnight, Jan 1, 1970    JD 06/25/93 */
  184.  
  185.   datetime.tm_year = yr - 1900;
  186.   datetime.tm_mon = mth;
  187.   datetime.tm_mday = dom;
  188.   datetime.tm_hour = hr;
  189.   datetime.tm_min = mn;
  190.   datetime.tm_sec = sc;
  191.  
  192.   return (mktime (&datetime));
  193. }
  194.  
  195. /*-- function StringDate ----------------*/
  196. char *
  197. StringDate (char *s, time_t time)
  198. {
  199.   struct tm *datetime;
  200.   if (time != 0) {
  201.     datetime = localtime (&time);
  202.  
  203.     if (fmtDaysB4Mth) {
  204.       sprintf (s, "%02d%s%02d", datetime->tm_mday, fmtDateDelim, datetime->tm_mon + 1);
  205.     }
  206.     else {
  207.       sprintf (s, "%02d%s%02d", datetime->tm_mon + 1, fmtDateDelim, datetime->tm_mday);
  208.     }
  209.     return (s);
  210.   }
  211.   else
  212.     return ("-----");
  213. }
  214.  
  215. /*-- function DoCommInput ---------------------------------------
  216.  *
  217.  *
  218.  */
  219. void
  220. DoCommInput ()
  221. {
  222.   int ch;
  223.  
  224.   while ((CommState != ST_CLOSED_COMM) && ((ch = MRRReadComm ()) >= 0)) {
  225.     if (ch == IgnoreCommCh) {
  226.     }
  227.     else if (ch == EOLCommCh) {
  228.       *CommLinePtr = '\0';
  229.       DoCommState ();
  230.       CommLinePtr = CommLineIn;
  231.     }
  232.     else {
  233.       *(CommLinePtr++) = (char) ch;
  234.       if (CommLinePtr == CommLineLWAp1)
  235.    CommLinePtr--;
  236.     }
  237.   }
  238. }
  239.  
  240. void
  241. update_window_title (HWND hwnd,
  242.            char *group_name,
  243.            unsigned long line_number,
  244.            unsigned long total_lines)
  245. {
  246.   char title[200];
  247.   static int prevPercent, newPercent;
  248.   // to avoid flicker, update percent only if it has changed more than 1%
  249.  
  250.   line_number *= 100;
  251.   if (newPercent < prevPercent)
  252.     prevPercent = 0;
  253.  
  254.   if ((line_number % UPDATE_TITLE_FREQ) == 0) {
  255.     newPercent = (int) (line_number / total_lines);
  256.     if (newPercent != prevPercent && newPercent - prevPercent > 1) {
  257.       sprintf (title, "Retrieving headers for '%s' : %d%%", group_name, newPercent);
  258.       SetWindowText (hwnd, title);
  259.       prevPercent = newPercent;
  260.     }
  261.   }
  262. }
  263.  
  264. int
  265. check_server_code (int retcode)
  266. {
  267.   int class = retcode / 100;
  268.   switch (class) {
  269.   case 5:
  270.     MessageBox (hWndConf, "News Server Error", "WinVN", MB_OK | MB_ICONHAND);
  271.     CommBusy = FALSE;
  272.     CommState = ST_NONE;
  273.     return (1);
  274.     break;
  275.   case 4:
  276.     MessageBox (hWndConf, CommLineIn, "WinVN", MB_OK | MB_ICONHAND);
  277.     switch (class) {
  278.     case 400:
  279.       /* service discontinued */
  280.       MRRCloseComm ();
  281.       PostQuitMessage (0);
  282.       break;
  283.     default:
  284.       break;
  285.     }
  286.     CommBusy = FALSE;
  287.     CommState = ST_NONE;
  288.     return (1);
  289.     break;
  290.   }
  291.   return (0);
  292. }
  293.  
  294. /*  Function sync_artnum
  295.  
  296.    Normally XREF returns lists of the same length for each header type
  297.    but some servers have errors that could cause these lists to get
  298.    out of sync. This function tries to find the proper location in the
  299.    headers array and returns that location.  If the article number isn't
  300.    found, it returns -1.  JD 6/19/93 */
  301.  
  302. long
  303. sync_artnum (unsigned long artnum,
  304.         unsigned long activenum,
  305.         header_p headers, TypGroup far * GroupDoc)
  306. {
  307.   long headerloc = CommDoc->ActiveLines;
  308.   if (artnum == activenum)
  309.     return (headerloc);
  310.   else if (artnum < activenum) {
  311.     while ((artnum != activenum) && (headerloc > 0)) {
  312.       headerloc--;
  313.       if ((header_elt (headers, headerloc))->number == artnum)
  314.    return (headerloc);
  315.     }
  316.     return (-1);
  317.   }
  318.   else {
  319.     while ((artnum != activenum) && (headerloc <= GroupDoc->total_headers)) {
  320.       headerloc++;
  321.       if ((header_elt (headers, headerloc))->number == artnum)
  322.    return (headerloc);
  323.     }
  324.     return (-1);
  325.   }
  326. }
  327.  
  328. char *
  329. get_best_reference (char *refer)
  330. {
  331.   char *bracket1, *bracket2;
  332.   char *end = refer + strlen (refer) - 1; /* points to NULL */
  333.   int bracket_len;
  334.  
  335.   bracket1 = strrchr (refer, '<');
  336.   if (bracket1) {
  337.     bracket_len = (int) (end - bracket1) + 2;
  338.     if ((bracket_len < 30) && (!strrchr (bracket1, '>'))) {
  339.       *bracket1 = (char) NULL;
  340.       bracket1 = strrchr (refer, '<');
  341.       if (!bracket1)
  342.    bracket_len = 0;
  343.       else {
  344.    bracket2 = strrchr (refer, '>');
  345.    if (bracket2)
  346.      bracket_len = (int) (bracket2 - bracket1) + 1;
  347.       }
  348.     }
  349.   }
  350.   else
  351.     bracket_len = 0;
  352.  
  353.   if (!bracket_len)
  354.     return (NULL);
  355.   else if (bracket_len > 29)
  356.     bracket_len = 29;
  357.  
  358.   if ((bracket1 + bracket_len) < end)
  359.     *(bracket1 + bracket_len) = (char) NULL;
  360.  
  361.   return (bracket1);
  362. }
  363.  
  364. /*-- function DoCommState ----------------------------------------------
  365.  *
  366.  *  Function to implement an FSA to process incoming lines from
  367.  *  the server.
  368.  *  This function is called once for each line from the server.
  369.  *
  370.  *    Entry    CommLineIn  is a zero-terminated line received from
  371.  *                         the server.
  372.  *             CommState   is the current state of the FSA.
  373.  */
  374. void
  375. DoCommState ()
  376. {
  377.   static BOOL dialog_active = FALSE;
  378.   TypLine far *LinePtr;
  379.   TypBlock far *BlockPtr;
  380.   HANDLE hBlock;
  381.   unsigned int Offset;
  382.   TypLineID MyLineID;
  383.   int retcode;
  384.   int found;
  385.   unsigned long estnum;
  386.   long int first, last, syncnum;
  387.   unsigned long artnum;
  388.   int mylen;
  389.   BOOL done = FALSE;
  390.   BOOL DlgStatus = FALSE;
  391.   BOOL dolist, do_authinfo;
  392.   static char group[MAXINTERNALLINE];
  393.   char mybuf[MAXINTERNALLINE];
  394.   char mybuf2[MAXINTERNALLINE];
  395.   char far *lpsz;
  396.   HANDLE header_handle;
  397.   HANDLE thread_handle;
  398.   header_p headers;
  399.   header_p header;
  400.   TypGroup far *GroupDoc;
  401.   TypRange *RangePtr;
  402.  
  403.   /* CommDoc is !NULL if retrieving group list, article headers or articles */
  404.   /* CommDecoding is true if retrieving article in decode mode (not to a doc) */
  405.   /* PostEdit !NULL if we are posting (this is from an edit, no doc involved) */
  406.   if (CommDoc || CommDecoding || PostEdit) {
  407.  
  408.     switch (CommState) {
  409.     case ST_NONE:
  410.       break;
  411.  
  412.     case ST_ESTABLISH_COMM:
  413.       if (!sscanf (CommLineIn, "%u", &retcode))
  414.    break;
  415.       if (retcode == 200 || retcode == 201) {   /* was 500 from induced error */
  416.    CommBusy = TRUE;
  417.    do_authinfo = FALSE;
  418.    Authenticated = FALSE;
  419.    if (strlen (NNTPUserName)) {
  420.      /* We have the AUTHINFO username.  Do we have the password? */
  421.      if (!strlen (NNTPPasswordEncrypted)) {
  422.        /* Get the news server user password from the user */
  423.        if (DialogBox (hInst, (LPCSTR) "WinVnComm", hWndConf, (DLGPROC) lpfnWinVnCommDlg)
  424.       && strlen (NNTPPasswordEncrypted)) {
  425.          do_authinfo = TRUE;
  426.        }
  427.      }
  428.      else {
  429.        do_authinfo = TRUE;
  430.      }
  431.    }
  432.    if (do_authinfo) {
  433.      sprintf (mybuf, "AUTHINFO user %s", NNTPUserName);
  434.      CommState = ST_CHECK_AUTHINFO_USERNAME;
  435.      PutCommLine (mybuf);
  436.    }
  437.    else {
  438.      goto End_Authinfo;
  439.    }
  440.  
  441.       }
  442.       else {
  443.    MessageBox (hWndConf, CommLineIn, "Access Problem", MB_OK);
  444.    /*      MRRCloseComm (); */
  445.    /*        PostQuitMessage (0); */
  446.    Initializing = INIT_NOT_CONNECTED;
  447.    InvalidateRect (hWndConf, NULL, TRUE);
  448.       }
  449.  
  450.       break;
  451.  
  452.     case ST_CHECK_AUTHINFO_USERNAME:
  453.       retcode = 0;
  454.       sscanf (CommLineIn, "%u", &retcode);
  455.       if (!retcode)
  456.    break;
  457.       if (retcode >= 500) {
  458.    MessageBox (hWndConf, "Error authorizing your username with the News Server.", "WinVN", MB_OK | MB_ICONHAND);
  459.    goto End_Authinfo;
  460.       }
  461.       MRRDecrypt (NNTPPasswordEncrypted, (unsigned char *) mybuf2, MAXINTERNALLINE);
  462.       sprintf (mybuf, "AUTHINFO pass %s", mybuf2);
  463.       CommState = ST_CHECK_AUTHINFO_PASSWORD;
  464.       PutCommLine (mybuf);
  465.       break;
  466.  
  467.     case ST_CHECK_AUTHINFO_PASSWORD:
  468.       if (dialog_active)
  469.    break;
  470.       retcode = 0;
  471.       if (sscanf (CommLineIn, "%u", &retcode) <= 0)
  472.    break;
  473.       if (retcode < 200 || retcode > 299) {
  474.    dialog_active = TRUE;
  475.    sprintf (mybuf, "Error authorizing your password with the News Server:\n%s.", CommLineIn);
  476.    MessageBox (hWndConf, mybuf, "WinVN", MB_OK | MB_ICONHAND);
  477.    dialog_active = FALSE;
  478.       } else {                                               
  479.          /* Authentication was successful.  Store this fact, and the name under
  480.           * which the user was authenticated.
  481.           */
  482.          Authenticated = TRUE;
  483.          strncpy(AuthenticatedName,NNTPUserName,MAXNNTPSIZE);
  484.       }
  485.       goto End_Authinfo;
  486.  
  487.  
  488.     case ST_END_AUTHINFO:
  489.     End_Authinfo:;
  490.       /* now check for the XOVER command */
  491.       CommState = ST_XOVER_CHECK;
  492.       PutCommLine ("XOVER");
  493.       break;
  494.  
  495.     case ST_XOVER_CHECK:
  496.       retcode = 0;
  497.       sscanf (CommLineIn, "%u", &retcode);
  498.       if (retcode == 412 && !force_xhdr)  /* 412 == 'not in a newsgroup' */
  499.    xoverp = 1;
  500.       else        /* 500 == 'command not understood' */
  501.    xoverp = 0;
  502.  
  503.       dolist = DoList;
  504.       if (dolist == ID_DOLIST_ASK - ID_DOLIST_BASE)
  505.    if (MessageBox (hWndConf, "Request the latest group list from server?\n(This can be time consuming)",
  506.          "Request LIST from server?", MB_ICONQUESTION | MB_YESNO | MB_DEFBUTTON2) == IDNO)
  507.      dolist = 0;
  508.  
  509.       if (dolist) {
  510.    StartList ();
  511.    did_list = 1;
  512.       }
  513.       else {
  514.    did_list = 0;
  515.    CommState = ST_NONE;
  516.    CommBusy = FALSE;
  517.    Initializing = INIT_READY;
  518.       }
  519.       InvalidateRect (hWndConf, NULL, FALSE);
  520.       break;
  521.  
  522.     case ST_LIST_RESP:
  523.       retcode = 0;
  524.       sscanf (CommLineIn, "%u", &retcode);
  525.       if (retcode != 215) {
  526.    check_server_code (retcode);
  527.    break;
  528.       }
  529.  
  530.       CommState = ST_LIST_GROUPLINE;
  531.       RcvLineCount = 0;
  532.       break;
  533.  
  534.     case ST_LIST_GROUPLINE:
  535.       if (strcmp (CommLineIn, ".") == 0) {
  536.    CommState = ST_NONE;
  537.    CommBusy = FALSE;
  538.    Initializing = INIT_READY;
  539.    InvalidateRect (hWndConf, NULL, FALSE);
  540.  
  541.    ProcEndList ();
  542.       }
  543.       else {
  544.    ProcListLine ((unsigned char *) CommLineIn);
  545.       }
  546.       break;
  547.  
  548.     case ST_GROUP_RESP:
  549.       retcode = 0;
  550.       sscanf (CommLineIn, "%u", &retcode);
  551.       switch (retcode) {
  552.       case 411:
  553.    MessageBox (hWndConf, "No Such Newsgroup", "Error", MB_OK | MB_ICONHAND);
  554.    /* abort the fledgling group window */
  555.    DestroyWindow (CommDoc->hDocWnd);
  556.    CommBusy = FALSE;
  557.    CommState = ST_NONE;
  558.    return;
  559.    break;
  560.       case 502:
  561.    MessageBox (hWndConf, "Restricted Access", "WinVN", MB_OK | MB_ICONHAND);
  562.    /* abort the fledgling group window */
  563.    DestroyWindow (CommDoc->hDocWnd);
  564.    CommBusy = FALSE;
  565.    CommState = ST_NONE;
  566.    return;
  567.    break;
  568.       default:
  569.    if (check_server_code (retcode))
  570.      return;
  571.    break;
  572.       }
  573.  
  574.       sscanf (CommLineIn, "%u %lu %ld %ld %s", &retcode, &estnum, &first, &last, group);
  575.  
  576.       LockLine (CommDoc->hParentBlock, CommDoc->ParentOffset,
  577.       CommDoc->ParentLineID, &BlockPtr, &LinePtr);
  578.       GroupDoc = ((TypGroup far *) ((char far *) LinePtr + sizeof (TypLine)));
  579.       RangePtr = (TypRange far *) ((char far *) GroupDoc + RangeOffset (GroupDoc->NameLen));
  580.       GroupDoc->Determined = TRUE;
  581.  
  582.       /* we don't want to grab *that* many! */
  583.       if (estnum >= article_threshold) {
  584.    if (!ShowUnreadOnly)
  585.      { 
  586.      arts_to_retrieve = (int) estnum;
  587.      DlgStatus = DialogBox (hInst, (LPCSTR) "THRESHOLD", CommDoc->hDocWnd, (DLGPROC) lpfnWinVnThresholdDlg);
  588.           if (DlgStatus == FALSE)
  589.              {             
  590.               DestroyWindow (CommDoc->hDocWnd);
  591.          CommBusy = FALSE;
  592.          CommState = ST_NONE;
  593.          GroupDoc->ServerFirst = GroupDoc->ServerLast;
  594.          GroupDoc->ServerEstNum = estnum;
  595.               return;
  596.               }
  597.           }
  598.    if ((arts_to_retrieve > 0)
  599.        && ((last - arts_to_retrieve) > first)) {
  600.      first = (last - arts_to_retrieve) + 1;
  601.    }
  602.    else if (arts_to_retrieve == -1) /* they clicked 'all of them' */
  603.      arts_to_retrieve = (int) estnum;
  604.    /* added by jlg */
  605.    else if ((arts_to_retrieve == -2)   /* they clicked 'unread' */
  606.        ||(ShowUnreadOnly)) {
  607.      if (GroupDoc->nRanges) {
  608.        first = RangePtr[0].Last + 1;
  609.        arts_to_retrieve = (int) ((last - first) + 1);
  610.        if (arts_to_retrieve < 50) {
  611.          arts_to_retrieve = 50;
  612.          first = last - 49;
  613.        }
  614.      }
  615.      else
  616.        arts_to_retrieve = (int) estnum;
  617.    }
  618.       }
  619.       else {
  620.    if (estnum > 0)
  621.      arts_to_retrieve = (int) estnum;
  622.    else {
  623.      MessageBox (hWndConf, "Empty Newsgroup", "WinVN", MB_OK | MB_ICONHAND);
  624.      /* abort the fledgling group window */
  625.      DestroyWindow (CommDoc->hDocWnd);
  626.      InvalidateRect(hWndConf, NULL, FALSE);
  627.      CommBusy = FALSE;
  628.      CommState = ST_NONE;
  629.      GroupDoc->ServerFirst = GroupDoc->ServerLast;
  630.      GroupDoc->ServerEstNum = 0;
  631.      return;
  632.    }
  633.       }
  634.  
  635.       CommDoc->TotalLines = arts_to_retrieve;
  636.  
  637.       if (arts_to_retrieve > 0) {
  638.    header_handle =
  639.      GlobalAlloc (GMEM_MOVEABLE, (long)
  640.              ((sizeof (TypHeader)) *
  641.             (long) (arts_to_retrieve)) + sizeof (thread_array *));
  642.  
  643.    /* allocate space for the header_array index table */
  644.    thread_handle =
  645.      GlobalAlloc (GMEM_MOVEABLE,
  646.              (long) ((sizeof (long)) * (long) (arts_to_retrieve)));
  647.  
  648.    GroupDoc->header_handle = header_handle;
  649.    GroupDoc->thread_handle = thread_handle;
  650.  
  651.       }
  652.  
  653.       /* stick nulls and 0's, etc.. in case display code get mis-threaded */
  654.       initialize_header_array (header_handle, thread_handle, arts_to_retrieve);
  655.  
  656.       GroupDoc = (TypGroup far *) ((char far *) LinePtr + sizeof (TypLine));
  657.       GroupDoc->ServerEstNum = estnum;
  658.       GroupDoc->ServerFirst = first;
  659.       GroupDoc->ServerLast = last;
  660.  
  661.       GlobalUnlock (BlockPtr->hCurBlock);
  662.  
  663.       if (xoverp) {
  664.    mylen = sprintf (mybuf, "XOVER %ld-%ld", first, last);
  665.    CommState = ST_XOVER_START;
  666.    PutCommLine (mybuf);
  667.       }
  668.       else {
  669.    mylen = sprintf (mybuf, "XHDR from %ld-%ld", first, last);
  670.    CommState = ST_XHDR_FROM_START;
  671.    PutCommLine (mybuf);
  672.       }
  673.  
  674.       break;
  675.  
  676.       /* The next few cases handle retrieval of XHDR information for display */
  677.       /* in the group window.  If you change the number of XHDR's retrieved */
  678.       /* (such as adding 'XHDR References' back into the state machine), you */
  679.       /* need to reflect that change in the variable total_xhdrs. */
  680.  
  681.       /* the current flow is FROM -> DATE -> LINES -> SUBJECT */
  682.       /* (threadp) FROM -> DATE -> LINES -> REF -> ID -> SUBJECT */
  683.  
  684.       /* this will now be done dynamically, depending on the state of */
  685.       /* the 'threadp' variable */
  686.  
  687.     case ST_XOVER_START:
  688.       retcode = 0;
  689.       sscanf (CommLineIn, "%d", &retcode);
  690.       if (retcode == 224) {
  691.    CommState = ST_XOVER_DATA;
  692.    CommDoc->ActiveLines = 0;
  693.       }
  694.       else {
  695.    mylen = sprintf (mybuf, "XHDR from %ld-%ld", first, last);
  696.    CommState = ST_XHDR_FROM_START;
  697.    PutCommLine (mybuf);
  698.       }
  699.       break;
  700.  
  701.     case ST_XOVER_DATA:
  702.       if (strcmp (CommLineIn, ".") == 0) {
  703.    /* this is a yuck way to do this */
  704.    CommState = ST_IN_GROUP;
  705.    CommBusy = FALSE;
  706.    finish_header_retrieval (CommDoc);
  707.    InvalidateRect (CommDoc->hDocWnd, NULL, FALSE);
  708.       }
  709.       else {
  710.    char *this_hop, *next_hop;
  711.    char *reference;
  712.  
  713.    LockLine (CommDoc->hParentBlock, CommDoc->ParentOffset,
  714.         CommDoc->ParentLineID, &BlockPtr, &LinePtr);
  715.    GroupDoc = ((TypGroup far *) ((char far *) LinePtr + sizeof (TypLine)));
  716.    header_handle = GroupDoc->header_handle;
  717.    thread_handle = GroupDoc->thread_handle;
  718.  
  719.    GlobalUnlock (BlockPtr->hCurBlock);
  720.  
  721.    /* Lock the header data */
  722.    headers = lock_headers (header_handle, thread_handle);
  723.    header = header_elt (headers, CommDoc->ActiveLines);
  724.  
  725.    this_hop = CommLineIn;
  726.  
  727.    /* article number */
  728.    next_hop = strchr (this_hop, '\t');
  729.    *(next_hop++) = (char) NULL;
  730.  
  731.    header->number = atol (this_hop);
  732.  
  733.    /* subject */
  734.    this_hop = next_hop;
  735.    next_hop = strchr (this_hop, '\t');
  736.    *(next_hop++) = (char) NULL;
  737.  
  738.    mylstrncpy (header->subject, this_hop, HEADER_SUBJECT_LENGTH);
  739.    CommDoc->LongestLine = max (CommDoc->LongestLine,
  740.                 ARTICLE_SUBJECT_OFFSET +
  741.                 (unsigned) lstrlen (header->subject));
  742.    /* author */
  743.    this_hop = next_hop;
  744.    next_hop = strchr (this_hop, '\t');
  745.    *(next_hop++) = (char) NULL;
  746.  
  747.    ParseAddress (this_hop,
  748.             AddressString, MAXDIALOGSTRING,
  749.             NameString, MAXDIALOGSTRING);
  750.  
  751.    if (FullNameFrom)
  752.      mylstrncpy (header->from, NameString, HEADER_FROM_LENGTH);
  753.    else
  754.      mylstrncpy (header->from, this_hop, HEADER_FROM_LENGTH);
  755.  
  756.    /* date */
  757.    this_hop = next_hop;
  758.    next_hop = strchr (this_hop, '\t');
  759.    *(next_hop++) = (char) NULL;
  760.  
  761.    header->date = parse_usenet_date (this_hop);
  762.  
  763.    /* message-id */
  764.    this_hop = next_hop;
  765.    next_hop = strchr (this_hop, '\t');
  766.    *(next_hop++) = (char) NULL;
  767.  
  768.    mylstrncpy (header->message_id, this_hop, HEADER_MESSAGE_ID_LENGTH);
  769.  
  770.    /* references */
  771.    this_hop = next_hop;
  772.    next_hop = strchr (this_hop, '\t');
  773.    *(next_hop++) = (char) NULL;
  774.  
  775.    reference = get_best_reference (this_hop);
  776.    if (reference)
  777.      mylstrncpy (header->references, reference, HEADER_REFERENCES_LENGTH);
  778.  
  779.    /* bytes (ignored) */
  780.    this_hop = next_hop;
  781.    next_hop = strchr (this_hop, '\t');
  782.    *(next_hop++) = (char) NULL;
  783.  
  784.    /* lines (last one doesn't have to have the tab */
  785.    this_hop = next_hop;
  786.    header->lines = atoi (this_hop);
  787.  
  788.    /* set other header fields */
  789.    header->Selected = FALSE;
  790.    header->ArtDoc = (TypDoc *) NULL;
  791.    header->Seen = WasArtSeen (header->number, GroupDoc);
  792.  
  793.    unlock_headers (header_handle, thread_handle);
  794.  
  795.    CommDoc->ActiveLines++;
  796.  
  797.    update_window_title (CommDoc->hDocWnd, group,
  798.               RcvLineCount++,
  799.               CommDoc->TotalLines);
  800.       }
  801.  
  802.       break;
  803.  
  804.  
  805.       /* The next few cases handle retrieval of XHDR information for display */
  806.       /* in the group window.  If you change the number of XHDR's retrieved */
  807.       /* (such as adding 'XHDR References' back into the state machine), you */
  808.       /* need to reflect that change in the variable total_xhdrs. */
  809.  
  810.       /* the current flow is FROM -> DATE -> LINES -> SUBJECT */
  811.       /* (threadp) FROM -> DATE -> LINES -> REF -> ID -> SUBJECT */
  812.  
  813.       /* this will now be done dynamically, depending on the state of */
  814.       /* the 'threadp' variable */
  815.  
  816.     case ST_XHDR_FROM_START:
  817.       retcode = 0;
  818.       sscanf (CommLineIn, "%d", &retcode);
  819.       total_xhdrs = threadp ? 6 : 4;   /* we do this here to allow */
  820.       /* mid-session change-of-mind  */
  821.       if (retcode < 100)
  822.    break;
  823.       CommState = ST_XHDR_FROM_DATA;
  824.       CommDoc->ActiveLines = 0;
  825.       break;
  826.  
  827.     case ST_XHDR_FROM_DATA:
  828.       if (strcmp (CommLineIn, ".") == 0) {
  829.    LockLine (CommDoc->hParentBlock, CommDoc->ParentOffset,
  830.         CommDoc->ParentLineID, &BlockPtr, &LinePtr);
  831.  
  832.    GroupDoc = ((TypGroup far *) ((char far *) LinePtr + sizeof (TypLine)));
  833.    GroupDoc->total_headers = CommDoc->ActiveLines;
  834.  
  835.    first = GroupDoc->ServerFirst;
  836.    last = GroupDoc->ServerLast;
  837.  
  838.    GlobalUnlock (BlockPtr->hCurBlock);
  839.    CommDoc->ActiveLines = 0;
  840.  
  841.    /* Now ask for the date lines */
  842.    mylen = sprintf (mybuf, "XHDR date %ld-%ld", first, last);
  843.    CommState = ST_XHDR_DATE_START;
  844.    PutCommLine (mybuf);
  845.       }
  846.       else {
  847.    /*      char neat_from [80]; */
  848.    /* Access the Group struct, get HANDLE for header data */
  849.    LockLine (CommDoc->hParentBlock, CommDoc->ParentOffset,
  850.         CommDoc->ParentLineID, &BlockPtr, &LinePtr);
  851.  
  852.    GroupDoc = ((TypGroup far *) ((char far *) LinePtr + sizeof (TypLine)));
  853.    header_handle = GroupDoc->header_handle;
  854.    thread_handle = GroupDoc->thread_handle;
  855.  
  856.    GlobalUnlock (BlockPtr->hCurBlock);
  857.  
  858.    /* Lock the header data */
  859.    headers = lock_headers (header_handle, thread_handle);
  860.  
  861.    sscanf (CommLineIn, "%ld", &artnum);
  862.    header = header_elt (headers, CommDoc->ActiveLines);
  863.    header->number = artnum;
  864.  
  865.    /* now use some of our nice formatting of email addresses */
  866.    ParseAddress (get_xhdr_line (CommLineIn),
  867.             AddressString, MAXDIALOGSTRING,
  868.             NameString, MAXDIALOGSTRING);
  869.  
  870.    /* copy that into headers[].from */
  871.    if (FullNameFrom)
  872.      mylstrncpy (header->from, NameString, HEADER_FROM_LENGTH);
  873.    else
  874.      mylstrncpy (header->from, AddressString, HEADER_FROM_LENGTH);
  875.  
  876.    unlock_headers (header_handle, thread_handle);
  877.  
  878.    CommDoc->ActiveLines++;
  879.  
  880.    update_window_title (CommDoc->hDocWnd, group,
  881.               RcvLineCount++,
  882.               CommDoc->TotalLines * total_xhdrs);
  883.       }
  884.  
  885.       break;
  886.  
  887.     case ST_XHDR_DATE_START:
  888.       retcode = 0;
  889.       sscanf (CommLineIn, "%d", &retcode);
  890.       if (check_server_code (retcode))
  891.    break;
  892.       CommState = ST_XHDR_DATE_DATA;
  893.       CommDoc->ActiveLines = 0;
  894.       break;
  895.  
  896.     case ST_XHDR_DATE_DATA:
  897.       if (strcmp (CommLineIn, ".") == 0) {
  898.    LockLine (CommDoc->hParentBlock, CommDoc->ParentOffset,
  899.         CommDoc->ParentLineID, &BlockPtr, &LinePtr);
  900.  
  901.    GroupDoc = ((TypGroup far *) ((char far *) LinePtr + sizeof (TypLine)));
  902.    GroupDoc->total_headers = CommDoc->ActiveLines;
  903.  
  904.    first = GroupDoc->ServerFirst;
  905.    last = GroupDoc->ServerLast;
  906.  
  907.    GlobalUnlock (BlockPtr->hCurBlock);
  908.    CommDoc->ActiveLines = 0;
  909.  
  910.    /* Now ask for the #of lines */
  911.    mylen = sprintf (mybuf, "XHDR lines %ld-%ld", first, last);
  912.    CommState = ST_XHDR_LINES_START;
  913.    PutCommLine (mybuf);
  914.       }
  915.       else {
  916.  
  917.    /* Access the Group struct, get HANDLE for header data */
  918.    LockLine (CommDoc->hParentBlock, CommDoc->ParentOffset,
  919.         CommDoc->ParentLineID, &BlockPtr, &LinePtr);
  920.    GroupDoc = ((TypGroup far *) ((char far *) LinePtr + sizeof (TypLine)));
  921.    header_handle = GroupDoc->header_handle;
  922.    thread_handle = GroupDoc->thread_handle;
  923.  
  924.    GlobalUnlock (BlockPtr->hCurBlock);
  925.  
  926.    /* Lock the header data */
  927.    headers = lock_headers (header_handle, thread_handle);
  928.    syncnum = sync_artnum (atol (CommLineIn),
  929.              (header_elt (headers, CommDoc->ActiveLines))->number,
  930.                 headers,
  931.                 GroupDoc);
  932.    if (syncnum >= 0)
  933.      (header_elt (headers, syncnum))->date
  934.        = parse_usenet_date (get_xhdr_line (CommLineIn));
  935.  
  936.    unlock_headers (header_handle, thread_handle);
  937.  
  938.    CommDoc->ActiveLines++;
  939.    update_window_title (CommDoc->hDocWnd, group,
  940.               RcvLineCount++,
  941.               CommDoc->TotalLines * total_xhdrs);
  942.       }
  943.  
  944.       break;
  945.  
  946.     case ST_XHDR_LINES_START:
  947.       retcode = 0;
  948.       sscanf (CommLineIn, "%d", &retcode);
  949.       if (check_server_code (retcode))
  950.    break;
  951.       CommState = ST_XHDR_LINES_DATA;
  952.       CommDoc->ActiveLines = 0;
  953.       break;
  954.  
  955.     case ST_XHDR_LINES_DATA:
  956.       if (strcmp (CommLineIn, ".") == 0) {
  957.    LockLine (CommDoc->hParentBlock, CommDoc->ParentOffset,
  958.         CommDoc->ParentLineID, &BlockPtr, &LinePtr);
  959.  
  960.    GroupDoc = ((TypGroup far *) ((char far *) LinePtr + sizeof (TypLine)));
  961.    GroupDoc->total_headers = CommDoc->ActiveLines;
  962.  
  963.    first = GroupDoc->ServerFirst;
  964.    last = GroupDoc->ServerLast;
  965.  
  966.    GlobalUnlock (BlockPtr->hCurBlock);
  967.    CommDoc->ActiveLines = 0;
  968.  
  969.    /* Check for threading option, if enabled, go to REF & ID */
  970.    /* states first */
  971.  
  972.    if (threadp) {
  973.      CommState = ST_XHDR_REF_START;
  974.      mylen = sprintf (mybuf, "XHDR references %ld-%ld", first, last);
  975.      PutCommLine (mybuf);
  976.    }
  977.    else {
  978.      CommState = ST_XHDR_SUBJECT_START;
  979.      mylen = sprintf (mybuf, "XHDR subject %ld-%ld", first, last);
  980.      PutCommLine (mybuf);
  981.    }
  982.       }
  983.  
  984.       else {
  985.  
  986.    /* Access the Group struct, get HANDLE for header data */
  987.    LockLine (CommDoc->hParentBlock, CommDoc->ParentOffset,
  988.         CommDoc->ParentLineID, &BlockPtr, &LinePtr);
  989.    GroupDoc = ((TypGroup far *) ((char far *) LinePtr + sizeof (TypLine)));
  990.  
  991.    header_handle = GroupDoc->header_handle;
  992.    thread_handle = GroupDoc->thread_handle;
  993.  
  994.    GlobalUnlock (BlockPtr->hCurBlock);
  995.  
  996.    /* Lock the header data */
  997.    headers = lock_headers (header_handle, thread_handle);
  998.  
  999.    syncnum = sync_artnum (atol (CommLineIn),
  1000.              (header_elt (headers, CommDoc->ActiveLines))->number,
  1001.                 headers,
  1002.                 GroupDoc);
  1003.    if (syncnum >= 0)
  1004.      sscanf (CommLineIn, "%ld %Fd", &artnum, &((header_elt (headers, syncnum))->lines));
  1005.  
  1006.    unlock_headers (header_handle, thread_handle);
  1007.    CommDoc->ActiveLines++;
  1008.    update_window_title (CommDoc->hDocWnd, group,
  1009.               RcvLineCount++,
  1010.               CommDoc->TotalLines * total_xhdrs);
  1011.       }
  1012.  
  1013.       break;
  1014.  
  1015.     case ST_XHDR_REF_START:
  1016.       retcode = 0;
  1017.       sscanf (CommLineIn, "%d", &retcode);
  1018.       if (check_server_code (retcode))
  1019.    break;
  1020.       CommState = ST_XHDR_REF_DATA;
  1021.       CommDoc->ActiveLines = 0;
  1022.       break;
  1023.  
  1024.     case ST_XHDR_REF_DATA:
  1025.       if (strcmp (CommLineIn, ".") == 0) {
  1026.    LockLine (CommDoc->hParentBlock, CommDoc->ParentOffset,
  1027.         CommDoc->ParentLineID, &BlockPtr, &LinePtr);
  1028.  
  1029.    GroupDoc = (TypGroup far *) ((char far *) LinePtr + sizeof (TypLine));
  1030.    GroupDoc->total_headers = CommDoc->ActiveLines;
  1031.  
  1032.    first = GroupDoc->ServerFirst;
  1033.    last = GroupDoc->ServerLast;
  1034.  
  1035.    GlobalUnlock (BlockPtr->hCurBlock);
  1036.    CommDoc->ActiveLines = 0;
  1037.  
  1038.    /* Now ask for the message-id lines */
  1039.    mylen = sprintf (mybuf, "XHDR message-id %ld-%ld", first, last);
  1040.    CommState = ST_XHDR_MID_START;
  1041.    PutCommLine (mybuf);
  1042.       }
  1043.       else {
  1044.    char far *refer;  /* , far * end,far * bracket1,far *bracket2; */
  1045.    /*      int bracket_len; */
  1046.  
  1047.    /* Access the Group struct, get HANDLE for header data */
  1048.    LockLine (CommDoc->hParentBlock, CommDoc->ParentOffset,
  1049.         CommDoc->ParentLineID, &BlockPtr, &LinePtr);
  1050.    GroupDoc = ((TypGroup far *) ((char far *) LinePtr + sizeof (TypLine)));
  1051.  
  1052.    header_handle = GroupDoc->header_handle;
  1053.    thread_handle = GroupDoc->thread_handle;
  1054.  
  1055.    GlobalUnlock (BlockPtr->hCurBlock);
  1056.  
  1057.    /* Lock the header data */
  1058.    headers = lock_headers (header_handle, thread_handle);
  1059.  
  1060.    /* for now, we only pay attention to first (whole) referral */
  1061.    refer = get_xhdr_line (CommLineIn);
  1062.  
  1063.    refer = get_best_reference (refer);
  1064.  
  1065.    if (refer) {
  1066.      /* Patch to check for bad info from server JD 6/19/93 */
  1067.      syncnum = sync_artnum (atol (CommLineIn),
  1068.              (header_elt (headers,
  1069.                      CommDoc->ActiveLines))->number,
  1070.              headers, GroupDoc);
  1071.      if (syncnum >= 0)
  1072.        mylstrncpy ((header_elt (headers, syncnum))->references,
  1073.          refer, HEADER_REFERENCES_LENGTH);
  1074.    }
  1075.  
  1076.    unlock_headers (header_handle, thread_handle);
  1077.  
  1078.    CommDoc->ActiveLines++;
  1079.    update_window_title (CommDoc->hDocWnd, group,
  1080.               RcvLineCount++,
  1081.               CommDoc->TotalLines * total_xhdrs);
  1082.  
  1083.       }
  1084.  
  1085.       break;
  1086.  
  1087.  
  1088.     case ST_XHDR_MID_START:
  1089.       retcode = 0;
  1090.       sscanf (CommLineIn, "%d", &retcode);
  1091.       if (check_server_code (retcode))
  1092.    break;
  1093.       CommState = ST_XHDR_MID_DATA;
  1094.       CommDoc->ActiveLines = 0;
  1095.       break;
  1096.  
  1097.     case ST_XHDR_MID_DATA:
  1098.       if (strcmp (CommLineIn, ".") == 0) {
  1099.    LockLine (CommDoc->hParentBlock, CommDoc->ParentOffset,
  1100.         CommDoc->ParentLineID, &BlockPtr, &LinePtr);
  1101.  
  1102.    GroupDoc = (TypGroup far *) ((char far *) LinePtr + sizeof (TypLine));
  1103.  
  1104.    GroupDoc->total_headers = CommDoc->ActiveLines;
  1105.  
  1106.    first = GroupDoc->ServerFirst;
  1107.    last = GroupDoc->ServerLast;
  1108.  
  1109.    GlobalUnlock (BlockPtr->hCurBlock);
  1110.    CommDoc->ActiveLines = 0;
  1111.  
  1112.    /* Now ask for the subject lines */
  1113.    mylen = sprintf (mybuf, "XHDR subject %ld-%ld", first, last);
  1114.    CommState = ST_XHDR_SUBJECT_START;
  1115.    PutCommLine (mybuf);
  1116.       }
  1117.       else {
  1118.    /* Access the Group struct, get HANDLE for header data */
  1119.    LockLine (CommDoc->hParentBlock, CommDoc->ParentOffset,
  1120.         CommDoc->ParentLineID, &BlockPtr, &LinePtr);
  1121.  
  1122.    GroupDoc = (TypGroup far *) ((char far *) LinePtr + sizeof (TypLine));
  1123.  
  1124.    header_handle = GroupDoc->header_handle;
  1125.    thread_handle = GroupDoc->thread_handle;
  1126.  
  1127.    GlobalUnlock (BlockPtr->hCurBlock);
  1128.  
  1129.    /* Lock the header data */
  1130.    headers = lock_headers (header_handle, thread_handle);
  1131.  
  1132.    syncnum = sync_artnum (atol (CommLineIn),
  1133.              (header_elt (headers, CommDoc->ActiveLines))->number,
  1134.                 headers,
  1135.                 GroupDoc);
  1136.    if (syncnum >= 0)
  1137.      mylstrncpy ((header_elt (headers, syncnum))->message_id,
  1138.             (char far *) (get_xhdr_line (CommLineIn)),
  1139.             HEADER_MESSAGE_ID_LENGTH); /* bad, hardcoded. */
  1140.  
  1141.    unlock_headers (header_handle, thread_handle);
  1142.  
  1143.    CommDoc->ActiveLines++;
  1144.    update_window_title (CommDoc->hDocWnd, group,
  1145.               RcvLineCount++,
  1146.               CommDoc->TotalLines * total_xhdrs);
  1147.  
  1148.       }
  1149.  
  1150.       break;
  1151.  
  1152.  
  1153.     case ST_XHDR_SUBJECT_START:
  1154.       retcode = 0;
  1155.       sscanf (CommLineIn, "%d", &retcode);
  1156.       if (check_server_code (retcode))
  1157.    break;
  1158.       CommState = ST_XHDR_SUBJECT_DATA;
  1159.       break;
  1160.  
  1161.     case ST_XHDR_SUBJECT_DATA:
  1162.  
  1163.       if (strcmp (CommLineIn, ".") == 0) {
  1164.    CommState = ST_IN_GROUP;
  1165.    CommBusy = FALSE;
  1166.    finish_header_retrieval (CommDoc);
  1167.    InvalidateRect (CommDoc->hDocWnd, NULL, FALSE);
  1168.       }
  1169.       else {
  1170.  
  1171.    artnum = 0;
  1172.    sscanf (CommLineIn, "%ld", &artnum);
  1173.    if (artnum) {
  1174.      LockLine (CommDoc->hParentBlock, CommDoc->ParentOffset,
  1175.           CommDoc->ParentLineID, &BlockPtr, &LinePtr);
  1176.  
  1177.      GroupDoc = ((TypGroup far *) ((char far *) LinePtr + sizeof (TypLine)));
  1178.      header_handle = GroupDoc->header_handle;
  1179.      thread_handle = GroupDoc->thread_handle;
  1180.      headers = lock_headers (header_handle, thread_handle);
  1181.  
  1182.      /* update the seen thing. */
  1183.      syncnum = sync_artnum (atol (CommLineIn),
  1184.              (header_elt (headers, CommDoc->ActiveLines))->number,
  1185.              headers,
  1186.              GroupDoc);
  1187.      if (syncnum >= 0)
  1188.        header = header_elt (headers, syncnum);
  1189.      else
  1190.        header = header_elt (headers, CommDoc->ActiveLines);
  1191.  
  1192.      header->Selected = FALSE;
  1193.      header->ArtDoc = (TypDoc *) NULL;
  1194.      header->Seen =
  1195.        WasArtSeen (artnum, (TypGroup far *) (((char far *) LinePtr) +
  1196.                     sizeof (TypLine)));
  1197.  
  1198.      UnlockLine (BlockPtr, LinePtr, &(CommDoc->hParentBlock),
  1199.             &(CommDoc->ParentOffset), &(CommDoc->ParentLineID));
  1200.  
  1201.      mylstrncpy (header->subject,
  1202.             get_xhdr_line (CommLineIn), HEADER_SUBJECT_LENGTH);
  1203.  
  1204.      CommDoc->LongestLine = max (CommDoc->LongestLine,
  1205.                   ARTICLE_SUBJECT_OFFSET +
  1206.                   (unsigned) lstrlen (header->subject));
  1207.  
  1208.      unlock_headers (header_handle, thread_handle);
  1209.      CommDoc->ActiveLines++;
  1210.      update_window_title (CommDoc->hDocWnd, group,
  1211.                 RcvLineCount++,
  1212.                 CommDoc->TotalLines * total_xhdrs);
  1213.  
  1214.    }
  1215.       }
  1216.  
  1217.       break;
  1218.  
  1219.     case ST_IN_GROUP:
  1220.       break;
  1221.  
  1222.     case ST_ARTICLE_RESP:
  1223.       retcode = 0;
  1224.       sscanf (CommLineIn, "%d", &retcode);
  1225.       if (check_server_code (retcode)) {
  1226.    if (CommDoc->hDocWnd)
  1227.      DestroyWindow (CommDoc->hDocWnd);
  1228.    break;
  1229.       }
  1230.       CommState = ST_REC_ARTICLE_HEADER;
  1231.       /*      Bossanova = FALSE; */
  1232.       break;
  1233.  
  1234.     case ST_REC_ARTICLE_HEADER:
  1235.       if (strcmp (CommLineIn, ".") == 0) {
  1236.    ;        /* error: empty article (end in middle of header) */
  1237.  
  1238.       }
  1239.       if (IsBlankStr (CommLineIn))  /* headers end in blank line */
  1240.    CommState = ST_REC_ARTICLE;
  1241.       AddCommLineToDoc (CommLineIn);
  1242.       break;
  1243.  
  1244.     case ST_REC_ARTICLE:
  1245.       if (strcmp (CommLineIn, ".") == 0) {   /* article receive complete */
  1246.  
  1247.    CommState = ST_IN_GROUP;
  1248.    CommBusy = FALSE;
  1249.  
  1250.    if (CommDecoding) {
  1251.      SendMessage (currentCoded->hParentWnd, IDM_ARTICLE_RETRIEVE_COMPLETE, 0, 0);
  1252.      break;
  1253.    }
  1254.    else
  1255.    {
  1256.           SendMessage (CommDoc->ParentDoc->hDocWnd, IDM_ARTICLE_RETRIEVE_COMPLETE, 0, 0);
  1257.           SendMessage(CommDoc->hDocWnd, IDM_ARTICLE_RETRIEVE_COMPLETE, 0, 0);
  1258.         }
  1259.  
  1260.    LockLine (CommDoc->ParentDoc->hParentBlock,
  1261.         CommDoc->ParentDoc->ParentOffset,
  1262.         CommDoc->ParentDoc->ParentLineID,
  1263.         &BlockPtr, &LinePtr);
  1264.  
  1265.    GroupDoc = (TypGroup far *) ((char far *) LinePtr + sizeof (TypLine));
  1266.    header_handle = GroupDoc->header_handle;
  1267.    thread_handle = GroupDoc->thread_handle;
  1268.  
  1269.    headers = lock_headers (header_handle, thread_handle);
  1270.    lpsz = (char far *) ((header_elt (headers, CommDoc->LastSeenLineID))->subject);
  1271.    unlock_headers (header_handle, thread_handle);
  1272.  
  1273.    mylstrncpy (group, lpsz, MAXGROUPNAME);
  1274.    sprintf (mybuf, "%s (%u lines)", group, CommDoc->TotalLines);
  1275.    SetWindowText (CommDoc->hDocWnd, mybuf);
  1276.    InvalidateRect (CommDoc->hDocWnd, NULL, FALSE);
  1277.    GlobalUnlock (BlockPtr->hCurBlock);
  1278.  
  1279.    /* Skip to the first line of the text of the article
  1280.     * and make sure it's visible on the screen.  This is
  1281.     * so that the user doesn't have to have the first
  1282.     * screen filled with a lengthy, worthless header.
  1283.     *
  1284.     * and save number of header lines (on display)
  1285.     * for later (Bretherton)
  1286.     */
  1287.    TopOfDoc (CommDoc, &BlockPtr, &LinePtr);
  1288.    found = FALSE;
  1289.    do {
  1290.      lpsz = ((char far *) LinePtr + sizeof (TypLine) + sizeof (TypText));
  1291.      if (IsLineBlank (lpsz)) {
  1292.        found = TRUE;
  1293.        CommDoc->HeaderLines = WhatLine (BlockPtr, LinePtr);
  1294.        break;
  1295.      }
  1296.      if (!NextLine (&BlockPtr, &LinePtr))
  1297.        break;
  1298.    }
  1299.    while (!found);
  1300.    NextLine (&BlockPtr, &LinePtr);
  1301.  
  1302.    /* If the line is in the last screen's worth of lines, back
  1303.     * up the pointer so it points to the first line of the last
  1304.     * screen.
  1305.     */
  1306.    if (found && CommDoc->TotalLines > CommDoc->ScYLines &&
  1307.        !CommDoc->TopScLineID)
  1308.      AdjustTopSc (BlockPtr, LinePtr);
  1309.  
  1310.    UnlockLine (BlockPtr, LinePtr, &hBlock, &Offset, &MyLineID);
  1311.       }
  1312.       else        /* not finished, continue article receive */
  1313.    AddCommLineToDoc (CommLineIn);
  1314.  
  1315.       break;
  1316.  
  1317.     case ST_POST_WAIT_PERMISSION:
  1318.  
  1319.       /*      WndPost = getWndEdit(WndPosts,CommWnd,MAXPOSTWNDS) ; */
  1320.       /*      found = (WndPost != NULL) ; */
  1321.  
  1322.       /* lock out changes */
  1323.       SendMessage (PostEdit->hWndEdit, EM_SETREADONLY, TRUE, 0L);
  1324.  
  1325.       retcode = 0;
  1326.       sscanf (CommLineIn, "%u", &retcode);
  1327.  
  1328.       if (retcode == 340) {
  1329.    if (Attaching && !ReviewAttach)
  1330.      ProcessAttach (CONTINUE);
  1331.    else
  1332.      PostText (PostEdit);
  1333.       }
  1334.       else {
  1335.    check_server_code (retcode);
  1336.    MessageBox (PostEdit->hWnd, CommLineIn + 4, "Cannot Post Article",
  1337.           MB_OK | MB_ICONEXCLAMATION);
  1338.    CommBusy = FALSE;
  1339.    CommState = ST_NONE;
  1340.  
  1341.    if (Attaching && !ReviewAttach)
  1342.      ProcessAttach (ABORT);   /* cancel attachment */
  1343.  
  1344.    else
  1345.      /* unlock to allow user modification */
  1346.      SendMessage (PostEdit->hWndEdit, EM_SETREADONLY, FALSE, 0L);
  1347.       }
  1348.       break;
  1349.  
  1350.     case ST_POST_WAIT_END:
  1351.  
  1352.       /*      WndPost = getWndEdit(WndPosts,CommWnd,MAXPOSTWNDS) ; */
  1353.       /*      found = (WndPost != NULL) ; */
  1354.  
  1355.       /* no check for failure to find posting documents */
  1356.       retcode = 0;
  1357.       sscanf (CommLineIn, "%d", &retcode);
  1358.       if (retcode == 441 || retcode == 440) {
  1359.    CompletePost(PostEdit, FAIL);
  1360.    done = TRUE;
  1361.    nextBatchIndex = 0;  /* cancel any batch */
  1362.  
  1363.    if (Attaching && !ReviewAttach)
  1364.      ProcessAttach (ABORT);/* cancel attachment */
  1365.  
  1366.       }
  1367.       else if (retcode == 240) {
  1368.    CompletePost(PostEdit, SUCCESS);
  1369.    done = TRUE;
  1370.       }
  1371.       else if (check_server_code (retcode))
  1372.    break;
  1373.  
  1374.       if (done) {
  1375.    CommBusy = FALSE;
  1376.    CommState = ST_NONE;
  1377.    PostEdit = (WndEdit *) NULL;
  1378.  
  1379.    if (nextBatchIndex)  /* if we're sending a batch, send the next */
  1380.      BatchSend (DOCTYPE_POSTING);
  1381.       }
  1382.       if (Attaching && !ReviewAttach)
  1383.    ProcessAttach (CONTINUE);
  1384.       break;
  1385.  
  1386.       /* the following code is for an MRR-hacked nntp server */
  1387.  
  1388.     case ST_GROUP_REJOIN:
  1389.       CommState = ST_ARTICLE_RESP;
  1390.       break;
  1391.     }
  1392.   }
  1393. }
  1394.  
  1395. BOOL
  1396. isLineQuotation (char *textptr)
  1397. {
  1398.   char *loc;
  1399.   loc = memchr (textptr, QuoteLineInd, 2);
  1400.   if (!loc)
  1401.     loc = memchr (textptr, '|', 2);
  1402.   if (!loc)
  1403.     loc = memchr (textptr, ':', 2);
  1404.   return (loc != NULL);
  1405. }
  1406.  
  1407.  
  1408. /*-- function AddCommLineToDoc ---------------------------------------
  1409.  *
  1410.  *  Adds the given line to the comm doc
  1411.  */
  1412. void
  1413. AddCommLineToDoc (char *line)
  1414. {
  1415.   TypLine far *LinePtr;
  1416.   TypBlock far *BlockPtr;
  1417.   char *cptr, *cdest;
  1418.   int col, mylen;
  1419.   char artline[MAXINTERNALLINE];
  1420.  
  1421.   /* special case for lines starting with '..' */
  1422.   if (strncmp (CommLineIn, "..", 2))
  1423.     cptr = CommLineIn;
  1424.   else
  1425.     cptr = CommLineIn + 1;
  1426.  
  1427.   if (CommDecoding) {
  1428.     DecodeLine (currentCoded, cptr);
  1429.     return;
  1430.   }
  1431.   /* Copy this line into an image of a textblock line,
  1432.    * expanding tabs.
  1433.    */
  1434.   cdest = artline + sizeof (TypLine) + sizeof (TypText);
  1435.   for (col = 0;
  1436.   *cptr && col < (MAXINTERNALLINE - 3 * sizeof (TypLine) - sizeof (TypText));
  1437.        cptr++) {
  1438.     if (*cptr == '\t') {
  1439.       do {
  1440.    *(cdest++) = ' ';
  1441.       }
  1442.       while (++col & 7);
  1443.     }
  1444.     else {
  1445.       *(cdest++) = *cptr;
  1446.       col++;
  1447.     }
  1448.   }
  1449.   *(cdest++) = '\0';
  1450.  
  1451.   ((TypLine *) artline)->LineID = NextLineID++;
  1452.   LockLine (CommDoc->hCurAddBlock, CommDoc->AddOffset, CommDoc->AddLineID, &BlockPtr, &LinePtr);
  1453.   mylen = (cdest - artline) + sizeof (int);
  1454.   mylen += mylen % 2;
  1455.   ((TypText *) (artline + sizeof (TypLine)))->NameLen =
  1456.     (cdest - 1) - (artline + sizeof (TypLine) + sizeof (TypText));
  1457.   ((TypLine *) artline)->length = mylen;
  1458.   ((TypLine *) artline)->active = TRUE;
  1459.   *((int *) (artline + mylen - sizeof (int))) = mylen;
  1460.   AddLine ((TypLine *) artline, &BlockPtr, &LinePtr);
  1461.   CommDoc->LongestLine = max (CommDoc->LongestLine, (unsigned) mylen);
  1462.   CommDoc->ActiveLines++;
  1463.   UnlockLine (BlockPtr, LinePtr, &(CommDoc->hCurAddBlock),
  1464.          &(CommDoc->AddOffset), &(CommDoc->AddLineID));
  1465.  
  1466.   if ((CommDoc->TotalLines % UPDATE_ART_FREQ) == 0)
  1467.     InvalidateRect (CommDoc->hDocWnd, NULL, FALSE);
  1468. }
  1469.  
  1470. /*-- function WasArtSeen ---------------------------------------------
  1471.  *
  1472.  *  Determines whether (according to the information in a TypGroup entry)
  1473.  *  a given article number was seen.
  1474.  *
  1475.  *  Returns TRUE iff the article has been seen.
  1476.  */
  1477. BOOL
  1478. WasArtSeen (ArtNum, GroupPtr)
  1479.      unsigned long ArtNum;
  1480.      TypGroup far *GroupPtr;
  1481. {
  1482.   TypRange far *RangePtr = (TypRange far *) ((char far *)
  1483.             GroupPtr + RangeOffset (GroupPtr->NameLen));
  1484.   unsigned int nr;
  1485.  
  1486.   for (nr = 0; nr < GroupPtr->nRanges; nr++) {
  1487.     if (ArtNum >= (unsigned long) RangePtr->First &&
  1488.    ArtNum <= (unsigned long) RangePtr->Last) {
  1489.       return (TRUE);
  1490.     }
  1491.     else {
  1492.       RangePtr++;
  1493.     }
  1494.   }
  1495.   return (FALSE);
  1496. }
  1497.  
  1498.  
  1499. /*--- function mylstrncmp -----------------------------------------------
  1500.  *
  1501.  *   Just like strncmp, except takes long pointers.
  1502.  */
  1503. int
  1504. mylstrncmp (ptr1, ptr2, len)
  1505.      char far *ptr1;
  1506.      char far *ptr2;
  1507.      int len;
  1508. {
  1509.   for (; len--; ptr1++, ptr2++) {
  1510.     if (*ptr1 > *ptr2) {
  1511.       return (1);
  1512.     }
  1513.     else if (*ptr1 < *ptr2) {
  1514.       return (-1);
  1515.     }
  1516.   }
  1517.   return (0);
  1518. }
  1519.  
  1520. /*--- function mylstrncpy -----------------------------------------------
  1521.  *
  1522.  *   Just like strncpy, except takes long pointers.
  1523.  */
  1524. char far *
  1525. mylstrncpy (ptr1, ptr2, len)
  1526.      char far *ptr1;
  1527.      char far *ptr2;
  1528.      int len;
  1529. {
  1530.   char far *targ = ptr1;
  1531.  
  1532.   for (; --len && *ptr2; ptr1++, ptr2++) {
  1533.     *ptr1 = *ptr2;
  1534.   }
  1535.   *ptr1 = '\0';
  1536.   return (targ);
  1537. }
  1538.  
  1539. /* this is a temporary test... */
  1540. char far *
  1541. mylstrcpy (ptr1, ptr2)
  1542.      char_p ptr1;
  1543.      char far *ptr2;
  1544. {
  1545.   char far *targ = ptr1;
  1546.   for (; *ptr2; ptr1++, ptr2++) {
  1547.     *ptr1 = *ptr2;
  1548.   }
  1549.   *ptr1 = '\0';
  1550.   return (targ);
  1551. }
  1552.  
  1553. #if 0
  1554. /*--- function lstrcmpnoblank ------------------------------------------
  1555.  *
  1556.  *   Like strcmp, except takes long pointers and also stops at
  1557.  *   the first blank.
  1558.  */
  1559. int
  1560. lstrcmpnoblank (str1, str2)
  1561.      char far **str1;
  1562.      char far **str2;
  1563. {
  1564.   register char far *s1 = *str1, far * s2 = *str2;
  1565.  
  1566.   for (; *s1 && *s2 && *s1 != ' ' && *s2 != ' '; s1++, s2++) {
  1567.     if (*s1 > *s2) {
  1568.       return (1);
  1569.     }
  1570.     else if (*s1 < *s2) {
  1571.       return (-1);
  1572.     }
  1573.   }
  1574.   if (*s1 == *s2) {
  1575.     return (0);
  1576.   }
  1577.   else if (*s1) {
  1578.     return (1);
  1579.   }
  1580.   else {
  1581.     return (-1);
  1582.   }
  1583. }
  1584. #endif
  1585.  
  1586. void
  1587. finish_header_retrieval ()
  1588. {
  1589.   TypLine far *LinePtr;
  1590.   TypBlock far *BlockPtr;
  1591.   TypGroup far *GroupDoc;
  1592.   HANDLE header_handle, thread_handle;
  1593. /*  HANDLE hBlock; */
  1594.   char far *lpsz;
  1595.   char group[MAXGROUPNAME];
  1596.   char mybuf[MAXINTERNALLINE];
  1597.   header_p headers;
  1598.  
  1599.   /* release the mouse that is captured to the usenet window */
  1600.   ReleaseCapture ();
  1601.  
  1602.   CommDoc->TotalLines = CommDoc->ActiveLines;
  1603.   /* Disabled by MRR so that ActiveLines is the number of lines
  1604.    * we should display in the Group window.  Eventually, will
  1605.    * change it so that ActiveLines will count only unread articles
  1606.    * if the user desires.
  1607.    */
  1608.   /* CommDoc->ActiveLines = 0; */
  1609.   /* Fetch this group's line in NetDoc so we can get the
  1610.    * group's name for the window's title bar.
  1611.    */
  1612.   LockLine (CommDoc->hParentBlock, CommDoc->ParentOffset,
  1613.        CommDoc->ParentLineID, &BlockPtr, &LinePtr);
  1614.  
  1615.   GroupDoc = ((TypGroup far *) ((char far *) LinePtr + sizeof (TypLine)));
  1616.   header_handle = GroupDoc->header_handle;
  1617.   thread_handle = GroupDoc->thread_handle;
  1618.   lock_headers (header_handle, thread_handle);
  1619.  
  1620.   if (threadp) {
  1621.     SetWindowText (CommDoc->hDocWnd, "sorting headers...");
  1622.     sort_by_threads (header_handle, thread_handle, CommDoc->TotalLines);
  1623.   }
  1624.  
  1625.   unlock_headers (header_handle, thread_handle);
  1626.  
  1627.   GroupDoc->total_headers = CommDoc->TotalLines;
  1628.  
  1629.   lpsz = (char far *) (((char far *) LinePtr) +
  1630.              sizeof (TypLine) + sizeof (TypGroup));
  1631.  
  1632.   mylstrncpy (group, lpsz, MAXGROUPNAME);
  1633.   sprintf (mybuf, "%s (%u articles)", group, CommDoc->TotalLines);
  1634.   SetWindowText (CommDoc->hDocWnd, mybuf);
  1635.  
  1636.   /* If we have information from NEWSRC on the highest-
  1637.    * numbered article previously seen, position the window
  1638.    * so the new articles can be seen without scrolling.
  1639.    */
  1640.   {
  1641.     unsigned int i;
  1642.  
  1643.     LockLine (CommDoc->hParentBlock, CommDoc->ParentOffset,
  1644.          CommDoc->ParentLineID, &BlockPtr, &LinePtr);
  1645.  
  1646.     /* inside the lock, can access the GroupStruct */
  1647.     GroupDoc = ((TypGroup far *) ((char far *) LinePtr + sizeof (TypLine)));
  1648.     header_handle = GroupDoc->header_handle;
  1649.     thread_handle = GroupDoc->thread_handle;
  1650.     headers = lock_headers (header_handle, thread_handle);
  1651.  
  1652.     if (CommDoc->TotalLines > 0)
  1653.       for (i = CommDoc->TotalLines - 1;
  1654.       ((i > 0) && ((header_elt (headers, i))->Seen == FALSE));
  1655.       i--);
  1656.     CommDoc->TopLineOrd = (i > 5) ? i - 4 : 0;
  1657.  
  1658.     unlock_headers (header_handle, thread_handle);
  1659.   }
  1660.  
  1661.   SendMessage (CommDoc->hDocWnd, IDM_RETRIEVE_COMPLETE, 0, 0);
  1662.   InvalidateRect (CommDoc->hDocWnd, NULL, FALSE);
  1663. }
  1664.  
  1665. /*
  1666.  * Look through the MAIL or Post edits and return the edit with
  1667.  * matching window handle Consider - centralising initial window
  1668.  * location in wvmail and wndpost using a single array (save passing
  1669.  * structure and size into this module)
  1670.  *
  1671.  */
  1672.  
  1673. WndEdit *
  1674. getWndEdit (WndEdit * WndEdits, HWND hWnd, int numEntries)
  1675. {
  1676.   int ih;
  1677.  
  1678.   for (ih = 0; ih < numEntries; ih++) {
  1679.     if (WndEdits[ih].hWnd == hWnd) {
  1680.       return &WndEdits[ih];
  1681.     }
  1682.   }
  1683.  
  1684.   /*MessageBox(0,"getWndEditFound Nothing","mrb debug", MB_OK | MB_ICONHAND); */
  1685.  
  1686.   return (WndEdit *) NULL;
  1687. }
  1688.  
  1689.  
  1690.  
  1691. /* ------------------------------------------------------------------------
  1692.  * Replace any white space at end of string with NULL's
  1693.  * JSC 11/1/93
  1694.  */
  1695. void
  1696. RemoveTrailingWhiteSpace (char *str)
  1697. {
  1698.   register int i;
  1699.  
  1700.   for (i = strlen (str) - 1; i > 0 && isspace (str[i]); i--)
  1701.     str[i] = '\0';
  1702. }
  1703.  
  1704. /*------------------------------------------------------------------------------
  1705.  * IsBlankStr
  1706.  * Returns true if the string is entirely whitespace, else false
  1707.  * JSC 12/6/93
  1708.  */
  1709. BOOL
  1710. IsBlankStr (char *temp)
  1711. {
  1712.   register char *ptr;
  1713.  
  1714.   for (ptr = temp; *ptr; ptr++)
  1715.     if (!isspace (*ptr))
  1716.       return (FALSE);
  1717.   return (TRUE);
  1718. }
  1719.  
  1720. /*------------------------------------------------------------------------------
  1721.  * isnumber
  1722.  * Returns true if the string is a all digits
  1723.  * JSC 12/6/93
  1724.  */
  1725. BOOL
  1726. isnumber (char *str)
  1727. {
  1728.   char *ptr;
  1729.  
  1730.   for (ptr = str; *ptr != '\0'; ptr++)
  1731.     if (!isdigit (*ptr))
  1732.       return (FALSE);
  1733.   return (TRUE);
  1734. }
  1735.  
  1736. /* ------------------------------------------------------------------------
  1737.  *    Open the common font dialog
  1738.  *      Place resulting selection name and size in face,style and size
  1739.  *      Note: to select a printer font, send style as "Printer"
  1740.  *      printer font selection ignores any chosen style
  1741.  *      (JSC 1/9/94)
  1742.  */
  1743. BOOL
  1744. AskForFont (HWND hParentWnd, char *face, int *size, char *style)
  1745. {
  1746.   LOGFONT lf;
  1747.   CHOOSEFONT cf;
  1748.   HDC hDC;
  1749.  
  1750.   memset (&lf, 0, sizeof (LOGFONT));
  1751.   strcpy (lf.lfFaceName, face);
  1752.   /* convert points to logical units (1 pt = 1/72 inch) */
  1753.   /* For printer fonts, use ScreenYPixels here anyway - the choosefont */
  1754.   /* dialog appears to require the lfHeight to be in screen units */
  1755.   /* we will convert point size to PrinterUnits in InitPrinterFonts() */
  1756.   lf.lfHeight = -MulDiv (*size, ScreenYPixels, 72);
  1757.  
  1758.   memset (&cf, 0, sizeof (CHOOSEFONT));
  1759.   cf.lStructSize = sizeof (CHOOSEFONT);
  1760.   cf.hwndOwner = hParentWnd;
  1761.   cf.lpLogFont = &lf;
  1762.   if (!stricmp (style, "Printer")) {
  1763.     cf.nFontType = PRINTER_FONTTYPE;
  1764.     hDC = GetPrinterDC (hParentWnd);
  1765.     cf.hDC = hDC;
  1766.     cf.Flags = CF_PRINTERFONTS | CF_INITTOLOGFONTSTRUCT | CF_FORCEFONTEXIST;
  1767.   }
  1768.   else {
  1769.     cf.nFontType = SCREEN_FONTTYPE;
  1770.     cf.Flags = CF_SCREENFONTS | CF_INITTOLOGFONTSTRUCT | CF_USESTYLE | CF_FORCEFONTEXIST;
  1771.     cf.lpszStyle = style;
  1772.   }
  1773.   if (!ChooseFont (&cf))
  1774.     return (FAIL);
  1775.  
  1776. /*      if (!stricmp (style, "Printer"))      // commented out JD 6/17/94 */
  1777.   /*         ReleaseDC (hWndConf, hDC);  */
  1778.  
  1779. /*      if (!stricmp (style, "Printer")) */
  1780.   /*         DeletePrinterDC (hDC); */
  1781.  
  1782.   *size = cf.iPointSize / 10; /* iPointSize is in tenths of a point */
  1783.  
  1784.   strcpy (face, lf.lfFaceName);
  1785.   return (SUCCESS);
  1786. }
  1787.  
  1788. /* ------------------------------------------------------------------------
  1789.  *    Open the common color dialog
  1790.  *      (JSC 1/9/94)
  1791.  */
  1792. BOOL
  1793. AskForColor (HWND hParentWnd, COLORREF * color)
  1794. {
  1795.   CHOOSECOLOR cc;
  1796.   COLORREF nearC;
  1797.   HDC hDC;
  1798.  
  1799.   memset (&cc, 0, sizeof (CHOOSECOLOR));
  1800.   cc.lStructSize = sizeof (CHOOSECOLOR);
  1801.   cc.hwndOwner = hParentWnd;
  1802.   cc.rgbResult = *color;
  1803.   cc.lpCustColors = CustomColors;
  1804.   cc.Flags = CC_RGBINIT;
  1805.  
  1806.   if (!ChooseColor (&cc))
  1807.     return (FAIL);
  1808.  
  1809.   /* until we figure out how to deal with dithered colors, force */
  1810.   /* the color to the nearest physical color */
  1811.   hDC = GetDC (hParentWnd);
  1812.   nearC = GetNearestColor (hDC, cc.rgbResult);
  1813.   if (cc.rgbResult != nearC)
  1814.     MessageBox (hParentWnd, "WinVn does not currently support dithered (non-solid) colors.\nThe nearest physical solid color has been selected.",
  1815.       "Sorry", MB_OK | MB_ICONINFORMATION);
  1816.   *color = nearC;
  1817.   ReleaseDC (hParentWnd, hDC);
  1818.   return (SUCCESS);
  1819. }
  1820.  
  1821. /* ------------------------------------------------------------------------
  1822.  * This should be used instead of EM_GETHANDLE on global edit buf
  1823.  * Returns a string containing the contents of an edit wnd
  1824.  * It is the reponsibility of the caller to GlobalFreePtr the string
  1825.  * (JSC)
  1826.  */
  1827. char *
  1828. GetEditText (HWND hWndEdit)
  1829. {
  1830.   unsigned int size;
  1831.   char *newText;
  1832.  
  1833.   /* SendMessage (hWndEdit, EM_FMTLINES, TRUE, 0); */
  1834.  
  1835.   size = (unsigned int) SendMessage (hWndEdit, WM_GETTEXTLENGTH, 0, 0L) + 1;
  1836.  
  1837.   if ((newText = (char *) GlobalAllocPtr (GMEM_MOVEABLE, size * sizeof (char))) == NULL) {
  1838.     MessageBox (hWndEdit, "Memory allocation failure", "Edit Text", MB_OK);
  1839.     return (NULL);
  1840.   }
  1841.   if (SendMessage (hWndEdit, WM_GETTEXT, size, (LPARAM) ((LPCSTR) newText)) != (long) (size - 1)) {
  1842.     MessageBox (hWndEdit, "Failed to get text", "Edit Text", MB_OK);
  1843.     return (NULL);
  1844.   }
  1845.  
  1846.   return (newText);
  1847. }
  1848. LRESULT
  1849. SetEditText (HWND hWndEdit, char *editMem)
  1850. {
  1851.   return (SendMessage (hWndEdit, WM_SETTEXT, 0, (LPARAM) (LPCSTR) editMem));
  1852. }
  1853.  
  1854. /* ------------------------------------------------------------------------
  1855.  * To maximize amount of data allowable in posting, allocate a new
  1856.  * data segment for this posting from the Global heap, and set the
  1857.  * window hInstance to this segment
  1858.  * Don't forget you can't use EM_GET/SETHANDLE on an edit buf with
  1859.  * a global mem segment
  1860.  */
  1861. BOOL
  1862. CreateEditWnd (WndEdit * NewWnd)
  1863. {
  1864. #ifndef _WIN32
  1865.   GLOBALHANDLE editDS;
  1866.   LPVOID lpPtr;
  1867.   if ((editDS = GlobalAlloc (GMEM_DDESHARE | GMEM_MOVEABLE | GMEM_ZEROINIT, 1024L)) == NULL) {
  1868.     MessageBox (NewWnd->hWnd, "Memory allocation failure", "Edit Buffer", MB_OK);
  1869.     editDS = hInst;     /* use local heap instead */
  1870.  
  1871.   }
  1872.   else {
  1873.     lpPtr = GlobalLock (editDS);
  1874.     LocalInit (HIWORD ((LONG) lpPtr), 0, (WORD) (GlobalSize (editDS) - 16));
  1875.     UnlockSegment (HIWORD ((LONG) lpPtr));   /* we still have a global lock */
  1876.  
  1877.   }
  1878.   NewWnd->hWndEdit = CreateWindow ((LPCSTR) "edit", (LPCSTR) NULL,
  1879.           WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL | WS_BORDER |
  1880.          ES_LEFT | ES_MULTILINE | ES_AUTOHSCROLL | ES_AUTOVSCROLL,
  1881.                0, 0, 0, 0,
  1882.           NewWnd->hWnd, (HMENU) EDITID, (HINSTANCE) HIWORD ((LONG) lpPtr), NULL);
  1883. #else
  1884.   NewWnd->hWndEdit = CreateWindow ("edit", (char *) NULL,
  1885.           WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL | WS_BORDER |
  1886.          ES_LEFT | ES_MULTILINE | ES_AUTOHSCROLL | ES_AUTOVSCROLL,
  1887.                0, 0, 0, 0,
  1888.                NewWnd->hWnd, (HMENU) EDITID, hInst, NULL);  /* No problems with Win32 */
  1889. #endif
  1890.   if (!NewWnd->hWndEdit) {
  1891.     MessageBox (NewWnd->hWnd, "Window creation failure", "Edit Buffer", MB_OK);
  1892.     return (FAIL);
  1893.   }
  1894.  
  1895.   SendMessage (NewWnd->hWndEdit, EM_LIMITTEXT, 0, 0L);
  1896.   SetHandleBkBrush (NewWnd->hWndEdit, hArticleBackgroundBrush);
  1897.   NewWnd->dirty = DT_CLEAN;
  1898.  
  1899.   return (SUCCESS);
  1900. }
  1901. /* ------------------------------------------------------------------------
  1902.  *    Write an integer to the private profile
  1903.  */
  1904. BOOL
  1905. WritePrivateProfileInt (lpAppName, lpKeyName, intval, lpProFile)
  1906.      char far *lpAppName;
  1907.      char far *lpKeyName;
  1908.      char far *lpProFile;
  1909.      int intval;
  1910. {
  1911.   char msg[20];
  1912.  
  1913.   itoa (intval, msg, 10);
  1914.   return (WritePrivateProfileString (lpAppName, lpKeyName, msg, lpProFile));
  1915. }
  1916.  
  1917. /* ------------------------------------------------------------------------
  1918.  *    Write an unsigned integer to the private profile
  1919.  *      (JSC 1/8/94)
  1920.  */
  1921. BOOL
  1922. WritePrivateProfileUInt (lpAppName, lpKeyName, intval, lpProFile)
  1923.      char far *lpAppName;
  1924.      char far *lpKeyName;
  1925.      char far *lpProFile;
  1926.      unsigned int intval;
  1927. {
  1928.   char msg[20];
  1929.  
  1930.   uitoa (intval, msg, 10);
  1931.   return (WritePrivateProfileString (lpAppName, lpKeyName, msg, lpProFile));
  1932. }
  1933.  
  1934. /* ------------------------------------------------------------------------
  1935.  *    Get an unsigned integer to the private profile
  1936.  *      (JSC 1/8/94)
  1937.  */
  1938. unsigned int
  1939. GetPrivateProfileUInt (lpAppName, lpKeyName, intval, lpProFile)
  1940.      char far *lpAppName;
  1941.      char far *lpKeyName;
  1942.      char far *lpProFile;
  1943.      unsigned int intval;
  1944. {
  1945.   char buf[20];
  1946.  
  1947.   GetPrivateProfileString (lpAppName, lpKeyName, "", buf, 20, lpProFile);
  1948.  
  1949.   if (buf[0] == '\0')
  1950.     return (intval);
  1951.   else
  1952.     return (atoui (buf));
  1953. }
  1954. /* ------------------------------------------------------------------------
  1955.  *    Refresh Window functions
  1956.  *      Called after a font/color selection has changed to affect all
  1957.  *      windows of a certain type (group/article/status)
  1958.  *      (JSC 1/9/94)
  1959.  */
  1960. void
  1961. RefreshGroupWnds ()
  1962. {
  1963.   register int i;
  1964.   for (i = 0; i < MAXGROUPWNDS; i++)
  1965.     if (GroupDocs[i].InUse && GroupDocs[i].hDocWnd) {
  1966.       SetHandleBkBrush (GroupDocs[i].hDocWnd, hListBackgroundBrush);
  1967.       SendMessage (GroupDocs[i].hDocWnd, WM_SIZE, 0, 0L);
  1968.       InvalidateRect (GroupDocs[i].hDocWnd, NULL, TRUE);
  1969.     }
  1970. }
  1971.  
  1972. void
  1973. RefreshArticleWnds ()
  1974. {
  1975.   register int i;
  1976.  
  1977.   for (i = 0; i < MAXARTICLEWNDS; i++)
  1978.     if (ArticleDocs[i].InUse && ArticleDocs[i].hDocWnd) {
  1979.       SetHandleBkBrush (ArticleDocs[i].hDocWnd, hArticleBackgroundBrush);
  1980.       SendMessage (ArticleDocs[i].hDocWnd, WM_SIZE, 0, 0L);
  1981.       InvalidateRect (ArticleDocs[i].hDocWnd, NULL, TRUE);
  1982.     }
  1983.  
  1984.   for (i = 0; i < MAXPOSTWNDS; i++)
  1985.     if (WndPosts[i].hWnd) {
  1986.       SendMessage (WndPosts[i].hWndEdit, WM_SETFONT, (WPARAM) hFontArtNormal, TRUE);
  1987.       SetHandleBkBrush (WndPosts[i].hWndEdit, hArticleBackgroundBrush);
  1988.       InvalidateRect (WndPosts[i].hWndEdit, NULL, TRUE);
  1989.     }
  1990.  
  1991.   for (i = 0; i < MAXMAILWNDS; i++)
  1992.     if (WndMails[i].hWnd) {
  1993.       SendMessage (WndMails[i].hWndEdit, WM_SETFONT, (WPARAM) hFontArtNormal, TRUE);
  1994.       SetHandleBkBrush (WndMails[i].hWndEdit, hArticleBackgroundBrush);
  1995.       InvalidateRect (WndMails[i].hWndEdit, NULL, TRUE);
  1996.     }
  1997.  
  1998. }
  1999. void
  2000. RefreshStatusWnds ()
  2001. {
  2002.   register int i;
  2003.  
  2004.   for (i = 0; i < NumStatusTexts; i++)
  2005.     if (CodingStatusText[i]->hTextWnd) {
  2006.       SetHandleBkBrush (CodingStatusText[i]->hTextWnd, hStatusBackgroundBrush);
  2007.       SendMessage (CodingStatusText[i]->hTextWnd, WM_SIZE, 0, 0L);
  2008.       InvalidateRect (CodingStatusText[i]->hTextWnd, NULL, TRUE);
  2009.     }
  2010.   if (hCodedBlockWnd) {
  2011.     SetHandleBkBrush (hCodedBlockWnd, hStatusBackgroundBrush);
  2012.     SendMessage (hCodedBlockWnd, WM_SIZE, 0, 0L);
  2013.     InvalidateRect (hCodedBlockWnd, NULL, TRUE);
  2014.   }
  2015.  
  2016. }
  2017. /* ------------------------------------------------------------------------
  2018.  *    Close Window functions
  2019.  *      Batch operation, close all windows of a certain type
  2020.  *      (group/article/status)
  2021.  *      (JSC 1/18/94)
  2022.  */
  2023. void
  2024. CloseGroupWnds ()
  2025. {
  2026.   register int i;
  2027.   for (i = 0; i < MAXGROUPWNDS; i++)
  2028.     if (GroupDocs[i].InUse && GroupDocs[i].hDocWnd && (!CommBusy || &GroupDocs[i] != CommDoc))
  2029.       SendMessage (GroupDocs[i].hDocWnd, WM_CLOSE, 0, 0L);
  2030. }
  2031.  
  2032. void
  2033. CloseArticleWnds ()
  2034. {
  2035.   register int i;
  2036.  
  2037.   for (i = 0; i < MAXARTICLEWNDS; i++)
  2038.     if (ArticleDocs[i].InUse && ArticleDocs[i].hDocWnd && (!CommBusy || &ArticleDocs[i] != CommDoc))
  2039.       SendMessage (ArticleDocs[i].hDocWnd, WM_CLOSE, 0, 0L);
  2040. }
  2041. void
  2042. CloseStatusWnds ()
  2043. {
  2044.   /* destroying a coding status text is like popping from a stack */
  2045.   /* so we just loop while the top of the stack still exists */
  2046.   int numSkipped = 0;
  2047.   while (numSkipped < NumStatusTexts && CodingStatusText[numSkipped]->hTextWnd)
  2048.     if (!CodingStatusText[numSkipped]->IsBusy)
  2049.       SendMessage (CodingStatusText[numSkipped]->hTextWnd, WM_CLOSE, 0, 0L);
  2050.     else
  2051.       numSkipped++;
  2052. #if 0
  2053.     if (CodingState) {
  2054.       MessageBox (CodingStatusText[0]->hTextWnd,
  2055.         "Please wait until en/decoding is complete",
  2056.         "Cannot close status window", MB_OK | MB_ICONSTOP);
  2057.       break;
  2058.     }
  2059.     else
  2060.       SendMessage (CodingStatusText[0]->hTextWnd, WM_CLOSE, 0, 0L);
  2061. #endif
  2062. }
  2063.  
  2064. /* ------------------------------------------------------------------------
  2065.  *    CascadeWindows (and helper CascadeWnd)
  2066.  *      cascade em
  2067.  *      jsc 9/18/94
  2068.  */
  2069. HWND
  2070. CascadeWnd(HWND hWnd, HWND prevWnd, int nthWnd, int width, int height, int maxX, int maxY)
  2071. {
  2072.   short x, y;
  2073.   
  2074. //  if (IsMaximized(hWnd))
  2075. //     ShowWindow(hWnd, SW_SHOWNORMAL);
  2076.  
  2077.   x = (nthWnd * 12) % maxX;
  2078.   y = (nthWnd * (CaptionHeight+2)) % maxY;
  2079.   SetWindowPos(hWnd, prevWnd, x, y, width, height, SWP_DRAWFRAME);
  2080.   
  2081.   return hWnd;
  2082. }
  2083.  
  2084. void
  2085. CascadeWindows()
  2086. {
  2087.   register int i;
  2088.   int nthWnd, width, height, maxX, maxY;
  2089.   HWND prevWnd;
  2090.  
  2091.   width = (int)(xScreen>>1);
  2092.   height = (int)(yScreen>>1);
  2093.   maxX = 3 * (width>>1);    /* 3/4 screen width  */
  2094.   maxY = 3 * (height>>1);    /* 3/4 screen height */
  2095.   
  2096.   prevWnd = CascadeWnd(hWndConf, (HWND)NULL, 1, width, height, maxX, maxY);
  2097.   nthWnd = 2;
  2098.   for (i = 0; i < MAXGROUPWNDS; i++)
  2099.     if (GroupDocs[i].InUse && GroupDocs[i].hDocWnd && !IsMinimized(GroupDocs[i].hDocWnd)) {
  2100.         prevWnd = CascadeWnd(GroupDocs[i].hDocWnd, prevWnd, nthWnd, width, height, maxX, maxY);
  2101.         nthWnd++;
  2102.     }
  2103.     
  2104.   for (i = 0; i < MAXARTICLEWNDS; i++)
  2105.     if (ArticleDocs[i].InUse && ArticleDocs[i].hDocWnd && !IsMinimized(ArticleDocs[i].hDocWnd)) {
  2106.         prevWnd = CascadeWnd(ArticleDocs[i].hDocWnd, prevWnd, nthWnd, width, height, maxX, maxY);
  2107.         nthWnd++;
  2108.     }
  2109.  
  2110.   for (i = 0; i < MAXPOSTWNDS; i++)
  2111.     if (WndPosts[i].hWnd && !IsMinimized(WndPosts[i].hWnd)) {
  2112.         prevWnd = CascadeWnd(WndPosts[i].hWnd, prevWnd, nthWnd, width, height, maxX, maxY);
  2113.         nthWnd++;
  2114.     }
  2115.  
  2116.   for (i = 0; i < MAXMAILWNDS; i++)
  2117.     if (WndMails[i].hWnd && !IsMinimized(WndMails[i].hWnd)) {
  2118.         prevWnd = CascadeWnd(WndMails[i].hWnd, prevWnd, nthWnd, width, height, maxX, maxY);
  2119.         nthWnd++;
  2120.     }
  2121.  
  2122.   for (i = 0; i < NumStatusTexts; i++)
  2123.     if (CodingStatusText[i]->hTextWnd && !IsMinimized(CodingStatusText[i]->hTextWnd)) {
  2124.         prevWnd = CascadeWnd(CodingStatusText[i]->hTextWnd, prevWnd, nthWnd, width, height, maxX, maxY);
  2125.         nthWnd++;
  2126.     }
  2127.  
  2128.   /* move coded block status window to top center */
  2129.   if (hCodedBlockWnd)
  2130.     SetWindowPos(hCodedBlockWnd, (HWND)NULL, (xScreen-STATUSWIDTH)>>1, 1, STATUSWIDTH, STATUSHEIGHT, SWP_DRAWFRAME);
  2131.   
  2132. }
  2133. /* ------------------------------------------------------------------------
  2134.  *    BatchSend
  2135.  *      type is DOCTYPE_MAIL or _POST
  2136.  *      Increments nextBatchIndex and initiates mail/post
  2137.  *      Note: on entry nextBatchIndex is the index we will use for this send
  2138.  *      on exit, nextBatchIndex is either 0 (no more to send) or the index
  2139.  *      of the next mail/post to send
  2140.  *      (JSC 1/18/94)
  2141.  */
  2142. void
  2143. BatchSend (int type)
  2144. {
  2145.   int thisSend;
  2146.   int maxWnds;
  2147.   WndEdit *WndEdits;
  2148.  
  2149.   if (type == DOCTYPE_POSTING) {
  2150.     WndEdits = WndPosts;
  2151.     maxWnds = MAXPOSTWNDS;
  2152.   }
  2153.   else {
  2154.     WndEdits = WndMails;
  2155.     maxWnds = MAXMAILWNDS;
  2156.   }
  2157.  
  2158.   thisSend = nextBatchIndex;
  2159.   if (thisSend == 0) {     /* find first in batch (if any) */
  2160.     while (thisSend < maxWnds)
  2161.       if (WndEdits[thisSend].hWnd)
  2162.    break;
  2163.       else
  2164.    thisSend++;
  2165.  
  2166.     if (thisSend == maxWnds)
  2167.       return;        /* no open windows.  cancel */
  2168.  
  2169.     nextBatchIndex = thisSend;
  2170.   }
  2171.  
  2172.   /* find next in batch (if any) */
  2173.   while (++nextBatchIndex < maxWnds)
  2174.     if (WndEdits[nextBatchIndex].hWnd)
  2175.       break;
  2176.  
  2177.   if (nextBatchIndex == maxWnds)
  2178.     nextBatchIndex = 0;    /* no more */
  2179.  
  2180.   if (type == DOCTYPE_POSTING)
  2181.     StartPost (&WndEdits[thisSend]);
  2182.   else
  2183.     StartMail (&WndEdits[thisSend]);
  2184. }
  2185.  
  2186. /* ------------------------------------------------------------------------
  2187.  *    Test busy functions
  2188.  *      Called to test if a comm or decoding is busy
  2189.  *      Returns true if busy, false if not busy
  2190.  *      (JSC 1/9/94)
  2191.  */
  2192. BOOL
  2193. TestCommBusy (HWND hParentWnd, char *msg)
  2194. {
  2195.   if (CommBusy) {
  2196.     MessageBox (hParentWnd,
  2197.       "Sorry, I am already busy communicating with the server.\n"
  2198.       "Try again in a little while.", msg,
  2199.       MB_OK | MB_ICONASTERISK);
  2200.     return (TRUE);
  2201.   }
  2202.   else
  2203.     return (FALSE);
  2204. }
  2205.  
  2206. BOOL
  2207. TestDecodeBusy (HWND hParentWnd, char *msg)
  2208. {
  2209.   if (Decoding || CommDecoding) {
  2210.     MessageBox (hParentWnd,
  2211.         "Sorry, I can only handle one en/decoding session at a time.\n"
  2212.       "Try again in a little while.", msg,
  2213.       MB_OK | MB_ICONASTERISK);
  2214.     return (TRUE);
  2215.   }
  2216.   else
  2217.     return (FALSE);
  2218. }
  2219.  
  2220. /* ------------------------------------------------------------------------
  2221.  *        Update the mail menus -- called on mail transport change
  2222.  *        jsc 9/9/94
  2223.  */
  2224. void
  2225. UpdateAllMailMenus()
  2226. {
  2227.     register int i;
  2228.     
  2229.     SetMainMailMenu(hWndConf);
  2230.     for (i = 0; i < MAXARTICLEWNDS; i++)
  2231.         if (ArticleDocs[i].hDocWnd) 
  2232.             SetArticleMailMenu(ArticleDocs[i].hDocWnd);
  2233.  
  2234.     for (i = 0; i < MAXGROUPWNDS; i++)
  2235.         if (GroupDocs[i].hDocWnd)
  2236.             SetGroupMailMenu(GroupDocs[i].hDocWnd);
  2237. }       
  2238.