home *** CD-ROM | disk | FTP | other *** search
/ Dream 52 / Amiga_Dream_52.iso / Amiga / Workbench / Archivers / mpackWOS.lha / mpackppc / src / macmpack.c < prev    next >
C/C++ Source or Header  |  1998-04-08  |  54KB  |  2,129 lines

  1. /* macmpack.c -- Mac user interface to mpack routines
  2.  *
  3.  * (C) Copyright 1993-1995 by Carnegie Mellon University
  4.  * All Rights Reserved.
  5.  *
  6.  * Permission to use, copy, modify, distribute, and sell this software
  7.  * and its documentation for any purpose is hereby granted without fee,
  8.  * provided that the above copyright notice appear in all copies and
  9.  * that both that copyright notice and this permission notice appear in
  10.  * supporting documentation, and that the name of Carnegie Mellon University
  11.  * not be used in advertising or publicity pertaining to distribution of the
  12.  * software without specific, written prior permission.  Carnegie
  13.  * Mellon University makes no representations about the suitability of
  14.  * this software for any purpose.  It is provided "as is" without
  15.  * express or implied warranty.
  16.  *
  17.  * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
  18.  * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  19.  * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
  20.  * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  21.  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
  22.  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
  23.  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
  24.  * SOFTWARE.
  25.  *
  26.  * NOTE: a good GUI requires a lot of work...  This needs more.
  27.  */
  28.  
  29. #include <Folders.h>
  30. #include <Script.h>
  31. #include <GestaltEqu.h>
  32.  
  33. #include <stdio.h>
  34. #include <time.h>
  35. #include <string.h>
  36. #include <ctype.h>
  37. #include "version.h"
  38. #include "part.h"
  39. #include "macnapp.h"
  40. #include "macmpack.h"
  41. #include "macICTypes.h"
  42. #include "macICAPI.h"
  43. #include "macICKeys.h"
  44.  
  45. /* ThinkC's internal stdio functions: */
  46. #include <ansi_private.h>
  47.  
  48. /* window types: */
  49. #define DECODELIST    1
  50. #define PREFWIN        2
  51.  
  52. /* save watch cursor */
  53. Cursor watch;
  54.  
  55. /* preferences */
  56. struct pref_folder *pfolder = NULL;
  57. struct mpack_preferences **mpack_prefs = NULL;
  58. static ICInstance icinst = NULL;
  59.  
  60. /* flag for active help window */
  61. static WindowPtr helpw = NULL;
  62.  
  63. /* active decode status window */
  64. static na_win *curstatwin = NULL;
  65. short didchat;
  66.  
  67. /* MacTCP started: -1 = error, 1 = active, 0 = unknown */
  68. static short tcpstart = 0;
  69.  
  70. /* this is used for opening TEXT files */
  71. SFTypeList textList = { 'TEXT', 0, 0, 0 };
  72.  
  73. /* next two types are used in the dialog used to select files to decode
  74.  */
  75. typedef struct filelist {
  76.     short vRefNum;
  77.     long dirID;
  78.     PCstr fname[65];
  79. } filelist;
  80. typedef struct listwin {
  81.     na_win win;
  82.     int count;
  83.     filelist **hflist;
  84.     ListHandle l;
  85. } listwin;
  86.  
  87. /* this is the status window for decoding
  88.  */
  89. typedef struct statuswin {
  90.     na_win win;
  91.     RgnHandle urgn;            /* user region */
  92.     Rect urect;                /* user rectangle */
  93.     Rect frect;                /* frame rectangle */
  94.     short row;                /* row at top of scroll area */
  95.     short nrow;                /* rows of status text */
  96.     long size, used;        /* bytes of status text & amount used */
  97.     Handle text;            /* status text */
  98.     ControlHandle sb;        /* scroll bar control */
  99.     short height, ascent;    /* font height and ascent */
  100. } statuswin;
  101.  
  102. /* this is for the encode window
  103.  */
  104. typedef struct encodewin {
  105.     nate_win w;
  106.     Boolean nateon;                /* cursor in Desc edit field */
  107.     Boolean useemail;            /* sending email */
  108.     long partsize;                /* max part size (0 = no limit) */
  109.     OSType ftype;                /* type of file to encode */
  110.     FSSpec fspec;                /* file to encode */
  111.     FSSpec ofile;                /* output file */
  112. } encodewin;
  113.  
  114. /* show progress
  115.  */
  116. typedef struct progresswin {
  117.     natcp_win w;
  118.     short percent;
  119. } progresswin;
  120.  
  121. /* send mail
  122.  */
  123. typedef struct mailwin {
  124.     progresswin w;
  125.     Handle headers;        /* email headers */
  126.     Handle envelope;    /* envelope */
  127.     short state;        /* state */
  128.     short remaining;    /* messages remaining */
  129.     Boolean sending;    /* flag for active SMTP task */
  130.     Boolean useemail;    /* sending email */
  131.     Boolean gothost;    /* got the hostname */
  132.     long partsize;        /* max part size (0 = no limit) */
  133.     long dirID;            /* ID for temp dir */
  134.     OSType ftype;        /* type of file to encode */
  135.     FSSpec fspec;        /* file to be encoded */
  136.     FSSpec ofile;        /* output file */
  137.     FILE *dfile;        /* desc file */
  138.     PCstr server[257];    /* SMTP server */
  139.     PCstr subj[257];    /* subject */
  140.     CInfoPBRec cpb;
  141. } mailwin;
  142.  
  143. /* mailwin states */
  144. #define MS_MACTCP    0    /* Starting MacTCP */
  145. #define MS_GETHOST    1    /* Getting hostname */
  146. #define MS_ENCODE    2    /* Do encoding */
  147. #define MS_SENDING    3    /* Sending email */
  148.  
  149. /* some prototypes */
  150. void warn(char *str);
  151. void stattext(Str255, unsigned char);
  152. static void do_decodefiles(na_win *);
  153. static void addfile(listwin *, FSSpec *);
  154. static void removefile(listwin *);
  155. static short listclose(na_win *);
  156. static short listmouse(na_win *, Point, short, short);
  157. static short listctrl(na_win *, Point, short, short, ControlHandle);
  158. static short listupdate(na_win *, Boolean);
  159. static short listinit(na_win *,long *);
  160. static short prefsctrl(na_win *, Point, short, short, ControlHandle);
  161. static short prefsinit(na_win *, long *);
  162. static void do_decode(FSSpec *);
  163. static void do_encode(FSSpec *, OSType);
  164. static short mainmenu(struct na_win*, WORD, WORD);
  165.  
  166. #define dwin ((listwin *) win)
  167. #define swin ((statuswin *) win)
  168. #define ewin ((encodewin *) win)
  169. #define twin ((nate_win *) win)
  170. #define prwin ((progresswin *) win)
  171. #define mwin ((mailwin *) win)
  172.  
  173. /* Get a FILE* to a Macintosh file
  174.  *******************************     ###############################
  175.  * KLUDGE ALERT! KLUDGE ALERT! *     # KLUDGE ALERT! KLUDGE ALERT! #
  176.  *******************************     ###############################
  177.  * Mac files are specified by name/vRefNum/dirID combo, but the portable
  178.  * portions of mpack use FILE* to do I/O.  We need a way to get an open FILE*
  179.  * from a file specified by name/vRefNum/dirID.  Here we use the proper Macintosh
  180.  * routines to open a file, then hack together a FILE* using ThinkC's internal
  181.  * routines.  The major trouble is that we have no way to get at the FILE action
  182.  * procedure (fp->proc), so we need a sample FILE* to be passed in.  Bleargh!
  183.  *******************************     ###############################
  184.  * KLUDGE ALERT! KLUDGE ALERT! *     # KLUDGE ALERT! KLUDGE ALERT! #
  185.  *******************************     ###############################
  186.  */
  187. FILE *Macopen(FILE *sample, Str255 name, short vRefNum, long dirID,
  188.                 short binary_flag, short res_fork, SignedByte permission)
  189. {
  190.     FILE *fp = NULL;
  191.     short refnum;
  192.     long curEOF;
  193.     OSErr err;
  194.     
  195.     if ((!res_fork && (err = HOpen(vRefNum, dirID, name, permission, &refnum)) == noErr)
  196.         || (res_fork && (err = HOpenRF(vRefNum, dirID, name, permission, &refnum)) == noErr)) {
  197.         if ((fp = __getfile()) == NULL) {
  198.             FSClose(refnum);
  199.         } else {
  200.             if (permission == fsWrPerm) {
  201.                 /* if we're writing to the file, truncate it */
  202.                 SetEOF(refnum, curEOF = 0);
  203.             } else {
  204.                 GetEOF(refnum, &curEOF);
  205.             }
  206.             fp->refnum = refnum;
  207.             fp->len = (fpos_t) curEOF;
  208.             fp->binary = binary_flag;
  209.             setvbuf(fp, NULL, _IOFBF, BUFSIZ);
  210.             fp->proc = sample->proc;
  211.         }
  212.     }
  213.     
  214.     return (fp);
  215. }
  216.  
  217.  
  218. /* warn the user
  219.  */
  220. void warn(char *str)
  221. {
  222.     PCstr wstr[257];
  223.     
  224.     CtoPCstrncpy(wstr, str, 255);
  225.     ParamText(P(wstr), NULL, NULL, NULL);
  226.     NAalert(warnALRT);
  227. }
  228.  
  229. /* yell at the user
  230.  */
  231. void yell(char *str)
  232. {
  233.     PCstr wstr[257];
  234.     
  235.     CtoPCstrncpy(wstr, str, 255);
  236.     ParamText(P(wstr), NULL, NULL, NULL);
  237.     NAalert(errorALRT);
  238. }
  239.  
  240. /* chat with user
  241.  */
  242. chat(char *str)
  243. {
  244.     PCstr tmpstr[257];
  245.  
  246.     CtoPCstrcpy(tmpstr, str);
  247.     stattext(P(tmpstr), 0);
  248. }
  249.  
  250. /* returns NA_ALLCLOSED if appropriate, else NA_CLOSED
  251.  */
  252. static short alldone(na_win *win)
  253. {
  254.     if (win->next == NULL && win->afterp == NULL && (*mpack_prefs)->quit_finished
  255.         && RecoverHandle((Ptr) win) == (Handle) NAhead) {
  256.         return (NA_ALLCLOSED);
  257.     }
  258.     
  259.     return (NA_CLOSED);
  260. }
  261.  
  262. /* update procedure for status dialog box
  263.  */
  264. static short statupdate(na_win *win, Boolean newsize)
  265. {
  266.     RgnHandle savergn;
  267.     unsigned char *s;
  268.     short row, top;
  269.     Rect tmpr;
  270.     
  271.     FrameRect(&swin->frect);
  272.     savergn = NewRgn();
  273.     if (savergn) {
  274.         GetClip(savergn);
  275.         SetClip(swin->urgn);
  276.     }
  277.     
  278.     /* redraw text area */
  279.     HLock(swin->text);
  280.     s = * (unsigned char **) swin->text;
  281.     top = swin->urect.top;
  282.     for (row = 0; row < swin->row; ++row) {
  283.         s += s[1] + 2;
  284.     }
  285.     for (; row < swin->nrow && top + swin->height <= swin->urect.bottom; ++row) {
  286.         MoveTo(swin->urect.left, top + swin->ascent);
  287.         if (*s) TextFace(1);
  288.         DrawString(s + 1);
  289.         if (*s) TextFace(0);
  290.         /* advance to next string */
  291.         top += swin->height;
  292.         s += s[1] + 2;
  293.     }
  294.     HUnlock(swin->text);
  295.     
  296.     if (savergn) {
  297.         SetClip(savergn);
  298.         DisposeRgn(savergn);
  299.     }
  300.  
  301.     return (NA_NOTPROCESSED);
  302. }
  303.  
  304. /* refresh status window
  305.  */
  306. void statrefresh()
  307. {
  308.     na_win *win = curstatwin;
  309.     
  310.     Draw1Control(swin->sb);
  311.     statupdate(win, false);
  312. }
  313.  
  314. /* add text to the status window
  315.  */
  316. void stattext(Str255 str, unsigned char bold)
  317. {
  318.     na_win *win = curstatwin;
  319.     short i, len;
  320.     unsigned char *s, *start;
  321.     RgnHandle rgn;
  322.     Rect tmpr;
  323.     
  324.     if (!win) return;
  325.     didchat = 1;
  326.     
  327.     /* advance to next row */
  328.     if (swin->height * (swin->nrow++ - swin->row)
  329.         >= swin->urect.bottom - swin->urect.top) {
  330.         SetCtlMax(swin->sb, ++swin->row);
  331.         SetCtlValue(swin->sb, swin->row);
  332.         if ((rgn = NewRgn()) != NULL) {
  333.             tmpr = swin->urect;
  334.             ScrollRect(&tmpr, 0, -swin->height, rgn);
  335.             DisposeRgn(rgn);
  336.         }
  337.     }
  338.     
  339.     /* add the text */
  340.     len = * (unsigned char *) str;
  341.     if (swin->size - swin->used < len + 1) {
  342.         SetHandleSize(swin->text, swin->size * 2);
  343.         if (MemError() == 0) swin->size *= 2;
  344.     }
  345.     HLock(swin->text);
  346.     s = start = * (unsigned char **) swin->text;
  347.     for (i = 1; i < swin->nrow; ++i) {
  348.         s += s[1] + 2;
  349.     }
  350.     if (len + 2 + s < start + swin->size) {
  351.         *s = bold;
  352.         memcpy(s + 1, str, len + 1);
  353.         swin->used = s + len + 2 - start;
  354.     }
  355.     HUnlock(swin->text);
  356.     statupdate(win, false);
  357. }
  358.  
  359. /* scroll the status dialog
  360.  */
  361. static void statscroll(na_win *win, short rows)
  362. {
  363.     RgnHandle rgn;
  364.     
  365.     if ((rgn = NewRgn()) != NULL) {
  366.         SetCtlValue(swin->sb, swin->row += rows);
  367.         ScrollRect(&swin->urect, 0, - swin->height * rows, rgn);
  368.         EraseRgn(rgn);
  369.         DisposeRgn(rgn);
  370.     }
  371.     statupdate(win, false);
  372. }
  373.  
  374. /* scroll bar procedure
  375.  */
  376. static pascal void statscollbar(ControlHandle ctrlh, short part)
  377. {
  378.     na_win *win = (na_win *) GetCRefCon(ctrlh);
  379.     short max, new, page;
  380.     
  381.     max = GetCtlMax(ctrlh);
  382.     page = (swin->urect.bottom - swin->urect.top) / swin->height - 1;
  383.     switch (part) {
  384.         case inUpButton:
  385.             page = 1;
  386.             /* fall through */
  387.         case inPageUp:
  388.             if (swin->row > 0) {
  389.                 statscroll(win, - (swin->row < page ? swin->row : page));
  390.             }
  391.             break;
  392.         case inDownButton:
  393.             page = 1;
  394.             /* fall through */
  395.         case inPageDown:
  396.             if (swin->row < max) {
  397.                 statscroll(win, max - swin->row < page ? max - swin->row : page);
  398.             }
  399.             break;
  400.         case inThumb:
  401.             break;
  402.     }
  403. }
  404.  
  405. /* control procedure for status dialog box
  406.  */
  407. static short statctrl(na_win *win, Point p, short item, short mods, ControlHandle ctrlh)
  408. {
  409.     short value;
  410.     
  411.     if (ctrlh == swin->sb) {
  412.         ctrlh = swin->sb;
  413.         if (item != inThumb) {
  414.             SetCRefCon(ctrlh, (long) win);
  415.             TrackControl(ctrlh, p, statscollbar);
  416.         } else {
  417.             TrackControl(ctrlh, p, nil);
  418.             value = GetCtlValue(ctrlh);
  419.             if (value != swin->row) statscroll(win, value - swin->row);
  420.         }
  421.     } else if (item == iOk) {
  422.         return (NA_REQCLOSE);
  423.     }
  424.     
  425.     return (NA_NOTPROCESSED);
  426. }
  427.  
  428. /* close procedure for status dialog box
  429.  */
  430. static short statclose(na_win *win)
  431. {
  432.     DisposeRgn(swin->urgn);
  433.     DisposHandle(swin->text);
  434.     DisposeControl(swin->sb);
  435.     
  436.     return (alldone(win));
  437. }
  438.  
  439. /* init procedure for status dialog box
  440.  */
  441. static short statinit(na_win *win, long *data)
  442. {
  443.     Rect tmpr;
  444.     FontInfo finfo;
  445.     
  446.     /* disable OK button while working */
  447.     NAhiliteDItem(win->pwin, iOk, 255);
  448.     
  449.     /* set up status text area & font */
  450.     if ((swin->urgn = NewRgn()) == NULL) return (NA_CLOSED);
  451.     TextFont(geneva);
  452.     TextSize(9);
  453.     GetFontInfo(&finfo);
  454.     swin->ascent = finfo.ascent;
  455.     swin->height = finfo.ascent + finfo.descent + finfo.leading;
  456.     NAgetDRect(win->pwin, iStatus, &swin->frect);
  457.     swin->urect = swin->frect;
  458.     InsetRect(&swin->urect, 2, 
  459.         2 + ((swin->urect.bottom - swin->urect.top - 4) % swin->height) / 2);
  460.     RectRgn(swin->urgn, &swin->urect);
  461.  
  462.     /* set up text storage */
  463.     if ((swin->text = NewHandle(swin->size = 1024)) == NULL) {
  464.         DisposeRgn(swin->urgn);
  465.         return (NA_CLOSED);
  466.     }
  467.     **(char **)swin->text = '\0';
  468.     
  469.     /* set up scrollbar */
  470.     NAgetDRect(win->pwin, iStatScroll, &tmpr);
  471.     swin->sb = NewControl(win->pwin, &tmpr, "\p", true, 0, 0, 0, scrollBarProc, 0);
  472.     if (!swin->sb) {
  473.         DisposeRgn(swin->urgn);
  474.         DisposHandle(swin->text);
  475.         return (NA_CLOSED);
  476.     }
  477.     
  478.     /* set up procedures */
  479.     win->closep = statclose;
  480.     win->ctrlp = statctrl;
  481.     win->updatep = statupdate;
  482.     
  483.     /* keep window locked until decoding is done */
  484.     ++win->locks;
  485.     curstatwin = win;
  486.     
  487.     return (NA_NOTPROCESSED);
  488. }
  489.  
  490. /* process the files in the file list
  491.  */
  492. static void do_decodefiles(na_win *win)
  493. {
  494.     int count = dwin->count;
  495.     filelist *fl;
  496.     FILE *dfile, *tmpf;
  497.     extern long _ftype, _fcreator;
  498.     long ticks;
  499.     int result;
  500.     
  501.     MapTypeCreator("text/plain", 0);
  502.     SetCursor(&watch);
  503.     if (NAwindow(0, NA_DIALOGWINDOW | NA_TITLEBAR | NA_DEFBUTTON | NA_USERESOURCE
  504.         | NA_CLOSEBOX | NA_HASCONTROLS,
  505.         0, dstatDLOG, 0, sizeof (statuswin), statinit) == NA_CLOSED) {
  506.         warn("Not enough memory to decode");
  507.         return;
  508.     }
  509.     MoveHHi((Handle) dwin->hflist);
  510.     HLock((Handle) dwin->hflist);
  511.     fl = *dwin->hflist;
  512.     tmpf = tmpfile();
  513.     while (count--) {
  514.         stattext(fl->fname, 1);
  515.         didchat = 0;
  516.         if (dfile = Macopen(tmpf, fl->fname, fl->vRefNum, fl->dirID, 0, 0, 1)) {
  517.             result = handleMessage(part_init(dfile), "text/plain",
  518.                 0, (*mpack_prefs)->extract_text);
  519.             if (result != 0 || didchat <= 0) {
  520.                 if (didchat < 0) {
  521.                     chat("Decoding cancelled");
  522.                 } else {
  523.                     chat("Found nothing to decode");
  524.                 }
  525.             }
  526.             fclose(dfile);
  527.         } else {
  528.             chat("Couldn't find source file");
  529.         }
  530.         ++fl;
  531.     }
  532.     fclose(tmpf);
  533.     HUnlock((Handle) dwin->hflist);
  534.     NAhiliteDItem(curstatwin->pwin, iOk, 0);
  535.     NAunlockWindow(curstatwin);
  536.     curstatwin = NULL;
  537.     SetCursor(&arrow);
  538.     DisposHandle((Handle) dwin->hflist);
  539. }
  540.  
  541. /* return non-zero if two filenames have the same prefix
  542.  */
  543. static int fprefixMatch(char *base, PCstr *match)
  544. {
  545.     PCstr temp[257];
  546.     char *scan;
  547.     short prefixlen;
  548.     
  549.     PtoPCstrcpy(temp, base);
  550.     scan = C(temp) + PCstrlen(temp) - 1;
  551.     while (isdigit(*scan) && scan > C(temp)) --scan;
  552.     prefixlen = scan - C(temp) + 1;
  553.     if (strncmp(C(temp), C(match), prefixlen)) return (0);
  554.     scan = C(match) + prefixlen;
  555.     while (isdigit(*scan)) ++scan;
  556.     
  557.     return (!*scan);
  558. }
  559.  
  560. /* do the add of a file to a list
  561.  */
  562. static void addit(listwin *dw, short vRefNum, long dirID, char *fname)
  563. {
  564.     long size = GetHandleSize((Handle) dw->hflist) / sizeof (filelist);
  565.     filelist *fl;
  566.     char *bp;
  567.     Cell c;
  568.     int i;
  569.     PCstr fbuf[42];
  570.  
  571.     if (size == dw->count) {
  572.         SetHandleSize((Handle) dw->hflist, (++size * sizeof (filelist)));
  573.         if (MemError() != noErr) return;
  574.     }
  575.     MoveHHi((Handle) dw->hflist);
  576.     HLock((Handle) dw->hflist);
  577.     fl = *dw->hflist;
  578.     for (i = dw->count; i; --i, ++fl) {
  579.         if (fl->vRefNum == vRefNum && fl->dirID == dirID &&
  580.             *fl->fname == *fname && !strncmp(C(fl->fname), C(fname), *fl->fname)) {
  581.             break;
  582.         }
  583.     }
  584.     if (!i) {
  585.         fl->vRefNum = vRefNum;
  586.         fl->dirID = dirID;
  587.         PtoPCstrcpy(fl->fname, fname);
  588.         SetPt(&c, 0, dw->count);
  589.         LAddRow(1, ++dw->count, dw->l);
  590.         LSetCell((Ptr) C(fname), (short) Pstrlen(fname), c, dw->l);
  591.     }
  592.     HUnlock((Handle) dw->hflist);
  593. }
  594.  
  595. /* add file set to file list
  596.  */
  597. static void addfile(dw, fspec)
  598.     listwin *dw;
  599.     FSSpec *fspec;
  600. {
  601.     CInfoPBRec cipbr;
  602.     HFileInfo *fpb = (HFileInfo *)&cipbr;
  603.     PCstr fbuf[42];
  604.     short idx, foundone = 0;
  605.     long procid;
  606.     
  607.     /* remove working directory stuff */
  608.     if (fspec->parID == 0) {
  609.         GetWDInfo(fspec->vRefNum, &fspec->vRefNum, &fspec->parID, &procid);
  610.     }
  611.  
  612.     /* loop through directory */
  613.     for (idx = 1; ; ++idx) {
  614.         fpb->ioVRefNum = fspec->vRefNum;
  615.         fpb->ioNamePtr = P(fbuf);
  616.         fpb->ioDirID = fspec->parID;
  617.         fpb->ioFDirIndex = idx;
  618.         if (PBGetCatInfoSync(&cipbr)) break;
  619.         SetClen(fbuf);
  620.         
  621.         if (!(fpb->ioFlAttrib & 16) && fprefixMatch((char *)fspec->name, fbuf)) {
  622.             addit(dw, fspec->vRefNum, fspec->parID, (char *) P(fbuf));
  623.             foundone = 1;
  624.         }
  625.     }
  626.     if (!foundone) {
  627.         addit(dw, fspec->vRefNum, fspec->parID, (char *) fspec->name);
  628.     }
  629. }
  630.  
  631. /* remove file from file list
  632.  */
  633. static void removefile(dw)
  634.     listwin *dw;
  635. {
  636.     filelist *fl;
  637.     int count;
  638.     Cell c;
  639.     
  640.     c.h = c.v = 0;
  641.     if (LGetSelect(TRUE, &c, dw->l)) {
  642.         MoveHHi((Handle) dw->hflist);
  643.         HLock((Handle) dw->hflist);
  644.         fl = *dw->hflist + c.v;
  645.         count = dw->count - c.v;
  646.         while (--count) {
  647.             fl[0] = fl[1];
  648.             ++fl;
  649.         }
  650.         HUnlock((Handle) dw->hflist);        
  651.         --dw->count;
  652.         LDelRow(1, c.v, dw->l);
  653.     }
  654. }
  655.  
  656. /* close list window
  657.  */
  658. static short listclose(win)
  659.     na_win *win;
  660. {
  661.     LDispose(dwin->l);
  662.     
  663.     return (alldone(win));
  664. }
  665.  
  666. /* mouse procedure
  667.  */
  668. static short listmouse(na_win *win, Point p, short type, short mods)
  669. {
  670.     Cell c;
  671.     
  672.     if (!(type & 1)) {
  673.         LClick(p, mods, dwin->l);
  674.         c.h = c.v = 0;
  675.         NAhiliteDItem((DialogPtr)win->pwin, iRemove, LGetSelect(TRUE, &c, dwin->l) ? 0 : 255);
  676.     }
  677.     
  678.     return (NA_NOTPROCESSED);
  679. }
  680.  
  681. /* control procedure
  682.  */
  683. static short listctrl(na_win *win, Point p, short item, short mods, ControlHandle ctrlh)
  684. {
  685.     StandardFileReply reply;
  686.  
  687.     switch (item) {
  688.         case iAdd:
  689.             NAgetFile(NULL, 1, textList, &reply);
  690.             if (reply.sfGood) {
  691.                 if (!dwin->count) {
  692.                     NAhiliteDItem((DialogPtr)win->pwin, iOk, 0);
  693.                 }
  694.                 addfile(dwin, &reply.sfFile);
  695.             }
  696.             return (NA_PROCESSED);
  697.             
  698.         case iRemove:
  699.             removefile(dwin);
  700.             NAhiliteDItem((DialogPtr)win->pwin, iRemove, 255);
  701.             if (!dwin->count) {
  702.                 NAhiliteDItem((DialogPtr)win->pwin, iOk, 255);
  703.             }
  704.             return (NA_PROCESSED);
  705.  
  706.         case iOk:
  707.             win->afterp = do_decodefiles;
  708.         case iCancel:
  709.             return (NA_REQCLOSE);
  710.     }
  711.     
  712.     return (NA_NOTPROCESSED);
  713. }
  714.  
  715. /* update the list window
  716.  */
  717. static short listupdate(na_win *win, Boolean resize)
  718. {
  719.     Rect r;
  720.     
  721.     NAgetDRect(win->pwin, iFileList, &r);
  722.     FrameRect(&r);
  723.     LUpdate(win->pwin->visRgn, dwin->l);
  724.     
  725.     return (NA_NOTPROCESSED);
  726. }
  727.  
  728. /* initialize the list window
  729.  */
  730. static short listinit(win, data)
  731.     na_win *win;
  732.     long *data;
  733. {
  734.     FSSpec *fspec = (FSSpec *) data;
  735.     Rect r, zrect;
  736.     Point p;
  737.     Handle hand;
  738.     short type;
  739.     
  740.     GetDItem((DialogPtr)win->pwin, iFileList, &type, &hand, &r);
  741.     InsetRect(&r, 1, 1);
  742.     zrect.top = zrect.bottom = zrect.left = p.h = p.v = 0;\
  743.     zrect.right = 1;
  744.     dwin->l = LNew(&r, &zrect, p, 0, win->pwin, 0, 0, 0, 1);
  745.     if (!dwin->l) return (NA_CLOSED);
  746.     (*dwin->l)->selFlags = lOnlyOne;
  747.     dwin->hflist = (filelist **) NewHandle(sizeof (filelist));
  748.     if (!dwin->hflist) {
  749.         LDispose(dwin->l);
  750.         return (NA_CLOSED);
  751.     }
  752.     dwin->count = 0;
  753.     addfile(dwin, fspec);
  754.     win->closep = listclose;
  755.     win->updatep = listupdate;
  756.     win->ctrlp = listctrl;
  757.     win->mousep = listmouse;
  758.     win->type = DECODELIST;
  759.     NAhiliteDItem((DialogPtr)win->pwin, iRemove, 255);
  760.     ShowWindow(win->pwin);
  761.     LDoDraw(TRUE, dwin->l);
  762.     
  763.     return (NA_NOTPROCESSED);
  764. }
  765.  
  766. /* Decode procedure: first get a file, then open decode window
  767.  */
  768. static void do_decode(FSSpec *fspec)
  769. {
  770.     StandardFileReply infile;
  771.     na_win **wh, *wp;
  772.     
  773.     if (!fspec) {
  774.         NAgetFile(NULL, 1, textList, &infile);
  775.         if (!infile.sfGood) return;
  776.         fspec = &infile.sfFile;
  777.     } else {
  778.         /* file supplied by drag & drop, look for existing decode window: */
  779.         for (wh = NAhead; wh && (*wh)->type != DECODELIST; wh = (*wh)->next);
  780.         if (wh && (wp = NAlockWindow(wh)) != NULL) {
  781.             addfile((listwin *) wp, fspec);
  782.             NAunlockWindow(wp);
  783.             return;
  784.         }
  785.     }
  786.     NAwindow(0, NA_DIALOGWINDOW | NA_USERESOURCE | NA_DEFBUTTON |
  787.         NA_HASCONTROLS | NA_CLOSEBOX, NULL, decodeDLOG, (long *) fspec,
  788.         sizeof (listwin), listinit);
  789. }
  790.  
  791. /* Map MIME type to/from Macintosh file types
  792.  */
  793. void MapTypeCreator(char *contenttype, OSType type)
  794. {
  795.     extern long _ftype, _fcreator;
  796.     PCstr tstr[257];
  797.     Handle h;
  798.     ICAttr attr;
  799.     long size = 0;
  800.     Ptr map;
  801.     ICMapEntry *ment;
  802.     unsigned char *scan, *end, *pstr;
  803.     short mapcount, i, foundit = 0;
  804.     OSType temp;
  805.     
  806.     if (!type) CtoPCstrncpy(tstr, contenttype, 255);
  807.     
  808.     /* first try a lookup via Internet Config */
  809.     if (icinst && ICBegin(icinst, icReadOnlyPerm) == noErr) {
  810.         if (ICGetPref(icinst, kICMapping, &attr, nil, &size) == noErr
  811.             && size > 0 && (map = NewPtr(size)) != nil) {
  812.             if (ICGetPref(icinst, kICMapping, &attr, map, &size) == noErr) {
  813.                 scan = (unsigned char *) map;
  814.                 end = scan + size;
  815.                 while (scan < end) {
  816.                     ment = (ICMapEntry *) scan;
  817.                     pstr = scan + ment->fixed_length;
  818.                     scan += ment->total_length;
  819.                     if (type && ment->file_type != type) continue;
  820.                     pstr += *pstr + 1; /* skip over extension */
  821.                     pstr += *pstr + 1; /* skip over creator app name */
  822.                     pstr += *pstr + 1; /* skip over post app name */
  823.                     if (type) {
  824.                         PtoPCstrcpy((PCstr *) contenttype, (char *) pstr);
  825.                         foundit = 1;
  826.                         break;
  827.                     } else if (EqualString(P(tstr), pstr, false, true)) {
  828.                         _ftype = ment->file_type;
  829.                         _fcreator = ment->file_creator;
  830.                         foundit = 1;
  831.                         break;
  832.                     }
  833.                 }
  834.             }
  835.             DisposPtr(map);
  836.         }
  837.         ICEnd(icinst);
  838.     }
  839.     
  840.     /* if we didn't find it, try our quick&dirty mappings */
  841.     if (!foundit) {
  842.         if (type) {
  843.             mapcount = CountResources('TyCr');
  844.             for (i = 1; i <= mapcount; ++i) {
  845.                 h = GetIndResource('TyCr', i);
  846.                 if (h && **(OSType **)h == type) {
  847.                     GetResInfo(h, &i, &temp, P(contenttype));
  848.                     if (ResError() == noErr) break;
  849.                 }
  850.             }
  851.             SetClen((PCstr *) contenttype);
  852.         } else {
  853.             h = GetNamedResource('TyCr', P(tstr));
  854.             if (h) {
  855.                 _ftype = (*(OSType **)h)[0];
  856.                 _fcreator = (*(OSType **)h)[1];
  857.             } else {
  858.                 _ftype = '????';
  859.                 _fcreator = 'mPAK';
  860.             }
  861.         }
  862.     }
  863. }
  864.  
  865. /* get internet config string prefs
  866.  */
  867. static short getICprefs(na_win *win, PCstr *eaddr, PCstr *smtphost)
  868. {
  869.     char *scan, *end;
  870.     ICAttr attr;
  871.     long size;
  872.     ICError err = noErr;
  873.     
  874.     *C(eaddr) = '\0';
  875.     SetPlen(eaddr);
  876.     *C(smtphost) = '\0';
  877.     SetPlen(smtphost);
  878.     if (icinst && ICBegin(icinst, icReadOnlyPerm) == noErr) {
  879.         size = 256;
  880.         if (ICGetPref(icinst, kICEmail, &attr, (Ptr) eaddr, &size) == noErr
  881.             && win && (attr & ICattr_locked_mask)) {
  882.             NAenableDItem(win->pwin, iEmailAddr, 0);
  883.         }
  884.         SetClen(eaddr);
  885.         size = 256;
  886.         if (ICGetPref(icinst, kICSMTPHost, &attr, (Ptr) smtphost, &size) == noErr
  887.             && win && (attr & ICattr_locked_mask)) {
  888.             NAenableDItem(win->pwin, iMailServer, 0);
  889.         }
  890.         SetClen(smtphost);
  891.         ICEnd(icinst);
  892.     } else {
  893.         HLock((Handle) mpack_prefs);
  894.         end = (char *) (*mpack_prefs) + GetHandleSize((Handle) mpack_prefs);
  895.         scan = (*mpack_prefs)->internet_host;
  896.         while (scan < end && *scan++);
  897.         if (scan < end) CtoPCstrcpy(eaddr, scan);
  898.         while (scan < end && *scan++);
  899.         if (scan < end) CtoPCstrcpy(smtphost, scan);
  900.         HUnlock((Handle) mpack_prefs);
  901.     }
  902. }
  903.  
  904. /* copy desc file, with word-wrap
  905.  */
  906. static short copydesc(FILE *out, TEHandle hTE)
  907. {
  908.     char c;
  909.     short i, count, word, col, reset;
  910.     char **htxt;
  911.     
  912.     count = (*hTE)->teLength;
  913.     htxt = (char **) (*hTE)->hText;
  914.     for (i = word = col = 0; i < count; ++i) {
  915.         c = (*htxt)[i];
  916.         reset = i - word == 80 || c == '\r';
  917.         if (reset || c == ' ') {
  918.             while (word < i) putc((*htxt)[word], out), ++word;
  919.         }
  920.         if (reset || ++col == 80) {
  921.             putc('\r', out);
  922.             c = (*htxt)[word];
  923.             if (c == ' ' || c == '\r') ++word;
  924.             col = 0;
  925.         }
  926.     }
  927.     while (word < i) putc((*htxt)[word], out), ++word;
  928.     rewind(out);
  929. }
  930.  
  931. /* start up MacTCP callback
  932.  */
  933. static void mytcpinit(short status)
  934. {
  935.     static DialogPtr dialog = NULL;
  936.     GrafPtr save;
  937.     Rect box;
  938.     
  939.     if (status < 0) {
  940.         tcpstart = -1;
  941.     } else if (status == 0) {
  942.         tcpstart = 1;
  943.     } else {
  944.         if (!dialog && status < 100) {
  945.             dialog = GetNewDialog(progDLOG, nil, (WindowPtr) -1);
  946.             NAsetIText(dialog, iWorkText, "\pWaiting for MacTCP to finish.  Press \021. to stop.");
  947.         }
  948.         if (dialog) {
  949.             GetPort(&save);
  950.             SetPort(dialog);
  951.             NAgetDRect(dialog, iProgress, &box);
  952.             FrameRect(&box);
  953.             InsetRect(&box, 1, 1);
  954.             box.right = box.left + (box.right - box.left) * status / 100;
  955.             FillRect(&box, qd.dkGray);
  956.             SetPort(save);
  957.             if (status == 100) {
  958.                 DisposDialog(dialog);
  959.                 dialog = NULL;
  960.             }
  961.         }
  962.     }
  963. }
  964.  
  965. /* update the progress bar
  966.  */
  967. static short progressupdate(na_win *win, Boolean newsize)
  968. {
  969.     Rect box;
  970.     
  971.     if (prwin->percent >= 0) {
  972.         NAgetDRect(win->pwin, iProgress, &box);
  973.         FrameRect(&box);
  974.         InsetRect(&box, 1, 1);
  975.         if (prwin->percent) {
  976.             box.right = box.left + (box.right - box.left) * prwin->percent / 100;
  977.             FillRect(&box, qd.dkGray);
  978.         } else {
  979.             EraseRect(&box);
  980.         }
  981.     }
  982.     
  983.     return (NA_NOTPROCESSED);
  984. }
  985.  
  986. /* handle the cancel button
  987.  */
  988. static short progressctrl(na_win *win, Point p, short item, short mods,
  989.     ControlHandle ctrlh)
  990. {
  991.     return (item == iCancel ? NA_REQCLOSE : NA_NOTPROCESSED);
  992. }
  993.  
  994. /* close progress window
  995.  */
  996. static short progressclose(na_win *win)
  997. {
  998.     NAmodalMenus(0);
  999.     
  1000.     return (NA_CLOSED);
  1001. }
  1002.  
  1003. /* make/go directory under prefs and return directory number
  1004.  */
  1005. static OSErr prefsubdir(PCstr *name, long *dirID)
  1006. {
  1007.     CInfoPBRec cipbr;
  1008.     DirInfo *dpb = &cipbr.dirInfo;
  1009.     long subdir, dir;
  1010.     short vref = pfolder->fspec.vRefNum;
  1011.     OSErr err;
  1012.     
  1013.     err = DirCreate(vref, dir = pfolder->fspec.parID, P(name), &subdir);
  1014.     if (err == dupFNErr) {
  1015.         dpb->ioVRefNum = vref;
  1016.         dpb->ioNamePtr = P(name);
  1017.         dpb->ioDrDirID = dir;
  1018.         dpb->ioFDirIndex = 0;
  1019.         if ((err = PBGetCatInfoSync(&cipbr)) != noErr) return (err);
  1020.         subdir = dpb->ioDrDirID;
  1021.     } else if (err != noErr) {
  1022.         return (err);
  1023.     }
  1024.     *dirID = subdir;
  1025.     
  1026.     return (noErr);
  1027. }
  1028.  
  1029. /* smtp status task
  1030.  */
  1031. static void smtpstat(void *wh, short code, short err, long num, char *errstr)
  1032. {
  1033.     na_win *win, **winh;
  1034.     char msg[256];
  1035.     OSErr oserr = noErr;
  1036.     
  1037.     /* verify win is valid */
  1038.     for (winh = NAhead; winh && winh != wh; winh = (*winh)->next);
  1039.     if (!winh) return;
  1040.     
  1041.     /* handle SMTP callback */
  1042.     win = NAlockWindow((na_win **) wh);
  1043.     if (code == NASMTP_progress) {
  1044.         prwin->percent = err;
  1045.         progressupdate(win, false);
  1046.     } else if (code == NASMTP_badaddr) {
  1047.         sprintf(msg, "Invalid address: <%s>.  Email will be sent to valid recipients.",
  1048.             errstr);
  1049.         yell(msg);
  1050.     } else {
  1051.         switch (code) {
  1052.             case NASMTP_nomem:
  1053.                 yell("Not enough memory to send email");
  1054.                 break;
  1055.             case NASMTP_tcpfail:
  1056.                 yell("Failed to connect to mail host");
  1057.                 break;
  1058.             case NASMTP_temperr:
  1059.             case NASMTP_permerr:
  1060.                 sprintf(msg, "Delivery failed: %s", errstr);
  1061.                 yell(msg);
  1062.                 break;
  1063.             default:
  1064.                 yell("Mail delivery failed.");
  1065.             case NASMTP_completed:
  1066.                 break;
  1067.         }
  1068.         mwin->sending = false;
  1069.         oserr = HDelete(mwin->fspec.vRefNum, mwin->fspec.parID, mwin->fspec.name);
  1070.     }
  1071.     if (oserr != noErr && oserr != fnfErr) {
  1072.         if (mwin->remaining) ++mwin->cpb.hFileInfo.ioFDirIndex;
  1073.         yell("Unable to remove temporary email file.");
  1074.     }
  1075.     NAunlockWindowh((na_win **) wh, win);
  1076. }
  1077.  
  1078. /* Get the email hostname
  1079.  */
  1080. static void mailhost(void *user, na_tcp s, short status, long size, char *data)
  1081. {
  1082.     struct mpack_preferences *mp;
  1083.     char *ihost;
  1084.     na_win *win, **winh;
  1085.     long len, oldsize;
  1086.     
  1087.     /* first make sure our window still exists */
  1088.     for (winh = NAhead; winh && winh != user; winh = (*winh)->next);
  1089.     if (!winh) return;
  1090.     win = NAlockWindow(winh);
  1091.     
  1092.     /* check for errors */
  1093.     if (status != NATCP_connect) {
  1094.         warn("Failed to get hostname from MacTCP");
  1095.     } else {
  1096.         mwin->gothost = true;
  1097.         if (data[size - 1] == '.') --size;
  1098.         
  1099.         /* update internet_host preference */
  1100.         len = strlen((*mpack_prefs)->internet_host);
  1101.         oldsize = GetHandleSize((Handle) mpack_prefs);
  1102.         if (len < size) {
  1103.             SetHandleSize((Handle) mpack_prefs, oldsize + (size - len));
  1104.             if (MemError() != noErr) return;
  1105.         }
  1106.         HLock((Handle) mpack_prefs);
  1107.         mp = *mpack_prefs;
  1108.         ihost = mp->internet_host;
  1109.         memmove(ihost + size + 1, ihost + len + 1,
  1110.             oldsize - len - 1 - ((char *) ihost - (char *) mp));
  1111.         memcpy(ihost, data, size);
  1112.         ihost[size] = '\0';
  1113.         HUnlock((Handle) mpack_prefs);
  1114.     }
  1115.     NAunlockWindowh(winh, win);
  1116. }
  1117.  
  1118. /* clean up mail task
  1119.  */
  1120. static short mailclose(na_win *win)
  1121. {
  1122.     if (mwin->dfile != NULL) fclose(mwin->dfile);
  1123.     if (mwin->envelope) DisposeHandle(mwin->envelope);
  1124.     if (mwin->headers) DisposeHandle(mwin->headers);
  1125.     NAmodalMenus(0);
  1126.     
  1127.     return (alldone(win));
  1128. }
  1129.  
  1130. /* send email
  1131.  */
  1132. static short mailtask(na_win *win)
  1133. {
  1134.     short vrefnum, encoding, refnum, result;
  1135.     long procid;
  1136.     FILE *tmpf, *fp, *resfork;
  1137.     OSErr err;
  1138.     CInfoPBRec cipbr;
  1139.     HFileInfo *fpb = (HFileInfo *)&cipbr;
  1140.     PCstr tstr[257], mtype[257], fname[257];
  1141.     extern long _ftype, _fcreator;
  1142.  
  1143.     switch (mwin->state) {
  1144.         case MS_MACTCP:
  1145.             if (tcpstart < 0) {
  1146.                 yell("Couldn't find MacTCP");
  1147.                 return (NA_REQCLOSE);
  1148.             }
  1149.             if (tcpstart == 0) break;
  1150.             ++mwin->state;
  1151.             NAsetIText(win->pwin, iWorkText, "\pGetting Hostname");
  1152.             mwin->gothost = false;
  1153.             NATCPgethost(mailhost, (void *) GetWRefCon(win->pwin));
  1154.             /* fall through */
  1155.         case MS_GETHOST:
  1156.             if (!mwin->gothost) break;
  1157.             ++mwin->state;
  1158.             /* fall through */
  1159.         case MS_ENCODE:
  1160.             NAsetIText(win->pwin, iWorkText, "\pEncoding file");
  1161.             
  1162.             /* get temp output filename for email */
  1163.             if (mwin->useemail) {
  1164.                 mwin->ofile.vRefNum = pfolder->fspec.vRefNum;
  1165.                 memcpy(mwin->ofile.name, "\pemail", 6);
  1166.                 if (prefsubdir("\poutgoing-email", &mwin->ofile.parID) != noErr) {
  1167.                     yell("Failed to write encoded file");
  1168.                     return (NA_REQCLOSE);
  1169.                 }
  1170.             }
  1171.             
  1172.             /* set file type */
  1173.             SetCursor(&watch);
  1174.             MapTypeCreator((char *) mtype, mwin->ftype);
  1175.             
  1176.             /* Determine the correct encoding */
  1177.             encoding = (*mpack_prefs)->encoding;
  1178.             fpb->ioVRefNum = mwin->fspec.vRefNum;
  1179.             fpb->ioNamePtr = mwin->fspec.name;
  1180.             fpb->ioDirID = mwin->fspec.parID;
  1181.             fpb->ioFDirIndex = 0;
  1182.             if (PBGetCatInfoSync(&cipbr) != noErr) {
  1183.                 SetCursor(&arrow);
  1184.                 yell("File disappeared before being encoded!");
  1185.                 return (NA_REQCLOSE);
  1186.             }
  1187.             if (encoding == EN_AUTO) {
  1188.                 encoding = EN_DOUBLE;
  1189.                 if (!fpb->ioFlRLgLen && *mtype != '\0') encoding = EN_DATA;
  1190.             }
  1191.             if (!fpb->ioFlLgLen) encoding = EN_SINGLE;
  1192.             
  1193.             /* do applesingle/appledouble encoding */
  1194.             tmpf = tmpfile();
  1195.             fp = Macopen(tmpf, mwin->fspec.name, mwin->fspec.vRefNum, mwin->fspec.parID,
  1196.                 strcmp(C(mtype), "text/plain") ? 1 : 0, 0, fsRdPerm);
  1197.             if (!fp) {
  1198.                 fclose(tmpf);
  1199.                 SetCursor(&arrow);
  1200.                 yell("Couldn't save encoded file");
  1201.                 return (NA_REQCLOSE);
  1202.             }
  1203.             if (encoding == EN_DATA) {
  1204.                 fclose(tmpf);
  1205.                 tmpf = NULL;
  1206.             } else {
  1207.                 /* open resource fork & output file for applesingle/double encoding */
  1208.                 resfork = Macopen(tmpf, mwin->fspec.name, mwin->fspec.vRefNum,
  1209.                     mwin->fspec.parID, 1, 1, 1);
  1210.                 if (encode_applefile(tmpf, fpb, resfork, encoding == EN_SINGLE ? fp : NULL) < 0) {
  1211.                     SetCursor(&arrow);
  1212.                     yell("Couldn't save encoded file");
  1213.                     return (NA_REQCLOSE);
  1214.                 }
  1215.                 rewind(tmpf);
  1216.                 if (encoding == EN_SINGLE) {
  1217.                     fp = tmpf;
  1218.                     tmpf = NULL;
  1219.                     strcpy(C(mtype), "application/applefile");
  1220.                     SetPlen(mtype);
  1221.                 }
  1222.             }
  1223.             
  1224.             /* generate output files */
  1225.             _fcreator = 'mPAK';
  1226.             _ftype = 'TEXT';
  1227.             GetVol(0, &vrefnum);
  1228.             err = OpenWD(mwin->ofile.vRefNum, mwin->ofile.parID, 0, &refnum);
  1229.             SetVol(0, err == noErr ? refnum : mwin->ofile.vRefNum);
  1230.             PtoPCstrcpy(tstr, (char *) mwin->ofile.name);
  1231.             PtoPCstrcpy(fname, (char *) mwin->fspec.name);
  1232.             result = encode(fp, tmpf, C(fname), mwin->dfile, C(mwin->subj), NULL,
  1233.                 mwin->partsize, PCstrlen(mtype) ? C(mtype) : NULL, C(tstr));
  1234.             if (err == noErr) CloseWD(refnum);
  1235.             SetVol(0, vrefnum);
  1236.             if (tmpf) fclose(tmpf);
  1237.             fclose(fp);
  1238.             if (mwin->dfile) {
  1239.                 fclose(mwin->dfile);
  1240.                 mwin->dfile = NULL;
  1241.             }
  1242.             SetCursor(&arrow);
  1243.             if (!mwin->useemail) return (NA_REQCLOSE);
  1244.             prwin->percent = 0;
  1245.             progressupdate(win, false);
  1246.             ++mwin->state;
  1247.             
  1248.             /* count files */
  1249.             mwin->cpb.dirInfo.ioVRefNum = mwin->ofile.vRefNum;
  1250.             mwin->cpb.dirInfo.ioDrDirID = mwin->dirID = mwin->ofile.parID;
  1251.             mwin->cpb.dirInfo.ioFDirIndex = -1;
  1252.             if (PBGetCatInfoSync(&mwin->cpb) != noErr) {
  1253.                 return (NA_CLOSED);
  1254.             }
  1255.             mwin->remaining = mwin->cpb.dirInfo.ioDrNmFls;
  1256.             mwin->cpb.dirInfo.ioFDirIndex = 1;
  1257.             /* fall through */
  1258.         case MS_SENDING:
  1259.             if (mwin->sending) break;
  1260.             if (!mwin->remaining) return (NA_REQCLOSE);
  1261.             sprintf(C(tstr), "Email parts remaining to submit: %d", mwin->remaining--);
  1262.             SetPlen(tstr);
  1263.             NAsetIText(win->pwin, iWorkText, tstr);
  1264.             prwin->percent = 0;
  1265.             progressupdate(win, false);
  1266.             mwin->cpb.hFileInfo.ioDirID = mwin->dirID;
  1267.             mwin->cpb.hFileInfo.ioNamePtr = (StringPtr) &mwin->fspec.name;
  1268.             if (PBGetCatInfoSync(&mwin->cpb) != noErr) {
  1269.                 yell("Email disappeared before submission!");
  1270.                 return (NA_REQCLOSE);
  1271.             }
  1272.             mwin->sending = true;
  1273.             mwin->fspec.vRefNum = mwin->cpb.hFileInfo.ioVRefNum;
  1274.             mwin->fspec.parID = mwin->dirID;
  1275.             NASMTPsubmit(smtpstat, C(mwin->server), &mwin->fspec,
  1276.                 mwin->headers, mwin->envelope,
  1277.                 NASMTP_crtrans, (void *) GetWRefCon(win->pwin));
  1278.             break;
  1279.     }
  1280.     
  1281.     return (NA_NOTPROCESSED);
  1282. }
  1283.  
  1284. /* Following routine stolen from Mark Crispin's c-client library:
  1285.  *
  1286.  * Write current time in RFC 822 format
  1287.  * Accepts: destination string
  1288.  *
  1289.  * This depends upon the ReadLocation() call in System 7 and the
  1290.  * user properly setting his location/timezone in the Map control
  1291.  * panel.
  1292.  * 2/95 - I added support for dlsDelta & compatibility checking
  1293.  */
  1294. void rfc822_date(char *string)
  1295. {
  1296.     long tz, tzm;
  1297.     time_t ti = time (0);
  1298.     struct tm *t = localtime (&ti);
  1299.     MachineLocation loc;
  1300.     
  1301.     /* output date */
  1302.     strcpy(string, "Date: ");
  1303.     string += 6;
  1304.     strftime (string,1024,"%a, %d %b %Y %H:%M:%S ",t);
  1305.     /* now output time zone, if we can get it */
  1306.     tz = 0;
  1307.     if (Gestalt(gestaltScriptMgrVersion, &tz) == noErr && tz >= 200) {
  1308.         ReadLocation(&loc);        /* get location/timezone */
  1309.         /* get sign-extended time zone */
  1310.         tz = (loc.gmtFlags.gmtDelta & 0x00ffffff) |
  1311.             ((loc.gmtFlags.gmtDelta & 0x00800000) ? 0xff000000 : 0);
  1312.         tz /= 60;            /* get timezone in minutes */
  1313.         tzm = tz % 60;        /* get minutes from the hour */
  1314.         sprintf (string += strlen(string),"%+03ld%02ld",
  1315.             tz/60,tzm >= 0 ? tzm : -tzm);
  1316.         if (!tzm && tz <= -240 && tz >= -660) {
  1317.             string += strlen(string);
  1318.             if (loc.gmtFlags.dlsDelta & 0x80) {
  1319.                 sprintf(string, " (%cDT)", "AECMPYHB"[- (tz / 60) - 3]);
  1320.             } else {
  1321.                 sprintf(string, " (%cST)", "AECMPYHB"[- (tz / 60) - 4]);
  1322.             }
  1323.         }
  1324.     } else {
  1325.         sprintf(string + strlen(string), "+0000 (Local Time Zone Unknown)");
  1326.     }
  1327. }
  1328.  
  1329. /* init mail sending
  1330.  */
  1331. static short mailinit(na_win *win, long *data)
  1332. {
  1333.     encodewin *ew = (encodewin *) data;
  1334.     WindowPtr pwin = ew->w.winp.pwin;
  1335.     ControlHandle ctrlh;
  1336.     PCstr tstr[257], email[257];
  1337.     
  1338.     /* copy values from encode window */
  1339.     NAgetIText(pwin, iSubj, mwin->subj);
  1340.     NAgetIText(pwin, iEmailto, email);
  1341.     mwin->partsize = ew->partsize;
  1342.     mwin->useemail = ew->useemail;
  1343.     mwin->fspec = ew->fspec;
  1344.     mwin->ftype = ew->ftype;
  1345.     mwin->ofile = ew->ofile;
  1346.  
  1347.     /* copy desc file */
  1348.     mwin->dfile = NULL;
  1349.     if ((*ew->w.hTE)->teLength && (mwin->dfile = tmpfile()) != NULL) {
  1350.         copydesc(mwin->dfile, ew->w.hTE);
  1351.     }
  1352.     
  1353.     /* set procedures */
  1354.     win->taskp = mailtask;
  1355.     win->updatep = progressupdate;
  1356.     win->ctrlp = progressctrl;
  1357.     win->closep = mailclose;
  1358.     
  1359.     /* Customize Progress window, set up envelope & headers for email */
  1360.     prwin->percent = -1;
  1361.     NAgetDHandle(win->pwin, iCancel, &ctrlh);
  1362.     SetCTitle(ctrlh, "\pStop");
  1363.     NAmodalMenus(1);
  1364.     if (!mwin->useemail) {
  1365.         mwin->state = MS_ENCODE;
  1366.     } else {
  1367.         if (!tcpstart) NATCPinit(mytcpinit);
  1368.         NAsetIText(win->pwin, iWorkText, "\pLooking for MacTCP");
  1369.         mwin->state = MS_MACTCP;
  1370.  
  1371.         /* create envelope, get server */
  1372.         getICprefs(NULL, tstr, mwin->server);
  1373.         if (PtrToHand(C(tstr), &mwin->envelope, PCstrlen(tstr) + 1) != noErr
  1374.             || PtrAndHand(C(email), mwin->envelope, PCstrlen(email) + 1) != noErr) {
  1375.             if (mwin->envelope) DisposeHandle(mwin->envelope);
  1376.             return (NA_CLOSED);
  1377.         }
  1378.     
  1379.         /* create headers */
  1380.         if ((mwin->headers = NewHandle(1024)) == NULL) {
  1381.             DisposeHandle(mwin->envelope);
  1382.             return (NA_CLOSED);
  1383.         }
  1384.         HLock(mwin->headers);
  1385.         rfc822_date((char *) *mwin->headers);
  1386.         sprintf((char *) (*mwin->headers) + strlen((char *) (*mwin->headers)),
  1387.             "\r\nFrom: %s\r\nTo: %s\r\n", C(tstr), C(email));
  1388.         HUnlock(mwin->headers);
  1389.         SetHandleSize(mwin->headers, strlen((char *) (*mwin->headers)));
  1390.     }
  1391.  
  1392.     return (NA_NOTPROCESSED);
  1393. }
  1394.  
  1395. /* update the encode window
  1396.  */
  1397. static short encodeupdate(na_win *win, Boolean newsize)
  1398. {
  1399.     Rect btmp;
  1400.     ControlHandle ctrlh;
  1401.     
  1402.     /* draw double-line */
  1403.     NAgetDRect(win->pwin, iBar, &btmp);
  1404.     FrameRect(&btmp);
  1405.     
  1406.     /* draw disabled edittext boxes */
  1407.     NAgetDHandle(win->pwin, iLimit, &ctrlh);
  1408.     if (!GetCtlValue(ctrlh)) {
  1409.         NAhiliteDItem(win->pwin, iPartLimit, 255);
  1410.     }
  1411.     if (NAradioGet(win->pwin, iEmail, iSavefile) == iSavefile) {
  1412.         NAhiliteDItem(win->pwin, iEmailto, 255);
  1413.     }
  1414.     
  1415.     return (NATEupdatep(win, newsize));
  1416. }
  1417.  
  1418. /* select desc text
  1419.  */
  1420. static short seldesctext(na_win *win)
  1421. {
  1422.     win->activep = NATEactivep;
  1423.     win->idlep = NATEidlep;
  1424.     NATEactivep(win, true);
  1425.     ewin->nateon = true;
  1426.     SelIText(win->pwin, iDescEdit, 0, 0);
  1427.     TESetSelect(32767, 32767, twin->hTE);
  1428. }
  1429.  
  1430. /* encode control proc
  1431.  */
  1432. static short encodectrl(na_win *win, Point p, short item,
  1433.     short mods, ControlHandle ctrlh)
  1434. {
  1435.     short value;
  1436.     DialogPeek dpeek = (DialogPeek) win->pwin;
  1437.     char *scan;
  1438.     Boolean good;
  1439.     StandardFileReply reply;
  1440.     PCstr tstr[257];
  1441.     
  1442.     if (ctrlh == twin->vctrl) {
  1443.         return (NATEctrlp(win, p, item, mods, ctrlh));
  1444.     }
  1445.     switch (item) {
  1446.         case iOk:
  1447.             /* get part size */
  1448.             ewin->partsize = 0;
  1449.             NAgetDHandle(win->pwin, iLimit, &ctrlh);
  1450.             if (GetCtlValue(ctrlh)) {
  1451.                 NAgetIText(win->pwin, iPartLimit, tstr);
  1452.                 ewin->partsize = atol(C(tstr)) * 1000;
  1453.             }
  1454.             NAgetIText(win->pwin, iEmailto, tstr);
  1455.             ewin->useemail = NAradioGet(win->pwin, iEmail, iSavefile) == iEmail;
  1456.             if (ewin->useemail) {
  1457.                 /* verify email address */
  1458.                 if (!strchr(C(tstr), '@')) {
  1459.                     yell("Invalid Email address, please re-enter");
  1460.                     SelIText(win->pwin, iEmailto, 0, 32767);
  1461.                     break;
  1462.                 }
  1463.             } else {
  1464.                 /* get output filename */
  1465.                 PtoPCstrcpy(tstr, (char *) ewin->fspec.name);
  1466.                 if (PCstrlen(tstr) > 23) {
  1467.                     PCstrlen(tstr) = 23;
  1468.                     SetClen(tstr);
  1469.                 }
  1470.                 strcat(C(tstr), ".mime");
  1471.                 SetPlen(tstr);
  1472.                 do {
  1473.                     NAputFile(ewin->partsize ? "\pPart prefix" : "\pEmail file:",
  1474.                         tstr, &reply);
  1475.                     good = true;
  1476.                     if (reply.sfGood
  1477.                         && EqualString(reply.sfFile.name,
  1478.                         ewin->fspec.name, true, false)) {
  1479.                         good = false;
  1480.                         yell("The output filename must be different from the input filename");
  1481.                     }
  1482.                 } while (!good);
  1483.                 if (!reply.sfGood) break;
  1484.                 ewin->ofile = reply.sfFile;
  1485.             }
  1486.             if (NAwindow(0, NA_DIALOGWINDOW | NA_TITLEBAR | NA_HASTASK
  1487.                 | NA_USERESOURCE | NA_MODAL, NULL, progDLOG,
  1488.                 (long *) win, sizeof (mailwin), mailinit) == NA_CLOSED) {
  1489.                 warn("Not enough memory to proceed");
  1490.                 break;
  1491.             }
  1492.         case iCancel:
  1493.             return (NA_REQCLOSE);
  1494.         case iEmail:
  1495.         case iSavefile:
  1496.             NAradioSet(win->pwin, iEmail, iSavefile, item);
  1497.             NAenableDItem(win->pwin, iEmailto, item == iEmail ? 1 : 0);
  1498.             NAhiliteDItem(win->pwin, iEmailto, item == iEmail ? 0 : 255);
  1499.             if (item == iEmail || dpeek->editField == iEmailto - 1) {
  1500.                 SelIText(win->pwin, item == iEmail ? iEmailto : iSubj, 0, 32767);
  1501.             }
  1502.             break;
  1503.         case iLimit:
  1504.             SetCtlValue(ctrlh, value = !GetCtlValue(ctrlh));
  1505.             NAenableDItem(win->pwin, iPartLimit, value ? 1 : 0);
  1506.             NAhiliteDItem(win->pwin, iPartLimit, value ? 0 : 255);
  1507.             if (value || dpeek->editField == iPartLimit - 1) {
  1508.                 SelIText(win->pwin, value ? iPartLimit : iSubj, 0, 32767);
  1509.             }
  1510.             break;
  1511.         case iDescEdit:
  1512.         case iSubj:
  1513.         case iEmailto:
  1514.         case iPartLimit:
  1515.             if (!ewin->nateon && dpeek->editField == iDescEdit - 1) {
  1516.                 seldesctext(win);
  1517.             }
  1518.             break;
  1519.     }
  1520.     if (ewin->nateon && dpeek->editField != iDescEdit - 1) {
  1521.         win->activep = NULL;
  1522.         win->idlep = NULL;
  1523.         NATEactivep(win, false);
  1524.         ewin->nateon = false;
  1525.     }
  1526.     
  1527.     return (NA_NOTPROCESSED);
  1528. }
  1529.  
  1530. /* encode key proc
  1531.  */
  1532. static short encodekey(na_win *win, long c, short mods)
  1533. {
  1534.     if (!(mods & cmdKey)) {
  1535.         if (ewin->nateon && c != '\t' && c != '\n' && c != '\3' && c != '\033') {
  1536.             return (NATEkeyp(win, c, mods));
  1537.         }
  1538.     }
  1539.     
  1540.     return (NA_NOTPROCESSED);
  1541. }
  1542.  
  1543. /* menu proc for encode window
  1544.  */
  1545. static short encodemenu(na_win *win, WORD menu, WORD item)
  1546. {
  1547.     StandardFileReply descfile;
  1548.     MenuHandle mf = NAmenuh(mFile);
  1549.     short result = NA_NOTPROCESSED;
  1550.     short refnum;
  1551.     long size;
  1552.     Ptr text;
  1553.     Boolean success;
  1554.     
  1555.     switch (menu) {
  1556.         case 0:
  1557.             EnableItem(mf, iInsert);
  1558.             /* fall through */
  1559.         case mEdit:
  1560.             result = ewin->nateon ? NATEmenup(win, menu, item)
  1561.                 : NAdialogMenu(win, menu, item);
  1562.             break;
  1563.         case mFile:
  1564.             if (item != iInsert) break;
  1565.             result = NA_PROCESSED;
  1566.             NAgetFile(NULL, 1, textList, &descfile);
  1567.             if (!descfile.sfGood) break;
  1568.             if (HOpen(descfile.sfFile.vRefNum, descfile.sfFile.parID,
  1569.                 descfile.sfFile.name, fsRdPerm, &refnum) != noErr) {
  1570.                 warn("Failed to open file");
  1571.                 break;
  1572.             }
  1573.             text = NULL;
  1574.             success = GetEOF(refnum, &size) == noErr && (text = NewPtr(size)) != NULL
  1575.                 && FSRead(refnum, &size, text) == noErr;
  1576.             if (success) {
  1577.                 TEInsert(text, size, twin->hTE);
  1578.                 TESelView(twin->hTE);
  1579.                 NATEsetscroll(win, false, (Rect*) NULL, (Rect*) NULL);
  1580.             } else {
  1581.                 warn("Failed to read file");
  1582.             }
  1583.             if (text) DisposPtr(text);
  1584.             FSClose(refnum);
  1585.             break;
  1586.     }
  1587.     if (menu != 0) DisableItem(mf, iInsert);
  1588.     
  1589.     return (result);
  1590. }
  1591.  
  1592. /* mouse proc for encode window
  1593.  */
  1594. static short encodemouse(na_win *win, Point p, short type, short mods)
  1595. {
  1596.     if (p.v >= twin->topoff && !ewin->nateon) seldesctext(win);
  1597.     
  1598.     return (NATEmousep(win, p, type, mods));
  1599. }
  1600.  
  1601. /* close the encode window
  1602.  */
  1603. static short encodeclose(na_win *win)
  1604. {
  1605.     NATEclosep(win);
  1606.     
  1607.     return (NA_CLOSED);
  1608. }
  1609.  
  1610. /* init the encode window
  1611.  */
  1612. static short encodeinit(na_win *win, long *data)
  1613. {
  1614.     StandardFileReply *sf = (StandardFileReply *) data;
  1615.     Rect rtmp, btmp;
  1616.     FontInfo finfo;
  1617.     
  1618.     /* copy data */
  1619.     ewin->fspec = sf->sfFile;
  1620.     ewin->ftype = sf->sfType;
  1621.     
  1622.     /* set sizing limits */
  1623.     NAgetDRect(win->pwin, iBar, &btmp);
  1624.     rtmp = win->pwin->portRect;
  1625.     win->minw = win->maxw = rtmp.right - rtmp.left;
  1626.     win->minh = btmp.bottom + 64;
  1627.     twin->topoff = btmp.bottom;
  1628.     
  1629.     /* init text area */
  1630.     TextFont(monaco);
  1631.     TextSize(9);
  1632.     GetFontInfo(&finfo);
  1633.     NATEinit(win, NATE_NOHSCROLL, 80 * finfo.widMax + 2, NULL, 0);
  1634.     ewin->nateon = 0;
  1635.     TextFont(0);
  1636.     TextSize(0);
  1637.     
  1638.     /* set control values */
  1639.     NAradioSet(win->pwin, iEmail, iSavefile, iSavefile);
  1640.     if (tcpstart < 0) NAhiliteDItem(win->pwin, iEmail, 255);
  1641.     NAenableDItem(win->pwin, iEmailto, 0);
  1642.     NAenableDItem(win->pwin, iPartLimit, 0);
  1643.     NAsetIText(win->pwin, iSubj, ewin->fspec.name);
  1644.     SelIText(win->pwin, iSubj, 0, 32767);
  1645.     SetWTitle(win->pwin, ewin->fspec.name);
  1646.     ShowWindow(win->pwin);
  1647.     
  1648.     /* set window procedures */
  1649.     win->updatep = encodeupdate;
  1650.     win->closep = encodeclose;
  1651.     win->keyp = encodekey;
  1652.     win->ctrlp = encodectrl;
  1653.     win->mousep = encodemouse;
  1654.     win->menup = encodemenu;
  1655.     win->idlep = NULL;
  1656.     win->activep = NULL;
  1657.     
  1658.     return (NA_NOTPROCESSED);
  1659. }
  1660.  
  1661. /* Encode procedure: first get a file, then open encode save window
  1662.  */
  1663. static void do_encode(FSSpec *fspec, OSType ftype)
  1664. {
  1665.     StandardFileReply infile;
  1666.  
  1667.     if (!fspec) {
  1668.         NAgetFile(NULL, -1, NULL, &infile);
  1669.         if (!infile.sfGood) return;
  1670.     } else {
  1671.         infile.sfFile = *fspec;
  1672.         infile.sfType = ftype;
  1673.     }
  1674.     NAwindow(NULL, NA_DIALOGWINDOW | NA_TITLEBAR | NA_GROWBOX | NA_USERESOURCE
  1675.         | NA_DEFBUTTON | NA_HASCONTROLS,
  1676.         NULL, sendDLOG, (long *) &infile, sizeof (encodewin), encodeinit);
  1677. }
  1678.  
  1679. /* Open a file via drag&drop
  1680.  */
  1681. static short openfile(short message, FSSpec *fspec, FInfo *finfo)
  1682. {    
  1683.     if (message != appOpen) return (-1);
  1684.     
  1685.     /* open file */
  1686.     if (finfo->fdType == 'TEXT') {
  1687.         do_decode(fspec);
  1688.     } else {
  1689.         do_encode(fspec, finfo->fdType);
  1690.     }
  1691.     
  1692.     return (0);
  1693. }
  1694.  
  1695. #define hwinfo ((nate_win *)win)
  1696.  
  1697. /* help close procedure
  1698.  */
  1699. static short helpclose(na_win *win)
  1700. {
  1701.     helpw = NULL;
  1702.  
  1703.     return (NATEclosep(win));
  1704. }
  1705.  
  1706. /* help window procedure
  1707.  */
  1708. static short helpwindow(na_win *win, long *data)
  1709. {
  1710.     Rect        rtemp, vtemp;
  1711.     Handle        h, hs;
  1712.     long        len;
  1713.     TEHandle    hTE;
  1714.     
  1715.     rtemp = win->pwin->portRect;
  1716.     vtemp = rtemp;
  1717.     vtemp.right = vtemp.left + (hwinfo->docwidth = 475);
  1718.     win->mousep = NATEmousep;
  1719.     win->idlep = NATEidlep;
  1720.     win->menup = NATEmenup;
  1721.     win->activep = NATEactivep;
  1722.     win->updatep = NATEupdatep;
  1723.     win->ctrlp = NATEctrlp;
  1724.     win->closep = helpclose;
  1725.     win->cursorRgn = NewRgn();
  1726.     hwinfo->vctrl = hwinfo->hctrl = NULL;
  1727.     
  1728.     TEAutoView(true, hTE = hwinfo->hTE = TEStylNew(&vtemp, &rtemp));
  1729.     h = GetResource('TEXT', helpTEXT);
  1730.     hs = GetResource('styl', helpSTYL);
  1731.     len = GetHandleSize(h);
  1732.     HLock(h);
  1733.     TEStylInsert(*h, len, (StScrpHandle) hs, hTE);
  1734.     HUnlock(h);
  1735.     TESetSelect(0, 0, hTE);
  1736.     hwinfo->lheight = TEGetHeight((*hTE)->nLines, 0, hTE) / (*hTE)->nLines;
  1737.     ShowWindow(helpw = win->pwin);
  1738.     
  1739.     return (NA_NOTPROCESSED);
  1740. }
  1741.  
  1742. /* Set the hostname: TCP callback
  1743.  */
  1744. static void sethost(void *user, na_tcp s, short status, long size, char *data)
  1745. {
  1746.     PCstr host[65];
  1747.     Rect box;
  1748.     na_win *win, **winh;
  1749.     
  1750.     /* first make sure our window still exists */
  1751.     for (winh = NAhead; winh && (*winh)->type != PREFWIN; winh = (*winh)->next);
  1752.     if (!winh || (*winh)->child != user) return;
  1753.     win = NAlockWindow((na_win **) user);
  1754.     
  1755.     /* check for errors */
  1756.     if (status != NATCP_connect) {
  1757.         warn("Failed to get hostname from MacTCP");
  1758.     } else {
  1759.         if (data[size - 1] == '.') --size;
  1760.         PCstrlen(host) = size;
  1761.         memcpy(C(host), data, size);
  1762.         NAsetIText((*winh)->pwin, iHost, host);
  1763.         SelIText((*winh)->pwin, iHost, 0, 32767);
  1764.     }
  1765.     prwin->percent = 100;
  1766.     progressupdate(win, false);
  1767.     NAunlockWindowh(winh, win);
  1768. }
  1769.  
  1770. /* if TCP is active, get hostname
  1771.  */
  1772. static short settask(na_win *win)
  1773. {
  1774.     if (tcpstart == 0 && !prwin->percent) {
  1775.         NAsetIText(win->pwin, iWorkText, "\pLooking for MacTCP");
  1776.         prwin->percent = 1;
  1777.         progressupdate(win, false);
  1778.         NATCPinit(mytcpinit);
  1779.     } else if (tcpstart == 1 && prwin->percent < 50) {
  1780.         NAsetIText(win->pwin, iWorkText, "\pLooking up Internet hostname");
  1781.         prwin->percent = 50;
  1782.         progressupdate(win, false);
  1783.         NATCPgethost(sethost, (void *) GetWRefCon(win->pwin));
  1784.     }
  1785.     progressupdate(win, false);
  1786.     if (tcpstart == -1) {
  1787.         warn("MacTCP not available");
  1788.         NAhiliteDItem((*win->parent)->pwin, iSet, 255);
  1789.     }
  1790.     
  1791.     return (tcpstart == -1 || prwin->percent == 100 ? NA_CLOSED : NA_NOTPROCESSED);
  1792. }
  1793.  
  1794. /* set the Internet host via MacTCP
  1795.  */
  1796. static short setinit(na_win *win, long *data)
  1797. {
  1798.     win->taskp = settask;
  1799.     win->updatep = progressupdate;
  1800.     win->closep = progressclose;
  1801.     NAmodalMenus(1);
  1802.     
  1803.     return (NA_NOTPROCESSED);
  1804. }
  1805.  
  1806. /* preference control procedure
  1807.  */
  1808. static short prefsctrl(na_win *win, Point p, short item, short mods, ControlHandle ctrlh)
  1809. {
  1810.     PCstr tmpstr[257];
  1811.     short encoding, extract_text, quit_finished, result = NA_NOTPROCESSED;
  1812.     ControlHandle ctrl;
  1813.     char *scan, *end;
  1814.     short changed, len, i, useic;
  1815.     static short prefitem[3] = { iHost, iEmailAddr, iMailServer };
  1816.     
  1817.     switch (item) {
  1818.         case iOk:
  1819.             HLock((Handle) mpack_prefs);
  1820.             changed = 0;
  1821.             encoding = NAradioGet(win->pwin, iAuto, iDouble) - iAuto;
  1822.             NAgetDHandle(win->pwin, iTextEncode, &ctrl);
  1823.             extract_text = GetCtlValue(ctrl);
  1824.             NAgetDHandle(win->pwin, iQuitFinish, &ctrl);
  1825.             quit_finished = GetCtlValue(ctrl);
  1826.             if (encoding != (*mpack_prefs)->encoding
  1827.                 || extract_text != (*mpack_prefs)->extract_text
  1828.                 || quit_finished != (*mpack_prefs)->quit_finished) {
  1829.                 changed = 1;
  1830.             }
  1831.             if (changed) {
  1832.                 (*mpack_prefs)->encoding = encoding;
  1833.                 (*mpack_prefs)->extract_text = extract_text;
  1834.                 (*mpack_prefs)->quit_finished = quit_finished;
  1835.                 ChangedResource((Handle) mpack_prefs);
  1836.                 changed = 0;
  1837.             }
  1838.             len = 1;
  1839.             scan = (*mpack_prefs)->internet_host;
  1840.             end = (char *) *mpack_prefs + GetHandleSize((Handle) mpack_prefs);
  1841.             for (i = 0; i < 3; ++i) {
  1842.                 NAgetIText(win->pwin, prefitem[i], P(tmpstr));
  1843.                 SetClen(tmpstr);
  1844.                 len += PCstrlen(tmpstr);
  1845.                 if (scan == end || strcmp(C(tmpstr), scan)) {
  1846.                     changed = 1;
  1847.                 }
  1848.                 while (scan < end && *scan++);
  1849.             }
  1850.             if (changed) {
  1851.                 HUnlock((Handle) mpack_prefs);
  1852.                 /* update the preferences resource */
  1853.                 SetHandleSize((Handle) mpack_prefs, sizeof (struct mpack_preferences)
  1854.                     + len);
  1855.                 HLock((Handle) mpack_prefs);
  1856.                 scan = (*mpack_prefs)->internet_host;
  1857.                 useic = icinst && ICBegin(icinst, icReadWritePerm) == noErr;
  1858.                 for (i = 0; i < 3; ++i) {
  1859.                     NAgetIText(win->pwin, prefitem[i], P(tmpstr));
  1860.                     SetClen(tmpstr);
  1861.                     strcpy(scan, C(tmpstr));
  1862.                     scan += PCstrlen(tmpstr) + 1;
  1863.                     if (i && useic) {
  1864.                         ICSetPref(icinst, i == 1 ? kICEmail : kICSMTPHost,
  1865.                             ICattr_no_change, (Ptr) P(tmpstr), PCstrlen(tmpstr) + 1);
  1866.                     }
  1867.                 }
  1868.                 if (useic) ICEnd(icinst);
  1869.                 ChangedResource((Handle) mpack_prefs);
  1870.             }
  1871.             HUnlock((Handle) mpack_prefs);
  1872.         case iCancel:
  1873.             result = NA_REQCLOSE;
  1874.             NAmodalMenus(0);
  1875.             break;
  1876.         case iAuto:
  1877.         case iData:
  1878.         case iSingle:
  1879.         case iDouble:
  1880.             NAradioSet(win->pwin, iAuto, iDouble, item);
  1881.             break;
  1882.         case iTextEncode:
  1883.         case iQuitFinish:
  1884.             SetCtlValue(ctrlh, !GetCtlValue(ctrlh));
  1885.             break;
  1886.         case iSet:
  1887.             NAwindow(0, NA_DIALOGWINDOW | NA_TITLEBAR | NA_HASTASK | NA_USERESOURCE
  1888.                 | NA_MODAL | NA_CHILDWINDOW,
  1889.                 NULL, progDLOG, NULL, sizeof (progresswin), setinit);
  1890.             break;
  1891.     }
  1892.     
  1893.     return (result);
  1894. }
  1895.  
  1896. /* update preferences dialog
  1897.  */
  1898. static short prefsupdate(na_win *win, Boolean newsize)
  1899. {
  1900.     Handle hn;
  1901.     Rect box;
  1902.     short type;
  1903.     
  1904.     /* draw disabled items */
  1905.     GetDItem(win->pwin, iEmailAddr, &type, &hn, &box);
  1906.     if (type == statText) NAhiliteDItem(win->pwin, iEmailAddr, 255);
  1907.     GetDItem(win->pwin, iMailServer, &type, &hn, &box);
  1908.     if (type == statText) NAhiliteDItem(win->pwin, iMailServer, 255);
  1909.     
  1910.     return (NA_NOTPROCESSED);
  1911. }
  1912.  
  1913. /* initialize preferences dialog
  1914.  */
  1915. static short prefsinit(na_win *win, long *data)
  1916. {
  1917.     PCstr tmpstr[257], eaddr[257];
  1918.     ControlHandle ctrl;
  1919.     
  1920.     win->type = PREFWIN;
  1921.     win->ctrlp = prefsctrl;
  1922.     win->menup = NAdialogMenu;
  1923.     win->updatep = prefsupdate;
  1924.     HLock((Handle) mpack_prefs);
  1925.     strcpy(C(tmpstr), (*mpack_prefs)->internet_host);
  1926.     HUnlock((Handle) mpack_prefs);
  1927.     SetPlen(tmpstr);
  1928.     NAsetIText(win->pwin, iHost, P(tmpstr));
  1929.     SelIText(win->pwin, iHost, 0, 32767);
  1930.     getICprefs(win, eaddr, tmpstr);
  1931.     NAsetIText(win->pwin, iEmailAddr, P(eaddr));
  1932.     NAsetIText(win->pwin, iMailServer, P(tmpstr));
  1933.     NAradioSet(win->pwin, iAuto, iDouble, (*mpack_prefs)->encoding + iAuto);
  1934.     NAsetIval(win->pwin, iTextEncode, (*mpack_prefs)->extract_text);
  1935.     NAsetIval(win->pwin, iQuitFinish, (*mpack_prefs)->quit_finished);
  1936.     if (tcpstart == -1) NAhiliteDItem(win->pwin, iSet, 255);
  1937.     NAmodalMenus(1);
  1938.     ShowWindow(win->pwin);
  1939.     
  1940.     return (NA_NOTPROCESSED);
  1941. }
  1942.  
  1943. /* Main menu procedure
  1944.  */
  1945. static short mainmenu(na_win *win, WORD menuid, WORD itemno)
  1946. {
  1947.     short status = NA_NOTPROCESSED;
  1948.     MenuHandle mh;
  1949.     PCstr version[32];
  1950.  
  1951.     switch (menuid) {
  1952.         case 0:
  1953.             NAenableMItem(mApple, iAbout);
  1954.             return (status);
  1955.         case mApple:
  1956.             if (itemno == iAbout) {
  1957.                 CtoPCstrcpy(version, MPACK_VERSION);
  1958.                 ParamText(P(version), NULL, NULL, NULL);
  1959.                 return (NA_NOTPROCESSED);
  1960.             }
  1961.             break;
  1962.             
  1963.         case mFile:
  1964.             switch (itemno) {
  1965.                 case iEncode:
  1966.                     do_encode(NULL, 0);
  1967.                     status = NA_PROCESSED;
  1968.                     break;
  1969.                     
  1970.                 case iDecode:
  1971.                     do_decode(NULL);
  1972.                     status = NA_PROCESSED;
  1973.                     break;
  1974.  
  1975.                 case iClose:
  1976.                     break;
  1977.                     
  1978.                 case iPrefs:
  1979.                     status = NAwindow(0, NA_DIALOGWINDOW | NA_USERESOURCE
  1980.                         | NA_MODAL | NA_DEFBUTTON | NA_TITLEBAR,
  1981.                         NULL, prefsDLOG, (long *) NULL, 0, prefsinit);
  1982.                     break;
  1983.  
  1984.                 case iQuit:
  1985.                     status = NA_REQCLOSEALL;
  1986.                     break;
  1987.             }
  1988.             break;
  1989.  
  1990.         case mEdit:
  1991.             break;
  1992.         
  1993.         case mHelp:
  1994.             if (!helpw) {
  1995.                 NAwindow(0, NA_USERESOURCE | NATEflags | NATE_READONLY | NA_SMARTSIZE,
  1996.                     NULL, helpWIND, (long *) NULL, sizeof (nate_win), helpwindow);
  1997.             } else {
  1998.                 SelectWindow(helpw);
  1999.             }
  2000.             break;
  2001.     }
  2002.     NAdisableMItem(mApple, iAbout);
  2003.     
  2004.     return (status);
  2005. }
  2006.  
  2007. /* make preferences folder/file
  2008.  *  returns -1 on failure.
  2009.  */
  2010. static short makepref()
  2011. {
  2012.     Handle hpref = NULL, htmpl;
  2013.     long dirID;
  2014.     short vRefNum;
  2015.     char *scan, *end;
  2016.     PCstr dname[257];
  2017.     CInfoPBRec cpb;
  2018.     DirInfo *dp = &cpb.dirInfo;
  2019.     ParamBlockRec pb;
  2020.     VolumeParam *vp = &pb.volumeParam;
  2021.     FInfo finfo;
  2022.     static unsigned char pname[] = "\pprefs";
  2023.     
  2024.     /* set up pref folder storage */
  2025.     pfolder = (struct pref_folder *) NewPtr(sizeof (struct pref_folder));
  2026.     if (!pfolder) return (-1);
  2027.     end = scan = (char *) pfolder->prefs + sizeof (pfolder->prefs) - 1;
  2028.     *scan = '\0';
  2029.     
  2030.     /* get pref folder */
  2031.     if (FindFolder(kOnSystemDisk, kPreferencesFolderType,
  2032.             kCreateFolder, &vRefNum, &pfolder->fspec.parID) != noErr) {
  2033.         return (-1);
  2034.     }
  2035.     
  2036.     /* create subfolder, if needed */
  2037.     PtoPCstrcpy(dname, (char *) "\pMpack");
  2038.     (void) DirCreate(vRefNum, pfolder->fspec.parID, P(dname), &dirID);
  2039.     
  2040.     /* get mpack prefs folder info */
  2041.     dp->ioNamePtr = P(dname);
  2042.     dp->ioVRefNum = vRefNum;
  2043.     dp->ioFDirIndex = 0;
  2044.     dp->ioDrDirID = pfolder->fspec.parID;
  2045.     if (PBGetCatInfoSync(&cpb) != noErr) return (-1);
  2046.     pfolder->fspec.parID = dirID = dp->ioDrDirID;
  2047.     pfolder->fspec.vRefNum = vRefNum;
  2048.     
  2049.     /* generate pathname */
  2050.     dp->ioFDirIndex = -1;
  2051.     for (;;) {
  2052.         *--scan = ':';
  2053.         if (scan - (char *) pfolder->prefs < 1 + PCstrlen(dname)) return (-1);
  2054.         scan -= PCstrlen(dname);
  2055.         memcpy(scan, C(dname), PCstrlen(dname));
  2056.         if ((dp->ioDrDirID = dp->ioDrParID) == 2) break;
  2057.         if (PBGetCatInfoSync(&cpb) != noErr) return (-1);
  2058.     }
  2059.     vp->ioVolIndex = 0;
  2060.     vp->ioNamePtr = P(dname);
  2061.     vp->ioVRefNum = vRefNum;
  2062.     if (PBGetVInfoSync(&pb) != noErr) return (-1);
  2063.     *--scan = ':';
  2064.     if (scan - (char *) pfolder->prefs < 16 + PCstrlen(dname)) return (-1);
  2065.     PtoPCstrcpy(pfolder->prefs, (char *) P(dname));
  2066.     CtoPCstrcat(pfolder->prefs, scan);
  2067.     
  2068.     /* Get/Create preferences file */
  2069.     HCreateResFile(vRefNum, dirID, pname);
  2070.     if (ResError() == noErr) {
  2071.         HGetFInfo(vRefNum, dirID, pname, &finfo);
  2072.         finfo.fdType = 'pref';
  2073.         finfo.fdCreator = 'mPAK';
  2074.         HSetFInfo(vRefNum, dirID, pname, &finfo);
  2075.         hpref = GetResource('mPRF', prefsID);
  2076.         DetachResource(hpref);
  2077.         htmpl = GetResource('TMPL', IDnaID);
  2078.         DetachResource(htmpl);
  2079.     }
  2080.     pfolder->refnum = HOpenResFile(vRefNum, dirID, pname, fsRdWrPerm);
  2081.     if (pfolder->refnum < 0) return (-1);
  2082.     if (hpref) {
  2083.         AddResource(hpref, 'mPRF', prefsID, "\p");
  2084.         AddResource(htmpl, 'TMPL', IDnaID, "\pIDna");
  2085.         ReleaseResource(htmpl);
  2086.     } else {
  2087.         hpref = GetResource('mPRF', prefsID);
  2088.     }
  2089.     if (!hpref) return (-1);
  2090.     mpack_prefs = (struct mpack_preferences **) hpref;
  2091.     
  2092.     return (0);
  2093. }
  2094.  
  2095. /* cleanup shared resources
  2096.  */
  2097. void maccleanup()
  2098. {
  2099.     if (pfolder) {
  2100.         CloseResFile(pfolder->refnum);
  2101.         DisposPtr((Ptr) pfolder);
  2102.     }
  2103.     if (icinst) ICStop(icinst);
  2104.     if (tcpstart == 1) NATCPdone(120); /* give 2 seconds to go away */
  2105. }
  2106.  
  2107. main()
  2108. {
  2109.     CursHandle cursH;
  2110.  
  2111.     if (NAinit(128, 2, openfile, mainmenu, 3, 1, 0, iClose) == 0) {
  2112.         /* set up preferences */
  2113.         if (makepref() < 0) {
  2114.             yell("Couldn't create preferences file");
  2115.         } else {
  2116.             /* set up internet config */
  2117.             if (ICStart(&icinst, 'mPAK') == noErr) {
  2118.                 (void) ICFindConfigFile(icinst, 0, NULL);
  2119.             }
  2120.             /* save watch cursor */
  2121.             cursH = GetCursor(watchCursor);
  2122.             watch = **cursH;
  2123.             /* enter main loop, cleanup on exit */
  2124.             NAmainloop();
  2125.             maccleanup();
  2126.         }
  2127.     }
  2128. }
  2129.