Back to index

lightning-sunbird  0.9+nobinonly
nsMsgGroupRecord.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either of the GNU General Public License Version 2 or later (the "GPL"),
00026  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #include "msgCore.h"    // precompiled header...
00039 #include "prlog.h"
00040 
00041 #include "nsMsgGroupRecord.h"
00042 
00043 #include "plstr.h"
00044 #include "prmem.h"
00045 #include "prprf.h"
00046 #include "nsEscape.h"
00047 #include "nsCRT.h"
00048 
00049 // mscott: this is lame...I know....
00050 #define MK_OUT_OF_MEMORY  1
00051 
00052 const PRUint32 F_ISGROUP                   = 0x00000001;
00053 const PRUint32 F_EXPANDED                  = 0x00000002;
00054 const PRUint32 F_CATCONT                   = 0x00000004;
00055 const PRUint32 F_VIRTUAL                   = 0x00000008;
00056 const PRUint32 F_DIRTY                     = 0x00000010;
00057 const PRUint32 F_DESCENDENTSLOADED = 0x00000020;
00058 const PRUint32 F_HTMLOKGROUP               = 0x00000040;
00059 const PRUint32 F_HTMLOKTREE         = 0x00000080;
00060 const PRUint32 F_NEEDEXTRAINFO      = 0x00000100;
00061 const PRUint32 F_DOESNOTEXIST              = 0x00000200;
00062 const PRUint32 RUNTIMEFLAGS =             // Flags to be sure *not* to write to disk.
00063        F_DIRTY | F_DESCENDENTSLOADED | F_EXPANDED;
00064 
00065 
00066 
00067 int
00068 nsMsgGroupRecord::GroupNameCompare(const char* name1, const char* name2,
00069                                                           char delimiter, PRBool caseInsensitive)
00070 {
00071        if (caseInsensitive)
00072        {
00073               while (*name1 && (nsCRT::ToUpper(*name1) == nsCRT::ToUpper(*name2))) {
00074                      name1++;
00075                      name2++;
00076               }             
00077        }
00078        else
00079        {
00080               while (*name1 && *name1 == *name2) {
00081                      name1++;
00082                      name2++;
00083               }
00084        }
00085 
00086        if (*name1 && *name2) {
00087               if (*name1 == delimiter) return -1;
00088               if (*name2 == delimiter) return 1;
00089        }
00090 
00091        if (caseInsensitive)
00092               return int(nsCRT::ToUpper(*name1)) - int(nsCRT::ToUpper(*name2));
00093        else
00094               return int(*name1) - int(*name2);
00095 }
00096 
00097 
00098 nsMsgGroupRecord*
00099 nsMsgGroupRecord::Create(nsMsgGroupRecord* parent, const char* partname,
00100                                           PRInt64 aTime, PRInt32 uniqueid, PRInt32 fileoffset)
00101 {
00102        nsMsgGroupRecord* result = new nsMsgGroupRecord(parent, partname,
00103                                                                                       aTime, uniqueid, fileoffset);
00104        if (result && partname && !result->m_partname) {
00105               // We ran out of memory.
00106               delete result;
00107               result = NULL;
00108        }
00109        result->InitializeSibling();
00110        return result;
00111 }
00112 
00113 MOZ_DECL_CTOR_COUNTER(nsMsgGroupRecord)
00114 
00115 nsMsgGroupRecord::nsMsgGroupRecord(nsMsgGroupRecord* parent, const char* partname,
00116                                                          PRInt64 aTime, PRInt32 uniqueid, PRInt32 fileoffset,
00117                                                          char delimiter /* = '.' */)
00118 {
00119        MOZ_COUNT_CTOR(nsMsgGroupRecord);
00120        int length;
00121        m_prettyname = NULL;
00122        m_parent = parent;
00123        m_children = NULL;
00124        m_sibling = NULL;
00125        m_flags = 0;
00126        m_partname = NULL;
00127        m_addtime = aTime;
00128        m_uniqueId = uniqueid;
00129        m_fileoffset = fileoffset;
00130        m_delimiter = delimiter;
00131        if (partname) {
00132               length = PL_strlen(partname);
00133               // PR_ASSERT(parent != NULL);
00134               m_partname = new char [length + 1];
00135               if (!m_partname) {
00136                      m_parent = NULL;
00137                      return;
00138               }
00139               PL_strcpy(m_partname, partname);
00140        }
00141 }
00142 
00143 
00144 nsMsgGroupRecord::~nsMsgGroupRecord()
00145 {
00146        MOZ_COUNT_DTOR(nsMsgGroupRecord);
00147        delete [] m_partname;
00148        m_partname = NULL;
00149        delete [] m_prettyname;
00150        m_prettyname = NULL;
00151        while (m_children) {
00152               delete m_children;
00153        }
00154        m_children = NULL;
00155        if (m_parent) {
00156               nsMsgGroupRecord** ptr;
00157               for (ptr = &(m_parent->m_children);
00158                       *ptr;
00159                       ptr = &((*ptr)->m_sibling)) {
00160                      if (*ptr == this) {
00161                             *ptr = m_sibling;
00162                             break;
00163                      }
00164               }
00165        }
00166 }
00167 
00168 void nsMsgGroupRecord::InitializeSibling()
00169 {
00170        if (m_parent) {
00171               PR_ASSERT(m_partname != NULL);
00172               nsMsgGroupRecord** ptr;
00173               for (ptr = &(m_parent->m_children) ; *ptr ; ptr = &((*ptr)->m_sibling)) {
00174                      int comp = GroupNameCompare((*ptr)->m_partname, m_partname, m_delimiter, IsIMAPGroupRecord());
00175                      PR_ASSERT(comp != 0);
00176                      if (comp >= 0) break;
00177               }
00178               m_sibling = *ptr;
00179               *ptr = this;
00180        }
00181 }
00182 
00183 
00184 nsMsgGroupRecord*
00185 nsMsgGroupRecord::FindDescendant(const char* name)
00186 {
00187        if (!name || !*name) return this;
00188        char* ptr = PL_strchr(name, m_delimiter);
00189        if (ptr) *ptr = '\0';
00190        nsMsgGroupRecord* child;
00191        for (child = m_children ; child ; child = child->m_sibling) {
00192               if (PL_strcmp(child->m_partname, name) == 0) {
00193                      break;
00194               }
00195        }
00196        if (ptr) {
00197               *ptr++ = m_delimiter;
00198               if (child) {
00199                      return child->FindDescendant(ptr);
00200               }
00201        }
00202        return child;
00203 }
00204 
00205 
00206 nsMsgGroupRecord*
00207 nsMsgGroupRecord::GetSiblingOrAncestorSibling()
00208 {
00209        if (m_sibling) return m_sibling;
00210        if (m_parent) return m_parent->GetSiblingOrAncestorSibling();
00211        return NULL;
00212 }
00213 
00214 nsMsgGroupRecord*
00215 nsMsgGroupRecord::GetNextAlphabetic()
00216 {
00217        nsMsgGroupRecord* result;
00218        if (m_children) result = m_children;
00219        else result = GetSiblingOrAncestorSibling();
00220 #ifdef DEBUG_slowAndParanoid
00221        if (result) {
00222               char* ptr1 = GetFullName();
00223               char* ptr2 = result->GetFullName();
00224               PR_ASSERT(GroupNameCompare(ptr1, ptr2) < 0);
00225               delete [] ptr1;
00226               delete [] ptr2;
00227        }
00228 #endif
00229        return result;
00230 }
00231 
00232 nsMsgGroupRecord*
00233 nsMsgGroupRecord::GetNextAlphabeticNoCategories()
00234 {
00235        if (IsCategoryContainer()) {
00236               return GetSiblingOrAncestorSibling();
00237        } else {
00238               return GetNextAlphabetic();
00239        }
00240 }
00241 
00242 
00243 char*
00244 nsMsgGroupRecord::GetFullName()
00245 {
00246        int length = 0;
00247        nsMsgGroupRecord* ptr;
00248        for (ptr = this ; ptr ; ptr = ptr->m_parent) {
00249               if (ptr->m_partname) length += PL_strlen(ptr->m_partname) + 1;
00250        }
00251        PR_ASSERT(length > 0);
00252        if (length <= 0) return NULL;
00253        char* result = new char [length];
00254        if (result) {
00255               SuckInName(result);
00256               PR_ASSERT(int(PL_strlen(result)) + 1 == length);
00257        }
00258        return result;
00259 }
00260 
00261 
00262 char* 
00263 nsMsgGroupRecord::SuckInName(char* ptr)
00264 {
00265        if (m_parent && m_parent->m_partname) {
00266               ptr = m_parent->SuckInName(ptr);
00267               *ptr++ = m_delimiter;
00268        }
00269        PL_strcpy(ptr, m_partname);
00270        return ptr + PL_strlen(ptr);
00271 }
00272 
00273 
00274 
00275 int
00276 nsMsgGroupRecord::SetPrettyName(const char* name)
00277 {
00278        if (name == NULL && m_prettyname == NULL) return 0;
00279        m_flags |= F_DIRTY;
00280        delete [] m_prettyname;
00281        m_prettyname = NULL;
00282        if (!name || !*name) {
00283               return 0;
00284        }
00285        int length = PL_strlen(name);
00286        m_prettyname = new char [length + 1];
00287        if (!m_prettyname) {
00288               return MK_OUT_OF_MEMORY;
00289        }
00290        PL_strcpy(m_prettyname, name);
00291        return 1;
00292 }
00293 
00294 
00295 PRBool
00296 nsMsgGroupRecord::IsCategory()
00297 {
00298        return GetCategoryContainer() != NULL;
00299 }
00300 
00301 PRBool
00302 nsMsgGroupRecord::IsCategoryContainer()
00303 {
00304        return (m_flags & F_CATCONT) != 0;
00305 }
00306 
00307 PRBool
00308 nsMsgGroupRecord::NeedsExtraInfo()
00309 {
00310        return (m_flags & F_NEEDEXTRAINFO) != 0;
00311 }
00312 
00313 int
00314 nsMsgGroupRecord::SetNeedsExtraInfo(PRBool value)
00315 {
00316        return TweakFlag(F_NEEDEXTRAINFO, value);
00317 }
00318 
00319 
00320 int
00321 nsMsgGroupRecord::SetIsCategoryContainer(PRBool value)
00322 {
00323        // refuse to set a group to be a category container if it has a parent
00324        // that's a category container.
00325        if (! (value && GetCategoryContainer()))
00326               return TweakFlag(F_CATCONT, value);
00327        else
00328               return 0;
00329 }
00330 
00331 
00332 nsMsgGroupRecord*
00333 nsMsgGroupRecord::GetCategoryContainer()
00334 {
00335        if (IsCategoryContainer()) return NULL;
00336        for (nsMsgGroupRecord* ptr = m_parent ; ptr ; ptr = ptr->m_parent) {
00337               if (ptr->IsCategoryContainer()) return ptr;
00338        }
00339        return NULL;
00340 }
00341 
00342 
00343 nsresult
00344 nsMsgGroupRecord::IsVirtual(PRBool *retval)
00345 {
00346        *retval =( (m_flags & F_VIRTUAL) != 0);
00347     return NS_OK;
00348 }
00349 
00350 nsresult
00351 nsMsgGroupRecord::SetIsVirtual(PRBool value)
00352 {
00353        TweakFlag(F_VIRTUAL, value);
00354     return NS_OK;
00355 }
00356 
00357 
00358 
00359 PRBool
00360 nsMsgGroupRecord::IsExpanded()
00361 {
00362        return (m_flags & F_EXPANDED) != 0;
00363 }
00364 
00365 int
00366 nsMsgGroupRecord::SetIsExpanded(PRBool value)
00367 {
00368        return TweakFlag(F_EXPANDED, value);
00369 }
00370 
00371 
00372 PRBool
00373 nsMsgGroupRecord::IsHTMLOKGroup()
00374 {
00375        return (m_flags & F_HTMLOKGROUP) != 0;
00376 }
00377 
00378 int
00379 nsMsgGroupRecord::SetIsHTMLOKGroup(PRBool value)
00380 {
00381        return TweakFlag(F_HTMLOKGROUP, value);
00382 }
00383 
00384 
00385 
00386 PRBool
00387 nsMsgGroupRecord::IsHTMLOKTree()
00388 {
00389        return (m_flags & F_HTMLOKTREE) != 0;
00390 }
00391 
00392 int
00393 nsMsgGroupRecord::SetIsHTMLOKTree(PRBool value)
00394 {
00395        return TweakFlag(F_HTMLOKTREE, value);
00396 }
00397 
00398 
00399 
00400 PRBool
00401 nsMsgGroupRecord::IsGroup()
00402 {
00403        return (m_flags & F_ISGROUP) != 0;
00404 }
00405 
00406 int
00407 nsMsgGroupRecord::SetIsGroup(PRBool value)
00408 {
00409        return TweakFlag(F_ISGROUP, value);
00410 }
00411 
00412 
00413 PRBool
00414 nsMsgGroupRecord::IsDescendentsLoaded()
00415 {
00416        return (m_flags & F_DESCENDENTSLOADED) != 0;
00417 }
00418 
00419 
00420 int
00421 nsMsgGroupRecord::SetIsDescendentsLoaded(PRBool value)
00422 {
00423        PR_ASSERT(value);                  // No reason we'd ever unset this.
00424        TweakFlag(F_DESCENDENTSLOADED, PR_TRUE);
00425        nsMsgGroupRecord* child;
00426        for (child = m_children ; child ; child = child->m_sibling) {
00427               child->SetIsDescendentsLoaded(value);
00428        }
00429        return 0;
00430 }
00431 
00432 PRBool nsMsgGroupRecord::DoesNotExistOnServer()
00433 {
00434        return (m_flags & F_DOESNOTEXIST) != 0;
00435 }
00436 
00437 int nsMsgGroupRecord::SetDoesNotExistOnServer(PRBool value)
00438 {
00439        if (value)           // turn off group flag if doesn't exist on server.
00440               TweakFlag(F_ISGROUP, PR_FALSE);
00441        return TweakFlag(F_DOESNOTEXIST, value);
00442 }
00443 
00444 int
00445 nsMsgGroupRecord::TweakFlag(PRUint32 flagbit, PRBool value)
00446 {
00447        if (value) {
00448               if (!(m_flags & flagbit)) {
00449                      m_flags |= flagbit;
00450                      if (flagbit & ~RUNTIMEFLAGS)
00451                             m_flags |= F_DIRTY;
00452                      return 1;
00453               }
00454        } else {
00455               if (m_flags & flagbit) {
00456                      m_flags &= ~flagbit;
00457                      if (flagbit & ~RUNTIMEFLAGS)
00458                             m_flags |= F_DIRTY;
00459                      return 1;
00460               }
00461        }
00462        return 0;
00463 }
00464 
00465 
00466 PRInt32
00467 nsMsgGroupRecord::GetNumKids()
00468 {
00469        PRInt32 result = 0;
00470        nsMsgGroupRecord* child;
00471        for (child = m_children ; child ; child = child->m_sibling) {
00472               if (IsIMAPGroupRecord())
00473                      result++;
00474               else
00475                      if (child->m_flags & F_ISGROUP) result++;
00476 
00477               if (!IsIMAPGroupRecord())
00478                      result += child->GetNumKids();
00479        }
00480        return result;
00481 }
00482 
00483 
00484 
00485 
00486 char*
00487 nsMsgGroupRecord::GetSaveString()
00488 {
00489        char* pretty = NULL;
00490        char* result = nsnull;
00491        
00492        if (m_prettyname) {
00493               pretty = nsEscape(m_prettyname, url_XAlphas);
00494               if (!pretty) return NULL;
00495        }
00496        char* fullname = GetFullName();
00497        if (!fullname) return NULL; {
00498               long nAddTime;
00499               LL_L2I(nAddTime, m_addtime);
00500         result = PR_smprintf("%s,%s,%lx,%lx,%lx" MSG_LINEBREAK,
00501                                                     fullname, pretty ? pretty : "",
00502                                                     (long) (m_flags & ~RUNTIMEFLAGS),
00503                                                     nAddTime,
00504                                                     (long) m_uniqueId);
00505        }
00506        delete [] fullname;
00507        if (pretty) nsCRT::free(pretty);
00508        m_flags &= ~F_DIRTY;
00509        return result;
00510 }
00511 
00512 
00513 PRBool
00514 nsMsgGroupRecord::IsDirty()
00515 {
00516        return (m_flags & F_DIRTY) != 0;
00517 }
00518 
00519 
00520 PRInt32
00521 nsMsgGroupRecord::GetDepth()
00522 {
00523        PRInt32 result = 0;
00524        nsMsgGroupRecord* tmp = m_parent;
00525        while (tmp) {
00526               tmp = tmp->m_parent;
00527               result++;
00528        }
00529        return result;
00530 }
00531 
00532 
00533 
00534 
00535 nsMsgGroupRecord*
00536 nsMsgGroupRecord::Create(nsMsgGroupRecord* parent, const char* saveline,
00537                                           PRInt32 savelinelength, PRInt32 fileoffset)
00538 {
00539        char* tmp;
00540        char* ptr;
00541        char* endptr;
00542        char* partname;
00543        char* prettyname;
00544        PRInt32 flags;
00545        PRInt32 addtime;
00546        PRInt32 uniqueid;
00547        nsMsgGroupRecord* result = NULL;
00548 
00549        if (savelinelength < 0) savelinelength = PL_strlen(saveline);
00550        tmp = (char*) PR_Malloc(savelinelength + 1);
00551        if (!tmp) return NULL;
00552        PL_strncpy(tmp, saveline, savelinelength);
00553        tmp[savelinelength] = '\0';
00554        ptr = PL_strchr(tmp, ',');
00555        PR_ASSERT(ptr);
00556        if (!ptr) goto FAIL;
00557        *ptr++ = '\0';
00558        partname = PL_strrchr(tmp, '.');
00559        if (!partname) partname = tmp;
00560        else partname++;
00561 
00562 #ifdef DEBUG_slowAndParanoid
00563        if (parent->m_partname) {
00564               char* parentname = parent->GetFullName();
00565               PR_ASSERT(partname > tmp && partname[-1] == '.');
00566               partname[-1] = '\0';
00567               PR_ASSERT(PL_strcmp(parentname, tmp) == 0);
00568               partname[-1] = '.';
00569               delete [] parentname;
00570               parentname = NULL;
00571        } else {
00572               PR_ASSERT(partname == tmp);
00573        }
00574 #endif
00575 
00576        endptr = PL_strchr(ptr, ',');
00577        PR_ASSERT(endptr);
00578        if (!endptr) goto FAIL;
00579        *endptr++ = '\0';
00580        prettyname = nsUnescape(ptr);
00581 
00582        ptr = endptr;
00583        endptr = PL_strchr(ptr, ',');
00584        PR_ASSERT(endptr);
00585        if (!endptr) goto FAIL;
00586        *endptr++ = '\0';
00587        flags = strtol(ptr, NULL, 16);
00588 
00589        ptr = endptr;
00590        endptr = PL_strchr(ptr, ',');
00591        PR_ASSERT(endptr);
00592        if (!endptr) goto FAIL;
00593        *endptr++ = '\0';
00594        addtime = strtol(ptr, NULL, 16);
00595 
00596        ptr = endptr;
00597        uniqueid = strtol(ptr, NULL, 16);
00598 
00599        PRInt64 llAddtime;
00600        LL_I2L(llAddtime, addtime);
00601        result = Create(parent, partname, llAddtime, uniqueid, fileoffset);
00602        if (result) {
00603               PRBool maybeCategoryContainer = flags & F_CATCONT;
00604               flags &= ~F_CATCONT;
00605               result->m_flags = flags;
00606               if (maybeCategoryContainer)
00607                      result->SetIsCategoryContainer(PR_TRUE);
00608               if (prettyname && *prettyname) result->SetPrettyName(prettyname);
00609        }
00610 
00611 FAIL:
00612        PR_Free(tmp);
00613        return result;
00614        
00615 }
00616 
00617 
00618