/* -*- 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; }