/* Copyright (c) 2017, Computer History Museum All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted (subject to the limitations in the disclaimer below) provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Computer History Museum nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "nickmng.h" #define FILE_NUM 2 /* Copyright (c) 1990-1992 by the University of Illinois Board of Trustees */ /************************************************************************ * routines to manage the nickname list ************************************************************************/ #pragma segment NickMng /************************************************************************ * 7/17/96 ALB * Performance update for very large nickname files. * - Each nickname is no longer stored in a separate handle. They * are all stored in one handle with an offset to each one * - Addresses, notes, and view data are no longer kept in a separate handle ************************************************************************/ /************************************************************************ * The aliases list has the following structure: * name-of-alias<2 length bytes>expansion-of-alias... * The aliases file contains lines of the form: * "alias" name-of-alias expansion of alias * Newlines may be escaped with "\". * Lines not beginning with "alias" will be ignored ************************************************************************/ /************************************************************************ * private functions ************************************************************************/ typedef struct { Str31 name; long offset; } AliasSortType, *AliasSortPtr, **AliasSortHandle; typedef struct { long hashName; long hashAddress; // long createDate; (eventually) // long modDate; (eventually) // long usageDate; (eventually) long cacheDate; long addressOffset; long notesOffset; NicknameFlagsType flags; // New for 5.0 } NickTOCStruct; typedef struct { long offset; short nickIndex; } NickOffSetSortType; Boolean NeatenLine(UPtr line, long *len); void CheckForNicknameBogosity (short which); Boolean SaveIndNickFile(short which, Boolean saveChangeBits); Boolean SaveFileFast (short which, Boolean saveChangeBits); int NickOffsetCompare(NickOffSetSortType *n1, NickOffSetSortType *n2); void NickOffsetSwap(NickOffSetSortType *n1, NickOffSetSortType *n2); PStr StripQualifiersAndHonorifics (PStr name, PStr strippedName); OSErr AddHandleToAddressHashes(TextAddrHandle sourceHandle,AccuPtr a); int SortAddrNameCompare(UPtr *s1, UPtr *s2); #define This (*Aliases)[which] short TotalNumOfNicks; OSErr ReadNicknames(short which); Boolean SplitNicknames (short ab); OSErr KillNickTOC(FSSpecPtr spec); OSErr ReadNickTOC(short which); OSErr WriteNickTOC(short which); Boolean NickFileOkForFastSave(short which); void SaveDirtyPictures (short ab); long NickMatchFoundLo(NickStructHandle theNicknames,long theNicknamesLen,long hashName,PStr theName,short which); /************************************************************************ * ReadNicknames - read the list of aliases * Can be called either to read the nicknames initially or simply to make * sure they're all in memory ************************************************************************/ OSErr ReadNicknames(short which) { BinAddrHandle shortAddress; FSSpec spec; OSErr err = noErr; Str255 line; short type; Boolean exLine=False; long expOffset=0; long len; Str31 aliasCmd, noteCmd; Str31 currentCmd,tempName; LineIOD lid; long i,count; long currNickIndex; long hashName; Boolean doingAlias,doingNote; OSErr theMemErr = noErr; long nameOffset = 0; Accumulator nameAcc,dataAcc,lineAcc; char lookingFor; long firstOffset; long lineOffset; long curOffset; Boolean firstRead = This.hNames==nil; // are we reading for the first time? (or filling in) Boolean lastExLine; /* * Setup section */ Zero(nameAcc); Zero(dataAcc); Zero(lineAcc); // put existing handles into accumulators nameAcc.data = This.hNames; if (nameAcc.data) nameAcc.size = nameAcc.offset = GetHandleSize(nameAcc.data); dataAcc.data = (void*)This.theData; if (dataAcc.data) dataAcc.size = dataAcc.offset = GetHandleSize(dataAcc.data); //Find the file, open it for reading, get the command names spec = This.spec; This.collapsed = FindSTRNIndex(NickFileCollapseStrn,spec.name) > 0; GetRString(aliasCmd,ALIAS_CMD); GetRString(noteCmd,NOTE_CMD); if (err=FSpOpenLine(&spec,fsRdPerm,&lid)) return(err==fnfErr ? noErr : FileSystemError(OPEN_ALIAS,spec.name,err)); // accumulator to hold the line we're working on if (theMemErr = AccuInit(&lineAcc)) goto hitMemError; /* * the main reading loop; read lines until no more */ curOffset = firstOffset = lastExLine = 0; while ((type=GetLine(line,sizeof(line),&len,&lid))>0) { lastExLine = exLine; lineOffset = curOffset; // offset of beginning of line curOffset += len; // this is the offset of the next line exLine = line[len-1]!='\015'; // Record the offset where we first start collecting data if (lineAcc.offset==0) firstOffset = lineOffset; // if the last line was escaped or we're looking at a partial line, just accumulate if (lastExLine || exLine) // If the line was escaped or we're in the middle of a line { // first, fix up the line exLine = NeatenLine(line,&len) || exLine; // if the previous line was escaped and this line doesn't begin // with a space, add one now if (lastExLine && type!=LINE_MIDDLE && !issep(*line)) if (theMemErr=AccuAddChar(&lineAcc,' ')) goto hitMemError; // if this line is further extended, append it now // if it's not, it will get appended inside the next if if (exLine && (theMemErr=AccuAddPtr(&lineAcc,line,len))) goto hitMemError; } if (!exLine) { // Previous line was not escaped. Is this one? exLine = NeatenLine(line,&len) || exLine; // and append the current line to the line accumulator if (theMemErr=AccuAddPtr(&lineAcc,line,len)) goto hitMemError; // if the line was escaped, just go round again; we want to deal with // full lines only if (exLine) continue; // ok, now we know that we have a full line // if not empty, do parsing and add nickname here if (lineAcc.offset) { // first string of non-space characters is the command name for (i=0;i sizeof(Str31) - 1) return (-1); stop = (theNicknamesLen/sizeof(NickStruct)); endStruct = (*theNicknames)+stop; for (theStruct=*theNicknames; theStructhashName == hashName && !theStruct->deleted) if (matched==-1) matched = theStruct-*theNicknames; else {needStringMatch = True; break;} if (!needStringMatch) return(matched); PSCopy(tempName,theName); *tempName = RemoveChar(' ',tempName+1,*tempName); for (i=0;ihashAddress == hashAddress && !theStruct->deleted) if (matched == -1) matched = theStruct - *theNicknames; else { needStringMatch = true; break; } if (!needStringMatch) return (matched); PSCopy(tempAddress, theAddress); *tempAddress = RemoveChar (' ', tempAddress + 1, *tempAddress); for (i = 0; i < stop; i++) if ((*theNicknames)[i].hashAddress == hashAddress && !(*theNicknames)[i].deleted) if (theAddresses = GetNicknameData(which,i,true,true)) { SuckPtrAddresses (&addresses, LDRef (theAddresses), GetHandleSize (theAddresses), False, False, False, nil); UL (theAddresses); if (addresses) { PCopy (tempStr, *addresses); *tempStr = RemoveChar (' ', tempStr + 1, *tempStr); ZapHandle (addresses); if (StringSame (tempAddress, tempStr)) return (i); } } return (-1); } /************************************************************************ * RegenerateAliases - make sure the alias list is in memory ************************************************************************/ OSErr RegenerateAliases(short which,Boolean rebuild) { OSErr err = noErr; UHandle hand; if (rebuild) ZapAliasFile(which); if (!This.theData) // If handle for data doesn't exist, create it { hand = NuHTempOK(0L); if (!hand) err = WarnUser(ALLO_ALIAS,MemError()); This.theData = (NickStructHandle)hand; HNoPurge_(This.theData); } else // Handle exists { if (!rebuild) return (noErr); } if (!err) { // Check to see if the TOC exists in the resource. If so, read it into the structure. // If not, read in the nicknames and then write out the TOC if (!rebuild) err = ReadNickTOC(which); if (err != noErr || rebuild) { ZapHandle(This.theData); hand = NuHTempOK(0L); if (!hand) err = WarnUser(ALLO_ALIAS,MemError()); This.theData = (NickStructHandle)hand; HNoPurge_(This.theData); err = ReadNicknames(which); if (!err && !This.ro) { WriteNickTOC(which); #ifdef VCARD if (!IsPluginAddressBook (which) && !IsPersonalAddressBook (which) && SplitNicknames (which)) #else if (!IsPluginAddressBook (which) && SplitNicknames (which)) #endif { SetAliasDirty(which); SaveIndNickFile (which, true); } } } } if (err) ZapHandle(This.theData); return(err); } Boolean SplitNicknames (short ab) { Str255 name; Handle notes; short nick, totalNicks; SInt8 oldHState; Boolean notesChanged; notesChanged = false; totalNicks = (*Aliases)[ab].theData ? (GetHandleSize_((*Aliases)[ab].theData) / sizeof (NickStruct)) : 0; for (nick = 0; nick < totalNicks; ++nick) { if (notes = GetNicknameData (ab, nick, false, true)) { oldHState = HGetState (notes); if (MaybeApplySplittingAlgorithm (notes) && !(*Aliases)[ab].ro) { ReplaceNicknameNotes (ab, GetNicknameNamePStr (ab, nick, name), notes); notesChanged = true; } else // If we did not split the name, restore the note's previous purge state HSetState (notes, oldHState); } } return (notesChanged); } // // MaybeApplySplittingAlgorithm // // Apply our name splitting algorithm to the notes if we have a full name, // and we have no first or last name. This grows the notes handle and returns // true when a split has taken place. Likewise, if we have a first and/or // last name, but no full name, employ our joining algorithm to build the // full name. // Boolean MaybeApplySplittingAlgorithm (Handle notes) { Str255 nameTag, firstTag, lastTag, realName, firstName, lastName; UPtr notesPtr, newPtr, attribute, value; Size notesSize; long attributeLength, valueLength; Boolean notesChanged; notesChanged = false; *realName = 0; *firstName = 0; *lastName = 0; GetRString (nameTag, ABReservedTagsStrn + abTagName); GetRString (firstTag, ABReservedTagsStrn + abTagFirst); GetRString (lastTag, ABReservedTagsStrn + abTagLast); // Walk through the 'notes', looking for attribute/value pairs we care about. notesSize = GetHandleSize (notes); notesPtr = LDRef (notes); while ((!*realName || !*firstName || !*lastName) && (newPtr = ParseAttributeValuePair (notesPtr, notesSize - (notesPtr - *notes), &attribute, &attributeLength, &value, &valueLength))) { // Check to see if we hit a tag we care about if (*nameTag == attributeLength && !memcmp (&nameTag[1], attribute, attributeLength)) MakePPtr (realName, value, valueLength); else if (*firstTag == attributeLength && !memcmp (&firstTag[1], attribute, attributeLength)) MakePPtr (firstName, value, valueLength); else if (*lastTag == attributeLength && !memcmp (&lastTag[1], attribute, attributeLength)) MakePPtr (lastName, value, valueLength); notesPtr = newPtr; } UL (notes); // If we have a real name, but neither a first or last name, apply our splitting algorithm if (*realName && !*firstName && !*lastName) { ParseFirstLast (realName, firstName, lastName); // Append the first and last names to the end of our notes if (*firstName) if (!AddAttributeValuePair (notes, firstTag, &firstName[1], *firstName)) notesChanged = true; if (*lastName) if (!AddAttributeValuePair (notes, lastTag, &lastName[1], *lastName)) notesChanged = true; } else // If we have a first and/or last name, but no real name, create one if (!*realName && (*firstName || *lastName)) { // Apply joining algorithm JoinFirstLast (realName, firstName, lastName); if (*realName) if (!AddAttributeValuePair (notes, nameTag, &realName[1], *realName)) notesChanged = true; } return (notesChanged); } // // GetTaggedFieldValue // // Pull a tagged value out of the notes portion of a nickname // Handle GetTaggedFieldValue (short ab, short nick, PStr tag) { return (GetTaggedFieldValueInNotes (GetNicknameData (ab, nick, false, true), tag)); } Handle GetTaggedFieldValueInNotes (Handle notes, PStr tag) { Handle hValue; UPtr attribute, value, notesPtr, newPtr; Size notesSize; long attributeLength, valueLength; Boolean found; hValue = nil; if (notes) { // Walk through the 'notes', looking for attribute/value pairs. We'll set the value of any object // we find notesSize = GetHandleSize (notes); notesPtr = LDRef (notes); found = false; while (!found && (newPtr = ParseAttributeValuePair (notesPtr, notesSize - (notesPtr - *notes), &attribute, &attributeLength, &value, &valueLength))) { // Is this the tag we're looking for? if (*tag == attributeLength && !memcmp (&tag[1], attribute, attributeLength)) found = true; else notesPtr = newPtr; } if (found) { PtrToHand (value, &hValue, valueLength); Tr(hValue,"\002",">"); } UL (notes); } return (hValue); } PStr GetTaggedFieldValueStr (short ab, short nick, PStr tag, PStr value) { return (GetTaggedFieldValueStrInNotes (GetNicknameData (ab, nick, false, true), tag, value)); } PStr GetTaggedFieldValueStrInNotes (Handle notes, PStr tag, PStr value) { Str255 attribute; UPtr notesPtr, newPtr; Size notesSize; Boolean found; *value = 0; if (notes) { // Walk through the 'notes', looking for attribute/value pairs. notesSize = GetHandleSize (notes); notesPtr = LDRef (notes); found = false; while (!found && (newPtr = ParseAttributeValuePairStr (notesPtr, notesSize - (notesPtr - *notes), attribute, value))) { // Is this the tag we're looking for? if (StringSame (tag, attribute)) found = true; else notesPtr = newPtr; } if (!found) *value = 0; else TrLo(value+1,*value,"\002",">"); UL (notes); } return (value); } OSErr SetTaggedFieldValue (short ab, short nick, PStr tag, PStr value, NickFieldSetValueType setValue, short separatorIndex, Boolean *ignored) { Handle notes; OSErr theError; theError = noErr; if (notes = GetNicknameData (ab, nick, false, true)) theError = SetTaggedFieldValueInNotes (notes, tag, &value[1], *value, setValue, separatorIndex, ignored); return (theError); } OSErr SetTaggedFieldValueInNotes (Handle notes, PStr tag, Ptr value, long length, NickFieldSetValueType setValue, short separatorIndex, Boolean *ignored) { Str255 concatString; UPtr attribute, originalValue, notesPtr, newPtr; OSErr theError; Size notesSize; long attributeLength, originalValueLength; Boolean found; TrLo(value,length,">","\002"); theError = noErr; if (notes) { // Walk through the 'notes', looking for attribute/value pairs. notesSize = GetHandleSize (notes); notesPtr = LDRef (notes); found = false; while (!found && (newPtr = ParseAttributeValuePair (notesPtr, notesSize - (notesPtr - *notes), &attribute, &attributeLength, &originalValue, &originalValueLength))) { // Is this the tag we're looking for? if ((*tag == attributeLength) && !memcmp (&tag[1], attribute, attributeLength)) found = true; else notesPtr = newPtr; } UL (notes); if (found) { switch (setValue) { case nickFieldReplaceExisting: Munger (notes, originalValue - *notes, nil, originalValueLength, value, length); break; case nickFieldAppendExisting: GetRString (concatString, separatorIndex); if (*concatString) { Munger (notes, originalValue - *notes + originalValueLength, nil, 0, &concatString[1], *concatString); originalValueLength += *concatString; } Munger (notes, originalValue - *notes + originalValueLength, nil, 0, value, length); break; case nickFieldIgnoreExisting: if (ignored) *ignored = true; } theError = MemError (); } else theError = AddAttributeValuePair (notes, tag, value, length); } TrLo(value,length,"\002",">"); return (theError); } OSErr SetNicknameChangeBit (Handle notes, ChangeBitType changeBits, Boolean clearFirst) { Str255 changeTag, value; OSErr theError; long num; theError = noErr; if (notes) { GetRString (changeTag, ABHiddenTagsStrn + abTagChangeBits); num = clearFirst ? 0 : GetNicknameChangeBits (notes); num |= (long) changeBits; NumToString (num, value); theError = SetTaggedFieldValueInNotes (notes, changeTag, &value[1], *value, nickFieldReplaceExisting, 0, nil); } return (theError); } long GetNicknameChangeBits (Handle notes) { Str255 changeTag, value; long num; num = 0; if (notes) { GetRString (changeTag, ABHiddenTagsStrn + abTagChangeBits); // Retrieve the current value GetTaggedFieldValueStrInNotes (notes, changeTag, value); // Convert it to a number StringToNum (value, &num); } return (num); } // // FindTaggedFieldValue // // Find a particular tagged field in a nickname, returning offsets to the attribute and value. // Boolean FindTaggedFieldValueOffsets (short ab, short nick, PStr tag, long *attributeOffset, long *attributeLength, long *valueOffset, long *valueLength) { Handle notes; UPtr notesPtr, newPtr, attribute, value; Size notesSize; Boolean found; found = false; if (notes = GetNicknameData (ab, nick, false, true)) { // Walk through the 'notes', looking for attribute/value pairs. notesSize = GetHandleSize (notes); notesPtr = LDRef (notes); while (!found && (newPtr = ParseAttributeValuePair (notesPtr, notesSize - (notesPtr - *notes), &attribute, attributeLength, &value, valueLength))) // Is this the tag we're looking for? if ((*tag == *attributeLength) && !memcmp (&tag[1], attribute, *attributeLength)) { *attributeOffset = attribute - *notes; *valueOffset = value - *notes; found = true; } else notesPtr = newPtr; UL (notes); } return (found); } /************************************************************************ * RegnerateAllAliases - regnerate aliases from all files ************************************************************************/ OSErr RegenerateAllAliases(Boolean rebuild) { short which = NAliases; short err = 0; Str255 scratch,name; short i,n; while (which--) if (err = RegenerateAliases(which,rebuild)) { if (which) { if (err != -108) { PCopy(name,This.spec.name); ComposeRString(scratch,NICK_FILE_GONE,name); AlertStr(OK_ALRT,Caution,scratch); } n = NAliases; for (i=which;i sizeof(Str31) - 1) return (-1); return (NickHashString (newName)); } // Redundancy ensues... (clean all this up later) long NickHashString (PStr string) { Str255 tempStr; PCopy (tempStr, string); *tempStr = RemoveChar (' ', tempStr + 1, *tempStr); *tempStr = RemoveChar (optSpace, tempStr + 1, *tempStr); MyLowercaseText (tempStr + 1, *tempStr); return (Hash (tempStr)); } long NickHashHandle (Handle h) { Str255 tempStr; Size len; *tempStr = 0; if (h) { len = GetHandleSize (h); tempStr[0] = MIN (len, (sizeof (tempStr) - 1)); BlockMoveData (*h, &tempStr[1], tempStr[0]); *tempStr = RemoveChar (' ', tempStr + 1, *tempStr); MyLowercaseText (tempStr + 1, *tempStr); } return (Hash (tempStr)); } long NickHashRawAddresses (Handle addresses, Boolean *group) { BinAddrHandle shortAddress; long hashValue; hashValue = -1; if (addresses) { // Hash the addresses in semi-complicated fashion shortAddress = nil; // As a last check... see if the address is already present in a nickname file if (!SuckPtrAddresses (&shortAddress, LDRef (addresses), GetHandleSize (addresses), false, false, false, nil)) hashValue = NickHashString (LDRef (shortAddress)); UL (addresses); *group = ContainsMultipleAddresses (shortAddress); ZapHandle (shortAddress); } return (hashValue); } /************************************************************************ * NickGenerateUniqueID - generate a unique ID for a nickname * * The ID will put a counter in the upper 16 bits, and Random * in the lower 16. The counter is, effectively, unique until * the user resets the 'next id' preference in settings. But * even with the sequence starting over again, each is matched * with a random number, so we're in pretty good shape. ************************************************************************/ long NickGenerateUniqueID(void) { long id; id = GetPrefLong (PREF_NEXT_NICK_UNIQUE_ID); SetPrefLong (PREF_NEXT_NICK_UNIQUE_ID, id + 1); id <<= 16; id |= ((long) Random () & 0x0000FFFF); return (id); } OSErr PrepAllAddressBooksForSync (void) { OSErr theError; short totalABs, ab; theError = noErr; totalABs = NAliases; for (ab = 0; !theError && ab < totalABs; ++ab) theError = PrepAddressBookForSync (ab); return (theError); } OSErr PrepAddressBookForSync (short ab) { Str255 idTag, changeBitsTag; OSErr theError; short totalNicks, nick; theError = noErr; GetRString (idTag, ABHiddenTagsStrn + abTagUniqueID); GetRString (changeBitsTag, ABHiddenTagsStrn + abTagChangeBits); totalNicks = (*Aliases)[ab].theData ? (GetHandleSize_ ((*Aliases)[ab].theData) / sizeof (NickStruct)) : 0; for (nick = 0; !theError && nick < totalNicks; ++nick) theError = PrepNicknameForSync (ab, nick, idTag, changeBitsTag); return (theError); } OSErr PrepNicknameForSync (short ab, short nick, Str255 idTag, Str255 changeBitsTag) { Handle notes; Str255 idString, scratch, name; OSErr theError; Boolean notesExist, replaceNotes; theError = noErr; notes = GetNicknameData (ab, nick, false, true); if (!(notesExist = notes ? true : false)) notes = NuHandle (0); if (notes) { replaceNotes = false; GetTaggedFieldValueStrInNotes (notes, idTag, scratch); if (!*scratch) { replaceNotes = true; NumToString (NickGenerateUniqueID (), idString); theError = SetTaggedFieldValueInNotes (notes, idTag, &idString[1], *idString, nickFieldReplaceExisting, 0, nil); } GetTaggedFieldValueStrInNotes (notes, changeBitsTag, scratch); if (!theError && !*scratch) { replaceNotes = true; theError = SetNicknameChangeBit (notes, changeBitAdded, false); } if (!theError && replaceNotes) ReplaceNicknameNotes (ab, GetNicknameNamePStr (ab, nick, name), notes); } if (!notesExist) ZapHandle (notes); return (theError); } // // ClearAllAddressBookChangeBits // // Clears change bits as specified by the mask. // OSErr ClearAllAddressBookChangeBits (long mask) { OSErr theError; short totalABs, ab; theError = noErr; totalABs = NAliases; for (ab = 0; !theError && ab < totalABs; ++ab) theError = ClearAddressBookChangeBits (ab, mask); return (theError); } OSErr ClearAddressBookChangeBits (short ab, long mask) { OSErr theError; short totalNicks, nick; theError = noErr; totalNicks = (*Aliases)[ab].theData ? (GetHandleSize_ ((*Aliases)[ab].theData) / sizeof (NickStruct)) : 0; for (nick = 0; !theError && nick < totalNicks; ++nick) theError = ClearNicknameChangeBits (ab, nick, mask); return (theError); } OSErr ClearNicknameChangeBits (short ab, short nick, long mask) { Handle notes; Str255 name; OSErr theError = noErr; long value; theError = noErr; if (notes = GetNicknameData (ab, nick, false, true)) { value = GetNicknameChangeBits (notes); if (value != (value & ~mask)) { value &= ~mask; theError = SetNicknameChangeBit (notes, value, true); if (!theError) ReplaceNicknameNotes (ab, GetNicknameNamePStr (ab, nick, name), notes); } } return (theError); } /********************************************************************** * KillNickTOC - kill the toc for a file **********************************************************************/ OSErr KillNickTOC(FSSpecPtr spec) { OSErr err = noErr; /* * if the resource fork is bad or we can't open it, just remove it */ err = FSpKillRFork(spec); return (err); } /********************************************************************** * ReadNickTOC - read the toc for a file **********************************************************************/ OSErr ReadNickTOC(short which) { uLong fileModDate = AFSpGetMod(&This.spec); uLong TOCModDate; FSSpec lSpec = This.spec; Boolean sane; OSErr err = noErr; Handle structR = nil,nameR = nil,r1 = nil; long structSize; short numberOfNicks = 0; long currentStructPos = 0,currentNamePos = 0,currNickCount; short refN=0; long theSize = 0; long eof; NickStructHandle theData; NickTOCStruct *pTOCInfo,*pTOCEnd; NickStruct *pNick; short oldResF = CurResFile(); short oldId; theData = (*Aliases)[which].theData; IsAlias(&This.spec,&lSpec); This.collapsed = FindSTRNIndex(NickFileCollapseStrn,This.spec.name) > 0; if (err=FSpRFSane(&lSpec,&sane)) return(-1); if (!sane) { FSpKillRFork(&lSpec); return (-1); } else if (-1!=(refN=FSpOpenResFile(&lSpec,fsRdPerm))) { Handle hOldTOC; SetResLoad(False); // Don't load now so we can use temporary memory if (hOldTOC = Get1Resource(OLD_NICK_TYPE,OLD_NICK_RESID1)) { // Remove old-style TOC resources RemoveResource(hOldTOC); if (hOldTOC = Get1Resource(OLD_NICK_TYPE,OLD_NICK_RESID2)) RemoveResource(hOldTOC); } for (oldId=NICK_BASE_RESID;oldIdhashName = pTOCInfo->hashName; pNick->hashAddress = pTOCInfo->hashAddress; pNick->group = pTOCInfo->flags & nfMultipleAddresses ? true : false; if ((pNick->addressOffset = pTOCInfo->addressOffset) > eof) goto theExit; // Bad data if ((pNick->notesOffset = pTOCInfo->notesOffset) > eof) goto theExit; // Bad data pNick->nameTOCOffset = currentNamePos; // Advance to next name currentNamePos += *(*nameR + currentNamePos) + 1; } } } CheckForNicknameBogosity (which); } else goto theExit; } if (numberOfNicks == 0) theExit: numberOfNicks = 0; // Error if (refN) CloseResFile(refN); if (nameR) UL(nameR); if (err) ZapHandle(This.hNames); ZapHandle(structR); UseResFile (oldResF); if (numberOfNicks == 0) return (-1); return(noErr); } /********************************************************************** * WriteNickTOC - write the toc for a file **********************************************************************/ OSErr WriteNickTOC(short which) { uLong fileModDate; FSSpec spec = This.spec; OSErr err = noErr; short refN; NickTOCStruct tempStruct; short i,totalNicks = NNicknames,realCount = 0; Handle structR = nil,nameR = nil,tempHandle = nil; Accumulator structAcc,nameAcc; NickStruct *pNickInfo; NickStructHandle theData; Boolean fSaved = false; short oldResF = CurResFile(); theData = (*Aliases)[which].theData; IsAlias(&spec,&spec); KillNickTOC(&spec); FSpCreateResFile(&spec,CREATOR,'TEXT',smSystemScript); fileModDate = AFSpGetMod(&spec); structR = NuHTempOK(0); nameR = NuHTempOK(0); Zero(structAcc); Zero(nameAcc); structAcc.data = (Handle)structR; nameAcc.data = (Handle)nameR; if (!structR || !nameR) //SD and drop whichever one might not be nil { UseResFile (oldResF); return (-1); } realCount = 0; for (i=0,pNickInfo=(*theData);ideleted) realCount++; } err = AccuAddPtr(&structAcc,(void*)&fileModDate,sizeof(fileModDate)); if (err) goto exit; err = AccuAddPtr(&structAcc,(void*)&realCount,sizeof(realCount)); if (err) goto exit; // Build nick TOC struct for (i=0;ideleted) { Str31 sName; // Add nick info tempStruct.hashName = pNickInfo->hashName; tempStruct.hashAddress = pNickInfo->hashAddress; tempStruct.addressOffset = pNickInfo->addressOffset; if (pNickInfo->group) tempStruct.flags |= nfMultipleAddresses; else tempStruct.flags &= ~nfMultipleAddresses; tempStruct.notesOffset = pNickInfo->notesOffset; if (err=AccuAddPtr(&structAcc,(void*)&tempStruct,sizeof(tempStruct))) goto exit; // Add nick name GetNicknameNamePStr(which,i,sName); if (err=AccuAddPtr(&nameAcc,sName,*sName+1)) goto exit; } } AccuTrim((void*)&nameAcc); AccuTrim((void*)&structAcc); if (-1!=(refN=FSpOpenResFile(&spec,fsRdWrPerm))) { AddResource(structR,NICK_TOC_TYPE,NICK_RESID,""); if (!ResError()) AddResource(nameR,NICK_NAMES_TYPE,NICK_RESID,""); err = ResError(); if (!err) { UpdateResFile(refN); err = ResError(); fSaved = true; } // ALB DetachResource(structR); // ALB DetachResource(nameR); CloseResFile(refN); } else err = ResError(); if (!err) err = AFSpSetMod(&spec,fileModDate); exit: if (!fSaved) { // Failed to save, get rid of the temporary handles ZapHandle(structR); ZapHandle(nameR); } UseResFile (oldResF); return (err); } /************************************************************************ * NeatenLine - strip the newline from a line; if it was escaped, strip * the backslash, and return True ************************************************************************/ Boolean NeatenLine(UPtr line, long *len) { if (line[*len-1]=='\015') line[--*len] = 0; if (line[*len-1]=='\\') { line[--*len] = 0; return(True); } return(False); } /************************************************************************ * AliasExpansion - return pointer to expansion of an alias ************************************************************************/ UPtr AliasExpansion(UPtr data, long offset) { UPtr ptr = data+offset; ptr += *ptr+3; return(ptr); } /************************************************************************ * AddNickToTOC - add nickname to TOC * * Note!! This routine makes a copy of the data handle (notes or addresses) * which is assigned to the TOC. Therefore, there is no need to hang onto * the handle passed in hData. ************************************************************************/ static short AddNickToTOC(short which,UPtr name,Handle hData,Boolean fFromAddress) { long currNickCount; NickStructHandle aliases = This.theData; OSErr err = noErr; NickStruct nickInfo; Str255 beautifulName; long nameOffset; Boolean group; // Nickname doesn't exist...create new one and clear it out // If it doesn't exist, add it HUnlock((Handle)aliases); currNickCount = NNicknames; // Get nickname count if (currNickCount > 0) { SetHandleBig_((Handle)aliases,(currNickCount + 1)*sizeof(NickStruct)); // Add a nickname if (err = MemError()) return(WarnUser(ALIAS_NEW_NICK_ERR,err)); } else { ZapHandle(This.theData); // (jp) 7/31/99 Operate on This.theData instead of aliases aliases = NuHTempOK(sizeof(NickStruct)); if (!aliases) return(WarnUser(ALIAS_NEW_NICK_ERR,MemError())); else This.theData = aliases; // (jp) 7/31/99 The newly created data handle } nameOffset = GetHandleSize(This.hNames); err = PtrPlusHand_(name,This.hNames,*name+1); if (err) return(WarnUser(ALIAS_NEW_NICK_ERR,err)); // Fill in the basic data WriteZero(&nickInfo,sizeof(nickInfo)); nickInfo.hashName = NickHash(name); nickInfo.hashAddress = 0; nickInfo.addressOffset = (-1L); nickInfo.notesOffset = (-1L); nickInfo.addressesDirty = true; nickInfo.notesDirty = true; nickInfo.pornography = false; nickInfo.group = false; nickInfo.nameTOCOffset = nameOffset; if (!This.containsBogusNicks) { PCopy (beautifulName, name); BeautifyFrom (beautifulName); if (nickInfo.hashName != NickHash (beautifulName)) This.containsBogusNicks = true; } if (hData) // Put the address information into the nickname { Handle hNewData; hNewData = NuHTempOK(0); if (!hNewData) return(WarnUser(ALIAS_NEW_NICK_ERR,MemError())); err = PtrPlusHand(*hData,hNewData,GetHandleSize_(hData)); if (err) return(WarnUser(ALIAS_NEW_NICK_ERR,err)); if (fFromAddress) { // Data is addresses nickInfo.theAddresses = hNewData; nickInfo.hashAddress = NickHashRawAddresses (hNewData, &group); nickInfo.group = group; } else // Data is notes nickInfo.theNotes = hNewData; } // Put nick info in nick array (*(aliases))[currNickCount] = nickInfo; // This data CANNOT be purged because it hasn't been written to disk HNoPurge((Handle)aliases); UL(aliases); return(0); } /************************************************************************ * AddNickToTOCfromName - add nickname to TOC ************************************************************************/ short AddNickToTOCfromName(short which,UPtr name,Handle addresses) { return AddNickToTOC(which,name,addresses,true); } /************************************************************************ * AddNickToTOCfromNotes - add nickname to TOC from notes ************************************************************************/ short AddNickToTOCfromNotes(short which,UPtr name,Handle notes) { // return AddNickToTOC(which,name,notes,true); // (jp) This is wrong!! We're setting notes, not addresses return AddNickToTOC(which,name,notes,false); } /************************************************************************ * RemoveNamedNickname - remove the named nickname * The nickname CANNOT be completely removed from memory in case the user * discards the changes from the window. ************************************************************************/ void RemoveNamedNickname(short which,UPtr name) { NickStructHandle aliases = This.theData; long hashName = NickHash(name); long index = NickMatchFound(aliases,hashName,name,which); // returns index of match if (index >= 0 ) // the nickname exists { (*((*Aliases)[which].theData))[index].addressesDirty = true; (*((*Aliases)[which].theData))[index].notesDirty = true; (*((*Aliases)[which].theData))[index].deleted = true; } } /************************************************************************ * NickUniq - uniquify a nicknames list ************************************************************************/ OSErr NickUniq(TextAddrHandle addresses,PStr sep,Boolean wantErrors) { TextAddrHandle cmntOrig=nil, unOrig=nil, cmntNew=nil, unNew=nil; short err = noErr; UPtr spot; UPtr newSpot; UPtr end; long size; Boolean group, groupWas=False; err = SuckAddresses(&cmntOrig,addresses,True,wantErrors,False,nil); cmntNew = NuHTempOK(0); unNew = NuHTempOK(0); if (!err && cmntNew && unNew && cmntOrig) { for (spot = LDRef(cmntOrig);*spot && !err;spot+=*spot+2) { err = SuckPtrAddresses(&unOrig,spot+1,*spot,False,wantErrors,False,nil); if (!err && unOrig && **unOrig) { LDRef(unOrig); group = (*unOrig)[**unOrig]==':'; for (newSpot=LDRef(unNew),end=newSpot+GetHandleSize_(unNew); newSpot=end || !*newSpot) { /* did NOT find it */ if (!groupWas && GetHandleSize_(cmntNew)) PtrPlusHand_(sep+1,cmntNew,*sep); err = PtrPlusHand_(spot+1,cmntNew,*spot); if (err) break; err = PtrPlusHand_(*unOrig,unNew,**unOrig+2); if (err) break; } ZapHandle(unOrig); groupWas = group; } } } if (!err) { size = GetHandleSize_(cmntNew); HUnlock(addresses); SetHandleBig_(addresses,size); if (!(err=MemError())) BlockMoveData(*cmntNew,*addresses,size); } if (err<0 && wantErrors) WarnUser(MEM_ERR,err); ZapHandle(cmntOrig); ZapHandle(unOrig); ZapHandle(cmntNew); ZapHandle(unNew); return(err); } /************************************************************************ * ReplaceNicknameInfo - replace nickname address or notes * * Note!! This routine makes a copy of the data handle (notes or addresses) * which is assigned to the TOC. Therefore, there is no need to hang onto * the handle passed in hData. ************************************************************************/ static short ReplaceNicknameInfo(short which,UPtr theName,TextAddrHandle text,Boolean fAddresses) { long hashName = NickHash(theName); long index; NickStructHandle aliases = This.theData; OSErr err = noErr; NickStruct tempNick; long textSize; Handle hTemp; Boolean group; if (theName && *theName && aliases) { index = NickMatchFound(aliases,hashName,theName,which); if (index<0) // Not found, add nickname if (fAddresses) return (AddNickToTOCfromName(which,theName,text)); else return (AddNickToTOCfromNotes(which,theName,text)); // Get nick info tempNick = (*aliases)[index]; if (text) { // Make a copy of addresses textSize = GetHandleSize(text); if (hTemp = NuHTempOK(textSize)) BlockMoveData(*text,*hTemp,textSize); else // Memory error return (WarnUser(ALIAS_REPLACE_NICK_ERR,MemError())); } else hTemp = nil; if (fAddresses) { // Replace the addresses if (tempNick.theAddresses) ZapHandle(tempNick.theAddresses); // Dispose of former addresses // Don't let the user put crap in here; bug 4519 if (hTemp) TransLitRes(*hTemp,GetHandleSize(hTemp),ktFlatten); tempNick.theAddresses = hTemp; tempNick.hashAddress = NickHashRawAddresses (tempNick.theAddresses, &group); tempNick.group = group; tempNick.theNotes = GetNicknameData(which,index,false,true); // Make sure notes are loaded } else { // Replace notes if (tempNick.theNotes) ZapHandle(tempNick.theNotes); // Dispose of former notes tempNick.theNotes = hTemp; tempNick.theAddresses = GetNicknameData(which,index,true,true); // Make sure addresses are loaded tempNick.hashAddress = NickHashRawAddresses (tempNick.theAddresses, &group); tempNick.group = group; } if (tempNick.theAddresses) HNoPurge(tempNick.theAddresses); if (tempNick.theNotes) HNoPurge(tempNick.theNotes); UL(text); tempNick.addressesDirty = true; tempNick.notesDirty = true; // Save temp nick info (*aliases)[index] = tempNick; SetAliasDirty(which); } return noErr; } /************************************************************************ * ReplaceNicknameAddresses - replace one nickname definition with another ************************************************************************/ short ReplaceNicknameAddresses(short which,UPtr theName,TextAddrHandle text) { return ReplaceNicknameInfo(which,theName,text,true); } /************************************************************************ * ChangeNameOfNick - change the nickname ************************************************************************/ short ChangeNameOfNick(short which,UPtr oldName,UPtr newName) { NickStructHandle aliases = This.theData; long hashName = NickHash(oldName); long index=-1; if (oldName && *oldName) index = NickMatchFound(aliases,hashName,oldName,which); if (index<0) return(-1); SetNickname (which, index, newName); return(0); } void SetNickname (short ab, short nick, UPtr name) { NickStructHandle aliases; NickStruct *pNick; Str32 oldName; long oldOffset, adjustment, nickCount, i; GetNicknameNamePStr (ab, nick, oldName); if (aliases = (*Aliases)[ab].theData) { oldOffset = (*aliases)[nick].nameTOCOffset; // Replace the name Munger ((*Aliases)[ab].hNames, oldOffset, nil, *oldName + 1, name, *name + 1); // New hash value (*aliases)[nick].hashName = NickHash (name); // Calculate new name offsets if the new name is a different length // Only need to adjust those that are past the one we changed if (adjustment = *name - *oldName) { nickCount = GetHandleSize_(aliases) / sizeof (NickStruct); for (i = 0, pNick = *aliases; i < nickCount; i++, pNick++) if (pNick->nameTOCOffset > oldOffset) pNick->nameTOCOffset += adjustment; } // Mark the nickname as dirty so that the alias and note entries in the nickname file get rewritten // (9/11/00) We don't really want to do this because the addresses and note might not be in memory, in which // case we'd falsely save them as empty. // (*aliases)[nick].addressesDirty = true; // (*aliases)[nick].notesDirty = true; } } /********************************************************************** * IsAnyNickname - is the name a nickname? **********************************************************************/ Boolean IsAnyNickname(PStr name) { short which; for (which=NAliases;which--;) if (IsNickname(name,which)) return(True); return(False); } /************************************************************************ * ReplaceNicknameNotes - replace nickname notes with another ************************************************************************/ short ReplaceNicknameNotes(short which,UPtr theName,TextAddrHandle text) { return ReplaceNicknameInfo(which,theName,text,false); } /* * Get the addresses or notes of a given nickname; if not in memory, it will read from disk */ Handle GetNicknameData(short which,short index,Boolean wantAddresses,Boolean readFromDisk) { Handle tempHandle; NickStructHandle aliases = This.theData; FSSpec spec; Boolean finished = false; int err; Str255 line; short type; Boolean exLine=False; long len; Handle dataHandle; LineIOD lid; long theOffset,count; Str31 theCmd; Byte lookingFor; Boolean group; spec = This.spec; if (!aliases || index < 0 || which < 0 || index >= NNicknames || (*aliases)[index].deleted) return (nil); if (wantAddresses) tempHandle = (*aliases)[index].theAddresses; else tempHandle = (*aliases)[index].theNotes; if ((*aliases)[index].addressesDirty) { if (tempHandle) HNoPurge(tempHandle); return (tempHandle); } if (tempHandle !=nil && *tempHandle !=nil) return (tempHandle); if (tempHandle != nil) ZapHandle(tempHandle); if (!readFromDisk) return(nil); //SD - Scott, is this ok? if (wantAddresses) { theOffset = (*aliases)[index].addressOffset; GetRString(theCmd,ALIAS_CMD); } else { theOffset = (*aliases)[index].notesOffset; GetRString(theCmd,NOTE_CMD); } if ( theOffset >= 0) { if (err=FSpOpenLine(&spec,fsRdPerm,&lid)) { if (err !=fnfErr) FileSystemError(OPEN_ALIAS,spec.name,err); return (nil); } dataHandle = NuHTempOK(0L); if (!dataHandle) { WarnUser(ALIAS_GET_NICK_DATA_ERR,MemError()); return (nil); } /* * Offset is from the beginning of the line; add in length of the command and length of name * and two spaces */ theOffset += *theCmd + 1; SeekLine(theOffset - 1,&lid); type = GetLine(line,sizeof(line),&len,&lid); if (type==LINE_START) do { // process current line len = strlen(line); exLine = NeatenLine(line,&len); if (exLine && !issep(*line)) // If line was escaped and the first character isn't a space, add one { if (err=PtrPlusHand_(" ",dataHandle,1)) break; } if (err=PtrPlusHand_(line,dataHandle,len)) break; // grab the next line; may or may not be ours type = GetLine(line,sizeof(line),&len,&lid); if (exLine && type==LINE_START) type = LINE_MIDDLE; // extended line means new line is really part of this line } while (type==LINE_MIDDLE); #ifdef NEVER while (!finished && (type=GetLine(line,sizeof(line),&len,&lid))>0) { if (type==LINE_MIDDLE && len < sizeof(line)-1) // We're not really in the middle of a line...we're at the end // so we need to handle the completion of the nickname type = 1; if (exLine || (type==LINE_MIDDLE)) // If the line was escaped or we're in the middle of a line { len = strlen(line); exLine = NeatenLine(line,&len); if (exLine && !issep(*line)) // If line was escaped and the first character isn't a space, add one { if (err=PtrPlusHand_(" ",dataHandle,1)) break; } if (err=PtrPlusHand_(line,dataHandle,len)) break; } else { if (*line) if (err=PtrPlusHand_(line,dataHandle,len)) break; if (len < (sizeof(line) - 1 ) && len > 0) // Got less than a full line of text; therefore we are done { finished = true; break; } } } #endif CloseLine(&lid); if (err) { WarnUser(ALIAS_GET_NICK_DATA_ERR,err); ZapHandle(dataHandle); return (nil); } HLock(dataHandle); len = GetHandleSize_(dataHandle); for (count=0;count= NNicknames || (*aliases)[index].deleted) return (theName); hNames = This.hNames; if (!hNames || !*hNames) return theName; PCopy(theName,*(hNames)+(*aliases)[index].nameTOCOffset); return (theName); } /************************************************************************ * MakeCompNick - make a nickname out of a comp window ************************************************************************/ #ifdef VCARD void MakeCompNick(MyWindowPtr win, FSSpec *vcardSpec) #else void MakeCompNick(MyWindowPtr win) #endif { TextAddrHandle biglist; OSErr err = noErr; biglist = NuHTempOK(0); if (!biglist) { WarnUser(ALIAS_NEW_NICK_ERR,MemError()); return; } if (err=GatherCompAddresses(win,biglist)) { if (err!=paramErr) WarnUser(MEM_ERR,err); return; } /* * and make the nickname... */ if (GetHandleSize (biglist) && **biglist) #ifdef VCARD NewNick(biglist,vcardSpec,0); #else NewNick(biglist,0); #endif else NoteUser(NO_ADDRESSES,0); ZapHandle(biglist); } /********************************************************************** * MakeNickFromSelection - make a nickname from the selected addresses **********************************************************************/ void MakeNickFromSelection(MyWindowPtr win) { Handle list = nil; Handle text; Handle textCopy; long selStart, selEnd; if (PeteGetTextAndSelection(win->pte,&text,&selStart,&selEnd) || selStart>=selEnd) NoteUser(NO_ADDRESSES,0); else { // ALB 9/5/96, replaced this with SuckPtrAddresses for bug 602 // selEnd = MIN(selEnd,selStart+250); // list = ZeroHandle(NuHTempBetter(selEnd-selStart+3)); // if (!list) // { // WarnUser(ALLO_ALIAS,MemError()); // return; // } // **list = len = selEnd-selStart; // BMD(*text+selStart,*list+1,len); // (jp) 12/12/00 Translate carriage returns to commas so we can discern individual addresses textCopy = text; if (!HandToHand (&textCopy)) { Tr (textCopy, "\015", ","); HLock(textCopy); if (!SuckPtrAddresses(&list,*textCopy+selStart,selEnd-selStart,True,True,False,nil)) { if (!list || !*list || !**list) WarnUser(NO_ADDRESSES,0); else #ifdef VCARD NewNick(list,nil,0); #else NewNick(list,0); #endif ZapHandle(list); } HUnlock(textCopy); } ZapHandle (textCopy); } } /************************************************************************ * GatherCompAddresses - gather the addresses from a window ************************************************************************/ short GatherCompAddresses(MyWindowPtr win,Handle biglist) { Handle littlelist; MessHandle messH; static short heads[] = {TO_HEAD,CC_HEAD,BCC_HEAD}; short err=0; short h; Uhandle text=nil; HeadSpec hs; /* * I vaant to suck your addresses... */ messH = Win2MessH(win); for (h=0;!err && htocH; int sumNum = (*messH)->sumNum; FSSpec attSpec; Handle text; long offset; Boolean foundVCard; #endif TOCHandle out=GetOutTOC(); Boolean all, quote, self; #ifdef VCARD // Is a vCard attached to this message? foundVCard = false; if (IsVCardAvailable ()) { CacheMessage (tocH, sumNum); if (!(text = (*tocH)->sums[sumNum].cache)) return; HNoPurge (text); offset = (*tocH)->sums[sumNum].bodyOffset-1; while (!foundVCard && (0<=(offset = FindAnAttachment(text,offset+1,&attSpec,true,nil,nil,nil)))) { foundVCard = IsVCardFile (&attSpec); } } #endif ReplyDefaults(modifiers,&all,&self,"e); if (out) { Boolean wasDirty = (*out)->win->isDirty; win = DoReplyMessage(win,all,self,False,False,0,False,False,False); if (!win) return; #ifdef VCARD MakeCompNick(win, foundVCard ? &attSpec : nil); #else MakeCompNick(win); #endif CloseMyWindow(GetMyWindowWindowPtr(win)); (*out)->win->isDirty = wasDirty; } } /************************************************************************ * MakeMboxNick - make a nickname out of the selected messages in an mbox ************************************************************************/ void MakeMboxNick(MyWindowPtr win,short modifiers) { TextAddrHandle addresses=nil; TOCHandle tocH = (TOCHandle)GetMyWindowPrivateData(win); OSErr err = GatherBoxAddresses(tocH,modifiers,-1,-1,&addresses,false); if (!err) { if (**addresses) #ifdef VCARD NewNick(addresses,nil,0); #else NewNick(addresses,0); #endif else WarnUser(NO_ADDRESSES,0); } ZapHandle(addresses); } /************************************************************************ * GatherBoxAddresses - gather addresses from the selected messages in an mbox ************************************************************************/ OSErr GatherBoxAddresses(TOCHandle tocH,short modifiers,short from, short to, UHandle *addresses, Boolean caching) { MyWindowPtr messWin,compWin; short sumNum; short err = 0; Boolean all, quote, self; Boolean selected = from==-1; if (selected) {from=0;to=(*tocH)->count-1;} ReplyDefaults(modifiers,&all,&self,"e); if (!(*addresses=NuHTempOK(0))) return(MemError()); for (sumNum=from;!err && sumNum<=to;sumNum++) { MiniEvents(); if (CommandPeriod) break; if (!selected || (*tocH)->sums[sumNum].selected) { #ifdef IMAP // if this is an IMAP message, make sure it's been fully fetched. if (tocH && (*tocH)->imapTOC && !EnsureMsgDownloaded(tocH,sumNum,false)) return (err = 1); #endif if (messWin = GetAMessage(tocH,sumNum,nil,nil,False)) { compWin = MessFlagIsSet(Win2MessH(messWin),FLAG_OUT) ? messWin : DoReplyMessage(messWin,all,self,False,False,0,False,False,caching); if (compWin) { err=GatherCompAddresses(compWin,*addresses); if (compWin != messWin) CloseMyWindow(GetMyWindowWindowPtr(compWin)); } if (!IsWindowVisible(GetMyWindowWindowPtr(messWin))) CloseMyWindow(GetMyWindowWindowPtr(messWin)); } else err = 1; } } if (CommandPeriod) err = userCancelled; if (err) ZapHandle(*addresses); return(err); } /************************************************************************ * MakeCboxNick - make a nickname out of the selected messages in Out ************************************************************************/ void MakeCboxNick(MyWindowPtr win) { Handle addresses=NuHTempOK(0); TOCHandle tocH = (TOCHandle)GetMyWindowPrivateData(win); MyWindowPtr compWin; short sumNum; short err = 0; if (!addresses) return; for (sumNum=0;!err && sumNum<(*tocH)->count;sumNum++) { MiniEvents(); if (CommandPeriod) break; if ((*tocH)->sums[sumNum].selected) if (compWin=GetAMessage(tocH,sumNum,nil,nil,False)) { WindowPtr compWinWP = GetMyWindowWindowPtr (compWin); err=GatherCompAddresses(compWin,addresses); if (!IsWindowVisible(compWinWP)) CloseMyWindow(compWinWP); } else err = 1; } if (!err && !CommandPeriod) { if (**addresses) #ifdef VCARD NewNick(addresses,nil,0); #else NewNick(addresses,0); #endif else WarnUser(NO_ADDRESSES,0); } ZapHandle(addresses); } /************************************************************************ * FlattenListWith - make an address list one to a line ************************************************************************/ void FlattenListWith(Handle h,Byte c) { UPtr from, to; Boolean colon; from = to = *h; while (*from) { if (from[1]==';' && to!=*h && to[-1]!=':') to--; //backup over separator while (*++from) *to++ = *from; /* skip length byte, copy string */ colon = from[-1]==':'; from++; /* skip terminator */ if (!colon) *to++ = c; /* and add a separator */ } if (to > *h) to--; SetHandleBig_(h,to-*h); } /************************************************************************ * CommaList - make an address list have commas ************************************************************************/ void CommaList(Handle h) { UPtr from, to; Boolean colon; from = to = *h; while (*from) { if (from[1]==';' && to!=*h && to[-1]!=':') to-=2; //backup over separator while (*++from) *to++ = *from; /* skip length byte, copy string */ colon = from[-1]==':'; from++; /* skip terminator */ if (!colon) { *to++ = ','; /* and add a separator */ *to++ = ' '; /* and add a separator */ } } if (to > *h) to -= 2; SetHandleBig_(h,to-*h); } /************************************************************************ * SaveAliases - save the edited aliases (if necessary) * returns False if the operation failed ************************************************************************/ Boolean SaveAliases(Boolean saveChangeBits) { short ab; Boolean fResult; // Save each address book ab = NAliases; fResult = true; while (ab-- && fResult) fResult = SaveIndNickFile (ab, saveChangeBits); // If the address books were successfully saved, tell the Adddres Book window if (fResult) ABClean (); return (fResult); } /************************************************************************ * SetAliasDirty - set the dirty bit for an address book, and kill the * address hashes ************************************************************************/ void SetAliasDirty(short which) { This.dirty = true; ZapAliasHash(which); } /************************************************************************ * SaveIndNickFile - save an individual alias file ************************************************************************/ Boolean SaveIndNickFile(short which, Boolean saveChangeBits) { Str31 aliasCmd; Str255 scratch; int err; long bytes,offset; short refN=0; long i, count; FSSpec spec,tmpSpec; Boolean junk; Handle tempHandle; short numInMemory; /* * do we need to save it? */ if (!This.dirty) return(True); SaveDirtyPictures (which); /* * notify the nickname completion stuff that the world is changing */ InvalCachedNicknameData(); /* * make a backup */ if (PrefIsSet(PREF_NICK_BACKUP)) NickBackup(&(This.spec)); /* * If fast save allowed, do one. If it returns false, must do complete save */ if (!PrefIsSet(PREF_NO_NICK_FAST_SAVE) && SaveFileFast(which, saveChangeBits)) return(true); /* * find the file */ spec = This.spec; if (err=FSpMyResolve(&spec,&junk)) { FileSystemError(SAVE_ALIAS,spec.name,err); return(False); } /* * regnerate the aliases, if need be */ if (!This.theData || !*This.theData) {WarnUser(SAVE_ALIAS,0);return(False);} count = NNicknames; // Get nickname count numInMemory = 0; // This is going to give us a ROUGH count on the number of nicknames in memory. // It is possible to have the address info in memory, but not the notes...we'll just count that as // being in memory. for (i=0;i=0 && (*(This.theData))[i].theAddresses != nil) numInMemory++; // If we actually have some notes info and it is in memory, then increase the count else if ((*(This.theData))[i].notesOffset >=0 && (*(This.theData))[i].theNotes != nil) numInMemory++; } // If we have more than 75% of the nicknames in memory, don't reread them. if (numInMemory < (count * 3/4)) { err = ReadNicknames(which); if (err) return (false); } /* * make && open a temp file */ //tmpSpec = spec; //PCat(tmpSpec.name,GetRString(scratch,TEMP_SUFFIX)); if (err=NewTempSpec(spec.vRefNum,spec.parID,nil,&tmpSpec)) goto done; if (err=FSpCreate(&tmpSpec,CREATOR,MAILBOX_TYPE,smSystemScript)) {FileSystemError(SAVE_ALIAS,tmpSpec.name,err); goto done;} if (err=FSpOpenDF(&tmpSpec,fsRdWrPerm,&refN)) {FileSystemError(OPEN_ALIAS,tmpSpec.name,err); goto done;} count = NNicknames; // Get nickname count GetRString(aliasCmd,ALIAS_CMD); PCatC(aliasCmd,' '); HLock((Handle)This.theData); for (i=0;!err && i0 && !(*(This.theData))[i].deleted) { GetFPos(refN,&offset); (*(This.theData))[i].addressOffset = offset; } else { (*(This.theData))[i].addressOffset = -1; (*(This.theData))[i].group = false; } if ((!(*(This.theData))[i].deleted) && bytes > 0 && !(err = FSWriteP(refN,aliasCmd))) { if (PIndex(scratch,' ')) { PInsert(scratch,sizeof(scratch),"\p\"",scratch+1); PCatC(scratch,'"'); } PCatC(scratch,' '); if (!(err=FSWriteP(refN,scratch))) { if (!(err = AWrite(refN,&bytes,*tempHandle))) { bytes = 1; err = AWrite(refN,&bytes,"\015"); } } HPurge(tempHandle); } HUnlock(tempHandle); } GetRString(aliasCmd,NOTE_CMD); PCatC(aliasCmd,' '); for (i=0;!err && i0 && !(*(This.theData))[i].deleted) { GetFPos(refN,&offset); (*(This.theData))[i].notesOffset = offset; } else (*(This.theData))[i].notesOffset = -1; if ((!(*(This.theData))[i].deleted) && bytes >0 && !(err = FSWriteP(refN,aliasCmd))) { if (PIndex(scratch,' ')) { PInsert(scratch,sizeof(scratch),"\p\"",scratch+1); PCatC(scratch,'"'); } PCatC(scratch,' '); if (!(err=FSWriteP(refN,scratch))) { if (!(err = AWrite(refN,&bytes,*tempHandle))) { bytes = 1; err = AWrite(refN,&bytes,"\015"); } } HPurge(tempHandle); } HUnlock(tempHandle); if (err) {FileSystemError(SAVE_ALIAS,tmpSpec.name,err); goto done;} } UL(This.theData); GetFPos(refN,&bytes); SetEOF(refN,bytes); MyFSClose(refN); refN = 0; /* do the deed */ if (!err) err = ExchangeAndDel(&tmpSpec,&spec); if (!err) This.dirty = False; WriteNickTOC(which); done: if (This.theData) UL(This.theData); if (refN) MyFSClose(refN); if (err) FSpDelete(&tmpSpec); return(err==noErr); } /************************************************************************ * SaveFileFast - incremental save of the nickname file * Currently assumes that all notes commands follow all alias commands. * Fast save must not be done if this isn't the case ************************************************************************/ Boolean SaveFileFast (short which, Boolean saveChangeBits) { NickOffSetSortType **addressOffsetHandle; NickOffSetSortType **notesOffsetHandle; NickOffSetSortType dummyValue; short numOfNicks = NNicknames; long theSize; short count,tempCount; FSSpec spec,tmpSpec; OSErr err = fnfErr; long bytes; short tempRefN=0,nickRefN = 0; Str31 aliasCmd; long cleanStartOffset,cleanStopOffset,cleanStartIndex,cleanStopIndex; Boolean junk; Str255 scratch; Handle tempHandle = nil; Boolean dirty; Boolean deleted; long offset; short theIndex,i; long firstNoteStart; long bytesToShift; char theChar; /* * first make sure the file is ordered properly */ if (!NickFileOkForFastSave(which)) return(false); addressOffsetHandle=NuHTempOK(sizeof(NickOffSetSortType) * (numOfNicks + 1)); notesOffsetHandle=NuHTempOK(sizeof(NickOffSetSortType) * (numOfNicks + 1)); if (addressOffsetHandle == nil || notesOffsetHandle == nil) { err = fnfErr; goto done; } TotalNumOfNicks = numOfNicks; HLock((Handle)addressOffsetHandle); HLock((Handle)notesOffsetHandle); for (count = 0; count < numOfNicks; count++) { (*addressOffsetHandle)[count].offset = (*(This.theData))[count].addressOffset; (*addressOffsetHandle)[count].nickIndex = count; (*notesOffsetHandle)[count].offset = (*(This.theData))[count].notesOffset; (*notesOffsetHandle)[count].nickIndex = count; } dummyValue.offset = -1000; dummyValue.nickIndex = numOfNicks + 1000; theSize = GetHandleSize_(addressOffsetHandle) - sizeof(NickOffSetSortType); BlockMoveData(&dummyValue,*addressOffsetHandle + (theSize/sizeof(NickOffSetSortType)),sizeof(NickOffSetSortType)); BlockMoveData(&dummyValue,*notesOffsetHandle + (theSize/sizeof(NickOffSetSortType)),sizeof(NickOffSetSortType)); QuickSort((void *)(*addressOffsetHandle),sizeof(NickOffSetSortType),0,numOfNicks,(void*)NickOffsetCompare,(void*)NickOffsetSwap); SetHandleBig_((Handle)addressOffsetHandle,theSize); QuickSort((void *)(*notesOffsetHandle),sizeof(NickOffSetSortType),0,numOfNicks,(void*)NickOffsetCompare,(void*)NickOffsetSwap); SetHandleBig_((Handle)notesOffsetHandle,theSize); HLock((Handle)addressOffsetHandle); HLock((Handle)notesOffsetHandle); firstNoteStart = -1; // Find the offset of the first non-blank note for (count=0;count= 0) break; } /* * find the file */ spec = This.spec; if (err=FSpMyResolve(&spec,&junk)) return(False); /* * make && open a temp file */ // tmpSpec = spec; // PCat(tmpSpec.name,GetRString(scratch,TEMP_SUFFIX)); if (err=NewTempSpec(spec.vRefNum,spec.parID,nil,&tmpSpec)) goto done; if (err=FSpCreate(&tmpSpec,CREATOR,MAILBOX_TYPE,smSystemScript)) goto done; if (err=FSpOpenDF(&tmpSpec,fsRdWrPerm,&tempRefN)) goto done; if (err=FSpOpenDF(&spec,fsRdPerm,&nickRefN)) goto done; cleanStartOffset = -1; cleanStopOffset = -1; cleanStartIndex = -1; cleanStopIndex = -1; tempHandle = NuHTempOK(0); if (tempHandle == nil) { err = fnfErr; goto done; } HLock((Handle)This.theData); count = 0; while (count = numOfNicks) break; theIndex = (*addressOffsetHandle)[cleanStopIndex].nickIndex; dirty = (*(This.theData))[theIndex].addressesDirty; deleted = (*(This.theData))[theIndex].deleted; offset = (*(This.theData))[theIndex].addressOffset; } while ((!dirty && !deleted && offset >= 0) && cleanStopIndex= numOfNicks && !(*((*Aliases)[which].theData))[(*addressOffsetHandle)[numOfNicks - 1].nickIndex].addressesDirty && !(*((*Aliases)[which].theData))[(*addressOffsetHandle)[numOfNicks - 1].nickIndex].deleted) { if (firstNoteStart >= 0) cleanStopOffset = firstNoteStart; else GetEOF(nickRefN,&cleanStopOffset); cleanStopIndex = numOfNicks; } else { theIndex = (*addressOffsetHandle)[cleanStopIndex].nickIndex; cleanStopOffset = (*(This.theData))[theIndex].addressOffset; } if (cleanStartOffset <= 0 ) cleanStartOffset = 1; if (err = SetFPos(nickRefN,fsFromStart,cleanStartOffset - 1)) goto done; GetFPos(tempRefN,&bytes); bytesToShift = cleanStartOffset - bytes - 1; bytes = cleanStopOffset-cleanStartOffset; if (cleanStartOffset >=0 && bytes > 0) { long readBytes; SetHandleBig_(tempHandle,bytes); if (err = MemError()) goto done; HLock(tempHandle); readBytes = bytes; if (err = ARead(nickRefN,&readBytes,*tempHandle)) goto done; if (readBytes != bytes) { err = readErr; goto done; } if (err = AWrite(tempRefN,&bytes,*tempHandle)) goto done; HUnlock(tempHandle); for (tempCount = cleanStartIndex; tempCount < cleanStopIndex; tempCount++) { theIndex = (*addressOffsetHandle)[tempCount].nickIndex; if ((*(This.theData))[theIndex].addressOffset >=0 && !(*(This.theData))[theIndex].deleted) (*(This.theData))[theIndex].addressOffset-=bytesToShift; } } count = cleanStopIndex + 1; } ZapHandle(tempHandle); // leftover from last loop. SD 4/16 SetFPos(tempRefN,fsFromLEOF,-1); bytes = 1; ARead(tempRefN,&bytes,&theChar); GetEOF(tempRefN,&bytes); SetFPos(tempRefN,fsFromStart,bytes); if (theChar != '\015' && bytes >0) { bytes = 1; err = AWrite(tempRefN,&bytes,"\015"); } GetRString(aliasCmd,ALIAS_CMD); PCatC(aliasCmd,' '); HLock((Handle)This.theData); for (i=0;!err && i0 && !(*(This.theData))[i].deleted) { GetFPos(tempRefN,&offset); (*(This.theData))[i].addressOffset = offset; } else { (*(This.theData))[i].addressOffset = -1; (*(This.theData))[i].group = false; } if ((!(*(This.theData))[i].deleted) && bytes > 0 && !(err = FSWriteP(tempRefN,aliasCmd))) { if (PIndex(scratch,' ')) { PInsert(scratch,sizeof(scratch),"\p\"",scratch+1); PCatC(scratch,'"'); } PCatC(scratch,' '); if (!(err=FSWriteP(tempRefN,scratch))) { if (!(err = AWrite(tempRefN,&bytes,*tempHandle))) { bytes = 1; err = AWrite(tempRefN,&bytes,"\015"); } } HPurge(tempHandle); } HUnlock(tempHandle); } else if ((*(This.theData))[i].deleted) { (*(This.theData))[i].addressOffset = -1; (*(This.theData))[i].group = false; } } tempHandle = NuHTempOK(0); if (tempHandle == nil) goto done; // Do the notes count = 0; while (count = numOfNicks) break; theIndex = (*notesOffsetHandle)[cleanStopIndex].nickIndex; dirty = (*(This.theData))[theIndex].addressesDirty; deleted = (*(This.theData))[theIndex].deleted; offset = (*(This.theData))[theIndex].notesOffset; } while ((!dirty && !deleted && offset >= 0) && cleanStopIndex= numOfNicks && !(*((*Aliases)[which].theData))[(*notesOffsetHandle)[numOfNicks - 1].nickIndex].addressesDirty && !(*((*Aliases)[which].theData))[(*notesOffsetHandle)[numOfNicks - 1].nickIndex].deleted) { GetEOF(nickRefN,&cleanStopOffset); cleanStopIndex = numOfNicks; } else { theIndex = (*notesOffsetHandle)[cleanStopIndex].nickIndex; cleanStopOffset = (*(This.theData))[theIndex].notesOffset; } if (cleanStartOffset > 0) { if (err = SetFPos(nickRefN,fsFromStart,cleanStartOffset - 1)) goto done; GetFPos(tempRefN,&bytes); bytesToShift = cleanStartOffset - bytes - 1; bytes = cleanStopOffset-cleanStartOffset; if (cleanStartOffset >=0 && bytes >= 0) { long readBytes; SetHandleBig_(tempHandle,bytes); if (err=MemError()) goto done; HLock(tempHandle); readBytes = bytes; if (err = ARead(nickRefN,&readBytes,*tempHandle)) goto done; if (readBytes != bytes) { err = readErr; goto done; } if (err = AWrite(tempRefN,&bytes,*tempHandle)) goto done; HUnlock(tempHandle); for (tempCount = cleanStartIndex; tempCount < cleanStopIndex; tempCount++) { theIndex = (*notesOffsetHandle)[tempCount].nickIndex; if ((*(This.theData))[theIndex].notesOffset >= 0 && !(*(This.theData))[theIndex].deleted) (*(This.theData))[theIndex].notesOffset-=bytesToShift; } } } count = cleanStopIndex + 1; } ZapHandle(tempHandle); SetFPos(tempRefN,fsFromLEOF,-1); bytes = 1; ARead(tempRefN,&bytes,&theChar); GetEOF(tempRefN,&bytes); SetFPos(tempRefN,fsFromStart,bytes); if (theChar != '\015' && bytes > 0) { bytes = 1; err = AWrite(tempRefN,&bytes,"\015"); } GetRString(aliasCmd,NOTE_CMD); PCatC(aliasCmd,' '); if (This.theData) { HLock((Handle)This.theData); for (i=0;!err && i0 && !(*(This.theData))[i].deleted) { GetFPos(tempRefN,&offset); (*(This.theData))[i].notesOffset = offset; } else (*(This.theData))[i].notesOffset = -1; if ((!(*(This.theData))[i].deleted) && bytes > 0 && !(err = FSWriteP(tempRefN,aliasCmd))) { if (PIndex(scratch,' ')) { PInsert(scratch,sizeof(scratch),"\p\"",scratch+1); PCatC(scratch,'"'); } PCatC(scratch,' '); if (!(err=FSWriteP(tempRefN,scratch))) { if (!(err = AWrite(tempRefN,&bytes,*tempHandle))) { bytes = 1; err = AWrite(tempRefN,&bytes,"\015"); } } } if (tempHandle) { HPurge(tempHandle); HUnlock(tempHandle); } } } UL(This.theData); } tempHandle = nil; GetFPos(tempRefN,&bytes); SetEOF(tempRefN,bytes); MyFSClose(tempRefN); tempRefN = 0; MyFSClose(nickRefN); nickRefN = 0; /* do the deed */ if (!err) err = ExchangeAndDel(&tmpSpec,&spec); if (!err) This.dirty = False; if (!err) WriteNickTOC(which); done: if (This.theData) UL(This.theData); if (tempRefN) MyFSClose(tempRefN); if (nickRefN) MyFSClose(nickRefN); if (err) FSpDelete(&tmpSpec); UL(addressOffsetHandle); UL(notesOffsetHandle); ZapHandle(addressOffsetHandle); ZapHandle(notesOffsetHandle); UL(This.theData); return(err==noErr); } // // Nickname photos are initially saved into the temporary items folder. // When we save the address book, we need to move these files into the // photo album and modify tthe nickname 'picture' tag to point to the // new location. // void SaveDirtyPictures (short ab) { NickStructHandle theData; NickStructPtr pData; FInfo fInfo; FSSpec photoSpec, urlSpec; Str255 pictureTag, nickname, url; Handle urlString; OSErr theError; short nick, numNicks; Boolean alreadyExists; GetRString (pictureTag, ABReservedTagsStrn + abTagPicture); theError = noErr; if (theData = (*Aliases)[ab].theData) { numNicks = GetHandleSize_ (theData) / sizeof (NickStruct); for (nick = 0, pData = *theData; nick < numNicks; nick++, pData++) // Does this nickname have a dirty picture? if (!pData->deleted) if (pData->pornography) { // Grab the current 'file' URL from the notes, and turn it into an FSSpec if (urlString = GetTaggedFieldValue (ab, nick, pictureTag)) { theError = URLStringToSpec (urlString, &urlSpec); // Make an FSSpec for the photo in the Photo Album if (!theError) theError = GetPhotoSpec (&photoSpec, ab, nick, &alreadyExists); if (!theError && !alreadyExists) { theError = FSpGetFInfo (&urlSpec, &fInfo); if (!theError) theError = FSpCreate (&photoSpec, fInfo.fdCreator, fInfo.fdType, smSystemScript); if (!theError) theError = FSpSetFInfo (&photoSpec, &fInfo); } // Swap specs and delete the temporary if (!theError) theError = ExchangeAndDel (&urlSpec, &photoSpec); // Make a url for the file's permanent location if (!theError) { SetTaggedFieldValue (ab, nick, pictureTag, MakeFileURL (url, &photoSpec, 0), nickFieldReplaceExisting, 0, nil); pData->pornography = false; } else ComposeStdAlert (Caution, NICK_PHOTO_COULD_NOT_SAVE, GetNicknameNamePStr (ab, nick, nickname), theError); } } } } // // URLStringToSpec // // Take a URL and turn it into an FSSpec // OSErr URLStringToSpec (StringHandle urlString, FSSpec *spec) { Str255 fullPath, proto, host; Ptr query; long urlSize, origQueryLen, queryLen; OSErr theError; theError = ParseURLPtr (LDRef (urlString), urlSize = GetHandleSize (urlString), proto, host, &query, &queryLen); origQueryLen = queryLen; if (!theError) switch (FindSTRNIndex (ProtocolStrn, proto)) { case proFile: TrLo (query, queryLen, "/", ":"); FixURLPtr (query, &queryLen); MakePPtr (fullPath, query, queryLen); theError = FSMakeFSSpec (0, 0, fullPath, spec); break; } UL (urlString); if (!theError) { SetHandleBig (urlString, urlSize + (queryLen - origQueryLen)); IsAlias (spec, spec); } return (theError); } /************************************************************************ * NickFileOkForFastSave - see if all addresses come before all notes ************************************************************************/ Boolean NickFileOkForFastSave(short which) { short n = NNicknames; long minNote = 0x7fffffff; long maxAlias = 0; long note, alias; while (n--) { note = (*(This.theData))[n].notesOffset; alias = (*(This.theData))[n].addressOffset; if (note>=0) minNote = MIN(minNote,note); if (alias>=0) maxAlias = MAX(maxAlias,alias); if (minNote <= maxAlias) return(false); // at least one note comes before at least one alias } return(true); // all aliases come after all notes } /************************************************************************ * NickOffsetCompare - compare two names ************************************************************************/ int NickOffsetCompare(NickOffSetSortType *n1, NickOffSetSortType *n2) { int result; if (n1->nickIndex>=TotalNumOfNicks) return(1); if (n2->nickIndex>=TotalNumOfNicks) return(-1); if (n1->offset == n2->offset) result = 0; else result = n1->offset > n2->offset?1:-1; return(result); } /************************************************************************ * NickNameSwap - swap two names ************************************************************************/ void NickOffsetSwap(NickOffSetSortType *n1, NickOffSetSortType *n2) { NickOffSetSortType temp; BlockMoveData(n2,&temp,sizeof(NickOffSetSortType)); BlockMoveData(n1,n2,sizeof(NickOffSetSortType)); BlockMoveData(&temp,n1,sizeof(NickOffSetSortType)); } /************************************************************************ * AppendTextToNick - append (comma separated) addresses to a nickname ************************************************************************/ OSErr AddTextToNick(short which, PStr name, Handle text,Boolean append) { long hashName = NickHash(name); long index; NickStructHandle aliases = This.theData; OSErr err = noErr; Handle tempHandle; // AliasWinGonnaSave(); index = NickMatchFound(aliases,hashName,name,which); if (append) { tempHandle = GetNicknameData(which,index,true,true); err = PtrPlusHand_(",",text,1); if (!err) err = PtrPlusHand_(LDRef(tempHandle),text,GetHandleSize_(GetNicknameData(which,index,true,true))); UL(tempHandle); if (err) {WarnUser(MEM_ERR,err); return(err);} UL(This.theData); } NickUniq(text,"\p,",True); ReplaceNicknameAddresses(which,name,text); SetAliasDirty(which); if (AliasWinIsOpen()) AliasWinRefresh(); else SaveAliases(true); return err; } /************************************************************************ * ReadNickFileList - get list of nickfiles ************************************************************************/ void ReadNickFileList(FSSpec *pSpec, AddressBookType type, Boolean reread) { Str31 name; CInfoPBRec hfi; AliasDesc ad; Boolean multipleNickFiles; multipleNickFiles = false; Zero(ad); ad.type = type; hfi.hFileInfo.ioNamePtr = name; hfi.hFileInfo.ioFDirIndex = 0; while (!DirIterate(pSpec->vRefNum,pSpec->parID,&hfi)) if (hfi.hFileInfo.ioFlFndrInfo.fdType=='TEXT' || hfi.hFileInfo.ioFlFndrInfo.fdCreator==CREATOR) { multipleNickFiles = true; SimpleMakeFSSpec(pSpec->vRefNum,pSpec->parID,name,&ad.spec); if (!CanWrite(&ad.spec,&ad.ro)) { ad.ro = !ad.ro; /* opposite sense! */ if (PtrPlusHand_(&ad,Aliases,sizeof(ad))) DieWithError(MEM_ERR,MemError()); if (type == pluginAddressBook && reread) FSpKillRFork(&ad.spec); } else { Str255 scratch; ComposeRString(scratch,NICK_FILE_GONE,ad.spec.name); AlertStr(OK_ALRT,Caution,scratch); } } if (multipleNickFiles) UseFeature (featureMultipleNicknameFiles); } /************************************************************************ * BuildAddressHashes - build a hash table for all the expanded addresses ************************************************************************/ OSErr BuildAddressHashes(short which) { Accumulator a; NickStructHandle nicks = (*Aliases)[which].theData; short n = NNicknames; TextAddrHandle tempHandle=nil; Str31 s; OSErr err = noErr; Zero(a); for (n--;!err && n>=0;n--) { CycleBalls(); if (!(*This.theData)[n].deleted) { if ((*(This.theData))[n].addressesDirty) // Nickname has been modified...use in memory copy tempHandle = GetNicknameData(which,n,true,false); else // Nickname hasn't been modified, use on disk copy if necessary tempHandle = GetNicknameData(which,n,true,true); // main addresses if (tempHandle) { err = AddHandleToAddressHashes(tempHandle,&a); if (!(*(This.theData))[n].addressesDirty) HPurge(tempHandle); } // other addresses if (!err && (tempHandle = GetTaggedFieldValue(which,n,GetRString(s,ABReservedTagsStrn+abTagOtherEmail)))) { err = AddHandleToAddressHashes(tempHandle,&a); ZapHandle(tempHandle); } } } // Did we win? if (!err) { AccuTrim(&a); (*Aliases)[which].addressHashes = a; } else AccuZap(a); return err; } /************************************************************************ * AddHandleToAddressHashes - add addresses from a handle to a hash accumulator ************************************************************************/ OSErr AddHandleToAddressHashes(TextAddrHandle sourceHandle,AccuPtr a) { BinAddrHandle rawHandle=nil; BinAddrHandle expandedHandle=nil; Str255 oneAddr; long spot, size; uLong hash; OSErr err = noErr; if (sourceHandle) if (!SuckAddresses(&rawHandle,sourceHandle,false,false,true,nil)) if (!ExpandAliases(&expandedHandle,rawHandle,0,false)) { size = GetHandleSize(expandedHandle)-1; for (spot=0;!err && spot realName + 1) lastTokenSeperator = *(spot - 1); // Skip white space before the next token while (spot <= &realName[realName[0]] && *spot == ' ' || *spot == tabChar) ++spot; } // Okay, at this point we have stuff in 4 places: name1, name2, qualifiers and honorifics // We can now go about building the actual first and last names based on whether or not // we've found a comma and whether or not we're working with eastern vs western style names. name = name1; numPtr = &numName1; // If we found no commas, or the _opposite_ name is empty (indicating that the entire name is // stuffed into the primary (first) name fiels, then we'll make the opposite (last) name the // final token appearing in the primary name field (whew!) if (!numCommas || !((name == name1) ? *name2 : *name1)) { if (*numPtr > 1) { short name2len; spot = PRIndex (name, lastDelimiter); name2len = *name - (spot - name); BMD (spot, (name == name1) ? name2 : name1, name2len + 1); *name2 = name2len; *name -= (name2len + 1); } if ((GetPrefLong(PREF_NICK_GEN_OPTIONS)&kNickGenOptAsian) || IsAllUpper(name1) && !IsAllUpper(name2)) { PCopy (firstName, name2); PCopy (lastName, name1); } else { PCopy (firstName, name1); PCopy (lastName, name2); } } else if ((GetPrefLong(PREF_NICK_GEN_OPTIONS)&kNickGenOptAsian) || IsAllUpper(name2) && !IsAllUpper(name1)) { PCopy (firstName, name1); PCopy (lastName, name2); } else { PCopy (firstName, name2); PCopy (lastName, name1); } // Append qualifiers to the first name if (numQualifiers) { PCatC (firstName, ' '); PCat (firstName, qualifiers); } return (realName); } PStr JoinFirstLast (PStr fullName, PStr firstName, PStr lastName) { Str255 first, last; *fullName = 0; *first = 0; *last = 0; // Strip Qualifiers and Honorifics from the first and last names StripQualifiersAndHonorifics (firstName, first); StripQualifiersAndHonorifics (lastName, last); // Join them based on our "international joining preferences" if ((GetPrefLong(PREF_NICK_GEN_OPTIONS)&kNickGenOptAsian) || IsAllUpper(last) && !IsAllUpper(first)) { if (*last) PCat (fullName, last); if (*first) { if (*last) PCatC (fullName, ' '); PCat (fullName, first); } } else { if (*first) PCat (fullName, first); if (*last) { if (*first) PCatC (fullName, ' '); PCat (fullName, last); } } return (fullName); } PStr StripQualifiersAndHonorifics (PStr name, PStr strippedName) { Str255 token, tokenCopy; UPtr spot; char lastTokenSeperator; *strippedName = 0; lastTokenSeperator = 0; // Loop through all the tokens in the first name spot = name + 1; while (PToken (name, token, &spot, " ,")) { PCopy (tokenCopy, token); // Cleanup the token a bit before giving the qualifiers and honorifics a shot PStripChar (tokenCopy, '.'); // If the token neither a name qualifier or honorific, keep what we found if (!FindSTRNIndex (NameQualifiersStrn, tokenCopy) && !FindSTRNIndex (NameHonorificsStrn, tokenCopy)) { if (lastTokenSeperator) PCatC (strippedName, lastTokenSeperator); PCat (strippedName, token); } if (spot > name + 1) lastTokenSeperator = *(spot - 1); // Skip white space before the next token while (spot <= &name[name[0]] && *spot == ' ' || *spot == tabChar) ++spot; } return (strippedName); } // Walk the text of the name backwards, inserting spaces as we find periods that // follow runs of two or more characters PStr ScanNameForSpaces (PStr name) { Ptr spot; short chars; // don't insert spaces if name contains an @, since it's probably an address if (PIndex(name,'@')) return name; chars = 0; for (spot = name + *name; spot > name; spot--) switch (*spot) { case '.': if (chars > 1) { PInsertC (name, sizeof (Str255), ' ', spot + 1); } case ' ': chars = 0; break; default: ++chars; } return (name); } /************************************************************************ * MakeUniqueNickname - Make a unique "untitled" nickname ************************************************************************/ void MakeUniqueNickname (short ab, Str31 nickname) { NickStructHandle aliases; Str31 s; long hashName, suffix; Byte saveLen; if (!*nickname) GetRString (nickname, UNTITLED_NICKNAME); if (aliases = (*Aliases)[ab].theData) { hashName = NickHash (nickname); suffix = 2; saveLen = *nickname; while (NickMatchFound (aliases, hashName, nickname, ab) >= 0) { *nickname = saveLen; s[*s = 1] = ' '; PLCat (s, suffix++); if (*nickname + *s + 1 < sizeof (Str31) - 1) PCat (nickname, s); else { BlockMoveData (&s[1], &nickname[*nickname - *s + 1], *s); *nickname = sizeof (Str31) - 1; } hashName = NickHash (nickname); } } } /************************************************************************ * NickBackup - Make a backup of a nickname file ************************************************************************/ OSErr NickBackup(FSSpecPtr spec) { OSErr err = noErr; FSSpec spoolSpec; DateTimeRec dtr; Str31 name; if ((err=SubFolderSpec(SPOOL_FOLDER,&spoolSpec))==noErr) { GetTime(&dtr); FSMakeFSSpec(spoolSpec.vRefNum,spoolSpec.parID,ComposeString(name,"\p%p.%d.%d.%d.%d",spec->name,dtr.day,dtr.hour,dtr.minute,dtr.second),&spoolSpec); err = FSpDupFile(&spoolSpec,spec,False,False); } return (err); } // // GetNicknameTagMap // // Retrieve a Nickname Tag Map from a 'TGMP' resource, placing the contents // into a NicknameTagMapRec. In searching for the proper resource we look // for a 'TGMP' with a resource name that matches: // 1. The requested server // 2. The requested service (if no server match was found) // OSErr GetNicknameTagMap (PStr service, PStr server, NicknameTagMapRecPtr tagMapPtr) { Handle resource; Ptr from; Str255 scratch; OSErr theError; short i; // First, find a 'TGMP' resource resource = nil; if (*server) resource = GetNamedResource (TAG_MAP_TYPE, server); if (!resource && *service) resource = GetNamedResource (TAG_MAP_TYPE, service); if (!resource) return (ResError ()); // Create handles for service and nickname tags tagMapPtr->serviceTags = NuHandle (0); tagMapPtr->nicknameTags = NuHandle (0); theError = MemError (); if (!theError) { // Read the information out of the 'TGMP' resource from = LDRef (resource); // Service from = CopyBytesAndMovePtr (from, &tagMapPtr->service, *from + 1); // Server from = CopyBytesAndMovePtr (from, &tagMapPtr->server, *from + 1); // Count from = CopyBytesAndMovePtr (from, &tagMapPtr->count, sizeof (tagMapPtr->count)); // The tags for (i = 0; i < tagMapPtr->count && !theError; ++i) { from = CopyBytesAndMovePtr (from, scratch, *from + 1); theError = PtrPlusHand (scratch, tagMapPtr->serviceTags, *scratch + 1); if (!theError) { from = CopyBytesAndMovePtr (from, scratch, *from + 1); theError = PtrPlusHand (scratch, tagMapPtr->nicknameTags, *scratch + 1); } } UL (resource); } ReleaseResource (resource); // Get rid of any memory we've allocated if trouble is brewing if (theError) DisposeNicknameTagMap (tagMapPtr); return (theError); } void DisposeNicknameTagMap (NicknameTagMapRecPtr tagMapPtr) { if (tagMapPtr) { ZapHandle (tagMapPtr->serviceTags); ZapHandle (tagMapPtr->nicknameTags); } } // // NicknameTag2ServiceTag // // Given a pointer to a Nickname Tag Map and a Eudora nickname tag, return the corresponding // tag for a given service. // PStr NicknameTag2ServiceTag (NicknameTagMapRecPtr tagMapPtr, PStr nicknameTag, PStr serviceTag) { Ptr serviceTagsPtr, nicknameTagsPtr; short i; *serviceTag = 0; nicknameTagsPtr = LDRef (tagMapPtr->nicknameTags); serviceTagsPtr = LDRef (tagMapPtr->serviceTags); for (i = 0; i < tagMapPtr->count && !*serviceTag; ++i) if (StringSame (nicknameTagsPtr, nicknameTag)) PCopy (serviceTag, serviceTagsPtr); else { nicknameTagsPtr += (*nicknameTagsPtr + 1); serviceTagsPtr += (*serviceTagsPtr + 1); } UL (tagMapPtr->serviceTags); UL (tagMapPtr->nicknameTags); return (serviceTag); } // // ServiceTag2NicknameTag // // Given a pointer to e Nickname Tag Map and a service tag, return the corresponding // tag for a Eudora nickname // PStr ServiceTag2NicknameTag (NicknameTagMapRecPtr tagMapPtr, PStr serviceTag, PStr nicknameTag) { Ptr serviceTagsPtr, nicknameTagsPtr; short i; *nicknameTag = 0; serviceTagsPtr = LDRef (tagMapPtr->serviceTags); nicknameTagsPtr = LDRef (tagMapPtr->nicknameTags); for (i = 0; i < tagMapPtr->count && !*nicknameTag; ++i) if (StringSame (serviceTagsPtr, serviceTag)) PCopy (nicknameTag, nicknameTagsPtr); else { serviceTagsPtr += (*serviceTagsPtr + 1); nicknameTagsPtr += (*nicknameTagsPtr + 1); } UL (tagMapPtr->nicknameTags); UL (tagMapPtr->serviceTags); return (nicknameTag); } PStr GetIndNicknameTag (NicknameTagMapRecPtr tagMapPtr, short index, PStr nicknameTag) { Ptr nicknameTagsPtr; short i; *nicknameTag = 0; nicknameTagsPtr = *tagMapPtr->nicknameTags; if (index < tagMapPtr->count) { for (i = 0; i < index; ++i) nicknameTagsPtr += (*nicknameTagsPtr + 1); PCopy (nicknameTag, nicknameTagsPtr); } return (nicknameTag); } short FindServiceTagIndex (NicknameTagMapRecPtr tagMapPtr, PStr serviceTag) { Ptr serviceTagsPtr; short index, i; serviceTagsPtr = LDRef (tagMapPtr->serviceTags); index = 0; for (i = 1; !index && i <= tagMapPtr->count; ++i) if (StringSame (serviceTagsPtr, serviceTag)) index = i; else serviceTagsPtr += (*serviceTagsPtr + 1); UL (tagMapPtr->serviceTags); return (index); } PrimaryLocationType GetPrimaryLocation (Handle notes) { PrimaryLocationType location; Str255 tag, value; location = noPrimary; GetTaggedFieldValueStrInNotes (notes, GetRString (tag, ABReservedTagsStrn + abTagPrimary), value); if (StringSame (value, GetRString (tag, VCardKeywordStrn + vcHome))) location = homePrimary; else if (StringSame (value, GetRString (tag, VCardKeywordStrn + vcWork))) location = workPrimary; return (location); } short FindAddressBookType (AddressBookType type) { short addressBooks, ab; addressBooks = NAliases; for (ab = 0; ab < addressBooks; ++ab) if ((*Aliases)[ab].type == type) return (ab); return (-1); } #ifdef VCARD Boolean AnyPersonalNicknames (void) { short ab, nick; Boolean anyNicks; anyNicks = false; ab = FindAddressBookType (personalAddressBook); if (ValidAddressBook (ab)) if ((*Aliases)[ab].theData) { nick = GetHandleSize_ ((*Aliases)[ab].theData) / sizeof (NickStruct); while (!anyNicks && nick--) if (!(*((*Aliases)[ab].theData))[nick].deleted) anyNicks = true; } return (anyNicks); } /************************************************************************ * WhiteListTS - add a message's sender to the whitelist ************************************************************************/ OSErr WhiteListTS(TOCHandle tocH,short sumNum) { Str255 scratch; HeadSpec hs; TextAddrHandle addr=nil; Accumulator a; uLong hash; if (sumNum<0) { Zero(a); // Examine all the selected messages for (sumNum=(*tocH)->count-1;sumNum>=0;sumNum--) { if ((*tocH)->sums[sumNum].selected) { WhiteListTS(tocH,sumNum); EnsureFromHash(tocH,sumNum); hash = (*tocH)->sums[sumNum].fromHash; if (ValidHash(hash) && 0>AccuFindPtr(&a,&hash,sizeof(hash))) AccuAddPtr(&a,&hash,sizeof(hash)); } } // select everything with the same hash for (sumNum=(*tocH)->count;sumNum--;) if (!(*tocH)->sums[sumNum].selected) { EnsureFromHash(tocH,sumNum); hash = (*tocH)->sums[sumNum].fromHash; if (ValidHash(hash) && 0<=AccuFindPtr(&a,&hash,sizeof(hash))) BoxSetSummarySelected(tocH,sumNum,true); } AccuZap(a); } else { if (!CacheMessage(tocH,sumNum)) { HNoPurge((*tocH)->sums[sumNum].cache); HeaderName(FROM_HEAD); // weird--goes into scratch TrimWhite(scratch); if (HandleHeadFindStr((*tocH)->sums[sumNum].cache,scratch,&hs)) { HandleHeadGetText((*tocH)->sums[sumNum].cache,&hs,&addr); if (addr) WhiteListAddr(addr); ZapHandle(addr); } HPurge((*tocH)->sums[sumNum].cache); // Now that we've whitelisted the message, bop its junk score JunkSetScore(tocH,sumNum,JUNK_BECAUSE_WHITE,0); } } return noErr; // la la la la la la la la la la la la la la } /************************************************************************ * WhiteListAddr - add a an address to the whitelist ************************************************************************/ OSErr WhiteListAddr(TextAddrHandle addr) { Str255 scratch; Str31 abName; Str63 nick; Str255 first, last; short which=0; BinAddrHandle binAddr, justAddr; OSErr err = fnfErr; // is it there already? MakePStr(scratch,*addr,GetHandleSize(addr)); if (AppearsInAliasFile(scratch,nil)) return dupFNErr; // already there // Nope. Add it. // find which book if (*GetRString(scratch,WHITELIST_ADDRBOOK)) { for (which = NAliases-1; which>0; which--) { PCopy(abName,(*Aliases)[which].spec.name); if (StringSame(abName,scratch)) break; } } else which = FindAddressBookType(eudoraAddressBook); // reformat the address if (!SuckAddresses(&binAddr,addr,true,false,false,nil)) { // get a nickname suggestion NickSuggest(binAddr,nick,scratch,true,JUNK_NICK_FMT); if (*nick) if (!SuckAddresses(&justAddr,addr,false,false,false,nil)) { // fancy names are good ParseFirstLast(scratch,first,last); // finally add the darn thing if (0<=NewNickLow(justAddr, CreateSimpleNotes(scratch,first,last), which, nick, false, nrNone, !NickWinIsOpen())) ABTickleHardEnoughToMakeYouPuke(); // the rest is gravy... ZapHandle(justAddr); } ZapHandle(binAddr); } return err; } #endif /************************************************************************ * UniqBinAddr - make a BinAddrHandle contain only one of each address ************************************************************************/ BinAddrHandle UniqBinAddr(BinAddrHandle addresses) { Accumulator hashAcc, addrAcc; Str255 addr; long offset = 0; long len = GetHandleSize(addresses); Zero(hashAcc); Zero(addrAcc); // loop through all the addresses in the bin addr list for (offset = 0; offset