home *** CD-ROM | disk | FTP | other *** search
/ Magazyn Exec 5 / CD_Magazyn_EXEC_nr_5.iso / Programy / Internet / Poczta / YAM22src.lha / YAM_UT.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-11-04  |  82.6 KB  |  2,754 lines

  1. /***************************************************************************
  2.  
  3.  YAM - Yet Another Mailer
  4.  Copyright (C) 2000  Marcel Beck <mbeck@yam.ch>
  5.  
  6.  This program is free software; you can redistribute it and/or modify
  7.  it under the terms of the GNU General Public License as published by
  8.  the Free Software Foundation; either version 2 of the License, or
  9.  (at your option) any later version.
  10.  
  11.  This program is distributed in the hope that it will be useful,
  12.  but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.  GNU General Public License for more details.
  15.  
  16.  You should have received a copy of the GNU General Public License
  17.  along with this program; if not, write to the Free Software
  18.  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  19.  
  20.  YAM Official Support Site :  http://www.yam.ch
  21.  YAM OpenSource project    :  http://sourceforge.net/projects/yamos/
  22.  
  23. ***************************************************************************/
  24.  
  25. #include "YAM.h"
  26.  
  27. /***************************************************************************
  28.  Utilities
  29. ***************************************************************************/
  30.  
  31. /*** Requesters ***/
  32. /// StringRequest
  33. //  Puts up a string requester
  34. int StringRequest(char *string, int size, char *title, char *body, char *yestext, char *alttext, char *notext, BOOL secret, APTR parent)
  35. {
  36.    APTR bt_okay, bt_middle, bt_cancel, wi_sr, st_in;
  37.    int ret_code = -1;
  38.  
  39.    wi_sr = WindowObject,
  40.       MUIA_Window_Title, title ? title : "YAM",
  41.       MUIA_Window_RefWindow, parent,
  42.       MUIA_Window_LeftEdge, MUIV_Window_LeftEdge_Centered,
  43.       MUIA_Window_TopEdge, MUIV_Window_TopEdge_Centered,
  44.       MUIA_Window_ID, MAKE_ID('S','R','E','Q'),
  45.       WindowContents, VGroup,
  46.          Child, VGroup,
  47.             GroupFrame,
  48.             MUIA_Background, MUII_GroupBack,
  49.             Child, LLabel(body),
  50.             Child, st_in = secret ? MakePassString("") : MakeString(size, ""),
  51.          End,
  52.          Child, ColGroup(3),
  53.             Child, bt_okay = MakeButton(yestext),
  54.             Child, bt_middle = alttext ? MakeButton(alttext) : HSpace(0),
  55.             Child, bt_cancel = MakeButton(notext),
  56.          End,
  57.       End,
  58.    End;
  59.    setstring(st_in, string);
  60.    set(wi_sr, MUIA_Window_ActiveObject, st_in);
  61.    set(G->App, MUIA_Application_Sleep, TRUE);
  62.    DoMethod(G->App, OM_ADDMEMBER, wi_sr);
  63.    DoMethod(bt_okay  , MUIM_Notify, MUIA_Pressed, FALSE, G->App, 2, MUIM_Application_ReturnID, 1);
  64.    DoMethod(bt_middle, MUIM_Notify, MUIA_Pressed, FALSE, G->App, 2, MUIM_Application_ReturnID, 2);
  65.    DoMethod(bt_cancel, MUIM_Notify, MUIA_Pressed, FALSE, G->App, 2, MUIM_Application_ReturnID, 3);
  66.    DoMethod(st_in, MUIM_Notify, MUIA_String_Acknowledge, MUIV_EveryTime, G->App, 2, MUIM_Application_ReturnID, 1);
  67.    DoMethod(wi_sr, MUIM_Notify, MUIA_Window_CloseRequest, TRUE, G->App, 2, MUIM_Application_ReturnID, 3);
  68.    if (!SafeOpenWindow(wi_sr)) ret_code = 0;
  69.    else while (ret_code == -1)
  70.    {
  71.       ULONG signals;
  72.       switch (DoMethod(G->App, MUIM_Application_Input, &signals))
  73.       {
  74.          case 1: ret_code = 1; break;
  75.          case 2: ret_code = 2; break;
  76.          case 3: ret_code = 0; break;
  77.       }
  78.       if (ret_code == -1 && signals) Wait(signals);
  79.    }
  80.    if (ret_code > 0) GetMUIString(string, st_in);
  81.    DoMethod(G->App, OM_REMMEMBER, wi_sr);
  82.    set(G->App, MUIA_Application_Sleep, FALSE);
  83.    return ret_code;
  84. }
  85. ///
  86. /// FolderRequest
  87. //  Allows user to choose a folder from a list
  88. struct Folder *FolderRequest(char *title, char *body, char *yestext, char *notext, struct Folder *exclude, APTR parent)
  89. {
  90.    static int lastactive;
  91.    struct Folder **flist, *folder = (struct Folder *)-1;
  92.    int act, i;
  93.    char *fname;
  94.    APTR bt_okay, bt_cancel, wi_fr, lv_folder;
  95.  
  96.    wi_fr = WindowObject,
  97.       MUIA_Window_Title, title ? title : "YAM",
  98.       MUIA_Window_RefWindow, parent,
  99.       MUIA_Window_LeftEdge, MUIV_Window_LeftEdge_Centered,
  100.       MUIA_Window_TopEdge, MUIV_Window_TopEdge_Centered,
  101.       MUIA_Window_ID, MAKE_ID('F','R','E','Q'),
  102.       WindowContents, VGroup,
  103.          Child, LLabel(body),
  104.          Child, lv_folder = ListviewObject,
  105.             MUIA_CycleChain, 1,
  106.             MUIA_Listview_DoubleClick, TRUE,
  107.             MUIA_Listview_List, ListObject,
  108.                InputListFrame,
  109.                MUIA_List_AutoVisible, TRUE,
  110.             End,
  111.          End,
  112.          Child, ColGroup(3),
  113.             Child, bt_okay = MakeButton(yestext),
  114.             Child, HSpace(0),
  115.             Child, bt_cancel = MakeButton(notext),
  116.          End,
  117.       End,
  118.    End;
  119.    if (wi_fr)
  120.    {
  121.       flist = FO_CreateList();
  122.       for (i = 1; i <= (int)*flist; i++) if (flist[i] != exclude) if (flist[i]->Type != FT_SEPARATOR)
  123.          DoMethod(lv_folder, MUIM_List_InsertSingle, flist[i]->Name, MUIV_List_Insert_Bottom);
  124.       free(flist);
  125.       set(lv_folder, MUIA_List_Active, lastactive);
  126.       set(wi_fr, MUIA_Window_ActiveObject, lv_folder);
  127.       set(G->App, MUIA_Application_Sleep, TRUE);
  128.       DoMethod(G->App, OM_ADDMEMBER, wi_fr);
  129.       DoMethod(bt_okay  , MUIM_Notify, MUIA_Pressed, FALSE, G->App, 2, MUIM_Application_ReturnID, 1);
  130.       DoMethod(bt_cancel, MUIM_Notify, MUIA_Pressed, FALSE, G->App, 2, MUIM_Application_ReturnID, 3);
  131.       DoMethod(lv_folder, MUIM_Notify, MUIA_Listview_DoubleClick, MUIV_EveryTime, G->App, 2, MUIM_Application_ReturnID, 1);
  132.       DoMethod(wi_fr, MUIM_Notify, MUIA_Window_CloseRequest, TRUE, G->App, 2, MUIM_Application_ReturnID, 3);
  133.       if (!SafeOpenWindow(wi_fr)) folder = NULL;
  134.       else while (folder == (struct Folder *)-1)
  135.       {
  136.          ULONG signals, oo = DoMethod(G->App, MUIM_Application_Input, &signals);
  137.          switch (oo)
  138.          {
  139.             case 1:
  140.                get(lv_folder, MUIA_List_Active, &act);
  141.                DoMethod(lv_folder, MUIM_List_GetEntry, act, &fname);
  142.                if (folder = FO_GetFolderByName(fname, NULL)) lastactive = act;
  143.                break;
  144.             case 3: folder = NULL; break;
  145.          }
  146.          if (folder == (struct Folder *)-1 && signals) Wait(signals);
  147.       }
  148.       DoMethod(G->App, OM_REMMEMBER, wi_fr);
  149.       set(G->App, MUIA_Application_Sleep, FALSE);
  150.    }
  151.    return folder;
  152. }
  153. ///
  154. /// AttachRequest
  155. //  Allows user to select a message part (attachment) from a list
  156. struct Part *AttachRequest(char *title, char *body, char *yestext, char *notext, int winnum, int mode, APTR parent)
  157. {
  158.    struct Part *retpart = (struct Part *)-1, *part, *prevpart;
  159.    APTR bt_okay, bt_cancel, wi_ar, lv_attach;
  160.  
  161.    wi_ar = WindowObject,
  162.       MUIA_Window_Title, title ? title : "YAM",
  163.       MUIA_Window_RefWindow, parent,
  164.       MUIA_Window_LeftEdge, MUIV_Window_LeftEdge_Centered,
  165.       MUIA_Window_TopEdge, MUIV_Window_TopEdge_Centered,
  166.       MUIA_Window_ID, MAKE_ID('A','R','E','Q'),
  167.       WindowContents, VGroup,
  168.          Child, LLabel(body),
  169.          Child, lv_attach = NListviewObject,
  170.             MUIA_CycleChain, 1,
  171.             MUIA_NListview_NList, NListObject,
  172.                InputListFrame,
  173.                MUIA_NList_DoubleClick, TRUE,
  174.                MUIA_NList_MultiSelect, (mode&ATTREQ_MULTI) ? MUIV_NList_MultiSelect_Default : MUIV_NList_MultiSelect_None,
  175.                MUIA_NList_DisplayHook, &RE_LV_AttachDspFuncHook,
  176.                MUIA_NList_Format, "BAR,BAR,",
  177.             End,
  178.          End,
  179.          Child, ColGroup(3),
  180.             Child, bt_okay = MakeButton(yestext),
  181.             Child, HSpace(0),
  182.             Child, bt_cancel = MakeButton(notext),
  183.          End,
  184.       End,
  185.    End;
  186.    if (wi_ar)
  187.    {
  188.       static struct Part spart[2];
  189.       spart[0].Nr = -2; strcpy(spart[0].Name, GetStr(MSG_RE_Original)); spart[0].Size = G->RE[winnum]->Mail.Size; spart[0].Decoded = TRUE;
  190.       spart[1].Nr = -1; strcpy(spart[1].Name, GetStr(MSG_RE_AllTexts)); spart[1].Size = 0;
  191.       DoMethod(lv_attach, MUIM_NList_InsertSingle, &spart[0], MUIV_NList_Insert_Bottom);
  192.       if ((mode&0xF) != ATTREQ_DISP) DoMethod(lv_attach, MUIM_NList_InsertSingle, &spart[1], MUIV_NList_Insert_Bottom);
  193.       for (part = G->RE[winnum]->FirstPart->Next; part; part = part->Next)
  194.          if ((mode&0xF) != ATTREQ_PRINT || part->Printable) DoMethod(lv_attach, MUIM_NList_InsertSingle, part, MUIV_NList_Insert_Bottom);
  195. //      set(lv_attach, MUIA_NList_Active, 1);
  196.       set(wi_ar, MUIA_Window_ActiveObject, lv_attach);
  197.       set(G->App, MUIA_Application_Sleep, TRUE);
  198.       DoMethod(G->App, OM_ADDMEMBER, wi_ar);
  199.       DoMethod(bt_okay  , MUIM_Notify, MUIA_Pressed, FALSE, G->App, 2, MUIM_Application_ReturnID, 1);
  200.       DoMethod(bt_cancel, MUIM_Notify, MUIA_Pressed, FALSE, G->App, 2, MUIM_Application_ReturnID, 3);
  201.       DoMethod(lv_attach, MUIM_Notify, MUIA_NList_DoubleClick, MUIV_EveryTime, G->App, 2, MUIM_Application_ReturnID, 1);
  202.       DoMethod(wi_ar, MUIM_Notify, MUIA_Window_CloseRequest, TRUE, G->App, 2, MUIM_Application_ReturnID, 3);
  203.       if (!SafeOpenWindow(wi_ar)) retpart = NULL;
  204.       else  while (retpart == (struct Part *)-1)
  205.       {
  206.          ULONG signals;
  207.          int id;
  208.          switch (DoMethod(G->App, MUIM_Application_Input, &signals))
  209.          {
  210.             case 1:
  211.                for (id = MUIV_NList_NextSelected_Start;; prevpart = part)
  212.                {
  213.                   DoMethod(lv_attach, MUIM_NList_NextSelected, &id);
  214.                   if (id == MUIV_NList_NextSelected_End) break;
  215.                   DoMethod(lv_attach, MUIM_NList_GetEntry, id, &part);
  216.                   if (retpart == (struct Part *)-1) retpart = part;
  217.                   else prevpart->NextSelected = part;
  218.                }
  219.                break;
  220.             case 3: retpart = NULL; break;
  221.          }
  222.          if (retpart == (struct Part *)-1 && signals) Wait(signals);
  223.       }
  224.       DoMethod(G->App, OM_REMMEMBER, wi_ar);
  225.       set(G->App, MUIA_Application_Sleep, FALSE);
  226.    }
  227.    return retpart;
  228. }
  229. ///
  230. /// InfoWindow
  231. //  Displays a text in an own window
  232. void InfoWindow(char *title, char *body, char *oktext, APTR parent)
  233. {
  234.    APTR bt_okay, wi_iw;
  235.  
  236.    if (wi_iw = WindowObject,
  237.          MUIA_Window_Title, title,
  238.          MUIA_Window_RefWindow, parent,
  239.          MUIA_Window_LeftEdge, MUIV_Window_LeftEdge_Centered,
  240.          MUIA_Window_TopEdge, MUIV_Window_TopEdge_Centered,
  241.          WindowContents, VGroup,
  242.             MUIA_Background, MUII_RequesterBack,
  243.             Child, VGroup,
  244.                TextFrame,
  245.                MUIA_Background, MUII_TextBack,
  246.                Child, LLabel(body),
  247.             End,
  248.             Child, HCenter(bt_okay = MakeButton(oktext)),
  249.          End,
  250.       End)
  251.    {
  252.       DoMethod(G->App, OM_ADDMEMBER, wi_iw);
  253.       DoMethod(bt_okay, MUIM_Notify, MUIA_Pressed, FALSE, MUIV_Notify_Application, 5, MUIM_Application_PushMethod, parent, 2, MUIM_MainWindow_CloseWindow, wi_iw);
  254.       DoMethod(wi_iw  , MUIM_Notify, MUIA_Window_CloseRequest, TRUE, MUIV_Notify_Application, 5, MUIM_Application_PushMethod, parent, 2, MUIM_MainWindow_CloseWindow, wi_iw);
  255.       set(wi_iw, MUIA_Window_DefaultObject, bt_okay);
  256.       set(wi_iw, MUIA_Window_Open, TRUE);
  257.    }
  258. }
  259. ///
  260.  
  261. /*** String related ***/
  262. /// itoa
  263. //  Converts an integer into a string
  264. char *itoa(int val)
  265. {
  266.    static char str[SIZE_SMALL];
  267.    sprintf(str, "%ld", val);
  268.    return str;
  269. }
  270. ///
  271. /// MatchNoCase
  272. //  Case insensitive pattern matching
  273. int MatchNoCase(char *string, char *match)
  274. {
  275.    char pattern[SIZE_PATTERN];
  276.    ParsePatternNoCase(match, pattern, SIZE_PATTERN);
  277.    return MatchPatternNoCase(pattern, string);
  278. }
  279. ///
  280. /// CompNoCase
  281. //  Case insensitive string comparison
  282. BOOL CompNoCase(char *a, char *b, int l)
  283. {
  284.    for (; l; l--, a++, b++) if (toupper(*a) != toupper(*b)) return FALSE;
  285.    return TRUE;
  286. }
  287. ///
  288. /// MatchTT
  289. //  Checks if charset matches a translation table
  290. BOOL MatchTT(char *charset, struct TranslationTable *tt, BOOL in)
  291. {
  292.    if (!tt) return FALSE;
  293.    return (BOOL)MatchNoCase(charset, in ? tt->SourceCharset : tt->DestCharset);
  294. }
  295. ////
  296. /// ISpace
  297. //  Checks if character is a white-space
  298. BOOL ISpace(char ch)
  299. {
  300.    return (BOOL)(ch == ' ' || (ch >= 9 && ch <= 13));
  301. }
  302. ///
  303. /// isSpace
  304. //  Localized version if isspace()
  305. BOOL isSpace(int c)
  306. {
  307.    UBYTE ch = (UBYTE)c;
  308.    return (BOOL)(G->Locale ? (IsSpace(G->Locale,(ULONG)ch) != 0) : (isspace(ch) != 0));
  309. }
  310. ///
  311. /// isGraph
  312. //  Localized version of isgraph()
  313. BOOL isGraph(int c)
  314. {
  315.    UBYTE ch = (UBYTE)c;
  316.    return (BOOL)(G->Locale ? (IsGraph(G->Locale,(ULONG)ch) != 0) : (isgraph(ch) != 0));
  317. }
  318. ///
  319. /// isAlNum
  320. //  Localized version of isalnum()
  321. BOOL isAlNum(int c)
  322. {
  323.    UBYTE ch = (UBYTE)c;
  324.    return (BOOL)(G->Locale ? (IsAlNum(G->Locale,(ULONG)ch) != 0) : (isalnum(ch) != 0));
  325. }
  326. ///
  327. /// StripUnderscore
  328. //  Removes underscore from button labels
  329. char *StripUnderscore(char *label)
  330. {
  331.    if (strchr(label,'_'))
  332.    {
  333.       static char newlabel[SIZE_DEFAULT];
  334.       char *p = newlabel;
  335.       for (; *label; label++) if (*label != '_') *p++ = *label;
  336.       *p = 0;
  337.       return newlabel;
  338.    }
  339.    else return label;
  340. }
  341. ///
  342. /// GetNextLine
  343. //  Reads next line from a multi-line string
  344. char *GetNextLine(char *p1)
  345. {
  346.    static char *begin;
  347.    char *p2;
  348.    if (p1) begin = p1;
  349.    if (p1 = strchr(p2 = begin, '\n')) { *p1 = 0; begin = ++p1; }
  350.    return p2;
  351. }
  352. ///
  353. /// TrimStart
  354. //  Strips leading spaces
  355. char *TrimStart(char *s)
  356. {
  357.    while (*s && ISpace(*s)) ++s;
  358.    return s;
  359. }       
  360. ///
  361. /// TrimEnd
  362. //  Removes trailing spaces
  363. char *TrimEnd(char *s)
  364. {
  365.    char *e = s+strlen(s)-1;
  366.    while (e >= s && ISpace(*e)) *e-- = 0;
  367.    return s;
  368. }
  369. ///
  370. /// Trim
  371. //  Removes leading and trailing spaces
  372. char *Trim(char *s)
  373. {
  374.    char *e = s+strlen(s)-1;
  375.    while (*s && ISpace(*s)) ++s;
  376.    while (e >= s && ISpace(*e)) *e-- = 0;
  377.    return s;
  378. }       
  379. ///
  380. /// stccat
  381. //  Safe string concatenation
  382. char *stccat(char *a, char *b, int n)
  383. {
  384.    int m = 1;
  385.    char *p = a;
  386.    while (*p) { p++; m++; }
  387.    while (*b && m < n) { *p++ = *b++; m++; }
  388.    *p = 0;
  389.    return a;
  390. }
  391. ///
  392. /// stristr
  393. //  Case insensitive version of strstr()
  394. char *stristr(char *a, char *b)
  395. {
  396.    int l = strlen(b);
  397.    for (; *a; a++) if (CompNoCase(a, b, l)) return a;
  398.    return NULL;
  399. }
  400. ///
  401. /// MyStrChr
  402. //  Searches for a character in string, ignoring text in quotes
  403. char *MyStrChr(char *s, int c)
  404. {
  405.    BOOL nested = FALSE;
  406.    for (; *s; s++)
  407.       if (*s == '\"') nested = !nested; 
  408.       else if (*s == (char)c && !nested) return s;
  409.    return NULL;
  410. }
  411. ///
  412. /// Index
  413. //  Returns position of a character in a string
  414. int Index(char *str, char chr)
  415. {
  416.    char *t = strchr(str, chr);
  417.    return t ? (int)(t-str) : -1;
  418. }
  419. ///
  420. /// AllocStrBuf
  421. //  Allocates a dynamic buffer
  422. char *AllocStrBuf(long initlen)
  423. {
  424.    char *strbuf = AllocMem(initlen+4, MEMF_PUBLIC|MEMF_CLEAR);
  425.    if (strbuf)
  426.    {
  427.       *((long *)strbuf) = initlen;
  428.       return &strbuf[4];
  429.    }
  430.    return NULL;
  431. }
  432. ///
  433. /// FreeStrBuf
  434. //  Frees a dynamic buffer
  435. void FreeStrBuf(char *strbuf)
  436. {
  437.    long len;
  438.  
  439.    if (!strbuf) return;
  440.    len = *((long *)(strbuf-4));
  441.    FreeMem(strbuf-4, len+4);        
  442. }
  443. ///
  444. /// StrBufCpy
  445. //  Fills a dynamic buffer
  446. char *StrBufCpy(char *strbuf, char *source)
  447. {
  448.    long oldlen, newlen;
  449.    char *newstrbuf;
  450.  
  451.    if (!strbuf) strbuf = AllocStrBuf(strlen(source)+1);
  452.    oldlen = *((long *)(strbuf-sizeof(long)));
  453.    newstrbuf = strbuf;
  454.    for (newlen = oldlen; newlen <= strlen(source); newlen += SIZE_DEFAULT);
  455.    if (newlen != oldlen)
  456.    {
  457.       newstrbuf = AllocStrBuf(newlen);
  458.       FreeStrBuf(strbuf);
  459.    }
  460.    strcpy(newstrbuf, source);
  461.    return newstrbuf;
  462. }
  463. ///
  464. /// StrBufCat
  465. //  String concatenation using a dynamic buffer
  466. char *StrBufCat(char *strbuf, char *source)
  467. {
  468.    long oldlen, newlen;
  469.    char *newstrbuf;
  470.  
  471.    if (!strbuf) strbuf = AllocStrBuf(strlen(source)+1);
  472.    oldlen = *((long *)(strbuf-sizeof(long)));
  473.    newstrbuf = strbuf;
  474.    for (newlen = oldlen; newlen <= strlen(strbuf)+strlen(source); newlen += SIZE_DEFAULT);
  475.    if (newlen != oldlen)
  476.    {
  477.       newstrbuf = AllocStrBuf(newlen);
  478.       strcpy(newstrbuf, strbuf);
  479.       FreeStrBuf(strbuf);
  480.    }
  481.    strcat(newstrbuf, source);
  482.    return newstrbuf;
  483. }
  484. ///
  485. /// FreeData2D
  486. //  Frees dynamic two-dimensional array
  487. void FreeData2D(struct Data2D *data)
  488. {
  489.    while (data->Used) FreeStrBuf(data->Data[--data->Used]);
  490.    if (data->Allocated) free(data->Data);
  491.    data->Data = NULL; data->Allocated = 0;
  492. }
  493. ///
  494. /// AllocData2D
  495. //  Allocates dynamic two-dimensional array
  496. char *AllocData2D(struct Data2D *data, int initsize)
  497. {
  498.    if (data->Used >= data->Allocated)
  499.    {
  500.       data->Allocated += 10;
  501.       if (data->Data) data->Data = realloc(data->Data, data->Allocated*sizeof(char *));
  502.       else            data->Data = malloc(data->Allocated*sizeof(char *));
  503.    }
  504.    return data->Data[data->Used++] = AllocStrBuf(initsize);
  505. }
  506. ///
  507. /// AllocCopy
  508. //  Duplicates a memory block
  509. APTR AllocCopy(APTR source, int size)
  510. {
  511.    APTR dest;
  512.    if (dest = malloc(size)) memcpy(dest, source, size);
  513.    return dest;
  514. }
  515. ///
  516. /// Decrypt
  517. //  Decrypts passwords
  518. char *Decrypt(char *source)
  519. {
  520.    static char buffer[SIZE_PASSWORD+2];
  521.    char *write = &buffer[SIZE_PASSWORD];
  522.  
  523.    *write-- = 0;
  524.    while (*source)
  525.    {
  526.       *write-- = ((char)atoi(source))^CRYPTBYTE;
  527.       source += 4;
  528.    }
  529.    return ++write;
  530. }
  531. ///
  532. /// Encrypt
  533. //  Encrypts passwords
  534. char *Encrypt(char *source)
  535. {
  536.    static char buffer[4*SIZE_PASSWORD+2];
  537.    char *read = source+strlen(source)-1;
  538.  
  539.    *buffer = 0;
  540.    while (read >= source)
  541.    {
  542.       unsigned char c = (*read--)^CRYPTBYTE;
  543.       sprintf(&buffer[strlen(buffer)], "%03d ", c);
  544.    }
  545.    return buffer;
  546. }
  547. ///
  548.  
  549. /*** File related ***/
  550. /// GetLine
  551. //  Gets Null terminated line of a text file
  552. char *GetLine(FILE *fh, char *buffer, int bufsize)
  553. {
  554.    char *ptr;
  555.    clear(buffer, bufsize);
  556.    if (!fgets(buffer, bufsize, fh)) return NULL;
  557.    if (ptr = strpbrk(buffer, "\r\n")) *ptr = 0;
  558.    return buffer;
  559. }       
  560. ///
  561. /// FileInfo
  562. //  Gets size, protection bits and type of a file/directory
  563. BOOL FileInfo(char *filename, int *size, long *bits, long *type)
  564. {
  565.    struct FileInfoBlock *fib;
  566.    BPTR lock;
  567.  
  568.    if (lock = Lock((STRPTR)filename,ACCESS_READ))
  569.    {
  570.       fib = AllocDosObject(DOS_FIB, NULL);
  571.       Examine(lock, fib);
  572.       if (size) *size = fib->fib_Size;
  573.       if (bits) *bits = fib->fib_Protection;
  574.       if (type) *type = fib->fib_DirEntryType;
  575.       FreeDosObject(DOS_FIB, fib);
  576.       UnLock(lock);
  577.       return TRUE;
  578.    }
  579.    return FALSE;
  580. }
  581. ///
  582. /// FileSize
  583. //  Returns size of a file
  584. int FileSize(char *filename)
  585. {
  586.    int size;
  587.    if (FileInfo(filename, &size, NULL, NULL)) return size; else return -1;
  588. }
  589. ///
  590. /// FileProtection
  591. //  Returns protection bits of a file
  592. long FileProtection(char *filename)
  593. {
  594.    long bits;
  595.    if (FileInfo(filename, NULL, &bits, NULL)) return bits; else return -1;
  596. }
  597. ///
  598. /// FileType
  599. //  Returns file type (file/directory)
  600. int FileType(char *filename)
  601. {
  602.    long type;
  603.    if (FileInfo(filename, NULL, NULL, &type)) return (type < 0 ? 1 : 2); else return 0;
  604. }
  605. ///
  606. /// FileExists
  607. //  Checks if a file exists
  608. BOOL FileExists(char *filename)
  609. {
  610.    return FileInfo(filename, NULL, NULL, NULL);
  611. }
  612. ///
  613. /// RenameFile
  614. //  Renames a file and restores the protection bits
  615. BOOL RenameFile(char *oldname, char *newname)
  616. {
  617.    struct FileInfoBlock *fib;
  618.    BPTR lock;
  619.    if (!Rename(oldname, newname)) return FALSE;
  620.    fib = AllocDosObject(DOS_FIB,NULL);
  621.    if (lock = Lock(newname, ACCESS_READ))
  622.    {
  623.       Examine(lock, fib); UnLock(lock);
  624.       SetProtection(newname, fib->fib_Protection & (~FIBF_ARCHIVE));
  625.    }
  626.    FreeDosObject(DOS_FIB,fib);
  627.    return TRUE;
  628. }
  629. ///
  630. /// CopyFile
  631. //  Copies a file
  632. BOOL CopyFile(char *dest, FILE *destfh, char *sour, FILE *sourfh)
  633. {
  634.    BOOL success = FALSE;
  635.  
  636.    if (sour) sourfh = fopen(sour, "r");
  637.    if (dest) destfh = fopen(dest, "w");
  638.    if (sourfh && destfh)
  639.    {
  640.       char buf[SIZE_LARGE];
  641.       int len;
  642.       success = TRUE;
  643.       while (len = fread(buf, 1, SIZE_LARGE, sourfh))
  644.          if (fwrite(buf, 1, len, destfh) != len) { success = FALSE; break; }
  645.    }
  646.    if (dest && destfh) fclose(destfh);
  647.    if (sour && sourfh) fclose(sourfh);
  648.    return success;
  649. }
  650. ///
  651. /// ConvertCRLF
  652. //  Converts line breaks from LF to CRLF or vice versa
  653. BOOL ConvertCRLF(char *in, char *out, BOOL to)
  654. {
  655.    BOOL success = FALSE;
  656.    char buf[SIZE_LINE];
  657.    FILE *infh, *outfh;
  658.  
  659.    if (infh = fopen(in, "r"))
  660.    {
  661.       if (outfh = fopen(out, "w"))
  662.       {
  663.          while (GetLine(infh, buf, SIZE_LINE)) fprintf(outfh, "%s%s\n", buf, to?"\r":"");
  664.          success = TRUE;
  665.          fclose(outfh);
  666.       }
  667.       fclose(infh);
  668.    }
  669.    return success;
  670. }
  671. ///
  672. /// GetWord
  673. //  Word-wrapping algorithm: gets next word
  674. int GetWord(char **rptr, char *wbuf, int max)
  675. {
  676.    int c, i = 0;
  677.    static int nonblanks = 0;
  678.  
  679.    if (!(c = *(*rptr)++)) { *wbuf = 0; return 0; }
  680.    if (isSpace(c))
  681.    {
  682.       while (isSpace(c) && c != '\n')
  683.       {
  684.          if (i < max-1) wbuf[i++] = c;
  685.          c = *(*rptr)++;
  686.       }
  687.       if (c == '\n' || !c) { i = 0; wbuf[i++] = '\n'; }
  688.    }
  689.    else
  690.    {
  691.       while (isGraph(c))
  692.       {
  693.          if (i < max-1) wbuf[i++] = c; else break;
  694.          c = *(*rptr)++;
  695.       }
  696.       nonblanks += i;
  697.       while (isSpace(c) && c != '\n') c = *(*rptr)++;
  698.       if (c == '\n')
  699.       {
  700.          if (nonblanks <= 20) wbuf[i++] = '\n';
  701.          nonblanks = 0;
  702.       }
  703.    }
  704.    if (isGraph(c)) (*rptr)--;
  705.    wbuf[i] = '\0'; return i;
  706. }
  707. ///
  708. /// ReflowParagraph
  709. //  Word-wrapping algorithm: process a paragraph
  710. char *ReflowParagraph(char *start, char *end, int lmax, char *dest)
  711. {
  712.    int lword, lline = 0;
  713.    char c, word[SIZE_LARGE], *p;
  714.    BOOL dented = FALSE;
  715.  
  716.    while (lword = GetWord(&start, word, SIZE_LARGE))
  717.    {
  718.       if ((c = word[lword-1]) == '\n')  word[--lword] = '\0';
  719.       if (!lword);
  720.       else if (isSpace(*word))
  721.       {
  722.          if (lline) *dest++ = '\n';
  723.          dented = TRUE; lline = lword;
  724.          for (p = word; *p; p++) *dest++ = *p;
  725.       }
  726.       else
  727.       {
  728.          if (lline == 0 || dented) { for (p = word; *p; p++) *dest++ = *p; lline += lword; dented = FALSE; }
  729.          else
  730.          {
  731.             if (lline+lword < lmax) { *dest++ = ' '; for (p = word; *p; p++) *dest++ = *p; lline += lword+1; }
  732.             else { *dest++ = '\n'; for (p = word; *p; p++) *dest++ = *p; lline = lword; }
  733.          }
  734.       }
  735.       if (c == '\n') { *dest++ = '\n'; lline = 0; }
  736.       if (start > end) break;
  737.    }
  738.    if (lline) *dest++ = '\n';
  739.    *dest-- = 0;
  740.    return dest;
  741. }
  742. ///
  743. /// RemoveQuoteString
  744. //  Removes reply prefix
  745. char *RemoveQuoteString(char *start, char *end, char *quot, char *dest)
  746. {
  747.    while (start <= end)
  748.    {
  749.       if (!strncmp(start, quot, strlen(quot))) start += strlen(quot);
  750.       while (*start && *start != '\n') *dest++ = *start++;
  751.       if (*start) *dest++ = *start++;
  752.    }
  753.    *dest-- = 0;
  754.    return dest;
  755. }
  756. ///
  757. /// InsertQuoteString
  758. //  Inserts reply prefix
  759. char *InsertQuoteString(char *start, char *quote, FILE *out)
  760. {
  761.    if ((*start != '\n' || C->QuoteEmptyLines) && strncmp(start, "<sb>", 4) && strncmp(start, "<tsb>", 5))
  762.    {
  763.       int level, i;
  764.       for (i = level = 0; start[i] && start[i] != '\n' && i < 16; i++)
  765.       {
  766.          if (isSpace(start[i]) && (!level || start[i+1] == '>')) continue;
  767.          if (start[i] == '>') level++; else if (!isAlNum(start[i]) || level) break;
  768.       }
  769.       if (level) start = &start[i];
  770.       if (level > 8) level = 8;
  771.       fputs(quote, out); while (level--) fputc('>', out);
  772.       if (*start != ' ') fputc(' ', out);
  773.    }
  774.    return start;
  775. }
  776. ///
  777. /// SaveParagraph
  778. //  Writes a paragraph and inserts reply prefixes
  779. void SaveParagraph(char *start, char *end, char *prefix, FILE *out)
  780. {
  781.    while (start <= end)
  782.    {
  783.       if (*prefix) start = InsertQuoteString(start, prefix, out);
  784.       while (*start && *start != '\n') fputc(*start++, out);
  785.       if (*start) fputc(*start++, out);
  786.    }
  787. }
  788. ///
  789. /// QuoteWordWrap
  790. //  Reformats quoted messages to a new line length
  791. void QuoteWordWrap(char *rptr, int lmax, char *prefix, char *firstprefix, FILE *out)
  792. {
  793.    if (!prefix) prefix = firstprefix;
  794.    while (*rptr)
  795.    {
  796.       char *p, *ps = rptr, *pe, quot[17], c;
  797.       int lsm = 0;
  798.       *quot = 0;
  799.       while (TRUE)
  800.       {
  801.          int ls = 0;
  802.          char *p = rptr;
  803.          while (*rptr && *rptr != '\n') { rptr++; ls++; }
  804.          if (ls && *p == '>' && !*quot)
  805.          {
  806.             int i = 0;
  807.             while ((*p == ' ' || *p == '>') && i < 16) quot[i++] = *p++;
  808.             quot[i] = 0;
  809.             while (i > 1) if (quot[i-2] == ' ' && quot[i-1] == ' ') quot[--i] = 0; else break;
  810.          }
  811.          if (ls > lsm) lsm = ls;
  812.          if (!*rptr) break;
  813.          c = rptr[1];
  814.          if (isSpace(c) || !c || c == '\n') break;
  815.          if (!*quot && c == '>') break;
  816.          if (*quot) if (strncmp(&rptr[1], quot, strlen(quot))) break;
  817.          rptr++;
  818.       }
  819.       pe = rptr;
  820.       if (lsm > lmax)
  821.       {
  822.          if (*quot)
  823.          {
  824.             char *buf = calloc(2*(pe-ps+2),1), newprefix[SIZE_DEFAULT];
  825.             RemoveQuoteString(ps, pe, quot, buf);
  826.             strcpy(newprefix, firstprefix); strcat(newprefix, TrimEnd(quot));
  827.             QuoteWordWrap(buf, lmax-strlen(quot), newprefix, firstprefix, out);
  828.             free(buf);
  829.          }
  830.          else
  831.          {
  832.             char *buf = calloc(2*(pe-ps+2),1);
  833.             p = ReflowParagraph(ps, pe, lmax, buf);
  834.             SaveParagraph(buf, p, prefix, out);
  835.             free(buf);
  836.          }
  837.       }
  838.       else SaveParagraph(ps, pe, prefix, out);
  839.       rptr = pe+1;
  840.    }
  841. }
  842. ///
  843. /// SimpleWordWrap
  844. //  Reformats a file to a new line length
  845. void SimpleWordWrap(char *filename, int wrapsize)
  846. {
  847.    BPTR fh;
  848.    if (fh = Open((STRPTR)filename, MODE_OLDFILE))
  849.    {
  850.       char ch;
  851.       int p = 0, lsp = -1, sol = 0;
  852.  
  853.       while (Read(fh, &ch, 1))
  854.       {
  855.          if (p-sol > wrapsize && lsp >= 0) 
  856.          {
  857.             ch = '\n';
  858.             Seek(fh, lsp-p-1, OFFSET_CURRENT);
  859.             p = lsp;
  860.             Write(fh, &ch, 1);
  861.          }
  862.          if (ISpace(ch)) lsp = p;
  863.          if (ch == '\n') { sol = p+1; lsp = -1; }
  864.          p++;
  865.       }
  866.       Close(fh);
  867.    }
  868. }
  869. ///
  870. /// ReqFile
  871. //  Puts up a file requester
  872. int ReqFile(int num, struct Object *win, char *title, int mode, char *drawer, char *file)
  873. {   
  874.    static BOOL init[MAXASL] = { FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE };
  875.    static char *pattern[MAXASL] =  { "#?.addressbook#?", "#?.config#?", NULL, NULL, "#?.(yam|rexx)", "#?.(gif|jpg|jpeg|png|iff|ilbm)", NULL, NULL };
  876.    struct Window *truewin;
  877.    int skip = *file ? 1 : 2;
  878.    char *postext = (mode & 1)==1 ? GetStr(MSG_UT_Save) : GetStr(MSG_UT_Load);
  879.  
  880.    get(win, MUIA_Window_Window, &truewin);
  881.    if (!init[num]) { init[num] = TRUE; skip = 0; }
  882.    return MUI_AslRequestTags(G->ASLReq[num],
  883.       ASLFR_Window, truewin,
  884.       ASLFR_TitleText, title,
  885.       ASLFR_PositiveText, postext,
  886.       ASLFR_InitialFile, file,
  887.       ASLFR_DoSaveMode, (mode & 1)==1,
  888.       ASLFR_DoMultiSelect, (mode & 2)==2,
  889.       ASLFR_DrawersOnly, (mode & 4)==4,
  890.       ASLFR_DoPatterns, pattern[num] != NULL,
  891.       skip ? TAG_DONE : ASLFR_InitialDrawer, drawer,
  892.       ASLFR_InitialPattern, pattern[num] ? pattern[num] : "#?",
  893.       TAG_DONE);
  894. }
  895. ///
  896. /// OpenTempFile
  897. //  Creates or opens a temporary file
  898. struct TempFile *OpenTempFile(char *mode)
  899. {
  900.    static int count = 0;
  901.    struct TempFile *tf;
  902.    if (tf = calloc(1, sizeof(struct TempFile)))
  903.    {
  904.       char buf[SIZE_SMALL];
  905.       sprintf(buf, "YAM.%ld.tmp", ++count);
  906.       strmfp(tf->Filename, C->TempDir, buf);
  907.       if (!mode) return tf;
  908.       if (tf->FP = fopen(tf->Filename, mode)) return tf;
  909.       free(tf);
  910.    }
  911.    return NULL;
  912. }
  913. ///
  914. /// CloseTempFile
  915. //  Closes a temporary file
  916. void CloseTempFile(struct TempFile *tf)
  917. {
  918.    if (tf)
  919.    {
  920.       if (tf->FP) fclose(tf->FP);
  921.       DeleteFile(tf->Filename);
  922.       free(tf);
  923.    }
  924. }
  925. ///
  926. /// DumpClipboard
  927. //  Exports contents of clipboard unit 0 to a file
  928. #define ID_FTXT   MAKE_ID('F','T','X','T')
  929. #define ID_CHRS   MAKE_ID('C','H','R','S')
  930. BOOL DumpClipboard(FILE *out)
  931. {
  932.    struct IFFHandle *iff;
  933.    struct ContextNode *cn;
  934.    long   error, rlen;
  935.    UBYTE  readbuf[SIZE_DEFAULT];
  936.    BOOL   success = FALSE;
  937.  
  938.    if (iff = AllocIFF())
  939.    {
  940.       if (iff->iff_Stream = (ULONG)OpenClipboard(PRIMARY_CLIP))
  941.       {
  942.          InitIFFasClip(iff);
  943.          if (!OpenIFF(iff, IFFF_READ))
  944.          {
  945.             if (!StopChunk(iff, ID_FTXT, ID_CHRS)) while (TRUE)
  946.             {
  947.                error = ParseIFF(iff, IFFPARSE_SCAN);
  948.                if (error == IFFERR_EOC) continue; else if (error) break;
  949.                cn = CurrentChunk(iff);
  950.                if (cn) if (cn->cn_Type == ID_FTXT && cn->cn_ID == ID_CHRS)
  951.                {
  952.                   success = TRUE;
  953.                   while ((rlen = ReadChunkBytes(iff, readbuf, SIZE_DEFAULT)) > 0)
  954.                      fwrite(readbuf, 1, rlen, out);
  955.                }
  956.             }
  957.             CloseIFF(iff);
  958.          }
  959.          CloseClipboard((struct ClipboardHandle *)iff->iff_Stream);
  960.       }
  961.       FreeIFF(iff);
  962.    }
  963.    return success;
  964. }
  965. ///
  966. /// IsFolderDir
  967. //  Checks if a directory is used as a mail folder
  968. BOOL IsFolderDir(char *dir)
  969. {
  970.    char *filename = FilePart(dir);
  971.    int i;
  972.    for (i = 0; i < 4; i++) if (!stricmp(filename, FolderNames[i])) return TRUE;
  973.    return (BOOL)(PFExists(dir, ".fconfig") || PFExists(dir, ".index"));
  974. }
  975. ///
  976. /// PFExists
  977. //  Checks if a file exists in the specified directory
  978. BOOL PFExists(char *path, char *file)
  979. {
  980.    char fname[SIZE_PATHFILE];
  981.    strmfp(fname, path, file);
  982.    return (BOOL)(FileSize(fname) >= 0);
  983. }
  984. ///
  985. /// DeleteMailDir
  986. //  Recursively deletes a mail directory
  987. void DeleteMailDir(char *dir, BOOL isroot)
  988. {
  989.    BOOL cont, isdir;
  990.    struct FileInfoBlock *fib;
  991.    BPTR lock;
  992.    char fname[SIZE_PATHFILE], *filename, dirname[SIZE_PATHFILE];
  993.  
  994.    fib = AllocDosObject(DOS_FIB,NULL);
  995.    if (lock = Lock(dir, ACCESS_READ))
  996.    {
  997.       strcpy(dirname, dir);
  998.       Examine(lock, fib);
  999.       cont = (ExNext(lock,fib) && IoErr() != ERROR_NO_MORE_ENTRIES);
  1000.       while (cont)
  1001.       {
  1002.          strmfp(fname, dir, fib->fib_FileName);
  1003.          filename = FilePart(fname);
  1004.          isdir = fib->fib_DirEntryType > 0;
  1005.          cont = (ExNext(lock,fib) && IoErr() != ERROR_NO_MORE_ENTRIES);
  1006.          if (isroot)
  1007.          {
  1008.             if (isdir)
  1009.             {
  1010.                if (IsFolderDir(fname)) DeleteMailDir(fname, FALSE);
  1011.             }
  1012.             else
  1013.             {
  1014.                if (!stricmp(filename, ".config") || !stricmp(filename, ".glossary") || !stricmp(filename, ".addressbook")) DeleteFile(fname);
  1015.             }
  1016.          }
  1017.          else if (!isdir) if (!stricmp(filename, ".fconfig") || !stricmp(filename, ".index") || IsValidMailFile(filename)) DeleteFile(fname);
  1018.       }
  1019.       UnLock(lock);
  1020.       DeleteFile(dirname);
  1021.    }
  1022.    FreeDosObject(DOS_FIB,fib);
  1023. }
  1024. ///
  1025. /// FileToBuffer
  1026. //  Reads a complete file into memory
  1027. char *FileToBuffer(char *file)
  1028. {
  1029.    char *text;
  1030.    int size = FileSize(file);
  1031.    FILE *fh;
  1032.  
  1033.    if (size >= 0) if (text = calloc(size+1,1))
  1034.    {
  1035.       if (fh = fopen(file, "r"))
  1036.       {
  1037.          fread(text, 1, size, fh);
  1038.          fclose(fh);
  1039.          return text;
  1040.       }
  1041.       free(text);
  1042.    }
  1043.    return NULL;
  1044. }
  1045. ///
  1046.  
  1047. /*** Mail related ***/
  1048. /// MyAddTail
  1049. //  Adds a message to a message list
  1050. void MyAddTail(struct Mail **list, struct Mail *new)
  1051. {
  1052.    struct Mail *mail;
  1053.    new->Next = NULL;
  1054.    if (!*list) { *list = new; return; }
  1055.    for (mail = *list; mail->Next; mail = mail->Next);
  1056.    mail->Next = new;
  1057. }
  1058. ///
  1059. /// MyRemove
  1060. //  Removes a message from a message list
  1061. void MyRemove(struct Mail **list, struct Mail *rem)
  1062. {
  1063.    struct Mail *mail;
  1064.    if (*list == rem) { *list = rem->Next; return; }
  1065.    for (mail = *list; mail->Next; mail = mail->Next)
  1066.       if (mail->Next == rem) { mail->Next = rem->Next; return; }
  1067. }
  1068. ///
  1069. /// WhichLV
  1070. //  Returns pointer to message listview if folder is active
  1071. APTR WhichLV(struct Folder *folder)
  1072. {
  1073.    if (folder == FO_GetCurrentFolder()) return G->MA->GUI.NL_MAILS; else return NULL;
  1074. }
  1075. ///
  1076. /// CreateFilename
  1077. //  Prepends mail directory to a file name
  1078. char *CreateFilename(char *file)
  1079. {
  1080.    static char buffer[SIZE_PATHFILE];
  1081.    strmfp(buffer, G->MA_MailDir, file);
  1082.    return buffer;
  1083. }
  1084. ///
  1085. /// CreateDirectory
  1086. //  Makes a directory
  1087. BOOL CreateDirectory(char *dir)
  1088. {
  1089.    int t = FileType(dir);
  1090.    if (t == 2) return TRUE;
  1091.    if (t == 0) if (!mkdir(dir)) return TRUE;
  1092.    if (G->MA) ER_NewError(GetStr(MSG_ER_CantCreateDir), dir, NULL);
  1093.    return FALSE;
  1094. }
  1095. ///
  1096. /// GetFolderDir
  1097. //  Returns path of a folder directory
  1098. char *GetFolderDir(struct Folder *fo)
  1099. {
  1100.    static char buffer[SIZE_PATH];
  1101.    if (strchr(fo->Path, ':')) return fo->Path;
  1102.    strmfp(buffer, G->MA_MailDir, fo->Path);
  1103.    return buffer;
  1104. }
  1105. ///
  1106. /// GetMailFile
  1107. //  Returns path of a message file
  1108. char *GetMailFile(char *string, struct Folder *folder, struct Mail *mail)
  1109. {
  1110.    static char buffer[SIZE_PATHFILE];
  1111.    if (!folder && mail) folder = mail->Folder;
  1112.    if (!string) string = buffer;
  1113.    strmfp(string, (int)folder <= 0 ? C->TempDir : GetFolderDir(folder), mail->MailFile);
  1114.    return string;
  1115. }
  1116. ///
  1117. /// GetMailInfo
  1118. //  Returns location of a message
  1119. struct MailInfo *GetMailInfo(struct Mail *smail)
  1120. {
  1121.    static struct MailInfo mi;
  1122.    int i;
  1123.    
  1124.    mi.Display = smail->Folder == FO_GetCurrentFolder();
  1125.    mi.Pos = -1;
  1126.    mi.FName = GetMailFile(NULL, smail->Folder, smail);
  1127.    if (mi.Display) for (i=0; mi.Pos == -1; i++)
  1128.    {
  1129.       struct Mail *mail;
  1130.       DoMethod(G->MA->GUI.NL_MAILS, MUIM_NList_GetEntry, i, &mail);
  1131.       if (!mail) break;
  1132.       if (mail == smail) mi.Pos = i;
  1133.    }
  1134.    return &mi;
  1135. }
  1136. ///
  1137. /// GetReturnAddress
  1138. //  Gets return address of a message
  1139. struct Person *GetReturnAddress(struct Mail *mail)
  1140. {
  1141.    if (mail->ReplyTo.Address[0]) return &mail->ReplyTo;
  1142.    return &mail->From;
  1143. }
  1144. ///
  1145. /// BuildAddrName
  1146. //  Creates "Real Name" <E-mail> string
  1147. char *BuildAddrName(char *address, char *name)
  1148. {
  1149.    static char buffer[SIZE_ADDRESS+SIZE_REALNAME+4];
  1150.    char *delim;
  1151.  
  1152.    if (*name)
  1153.    {
  1154.       if (strpbrk(name, ",.()")) delim = "\""; else delim = "";
  1155.       sprintf(buffer, "%s%s%s <%s>", delim, name, delim, address);
  1156.    }
  1157.    else sprintf(buffer, "%s", address);
  1158.    return buffer;
  1159. }
  1160. char *BuildAddrName2(struct Person *pe)
  1161. {
  1162.    return BuildAddrName(pe->Address, pe->RealName);
  1163. }
  1164. ///
  1165. /// ExtractAddress
  1166. //  Extracts e-mail address and real name
  1167. void ExtractAddress(char *line, struct Person *pe)
  1168. {
  1169.    char *p = line, *ra[4], *save = malloc(strlen(line)+1);
  1170.    BOOL found = FALSE;
  1171.  
  1172.    ra[2] = ra[3] = NULL;
  1173.    strcpy(save, line);
  1174.    pe->Address[0] = pe->RealName[0] = 0;
  1175.    while (ISpace(*p)) p++;
  1176.    if (ra[0] = MyStrChr(p,'<')) if (ra[1] = MyStrChr(ra[0],'>'))
  1177.    {
  1178.       *ra[0]++ = 0; *ra[1] = 0;
  1179.       for (ra[2] = p, ra[3] = ra[0]-2; ISpace(*ra[3]) && ra[3] >= ra[2]; ra[3]--) *ra[3] = 0;
  1180.       found = TRUE;
  1181.    }
  1182.    if (!found)
  1183.    {
  1184.       for (ra[1] = ra[0] = p; *ra[1] && *ra[1] != '\t' && *ra[1] != ' ' && *ra[1] != ','; ra[1]++);
  1185.       if (ra[2] = MyStrChr(ra[1],'(')) if (ra[3] = MyStrChr(ra[2],')'))
  1186.       {
  1187.          ra[2]++; *ra[3]-- = 0;
  1188.          found = TRUE;
  1189.       }
  1190.       *ra[1] = 0;
  1191.       if (!found) ra[2] = ra[3] = "";
  1192.    }
  1193.    if (*ra[2] == '\"') ra[2]++;
  1194.    if (*ra[3] == '\"' && *(ra[3]-1) != '\\') *ra[3] = 0;
  1195.    stccpy(pe->Address , Trim(ra[0]), SIZE_ADDRESS);
  1196.    stccpy(pe->RealName, Trim(ra[2]), SIZE_REALNAME);
  1197.    strcpy(line, save);
  1198.    free(save);
  1199. }
  1200. ///
  1201. /// CompressMsgID
  1202. //  Creates a compressed version of the message ID
  1203. int CompressMsgID(char *msgid)
  1204. {
  1205.    int i = 0, compr = 0;
  1206.    char *ptr = msgid;
  1207.    while (*ptr) compr += ++i*123*(*ptr++);
  1208.    return compr;
  1209. }
  1210. ///
  1211. /// ExpandText
  1212. //  Replaces variables with values
  1213. char *ExpandText(char *src, struct ExpandTextData *etd)
  1214. {
  1215.    static char chr[2] = { 0,0 };
  1216.    char buf[SIZE_ADDRESS], *p, *p2, *dst = AllocStrBuf(SIZE_DEFAULT);
  1217.    struct DateStamp adate;
  1218.   
  1219.    for (; *src; src++)
  1220.       if (*src == '\\')
  1221.       {
  1222.          src++;
  1223.          switch (*src)
  1224.          {
  1225.             case '\\':  dst = StrBufCat(dst, "\\"); break;
  1226.             case 'n':  dst = StrBufCat(dst, "\n"); break;
  1227.          }
  1228.       }
  1229.       else if (*src == '%' && etd)
  1230.       {
  1231.          if (!etd->OM_Date) etd->OM_Date = DateStamp(&adate);
  1232.          src++;
  1233.          switch (*src)
  1234.          {
  1235.             case 'n': dst = StrBufCat(dst, etd->OS_Name); break;
  1236.             case 'f': stccpy(buf, etd->OS_Name, SIZE_ADDRESS);
  1237.                       if (p = strchr(buf, ',')) p = Trim(++p);
  1238.                       else { for (p = buf; *p && *p != ' '; p++); *p = 0; p = buf; }
  1239.                       dst = StrBufCat(dst, p); break;
  1240.             case 's': dst = StrBufCat(dst, etd->OM_Subject); break;
  1241.             case 'e': dst = StrBufCat(dst, etd->OS_Address); break;
  1242.             case 'd': dst = StrBufCat(dst, DateStamp2String(etd->OM_Date, DSS_DATE)); break;
  1243.             case 't': dst = StrBufCat(dst, DateStamp2String(etd->OM_Date, DSS_TIME)); break;
  1244.             case 'w': dst = StrBufCat(dst, DateStamp2String(etd->OM_Date, DSS_WEEKDAY)); break;
  1245.             case 'm': dst = StrBufCat(dst, etd->OM_MessageID); break;
  1246.             case 'r': dst = StrBufCat(dst, etd->R_Name); break;
  1247.             case 'v': strcpy(buf, etd->R_Name);
  1248.                       if (p = strchr(buf, ',')) p = Trim(++p);
  1249.                       else { for (p = buf; *p && *p != ' '; p++); *p = 0; p = buf; }
  1250.                       dst = StrBufCat(dst, p); break;
  1251.             case 'a': dst = StrBufCat(dst, etd->R_Address); break;
  1252.             case 'i': strcpy(buf, etd->OS_Name);
  1253.                       for (p = p2 = &buf[1]; *p; p++)
  1254.                          if (*p == ' ' && p[1] && p[1] != ' ') *p2++ = *++p;
  1255.                       *p2 = 0;
  1256.                       dst = StrBufCat(dst, buf); break;
  1257.             case 'j': strcpy(buf, etd->OS_Name);
  1258.                       for (p2 = &buf[1], p = &buf[strlen(buf)-1]; p > p2; p--)
  1259.                          if (p[-1] == ' ') { *p2++ = *p; break; }
  1260.                       *p2 = 0;
  1261.                       dst = StrBufCat(dst, buf); break;
  1262.             case 'h': if (p = FileToBuffer(etd->HeaderFile))
  1263.                       {
  1264.                          dst = StrBufCat(dst, p); free(p);
  1265.                       }
  1266.                       break;
  1267.         }
  1268.       }
  1269.       else
  1270.       {
  1271.          chr[0] = *src;
  1272.          dst = StrBufCat(dst, chr);
  1273.       }
  1274.    return dst;
  1275. }              
  1276. ///
  1277. /// DescribeCT
  1278. //  Returns description of a content type
  1279. char *DescribeCT(char *ct)
  1280. {
  1281.    int i;
  1282.    for (i = 0; i < MAXCTYPE; i++)
  1283.       if (!stricmp(ct, ContType[i])) { ct = GetStr(ContTypeDesc[i]); break; }
  1284.    return ct;
  1285. }
  1286. ///
  1287. /// DateStamp2String
  1288. //  Converts a datestamp to a string
  1289. char *DateStamp2String(struct DateStamp *date, int mode)
  1290. {
  1291.    static char resstr[32];
  1292.    char *s, datestr[16], timestr[16], daystr[16];
  1293.    struct DateTime dt;
  1294.    struct DateStamp dsnow;
  1295.    long beat;
  1296.  
  1297.    if (!date) date = DateStamp(&dsnow);
  1298.    clear(&dt, sizeof(struct DateTime));
  1299.    dt.dat_Stamp   = *date;
  1300.    dt.dat_Format  = FORMAT_DOS;
  1301.    if (mode == DSS_USDATETIME || mode == DSS_UNIXDATE) dt.dat_Format = FORMAT_USA;
  1302.    dt.dat_StrDate = (STRPTR)datestr;
  1303.    dt.dat_StrTime = (STRPTR)timestr;
  1304.    dt.dat_StrDay  = (STRPTR)daystr;
  1305.    DateToStr(&dt);
  1306.    beat = (((date->ds_Minute-60*C->TimeZone+(C->DaylightSaving?0:60)+1440)%1440)*1000)/1440;
  1307.    s = Trim(datestr);
  1308.    switch (mode)
  1309.    {
  1310.       case DSS_UNIXDATE:   sprintf(resstr, "%s %s %02ld %s %ld\n", wdays[dt.dat_Stamp.ds_Days%7], months[atoi(s)-1], atoi(&s[3]), timestr, 1900+atoi(&s[6])); break;
  1311.       case DSS_DATETIME:   timestr[5] = 0; sprintf(resstr, "%s %s", s, timestr); break;
  1312.       case DSS_DATEBEAT:   sprintf(resstr, "%s @%03ld", s, beat); break;
  1313.       case DSS_USDATETIME: sprintf(resstr, "%s %s", s, timestr); break;
  1314.       case DSS_WEEKDAY:    strcpy(resstr, daystr); break;
  1315.       case DSS_DATE:       strcpy(resstr, s); break;
  1316.       case DSS_TIME:       strcpy(resstr, timestr); break;
  1317.       case DSS_BEAT:       sprintf(resstr, "@%03ld", beat); break;
  1318.    }
  1319.    return resstr;
  1320. }
  1321. ///
  1322. /// DateStamp2Long
  1323. // Converts a datestamp to a numeric value
  1324. long DateStamp2Long(struct DateStamp *date)
  1325. {
  1326.    char *s, datestr[16];
  1327.    struct DateStamp dsnow;
  1328.    struct DateTime dt;
  1329.    int y;
  1330.  
  1331.    if (!date) date = DateStamp(&dsnow);
  1332.    clear(&dt, sizeof(struct DateTime));
  1333.    dt.dat_Stamp   = *date;
  1334.    dt.dat_Format  = FORMAT_USA;
  1335.    dt.dat_StrDate = (STRPTR)datestr;
  1336.    DateToStr(&dt); s = Trim(datestr);
  1337.    if ((y = atoi(&s[6])) < 78) y += 100;
  1338.    return (100*atoi(&s[3])+atoi(s))*10000+y+1900;
  1339. }
  1340. ///
  1341. /// GetTZ
  1342. //  Gets the locale time zone
  1343. char *GetTZ(void)
  1344. {
  1345.    static char tzone[SIZE_SMALL];
  1346.    if (GetVar("YAM_TZ", tzone, SIZE_SMALL, 0) < 0)
  1347.    {
  1348.       int tz = C->TimeZone + (C->DaylightSaving ? 1 : 0);
  1349.       if (C->TimeZone >= 0) sprintf(tzone, "+%02d00", tz);
  1350.       else                 sprintf(tzone, "-%02d00", -tz);
  1351.    }
  1352.    return tzone;
  1353. }
  1354. ///
  1355. /// TZtoMinutes
  1356. //  Converts time zone into a numeric offset
  1357. int TZtoMinutes(char *tzone)
  1358. {
  1359.    int i, tzcorr = 0;
  1360.    static struct TimeZones { char *TZname; int TZcorr; }
  1361.    tzones[] = { "sst",-1100, "pst",-800, "mst",-700, "pdt",-700, "cst",-600,
  1362.       "mdt",-600, "est",-500, "ast",-500, "edt",-400, "wgt",-300, "wgst",-200,
  1363.       "aat",-100, "egt",-100, "egst",0, "gmt",0, "utc",0, "wat",0, "wet",0,
  1364.       "bst",100, "cat",100, "cet",100, "met",100, "west",100, "cest",200,
  1365.       "met dst",200, "eet",200, "ist",200, "sat",200, "ast",300, "eat",300,
  1366.       "eest",300, "idt",300, "msk",300, "adt",400, "msd",300, "gst",400,
  1367.       "smt",400, "ict",400, "hkt",800, "wst",800, "jst",900, "kst",1000,
  1368.       "nzst",1200, "nzdt",1300, NULL,0 };
  1369.  
  1370.    if (tzone[0] == '+') tzcorr = atoi(&tzone[1]);
  1371.    else if (tzone[0] == '-') tzcorr = -atoi(&tzone[1]);
  1372.    else for (i = 0; tzones[i].TZname; i++)
  1373.       if (!strnicmp(tzones[i].TZname, tzone, strlen(tzones[i].TZname)))
  1374.       { tzcorr = tzones[i].TZcorr; break; }
  1375.    tzcorr = (tzcorr/100)*60 + (tzcorr%100);
  1376.    return tzcorr;
  1377. }
  1378. ///
  1379. /// ScanDate
  1380. //  Converts textual date header into datestamp format
  1381. struct DateStamp *ScanDate(char *date)
  1382. {
  1383.    int count = 0, day = 0, mon = 0, year = 0, hour = 0, min = 0, sec = 0;
  1384.    char *tzone = "", *p, tdate[SIZE_SMALL], ttime[SIZE_SMALL];
  1385.    static struct DateTime dt;
  1386.    struct DateStamp *ds = &dt.dat_Stamp;
  1387.  
  1388.    if (p = strchr(date, ',')) p++; else p = date;
  1389.    while (*p && ISpace(*p)) p++;
  1390.    while (p = strtok(p, " \t"))
  1391.    {
  1392.       switch (count)
  1393.       {
  1394.          case 0: if (!isdigit(*p)) return NULL;
  1395.                  if ((day = atoi(p)) > 31) return NULL;
  1396.                  break;
  1397.          case 1: for (mon = 1; mon <= 12; mon++) if (!strnicmp(p, months[mon-1], 3)) break;
  1398.                  if (mon > 12) return NULL;
  1399.                  break;
  1400.          case 2: year = atoi(p);
  1401.                  break;
  1402.          case 3: if (sscanf(p, "%d:%d:%d", &hour, &min, &sec) == 3) ;
  1403.                  else if (sscanf (p, "%d:%d", &hour, &min) == 2) sec = 0;
  1404.                  else return NULL;
  1405.                  break;
  1406.          case 4: while (*p && (ISpace(*p) || *p == '(')) p++;
  1407.                  tzone = p;
  1408.                  break;
  1409.       }
  1410.       count++; p = NULL;
  1411.    }
  1412.    sprintf(tdate, "%02ld-%02ld-%02ld", mon, day, year%100);
  1413.    sprintf(ttime, "%02ld:%02ld:%02ld", hour, min, sec);
  1414.    dt.dat_Format  = FORMAT_USA;
  1415.    dt.dat_Flags   = 0;
  1416.    dt.dat_StrDate = (STRPTR)tdate;
  1417.    dt.dat_StrTime = (STRPTR)ttime;
  1418.    StrToDate(&dt);
  1419.    ds->ds_Minute -= TZtoMinutes(tzone);
  1420.    ds->ds_Minute += TZtoMinutes(GetTZ());
  1421.    while (ds->ds_Minute < 0)  { ds->ds_Minute += 1440; ds->ds_Days--; }
  1422.    while (ds->ds_Minute >= 1440) { ds->ds_Minute -= 1440; ds->ds_Days++; }
  1423.    return ds;
  1424. }
  1425. ///
  1426. /// FormatSize
  1427. //  Displays large numbers using group separators
  1428. void FormatSize(int size, char *buffer)
  1429. {
  1430.    char *gs = G->Locale ? (char *)G->Locale->loc_GroupSeparator : ".";
  1431.    char *p = &buffer[strlen(buffer)];
  1432.    if (size >= 1000000) sprintf(p, "%d%s%03d%s%03d", size/1000000, gs, (size%1000000)/1000, gs, size%1000);
  1433.    if (size >= 1000 && size < 1000000) sprintf(p, "%d%s%03d", size/1000, gs, size%1000);
  1434.    if (size < 1000) sprintf(p, "%d", size);
  1435. }
  1436. ///
  1437. /// MailExists
  1438. //  Checks if a message still exists
  1439. BOOL MailExists(struct Mail *mailptr, struct Folder *folder)
  1440. {
  1441.    struct Mail *work;
  1442.    if (Virtual(mailptr)) return TRUE;
  1443.    if (!folder) folder = mailptr->Folder;
  1444.    for (work = folder->Messages; work; work = work->Next) if (work == mailptr) return TRUE;
  1445.    return FALSE;
  1446. }
  1447. ///
  1448. /// SelectMessage
  1449. //  Activates a message in the message listview
  1450. int SelectMessage(struct Mail *mail)
  1451. {
  1452.    struct MailInfo *mi;
  1453.    if (mail->Folder != FO_GetCurrentFolder()) MA_ChangeFolder(mail->Folder);
  1454.    mi = GetMailInfo(mail);
  1455.    if (mi->Pos >= 0) set(G->MA->GUI.NL_MAILS, MUIA_NList_Active, mi->Pos);
  1456.    return mi->Pos;
  1457. }
  1458. ///
  1459. /// DisplayMailList
  1460. //  Lists folder contents in the message listview
  1461. void DisplayMailList(struct Folder *fo, APTR lv)
  1462. {
  1463.    struct Mail *work, **array;
  1464.  
  1465.    if (array = (struct Mail **)calloc(fo->Total+1,sizeof(struct Mail *)))
  1466.    {
  1467.       int i = 0;
  1468.       Busy(GetStr(MSG_BusyDisplayingList), "", 0, 0);
  1469.       for (work = fo->Messages; work; work = work->Next) array[i++] = work;
  1470.       set(lv, MUIA_NList_Quiet, TRUE);
  1471.       DoMethod(lv, MUIM_NList_Clear);
  1472.       DoMethod(lv, MUIM_NList_Insert, array, fo->Total, MUIV_NList_Insert_Sorted);
  1473.       set(lv, MUIA_NList_Quiet, FALSE);
  1474.       free(array);
  1475.       BusyEnd;
  1476.    }
  1477. }
  1478. ///
  1479. /// AddMailToList
  1480. //  Adds a message to a folder
  1481. struct Mail *AddMailToList(struct Mail *mail, struct Folder *folder)
  1482. {
  1483.    struct Mail *new = malloc(sizeof(struct Mail));
  1484.    if (new) 
  1485.    {
  1486.       *new = *mail;
  1487.       new->Folder = folder;
  1488.       MyAddTail(&(folder->Messages), new);
  1489.       folder->Total++;
  1490.       folder->Size += mail->Size;
  1491.       if (mail->Status == STATUS_NEW) { folder->New++; folder->Unread++; }
  1492.       if (mail->Status == STATUS_UNR) folder->Unread++;
  1493.       MA_ExpireIndex(folder);
  1494.    }
  1495.    return new;
  1496. }
  1497. ///
  1498. /// RemoveMailFromList
  1499. //  Removes a message from a folder
  1500. void RemoveMailFromList(struct Mail *mail)
  1501. {
  1502.    struct Folder *folder = mail->Folder;
  1503.    folder->Total--;
  1504.    folder->Size -= mail->Size;
  1505.    if (mail->Status == STATUS_NEW) { folder->New--; folder->Unread--; }
  1506.    if (mail->Status == STATUS_UNR) folder->Unread--;
  1507.    MA_ExpireIndex(folder);
  1508.    MyRemove(&(folder->Messages), mail);
  1509.    free(mail);
  1510. }
  1511. ///
  1512. /// ClearMailList
  1513. //  Removes all messages from a folder
  1514. void ClearMailList(struct Folder *folder, BOOL resetstats)
  1515. {
  1516.    struct Mail *work, *next;
  1517.    for (work = folder->Messages; work; work = next)
  1518.    {
  1519.       next = work->Next;
  1520.       free(work);
  1521.    }
  1522.    if (resetstats) folder->Total = folder->New = folder->Unread = folder->Size = 0;
  1523.    folder->Messages = NULL;
  1524. }
  1525. ///
  1526. /// GetPackMethod
  1527. //  Returns packer type and efficiency
  1528. BOOL GetPackMethod(int xpktype, char **method, int *eff)
  1529. {
  1530.    if (xpktype == 2) { *method = C->XPKPack; *eff = C->XPKPackEff; return TRUE; }
  1531.    if (xpktype == 3) { *method = C->XPKPackEncrypt; *eff = C->XPKPackEncryptEff; return TRUE; }
  1532.    return FALSE;
  1533. }
  1534. ///
  1535. /// CompressMailFile
  1536. //  Shrinks a message file
  1537. BOOL CompressMailFile(char *src, char *dst, char *passwd, char *method, int eff)
  1538. {
  1539.    int err;
  1540.    if (!XpkBase) return FALSE;
  1541.    err = XpkPackTags(XPK_InName, src, XPK_OutName, dst, XPK_Password, passwd, XPK_PackMethod, method, XPK_PackMode, eff, TAG_DONE);
  1542.    return (BOOL)!err;
  1543. }
  1544. ///
  1545. /// UncompressMailFile
  1546. //  Expands a compressed message file
  1547. BOOL UncompressMailFile(char *src, char *dst, char *passwd)
  1548. {
  1549.    if (!XpkBase) return FALSE;
  1550.    return (BOOL)(!XpkUnpackTags(XPK_InName, src, XPK_OutName, dst, XPK_Password, passwd, TAG_DONE));
  1551. }
  1552. ///
  1553. /// TransferMailFile
  1554. //  Copies or moves a message file, handles compression
  1555. BOOL TransferMailFile(BOOL copyit, struct Mail *mail, struct Folder *dstfolder)
  1556. {
  1557.    char *pmeth, srcbuf[SIZE_PATHFILE], dstbuf[SIZE_PATHFILE];
  1558.    struct Folder *srcfolder = mail->Folder;
  1559.    int peff = 0;
  1560.    int srcxpk = srcfolder->XPKType, dstxpk = dstfolder->XPKType;
  1561.    char *srcpw = srcfolder->Password, *dstpw = dstfolder->Password;
  1562.    BOOL one2one, needuncomp, needcomp, done = FALSE, success = FALSE;
  1563.  
  1564.    if (!MA_GetIndex(srcfolder)) return FALSE;
  1565.    if (!MA_GetIndex(dstfolder)) return FALSE;
  1566.    one2one = (srcxpk == dstxpk) && (srcxpk != 3);
  1567.    needuncomp = srcxpk > 1;
  1568.    needcomp   = dstxpk > 1;
  1569.    GetPackMethod(dstxpk, &pmeth, &peff);
  1570.    GetMailFile(srcbuf, srcfolder, mail);
  1571.    strcpy(dstbuf, MA_NewMailFile(dstfolder, mail->MailFile, atoi(mail->MailFile)));
  1572.    if (one2one && !copyit) if (done = RenameFile(srcbuf, dstbuf)) success = TRUE;
  1573.    if (!done)
  1574.    {
  1575.       if (needuncomp)
  1576.          if (needcomp)
  1577.             if (one2one)
  1578.                success = CopyFile(dstbuf, 0, srcbuf, 0);
  1579.             else
  1580.             {
  1581.                struct TempFile *tf = OpenTempFile(NULL);
  1582.                if (UncompressMailFile(srcbuf, tf->Filename, srcpw))
  1583.                   success = CompressMailFile(tf->Filename, dstbuf, dstpw, pmeth, peff);
  1584.                CloseTempFile(tf);
  1585.             }
  1586.          else
  1587.             success = UncompressMailFile(srcbuf, dstbuf, srcpw);
  1588.       else
  1589.          if (needcomp)
  1590.             success = CompressMailFile(srcbuf, dstbuf, dstpw, pmeth, peff);
  1591.          else
  1592.             success = CopyFile(dstbuf, 0, srcbuf, 0);
  1593.       if (success && !copyit) DeleteFile(srcbuf);
  1594.       if (success) SetComment(dstbuf, Status[mail->Status]);
  1595.    }
  1596.    return success;
  1597. }
  1598. ///
  1599. /// RepackMailFile
  1600. //  (Re/Un)Compresses a message file
  1601. BOOL RepackMailFile(struct Mail *mail, int dstxpk, char *passwd)
  1602. {
  1603.    char *pmeth = NULL, srcbuf[SIZE_PATHFILE], dstbuf[SIZE_PATHFILE];
  1604.    struct Folder *folder = mail->Folder;
  1605.    int peff = 0, srcxpk = folder->XPKType;
  1606.    BOOL success = TRUE;
  1607.  
  1608.    MA_GetIndex(folder);
  1609.    GetMailFile(srcbuf, folder, mail);
  1610.    GetPackMethod(dstxpk, &pmeth, &peff);
  1611.    sprintf(dstbuf, "%s.tmp", srcbuf);
  1612.    switch (4*srcxpk+dstxpk)
  1613.    {
  1614.       case  0: case  5: case 10: case 15:
  1615.       case  1: case  4:                   return TRUE;
  1616.       case  2: case  3: case  6: case  7: if (success = CompressMailFile(srcbuf, dstbuf, passwd, pmeth, peff))
  1617.                                           {
  1618.                                              DeleteFile(srcbuf);
  1619.                                              success = RenameFile(dstbuf, srcbuf);
  1620.                                           }
  1621.                                           break;
  1622.       case  8: case 9:  case 12: case 13: if (success = UncompressMailFile(srcbuf, dstbuf, folder->Password))
  1623.                                           {
  1624.                                              DeleteFile(srcbuf);
  1625.                                              success = RenameFile(dstbuf, srcbuf);
  1626.                                           }
  1627.                                           break;
  1628.       case 11: case 14:                   if (success = UncompressMailFile(srcbuf, dstbuf, folder->Password))
  1629.                                           {
  1630.                                              success = CompressMailFile(dstbuf, srcbuf, passwd, pmeth, peff);
  1631.                                              DeleteFile(dstbuf);
  1632.                                           }
  1633.                                           break;
  1634.    }
  1635.    MA_SetMailStatus(mail, mail->Status);
  1636.    return success;
  1637. }
  1638. ///
  1639. /// DoPack
  1640. //  Compresses a file
  1641. BOOL DoPack(char *file, char *newfile, struct Folder *folder)
  1642. {
  1643.    char *pmeth = NULL;
  1644.    int peff = 0;
  1645.  
  1646.    GetPackMethod(folder->XPKType, &pmeth, &peff);
  1647.    if (!CompressMailFile(file, newfile, folder->Password, pmeth, peff)) return FALSE;
  1648.    DeleteFile(file);
  1649.    return TRUE;
  1650. }
  1651. ///
  1652. /// StartUnpack
  1653. //  Unpacks a file to a temporary file
  1654. char *StartUnpack(char *file, char *newfile, struct Folder *folder)
  1655. {
  1656.    FILE *fh;
  1657.    BOOL xpk = FALSE;
  1658.    if (fh = fopen(file, "r"))
  1659.    {
  1660.       if (fgetc(fh) == 'X') if (fgetc(fh) == 'P') if (fgetc(fh) == 'K') xpk = TRUE;
  1661.       fclose(fh);
  1662.       if (xpk)
  1663.       {
  1664.          char nfile[SIZE_FILE];
  1665.          stcgfn(nfile, file); sprintf(&nfile[strlen(nfile)], "_%08lx.unp", folder);
  1666.          strmfp(newfile, C->TempDir, nfile);
  1667.          if (FileSize(newfile) < 0) if (!UncompressMailFile(file, newfile, folder ? folder->Password : "")) return NULL;
  1668.       }
  1669.       else strcpy(newfile, file);
  1670.       return newfile;
  1671.    }
  1672.    return NULL;
  1673. }
  1674. ///
  1675. /// FinishUnpack
  1676. //  Deletes temporary unpacked file
  1677. void FinishUnpack(char *file)
  1678. {
  1679.    if (strstr(file, ".unp")) DeleteFile(file);
  1680. }
  1681. ///
  1682. /// IsValidMailFile
  1683. //  Checks if a message file name is valid
  1684. BOOL IsValidMailFile(char *fname)
  1685. {
  1686.    int l = strlen(fname);
  1687.    if (l < 7 || l > 10 || fname[5] != '.') return FALSE;
  1688.    while (--l >= 0) if (l != 5 && !isdigit(fname[l])) return FALSE;
  1689.    return TRUE;
  1690. }
  1691. ///
  1692.  
  1693. /*** Editor related ***/
  1694. /// EditorToFile
  1695. //  Saves contents of a texteditor object to a file
  1696. BOOL EditorToFile(struct Object *editor, char *file, struct TranslationTable *tt)
  1697. {
  1698.    char *text;
  1699.    UBYTE *p;
  1700.    FILE *fh;
  1701.  
  1702.    if (!(fh = fopen(file, "w"))) return FALSE;
  1703.    text = (char *)DoMethod((Object *)editor, MUIM_TextEditor_ExportText);
  1704.    if (tt) for (p = text; *p; ++p) *p = tt->Table[*p];
  1705.    fputs(text, fh);
  1706.    fclose(fh);
  1707.    FreeVec(text);
  1708.    return TRUE;
  1709. }
  1710. ///
  1711. /// FileToEditor
  1712. //  Loads a file into a texteditor object
  1713. BOOL FileToEditor(char *file, struct Object *editor)
  1714. {
  1715.    char *text = FileToBuffer(file);
  1716.    if (!text) return FALSE;
  1717.    set(editor, MUIA_TextEditor_Contents, text);
  1718.    free(text);
  1719.    return TRUE;
  1720. }
  1721. ///
  1722.  
  1723. /*** Hooks ***/
  1724. /// GeneralDesFunc
  1725. //  General purpose destruction hook
  1726. SAVEDS ASM long GeneralDesFunc(REG(a1) void *entry)
  1727. {
  1728.    free(entry);
  1729.    return 0;
  1730. }
  1731. MakeHook(GeneralDesHook, GeneralDesFunc);
  1732. ///
  1733. /// PO_SetPublicKey
  1734. //  Copies public PGP key from list to string gadget
  1735. SAVEDS ASM void PO_SetPublicKey(REG(a1) APTR string, REG(a2) APTR pop)
  1736. {
  1737.    char *var, buf[SIZE_SMALL];
  1738.  
  1739.    DoMethod(pop, MUIM_List_GetEntry, MUIV_List_GetEntry_Active, &var);
  1740.    if (var)
  1741.    {
  1742.       strcpy(buf, "0x");
  1743.       strncat(buf, var, 8);
  1744.       setstring(string, buf);
  1745.    }
  1746. }
  1747. MakeHook(PO_SetPublicKeyHook, PO_SetPublicKey);
  1748. ///
  1749. /// PO_ListPublicKeys
  1750. //  Lists keys of public PGP keyring in a popup window
  1751. SAVEDS ASM long PO_ListPublicKeys(REG(a1) APTR string, REG(a2) APTR pop)
  1752. {  
  1753.    BOOL secret;
  1754.    char buf[SIZE_LARGE], *str, p;
  1755.    int retc, keys = 0;
  1756.    FILE *fp;
  1757.  
  1758.    get(pop, MUIA_UserData, &str); secret = (BOOL)str;
  1759.    if (G->PGPVersion == 5)
  1760.       retc = PGPCommand("pgpk", "-l +language=us", KEEPLOG);
  1761.    else
  1762.    {
  1763.       strcpy(buf, "-kv  ");
  1764.       if (secret)
  1765.       {
  1766.          GetVar("PGPPATH", &buf[4], SIZE_DEFAULT, 0);
  1767.          if ((p = buf[strlen(buf)-1]) != ':' && p != '/') strcat(buf, "/");
  1768.          strcat(buf, "secring.pgp");
  1769.       }
  1770.       retc = PGPCommand("pgp", buf, KEEPLOG);
  1771.    }
  1772.    if (!retc) if (fp = fopen(PGPLOGFILE, "r"))
  1773.    {
  1774.       get(string, MUIA_String_Contents, &str);
  1775.       DoMethod(pop, MUIM_List_Clear);
  1776.       while (GetLine(fp, buf, SIZE_LARGE))
  1777.       {
  1778.          char entry[SIZE_DEFAULT];
  1779.          clear(entry, SIZE_DEFAULT);
  1780.          if (G->PGPVersion == 5)
  1781.          {
  1782.             if (!strncmp(buf, "sec", 3) || (!strncmp(&buf[1], "ub", 2) && !secret))
  1783.             {
  1784.                memcpy(entry, &buf[12], 8);
  1785.                while (GetLine(fp, buf, SIZE_LARGE))
  1786.                   if (!strncmp(buf, "uid", 3)) { strncat(entry, &buf[4], SIZE_DEFAULT-8); break; }
  1787.              }
  1788.          }
  1789.          else
  1790.          {
  1791.             if (buf[9] == '/' && buf[23] == '/')
  1792.             {
  1793.                memcpy(entry, &buf[10], 8);
  1794.                strncat(entry, &buf[29], SIZE_DEFAULT-8);
  1795.             }
  1796.          }
  1797.          if (*entry)
  1798.          {
  1799.             DoMethod(pop, MUIM_List_InsertSingle, entry, MUIV_List_Insert_Bottom);
  1800.             if (!strncmp(entry, str, 8)) set(pop, MUIA_List_Active, keys);
  1801.             keys++;
  1802.          }
  1803.       }
  1804.       fclose(fp);
  1805.       DeleteFile(PGPLOGFILE);
  1806.    }
  1807.    if (!keys) ER_NewError(GetStr(MSG_ER_NoPublicKeys), "", "");
  1808.    return keys > 0;
  1809. }
  1810. MakeHook(PO_ListPublicKeysHook, PO_ListPublicKeys);
  1811. ///
  1812.  
  1813. /*** MUI related ***/
  1814. /// ShortCut
  1815. //  Finds keyboard shortcut in text label
  1816. char ShortCut(char *label)
  1817. {
  1818.    char *ptr = strchr(label, '_'), sc;
  1819.  
  1820.    if (!ptr) return 0;
  1821.    sc = ToLower(*++ptr);
  1822.    return sc;
  1823. }
  1824. ///
  1825. /// RemoveCut
  1826. //  Removes shortcut character from text label
  1827. char *RemoveCut(char *label)
  1828. {
  1829.    static char lab[SIZE_DEFAULT], *p;
  1830.  
  1831.    for (p = lab; *label; label++) if (*label != '_') *p++ = *label;
  1832.    *p = 0;
  1833.    return lab;
  1834. }
  1835. ///
  1836. /// SetHelp
  1837. //  Sets bubble help of a MUI object
  1838. void SetHelp(APTR object, APTR strnum)
  1839. {
  1840.    set(object, MUIA_ShortHelp, GetStr(strnum));
  1841. }
  1842. ///
  1843. /// MakeCycle
  1844. //  Creates a MUI cycle object
  1845. Object *MakeCycle(char **labels, char *label)
  1846. {
  1847.    Object *obj = KeyCycle(labels, ShortCut(label));
  1848.    if (obj) set(obj, MUIA_CycleChain, 1);
  1849.    return obj;
  1850. }
  1851. ///
  1852. /// MakeButton
  1853. //  Creates a MUI button
  1854. Object *MakeButton(char *txt)
  1855. {
  1856.    Object *obj = MUI_MakeObject(MUIO_Button,txt);
  1857.    if (obj) set(obj, MUIA_CycleChain, 1);
  1858.    return obj;
  1859. }
  1860. ///
  1861. /// MakeCheck
  1862. //  Creates a MUI checkmark object
  1863. Object *MakeCheck(char *label)
  1864. {
  1865.    return 
  1866.    ImageObject,
  1867.       ImageButtonFrame,
  1868.       MUIA_InputMode   , MUIV_InputMode_Toggle,
  1869.       MUIA_Image_Spec  , MUII_CheckMark, 
  1870.       MUIA_Background  , MUII_ButtonBack, 
  1871.       MUIA_ShowSelState, FALSE,
  1872.       MUIA_ControlChar , ShortCut(label),
  1873.       MUIA_CycleChain  , 1,
  1874.    End;
  1875. }
  1876. ///
  1877. /// MakeCheckGroup
  1878. //  Creates a labelled MUI checkmark object
  1879. Object *MakeCheckGroup(Object **check, char *label)
  1880. {
  1881.    return 
  1882.    HGroup,
  1883.       Child, *check = MakeCheck(label),
  1884.       Child, Label(label),
  1885.       Child, HSpace(0),
  1886.    End;
  1887. }
  1888. ///
  1889. /// MakeString
  1890. //  Creates a MUI string object
  1891. Object *MakeString(int maxlen, char *label)
  1892. {
  1893.    return BetterStringObject,
  1894.       StringFrame,
  1895.       MUIA_String_MaxLen     , maxlen,
  1896.       MUIA_String_AdvanceOnCR, TRUE,
  1897.       MUIA_ControlChar       , ShortCut(label),
  1898.       MUIA_CycleChain        , 1,
  1899.    End;
  1900. }
  1901. ///
  1902. /// MakePassString
  1903. //  Creates a MUI string object with hidden text
  1904. Object *MakePassString(char *label)
  1905. {
  1906.    return BetterStringObject,
  1907.       StringFrame,
  1908.       MUIA_String_MaxLen     , SIZE_PASSWORD,
  1909.       MUIA_String_Secret     , TRUE,
  1910.       MUIA_String_AdvanceOnCR, TRUE,
  1911.       MUIA_ControlChar       , ShortCut(label),
  1912.       MUIA_CycleChain        , 1,
  1913.    End;
  1914. }
  1915. ///
  1916. /// MakeInteger
  1917. //  Creates a MUI string object for numeric input
  1918. Object *MakeInteger(int maxlen, char *label)
  1919. {
  1920.    Object *str = MakeString(maxlen, label);
  1921.    if (str)
  1922.    {
  1923.       set(str, MUIA_String_Integer, 0);
  1924.       set(str, MUIA_String_Accept, "0123456789");
  1925.    }
  1926.    return str;
  1927. }
  1928. ///
  1929. /// MakePGPKeyList
  1930. //  Creates a PGP id popup list
  1931. Object *MakePGPKeyList(APTR *st, BOOL secret, char *label)
  1932. {
  1933.    APTR po, lv;
  1934.  
  1935.    if (po = PopobjectObject,
  1936.          MUIA_Popstring_String, *st = MakeString(SIZE_DEFAULT, label),
  1937.          MUIA_Popstring_Button, PopButton(MUII_PopUp),
  1938.          MUIA_Popobject_StrObjHook, &PO_ListPublicKeysHook,
  1939.          MUIA_Popobject_ObjStrHook, &PO_SetPublicKeyHook,
  1940.          MUIA_Popobject_WindowHook, &PO_WindowHook,
  1941.          MUIA_Popobject_Object, lv = ListviewObject,
  1942.             MUIA_UserData, secret,
  1943.             MUIA_Listview_List, ListObject,
  1944.                InputListFrame,
  1945.                MUIA_List_AdjustWidth, TRUE,
  1946.                MUIA_List_ConstructHook, MUIV_List_ConstructHook_String,
  1947.                MUIA_List_DestructHook, MUIV_List_DestructHook_String,
  1948.             End,
  1949.          End,
  1950.       End)
  1951.       DoMethod(lv, MUIM_Notify, MUIA_Listview_DoubleClick, TRUE, po, 2, MUIM_Popstring_Close, TRUE);
  1952.    return po;
  1953. }
  1954. ///
  1955. /// MakePicture
  1956. //  Creates a MUI image object that uses image datatypes
  1957. Object *MakePicture(char *fname)
  1958. {
  1959.    return G->DtpicSupported ?
  1960.       MUI_NewObject("Dtpic.mui", MUIA_Dtpic_Name, fname, End :
  1961.       NewObject(CL_BodyChunk->mcc_Class,NULL, MUIA_Bodychunk_File, fname, End;
  1962. }
  1963. ///
  1964. /// MakeStatusFlag
  1965. //  Creates a MUI object for status images
  1966. Object *MakeStatusFlag(char *fname)
  1967. {
  1968.    return NewObject(CL_BodyChunk->mcc_Class,NULL,
  1969.       MUIA_Bodychunk_File, fname,
  1970.       MUIA_Bodychunk_UseOld, TRUE,
  1971.       MUIA_Bitmap_Transparent, 0,
  1972.    End;
  1973. }
  1974. ///
  1975. /// MakeNumeric
  1976. //  Creates a MUI numeric slider
  1977. Object *MakeNumeric(int min, int max, BOOL percent)
  1978. {
  1979.    return
  1980.    NumericbuttonObject,
  1981.       MUIA_Numeric_Min, min,
  1982.       MUIA_Numeric_Max, max,
  1983.       MUIA_Numeric_Format, percent ? "%ld%%" : "%ld",
  1984.       MUIA_CycleChain, 1,
  1985.    End;
  1986. }
  1987. ///
  1988. /// SetupToolbar
  1989. //  Initializes a single button in a MUI toolbar object
  1990. void SetupToolbar(struct MUIP_Toolbar_Description *tb, char *label, char *help, UWORD flags)
  1991. {
  1992.    tb->Type = label ? (*label ? TDT_BUTTON : TDT_SPACE) : TDT_END;
  1993.    tb->Flags = flags;
  1994.    tb->ToolText = tb->Type == TDT_BUTTON ? label : NULL;
  1995.    tb->HelpString = help;
  1996.    tb->MutualExclude = 0; tb->Key = 0;
  1997. }
  1998. ///
  1999. /// SetupMenu
  2000. //  Initializes a MUI menu item
  2001. void SetupMenu(int type, struct NewMenu *menu, char *label, char *shortcut, int id)
  2002. {
  2003.    menu->nm_Type = type;
  2004.    menu->nm_Label = (STRPTR)label;
  2005.    menu->nm_CommKey = (STRPTR)shortcut;
  2006.    menu->nm_Flags = 0;
  2007.    menu->nm_MutualExclude = 0;
  2008.    menu->nm_UserData = (APTR)id;
  2009. }
  2010. ///
  2011. /// DoSuperNew
  2012. //  Calls parent NEW method within a subclass
  2013. ULONG DoSuperNew(struct IClass *cl, Object *obj, ULONG tag1, ...)
  2014. {
  2015.    return DoSuperMethod(cl, obj, OM_NEW, &tag1, NULL);
  2016. }
  2017. ///
  2018. /// GetMUI
  2019. //  Gets an attribute value from a MUI object
  2020. int GetMUI(struct Object *obj,int attr)
  2021. {
  2022.    int b;
  2023.    get(obj,attr,&b);
  2024.    return b;
  2025. }
  2026. ///
  2027. /// GetMUIStringPtr
  2028. //  Returns a pointer to the value of a MUI string object
  2029. char *GetMUIStringPtr(struct Object *obj)
  2030. {
  2031.    char *b;
  2032.    get(obj,MUIA_String_Contents,&b);
  2033.    return b;
  2034. }
  2035. ///
  2036. /// GetMUIString
  2037. //  Returns the value of a MUI string object
  2038. void GetMUIString(char *a,struct Object *obj)
  2039. {
  2040.    char *b;
  2041.    get(obj,MUIA_String_Contents,&b);
  2042.    strcpy(a,b);
  2043. }
  2044. ///
  2045. /// GetMUIText
  2046. //  Returns the value of a MUI text object
  2047. void GetMUIText(char *a,struct Object *obj)
  2048. {
  2049.    char *b;
  2050.    get(obj,MUIA_Text_Contents,&b);
  2051.    strcpy(a,b);
  2052. }
  2053. ///
  2054. /// GetMUIInteger
  2055. //  Returns the numeric value of a MUI string object
  2056. int GetMUIInteger(struct Object *obj)
  2057. {
  2058.    char *b;
  2059.    get(obj,MUIA_String_Contents,&b);
  2060.    return atoi(b);
  2061. }
  2062. ///
  2063. /// GetMUICheck
  2064. //  Returns the value of a MUI checkmark object
  2065. BOOL GetMUICheck(struct Object *obj)
  2066. {
  2067.    long b;         
  2068.    get(obj, MUIA_Selected, &b);
  2069.    return (BOOL)b;
  2070. }
  2071. ///
  2072. /// GetMUICycle
  2073. //  Returns the value of a MUI cycle object
  2074. int GetMUICycle(struct Object *obj)
  2075. {
  2076.    long b;         
  2077.    get(obj, MUIA_Cycle_Active, &b);
  2078.    return (int)b;
  2079. }
  2080. ///
  2081. /// GetMUIRadio
  2082. //  Returns the value of a MUI radio object
  2083. int GetMUIRadio(struct Object *obj)
  2084. {
  2085.    long b;         
  2086.    get(obj, MUIA_Radio_Active, &b);
  2087.    return (int)b;
  2088. }
  2089. ///
  2090. /// GetMUINumer
  2091. //  Returns the value of a MUI numeric slider
  2092. int GetMUINumer(struct Object *obj)
  2093. {
  2094.    long b;         
  2095.    get(obj, MUIA_Numeric_Value, &b);
  2096.    return (int)b;
  2097. }
  2098. ///
  2099. /// SafeOpenWindow
  2100. //  Tries to open a window
  2101. BOOL SafeOpenWindow(struct Object *obj)
  2102. {
  2103.    int isopen, isicon;
  2104.    set(obj, MUIA_Window_Open, TRUE);
  2105.    get(obj, MUIA_Window_Open, &isopen);
  2106.    get(_app(obj), MUIA_Application_Iconified, &isicon);
  2107.    if (isopen || isicon) return TRUE;
  2108.    DisplayBeep(0);
  2109.    return FALSE;
  2110. }
  2111. ///
  2112. /// DisposeModule
  2113. //  Frees resources of a MUI window
  2114. void DisposeModulePush(void *module)
  2115. {
  2116.    DoMethod(G->App, MUIM_Application_PushMethod, G->App, 3, MUIM_CallHook, &DisposeModuleHook, module);
  2117. }
  2118. void DisposeModule(void *modptr)
  2119. {
  2120.    struct UniversalClassData **module = (struct UniversalClassData **)modptr;
  2121.    if (*module)
  2122.    {
  2123.       APTR window = (*module)->GUI.WI;
  2124.       set(window, MUIA_Window_Open, FALSE);
  2125.       DoMethod(G->App, OM_REMMEMBER, window);
  2126.       MUI_DisposeObject(window);
  2127.       free(*module);
  2128.       *module = NULL;
  2129.    }
  2130. }
  2131. SAVEDS ASM void DisposeModuleFunc(REG(a1) void **arg)
  2132. {
  2133.    DisposeModule(arg[0]);
  2134. }
  2135. MakeHook(DisposeModuleHook,DisposeModuleFunc);
  2136. ///
  2137. /// LoadLayout
  2138. //  Loads column widths from ENV:MUI/YAM.cfg
  2139. void LoadLayout(void)
  2140. {
  2141.    char *ls;
  2142.    DoMethod(G->App, MUIM_Application_Load, MUIV_Application_Load_ENV);
  2143.    if (!*(ls = GetMUIStringPtr(G->MA->GUI.ST_LAYOUT))) ls = "35 100 25 100 30 100";
  2144.    sscanf(ls, "%ld %ld %ld %ld %ld %ld", &G->Weights[0], &G->Weights[1], &G->Weights[2], &G->Weights[3], &G->Weights[4], &G->Weights[5]);
  2145. }
  2146. ///
  2147. /// SaveLayout
  2148. //  Saves column widths to ENV(ARC):MUI/YAM.cfg
  2149. void SaveLayout(BOOL permanent)
  2150. {
  2151.    char buf[SIZE_DEFAULT];
  2152.    sprintf(buf, "%ld %ld %ld %ld %ld %ld", G->Weights[0], G->Weights[1], G->Weights[2], G->Weights[3], G->Weights[4], G->Weights[5]);
  2153.    setstring(G->MA->GUI.ST_LAYOUT, buf);
  2154.    DoMethod(G->App, MUIM_Application_Save, MUIV_Application_Save_ENV);
  2155.    if (permanent) DoMethod(G->App, MUIM_Application_Save, MUIV_Application_Save_ENVARC);
  2156. }
  2157. ///
  2158. /// ConvertKey
  2159. //  Converts input event to key code
  2160. ULONG ConvertKey(struct IntuiMessage *imsg)
  2161. {
  2162.    struct InputEvent event;
  2163.    UBYTE code = 0;
  2164.    event.ie_NextEvent    = NULL;
  2165.    event.ie_Class        = IECLASS_RAWKEY;
  2166.    event.ie_SubClass     = 0;
  2167.    event.ie_Code         = imsg->Code;
  2168.    event.ie_Qualifier    = imsg->Qualifier;
  2169.    event.ie_EventAddress = (APTR *) *((ULONG *)imsg->IAddress);
  2170.    MapRawKey(&event, &code, 1, NULL);
  2171.    return code;
  2172. }
  2173. ///
  2174.  
  2175. /**** BodyChunk ****/
  2176. /// FreeBCImage
  2177. //  Frees a bodychunk image
  2178. void FreeBCImage(struct BodyChunkData *bcd)
  2179. {
  2180.    if (bcd)
  2181.    {
  2182.       if (bcd->Colors) free(bcd->Colors);
  2183.       if (bcd->Body) free(bcd->Body);
  2184.       free(bcd);
  2185.    }
  2186. }
  2187. ///
  2188. /// GetBCImage
  2189. //  Searches for a bodychunk image by filename
  2190. struct BodyChunkData *GetBCImage(char *fname)
  2191. {
  2192.    int i;
  2193.    for (i = 0; i < MAXIMAGES; i++)
  2194.       if (G->BImage[i]) if (!strcmp(G->BImage[i]->File, fname)) return G->BImage[i];
  2195.    return NULL;
  2196. }
  2197. ///
  2198. /// LoadBCImage
  2199. //  Loads a bodychunk image from disk
  2200. struct BodyChunkData *LoadBCImage(char *fname)
  2201. {
  2202.    struct IFFHandle *iff;
  2203.    struct ContextNode *cn;
  2204.    struct BodyChunkData *bcd;
  2205.  
  2206.    if (bcd = calloc(1,sizeof(struct BodyChunkData)))
  2207.    {
  2208.       if (iff = AllocIFF())
  2209.       {
  2210.          if (iff->iff_Stream = Open(fname, MODE_OLDFILE))
  2211.          {
  2212.             InitIFFasDOS(iff);
  2213.             if (!OpenIFF(iff, IFFF_READ))
  2214.             {
  2215.                if (!ParseIFF(iff, IFFPARSE_STEP))
  2216.                {
  2217.                   if ((cn=CurrentChunk(iff)) && (cn->cn_ID == ID_FORM))
  2218.                   {
  2219.                      if (cn->cn_Type == ID_ILBM)
  2220.                      {
  2221.                         struct StoredProperty *sp;
  2222.                         struct BitMapHeader *bmhd;
  2223.                         int i, size;
  2224.  
  2225.                         if (!PropChunk (iff, ID_ILBM, ID_BMHD) &&
  2226.                             !PropChunk (iff, ID_ILBM, ID_CMAP) &&
  2227.                             !StopChunk (iff, ID_ILBM, ID_BODY) &&
  2228.                             !StopOnExit(iff, ID_ILBM, ID_FORM) &&
  2229.                             !ParseIFF  (iff, IFFPARSE_SCAN))
  2230.                         {
  2231.                            if (sp = FindProp(iff, ID_ILBM, ID_CMAP))
  2232.                            {
  2233.                               bcd->Colors = calloc(sp->sp_Size,sizeof(ULONG));
  2234.                               for (i = 0; i < sp->sp_Size; i++)
  2235.                               {
  2236.                                  UBYTE c = ((UBYTE *)sp->sp_Data)[i];
  2237.                                  bcd->Colors[i] = (c<<24)|(c<<16)|(c<<8)|c;
  2238.                               }
  2239.                            }
  2240.                            if (sp = FindProp(iff,ID_ILBM,ID_BMHD))
  2241.                            {
  2242.                               bmhd = (struct BitMapHeader *)sp->sp_Data;
  2243.                               if (bmhd->bmh_Compression == cmpNone || bmhd->bmh_Compression==cmpByteRun1)
  2244.                               {
  2245.                                  size = CurrentChunk(iff)->cn_Size;
  2246.                                  if (bcd->Body = calloc(size,1))
  2247.                                  {
  2248.                                     if (ReadChunkBytes(iff, bcd->Body, size) == size)
  2249.                                     {
  2250.                                        bcd->Width  = bmhd->bmh_Width;
  2251.                                        bcd->Height = bmhd->bmh_Height;
  2252.                                        bcd->Depth = bmhd->bmh_Depth;
  2253.                                        bcd->Compression = bmhd->bmh_Compression;
  2254.                                        bcd->Masking = bmhd->bmh_Masking;
  2255.                                        stcgfn(bcd->File, fname);
  2256.                                     }
  2257.                                  }
  2258.                               }
  2259.                            }
  2260.                         }
  2261.                      }
  2262.                   }
  2263.                }
  2264.             }
  2265.             CloseIFF(iff);
  2266.          }
  2267.          Close(iff->iff_Stream);
  2268.       }
  2269.       FreeIFF(iff);
  2270.    }
  2271.    else return NULL;
  2272.    if (!bcd->Depth) { FreeBCImage(bcd); return NULL; }
  2273.    return bcd;
  2274. }
  2275. ///
  2276.  
  2277. /*** Miscellaneous stuff ***/
  2278. /// PGPGetPassPhrase
  2279. //  Asks user for the PGP passphrase
  2280. void PGPGetPassPhrase(void)
  2281. {
  2282.    if (!G->PGPPassPhrase[0])
  2283.    {
  2284.       G->PGPPassVolatile = FALSE;
  2285.       if (GetVar("PGPPASS", G->PGPPassPhrase, SIZE_DEFAULT, 0) < 0)
  2286.       {
  2287.          char pgppass[SIZE_DEFAULT];
  2288.          G->PGPPassVolatile = TRUE; *pgppass = 0;
  2289.          if (StringRequest(pgppass, SIZE_DEFAULT, "PGP", GetStr(MSG_UT_PGPPassReq), GetStr(MSG_Okay), NULL, GetStr(MSG_Cancel), TRUE, G->MA->GUI.WI))
  2290.             strcpy(G->PGPPassPhrase, pgppass);
  2291.       }
  2292.       else return;
  2293.    }
  2294.    SetVar("PGPPASS", G->PGPPassPhrase, -1, GVF_GLOBAL_ONLY);
  2295. }
  2296. ///
  2297. /// PGPClearPassPhrase
  2298. //  Clears the ENV variable containing the PGP passphrase
  2299. void PGPClearPassPhrase(BOOL force)
  2300. {
  2301.    if (G->PGPPassVolatile) DeleteVar("PGPPASS", 0);
  2302.    if (force) G->PGPPassPhrase[0] = 0;
  2303. }
  2304. ///
  2305. /// PGPCommand
  2306. //  Launches a PGP command
  2307. int PGPCommand(char *progname, char *options, int flags)
  2308. {
  2309.    BPTR fh;
  2310.    int error = -1;
  2311.    char command[SIZE_LARGE];
  2312.  
  2313.    if (fh = Open("NIL:", MODE_READWRITE))
  2314.    {
  2315.       Busy(GetStr(MSG_BusyPGPrunning), "", 0, 0);
  2316.       strmfp(command, C->PGPCmdPath, progname);
  2317.       strcat(command, " >" PGPLOGFILE " ");
  2318.       strcat(command, options);
  2319.       error = SystemTags(command, SYS_Input, fh, SYS_Output, fh, NP_StackSize, C->StackSize, TAG_DONE);
  2320.       Close(fh);
  2321.       BusyEnd;
  2322.    }
  2323.    if (error > 0 && !(flags & NOERRORS)) ER_NewError(GetStr(MSG_ER_PGPreturnsError), command, PGPLOGFILE);
  2324.    if (error < 0) ER_NewError(GetStr(MSG_ER_PGPnotfound), C->PGPCmdPath, NULL);
  2325.    if (!error && !(flags & KEEPLOG)) DeleteFile(PGPLOGFILE);
  2326.    return error;
  2327. }
  2328. ///
  2329. /// AppendToLogfile
  2330. //  Appends a line to the logfile
  2331. void AppendToLogfile(int id, char *text, void *a1, void *a2, void *a3, void *a4)
  2332. {
  2333.    FILE *fh;
  2334.    char logfile[SIZE_PATHFILE], filename[SIZE_FILE];
  2335.    if (!C->LogAllEvents && (id < 30 || id > 49)) return;
  2336.    if (C->SplitLogfile) 
  2337.    {
  2338.       time_t now;
  2339.       struct tm tm;
  2340.       time(&now); tm = *(localtime(&now));
  2341.       sprintf(filename, "YAM-%s%ld.log", months[tm.tm_mon], 1900+tm.tm_year);
  2342.    }
  2343.    else strcpy(filename, "YAM.log");
  2344.    strmfp(logfile, *C->LogfilePath ? C->LogfilePath : G->ProgDir, filename);
  2345.    if (fh = fopen(logfile, "a"))
  2346.    {
  2347.       fprintf(fh, "%s [%02ld] ", DateStamp2String(NULL, DSS_DATETIME), id);
  2348.       fprintf(fh, text, a1, a2, a3, a4);
  2349.       fprintf(fh, "\n");
  2350.       fclose(fh);
  2351.    }
  2352. }
  2353. ///
  2354. /// AppendLog
  2355. //  Appends a line to the logfile, depending on log mode
  2356. void AppendLog(int id, char *text, void *a1, void *a2, void *a3, void *a4)
  2357. {
  2358.    if (C->LogfileMode != 0) AppendToLogfile(id, text, a1, a2, a3, a4);
  2359. }
  2360. void AppendLogNormal(int id, char *text, void *a1, void *a2, void *a3, void *a4)
  2361. {
  2362.    if (C->LogfileMode == 1) AppendToLogfile(id, text, a1, a2, a3, a4);
  2363. }
  2364. void AppendLogVerbose(int id, char *text, void *a1, void *a2, void *a3, void *a4)
  2365. {
  2366.    if (C->LogfileMode == 2) AppendToLogfile(id, text, a1, a2, a3, a4);
  2367. }
  2368. ///
  2369. /// Busy
  2370. //  Displays busy message and sleep pointer
  2371. int BusyLevel = 0;
  2372. void Busy(char *text, char *parameter, int cur, int max)
  2373. {
  2374.    static char infotext[SIZE_DEFAULT];
  2375.    if (text)
  2376.    {
  2377.       if (*text)
  2378.       {
  2379.          sprintf(infotext, text, parameter);
  2380.          if (G->MA && !BusyLevel)
  2381.          {
  2382.             set(G->MA->GUI.GA_INFO, MUIA_Gauge_InfoText, infotext);
  2383.             set(G->MA->GUI.GA_INFO, MUIA_Gauge_Max, max);
  2384.          }
  2385.          BusyLevel++;
  2386.          set(G->App, MUIA_Application_Sleep, TRUE);
  2387.       }
  2388.       else
  2389.       {
  2390.          set(G->App, MUIA_Application_Sleep, FALSE);
  2391.          if (BusyLevel) --BusyLevel;
  2392.          if (G->MA && !BusyLevel)
  2393.          {
  2394.             set(G->MA->GUI.GA_INFO, MUIA_Gauge_InfoText, "");
  2395.             set(G->MA->GUI.GA_INFO, MUIA_Gauge_Current, 0);
  2396.          }
  2397.       }
  2398.    }
  2399.    else
  2400.    {
  2401.       set(G->MA->GUI.GA_INFO, MUIA_Gauge_Current, cur);
  2402.    }
  2403. }
  2404. ///
  2405. /// DisplayStatistics
  2406. //  Calculates folder statistics and update mailbox status icon
  2407. void DisplayStatistics(struct Folder *fo)
  2408. {
  2409.    struct Mail *mail;
  2410.    BOOL check = FALSE;
  2411.    struct Folder *actfo = FO_GetCurrentFolder();
  2412.    static char apptit[SIZE_DEFAULT/2];
  2413.  
  2414.    if (!fo) fo = actfo;
  2415.    if (fo == (struct Folder *)-1) { fo = FO_GetFolderByType(FT_INCOMING, NULL); check = TRUE; }
  2416.    for (mail = fo->Messages, fo->Unread = fo->New = 0; mail; mail = mail->Next)
  2417.    {
  2418.       if (mail->Status == STATUS_NEW) { fo->New++; fo->Unread++; }
  2419.       if (mail->Status == STATUS_UNR) fo->Unread++;
  2420.    }
  2421.    DoMethod(G->MA->GUI.NL_FOLDERS, MUIM_NList_Redraw, FO_GetFolderPosition(fo));
  2422.    if (fo == actfo) { MA_SetMessageInfoFunc(); MA_SetFolderInfoFunc(); }
  2423.    if (fo->Type == FT_INCOMING && !G->AppIconQuiet) if (check || fo->New != G->NewMsgs || fo->Total != G->TotMsgs)
  2424.    {
  2425.       int mode = fo->Total ? (fo->New ? 2 : 1) : 0;
  2426.       if (G->TR) if (G->TR->Checking) mode = 3;
  2427.       sprintf(apptit, GetStr(MSG_UT_AppStats), fo->New, fo->Total);
  2428.       if (G->AppIcon) { RemoveAppIcon(G->AppIcon); G->AppIcon = NULL; }
  2429.       if (G->DiskObj[mode]) G->AppIcon = AddAppIconA(0, 0, (STRPTR)apptit, G->AppPort, NULL, G->DiskObj[mode], NULL);
  2430.       G->NewMsgs = fo->New; G->TotMsgs = fo->Total;
  2431.    }
  2432. }
  2433. ///
  2434. /// CheckPrinter
  2435. //  Checks if printer is ready
  2436. BOOL CheckPrinter(void)
  2437. {
  2438.    struct MsgPort *PrintPort;
  2439.    struct IOStdReq *PrintIO;
  2440.    UWORD  Result = 0;
  2441.    char  _PrinterDeviceName[] = "printer.device";
  2442.    long  _PrinterDeviceUnit = 0;
  2443.    char *error = NULL;
  2444.  
  2445.    if (PrintPort = CreateMsgPort())
  2446.    {
  2447.       PrintPort->mp_Node.ln_Name = "YAM PrintPort";
  2448.       if (PrintIO = CreateIORequest(PrintPort, sizeof(struct IOStdReq)))
  2449.       {
  2450.          if (!(OpenDevice((STRPTR)_PrinterDeviceName, _PrinterDeviceUnit, (struct IORequest *)PrintIO, 0)))
  2451.          {
  2452.             PrintIO->io_Message.mn_ReplyPort = PrintPort;
  2453.             PrintIO->io_Command = PRD_QUERY;
  2454.             PrintIO->io_Data = &Result;
  2455.             DoIO((struct IORequest *)PrintIO);
  2456.             if (((Result>>8) & 3) == 0) error = NULL;
  2457.             else if ((Result>>8) & 01) error = GetStr(MSG_UT_NoPaper);
  2458.             else error = GetStr(MSG_UT_NoPrinter);
  2459.             CloseDevice((struct IORequest *)PrintIO);
  2460.          }
  2461.          DeleteIORequest((struct IORequest *)PrintIO);
  2462.       }
  2463.       DeleteMsgPort(PrintPort);
  2464.    }
  2465.    if (error) if (!MUI_Request(G->App, NULL, 0, GetStr(MSG_ErrorReq), GetStr(MSG_OkayCancelReq), error)) return False;
  2466.    return True;
  2467. }
  2468. ///
  2469. /// PlaySound
  2470. //  Plays a sound file using datatypes
  2471. void PlaySound(char *filename)
  2472. {
  2473.    if (DataTypesBase)
  2474.    {
  2475.       struct dtTrigger Play = { DTM_TRIGGER, NULL, STM_PLAY, NULL };
  2476.       Object *sound;
  2477.       BYTE sig;
  2478.       if (sound = NewDTObject((APTR)filename, DTA_GroupID, GID_SOUND, SDTA_Volume, 64, SDTA_Cycles, 1, TAG_DONE))
  2479.       {
  2480.          if ((sig = AllocSignal(-1)) >= 0)
  2481.          {
  2482.             if (SetDTAttrs(sound, NULL, NULL, SDTA_SignalTask, FindTask(NULL), SDTA_SignalBit, 1<<sig, TAG_END) == 2)
  2483.             {
  2484.                DoDTMethodA(sound, NULL, NULL, (Msg)&Play);
  2485.                Wait(1<<sig);
  2486.             }
  2487.             else
  2488.             {
  2489.                ULONG length = 0, period = 394, cycles = 1, frequency, seconds;
  2490.                GetDTAttrs(sound, SDTA_SampleLength, &length, SDTA_Period, &period, SDTA_Cycles, &cycles, TAG_END);
  2491.                if (length > 131072) length = 131072;
  2492.                frequency = (ULONG)(SysBase->ex_EClockFrequency * 5) / period;
  2493.                seconds = (length * cycles) / frequency + 1;
  2494.                DoDTMethodA(sound, NULL, NULL, (Msg)&Play);
  2495.                Delay(seconds * 50);
  2496.             }
  2497.             FreeSignal(sig);
  2498.          }
  2499.          DisposeDTObject(sound);
  2500.       }
  2501.    }
  2502. }
  2503. ///
  2504. /// MatchExtension
  2505. //  Matches a file extension against a list of extension
  2506. BOOL MatchExtension(char *fileext, char *extlist)
  2507. {
  2508.    while (extlist = strtok(extlist, " "))
  2509.    {
  2510.       if (!stricmp(extlist, fileext)) return TRUE;
  2511.       extlist = NULL;
  2512.    }
  2513.    return FALSE;
  2514. }
  2515. ///
  2516. /// IdentifyFileDT
  2517. //  Detects the file type using datatypes.library
  2518. char *IdentifyFileDT(char *fname)
  2519. {
  2520.    static char ctype[SIZE_CTYPE], *type = NULL;
  2521.    struct Library *DataTypesBase = OpenLibrary("datatypes.library", 39);
  2522.    strcpy(ctype, "application/octet-stream");
  2523.    if (DataTypesBase)
  2524.    {
  2525.       BPTR lock;
  2526.       if (lock = Lock(fname, ACCESS_READ))
  2527.       {
  2528.          struct DataType *dtn;
  2529.          if (dtn = ObtainDataTypeA(DTST_FILE, (APTR)lock, NULL))
  2530.          {
  2531.             struct DataTypeHeader *dth = dtn->dtn_Header;
  2532.             switch (dth->dth_GroupID)
  2533.             {
  2534.                case GID_SYSTEM:     break;
  2535.                case GID_DOCUMENT:   type = "application"; break;
  2536.                case GID_TEXT:       type = "text"; break;
  2537.                case GID_SOUND:
  2538.                case GID_INSTRUMENT: type = "audio"; break;
  2539.                case GID_PICTURE:    type = "image"; break;
  2540.                case GID_MOVIE:
  2541.                case GID_ANIMATION:  type = "video"; break;
  2542.             }
  2543.             if (type) sprintf(ctype, "%s/x-%s", type, dth->dth_BaseName);
  2544.             ReleaseDataType(dtn);
  2545.          }
  2546.          UnLock (lock);
  2547.       }
  2548.       CloseLibrary(DataTypesBase);
  2549.    }
  2550.    return ctype;
  2551. }
  2552. ///
  2553. /// IdentifyFile
  2554. //  Detects the file type by analyzing file extension and contents
  2555. char *IdentifyFile(char *fname)
  2556. {
  2557.    char *ctype = "";
  2558.    long bits = FileProtection(fname);
  2559.    FILE *fh;
  2560.  
  2561.    if (fh = fopen(fname, "r"))
  2562.    {
  2563.       int i, len;
  2564.       char buffer[SIZE_LARGE], *ext;
  2565.  
  2566.       len = fread(buffer, 1, SIZE_LARGE-1, fh);
  2567.       buffer[len] = 0;
  2568.       fclose(fh);
  2569.       if (ext = strrchr(fname, '.')) ++ext; else ext = "--";
  2570.       if (!stricmp(ext, "htm") || !stricmp(ext, "html"))
  2571.          ctype = ContType[CT_TX_HTML];
  2572.       else if (!strnicmp(buffer, "@database", 9) || !stricmp(ext, "guide"))
  2573.          ctype = ContType[CT_TX_GUIDE];
  2574.       else if (!stricmp(ext, "ps") || !stricmp(ext, "eps"))
  2575.          ctype = ContType[CT_AP_PS];
  2576.       else if (!stricmp(ext, "rtf"))
  2577.          ctype = ContType[CT_AP_RTF];
  2578.       else if (!stricmp(ext, "lha") || !strncmp(&buffer[2], "-lh5-", 5))
  2579.          ctype = ContType[CT_AP_LHA];
  2580.       else if (!stricmp(ext, "lzx") || !strncmp(buffer, "LZX", 3))
  2581.          ctype = ContType[CT_AP_LZX];
  2582.       else if (!stricmp(ext, "zip"))
  2583.          ctype = ContType[CT_AP_ZIP];
  2584.       else if (*((long *)buffer) >= HUNK_UNIT && *((long *)buffer) <= HUNK_INDEX)
  2585.          ctype = ContType[CT_AP_AEXE];
  2586.       else if (!stricmp(ext, "rexx") || !stricmp(ext+strlen(ext)-2, "rx"))
  2587.          ctype = ContType[CT_AP_REXX];
  2588.       else if (!strncmp(&buffer[6], "JFIF", 4))
  2589.          ctype = ContType[CT_IM_JPG];
  2590.       else if (!strncmp(buffer, "GIF8", 4))
  2591.          ctype = ContType[CT_IM_GIF];
  2592.       else if (!strnicmp(ext, "png",4) || !strncmp(&buffer[1], "PNG", 3))
  2593.          ctype = ContType[CT_IM_PNG];
  2594.       else if (!strnicmp(ext, "tif",4))
  2595.          ctype = ContType[CT_IM_TIFF];
  2596.       else if (!strncmp(buffer, "FORM", 4) && !strncmp(&buffer[8], "ILBM", 4))
  2597.          ctype = ContType[CT_IM_ILBM];
  2598.       else if (!stricmp(ext, "au") || !stricmp(ext, "snd"))
  2599.          ctype = ContType[CT_AU_AU];
  2600.       else if (!strncmp(buffer, "FORM", 4) && !strncmp(&buffer[8], "8SVX", 4))
  2601.          ctype = ContType[CT_AU_8SVX];
  2602.       else if (!stricmp(ext, "wav"))
  2603.          ctype = ContType[CT_AU_WAV];
  2604.       else if (!stricmp(ext, "mpg") || !stricmp(ext, "mpeg"))
  2605.          ctype = ContType[CT_VI_MPG];
  2606.       else if (!stricmp(ext, "qt") || !stricmp(ext, "mov"))
  2607.          ctype = ContType[CT_VI_MOV];
  2608.       else if (!strncmp(buffer, "FORM", 4) && !strncmp(&buffer[8], "ANIM", 4))
  2609.          ctype = ContType[CT_VI_ANIM];
  2610.       else if (!stricmp(ext, "avi"))
  2611.          ctype = ContType[CT_VI_AVI];
  2612.       else if (stristr(buffer, "\nFrom:"))
  2613.          ctype = ContType[CT_ME_EMAIL];
  2614.       else
  2615.       {
  2616.          for (i = 1; i < MAXMV; i++) if (C->MV[i])
  2617.             if (MatchExtension(ext, C->MV[i]->Extension)) ctype = C->MV[i]->ContentType;
  2618.       }
  2619.       if (!*ctype)
  2620.       {
  2621.          int notascii = 0;
  2622.          for (i = 0; i < len; i++)
  2623.             if ((int)buffer[i] < 32 || (int)buffer[i] > 127)
  2624.                if (buffer[i] != '\t' && buffer[i] != '\n') notascii++;
  2625.          if (notascii < len/10) ctype =  ContType[(bits&FIBF_SCRIPT) ? CT_AP_SCRIPT : CT_TX_PLAIN];
  2626.          else ctype = IdentifyFileDT(fname);
  2627.       }
  2628.    }
  2629.    return ctype;
  2630. }
  2631. ///
  2632. /// LoadTranslationTable
  2633. //  Load a translation table into memory
  2634. BOOL LoadTranslationTable(struct TranslationTable **tt, char *file)
  2635. {
  2636.    FILE *fp;
  2637.    if (*tt) free(*tt);
  2638.    if (!*file) return FALSE;
  2639.    if (!(*tt = calloc(1,sizeof(struct TranslationTable)))) return FALSE;
  2640.    if (fp = fopen(file, "r"))
  2641.    {
  2642.       UBYTE buf[SIZE_DEFAULT], *p;
  2643.       int i;
  2644.       for (i = 0; i < 256; i++) (*tt)->Table[i] = (UBYTE)i;
  2645.       stccpy((*tt)->File, file, SIZE_PATHFILE);
  2646.       fgets(buf, SIZE_DEFAULT, fp);
  2647.       if (!strncmp(buf, "YCT1", 4))
  2648.       {
  2649.          fgets((*tt)->Name, SIZE_DEFAULT, fp);
  2650.          if (p = strchr((*tt)->Name,'\n')) *p = 0;
  2651.          while (fgets(buf, SIZE_DEFAULT, fp))
  2652.             if (!strnicmp(buf, "from", 4)) stccpy((*tt)->SourceCharset, Trim(&buf[5]), SIZE_CTYPE);
  2653.             else if  (!strnicmp(buf, "to", 2)) stccpy((*tt)->DestCharset, Trim(&buf[3]), SIZE_CTYPE);
  2654.             else if (!strnicmp(buf, "header", 6)) (*tt)->Header = TRUE;
  2655.             else if (!strnicmp(buf, "author", 6));
  2656.             else if (strchr(buf, '='))
  2657.             {
  2658.                int source, dest;
  2659.                p = buf;
  2660.                if (*p == '$') stch_i(&p[1], &source); else source = (int)*p;
  2661.                while (*p++ != '=');
  2662.                if (*p == '$') stch_i(&p[1], &dest); else dest = (int)*p;
  2663.                if (source >= 0 && source <= 0xFF && dest >= 0 && dest <= 0xFF) (*tt)->Table[source] = (UBYTE)dest;
  2664.             }
  2665.          fclose(fp);
  2666.          return TRUE;
  2667.       }
  2668.       fclose(fp);
  2669.    }
  2670.    ER_NewError(GetStr(MSG_ER_ErrorTTable), file, NULL);
  2671.    free(*tt); *tt = NULL;
  2672.    return FALSE;
  2673. }
  2674. ///
  2675. /// ExecuteCommand
  2676. //  Executes a DOS command
  2677. BOOL ExecuteCommand(char *cmd, BOOL asynch, BPTR outdef)
  2678. {
  2679.    BPTR in, out, path;
  2680.    int ret;
  2681.    switch (outdef)
  2682.    {
  2683.       case NULL:    in = Input();out = Output(); break;
  2684.       case OUT_NIL: out = Open("NIL:", MODE_NEWFILE); in = Open("NIL:", MODE_OLDFILE); break;
  2685.       default:      out = outdef; in = Open("NIL:", MODE_OLDFILE); break;
  2686.    }
  2687.    if (!outdef) asynch = FALSE;
  2688.    if (_WBenchMsg)
  2689.    {
  2690.       path = CloneWorkbenchPath(_WBenchMsg);
  2691.       if ((ret = SystemTags(cmd, SYS_Input,in, SYS_Output,out, NP_Path,path, NP_StackSize,C->StackSize, SYS_Asynch,asynch, TAG_DONE)) == -1) FreeWorkbenchPath(path);
  2692.    }
  2693.    else ret = SystemTags(cmd, SYS_Input,in, SYS_Output,out, NP_StackSize,C->StackSize, SYS_Asynch,asynch, TAG_DONE);
  2694.    if (ret == -1 && asynch && outdef) { Close(out); Close(in); }
  2695.    return (BOOL)(!ret);
  2696. }
  2697. ///
  2698. /// GetSimpleID
  2699. //  Returns a unique number
  2700. int GetSimpleID(void)
  2701. {
  2702.    static int num = 0;
  2703.    return ++num;
  2704. }
  2705. ///
  2706. /// GotoURL
  2707. //  Loads an URL using an ARexx script or openurl.library
  2708. void GotoURL(char *url)
  2709. {
  2710.    if (C->RX[MACRO_URL].Script[0])
  2711.    {
  2712.       char newurl[SIZE_LARGE];
  2713.       sprintf(newurl, "%c%s%c", '"', url, '"');
  2714.       MA_StartMacro(MACRO_URL, newurl);
  2715.    }
  2716.    else if (OpenURLBase = OpenLibrary("openurl.library", 1))
  2717.    {
  2718.       URL_Open(url, TAG_DONE);
  2719.       CloseLibrary(OpenURLBase);
  2720.    }
  2721. }
  2722. ///
  2723.  
  2724. /*** REXX interface support ***/
  2725.  
  2726. /// InsertAddresses
  2727. //  Appends an array of addresses to a string gadget
  2728. void InsertAddresses(APTR obj, char **addr, BOOL add)
  2729. {
  2730.    char *buf;
  2731.    get(obj, MUIA_String_Contents, &buf);
  2732.    if (*buf && add) DoMethod(obj, MUIM_BetterString_Insert, ", ", MUIV_BetterString_Insert_EndOfString);
  2733.    else setstring(obj, "");
  2734.    DoMethod(obj, MUIM_BetterString_Insert, *addr, MUIV_BetterString_Insert_EndOfString);
  2735.    while (*++addr)
  2736.    {
  2737.       DoMethod(obj, MUIM_BetterString_Insert, ", ", MUIV_BetterString_Insert_EndOfString);
  2738.       DoMethod(obj, MUIM_BetterString_Insert, *addr, MUIV_BetterString_Insert_EndOfString);
  2739.    }
  2740. }
  2741. ///
  2742. /// AllocReqText
  2743. //  Prepare multi-line text for requesters, converts \n to line breaks
  2744. char *AllocReqText(char *s)
  2745. {
  2746.    char *d, *reqtext;
  2747.    d = reqtext = calloc(strlen(s)+1, 1);
  2748.    while (*s)
  2749.       if (*s == '\\' && s[1] == 'n') { *d++ = '\n'; s++; s++; }
  2750.       else *d++ = *s++;
  2751.    return reqtext;
  2752. }
  2753. ///
  2754.