1998-09-04 19:04:30 +00:00
|
|
|
|
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
|
|
|
*
|
1999-11-06 03:43:54 +00:00
|
|
|
|
* The contents of this file are subject to the Netscape Public
|
|
|
|
|
* License Version 1.1 (the "License"); you may not use this file
|
|
|
|
|
* except in compliance with the License. You may obtain a copy of
|
|
|
|
|
* the License at http://www.mozilla.org/NPL/
|
1998-09-04 19:04:30 +00:00
|
|
|
|
*
|
1999-11-06 03:43:54 +00:00
|
|
|
|
* Software distributed under the License is distributed on an "AS
|
|
|
|
|
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
|
|
|
|
* implied. See the License for the specific language governing
|
|
|
|
|
* rights and limitations under the License.
|
1998-09-04 19:04:30 +00:00
|
|
|
|
*
|
1999-11-06 03:43:54 +00:00
|
|
|
|
* The Original Code is mozilla.org code.
|
|
|
|
|
*
|
|
|
|
|
* The Initial Developer of the Original Code is Netscape
|
1998-09-04 19:04:30 +00:00
|
|
|
|
* Communications Corporation. Portions created by Netscape are
|
1999-11-06 03:43:54 +00:00
|
|
|
|
* Copyright (C) 1998 Netscape Communications Corporation. All
|
|
|
|
|
* Rights Reserved.
|
|
|
|
|
*
|
|
|
|
|
* Contributor(s):
|
1998-09-04 19:04:30 +00:00
|
|
|
|
*/
|
|
|
|
|
/* msgutils.c --- various and sundry
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "msg.h"
|
|
|
|
|
#include "xp_time.h"
|
|
|
|
|
#include "xpgetstr.h"
|
|
|
|
|
#include "xplocale.h"
|
|
|
|
|
#include "htmldlgs.h"
|
|
|
|
|
#include "prefapi.h"
|
|
|
|
|
#include "xp_qsort.h"
|
|
|
|
|
|
|
|
|
|
extern int MK_OUT_OF_MEMORY;
|
|
|
|
|
extern int MK_MSG_NON_MAIL_FILE_WRITE_QUESTION;
|
|
|
|
|
extern int MK_MSG_HTML_DOMAINS_DIALOG;
|
|
|
|
|
extern int MK_MSG_HTML_DOMAINS_DIALOG_TITLE;
|
|
|
|
|
|
|
|
|
|
#ifdef XP_MAC
|
|
|
|
|
#pragma warn_unusedarg off
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
msg_GetURL(MWContext* context, URL_Struct* url, XP_Bool issafe)
|
|
|
|
|
{
|
|
|
|
|
XP_ASSERT(context);
|
|
|
|
|
|
|
|
|
|
/* phil & bienvenu think issafe means "allowed to start another URL even if
|
|
|
|
|
one is already runing". e.g. delete from msgPane, and load next msg */
|
|
|
|
|
if (!issafe)
|
|
|
|
|
msg_InterruptContext (context, TRUE);
|
|
|
|
|
|
|
|
|
|
url->internal_url = TRUE;
|
|
|
|
|
if (!url->open_new_window_specified)
|
|
|
|
|
{
|
|
|
|
|
url->open_new_window_specified = TRUE;
|
|
|
|
|
url->open_new_window = FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return FE_GetURL(context, url);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
msg_InterruptContext(MWContext* context, XP_Bool safetoo)
|
|
|
|
|
{
|
|
|
|
|
XP_InterruptContext(context);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef NOTDEF /* ###tw */
|
|
|
|
|
|
|
|
|
|
struct MSG_Frame *msg_frame;
|
|
|
|
|
if (!context) return;
|
|
|
|
|
if (safetoo || !context->msgframe ||
|
|
|
|
|
!context->msgframe->safe_background_activity) {
|
|
|
|
|
/* save msg_frame in case context gets deleted on interruption */
|
|
|
|
|
msg_frame = context->msgframe;
|
|
|
|
|
XP_InterruptContext(context);
|
|
|
|
|
if (msg_frame) {
|
|
|
|
|
msg_frame->safe_background_activity = FALSE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
XP_Bool
|
|
|
|
|
MSG_RequiresComposeWindow (const char *url)
|
|
|
|
|
{
|
|
|
|
|
if (!url) return FALSE;
|
|
|
|
|
if (!strncasecomp (url, "mailto:", 7))
|
|
|
|
|
{
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
XP_Bool
|
|
|
|
|
MSG_RequiresBrowserWindow (const char *url)
|
|
|
|
|
{
|
|
|
|
|
if (!url) return FALSE;
|
|
|
|
|
if (MSG_RequiresNewsWindow (url) ||
|
|
|
|
|
MSG_RequiresMailWindow (url) ||
|
|
|
|
|
!strncasecomp (url, "about:", 6) ||
|
|
|
|
|
!strncasecomp (url, "addbook:", 8) ||
|
|
|
|
|
!strncasecomp (url, "addbook-ldap", 12) || /* no colon so addbook-ldap and addbook-ldaps both match */
|
|
|
|
|
!strncasecomp (url, "mailto:", 7) ||
|
|
|
|
|
!strncasecomp (url, "view-source:", 12) ||
|
|
|
|
|
!strncasecomp (url, "internal-callback-handler:", 26) ||
|
|
|
|
|
!strncasecomp (url, "internal-panel-handler", 22) ||
|
|
|
|
|
!strncasecomp (url, "internal-dialog-handler", 23))
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
else if (!strncasecomp (url, "news:", 5) ||
|
|
|
|
|
!strncasecomp (url, "snews:", 6) ||
|
|
|
|
|
!strncasecomp (url, "mailbox:", 8) ||
|
|
|
|
|
!strncasecomp (url, "IMAP:", 5))
|
|
|
|
|
{
|
|
|
|
|
/* Mail and news messages themselves don't require browser windows,
|
|
|
|
|
but their attachments do. */
|
|
|
|
|
if (XP_STRSTR(url, "?part=") || XP_STRSTR(url, "&part="))
|
|
|
|
|
return TRUE;
|
|
|
|
|
else
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* If we're in a mail window, and clicking on a link which will itself
|
|
|
|
|
require a mail window, then don't allow this to show up in a different
|
|
|
|
|
window - since there can only be one mail window.
|
|
|
|
|
*/
|
|
|
|
|
XP_Bool
|
|
|
|
|
MSG_NewWindowProhibited (MWContext *context, const char *url)
|
|
|
|
|
{
|
|
|
|
|
if (!context) return FALSE;
|
|
|
|
|
if ((context->type == MWContextMail &&
|
|
|
|
|
MSG_RequiresMailWindow (url)) ||
|
|
|
|
|
(context->type == MWContextNews &&
|
|
|
|
|
MSG_RequiresNewsWindow (url)) ||
|
|
|
|
|
(MSG_RequiresComposeWindow (url)))
|
|
|
|
|
return TRUE;
|
|
|
|
|
else
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
char *
|
|
|
|
|
MSG_ConvertToQuotation (const char *string)
|
|
|
|
|
{
|
|
|
|
|
int32 column = 0;
|
|
|
|
|
int32 newlines = 0;
|
|
|
|
|
int32 chars = 0;
|
|
|
|
|
const char *in;
|
|
|
|
|
char *out;
|
|
|
|
|
char *new_string;
|
|
|
|
|
|
|
|
|
|
if (! string) return 0;
|
|
|
|
|
|
|
|
|
|
/* First, count up the lines in the string. */
|
|
|
|
|
for (in = string; *in; in++)
|
|
|
|
|
{
|
|
|
|
|
chars++;
|
|
|
|
|
if (*in == CR || *in == LF)
|
|
|
|
|
{
|
|
|
|
|
if (in[0] == CR && in[1] == LF) {
|
|
|
|
|
in++;
|
|
|
|
|
chars++;
|
|
|
|
|
}
|
|
|
|
|
newlines++;
|
|
|
|
|
column = 0;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
column++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
/* If the last line doesn't end in a newline, pretend it does. */
|
|
|
|
|
if (column != 0)
|
|
|
|
|
newlines++;
|
|
|
|
|
|
|
|
|
|
/* 2 characters for each '> ', +1 for '\0', and + potential linebreak */
|
|
|
|
|
new_string = (char *) XP_ALLOC (chars + (newlines * 2) + 1 + LINEBREAK_LEN);
|
|
|
|
|
if (! new_string)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
column = 0;
|
|
|
|
|
out = new_string;
|
|
|
|
|
|
|
|
|
|
/* Now copy. */
|
|
|
|
|
for (in = string; *in; in++)
|
|
|
|
|
{
|
|
|
|
|
if (column == 0)
|
|
|
|
|
{
|
|
|
|
|
*out++ = '>';
|
|
|
|
|
*out++ = ' ';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*out++ = *in;
|
|
|
|
|
if (*in == CR || *in == LF)
|
|
|
|
|
{
|
|
|
|
|
if (in[0] == CR && in[1] == LF)
|
|
|
|
|
*out++ = *++in;
|
|
|
|
|
newlines++;
|
|
|
|
|
column = 0;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
column++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If the last line doesn't end in a newline, add one. */
|
|
|
|
|
if (column != 0)
|
|
|
|
|
{
|
|
|
|
|
XP_STRCPY (out, LINEBREAK);
|
|
|
|
|
out += LINEBREAK_LEN;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*out = 0;
|
|
|
|
|
|
|
|
|
|
return new_string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Given a string and a length, removes any "Re:" strings from the front.
|
|
|
|
|
It also deals with that "Re[2]:" thing that some mailers do.
|
|
|
|
|
|
|
|
|
|
Returns TRUE if it made a change, FALSE otherwise.
|
|
|
|
|
|
|
|
|
|
The string is not altered: the pointer to its head is merely advanced,
|
|
|
|
|
and the length correspondingly decreased.
|
|
|
|
|
*/
|
|
|
|
|
XP_Bool
|
|
|
|
|
msg_StripRE(const char **stringP, uint32 *lengthP)
|
|
|
|
|
{
|
|
|
|
|
const char *s, *s_end;
|
|
|
|
|
const char *last;
|
|
|
|
|
uint32 L;
|
|
|
|
|
XP_Bool result = FALSE;
|
|
|
|
|
XP_ASSERT(stringP);
|
|
|
|
|
if (!stringP) return FALSE;
|
|
|
|
|
s = *stringP;
|
|
|
|
|
L = lengthP ? *lengthP : XP_STRLEN(s);
|
|
|
|
|
|
|
|
|
|
s_end = s + L;
|
|
|
|
|
last = s;
|
|
|
|
|
|
|
|
|
|
AGAIN:
|
|
|
|
|
|
|
|
|
|
while (s < s_end && XP_IS_SPACE(*s))
|
|
|
|
|
s++;
|
|
|
|
|
|
|
|
|
|
if (s < (s_end-2) &&
|
|
|
|
|
(s[0] == 'r' || s[0] == 'R') &&
|
|
|
|
|
(s[1] == 'e' || s[1] == 'E'))
|
|
|
|
|
{
|
|
|
|
|
if (s[2] == ':')
|
|
|
|
|
{
|
|
|
|
|
s = s+3; /* Skip over "Re:" */
|
|
|
|
|
result = TRUE; /* Yes, we stripped it. */
|
|
|
|
|
goto AGAIN; /* Skip whitespace and try again. */
|
|
|
|
|
}
|
|
|
|
|
else if (s[2] == '[' || s[2] == '(')
|
|
|
|
|
{
|
|
|
|
|
const char *s2 = s+3; /* Skip over "Re[" */
|
|
|
|
|
|
|
|
|
|
/* Skip forward over digits after the "[". */
|
|
|
|
|
while (s2 < (s_end-2) && XP_IS_DIGIT(*s2))
|
|
|
|
|
s2++;
|
|
|
|
|
|
|
|
|
|
/* Now ensure that the following thing is "]:"
|
|
|
|
|
Only if it is do we alter `s'.
|
|
|
|
|
*/
|
|
|
|
|
if ((s2[0] == ']' || s2[0] == ')') && s2[1] == ':')
|
|
|
|
|
{
|
|
|
|
|
s = s2+2; /* Skip over "]:" */
|
|
|
|
|
result = TRUE; /* Yes, we stripped it. */
|
|
|
|
|
goto AGAIN; /* Skip whitespace and try again. */
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Decrease length by difference between current ptr and original ptr.
|
|
|
|
|
Then store the current ptr back into the caller. */
|
|
|
|
|
if (lengthP) *lengthP -= (s - (*stringP));
|
|
|
|
|
*stringP = s;
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
char*
|
|
|
|
|
msg_GetDummyEnvelope(void)
|
|
|
|
|
{
|
|
|
|
|
static char result[75];
|
|
|
|
|
char *ct;
|
|
|
|
|
time_t now = time ((time_t *) 0);
|
|
|
|
|
#if defined (XP_WIN)
|
|
|
|
|
if (now < 0 || now > 0x7FFFFFFF)
|
|
|
|
|
now = 0x7FFFFFFF;
|
|
|
|
|
#endif
|
|
|
|
|
ct = ctime(&now);
|
|
|
|
|
XP_ASSERT(ct[24] == CR || ct[24] == LF);
|
|
|
|
|
ct[24] = 0;
|
|
|
|
|
/* This value must be in ctime() format, with English abbreviations.
|
|
|
|
|
strftime("... %c ...") is no good, because it is localized. */
|
|
|
|
|
XP_STRCPY(result, "From - ");
|
|
|
|
|
XP_STRCPY(result + 7, ct);
|
|
|
|
|
XP_STRCPY(result + 7 + 24, LINEBREAK);
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* #define STRICT_ENVELOPE */
|
|
|
|
|
|
|
|
|
|
XP_Bool
|
|
|
|
|
msg_IsEnvelopeLine(const char *buf, int32 buf_size)
|
|
|
|
|
{
|
|
|
|
|
#ifdef STRICT_ENVELOPE
|
|
|
|
|
/* The required format is
|
|
|
|
|
From jwz Fri Jul 1 09:13:09 1994
|
|
|
|
|
But we should also allow at least:
|
|
|
|
|
From jwz Fri, Jul 01 09:13:09 1994
|
|
|
|
|
From jwz Fri Jul 1 09:13:09 1994 PST
|
|
|
|
|
From jwz Fri Jul 1 09:13:09 1994 (+0700)
|
|
|
|
|
|
|
|
|
|
We can't easily call XP_ParseTimeString() because the string is not
|
|
|
|
|
null terminated (ok, we could copy it after a quick check...) but
|
|
|
|
|
XP_ParseTimeString() may be too lenient for our purposes.
|
|
|
|
|
|
|
|
|
|
DANGER!! The released version of 2.0b1 was (on some systems,
|
|
|
|
|
some Unix, some NT, possibly others) writing out envelope lines
|
|
|
|
|
like "From - 10/13/95 11:22:33" which STRICT_ENVELOPE will reject!
|
|
|
|
|
*/
|
|
|
|
|
const char *date, *end;
|
|
|
|
|
|
|
|
|
|
if (buf_size < 29) return FALSE;
|
|
|
|
|
if (*buf != 'F') return FALSE;
|
|
|
|
|
if (XP_STRNCMP(buf, "From ", 5)) return FALSE;
|
|
|
|
|
|
|
|
|
|
end = buf + buf_size;
|
|
|
|
|
date = buf + 5;
|
|
|
|
|
|
|
|
|
|
/* Skip horizontal whitespace between "From " and user name. */
|
|
|
|
|
while ((*date == ' ' || *date == '\t') && date < end)
|
|
|
|
|
date++;
|
|
|
|
|
|
|
|
|
|
/* If at the end, it doesn't match. */
|
|
|
|
|
if (XP_IS_SPACE(*date) || date == end)
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
/* Skip over user name. */
|
|
|
|
|
while (!XP_IS_SPACE(*date) && date < end)
|
|
|
|
|
date++;
|
|
|
|
|
|
|
|
|
|
/* Skip horizontal whitespace between user name and date. */
|
|
|
|
|
while ((*date == ' ' || *date == '\t') && date < end)
|
|
|
|
|
date++;
|
|
|
|
|
|
|
|
|
|
/* Don't want this to be localized. */
|
|
|
|
|
# define TMP_ISALPHA(x) (((x) >= 'A' && (x) <= 'Z') || \
|
|
|
|
|
((x) >= 'a' && (x) <= 'z'))
|
|
|
|
|
|
|
|
|
|
/* take off day-of-the-week. */
|
|
|
|
|
if (date >= end - 3)
|
|
|
|
|
return FALSE;
|
|
|
|
|
if (!TMP_ISALPHA(date[0]) || !TMP_ISALPHA(date[1]) || !TMP_ISALPHA(date[2]))
|
|
|
|
|
return FALSE;
|
|
|
|
|
date += 3;
|
|
|
|
|
/* Skip horizontal whitespace (and commas) between dotw and month. */
|
|
|
|
|
if (*date != ' ' && *date != '\t' && *date != ',')
|
|
|
|
|
return FALSE;
|
|
|
|
|
while ((*date == ' ' || *date == '\t' || *date == ',') && date < end)
|
|
|
|
|
date++;
|
|
|
|
|
|
|
|
|
|
/* take off month. */
|
|
|
|
|
if (date >= end - 3)
|
|
|
|
|
return FALSE;
|
|
|
|
|
if (!TMP_ISALPHA(date[0]) || !TMP_ISALPHA(date[1]) || !TMP_ISALPHA(date[2]))
|
|
|
|
|
return FALSE;
|
|
|
|
|
date += 3;
|
|
|
|
|
/* Skip horizontal whitespace between month and dotm. */
|
|
|
|
|
if (date == end || (*date != ' ' && *date != '\t'))
|
|
|
|
|
return FALSE;
|
|
|
|
|
while ((*date == ' ' || *date == '\t') && date < end)
|
|
|
|
|
date++;
|
|
|
|
|
|
|
|
|
|
/* Skip over digits and whitespace. */
|
|
|
|
|
while (((*date >= '0' && *date <= '9') || *date == ' ' || *date == '\t') &&
|
|
|
|
|
date < end)
|
|
|
|
|
date++;
|
|
|
|
|
/* Next character should be a colon. */
|
|
|
|
|
if (date >= end || *date != ':')
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
/* Ok, that ought to be enough... */
|
|
|
|
|
|
|
|
|
|
# undef TMP_ISALPHA
|
|
|
|
|
|
|
|
|
|
#else /* !STRICT_ENVELOPE */
|
|
|
|
|
|
|
|
|
|
if (buf_size < 5) return FALSE;
|
|
|
|
|
if (*buf != 'F') return FALSE;
|
|
|
|
|
if (XP_STRNCMP(buf, "From ", 5)) return FALSE;
|
|
|
|
|
|
|
|
|
|
#endif /* !STRICT_ENVELOPE */
|
|
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
XP_Bool
|
|
|
|
|
msg_ConfirmMailFile (MWContext *context, const char *file_name)
|
|
|
|
|
{
|
|
|
|
|
XP_File in = XP_FileOpen (file_name, xpMailFolder, XP_FILE_READ_BIN);
|
|
|
|
|
char buf[100];
|
|
|
|
|
char *s = buf;
|
|
|
|
|
int L;
|
|
|
|
|
if (!in) return TRUE;
|
|
|
|
|
L = XP_FileRead(buf, sizeof(buf)-2, in);
|
|
|
|
|
XP_FileClose (in);
|
|
|
|
|
if (L < 1) return TRUE;
|
|
|
|
|
buf[L] = 0;
|
|
|
|
|
while (XP_IS_SPACE(*s))
|
|
|
|
|
s++, L--;
|
|
|
|
|
if (L > 5 && msg_IsEnvelopeLine(s, L))
|
|
|
|
|
return TRUE;
|
|
|
|
|
PR_snprintf (buf, sizeof(buf),
|
|
|
|
|
XP_GetString (MK_MSG_NON_MAIL_FILE_WRITE_QUESTION), file_name);
|
|
|
|
|
return FE_Confirm (context, buf);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void msg_ClearMessageArea(MWContext* context)
|
|
|
|
|
{
|
|
|
|
|
/* ###tw This needs to be replaced by stuff that notifies FE's the
|
|
|
|
|
right way */
|
|
|
|
|
#ifndef _USRDLL
|
|
|
|
|
/* hack. Open a stream to layout, give it
|
|
|
|
|
whitespace (it needs something) and then close it
|
|
|
|
|
right away. This has the effect of getting the front end to
|
|
|
|
|
clear out the HTML display area.
|
|
|
|
|
*/
|
|
|
|
|
NET_StreamClass *stream;
|
|
|
|
|
static PA_InitData data;
|
|
|
|
|
URL_Struct *url;
|
|
|
|
|
if (!context)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
data.output_func = LO_ProcessTag;
|
|
|
|
|
url = NET_CreateURLStruct ("", NET_NORMAL_RELOAD);
|
|
|
|
|
if (!url) return;
|
|
|
|
|
stream = PA_BeginParseMDL (FO_PRESENT, &data, url, context);
|
|
|
|
|
if (stream)
|
|
|
|
|
{
|
|
|
|
|
char buf[] = "<BODY></BODY>";
|
|
|
|
|
int status = (*stream->put_block) (stream, buf, 13);
|
|
|
|
|
if (status < 0)
|
|
|
|
|
(*stream->abort) (stream, status);
|
|
|
|
|
else
|
|
|
|
|
(*stream->complete) (stream);
|
|
|
|
|
XP_FREE (stream);
|
|
|
|
|
}
|
|
|
|
|
NET_FreeURLStruct (url);
|
|
|
|
|
FE_SetProgressBarPercent (context, 0);
|
|
|
|
|
#endif
|
|
|
|
|
/* MSG_LoadMessage, with MSG_MESSAGEKEYNONE calls msg_ClearMessageArea, which
|
|
|
|
|
// has a bug (drawing the background in grey).<2E> So I just changed MSG_LoadMessage() to
|
|
|
|
|
// do nothing for XP_MAC except set its m_Key to MSG_MESSAGEKEYNONE and exit.<2E> This transfers
|
|
|
|
|
// the responsibility for clearing the message area to the front end.<2E> So far, so good.
|
|
|
|
|
//
|
|
|
|
|
// Now, it's no good just painting the area, because the next refresh will redraw using the
|
|
|
|
|
// existing history entry.<2E> So somehow we have to remove the history entry.
|
|
|
|
|
// There's no API for doing this, except calling SHIST_AddDocument with an entry whose
|
|
|
|
|
// address string is null or empty.
|
|
|
|
|
//
|
|
|
|
|
// If this state of affairs changes, this code will break, but I put in asserts to
|
|
|
|
|
// notify us about it. 98/01/21
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
#if 0
|
|
|
|
|
/* It would be nice to do it this way, but we need to cause a refresh in the fe */
|
|
|
|
|
URL_Struct* url = NET_CreateURLStruct("", NET_NORMAL_RELOAD);
|
|
|
|
|
History_entry* newEntry;
|
|
|
|
|
LO_DiscardDocument(context);
|
|
|
|
|
XP_ASSERT(url);
|
|
|
|
|
newEntry = SHIST_CreateHistoryEntry(url, "Nobody Home");
|
|
|
|
|
XP_FREEIF(url);
|
|
|
|
|
XP_ASSERT(newEntry);
|
|
|
|
|
/* Using an empty address string will cause "AddDocument" to do a removal of the old entry,
|
|
|
|
|
<EFBFBD>// then delete the new entry, and exit.
|
|
|
|
|
*/
|
|
|
|
|
SHIST_AddDocument(context, newEntry);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static MSG_HEADER_SET standard_header_set[] = {
|
|
|
|
|
MSG_TO_HEADER_MASK,
|
|
|
|
|
MSG_REPLY_TO_HEADER_MASK,
|
|
|
|
|
MSG_CC_HEADER_MASK,
|
|
|
|
|
MSG_BCC_HEADER_MASK,
|
|
|
|
|
MSG_NEWSGROUPS_HEADER_MASK,
|
|
|
|
|
MSG_FOLLOWUP_TO_HEADER_MASK
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#define TOTAL_HEADERS (sizeof(standard_header_set)/sizeof(MSG_HEADER_SET))
|
|
|
|
|
|
|
|
|
|
extern int MSG_ExplodeHeaderField(MSG_HEADER_SET msg_header, const char * field, MSG_HeaderEntry ** return_list)
|
|
|
|
|
{
|
|
|
|
|
XP_ASSERT(return_list);
|
|
|
|
|
*return_list = NULL;
|
|
|
|
|
if (field && strlen(field)) {
|
|
|
|
|
MSG_HeaderEntry *list=NULL;
|
|
|
|
|
char * name;
|
|
|
|
|
char * address ;
|
|
|
|
|
int count;
|
|
|
|
|
|
|
|
|
|
count = MSG_ParseRFC822Addresses (field, &name, &address);
|
|
|
|
|
if (count > 0) {
|
|
|
|
|
char * address_start = address;
|
|
|
|
|
char * name_start = name;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
list = (MSG_HeaderEntry*)XP_ALLOC(sizeof(MSG_HeaderEntry)*count);
|
|
|
|
|
if (!list)
|
|
|
|
|
return(-1);
|
|
|
|
|
|
|
|
|
|
for (i=0; i<count; i++) {
|
|
|
|
|
list[i].header_type = msg_header;
|
|
|
|
|
list[i].header_value = XP_STRDUP(address);
|
|
|
|
|
if (name && strlen(name))
|
|
|
|
|
list[i].header_value = PR_smprintf("%s <%s>", name, address);
|
|
|
|
|
else
|
|
|
|
|
list[i].header_value = XP_STRDUP(address);
|
|
|
|
|
while (*address != '\0')
|
|
|
|
|
address++;
|
|
|
|
|
address++;
|
|
|
|
|
while (*name != '\0')
|
|
|
|
|
name++;
|
|
|
|
|
name++;
|
|
|
|
|
}
|
|
|
|
|
if (name)
|
|
|
|
|
XP_FREE(name_start);
|
|
|
|
|
if (address)
|
|
|
|
|
XP_FREE(address_start);
|
|
|
|
|
}
|
|
|
|
|
*return_list = list;
|
|
|
|
|
return count;
|
|
|
|
|
}
|
|
|
|
|
return(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
extern int MSG_CompressHeaderEntries( MSG_HeaderEntry * in_list, int list_size, MSG_HeaderEntry ** return_list)
|
|
|
|
|
{
|
|
|
|
|
int total = 0;
|
|
|
|
|
*return_list = NULL;
|
|
|
|
|
if (in_list != NULL && list_size > 0) {
|
|
|
|
|
MSG_HeaderEntry * list = NULL;
|
|
|
|
|
char * new_header_value;
|
|
|
|
|
int i,j;
|
|
|
|
|
|
|
|
|
|
for (i=0; i<TOTAL_HEADERS; i++) {
|
|
|
|
|
new_header_value = NULL;
|
|
|
|
|
for (j=0; j<list_size; j++) {
|
|
|
|
|
if (in_list[j].header_type == standard_header_set[i]) {
|
|
|
|
|
XP_Bool zero_init = FALSE;
|
|
|
|
|
int header_length = 0;
|
|
|
|
|
if (!new_header_value)
|
|
|
|
|
zero_init = TRUE;
|
|
|
|
|
else
|
|
|
|
|
header_length = XP_STRLEN(new_header_value)+1;
|
|
|
|
|
if (XP_STRLEN(in_list[j].header_value) == 0)
|
|
|
|
|
continue;
|
|
|
|
|
new_header_value = (char*)XP_REALLOC(
|
|
|
|
|
new_header_value,
|
|
|
|
|
header_length+XP_STRLEN(in_list[j].header_value)+XP_STRLEN(",")+1);
|
|
|
|
|
if (new_header_value == NULL) {
|
|
|
|
|
if (list != NULL)
|
|
|
|
|
XP_FREE(list);
|
|
|
|
|
/* don't forget to free up previous header_value entries */
|
|
|
|
|
return(-1);
|
|
|
|
|
}
|
|
|
|
|
if (zero_init)
|
|
|
|
|
new_header_value[0]='\0';
|
|
|
|
|
if (new_header_value && XP_STRLEN(new_header_value))
|
|
|
|
|
XP_STRCAT(new_header_value,",");
|
|
|
|
|
XP_STRCAT(new_header_value,in_list[j].header_value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (new_header_value) {
|
|
|
|
|
total++;
|
|
|
|
|
list = (MSG_HeaderEntry *)XP_REALLOC(list,total*sizeof(MSG_HeaderEntry));
|
|
|
|
|
if (list==NULL) {
|
|
|
|
|
if (new_header_value)
|
|
|
|
|
XP_FREE(new_header_value);
|
|
|
|
|
return(-1);
|
|
|
|
|
}
|
|
|
|
|
list[total-1].header_type = standard_header_set[i];
|
|
|
|
|
list[total-1].header_value = new_header_value;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
*return_list = list;
|
|
|
|
|
}
|
|
|
|
|
return(total);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
msg_qsort_domains(const void* a, const void* b)
|
|
|
|
|
{
|
|
|
|
|
return XP_STRCMP(*((const char**) a), *((const char**) b));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
msg_generate_domains_list(char* domainlist, int* numfound, char*** returnlist)
|
|
|
|
|
{
|
|
|
|
|
int num;
|
|
|
|
|
int i;
|
|
|
|
|
int j;
|
|
|
|
|
char* ptr;
|
|
|
|
|
char* endptr;
|
|
|
|
|
char** list = NULL;
|
|
|
|
|
for (num=0 , ptr = domainlist ; ptr ; num++, ptr = endptr) {
|
|
|
|
|
endptr = XP_STRCHR(ptr, ',');
|
|
|
|
|
if (endptr) endptr++;
|
|
|
|
|
}
|
|
|
|
|
if (num > 0) {
|
|
|
|
|
list = (char**) XP_CALLOC(num, sizeof(char*));
|
|
|
|
|
if (!list) return MK_OUT_OF_MEMORY;
|
|
|
|
|
for (i=0 , ptr = domainlist ; ptr ; i++, ptr = endptr) {
|
|
|
|
|
endptr = XP_STRCHR(ptr, ',');
|
|
|
|
|
if (endptr) *endptr++ = '\0';
|
|
|
|
|
XP_ASSERT(i < num);
|
|
|
|
|
if (i < num) list[i] = ptr;
|
|
|
|
|
}
|
|
|
|
|
XP_QSORT(list, num, sizeof(char*), msg_qsort_domains);
|
|
|
|
|
/* Now, remove any empty entries or duplicates. */
|
|
|
|
|
for (i=0, j=0 ; i<num ; i++) {
|
|
|
|
|
ptr = list[i];
|
|
|
|
|
if (ptr && (j == 0 || XP_STRCMP(ptr, list[j - 1]) != 0)) {
|
|
|
|
|
list[j++] = ptr;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
num = j;
|
|
|
|
|
}
|
|
|
|
|
*numfound = num;
|
|
|
|
|
*returnlist = list;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static PRBool
|
|
|
|
|
HTMLDomainsDialogDone(XPDialogState* state, char** argv, int argc,
|
|
|
|
|
unsigned int button)
|
|
|
|
|
{
|
|
|
|
|
char* domainlist = NULL;
|
|
|
|
|
char* gone;
|
|
|
|
|
char* ptr;
|
|
|
|
|
char* endptr;
|
|
|
|
|
char** list = NULL;
|
|
|
|
|
int num;
|
|
|
|
|
int i;
|
|
|
|
|
int status;
|
|
|
|
|
|
|
|
|
|
if (button != XP_DIALOG_OK_BUTTON) return PR_FALSE;
|
|
|
|
|
PREF_CopyCharPref("mail.htmldomains", &domainlist);
|
|
|
|
|
if (!domainlist || !*domainlist) return PR_FALSE;
|
|
|
|
|
gone = XP_FindValueInArgs("gone", argv, argc);
|
|
|
|
|
XP_ASSERT(gone);
|
|
|
|
|
if (!gone || !*gone) return PR_FALSE;
|
|
|
|
|
|
|
|
|
|
status = msg_generate_domains_list(domainlist, &num, &list);
|
|
|
|
|
if (status < 0) goto FAIL;
|
|
|
|
|
|
|
|
|
|
for (ptr = gone ; ptr ; ptr = endptr) {
|
|
|
|
|
endptr = XP_STRCHR(ptr, ',');
|
|
|
|
|
if (endptr) *endptr++ = '\0';
|
|
|
|
|
if (*ptr) {
|
|
|
|
|
i = atoi(ptr);
|
|
|
|
|
XP_ASSERT(i >= 0 && i < num);
|
|
|
|
|
if (i >= 0 && i < num) {
|
|
|
|
|
XP_ASSERT(list[i]);
|
|
|
|
|
list[i] = NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
ptr = NULL;
|
|
|
|
|
for (i=0 ; i<num ; i++) {
|
|
|
|
|
if (list[i]) {
|
|
|
|
|
if (ptr) StrAllocCat(ptr, ",");
|
|
|
|
|
StrAllocCat(ptr, list[i]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
PREF_SetCharPref("mail.htmldomains", ptr ? ptr : "");
|
|
|
|
|
PREF_SavePrefFile();
|
|
|
|
|
FREEIF(ptr);
|
|
|
|
|
|
|
|
|
|
FAIL:
|
|
|
|
|
FREEIF(list);
|
|
|
|
|
FREEIF(domainlist);
|
|
|
|
|
|
|
|
|
|
return PR_FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
MSG_DisplayHTMLDomainsDialog(MWContext* context)
|
|
|
|
|
{
|
|
|
|
|
static XPDialogInfo dialogInfo = {
|
|
|
|
|
XP_DIALOG_OK_BUTTON | XP_DIALOG_CANCEL_BUTTON,
|
|
|
|
|
HTMLDomainsDialogDone,
|
|
|
|
|
600,
|
|
|
|
|
440
|
|
|
|
|
};
|
|
|
|
|
int status = 0;
|
|
|
|
|
char* domainlist;
|
|
|
|
|
char* list = NULL;
|
|
|
|
|
char* tmp;
|
|
|
|
|
XPDialogStrings* strings;
|
|
|
|
|
int i;
|
|
|
|
|
int num = 0;
|
|
|
|
|
char** array = NULL;
|
|
|
|
|
|
|
|
|
|
PREF_CopyCharPref("mail.htmldomains", &domainlist);
|
|
|
|
|
|
|
|
|
|
status = msg_generate_domains_list(domainlist, &num, &array);
|
|
|
|
|
if (status < 0) goto FAIL;
|
|
|
|
|
|
|
|
|
|
for (i=0 ; i<num ; i++) {
|
|
|
|
|
tmp = PR_smprintf("<option value=%d>%s\n", i, array[i]);
|
|
|
|
|
if (!tmp) {
|
|
|
|
|
status = MK_OUT_OF_MEMORY;
|
|
|
|
|
goto FAIL;
|
|
|
|
|
}
|
|
|
|
|
StrAllocCat(list, tmp);
|
|
|
|
|
XP_FREE(tmp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
strings = XP_GetDialogStrings(MK_MSG_HTML_DOMAINS_DIALOG);
|
|
|
|
|
if (!strings) {
|
|
|
|
|
status = MK_OUT_OF_MEMORY;
|
|
|
|
|
goto FAIL;
|
|
|
|
|
}
|
|
|
|
|
XP_CopyDialogString(strings, 0, list ? list : "");
|
|
|
|
|
XP_MakeHTMLDialog(context, &dialogInfo, MK_MSG_HTML_DOMAINS_DIALOG_TITLE,
|
|
|
|
|
strings, NULL,PR_FALSE);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
FAIL:
|
|
|
|
|
FREEIF(array);
|
|
|
|
|
FREEIF(domainlist);
|
|
|
|
|
FREEIF(list);
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Does in-place modification of input param to conform with son-of-1036 rules
|
|
|
|
|
*
|
|
|
|
|
* A newsgroup component is one portion of the newsgroup name, separated by
|
|
|
|
|
* dots. So mcom.dev.fouroh has three newsgroup components. This function is
|
|
|
|
|
* only concerned with one component because that's what we need for virtual
|
|
|
|
|
* newsgroups.
|
|
|
|
|
*/
|
|
|
|
|
void msg_MakeLegalNewsgroupComponent (char *name)
|
|
|
|
|
{
|
|
|
|
|
int i = 0;
|
|
|
|
|
char ch;
|
|
|
|
|
|
|
|
|
|
while ((ch = name[i]) != '\0')
|
|
|
|
|
{
|
|
|
|
|
/* legal chars are 0-9,a-z, and +,-,_ */
|
|
|
|
|
|
|
|
|
|
if (!(ch >= '0' && ch <= '9'))
|
|
|
|
|
if (!(ch >= 'a' && ch <= 'z'))
|
|
|
|
|
if (!(ch == '+' || ch == '-' || ch == '_'))
|
|
|
|
|
{
|
|
|
|
|
/* ch is illegal. We can lowercase an uppercase letter
|
|
|
|
|
* but everything else goes to some legal char, like '_'
|
|
|
|
|
*/
|
|
|
|
|
if (ch >= 'A' && ch <= 'Z')
|
|
|
|
|
name[i] += 32;
|
|
|
|
|
else
|
|
|
|
|
name[i] = '_';
|
|
|
|
|
}
|
|
|
|
|
i++;
|
|
|
|
|
|
|
|
|
|
/* a newsgroup component is limited to 14 chars */
|
|
|
|
|
if (i > 13)
|
|
|
|
|
{
|
|
|
|
|
name[i] = '\0';
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MSG_SetPercentProgress(MWContext *context, int32 numerator, int32 denominator)
|
|
|
|
|
{
|
|
|
|
|
XP_ASSERT(numerator <= denominator && numerator >= 0 && denominator > 0);
|
|
|
|
|
if (numerator > denominator || numerator < 0 || denominator < 0)
|
|
|
|
|
{
|
|
|
|
|
FE_SetProgressBarPercent(context, -1);
|
|
|
|
|
}
|
|
|
|
|
else if (denominator > 0)
|
|
|
|
|
{
|
|
|
|
|
int32 percent;
|
|
|
|
|
if (denominator > 100L)
|
|
|
|
|
percent = (numerator / (denominator / 100L));
|
|
|
|
|
else
|
|
|
|
|
percent = (100L * numerator) / denominator;
|
|
|
|
|
FE_SetProgressBarPercent (context, percent);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char* MSG_FormatDateFromContext(MWContext *context, time_t date)
|
|
|
|
|
{
|
|
|
|
|
/* fix i18n. Well, maybe. Isn't strftime() supposed to be i18n? */
|
|
|
|
|
/* ftong- Well.... strftime() in Mac and Window is not really i18n */
|
|
|
|
|
/* We need to use XP_StrfTime instead of strftime */
|
|
|
|
|
static char result[40]; /* 30 probably not enough */
|
|
|
|
|
time_t now = time ((time_t *) 0);
|
|
|
|
|
|
|
|
|
|
int32 offset = XP_LocalZoneOffset() * 60L; /* Number of seconds between
|
|
|
|
|
local and GMT. */
|
|
|
|
|
|
|
|
|
|
int32 secsperday = 24L * 60L * 60L;
|
|
|
|
|
|
|
|
|
|
int32 nowday = (now + offset) / secsperday;
|
|
|
|
|
int32 day = (date + offset) / secsperday;
|
|
|
|
|
|
|
|
|
|
if (day == nowday) {
|
|
|
|
|
XP_StrfTime(context, result, sizeof(result), XP_TIME_FORMAT,
|
|
|
|
|
localtime(&date));
|
|
|
|
|
} else if (day < nowday && day > nowday - 7) {
|
|
|
|
|
XP_StrfTime(context, result, sizeof(result), XP_WEEKDAY_TIME_FORMAT,
|
|
|
|
|
localtime(&date));
|
|
|
|
|
} else {
|
|
|
|
|
#if defined (XP_WIN)
|
|
|
|
|
if (date < 0 || date > 0x7FFFFFFF)
|
|
|
|
|
date = 0x7FFFFFFF;
|
|
|
|
|
#endif
|
|
|
|
|
XP_StrfTime(context, result, sizeof(result), XP_DATE_TIME_FORMAT,
|
|
|
|
|
localtime(&date));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|