src/mui/ReadMailGroup.c
/***************************************************************************
YAM - Yet Another Mailer
Copyright (C) 1995-2000 Marcel Beck
Copyright (C) 2000-2022 YAM Open Source Team
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
YAM Official Support Site : http://www.yam.ch
YAM OpenSource project : http://sourceforge.net/projects/yamos/
$Id$
Superclass: MUIC_Group
Description: Mail read GUI group which include a texteditor and a header display
***************************************************************************/
#include "ReadMailGroup_cl.h"
#include <ctype.h>
#include <string.h>
#include <proto/dos.h>
#include <proto/muimaster.h>
#include <libraries/gadtools.h>
#include <libraries/iffparse.h>
#include <mui/NBalance_mcc.h>
#include <mui/NList_mcc.h>
#include <mui/NListview_mcc.h>
#include <mui/TextEditor_mcc.h>
#include "SDI_hook.h"
#include "YAM.h"
#include "YAM_error.h"
#include "YAM_mainFolder.h"
#include "YAM_read.h"
#include "mui/AttachmentGroup.h"
#include "mui/HeaderList.h"
#include "mui/ImageArea.h"
#include "mui/MainMailListGroup.h"
#include "mui/MailTextEdit.h"
#include "mui/SearchTextWindow.h"
#include "mui/ReadMailGroup.h"
#include "mui/ReadWindow.h"
#include "mui/YAMApplication.h"
#include "Busy.h"
#include "Config.h"
#include "DynamicString.h"
#include "FileInfo.h"
#include "HTML2Mail.h"
#include "Locale.h"
#include "Logfile.h"
#include "MailList.h"
#include "MethodStack.h"
#include "MimeTypes.h"
#include "MUIObjects.h"
#include "ParseEmail.h"
#include "Requesters.h"
#include "Timer.h"
#include "UserIdentity.h"
#include "Debug.h"
/* CLASSDATA
struct Data
{
struct ReadMailData *readMailData;
Object *headerGroup;
Object *headerListview;
Object *headerList;
Object *senderImageGroup;
Object *senderImage;
Object *senderImageSpace;
Object *balanceObjectTop;
Object *balanceObjectBottom;
Object *mailBodyGroup;
Object *mailTextObject;
Object *textEditScrollbar;
Object *scrolledAttachmentGroup;
Object *attachmentGroup;
Object *contextMenu;
Object *searchWindow;
char menuTitle[SIZE_DEFAULT];
struct MinList senderInfoHeaders;
BOOL hasContent;
BOOL activeAttachmentGroup;
};
*/
/* EXPORT
#define MUIF_ReadMailGroup_Clear_KeepText (1<<0) // don't clear the text editor content
#define MUIF_ReadMailGroup_Clear_KeepAttachmentGroup (1<<1) // don't clear the attachmentgroup
#define hasKeepTextFlag(v) (isFlagSet((v), MUIF_ReadMailGroup_Clear_KeepText))
#define hasKeepAttachmentGroupFlag(v) (isFlagSet((v), MUIF_ReadMailGroup_Clear_KeepAttachmentGroup))
#define MUIF_ReadMailGroup_ReadMail_UpdateOnly (1<<0) // the call to ReadMail is just because of an update of the same mail
#define MUIF_ReadMailGroup_ReadMail_StatusChangeDelay (1<<1) // the mail status should not be change immediatley but with a specified time interval
#define MUIF_ReadMailGroup_ReadMail_UpdateTextOnly (1<<2) // update the main mail text only
#define hasUpdateOnlyFlag(v) (isFlagSet((v), MUIF_ReadMailGroup_ReadMail_UpdateOnly))
#define hasStatusChangeDelayFlag(v) (isFlagSet((v), MUIF_ReadMailGroup_ReadMail_StatusChangeDelay))
#define hasUpdateTextOnlyFlag(v) (isFlagSet((v), MUIF_ReadMailGroup_ReadMail_UpdateTextOnly))
#define MUIF_ReadMailGroup_Search_Again (1<<0) // perform the same search again
#define hasSearchAgainFlag(v) (isFlagSet((v), MUIF_ReadMailGroup_Search_Again))
#define MUIF_ReadMailGroup_DoEditAction_Fallback (1<<0) // fallback to default
#define hasEditActionFallbackFlag(v) (isFlagSet((v), MUIF_ReadMailGroup_DoEditAction_Fallback))
*/
/// Menu enumerations
enum { RMEN_HSHORT=100, RMEN_HFULL, RMEN_SNONE, RMEN_SDATA, RMEN_SFULL, RMEN_SIMAGE, RMEN_WRAPH,
RMEN_TSTYLE, RMEN_FFONT, RMEN_EXTKEY, RMEN_CHKSIG, RMEN_SAVEDEC, RMEN_DISPLAY, RMEN_DETACH,
RMEN_DELETEATT, RMEN_PRINT, RMEN_SAVE, RMEN_REPLY, RMEN_FORWARD_ATTACH, RMEN_FORWARD_INLINE,
RMEN_MOVE, RMEN_COPY, RMEN_DELETE, RMEN_SEARCH, RMEN_SEARCHAGAIN, RMEN_TCOLOR
};
///
/* Hooks */
/// TextEditDoubleClickHook
// Handles double-clicks on an URL
HOOKPROTONH(TextEditDoubleClickFunc, BOOL, Object *obj, struct ClickMessage *clickmsg)
{
// default to let TextEditor.mcc handle the double click
BOOL result = FALSE;
ENTER();
D(DBF_GUI, "DoubleClick: %ld - [%s]", clickmsg->ClickPosition, clickmsg->LineContents);
// if the user clicked on space we skip the following
// analysis of a URL and just check if it was an attachment the user clicked at
if(!isspace(clickmsg->LineContents[clickmsg->ClickPosition]))
{
char *line;
// then we make a copy of the LineContents
if((line = strdup(clickmsg->LineContents)) != NULL)
{
int pos = clickmsg->ClickPosition;
char *surl;
char *url = NULL;
char *eol = &line[strlen(line)];
char *p;
enum tokenType type;
// find the beginning of the word we clicked at
surl = &line[pos];
while(surl != &line[0] && !isspace(*(surl-1)))
surl--;
// now find the end of the word the user clicked at
p = &line[pos];
while(p+1 != eol && !isspace(*(p+1)))
p++;
*(++p) = '\0';
SHOWSTRING(DBF_GUI, surl);
// now we start our quick lexical analysis to find a clickable element within
// the doubleclick area
if((type = ExtractURL(surl, &url)) != 0)
{
SHOWSTRING(DBF_GUI, url);
switch(type)
{
case tEMAIL:
{
RE_ClickedOnMessage(url, _win(obj));
// no further handling by TextEditor.mcc required
result = TRUE;
}
break;
case tMAILTO:
{
RE_ClickedOnMessage(&url[7], _win(obj));
// no further handling by TextEditor.mcc required
result = TRUE;
}
break;
case tHTTP:
case tHTTPS:
case tFTP:
case tFILE:
case tGOPHER:
case tTELNET:
case tNEWS:
case tURL:
{
BOOL newWindow = FALSE;
// TextEditor.mcc V15.26+ tells us the pressed qualifier
// if the CTRL key is pressed we try to open a new window
newWindow = isAnyFlagSet(clickmsg->Qualifier, IEQUALIFIER_CONTROL);
SHOWVALUE(DBF_GUI, newWindow);
// don't invoke the GotoURL command right here in there hook, as the
// execution may take lots of time
PushMethodOnStack(_app(obj), 3, MUIM_YAMApplication_GotoURL, url, newWindow);
// don't free the URL string in this context
url = NULL;
// no further handling by TextEditor.mcc required
result = TRUE;
}
break;
default:
// nothing
break;
}
}
free(url);
free(line);
}
}
RETURN(result);
return result;
}
MakeStaticHook(TextEditDoubleClickHook, TextEditDoubleClickFunc);
///
/* Private Functions */
/// ShowAttachmentGroup
// add the attachmentrgoup to our readmailgroup object
static BOOL ShowAttachmentGroup(struct Data *data)
{
BOOL success = FALSE;
ENTER();
if(data->activeAttachmentGroup == FALSE)
{
if(DoMethod(data->mailBodyGroup, MUIM_Group_InitChange))
{
// add the group to the surrounding group
DoMethod(data->mailBodyGroup, OM_ADDMEMBER, data->balanceObjectBottom);
DoMethod(data->mailBodyGroup, OM_ADDMEMBER, data->scrolledAttachmentGroup);
data->activeAttachmentGroup = TRUE;
DoMethod(data->mailBodyGroup, MUIM_Group_ExitChange);
success = TRUE;
}
}
else
{
// force a relayout of the scrollgroup, otherwise the scrollbars of the
// attachment group might not be refreshed correctly
if(DoMethod(data->scrolledAttachmentGroup, MUIM_Group_InitChange))
DoMethod(data->scrolledAttachmentGroup, MUIM_Group_ExitChange2, TRUE);
// an attachment group already existed so signal success
success = TRUE;
}
RETURN(success);
return success;
}
///
/// HideAttachmentGroup
// remove an attachment group from the window if it exists
static void HideAttachmentGroup(struct Data *data)
{
ENTER();
if(data->activeAttachmentGroup)
{
if(DoMethod(data->mailBodyGroup, MUIM_Group_InitChange))
{
// remove the attachment group and free it
DoMethod(data->mailBodyGroup, OM_REMMEMBER, data->balanceObjectBottom);
DoMethod(data->mailBodyGroup, OM_REMMEMBER, data->scrolledAttachmentGroup);
data->activeAttachmentGroup = FALSE;
DoMethod(data->mailBodyGroup, MUIM_Group_ExitChange);
}
}
LEAVE();
}
///
/// ParamEnd
// Finds next parameter in header field
static char *ParamEnd(const char *s)
{
char *result = NULL;
BOOL inquotes = FALSE;
ENTER();
while(*s != '\0')
{
if(inquotes == TRUE)
{
if(*s == '"')
inquotes = FALSE;
else if(*s == '\\')
s++;
}
else if(*s == ';')
{
result = (char *)s;
break;
}
else if(*s == '"')
{
inquotes = TRUE;
}
s++;
}
RETURN(result);
return result;
}
///
/// Cleanse
// Removes trailing and leading spaces and converts string to lower case
static char *Cleanse(char *s)
{
char *tmp;
ENTER();
// skip all leading spaces and return pointer
// to first real char.
s = TrimStart(s);
// convert all chars to lowercase no matter what
for(tmp=s; *tmp; ++tmp)
*tmp = tolower((int)*tmp);
// now we walk back from the end of the string
// and strip the trailing spaces.
while(tmp > s && *--tmp && isspace(*tmp))
*tmp = '\0';
RETURN(s);
return s;
}
///
/// ExtractSenderInfo
// Extracts all sender information of a mail and put it
// into a supplied ABEntry. (parses X-SenderInfo header field)
static void ExtractSenderInfo(const struct Mail *mail, struct ABookNode *ab)
{
ENTER();
InitABookNode(ab, ABNT_USER);
strlcpy(ab->Address, mail->From.Address, sizeof(ab->Address));
strlcpy(ab->RealName, mail->From.RealName, sizeof(ab->RealName));
if(isSenderInfoMail(mail))
{
struct ExtendedMail *email;
if((email = MA_ExamineMail(mail->Folder, mail->MailFile, TRUE)) != NULL)
{
char *s;
if((s = strchr(email->SenderInfo, ';')) != NULL)
{
char *t;
*s++ = '\0';
do
{
char *eq;
if((t = ParamEnd(s)) != NULL)
*t++ = '\0';
if((eq = strchr(s, '=')) == NULL)
Cleanse(s);
else
{
*eq++ = '\0';
s = Cleanse(s);
eq = TrimStart(eq);
TrimEnd(eq);
UnquoteString(eq, FALSE);
if(stricmp(s, "street") == 0)
strlcpy(ab->Street, eq, sizeof(ab->Street));
else if(stricmp(s, "city") == 0)
strlcpy(ab->City, eq, sizeof(ab->City));
else if(stricmp(s, "country") == 0)
strlcpy(ab->Country, eq, sizeof(ab->Country));
else if(stricmp(s, "phone") == 0)
strlcpy(ab->Phone, eq, sizeof(ab->Phone));
else if(stricmp(s, "homepage") == 0)
strlcpy(ab->Homepage, eq, sizeof(ab->Homepage));
else if(stricmp(s, "dob") == 0)
ab->Birthday = atol(eq);
else if(stricmp(s, "picture") == 0)
strlcpy(ab->Photo, eq, sizeof(ab->Photo));
}
s = t;
}
while(t != NULL);
}
MA_FreeEMailStruct(email);
}
}
LEAVE();
}
///
/* Overloaded Methods */
/// OVERLOAD(OM_NEW)
OVERLOAD(OM_NEW)
{
Object *result = NULL;
struct TagItem *tags = inittags(msg);
struct TagItem *tag;
struct ReadMailData *rmData;
LONG hgVertWeight = 5;
LONG tgVertWeight = 100;
LONG agVertWeight = 1;
ENTER();
// get eventually set attributes first
while((tag = NextTagItem((APTR)&tags)) != NULL)
{
switch(tag->ti_Tag)
{
case ATTR(HGVertWeight): hgVertWeight = tag->ti_Data; break;
case ATTR(TGVertWeight): tgVertWeight = tag->ti_Data; break;
}
}
// allocate the readMailData structure
if((rmData = AllocPrivateRMData(NULL, 0)) != NULL)
{
Object *headerGroup;
Object *headerListview;
Object *headerList;
Object *senderImageGroup;
Object *senderImageSpace;
Object *balanceObjectTop;
Object *balanceObjectBottom;
Object *mailBodyGroup;
Object *mailTextObject;
Object *textEditScrollbar;
Object *scrolledAttachmentGroup;
Object *attachmentGroup;
// set some default values for a new readMailGroup
rmData->headerMode = C->ShowHeader;
rmData->senderInfoMode = C->ShowSenderInfo;
rmData->wrapHeaders = C->WrapHeader;
rmData->useTextcolors = C->UseTextColorsRead;
rmData->useTextstyles = C->UseTextStylesRead;
rmData->useFixedFont = C->FixedFontEdit;
// create the scrollbar for the TE.mcc object
textEditScrollbar = ScrollbarObject, End;
// create a balance object we can use between our texteditor
// and the attachment display
balanceObjectBottom = NBalanceObject,
MUIA_ObjectID, MAKE_ID('B','0','0','3'),
MUIA_Balance_Quiet, TRUE,
End;
// create the scrolled group for our attachment display
scrolledAttachmentGroup = HGroup,
MUIA_VertWeight, agVertWeight,
Child, VGroup,
Child, TextObject,
MUIA_Font, MUIV_Font_Tiny,
MUIA_Text_SetMax, TRUE,
MUIA_Text_Contents, tr(MSG_MA_ATTACHMENTS),
MUIA_Text_PreParse, "\033b",
MUIA_Text_Copy, FALSE,
End,
Child, VSpace(0),
End,
Child, ScrollgroupObject,
MUIA_Scrollgroup_FreeHoriz, FALSE,
MUIA_Scrollgroup_AutoBars, TRUE,
MUIA_Scrollgroup_Contents, attachmentGroup = AttachmentGroupObject,
End,
End,
End;
// create the readmailgroup obj
obj = DoSuperNew(cl, obj,
MUIA_Group_Horiz, FALSE,
GroupSpacing(0),
Child, headerGroup = HGroup,
GroupSpacing(0),
MUIA_VertWeight, hgVertWeight,
MUIA_ShowMe, rmData->headerMode != HM_NOHEADER,
Child, headerListview = NListviewObject,
MUIA_NListview_NList, headerList = HeaderListObject,
MUIA_HeaderList_ReadMailData, rmData,
End,
End,
Child, senderImageGroup = VGroup,
GroupSpacing(0),
InnerSpacing(0,0),
MUIA_CycleChain, FALSE,
MUIA_ShowMe, FALSE,
MUIA_Weight, 1,
Child, senderImageSpace = RectangleObject,
MUIA_Weight, 1,
End,
End,
End,
Child, balanceObjectTop = NBalanceObject,
MUIA_ObjectID, MAKE_ID('B','0','0','4'),
MUIA_ShowMe, rmData->headerMode != HM_NOHEADER,
MUIA_Balance_Quiet, TRUE,
End,
Child, mailBodyGroup = VGroup,
MUIA_VertWeight, tgVertWeight,
GroupSpacing(0),
Child, HGroup,
GroupSpacing(0),
Child, mailTextObject = MailTextEditObject,
InputListFrame,
MUIA_TextEditor_Slider, textEditScrollbar,
MUIA_TextEditor_FixedFont, rmData->useFixedFont,
MUIA_TextEditor_DoubleClickHook, &TextEditDoubleClickHook,
MUIA_TextEditor_ImportHook, MUIV_TextEditor_ImportHook_Plain,
MUIA_TextEditor_ExportHook, MUIV_TextEditor_ExportHook_Plain,
MUIA_TextEditor_ReadOnly, TRUE,
MUIA_TextEditor_AutoClip, C->AutoClip,
MUIA_TextEditor_ActiveObjectOnClick, TRUE,
MUIA_CycleChain, TRUE,
End,
Child, textEditScrollbar,
End,
End,
TAG_MORE, inittags(msg));
// check if all necessary data was created
if(obj != NULL &&
textEditScrollbar != NULL &&
attachmentGroup != NULL)
{
GETDATA;
// copy back the temporary object pointers
data->readMailData = rmData;
data->textEditScrollbar = textEditScrollbar;
data->scrolledAttachmentGroup = scrolledAttachmentGroup;
data->attachmentGroup = attachmentGroup;
data->headerGroup = headerGroup;
data->headerListview = headerListview;
data->headerList = headerList;
data->senderImageGroup = senderImageGroup;
data->senderImageSpace = senderImageSpace;
data->balanceObjectTop = balanceObjectTop;
data->balanceObjectBottom = balanceObjectBottom;
data->mailBodyGroup = mailBodyGroup;
data->mailTextObject = mailTextObject;
// prepare the senderInfoHeader list
NewMinList(&data->senderInfoHeaders);
// place our data in the node and add it to the readMailDataList
rmData->readMailGroup = obj;
AddTail((struct List *)&G->readMailDataList, (struct Node *)data->readMailData);
// now we connect some notifies.
DoMethod(data->headerList, MUIM_Notify, MUIA_NList_DoubleClick, MUIV_EveryTime, obj, 1, METHOD(HeaderListDoubleClicked));
result = obj;
}
else
FreeSysObject(ASOT_NODE, rmData);
}
RETURN((IPTR)result);
return (IPTR)result;
}
///
/// OVERLOAD(OM_DISPOSE)
OVERLOAD(OM_DISPOSE)
{
GETDATA;
ULONG result;
ENTER();
// clear the senderInfoHeaders
ClearHeaderList(&data->senderInfoHeaders);
// free the readMailData pointer
if(data->readMailData != NULL)
{
// Remove our readWindowNode and free it afterwards
Remove((struct Node *)data->readMailData);
// don't use FreePrivateRMData()!
FreeSysObject(ASOT_NODE, data->readMailData);
data->readMailData = NULL;
}
// Dispose the attachmentgroup object in case it isn't
// a child of this readmailgroup
if(data->activeAttachmentGroup == FALSE)
{
MUI_DisposeObject(data->balanceObjectBottom);
MUI_DisposeObject(data->scrolledAttachmentGroup);
data->balanceObjectBottom = NULL;
data->scrolledAttachmentGroup = NULL;
data->attachmentGroup = NULL;
}
// signal the super class to dipose as well
result = DoSuperMethodA(cl, obj, msg);
RETURN(result);
return result;
}
///
/// OVERLOAD(OM_GET)
OVERLOAD(OM_GET)
{
GETDATA;
IPTR *store = ((struct opGet *)msg)->opg_Storage;
switch(((struct opGet *)msg)->opg_AttrID)
{
case ATTR(HGVertWeight) : *store = xget(data->headerGroup, MUIA_VertWeight); return TRUE;
case ATTR(TGVertWeight) : *store = xget(data->mailBodyGroup, MUIA_VertWeight); return TRUE;
case ATTR(ReadMailData) : *store = (ULONG)data->readMailData; return TRUE;
case ATTR(DefaultObject): *store = (ULONG)data->mailTextObject; return TRUE;
case ATTR(ActiveObject):
{
Object *actobj = (Object *)xget(_win(obj), MUIA_Window_ActiveObject);
if(actobj == data->headerListview || actobj == data->headerList || actobj == data->mailTextObject)
*store = (ULONG)actobj;
else
*store = (ULONG)NULL;
return TRUE;
}
}
return DoSuperMethodA(cl, obj, msg);
}
///
/// OVERLOAD(OM_SET)
OVERLOAD(OM_SET)
{
GETDATA;
struct TagItem *tags = inittags(msg), *tag;
while((tag = NextTagItem((APTR)&tags)) != NULL)
{
switch(tag->ti_Tag)
{
case ATTR(HGVertWeight):
{
set(data->headerGroup, MUIA_VertWeight, tag->ti_Data);
// make the superMethod call ignore those tags
tag->ti_Tag = TAG_IGNORE;
}
break;
case ATTR(TGVertWeight):
{
set(data->mailBodyGroup, MUIA_VertWeight, tag->ti_Data);
// make the superMethod call ignore those tags
tag->ti_Tag = TAG_IGNORE;
}
break;
}
}
return DoSuperMethodA(cl, obj, msg);
}
///
/// OVERLOAD(MUIM_ContextMenuBuild)
OVERLOAD(MUIM_ContextMenuBuild)
{
GETDATA;
struct MUIP_ContextMenuBuild *mb = (struct MUIP_ContextMenuBuild *)msg;
struct ReadMailData *rmData = data->readMailData;
BOOL hasContent = data->hasContent;
ENTER();
// dispose the old contextMenu if it still exists
if(data->contextMenu != NULL)
{
MUI_DisposeObject(data->contextMenu);
data->contextMenu = NULL;
}
// generate a context menu title now
if(_isinobject(data->headerGroup, mb->mx, mb->my))
{
strlcpy(data->menuTitle, tr(MSG_RE_HEADERDISPLAY), sizeof(data->menuTitle));
data->contextMenu = MenustripObject,
Child, MenuObjectT(data->menuTitle),
Child, MenuitemCheck(tr(MSG_RE_ShortHeaders), NULL, hasContent, rmData->headerMode==HM_SHORTHEADER, FALSE, 0x05, RMEN_HSHORT),
Child, MenuitemCheck(tr(MSG_RE_FullHeaders), NULL, hasContent, rmData->headerMode==HM_FULLHEADER, FALSE, 0x03, RMEN_HFULL),
Child, MenuBarLabel,
Child, MenuitemCheck(tr(MSG_RE_NoSInfo), NULL, hasContent, rmData->senderInfoMode==SIM_OFF, FALSE, 0xE0, RMEN_SNONE),
Child, MenuitemCheck(tr(MSG_RE_SInfo), NULL, hasContent, rmData->senderInfoMode==SIM_DATA, FALSE, 0xD0, RMEN_SDATA),
Child, MenuitemCheck(tr(MSG_RE_SInfoImage), NULL, hasContent, rmData->senderInfoMode==SIM_ALL, FALSE, 0x90, RMEN_SFULL),
Child, MenuitemCheck(tr(MSG_RE_SImageOnly), NULL, hasContent, rmData->senderInfoMode==SIM_IMAGE, FALSE, 0x70, RMEN_SIMAGE),
Child, MenuBarLabel,
Child, MenuitemCheck(tr(MSG_RE_WrapHeader), NULL, hasContent, rmData->wrapHeaders, TRUE, 0, RMEN_WRAPH),
End,
End;
}
else if(rmData->mail != NULL)
{
struct Mail *mail = rmData->mail;
BOOL isRealMail = !isVirtualMail(mail);
BOOL hasAttach = data->activeAttachmentGroup;
BOOL hasPGPKey = rmData->hasPGPKey;
BOOL hasPGPSig = (hasPGPSOldFlag(rmData) || hasPGPSMimeFlag(rmData));
BOOL isPGPEnc = isRealMail && (hasPGPEMimeFlag(rmData) || hasPGPEOldFlag(rmData));
if(hasContent == TRUE)
{
snprintf(data->menuTitle, sizeof(data->menuTitle), "%s: ", tr(MSG_Subject));
strlcat(data->menuTitle, rmData->mail->Subject, 30-strlen(data->menuTitle) > 0 ? 30-strlen(data->menuTitle) : 0);
strlcat(data->menuTitle, "...", sizeof(data->menuTitle));
}
else
strlcpy(data->menuTitle, tr(MSG_RE_MAILDETAILS), sizeof(data->menuTitle));
data->contextMenu = MenustripObject,
Child, MenuObjectT(data->menuTitle),
Child, Menuitem(tr(MSG_MA_MREPLY), NULL, hasContent, FALSE, RMEN_REPLY),
Child, MenuitemObject,
MUIA_Menuitem_Title, tr(MSG_MA_MFORWARD),
MUIA_Menuitem_CopyStrings, FALSE,
MUIA_Menuitem_Enabled, hasContent,
Child, Menuitem(tr(MSG_MA_MFORWARD_ATTACH), NULL, hasContent, FALSE, RMEN_FORWARD_ATTACH),
Child, Menuitem(tr(MSG_MA_MFORWARD_INLINE), NULL, hasContent, FALSE, RMEN_FORWARD_INLINE),
End,
Child, Menuitem(tr(MSG_MA_MMOVE), NULL, hasContent && isRealMail, FALSE, RMEN_MOVE),
Child, Menuitem(tr(MSG_MA_MCOPY), NULL, hasContent, FALSE, RMEN_COPY),
Child, MenuBarLabel,
Child, Menuitem(tr(MSG_RE_MDisplay), NULL, hasContent, FALSE, RMEN_DISPLAY),
Child, Menuitem(tr(MSG_MA_Save), NULL, hasContent, FALSE, RMEN_SAVE),
Child, Menuitem(tr(MSG_Print), NULL, hasContent, FALSE, RMEN_PRINT),
Child, Menuitem(tr(MSG_MA_MDelete), NULL, hasContent && isRealMail, TRUE, RMEN_DELETE),
Child, MenuBarLabel,
Child, Menuitem(tr(MSG_RE_SEARCH), NULL, hasContent, FALSE, RMEN_SEARCH),
Child, Menuitem(tr(MSG_RE_SEARCH_AGAIN), NULL, hasContent, FALSE, RMEN_SEARCHAGAIN),
Child, MenuBarLabel,
Child, MenuitemObject,
MUIA_Menuitem_Title, tr(MSG_Attachments),
MUIA_Menuitem_CopyStrings, FALSE,
MUIA_Menuitem_Enabled, hasContent && hasAttach,
Child, Menuitem(tr(MSG_RE_SaveAll), NULL, hasContent && hasAttach, FALSE, RMEN_DETACH),
Child, Menuitem(tr(MSG_MA_DELETEATT), NULL, hasContent && isRealMail && hasAttach, FALSE, RMEN_DELETEATT),
End,
Child, MenuBarLabel,
Child, MenuitemObject,
MUIA_Menuitem_Title, "PGP",
MUIA_Menuitem_CopyStrings, FALSE,
MUIA_Menuitem_Enabled, hasContent && (hasPGPKey || hasPGPSig || isPGPEnc),
Child, Menuitem(tr(MSG_RE_ExtractKey), NULL, hasPGPKey, FALSE, RMEN_EXTKEY),
Child, Menuitem(tr(MSG_RE_SigCheck), NULL, hasPGPSig, FALSE, RMEN_CHKSIG),
Child, Menuitem(tr(MSG_RE_SaveDecrypted), NULL, isPGPEnc, FALSE, RMEN_SAVEDEC),
End,
Child, MenuBarLabel,
Child, MenuitemCheck(tr(MSG_RE_FixedFont), NULL, hasContent, rmData->useFixedFont, TRUE, 0, RMEN_FFONT),
Child, MenuitemCheck(tr(MSG_RE_TEXTCOLORS), NULL, hasContent, rmData->useTextcolors, TRUE, 0, RMEN_TCOLOR),
Child, MenuitemCheck(tr(MSG_RE_Textstyles), NULL, hasContent, rmData->useTextstyles, TRUE, 0, RMEN_TSTYLE),
End,
End;
}
RETURN((IPTR)data->contextMenu);
return (IPTR)data->contextMenu;
}
///
/// OVERLOAD(MUIM_ContextMenuChoice)
OVERLOAD(MUIM_ContextMenuChoice)
{
GETDATA;
struct MUIP_ContextMenuChoice *m = (struct MUIP_ContextMenuChoice *)msg;
struct ReadMailData *rmData = data->readMailData;
BOOL updateHeader = FALSE;
BOOL updateText = FALSE;
BOOL checked = xget(m->item, MUIA_Menuitem_Checked);
IPTR result = 0;
switch(xget(m->item, MUIA_UserData))
{
case RMEN_REPLY: DoMethod(_app(obj), MUIM_CallHook, &MA_NewMessageHook, NMM_REPLY, 0); break;
case RMEN_FORWARD_ATTACH: DoMethod(_app(obj), MUIM_CallHook, &MA_NewMessageHook, NMM_FORWARD_ATTACH, 0); break;
case RMEN_FORWARD_INLINE: DoMethod(_app(obj), MUIM_CallHook, &MA_NewMessageHook, NMM_FORWARD_INLINE, 0); break;
case RMEN_MOVE: DoMethod(_app(obj), MUIM_CallHook, &MA_MoveMessageHook); break;
case RMEN_COPY: DoMethod(_app(obj), MUIM_CallHook, &MA_CopyMessageHook); break;
case RMEN_DISPLAY: DoMethod(obj, METHOD(DisplayMailRequest)); break;
case RMEN_SAVE: DoMethod(obj, METHOD(SaveMailRequest)); break;
case RMEN_PRINT: DoMethod(obj, METHOD(PrintMailRequest)); break;
case RMEN_DELETE: DoMethod(obj, METHOD(DeleteMail)); break;
case RMEN_DETACH: DoMethod(obj, METHOD(SaveAllAttachments)); break;
case RMEN_DELETEATT: DoMethod(obj, METHOD(DeleteAttachmentsRequest)); break;
case RMEN_EXTKEY: DoMethod(obj, METHOD(ExtractPGPKey)); break;
case RMEN_CHKSIG: DoMethod(obj, METHOD(CheckPGPSignature), TRUE); break;
case RMEN_SAVEDEC: DoMethod(obj, METHOD(SaveDecryptedMail)); break;
case RMEN_SEARCH: DoMethod(obj, METHOD(Search), MUIF_NONE); break;
case RMEN_SEARCHAGAIN: DoMethod(obj, METHOD(Search), MUIF_ReadMailGroup_Search_Again); break;
// now we check the checkmarks of the
// context-menu
case RMEN_HSHORT: rmData->headerMode = HM_SHORTHEADER; updateHeader = TRUE; break;
case RMEN_HFULL: rmData->headerMode = HM_FULLHEADER; updateHeader = TRUE; break;
case RMEN_SNONE: rmData->senderInfoMode = SIM_OFF; updateHeader = TRUE; break;
case RMEN_SDATA: rmData->senderInfoMode = SIM_DATA; updateHeader = TRUE; break;
case RMEN_SFULL: rmData->senderInfoMode = SIM_ALL; updateHeader = TRUE; break;
case RMEN_SIMAGE: rmData->senderInfoMode = SIM_IMAGE; updateHeader = TRUE; break;
case RMEN_WRAPH: rmData->wrapHeaders = checked; updateHeader = TRUE; break;
case RMEN_FFONT: rmData->useFixedFont = checked; updateText = TRUE; break;
case RMEN_TCOLOR: rmData->useTextcolors = checked; updateText = TRUE; break;
case RMEN_TSTYLE: rmData->useTextstyles = checked; updateText = TRUE; break;
default:
result = DoSuperMethodA(cl, obj, msg);
}
if(updateText == TRUE)
DoMethod(obj, METHOD(ReadMail), rmData->mail, MUIF_ReadMailGroup_ReadMail_UpdateOnly|MUIF_ReadMailGroup_ReadMail_UpdateTextOnly);
else if(updateHeader == TRUE)
DoMethod(obj, METHOD(UpdateHeaderDisplay), MUIF_ReadMailGroup_ReadMail_UpdateOnly);
RETURN(result);
return result;
}
///
/* Public Methods */
/// DECLARE(Clear)
DECLARE(Clear) // ULONG flags
{
GETDATA;
ENTER();
if(data->hasContent == TRUE)
{
// clear all relevant GUI elements
DoMethod(data->headerList, MUIM_NList_Clear);
if(hasKeepTextFlag(msg->flags) == FALSE)
set(data->mailTextObject, MUIA_TextEditor_Contents, "");
// cleanup the senderInfoHeaders list
ClearHeaderList(&data->senderInfoHeaders);
// cleanup the SenderImage field as well
if(DoMethod(data->senderImageGroup, MUIM_Group_InitChange))
{
if(data->senderImage != NULL)
{
DoMethod(data->senderImageGroup, OM_REMMEMBER, data->senderImage);
MUI_DisposeObject(data->senderImage);
data->senderImage = NULL;
}
DoMethod(data->senderImageGroup, MUIM_Group_ExitChange);
}
set(data->senderImageGroup, MUIA_ShowMe, FALSE);
// hide the attachmentGroup
if(hasKeepAttachmentGroupFlag(msg->flags) == FALSE)
HideAttachmentGroup(data);
}
CleanupReadMailData(data->readMailData, FALSE);
data->hasContent = FALSE;
RETURN(0);
return 0;
}
///
/// DECLARE(ReadMail)
DECLARE(ReadMail) // struct Mail *mail, ULONG flags
{
GETDATA;
struct Mail *mail = msg->mail;
struct Folder *folder = mail->Folder;
struct ReadMailData *rmData = data->readMailData;
BOOL result = FALSE; // error per default
ENTER();
// before we actually start loading data into our readmailGroup
// we have to make sure we didn't actually have something displayed
// which should get freed first
if(!hasUpdateTextOnlyFlag(msg->flags))
{
DoMethod(obj, METHOD(Clear), MUIF_ReadMailGroup_Clear_KeepAttachmentGroup |
(hasUpdateOnlyFlag(msg->flags) ? MUIF_ReadMailGroup_Clear_KeepText : 0));
}
// set the passed mail as the current mail read by our ReadMailData
// structure
rmData->mail = mail;
setFlag(rmData->parseFlags, PM_ALL);
// load the message now
if(RE_LoadMessage(rmData) == TRUE)
{
struct BusyNode *busy;
char *cmsg;
busy = BusyBegin(BUSY_TEXT);
BusyText(busy, tr(MSG_BusyDisplaying), "");
// now read in the Mail in a temporary buffer
if((cmsg = RE_ReadInMessage(rmData, RIM_READ)) != NULL)
{
char *body;
// the first operation should be: check if the mail is a multipart mail and if so we tell
// our attachment group about it and read the partlist or otherwise a previously opened
// attachmentgroup may still hold some references to our already deleted parts
if(isMultiPartMail(mail))
{
if(DoMethod(data->attachmentGroup, MUIM_AttachmentGroup_Refresh, rmData->firstPart) > 0)
{
// make sure we show the attachmentGroup
ShowAttachmentGroup(data);
}
else
{
// make sure the attachmentGroup is hidden
HideAttachmentGroup(data);
// if this mail was/is a multipart mail but no valid part was
// found we can remove the multipart flag again
if(isMP_MixedMail(mail))
{
// clear the multipart/mixed flag
clearFlag(mail->mflags, MFLAG_MP_MIXED);
// update the status bar of an eventually existing read window.
if(rmData->readWindow != NULL)
DoMethod(rmData->readWindow, MUIM_ReadWindow_UpdateStatusBar);
// if the mail is no virtual mail we can also
// refresh the maillist depending information
if(!isVirtualMail(mail))
{
// flag folder as modified and redraw the mail list entry
setFlag(mail->Folder->Flags, FOFL_MODIFY);
DoMethod(G->MA->GUI.PG_MAILLIST, MUIM_MainMailListGroup_RedrawMail, mail);
}
}
}
}
else
HideAttachmentGroup(data);
// make sure the header display is also updated correctly.
if(hasUpdateTextOnlyFlag(msg->flags) == FALSE)
DoMethod(obj, METHOD(UpdateHeaderDisplay), msg->flags);
// before we can put the message body into the TextEditor, we have to preparse the text and
// try to set some styles, as we don't use the buggy import hooks of TextEditor anymore and
// are more powerful this way.
if(rmData->useTextstyles == TRUE || rmData->useTextcolors == TRUE)
body = ParseEmailText(cmsg, TRUE, rmData->useTextstyles, rmData->useTextcolors);
else
body = cmsg;
xset(data->mailTextObject, MUIA_TextEditor_FixedFont, rmData->useFixedFont,
MUIA_TextEditor_Contents, body);
// free the parsed text afterwards as the texteditor has copied it anyway.
if(rmData->useTextstyles == TRUE || rmData->useTextcolors == TRUE)
dstrfree(body);
dstrfree(cmsg);
// start the macro
if(rmData->readWindow != NULL)
{
char numStr[SIZE_SMALL];
snprintf(numStr, sizeof(numStr), "%d", (int)xget(rmData->readWindow, MUIA_ReadWindow_Num));
DoMethod(_app(obj), MUIM_YAMApplication_StartMacro, MACRO_READ, numStr);
}
else
DoMethod(_app(obj), MUIM_YAMApplication_StartMacro, MACRO_READ, "-1");
// if the displayed mail isn't a virtual one we flag it as read now
if(!isVirtualMail(mail) &&
(hasStatusNew(mail) || !hasStatusRead(mail)))
{
// first, depending on the PGP status we either check an existing
// PGP signature or not, but only for new mails. A second check must
// be triggered by the user via the menus.
// This has to be done before we eventually set the mail state to "read"
// which will in turn rename the mail file, which will make PGP fail,
// because it cannot read the desired file anymore because of the delayed
// status change.
if((hasPGPSOldFlag(rmData) || hasPGPSMimeFlag(rmData))
&& hasStatusNew(mail))
{
DoMethod(obj, METHOD(CheckPGPSignature), FALSE);
}
// second, depending on the local delayedStatusChange and the global
// configuration settings for the mail status change interval we either
// start the timer that will change the mail status to read after a
// specified time has passed or we change it immediatley here
if(hasStatusChangeDelayFlag(msg->flags) &&
C->StatusChangeDelayOn == TRUE && C->StatusChangeDelay > 0)
{
// start the timer event. Please note that the timer event might be
// canceled by the MA_ChangeSelected() function when the next mail
// will be selected.
RestartTimer(TIMER_READSTATUSUPDATE, 0, (C->StatusChangeDelay-500)*1000, FALSE);
}
else
{
if(!isOutgoingFolder(mail->Folder) && !isDraftsFolder(mail->Folder))
{
// set mail status to OLD in any non-outgoing folder
setStatusToRead(mail);
DisplayStatistics(folder, TRUE);
}
}
// check for any MDN and allow to reply to it.
if(isSendMDNMail(mail))
RE_ProcessMDN(MDN_MODE_DISPLAY, mail, FALSE, FALSE, _win(obj));
}
// everything worked out fine so lets return it
result = TRUE;
}
else
{
char mailfile[SIZE_PATHFILE];
GetMailFile(mailfile, sizeof(mailfile), NULL, mail);
ER_NewError(tr(MSG_ER_ErrorReadMailfile), mailfile);
}
BusyEnd(busy);
}
else
{
// check first if the mail file exists and if not we have to exit with an error
if(FileExists(mail->MailFile) == FALSE)
ER_NewError(tr(MSG_ER_CantOpenFile), mail->MailFile);
}
// make sure we know that there is some content
data->hasContent = TRUE;
RETURN(result);
return result;
}
///
/// DECLARE(UpdateHeaderDisplay)
DECLARE(UpdateHeaderDisplay) // ULONG flags
{
GETDATA;
struct ReadMailData *rmData = data->readMailData;
struct Person *from = &rmData->mail->From;
struct ABookNode *ab = NULL;
struct ABookNode abtmpl;
BOOL foundIdentity;
BOOL dispheader;
int hits;
ENTER();
// make sure the headerList is cleared if necessary
if(data->hasContent == TRUE)
DoMethod(data->headerList, MUIM_NList_Clear);
// then we check whether we should disable the headerList display
// or not.
dispheader = (rmData->headerMode != HM_NOHEADER);
set(data->headerGroup, MUIA_ShowMe, dispheader);
set(data->balanceObjectTop, MUIA_ShowMe, dispheader);
set(data->headerList, MUIA_NList_Quiet, TRUE);
// we first go through the headerList of our first Part, which should in fact
// be the headerPart
if(dispheader == TRUE && rmData->firstPart != NULL && rmData->firstPart->headerList != NULL)
{
struct HeaderNode *hdrNode;
// Now we process the read headers to set all flags accordingly
IterateList(rmData->firstPart->headerList, struct HeaderNode *, hdrNode)
{
// now we use MatchNoCase() to find out if we should include that headerNode
// in our headerList or not
if(rmData->headerMode == HM_SHORTHEADER)
dispheader = MatchNoCase(hdrNode->name, C->ShortHeaders);
else
dispheader = (rmData->headerMode == HM_FULLHEADER);
if(dispheader == TRUE)
{
// we simply insert the whole headerNode and split the display later in our HeaderDisplayHook
DoMethod(data->headerList, MUIM_NList_InsertSingleWrap, hdrNode,
MUIV_NList_Insert_Sorted,
rmData->wrapHeaders ? WRAPCOL1 : NOWRAP, ALIGN_LEFT);
}
}
}
// we search in the address for the matching entry by just comparing
// the email addresses or otherwise we can't be certain that the found
// addressbook entry really belongs to the sender address of the mail.
hits = SearchABook(&G->abook, from->Address, ASM_ADDRESS|ASM_USER, &ab);
// extract the realname/email address and
// X-SenderInfo information from the mail into a temporary ABEntry
ExtractSenderInfo(rmData->mail, &abtmpl);
// we have to search through our identities and
// if the email address matches the from address
// we simply reuse that information
if(FindUserIdentityByAddress(&C->userIdentityList, from->Address) != NULL)
{
// if there is no addressbook entry for the user
// we use the template one but erase the photo link
if(ab == NULL)
{
ab = &abtmpl;
*ab->Photo = '\0';
}
foundIdentity = TRUE;
}
else
foundIdentity = FALSE;
if(foundIdentity == FALSE)
{
if(ab != NULL)
RE_UpdateSenderInfo(ab, &abtmpl);
else
{
if(!hasUpdateOnlyFlag(msg->flags) &&
C->AddToAddrbook > 0)
{
if((ab = RE_AddToAddrbook(_win(obj), &abtmpl)) == NULL)
{
ab = &abtmpl;
*ab->Photo = '\0';
}
}
else
{
ab = &abtmpl;
*ab->Photo = '\0';
}
}
}
if(rmData->senderInfoMode != SIM_OFF)
{
if(rmData->senderInfoMode != SIM_IMAGE)
{
if(hits == 1 || ab->type == ABNT_LIST)
{
struct HeaderNode *newNode;
char dateStr[SIZE_SMALL];
// make sure we cleaned up the senderInfoHeader List beforehand
ClearHeaderList(&data->senderInfoHeaders);
if(*ab->RealName != '\0' && (newNode = AllocHeaderNode()) != NULL)
{
dstrcpy(&newNode->name, MUIX_I);
dstrcat(&newNode->name, StripUnderscore(tr(MSG_EA_RealName)));
dstrcpy(&newNode->content, ab->RealName);
AddTail((struct List *)&data->senderInfoHeaders, (struct Node *)newNode);
DoMethod(data->headerList, MUIM_NList_InsertSingle, newNode, MUIV_NList_Insert_Sorted);
}
if(*ab->Street != '\0' && (newNode = AllocHeaderNode()) != NULL)
{
dstrcpy(&newNode->name, MUIX_I);
dstrcat(&newNode->name, StripUnderscore(tr(MSG_EA_Street)));
dstrcpy(&newNode->content, ab->Street);
AddTail((struct List *)&data->senderInfoHeaders, (struct Node *)newNode);
DoMethod(data->headerList, MUIM_NList_InsertSingle, newNode, MUIV_NList_Insert_Sorted);
}
if(*ab->City != '\0' && (newNode = AllocHeaderNode()) != NULL)
{
dstrcpy(&newNode->name, MUIX_I);
dstrcat(&newNode->name, StripUnderscore(tr(MSG_EA_City)));
dstrcpy(&newNode->content, ab->City);
AddTail((struct List *)&data->senderInfoHeaders, (struct Node *)newNode);
DoMethod(data->headerList, MUIM_NList_InsertSingle, newNode, MUIV_NList_Insert_Sorted);
}
if(*ab->Country != '\0' && (newNode = AllocHeaderNode()) != NULL)
{
dstrcpy(&newNode->name, MUIX_I);
dstrcat(&newNode->name, StripUnderscore(tr(MSG_EA_Country)));
dstrcpy(&newNode->content, ab->Country);
AddTail((struct List *)&data->senderInfoHeaders, (struct Node *)newNode);
DoMethod(data->headerList, MUIM_NList_InsertSingle, newNode, MUIV_NList_Insert_Sorted);
}
if(*ab->Phone != '\0' && (newNode = AllocHeaderNode()) != NULL)
{
dstrcpy(&newNode->name, MUIX_I);
dstrcat(&newNode->name, StripUnderscore(tr(MSG_EA_Phone)));
dstrcpy(&newNode->content, ab->Phone);
AddTail((struct List *)&data->senderInfoHeaders, (struct Node *)newNode);
DoMethod(data->headerList, MUIM_NList_InsertSingle, newNode, MUIV_NList_Insert_Sorted);
}
if(BirthdayToString(ab->Birthday, dateStr, sizeof(dateStr)) == TRUE && (newNode = AllocHeaderNode()) != NULL)
{
dstrcpy(&newNode->name, MUIX_I);
dstrcat(&newNode->name, StripUnderscore(tr(MSG_EA_DOB)));
dstrcpy(&newNode->content, dateStr);
AddTail((struct List *)&data->senderInfoHeaders, (struct Node *)newNode);
DoMethod(data->headerList, MUIM_NList_InsertSingle, newNode, MUIV_NList_Insert_Sorted);
}
if(*ab->Comment != '\0' && (newNode = AllocHeaderNode()) != NULL)
{
dstrcpy(&newNode->name, MUIX_I);
dstrcat(&newNode->name, StripUnderscore(tr(MSG_EA_Description)));
dstrcpy(&newNode->content, ab->Comment);
AddTail((struct List *)&data->senderInfoHeaders, (struct Node *)newNode);
DoMethod(data->headerList, MUIM_NList_InsertSingle, newNode, MUIV_NList_Insert_Sorted);
}
if(*ab->Homepage != '\0' && (newNode = AllocHeaderNode()) != NULL)
{
dstrcpy(&newNode->name, MUIX_I);
dstrcat(&newNode->name, StripUnderscore(tr(MSG_EA_Homepage)));
dstrcpy(&newNode->content, ab->Homepage);
AddTail((struct List *)&data->senderInfoHeaders, (struct Node *)newNode);
DoMethod(data->headerList, MUIM_NList_InsertSingle, newNode, MUIV_NList_Insert_Sorted);
}
}
}
// now we check if that addressbook entry has a specified
// photo entry and if so we go and show that sender picture
if((rmData->senderInfoMode == SIM_ALL || rmData->senderInfoMode == SIM_IMAGE) &&
(data->senderImage != NULL || FileExists(ab->Photo)))
{
if(DoMethod(data->senderImageGroup, MUIM_Group_InitChange))
{
if(data->senderImage != NULL)
{
DoMethod(data->senderImageGroup, OM_REMMEMBER, data->senderImage);
MUI_DisposeObject(data->senderImage);
data->senderImage = NULL;
}
if(FileExists(ab->Photo) &&
(data->senderImage = ImageAreaObject,
MUIA_Weight, 100,
MUIA_ImageArea_ID, from->Address,
MUIA_ImageArea_Filename, ab->Photo,
MUIA_ImageArea_MaxHeight, 64,
MUIA_ImageArea_MaxWidth, 64,
MUIA_ImageArea_NoMinHeight, TRUE,
MUIA_ImageArea_ShowLabel, FALSE,
End))
{
D(DBF_GUI, "SenderPicture found: %s %ld %ld", ab->Photo, xget(data->headerList, MUIA_Width), xget(data->headerList, MUIA_Height));
DoMethod(data->senderImageGroup, OM_ADDMEMBER, data->senderImage);
// resort the group so that the space object is at the bottom of it.
DoMethod(data->senderImageGroup, MUIM_Group_Sort, data->senderImage,
data->senderImageSpace,
NULL);
}
DoMethod(data->senderImageGroup, MUIM_Group_ExitChange);
}
}
}
set(data->senderImageGroup, MUIA_ShowMe, (rmData->senderInfoMode == SIM_ALL ||
rmData->senderInfoMode == SIM_IMAGE) &&
(data->senderImage != NULL));
// enable the headerList again
set(data->headerList, MUIA_NList_Quiet, FALSE);
RETURN(0);
return 0;
}
///
/// DECLARE(CheckPGPSignature)
DECLARE(CheckPGPSignature) // ULONG forceRequester
{
GETDATA;
BOOL result = FALSE;
struct ReadMailData *rmData = data->readMailData;
ENTER();
// Don't try to use PGP if it's not installed
if(G->PGPVersion >= 0)
{
if(hasPGPSOldFlag(rmData) || hasPGPSMimeFlag(rmData))
{
char options[SIZE_LARGE];
int error;
struct Part *part;
struct Part *letterPart = NULL;
struct Part *pgpPart = NULL;
// find the letter and signature parts
part = rmData->firstPart;
do
{
if(letterPart == NULL && part->Nr == rmData->letterPartNum)
{
letterPart = part;
}
else if(pgpPart == NULL && stricmp(part->ContentType, "application/pgp-signature") == 0)
{
pgpPart = part;
}
part = part->Next;
}
while(part != NULL);
if(letterPart != NULL && pgpPart != NULL)
{
// decode the letter part first, otherwise PGP might want to check
// a still encoded file which definitely will fail.
RE_DecodePart(letterPart);
snprintf(options, sizeof(options), (G->PGPVersion == 5) ? "%s -o %s +batchmode=1 +force +language=us" : "%s %s +bat +f +lang=en", pgpPart->Filename, letterPart->Filename);
error = PGPCommand((G->PGPVersion == 5) ? "pgpv": "pgp", options, KEEPLOG);
if(error > 0)
setFlag(rmData->signedFlags, PGPS_BADSIG);
if(error >= 0)
{
// running PGP was successful, but that still doesn't mean that the check itself succeeded!
RE_GetSigFromLog(rmData, NULL);
result = TRUE;
}
if(result == TRUE && (hasPGPSBadSigFlag(rmData) || msg->forceRequester == TRUE))
{
char buffer[SIZE_LARGE];
strlcpy(buffer, hasPGPSBadSigFlag(rmData) ? tr(MSG_RE_BadSig) : tr(MSG_RE_GoodSig), sizeof(buffer));
if(hasPGPSAddressFlag(rmData))
{
strlcat(buffer, tr(MSG_RE_SigFrom), sizeof(buffer));
strlcat(buffer, rmData->sigAuthor, sizeof(buffer));
}
MUI_Request(_app(obj), _win(obj), MUIF_NONE, tr(MSG_RE_SigCheck), tr(MSG_OkayReq), buffer);
}
}
}
}
RETURN(result);
return result;
}
///
/// DECLARE(ExtractPGPKey)
// Extracts public PGP key from the current message
DECLARE(ExtractPGPKey)
{
GETDATA;
struct ReadMailData *rmData = data->readMailData;
struct Mail *mail = rmData->mail;
char mailfile[SIZE_PATHFILE];
char fullfile[SIZE_PATHFILE];
char options[SIZE_PATHFILE];
GetMailFile(mailfile, sizeof(mailfile), NULL, mail);
if(StartUnpack(mailfile, fullfile, mail->Folder) != NULL)
{
snprintf(options, sizeof(options), (G->PGPVersion == 5) ? "-a %s +batchmode=1 +force" : "-ka %s +bat +f", fullfile);
PGPCommand((G->PGPVersion == 5) ? "pgpk" : "pgp", options, 0);
FinishUnpack(fullfile);
}
return 0;
}
///
/// DECLARE(SaveDisplay)
// Saves current message as displayed
DECLARE(SaveDisplay) // FILE *fileHandle
{
GETDATA;
struct ReadMailData *rmData = data->readMailData;
FILE *fh = msg->fileHandle;
char *ptr;
if(rmData->headerMode != HM_NOHEADER)
{
int i;
struct MUI_NList_GetEntryInfo res;
fputs("\033[3m", fh);
for(i=0;;i++)
{
struct HeaderNode *curNode;
res.pos = MUIV_NList_GetEntryInfo_Line;
res.line = i;
DoMethod(data->headerList, MUIM_NList_GetEntryInfo, &res);
if((curNode = (struct HeaderNode *)res.entry) != NULL)
{
char *name = curNode->name;
char *content = curNode->content;
// skip the italic style if present
if(strncmp(name, MUIX_I, strlen(MUIX_I)) == 0)
name += strlen(MUIX_I);
fprintf(fh, "%s: %s\n", name, content);
}
else
break;
}
fputs("\033[23m\n", fh);
}
ptr = (char *)DoMethod(data->mailTextObject, MUIM_TextEditor_ExportText);
for(; *ptr; ptr++)
{
if(*ptr == '\033')
{
switch(*++ptr)
{
case 'u':
fputs("\033[4m", fh);
break;
case 'b':
fputs("\033[1m", fh);
break;
case 'i':
fputs("\033[3m", fh);
break;
case 'n':
fputs("\033[0m", fh);
break;
case 'h':
break;
case '[':
{
if(!strncmp(ptr, "[s:18]", 6))
{
fputs("===========================================================", fh);
ptr += 5;
}
else if(!strncmp(ptr, "[s:2]", 5))
{
fputs("-----------------------------------------------------------", fh);
ptr += 4;
}
}
break;
case 'p':
while(*ptr != ']' && *ptr && *ptr != '\n')
ptr++;
break;
}
}
else
fputc(*ptr, fh);
}
return 0;
}
///
/// DECLARE(SaveDecryptedMail)
// Saves decrypted version of a PGP message
DECLARE(SaveDecryptedMail)
{
GETDATA;
struct ReadMailData *rmData = data->readMailData;
struct Mail *mail = rmData->mail;
struct Folder *folder = mail->Folder;
int choice;
if(folder == NULL)
return 0;
if((choice = MUI_Request(_app(obj), rmData->readWindow, MUIF_NONE, tr(MSG_RE_SaveDecrypted),
tr(MSG_RE_SaveDecGads),
tr(MSG_RE_SaveDecReq))) != 0)
{
struct Compose comp;
char mfilePath[SIZE_PATHFILE];
memset(&comp, 0, sizeof(struct Compose));
if(MA_NewMailFile(folder, mfilePath, sizeof(mfilePath)) == TRUE &&
(comp.FH = fopen(mfilePath, "w")) != NULL)
{
struct ExtendedMail *email;
setvbuf(comp.FH, NULL, _IOFBF, SIZE_FILEBUF);
comp.Mode = NMM_SAVEDEC;
comp.refMail = mail;
if((comp.FirstPart = NewMIMEpart(NULL)) != NULL)
{
struct WritePart *p1 = comp.FirstPart;
p1->Filename = rmData->firstPart->Next->Filename;
WriteOutMessage(&comp);
FreePartsList(p1, TRUE);
}
fclose(comp.FH);
if((email = MA_ExamineMail(folder, FilePart(mfilePath), TRUE)) != NULL)
{
struct Mail *newmail;
// lets set some values depending on the original message
email->Mail.sflags = mail->sflags;
memcpy(&email->Mail.transDate, &mail->transDate, sizeof(email->Mail.transDate));
// add the mail to the folder now
if((newmail = CloneMail(&email->Mail)) != NULL)
{
AddMailToFolder(newmail, folder);
// if this was a compressed/encrypted folder we need to pack the mail now
if(folder->Mode > FM_SIMPLE)
RepackMailFile(newmail, -1, NULL);
if(GetCurrentFolder() == folder)
DoMethod(G->MA->GUI.PG_MAILLIST, MUIM_NList_InsertSingle, newmail, MUIV_NList_Insert_Sorted);
if(choice == 2)
{
MA_DeleteSingle(mail, DELF_UPDATE_APPICON|DELF_CHECK_CONNECTIONS);
// erase the old pointer as this has been free()ed by MA_DeleteSingle()
rmData->mail = NULL;
DoMethod(rmData->readWindow, MUIM_ReadWindow_ReadMail, newmail);
}
}
MA_FreeEMailStruct(email);
}
}
else
{
ER_NewError(tr(MSG_ER_CANNOT_CREATE_MAIL_FILE), mfilePath);
}
}
return 0;
}
///
/// DECLARE(SaveAllAttachments)
DECLARE(SaveAllAttachments)
{
GETDATA;
return DoMethod(data->attachmentGroup, MUIM_AttachmentGroup_SaveAll);
}
///
/// DECLARE(ActivateMailText)
// sets the mailTextObject as the active object of the window
DECLARE(ActivateMailText)
{
GETDATA;
struct ReadMailData *rmData = data->readMailData;
if(rmData->readWindow != NULL)
set(rmData->readWindow, MUIA_Window_DefaultObject, data->mailTextObject);
return 0;
}
///
/// DECLARE(SaveMailRequest)
// Saves the current message or an attachment to disk
DECLARE(SaveMailRequest)
{
GETDATA;
struct ReadMailData *rmData = data->readMailData;
struct Part *part;
struct TempFile *tf;
ENTER();
if((part = AttachRequest(tr(MSG_RE_SaveMessage),
tr(MSG_RE_SelectSavePart),
tr(MSG_RE_SaveGad),
tr(MSG_Cancel), ATTREQ_SAVE|ATTREQ_MULTI, rmData)) != NULL)
{
struct BusyNode *busy;
busy = BusyBegin(BUSY_TEXT);
BusyText(busy, tr(MSG_BusyDecSaving), "");
for(; part; part = part->NextSelected)
{
switch(part->Nr)
{
case PART_ORIGINAL:
{
RE_Export(rmData, rmData->readFile, "", "", 0, FALSE, FALSE, IntMimeTypeArray[MT_ME_EMAIL].ContentType);
}
break;
case PART_ALLTEXT:
{
if((tf = OpenTempFile("w")) != NULL)
{
DoMethod(obj, METHOD(SaveDisplay), tf->FP);
fclose(tf->FP);
tf->FP = NULL;
RE_Export(rmData, tf->Filename, "", "", 0, FALSE, FALSE, IntMimeTypeArray[MT_TX_PLAIN].ContentType);
CloseTempFile(tf);
}
}
break;
default:
{
char *suggestedFileName;
RE_DecodePart(part);
suggestedFileName = SuggestPartFileName(part);
RE_Export(rmData, part->Filename, "",
suggestedFileName, part->Nr,
FALSE, FALSE, part->ContentType);
free(suggestedFileName);
}
}
}
BusyEnd(busy);
}
RETURN(0);
return 0;
}
///
/// DECLARE(PrintMailRequest)
DECLARE(PrintMailRequest)
{
GETDATA;
struct ReadMailData *rmData = data->readMailData;
struct Part *part;
struct TempFile *prttmp;
ENTER();
if((part = AttachRequest(tr(MSG_RE_PrintMsg),
tr(MSG_RE_SelectPrintPart),
tr(MSG_RE_PrintGad),
tr(MSG_Cancel), ATTREQ_PRINT|ATTREQ_MULTI, rmData)) != NULL)
{
struct BusyNode *busy;
busy = BusyBegin(BUSY_TEXT);
BusyText(busy, tr(MSG_BusyDecPrinting), "");
for(; part; part = part->NextSelected)
{
switch(part->Nr)
{
case PART_ORIGINAL:
RE_PrintFile(rmData->readFile, _win(obj));
break;
case PART_ALLTEXT:
{
if((prttmp = OpenTempFile("w")) != NULL)
{
DoMethod(obj, METHOD(SaveDisplay), prttmp->FP);
fclose(prttmp->FP);
prttmp->FP = NULL;
RE_PrintFile(prttmp->Filename, _win(obj));
CloseTempFile(prttmp);
}
}
break;
default:
RE_PrintFile(part->Filename, _win(obj));
}
}
BusyEnd(busy);
}
RETURN(0);
return 0;
}
///
/// DECLARE(DisplayMailRequest)
DECLARE(DisplayMailRequest)
{
GETDATA;
struct ReadMailData *rmData = data->readMailData;
struct Part *part;
ENTER();
if((part = AttachRequest(tr(MSG_RE_DisplayMsg),
tr(MSG_RE_SelectDisplayPart),
tr(MSG_RE_DisplayGad),
tr(MSG_Cancel), ATTREQ_DISP|ATTREQ_MULTI, rmData)) != NULL)
{
struct BusyNode *busy;
busy = BusyBegin(BUSY_TEXT);
BusyText(busy, tr(MSG_BusyDecDisplaying), "");
for(; part; part = part->NextSelected)
{
RE_DecodePart(part);
switch(part->Nr)
{
case PART_ORIGINAL:
RE_DisplayMIME(rmData->readFile, NULL, "text/plain", TRUE);
break;
default:
{
char *fileName;
// get the suggested filename for the mail part
fileName = SuggestPartFileName(part);
RE_DisplayMIME(part->Filename, fileName,
part->ContentType, isPrintable(part));
free(fileName);
}
break;
}
}
BusyEnd(busy);
}
RETURN(0);
return 0;
}
///
/// DECLARE(DeleteMail)
DECLARE(DeleteMail)
{
GETDATA;
struct ReadMailData *rmData = data->readMailData;
struct Mail *mail = rmData->mail;
struct Folder *folder = mail->Folder;
ENTER();
if(MailExists(mail, folder) == TRUE)
{
struct Folder *delfolder = FO_GetFolderByType(FT_TRASH, NULL);
// delete the mail
MA_DeleteSingle(mail, DELF_UPDATE_APPICON|DELF_CHECK_CONNECTIONS);
AppendToLogfile(LF_NORMAL, 22, tr(MSG_LOG_Moving), 1, folder->Name, delfolder->Name);
// erase the old pointer as this has been free()ed by MA_MoveCopy()
rmData->mail = NULL;
}
RETURN(0);
return 0;
}
///
/// DECLARE(DeleteAttachmentsRequest)
// Removes attachments from the current message
DECLARE(DeleteAttachmentsRequest)
{
GETDATA;
struct ReadMailData *rmData = data->readMailData;
struct Mail *mail = rmData->mail;
ENTER();
// remove the attchments now
MA_RemoveAttach(mail, NULL, C->ConfirmRemoveAttachments);
RETURN(0);
return 0;
}
///
/// DECLARE(HeaderListDoubleClicked)
// Switches between the SHORT and FULL header view of
// the header list
DECLARE(HeaderListDoubleClicked)
{
GETDATA;
struct ReadMailData *rmData = data->readMailData;
ENTER();
if(rmData->headerMode == HM_SHORTHEADER)
rmData->headerMode = HM_FULLHEADER;
else
rmData->headerMode = HM_SHORTHEADER;
DoMethod(obj, METHOD(UpdateHeaderDisplay), MUIF_ReadMailGroup_ReadMail_UpdateOnly);
RETURN(0);
return 0;
}
///
/// DECLARE(Search)
DECLARE(Search) // int flags
{
GETDATA;
ENTER();
if(data->searchWindow == NULL)
{
if((data->searchWindow = SearchTextWindowObject, End) != NULL)
{
// perform the search operation
DoMethod(data->searchWindow, MUIM_SearchTextWindow_Open, data->mailTextObject);
}
}
else
{
if(hasSearchAgainFlag(msg->flags))
DoMethod(data->searchWindow, MUIM_SearchTextWindow_Next);
else
DoMethod(data->searchWindow, MUIM_SearchTextWindow_Open, data->mailTextObject);
}
RETURN(0);
return 0;
}
///
/// DECLARE(ChangeHeaderMode)
DECLARE(ChangeHeaderMode) // enum HeaderMode hmode
{
GETDATA;
ENTER();
if(data->readMailData->headerMode != msg->hmode)
{
// change the header display mode
data->readMailData->headerMode = msg->hmode;
// issue an update of ourself
DoMethod(obj, METHOD(UpdateHeaderDisplay), MUIF_ReadMailGroup_ReadMail_UpdateOnly);
}
RETURN(0);
return 0;
}
///
/// DECLARE(ChangeSenderInfoMode)
DECLARE(ChangeSenderInfoMode) // enum SInfoMode simode
{
GETDATA;
ENTER();
if(data->readMailData->senderInfoMode != msg->simode)
{
// change the sender info mode
data->readMailData->senderInfoMode = msg->simode;
// issue an update of ourself
DoMethod(obj, METHOD(UpdateHeaderDisplay), MUIF_ReadMailGroup_ReadMail_UpdateOnly);
}
RETURN(0);
return 0;
}
///
/// DECLARE(DoEditAction)
DECLARE(DoEditAction) // enum EditAction editAction, ULONG flags
{
GETDATA;
Object *curObj;
BOOL result = FALSE;
ENTER();
// we first check which object is currently selected
// as the 'active' Object
curObj = (Object *)xget(_win(obj), MUIA_Window_ActiveObject);
if(curObj == NULL)
curObj = (Object *)xget(_win(obj), MUIA_Window_DefaultObject);
// check if the current Object matches any of our
// maintained objects or if the fallback option was
// specified.
if(curObj != data->mailTextObject &&
curObj != data->headerListview &&
curObj != data->headerList &&
hasEditActionFallbackFlag(msg->flags))
{
// we fallback to the mailTextObject
curObj = data->mailTextObject;
}
// if we still haven't got anything selected
// something must be extremly strange ;)
if(curObj != NULL)
{
// check which action we got
switch(msg->editAction)
{
case EA_COPY:
{
if(curObj == data->mailTextObject)
{
DoMethod(curObj, MUIM_TextEditor_ARexxCmd, "COPY");
result = TRUE;
}
else if(curObj == data->headerListview || curObj == data->headerList)
{
DoMethod(data->headerList, MUIM_NList_CopyToClip, MUIV_NList_CopyToClip_Selected, 0, NULL, NULL);
DoMethod(data->headerList, MUIM_NList_Select, MUIV_NList_Select_All, MUIV_NList_Select_Off, NULL);
result = TRUE;
}
}
break;
case EA_SELECTALL:
{
if(curObj == data->mailTextObject)
{
DoMethod(curObj, MUIM_TextEditor_ARexxCmd, "SELECTALL");
result = TRUE;
}
else if(curObj == data->headerListview || curObj == data->headerList)
{
DoMethod(data->headerList, MUIM_NList_Select, MUIV_NList_Select_All, MUIV_NList_Select_On, NULL);
result = TRUE;
}
}
break;
case EA_SELECTNONE:
{
if(curObj == data->mailTextObject)
{
DoMethod(curObj, MUIM_TextEditor_ARexxCmd, "SELECTNONE");
result = TRUE;
}
else if(curObj == data->headerListview || curObj == data->headerList)
{
DoMethod(data->headerList, MUIM_NList_Select, MUIV_NList_Select_All, MUIV_NList_Select_Off, NULL);
result = TRUE;
}
}
break;
default:
// nothing
W(DBF_GUI, "edit action %ld not supported by ReadMailGroup", msg->editAction);
break;
}
}
else
E(DBF_GUI, "no active nor default object of window: 0x%08lx", _win(obj));
RETURN(result);
return result;
}
///
/// DECLARE(ExportSelectedText)
DECLARE(ExportSelectedText)
{
GETDATA;
char *result = NULL;
ENTER();
result = (char *)DoMethod(data->mailTextObject, MUIM_TextEditor_ExportBlock, MUIF_NONE);
RETURN(result);
return (ULONG)result;
}
///