gecko-dev/lib/mailto/msgpane.cpp
akkana%netscape.com 1a97e11ffd Mailto landing.
The mailto library is the mail compose code ripped out of the old
Messenger libmsg library, then cleaned up somewhat
(it could still use more cleaning).
This library should only be built ifdef MOZ_MAIL_COMPOSE.
1998-09-04 19:04:30 +00:00

648 lines
16 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#include "rosetta.h"
#include "msg.h"
#include "errcode.h"
#include "xpgetstr.h"
#include "libi18n.h"
#include "msgpane.h"
#include "msgprefs.h"
#include "msgmast.h"
#include "msghdr.h"
#include "xp_time.h"
#include "xplocale.h"
#include "msg.h"
#include "msgmast.h"
#include "prefapi.h"
#include "xp_qsort.h"
#include "intl_csi.h"
#include "xlate.h"
#include "msgurlq.h"
#include "pw_public.h"
#include "mime.h"
extern "C"
{
extern int MK_MSG_ADDRESS_BOOK;
extern int MK_MSG_COMPRESS_ALL_FOLDER;
extern int MK_MSG_EMPTY_TRASH_FOLDER;
extern int MK_MSG_ERROR_WRITING_MAIL_FOLDER;
extern int MK_MSG_GET_NEW_MAIL;
extern int MK_MSG_GET_NEW_DISCUSSION_MSGS;
extern int MK_MSG_NEW_MAIL_MESSAGE;
extern int MK_MSG_NO_POP_HOST;
extern int MK_OUT_OF_MEMORY;
extern int MK_MSG_SAVE_MESSAGE_AS;
extern int MK_MSG_SAVE_MESSAGES_AS;
extern int MK_MSG_OPEN_DRAFT;
extern int MK_MSG_ID_NOT_IN_FOLDER;
extern int MK_MSG_FOLDER_UNREADABLE;
extern int MK_MSG_DELIV_NEW_MSGS;
extern int MK_MSG_QUEUED_DELIVERY_FAILED;
extern int MK_MSG_NEWS_HOST_TABLE_INVALID;
extern int MK_MSG_CANCEL_MESSAGE;
extern int MK_MSG_MESSAGE_CANCELLED;
extern int MK_MSG_MARK_SEL_AS_READ;
extern int MK_MSG_MARK_SEL_AS_UNREAD;
extern int MK_MSG_MARK_THREAD_READ;
extern int MK_MSG_MARK_ALL_READ;
extern int MK_MSG_BACKTRACK;
extern int MK_MSG_GO_FORWARD;
extern int MK_MSG_UNABLE_MANAGE_MAIL_ACCOUNT;
extern int MK_POP3_NO_MESSAGES;
extern int MK_MSG_MANAGE_MAIL_ACCOUNT;
extern int MK_MSG_CANT_DELETE_RESERVED_FOLDER;
extern int MK_MSG_PANES_OPEN_ON_FOLDER;
extern int MK_MSG_DELETE_FOLDER_MESSAGES;
extern int MK_MSG_NO_POST_TO_DIFFERENT_HOSTS_ALLOWED;
extern int MK_MSG_GROUP_NOT_ON_SERVER;
extern int MK_MSG_NEW_NEWSGROUP;
extern int MK_MSG_ADVANCE_TO_NEXT_FOLDER;
extern int MK_MSG_FLAG_MESSAGE;
extern int MK_MSG_UNFLAG_MESSAGE;
extern int MK_MSG_RETRIEVE_FLAGGED;
extern int MK_MSG_RETRIEVE_SELECTED;
XP_Bool NET_IsNewsMessageURL (const char *url);
}
MSG_Pane* MSG_Pane::MasterList = NULL;
typedef struct msg_incorporate_state
{
MWContext *context;
MSG_FolderInfoMail *inbox;
MSG_Pane *pane;
const char* dest;
const char *destName;
int32 start_length;
// int numdup;
char *ibuffer;
uint32 ibuffer_size;
uint32 ibuffer_fp;
#ifdef MANGLE_INTERNAL_ENVELOPE_LINES
XP_Bool mangle_from; /* True if "From " lines need to be subject
to the Usual Mangling Conventions.*/
#endif /* MANGLE_INTERNAL_ENVELOPE_LINES */
char* headers;
uint32 headers_length;
uint32 headers_maxlength;
XP_Bool gathering_headers;
XP_Bool expect_multiple;
XP_Bool expect_envelope;
ParseMailboxState *incparsestate; /* Parse state for messages */
int status;
} msg_incorporate_state;
MSG_Pane::MSG_Pane(MWContext* context, MSG_Master* master) {
m_context = context;
m_nextInMasterList = MasterList;
MasterList = this;
m_master = master;
if (master) {
m_nextPane = master->GetFirstPane();
master->SetFirstPane(this);
m_prefs = master->GetPrefs();
m_prefs->AddNotify(this);
m_context->mailMaster = master;
}
}
MSG_Pane::~MSG_Pane() {
UnregisterFromPaneList();
if (m_master) {
m_master->GetPrefs()->RemoveNotify(this);
}
if (m_progressContext)
PW_DestroyProgressContext(m_progressContext);
}
MSG_Pane* MSG_Pane::GetFirstPaneForContext(MWContext *context)
{
if (context)
return GetNextPaneForContext(NULL, context);
return(NULL);
}
MSG_Pane* MSG_Pane::GetNextPaneForContext(MSG_Pane *startPane, MWContext *context)
{
MSG_Pane* result = NULL;
result = (startPane) ? startPane->m_nextInMasterList : MasterList;
for (; result ; result = result->m_nextInMasterList)
{
if (result->GetContext() == context)
return result;
}
return NULL;
}
// Remove a pane from the pane list
// Note that if after we remove ourselves from the list, the only pane left
// belongs to the Biff (Check for New Mail) Master then we tell it to go away, which will cause
// its own hidden progress window and context to be deleted.
void MSG_Pane::UnregisterFromPaneList()
{
if (m_master) {
MSG_Pane* tmp = m_master->GetFirstPane();
if (tmp == this) {
m_master->SetFirstPane(m_nextPane);
} else {
for (; tmp ; tmp = tmp->m_nextPane) {
if (tmp->m_nextPane == this) {
tmp->m_nextPane = m_nextPane;
break;
}
}
}
}
MSG_Pane** ptr;
for (ptr = &MasterList ; *ptr ; ptr = &((*ptr)->m_nextInMasterList)) {
if (*ptr == this) {
*ptr = this->m_nextInMasterList;
break;
}
}
}
// this method can be used to find out if a pane has been deleted...
/*static*/ XP_Bool MSG_Pane::PaneInMasterList(MSG_Pane *pane)
{
MSG_Pane* curPane;
XP_Bool ret = FALSE;
// it will return FALSE if pane is NULL
for (curPane = MasterList ; curPane ; curPane = curPane->m_nextInMasterList)
{
if (curPane == pane)
{
ret = TRUE;
break;
}
}
return ret;
}
MSG_Pane* MSG_Pane::FindPane(MWContext* context, MSG_PaneType type, XP_Bool contextMustMatch /* = FALSE */) {
MSG_Pane* result;
for (result = MasterList ; result ; result = result->m_nextInMasterList) {
if (result->GetContext() == context && (type == MSG_ANYPANE ||
result->GetPaneType() == type)) {
return result;
}
}
if (!contextMustMatch)
{
for (result = MasterList ; result ; result = result->m_nextInMasterList) {
if (type == MSG_ANYPANE || result->GetPaneType() == type) {
return result;
}
}
}
return NULL;
}
/* static */ MSG_PaneType MSG_Pane::PaneTypeForURL(const char *url)
{
MSG_PaneType retType = MSG_ANYPANE;
int urlType = NET_URL_Type(url);
char *folderName = NULL;
switch (urlType)
{
case MAILTO_TYPE_URL:
retType = MSG_COMPOSITIONPANE;
break;
default:
break;
}
FREEIF(folderName);
return retType;
}
/* inline virtuals moved to cpp file to help compilers that don't implement virtuals
defined in headers well.
*/
MSG_PaneType MSG_Pane::GetPaneType() {return MSG_PANE;}
void MSG_Pane::NotifyPrefsChange(NotifyCode /*code*/) {}
MSG_Pane* MSG_Pane::GetParentPane() {return NULL;}
void MSG_Pane::SetFEData(void* data) {
m_fedata = data;
}
void* MSG_Pane::GetFEData() {
return m_fedata;
}
XP_Bool MSG_Pane::IsLinePane() {
return FALSE;
}
MWContext* MSG_Pane::GetContext() {
return m_context;
}
MSG_Prefs* MSG_Pane::GetPrefs() {
return m_prefs;
}
MsgERR
MSG_Pane::ComposeNewMessage()
{
return (MSG_Mail(GetContext())) ? 0 : MK_OUT_OF_MEMORY;
}
//This is a long one. I'd like to break it down but some of the work
//that is done here just isn't really useful elsewhere. This handles
//serveral selection cases in the threadwindow of the Mail and News pane.
//If the individual selected something which we can use to populate the
//send to lines, it get added into the addressee fields with the group label.
//If nothing of any use was selected, the "mailto" label is used with a blank.
//They are not allowed to select groups across news servers. Such an act
//will result in an informative error message and bring you back to the
//mail and news pane.
MsgERR
MSG_Pane::ComposeMessageToMany(MSG_ViewIndex* , int32 )
{
return ComposeNewMessage();
}
char*
MSG_Pane::CreateForwardSubject(MessageHdrStruct* header)
{
char *fwd_subject = 0;
const char *subject = 0;
char *conv_subject = 0;
INTL_CharSetInfo c = LO_GetDocumentCharacterSetInfo(GetContext());
subject = header->m_subject;
while (XP_IS_SPACE(*subject)) subject++;
conv_subject = IntlDecodeMimePartIIStr(subject, INTL_GetCSIWinCSID(c), FALSE);
if (conv_subject == NULL)
conv_subject = (char *) subject;
fwd_subject =
(char *) XP_ALLOC((conv_subject ? XP_STRLEN(conv_subject) : 0) + 20);
if (!fwd_subject) goto FAIL;
XP_STRCPY (fwd_subject, "[Fwd: ");
if (header->m_flags & kHasRe) {
XP_STRCAT (fwd_subject, "Re: ");
}
XP_STRCAT (fwd_subject, conv_subject);
XP_STRCAT (fwd_subject, "]");
FAIL:
if (conv_subject != subject) {
FREEIF(conv_subject);
}
return fwd_subject;
}
void
MSG_Pane::InterruptContext(XP_Bool /*safetoo*/)
{
XP_InterruptContext(m_context);
}
MsgERR
MSG_Pane::DoCommand(MSG_CommandType command, MSG_ViewIndex* indices,
int32 numIndices)
{
MsgERR status = 0;
switch (command) {
break;
case MSG_MailNew:
status = ComposeMessageToMany(indices, numIndices);
break;
default:
#ifdef DEBUG
FE_Alert (GetContext(), "command not implemented");
#endif
break;
}
return status;
}
MsgERR
MSG_Pane::GetCommandStatus(MSG_CommandType command,
const MSG_ViewIndex* indices, int32 numIndices,
XP_Bool *selectable_pP,
MSG_COMMAND_CHECK_STATE *selected_pP,
const char **display_stringP,
XP_Bool *plural_pP)
{
const char *display_string = 0;
XP_Bool plural_p = FALSE;
XP_Bool selectable_p = FALSE;
XP_Bool selected_p = FALSE;
XP_Bool selected_used_p = FALSE;
switch(command) {
case MSG_MailNew:
display_string = XP_GetString(MK_MSG_NEW_MAIL_MESSAGE);
// don't enable compose if we're parsing a folder
selectable_p = TRUE;
break;
default:
// XP_ASSERT(0);
break;
}
if (selectable_pP)
*selectable_pP = selectable_p;
if (selected_pP)
{
if (selected_used_p)
{
if (selected_p)
*selected_pP = MSG_Checked;
else
*selected_pP = MSG_Unchecked;
}
else
{
*selected_pP = MSG_NotUsed;
}
}
if (display_stringP)
*display_stringP = display_string;
if (plural_pP)
*plural_pP = plural_p;
return 0;
}
MSG_COMMAND_CHECK_STATE
MSG_Pane::GetToggleStatus(MSG_CommandType command, MSG_ViewIndex* indices,
int32 numindices)
{
MSG_COMMAND_CHECK_STATE result = MSG_NotUsed;
if (GetCommandStatus(command, indices, numindices, NULL, &result,
NULL, NULL) != 0) {
return MSG_NotUsed;
}
return result;
}
MsgERR
MSG_Pane::SetToggleStatus(MSG_CommandType command,
MSG_ViewIndex* indices, int32 numindices,
MSG_COMMAND_CHECK_STATE value)
{
MsgERR status = eSUCCESS;
MSG_COMMAND_CHECK_STATE old = GetToggleStatus(command, indices, numindices);
if (old == MSG_NotUsed) return eUNKNOWN;
if (old != value)
{
if ((status = DoCommand(command, indices, numindices)) == eSUCCESS)
{
if (GetToggleStatus(command, indices, numindices) != value)
{
XP_ASSERT(0);
return eUNKNOWN;
}
}
}
return status;
}
void MSG_Pane::SetRequestForReturnReceipt(XP_Bool bRequested)
{
m_requestForReturnReceipt = bRequested;
}
XP_Bool MSG_Pane::GetRequestForReturnReceipt()
{
return m_requestForReturnReceipt;
}
void MSG_Pane::SetSendingMDNInProgress(XP_Bool inProgress)
{
m_sendingMDNInProgress = inProgress;
}
XP_Bool MSG_Pane::GetSendingMDNInProgress()
{
return m_sendingMDNInProgress;
}
char*
MSG_Pane::MakeMailto(const char *to, const char *cc,
const char *newsgroups,
const char *subject, const char *references,
const char *attachment, const char *host_data,
XP_Bool xxx_p, XP_Bool sign_p)
{
char *to2 = 0, *cc2 = 0;
char *out, *head;
char *qto, *qcc, *qnewsgroups, *qsubject, *qreferences;
char *qattachment, *qhost_data;
char *url;
char *me = MIME_MakeFromField();
char *to_plus_me = 0;
to2 = MSG_RemoveDuplicateAddresses (to, ((cc && *cc) ? me : 0), TRUE /*removeAliasesToMe*/);
if (to2 && !*to2) {
XP_FREE(to2);
to2 = 0;
}
/* This to_plus_me business is so that, in reply-to-all of a message
to which I was a recipient, I don't go into the CC field (that's
what BCC/FCC are for.) */
if (to2 && cc && me) {
to_plus_me = (char *) XP_ALLOC(XP_STRLEN(to2) + XP_STRLEN(me) + 10);
}
if (to_plus_me) {
XP_STRCPY(to_plus_me, me);
XP_STRCAT(to_plus_me, ", ");
XP_STRCAT(to_plus_me, to2);
}
FREEIF(me);
cc2 = MSG_RemoveDuplicateAddresses (cc, (to_plus_me ? to_plus_me : to2), TRUE /*removeAliasesToMe*/);
if (cc2 && !*cc2) {
XP_FREE(cc2);
cc2 = 0;
}
FREEIF(to_plus_me);
/* Catch the case of "Reply To All" on a message that was from me.
In that case, we've got an empty To: field at this point.
What we should do is, promote the first CC address to the To:
field. But I'll settle for promoting all of them.
*/
if (cc2 && *cc2 && (!to2 || !*to2)) {
FREEIF(to2);
to2 = cc2;
cc2 = 0;
}
qto = to2 ? NET_Escape (to2, URL_XALPHAS) : 0;
qcc = cc2 ? NET_Escape (cc2, URL_XALPHAS) : 0;
qnewsgroups = newsgroups ? NET_Escape (newsgroups, URL_XALPHAS) : 0;
qsubject = subject ? NET_Escape (subject, URL_XALPHAS) : 0;
qreferences = references ? NET_Escape (references, URL_XALPHAS) : 0;
qattachment = attachment ? NET_Escape (attachment, URL_XALPHAS) : 0;
qhost_data = host_data ? NET_Escape (host_data, URL_XALPHAS) : 0;
url = (char *)
XP_ALLOC ((qto ? XP_STRLEN(qto) + 15 : 0) +
(qcc ? XP_STRLEN(qcc) + 15 : 0) +
(qnewsgroups ? XP_STRLEN(qnewsgroups) + 15 : 0) +
(qsubject ? XP_STRLEN(qsubject) + 15 : 0) +
(qreferences ? XP_STRLEN(qreferences) + 15 : 0) +
(qhost_data ? XP_STRLEN(qhost_data) + 15 : 0) +
(qattachment ? XP_STRLEN(qattachment) + 15 : 0) +
60);
if (!url) goto FAIL;
XP_STRCPY (url, "mailto:");
head = url + XP_STRLEN (url);
out = head;
# define PUSH_STRING(S) XP_STRCPY(out, S), out += XP_STRLEN(S)
# define PUSH_PARM(prefix,var) \
if (var) { \
if (out == head) \
*out++ = '?'; \
else \
*out++ = '&'; \
PUSH_STRING (prefix); \
*out++ = '='; \
PUSH_STRING (var); \
} \
PUSH_PARM("to", qto);
PUSH_PARM("cc", qcc);
PUSH_PARM("newsgroups", qnewsgroups);
PUSH_PARM("subject", qsubject);
PUSH_PARM("references", qreferences);
PUSH_PARM("attachment", qattachment);
PUSH_PARM("newshost", qhost_data);
{
char *t = "true"; /* avoid silly compiler warning */
HG92725
if (sign_p) PUSH_PARM("sign", t);
}
# undef PUSH_PARM
# undef PUSH_STRING
FAIL:
FREEIF (to2);
FREEIF (cc2);
FREEIF (qto);
FREEIF (qcc);
FREEIF (qnewsgroups);
FREEIF (qsubject);
FREEIF (qreferences);
FREEIF (qattachment);
FREEIF (qhost_data);
return url;
}
#ifdef GENERATINGPOWERPC
#pragma global_optimizer off
#endif
MSG_PaneURLChain::MSG_PaneURLChain(MSG_Pane *pane)
{
m_pane = pane;
}
MSG_PaneURLChain::~MSG_PaneURLChain()
{
}
// override to chain urls. return non-zero to continue.
int MSG_PaneURLChain::GetNextURL()
{
return 0;
}
#ifndef MOZ_MAIL_NEWS
/* This is normally in mkpop3.c, of all the odd places!
But it's required for mail compose.
*/
int
msg_GrowBuffer (uint32 desired_size, uint32 element_size, uint32 quantum,
char **buffer, uint32 *size)
{
if (*size <= desired_size)
{
char *new_buf;
uint32 increment = desired_size - *size;
if (increment < quantum) /* always grow by a minimum of N bytes */
increment = quantum;
#ifdef TESTFORWIN16
if (((*size + increment) * (element_size / sizeof(char))) >= 64000)
{
/* Make sure we don't choke on WIN16 */
XP_ASSERT(0);
return MK_OUT_OF_MEMORY;
}
#endif /* DEBUG */
new_buf = (*buffer
? (char *) XP_REALLOC (*buffer, (*size + increment)
* (element_size / sizeof(char)))
: (char *) XP_ALLOC ((*size + increment)
* (element_size / sizeof(char))));
if (! new_buf)
return MK_OUT_OF_MEMORY;
*buffer = new_buf;
*size += increment;
}
return 0;
}
#endif /* ! MOZ_MAIL_NEWS */