зеркало из https://github.com/mozilla/pjs.git
853 строки
21 KiB
C
853 строки
21 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.
|
||
*/
|
||
/* 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,
|
||
<09>// 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;
|
||
}
|
||
|