mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-05 00:25:27 +00:00
619 lines
13 KiB
C++
619 lines
13 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/* ***** BEGIN LICENSE BLOCK *****
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
*
|
|
* The contents of this file are subject to the Mozilla 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/MPL/
|
|
*
|
|
* 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.
|
|
*
|
|
* The Original Code is mozilla.org code.
|
|
*
|
|
* The Initial Developer of the Original Code is
|
|
* Netscape Communications Corporation.
|
|
* Portions created by the Initial Developer are Copyright (C) 1998
|
|
* the Initial Developer. All Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
*
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
|
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
* the provisions above, a recipient may use your version of this file under
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
*
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
#include "msgCore.h" // precompiled header...
|
|
#include "prlog.h"
|
|
|
|
#include "nsMsgGroupRecord.h"
|
|
|
|
#include "plstr.h"
|
|
#include "prmem.h"
|
|
#include "prprf.h"
|
|
#include "nsEscape.h"
|
|
#include "nsCRT.h"
|
|
|
|
// mscott: this is lame...I know....
|
|
#define MK_OUT_OF_MEMORY 1
|
|
|
|
const PRUint32 F_ISGROUP = 0x00000001;
|
|
const PRUint32 F_EXPANDED = 0x00000002;
|
|
const PRUint32 F_CATCONT = 0x00000004;
|
|
const PRUint32 F_VIRTUAL = 0x00000008;
|
|
const PRUint32 F_DIRTY = 0x00000010;
|
|
const PRUint32 F_DESCENDENTSLOADED = 0x00000020;
|
|
const PRUint32 F_HTMLOKGROUP = 0x00000040;
|
|
const PRUint32 F_HTMLOKTREE = 0x00000080;
|
|
const PRUint32 F_NEEDEXTRAINFO = 0x00000100;
|
|
const PRUint32 F_DOESNOTEXIST = 0x00000200;
|
|
const PRUint32 RUNTIMEFLAGS = // Flags to be sure *not* to write to disk.
|
|
F_DIRTY | F_DESCENDENTSLOADED | F_EXPANDED;
|
|
|
|
|
|
|
|
int
|
|
nsMsgGroupRecord::GroupNameCompare(const char* name1, const char* name2,
|
|
char delimiter, PRBool caseInsensitive)
|
|
{
|
|
if (caseInsensitive)
|
|
{
|
|
while (*name1 && (nsCRT::ToUpper(*name1) == nsCRT::ToUpper(*name2))) {
|
|
name1++;
|
|
name2++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while (*name1 && *name1 == *name2) {
|
|
name1++;
|
|
name2++;
|
|
}
|
|
}
|
|
|
|
if (*name1 && *name2) {
|
|
if (*name1 == delimiter) return -1;
|
|
if (*name2 == delimiter) return 1;
|
|
}
|
|
|
|
if (caseInsensitive)
|
|
return int(nsCRT::ToUpper(*name1)) - int(nsCRT::ToUpper(*name2));
|
|
else
|
|
return int(*name1) - int(*name2);
|
|
}
|
|
|
|
|
|
nsMsgGroupRecord*
|
|
nsMsgGroupRecord::Create(nsMsgGroupRecord* parent, const char* partname,
|
|
PRInt64 aTime, PRInt32 uniqueid, PRInt32 fileoffset)
|
|
{
|
|
nsMsgGroupRecord* result = new nsMsgGroupRecord(parent, partname,
|
|
aTime, uniqueid, fileoffset);
|
|
if (result && partname && !result->m_partname) {
|
|
// We ran out of memory.
|
|
delete result;
|
|
result = NULL;
|
|
}
|
|
result->InitializeSibling();
|
|
return result;
|
|
}
|
|
|
|
MOZ_DECL_CTOR_COUNTER(nsMsgGroupRecord)
|
|
|
|
nsMsgGroupRecord::nsMsgGroupRecord(nsMsgGroupRecord* parent, const char* partname,
|
|
PRInt64 aTime, PRInt32 uniqueid, PRInt32 fileoffset,
|
|
char delimiter /* = '.' */)
|
|
{
|
|
MOZ_COUNT_CTOR(nsMsgGroupRecord);
|
|
int length;
|
|
m_prettyname = NULL;
|
|
m_parent = parent;
|
|
m_children = NULL;
|
|
m_sibling = NULL;
|
|
m_flags = 0;
|
|
m_partname = NULL;
|
|
m_addtime = aTime;
|
|
m_uniqueId = uniqueid;
|
|
m_fileoffset = fileoffset;
|
|
m_delimiter = delimiter;
|
|
if (partname) {
|
|
length = PL_strlen(partname);
|
|
// PR_ASSERT(parent != NULL);
|
|
m_partname = new char [length + 1];
|
|
if (!m_partname) {
|
|
m_parent = NULL;
|
|
return;
|
|
}
|
|
PL_strcpy(m_partname, partname);
|
|
}
|
|
}
|
|
|
|
|
|
nsMsgGroupRecord::~nsMsgGroupRecord()
|
|
{
|
|
MOZ_COUNT_DTOR(nsMsgGroupRecord);
|
|
delete [] m_partname;
|
|
m_partname = NULL;
|
|
delete [] m_prettyname;
|
|
m_prettyname = NULL;
|
|
while (m_children) {
|
|
delete m_children;
|
|
}
|
|
m_children = NULL;
|
|
if (m_parent) {
|
|
nsMsgGroupRecord** ptr;
|
|
for (ptr = &(m_parent->m_children);
|
|
*ptr;
|
|
ptr = &((*ptr)->m_sibling)) {
|
|
if (*ptr == this) {
|
|
*ptr = m_sibling;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void nsMsgGroupRecord::InitializeSibling()
|
|
{
|
|
if (m_parent) {
|
|
PR_ASSERT(m_partname != NULL);
|
|
nsMsgGroupRecord** ptr;
|
|
for (ptr = &(m_parent->m_children) ; *ptr ; ptr = &((*ptr)->m_sibling)) {
|
|
int comp = GroupNameCompare((*ptr)->m_partname, m_partname, m_delimiter, IsIMAPGroupRecord());
|
|
PR_ASSERT(comp != 0);
|
|
if (comp >= 0) break;
|
|
}
|
|
m_sibling = *ptr;
|
|
*ptr = this;
|
|
}
|
|
}
|
|
|
|
|
|
nsMsgGroupRecord*
|
|
nsMsgGroupRecord::FindDescendant(const char* name)
|
|
{
|
|
if (!name || !*name) return this;
|
|
char* ptr = PL_strchr(name, m_delimiter);
|
|
if (ptr) *ptr = '\0';
|
|
nsMsgGroupRecord* child;
|
|
for (child = m_children ; child ; child = child->m_sibling) {
|
|
if (PL_strcmp(child->m_partname, name) == 0) {
|
|
break;
|
|
}
|
|
}
|
|
if (ptr) {
|
|
*ptr++ = m_delimiter;
|
|
if (child) {
|
|
return child->FindDescendant(ptr);
|
|
}
|
|
}
|
|
return child;
|
|
}
|
|
|
|
|
|
nsMsgGroupRecord*
|
|
nsMsgGroupRecord::GetSiblingOrAncestorSibling()
|
|
{
|
|
if (m_sibling) return m_sibling;
|
|
if (m_parent) return m_parent->GetSiblingOrAncestorSibling();
|
|
return NULL;
|
|
}
|
|
|
|
nsMsgGroupRecord*
|
|
nsMsgGroupRecord::GetNextAlphabetic()
|
|
{
|
|
nsMsgGroupRecord* result;
|
|
if (m_children) result = m_children;
|
|
else result = GetSiblingOrAncestorSibling();
|
|
#ifdef DEBUG_slowAndParanoid
|
|
if (result) {
|
|
char* ptr1 = GetFullName();
|
|
char* ptr2 = result->GetFullName();
|
|
PR_ASSERT(GroupNameCompare(ptr1, ptr2) < 0);
|
|
delete [] ptr1;
|
|
delete [] ptr2;
|
|
}
|
|
#endif
|
|
return result;
|
|
}
|
|
|
|
nsMsgGroupRecord*
|
|
nsMsgGroupRecord::GetNextAlphabeticNoCategories()
|
|
{
|
|
if (IsCategoryContainer()) {
|
|
return GetSiblingOrAncestorSibling();
|
|
} else {
|
|
return GetNextAlphabetic();
|
|
}
|
|
}
|
|
|
|
|
|
char*
|
|
nsMsgGroupRecord::GetFullName()
|
|
{
|
|
int length = 0;
|
|
nsMsgGroupRecord* ptr;
|
|
for (ptr = this ; ptr ; ptr = ptr->m_parent) {
|
|
if (ptr->m_partname) length += PL_strlen(ptr->m_partname) + 1;
|
|
}
|
|
PR_ASSERT(length > 0);
|
|
if (length <= 0) return NULL;
|
|
char* result = new char [length];
|
|
if (result) {
|
|
SuckInName(result);
|
|
PR_ASSERT(int(PL_strlen(result)) + 1 == length);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
char*
|
|
nsMsgGroupRecord::SuckInName(char* ptr)
|
|
{
|
|
if (m_parent && m_parent->m_partname) {
|
|
ptr = m_parent->SuckInName(ptr);
|
|
*ptr++ = m_delimiter;
|
|
}
|
|
PL_strcpy(ptr, m_partname);
|
|
return ptr + PL_strlen(ptr);
|
|
}
|
|
|
|
|
|
|
|
int
|
|
nsMsgGroupRecord::SetPrettyName(const char* name)
|
|
{
|
|
if (name == NULL && m_prettyname == NULL) return 0;
|
|
m_flags |= F_DIRTY;
|
|
delete [] m_prettyname;
|
|
m_prettyname = NULL;
|
|
if (!name || !*name) {
|
|
return 0;
|
|
}
|
|
int length = PL_strlen(name);
|
|
m_prettyname = new char [length + 1];
|
|
if (!m_prettyname) {
|
|
return MK_OUT_OF_MEMORY;
|
|
}
|
|
PL_strcpy(m_prettyname, name);
|
|
return 1;
|
|
}
|
|
|
|
|
|
PRBool
|
|
nsMsgGroupRecord::IsCategory()
|
|
{
|
|
return GetCategoryContainer() != NULL;
|
|
}
|
|
|
|
PRBool
|
|
nsMsgGroupRecord::IsCategoryContainer()
|
|
{
|
|
return (m_flags & F_CATCONT) != 0;
|
|
}
|
|
|
|
PRBool
|
|
nsMsgGroupRecord::NeedsExtraInfo()
|
|
{
|
|
return (m_flags & F_NEEDEXTRAINFO) != 0;
|
|
}
|
|
|
|
int
|
|
nsMsgGroupRecord::SetNeedsExtraInfo(PRBool value)
|
|
{
|
|
return TweakFlag(F_NEEDEXTRAINFO, value);
|
|
}
|
|
|
|
|
|
int
|
|
nsMsgGroupRecord::SetIsCategoryContainer(PRBool value)
|
|
{
|
|
// refuse to set a group to be a category container if it has a parent
|
|
// that's a category container.
|
|
if (! (value && GetCategoryContainer()))
|
|
return TweakFlag(F_CATCONT, value);
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
|
|
nsMsgGroupRecord*
|
|
nsMsgGroupRecord::GetCategoryContainer()
|
|
{
|
|
if (IsCategoryContainer()) return NULL;
|
|
for (nsMsgGroupRecord* ptr = m_parent ; ptr ; ptr = ptr->m_parent) {
|
|
if (ptr->IsCategoryContainer()) return ptr;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
nsresult
|
|
nsMsgGroupRecord::IsVirtual(PRBool *retval)
|
|
{
|
|
*retval =( (m_flags & F_VIRTUAL) != 0);
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
nsMsgGroupRecord::SetIsVirtual(PRBool value)
|
|
{
|
|
TweakFlag(F_VIRTUAL, value);
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
|
|
PRBool
|
|
nsMsgGroupRecord::IsExpanded()
|
|
{
|
|
return (m_flags & F_EXPANDED) != 0;
|
|
}
|
|
|
|
int
|
|
nsMsgGroupRecord::SetIsExpanded(PRBool value)
|
|
{
|
|
return TweakFlag(F_EXPANDED, value);
|
|
}
|
|
|
|
|
|
PRBool
|
|
nsMsgGroupRecord::IsHTMLOKGroup()
|
|
{
|
|
return (m_flags & F_HTMLOKGROUP) != 0;
|
|
}
|
|
|
|
int
|
|
nsMsgGroupRecord::SetIsHTMLOKGroup(PRBool value)
|
|
{
|
|
return TweakFlag(F_HTMLOKGROUP, value);
|
|
}
|
|
|
|
|
|
|
|
PRBool
|
|
nsMsgGroupRecord::IsHTMLOKTree()
|
|
{
|
|
return (m_flags & F_HTMLOKTREE) != 0;
|
|
}
|
|
|
|
int
|
|
nsMsgGroupRecord::SetIsHTMLOKTree(PRBool value)
|
|
{
|
|
return TweakFlag(F_HTMLOKTREE, value);
|
|
}
|
|
|
|
|
|
|
|
PRBool
|
|
nsMsgGroupRecord::IsGroup()
|
|
{
|
|
return (m_flags & F_ISGROUP) != 0;
|
|
}
|
|
|
|
int
|
|
nsMsgGroupRecord::SetIsGroup(PRBool value)
|
|
{
|
|
return TweakFlag(F_ISGROUP, value);
|
|
}
|
|
|
|
|
|
PRBool
|
|
nsMsgGroupRecord::IsDescendentsLoaded()
|
|
{
|
|
return (m_flags & F_DESCENDENTSLOADED) != 0;
|
|
}
|
|
|
|
|
|
int
|
|
nsMsgGroupRecord::SetIsDescendentsLoaded(PRBool value)
|
|
{
|
|
PR_ASSERT(value); // No reason we'd ever unset this.
|
|
TweakFlag(F_DESCENDENTSLOADED, PR_TRUE);
|
|
nsMsgGroupRecord* child;
|
|
for (child = m_children ; child ; child = child->m_sibling) {
|
|
child->SetIsDescendentsLoaded(value);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
PRBool nsMsgGroupRecord::DoesNotExistOnServer()
|
|
{
|
|
return (m_flags & F_DOESNOTEXIST) != 0;
|
|
}
|
|
|
|
int nsMsgGroupRecord::SetDoesNotExistOnServer(PRBool value)
|
|
{
|
|
if (value) // turn off group flag if doesn't exist on server.
|
|
TweakFlag(F_ISGROUP, PR_FALSE);
|
|
return TweakFlag(F_DOESNOTEXIST, value);
|
|
}
|
|
|
|
int
|
|
nsMsgGroupRecord::TweakFlag(PRUint32 flagbit, PRBool value)
|
|
{
|
|
if (value) {
|
|
if (!(m_flags & flagbit)) {
|
|
m_flags |= flagbit;
|
|
if (flagbit & ~RUNTIMEFLAGS)
|
|
m_flags |= F_DIRTY;
|
|
return 1;
|
|
}
|
|
} else {
|
|
if (m_flags & flagbit) {
|
|
m_flags &= ~flagbit;
|
|
if (flagbit & ~RUNTIMEFLAGS)
|
|
m_flags |= F_DIRTY;
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
PRInt32
|
|
nsMsgGroupRecord::GetNumKids()
|
|
{
|
|
PRInt32 result = 0;
|
|
nsMsgGroupRecord* child;
|
|
for (child = m_children ; child ; child = child->m_sibling) {
|
|
if (IsIMAPGroupRecord())
|
|
result++;
|
|
else
|
|
if (child->m_flags & F_ISGROUP) result++;
|
|
|
|
if (!IsIMAPGroupRecord())
|
|
result += child->GetNumKids();
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
|
|
char*
|
|
nsMsgGroupRecord::GetSaveString()
|
|
{
|
|
char* pretty = NULL;
|
|
char* result = nsnull;
|
|
|
|
if (m_prettyname) {
|
|
pretty = nsEscape(m_prettyname, url_XAlphas);
|
|
if (!pretty) return NULL;
|
|
}
|
|
char* fullname = GetFullName();
|
|
if (!fullname) return NULL; {
|
|
long nAddTime;
|
|
LL_L2I(nAddTime, m_addtime);
|
|
result = PR_smprintf("%s,%s,%lx,%lx,%lx" MSG_LINEBREAK,
|
|
fullname, pretty ? pretty : "",
|
|
(long) (m_flags & ~RUNTIMEFLAGS),
|
|
nAddTime,
|
|
(long) m_uniqueId);
|
|
}
|
|
delete [] fullname;
|
|
if (pretty) nsCRT::free(pretty);
|
|
m_flags &= ~F_DIRTY;
|
|
return result;
|
|
}
|
|
|
|
|
|
PRBool
|
|
nsMsgGroupRecord::IsDirty()
|
|
{
|
|
return (m_flags & F_DIRTY) != 0;
|
|
}
|
|
|
|
|
|
PRInt32
|
|
nsMsgGroupRecord::GetDepth()
|
|
{
|
|
PRInt32 result = 0;
|
|
nsMsgGroupRecord* tmp = m_parent;
|
|
while (tmp) {
|
|
tmp = tmp->m_parent;
|
|
result++;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
|
|
nsMsgGroupRecord*
|
|
nsMsgGroupRecord::Create(nsMsgGroupRecord* parent, const char* saveline,
|
|
PRInt32 savelinelength, PRInt32 fileoffset)
|
|
{
|
|
char* tmp;
|
|
char* ptr;
|
|
char* endptr;
|
|
char* partname;
|
|
char* prettyname;
|
|
PRInt32 flags;
|
|
PRInt32 addtime;
|
|
PRInt32 uniqueid;
|
|
nsMsgGroupRecord* result = NULL;
|
|
|
|
if (savelinelength < 0) savelinelength = PL_strlen(saveline);
|
|
tmp = (char*) PR_Malloc(savelinelength + 1);
|
|
if (!tmp) return NULL;
|
|
PL_strncpy(tmp, saveline, savelinelength);
|
|
tmp[savelinelength] = '\0';
|
|
ptr = PL_strchr(tmp, ',');
|
|
PR_ASSERT(ptr);
|
|
if (!ptr) goto FAIL;
|
|
*ptr++ = '\0';
|
|
partname = PL_strrchr(tmp, '.');
|
|
if (!partname) partname = tmp;
|
|
else partname++;
|
|
|
|
#ifdef DEBUG_slowAndParanoid
|
|
if (parent->m_partname) {
|
|
char* parentname = parent->GetFullName();
|
|
PR_ASSERT(partname > tmp && partname[-1] == '.');
|
|
partname[-1] = '\0';
|
|
PR_ASSERT(PL_strcmp(parentname, tmp) == 0);
|
|
partname[-1] = '.';
|
|
delete [] parentname;
|
|
parentname = NULL;
|
|
} else {
|
|
PR_ASSERT(partname == tmp);
|
|
}
|
|
#endif
|
|
|
|
endptr = PL_strchr(ptr, ',');
|
|
PR_ASSERT(endptr);
|
|
if (!endptr) goto FAIL;
|
|
*endptr++ = '\0';
|
|
prettyname = nsUnescape(ptr);
|
|
|
|
ptr = endptr;
|
|
endptr = PL_strchr(ptr, ',');
|
|
PR_ASSERT(endptr);
|
|
if (!endptr) goto FAIL;
|
|
*endptr++ = '\0';
|
|
flags = strtol(ptr, NULL, 16);
|
|
|
|
ptr = endptr;
|
|
endptr = PL_strchr(ptr, ',');
|
|
PR_ASSERT(endptr);
|
|
if (!endptr) goto FAIL;
|
|
*endptr++ = '\0';
|
|
addtime = strtol(ptr, NULL, 16);
|
|
|
|
ptr = endptr;
|
|
uniqueid = strtol(ptr, NULL, 16);
|
|
|
|
PRInt64 llAddtime;
|
|
LL_I2L(llAddtime, addtime);
|
|
result = Create(parent, partname, llAddtime, uniqueid, fileoffset);
|
|
if (result) {
|
|
PRBool maybeCategoryContainer = flags & F_CATCONT;
|
|
flags &= ~F_CATCONT;
|
|
result->m_flags = flags;
|
|
if (maybeCategoryContainer)
|
|
result->SetIsCategoryContainer(PR_TRUE);
|
|
if (prettyname && *prettyname) result->SetPrettyName(prettyname);
|
|
}
|
|
|
|
FAIL:
|
|
PR_Free(tmp);
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|